feat: add option to control enable/disable oxc linter (#1665)

1. Closed https://github.com/oxc-project/oxc/issues/1655
This commit is contained in:
IWANABETHATGUY 2023-12-14 12:45:34 +08:00 committed by GitHub
parent d719af473c
commit e529b38e32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 122 additions and 15 deletions

View file

@ -1,4 +1,13 @@
import { ExtensionContext, window, commands, workspace } from "vscode"; import {
ExtensionContext,
window,
commands,
workspace,
StatusBarItem,
StatusBarAlignment,
ConfigurationTarget,
ThemeColor,
} from "vscode";
import { import {
Executable, Executable,
@ -19,10 +28,13 @@ const enum OxcCommands {
ApplyAllFixes = "oxc.applyAllFixes", ApplyAllFixes = "oxc.applyAllFixes",
ShowOutputChannel = "oxc.showOutputChannel", ShowOutputChannel = "oxc.showOutputChannel",
ShowTraceOutputChannel = "oxc.showTraceOutputChannel", ShowTraceOutputChannel = "oxc.showTraceOutputChannel",
ToggleEnable = "oxc.toggleEnable",
} }
let client: LanguageClient; let client: LanguageClient;
let myStatusBarItem: StatusBarItem;
export async function activate(context: ExtensionContext) { export async function activate(context: ExtensionContext) {
const restartCommand = commands.registerCommand( const restartCommand = commands.registerCommand(
OxcCommands.RestartServer, OxcCommands.RestartServer,
@ -60,10 +72,22 @@ export async function activate(context: ExtensionContext) {
}, },
); );
const toggleEnable = commands.registerCommand(
OxcCommands.ToggleEnable,
() => {
let enabled = workspace.getConfiguration("oxc-client").get("enable");
let nextState = !enabled;
workspace
.getConfiguration("oxc-client")
.update("enable", nextState, ConfigurationTarget.Global);
},
);
context.subscriptions.push( context.subscriptions.push(
restartCommand, restartCommand,
showOutputCommand, showOutputCommand,
showTraceOutputCommand, showTraceOutputCommand,
toggleEnable,
); );
const outputChannel = window.createOutputChannel(outputChannelName); const outputChannel = window.createOutputChannel(outputChannelName);
@ -101,7 +125,10 @@ export async function activate(context: ExtensionContext) {
"javascript", "javascript",
"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"),
@ -121,15 +148,34 @@ export async function activate(context: ExtensionContext) {
clientOptions, clientOptions,
); );
workspace.onDidChangeConfiguration((e) => { workspace.onDidChangeConfiguration((e) => {
let settings: any = {}; let settings: any = JSON.parse(
if (e.affectsConfiguration("oxc-client.run")) { JSON.stringify(workspace.getConfiguration("oxc-client")),
settings.run = workspace.getConfiguration("oxc-client").get("run"); );
} updateStatsBar(settings.enable);
client.sendNotification("workspace/didChangeConfiguration", { client.sendNotification("workspace/didChangeConfiguration", {
settings, settings,
}); });
}); });
function updateStatsBar(enable: boolean) {
if (!myStatusBarItem) {
myStatusBarItem = window.createStatusBarItem(
StatusBarAlignment.Right,
100,
);
myStatusBarItem.command = OxcCommands.ToggleEnable;
context.subscriptions.push(myStatusBarItem);
myStatusBarItem.show();
}
let bgColor = new ThemeColor(
enable
? "statusBarItem.activeBackground"
: "statusBarItem.errorBackground",
);
myStatusBarItem.text = `oxc: ${enable ? "on" : "off"}`;
myStatusBarItem.backgroundColor = bgColor;
}
updateStatsBar(clientConfig.enable);
client.start(); client.start();
} }

View file

@ -42,6 +42,11 @@
"title": "Restart Oxc Server", "title": "Restart Oxc Server",
"category": "Oxc" "category": "Oxc"
}, },
{
"command": "oxc.toggleEnable",
"title": "toggle enable",
"category": "Oxc"
},
{ {
"command": "oxc.showOutputChannel", "command": "oxc.showOutputChannel",
"title": "Show Output Channel", "title": "Show Output Channel",
@ -67,6 +72,11 @@
"default": "onType", "default": "onType",
"description": "Run the linter on save (onSave) or on type (onType)" "description": "Run the linter on save (onSave) or on type (onType)"
}, },
"oxc-client.enable": {
"type": "boolean",
"default": true,
"description": "enable oxc linter"
},
"oxc-client.trace.server": { "oxc-client.trace.server": {
"type": "string", "type": "string",
"scope": "window", "scope": "window",

View file

@ -9,6 +9,7 @@ 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 std::str::FromStr;
use dashmap::DashMap; use dashmap::DashMap;
use futures::future::join_all; use futures::future::join_all;
@ -17,11 +18,11 @@ 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, DidChangeConfigurationParams, CodeActionProviderCapability, CodeActionResponse, Diagnostic, DidChangeConfigurationParams,
DidChangeTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams, DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams,
InitializeParams, InitializeResult, InitializedParams, MessageType, OneOf, Registration, DidSaveTextDocumentParams, InitializeParams, InitializeResult, InitializedParams, MessageType,
ServerCapabilities, ServerInfo, TextDocumentSyncCapability, TextDocumentSyncKind, TextEdit, OneOf, Registration, ServerCapabilities, ServerInfo, TextDocumentSyncCapability,
Url, WorkDoneProgressOptions, WorkspaceEdit, WorkspaceFoldersServerCapabilities, TextDocumentSyncKind, TextEdit, Url, WorkDoneProgressOptions, WorkspaceEdit,
WorkspaceServerCapabilities, WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
}; };
use tower_lsp::{Client, LanguageServer, LspService, Server}; use tower_lsp::{Client, LanguageServer, LspService, Server};
@ -43,6 +44,27 @@ enum Run {
#[derive(Debug, Serialize, Deserialize, Default, Clone)] #[derive(Debug, Serialize, Deserialize, Default, Clone)]
struct Options { struct Options {
run: Run, run: Run,
enable: bool,
}
impl Options {
fn get_lint_level(&self) -> SyntheticRunLevel {
if self.enable {
match self.run {
Run::OnSave => SyntheticRunLevel::OnSave,
Run::OnType => SyntheticRunLevel::OnType,
}
} else {
SyntheticRunLevel::Disable
}
}
}
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
enum SyntheticRunLevel {
Disable,
OnSave,
OnType,
} }
#[tower_lsp::async_trait] #[tower_lsp::async_trait]
@ -94,6 +116,25 @@ impl LanguageServer for Backend {
return; return;
} }
}; };
debug!("{:?}", &changed_options.get_lint_level());
if changed_options.get_lint_level() == SyntheticRunLevel::Disable {
// clear all exists diagnostics when linter is disabled
let opened_files = self.diagnostics_report_map.iter().map(|k| k.key().to_string());
let cleared_diagnostics = opened_files
.into_iter()
.map(|uri| {
(
// should convert successfully, case the key is from `params.document.uri`
Url::from_str(&uri)
.ok()
.and_then(|url| url.to_file_path().ok())
.expect("should convert to path"),
vec![],
)
})
.collect::<Vec<_>>();
self.publish_all_diagnostics(&cleared_diagnostics).await;
}
*self.options.lock().await = changed_options; *self.options.lock().await = changed_options;
} }
@ -121,8 +162,8 @@ 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 // drop as fast as possible
let options = { self.options.lock().await.run }; let run_level = { self.options.lock().await.get_lint_level() };
if options < Run::OnSave { if run_level < SyntheticRunLevel::OnSave {
return; return;
} }
self.handle_file_update(params.text_document.uri, None).await; self.handle_file_update(params.text_document.uri, None).await;
@ -131,8 +172,8 @@ impl LanguageServer for Backend {
/// 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 }; let run_level = { self.options.lock().await.get_lint_level() };
if options < Run::OnType { if run_level < SyntheticRunLevel::OnType {
return; return;
} }
let content = params.content_changes.first().map(|c| c.text.clone()); let content = params.content_changes.first().map(|c| c.text.clone());
@ -140,9 +181,18 @@ impl LanguageServer for Backend {
} }
async fn did_open(&self, params: DidOpenTextDocumentParams) { async fn did_open(&self, params: DidOpenTextDocumentParams) {
let run_level = { self.options.lock().await.get_lint_level() };
if run_level < SyntheticRunLevel::OnType {
return;
}
self.handle_file_update(params.text_document.uri, None).await; self.handle_file_update(params.text_document.uri, None).await;
} }
async fn did_close(&self, params: DidCloseTextDocumentParams) {
let uri = params.text_document.uri.to_string();
self.diagnostics_report_map.remove(&uri);
}
async fn code_action(&self, params: CodeActionParams) -> Result<Option<CodeActionResponse>> { async fn code_action(&self, params: CodeActionParams) -> Result<Option<CodeActionResponse>> {
let uri = params.text_document.uri; let uri = params.text_document.uri;
@ -199,6 +249,7 @@ impl Backend {
#[allow(clippy::ptr_arg)] #[allow(clippy::ptr_arg)]
async fn publish_all_diagnostics(&self, result: &Vec<(PathBuf, Vec<Diagnostic>)>) { async fn publish_all_diagnostics(&self, result: &Vec<(PathBuf, Vec<Diagnostic>)>) {
debug!("{:?}", result);
join_all(result.iter().map(|(path, diagnostics)| { join_all(result.iter().map(|(path, diagnostics)| {
self.client.publish_diagnostics( self.client.publish_diagnostics(
Url::from_file_path(path).unwrap(), Url::from_file_path(path).unwrap(),