mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
[visual] Handle selections adjacent to lists and section headings (#13581)
GitOrigin-RevId: 35b289102110f88587679740eeed575e16f6788b
This commit is contained in:
parent
1d33231392
commit
52d9ee79a3
3 changed files with 59 additions and 13 deletions
|
@ -17,6 +17,7 @@ import {
|
|||
ancestorOfNodeWithType,
|
||||
ancestorWithType,
|
||||
descendantsOfNodeWithType,
|
||||
wrappedNodeOfType,
|
||||
} from '../../utils/tree-operations/ancestors'
|
||||
import { getEnvironmentName } from '../../utils/tree-operations/environments'
|
||||
import { ListEnvironment } from '../../lezer-latex/latex.terms.mjs'
|
||||
|
@ -195,11 +196,9 @@ const toggleListForRange = (
|
|||
range: SelectionRange,
|
||||
environment: string
|
||||
) => {
|
||||
const ancestorNode = ancestorNodeOfType(
|
||||
view.state,
|
||||
range.head,
|
||||
ListEnvironment
|
||||
)
|
||||
const ancestorNode =
|
||||
ancestorNodeOfType(view.state, range.head, ListEnvironment) ??
|
||||
wrappedNodeOfType(view.state, range, ListEnvironment)
|
||||
|
||||
if (ancestorNode) {
|
||||
const beginEnvNode = ancestorNode.getChild('BeginEnv')
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { EditorSelection, EditorState, SelectionRange } from '@codemirror/state'
|
||||
import { EditorView } from '@codemirror/view'
|
||||
import { syntaxTree } from '@codemirror/language'
|
||||
import { ancestorsOfNodeWithType } from '../../utils/tree-operations/ancestors'
|
||||
import { ancestorOfNodeWithType } from '../../utils/tree-operations/ancestors'
|
||||
import { SyntaxNode } from '@lezer/common'
|
||||
|
||||
export const findCurrentSectionHeadingLevel = (state: EditorState) => {
|
||||
|
@ -25,13 +25,38 @@ export const rangeInfo = (
|
|||
range: SelectionRange
|
||||
): RangeInfo => {
|
||||
const tree = syntaxTree(state)
|
||||
const node = tree.resolveInner(range.anchor)
|
||||
const command = ancestorsOfNodeWithType(node, 'SectioningCommand').next()
|
||||
.value
|
||||
const ctrlSeq = command?.firstChild
|
||||
const level = ctrlSeq
|
||||
? state.sliceDoc(ctrlSeq.from + 1, ctrlSeq.to).trim()
|
||||
: 'text'
|
||||
|
||||
const fromNode = tree.resolveInner(range.from, 1)
|
||||
const fromAncestor = ancestorOfNodeWithType(fromNode, 'SectioningCommand')
|
||||
|
||||
const toNode = tree.resolveInner(range.to, -1)
|
||||
const toAncestor = ancestorOfNodeWithType(toNode, 'SectioningCommand')
|
||||
|
||||
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 }
|
||||
}
|
||||
|
|
|
@ -64,6 +64,28 @@ export function getAncestorStack(
|
|||
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 = (
|
||||
state: EditorState,
|
||||
pos: number,
|
||||
|
|
Loading…
Reference in a new issue