diff --git a/its/src/test/csharpsuite/csharp.test.ts b/its/src/test/csharpsuite/csharp.test.ts index d0196c2ce..def5c710c 100644 --- a/its/src/test/csharpsuite/csharp.test.ts +++ b/its/src/test/csharpsuite/csharp.test.ts @@ -54,7 +54,7 @@ suite('CSharp Test Suite', () => { const expectedActionTitles = [ 'SonarLint: Add comment', "SonarLint: Deactivate rule 'csharpsquid:S1186'", - "SonarLint: Open description of rule 'csharpsquid:S1186'", + "SonarLint: Show issue details for 'csharpsquid:S1186'", 'SonarLint: Throw NotSupportedException' ]; const actualCodeActionTitles = codeActionsResult diff --git a/its/src/test/javaSuite/java.test.ts b/its/src/test/javaSuite/java.test.ts index 448e14177..7b9a338ad 100644 --- a/its/src/test/javaSuite/java.test.ts +++ b/its/src/test/javaSuite/java.test.ts @@ -64,12 +64,12 @@ suite('Java Test Suite', () => { // Check that the exception-related diagnostic has 3 code actions const rangeInMiddleOfThrowsMyException = new vscode.Range(8, 54, 8, 54); - const codeActionsResult = (await vscode.commands.executeCommand<(vscode.Command | vscode.CodeAction)[]>('vscode.executeCodeActionProvider', document.uri, rangeInMiddleOfThrowsMyException, vscode.CodeActionKind.QuickFix.value))!; + const codeActionsResult = (await vscode.commands.executeCommand<(vscode.Command | vscode.CodeAction)[]>('vscode.executeCodeActionProvider', document.uri, rangeInMiddleOfThrowsMyException, vscode.CodeActionKind.QuickFix.value)); // With old versions of VSCode, code actions are not necessarily filtered on kind const expectedActionTitles = [ "SonarLint: Deactivate rule 'java:S1130'", - "SonarLint: Open description of rule 'java:S1130'", - 'SonarLint: Remove "MyException"' + "SonarLint: Show issue details for 'java:S1130'", + "SonarLint: Remove \"MyException\"" ]; const actualCodeActionTitles = codeActionsResult.filter(c => expectedActionTitles.indexOf(c.title) >= 0).map(c => c.title); // Order of code actions is not stable, forcing lexicographic order for assertion diff --git a/scripts/dependencies.json b/scripts/dependencies.json index 6032a9d67..700d0425b 100644 --- a/scripts/dependencies.json +++ b/scripts/dependencies.json @@ -2,7 +2,7 @@ { "groupId": "org.sonarsource.sonarlint.ls", "artifactId": "sonarlint-language-server", - "version": "3.11.1.75553", + "version": "3.12.0.75611", "output": "server/sonarlint-ls.jar" }, { diff --git a/src/issue/issue.ts b/src/issue/issue.ts index eef1af6b8..3c15ed5ab 100644 --- a/src/issue/issue.ts +++ b/src/issue/issue.ts @@ -130,10 +130,9 @@ export class IssueService { if (issue.shouldOpenRuleDescription) { await VSCode.commands.executeCommand( - 'SonarLint.OpenRuleDescCodeAction', + 'SonarLint.OpenRuleDesc', issue.ruleKey, - code2ProtocolConverter(documentUri), - '' + issue.fileUri, ); } } diff --git a/src/lsp/protocol.ts b/src/lsp/protocol.ts index ab595bfc7..60b48b509 100644 --- a/src/lsp/protocol.ts +++ b/src/lsp/protocol.ts @@ -11,7 +11,7 @@ import * as lsp from 'vscode-languageserver-protocol'; //#region Client side extensions to LSP export namespace ShowRuleDescriptionNotification { - export const type = new lsp.NotificationType('sonarlint/showRuleDescription'); + export const type = new lsp.NotificationType('sonarlint/showRuleDescription'); } export namespace SuggestBindingNotification { @@ -61,7 +61,7 @@ export interface ShowHotspotRuleDescriptionNotificationParams { fileUri: string; } -export interface ShowRuleDescriptionParams { +export interface ShowStandaloneRuleDescriptionParams { key: string; name: string; htmlDescription: string; @@ -92,6 +92,41 @@ export interface ShowRuleDescriptionParams { }>; } +export interface ShowRuleDescriptionParams { + key: string; + name: string; + htmlDescription: string; + htmlDescriptionTabs: Array<{ + title: string; + ruleDescriptionTabNonContextual?: { + htmlContent: string; + }; + ruleDescriptionTabContextual?: Array<{ + htmlContent: string; + contextKey: string; + displayName: string; + }>; + hasContextualInformation: boolean; + defaultContextKey?: string; + }>; + severityDetails: SeverityDetails; + languageKey: string; + isTaint: boolean; + parameters?: Array<{ + name: string; + description: string; + defaultValue: string; + }>; +} + +interface SeverityDetails { + severity?: string; + type?: string; + cleanCodeAttribute?: string; + cleanCodeAttributeCategory?: string; + impacts?: { [softwareQuality: string]: string }; +} + export namespace GetJavaConfigRequest { export const type = new lsp.RequestType('sonarlint/getJavaConfig'); } diff --git a/src/rules/rulepanel.ts b/src/rules/rulepanel.ts index 3e710b89b..0d11dff8b 100644 --- a/src/rules/rulepanel.ts +++ b/src/rules/rulepanel.ts @@ -89,8 +89,8 @@ function computeRuleDescPanelContent( } function renderCleanCodeAttribute(rule: ShowRuleDescriptionParams) { - const categoryLabel = escapeHtml(rule.cleanCodeAttributeCategory); - const attributeLabel = escapeHtml(rule.cleanCodeAttribute); + const categoryLabel = escapeHtml(rule.severityDetails.cleanCodeAttributeCategory); + const attributeLabel = escapeHtml(rule.severityDetails.cleanCodeAttribute); return `
${categoryLabel} issue ${attributeLabel} @@ -109,9 +109,9 @@ function renderImpact(softwareQuality: string, severity: string, resolver: Resou } function renderTaxonomyInfo(rule: ShowRuleDescriptionParams, resolver: ResourceResolver) { - if (rule.impacts && Object.keys(rule.impacts).length > 0) { + if (rule.severityDetails.impacts && Object.keys(rule.severityDetails.impacts).length > 0) { // Clean Code taxonomy - const renderedImpacts = Object.entries(rule.impacts).map(([softwareQuality, severity]) => renderImpact(softwareQuality, severity, resolver)); + const renderedImpacts = Object.entries(rule.severityDetails.impacts).map(([softwareQuality, severity]) => renderImpact(softwareQuality, severity, resolver)); return `
${renderCleanCodeAttribute(rule)}   @@ -123,16 +123,16 @@ function renderTaxonomyInfo(rule: ShowRuleDescriptionParams, resolver: ResourceR
`; } else { // Old type + severity taxonomy - const severityImgSrc = resolver.resolve('images', 'severity', `${rule.severity.toLowerCase()}.png`); - const typeImgSrc = resolver.resolve('images', 'type', `${rule.type.toLowerCase()}.png`); + const severityImgSrc = resolver.resolve('images', 'severity', `${rule.severityDetails.severity.toLowerCase()}.png`); + const typeImgSrc = resolver.resolve('images', 'type', `${rule.severityDetails.type.toLowerCase()}.png`); return `
- ${clean(rule.type)} - ${rule.type} + ${clean(rule.severityDetails.type)} + ${rule.severityDetails.type}
- ${clean(rule.severity)} - ${rule.severity} + ${clean(rule.severityDetails.severity)} + ${rule.severityDetails.severity}
`; } @@ -153,7 +153,7 @@ export function renderTaintBanner(rule: ShowRuleDescriptionParams, infoImgSrc: s } export function renderHotspotBanner(rule: ShowRuleDescriptionParams, infoImgSrc: string) { - if (rule.type !== 'SECURITY_HOTSPOT') { + if (rule.severityDetails.type !== 'SECURITY_HOTSPOT') { return ''; } return `
diff --git a/test/suite/rulepanel.test.ts b/test/suite/rulepanel.test.ts index cd3a120bf..3853fbcaf 100644 --- a/test/suite/rulepanel.test.ts +++ b/test/suite/rulepanel.test.ts @@ -23,8 +23,10 @@ suite('rulepanel', () => { isTaint: false, key: '', name: '', - severity: '', - type: '', + severityDetails: { + severity: '', + type: '' + }, languageKey: '', htmlDescription: 'monolithicDescription' }) @@ -56,9 +58,11 @@ suite('rulepanel', () => { isTaint: true, key: '', name: '', - severity: '', + severityDetails: { + severity: '', + type: '' + }, languageKey: '', - type: '', htmlDescription: 'contextualDescription' }); let inlineHtmlResult = htmlResult.replace(/(\r\n|\n|\r)/gm, ""); @@ -91,9 +95,11 @@ suite('rulepanel', () => { isTaint: false, key: '', name: '', - severity: '', + severityDetails: { + severity: '', + type: '' + }, languageKey: '', - type: '', htmlDescription: '' }) ).to.be.empty; @@ -103,8 +109,10 @@ suite('rulepanel', () => { isTaint: false, key: '', name: '', - severity: '', - type: '', + severityDetails: { + severity: '', + type: '' + }, languageKey: '', htmlDescription: '', parameters: [] @@ -119,8 +127,10 @@ suite('rulepanel', () => { isTaint: false, key: '', name: '', - severity: '', - type: '', + severityDetails: { + severity: '', + type: '' + }, languageKey: '', htmlDescription: '', parameters: [ @@ -142,8 +152,10 @@ suite('rulepanel', () => { isTaint: false, key: '', name: '', - severity: '', - type: 'NOT A HOTSPOT', + severityDetails: { + severity: '', + type: 'NOT_A_HOTSPOT' + }, languageKey: '', htmlDescription: '' }, @@ -160,9 +172,11 @@ suite('rulepanel', () => { isTaint: false, key: '', name: '', - severity: '', + severityDetails: { + severity: '', + type: 'SECURITY_HOTSPOT' + }, languageKey: '', - type: 'SECURITY_HOTSPOT', htmlDescription: '' }, '' @@ -178,9 +192,11 @@ suite('rulepanel', () => { isTaint: false, key: '', name: '', - severity: '', + severityDetails: { + severity: '', + type: '' + }, languageKey: '', - type: '', htmlDescription: '' }, '' @@ -196,9 +212,11 @@ suite('rulepanel', () => { isTaint: true, key: '', name: '', - severity: '', + severityDetails: { + severity: '', + type: '' + }, languageKey: '', - type: '', htmlDescription: '' }, ''