diff --git a/src/languageFeatures.ts b/src/languageFeatures.ts index 367e1e80..7dc0d1de 100644 --- a/src/languageFeatures.ts +++ b/src/languageFeatures.ts @@ -163,13 +163,16 @@ export class DiagnosticsAdapter extends Adapter { return null; } const promises: Promise[] = []; - const { noSyntaxValidation, noSemanticValidation } = this._defaults.getDiagnosticsOptions(); + const { noSyntaxValidation, noSemanticValidation, noSuggestionDiagnostics } = this._defaults.getDiagnosticsOptions(); if (!noSyntaxValidation) { promises.push(worker.getSyntacticDiagnostics(resource.toString())); } if (!noSemanticValidation) { promises.push(worker.getSemanticDiagnostics(resource.toString())); } + if (!noSuggestionDiagnostics) { + promises.push(worker.getSuggestionDiagnostics(resource.toString())); + } return Promise.all(promises); }).then(diagnostics => { if (!diagnostics || !monaco.editor.getModel(resource)) { @@ -191,14 +194,24 @@ export class DiagnosticsAdapter extends Adapter { const { lineNumber: endLineNumber, column: endColumn } = this._offsetToPosition(resource, diag.start + diag.length); return { - severity: monaco.MarkerSeverity.Error, + severity: this._tsDiagnosticCategoryToMarkerSeverity(diag.category), startLineNumber, startColumn, endLineNumber, endColumn, - message: flattenDiagnosticMessageText(diag.messageText, '\n') + message: flattenDiagnosticMessageText(diag.messageText, '\n'), + code: diag.code.toString() }; } + + private _tsDiagnosticCategoryToMarkerSeverity(category: ts.DiagnosticCategory): monaco.MarkerSeverity { + switch (category) { + case ts.DiagnosticCategory.Error: return monaco.MarkerSeverity.Error + case ts.DiagnosticCategory.Message: return monaco.MarkerSeverity.Info + case ts.DiagnosticCategory.Warning: return monaco.MarkerSeverity.Warning + case ts.DiagnosticCategory.Suggestion: return monaco.MarkerSeverity.Hint + } + } } // --- suggest ------ @@ -356,7 +369,7 @@ export class SignatureHelpAdapter extends Adapter implements monaco.languages.Si return { value: ret, - dispose() {} + dispose() { } }; }); } @@ -632,6 +645,58 @@ export class FormatOnTypeAdapter extends FormatHelper implements monaco.language } } +// --- code actions ------ + +export class CodeActionAdaptor extends FormatHelper implements monaco.languages.CodeActionProvider { + + public provideCodeActions(model: monaco.editor.ITextModel, range: Range, context: monaco.languages.CodeActionContext, token: CancellationToken): Promise { + const resource = model.uri; + + return this._worker(resource).then(worker => { + const start = this._positionToOffset(resource, { lineNumber: range.startLineNumber, column: range.startColumn }); + const end = this._positionToOffset(resource, { lineNumber: range.endLineNumber, column: range.endColumn }); + + const formatOptions = FormatHelper._convertOptions(model.getOptions()); + const errorCodes = context.markers.filter(m => m.code).map(m => m.code).map(Number); + + return worker.getCodeFixesAtPosition(resource.toString(), start, end, errorCodes, formatOptions); + + }).then(codeFixes => { + + return codeFixes.filter(fix => { + // Removes any 'make a new file'-type code fix + return fix.changes.filter(change => change.isNewFile).length === 0; + }).map(fix => { + return this._tsCodeFixActionToMonacoCodeAction(model, context, fix); + }) + }).then(result => { + return { + actions: result, + dispose: () => { } + }; + }); + } + + + private _tsCodeFixActionToMonacoCodeAction(model: monaco.editor.ITextModel, context: monaco.languages.CodeActionContext, codeFix: ts.CodeFixAction): monaco.languages.CodeAction { + const edits: monaco.languages.ResourceTextEdit[] = codeFix.changes.map(edit => ({ + resource: model.uri, + edits: edit.textChanges.map(tc => ({ + range: this._textSpanToRange(model.uri, tc.span), + text: tc.newText + })) + })); + + const action: monaco.languages.CodeAction = { + title: codeFix.description, + edit: { edits: edits }, + diagnostics: context.markers, + kind: "quickfix" + }; + + return action; + } +} // --- rename ---- export class RenameAdapter extends Adapter implements monaco.languages.RenameProvider { @@ -675,4 +740,4 @@ export class RenameAdapter extends Adapter implements monaco.languages.RenamePro return { edits }; } -} \ No newline at end of file +} diff --git a/src/monaco.d.ts b/src/monaco.d.ts index c3691903..8aa58804 100644 --- a/src/monaco.d.ts +++ b/src/monaco.d.ts @@ -128,6 +128,7 @@ declare module monaco.languages.typescript { export interface DiagnosticsOptions { noSemanticValidation?: boolean; noSyntaxValidation?: boolean; + noSuggestionDiagnostics?: boolean; } export interface LanguageServiceDefaults { diff --git a/src/tsMode.ts b/src/tsMode.ts index 5c2f1fd2..8842fd63 100644 --- a/src/tsMode.ts +++ b/src/tsMode.ts @@ -64,6 +64,7 @@ function setupMode(defaults: LanguageServiceDefaultsImpl, modeId: string): (firs monaco.languages.registerDocumentSymbolProvider(modeId, new languageFeatures.OutlineAdapter(worker)); monaco.languages.registerDocumentRangeFormattingEditProvider(modeId, new languageFeatures.FormatAdapter(worker)); monaco.languages.registerOnTypeFormattingEditProvider(modeId, new languageFeatures.FormatOnTypeAdapter(worker)); + monaco.languages.registerCodeActionProvider(modeId, new languageFeatures.CodeActionAdaptor(worker)); monaco.languages.registerRenameProvider(modeId, new languageFeatures.RenameAdapter(worker)); new languageFeatures.DiagnosticsAdapter(defaults, modeId, worker); diff --git a/src/tsWorker.ts b/src/tsWorker.ts index 654fdb3c..06787de2 100644 --- a/src/tsWorker.ts +++ b/src/tsWorker.ts @@ -146,6 +146,12 @@ export class TypeScriptWorker implements ts.LanguageServiceHost { return Promise.resolve(diagnostics); } + getSuggestionDiagnostics(fileName: string): Promise { + const diagnostics = this._languageService.getSuggestionDiagnostics(fileName); + TypeScriptWorker.clearFiles(diagnostics); + return Promise.resolve(diagnostics); + } + getCompilerOptionsDiagnostics(fileName: string): Promise { const diagnostics = this._languageService.getCompilerOptionsDiagnostics(); TypeScriptWorker.clearFiles(diagnostics); @@ -208,6 +214,11 @@ export class TypeScriptWorker implements ts.LanguageServiceHost { return Promise.resolve(this._languageService.getEmitOutput(fileName)); } + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[], formatOptions: ts.FormatCodeOptions): Promise> { + const preferences = {} + return Promise.resolve(this._languageService.getCodeFixesAtPosition(fileName, start, end, errorCodes, formatOptions, preferences)); + } + updateExtraLibs(extraLibs: IExtraLibs) { this._extraLibs = extraLibs; } diff --git a/test/index.html b/test/index.html index 02bbed65..11c3adcb 100644 --- a/test/index.html +++ b/test/index.html @@ -160,10 +160,8 @@ '}', '', 'var game = new Conway.GameOfLife();', - - ].join('\n') + ].join('\n'); } - require([ 'vs/basic-languages/monaco.contribution', 'vs/language/typescript/monaco.contribution'