import { Extension, Range } from '@codemirror/state'; import { Decoration, DecorationSet, EditorView, ViewPlugin, ViewUpdate } from '@codemirror/view'; import { syntaxTree } from '@codemirror/language'; import { isCursorInRange } from '../util'; /** * Inline code styling plugin. * * This plugin adds visual styling to inline code (`code`): * - Background color * - Border radius * - Padding effect via marks */ export const inlineCode = (): Extension => [inlineCodePlugin, baseTheme]; /** * Build inline code decorations. */ function buildInlineCodeDecorations(view: EditorView): DecorationSet { const decorations: Range[] = []; for (const { from, to } of view.visibleRanges) { syntaxTree(view.state).iterate({ from, to, enter: ({ type, from: nodeFrom, to: nodeTo }) => { if (type.name !== 'InlineCode') return; const cursorInCode = isCursorInRange(view.state, [nodeFrom, nodeTo]); // Skip background decoration when cursor is in the code // This allows selection highlighting to be visible when editing if (cursorInCode) return; // Get the actual code content (excluding backticks) const text = view.state.doc.sliceString(nodeFrom, nodeTo); // Find backtick positions let codeStart = nodeFrom; let codeEnd = nodeTo; // Skip opening backticks let i = 0; while (i < text.length && text[i] === '`') { codeStart++; i++; } // Skip closing backticks let j = text.length - 1; while (j >= 0 && text[j] === '`') { codeEnd--; j--; } // Only add decoration if there's actual content if (codeStart < codeEnd) { // Add mark decoration for the code content decorations.push( Decoration.mark({ class: 'cm-inline-code' }).range(codeStart, codeEnd) ); } } }); } return Decoration.set(decorations, true); } /** * Inline code plugin class. */ class InlineCodePlugin { decorations: DecorationSet; constructor(view: EditorView) { this.decorations = buildInlineCodeDecorations(view); } update(update: ViewUpdate) { if (update.docChanged || update.viewportChanged || update.selectionSet) { this.decorations = buildInlineCodeDecorations(update.view); } } } const inlineCodePlugin = ViewPlugin.fromClass(InlineCodePlugin, { decorations: (v) => v.decorations }); /** * Base theme for inline code. * Uses CSS variables from variables.css for consistent theming. */ const baseTheme = EditorView.baseTheme({ '.cm-inline-code': { backgroundColor: 'var(--cm-inline-code-bg)', borderRadius: '0.25rem', padding: '0.1rem 0.3rem', fontFamily: 'var(--voidraft-font-mono)' } });