mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-15 14:43:46 +01:00
Include \def, \let, \newenvironment and \renewenvironment commands in the math preview (#20197)
* Include \def commands in command definitions * Allow command name to be in CtrlSym * Pull nodeIntersectsChange check out of branches * Fix \def handling * Add handling for \newenvironment and \renewenvironment * Allow \def to have optional arguments * :x Revert "Add handling for \newenvironment and \renewenvironment" This reverts commit a70d3a0a13ed552daf3b761893e3f8609f0b0fc8. * Add let commands to defintions * Add environment names to math preview * Ignore let command definitions in autocomplete * Move nodeIntersectsChange check back into each block * Add childOfNodeWithType utility and use to get command names * commandNameText -> commandName * Only include environment definitions * Rename documentEnvironmentNames to documentEnvironments * EnvironmentName -> Environment * Format GitOrigin-RevId: 9c5d701423ae786e5ff91960b4bcd94cd35d21c8
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { EditorState } from '@codemirror/state'
|
||||
import { SyntaxNode, SyntaxNodeRef } from '@lezer/common'
|
||||
import { getOptionalArgumentText } from './common'
|
||||
import { childOfNodeWithType, getOptionalArgumentText } from './common'
|
||||
import { NodeIntersectsChangeFn, ProjectionItem } from './projection'
|
||||
|
||||
/**
|
||||
@@ -8,15 +8,34 @@ import { NodeIntersectsChangeFn, ProjectionItem } from './projection'
|
||||
*/
|
||||
export class Command extends ProjectionItem {
|
||||
readonly title: string = ''
|
||||
readonly optionalArgCount: number = 0
|
||||
readonly requiredArgCount: number = 0
|
||||
readonly optionalArgCount: number | undefined = 0
|
||||
readonly requiredArgCount: number | undefined = 0
|
||||
readonly type: 'usage' | 'definition' = 'usage'
|
||||
readonly raw: string | undefined = undefined
|
||||
readonly ignoreInAutocomplete?: boolean = false
|
||||
}
|
||||
|
||||
const getCommandName = (
|
||||
node: SyntaxNode,
|
||||
state: EditorState,
|
||||
childTypes: string[]
|
||||
): string | null => {
|
||||
const child = childOfNodeWithType(node, ...childTypes)
|
||||
|
||||
if (child) {
|
||||
const commandName = state.doc.sliceString(child.from, child.to)
|
||||
if (commandName.length > 0) {
|
||||
return commandName
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts Command instances from the syntax tree.
|
||||
* `\newcommand` and `\renewcommand` are treated specially
|
||||
* `\newcommand`, `\renewcommand`, `\newenvironment`, `\renewenvironment`
|
||||
* and `\def` are treated specially.
|
||||
*/
|
||||
export const enterNode = (
|
||||
state: EditorState,
|
||||
@@ -29,19 +48,13 @@ export const enterNode = (
|
||||
// This should already be in `items`
|
||||
return
|
||||
}
|
||||
let commandName = node.node.getChild('LiteralArgContent')
|
||||
if (!commandName) {
|
||||
commandName = node.node.getChild('Csname')
|
||||
}
|
||||
if (!commandName) {
|
||||
return
|
||||
}
|
||||
const commandNameText = state.doc.sliceString(
|
||||
commandName.from,
|
||||
commandName.to
|
||||
)
|
||||
|
||||
if (commandNameText.length < 1) {
|
||||
const commandName = getCommandName(node.node, state, [
|
||||
'LiteralArgContent',
|
||||
'Csname',
|
||||
])
|
||||
|
||||
if (commandName === null) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -66,7 +79,7 @@ export const enterNode = (
|
||||
|
||||
items.push({
|
||||
line: state.doc.lineAt(node.from).number,
|
||||
title: commandNameText,
|
||||
title: commandName,
|
||||
from: node.from,
|
||||
to: node.to,
|
||||
optionalArgCount: commandDefinitionHasOptionalArgument ? 1 : 0,
|
||||
@@ -74,12 +87,66 @@ export const enterNode = (
|
||||
type: 'definition',
|
||||
raw: state.sliceDoc(node.from, node.to),
|
||||
})
|
||||
} else if (node.type.is('Def')) {
|
||||
if (!nodeIntersectsChange(node.node)) {
|
||||
// This should already be in `items`
|
||||
return
|
||||
}
|
||||
|
||||
const commandName = getCommandName(node.node, state, ['Csname', 'CtrlSym'])
|
||||
|
||||
if (commandName === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const requiredArgCount = node.node.getChildren('MacroParameter').length
|
||||
const optionalArgCount = node.node.getChildren(
|
||||
'OptionalMacroParameter'
|
||||
).length
|
||||
|
||||
items.push({
|
||||
line: state.doc.lineAt(node.from).number,
|
||||
title: commandName,
|
||||
from: node.from,
|
||||
to: node.to,
|
||||
optionalArgCount,
|
||||
requiredArgCount,
|
||||
type: 'definition',
|
||||
raw: state.sliceDoc(node.from, node.to),
|
||||
})
|
||||
} else if (node.type.is('Let')) {
|
||||
if (!nodeIntersectsChange(node.node)) {
|
||||
// This should already be in `items`
|
||||
return
|
||||
}
|
||||
|
||||
const commandName = getCommandName(node.node, state, ['Csname'])
|
||||
|
||||
if (commandName === null) {
|
||||
return
|
||||
}
|
||||
items.push({
|
||||
line: state.doc.lineAt(node.from).number,
|
||||
title: commandName,
|
||||
from: node.from,
|
||||
to: node.to,
|
||||
ignoreInAutocomplete: true, // Ignoring since we don't know the argument counts
|
||||
optionalArgCount: undefined,
|
||||
requiredArgCount: undefined,
|
||||
type: 'definition',
|
||||
raw: state.sliceDoc(node.from, node.to),
|
||||
})
|
||||
} else if (
|
||||
node.type.is('UnknownCommand') ||
|
||||
node.type.is('KnownCommand') ||
|
||||
node.type.is('MathUnknownCommand') ||
|
||||
node.type.is('DefinitionFragmentUnknownCommand')
|
||||
) {
|
||||
if (!nodeIntersectsChange(node.node)) {
|
||||
// This should already be in `items`
|
||||
return
|
||||
}
|
||||
|
||||
let commandNode: SyntaxNode | null = node.node
|
||||
if (node.type.is('KnownCommand')) {
|
||||
// KnownCommands are defined as
|
||||
@@ -96,10 +163,7 @@ export const enterNode = (
|
||||
if (!commandNode) {
|
||||
return
|
||||
}
|
||||
if (!nodeIntersectsChange(node.node)) {
|
||||
// This should already be in `items`
|
||||
return
|
||||
}
|
||||
|
||||
const ctrlSeq = commandNode.getChild('$CtrlSeq')
|
||||
if (!ctrlSeq) {
|
||||
return
|
||||
|
||||
@@ -78,3 +78,27 @@ export const nodeHasError = (node: SyntaxNode): boolean => {
|
||||
|
||||
return hasError
|
||||
}
|
||||
|
||||
export const childOfNodeWithType = (
|
||||
node: SyntaxNode,
|
||||
...types: (string | number)[]
|
||||
): SyntaxNode | null => {
|
||||
let childOfType: SyntaxNode | null = null
|
||||
|
||||
node.cursor().iterate(child => {
|
||||
if (childOfType !== null) {
|
||||
return false
|
||||
}
|
||||
|
||||
for (const type of types) {
|
||||
if (child.type.is(type)) {
|
||||
childOfType = child.node
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return childOfType
|
||||
}
|
||||
|
||||
+10
-4
@@ -7,14 +7,16 @@ import { FigureData } from '../../extensions/figure-modal'
|
||||
|
||||
const HUNDRED_MS = 100
|
||||
|
||||
export class EnvironmentName extends ProjectionItem {
|
||||
export class Environment extends ProjectionItem {
|
||||
readonly title: string = ''
|
||||
readonly type: 'usage' | 'definition' = 'usage'
|
||||
readonly raw: string = ''
|
||||
}
|
||||
|
||||
export const enterNode = (
|
||||
state: EditorState,
|
||||
node: SyntaxNodeRef,
|
||||
items: EnvironmentName[],
|
||||
items: Environment[],
|
||||
nodeIntersectsChange: NodeIntersectsChangeFn
|
||||
): any => {
|
||||
if (node.type.is('EnvNameGroup')) {
|
||||
@@ -38,11 +40,13 @@ export const enterNode = (
|
||||
return false
|
||||
}
|
||||
|
||||
const thisEnvironmentName = {
|
||||
const thisEnvironmentName: Environment = {
|
||||
title: envNameText,
|
||||
from: envNameNode.from,
|
||||
to: envNameNode.to,
|
||||
line: state.doc.lineAt(envNameNode.from).number,
|
||||
type: 'usage',
|
||||
raw: state.sliceDoc(node.from, node.to),
|
||||
}
|
||||
|
||||
items.push(thisEnvironmentName)
|
||||
@@ -65,11 +69,13 @@ export const enterNode = (
|
||||
return
|
||||
}
|
||||
|
||||
const thisEnvironmentName = {
|
||||
const thisEnvironmentName: Environment = {
|
||||
title: envNameText,
|
||||
from: envNameNode.from,
|
||||
to: envNameNode.to,
|
||||
line: state.doc.lineAt(envNameNode.from).number,
|
||||
type: 'definition',
|
||||
raw: state.sliceDoc(node.from, node.to),
|
||||
}
|
||||
|
||||
items.push(thisEnvironmentName)
|
||||
|
||||
Reference in New Issue
Block a user