mirror of
https://github.com/atom/atom.git
synced 2026-01-24 22:38:20 -05:00
Fix folding of internal nodes when fold end isn't specified
This commit is contained in:
@@ -6,6 +6,8 @@ const TextEditor = require('../src/text-editor')
|
||||
const TreeSitterGrammar = require('../src/tree-sitter-grammar')
|
||||
const TreeSitterLanguageMode = require('../src/tree-sitter-language-mode')
|
||||
|
||||
const cGrammarPath = require.resolve('language-c/grammars/tree-sitter-c.cson')
|
||||
const pythonGrammarPath = require.resolve('language-python/grammars/tree-sitter-python.cson')
|
||||
const jsGrammarPath = require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')
|
||||
|
||||
describe('TreeSitterLanguageMode', () => {
|
||||
@@ -73,7 +75,7 @@ describe('TreeSitterLanguageMode', () => {
|
||||
editor.displayLayer.reset({foldCharacter: '…'})
|
||||
})
|
||||
|
||||
it('can fold nodes that start and end with specified tokens and span multiple lines', () => {
|
||||
it('can fold nodes that start and end with specified tokens', () => {
|
||||
const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, {
|
||||
parser: 'tree-sitter-javascript',
|
||||
folds: [
|
||||
@@ -107,6 +109,7 @@ describe('TreeSitterLanguageMode', () => {
|
||||
expect(editor.isFoldableAtBufferRow(2)).toBe(true)
|
||||
expect(editor.isFoldableAtBufferRow(3)).toBe(false)
|
||||
expect(editor.isFoldableAtBufferRow(4)).toBe(true)
|
||||
expect(editor.isFoldableAtBufferRow(5)).toBe(false)
|
||||
|
||||
editor.foldBufferRow(2)
|
||||
expect(getDisplayText(editor)).toBe(dedent `
|
||||
@@ -127,20 +130,24 @@ describe('TreeSitterLanguageMode', () => {
|
||||
`)
|
||||
})
|
||||
|
||||
it('can fold nodes that start and end with specified tokens and span multiple lines', () => {
|
||||
it('can fold nodes of specified types', () => {
|
||||
const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, {
|
||||
parser: 'tree-sitter-javascript',
|
||||
folds: [
|
||||
// Start the fold after the first child (the opening tag) and end it at the last child
|
||||
// (the closing tag).
|
||||
{
|
||||
type: 'jsx_element',
|
||||
start: {index: 0, type: 'jsx_opening_element'},
|
||||
end: {index: -1, type: 'jsx_closing_element'}
|
||||
start: {index: 0},
|
||||
end: {index: -1}
|
||||
},
|
||||
|
||||
// End the fold at the *second* to last child of the self-closing tag: the `/`.
|
||||
{
|
||||
type: 'jsx_self_closing_element',
|
||||
start: {index: 1},
|
||||
end: {type: '/', index: -2}
|
||||
},
|
||||
end: {index: -2}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
@@ -183,11 +190,12 @@ describe('TreeSitterLanguageMode', () => {
|
||||
`)
|
||||
})
|
||||
|
||||
it('can fold specified types of multi-line nodes', () => {
|
||||
it('can fold entire nodes when no start or end parameters are specified', () => {
|
||||
const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, {
|
||||
parser: 'tree-sitter-javascript',
|
||||
folds: [
|
||||
{type: 'template_string'},
|
||||
// By default, for a node with no children, folds are started at the *end* of the first
|
||||
// line of a node, and ended at the *beginning* of the last line.
|
||||
{type: 'comment'}
|
||||
]
|
||||
})
|
||||
@@ -197,9 +205,9 @@ describe('TreeSitterLanguageMode', () => {
|
||||
/**
|
||||
* Important
|
||||
*/
|
||||
const x = \`one
|
||||
two
|
||||
three\`
|
||||
const x = 1 /*
|
||||
Also important
|
||||
*/
|
||||
`)
|
||||
|
||||
editor.screenLineForScreenRow(0)
|
||||
@@ -213,17 +221,136 @@ describe('TreeSitterLanguageMode', () => {
|
||||
editor.foldBufferRow(0)
|
||||
expect(getDisplayText(editor)).toBe(dedent `
|
||||
/**… */
|
||||
const x = \`one
|
||||
two
|
||||
three\`
|
||||
const x = 1 /*
|
||||
Also important
|
||||
*/
|
||||
`)
|
||||
|
||||
editor.foldBufferRow(3)
|
||||
expect(getDisplayText(editor)).toBe(dedent `
|
||||
/**… */
|
||||
const x = \`one… three\`
|
||||
const x = 1 /*…*/
|
||||
`)
|
||||
})
|
||||
|
||||
it('tries each folding strategy for a given node in the order specified', () => {
|
||||
const grammar = new TreeSitterGrammar(atom.grammars, cGrammarPath, {
|
||||
parser: 'tree-sitter-c',
|
||||
folds: [
|
||||
// If the #ifdef has an `#else` clause, then end the fold there.
|
||||
{
|
||||
type: 'preproc_ifdef',
|
||||
start: {index: 1},
|
||||
end: {type: 'preproc_else'}
|
||||
},
|
||||
|
||||
// Otherwise, end the fold at the last child - the `#endif`.
|
||||
{
|
||||
type: 'preproc_ifdef',
|
||||
start: {index: 1},
|
||||
end: {index: -1}
|
||||
},
|
||||
|
||||
// When folding an `#else` clause, the fold extends to the end of the clause.
|
||||
{
|
||||
type: 'preproc_else',
|
||||
start: {index: 0}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar}))
|
||||
|
||||
buffer.setText(dedent `
|
||||
#ifndef FOO_H_
|
||||
#define FOO_H_
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
const char *path_separator = "\\";
|
||||
|
||||
#else
|
||||
|
||||
#include <dirent.h>
|
||||
const char *path_separator = "/";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
`)
|
||||
|
||||
editor.screenLineForScreenRow(0)
|
||||
|
||||
editor.foldBufferRow(3)
|
||||
expect(getDisplayText(editor)).toBe(dedent `
|
||||
#ifndef FOO_H_
|
||||
#define FOO_H_
|
||||
|
||||
#ifdef _WIN32…#else
|
||||
|
||||
#include <dirent.h>
|
||||
const char *path_separator = "/";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
`)
|
||||
|
||||
editor.foldBufferRow(8)
|
||||
expect(getDisplayText(editor)).toBe(dedent `
|
||||
#ifndef FOO_H_
|
||||
#define FOO_H_
|
||||
|
||||
#ifdef _WIN32…#else…
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
`)
|
||||
|
||||
editor.foldBufferRow(0)
|
||||
expect(getDisplayText(editor)).toBe(dedent `
|
||||
#ifndef FOO_H_…#endif
|
||||
`)
|
||||
})
|
||||
|
||||
describe('when folding a node that ends with a line break', () => {
|
||||
it('ends the fold at the end of the previous line', () => {
|
||||
const grammar = new TreeSitterGrammar(atom.grammars, pythonGrammarPath, {
|
||||
parser: 'tree-sitter-python',
|
||||
folds: [
|
||||
{
|
||||
type: 'function_definition',
|
||||
start: {type: ':'}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar}))
|
||||
|
||||
buffer.setText(dedent `
|
||||
def ab():
|
||||
print 'a'
|
||||
print 'b'
|
||||
|
||||
def cd():
|
||||
print 'c'
|
||||
print 'd'
|
||||
`)
|
||||
|
||||
editor.screenLineForScreenRow(0)
|
||||
|
||||
editor.foldBufferRow(0)
|
||||
expect(getDisplayText(editor)).toBe(dedent `
|
||||
def ab():…
|
||||
|
||||
def cd():
|
||||
print 'c'
|
||||
print 'd'
|
||||
`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('TextEditor.selectLargerSyntaxNode and .selectSmallerSyntaxNode', () => {
|
||||
|
||||
@@ -203,57 +203,55 @@ class TreeSitterLanguageMode {
|
||||
}
|
||||
}
|
||||
|
||||
let childBeforeFold
|
||||
let foldStart
|
||||
const startEntry = foldEntry.start
|
||||
if (startEntry) {
|
||||
if (startEntry.index != null) {
|
||||
childBeforeFold = children[startEntry.index]
|
||||
if (!childBeforeFold) continue
|
||||
if (startEntry.type && startEntry.type !== childBeforeFold.type) continue
|
||||
const child = children[startEntry.index]
|
||||
if (!child || (startEntry.type && startEntry.type !== child.type)) continue
|
||||
foldStart = child.endPosition
|
||||
} else {
|
||||
if (!childTypes) childTypes = children.map(child => child.type)
|
||||
let index = childTypes.indexOf(startEntry.type)
|
||||
const index = childTypes.indexOf(startEntry.type)
|
||||
if (index === -1) continue
|
||||
childBeforeFold = children[index]
|
||||
foldStart = children[index].endPosition
|
||||
}
|
||||
}
|
||||
|
||||
let childAfterFold
|
||||
let foldEnd
|
||||
const endEntry = foldEntry.end
|
||||
if (endEntry) {
|
||||
if (endEntry.index != null) {
|
||||
const index = endEntry.index < 0 ? childCount + endEntry.index : endEntry.index
|
||||
childAfterFold = children[index]
|
||||
if (!childAfterFold) continue
|
||||
if (endEntry.type && endEntry.type !== childAfterFold.type) continue
|
||||
const child = children[index]
|
||||
if (!child || (endEntry.type && endEntry.type !== child.type)) continue
|
||||
foldEnd = child.startPosition
|
||||
} else {
|
||||
if (!childTypes) childTypes = children.map(child => child.type)
|
||||
let index = childTypes.lastIndexOf(endEntry.type)
|
||||
const index = childTypes.lastIndexOf(endEntry.type)
|
||||
if (index === -1) continue
|
||||
childAfterFold = children[index]
|
||||
foldEnd = children[index].startPosition
|
||||
}
|
||||
}
|
||||
|
||||
if (existenceOnly) return true
|
||||
|
||||
let start, end
|
||||
if (childBeforeFold) {
|
||||
start = childBeforeFold.endPosition
|
||||
} else {
|
||||
start = new Point(node.startPosition.row, Infinity)
|
||||
if (!foldStart) {
|
||||
foldStart = new Point(node.startPosition.row, Infinity)
|
||||
}
|
||||
if (childAfterFold) {
|
||||
end = childAfterFold.startPosition
|
||||
} else {
|
||||
|
||||
if (!foldEnd) {
|
||||
const {endPosition} = node
|
||||
if (endPosition.column === 0) {
|
||||
end = Point(endPosition.row - 1, Infinity)
|
||||
foldEnd = Point(endPosition.row - 1, Infinity)
|
||||
} else if (childCount > 0) {
|
||||
foldEnd = endPosition
|
||||
} else {
|
||||
end = Point(endPosition.row, 0)
|
||||
foldEnd = Point(endPosition.row, 0)
|
||||
}
|
||||
}
|
||||
|
||||
return new Range(start, end)
|
||||
return new Range(foldStart, foldEnd)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user