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,
|
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')
|
||||||
|
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue