mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Editor: Ignore closing brace when it was added by editor (#21172)
* Editor: Ignore closing brace when it was added by editor - brace completion gets annoying if the user still types closing brace - this change marks automatically added closing braces and when the user types a closing brace at that position, it will be overridden instead of added * Fix label suggestions * Correct brace behavior, but broken completion * Rewrite auto-match detection with annotations
This commit is contained in:
@@ -18,7 +18,7 @@ describe('braces', () => {
|
|||||||
const value = Plain.deserialize('');
|
const value = Plain.deserialize('');
|
||||||
const editor = shallow<Editor>(<Editor value={value} />);
|
const editor = shallow<Editor>(<Editor value={value} />);
|
||||||
const event = new window.KeyboardEvent('keydown', { key: '(' });
|
const event = new window.KeyboardEvent('keydown', { key: '(' });
|
||||||
handler(event as Event, editor.instance() as any, nextMock);
|
expect(handler(event as Event, editor.instance() as any, nextMock)).toBeTruthy();
|
||||||
expect(Plain.serialize(editor.instance().value)).toEqual('()');
|
expect(Plain.serialize(editor.instance().value)).toEqual('()');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,4 +37,24 @@ describe('braces', () => {
|
|||||||
const handled = handler(event as Event, editor.instance().moveForward(5) as any, nextMock);
|
const handled = handler(event as Event, editor.instance().moveForward(5) as any, nextMock);
|
||||||
expect(handled).toBeFalsy();
|
expect(handled).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('overrides an automatically inserted brace', () => {
|
||||||
|
const value = Plain.deserialize('');
|
||||||
|
const editor = shallow<Editor>(<Editor value={value} />);
|
||||||
|
const opening = new window.KeyboardEvent('keydown', { key: '(' });
|
||||||
|
expect(handler(opening as Event, editor.instance() as any, nextMock)).toBeTruthy();
|
||||||
|
const closing = new window.KeyboardEvent('keydown', { key: ')' });
|
||||||
|
expect(handler(closing as Event, editor.instance() as any, nextMock)).toBeTruthy();
|
||||||
|
expect(Plain.serialize(editor.instance().value)).toEqual('()');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip('does not override manually inserted braces', () => {
|
||||||
|
const value = Plain.deserialize('');
|
||||||
|
const editor = shallow<Editor>(<Editor value={value} />);
|
||||||
|
const event1 = new window.KeyboardEvent('keydown', { key: ')' });
|
||||||
|
expect(handler(event1 as Event, editor.instance() as any, nextMock)).toBeFalsy();
|
||||||
|
const event2 = new window.KeyboardEvent('keydown', { key: ')' });
|
||||||
|
expect(handler(event2 as Event, editor.instance().moveBackward(1) as any, nextMock)).toBeFalsy();
|
||||||
|
expect(Plain.serialize(editor.instance().value)).toEqual('))');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Plugin } from '@grafana/slate-react';
|
import { Plugin } from '@grafana/slate-react';
|
||||||
import { Editor as CoreEditor } from 'slate';
|
import { Editor as CoreEditor, Annotation } from 'slate';
|
||||||
|
|
||||||
const BRACES: any = {
|
const BRACES: any = {
|
||||||
'[': ']',
|
'[': ']',
|
||||||
@@ -7,6 +7,8 @@ const BRACES: any = {
|
|||||||
'(': ')',
|
'(': ')',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MATCH_MARK = 'brace_match';
|
||||||
|
|
||||||
export function BracesPlugin(): Plugin {
|
export function BracesPlugin(): Plugin {
|
||||||
return {
|
return {
|
||||||
onKeyDown(event: Event, editor: CoreEditor, next: Function) {
|
onKeyDown(event: Event, editor: CoreEditor, next: Function) {
|
||||||
@@ -17,7 +19,6 @@ export function BracesPlugin(): Plugin {
|
|||||||
case '(':
|
case '(':
|
||||||
case '{':
|
case '{':
|
||||||
case '[': {
|
case '[': {
|
||||||
keyEvent.preventDefault();
|
|
||||||
const {
|
const {
|
||||||
start: { offset: startOffset, key: startKey },
|
start: { offset: startOffset, key: startKey },
|
||||||
end: { offset: endOffset, key: endKey },
|
end: { offset: endOffset, key: endKey },
|
||||||
@@ -27,21 +28,67 @@ export function BracesPlugin(): Plugin {
|
|||||||
|
|
||||||
// If text is selected, wrap selected text in parens
|
// If text is selected, wrap selected text in parens
|
||||||
if (value.selection.isExpanded) {
|
if (value.selection.isExpanded) {
|
||||||
|
keyEvent.preventDefault();
|
||||||
editor
|
editor
|
||||||
.insertTextByKey(startKey, startOffset, keyEvent.key)
|
.insertTextByKey(startKey, startOffset, keyEvent.key)
|
||||||
.insertTextByKey(endKey, endOffset + 1, BRACES[keyEvent.key])
|
.insertTextByKey(endKey, endOffset + 1, BRACES[keyEvent.key])
|
||||||
.moveEndBackward(1);
|
.moveEndBackward(1);
|
||||||
|
return true;
|
||||||
} else if (
|
} else if (
|
||||||
|
// Insert matching brace when there is no input after caret
|
||||||
focusOffset === text.length ||
|
focusOffset === text.length ||
|
||||||
text[focusOffset] === ' ' ||
|
text[focusOffset] === ' ' ||
|
||||||
Object.values(BRACES).includes(text[focusOffset])
|
Object.values(BRACES).includes(text[focusOffset])
|
||||||
) {
|
) {
|
||||||
editor.insertText(`${keyEvent.key}${BRACES[keyEvent.key]}`).moveBackward(1);
|
keyEvent.preventDefault();
|
||||||
} else {
|
const complement = BRACES[keyEvent.key];
|
||||||
editor.insertText(keyEvent.key);
|
const matchAnnotation = {
|
||||||
}
|
key: `${MATCH_MARK}-${Date.now()}`,
|
||||||
|
type: `${MATCH_MARK}-${complement}`,
|
||||||
|
anchor: {
|
||||||
|
key: startKey,
|
||||||
|
offset: startOffset,
|
||||||
|
object: 'point',
|
||||||
|
},
|
||||||
|
focus: {
|
||||||
|
key: endKey,
|
||||||
|
offset: endOffset + 1,
|
||||||
|
object: 'point',
|
||||||
|
},
|
||||||
|
object: 'annotation',
|
||||||
|
} as Annotation;
|
||||||
|
editor
|
||||||
|
.insertText(keyEvent.key)
|
||||||
|
.insertText(complement)
|
||||||
|
.addAnnotation(matchAnnotation)
|
||||||
|
.moveBackward(1);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ')':
|
||||||
|
case '}':
|
||||||
|
case ']': {
|
||||||
|
const text = value.anchorText.text;
|
||||||
|
const offset = value.selection.anchor.offset;
|
||||||
|
const nextChar = text[offset];
|
||||||
|
// Handle closing brace when it's already the next character
|
||||||
|
const complement = keyEvent.key;
|
||||||
|
const annotationType = `${MATCH_MARK}-${complement}`;
|
||||||
|
const annotation = value.annotations.find(
|
||||||
|
a => a?.type === annotationType && a.anchor.key === value.anchorText.key
|
||||||
|
);
|
||||||
|
if (annotation && nextChar === complement && !value.selection.isExpanded) {
|
||||||
|
keyEvent.preventDefault();
|
||||||
|
editor
|
||||||
|
.moveFocusForward(1)
|
||||||
|
.removeAnnotation(annotation)
|
||||||
|
.moveAnchorForward(1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'Backspace': {
|
case 'Backspace': {
|
||||||
|
|||||||
Reference in New Issue
Block a user