diff --git a/native/atom_cef_client.cpp b/native/atom_cef_client.cpp index 3acb53951..27badf291 100644 --- a/native/atom_cef_client.cpp +++ b/native/atom_cef_client.cpp @@ -80,6 +80,9 @@ bool AtomCefClient::OnProcessMessageReceived(CefRefPtr browser, else if (name == "show") { Show(browser); } + else if (name == "toggleFullScreen") { + ToggleFullScreen(browser); + } else { return false; } diff --git a/native/atom_cef_client.h b/native/atom_cef_client.h index d1961d8d3..85d9a031b 100644 --- a/native/atom_cef_client.h +++ b/native/atom_cef_client.h @@ -124,6 +124,7 @@ class AtomCefClient : public CefClient, void Exit(int status); void Log(const char *message); void Show(CefRefPtr browser); + void ToggleFullScreen(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 be4879d97..d2f250ec0 100644 --- a/native/atom_cef_client_mac.mm +++ b/native/atom_cef_client_mac.mm @@ -118,6 +118,10 @@ void AtomCefClient::Show(CefRefPtr browser) { [windowController.webView setHidden:NO]; } +void AtomCefClient::ToggleFullScreen(CefRefPtr browser) { + [[browser->GetHost()->GetWindowHandle() window] toggleFullScreen:nil]; +} + void AtomCefClient::ShowSaveDialog(int replyId, CefRefPtr browser) { CefRefPtr replyMessage = CefProcessMessage::Create("reply"); CefRefPtr replyArguments = replyMessage->GetArgumentList(); diff --git a/spec/app/buffer-spec.coffee b/spec/app/buffer-spec.coffee index b13c2b7c2..2a1eaa30c 100644 --- a/spec/app/buffer-spec.coffee +++ b/spec/app/buffer-spec.coffee @@ -889,3 +889,17 @@ describe 'Buffer', -> buffer.setText("\ninitialtext") buffer.append("hello\n1\r\n2\n") expect(buffer.getText()).toBe "\ninitialtexthello\n1\n2\n" + + describe ".clipPosition(position)", -> + describe "when the position is before the start of the buffer", -> + it "returns the first position in the buffer", -> + expect(buffer.clipPosition([-1,0])).toEqual [0,0] + expect(buffer.clipPosition([0,-1])).toEqual [0,0] + expect(buffer.clipPosition([-1,-1])).toEqual [0,0] + + describe "when the position is after the end of the buffer", -> + it "returns the last position in the buffer", -> + buffer.setText('some text') + expect(buffer.clipPosition([1, 0])).toEqual [0,9] + expect(buffer.clipPosition([0,10])).toEqual [0,9] + expect(buffer.clipPosition([10,Infinity])).toEqual [0,9] diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index cf4524393..25620a25e 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2523,3 +2523,46 @@ describe "Editor", -> editor.trigger 'editor:move-line-down' expect(buffer.lineForRow(1)).toBe ' if (items.length <= 1) return items;' expect(buffer.lineForRow(2)).toBe ' var sort = function(items) {' + + describe "when editor:duplicate-line is triggered", -> + describe "where there is no selection", -> + describe "when the cursor isn't on a folded line", -> + it "duplicates the current line below and moves the cursor down one row", -> + editor.setCursorBufferPosition([0, 5]) + editor.trigger 'editor:duplicate-line' + expect(buffer.lineForRow(0)).toBe 'var quicksort = function () {' + expect(buffer.lineForRow(1)).toBe 'var quicksort = function () {' + expect(editor.getCursorBufferPosition()).toEqual [1, 5] + + describe "when the cursor is on a folded line", -> + it "duplicates the entire fold before and moves the cursor to the new fold", -> + editor.setCursorBufferPosition([4]) + editor.foldCurrentRow() + editor.trigger 'editor:duplicate-line' + expect(editor.getCursorScreenPosition()).toEqual [5] + expect(editor.isFoldedAtScreenRow(4)).toBeTruthy() + expect(editor.isFoldedAtScreenRow(5)).toBeTruthy() + expect(buffer.lineForRow(8)).toBe ' while(items.length > 0) {' + expect(buffer.lineForRow(9)).toBe ' current = items.shift();' + expect(buffer.lineForRow(10)).toBe ' current < pivot ? left.push(current) : right.push(current);' + expect(buffer.lineForRow(11)).toBe ' }' + + describe "when the cursor is on the last line and it doesn't have a trailing newline", -> + it "inserts a newline and the duplicated line", -> + editor.moveCursorToBottom() + editor.trigger 'editor:duplicate-line' + expect(buffer.lineForRow(12)).toBe '};' + expect(buffer.lineForRow(13)).toBe '};' + expect(buffer.lineForRow(14)).toBeUndefined() + expect(editor.getCursorBufferPosition()).toEqual [13, 2] + + describe "when the cursor in on the last line and it is only a newline", -> + it "duplicates the current line below and moves the cursor down one row", -> + editor.moveCursorToBottom() + editor.insertNewline() + editor.moveCursorToBottom() + editor.trigger 'editor:duplicate-line' + expect(buffer.lineForRow(13)).toBe '' + expect(buffer.lineForRow(14)).toBe '' + expect(buffer.lineForRow(15)).toBeUndefined() + expect(editor.getCursorBufferPosition()).toEqual [14, 0] diff --git a/src/app/atom.coffee b/src/app/atom.coffee index e5eb0197f..b080e03bb 100644 --- a/src/app/atom.coffee +++ b/src/app/atom.coffee @@ -111,6 +111,9 @@ _.extend atom, endTracing: -> @sendMessageToBrowserProcess('endTracing') + toggleFullScreen: -> + @sendMessageToBrowserProcess('toggleFullScreen') + getRootViewStateForPath: (path) -> if json = localStorage[path] JSON.parse(json) diff --git a/src/app/buffer.coffee b/src/app/buffer.coffee index db7de2e75..62511d2f9 100644 --- a/src/app/buffer.coffee +++ b/src/app/buffer.coffee @@ -212,13 +212,15 @@ class Buffer range clipPosition: (position) -> - { row, column } = Point.fromObject(position) - row = 0 if row < 0 - column = 0 if column < 0 - row = Math.min(@getLastRow(), row) - column = Math.min(@lineLengthForRow(row), column) - - new Point(row, column) + position = Point.fromObject(position) + eofPosition = @getEofPosition() + if position.isGreaterThan(eofPosition) + eofPosition + else + row = Math.max(position.row, 0) + column = Math.max(position.column, 0) + column = Math.min(@lineLengthForRow(row), column) + new Point(row, column) clipRange: (range) -> range = Range.fromObject(range) diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 838a13b76..c0df1d6f0 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -411,6 +411,28 @@ class EditSession @setSelectedBufferRange(selection.translate([1]), preserveFolds: true) + duplicateLine: -> + return unless @getSelection().isEmpty() + + @transact => + cursorPosition = @getCursorBufferPosition() + cursorRowFolded = @isFoldedAtCursorRow() + if cursorRowFolded + screenRow = @screenPositionForBufferPosition(cursorPosition).row + bufferRange = @bufferRangeForScreenRange([[screenRow], [screenRow + 1]]) + else + bufferRange = new Range([cursorPosition.row], [cursorPosition.row + 1]) + + insertPosition = new Point(bufferRange.end.row) + if insertPosition.row >= @buffer.getLastRow() + @unfoldCurrentRow() if cursorRowFolded + @buffer.append("\n#{@getTextInBufferRange(bufferRange)}") + @foldCurrentRow() if cursorRowFolded + else + @buffer.insert(insertPosition, @getTextInBufferRange(bufferRange)) + + @setCursorScreenPosition(@getCursorScreenPosition().translate([1])) + @foldCurrentRow() if cursorRowFolded mutateSelectedText: (fn) -> @transact => fn(selection) for selection in @getSelections() diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 8d41771c2..a11807bb0 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -186,6 +186,7 @@ class Editor extends View 'editor:copy-path': @copyPathToPasteboard 'editor:move-line-up': @moveLineUp 'editor:move-line-down': @moveLineDown + 'editor:duplicate-line': @duplicateLine documentation = {} for name, method of editorBindings @@ -209,6 +210,7 @@ class Editor extends View moveCursorToEndOfLine: -> @activeEditSession.moveCursorToEndOfLine() moveLineUp: -> @activeEditSession.moveLineUp() moveLineDown: -> @activeEditSession.moveLineDown() + duplicateLine: -> @activeEditSession.duplicateLine() setCursorScreenPosition: (position) -> @activeEditSession.setCursorScreenPosition(position) getCursorScreenPosition: -> @activeEditSession.getCursorScreenPosition() getCursorScreenRow: -> @activeEditSession.getCursorScreenRow() diff --git a/src/app/keymaps/atom.cson b/src/app/keymaps/atom.cson index 97c080d26..2d1d6c01e 100644 --- a/src/app/keymaps/atom.cson +++ b/src/app/keymaps/atom.cson @@ -28,6 +28,7 @@ 'meta--': 'window:decrease-font-size' 'ctrl-w w': 'window:focus-next-pane' 'ctrl-tab': 'window:focus-next-pane' + 'ctrl-meta-f': 'window:toggle-full-screen' 'alt-meta-i': 'toggle-dev-tools' diff --git a/src/app/keymaps/editor.cson b/src/app/keymaps/editor.cson index 4c977b698..fdccfd939 100644 --- a/src/app/keymaps/editor.cson +++ b/src/app/keymaps/editor.cson @@ -40,3 +40,4 @@ 'ctrl-C': 'editor:copy-path' 'ctrl-meta-up': 'editor:move-line-up' 'ctrl-meta-down': 'editor:move-line-down' + 'meta-D': 'editor:duplicate-line' diff --git a/src/app/window.coffee b/src/app/window.coffee index cc465aa47..1bea3a6d9 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -28,12 +28,9 @@ windowAdditions = $(window).on 'core:close', => @close() $(window).command 'window:close', => @close() - - $(window).on 'focus', -> - $('body').removeClass 'is-blurred' - - $(window).on 'blur', -> - $('body').addClass 'is-blurred' + $(window).command 'window:toggle-full-screen', => atom.toggleFullScreen() + $(window).on 'focus', -> $("body").removeClass('is-blurred') + $(window).on 'blur', -> $("body").addClass('is-blurred') # This method is intended only to be run when starting a normal application # Note: RootView assigns itself on window on initialization so that diff --git a/src/packages/symbols-view/src/tag-generator.coffee b/src/packages/symbols-view/src/tag-generator.coffee index bcc958bb4..7517d4dea 100644 --- a/src/packages/symbols-view/src/tag-generator.coffee +++ b/src/packages/symbols-view/src/tag-generator.coffee @@ -6,40 +6,13 @@ class TagGenerator constructor: (@path, @callback) -> - parsePrefix: (section = "") -> - if section.indexOf('class:') is 0 - section.substring(6) - else if section.indexOf('namespace:') is 0 - section.substring(10) - else if section.indexOf('file:') is 0 - section.substring(5) - else if section.indexOf('signature:') is 0 - section.substring(10) - else if section.indexOf('struct:') is 0 - section.substring(7) - else - section - parseTagLine: (line) -> sections = line.split('\t') - return null if sections.length < 4 - - label = sections[0] - line = parseInt(sections[2]) - 1 - if sections.length > 5 - if prefix = @parsePrefix(sections[4]) - label = "#{prefix}::#{label}" - if suffix = @parsePrefix(sections[5]) - label = "#{label}#{suffix}" + if sections.length > 3 + position: new Point(parseInt(sections[2]) - 1) + name: sections[0] else - if suffix = @parsePrefix(sections[4]) - label = "#{label}#{suffix}" - - tag = - position: new Point(line, 0) - name: label - - return tag + null generate: -> options =