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 {
Executable,
@ -19,10 +28,13 @@ const enum OxcCommands {
ApplyAllFixes = "oxc.applyAllFixes",
ShowOutputChannel = "oxc.showOutputChannel",
ShowTraceOutputChannel = "oxc.showTraceOutputChannel",
ToggleEnable = "oxc.toggleEnable",
}
let client: LanguageClient;
let myStatusBarItem: StatusBarItem;
export async function activate(context: ExtensionContext) {
const restartCommand = commands.registerCommand(
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(
restartCommand,
showOutputCommand,
showTraceOutputCommand,
toggleEnable,
);
const outputChannel = window.createOutputChannel(outputChannelName);
@ -101,7 +125,10 @@ export async function activate(context: ExtensionContext) {
"javascript",
"typescriptreact",
"javascriptreact",
].map((lang) => ({ language: lang, scheme: "file" })),
].map((lang) => ({
language: lang,
scheme: "file",
})),
synchronize: {
// Notify the server about file changes to '.clientrc files contained in the workspace
fileEvents: workspace.createFileSystemWatcher("**/.clientrc"),
@ -121,15 +148,34 @@ export async function activate(context: ExtensionContext) {
clientOptions,
);
workspace.onDidChangeConfiguration((e) => {
let settings: any = {};
if (e.affectsConfiguration("oxc-client.run")) {
settings.run = workspace.getConfiguration("oxc-client").get("run");
}
let settings: any = JSON.parse(
JSON.stringify(workspace.getConfiguration("oxc-client")),
);
updateStatsBar(settings.enable);
client.sendNotification("workspace/didChangeConfiguration", {
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();
}

View file

@ -42,6 +42,11 @@
"title": "Restart Oxc Server",
"category": "Oxc"
},
{
"command": "oxc.toggleEnable",
"title": "toggle enable",
"category": "Oxc"
},
{
"command": "oxc.showOutputChannel",
"title": "Show Output Channel",
@ -67,6 +72,11 @@
"default": "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": {
"type": "string",
"scope": "window",

View file

@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt::Debug;
use std::path::PathBuf;
use std::str::FromStr;
use dashmap::DashMap;
use futures::future::join_all;
@ -17,11 +18,11 @@ use tower_lsp::jsonrpc::{Error, ErrorCode, Result};
use tower_lsp::lsp_types::{
CodeAction, CodeActionKind, CodeActionOptions, CodeActionOrCommand, CodeActionParams,
CodeActionProviderCapability, CodeActionResponse, Diagnostic, DidChangeConfigurationParams,
DidChangeTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams,
InitializeParams, InitializeResult, InitializedParams, MessageType, OneOf, Registration,
ServerCapabilities, ServerInfo, TextDocumentSyncCapability, TextDocumentSyncKind, TextEdit,
Url, WorkDoneProgressOptions, WorkspaceEdit, WorkspaceFoldersServerCapabilities,
WorkspaceServerCapabilities,
DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams,
DidSaveTextDocumentParams, InitializeParams, InitializeResult, InitializedParams, MessageType,
OneOf, Registration, ServerCapabilities, ServerInfo, TextDocumentSyncCapability,
TextDocumentSyncKind, TextEdit, Url, WorkDoneProgressOptions, WorkspaceEdit,
WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
};
use tower_lsp::{Client, LanguageServer, LspService, Server};
@ -43,6 +44,27 @@ enum Run {
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
struct Options {
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]
@ -94,6 +116,25 @@ impl LanguageServer for Backend {
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;
}
@ -121,8 +162,8 @@ impl LanguageServer for Backend {
async fn did_save(&self, params: DidSaveTextDocumentParams) {
debug!("oxc server did save");
// drop as fast as possible
let options = { self.options.lock().await.run };
if options < Run::OnSave {
let run_level = { self.options.lock().await.get_lint_level() };
if run_level < SyntheticRunLevel::OnSave {
return;
}
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
/// get the file context from the language client
async fn did_change(&self, params: DidChangeTextDocumentParams) {
let options = { self.options.lock().await.run };
if options < Run::OnType {
let run_level = { self.options.lock().await.get_lint_level() };
if run_level < SyntheticRunLevel::OnType {
return;
}
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) {
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;
}
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>> {
let uri = params.text_document.uri;
@ -199,6 +249,7 @@ impl Backend {
#[allow(clippy::ptr_arg)]
async fn publish_all_diagnostics(&self, result: &Vec<(PathBuf, Vec<Diagnostic>)>) {
debug!("{:?}", result);
join_all(result.iter().map(|(path, diagnostics)| {
self.client.publish_diagnostics(
Url::from_file_path(path).unwrap(),