mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-03-27 05:58:10 +00:00
syntax highligting for legacy as well
This commit is contained in:
@@ -8,6 +8,8 @@ import {
|
|||||||
highlightActiveLineGutter,
|
highlightActiveLineGutter,
|
||||||
highlightActiveLine,
|
highlightActiveLine,
|
||||||
scrollPastEnd,
|
scrollPastEnd,
|
||||||
|
Decoration,
|
||||||
|
ViewPlugin,
|
||||||
} from '@codemirror/view';
|
} from '@codemirror/view';
|
||||||
import { EditorState, Compartment } from '@codemirror/state';
|
import { EditorState, Compartment } from '@codemirror/state';
|
||||||
import { foldGutter, foldKeymap, syntaxHighlighting } from '@codemirror/language';
|
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';
|
import * as themes from '@uiw/codemirror-themes-all';
|
||||||
const themeCompartment = new Compartment();
|
const themeCompartment = new Compartment();
|
||||||
|
|
||||||
import { customHighlightPlugin, customHighlightStyle } from './customHighlight.js';
|
const highlightCompartment = new Compartment();
|
||||||
|
|
||||||
import { homebreweryFold, hbFolding } from './customFolding.js';
|
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(
|
const CodeEditor = forwardRef(
|
||||||
(
|
(
|
||||||
@@ -32,6 +76,7 @@ const CodeEditor = forwardRef(
|
|||||||
editorTheme = 'default',
|
editorTheme = 'default',
|
||||||
view,
|
view,
|
||||||
style,
|
style,
|
||||||
|
renderer,
|
||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
ref,
|
ref,
|
||||||
@@ -79,11 +124,24 @@ const CodeEditor = forwardRef(
|
|||||||
{ key: 'Mod-i', run: italicCommand },
|
{ key: 'Mod-i', run: italicCommand },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const highlightExtension = renderer === 'V3'
|
||||||
|
? syntaxHighlighting(customHighlightStyle)
|
||||||
|
: syntaxHighlighting(legacyCustomHighlightStyle);
|
||||||
|
|
||||||
|
const customHighlightPlugin = createHighlightPlugin(renderer);
|
||||||
|
|
||||||
|
const combinedHighlight = [
|
||||||
|
customHighlightPlugin,
|
||||||
|
highlightExtension,
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
const languageExtension =
|
const languageExtension =
|
||||||
language === 'css' ? css() : markdown({ base: markdownLanguage, codeLanguages: languages });
|
language === 'css' ? css() : markdown({ base: markdownLanguage, codeLanguages: languages });
|
||||||
|
|
||||||
const themeExtension = Array.isArray(themes[editorTheme]) ? themes[editorTheme] : [];
|
const themeExtension = Array.isArray(themes[editorTheme]) ? themes[editorTheme] : [];
|
||||||
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
history(),
|
history(),
|
||||||
keymap.of(defaultKeymap),
|
keymap.of(defaultKeymap),
|
||||||
@@ -107,7 +165,7 @@ const CodeEditor = forwardRef(
|
|||||||
highlightActiveLine(),
|
highlightActiveLine(),
|
||||||
highlightActiveLineGutter(),
|
highlightActiveLineGutter(),
|
||||||
customHighlightPlugin,
|
customHighlightPlugin,
|
||||||
syntaxHighlighting(customHighlightStyle),
|
highlightCompartment.of(combinedHighlight),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -175,6 +233,21 @@ const CodeEditor = forwardRef(
|
|||||||
effects : themeCompartment.reconfigure(themeExtension),
|
effects : themeCompartment.reconfigure(themeExtension),
|
||||||
});
|
});
|
||||||
}, [editorTheme]);
|
}, [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, ()=>({
|
useImperativeHandle(ref, ()=>({
|
||||||
getValue : ()=>viewRef.current.state.doc.toString(),
|
getValue : ()=>viewRef.current.state.doc.toString(),
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ export const homebreweryFold = foldService.of((state, lineStart)=>{
|
|||||||
if(endLine === startLine.number) return null;
|
if(endLine === startLine.number) return null;
|
||||||
|
|
||||||
const widgetObject = { from: startLine.from, to: doc.line(endLine).to };
|
const widgetObject = { from: startLine.from, to: doc.line(endLine).to };
|
||||||
console.log(widgetObject);
|
|
||||||
|
|
||||||
return widgetObject;
|
return widgetObject;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
import { HighlightStyle } from '@codemirror/language';
|
import { HighlightStyle } from '@codemirror/language';
|
||||||
import { tags } from '@lezer/highlight';
|
import { tags } from '@lezer/highlight';
|
||||||
import {
|
|
||||||
Decoration,
|
|
||||||
ViewPlugin,
|
|
||||||
} from '@codemirror/view';
|
|
||||||
|
|
||||||
// Making the tokens
|
// Making the tokens
|
||||||
const customTags = {
|
const customTags = {
|
||||||
@@ -23,7 +19,7 @@ const customTags = {
|
|||||||
definitionColon : 'definitionColon', // .cm-definitionColon
|
definitionColon : 'definitionColon', // .cm-definitionColon
|
||||||
};
|
};
|
||||||
|
|
||||||
function tokenizeCustomMarkdown(text) {
|
export function tokenizeCustomMarkdown(text) {
|
||||||
const tokens = [];
|
const tokens = [];
|
||||||
const lines = text.split('\n');
|
const lines = text.split('\n');
|
||||||
|
|
||||||
@@ -39,7 +35,18 @@ function tokenizeCustomMarkdown(text) {
|
|||||||
if(/\\snippet/.test(lineText)) tokens.push({ line: lineNumber, type: customTags.snippetBreak });
|
if(/\\snippet/.test(lineText)) tokens.push({ line: lineNumber, type: customTags.snippetBreak });
|
||||||
|
|
||||||
// --- Emoji ---
|
// --- 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 ---
|
// --- Superscript / Subscript ---
|
||||||
if(/\^/.test(lineText)) {
|
if(/\^/.test(lineText)) {
|
||||||
@@ -243,41 +250,5 @@ export const customHighlightStyle = HighlightStyle.define([
|
|||||||
{ tag: customTags.definitionDesc, class: 'cm-dd', color: '#070' },
|
{ 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,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|||||||
34
client/components/codeEditor/legacyCustomHighlight.js
Normal file
34
client/components/codeEditor/legacyCustomHighlight.js
Normal 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' },
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
@@ -447,6 +447,7 @@ const Editor = createReactClass({
|
|||||||
value={this.props.brew.text}
|
value={this.props.brew.text}
|
||||||
onChange={this.props.onBrewChange('text')}
|
onChange={this.props.onBrewChange('text')}
|
||||||
editorTheme={this.state.editorTheme}
|
editorTheme={this.state.editorTheme}
|
||||||
|
renderer={this.props.brew.renderer}
|
||||||
rerenderParent={this.rerenderParent}
|
rerenderParent={this.rerenderParent}
|
||||||
style={{ height: `calc(100% - ${this.state.snippetBarHeight}px)` }} />
|
style={{ height: `calc(100% - ${this.state.snippetBarHeight}px)` }} />
|
||||||
</>;
|
</>;
|
||||||
@@ -462,6 +463,7 @@ const Editor = createReactClass({
|
|||||||
onChange={this.props.onBrewChange('style')}
|
onChange={this.props.onBrewChange('style')}
|
||||||
enableFolding={true}
|
enableFolding={true}
|
||||||
editorTheme={this.state.editorTheme}
|
editorTheme={this.state.editorTheme}
|
||||||
|
renderer={this.props.brew.renderer}
|
||||||
rerenderParent={this.rerenderParent}
|
rerenderParent={this.rerenderParent}
|
||||||
style={{ height: `calc(100% - ${this.state.snippetBarHeight}px)` }} />
|
style={{ height: `calc(100% - ${this.state.snippetBarHeight}px)` }} />
|
||||||
</>;
|
</>;
|
||||||
@@ -492,6 +494,7 @@ const Editor = createReactClass({
|
|||||||
onChange={this.props.onBrewChange('snippets')}
|
onChange={this.props.onBrewChange('snippets')}
|
||||||
enableFolding={true}
|
enableFolding={true}
|
||||||
editorTheme={this.state.editorTheme}
|
editorTheme={this.state.editorTheme}
|
||||||
|
renderer={this.props.brew.renderer}
|
||||||
rerenderParent={this.rerenderParent}
|
rerenderParent={this.rerenderParent}
|
||||||
style={{ height: `calc(100% -${this.state.snippetBarHeight}px)` }} />
|
style={{ height: `calc(100% -${this.state.snippetBarHeight}px)` }} />
|
||||||
</>;
|
</>;
|
||||||
|
|||||||
Reference in New Issue
Block a user