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
6116export 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
9450export interface ISingleMonacoEditorProps extends IGeneralManacoEditorProps {
@@ -103,16 +59,15 @@ export interface IDiffMonacoEditorProps extends IGeneralManacoEditorProps {
10359const CURRENT_LANGUAGE = ( ( window as any ) . locale || window . localStorage . getItem ( 'vdev-locale' ) || '' ) . replace ( / _ / , '-' ) || 'zh-CN' ;
10460export 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
367353function 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