Skip to content

Commit 24b352d

Browse files
authored
Feat/upstream (#71)
* feat: Add option to disable ngcc * feat: add command to run ngcc manually * feat(extension): Add option to force strict templates * feat(extension): Update untrusted workspace support * feat provide folding ranges for inline templates * Ensure Angular code actions are only retrieved in angular contexts * feat: Add option to disable code actions * Revert "feat: Add option to disable code actions" This reverts commit 9b40f81. * feat: Remove ngcc from extension * refactor: remove support for VE projects * feat: Support inline styles as string * fix: fix detection of Angular context after string interpolation * refactor(client): Show warning message if a project uses a newer version than extension * fix: Disable block syntax parsing when no project in workspace supports it * feat(client): v17.0.1 - upgrade server to v17.0.1 - support to show the tag info in the jsDoc - remove view-engine config option - remove provideCompletionItem middleware
1 parent 3544e90 commit 24b352d

File tree

194 files changed

+572
-118823
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

194 files changed

+572
-118823
lines changed

.vim/coc-settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"cSpell.words": [
3-
"mutex"
3+
"Ngcc",
4+
"mutex",
5+
"nvim",
6+
"untrusted"
47
]
58
}

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Angular Language Service
22

3-
> fork from [angular/vscode-ng-language-service](https://github.com/angular/vscode-ng-language-service) v13.3.4
4-
> [commit](https://github.com/angular/vscode-ng-language-service/commit/6d1a664e05ec569d96afdcfc871acb176e8ff846)
3+
> fork from [angular/vscode-ng-language-service](https://github.com/angular/vscode-ng-language-service) v17.0.1
4+
> [commit](https://github.com/angular/vscode-ng-language-service/commit/1dd740af951782d8ae19420bc553dee305b02eac)
55
66
An angular language service coc extension for (neo)vim 💖
77

8-
**Note:** require nodejs >= v12 for `view-engine` and nodejs >= v14 for `lvy`
8+
**Note:** only version <= 13.3.6 support view-engine
99

1010
## Install
1111

@@ -39,7 +39,6 @@ and external templates including:
3939

4040
- `angular.trace.server` enable angular language server trace log
4141
- `angular.log` Enables logging of the Angular server to a file. This log can be used to diagnose Angular Server issues. The log may contain file paths, source code, and other potentially sensitive information from your project.
42-
- `angular.view-engine` Use legacy View Engine language service.
4342
- `angular.suggest.includeAutomaticOptionalChainCompletions` 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.
4443
- `angular.suggest.includeCompletionsWithSnippetText` 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.
4544

package.json

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "coc-angular",
33
"description": "Editor services for Angular templates",
4-
"version": "13.3.6",
4+
"version": "17.0.1",
55
"keywords": [
66
"coc.nvim",
77
"angular",
@@ -16,12 +16,15 @@
1616
"url": "https://github.com/iamcco/coc-angular"
1717
},
1818
"engines": {
19-
"coc": "^0.0.80"
19+
"coc": "^0.0.82"
2020
},
2121
"capabilities": {
2222
"untrustedWorkspaces": {
23-
"supported": false,
24-
"description": "This extension requires workspace trust because it needs to execute ngcc from the node_modules in the workspace."
23+
"supported": true
24+
},
25+
"virtualWorkspaces": {
26+
"supported": "limited",
27+
"description": "The Language Server Protocol does not support remote file systems. Functionality is limited to syntax highlighting only."
2528
}
2629
},
2730
"main": "./out/index.js",
@@ -81,11 +84,6 @@
8184
"default": "off",
8285
"description": "Enables logging of the Angular server to a file. This log can be used to diagnose Angular Server issues. The log may contain file paths, source code, and other potentially sensitive information from your project."
8386
},
84-
"angular.view-engine": {
85-
"type": "boolean",
86-
"default": false,
87-
"description": "Use legacy View Engine language service. This option is incompatible with projects using Angular v13 and above."
88-
},
8987
"angular.enable-strict-mode-prompt": {
9088
"type": "boolean",
9189
"default": true,
@@ -100,6 +98,11 @@
10098
"type": "boolean",
10199
"default": true,
102100
"description": "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."
101+
},
102+
"angular.forceStrictTemplates": {
103+
"type": "boolean",
104+
"default": false,
105+
"markdownDescription": "Enabling this option will force the language service to use [strictTemplates](https://angular.io/guide/angular-compiler-options#stricttemplates) and ignore the user settings in the `tsconfig.json`."
103106
}
104107
}
105108
}
@@ -109,16 +112,15 @@
109112
"watch": "tsc -w -p ./"
110113
},
111114
"devDependencies": {
112-
"@types/node": "^10.9.4",
113-
"coc.nvim": "^0.0.80",
114-
"ts-loader": "^8.0.14",
115-
"vscode-languageserver-protocol": "^3.16.0",
116-
"webpack": "^5.19.0",
117-
"webpack-cli": "^4.4.0"
115+
"@types/node": "^20.9.0",
116+
"coc.nvim": "^0.0.83-next.9",
117+
"ts-loader": "^9.5.0",
118+
"vscode-languageserver-protocol": "^3.17.5",
119+
"webpack": "^5.89.0",
120+
"webpack-cli": "^5.1.4"
118121
},
119122
"dependencies": {
120-
"v12_language_service": "file:v12_language_service",
121-
"@angular/language-server": "13.3.4",
122-
"typescript": "~4.6.2"
123+
"@angular/language-server": "17.0.1",
124+
"typescript": "5.2.2"
123125
}
124126
}

src/client.ts

Lines changed: 73 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ import * as vscode from 'coc.nvim';
1212

1313
import {OpenOutputChannel, ProjectLoadingFinish, ProjectLoadingStart, SuggestStrictMode, SuggestStrictModeParams} from './common/notifications';
1414
import {GetCompleteItems, GetComponentsWithTemplateFile, GetHoverInfo, GetTcbRequest, GetTemplateLocationForComponent, IsInAngularProject} from './common/requests';
15-
import {provideCompletionItem} from './middleware/provideCompletionItem';
16-
import {resolve, Version} from './common/resolver';
15+
import {NodeModule, resolve} from './common/resolver';
1716

1817
import {isInsideComponentDecorator, isInsideInlineTemplateRegion, isInsideStringLiteral} from './embedded_support';
1918
import {code2ProtocolConverter, protocol2CodeConverter} from './common/utils';
@@ -63,10 +62,22 @@ export class AngularLanguageClient implements vscode.Disposable {
6362
// Don't let our output console pop open
6463
revealOutputChannelOn: vscode.RevealOutputChannelOn.Never,
6564
outputChannel: this.outputChannel,
65+
markdown: {
66+
isTrusted: true,
67+
},
6668
// middleware
6769
middleware: {
70+
provideCodeActions: async (
71+
document: vscode.LinesTextDocument, range: vscode.Range, context: vscode.CodeActionContext,
72+
token: vscode.CancellationToken, next: vscode.ProvideCodeActionsSignature) => {
73+
if (await this.isInAngularProject(document) &&
74+
isInsideInlineTemplateRegion(document, range.start) &&
75+
isInsideInlineTemplateRegion(document, range.end)) {
76+
return next(document, range, context, token);
77+
}
78+
},
6879
prepareRename: async (
69-
document: vscode.TextDocument, position: vscode.Position,
80+
document: vscode.LinesTextDocument, position: vscode.Position,
7081
token: vscode.CancellationToken, next: vscode.PrepareRenameSignature) => {
7182
// We are able to provide renames for many types of string literals: template strings,
7283
// pipe names, and hopefully in the future selectors and input/output aliases. Because
@@ -82,23 +93,23 @@ export class AngularLanguageClient implements vscode.Disposable {
8293
}
8394
},
8495
provideDefinition: async (
85-
document: vscode.TextDocument, position: vscode.Position,
96+
document: vscode.LinesTextDocument, position: vscode.Position,
8697
token: vscode.CancellationToken, next: vscode.ProvideDefinitionSignature) => {
8798
if (await this.isInAngularProject(document) &&
8899
isInsideComponentDecorator(document, position)) {
89100
return next(document, position, token);
90101
}
91102
},
92103
provideTypeDefinition: async (
93-
document: vscode.TextDocument, position: vscode.Position,
104+
document: vscode.LinesTextDocument, position: vscode.Position,
94105
token: vscode.CancellationToken, next) => {
95106
if (await this.isInAngularProject(document) &&
96107
isInsideInlineTemplateRegion(document, position)) {
97108
return next(document, position, token);
98109
}
99110
},
100111
provideHover: async (
101-
document: vscode.TextDocument, position: vscode.Position,
112+
document: vscode.LinesTextDocument, position: vscode.Position,
102113
token: vscode.CancellationToken, next: vscode.ProvideHoverSignature) => {
103114
if (!(await this.isInAngularProject(document)) ||
104115
!isInsideInlineTemplateRegion(document, position)) {
@@ -126,7 +137,7 @@ export class AngularLanguageClient implements vscode.Disposable {
126137
return angularResultsPromise;
127138
},
128139
provideSignatureHelp: async (
129-
document: vscode.TextDocument, position: vscode.Position,
140+
document: vscode.LinesTextDocument, position: vscode.Position,
130141
context: vscode.SignatureHelpContext, token: vscode.CancellationToken,
131142
next: vscode.ProvideSignatureHelpSignature) => {
132143
if (await this.isInAngularProject(document) &&
@@ -135,7 +146,7 @@ export class AngularLanguageClient implements vscode.Disposable {
135146
}
136147
},
137148
provideCompletionItem: async (
138-
document: vscode.TextDocument, position: vscode.Position,
149+
document: vscode.LinesTextDocument, position: vscode.Position,
139150
context: vscode.CompletionContext, token: vscode.CancellationToken,
140151
next: vscode.ProvideCompletionItemsSignature) => {
141152
// If not in inline template, do not perform request forwarding
@@ -167,7 +178,15 @@ export class AngularLanguageClient implements vscode.Disposable {
167178
return [...(angularCompletions ?? []), ...(htmlProviderCompletions?.items ?? [])];
168179
}
169180

170-
return angularCompletionsPromise.then(items => provideCompletionItem(document, position, items ?? []));
181+
return angularCompletionsPromise;
182+
},
183+
provideFoldingRanges: async (
184+
document: vscode.LinesTextDocument, context: vscode.FoldingContext,
185+
token: vscode.CancellationToken, next) => {
186+
if (!await this.isInAngularProject(document)) {
187+
return null;
188+
}
189+
return next(document, context, token);
171190
}
172191
}
173192
};
@@ -232,7 +251,7 @@ export class AngularLanguageClient implements vscode.Disposable {
232251
this.clientOptions,
233252
forceDebug,
234253
);
235-
this.disposables.push(this.client.start());
254+
vscode.services.registerLanguageClient(this.client);
236255
await this.client.onReady();
237256
// Must wait for the client to be ready before registering notification
238257
// handlers.
@@ -364,7 +383,8 @@ function registerNotificationHandlers(client: vscode.LanguageClient) {
364383
})
365384
client.onNotification(SuggestStrictMode, async (params: SuggestStrictModeParams) => {
366385
const config = vscode.workspace.getConfiguration();
367-
if (config.get('angular.enable-strict-mode-prompt') === false) {
386+
if (config.get('angular.enable-strict-mode-prompt') === false ||
387+
config.get('angular.forceStrictTemplates')) {
368388
return;
369389
}
370390
const openTsConfig = 'Open tsconfig.json';
@@ -416,7 +436,7 @@ function getProbeLocations(bundled: string): string[] {
416436
* Construct the arguments that's used to spawn the server process.
417437
* @param ctx vscode extension context
418438
*/
419-
function constructArgs(ctx: vscode.ExtensionContext, viewEngine: boolean): string[] {
439+
function constructArgs(ctx: vscode.ExtensionContext): string[] {
420440
const config = vscode.workspace.getConfiguration();
421441
const args: string[] = ['--logToConsole'];
422442

@@ -429,15 +449,7 @@ function constructArgs(ctx: vscode.ExtensionContext, viewEngine: boolean): strin
429449
}
430450

431451
const ngProbeLocations = getProbeLocations(ctx.extensionPath);
432-
if (viewEngine) {
433-
args.push('--viewEngine');
434-
args.push('--ngProbeLocations', [
435-
path.join(ctx.extensionPath, 'v12_language_service'),
436-
...ngProbeLocations,
437-
].join(','));
438-
} else {
439-
args.push('--ngProbeLocations', ngProbeLocations.join(','));
440-
}
452+
args.push('--ngProbeLocations', ngProbeLocations.join(','));
441453

442454
const includeAutomaticOptionalChainCompletions =
443455
config.get<boolean>('angular.suggest.includeAutomaticOptionalChainCompletions');
@@ -451,6 +463,18 @@ function constructArgs(ctx: vscode.ExtensionContext, viewEngine: boolean): strin
451463
args.push('--includeCompletionsWithSnippetText');
452464
}
453465

466+
const angularVersions = getAngularVersionsInWorkspace();
467+
// Only disable block syntax if we find angular/core and every one we find does not support block
468+
// syntax
469+
if (angularVersions.size > 0 && Array.from(angularVersions).every(v => v.version.major < 17)) {
470+
args.push('--disableBlockSyntax');
471+
}
472+
473+
const forceStrictTemplates = config.get<boolean>('angular.forceStrictTemplates');
474+
if (forceStrictTemplates) {
475+
args.push('--forceStrictTemplates');
476+
}
477+
454478
const tsdk: string|null = config.get('typescript.tsdk', null);
455479
const tsProbeLocations = [tsdk, ...getProbeLocations(ctx.extensionPath)];
456480
args.push('--tsProbeLocations', tsProbeLocations.join(','));
@@ -466,35 +490,19 @@ function getServerOptions(ctx: vscode.ExtensionContext, debug: boolean): vscode.
466490
NG_DEBUG: true,
467491
};
468492

469-
// Because the configuration is typed as "boolean" in package.json, vscode
470-
// will return false even when the value is not set. If value is false, then
471-
// we need to check if all projects support Ivy language service.
472-
const config = vscode.workspace.getConfiguration();
473-
let viewEngine: boolean = config.get('angular.view-engine') || !allProjectsSupportIvy();
474-
if (viewEngine && !allProjectsSupportVE()) {
475-
viewEngine = false;
476-
if (config.get('angular.view-engine')) {
477-
vscode.window.showErrorMessage(
478-
`The legacy View Engine option is enabled but the workspace contains a version 13 Angular project.` +
479-
` Legacy View Engine will be disabled since support for it was dropped in v13.`,
480-
);
481-
} else if (!allProjectsSupportIvy() && !allProjectsSupportVE()) {
482-
vscode.window.showErrorMessage(
483-
`The workspace contains a project that does not support legacy View Engine (Angular v13+) and a project that does not support the new current runtime (v8 and below).` +
484-
`The extension will not work for the legacy project in this workspace.`);
485-
}
486-
}
487-
488493
// Node module for the language server
489-
const args = constructArgs(ctx, viewEngine);
494+
const args = constructArgs(ctx);
490495
const prodBundle = ctx.asAbsolutePath(path.join('node_modules', '@angular', 'language-server'));
491496
const devBundle = ctx.asAbsolutePath(path.join('node_modules', '@angular', 'language-server'));
492497
// VS Code Insider launches extensions in debug mode by default but users
493498
// install prod bundle so we have to check whether dev bundle exists.
494499
const latestServerModule = debug && fs.existsSync(devBundle) ? devBundle : prodBundle;
495-
const v12ServerModule = ctx.asAbsolutePath(
496-
path.join('node_modules', 'v12_language_service', 'node_modules', '@angular', 'language-server'));
497-
const module = viewEngine ? v12ServerModule : latestServerModule;
500+
501+
if (!extensionVersionCompatibleWithAllProjects(latestServerModule)) {
502+
vscode.window.showWarningMessage(
503+
`A project in the workspace is using a newer version of Angular than the language service extension. ` +
504+
`This may cause the extension to show incorrect diagnostics.`);
505+
}
498506

499507
// Argv options for Node.js
500508
const prodExecArgv: string[] = [];
@@ -508,7 +516,7 @@ function getServerOptions(ctx: vscode.ExtensionContext, debug: boolean): vscode.
508516
return {
509517
// VS Code Insider launches extensions in debug mode by default but users
510518
// install prod bundle so we have to check whether dev bundle exists.
511-
module,
519+
module: latestServerModule,
512520
transport: vscode.TransportKind.ipc,
513521
args,
514522
options: {
@@ -518,24 +526,38 @@ function getServerOptions(ctx: vscode.ExtensionContext, debug: boolean): vscode.
518526
};
519527
}
520528

521-
function allProjectsSupportIvy() {
529+
function extensionVersionCompatibleWithAllProjects(serverModuleLocation: string): boolean {
530+
const languageServiceVersion =
531+
resolve('@angular/language-service', serverModuleLocation)?.version;
532+
if (languageServiceVersion === undefined) {
533+
return true;
534+
}
535+
522536
const workspaceFolders = vscode.workspace.workspaceFolders || [];
523537
for (const workspaceFolder of workspaceFolders) {
524538
const angularCore = resolve('@angular/core', vscode.Uri.parse(workspaceFolder.uri).fsPath);
525-
if (angularCore?.version.greaterThanOrEqual(new Version('9')) === false) {
539+
if (angularCore === undefined) {
540+
continue;
541+
}
542+
if (!languageServiceVersion.greaterThanOrEqual(angularCore.version, 'minor')) {
526543
return false;
527544
}
528545
}
529546
return true;
530547
}
531548

532-
function allProjectsSupportVE() {
549+
/**
550+
* Returns true if any project in the workspace supports block syntax (v17+).
551+
*/
552+
function getAngularVersionsInWorkspace(): Set<NodeModule> {
553+
const angularCoreModules = new Set<NodeModule>();
533554
const workspaceFolders = vscode.workspace.workspaceFolders || [];
534555
for (const workspaceFolder of workspaceFolders) {
535556
const angularCore = resolve('@angular/core', vscode.Uri.parse(workspaceFolder.uri).fsPath);
536-
if (angularCore?.version.greaterThanOrEqual(new Version('13')) === true) {
537-
return false;
557+
if (angularCore === undefined) {
558+
continue;
538559
}
560+
angularCoreModules.add(angularCore);
539561
}
540-
return true;
562+
return angularCoreModules;
541563
}

0 commit comments

Comments
 (0)