Skip to content

Commit 3b2faf5

Browse files
anderson4jOskarDamkjaerclaude
authored
Symbol fetches in React Codemirror (#661)
* make lint call need parserWrapper * move applySyntaxColouring to parserwrapper * move sighelp to parserWrapper * move autocompletion to parserWrapper * add symbolfetcher to cyphereditor * update version * update versions in react-codemirrors dependencies. Will this work, or do I need to verdaccio publish? * add logging to symbolfetcher * move so language server and codemirrors use helper methods everywhere. Rename ParserWrapper to CypherHelper * Revert "add logging to symbolfetcher" This reverts commit c100cac. * rename parserWrapper file and imports * replace defaultCypherHelper with per-codemirror in parser-adapter * revert package.json versions * fix extra letter * cleanup * add changesets * add err to msg when symbol fetcher fails * format * Rename CypherHelper to CypherLanguageService. add default parsingresult when not using languageservice. * remove leftover "new CypherLanguageService" from tests, move up slicing of query to pre-parse in completions + add slicing to completionsAssertionHelper * rename SymbolFetcher in codemirror to CodemirrorSymbolFetcher, to separate it from the slightly different symbol fetcher used in language-server * format codemirror * make each parsing call take consoleCommandsEnabled, to prevent caching issues * remove caching * rename helpers in cypherLanguageService and rename syntaxColouring -> syntaxHighlighting * rewrite to use object parameter for optional values * remove _internalFeatureFlags usages outside of testing / debugging * format * move consoleCommandsEnabled to constructor, clean up API signatures Move consoleCommandsEnabled from per-call option to CypherLanguageService constructor since it's an environment-level concern. Apply consistent (query, dbSchema, options?) pattern with destructured defaults across all standalone functions and class methods. Fix stale schema reference in CodemirrorSymbolFetcher, remove dead consoleCommands field in signatureHelp, replace deprecated trimLeft() with trimStart(), and update stale comment in featureFlags.ts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * review fixes: worker pool cleanup, private fields, remove global mutable flag - Add terminate() to CodemirrorSymbolFetcher and call in componentWillUnmount - Make parsingResult and symbolsInfo private on CypherLanguageService - Remove _internalFeatureFlags.consoleCommands global mutable state from tests, pass consoleCommandsEnabled explicitly via function parameters instead - Remove double-slicing in autocomplete test helpers - Update changeset to document renamed/removed exports and changed signatures Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Update .changeset/seven-cats-shout.md Co-authored-by: Isak Nilsson <143806924+anderson4j@users.noreply.github.com> --------- Co-authored-by: Oskar Damkjaer <oskar.damkjaer@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Oskar Damkjær <OskarDamkjaer@users.noreply.github.com>
1 parent be27329 commit 3b2faf5

49 files changed

Lines changed: 606 additions & 521 deletions

Some content is hidden

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

.changeset/funny-tips-glow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@neo4j-cypher/react-codemirror": patch
3+
---
4+
5+
Rewrite global parserWrapper to instantiated languageService and add symbol fetching to react-codemirror

.changeset/seven-cats-shout.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@neo4j-cypher/react-codemirror-playground": patch
3+
"@neo4j-cypher/language-support": patch
4+
"@neo4j-cypher/language-server": patch
5+
"@neo4j-cypher/lint-worker": patch
6+
---
7+
8+
Rewrite global parserWrapper for instantiated CypherLanguageService objects
9+
10+
Breaking changes for direct consumers of `@neo4j-cypher/language-support`:
11+
- Removed export: `parserWrapper`. The same `ParsingResult` as `parserWrapper.parse` would yield can still be received by `createParsingResult` in `CypherLanguageService`. Instances of the new CypherLanguageService class provide helpers for linting/highlighting/completions/signature info where parsing is handled internally.
12+
- Renamed exports: `applySyntaxColouring``highlightSyntax`, `syntaxColouringLegend``syntaxHighlightingLegend`
13+
- `autocomplete()`, `lintCypherQuery()`, `highlightSyntax()`, and `getSignatureInfo()` now take an options object as the third parameter instead of positional args

packages/language-server/src/autocompletion.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ import {
66
} from 'vscode-languageserver/node';
77

88
import type { CompletionItem } from '@neo4j-cypher/language-support';
9-
import {
10-
autocomplete,
11-
shouldAutoCompleteYield,
12-
} from '@neo4j-cypher/language-support';
9+
import { shouldAutoCompleteYield } from '@neo4j-cypher/language-support';
1310
import { Neo4jSchemaPoller } from '@neo4j-cypher/query-tools';
1411
import { TextDocument } from 'vscode-languageserver-textdocument';
12+
import { languageService } from './server';
1513

1614
export function doAutoCompletion(
1715
documents: TextDocuments<TextDocument>,
@@ -30,11 +28,15 @@ export function doAutoCompletion(
3028
const manualOrCharacterOrInwordTriggered =
3129
completionParams.context?.triggerCharacter !== ' ';
3230
if (yieldTriggered || manualOrCharacterOrInwordTriggered) {
33-
const completions: CompletionItem[] = autocomplete(
31+
const completions: CompletionItem[] = languageService.autocomplete(
3432
textDocument.getText(),
3533
neo4j.metadata?.dbSchema ?? {},
36-
offset,
37-
completionParams.context.triggerKind === CompletionTriggerKind.Invoked,
34+
{
35+
caretPosition: offset,
36+
manual:
37+
completionParams.context.triggerKind ===
38+
CompletionTriggerKind.Invoked,
39+
},
3840
);
3941

4042
const result = completions.map((item) => {

packages/language-server/src/linting.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import {
2-
_internalFeatureFlags,
32
clampUnsafePositions,
4-
parserWrapper,
53
SymbolTable,
64
} from '@neo4j-cypher/language-support';
75
import { Neo4jSchemaPoller } from '@neo4j-cypher/query-tools';
@@ -15,6 +13,7 @@ import {
1513
LinterTask,
1614
LintWorker,
1715
} from '@neo4j-cypher/lint-worker';
16+
import { languageService } from './server';
1817

1918
const defaultWorkerPath = join(__dirname, 'lintWorker.cjs');
2019

@@ -59,11 +58,9 @@ async function rawLintDocument(
5958
const proxyWorker = (await pool.proxy()) as unknown as LintWorker;
6059

6160
const fixedDbSchema = convertDbSchema(dbSchema, linterVersion);
62-
lastSemanticJob = proxyWorker.lintCypherQuery(
63-
query,
64-
fixedDbSchema,
65-
_internalFeatureFlags,
66-
);
61+
lastSemanticJob = proxyWorker.lintCypherQuery(query, fixedDbSchema, {
62+
consoleCommands: false,
63+
});
6764
const result = await lastSemanticJob;
6865

6966
//marks the entire text if any position is negative
@@ -74,7 +71,7 @@ async function rawLintDocument(
7471

7572
// Pass the computed symbol tables to the parser
7673
if (result.symbolTables) {
77-
parserWrapper.setSymbolsInfo(
74+
languageService.setSymbolsInfo(
7875
{
7976
query,
8077
symbolTables: result.symbolTables,

packages/language-server/src/server.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@ import {
1313
import { TextDocument } from 'vscode-languageserver-textdocument';
1414
import {
1515
DbSchema,
16-
parserWrapper,
1716
SymbolTable,
18-
syntaxColouringLegend,
17+
syntaxHighlightingLegend,
18+
CypherLanguageService,
1919
} from '@neo4j-cypher/language-support';
2020
import { Neo4jSchemaPoller } from '@neo4j-cypher/query-tools';
2121
import { doAutoCompletion } from './autocompletion';
2222
import { formatDocument } from './formatting';
2323
import { cleanupWorkers, lintDocument, setLintWorker } from './linting';
2424
import { doSignatureHelp } from './signatureHelp';
25-
import { applySyntaxColouringForDocument } from './syntaxColouring';
25+
import { highlightSyntaxForDocument } from './syntaxHighlighting';
2626
import {
2727
LintWorkerSettings,
2828
Neo4jConnectionSettings,
@@ -35,6 +35,9 @@ import { join } from 'path';
3535

3636
const defaultWorkerPath: string = join(__dirname, 'lintWorker.cjs');
3737
let workerPath = defaultWorkerPath;
38+
export const languageService = new CypherLanguageService({
39+
consoleCommandsEnabled: false,
40+
});
3841

3942
class SymbolFetcher {
4043
private processing = false;
@@ -86,7 +89,7 @@ class SymbolFetcher {
8689
result.symbolTables &&
8790
!(this.nextJob && this.nextJob.uri != docUri)
8891
) {
89-
parserWrapper.setSymbolsInfo(
92+
languageService.setSymbolsInfo(
9093
{
9194
query,
9295
symbolTables: result.symbolTables,
@@ -158,7 +161,7 @@ connection.onInitialize(() => {
158161
},
159162
semanticTokensProvider: {
160163
documentSelector: [{ language: 'cypher' }],
161-
legend: syntaxColouringLegend,
164+
legend: syntaxHighlightingLegend,
162165
range: false,
163166
full: {
164167
delta: false,
@@ -181,7 +184,7 @@ connection.onInitialized(() => {
181184

182185
const registrationOptions: SemanticTokensRegistrationOptions = {
183186
documentSelector: [{ language: 'cypher' }],
184-
legend: syntaxColouringLegend,
187+
legend: syntaxHighlightingLegend,
185188
range: false,
186189
full: {
187190
delta: false,
@@ -201,10 +204,8 @@ connection.onDidChangeConfiguration((params) => {
201204

202205
documents.onDidChangeContent((change) => lintSingleDocument(change.document));
203206

204-
// Trigger the syntax colouring
205-
connection.languages.semanticTokens.on(
206-
applySyntaxColouringForDocument(documents),
207-
);
207+
// Trigger the syntax highlighting
208+
connection.languages.semanticTokens.on(highlightSyntaxForDocument(documents));
208209

209210
// Trigger the signature help, providing info about functions / procedures
210211
connection.onSignatureHelp(doSignatureHelp(documents, neo4jSchemaPoller));

packages/language-server/src/signatureHelp.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { signatureHelp } from '@neo4j-cypher/language-support';
21
import {
32
SignatureHelp,
43
SignatureHelpParams,
@@ -7,6 +6,7 @@ import {
76

87
import { Neo4jSchemaPoller } from '@neo4j-cypher/query-tools';
98
import { TextDocument } from 'vscode-languageserver-textdocument';
9+
import { languageService } from './server';
1010

1111
export const emptyResult: SignatureHelp = {
1212
signatures: [],
@@ -26,10 +26,10 @@ export function doSignatureHelp(
2626
const position = params.position;
2727
const offset = textDocument.offsetAt(position);
2828

29-
return signatureHelp(
29+
return languageService.getSignatureHelp(
3030
textDocument.getText(),
3131
neo4j.metadata?.dbSchema ?? {},
32-
offset,
32+
{ caretPosition: offset },
3333
);
3434
};
3535
}

packages/language-server/src/syntaxColouring.ts renamed to packages/language-server/src/syntaxHighlighting.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
import {
2-
applySyntaxColouring,
3-
mapCypherToSemanticTokenIndex,
4-
} from '@neo4j-cypher/language-support';
1+
import { mapCypherToSemanticTokenIndex } from '@neo4j-cypher/language-support';
52
import {
63
SemanticTokensBuilder,
74
SemanticTokensParams,
85
TextDocument,
96
TextDocuments,
107
} from 'vscode-languageserver';
8+
import { languageService } from './server';
119

12-
export function applySyntaxColouringForDocument(
10+
export function highlightSyntaxForDocument(
1311
documents: TextDocuments<TextDocument>,
1412
) {
1513
return (params: SemanticTokensParams) => {
1614
const textDocument = documents.get(params.textDocument.uri);
1715
if (textDocument === undefined) return { data: [] };
1816

19-
const tokens = applySyntaxColouring(textDocument.getText());
17+
const tokens = languageService.highlightSyntax(textDocument.getText());
2018

2119
const builder = new SemanticTokensBuilder();
2220

packages/language-support/src/autocompletion/autocompletion.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,42 @@
11
import { DbSchema } from '../dbSchema.js';
22
import { findCaret } from '../helpers.js';
3-
import { parserWrapper } from '../parserWrapper.js';
4-
import { CompletionItem } from '../types.js';
3+
import {
4+
createParsingResult,
5+
ParsingResult,
6+
} from '../cypherLanguageService.js';
7+
import { CompletionItem, SymbolsInfo } from '../types.js';
58
import { completionCoreCompletion } from './completionCoreCompletions.js';
69

710
export function autocomplete(
811
query: string,
912
dbSchema: DbSchema,
10-
caretPosition: number = query.length,
11-
manual = false,
13+
{
14+
symbolsInfo,
15+
parsingResult,
16+
caretPosition = query.length,
17+
manual = false,
18+
consoleCommandsEnabled = true,
19+
}: {
20+
symbolsInfo?: SymbolsInfo;
21+
parsingResult?: ParsingResult;
22+
caretPosition?: number;
23+
manual?: boolean;
24+
consoleCommandsEnabled?: boolean;
25+
} = {},
1226
): CompletionItem[] {
13-
// TODO This is a temporary hack because completions are not working well
14-
query = query.slice(0, caretPosition);
15-
const parsingResult = parserWrapper.parse(query);
16-
const symbolsInfo = parserWrapper.symbolsInfo;
27+
const resolvedParsingResult =
28+
parsingResult ?? createParsingResult(query, { consoleCommandsEnabled });
29+
1730
/* We try to locate the statement where the caret is and the token of the caret
1831
19-
The reason for doing that is we need a way to "resynchronise" when the
32+
The reason for doing that is we need a way to "resynchronise" when the
2033
previous statements have errors and the parser fails from them onwards:
2134
2235
MATCH (m) REUT m; CREATE (n) R
23-
^ we should still be getting autocompletions here
36+
^ we should still be getting autocompletions here
2437
2538
*/
26-
const caret = findCaret(parsingResult, caretPosition);
39+
const caret = findCaret(resolvedParsingResult, caretPosition);
2740
if (caret) {
2841
const statement = caret.statement;
2942
const caretToken = caret.token;
@@ -33,6 +46,7 @@ export function autocomplete(
3346
caretToken,
3447
symbolsInfo,
3548
manual,
49+
consoleCommandsEnabled,
3650
);
3751
}
3852

packages/language-support/src/autocompletion/completionCoreCompletions.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,14 @@ import {
2424
tokenNames,
2525
} from '../lexerSymbols.js';
2626

27-
import { getMethodName, ParsedStatement } from '../parserWrapper.js';
27+
import { getMethodName, ParsedStatement } from '../cypherLanguageService.js';
2828

2929
import type { CandidateRule } from '../../../../vendor/antlr4-c3/dist/esm/index.js';
3030
import {
3131
CandidatesCollection,
3232
CodeCompletionCore,
3333
Token,
3434
} from '../../../../vendor/antlr4-c3/dist/esm/index.js';
35-
import { _internalFeatureFlags } from '../featureFlags.js';
3635
import {
3736
CompletionItem,
3837
CypherVersion,
@@ -463,6 +462,7 @@ export function completionCoreCompletion(
463462
caretToken: Token,
464463
symbolsInfo: SymbolsInfo | undefined,
465464
manualTrigger = false,
465+
consoleCommandsEnabled = true,
466466
): CompletionItem[] {
467467
const cypherVersion = resolveCypherVersion(
468468
parsingResult.cypherVersion,
@@ -518,7 +518,7 @@ export function completionCoreCompletion(
518518
CypherParser.RULE_relType,
519519
// Either enable the helper rules for lexer clashes,
520520
// or collect all console commands like below with symbolicNameString
521-
...(_internalFeatureFlags.consoleCommands
521+
...(consoleCommandsEnabled
522522
? [
523523
CypherParser.RULE_useCompletionRule,
524524
CypherParser.RULE_listCompletionRule,

packages/language-support/src/autocompletion/schemaBasedCompletions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
InsertTextFormat,
55
} from 'vscode-languageserver-types';
66
import { DbSchema } from '../dbSchema.js';
7-
import { ParsedStatement } from '../parserWrapper.js';
7+
import { ParsedStatement } from '../cypherLanguageService.js';
88
import {
99
ConditionNode,
1010
isLabelLeaf,

0 commit comments

Comments
 (0)