From bf1d9ecb641ce0c51062882cf853790c8155b57c Mon Sep 17 00:00:00 2001 From: Tamas Balog Date: Tue, 7 Mar 2023 12:48:01 +0100 Subject: [PATCH 1/3] Remove capability check for publishDiagnostics --- server/src/server.ts | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/server/src/server.ts b/server/src/server.ts index 7672d11..ecac592 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -50,7 +50,6 @@ const documents: TextDocuments = new TextDocuments(TextDocument); let hasConfigurationCapability: boolean; let hasWorkspaceCapability: boolean; let hasWorkspaceFoldersCapability: boolean; -let hasDiagnosticCapability: boolean; let hasApplyEditCapability: boolean; let hasCodeActionLiteralSupport: boolean; let hasCodeActionResolveSupport: boolean; @@ -64,6 +63,9 @@ let clientVersion: string | undefined; * * In case of VS Code, upon opening a different folder in the same window, the server is shut down, * and a new language client is initialized. + * + * The language server presumes that diagnostics are supported by the client application, otherwise the integration + * of the server would not make much sense, thus there is no check for the textDocument/publishDiagnostics capability. */ connection.onInitialize((_params: InitializeParams) => { //https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_didChangeConfiguration @@ -82,12 +84,6 @@ connection.onInitialize((_params: InitializeParams) => { cacheWorkspaceFolders(_params.rootUri ? [_params.rootUri] : []); } - //https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_publishDiagnostics - hasDiagnosticCapability = !!( - _params.capabilities.textDocument && - _params.capabilities.textDocument.publishDiagnostics - ); - //https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_executeCommand //https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_applyEdit hasApplyEditCapability = !!(hasWorkspaceCapability && _params.capabilities.workspace?.applyEdit); @@ -102,11 +98,6 @@ connection.onInitialize((_params: InitializeParams) => { hasCodeActionResolveSupport = !!(_params.capabilities.textDocument?.codeAction?.resolveSupport); hasCodeActionDataSupport = !!(_params.capabilities.textDocument?.codeAction?.dataSupport); - //If there is no support for diagnostics, which is the core functionality and purpose of the Rosie platform, - // return with no capability, and don't register any further event handler. - if (!hasDiagnosticCapability) - return { capabilities: {} }; - //Retrieves client information, so that we can use it in the User-Agent header of GraphQL requests clientName = _params.clientInfo?.name; clientVersion = _params.clientInfo?.version; @@ -280,11 +271,8 @@ connection.onInitialized(async () => { cacheCodigaApiToken(apiToken); } - //Start the rules cache updater only if the client supports diagnostics - if (hasDiagnosticCapability) { - setAllTextDocumentsValidator(() => documents.all().forEach(validateTextDocument)); - refreshCachePeriodic(); - } + setAllTextDocumentsValidator(() => documents.all().forEach(validateTextDocument)); + refreshCachePeriodic(); }); /** From 3b8b75a9da49e9123410be373e4b86388562e5e1 Mon Sep 17 00:00:00 2001 From: Tamas Balog Date: Tue, 7 Mar 2023 12:48:45 +0100 Subject: [PATCH 2/3] Handle CodeAction edit property resolution outside of codeAction/resolve in case a client application doesn't have support for that. --- server/src/diagnostics/ignore-violation.ts | 15 ++++++++---- server/src/rosie/rosiefix.ts | 15 ++++++++---- server/src/server.ts | 27 ++++++++++++++++++---- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/server/src/diagnostics/ignore-violation.ts b/server/src/diagnostics/ignore-violation.ts index 1e0a7c1..5ada50b 100644 --- a/server/src/diagnostics/ignore-violation.ts +++ b/server/src/diagnostics/ignore-violation.ts @@ -18,14 +18,15 @@ import { CodeActionParams } from 'vscode-languageserver'; export const provideIgnoreFixCodeActions = ( document: TextDocument, range: Range, - context: CodeActionParams + context: CodeActionParams, + shouldComputeEdit: boolean | undefined = false ): CodeAction[] => { const diagnostics = context.context.diagnostics .filter(diagnostic => diagnostic.source?.toLocaleString().indexOf(DIAGNOSTIC_SOURCE) != -1); const ignoreFixes: CodeAction[] = []; for (const diagnostic of diagnostics) { - ignoreFixes.push(createIgnoreFix(diagnostic, document)); + ignoreFixes.push(createIgnoreFix(diagnostic, document, shouldComputeEdit)); } return ignoreFixes; @@ -39,14 +40,15 @@ export const provideIgnoreFixCodeActions = ( */ export const createIgnoreFix = ( diagnostic: Diagnostic, - document: TextDocument + document: TextDocument, + shouldComputeEdit: boolean | undefined = false ): CodeAction => { const ruleIdentifier = diagnostic.code; const title = ruleIdentifier ? `Ignore rule ${ruleIdentifier}` : "Ignore rule"; - return { + const ignoreFix: CodeAction = { title: title, kind: CodeActionKind.QuickFix, /** @@ -73,6 +75,11 @@ export const createIgnoreFix = ( documentUri: document.uri } }; + if (shouldComputeEdit) { + // @ts-ignore + ignoreFix.edit = createIgnoreWorkspaceEdit(document, ignoreFix.diagnostics[0]?.range); + } + return ignoreFix; }; /** diff --git a/server/src/rosie/rosiefix.ts b/server/src/rosie/rosiefix.ts index 6c84394..7d1b2d8 100644 --- a/server/src/rosie/rosiefix.ts +++ b/server/src/rosie/rosiefix.ts @@ -121,11 +121,12 @@ const validateOffsetsAndCreateTextEdit = ( */ export const provideApplyFixCodeActions = ( document: TextDocument, - range: Range + range: Range, + shouldComputeEdit: boolean | undefined = false ): CodeAction[] => { const fixes = getFixesForDocument(document.uri, range); return fixes - ? fixes?.map(rosieFix => createRuleFix(document, rosieFix)) + ? fixes?.map(rosieFix => createRuleFix(document, rosieFix, shouldComputeEdit)) : []; }; @@ -139,13 +140,14 @@ export const provideApplyFixCodeActions = ( */ export const createRuleFix = ( document: TextDocument, - rosieFix: RosieFix + rosieFix: RosieFix, + shouldComputeEdit: boolean | undefined = false ): CodeAction => { /* From CodeAction's documentation: If a code action provides an edit and a command, first the edit is executed and then the command. */ - return { + const ruleFix: CodeAction = { title: `Fix: ${rosieFix.description}`, kind: CodeActionKind.QuickFix, //Registers the 'codiga.applyFix' command for this CodeAction, so that we can execute further @@ -165,4 +167,9 @@ export const createRuleFix = ( rosieFixEdits: rosieFix.edits } }; + if (shouldComputeEdit) { + const rosieFixEdits = ruleFix.data.rosieFixEdits as RosieFixEdit[]; + createAndSetRuleFixCodeActionEdit(ruleFix, document, rosieFixEdits); + } + return ruleFix; }; diff --git a/server/src/server.ts b/server/src/server.ts index ecac592..3350c60 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -57,6 +57,11 @@ let hasCodeActionDataSupport: boolean; let clientName: string | undefined; let clientVersion: string | undefined; +/** + * This is set to true for clients that don't support codeAction/resolve, + * or they support it, but they announce their support incorrectly, e.g. due to a bug. + */ +let shouldComputeEditInCodeAction: boolean | undefined = false; /** * Starts to initialize the language server. @@ -101,12 +106,20 @@ connection.onInitialize((_params: InitializeParams) => { //Retrieves client information, so that we can use it in the User-Agent header of GraphQL requests clientName = _params.clientInfo?.name; clientVersion = _params.clientInfo?.version; + //The condition for Eclipse can be removed, when + // https://github.com/eclipse/lsp4e/commit/2cf0a803936635a62d7fad2d05fde78bc7ce6a17 is released. + if (clientName?.startsWith("Eclipse IDE")) { + shouldComputeEditInCodeAction = true; + } /** * Runs when the configuration, e.g. the Codiga API Token changes. */ connection.onDidChangeConfiguration(async _change => { - cacheCodigaApiToken(_change.settings?.codiga?.api?.token); + if (_change.settings?.codiga?.api?.token) + cacheCodigaApiToken(_change.settings?.codiga?.api?.token); + else if (_change.settings?.codigaApiToken) + cacheCodigaApiToken(_change.settings?.codigaApiToken); documents.all().forEach(validateTextDocument); }); @@ -125,8 +138,8 @@ connection.onInitialize((_params: InitializeParams) => { if (hasApplyEditCapability && hasCodeActionLiteralSupport && params.context.diagnostics.length > 0) { const document = documents.get(params.textDocument.uri); if (document) { - codeActions.push(...provideApplyFixCodeActions(document, params.range)); - const ignoreFixes = provideIgnoreFixCodeActions(document, params.range, params); + codeActions.push(...provideApplyFixCodeActions(document, params.range, shouldComputeEditInCodeAction)); + const ignoreFixes = provideIgnoreFixCodeActions(document, params.range, params, shouldComputeEditInCodeAction); codeActions.push(...ignoreFixes); } } @@ -141,7 +154,7 @@ connection.onInitialize((_params: InitializeParams) => { * only when we actually need that information, kind of lazy evaluation. */ connection.onCodeActionResolve(codeAction => { - if (codeAction.data) { + if (!shouldComputeEditInCodeAction && codeAction.data) { if (codeAction.data.fixKind === "rosie.rule.fix") { const document = documents.get(codeAction.data.documentUri); if (document) { @@ -266,7 +279,11 @@ connection.onInitialized(async () => { - if `onDidChangeConfiguration()` cached the value in the meantime, we don't update the cache (e.g. Jupyter Lab) - if `onDidChangeConfiguration()` didn't cache the value, we use the returned value (e.g. VS Code) */ - const apiToken = await connection.workspace.getConfiguration("codiga.api.token"); + let apiToken = await connection.workspace.getConfiguration("codiga.api.token"); + if (!apiToken) { + apiToken = await connection.workspace.getConfiguration("codigaApiToken"); + } + if (!getApiToken()) { cacheCodigaApiToken(apiToken); } From 1937557b21d611110bc54406c530d7c5b3f88eac Mon Sep 17 00:00:00 2001 From: Tamas Balog Date: Tue, 7 Mar 2023 12:59:03 +0100 Subject: [PATCH 3/3] Rename variable --- server/src/server.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/src/server.ts b/server/src/server.ts index 3350c60..63bb947 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -61,7 +61,7 @@ let clientVersion: string | undefined; * This is set to true for clients that don't support codeAction/resolve, * or they support it, but they announce their support incorrectly, e.g. due to a bug. */ -let shouldComputeEditInCodeAction: boolean | undefined = false; +let isShouldComputeEditInCodeAction: boolean | undefined = false; /** * Starts to initialize the language server. @@ -109,7 +109,7 @@ connection.onInitialize((_params: InitializeParams) => { //The condition for Eclipse can be removed, when // https://github.com/eclipse/lsp4e/commit/2cf0a803936635a62d7fad2d05fde78bc7ce6a17 is released. if (clientName?.startsWith("Eclipse IDE")) { - shouldComputeEditInCodeAction = true; + isShouldComputeEditInCodeAction = true; } /** @@ -138,8 +138,8 @@ connection.onInitialize((_params: InitializeParams) => { if (hasApplyEditCapability && hasCodeActionLiteralSupport && params.context.diagnostics.length > 0) { const document = documents.get(params.textDocument.uri); if (document) { - codeActions.push(...provideApplyFixCodeActions(document, params.range, shouldComputeEditInCodeAction)); - const ignoreFixes = provideIgnoreFixCodeActions(document, params.range, params, shouldComputeEditInCodeAction); + codeActions.push(...provideApplyFixCodeActions(document, params.range, isShouldComputeEditInCodeAction)); + const ignoreFixes = provideIgnoreFixCodeActions(document, params.range, params, isShouldComputeEditInCodeAction); codeActions.push(...ignoreFixes); } } @@ -154,7 +154,7 @@ connection.onInitialize((_params: InitializeParams) => { * only when we actually need that information, kind of lazy evaluation. */ connection.onCodeActionResolve(codeAction => { - if (!shouldComputeEditInCodeAction && codeAction.data) { + if (!isShouldComputeEditInCodeAction && codeAction.data) { if (codeAction.data.fixKind === "rosie.rule.fix") { const document = documents.get(codeAction.data.documentUri); if (document) {