Skip to content

Commit 5a0dfdc

Browse files
committed
add GroovyCodeEditor
1 parent 79bd9e5 commit 5a0dfdc

5 files changed

Lines changed: 2759 additions & 7 deletions

File tree

apps/app-pc/package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@
1212
"@coding-script/script-engine": "workspace:*",
1313
"antd": "^6.2.1",
1414
"react": "^18.3.1",
15-
"react-dom": "^18.3.1"
15+
"react-dom": "^18.3.1",
16+
"@codemirror/commands": "^6.10.2",
17+
"@codemirror/lang-java": "^6.0.2",
18+
"@codemirror/language": "^6.12.2",
19+
"@codemirror/state": "^6.5.4",
20+
"@codemirror/theme-one-dark": "^6.1.3",
21+
"@codemirror/view": "^6.39.16"
1622
},
1723
"devDependencies": {
1824
"@rsbuild/core": "^1.7.1",
@@ -21,4 +27,4 @@
2127
"@types/react-dom": "^18.3.5",
2228
"typescript": "^5.9.3"
2329
}
24-
}
30+
}

apps/app-pc/src/pages/home.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import type {FormMeta} from "@coding-script/script-engine";
2-
import {createFormInstance, FormInstance, FormView} from "@coding-script/script-engine";
3-
import {Button, Space} from "antd";
4-
import type {CardLayout} from "@/layout/card-form-layout.tsx";
1+
import { GroovyCodeEditor } from "@coding-script/script-engine";
52

63
const HomePage = () => {
74

85
return (
96
<div>
107
<h1>Home Page</h1>
8+
<GroovyCodeEditor
9+
value={`println("Hello, World!")`}
10+
onChange={(value) => {
11+
console.log("Code changed:", value);
12+
}}
13+
/>
1114
</div>
1215
);
1316
};
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export {}
1+
export * from "./script-code";
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import React, {useEffect, useRef} from 'react';
2+
import {EditorState} from '@codemirror/state';
3+
import {
4+
crosshairCursor,
5+
drawSelection,
6+
dropCursor,
7+
EditorView,
8+
highlightActiveLine,
9+
highlightActiveLineGutter,
10+
highlightSpecialChars,
11+
keymap,
12+
lineNumbers,
13+
placeholder as cmPlaceholder,
14+
rectangularSelection
15+
} from '@codemirror/view';
16+
import {defaultKeymap, history, historyKeymap, indentWithTab} from '@codemirror/commands';
17+
import {
18+
bracketMatching,
19+
defaultHighlightStyle,
20+
foldGutter,
21+
foldKeymap,
22+
HighlightStyle,
23+
indentOnInput,
24+
syntaxHighlighting
25+
} from '@codemirror/language';
26+
import {java} from '@codemirror/lang-java';
27+
import {oneDark} from '@codemirror/theme-one-dark';
28+
import {tags} from '@lezer/highlight';
29+
30+
interface GroovyCodeEditorProps {
31+
value?: string;
32+
readonly?: boolean;
33+
onChange?: (value: string) => void;
34+
placeholder?: string;
35+
theme?: 'dark' | 'light';
36+
options?: {
37+
fontSize?: number;
38+
minHeight?: number;
39+
maxHeight?: number;
40+
};
41+
}
42+
43+
const darkHighlightStyle = HighlightStyle.define([
44+
{tag: tags.keyword, color: '#c678dd'},
45+
{tag: tags.operator, color: '#56b6c2'},
46+
{tag: tags.variableName, color: '#e5c07b'},
47+
{tag: tags.string, color: '#98c379'},
48+
{tag: tags.comment, color: '#5c6370', fontStyle: 'italic'},
49+
{tag: tags.number, color: '#d19a66'},
50+
{tag: tags.bool, color: '#d19a66'},
51+
{tag: tags.null, color: '#d19a66'},
52+
{tag: tags.propertyName, color: '#e06c75'},
53+
{tag: tags.function(tags.variableName), color: '#61afef'},
54+
{tag: tags.definition(tags.variableName), color: '#e5c07b'},
55+
{tag: tags.typeName, color: '#e5c07b'},
56+
{tag: tags.className, color: '#e5c07b'},
57+
{tag: tags.annotation, color: '#d19a66'},
58+
]);
59+
60+
export const GroovyCodeEditor: React.FC<GroovyCodeEditorProps> = (props) => {
61+
const {
62+
value,
63+
readonly = false,
64+
onChange,
65+
placeholder = '请输入 Groovy 脚本...',
66+
theme = 'dark',
67+
options = {}
68+
} = props;
69+
70+
const editorRef = useRef<HTMLDivElement>(null);
71+
const viewRef = useRef<EditorView | null>(null);
72+
73+
const {fontSize = 14, minHeight = 300, maxHeight = 300} = options;
74+
75+
useEffect(() => {
76+
if (!editorRef.current) return;
77+
78+
const extensions = [
79+
lineNumbers(),
80+
highlightActiveLineGutter(),
81+
highlightSpecialChars(),
82+
history(),
83+
foldGutter(),
84+
drawSelection(),
85+
dropCursor(),
86+
EditorState.allowMultipleSelections.of(true),
87+
indentOnInput(),
88+
bracketMatching(),
89+
rectangularSelection(),
90+
crosshairCursor(),
91+
highlightActiveLine(),
92+
keymap.of([
93+
...defaultKeymap,
94+
...historyKeymap,
95+
...foldKeymap,
96+
indentWithTab,
97+
]),
98+
java(),
99+
cmPlaceholder(placeholder),
100+
EditorView.updateListener.of((update) => {
101+
if (update.docChanged && onChange) {
102+
onChange(update.state.doc.toString());
103+
}
104+
}),
105+
EditorView.theme({
106+
'&': {
107+
fontSize: `${fontSize}px`,
108+
},
109+
'.cm-scroller': {
110+
overflow: 'auto',
111+
minHeight: `${minHeight}px`,
112+
maxHeight: `${maxHeight}px`,
113+
},
114+
'.cm-content': {
115+
fontFamily: 'monospace',
116+
minHeight: `${minHeight}px`,
117+
},
118+
'.cm-gutters': {
119+
minHeight: `${minHeight}px`,
120+
},
121+
}),
122+
];
123+
124+
if (theme === 'dark') {
125+
extensions.push(oneDark);
126+
extensions.push(syntaxHighlighting(darkHighlightStyle));
127+
} else {
128+
extensions.push(syntaxHighlighting(defaultHighlightStyle));
129+
}
130+
131+
if (readonly) {
132+
extensions.push(EditorState.readOnly.of(true));
133+
}
134+
135+
const state = EditorState.create({
136+
doc: value,
137+
extensions,
138+
});
139+
140+
const view = new EditorView({
141+
state,
142+
parent: editorRef.current,
143+
});
144+
145+
viewRef.current = view;
146+
147+
return () => {
148+
view.destroy();
149+
};
150+
}, [theme, fontSize, minHeight, maxHeight, placeholder, readonly]);
151+
152+
useEffect(() => {
153+
if (viewRef.current && value !== viewRef.current.state.doc.toString()) {
154+
viewRef.current.dispatch({
155+
changes: {
156+
from: 0,
157+
to: viewRef.current.state.doc.length,
158+
insert: value,
159+
},
160+
});
161+
}
162+
}, [value]);
163+
164+
return <div ref={editorRef} style={{border: '1px solid #434343', borderRadius: 6, overflow: 'hidden'}}/>;
165+
};

0 commit comments

Comments
 (0)