X Tutup
Skip to content

Commit 598a43e

Browse files
committed
feat: support singleton mode / upgrade linkage
1 parent 073c1ff commit 598a43e

File tree

6 files changed

+187
-126
lines changed

6 files changed

+187
-126
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* @leoyuan @alvarto
66

77
/packages/plugin-manual/ @alvarto
8-
/packages/base-monaco-editor/ @alvarto
8+
/packages/base-monaco-editor/ @alvarto @wangshihao111
99
/packages/plugin-code-editor/ @alvarto
1010
/packages/plugin-schema/ @alvarto
1111
/packages/plugin-components-pane/ @mark-ck

packages/base-monaco-editor/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 1.1.0-beta.0
2+
3+
- 重构 type / path / value 联动,彻底修复 monaco 在多文件模式下覆盖多个 path 值的 bug
4+
- 修复 ts 类型问题 @wangshihao111
5+
- 添加 configure 方法,支持配置是否开启 monaco 单例模式 @wangshihao111
6+
- 新增插件参数:`enhancers`,用于强化编辑器功能 @wangshihao111
7+
18
## 1.0.0
29

310
- Publish following changes

packages/base-monaco-editor/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@alilc/lowcode-plugin-base-monaco-editor",
3-
"version": "1.0.0",
3+
"version": "1.1.0-beta.0",
44
"description": "代码编辑组件,monaco-editor 的低代码适配封装",
55
"publishConfig": {
66
"access": "public"
@@ -31,7 +31,7 @@
3131
"component"
3232
],
3333
"dependencies": {
34-
"@monaco-editor/loader": "^1.2.0",
34+
"@monaco-editor/loader": "1.3.0",
3535
"classnames": "^2.3.1"
3636
},
3737
"devDependencies": {

packages/base-monaco-editor/src/helper.ts

Lines changed: 96 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,25 @@
11
/* eslint-disable no-empty */
2-
import { useEffect, useState, useRef, CSSProperties } from 'react';
3-
import loader from '@monaco-editor/loader';
4-
5-
loader.config({
6-
paths: {
7-
vs: 'https://g.alicdn.com/code/lib/monaco-editor/0.31.1/min/vs',
8-
},
9-
});
10-
11-
type IAmbigousFn = (...args: any[]) => any;
2+
import React, { useEffect, useState, useRef, CSSProperties } from 'react';
3+
import { Monaco } from '@monaco-editor/loader';
4+
import type { editor as oEditor } from 'monaco-editor';
5+
import { getMonaco } from './monaco';
126

137
// @todo fill type def for monaco editor without refering monaco editor
148
/**
159
* @see https://microsoft.github.io/monaco-editor/api/index.html
1610
*/
17-
export interface IEditorInstance {
18-
getModel: IAmbigousFn;
19-
dispose: IAmbigousFn;
20-
getValue: () => string;
21-
onDidChangeModelContent: (input: any) => void;
22-
setTheme: (input: string) => void;
23-
setModelLanguage: (model: any, language: string) => void;
24-
layout: () => void;
25-
setValue: (value: string) => void;
26-
executeEdits: IAmbigousFn;
27-
pushUndoStop: IAmbigousFn;
28-
EditorOption?: Record<string, any>;
29-
getOption?: (input: string) => any;
30-
onDidFocusEditorText: (...args: any[]) => void;
31-
onDidBlurEditorText: (...args: any[]) => void;
32-
getModifiedEditor?: () => IEditorInstance;
33-
setModel: IAmbigousFn;
34-
revealLineInCenter: IAmbigousFn;
35-
focus: IAmbigousFn;
36-
Range: new(...args: any[]) => any;
37-
getPosition: IAmbigousFn;
38-
setPosition: IAmbigousFn;
39-
deltaDecorations: IAmbigousFn;
40-
addAction: IAmbigousFn;
41-
saveViewState: () => ICodeEditorViewState;
42-
createModel: IAmbigousFn;
43-
[key: string]: any;
44-
}
45-
export interface IMonacoInstance {
46-
editor?: {
47-
create: IAmbigousFn;
48-
[key: string]: any;
49-
};
50-
KeyCode?: Record<string, any>;
51-
KeyMod?: Record<string, any>;
52-
[otherKeys: string]: any;
53-
}
11+
export type IEditorInstance = oEditor.IStandaloneCodeEditor | oEditor.IStandaloneDiffEditor;
5412

55-
type ICodeEditorViewState = {
56-
contributionsState: any;
57-
cursorState: any;
58-
viewState: any;
59-
}
13+
export type EditorEnhancer =
14+
(monaco: Monaco, editorIns: IEditorInstance) => any;
6015

6116
export interface IGeneralManacoEditorProps {
6217
/** [Monaco editor options](https://microsoft.github.io/monaco-editor/) */
6318
options?: Record<string, any>;
6419
/** callback after monaco's loaded and after editor's loaded */
65-
editorDidMount?: (monaco: IMonacoInstance, editor: IEditorInstance) => void;
20+
editorDidMount?: (monaco: Monaco, editor: IEditorInstance) => void;
6621
/** callback after monaco's loaded and before editor's loaded */
67-
editorWillMount?: (monaco: IMonacoInstance) => void;
22+
editorWillMount?: (monaco: Monaco) => void;
6823
/** path of the current model, useful when creating a multi-model editor */
6924
path?: string;
7025
/** whether to save the models' view states between model changes or not */
@@ -89,6 +44,7 @@ export interface IGeneralManacoEditorProps {
8944
enableOutline?: boolean;
9045
/** style of wrapper */
9146
style?: CSSProperties;
47+
enhancers?: EditorEnhancer[];
9248
}
9349

9450
export interface ISingleMonacoEditorProps extends IGeneralManacoEditorProps {
@@ -103,16 +59,15 @@ export interface IDiffMonacoEditorProps extends IGeneralManacoEditorProps {
10359
const CURRENT_LANGUAGE = ((window as any).locale || window.localStorage.getItem('vdev-locale') || '').replace(/_/, '-') || 'zh-CN';
10460
export const WORD_EDITOR_INITIALIZING = CURRENT_LANGUAGE === 'en-US' ? 'Initializing Editor' : '编辑器初始化中';
10561

106-
export const INITIAL_OPTIONS = {
62+
export const INITIAL_OPTIONS: oEditor.IStandaloneEditorConstructionOptions = {
10763
fontSize: 12,
10864
tabSize: 2,
10965
fontFamily: 'Menlo, Monaco, Courier New, monospace',
110-
renderIndentGuides: true,
11166
folding: true,
11267
minimap: {
11368
enabled: false,
11469
},
115-
autoIndent: true,
70+
autoIndent: 'advanced',
11671
contextmenu: true,
11772
useTabStops: true,
11873
wordBasedSuggestions: true,
@@ -131,9 +86,34 @@ export const INITIAL_OPTIONS = {
13186
},
13287
};
13388

134-
export const useEditor = (type: 'single' | 'diff', props: IGeneralManacoEditorProps) => {
89+
const DIFF_EDITOR_INITIAL_OPTIONS: oEditor.IStandaloneDiffEditorConstructionOptions = {
90+
fontSize: 12,
91+
fontFamily: 'Menlo, Monaco, Courier New, monospace',
92+
folding: true,
93+
minimap: {
94+
enabled: false,
95+
},
96+
autoIndent: 'advanced',
97+
contextmenu: true,
98+
useTabStops: true,
99+
formatOnPaste: true,
100+
automaticLayout: true,
101+
lineNumbers: 'on',
102+
wordWrap: 'off',
103+
scrollBeyondLastLine: false,
104+
fixedOverflowWidgets: false,
105+
snippetSuggestions: 'top',
106+
scrollbar: {
107+
vertical: 'auto',
108+
horizontal: 'auto',
109+
verticalScrollbarSize: 10,
110+
horizontalScrollbarSize: 10,
111+
},
112+
};
113+
114+
export const useEditor = <T = IEditorInstance>(type: 'single' | 'diff', props: IGeneralManacoEditorProps) => {
135115
const {
136-
editorDidMount, editorWillMount, theme, value, path, language, saveViewState, defaultValue,
116+
editorDidMount, editorWillMount, theme, value, path, language, saveViewState, defaultValue, enhancers,
137117
} = props;
138118

139119
const [isEditorReady, setIsEditorReady] = useState(false);
@@ -146,15 +126,21 @@ export const useEditor = (type: 'single' | 'diff', props: IGeneralManacoEditorPr
146126
const previousPath = usePrevious(path);
147127
const requireConfigRef = useRef(props.requireConfig);
148128
const optionRef = useRef(props.options);
149-
const monacoRef = useRef<IMonacoInstance>();
129+
const monacoRef = useRef<Monaco>();
150130
const editorRef = useRef<IEditorInstance>();
151131
const containerRef = useRef<HTMLDivElement>();
152132
const typeRef = useRef(type);
153133
const editorDidMountRef = useRef<ISingleMonacoEditorProps['editorDidMount']>();
154134
const editorWillMountRef = useRef<ISingleMonacoEditorProps['editorWillMount']>();
155135

156136
const decomposeRef = useRef(false);
157-
const viewStatusRef = useRef<Map<any, ICodeEditorViewState>>(new Map())
137+
const viewStatusRef = useRef<Map<any, oEditor.ICodeEditorViewState>>(new Map());
138+
139+
const enhancersRef = useRef<any>({});
140+
141+
useEffect(() => {
142+
enhancersRef.current.enhancers = enhancers;
143+
}, [enhancers]);
158144

159145
useEffect(() => {
160146
editorDidMountRef.current = editorDidMount;
@@ -180,13 +166,8 @@ export const useEditor = (type: 'single' | 'diff', props: IGeneralManacoEditorPr
180166
// make sure loader / editor only init once
181167
useEffect(() => {
182168
setLoading(true);
183-
184-
if (requireConfigRef.current) {
185-
loader.config(requireConfigRef.current);
186-
}
187-
188-
loader.init()
189-
.then((monaco: any) => {
169+
getMonaco(requireConfigRef.current)
170+
.then((monaco: Monaco) => {
190171
// 兼容旧版本 monaco-editor 写死 MonacoEnvironment 的问题
191172
(window as any).MonacoEnvironment = undefined;
192173
if (typeof (window as any).define === 'function' && (window as any).define.amd) {
@@ -203,13 +184,13 @@ export const useEditor = (type: 'single' | 'diff', props: IGeneralManacoEditorPr
203184
if (!containerRef.current) {
204185
return;
205186
}
206-
let editor: IEditorInstance;
187+
let editor: oEditor.IStandaloneCodeEditor | oEditor.IStandaloneDiffEditor;
207188
if (typeRef.current === 'single') {
208189
const model = getOrCreateModel(
209190
monaco,
210191
valueRef.current ?? defaultValueRef.current ?? '',
211192
languageRef.current,
212-
pathRef.current
193+
pathRef.current,
213194
);
214195
editor = monaco.editor.create(containerRef.current, {
215196
automaticLayout: true,
@@ -228,14 +209,14 @@ export const useEditor = (type: 'single' | 'diff', props: IGeneralManacoEditorPr
228209

229210
editor = monaco.editor.createDiffEditor(containerRef.current, {
230211
automaticLayout: true,
231-
...INITIAL_OPTIONS,
212+
...DIFF_EDITOR_INITIAL_OPTIONS,
232213
...optionRef.current,
233214
});
234215

235216
editor.setModel({ original: originalModel, modified: modifiedModel });
236217
}
237-
238218
editorRef.current = editor;
219+
enhancersRef.current.enhancers?.forEach((en: any) => en(monaco, editor as any));
239220
try {
240221
editorDidMountRef.current?.(monaco, editor);
241222
} catch (err) { }
@@ -258,39 +239,15 @@ export const useEditor = (type: 'single' | 'diff', props: IGeneralManacoEditorPr
258239
monacoRef.current.editor.setTheme(theme);
259240
}, [isEditorReady, theme]);
260241

261-
// controlled value
262-
useEffect(() => {
263-
if (!isEditorReady) {
264-
return;
265-
}
266-
267-
const editor = type === 'diff'
268-
? editorRef.current.getModifiedEditor()
269-
: editorRef.current;
270-
271-
const nextValue = value ?? defaultValueRef.current ?? ''
272-
if (editor?.getOption?.(monacoRef.current?.editor.EditorOption.readOnly)) {
273-
editor?.setValue(nextValue);
274-
} else if (value !== editor?.getValue()) {
275-
editor?.executeEdits('', [{
276-
range: editor?.getModel().getFullModelRange(),
277-
text: nextValue,
278-
forceMoveMarkers: true,
279-
}]);
280-
281-
editor?.pushUndoStop();
282-
}
283-
}, [isEditorReady, type, value]);
284-
285242
// focus status
286243
useEffect(() => {
287244
if (!isEditorReady) {
288245
return;
289246
}
290247

291248
const editor = type === 'diff'
292-
? editorRef.current.getModifiedEditor()
293-
: editorRef.current;
249+
? (editorRef.current as oEditor.IStandaloneDiffEditor).getModifiedEditor()
250+
: editorRef.current as oEditor.IStandaloneCodeEditor;
294251
editor?.onDidFocusEditorText(() => {
295252
!decomposeRef.current && setFocused(true);
296253
});
@@ -306,7 +263,35 @@ export const useEditor = (type: 'single' | 'diff', props: IGeneralManacoEditorPr
306263
};
307264
}, []);
308265

309-
// multi-model implementation
266+
// controlled value -- diff mode / without path only
267+
useEffect(() => {
268+
if (!isEditorReady) {
269+
return;
270+
}
271+
272+
if (type !== 'diff' && !!path) {
273+
return;
274+
}
275+
276+
const editor = type === 'diff'
277+
? (editorRef.current as oEditor.IStandaloneDiffEditor).getModifiedEditor()
278+
: editorRef.current as oEditor.IStandaloneCodeEditor;
279+
280+
const nextValue = value ?? defaultValueRef.current ?? '';
281+
if (editor?.getOption?.(monacoRef.current?.editor.EditorOption.readOnly)) {
282+
editor?.setValue(nextValue);
283+
} else if (value !== editor?.getValue()) {
284+
editor?.executeEdits('', [{
285+
range: editor?.getModel().getFullModelRange(),
286+
text: nextValue,
287+
forceMoveMarkers: true,
288+
}]);
289+
290+
editor?.pushUndoStop();
291+
}
292+
}, [isEditorReady, path, type, value]);
293+
294+
// multi-model && controlled value (shouldn't be diff mode)
310295
useEffect(() => {
311296
if (!isEditorReady) {
312297
return;
@@ -327,29 +312,30 @@ export const useEditor = (type: 'single' | 'diff', props: IGeneralManacoEditorPr
327312
path,
328313
);
329314

315+
const editor = editorRef.current as oEditor.IStandaloneCodeEditor;
330316
if (valueRef.current !== null && valueRef.current !== undefined && model.getValue() !== valueRef.current) {
331317
model.setValue(valueRef.current);
332318
}
333-
334319
if (model !== editorRef.current.getModel()) {
335-
saveViewState && viewStatusRef.current.set(previousPath, editorRef.current.saveViewState());
336-
editorRef.current.setModel(model);
337-
saveViewState && editorRef.current.restoreViewState(viewStatusRef.current.get(path));
320+
saveViewState && viewStatusRef.current.set(previousPath, editor.saveViewState());
321+
editor.setModel(model);
322+
saveViewState && editor.restoreViewState(viewStatusRef.current.get(path));
338323
}
339-
}, [isEditorReady, path, previousPath, type]);
324+
}, [isEditorReady, value, path, previousPath, type]);
340325

326+
let retEditorRef: React.MutableRefObject<T> = editorRef as any;
341327
return {
342328
isEditorReady,
343329
focused,
344330
loading,
345331
containerRef,
346332
monacoRef,
347-
editorRef,
333+
editorRef: retEditorRef,
348334
valueRef,
349335
} as const;
350336
};
351337

352-
function getOrCreateModel(monaco: IMonacoInstance, value?: string, language?: string, path?: string) {
338+
function getOrCreateModel(monaco: Monaco, value?: string, language?: string, path?: string) {
353339
if (path) {
354340
const prevModel = monaco
355341
.editor
@@ -365,9 +351,9 @@ function getOrCreateModel(monaco: IMonacoInstance, value?: string, language?: st
365351
}
366352

367353
function usePrevious<T>(value: T) {
368-
const ref = useRef<T>()
354+
const ref = useRef<T>();
369355
useEffect(() => {
370-
ref.current = value
371-
}, [value])
372-
return ref.current
356+
ref.current = value;
357+
}, [value]);
358+
return ref.current;
373359
}

0 commit comments

Comments
 (0)
X Tutup