mirror of
https://github.com/atom/atom.git
synced 2026-04-28 03:01:47 -04:00
Merge master.
This commit is contained in:
@@ -969,6 +969,67 @@ describe('Project', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('.observeRepositories()', () => {
|
||||
it('invokes the observer with current and future repositories', () => {
|
||||
const observed = []
|
||||
|
||||
const directory1 = temp.mkdirSync('git-repo1')
|
||||
const gitDirPath1 = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git'))
|
||||
fs.copySync(gitDirPath1, path.join(directory1, '.git'))
|
||||
|
||||
const directory2 = temp.mkdirSync('git-repo2')
|
||||
const gitDirPath2 = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'repo-with-submodules', 'git.git'))
|
||||
fs.copySync(gitDirPath2, path.join(directory2, '.git'))
|
||||
|
||||
atom.project.setPaths([directory1])
|
||||
|
||||
const disposable = atom.project.observeRepositories((repo) => observed.push(repo))
|
||||
expect(observed.length).toBe(1)
|
||||
expect(observed[0].getReferenceTarget('refs/heads/master')).toBe('ef046e9eecaa5255ea5e9817132d4001724d6ae1')
|
||||
|
||||
atom.project.addPath(directory2)
|
||||
expect(observed.length).toBe(2)
|
||||
expect(observed[1].getReferenceTarget('refs/heads/master')).toBe('d2b0ad9cbc6f6c4372e8956e5cc5af771b2342e5')
|
||||
|
||||
disposable.dispose()
|
||||
})
|
||||
})
|
||||
|
||||
describe('.onDidAddRepository()', () => {
|
||||
it('invokes callback when a path is added and the path is the root of a repository', () => {
|
||||
const observed = []
|
||||
const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo))
|
||||
|
||||
const repositoryPath = path.join(__dirname, '..')
|
||||
atom.project.addPath(repositoryPath)
|
||||
expect(observed.length).toBe(1)
|
||||
expect(observed[0].getOriginURL()).toContain('atom/atom')
|
||||
|
||||
disposable.dispose()
|
||||
})
|
||||
|
||||
it('invokes callback when a path is added and the path is subdirectory of a repository', () => {
|
||||
const observed = []
|
||||
const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo))
|
||||
|
||||
atom.project.addPath(__dirname)
|
||||
expect(observed.length).toBe(1)
|
||||
expect(observed[0].getOriginURL()).toContain('atom/atom')
|
||||
|
||||
disposable.dispose()
|
||||
})
|
||||
|
||||
it('does not invoke callback when a path is added and the path is not part of a repository', () => {
|
||||
const observed = []
|
||||
const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo))
|
||||
|
||||
atom.project.addPath(temp.mkdirSync('not-a-repository'))
|
||||
expect(observed.length).toBe(0)
|
||||
|
||||
disposable.dispose()
|
||||
})
|
||||
})
|
||||
|
||||
describe('.relativize(path)', () => {
|
||||
it('returns the path, relative to whichever root directory it is inside of', () => {
|
||||
atom.project.addPath(temp.mkdirSync('another-path'))
|
||||
|
||||
@@ -818,6 +818,18 @@ describe('TextEditorComponent', () => {
|
||||
expect(element.className).toBe('editor a b')
|
||||
})
|
||||
|
||||
it('does not blow away class names managed by the component when packages change the element class name', async () => {
|
||||
assertDocumentFocused()
|
||||
const {component, element, editor} = buildComponent({mini: true})
|
||||
element.classList.add('a', 'b')
|
||||
element.focus()
|
||||
await component.getNextUpdatePromise()
|
||||
expect(element.className).toBe('editor mini a b is-focused')
|
||||
element.className = 'a c d';
|
||||
await component.getNextUpdatePromise()
|
||||
expect(element.className).toBe('a c d editor is-focused mini')
|
||||
})
|
||||
|
||||
it('ignores resize events when the editor is hidden', async () => {
|
||||
const {component, element, editor} = buildComponent({autoHeight: false})
|
||||
element.style.height = 5 * component.getLineHeight() + 'px'
|
||||
|
||||
@@ -12,6 +12,7 @@ const pythonGrammarPath = require.resolve('language-python/grammars/tree-sitter-
|
||||
const jsGrammarPath = require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')
|
||||
const htmlGrammarPath = require.resolve('language-html/grammars/tree-sitter-html.cson')
|
||||
const ejsGrammarPath = require.resolve('language-html/grammars/tree-sitter-ejs.cson')
|
||||
const rubyGrammarPath = require.resolve('language-ruby/grammars/tree-sitter-ruby.cson')
|
||||
|
||||
describe('TreeSitterLanguageMode', () => {
|
||||
let editor, buffer
|
||||
@@ -346,6 +347,38 @@ describe('TreeSitterLanguageMode', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('handles nodes that start before their first child and end after their last child', async () => {
|
||||
const grammar = new TreeSitterGrammar(atom.grammars, rubyGrammarPath, {
|
||||
parser: 'tree-sitter-ruby',
|
||||
scopes: {
|
||||
'bare_string': 'string',
|
||||
'interpolation': 'embedded',
|
||||
'"#{"': 'punctuation',
|
||||
'"}"': 'punctuation',
|
||||
}
|
||||
})
|
||||
|
||||
// The bare string node `bc#{d}ef` has one child: the interpolation, and that child
|
||||
// starts later and ends earlier than the bare string.
|
||||
buffer.setText('a = %W( bc#{d}ef )')
|
||||
|
||||
const languageMode = new TreeSitterLanguageMode({buffer, grammar})
|
||||
buffer.setLanguageMode(languageMode)
|
||||
await nextHighlightingUpdate(languageMode)
|
||||
|
||||
expectTokensToEqual(editor, [
|
||||
[
|
||||
{text: 'a = %W( ', scopes: []},
|
||||
{text: 'bc', scopes: ['string']},
|
||||
{text: '#{', scopes: ['string', 'embedded', 'punctuation']},
|
||||
{text: 'd', scopes: ['string', 'embedded']},
|
||||
{text: '}', scopes: ['string', 'embedded', 'punctuation']},
|
||||
{text: 'ef', scopes: ['string']},
|
||||
{text: ' )', scopes: []},
|
||||
]
|
||||
])
|
||||
})
|
||||
|
||||
describe('when the buffer changes during a parse', () => {
|
||||
it('immediately parses again when the current parse completes', async () => {
|
||||
const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, {
|
||||
@@ -707,7 +740,7 @@ describe('TreeSitterLanguageMode', () => {
|
||||
expect(getDisplayText(editor)).toBe(dedent `
|
||||
module.exports =
|
||||
class A {
|
||||
getB (…) {
|
||||
getB (c,…) {
|
||||
return this.f(g)
|
||||
}
|
||||
}
|
||||
@@ -717,11 +750,60 @@ describe('TreeSitterLanguageMode', () => {
|
||||
expect(getDisplayText(editor)).toBe(dedent `
|
||||
module.exports =
|
||||
class A {
|
||||
getB (…) {…}
|
||||
getB (c,…) {…}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
it('folds entire buffer rows when necessary to keep words on separate lines', async () => {
|
||||
const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, {
|
||||
parser: 'tree-sitter-javascript',
|
||||
folds: [
|
||||
{
|
||||
start: {type: '{', index: 0},
|
||||
end: {type: '}', index: -1}
|
||||
},
|
||||
{
|
||||
start: {type: '(', index: 0},
|
||||
end: {type: ')', index: -1}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
buffer.setText(dedent `
|
||||
if (a) {
|
||||
b
|
||||
} else if (c) {
|
||||
d
|
||||
} else {
|
||||
e
|
||||
}
|
||||
`)
|
||||
|
||||
const languageMode = new TreeSitterLanguageMode({buffer, grammar})
|
||||
buffer.setLanguageMode(languageMode)
|
||||
await nextHighlightingUpdate(languageMode)
|
||||
|
||||
// Avoid bringing the `else if...` up onto the same screen line as the preceding `if`.
|
||||
editor.foldBufferRow(1)
|
||||
editor.foldBufferRow(3)
|
||||
expect(getDisplayText(editor)).toBe(dedent `
|
||||
if (a) {…
|
||||
} else if (c) {…
|
||||
} else {
|
||||
e
|
||||
}
|
||||
`)
|
||||
|
||||
// It's ok to bring the final `}` onto the same screen line as the preceding `else`.
|
||||
editor.foldBufferRow(5)
|
||||
expect(getDisplayText(editor)).toBe(dedent `
|
||||
if (a) {…
|
||||
} else if (c) {…
|
||||
} else {…}
|
||||
`)
|
||||
})
|
||||
|
||||
it('can fold nodes of specified types', async () => {
|
||||
const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, {
|
||||
parser: 'tree-sitter-javascript',
|
||||
@@ -975,6 +1057,93 @@ describe('TreeSitterLanguageMode', () => {
|
||||
`)
|
||||
})
|
||||
|
||||
it('can target named vs anonymous nodes as fold boundaries', async () => {
|
||||
const grammar = new TreeSitterGrammar(atom.grammars, rubyGrammarPath, {
|
||||
parser: 'tree-sitter-ruby',
|
||||
folds: [
|
||||
{
|
||||
type: 'elsif',
|
||||
start: {index: 1},
|
||||
|
||||
// There are no double quotes around the `elsif` type. This indicates
|
||||
// that we're targeting a *named* node in the syntax tree. The fold
|
||||
// should end at the nested `elsif` node, not at the token that represents
|
||||
// the literal string "elsif".
|
||||
end: {type: ['else', 'elsif']}
|
||||
},
|
||||
{
|
||||
type: 'else',
|
||||
|
||||
// There are double quotes around the `else` type. This indicates that
|
||||
// we're targetting an *anonymous* node in the syntax tree. The fold
|
||||
// should start at the token representing the literal string "else",
|
||||
// not at an `else` node.
|
||||
start: {type: '"else"'}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
buffer.setText(dedent `
|
||||
if a
|
||||
b
|
||||
elsif c
|
||||
d
|
||||
elsif e
|
||||
f
|
||||
else
|
||||
g
|
||||
end
|
||||
`)
|
||||
|
||||
const languageMode = new TreeSitterLanguageMode({buffer, grammar})
|
||||
buffer.setLanguageMode(languageMode)
|
||||
await nextHighlightingUpdate(languageMode)
|
||||
|
||||
expect(languageMode.tree.rootNode.toString()).toBe(
|
||||
"(program (if (identifier) " +
|
||||
"(identifier) " +
|
||||
"(elsif (identifier) " +
|
||||
"(identifier) " +
|
||||
"(elsif (identifier) " +
|
||||
"(identifier) " +
|
||||
"(else " +
|
||||
"(identifier))))))"
|
||||
)
|
||||
|
||||
editor.foldBufferRow(2)
|
||||
expect(getDisplayText(editor)).toBe(dedent `
|
||||
if a
|
||||
b
|
||||
elsif c…
|
||||
elsif e
|
||||
f
|
||||
else
|
||||
g
|
||||
end
|
||||
`)
|
||||
|
||||
editor.foldBufferRow(4)
|
||||
expect(getDisplayText(editor)).toBe(dedent `
|
||||
if a
|
||||
b
|
||||
elsif c…
|
||||
elsif e…
|
||||
else
|
||||
g
|
||||
end
|
||||
`)
|
||||
|
||||
editor.foldBufferRow(6)
|
||||
expect(getDisplayText(editor)).toBe(dedent `
|
||||
if a
|
||||
b
|
||||
elsif c…
|
||||
elsif e…
|
||||
else…
|
||||
end
|
||||
`)
|
||||
})
|
||||
|
||||
describe('when folding a node that ends with a line break', () => {
|
||||
it('ends the fold at the end of the previous line', async () => {
|
||||
const grammar = new TreeSitterGrammar(atom.grammars, pythonGrammarPath, {
|
||||
@@ -1065,7 +1234,8 @@ describe('TreeSitterLanguageMode', () => {
|
||||
expect(getDisplayText(editor)).toBe(
|
||||
`a = html \`
|
||||
<div>
|
||||
c\${def(…)}e\${f}g
|
||||
c\${def(…
|
||||
)}e\${f}g
|
||||
</div>
|
||||
\`
|
||||
`
|
||||
|
||||
Reference in New Issue
Block a user