diff --git a/Gruntfile.coffee b/Gruntfile.coffee index b8d215040..f0acb96b8 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -151,7 +151,7 @@ module.exports = (grunt) -> shell: 'kill-atom': - command: 'pkill Atom' + command: 'pkill -9 Atom' options: stdout: false stderr: false diff --git a/package.json b/package.json index b93a3564a..b1c29e43f 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "bugs": { "url": "https://github.com/atom/atom/issues" }, - "atomShellVersion": "0.4.9", + "atomShellVersion": "0.5.1", "dependencies": { "async": "0.2.6", "bootstrap": "git://github.com/twbs/bootstrap.git#v3.0.0", @@ -22,41 +22,40 @@ "mkdirp": "0.3.5", "less": "git://github.com/nathansobo/less.js.git", "less-cache": "0.8.0", - "nak": "0.2.18", "nslog": "0.1.0", "oniguruma": "0.20.0", "optimist": "0.4.0", "pathwatcher": "0.5.0", - "patrick": "0.4.0", "pegjs": "0.7.0", "plist": "git://github.com/nathansobo/node-plist.git", "rimraf": "2.1.4", + "scandal": "0.2.0", "season": "0.13.0", "semver": "1.1.4", "space-pen": "1.2.0", "tantamount": "0.3.0", - "telepath": "0.4.0", + "telepath": "0.6.0", "temp": "0.5.0", "underscore": "1.4.4", - "atom-light-ui": "0.2.1", - "atom-light-syntax": "0.2.0", - "atom-dark-ui": "0.2.0", - "atom-dark-syntax": "0.2.0", + "atom-light-ui": "0.3.0", + "atom-light-syntax": "0.3.0", + "atom-dark-ui": "0.3.0", + "atom-dark-syntax": "0.3.0", "base16-tomorrow-dark-theme": "0.1.0", - "solarized-dark-syntax": "0.1.0", + "solarized-dark-syntax": "0.2.0", "archive-view": "0.7.0", "autocomplete": "0.5.0", "autoflow": "0.2.0", "bookmarks": "0.3.0", "bracket-matcher": "0.4.0", - "collaboration": "0.11.0", + "collaboration": "0.16.0", "command-logger": "0.3.0", "command-palette": "0.3.0", "editor-stats": "0.2.0", "exception-reporting": "0.1.0", - "find-and-replace": "0.11.0", + "find-and-replace": "0.16.0", "fuzzy-finder": "0.5.0", "gfm": "0.4.0", "git-diff": "0.3.0", @@ -67,16 +66,16 @@ "image-view": "0.5.0", "link": "0.2.0", "markdown-preview": "0.3.0", - "metrics": "0.1.1", + "metrics": "0.3.0", "package-generator": "0.8.0", - "settings-view": "0.22.0", + "settings-view": "0.23.0", "snippets": "0.5.0", "spell-check": "0.5.0", "status-bar": "0.7.0", "symbols-view": "0.5.0", "tabs": "0.4.0", "terminal": "0.9.0", - "timecop": "0.2.0", + "timecop": "0.4.0", "to-the-hubs": "0.3.0", "toml": "0.2.0", "tree-view": "0.6.0", @@ -118,6 +117,7 @@ }, "devDependencies": { "biscotto": "0.0.17", + "fstream": "0.1.24", "grunt": "~0.4.1", "grunt-cli": "~0.1.9", "grunt-coffeelint": "0.0.6", @@ -138,6 +138,6 @@ "private": true, "scripts": { "preinstall": "true", - "test": "grunt test" + "test": "script/test" } } diff --git a/spec/atom-reporter.coffee b/spec/atom-reporter.coffee index 7c2203c51..099a78f1f 100644 --- a/spec/atom-reporter.coffee +++ b/spec/atom-reporter.coffee @@ -92,7 +92,7 @@ class AtomReporter extends View clearTimeout @timeoutId if @timeoutId? @specPopup.show() - spec = _.find(window.timedSpecs, (spec) -> description is spec.name) + spec = _.find(window.timedSpecs, ({fullName}) -> description is fullName) description = "#{description} #{spec.time}ms" if spec @specPopup.text description {left, top} = element.offset() diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index 797f63d62..3890f1448 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -63,6 +63,15 @@ describe "Editor", -> expect(editor).not.toMatchSelector ':focus' expect(editor.hiddenInput).toMatchSelector ':focus' + it "does not scroll the editor (regression)", -> + editor.attachToDom(heightInLines: 2) + editor.selectAll() + editor.hiddenInput.blur() + editor.focus() + + expect(editor.hiddenInput).toMatchSelector ':focus' + expect($(editor[0]).scrollTop()).toBe 0 + describe "when the hidden input is focused / unfocused", -> it "assigns the isFocused flag on the editor and also adds/removes the .focused css class", -> editor.attachToDom() diff --git a/spec/fixtures/git/working-dir/.gitignore b/spec/fixtures/git/working-dir/.gitignore index 09f5ff762..23238eafc 100644 --- a/spec/fixtures/git/working-dir/.gitignore +++ b/spec/fixtures/git/working-dir/.gitignore @@ -1 +1,2 @@ -ignored.txt \ No newline at end of file +poop +ignored.txt diff --git a/spec/fixtures/git/working-dir/git.git/HEAD b/spec/fixtures/git/working-dir/git.git/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/spec/fixtures/git/working-dir/git.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/spec/fixtures/git/working-dir/git.git/config b/spec/fixtures/git/working-dir/git.git/config new file mode 100644 index 000000000..af107929f --- /dev/null +++ b/spec/fixtures/git/working-dir/git.git/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true diff --git a/spec/fixtures/git/working-dir/git.git/index b/spec/fixtures/git/working-dir/git.git/index new file mode 100644 index 000000000..bf35b18cd Binary files /dev/null and b/spec/fixtures/git/working-dir/git.git/index differ diff --git a/spec/fixtures/git/working-dir/git.git/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 b/spec/fixtures/git/working-dir/git.git/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 new file mode 100644 index 000000000..ba1f06fc0 Binary files /dev/null and b/spec/fixtures/git/working-dir/git.git/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 differ diff --git a/spec/fixtures/git/working-dir/git.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/spec/fixtures/git/working-dir/git.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 new file mode 100644 index 000000000..711223894 Binary files /dev/null and b/spec/fixtures/git/working-dir/git.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 differ diff --git a/spec/fixtures/git/working-dir/git.git/objects/ef/046e9eecaa5255ea5e9817132d4001724d6ae1 b/spec/fixtures/git/working-dir/git.git/objects/ef/046e9eecaa5255ea5e9817132d4001724d6ae1 new file mode 100644 index 000000000..eaf6eeff3 Binary files /dev/null and b/spec/fixtures/git/working-dir/git.git/objects/ef/046e9eecaa5255ea5e9817132d4001724d6ae1 differ diff --git a/spec/fixtures/git/working-dir/git.git/refs/heads/master b/spec/fixtures/git/working-dir/git.git/refs/heads/master new file mode 100644 index 000000000..6134b5707 --- /dev/null +++ b/spec/fixtures/git/working-dir/git.git/refs/heads/master @@ -0,0 +1 @@ +ef046e9eecaa5255ea5e9817132d4001724d6ae1 diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 2a972c4ca..26966b2a5 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -1,3 +1,4 @@ +temp = require 'temp' Git = require '../src/git' {fs} = require 'atom' path = require 'path' diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 0918c755f..5b37a9766 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -1,3 +1,5 @@ +temp = require 'temp' +fstream = require 'fstream' Project = require '../src/project' {_, fs} = require 'atom' path = require 'path' @@ -232,33 +234,37 @@ describe "Project", -> describe ".scan(options, callback)", -> describe "when called with a regex", -> - it "calls the callback with all regex matches in all files in the project", -> - matches = [] + it "calls the callback with all regex results in all files in the project", -> + results = [] waitsForPromise -> - project.scan /(a)+/, (match) -> matches.push(match) + project.scan /(a)+/, (result) -> + results.push(result) runs -> - expect(matches[0]).toEqual - path: project.resolve('a') - match: 'aaa' + expect(results).toHaveLength(3) + expect(results[0].filePath).toBe project.resolve('a') + expect(results[0].matches).toHaveLength(3) + expect(results[0].matches[0]).toEqual + matchText: 'aaa' + lineText: 'aaa bbb' + lineTextOffset: 0 range: [[0, 0], [0, 3]] - expect(matches[1]).toEqual - path: project.resolve('a') - match: 'aa' - range: [[1, 3], [1, 5]] - it "works with with escaped literals (like $ and ^)", -> - matches = [] + results = [] waitsForPromise -> - project.scan /\$\w+/, (match) -> matches.push(match) + project.scan /\$\w+/, (result) -> results.push(result) runs -> - expect(matches.length).toBe 1 + expect(results.length).toBe 1 + {filePath, matches} = results[0] + expect(filePath).toBe project.resolve('a') + expect(matches).toHaveLength 1 expect(matches[0]).toEqual - path: project.resolve('a') - match: '$bill' + matchText: '$bill' + lineText: 'dollar$bill' + lineTextOffset: 0 range: [[2, 6], [2, 11]] it "works on evil filenames", -> @@ -267,12 +273,12 @@ describe "Project", -> matches = [] waitsForPromise -> project.scan /evil/, (result) -> - paths.push(result.path) - matches.push(result.match) + paths.push(result.filePath) + matches = matches.concat(result.matches) runs -> expect(paths.length).toBe 5 - matches.forEach (match) -> expect(match).toEqual 'evil' + matches.forEach (match) -> expect(match.matchText).toEqual 'evil' expect(paths[0]).toMatch /a_file_with_utf8.txt$/ expect(paths[1]).toMatch /file with spaces.txt$/ expect(paths[2]).toMatch /goddam\nnewlines$/m @@ -280,57 +286,63 @@ describe "Project", -> expect(path.basename(paths[4])).toBe "utfa\u0306.md" it "ignores case if the regex includes the `i` flag", -> - matches = [] + results = [] waitsForPromise -> - project.scan /DOLLAR/i, (match) -> matches.push(match) + project.scan /DOLLAR/i, (result) -> results.push(result) runs -> - expect(matches).toHaveLength 1 - - it "handles breaks in the search subprocess's output following the filename", -> - spyOn(BufferedProcess.prototype, 'bufferStream') - - iterator = jasmine.createSpy('iterator') - project.scan /a+/, iterator - - stdout = BufferedProcess.prototype.bufferStream.argsForCall[0][1] - stdout ":#{path.join(__dirname, 'fixtures', 'dir', 'a')}\n" - stdout "1;0 3:aaa bbb\n2;3 2:cc aa cc\n" - - expect(iterator.argsForCall[0][0]).toEqual - path: project.resolve('a') - match: 'aaa' - range: [[0, 0], [0, 3]] - - expect(iterator.argsForCall[1][0]).toEqual - path: project.resolve('a') - match: 'aa' - range: [[1, 3], [1, 5]] + expect(results).toHaveLength 1 describe "when the core.excludeVcsIgnoredPaths config is truthy", -> [projectPath, ignoredPath] = [] beforeEach -> - projectPath = path.join(__dirname, 'fixtures', 'git', 'working-dir') - ignoredPath = path.join(projectPath, 'ignored.txt') - fs.writeSync(ignoredPath, 'this match should not be included') + sourceProjectPath = path.join(__dirname, 'fixtures', 'git', 'working-dir') + projectPath = path.join(temp.mkdirSync("atom")) + + writerStream = fstream.Writer(projectPath) + fstream.Reader(sourceProjectPath).pipe(writerStream) + + waitsFor (done) -> + writerStream.on 'close', done + writerStream.on 'error', done + + runs -> + fs.rename(path.join(projectPath, 'git.git'), path.join(projectPath, '.git')) + ignoredPath = path.join(projectPath, 'ignored.txt') + fs.writeSync(ignoredPath, 'this match should not be included') afterEach -> - fs.remove(ignoredPath) if fs.exists(ignoredPath) + fs.remove(projectPath) if fs.exists(projectPath) it "excludes ignored files", -> project.setPath(projectPath) config.set('core.excludeVcsIgnoredPaths', true) - paths = [] - matches = [] + resultHandler = jasmine.createSpy("result found") waitsForPromise -> - project.scan /match/, (result) -> - paths.push(result.path) - matches.push(result.match) + project.scan /match/, (results) -> + resultHandler() runs -> - expect(paths.length).toBe 0 - expect(matches.length).toBe 0 + expect(resultHandler).not.toHaveBeenCalled() + + it "includes only files when a directory filter is specified", -> + projectPath = path.join(path.join(__dirname, 'fixtures', 'dir')) + project.setPath(projectPath) + + filePath = path.join(projectPath, 'a-dir', 'oh-git') + + paths = [] + matches = [] + waitsForPromise -> + project.scan /aaa/, paths: ['a-dir/'], (result) -> + paths.push(result.filePath) + matches = matches.concat(result.matches) + + runs -> + expect(paths.length).toBe 1 + expect(paths[0]).toBe filePath + expect(matches.length).toBe 1 it "includes files and folders that begin with a '.'", -> projectPath = '/tmp/atom-tests/folder-with-dot-file' @@ -341,8 +353,8 @@ describe "Project", -> matches = [] waitsForPromise -> project.scan /match this/, (result) -> - paths.push(result.path) - matches.push(result.match) + paths.push(result.filePath) + matches = matches.concat(result.matches) runs -> expect(paths.length).toBe 1 @@ -350,17 +362,16 @@ describe "Project", -> expect(matches.length).toBe 1 it "excludes values in core.ignoredNames", -> - projectPath = '/tmp/atom-tests/folder-with-dot-git/.git' - filePath = path.join(projectPath, 'test.txt') - fs.writeSync(filePath, 'match this') - project.setPath(projectPath) - paths = [] - matches = [] + projectPath = path.join(__dirname, 'fixtures', 'git', 'working-dir') + ignoredNames = config.get("core.ignoredNames") + ignoredNames.push("a") + config.set("core.ignoredNames", ignoredNames) + + resultHandler = jasmine.createSpy("result found") waitsForPromise -> - project.scan /match/, (result) -> - paths.push(result.path) - matches.push(result.match) + project.scan /dollar/, (results) -> + console.log results + resultHandler() runs -> - expect(paths.length).toBe 0 - expect(matches.length).toBe 0 + expect(resultHandler).not.toHaveBeenCalled() diff --git a/spec/text-buffer-spec.coffee b/spec/text-buffer-spec.coffee index bb715a467..ec6efb63f 100644 --- a/spec/text-buffer-spec.coffee +++ b/spec/text-buffer-spec.coffee @@ -602,6 +602,17 @@ describe 'TextBuffer', -> it "clips the range to the end of the buffer", -> expect(buffer.getTextInRange([[12], [13, Infinity]])).toBe buffer.lineForRow(12) + describe ".scan(regex, fn)", -> + it "retunrns lineText and lineTextOffset", -> + matches = [] + buffer.scan /current/, (match) -> + matches.push(match) + expect(matches.length).toBe 1 + + expect(matches[0].matchText).toEqual 'current' + expect(matches[0].lineText).toEqual ' var pivot = items.shift(), current, left = [], right = [];' + expect(matches[0].lineTextOffset).toBe 0 + describe ".scanInRange(range, regex, fn)", -> describe "when given a regex with a ignore case flag", -> it "does a case-insensitive search", -> diff --git a/spec/time-reporter.coffee b/spec/time-reporter.coffee index 49ef984c0..f5555f392 100644 --- a/spec/time-reporter.coffee +++ b/spec/time-reporter.coffee @@ -53,6 +53,7 @@ class TimeReporter extends jasmine.Reporter window.timedSpecs.push description: @description time: duration + fullName: spec.getFullName() if timedSuites[@suite] window.timedSuites[@suite] += duration diff --git a/src/atom-application.coffee b/src/atom-application.coffee index 9603c36f4..690c21332 100644 --- a/src/atom-application.coffee +++ b/src/atom-application.coffee @@ -93,8 +93,8 @@ class AtomApplication fs.unlinkSync socketPath if fs.existsSync(socketPath) server = net.createServer (connection) => connection.on 'data', (data) => - {pathsToOpen, pidToKillWhenClosed, newWindow} = JSON.parse(data) - @openPaths({pathsToOpen, pidToKillWhenClosed, newWindow}) + options = JSON.parse(data) + @openPaths(options) server.listen socketPath server.on 'error', (error) -> console.error 'Application server failed', error diff --git a/src/atom-package.coffee b/src/atom-package.coffee index ced29a9df..1074eca48 100644 --- a/src/atom-package.coffee +++ b/src/atom-package.coffee @@ -28,41 +28,44 @@ class AtomPackage extends Package getType: -> 'atom' load: -> - try - @metadata = Package.loadMetadata(@path) - if @isTheme() - @stylesheets = [] - @keymaps = [] - @menus = [] - @grammars = [] - @scopedProperties = [] - else - @loadKeymaps() - @loadMenus() - @loadStylesheets() - @loadGrammars() - @loadScopedProperties() - - if @metadata.activationEvents? - @registerDeferredDeserializers() + @measure 'loadTime', => + try + @metadata = Package.loadMetadata(@path) + if @isTheme() + @stylesheets = [] + @keymaps = [] + @menus = [] + @grammars = [] + @scopedProperties = [] else - @requireMainModule() + @loadKeymaps() + @loadMenus() + @loadStylesheets() + @loadGrammars() + @loadScopedProperties() - catch e - console.warn "Failed to load package named '#{@name}'", e.stack ? e + if @metadata.activationEvents? + @registerDeferredDeserializers() + else + @requireMainModule() + + catch e + console.warn "Failed to load package named '#{@name}'", e.stack ? e this activate: ({immediate}={}) -> - @loadStylesheets() if @isTheme() - @activateResources() - if @metadata.activationEvents? and not immediate - @subscribeToActivationEvents() - else - @activateNow() + @measure 'activateTime', => + @loadStylesheets() if @isTheme() + @activateResources() + if @metadata.activationEvents? and not immediate + @subscribeToActivationEvents() + else + @activateNow() activateNow: -> try @activateConfig() + @activateStylesheets() if @requireMainModule() @mainModule.activate(atom.getPackageState(@name) ? {}) @mainActivated = true @@ -78,11 +81,13 @@ class AtomPackage extends Package @mainModule.activateConfig?() @configActivated = true + activateStylesheets: -> + type = if @metadata.theme then 'theme' else 'bundled' + applyStylesheet(stylesheetPath, content, type) for [stylesheetPath, content] in @stylesheets + activateResources: -> keymap.add(keymapPath, map) for [keymapPath, map] in @keymaps atom.contextMenu.add(menuPath, map['context-menu']) for [menuPath, map] in @menus - type = if @metadata.theme then 'theme' else 'bundled' - applyStylesheet(stylesheetPath, content, type) for [stylesheetPath, content] in @stylesheets syntax.addGrammar(grammar) for grammar in @grammars for [scopedPropertiesPath, selector, properties] in @scopedProperties syntax.addProperties(scopedPropertiesPath, selector, properties) diff --git a/src/atom-window.coffee b/src/atom-window.coffee index 3da832046..5da1d3b2e 100644 --- a/src/atom-window.coffee +++ b/src/atom-window.coffee @@ -42,10 +42,7 @@ class AtomWindow @openPath(pathToOpen, initialLine) setupNodePath: (resourcePath) -> - paths = [ - 'exports' - 'node_modules' - ] + paths = ['exports', 'node_modules'] paths = paths.map (relativePath) -> path.resolve(resourcePath, relativePath) process.env['NODE_PATH'] = paths.join path.delimiter diff --git a/src/atom.coffee b/src/atom.coffee index 63d395284..12e9924fe 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -8,7 +8,7 @@ crypto = require 'crypto' path = require 'path' dialog = remote.require 'dialog' app = remote.require 'app' -telepath = require 'telepath' +{Document} = require 'telepath' ThemeManager = require './theme-manager' ContextMenuManager = require './context-menu-manager' @@ -34,9 +34,7 @@ window.atom = activatePackage: (name, options) -> if pack = @loadPackage(name, options) @activePackages[pack.name] = pack - startTime = new Date().getTime() pack.activate(options) - pack.activateTime = new Date().getTime() - startTime pack deactivatePackages: -> @@ -73,9 +71,7 @@ window.atom = if packagePath = @resolvePackagePath(name) return pack if pack = @getLoadedPackage(name) - startTime = new Date().getTime() pack = Package.load(packagePath, options) - pack.loadTime = new Date().getTime() - startTime if pack.metadata.theme @themes.register(pack) else @@ -249,34 +245,28 @@ window.atom = if windowStatePath = @getWindowStatePath() if fsUtils.exists(windowStatePath) try - windowStateJson = fsUtils.read(windowStatePath) + documentStateJson = fsUtils.read(windowStatePath) catch error console.warn "Error reading window state: #{windowStatePath}", error.stack, error else - windowStateJson = @getLoadSettings().windowState + documentStateJson = @getLoadSettings().windowState try - windowState = JSON.parse(windowStateJson or '{}') + documentState = JSON.parse(documentStateJson) if documentStateJson? catch error console.warn "Error parsing window state: #{windowStatePath}", error.stack, error - {site, document} = windowState ? {} - if site? and document? - window.site = telepath.Site.deserialize(site) - window.site.deserializeDocument(document) ? window.site.createDocument({}) - else - window.site = new telepath.Site(1) - window.site.createDocument({}) + doc = Document.deserialize(state: documentState) if documentState? + doc ?= Document.create() + window.site = doc.site # TODO: Remove this when everything is using telepath models + doc saveWindowState: -> - windowState = - site: site.serialize() - document: @getWindowState().serialize() - windowStateJson = JSON.stringify(windowState) + windowState = @getWindowState() if windowStatePath = @getWindowStatePath() - fsUtils.writeSync(windowStatePath, "#{windowStateJson}\n") + windowState.saveSync(path: windowStatePath) else - @getLoadSettings().windowState = windowStateJson + @getLoadSettings().windowState = JSON.stringify(windowState.serialize()) getWindowState: (keyPath) -> @windowState ?= @loadWindowState() diff --git a/src/edit-session.coffee b/src/edit-session.coffee index 7490f7ca3..558d926d4 100644 --- a/src/edit-session.coffee +++ b/src/edit-session.coffee @@ -117,6 +117,7 @@ class EditSession project.setPath(path.dirname(@getPath())) unless project.getPath()? @trigger "title-changed" @trigger "path-changed" + @subscribe @buffer, "contents-modified", => @trigger "contents-modified" @subscribe @buffer, "contents-conflicted", => @trigger "contents-conflicted" @subscribe @buffer, "modified-status-changed", => @trigger "modified-status-changed" @preserveCursorPositionOnBufferReload() @@ -376,6 +377,9 @@ class EditSession # {Delegates to: TextBuffer.lineLengthForRow} lineLengthForBufferRow: (row) -> @buffer.lineLengthForRow(row) + # {Delegates to: TextBuffer.scan} + scan: (args...) -> @buffer.scan(args...) + # {Delegates to: TextBuffer.scanInRange} scanInBufferRange: (args...) -> @buffer.scanInRange(args...) diff --git a/src/editor.coffee b/src/editor.coffee index 6f5a505ee..028655cfd 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -640,6 +640,7 @@ class Editor extends View @hiddenInput.on 'focusout', => @isFocused = false @removeClass 'is-focused' + @hiddenInput.offset(top: 0, left: 0) @underlayer.on 'mousedown', (e) => @renderedLines.trigger(e) diff --git a/src/event-emitter.coffee b/src/event-emitter.coffee index 83eb6da95..0b6224707 100644 --- a/src/event-emitter.coffee +++ b/src/event-emitter.coffee @@ -105,8 +105,11 @@ module.exports = # Identifies how many events are registered. # # Returns a {Number}. - subscriptionCount: -> + getSubscriptionCount: -> count = 0 for name, handlers of @eventHandlersByEventName count += handlers.length count + + # Deprecated + subscriptionCount: -> @getSubscriptionCount() diff --git a/src/main.coffee b/src/main.coffee index fda609044..2e42f9888 100644 --- a/src/main.coffee +++ b/src/main.coffee @@ -1,13 +1,15 @@ +startTime = new Date().getTime() + autoUpdater = require 'auto-updater' crashReporter = require 'crash-reporter' delegate = require 'atom-delegate' app = require 'app' fs = require 'fs' +module = require 'module' path = require 'path' optimist = require 'optimist' nslog = require 'nslog' dialog = require 'dialog' -_ = require 'underscore' console.log = (args...) -> nslog(args.map((arg) -> JSON.stringify(arg)).join(" ")) @@ -45,14 +47,15 @@ delegate.browserMainParts.preMainMessageLoopRun = -> require('coffee-script') if args.devMode require(path.join(args.resourcePath, 'src', 'coffee-cache')) - require('module').globalPaths.push(path.join(args.resourcePath, 'src')) + module.globalPaths.push(path.join(args.resourcePath, 'src')) else appSrcPath = path.resolve(process.argv[0], "../../Resources/app/src") - require('module').globalPaths.push(appSrcPath) + module.globalPaths.push(appSrcPath) AtomApplication = require 'atom-application' AtomApplication.open(args) + console.log("App load time: #{new Date().getTime() - startTime}ms") global.devResourcePath = path.join(app.getHomeDir(), 'github', 'atom') diff --git a/src/package.coffee b/src/package.coffee index 81cc45fed..4a1a2571f 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -39,3 +39,9 @@ class Package isTheme: -> !!@metadata?.theme + + # Private: + measure: (key, fn) -> + startTime = new Date().getTime() + fn() + @[key] = new Date().getTime() - startTime diff --git a/src/project.coffee b/src/project.coffee index fe0cc0b8c..7ee3ac00a 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -10,7 +10,7 @@ TextBuffer = require './text-buffer' EditSession = require './edit-session' EventEmitter = require './event-emitter' Directory = require './directory' -BufferedNodeProcess = require './buffered-node-process' +Task = require './task' Git = require './git' # Public: Represents a project that's opened in Atom. @@ -278,65 +278,30 @@ class Project # # * regex: # A RegExp to search with + # * options: + # - paths: an {Array} of glob patterns to search within # * iterator: # A Function callback on each file found - scan: (regex, iterator) -> - bufferedData = "" - state = 'readingPath' - filePath = null - - readPath = (line) -> - if /^[0-9,; ]+:/.test(line) - state = 'readingLines' - else if /^:/.test line - filePath = line.substr(1) - else - filePath += ('\n' + line) - - readLine = (line) -> - if line.length == 0 - state = 'readingPath' - filePath = null - else - colonIndex = line.indexOf(':') - matchInfo = line.substring(0, colonIndex) - lineText = line.substring(colonIndex + 1) - readMatches(matchInfo, lineText) - - readMatches = (matchInfo, lineText) -> - [lineNumber, matchPositionsText] = matchInfo.match(/(\d+);(.+)/)[1..] - row = parseInt(lineNumber) - 1 - matchPositions = matchPositionsText.split(',').map (positionText) -> positionText.split(' ').map (pos) -> parseInt(pos) - - for [column, length] in matchPositions - range = new Range([row, column], [row, column + length]) - match = lineText.substr(column, length) - iterator({path: filePath, range, match}) + scan: (regex, options={}, iterator) -> + if _.isFunction(options) + iterator = options + options = {} deferred = $.Deferred() - errors = [] - stderr = (data) -> - errors.push(data) - stdout = (data) -> - lines = data.split('\n') - lines.pop() # the last segment is a spurious '' because data always ends in \n due to bufferLines: true - for line in lines - readPath(line) if state is 'readingPath' - readLine(line) if state is 'readingLines' - exit = (code) -> - if code is 0 - deferred.resolve() - else - console.error("Project scan failed: #{code}", errors.join('\n')) - deferred.reject({command, code}) - command = require.resolve('.bin/nak') - args = ['--hidden', '--ackmate', regex.source, @getPath()] - ignoredNames = config.get('core.ignoredNames') ? [] - args.unshift('--ignore', ignoredNames.join(',')) if ignoredNames.length > 0 - args.unshift('--ignoreCase') if regex.ignoreCase - args.unshift('--addVCSIgnores') if config.get('core.excludeVcsIgnoredPaths') - new BufferedNodeProcess({command, args, stdout, stderr, exit}) + searchOptions = + ignoreCase: regex.ignoreCase + inclusions: options.paths + includeHidden: true + excludeVcsIgnores: config.get('core.excludeVcsIgnoredPaths') + exclusions: config.get('core.ignoredNames') + + task = Task.once require.resolve('./scan-handler'), @getPath(), regex.source, searchOptions, -> + deferred.resolve() + + task.on 'scan:result-found', (result) => + iterator(result) + deferred # Private: diff --git a/src/root-view.coffee b/src/root-view.coffee index 599e9b94c..28fd64b6b 100644 --- a/src/root-view.coffee +++ b/src/root-view.coffee @@ -126,7 +126,8 @@ class RootView extends View @command 'pane:reopen-closed-item', => @panes.reopenItem() - _.nextTick => atom.setFullScreen(@state.get('fullScreen')) + if @state.get('fullScreen') + _.nextTick => atom.setFullScreen(true) # Private: serialize: -> @@ -173,8 +174,10 @@ class RootView extends View initialLine = options.initialLine path = project.relativize(path) if activePane = @getActivePane() - editSession = activePane.itemForUri(path) if path - editSession ?= project.open(path, {initialLine}) + if path + editSession = activePane.itemForUri(path) ? project.open(path, {initialLine}) + else + editSession = project.open() activePane.showItem(editSession) else editSession = project.open(path, {initialLine}) diff --git a/src/scan-handler.coffee b/src/scan-handler.coffee new file mode 100644 index 000000000..735fc2f00 --- /dev/null +++ b/src/scan-handler.coffee @@ -0,0 +1,15 @@ +{PathSearcher, PathScanner, search} = require 'scandal' + +module.exports = (rootPath, regexSource, options) -> + callback = @async() + + searcher = new PathSearcher() + scanner = new PathScanner(rootPath, options) + + searcher.on 'results-found', (result) -> + emit('scan:result-found', result) + + flags = "g" + flags += "i" if options.ignoreCase + regex = new RegExp(regexSource, flags) + search regex, scanner, searcher, callback diff --git a/src/text-buffer.coffee b/src/text-buffer.coffee index cfad306c0..0b1431061 100644 --- a/src/text-buffer.coffee +++ b/src/text-buffer.coffee @@ -6,6 +6,7 @@ File = require './file' EventEmitter = require './event-emitter' Subscriber = require './subscriber' guid = require 'guid' +{P} = require 'scandal' # Private: Represents the contents of a file. # @@ -501,7 +502,10 @@ class TextBuffer # regex - A {RegExp} representing the text to find # iterator - A {Function} that's called on each match scan: (regex, iterator) -> - @scanInRange(regex, @getRange(), iterator) + @scanInRange regex, @getRange(), (result) => + result.lineText = @lineForRow(result.range.start.row) + result.lineTextOffset = 0 + iterator(result) # Scans for text in a given range, calling a function on each match. # @@ -538,7 +542,8 @@ class TextBuffer range = new Range(startPosition, endPosition) keepLooping = true replacementText = null - iterator({match, range, stop, replace }) + matchText = match[0] + iterator({ match, matchText, range, stop, replace }) if replacementText? @change(range, replacementText) diff --git a/src/text-mate-package.coffee b/src/text-mate-package.coffee index b3cc97843..1cfb9d5ba 100644 --- a/src/text-mate-package.coffee +++ b/src/text-mate-package.coffee @@ -29,18 +29,20 @@ class TextMatePackage extends Package getType: -> 'textmate' load: ({sync}={}) -> - @metadata = Package.loadMetadata(@path, true) + @measure 'loadTime', => + @metadata = Package.loadMetadata(@path, true) - if sync - @loadGrammarsSync() - @loadScopedPropertiesSync() - else - TextMatePackage.getLoadQueue().push(this) + if sync + @loadGrammarsSync() + @loadScopedPropertiesSync() + else + TextMatePackage.getLoadQueue().push(this) activate: -> - syntax.addGrammar(grammar) for grammar in @grammars - for { selector, properties } in @scopedProperties - syntax.addProperties(@path, selector, properties) + @measure 'activateTime', => + syntax.addGrammar(grammar) for grammar in @grammars + for { selector, properties } in @scopedProperties + syntax.addProperties(@path, selector, properties) activateConfig: -> # noop diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index 66ed646c5..8fbbea4e3 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -29,6 +29,10 @@ class ThemeManager getActiveThemes: -> _.clone(@activeThemes) + # Internal-only: + getLoadedThemes: -> + _.clone(@loadedThemes) + # Internal-only: unload: -> removeStylesheet(@userStylesheetPath) if @userStylesheetPath? diff --git a/src/underscore-extensions.coffee b/src/underscore-extensions.coffee index 46ab466e4..fdfc0b845 100644 --- a/src/underscore-extensions.coffee +++ b/src/underscore-extensions.coffee @@ -28,6 +28,9 @@ _.mixin escapeRegExp: (string) -> string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + escapeAttribute: (string) -> + string.replace(/"/g, '"').replace(/\n/g, '') + humanizeEventName: (eventName, eventDoc) -> [namespace, event] = eventName.split(':') return _.undasherize(namespace) unless event? diff --git a/src/window-bootstrap.coffee b/src/window-bootstrap.coffee index cf7cbdaa6..bccf2ee92 100644 --- a/src/window-bootstrap.coffee +++ b/src/window-bootstrap.coffee @@ -1,8 +1,9 @@ # Like sands through the hourglass, so are the days of our lives. -date = new Date().getTime() +startTime = new Date().getTime() + require './atom' require './window' window.setUpEnvironment('editor') window.startEditorWindow() -console.log "Load time: #{new Date().getTime() - date}" +console.log "Window load time: #{new Date().getTime() - startTime}ms" diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index 332425abc..33b6f2cd5 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -14,7 +14,7 @@ class WindowEventHandler @reloadRequested = false @subscribe ipc, 'command', (command, args...) -> - $(window).trigger(command, args...) + $(document.activeElement).trigger(command, args...) @subscribe ipc, 'context-command', (command, args...) -> $(atom.contextMenu.activeElement).trigger(command, args...)