* Creates a Proxy around an EditorState that provides Ace-compatible methods. * @param {EditorState} state - The raw CodeMirror EditorState * @param {EditorFile} file - The parent EditorFile instance * @returns {Proxy} Proxied state with Ace-compatible methods
(state, file)
| 35 | * @returns {Proxy} Proxied state with Ace-compatible methods |
| 36 | */ |
| 37 | function createSessionProxy(state, file) { |
| 38 | if (!state) return null; |
| 39 | |
| 40 | /** |
| 41 | * Convert Ace position {row, column} to CodeMirror offset |
| 42 | */ |
| 43 | function positionToOffset(pos, doc) { |
| 44 | if (!pos || !doc) return 0; |
| 45 | try { |
| 46 | const lineNum = Math.max(1, Math.min((pos.row ?? 0) + 1, doc.lines)); |
| 47 | const line = doc.line(lineNum); |
| 48 | const col = Math.max(0, Math.min(pos.column ?? 0, line.length)); |
| 49 | return line.from + col; |
| 50 | } catch (_) { |
| 51 | return 0; |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | /** |
| 56 | * Convert CodeMirror offset to Ace position {row, column} |
| 57 | */ |
| 58 | function offsetToPosition(offset, doc) { |
| 59 | if (!doc) return { row: 0, column: 0 }; |
| 60 | try { |
| 61 | const line = doc.lineAt(offset); |
| 62 | return { row: line.number - 1, column: offset - line.from }; |
| 63 | } catch (_) { |
| 64 | return { row: 0, column: 0 }; |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | function recordInactiveEdit() { |
| 69 | if (file.markChanged === false) return; |
| 70 | file.markEdited(); |
| 71 | file.scheduleCacheWrite(); |
| 72 | editorManager.emit("file-content-changed", file); |
| 73 | editorManager.onupdate("file-changed"); |
| 74 | editorManager.emit("update", "file-changed"); |
| 75 | } |
| 76 | |
| 77 | return new Proxy(state, { |
| 78 | get(target, prop) { |
| 79 | if (prop === "__rawState") { |
| 80 | return target; |
| 81 | } |
| 82 | |
| 83 | // Ace-compatible method: getValue() |
| 84 | if (prop === "getValue") { |
| 85 | return () => target.doc.toString(); |
| 86 | } |
| 87 | |
| 88 | // Ace-compatible method: setValue(text) |
| 89 | if (prop === "setValue") { |
| 90 | return (text) => { |
| 91 | const newText = String(text ?? ""); |
| 92 | const { activeFile, editor } = editorManager; |
| 93 | if (activeFile?.id === file.id && editor) { |
| 94 | // Active file: dispatch to live EditorView |