Skip to content

Commit 5acbfb4

Browse files
authored
fix(editor): Prevent duplicate values in preview for SQL editor (#9129)
1 parent 6c63cd9 commit 5acbfb4

File tree

6 files changed

+236
-198
lines changed

6 files changed

+236
-198
lines changed

packages/editor-ui/src/components/ExpressionEdit.vue

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@
6565
{{ $locale.baseText('expressionEdit.resultOfItem1') }}
6666
</div>
6767
<div :class="{ 'ph-no-capture': redactValues }">
68-
<ExpressionEditorModalOutput
68+
<ExpressionOutput
6969
ref="expressionResult"
7070
:segments="segments"
71+
:extensions="theme"
7172
data-test-id="expression-modal-output"
7273
/>
7374
</div>
@@ -82,7 +83,6 @@
8283
import { defineComponent } from 'vue';
8384
import { mapStores } from 'pinia';
8485
import ExpressionEditorModalInput from '@/components/ExpressionEditorModal/ExpressionEditorModalInput.vue';
85-
import ExpressionEditorModalOutput from '@/components/ExpressionEditorModal/ExpressionEditorModalOutput.vue';
8686
import VariableSelector from '@/components/VariableSelector.vue';
8787
8888
import type { IVariableItemSelected } from '@/Interface';
@@ -96,12 +96,14 @@ import { createExpressionTelemetryPayload } from '@/utils/telemetryUtils';
9696
import { useDebounce } from '@/composables/useDebounce';
9797
9898
import type { Segment } from '@/types/expressions';
99+
import ExpressionOutput from './InlineExpressionEditor/ExpressionOutput.vue';
100+
import { outputTheme } from './ExpressionEditorModal/theme';
99101
100102
export default defineComponent({
101103
name: 'ExpressionEdit',
102104
components: {
103105
ExpressionEditorModalInput,
104-
ExpressionEditorModalOutput,
106+
ExpressionOutput,
105107
VariableSelector,
106108
},
107109
props: {
@@ -149,6 +151,7 @@ export default defineComponent({
149151
latestValue: '',
150152
segments: [] as Segment[],
151153
expressionsDocsUrl: EXPRESSIONS_DOCS_URL,
154+
theme: outputTheme(),
152155
};
153156
},
154157
computed: {
@@ -160,11 +163,7 @@ export default defineComponent({
160163
this.latestValue = this.modelValue;
161164
162165
const resolvedExpressionValue =
163-
(
164-
this.$refs.expressionResult as {
165-
getValue: () => string;
166-
}
167-
)?.getValue() || '';
166+
(this.$refs.expressionResult as InstanceType<typeof ExpressionOutput>)?.getValue() || '';
168167
void this.externalHooks.run('expressionEdit.dialogVisibleChanged', {
169168
dialogVisible: newValue,
170169
parameter: this.parameter,

packages/editor-ui/src/components/ExpressionEditorModal/ExpressionEditorModalOutput.vue

Lines changed: 0 additions & 101 deletions
This file was deleted.
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<script setup lang="ts">
2+
import { EditorState, type Extension } from '@codemirror/state';
3+
import { EditorView } from '@codemirror/view';
4+
5+
import { useI18n } from '@/composables/useI18n';
6+
import { highlighter } from '@/plugins/codemirror/resolvableHighlighter';
7+
import type { Plaintext, Resolved, Segment } from '@/types/expressions';
8+
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
9+
import { forceParse } from '@/utils/forceParse';
10+
11+
interface ExpressionOutputProps {
12+
segments: Segment[];
13+
extensions?: Extension[];
14+
}
15+
16+
const props = withDefaults(defineProps<ExpressionOutputProps>(), { extensions: () => [] });
17+
18+
const i18n = useI18n();
19+
20+
const editor = ref<EditorView | null>(null);
21+
const root = ref<HTMLElement | null>(null);
22+
23+
const resolvedExpression = computed(() => {
24+
if (props.segments.length === 0) {
25+
return i18n.baseText('parameterInput.emptyString');
26+
}
27+
28+
return props.segments.reduce(
29+
(acc, segment) => {
30+
// skip duplicate segments
31+
if (acc.cursor >= segment.to) return acc;
32+
33+
acc.resolved += segment.kind === 'resolvable' ? String(segment.resolved) : segment.plaintext;
34+
acc.cursor = segment.to;
35+
36+
return acc;
37+
},
38+
{ resolved: '', cursor: 0 },
39+
).resolved;
40+
});
41+
42+
const plaintextSegments = computed<Plaintext[]>(() => {
43+
return props.segments.filter((s): s is Plaintext => s.kind === 'plaintext');
44+
});
45+
46+
const resolvedSegments = computed<Resolved[]>(() => {
47+
if (props.segments.length === 0) {
48+
const emptyExpression = resolvedExpression.value;
49+
const emptySegment: Resolved = {
50+
from: 0,
51+
to: emptyExpression.length,
52+
kind: 'resolvable',
53+
error: null,
54+
resolvable: '',
55+
resolved: emptyExpression,
56+
state: 'pending',
57+
};
58+
return [emptySegment];
59+
}
60+
61+
let cursor = 0;
62+
63+
return props.segments
64+
.map((segment) => {
65+
segment.from = cursor;
66+
cursor +=
67+
segment.kind === 'plaintext'
68+
? segment.plaintext.length
69+
: segment.resolved
70+
? (segment.resolved as string | number | boolean).toString().length
71+
: 0;
72+
segment.to = cursor;
73+
return segment;
74+
})
75+
.filter((segment): segment is Resolved => segment.kind === 'resolvable');
76+
});
77+
78+
watch(
79+
() => props.segments,
80+
() => {
81+
if (!editor.value) return;
82+
83+
editor.value.dispatch({
84+
changes: { from: 0, to: editor.value.state.doc.length, insert: resolvedExpression.value },
85+
});
86+
87+
highlighter.addColor(editor.value as EditorView, resolvedSegments.value);
88+
highlighter.removeColor(editor.value as EditorView, plaintextSegments.value);
89+
},
90+
);
91+
92+
onMounted(() => {
93+
editor.value = new EditorView({
94+
parent: root.value as HTMLElement,
95+
state: EditorState.create({
96+
doc: resolvedExpression.value,
97+
extensions: [
98+
EditorState.readOnly.of(true),
99+
EditorView.lineWrapping,
100+
EditorView.editable.of(false),
101+
EditorView.domEventHandlers({ scroll: forceParse }),
102+
...props.extensions,
103+
],
104+
}),
105+
});
106+
});
107+
108+
onBeforeUnmount(() => {
109+
editor.value?.destroy();
110+
});
111+
112+
defineExpose({ getValue: () => '=' + resolvedExpression.value });
113+
</script>
114+
115+
<template>
116+
<div ref="root" data-test-id="expression-output"></div>
117+
</template>

0 commit comments

Comments
 (0)