Skip to content

Commit

Permalink
feat: go to executor definition (nrwl#1962)
Browse files Browse the repository at this point in the history
  • Loading branch information
MJez29 authored and MaxKless committed Feb 28, 2024
1 parent b52ab82 commit 02c1923
Show file tree
Hide file tree
Showing 27 changed files with 326 additions and 69 deletions.
14 changes: 14 additions & 0 deletions apps/nxls/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
getCompletionItems,
projectSchemaIsRegistered,
} from '@nx-console/language-server/capabilities/code-completion';
import { getDefinition } from '@nx-console/language-server/capabilities/definition';
import { getDocumentLinks } from '@nx-console/language-server/capabilities/document-links';
import { getHover } from '@nx-console/language-server/capabilities/hover';
import {
Expand Down Expand Up @@ -154,6 +155,7 @@ connection.onInitialize(async (params) => {
triggerCharacters: ['"', ':'],
},
hoverProvider: true,
definitionProvider: true,
documentLinkProvider: {
resolveProvider: false,
workDoneProgress: false,
Expand Down Expand Up @@ -261,6 +263,18 @@ connection.onHover(async (hoverParams) => {
return await getHover(hoverParams, jsonAst, document);
});

connection.onDefinition((definitionParams) => {
const definitionDocument = documents.get(definitionParams.textDocument.uri);

if (!definitionDocument || !WORKING_PATH) {
return null;
}

const { jsonAst, document } = getJsonDocument(definitionDocument);

return getDefinition(WORKING_PATH, definitionParams, jsonAst, document);
});

connection.onDocumentLinks(async (params) => {
const linkDocument = documents.get(params.textDocument.uri);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ async function getProjectSchema(
}
const schemaRef =
platform() === 'win32'
? matchingCollection?.path
: `file://${matchingCollection?.path}`;
? matchingCollection.schemaPath
: `file://${matchingCollection.schemaPath}`;

targetsProperties[key] = {
properties: {
Expand Down
18 changes: 18 additions & 0 deletions libs/language-server/capabilities/definition/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"extends": ["../../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
11 changes: 11 additions & 0 deletions libs/language-server/capabilities/definition/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# language-server-capabilities-hover

This library was generated with [Nx](https://nx.dev).

## Building

Run `nx build language-server-capabilities-definition` to build the library.

## Running unit tests

Run `nx test language-server-capabilities-definition` to execute the unit tests via [Jest](https://jestjs.io).
11 changes: 11 additions & 0 deletions libs/language-server/capabilities/definition/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable */
export default {
displayName: 'language-server-capabilities-definition',
preset: '../../../../jest.preset.js',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory:
'../../../../coverage/libs/language-server/capabilities/definition',
};
5 changes: 5 additions & 0 deletions libs/language-server/capabilities/definition/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "@nx-console/language-server/capabilities/definition",
"version": "0.0.1",
"type": "commonjs"
}
25 changes: 25 additions & 0 deletions libs/language-server/capabilities/definition/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "language-server-capabilities-definition",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/language-server/capabilities/definition/src",
"projectType": "library",
"targets": {
"lint": {
"executor": "@nx/eslint:lint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
"libs/language-server/capabilities/definition/**/*.ts"
]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/language-server/capabilities/definition/jest.config.ts"
}
}
},
"tags": ["type:lsp"]
}
1 change: 1 addition & 0 deletions libs/language-server/capabilities/definition/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './lib/get-definition';
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { isExecutorStringNode } from '@nx-console/language-server/utils';
import { getExecutors } from '@nx-console/language-server/workspace';
import { resolveImplementation } from 'nx/src/config/schema-utils';
import { dirname } from 'path';
import { JSONDocument } from 'vscode-json-languageservice';
import { DefinitionParams, LocationLink } from 'vscode-languageserver';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { URI } from 'vscode-uri';

export async function getDefinition(
workingPath: string,
definitionParams: DefinitionParams,
jsonAst: JSONDocument,
document: TextDocument
): Promise<LocationLink[] | undefined> {
const offset = document.offsetAt(definitionParams.position);
const node = jsonAst.getNodeFromOffset(offset);

if (!node || !isExecutorStringNode(node)) {
return undefined;
}

const executors = await getExecutors(workingPath);

const executor = executors.find((e) => e.name === node.value);

if (!executor) {
return undefined;
}

const executorFile = resolveImplementation(
executor.implementationPath,
dirname(executor.configPath)
);

return [
LocationLink.create(
URI.file(executorFile).toString(),
// Link to start of file because we cannot find the exact location without parsing the file
{
start: {
line: 0,
character: 0,
},
end: {
line: 0,
character: 0,
},
},
{
start: {
line: 0,
character: 0,
},
end: {
line: 0,
character: 0,
},
},
{
// So that the underline is
// "executor": "nx:build"
// ^^^^^^^^
start: document.positionAt(node.offset + 1),
end: document.positionAt(node.offset + node.length - 1),
}
),
];
}
22 changes: 22 additions & 0 deletions libs/language-server/capabilities/definition/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
10 changes: 10 additions & 0 deletions libs/language-server/capabilities/definition/tsconfig.lib.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"declaration": true,
"types": ["node"]
},
"include": ["src/**/*.ts"],
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
}
14 changes: 14 additions & 0 deletions libs/language-server/capabilities/definition/tsconfig.spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}
11 changes: 4 additions & 7 deletions libs/language-server/capabilities/hover/src/lib/get-hover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { TextDocument } from 'vscode-languageserver-textdocument';
import { Hover, HoverParams } from 'vscode-languageserver';
import {
getJsonLanguageService,
isPropertyNode,
isStringNode,
isExecutorStringNode,
} from '@nx-console/language-server/utils';

export async function getHover(
Expand All @@ -33,7 +32,7 @@ export async function getHover(
return hover;
}

if (isExecutorStringNode(node)) {
if (isNxExecutorStringNode(node)) {
hover.contents = {
kind: 'markdown',
value: `[View executor documentation on nx.dev](${constructExecutorUrl(
Expand All @@ -45,11 +44,9 @@ export async function getHover(
return hover;
}

function isExecutorStringNode(node: ASTNode): node is StringASTNode {
function isNxExecutorStringNode(node: ASTNode): node is StringASTNode {
return (
isStringNode(node) &&
isPropertyNode(node.parent) &&
node.parent.keyNode.value === 'executor' &&
isExecutorStringNode(node) &&
(RegExp(/@nx|@nrwl\/\w+:\w+/).test(node.value) ||
RegExp(/nx:\w+/).test(node.value))
);
Expand Down
4 changes: 2 additions & 2 deletions libs/language-server/types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
GeneratorSchema,
} from '@nx-console/shared/generate-ui-types';
import {
CollectionInfo,
GeneratorCollectionInfo,
Option,
TaskExecutionSchema,
} from '@nx-console/shared/schema';
Expand Down Expand Up @@ -41,7 +41,7 @@ export const NxGeneratorsRequest: RequestType<
{
options?: NxGeneratorsRequestOptions;
},
CollectionInfo[],
GeneratorCollectionInfo[],
unknown
> = new RequestType('nx/generators');

Expand Down
1 change: 1 addition & 0 deletions libs/language-server/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './lib/default-completion';
export * from './lib/lsp-log';
export * from './lib/json-language-service';
export * from './lib/find-property';
export * from './lib/executor';
10 changes: 10 additions & 0 deletions libs/language-server/utils/src/lib/executor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ASTNode, StringASTNode } from 'vscode-json-languageservice';
import { isPropertyNode, isStringNode } from './node-types';

export function isExecutorStringNode(node: ASTNode): node is StringASTNode {
return (
isStringNode(node) &&
isPropertyNode(node.parent) &&
node.parent.keyNode.value === 'executor'
);
}
9 changes: 6 additions & 3 deletions libs/language-server/workspace/src/lib/get-executors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CollectionInfo } from '@nx-console/shared/schema';
import { ExecutorCollectionInfo } from '@nx-console/shared/schema';
import { readCollections } from './read-collections';

export type GetExecutorsOptions = {
Expand All @@ -12,11 +12,14 @@ export async function getExecutors(
includeHidden: false,
clearPackageJsonCache: false,
}
): Promise<CollectionInfo[]> {
): Promise<ExecutorCollectionInfo[]> {
return (
await readCollections(workspacePath, {
clearPackageJsonCache: options.clearPackageJsonCache,
includeHidden: options.includeHidden,
})
).filter((collection) => collection.type === 'executor');
).filter(
(collection): collection is ExecutorCollectionInfo =>
collection.type === 'executor'
);
}
13 changes: 9 additions & 4 deletions libs/language-server/workspace/src/lib/get-generators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import {
listFiles,
readAndCacheJsonFile,
} from '@nx-console/shared/file-system';
import { CollectionInfo, GeneratorType } from '@nx-console/shared/schema';
import {
CollectionInfo,
GeneratorCollectionInfo,
GeneratorType,
} from '@nx-console/shared/schema';
import { normalizeSchema } from '@nx-console/shared/schema/normalize';
import { basename, join } from 'path';
import { getCollectionInfo, readCollections } from './read-collections';
Expand All @@ -16,7 +20,7 @@ export async function getGenerators(
includeHidden: false,
includeNgAdd: false,
}
): Promise<CollectionInfo[]> {
): Promise<GeneratorCollectionInfo[]> {
const basedir = workspacePath;
const collections = await readCollections(workspacePath, {
clearPackageJsonCache: false,
Expand All @@ -33,7 +37,8 @@ export async function getGenerators(
...(await checkAndReadWorkspaceGenerators(basedir, 'generators', options)),
];
return generatorCollections.filter(
(collection): collection is CollectionInfo => !!collection.data
(collection): collection is GeneratorCollectionInfo =>
collection.type === 'generator'
);
}

Expand Down Expand Up @@ -94,7 +99,7 @@ async function readWorkspaceGeneratorsCollection(
return {
name: collectionName,
type: 'generator',
path: schemaJsonPath,
schemaPath: schemaJsonPath,
data: {
name,
collection: collectionName,
Expand Down
Loading

0 comments on commit 02c1923

Please sign in to comment.