Skip to content

Commit

Permalink
Feat/upstream (#71)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
iamcco authored Nov 11, 2023
1 parent 3544e90 commit 24b352d
Show file tree
Hide file tree
Showing 194 changed files with 572 additions and 118,823 deletions.
5 changes: 4 additions & 1 deletion .vim/coc-settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"cSpell.words": [
"mutex"
"Ngcc",
"mutex",
"nvim",
"untrusted"
]
}
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Angular Language Service

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

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

## Install

Expand Down Expand Up @@ -39,7 +39,6 @@ and external templates including:

- `angular.trace.server` enable angular language server trace log
- `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.
- `angular.view-engine` Use legacy View Engine language service.
- `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.
- `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.

Expand Down
38 changes: 20 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "coc-angular",
"description": "Editor services for Angular templates",
"version": "13.3.6",
"version": "17.0.1",
"keywords": [
"coc.nvim",
"angular",
Expand All @@ -16,12 +16,15 @@
"url": "https://github.com/iamcco/coc-angular"
},
"engines": {
"coc": "^0.0.80"
"coc": "^0.0.82"
},
"capabilities": {
"untrustedWorkspaces": {
"supported": false,
"description": "This extension requires workspace trust because it needs to execute ngcc from the node_modules in the workspace."
"supported": true
},
"virtualWorkspaces": {
"supported": "limited",
"description": "The Language Server Protocol does not support remote file systems. Functionality is limited to syntax highlighting only."
}
},
"main": "./out/index.js",
Expand Down Expand Up @@ -81,11 +84,6 @@
"default": "off",
"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."
},
"angular.view-engine": {
"type": "boolean",
"default": false,
"description": "Use legacy View Engine language service. This option is incompatible with projects using Angular v13 and above."
},
"angular.enable-strict-mode-prompt": {
"type": "boolean",
"default": true,
Expand All @@ -100,6 +98,11 @@
"type": "boolean",
"default": true,
"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."
},
"angular.forceStrictTemplates": {
"type": "boolean",
"default": false,
"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`."
}
}
}
Expand All @@ -109,16 +112,15 @@
"watch": "tsc -w -p ./"
},
"devDependencies": {
"@types/node": "^10.9.4",
"coc.nvim": "^0.0.80",
"ts-loader": "^8.0.14",
"vscode-languageserver-protocol": "^3.16.0",
"webpack": "^5.19.0",
"webpack-cli": "^4.4.0"
"@types/node": "^20.9.0",
"coc.nvim": "^0.0.83-next.9",
"ts-loader": "^9.5.0",
"vscode-languageserver-protocol": "^3.17.5",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4"
},
"dependencies": {
"v12_language_service": "file:v12_language_service",
"@angular/language-server": "13.3.4",
"typescript": "~4.6.2"
"@angular/language-server": "17.0.1",
"typescript": "5.2.2"
}
}
124 changes: 73 additions & 51 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import * as vscode from 'coc.nvim';

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

import {isInsideComponentDecorator, isInsideInlineTemplateRegion, isInsideStringLiteral} from './embedded_support';
import {code2ProtocolConverter, protocol2CodeConverter} from './common/utils';
Expand Down Expand Up @@ -63,10 +62,22 @@ export class AngularLanguageClient implements vscode.Disposable {
// Don't let our output console pop open
revealOutputChannelOn: vscode.RevealOutputChannelOn.Never,
outputChannel: this.outputChannel,
markdown: {
isTrusted: true,
},
// middleware
middleware: {
provideCodeActions: async (
document: vscode.LinesTextDocument, range: vscode.Range, context: vscode.CodeActionContext,
token: vscode.CancellationToken, next: vscode.ProvideCodeActionsSignature) => {
if (await this.isInAngularProject(document) &&
isInsideInlineTemplateRegion(document, range.start) &&
isInsideInlineTemplateRegion(document, range.end)) {
return next(document, range, context, token);
}
},
prepareRename: async (
document: vscode.TextDocument, position: vscode.Position,
document: vscode.LinesTextDocument, position: vscode.Position,
token: vscode.CancellationToken, next: vscode.PrepareRenameSignature) => {
// We are able to provide renames for many types of string literals: template strings,
// pipe names, and hopefully in the future selectors and input/output aliases. Because
Expand All @@ -82,23 +93,23 @@ export class AngularLanguageClient implements vscode.Disposable {
}
},
provideDefinition: async (
document: vscode.TextDocument, position: vscode.Position,
document: vscode.LinesTextDocument, position: vscode.Position,
token: vscode.CancellationToken, next: vscode.ProvideDefinitionSignature) => {
if (await this.isInAngularProject(document) &&
isInsideComponentDecorator(document, position)) {
return next(document, position, token);
}
},
provideTypeDefinition: async (
document: vscode.TextDocument, position: vscode.Position,
document: vscode.LinesTextDocument, position: vscode.Position,
token: vscode.CancellationToken, next) => {
if (await this.isInAngularProject(document) &&
isInsideInlineTemplateRegion(document, position)) {
return next(document, position, token);
}
},
provideHover: async (
document: vscode.TextDocument, position: vscode.Position,
document: vscode.LinesTextDocument, position: vscode.Position,
token: vscode.CancellationToken, next: vscode.ProvideHoverSignature) => {
if (!(await this.isInAngularProject(document)) ||
!isInsideInlineTemplateRegion(document, position)) {
Expand Down Expand Up @@ -126,7 +137,7 @@ export class AngularLanguageClient implements vscode.Disposable {
return angularResultsPromise;
},
provideSignatureHelp: async (
document: vscode.TextDocument, position: vscode.Position,
document: vscode.LinesTextDocument, position: vscode.Position,
context: vscode.SignatureHelpContext, token: vscode.CancellationToken,
next: vscode.ProvideSignatureHelpSignature) => {
if (await this.isInAngularProject(document) &&
Expand All @@ -135,7 +146,7 @@ export class AngularLanguageClient implements vscode.Disposable {
}
},
provideCompletionItem: async (
document: vscode.TextDocument, position: vscode.Position,
document: vscode.LinesTextDocument, position: vscode.Position,
context: vscode.CompletionContext, token: vscode.CancellationToken,
next: vscode.ProvideCompletionItemsSignature) => {
// If not in inline template, do not perform request forwarding
Expand Down Expand Up @@ -167,7 +178,15 @@ export class AngularLanguageClient implements vscode.Disposable {
return [...(angularCompletions ?? []), ...(htmlProviderCompletions?.items ?? [])];
}

return angularCompletionsPromise.then(items => provideCompletionItem(document, position, items ?? []));
return angularCompletionsPromise;
},
provideFoldingRanges: async (
document: vscode.LinesTextDocument, context: vscode.FoldingContext,
token: vscode.CancellationToken, next) => {
if (!await this.isInAngularProject(document)) {
return null;
}
return next(document, context, token);
}
}
};
Expand Down Expand Up @@ -232,7 +251,7 @@ export class AngularLanguageClient implements vscode.Disposable {
this.clientOptions,
forceDebug,
);
this.disposables.push(this.client.start());
vscode.services.registerLanguageClient(this.client);
await this.client.onReady();
// Must wait for the client to be ready before registering notification
// handlers.
Expand Down Expand Up @@ -364,7 +383,8 @@ function registerNotificationHandlers(client: vscode.LanguageClient) {
})
client.onNotification(SuggestStrictMode, async (params: SuggestStrictModeParams) => {
const config = vscode.workspace.getConfiguration();
if (config.get('angular.enable-strict-mode-prompt') === false) {
if (config.get('angular.enable-strict-mode-prompt') === false ||
config.get('angular.forceStrictTemplates')) {
return;
}
const openTsConfig = 'Open tsconfig.json';
Expand Down Expand Up @@ -416,7 +436,7 @@ function getProbeLocations(bundled: string): string[] {
* Construct the arguments that's used to spawn the server process.
* @param ctx vscode extension context
*/
function constructArgs(ctx: vscode.ExtensionContext, viewEngine: boolean): string[] {
function constructArgs(ctx: vscode.ExtensionContext): string[] {
const config = vscode.workspace.getConfiguration();
const args: string[] = ['--logToConsole'];

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

const ngProbeLocations = getProbeLocations(ctx.extensionPath);
if (viewEngine) {
args.push('--viewEngine');
args.push('--ngProbeLocations', [
path.join(ctx.extensionPath, 'v12_language_service'),
...ngProbeLocations,
].join(','));
} else {
args.push('--ngProbeLocations', ngProbeLocations.join(','));
}
args.push('--ngProbeLocations', ngProbeLocations.join(','));

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

const angularVersions = getAngularVersionsInWorkspace();
// Only disable block syntax if we find angular/core and every one we find does not support block
// syntax
if (angularVersions.size > 0 && Array.from(angularVersions).every(v => v.version.major < 17)) {
args.push('--disableBlockSyntax');
}

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

const tsdk: string|null = config.get('typescript.tsdk', null);
const tsProbeLocations = [tsdk, ...getProbeLocations(ctx.extensionPath)];
args.push('--tsProbeLocations', tsProbeLocations.join(','));
Expand All @@ -466,35 +490,19 @@ function getServerOptions(ctx: vscode.ExtensionContext, debug: boolean): vscode.
NG_DEBUG: true,
};

// Because the configuration is typed as "boolean" in package.json, vscode
// will return false even when the value is not set. If value is false, then
// we need to check if all projects support Ivy language service.
const config = vscode.workspace.getConfiguration();
let viewEngine: boolean = config.get('angular.view-engine') || !allProjectsSupportIvy();
if (viewEngine && !allProjectsSupportVE()) {
viewEngine = false;
if (config.get('angular.view-engine')) {
vscode.window.showErrorMessage(
`The legacy View Engine option is enabled but the workspace contains a version 13 Angular project.` +
` Legacy View Engine will be disabled since support for it was dropped in v13.`,
);
} else if (!allProjectsSupportIvy() && !allProjectsSupportVE()) {
vscode.window.showErrorMessage(
`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).` +
`The extension will not work for the legacy project in this workspace.`);
}
}

// Node module for the language server
const args = constructArgs(ctx, viewEngine);
const args = constructArgs(ctx);
const prodBundle = ctx.asAbsolutePath(path.join('node_modules', '@angular', 'language-server'));
const devBundle = ctx.asAbsolutePath(path.join('node_modules', '@angular', 'language-server'));
// VS Code Insider launches extensions in debug mode by default but users
// install prod bundle so we have to check whether dev bundle exists.
const latestServerModule = debug && fs.existsSync(devBundle) ? devBundle : prodBundle;
const v12ServerModule = ctx.asAbsolutePath(
path.join('node_modules', 'v12_language_service', 'node_modules', '@angular', 'language-server'));
const module = viewEngine ? v12ServerModule : latestServerModule;

if (!extensionVersionCompatibleWithAllProjects(latestServerModule)) {
vscode.window.showWarningMessage(
`A project in the workspace is using a newer version of Angular than the language service extension. ` +
`This may cause the extension to show incorrect diagnostics.`);
}

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

function allProjectsSupportIvy() {
function extensionVersionCompatibleWithAllProjects(serverModuleLocation: string): boolean {
const languageServiceVersion =
resolve('@angular/language-service', serverModuleLocation)?.version;
if (languageServiceVersion === undefined) {
return true;
}

const workspaceFolders = vscode.workspace.workspaceFolders || [];
for (const workspaceFolder of workspaceFolders) {
const angularCore = resolve('@angular/core', vscode.Uri.parse(workspaceFolder.uri).fsPath);
if (angularCore?.version.greaterThanOrEqual(new Version('9')) === false) {
if (angularCore === undefined) {
continue;
}
if (!languageServiceVersion.greaterThanOrEqual(angularCore.version, 'minor')) {
return false;
}
}
return true;
}

function allProjectsSupportVE() {
/**
* Returns true if any project in the workspace supports block syntax (v17+).
*/
function getAngularVersionsInWorkspace(): Set<NodeModule> {
const angularCoreModules = new Set<NodeModule>();
const workspaceFolders = vscode.workspace.workspaceFolders || [];
for (const workspaceFolder of workspaceFolders) {
const angularCore = resolve('@angular/core', vscode.Uri.parse(workspaceFolder.uri).fsPath);
if (angularCore?.version.greaterThanOrEqual(new Version('13')) === true) {
return false;
if (angularCore === undefined) {
continue;
}
angularCoreModules.add(angularCore);
}
return true;
return angularCoreModules;
}
Loading

0 comments on commit 24b352d

Please sign in to comment.