@xh/hoist 79.0.0-SNAPSHOT.1765833120742 → 79.0.0-SNAPSHOT.1765835240904
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 +0 -26
- package/admin/tabs/cluster/instances/connpool/ConnPoolMonitorPanel.ts +1 -1
- package/admin/tabs/cluster/instances/services/DetailsPanel.ts +1 -1
- package/admin/tabs/cluster/objects/DetailPanel.ts +1 -1
- package/build/types/core/HoistBase.d.ts +1 -1
- package/build/types/desktop/cmp/input/CodeInput.d.ts +21 -9
- package/build/types/desktop/cmp/input/JsonInput.d.ts +3 -15
- package/core/HoistBase.ts +7 -1
- package/desktop/cmp/filechooser/FileChooserModel.ts +2 -1
- package/desktop/cmp/input/CodeInput.scss +15 -21
- package/desktop/cmp/input/CodeInput.ts +194 -263
- package/desktop/cmp/input/JsonInput.ts +22 -126
- package/package.json +2 -13
- package/tsconfig.tsbuildinfo +1 -1
- package/build/types/desktop/cmp/input/impl/one-dark.d.ts +0 -23
- package/desktop/cmp/input/impl/one-dark.ts +0 -163
|
@@ -4,38 +4,6 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {autocompletion} from '@codemirror/autocomplete';
|
|
8
|
-
import {defaultKeymap, history, historyKeymap, indentWithTab} from '@codemirror/commands';
|
|
9
|
-
import {
|
|
10
|
-
defaultHighlightStyle,
|
|
11
|
-
foldGutter,
|
|
12
|
-
foldKeymap,
|
|
13
|
-
indentOnInput,
|
|
14
|
-
LanguageSupport,
|
|
15
|
-
syntaxHighlighting
|
|
16
|
-
} from '@codemirror/language';
|
|
17
|
-
import {linter, lintGutter} from '@codemirror/lint';
|
|
18
|
-
import {highlightSelectionMatches, search} from '@codemirror/search';
|
|
19
|
-
import {
|
|
20
|
-
Compartment,
|
|
21
|
-
EditorState,
|
|
22
|
-
Extension,
|
|
23
|
-
RangeSetBuilder,
|
|
24
|
-
StateEffect,
|
|
25
|
-
StateField
|
|
26
|
-
} from '@codemirror/state';
|
|
27
|
-
import {
|
|
28
|
-
Decoration,
|
|
29
|
-
DecorationSet,
|
|
30
|
-
EditorView,
|
|
31
|
-
highlightActiveLine,
|
|
32
|
-
highlightActiveLineGutter,
|
|
33
|
-
keymap,
|
|
34
|
-
lineNumbers,
|
|
35
|
-
ViewPlugin,
|
|
36
|
-
ViewUpdate
|
|
37
|
-
} from '@codemirror/view';
|
|
38
|
-
import {oneDark} from './impl/one-dark';
|
|
39
7
|
import {HoistInputModel, HoistInputProps, useHoistInputModel} from '@xh/hoist/cmp/input';
|
|
40
8
|
import {box, div, filler, fragment, frame, hbox, label, span, vbox} from '@xh/hoist/cmp/layout';
|
|
41
9
|
import {hoistCmp, HoistProps, LayoutProps, managed, PlainObject, XH} from '@xh/hoist/core';
|
|
@@ -45,19 +13,32 @@ import {textInput} from '@xh/hoist/desktop/cmp/input/TextInput';
|
|
|
45
13
|
import {modalSupport} from '@xh/hoist/desktop/cmp/modalsupport/ModalSupport';
|
|
46
14
|
import {ModalSupportModel} from '@xh/hoist/desktop/cmp/modalsupport/ModalSupportModel';
|
|
47
15
|
import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
|
|
16
|
+
import '@xh/hoist/desktop/register';
|
|
48
17
|
import {Icon} from '@xh/hoist/icon';
|
|
18
|
+
import {textArea} from '@xh/hoist/kit/blueprint';
|
|
49
19
|
import {action, bindable, makeObservable, observable} from '@xh/hoist/mobx';
|
|
20
|
+
import {wait} from '@xh/hoist/promise';
|
|
50
21
|
import {withDefault} from '@xh/hoist/utils/js';
|
|
51
22
|
import {getLayoutProps} from '@xh/hoist/utils/react';
|
|
52
23
|
import classNames from 'classnames';
|
|
53
|
-
import
|
|
24
|
+
import * as codemirror from 'codemirror';
|
|
25
|
+
import 'codemirror/addon/fold/brace-fold.js';
|
|
26
|
+
import 'codemirror/addon/fold/foldcode.js';
|
|
27
|
+
import 'codemirror/addon/fold/foldgutter.css';
|
|
28
|
+
import 'codemirror/addon/fold/foldgutter.js';
|
|
29
|
+
import 'codemirror/addon/lint/lint.css';
|
|
30
|
+
import 'codemirror/addon/lint/lint.js';
|
|
31
|
+
import 'codemirror/addon/scroll/simplescrollbars.css';
|
|
32
|
+
import 'codemirror/addon/scroll/simplescrollbars.js';
|
|
33
|
+
import 'codemirror/addon/search/searchcursor.js';
|
|
34
|
+
import 'codemirror/addon/selection/mark-selection.js';
|
|
35
|
+
import 'codemirror/lib/codemirror.css';
|
|
36
|
+
import 'codemirror/theme/dracula.css';
|
|
37
|
+
import {compact, defaultsDeep, isEqual, isFunction} from 'lodash';
|
|
54
38
|
import {ReactElement} from 'react';
|
|
39
|
+
import {findDOMNode} from 'react-dom';
|
|
55
40
|
import './CodeInput.scss';
|
|
56
|
-
|
|
57
|
-
import {json} from '@codemirror/lang-json';
|
|
58
|
-
import {html} from '@codemirror/lang-html';
|
|
59
|
-
import {sql} from '@codemirror/lang-sql';
|
|
60
|
-
import {css} from '@codemirror/lang-css';
|
|
41
|
+
|
|
61
42
|
export interface CodeInputProps extends HoistProps, HoistInputProps, LayoutProps {
|
|
62
43
|
/** True to focus the control on render. */
|
|
63
44
|
autoFocus?: boolean;
|
|
@@ -65,6 +46,12 @@ export interface CodeInputProps extends HoistProps, HoistInputProps, LayoutProps
|
|
|
65
46
|
/** False to not commit on every change/keystroke, default true. */
|
|
66
47
|
commitOnChange?: boolean;
|
|
67
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Configuration object with any properties supported by the CodeMirror API.
|
|
51
|
+
* @see {@link https://codemirror.net/doc/manual.html#api_configuration|CodeMirror Docs}
|
|
52
|
+
*/
|
|
53
|
+
editorProps?: PlainObject;
|
|
54
|
+
|
|
68
55
|
/**
|
|
69
56
|
* True to enable case-insensitive searching within the input. Default false, except in
|
|
70
57
|
* fullscreen mode, where search will be shown unless explicitly *disabled*. Note that
|
|
@@ -85,10 +72,10 @@ export interface CodeInputProps extends HoistProps, HoistInputProps, LayoutProps
|
|
|
85
72
|
|
|
86
73
|
/**
|
|
87
74
|
* A CodeMirror language mode - default none (plain-text). See the CodeMirror docs
|
|
88
|
-
* ({@link https://
|
|
89
|
-
*
|
|
75
|
+
* ({@link https://codemirror.net/mode/}) regarding available modes.
|
|
76
|
+
* Applications must import any mode they wish to enable.
|
|
90
77
|
*/
|
|
91
|
-
|
|
78
|
+
mode?: string;
|
|
92
79
|
|
|
93
80
|
/**
|
|
94
81
|
* True to prevent user modification of editor contents, while still allowing user to
|
|
@@ -114,15 +101,6 @@ export interface CodeInputProps extends HoistProps, HoistInputProps, LayoutProps
|
|
|
114
101
|
* action buttons show only when the input focused and float in the bottom-right corner.
|
|
115
102
|
*/
|
|
116
103
|
showToolbar?: boolean;
|
|
117
|
-
|
|
118
|
-
/** False (default) to highlight active line in input. */
|
|
119
|
-
highlightActiveLine?: boolean;
|
|
120
|
-
|
|
121
|
-
/** True (default) to add line numbers to gutter. */
|
|
122
|
-
lineNumbers?: boolean | PlainObject;
|
|
123
|
-
|
|
124
|
-
/** False (default) to add line numbers to gutter. */
|
|
125
|
-
lineWrapping?: boolean | PlainObject;
|
|
126
104
|
}
|
|
127
105
|
|
|
128
106
|
/**
|
|
@@ -153,17 +131,17 @@ class CodeInputModel extends HoistInputModel {
|
|
|
153
131
|
@managed
|
|
154
132
|
modalSupportModel: ModalSupportModel = new ModalSupportModel();
|
|
155
133
|
|
|
156
|
-
editor
|
|
134
|
+
/** A CodeMirror editor instance. */
|
|
135
|
+
editor: any;
|
|
157
136
|
|
|
158
137
|
// Support for internal search feature.
|
|
159
138
|
cursor = null;
|
|
160
139
|
@bindable query: string = '';
|
|
161
140
|
@observable currentMatchIdx: number = -1;
|
|
162
|
-
@observable.ref matches
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
private themeCompartment = new Compartment();
|
|
141
|
+
@observable.ref matches = [];
|
|
142
|
+
get matchCount(): number {
|
|
143
|
+
return this.matches.length;
|
|
144
|
+
}
|
|
167
145
|
|
|
168
146
|
get fullScreen(): boolean {
|
|
169
147
|
return this.modalSupportModel.isModal;
|
|
@@ -236,7 +214,8 @@ class CodeInputModel extends HoistInputModel {
|
|
|
236
214
|
}
|
|
237
215
|
|
|
238
216
|
override blur() {
|
|
239
|
-
this.editor?.
|
|
217
|
+
this.editor?.execCommand('undoSelection');
|
|
218
|
+
this.editor?.getInputField().blur();
|
|
240
219
|
}
|
|
241
220
|
|
|
242
221
|
override focus() {
|
|
@@ -244,34 +223,12 @@ class CodeInputModel extends HoistInputModel {
|
|
|
244
223
|
}
|
|
245
224
|
|
|
246
225
|
override select() {
|
|
247
|
-
|
|
248
|
-
this.editor.dispatch({selection: {anchor: 0, head: this.editor.state.doc.length}});
|
|
226
|
+
this.editor?.execCommand('selectAll');
|
|
249
227
|
}
|
|
250
228
|
|
|
251
229
|
constructor() {
|
|
252
230
|
super();
|
|
253
231
|
makeObservable(this);
|
|
254
|
-
|
|
255
|
-
this.highlightField = StateField.define<DecorationSet>({
|
|
256
|
-
create: () => Decoration.none,
|
|
257
|
-
update: (deco, tr) => {
|
|
258
|
-
deco = deco.map(tr.changes);
|
|
259
|
-
if (tr.effects.some(e => e.is(this.updateMatchesEffect))) {
|
|
260
|
-
const builder = new RangeSetBuilder<Decoration>();
|
|
261
|
-
this.matches.forEach(match => {
|
|
262
|
-
builder.add(
|
|
263
|
-
match.from,
|
|
264
|
-
match.to,
|
|
265
|
-
Decoration.mark({class: 'xh-code-input--highlight'})
|
|
266
|
-
);
|
|
267
|
-
});
|
|
268
|
-
deco = builder.finish();
|
|
269
|
-
}
|
|
270
|
-
return deco;
|
|
271
|
-
},
|
|
272
|
-
provide: f => EditorView.decorations.from(f)
|
|
273
|
-
});
|
|
274
|
-
|
|
275
232
|
this.addReaction({
|
|
276
233
|
track: () => this.modalSupportModel.isModal,
|
|
277
234
|
run: () => this.focus(),
|
|
@@ -283,61 +240,103 @@ class CodeInputModel extends HoistInputModel {
|
|
|
283
240
|
this.addReaction({
|
|
284
241
|
track: () => XH.darkTheme,
|
|
285
242
|
run: () => {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
effects: this.themeCompartment.reconfigure(this.getThemeExtension())
|
|
289
|
-
});
|
|
243
|
+
const {editor} = this;
|
|
244
|
+
if (editor) editor.setOption('theme', XH.darkTheme ? 'dracula' : 'default');
|
|
290
245
|
}
|
|
291
246
|
});
|
|
292
247
|
|
|
293
248
|
this.addReaction({
|
|
294
249
|
track: () => this.renderValue,
|
|
295
|
-
run:
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
250
|
+
run: value => {
|
|
251
|
+
const {editor} = this;
|
|
252
|
+
if (editor && editor.getValue() != value) {
|
|
253
|
+
// CodeMirror will throw on null value.
|
|
254
|
+
editor.setValue(value == null ? '' : value);
|
|
300
255
|
}
|
|
301
256
|
}
|
|
302
257
|
});
|
|
303
258
|
|
|
304
259
|
this.addReaction({
|
|
305
260
|
track: () => this.componentProps.readonly || this.componentProps.disabled,
|
|
306
|
-
run:
|
|
307
|
-
|
|
308
|
-
this.editor.dispatch({
|
|
309
|
-
effects: StateEffect.appendConfig.of(EditorView.editable.of(!readOnly))
|
|
310
|
-
});
|
|
261
|
+
run: editorReadOnly => {
|
|
262
|
+
this.editor.setOption('readOnly', editorReadOnly);
|
|
311
263
|
}
|
|
312
264
|
});
|
|
313
265
|
|
|
314
266
|
this.addReaction({
|
|
315
267
|
track: () => this.query,
|
|
316
268
|
run: query => {
|
|
317
|
-
if (query?.trim())
|
|
318
|
-
|
|
269
|
+
if (query?.trim()) {
|
|
270
|
+
this.findAll();
|
|
271
|
+
} else {
|
|
272
|
+
this.clearSearchResults();
|
|
273
|
+
}
|
|
319
274
|
},
|
|
320
275
|
debounce: 300
|
|
321
276
|
});
|
|
322
277
|
}
|
|
323
278
|
|
|
324
|
-
|
|
325
|
-
if (
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
this.editor = new EditorView({state, parent: container});
|
|
279
|
+
manageCodeEditor = textAreaComp => {
|
|
280
|
+
if (textAreaComp) {
|
|
281
|
+
this.editor = this.createCodeEditor(textAreaComp);
|
|
282
|
+
this.preserveSearchResults();
|
|
283
|
+
}
|
|
330
284
|
};
|
|
331
285
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
286
|
+
createCodeEditor(textAreaComp) {
|
|
287
|
+
const editorSpec = defaultsDeep(this.componentProps.editorProps, this.createDefaults());
|
|
288
|
+
|
|
289
|
+
const taDom = findDOMNode(textAreaComp),
|
|
290
|
+
editor = codemirror.fromTextArea(taDom, editorSpec);
|
|
291
|
+
|
|
292
|
+
editor.on('change', this.handleEditorChange);
|
|
293
|
+
return editor;
|
|
336
294
|
}
|
|
337
295
|
|
|
338
|
-
|
|
296
|
+
createDefaults() {
|
|
297
|
+
const {disabled, readonly, mode, linter, autoFocus} = this.componentProps;
|
|
298
|
+
let gutters = ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'];
|
|
299
|
+
if (linter) gutters.push('CodeMirror-lint-markers');
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
mode,
|
|
303
|
+
theme: XH.darkTheme ? 'dracula' : 'default',
|
|
304
|
+
lineWrapping: false,
|
|
305
|
+
lineNumbers: true,
|
|
306
|
+
autoCloseBrackets: true,
|
|
307
|
+
extraKeys: {
|
|
308
|
+
'Cmd-P': this.onAutoFormat,
|
|
309
|
+
'Ctrl-P': this.onAutoFormat
|
|
310
|
+
},
|
|
311
|
+
foldGutter: true,
|
|
312
|
+
scrollbarStyle: 'simple',
|
|
313
|
+
readOnly: disabled || readonly,
|
|
314
|
+
gutters,
|
|
315
|
+
lint: linter ? {getAnnotations: linter} : false,
|
|
316
|
+
autoFocus
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
onChange = ev => {
|
|
321
|
+
this.noteValueChange(ev.target.value);
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
handleEditorChange = editor => {
|
|
325
|
+
this.noteValueChange(editor.getValue());
|
|
326
|
+
if (this.cursor) this.clearSearchResults();
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
onAutoFormat = () => {
|
|
330
|
+
if (!isFunction(this.componentProps.formatter)) return;
|
|
331
|
+
|
|
332
|
+
const editor = this.editor,
|
|
333
|
+
val = this.tryPrettyPrint(editor.getValue());
|
|
334
|
+
editor.setValue(val);
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
tryPrettyPrint(str) {
|
|
339
338
|
try {
|
|
340
|
-
return this.componentProps.formatter
|
|
339
|
+
return this.componentProps.formatter(str);
|
|
341
340
|
} catch (e) {
|
|
342
341
|
return str;
|
|
343
342
|
}
|
|
@@ -345,180 +344,106 @@ class CodeInputModel extends HoistInputModel {
|
|
|
345
344
|
|
|
346
345
|
toggleFullScreen() {
|
|
347
346
|
this.modalSupportModel.toggleIsModal();
|
|
347
|
+
|
|
348
|
+
// 'Nudge' the mouse wheel to trigger CodeMirror to update scrollbar state
|
|
349
|
+
const scrollEvent = d => new window.WheelEvent('mousewheel', {deltaX: d, deltaY: d});
|
|
350
|
+
wait().then(() => {
|
|
351
|
+
this.editor.getScrollerElement().dispatchEvent(scrollEvent(2));
|
|
352
|
+
this.editor.getScrollerElement().dispatchEvent(scrollEvent(-2));
|
|
353
|
+
});
|
|
348
354
|
}
|
|
349
355
|
|
|
356
|
+
//------------------------
|
|
357
|
+
// Local Searching
|
|
358
|
+
//------------------------
|
|
350
359
|
@action
|
|
351
360
|
findAll() {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
this.editor.dispatch({
|
|
368
|
-
selection: {anchor: match.from, head: match.to},
|
|
369
|
-
scrollIntoView: true
|
|
361
|
+
this.clearSearchResults();
|
|
362
|
+
if (!this.query?.trim()) return;
|
|
363
|
+
|
|
364
|
+
this.cursor = this.editor.getSearchCursor(this.query, 0, true);
|
|
365
|
+
|
|
366
|
+
const {cursor, editor} = this,
|
|
367
|
+
newMatches = [];
|
|
368
|
+
|
|
369
|
+
while (cursor.findNext()) {
|
|
370
|
+
const anchor = cursor.from(),
|
|
371
|
+
head = cursor.to();
|
|
372
|
+
newMatches.push({
|
|
373
|
+
anchor,
|
|
374
|
+
head,
|
|
375
|
+
textMarker: editor.markText(anchor, head, {className: 'xh-code-input--highlight'})
|
|
370
376
|
});
|
|
371
377
|
}
|
|
378
|
+
|
|
379
|
+
this.matches = newMatches;
|
|
380
|
+
if (newMatches.length) {
|
|
381
|
+
this.findNext();
|
|
382
|
+
} else {
|
|
383
|
+
this.currentMatchIdx = -1;
|
|
384
|
+
}
|
|
372
385
|
}
|
|
373
386
|
|
|
374
387
|
@action
|
|
375
388
|
findNext() {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
389
|
+
const {editor, query, cursor, matchCount} = this;
|
|
390
|
+
if (!cursor || !matchCount) return;
|
|
391
|
+
|
|
392
|
+
if (cursor.findNext(query)) {
|
|
393
|
+
this.handleCursorMatchUpdate();
|
|
394
|
+
} else {
|
|
395
|
+
// Loop around
|
|
396
|
+
this.cursor = editor.getSearchCursor(query, 0, true);
|
|
397
|
+
this.findNext();
|
|
398
|
+
}
|
|
383
399
|
}
|
|
384
400
|
|
|
385
401
|
@action
|
|
386
402
|
findPrevious() {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
403
|
+
const {editor, query, cursor, matches, matchCount} = this;
|
|
404
|
+
if (!cursor || !matchCount) return;
|
|
405
|
+
|
|
406
|
+
if (cursor.findPrevious(query)) {
|
|
407
|
+
this.handleCursorMatchUpdate();
|
|
408
|
+
} else {
|
|
409
|
+
// Loop around
|
|
410
|
+
this.cursor = editor.getSearchCursor(query, matches[matchCount - 1].head, true);
|
|
411
|
+
this.findPrevious();
|
|
412
|
+
}
|
|
395
413
|
}
|
|
396
414
|
|
|
397
415
|
@action
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
416
|
+
handleCursorMatchUpdate() {
|
|
417
|
+
const {editor, cursor, matches} = this,
|
|
418
|
+
from = cursor.from(),
|
|
419
|
+
to = cursor.to();
|
|
420
|
+
editor.scrollIntoView({from, to}, 50);
|
|
421
|
+
editor.setSelection(from, to);
|
|
422
|
+
this.currentMatchIdx = matches.findIndex(match => isEqual(match.anchor, from));
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
preserveSearchResults() {
|
|
426
|
+
const {matches, editor} = this;
|
|
427
|
+
matches.forEach(match => {
|
|
428
|
+
match.textMarker = editor.markText(match.anchor, match.head, {
|
|
429
|
+
className: 'xh-code-input--highlight'
|
|
430
|
+
});
|
|
431
|
+
});
|
|
401
432
|
}
|
|
402
433
|
|
|
403
434
|
@action
|
|
404
435
|
clearSearchResults() {
|
|
405
|
-
this.
|
|
436
|
+
this.cursor = null;
|
|
406
437
|
this.currentMatchIdx = -1;
|
|
407
|
-
this.
|
|
438
|
+
this.matches.forEach(match => match.textMarker.clear());
|
|
439
|
+
this.matches = [];
|
|
408
440
|
}
|
|
409
441
|
|
|
410
442
|
override destroy() {
|
|
411
|
-
|
|
443
|
+
// Cleanup editor component as per CodeMirror docs.
|
|
444
|
+
if (this.editor) this.editor.toTextArea();
|
|
412
445
|
super.destroy();
|
|
413
446
|
}
|
|
414
|
-
|
|
415
|
-
//------------------------
|
|
416
|
-
// Implementation
|
|
417
|
-
//------------------------
|
|
418
|
-
private async getExtensionsAsync(): Promise<Extension[]> {
|
|
419
|
-
const {
|
|
420
|
-
autoFocus,
|
|
421
|
-
language,
|
|
422
|
-
readonly,
|
|
423
|
-
highlightActiveLine: propsHighlightActiveLine,
|
|
424
|
-
linter: propsLinter,
|
|
425
|
-
lineNumbers: propsLineNumbers = true,
|
|
426
|
-
lineWrapping: propsLineWrapping = false
|
|
427
|
-
} = this.componentProps,
|
|
428
|
-
extensions = [
|
|
429
|
-
// Theme
|
|
430
|
-
this.themeCompartment.of(this.getThemeExtension()),
|
|
431
|
-
// Editor state
|
|
432
|
-
EditorView.editable.of(!readonly),
|
|
433
|
-
EditorView.updateListener.of((update: ViewUpdate) => {
|
|
434
|
-
if (update.docChanged) this.noteValueChange(update.state.doc.toString());
|
|
435
|
-
}),
|
|
436
|
-
// Search & custom highlight
|
|
437
|
-
search(),
|
|
438
|
-
syntaxHighlighting(defaultHighlightStyle),
|
|
439
|
-
highlightSelectionMatches(),
|
|
440
|
-
this.highlightField,
|
|
441
|
-
// Editor UI
|
|
442
|
-
foldGutter(),
|
|
443
|
-
lintGutter(),
|
|
444
|
-
indentOnInput(),
|
|
445
|
-
autocompletion(),
|
|
446
|
-
history(),
|
|
447
|
-
// Linter
|
|
448
|
-
propsLinter
|
|
449
|
-
? linter(async view => {
|
|
450
|
-
const text = view.state.doc.toString();
|
|
451
|
-
return await propsLinter(text);
|
|
452
|
-
})
|
|
453
|
-
: [],
|
|
454
|
-
// Key bindings
|
|
455
|
-
keymap.of([
|
|
456
|
-
...defaultKeymap,
|
|
457
|
-
...historyKeymap,
|
|
458
|
-
...foldKeymap,
|
|
459
|
-
indentWithTab,
|
|
460
|
-
{
|
|
461
|
-
key: 'Mod-p',
|
|
462
|
-
run: () => {
|
|
463
|
-
this.onAutoFormat();
|
|
464
|
-
return true;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
])
|
|
468
|
-
];
|
|
469
|
-
|
|
470
|
-
if (propsLineWrapping) extensions.push(EditorView.lineWrapping);
|
|
471
|
-
if (propsLineNumbers) {
|
|
472
|
-
isObject(propsLineNumbers)
|
|
473
|
-
? extensions.push(lineNumbers(propsLineNumbers))
|
|
474
|
-
: extensions.push(lineNumbers());
|
|
475
|
-
}
|
|
476
|
-
if (propsHighlightActiveLine)
|
|
477
|
-
extensions.push(highlightActiveLine(), highlightActiveLineGutter());
|
|
478
|
-
if (autoFocus) extensions.push(this.autofocusExtension);
|
|
479
|
-
if (language) extensions.push(await this.getLanguageExtensionAsync(language));
|
|
480
|
-
|
|
481
|
-
return extensions.filter(it => !isEmpty(it));
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
private getThemeExtension() {
|
|
485
|
-
const lightTheme = EditorView.theme({}, {dark: false});
|
|
486
|
-
return XH.darkTheme ? oneDark : lightTheme;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
private LANGUAGE_EXTENSIONS: Record<string, () => LanguageSupport> = {
|
|
490
|
-
js: javascript,
|
|
491
|
-
javascript: javascript,
|
|
492
|
-
html: html,
|
|
493
|
-
css: css,
|
|
494
|
-
json: json,
|
|
495
|
-
sql: sql
|
|
496
|
-
};
|
|
497
|
-
|
|
498
|
-
private async getLanguageExtensionAsync(lang: string): Promise<LanguageSupport> {
|
|
499
|
-
if (!lang) return null;
|
|
500
|
-
const extFactory = this.LANGUAGE_EXTENSIONS[lang.toLowerCase()];
|
|
501
|
-
if (!extFactory) {
|
|
502
|
-
console.warn(`Language not found: ${lang}`);
|
|
503
|
-
return null;
|
|
504
|
-
}
|
|
505
|
-
try {
|
|
506
|
-
// DEBUG
|
|
507
|
-
console.log(extFactory());
|
|
508
|
-
return extFactory(); // returns a LanguageSupport instance
|
|
509
|
-
} catch (err) {
|
|
510
|
-
console.error(`Failed to load language: ${lang}`, err);
|
|
511
|
-
return null;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
private autofocusExtension = ViewPlugin.fromClass(
|
|
516
|
-
class {
|
|
517
|
-
constructor(view: EditorView) {
|
|
518
|
-
queueMicrotask(() => view.focus());
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
);
|
|
522
447
|
}
|
|
523
448
|
|
|
524
449
|
const cmp = hoistCmp.factory<CodeInputModel>(({model, className, ...props}, ref) => {
|
|
@@ -546,7 +471,11 @@ const inputCmp = hoistCmp.factory<CodeInputModel>(({model, ...props}, ref) =>
|
|
|
546
471
|
items: [
|
|
547
472
|
div({
|
|
548
473
|
className: 'xh-code-input__inner-wrapper',
|
|
549
|
-
|
|
474
|
+
item: textArea({
|
|
475
|
+
value: model.renderValue || '',
|
|
476
|
+
inputRef: model.manageCodeEditor,
|
|
477
|
+
onChange: model.onChange
|
|
478
|
+
})
|
|
550
479
|
}),
|
|
551
480
|
model.showToolbar ? toolbarCmp() : actionButtonsCmp()
|
|
552
481
|
],
|
|
@@ -567,9 +496,7 @@ const toolbarCmp = hoistCmp.factory<CodeInputModel>(({model}) => {
|
|
|
567
496
|
});
|
|
568
497
|
|
|
569
498
|
const searchInputCmp = hoistCmp.factory<CodeInputModel>(({model}) => {
|
|
570
|
-
const {query, currentMatchIdx,
|
|
571
|
-
matchCount = matches.length;
|
|
572
|
-
|
|
499
|
+
const {query, cursor, currentMatchIdx, matchCount, fullScreen} = model;
|
|
573
500
|
return fragment(
|
|
574
501
|
// Frame wrapper added due to issues with textInput not supporting all layout props as it should.
|
|
575
502
|
frame({
|
|
@@ -578,16 +505,20 @@ const searchInputCmp = hoistCmp.factory<CodeInputModel>(({model}) => {
|
|
|
578
505
|
item: textInput({
|
|
579
506
|
width: null,
|
|
580
507
|
flex: 1,
|
|
581
|
-
model,
|
|
508
|
+
model: this,
|
|
582
509
|
bind: 'query',
|
|
583
510
|
leftIcon: Icon.search(),
|
|
584
511
|
enableClear: true,
|
|
585
512
|
commitOnChange: true,
|
|
586
513
|
onKeyDown: e => {
|
|
587
514
|
if (e.key !== 'Enter') return;
|
|
588
|
-
if (!
|
|
589
|
-
|
|
590
|
-
else
|
|
515
|
+
if (!cursor) {
|
|
516
|
+
model.findAll();
|
|
517
|
+
} else if (e.shiftKey) {
|
|
518
|
+
model.findPrevious();
|
|
519
|
+
} else {
|
|
520
|
+
model.findNext();
|
|
521
|
+
}
|
|
591
522
|
}
|
|
592
523
|
})
|
|
593
524
|
}),
|