Skip to content

Commit

Permalink
Adapts procedure registry to depend on the cypher version (#331)
Browse files Browse the repository at this point in the history
  • Loading branch information
ncordon authored Feb 4, 2025
1 parent 89dc5bb commit 5acfe46
Show file tree
Hide file tree
Showing 48 changed files with 160,199 additions and 69,844 deletions.
41 changes: 30 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"devDependencies": {
"@changesets/cli": "^2.27.8",
"@typescript-eslint/eslint-plugin": "^7.8.0",
"cross-env": "^7.0.3",
"eslint": "^8.32.0",
"eslint-config-prettier": "^8.6.0",
"husky": "^8.0.0",
Expand Down
21 changes: 20 additions & 1 deletion packages/language-server/src/lintWorker.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
import { lintCypherQuery } from '@neo4j-cypher/language-support';
import {
DbSchema,
lintCypherQuery as _lintCypherQuery,
_internalFeatureFlags,
} from '@neo4j-cypher/language-support';
import workerpool from 'workerpool';

function lintCypherQuery(
query: string,
dbSchema: DbSchema,
featureFlags: { consoleCommands?: boolean; cypher25?: boolean } = {},
) {
// We allow to override the consoleCommands feature flag
if (featureFlags.consoleCommands !== undefined) {
_internalFeatureFlags.consoleCommands = featureFlags.consoleCommands;
}
if (featureFlags.cypher25 !== undefined) {
_internalFeatureFlags.cypher25 = featureFlags.cypher25;
}
return _lintCypherQuery(query, dbSchema);
}

workerpool.worker({ lintCypherQuery });

type LinterArgs = Parameters<typeof lintCypherQuery>;
Expand Down
7 changes: 6 additions & 1 deletion packages/language-server/src/linting.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { _internalFeatureFlags } from '@neo4j-cypher/language-support';
import { Neo4jSchemaPoller } from '@neo4j-cypher/schema-poller';
import debounce from 'lodash.debounce';
import { join } from 'path';
Expand Down Expand Up @@ -31,7 +32,11 @@ async function rawLintDocument(
}

const proxyWorker = (await pool.proxy()) as unknown as LintWorker;
lastSemanticJob = proxyWorker.lintCypherQuery(query, dbSchema);
lastSemanticJob = proxyWorker.lintCypherQuery(
query,
dbSchema,
_internalFeatureFlags,
);
const result = await lastSemanticJob;

sendDiagnostics(result);
Expand Down
9 changes: 8 additions & 1 deletion packages/language-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,21 @@ import {

import { TextDocument } from 'vscode-languageserver-textdocument';

import { syntaxColouringLegend } from '@neo4j-cypher/language-support';
import {
syntaxColouringLegend,
_internalFeatureFlags,
} from '@neo4j-cypher/language-support';
import { Neo4jSchemaPoller } from '@neo4j-cypher/schema-poller';
import { doAutoCompletion } from './autocompletion';
import { cleanupWorkers, lintDocument } from './linting';
import { doSignatureHelp } from './signatureHelp';
import { applySyntaxColouringForDocument } from './syntaxColouring';
import { Neo4jSettings } from './types';

if (process.env.CYPHER_25 === 'true') {
_internalFeatureFlags.cypher25 = true;
}

const connection = createConnection(ProposedFeatures.all);

// Create a simple text document manager.
Expand Down
2 changes: 1 addition & 1 deletion packages/language-support/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"vscode-languageserver-types": "^3.17.3"
},
"scripts": {
"gen-parser": "antlr4 -Dlanguage=TypeScript -visitor src/antlr-grammar/CypherCmdLexer.g4 src/antlr-grammar/CypherCmdParser.g4 -o src/generated-parser/ -Xexact-output-dir",
"gen-parser": "cross-env ANTLR4_TOOLS_ANTLR_VERSION=4.13.2 antlr4 -Dlanguage=TypeScript -visitor src/antlr-grammar/CypherCmdLexer.g4 src/antlr-grammar/CypherCmdParser.g4 -o src/generated-parser/ -Xexact-output-dir",
"build": "npm run gen-parser && concurrently 'npm:build-types' 'npm:build-esm' 'npm:build-commonjs'",
"build-types": "tsc --emitDeclarationOnly --outDir dist/types",
"build-esm": "esbuild ./src/index.ts --bundle --format=esm --sourcemap --outfile=dist/esm/index.mjs",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import CypherParser, {
import {
findParent,
findPreviousNonSpace,
resolveCypherVersion,
rulesDefiningVariables,
} from '../helpers';
import {
Expand All @@ -27,7 +28,12 @@ import {
import { getMethodName, ParsedStatement } from '../parserWrapper';

import { _internalFeatureFlags } from '../featureFlags';
import { CompletionItem, Neo4jFunction, Neo4jProcedure } from '../types';
import {
CompletionItem,
CypherVersion,
Neo4jFunction,
Neo4jProcedure,
} from '../types';

const uniq = <T>(arr: T[]) => Array.from(new Set(arr));

Expand Down Expand Up @@ -94,9 +100,12 @@ const reltypeCompletions = (dbSchema: DbSchema) =>
const procedureReturnCompletions = (
procedureName: string,
dbSchema: DbSchema,
cypherVersion: CypherVersion,
): CompletionItem[] => {
return (
dbSchema?.procedures?.[procedureName]?.returnDescription?.map((x) => {
dbSchema.procedures?.[cypherVersion]?.[
procedureName
]?.returnDescription?.map((x) => {
return { label: x.name, kind: CompletionItemKind.Variable };
}) ?? []
);
Expand All @@ -106,23 +115,25 @@ const functionNameCompletions = (
candidateRule: CandidateRule,
tokens: Token[],
dbSchema: DbSchema,
cypherVersion: CypherVersion,
): CompletionItem[] =>
namespacedCompletion(
candidateRule,
tokens,
dbSchema?.functions ?? {},
dbSchema.functions?.[cypherVersion] ?? {},
'function',
);

const procedureNameCompletions = (
candidateRule: CandidateRule,
tokens: Token[],
dbSchema: DbSchema,
cypherVersion: CypherVersion,
): CompletionItem[] =>
namespacedCompletion(
candidateRule,
tokens,
dbSchema?.procedures ?? {},
dbSchema.procedures?.[cypherVersion] ?? {},
'procedure',
);

Expand Down Expand Up @@ -430,6 +441,10 @@ export function completionCoreCompletion(
caretToken: Token,
manualTrigger = false,
): CompletionItem[] {
const cypherVersion = resolveCypherVersion(
parsingResult.cypherVersion,
dbSchema,
);
const parser = parsingResult.parser;
const tokens = parsingResult.tokens;

Expand Down Expand Up @@ -536,18 +551,30 @@ export function completionCoreCompletion(
callContext.procedureResultItem_list().map((a) => a.getText()),
);
const name = getMethodName(procedureNameCtx);
return procedureReturnCompletions(name, dbSchema).filter(
(a) => !existingYieldItems.has(a?.label),
);
return procedureReturnCompletions(
name,
dbSchema,
cypherVersion,
).filter((a) => !existingYieldItems.has(a?.label));
}
}

if (ruleNumber === CypherParser.RULE_functionName) {
return functionNameCompletions(candidateRule, tokens, dbSchema);
return functionNameCompletions(
candidateRule,
tokens,
dbSchema,
cypherVersion,
);
}

if (ruleNumber === CypherParser.RULE_procedureName) {
return procedureNameCompletions(candidateRule, tokens, dbSchema);
return procedureNameCompletions(
candidateRule,
tokens,
dbSchema,
cypherVersion,
);
}

if (ruleNumber === CypherParser.RULE_parameter) {
Expand Down Expand Up @@ -769,7 +796,7 @@ function completeAliasName({
) {
return [
...parameterSuggestions,
...(dbSchema?.aliasNames ?? []).map((aliasName) => ({
...(dbSchema.aliasNames ?? []).map((aliasName) => ({
label: aliasName,
kind: CompletionItemKind.Value,
insertText: backtickDbNameIfNeeded(aliasName),
Expand Down Expand Up @@ -834,7 +861,7 @@ function completeSymbolicName({
if (rulesThatAcceptExistingUsers.some((rule) => ruleList.includes(rule))) {
const result = [
...parameterSuggestions,
...(dbSchema?.userNames ?? []).map((userName) => ({
...(dbSchema.userNames ?? []).map((userName) => ({
label: userName,
kind: CompletionItemKind.Value,
})),
Expand All @@ -852,7 +879,7 @@ function completeSymbolicName({
if (rulesThatAcceptExistingRoles.some((rule) => ruleList.includes(rule))) {
return [
...parameterSuggestions,
...(dbSchema?.roleNames ?? []).map((roleName) => ({
...(dbSchema.roleNames ?? []).map((roleName) => ({
label: roleName,
kind: CompletionItemKind.Value,
})),
Expand Down
7 changes: 5 additions & 2 deletions packages/language-support/src/dbSchema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { CypherVersion, Neo4jFunction, Neo4jProcedure } from './types';

export type Registry<T> = Record<string, T>;
type ScopedRegistry<T> = Partial<Record<CypherVersion, Registry<T>>>;

export interface DbSchema {
labels?: string[];
relationshipTypes?: string[];
Expand All @@ -9,7 +12,7 @@ export interface DbSchema {
roleNames?: string[];
parameters?: Record<string, unknown>;
propertyKeys?: string[];
procedures?: Record<string, Neo4jProcedure>;
functions?: Record<string, Neo4jFunction>;
procedures?: ScopedRegistry<Neo4jProcedure>;
functions?: ScopedRegistry<Neo4jFunction>;
defaultLanguage?: CypherVersion;
}
2 changes: 2 additions & 0 deletions packages/language-support/src/featureFlags.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
interface FeatureFlags {
consoleCommands: boolean;
cypher25: boolean;
}

export const _internalFeatureFlags: FeatureFlags = {
Expand All @@ -11,4 +12,5 @@ export const _internalFeatureFlags: FeatureFlags = {
it's own cache and preference on if console commands are enabled or not.
*/
consoleCommands: false,
cypher25: false,
};
2 changes: 1 addition & 1 deletion packages/language-support/src/formatting/formatting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ export function formatQuery(
): string | FormattingResultWithCursor {
const { tree, tokens } = getParseTreeAndTokens(query);
const visitor = new TreePrintVisitor(tokens);

tokens.fill();

if (cursorPosition === undefined) return visitor.format(tree);
Expand Down
Loading

0 comments on commit 5acfe46

Please sign in to comment.