reducer refactor

pull/1701/head^2
yann300 3 years ago
parent bc4cee9952
commit 77810a8684
  1. 53
      apps/remix-ide/src/app/editor/editor.js
  2. 125
      libs/remix-ui/editor/src/lib/actions/editor.ts
  3. 99
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx

@ -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(
<EditorUI editorAPI={this.api} theme={this.currentTheme} currentFile={this.currentFile} sourceAnnotationsPerFile={this.sourceAnnotationsPerFile} markerPerFile={this.markerPerFile} onBreakPointAdded={this.onBreakPointAdded} onBreakPointCleared={this.onBreakPointCleared} onDidChangeContent={this.onDidChangeContent} onEditorMounted={this.onEditorMounted} />
<EditorUI
editorAPI={this.api}
theme={this.currentTheme}
currentFile={this.currentFile}
sourceAnnotationsPerFile={this.sourceAnnotationsPerFile}
markerPerFile={this.markerPerFile}
events={this.events}
plugin={this}
/>
, 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)
}
/**

@ -0,0 +1,125 @@
export interface Action {
type: string;
payload: Record<string, any>
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
})
})
}

@ -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 } }

Loading…
Cancel
Save