diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js
index 0ad35409b6..b2d488a17d 100644
--- a/apps/remix-ide/src/app/editor/editor.js
+++ b/apps/remix-ide/src/app/editor/editor.js
@@ -40,7 +40,6 @@ class Editor extends Plugin {
this.renderComponent()
})
this.currentTheme = translateTheme(this._deps.themeModule.currentTheme())
- this.models = []
// Init
this.event = new EventManager()
this.sessions = {}
@@ -65,12 +64,14 @@ class Editor extends Plugin {
rs: 'rust'
}
- this.onBreakPointAdded = (file, line) => this.triggerEvent('breakpointAdded', [file, line])
- this.onBreakPointCleared = (file, line) => this.triggerEvent('breakpointCleared', [file, line])
+ this.activated = false
- this.onDidChangeContent = (file) => this._onChange(file)
-
- this.onEditorMounted = () => this.triggerEvent('editorMounted', [])
+ this.events = {
+ onBreakPointAdded: (file, line) => this.triggerEvent('breakpointAdded', [file, line]),
+ onBreakPointCleared: (file, line) => this.triggerEvent('breakpointCleared', [file, line]),
+ onDidChangeContent: (file) => this._onChange(file),
+ onEditorMounted: () => this.triggerEvent('editorMounted', [])
+ }
// to be implemented by the react component
this.api = {}
@@ -96,7 +97,15 @@ class Editor extends Plugin {
renderComponent () {
ReactDOM.render(
-
+
, this.el)
}
@@ -106,6 +115,7 @@ class Editor extends Plugin {
}
onActivation () {
+ this.activated = true
this.on('sidePanel', 'focusChanged', (name) => {
this.keepDecorationsFor(name, 'sourceAnnotationsPerFile')
this.keepDecorationsFor(name, 'markerPerFile')
@@ -120,10 +130,6 @@ class Editor extends Plugin {
this.off('sidePanel', 'pluginDisabled')
}
- setTheme (type) {
- this.api.setTheme(this._themes[type])
- }
-
_onChange (file) {
const currentFile = this._deps.config.get('currentFile')
if (!currentFile) {
@@ -180,18 +186,19 @@ class Editor extends Plugin {
* @param {string} mode Mode for this file [Default is `text`]
*/
_createSession (path, content, mode) {
- this.api.addModel(content, mode, path, false)
+ if (!this.activated) return
+ this.emit('addModel', content, mode, path, false)
return {
path,
language: mode,
setValue: (content) => {
- this.api.setValue(path, content)
+ this.emit('setValue', path, content)
},
getValue: () => {
return this.api.getValue(path, content)
},
dispose: () => {
- this.api.disposeModel(path)
+ this.emit('disposeModel', path)
}
}
}
@@ -208,9 +215,9 @@ class Editor extends Plugin {
* Display an Empty read-only session
*/
displayEmptyReadOnlySession () {
+ if (!this.activated) return
this.currentFile = null
- this.api.addModel('', 'text', '_blank', true)
- this.api.setCurrentPath('_blank')
+ this.emit('addModel', '', 'text', '_blank', true)
}
/**
@@ -324,9 +331,10 @@ class Editor extends Plugin {
* @param {number} incr The amount of pixels to add to the font.
*/
editorFontSize (incr) {
+ if (!this.activated) return
const newSize = this.api.getFontSize() + incr
if (newSize >= 6) {
- this.api.setFontSize(newSize)
+ this.emit('setFontSize', newSize)
}
}
@@ -335,7 +343,8 @@ class Editor extends Plugin {
* @param {boolean} useWrapMode Enable (or disable) wrap mode
*/
resize (useWrapMode) {
- this.api.setWordWrap(useWrapMode)
+ if (!this.activated) return
+ this.emit('setWordWrap', useWrapMode)
}
/**
@@ -344,8 +353,9 @@ class Editor extends Plugin {
* @param {number} col
*/
gotoLine (line, col) {
- this.api.focus()
- this.api.revealLine(line + 1, col)
+ if (!this.activated) return
+ this.emit('focus')
+ this.emit('revealLine', line + 1, col)
}
/**
@@ -353,7 +363,8 @@ class Editor extends Plugin {
* @param {number} line The line to scroll to
*/
scrollToLine (line) {
- this.api.revealLine(line)
+ if (!this.activated) return
+ this.emit('revealLine', line, 0)
}
/**
diff --git a/libs/remix-ui/editor/src/lib/actions/editor.ts b/libs/remix-ui/editor/src/lib/actions/editor.ts
new file mode 100644
index 0000000000..ee12a1691c
--- /dev/null
+++ b/libs/remix-ui/editor/src/lib/actions/editor.ts
@@ -0,0 +1,125 @@
+export interface Action {
+ type: string;
+ payload: Record
+ monaco: any
+}
+
+export const initialState = {}
+
+export const reducerActions = (models = initialState, action: Action) => {
+ const monaco = action.monaco
+ switch (action.type) {
+ case 'ADD_MODEL': {
+ if (!monaco) return models
+ const uri = action.payload.uri
+ const value = action.payload.value
+ const language = action.payload.language
+ const readOnly = action.payload.readOnly
+ if (models[uri]) return models // already existing
+ const model = monaco.editor.createModel(value, language, monaco.Uri.parse(uri))
+ model.onDidChangeContent(() => action.payload.onDidChangeContent(uri))
+ models[uri] = { language, uri, readOnly, model }
+ return models
+ }
+ case 'DISPOSE_MODEL': {
+ const uri = action.payload.uri
+ const model = models[uri]?.model
+ if (model) model.dispose()
+ delete models[uri]
+ return models
+ }
+ case 'SET_VALUE': {
+ if (!monaco.editor) return models
+ const uri = action.payload.uri
+ const value = action.payload.value
+ const model = models[uri]?.model
+ if (model) {
+ model.setValue(value)
+ }
+ return models
+ }
+ case 'REVEAL_LINE': {
+ if (!monaco.editor) return models
+ const line = action.payload.line
+ const column = action.payload.column
+ monaco.editor.revealLine(line)
+ monaco.editor.setPosition({ column, lineNumber: line })
+ return models
+ }
+ case 'FOCUS': {
+ if (!monaco.editor) return models
+ monaco.editor.focus()
+ return models
+ }
+ case 'SET_FONTSIZE': {
+ if (!monaco.editor) return models
+ const size = action.payload.size
+ monaco.editor.updateOptions({ fontSize: size })
+ return models
+ }
+ case 'SET_WORDWRAP': {
+ if (!monaco.editor) return models
+ const wrap = action.payload.wrap
+ monaco.editor.updateOptions({ wordWrap: wrap ? 'on' : 'off' })
+ return models
+ }
+ }
+}
+
+export const reducerListener = (plugin, dispatch, monaco) => {
+ plugin.on('editor', 'addModel', (value, language, uri, readOnly) => {
+ dispatch({
+ type: 'ADD_MODEL',
+ payload: { uri, value, language, readOnly },
+ monaco
+ })
+ })
+
+ plugin.on('editor', 'disposeModel', (uri) => {
+ dispatch({
+ type: 'DISPOSE_MODEL',
+ payload: { uri },
+ monaco
+ })
+ })
+
+ plugin.on('editor', 'setValue', (uri, value) => {
+ dispatch({
+ type: 'SET_VALUE',
+ payload: { uri, value },
+ monaco
+ })
+ })
+
+ plugin.on('editor', 'revealLine', (line, column) => {
+ dispatch({
+ type: 'REVEAL_LINE',
+ payload: { line, column },
+ monaco
+ })
+ })
+
+ plugin.on('editor', 'focus', () => {
+ dispatch({
+ type: 'FOCUS',
+ payload: {},
+ monaco
+ })
+ })
+
+ plugin.on('editor', 'setFontSize', (size) => {
+ dispatch({
+ type: 'SET_FONTSIZE',
+ payload: { size },
+ monaco
+ })
+ })
+
+ plugin.on('editor', 'setWordWrap', (wrap) => {
+ dispatch({
+ type: 'SET_WORDWRAP',
+ payload: { wrap },
+ monaco
+ })
+ })
+}
diff --git a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
index d85da7c6c9..2729970a68 100644
--- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
+++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
@@ -1,5 +1,6 @@
-import React, { useState, useRef, useEffect } from 'react'
+import React, { useState, useRef, useEffect, useReducer } from 'react'
import Editor from '@monaco-editor/react'
+import { reducerActions, reducerListener, initialState } from './actions/editor'
import './remix-ui-editor.css'
@@ -44,31 +45,29 @@ type sourceMarkerMap = {
/* eslint-disable-next-line */
export interface EditorUIProps {
+ activated: boolean
theme: string
currentFile: string
sourceAnnotationsPerFile: sourceAnnotationMap
markerPerFile: sourceMarkerMap
- onBreakPointAdded: (file: string, line: number) => void
- onBreakPointCleared: (file: string, line: number) => void
- onDidChangeContent: (file: string) => void
- onEditorMounted: () => void
+ events: {
+ onBreakPointAdded: (file: string, line: number) => void
+ onBreakPointCleared: (file: string, line: number) => void
+ onDidChangeContent: (file: string) => void
+ onEditorMounted: () => void
+ }
+ plugin: {
+ on: (plugin: string, event: string, listener: any) => void
+ }
editorAPI:{
findMatches: (uri: string, value: string) => any
- addModel: (value: string, language: string, uri: string, readOnly: boolean) => void
- disposeModel: (uri: string) => void,
- setFontSize: (fontSize: number) => void,
getFontSize: () => number,
getValue: (uri: string) => string
getCursorPosition: () => cursorPosition
- revealLine: (line: number, column: number) => void
- focus: () => void
- setWordWrap: (wrap: boolean) => void
- setValue: (uri: string, value: string) => void
}
}
export const EditorUI = (props: EditorUIProps) => {
- const [models, setModels] = useState({})
const [, setCurrentBreakpoints] = useState({})
const [currentAnnotations, setCurrentAnnotations] = useState({})
const [currentMarkers, setCurrentMarkers] = useState({})
@@ -76,6 +75,8 @@ export const EditorUI = (props: EditorUIProps) => {
const monacoRef = useRef(null)
const currentFileRef = useRef('')
+ const [editorModelsState, dispatch] = useReducer(reducerActions, initialState)
+
useEffect(() => {
if (!monacoRef.current) return
monacoRef.current.editor.setTheme(props.theme)
@@ -83,7 +84,7 @@ export const EditorUI = (props: EditorUIProps) => {
const setAnnotationsbyFile = (uri) => {
if (props.sourceAnnotationsPerFile[uri]) {
- const model = models[uri]?.model
+ const model = editorModelsState[uri]?.model
const newAnnotations = []
for (const annotation of props.sourceAnnotationsPerFile[uri]) {
if (!annotation.hide) {
@@ -106,7 +107,7 @@ export const EditorUI = (props: EditorUIProps) => {
const setMarkerbyFile = (uri) => {
if (props.markerPerFile[uri]) {
- const model = models[uri]?.model
+ const model = editorModelsState[uri]?.model
const newMarkers = []
for (const marker of props.markerPerFile[uri]) {
if (!marker.hide) {
@@ -134,8 +135,8 @@ export const EditorUI = (props: EditorUIProps) => {
useEffect(() => {
if (!editorRef.current) return
currentFileRef.current = props.currentFile
- editorRef.current.setModel(models[props.currentFile].model)
- editorRef.current.updateOptions({ readOnly: models[props.currentFile].readOnly })
+ editorRef.current.setModel(editorModelsState[props.currentFile].model)
+ editorRef.current.updateOptions({ readOnly: editorModelsState[props.currentFile].readOnly })
setAnnotationsbyFile(props.currentFile)
setMarkerbyFile(props.currentFile)
}, [props.currentFile])
@@ -150,80 +151,31 @@ export const EditorUI = (props: EditorUIProps) => {
props.editorAPI.findMatches = (uri: string, value: string) => {
if (!editorRef.current) return
- const model = models[uri]?.model
+ const model = editorModelsState[uri]?.model
if (model) return model.findMatches(value)
}
- props.editorAPI.addModel = (value: string, language: string, uri: string, readOnly: boolean) => {
- if (!monacoRef.current) return
- if (models[uri]) return // already existing
- const model = monacoRef.current.editor.createModel(value, language, monacoRef.current.Uri.parse(uri))
- model.onDidChangeContent(() => props.onDidChangeContent(uri))
- setModels(prevState => {
- prevState[uri] = { language, uri, readOnly, model }
- return prevState
- })
- }
-
- props.editorAPI.disposeModel = (uri: string) => {
- const model = models[uri]?.model
- if (model) model.dispose()
- setModels(prevState => {
- delete prevState[uri]
- return prevState
- })
- }
-
props.editorAPI.getValue = (uri: string) => {
if (!editorRef.current) return
- const model = models[uri]?.model
+ const model = editorModelsState[uri]?.model
if (model) {
return model.getValue()
}
}
- props.editorAPI.setValue = (uri: string, value: string) => {
- if (!editorRef.current) return
- const model = models[uri]?.model
- if (model) {
- model.setValue(value)
- }
- }
-
props.editorAPI.getCursorPosition = () => {
if (!monacoRef.current) return
- const model = models[currentFileRef.current]?.model
+ const model = editorModelsState[currentFileRef.current]?.model
if (model) {
return model.getOffsetAt(editorRef.current.getPosition())
}
}
- props.editorAPI.revealLine = (line: number, column: number) => {
- if (!editorRef.current) return
- editorRef.current.revealLine(line)
- editorRef.current.setPosition({ column, lineNumber: line })
- }
-
- props.editorAPI.focus = () => {
- if (!editorRef.current) return
- editorRef.current.focus()
- }
-
- props.editorAPI.setFontSize = (size: number) => {
- if (!editorRef.current) return
- editorRef.current.updateOptions({ fontSize: size })
- }
-
props.editorAPI.getFontSize = () => {
if (!editorRef.current) return
return editorRef.current.getOption(42).fontSize
}
- props.editorAPI.setWordWrap = (wrap: boolean) => {
- if (!editorRef.current) return
- editorRef.current.updateOptions({ wordWrap: wrap ? 'on' : 'off' })
- }
-
(window as any).addRemixBreakpoint = (position) => { // make it available from e2e testing...
const model = editorRef.current.getModel()
if (model) {
@@ -232,11 +184,11 @@ export const EditorUI = (props: EditorUIProps) => {
if (!prevState[currentFile]) prevState[currentFile] = {}
const decoration = Object.keys(prevState[currentFile]).filter((line) => parseInt(line) === position.lineNumber)
if (decoration.length) {
- props.onBreakPointCleared(currentFile, position.lineNumber)
+ props.events.onBreakPointCleared(currentFile, position.lineNumber)
model.deltaDecorations([prevState[currentFile][position.lineNumber]], [])
delete prevState[currentFile][position.lineNumber]
} else {
- props.onBreakPointAdded(currentFile, position.lineNumber)
+ props.events.onBreakPointAdded(currentFile, position.lineNumber)
const decorationIds = model.deltaDecorations([], [{
range: new monacoRef.current.Range(position.lineNumber, 1, position.lineNumber, 1),
options: {
@@ -254,7 +206,7 @@ export const EditorUI = (props: EditorUIProps) => {
function handleEditorDidMount (editor) {
editorRef.current = editor
monacoRef.current.editor.setTheme(props.theme)
- props.onEditorMounted()
+ props.events.onEditorMounted()
editor.onMouseUp((e) => {
if (e && e.target && e.target.toString().startsWith('GUTTER')) {
(window as any).addRemixBreakpoint(e.target.position)
@@ -264,6 +216,7 @@ export const EditorUI = (props: EditorUIProps) => {
function handleEditorWillMount (monaco) {
monacoRef.current = monaco
+ reducerListener(props.plugin, dispatch, monacoRef.current)
// see https://microsoft.github.io/monaco-editor/playground.html#customizing-the-appearence-exposed-colors
const backgroundColor = window.getComputedStyle(document.documentElement).getPropertyValue('--light').trim()
const infoColor = window.getComputedStyle(document.documentElement).getPropertyValue('--info').trim()
@@ -285,7 +238,7 @@ export const EditorUI = (props: EditorUIProps) => {
width="100%"
height="100%"
path={props.currentFile}
- language={models[props.currentFile] ? models[props.currentFile].language : 'text'}
+ language={editorModelsState[props.currentFile] ? editorModelsState[props.currentFile].language : 'text'}
onMount={handleEditorDidMount}
beforeMount={handleEditorWillMount}
options= { { glyphMargin: true } }