Skip to content

Commit 8d97006

Browse files
authored
fix(compass-editor): fix tab behavior with selection COMPASS-7013 (#4940)
1 parent a1f53d1 commit 8d97006

File tree

1 file changed

+42
-28
lines changed

1 file changed

+42
-28
lines changed

packages/compass-editor/src/json-editor.tsx

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -92,26 +92,46 @@ function isReadOnly(state: EditorState): boolean {
9292
return state.facet(EditorState.readOnly);
9393
}
9494

95-
// Breaks keyboard navigation out of the editor, but we want that
96-
const breakFocusOutBinding: KeyBinding = {
97-
// https://codemirror.net/examples/tab/
98-
...indentWithTab,
99-
// `indentWithTab` will also do indent when tab is pressed without Shift (like
100-
// in browser devtools for example). We want to just input tab symbol in that
101-
// case
102-
run({ state, dispatch }) {
103-
if (isReadOnly(state)) {
95+
// Breaks keyboard navigation out of the editor, but we want that.
96+
// A user has to hit `Escape` then `Tab` to enter keyboard navigation.
97+
// Note that the ordering of these matters as no more key handlers are called
98+
// after the first corresponding `run` function returns true.
99+
// https://codemirror.net/examples/tab/
100+
const tabKeymap: KeyBinding[] = [
101+
{
102+
key: 'Tab',
103+
run(context) {
104+
if (isReadOnly(context.state)) {
105+
return false;
106+
}
107+
return acceptCompletion(context);
108+
},
109+
},
110+
{
111+
key: 'Tab',
112+
run({ state, dispatch }) {
113+
if (isReadOnly(state)) {
114+
return false;
115+
}
116+
117+
// `indentWithTab` will indent when `Tab` is pressed without any selection (like
118+
// in browser devtools for example). Instead we want to input the tab symbol in
119+
// that case, to have behavior similar to VSCode's editors.
120+
if (!state.selection.ranges.some((range) => !range.empty)) {
121+
dispatch(
122+
state.update(state.replaceSelection('\t'), {
123+
scrollIntoView: true,
124+
userEvent: 'input',
125+
})
126+
);
127+
return true;
128+
}
129+
104130
return false;
105-
}
106-
dispatch(
107-
state.update(state.replaceSelection('\t'), {
108-
scrollIntoView: true,
109-
userEvent: 'input',
110-
})
111-
);
112-
return true;
131+
},
113132
},
114-
};
133+
indentWithTab,
134+
];
115135

116136
type CodemirrorThemeType = 'light' | 'dark';
117137

@@ -845,16 +865,10 @@ const BaseEditor = React.forwardRef<EditorRef, EditorProps>(function BaseEditor(
845865
placeholderExtension,
846866
// User provided commands should take precedence over default keybindings.
847867
commandsExtension,
868+
// The order of this keymap matters, when the `run` function of the corresponding key
869+
// returns false it goes to the next corresponding key, if it returns true then
870+
// it completes and does not try further handlers.
848871
keymap.of([
849-
{
850-
key: 'Tab',
851-
run(context) {
852-
if (isReadOnly(context.state)) {
853-
return false;
854-
}
855-
return acceptCompletion(context);
856-
},
857-
},
858872
{
859873
key: 'Ctrl-Shift-b',
860874
run: prettify,
@@ -868,7 +882,7 @@ const BaseEditor = React.forwardRef<EditorRef, EditorProps>(function BaseEditor(
868882
...historyKeymap,
869883
...foldKeymap,
870884
...completionKeymap,
871-
breakFocusOutBinding,
885+
...tabKeymap,
872886
]),
873887
readOnlyExtension,
874888
EditorView.updateListener.of((update) => {

0 commit comments

Comments
 (0)