From c6d832d6d9ff3e0898451ef2e3ca1576d23f7722 Mon Sep 17 00:00:00 2001 From: Alf Eaton Date: Thu, 29 Jun 2023 09:53:43 +0100 Subject: [PATCH] Merge pull request #13594 from overleaf/ae-starred-caption [cm6] Allow an asterisk after CaptionCtrlSeq GitOrigin-RevId: e76e15afb676f6eddb8f09d710b0d90408584e29 --- .../extensions/visual/atomic-decorations.ts | 3 +- .../extensions/visual/mark-decorations.ts | 7 +- .../source-editor/lezer-latex/latex.grammar | 2 +- .../source-editor/lezer-latex/tokens.mjs | 1 + .../utils/tree-operations/environments.ts | 5 ++ .../codemirror-editor-visual-floats.spec.tsx | 72 +++++++++++++++++++ 6 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-floats.spec.tsx diff --git a/services/web/frontend/js/features/source-editor/extensions/visual/atomic-decorations.ts b/services/web/frontend/js/features/source-editor/extensions/visual/atomic-decorations.ts index 29223434d5..7437bce8d6 100644 --- a/services/web/frontend/js/features/source-editor/extensions/visual/atomic-decorations.ts +++ b/services/web/frontend/js/features/source-editor/extensions/visual/atomic-decorations.ts @@ -24,6 +24,7 @@ import { EndWidget } from './visual-widgets/end' import { getEnvironmentArguments, getEnvironmentName, + getUnstarredEnvironmentName, parseFigureData, } from '../../utils/tree-operations/environments' import { MathWidget } from './visual-widgets/math' @@ -224,7 +225,7 @@ export const atomicDecorations = (options: Options) => { enter(nodeRef) { if (nodeRef.type.is('$Environment')) { if (shouldDecorate(state, nodeRef)) { - const envName = getEnvironmentName(nodeRef.node, state) + const envName = getUnstarredEnvironmentName(nodeRef.node, state) const hideInEnvironmentTypes = ['figure', 'table'] if (envName && hideInEnvironmentTypes.includes(envName)) { const beginNode = nodeRef.node.getChild('BeginEnv') diff --git a/services/web/frontend/js/features/source-editor/extensions/visual/mark-decorations.ts b/services/web/frontend/js/features/source-editor/extensions/visual/mark-decorations.ts index dce01251da..2bd42c6b17 100644 --- a/services/web/frontend/js/features/source-editor/extensions/visual/mark-decorations.ts +++ b/services/web/frontend/js/features/source-editor/extensions/visual/mark-decorations.ts @@ -6,7 +6,7 @@ import { } from '@codemirror/view' import { EditorState, Range } from '@codemirror/state' import { syntaxTree } from '@codemirror/language' -import { getEnvironmentName } from '../../utils/tree-operations/environments' +import { getUnstarredEnvironmentName } from '../../utils/tree-operations/environments' import { centeringNodeForEnvironment } from '../../utils/tree-operations/figure' import { Tree } from '@lezer/common' @@ -103,7 +103,10 @@ export const markDecorations = ViewPlugin.define( } } } else if (nodeRef.type.is('$Environment')) { - const environmentName = getEnvironmentName(nodeRef.node, state) + const environmentName = getUnstarredEnvironmentName( + nodeRef.node, + state + ) switch (environmentName) { case 'abstract': diff --git a/services/web/frontend/js/features/source-editor/lezer-latex/latex.grammar b/services/web/frontend/js/features/source-editor/lezer-latex/latex.grammar index 03c44b75e6..efb5e2587f 100644 --- a/services/web/frontend/js/features/source-editor/lezer-latex/latex.grammar +++ b/services/web/frontend/js/features/source-editor/lezer-latex/latex.grammar @@ -250,7 +250,7 @@ KnownCommand { IncludeGraphicsArgument { FilePathArgument } } | Caption { - CaptionCtrlSeq optionalWhitespace? OptionalArgument? TextArgument + CaptionCtrlSeq "*"? optionalWhitespace? OptionalArgument? TextArgument } | Label { LabelCtrlSeq optionalWhitespace? LabelArgument diff --git a/services/web/frontend/js/features/source-editor/lezer-latex/tokens.mjs b/services/web/frontend/js/features/source-editor/lezer-latex/tokens.mjs index 29674c79c6..ef72e98ee2 100644 --- a/services/web/frontend/js/features/source-editor/lezer-latex/tokens.mjs +++ b/services/web/frontend/js/features/source-editor/lezer-latex/tokens.mjs @@ -739,6 +739,7 @@ const otherKnownEnvNames = { document: DocumentEnvName, tikzpicture: TikzPictureEnvName, figure: FigureEnvName, + 'figure*': FigureEnvName, subfigure: FigureEnvName, enumerate: ListEnvName, itemize: ListEnvName, diff --git a/services/web/frontend/js/features/source-editor/utils/tree-operations/environments.ts b/services/web/frontend/js/features/source-editor/utils/tree-operations/environments.ts index 7087486fd5..fe754ff66c 100644 --- a/services/web/frontend/js/features/source-editor/utils/tree-operations/environments.ts +++ b/services/web/frontend/js/features/source-editor/utils/tree-operations/environments.ts @@ -236,6 +236,11 @@ export function getEnvironmentName( return state.sliceDoc(nameNode.from, nameNode.to) } +export const getUnstarredEnvironmentName = ( + node: SyntaxNode | null, + state: EditorState +): string | undefined => getEnvironmentName(node, state)?.replace(/\*$/, '') + export function getEnvironmentArguments(environmentNode: SyntaxNode) { return environmentNode.getChild('BeginEnv')?.getChildren('TextArgument') } diff --git a/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-floats.spec.tsx b/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-floats.spec.tsx new file mode 100644 index 0000000000..0ac5428967 --- /dev/null +++ b/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-floats.spec.tsx @@ -0,0 +1,72 @@ +import { FC } from 'react' +import { EditorProviders } from '../../../helpers/editor-providers' +import CodemirrorEditor from '../../../../../frontend/js/features/source-editor/components/codemirror-editor' +import { mockScope } from '../helpers/mock-scope' + +const Container: FC = ({ children }) => ( +
{children}
+) + +const mountEditor = (content: string) => { + const scope = mockScope(content) + scope.editor.showVisual = true + + cy.mount( + + + + + + ) + + // wait for the content to be parsed and revealed + cy.get('.cm-content').should('have.css', 'opacity', '1') +} + +describe(' floats', function () { + beforeEach(function () { + window.metaAttributesCache.set('ol-preventCompileOnLoad', true) + cy.interceptEvents() + cy.interceptSpelling() + }) + + afterEach(function () { + window.metaAttributesCache.clear() + }) + + it('decorates a caption', function () { + mountEditor('\n\\caption{Foo}\n') + cy.get('.cm-line').eq(2).click() + cy.get('.cm-content').should('have.text', 'Foo') + }) + + it('decorates a caption in a figure', function () { + mountEditor('\\begin{figure}\n\\caption{Foo}\n\\end{figure}\n') + cy.get('.cm-line').eq(3).click() + cy.get('.cm-content').should('have.text', 'Foo') + }) + + it('decorates a caption in a table', function () { + mountEditor('\\begin{table}\n\\caption{Foo}\n\\end{table}\n') + cy.get('.cm-line').eq(3).click() + cy.get('.cm-content').should('have.text', 'Foo') + }) + + it('decorates a starred caption', function () { + mountEditor('\n\\caption*{Foo}\n') + cy.get('.cm-line').eq(2).click() + cy.get('.cm-content').should('have.text', 'Foo') + }) + + it('decorates a starred figure', function () { + mountEditor('\\begin{figure*}\n\\caption{Foo}\n\\end{figure*}\n') + cy.get('.cm-line').eq(3).click() + cy.get('.cm-content').should('have.text', 'Foo') + }) + + it('decorates a starred table', function () { + mountEditor('\\begin{table*}\n\\caption{Foo}\n\\end{table*}\n') + cy.get('.cm-line').eq(3).click() + cy.get('.cm-content').should('have.text', 'Foo') + }) +})