Compare commits
15 Commits
main
...
andrewvarg
Author | SHA1 | Date | |
---|---|---|---|
ba9d060338 | |||
1fb3e373f2 | |||
29d16d1df6 | |||
20d6f9e253 | |||
9b0f74e3a0 | |||
6d0d89e4a8 | |||
c9dde2d96c | |||
ad7174915b | |||
a7b634e7b7 | |||
1b65614c48 | |||
5054672190 | |||
896a4c6072 | |||
68ba613d0e | |||
82b7924362 | |||
af8d1330fd |
@ -183,14 +183,15 @@ export class EditorFixture {
|
||||
scrollToText(text: string, placeCursor?: boolean) {
|
||||
return this.page.evaluate(
|
||||
(args: { text: string; placeCursor?: boolean }) => {
|
||||
const editorView = window.editorManager.getEditorView()
|
||||
// error TS2339: Property 'docView' does not exist on type 'EditorView'.
|
||||
// Except it does so :shrug:
|
||||
// @ts-ignore
|
||||
let index = window.editorManager._editorView?.docView.view.state.doc
|
||||
const index = editorView?.docView.view.state.doc
|
||||
.toString()
|
||||
.indexOf(args.text)
|
||||
window.editorManager._editorView?.focus()
|
||||
window.editorManager._editorView?.dispatch({
|
||||
editorView?.focus()
|
||||
editorView?.dispatch({
|
||||
selection: window.EditorSelection.create([
|
||||
window.EditorSelection.cursor(index),
|
||||
]),
|
||||
|
@ -158,10 +158,10 @@ async function openKclCodePanel(page: Page) {
|
||||
await page.evaluate(() => {
|
||||
// editorManager is available on the window object.
|
||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
||||
editorManager._editorView.dispatch({
|
||||
editorManager.getEditorView().dispatch({
|
||||
selection: {
|
||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
||||
anchor: editorManager._editorView.docView.length,
|
||||
anchor: editorManager.getEditorView().docView.length,
|
||||
},
|
||||
scrollIntoView: true,
|
||||
})
|
||||
|
11
src/App.tsx
11
src/App.tsx
@ -29,6 +29,7 @@ import {
|
||||
codeManager,
|
||||
kclManager,
|
||||
settingsActor,
|
||||
editorManager,
|
||||
} from '@src/lib/singletons'
|
||||
import { maybeWriteToDisk } from '@src/lib/telemetry'
|
||||
import type { IndexLoaderData } from '@src/lib/types'
|
||||
@ -94,6 +95,16 @@ export function App() {
|
||||
useHotkeys('backspace', (e) => {
|
||||
e.preventDefault()
|
||||
})
|
||||
// Since these already exist in the editor, we don't need to define them
|
||||
// with the wrapper.
|
||||
useHotkeys('mod+z', (e) => {
|
||||
e.preventDefault()
|
||||
editorManager.undo()
|
||||
})
|
||||
useHotkeys('mod+shift+z', (e) => {
|
||||
e.preventDefault()
|
||||
editorManager.redo()
|
||||
})
|
||||
useHotkeyWrapper(
|
||||
[isDesktop() ? 'mod + ,' : 'shift + mod + ,'],
|
||||
() => navigate(filePath + PATHS.SETTINGS),
|
||||
|
@ -88,14 +88,14 @@ export function Toolbar({
|
||||
modelingState: state,
|
||||
modelingSend: send,
|
||||
sketchPathId,
|
||||
editorHasFocus: editorManager.editorView?.hasFocus,
|
||||
editorHasFocus: editorManager.getEditorView()?.hasFocus,
|
||||
}),
|
||||
[
|
||||
state,
|
||||
send,
|
||||
commandBarActor.send,
|
||||
sketchPathId,
|
||||
editorManager.editorView?.hasFocus,
|
||||
editorManager.getEditorView()?.hasFocus,
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -65,7 +65,7 @@ const CodeEditor = forwardRef<CodeEditorRef, CodeEditorProps>((props, ref) => {
|
||||
} = props
|
||||
const editor = useRef<HTMLDivElement>(null)
|
||||
|
||||
const { view, state, container } = useCodeMirror({
|
||||
const { view, container } = useCodeMirror({
|
||||
container: editor.current,
|
||||
onCreateEditor,
|
||||
extensions,
|
||||
@ -77,8 +77,8 @@ const CodeEditor = forwardRef<CodeEditorRef, CodeEditorProps>((props, ref) => {
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({ editor: editor.current, view: view, state: state }),
|
||||
[editor, container, view, state]
|
||||
() => ({ editor: editor.current, view: view, state: view?.state }),
|
||||
[editor, container, view]
|
||||
)
|
||||
|
||||
return <div ref={editor}></div>
|
||||
@ -175,7 +175,7 @@ export function useCodeMirror(props: UseCodeMirror) {
|
||||
}
|
||||
}, [targetExtensions, view, isFirstRender])
|
||||
|
||||
return { view, setView, container, setContainer, state, setState }
|
||||
return { view, container, setContainer, state }
|
||||
}
|
||||
|
||||
export default CodeEditor
|
||||
|
@ -45,7 +45,7 @@ export const FeatureTreePane = () => {
|
||||
guards: {
|
||||
codePaneIsOpen: () =>
|
||||
modelingState.context.store.openPanes.includes('code') &&
|
||||
editorManager.editorView !== null,
|
||||
editorManager.getEditorView() !== null,
|
||||
},
|
||||
actions: {
|
||||
openCodePane: () => {
|
||||
|
@ -37,13 +37,12 @@ import interact from '@replit/codemirror-interact'
|
||||
import { TEST } from '@src/env'
|
||||
import { useSelector } from '@xstate/react'
|
||||
import { useEffect, useMemo, useRef } from 'react'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
|
||||
import { useLspContext } from '@src/components/LspProvider'
|
||||
import CodeEditor from '@src/components/ModelingSidebar/ModelingPanes/CodeEditor'
|
||||
import { lineHighlightField } from '@src/editor/highlightextension'
|
||||
import { modelingMachineEvent } from '@src/editor/manager'
|
||||
import { codeManagerHistoryCompartment } from '@src/lang/codeManager'
|
||||
import { historyCompartment } from '@src/editor/compartments'
|
||||
import { codeManager, editorManager, kclManager } from '@src/lib/singletons'
|
||||
import { Themes, getSystemTheme } from '@src/lib/theme'
|
||||
import { onMouseDragMakeANewNumber, onMouseDragRegex } from '@src/lib/utils'
|
||||
@ -75,17 +74,6 @@ export const KclEditorPane = () => {
|
||||
: context.app.theme.current
|
||||
const { copilotLSP, kclLSP } = useLspContext()
|
||||
|
||||
// Since these already exist in the editor, we don't need to define them
|
||||
// with the wrapper.
|
||||
useHotkeys('mod+z', (e) => {
|
||||
e.preventDefault()
|
||||
editorManager.undo()
|
||||
})
|
||||
useHotkeys('mod+shift+z', (e) => {
|
||||
e.preventDefault()
|
||||
editorManager.redo()
|
||||
})
|
||||
|
||||
// When this component unmounts, we need to tell the machine that the editor
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
@ -96,12 +84,13 @@ export const KclEditorPane = () => {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!editorIsMounted || !lastSelectionEvent || !editorManager.editorView) {
|
||||
const editorView = editorManager.getEditorView()
|
||||
if (!editorIsMounted || !lastSelectionEvent || !editorView) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
editorManager.editorView.dispatch({
|
||||
editorView.dispatch({
|
||||
selection: lastSelectionEvent.codeMirrorSelection,
|
||||
annotations: [modelingMachineEvent, Transaction.addToHistory.of(false)],
|
||||
scrollIntoView: lastSelectionEvent.scrollIntoView,
|
||||
@ -125,7 +114,7 @@ export const KclEditorPane = () => {
|
||||
cursorBlinkRate: cursorBlinking.current ? 1200 : 0,
|
||||
}),
|
||||
lineHighlightField,
|
||||
codeManagerHistoryCompartment.of(history()),
|
||||
historyCompartment.of(history()),
|
||||
closeBrackets(),
|
||||
codeFolding(),
|
||||
keymap.of([
|
||||
|
3
src/editor/compartments.ts
Normal file
3
src/editor/compartments.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { Compartment } from '@codemirror/state'
|
||||
|
||||
export const historyCompartment = new Compartment()
|
@ -1,10 +1,22 @@
|
||||
import { redo, undo } from '@codemirror/commands'
|
||||
import {
|
||||
defaultKeymap,
|
||||
history,
|
||||
historyKeymap,
|
||||
redo,
|
||||
undo,
|
||||
} from '@codemirror/commands'
|
||||
import { syntaxTree } from '@codemirror/language'
|
||||
import type { Diagnostic } from '@codemirror/lint'
|
||||
import { forEachDiagnostic, setDiagnosticsEffect } from '@codemirror/lint'
|
||||
import { Annotation, EditorSelection, Transaction } from '@codemirror/state'
|
||||
import {
|
||||
Annotation,
|
||||
EditorSelection,
|
||||
EditorState,
|
||||
Transaction,
|
||||
type TransactionSpec,
|
||||
} from '@codemirror/state'
|
||||
import type { ViewUpdate } from '@codemirror/view'
|
||||
import { EditorView } from '@codemirror/view'
|
||||
import { EditorView, keymap } from '@codemirror/view'
|
||||
import type { StateFrom } from 'xstate'
|
||||
|
||||
import {
|
||||
@ -22,6 +34,8 @@ import type {
|
||||
ModelingMachineEvent,
|
||||
modelingMachine,
|
||||
} from '@src/machines/modelingMachine'
|
||||
import { historyCompartment } from '@src/editor/compartments'
|
||||
import type CodeManager from '@src/lang/codeManager'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@ -65,11 +79,28 @@ export default class EditorManager {
|
||||
|
||||
private _highlightRange: Array<[number, number]> = [[0, 0]]
|
||||
|
||||
public _editorView: EditorView | null = null
|
||||
private _editorState: EditorState
|
||||
private _editorView: EditorView | null = null
|
||||
public kclManager?: KclManager
|
||||
public codeManager?: CodeManager
|
||||
|
||||
constructor(engineCommandManager: EngineCommandManager) {
|
||||
this.engineCommandManager = engineCommandManager
|
||||
|
||||
this._editorState = EditorState.create({
|
||||
doc: '',
|
||||
extensions: [
|
||||
historyCompartment.of(history()),
|
||||
keymap.of([...defaultKeymap, ...historyKeymap]),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
get editorState(): EditorState {
|
||||
return this._editorView?.state || this._editorState
|
||||
}
|
||||
get state() {
|
||||
return this.editorState
|
||||
}
|
||||
|
||||
setCopilotEnabled(enabled: boolean) {
|
||||
@ -81,11 +112,20 @@ export default class EditorManager {
|
||||
}
|
||||
|
||||
setEditorView(editorView: EditorView) {
|
||||
if (this._editorView !== editorView) {
|
||||
if (this._editorView) {
|
||||
// this._editorView.destroy()
|
||||
}
|
||||
}
|
||||
this._editorView = editorView
|
||||
kclEditorActor.send({ type: 'setKclEditorMounted', data: true })
|
||||
this.overrideTreeHighlighterUpdateForPerformanceTracking()
|
||||
}
|
||||
|
||||
getEditorView(): EditorView | null {
|
||||
return this._editorView
|
||||
}
|
||||
|
||||
overrideTreeHighlighterUpdateForPerformanceTracking() {
|
||||
// @ts-ignore
|
||||
this._editorView?.plugins.forEach((e) => {
|
||||
@ -132,10 +172,6 @@ export default class EditorManager {
|
||||
return this._isAllTextSelected
|
||||
}
|
||||
|
||||
get editorView(): EditorView | null {
|
||||
return this._editorView
|
||||
}
|
||||
|
||||
get isShiftDown(): boolean {
|
||||
return this._isShiftDown
|
||||
}
|
||||
@ -285,14 +321,41 @@ export default class EditorManager {
|
||||
}
|
||||
|
||||
undo() {
|
||||
if (this._editorView) {
|
||||
undo(this._editorView)
|
||||
const state = this.editorState
|
||||
if (state) {
|
||||
console.log('before', state.doc.length, state.doc.toString())
|
||||
const undoPerformed = undo(this)
|
||||
if (undoPerformed) {
|
||||
this.codeManager!.code = state.doc.toString()
|
||||
void this.kclManager!.executeCode()
|
||||
}
|
||||
console.log(state.doc.length, state.doc.toString())
|
||||
}
|
||||
}
|
||||
|
||||
redo() {
|
||||
if (this._editorView) {
|
||||
redo(this._editorView)
|
||||
} else {
|
||||
const state = this.editorState
|
||||
if (state) {
|
||||
console.log('before', state.doc.length, state.doc.toString())
|
||||
const redoPerformed = redo(this)
|
||||
if (redoPerformed) {
|
||||
this.codeManager!.code = state.doc.toString()
|
||||
void this.kclManager!.executeCode()
|
||||
}
|
||||
console.log(state.doc.length, state.doc.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invoked by codeMirror with incorrect "this" so it needs to be an arrow function
|
||||
dispatch = (spec: TransactionSpec) => {
|
||||
if (this._editorView) {
|
||||
this._editorView.dispatch(spec)
|
||||
} else if (this._editorState) {
|
||||
this._editorState = this._editorState.update(spec).state
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,8 +50,6 @@ export function useQueryParamEffects() {
|
||||
searchParams.has(CMD_NAME_QUERY_PARAM) &&
|
||||
searchParams.has(CMD_GROUP_QUERY_PARAM)
|
||||
|
||||
console.log(window.location.href)
|
||||
|
||||
/**
|
||||
* Watches for legacy `?create-file` hook, which share links currently use.
|
||||
*/
|
||||
|
@ -2,10 +2,11 @@
|
||||
// NOT updating the code state when we don't need to.
|
||||
// This prevents re-renders of the codemirror editor, when typing.
|
||||
import { history } from '@codemirror/commands'
|
||||
import { Annotation, Compartment, Transaction } from '@codemirror/state'
|
||||
import { Annotation, Transaction } from '@codemirror/state'
|
||||
import type { EditorView, KeyBinding } from '@codemirror/view'
|
||||
import toast from 'react-hot-toast'
|
||||
|
||||
import { historyCompartment } from '@src/editor/compartments'
|
||||
import type { Program } from '@src/lang/wasm'
|
||||
import { parse, recast } from '@src/lang/wasm'
|
||||
import { bracket } from '@src/lib/exampleKcl'
|
||||
@ -17,7 +18,6 @@ const PERSIST_CODE_KEY = 'persistCode'
|
||||
|
||||
const codeManagerUpdateAnnotation = Annotation.define<boolean>()
|
||||
export const codeManagerUpdateEvent = codeManagerUpdateAnnotation.of(true)
|
||||
export const codeManagerHistoryCompartment = new Compartment()
|
||||
|
||||
export default class CodeManager {
|
||||
private _code: string = bracket
|
||||
@ -106,22 +106,22 @@ export default class CodeManager {
|
||||
*/
|
||||
updateCodeEditor(code: string, clearHistory?: boolean): void {
|
||||
this.code = code
|
||||
if (editorManager.editorView) {
|
||||
if (clearHistory) {
|
||||
clearCodeMirrorHistory(editorManager.editorView)
|
||||
}
|
||||
editorManager.editorView.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: editorManager.editorView.state.doc.length,
|
||||
insert: code,
|
||||
},
|
||||
annotations: [
|
||||
codeManagerUpdateEvent,
|
||||
Transaction.addToHistory.of(!clearHistory),
|
||||
],
|
||||
})
|
||||
if (clearHistory) {
|
||||
// TODO make this work without EditorView
|
||||
const editorView = editorManager.getEditorView()
|
||||
if (editorView) clearCodeMirrorHistory(editorView)
|
||||
}
|
||||
editorManager.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: editorManager.editorState?.doc.length || 0,
|
||||
insert: code,
|
||||
},
|
||||
annotations: [
|
||||
codeManagerUpdateEvent,
|
||||
Transaction.addToHistory.of(!clearHistory),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -216,13 +216,13 @@ function safeLSSetItem(key: string, value: string) {
|
||||
function clearCodeMirrorHistory(view: EditorView) {
|
||||
// Clear history
|
||||
view.dispatch({
|
||||
effects: [codeManagerHistoryCompartment.reconfigure([])],
|
||||
effects: [historyCompartment.reconfigure([])],
|
||||
annotations: [codeManagerUpdateEvent],
|
||||
})
|
||||
|
||||
// Add history back
|
||||
view.dispatch({
|
||||
effects: [codeManagerHistoryCompartment.reconfigure([history()])],
|
||||
effects: [historyCompartment.reconfigure([history()])],
|
||||
annotations: [codeManagerUpdateEvent],
|
||||
})
|
||||
}
|
||||
|
@ -509,7 +509,7 @@ export async function promptToEditFlow({
|
||||
const ranges: SelectionRange[] = diff.insertRanges.map((range) =>
|
||||
EditorSelection.range(range[0], range[1])
|
||||
)
|
||||
editorManager?.editorView?.dispatch({
|
||||
editorManager?.getEditorView()?.dispatch({
|
||||
selection: EditorSelection.create(
|
||||
ranges,
|
||||
selections.graphSelections.length - 1
|
||||
|
@ -69,6 +69,7 @@ export const kclManager = new KclManager(engineCommandManager, {
|
||||
// method requires it for the current ast.
|
||||
// CYCLIC REF
|
||||
editorManager.kclManager = kclManager
|
||||
editorManager.codeManager = codeManager
|
||||
|
||||
// These are all late binding because of their circular dependency.
|
||||
// TODO: proper dependency injection.
|
||||
|
Reference in New Issue
Block a user