| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- import { Decoration, EditorView, Panel, showPanel } from '@codemirror/view'
- import { languageLoadedEffect } from '../../extensions/language'
- import { Compartment, EditorState } from '@codemirror/state'
- import { getAncestorStack } from '../../utils/tree-query'
- import { resolveNodeAtPos } from '../../utils/tree-operations/common'
- const decorationsConf = new Compartment()
- export const debugPanel = () => {
- const enableDebugPanel = new URLSearchParams(window.location.search).has(
- 'cm_debug_panel'
- )
- if (!enableDebugPanel) {
- return []
- }
- return [
- showPanel.of(createInfoPanel),
- decorationsConf.of(EditorView.decorations.of(Decoration.none)),
- // clear the highlight when the selection changes
- EditorView.updateListener.of(update => {
- if (update.selectionSet) {
- update.view.dispatch({
- effects: decorationsConf.reconfigure(
- EditorView.decorations.of(Decoration.none)
- ),
- })
- }
- }),
- EditorView.baseTheme({
- '.ol-cm-debug-panel': {
- paddingBottom: '24px',
- },
- '.ol-cm-debug-panel-type': {
- backgroundColor: '#138a07',
- color: '#fff',
- padding: '0px 4px',
- marginLeft: '4px',
- borderRadius: '4px',
- },
- '.ol-cm-debug-panel-item': {
- border: 'none',
- backgroundColor: '#fff',
- color: '#000',
- outline: '1px solid transparent',
- marginBottom: '2px',
- display: 'inline-flex',
- alignItems: 'center',
- '&:hover': {
- outlineColor: '#000',
- },
- },
- '.ol-cm-debug-panel-position': {
- position: 'absolute',
- bottom: '0',
- right: '0',
- padding: '5px',
- },
- '.ol-cm-debug-panel-node-highlight': {
- backgroundColor: '#ffff0077',
- },
- }),
- ]
- }
- const placeholder = () => document.createElement('div')
- const createInfoPanel = (view: EditorView): Panel => {
- const dom = document.createElement('div')
- dom.className = 'ol-cm-debug-panel'
- dom.append(buildPanelContent(view, view.state))
- return {
- dom,
- update(update) {
- if (update.selectionSet) {
- // update when the selection changes
- dom.firstChild!.replaceWith(
- buildPanelContent(update.view, update.state)
- )
- } else {
- // update when the language is loaded
- for (const tr of update.transactions) {
- if (tr.effects.some(effect => effect.is(languageLoadedEffect))) {
- dom.firstChild!.replaceWith(
- buildPanelContent(update.view, update.state)
- )
- }
- }
- }
- },
- }
- }
- const buildPanelContent = (
- view: EditorView,
- state: EditorState
- ): HTMLDivElement => {
- const pos = state.selection.main.anchor
- const ancestors = getAncestorStack(state, pos)
- if (!ancestors) {
- return placeholder()
- }
- if (ancestors.length > 0) {
- const node = ancestors[ancestors.length - 1]
- const nodeBefore = resolveNodeAtPos(state, pos, -1)
- const nodeAfter = resolveNodeAtPos(state, pos, 1)
- const parts = []
- if (nodeBefore) {
- parts.push(`[${nodeBefore.name}]`)
- }
- parts.push(node.label)
- if (nodeAfter) {
- parts.push(`[${nodeAfter.name}]`)
- }
- node.label = parts.join(' ')
- }
- const panelContent = document.createElement('div')
- panelContent.style.padding = '5px 10px'
- const line = state.doc.lineAt(pos)
- const column = pos - line.from + 1
- const positionContainer = document.createElement('div')
- positionContainer.className = 'ol-cm-debug-panel-position'
- positionContainer.textContent = `line ${line.number}, col ${column}, pos ${pos}`
- panelContent.appendChild(positionContainer)
- const stackContainer = document.createElement('div')
- for (const [index, item] of ancestors.entries()) {
- if (index > 0) {
- stackContainer.append(' > ')
- }
- const element = document.createElement('button')
- element.className = 'ol-cm-debug-panel-item'
- const label = document.createElement('span')
- label.className = 'ol-cm-debug-panel-label'
- label.textContent = item.label
- element.append(label)
- if (item.type) {
- const type = document.createElement('span')
- type.className = 'ol-cm-debug-panel-type'
- type.textContent = item.type
- element.append(type)
- }
- element.addEventListener('click', () => {
- view.dispatch({
- effects: [
- decorationsConf.reconfigure(
- EditorView.decorations.of(
- Decoration.set(
- Decoration.mark({
- class: 'ol-cm-debug-panel-node-highlight',
- }).range(item.from, item.to)
- )
- )
- ),
- EditorView.scrollIntoView(item.from, { y: 'center' }),
- ],
- })
- })
- stackContainer.append(element)
- }
- panelContent.appendChild(stackContainer)
- return panelContent
- }
|