├── .nvmrc ├── packages ├── lint-worker │ ├── server-version-tag.txt │ ├── src │ │ ├── index.ts │ │ ├── version.ts │ │ └── lintWorker.ts │ ├── vitest.config.mts │ ├── tsconfig.node.json │ ├── tsconfig.json │ └── README.md ├── react-codemirror │ ├── src │ │ ├── constants.ts │ │ ├── viteEnv.d.ts │ │ ├── index.ts │ │ ├── lang-cypher │ │ │ ├── utils.ts │ │ │ └── langCypher.ts │ │ ├── ndlTokensCopy.test.ts │ │ └── e2e_tests │ │ │ └── extraKeybindings.spec.tsx │ ├── playwright │ │ ├── index.tsx │ │ └── index.html │ ├── .gitignore │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vite.config.ts │ ├── README.md │ └── playwright-ct.config.ts ├── vscode-extension │ ├── tests │ │ ├── fixtures │ │ │ ├── invalid.cypher │ │ │ ├── syntax-validation.cypher │ │ │ ├── valid.cypher │ │ │ ├── create-for-match.cypher │ │ │ ├── cypher-versioned.cypher │ │ │ ├── function-completion.cypher │ │ │ ├── match-for-create.cypher │ │ │ ├── procedure-completion.cypher │ │ │ ├── .vscode │ │ │ │ └── settings.json │ │ │ ├── auto-completion.cypher │ │ │ ├── params.cypher │ │ │ ├── deprecated-by.cypher │ │ │ ├── textmate │ │ │ │ ├── simple-match.cypher │ │ │ │ ├── comments.md │ │ │ │ ├── functions.md │ │ │ │ ├── literals.md │ │ │ │ ├── parameters.md │ │ │ │ ├── labels.md │ │ │ │ ├── properties.md │ │ │ │ ├── simple-match.fs │ │ │ │ ├── simple-match.cs │ │ │ │ ├── simple-match.java │ │ │ │ ├── simple-match.go │ │ │ │ ├── procedures.md │ │ │ │ ├── call-subquery.md │ │ │ │ ├── complex-match.md │ │ │ │ ├── simple-match.py │ │ │ │ ├── simple-match.md │ │ │ │ ├── simple-match.js │ │ │ │ └── simple-match.ts │ │ │ ├── symbol-table.cypher │ │ │ ├── multiline.cypher │ │ │ ├── movies-syntax-validation.cypher │ │ │ ├── signature-help.cypher │ │ │ ├── call-in-transactions.cypher │ │ │ ├── allShortestPaths-completion.cypher │ │ │ ├── syntax-highlighting.cypher │ │ │ └── textmate-results │ │ │ │ └── simple-match-cypher.json │ │ ├── specs │ │ │ ├── webviews │ │ │ │ └── setup.spec.ts │ │ │ ├── api │ │ │ │ ├── setup.spec.ts │ │ │ │ └── formatting.spec.ts │ │ │ └── unit │ │ │ │ └── parametersTreeDataProvider.spec.ts │ │ ├── mocks │ │ │ ├── inMemorySecretStorage.ts │ │ │ ├── inMemoryMemento.ts │ │ │ ├── mockExtensionContext.ts │ │ │ ├── mockLanguageClient.ts │ │ │ ├── setupMockContextStubs.ts │ │ │ └── mockSchemaPoller.ts │ │ ├── testRunnerDebug.ts │ │ ├── setupTestContainer.ts │ │ └── testRunner.ts │ ├── src │ │ ├── ndl.ts │ │ ├── getNonce.ts │ │ ├── helpers.ts │ │ ├── cypherRunner.ts │ │ ├── treeviews │ │ │ ├── connectionTreeDecorationProvider.ts │ │ │ └── parametersTreeDataProvider.ts │ │ ├── languageClientService.ts │ │ ├── webviews │ │ │ └── queryResults │ │ │ │ ├── queryResultsTypes.ts │ │ │ │ └── querySummary.ts │ │ ├── typeUtils.ts │ │ ├── commandHandlers │ │ │ └── linters.ts │ │ └── components │ │ │ └── collapsible.tsx │ ├── .gitignore │ ├── resources │ │ ├── images │ │ │ ├── demo.gif │ │ │ ├── logo.png │ │ │ ├── neo4j.png │ │ │ ├── demo-connect.png │ │ │ ├── demo-execution.png │ │ │ ├── demo-linter-5.png │ │ │ ├── demo-disconnect.png │ │ │ ├── demo-linter-2025.png │ │ │ ├── demo-add-parameter.png │ │ │ ├── demo-new-connection.png │ │ │ ├── demo-param-editing.png │ │ │ ├── demo-manage-connection.png │ │ │ ├── demo-embedded-cypher-menus.png │ │ │ ├── demo-linter-manual-adjusting.png │ │ │ ├── demo-linter-automatic-adjusting.png │ │ │ ├── ParameterDark.svg │ │ │ ├── ParameterLight.svg │ │ │ ├── ConnectedDark.svg │ │ │ ├── ConnectedLight.svg │ │ │ ├── Neo4jActiveDark.svg │ │ │ ├── Neo4jActiveLight.svg │ │ │ ├── Neo4jInactiveDark.svg │ │ │ └── Neo4jInactiveLight.svg │ │ └── styles │ │ │ ├── queryDetails.css │ │ │ ├── queryVisualization.css │ │ │ ├── reset.css │ │ │ ├── connectionPanel.css │ │ │ └── components │ │ │ ├── collapsible.css │ │ │ └── vizWrapper.css │ ├── tsconfig.json │ ├── genTextMate.js │ ├── tsconfig.node.json │ ├── .vscodeignore │ ├── cypher-language-configuration.json │ ├── esbuild-extension.mts │ ├── syntaxes │ │ ├── cypher.markdown.json │ │ ├── cypher.go.json │ │ ├── cypher.csharp.json │ │ ├── cypher.java.json │ │ └── cypher.fsharp.json │ └── test │ │ └── paramUnitTest.test.ts ├── language-support │ ├── src │ │ ├── tests │ │ │ ├── formatting │ │ │ │ ├── cli-test-files │ │ │ │ │ ├── formatted.cy │ │ │ │ │ ├── not-formatted.cy │ │ │ │ │ └── nested │ │ │ │ │ │ └── also-not-formatted.cy │ │ │ │ └── testutil.ts │ │ │ ├── syntaxValidation │ │ │ │ └── helpers.ts │ │ │ ├── autocompletion │ │ │ │ ├── autocompletionHelper.test.ts │ │ │ │ └── expressionCompletion.test.ts │ │ │ └── benchmarks │ │ │ │ └── completions.benchmark.ts │ │ ├── formatting │ │ │ ├── architecture.png │ │ │ └── standardizer.ts │ │ ├── antlr-grammar │ │ │ ├── CypherPreLexer.g4 │ │ │ ├── CypherCmdLexer.g4 │ │ │ ├── CypherPreParser.g4 │ │ │ └── CypherCmdParser.g4 │ │ ├── dbSchema.ts │ │ ├── featureFlags.ts │ │ └── autocompletion │ │ │ ├── autocompletionHelpers.ts │ │ │ └── autocompletion.ts │ ├── vitest.config.mts │ ├── tsconfig.node.json │ ├── tsconfig.json │ └── README.md ├── react-codemirror-playground │ ├── postcss.config.js │ ├── src │ │ ├── index.css │ │ ├── index.tsx │ │ ├── viteEnv.d.ts │ │ ├── treeUtil.ts │ │ └── TokenTable.tsx │ ├── README.md │ ├── vite.config.ts │ ├── tailwind.config.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── index.html │ └── package.json ├── language-server │ ├── tsconfig.json │ └── src │ │ ├── types.ts │ │ ├── formatting.ts │ │ ├── signatureHelp.ts │ │ ├── syntaxColouring.ts │ │ └── autocompletion.ts └── query-tools │ ├── vitest.config.mts │ ├── tsconfig.node.json │ ├── tsconfig.json │ ├── src │ ├── data-transforms │ │ ├── format-float.ts │ │ └── clean-type.ts │ ├── queries │ │ ├── roles.ts │ │ ├── users.ts │ │ ├── version.ts │ │ ├── dataSummary.ts │ │ └── databases.ts │ ├── types │ │ └── sdkTypes.ts │ ├── integration-tests │ │ └── setupTestContainer.ts │ ├── index.ts │ └── result-transformers │ │ └── graph-result-transformer.ts │ ├── README.md │ └── package.json ├── editor-plugin └── intellij │ ├── src │ └── main │ │ ├── resources │ │ ├── fileTemplates │ │ │ └── Cypher File.cypher.ft │ │ └── META-INF │ │ │ ├── plugin.xml │ │ │ └── fileIcon.svg │ │ └── java │ │ └── org │ │ └── neo4j │ │ └── intellij │ │ └── lsp │ │ └── language │ │ ├── CypherLanguage.java │ │ ├── CypherIcons.java │ │ └── CypherFileType.java │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── settings.gradle.kts │ ├── README.md │ ├── .gitignore │ ├── .run │ └── Run IDE with Plugin.run.xml │ └── build.gradle.kts ├── .gitattributes ├── .husky └── pre-commit ├── .prettierrc.json ├── imgs ├── auto-completion.gif ├── repo-overview.png └── vscode-playground.png ├── .changeset ├── blue-dolls-thank.md ├── thirty-bats-pay.md ├── chatty-views-hide.md ├── cyan-ghosts-nail.md ├── stale-peas-warn.md ├── chatty-needles-guess.md ├── fuzzy-mangos-throw.md ├── lucky-plants-own.md ├── mean-donkeys-guess.md ├── moody-rice-wave.md ├── late-facts-move.md ├── lemon-llamas-stay.md ├── quick-berries-report.md ├── afraid-beans-juggle.md ├── green-loops-yawn.md ├── loud-meals-attend.md ├── nervous-rabbits-greet.md ├── nice-elephants-prove.md ├── nine-bobcats-sit.md ├── purple-buckets-applaud.md ├── rich-fans-happen.md ├── rich-mammals-rule.md ├── unlucky-dragons-tickle.md ├── chatty-plants-greet.md ├── clean-apes-pay.md ├── five-maps-divide.md ├── grumpy-toys-vanish.md ├── little-vans-begin.md ├── poor-toys-change.md ├── young-llamas-teach.md ├── bitter-rocks-bet.md ├── cyan-seals-burn.md ├── itchy-deers-type.md ├── neat-panthers-whisper.md ├── old-shoes-provide.md ├── polite-dolls-glow.md ├── polite-meals-relax.md ├── salty-houses-camp.md ├── serious-ducks-sleep.md ├── silver-students-wait.md ├── true-olives-stay.md ├── twelve-cycles-call.md ├── violet-peas-float.md ├── wet-tigers-beam.md ├── itchy-queens-bathe.md ├── nice-jokes-give.md ├── ninety-panthers-pretend.md ├── silly-bugs-doubt.md ├── brave-donuts-camp.md ├── four-tools-leave.md ├── lovely-mails-ring.md ├── odd-humans-judge.md ├── slick-pandas-dance.md ├── stale-donkeys-grow.md ├── tender-insects-admire.md ├── wild-rooms-shop.md ├── angry-beers-explain.md ├── gentle-pans-rescue.md ├── purple-suns-applaud.md ├── strong-squids-relax.md ├── tough-dots-shout.md ├── brown-ravens-obey.md ├── loud-shrimps-push.md ├── ninety-doors-brush.md ├── old-ends-agree.md ├── silent-kangaroos-melt.md ├── moody-fireants-guess.md ├── nervous-taxis-admire.md ├── selfish-wombats-pay.md ├── seven-ducks-dig.md ├── open-rules-raise.md ├── shiny-parents-cry.md ├── spicy-cheetahs-repeat.md ├── chilled-dots-guess.md ├── cuddly-vans-flash.md ├── healthy-turtles-change.md ├── thick-signs-prove.md ├── thick-windows-know.md ├── curvy-singers-greet.md ├── deep-symbols-strive.md ├── modern-parrots-sin.md ├── blue-penguins-worry.md ├── eleven-scissors-type.md ├── shiny-dodos-rush.md ├── fruity-pots-marry.md ├── yellow-grapes-ring.md ├── hungry-beans-eat.md ├── swift-hairs-sell.md ├── fifty-zebras-grow.md ├── pink-chefs-trade.md ├── stupid-coins-itch.md ├── config.json ├── fuzzy-rice-train.md └── README.md ├── .lintstagedrc.json ├── vendor └── antlr4-c3 │ ├── images │ └── token-position.png │ ├── vitest.config.ts │ ├── tests │ ├── generate.sh │ ├── readme.md │ ├── Whitebox.g4 │ ├── Expr.g4 │ └── Optionals.g4 │ ├── index.ts │ ├── .npmignore │ ├── cspell.json │ ├── tsconfig.json │ ├── .gitignore │ ├── LICENSE │ └── package.json ├── tsconfig.node.json ├── vitest.workspace.ts ├── .prettierignore ├── .vscode ├── extensions.json ├── settings.json └── tasks.json ├── .eslintignore ├── .gitignore ├── tsconfig.base.json ├── pnpm-workspace.yaml ├── .github ├── workflows │ ├── benchmark.yaml │ ├── formatting-integrity-check.yaml │ ├── archive-vscode-artifacts.yaml │ ├── publish-vscode-extension.yaml │ ├── publish-npm-packages.yaml │ └── deploy-demo.yml └── actions │ └── codemirror-e2e-tests │ └── action.yaml ├── .eslintrc.js ├── tsconfig.json ├── README.md └── CONTRIBUTING.md /.nvmrc: -------------------------------------------------------------------------------- 1 | v24.11.1 -------------------------------------------------------------------------------- /packages/lint-worker/server-version-tag.txt: -------------------------------------------------------------------------------- 1 | neo4j-2025.12 2 | -------------------------------------------------------------------------------- /editor-plugin/intellij/src/main/resources/fileTemplates/Cypher File.cypher.ft: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | semanticAnalysis.js linguist-vendored 2 | ndl.css linguist-vendored -------------------------------------------------------------------------------- /packages/react-codemirror/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const DEBOUNCE_TIME = 200; 2 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/invalid.cypher: -------------------------------------------------------------------------------- 1 | WITH (n:Person) RETURN n -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/syntax-validation.cypher: -------------------------------------------------------------------------------- 1 | MATCH (n) RETURN m -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/valid.cypher: -------------------------------------------------------------------------------- 1 | CREATE (n:Person) RETURN n -------------------------------------------------------------------------------- /packages/react-codemirror/src/viteEnv.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/ndl.ts: -------------------------------------------------------------------------------- 1 | import '@neo4j-ndl/base/lib/neo4j-ds-styles.css'; 2 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/create-for-match.cypher: -------------------------------------------------------------------------------- 1 | CREATE (n:NewLabel) RETURN n -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/cypher-versioned.cypher: -------------------------------------------------------------------------------- 1 | CYPHER 5 MATCH (n) RETURN m -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/function-completion.cypher: -------------------------------------------------------------------------------- 1 | RETURN apoc.create. 2 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/match-for-create.cypher: -------------------------------------------------------------------------------- 1 | MATCH (n:NewLabel) RETURN n -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/procedure-completion.cypher: -------------------------------------------------------------------------------- 1 | CALL apoc.trigger. 2 | -------------------------------------------------------------------------------- /packages/language-support/src/tests/formatting/cli-test-files/formatted.cy: -------------------------------------------------------------------------------- 1 | MATCH (n) 2 | RETURN n -------------------------------------------------------------------------------- /packages/language-support/src/tests/formatting/cli-test-files/not-formatted.cy: -------------------------------------------------------------------------------- 1 | match (n) return n -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.eol": "\n" 3 | } 4 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/auto-completion.cypher: -------------------------------------------------------------------------------- 1 | MATCH (n) ; 2 | MATCH (n) RETURN n.foo -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /packages/language-support/src/tests/formatting/cli-test-files/nested/also-not-formatted.cy: -------------------------------------------------------------------------------- 1 | match (n) return n -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/params.cypher: -------------------------------------------------------------------------------- 1 | RETURN $a, $b, $`some param`, $`some-param`, $a + $b; -------------------------------------------------------------------------------- /imgs/auto-completion.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/imgs/auto-completion.gif -------------------------------------------------------------------------------- /imgs/repo-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/imgs/repo-overview.png -------------------------------------------------------------------------------- /.changeset/blue-dolls-thank.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/lint-worker': patch 3 | --- 4 | 5 | Fix aura linter version 6 | -------------------------------------------------------------------------------- /imgs/vscode-playground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/imgs/vscode-playground.png -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/deprecated-by.cypher: -------------------------------------------------------------------------------- 1 | CALL apoc.create.uuids(5); 2 | RETURN apoc.create.uuid(); -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/simple-match.cypher: -------------------------------------------------------------------------------- 1 | MATCH (n:Label) RETURN function(n.property) 2 | -------------------------------------------------------------------------------- /packages/vscode-extension/.gitignore: -------------------------------------------------------------------------------- 1 | tests/fixtures/.env.test 2 | webview_tests/fixtures/.env.test 3 | .wdio-vscode-service -------------------------------------------------------------------------------- /.changeset/thirty-bats-pay.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Re-surfaces preparser errors 6 | -------------------------------------------------------------------------------- /.changeset/chatty-views-hide.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/react-codemirror': patch 3 | --- 4 | 5 | resetting z index for cm panels 6 | -------------------------------------------------------------------------------- /.changeset/cyan-ghosts-nail.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Fix formatting of dynamic labels 6 | -------------------------------------------------------------------------------- /.changeset/stale-peas-warn.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Fixes bug in labels completion 6 | -------------------------------------------------------------------------------- /packages/lint-worker/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lintWorker'; 2 | export * from './helpers'; 3 | export * from './version'; 4 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/symbol-table.cypher: -------------------------------------------------------------------------------- 1 | MATCH (n:Person) 2 | RETURN n.name; 3 | WITH 1 AS x 4 | RETURN x.name; -------------------------------------------------------------------------------- /.changeset/chatty-needles-guess.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | adding support for :play command 6 | -------------------------------------------------------------------------------- /.changeset/fuzzy-mangos-throw.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | adding support for the help command 6 | -------------------------------------------------------------------------------- /.changeset/lucky-plants-own.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@neo4j-cypher/language-support": patch 3 | --- 4 | 5 | Add `:style` cmd to command parser 6 | -------------------------------------------------------------------------------- /.changeset/mean-donkeys-guess.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': minor 3 | --- 4 | 5 | Upgrades formatter from V1 -> V2 6 | -------------------------------------------------------------------------------- /.changeset/moody-rice-wave.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/react-codemirror': patch 3 | --- 4 | 5 | updating themes to use needle colors 6 | -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.ts": ["prettier --write", "eslint --fix --max-warnings 0"], 3 | "*.json": ["prettier --write"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/react-codemirror/playwright/index.tsx: -------------------------------------------------------------------------------- 1 | // Import styles, initialize component theme here. 2 | // import '../src/common.css'; 3 | -------------------------------------------------------------------------------- /.changeset/late-facts-move.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/react-codemirror': patch 3 | --- 4 | 5 | adding rich clipboard copier to codemirror 6 | -------------------------------------------------------------------------------- /.changeset/lemon-llamas-stay.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/query-tools': minor 3 | --- 4 | 5 | Adds server-version to connection in dbSchema 6 | -------------------------------------------------------------------------------- /.changeset/quick-berries-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/react-codemirror': patch 3 | --- 4 | 5 | Editor history is now a controlled prop 6 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/multiline.cypher: -------------------------------------------------------------------------------- 1 | CREATE (n:Person) RETURN n; 2 | CREATE (n:Person), (m:Movie); 3 | MATCH (n) RETURN n -------------------------------------------------------------------------------- /.changeset/afraid-beans-juggle.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/react-codemirror': patch 3 | --- 4 | 5 | Adds more props to the CypherEditor component 6 | -------------------------------------------------------------------------------- /.changeset/green-loops-yawn.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Adds support for labels/reltypes in symbol table 6 | -------------------------------------------------------------------------------- /.changeset/loud-meals-attend.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@neo4j-cypher/react-codemirror": patch 3 | --- 4 | 5 | execute single line query on enter by default 6 | -------------------------------------------------------------------------------- /.changeset/nervous-rabbits-greet.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-server': patch 3 | --- 4 | 5 | Makes language server executable with npx 6 | -------------------------------------------------------------------------------- /.changeset/nice-elephants-prove.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Adds auto-completion of usernames and roles 6 | -------------------------------------------------------------------------------- /.changeset/nine-bobcats-sit.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Adds signature information on auto-completions 6 | -------------------------------------------------------------------------------- /.changeset/purple-buckets-applaud.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/react-codemirror': patch 3 | --- 4 | 5 | Improved history prop change detection 6 | -------------------------------------------------------------------------------- /.changeset/rich-fans-happen.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | adding semantic validation for missing paramters 6 | -------------------------------------------------------------------------------- /.changeset/rich-mammals-rule.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/query-tools': patch 3 | --- 4 | 5 | Fix vector serialization for query running/params 6 | -------------------------------------------------------------------------------- /.changeset/unlucky-dragons-tickle.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Adds autocompletions for CYPHER 6 | -------------------------------------------------------------------------------- /packages/react-codemirror/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | test-results/ 3 | playwright-report/ 4 | playwright/.cache/ 5 | src/lang-cypher/lintWorker.mjs -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/movies-syntax-validation.cypher: -------------------------------------------------------------------------------- 1 | MATCH q=(p:Person)-[:ACTED_IN]->(m:Movie { title: "The Matrix" }) RETURN q; -------------------------------------------------------------------------------- /.changeset/chatty-plants-greet.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Adds errors for undeclared procedures / functions 6 | -------------------------------------------------------------------------------- /.changeset/clean-apes-pay.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Fixes database completions for CREATE ALIAS commands 6 | -------------------------------------------------------------------------------- /.changeset/five-maps-divide.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Fix comment, QPP and pattern bugs in v1 formatter 6 | -------------------------------------------------------------------------------- /.changeset/grumpy-toys-vanish.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Wires the symbol table into the language support 6 | -------------------------------------------------------------------------------- /.changeset/little-vans-begin.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@neo4j-cypher/react-codemirror": patch 3 | --- 4 | 5 | using custom light color theme for the cypher editor 6 | -------------------------------------------------------------------------------- /.changeset/poor-toys-change.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Fixes backticking of parameters and dbNames/aliases 6 | -------------------------------------------------------------------------------- /.changeset/young-llamas-teach.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@neo4j-cypher/react-codemirror": patch 3 | --- 4 | 5 | Re-export language support from react codemirror 6 | -------------------------------------------------------------------------------- /vendor/antlr4-c3/images/token-position.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/vendor/antlr4-c3/images/token-position.png -------------------------------------------------------------------------------- /.changeset/bitter-rocks-bet.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/lint-worker': patch 3 | --- 4 | 5 | Fixes bug when switching to linters with new npm version scheme 6 | -------------------------------------------------------------------------------- /.changeset/cyan-seals-burn.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Updates grammar and semantic analysis to version 2025.01.0 6 | -------------------------------------------------------------------------------- /.changeset/itchy-deers-type.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Updates parser and semantic analysis to version 2025.02 6 | -------------------------------------------------------------------------------- /.changeset/neat-panthers-whisper.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Bugfix for case-insensitivity of built-in functions 6 | -------------------------------------------------------------------------------- /.changeset/old-shoes-provide.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Adds autocompletions following YIELD in a procedure call 6 | -------------------------------------------------------------------------------- /.changeset/polite-dolls-glow.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Makes the semantic analysis work with partial queries 6 | -------------------------------------------------------------------------------- /.changeset/polite-meals-relax.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Updates grammar and semantic analysis to version 2025.07 6 | -------------------------------------------------------------------------------- /.changeset/salty-houses-camp.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Updates grammar and semantic analysis to version 2025.06 6 | -------------------------------------------------------------------------------- /.changeset/serious-ducks-sleep.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Fixes bug using console commands as variables or labels 6 | -------------------------------------------------------------------------------- /.changeset/silver-students-wait.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Adds semantic analysis for procedures and functions 6 | -------------------------------------------------------------------------------- /.changeset/true-olives-stay.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': minor 3 | --- 4 | 5 | New interface for formatQuery, support maxColumn option 6 | -------------------------------------------------------------------------------- /.changeset/twelve-cycles-call.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Adds signature help capabilities to the language support 6 | -------------------------------------------------------------------------------- /.changeset/violet-peas-float.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Improves speed of semantic analysis between 2x and 6x 6 | -------------------------------------------------------------------------------- /.changeset/wet-tigers-beam.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@neo4j-cypher/react-codemirror": patch 3 | --- 4 | 5 | Ensures CRLF newlines are handled when setting editor value 6 | -------------------------------------------------------------------------------- /.changeset/itchy-queens-bathe.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Adds errors when passing an invalid cypher version in query 6 | -------------------------------------------------------------------------------- /.changeset/nice-jokes-give.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Modified backtick insertion to only happen when really necessary 6 | -------------------------------------------------------------------------------- /.changeset/ninety-panthers-pretend.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@neo4j-cypher/react-codemirror": patch 3 | --- 4 | 5 | Simplify detection and handling of value prop updates 6 | -------------------------------------------------------------------------------- /.changeset/silly-bugs-doubt.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Moves the syntax errors to the semantic analysis web worker 6 | -------------------------------------------------------------------------------- /packages/react-codemirror-playground/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /.changeset/brave-donuts-camp.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Corrects bug with semantic analysis and List of non-numeric types 6 | -------------------------------------------------------------------------------- /.changeset/four-tools-leave.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Added parsing of CYPHER and CYPHER = 6 | -------------------------------------------------------------------------------- /.changeset/lovely-mails-ring.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Adds deprecation warning tags on deprecated functions/procedures 6 | -------------------------------------------------------------------------------- /.changeset/odd-humans-judge.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | '@neo4j-cypher/lint-worker': patch 4 | --- 5 | 6 | Add vector support to linting 7 | -------------------------------------------------------------------------------- /.changeset/slick-pandas-dance.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Fixes "missing label/rel type" warnings for some special cases 6 | -------------------------------------------------------------------------------- /.changeset/stale-donkeys-grow.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Fix incorrect completions when identifier overlaps with keyword 6 | -------------------------------------------------------------------------------- /.changeset/tender-insects-admire.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Adds support for the access-mode console command to the grammar 6 | -------------------------------------------------------------------------------- /.changeset/wild-rooms-shop.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/query-tools': patch 3 | --- 4 | 5 | Fixes execution of queries that need to be ran in implicit transactions 6 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/demo.gif -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/logo.png -------------------------------------------------------------------------------- /.changeset/angry-beers-explain.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Fixes signature help bug with arguments that include a default value 6 | -------------------------------------------------------------------------------- /.changeset/gentle-pans-rescue.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Fixes bug in signature help of functions nested inside procedure calls 6 | -------------------------------------------------------------------------------- /.changeset/purple-suns-applaud.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Fix issue where syntax highlighting crashes on create constraint query 6 | -------------------------------------------------------------------------------- /.changeset/strong-squids-relax.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@neo4j-cypher/react-codemirror": patch 3 | --- 4 | 5 | Fix a bug causing debounced value updates to get cancelled erroneously 6 | -------------------------------------------------------------------------------- /.changeset/tough-dots-shout.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | '@neo4j-cypher/lint-worker': patch 4 | --- 5 | 6 | Updates dev artifacts to 2025.12 7 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/neo4j.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/neo4j.png -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/signature-help.cypher: -------------------------------------------------------------------------------- 1 | RETURN abs() 2 | CALL apoc.import.csv(nodes, rels, config) 3 | CALL apoc.import.csv(nodes, rels, config, other) -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "noEmit": true 5 | }, 6 | "include": [".eslintrc.js", "vitest.workspace.ts"] 7 | } 8 | -------------------------------------------------------------------------------- /vitest.workspace.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | 'packages/language-support', 3 | 'packages/react-codemirror', 4 | 'packages/query-tools', 5 | 'vendor/antlr4-c3', 6 | ]; 7 | -------------------------------------------------------------------------------- /.changeset/brown-ravens-obey.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/react-codemirror-playground': patch 3 | '@neo4j-cypher/react-codemirror': patch 4 | --- 5 | 6 | Fix incorrect exports 7 | -------------------------------------------------------------------------------- /.changeset/loud-shrimps-push.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/react-codemirror': patch 3 | '@neo4j-cypher/language-server': patch 4 | --- 5 | 6 | Fixes bug with auto-completions 7 | -------------------------------------------------------------------------------- /.changeset/ninety-doors-brush.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Adds backticking to labels, rel types and property names on auto-completions 6 | -------------------------------------------------------------------------------- /.changeset/old-ends-agree.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-server': patch 3 | '@neo4j-cypher/lint-worker': patch 4 | --- 5 | 6 | Adds support for manual linter switching 7 | -------------------------------------------------------------------------------- /.changeset/silent-kangaroos-melt.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Fixes bug reporting missing label / rel type when inside opposite pattern 6 | -------------------------------------------------------------------------------- /editor-plugin/intellij/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/editor-plugin/intellij/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /vendor/antlr4-c3/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /.changeset/moody-fireants-guess.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Adds backticking when needed on autocompletions of aliases and database names 6 | -------------------------------------------------------------------------------- /.changeset/nervous-taxis-admire.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Solves highlighting bug with multiline constructs embedded in the preparser 6 | -------------------------------------------------------------------------------- /.changeset/selfish-wombats-pay.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Fixes bug using non language keywords (EXPLAIN, PROFILE, etc) as symbolic names 6 | -------------------------------------------------------------------------------- /.changeset/seven-ducks-dig.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-server': minor 3 | '@neo4j-cypher/lint-worker': minor 4 | --- 5 | 6 | Adds support for server-versioned lint-workers 7 | -------------------------------------------------------------------------------- /packages/language-support/src/formatting/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/language-support/src/formatting/architecture.png -------------------------------------------------------------------------------- /packages/lint-worker/vitest.config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/demo-connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/demo-connect.png -------------------------------------------------------------------------------- /.changeset/open-rules-raise.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | '@neo4j-cypher/lint-worker': patch 4 | --- 5 | 6 | Updates semantic analysis and grammar to 2025.10 7 | -------------------------------------------------------------------------------- /.changeset/shiny-parents-cry.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Solves bug splitting commands which was breaking for queries just containing comments 6 | -------------------------------------------------------------------------------- /.changeset/spicy-cheetahs-repeat.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@neo4j-cypher/language-support": patch 3 | --- 4 | 5 | Add support for sysinfo, welcome, connect, disconnect and server console commands 6 | -------------------------------------------------------------------------------- /packages/language-support/vitest.config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/demo-execution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/demo-execution.png -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/demo-linter-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/demo-linter-5.png -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/call-in-transactions.cypher: -------------------------------------------------------------------------------- 1 | UNWIND range(1, 3) AS i 2 | CALL (i) { 3 | UNWIND [1, 2] AS j 4 | CREATE (n:N {i: i, j: j}) 5 | } IN TRANSACTIONS -------------------------------------------------------------------------------- /.changeset/chilled-dots-guess.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@neo4j-cypher/react-codemirror": patch 3 | --- 4 | 5 | allow signature help panel to render below editor when there's not enough space above it 6 | -------------------------------------------------------------------------------- /.changeset/cuddly-vans-flash.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Adds hints to the error when using a procedure/function where the other would be appropriate 6 | -------------------------------------------------------------------------------- /.changeset/healthy-turtles-change.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Updates the semantic analysis module to use the antlr parser instead of the javaCC one 6 | -------------------------------------------------------------------------------- /.changeset/thick-signs-prove.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | --- 4 | 5 | Fixes a bug in formatting when using preparser keywords in function/procedure namespaces 6 | -------------------------------------------------------------------------------- /.changeset/thick-windows-know.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | '@neo4j-cypher/lint-worker': patch 4 | --- 5 | 6 | Updates grammar and semantic analysis to 2025.11 7 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | out/ 2 | packages/language-support/src/generated-parser/* 3 | vendor/* 4 | packages/vscode-extension/tests/fixtures/textmate/* 5 | packages/vscode-extension/syntaxes/cypher.json -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/demo-disconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/demo-disconnect.png -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/demo-linter-2025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/demo-linter-2025.png -------------------------------------------------------------------------------- /.changeset/curvy-singers-greet.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@neo4j-cypher/react-codemirror-playground": patch 3 | "@neo4j-cypher/react-codemirror": patch 4 | --- 5 | 6 | Add an ariaLabel prop to CypherEditor 7 | -------------------------------------------------------------------------------- /.changeset/deep-symbols-strive.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | '@neo4j-cypher/lint-worker': patch 4 | --- 5 | 6 | Updated grammar and semantic analysis to version 2025.08 7 | -------------------------------------------------------------------------------- /.changeset/modern-parrots-sin.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | '@neo4j-cypher/lint-worker': patch 4 | --- 5 | 6 | Update grammar and semantic analysis to version 2025.09 7 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/demo-add-parameter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/demo-add-parameter.png -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/demo-new-connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/demo-new-connection.png -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/demo-param-editing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/demo-param-editing.png -------------------------------------------------------------------------------- /.changeset/blue-penguins-worry.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | '@neo4j-cypher/query-tools': patch 4 | --- 5 | 6 | Updates semantic error worker to use given cypher version 7 | -------------------------------------------------------------------------------- /.changeset/eleven-scissors-type.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@neo4j-cypher/react-codemirror-playground": patch 3 | "@neo4j-cypher/react-codemirror": patch 4 | --- 5 | 6 | Fix bug causing debouncing to override value 7 | -------------------------------------------------------------------------------- /.changeset/shiny-dodos-rush.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/react-codemirror': patch 3 | --- 4 | 5 | Expose moveFocusOnTab property on the CypherEditor component to conditionally disable tab keymappings 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "vitest.explorer", 4 | "esbenp.prettier-vscode", 5 | "dbaeumer.vscode-eslint", 6 | "bradlc.vscode-tailwindcss" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/demo-manage-connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/demo-manage-connection.png -------------------------------------------------------------------------------- /editor-plugin/intellij/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "cypher-lsp-support" -------------------------------------------------------------------------------- /packages/language-server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDir": "src" 6 | }, 7 | "include": ["src"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/query-tools/vitest.config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | hookTimeout: 90000 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /packages/react-codemirror-playground/src/index.css: -------------------------------------------------------------------------------- 1 | @import url(https://cdn.jsdelivr.net/npm/firacode@6.2.0/distr/fira_code.css); 2 | 3 | @tailwind base; 4 | @tailwind components; 5 | @tailwind utilities; 6 | -------------------------------------------------------------------------------- /.changeset/fruity-pots-marry.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': minor 3 | '@neo4j-cypher/language-server': minor 4 | --- 5 | 6 | Fixes handling of negative positions and syntaxchecker-exceptions 7 | -------------------------------------------------------------------------------- /.changeset/yellow-grapes-ring.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@neo4j-cypher/react-codemirror": patch 3 | --- 4 | 5 | Set initial latestDispatchedValue and flush debounced changes onExecute 6 | Add tests for debounce behaviour 7 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/demo-embedded-cypher-menus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/demo-embedded-cypher-menus.png -------------------------------------------------------------------------------- /vendor/antlr4-c3/tests/generate.sh: -------------------------------------------------------------------------------- 1 | GRAMMAR_DIR=${PWD} 2 | 3 | antlr4 -Dlanguage=TypeScript \ 4 | -no-listener -no-visitor \ 5 | ${GRAMMAR_DIR}/*.g4 \ 6 | -o ${GRAMMAR_DIR}/generated -Xexact-output-dir 7 | -------------------------------------------------------------------------------- /packages/react-codemirror-playground/README.md: -------------------------------------------------------------------------------- 1 | # React Codemirror Playground 2 | 3 | Run `npm install` && `pnpm dev-codemirror` in the root folder of the project or use launch the demo from the "Run & Debug" tab. 4 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/demo-linter-manual-adjusting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/demo-linter-manual-adjusting.png -------------------------------------------------------------------------------- /.changeset/hungry-beans-eat.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | '@neo4j-cypher/react-codemirror': patch 4 | '@neo4j-cypher/language-server': patch 5 | --- 6 | 7 | Add support for console commands 8 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/demo-linter-automatic-adjusting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j/cypher-language-support/HEAD/packages/vscode-extension/resources/images/demo-linter-automatic-adjusting.png -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/allShortestPaths-completion.cypher: -------------------------------------------------------------------------------- 1 | // The first query contains errors on purpose so the 2 | // syntax errors get triggered before the auto-completion 3 | MATCH (n) REURN n; MATCH a -------------------------------------------------------------------------------- /.changeset/swift-hairs-sell.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/react-codemirror': patch 3 | '@neo4j-cypher/language-server': patch 4 | '@neo4j-cypher/lint-worker': patch 5 | --- 6 | 7 | Fixes faulty worker termination on big queries 8 | -------------------------------------------------------------------------------- /packages/lint-worker/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["vitest.config.mts"], 4 | "compilerOptions": { 5 | "noEmit": true, 6 | "declaration": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/query-tools/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["vitest.config.mts"], 4 | "compilerOptions": { 5 | "noEmit": true, 6 | "declaration": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/language-support/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["vitest.config.mts"], 4 | "compilerOptions": { 5 | "noEmit": true, 6 | "declaration": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/react-codemirror-playground/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from 'react-dom/client'; 2 | import { App } from './App'; 3 | import './index.css'; 4 | 5 | createRoot(document.getElementById('root')).render(); 6 | -------------------------------------------------------------------------------- /packages/react-codemirror-playground/vite.config.ts: -------------------------------------------------------------------------------- 1 | import react from '@vitejs/plugin-react'; 2 | import { defineConfig } from 'vite'; 3 | 4 | export default defineConfig({ 5 | plugins: [react()], 6 | base: './', 7 | }); 8 | -------------------------------------------------------------------------------- /.changeset/fifty-zebras-grow.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-support': patch 3 | '@neo4j-cypher/react-codemirror': patch 4 | '@neo4j-cypher/language-server': patch 5 | --- 6 | 7 | Automatically opens autocompletions after "YIELD " 8 | -------------------------------------------------------------------------------- /packages/language-support/src/antlr-grammar/CypherPreLexer.g4: -------------------------------------------------------------------------------- 1 | lexer grammar CypherPreLexer; 2 | 3 | import Cypher25Lexer; 4 | 5 | EXPLAIN: 6 | E X P L A I N; 7 | 8 | PROFILE: 9 | P R O F I L E; 10 | 11 | CYPHER: 12 | C Y P H E R; -------------------------------------------------------------------------------- /packages/react-codemirror-playground/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | darkMode: 'class', 4 | content: ['index.html', './src/**/*.tsx'], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | }; 10 | -------------------------------------------------------------------------------- /vendor/antlr4-c3/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is released under the MIT license. 3 | * Copyright (c) 2016, 2021 Mike Lischke 4 | * 5 | * See LICENSE file for more info. 6 | */ 7 | 8 | export * from "./src/CodeCompletionCore"; 9 | export * from "./src/antlr4"; 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | out/ 2 | dist/ 3 | esm/ 4 | packages/language-support/src/generated-parser/* 5 | semanticAnalysis.js 6 | vendor/* 7 | node_modules/* 8 | packages/vscode-extension/tests/fixtures/textmate/simple-match.js 9 | packages/react-codemirror/playwright/index.tsx 10 | -------------------------------------------------------------------------------- /packages/vscode-extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["src", "tests", "test"], 4 | "compilerOptions": { 5 | "outDir": "dist", 6 | "jsx": "react-jsx", 7 | "types": ["@wdio/globals/types"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/react-codemirror-playground/src/viteEnv.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module 'vite-plugin-node-stdlib-browser' { 4 | import { PluginOption } from 'vite'; 5 | function nodePolyfills(): PluginOption[]; 6 | export default nodePolyfills; 7 | } 8 | -------------------------------------------------------------------------------- /packages/react-codemirror/src/index.ts: -------------------------------------------------------------------------------- 1 | export * as LanguageSupport from '@neo4j-cypher/language-support'; 2 | export { CypherEditor } from './CypherEditor'; 3 | export { cypher } from './lang-cypher/langCypher'; 4 | export { darkThemeConstants, lightThemeConstants } from './themes'; 5 | -------------------------------------------------------------------------------- /.changeset/pink-chefs-trade.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/react-codemirror-playground': patch 3 | '@neo4j-cypher/language-support': patch 4 | '@neo4j-cypher/react-codemirror': patch 5 | '@neo4j-cypher/language-server': patch 6 | --- 7 | 8 | Moves semantic analysis to a separate worker file 9 | -------------------------------------------------------------------------------- /packages/query-tools/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["src", "package.json", "vitest.config.mts"], 4 | "compilerOptions": { 5 | "strict": true, 6 | "resolveJsonModule": true, 7 | "types": ["vitest/globals"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/styles/queryDetails.css: -------------------------------------------------------------------------------- 1 | @import './components/collapsible.css'; 2 | 3 | body { 4 | background-color: rgb(var(--theme-palette-neutral-bg-default)); 5 | color: rgb(var(--theme-palette-neutral-text-default)); 6 | font-size: var(--vscode-editor-font-size); 7 | } 8 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/styles/queryVisualization.css: -------------------------------------------------------------------------------- 1 | @import './components/vizWrapper.css'; 2 | 3 | body { 4 | background-color: rgb(var(--theme-palette-neutral-bg-default)); 5 | color: rgb(var(--theme-palette-neutral-text-default)); 6 | font-size: var(--vscode-editor-font-size); 7 | } 8 | -------------------------------------------------------------------------------- /editor-plugin/intellij/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 02 15:39:48 BST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /packages/react-codemirror-playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["src"], 4 | "compilerOptions": { 5 | "noEmit": true, 6 | "jsx": "react-jsx", 7 | "lib": ["DOM"], 8 | "esModuleInterop": true, 9 | "module": "ES2020" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/react-codemirror/src/lang-cypher/utils.ts: -------------------------------------------------------------------------------- 1 | import { MarkupContent } from 'vscode-languageserver-types'; 2 | 3 | export function getDocString(result: string | MarkupContent): string { 4 | if (MarkupContent.is(result)) { 5 | result.value; 6 | } else { 7 | return result; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/react-codemirror/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["src"], 4 | "exclude": ["**/lintWorker.mjs"], 5 | "compilerOptions": { 6 | "jsx": "react-jsx", 7 | "allowJs": true, 8 | "module": "esnext", 9 | "outDir": "dist" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/specs/webviews/setup.spec.ts: -------------------------------------------------------------------------------- 1 | import { before } from 'mocha'; 2 | import { createNewConnection } from '../../webviewUtils'; 3 | 4 | before(async () => { 5 | await createNewConnection('vscode-webview-tests-1'); 6 | await createNewConnection('vscode-webview-tests-2'); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/vscode-extension/genTextMate.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const languageSupport = require('@neo4j-cypher/language-support'); 3 | const textMateGrammar = languageSupport.textMateGrammar; 4 | 5 | fs.writeFileSync( 6 | './syntaxes/cypher.json', 7 | JSON.stringify(textMateGrammar, undefined, 2), 8 | ); 9 | -------------------------------------------------------------------------------- /editor-plugin/intellij/README.md: -------------------------------------------------------------------------------- 1 | # Neo4j Cypher LSP support for IntelliJ 2 | 3 | ## Build and run 4 | 5 | Prerequisite: Java 21 6 | 7 | - Build the plugin via `./gradlew buildPlugin`. 8 | - Choose the plugin's zip archive from `build/distributions/cypher-lsp-support-.zip` when installing it manually in IntelliJ 9 | -------------------------------------------------------------------------------- /.changeset/stupid-coins-itch.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/react-codemirror-playground': patch 3 | '@neo4j-cypher/language-support': patch 4 | '@neo4j-cypher/react-codemirror': patch 5 | '@neo4j-cypher/language-server': patch 6 | '@neo4j-cypher/query-tools': patch 7 | --- 8 | 9 | restructure packages as part of move to pnpm 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | build 3 | dist 4 | esm 5 | node_modules 6 | generated-parser 7 | generated 8 | .vscode-test 9 | *.DS_Store 10 | *.interp 11 | *.tokens 12 | *.vsix 13 | .turbo 14 | *.antlr 15 | *.jar 16 | *.clinic 17 | *.tsbuildinfo 18 | *.npmrc 19 | benchmarks.txt 20 | .idea/ 21 | vite.config.ts.timestamp* 22 | .wdio-vscode-service -------------------------------------------------------------------------------- /packages/query-tools/src/data-transforms/format-float.ts: -------------------------------------------------------------------------------- 1 | export const formatFloat = (anything: number) => { 2 | if ([Infinity, -Infinity, NaN].includes(anything)) { 3 | return `${anything}`; 4 | } 5 | if (Math.floor(anything) === anything) { 6 | return `${anything}.0`; 7 | } 8 | return anything.toString(); 9 | }; 10 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/comments.md: -------------------------------------------------------------------------------- 1 | # Comments test 2 | 3 | ```cypher 4 | /* This is a 5 | multiline comment 6 | */ 7 | MATCH (u1:CommerceUser), (u2: CommerceUser) 8 | WHERE u1 <> u2 9 | // This is a single line comment 10 | RETURN relsDate, nodes 11 | /* Another multiline 12 | comment */ 13 | LIMIT $limit 14 | ``` 15 | -------------------------------------------------------------------------------- /vendor/antlr4-c3/.npmignore: -------------------------------------------------------------------------------- 1 | # Node generated files 2 | node_modules 3 | npm-debug.log 4 | 5 | # OS generated files 6 | Thumbs.db 7 | .DS_Store 8 | 9 | # Ignored files 10 | lib/test/* 11 | tests/ 12 | ports/ 13 | package 14 | *.tgz 15 | .vscode/ 16 | .eslintrc.json 17 | .github/ 18 | .git/ 19 | cspell.json 20 | tsconfig.json 21 | index.ts 22 | -------------------------------------------------------------------------------- /packages/react-codemirror/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true, 7 | "esModuleInterop": true, 8 | "allowJs": true 9 | }, 10 | "include": ["*.config.ts", "*.config.js", "vite-env.d.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "public", 8 | "baseBranch": "main", 9 | "ignore": ["neo4j-for-vscode"], 10 | "updateInternalDependencies": "patch" 11 | } 12 | -------------------------------------------------------------------------------- /packages/vscode-extension/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "./esbuild-extension.mts", 4 | "./genTextMate.js", 5 | "./tests/fixtures/textmate/simple-match.js" 6 | ], 7 | "compilerOptions": { 8 | "module": "ESNext", 9 | "target": "ESNext", 10 | "moduleResolution": "bundler", 11 | "noEmit": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/language-support/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["src", "../../vendor/antlr4-c3/dist/esm"], 4 | "exclude": ["**/semanticAnalysis.js"], 5 | "compilerOptions": { 6 | "outDir": "dist", 7 | "declaration": true, 8 | "esModuleInterop": true, 9 | "types": ["vitest/globals"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/lint-worker/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["src/"], 4 | "exclude": [], 5 | "compilerOptions": { 6 | "outDir": "./dist", 7 | "declaration": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "node", 11 | "types": ["vitest/globals"] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/react-codemirror-playground/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true, 7 | "esModuleInterop": true, 8 | "allowJs": true 9 | }, 10 | "include": ["*.config.ts", "*.config.js", "src/vite-env.d.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/functions.md: -------------------------------------------------------------------------------- 1 | # Functions test 2 | 3 | ```cypher 4 | RETURN apoc.agg.first(apoc.agg.first([1,3,5])) 5 | 6 | CALL some.procedure(function(), other()) 7 | 8 | RETURN apoc.coll.elements() 9 | RETURN apoc . coll . elements() 10 | RETURN `apoc` . coll . `elements`() 11 | RETURN `apoc.coll.elements`() 12 | ``` 13 | -------------------------------------------------------------------------------- /packages/lint-worker/README.md: -------------------------------------------------------------------------------- 1 | # Lint Worker 2 | 3 | This package contains the Cypher lint worker used in both the language server and react-codemirror. For compatability with the vscode API it is compiled to CommonJS and for react-codemirror it is compiled to ESM. 4 | 5 | Since the TeaVM 0.13.0 update, the bundle minification is broken for older versions of esbuild. >= 0.27.0 works -------------------------------------------------------------------------------- /editor-plugin/intellij/src/main/java/org/neo4j/intellij/lsp/language/CypherLanguage.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.intellij.lsp.language; 2 | 3 | import com.intellij.lang.Language; 4 | 5 | public class CypherLanguage extends Language { 6 | 7 | public static final CypherLanguage INSTANCE = new CypherLanguage(); 8 | private CypherLanguage() { 9 | super("Cypher"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /vendor/antlr4-c3/cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2", 3 | "words": [ 4 | "ANTLR", 5 | "Struct", 6 | "structs", 7 | "whitespaces" 8 | ], 9 | "ignoreWords": [ 10 | "Xexact", 11 | "philipps", 12 | "unmocked" 13 | ], 14 | "ignorePaths": [ 15 | ".eslintrc.json", 16 | "package.json" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/literals.md: -------------------------------------------------------------------------------- 1 | # Literals test 2 | 3 | ```cypher 4 | // At some point it was colouring the () inside strings, 5 | // which it shouldn't hence why those are included in here 6 | RETURN "double quoted string ()" AS a, 7 | 'single quoted string ()' AS b; 8 | 9 | 10 | RETURN 5 + 12345 + 3.123 + .123 ; 11 | RETURN TRUE, false, true, false; 12 | ``` 13 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/parameters.md: -------------------------------------------------------------------------------- 1 | # Parameters test 2 | 3 | ```cypher 4 | MATCH 5 | // Test with and without spaces 6 | // This is here because at some point param maps inside labels where not picked up correctly 7 | (n:Label {one : $param1, two: $param2, three:$param3}), 8 | (m:Label {one: $param1}) 9 | RETURN $param1, $param2, n, m, {key:$value} 10 | ``` 11 | -------------------------------------------------------------------------------- /.changeset/fuzzy-rice-train.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@neo4j-cypher/language-server': major 3 | '@neo4j-cypher/language-support': major 4 | '@neo4j-cypher/react-codemirror': major 5 | '@neo4j-cypher/react-codemirror-playground': major 6 | '@neo4j-cypher/query-tools': major 7 | --- 8 | 9 | First alpha release of the new Neo4j's Cypher Language Support, including syntax highlighting, auto-completion and linting as features 10 | -------------------------------------------------------------------------------- /packages/query-tools/README.md: -------------------------------------------------------------------------------- 1 | # Neo4j Query Tools 2 | 3 | This package contains utilities for interacting with Neo4j databases. Main export at this stage is the `SchemaPoller` class, which is used to keep the schema (procedure names, labels, database names, etc.) up to date in the language server. 4 | 5 | ## Disclaimer 6 | 7 | Not ready for external useage yet, no guarantees are made about the stability of the API. 8 | -------------------------------------------------------------------------------- /packages/react-codemirror/playwright/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Testing Page 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/labels.md: -------------------------------------------------------------------------------- 1 | # Labels test 2 | 3 | ```cypher 4 | MATCH 5 | (n:Label), 6 | (m:Label1&Label2), (k:(Label1 | (Label2&Label3 & !Label4))); 7 | 8 | MATCH (n:(AAA & (BBB & CCC) | AAA), (m: Label1&Label2), (k:Label1|Label2&Label3); 9 | 10 | MATCH (n:(`AAA` & (`BBB & CCC`) | AAA)), (m: (AAA | (BBB & CCC) | DDD)) RETURN n 11 | 12 | MATCH (n: AAA : BBB : CCC) 13 | 14 | ``` 15 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/styles/reset.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | font-size: 13px; 4 | } 5 | 6 | *, 7 | *:before, 8 | *:after { 9 | box-sizing: inherit; 10 | } 11 | 12 | body, 13 | h1, 14 | h2, 15 | h3, 16 | h4, 17 | h5, 18 | h6, 19 | p, 20 | ol, 21 | ul { 22 | margin: 0; 23 | padding: 0; 24 | font-weight: normal; 25 | } 26 | 27 | img { 28 | max-width: 100%; 29 | height: auto; 30 | } 31 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/properties.md: -------------------------------------------------------------------------------- 1 | # Properties test 2 | 3 | ```cypher 4 | MATCH p = (u1)-[]->(u2) 5 | WHERE u1.foo.bar = 5 AND u2.foo = TRUE 6 | WITH 7 | reduce(output = [], n IN relationships(p) | output + n.create_time.epochMillis ) as relsDate 8 | 9 | // Note we want the .nodes and the .reduce to be coloured 10 | // as variables, not keywords 11 | RETURN relsDate, u1.nodes.reduce 12 | ``` 13 | -------------------------------------------------------------------------------- /editor-plugin/intellij/src/main/java/org/neo4j/intellij/lsp/language/CypherIcons.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.intellij.lsp.language; 2 | 3 | import com.intellij.openapi.util.IconLoader; 4 | 5 | import javax.swing.*; 6 | 7 | public class CypherIcons { 8 | /* File icon - 16x16 pixels - displayed next to the file name on the file tab */ 9 | public static final Icon FILE = IconLoader.getIcon("META-INF/fileIcon.svg", CypherIcons.class); 10 | } 11 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/syntax-highlighting.cypher: -------------------------------------------------------------------------------- 1 | MATCH (u1:CommerceUser{user_id:$seller_id}), 2 | (u2:CommerceUser{user_id:$customer_id}), 3 | p = allShortestPaths((u1)-[:PURCHASE*..{{depth}}]->(u2)) 4 | WHERE u1 <> u2 5 | WITH 6 | reduce(output = [], n IN relationships(p) | output + n.create_time.epochMillis ) as relsDate, 7 | reduce(output = [], n IN nodes(p) | output + n ) as nodes 8 | 9 | RETURN relsDate, nodes 10 | LIMIT $limit -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/simple-match.fs: -------------------------------------------------------------------------------- 1 | let a = "//cypher 2 | // This is a comment 3 | MATCH (n:Label) RETURN function(n.property) 4 | " 5 | 6 | let b = "/* cypher */ 7 | // This is a comment 8 | MATCH (n:Label) RETURN function(n.property) 9 | " 10 | 11 | let c = "/*cypher*/ MATCH (n:Label) RETURN function(n.property)" 12 | 13 | // This one shouldn't highglight 14 | let d = "//cypher MATCH (n:Label) RETURN function(n.property)" -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "allowJs": false, 5 | "declaration": true, 6 | "esModuleInterop": true, 7 | "lib": ["dom", "ESNext"], 8 | "module": "commonjs", 9 | "moduleResolution": "node", 10 | "skipLibCheck": true, 11 | "sourceMap": true, 12 | "target": "ESNext", 13 | "outDir": "dist", 14 | "incremental": true 15 | }, 16 | "exclude": ["node_modules", ".vscode-test"] 17 | } 18 | -------------------------------------------------------------------------------- /packages/vscode-extension/.vscodeignore: -------------------------------------------------------------------------------- 1 | # Ignore everything, https://github.com/microsoft/vscode-vsce/issues/12 2 | ** 3 | # Required for the ignore everything rule to work from testing 4 | ../../ 5 | ../ 6 | 7 | # Unignore only required files, readme/package.json automatically included 8 | !LICENSE.md 9 | !resources/ 10 | !dist/extension.js 11 | !dist/lintWorker.cjs 12 | !dist/cypher-language-server.js 13 | !dist/webviews/ 14 | !cypher-language-configuration.json 15 | !syntaxes/ -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/simple-match.cs: -------------------------------------------------------------------------------- 1 | var a = """//cypher 2 | // This is a comment 3 | MATCH (n:Label) RETURN function(n.property) 4 | """ 5 | 6 | var b = """/* cypher */ 7 | // This is a comment 8 | MATCH (n:Label) RETURN function(n.property) 9 | """ 10 | 11 | var c = "/*cypher*/ MATCH (n:Label) RETURN function(n.property)" 12 | 13 | // This one shouldn't highglight 14 | var d = "//cypher MATCH (n:Label) RETURN function(n.property)" -------------------------------------------------------------------------------- /packages/query-tools/src/data-transforms/clean-type.ts: -------------------------------------------------------------------------------- 1 | export function cleanType(type: string): string { 2 | let resultType = type; 3 | 4 | if (resultType.startsWith('LIST? OF ')) { 5 | resultType = resultType.replace('LIST? OF ', 'LIST<'); 6 | resultType += '>'; 7 | } 8 | return resultType.replace(/\?/g, ''); 9 | } 10 | 11 | export function cleanTypeDescription(arg: T) { 12 | return { ...arg, type: cleanType(arg.type) }; 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsc.autoDetect": "off", 3 | "typescript.preferences.quoteStyle": "single", 4 | "editor.codeActionsOnSave": { 5 | "source.fixAll.eslint": "explicit" 6 | }, 7 | "editor.defaultFormatter": "esbenp.prettier-vscode", 8 | "editor.formatOnSave": true, 9 | "[javascript]": { 10 | "editor.defaultFormatter": "esbenp.prettier-vscode" 11 | }, 12 | "playwright.env": { 13 | "NODE_OPTIONS": "--max_old_space_size=8192" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/simple-match.java: -------------------------------------------------------------------------------- 1 | String a = """//cypher 2 | // This is a comment 3 | MATCH (n:Label) RETURN function(n.property) 4 | """ 5 | 6 | String b = """/* cypher */ 7 | // This is a comment 8 | MATCH (n:Label) RETURN function(n.property) 9 | """ 10 | 11 | String c = "/*cypher*/ MATCH (n:Label) RETURN function(n.property)" 12 | 13 | // This one shouldn't highglight 14 | String d = "//cypher MATCH (n:Label) RETURN function(n.property)" -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/simple-match.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var a = `//cypher 4 | // This is a comment 5 | MATCH (n:Label) RETURN function(n.property) 6 | ` 7 | 8 | var b = `/* cypher */ 9 | // This is a comment 10 | MATCH (n:Label) RETURN function(n.property) 11 | ` 12 | 13 | var c = "/*cypher*/ MATCH (n:Label) RETURN function(n.property)" 14 | 15 | // This one shouldn't highglight 16 | var d = "//cypher MATCH (n:Label) RETURN function(n.property)" 17 | -------------------------------------------------------------------------------- /packages/language-support/src/antlr-grammar/CypherCmdLexer.g4: -------------------------------------------------------------------------------- 1 | lexer grammar CypherCmdLexer; 2 | 3 | import CypherPreLexer; 4 | 5 | PARAM: P A R A M S?; 6 | 7 | CLEAR: C L E A R; 8 | 9 | HISTORY: H I S T O R Y; 10 | 11 | CONNECT: C O N N E C T; 12 | 13 | DISCONNECT: D I S C O N N E C T; 14 | 15 | WELCOME: W E L C O M E; 16 | 17 | SYSINFO: S Y S I N F O; 18 | 19 | STYLE: S T Y L E; 20 | 21 | RESET: R E S E T; 22 | 23 | PLAY: P L A Y; 24 | 25 | ACCESSMODE: A C C E S S '-' M O D E; 26 | 27 | HELP: H E L P; -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - packages/* 3 | - vendor/* 4 | catalog: 5 | neo4j-driver: 6.0.1 6 | 7 | minimumReleaseAge: 4320 8 | minimumReleaseAgeExclude: 9 | # all neo4j-ndl and @neo4j-nvl have a minumum release age so we can exclude them here 10 | - '@neo4j-ndl/base' 11 | - '@neo4j-ndl/react' 12 | - '@neo4j-ndl/react-charts' 13 | - '@neo4j-ndl/react-graph' 14 | - '@neo4j-nvl/base' 15 | - '@neo4j-nvl/interaction-handlers' 16 | - '@neo4j-nvl/layout-workers' 17 | - '@neo4j-nvl/react' 18 | -------------------------------------------------------------------------------- /packages/react-codemirror/src/ndlTokensCopy.test.ts: -------------------------------------------------------------------------------- 1 | import { tokens } from '@neo4j-ndl/base'; 2 | import { expect, test } from 'vitest'; 3 | import { tokens as tokensCopy } from './ndlTokensCopy'; 4 | 5 | /* 6 | * Needle has some odd package configuration that made playwright stop working 7 | * so for now we inline a copy of the tokens we need. Use this test to make 8 | * sure the tokens are still up to date. 9 | */ 10 | test('copy of tokens is up to date', () => { 11 | expect(tokens).toEqual(tokensCopy); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/vscode-extension/cypher-language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "//", 4 | "blockComment": ["/*", "*/"] 5 | }, 6 | "brackets": [ 7 | ["{", "}"], 8 | ["[", "]"], 9 | ["(", ")"] 10 | ], 11 | "autoClosingPairs": [ 12 | { "open": "{", "close": "}" }, 13 | { "open": "[", "close": "]" }, 14 | { "open": "(", "close": ")" }, 15 | { "open": "'", "close": "'" }, 16 | { "open": "\"", "close": "\"" }, 17 | { "open": "`", "close": "`" } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/procedures.md: -------------------------------------------------------------------------------- 1 | # Procedures test 2 | 3 | ```cypher 4 | CALL apoc.periodic.iterate( 5 | "MATCH (p:Person) RETURN p", 6 | // Extract `p` variable using list comprehension 7 | "CALL apoc.nodes.delete([item in $_batch | item.p], size($_batch))", 8 | {batchMode: "BATCH_SINGLE", batchSize: 100} 9 | ) 10 | YIELD batch, operations; 11 | 12 | CALL apoc.coll.elements() 13 | CALL apoc . coll . elements() 14 | CALL `apoc` . coll . `elements`() 15 | CALL `apoc.coll.elements`() 16 | ``` 17 | -------------------------------------------------------------------------------- /packages/react-codemirror-playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | Codemirror Playground 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/getNonce.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Non crypto secure random alphanumeric generator 3 | * @param length Optional length of the nonce to generate. Default is 32. 4 | * @returns A random alphanumeric string. 5 | */ 6 | export function getNonce(length: number = 32): string { 7 | let text = ''; 8 | const possible = 9 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 10 | for (let i = 0; i < length; i++) { 11 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 12 | } 13 | return text; 14 | } 15 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/call-subquery.md: -------------------------------------------------------------------------------- 1 | # Call subquery tests 2 | 3 | ```cypher 4 | CALL { 5 | CALL { 6 | UNWIND range(1,4) AS direction 7 | MATCH p = (start:Cell)(()-[r WHERE r.direction = direction]->()){4}(end) 8 | OPTIONAL MATCH (before_start)-[r0]->(start) 9 | WHERE r0.direction = direction 10 | OPTIONAL MATCH (end)-[r5]->(after_end) 11 | WHERE r5.direction = direction 12 | RETURN p, before_start, start, end, after_end, direction 13 | } // -- all valid paths 14 | } 15 | ``` 16 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/specs/api/setup.spec.ts: -------------------------------------------------------------------------------- 1 | import { before } from 'mocha'; 2 | import * as vscode from 'vscode'; 3 | import { createTestDatabase, saveDefaultConnection } from '../../suiteSetup'; 4 | 5 | before(async () => { 6 | await saveDefaultConnection({ version: 'neo4j 5' }); 7 | await saveDefaultConnection({ version: 'neo4j 2025' }); 8 | await createTestDatabase({ version: 'neo4j 2025' }); 9 | await vscode.commands.executeCommand('workbench.action.closeAllEditors'); 10 | await vscode.commands.executeCommand('neo4j.clearParameters'); 11 | }); 12 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "build", 7 | "group": "build", 8 | "presentation": { 9 | "panel": "dedicated", 10 | "reveal": "never" 11 | }, 12 | "problemMatcher": ["$tsc"] 13 | }, 14 | { 15 | "type": "npm", 16 | "script": "dev-vscode", 17 | "group": "build", 18 | "problemMatcher": "$tsc-watch", 19 | "isBackground": true, 20 | "label": "npm: dev-vscode", 21 | "detail": "pnpm dev-vscode" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/ParameterDark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/ParameterLight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/helpers.ts: -------------------------------------------------------------------------------- 1 | import { DbSchema, lintCypherQuery } from '@neo4j-cypher/language-support'; 2 | 3 | export function validateParamInput( 4 | paramValue: string, 5 | dbSchema: DbSchema, 6 | ): string | undefined { 7 | const diagnostics = lintCypherQuery( 8 | `RETURN ${paramValue}`, 9 | dbSchema, 10 | true, 11 | ).diagnostics; 12 | const errors = diagnostics.filter((d) => d.severity === 1); 13 | if (errors.length > 0) { 14 | return ( 15 | 'Value cannot be evaluated: ' + errors.map((e) => e.message).join('. ') 16 | ); 17 | } 18 | return undefined; 19 | } 20 | -------------------------------------------------------------------------------- /packages/vscode-extension/esbuild-extension.mts: -------------------------------------------------------------------------------- 1 | import * as esbuild from 'esbuild'; 2 | const production = process.argv.includes('--production'); 3 | 4 | const ctx = await esbuild.context({ 5 | entryPoints: ['src/extension.ts'], 6 | bundle: true, 7 | format: 'cjs', 8 | minify: true, 9 | sourcemap: !production, 10 | sourcesContent: false, 11 | platform: 'node', 12 | outfile: 'dist/extension.js', 13 | external: ['vscode'], 14 | logLevel: 'info', 15 | conditions: ['require'], 16 | }); 17 | 18 | if (production) { 19 | await ctx.rebuild(); 20 | await ctx.dispose(); 21 | } else { 22 | await ctx.watch(); 23 | } 24 | -------------------------------------------------------------------------------- /packages/language-support/src/tests/syntaxValidation/helpers.ts: -------------------------------------------------------------------------------- 1 | import { DbSchema } from '../../dbSchema'; 2 | import { lintCypherQuery } from '../../syntaxValidation/syntaxValidation'; 3 | 4 | type SyntaxValidationTestArgs = { 5 | query: string; 6 | dbSchema?: DbSchema; 7 | }; 8 | 9 | export function getDiagnosticsForQuery({ 10 | query, 11 | dbSchema = {}, 12 | }: SyntaxValidationTestArgs) { 13 | return lintCypherQuery(query, dbSchema).diagnostics; 14 | } 15 | 16 | export function getSymbolTablesForQuery({ 17 | query, 18 | dbSchema = {}, 19 | }: SyntaxValidationTestArgs) { 20 | return lintCypherQuery(query, dbSchema).symbolTables; 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/benchmark.yaml: -------------------------------------------------------------------------------- 1 | name: Push benchmarks to grafana 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | benchmark: 9 | name: Time benchmark 10 | environment: grafana-api-key 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Setup and build project 17 | uses: ./.github/actions/setup-and-build 18 | 19 | - name: Install Playwright Browsers 20 | run: pnpm exec playwright install --only-shell --with-deps 21 | working-directory: packages/react-codemirror 22 | 23 | - name: Benchmark 24 | run: pnpm benchmark --concurrency=1 25 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/complex-match.md: -------------------------------------------------------------------------------- 1 | # Complex match test 2 | 3 | ```cypher 4 | /* This is a 5 | multiline comment 6 | */ 7 | MATCH (u1:CommerceUser{user_id:$seller_id}), 8 | (u2:CommerceUser{user_id:$customer_id}), 9 | p = allShortestPaths((u1)-[:PURCHASE*..10]->(u2)) 10 | WHERE u1 <> u2 11 | WITH 12 | // This is a single line comment 13 | reduce(output = [], n IN relationships(p) | output + n.create_time.epochMillis ) as relsDate, 14 | reduce(output = [], n IN nodes(p) | output + n ) as nodes, 15 | "double quoted string" as a, 16 | 'single quoted string' as b 17 | RETURN relsDate, nodes 18 | LIMIT $limit 19 | ``` 20 | -------------------------------------------------------------------------------- /.github/workflows/formatting-integrity-check.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | paths: 6 | - 'packages/language-support/src/formatting/**' 7 | 8 | pull_request: 9 | branches: 10 | - main 11 | paths: 12 | - 'packages/language-support/src/formatting/**' 13 | 14 | jobs: 15 | formatting-integrity-check: 16 | name: Formatter integrity check 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Setup and build project 22 | uses: ./.github/actions/setup-and-build 23 | 24 | - name: Run formatting check 25 | run: pnpm test:formattingIntegrity 26 | -------------------------------------------------------------------------------- /packages/react-codemirror/vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import react from '@vitejs/plugin-react'; 3 | import { defineConfig } from 'vite'; 4 | 5 | export default defineConfig({ 6 | plugins: [react()], 7 | // todo investigate if common js is back on the menu 8 | build: { lib: { entry: 'src/index.ts', formats: ['es'] } }, 9 | test: { 10 | exclude: [ 11 | '**/node_modules/**', 12 | '**/dist/**', 13 | '**/.{idea,git,cache,output,temp}/**', 14 | '**/e2e_tests/**', 15 | ], 16 | // Fix for error in pipeline, see https://github.com/vitest-dev/vitest/discussions/6131 17 | minWorkers: 1, 18 | maxWorkers: 1, 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /vendor/antlr4-c3/tests/readme.md: -------------------------------------------------------------------------------- 1 | antlr4-c3 unit tests 2 | -------------------- 3 | This folder contains the source code and some support files of unit tests for the code completion core. There are 2 grammars from which parsers + lexer were generated. One grammar is a very simple expression grammar, while the other one (CPP14.g4) has been downloaded from the [ANTLR4 grammar directory](https://github.com/antlr/grammars-v4/blob/master/cpp/CPP14.g4). 4 | 5 | You can easily regenerate the parser and lexers by running: 6 | 7 | ```bash 8 | pnpm run-script generate 9 | ``` 10 | 11 | from the root folder of the module. It requires the `antlr4ts` node module, which is installed when you run `pnpm install` first. 12 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/mocks/inMemorySecretStorage.ts: -------------------------------------------------------------------------------- 1 | import { Event, SecretStorage, SecretStorageChangeEvent } from 'vscode'; 2 | 3 | export class InMemorySecretStorage implements SecretStorage { 4 | private _storage: { [keyName: string]: string } = {}; 5 | 6 | get(key: string): Thenable { 7 | return Promise.resolve(this._storage[key]); 8 | } 9 | 10 | store(key: string, value: string): Thenable { 11 | this._storage[key] = value; 12 | return Promise.resolve(); 13 | } 14 | 15 | delete(key: string): Thenable { 16 | this._storage[key] = undefined; 17 | return Promise.resolve(); 18 | } 19 | 20 | onDidChange: Event; 21 | } 22 | -------------------------------------------------------------------------------- /packages/language-support/src/dbSchema.ts: -------------------------------------------------------------------------------- 1 | import { CypherVersion, Neo4jFunction, Neo4jProcedure } from './types'; 2 | 3 | export type Registry = Record; 4 | type ScopedRegistry = Partial>>; 5 | 6 | export interface DbSchema { 7 | labels?: string[]; 8 | relationshipTypes?: string[]; 9 | databaseNames?: string[]; 10 | aliasNames?: string[]; 11 | userNames?: string[]; 12 | roleNames?: string[]; 13 | parameters?: Record; 14 | propertyKeys?: string[]; 15 | procedures?: ScopedRegistry; 16 | functions?: ScopedRegistry; 17 | defaultLanguage?: CypherVersion; 18 | graphSchema?: { from: string; to: string; relType: string }[]; 19 | } 20 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/specs/api/formatting.spec.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import * as vscode from 'vscode'; 3 | import { newUntitledFileWithContent } from '../../helpers'; 4 | 5 | suite('Formatting', () => { 6 | test('tests that formatting document works', async () => { 7 | const query = `match (p: Person) where p.name = "John Doe" reTUrn p lIMIt 25`; 8 | const document = await newUntitledFileWithContent(query); 9 | await vscode.commands.executeCommand('editor.action.formatDocument'); 10 | const formattedText = document.getText(); 11 | const expected = `MATCH (p:Person) 12 | WHERE p.name = "John Doe" 13 | RETURN p 14 | LIMIT 25`; 15 | assert.equal(formattedText, expected); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/language-support/src/featureFlags.ts: -------------------------------------------------------------------------------- 1 | interface FeatureFlags { 2 | consoleCommands: boolean; 3 | debugSymbolTable: boolean; 4 | } 5 | 6 | export const _internalFeatureFlags: FeatureFlags = { 7 | /* 8 | Because the parserWrapper is done as a single-ton global variable, the setting for 9 | console commands was also easiest to do as a global variable as it avoid messing with the cache 10 | 11 | It would make sense for the client to initialize and own the ParserWrapper, then each editor can have 12 | it's own cache and preference on if console commands are enabled or not. 13 | */ 14 | consoleCommands: false, 15 | debugSymbolTable: 16 | typeof process === 'undefined' 17 | ? false 18 | : process.env.debugSymbolTable == 'true', 19 | }; 20 | -------------------------------------------------------------------------------- /vendor/antlr4-c3/tests/Whitebox.g4: -------------------------------------------------------------------------------- 1 | grammar Whitebox; 2 | 3 | @eader { 4 | /* eslint-disable @typescript-eslint/no-unused-vars, no-useless-escape */ 5 | } 6 | 7 | test1: rule1 ADIPISCING ; 8 | rule1: rule2 CONSECTETUR ; 9 | rule2: LOREM rule3 rule5 SIT* AMET? ; 10 | rule3: rule4 DOLOR? ; 11 | rule4: IPSUM? ; 12 | rule5: ; 13 | 14 | test2: rule7 ADIPISCING ; 15 | rule7: rule8 CONSECTETUR ; 16 | rule8: LOREM rule11 rule9 SIT* AMET? ; 17 | rule9: rule10 DOLOR? ; 18 | rule10: IPSUM? ; 19 | rule11: ; 20 | 21 | test3: LOREM IPSUM? rule13 AMET+ CONSECTETUR ; 22 | rule13: (DOLOR | SIT)* ; 23 | 24 | LOREM: 'LOREM'; 25 | IPSUM: 'IPSUM'; 26 | DOLOR: 'DOLOR'; 27 | SIT: 'SIT'; 28 | AMET: 'AMET'; 29 | CONSECTETUR: 'CONSECTETUR'; 30 | ADIPISCING: 'ADIPISCING'; 31 | WS: [ \n\r\t] -> skip; 32 | -------------------------------------------------------------------------------- /packages/language-server/src/types.ts: -------------------------------------------------------------------------------- 1 | import { DbSchema } from '@neo4j-cypher/language-support'; 2 | 3 | // These settings are defined in the package.json 4 | export type Neo4jConnectionSettings = { 5 | connect?: boolean; 6 | user?: string; 7 | password?: string; 8 | connectURL?: string; 9 | database?: string; 10 | }; 11 | 12 | export type LintWorkerSettings = { 13 | lintWorkerPath: string; 14 | linterVersion: string; 15 | }; 16 | 17 | export type SymbolFetchingParams = { 18 | query: string; 19 | uri: string; 20 | schema: DbSchema; 21 | }; 22 | 23 | export type Neo4jSettings = { 24 | trace: { 25 | server: 'off' | 'messages' | 'verbose'; 26 | }; 27 | features: { linting: boolean }; 28 | }; 29 | 30 | export type Neo4jParameters = Record; 31 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/simple-match.py: -------------------------------------------------------------------------------- 1 | a = """//cypher 2 | // This is a comment 3 | MATCH (n:Label) RETURN function(n.property) 4 | """ 5 | 6 | b = """/* cypher */ 7 | // This is a comment 8 | MATCH (n:Label) RETURN function(n.property) 9 | """ 10 | 11 | d = '''//cypher 12 | // This is a comment 13 | MATCH (n:Label) RETURN function(n.property) 14 | ''' 15 | 16 | d = '''/* cypher */ 17 | // This is a comment 18 | MATCH (n:Label) RETURN function(n.property) 19 | ''' 20 | 21 | e = "/*cypher*/ MATCH (n:Label) RETURN n" 22 | 23 | f = '/*cypher*/ MATCH (n:Label) RETURN n' 24 | 25 | # This one shouldn't highlight 26 | g = "//cypher MATCH (n:Label) RETURN n" 27 | 28 | # This one shouldn't highlight 29 | h = '//cypher MATCH (n:Label) RETURN n' -------------------------------------------------------------------------------- /packages/language-support/src/antlr-grammar/CypherPreParser.g4: -------------------------------------------------------------------------------- 1 | parser grammar CypherPreParser; 2 | 3 | import Cypher25Parser; 4 | 5 | options { tokenVocab = CypherPreLexer; } 6 | 7 | preparsedStatement: 8 | preparserOption* statement; 9 | 10 | preparserKeyword: 11 | EXPLAIN | PROFILE | CYPHER; 12 | 13 | preparserOption: 14 | EXPLAIN | PROFILE | cypherOptions; 15 | 16 | cypher: 17 | CYPHER; 18 | 19 | cypherOptions: 20 | cypher cypherVersion? cypherOption*; 21 | 22 | cypherOption: 23 | cypherOptionName EQ cypherOptionValue; 24 | 25 | cypherOptionValue: 26 | (unescapedSymbolicNameString | numberLiteral); 27 | 28 | cypherOptionName: 29 | unescapedSymbolicNameString; 30 | 31 | cypherVersion: 32 | (EXTENDED_IDENTIFIER | UNSIGNED_DECIMAL_INTEGER | DOT | DECIMAL_DOUBLE)+; -------------------------------------------------------------------------------- /packages/vscode-extension/tests/mocks/inMemoryMemento.ts: -------------------------------------------------------------------------------- 1 | import { Memento } from 'vscode'; 2 | import { Connections } from '../../src/connectionService'; 3 | 4 | export class InMemoryMemento implements Memento { 5 | private _storage: { [keyName: string]: Connections } = {}; 6 | 7 | get(key: string): T | undefined; 8 | get(key: string, defaultValue: T): T; 9 | get(key: string, defaultValue?: null) { 10 | return this._storage[key] || defaultValue; 11 | } 12 | 13 | update(key: string, value: Connections): Thenable { 14 | this._storage[key] = value; 15 | return Promise.resolve(); 16 | } 17 | 18 | keys(): readonly string[] { 19 | return Object.keys(this._storage); 20 | } 21 | 22 | setKeysForSync(keys: string[]): void { 23 | keys; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /vendor/antlr4-c3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "incremental": true, 5 | "target": "ESNext", 6 | "esModuleInterop": true, 7 | "module": "ESNext", 8 | "moduleResolution": "node", 9 | "outDir": "out", 10 | "rootDir": ".", 11 | "removeComments": false, 12 | "noImplicitAny": true, 13 | "noImplicitOverride": false, 14 | "sourceMap": true, 15 | "inlineSources": true, 16 | "isolatedModules": false, 17 | "allowSyntheticDefaultImports": false, 18 | "strictNullChecks": false, 19 | "useDefineForClassFields": false, 20 | "skipLibCheck": true, 21 | "types": ["vitest/globals"] 22 | }, 23 | "compileOnSave": true, 24 | "include": ["src", "index.ts", "tests"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/archive-vscode-artifacts.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | push: 4 | branches: 5 | - main 6 | 7 | env: 8 | NODE_OPTIONS: '--max_old_space_size=8192' 9 | 10 | jobs: 11 | archive-vscode-artifacts: 12 | name: Archives VSCode artifacts 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Setup project 18 | uses: ./.github/actions/setup-and-build 19 | 20 | - name: Generate VSCode plugin package 21 | run: | 22 | cd packages/vscode-extension 23 | pnpm package 24 | 25 | - uses: actions/upload-artifact@v4 26 | if: always() 27 | with: 28 | name: vscode-extension 29 | path: packages/vscode-extension/*.vsix 30 | retention-days: 30 31 | -------------------------------------------------------------------------------- /editor-plugin/intellij/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | .intellijPlatform 19 | 20 | ### Eclipse ### 21 | .apt_generated 22 | .classpath 23 | .factorypath 24 | .project 25 | .settings 26 | .springBeans 27 | .sts4-cache 28 | bin/ 29 | !**/src/main/**/bin/ 30 | !**/src/test/**/bin/ 31 | 32 | ### NetBeans ### 33 | /nbproject/private/ 34 | /nbbuild/ 35 | /dist/ 36 | /nbdist/ 37 | /.nb-gradle/ 38 | 39 | ### VS Code ### 40 | .vscode/ 41 | 42 | ### Mac OS ### 43 | .DS_Store 44 | 45 | ### Javascript files 46 | *.js -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'eslint:recommended', 4 | 'prettier', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:@typescript-eslint/recommended-requiring-type-checking', 7 | ], 8 | env: { 9 | browser: true, 10 | es2022: true, 11 | node: true, 12 | }, 13 | parser: '@typescript-eslint/parser', 14 | parserOptions: { 15 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 16 | tsconfigRootDir: __dirname, 17 | project: ['./**/tsconfig.json', './**/tsconfig.node.json'], 18 | }, 19 | rules: { 20 | '@typescript-eslint/consistent-type-exports': 'error', 21 | 'no-console': ['warn', { allow: ['warn', 'error'] }], 22 | '@typescript-eslint/no-var-requires': 'off', 23 | '@typescript-eslint/unbound-method': 'off', 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /vendor/antlr4-c3/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *~ 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled output 28 | lib/ 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional REPL history 38 | .node_repl_history 39 | 40 | # Optional vscode-antlr4 directory if plugin enabled 41 | .antlr 42 | 43 | # Generated files 44 | /ports/java/target/ 45 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/ConnectedDark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/ConnectedLight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /vendor/antlr4-c3/tests/Expr.g4: -------------------------------------------------------------------------------- 1 | grammar Expr; 2 | 3 | @eader { 4 | /* eslint-disable @typescript-eslint/no-unused-vars, no-useless-escape */ 5 | } 6 | 7 | expression: assignment | simpleExpression; 8 | 9 | assignment: (VAR | LET) ID EQUAL simpleExpression; 10 | 11 | simpleExpression: 12 | simpleExpression (PLUS | MINUS) simpleExpression 13 | | simpleExpression (MULTIPLY | DIVIDE) simpleExpression 14 | | variableRef 15 | | functionRef 16 | ; 17 | 18 | variableRef: identifier; 19 | 20 | functionRef: identifier OPEN_PAR CLOSE_PAR; 21 | 22 | identifier: ID; 23 | 24 | VAR: [vV] [aA] [rR]; 25 | LET: [lL] [eE] [tT]; 26 | 27 | PLUS: '+'; 28 | MINUS: '-'; 29 | MULTIPLY: '*'; 30 | DIVIDE: '/'; 31 | EQUAL: '='; 32 | OPEN_PAR: '('; 33 | CLOSE_PAR: ')'; 34 | ID: [a-zA-Z] [a-zA-Z0-9_]*; 35 | WS: [ \n\r\t] -> channel(HIDDEN); 36 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/cypherRunner.ts: -------------------------------------------------------------------------------- 1 | import { 2 | parseParameters, 3 | parseStatementsStrs, 4 | } from '@neo4j-cypher/language-support'; 5 | import { addParameter } from './commandHandlers/params'; 6 | import { getDeserializedParams } from './parameterService'; 7 | 8 | export default class CypherRunner { 9 | constructor() {} 10 | 11 | async run( 12 | input: string, 13 | renderBottomPanel: (statements: string[]) => Promise, 14 | ) { 15 | const statements = parseStatementsStrs(input); 16 | const statementParams = parseParameters(input, false); 17 | const parameters = getDeserializedParams(); 18 | 19 | for (const param of statementParams) { 20 | if (parameters[param] === undefined) { 21 | await addParameter(param); 22 | } 23 | } 24 | 25 | await renderBottomPanel(statements); 26 | return; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // the empty files is so you can run `tsc -b` in the root without double linting (first root, then subprojects) 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { "path": "packages/language-support" }, 7 | { "path": "packages/react-codemirror/" }, 8 | { "path": "packages/react-codemirror/tsconfig.node.json" }, 9 | { "path": "packages/react-codemirror-playground/" }, 10 | { "path": "packages/react-codemirror-playground/tsconfig.node.json" }, 11 | { "path": "packages/language-server/" }, 12 | { "path": "packages/language-server/tsconfig.node.json" }, 13 | { "path": "packages/vscode-extension/" }, 14 | { "path": "packages/vscode-extension/tsconfig.node.json" }, 15 | { "path": "packages/query-tools/" } 16 | // { "path": "vendor/antlr4-c3" } -> this one does not inherit from the root tsconfig.json 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/react-codemirror-playground/src/treeUtil.ts: -------------------------------------------------------------------------------- 1 | interface SimpleTree { 2 | name: string; 3 | children?: SimpleTree[]; 4 | } 5 | 6 | import { 7 | antlrUtils, 8 | CypherParser, 9 | parse, 10 | ParserRuleContext, 11 | } from '@neo4j-cypher/language-support'; 12 | 13 | export function getDebugTree(cypher: string): SimpleTree { 14 | const statements = parse(cypher); 15 | 16 | function walk(node: ParserRuleContext): SimpleTree { 17 | const name = antlrUtils.tree.Trees.getNodeText( 18 | node, 19 | CypherParser.ruleNames, 20 | CypherParser, 21 | ); 22 | 23 | return { 24 | name: name, 25 | children: antlrUtils.tree.Trees.getChildren(node).map(walk), 26 | }; 27 | } 28 | 29 | const children = statements.map((statement) => walk(statement)); 30 | 31 | return { 32 | name: 'topNode', 33 | children: children, 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/treeviews/connectionTreeDecorationProvider.ts: -------------------------------------------------------------------------------- 1 | import { 2 | FileDecoration, 3 | FileDecorationProvider, 4 | ProviderResult, 5 | ThemeColor, 6 | Uri, 7 | } from 'vscode'; 8 | import { ConnectionItemType } from './connectionTreeDataProvider'; 9 | 10 | class ConnectionTreeDecorationProvider implements FileDecorationProvider { 11 | provideFileDecoration(uri: Uri): ProviderResult { 12 | const params: URLSearchParams = new URLSearchParams(uri.query); 13 | const type: ConnectionItemType = params.get('type') as ConnectionItemType; 14 | 15 | switch (type) { 16 | case 'activeDatabase': 17 | return { 18 | color: new ThemeColor('charts.green'), 19 | }; 20 | default: 21 | return undefined; 22 | } 23 | } 24 | } 25 | 26 | export const connectionTreeDecorationProvider = 27 | new ConnectionTreeDecorationProvider(); 28 | -------------------------------------------------------------------------------- /packages/language-support/src/formatting/standardizer.ts: -------------------------------------------------------------------------------- 1 | import { TerminalNode } from 'antlr4'; 2 | import { StatementsOrCommandsContext } from '../generated-parser/CypherCmdParser'; 3 | import CypherCmdParserVisitor from '../generated-parser/CypherCmdParserVisitor'; 4 | import { getParseTreeAndTokens } from './formattingHelpers'; 5 | 6 | class StandardizingVisitor extends CypherCmdParserVisitor { 7 | buffer = []; 8 | 9 | format = (root: StatementsOrCommandsContext) => { 10 | this.visit(root); 11 | return this.buffer.join(''); 12 | }; 13 | 14 | visitTerminal = (node: TerminalNode) => { 15 | this.buffer.push(node.getText().toLowerCase()); 16 | this.buffer.push(' '); 17 | }; 18 | } 19 | 20 | export function standardizeQuery(query: string): string { 21 | const { tree } = getParseTreeAndTokens(query); 22 | const visitor = new StandardizingVisitor(); 23 | return visitor.format(tree); 24 | } 25 | -------------------------------------------------------------------------------- /packages/query-tools/src/queries/roles.ts: -------------------------------------------------------------------------------- 1 | import { resultTransformers } from 'neo4j-driver'; 2 | import type { ExecuteQueryArgs } from '../types/sdkTypes.js'; 3 | 4 | export type Neo4jRole = { 5 | role: string; 6 | }; 7 | 8 | /** 9 | * Gets available roles in your database 10 | * https://neo4j.com/docs/operations-manual/current/authentication-authorization/manage-roles/#access-control-list-roles 11 | */ 12 | export function listRoles(): ExecuteQueryArgs<{ 13 | roles: Neo4jRole[]; 14 | }> { 15 | const query = `SHOW ROLES`; 16 | 17 | const resultTransformer = resultTransformers.mappedResultTransformer({ 18 | map(record) { 19 | return record.toObject() as Neo4jRole; 20 | }, 21 | collect(roles, summary) { 22 | return { roles, summary }; 23 | }, 24 | }); 25 | 26 | return { 27 | query, 28 | queryConfig: { resultTransformer, routing: 'READ', database: 'system' }, 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /packages/language-support/src/tests/formatting/testutil.ts: -------------------------------------------------------------------------------- 1 | import { formatQuery, FormattingOptions } from '../../formatting/formatting'; 2 | import { standardizeQuery } from '../../formatting/standardizer'; 3 | 4 | export function verifyFormatting( 5 | query: string, 6 | expected: string, 7 | formattingOptions?: FormattingOptions, 8 | ): void { 9 | const formatted = formatQuery(query, formattingOptions).formattedQuery; 10 | expect(formatted).toEqual(expected); 11 | const queryStandardized = standardizeQuery(query); 12 | const formattedStandardized = standardizeQuery(formatted); 13 | if (formattedStandardized !== queryStandardized) { 14 | throw new Error( 15 | `Standardized query does not match standardized formatted query`, 16 | ); 17 | } 18 | // Idempotency check 19 | const formattedTwice = formatQuery(query, formattingOptions).formattedQuery; 20 | expect(formattedTwice).toEqual(formatted); 21 | } 22 | -------------------------------------------------------------------------------- /packages/lint-worker/src/version.ts: -------------------------------------------------------------------------------- 1 | import semver from 'semver'; 2 | import { integer } from 'vscode-languageserver-types'; 3 | 4 | /**Like semver.compare - Returns: 5 | * - -1 if v1 < v2 6 | * - 0 if v1 === v2 7 | * - 1 if v1 > v2 8 | * But ignores patch version, 9 | * and returns undefined if versions are of incorrect format */ 10 | export function compareMajorMinorVersions( 11 | version1: string, 12 | version2: string, 13 | ): integer | undefined { 14 | const semVer1: semver.SemVer | null = semver.coerce(version1, { 15 | includePrerelease: false, 16 | loose: true, 17 | }); 18 | const semVer2: semver.SemVer | null = semver.coerce(version2, { 19 | includePrerelease: false, 20 | loose: true, 21 | }); 22 | if (semVer1 && semVer2) { 23 | semVer1.patch = 0; 24 | semVer2.patch = 0; 25 | const result = semver.compare(semVer1, semVer2, true); 26 | return result; 27 | } 28 | return undefined; 29 | } 30 | -------------------------------------------------------------------------------- /packages/react-codemirror-playground/src/TokenTable.tsx: -------------------------------------------------------------------------------- 1 | import { applySyntaxColouring } from '@neo4j-cypher/language-support'; 2 | import React from 'react'; 3 | 4 | export function TokenTable({ document }: { document: string }) { 5 | const tokens = applySyntaxColouring(document); 6 | 7 | const tableHeadings = ['text', 'type', 'startIndex']; 8 | 9 | return ( 10 |
11 | {tableHeadings.map((heading) => ( 12 | 13 | {heading} 14 | 15 | ))} 16 | {tokens.map(({ token, tokenType, position }) => ( 17 | 18 |
{token}
19 |
{tokenType}
20 |
21 | {position.line}:{position.startCharacter} 22 |
23 |
24 | ))} 25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /packages/query-tools/src/types/sdkTypes.ts: -------------------------------------------------------------------------------- 1 | import type { QueryConfig, ResultSummary } from 'neo4j-driver'; 2 | 3 | export type DbType = 'system' | 'standard' | 'composite'; 4 | 5 | export type QueryType = 6 | // Query automatically run by the app. 7 | | 'system' 8 | // Query the user directly submitted to/through the app. 9 | | 'user-direct' 10 | // Query resulting from an action the user performed. 11 | | 'user-action' 12 | // Query that has been derived from the user input. 13 | | 'user-transpiled'; 14 | 15 | export type CypherTransactionMetadata = { 16 | app: 'neo4j-sdk'; 17 | type: QueryType; 18 | version?: string; 19 | }; 20 | 21 | export type ExtendedQueryConfig = QueryConfig & 22 | Required, 'routing' | 'resultTransformer'>>; 23 | 24 | type Result = T & { summary: ResultSummary }; 25 | 26 | export type ExecuteQueryArgs = { 27 | query: string; 28 | queryConfig: ExtendedQueryConfig>; 29 | }; 30 | -------------------------------------------------------------------------------- /packages/language-server/src/formatting.ts: -------------------------------------------------------------------------------- 1 | import { formatQuery } from '@neo4j-cypher/language-support'; 2 | import { TextDocument } from 'vscode-languageserver-textdocument'; 3 | import { 4 | DocumentFormattingParams, 5 | TextDocuments, 6 | TextEdit, 7 | } from 'vscode-languageserver/node'; 8 | 9 | export const formatDocument = ( 10 | params: DocumentFormattingParams, 11 | documents: TextDocuments, 12 | ): TextEdit[] => { 13 | const document = documents.get(params.textDocument.uri); 14 | if (!document) { 15 | return []; 16 | } 17 | 18 | const text = document.getText(); 19 | const formattedText = formatQuery(text).formattedQuery; 20 | 21 | if (text === formattedText) { 22 | return []; 23 | } 24 | 25 | return [ 26 | TextEdit.replace( 27 | { 28 | start: { line: 0, character: 0 }, 29 | end: { line: document.lineCount, character: 0 }, 30 | }, 31 | formattedText, 32 | ), 33 | ]; 34 | }; 35 | -------------------------------------------------------------------------------- /packages/language-support/src/tests/autocompletion/autocompletionHelper.test.ts: -------------------------------------------------------------------------------- 1 | import { shouldAutoCompleteYield } from '../../autocompletion/autocompletionHelpers'; 2 | 3 | test('shouldAutoCompleteYield does not produce false negatives', () => { 4 | const query = 'CALL dbms.components() YIELD '; 5 | expect(shouldAutoCompleteYield(query, 29)).toBe(true); 6 | }); 7 | 8 | test('shouldAutoCompleteYield is not case sensitive', () => { 9 | const query = 'CALL dbms.components() yIeLd '; 10 | expect(shouldAutoCompleteYield(query, 29)).toBe(true); 11 | }); 12 | 13 | test('shouldAutoCompleteYield does not produce false positives', () => { 14 | const query1 = 'CALL dbms.components() YIELD'; 15 | const query2 = 'CALL dbms.components() yield'; 16 | const query3 = 'CALL dbms.components() '; 17 | expect(shouldAutoCompleteYield(query1, 29)).toBe(false); 18 | expect(shouldAutoCompleteYield(query2, 29)).toBe(false); 19 | expect(shouldAutoCompleteYield(query3, 24)).toBe(false); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/testRunnerDebug.ts: -------------------------------------------------------------------------------- 1 | import { createAndStartTestContainer } from './setupTestContainer'; 2 | import { run as testRunner } from './testRunner'; 3 | 4 | export async function run(): Promise { 5 | const [neo4j5Instance, neo4j2025Instance] = await Promise.all([ 6 | createAndStartTestContainer({ 7 | containerName: 'vscode-it-neo4j-5', 8 | neo4jVersion: 'neo4j:5.20-enterprise', 9 | }), 10 | createAndStartTestContainer({ 11 | containerName: 'vscode-it-neo4j-2025', 12 | neo4jVersion: 'neo4j:2025.05.1-enterprise', 13 | env: { 14 | NEO4J_internal_dbms_cypher_enable__experimental__versions: 'true', 15 | }, 16 | }), 17 | ]); 18 | (process.env.NEO4J_5_PORT = neo4j5Instance.getMappedPort(7687).toString()), 19 | (process.env.NEO4J_2025_PORT = neo4j2025Instance 20 | .getMappedPort(7687) 21 | .toString()), 22 | (process.env.DEBUG_VSCODE_TESTS = 'true'); 23 | return testRunner(); 24 | } 25 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/simple-match.md: -------------------------------------------------------------------------------- 1 | # Simple match test 2 | 3 | 4 | ## Cypher blocks 5 | 6 | ```cypher 7 | MATCH (n:Label) RETURN function(n.property) 8 | ``` 9 | 10 | ## Js/Ts blocks 11 | 12 | ```ts 13 | const a = "/*cypher*/ MATCH (n:Label) RETURN function(n.property)" 14 | ``` 15 | 16 | ```js 17 | const a = "/*cypher*/ MATCH (n:Label) RETURN function(n.property)" 18 | ``` 19 | 20 | ## Java blocks 21 | 22 | ```java 23 | String a = "/*cypher*/ MATCH (n:Label) RETURN function(n.property)" 24 | ``` 25 | 26 | ## Python blocks 27 | 28 | ```python 29 | a = "/*cypher*/ MATCH (n:Label) RETURN function(n.property)" 30 | ``` 31 | 32 | ## Golang blocks 33 | 34 | ```go 35 | var a = "/*cypher*/ MATCH (n:Label) RETURN function(n.property)" 36 | ``` 37 | 38 | ## .NET blocks 39 | 40 | ```cs 41 | var a = "/*cypher*/ MATCH (n:Label) RETURN function(n.property)" 42 | ``` 43 | 44 | ```fsharp 45 | let a = "/*cypher*/ MATCH (n:Label) RETURN function(n.property)" 46 | ``` 47 | 48 | -------------------------------------------------------------------------------- /editor-plugin/intellij/src/main/java/org/neo4j/intellij/lsp/language/CypherFileType.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.intellij.lsp.language; 2 | 3 | import com.intellij.openapi.fileTypes.LanguageFileType; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import javax.swing.*; 8 | 9 | public final class CypherFileType extends LanguageFileType { 10 | 11 | public static final CypherFileType INSTANCE = new CypherFileType(); 12 | 13 | private CypherFileType() { 14 | super(CypherLanguage.INSTANCE); 15 | } 16 | 17 | @NotNull 18 | @Override 19 | public String getName() { 20 | return "Cypher File"; 21 | } 22 | 23 | @NotNull 24 | @Override 25 | public String getDescription() { 26 | return "Cypher language file"; 27 | } 28 | 29 | @NotNull 30 | @Override 31 | public String getDefaultExtension() { 32 | return "cypher"; 33 | } 34 | 35 | @Nullable 36 | @Override 37 | public Icon getIcon() { 38 | return CypherIcons.FILE; 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/simple-match.js: -------------------------------------------------------------------------------- 1 | 2 | const a = /* cypher */` 3 | // This is a comment 4 | MATCH (n:Label) RETURN function(n.property) 5 | ` 6 | 7 | const b = /*cypher*/` 8 | // This is a comment 9 | MATCH (n:Label) RETURN function(n.property) 10 | ` 11 | 12 | const c = `//cypher 13 | // This is a comment 14 | MATCH (n:Label) RETURN function(n.property) 15 | ` 16 | 17 | const e = `/* cypher */ 18 | // This is a comment 19 | MATCH (n:Label) RETURN function(n.property) 20 | ` 21 | 22 | const f = `/*cypher*/ 23 | // This is a comment 24 | MATCH (n:Label) RETURN function(n.property) 25 | ` 26 | 27 | const g = "/*cypher*/ MATCH (n:Label) RETURN function(n.property)" 28 | 29 | const h = '/*cypher*/ MATCH (n:Label) RETURN function(n.property)' 30 | 31 | // This one shouldn't highglight 32 | const i = "//cypher MATCH (n:Label) RETURN function(n.property)" 33 | 34 | // This one shouldn't highglight 35 | const j = '//cypher MATCH (n:Label) RETURN function(n.property)' -------------------------------------------------------------------------------- /packages/query-tools/src/queries/users.ts: -------------------------------------------------------------------------------- 1 | import { resultTransformers } from 'neo4j-driver'; 2 | import type { ExecuteQueryArgs } from '../types/sdkTypes.js'; 3 | 4 | export type Neo4jUser = { 5 | user: string; 6 | roles: string[]; 7 | // below are enterprise only 8 | passwordChangeRequired: string; 9 | suspended?: boolean; 10 | home?: string; 11 | }; 12 | 13 | /** 14 | * Gets available users in your database 15 | * https://neo4j.com/docs/cypher-manual/current/administration/access-control/manage-users/#access-control-list-users 16 | */ 17 | export function listUsers(): ExecuteQueryArgs<{ 18 | users: Neo4jUser[]; 19 | }> { 20 | const query = `SHOW USERS`; 21 | 22 | const resultTransformer = resultTransformers.mappedResultTransformer({ 23 | map(record) { 24 | return record.toObject() as Neo4jUser; 25 | }, 26 | collect(users, summary) { 27 | return { users, summary }; 28 | }, 29 | }); 30 | 31 | return { 32 | query, 33 | queryConfig: { resultTransformer, routing: 'READ', database: 'system' }, 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/setupTestContainer.ts: -------------------------------------------------------------------------------- 1 | import { Neo4jContainer, StartedNeo4jContainer } from '@testcontainers/neo4j'; 2 | 3 | type ContainerOpts = { 4 | containerName?: string; 5 | neo4jVersion: string; 6 | env?: { [key: string]: string }; 7 | }; 8 | 9 | export async function createAndStartTestContainer( 10 | opts: ContainerOpts = { 11 | containerName: 'vscode-integration-tests', 12 | neo4jVersion: 'neo4j:5-enterprise', 13 | }, 14 | ): Promise { 15 | const password = 'password'; 16 | const env = { ...(opts.env ?? {}), NEO4J_ACCEPT_LICENSE_AGREEMENT: 'yes' }; 17 | 18 | const container = await new Neo4jContainer(opts.neo4jVersion) 19 | .withExposedPorts(7474, 7687) 20 | .withApoc() 21 | .withPassword(password) 22 | .withEnvironment(env) 23 | // Giving it a name prevents us from spinning up a different 24 | // container every time we run the tests and allows us 25 | // closing a lingering one when the tests finish 26 | .withName(opts.containerName) 27 | .start(); 28 | 29 | return container; 30 | } 31 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate/simple-match.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | 3 | const a = /* cypher */ ` 4 | // This is a comment 5 | MATCH (n:Label) RETURN function(n.property) 6 | `; 7 | 8 | const b = /*cypher*/ ` 9 | // This is a comment 10 | MATCH (n:Label) RETURN function(n.property) 11 | `; 12 | 13 | const c = `//cypher 14 | // This is a comment 15 | MATCH (n:Label) RETURN function(n.property) 16 | `; 17 | 18 | const e = `/* cypher */ 19 | // This is a comment 20 | MATCH (n:Label) RETURN function(n.property) 21 | `; 22 | 23 | const f = `/*cypher*/ 24 | // This is a comment 25 | MATCH (n:Label) RETURN function(n.property) 26 | `; 27 | 28 | const g = "/*cypher*/ MATCH (n:Label) RETURN function(n.property)" 29 | 30 | const h = '/*cypher*/ MATCH (n:Label) RETURN function(n.property)' 31 | 32 | // This one shouldn't highglight 33 | const i = "//cypher MATCH (n:Label) RETURN function(n.property)" 34 | 35 | // This one shouldn't highglight 36 | const j = '//cypher MATCH (n:Label) RETURN function(n.property)' -------------------------------------------------------------------------------- /packages/query-tools/src/integration-tests/setupTestContainer.ts: -------------------------------------------------------------------------------- 1 | import { Neo4jContainer, StartedNeo4jContainer } from '@testcontainers/neo4j'; 2 | 3 | type ContainerOpts = { 4 | containerName: string; 5 | neo4jVersion: string; 6 | env?: { [key: string]: string }; 7 | }; 8 | 9 | export async function createAndStartTestContainer( 10 | opts: ContainerOpts = { 11 | containerName: 'polling-integration-tests', 12 | neo4jVersion: 'neo4j:5-enterprise', 13 | }, 14 | ): Promise { 15 | const password = 'password'; 16 | const env = { ...(opts.env ?? {}), NEO4J_ACCEPT_LICENSE_AGREEMENT: 'yes' }; 17 | 18 | const container = await new Neo4jContainer(opts.neo4jVersion) 19 | .withExposedPorts(7474, 7687) 20 | .withApoc() 21 | .withPassword(password) 22 | .withEnvironment(env) 23 | // Giving it a name prevents us from spinning up a different 24 | // container every time we run the tests and allows us 25 | // closing a lingering one when the tests finish 26 | .withName(opts.containerName) 27 | .start(); 28 | 29 | return container; 30 | } 31 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/mocks/mockExtensionContext.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Extension, 3 | ExtensionContext, 4 | ExtensionMode, 5 | EnvironmentVariableCollection, 6 | Memento, 7 | Uri, 8 | } from 'vscode'; 9 | import { InMemoryMemento } from './inMemoryMemento'; 10 | import { InMemorySecretStorage } from './inMemorySecretStorage'; 11 | import { getExtensionStoragePath } from '../helpers'; 12 | 13 | export class MockExtensionContext implements ExtensionContext { 14 | subscriptions: { dispose(): unknown }[] = []; 15 | workspaceState: Memento; 16 | globalState = new InMemoryMemento(); 17 | secrets = new InMemorySecretStorage(); 18 | extensionUri: Uri; 19 | extensionPath: string; 20 | environmentVariableCollection: EnvironmentVariableCollection; 21 | asAbsolutePath(relativePath: string): string { 22 | return relativePath; 23 | } 24 | storageUri: Uri; 25 | storagePath: string; 26 | globalStorageUri: Uri = Uri.file(getExtensionStoragePath()); 27 | globalStoragePath: string; 28 | logUri: Uri; 29 | logPath: string; 30 | extensionMode: ExtensionMode; 31 | extension: Extension; 32 | } 33 | -------------------------------------------------------------------------------- /packages/lint-worker/src/lintWorker.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | SymbolTable, 3 | SyntaxDiagnostic, 4 | } from '@neo4j-cypher/language-support'; 5 | import { 6 | DbSchema, 7 | lintCypherQuery as _lintCypherQuery, 8 | _internalFeatureFlags, 9 | } from '@neo4j-cypher/language-support'; 10 | import workerpool from 'workerpool'; 11 | 12 | function lintCypherQuery( 13 | query: string, 14 | dbSchema, 15 | featureFlags: { consoleCommands?: boolean } = {}, 16 | ): { diagnostics: SyntaxDiagnostic[]; symbolTables?: SymbolTable[] } { 17 | // We allow to override the consoleCommands feature flag 18 | if (featureFlags.consoleCommands !== undefined) { 19 | _internalFeatureFlags.consoleCommands = featureFlags.consoleCommands; 20 | } 21 | //cast to appease git lint check 22 | return _lintCypherQuery(query, dbSchema as DbSchema); 23 | } 24 | 25 | workerpool.worker({ lintCypherQuery }); 26 | 27 | type LinterArgs = Parameters; 28 | 29 | export type LinterTask = workerpool.Promise>; 30 | 31 | export type LintWorker = { 32 | lintCypherQuery: (...args: LinterArgs) => LinterTask; 33 | }; 34 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/styles/connectionPanel.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: grid; 3 | place-items: center; 4 | margin-top: 2rem; 5 | } 6 | 7 | .container > div { 8 | border: 1px solid var(--vscode-input-border, transparent); 9 | padding: 1rem; 10 | width: 80vw; 11 | } 12 | 13 | .form { 14 | margin-top: 2rem; 15 | display: grid; 16 | grid-template-columns: 1fr; 17 | } 18 | 19 | .form--input-wrapper { 20 | display: grid; 21 | grid-template-columns: 10rem 1fr; 22 | gap: 1rem; 23 | align-items: center; 24 | margin: 0 0 1rem 0; 25 | } 26 | 27 | .form--input-wrapper input.invalid, 28 | .form--input-wrapper select.invalid { 29 | border-color: var(--vscode-inputValidation-errorBorder, transparent); 30 | outline-color: var(--vscode-inputValidation-errorBorder, transparent); 31 | } 32 | 33 | .form--actions { 34 | display: grid; 35 | grid-template-columns: 1fr auto; 36 | justify-items: end; 37 | margin: 1rem 0 0 0; 38 | } 39 | 40 | .form--actions button, 41 | .form--actions input[type='submit'] { 42 | width: auto; 43 | margin-left: 1rem; 44 | padding-left: 1rem; 45 | padding-right: 1rem; 46 | } 47 | -------------------------------------------------------------------------------- /packages/query-tools/src/queries/version.ts: -------------------------------------------------------------------------------- 1 | import { resultTransformers } from 'neo4j-driver'; 2 | import { ExecuteQueryArgs } from '../types/sdkTypes'; 3 | 4 | /** 5 | * Get dbms version 6 | */ 7 | export function getVersion(): ExecuteQueryArgs<{ 8 | serverVersion: string | undefined; 9 | }> { 10 | const query = 'CALL dbms.components() YIELD name, versions'; 11 | 12 | const resultTransformer = resultTransformers.mappedResultTransformer({ 13 | map(record) { 14 | const obj = record.toObject(); 15 | const name = obj.name as string; 16 | const versions = obj.versions as string[]; 17 | return { name, versions }; 18 | }, 19 | collect(rows, summary) { 20 | for (const row of rows) { 21 | if (row.name === 'Neo4j Kernel') { 22 | return { serverVersion: row.versions[0], summary }; 23 | } 24 | } 25 | 26 | //We should not reach this unless the "name" field changes 27 | return { serverVersion: undefined, summary }; 28 | }, 29 | }); 30 | 31 | return { 32 | query, 33 | queryConfig: { resultTransformer, routing: 'READ', database: 'system' }, 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/publish-vscode-extension.yaml: -------------------------------------------------------------------------------- 1 | name: Publish VSCode extension to Marketplace and OpenVSX 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - 'neo4j-for-vscode@*.*.*' 8 | 9 | env: 10 | NODE_OPTIONS: '--max_old_space_size=4096' 11 | 12 | jobs: 13 | publish-vscode-extension: 14 | name: 'Publish VSCode extension to Marketplace and OpenVSX' 15 | environment: publish-vscode-extension 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | # todo verify caching works well here too 21 | - name: Setup and build project 22 | uses: ./.github/actions/setup-and-build 23 | 24 | - name: Publish to VSCode Marketplace 25 | env: 26 | VSCE_PAT: ${{ secrets.VSCE_PAT }} 27 | run: | 28 | cd packages/vscode-extension 29 | pnpm package 30 | pnpm vsce publish --no-dependencies 31 | 32 | - name: Publish to OpenVSX 33 | env: 34 | VSX_PAT: ${{ secrets.VSX_PAT }} 35 | run: | 36 | cd packages/vscode-extension 37 | pnpm ovsx publish -p $VSX_PAT --no-dependencies 38 | -------------------------------------------------------------------------------- /editor-plugin/intellij/src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | org.neo4j.intellij.lsp.cypher-lsp-support 3 | 4 | Cypher Lsp Support 5 | 6 | Neo4j Inc. 7 | 8 | 12 | 13 | com.intellij.modules.platform 14 | com.intellij.modules.ultimate 15 | JavaScript 16 | 17 | 18 | 20 | 26 | 27 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/languageClientService.ts: -------------------------------------------------------------------------------- 1 | import { 2 | LintWorkerSettings, 3 | Neo4jConnectionSettings, 4 | SymbolFetchingParams, 5 | } from '@neo4j-cypher/language-server/src/types'; 6 | import { getLanguageClient } from './contextService'; 7 | import { SymbolTable } from '@neo4j-cypher/language-support'; 8 | 9 | export type MethodName = 10 | | 'connectionUpdated' 11 | | 'connectionDisconnected' 12 | | 'updateParameters' 13 | | 'updateLintWorker' 14 | | 'symbolTableDone' 15 | | 'fetchSymbolTable'; 16 | 17 | /** 18 | * Communicates to the language client that a connection has been updated or disconnected and needs to take action. 19 | * @param methodName The name of the method to call. 20 | * @param settings The settings to send to the language client. 21 | */ 22 | export async function sendNotificationToLanguageClient( 23 | methodName: MethodName, 24 | params?: 25 | | Neo4jConnectionSettings 26 | | LintWorkerSettings 27 | | { symbolTable: SymbolTable } 28 | | SymbolFetchingParams, 29 | ) { 30 | const languageClient = getLanguageClient(); 31 | await languageClient.sendNotification(methodName, params); 32 | } 33 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/mocks/mockLanguageClient.ts: -------------------------------------------------------------------------------- 1 | import { 2 | LintWorkerSettings, 3 | Neo4jConnectionSettings, 4 | Neo4jSettings, 5 | SymbolFetchingParams, 6 | } from '@neo4j-cypher/language-server/src/types'; 7 | import { SymbolTable } from '@neo4j-cypher/language-support'; 8 | 9 | export class MockLanguageClient { 10 | async sendNotification( 11 | methodName: string, 12 | settings?: 13 | | LintWorkerSettings 14 | | Neo4jConnectionSettings 15 | | Neo4jSettings 16 | | { symbolTable: SymbolTable } 17 | | SymbolFetchingParams, 18 | ): Promise { 19 | if (settings) { 20 | if ('trace' in settings) { 21 | await Promise.resolve( 22 | `sending settings notification using ${methodName} with ${settings?.trace.server}, ${settings?.features.linting}`, 23 | ); 24 | } else if ('connectURL' in settings) { 25 | await Promise.resolve( 26 | `sending connection notification using ${methodName} with ${settings?.connectURL}, ${settings?.connect}, ${settings?.database}, ${settings?.password}, ${settings?.user}`, 27 | ); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /vendor/antlr4-c3/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016, 2017, Mike Lischke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/language-server/src/signatureHelp.ts: -------------------------------------------------------------------------------- 1 | import { signatureHelp } from '@neo4j-cypher/language-support'; 2 | import { 3 | SignatureHelp, 4 | SignatureHelpParams, 5 | TextDocuments, 6 | } from 'vscode-languageserver/node'; 7 | 8 | import { Neo4jSchemaPoller } from '@neo4j-cypher/query-tools'; 9 | import { TextDocument } from 'vscode-languageserver-textdocument'; 10 | 11 | export const emptyResult: SignatureHelp = { 12 | signatures: [], 13 | activeSignature: undefined, 14 | activeParameter: undefined, 15 | }; 16 | 17 | export function doSignatureHelp( 18 | documents: TextDocuments, 19 | neo4j: Neo4jSchemaPoller, 20 | ) { 21 | return (params: SignatureHelpParams) => { 22 | const textDocument = documents.get(params.textDocument.uri); 23 | const endOfTriggerHelp = params.context?.triggerCharacter === ')'; 24 | if (textDocument === undefined || endOfTriggerHelp) return emptyResult; 25 | 26 | const position = params.position; 27 | const offset = textDocument.offsetAt(position); 28 | 29 | return signatureHelp( 30 | textDocument.getText(), 31 | neo4j.metadata?.dbSchema ?? {}, 32 | offset, 33 | ); 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /packages/language-server/src/syntaxColouring.ts: -------------------------------------------------------------------------------- 1 | import { 2 | applySyntaxColouring, 3 | mapCypherToSemanticTokenIndex, 4 | } from '@neo4j-cypher/language-support'; 5 | import { 6 | SemanticTokensBuilder, 7 | SemanticTokensParams, 8 | TextDocument, 9 | TextDocuments, 10 | } from 'vscode-languageserver'; 11 | 12 | export function applySyntaxColouringForDocument( 13 | documents: TextDocuments, 14 | ) { 15 | return (params: SemanticTokensParams) => { 16 | const textDocument = documents.get(params.textDocument.uri); 17 | if (textDocument === undefined) return { data: [] }; 18 | 19 | const tokens = applySyntaxColouring(textDocument.getText()); 20 | 21 | const builder = new SemanticTokensBuilder(); 22 | 23 | tokens.forEach((token) => { 24 | const tokenColour = mapCypherToSemanticTokenIndex(token.tokenType); 25 | 26 | if (tokenColour !== undefined) { 27 | builder.push( 28 | token.position.line, 29 | token.position.startCharacter, 30 | token.length, 31 | tokenColour, 32 | 0, 33 | ); 34 | } 35 | }); 36 | const results = builder.build(); 37 | return results; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/mocks/setupMockContextStubs.ts: -------------------------------------------------------------------------------- 1 | import * as sinon from 'sinon'; 2 | import * as contextService from '../../src/contextService'; 3 | import { MockExtensionContext } from './mockExtensionContext'; 4 | import { MockLanguageClient } from './mockLanguageClient'; 5 | import { MockSchemaPoller } from './mockSchemaPoller'; 6 | 7 | export const setupMockContextStubs = ( 8 | sandbox: sinon.SinonSandbox, 9 | ): { 10 | mockContext: MockExtensionContext; 11 | mockLanguageClient: MockLanguageClient; 12 | mockSchemaPoller: MockSchemaPoller; 13 | } => { 14 | const mockContext = new MockExtensionContext(); 15 | const mockLanguageClient = new MockLanguageClient(); 16 | const mockSchemaPoller = new MockSchemaPoller(); 17 | 18 | sandbox.stub(contextService, 'getExtensionContext').returns(mockContext); 19 | sandbox.stub(contextService, 'getLanguageClient').returns(mockLanguageClient); 20 | sandbox.stub(contextService, 'getSchemaPoller').returns(mockSchemaPoller); 21 | 22 | const setContextStub = sandbox.stub(contextService, 'setContext'); 23 | 24 | setContextStub(mockContext, mockLanguageClient); 25 | 26 | return { mockContext, mockLanguageClient, mockSchemaPoller }; 27 | }; 28 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/fixtures/textmate-results/simple-match-cypher.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "character": "MATCH", 4 | "type": "source.cypher keyword" 5 | }, 6 | { 7 | "character": " (", 8 | "type": "source.cypher" 9 | }, 10 | { 11 | "character": "n", 12 | "type": "source.cypher variable" 13 | }, 14 | { 15 | "character": ":", 16 | "type": "source.cypher keyword.operator" 17 | }, 18 | { 19 | "character": "Label", 20 | "type": "source.cypher entity.name.class" 21 | }, 22 | { 23 | "character": ") ", 24 | "type": "source.cypher" 25 | }, 26 | { 27 | "character": "RETURN", 28 | "type": "source.cypher keyword" 29 | }, 30 | { 31 | "character": " ", 32 | "type": "source.cypher" 33 | }, 34 | { 35 | "character": "function(", 36 | "type": "source.cypher entity.name.function" 37 | }, 38 | { 39 | "character": "n", 40 | "type": "source.cypher variable" 41 | }, 42 | { 43 | "character": ".", 44 | "type": "source.cypher keyword.operator" 45 | }, 46 | { 47 | "character": "property", 48 | "type": "source.cypher variable.property" 49 | }, 50 | { 51 | "character": ")", 52 | "type": "source.cypher" 53 | } 54 | ] 55 | -------------------------------------------------------------------------------- /packages/vscode-extension/syntaxes/cypher.markdown.json: -------------------------------------------------------------------------------- 1 | { 2 | "scopeName": "inline.cypher.markdown.codeblock", 3 | "injectionSelector": "L:text.html.markdown -meta.embedded.block", 4 | "patterns": [ 5 | { 6 | "begin": "(^|\\G)(\\s*)(\\`{3,}|~{3,})\\s*(?i:(cypher)(\\s+[^`~]*)?$)", 7 | "name": "markup.fenced_code.block.markdown", 8 | "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", 9 | "beginCaptures": { 10 | "3": { 11 | "name": "punctuation.definition.markdown" 12 | }, 13 | "4": { 14 | "name": "fenced_code.block.language.markdown" 15 | }, 16 | "5": { 17 | "name": "fenced_code.block.language.attributes.markdown" 18 | } 19 | }, 20 | "endCaptures": { 21 | "3": { 22 | "name": "punctuation.definition.markdown" 23 | } 24 | }, 25 | "patterns": [ 26 | { 27 | "begin": "(^|\\G)(\\s*)(.*)", 28 | "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", 29 | "contentName": "meta.embedded.block.cypher", 30 | "patterns": [ 31 | { 32 | "include": "source.cypher" 33 | } 34 | ] 35 | } 36 | ] 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /packages/language-support/src/tests/benchmarks/completions.benchmark.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import { bench, describe } from 'vitest'; 3 | import { autocomplete } from '../../autocompletion/autocompletion'; 4 | import { parse, parserWrapper } from '../../parserWrapper'; 5 | import { testData } from '../testData'; 6 | import { autocompletionQueries, tictactoe } from './benchmarkQueries'; 7 | 8 | describe('completions', () => { 9 | bench('multistatement - autocompletion', function () { 10 | const query = tictactoe + ';\n' + tictactoe; 11 | const subQuery = tictactoe; 12 | parserWrapper.clearCache(); 13 | // This mimics getting the cursor back in the query and retriggering auto-completion 14 | autocomplete(query, testData.mockSchema); 15 | autocomplete(query, testData.mockSchema, subQuery.length); 16 | }); 17 | 18 | // Handle autocomplete queries 19 | Object.entries(autocompletionQueries).forEach(([name, query]) => { 20 | bench(`autocomplete - ${name} (parse.time.only)`, function () { 21 | parse(query); 22 | }); 23 | 24 | bench(`autocomplete - ${name}`, function () { 25 | parserWrapper.clearCache(); 26 | autocomplete(query, testData.mockSchema); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /editor-plugin/intellij/.run/Run IDE with Plugin.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 19 | true 20 | true 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.github/actions/codemirror-e2e-tests/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Codemirror E2E tests' 2 | inputs: 3 | browser: 4 | description: 'Browser to run tests (chromium or firefox)' 5 | required: true 6 | default: 'chromium' 7 | 8 | env: 9 | NODE_OPTIONS: '--max_old_space_size=8192' 10 | 11 | runs: 12 | using: 'composite' 13 | steps: 14 | - name: Validate browser input 15 | shell: bash 16 | run: | 17 | if [[ "${{ inputs.browser }}" != "chromium" && "${{ inputs.browser }}" != "firefox" ]]; then 18 | echo "Invalid browser (should be one of firefox or chromium): ${{ inputs.browser }}" 19 | exit 1 20 | fi 21 | 22 | - name: Install Playwright Browsers 23 | shell: bash 24 | run: pnpm exec playwright install --only-shell --with-deps ${{ inputs.browser }} 25 | working-directory: packages/react-codemirror 26 | 27 | - name: Run e2e test 28 | shell: bash 29 | run: pnpm --filter react-codemirror test:e2e --project=${{ inputs.browser }} 30 | 31 | - name: Upload Playwright Report 32 | uses: actions/upload-artifact@v4 33 | if: always() 34 | with: 35 | name: playwright-report-${{ inputs.browser }} 36 | path: packages/react-codemirror/playwright-report/ 37 | retention-days: 30 -------------------------------------------------------------------------------- /packages/vscode-extension/tests/testRunner.ts: -------------------------------------------------------------------------------- 1 | // Taken from https://code.visualstudio.com/api/working-with-extensions/testing-extension#the-test-runner-script 2 | import { glob } from 'glob'; 3 | import Mocha from 'mocha'; 4 | import path from 'path'; 5 | 6 | export function run(): Promise { 7 | // Create the mocha test 8 | const mocha = new Mocha({ 9 | ui: 'tdd', 10 | color: true, 11 | }); 12 | mocha.timeout( 13 | process.env.DEBUG_VSCODE_TESTS === 'true' 14 | ? Number.POSITIVE_INFINITY 15 | : 30000, 16 | ); 17 | 18 | const testsRoot = __dirname; 19 | 20 | return new Promise((resolve, reject) => { 21 | glob('**/{api,unit}/**/*.spec.js', { cwd: testsRoot }) 22 | .then((files) => { 23 | // Add files to the test suite 24 | files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); 25 | 26 | try { 27 | // Run the mocha test 28 | mocha.run((failures) => { 29 | if (failures > 0) { 30 | reject(new Error(`${failures} tests failed.`)); 31 | } else { 32 | resolve(); 33 | } 34 | }); 35 | } catch (err) { 36 | console.error(err); 37 | reject(err); 38 | } 39 | }) 40 | .catch(reject); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /packages/query-tools/src/queries/dataSummary.ts: -------------------------------------------------------------------------------- 1 | import { resultTransformers } from 'neo4j-driver'; 2 | import { ExecuteQueryArgs } from '../types/sdkTypes'; 3 | 4 | export type DataSummary = { 5 | labels: string[]; 6 | relationshipTypes: string[]; 7 | propertyKeys: string[]; 8 | }; 9 | 10 | const ITEM_LIMIT = 1000; 11 | 12 | export function getDataSummary( 13 | database?: string, 14 | ): ExecuteQueryArgs { 15 | const query = `CALL db.labels() YIELD label 16 | RETURN COLLECT(label)[..${ITEM_LIMIT}] AS result 17 | UNION ALL 18 | CALL db.relationshipTypes() YIELD relationshipType 19 | RETURN COLLECT(relationshipType)[..${ITEM_LIMIT}] AS result 20 | UNION ALL 21 | CALL db.propertyKeys() YIELD propertyKey 22 | RETURN COLLECT(propertyKey)[..${ITEM_LIMIT}] AS result`; 23 | 24 | const resultTransformer = resultTransformers.mappedResultTransformer({ 25 | map(record) { 26 | return record.toObject().result as string[]; 27 | }, 28 | collect(results, summary) { 29 | return { 30 | labels: results[0], 31 | relationshipTypes: results[1], 32 | propertyKeys: results[2], 33 | summary, 34 | }; 35 | }, 36 | }); 37 | 38 | return { 39 | query, 40 | queryConfig: { resultTransformer, routing: 'READ', database: database }, 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /packages/query-tools/src/index.ts: -------------------------------------------------------------------------------- 1 | export { FRIENDLY_ERROR_MESSAGES } from './connectionErrorHandler'; 2 | export type { ConnectionError } from './connectionErrorHandler'; 3 | export * from './cypher-execution/extract-unique-nodes-and-relationships'; 4 | export * from './cypher-execution/query-result'; 5 | export type { Neo4jType } from './cypher-execution/query-result'; 6 | export { 7 | getCypherTypeName, 8 | getPropertyTypeDisplayName, 9 | } from './data-transforms/cypher-type-names'; 10 | export type { CypherDataTypeName } from './data-transforms/cypher-type-names'; 11 | export * from './data-transforms/record-to-string'; 12 | export * from './types/cypher-data-types'; 13 | export type { 14 | ConnectedMetadataPoller as CypherMetadataPoller, 15 | DisconnectedMetadataPoller as EmptyMetadataPoller, 16 | MetadataPoller, 17 | } from './metadataPoller'; 18 | export type { Neo4jConnection, QueryResultWithLimit } from './neo4jConnection'; 19 | export type { Database } from './queries/databases'; 20 | export { graphResultTransformer } from './result-transformers/graph-result-transformer'; 21 | export { Neo4jSchemaPoller } from './schemaPoller'; 22 | export type { ConnnectionResult } from './schemaPoller'; 23 | export type { CypherDataType } from './types/cypher-data-types'; 24 | export { getVersion } from './queries/version'; 25 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/mocks/mockSchemaPoller.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | import { 3 | ConnnectionResult, 4 | Neo4jSchemaPoller, 5 | } from '@neo4j-cypher/query-tools'; 6 | import EventEmitter from 'events'; 7 | import { Config } from 'neo4j-driver'; 8 | 9 | export class MockSchemaPoller extends Neo4jSchemaPoller { 10 | events: EventEmitter = new EventEmitter(); 11 | 12 | async connect( 13 | url: string, 14 | credentials: { 15 | username: string; 16 | password: string; 17 | }, 18 | config: { 19 | driverConfig?: Config; 20 | appName: string; 21 | }, 22 | database?: string, 23 | ): Promise { 24 | return Promise.resolve({ 25 | success: true, 26 | retriable: false, 27 | error: undefined, 28 | }); 29 | } 30 | 31 | async persistentConnect( 32 | url: string, 33 | credentials: { 34 | username: string; 35 | password: string; 36 | }, 37 | config: { 38 | driverConfig?: Config; 39 | appName: string; 40 | }, 41 | database?: string, 42 | ): Promise { 43 | return Promise.resolve({ 44 | success: true, 45 | retriable: false, 46 | error: undefined, 47 | }); 48 | } 49 | 50 | disconnect(): void {} 51 | } 52 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/webviews/queryResults/queryResultsTypes.ts: -------------------------------------------------------------------------------- 1 | import { NeoNode, NeoRel } from '@neo4j-ndl/react'; 2 | import * as vscode from 'vscode'; 3 | 4 | export const views = { 5 | detailsView: undefined as vscode.WebviewView | undefined, 6 | visualizationView: undefined as vscode.WebviewView | undefined, 7 | }; 8 | 9 | export type QueryResultViews = keyof typeof views; 10 | 11 | export type ResultRows = Record[]; 12 | 13 | export type QueryResult = 14 | | { statement: string; type: 'executing' } 15 | | { statement: string; type: 'error'; errorMessage: string } 16 | | { 17 | statement: string; 18 | type: 'success'; 19 | rows: ResultRows; 20 | nodes: NeoNode[]; 21 | relationships: NeoRel[]; 22 | querySummary: string[]; 23 | }; 24 | 25 | export type QueryResults = QueryResult[]; 26 | 27 | export type QueryResultsMessage = 28 | | { 29 | type: 'visualizationUpdate'; 30 | result: QueryResult; 31 | to: QueryResultViews; 32 | } 33 | | { 34 | type: 'executionStart'; 35 | result: QueryResult[]; 36 | to: QueryResultViews; 37 | } 38 | | { 39 | type: 'executionUpdate'; 40 | result: QueryResult; 41 | to: QueryResultViews; 42 | } 43 | | { 44 | type: 'themeUpdate'; 45 | isDarkTheme: boolean; 46 | to: QueryResultViews; 47 | }; 48 | -------------------------------------------------------------------------------- /packages/language-support/src/autocompletion/autocompletionHelpers.ts: -------------------------------------------------------------------------------- 1 | export function shouldAutoCompleteYield(query: string, offset: number) { 2 | const yieldTriggerPhrase = 'yield '; 3 | const text = query.slice(0, offset); 4 | const yieldStart = offset - yieldTriggerPhrase.length; 5 | if (yieldStart >= 0) { 6 | const precedingText = text.slice(yieldStart, offset); 7 | return precedingText.toLowerCase() === yieldTriggerPhrase; 8 | } 9 | return false; 10 | } 11 | 12 | export const uniq = (arr: T[]) => Array.from(new Set(arr)); 13 | 14 | type BacktickVariant = 'label' | 'propertyKey' | 'relType' | 'dbName' | 'param'; 15 | 16 | export function backtickIfNeeded( 17 | e: string, 18 | variant: BacktickVariant, 19 | ): string | undefined { 20 | if (e == null || e == '') { 21 | return undefined; 22 | } else if ( 23 | (variant === 'label' || 24 | variant === 'propertyKey' || 25 | variant === 'relType') && 26 | (/[^\p{L}\p{N}_]/u.test(e) || /[^\p{L}_]/u.test(e[0])) 27 | ) { 28 | return `\`${e}\``; 29 | } else if ( 30 | variant === 'dbName' && 31 | (/[^\p{L}\p{N}_.]/u.test(e) || 32 | /[^\p{L}_]/u.test(e[0]) || 33 | /[^\p{L}\p{N}_]/u.test(e.at(-1))) 34 | ) { 35 | return `\`${e}\``; 36 | } else if (variant === 'param' && /[^\p{L}\p{N}_]/u.test(e)) { 37 | return `\`${e}\``; 38 | } else { 39 | return undefined; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/publish-npm-packages.yaml: -------------------------------------------------------------------------------- 1 | name: Publish packages to npm 2 | 3 | on: 4 | workflow_run: 5 | workflows: ['ci'] 6 | branches: ['main'] 7 | types: 8 | - completed 9 | 10 | env: 11 | NODE_OPTIONS: '--max_old_space_size=4096' 12 | 13 | permissions: 14 | id-token: write 15 | pull-requests: write 16 | contents: write 17 | 18 | jobs: 19 | canary-release-to-npm: 20 | name: 'Canary release to npm' 21 | needs: stable-release-to-npm 22 | environment: publish-npm 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - name: Setup and build project 28 | uses: ./.github/actions/setup-and-build 29 | 30 | - name: Build and release canary package to npm 31 | run: pnpm snapshot-release 32 | 33 | stable-release-to-npm: 34 | name: 'Stable release to npm' 35 | environment: publish-npm 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v4 39 | 40 | - name: Setup and build project 41 | uses: ./.github/actions/setup-and-build 42 | 43 | - name: Create Release Pull Request or Publish to npm 44 | id: changesets 45 | uses: changesets/action@e0145edc7d9d8679003495b11f87bd8ef63c0cba # v1.5.3 46 | with: 47 | publish: pnpm release 48 | env: 49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 50 | NPM_TOKEN: '' 51 | -------------------------------------------------------------------------------- /packages/language-support/README.md: -------------------------------------------------------------------------------- 1 | # Language Support 2 | 3 | This package contains the core language support features for Cypher built with ANTLR4. 4 | 5 | ## Usage 6 | 7 | The API is not yet properly documented, but here are two simple examples of what you can do! 8 | 9 | `npm install @neo4j-cypher/language-support@next` 10 | 11 | ```typescript 12 | import { 13 | autocomplete, 14 | validateSyntax, 15 | DbSchema, 16 | } from '@neo4j-cypher/language-support'; 17 | 18 | const schema: DbSchema = { labels: ['Person'] }; 19 | 20 | autocomplete('MATCH (n:', schema); // yields CompletionItem[] containing "Person" 21 | 22 | validateSyntax('RETRN 123', schema); // yields SyntaxDiagnostic[] with Invalid keyword, did you mean RETURN? 23 | ``` 24 | 25 | ## Cypherfmt CLI 26 | 27 | This package includes a command-line tool for formatting Cypher queries using `cypherfmt`. After installation, you can use it via the `cypherfmt` command: 28 | 29 | ```bash 30 | # Format a file and output to stdout 31 | cypherfmt file.cy 32 | 33 | # Format a file in place 34 | cypherfmt -i file.cy 35 | 36 | # Check if a file is formatted correctly (exits with code 1 if not formatted correctly) 37 | cypherfmt -c file.cy 38 | 39 | # Format all .cy, .cyp, and .cypher files in a directory recursively 40 | cypherfmt directory/ 41 | 42 | # Format input from stdin 43 | cat file.cy | cypherfmt 44 | ``` 45 | 46 | For more information, run `cypherfmt --help`. 47 | -------------------------------------------------------------------------------- /packages/language-support/src/autocompletion/autocompletion.ts: -------------------------------------------------------------------------------- 1 | import { DbSchema } from '../dbSchema'; 2 | import { findCaret } from '../helpers'; 3 | import { parserWrapper } from '../parserWrapper'; 4 | import { CompletionItem } from '../types'; 5 | import { completionCoreCompletion } from './completionCoreCompletions'; 6 | 7 | export function autocomplete( 8 | query: string, 9 | dbSchema: DbSchema, 10 | caretPosition: number = query.length, 11 | manual = false, 12 | ): 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; 17 | /* We try to locate the statement where the caret is and the token of the caret 18 | 19 | The reason for doing that is we need a way to "resynchronise" when the 20 | previous statements have errors and the parser fails from them onwards: 21 | 22 | MATCH (m) REUT m; CREATE (n) R 23 | ^ we should still be getting autocompletions here 24 | 25 | */ 26 | const caret = findCaret(parsingResult, caretPosition); 27 | if (caret) { 28 | const statement = caret.statement; 29 | const caretToken = caret.token; 30 | return completionCoreCompletion( 31 | statement, 32 | dbSchema, 33 | caretToken, 34 | symbolsInfo, 35 | manual, 36 | ); 37 | } 38 | 39 | return []; 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/deploy-demo.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Codemirror Demo to GitHub Pages 2 | 3 | on: 4 | # Runs on pushes targeting the default branch 5 | push: 6 | branches: ['main'] 7 | 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 12 | permissions: 13 | contents: read 14 | pages: write 15 | id-token: write 16 | 17 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 18 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 19 | concurrency: 20 | group: 'pages' 21 | cancel-in-progress: false 22 | 23 | env: 24 | NODE_OPTIONS: '--max_old_space_size=4096' 25 | 26 | jobs: 27 | deploy: 28 | environment: 29 | name: github-pages 30 | url: ${{ steps.deployment.outputs.page_url }} 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v4 34 | 35 | - name: Setup and build project 36 | uses: ./.github/actions/setup-and-build 37 | 38 | - name: Setup Pages 39 | uses: actions/configure-pages@v5 40 | 41 | - name: Upload artifact 42 | uses: actions/upload-pages-artifact@v3 43 | with: 44 | path: './packages/react-codemirror-playground/dist/' 45 | 46 | - name: Deploy to GitHub Pages 47 | id: deployment 48 | uses: actions/deploy-pages@v4 49 | -------------------------------------------------------------------------------- /packages/react-codemirror/src/lang-cypher/langCypher.ts: -------------------------------------------------------------------------------- 1 | import { autocompletion } from '@codemirror/autocomplete'; 2 | import { 3 | defineLanguageFacet, 4 | Language, 5 | LanguageSupport, 6 | } from '@codemirror/language'; 7 | import { type DbSchema } from '@neo4j-cypher/language-support'; 8 | import { completionStyles, cypherAutocomplete } from './autocomplete'; 9 | import { ParserAdapter } from './parser-adapter'; 10 | import { signatureHelpTooltip } from './signatureHelp'; 11 | import { cypherLinter } from './syntaxValidation'; 12 | 13 | const facet = defineLanguageFacet({ 14 | commentTokens: { block: { open: '/*', close: '*/' }, line: '//' }, 15 | closeBrackets: { brackets: ['(', '[', '{', "'", '"', '`'] }, 16 | }); 17 | 18 | export type CypherConfig = { 19 | lint?: boolean; 20 | showSignatureTooltipBelow?: boolean; 21 | featureFlags?: { 22 | consoleCommands?: boolean; 23 | }; 24 | schema?: DbSchema; 25 | useLightVersion: boolean; 26 | setUseLightVersion?: (useLightVersion: boolean) => void; 27 | }; 28 | 29 | export function cypher(config: CypherConfig) { 30 | const parserAdapter = new ParserAdapter(facet, config); 31 | 32 | const cypherLanguage = new Language(facet, parserAdapter, [], 'cypher'); 33 | 34 | return new LanguageSupport(cypherLanguage, [ 35 | autocompletion({ 36 | override: [cypherAutocomplete(config)], 37 | optionClass: completionStyles, 38 | }), 39 | cypherLinter(config), 40 | signatureHelpTooltip(config), 41 | ]); 42 | } 43 | -------------------------------------------------------------------------------- /packages/react-codemirror-playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@neo4j-cypher/react-codemirror-playground", 3 | "private": true, 4 | "version": "2.0.0-next.30", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite --open", 8 | "build": "tsc --noEmit && vite build", 9 | "preview": "vite preview", 10 | "clean": "rm -rf {dist,tsconfig.tsbuildinfo}" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/neo4j/cypher-language-support.git" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/neo4j/cypher-language-support/issues" 18 | }, 19 | "engineStrict": true, 20 | "engines": { 21 | "node": ">=24.11.1" 22 | }, 23 | "dependencies": { 24 | "@codemirror/autocomplete": "^6.18.6", 25 | "@codemirror/commands": "^6.8.1", 26 | "@codemirror/language": "^6.11.2", 27 | "@lezer/common": "^1.0.2", 28 | "@lezer/highlight": "^1.1.3", 29 | "@neo4j-cypher/language-support": "workspace:*", 30 | "@neo4j-cypher/react-codemirror": "workspace:*", 31 | "react": "^18.2.0", 32 | "react-d3-tree": "^3.6.1", 33 | "react-dom": "^18.2.0", 34 | "vite-plugin-node-stdlib-browser": "^0.2.1", 35 | "vscode-languageserver-types": "^3.17.3" 36 | }, 37 | "devDependencies": { 38 | "@types/react": "^18.0.28", 39 | "@types/react-dom": "^18.0.11", 40 | "@vitejs/plugin-react": "^3.1.0", 41 | "autoprefixer": "^10.4.14", 42 | "postcss": "^8.4.23", 43 | "tailwindcss": "^3.3.1", 44 | "vite": "^4.5.10" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/webviews/queryResults/querySummary.ts: -------------------------------------------------------------------------------- 1 | import type { QueryResultWithLimit } from '@neo4j-cypher/query-tools'; 2 | 3 | export function querySummary(result: QueryResultWithLimit): string[] { 4 | const rows = result.records.length; 5 | const counters = result.summary.counters; 6 | const output: string[] = []; 7 | 8 | // Streamed 9 | if (rows > 0) { 10 | // Started streaming 1 records after 5 ms and completed after 10 ms. 11 | output.push( 12 | `${ 13 | result.recordLimitHit 14 | ? `Fetch limit hit at ${result.records.length} records. ` 15 | : '' 16 | }Started streaming ${rows} record${ 17 | rows === 1 ? '' : 's' 18 | } after ${result.summary.resultConsumedAfter.toString()} ms and completed after ${result.summary.resultAvailableAfter.toString()}ms.`, 19 | ); 20 | } 21 | 22 | if (counters.containsUpdates()) { 23 | const updates = []; 24 | 25 | const updateCounts = counters.updates(); 26 | 27 | for (const key in updateCounts) { 28 | const count = updateCounts[key]; 29 | if (count > 0) { 30 | const parts = key.split(/(?=[A-Z])/); 31 | updates.push( 32 | `${count} ${parts.map((value) => value.toLowerCase()).join(' ')}`, 33 | ); 34 | } 35 | } 36 | 37 | if (updates.length > 0) { 38 | output.push(`${updates.join(', ')}.`); 39 | } 40 | } 41 | 42 | if (counters.containsSystemUpdates()) { 43 | output.push(`${counters.systemUpdates()} system updates.`); 44 | } 45 | 46 | return output; 47 | } 48 | -------------------------------------------------------------------------------- /packages/react-codemirror/README.md: -------------------------------------------------------------------------------- 1 | # React Codemirror 2 | 3 | This package can be built with `pnpm build` and then published to npm with `pnpm publish`. 4 | 5 | ## Usage 6 | 7 | `npm install @neo4j-cypher/react-codemirror@next` 8 | 9 | ```tsx 10 | import { useState } from 'react'; 11 | 12 | import { CypherEditor } from '@neo4j-cypher/react-codemirror@next'; 13 | 14 | export function CodeEditor() { 15 | const [value, setValue] = useState(''); 16 | // can be used to access underlying codemirror state or call for example `focus` 17 | const editorRef = useRef(null); 18 | 19 | return ; 20 | } 21 | ``` 22 | 23 | For a full example, see the [react-codemirror-playground](https://github.com/neo4j/cypher-language-support/tree/main/packages/react-codemirror-playground). 24 | 25 | ## Usage without react 26 | 27 | Currently we only support using the codemirror editor via the react wrapper, but we plan to release the codemirror extensions separately as well. 28 | 29 | ## Learning codemirror 30 | 31 | It can take a little time to get into the CodeMirror6 ways of thinking, Trevor Harmon has a [great blog post](https://thetrevorharmon.com/blog/learning-codemirror/) explaining the cm6 "primitives". He also has a demo on how to integrate ANTLR4 with codemirror over [here](https://github.com/thetrevorharmon/zephyr-demo). 32 | 33 | ### Completion Icons 34 | 35 | We use unmodified copies of Visual Studio Code Icons from microsofts repository [here](https://github.com/microsoft/vscode-icons) licensed under creative commons. 36 | -------------------------------------------------------------------------------- /vendor/antlr4-c3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "antlr4-c3", 3 | "version": "3.0.1", 4 | "description": "A code completion core implementation for ANTLR4 based parsers", 5 | "author": "Mike Lischke", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/mike-lischke/antlr4-c3" 10 | }, 11 | "private": true, 12 | "keywords": [ 13 | "ANTLR4", 14 | "code completion", 15 | "auto completion", 16 | "grammar", 17 | "parser" 18 | ], 19 | "type": "module", 20 | "main": "./dist/cjs/index.js", 21 | "types": "./dist/cjs/index.d.ts", 22 | "module": "./dist/esm/index.js", 23 | "exports": { 24 | "import": "./dist/esm/index.js", 25 | "require": "./dist/cjs/index.js" 26 | }, 27 | "scripts": { 28 | "build": "pnpm generate && pnpm build-esm && pnpm build-commonjs", 29 | "build-esm": "tsc --module esnext --outDir dist/esm", 30 | "build-commonjs": "tsc --module commonjs --outDir dist/cjs", 31 | "prepublishOnly": "pnpm generate && pnpm test", 32 | "test": "vitest run", 33 | "generate": "cd tests && sh generate.sh", 34 | "eslint": "eslint .", 35 | "clean": "rm -rf dist" 36 | }, 37 | "dependencies": { 38 | "antlr4": "4.13.2" 39 | }, 40 | "devDependencies": { 41 | "@types/unicode-properties": "1.3.0", 42 | "@typescript-eslint/eslint-plugin": "5.56.0", 43 | "@typescript-eslint/parser": "5.56.0", 44 | "eslint": "8.36.0", 45 | "eslint-plugin-import": "2.27.5", 46 | "eslint-plugin-jsdoc": "48.2.4", 47 | "eslint-plugin-prefer-arrow": "1.2.3", 48 | "ts-node": "10.9.1" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/Neo4jActiveDark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/Neo4jActiveLight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/Neo4jInactiveDark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/images/Neo4jInactiveLight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/styles/components/collapsible.css: -------------------------------------------------------------------------------- 1 | .collapsible:not(:last-child) { 2 | border-bottom: 1px solid rgb(var(--theme-palette-neutral-border-weak)); 3 | background-color: transparent; 4 | } 5 | 6 | .collapsible-header { 7 | display: flex; 8 | align-items: flex-start; 9 | padding: 2px 4px; 10 | gap: 4px; 11 | border: 1px solid transparent; 12 | } 13 | 14 | .collapsible-header:hover { 15 | background-color: rgb(var(--theme-palette-neutral-hover)); 16 | } 17 | 18 | .collapsible-title { 19 | cursor: pointer; 20 | padding: 2px; 21 | display: flex; 22 | width: 100%; 23 | border: 1px solid rgb(var(--theme-palette-neutral-border-weak)); 24 | border-radius: 4px; 25 | overflow: hidden; 26 | background-color: rgb(var(--theme-palette-neutral-bg-weak)); 27 | align-items: center; 28 | } 29 | 30 | .collapsible-title:focus-visible { 31 | outline-width: 2px; 32 | outline-offset: 2px; 33 | outline-color: rgb(var(--theme-palette-primary-focus)); 34 | } 35 | 36 | .collapsible-title-text { 37 | padding-left: 6px; 38 | overflow: hidden; 39 | white-space: nowrap; 40 | text-overflow: ellipsis; 41 | } 42 | 43 | .collapsible-header[data-expanded='true'] .collapsible-title-text { 44 | white-space: normal; 45 | } 46 | 47 | .collapsible-title[data-active='true'] { 48 | border-color: rgb(var(--theme-palette-primary-border-weak)); 49 | background-color: rgb(var(--theme-palette-primary-bg-weak)); 50 | color: rgb(var(--theme-palette-primary-text)); 51 | } 52 | 53 | .collapsible-title-icon { 54 | margin-left: auto; 55 | } 56 | 57 | .collapsible-content { 58 | padding: 4px 12px; 59 | } 60 | -------------------------------------------------------------------------------- /packages/react-codemirror/src/e2e_tests/extraKeybindings.spec.tsx: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@playwright/experimental-ct-react'; 2 | import { CypherEditor } from '../CypherEditor'; 3 | import { CypherEditorPage } from './e2eUtils'; 4 | 5 | test('can add extra keybinding statically', async ({ mount, page }) => { 6 | const editorPage = new CypherEditorPage(page); 7 | let hasRun = false; 8 | const component = await mount( 9 | { 15 | hasRun = true; 16 | return true; 17 | }, 18 | }, 19 | ]} 20 | />, 21 | ); 22 | 23 | await editorPage.getEditor().press('a'); 24 | 25 | await expect(component).not.toHaveText('a'); 26 | expect(hasRun).toBe(true); 27 | }); 28 | 29 | test('can add extra keybinding dymanically', async ({ mount, page }) => { 30 | const editorPage = new CypherEditorPage(page); 31 | let hasRun = false; 32 | const component = await mount(); 33 | await editorPage.getEditor().press('a'); 34 | await editorPage.getEditor().press('Escape'); 35 | await expect(component).toContainText('a'); 36 | 37 | await component.update( 38 | { 44 | hasRun = true; 45 | return true; 46 | }, 47 | }, 48 | ]} 49 | />, 50 | ); 51 | 52 | await editorPage.getEditor().press('a'); 53 | await expect(component).not.toContainText('aa'); 54 | expect(hasRun).toBe(true); 55 | }); 56 | -------------------------------------------------------------------------------- /packages/query-tools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@neo4j-cypher/query-tools", 3 | "description": "", 4 | "author": "Neo4j Inc.", 5 | "license": "Apache-2.0", 6 | "version": "2.0.0-next.27", 7 | "main": "./dist/cjs/src/index.js", 8 | "module": "./dist/esm/src/index.js", 9 | "types": "./dist/types/src/index.d.ts", 10 | "exports": { 11 | ".": { 12 | "types": "./dist/esm/index.d.ts", 13 | "require": "./dist/cjs/src/index.js", 14 | "import": "./dist/esm/src/index.js", 15 | "default": "./dist/cjs/src/index.js" 16 | } 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git://github.com/neo4j/cypher-language-support.git" 21 | }, 22 | "bugs": { 23 | "url": "https://github.com/neo4j/cypher-language-support/issues" 24 | }, 25 | "engineStrict": true, 26 | "engines": { 27 | "node": ">=24.11.1" 28 | }, 29 | "dependencies": { 30 | "@neo4j-cypher/language-support": "workspace:*", 31 | "ajv": "^8.12.0", 32 | "neo4j-driver": "catalog:" 33 | }, 34 | "devDependencies": { 35 | "@testcontainers/neo4j": "^11.5.1" 36 | }, 37 | "scripts": { 38 | "build": "concurrently 'npm:build-esm' 'npm:build-commonjs' 'npm:build-types'", 39 | "build-esm": "tsc --module esnext --outDir dist/esm", 40 | "build-commonjs": "tsc --module commonjs --outDir dist/cjs", 41 | "build-types": "tsc --emitDeclarationOnly --outDir dist/types", 42 | "dev": "concurrently 'npm:build-esm -- --watch' 'npm:build-commonjs -- --watch'", 43 | "clean": "rm -rf {dist,tsconfig.tsbuildinfo}", 44 | "test": "vitest run extract-unique-nodes-and-relationships", 45 | "test:polling": "vitest run pollingIntegrationTests" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/language-support/src/tests/autocompletion/expressionCompletion.test.ts: -------------------------------------------------------------------------------- 1 | import { CompletionItemKind } from 'vscode-languageserver-types'; 2 | import { testCompletions } from './completionAssertionHelpers'; 3 | 4 | describe('expression completions', () => { 5 | describe('misc expression tests', () => { 6 | test('Can offer keyword literals in expressions when appropriate', () => { 7 | const query = 'MATCH (n:Person) WHERE n.name = N'; 8 | 9 | testCompletions({ 10 | query, 11 | expected: [ 12 | { label: 'NAN', kind: CompletionItemKind.Keyword }, 13 | { label: 'NULL', kind: CompletionItemKind.Keyword }, 14 | ], 15 | }); 16 | }); 17 | 18 | test('Does not incorrectly offer keywords when building string', () => { 19 | const query = 'MATCH (n:Person) WHERE n.name = "N'; 20 | 21 | testCompletions({ 22 | query, 23 | excluded: [ 24 | { label: 'NONE', kind: CompletionItemKind.Keyword }, 25 | { label: 'NULL', kind: CompletionItemKind.Keyword }, 26 | { label: 'UnescapedSymbolicName', kind: CompletionItemKind.Keyword }, 27 | { label: 'EscapedSymbolicName', kind: CompletionItemKind.Keyword }, 28 | ], 29 | }); 30 | }); 31 | 32 | test('Completes expression in a multiline query', () => { 33 | const query = `MATCH (n) WHERE n:A|B AND n.name = 'foo' 34 | CALL { 35 | MATCH (n) WHERE n:A|AND 36 | `; 37 | 38 | expect( 39 | testCompletions({ 40 | query: query, 41 | expected: [{ label: 'AND', kind: CompletionItemKind.Keyword }], 42 | }), 43 | ); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /editor-plugin/intellij/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.intellij.platform") version "2.1.0" 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | 8 | intellijPlatform { 9 | defaultRepositories() 10 | } 11 | } 12 | 13 | dependencies { 14 | intellijPlatform { 15 | intellijIdeaUltimate("2024.2.3") 16 | bundledPlugin("JavaScript") 17 | instrumentationTools() 18 | } 19 | } 20 | 21 | group = "org.neo4j.intellij.lsp" 22 | version = "1.1-SNAPSHOT" 23 | 24 | tasks { 25 | withType { 26 | sourceCompatibility = "21" 27 | targetCompatibility = "21" 28 | } 29 | 30 | clean { 31 | doFirst { 32 | exec { 33 | commandLine("bash", "-c", "rm -rf *.js") 34 | } 35 | } 36 | } 37 | 38 | prepareSandbox { 39 | doFirst { 40 | exec { 41 | commandLine("bash", "-c", "cd ../.. && pnpm build && cp packages/language-server/dist/cypher-language-server.js ./editor-plugin/intellij") 42 | } 43 | } 44 | from(".") { 45 | include("*.js") 46 | into("cypher-lsp-support") 47 | } 48 | } 49 | 50 | patchPluginXml { 51 | sinceBuild.set("242") 52 | untilBuild.set("242.*") 53 | } 54 | 55 | signPlugin { 56 | certificateChain.set(System.getenv("CERTIFICATE_CHAIN")) 57 | privateKey.set(System.getenv("PRIVATE_KEY")) 58 | password.set(System.getenv("PRIVATE_KEY_PASSWORD")) 59 | } 60 | 61 | publishPlugin { 62 | // alpha, beta or eap 63 | channels.set(listOf("beta")) 64 | token.set(System.getenv("PUBLISH_TOKEN")) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Neo4j Cypher Language Support 2 | 3 | This mono repo contains packages that together make up Neo4j's Cypher Language support. 4 | 5 | ## Project status 6 | 7 | The project is in an early stage. We are still missing important features and the project is not yet stable. We welcome feedback and contributions! 8 | 9 | Try it out in our [demo](https://neo4j.github.io/cypher-language-support/) or in our alpha releases in [Neo4j Workspace](https://workspace.neo4j.io) and soon also in our VS Code extension. 10 | 11 | ## Project Overview 12 | 13 | ![](./imgs/repo-overview.png) 14 | 15 | The project comprises several packages: 16 | 17 | - [language-support](./packages/language-support/README.md) - The core library implementing the language support features. 18 | - [language-server](./packages/language-server/README.md) - The language server wrapper for the `language-support` package. 19 | - [vscode-extension](./packages/vscode-extension/README.md) - The Neo4j VS Code extension which bundles the `language-server` 20 | - [react-codemirror](./packages/react-codemirror/README.md) - A set of [codemirror6](https://codemirror.net/) cypher language support plugins and a react wrapper. 21 | - [react-codemirror-playground](./packages/react-codemirror-playground/README.md) - A playground for the codemirror integration. 22 | - [query-tools](./packages/query-tools/README.md) - An internal package we use to manage the Neo4j connection and keep the schema (procedure names, labels, database names, etc.) up to date in the language server. 23 | 24 | ## Capabilities 25 | 26 | - Syntax highlighting 27 | - Autocompletion 28 | - Linting 29 | - Formatting 30 | 31 | ## Building the project and contributing 32 | 33 | See [CONTRIBUTING.md](./CONTRIBUTING.md). 34 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/typeUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Convert Neo4j Properties back into JavaScript types 3 | * 4 | * @param {Record} properties 5 | * @return {Record} 6 | */ 7 | 8 | import { 9 | isDateTime, 10 | isDuration, 11 | isInt, 12 | isLocalDateTime, 13 | isLocalTime, 14 | isPoint, 15 | isTime, 16 | isVector, 17 | } from 'neo4j-driver'; 18 | import { isDate } from 'util/types'; 19 | import { spacialFormat } from '@neo4j-cypher/query-tools'; 20 | 21 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 22 | export function toNativeTypes(properties: Record) { 23 | return Object.fromEntries( 24 | Object.keys(properties).map((key) => { 25 | const value = valueToNativeType(properties[key]); 26 | 27 | return [key, value]; 28 | }), 29 | ); 30 | } 31 | 32 | /** 33 | * Convert an individual value to its JavaScript equivalent 34 | * 35 | * @param {any} value 36 | * @returns {any} 37 | */ 38 | function valueToNativeType(value: unknown) { 39 | if (Array.isArray(value)) { 40 | value = value.map((innerValue) => valueToNativeType(innerValue)); 41 | } else if (isInt(value)) { 42 | value = value.toNumber(); 43 | } else if ( 44 | isDate(value) || 45 | isDateTime(value) || 46 | isTime(value) || 47 | isLocalDateTime(value) || 48 | isLocalTime(value) || 49 | isDuration(value) || 50 | isVector(value) 51 | ) { 52 | value = value.toString(); 53 | } else if (isPoint(value)) { 54 | value = spacialFormat(value); 55 | } else if ( 56 | typeof value === 'object' && 57 | value !== undefined && 58 | value !== null 59 | ) { 60 | value = toNativeTypes(value); 61 | } 62 | 63 | return value; 64 | } 65 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/treeviews/parametersTreeDataProvider.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { Event, EventEmitter, TreeDataProvider, TreeItem, Uri } from 'vscode'; 3 | import { getParameters, Parameter } from '../parameterService'; 4 | 5 | export class ParameterItem extends TreeItem { 6 | constructor(parameter: Parameter) { 7 | const label = `${parameter.key}: ${parameter.stringValue} (${parameter.type})`; 8 | super(label); 9 | this.id = parameter.key; 10 | this.contextValue = 'parameter'; 11 | this.label = label; 12 | this.iconPath = { 13 | light: Uri.file( 14 | path.join(__dirname, '..', 'resources', 'images', 'ParameterLight.svg'), 15 | ), 16 | dark: Uri.file( 17 | path.join(__dirname, '..', 'resources', 'images', 'ParameterDark.svg'), 18 | ), 19 | }; 20 | } 21 | } 22 | 23 | export class ParameterTreeDataProvider 24 | implements TreeDataProvider 25 | { 26 | private _onDidChangeTreeData: EventEmitter = 27 | new EventEmitter(); 28 | readonly onDidChangeTreeData: Event = 29 | this._onDidChangeTreeData.event; 30 | 31 | refresh(): void { 32 | this._onDidChangeTreeData.fire(); 33 | } 34 | 35 | getTreeItem(element: ParameterItem): TreeItem | Thenable { 36 | return element; 37 | } 38 | 39 | getChildren(): ParameterItem[] { 40 | const parameters: Record = getParameters(); 41 | 42 | return Object.values(parameters).map( 43 | (parameter) => new ParameterItem(parameter), 44 | ); 45 | } 46 | } 47 | 48 | export const parametersTreeDataProvider = new ParameterTreeDataProvider(); 49 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Cypher Language Support 2 | 3 | We're grateful for any thoughts or code you share. Feel free to open an issue if you run into a bug or have other improvement suggestions. To contribute a fix or a new feature we have a bit of process you'll need to follow: 4 | 5 | - Do your work in a personal fork of the original repository 6 | - Create a branch (with a useful name) for your contribution 7 | - Include unit/e2e tests if appropriate (obviously not necessary for documentation changes) 8 | - Take a moment to read and sign our [Contributor License Agreement](https://neo4j.com/developer/cla). 9 | 10 | We can't guarantee that we'll accept pull requests and may ask you to make some changes before they go in. 11 | Occasionally, we might also have logistical, commercial, or legal reasons why we can't accept your work but we'll try to find an alternative way for you to contribute in that case. 12 | 13 | ## Building the project 14 | 15 | Pre-requisites: 16 | 17 | - Node.js LTS (18.x) 18 | - [pnpm](https://pnpm.io/installation#using-corepack) 19 | - [antlr4-tools](https://github.com/antlr/antlr4-tools) easiest to install with `pip install antlr4-tools` (python3 required) 20 | - Java (e.g. `brew install java` on macOS) 21 | 22 | In the root folder of the project run: 23 | 24 | - `pnpm install` 25 | - `pnpm run build` 26 | 27 | From here you can start the `react-codemirror-playground` with: 28 | 29 | `pnpm dev-codemirror` 30 | 31 | To run the VS Code extension, choose `VSCode Playground` in the `Run & Debug` menu in VS Code. 32 | 33 | ## Executing the tests 34 | 35 | Unit tests: 36 | 37 | ``` 38 | pnpm test 39 | ``` 40 | 41 | End to end tests: 42 | 43 | ``` 44 | pnpm exec playwright install // Only the first time that you need to run these 45 | pnpm test:e2e 46 | ``` 47 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/commandHandlers/linters.ts: -------------------------------------------------------------------------------- 1 | import { 2 | compareMajorMinorVersions, 3 | getTaggedRegistryVersions, 4 | linterFileToServerVersion, 5 | npmTagToLinterVersion, 6 | } from '@neo4j-cypher/lint-worker'; 7 | import { switchToLinter } from '../linterSwitching'; 8 | import { window } from 'vscode'; 9 | import { getFilesInExtensionStorage } from '../linterService'; 10 | 11 | /** 12 | * Handler for SWITCH_LINTWORKER_COMMAND (neo4j.editLinter) 13 | * This can be triggered on the connection tree view, through the status bar or via the command palette. 14 | * Triggering shows a list of available linters. Picking one switches the linter used. 15 | * @returns A promise that resolves when the handler has completed. 16 | */ 17 | export async function manuallyAdjustLinter(): Promise { 18 | const npmReleases = await getTaggedRegistryVersions(); 19 | const fileNames = await getFilesInExtensionStorage(); 20 | const npmLinterVersions = npmReleases.map((release) => 21 | npmTagToLinterVersion(release.tag), 22 | ); 23 | const existingVersions = fileNames.map((name) => 24 | linterFileToServerVersion(name), 25 | ); 26 | const allVersions = new Set( 27 | existingVersions.concat(npmLinterVersions).filter((v) => v !== undefined), 28 | ); 29 | // This is to show Default on top and then the versions in decreasing order 30 | const sanitizedVersions = [ 31 | 'Default', 32 | ...Array.from(allVersions).sort(compareMajorMinorVersions).reverse(), 33 | ]; 34 | const picked = await window.showQuickPick(sanitizedVersions, { 35 | placeHolder: 'Select Cypher linter version', 36 | }); 37 | 38 | //closing the quickpick menu will return undefined 39 | if (picked === undefined) { 40 | return; 41 | } 42 | await switchToLinter(picked, npmReleases); 43 | } 44 | -------------------------------------------------------------------------------- /packages/vscode-extension/syntaxes/cypher.go.json: -------------------------------------------------------------------------------- 1 | { 2 | "scopeName": "inline.cypher.go", 3 | "injectionSelector": "L:(meta.embedded.block.go | source.go) -source.cypher -inline.cypher.go -string -comment", 4 | "patterns": [ 5 | { 6 | "name": "multiLineString", 7 | "contentName": "meta.embedded.block.cypher", 8 | "begin": "(`)((//\\s?cypher)|(/\\*\\s?cypher\\s?\\*/\\s*))", 9 | "end": "(`)", 10 | "beginCaptures": { 11 | "1": { 12 | "name": "string.quoted.raw.go punctuation.definition.string.begin.go" 13 | }, 14 | "2": { 15 | "patterns": [ 16 | { 17 | "include": "source.cypher" 18 | } 19 | ] 20 | } 21 | }, 22 | "endCaptures": { 23 | "1": { 24 | "name": "string.quoted.raw.go punctuation.definition.string.end.go" 25 | } 26 | }, 27 | "patterns": [ 28 | { 29 | "include": "source.cypher" 30 | } 31 | ] 32 | }, 33 | { 34 | "name": "singleLineString", 35 | "contentName": "meta.embedded.block.cypher", 36 | "begin": "(\")(/\\*\\s?cypher\\s?\\*/\\s*)", 37 | "end": "(\")", 38 | "beginCaptures": { 39 | "1": { 40 | "name": "string.quoted.double.go punctuation.definition.string.begin.go" 41 | }, 42 | "2": { 43 | "patterns": [ 44 | { 45 | "include": "source.cypher" 46 | } 47 | ] 48 | } 49 | }, 50 | "endCaptures": { 51 | "1": { 52 | "name": "string.quoted.double.go punctuation.definition.string.end.go" 53 | } 54 | }, 55 | "patterns": [ 56 | { 57 | "include": "source.cypher" 58 | } 59 | ] 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /packages/vscode-extension/syntaxes/cypher.csharp.json: -------------------------------------------------------------------------------- 1 | { 2 | "scopeName": "inline.cypher.csharp", 3 | "injectionSelector": "L:(meta.embedded.block.csharp | source.cs) -source.cypher -inline.cypher.csharp -string -comment", 4 | "patterns": [ 5 | { 6 | "name": "multiLineString", 7 | "contentName": "meta.embedded.block.cypher", 8 | "begin": "(\"\"\")((//\\s?cypher)|(/\\*\\s?cypher\\s?\\*/\\s*))", 9 | "end": "(\"\"\")", 10 | "beginCaptures": { 11 | "1": { 12 | "name": "string.quoted.double.cs punctuation.definition.string.begin.cs" 13 | }, 14 | "2": { 15 | "patterns": [ 16 | { 17 | "include": "source.cypher" 18 | } 19 | ] 20 | } 21 | }, 22 | "endCaptures": { 23 | "1": { 24 | "name": "string.quoted.double.cs punctuation.definition.string.end.cs" 25 | } 26 | }, 27 | "patterns": [ 28 | { 29 | "include": "source.cypher" 30 | } 31 | ] 32 | }, 33 | { 34 | "name": "singleLineString", 35 | "contentName": "meta.embedded.block.cypher", 36 | "begin": "(\")(/\\*\\s?cypher\\s?\\*/\\s*)", 37 | "end": "(\")", 38 | "beginCaptures": { 39 | "1": { 40 | "name": "string.quoted.double.cs punctuation.definition.string.begin.cs" 41 | }, 42 | "2": { 43 | "patterns": [ 44 | { 45 | "include": "source.cypher" 46 | } 47 | ] 48 | } 49 | }, 50 | "endCaptures": { 51 | "1": { 52 | "name": "string.quoted.double.cs punctuation.definition.string.end.cs" 53 | } 54 | }, 55 | "patterns": [ 56 | { 57 | "include": "source.cypher" 58 | } 59 | ] 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /packages/vscode-extension/syntaxes/cypher.java.json: -------------------------------------------------------------------------------- 1 | { 2 | "scopeName": "inline.cypher.java", 3 | "injectionSelector": "L:(meta.embedded.block.java | source.java) -source.cypher -inline.cypher.java -string -comment", 4 | "patterns": [ 5 | { 6 | "name": "multiLineString", 7 | "contentName": "meta.embedded.block.cypher", 8 | "begin": "(\"\"\")((//\\s?cypher)|(/\\*\\s?cypher\\s?\\*/\\s*))", 9 | "end": "(\"\"\")", 10 | "beginCaptures": { 11 | "1": { 12 | "name": "string.quoted.triple.java punctuation.definition.string.begin.java" 13 | }, 14 | "2": { 15 | "patterns": [ 16 | { 17 | "include": "source.cypher" 18 | } 19 | ] 20 | } 21 | }, 22 | "endCaptures": { 23 | "1": { 24 | "name": "string.quoted.triple.java punctuation.definition.string.end.java" 25 | } 26 | }, 27 | "patterns": [ 28 | { 29 | "include": "source.cypher" 30 | } 31 | ] 32 | }, 33 | { 34 | "name": "singleLineString", 35 | "contentName": "meta.embedded.block.cypher", 36 | "begin": "(\")(/\\*\\s?cypher\\s?\\*/\\s*)", 37 | "end": "(\")", 38 | "beginCaptures": { 39 | "1": { 40 | "name": "string.quoted.double.java punctuation.definition.string.begin.java" 41 | }, 42 | "2": { 43 | "patterns": [ 44 | { 45 | "include": "source.cypher" 46 | } 47 | ] 48 | } 49 | }, 50 | "endCaptures": { 51 | "1": { 52 | "name": "string.quoted.double.java punctuation.definition.string.end.java" 53 | } 54 | }, 55 | "patterns": [ 56 | { 57 | "include": "source.cypher" 58 | } 59 | ] 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /packages/vscode-extension/syntaxes/cypher.fsharp.json: -------------------------------------------------------------------------------- 1 | { 2 | "scopeName": "inline.cypher.fsharp", 3 | "injectionSelector": "L:(meta.embedded.block.fsharp | source.fsharp) -source.cypher -inline.cypher.fsharp -string -comment", 4 | "patterns": [ 5 | { 6 | "name": "multiLineString", 7 | "contentName": "meta.embedded.block.cypher", 8 | "begin": "(\")((//\\s?cypher)|(/\\*\\s?cypher\\s?\\*/\\s*))\\n", 9 | "end": "(\")", 10 | "beginCaptures": { 11 | "1": { 12 | "name": "string.quoted.double.fsharp punctuation.definition.string.begin.fsharp" 13 | }, 14 | "2": { 15 | "patterns": [ 16 | { 17 | "include": "source.cypher" 18 | } 19 | ] 20 | } 21 | }, 22 | "endCaptures": { 23 | "1": { 24 | "name": "string.quoted.double.fsharp punctuation.definition.string.end.fsharp" 25 | } 26 | }, 27 | "patterns": [ 28 | { 29 | "include": "source.cypher" 30 | } 31 | ] 32 | }, 33 | { 34 | "name": "singleLineString", 35 | "contentName": "meta.embedded.block.cypher", 36 | "begin": "(\")(/\\*\\s?cypher\\s?\\*/\\s*)", 37 | "end": "(\")", 38 | "beginCaptures": { 39 | "1": { 40 | "name": "string.quoted.double.fsharp punctuation.definition.string.begin.fsharp" 41 | }, 42 | "2": { 43 | "patterns": [ 44 | { 45 | "include": "source.cypher" 46 | } 47 | ] 48 | } 49 | }, 50 | "endCaptures": { 51 | "1": { 52 | "name": "string.quoted.double.fsharp punctuation.definition.string.end.fsharp" 53 | } 54 | }, 55 | "patterns": [ 56 | { 57 | "include": "source.cypher" 58 | } 59 | ] 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /packages/language-server/src/autocompletion.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CompletionParams, 3 | CompletionTriggerKind, 4 | Position, 5 | TextDocuments, 6 | } from 'vscode-languageserver/node'; 7 | 8 | import type { CompletionItem } from '@neo4j-cypher/language-support'; 9 | import { 10 | autocomplete, 11 | shouldAutoCompleteYield, 12 | } from '@neo4j-cypher/language-support'; 13 | import { Neo4jSchemaPoller } from '@neo4j-cypher/query-tools'; 14 | import { TextDocument } from 'vscode-languageserver-textdocument'; 15 | 16 | export function doAutoCompletion( 17 | documents: TextDocuments, 18 | neo4j: Neo4jSchemaPoller, 19 | ) { 20 | return (completionParams: CompletionParams) => { 21 | const textDocument = documents.get(completionParams.textDocument.uri); 22 | if (textDocument === undefined) return []; 23 | 24 | const position: Position = completionParams.position; 25 | const offset = textDocument.offsetAt(position); 26 | const yieldTriggered = shouldAutoCompleteYield( 27 | textDocument.getText(), 28 | offset, 29 | ); 30 | const manualOrCharacterOrInwordTriggered = 31 | completionParams.context?.triggerCharacter !== ' '; 32 | if (yieldTriggered || manualOrCharacterOrInwordTriggered) { 33 | const completions: CompletionItem[] = autocomplete( 34 | textDocument.getText(), 35 | neo4j.metadata?.dbSchema ?? {}, 36 | offset, 37 | completionParams.context.triggerKind === CompletionTriggerKind.Invoked, 38 | ); 39 | 40 | const result = completions.map((item) => { 41 | if (item.signature) { 42 | return { 43 | ...item, 44 | detail: item.detail + ' ' + item.signature, 45 | signature: undefined, 46 | }; 47 | } else { 48 | return item; 49 | } 50 | }); 51 | 52 | return result; 53 | } 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /packages/vscode-extension/tests/specs/unit/parametersTreeDataProvider.spec.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import { afterEach, beforeEach } from 'mocha'; 3 | import * as sinon from 'sinon'; 4 | import * as parameterService from '../../../src/parameterService'; 5 | import { parametersTreeDataProvider } from '../../../src/treeviews/parametersTreeDataProvider'; 6 | 7 | suite('Parameters tree data provider spec', () => { 8 | const mockParameters: parameterService.Parameters = { 9 | a: { 10 | key: 'a', 11 | serializedValue: 'charmander', 12 | stringValue: '"charmander"', 13 | type: 'String', 14 | evaluatedStatement: '"charmander"', 15 | }, 16 | b: { 17 | key: 'b', 18 | serializedValue: { 19 | low: 1234, 20 | high: 0, 21 | 'transport-class': 'Integer', 22 | }, 23 | stringValue: '1234', 24 | type: 'Integer', 25 | evaluatedStatement: '1234', 26 | }, 27 | }; 28 | 29 | let sandbox: sinon.SinonSandbox; 30 | 31 | beforeEach(() => { 32 | sandbox = sinon.createSandbox(); 33 | 34 | sandbox.stub(parameterService, 'getParameters').returns(mockParameters); 35 | }); 36 | 37 | afterEach(() => { 38 | sandbox.restore(); 39 | }); 40 | 41 | test('Shows parameters correctly', () => { 42 | const children = parametersTreeDataProvider.getChildren(); 43 | 44 | const params = children.map((value) => { 45 | return { 46 | label: value.label, 47 | id: value.id, 48 | contextValue: value.contextValue, 49 | }; 50 | }); 51 | 52 | assert.deepStrictEqual(params, [ 53 | { 54 | label: 'a: "charmander" (String)', 55 | id: 'a', 56 | contextValue: 'parameter', 57 | }, 58 | { 59 | label: 'b: 1234 (Integer)', 60 | id: 'b', 61 | contextValue: 'parameter', 62 | }, 63 | ]); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /packages/vscode-extension/resources/styles/components/vizWrapper.css: -------------------------------------------------------------------------------- 1 | .vizWrapper { 2 | padding: 10px; 3 | } 4 | 5 | .vizWrapper-content { 6 | height: calc(100vh - 20px); 7 | width: calc(100vw - 20px); 8 | min-height: 100px; 9 | overflow: hidden; 10 | display: flex; 11 | flex-direction: column; 12 | } 13 | 14 | .vizWrapper-controls { 15 | position: absolute; 16 | top: 12px; 17 | left: 12px; 18 | z-index: 10; 19 | } 20 | 21 | .vizWrapper-controls.block { 22 | position: initial; 23 | padding: 2px; 24 | } 25 | 26 | .vizWrapper-graph { 27 | background-color: rgb(var(--theme-palette-neutral-bg-weak)); 28 | border-radius: 10px; 29 | width: 100%; 30 | height: 100%; 31 | } 32 | 33 | .vizWrapper-table { 34 | overflow-y: scroll; 35 | flex: 1; 36 | } 37 | 38 | .vizWrapper table { 39 | border-collapse: collapse; 40 | overflow-y: scroll; 41 | flex: 1; 42 | width: 100%; 43 | } 44 | 45 | .vizWrapper thead { 46 | position: sticky; 47 | top: 0; 48 | background-color: rgb(var(--theme-palette-neutral-bg-default)); 49 | } 50 | 51 | .vizWrapper th:not(:first-child), 52 | .vizWrapper td:not(:first-child) { 53 | padding: 10px; 54 | border-bottom: 1px solid rgb(var(--theme-palette-neutral-border-weak)); 55 | text-align: left; 56 | } 57 | 58 | .vizWrapper th { 59 | font-weight: bold; 60 | } 61 | 62 | .vizWrapper td:first-child, 63 | .vizWrapper th:first-child { 64 | color: rgb(var(--theme-palette-neutral-text-weakest)); 65 | font-size: 8px; 66 | padding-left: 4px; 67 | border-bottom: 1px solid rgb(var(--theme-palette-neutral-border-weak)); 68 | width: 6px; 69 | } 70 | 71 | .vizWrapper tr:hover { 72 | background-color: rgb(var(--theme-palette-neutral-bg-on-bg-weak)); 73 | } 74 | 75 | .vizWrapper-table-cell { 76 | word-break: break-all; 77 | overflow-wrap: break-word; 78 | white-space: pre-wrap; 79 | max-width: 100%; 80 | font-size: var(--vscode-editor-font-size); 81 | } 82 | -------------------------------------------------------------------------------- /packages/vscode-extension/test/paramUnitTest.test.ts: -------------------------------------------------------------------------------- 1 | import { testData } from '@neo4j-cypher/language-support'; 2 | import assert from 'assert'; 3 | import { validateParamInput } from '../src/helpers'; 4 | 5 | describe('Parameter validation spec', () => { 6 | const dbSchema = { 7 | functions: { 8 | 'CYPHER 5': { 9 | datetime: { 10 | ...testData.emptyFunction, 11 | name: 'datetime', 12 | }, 13 | deprecatedFunction: { 14 | ...testData.emptyFunction, 15 | isDeprecated: true, 16 | name: 'deprecatedFunction', 17 | }, 18 | }, 19 | }, 20 | }; 21 | 22 | it('Parameter validation succeeds for correct inputs', () => { 23 | assert.strictEqual(validateParamInput('datetime()', dbSchema), undefined); 24 | assert.strictEqual(validateParamInput('500', dbSchema), undefined); 25 | }); 26 | 27 | it('Parameter validation fails for incorrect inputs', () => { 28 | assert.strictEqual( 29 | validateParamInput('datetime(', dbSchema), 30 | "Value cannot be evaluated: Variable `datetime` not defined. Invalid input '(': expected an expression, ',', 'AS', 'ORDER BY', 'CALL', 'CREATE', 'LOAD CSV', 'DELETE', 'DETACH', 'FINISH', 'FOREACH', 'INSERT', 'LIMIT', 'MATCH', 'MERGE', 'NODETACH', 'OFFSET', 'OPTIONAL', 'REMOVE', 'RETURN', 'SET', 'SKIP', 'UNION', 'UNWIND', 'USE', 'WITH' or ", 31 | ); 32 | assert.strictEqual( 33 | validateParamInput('500q', dbSchema), 34 | 'Value cannot be evaluated: invalid literal number', 35 | ); 36 | assert.strictEqual( 37 | validateParamInput('1 + ', dbSchema), 38 | `Value cannot be evaluated: Invalid input '': expected an expression`, 39 | ); 40 | }); 41 | 42 | it('Parameter validation succeeds on warnings', () => { 43 | assert.strictEqual( 44 | validateParamInput('deprecatedFunction()', dbSchema), 45 | undefined, 46 | ); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /vendor/antlr4-c3/tests/Optionals.g4: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) "Neo4j" 3 | * Neo4j Sweden AB [http://neo4j.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | grammar Optionals; 20 | 21 | 22 | expression: 23 | ruleOne | 24 | ruleTwo | 25 | ruleThree | 26 | ruleFour | 27 | ruleFive | 28 | ruleSix | 29 | ruleSeven | 30 | ruleEight | 31 | ruleNine | 32 | ruleTen | 33 | ruleEleven | 34 | ruleTwelve | 35 | ruleThirteen | 36 | ruleFourTeen | 37 | ruleFifTeen | 38 | ruleSixteen; 39 | 40 | ruleOne: A B; 41 | ruleTwo: C? D; 42 | ruleThree: E F?; 43 | ruleFour: G?? H; 44 | ruleFive: I J??; 45 | ruleSix: K* L; 46 | ruleSeven: M N*; 47 | ruleEight: O+ P; 48 | ruleNine: Q R+; 49 | ruleTen: S*? T; 50 | ruleEleven: U V*?; 51 | ruleTwelve: W+? Y; 52 | ruleThirteen: Z AA+?; 53 | ruleFourTeen: BB (A B)?; 54 | ruleFifTeen: CC (A B); 55 | ruleSixteen: DD (A B)? C; 56 | 57 | SPACE: ' ' -> channel(HIDDEN); 58 | A: 'A'; 59 | B: 'B'; 60 | C: 'C'; 61 | D: 'D'; 62 | E: 'E'; 63 | F: 'F'; 64 | G: 'G'; 65 | H: 'H'; 66 | I: 'I'; 67 | J: 'J'; 68 | K: 'K'; 69 | L: 'L'; 70 | M: 'M'; 71 | N: 'N'; 72 | O: 'O'; 73 | P: 'P'; 74 | Q: 'Q'; 75 | R: 'R'; 76 | S: 'S'; 77 | T: 'T'; 78 | U: 'U'; 79 | V: 'V'; 80 | W: 'W'; 81 | Y: 'Y'; 82 | Z: 'Z'; 83 | AA: 'AA'; 84 | BB: 'BB'; 85 | CC: 'CC'; 86 | DD: 'DD'; 87 | -------------------------------------------------------------------------------- /packages/language-support/src/antlr-grammar/CypherCmdParser.g4: -------------------------------------------------------------------------------- 1 | parser grammar CypherCmdParser; 2 | 3 | import CypherPreParser; 4 | 5 | options { tokenVocab = CypherCmdLexer; } 6 | 7 | statementsOrCommands: statementOrCommand (SEMICOLON statementOrCommand)* SEMICOLON? EOF; 8 | 9 | statementOrCommand: (preparsedStatement | consoleCommand); 10 | 11 | consoleCommand: COLON ( 12 | clearCmd 13 | | historyCmd 14 | | useCmd 15 | | paramsCmd 16 | | serverCmd 17 | | connectCmd 18 | | disconnectCmd 19 | | welcomeCmd 20 | | sysInfoCmd 21 | | styleCmd 22 | | playCmd 23 | | accessModeCmd 24 | | helpCmd 25 | ); 26 | 27 | paramsCmd: PARAM paramsArgs?; 28 | 29 | paramsArgs: (CLEAR | listCompletionRule | map | lambda); 30 | 31 | lambda: parameterName["ANY"] EQ GT expression; 32 | 33 | clearCmd: CLEAR; 34 | 35 | historyCmd: HISTORY; 36 | 37 | useCmd: useCompletionRule symbolicAliasName?; 38 | 39 | serverCmd: serverCompletionRule serverArgs; 40 | 41 | serverArgs: (CONNECT | DISCONNECT); 42 | 43 | connectCmd: CONNECT; 44 | 45 | disconnectCmd: DISCONNECT; 46 | 47 | sysInfoCmd: SYSINFO; 48 | 49 | styleCmd: STYLE RESET?; 50 | 51 | welcomeCmd: WELCOME; 52 | 53 | playCmd: PLAY; 54 | 55 | accessModeArgs: (readCompletionRule | writeCompletionRule); 56 | 57 | accessModeCmd: ACCESSMODE accessModeArgs?; 58 | 59 | helpCmd: HELP; 60 | 61 | // These rules are needed to distinguish cypher <-> commands, for exapmle `USE` and `:use` in autocompletion 62 | listCompletionRule: LIST; 63 | 64 | useCompletionRule: USE; 65 | 66 | serverCompletionRule: SERVER; 67 | 68 | readCompletionRule: READ; 69 | 70 | writeCompletionRule: WRITE; 71 | 72 | // This rule overrides the identifiers adding EXPLAIN, PROFILE, etc 73 | unescapedSymbolicNameString: 74 | preparserKeyword 75 | | HISTORY 76 | | CLEAR 77 | | PARAM 78 | | CONNECT 79 | | DISCONNECT 80 | | WELCOME 81 | | SYSINFO 82 | | unescapedSymbolicNameString_ 83 | ; 84 | -------------------------------------------------------------------------------- /editor-plugin/intellij/src/main/resources/META-INF/fileIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 17 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/vscode-extension/src/components/collapsible.tsx: -------------------------------------------------------------------------------- 1 | import { IconButton } from '@neo4j-ndl/react'; 2 | import { 3 | ChevronDownIconOutline, 4 | ChevronRightIconOutline, 5 | } from '@neo4j-ndl/react/icons'; 6 | import React from 'react'; 7 | 8 | type CollapsibleProps = { 9 | title: string; 10 | active: boolean; 11 | onToggle: () => void; 12 | children: React.ReactNode; 13 | }; 14 | 15 | export const Collapsible: React.FC = ({ 16 | title, 17 | active, 18 | onToggle, 19 | children, 20 | }) => { 21 | const [expanded, setExpanded] = React.useState(false); 22 | 23 | return ( 24 |
25 |
26 | { 34 | setExpanded((e) => !e); 35 | }} 36 | size="small" 37 | > 38 | {expanded ? ( 39 | 40 | ) : ( 41 | 42 | )} 43 | 44 | 45 |
{ 51 | if (e.key === 'Enter' || e.key === ' ') { 52 | e.preventDefault(); 53 | onToggle(); 54 | } 55 | }} 56 | > 57 |

{title}

58 |
59 |
60 | {expanded &&
{children}
} 61 |
62 | ); 63 | }; 64 | -------------------------------------------------------------------------------- /packages/query-tools/src/result-transformers/graph-result-transformer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Integer, 3 | Record, 4 | ResultSummary, 5 | resultTransformers, 6 | } from 'neo4j-driver'; 7 | 8 | import { 9 | DeduplicatedNodesAndRels, 10 | extractUniqueNodesAndRels, 11 | } from '../cypher-execution/extract-unique-nodes-and-relationships'; 12 | 13 | /** 14 | * Result type for graph queries that includes deduplicated nodes and relationships 15 | * along with the original records and query summary. 16 | * 17 | * See {@link DeduplicatedNodesAndRels} for the type of the nodes and relationships. 18 | */ 19 | export type GraphResult = DeduplicatedNodesAndRels & { 20 | /** Original Neo4j records returned by the query */ 21 | records: Record[]; 22 | summary: ResultSummary; 23 | }; 24 | 25 | /** 26 | * A result transformer that processes Neo4j query results into a graph format 27 | * with deduplicated nodes and relationships. 28 | * 29 | * This transformer extracts unique nodes and relationships from query records 30 | * while preserving the original records and summary information. It's particularly 31 | * useful for graph visualization and analysis where duplicate entities need to be 32 | * consolidated. 33 | * 34 | * @example 35 | * ```typescript 36 | * const result: GraphResult = await driver.executeQuery( 37 | * 'MATCH p=(name: $name)-[]->() RETURN p', 38 | * { name: 'John' }, 39 | * { resultTransformer: graphResultTransformer }, 40 | * ); 41 | * 42 | * console.log(result.nodes, result.relationships); 43 | * ``` 44 | * 45 | * @returns ResultTransformer producing {@link GraphResult} 46 | */ 47 | export const graphResultTransformer = 48 | resultTransformers.mappedResultTransformer({ 49 | map(record) { 50 | return record; 51 | }, 52 | collect(records, summary): GraphResult { 53 | const { nodes, relationships, limitHit } = 54 | extractUniqueNodesAndRels(records); 55 | return { nodes, relationships, limitHit, records, summary }; 56 | }, 57 | }); 58 | -------------------------------------------------------------------------------- /packages/query-tools/src/queries/databases.ts: -------------------------------------------------------------------------------- 1 | import type { CypherVersion } from '@neo4j-cypher/language-support'; 2 | import { resultTransformers } from 'neo4j-driver'; 3 | import { ExecuteQueryArgs } from '../types/sdkTypes'; 4 | 5 | export type DatabaseStatus = 6 | | 'online' 7 | | 'offline' 8 | | 'starting' 9 | | 'stopping' 10 | | 'store copying' 11 | | 'initial' 12 | | 'dirty' 13 | | 'quarantined' 14 | | 'unknown' 15 | | 'dropped' 16 | | 'deallocating'; 17 | 18 | export type Database = { 19 | name: string; 20 | address: string; 21 | role: string; 22 | requestedStatus: DatabaseStatus; 23 | currentStatus: DatabaseStatus; 24 | statusMessage?: string; 25 | error: string; 26 | default: boolean; 27 | home: boolean; 28 | aliases?: string[]; // introduced in neo4j 4.4 29 | type?: 'system' | 'composite' | 'standard'; // introduced in neo4j 5 30 | // "new keys", not sure which version introduced 31 | writer?: boolean; 32 | access?: string; 33 | constituents?: string[]; 34 | defaultLanguage?: CypherVersion; 35 | }; 36 | 37 | /** 38 | * List available databases in your dbms 39 | * https://neo4j.com/docs/cypher-manual/current/administration/databases/#administration-databases-show-databases 40 | */ 41 | export function listDatabases(): ExecuteQueryArgs<{ 42 | databases: Database[]; 43 | }> { 44 | const query = 'SHOW DATABASES YIELD *'; 45 | 46 | const resultTransformer = resultTransformers.mappedResultTransformer({ 47 | map(record) { 48 | const obj = record.toObject(); 49 | if (obj.defaultLanguage) { 50 | obj.defaultLanguage = (obj.defaultLanguage as string).toUpperCase(); 51 | } 52 | return obj as Database; 53 | }, 54 | collect(databases, summary) { 55 | return { 56 | databases: databases.filter((x) => x?.writer === true), 57 | summary, 58 | }; 59 | }, 60 | }); 61 | 62 | return { 63 | query, 64 | queryConfig: { resultTransformer, routing: 'READ', database: 'system' }, 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /packages/react-codemirror/playwright-ct.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from '@playwright/experimental-ct-react'; 2 | 3 | /** 4 | * See https://playwright.dev/docs/test-configuration. 5 | */ 6 | export default defineConfig({ 7 | testDir: './', 8 | /* The base directory, relative to the config file, for snapshot files created with toMatchSnapshot and toHaveScreenshot. */ 9 | snapshotDir: './__snapshots__', 10 | /* Maximum time one test can run for. */ 11 | timeout: 30 * 1000, 12 | /* Run tests in files in parallel */ 13 | fullyParallel: true, 14 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 15 | forbidOnly: !!process.env.CI, 16 | /* Retry on CI only */ 17 | retries: process.env.CI ? 2 : 0, 18 | /* Opt out of parallel tests on CI. */ 19 | workers: process.env.CI ? 1 : undefined, 20 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 21 | reporter: 'html', 22 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 23 | use: { 24 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 25 | trace: 'on-first-retry', 26 | 27 | /* Port to use for Playwright component endpoint. */ 28 | ctPort: 3100, 29 | 30 | // vite config to get worker working 31 | ctViteConfig: { 32 | build: { 33 | rollupOptions: { 34 | output: { 35 | entryFileNames: `assets/[name].js`, 36 | chunkFileNames: `assets/[name].js`, 37 | assetFileNames: `assets/[name].[ext]`, 38 | }, 39 | }, 40 | }, 41 | }, 42 | }, 43 | 44 | // Glob patterns or regular expressions that match test files. 45 | testMatch: '**/*.spec.tsx', 46 | 47 | projects: [ 48 | { 49 | name: 'chromium', 50 | use: { ...devices['Desktop Chrome'] }, 51 | }, 52 | { 53 | name: 'firefox', 54 | use: { ...devices['Desktop Firefox'] }, 55 | }, 56 | ], 57 | }); 58 | --------------------------------------------------------------------------------