diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 8ea13df2164..c3a4bb0a3f9 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -3674,6 +3674,32 @@ declare module 'vscode' { Hint = 3 } + /** + * Represents a related message and source code location for a diagnostic. This should be + * used to point to code locations that cause or related to a diagnostics, e.g when duplicating + * a symbol in a scope. + */ + export class DiagnosticRelatedInformation { + + /** + * The location of this related diagnostic information. + */ + location: Location; + + /** + * The message of this related diagnostic information. + */ + message: string; + + /** + * Creates a new related diagnostic information object. + * + * @param location The location. + * @param message The message. + */ + constructor(location: Location, message: string); + } + /** * Represents a diagnostic, such as a compiler error or warning. Diagnostic objects * are only valid in the scope of a file. @@ -3708,6 +3734,12 @@ declare module 'vscode' { */ code: string | number; + /** + * An array of related diagnostic information, e.g. when symbol-names within + * a scope collide all definitions can be marked via this property. + */ + relatedInformation: DiagnosticRelatedInformation[]; + /** * Creates a new diagnostic object. * diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index d41f0317bce..ac851683c8f 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -599,6 +599,7 @@ export function createApiFactory( CompletionTriggerKind: extHostTypes.CompletionTriggerKind, DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable, Diagnostic: extHostTypes.Diagnostic, + DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation, DiagnosticSeverity: extHostTypes.DiagnosticSeverity, Disposable: extHostTypes.Disposable, DocumentHighlight: extHostTypes.DocumentHighlight, diff --git a/src/vs/workbench/api/node/extHostDiagnostics.ts b/src/vs/workbench/api/node/extHostDiagnostics.ts index 55a2b9c51c8..661728303f0 100644 --- a/src/vs/workbench/api/node/extHostDiagnostics.ts +++ b/src/vs/workbench/api/node/extHostDiagnostics.ts @@ -10,6 +10,7 @@ import URI from 'vs/base/common/uri'; import * as vscode from 'vscode'; import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol'; import { DiagnosticSeverity } from './extHostTypes'; +import * as converter from './extHostTypeConverters'; import { mergeSort } from 'vs/base/common/arrays'; import { Event, Emitter, debounceEvent, mapEvent } from 'vs/base/common/event'; import { keys } from 'vs/base/common/map'; @@ -115,7 +116,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { orderLoop: for (let i = 0; i < 4; i++) { for (let diagnostic of diagnostics) { if (diagnostic.severity === order[i]) { - const len = marker.push(DiagnosticCollection.toMarkerData(diagnostic)); + const len = marker.push(converter.fromDiagnostic(diagnostic)); if (len === DiagnosticCollection._maxDiagnosticsPerFile) { break orderLoop; } @@ -133,7 +134,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { endColumn: marker[marker.length - 1].endColumn }); } else { - marker = diagnostics.map(DiagnosticCollection.toMarkerData); + marker = diagnostics.map(converter.fromDiagnostic); } } @@ -185,32 +186,6 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { } } - public static toMarkerData(diagnostic: vscode.Diagnostic): IMarkerData { - - let range = diagnostic.range; - - return { - startLineNumber: range.start.line + 1, - startColumn: range.start.character + 1, - endLineNumber: range.end.line + 1, - endColumn: range.end.character + 1, - message: diagnostic.message, - source: diagnostic.source, - severity: DiagnosticCollection._convertDiagnosticsSeverity(diagnostic.severity), - code: String(diagnostic.code) - }; - } - - private static _convertDiagnosticsSeverity(severity: number): MarkerSeverity { - switch (severity) { - case 0: return MarkerSeverity.Error; - case 1: return MarkerSeverity.Warning; - case 2: return MarkerSeverity.Info; - case 3: return MarkerSeverity.Hint; - default: return MarkerSeverity.Error; - } - } - private static _compareIndexedTuplesByUri(a: [vscode.Uri, vscode.Diagnostic[]], b: [vscode.Uri, vscode.Diagnostic[]]): number { if (a[0].toString() < b[0].toString()) { return -1; diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 3ddcc65229e..96a11c1c30e 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -15,7 +15,7 @@ import * as modes from 'vs/editor/common/modes'; import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService'; import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands'; -import { ExtHostDiagnostics, DiagnosticCollection } from 'vs/workbench/api/node/extHostDiagnostics'; +import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics'; import { asWinJsPromise } from 'vs/base/common/async'; import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, SymbolInformationDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto } from './extHost.protocol'; import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings'; @@ -313,7 +313,7 @@ class CodeActionAdapter { result.push({ title: candidate.title, command: candidate.command && this._commands.toInternal(candidate.command), - diagnostics: candidate.diagnostics && candidate.diagnostics.map(DiagnosticCollection.toMarkerData), + diagnostics: candidate.diagnostics && candidate.diagnostics.map(TypeConverters.fromDiagnostic), edit: candidate.edit && TypeConverters.WorkspaceEdit.from(candidate.edit), kind: candidate.kind && candidate.kind.value }); diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index a2acb9004c9..583ddf4fc68 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -20,7 +20,7 @@ import * as htmlContent from 'vs/base/common/htmlContent'; import { IRelativePattern } from 'vs/base/common/glob'; import { LanguageSelector, LanguageFilter } from 'vs/editor/common/modes/languageSelector'; import { WorkspaceEditDto, ResourceTextEditDto } from 'vs/workbench/api/node/extHost.protocol'; -import { MarkerSeverity } from 'vs/platform/markers/common/markers'; +import { MarkerSeverity, IRelatedInformation, IMarkerData } from 'vs/platform/markers/common/markers'; export interface PositionLike { line: number; @@ -83,6 +83,30 @@ export function fromPosition(position: types.Position): IPosition { return { lineNumber: position.line + 1, column: position.character + 1 }; } + +export function fromDiagnostic(value: vscode.Diagnostic): IMarkerData { + return { + ...fromRange(value.range), + message: value.message, + source: value.source, + code: String(value.code), + severity: fromDiagnosticSeverity(value.severity), + relatedInformation: value.relatedInformation && value.relatedInformation.map(fromDiagnosticRelatedInformation) + }; +} + +export function fromDiagnosticRelatedInformation(value: types.DiagnosticRelatedInformation): IRelatedInformation { + return { + ...fromRange(value.location.range), + message: value.message, + resource: value.location.uri + }; +} + +export function toDiagnosticRelatedInformation(value: IRelatedInformation): types.DiagnosticRelatedInformation { + return new types.DiagnosticRelatedInformation(new types.Location(value.resource, toRange(value)), value.message); +} + export function fromDiagnosticSeverity(value: number): MarkerSeverity { switch (value) { case types.DiagnosticSeverity.Error: @@ -111,6 +135,7 @@ export function toDiagnosticSeverty(value: MarkerSeverity): types.DiagnosticSeve return types.DiagnosticSeverity.Error; } + export function fromViewColumn(column?: vscode.ViewColumn): EditorPosition { let editorColumn = EditorPosition.ONE; if (typeof column !== 'number') { diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 1f37fd92e25..674a6aeb7f0 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -728,6 +728,27 @@ export class Location { } } +export class DiagnosticRelatedInformation { + + static is(thing: any): thing is DiagnosticRelatedInformation { + if (!thing) { + return false; + } + return typeof (thing).message === 'string' + && (thing).location + && Range.isRange((thing).location.range) + && URI.isUri((thing).location.uri); + } + + location: Location; + message: string; + + constructor(location: Location, message: string) { + this.location = location; + this.message = message; + } +} + export class Diagnostic { range: Range; @@ -735,6 +756,7 @@ export class Diagnostic { source: string; code: string | number; severity: DiagnosticSeverity; + relatedInformation: DiagnosticRelatedInformation[]; constructor(range: Range, message: string, severity: DiagnosticSeverity = DiagnosticSeverity.Error) { this.range = range; diff --git a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts index 5a4c3818f62..22c518c8972 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import URI, { UriComponents } from 'vs/base/common/uri'; import { DiagnosticCollection } from 'vs/workbench/api/node/extHostDiagnostics'; -import { Diagnostic, DiagnosticSeverity, Range } from 'vs/workbench/api/node/extHostTypes'; +import { Diagnostic, DiagnosticSeverity, Range, DiagnosticRelatedInformation, Location } from 'vs/workbench/api/node/extHostTypes'; import { MainThreadDiagnosticsShape } from 'vs/workbench/api/node/extHost.protocol'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; @@ -298,4 +298,30 @@ suite('ExtHostDiagnostics', () => { collection.clear(); await p; }); + + test('diagnostics with related information', function (done) { + + let collection = new DiagnosticCollection('ddd', new class extends DiagnosticsShape { + $changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]) { + + let [[, data]] = entries; + assert.equal(entries.length, 1); + assert.equal(data.length, 1); + + let [diag] = data; + assert.equal(diag.relatedInformation.length, 2); + assert.equal(diag.relatedInformation[0].message, 'more1'); + assert.equal(diag.relatedInformation[1].message, 'more2'); + done(); + } + }, new Emitter()); + + let diag = new Diagnostic(new Range(0, 0, 1, 1), 'Foo'); + diag.relatedInformation = [ + new DiagnosticRelatedInformation(new Location(URI.parse('cc:dd'), new Range(0, 0, 0, 0)), 'more1'), + new DiagnosticRelatedInformation(new Location(URI.parse('cc:ee'), new Range(0, 0, 0, 0)), 'more2') + ]; + + collection.set(URI.parse('aa:bb'), [diag]); + }); });