From 933979126042898fcd94d20e685263404d880b0e Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Mon, 11 Jun 2012 17:34:53 -0700 Subject: [PATCH 01/10] Set modified flag to true when there is a buffer change. --- spec/app/buffer-spec.coffee | 36 +++++++++++++++++++++++++++--------- src/app/buffer.coffee | 15 ++++++++++++--- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/spec/app/buffer-spec.coffee b/spec/app/buffer-spec.coffee index 217546cbc..9b58a395d 100644 --- a/spec/app/buffer-spec.coffee +++ b/spec/app/buffer-spec.coffee @@ -31,6 +31,32 @@ describe 'Buffer', -> buffer = new Buffer expect(buffer.getText()).toBe "" + describe "path-change event", -> + it "emits path-change event when path is changed", -> + eventHandler = jasmine.createSpy('eventHandler') + buffer.on 'path-change', eventHandler + buffer.setPath("moo.text") + expect(eventHandler).toHaveBeenCalledWith(buffer) + + describe ".isModified()", -> + describe "when deserialized", -> + it "returns false", -> + buffer = Buffer.deserialize(buffer.serialize(), new Project) + expect(buffer.isModified()).toBe false + + buffer = Buffer.deserialize((new Buffer).serialize(), new Project) + expect(buffer.isModified()).toBe false + + it "returns is true if buffer no path and had changes", -> + buffer = new Buffer + buffer.insert([0,0], "oh hi") + expect(buffer.isModified()).toBe true + + it "returns true when user changes buffer", -> + expect(buffer.isModified()).toBeFalsy() + buffer.insert([0,0], "hi") + expect(buffer.isModified()).toBe true + describe '.deserialize(state, project)', -> project = null @@ -365,7 +391,7 @@ describe 'Buffer', -> expect(ranges.length).toBe 2 - describe "backwardsScanInRange(range, regex, fn)", -> + describe ".backwardsScanInRange(range, regex, fn)", -> describe "when given a regex with no global flag", -> it "calls the iterator with the last match for the given regex in the given range", -> matches = [] @@ -446,11 +472,3 @@ describe 'Buffer', -> expect(buffer.positionForCharacterIndex(30)).toEqual [1, 0] expect(buffer.positionForCharacterIndex(61)).toEqual [2, 0] expect(buffer.positionForCharacterIndex(408)).toEqual [12, 2] - - describe "path-change event", -> - it "emits path-change event when path is changed", -> - eventHandler = jasmine.createSpy('eventHandler') - buffer.on 'path-change', eventHandler - buffer.setPath("moo.text") - expect(eventHandler).toHaveBeenCalledWith(buffer) - diff --git a/src/app/buffer.coffee b/src/app/buffer.coffee index 396241bab..66c2be231 100644 --- a/src/app/buffer.coffee +++ b/src/app/buffer.coffee @@ -8,16 +8,19 @@ UndoManager = require 'undo-manager' module.exports = class Buffer @idCounter = 1 + modified: null lines: null path: null @deserialize: (state, project) -> if state.path - project.open(state.path) + buffer = project.open(state.path) else buffer = project.bufferWithId(state.id) ? project.open() buffer.setText(state.text) - buffer + buffer.modified = state.modified + + buffer constructor: (path) -> @id = @constructor.idCounter++ @@ -28,12 +31,13 @@ class Buffer else @setText('') @undoManager = new UndoManager(this) + @modified = false serialize: -> if @getPath() { path: @path } else - { text: @getText(), id: @id } + { text: @getText(), id: @id , modified: @modified} getPath: -> @path @@ -139,6 +143,8 @@ class Buffer newTextLines[lastLineIndex] += suffix @lines[oldRange.start.row..oldRange.end.row] = newTextLines + @modified = true + @trigger 'change', { oldRange, newRange, oldText, newText } newRange @@ -164,6 +170,9 @@ class Buffer @setPath(path) @save() + isModified: -> + @modified + getMode: -> return @mode if @mode extension = if @getPath() then @getPath().split('/').pop().split('.').pop() else null From 0fec1fc5ba4521af2538ad0bb6a9041e41ffcac2 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Mon, 11 Jun 2012 17:35:21 -0700 Subject: [PATCH 02/10] Don't close edit session when its buffer is modified --- spec/app/editor-spec.coffee | 9 +++++++++ src/app/editor.coffee | 1 + 2 files changed, 10 insertions(+) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index d5a10b728..fc0fcdee0 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -1675,6 +1675,7 @@ describe "Editor", -> expect(editor.buffer).toBe buffer it "calls remove on the editor if there is one edit session and mini is false", -> + originalBuffer = editor.buffer expect(editor.mini).toBeFalsy() expect(editor.editSessions.length).toBe 1 spyOn(editor, 'remove') @@ -1686,3 +1687,11 @@ describe "Editor", -> spyOn(editor, 'remove') editor.trigger 'close' expect(editor.remove).not.toHaveBeenCalled() + + describe "when buffer is modified", -> + it "triggers alert and does not close session", -> + originalBuffer = editor.buffer + editor.insertText("I AM CHANGED!") + editor.trigger "close" + expect(editor.buffer).toBe originalBuffer + diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 0bd70f441..dfb50f4c0 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -784,6 +784,7 @@ class Editor extends View close: -> return if @mini + return if @buffer.isModified() @removeActiveEditSession() unsubscribeFromBuffer: -> From e56ef733ad2a9e04c7b04eb1e7951b3f4170a925 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 12 Jun 2012 09:20:04 -0700 Subject: [PATCH 03/10] Add save alert when closing a modified buffer --- spec/app/editor-spec.coffee | 7 ++++--- src/app/editor.coffee | 21 ++++++++++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index fc0fcdee0..7e4d91c95 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -1690,8 +1690,9 @@ describe "Editor", -> describe "when buffer is modified", -> it "triggers alert and does not close session", -> - originalBuffer = editor.buffer + spyOn(editor, 'remove') + spyOn($native, 'alert') editor.insertText("I AM CHANGED!") editor.trigger "close" - expect(editor.buffer).toBe originalBuffer - + expect(editor.remove).not.toHaveBeenCalled() + expect($native.alert).toHaveBeenCalled() diff --git a/src/app/editor.coffee b/src/app/editor.coffee index dfb50f4c0..3ca4c10fe 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -7,6 +7,8 @@ Range = require 'range' EditSession = require 'edit-session' CursorView = require 'cursor-view' SelectionView = require 'selection-view' +Native = require 'native' +fs = require 'fs' $ = require 'jquery' _ = require 'underscore' @@ -605,11 +607,13 @@ class Editor extends View save: -> if not @buffer.getPath() path = $native.saveDialog() - return if not path + return false if not path @buffer.saveAs(path) else @buffer.save() + true + clipScreenPosition: (screenPosition, options={}) -> @renderer.clipScreenPosition(screenPosition, options) @@ -784,8 +788,19 @@ class Editor extends View close: -> return if @mini - return if @buffer.isModified() - @removeActiveEditSession() + if @buffer.isModified() + filename = if @buffer.getPath() then fs.base(@buffer.getPath()) else "untitled buffer" + message = "'#{filename}' has changes, do you want to save them?" + detailedMessage = "Your changes will be lost if you don't save them" + buttons = [ + ["Save", => @save() and @removeActiveEditSession()] + ["Cancel", =>] + ["Don't save", => @removeActiveEditSession()] + ] + + Native.alert message, detailedMessage, buttons + else + @removeActiveEditSession() unsubscribeFromBuffer: -> @buffer.off ".editor#{@id}" From b5e3977ef27ada843c014963861b8b7766875a42 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 12 Jun 2012 09:21:45 -0700 Subject: [PATCH 04/10] Use Native class to call saveDialog --- src/app/editor.coffee | 2 +- src/stdlib/native.coffee | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 3ca4c10fe..4442ac606 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -606,7 +606,7 @@ class Editor extends View save: -> if not @buffer.getPath() - path = $native.saveDialog() + path = Native.saveDialog() return false if not path @buffer.saveAs(path) else diff --git a/src/stdlib/native.coffee b/src/stdlib/native.coffee index 163af0d17..543ca5cae 100644 --- a/src/stdlib/native.coffee +++ b/src/stdlib/native.coffee @@ -2,4 +2,6 @@ module.exports = class Native @alert: (args...) -> $native.alert(args...) + @saveDialog: (args...) -> $native.saveDialog(args...) + @moveToTrash: (args...) -> $native.moveToTrash(args...) From a2f60c141b916a245a3c240c002fdd39cf098b73 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 12 Jun 2012 09:23:13 -0700 Subject: [PATCH 05/10] Remove logs from spec --- spec/app/editor-spec.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 7e4d91c95..4d2b39092 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -277,12 +277,10 @@ describe "Editor", -> session0 = editor.activeEditSession buffer1 = new Buffer(require.resolve('fixtures/sample.txt')) - console.log "set buffer 1" editor.setBuffer(buffer1) session1 = editor.activeEditSession buffer2 = new Buffer(require.resolve('fixtures/two-hundred.txt')) - console.log "set buffer 2" editor.setBuffer(buffer2) session2 = editor.activeEditSession From 321e7b612920f958097b33b581f2fd55c9d0b1b6 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 12 Jun 2012 10:49:22 -0700 Subject: [PATCH 06/10] Add $native.reload --- Atom/src/native_handler.mm | 5 ++++- src/stdlib/native.coffee | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Atom/src/native_handler.mm b/Atom/src/native_handler.mm index ee48aac8e..b3435d978 100644 --- a/Atom/src/native_handler.mm +++ b/Atom/src/native_handler.mm @@ -16,7 +16,7 @@ NSString *stringFromCefV8Value(const CefRefPtr& value) { NativeHandler::NativeHandler() : CefV8Handler() { std::string extensionCode = "var $native = {}; (function() {"; - const char *functionNames[] = {"exists", "alert", "read", "write", "absolute", "list", "isFile", "isDirectory", "remove", "asyncList", "open", "openDialog", "quit", "writeToPasteboard", "readFromPasteboard", "showDevTools", "newWindow", "saveDialog", "exit", "watchPath", "unwatchPath", "makeDirectory", "move", "moveToTrash"}; + const char *functionNames[] = {"exists", "alert", "read", "write", "absolute", "list", "isFile", "isDirectory", "remove", "asyncList", "open", "openDialog", "quit", "writeToPasteboard", "readFromPasteboard", "showDevTools", "newWindow", "saveDialog", "exit", "watchPath", "unwatchPath", "makeDirectory", "move", "moveToTrash", "reload"}; NSUInteger arrayLength = sizeof(functionNames) / sizeof(const char *); for (NSUInteger i = 0; i < arrayLength; i++) { std::string functionName = std::string(functionNames[i]); @@ -375,5 +375,8 @@ bool NativeHandler::Execute(const CefString& name, return true; } + else if (name == "reload") { + CefV8Context::GetCurrentContext()->GetBrowser()->ReloadIgnoreCache(); + } return false; }; \ No newline at end of file diff --git a/src/stdlib/native.coffee b/src/stdlib/native.coffee index 543ca5cae..e468e4026 100644 --- a/src/stdlib/native.coffee +++ b/src/stdlib/native.coffee @@ -4,4 +4,6 @@ class Native @saveDialog: (args...) -> $native.saveDialog(args...) + @reload: -> $native.reload() + @moveToTrash: (args...) -> $native.moveToTrash(args...) From aa326555423d1582c5d32d19054cf25432d7b013 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 12 Jun 2012 10:49:34 -0700 Subject: [PATCH 07/10] Add RootView.modifiedBuffers() --- src/app/root-view.coffee | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee index 7ab1e92c9..ee438fa59 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -123,6 +123,14 @@ class RootView extends View editors: -> @panes.find('.editor').map -> $(this).view() + modifiedBuffers: -> + modifiedBuffers = [] + for editor in @editors() + for session in editor.editSessions + modifiedBuffers.push session.buffer if session.buffer.isModified() + + modifiedBuffers + activeEditor: -> if (editor = @panes.find('.editor.active')).length editor.view() From 8ab167fd001428022408f738dc1d81f3a0c524ef Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 12 Jun 2012 10:50:05 -0700 Subject: [PATCH 08/10] Reloading when there are modified buffers pops up a dialog. --- Atom/src/AtomController.mm | 14 +++++++++++++- spec/app/window-spec.coffee | 14 ++++++++++++++ src/app/window.coffee | 15 +++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/Atom/src/AtomController.mm b/Atom/src/AtomController.mm index 16ca86946..278036500 100644 --- a/Atom/src/AtomController.mm +++ b/Atom/src/AtomController.mm @@ -85,7 +85,19 @@ - (bool)keyEventOfType:(cef_handler_keyevent_type_t)type code:(int)code modifiers:(int)modifiers isSystemKey:(bool)isSystemKey isAfterJavaScript:(bool)isAfterJavaScript { if (isAfterJavaScript && type == KEYEVENT_RAWKEYDOWN && modifiers == KEY_META && code == 'R') { - _clientHandler->GetBrowser()->ReloadIgnoreCache(); + + CefRefPtr context = _clientHandler->GetBrowser()->GetMainFrame()->GetV8Context(); + CefRefPtr global = context->GetGlobal(); + + context->Enter(); + CefRefPtr retval; + CefRefPtr exception; + CefV8ValueList arguments; + global->GetValue("reload")->ExecuteFunction(global, arguments, retval, exception, true); + if (exception) _clientHandler->GetBrowser()->ReloadIgnoreCache(); + context->Exit(); + + return YES; } diff --git a/spec/app/window-spec.coffee b/spec/app/window-spec.coffee index c83769218..3ae18b0c2 100644 --- a/spec/app/window-spec.coffee +++ b/spec/app/window-spec.coffee @@ -17,6 +17,20 @@ describe "Window", -> $(window).trigger 'close' expect(window.close).toHaveBeenCalled() + describe ".reload()", -> + it "returns false when no buffers are modified", -> + spyOn($native, "reload") + window.reload() + expect($native.reload).toHaveBeenCalled() + + it "shows alert when a modifed buffer exists", -> + rootView.activeEditor().insertText("hi") + spyOn($native, "alert") + spyOn($native, "reload") + window.reload() + expect($native.reload).not.toHaveBeenCalled() + expect($native.alert).toHaveBeenCalled() + describe "requireStylesheet(path)", -> it "synchronously loads the stylesheet at the given path and installs a style tag for it in the head", -> $('head style[path*="atom.css"]').remove() diff --git a/src/app/window.coffee b/src/app/window.coffee index 5b6209d1d..06c1dc26f 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -1,6 +1,7 @@ # This a weirdo file. We don't create a Window class, we just add stuff to # the DOM window. +Native = require 'native' fs = require 'fs' _ = require 'underscore' $ = require 'jquery' @@ -60,6 +61,20 @@ windowAdditions = return if $("head style[path='#{fullPath}']").length $('head').append "" + reload: -> + console.log rootView.modifiedBuffers() + if rootView.modifiedBuffers().length > 0 + message = "There are unsaved buffers, reload anyway?" + detailedMessage = "You will lose all unsaved changes if you reload" + buttons = [ + ["Reload", -> Native.reload()] + ["Cancel", ->] + ] + + Native.alert(message, detailedMessage, buttons) + else + Native.reload() + showConsole: -> $native.showDevTools() From 1fb8852fc312e009010362d7921c88306c277535 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 12 Jun 2012 11:29:38 -0700 Subject: [PATCH 09/10] Saving a buffer sets its modified flag to false --- spec/app/buffer-spec.coffee | 11 +++++++++++ src/app/buffer.coffee | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/spec/app/buffer-spec.coffee b/spec/app/buffer-spec.coffee index 9b58a395d..f1478a3bb 100644 --- a/spec/app/buffer-spec.coffee +++ b/spec/app/buffer-spec.coffee @@ -57,6 +57,17 @@ describe 'Buffer', -> buffer.insert([0,0], "hi") expect(buffer.isModified()).toBe true + it "returns false after modified buffer is saved", -> + filePath = "/tmp/atom-tmp-file" + buffer = new Buffer(filePath) + expect(buffer.isModified()).toBe false + + buffer.insert([0,0], "hi") + expect(buffer.isModified()).toBe true + + buffer.save() + expect(buffer.isModified()).toBe false + describe '.deserialize(state, project)', -> project = null diff --git a/src/app/buffer.coffee b/src/app/buffer.coffee index 66c2be231..525b9a5eb 100644 --- a/src/app/buffer.coffee +++ b/src/app/buffer.coffee @@ -161,9 +161,10 @@ class Buffer @undoManager.redo() save: -> - if not @getPath() then throw new Error("Tried to save buffer with no file path") + if not @getPath() then throw new Error("Can't save buffer with no file path") @trigger 'before-save' fs.write @getPath(), @getText() + @modified = false @trigger 'after-save' saveAs: (path) -> From fc025156d34e9ed83e85de1987ca50205bcf6ed3 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 12 Jun 2012 11:30:08 -0700 Subject: [PATCH 10/10] console.log :shit: --- src/app/window.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/window.coffee b/src/app/window.coffee index 06c1dc26f..545bd25b9 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -62,7 +62,6 @@ windowAdditions = $('head').append "" reload: -> - console.log rootView.modifiedBuffers() if rootView.modifiedBuffers().length > 0 message = "There are unsaved buffers, reload anyway?" detailedMessage = "You will lose all unsaved changes if you reload"