shrimp/src/editor/plugins/keymap.tsx
2025-10-22 11:23:11 -07:00

159 lines
3.3 KiB
TypeScript

import { printBytecodeOutput, printParserOutput, runCode } from '#editor/runCode'
import { EditorState } from '@codemirror/state'
import { keymap } from '@codemirror/view'
let multilineMode = false
const customKeymap = keymap.of([
{
key: 'Enter',
run: (view) => {
if (multilineMode) return false
const input = view.state.doc.toString()
history.push(input)
runCode(input)
view.dispatch({
changes: { from: 0, to: view.state.doc.length, insert: '' },
selection: { anchor: 0 },
})
return true
},
},
{
key: 'Shift-Enter',
run: (view) => {
if (multilineMode) {
const input = view.state.doc.toString()
runCode(input)
return true
}
multilineMode = true
view.dispatch({
changes: { from: view.state.doc.length, insert: '\n' },
selection: { anchor: view.state.doc.length + 1 },
})
return true
},
},
{
key: 'Tab',
preventDefault: true,
run: (view) => {
return true
},
},
{
key: 'ArrowUp',
run: (view) => {
const command = history.previous()
if (command === undefined) return false
view.dispatch({
changes: { from: 0, to: view.state.doc.length, insert: command },
selection: { anchor: command.length },
})
return true
},
},
{
key: 'ArrowDown',
run: (view) => {
const command = history.next()
if (command === undefined) return false
view.dispatch({
changes: { from: 0, to: view.state.doc.length, insert: command },
selection: { anchor: command.length },
})
return true
},
},
{
key: 'Mod-k 1',
preventDefault: true,
run: (view) => {
const input = view.state.doc.toString()
printParserOutput(input)
return true
},
},
{
key: 'Mod-k 2',
preventDefault: true,
run: (view) => {
const input = view.state.doc.toString()
printBytecodeOutput(input)
return true
},
},
])
let firstTime = true
const singleLineFilter = EditorState.transactionFilter.of((transaction) => {
if (multilineMode) return transaction // Allow everything in multiline mode
if (firstTime) {
firstTime = false
if (transaction.newDoc.toString().includes('\n')) {
multilineMode = true
return transaction
}
}
transaction.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
if (inserted.toString().includes('\n')) {
multilineMode = true
return
}
})
return transaction
})
export const shrimpKeymap = [customKeymap, singleLineFilter]
class History {
private commands: string[] = []
private index: number | undefined
push(command: string) {
this.commands.push(command)
this.index = undefined
}
previous(): string | undefined {
if (this.commands.length === 0) return
if (this.index === undefined) {
this.index = this.commands.length - 1
} else if (this.index > 0) {
this.index -= 1
}
return this.commands[this.index]
}
next(): string | undefined {
if (this.commands.length === 0 || this.index === undefined) return
if (this.index < this.commands.length - 1) {
this.index += 1
return this.commands[this.index]
} else {
this.index = undefined
return ''
}
}
}
const history = new History()