[visual] Handle selections adjacent to lists and section headings (#13581)

GitOrigin-RevId: 35b289102110f88587679740eeed575e16f6788b
This commit is contained in:
Alf Eaton 2023-07-04 09:15:02 +01:00 committed by Copybot
parent 1d33231392
commit 52d9ee79a3
3 changed files with 59 additions and 13 deletions

View file

@ -17,6 +17,7 @@ import {
ancestorOfNodeWithType, ancestorOfNodeWithType,
ancestorWithType, ancestorWithType,
descendantsOfNodeWithType, descendantsOfNodeWithType,
wrappedNodeOfType,
} from '../../utils/tree-operations/ancestors' } from '../../utils/tree-operations/ancestors'
import { getEnvironmentName } from '../../utils/tree-operations/environments' import { getEnvironmentName } from '../../utils/tree-operations/environments'
import { ListEnvironment } from '../../lezer-latex/latex.terms.mjs' import { ListEnvironment } from '../../lezer-latex/latex.terms.mjs'
@ -195,11 +196,9 @@ const toggleListForRange = (
range: SelectionRange, range: SelectionRange,
environment: string environment: string
) => { ) => {
const ancestorNode = ancestorNodeOfType( const ancestorNode =
view.state, ancestorNodeOfType(view.state, range.head, ListEnvironment) ??
range.head, wrappedNodeOfType(view.state, range, ListEnvironment)
ListEnvironment
)
if (ancestorNode) { if (ancestorNode) {
const beginEnvNode = ancestorNode.getChild('BeginEnv') const beginEnvNode = ancestorNode.getChild('BeginEnv')

View file

@ -1,7 +1,7 @@
import { EditorSelection, EditorState, SelectionRange } from '@codemirror/state' import { EditorSelection, EditorState, SelectionRange } from '@codemirror/state'
import { EditorView } from '@codemirror/view' import { EditorView } from '@codemirror/view'
import { syntaxTree } from '@codemirror/language' import { syntaxTree } from '@codemirror/language'
import { ancestorsOfNodeWithType } from '../../utils/tree-operations/ancestors' import { ancestorOfNodeWithType } from '../../utils/tree-operations/ancestors'
import { SyntaxNode } from '@lezer/common' import { SyntaxNode } from '@lezer/common'
export const findCurrentSectionHeadingLevel = (state: EditorState) => { export const findCurrentSectionHeadingLevel = (state: EditorState) => {
@ -25,13 +25,38 @@ export const rangeInfo = (
range: SelectionRange range: SelectionRange
): RangeInfo => { ): RangeInfo => {
const tree = syntaxTree(state) const tree = syntaxTree(state)
const node = tree.resolveInner(range.anchor)
const command = ancestorsOfNodeWithType(node, 'SectioningCommand').next() const fromNode = tree.resolveInner(range.from, 1)
.value const fromAncestor = ancestorOfNodeWithType(fromNode, 'SectioningCommand')
const ctrlSeq = command?.firstChild
const level = ctrlSeq const toNode = tree.resolveInner(range.to, -1)
? state.sliceDoc(ctrlSeq.from + 1, ctrlSeq.to).trim() const toAncestor = ancestorOfNodeWithType(toNode, 'SectioningCommand')
: 'text'
const command = fromAncestor ?? toAncestor
// from and to are both outside section heading
if (!command) {
return { range, level: 'text' }
}
if (fromAncestor && toAncestor) {
// from and to are inside different section headings
if (fromAncestor !== toAncestor) {
return { range, level: 'text' }
}
} else {
// the range isn't empty and only one end is inside a section heading
if (!range.empty) {
return { range, level: 'text' }
}
}
const ctrlSeq = command.firstChild
if (!ctrlSeq) {
return { range, level: 'text' }
}
const level = state.sliceDoc(ctrlSeq.from + 1, ctrlSeq.to).trim()
return { command, ctrlSeq, level, range } return { command, ctrlSeq, level, range }
} }

View file

@ -64,6 +64,28 @@ export function getAncestorStack(
return stack.reverse() return stack.reverse()
} }
export const wrappedNodeOfType = (
state: EditorState,
range: SelectionRange,
type: string | number
): SyntaxNode | null => {
if (range.empty) {
return null
}
const ancestorNode = ancestorNodeOfType(state, range.from, type, 1)
if (
ancestorNode &&
ancestorNode.from === range.from &&
ancestorNode.to === range.to
) {
return ancestorNode
}
return null
}
export const ancestorNodeOfType = ( export const ancestorNodeOfType = (
state: EditorState, state: EditorState,
pos: number, pos: number,