mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
[visual] Avoid showing list environment markup when the selection is within list items (#13461)
GitOrigin-RevId: cadab83774d52dc6c4867fdd7300a1217423b837
This commit is contained in:
parent
afbd143f0f
commit
c16d2d5840
5 changed files with 126 additions and 212 deletions
|
@ -20,6 +20,7 @@ import {
|
|||
} from '../../utils/tree-operations/ancestors'
|
||||
import { getEnvironmentName } from '../../utils/tree-operations/environments'
|
||||
import { ListEnvironment } from '../../lezer-latex/latex.terms.mjs'
|
||||
import { SyntaxNode } from '@lezer/common'
|
||||
|
||||
export const ancestorListType = (state: EditorState): string | null => {
|
||||
const ancestorNode = ancestorWithType(state, ListEnvironment)
|
||||
|
@ -303,6 +304,21 @@ const toggleListForRange = (
|
|||
return { range }
|
||||
}
|
||||
|
||||
export const getListItems = (node: SyntaxNode): SyntaxNode[] => {
|
||||
const items: SyntaxNode[] = []
|
||||
|
||||
node.cursor().iterate(nodeRef => {
|
||||
if (nodeRef.type.is('Item')) {
|
||||
items.push(nodeRef.node)
|
||||
}
|
||||
if (nodeRef.type.is('ListEnvironment') && nodeRef.node !== node) {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
export const toggleListForRanges =
|
||||
(environment: string) => (view: EditorView) => {
|
||||
view.dispatch(
|
||||
|
|
|
@ -48,6 +48,7 @@ import { EditableGraphicsWidget } from './visual-widgets/editable-graphics'
|
|||
import { EditableInlineGraphicsWidget } from './visual-widgets/editable-inline-graphics'
|
||||
import { CloseBrace, OpenBrace } from '../../lezer-latex/latex.terms.mjs'
|
||||
import { FootnoteWidget } from './visual-widgets/footnote'
|
||||
import { getListItems } from '../toolbar/lists'
|
||||
|
||||
type Options = {
|
||||
fileTreeManager: {
|
||||
|
@ -225,7 +226,6 @@ export const atomicDecorations = (options: Options) => {
|
|||
tree.iterate({
|
||||
enter(nodeRef) {
|
||||
if (nodeRef.type.is('$Environment')) {
|
||||
if (shouldDecorate(state, nodeRef)) {
|
||||
const envName = getUnstarredEnvironmentName(nodeRef.node, state)
|
||||
const hideInEnvironmentTypes = ['figure', 'table']
|
||||
if (envName && hideInEnvironmentTypes.includes(envName)) {
|
||||
|
@ -300,7 +300,8 @@ export const atomicDecorations = (options: Options) => {
|
|||
|
||||
if (
|
||||
!selectionIntersects(state.selection, begin) &&
|
||||
!selectionIntersects(state.selection, end)
|
||||
!selectionIntersects(state.selection, end) &&
|
||||
getListItems(nodeRef.node).length > 0 // not empty
|
||||
) {
|
||||
decorations.push(
|
||||
Decoration.replace({
|
||||
|
@ -313,7 +314,6 @@ export const atomicDecorations = (options: Options) => {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (nodeRef.type.is('BeginEnv')) {
|
||||
// the beginning of an environment, with an environment name argument
|
||||
const envName = getEnvironmentName(nodeRef.node, state)
|
||||
|
|
|
@ -43,20 +43,10 @@ describe('<CodeMirrorEditor/> lists in Rich Text mode', function () {
|
|||
|
||||
// create a nested list
|
||||
cy.get('.cm-line')
|
||||
.eq(2)
|
||||
.eq(1)
|
||||
.type(isMac ? '{cmd}]' : '{ctrl}]')
|
||||
|
||||
cy.get('.cm-content').should(
|
||||
'have.text',
|
||||
[
|
||||
'\\begin{itemize}',
|
||||
' Test',
|
||||
'\\begin{itemize}',
|
||||
' Test',
|
||||
'\\end{itemize}',
|
||||
'\\end{itemize}',
|
||||
].join('')
|
||||
)
|
||||
cy.get('.cm-content').should('have.text', [' Test', ' Test'].join(''))
|
||||
})
|
||||
|
||||
it('creates a nested list inside an indented list', function () {
|
||||
|
@ -70,20 +60,10 @@ describe('<CodeMirrorEditor/> lists in Rich Text mode', function () {
|
|||
|
||||
// create a nested list
|
||||
cy.get('.cm-line')
|
||||
.eq(2)
|
||||
.eq(1)
|
||||
.type(isMac ? '{cmd}]' : '{ctrl}]')
|
||||
|
||||
cy.get('.cm-content').should(
|
||||
'have.text',
|
||||
[
|
||||
'\\begin{itemize}',
|
||||
' Test',
|
||||
'\\begin{itemize}',
|
||||
' Test',
|
||||
'\\end{itemize}',
|
||||
'\\end{itemize}',
|
||||
].join('')
|
||||
)
|
||||
cy.get('.cm-content').should('have.text', [' Test', ' Test'].join(''))
|
||||
})
|
||||
|
||||
it('creates a nested list on Tab at the start of an item', function () {
|
||||
|
@ -96,24 +76,14 @@ describe('<CodeMirrorEditor/> lists in Rich Text mode', function () {
|
|||
mountEditor(content)
|
||||
|
||||
// move to the start of the item and press Tab
|
||||
cy.get('.cm-line').eq(2).as('line')
|
||||
cy.get('.cm-line').eq(1).as('line')
|
||||
cy.get('@line').click()
|
||||
cy.get('@line').type('{leftArrow}'.repeat(4))
|
||||
cy.get('@line').trigger('keydown', {
|
||||
key: 'Tab',
|
||||
})
|
||||
|
||||
cy.get('.cm-content').should(
|
||||
'have.text',
|
||||
[
|
||||
'\\begin{itemize}',
|
||||
' Test',
|
||||
'\\begin{itemize}',
|
||||
' Test',
|
||||
'\\end{itemize}',
|
||||
'\\end{itemize}',
|
||||
].join('')
|
||||
)
|
||||
cy.get('.cm-content').should('have.text', [' Test', ' Test'].join(''))
|
||||
})
|
||||
|
||||
it('does not creates a nested list on Tab when not at the start of an item', function () {
|
||||
|
@ -126,22 +96,13 @@ describe('<CodeMirrorEditor/> lists in Rich Text mode', function () {
|
|||
mountEditor(content)
|
||||
|
||||
// focus a line (at the end of a list item) and press Tab
|
||||
cy.get('.cm-line').eq(2).as('line')
|
||||
cy.get('.cm-line').eq(1).as('line')
|
||||
cy.get('@line').click()
|
||||
cy.get('@line').trigger('keydown', {
|
||||
key: 'Tab',
|
||||
})
|
||||
|
||||
cy.get('.cm-content').should(
|
||||
'have.text',
|
||||
[
|
||||
//
|
||||
'\\begin{itemize}',
|
||||
' Test',
|
||||
' Test ',
|
||||
'\\end{itemize}',
|
||||
].join('')
|
||||
)
|
||||
cy.get('.cm-content').should('have.text', [' Test', ' Test '].join(''))
|
||||
})
|
||||
|
||||
it('removes a nested list on Shift-Tab', function () {
|
||||
|
@ -154,41 +115,22 @@ describe('<CodeMirrorEditor/> lists in Rich Text mode', function () {
|
|||
mountEditor(content)
|
||||
|
||||
// move to the start of the list item and press Tab
|
||||
cy.get('.cm-line').eq(2).as('line')
|
||||
cy.get('.cm-line').eq(1).as('line')
|
||||
cy.get('@line').click()
|
||||
cy.get('@line').type('{leftArrow}'.repeat(4))
|
||||
cy.get('@line').trigger('keydown', {
|
||||
key: 'Tab',
|
||||
})
|
||||
|
||||
cy.get('.cm-content').should(
|
||||
'have.text',
|
||||
[
|
||||
'\\begin{itemize}',
|
||||
' Test',
|
||||
'\\begin{itemize}',
|
||||
' Test',
|
||||
'\\end{itemize}',
|
||||
'\\end{itemize}',
|
||||
].join('')
|
||||
)
|
||||
cy.get('.cm-content').should('have.text', [' Test', ' Test'].join(''))
|
||||
|
||||
// focus the indented line and press Shift-Tab
|
||||
cy.get('.cm-line').eq(4).trigger('keydown', {
|
||||
cy.get('.cm-line').eq(1).trigger('keydown', {
|
||||
key: 'Tab',
|
||||
shiftKey: true,
|
||||
})
|
||||
|
||||
cy.get('.cm-content').should(
|
||||
'have.text',
|
||||
[
|
||||
//
|
||||
'\\begin{itemize}',
|
||||
' Test',
|
||||
' Test',
|
||||
'\\end{itemize}',
|
||||
].join('')
|
||||
)
|
||||
cy.get('.cm-content').should('have.text', [' Test', ' Test'].join(''))
|
||||
})
|
||||
|
||||
it('does not remove a top-level nested list on Shift-Tab', function () {
|
||||
|
@ -201,21 +143,12 @@ describe('<CodeMirrorEditor/> lists in Rich Text mode', function () {
|
|||
mountEditor(content)
|
||||
|
||||
// focus a list item and press Shift-Tab
|
||||
cy.get('.cm-line').eq(2).trigger('keydown', {
|
||||
cy.get('.cm-line').eq(1).trigger('keydown', {
|
||||
key: 'Tab',
|
||||
shiftKey: true,
|
||||
})
|
||||
|
||||
cy.get('.cm-content').should(
|
||||
'have.text',
|
||||
[
|
||||
//
|
||||
'\\begin{itemize}',
|
||||
' Test',
|
||||
' Test',
|
||||
'\\end{itemize}',
|
||||
].join('')
|
||||
)
|
||||
cy.get('.cm-content').should('have.text', [' Test', ' Test'].join(''))
|
||||
})
|
||||
|
||||
it('handles up arrow at the start of a list item', function () {
|
||||
|
@ -227,7 +160,7 @@ describe('<CodeMirrorEditor/> lists in Rich Text mode', function () {
|
|||
].join('\n')
|
||||
mountEditor(content)
|
||||
|
||||
cy.get('.cm-line').eq(2).as('line')
|
||||
cy.get('.cm-line').eq(1).as('line')
|
||||
cy.get('@line').click()
|
||||
cy.get('@line').type('{leftArrow}'.repeat(3)) // to the start of the item
|
||||
cy.get('@line').type('{upArrow}{Shift}{rightArrow}{rightArrow}{rightArrow}') // up and extend to the end of the item
|
||||
|
@ -246,7 +179,7 @@ describe('<CodeMirrorEditor/> lists in Rich Text mode', function () {
|
|||
].join('\n')
|
||||
mountEditor(content)
|
||||
|
||||
cy.get('.cm-line').eq(2).as('line')
|
||||
cy.get('.cm-line').eq(1).as('line')
|
||||
cy.get('@line').click()
|
||||
cy.get('@line').type('{leftArrow}'.repeat(3)) // to the start of the item
|
||||
cy.get('@line').type('{upArrow}{Shift}{rightArrow}{rightArrow}{rightArrow}') // up and extend to the end of the item
|
||||
|
@ -290,15 +223,12 @@ describe('<CodeMirrorEditor/> lists in Rich Text mode', function () {
|
|||
].join('\n')
|
||||
mountEditor(content)
|
||||
|
||||
cy.get('.cm-line').eq(2).as('line')
|
||||
cy.get('.cm-line').eq(0).as('line')
|
||||
cy.get('@line').click()
|
||||
cy.get('@line').type('\\ite')
|
||||
cy.get('@line').type('{enter}')
|
||||
cy.get('@line').type('second')
|
||||
|
||||
cy.get('.cm-content').should(
|
||||
'have.text',
|
||||
['\\begin{itemize}', ' first', ' second', '\\end{itemize}'].join('')
|
||||
)
|
||||
cy.get('.cm-content').should('have.text', [' first', ' second'].join(''))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -130,18 +130,10 @@ describe('<CodeMirrorEditor/> toolbar in Rich Text mode', function () {
|
|||
|
||||
clickToolbarButton('Bullet List')
|
||||
|
||||
cy.get('.cm-content').should(
|
||||
'have.text',
|
||||
[
|
||||
//
|
||||
'\\begin{itemize}',
|
||||
' test',
|
||||
'\\end{itemize}',
|
||||
].join('')
|
||||
)
|
||||
cy.get('.cm-content').should('have.text', ' test')
|
||||
|
||||
cy.get('.cm-line').eq(1).type('ing')
|
||||
cy.get('.cm-line').eq(1).should('have.text', ' testing')
|
||||
cy.get('.cm-line').eq(0).type('ing')
|
||||
cy.get('.cm-line').eq(0).should('have.text', ' testing')
|
||||
})
|
||||
|
||||
it('should insert a numbered list', function () {
|
||||
|
@ -150,17 +142,9 @@ describe('<CodeMirrorEditor/> toolbar in Rich Text mode', function () {
|
|||
|
||||
clickToolbarButton('Numbered List')
|
||||
|
||||
cy.get('.cm-content').should(
|
||||
'have.text',
|
||||
[
|
||||
//
|
||||
'\\begin{enumerate}',
|
||||
' test',
|
||||
'\\end{enumerate}',
|
||||
].join('')
|
||||
)
|
||||
cy.get('.cm-content').should('have.text', ' test')
|
||||
|
||||
cy.get('.cm-line').eq(1).type('ing')
|
||||
cy.get('.cm-line').eq(1).should('have.text', ' testing')
|
||||
cy.get('.cm-line').eq(0).type('ing')
|
||||
cy.get('.cm-line').eq(0).should('have.text', ' testing')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -68,25 +68,21 @@ describe('<CodeMirrorEditor/> in Visual mode', function () {
|
|||
// select the first autocomplete item
|
||||
cy.findByRole('option').eq(0).click()
|
||||
|
||||
cy.get('@first-line').should('have.text', '\\begin{itemize}')
|
||||
cy.get('@second-line')
|
||||
cy.get('@first-line')
|
||||
.should('have.text', ' ')
|
||||
.find('.ol-cm-item')
|
||||
.should('have.length', 1)
|
||||
cy.get('@third-line').should('have.text', '\\end{itemize}')
|
||||
|
||||
cy.get('@second-line').type('test{Enter}test')
|
||||
cy.get('@first-line').type('test{Enter}test')
|
||||
|
||||
cy.get('@first-line').should('have.text', '\\begin{itemize}')
|
||||
cy.get('@first-line')
|
||||
.should('have.text', ' test')
|
||||
.find('.ol-cm-item')
|
||||
.should('have.length', 1)
|
||||
cy.get('@second-line')
|
||||
.should('have.text', ' test')
|
||||
.find('.ol-cm-item')
|
||||
.should('have.length', 1)
|
||||
cy.get('@third-line')
|
||||
.should('have.text', ' test')
|
||||
.find('.ol-cm-item')
|
||||
.should('have.length', 1)
|
||||
cy.get('@fourth-line').should('have.text', '\\end{itemize}')
|
||||
})
|
||||
|
||||
it('finishes a list on Enter in the last item if empty', function () {
|
||||
|
@ -95,10 +91,9 @@ describe('<CodeMirrorEditor/> in Visual mode', function () {
|
|||
// select the first autocomplete item
|
||||
cy.findByRole('option').eq(0).click()
|
||||
|
||||
cy.get('@second-line').type('test{Enter}{Enter}')
|
||||
cy.get('@first-line').type('test{Enter}{Enter}')
|
||||
|
||||
cy.get('.cm-line')
|
||||
.eq(0)
|
||||
cy.get('@first-line')
|
||||
.should('have.text', ' test')
|
||||
.find('.ol-cm-item')
|
||||
.should('have.length', 1)
|
||||
|
@ -113,18 +108,7 @@ describe('<CodeMirrorEditor/> in Visual mode', function () {
|
|||
cy.findByRole('option').eq(0).click()
|
||||
|
||||
cy.get('@second-line').type('test{Enter}test{Enter}{upArrow}{Enter}{Enter}')
|
||||
|
||||
const lines = [
|
||||
'\\begin{itemize}',
|
||||
' test',
|
||||
' ',
|
||||
' ',
|
||||
' test',
|
||||
' ',
|
||||
'\\end{itemize}',
|
||||
]
|
||||
|
||||
cy.get('.cm-content').should('have.text', lines.join(''))
|
||||
cy.get('.cm-content').should('have.text', ' testtest')
|
||||
})
|
||||
|
||||
forEach(['textbf', 'textit', 'underline']).it(
|
||||
|
|
Loading…
Reference in a new issue