0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-03-26 21:18:12 +00:00

syntax highligting for legacy as well

This commit is contained in:
Víctor Losada Hernández
2026-03-26 14:22:39 +01:00
parent f50c25b906
commit b0ebeaffb9
5 changed files with 125 additions and 45 deletions

View File

@@ -8,6 +8,8 @@ import {
highlightActiveLineGutter,
highlightActiveLine,
scrollPastEnd,
Decoration,
ViewPlugin,
} from '@codemirror/view';
import { EditorState, Compartment } from '@codemirror/state';
import { foldGutter, foldKeymap, syntaxHighlighting } from '@codemirror/language';
@@ -19,8 +21,50 @@ import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
import * as themes from '@uiw/codemirror-themes-all';
const themeCompartment = new Compartment();
import { customHighlightPlugin, customHighlightStyle } from './customHighlight.js';
const highlightCompartment = new Compartment();
import { homebreweryFold, hbFolding } from './customFolding.js';
import { customHighlightStyle, tokenizeCustomMarkdown } from './customHighlight.js';
import { legacyCustomHighlightStyle, legacyTokenizeCustomMarkdown } from './legacyCustomHighlight.js'; //only makes highlight for
const createHighlightPlugin = (renderer)=>{
console.log(renderer);
const tokenize = renderer === 'V3' ? tokenizeCustomMarkdown : legacyTokenizeCustomMarkdown;
return ViewPlugin.fromClass(
class {
constructor(view) {
this.decorations = this.buildDecorations(view);
}
update(update) {
if(update.docChanged) {
this.decorations = this.buildDecorations(update.view);
}
}
buildDecorations(view) {
const decos = [];
const tokens = tokenize(view.state.doc.toString());
tokens.forEach((tok)=>{
const line = view.state.doc.line(tok.line + 1);
if(tok.from != null && tok.to != null && tok.from < tok.to) {
decos.push(
Decoration.mark({ class: `cm-${tok.type}` }).range(line.from + tok.from, line.from + tok.to)
);
} else {
decos.push(Decoration.line({ class: `cm-${tok.type}` }).range(line.from));
}
});
decos.sort((a, b)=>a.from - b.from || a.to - b.to);
return Decoration.set(decos);
}
},
{ decorations: (v)=>v.decorations }
);
};
const CodeEditor = forwardRef(
(
@@ -32,6 +76,7 @@ const CodeEditor = forwardRef(
editorTheme = 'default',
view,
style,
renderer,
...props
},
ref,
@@ -79,11 +124,24 @@ const CodeEditor = forwardRef(
{ key: 'Mod-i', run: italicCommand },
]);
const highlightExtension = renderer === 'V3'
? syntaxHighlighting(customHighlightStyle)
: syntaxHighlighting(legacyCustomHighlightStyle);
const customHighlightPlugin = createHighlightPlugin(renderer);
const combinedHighlight = [
customHighlightPlugin,
highlightExtension,
];
const languageExtension =
language === 'css' ? css() : markdown({ base: markdownLanguage, codeLanguages: languages });
const themeExtension = Array.isArray(themes[editorTheme]) ? themes[editorTheme] : [];
return [
history(),
keymap.of(defaultKeymap),
@@ -107,7 +165,7 @@ const CodeEditor = forwardRef(
highlightActiveLine(),
highlightActiveLineGutter(),
customHighlightPlugin,
syntaxHighlighting(customHighlightStyle),
highlightCompartment.of(combinedHighlight),
];
};
@@ -175,6 +233,21 @@ const CodeEditor = forwardRef(
effects : themeCompartment.reconfigure(themeExtension),
});
}, [editorTheme]);
useEffect(()=>{
const view = viewRef.current;
if(!view) return;
const highlightExtension =
renderer === 'V3'
? syntaxHighlighting(customHighlightStyle)
: syntaxHighlighting(legacyCustomHighlightStyle);
const customHighlightPlugin = createHighlightPlugin(renderer);
view.dispatch({
effects : highlightCompartment.reconfigure([customHighlightPlugin, highlightExtension]),
});
}, [renderer]);
useImperativeHandle(ref, ()=>({
getValue : ()=>viewRef.current.state.doc.toString(),

View File

@@ -31,7 +31,6 @@ export const homebreweryFold = foldService.of((state, lineStart)=>{
if(endLine === startLine.number) return null;
const widgetObject = { from: startLine.from, to: doc.line(endLine).to };
console.log(widgetObject);
return widgetObject;
});

View File

@@ -1,9 +1,5 @@
import { HighlightStyle } from '@codemirror/language';
import { tags } from '@lezer/highlight';
import {
Decoration,
ViewPlugin,
} from '@codemirror/view';
// Making the tokens
const customTags = {
@@ -23,7 +19,7 @@ const customTags = {
definitionColon : 'definitionColon', // .cm-definitionColon
};
function tokenizeCustomMarkdown(text) {
export function tokenizeCustomMarkdown(text) {
const tokens = [];
const lines = text.split('\n');
@@ -39,7 +35,18 @@ function tokenizeCustomMarkdown(text) {
if(/\\snippet/.test(lineText)) tokens.push({ line: lineNumber, type: customTags.snippetBreak });
// --- Emoji ---
if(/:\w+?:/.test(lineText)) tokens.push({ line: lineNumber, type: customTags.emoji });
if(/:.\w+?:/.test(lineText)) {
const emojiRegex = /(:\w+?:)/g;
let match;
while ((match = emojiRegex.exec(lineText)) !== null) {
tokens.push({
line : lineNumber,
type : customTags.emoji,
from : match.index,
to : match.index + match[0].length,
});
}
}
// --- Superscript / Subscript ---
if(/\^/.test(lineText)) {
@@ -243,41 +250,5 @@ export const customHighlightStyle = HighlightStyle.define([
{ tag: customTags.definitionDesc, class: 'cm-dd', color: '#070' },
]);
export const customHighlightPlugin = ViewPlugin.fromClass(
class {
constructor(view) {
this.decorations = this.buildDecorations(view);
}
update(update) {
if(update.docChanged) {
this.decorations = this.buildDecorations(update.view);
}
}
buildDecorations(view) {
const decos = [];
const tokens = tokenizeCustomMarkdown(view.state.doc.toString());
tokens.forEach((tok)=>{
const line = view.state.doc.line(tok.line + 1);
if(tok.from != null && tok.to != null && tok.from < tok.to) {
// inline decoration
decos.push(
Decoration.mark({ class: `cm-${tok.type}` }).range(line.from + tok.from, line.from + tok.to),
);
} else {
// full-line decoration
decos.push(Decoration.line({ class: `cm-${tok.type}` }).range(line.from));
}
});
// sort by absolute start position
decos.sort((a, b)=>a.from - b.from || a.to - b.to);
return Decoration.set(decos);
}
},
{
decorations : (v)=>v.decorations,
},
);

View File

@@ -0,0 +1,34 @@
import { HighlightStyle } from '@codemirror/language';
import { tags } from '@lezer/highlight';
// Making the tokens
const customTags = {
pageLine : 'pageLine', // .cm-pageLine
snippetLine : 'snippetLine', // .cm-snippetLine
};
export function legacyTokenizeCustomMarkdown(text) {
const tokens = [];
const lines = text.split('\n');
// Track multi-line blocks
const inBlock = false;
const blockStart = 0;
lines.forEach((lineText, lineNumber)=>{
// --- Page / snippet lines ---
if(/^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m.test(lineText)) tokens.push({ line: lineNumber, type: customTags.pageLine });
if(/^\\snippet\ .*$/.test(lineText)) tokens.push({ line: lineNumber, type: customTags.snippetLine });
});
return tokens;
}
export const legacyCustomHighlightStyle = HighlightStyle.define([
{ tag: tags.heading1, color: '#000', fontWeight: '700' },
{ tag: tags.keyword, color: '#07a' }, // example for your markdown headings
{ tag: customTags.pageLine, color: '#f0a' },
{ tag: customTags.snippetLine, class: 'cm-snippetLine', color: '#0af' },
]);

View File

@@ -447,6 +447,7 @@ const Editor = createReactClass({
value={this.props.brew.text}
onChange={this.props.onBrewChange('text')}
editorTheme={this.state.editorTheme}
renderer={this.props.brew.renderer}
rerenderParent={this.rerenderParent}
style={{ height: `calc(100% - ${this.state.snippetBarHeight}px)` }} />
</>;
@@ -462,6 +463,7 @@ const Editor = createReactClass({
onChange={this.props.onBrewChange('style')}
enableFolding={true}
editorTheme={this.state.editorTheme}
renderer={this.props.brew.renderer}
rerenderParent={this.rerenderParent}
style={{ height: `calc(100% - ${this.state.snippetBarHeight}px)` }} />
</>;
@@ -492,6 +494,7 @@ const Editor = createReactClass({
onChange={this.props.onBrewChange('snippets')}
enableFolding={true}
editorTheme={this.state.editorTheme}
renderer={this.props.brew.renderer}
rerenderParent={this.rerenderParent}
style={{ height: `calc(100% -${this.state.snippetBarHeight}px)` }} />
</>;