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 d852c9d77c..03b9aea758 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 @@ -1066,6 +1066,44 @@ export const atomicDecorations = (options: Options) => { ) ) } + } else if ( + nodeRef.type.is('FootnoteCommand') || + nodeRef.type.is('EndnoteCommand') + ) { + const textArgumentNode = nodeRef.node.getChild('TextArgument') + if (textArgumentNode) { + if ( + state.readOnly && + selectionIntersects(state.selection, nodeRef) + ) { + // a special case for a read-only document: + // always display the content, styled differently from the main content. + decorations.push( + ...decorateArgumentBraces( + new BraceWidget(), + textArgumentNode, + nodeRef.from + ), + Decoration.mark({ + class: 'ol-cm-footnote ol-cm-footnote-view', + }).range(textArgumentNode.from, textArgumentNode.to) + ) + } else { + if (shouldDecorate(state, nodeRef)) { + // collapse the footnote when the selection is outside it + decorations.push( + Decoration.replace({ + widget: new FootnoteWidget( + nodeRef.type.is('FootnoteCommand') + ? 'footnote' + : 'endnote' + ), + }).range(nodeRef.from, nodeRef.to) + ) + return false + } + } + } } else if (nodeRef.type.is('UnknownCommand')) { // a command that's not defined separately by the grammar const commandNode = nodeRef.node @@ -1091,43 +1129,6 @@ export const atomicDecorations = (options: Options) => { ) return false } - } else if ( - commandName === '\\footnote' || - commandName === '\\endnote' - ) { - if (textArgumentNode) { - if ( - state.readOnly && - selectionIntersects(state.selection, nodeRef) - ) { - // a special case for a read-only document: - // always display the content, styled differently from the main content. - decorations.push( - ...decorateArgumentBraces( - new BraceWidget(), - textArgumentNode, - nodeRef.from - ), - Decoration.mark({ - class: 'ol-cm-footnote ol-cm-footnote-view', - }).range(textArgumentNode.from, textArgumentNode.to) - ) - } else { - if (shouldDecorate(state, nodeRef)) { - // collapse the footnote when the selection is outside it - decorations.push( - Decoration.replace({ - widget: new FootnoteWidget( - commandName === '\\footnote' - ? 'footnote' - : 'endnote' - ), - }).range(nodeRef.from, nodeRef.to) - ) - return false - } - } - } } else if (commandName === '\\LaTeX') { if (shouldDecorate(state, nodeRef)) { decorations.push( 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 8f9080807c..fb8657a48c 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 @@ -108,7 +108,9 @@ TextStrikeOutCtrlSeq, EmphasisCtrlSeq, UnderlineCtrlSeq, - SetLengthCtrlSeq + SetLengthCtrlSeq, + FootnoteCtrlSeq, + EndnoteCtrlSeq } @external specialize {EnvName} specializeEnvName from "./tokens.mjs" { @@ -410,6 +412,12 @@ KnownCommand { } | SetLengthCommand { SetLengthCtrlSeq optionalWhitespace? ShortTextArgument optionalWhitespace? ShortTextArgument + } | + FootnoteCommand { + FootnoteCtrlSeq optionalWhitespace? OptionalArgument? optionalWhitespace? TextArgument + } | + EndnoteCommand { + EndnoteCtrlSeq optionalWhitespace? OptionalArgument? optionalWhitespace? TextArgument } } 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 933f2fc9a9..bf6ef18e15 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 @@ -99,6 +99,8 @@ import { EmphasisCtrlSeq, UnderlineCtrlSeq, SetLengthCtrlSeq, + FootnoteCtrlSeq, + EndnoteCtrlSeq, } from './latex.terms.mjs' const MAX_ARGUMENT_LOOKAHEAD = 100 @@ -605,6 +607,8 @@ const otherKnowncommands = { '\\emph': EmphasisCtrlSeq, '\\underline': UnderlineCtrlSeq, '\\setlength': SetLengthCtrlSeq, + '\\footnote': FootnoteCtrlSeq, + '\\endnote': EndnoteCtrlSeq, } // specializer for control sequences // return new tokens for specific control sequences