mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-18 09:35:20 +02:00
235 lines
5.9 KiB
TypeScript
235 lines
5.9 KiB
TypeScript
//
|
|
// Create a set of Extensions that provide syntax highlighting.
|
|
//
|
|
|
|
|
|
import { defaultHighlightStyle, syntaxHighlighting, HighlightStyle } from '@codemirror/language';
|
|
import { tags } from '@lezer/highlight';
|
|
import { EditorView } from '@codemirror/view';
|
|
import { Extension } from '@codemirror/state';
|
|
|
|
import { inlineMathTag, mathTag } from './markdownMathParser';
|
|
|
|
// For an example on how to customize the theme, see:
|
|
//
|
|
// https://github.com/codemirror/theme-one-dark/blob/main/src/one-dark.ts
|
|
//
|
|
// For a tutorial, see:
|
|
//
|
|
// https://codemirror.net/6/examples/styling/#themes
|
|
//
|
|
// Use Safari developer tools to view the content of the CodeMirror iframe while
|
|
// the app is running. It seems that what appears as ".ͼ1" in the CSS is the
|
|
// equivalent of "&" in the theme object. So to target ".ͼ1.cm-focused", you'd
|
|
// use '&.cm-focused' in the theme.
|
|
//
|
|
// [theme] should be a joplin theme (see @joplin/lib/theme)
|
|
const createTheme = (theme: any): Extension[] => {
|
|
const isDarkTheme = theme.appearance === 'dark';
|
|
|
|
const baseGlobalStyle: Record<string, string> = {
|
|
color: theme.color,
|
|
backgroundColor: theme.backgroundColor,
|
|
|
|
// On iOS, apply system font scaling (e.g. font scaling
|
|
// set in accessibility settings).
|
|
font: '-apple-system-body',
|
|
};
|
|
const baseCursorStyle: Record<string, string> = { };
|
|
const baseContentStyle: Record<string, string> = {
|
|
fontFamily: theme.fontFamily,
|
|
|
|
// To allow accessibility font scaling, we also need to set the
|
|
// fontSize to a value in `em`s (relative scaling relative to
|
|
// parent font size).
|
|
fontSize: `${theme.fontSize}em`,
|
|
};
|
|
const baseSelectionStyle: Record<string, string> = { };
|
|
const blurredSelectionStyle: Record<string, string> = { };
|
|
|
|
// If we're in dark mode, the caret and selection are difficult to see.
|
|
// Adjust them appropriately
|
|
if (isDarkTheme) {
|
|
// Styling the caret requires styling both the caret itself
|
|
// and the CodeMirror caret.
|
|
// See https://codemirror.net/6/examples/styling/#themes
|
|
baseContentStyle.caretColor = 'white';
|
|
baseCursorStyle.borderLeftColor = 'white';
|
|
|
|
baseSelectionStyle.backgroundColor = '#6b6b6b';
|
|
blurredSelectionStyle.backgroundColor = '#444';
|
|
}
|
|
|
|
const baseTheme = EditorView.baseTheme({
|
|
'&': baseGlobalStyle,
|
|
|
|
// These must be !important or more specific than CodeMirror's built-ins
|
|
'.cm-content': {
|
|
fontFamily: theme.fontFamily,
|
|
...baseContentStyle,
|
|
},
|
|
'&.cm-focused .cm-cursor': baseCursorStyle,
|
|
'&.cm-focused .cm-selectionBackground, ::selection': baseSelectionStyle,
|
|
'.cm-selectionBackground': blurredSelectionStyle,
|
|
|
|
'&.cm-focused': {
|
|
outline: 'none',
|
|
},
|
|
|
|
'& .cm-blockQuote': {
|
|
borderLeft: `4px solid ${theme.colorFaded}`,
|
|
opacity: theme.blockQuoteOpacity,
|
|
paddingLeft: '4px',
|
|
},
|
|
|
|
'& .cm-codeBlock': {
|
|
'&.cm-regionFirstLine, &.cm-regionLastLine': {
|
|
borderRadius: '3px',
|
|
},
|
|
'&:not(.cm-regionFirstLine)': {
|
|
borderTop: 'none',
|
|
borderTopLeftRadius: 0,
|
|
borderTopRightRadius: 0,
|
|
},
|
|
'&:not(.cm-regionLastLine)': {
|
|
borderBottom: 'none',
|
|
borderBottomLeftRadius: 0,
|
|
borderBottomRightRadius: 0,
|
|
},
|
|
|
|
borderWidth: '1px',
|
|
borderStyle: 'solid',
|
|
borderColor: theme.colorFaded,
|
|
backgroundColor: 'rgba(155, 155, 155, 0.1)',
|
|
},
|
|
|
|
// CodeMirror wraps the existing inline span in an additional element.
|
|
// Due to a Chrome rendering bug, because the .cm-inlineCode wraps a
|
|
// span with a larger font-size, the .cm-inlineCode's bounding box won't
|
|
// be big enough for its content.
|
|
// As such, we need to style whichever element directly wraps its content.
|
|
'& .cm-headerLine > .cm-inlineCode > *, & :not(.cm-headerLine) > .cm-inlineCode': {
|
|
borderWidth: '1px',
|
|
borderStyle: 'solid',
|
|
borderColor: isDarkTheme ? 'rgba(200, 200, 200, 0.5)' : 'rgba(100, 100, 100, 0.5)',
|
|
borderRadius: '4px',
|
|
},
|
|
|
|
'& .cm-mathBlock, & .cm-inlineMath': {
|
|
color: isDarkTheme ? '#9fa' : '#276',
|
|
},
|
|
|
|
|
|
// Style the search widget. Use ':root' to increase the selector's precedence
|
|
// (override the existing preset styles).
|
|
':root & .cm-panel.cm-search': {
|
|
'& label, & button, & input': {
|
|
fontSize: '1em',
|
|
color: isDarkTheme ? 'white' : 'black',
|
|
},
|
|
},
|
|
});
|
|
|
|
const appearanceTheme = EditorView.theme({}, { dark: isDarkTheme });
|
|
|
|
const baseHeadingStyle = {
|
|
fontWeight: 'bold',
|
|
fontFamily: theme.fontFamily,
|
|
};
|
|
|
|
const highlightingStyle = HighlightStyle.define([
|
|
{
|
|
tag: tags.strong,
|
|
fontWeight: 'bold',
|
|
},
|
|
{
|
|
tag: tags.emphasis,
|
|
fontStyle: 'italic',
|
|
},
|
|
{
|
|
...baseHeadingStyle,
|
|
tag: tags.heading1,
|
|
fontSize: '1.6em',
|
|
borderBottom: `1px solid ${theme.dividerColor}`,
|
|
},
|
|
{
|
|
...baseHeadingStyle,
|
|
tag: tags.heading2,
|
|
fontSize: '1.4em',
|
|
},
|
|
{
|
|
...baseHeadingStyle,
|
|
tag: tags.heading3,
|
|
fontSize: '1.3em',
|
|
},
|
|
{
|
|
...baseHeadingStyle,
|
|
tag: tags.heading4,
|
|
fontSize: '1.2em',
|
|
},
|
|
{
|
|
...baseHeadingStyle,
|
|
tag: tags.heading5,
|
|
fontSize: '1.1em',
|
|
},
|
|
{
|
|
...baseHeadingStyle,
|
|
tag: tags.heading6,
|
|
fontSize: '1.0em',
|
|
},
|
|
{
|
|
tag: tags.list,
|
|
fontFamily: theme.fontFamily,
|
|
},
|
|
{
|
|
tag: tags.comment,
|
|
opacity: 0.9,
|
|
fontStyle: 'italic',
|
|
},
|
|
{
|
|
tag: tags.link,
|
|
color: theme.urlColor,
|
|
textDecoration: 'underline',
|
|
},
|
|
{
|
|
tag: [mathTag, inlineMathTag],
|
|
fontStyle: 'italic',
|
|
},
|
|
|
|
// Content of code blocks
|
|
{
|
|
tag: tags.keyword,
|
|
color: isDarkTheme ? '#ff7' : '#740',
|
|
},
|
|
{
|
|
tag: tags.operator,
|
|
color: isDarkTheme ? '#f7f' : '#805',
|
|
},
|
|
{
|
|
tag: tags.literal,
|
|
color: isDarkTheme ? '#aaf' : '#037',
|
|
},
|
|
{
|
|
tag: tags.operator,
|
|
color: isDarkTheme ? '#fa9' : '#490',
|
|
},
|
|
{
|
|
tag: tags.typeName,
|
|
color: isDarkTheme ? '#7ff' : '#a00',
|
|
},
|
|
]);
|
|
|
|
return [
|
|
baseTheme,
|
|
appearanceTheme,
|
|
syntaxHighlighting(highlightingStyle),
|
|
|
|
// If we haven't defined highlighting for tags, fall back
|
|
// to the default.
|
|
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
|
];
|
|
};
|
|
|
|
|
|
export default createTheme;
|