Add unused diagnostic subtype (#49646)
* Add unused diagnostic subtype Fixes #15710 Adds a new `DiagnosticTag` class that provide additional information about a diagnostic. Introduce `DiagnosticTag.Unnecessary` to mark when a diagnostic is for unused / unnecessary code The design comes from Rosyln's diagnostic object and allows us to modify how a diagnostic is rendered without changing its serverity. Hooks up JS and TS to use this new tag. This is controlled by the `javascript.showUnused.enabled` setting which is enabled by default - Introduce a new diagnostic severity for unused. However, using this approach, if a user sets `noUnusedLocals` in their `tsconfig.json`, the resulting diagnostic could only show the squiggly OR be grayed out. Using `customTags` allows us to support both graying out and showing the squiggly - Custom JS/TS implementation using decorators Not themable. We want a standard experience across languages. * - Move to proposed - Use numeric enum
This commit is contained in:
parent
0f7ac44247
commit
8bb27cd255
17 changed files with 92 additions and 90 deletions
|
@ -32,6 +32,10 @@ declare namespace monaco {
|
|||
Error = 3,
|
||||
}
|
||||
|
||||
export enum MarkerTag {
|
||||
Unnecessary = 1,
|
||||
}
|
||||
|
||||
export enum MarkerSeverity {
|
||||
Hint = 1,
|
||||
Info = 2,
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { DiagnosticSet } from './diagnostics';
|
||||
|
||||
|
||||
export class UnusedHighlighter {
|
||||
|
||||
private readonly _decorationType: vscode.TextEditorDecorationType;
|
||||
|
||||
private readonly _diagnostics = new DiagnosticSet();
|
||||
private _validate: boolean = true;
|
||||
|
||||
constructor(
|
||||
) {
|
||||
this._decorationType = vscode.window.createTextEditorDecorationType({
|
||||
opacity: '0.7'
|
||||
});
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this._decorationType.dispose();
|
||||
}
|
||||
|
||||
public reInitialize(): void {
|
||||
this._diagnostics.clear();
|
||||
|
||||
for (const editor of vscode.window.visibleTextEditors) {
|
||||
editor.setDecorations(this._decorationType, []);
|
||||
}
|
||||
}
|
||||
|
||||
public set validate(value: boolean) {
|
||||
if (this._validate === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._validate = value;
|
||||
if (!value) {
|
||||
for (const editor of vscode.window.visibleTextEditors) {
|
||||
editor.setDecorations(this._decorationType, []);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public diagnosticsReceived(
|
||||
file: vscode.Uri,
|
||||
diagnostics: vscode.Diagnostic[]
|
||||
): void {
|
||||
// Undocumented flag to enable
|
||||
if (!vscode.workspace.getConfiguration('typescript').get('showUnused.experimentalFade')) {
|
||||
return;
|
||||
}
|
||||
this._diagnostics.set(file, diagnostics);
|
||||
this._updateCurrentHighlights(file);
|
||||
}
|
||||
|
||||
private _updateCurrentHighlights(file: vscode.Uri) {
|
||||
for (const editor of vscode.window.visibleTextEditors) {
|
||||
if (editor.document.uri.fsPath !== file.fsPath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const diagnostics = this._diagnostics.get(editor.document.uri);
|
||||
if (diagnostics) {
|
||||
editor.setDecorations(this._decorationType, diagnostics.map(x => x.range));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,7 +21,6 @@ import { CachedNavTreeResponse } from './features/baseCodeLensProvider';
|
|||
import { memoize } from './utils/memoize';
|
||||
import { disposeAll } from './utils/dispose';
|
||||
import TelemetryReporter from './utils/telemetry';
|
||||
import { UnusedHighlighter } from './features/unusedHighlighter';
|
||||
|
||||
const validateSetting = 'validate.enable';
|
||||
const suggestionSetting = 'suggestionActions.enabled';
|
||||
|
@ -30,7 +29,6 @@ const foldingSetting = 'typescript.experimental.syntaxFolding';
|
|||
export default class LanguageProvider {
|
||||
private readonly diagnosticsManager: DiagnosticsManager;
|
||||
private readonly bufferSyncSupport: BufferSyncSupport;
|
||||
private readonly ununsedHighlighter: UnusedHighlighter;
|
||||
private readonly fileConfigurationManager: FileConfigurationManager;
|
||||
|
||||
private readonly toUpdateOnConfigurationChanged: ({ updateConfiguration: () => void })[] = [];
|
||||
|
@ -58,7 +56,6 @@ export default class LanguageProvider {
|
|||
}, this._validate);
|
||||
|
||||
this.diagnosticsManager = new DiagnosticsManager(description.diagnosticOwner);
|
||||
this.ununsedHighlighter = new UnusedHighlighter();
|
||||
|
||||
workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables);
|
||||
this.configurationChanged();
|
||||
|
@ -229,7 +226,6 @@ export default class LanguageProvider {
|
|||
|
||||
public reInitialize(): void {
|
||||
this.diagnosticsManager.reInitialize();
|
||||
this.ununsedHighlighter.reInitialize();
|
||||
this.bufferSyncSupport.reOpenDocuments();
|
||||
this.bufferSyncSupport.requestAllDiagnostics();
|
||||
this.fileConfigurationManager.reset();
|
||||
|
@ -265,9 +261,6 @@ export default class LanguageProvider {
|
|||
public diagnosticsReceived(diagnosticsKind: DiagnosticKind, file: Uri, diagnostics: (Diagnostic & { reportUnnecessary: any })[]): void {
|
||||
const config = workspace.getConfiguration(this.id, file);
|
||||
const reportUnnecessary = config.get<boolean>('showUnused.enabled', true);
|
||||
if (diagnosticsKind === DiagnosticKind.Suggestion) {
|
||||
this.ununsedHighlighter.diagnosticsReceived(file, diagnostics.filter(diag => diag.reportUnnecessary));
|
||||
}
|
||||
this.diagnosticsManager.diagnosticsReceived(diagnosticsKind, file, diagnostics.filter(diag => diag.reportUnnecessary ? reportUnnecessary : true));
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
* https://github.com/Microsoft/TypeScript-Sublime-Plugin/blob/master/TypeScript%20Indent.tmPreferences
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import { workspace, Memento, Diagnostic, Range, Disposable, Uri, DiagnosticSeverity } from 'vscode';
|
||||
import { workspace, Memento, Diagnostic, Range, Disposable, Uri, DiagnosticSeverity, DiagnosticTag } from 'vscode';
|
||||
|
||||
import * as Proto from './protocol';
|
||||
import * as PConst from './protocol.const';
|
||||
|
@ -256,6 +256,9 @@ export default class TypeScriptServiceClientHost {
|
|||
if (diagnostic.code) {
|
||||
converted.code = diagnostic.code;
|
||||
}
|
||||
if (diagnostic.reportsUnnecessary) {
|
||||
converted.customTags = [DiagnosticTag.Unnecessary];
|
||||
}
|
||||
(converted as Diagnostic & { reportUnnecessary: any }).reportUnnecessary = diagnostic.reportsUnnecessary;
|
||||
return converted as Diagnostic & { reportUnnecessary: any };
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
|||
import { InternalEditorAction } from 'vs/editor/common/editorAction';
|
||||
import { ICommandDelegate } from 'vs/editor/browser/view/viewController';
|
||||
import { CoreEditorCommand } from 'vs/editor/browser/controller/coreCommands';
|
||||
import { editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder, editorInfoBorder, editorInfoForeground, editorHintForeground, editorHintBorder } from 'vs/editor/common/view/editorColorRegistry';
|
||||
import { editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder, editorInfoBorder, editorInfoForeground, editorHintForeground, editorHintBorder, editorUnnecessaryForeground } from 'vs/editor/common/view/editorColorRegistry';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { ClassName } from 'vs/editor/common/model/intervalTree';
|
||||
|
||||
|
@ -1795,4 +1795,9 @@ registerThemingParticipant((theme, collector) => {
|
|||
if (hintForeground) {
|
||||
collector.addRule(`.monaco-editor .${ClassName.EditorHintDecoration} { background: url("data:image/svg+xml,${getDotDotDotSVGData(hintForeground)}") no-repeat bottom left; }`);
|
||||
}
|
||||
|
||||
const unnecessaryForeground = theme.getColor(editorUnnecessaryForeground);
|
||||
if (unnecessaryForeground) {
|
||||
collector.addRule(`.monaco-editor .${ClassName.EditorUnnecessaryDecoration} { color: ${unnecessaryForeground}; }`);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -16,7 +16,8 @@ export const ClassName = {
|
|||
EditorHintDecoration: 'squiggly-hint',
|
||||
EditorInfoDecoration: 'squiggly-info',
|
||||
EditorWarningDecoration: 'squiggly-warning',
|
||||
EditorErrorDecoration: 'squiggly-error'
|
||||
EditorErrorDecoration: 'squiggly-error',
|
||||
EditorUnnecessaryDecoration: 'squiggly-overlay-unnecessary'
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,7 +11,7 @@ import { MarkdownString } from 'vs/base/common/htmlContent';
|
|||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IMarker, IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
import { IMarker, IMarkerService, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel';
|
||||
import { IMode, LanguageIdentifier } from 'vs/editor/common/modes';
|
||||
|
@ -127,10 +127,13 @@ class ModelMarkerHandler {
|
|||
let color: ThemeColor;
|
||||
let darkColor: ThemeColor;
|
||||
let zIndex: number;
|
||||
let inlineClassName: string;
|
||||
|
||||
switch (marker.severity) {
|
||||
case MarkerSeverity.Hint:
|
||||
className = ClassName.EditorHintDecoration;
|
||||
if (!marker.customTags || marker.customTags.indexOf(MarkerTag.Unnecessary) === -1) {
|
||||
className = ClassName.EditorHintDecoration;
|
||||
}
|
||||
zIndex = 0;
|
||||
break;
|
||||
case MarkerSeverity.Warning:
|
||||
|
@ -154,6 +157,12 @@ class ModelMarkerHandler {
|
|||
break;
|
||||
}
|
||||
|
||||
if (marker.customTags) {
|
||||
if (marker.customTags.indexOf(MarkerTag.Unnecessary) !== -1) {
|
||||
inlineClassName = ClassName.EditorUnnecessaryDecoration;
|
||||
}
|
||||
}
|
||||
|
||||
let hoverMessage: MarkdownString = null;
|
||||
let { message, source, relatedInformation } = marker;
|
||||
|
||||
|
@ -193,7 +202,8 @@ class ModelMarkerHandler {
|
|||
darkColor,
|
||||
position: OverviewRulerLane.Right
|
||||
},
|
||||
zIndex
|
||||
zIndex,
|
||||
inlineClassName,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ export enum Severity {
|
|||
Error = 3,
|
||||
}
|
||||
|
||||
export enum MarkerTag {
|
||||
Unnecessary = 1,
|
||||
}
|
||||
|
||||
export enum MarkerSeverity {
|
||||
Hint = 1,
|
||||
Info = 2,
|
||||
|
@ -246,6 +250,7 @@ export function createMonacoBaseAPI(): typeof monaco {
|
|||
SelectionDirection: SelectionDirection,
|
||||
Severity: Severity,
|
||||
MarkerSeverity: MarkerSeverity,
|
||||
MarkerTag: MarkerTag,
|
||||
Promise: TPromise,
|
||||
Uri: <any>URI,
|
||||
Token: Token
|
||||
|
|
|
@ -49,6 +49,8 @@ export const editorInfoBorder = registerColor('editorInfo.border', { dark: null,
|
|||
export const editorHintForeground = registerColor('editorHint.foreground', { dark: Color.fromHex('#eeeeee').transparent(0.7), light: '#6c6c6c', hc: null }, nls.localize('hintForeground', 'Foreground color of hint squigglies in the editor.'));
|
||||
export const editorHintBorder = registerColor('editorHint.border', { dark: null, light: null, hc: Color.fromHex('#eeeeee').transparent(0.8) }, nls.localize('hintBorder', 'Border color of hint squigglies in the editor.'));
|
||||
|
||||
export const editorUnnecessaryForeground = registerColor('editorUnnecessary.foreground', { dark: Color.fromHex('#eeeeee').transparent(0.7), light: '#6c6c6c', hc: null }, nls.localize('unnecessaryForeground', 'Foreground color of unnecessary code in the editor.'));
|
||||
|
||||
const rulerRangeDefault = new Color(new RGBA(0, 122, 204, 0.6));
|
||||
export const overviewRulerRangeHighlight = registerColor('editorOverviewRuler.rangeHighlightForeground', { dark: rulerRangeDefault, light: rulerRangeDefault, hc: rulerRangeDefault }, nls.localize('overviewRulerRangeHighlight', 'Overview ruler marker color for range highlights. The color must not be opaque to not hide underlying decorations.'), true);
|
||||
export const overviewRulerError = registerColor('editorOverviewRuler.errorForeground', { dark: new Color(new RGBA(255, 18, 18, 0.7)), light: new Color(new RGBA(255, 18, 18, 0.7)), hc: new Color(new RGBA(255, 50, 50, 1)) }, nls.localize('overviewRuleError', 'Overview ruler marker color for errors.'));
|
||||
|
|
|
@ -37,6 +37,7 @@ export const Selection = api.Selection;
|
|||
export const SelectionDirection = api.SelectionDirection;
|
||||
export const Severity = api.Severity;
|
||||
export const MarkerSeverity = api.MarkerSeverity;
|
||||
export const MarkerTag = api.MarkerTag;
|
||||
export const Promise = api.Promise;
|
||||
export const Uri = api.Uri;
|
||||
export const Token = api.Token;
|
||||
|
|
6
src/vs/monaco.d.ts
vendored
6
src/vs/monaco.d.ts
vendored
|
@ -32,6 +32,10 @@ declare namespace monaco {
|
|||
Error = 3,
|
||||
}
|
||||
|
||||
export enum MarkerTag {
|
||||
Unnecessary = 1,
|
||||
}
|
||||
|
||||
export enum MarkerSeverity {
|
||||
Hint = 1,
|
||||
Info = 2,
|
||||
|
@ -1089,6 +1093,7 @@ declare namespace monaco.editor {
|
|||
endLineNumber: number;
|
||||
endColumn: number;
|
||||
relatedInformation?: IRelatedInformation[];
|
||||
customTags?: MarkerTag[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1104,6 +1109,7 @@ declare namespace monaco.editor {
|
|||
endLineNumber: number;
|
||||
endColumn: number;
|
||||
relatedInformation?: IRelatedInformation[];
|
||||
customTags?: MarkerTag[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -183,7 +183,8 @@ export class MarkerService implements IMarkerService {
|
|||
code, severity,
|
||||
message, source,
|
||||
startLineNumber, startColumn, endLineNumber, endColumn,
|
||||
relatedInformation
|
||||
relatedInformation,
|
||||
customTags,
|
||||
} = data;
|
||||
|
||||
if (!message) {
|
||||
|
@ -208,7 +209,8 @@ export class MarkerService implements IMarkerService {
|
|||
startColumn,
|
||||
endLineNumber,
|
||||
endColumn,
|
||||
relatedInformation
|
||||
relatedInformation,
|
||||
customTags,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,10 @@ export interface IRelatedInformation {
|
|||
endColumn: number;
|
||||
}
|
||||
|
||||
export enum MarkerTag {
|
||||
Unnecessary = 1,
|
||||
}
|
||||
|
||||
export enum MarkerSeverity {
|
||||
Hint = 1,
|
||||
Info = 2,
|
||||
|
@ -83,6 +87,7 @@ export interface IMarkerData {
|
|||
endLineNumber: number;
|
||||
endColumn: number;
|
||||
relatedInformation?: IRelatedInformation[];
|
||||
customTags?: MarkerTag[];
|
||||
}
|
||||
|
||||
export interface IResourceMarker {
|
||||
|
@ -102,6 +107,7 @@ export interface IMarker {
|
|||
endLineNumber: number;
|
||||
endColumn: number;
|
||||
relatedInformation?: IRelatedInformation[];
|
||||
customTags?: MarkerTag[];
|
||||
}
|
||||
|
||||
export interface MarkerStatistics {
|
||||
|
|
20
src/vs/vscode.proposed.d.ts
vendored
20
src/vs/vscode.proposed.d.ts
vendored
|
@ -620,4 +620,24 @@ declare module 'vscode' {
|
|||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region mjbvz: Unused diagnostics
|
||||
/**
|
||||
* Additional metadata about the type of diagnostic.
|
||||
*/
|
||||
export enum DiagnosticTag {
|
||||
/**
|
||||
* Unused or unnecessary code.
|
||||
*/
|
||||
Unnecessary = 1,
|
||||
}
|
||||
|
||||
export interface Diagnostic {
|
||||
/**
|
||||
* Additional metadata about the type of the diagnostic.
|
||||
*/
|
||||
customTags?: DiagnosticTag[];
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
|
|
@ -677,6 +677,7 @@ export function createApiFactory(
|
|||
DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable,
|
||||
Diagnostic: extHostTypes.Diagnostic,
|
||||
DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation,
|
||||
DiagnosticTag: extHostTypes.DiagnosticTag,
|
||||
DiagnosticSeverity: extHostTypes.DiagnosticSeverity,
|
||||
Disposable: extHostTypes.Disposable,
|
||||
DocumentHighlight: extHostTypes.DocumentHighlight,
|
||||
|
|
|
@ -20,7 +20,7 @@ import * as htmlContent from 'vs/base/common/htmlContent';
|
|||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import * as languageSelector from 'vs/editor/common/modes/languageSelector';
|
||||
import { WorkspaceEditDto, ResourceTextEditDto } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { MarkerSeverity, IRelatedInformation, IMarkerData } from 'vs/platform/markers/common/markers';
|
||||
import { MarkerSeverity, IRelatedInformation, IMarkerData, MarkerTag } from 'vs/platform/markers/common/markers';
|
||||
|
||||
export interface PositionLike {
|
||||
line: number;
|
||||
|
@ -88,6 +88,16 @@ export namespace Position {
|
|||
}
|
||||
}
|
||||
|
||||
export namespace DiagnosticTag {
|
||||
export function from(value: vscode.DiagnosticTag): MarkerTag {
|
||||
switch (value) {
|
||||
case types.DiagnosticTag.Unnecessary:
|
||||
return MarkerTag.Unnecessary;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Diagnostic {
|
||||
export function from(value: vscode.Diagnostic): IMarkerData {
|
||||
return {
|
||||
|
@ -96,7 +106,8 @@ export namespace Diagnostic {
|
|||
source: value.source,
|
||||
code: String(value.code),
|
||||
severity: DiagnosticSeverity.from(value.severity),
|
||||
relatedInformation: value.relatedInformation && value.relatedInformation.map(DiagnosticRelatedInformation.from)
|
||||
relatedInformation: value.relatedInformation && value.relatedInformation.map(DiagnosticRelatedInformation.from),
|
||||
customTags: Array.isArray(value.customTags) ? value.customTags.map(DiagnosticTag.from) : undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -673,6 +673,10 @@ export class SnippetString {
|
|||
}
|
||||
}
|
||||
|
||||
export enum DiagnosticTag {
|
||||
Unnecessary = 1,
|
||||
}
|
||||
|
||||
export enum DiagnosticSeverity {
|
||||
Hint = 3,
|
||||
Information = 2,
|
||||
|
@ -747,6 +751,7 @@ export class Diagnostic {
|
|||
code: string | number;
|
||||
severity: DiagnosticSeverity;
|
||||
relatedInformation: DiagnosticRelatedInformation[];
|
||||
customTags?: DiagnosticTag[];
|
||||
|
||||
constructor(range: Range, message: string, severity: DiagnosticSeverity = DiagnosticSeverity.Error) {
|
||||
this.range = range;
|
||||
|
|
Loading…
Reference in a new issue