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 69d0958e8d..51f08865f1 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 @@ -616,14 +616,46 @@ export const atomicDecorations = (options: Options) => { return false // no markup in verbatim content } else if ( nodeRef.type.is('NewCommand') || - nodeRef.type.is('RenewCommand') + nodeRef.type.is('RenewCommand') || + nodeRef.type.is('Def') ) { - const argumentNode = nodeRef.node.getChild('LiteralArgContent') - if (argumentNode) { - const argument = state - .sliceDoc(argumentNode.from, argumentNode.to) - .trim() - if (/^\\\w+/.test(argument)) { + const nameNode = + nodeRef.node.getChild('LiteralArgContent') ?? + nodeRef.node.getChild('Csname') ?? + nodeRef.node.getChild('CtrlSym') + if (nameNode) { + const name = state.sliceDoc(nameNode.from, nameNode.to).trim() + if (/^\\\w+/.test(name)) { + const content = state.sliceDoc(nodeRef.from, nodeRef.to) + if (content) { + commandDefinitions += `${content}\n` + } + } + } + } else if ( + nodeRef.type.is('RenewEnvironment') || + nodeRef.type.is('NewEnvironment') + ) { + const nameNode = nodeRef.node.getChild('LiteralArgContent') + if (nameNode) { + const name = state.sliceDoc(nameNode.from, nameNode.to).trim() + if (/^\w+/.test(name)) { + const content = state.sliceDoc(nodeRef.from, nodeRef.to) + if (content) { + commandDefinitions += `${content}\n` + } + } + } + } else if (nodeRef.type.is('Let')) { + const commandNodes = nodeRef.node.getChildren('Csname') + if (commandNodes.length !== 2) { + return + } + const nameNode = commandNodes[0] + if (nameNode) { + // We support more flexible names in let (Csname) than in newcommand + const name = state.sliceDoc(nameNode.from, nameNode.to).trim() + if (name.length > 1 && name.startsWith('\\')) { const content = state.sliceDoc(nodeRef.from, nodeRef.to) if (content) { commandDefinitions += `${content}\n` diff --git a/services/web/frontend/js/features/source-editor/languages/latex/latex-language.ts b/services/web/frontend/js/features/source-editor/languages/latex/latex-language.ts index df899f9571..45c20ba6c1 100644 --- a/services/web/frontend/js/features/source-editor/languages/latex/latex-language.ts +++ b/services/web/frontend/js/features/source-editor/languages/latex/latex-language.ts @@ -129,7 +129,10 @@ export const LaTeXLanguage = LRLanguage.define({ ) { types.push('$TextArgument') } - } else if (type.name.endsWith('Environment')) { + } else if ( + type.name.endsWith('Environment') && + !['NewEnvironment', 'RenewEnvironment'].includes(type.name) + ) { types.push('$Environment') } else if (type.name.endsWith('Brace')) { types.push('$Brace') 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 95d9501da4..f4cad45736 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 @@ -60,6 +60,7 @@ IncludeGraphicsCtrlSeq, CaptionCtrlSeq, DefCtrlSeq, + LetCtrlSeq, LeftCtrlSeq, RightCtrlSeq, NewCommandCtrlSeq, @@ -293,6 +294,9 @@ KnownCommand { // allow more general Csname argument to \def commands, since other symbols such as '@' are often used in definitions DefCtrlSeq optionalWhitespace? (Csname | CtrlSym) optionalWhitespace? (MacroParameter | OptionalMacroParameter)* optionalWhitespace? DefinitionArgument } | + Let { + LetCtrlSeq Csname optionalWhitespace? "="? optionalWhitespace? Csname + } | Hbox { HboxCtrlSeq 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 ba0f168521..e46c43ac51 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 @@ -34,6 +34,7 @@ import { IncludeGraphicsCtrlSeq, CaptionCtrlSeq, DefCtrlSeq, + LetCtrlSeq, LeftCtrlSeq, RightCtrlSeq, NewCommandCtrlSeq, @@ -543,6 +544,7 @@ const otherKnowncommands = { '\\includegraphics': IncludeGraphicsCtrlSeq, '\\caption': CaptionCtrlSeq, '\\def': DefCtrlSeq, + '\\let': LetCtrlSeq, '\\left': LeftCtrlSeq, '\\right': RightCtrlSeq, '\\newcommand': NewCommandCtrlSeq,