Skip to content
This repository has been archived by the owner on May 7, 2020. It is now read-only.

Commit

Permalink
add code actions to js/ts
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesbirtles committed May 5, 2019
1 parent 8e20576 commit 9b66ae6
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 14 deletions.
37 changes: 35 additions & 2 deletions src/api/fragmentPositions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
SymbolInformation,
Location,
LocationLink,
CodeAction,
TextDocumentEdit,
} from './interfaces';

export function mapRangeToParent(fragment: Fragment, range: Range): Range {
Expand Down Expand Up @@ -57,6 +59,10 @@ export function mapDiagnosticToParent(fragment: Fragment, diagnostic: Diagnostic
return { ...diagnostic, range: mapRangeToParent(fragment, diagnostic.range) };
}

export function mapDiagnosticToFragment(fragment: Fragment, diagnostic: Diagnostic): Diagnostic {
return { ...diagnostic, range: mapRangeToFragment(fragment, diagnostic.range) };
}

export function mapColorInformationToParent(
fragment: Fragment,
info: ColorInformation,
Expand Down Expand Up @@ -95,8 +101,35 @@ export function mapSymbolInformationToParent(
export function mapLocationLinkToParent(fragment: Fragment, def: LocationLink): LocationLink {
return LocationLink.create(
def.targetUri,
def.targetRange,
def.targetSelectionRange,
fragment.getURL() === def.targetUri
? mapRangeToParent(fragment, def.targetRange)
: def.targetRange,
fragment.getURL() === def.targetUri
? mapRangeToParent(fragment, def.targetSelectionRange)
: def.targetSelectionRange,
def.originSelectionRange ? mapRangeToParent(fragment, def.originSelectionRange) : undefined,
);
}

export function mapTextDocumentEditToParent(fragment: Fragment, edit: TextDocumentEdit) {
if (edit.textDocument.uri !== fragment.getURL()) {
return edit;
}

return TextDocumentEdit.create(
edit.textDocument,
edit.edits.map(textEdit => mapTextEditToParent(fragment, textEdit)),
);
}

export function mapCodeActionToParent(fragment: Fragment, codeAction: CodeAction) {
return CodeAction.create(
codeAction.title,
{
documentChanges: codeAction.edit!.documentChanges!.map(edit =>
mapTextDocumentEditToParent(fragment, edit as TextDocumentEdit),
),
},
codeAction.kind,
);
}
26 changes: 26 additions & 0 deletions src/api/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ import {
SymbolKind,
DefinitionLink,
LocationLink,
CodeAction,
CodeActionContext,
TextDocumentEdit,
TextDocumentIdentifier,
VersionedTextDocumentIdentifier,
TextDocumentContentChangeEvent,
} from 'vscode-languageserver-types';
import { Document } from './Document';

Expand All @@ -47,6 +53,12 @@ export {
SymbolKind,
DefinitionLink,
LocationLink,
CodeAction,
CodeActionContext,
TextDocumentEdit,
TextDocumentIdentifier,
VersionedTextDocumentIdentifier,
TextDocumentContentChangeEvent,
};

export type Resolvable<T> = T | Promise<T>;
Expand Down Expand Up @@ -149,6 +161,20 @@ export namespace DefinitionsProvider {
}
}

export interface CodeActionsProvider {
getCodeActions(
document: Document,
range: Range,
context: CodeActionContext,
): Resolvable<CodeAction[]>;
}

export namespace CodeActionsProvider {
export function is(obj: any): obj is CodeActionsProvider {
return typeof obj.getCodeActions === 'function';
}
}

export interface Fragment extends Document {
details: FragmentDetails;

Expand Down
27 changes: 27 additions & 0 deletions src/api/wrapFragmentPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
DocumentSymbolsProvider,
SymbolInformation,
DefinitionsProvider,
CodeActionsProvider,
} from './interfaces';
import { Document } from './Document';
import {
Expand All @@ -30,6 +31,8 @@ import {
mapColorPresentationToParent,
mapSymbolInformationToParent,
mapLocationLinkToParent,
mapDiagnosticToFragment,
mapCodeActionToParent,
} from './fragmentPositions';
import { Host, OnRegister } from './Host';

Expand Down Expand Up @@ -211,5 +214,29 @@ export function wrapFragmentPlugin<P extends Plugin>(
};
}

if (CodeActionsProvider.is(plugin)) {
const getCodeActions: CodeActionsProvider['getCodeActions'] = plugin.getCodeActions.bind(
plugin,
);
plugin.getCodeActions = async function(document, range, context) {
const fragment = getFragment(document);
if (
!fragment ||
!fragment.isInFragment(range.start) ||
!fragment.isInFragment(range.end)
) {
return [];
}

const items = await getCodeActions(fragment, mapRangeToFragment(fragment, range), {
...context,
diagnostics: context.diagnostics.map(diag =>
mapDiagnosticToFragment(fragment, diag),
),
});
return items.map(item => mapCodeActionToParent(fragment, item));
};
}

return plugin;
}
37 changes: 28 additions & 9 deletions src/lib/documents/DocumentManager.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
import {
TextDocumentItem,
VersionedTextDocumentIdentifier,
TextDocumentContentChangeEvent,
TextDocumentIdentifier,
CompletionItem,
TextEdit,
DefinitionLink,
} from 'vscode-languageserver-types';
import { PluginHost, ExecuteMode } from '../PluginHost';
import { flatten } from '../../utils';
import {
Expand All @@ -19,6 +10,15 @@ import {
Color,
ColorPresentation,
SymbolInformation,
TextDocumentItem,
TextDocumentIdentifier,
VersionedTextDocumentIdentifier,
TextDocumentContentChangeEvent,
CompletionItem,
TextEdit,
DefinitionLink,
CodeActionContext,
CodeAction,
} from '../../api';

export interface DocumentManager {
Expand Down Expand Up @@ -224,4 +224,23 @@ export class DocumentManager extends PluginHost {
),
);
}

async getCodeActions(
textDocument: TextDocumentIdentifier,
range: Range,
context: CodeActionContext,
): Promise<CodeAction[]> {
const document = this.documents.get(textDocument.uri);
if (!document) {
throw new Error('Cannot call methods on an unopened document');
}

return flatten(
await this.execute<CodeAction[]>(
'getCodeActions',
[document, range, context],
ExecuteMode.Collect,
),
);
}
}
76 changes: 74 additions & 2 deletions src/plugins/TypeScriptPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,21 @@ import {
DefinitionsProvider,
DefinitionLink,
LocationLink,
CodeActionsProvider,
CodeAction,
Resolvable,
CodeActionContext,
TextEdit,
TextDocumentEdit,
VersionedTextDocumentIdentifier,
} from '../api';
import {
convertRange,
getScriptKindFromAttributes,
symbolKindFromString,
scriptElementKindToCompletionItemKind,
getCommitCharactersForScriptElement,
mapSeverity,
} from './typescript/utils';
import { getLanguageServiceForDocument, CreateDocument } from './typescript/service';
import { pathToUrl } from '../utils';
Expand All @@ -37,7 +45,8 @@ export class TypeScriptPlugin
OnRegister,
DocumentSymbolsProvider,
CompletionsProvider,
DefinitionsProvider {
DefinitionsProvider,
CodeActionsProvider {
public static matchFragment(fragment: Fragment) {
return fragment.details.attributes.tag == 'script';
}
Expand Down Expand Up @@ -84,9 +93,10 @@ export class TypeScriptPlugin

return diagnostics.map(diagnostic => ({
range: convertRange(document, diagnostic),
severity: DiagnosticSeverity.Error,
severity: mapSeverity(diagnostic.category),
source: isTypescript ? 'ts' : 'js',
message: ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'),
code: diagnostic.code,
}));
}

Expand Down Expand Up @@ -194,6 +204,10 @@ export class TypeScriptPlugin
}

getDefinitions(document: Document, position: Position): DefinitionLink[] {
if (!this.host.getConfig<boolean>('typescript.definitions.enable')) {
return [];
}

const lang = getLanguageServiceForDocument(document, this.createDocument);

const defs = lang.getDefinitionAndBoundSpan(
Expand Down Expand Up @@ -227,4 +241,62 @@ export class TypeScriptPlugin
})
.filter(res => !!res) as DefinitionLink[];
}

getCodeActions(
document: Document,
range: Range,
context: CodeActionContext,
): Resolvable<CodeAction[]> {
if (!this.host.getConfig<boolean>('typescript.codeActions.enable')) {
return [];
}

const lang = getLanguageServiceForDocument(document, this.createDocument);

const start = document.offsetAt(range.start);
const end = document.offsetAt(range.end);
const errorCodes: number[] = context.diagnostics.map(diag => Number(diag.code));
const codeFixes = lang.getCodeFixesAtPosition(
document.getFilePath()!,
start,
end,
errorCodes,
{},
{},
);

const docs = new Map<string, Document>([[document.getFilePath()!, document]]);
return codeFixes.map(fix => {
return CodeAction.create(
fix.description,
{
documentChanges: fix.changes.map(change => {
let doc = docs.get(change.fileName);
console.log('found', change.fileName, !!doc);
if (!doc) {
doc = new TextDocument(
pathToUrl(change.fileName),
ts.sys.readFile(change.fileName) || '',
);
docs.set(change.fileName, doc);
}

return TextDocumentEdit.create(
VersionedTextDocumentIdentifier.create(
pathToUrl(change.fileName),
null,
),
change.textChanges.map(edit => {
return TextEdit.replace(
convertRange(doc!, edit.span),
edit.newText,
);
}),
);
}),
},
fix.fixName,
);
});
}
}
17 changes: 16 additions & 1 deletion src/plugins/typescript/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ts from 'typescript';
import { Document, Range, SymbolKind, CompletionItemKind } from '../../api';
import { Document, Range, SymbolKind, CompletionItemKind, DiagnosticSeverity } from '../../api';

export function getScriptKindFromFileName(fileName: string): ts.ScriptKind {
const ext = fileName.substr(fileName.lastIndexOf('.'));
Expand Down Expand Up @@ -174,3 +174,18 @@ export function getCommitCharactersForScriptElement(

return commitCharacters.length === 0 ? undefined : commitCharacters;
}

export function mapSeverity(category: ts.DiagnosticCategory): DiagnosticSeverity {
switch (category) {
case ts.DiagnosticCategory.Error:
return DiagnosticSeverity.Error;
case ts.DiagnosticCategory.Warning:
return DiagnosticSeverity.Warning;
case ts.DiagnosticCategory.Suggestion:
return DiagnosticSeverity.Hint;
case ts.DiagnosticCategory.Message:
return DiagnosticSeverity.Information;
}

return DiagnosticSeverity.Error;
}
4 changes: 4 additions & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export function startServer() {
colorProvider: true,
documentSymbolProvider: true,
definitionProvider: true,
codeActionProvider: true,
},
};
});
Expand Down Expand Up @@ -84,6 +85,9 @@ export function startServer() {
);
connection.onDocumentSymbol(evt => manager.getDocumentSymbols(evt.textDocument));
connection.onDefinition(evt => manager.getDefinitions(evt.textDocument, evt.position));
connection.onCodeAction(evt =>
manager.getCodeActions(evt.textDocument, evt.range, evt.context),
);

manager.on('documentChange', async document => {
const diagnostics = await manager.getDiagnostics({ uri: document.getURL() });
Expand Down

0 comments on commit 9b66ae6

Please sign in to comment.