mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 12:51:57 +00:00
feat(vscode): add a option to control oxc lint timing (#1659)
1. Closed https://github.com/oxc-project/oxc/issues/1653 2. Now you use `oxc-client.run` to control the lint timing. [录屏 2023年12月12日 23时39分16秒.webm](https://github.com/oxc-project/oxc/assets/17974631/123326dc-6110-4f2e-9d48-ef1a3b0a4536)
This commit is contained in:
parent
117d95f0ae
commit
e63576d03c
5 changed files with 85 additions and 8 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1956,6 +1956,8 @@ dependencies = [
|
||||||
"oxc_span",
|
"oxc_span",
|
||||||
"rayon",
|
"rayon",
|
||||||
"ropey",
|
"ropey",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-lsp",
|
"tower-lsp",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ export async function activate(context: ExtensionContext) {
|
||||||
// If the extension is launched in debug mode then the debug server options are used
|
// If the extension is launched in debug mode then the debug server options are used
|
||||||
// Otherwise the run options are used
|
// Otherwise the run options are used
|
||||||
// Options to control the language client
|
// Options to control the language client
|
||||||
|
let clientConfig: any = JSON.parse(JSON.stringify(workspace.getConfiguration('oxc-client')));
|
||||||
let clientOptions: LanguageClientOptions = {
|
let clientOptions: LanguageClientOptions = {
|
||||||
// Register the server for plain text documents
|
// Register the server for plain text documents
|
||||||
documentSelector: [
|
documentSelector: [
|
||||||
|
|
@ -96,17 +97,28 @@ export async function activate(context: ExtensionContext) {
|
||||||
"typescriptreact",
|
"typescriptreact",
|
||||||
"javascriptreact",
|
"javascriptreact",
|
||||||
].map(lang => ({ language: lang, scheme: "file" })),
|
].map(lang => ({ language: lang, scheme: "file" })),
|
||||||
|
|
||||||
synchronize: {
|
synchronize: {
|
||||||
// Notify the server about file changes to '.clientrc files contained in the workspace
|
// Notify the server about file changes to '.clientrc files contained in the workspace
|
||||||
fileEvents: workspace.createFileSystemWatcher("**/.clientrc"),
|
fileEvents: workspace.createFileSystemWatcher("**/.clientrc"),
|
||||||
},
|
},
|
||||||
|
initializationOptions: {
|
||||||
|
settings: clientConfig
|
||||||
|
},
|
||||||
outputChannel,
|
outputChannel,
|
||||||
traceOutputChannel,
|
traceOutputChannel,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create the language client and start the client.
|
// Create the language client and start the client.
|
||||||
client = new LanguageClient(languageClientId, languageClientName, serverOptions, clientOptions);
|
client = new LanguageClient(languageClientId, languageClientName, serverOptions, clientOptions);
|
||||||
|
workspace.onDidChangeConfiguration((e) => {
|
||||||
|
let settings: any = {};
|
||||||
|
if (e.affectsConfiguration("oxc-client.run")) {
|
||||||
|
settings.run = workspace.getConfiguration('oxc-client').get("run")
|
||||||
|
}
|
||||||
|
client.sendNotification("workspace/didChangeConfiguration", {
|
||||||
|
settings
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
client.start();
|
client.start();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,16 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"title": "oxc",
|
"title": "oxc",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"oxc-client.run": {
|
||||||
|
"scope": "resource",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"onSave",
|
||||||
|
"onType"
|
||||||
|
],
|
||||||
|
"default": "onType",
|
||||||
|
"description": "Run the linter on save (onSave) or on type (onType)"
|
||||||
|
},
|
||||||
"oxc-client.trace.server": {
|
"oxc-client.trace.server": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"scope": "window",
|
"scope": "window",
|
||||||
|
|
|
||||||
|
|
@ -37,3 +37,5 @@ ropey = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["full"] }
|
tokio = { workspace = true, features = ["full"] }
|
||||||
tower-lsp = { workspace = true, features = ["proposed"] }
|
tower-lsp = { workspace = true, features = ["proposed"] }
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -4,21 +4,24 @@ mod options;
|
||||||
mod walk;
|
mod walk;
|
||||||
|
|
||||||
use crate::linter::{DiagnosticReport, ServerLinter};
|
use crate::linter::{DiagnosticReport, ServerLinter};
|
||||||
use log::debug;
|
use log::{debug, error};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use tokio::sync::{OnceCell, SetError};
|
use tokio::sync::{Mutex, OnceCell, SetError};
|
||||||
use tower_lsp::jsonrpc::{Error, ErrorCode, Result};
|
use tower_lsp::jsonrpc::{Error, ErrorCode, Result};
|
||||||
use tower_lsp::lsp_types::{
|
use tower_lsp::lsp_types::{
|
||||||
CodeAction, CodeActionKind, CodeActionOptions, CodeActionOrCommand, CodeActionParams,
|
CodeAction, CodeActionKind, CodeActionOptions, CodeActionOrCommand, CodeActionParams,
|
||||||
CodeActionProviderCapability, CodeActionResponse, Diagnostic, DidChangeTextDocumentParams,
|
CodeActionProviderCapability, CodeActionResponse, Diagnostic, DidChangeConfigurationParams,
|
||||||
DidOpenTextDocumentParams, DidSaveTextDocumentParams, InitializeParams, InitializeResult,
|
DidChangeTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams,
|
||||||
InitializedParams, MessageType, ServerCapabilities, ServerInfo, TextDocumentSyncCapability,
|
InitializeParams, InitializeResult, InitializedParams, MessageType, OneOf, Registration,
|
||||||
TextDocumentSyncKind, TextEdit, Url, WorkDoneProgressOptions, WorkspaceEdit,
|
ServerCapabilities, ServerInfo, TextDocumentSyncCapability, TextDocumentSyncKind, TextEdit,
|
||||||
|
Url, WorkDoneProgressOptions, WorkspaceEdit, WorkspaceFoldersServerCapabilities,
|
||||||
|
WorkspaceServerCapabilities,
|
||||||
};
|
};
|
||||||
use tower_lsp::{Client, LanguageServer, LspService, Server};
|
use tower_lsp::{Client, LanguageServer, LspService, Server};
|
||||||
|
|
||||||
|
|
@ -28,13 +31,33 @@ struct Backend {
|
||||||
root_uri: OnceCell<Option<Url>>,
|
root_uri: OnceCell<Option<Url>>,
|
||||||
server_linter: ServerLinter,
|
server_linter: ServerLinter,
|
||||||
diagnostics_report_map: DashMap<String, Vec<DiagnosticReport>>,
|
diagnostics_report_map: DashMap<String, Vec<DiagnosticReport>>,
|
||||||
|
options: Mutex<Options>,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, PartialOrd, Clone, Copy)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
enum Run {
|
||||||
|
OnSave,
|
||||||
|
#[default]
|
||||||
|
OnType,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||||
|
struct Options {
|
||||||
|
run: Run,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tower_lsp::async_trait]
|
#[tower_lsp::async_trait]
|
||||||
impl LanguageServer for Backend {
|
impl LanguageServer for Backend {
|
||||||
async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
|
async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
|
||||||
self.init(params.root_uri)?;
|
self.init(params.root_uri)?;
|
||||||
|
let options = params.initialization_options.and_then(|mut value| {
|
||||||
|
let settings = value.get_mut("settings")?.take();
|
||||||
|
serde_json::from_value::<Options>(settings).ok()
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(value) = options {
|
||||||
|
debug!("initialize: {:?}", value);
|
||||||
|
*self.options.lock().await = value;
|
||||||
|
}
|
||||||
Ok(InitializeResult {
|
Ok(InitializeResult {
|
||||||
server_info: Some(ServerInfo { name: "oxc".into(), version: None }),
|
server_info: Some(ServerInfo { name: "oxc".into(), version: None }),
|
||||||
offset_encoding: None,
|
offset_encoding: None,
|
||||||
|
|
@ -42,6 +65,13 @@ impl LanguageServer for Backend {
|
||||||
text_document_sync: Some(TextDocumentSyncCapability::Kind(
|
text_document_sync: Some(TextDocumentSyncCapability::Kind(
|
||||||
TextDocumentSyncKind::FULL,
|
TextDocumentSyncKind::FULL,
|
||||||
)),
|
)),
|
||||||
|
workspace: Some(WorkspaceServerCapabilities {
|
||||||
|
workspace_folders: Some(WorkspaceFoldersServerCapabilities {
|
||||||
|
supported: Some(true),
|
||||||
|
change_notifications: Some(OneOf::Left(true)),
|
||||||
|
}),
|
||||||
|
file_operations: None,
|
||||||
|
}),
|
||||||
code_action_provider: Some(CodeActionProviderCapability::Options(
|
code_action_provider: Some(CodeActionProviderCapability::Options(
|
||||||
CodeActionOptions {
|
CodeActionOptions {
|
||||||
code_action_kinds: Some(vec![CodeActionKind::QUICKFIX]),
|
code_action_kinds: Some(vec![CodeActionKind::QUICKFIX]),
|
||||||
|
|
@ -56,7 +86,18 @@ impl LanguageServer for Backend {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn initialized(&self, _: InitializedParams) {
|
async fn did_change_configuration(&self, params: DidChangeConfigurationParams) {
|
||||||
|
let changed_options = match serde_json::from_value::<Options>(params.settings) {
|
||||||
|
Ok(option) => option,
|
||||||
|
Err(err) => {
|
||||||
|
error!("error parsing settings: {:?}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
*self.options.lock().await = changed_options;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn initialized(&self, params: InitializedParams) {
|
||||||
debug!("oxc initialized.");
|
debug!("oxc initialized.");
|
||||||
|
|
||||||
if let Some(Some(root_uri)) = self.root_uri.get() {
|
if let Some(Some(root_uri)) = self.root_uri.get() {
|
||||||
|
|
@ -79,12 +120,21 @@ impl LanguageServer for Backend {
|
||||||
|
|
||||||
async fn did_save(&self, params: DidSaveTextDocumentParams) {
|
async fn did_save(&self, params: DidSaveTextDocumentParams) {
|
||||||
debug!("oxc server did save");
|
debug!("oxc server did save");
|
||||||
|
// drop as fast as possible
|
||||||
|
let options = { self.options.lock().await.run };
|
||||||
|
if options < Run::OnSave {
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.handle_file_update(params.text_document.uri, None).await;
|
self.handle_file_update(params.text_document.uri, None).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When the document changed, it may not be written to disk, so we should
|
/// When the document changed, it may not be written to disk, so we should
|
||||||
/// get the file context from the language client
|
/// get the file context from the language client
|
||||||
async fn did_change(&self, params: DidChangeTextDocumentParams) {
|
async fn did_change(&self, params: DidChangeTextDocumentParams) {
|
||||||
|
let options = { self.options.lock().await.run };
|
||||||
|
if options < Run::OnType {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let content = params.content_changes.first().map(|c| c.text.clone());
|
let content = params.content_changes.first().map(|c| c.text.clone());
|
||||||
self.handle_file_update(params.text_document.uri, content).await;
|
self.handle_file_update(params.text_document.uri, content).await;
|
||||||
}
|
}
|
||||||
|
|
@ -192,6 +242,7 @@ async fn main() {
|
||||||
root_uri: OnceCell::new(),
|
root_uri: OnceCell::new(),
|
||||||
server_linter,
|
server_linter,
|
||||||
diagnostics_report_map,
|
diagnostics_report_map,
|
||||||
|
options: tokio::sync::Mutex::new(Options::default()),
|
||||||
})
|
})
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue