Skip to content

Commit

Permalink
feat(server): support code action
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanwonder committed Jul 9, 2022
1 parent 7e91830 commit ce3f9d8
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 7 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@
"test:syntaxes": "yarn compile:syntaxes-test && yarn build:syntaxes && jasmine dist/syntaxes/test/driver.js"
},
"dependencies": {
"@angular/language-service": "14.1.0-next.0",
"@angular/language-service": "https://output.circle-artifacts.com/output/job/ad7b0112-3d86-4f40-a595-a399e55bc276/artifacts/0/angular/language-service-pr46764-e7f1b32d29.tgz",
"typescript": "4.7.4",
"vscode-jsonrpc": "6.0.0",
"vscode-languageclient": "7.0.0",
Expand Down
2 changes: 1 addition & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"ngserver": "./bin/ngserver"
},
"dependencies": {
"@angular/language-service": "14.0.0-next.0",
"@angular/language-service": "https://output.circle-artifacts.com/output/job/ad7b0112-3d86-4f40-a595-a399e55bc276/artifacts/0/angular/language-service-pr46764-e7f1b32d29.tgz",
"vscode-jsonrpc": "6.0.0",
"vscode-languageserver": "7.0.0",
"vscode-uri": "3.0.3"
Expand Down
89 changes: 88 additions & 1 deletion server/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {readNgCompletionData, tsCompletionEntryToLspCompletionItem} from './comp
import {tsDiagnosticToLspDiagnostic} from './diagnostic';
import {resolveAndRunNgcc} from './ngcc';
import {ServerHost} from './server_host';
import {filePathToUri, getMappedDefinitionInfo, isConfiguredProject, isDebugMode, lspPositionToTsPosition, lspRangeToTsPositions, MruTracker, tsDisplayPartsToText, tsTextSpanToLspRange, uriToFilePath} from './utils';
import {filePathToUri, fileTextChangesToWorkspaceEdit, getMappedDefinitionInfo, isConfiguredProject, isDebugMode, lspPositionToTsPosition, lspRangeToTsPositions, MruTracker, tsDisplayPartsToText, tsTextSpanToLspRange, uriToFilePath} from './utils';

export interface SessionOptions {
host: ServerHost;
Expand Down Expand Up @@ -197,6 +197,59 @@ export class Session {
conn.onCodeLens(p => this.onCodeLens(p));
conn.onCodeLensResolve(p => this.onCodeLensResolve(p));
conn.onSignatureHelp(p => this.onSignatureHelp(p));
conn.onCodeAction(p => this.codeAction(p));
conn.onCodeActionResolve(p => this.onCodeActionResolve(p));
}

private codeAction(params: lsp.CodeActionParams): lsp.CodeAction[]|null {
const filePath = uriToFilePath(params.textDocument.uri);
const lsInfo = this.getLSAndScriptInfo(params.textDocument);
if (!lsInfo) {
return null;
}
const start = lspPositionToTsPosition(lsInfo.scriptInfo, params.range.start);
const end = lspPositionToTsPosition(lsInfo.scriptInfo, params.range.end);
const errorCodes = params.context.diagnostics.map(diag => diag.code)
.filter((code): code is number => typeof code === 'number');

const codeActions =
lsInfo.languageService.getCodeFixesAtPosition(filePath, start, end, errorCodes, {}, {});
return codeActions
.map<lsp.CodeAction>(codeAction => {
return {
title: codeAction.description,
kind: lsp.CodeActionKind.QuickFix,
diagnostics: params.context.diagnostics,
edit: fileTextChangesToWorkspaceEdit(
codeAction.changes, (path: string) => this.projectService.getScriptInfo(path)),
};
})
.concat(getCodeFixesAll(codeActions, params.textDocument));
}

private onCodeActionResolve(param: lsp.CodeAction): lsp.CodeAction {
const codeActionResolve = param.data as unknown as CodeActionResolve;
const isCodeFixesAll = codeActionResolve.fixId !== undefined;
if (!isCodeFixesAll) {
return param;
}
const filePath = uriToFilePath(codeActionResolve.document.uri);
const lsInfo = this.getLSAndScriptInfo(codeActionResolve.document);
if (!lsInfo) {
return param;
}
const fixesAllChanges = lsInfo.languageService.getCombinedCodeFix(
{
type: 'file',
fileName: filePath,
},
codeActionResolve.fixId as {}, {}, {});

return {
title: codeActionResolve.title,
edit: fileTextChangesToWorkspaceEdit(
fixesAllChanges.changes, (path) => this.projectService.getScriptInfo(path)),
};
}

private isInAngularProject(params: IsInAngularProjectParams): boolean|null {
Expand Down Expand Up @@ -663,6 +716,10 @@ export class Session {
workspace: {
workspaceFolders: {supported: true},
},
codeActionProvider: this.ivy ? {
resolveProvider: true,
} :
undefined,
},
serverOptions,
};
Expand Down Expand Up @@ -1239,3 +1296,33 @@ function isTypeScriptFile(path: string): boolean {
function isExternalTemplate(path: string): boolean {
return !isTypeScriptFile(path);
}

interface CodeActionResolve {
fixId?: string;
document: lsp.TextDocumentIdentifier;
title: string;
}

function getCodeFixesAll(
codeActions: readonly ts.CodeFixAction[],
document: lsp.TextDocumentIdentifier): lsp.CodeAction[] {
const seen = new Set<string>();
const lspCodeActions: lsp.CodeAction[] = [];
for (const codeAction of codeActions) {
const fixId = codeAction.fixId as string | undefined;
if (fixId === undefined || codeAction.fixAllDescription === undefined || seen.has(fixId)) {
continue;
}
seen.add(fixId);
const codeActionResolve: CodeActionResolve = {
fixId,
document,
title: codeAction.fixAllDescription,
};
lspCodeActions.push({
title: codeAction.fixAllDescription,
data: codeActionResolve,
});
}
return lspCodeActions;
}
26 changes: 26 additions & 0 deletions server/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,32 @@ export function filePathToUri(filePath: string): lsp.DocumentUri {
return URI.file(filePath).toString();
}

export function fileTextChangesToWorkspaceEdit(
changes: readonly ts.FileTextChanges[],
getScriptInfo: (path: string) => ts.server.ScriptInfo | undefined): lsp.WorkspaceEdit {
const workspaceChanges: {[uri: string]: lsp.TextEdit[]} = {};
for (const change of changes) {
const scriptInfo = getScriptInfo(change.fileName);
const uri = filePathToUri(change.fileName);
if (scriptInfo === undefined) {
continue;
}
if (!workspaceChanges[uri]) {
workspaceChanges[uri] = [];
}
for (const textChange of change.textChanges) {
const textEdit: lsp.TextEdit = {
newText: textChange.newText,
range: tsTextSpanToLspRange(scriptInfo, textChange.span),
};
workspaceChanges[uri].push(textEdit);
}
}
return {
changes: workspaceChanges,
};
}

/**
* Convert ts.TextSpan to lsp.TextSpan. TypeScript keeps track of offset using
* 1-based index whereas LSP uses 0-based index.
Expand Down
7 changes: 3 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,9 @@
uuid "^8.3.2"
yargs "^17.0.0"

"@angular/language-service@14.1.0-next.0":
version "14.1.0-next.0"
resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-14.1.0-next.0.tgz#2cc7c30a7fe641ee2d255f03ef23028e8e574ab5"
integrity sha512-tMsrL/Ug35hnH14BjpLYdjy44F3Tzrfqem38vxWUyATwV1YLiqJrvzsUhvzMffyBAkMMgdfykrb50wyguUg/fQ==
"@angular/language-service@https://output.circle-artifacts.com/output/job/ad7b0112-3d86-4f40-a595-a399e55bc276/artifacts/0/angular/language-service-pr46764-e7f1b32d29.tgz":
version "14.1.0-next.4"
resolved "https://output.circle-artifacts.com/output/job/ad7b0112-3d86-4f40-a595-a399e55bc276/artifacts/0/angular/language-service-pr46764-e7f1b32d29.tgz#28b6dcb0af3ce46512981a5f3acfaef742ed2626"

"@assemblyscript/loader@^0.10.1":
version "0.10.1"
Expand Down

0 comments on commit ce3f9d8

Please sign in to comment.