Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catch up monaco-vscode-api and monaco #37

Merged
merged 8 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 16.x
node-version: 20.10.0
- name: Install dependencies
run: npm ci
- name: Build
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 16.x
node-version: 20.10.0
- name: Install dependencies
run: npm ci
- name: Build
Expand Down
23,811 changes: 10,747 additions & 13,064 deletions package-lock.json

Large diffs are not rendered by default.

64 changes: 43 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,43 +14,65 @@
"moduleResolution": "node",
"main": "dist/index.js",
"module": "dist/index.js",
"exports": {
".": {
"default": "./dist/index.js"
},
"./vscodeParts": {
"types": "./dist/vscodeParts.d.ts",
"default": "./dist/vscodeParts.js"
}
},
"typesVersions": {
"*": {
"vscodeParts": [
"./dist/vscodeParts.d.ts"
]
}
},
"files": [
"dist/"
],
"types": "dist/index.d.ts",
"dependencies": {
"@codingame/monaco-editor-wrapper": "^3.9.0",
"deep-equal": "^2.2.0",
"lodash.debounce": "^4.0.8"
"@codingame/monaco-editor-wrapper": "4.0.0",
"deep-equal": "^2.2.3",
"lodash.debounce": "^4.0.8",
"monaco-editor": "npm:@codingame/monaco-vscode-editor-api@~2.0.2",
"react-dom": "^18.2.0",
"uuid": "^9.0.1",
"vscode": "npm:@codingame/monaco-vscode-api@~2.0.2"
},
"devDependencies": {
"@codingame/commitlint-config-codingame": "^1.0.5",
"@codingame/eslint-config": "^1.1.6",
"@codingame/commitlint-config-codingame": "^1.0.7",
"@codingame/eslint-config": "^1.1.10",
"@codingame/eslint-config-react": "^1.0.2",
"@codingame/semantic-release-config": "^1.2.0",
"@codingame/semantic-release-config": "^1.3.5",
"@codingame/tsconfig": "^1.1.1",
"@commitlint/cli": "^17.4.4",
"@types/deep-equal": "^1.0.1",
"@types/lodash.debounce": "^4.0.7",
"@types/react": "17.0.40",
"@typescript-eslint/eslint-plugin": "5.53.0",
"@typescript-eslint/parser": "5.53.0",
"conventional-changelog-conventionalcommits": "^5.0.0",
"eslint": "8.34.0",
"eslint-config-standard": "17.0.0",
"@commitlint/cli": "^18.6.0",
"@types/deep-equal": "^1.0.4",
"@types/lodash.debounce": "^4.0.9",
"@types/react": "18.2.51",
"@types/react-dom": "^18.2.18",
"@types/vscode": "^1.85.0",
"@typescript-eslint/eslint-plugin": "6.20.0",
"@typescript-eslint/parser": "6.20.0",
"conventional-changelog-conventionalcommits": "^7.0.2",
"eslint": "8.56.0",
"eslint-config-standard": "17.1.0",
"eslint-config-standard-jsx": "11.0.0",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-react": "7.33.2",
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-unused-imports": "2.0.0",
"typescript": "4.9.5"
"eslint-plugin-unused-imports": "3.0.0",
"typescript": "5.3.3"
},
"peerDependencies": {
"react": ">=16.0.0"
},
"volta": {
"node": "16.14.0",
"npm": "8.5.0"
"node": "20.10.0",
"npm": "10.4.0"
}
}
127 changes: 72 additions & 55 deletions src/MonacoEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React, { ForwardedRef, forwardRef, ReactElement, useEffect, useMemo, useRef, useState } from 'react'
import debounce from 'lodash.debounce'
import { monaco, createEditor, getMonacoLanguage, updateEditorKeybindingsMode, registerEditorOpenHandler } from '@codingame/monaco-editor-wrapper'
import { IEditorOptions } from 'vscode/service-override/modelEditor'
import * as monaco from 'monaco-editor'
import { createEditor, getMonacoLanguage, updateEditorKeybindingsMode, registerEditorOpenHandler, createModelReference } from '@codingame/monaco-editor-wrapper'
import { IEditorOptions, IResolvedTextEditorModel } from '@codingame/monaco-vscode-editor-service-override'
import { IReference, ITextFileEditorModel } from 'vscode/monaco'
import { useDeepMemo, useLastValueRef, useLastVersion, useThemeColor } from './hooks'
import './style'

Expand Down Expand Up @@ -93,7 +95,7 @@ export interface MonacoEditorProps {
*
* Default is opening a new editor in a popup
*/
onEditorOpenRequest?: (model: monaco.editor.ITextModel, options: IEditorOptions | undefined, source: monaco.editor.ICodeEditor, sideBySide?: boolean) => Promise<monaco.editor.ICodeEditor | null>
onEditorOpenRequest?: (model: IReference<IResolvedTextEditorModel>, options: IEditorOptions | undefined, source: monaco.editor.ICodeEditor, sideBySide?: boolean) => Promise<monaco.editor.ICodeEditor | null>

/**
* if true, the models created by the component will be disposed when they are no longer displayed in the editor
Expand All @@ -115,11 +117,11 @@ function MonacoEditor ({
markers,
saveViewState = defaultSaveViewState,
restoreViewState = defaultRestoreViewState,
onEditorOpenRequest,
disposeModels = true
onEditorOpenRequest
}: MonacoEditorProps, ref: ForwardedRef<monaco.editor.IStandaloneCodeEditor>): ReactElement {
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>()
const modelRef = useRef<monaco.editor.ITextModel>()
const [modelReady, setModelReady] = useState(false)
const preventTriggerChangeEventRef = useRef<boolean>(false)

const [height, setHeight] = useState<number | string>(requestedHeight !== 'auto' ? requestedHeight : 50)
Expand All @@ -134,15 +136,10 @@ function MonacoEditor ({
const memoizedOptions = useDeepMemo(() => options, [options])
const allOptions = useMemo<monaco.editor.IEditorOptions>(() => {
return removeKeyBindingsManagedOptions({
...memoizedOptions,
automaticLayout: true
...memoizedOptions
}, keyBindingsMode)
}, [memoizedOptions, keyBindingsMode])

const modelUri = useMemo(() => {
return fileUri != null ? monaco.Uri.parse(fileUri) : undefined
}, [fileUri])

const fixedCode = useMemo(() => value != null ? fixCode(value) : null, [value])

const valueRef = useLastValueRef(fixedCode)
Expand All @@ -151,48 +148,15 @@ function MonacoEditor ({

const hasValue = fixedCode != null

// Create/Update model
useEffect(() => {
if (modelUri != null || hasValue) {
const value = valueRef.current
const existingModel = modelUri != null ? monaco.editor.getModel(modelUri) : null
const model = existingModel ?? monaco.editor.createModel(value!, monacoLanguage, modelUri)
if (monacoLanguage != null && model.getLanguageId() !== monacoLanguage) {
monaco.editor.setModelLanguage(model, monacoLanguage)
}
modelRef.current = model
editorRef.current?.setModel(model)
if (editorRef.current != null) {
lastRestoreViewState(editorRef.current, model)
}
return () => {
if (!disposeModels) {
return
}
lastSaveViewState(editorRef.current!, model)
if (existingModel == null) {
// Only dispose if we are the one who created the model
modelRef.current = undefined
model.dispose()
}
}
} else {
modelRef.current = undefined
editorRef.current?.setModel(null)
}
return undefined
}, [monacoLanguage, modelUri, valueRef, lastSaveViewState, lastRestoreViewState, disposeModels, hasValue])

// Create editor
useEffect(() => {
const containerElement = containerRef.current
if (containerElement != null) {
const model = modelRef.current
const editor = createEditor(
containerElement,
{
model,
// We need to pass options here due to https://github.com/microsoft/monaco-editor/issues/2873
automaticLayout: true,
// We need to pass options here for the `scrollbar` options to be used
...allOptions,
// We need to override all IStandaloneEditorConstructionOptions fields to prevent conflicts with proper editor options (especially `language`)
value: undefined,
Expand All @@ -206,9 +170,6 @@ function MonacoEditor ({
}
)
editorRef.current = editor
if (model != null) {
lastRestoreViewState(editor, model)
}

if (ref != null) {
if (typeof ref === 'function') {
Expand All @@ -224,15 +185,68 @@ function MonacoEditor ({
}
}
return undefined
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

// Create/Update model
useEffect(() => {
if (fileUri == null && !hasValue) {
modelRef.current = undefined
editorRef.current!.setModel(null)
return
}
let cancelled = false
async function updateModel () {
modelRef.current = undefined
editorRef.current!.setModel(null)
setModelReady(false)

const value = valueRef.current
let modelIRefPromise: Promise<IReference<ITextFileEditorModel>> | undefined
let modelIRef: IReference<ITextFileEditorModel> | undefined
let model: monaco.editor.ITextModel
if (fileUri != null) {
modelIRefPromise = createModelReference(monaco.Uri.parse(fileUri), value!)
modelIRef = (await modelIRefPromise)!
if (cancelled) {
modelIRef.dispose()
return () => {}
}
model = modelIRef.object.textEditorModel!
if (monacoLanguage != null && model.getLanguageId() !== monacoLanguage) {
monaco.editor.setModelLanguage(model, monacoLanguage)
}
} else {
model = monaco.editor.createModel(value!, monacoLanguage)
}

modelRef.current = model
setModelReady(true)
editorRef.current!.setModel(model)
if (editorRef.current != null) {
lastRestoreViewState(editorRef.current, model)
}
return () => {
if (editorRef.current != null) {
lastSaveViewState(editorRef.current, model)
}
modelIRefPromise?.then(modelIRef => modelIRef.dispose(), console.error)
modelRef.current = undefined
}
}
const disposePromise = updateModel()
return () => {
cancelled = true
disposePromise.then(dispose => dispose(), console.error)
}
}, [monacoLanguage, fileUri, valueRef, lastSaveViewState, lastRestoreViewState, hasValue])

// Update value
useEffect(() => {
if (fixedCode != null) {
const model = modelRef.current!
if (modelReady && fixedCode != null) {
const model = modelRef.current
const editor = editorRef.current!
if (fixedCode !== model.getValue()) {
if (model != null && fixedCode !== model.getValue()) {
preventTriggerChangeEventRef.current = true
console.debug('Replacing whole editor content')
editor.pushUndoStop()
Expand All @@ -241,7 +255,7 @@ function MonacoEditor ({
preventTriggerChangeEventRef.current = false
}
}
}, [fixedCode])
}, [fixedCode, modelReady])

// Update options from props
useEffect(() => {
Expand All @@ -259,6 +273,9 @@ function MonacoEditor ({

// Update markers
useEffect(() => {
if (!modelReady) {
return
}
const model = modelRef.current
if (markers != null && model != null) {
monaco.editor.setModelMarkers(model, 'customMarkers', markers)
Expand All @@ -267,7 +284,7 @@ function MonacoEditor ({
}
}
return undefined
}, [markers])
}, [markers, modelReady])

// Call onChange callback
useEffect(() => {
Expand Down
7 changes: 6 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { loadLanguage, monaco, updateKeybindings, updateUserConfiguration } from '@codingame/monaco-editor-wrapper'
import { IEditorOptions, IResolvedTextEditorModel } from '@codingame/monaco-vscode-editor-service-override'
import { IReference } from 'vscode/monaco'
import { useThemeColor, useUserConfiguration } from './hooks'
import MonacoEditor, { MonacoEditorProps } from './MonacoEditor'

Expand All @@ -12,5 +14,8 @@ export {
loadLanguage
}
export type {
MonacoEditorProps
MonacoEditorProps,
IReference,
IEditorOptions,
IResolvedTextEditorModel
}
Loading
Loading