From c0d6a9c5f05e2969b565f179ca512d573587e0a4 Mon Sep 17 00:00:00 2001 From: Alf Eaton Date: Fri, 25 Oct 2024 12:32:06 +0100 Subject: [PATCH] Use `noSpellCheckProp` NodeProp to exclude node types from spell check (#21335) GitOrigin-RevId: 91ba72285b0f01c3c00fdb1a64c30e9ff67f72f5 --- .../languages/latex/latex-language.ts | 19 +++++++++++++++++++ .../source-editor/lezer-latex/latex.grammar | 4 ++++ .../source-editor/lezer-latex/tokens.mjs | 2 ++ .../source-editor/utils/node-props.ts | 9 +++++++++ .../utils/tree-operations/text.ts | 19 ++++++------------- 5 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 services/web/frontend/js/features/source-editor/utils/node-props.ts 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 a7ef5a6c72..51fe607a56 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 @@ -10,6 +10,7 @@ import { getFoldRange, } from '../../utils/tree-query' import { closeBracketConfig } from './close-bracket-config' +import { noSpellCheckProp } from '@/features/source-editor/utils/node-props' const styleOverrides: Record = { DocumentClassCtrlSeq: t.keyword, @@ -118,6 +119,24 @@ export const LaTeXLanguage = LRLanguage.define({ return content }, }), + // disable spell check in these node types when they're inside these parents (empty string = any parent) + noSpellCheckProp.add({ + BibKeyArgument: [['']], + BibliographyArgument: [['']], + BibliographyStyleArgument: [['']], + DocumentClassArgument: [['']], + LabelArgument: [['']], + PackageArgument: [['']], + RefArgument: [['']], + OptionalArgument: [ + ['DocumentClass'], + ['IncludeGraphics'], + ['LineBreak'], + ['UsePackage'], + ], + ShortTextArgument: [['Date']], + TextArgument: [['TabularEnvironment', 'BeginEnv']], + }), // TODO: does this override groups defined in the grammar? NodeProp.group.add(type => { const types = [] 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 426b80387f..f3f21326a8 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 @@ -87,6 +87,7 @@ AuthorCtrlSeq, AffilCtrlSeq, AffiliationCtrlSeq, + DateCtrlSeq, MaketitleCtrlSeq, TextColorCtrlSeq, ColorBoxCtrlSeq, @@ -245,6 +246,9 @@ KnownCommand { Affiliation { AffiliationCtrlSeq optionalWhitespace? OptionalArgument? optionalWhitespace? TextArgument } | + Date { + DateCtrlSeq optionalWhitespace? OptionalArgument? optionalWhitespace? ShortTextArgument + } | DocumentClass { DocumentClassCtrlSeq optionalWhitespace? OptionalArgument? DocumentClassArgument { ShortTextArgument } 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 47dab9f3fb..2167923a8d 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 @@ -25,6 +25,7 @@ import { AuthorCtrlSeq, AffilCtrlSeq, AffiliationCtrlSeq, + DateCtrlSeq, DocumentClassCtrlSeq, UsePackageCtrlSeq, HrefCtrlSeq, @@ -546,6 +547,7 @@ const otherKnowncommands = { '\\author': AuthorCtrlSeq, '\\affil': AffilCtrlSeq, '\\affiliation': AffiliationCtrlSeq, + '\\date': DateCtrlSeq, '\\documentclass': DocumentClassCtrlSeq, '\\usepackage': UsePackageCtrlSeq, '\\href': HrefCtrlSeq, diff --git a/services/web/frontend/js/features/source-editor/utils/node-props.ts b/services/web/frontend/js/features/source-editor/utils/node-props.ts new file mode 100644 index 0000000000..2446743dbd --- /dev/null +++ b/services/web/frontend/js/features/source-editor/utils/node-props.ts @@ -0,0 +1,9 @@ +import { NodeProp } from '@lezer/common' + +/** + * A node prop that contains an array, each item of which is an array of parent node types that's + * passed to [matchContext](https://lezer.codemirror.net/docs/ref/#common.SyntaxNodeRef.matchContext) + * to test whether the node matches the given context. If so, the text in the node is excluded from spell checking. + * An empty string is treated as a wildcard, so `[['']]` indicates that the node type should always be excluded. + */ +export const noSpellCheckProp = new NodeProp() diff --git a/services/web/frontend/js/features/source-editor/utils/tree-operations/text.ts b/services/web/frontend/js/features/source-editor/utils/tree-operations/text.ts index fc7b6e8aa0..a443b942cd 100644 --- a/services/web/frontend/js/features/source-editor/utils/tree-operations/text.ts +++ b/services/web/frontend/js/features/source-editor/utils/tree-operations/text.ts @@ -3,6 +3,7 @@ import { Line } from '@codemirror/state' import { EditorView } from '@codemirror/view' import { SyntaxNodeRef } from '@lezer/common' import OError from '@overleaf/o-error' +import { noSpellCheckProp } from '@/features/source-editor/utils/node-props' /* A convenient wrapper around 'Normal' tokens */ export class NormalTextSpan { @@ -39,18 +40,6 @@ export class NormalTextSpan { } } -const NotNormalNodeAncestors = [ - 'Include', - 'Input', - 'IncludeGraphics', - 'Cite', - 'LabelArgument', - 'UsePackage', - 'PackageArgument', - 'EnvNameGroup', - 'RefArgument', -] - export const getNormalTextSpansFromLine = ( view: EditorView, line: Line @@ -64,7 +53,11 @@ export const getNormalTextSpansFromLine = ( from: lineStart, to: lineEnd, enter: (node: SyntaxNodeRef) => { - if (NotNormalNodeAncestors.includes(node.type.name)) { + if ( + node.type.prop(noSpellCheckProp)?.some(context => { + return node.matchContext(context) + }) + ) { return false } if (node.type.name === 'Normal') {