Skip to content

Instantly share code, notes, and snippets.

@andy0130tw
Last active October 8, 2024 07:02
Show Gist options
  • Save andy0130tw/96172fb589709ce34f80c9618631441e to your computer and use it in GitHub Desktop.
Save andy0130tw/96172fb589709ce34f80c9618631441e to your computer and use it in GitHub Desktop.
CodeMirror with a search field that is another CodeMirror instance!
import { basicSetup, minimalSetup } from 'codemirror'
import { Prec } from '@codemirror/state'
import { EditorView, keymap, placeholder } from '@codemirror/view'
import { search, findNext, findPrevious } from '@codemirror/search'
const SearchPanel = (() => {
const searchConfigFacet = search({})[0].facet
const dummyView = new EditorView({
doc: '',
extensions: [search()],
})
const config = dummyView.state.facet(searchConfigFacet)
const SearchPanel = config.createPanel(dummyView).constructor
dummyView.destroy()
return SearchPanel
})()
function enhancedCreatePanel(ext) {
return view => {
const panel = new SearchPanel(view)
console.log(panel)
const oldField = panel.searchField
// make a new view
const outer = document.createElement('div')
const editor = new EditorView({
doc: oldField.value,
extensions: [
ext,
placeholder(oldField.placeholder),
EditorView.theme({
'&.cm-editor': { height: 'auto' },
'&.cm-focused': { outlineStyle: 'none !important' },
'.cm-scroller': { overflow: 'hidden' },
'.cm-content': { padding: '0' },
'.cm-line': { paddingLeft: '1px' },
}),
// add the original keyup handler
EditorView.domEventHandlers({
change: (_e, innerView) => {
_latched = true
innerView.contentDOM.value = innerView.state.doc.toString()
_latched = false
panel.commit()
},
keyup: (_e, innerView) => {
_latched = true
innerView.contentDOM.value = innerView.state.doc.toString()
_latched = false
panel.commit()
},
}),
// simply disable enter key to break a line
Prec.highest(keymap.of([
{key: 'Enter', run: () => true},
])),
// TODO: restrict to single line
],
parent: outer,
})
// TODO: add all attributes from old field
outer.className = 'cm-textfield'
outer.style = `height: fit-content;
width: 160px;
margin-right: 4px;
display: inline-block;`
// monkey-patching the field to make it behave like a text field
editor.contentDOM.setAttribute('main-field', true)
let _value = ''
let _latched = false
// this is how one create a private property from outside
Object.defineProperty(editor.contentDOM, 'value', {
get() { return _value },
set(value) {
_value = value
if (!_latched)
editor.dispatch({ changes: { from: 0, to: editor.state.doc.length, insert: value } })
},
})
editor.contentDOM.select = () => {
const pos = editor.state.doc.length
editor.focus()
editor.dispatch({ selection: { anchor: 0, head: pos } })
}
oldField.after(outer)
oldField.remove()
panel.searchField = editor.contentDOM
return panel
}
}
new EditorView({
doc: "console.log('hello')\n",
extensions: [
basicSetup,
search({
createPanel: enhancedCreatePanel([minimalSetup]),
}),
],
parent: document.body,
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment