diff --git a/package.json b/package.json index a91044e1b..0afa64f32 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "2.0.2", + "electronVersion": "2.0.4", "dependencies": { "@atom/nsfw": "^1.0.18", "@atom/watcher": "1.0.3", @@ -92,7 +92,7 @@ "solarized-dark-syntax": "1.1.5", "solarized-light-syntax": "1.1.5", "about": "1.9.1", - "archive-view": "0.65.0", + "archive-view": "0.65.1", "autocomplete-atom-api": "0.10.7", "autocomplete-css": "0.17.5", "autocomplete-html": "0.8.4", @@ -121,7 +121,7 @@ "line-ending-selector": "0.7.7", "link": "0.31.4", "markdown-preview": "0.159.20", - "metrics": "1.3.0", + "metrics": "1.4.3", "notifications": "0.70.5", "open-on-github": "1.3.1", "package-generator": "1.3.0", diff --git a/script/lib/clean-caches.js b/script/lib/clean-caches.js index 1df3aa9c2..3861908bb 100644 --- a/script/lib/clean-caches.js +++ b/script/lib/clean-caches.js @@ -14,6 +14,7 @@ module.exports = function () { path.join(CONFIG.atomHomeDirPath, '.apm'), path.join(CONFIG.atomHomeDirPath, '.npm'), path.join(CONFIG.atomHomeDirPath, 'compile-cache'), + path.join(CONFIG.atomHomeDirPath, 'snapshot-cache'), path.join(CONFIG.atomHomeDirPath, 'atom-shell'), path.join(CONFIG.atomHomeDirPath, 'electron'), path.join(os.tmpdir(), 'atom-build'), diff --git a/spec/atom-reporter.coffee b/spec/atom-reporter.coffee index a522d9298..9af21a7dd 100644 --- a/spec/atom-reporter.coffee +++ b/spec/atom-reporter.coffee @@ -33,6 +33,8 @@ formatStackTrace = (spec, message='', stackTrace) -> line = line.trim() # at jasmine.Spec. (path:1:2) -> at path:1:2 .replace(/^at jasmine\.Spec\. \(([^)]+)\)/, 'at $1') + # at jasmine.Spec.it (path:1:2) -> at path:1:2 + .replace(/^at jasmine\.Spec\.f*it \(([^)]+)\)/, 'at $1') # at it (path:1:2) -> at path:1:2 .replace(/^at f*it \(([^)]+)\)/, 'at $1') # at spec/file-test.js -> at file-test.js diff --git a/spec/workspace-spec.js b/spec/workspace-spec.js index 5d920363a..091588a70 100644 --- a/spec/workspace-spec.js +++ b/spec/workspace-spec.js @@ -1299,6 +1299,34 @@ describe('Workspace', () => { }) }) + describe('the root-scope-used hook', () => { + it('fires when opening a file or changing the grammar of an open file', async () => { + await atom.packages.activatePackage('language-javascript') + await atom.packages.activatePackage('language-coffee-script') + + const observeTextEditorsSpy = jasmine.createSpy('observeTextEditors') + const javascriptGrammarUsed = jasmine.createSpy('javascript') + const coffeeScriptGrammarUsed = jasmine.createSpy('coffeescript') + + atom.packages.triggerDeferredActivationHooks() + atom.packages.onDidTriggerActivationHook('source.js:root-scope-used', () => { + atom.workspace.observeTextEditors(observeTextEditorsSpy) + javascriptGrammarUsed() + }) + atom.packages.onDidTriggerActivationHook('source.coffee:root-scope-used', coffeeScriptGrammarUsed) + + expect(javascriptGrammarUsed).not.toHaveBeenCalled() + expect(observeTextEditorsSpy).not.toHaveBeenCalled() + const editor = await atom.workspace.open('sample.js', {autoIndent: false}) + expect(javascriptGrammarUsed).toHaveBeenCalled() + expect(observeTextEditorsSpy.callCount).toBe(1) + + expect(coffeeScriptGrammarUsed).not.toHaveBeenCalled() + atom.grammars.assignLanguageMode(editor, 'source.coffee') + expect(coffeeScriptGrammarUsed).toHaveBeenCalled() + }) + }) + describe('::reopenItem()', () => { it("opens the uri associated with the last closed pane that isn't currently open", () => { const pane = workspace.getActivePane() diff --git a/src/tree-sitter-language-mode.js b/src/tree-sitter-language-mode.js index 8344c89a5..981b35af1 100644 --- a/src/tree-sitter-language-mode.js +++ b/src/tree-sitter-language-mode.js @@ -5,6 +5,7 @@ const {Emitter, Disposable} = require('event-kit') const ScopeDescriptor = require('./scope-descriptor') const TokenizedLine = require('./tokenized-line') const TextMateLanguageMode = require('./text-mate-language-mode') +const async = require('async') let nextId = 0 const MAX_RANGE = new Range(Point.ZERO, Point.INFINITY).freeze() @@ -30,6 +31,19 @@ class TreeSitterLanguageMode { this.parser = new Parser() this.rootLanguageLayer = new LanguageLayer(this, grammar) this.injectionsMarkerLayer = buffer.addMarkerLayer() + this.updatedGrammars = [] + + this.parsers = [] + this.parseQueue = async.queue(async ({language, oldTree, ranges}, done) => { + const parser = this.parsers.pop() || new Parser() + parser.setLanguage(language) + const newTree = await parser.parseTextBuffer(this.buffer.buffer, oldTree, { + syncOperationLimit: 1000, + includedRanges: ranges + }) + this.parsers.push(parser) + done(null, newTree) + }, 2) this.rootScopeDescriptor = new ScopeDescriptor({scopes: [this.grammar.id]}) this.emitter = new Emitter() @@ -80,6 +94,14 @@ class TreeSitterLanguageMode { } } + parse (language, oldTree, ranges) { + return new Promise(resolve => + this.parseQueue.push({language, oldTree, ranges}, (error, tree) => + resolve(tree) + ) + ) + } + get tree () { return this.rootLanguageLayer.tree } @@ -476,11 +498,10 @@ class LanguageLayer { if (includedRanges.length === 0) return } - this.languageMode.parser.setLanguage(this.grammar.languageModule) - const tree = await this.languageMode.parser.parseTextBuffer( - this.languageMode.buffer.buffer, + const tree = await this.languageMode.parse( + this.grammar.languageModule, this.tree, - {syncOperationLimit: 1000, includedRanges} + includedRanges ) tree.buffer = this.languageMode.buffer @@ -566,9 +587,11 @@ class LanguageLayer { } } + const promises = [] for (const [marker, injectionNode] of markersToUpdate) { - await marker.languageLayer.update(injectionNode) + promises.push(marker.languageLayer.update(injectionNode)) } + return Promise.all(promises) } /** diff --git a/src/workspace.js b/src/workspace.js index 00976759f..a3f85ddeb 100644 --- a/src/workspace.js +++ b/src/workspace.js @@ -1268,7 +1268,8 @@ module.exports = class Workspace extends Model { handleGrammarUsed (grammar) { if (grammar == null) { return } - return this.packageManager.triggerActivationHook(`${grammar.packageName}:grammar-used`) + this.packageManager.triggerActivationHook(`${grammar.scopeName}:root-scope-used`) + this.packageManager.triggerActivationHook(`${grammar.packageName}:grammar-used`) } // Public: Returns a {Boolean} that is `true` if `object` is a `TextEditor`.