@veltra/desktop 1.2.28 → 1.2.30
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/dist/components/action/action-group.vue.d.ts +2 -2
- package/dist/components/action/action.vue.d.ts +1 -1
- package/dist/components/auto-complete/auto-complete.vue.d.ts +2 -2
- package/dist/components/batch-edit/batch-edit.vue.d.ts +2 -2
- package/dist/components/cascade/cascade.vue.d.ts +5 -5
- package/dist/components/code-editor/code-editor.js +7 -2
- package/dist/components/code-editor/code-editor.js.map +1 -1
- package/dist/components/code-editor/lang-loaders.js +2 -1
- package/dist/components/code-editor/lang-loaders.js.map +1 -1
- package/dist/components/code-editor/langs/spel/completion.js +88 -0
- package/dist/components/code-editor/langs/spel/completion.js.map +1 -0
- package/dist/components/code-editor/langs/spel/index.js +12 -0
- package/dist/components/code-editor/langs/spel/index.js.map +1 -0
- package/dist/components/code-editor/langs/spel/language.js +177 -0
- package/dist/components/code-editor/langs/spel/language.js.map +1 -0
- package/dist/components/code-editor/style2.css +11 -0
- package/dist/components/date-picker/date-picker.vue.d.ts +2 -2
- package/dist/components/date-range-picker/date-range-picker.vue.d.ts +2 -2
- package/dist/components/empty/empty.vue.d.ts +1 -1
- package/dist/components/grid-input/grid-input.js +19 -2
- package/dist/components/grid-input/grid-input.js.map +1 -1
- package/dist/components/input/input.vue.d.ts +8 -8
- package/dist/components/multi-select/multi-select.vue.d.ts +2 -2
- package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +4 -4
- package/dist/components/number/number.vue.d.ts +1 -1
- package/dist/components/number-input/number-input.vue.d.ts +4 -4
- package/dist/components/number-range-input/number-range-input.vue.d.ts +1 -1
- package/dist/components/password-input/password-input.vue.d.ts +1 -1
- package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
- package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +1 -1
- package/dist/components/select/select.vue.d.ts +2 -2
- package/dist/components/steps/steps.vue.d.ts +3 -3
- package/dist/components/textarea/textarea.vue.d.ts +4 -4
- package/dist/components/tip/tip.vue.d.ts +2 -2
- package/dist/components/tree-select/tree-select.vue.d.ts +4 -4
- package/dist/components/watermark/watermark.vue.d.ts +1 -1
- package/dist/node_modules/.bun/@codemirror_language@6.12.4/node_modules/@codemirror/language/dist/index.js +452 -2
- package/dist/node_modules/.bun/@codemirror_language@6.12.4/node_modules/@codemirror/language/dist/index.js.map +1 -1
- package/dist/types/code-editor.d.ts +3 -2
- package/package.json +6 -6
- package/src/components/code-editor/code-editor.vue +6 -0
- package/src/components/code-editor/lang-loaders.ts +2 -1
- package/src/components/code-editor/langs/spel/completion.ts +30 -0
- package/src/components/code-editor/langs/spel/index.ts +12 -0
- package/src/components/code-editor/langs/spel/language.ts +198 -0
- package/src/components/code-editor/style.scss +12 -0
- package/src/components/grid-input/grid-input.vue +24 -2
- package/src/types/code-editor.ts +3 -2
|
@@ -7,10 +7,10 @@ type __VLS_Slots = {
|
|
|
7
7
|
};
|
|
8
8
|
declare const __VLS_base: import("vue").DefineComponent<ActionGroupProps, _ActionGroupExposed, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ActionGroupProps> & Readonly<{}>, {
|
|
9
9
|
type: import("@veltra/utils").ColorType;
|
|
10
|
+
text: boolean;
|
|
10
11
|
size: "small" | "default" | "large";
|
|
11
|
-
circle: boolean;
|
|
12
12
|
max: number;
|
|
13
|
-
|
|
13
|
+
circle: boolean;
|
|
14
14
|
loading: boolean;
|
|
15
15
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
16
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -13,9 +13,9 @@ declare const __VLS_base: import("vue").DefineComponent<ActionProps, {}, {}, {},
|
|
|
13
13
|
onRun?: (() => any) | undefined;
|
|
14
14
|
}>, {
|
|
15
15
|
type: ButtonType;
|
|
16
|
+
text: boolean;
|
|
16
17
|
size: import("@veltra/utils").ComponentSize;
|
|
17
18
|
circle: boolean;
|
|
18
|
-
text: boolean;
|
|
19
19
|
loading: boolean;
|
|
20
20
|
inDropdown: boolean;
|
|
21
21
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -22,10 +22,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, _Auto
|
|
|
22
22
|
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
23
23
|
onSelect?: ((value: string) => any) | undefined;
|
|
24
24
|
}>, {
|
|
25
|
-
disabled: boolean;
|
|
26
|
-
readonly: boolean;
|
|
27
25
|
placeholder: string;
|
|
28
26
|
clearable: boolean;
|
|
27
|
+
disabled: boolean;
|
|
28
|
+
readonly: boolean;
|
|
29
29
|
allowCustom: boolean;
|
|
30
30
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
31
31
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -6,20 +6,20 @@ declare const __VLS_base: import("vue").DefineComponent<BatchEditProps, {}, {},
|
|
|
6
6
|
"update:selected": (value: Record<string, any> | undefined) => any;
|
|
7
7
|
"update:checked": (value: Record<string, any>[]) => any;
|
|
8
8
|
"update:current": (row?: TableRow | undefined) => any;
|
|
9
|
+
"update:data": (value: Record<string, any>[]) => any;
|
|
9
10
|
"update:rows": (rows: TableRow[]) => any;
|
|
10
11
|
"update:forest": (rows?: import("@cat-kit/core").Forest<Record<string, unknown>, any> | undefined) => any;
|
|
11
12
|
"cell-click": (row: TableRow, column: TableColumn, ev: MouseEvent) => any;
|
|
12
13
|
"row-click": (row: TableRow, ev: MouseEvent) => any;
|
|
13
|
-
"update:data": (value: Record<string, any>[]) => any;
|
|
14
14
|
}, string, import("vue").PublicProps, Readonly<BatchEditProps> & Readonly<{
|
|
15
15
|
"onUpdate:selected"?: ((value: Record<string, any> | undefined) => any) | undefined;
|
|
16
16
|
"onUpdate:checked"?: ((value: Record<string, any>[]) => any) | undefined;
|
|
17
17
|
"onUpdate:current"?: ((row?: TableRow | undefined) => any) | undefined;
|
|
18
|
+
"onUpdate:data"?: ((value: Record<string, any>[]) => any) | undefined;
|
|
18
19
|
"onUpdate:rows"?: ((rows: TableRow[]) => any) | undefined;
|
|
19
20
|
"onUpdate:forest"?: ((rows?: import("@cat-kit/core").Forest<Record<string, unknown>, any> | undefined) => any) | undefined;
|
|
20
21
|
"onCell-click"?: ((row: TableRow, column: TableColumn, ev: MouseEvent) => any) | undefined;
|
|
21
22
|
"onRow-click"?: ((row: TableRow, ev: MouseEvent) => any) | undefined;
|
|
22
|
-
"onUpdate:data"?: ((value: Record<string, any>[]) => any) | undefined;
|
|
23
23
|
}>, {
|
|
24
24
|
cols: string | [string, string];
|
|
25
25
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -2,27 +2,27 @@ import { CascadeProps } from "../../types/cascade.js";
|
|
|
2
2
|
//#region src/components/cascade/cascade.vue.d.ts
|
|
3
3
|
declare const __VLS_export: import("vue").DefineComponent<CascadeProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
4
4
|
"update:modelValue": (value?: string | string[] | undefined) => any;
|
|
5
|
-
change: (...args: [item?: Record<string, any> | undefined] | [items: Record<string, any>[]]) => any;
|
|
6
5
|
clear: () => any;
|
|
6
|
+
change: (...args: [item?: Record<string, any> | undefined] | [items: Record<string, any>[]]) => any;
|
|
7
7
|
"update:label": (label?: string | string[] | undefined) => any;
|
|
8
8
|
}, string, import("vue").PublicProps, Readonly<CascadeProps> & Readonly<{
|
|
9
9
|
"onUpdate:modelValue"?: ((value?: string | string[] | undefined) => any) | undefined;
|
|
10
|
-
onChange?: ((...args: [item?: Record<string, any> | undefined] | [items: Record<string, any>[]]) => any) | undefined;
|
|
11
10
|
onClear?: (() => any) | undefined;
|
|
11
|
+
onChange?: ((...args: [item?: Record<string, any> | undefined] | [items: Record<string, any>[]]) => any) | undefined;
|
|
12
12
|
"onUpdate:label"?: ((label?: string | string[] | undefined) => any) | undefined;
|
|
13
13
|
}>, {
|
|
14
14
|
labelKey: string;
|
|
15
15
|
valueKey: string;
|
|
16
16
|
childrenKey: string;
|
|
17
17
|
data: Record<string, any>[];
|
|
18
|
-
disabled: boolean;
|
|
19
|
-
readonly: boolean;
|
|
20
18
|
placeholder: string;
|
|
21
19
|
clearable: boolean;
|
|
22
20
|
filterable: boolean;
|
|
23
|
-
|
|
21
|
+
disabled: boolean;
|
|
22
|
+
readonly: boolean;
|
|
24
23
|
separator: string;
|
|
25
24
|
showFullPath: boolean;
|
|
25
|
+
visibilityLimit: number;
|
|
26
26
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
27
27
|
declare const _default: typeof __VLS_export;
|
|
28
28
|
//#endregion
|
|
@@ -6,7 +6,7 @@ import { oneDark } from "../../node_modules/.bun/@codemirror_theme-one-dark@6.1.
|
|
|
6
6
|
import { basicSetup } from "./basic-setup.js";
|
|
7
7
|
import { loadLanguage } from "./lang-loaders.js";
|
|
8
8
|
import { buildFullDoc, extractBodyFromText, getShellRanges, mapSelectionToBody, shellExtension, textEqualsShellDoc } from "./shell-extension.js";
|
|
9
|
-
import { computed, createBlock, createCommentVNode, createElementVNode, defineComponent, mergeModels, normalizeClass, normalizeStyle, onBeforeUnmount, openBlock, shallowRef, unref, useModel, useTemplateRef, watch, withCtx, withKeys, withModifiers } from "vue";
|
|
9
|
+
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, defineComponent, mergeModels, normalizeClass, normalizeStyle, onBeforeUnmount, openBlock, shallowRef, toDisplayString, unref, useModel, useTemplateRef, watch, withCtx, withKeys, withModifiers } from "vue";
|
|
10
10
|
import { useFormFallbackProps } from "@veltra/compositions";
|
|
11
11
|
import { bem, injectFormContext, zIndex } from "@veltra/utils";
|
|
12
12
|
//#region src/components/code-editor/code-editor.vue
|
|
@@ -75,6 +75,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
75
75
|
})));
|
|
76
76
|
/** 实际生效的语言:优先 lang model,否则回落 langs[0] */
|
|
77
77
|
const activeLang = computed(() => lang.value ?? langs.value[0]);
|
|
78
|
+
/** 仅一种语言时,在选择器位置展示名称 */
|
|
79
|
+
const langLabel = computed(() => langs.value.length === 1 && activeLang.value ? activeLang.value.toUpperCase() : void 0);
|
|
78
80
|
const shellConfig = computed(() => ({
|
|
79
81
|
prefix: props.prefix ?? "",
|
|
80
82
|
suffix: props.suffix ?? ""
|
|
@@ -265,7 +267,10 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
265
267
|
"modelValue",
|
|
266
268
|
"options",
|
|
267
269
|
"disabled"
|
|
268
|
-
])) :
|
|
270
|
+
])) : langLabel.value ? (openBlock(), createElementBlock("span", {
|
|
271
|
+
key: 1,
|
|
272
|
+
class: normalizeClass(unref(cls).e("lang-label"))
|
|
273
|
+
}, toDisplayString(langLabel.value), 3)) : createCommentVNode("v-if", true)]),
|
|
269
274
|
_: 1
|
|
270
275
|
}, 8, ["class", "style"]);
|
|
271
276
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-editor.js","names":[],"sources":["../../../src/components/code-editor/code-editor.vue"],"sourcesContent":["<template>\n <u-scroll :class=\"className\" :style=\"rootStyle\" @keyup.enter.stop>\n <div ref=\"container\"></div>\n\n <u-select\n v-if=\"showLangSelect\"\n :class=\"cls.e('lang-select')\"\n v-model=\"lang\"\n size=\"small\"\n :options=\"langOptions\"\n :clearable=\"false\"\n :disabled=\"disabled\"\n />\n </u-scroll>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Compartment, EditorState, type Extension, type Text } from '@codemirror/state'\nimport { oneDark } from '@codemirror/theme-one-dark'\nimport { EditorView, tooltips } from '@codemirror/view'\nimport { useFormFallbackProps } from '@veltra/compositions'\nimport { bem, injectFormContext, zIndex } from '@veltra/utils'\nimport {\n computed,\n onBeforeUnmount,\n shallowRef,\n useTemplateRef,\n watch,\n type CSSProperties\n} from 'vue'\n\nimport type { CodeEditorEmits, CodeEditorLang, CodeEditorProps } from '../../types'\nimport { UScroll } from '../scroll'\nimport { USelect } from '../select'\nimport { basicSetup } from './basic-setup'\nimport { loadLanguage } from './lang-loaders'\nimport {\n buildFullDoc,\n extractBodyFromText,\n getShellRanges,\n mapSelectionToBody,\n shellExtension,\n textEqualsShellDoc,\n type ShellConfig\n} from './shell-extension'\n\ndefineOptions({ name: 'UCodeEditor' })\n\nconst props = withDefaults(defineProps<CodeEditorProps>(), {\n disabled: undefined,\n readonly: undefined,\n dark: false,\n defaultLines: 8\n})\n\nconst emit = defineEmits<CodeEditorEmits>()\n\nconst model = defineModel<string>()\nconst lang = defineModel<CodeEditorLang>('lang')\n\nconst cls = bem('code-editor')\n\nconst { formProps } = injectFormContext()\n\nconst { disabled, readonly } = useFormFallbackProps([formProps ?? {}, props], {\n disabled: false,\n readonly: false\n})\n\nconst className = computed<string[]>(() => [\n cls.b,\n bem.is('disabled', disabled.value),\n bem.is('readonly', readonly.value),\n bem.is('dark', props.dark)\n])\n\nconst rootStyle = computed<CSSProperties>(() => ({\n '--u-code-editor-default-lines': Math.max(1, props.defaultLines)\n}))\n\nconst langs = computed(() => props.langs ?? [])\n\nconst showLangSelect = computed(() => langs.value.length > 1)\n\nconst langOptions = computed(() =>\n langs.value.map((value) => ({ label: value.toUpperCase(), value }))\n)\n\n/** 实际生效的语言:优先 lang model,否则回落 langs[0] */\nconst activeLang = computed<CodeEditorLang | undefined>(() => lang.value ?? langs.value[0])\n\nconst shellConfig = computed<ShellConfig>(() => ({\n prefix: props.prefix ?? '',\n suffix: props.suffix ?? ''\n}))\n\nconst containerRef = useTemplateRef('container')\nconst editor = shallowRef<EditorView | null>(null)\n\nconst themeCompartment = new Compartment()\nconst editableCompartment = new Compartment()\nconst readOnlyCompartment = new Compartment()\nconst langCompartment = new Compartment()\nconst shellCompartment = new Compartment()\n\n/** 聚焦时的正文快照,用于失焦时判断是否触发 change */\nlet focusSnapshot = ''\n\n/**\n * 编辑器最近一次写出的正文引用。\n * 用于短路 model → doc 同步,避免每次按键对整篇文档做 O(n) 相等比较。\n */\nlet lastEditorBody: string | undefined\n\n/** 上一帧是否处于 IME composition,用于在 composition 结束时补一次 model 同步 */\nlet wasComposing = false\n\n/** 将编辑器正文写回 v-model(跳过与上次相同的值,避免多余更新) */\nfunction writeModelFromDoc(doc: Text) {\n const nextBody = extractBodyFromText(doc, shellConfig.value)\n if (nextBody === lastEditorBody && nextBody === model.value) return\n lastEditorBody = nextBody\n model.value = nextBody\n}\n\nfunction buildExtensions(): Extension[] {\n return [\n basicSetup,\n tooltips({ parent: document.body }),\n EditorView.theme({ '.cm-tooltip': { zIndex: zIndex() } }),\n themeCompartment.of(props.dark ? oneDark : []),\n editableCompartment.of(EditorView.editable.of(!disabled.value)),\n readOnlyCompartment.of(EditorState.readOnly.of(readonly.value)),\n langCompartment.of([]),\n shellCompartment.of(shellExtension(shellConfig.value)),\n EditorView.updateListener.of((update) => {\n const composing = update.view.composing\n\n // IME 进行中不写回,避免中间态触发父级重渲染;结束后再统一同步\n if (update.docChanged && !composing) {\n writeModelFromDoc(update.state.doc)\n } else if (wasComposing && !composing) {\n writeModelFromDoc(update.state.doc)\n }\n wasComposing = composing\n\n if (!update.focusChanged) return\n if (update.view.hasFocus) {\n focusSnapshot = extractBodyFromText(update.state.doc, shellConfig.value)\n return\n }\n const current = extractBodyFromText(update.state.doc, shellConfig.value)\n if (current !== focusSnapshot) {\n emit('change', current)\n }\n })\n ]\n}\n\nlet editorToken = 0\nlet langToken = 0\n\nasync function applyLanguage() {\n const view = editor.value\n if (!view) return\n const myToken = ++langToken\n const language = activeLang.value\n\n if (!language) {\n if (myToken !== langToken || editor.value !== view) return\n view.dispatch({ effects: langCompartment.reconfigure([]) })\n return\n }\n\n const ext = await loadLanguage(language)\n if (myToken !== langToken || editor.value !== view) return\n view.dispatch({ effects: langCompartment.reconfigure(ext) })\n}\n\nfunction destroyEditor() {\n ++editorToken\n ++langToken\n editor.value?.destroy()\n editor.value = null\n lastEditorBody = undefined\n wasComposing = false\n}\n\nfunction createEditor() {\n destroyEditor()\n const myToken = ++editorToken\n const parent = containerRef.value\n if (!parent) return\n\n const config = shellConfig.value\n const body = model.value ?? ''\n const doc = buildFullDoc(body, config)\n const { bodyStart } = getShellRanges(doc.length, config)\n lastEditorBody = body\n\n const view = new EditorView({\n doc,\n selection: { anchor: bodyStart, head: bodyStart },\n extensions: buildExtensions(),\n parent\n })\n\n if (myToken !== editorToken) {\n view.destroy()\n return\n }\n\n editor.value = view\n applyLanguage()\n}\n\n/**\n * 将 model 正文同步为完整文档;可选同时 reconfigure shell,\n * 避免 prefix/suffix 变更时「先改扩展、后改文档」导致选区越界。\n */\nfunction syncDocFromModel(\n view: EditorView,\n body: string,\n config: ShellConfig,\n options?: { reconfigureShell?: boolean; prevShell?: ShellConfig }\n) {\n const current = view.state.doc\n const docChanged = !textEqualsShellDoc(current, body, config)\n const reconfigureShell = options?.reconfigureShell ?? false\n\n if (!docChanged && !reconfigureShell) return\n\n const nextDoc = buildFullDoc(body, config)\n const prevShell = options?.prevShell ?? config\n const prevRanges = getShellRanges(current.length, prevShell)\n const nextRanges = getShellRanges(nextDoc.length, config)\n const selection = mapSelectionToBody(view.state.selection, prevRanges, nextRanges, nextDoc.length)\n\n view.dispatch({\n ...(docChanged\n ? {\n changes: { from: 0, to: current.length, insert: nextDoc },\n selection,\n // 仅外壳变更时滚入视口;外部 model 回写不应打断用户滚动位置\n ...(reconfigureShell ? { scrollIntoView: true } : {})\n }\n : { selection }),\n ...(reconfigureShell ? { effects: shellCompartment.reconfigure(shellExtension(config)) } : {}),\n // 绕过 shell 的 change/transactionFilter,避免全量替换被拦截或选区被错误钳制\n filter: false\n })\n}\n\nwatch(\n containerRef,\n (el) => {\n if (el) createEditor()\n else destroyEditor()\n },\n { immediate: true, flush: 'post' }\n)\n\nwatch(activeLang, applyLanguage)\n\n/** langs 变化时,若当前 lang 未设或不在列表中则纠正为 langs[0] */\nwatch(\n langs,\n (list) => {\n if (!list.length) return\n if (!lang.value || !list.includes(lang.value)) {\n lang.value = list[0]\n }\n },\n { immediate: true }\n)\n\nwatch(\n () => props.dark,\n (dark) => {\n editor.value?.dispatch({ effects: themeCompartment.reconfigure(dark ? oneDark : []) })\n }\n)\n\nwatch(disabled, (v) => {\n editor.value?.dispatch({ effects: editableCompartment.reconfigure(EditorView.editable.of(!v)) })\n})\n\nwatch(readonly, (v) => {\n editor.value?.dispatch({ effects: readOnlyCompartment.reconfigure(EditorState.readOnly.of(v)) })\n})\n\nwatch(\n [model, editor, shellConfig],\n ([v, e], [, , prevShell]) => {\n if (!e) return\n // IME 组字中不接受外部回写,避免打断输入\n if (e.composing) return\n const config = shellConfig.value\n const shellChanged =\n !prevShell || prevShell.prefix !== config.prefix || prevShell.suffix !== config.suffix\n // 编辑器自身写出的 model 无需回写文档\n if (!shellChanged && v === lastEditorBody) return\n const body = v ?? ''\n syncDocFromModel(e, body, config, {\n reconfigureShell: shellChanged,\n prevShell: prevShell ?? config\n })\n lastEditorBody = body\n },\n { immediate: true }\n)\n\nonBeforeUnmount(destroyEditor)\n</script>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"code-editor.js","names":[],"sources":["../../../src/components/code-editor/code-editor.vue"],"sourcesContent":["<template>\n <u-scroll :class=\"className\" :style=\"rootStyle\" @keyup.enter.stop>\n <div ref=\"container\"></div>\n\n <u-select\n v-if=\"showLangSelect\"\n :class=\"cls.e('lang-select')\"\n v-model=\"lang\"\n size=\"small\"\n :options=\"langOptions\"\n :clearable=\"false\"\n :disabled=\"disabled\"\n />\n <span v-else-if=\"langLabel\" :class=\"cls.e('lang-label')\">{{ langLabel }}</span>\n </u-scroll>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Compartment, EditorState, type Extension, type Text } from '@codemirror/state'\nimport { oneDark } from '@codemirror/theme-one-dark'\nimport { EditorView, tooltips } from '@codemirror/view'\nimport { useFormFallbackProps } from '@veltra/compositions'\nimport { bem, injectFormContext, zIndex } from '@veltra/utils'\nimport {\n computed,\n onBeforeUnmount,\n shallowRef,\n useTemplateRef,\n watch,\n type CSSProperties\n} from 'vue'\n\nimport type { CodeEditorEmits, CodeEditorLang, CodeEditorProps } from '../../types'\nimport { UScroll } from '../scroll'\nimport { USelect } from '../select'\nimport { basicSetup } from './basic-setup'\nimport { loadLanguage } from './lang-loaders'\nimport {\n buildFullDoc,\n extractBodyFromText,\n getShellRanges,\n mapSelectionToBody,\n shellExtension,\n textEqualsShellDoc,\n type ShellConfig\n} from './shell-extension'\n\ndefineOptions({ name: 'UCodeEditor' })\n\nconst props = withDefaults(defineProps<CodeEditorProps>(), {\n disabled: undefined,\n readonly: undefined,\n dark: false,\n defaultLines: 8\n})\n\nconst emit = defineEmits<CodeEditorEmits>()\n\nconst model = defineModel<string>()\nconst lang = defineModel<CodeEditorLang>('lang')\n\nconst cls = bem('code-editor')\n\nconst { formProps } = injectFormContext()\n\nconst { disabled, readonly } = useFormFallbackProps([formProps ?? {}, props], {\n disabled: false,\n readonly: false\n})\n\nconst className = computed<string[]>(() => [\n cls.b,\n bem.is('disabled', disabled.value),\n bem.is('readonly', readonly.value),\n bem.is('dark', props.dark)\n])\n\nconst rootStyle = computed<CSSProperties>(() => ({\n '--u-code-editor-default-lines': Math.max(1, props.defaultLines)\n}))\n\nconst langs = computed(() => props.langs ?? [])\n\nconst showLangSelect = computed(() => langs.value.length > 1)\n\nconst langOptions = computed(() =>\n langs.value.map((value) => ({ label: value.toUpperCase(), value }))\n)\n\n/** 实际生效的语言:优先 lang model,否则回落 langs[0] */\nconst activeLang = computed<CodeEditorLang | undefined>(() => lang.value ?? langs.value[0])\n\n/** 仅一种语言时,在选择器位置展示名称 */\nconst langLabel = computed(() =>\n langs.value.length === 1 && activeLang.value ? activeLang.value.toUpperCase() : undefined\n)\n\nconst shellConfig = computed<ShellConfig>(() => ({\n prefix: props.prefix ?? '',\n suffix: props.suffix ?? ''\n}))\n\nconst containerRef = useTemplateRef('container')\nconst editor = shallowRef<EditorView | null>(null)\n\nconst themeCompartment = new Compartment()\nconst editableCompartment = new Compartment()\nconst readOnlyCompartment = new Compartment()\nconst langCompartment = new Compartment()\nconst shellCompartment = new Compartment()\n\n/** 聚焦时的正文快照,用于失焦时判断是否触发 change */\nlet focusSnapshot = ''\n\n/**\n * 编辑器最近一次写出的正文引用。\n * 用于短路 model → doc 同步,避免每次按键对整篇文档做 O(n) 相等比较。\n */\nlet lastEditorBody: string | undefined\n\n/** 上一帧是否处于 IME composition,用于在 composition 结束时补一次 model 同步 */\nlet wasComposing = false\n\n/** 将编辑器正文写回 v-model(跳过与上次相同的值,避免多余更新) */\nfunction writeModelFromDoc(doc: Text) {\n const nextBody = extractBodyFromText(doc, shellConfig.value)\n if (nextBody === lastEditorBody && nextBody === model.value) return\n lastEditorBody = nextBody\n model.value = nextBody\n}\n\nfunction buildExtensions(): Extension[] {\n return [\n basicSetup,\n tooltips({ parent: document.body }),\n EditorView.theme({ '.cm-tooltip': { zIndex: zIndex() } }),\n themeCompartment.of(props.dark ? oneDark : []),\n editableCompartment.of(EditorView.editable.of(!disabled.value)),\n readOnlyCompartment.of(EditorState.readOnly.of(readonly.value)),\n langCompartment.of([]),\n shellCompartment.of(shellExtension(shellConfig.value)),\n EditorView.updateListener.of((update) => {\n const composing = update.view.composing\n\n // IME 进行中不写回,避免中间态触发父级重渲染;结束后再统一同步\n if (update.docChanged && !composing) {\n writeModelFromDoc(update.state.doc)\n } else if (wasComposing && !composing) {\n writeModelFromDoc(update.state.doc)\n }\n wasComposing = composing\n\n if (!update.focusChanged) return\n if (update.view.hasFocus) {\n focusSnapshot = extractBodyFromText(update.state.doc, shellConfig.value)\n return\n }\n const current = extractBodyFromText(update.state.doc, shellConfig.value)\n if (current !== focusSnapshot) {\n emit('change', current)\n }\n })\n ]\n}\n\nlet editorToken = 0\nlet langToken = 0\n\nasync function applyLanguage() {\n const view = editor.value\n if (!view) return\n const myToken = ++langToken\n const language = activeLang.value\n\n if (!language) {\n if (myToken !== langToken || editor.value !== view) return\n view.dispatch({ effects: langCompartment.reconfigure([]) })\n return\n }\n\n const ext = await loadLanguage(language)\n if (myToken !== langToken || editor.value !== view) return\n view.dispatch({ effects: langCompartment.reconfigure(ext) })\n}\n\nfunction destroyEditor() {\n ++editorToken\n ++langToken\n editor.value?.destroy()\n editor.value = null\n lastEditorBody = undefined\n wasComposing = false\n}\n\nfunction createEditor() {\n destroyEditor()\n const myToken = ++editorToken\n const parent = containerRef.value\n if (!parent) return\n\n const config = shellConfig.value\n const body = model.value ?? ''\n const doc = buildFullDoc(body, config)\n const { bodyStart } = getShellRanges(doc.length, config)\n lastEditorBody = body\n\n const view = new EditorView({\n doc,\n selection: { anchor: bodyStart, head: bodyStart },\n extensions: buildExtensions(),\n parent\n })\n\n if (myToken !== editorToken) {\n view.destroy()\n return\n }\n\n editor.value = view\n applyLanguage()\n}\n\n/**\n * 将 model 正文同步为完整文档;可选同时 reconfigure shell,\n * 避免 prefix/suffix 变更时「先改扩展、后改文档」导致选区越界。\n */\nfunction syncDocFromModel(\n view: EditorView,\n body: string,\n config: ShellConfig,\n options?: { reconfigureShell?: boolean; prevShell?: ShellConfig }\n) {\n const current = view.state.doc\n const docChanged = !textEqualsShellDoc(current, body, config)\n const reconfigureShell = options?.reconfigureShell ?? false\n\n if (!docChanged && !reconfigureShell) return\n\n const nextDoc = buildFullDoc(body, config)\n const prevShell = options?.prevShell ?? config\n const prevRanges = getShellRanges(current.length, prevShell)\n const nextRanges = getShellRanges(nextDoc.length, config)\n const selection = mapSelectionToBody(view.state.selection, prevRanges, nextRanges, nextDoc.length)\n\n view.dispatch({\n ...(docChanged\n ? {\n changes: { from: 0, to: current.length, insert: nextDoc },\n selection,\n // 仅外壳变更时滚入视口;外部 model 回写不应打断用户滚动位置\n ...(reconfigureShell ? { scrollIntoView: true } : {})\n }\n : { selection }),\n ...(reconfigureShell ? { effects: shellCompartment.reconfigure(shellExtension(config)) } : {}),\n // 绕过 shell 的 change/transactionFilter,避免全量替换被拦截或选区被错误钳制\n filter: false\n })\n}\n\nwatch(\n containerRef,\n (el) => {\n if (el) createEditor()\n else destroyEditor()\n },\n { immediate: true, flush: 'post' }\n)\n\nwatch(activeLang, applyLanguage)\n\n/** langs 变化时,若当前 lang 未设或不在列表中则纠正为 langs[0] */\nwatch(\n langs,\n (list) => {\n if (!list.length) return\n if (!lang.value || !list.includes(lang.value)) {\n lang.value = list[0]\n }\n },\n { immediate: true }\n)\n\nwatch(\n () => props.dark,\n (dark) => {\n editor.value?.dispatch({ effects: themeCompartment.reconfigure(dark ? oneDark : []) })\n }\n)\n\nwatch(disabled, (v) => {\n editor.value?.dispatch({ effects: editableCompartment.reconfigure(EditorView.editable.of(!v)) })\n})\n\nwatch(readonly, (v) => {\n editor.value?.dispatch({ effects: readOnlyCompartment.reconfigure(EditorState.readOnly.of(v)) })\n})\n\nwatch(\n [model, editor, shellConfig],\n ([v, e], [, , prevShell]) => {\n if (!e) return\n // IME 组字中不接受外部回写,避免打断输入\n if (e.composing) return\n const config = shellConfig.value\n const shellChanged =\n !prevShell || prevShell.prefix !== config.prefix || prevShell.suffix !== config.suffix\n // 编辑器自身写出的 model 无需回写文档\n if (!shellChanged && v === lastEditorBody) return\n const body = v ?? ''\n syncDocFromModel(e, body, config, {\n reconfigureShell: shellChanged,\n prevShell: prevShell ?? config\n })\n lastEditorBody = body\n },\n { immediate: true }\n)\n\nonBeforeUnmount(destroyEditor)\n</script>\n"],"mappings":"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"}
|
|
@@ -4,7 +4,8 @@ const LANG_LOADERS = {
|
|
|
4
4
|
js: () => import("../../node_modules/.bun/@codemirror_lang-javascript@6.2.5/node_modules/@codemirror/lang-javascript/dist/index.js").then((m) => m.javascript({ typescript: true })),
|
|
5
5
|
sql: () => import("../../node_modules/.bun/@codemirror_lang-sql@6.10.0/node_modules/@codemirror/lang-sql/dist/index.js").then((m) => m.sql()),
|
|
6
6
|
java: () => import("../../node_modules/.bun/@codemirror_lang-java@6.0.2/node_modules/@codemirror/lang-java/dist/index.js").then((m) => m.java()),
|
|
7
|
-
json: () => import("../../node_modules/.bun/@codemirror_lang-json@6.0.2/node_modules/@codemirror/lang-json/dist/index.js").then((m) => m.json())
|
|
7
|
+
json: () => import("../../node_modules/.bun/@codemirror_lang-json@6.0.2/node_modules/@codemirror/lang-json/dist/index.js").then((m) => m.json()),
|
|
8
|
+
spel: () => import("./langs/spel/index.js").then((m) => m.spel())
|
|
8
9
|
};
|
|
9
10
|
/** 按需加载语言扩展;模块缓存由运行时保证,重复切换同一语言不会重复下载 */
|
|
10
11
|
function loadLanguage(lang) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lang-loaders.js","names":[],"sources":["../../../src/components/code-editor/lang-loaders.ts"],"sourcesContent":["import type { Extension } from '@codemirror/state'\n\nimport type { CodeEditorLang } from '../../types'\n\n/** 语言包动态加载器,新增语言只需在此注册一条 */\nconst LANG_LOADERS: Record<CodeEditorLang, () => Promise<Extension>> = {\n js: () => import('@codemirror/lang-javascript').then((m) => m.javascript({ typescript: true })),\n sql: () => import('@codemirror/lang-sql').then((m) => m.sql()),\n java: () => import('@codemirror/lang-java').then((m) => m.java()),\n json: () => import('@codemirror/lang-json').then((m) => m.json())\n}\n\n/** 按需加载语言扩展;模块缓存由运行时保证,重复切换同一语言不会重复下载 */\nexport function loadLanguage(lang: CodeEditorLang): Promise<Extension> {\n return LANG_LOADERS[lang]()\n}\n"],"mappings":";;AAKA,MAAM,eAAiE;CACrE,UAAU,OAAO,mHAA8B,CAAC,MAAM,MAAM,EAAE,WAAW,EAAE,YAAY,KAAK,CAAC,CAAC;CAC9F,WAAW,OAAO,sGAAuB,CAAC,MAAM,MAAM,EAAE,IAAI,CAAC;CAC7D,YAAY,OAAO,uGAAwB,CAAC,MAAM,MAAM,EAAE,KAAK,CAAC;CAChE,YAAY,OAAO,uGAAwB,CAAC,MAAM,MAAM,EAAE,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"lang-loaders.js","names":[],"sources":["../../../src/components/code-editor/lang-loaders.ts"],"sourcesContent":["import type { Extension } from '@codemirror/state'\n\nimport type { CodeEditorLang } from '../../types'\n\n/** 语言包动态加载器,新增语言只需在此注册一条 */\nconst LANG_LOADERS: Record<CodeEditorLang, () => Promise<Extension>> = {\n js: () => import('@codemirror/lang-javascript').then((m) => m.javascript({ typescript: true })),\n sql: () => import('@codemirror/lang-sql').then((m) => m.sql()),\n java: () => import('@codemirror/lang-java').then((m) => m.java()),\n json: () => import('@codemirror/lang-json').then((m) => m.json()),\n spel: () => import('./langs/spel').then((m) => m.spel())\n}\n\n/** 按需加载语言扩展;模块缓存由运行时保证,重复切换同一语言不会重复下载 */\nexport function loadLanguage(lang: CodeEditorLang): Promise<Extension> {\n return LANG_LOADERS[lang]()\n}\n"],"mappings":";;AAKA,MAAM,eAAiE;CACrE,UAAU,OAAO,mHAA8B,CAAC,MAAM,MAAM,EAAE,WAAW,EAAE,YAAY,KAAK,CAAC,CAAC;CAC9F,WAAW,OAAO,sGAAuB,CAAC,MAAM,MAAM,EAAE,IAAI,CAAC;CAC7D,YAAY,OAAO,uGAAwB,CAAC,MAAM,MAAM,EAAE,KAAK,CAAC;CAChE,YAAY,OAAO,uGAAwB,CAAC,MAAM,MAAM,EAAE,KAAK,CAAC;CAChE,YAAY,OAAO,wBAAe,CAAC,MAAM,MAAM,EAAE,KAAK,CAAC;AACzD;;AAGA,SAAgB,aAAa,MAA0C;CACrE,OAAO,aAAa,KAAK,CAAC;AAC5B"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { completeFromList } from "../../../../node_modules/.bun/@codemirror_autocomplete@6.20.3/node_modules/@codemirror/autocomplete/dist/index.js";
|
|
2
|
+
/** 基于关键字列表的补全源 */
|
|
3
|
+
const spelCompletion = completeFromList([
|
|
4
|
+
{
|
|
5
|
+
label: "true",
|
|
6
|
+
type: "constant"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
label: "false",
|
|
10
|
+
type: "constant"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
label: "null",
|
|
14
|
+
type: "constant"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
label: "and",
|
|
18
|
+
type: "keyword"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: "or",
|
|
22
|
+
type: "keyword"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: "not",
|
|
26
|
+
type: "keyword"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: "eq",
|
|
30
|
+
type: "keyword"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
label: "ne",
|
|
34
|
+
type: "keyword"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
label: "lt",
|
|
38
|
+
type: "keyword"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
label: "le",
|
|
42
|
+
type: "keyword"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
label: "gt",
|
|
46
|
+
type: "keyword"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
label: "ge",
|
|
50
|
+
type: "keyword"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
label: "matches",
|
|
54
|
+
type: "keyword"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
label: "between",
|
|
58
|
+
type: "keyword"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
label: "instanceof",
|
|
62
|
+
type: "keyword"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
label: "new",
|
|
66
|
+
type: "keyword"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
label: "T",
|
|
70
|
+
type: "type",
|
|
71
|
+
detail: "T(Type)",
|
|
72
|
+
info: "类型引用"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
label: "#this",
|
|
76
|
+
type: "variable",
|
|
77
|
+
detail: "当前上下文"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
label: "#root",
|
|
81
|
+
type: "variable",
|
|
82
|
+
detail: "根对象"
|
|
83
|
+
}
|
|
84
|
+
]);
|
|
85
|
+
//#endregion
|
|
86
|
+
export { spelCompletion };
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=completion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion.js","names":[],"sources":["../../../../../src/components/code-editor/langs/spel/completion.ts"],"sourcesContent":["import { completeFromList, type Completion } from '@codemirror/autocomplete'\n\n/** SpEL 关键字与常用内置标识补全 */\nconst SPEL_COMPLETIONS: Completion[] = [\n // 字面量\n { label: 'true', type: 'constant' },\n { label: 'false', type: 'constant' },\n { label: 'null', type: 'constant' },\n // 关键字 / 运算符词\n { label: 'and', type: 'keyword' },\n { label: 'or', type: 'keyword' },\n { label: 'not', type: 'keyword' },\n { label: 'eq', type: 'keyword' },\n { label: 'ne', type: 'keyword' },\n { label: 'lt', type: 'keyword' },\n { label: 'le', type: 'keyword' },\n { label: 'gt', type: 'keyword' },\n { label: 'ge', type: 'keyword' },\n { label: 'matches', type: 'keyword' },\n { label: 'between', type: 'keyword' },\n { label: 'instanceof', type: 'keyword' },\n { label: 'new', type: 'keyword' },\n // 类型引用与上下文变量\n { label: 'T', type: 'type', detail: 'T(Type)', info: '类型引用' },\n { label: '#this', type: 'variable', detail: '当前上下文' },\n { label: '#root', type: 'variable', detail: '根对象' }\n]\n\n/** 基于关键字列表的补全源 */\nexport const spelCompletion = completeFromList(SPEL_COMPLETIONS)\n"],"mappings":";;AA6BA,MAAa,iBAAiB,iBAAiB;CAxB7C;EAAE,OAAO;EAAQ,MAAM;CAAW;CAClC;EAAE,OAAO;EAAS,MAAM;CAAW;CACnC;EAAE,OAAO;EAAQ,MAAM;CAAW;CAElC;EAAE,OAAO;EAAO,MAAM;CAAU;CAChC;EAAE,OAAO;EAAM,MAAM;CAAU;CAC/B;EAAE,OAAO;EAAO,MAAM;CAAU;CAChC;EAAE,OAAO;EAAM,MAAM;CAAU;CAC/B;EAAE,OAAO;EAAM,MAAM;CAAU;CAC/B;EAAE,OAAO;EAAM,MAAM;CAAU;CAC/B;EAAE,OAAO;EAAM,MAAM;CAAU;CAC/B;EAAE,OAAO;EAAM,MAAM;CAAU;CAC/B;EAAE,OAAO;EAAM,MAAM;CAAU;CAC/B;EAAE,OAAO;EAAW,MAAM;CAAU;CACpC;EAAE,OAAO;EAAW,MAAM;CAAU;CACpC;EAAE,OAAO;EAAc,MAAM;CAAU;CACvC;EAAE,OAAO;EAAO,MAAM;CAAU;CAEhC;EAAE,OAAO;EAAK,MAAM;EAAQ,QAAQ;EAAW,MAAM;CAAO;CAC5D;EAAE,OAAO;EAAS,MAAM;EAAY,QAAQ;CAAQ;CACpD;EAAE,OAAO;EAAS,MAAM;EAAY,QAAQ;CAAM;AAIL,CAAgB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { LanguageSupport } from "../../../../node_modules/.bun/@codemirror_language@6.12.4/node_modules/@codemirror/language/dist/index.js";
|
|
2
|
+
import { spelCompletion } from "./completion.js";
|
|
3
|
+
import { spelLanguage } from "./language.js";
|
|
4
|
+
//#region src/components/code-editor/langs/spel/index.ts
|
|
5
|
+
/** SpEL(Spring Expression Language)语言支持:语法高亮 + 基础补全 */
|
|
6
|
+
function spel() {
|
|
7
|
+
return new LanguageSupport(spelLanguage, [spelLanguage.data.of({ autocomplete: spelCompletion })]);
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { spel };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../../src/components/code-editor/langs/spel/index.ts"],"sourcesContent":["import { LanguageSupport } from '@codemirror/language'\n\nimport { spelCompletion } from './completion'\nimport { spelLanguage } from './language'\n\n/** SpEL(Spring Expression Language)语言支持:语法高亮 + 基础补全 */\nexport function spel(): LanguageSupport {\n return new LanguageSupport(spelLanguage, [spelLanguage.data.of({ autocomplete: spelCompletion })])\n}\n\nexport { spelLanguage } from './language'\nexport { spelCompletion } from './completion'\n"],"mappings":";;;;;AAMA,SAAgB,OAAwB;CACtC,OAAO,IAAI,gBAAgB,cAAc,CAAC,aAAa,KAAK,GAAG,EAAE,cAAc,eAAe,CAAC,CAAC,CAAC;AACnG"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { StreamLanguage } from "../../../../node_modules/.bun/@codemirror_language@6.12.4/node_modules/@codemirror/language/dist/index.js";
|
|
2
|
+
//#region src/components/code-editor/langs/spel/language.ts
|
|
3
|
+
const KEYWORDS = new Set([
|
|
4
|
+
"new",
|
|
5
|
+
"instanceof",
|
|
6
|
+
"matches",
|
|
7
|
+
"between",
|
|
8
|
+
"and",
|
|
9
|
+
"or",
|
|
10
|
+
"not",
|
|
11
|
+
"eq",
|
|
12
|
+
"ne",
|
|
13
|
+
"lt",
|
|
14
|
+
"le",
|
|
15
|
+
"gt",
|
|
16
|
+
"ge"
|
|
17
|
+
]);
|
|
18
|
+
const BOOL_LITERALS = new Set(["true", "false"]);
|
|
19
|
+
/** 多字符运算符,按长度优先匹配 */
|
|
20
|
+
const OPERATORS = [
|
|
21
|
+
"?.",
|
|
22
|
+
"?:",
|
|
23
|
+
"?[",
|
|
24
|
+
"![",
|
|
25
|
+
"==",
|
|
26
|
+
"!=",
|
|
27
|
+
"<=",
|
|
28
|
+
">=",
|
|
29
|
+
"&&",
|
|
30
|
+
"||",
|
|
31
|
+
"++",
|
|
32
|
+
"--"
|
|
33
|
+
];
|
|
34
|
+
function isIdentStart(ch) {
|
|
35
|
+
return /[A-Za-z_]/.test(ch);
|
|
36
|
+
}
|
|
37
|
+
function isIdentChar(ch) {
|
|
38
|
+
return /[A-Za-z0-9_]/.test(ch);
|
|
39
|
+
}
|
|
40
|
+
function tokenString(stream, quote) {
|
|
41
|
+
let escaped = false;
|
|
42
|
+
while (!stream.eol()) {
|
|
43
|
+
const next = stream.next();
|
|
44
|
+
if (next === quote && !escaped) break;
|
|
45
|
+
escaped = !escaped && next === "\\";
|
|
46
|
+
}
|
|
47
|
+
return "string";
|
|
48
|
+
}
|
|
49
|
+
function tokenNumber(stream) {
|
|
50
|
+
stream.match(/^\d+(\.\d+)?([eE][+-]?\d+)?/);
|
|
51
|
+
return "number";
|
|
52
|
+
}
|
|
53
|
+
function tokenIdentifier(stream, state) {
|
|
54
|
+
stream.eatWhile(isIdentChar);
|
|
55
|
+
const word = stream.current();
|
|
56
|
+
state.afterT = false;
|
|
57
|
+
if (state.typeDepth > 0) {
|
|
58
|
+
state.afterDot = false;
|
|
59
|
+
return "typeName";
|
|
60
|
+
}
|
|
61
|
+
if (state.afterDot) {
|
|
62
|
+
state.afterDot = false;
|
|
63
|
+
return "propertyName";
|
|
64
|
+
}
|
|
65
|
+
if (BOOL_LITERALS.has(word)) return "bool";
|
|
66
|
+
if (word === "null") return "null";
|
|
67
|
+
if (KEYWORDS.has(word)) return "keyword";
|
|
68
|
+
if (word === "T" && stream.peek() === "(") {
|
|
69
|
+
state.afterT = true;
|
|
70
|
+
return "typeName";
|
|
71
|
+
}
|
|
72
|
+
return "name";
|
|
73
|
+
}
|
|
74
|
+
function tokenSpel(stream, state) {
|
|
75
|
+
if (stream.eatSpace()) return null;
|
|
76
|
+
const ch = stream.peek();
|
|
77
|
+
if (!ch) return null;
|
|
78
|
+
if (ch === "'" || ch === "\"") {
|
|
79
|
+
stream.next();
|
|
80
|
+
state.afterDot = false;
|
|
81
|
+
state.afterT = false;
|
|
82
|
+
return tokenString(stream, ch);
|
|
83
|
+
}
|
|
84
|
+
if (/\d/.test(ch)) {
|
|
85
|
+
state.afterDot = false;
|
|
86
|
+
state.afterT = false;
|
|
87
|
+
return tokenNumber(stream);
|
|
88
|
+
}
|
|
89
|
+
if (ch === "#") {
|
|
90
|
+
stream.next();
|
|
91
|
+
state.afterDot = false;
|
|
92
|
+
state.afterT = false;
|
|
93
|
+
const peek = stream.peek();
|
|
94
|
+
if (peek && isIdentStart(peek)) {
|
|
95
|
+
stream.eatWhile(isIdentChar);
|
|
96
|
+
return "variableName";
|
|
97
|
+
}
|
|
98
|
+
return "operator";
|
|
99
|
+
}
|
|
100
|
+
for (const op of OPERATORS) if (stream.match(op)) {
|
|
101
|
+
state.afterDot = false;
|
|
102
|
+
state.afterT = false;
|
|
103
|
+
return "operator";
|
|
104
|
+
}
|
|
105
|
+
if ("+-*/%^!=<>&|?:".includes(ch)) {
|
|
106
|
+
stream.next();
|
|
107
|
+
state.afterDot = false;
|
|
108
|
+
state.afterT = false;
|
|
109
|
+
return "operator";
|
|
110
|
+
}
|
|
111
|
+
if (ch === "(") {
|
|
112
|
+
stream.next();
|
|
113
|
+
state.afterDot = false;
|
|
114
|
+
if (state.afterT || state.typeDepth > 0) {
|
|
115
|
+
state.typeDepth++;
|
|
116
|
+
state.afterT = false;
|
|
117
|
+
}
|
|
118
|
+
return "paren";
|
|
119
|
+
}
|
|
120
|
+
if (ch === ")") {
|
|
121
|
+
stream.next();
|
|
122
|
+
state.afterDot = false;
|
|
123
|
+
state.afterT = false;
|
|
124
|
+
if (state.typeDepth > 0) state.typeDepth--;
|
|
125
|
+
return "paren";
|
|
126
|
+
}
|
|
127
|
+
if (ch === "[" || ch === "]") {
|
|
128
|
+
stream.next();
|
|
129
|
+
state.afterDot = false;
|
|
130
|
+
state.afterT = false;
|
|
131
|
+
return "paren";
|
|
132
|
+
}
|
|
133
|
+
if (ch === "{" || ch === "}") {
|
|
134
|
+
stream.next();
|
|
135
|
+
state.afterDot = false;
|
|
136
|
+
state.afterT = false;
|
|
137
|
+
return "brace";
|
|
138
|
+
}
|
|
139
|
+
if (ch === ",") {
|
|
140
|
+
stream.next();
|
|
141
|
+
state.afterDot = false;
|
|
142
|
+
state.afterT = false;
|
|
143
|
+
return "punctuation";
|
|
144
|
+
}
|
|
145
|
+
if (ch === ".") {
|
|
146
|
+
stream.next();
|
|
147
|
+
state.afterDot = true;
|
|
148
|
+
state.afterT = false;
|
|
149
|
+
return "punctuation";
|
|
150
|
+
}
|
|
151
|
+
if (isIdentStart(ch)) return tokenIdentifier(stream, state);
|
|
152
|
+
stream.next();
|
|
153
|
+
state.afterDot = false;
|
|
154
|
+
state.afterT = false;
|
|
155
|
+
return "punctuation";
|
|
156
|
+
}
|
|
157
|
+
/** SpEL StreamLanguage */
|
|
158
|
+
const spelLanguage = StreamLanguage.define({
|
|
159
|
+
name: "spel",
|
|
160
|
+
startState: () => ({
|
|
161
|
+
typeDepth: 0,
|
|
162
|
+
afterDot: false,
|
|
163
|
+
afterT: false
|
|
164
|
+
}),
|
|
165
|
+
token: tokenSpel,
|
|
166
|
+
languageData: { closeBrackets: { brackets: [
|
|
167
|
+
"(",
|
|
168
|
+
"[",
|
|
169
|
+
"{",
|
|
170
|
+
"'",
|
|
171
|
+
"\""
|
|
172
|
+
] } }
|
|
173
|
+
});
|
|
174
|
+
//#endregion
|
|
175
|
+
export { spelLanguage };
|
|
176
|
+
|
|
177
|
+
//# sourceMappingURL=language.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language.js","names":[],"sources":["../../../../../src/components/code-editor/langs/spel/language.ts"],"sourcesContent":["import { StreamLanguage, type StringStream } from '@codemirror/language'\n\n/** 词法状态:跟踪 T(...) 类型引用与属性访问 */\ninterface SpelState {\n /** T(...) 内括号深度,>0 表示处于类型名上下文 */\n typeDepth: number\n /** 刚消费完 `.`,下一标识符按属性名着色 */\n afterDot: boolean\n /** 刚消费完类型引用关键字 `T`,下一 `(` 进入类型上下文 */\n afterT: boolean\n}\n\nconst KEYWORDS = new Set([\n 'new',\n 'instanceof',\n 'matches',\n 'between',\n 'and',\n 'or',\n 'not',\n 'eq',\n 'ne',\n 'lt',\n 'le',\n 'gt',\n 'ge'\n])\n\nconst BOOL_LITERALS = new Set(['true', 'false'])\n\n/** 多字符运算符,按长度优先匹配 */\nconst OPERATORS = ['?.', '?:', '?[', '![', '==', '!=', '<=', '>=', '&&', '||', '++', '--']\n\nfunction isIdentStart(ch: string): boolean {\n return /[A-Za-z_]/.test(ch)\n}\n\nfunction isIdentChar(ch: string): boolean {\n return /[A-Za-z0-9_]/.test(ch)\n}\n\nfunction tokenString(stream: StringStream, quote: string): string {\n let escaped = false\n while (!stream.eol()) {\n const next = stream.next()\n if (next === quote && !escaped) break\n escaped = !escaped && next === '\\\\'\n }\n return 'string'\n}\n\nfunction tokenNumber(stream: StringStream): string {\n stream.match(/^\\d+(\\.\\d+)?([eE][+-]?\\d+)?/)\n return 'number'\n}\n\nfunction tokenIdentifier(stream: StringStream, state: SpelState): string {\n stream.eatWhile(isIdentChar)\n const word = stream.current()\n state.afterT = false\n\n if (state.typeDepth > 0) {\n state.afterDot = false\n return 'typeName'\n }\n\n if (state.afterDot) {\n state.afterDot = false\n return 'propertyName'\n }\n\n if (BOOL_LITERALS.has(word)) return 'bool'\n if (word === 'null') return 'null'\n if (KEYWORDS.has(word)) return 'keyword'\n\n // T(...) 类型引用\n if (word === 'T' && stream.peek() === '(') {\n state.afterT = true\n return 'typeName'\n }\n\n return 'name'\n}\n\nfunction tokenSpel(stream: StringStream, state: SpelState): string | null {\n if (stream.eatSpace()) return null\n\n const ch = stream.peek()\n if (!ch) return null\n\n // 字符串\n if (ch === \"'\" || ch === '\"') {\n stream.next()\n state.afterDot = false\n state.afterT = false\n return tokenString(stream, ch)\n }\n\n // 数字\n if (/\\d/.test(ch)) {\n state.afterDot = false\n state.afterT = false\n return tokenNumber(stream)\n }\n\n // #变量 / #this / #root\n if (ch === '#') {\n stream.next()\n state.afterDot = false\n state.afterT = false\n const peek = stream.peek()\n if (peek && isIdentStart(peek)) {\n stream.eatWhile(isIdentChar)\n return 'variableName'\n }\n return 'operator'\n }\n\n // 多字符运算符\n for (const op of OPERATORS) {\n if (stream.match(op)) {\n state.afterDot = false\n state.afterT = false\n return 'operator'\n }\n }\n\n // 单字符运算符\n if ('+-*/%^!=<>&|?:'.includes(ch)) {\n stream.next()\n state.afterDot = false\n state.afterT = false\n return 'operator'\n }\n\n if (ch === '(') {\n stream.next()\n state.afterDot = false\n if (state.afterT || state.typeDepth > 0) {\n state.typeDepth++\n state.afterT = false\n }\n return 'paren'\n }\n\n if (ch === ')') {\n stream.next()\n state.afterDot = false\n state.afterT = false\n if (state.typeDepth > 0) state.typeDepth--\n return 'paren'\n }\n\n if (ch === '[' || ch === ']') {\n stream.next()\n state.afterDot = false\n state.afterT = false\n return 'paren'\n }\n\n if (ch === '{' || ch === '}') {\n stream.next()\n state.afterDot = false\n state.afterT = false\n return 'brace'\n }\n\n if (ch === ',') {\n stream.next()\n state.afterDot = false\n state.afterT = false\n return 'punctuation'\n }\n\n if (ch === '.') {\n stream.next()\n state.afterDot = true\n state.afterT = false\n return 'punctuation'\n }\n\n if (isIdentStart(ch)) {\n return tokenIdentifier(stream, state)\n }\n\n stream.next()\n state.afterDot = false\n state.afterT = false\n return 'punctuation'\n}\n\n/** SpEL StreamLanguage */\nexport const spelLanguage = StreamLanguage.define<SpelState>({\n name: 'spel',\n startState: () => ({ typeDepth: 0, afterDot: false, afterT: false }),\n token: tokenSpel,\n languageData: { closeBrackets: { brackets: ['(', '[', '{', \"'\", '\"'] } }\n})\n"],"mappings":"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"}
|
|
@@ -50,6 +50,17 @@
|
|
|
50
50
|
.u-code-editor__lang-select input {
|
|
51
51
|
text-align: right;
|
|
52
52
|
}
|
|
53
|
+
.u-code-editor__lang-label {
|
|
54
|
+
position: absolute;
|
|
55
|
+
top: 2px;
|
|
56
|
+
right: 8px;
|
|
57
|
+
z-index: 2;
|
|
58
|
+
font-size: 12px;
|
|
59
|
+
line-height: 24px;
|
|
60
|
+
color: var(--u-code-editor-gutter-fg);
|
|
61
|
+
pointer-events: none;
|
|
62
|
+
user-select: none;
|
|
63
|
+
}
|
|
53
64
|
.u-code-editor .cm-editor {
|
|
54
65
|
background: transparent;
|
|
55
66
|
color: var(--u-code-editor-fg);
|
|
@@ -6,10 +6,10 @@ declare const __VLS_export: import("vue").DefineComponent<DatePickerProps, {}, {
|
|
|
6
6
|
"onUpdate:modelValue"?: ((value?: string | number | Date | undefined) => any) | undefined;
|
|
7
7
|
}>, {
|
|
8
8
|
type: "date" | "month" | "year";
|
|
9
|
-
disabled: boolean;
|
|
10
|
-
readonly: boolean;
|
|
11
9
|
placeholder: string;
|
|
12
10
|
clearable: boolean;
|
|
11
|
+
disabled: boolean;
|
|
12
|
+
readonly: boolean;
|
|
13
13
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
14
|
declare const _default: typeof __VLS_export;
|
|
15
15
|
//#endregion
|
|
@@ -6,10 +6,10 @@ declare const __VLS_export: import("vue").DefineComponent<DateRangePickerProps,
|
|
|
6
6
|
"onUpdate:modelValue"?: ((value?: [string, string] | undefined) => any) | undefined;
|
|
7
7
|
}>, {
|
|
8
8
|
type: "date" | "month" | "year";
|
|
9
|
-
disabled: boolean;
|
|
10
|
-
readonly: boolean;
|
|
11
9
|
placeholder: [string, string];
|
|
12
10
|
clearable: boolean;
|
|
11
|
+
disabled: boolean;
|
|
12
|
+
readonly: boolean;
|
|
13
13
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
14
|
declare const _default: typeof __VLS_export;
|
|
15
15
|
//#endregion
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { EmptyProps } from "../../types/empty.js";
|
|
2
2
|
//#region src/components/empty/empty.vue.d.ts
|
|
3
3
|
declare const __VLS_export: import("vue").DefineComponent<EmptyProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<EmptyProps> & Readonly<{}>, {
|
|
4
|
-
size: number;
|
|
5
4
|
text: string;
|
|
5
|
+
size: number;
|
|
6
6
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
7
7
|
declare const _default: typeof __VLS_export;
|
|
8
8
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Fragment, computed, createCommentVNode, createElementBlock, defineComponent, getCurrentInstance, normalizeClass, openBlock, ref, renderList, toDisplayString, unref } from "vue";
|
|
1
|
+
import { Fragment, computed, createCommentVNode, createElementBlock, defineComponent, getCurrentInstance, normalizeClass, openBlock, ref, renderList, toDisplayString, unref, watch } from "vue";
|
|
2
2
|
import { useFallbackProps } from "@veltra/compositions";
|
|
3
3
|
import { bem } from "@veltra/utils";
|
|
4
4
|
//#region src/components/grid-input/grid-input.vue
|
|
@@ -30,8 +30,25 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
30
30
|
const position = ref(-1);
|
|
31
31
|
const blur = ref(false);
|
|
32
32
|
const instance = getCurrentInstance();
|
|
33
|
+
/** 将格子数组拼成对外 modelValue */
|
|
34
|
+
const joinValue = (cells) => cells.join(props.separator);
|
|
35
|
+
/** 按分隔符解析 modelValue 为格子内容 */
|
|
36
|
+
const parseValue = (value) => {
|
|
37
|
+
if (!value) return [];
|
|
38
|
+
const sep = props.separator;
|
|
39
|
+
return (sep === "" ? Array.from(value) : value.split(sep)).filter((part) => part !== "").slice(0, props.length);
|
|
40
|
+
};
|
|
41
|
+
watch(() => [
|
|
42
|
+
props.modelValue,
|
|
43
|
+
props.separator,
|
|
44
|
+
props.length
|
|
45
|
+
], () => {
|
|
46
|
+
const next = parseValue(props.modelValue);
|
|
47
|
+
if (joinValue(next) === joinValue(valueArray.value)) return;
|
|
48
|
+
valueArray.value = next;
|
|
49
|
+
}, { immediate: true });
|
|
33
50
|
const emitValue = () => {
|
|
34
|
-
const value = valueArray.value
|
|
51
|
+
const value = joinValue(valueArray.value);
|
|
35
52
|
emit("input", value);
|
|
36
53
|
emit("update:modelValue", value);
|
|
37
54
|
};
|