Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adapts procedure registry to depend on the cypher version #331

Merged
merged 37 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
34f2bb8
Altered semantic analysis to use default/query-specified version if a…
anderson4j Jan 13, 2025
1f76037
Merge branch 'main' of https://github.com/neo4j/cypher-language-suppo…
anderson4j Jan 13, 2025
d1d0a48
added tests
anderson4j Jan 15, 2025
d79e282
small refactor, fix defaultLanguage to be more like data according to…
anderson4j Jan 16, 2025
b9fc356
added changeset
anderson4j Jan 16, 2025
d653261
Adds some minor nits
ncordon Jan 21, 2025
33fd518
Adds versioned procedure registry
ncordon Jan 21, 2025
e58d2e5
Updates the semantic analysis to use different procedure registries d…
ncordon Jan 22, 2025
44c0b32
Adds tests for procedures / functions dependant on cypher versions
ncordon Jan 22, 2025
6e7e5c6
Adds tests for completions and signature help being cypher version de…
ncordon Jan 22, 2025
ff25f9e
Makes project compile
ncordon Jan 22, 2025
fbdf5bb
Fixes e2e tests
ncordon Jan 23, 2025
7cacbcb
Merge branch 'main' into polling-versioned-procs-fns
ncordon Jan 23, 2025
9d29a4b
Adds integration test to check the poller and VSCode extension can us…
ncordon Jan 23, 2025
ee324f9
Merge remote-tracking branch 'origin/polling-versioned-procs-fns' int…
ncordon Jan 23, 2025
6c98b22
Fixes e2e test
ncordon Jan 23, 2025
07f20b7
Self review
ncordon Jan 23, 2025
f3b41a1
Adds feature flag to the codemirror wiring
ncordon Jan 24, 2025
662b643
Fixes feature flag
ncordon Jan 26, 2025
cbda4a1
Adds more tests for codemirror
ncordon Jan 26, 2025
ef228ad
Merge remote-tracking branch 'origin/main' into polling-versioned-pro…
ncordon Jan 28, 2025
f18041e
Updates semantic analysis and removes exports
ncordon Jan 28, 2025
d2704ec
Addresses pr review comments
ncordon Jan 29, 2025
f2313a1
Merge branch 'main' into polling-versioned-procs-fns
ncordon Jan 29, 2025
d6d9547
Sets the env variable with cross-env
ncordon Jan 29, 2025
74f5ecb
Updates only needed resolver
ncordon Jan 29, 2025
cacf7a4
Widens timeout
ncordon Jan 30, 2025
fb785f4
Widens timeout even more
ncordon Jan 30, 2025
bdaa8f7
Removes helpers test
ncordon Jan 30, 2025
811d70a
Merge remote-tracking branch 'origin/main' into polling-versioned-pro…
ncordon Jan 30, 2025
3a10389
Forces update
ncordon Jan 30, 2025
f6ba2ef
Increases another timeout
ncordon Jan 31, 2025
b3a8b8b
Gates the test to have the test.only
ncordon Jan 31, 2025
8e00f98
Tries widening all timeouts
ncordon Jan 31, 2025
ee2cd18
Cleans up
ncordon Jan 31, 2025
aa7c1a3
Removes non needed force
ncordon Feb 4, 2025
04430fa
Merge remote-tracking branch 'origin/main' into polling-versioned-pro…
ncordon Feb 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 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
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_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",
"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 @@ -103,7 +103,7 @@ const procedureReturnCompletions = (
cypherVersion: CypherVersion,
): CompletionItem[] => {
return (
dbSchema?.procedures?.[cypherVersion]?.[
dbSchema.procedures?.[cypherVersion]?.[
procedureName
]?.returnDescription?.map((x) => {
return { label: x.name, kind: CompletionItemKind.Variable };
Expand All @@ -120,7 +120,7 @@ const functionNameCompletions = (
namespacedCompletion(
candidateRule,
tokens,
dbSchema?.functions?.[cypherVersion] ?? {},
dbSchema.functions?.[cypherVersion] ?? {},
'function',
);

Expand All @@ -133,7 +133,7 @@ const procedureNameCompletions = (
namespacedCompletion(
candidateRule,
tokens,
dbSchema?.procedures?.[cypherVersion] ?? {},
dbSchema.procedures?.[cypherVersion] ?? {},
'procedure',
);

Expand Down Expand Up @@ -796,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 @@ -861,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 @@ -879,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?: Partial<Record<CypherVersion, Record<string, Neo4jProcedure>>>;
functions?: Partial<Record<CypherVersion, Record<string, Neo4jFunction>>>;
procedures?: ScopedRegistry<Neo4jProcedure>;
functions?: ScopedRegistry<Neo4jFunction>;
defaultLanguage?: CypherVersion;
}
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
3 changes: 1 addition & 2 deletions packages/language-support/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,7 @@ export function resolveCypherVersion(
) {
if (_internalFeatureFlags.cypher25) {
const cypherVersion: CypherVersion =
anderson4j marked this conversation as resolved.
Show resolved Hide resolved
parsedVersion ??
(dbSchema.defaultLanguage ? dbSchema.defaultLanguage : 'CYPHER 5');
parsedVersion ?? dbSchema.defaultLanguage ?? 'CYPHER 5';

return cypherVersion;
} else {
Expand Down
2 changes: 1 addition & 1 deletion packages/language-support/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export { autocomplete } from './autocompletion/autocompletion';
export { shouldAutoCompleteYield } from './autocompletion/autocompletionHelpers';
export type { DbSchema } from './dbSchema';
export { _internalFeatureFlags } from './featureFlags';
export { formatQuery } from './formatting/formatting';
export { antlrUtils } from './helpers';
export { CypherTokenType, lexerSymbols } from './lexerSymbols';
export { parse, parserWrapper, parseStatementsStrs } from './parserWrapper';
Expand All @@ -27,4 +28,3 @@ export type {
export { CypherLexer, CypherParser };
import CypherLexer from './generated-parser/CypherCmdLexer';
import CypherParser from './generated-parser/CypherCmdParser';
export { formatQuery } from './formatting/formatting'
4 changes: 2 additions & 2 deletions packages/language-support/src/signatureHelp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,12 @@ export function signatureHelp(
);
if (method.methodType === MethodType.function) {
result = toSignatureHelp(
dbSchema?.functions?.[cypherVersion] ?? {},
dbSchema.functions?.[cypherVersion] ?? {},
method,
);
} else {
result = toSignatureHelp(
dbSchema?.procedures?.[cypherVersion] ?? {},
dbSchema.procedures?.[cypherVersion] ?? {},
method,
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */

import { DiagnosticSeverity, Position } from 'vscode-languageserver-types';
import { DbSchema } from '../dbSchema';
import { CypherVersion, cypherVersions } from '../types';
import { DbSchema, Registry } from '../dbSchema';
import { CypherVersion, Neo4jFunction, Neo4jProcedure } from '../types';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { analyzeQuery, updateSignatureResolver } from './semanticAnalysis';
Expand All @@ -29,7 +29,12 @@ export interface SemanticAnalysisElement {
};
}

let previousSchema: DbSchema | undefined = undefined;
const previousResolvers: {
[cypherVersion: CypherVersion]: {
functions: Registry<Neo4jFunction>;
procedures: Registry<Neo4jProcedure>;
};
} = {};

function copySettingSeverity(
elements: SemanticAnalysisElement[],
Expand All @@ -49,44 +54,41 @@ function copySettingSeverity(
}));
}

function updateResolverForVersion(
dbSchema: DbSchema,
cypherVersion: CypherVersion,
) {
const previousResolver = previousResolvers?.[cypherVersion];
const currentResolver = {
procedures: dbSchema?.procedures?.[cypherVersion],
functions: dbSchema?.functions?.[cypherVersion],
};
if (JSON.stringify(previousResolver) !== JSON.stringify(currentResolver)) {
previousResolvers[cypherVersion] = currentResolver;
const procedures = Object.values(
dbSchema?.procedures?.[cypherVersion] ?? {},
);
const functions = Object.values(dbSchema?.functions?.[cypherVersion] ?? {});
updateSignatureResolver(
{
procedures: procedures,
functions: functions,
},
cypherVersion,
);
}
}

export function wrappedSemanticAnalysis(
query: string,
dbSchema: DbSchema,
parsedVersion?: CypherVersion,
): SemanticAnalysisResult {
try {
if (JSON.stringify(dbSchema) !== JSON.stringify(previousSchema)) {
previousSchema = dbSchema;

cypherVersions.forEach((cypherVersion) => {
const procedures = Object.values(
dbSchema?.procedures?.[cypherVersion] ?? {},
);
const functions = Object.values(
dbSchema?.functions?.[cypherVersion] ?? {},
);
updateSignatureResolver(
{
procedures: procedures,
functions: functions,
},
cypherVersion,
);
});
}

let cypherVersion = 'CYPHER 5';
const defaultVersion = dbSchema.defaultLanguage?.toUpperCase();

if (parsedVersion) {
cypherVersion = parsedVersion;
} else if (dbSchema.defaultLanguage) {
cypherVersion = defaultVersion;
}
const semanticErrorsResult = analyzeQuery(
query,
cypherVersion.toLowerCase(),
);
const defaultVersion = dbSchema?.defaultLanguage;
const cypherVersion = parsedVersion ?? defaultVersion ?? 'CYPHER 5';
updateResolverForVersion(dbSchema, cypherVersion);
const semanticErrorsResult = analyzeQuery(query, cypherVersion);
const errors: SemanticAnalysisElement[] = semanticErrorsResult.errors;
const notifications: SemanticAnalysisElement[] =
semanticErrorsResult.notifications;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import { testData } from '../testData';
import { testCompletions } from './completionAssertionHelpers';

describe('function invocations', () => {
let isCypher25: boolean;

beforeAll(() => {
isCypher25 = _internalFeatureFlags.cypher25;
_internalFeatureFlags.cypher25 = true;
});

afterAll(() => {
_internalFeatureFlags.cypher25 = false;
_internalFeatureFlags.cypher25 = isCypher25;
});

const dbSchema: DbSchema = testData.mockSchema;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import { testData } from '../testData';
import { testCompletions } from './completionAssertionHelpers';

describe('Procedures auto-completion', () => {
let isCypher25: boolean;

beforeAll(() => {
isCypher25 = _internalFeatureFlags.cypher25;
_internalFeatureFlags.cypher25 = true;
});

afterAll(() => {
_internalFeatureFlags.cypher25 = false;
_internalFeatureFlags.cypher25 = isCypher25;
});

const procedures = testData.mockSchema.procedures;
Expand Down
27 changes: 22 additions & 5 deletions packages/language-support/src/tests/consoleCommands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,15 @@ function expectErrorMessage(query: string, msg: string) {
}

describe('sanity checks', () => {
let consoleCommands: boolean;

beforeAll(() => {
consoleCommands = _internalFeatureFlags.consoleCommands;
_internalFeatureFlags.consoleCommands = true;
});

afterAll(() => {
_internalFeatureFlags.consoleCommands = false;
_internalFeatureFlags.consoleCommands = consoleCommands;
});

test('parses simple commands without args ', () => {
Expand Down Expand Up @@ -219,11 +222,14 @@ describe('sanity checks', () => {
});

describe(':use', () => {
let consoleCommands: boolean;

beforeAll(() => {
consoleCommands = _internalFeatureFlags.consoleCommands;
_internalFeatureFlags.consoleCommands = true;
});
afterAll(() => {
_internalFeatureFlags.consoleCommands = false;
_internalFeatureFlags.consoleCommands = consoleCommands;
});
test('parses without arg', () => {
expectParsedCommands(':use', [{ type: 'use' }]);
Expand Down Expand Up @@ -285,11 +291,14 @@ describe(':use', () => {
});

describe('parameters', () => {
let consoleCommands: boolean;

beforeAll(() => {
consoleCommands = _internalFeatureFlags.consoleCommands;
_internalFeatureFlags.consoleCommands = true;
});
afterAll(() => {
_internalFeatureFlags.consoleCommands = false;
_internalFeatureFlags.consoleCommands = consoleCommands;
});
test('basic param usage', () => {
expectParsedCommands(':param', [{ type: 'list-parameters' }]);
Expand Down Expand Up @@ -550,12 +559,15 @@ describe('parameters', () => {
});

describe('server', () => {
let consoleCommands: boolean;

beforeAll(() => {
consoleCommands = _internalFeatureFlags.consoleCommands;
_internalFeatureFlags.consoleCommands = true;
});

afterAll(() => {
_internalFeatureFlags.consoleCommands = false;
_internalFeatureFlags.consoleCommands = consoleCommands;
});

test('basic server usage', () => {
Expand Down Expand Up @@ -655,12 +667,17 @@ describe('server', () => {
});

describe('command parser also handles cypher', () => {
let consoleCommands: boolean;

beforeAll(() => {
consoleCommands = _internalFeatureFlags.consoleCommands;
_internalFeatureFlags.consoleCommands = true;
});

afterAll(() => {
_internalFeatureFlags.consoleCommands = false;
_internalFeatureFlags.consoleCommands = consoleCommands;
});

test('parses cypher', () => {
expectParsedCommands('MATCH (n) RETURN n', [
{ statement: 'MATCH (n) RETURN n', type: 'cypher' },
Expand Down
Loading