Merge master.

This commit is contained in:
Ashi Krishnan
2018-07-24 13:51:52 -04:00
12 changed files with 816 additions and 149 deletions

View File

@@ -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'))

View File

@@ -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'

View File

@@ -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>
\`
`