@neo4j-cypher/react-codemirror
Advanced tools
Comparing version 2.0.0-canary-48fd427 to 2.0.0-canary-522bae6
# @neo4j-cypher/react-codemirror | ||
## 2.0.0-next.12 | ||
### Patch Changes | ||
- dcbe67d: Expose moveFocusOnTab property on the CypherEditor component to conditionally disable tab keymappings | ||
- e9621c8: Re-export language support from react codemirror | ||
- Updated dependencies [2e72ac8] | ||
- @neo4j-cypher/language-support@2.0.0-next.9 | ||
## 2.0.0-next.11 | ||
### Patch Changes | ||
- Updated dependencies [05663bd] | ||
- @neo4j-cypher/language-support@2.0.0-next.8 | ||
## 2.0.0-next.10 | ||
### Patch Changes | ||
- bb7e9d3: Simplify detection and handling of value prop updates | ||
## 2.0.0-next.9 | ||
### Patch Changes | ||
- fbd5f7e: allow signature help panel to render below editor when there's not enough space above it | ||
- 09dfae2: Add an ariaLabel prop to CypherEditor | ||
- 7154e94: Fix bug causing debouncing to override value | ||
- 62c152f: execute single line query on enter by default | ||
- cbfc75e: Fix a bug causing debounced value updates to get cancelled erroneously | ||
- 04ae35e: Set initial latestDispatchedValue and flush debounced changes onExecute | ||
Add tests for debounce behaviour | ||
- Updated dependencies [3661e9d] | ||
- Updated dependencies [b76af58] | ||
- Updated dependencies [21699b7] | ||
- Updated dependencies [6afc0e3] | ||
- Updated dependencies [39b924d] | ||
- @neo4j-cypher/language-support@2.0.0-next.7 | ||
## 2.0.0-next.8 | ||
@@ -4,0 +44,0 @@ |
@@ -25,2 +25,9 @@ import { EditorState, Extension } from '@codemirror/state'; | ||
/** | ||
* If true, pressing enter will add a new line to the editor and cmd/ctrl + enter will execute the query. | ||
* Otherwise pressing enter on a single line will execute the query. | ||
* | ||
* @default false | ||
*/ | ||
newLineOnEnter?: boolean; | ||
/** | ||
* The editor history navigable via up/down arrow keys. Order newest to oldest. | ||
@@ -132,5 +139,14 @@ * Add to this list with the `onExecute` callback for REPL style history. | ||
/** | ||
* String value to assign to the aria-label attribute of the editor | ||
* String value to assign to the aria-label attribute of the editor. | ||
*/ | ||
ariaLabel?: string; | ||
/** | ||
* Whether keybindings for inserting indents with the Tab key should be disabled. | ||
* | ||
* true will not create keybindings for inserting indents. | ||
* false will create keybindings for inserting indents. | ||
* | ||
* @default false | ||
*/ | ||
moveFocusOnTab?: boolean; | ||
} | ||
@@ -137,0 +153,0 @@ type CypherEditorState = { |
import { jsx as _jsx } from "react/jsx-runtime"; | ||
import { insertNewline } from '@codemirror/commands'; | ||
import { Annotation, Compartment, EditorState, } from '@codemirror/state'; | ||
@@ -6,2 +7,3 @@ import { EditorView, keymap, lineNumbers, placeholder, } from '@codemirror/view'; | ||
import { Component, createRef } from 'react'; | ||
import { DEBOUNCE_TIME } from './constants'; | ||
import { replaceHistory, replMode as historyNavigation, } from './historyNavigation'; | ||
@@ -12,6 +14,20 @@ import { cypher } from './lang-cypher/langCypher'; | ||
import { getThemeExtension } from './themes'; | ||
const executeKeybinding = (onExecute) => onExecute | ||
? [ | ||
{ | ||
const executeKeybinding = (onExecute, newLineOnEnter, flush) => { | ||
const keybindings = { | ||
'Shift-Enter': { | ||
key: 'Shift-Enter', | ||
run: insertNewline, | ||
}, | ||
'Ctrl-Enter': { | ||
key: 'Ctrl-Enter', | ||
run: () => true, | ||
}, | ||
Enter: { | ||
key: 'Enter', | ||
run: insertNewline, | ||
}, | ||
}; | ||
if (onExecute) { | ||
keybindings['Ctrl-Enter'] = { | ||
key: 'Ctrl-Enter', | ||
mac: 'Mod-Enter', | ||
@@ -22,2 +38,3 @@ preventDefault: true, | ||
if (doc.trim() !== '') { | ||
flush?.(); | ||
onExecute(doc); | ||
@@ -27,5 +44,25 @@ } | ||
}, | ||
}, | ||
] | ||
: []; | ||
}; | ||
if (!newLineOnEnter) { | ||
keybindings['Enter'] = { | ||
key: 'Enter', | ||
preventDefault: true, | ||
run: (view) => { | ||
const doc = view.state.doc.toString(); | ||
if (doc.includes('\n')) { | ||
// Returning false means the event will mark the event | ||
// as not handled and the default behavior will be executed | ||
return false; | ||
} | ||
if (doc.trim() !== '') { | ||
flush?.(); | ||
onExecute(doc); | ||
} | ||
return true; | ||
}, | ||
}; | ||
} | ||
} | ||
return Object.values(keybindings); | ||
}; | ||
const themeCompartment = new Compartment(); | ||
@@ -98,8 +135,12 @@ const keyBindingCompartment = new Compartment(); | ||
lineNumbers: true, | ||
newLineOnEnter: false, | ||
moveFocusOnTab: false, | ||
}; | ||
debouncedOnChange = this.props.onChange | ||
? debounce(this.props.onChange, 200) | ||
? debounce(((value, viewUpdate) => { | ||
this.props.onChange(value, viewUpdate); | ||
}), DEBOUNCE_TIME) | ||
: undefined; | ||
componentDidMount() { | ||
const { theme, extraKeybindings, lineWrap, overrideThemeBackgroundColor, schema, lint, showSignatureTooltipBelow, featureFlags, onExecute, } = this.props; | ||
const { theme, extraKeybindings, lineWrap, overrideThemeBackgroundColor, schema, lint, showSignatureTooltipBelow, featureFlags, onExecute, newLineOnEnter, } = this.props; | ||
this.schemaRef.current = { | ||
@@ -135,5 +176,8 @@ schema, | ||
extensions: [ | ||
keyBindingCompartment.of(keymap.of([...executeKeybinding(onExecute), ...extraKeybindings])), | ||
keyBindingCompartment.of(keymap.of([ | ||
...executeKeybinding(onExecute, newLineOnEnter, () => this.debouncedOnChange?.flush()), | ||
...extraKeybindings, | ||
])), | ||
historyNavigation(this.props), | ||
basicNeo4jSetup(), | ||
basicNeo4jSetup(this.props), | ||
themeCompartment.of(themeExtension), | ||
@@ -179,3 +223,6 @@ changeListener, | ||
const currentCmValue = this.editorView.current.state?.doc.toString() ?? ''; | ||
if (this.props.value !== undefined && currentCmValue !== this.props.value) { | ||
if (this.props.value !== undefined && // If the component becomes uncontolled, we just leave the value as is | ||
this.props.value !== prevProps.value && // The value prop has changed, we need to update the editor | ||
this.props.value !== currentCmValue // No need to dispatch an update if the value is the same | ||
) { | ||
this.editorView.current.dispatch({ | ||
@@ -221,3 +268,3 @@ changes: { | ||
effects: keyBindingCompartment.reconfigure(keymap.of([ | ||
...executeKeybinding(this.props.onExecute), | ||
...executeKeybinding(this.props.onExecute, this.props.newLineOnEnter, () => this.debouncedOnChange?.flush()), | ||
...this.props.extraKeybindings, | ||
@@ -224,0 +271,0 @@ ])), |
@@ -41,2 +41,20 @@ import { jsx as _jsx } from "react/jsx-runtime"; | ||
}); | ||
test('get completions when typing in controlled component', async ({ mount, page, }) => { | ||
let value = ''; | ||
const onChange = (val) => { | ||
value = val; | ||
void component.update(_jsx(CypherEditor, { value: val, onChange: onChange })); | ||
}; | ||
const component = await mount(_jsx(CypherEditor, { value: value, onChange: onChange })); | ||
const textField = page.getByRole('textbox'); | ||
await textField.fill('RETU'); | ||
await page.waitForTimeout(500); // wait for debounce | ||
await expect(page.locator('.cm-tooltip-autocomplete').getByText('RETURN')).toBeVisible(); | ||
// We need to wait for the editor to realise there is a completion open | ||
// so that it does not just indent with tab key | ||
await page.waitForTimeout(500); | ||
await textField.press('Tab'); | ||
await expect(page.locator('.cm-tooltip-autocomplete')).not.toBeVisible(); | ||
await expect(component).toContainText('RETURN'); | ||
}); | ||
test('can complete labels', async ({ mount, page }) => { | ||
@@ -43,0 +61,0 @@ const component = await mount(_jsx(CypherEditor, { schema: { |
@@ -17,3 +17,5 @@ import { jsx as _jsx } from "react/jsx-runtime"; | ||
await expect(page.getByText('second')).toBeVisible(); | ||
// First arrow down is to get to end of line | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await expect(page.getByText('first')).toBeVisible(); | ||
@@ -23,4 +25,48 @@ await editorPage.getEditor().press('ArrowDown'); | ||
}); | ||
test('can execute queries and see them in history', async ({ page, mount }) => { | ||
test('can add new lines without onExecute', async ({ page, mount }) => { | ||
const editorPage = new CypherEditorPage(page); | ||
const editorComponent = await mount(_jsx(CypherEditor, {})); | ||
// Ctrl-Enter does nothing when onExecute is false | ||
await editorPage.getEditor().press('Control+Enter'); | ||
await expect(editorComponent).toHaveText('1\n', { | ||
useInnerText: true, | ||
}); | ||
// Enter adds new lines | ||
await editorPage.getEditor().fill('Brock'); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
await expect(editorComponent).toHaveText('1\n2\n3\nBrock', { | ||
useInnerText: true, | ||
}); | ||
// Shift-Enter adds new lines | ||
await editorPage.getEditor().press('Shift+Enter'); | ||
await editorPage.getEditor().press('Shift+Enter'); | ||
await expect(editorComponent).toHaveText('1\n2\n3\n4\n5\nBrock', { | ||
useInnerText: true, | ||
}); | ||
}); | ||
test('can add new lines with newLineOnEnter and without onExecute', async ({ page, mount, }) => { | ||
const editorPage = new CypherEditorPage(page); | ||
const editorComponent = await mount(_jsx(CypherEditor, { newLineOnEnter: true })); | ||
// Ctrl-Enter does nothing when onExecute is false | ||
await editorPage.getEditor().press('Control+Enter'); | ||
await expect(editorComponent).toHaveText('1\n', { | ||
useInnerText: true, | ||
}); | ||
// Enter adds new lines | ||
await editorPage.getEditor().fill('Brock'); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
await expect(editorComponent).toHaveText('1\n2\n3\nBrock', { | ||
useInnerText: true, | ||
}); | ||
// Shift-Enter adds new lines | ||
await editorPage.getEditor().press('Shift+Enter'); | ||
await editorPage.getEditor().press('Shift+Enter'); | ||
await expect(editorComponent).toHaveText('1\n2\n3\n4\n5\nBrock', { | ||
useInnerText: true, | ||
}); | ||
}); | ||
test('can execute queries and see them in history with newLineOnEnter', async ({ page, mount, }) => { | ||
const editorPage = new CypherEditorPage(page); | ||
const initialValue = `MATCH (n) | ||
@@ -32,3 +78,3 @@ RETURN n;`; | ||
}; | ||
const editor = await mount(_jsx(CypherEditor, { value: initialValue, history: history, onExecute: onExecute })); | ||
const editor = await mount(_jsx(CypherEditor, { value: initialValue, history: history, onExecute: onExecute, newLineOnEnter: true })); | ||
// Execute initial query | ||
@@ -43,3 +89,3 @@ await editorPage.getEditor().press('Control+Enter'); | ||
expect(history.length).toBe(1); | ||
// Ensure only enter doesn't execute query | ||
// Ensure cmd+enter is required in multiline | ||
await editorPage.getEditor().fill('multiline'); | ||
@@ -49,3 +95,3 @@ await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Shift+Enter'); | ||
await page.keyboard.type('entry'); | ||
@@ -72,2 +118,4 @@ expect(history.length).toBe(1); | ||
// arrow movements don't matter until bottom is hit | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowUp'); | ||
@@ -77,2 +125,5 @@ await editorPage.getEditor().press('ArrowUp'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
// editor still multiline | ||
@@ -84,2 +135,43 @@ await expect(page.getByText('multiline')).toBeVisible(); | ||
}); | ||
test('can execute queries without newLineOnEnter', async ({ page, mount }) => { | ||
const editorPage = new CypherEditorPage(page); | ||
const initialValue = 'Brock'; | ||
const history = []; | ||
const onExecute = (cmd) => { | ||
history.unshift(cmd); | ||
}; | ||
const editorComponent = await mount(_jsx(CypherEditor, { value: initialValue, onExecute: onExecute })); | ||
// Cmd/Control still executes initial query | ||
await editorPage.getEditor().press('Control+Enter'); | ||
await editorPage.getEditor().press('Meta+Enter'); | ||
expect(history.length).toBe(1); | ||
// Ensure query execution doesn't fire if the query is only whitespace | ||
await editorPage.getEditor().fill(' '); | ||
await editorPage.getEditor().press('Control+Enter'); | ||
await editorPage.getEditor().press('Meta+Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
expect(history.length).toBe(1); | ||
// Ensure enter executes query when in single line | ||
await editorPage.getEditor().fill('Misty'); | ||
await editorPage.getEditor().press('Enter'); | ||
expect(history.length).toBe(2); | ||
expect(history).toEqual(['Misty', 'Brock']); | ||
// Ensure cmd+enter is required in multiline | ||
await editorPage.getEditor().fill('multiline'); | ||
await editorPage.getEditor().press('Shift+Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('A'); | ||
// line numbers and the text | ||
await expect(editorComponent).toHaveText('1\n2\n3\nmultiline\nA', { | ||
useInnerText: true, | ||
}); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
expect(history.length).toBe(2); | ||
await editorPage.getEditor().press('Control+Enter'); | ||
await editorPage.getEditor().press('Meta+Enter'); | ||
expect(history.length).toBe(3); | ||
}); | ||
test('can navigate with cmd+up as well', async ({ page, mount }) => { | ||
@@ -92,2 +184,3 @@ const editorPage = new CypherEditorPage(page); | ||
await mount(_jsx(CypherEditor, { value: initialValue, history: [ | ||
'first', | ||
`one | ||
@@ -97,3 +190,2 @@ multiline | ||
.`, | ||
'second', | ||
], onExecute: () => { | ||
@@ -103,18 +195,18 @@ /* needed to turn on history movements */ | ||
await editorPage.getEditor().press(metaUp); | ||
await expect(page.getByText('first')).toBeVisible(); | ||
// move in history | ||
await editorPage.getEditor().press(metaUp); | ||
await expect(page.getByText('multiline')).toBeVisible(); | ||
// Single meta up moves all the way to top of editor on mac | ||
// Single meta down moves all the way to top of editor on mac | ||
if (isMac) { | ||
await editorPage.getEditor().press(metaUp); | ||
await editorPage.getEditor().press(metaDown); | ||
} | ||
else { | ||
await editorPage.getEditor().press('ArrowUp'); | ||
await editorPage.getEditor().press('ArrowUp'); | ||
await editorPage.getEditor().press('ArrowUp'); | ||
await editorPage.getEditor().press('ArrowUp'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
} | ||
// move in history | ||
await editorPage.getEditor().press(metaUp); | ||
await expect(page.getByText('second')).toBeVisible(); | ||
await editorPage.getEditor().press(metaDown); | ||
await expect(page.getByText('multiline')).toBeVisible(); | ||
await expect(page.getByText('first')).toBeVisible(); | ||
}); | ||
@@ -121,0 +213,0 @@ test('test onExecute', async ({ page, mount }) => { |
@@ -24,3 +24,5 @@ import { jsx as _jsx } from "react/jsx-runtime"; | ||
const textField = page.getByRole('textbox'); | ||
await textField.fill(''); | ||
await textField.fill('RETURN 12'); | ||
await expect(textField).toHaveText('RETURN 12'); | ||
// editor update is debounced, retry wait for debounced | ||
@@ -30,6 +32,2 @@ await expect(() => { | ||
}).toPass({ intervals: [300, 300, 1000] }); | ||
await page.keyboard.type('34'); | ||
await expect(() => { | ||
expect(editorValueCopy).toBe('RETURN 12'); | ||
}).toPass({ intervals: [300, 300, 1000] }); | ||
}); | ||
@@ -36,0 +34,0 @@ test('can complete RETURN', async ({ page, mount }) => { |
@@ -84,3 +84,3 @@ import { StateEffect } from '@codemirror/state'; | ||
effects: moveInHistory.of(direction), | ||
selection: { anchor: view.state.doc.length }, | ||
selection: { anchor: direction === 'BACK' ? 0 : view.state.doc.length }, | ||
})); | ||
@@ -87,0 +87,0 @@ const updatedHistory = view.state.field(historyState, false); |
@@ -1,4 +0,4 @@ | ||
export { CypherParser, _internalFeatureFlags, } from '@neo4j-cypher/language-support'; | ||
export * as LanguageSupport from '@neo4j-cypher/language-support'; | ||
export { CypherEditor } from './CypherEditor'; | ||
export { cypher } from './lang-cypher/langCypher'; | ||
export { darkThemeConstants, lightThemeConstants } from './themes'; |
@@ -1,2 +0,2 @@ | ||
export { CypherParser, _internalFeatureFlags, } from '@neo4j-cypher/language-support'; | ||
export * as LanguageSupport from '@neo4j-cypher/language-support'; | ||
export { CypherEditor } from './CypherEditor'; | ||
@@ -3,0 +3,0 @@ export { cypher } from './lang-cypher/langCypher'; |
@@ -45,5 +45,5 @@ import { snippet, } from '@codemirror/autocomplete'; | ||
export const cypherAutocomplete = (config) => (context) => { | ||
const textUntilCursor = context.state.doc.toString().slice(0, context.pos); | ||
const documentText = context.state.doc.toString(); | ||
const triggerCharacters = ['.', ':', '{', '$', ')']; | ||
const lastCharacter = textUntilCursor.slice(-1); | ||
const lastCharacter = documentText.at(context.pos - 1); | ||
const lastWord = context.matchBefore(/\w*/); | ||
@@ -58,4 +58,4 @@ const inWord = lastWord.from !== lastWord.to; | ||
} | ||
const options = autocomplete(textUntilCursor, config.schema ?? {}, undefined, context.explicit); | ||
if (config.featureFlags.signatureInfoOnAutoCompletions) { | ||
const options = autocomplete(documentText, config.schema ?? {}, context.pos, context.explicit); | ||
if (config.featureFlags?.signatureInfoOnAutoCompletions) { | ||
return { | ||
@@ -62,0 +62,0 @@ from: context.matchBefore(/(\w|\$)*$/).from, |
import { tags } from '@lezer/highlight'; | ||
import { applySyntaxColouring, CypherTokenType, } from '@neo4j-cypher/language-support'; | ||
import { expect, test } from 'vitest'; | ||
import { tokenTypeToStyleTag } from './constants'; | ||
@@ -4,0 +5,0 @@ const cypherQueryWithAllTokenTypes = `MATCH (variable :Label)-[:REL_TYPE]->() |
@@ -5,5 +5,3 @@ import { linter } from '@codemirror/lint'; | ||
import workerpool from 'workerpool'; | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore ignore: https://v3.vitejs.dev/guide/features.html#import-with-query-suffixes | ||
import WorkerURL from './lintWorker?url&worker'; | ||
import WorkerURL from './lintWorker?worker&url'; | ||
const pool = workerpool.pool(WorkerURL, { | ||
@@ -41,4 +39,3 @@ minWorkers: 2, | ||
const statements = parse.statementsParsing; | ||
const anySyntacticError = statements.filter((statement) => statement.diagnostics.length !== 0) | ||
.length > 0; | ||
const anySyntacticError = statements.some((statement) => statement.syntaxErrors.length !== 0); | ||
if (anySyntacticError) { | ||
@@ -45,0 +42,0 @@ return []; |
import { tokens } from '@neo4j-ndl/base'; | ||
import { expect, test } from 'vitest'; | ||
import { tokens as tokensCopy } from './ndlTokensCopy'; | ||
@@ -3,0 +4,0 @@ /* |
import { Extension } from '@codemirror/state'; | ||
export declare const basicNeo4jSetup: () => Extension[]; | ||
type SetupProps = { | ||
moveFocusOnTab?: boolean; | ||
}; | ||
export declare const basicNeo4jSetup: ({ moveFocusOnTab, }: SetupProps) => Extension[]; | ||
export {}; |
@@ -27,3 +27,3 @@ import { acceptCompletion, autocompletion, clearSnippet, closeBrackets, closeBracketsKeymap, closeCompletion, completionKeymap, nextSnippetField, prevSnippetField, snippetKeymap, } from '@codemirror/autocomplete'; | ||
}; | ||
export const basicNeo4jSetup = () => { | ||
export const basicNeo4jSetup = ({ moveFocusOnTab = false, }) => { | ||
const keymaps = [ | ||
@@ -37,18 +37,18 @@ closeBracketsKeymap, | ||
lintKeymap, | ||
{ | ||
].flat(); | ||
if (!moveFocusOnTab) { | ||
keymaps.push({ | ||
key: 'Tab', | ||
preventDefault: true, | ||
run: acceptCompletion, | ||
}, | ||
{ | ||
}, { | ||
key: 'Tab', | ||
preventDefault: true, | ||
run: insertTab, | ||
}, | ||
{ | ||
}, { | ||
key: 'Shift-Tab', | ||
preventDefault: true, | ||
run: indentLess, | ||
}, | ||
].flat(); | ||
}); | ||
} | ||
const extensions = []; | ||
@@ -55,0 +55,0 @@ extensions.push(highlightSpecialChars()); |
@@ -20,3 +20,3 @@ { | ||
], | ||
"version": "2.0.0-canary-48fd427", | ||
"version": "2.0.0-canary-522bae6", | ||
"main": "./dist/index.js", | ||
@@ -29,3 +29,3 @@ "types": "./dist/index.d.ts", | ||
"clean": "rm -rf dist", | ||
"test": "jest", | ||
"test": "vitest run", | ||
"test:e2e": "playwright test -c playwright-ct.config.ts", | ||
@@ -47,12 +47,12 @@ "test:e2e-ui": "playwright test -c playwright-ct.config.ts --ui", | ||
"dependencies": { | ||
"@codemirror/autocomplete": "^6.5.1", | ||
"@codemirror/commands": "^6.2.2", | ||
"@codemirror/language": "^6.6.0", | ||
"@codemirror/lint": "^6.2.2", | ||
"@codemirror/search": "^6.5.0", | ||
"@codemirror/state": "^6.2.1", | ||
"@codemirror/view": "^6.13.2", | ||
"@codemirror/autocomplete": "^6.17.0", | ||
"@codemirror/commands": "^6.6.0", | ||
"@codemirror/language": "^6.10.2", | ||
"@codemirror/lint": "^6.8.1", | ||
"@codemirror/search": "^6.5.6", | ||
"@codemirror/state": "^6.4.1", | ||
"@codemirror/view": "^6.29.1", | ||
"@lezer/common": "^1.0.2", | ||
"@lezer/highlight": "^1.1.3", | ||
"@neo4j-cypher/language-support": "2.0.0-canary-48fd427", | ||
"@neo4j-cypher/language-support": "2.0.0-canary-522bae6", | ||
"@types/prismjs": "^1.26.3", | ||
@@ -69,13 +69,15 @@ "@types/workerpool": "^6.4.7", | ||
"@neo4j-ndl/base": "^1.10.1", | ||
"@playwright/experimental-ct-react": "^1.39.0", | ||
"@playwright/test": "^1.36.2", | ||
"@playwright/experimental-ct-react": "^1.45.3", | ||
"@playwright/test": "^1.45.3", | ||
"@types/lodash.debounce": "^4.0.9", | ||
"@types/react": "^18.0.28", | ||
"@vitejs/plugin-react": "^3.1.0", | ||
"@vitejs/plugin-react": "^4.3.1", | ||
"concurrently": "^8.2.1", | ||
"esbuild": "^0.19.4", | ||
"jsdom": "^24.1.1", | ||
"lodash": "^4.17.21", | ||
"playwright": "^1.36.2", | ||
"playwright": "^1.45.3", | ||
"react": "^18.2.0", | ||
"typescript": "^4.9.5" | ||
"typescript": "^4.9.5", | ||
"vite": "^5.3.5" | ||
}, | ||
@@ -82,0 +84,0 @@ "peerDependencies": { |
@@ -100,3 +100,3 @@ import { Extension, StateEffect } from '@codemirror/state'; | ||
effects: moveInHistory.of(direction), | ||
selection: { anchor: view.state.doc.length }, | ||
selection: { anchor: direction === 'BACK' ? 0 : view.state.doc.length }, | ||
}), | ||
@@ -103,0 +103,0 @@ ); |
@@ -1,7 +0,4 @@ | ||
export { | ||
CypherParser, | ||
_internalFeatureFlags, | ||
} from '@neo4j-cypher/language-support'; | ||
export * as LanguageSupport from '@neo4j-cypher/language-support'; | ||
export { CypherEditor } from './CypherEditor'; | ||
export { cypher } from './lang-cypher/langCypher'; | ||
export { darkThemeConstants, lightThemeConstants } from './themes'; |
@@ -60,6 +60,6 @@ import { | ||
(config) => (context) => { | ||
const textUntilCursor = context.state.doc.toString().slice(0, context.pos); | ||
const documentText = context.state.doc.toString(); | ||
const triggerCharacters = ['.', ':', '{', '$', ')']; | ||
const lastCharacter = textUntilCursor.slice(-1); | ||
const lastCharacter = documentText.at(context.pos - 1); | ||
@@ -81,9 +81,9 @@ const lastWord = context.matchBefore(/\w*/); | ||
const options = autocomplete( | ||
textUntilCursor, | ||
documentText, | ||
config.schema ?? {}, | ||
undefined, | ||
context.pos, | ||
context.explicit, | ||
); | ||
if (config.featureFlags.signatureInfoOnAutoCompletions) { | ||
if (config.featureFlags?.signatureInfoOnAutoCompletions) { | ||
return { | ||
@@ -90,0 +90,0 @@ from: context.matchBefore(/(\w|\$)*$/).from, |
@@ -6,2 +6,3 @@ import { tags } from '@lezer/highlight'; | ||
} from '@neo4j-cypher/language-support'; | ||
import { expect, test } from 'vitest'; | ||
import { tokenTypeToStyleTag } from './constants'; | ||
@@ -8,0 +9,0 @@ |
@@ -8,8 +8,5 @@ import { Diagnostic, linter } from '@codemirror/lint'; | ||
import type { LinterTask, LintWorker } from './lintWorker'; | ||
import WorkerURL from './lintWorker?worker&url'; | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore ignore: https://v3.vitejs.dev/guide/features.html#import-with-query-suffixes | ||
import WorkerURL from './lintWorker?url&worker'; | ||
const pool = workerpool.pool(WorkerURL as string, { | ||
const pool = workerpool.pool(WorkerURL, { | ||
minWorkers: 2, | ||
@@ -61,5 +58,5 @@ workerOpts: { type: 'module' }, | ||
const anySyntacticError = | ||
statements.filter((statement) => statement.diagnostics.length !== 0) | ||
.length > 0; | ||
const anySyntacticError = statements.some( | ||
(statement) => statement.syntaxErrors.length !== 0, | ||
); | ||
@@ -66,0 +63,0 @@ if (anySyntacticError) { |
import { tokens } from '@neo4j-ndl/base'; | ||
import { expect, test } from 'vitest'; | ||
import { tokens as tokensCopy } from './ndlTokensCopy'; | ||
@@ -3,0 +4,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
671838
138
8964
14
16
28
+ Added@neo4j-cypher/language-support@2.0.0-canary-522bae6(transitive)
- Removed@neo4j-cypher/language-support@2.0.0-canary-48fd427(transitive)
Updated@codemirror/commands@^6.6.0
Updated@codemirror/language@^6.10.2
Updated@codemirror/lint@^6.8.1
Updated@codemirror/search@^6.5.6
Updated@codemirror/state@^6.4.1
Updated@codemirror/view@^6.29.1