blob: 64b998cd6b7fbb4905c930235e2d0cda6610f1d6 [file] [log] [blame]
/**
* @license
* Copyright 2023 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {
EditorView,
keymap,
highlightSpecialChars,
drawSelection,
lineNumbers,
highlightActiveLineGutter,
highlightWhitespace,
highlightTrailingWhitespace,
} from '@codemirror/view';
import {EditorState, Extension} from '@codemirror/state';
import {
indentOnInput,
foldGutter,
foldKeymap,
bracketMatching,
syntaxHighlighting,
indentUnit,
HighlightStyle,
} from '@codemirror/language';
import {
defaultKeymap,
history,
historyKeymap,
indentWithTab,
} from '@codemirror/commands';
import {searchKeymap, highlightSelectionMatches} from '@codemirror/search';
import {closeBrackets, closeBracketsKeymap} from '@codemirror/autocomplete';
import {rulerPlugin} from './ruler';
import {language} from './language';
import {EditPreferencesInfo} from './codemirror-element';
import {tags as t} from '@lezer/highlight';
const colorTheme = (dark: boolean) =>
EditorView.theme(
{
'&': {
color: 'var(--primary-text-color)',
'background-color': 'var(--background-color-primary)',
},
'.cm-gutters': {
color: 'var(--deemphasized-text-color)',
'background-color': 'var(--background-color-secondary)',
},
},
{dark}
);
const trailingspace = () =>
EditorView.theme({
'.cm-trailingspace': {
'background-color': '#fce8e6',
border: '1px solid #c5221f',
'border-radius': '2px',
},
});
const fixedHeightEditor = (height: number) =>
EditorView.theme({
'&': {height: `${height}px`},
'.cm-scroller': {overflow: 'auto'},
});
const hideTabsAndSpaces = () =>
EditorView.theme({
// Class is created and used by highlightWhitespace()
// This hides tabs unless show_tabs is enabled in prefs.
'.cm-highlightTab': {
'background-image': 'none',
'background-size': 'none',
'background-position': 'none',
'background-repeat': 'none',
display: 'inline-block',
'text-decoration': 'inherit',
},
'.cm-highlightSpace': {
'background-image': 'none',
'background-size': 'none',
},
});
const tabTheme = () =>
EditorView.theme({
'.cm-tab:before': {
color: '#5f6368',
content: "'\\2192'",
position: 'absolute',
},
// Class is created and used by highlightWhitespace()
'.cm-highlightTab:before': {
color: '#5f6368',
content: "'\\2192'",
position: 'absolute',
},
});
export const gerritTheme = HighlightStyle.define([
{tag: [t.standard(t.tagName), t.tagName], color: 'var(--syntax-tag-color)'},
{tag: t.comment, color: 'var(--syntax-comment-color)'},
{tag: [t.className, t.propertyName], color: 'var(--syntax-attr-color)'},
{tag: t.variableName, color: 'var(--syntax-variable-color)'},
{tag: [t.attributeName, t.operator], color: 'var(--syntax-attr-color)'},
{tag: t.number, color: 'var(--syntax-number-color)'},
{tag: t.keyword, color: 'var(--syntax-keyword-color)'},
{tag: t.typeName, color: 'var(--syntax-type-color)'},
{tag: t.typeOperator, color: 'blue'},
{tag: t.string, color: 'var(--syntax-string-color)'},
{tag: t.regexp, color: 'var(--syntax-regexp-color)'},
{tag: t.meta, color: 'var(--syntax-meta-color)'},
{tag: [t.name, t.quote], color: 'var(--syntax-title-color)'},
{tag: t.definition(t.variableName), color: '#00f'},
{tag: t.local(t.variableName), color: '#30a'},
{tag: t.definition(t.propertyName), color: '#00c'},
{
tag: [t.heading, t.strong],
color: 'var(--syntax-strong-color)',
fontWeight: 'bold',
},
{tag: t.emphasis, fontStyle: 'italic'},
{tag: t.deleted, color: '#a11'},
{tag: t.inserted, color: '#164'},
{tag: t.literal, color: 'var(--syntax-literal-color)'},
{
tag: [t.atom, t.bool, t.special(t.variableName)],
color: 'var(--syntax-literal-color)',
},
{tag: [t.url, t.escape, t.link], color: 'var(--syntax-string-color)'},
{tag: t.special(t.string), color: '#e40'},
{tag: t.link, textDecoration: 'underline'},
{tag: t.strikethrough, textDecoration: 'line-through'},
{tag: t.invalid, color: '#f00'},
]);
export const extensions = (
height: number,
prefs?: EditPreferencesInfo,
fileType?: string,
fileContent?: string,
darkMode?: boolean
) => {
const codeExtensions: Array<Extension> = [
lineNumbers(),
highlightActiveLineGutter(),
highlightSpecialChars(),
history(),
foldGutter(),
drawSelection(),
EditorState.allowMultipleSelections.of(true),
indentOnInput(),
highlightSelectionMatches(),
keymap.of([
...closeBracketsKeymap,
...defaultKeymap,
...searchKeymap,
...historyKeymap,
...foldKeymap,
indentWithTab,
]),
trailingspace(),
fixedHeightEditor(height),
colorTheme(darkMode ?? false),
EditorState.tabSize.of(prefs?.tab_size ?? 0),
highlightWhitespace(),
hideTabsAndSpaces(),
];
if (!prefs) return codeExtensions;
if (prefs.line_length) {
codeExtensions.push(rulerPlugin);
}
if (prefs.auto_close_brackets) {
codeExtensions.push(closeBrackets());
}
if (prefs.line_wrapping) {
codeExtensions.push(EditorView.lineWrapping);
}
if (prefs.match_brackets) {
codeExtensions.push(bracketMatching());
}
if (prefs.syntax_highlighting && language(fileType)) {
codeExtensions.push(
language(fileType) as Extension,
syntaxHighlighting(gerritTheme, {fallback: true})
);
}
if (prefs.show_tabs) {
codeExtensions.push(tabTheme());
}
if (prefs.indent_with_tabs) {
codeExtensions.push(indentUnit.of('\t'));
} else {
codeExtensions.push(indentUnit.of(' '.repeat(prefs.indent_unit ?? 0)));
}
if (prefs.show_whitespace_errors) {
codeExtensions.push(highlightTrailingWhitespace());
}
if (fileContent?.includes('\r\n')) {
codeExtensions.push(EditorState.lineSeparator.of('\r\n'));
}
return codeExtensions;
};