Skip to content

Commit

Permalink
feat: provide snippets for attribute (#1509)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanwonder authored Oct 14, 2021
1 parent 7a3ac82 commit 0428c31
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 4 deletions.
6 changes: 6 additions & 0 deletions client/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,12 @@ function constructArgs(ctx: vscode.ExtensionContext): string[] {
args.push('--includeAutomaticOptionalChainCompletions');
}

const includeCompletionsWithSnippetText =
config.get<boolean>('angular.suggest.includeCompletionsWithSnippetText');
if (includeCompletionsWithSnippetText) {
args.push('--includeCompletionsWithSnippetText');
}

const tsdk: string|null = config.get('typescript.tsdk', null);
const tsProbeLocations = getProbeLocations(tsdk, ctx.extensionPath);
args.push('--tsProbeLocations', tsProbeLocations.join(','));
Expand Down
54 changes: 54 additions & 0 deletions integration/lsp/ivy_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,60 @@ describe('auto-apply optional chaining', () => {
});
});

describe('insert snippet text', () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; /* 10 seconds */

let client: MessageConnection;
beforeEach(async () => {
client = createConnection({
ivy: true,
includeCompletionsWithSnippetText: true,
});
// If debugging, set to
// - lsp.Trace.Messages to inspect request/response/notification, or
// - lsp.Trace.Verbose to inspect payload
client.trace(lsp.Trace.Off, createTracer());
client.listen();
await initializeServer(client);
});

afterEach(() => {
client.dispose();
});

it('should be able to complete for an attribute with the value is empty', async () => {
openTextDocument(client, FOO_TEMPLATE, `<my-app appOut></my-app>`);
const languageServiceEnabled = await waitForNgcc(client);
expect(languageServiceEnabled).toBeTrue();
const response = await client.sendRequest(lsp.CompletionRequest.type, {
textDocument: {
uri: FOO_TEMPLATE_URI,
},
position: {line: 0, character: 14},
}) as lsp.CompletionItem[];
const completion = response.find(i => i.label === '(appOutput)')!;
expect(completion.kind).toEqual(lsp.CompletionItemKind.Property);
expect(completion.insertTextFormat).toEqual(lsp.InsertTextFormat.Snippet);
expect((completion.textEdit as lsp.TextEdit).newText).toEqual('(appOutput)="$1"');
});

it('should not be included in the completion for an attribute with a value', async () => {
openTextDocument(client, FOO_TEMPLATE, `<my-app [appInput]="1"></my-app>`);
const languageServiceEnabled = await waitForNgcc(client);
expect(languageServiceEnabled).toBeTrue();
const response = await client.sendRequest(lsp.CompletionRequest.type, {
textDocument: {
uri: FOO_TEMPLATE_URI,
},
position: {line: 0, character: 17},
}) as lsp.CompletionItem[];
const completion = response.find(i => i.label === 'appInput')!;
expect(completion.kind).toEqual(lsp.CompletionItemKind.Property);
expect(completion.insertTextFormat).toBeUndefined;
expect((completion.textEdit as lsp.TextEdit).newText).toEqual('appInput');
});
});

function onNgccProgress(client: MessageConnection): Promise<string> {
return new Promise(resolve => {
client.onProgress(NgccProgressType, NgccProgressToken, (params: NgccProgress) => {
Expand Down
15 changes: 14 additions & 1 deletion integration/lsp/test_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {PROJECT_PATH, SERVER_PATH} from '../test_constants';
export interface ServerOptions {
ivy: boolean;
includeAutomaticOptionalChainCompletions?: boolean;
includeCompletionsWithSnippetText?: boolean;
}

export function createConnection(serverOptions: ServerOptions): MessageConnection {
Expand All @@ -33,6 +34,9 @@ export function createConnection(serverOptions: ServerOptions): MessageConnectio
if (serverOptions.includeAutomaticOptionalChainCompletions) {
argv.push('--includeAutomaticOptionalChainCompletions');
}
if (serverOptions.includeCompletionsWithSnippetText) {
argv.push('--includeCompletionsWithSnippetText');
}
const server = fork(SERVER_PATH, argv, {
cwd: PROJECT_PATH,
// uncomment to debug server process
Expand Down Expand Up @@ -61,7 +65,16 @@ export function initializeServer(client: MessageConnection): Promise<lsp.Initial
*/
processId: process.pid,
rootUri: `file://${PROJECT_PATH}`,
capabilities: {},
capabilities: {
textDocument: {
completion: {
completionItem: {
snippetSupport: true,
}
},
moniker: {},
}
},
/**
* Options are 'off' | 'messages' | 'verbose'.
* To debug test failure, set to 'verbose'.
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@
"type": "boolean",
"default": true,
"description": "Enable/disable showing completions on potentially undefined values that insert an optional chain call. Requires TS 3.7+ and strict null checks to be enabled."
},
"angular.suggest.includeCompletionsWithSnippetText": {
"type": "boolean",
"default": true,
"markdownDescription": "Enable/disable snippet completions from Angular language server. Requires using TypeScript 4.3+ in the workspace and the `legacy View Engine` option to be disabled."
}
}
},
Expand Down
2 changes: 2 additions & 0 deletions server/src/cmdline_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ interface CommandLineOptions {
ngProbeLocations: string[];
tsProbeLocations: string[];
includeAutomaticOptionalChainCompletions: boolean;
includeCompletionsWithSnippetText: boolean;
}

export function parseCommandLine(argv: string[]): CommandLineOptions {
Expand All @@ -51,6 +52,7 @@ export function parseCommandLine(argv: string[]): CommandLineOptions {
tsProbeLocations: parseStringArray(argv, '--tsProbeLocations'),
includeAutomaticOptionalChainCompletions:
hasArgument(argv, '--includeAutomaticOptionalChainCompletions'),
includeCompletionsWithSnippetText: hasArgument(argv, '--includeCompletionsWithSnippetText'),
};
}

Expand Down
3 changes: 3 additions & 0 deletions server/src/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ export function tsCompletionEntryToLspCompletionItem(
// range will include the dot. the `insertText` should be assigned to the `filterText` to filter
// the completion items.
item.filterText = entry.insertText;
if (entry.isSnippet) {
item.insertTextFormat = lsp.InsertTextFormat.Snippet;
}
}

item.data = {
Expand Down
3 changes: 2 additions & 1 deletion server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ function main() {
resolvedNgLsPath: ng.resolvedPath,
ivy: isG3 ? true : options.ivy,
logToConsole: options.logToConsole,
includeAutomaticOptionalChainCompletions: options.includeAutomaticOptionalChainCompletions
includeAutomaticOptionalChainCompletions: options.includeAutomaticOptionalChainCompletions,
includeCompletionsWithSnippetText: options.includeCompletionsWithSnippetText,
});

// Log initialization info
Expand Down
14 changes: 12 additions & 2 deletions server/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface SessionOptions {
ivy: boolean;
logToConsole: boolean;
includeAutomaticOptionalChainCompletions: boolean;
includeCompletionsWithSnippetText: boolean;
}

enum LanguageId {
Expand All @@ -54,6 +55,8 @@ export class Session {
private readonly logToConsole: boolean;
private readonly openFiles = new MruTracker();
private readonly includeAutomaticOptionalChainCompletions: boolean;
private readonly includeCompletionsWithSnippetText: boolean;
private snippetSupport: boolean|undefined;
// Tracks the spawn order and status of the `ngcc` processes. This allows us to ensure we enable
// the LS in the same order the projects were created in.
private projectNgccQueue: Array<{project: ts.server.Project, done: boolean}> = [];
Expand All @@ -71,6 +74,7 @@ export class Session {
constructor(options: SessionOptions) {
this.includeAutomaticOptionalChainCompletions =
options.includeAutomaticOptionalChainCompletions;
this.includeCompletionsWithSnippetText = options.includeCompletionsWithSnippetText;
this.logger = options.logger;
this.ivy = options.ivy;
this.logToConsole = options.logToConsole;
Expand Down Expand Up @@ -605,6 +609,8 @@ export class Session {
}

private onInitialize(params: lsp.InitializeParams): lsp.InitializeResult {
this.snippetSupport =
params.capabilities.textDocument?.completion?.completionItem?.snippetSupport;
const serverOptions: ServerOptions = {
logFile: this.logger.getLogFileName(),
};
Expand Down Expand Up @@ -1007,10 +1013,14 @@ export class Session {
const offset = lspPositionToTsPosition(scriptInfo, params.position);

let options: ts.GetCompletionsAtPositionOptions = {};
if (this.includeAutomaticOptionalChainCompletions) {
const includeCompletionsWithSnippetText =
this.includeCompletionsWithSnippetText && this.snippetSupport;
if (this.includeAutomaticOptionalChainCompletions || includeCompletionsWithSnippetText) {
options = {
includeAutomaticOptionalChainCompletions: this.includeAutomaticOptionalChainCompletions,
includeCompletionsWithInsertText: this.includeAutomaticOptionalChainCompletions,
includeCompletionsWithSnippetText: includeCompletionsWithSnippetText,
includeCompletionsWithInsertText:
this.includeAutomaticOptionalChainCompletions || includeCompletionsWithSnippetText,
};
}

Expand Down

0 comments on commit 0428c31

Please sign in to comment.