mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Add syntax highlighting
This commit is contained in:
parent
67d60ade1c
commit
616ceebcb3
|
@ -4,7 +4,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "lezer-generator src/components/RSInput/rslang.grammar -o src/components/RSInput/rslangparser.ts",
|
"prepare": "lezer-generator src/components/RSInput/rslang/rslang.grammar -o src/components/RSInput/rslang/parser.ts",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
import {styleTags, tags} from '@lezer/highlight';
|
|
||||||
|
|
||||||
export const highlighting = styleTags({
|
|
||||||
Identifier: tags.name,
|
|
||||||
Number: tags.number,
|
|
||||||
String: tags.string
|
|
||||||
});
|
|
|
@ -1,11 +1,13 @@
|
||||||
import { bracketMatching } from '@codemirror/language';
|
import { bracketMatching } from '@codemirror/language';
|
||||||
import { Extension } from '@codemirror/state';
|
import { Extension } from '@codemirror/state';
|
||||||
|
import { tags as t } from '@lezer/highlight';
|
||||||
import { createTheme } from '@uiw/codemirror-themes';
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||||
import { EditorView } from 'codemirror';
|
import { EditorView } from 'codemirror';
|
||||||
import { Ref, useMemo } from 'react';
|
import { Ref, useMemo } from 'react';
|
||||||
|
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
|
import { RSLanguage } from './rslang';
|
||||||
|
|
||||||
const editorSetup: BasicSetupOptions = {
|
const editorSetup: BasicSetupOptions = {
|
||||||
highlightSpecialChars: true,
|
highlightSpecialChars: true,
|
||||||
|
@ -37,6 +39,7 @@ const editorSetup: BasicSetupOptions = {
|
||||||
|
|
||||||
const editorExtensions = [
|
const editorExtensions = [
|
||||||
EditorView.lineWrapping,
|
EditorView.lineWrapping,
|
||||||
|
RSLanguage,
|
||||||
bracketMatching()
|
bracketMatching()
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -62,15 +65,16 @@ function RSInput({
|
||||||
fontFamily: 'inherit',
|
fontFamily: 'inherit',
|
||||||
background: editable ? '#ffffff' : '#f0f2f7',
|
background: editable ? '#ffffff' : '#f0f2f7',
|
||||||
foreground: '#000000',
|
foreground: '#000000',
|
||||||
selection: '#036dd626',
|
selection: '#aacef2',
|
||||||
selectionMatch: '#036dd626',
|
|
||||||
caret: '#5d00ff',
|
caret: '#5d00ff',
|
||||||
},
|
},
|
||||||
styles: [
|
styles: [
|
||||||
// { tag: t.comment, color: '#787b8099' },
|
{ tag: t.name, class: 'text-[#b266ff]' }, // GlobalID
|
||||||
// { tag: t.variableName, color: '#0080ff' },
|
{ tag: t.variableName, class: 'text-[#24821a]' }, // LocalID
|
||||||
// { tag: [t.string, t.special(t.brace)], color: '#5c6166' },
|
{ tag: t.propertyName, class: '' }, // Radical
|
||||||
// { tag: t.definition(t.typeName), color: '#5c6166' },
|
{ tag: t.keyword, class: 'text-[#001aff]' }, // keywords
|
||||||
|
{ tag: t.controlKeyword, class: 'font-semibold'}, // R | I | D
|
||||||
|
{ tag: t.unit, class: 'text-[0.75rem]' }, // indicies
|
||||||
]
|
]
|
||||||
}), [editable]);
|
}), [editable]);
|
||||||
|
|
||||||
|
@ -81,20 +85,21 @@ function RSInput({
|
||||||
fontFamily: 'inherit',
|
fontFamily: 'inherit',
|
||||||
background: editable ? '#070b12' : '#374151',
|
background: editable ? '#070b12' : '#374151',
|
||||||
foreground: '#e4e4e7',
|
foreground: '#e4e4e7',
|
||||||
selection: '#ffae00b0',
|
selection: '#8c6000',
|
||||||
selectionMatch: '#ffae00b0',
|
|
||||||
caret: '#ffaa00'
|
caret: '#ffaa00'
|
||||||
},
|
},
|
||||||
styles: [
|
styles: [
|
||||||
// { tag: t.comment, color: '#787b8099' },
|
{ tag: t.name, class: 'text-[#dfbfff]' }, // GlobalID
|
||||||
// { tag: t.variableName, color: '#0080ff' },
|
{ tag: t.variableName, class: 'text-[#69bf60]' }, // LocalID
|
||||||
// { tag: [t.string, t.special(t.brace)], color: '#5c6166' },
|
{ tag: t.propertyName, class: '' }, // Radical
|
||||||
// { tag: t.definition(t.typeName), color: '#5c6166' },
|
{ tag: t.keyword, class: 'text-[#808dff]' }, // keywords
|
||||||
|
{ tag: t.controlKeyword, class: 'font-semibold'}, // R | I | D
|
||||||
|
{ tag: t.unit, class: 'text-[0.75rem]' }, // indicies
|
||||||
]
|
]
|
||||||
}), [editable]);
|
}), [editable]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`w-full h-[${height}] ${cursor}`}>
|
<div className={`w-full h-[${height}] ${cursor} text-lg`}>
|
||||||
<CodeMirror
|
<CodeMirror
|
||||||
ref={innerref}
|
ref={innerref}
|
||||||
basicSetup={editorSetup}
|
basicSetup={editorSetup}
|
|
@ -1,20 +0,0 @@
|
||||||
@top Program { expression* }
|
|
||||||
|
|
||||||
@skip { space }
|
|
||||||
|
|
||||||
expression {
|
|
||||||
Identifier |
|
|
||||||
String |
|
|
||||||
Application { "(" expression* ")" }
|
|
||||||
}
|
|
||||||
|
|
||||||
@tokens {
|
|
||||||
space { @whitespace+ }
|
|
||||||
Identifier { $[a-zA-Z_\-0-9]+ }
|
|
||||||
String { '"' (!["\\] | "\\" _)* '"' }
|
|
||||||
"(" ")"
|
|
||||||
}
|
|
||||||
|
|
||||||
@detectDelim
|
|
||||||
|
|
||||||
@external propSource highlighting from "./highlight.ts"
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import {styleTags, tags} from '@lezer/highlight';
|
||||||
|
|
||||||
|
export const highlighting = styleTags({
|
||||||
|
Index: tags.unit,
|
||||||
|
ComplexIndex: tags.unit,
|
||||||
|
Integer: tags.literal,
|
||||||
|
|
||||||
|
Radical: tags.propertyName,
|
||||||
|
Global: tags.name,
|
||||||
|
Local: tags.variableName,
|
||||||
|
|
||||||
|
TextFunction: tags.keyword,
|
||||||
|
ConstructPrefix: tags.controlKeyword
|
||||||
|
});
|
|
@ -0,0 +1,8 @@
|
||||||
|
import {LRLanguage} from "@codemirror/language"
|
||||||
|
|
||||||
|
import { parser } from './parser';
|
||||||
|
|
||||||
|
export const RSLanguage = LRLanguage.define({
|
||||||
|
parser: parser,
|
||||||
|
languageData: {}
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
|
export const
|
||||||
|
Expression = 1,
|
||||||
|
TextFunction = 2,
|
||||||
|
ComplexIndex = 3,
|
||||||
|
ConstructPrefix = 4,
|
||||||
|
Integer = 5,
|
||||||
|
Global = 6,
|
||||||
|
Radical = 7,
|
||||||
|
Local = 8,
|
||||||
|
Index = 9
|
18
rsconcept/frontend/src/components/RSInput/rslang/parser.ts
Normal file
18
rsconcept/frontend/src/components/RSInput/rslang/parser.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
|
import {LRParser} from "@lezer/lr"
|
||||||
|
import {highlighting} from "./highlight.ts"
|
||||||
|
export const parser = LRParser.deserialize({
|
||||||
|
version: 14,
|
||||||
|
states: "!^QVQPOOO}QPO'#C^OOQO'#C^'#C^O!SQQO'#CdOOQO'#Cj'#CjOOQO'#Cf'#CfQVQPOOOOQO,58x,58xOOQO,59O,59OOOQO-E6d-E6d",
|
||||||
|
stateData: "#a~O]OS~OSSOTSOUSOVSO_PO`POaPObQOcQOdQOeQOfRO~ORVO~OXWOSWXTWXUWXVWXZWX_WX`WXaWXbWXcWXdWXeWXfWX~Oa_T]UVSbcde`bf~",
|
||||||
|
goto: "n_PP`PPPPP`PdPPPjTSOUQUORXUTTOU",
|
||||||
|
nodeNames: "⚠ Expression TextFunction ComplexIndex ConstructPrefix Integer Global Radical Local Index",
|
||||||
|
maxTerm: 22,
|
||||||
|
propSources: [highlighting],
|
||||||
|
skippedNodes: [0],
|
||||||
|
repeatNodeCount: 1,
|
||||||
|
tokenData: ".p~RqX^#Ypq#Y!Q!R%m!R![%u!c!d&b!e!f&b!f!g&p!h!i&x!k!l'W!r!s']!t!u'k!u!v&b!v!w&b!z!{&b#R#S'{#T#U'{#U#V(W#V#W)j#W#X*y#X#d'{#d#e-P#e#f'{#f#g-o#g#o'{#y#z#Y$f$g#Y5i6S'{#BY#BZ#Y$IS$I_#Y$I|$JO#Y$JT$JU#Y$KV$KW#Y&FU&FV#Y~#_Z]~X^#Ypq#Y!Q![$Q#y#z#Y$f$g#Y#BY#BZ#Y$IS$I_#Y$I|$JO#Y$JT$JU#Y$KV$KW#Y&FU&FV#Y~$VZT~X^$xpq$x!Q![$Q#y#z$x$f$g$x#BY#BZ$x$IS$I_$x$I|$JO$x$JT$JU$x$KV$KW$x&FU&FV$x~$}YT~X^$xpq$x#y#z$x$f$g$x#BY#BZ$x$IS$I_$x$I|$JO$x$JT$JU$x$KV$KW$x&FU&FV$xQ%rPXQ!Q![%mR%|QRPXQ|}&S!Q![%mP&VP!R![&YP&_PRP|}&S~&eP!Q![&h~&mPU~!Q![&h~&uPS~!Q![&h~&{Q!Q![&h#]#^'R~'WOa~~']OS~~'`Q!Q![&h#f#g'f~'kO_~~'pPS~!Q!['s~'xPV~!Q!['s~(QQf~#T#o'{5i6S'{~(]Sf~#T#c'{#c#d(i#d#o'{5i6S'{~(nSf~#T#c'{#c#d(z#d#o'{5i6S'{~)PSf~#T#`'{#`#a)]#a#o'{5i6S'{~)dQc~f~#T#o'{5i6S'{~)oRf~#T#U)x#U#o'{5i6S'{~)}Sf~#T#f'{#f#g*Z#g#o'{5i6S'{~*`Sf~#T#W'{#W#X*l#X#o'{5i6S'{~*sQb~f~#T#o'{5i6S'{~+OSf~#T#X'{#X#Y+[#Y#o'{5i6S'{~+aSf~#T#U'{#U#V+m#V#o'{5i6S'{~+rSf~#T#c'{#c#d,O#d#o'{5i6S'{~,TSf~#T#c'{#c#d,a#d#o'{5i6S'{~,fSf~#T#`'{#`#a,r#a#o'{5i6S'{~,yQd~f~#T#o'{5i6S'{~-USf~#T#f'{#f#g-b#g#o'{5i6S'{~-iQ`~f~#T#o'{5i6S'{~-tSf~#T#X'{#X#Y.Q#Y#o'{5i6S'{~.VSf~#T#W'{#W#X.c#X#o'{5i6S'{~.jQe~f~#T#o'{5i6S'{",
|
||||||
|
tokenizers: [0, 1],
|
||||||
|
topRules: {"Expression":[0,1]},
|
||||||
|
tokenPrec: 94
|
||||||
|
})
|
|
@ -0,0 +1,61 @@
|
||||||
|
@top Expression { token* }
|
||||||
|
|
||||||
|
@skip { space }
|
||||||
|
|
||||||
|
@tokens {
|
||||||
|
space { @whitespace+ }
|
||||||
|
Index { $[0-9]+ }
|
||||||
|
ComplexIndex { $[1-9](","$[1-9])* }
|
||||||
|
Integer { space$[0-9]+space? }
|
||||||
|
|
||||||
|
bigPr { "Pr" }
|
||||||
|
smallPr { "pr" }
|
||||||
|
filter { "Fi" }
|
||||||
|
card { "card" }
|
||||||
|
bool { "bool" }
|
||||||
|
debool { "debool" }
|
||||||
|
red { "red" }
|
||||||
|
|
||||||
|
ConstructPrefix { "D" | "R" | "I" }
|
||||||
|
|
||||||
|
Global { $[XCSDAPTF]$[0-9]+ }
|
||||||
|
Radical { "R"$[0-9]+ }
|
||||||
|
local { $[_a-zα-ω]$[a-zα-ω]* }
|
||||||
|
|
||||||
|
"(" ")"
|
||||||
|
"[" "]"
|
||||||
|
"{" "}"
|
||||||
|
|
||||||
|
@precedence { filter bigPr Global Radical ConstructPrefix }
|
||||||
|
@precedence { card bool debool red smallPr local }
|
||||||
|
@precedence { Integer space }
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFunction {
|
||||||
|
bigPr ComplexIndex |
|
||||||
|
smallPr ComplexIndex |
|
||||||
|
filter ComplexIndex |
|
||||||
|
card | bool | debool | red
|
||||||
|
}
|
||||||
|
|
||||||
|
Local { local Index? }
|
||||||
|
|
||||||
|
token {
|
||||||
|
TextFunction |
|
||||||
|
ConstructPrefix |
|
||||||
|
Integer |
|
||||||
|
Global |
|
||||||
|
Radical |
|
||||||
|
Local
|
||||||
|
}
|
||||||
|
|
||||||
|
@detectDelim
|
||||||
|
|
||||||
|
@external propSource highlighting from "./highlight.ts"
|
||||||
|
|
||||||
|
//@asciiLetter matches $[a-zA-Z]
|
||||||
|
//@asciiLowercase matches $[a-z]
|
||||||
|
//@asciiUppercase is equivalent to $[A-Z]
|
||||||
|
//@digit matches $[0-9]
|
||||||
|
//@whitespace matches any character the Unicode standard defines as whitespace.
|
||||||
|
//@eof matches the end of the input
|
|
@ -1,5 +0,0 @@
|
||||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
|
||||||
export const
|
|
||||||
Program = 1,
|
|
||||||
Identifier = 2,
|
|
||||||
String = 3
|
|
|
@ -1,22 +0,0 @@
|
||||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
|
||||||
import {LRParser} from "@lezer/lr"
|
|
||||||
import {highlighting} from "./highlight.ts"
|
|
||||||
export const parser = LRParser.deserialize({
|
|
||||||
version: 14,
|
|
||||||
states: "!WQVQPOOObQPO'#CbOOQO'#Cg'#CgOOQO'#Cc'#CcQVQPOOOOQO,58|,58|OpQPO,58|OOQO-E6a-E6aOOQO1G.h1G.h",
|
|
||||||
stateData: "!O~OYOS~OQQORQOTPO~OQQORQOSTOTPO~OQQORQOSWOTPO~O",
|
|
||||||
goto: "s[PPPPPP]cPPPmXQOPSUQSOQUPTVSUXROPSU",
|
|
||||||
nodeNames: "⚠ Program Identifier String ) ( Application",
|
|
||||||
maxTerm: 11,
|
|
||||||
nodeProps: [
|
|
||||||
["openedBy", 4,"("],
|
|
||||||
["closedBy", 5,")"]
|
|
||||||
],
|
|
||||||
propSources: [highlighting],
|
|
||||||
skippedNodes: [0],
|
|
||||||
repeatNodeCount: 1,
|
|
||||||
tokenData: "%[~RbX^!Zpq!Zrs#Oxy$lyz$q}!O$v!Q![$v!c!}$v#R#S$v#T#o$v#y#z!Z$f$g!Z#BY#BZ!Z$IS$I_!Z$I|$JO!Z$JT$JU!Z$KV$KW!Z&FU&FV!Z~!`YY~X^!Zpq!Z#y#z!Z$f$g!Z#BY#BZ!Z$IS$I_!Z$I|$JO!Z$JT$JU!Z$KV$KW!Z&FU&FV!Z~#RVOr#Ors#hs#O#O#O#P#m#P;'S#O;'S;=`$f<%lO#O~#mOR~~#pRO;'S#O;'S;=`#y;=`O#O~#|WOr#Ors#hs#O#O#O#P#m#P;'S#O;'S;=`$f;=`<%l#O<%lO#O~$iP;=`<%l#O~$qOT~~$vOS~~${TQ~}!O$v!Q![$v!c!}$v#R#S$v#T#o$v",
|
|
||||||
tokenizers: [0],
|
|
||||||
topRules: {"Program":[0,1]},
|
|
||||||
tokenPrec: 0
|
|
||||||
})
|
|
|
@ -19,11 +19,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor {
|
.cm-editor {
|
||||||
@apply border shadow rounded clr-border px-2
|
@apply border shadow rounded clr-border px-1
|
||||||
}
|
}
|
||||||
.cm-editor.cm-focused {
|
.cm-editor.cm-focused {
|
||||||
@apply border shadow rounded outline-2 outline
|
@apply border shadow rounded outline-2 outline
|
||||||
}
|
}
|
||||||
|
.cm-matchingBracket {
|
||||||
|
@apply font-semibold
|
||||||
|
}
|
||||||
|
.cm-nonmatchingBracket {
|
||||||
|
@apply font-bold text-red-500
|
||||||
|
}
|
||||||
|
|
||||||
@layer components {
|
@layer components {
|
||||||
h1 {
|
h1 {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||||
import Button from '../../components/Common/Button';
|
import Button from '../../components/Common/Button';
|
||||||
import Label from '../../components/Common/Label';
|
import Label from '../../components/Common/Label';
|
||||||
import { Loader } from '../../components/Common/Loader';
|
import { Loader } from '../../components/Common/Loader';
|
||||||
import RSInput from '../../components/RSInput/RSInput';
|
import RSInput from '../../components/RSInput';
|
||||||
import { getSymbolSubstitute, TextWrapper } from '../../components/RSInput/textEditing';
|
import { getSymbolSubstitute, TextWrapper } from '../../components/RSInput/textEditing';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import useCheckExpression from '../../hooks/useCheckExpression';
|
import useCheckExpression from '../../hooks/useCheckExpression';
|
||||||
|
|
Loading…
Reference in New Issue
Block a user