mirror of
https://github.com/overleaf/overleaf.git
synced 2024-09-16 02:52:31 -04:00
[visual] Apply style to the content of color commands (#13726)
GitOrigin-RevId: 4ae8b745618e91b487d17c357cdb0e697038b3a3
This commit is contained in:
parent
ca9593e74c
commit
08c82a24a9
6 changed files with 148 additions and 2 deletions
|
@ -46,7 +46,12 @@ import { InlineGraphicsWidget } from './visual-widgets/inline-graphics'
|
|||
import getMeta from '../../../../utils/meta'
|
||||
import { EditableGraphicsWidget } from './visual-widgets/editable-graphics'
|
||||
import { EditableInlineGraphicsWidget } from './visual-widgets/editable-inline-graphics'
|
||||
import { CloseBrace, OpenBrace } from '../../lezer-latex/latex.terms.mjs'
|
||||
import {
|
||||
CloseBrace,
|
||||
OpenBrace,
|
||||
ShortTextArgument,
|
||||
TextArgument,
|
||||
} from '../../lezer-latex/latex.terms.mjs'
|
||||
import { FootnoteWidget } from './visual-widgets/footnote'
|
||||
import { getListItems } from '../toolbar/lists'
|
||||
import { TildeWidget } from './visual-widgets/tilde'
|
||||
|
@ -868,6 +873,24 @@ export const atomicDecorations = (options: Options) => {
|
|||
const { name, label } = result
|
||||
theoremEnvironments.set(name, label)
|
||||
}
|
||||
} else if (
|
||||
nodeRef.type.is('TextColorCommand') ||
|
||||
nodeRef.type.is('ColorBoxCommand')
|
||||
) {
|
||||
if (shouldDecorate(state, nodeRef)) {
|
||||
const colorArgumentNode = nodeRef.node.getChild(ShortTextArgument)
|
||||
const contentArgumentNode = nodeRef.node.getChild(TextArgument)
|
||||
if (colorArgumentNode && contentArgumentNode) {
|
||||
// command name and opening brace
|
||||
decorations.push(
|
||||
...decorateArgumentBraces(
|
||||
new BraceWidget(),
|
||||
contentArgumentNode,
|
||||
nodeRef.from
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (nodeRef.type.is('UnknownCommand')) {
|
||||
// a command that's not defined separately by the grammar
|
||||
const commandNode = nodeRef.node
|
||||
|
|
|
@ -10,6 +10,7 @@ import { getUnstarredEnvironmentName } from '../../utils/tree-operations/environ
|
|||
import { centeringNodeForEnvironment } from '../../utils/tree-operations/figure'
|
||||
import { parseTheoremStyles } from '../../utils/tree-operations/theorems'
|
||||
import { Tree } from '@lezer/common'
|
||||
import { parseColorArguments } from '../../utils/tree-operations/colors'
|
||||
|
||||
/**
|
||||
* A view plugin that decorates ranges of text with Mark decorations.
|
||||
|
@ -105,6 +106,40 @@ export const markDecorations = ViewPlugin.define(
|
|||
)
|
||||
}
|
||||
}
|
||||
} else if (nodeRef.type.is('TextColorCommand')) {
|
||||
const result = parseColorArguments(state, nodeRef.node)
|
||||
|
||||
if (result) {
|
||||
const { color, from, to } = result
|
||||
|
||||
// decorate the content
|
||||
decorations.push(
|
||||
Decoration.mark({
|
||||
class: 'ol-cm-textcolor',
|
||||
inclusive: true,
|
||||
attributes: {
|
||||
style: `color: ${color}`,
|
||||
},
|
||||
}).range(from, to)
|
||||
)
|
||||
}
|
||||
} else if (nodeRef.type.is('ColorBoxCommand')) {
|
||||
const result = parseColorArguments(state, nodeRef.node)
|
||||
|
||||
if (result) {
|
||||
const { color, from, to } = result
|
||||
|
||||
// decorate the content
|
||||
decorations.push(
|
||||
Decoration.mark({
|
||||
class: 'ol-cm-colorbox',
|
||||
inclusive: true,
|
||||
attributes: {
|
||||
style: `background-color: ${color}`,
|
||||
},
|
||||
}).range(from, to)
|
||||
)
|
||||
}
|
||||
} else if (nodeRef.type.is('$Environment')) {
|
||||
const environmentName = getUnstarredEnvironmentName(
|
||||
nodeRef.node,
|
||||
|
|
|
@ -87,7 +87,9 @@
|
|||
BibliographyCtrlSeq,
|
||||
BibliographyStyleCtrlSeq,
|
||||
AuthorCtrlSeq,
|
||||
MaketitleCtrlSeq
|
||||
MaketitleCtrlSeq,
|
||||
TextColorCtrlSeq,
|
||||
ColorBoxCtrlSeq
|
||||
}
|
||||
|
||||
@external specialize {EnvName} specializeEnvName from "./tokens.mjs" {
|
||||
|
@ -239,6 +241,12 @@ KnownCommand {
|
|||
UsePackageCtrlSeq optionalWhitespace? OptionalArgument?
|
||||
PackageArgument
|
||||
} |
|
||||
TextColorCommand {
|
||||
TextColorCtrlSeq optionalWhitespace? ShortTextArgument optionalWhitespace? TextArgument
|
||||
} |
|
||||
ColorBoxCommand {
|
||||
ColorBoxCtrlSeq optionalWhitespace? ShortTextArgument optionalWhitespace? TextArgument
|
||||
} |
|
||||
HrefCommand {
|
||||
HrefCtrlSeq optionalWhitespace? UrlArgument ShortTextArgument
|
||||
} |
|
||||
|
|
|
@ -68,6 +68,8 @@ import {
|
|||
CenteringCtrlSeq,
|
||||
ListEnvName,
|
||||
MaketitleCtrlSeq,
|
||||
TextColorCtrlSeq,
|
||||
ColorBoxCtrlSeq,
|
||||
} from './latex.terms.mjs'
|
||||
|
||||
function nameChar(ch) {
|
||||
|
@ -639,6 +641,8 @@ const otherKnowncommands = {
|
|||
'\\bibliography': BibliographyCtrlSeq,
|
||||
'\\bibliographystyle': BibliographyStyleCtrlSeq,
|
||||
'\\maketitle': MaketitleCtrlSeq,
|
||||
'\\textcolor': TextColorCtrlSeq,
|
||||
'\\colorbox': ColorBoxCtrlSeq,
|
||||
}
|
||||
// specializer for control sequences
|
||||
// return new tokens for specific control sequences
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import { EditorState } from '@codemirror/state'
|
||||
import { SyntaxNode } from '@lezer/common'
|
||||
import {
|
||||
LongArg,
|
||||
ShortArg,
|
||||
ShortTextArgument,
|
||||
TextArgument,
|
||||
} from '../../lezer-latex/latex.terms.mjs'
|
||||
|
||||
// basic color definitions from the xcolor package
|
||||
// https://github.com/latex3/xcolor/blob/849682246582946835d28c8f9b2081ff2c340e09/xcolor.dtx#L7051-L7093
|
||||
const colors = new Map<string, string>([
|
||||
['red', 'rgb(255,0,0)'],
|
||||
['green', 'rgb(0,255,0)'],
|
||||
['blue', 'rgb(0,0,255)'],
|
||||
['brown', 'rgb(195,127,63)'],
|
||||
['lime', 'rgb(195,255,0)'],
|
||||
['orange', 'rgb(255,127,0)'],
|
||||
['pink', 'rgb(255,195,195)'],
|
||||
['purple', 'rgb(195,0,63)'],
|
||||
['teal', 'rgb(0,127,127)'],
|
||||
['violet', 'rgb(127,0,127)'],
|
||||
['cyan', 'rgb(0,255,255)'],
|
||||
['magenta', 'rgb(255,0,255)'],
|
||||
['yellow', 'rgb(255,255,0)'],
|
||||
['olive', 'rgb(127,127,0)'],
|
||||
['black', 'rgb(0,0,0)'],
|
||||
['darkgray', 'rgb(63,63,63)'],
|
||||
['gray', 'rgb(127,127,127)'],
|
||||
['lightgray', 'rgb(195,195,195)'],
|
||||
['white', 'rgb(255,255,255)'],
|
||||
])
|
||||
|
||||
export const parseColorArguments = (
|
||||
state: EditorState,
|
||||
node: SyntaxNode
|
||||
): { color: string; from: number; to: number } | undefined => {
|
||||
const colorArgumentNode = node.getChild(ShortTextArgument)?.getChild(ShortArg)
|
||||
const contentArgumentNode = node.getChild(TextArgument)?.getChild(LongArg)
|
||||
|
||||
if (colorArgumentNode && contentArgumentNode) {
|
||||
const { from, to } = contentArgumentNode
|
||||
|
||||
if (to > from) {
|
||||
const colorName = state
|
||||
.sliceDoc(colorArgumentNode.from, colorArgumentNode.to)
|
||||
.trim()
|
||||
|
||||
if (colorName) {
|
||||
const color = colors.get(colorName)
|
||||
|
||||
if (color) {
|
||||
return { color, from, to }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -566,6 +566,24 @@ describe('<CodeMirrorEditor/> in Visual mode', function () {
|
|||
)
|
||||
})
|
||||
|
||||
describe('decorates color commands', function () {
|
||||
it('decorates textcolor', function () {
|
||||
cy.get('@first-line').type('\\textcolor{{}red}{{}foo}')
|
||||
cy.get('.ol-cm-textcolor')
|
||||
.should('have.length', 1)
|
||||
.should('have.text', 'foo')
|
||||
.should('have.attr', 'style', 'color: rgb(255,0,0)')
|
||||
})
|
||||
|
||||
it('decorates colorbox', function () {
|
||||
cy.get('@first-line').type('\\colorbox{{}yellow}{{}foo}')
|
||||
cy.get('.ol-cm-colorbox')
|
||||
.should('have.length', 1)
|
||||
.should('have.text', 'foo')
|
||||
.should('have.attr', 'style', 'background-color: rgb(255,255,0)')
|
||||
})
|
||||
})
|
||||
|
||||
describe('handling of special characters', function () {
|
||||
it('decorates a tilde with a non-breaking space', function () {
|
||||
cy.get('@first-line').type('Test~test')
|
||||
|
|
Loading…
Reference in a new issue