@neo4j-cypher/react-codemirror 2.0.0-alpha.0 → 2.0.0-canary-a1ed8f3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -0
- package/LICENSE.md +201 -0
- package/README.md +27 -4
- package/dist/CypherEditor.d.ts +153 -0
- package/dist/CypherEditor.js +242 -0
- package/dist/CypherEditor.js.map +1 -0
- package/dist/e2e_tests/autoCompletion.spec.d.ts +1 -0
- package/dist/e2e_tests/autoCompletion.spec.js +133 -0
- package/dist/e2e_tests/autoCompletion.spec.js.map +1 -0
- package/dist/e2e_tests/configuration.spec.d.ts +1 -0
- package/dist/e2e_tests/configuration.spec.js +73 -0
- package/dist/e2e_tests/configuration.spec.js.map +1 -0
- package/dist/e2e_tests/e2eUtils.d.ts +12 -0
- package/dist/e2e_tests/e2eUtils.js +60 -0
- package/dist/e2e_tests/e2eUtils.js.map +1 -0
- package/dist/e2e_tests/extraKeybindings.spec.d.ts +1 -0
- package/dist/e2e_tests/extraKeybindings.spec.js +44 -0
- package/dist/e2e_tests/extraKeybindings.spec.js.map +1 -0
- package/dist/e2e_tests/historyNavigation.spec.d.ts +1 -0
- package/dist/e2e_tests/historyNavigation.spec.js +136 -0
- package/dist/e2e_tests/historyNavigation.spec.js.map +1 -0
- package/dist/e2e_tests/performanceTest.spec.d.ts +6 -0
- package/dist/e2e_tests/performanceTest.spec.js +96 -0
- package/dist/e2e_tests/performanceTest.spec.js.map +1 -0
- package/dist/e2e_tests/sanityChecks.spec.d.ts +1 -0
- package/dist/e2e_tests/sanityChecks.spec.js +56 -0
- package/dist/e2e_tests/sanityChecks.spec.js.map +1 -0
- package/dist/e2e_tests/signatureHelp.spec.d.ts +1 -0
- package/dist/e2e_tests/signatureHelp.spec.js +152 -0
- package/dist/e2e_tests/signatureHelp.spec.js.map +1 -0
- package/dist/e2e_tests/snippets.spec.d.ts +1 -0
- package/dist/e2e_tests/snippets.spec.js +63 -0
- package/dist/e2e_tests/snippets.spec.js.map +1 -0
- package/dist/e2e_tests/syntaxHighlighting.spec.d.ts +1 -0
- package/dist/e2e_tests/syntaxHighlighting.spec.js +91 -0
- package/dist/e2e_tests/syntaxHighlighting.spec.js.map +1 -0
- package/dist/e2e_tests/syntaxValidation.spec.d.ts +1 -0
- package/dist/e2e_tests/syntaxValidation.spec.js +79 -0
- package/dist/e2e_tests/syntaxValidation.spec.js.map +1 -0
- package/dist/historyNavigation.d.ts +7 -0
- package/dist/historyNavigation.js +163 -0
- package/dist/historyNavigation.js.map +1 -0
- package/dist/icons.d.ts +2 -0
- package/dist/icons.js +62 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/lang-cypher/autocomplete.d.ts +3 -0
- package/dist/lang-cypher/autocomplete.js +62 -0
- package/dist/lang-cypher/autocomplete.js.map +1 -0
- package/dist/lang-cypher/constants.d.ts +40 -0
- package/dist/lang-cypher/constants.js +65 -0
- package/dist/lang-cypher/constants.js.map +1 -0
- package/dist/lang-cypher/contants.test.d.ts +1 -0
- package/dist/lang-cypher/contants.test.js +102 -0
- package/dist/lang-cypher/contants.test.js.map +1 -0
- package/dist/lang-cypher/createCypherTheme.d.ts +26 -0
- package/dist/lang-cypher/createCypherTheme.js +172 -0
- package/dist/lang-cypher/createCypherTheme.js.map +1 -0
- package/dist/lang-cypher/langCypher.d.ts +9 -0
- package/dist/lang-cypher/langCypher.js +24 -0
- package/dist/lang-cypher/langCypher.js.map +1 -0
- package/dist/lang-cypher/lintWorker.d.ts +8 -0
- package/dist/lang-cypher/lintWorker.js +4 -0
- package/dist/lang-cypher/lintWorker.js.map +1 -0
- package/dist/lang-cypher/parser-adapter.d.ts +19 -0
- package/dist/lang-cypher/parser-adapter.js +113 -0
- package/dist/lang-cypher/parser-adapter.js.map +1 -0
- package/dist/lang-cypher/signatureHelp.d.ts +4 -0
- package/dist/lang-cypher/signatureHelp.js +93 -0
- package/dist/lang-cypher/signatureHelp.js.map +1 -0
- package/dist/lang-cypher/syntaxValidation.d.ts +5 -0
- package/dist/lang-cypher/syntaxValidation.js +71 -0
- package/dist/lang-cypher/syntaxValidation.js.map +1 -0
- package/dist/lang-cypher/themeIcons.d.ts +7 -0
- package/dist/lang-cypher/themeIcons.js +22 -0
- package/dist/lang-cypher/themeIcons.js.map +1 -0
- package/dist/ndlTokensCopy.d.ts +379 -0
- package/dist/ndlTokensCopy.js +380 -0
- package/dist/ndlTokensCopy.js.map +1 -0
- package/dist/ndlTokensCopy.test.d.ts +1 -0
- package/dist/ndlTokensCopy.test.js +11 -0
- package/dist/ndlTokensCopy.test.js.map +1 -0
- package/dist/neo4jSetup.d.ts +2 -0
- package/dist/neo4jSetup.js +120 -0
- package/dist/neo4jSetup.js.map +1 -0
- package/dist/themes.d.ts +11 -0
- package/dist/themes.js +114 -0
- package/dist/themes.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +46 -16
- package/src/CypherEditor.tsx +461 -0
- package/src/e2e_tests/autoCompletion.spec.tsx +236 -0
- package/src/e2e_tests/configuration.spec.tsx +97 -0
- package/src/e2e_tests/e2eUtils.ts +85 -0
- package/src/e2e_tests/extraKeybindings.spec.tsx +57 -0
- package/src/e2e_tests/historyNavigation.spec.tsx +196 -0
- package/src/e2e_tests/performanceTest.spec.tsx +158 -0
- package/src/e2e_tests/sanityChecks.spec.tsx +78 -0
- package/src/e2e_tests/signatureHelp.spec.tsx +309 -0
- package/src/e2e_tests/snippets.spec.tsx +94 -0
- package/src/e2e_tests/syntaxHighlighting.spec.tsx +198 -0
- package/src/e2e_tests/syntaxValidation.spec.tsx +156 -0
- package/src/historyNavigation.ts +191 -0
- package/{esm/index.mjs → src/icons.ts} +37 -1283
- package/src/index.ts +4 -0
- package/src/lang-cypher/autocomplete.ts +81 -0
- package/src/lang-cypher/constants.ts +84 -0
- package/src/lang-cypher/contants.test.ts +104 -0
- package/src/lang-cypher/createCypherTheme.ts +240 -0
- package/src/lang-cypher/langCypher.ts +41 -0
- package/src/lang-cypher/lintWorker.ts +14 -0
- package/src/lang-cypher/parser-adapter.ts +145 -0
- package/src/lang-cypher/signatureHelp.ts +131 -0
- package/src/lang-cypher/syntaxValidation.ts +99 -0
- package/src/lang-cypher/themeIcons.ts +27 -0
- package/src/ndlTokensCopy.test.ts +11 -0
- package/src/ndlTokensCopy.ts +379 -0
- package/src/neo4jSetup.tsx +179 -0
- package/src/themes.ts +132 -0
- package/dist/index.cjs +0 -1330
package/src/index.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { CompletionSource, snippet } from '@codemirror/autocomplete';
|
|
2
|
+
import { autocomplete } from '@neo4j-cypher/language-support';
|
|
3
|
+
import { CompletionItemKind } from 'vscode-languageserver-types';
|
|
4
|
+
import { CompletionItemIcons } from '../icons';
|
|
5
|
+
import type { CypherConfig } from './langCypher';
|
|
6
|
+
|
|
7
|
+
const completionKindToCodemirrorIcon = (c: CompletionItemKind) => {
|
|
8
|
+
const map: Record<CompletionItemKind, CompletionItemIcons> = {
|
|
9
|
+
[CompletionItemKind.Text]: 'Text',
|
|
10
|
+
[CompletionItemKind.Method]: 'Method',
|
|
11
|
+
[CompletionItemKind.Function]: 'Function',
|
|
12
|
+
[CompletionItemKind.Constructor]: 'Constructor',
|
|
13
|
+
[CompletionItemKind.Field]: 'Field',
|
|
14
|
+
[CompletionItemKind.Variable]: 'Variable',
|
|
15
|
+
[CompletionItemKind.Class]: 'Class',
|
|
16
|
+
[CompletionItemKind.Interface]: 'Interface',
|
|
17
|
+
[CompletionItemKind.Module]: 'Module',
|
|
18
|
+
[CompletionItemKind.Property]: 'Property',
|
|
19
|
+
[CompletionItemKind.Unit]: 'Unit',
|
|
20
|
+
[CompletionItemKind.Value]: 'Value',
|
|
21
|
+
[CompletionItemKind.Enum]: 'Enum',
|
|
22
|
+
[CompletionItemKind.Keyword]: 'Keyword',
|
|
23
|
+
[CompletionItemKind.Snippet]: 'Snippet',
|
|
24
|
+
[CompletionItemKind.Color]: 'Color',
|
|
25
|
+
[CompletionItemKind.File]: 'File',
|
|
26
|
+
[CompletionItemKind.Reference]: 'Reference',
|
|
27
|
+
[CompletionItemKind.Folder]: 'Folder',
|
|
28
|
+
[CompletionItemKind.EnumMember]: 'EnumMember',
|
|
29
|
+
[CompletionItemKind.Constant]: 'Constant',
|
|
30
|
+
[CompletionItemKind.Struct]: 'Struct',
|
|
31
|
+
// we're miss-using the enum here as there is no `Console` kind in the predefined list
|
|
32
|
+
[CompletionItemKind.Event]: 'Console',
|
|
33
|
+
[CompletionItemKind.Operator]: 'Operator',
|
|
34
|
+
[CompletionItemKind.TypeParameter]: 'TypeParameter',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return map[c];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const cypherAutocomplete: (config: CypherConfig) => CompletionSource =
|
|
41
|
+
(config) => (context) => {
|
|
42
|
+
const textUntilCursor = context.state.doc.toString().slice(0, context.pos);
|
|
43
|
+
|
|
44
|
+
const triggerCharacters = ['.', ':', '{', '$', ')'];
|
|
45
|
+
const lastCharacter = textUntilCursor.slice(-1);
|
|
46
|
+
|
|
47
|
+
const lastWord = context.matchBefore(/\w*/);
|
|
48
|
+
const inWord = lastWord.from !== lastWord.to;
|
|
49
|
+
|
|
50
|
+
const shouldTriggerCompletion =
|
|
51
|
+
inWord || context.explicit || triggerCharacters.includes(lastCharacter);
|
|
52
|
+
|
|
53
|
+
if (config.useLightVersion && !context.explicit) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!shouldTriggerCompletion) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const options = autocomplete(
|
|
62
|
+
textUntilCursor,
|
|
63
|
+
config.schema ?? {},
|
|
64
|
+
undefined,
|
|
65
|
+
context.explicit,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
from: context.matchBefore(/(\w|\$)*$/).from,
|
|
70
|
+
options: options.map((o) => ({
|
|
71
|
+
label: o.label,
|
|
72
|
+
type: completionKindToCodemirrorIcon(o.kind),
|
|
73
|
+
apply:
|
|
74
|
+
o.kind === CompletionItemKind.Snippet
|
|
75
|
+
? // codemirror requires an empty snippet space to be able to tab out of the completion
|
|
76
|
+
snippet((o.insertText ?? o.label) + '${}')
|
|
77
|
+
: undefined,
|
|
78
|
+
detail: o.detail,
|
|
79
|
+
})),
|
|
80
|
+
};
|
|
81
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { languageDataProp } from '@codemirror/language';
|
|
2
|
+
import type { Facet } from '@codemirror/state';
|
|
3
|
+
import { NodeSet, NodeType } from '@lezer/common';
|
|
4
|
+
import { styleTags, Tag, tags } from '@lezer/highlight';
|
|
5
|
+
import { CypherTokenType } from '@neo4j-cypher/language-support';
|
|
6
|
+
|
|
7
|
+
export const cypherTokenTypeToNode = (facet: Facet<unknown>) => ({
|
|
8
|
+
topNode: NodeType.define({
|
|
9
|
+
id: 0,
|
|
10
|
+
name: 'topNode',
|
|
11
|
+
props: [languageDataProp.add({ topNode: facet })],
|
|
12
|
+
}),
|
|
13
|
+
comment: NodeType.define({ id: 1, name: 'comment' }),
|
|
14
|
+
keyword: NodeType.define({ id: 2, name: 'keyword' }),
|
|
15
|
+
label: NodeType.define({ id: 3, name: 'label' }),
|
|
16
|
+
predicateFunction: NodeType.define({ id: 4, name: 'predicateFunction' }),
|
|
17
|
+
function: NodeType.define({ id: 5, name: 'function' }),
|
|
18
|
+
procedure: NodeType.define({ id: 6, name: 'procedure' }),
|
|
19
|
+
variable: NodeType.define({ id: 7, name: 'variable' }),
|
|
20
|
+
paramDollar: NodeType.define({ id: 8, name: 'paramDollar' }),
|
|
21
|
+
paramValue: NodeType.define({ id: 9, name: 'paramValue' }),
|
|
22
|
+
symbolicName: NodeType.define({ id: 10, name: 'symbolicName' }),
|
|
23
|
+
operator: NodeType.define({ id: 11, name: 'operator' }),
|
|
24
|
+
stringLiteral: NodeType.define({ id: 12, name: 'stringLiteral' }),
|
|
25
|
+
numberLiteral: NodeType.define({ id: 13, name: 'numberLiteral' }),
|
|
26
|
+
booleanLiteral: NodeType.define({ id: 14, name: 'booleanLiteral' }),
|
|
27
|
+
keywordLiteral: NodeType.define({ id: 15, name: 'keywordLiteral' }),
|
|
28
|
+
property: NodeType.define({ id: 16, name: 'property' }),
|
|
29
|
+
namespace: NodeType.define({ id: 17, name: 'namespace' }),
|
|
30
|
+
bracket: NodeType.define({ id: 18, name: 'bracket' }),
|
|
31
|
+
none: NodeType.define({ id: 19, name: 'none' }),
|
|
32
|
+
separator: NodeType.define({ id: 20, name: 'separator' }),
|
|
33
|
+
punctuation: NodeType.define({ id: 21, name: 'punctuation' }),
|
|
34
|
+
consoleCommand: NodeType.define({ id: 22, name: 'consoleCommand' }),
|
|
35
|
+
// also include prism token types
|
|
36
|
+
'class-name': NodeType.define({ id: 23, name: 'label' }),
|
|
37
|
+
// this is escaped variables
|
|
38
|
+
identifier: NodeType.define({ id: 24, name: 'variable' }),
|
|
39
|
+
string: NodeType.define({ id: 25, name: 'stringLiteral' }),
|
|
40
|
+
relationship: NodeType.define({ id: 26, name: 'label' }),
|
|
41
|
+
boolean: NodeType.define({ id: 27, name: 'booleanLiteral' }),
|
|
42
|
+
number: NodeType.define({ id: 28, name: 'numberLiteral' }),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export type PrismSpecificTokenType =
|
|
46
|
+
| 'class-name'
|
|
47
|
+
| 'identifier'
|
|
48
|
+
| 'string'
|
|
49
|
+
| 'relationship'
|
|
50
|
+
| 'boolean'
|
|
51
|
+
| 'number';
|
|
52
|
+
|
|
53
|
+
export type CodemirrorParseTokenType =
|
|
54
|
+
| CypherTokenType
|
|
55
|
+
| PrismSpecificTokenType
|
|
56
|
+
| 'topNode';
|
|
57
|
+
|
|
58
|
+
export type HighlightedCypherTokenTypes = Exclude<CypherTokenType, 'none'>;
|
|
59
|
+
export const tokenTypeToStyleTag: Record<HighlightedCypherTokenTypes, Tag> = {
|
|
60
|
+
comment: tags.comment,
|
|
61
|
+
keyword: tags.keyword,
|
|
62
|
+
label: tags.typeName,
|
|
63
|
+
predicateFunction: tags.function(tags.variableName),
|
|
64
|
+
function: tags.function(tags.variableName),
|
|
65
|
+
procedure: tags.function(tags.variableName),
|
|
66
|
+
variable: tags.variableName,
|
|
67
|
+
paramDollar: tags.atom,
|
|
68
|
+
paramValue: tags.atom,
|
|
69
|
+
symbolicName: tags.variableName,
|
|
70
|
+
operator: tags.operator,
|
|
71
|
+
stringLiteral: tags.string,
|
|
72
|
+
numberLiteral: tags.number,
|
|
73
|
+
booleanLiteral: tags.bool,
|
|
74
|
+
keywordLiteral: tags.operatorKeyword,
|
|
75
|
+
property: tags.propertyName,
|
|
76
|
+
namespace: tags.namespace,
|
|
77
|
+
bracket: tags.bracket,
|
|
78
|
+
punctuation: tags.punctuation,
|
|
79
|
+
separator: tags.separator,
|
|
80
|
+
consoleCommand: tags.macroName,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const parserAdapterNodeSet = (nodes: Record<string, NodeType>) =>
|
|
84
|
+
new NodeSet(Object.values(nodes)).extend(styleTags(tokenTypeToStyleTag));
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { tags } from '@lezer/highlight';
|
|
2
|
+
import { applySyntaxColouring } from '@neo4j-cypher/language-support';
|
|
3
|
+
import { tokenTypeToStyleTag } from './constants';
|
|
4
|
+
|
|
5
|
+
const cypherQueryWithAllTokenTypes = `MATCH (variable :Label)-[:REL_TYPE]->()
|
|
6
|
+
WHERE variable.property = "String"
|
|
7
|
+
OR namespaced.function() = false
|
|
8
|
+
// comment
|
|
9
|
+
OR $parameter > 2
|
|
10
|
+
RETURN variable;`;
|
|
11
|
+
|
|
12
|
+
test('correctly parses all cypher token types to style tags', () => {
|
|
13
|
+
const tokens = applySyntaxColouring(cypherQueryWithAllTokenTypes);
|
|
14
|
+
const tokenTypes = tokens.map((token) => token.tokenType);
|
|
15
|
+
expect(tokenTypes).toEqual([
|
|
16
|
+
'keyword',
|
|
17
|
+
'bracket',
|
|
18
|
+
'variable',
|
|
19
|
+
'operator',
|
|
20
|
+
'label',
|
|
21
|
+
'bracket',
|
|
22
|
+
'separator',
|
|
23
|
+
'bracket',
|
|
24
|
+
'operator',
|
|
25
|
+
'label',
|
|
26
|
+
'bracket',
|
|
27
|
+
'separator',
|
|
28
|
+
'separator',
|
|
29
|
+
'bracket',
|
|
30
|
+
'bracket',
|
|
31
|
+
'keyword',
|
|
32
|
+
'variable',
|
|
33
|
+
'operator',
|
|
34
|
+
'property',
|
|
35
|
+
'operator',
|
|
36
|
+
'stringLiteral',
|
|
37
|
+
'keyword',
|
|
38
|
+
'function',
|
|
39
|
+
'operator',
|
|
40
|
+
'function',
|
|
41
|
+
'bracket',
|
|
42
|
+
'bracket',
|
|
43
|
+
'operator',
|
|
44
|
+
'booleanLiteral',
|
|
45
|
+
'comment',
|
|
46
|
+
'keyword',
|
|
47
|
+
'paramDollar',
|
|
48
|
+
'paramValue',
|
|
49
|
+
'operator',
|
|
50
|
+
'numberLiteral',
|
|
51
|
+
'keyword',
|
|
52
|
+
'variable',
|
|
53
|
+
'punctuation',
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
const styleTags = tokenTypes.map((tokenType) => {
|
|
57
|
+
if (tokenType === 'none') return undefined;
|
|
58
|
+
return tokenTypeToStyleTag[tokenType];
|
|
59
|
+
});
|
|
60
|
+
const correctTags = [
|
|
61
|
+
tags.keyword,
|
|
62
|
+
tags.bracket,
|
|
63
|
+
tags.variableName,
|
|
64
|
+
tags.operator,
|
|
65
|
+
tags.typeName,
|
|
66
|
+
tags.bracket,
|
|
67
|
+
tags.separator,
|
|
68
|
+
tags.bracket,
|
|
69
|
+
tags.operator,
|
|
70
|
+
tags.typeName,
|
|
71
|
+
tags.bracket,
|
|
72
|
+
tags.separator,
|
|
73
|
+
tags.separator,
|
|
74
|
+
tags.bracket,
|
|
75
|
+
tags.bracket,
|
|
76
|
+
tags.keyword,
|
|
77
|
+
tags.variableName,
|
|
78
|
+
tags.operator,
|
|
79
|
+
tags.propertyName,
|
|
80
|
+
tags.operator,
|
|
81
|
+
tags.string,
|
|
82
|
+
tags.keyword,
|
|
83
|
+
tags.function(tags.variableName),
|
|
84
|
+
tags.operator,
|
|
85
|
+
tags.function(tags.variableName),
|
|
86
|
+
tags.bracket,
|
|
87
|
+
tags.bracket,
|
|
88
|
+
tags.operator,
|
|
89
|
+
tags.bool,
|
|
90
|
+
tags.comment,
|
|
91
|
+
tags.keyword,
|
|
92
|
+
tags.atom,
|
|
93
|
+
tags.atom,
|
|
94
|
+
tags.operator,
|
|
95
|
+
tags.number,
|
|
96
|
+
tags.keyword,
|
|
97
|
+
tags.variableName,
|
|
98
|
+
tags.punctuation,
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
styleTags.forEach((styleTag, index) => {
|
|
102
|
+
expect(styleTag).toEqual(correctTags[index]);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HighlightStyle,
|
|
3
|
+
syntaxHighlighting,
|
|
4
|
+
TagStyle,
|
|
5
|
+
} from '@codemirror/language';
|
|
6
|
+
import { Extension } from '@codemirror/state';
|
|
7
|
+
import { EditorView } from '@codemirror/view';
|
|
8
|
+
import { StyleSpec } from 'style-mod';
|
|
9
|
+
import { HighlightedCypherTokenTypes, tokenTypeToStyleTag } from './constants';
|
|
10
|
+
import {
|
|
11
|
+
byWordSvg,
|
|
12
|
+
caseSensitiveSvg,
|
|
13
|
+
downArrowSvg,
|
|
14
|
+
regexSvg,
|
|
15
|
+
replaceAllSvg,
|
|
16
|
+
replaceSvg,
|
|
17
|
+
upArrowSvg,
|
|
18
|
+
} from './themeIcons';
|
|
19
|
+
|
|
20
|
+
export interface ThemeOptions {
|
|
21
|
+
dark: boolean;
|
|
22
|
+
editorSettings: {
|
|
23
|
+
background: string;
|
|
24
|
+
foreground: string;
|
|
25
|
+
gutterForeground: string;
|
|
26
|
+
cursor: string;
|
|
27
|
+
selection: string;
|
|
28
|
+
textMatchingSelection: string;
|
|
29
|
+
searchPanel: {
|
|
30
|
+
background: string;
|
|
31
|
+
text: string;
|
|
32
|
+
buttonHoverBackground: string;
|
|
33
|
+
};
|
|
34
|
+
autoCompletionPanel: {
|
|
35
|
+
selectedColor: string;
|
|
36
|
+
backgroundColor: string;
|
|
37
|
+
matchingTextColor: string;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
highlightStyles: Partial<Record<HighlightedCypherTokenTypes, string>>;
|
|
41
|
+
inheritBgColor?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const createCypherTheme = ({
|
|
45
|
+
dark,
|
|
46
|
+
editorSettings: settings,
|
|
47
|
+
highlightStyles,
|
|
48
|
+
inheritBgColor,
|
|
49
|
+
}: ThemeOptions): Extension => {
|
|
50
|
+
const themeOptions: Record<string, StyleSpec> = {
|
|
51
|
+
'&': {
|
|
52
|
+
backgroundColor: inheritBgColor ? 'inherit' : settings.background,
|
|
53
|
+
color: settings.foreground,
|
|
54
|
+
fontVariantLigatures: 'none',
|
|
55
|
+
},
|
|
56
|
+
'& .cm-snippetField': {
|
|
57
|
+
backgroundColor: settings.autoCompletionPanel.selectedColor,
|
|
58
|
+
},
|
|
59
|
+
'&.cm-focused': {
|
|
60
|
+
outline: 'none',
|
|
61
|
+
},
|
|
62
|
+
'.cm-gutters': {
|
|
63
|
+
backgroundColor: inheritBgColor ? 'inherit' : settings.background,
|
|
64
|
+
color: settings.gutterForeground,
|
|
65
|
+
border: 'none',
|
|
66
|
+
},
|
|
67
|
+
'&.cm-editor': {
|
|
68
|
+
fontFamily: 'Fira Code, Menlo, Monaco, Lucida Console, monospace',
|
|
69
|
+
height: '100%',
|
|
70
|
+
},
|
|
71
|
+
'.cm-content': {
|
|
72
|
+
caretColor: settings.cursor,
|
|
73
|
+
},
|
|
74
|
+
'.cm-cursor, .cm-dropCursor': {
|
|
75
|
+
borderLeftColor: settings.cursor,
|
|
76
|
+
},
|
|
77
|
+
'.cm-activeLine ::not(::selection)': {
|
|
78
|
+
backgroundColor: settings.background,
|
|
79
|
+
},
|
|
80
|
+
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection ':
|
|
81
|
+
{
|
|
82
|
+
backgroundColor: settings.selection,
|
|
83
|
+
},
|
|
84
|
+
'& .cm-selectionMatch': {
|
|
85
|
+
backgroundColor: settings.textMatchingSelection,
|
|
86
|
+
},
|
|
87
|
+
'& .cm-bold': {
|
|
88
|
+
fontWeight: 'bold',
|
|
89
|
+
},
|
|
90
|
+
'& .cm-panels': {
|
|
91
|
+
backgroundColor: settings.searchPanel.background,
|
|
92
|
+
fontFamily: 'Fira Code, Menlo, Monaco, Lucida Console, monospace',
|
|
93
|
+
},
|
|
94
|
+
'& .cm-completionLabel': {
|
|
95
|
+
fontFamily: 'Fira Code, Menlo, Monaco, Lucida Console, monospace',
|
|
96
|
+
verticalAlign: 'middle',
|
|
97
|
+
},
|
|
98
|
+
'& .cm-completionMatchedText': {
|
|
99
|
+
fontWeight: 'bold',
|
|
100
|
+
color: settings.autoCompletionPanel.matchingTextColor,
|
|
101
|
+
textDecoration: 'none',
|
|
102
|
+
},
|
|
103
|
+
'& .cm-signature-help-panel': {
|
|
104
|
+
backgroundColor: settings.autoCompletionPanel.backgroundColor,
|
|
105
|
+
maxWidth: '700px',
|
|
106
|
+
maxHeight: '250px',
|
|
107
|
+
fontFamily: 'Fira Code, Menlo, Monaco, Lucida Console, monospace',
|
|
108
|
+
},
|
|
109
|
+
'& .cm-signature-help-panel-contents': {
|
|
110
|
+
overflow: 'auto',
|
|
111
|
+
maxHeight: '100%',
|
|
112
|
+
},
|
|
113
|
+
'& .cm-signature-help-panel-current-argument': {
|
|
114
|
+
color: settings.autoCompletionPanel.matchingTextColor,
|
|
115
|
+
fontWeight: 'bold',
|
|
116
|
+
},
|
|
117
|
+
'& .cm-signature-help-panel-separator': {
|
|
118
|
+
borderBottom: '1px solid #ccc',
|
|
119
|
+
},
|
|
120
|
+
'& .cm-signature-help-panel-name': {
|
|
121
|
+
padding: '5px',
|
|
122
|
+
},
|
|
123
|
+
'& .cm-signature-help-panel-description': {
|
|
124
|
+
padding: '5px',
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
'.cm-tooltip-autocomplete': {
|
|
128
|
+
maxWidth: '430px',
|
|
129
|
+
'& > ul > li[aria-selected]': {
|
|
130
|
+
backgroundColor: settings.autoCompletionPanel.selectedColor,
|
|
131
|
+
color: settings.foreground,
|
|
132
|
+
},
|
|
133
|
+
'& > ul': {
|
|
134
|
+
backgroundColor: settings.autoCompletionPanel.backgroundColor,
|
|
135
|
+
color: settings.foreground,
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
'& .cm-search.cm-panel': {
|
|
139
|
+
'& input': {
|
|
140
|
+
fontFamily: 'Fira Code, Menlo, Monaco, Lucida Console, monospace',
|
|
141
|
+
},
|
|
142
|
+
backgroundColor: settings.searchPanel.background,
|
|
143
|
+
color: settings.searchPanel.text,
|
|
144
|
+
|
|
145
|
+
'& .cm-button[name=select]': { display: 'none' },
|
|
146
|
+
'& .cm-button': {
|
|
147
|
+
backgroundImage: 'none',
|
|
148
|
+
color: settings.searchPanel.text,
|
|
149
|
+
fontSize: 0,
|
|
150
|
+
border: 'none',
|
|
151
|
+
verticalAlign: 'middle',
|
|
152
|
+
'&[name=next]::before': {
|
|
153
|
+
content: `url("data:image/svg+xml;base64,${window.btoa(
|
|
154
|
+
downArrowSvg,
|
|
155
|
+
)}")`,
|
|
156
|
+
},
|
|
157
|
+
'&[name=prev]::before': {
|
|
158
|
+
content: `url("data:image/svg+xml;base64,${window.btoa(
|
|
159
|
+
upArrowSvg,
|
|
160
|
+
)}")`,
|
|
161
|
+
},
|
|
162
|
+
'&[name=replace]::before': {
|
|
163
|
+
content: `url("data:image/svg+xml;base64,${window.btoa(
|
|
164
|
+
replaceSvg,
|
|
165
|
+
)}")`,
|
|
166
|
+
},
|
|
167
|
+
'&[name=replaceAll]::before': {
|
|
168
|
+
content: `url("data:image/svg+xml;base64,${window.btoa(
|
|
169
|
+
replaceAllSvg,
|
|
170
|
+
)}")`,
|
|
171
|
+
},
|
|
172
|
+
width: '20px',
|
|
173
|
+
height: '20px',
|
|
174
|
+
|
|
175
|
+
marginRight: '1px',
|
|
176
|
+
marginLeft: '1px',
|
|
177
|
+
borderRadius: '4px',
|
|
178
|
+
padding: '2px',
|
|
179
|
+
'&:hover': {
|
|
180
|
+
backgroundColor: settings.searchPanel.buttonHoverBackground,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
'& label': {
|
|
185
|
+
fontSize: '0',
|
|
186
|
+
height: '20px',
|
|
187
|
+
width: '20px',
|
|
188
|
+
verticalAlign: 'middle',
|
|
189
|
+
"& input[type='checkbox']": {
|
|
190
|
+
cursor: 'pointer',
|
|
191
|
+
appearance: 'none',
|
|
192
|
+
marginRight: '1px',
|
|
193
|
+
marginLeft: '1px',
|
|
194
|
+
padding: '2px',
|
|
195
|
+
height: '20px',
|
|
196
|
+
width: '20px',
|
|
197
|
+
verticalAlign: 'middle',
|
|
198
|
+
display: 'inline-flex',
|
|
199
|
+
borderRadius: '4px',
|
|
200
|
+
|
|
201
|
+
'&[name=case]::before': {
|
|
202
|
+
content: `url("data:image/svg+xml;base64,${window.btoa(
|
|
203
|
+
caseSensitiveSvg,
|
|
204
|
+
)}")`,
|
|
205
|
+
},
|
|
206
|
+
'&[name=re]::before': {
|
|
207
|
+
content: `url("data:image/svg+xml;base64,${window.btoa(
|
|
208
|
+
regexSvg,
|
|
209
|
+
)}")`,
|
|
210
|
+
},
|
|
211
|
+
'&[name=word]::before': {
|
|
212
|
+
content: `url("data:image/svg+xml;base64,${window.btoa(
|
|
213
|
+
byWordSvg,
|
|
214
|
+
)}")`,
|
|
215
|
+
},
|
|
216
|
+
'&:hover': {
|
|
217
|
+
backgroundColor: settings.searchPanel.buttonHoverBackground,
|
|
218
|
+
},
|
|
219
|
+
'&:checked': {
|
|
220
|
+
backgroundColor: settings.searchPanel.buttonHoverBackground,
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const themeExtension = EditorView.theme(themeOptions, { dark });
|
|
228
|
+
|
|
229
|
+
const styles = Object.entries(highlightStyles).map(
|
|
230
|
+
([token, color]: [HighlightedCypherTokenTypes, string]): TagStyle => ({
|
|
231
|
+
tag: tokenTypeToStyleTag[token],
|
|
232
|
+
color,
|
|
233
|
+
class: token === 'consoleCommand' ? 'cm-bold' : undefined,
|
|
234
|
+
}),
|
|
235
|
+
);
|
|
236
|
+
const highlightStyle = HighlightStyle.define(styles);
|
|
237
|
+
const extension = [themeExtension, syntaxHighlighting(highlightStyle)];
|
|
238
|
+
|
|
239
|
+
return extension;
|
|
240
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineLanguageFacet,
|
|
3
|
+
Language,
|
|
4
|
+
LanguageSupport,
|
|
5
|
+
} from '@codemirror/language';
|
|
6
|
+
import {
|
|
7
|
+
setConsoleCommandsEnabled,
|
|
8
|
+
type DbSchema,
|
|
9
|
+
} from '@neo4j-cypher/language-support';
|
|
10
|
+
import { cypherAutocomplete } from './autocomplete';
|
|
11
|
+
import { ParserAdapter } from './parser-adapter';
|
|
12
|
+
import { signatureHelpTooltip } from './signatureHelp';
|
|
13
|
+
import { cypherLinter, semanticAnalysisLinter } from './syntaxValidation';
|
|
14
|
+
|
|
15
|
+
const facet = defineLanguageFacet({
|
|
16
|
+
commentTokens: { block: { open: '/*', close: '*/' }, line: '//' },
|
|
17
|
+
closeBrackets: { brackets: ['(', '[', '{', "'", '"', '`'] },
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export type CypherConfig = {
|
|
21
|
+
lint?: boolean;
|
|
22
|
+
schema?: DbSchema;
|
|
23
|
+
useLightVersion: boolean;
|
|
24
|
+
setUseLightVersion?: (useLightVersion: boolean) => void;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function cypher(config: CypherConfig) {
|
|
28
|
+
setConsoleCommandsEnabled(true);
|
|
29
|
+
const parserAdapter = new ParserAdapter(facet, config);
|
|
30
|
+
|
|
31
|
+
const cypherLanguage = new Language(facet, parserAdapter, [], 'cypher');
|
|
32
|
+
|
|
33
|
+
return new LanguageSupport(cypherLanguage, [
|
|
34
|
+
cypherLanguage.data.of({
|
|
35
|
+
autocomplete: cypherAutocomplete(config),
|
|
36
|
+
}),
|
|
37
|
+
cypherLinter(config),
|
|
38
|
+
semanticAnalysisLinter(config),
|
|
39
|
+
signatureHelpTooltip(config),
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { validateSemantics } from '@neo4j-cypher/language-support';
|
|
2
|
+
import workerpool from 'workerpool';
|
|
3
|
+
|
|
4
|
+
workerpool.worker({ validateSemantics });
|
|
5
|
+
|
|
6
|
+
type LinterArgs = Parameters<typeof validateSemantics>;
|
|
7
|
+
|
|
8
|
+
export type LinterTask = workerpool.Promise<
|
|
9
|
+
ReturnType<typeof validateSemantics>
|
|
10
|
+
>;
|
|
11
|
+
|
|
12
|
+
export type LintWorker = {
|
|
13
|
+
validateSemantics: (...args: LinterArgs) => LinterTask;
|
|
14
|
+
};
|