From a8a46470de3687647d144d6f08b18d3adc3f6339 Mon Sep 17 00:00:00 2001 From: Brian Lopez & Nathan Sobo Date: Thu, 17 Jan 2013 16:29:24 -0800 Subject: [PATCH 01/23] add me to the pairs file --- .pairs | 1 + 1 file changed, 1 insertion(+) diff --git a/.pairs b/.pairs index 726f863bc..c939edd3c 100644 --- a/.pairs +++ b/.pairs @@ -4,6 +4,7 @@ pairs: dg: David Graham; dgraham ks: Kevin Sawicki; kevin jc: Jerry Cheung; jerry + bl: Brian Lopez; brian email: domain: github.com #global: true From b82e9df5f534ddf587486603e114023dee24d3b1 Mon Sep 17 00:00:00 2001 From: Brian Lopez & Nathan Sobo Date: Thu, 17 Jan 2013 16:30:09 -0800 Subject: [PATCH 02/23] Fix crash when deserializing a pane without a wrapped view --- spec/app/root-view-spec.coffee | 192 +++++++++++++++++---------------- src/app/pane.coffee | 4 +- 2 files changed, 104 insertions(+), 92 deletions(-) diff --git a/spec/app/root-view-spec.coffee b/spec/app/root-view-spec.coffee index e5abf32a0..2ed222f58 100644 --- a/spec/app/root-view-spec.coffee +++ b/spec/app/root-view-spec.coffee @@ -18,7 +18,7 @@ describe "RootView", -> afterEach -> rootView.deactivate() - describe "initialize(pathToOpen)", -> + describe ".initialize(pathToOpen)", -> describe "when called with a pathToOpen", -> describe "when pathToOpen references a file", -> it "creates a project for the file's parent directory, then sets the title and opens the file in an editor", -> @@ -42,95 +42,6 @@ describe "RootView", -> expect(rootView.getEditors().length).toBe 0 expect(rootView.getTitle()).toBe rootView.project.getPath() - describe "when called with view state data returned from a previous call to RootView.prototype.serialize", -> - viewState = null - - describe "when the serialized RootView has an unsaved buffer", -> - buffer = null - - beforeEach -> - rootView.remove() - rootView = new RootView - rootView.open() - editor1 = rootView.getActiveEditor() - buffer = editor1.getBuffer() - editor1.splitRight() - viewState = rootView.serialize() - - it "constructs the view with the same panes", -> - rootView = RootView.deserialize(viewState) - expect(rootView.project.getPath()?).toBeFalsy() - expect(rootView.getEditors().length).toBe 2 - expect(rootView.getActiveEditor().getText()).toBe buffer.getText() - expect(rootView.getTitle()).toBe 'untitled' - - describe "when the serialized RootView has a project", -> - beforeEach -> - path = require.resolve 'fixtures' - rootView.remove() - rootView = new RootView(path) - - describe "when there are open editors", -> - beforeEach -> - rootView.open('dir/a') - editor1 = rootView.getActiveEditor() - editor2 = editor1.splitRight() - editor3 = editor2.splitRight() - editor4 = editor2.splitDown() - editor2.edit(rootView.project.buildEditSessionForPath('dir/b')) - editor3.edit(rootView.project.buildEditSessionForPath('sample.js')) - editor3.setCursorScreenPosition([2, 4]) - editor4.edit(rootView.project.buildEditSessionForPath('sample.txt')) - editor4.setCursorScreenPosition([0, 2]) - rootView.attachToDom() - editor2.focus() - viewState = rootView.serialize() - rootView.remove() - - it "constructs the view with the same project and panes", -> - rootView = RootView.deserialize(viewState) - rootView.attachToDom() - - expect(rootView.getEditors().length).toBe 4 - editor1 = rootView.panes.find('.row > .pane .editor:eq(0)').view() - editor3 = rootView.panes.find('.row > .pane .editor:eq(1)').view() - editor2 = rootView.panes.find('.row > .column > .pane .editor:eq(0)').view() - editor4 = rootView.panes.find('.row > .column > .pane .editor:eq(1)').view() - - expect(editor1.getPath()).toBe require.resolve('fixtures/dir/a') - expect(editor2.getPath()).toBe require.resolve('fixtures/dir/b') - expect(editor3.getPath()).toBe require.resolve('fixtures/sample.js') - expect(editor3.getCursorScreenPosition()).toEqual [2, 4] - expect(editor4.getPath()).toBe require.resolve('fixtures/sample.txt') - expect(editor4.getCursorScreenPosition()).toEqual [0, 2] - - # ensure adjust pane dimensions is called - expect(editor1.width()).toBeGreaterThan 0 - expect(editor2.width()).toBeGreaterThan 0 - expect(editor3.width()).toBeGreaterThan 0 - expect(editor4.width()).toBeGreaterThan 0 - - # ensure correct editor is focused again - expect(editor2.isFocused).toBeTruthy() - expect(editor1.isFocused).toBeFalsy() - expect(editor3.isFocused).toBeFalsy() - expect(editor4.isFocused).toBeFalsy() - - expect(rootView.getTitle()).toBe "#{fs.base(editor2.getPath())} – #{rootView.project.getPath()}" - - describe "where there are no open editors", -> - beforeEach -> - rootView.attachToDom() - viewState = rootView.serialize() - rootView.remove() - - it "constructs the view with no open editors", -> - rootView = RootView.deserialize(viewState) - rootView.attachToDom() - - expect(rootView.getEditors().length).toBe 0 - - describe "when called with no pathToOpen", -> it "opens an empty buffer", -> rootView.remove() @@ -139,6 +50,107 @@ describe "RootView", -> expect(rootView.getEditors()[0].getText()).toEqual "" expect(rootView.getTitle()).toBe 'untitled' + describe "@deserialize()", -> + viewState = null + + describe "when the serialized RootView has an unsaved buffer", -> + buffer = null + + beforeEach -> + rootView.remove() + rootView = new RootView + rootView.open() + editor1 = rootView.getActiveEditor() + buffer = editor1.getBuffer() + editor1.splitRight() + viewState = rootView.serialize() + + it "constructs the view with the same panes", -> + rootView = RootView.deserialize(viewState) + expect(rootView.project.getPath()?).toBeFalsy() + expect(rootView.getEditors().length).toBe 2 + expect(rootView.getActiveEditor().getText()).toBe buffer.getText() + expect(rootView.getTitle()).toBe 'untitled' + + describe "when the serialized RootView has a project", -> + beforeEach -> + path = require.resolve 'fixtures' + rootView.remove() + rootView = new RootView(path) + + describe "when there are open editors", -> + beforeEach -> + rootView.open('dir/a') + editor1 = rootView.getActiveEditor() + editor2 = editor1.splitRight() + editor3 = editor2.splitRight() + editor4 = editor2.splitDown() + editor2.edit(rootView.project.buildEditSessionForPath('dir/b')) + editor3.edit(rootView.project.buildEditSessionForPath('sample.js')) + editor3.setCursorScreenPosition([2, 4]) + editor4.edit(rootView.project.buildEditSessionForPath('sample.txt')) + editor4.setCursorScreenPosition([0, 2]) + rootView.attachToDom() + editor2.focus() + viewState = rootView.serialize() + rootView.remove() + + it "constructs the view with the same project and panes", -> + rootView = RootView.deserialize(viewState) + rootView.attachToDom() + + expect(rootView.getEditors().length).toBe 4 + editor1 = rootView.panes.find('.row > .pane .editor:eq(0)').view() + editor3 = rootView.panes.find('.row > .pane .editor:eq(1)').view() + editor2 = rootView.panes.find('.row > .column > .pane .editor:eq(0)').view() + editor4 = rootView.panes.find('.row > .column > .pane .editor:eq(1)').view() + + expect(editor1.getPath()).toBe require.resolve('fixtures/dir/a') + expect(editor2.getPath()).toBe require.resolve('fixtures/dir/b') + expect(editor3.getPath()).toBe require.resolve('fixtures/sample.js') + expect(editor3.getCursorScreenPosition()).toEqual [2, 4] + expect(editor4.getPath()).toBe require.resolve('fixtures/sample.txt') + expect(editor4.getCursorScreenPosition()).toEqual [0, 2] + + # ensure adjust pane dimensions is called + expect(editor1.width()).toBeGreaterThan 0 + expect(editor2.width()).toBeGreaterThan 0 + expect(editor3.width()).toBeGreaterThan 0 + expect(editor4.width()).toBeGreaterThan 0 + + # ensure correct editor is focused again + expect(editor2.isFocused).toBeTruthy() + expect(editor1.isFocused).toBeFalsy() + expect(editor3.isFocused).toBeFalsy() + expect(editor4.isFocused).toBeFalsy() + + expect(rootView.getTitle()).toBe "#{fs.base(editor2.getPath())} – #{rootView.project.getPath()}" + + describe "where there are no open editors", -> + beforeEach -> + rootView.attachToDom() + viewState = rootView.serialize() + rootView.remove() + + it "constructs the view with no open editors", -> + rootView = RootView.deserialize(viewState) + rootView.attachToDom() + + expect(rootView.getEditors().length).toBe 0 + + describe "when a pane's wrapped view cannot be deserialized", -> + fit "renders an empty pane", -> + viewState = + panesViewState: + viewClass: "Pane", + wrappedView: + viewClass: "BogusView" + + rootView.remove() + rootView = RootView.deserialize(viewState) + expect(rootView.find('.pane').length).toBe 1 + expect(rootView.find('.pane').children().length).toBe 0 + describe ".serialize()", -> it "absorbs exceptions that are thrown by the package module's serialize methods", -> spyOn(console, 'error') diff --git a/src/app/pane.coffee b/src/app/pane.coffee index fb5317b8a..81881e74a 100644 --- a/src/app/pane.coffee +++ b/src/app/pane.coffee @@ -6,14 +6,14 @@ module.exports = class Pane extends View @content: (wrappedView) -> @div class: 'pane', => - @subview 'wrappedView', wrappedView + @subview 'wrappedView', wrappedView if wrappedView @deserialize: ({wrappedView}, rootView) -> new Pane(rootView.deserializeView(wrappedView)) serialize: -> viewClass: "Pane" - wrappedView: @wrappedView.serialize() + wrappedView: @wrappedView?.serialize() adjustDimensions: -> # do nothing From 23e917147dce909bf83ac85ec14b4321a31d4646 Mon Sep 17 00:00:00 2001 From: Brian Lopez & Nathan Sobo Date: Thu, 17 Jan 2013 16:32:28 -0800 Subject: [PATCH 03/23] Make view deserialization work with arbitrary view classes --- src/app/root-view.coffee | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee index f4f6c271c..3a187d6a7 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -42,6 +42,12 @@ class RootView extends View window.rootView = this @packageStates ?= {} @packageModules = {} + @viewClasses = { + "Pane": Pane, + "PaneRow": PaneRow, + "PaneColumn": PaneColumn, + "Editor": Editor + } @handleEvents() if not projectOrPathToOpen or _.isString(projectOrPathToOpen) @@ -121,12 +127,11 @@ class RootView extends View console?.error("Exception serializing '#{name}' package's module\n", e.stack) packageStates + registerViewClass: (viewClass) -> + @viewClasses[viewClass.name] = viewClass + deserializeView: (viewState) -> - switch viewState.viewClass - when 'Pane' then Pane.deserialize(viewState, this) - when 'PaneRow' then PaneRow.deserialize(viewState, this) - when 'PaneColumn' then PaneColumn.deserialize(viewState, this) - when 'Editor' then Editor.deserialize(viewState, this) + @viewClasses[viewState.viewClass]?.deserialize(viewState, this) activatePackage: (name, packageModule) -> config.setDefaults(name, packageModule.configDefaults) if packageModule.configDefaults? From 12fdf07137e2f65760a6175fbf53cf218ac45c9e Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 17 Jan 2013 09:45:08 -0800 Subject: [PATCH 04/23] :lipstick: --- native/atom_window_controller.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/native/atom_window_controller.mm b/native/atom_window_controller.mm index 3c22c82e6..60d54d4d8 100644 --- a/native/atom_window_controller.mm +++ b/native/atom_window_controller.mm @@ -180,9 +180,9 @@ if (_cefClient && _cefClient->GetBrowser()) { _cefClient->GetBrowser()->SendProcessMessage(PID_RENDERER, CefProcessMessage::Create("shutdown")); } - + if (_pidToKillOnClose) kill([_pidToKillOnClose intValue], SIGQUIT); - + [self autorelease]; return YES; } From ef37659e56477910ac7f4228d7a5437ef2572b8c Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 17 Jan 2013 10:11:04 -0800 Subject: [PATCH 05/23] Don't reset body background to white --- static/reset.css | 1 - 1 file changed, 1 deletion(-) diff --git a/static/reset.css b/static/reset.css index 2daea0c94..44497b27d 100755 --- a/static/reset.css +++ b/static/reset.css @@ -40,7 +40,6 @@ footer, header, hgroup, menu, nav, section { /* Line-height should always be unitless! */ body { line-height: 1.5; - background: white; } /* Tables still need 'cellspacing="0"' in the markup. */ From 17e7e05e8b3e2df6be2b6c102439d7a2d503af31 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 17 Jan 2013 10:11:57 -0800 Subject: [PATCH 06/23] Set background color on root HTML element --- static/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/index.html b/static/index.html index e65255bd2..582a8c87b 100644 --- a/static/index.html +++ b/static/index.html @@ -1,5 +1,5 @@ - + From 7f8a64b69cc745c4b6704e0191890ea315e2e2ce Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 17 Jan 2013 11:18:00 -0800 Subject: [PATCH 07/23] Show window only after root view is attached This prevents a white flicker when the page starts to load. --- native/atom_cef_client.cpp | 5 ++++- native/atom_cef_client.h | 1 + native/atom_cef_client_mac.mm | 7 ++++++- native/atom_window_controller.mm | 3 +++ src/app/atom.coffee | 3 +++ src/window-bootstrap.coffee | 1 + 6 files changed, 18 insertions(+), 2 deletions(-) diff --git a/native/atom_cef_client.cpp b/native/atom_cef_client.cpp index e36215144..41a0c2383 100644 --- a/native/atom_cef_client.cpp +++ b/native/atom_cef_client.cpp @@ -76,6 +76,9 @@ bool AtomCefClient::OnProcessMessageReceived(CefRefPtr browser, else if (name == "endTracing") { EndTracing(); } + else if (name == "show") { + Show(browser); + } else { return false; } @@ -235,4 +238,4 @@ bool AtomCefClient::Save(const std::string& path, const std::string& data) { fwrite(data.c_str(), data.size(), 1, f); fclose(f); return true; -} \ No newline at end of file +} diff --git a/native/atom_cef_client.h b/native/atom_cef_client.h index 92c3784bb..75b787138 100644 --- a/native/atom_cef_client.h +++ b/native/atom_cef_client.h @@ -122,6 +122,7 @@ class AtomCefClient : public CefClient, CefRefPtr CreateReplyDescriptor(int replyId, int callbackIndex); void Exit(int status); void Log(const char *message); + void Show(CefRefPtr browser); IMPLEMENT_REFCOUNTING(AtomCefClient); IMPLEMENT_LOCKING(AtomCefClient); diff --git a/native/atom_cef_client_mac.mm b/native/atom_cef_client_mac.mm index 628017009..c20bd5fbe 100644 --- a/native/atom_cef_client_mac.mm +++ b/native/atom_cef_client_mac.mm @@ -95,6 +95,11 @@ void AtomCefClient::ShowDevTools(CefRefPtr browser) { [windowController showDevTools]; } +void AtomCefClient::Show(CefRefPtr browser) { + AtomWindowController *windowController = [[browser->GetHost()->GetWindowHandle() window] windowController]; + [windowController.webView setHidden:NO]; +} + void AtomCefClient::ShowSaveDialog(int replyId, CefRefPtr browser) { CefRefPtr replyMessage = CefProcessMessage::Create("reply"); CefRefPtr replyArguments = replyMessage->GetArgumentList(); @@ -127,4 +132,4 @@ void AtomCefClient::Exit(int status) { void AtomCefClient::Log(const char *message) { std::cout << message << "\n"; -} \ No newline at end of file +} diff --git a/native/atom_window_controller.mm b/native/atom_window_controller.mm index 60d54d4d8..785d85071 100644 --- a/native/atom_window_controller.mm +++ b/native/atom_window_controller.mm @@ -41,6 +41,8 @@ if (!background) { [self setShouldCascadeWindows:NO]; [self setWindowFrameAutosaveName:@"AtomWindow"]; + NSColor *background = [NSColor colorWithCalibratedRed:(51.0/255.0) green:(51.0/255.0f) blue:(51.0/255.0f) alpha:1.0]; + [self.window setBackgroundColor:background]; [self showWindow:self]; } @@ -116,6 +118,7 @@ [urlString appendFormat:@"&pathToOpen=%@", [self encodeUrlParam:_pathToOpen]]; _cefClient = new AtomCefClient(); + [self.webView setHidden:YES]; [self addBrowserToView:self.webView url:[urlString UTF8String] cefHandler:_cefClient]; } diff --git a/src/app/atom.coffee b/src/app/atom.coffee index 8f02a3db0..ee5c8a442 100644 --- a/src/app/atom.coffee +++ b/src/app/atom.coffee @@ -75,6 +75,9 @@ _.extend atom, focus: -> @sendMessageToBrowserProcess('focus') + show: -> + @sendMessageToBrowserProcess('show') + exit: (status) -> @sendMessageToBrowserProcess('exit', [status]) diff --git a/src/window-bootstrap.coffee b/src/window-bootstrap.coffee index 5720d9af9..fa815e832 100644 --- a/src/window-bootstrap.coffee +++ b/src/window-bootstrap.coffee @@ -4,3 +4,4 @@ require 'window' pathToOpen = atom.getWindowState('pathToOpen') ? window.location.params.pathToOpen window.attachRootView(pathToOpen) +atom.show() From 781f58e33a01e059d2c3044a6da0785f261ea0b7 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 17 Jan 2013 13:08:27 -0800 Subject: [PATCH 08/23] Precompile cson files to json --- package.json | 3 ++- script/copy-files-to-bundle | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 6e9696a82..1d8e2a164 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "version" : "0.0.0", "dependencies": { - "coffee-script": "1.x" + "coffee-script": "1.x", + "cson": "1.x" }, "scripts": { diff --git a/script/copy-files-to-bundle b/script/copy-files-to-bundle index 659c87e04..379f5837c 100755 --- a/script/copy-files-to-bundle +++ b/script/copy-files-to-bundle @@ -15,7 +15,7 @@ cp "$PROJECT_DIR/native/v8_extensions/"*.js "$RESOUCES_PATH/v8_extensions/" DIRS="src static vendor" -# Compile and .coffee files into bundle +# Compile .coffee files into bundle COFFEE_FILES=$(find $DIRS -type file -name '*.coffee') for COFFEE_FILE in $COFFEE_FILES; do echo $COFFEE_FILE @@ -28,5 +28,18 @@ for COFFEE_FILE in $COFFEE_FILES; do fi done; +# Compile .cson files into bundle +CSON_FILES=$(find $DIRS -type file -name '*.cson') +for CSON_FILE in $CSON_FILES; do + echo $CSON_FILE + JSON_FILE=$(echo "$RESOUCES_PATH/$CSON_FILE" | sed 's/.cson/.json/' ) + OUTPUT_PATH="$RESOUCES_PATH/$(dirname "$CSON_FILE")" + + if [ $CSON_FILE -nt "$JSON_FILE" ]; then + mkdir -p "$OUTPUT_PATH" + node_modules/.bin/cson2json "$CSON_FILE" > "$JSON_FILE" + fi +done; + # Copy non-coffee files into bundle -rsync --archive --recursive --exclude="src/**/*.coffee" src static vendor spec benchmark "$RESOUCES_PATH" +rsync --archive --recursive --exclude="src/**/*.coffee" --exclude="src/**/*.cson" src static vendor spec benchmark "$RESOUCES_PATH" From 97e808620e60051fe349f4a848621d6a49836cbb Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 17 Jan 2013 13:16:32 -0800 Subject: [PATCH 09/23] Copy themes folder to bundle --- script/copy-files-to-bundle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/copy-files-to-bundle b/script/copy-files-to-bundle index 379f5837c..c8ab8dd4f 100755 --- a/script/copy-files-to-bundle +++ b/script/copy-files-to-bundle @@ -42,4 +42,4 @@ for CSON_FILE in $CSON_FILES; do done; # Copy non-coffee files into bundle -rsync --archive --recursive --exclude="src/**/*.coffee" --exclude="src/**/*.cson" src static vendor spec benchmark "$RESOUCES_PATH" +rsync --archive --recursive --exclude="src/**/*.coffee" --exclude="src/**/*.cson" src static vendor spec benchmark themes "$RESOUCES_PATH" From 9ff0776fbfebcc1cc7ab2093a2133ccdfd797bd5 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Kevin Sawicki Date: Thu, 17 Jan 2013 13:33:40 -0800 Subject: [PATCH 10/23] Store built parser as class variable --- src/app/binding-set.coffee | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/app/binding-set.coffee b/src/app/binding-set.coffee index d3a40a8b1..168de6bbe 100644 --- a/src/app/binding-set.coffee +++ b/src/app/binding-set.coffee @@ -7,13 +7,16 @@ PEG = require 'pegjs' module.exports = class BindingSet + + @parser: null + selector: null commandsByKeystrokes: null commandForEvent: null parser: null constructor: (@selector, commandsByKeystrokes, @index) -> - @parser = PEG.buildParser(fs.read(require.resolve 'keystroke-pattern.pegjs')) + BindingSet.parser ?= PEG.buildParser(fs.read(require.resolve 'keystroke-pattern.pegjs')) @specificity = Specificity(@selector) @commandsByKeystrokes = @normalizeCommandsByKeystrokes(commandsByKeystrokes) @@ -42,7 +45,7 @@ class BindingSet normalizedKeystrokes.join(' ') normalizeKeystroke: (keystroke) -> - keys = @parser.parse(keystroke) + keys = BindingSet.parser.parse(keystroke) modifiers = keys[0...-1] modifiers.sort() [modifiers..., _.last(keys)].join('-') From f21e2641896152e9adb7a6324a33ee69d71a9993 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 17 Jan 2013 13:42:20 -0800 Subject: [PATCH 11/23] Inline require of d3 It is almost 8,000 lines and does not need to be loaded until the heatmap displays. --- src/packages/command-logger/src/command-logger.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/packages/command-logger/src/command-logger.coffee b/src/packages/command-logger/src/command-logger.coffee index 75b66cf29..ebe1e7164 100644 --- a/src/packages/command-logger/src/command-logger.coffee +++ b/src/packages/command-logger/src/command-logger.coffee @@ -2,7 +2,6 @@ ScrollView = require 'scroll-view' $ = require 'jquery' _ = require 'underscore' -d3 = require 'd3.v3' module.exports = class CommandLogger extends ScrollView @@ -121,6 +120,8 @@ class CommandLogger extends ScrollView w = @treeMap.width() h = @treeMap.height() + d3 = require 'd3.v3' + x = d3.scale.linear().range([0, w]) y = d3.scale.linear().range([0, h]) color = d3.scale.category20() From 7c9ac5a4ac04d057c062cc97e71fbc7f7766be55 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 17 Jan 2013 14:38:12 -0800 Subject: [PATCH 12/23] Defer building parser until eval'ing --- src/packages/command-panel/src/command-interpreter.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/command-panel/src/command-interpreter.coffee b/src/packages/command-panel/src/command-interpreter.coffee index eb328c9b6..0c8a08d8a 100644 --- a/src/packages/command-panel/src/command-interpreter.coffee +++ b/src/packages/command-panel/src/command-interpreter.coffee @@ -4,9 +4,9 @@ PEG = require 'pegjs' module.exports = class CommandInterpreter constructor: (@project) -> - @parser = PEG.buildParser(fs.read(require.resolve 'command-panel/commands.pegjs')) eval: (string, activeEditSession) -> + @parser ?= PEG.buildParser(fs.read(require.resolve 'command-panel/commands.pegjs')) compositeCommand = @parser.parse(string) @lastRelativeAddress = compositeCommand if compositeCommand.isRelativeAddress() compositeCommand.execute(@project, activeEditSession) From 60f25d7069c2243cea87aaa4f38f7395fdc0e710 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 17 Jan 2013 14:42:28 -0800 Subject: [PATCH 13/23] Call atom.show() in spec bootstrap --- spec/spec-bootstrap.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/spec-bootstrap.coffee b/spec/spec-bootstrap.coffee index c3a680ba7..dbffbeece 100644 --- a/spec/spec-bootstrap.coffee +++ b/spec/spec-bootstrap.coffee @@ -1,4 +1,5 @@ require 'atom' +atom.show() {runSpecSuite} = require 'jasmine-helper' document.title = "Spec Suite" From 2ca4b278b089d485c7d97c89049f923c4a333081 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 17 Jan 2013 14:42:47 -0800 Subject: [PATCH 14/23] Set spec body background to white --- static/jasmine.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/static/jasmine.css b/static/jasmine.css index 72301db23..6bf9eef9c 100644 --- a/static/jasmine.css +++ b/static/jasmine.css @@ -3,6 +3,9 @@ font-size: 16px; } +body { + background: #eee; +} .jasmine_reporter a:visited, .jasmine_reporter a { color: #303; From 0ad71a60082572dc41465e8721992bf5ae8639d8 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 17 Jan 2013 17:25:12 -0800 Subject: [PATCH 15/23] un-f RootView spec --- spec/app/root-view-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/root-view-spec.coffee b/spec/app/root-view-spec.coffee index 2ed222f58..20b5bca36 100644 --- a/spec/app/root-view-spec.coffee +++ b/spec/app/root-view-spec.coffee @@ -139,7 +139,7 @@ describe "RootView", -> expect(rootView.getEditors().length).toBe 0 describe "when a pane's wrapped view cannot be deserialized", -> - fit "renders an empty pane", -> + it "renders an empty pane", -> viewState = panesViewState: viewClass: "Pane", From 62f11557067f1f7c55b536b7c131ec13c1da8f95 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 17 Jan 2013 17:25:50 -0800 Subject: [PATCH 16/23] Don't prompt to save on close if buffer is opened elsewhere Only prompt to save when the buffer is dirty and the last open session on it is being destroyed. --- spec/app/editor-spec.coffee | 9 +++++++++ src/app/buffer.coffee | 2 ++ src/app/edit-session.coffee | 2 ++ src/app/editor.coffee | 2 +- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index ae616f814..777e7b7b3 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -199,6 +199,15 @@ describe "Editor", -> expect(editor.remove).not.toHaveBeenCalled() expect(atom.confirm).toHaveBeenCalled() + it "doesn't trigger an alert if the buffer is opened in multiple sessions", -> + spyOn(editor, 'remove').andCallThrough() + spyOn(atom, 'confirm') + editor.insertText("I AM CHANGED!") + editor.splitLeft() + editor.trigger "core:close" + expect(editor.remove).toHaveBeenCalled() + expect(atom.confirm).not.toHaveBeenCalled() + describe ".edit(editSession)", -> otherEditSession = null diff --git a/src/app/buffer.coffee b/src/app/buffer.coffee index 59c6bdd21..d5f9bd34c 100644 --- a/src/app/buffer.coffee +++ b/src/app/buffer.coffee @@ -54,6 +54,8 @@ class Buffer @destroy() if @refcount <= 0 this + hasEditors: -> @refcount > 1 + subscribeToFile: -> @file.on "contents-changed", => if @isModified() diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 24bcb6bf1..36bec76ca 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -142,6 +142,8 @@ class EditSession lineLengthForBufferRow: (row) -> @buffer.lineLengthForRow(row) scanInRange: (args...) -> @buffer.scanInRange(args...) backwardsScanInRange: (args...) -> @buffer.backwardsScanInRange(args...) + isModified: -> @buffer.isModified() + hasEditors: -> @buffer.hasEditors() screenPositionForBufferPosition: (bufferPosition, options) -> @displayBuffer.screenPositionForBufferPosition(bufferPosition, options) bufferPositionForScreenPosition: (screenPosition, options) -> @displayBuffer.bufferPositionForScreenPosition(screenPosition, options) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 720afce55..7d8b2b1a6 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -463,7 +463,7 @@ class Editor extends View @remove() if @editSessions.length is 0 callback(index) if callback - if editSession.buffer.isModified() + if editSession.isModified() and not editSession.hasEditors() @promptToSaveDirtySession(editSession, destroySession) else destroySession(editSession) From 9067f1fbb9b36a72f74a8927ede00a3bf4679226 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 17 Jan 2013 23:01:53 -0800 Subject: [PATCH 17/23] Return what function returns from measure --- src/app/window.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/window.coffee b/src/app/window.coffee index b7988eba8..9e53ed5b6 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -99,9 +99,10 @@ windowAdditions = measure: (description, fn) -> start = new Date().getTime() - fn() + value = fn() result = new Date().getTime() - start console.log description, result + value window[key] = value for key, value of windowAdditions window.startup() From f29c3a083642d9cc079864dfc78d6f961a600f52 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 18 Jan 2013 08:50:07 -0800 Subject: [PATCH 18/23] Check if module path is already loaded before doing fs operations resolve now checks the existence of a module already loaded at the given path before checking if the file exists on disk. This removes the need for many redundant fs.exists and fs.isFile calls for already loaded modules. This speeds up package loading since most packages have a common set of requires that were doing needless fs operations for each searched path when the module was already in the cache at that path. --- src/stdlib/require.coffee | 46 ++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/stdlib/require.coffee b/src/stdlib/require.coffee index 5504ee78b..94e7f1b65 100644 --- a/src/stdlib/require.coffee +++ b/src/stdlib/require.coffee @@ -73,16 +73,25 @@ resolve = (name, {verifyExistence}={}) -> file = file.replace '../', "#{prefix}/" if file[0] isnt '/' - paths.some (path) -> - fileExists = /\.(.+)$/.test(file) and __exists "#{path}/#{file}" - jsFileExists = not /\.(.+)$/.test(file) and __exists "#{path}/#{file}.js" - - if jsFileExists - file = "#{path}/#{file}.js" - else if fileExists + moduleAlreadyLoaded = paths.some (path) -> + if __moduleExists "#{path}/#{file}" file = "#{path}/#{file}" - else if expanded = __expand "#{path}/#{file}" + else if __moduleExists "#{path}/#{file}.js" + file = "#{path}/#{file}.js" + else if expanded = __moduleExpand "#{path}/#{file}" file = expanded + + if not moduleAlreadyLoaded + paths.some (path) -> + fileExists = /\.(.+)$/.test(file) and __exists "#{path}/#{file}" + jsFileExists = not /\.(.+)$/.test(file) and __exists "#{path}/#{file}.js" + + if jsFileExists + file = "#{path}/#{file}.js" + else if fileExists + file = "#{path}/#{file}" + else if expanded = __expand "#{path}/#{file}" + file = expanded else file = __expand(file) or file @@ -92,16 +101,27 @@ resolve = (name, {verifyExistence}={}) -> console.warn("Failed to resolve '#{name}'") if verifyExistence null +__moduleExists = (path) -> + __modules[path] isnt undefined + +__moduleExpand = (path) -> + return path if __moduleExists path + for ext, handler of exts + return "#{path}.#{ext}" if __moduleExists "#{path}.#{ext}" + return "#{path}/index.#{ext}" if __moduleExists "#{path}/index.#{ext}" + null + __expand = (path) -> + modulePath = __moduleExpand path + return modulePath if modulePath + return path if __isFile path for ext, handler of exts - if __exists "#{path}.#{ext}" - return "#{path}.#{ext}" - else if __exists "#{path}/index.#{ext}" - return "#{path}/index.#{ext}" + return "#{path}.#{ext}" if __exists "#{path}.#{ext}" + return "#{path}/index.#{ext}" if __exists "#{path}/index.#{ext}" return path if __exists path - return null + null __exists = (path) -> $native.exists path From a7f03f4aeba3f128b527bfb2fd45a07782cf96f6 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 18 Jan 2013 09:08:34 -0800 Subject: [PATCH 19/23] Use __moduleExists from require --- src/stdlib/require.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stdlib/require.coffee b/src/stdlib/require.coffee index 94e7f1b65..14b1a0506 100644 --- a/src/stdlib/require.coffee +++ b/src/stdlib/require.coffee @@ -28,7 +28,7 @@ require = (path, cb) -> parts = file.split '.' ext = parts[parts.length-1] - if __modules[file]? + if __moduleExists file if not __modules.loaded[file.toLowerCase()]? console.warn "Circular require: #{__filename} required #{file}" return __modules[file] @@ -102,7 +102,7 @@ resolve = (name, {verifyExistence}={}) -> null __moduleExists = (path) -> - __modules[path] isnt undefined + __modules[path]? __moduleExpand = (path) -> return path if __moduleExists path From 43d0c8a2a1558c1f0f922765eed711653be7e5e2 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 18 Jan 2013 09:14:14 -0800 Subject: [PATCH 20/23] Perform extension test once This does not need to be computed on each path since file isn't changing between paths visited --- src/stdlib/require.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/stdlib/require.coffee b/src/stdlib/require.coffee index 14b1a0506..1076bcc49 100644 --- a/src/stdlib/require.coffee +++ b/src/stdlib/require.coffee @@ -82,9 +82,10 @@ resolve = (name, {verifyExistence}={}) -> file = expanded if not moduleAlreadyLoaded + hasExtension = /\.(.+)$/.test(file) paths.some (path) -> - fileExists = /\.(.+)$/.test(file) and __exists "#{path}/#{file}" - jsFileExists = not /\.(.+)$/.test(file) and __exists "#{path}/#{file}.js" + fileExists = hasExtension and __exists "#{path}/#{file}" + jsFileExists = not hasExtension and __exists "#{path}/#{file}.js" if jsFileExists file = "#{path}/#{file}.js" From f386d5cc4bbfd09c46935e98d33081c74c1f8f8a Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 18 Jan 2013 11:17:34 -0800 Subject: [PATCH 21/23] Call fs.list() without prior call to fs.exists() fs.list() returns an empty array if calle for a non-existent or non-directory path. --- src/app/atom-package.coffee | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/app/atom-package.coffee b/src/app/atom-package.coffee index 7d66ab6a7..e86678a37 100644 --- a/src/app/atom-package.coffee +++ b/src/app/atom-package.coffee @@ -35,10 +35,7 @@ class AtomPackage extends Package keymaps.map (relativePath) => fs.resolve(@keymapsDirPath, relativePath, ['cson', 'json', '']) else - if fs.exists(@keymapsDirPath) - fs.list(@keymapsDirPath) - else - [] + fs.list(@keymapsDirPath) loadStylesheets: -> for stylesheetPath in @getStylesheetPaths() @@ -46,7 +43,4 @@ class AtomPackage extends Package getStylesheetPaths: -> stylesheetDirPath = fs.join(@path, 'stylesheets') - if fs.exists stylesheetDirPath - fs.list stylesheetDirPath - else - [] + fs.list(stylesheetDirPath) From 176ca529e3da2bf77453060240adbbc1ddaa1954 Mon Sep 17 00:00:00 2001 From: Derek Greentree Date: Thu, 17 Jan 2013 18:31:21 -0800 Subject: [PATCH 22/23] Bind meta-S to save as in editor Close #144 --- src/app/editor.coffee | 4 ++++ src/app/keymaps/atom.cson | 4 ++-- src/app/keymaps/editor.cson | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 7d8b2b1a6..d0be64e3f 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -151,6 +151,7 @@ class Editor extends View 'core:select-to-bottom': @selectToBottom 'core:close': @destroyActiveEditSession 'editor:save': @save + 'editor:save-as': @saveAs 'editor:newline-below': @insertNewlineBelow 'editor:toggle-soft-tabs': @toggleSoftTabs 'editor:toggle-soft-wrap': @toggleSoftWrap @@ -653,6 +654,9 @@ class Editor extends View session.save() onSuccess?() else + @saveAs(session, onSuccess) + + saveAs: (session=@activeEditSession, onSuccess) -> atom.showSaveDialog (path) => if path session.saveAs(path) diff --git a/src/app/keymaps/atom.cson b/src/app/keymaps/atom.cson index 0780ad30e..a4f64a990 100644 --- a/src/app/keymaps/atom.cson +++ b/src/app/keymaps/atom.cson @@ -22,7 +22,7 @@ 'pageup': 'core:page-up' 'pagedown': 'core:page-down' - 'meta-S': 'window:save-all' + 'meta-alt-s': 'window:save-all' 'meta-W': 'window:close' 'meta-+': 'window:increase-font-size' 'meta--': 'window:decrease-font-size' @@ -33,4 +33,4 @@ '.tool-panel': 'meta-escape': 'tool-panel:unfocus' 'escape': 'core:close' - 'meta-w': 'noop' \ No newline at end of file + 'meta-w': 'noop' diff --git a/src/app/keymaps/editor.cson b/src/app/keymaps/editor.cson index 376f822a2..7232af732 100644 --- a/src/app/keymaps/editor.cson +++ b/src/app/keymaps/editor.cson @@ -1,5 +1,6 @@ '.editor': 'meta-s': 'editor:save' + 'meta-S': 'editor:save-as' 'enter': 'editor:newline' 'meta-enter': 'editor:newline-below' 'tab': 'editor:indent' From 759fe2dd5bfce6579a46ba5b55649b0a9f74ed20 Mon Sep 17 00:00:00 2001 From: Andy Delcambre Date: Thu, 17 Jan 2013 16:15:20 -0800 Subject: [PATCH 23/23] Bind meta-= to autoindent currently selected rows Closes #142 --- spec/app/edit-session-spec.coffee | 11 +++++++++++ src/app/edit-session.coffee | 3 +++ src/app/editor.coffee | 2 ++ src/app/keymaps/editor.cson | 1 + src/app/selection.coffee | 4 ++++ 5 files changed, 21 insertions(+) diff --git a/spec/app/edit-session-spec.coffee b/spec/app/edit-session-spec.coffee index 2adf7feb4..c74a6605e 100644 --- a/spec/app/edit-session-spec.coffee +++ b/spec/app/edit-session-spec.coffee @@ -1973,6 +1973,17 @@ describe "EditSession", -> editSession.indent() expect(editSession.lineForBufferRow(2)).toBe " " + it "auto-indents selection when autoIndent is called", -> + editSession.setCursorBufferPosition([2, 0]) + editSession.insertText(" 0\n 2\n4\n") + + editSession.setSelectedBufferRange([[2, 0], [4, 0]]) + editSession.autoIndentSelectedRows() + + expect(editSession.lineForBufferRow(2)).toBe " 0" + expect(editSession.lineForBufferRow(3)).toBe " 2" + expect(editSession.lineForBufferRow(4)).toBe "4" + describe "editor.autoIndentOnPaste", -> it "does not auto-indent pasted text by default", -> editSession.setCursorBufferPosition([2, 0]) diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 36bec76ca..7e7791963 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -208,6 +208,9 @@ class EditSession toggleLineCommentsInSelection: -> @mutateSelectedText (selection) -> selection.toggleLineComments() + autoIndentSelectedRows: -> + @mutateSelectedText (selection) -> selection.autoIndentSelectedRows() + cutToEndOfLine: -> maintainPasteboard = false @mutateSelectedText (selection) -> diff --git a/src/app/editor.coffee b/src/app/editor.coffee index d0be64e3f..649d8afbd 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -117,6 +117,7 @@ class Editor extends View 'editor:select-word': @selectWord 'editor:newline': @insertNewline 'editor:indent': @indent + 'editor:auto-indent': @autoIndent 'editor:indent-selected-rows': @indentSelectedRows 'editor:outdent-selected-rows': @outdentSelectedRows 'editor:backspace-to-beginning-of-word': @backspaceToBeginningOfWord @@ -248,6 +249,7 @@ class Editor extends View insertNewline: -> @activeEditSession.insertNewline() insertNewlineBelow: -> @activeEditSession.insertNewlineBelow() indent: (options) -> @activeEditSession.indent(options) + autoIndent: (options) -> @activeEditSession.autoIndentSelectedRows(options) indentSelectedRows: -> @activeEditSession.indentSelectedRows() outdentSelectedRows: -> @activeEditSession.outdentSelectedRows() cutSelection: -> @activeEditSession.cutSelectedText() diff --git a/src/app/keymaps/editor.cson b/src/app/keymaps/editor.cson index 7232af732..2f48d27bb 100644 --- a/src/app/keymaps/editor.cson +++ b/src/app/keymaps/editor.cson @@ -4,6 +4,7 @@ 'enter': 'editor:newline' 'meta-enter': 'editor:newline-below' 'tab': 'editor:indent' + 'meta-=': 'editor:auto-indent' 'meta-d': 'editor:delete-line' 'ctrl-[': 'editor:fold-current-row' 'ctrl-]': 'editor:unfold-current-row' diff --git a/src/app/selection.coffee b/src/app/selection.coffee index dce6f1b3e..f30eea09a 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -307,6 +307,10 @@ class Selection if matchLength = buffer.lineForRow(row).match(leadingTabRegex)?[0].length buffer.delete [[row, 0], [row, matchLength]] + autoIndentSelectedRows: -> + [start, end] = @getBufferRowRange() + @editSession.autoIndentBufferRows(start, end) + toggleLineComments: -> @modifySelection => @editSession.toggleLineCommentsForBufferRows(@getBufferRowRange()...)