123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- 'use strict';
- import { LanguageServiceDefaultsImpl } from './monaco.contribution';
- import * as ts from './lib/typescriptServices';
- import { TypeScriptWorker } from './tsWorker';
- import Uri = monaco.Uri;
- import Position = monaco.Position;
- import Range = monaco.Range;
- import Thenable = monaco.Thenable;
- import CancellationToken = monaco.CancellationToken;
- import IDisposable = monaco.IDisposable;
- //#region utils copied from typescript to prevent loading the entire typescriptServices ---
- enum IndentStyle {
- None = 0,
- Block = 1,
- Smart = 2
- }
- export function flattenDiagnosticMessageText(diag: string | ts.DiagnosticMessageChain | undefined, newLine: string, indent = 0): string {
- if (typeof diag === "string") {
- return diag;
- }
- else if (diag === undefined) {
- return "";
- }
- let result = "";
- if (indent) {
- result += newLine;
- for (let i = 0; i < indent; i++) {
- result += " ";
- }
- }
- result += diag.messageText;
- indent++;
- if (diag.next) {
- for (const kid of diag.next) {
- result += flattenDiagnosticMessageText(kid, newLine, indent);
- }
- }
- return result;
- }
- function displayPartsToString(displayParts: ts.SymbolDisplayPart[]): string {
- if (displayParts) {
- return displayParts.map((displayPart) => displayPart.text).join("");
- }
- return "";
- }
- //#endregion
- export abstract class Adapter {
- constructor(protected _worker: (first: Uri, ...more: Uri[]) => Promise<TypeScriptWorker>) {
- }
- protected _positionToOffset(uri: Uri, position: monaco.IPosition): number {
- let model = monaco.editor.getModel(uri);
- return model.getOffsetAt(position);
- }
- protected _offsetToPosition(uri: Uri, offset: number): monaco.IPosition {
- let model = monaco.editor.getModel(uri);
- return model.getPositionAt(offset);
- }
- protected _textSpanToRange(uri: Uri, span: ts.TextSpan): monaco.IRange {
- let p1 = this._offsetToPosition(uri, span.start);
- let p2 = this._offsetToPosition(uri, span.start + span.length);
- let { lineNumber: startLineNumber, column: startColumn } = p1;
- let { lineNumber: endLineNumber, column: endColumn } = p2;
- return { startLineNumber, startColumn, endLineNumber, endColumn };
- }
- }
- // --- diagnostics --- ---
- export class DiagnosticsAdapter extends Adapter {
- private _disposables: IDisposable[] = [];
- private _listener: { [uri: string]: IDisposable } = Object.create(null);
- constructor(private _defaults: LanguageServiceDefaultsImpl, private _selector: string,
- worker: (first: Uri, ...more: Uri[]) => Promise<TypeScriptWorker>
- ) {
- super(worker);
- const onModelAdd = (model: monaco.editor.IModel): void => {
- if (model.getModeId() !== _selector) {
- return;
- }
- let handle: number;
- const changeSubscription = model.onDidChangeContent(() => {
- clearTimeout(handle);
- handle = setTimeout(() => this._doValidate(model.uri), 500);
- });
- this._listener[model.uri.toString()] = {
- dispose() {
- changeSubscription.dispose();
- clearTimeout(handle);
- }
- };
- this._doValidate(model.uri);
- };
- const onModelRemoved = (model: monaco.editor.IModel): void => {
- monaco.editor.setModelMarkers(model, this._selector, []);
- const key = model.uri.toString();
- if (this._listener[key]) {
- this._listener[key].dispose();
- delete this._listener[key];
- }
- };
- this._disposables.push(monaco.editor.onDidCreateModel(onModelAdd));
- this._disposables.push(monaco.editor.onWillDisposeModel(onModelRemoved));
- this._disposables.push(monaco.editor.onDidChangeModelLanguage(event => {
- onModelRemoved(event.model);
- onModelAdd(event.model);
- }));
- this._disposables.push({
- dispose() {
- for (const model of monaco.editor.getModels()) {
- onModelRemoved(model);
- }
- }
- });
- const recomputeDiagostics = () => {
- // redo diagnostics when options change
- for (const model of monaco.editor.getModels()) {
- onModelRemoved(model);
- onModelAdd(model);
- }
- };
- this._disposables.push(this._defaults.onDidChange(recomputeDiagostics));
- this._disposables.push(this._defaults.onDidExtraLibsChange(recomputeDiagostics));
- monaco.editor.getModels().forEach(onModelAdd);
- }
- public dispose(): void {
- this._disposables.forEach(d => d && d.dispose());
- this._disposables = [];
- }
- private _doValidate(resource: Uri): void {
- this._worker(resource).then(worker => {
- if (!monaco.editor.getModel(resource)) {
- // model was disposed in the meantime
- return null;
- }
- const promises: Promise<ts.Diagnostic[]>[] = [];
- 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)) {
- // model was disposed in the meantime
- return null;
- }
- const markers = diagnostics
- .reduce((p, c) => c.concat(p), [])
- .map(d => this._convertDiagnostics(resource, d));
- monaco.editor.setModelMarkers(monaco.editor.getModel(resource), this._selector, markers);
- }).then(undefined, err => {
- console.error(err);
- });
- }
- private _convertDiagnostics(resource: Uri, diag: ts.Diagnostic): monaco.editor.IMarkerData {
- const { lineNumber: startLineNumber, column: startColumn } = this._offsetToPosition(resource, diag.start);
- const { lineNumber: endLineNumber, column: endColumn } = this._offsetToPosition(resource, diag.start + diag.length);
- return {
- severity: this._tsDiagnosticCategoryToMarkerSeverity(diag.category),
- startLineNumber,
- startColumn,
- endLineNumber,
- endColumn,
- message: flattenDiagnosticMessageText(diag.messageText, '\n'),
- code: diag.code.toString(),
- relatedInformation: this._convertRelatedInformation(resource, diag.relatedInformation)
- };
- }
- private _convertRelatedInformation(resource: Uri, relatedInformation?: ts.DiagnosticRelatedInformation[]): monaco.editor.IRelatedInformation[] {
- if (relatedInformation === undefined)
- return undefined;
- return relatedInformation.map(info => {
- const relatedResource = info.file === undefined ? resource : monaco.Uri.parse(info.file.fileName);
- const { lineNumber: startLineNumber, column: startColumn } = this._offsetToPosition(relatedResource, info.start);
- const { lineNumber: endLineNumber, column: endColumn } = this._offsetToPosition(relatedResource, info.start + info.length);
- return {
- resource: relatedResource,
- startLineNumber,
- startColumn,
- endLineNumber,
- endColumn,
- message: flattenDiagnosticMessageText(info.messageText, '\n')
- };
- });
- }
- 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 ------
- interface MyCompletionItem extends monaco.languages.CompletionItem {
- uri: Uri;
- position: Position;
- }
- export class SuggestAdapter extends Adapter implements monaco.languages.CompletionItemProvider {
- public get triggerCharacters(): string[] {
- return ['.'];
- }
- provideCompletionItems(model: monaco.editor.IReadOnlyModel, position: Position, _context: monaco.languages.CompletionContext, token: CancellationToken): Thenable<monaco.languages.CompletionList> {
- const wordInfo = model.getWordUntilPosition(position);
- const wordRange = new Range(position.lineNumber, wordInfo.startColumn, position.lineNumber, wordInfo.endColumn);
- const resource = model.uri;
- const offset = this._positionToOffset(resource, position);
- return this._worker(resource).then(worker => {
- return worker.getCompletionsAtPosition(resource.toString(), offset);
- }).then(info => {
- if (!info) {
- return;
- }
- let suggestions: MyCompletionItem[] = info.entries.map(entry => {
- let range = wordRange;
- if (entry.replacementSpan) {
- const p1 = model.getPositionAt(entry.replacementSpan.start);
- const p2 = model.getPositionAt(entry.replacementSpan.start + entry.replacementSpan.length);
- range = new Range(p1.lineNumber, p1.column, p2.lineNumber, p2.column);
- }
- return {
- uri: resource,
- position: position,
- range: range,
- label: entry.name,
- insertText: entry.name,
- sortText: entry.sortText,
- kind: SuggestAdapter.convertKind(entry.kind)
- };
- });
- return {
- suggestions
- };
- });
- }
- resolveCompletionItem(_model: monaco.editor.IReadOnlyModel, _position: Position, item: monaco.languages.CompletionItem, token: CancellationToken): Thenable<monaco.languages.CompletionItem> {
- let myItem = <MyCompletionItem>item;
- const resource = myItem.uri;
- const position = myItem.position;
- return this._worker(resource).then(worker => {
- return worker.getCompletionEntryDetails(resource.toString(),
- this._positionToOffset(resource, position),
- myItem.label);
- }).then(details => {
- if (!details) {
- return myItem;
- }
- return <MyCompletionItem>{
- uri: resource,
- position: position,
- label: details.name,
- kind: SuggestAdapter.convertKind(details.kind),
- detail: displayPartsToString(details.displayParts),
- documentation: {
- value: displayPartsToString(details.documentation)
- }
- };
- });
- }
- private static convertKind(kind: string): monaco.languages.CompletionItemKind {
- switch (kind) {
- case Kind.primitiveType:
- case Kind.keyword:
- return monaco.languages.CompletionItemKind.Keyword;
- case Kind.variable:
- case Kind.localVariable:
- return monaco.languages.CompletionItemKind.Variable;
- case Kind.memberVariable:
- case Kind.memberGetAccessor:
- case Kind.memberSetAccessor:
- return monaco.languages.CompletionItemKind.Field;
- case Kind.function:
- case Kind.memberFunction:
- case Kind.constructSignature:
- case Kind.callSignature:
- case Kind.indexSignature:
- return monaco.languages.CompletionItemKind.Function;
- case Kind.enum:
- return monaco.languages.CompletionItemKind.Enum;
- case Kind.module:
- return monaco.languages.CompletionItemKind.Module;
- case Kind.class:
- return monaco.languages.CompletionItemKind.Class;
- case Kind.interface:
- return monaco.languages.CompletionItemKind.Interface;
- case Kind.warning:
- return monaco.languages.CompletionItemKind.File;
- }
- return monaco.languages.CompletionItemKind.Property;
- }
- }
- export class SignatureHelpAdapter extends Adapter implements monaco.languages.SignatureHelpProvider {
- public signatureHelpTriggerCharacters = ['(', ','];
- provideSignatureHelp(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.SignatureHelpResult> {
- let resource = model.uri;
- return this._worker(resource).then(worker => worker.getSignatureHelpItems(resource.toString(), this._positionToOffset(resource, position))).then(info => {
- if (!info) {
- return;
- }
- let ret: monaco.languages.SignatureHelp = {
- activeSignature: info.selectedItemIndex,
- activeParameter: info.argumentIndex,
- signatures: []
- };
- info.items.forEach(item => {
- let signature: monaco.languages.SignatureInformation = {
- label: '',
- parameters: []
- };
- signature.label += displayPartsToString(item.prefixDisplayParts);
- item.parameters.forEach((p, i, a) => {
- let label = displayPartsToString(p.displayParts);
- let parameter: monaco.languages.ParameterInformation = {
- label: label,
- documentation: displayPartsToString(p.documentation)
- };
- signature.label += label;
- signature.parameters.push(parameter);
- if (i < a.length - 1) {
- signature.label += displayPartsToString(item.separatorDisplayParts);
- }
- });
- signature.label += displayPartsToString(item.suffixDisplayParts);
- ret.signatures.push(signature);
- });
- return {
- value: ret,
- dispose() { }
- };
- });
- }
- }
- // --- hover ------
- export class QuickInfoAdapter extends Adapter implements monaco.languages.HoverProvider {
- provideHover(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.Hover> {
- let resource = model.uri;
- return this._worker(resource).then(worker => {
- return worker.getQuickInfoAtPosition(resource.toString(), this._positionToOffset(resource, position));
- }).then(info => {
- if (!info) {
- return;
- }
- let documentation = displayPartsToString(info.documentation);
- let tags = info.tags ? info.tags.map(tag => {
- const label = `*@${tag.name}*`;
- if (!tag.text) {
- return label;
- }
- return label + (tag.text.match(/\r\n|\n/g) ? ' \n' + tag.text : ` - ${tag.text}`);
- })
- .join(' \n\n') : '';
- let contents = displayPartsToString(info.displayParts);
- return {
- range: this._textSpanToRange(resource, info.textSpan),
- contents: [{
- value: '```js\n' + contents + '\n```\n'
- }, {
- value: documentation + (tags ? '\n\n' + tags : '')
- }]
- };
- });
- }
- }
- // --- occurrences ------
- export class OccurrencesAdapter extends Adapter implements monaco.languages.DocumentHighlightProvider {
- public provideDocumentHighlights(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.DocumentHighlight[]> {
- const resource = model.uri;
- return this._worker(resource).then(worker => {
- return worker.getOccurrencesAtPosition(resource.toString(), this._positionToOffset(resource, position));
- }).then(entries => {
- if (!entries) {
- return;
- }
- return entries.map(entry => {
- return <monaco.languages.DocumentHighlight>{
- range: this._textSpanToRange(resource, entry.textSpan),
- kind: entry.isWriteAccess ? monaco.languages.DocumentHighlightKind.Write : monaco.languages.DocumentHighlightKind.Text
- };
- });
- });
- }
- }
- // --- definition ------
- export class DefinitionAdapter extends Adapter {
- public provideDefinition(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.Definition> {
- const resource = model.uri;
- return this._worker(resource).then(worker => {
- return worker.getDefinitionAtPosition(resource.toString(), this._positionToOffset(resource, position));
- }).then(entries => {
- if (!entries) {
- return;
- }
- const result: monaco.languages.Location[] = [];
- for (let entry of entries) {
- const uri = Uri.parse(entry.fileName);
- if (monaco.editor.getModel(uri)) {
- result.push({
- uri: uri,
- range: this._textSpanToRange(uri, entry.textSpan)
- });
- }
- }
- return result;
- });
- }
- }
- // --- references ------
- export class ReferenceAdapter extends Adapter implements monaco.languages.ReferenceProvider {
- provideReferences(model: monaco.editor.IReadOnlyModel, position: Position, context: monaco.languages.ReferenceContext, token: CancellationToken): Thenable<monaco.languages.Location[]> {
- const resource = model.uri;
- return this._worker(resource).then(worker => {
- return worker.getReferencesAtPosition(resource.toString(), this._positionToOffset(resource, position));
- }).then(entries => {
- if (!entries) {
- return;
- }
- const result: monaco.languages.Location[] = [];
- for (let entry of entries) {
- const uri = Uri.parse(entry.fileName);
- if (monaco.editor.getModel(uri)) {
- result.push({
- uri: uri,
- range: this._textSpanToRange(uri, entry.textSpan)
- });
- }
- }
- return result;
- });
- }
- }
- // --- outline ------
- export class OutlineAdapter extends Adapter implements monaco.languages.DocumentSymbolProvider {
- public provideDocumentSymbols(model: monaco.editor.IReadOnlyModel, token: CancellationToken): Thenable<monaco.languages.DocumentSymbol[]> {
- const resource = model.uri;
- return this._worker(resource).then(worker => worker.getNavigationBarItems(resource.toString())).then(items => {
- if (!items) {
- return;
- }
- const convert = (bucket: monaco.languages.DocumentSymbol[], item: ts.NavigationBarItem, containerLabel?: string): void => {
- let result: monaco.languages.DocumentSymbol = {
- name: item.text,
- detail: '',
- kind: <monaco.languages.SymbolKind>(outlineTypeTable[item.kind] || monaco.languages.SymbolKind.Variable),
- range: this._textSpanToRange(resource, item.spans[0]),
- selectionRange: this._textSpanToRange(resource, item.spans[0]),
- tags: [],
- containerName: containerLabel
- };
- if (item.childItems && item.childItems.length > 0) {
- for (let child of item.childItems) {
- convert(bucket, child, result.name);
- }
- }
- bucket.push(result);
- }
- let result: monaco.languages.DocumentSymbol[] = [];
- items.forEach(item => convert(result, item));
- return result;
- });
- }
- }
- export class Kind {
- public static unknown: string = '';
- public static keyword: string = 'keyword';
- public static script: string = 'script';
- public static module: string = 'module';
- public static class: string = 'class';
- public static interface: string = 'interface';
- public static type: string = 'type';
- public static enum: string = 'enum';
- public static variable: string = 'var';
- public static localVariable: string = 'local var';
- public static function: string = 'function';
- public static localFunction: string = 'local function';
- public static memberFunction: string = 'method';
- public static memberGetAccessor: string = 'getter';
- public static memberSetAccessor: string = 'setter';
- public static memberVariable: string = 'property';
- public static constructorImplementation: string = 'constructor';
- public static callSignature: string = 'call';
- public static indexSignature: string = 'index';
- public static constructSignature: string = 'construct';
- public static parameter: string = 'parameter';
- public static typeParameter: string = 'type parameter';
- public static primitiveType: string = 'primitive type';
- public static label: string = 'label';
- public static alias: string = 'alias';
- public static const: string = 'const';
- public static let: string = 'let';
- public static warning: string = 'warning';
- }
- let outlineTypeTable: { [kind: string]: monaco.languages.SymbolKind } = Object.create(null);
- outlineTypeTable[Kind.module] = monaco.languages.SymbolKind.Module;
- outlineTypeTable[Kind.class] = monaco.languages.SymbolKind.Class;
- outlineTypeTable[Kind.enum] = monaco.languages.SymbolKind.Enum;
- outlineTypeTable[Kind.interface] = monaco.languages.SymbolKind.Interface;
- outlineTypeTable[Kind.memberFunction] = monaco.languages.SymbolKind.Method;
- outlineTypeTable[Kind.memberVariable] = monaco.languages.SymbolKind.Property;
- outlineTypeTable[Kind.memberGetAccessor] = monaco.languages.SymbolKind.Property;
- outlineTypeTable[Kind.memberSetAccessor] = monaco.languages.SymbolKind.Property;
- outlineTypeTable[Kind.variable] = monaco.languages.SymbolKind.Variable;
- outlineTypeTable[Kind.const] = monaco.languages.SymbolKind.Variable;
- outlineTypeTable[Kind.localVariable] = monaco.languages.SymbolKind.Variable;
- outlineTypeTable[Kind.variable] = monaco.languages.SymbolKind.Variable;
- outlineTypeTable[Kind.function] = monaco.languages.SymbolKind.Function;
- outlineTypeTable[Kind.localFunction] = monaco.languages.SymbolKind.Function;
- // --- formatting ----
- export abstract class FormatHelper extends Adapter {
- protected static _convertOptions(options: monaco.languages.FormattingOptions): ts.FormatCodeOptions {
- return {
- ConvertTabsToSpaces: options.insertSpaces,
- TabSize: options.tabSize,
- IndentSize: options.tabSize,
- IndentStyle: IndentStyle.Smart,
- NewLineCharacter: '\n',
- InsertSpaceAfterCommaDelimiter: true,
- InsertSpaceAfterSemicolonInForStatements: true,
- InsertSpaceBeforeAndAfterBinaryOperators: true,
- InsertSpaceAfterKeywordsInControlFlowStatements: true,
- InsertSpaceAfterFunctionKeywordForAnonymousFunctions: true,
- InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
- InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
- InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
- PlaceOpenBraceOnNewLineForControlBlocks: false,
- PlaceOpenBraceOnNewLineForFunctions: false
- };
- }
- protected _convertTextChanges(uri: Uri, change: ts.TextChange): monaco.editor.ISingleEditOperation {
- return <monaco.editor.ISingleEditOperation>{
- text: change.newText,
- range: this._textSpanToRange(uri, change.span)
- };
- }
- }
- export class FormatAdapter extends FormatHelper implements monaco.languages.DocumentRangeFormattingEditProvider {
- provideDocumentRangeFormattingEdits(model: monaco.editor.IReadOnlyModel, range: Range, options: monaco.languages.FormattingOptions, token: CancellationToken): Thenable<monaco.editor.ISingleEditOperation[]> {
- const resource = model.uri;
- return this._worker(resource).then(worker => {
- return worker.getFormattingEditsForRange(resource.toString(),
- this._positionToOffset(resource, { lineNumber: range.startLineNumber, column: range.startColumn }),
- this._positionToOffset(resource, { lineNumber: range.endLineNumber, column: range.endColumn }),
- FormatHelper._convertOptions(options));
- }).then(edits => {
- if (edits) {
- return edits.map(edit => this._convertTextChanges(resource, edit));
- }
- });
- }
- }
- export class FormatOnTypeAdapter extends FormatHelper implements monaco.languages.OnTypeFormattingEditProvider {
- get autoFormatTriggerCharacters() {
- return [';', '}', '\n'];
- }
- provideOnTypeFormattingEdits(model: monaco.editor.IReadOnlyModel, position: Position, ch: string, options: monaco.languages.FormattingOptions, token: CancellationToken): Thenable<monaco.editor.ISingleEditOperation[]> {
- const resource = model.uri;
- return this._worker(resource).then(worker => {
- return worker.getFormattingEditsAfterKeystroke(resource.toString(),
- this._positionToOffset(resource, position),
- ch, FormatHelper._convertOptions(options));
- }).then(edits => {
- if (edits) {
- return edits.map(edit => this._convertTextChanges(resource, edit));
- }
- });
- }
- }
- // --- 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<monaco.languages.CodeActionList> {
- 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 {
- async provideRenameEdits(model: monaco.editor.ITextModel, position: Position, newName: string, token: CancellationToken): Promise<monaco.languages.WorkspaceEdit & monaco.languages.Rejection> {
- const resource = model.uri;
- const fileName = resource.toString();
- const offset = this._positionToOffset(resource, position);
- const worker = await this._worker(resource);
- const renameInfo = await worker.getRenameInfo(fileName, offset, { allowRenameOfImportPath: false });
- if (renameInfo.canRename === false) { // use explicit comparison so that the discriminated union gets resolved properly
- return {
- edits: [],
- rejectReason: renameInfo.localizedErrorMessage
- };
- }
- if (renameInfo.fileToRename !== undefined) {
- throw new Error("Renaming files is not supported.");
- }
- const renameLocations = await worker.findRenameLocations(fileName, offset, /*strings*/ false, /*comments*/ false, /*prefixAndSuffix*/ false);
- const fileNameToResourceTextEditMap: { [fileName: string]: monaco.languages.ResourceTextEdit } = {};
- const edits: monaco.languages.ResourceTextEdit[] = [];
- for (const renameLocation of renameLocations) {
- if (!(renameLocation.fileName in fileNameToResourceTextEditMap)) {
- const resourceTextEdit = {
- edits: [],
- resource: monaco.Uri.parse(renameLocation.fileName)
- };
- fileNameToResourceTextEditMap[renameLocation.fileName] = resourceTextEdit;
- edits.push(resourceTextEdit);
- }
- fileNameToResourceTextEditMap[renameLocation.fileName].edits.push({
- range: this._textSpanToRange(resource, renameLocation.textSpan),
- text: newName
- });
- }
- return { edits };
- }
- }
|