Skip to content

Commit 8788d8a

Browse files
committed
feat: CodeMirror - add numbers/bool syntax highlighting for YAML mode
1 parent 0221a3b commit 8788d8a

File tree

6 files changed

+102
-18
lines changed

6 files changed

+102
-18
lines changed

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
"@codemirror/legacy-modes": "6.4.2",
104104
"@codemirror/lint": "6.8.4",
105105
"@codemirror/search": "6.5.8",
106+
"@lezer/highlight": "1.2.1",
106107
"@replit/codemirror-indentation-markers": "6.5.3",
107108
"@replit/codemirror-vscode-keymap": "6.0.2",
108109
"@types/react-dates": "^21.8.6",

src/Common/CodeMirror/CodeEditor.theme.ts

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,55 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { githubDarkInit, githubLightStyle, githubDarkStyle, githubLightInit } from '@uiw/codemirror-theme-github'
17+
import { EditorView } from '@uiw/react-codemirror'
18+
import { githubDarkInit, githubLightInit } from '@uiw/codemirror-theme-github'
19+
import { tags as t } from '@lezer/highlight'
1820

1921
export const getCodeEditorTheme = (isDark: boolean) => {
2022
const themeInit = isDark ? githubDarkInit : githubLightInit
21-
const styles = isDark ? githubDarkStyle : githubLightStyle
23+
const styles = isDark
24+
? [
25+
{ tag: [t.comment], color: '#8b949e' },
26+
{ tag: [t.variableName, t.attributeName], color: '#79c0ff' },
27+
{ tag: [t.string], color: '#a5d6ff' },
28+
{ tag: [t.number, t.bool], color: '#ffab70' },
29+
{ tag: [t.keyword], color: '#ff7b72' },
30+
{ tag: [t.operator, t.punctuation], color: '#8b949e' },
31+
{ tag: [t.meta], color: '#d2a8ff' },
32+
]
33+
: [
34+
{ tag: [t.comment], color: '#6a737d' },
35+
{ tag: [t.variableName, t.attributeName], color: '#005cc5' },
36+
{ tag: [t.string], color: '#032f62' },
37+
{ tag: [t.number, t.bool], color: '#e36209' },
38+
{ tag: [t.keyword], color: '#d73a49' },
39+
{ tag: [t.operator, t.punctuation], color: '#6a737d' },
40+
{ tag: [t.meta], color: '#6f42c1' },
41+
]
2242

23-
return themeInit({
24-
settings: {
25-
fontSize: '14px',
26-
fontFamily: 'Inconsolata, monospace',
27-
background: 'var(--bg-code-editor-base)',
28-
foreground: 'var(--N900)',
29-
caret: 'var(--N900)',
30-
gutterBackground: 'var(--bg-code-editor-base-gutter)',
31-
gutterForeground: 'var(--N900)',
32-
gutterBorder: 'transparent',
33-
lineHighlight: 'var(--active-line)',
34-
},
35-
styles,
36-
})
43+
return {
44+
themeExtension: EditorView.theme({
45+
'.cm-highlight-number': {
46+
color: isDark ? '#ffab70' : '#e36209',
47+
},
48+
'.cm-highlight-bool': {
49+
color: isDark ? '#ffab70' : '#e36209',
50+
},
51+
}),
52+
codeEditorTheme: themeInit({
53+
settings: {
54+
fontSize: '14px',
55+
fontFamily: 'Inconsolata, monospace',
56+
background: 'var(--bg-code-editor-base)',
57+
foreground: 'var(--N900)',
58+
caret: 'var(--N900)',
59+
gutterBackground: 'var(--bg-code-editor-base-gutter)',
60+
gutterForeground: 'var(--N900)',
61+
gutterBorder: 'transparent',
62+
lineHighlight: 'var(--active-line)',
63+
},
64+
styles,
65+
theme: isDark ? 'dark' : 'light',
66+
}),
67+
}
3768
}

src/Common/CodeMirror/CodeEditor.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { getUniqueId } from '@Shared/Helpers'
3434
import { cleanKubeManifest, useEffectAfterMount } from '@Common/Helper'
3535
import { DEFAULT_JSON_SCHEMA_URI, MODES } from '@Common/Constants'
3636

37-
import { codeEditorFindReplace, readOnlyTooltip } from './Extensions'
37+
import { codeEditorFindReplace, readOnlyTooltip, yamlHighlight } from './Extensions'
3838
import { CodeEditorContextProps, CodeEditorProps } from './types'
3939
import { CodeEditorReducer, initialState, parseValueToCode } from './CodeEditor.reducer'
4040
import { getFoldGutterElement, getLanguageExtension, getValidationSchema } from './utils'
@@ -89,7 +89,7 @@ const CodeEditor = <DiffView extends boolean = false>({
8989

9090
// CONSTANTS
9191
const isDarkTheme = (theme ?? appTheme) === AppThemeType.dark
92-
const codeEditorTheme = getCodeEditorTheme(isDarkTheme)
92+
const { codeEditorTheme, themeExtension } = getCodeEditorTheme(isDarkTheme)
9393

9494
// STATES
9595
const [codemirrorMergeKey, setCodemirrorMergeKey] = useState<string>()
@@ -196,6 +196,7 @@ const CodeEditor = <DiffView extends boolean = false>({
196196

197197
const baseExtensions: Extension[] = [
198198
basicSetup(basicSetupOptions),
199+
themeExtension,
199200
keymap.of(vscodeKeymap.filter(({ key }) => !disableSearch || key !== 'Mod-f')),
200201
indentationMarkers(),
201202
getLanguageExtension(mode),
@@ -204,6 +205,7 @@ const CodeEditor = <DiffView extends boolean = false>({
204205
search({
205206
createPanel: codeEditorFindReplace,
206207
}),
208+
...(mode === MODES.YAML ? [yamlHighlight] : []),
207209
]
208210

209211
const extensions: Extension[] = [

src/Common/CodeMirror/Extensions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@
1717
export * from './yamlParseLinter'
1818
export * from './readOnlyTooltip'
1919
export * from './findAndReplace'
20+
export * from './yamlHighlight'
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Decoration, DecorationSet, EditorState, EditorView, Extension, Range, StateField } from '@uiw/react-codemirror'
2+
import { ensureSyntaxTree } from '@codemirror/language'
3+
4+
const isBool = (value: string) => /^(true|false)$/i.test(value)
5+
6+
const isNumberOrBool = (value: string) => isBool(value) || /^[-+]?\d*\.\d+$/.test(value) || /^[-+]?\d+$/.test(value)
7+
8+
const calculateDecorations = (state: EditorState) => {
9+
const decorations: Range<Decoration>[] = []
10+
const tree = ensureSyntaxTree(state, state.doc.length)
11+
tree.iterate({
12+
from: 0,
13+
to: state.doc.length,
14+
enter: (node) => {
15+
if (node.name === 'Pair') {
16+
const valueNode = node.node.getChild('Literal')
17+
if (valueNode) {
18+
const valueText = state.sliceDoc(valueNode.from, valueNode.to).trim()
19+
if (isNumberOrBool(valueText)) {
20+
decorations.push(
21+
Decoration.mark({
22+
class: isBool(valueText) ? 'cm-highlight-bool' : 'cm-highlight-number',
23+
}).range(valueNode.from, valueNode.to),
24+
)
25+
}
26+
}
27+
}
28+
},
29+
})
30+
31+
return Decoration.set(decorations)
32+
}
33+
34+
const highlightDecorationField = StateField.define<DecorationSet>({
35+
create(state) {
36+
return calculateDecorations(state)
37+
},
38+
update(decorations, tr) {
39+
let updatedDecorations = decorations.map(tr.changes)
40+
if (tr.docChanged) {
41+
updatedDecorations = calculateDecorations(tr.state)
42+
}
43+
return updatedDecorations
44+
},
45+
provide: (field) => EditorView.decorations.from(field),
46+
})
47+
48+
export const yamlHighlight: Extension = highlightDecorationField

0 commit comments

Comments
 (0)