From 7f7ca75113533cefb7a4f283f66437245b831794 Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Sun, 3 May 2015 19:44:26 -0400 Subject: [PATCH 001/971] Implement editor:move-selection-right command --- menus/darwin.cson | 1 + src/text-editor-element.coffee | 1 + src/text-editor.coffee | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/menus/darwin.cson b/menus/darwin.cson index 17072e14f..7fef9f9a4 100644 --- a/menus/darwin.cson +++ b/menus/darwin.cson @@ -128,6 +128,7 @@ { label: 'Select to First Character of Line', command: 'editor:select-to-first-character-of-line' } { label: 'Select to End of Word', command: 'editor:select-to-end-of-word' } { label: 'Select to End of Line', command: 'editor:select-to-end-of-line' } + { label: 'Move Selection Right', command: 'editor:move-selection-right' } ] } diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index e3eaaeb2a..8e6878a16 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -343,6 +343,7 @@ atom.commands.add 'atom-text-editor:not([mini])', stopEventPropagationAndGroupUn 'editor:checkout-head-revision': -> @checkoutHeadRevision() 'editor:move-line-up': -> @moveLineUp() 'editor:move-line-down': -> @moveLineDown() + 'editor:move-selection-right': -> @moveSelectionRight() 'editor:duplicate-lines': -> @duplicateLines() 'editor:join-lines': -> @joinLines() ) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 7afc99236..59bb91ebe 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -932,6 +932,25 @@ class TextEditor extends Model @setSelectedBufferRange(selection.translate([insertDelta]), preserveFolds: true, autoscroll: true) + moveSelectionRight: -> + selections = @getSelectedBufferRanges() + + translationDelta = [0, 1] + translatedRanges = [] + + @transact => + for selection in selections + range = new Range(selection.end, selection.end.translate(translationDelta)) + + insertionPoint = selection.start + text = @buffer.getTextInRange(range) + + @buffer.delete(range) + @buffer.insert(insertionPoint, text) + translatedRanges.push(selection.translate(translationDelta)) + + @setSelectedBufferRanges(translatedRanges) + # Duplicate the most recent cursor's current line. duplicateLines: -> @transact => From 1f023d1d2c64b44e1d120872c67c30426916ffaf Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Sun, 3 May 2015 20:25:47 -0400 Subject: [PATCH 002/971] Implement editor:move-selection-left --- keymaps/darwin.cson | 2 ++ menus/darwin.cson | 8 +++++++- src/text-editor-element.coffee | 1 + src/text-editor.coffee | 21 +++++++++++++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index 34d18e159..cd7d0ecb6 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -127,6 +127,8 @@ # Atom Specific 'ctrl-W': 'editor:select-word' + 'cmd-ctrl-left': 'editor:move-selection-left' + 'cmd-ctrl-right': 'editor:move-selection-right' # Sublime Parity 'cmd-a': 'core:select-all' diff --git a/menus/darwin.cson b/menus/darwin.cson index 7fef9f9a4..642126259 100644 --- a/menus/darwin.cson +++ b/menus/darwin.cson @@ -74,6 +74,13 @@ { label: 'Join Lines', command: 'editor:join-lines' } ] } + { + label: 'Columns', + submenu: [ + { label: 'Move Selection Left', command: 'editor:move-selection-left' } + { label: 'Move Selection Right', command: 'editor:move-selection-right' } + ] + } { label: 'Text', submenu: [ @@ -128,7 +135,6 @@ { label: 'Select to First Character of Line', command: 'editor:select-to-first-character-of-line' } { label: 'Select to End of Word', command: 'editor:select-to-end-of-word' } { label: 'Select to End of Line', command: 'editor:select-to-end-of-line' } - { label: 'Move Selection Right', command: 'editor:move-selection-right' } ] } diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index 8e6878a16..f428e3ced 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -343,6 +343,7 @@ atom.commands.add 'atom-text-editor:not([mini])', stopEventPropagationAndGroupUn 'editor:checkout-head-revision': -> @checkoutHeadRevision() 'editor:move-line-up': -> @moveLineUp() 'editor:move-line-down': -> @moveLineDown() + 'editor:move-selection-left': -> @moveSelectionLeft() 'editor:move-selection-right': -> @moveSelectionRight() 'editor:duplicate-lines': -> @duplicateLines() 'editor:join-lines': -> @joinLines() diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 59bb91ebe..4071e5cfc 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -932,6 +932,27 @@ class TextEditor extends Model @setSelectedBufferRange(selection.translate([insertDelta]), preserveFolds: true, autoscroll: true) + moveSelectionLeft: -> + selections = @getSelectedBufferRanges() + + translationDelta = [0, -1] + translatedRanges = [] + + @transact => + for selection in selections + range = new Range(selection.start.translate(translationDelta), selection.start) + + insertionPoint = selection.end + text = @buffer.getTextInRange(range) + + console.log(text) + + @buffer.insert(insertionPoint, text) + @buffer.delete(range) + translatedRanges.push(selection.translate(translationDelta)) + + @setSelectedBufferRanges(translatedRanges) + moveSelectionRight: -> selections = @getSelectedBufferRanges() From 93e019407d99e83d170d73f781fbb5c7d40b4242 Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Sun, 3 May 2015 20:32:44 -0400 Subject: [PATCH 003/971] :fire: Errant console.log --- src/text-editor.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 4071e5cfc..e7dcd3f26 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -945,8 +945,6 @@ class TextEditor extends Model insertionPoint = selection.end text = @buffer.getTextInRange(range) - console.log(text) - @buffer.insert(insertionPoint, text) @buffer.delete(range) translatedRanges.push(selection.translate(translationDelta)) From 7a928be1143e3d7b2923afe10f9d886953d32d19 Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Sun, 3 May 2015 20:58:52 -0400 Subject: [PATCH 004/971] Rename range to make its contents more clear --- src/text-editor.coffee | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e7dcd3f26..720171c0a 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -940,13 +940,13 @@ class TextEditor extends Model @transact => for selection in selections - range = new Range(selection.start.translate(translationDelta), selection.start) + charToLeftOfSelection = new Range(selection.start.translate(translationDelta), selection.start) insertionPoint = selection.end - text = @buffer.getTextInRange(range) + text = @buffer.getTextInRange(charToLeftOfSelection) @buffer.insert(insertionPoint, text) - @buffer.delete(range) + @buffer.delete(charToLeftOfSelection) translatedRanges.push(selection.translate(translationDelta)) @setSelectedBufferRanges(translatedRanges) @@ -959,12 +959,12 @@ class TextEditor extends Model @transact => for selection in selections - range = new Range(selection.end, selection.end.translate(translationDelta)) + charToRightOfSelection = new Range(selection.end, selection.end.translate(translationDelta)) insertionPoint = selection.start - text = @buffer.getTextInRange(range) + text = @buffer.getTextInRange(charToRightOfSelection) - @buffer.delete(range) + @buffer.delete(charToRightOfSelection) @buffer.insert(insertionPoint, text) translatedRanges.push(selection.translate(translationDelta)) From 4931609d4ba0109cafd60cc934653ba0749243c5 Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Sun, 3 May 2015 22:55:55 -0400 Subject: [PATCH 005/971] Add specs for moveSelectionLeft() --- spec/text-editor-spec.coffee | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 53eb32acb..df87a29e4 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3427,6 +3427,36 @@ describe "TextEditor", -> expect(cursor1.getBufferPosition()).toEqual [0, 0] expect(cursor3.getBufferPosition()).toEqual [1, 2] + describe ".moveSelectionLeft()", -> + it "moves one active selection on one line one column to the left", -> + editor.setSelectedBufferRange [[0, 4], [0, 13]] + expect(editor.getSelectedText()).toBe 'quicksort' + editor.moveSelectionLeft() + expect(editor.getSelectedText()).toBe 'quicksort' + expect(editor.getSelectedBufferRange()).toEqual [[0, 3], [0, 12]] + + it "moves multiple active selections on one line one column to the left", -> + editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[0, 16], [0, 24]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'function' + editor.moveSelectionLeft() + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'function' + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 3], [0, 12]], [[0, 15], [0, 23]]] + + it "moves multiple active selections on multiple lines one column to the left", -> + editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'sort' + editor.moveSelectionLeft() + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'sort' + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 3], [0, 12]], [[1, 5], [1, 9]]] + describe 'reading text', -> it '.lineTextForScreenRow(row)', -> editor.foldBufferRow(4) From c2132114af7fb8c0dd01ca07e6de51bcacada546 Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Sun, 3 May 2015 22:59:17 -0400 Subject: [PATCH 006/971] Add specs for moveSelectionRight() --- spec/text-editor-spec.coffee | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index df87a29e4..83747aee4 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3457,6 +3457,36 @@ describe "TextEditor", -> expect(selections[1].getText()).toBe 'sort' expect(editor.getSelectedBufferRanges()).toEqual [[[0, 3], [0, 12]], [[1, 5], [1, 9]]] + describe ".moveSelectionRight()", -> + it "moves one active selection on one line one column to the right", -> + editor.setSelectedBufferRange [[0, 4], [0, 13]] + expect(editor.getSelectedText()).toBe 'quicksort' + editor.moveSelectionRight() + expect(editor.getSelectedText()).toBe 'quicksort' + expect(editor.getSelectedBufferRange()).toEqual [[0, 5], [0, 14]] + + it "moves multiple active selections on one line one column to the right", -> + editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[0, 16], [0, 24]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'function' + editor.moveSelectionRight() + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'function' + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 5], [0, 14]], [[0, 17], [0, 25]]] + + it "moves multiple active selections on multiple lines one column to the right", -> + editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'sort' + editor.moveSelectionRight() + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'sort' + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 5], [0, 14]], [[1, 7], [1, 11]]] + describe 'reading text', -> it '.lineTextForScreenRow(row)', -> editor.foldBufferRow(4) From aabde38f614ac781b230c92e3e965759c480cd49 Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Sun, 3 May 2015 23:32:52 -0400 Subject: [PATCH 007/971] Fix case when selection is at the beginning or end of a line --- src/text-editor.coffee | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 720171c0a..0594b7365 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -942,14 +942,15 @@ class TextEditor extends Model for selection in selections charToLeftOfSelection = new Range(selection.start.translate(translationDelta), selection.start) - insertionPoint = selection.end - text = @buffer.getTextInRange(charToLeftOfSelection) + unless charToLeftOfSelection.start.column < 0 + insertionPoint = selection.end + text = @buffer.getTextInRange(charToLeftOfSelection) - @buffer.insert(insertionPoint, text) - @buffer.delete(charToLeftOfSelection) - translatedRanges.push(selection.translate(translationDelta)) + @buffer.insert(insertionPoint, text) + @buffer.delete(charToLeftOfSelection) + translatedRanges.push(selection.translate(translationDelta)) - @setSelectedBufferRanges(translatedRanges) + @setSelectedBufferRanges(translatedRanges) if translatedRanges.length > 0 moveSelectionRight: -> selections = @getSelectedBufferRanges() @@ -961,14 +962,15 @@ class TextEditor extends Model for selection in selections charToRightOfSelection = new Range(selection.end, selection.end.translate(translationDelta)) - insertionPoint = selection.start - text = @buffer.getTextInRange(charToRightOfSelection) + unless charToRightOfSelection.end.column > @buffer.lineLengthForRow(charToRightOfSelection.end.row) + insertionPoint = selection.start + text = @buffer.getTextInRange(charToRightOfSelection) - @buffer.delete(charToRightOfSelection) - @buffer.insert(insertionPoint, text) - translatedRanges.push(selection.translate(translationDelta)) + @buffer.delete(charToRightOfSelection) + @buffer.insert(insertionPoint, text) + translatedRanges.push(selection.translate(translationDelta)) - @setSelectedBufferRanges(translatedRanges) + @setSelectedBufferRanges(translatedRanges) if translatedRanges.length > 0 # Duplicate the most recent cursor's current line. duplicateLines: -> From e269b3bad9bc2e1b5329af9c8bbd2e86d2d5aff5 Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Sun, 3 May 2015 23:43:19 -0400 Subject: [PATCH 008/971] :art: --- src/text-editor.coffee | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 0594b7365..8ecd5bf40 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -943,10 +943,9 @@ class TextEditor extends Model charToLeftOfSelection = new Range(selection.start.translate(translationDelta), selection.start) unless charToLeftOfSelection.start.column < 0 - insertionPoint = selection.end text = @buffer.getTextInRange(charToLeftOfSelection) - @buffer.insert(insertionPoint, text) + @buffer.insert(selection.end, text) @buffer.delete(charToLeftOfSelection) translatedRanges.push(selection.translate(translationDelta)) @@ -963,11 +962,10 @@ class TextEditor extends Model charToRightOfSelection = new Range(selection.end, selection.end.translate(translationDelta)) unless charToRightOfSelection.end.column > @buffer.lineLengthForRow(charToRightOfSelection.end.row) - insertionPoint = selection.start text = @buffer.getTextInRange(charToRightOfSelection) @buffer.delete(charToRightOfSelection) - @buffer.insert(insertionPoint, text) + @buffer.insert(selection.start, text) translatedRanges.push(selection.translate(translationDelta)) @setSelectedBufferRanges(translatedRanges) if translatedRanges.length > 0 From 8479e1c34f5866d97998e9fdcfdf6e39cdbb74ec Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Sun, 3 May 2015 23:59:33 -0400 Subject: [PATCH 009/971] Fix selection behavior at beginning or end of line --- src/text-editor.coffee | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 8ecd5bf40..c35438cdb 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -942,14 +942,16 @@ class TextEditor extends Model for selection in selections charToLeftOfSelection = new Range(selection.start.translate(translationDelta), selection.start) - unless charToLeftOfSelection.start.column < 0 + if charToLeftOfSelection.start.column < 0 + translatedRanges.push(selection) + else text = @buffer.getTextInRange(charToLeftOfSelection) @buffer.insert(selection.end, text) @buffer.delete(charToLeftOfSelection) translatedRanges.push(selection.translate(translationDelta)) - @setSelectedBufferRanges(translatedRanges) if translatedRanges.length > 0 + @setSelectedBufferRanges(translatedRanges) moveSelectionRight: -> selections = @getSelectedBufferRanges() @@ -961,14 +963,16 @@ class TextEditor extends Model for selection in selections charToRightOfSelection = new Range(selection.end, selection.end.translate(translationDelta)) - unless charToRightOfSelection.end.column > @buffer.lineLengthForRow(charToRightOfSelection.end.row) + if charToRightOfSelection.end.column > @buffer.lineLengthForRow(charToRightOfSelection.end.row) + translatedRanges.push(selection) + else text = @buffer.getTextInRange(charToRightOfSelection) @buffer.delete(charToRightOfSelection) @buffer.insert(selection.start, text) translatedRanges.push(selection.translate(translationDelta)) - @setSelectedBufferRanges(translatedRanges) if translatedRanges.length > 0 + @setSelectedBufferRanges(translatedRanges) # Duplicate the most recent cursor's current line. duplicateLines: -> From f396b972987ec170a32cbbc411aa057f464f7bf0 Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Mon, 4 May 2015 00:11:42 -0400 Subject: [PATCH 010/971] Add specs for first / last column cases --- spec/text-editor-spec.coffee | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 83747aee4..ee1d513ae 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3457,6 +3457,19 @@ describe "TextEditor", -> expect(selections[1].getText()).toBe 'sort' expect(editor.getSelectedBufferRanges()).toEqual [[[0, 3], [0, 12]], [[1, 5], [1, 9]]] + describe "when a selection is at the first column of a line", -> + it "does not change the selection", -> + editor.setSelectedBufferRanges([[[0, 0], [0, 3]], [[1, 0], [1, 3]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'var' + expect(selections[1].getText()).toBe ' v' + editor.moveSelectionLeft() + editor.moveSelectionLeft() + expect(selections[0].getText()).toBe 'var' + expect(selections[1].getText()).toBe ' v' + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]], [[1, 0], [1, 3]]] + describe ".moveSelectionRight()", -> it "moves one active selection on one line one column to the right", -> editor.setSelectedBufferRange [[0, 4], [0, 13]] @@ -3487,6 +3500,19 @@ describe "TextEditor", -> expect(selections[1].getText()).toBe 'sort' expect(editor.getSelectedBufferRanges()).toEqual [[[0, 5], [0, 14]], [[1, 7], [1, 11]]] + describe "when a selection is at the last column of a line", -> + it "does not change the selection", -> + editor.setSelectedBufferRanges([[[2, 34], [2, 40]], [[5, 22], [5, 30]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'items;' + expect(selections[1].getText()).toBe 'shift();' + editor.moveSelectionRight() + editor.moveSelectionRight() + expect(selections[0].getText()).toBe 'items;' + expect(selections[1].getText()).toBe 'shift();' + expect(editor.getSelectedBufferRanges()).toEqual [[[2, 34], [2, 40]], [[5, 22], [5, 30]]] + describe 'reading text', -> it '.lineTextForScreenRow(row)', -> editor.foldBufferRow(4) From 7028fb4add1670518c6fcd924c8fc4e3a95448c8 Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Mon, 4 May 2015 00:17:43 -0400 Subject: [PATCH 011/971] Add Linux and Windows menu items --- menus/linux.cson | 7 +++++++ menus/win32.cson | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/menus/linux.cson b/menus/linux.cson index 9363d02e5..6253f21eb 100644 --- a/menus/linux.cson +++ b/menus/linux.cson @@ -48,6 +48,13 @@ { label: '&Join Lines', command: 'editor:join-lines' } ] } + { + label: 'Columns', + submenu: [ + { label: 'Move Selection &Left', command: 'editor:move-selection-left' } + { label: 'Move Selection &Right', command: 'editor:move-selection-right' } + ] + } { label: 'Text', submenu: [ diff --git a/menus/win32.cson b/menus/win32.cson index 068817888..f612983f7 100644 --- a/menus/win32.cson +++ b/menus/win32.cson @@ -55,6 +55,13 @@ { label: '&Join Lines', command: 'editor:join-lines' } ] } + { + label: 'Columns', + submenu: [ + { label: 'Move Selection &Left', command: 'editor:move-selection-left' } + { label: 'Move Selection &Right', command: 'editor:move-selection-right' } + ] + } { label: 'Text', submenu: [ From b0f38066fa7d188115e6f257f9e34831eee138f1 Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Mon, 4 May 2015 00:39:07 -0400 Subject: [PATCH 012/971] :art: Add comments to new methods --- src/text-editor.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c35438cdb..106309235 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -932,6 +932,7 @@ class TextEditor extends Model @setSelectedBufferRange(selection.translate([insertDelta]), preserveFolds: true, autoscroll: true) + # Move any active selections one column to the left. moveSelectionLeft: -> selections = @getSelectedBufferRanges() @@ -953,6 +954,7 @@ class TextEditor extends Model @setSelectedBufferRanges(translatedRanges) + # Move any active selections one column to the right. moveSelectionRight: -> selections = @getSelectedBufferRanges() From 98aac5aba62da12692fbb88ae2c2503a13d80620 Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Mon, 4 May 2015 10:04:54 -0400 Subject: [PATCH 013/971] :art: Better name for text --- src/text-editor.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 106309235..54f1d61de 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -946,9 +946,9 @@ class TextEditor extends Model if charToLeftOfSelection.start.column < 0 translatedRanges.push(selection) else - text = @buffer.getTextInRange(charToLeftOfSelection) + charTextToLeftOfSelection = @buffer.getTextInRange(charToLeftOfSelection) - @buffer.insert(selection.end, text) + @buffer.insert(selection.end, charTextToLeftOfSelection) @buffer.delete(charToLeftOfSelection) translatedRanges.push(selection.translate(translationDelta)) @@ -968,10 +968,10 @@ class TextEditor extends Model if charToRightOfSelection.end.column > @buffer.lineLengthForRow(charToRightOfSelection.end.row) translatedRanges.push(selection) else - text = @buffer.getTextInRange(charToRightOfSelection) + charTextToRightOfSelection = @buffer.getTextInRange(charToRightOfSelection) @buffer.delete(charToRightOfSelection) - @buffer.insert(selection.start, text) + @buffer.insert(selection.start, charTextToRightOfSelection) translatedRanges.push(selection.translate(translationDelta)) @setSelectedBufferRanges(translatedRanges) From de186c1d0eea729a3fdf8467152aef202f06a0a3 Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Thu, 7 May 2015 13:03:19 -0400 Subject: [PATCH 014/971] Add Linux and Windows keybindings for move selection --- keymaps/linux.cson | 2 ++ keymaps/win32.cson | 2 ++ 2 files changed, 4 insertions(+) diff --git a/keymaps/linux.cson b/keymaps/linux.cson index cd71d99e7..31218b428 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -15,6 +15,8 @@ 'ctrl-shift-pageup': 'pane:move-item-left' 'ctrl-shift-pagedown': 'pane:move-item-right' 'F11': 'window:toggle-full-screen' + 'alt-shift-left': 'editor:move-selection-left' + 'alt-shift-right': 'editor:move-selection-right' # Sublime Parity 'ctrl-,': 'application:show-settings' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index 5d3629386..7422d045d 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -19,6 +19,8 @@ 'ctrl-shift-left': 'pane:move-item-left' 'ctrl-shift-right': 'pane:move-item-right' 'F11': 'window:toggle-full-screen' + 'alt-shift-left': 'editor:move-selection-left' + 'alt-shift-right': 'editor:move-selection-right' # Sublime Parity 'ctrl-,': 'application:show-settings' From b5c9a90ae00ec44e91782b0d6e30be7ca2fea11c Mon Sep 17 00:00:00 2001 From: Einar Boson Date: Sun, 5 Jul 2015 01:04:15 +0200 Subject: [PATCH 015/971] :bug: Ask user to 'save as' if save fails when closing tab, fixes #7708 --- spec/pane-spec.coffee | 98 +++++++++++++++++++++++++++++++++++++++++++ src/pane.coffee | 58 +++++++++++++++++-------- 2 files changed, 139 insertions(+), 17 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 3453ac44d..bdeb9615a 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -671,6 +671,104 @@ describe "Pane", -> expect(item1.save).not.toHaveBeenCalled() expect(pane.isDestroyed()).toBe false + it "does not destroy the pane if save fails and user clicks cancel", -> + pane = new Pane(items: [new Item("A"), new Item("B")]) + [item1, item2] = pane.getItems() + + item1.shouldPromptToSave = -> true + item1.getURI = -> "/test/path" + + item1.save = jasmine.createSpy("save").andCallFake -> + error = new Error("EACCES, permission denied '/test/path'") + error.path = '/test/path' + error.code = 'EACCES' + throw error + + confirmations = 0 + spyOn(atom, 'confirm').andCallFake -> + confirmations++ + if confirmations is 1 + return 0 + else + return 1 + + pane.close() + + expect(atom.confirm).toHaveBeenCalled() + expect(confirmations).toBe(2) + expect(item1.save).toHaveBeenCalled() + expect(pane.isDestroyed()).toBe false + + it "does destroy the pane if the user saves the file under a new name", -> + pane = new Pane(items: [new Item("A"), new Item("B")]) + [item1, item2] = pane.getItems() + + item1.shouldPromptToSave = -> true + item1.getURI = -> "/test/path" + + item1.save = jasmine.createSpy("save").andCallFake -> + error = new Error("EACCES, permission denied '/test/path'") + error.path = '/test/path' + error.code = 'EACCES' + throw error + + item1.saveAs = jasmine.createSpy("saveAs").andReturn(true) + + confirmations = 0 + spyOn(atom, 'confirm').andCallFake -> + confirmations++ + return 0 + + spyOn(atom, 'showSaveDialogSync').andReturn("new/path") + + pane.close() + + expect(atom.confirm).toHaveBeenCalled() + expect(confirmations).toBe(2) + expect(atom.showSaveDialogSync).toHaveBeenCalled() + expect(item1.save).toHaveBeenCalled() + expect(item1.saveAs).toHaveBeenCalled() + expect(pane.isDestroyed()).toBe true + + it "asks again if the saveAs also fails", -> + pane = new Pane(items: [new Item("A"), new Item("B")]) + [item1, item2] = pane.getItems() + + item1.shouldPromptToSave = -> true + item1.getURI = -> "/test/path" + + item1.save = jasmine.createSpy("save").andCallFake -> + error = new Error("EACCES, permission denied '/test/path'") + error.path = '/test/path' + error.code = 'EACCES' + throw error + + item1.saveAs = jasmine.createSpy("saveAs").andCallFake -> + error = new Error("EACCES, permission denied '/test/path'") + error.path = '/test/path' + error.code = 'EACCES' + throw error + + + confirmations = 0 + spyOn(atom, 'confirm').andCallFake -> + confirmations++ + if confirmations < 3 + return 0 + return 2 + + spyOn(atom, 'showSaveDialogSync').andReturn("new/path") + + pane.close() + + expect(atom.confirm).toHaveBeenCalled() + expect(confirmations).toBe(3) + expect(atom.showSaveDialogSync).toHaveBeenCalled() + expect(item1.save).toHaveBeenCalled() + expect(item1.saveAs).toHaveBeenCalled() + expect(pane.isDestroyed()).toBe true + + describe "::destroy()", -> [container, pane1, pane2] = [] diff --git a/src/pane.coffee b/src/pane.coffee index f77da9d58..759146564 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -454,13 +454,26 @@ class Pane extends Model else return true + saveError = (error) => + if error + chosen = atom.confirm + message: @getSaveErrorMessage(error) + detailedMessage: "Your changes will be lost if you close this item without saving." + buttons: ["Save as", "Cancel", "Don't save"] + switch chosen + when 0 then @saveItemAs item, saveError + when 1 then false + when 2 then true + else + true + chosen = atom.confirm message: "'#{item.getTitle?() ? uri}' has changes, do you want to save them?" detailedMessage: "Your changes will be lost if you close this item without saving." buttons: ["Save", "Cancel", "Don't Save"] switch chosen - when 0 then @saveItem(item, -> true) + when 0 then @saveItem(item, saveError) when 1 then false when 2 then true @@ -479,8 +492,10 @@ class Pane extends Model # Public: Save the given item. # # * `item` The item to save. - # * `nextAction` (optional) {Function} which will be called after the item is - # successfully saved. + # * `nextAction` (optional) {Function} which will be called with no argument + # after the item is successfully saved, or with the error if it failed. + # The return value will be that of `nextAction` or `undefined` if it was not + # provided saveItem: (item, nextAction) -> if typeof item?.getURI is 'function' itemURI = item.getURI() @@ -490,9 +505,9 @@ class Pane extends Model if itemURI? try item.save?() + nextAction?() catch error - @handleSaveError(error) - nextAction?() + (nextAction ? @handleSaveError)(error) else @saveItemAs(item, nextAction) @@ -500,8 +515,10 @@ class Pane extends Model # path they select. # # * `item` The item to save. - # * `nextAction` (optional) {Function} which will be called after the item is - # successfully saved. + # * `nextAction` (optional) {Function} which will be called with no argument + # after the item is successfully saved, or with the error if it failed. + # The return value will be that of `nextAction` or `undefined` if it was not + # provided saveItemAs: (item, nextAction) -> return unless item?.saveAs? @@ -511,9 +528,9 @@ class Pane extends Model if newItemPath try item.saveAs(newItemPath) + nextAction?() catch error - @handleSaveError(error) - nextAction?() + (nextAction ? @handleSaveError)(error) # Public: Save all items. saveItems: -> @@ -676,22 +693,29 @@ class Pane extends Model return false unless @promptToSaveItem(item) true - handleSaveError: (error) -> + # Translate an error object to a human readable string + getSaveErrorMessage: (error) -> if error.code is 'EISDIR' or error.message.endsWith('is a directory') - atom.notifications.addWarning("Unable to save file: #{error.message}") + "Unable to save file: #{error.message}." else if error.code is 'EACCES' and error.path? - atom.notifications.addWarning("Unable to save file: Permission denied '#{error.path}'") + "Unable to save file: Permission denied '#{error.path}'." else if error.code in ['EPERM', 'EBUSY', 'UNKNOWN', 'EEXIST'] and error.path? - atom.notifications.addWarning("Unable to save file '#{error.path}'", detail: error.message) + "Unable to save file '#{error.path}': #{error.message}." else if error.code is 'EROFS' and error.path? - atom.notifications.addWarning("Unable to save file: Read-only file system '#{error.path}'") + "Unable to save file: Read-only file system '#{error.path}'." else if error.code is 'ENOSPC' and error.path? - atom.notifications.addWarning("Unable to save file: No space left on device '#{error.path}'") + "Unable to save file: No space left on device '#{error.path}'." else if error.code is 'ENXIO' and error.path? - atom.notifications.addWarning("Unable to save file: No such device or address '#{error.path}'") + "Unable to save file: No such device or address '#{error.path}'." else if errorMatch = /ENOTDIR, not a directory '([^']+)'/.exec(error.message) fileName = errorMatch[1] - atom.notifications.addWarning("Unable to save file: A directory in the path '#{fileName}' could not be written to") + "Unable to save file: A directory in the path '#{fileName}' could not be written to." + + # Display a popup warning to the user + handleSaveError: (error) -> + errorMessage = Pane::getSaveErrorMessage(error) + if errorMessage? + atom.notifications.addWarning(errorMessage) else throw error From f672f8ea925b804e49b623ef4b9acddd6827928b Mon Sep 17 00:00:00 2001 From: Giuseppe Piscopo Date: Tue, 15 Sep 2015 11:44:38 +0200 Subject: [PATCH 016/971] squirrel-update test on desktop shortcut groups too many assertions Every test on desktop shortcut now tends to have 1 assertion. They have been rearranged to show more clearly what is the actual test intent. Expectation already checked somewhere else have been removed. --- spec/squirrel-update-spec.coffee | 91 +++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 32 deletions(-) diff --git a/spec/squirrel-update-spec.coffee b/spec/squirrel-update-spec.coffee index b71a9b0c4..e3891ed1c 100644 --- a/spec/squirrel-update-spec.coffee +++ b/spec/squirrel-update-spec.coffee @@ -5,25 +5,25 @@ path = require 'path' temp = require 'temp' SquirrelUpdate = require '../src/browser/squirrel-update' -describe "Windows squirrel updates", -> +describe "Windows Squirrel Update", -> tempHomeDirectory = null + originalSpawn = ChildProcess.spawn + + harmlessSpawn = -> + # Just spawn something that won't actually modify the host + if process.platform is 'win32' + originalSpawn('dir') + else + originalSpawn('ls') beforeEach -> - # Prevent the actually home directory from being manipulated + # Prevent the actual home directory from being manipulated tempHomeDirectory = temp.mkdirSync('atom-temp-home-') spyOn(fs, 'getHomeDirectory').andReturn(tempHomeDirectory) # Prevent any commands from actually running and affecting the host - originalSpawn = ChildProcess.spawn spyOn(ChildProcess, 'spawn').andCallFake (command, args) -> - if path.basename(command) is 'Update.exe' and args?[0] is '--createShortcut' - fs.writeFileSync(path.join(tempHomeDirectory, 'Desktop', 'Atom.lnk'), '') - - # Just spawn something that won't actually modify the host - if process.platform is 'win32' - originalSpawn('dir') - else - originalSpawn('ls') + harmlessSpawn() it "ignores errors spawning Squirrel", -> jasmine.unspy(ChildProcess, 'spawn') @@ -67,28 +67,55 @@ describe "Windows squirrel updates", -> runs -> expect(SquirrelUpdate.handleStartupEvent(app, '--not-squirrel')).toBe false - it "keeps the desktop shortcut deleted on updates if it was previously deleted after install", -> - desktopShortcutPath = path.join(tempHomeDirectory, 'Desktop', 'Atom.lnk') - expect(fs.existsSync(desktopShortcutPath)).toBe false - - app = quit: jasmine.createSpy('quit') - expect(SquirrelUpdate.handleStartupEvent(app, '--squirrel-install')).toBe true - - waitsFor -> - app.quit.callCount is 1 - - runs -> - app.quit.reset() - expect(fs.existsSync(desktopShortcutPath)).toBe true - fs.removeSync(desktopShortcutPath) - expect(fs.existsSync(desktopShortcutPath)).toBe false - expect(SquirrelUpdate.handleStartupEvent(app, '--squirrel-updated')).toBe true - - waitsFor -> - app.quit.callCount is 1 - - runs -> + describe "Desktop shortcut", -> + desktopShortcutPath = '/non/existing/path' + + beforeEach -> + desktopShortcutPath = path.join(tempHomeDirectory, 'Desktop', 'Atom.lnk') + + jasmine.unspy(ChildProcess, 'spawn') + spyOn(ChildProcess, 'spawn').andCallFake (command, args) -> + if path.basename(command) is 'Update.exe' and args?[0] is '--createShortcut' + fs.writeFileSync(path.join(tempHomeDirectory, 'Desktop', 'Atom.lnk'), '') + harmlessSpawn() + else + throw new Error("API not mocked") + + it "does not exist before install", -> expect(fs.existsSync(desktopShortcutPath)).toBe false + + describe "on install", -> + beforeEach -> + app = quit: jasmine.createSpy('quit') + SquirrelUpdate.handleStartupEvent(app, '--squirrel-install') + waitsFor -> + app.quit.callCount is 1 + + it "creates desktop shortcut", -> + expect(fs.existsSync(desktopShortcutPath)).toBe true + + describe "when shortcut is deleted and then app is updated", -> + beforeEach -> + fs.removeSync(desktopShortcutPath) + expect(fs.existsSync(desktopShortcutPath)).toBe false + + app = quit: jasmine.createSpy('quit') + SquirrelUpdate.handleStartupEvent(app, '--squirrel-updated') + waitsFor -> + app.quit.callCount is 1 + + it "does not recreate shortcut", -> + expect(fs.existsSync(desktopShortcutPath)).toBe false + + describe "when shortcut is kept and app is updated", -> + beforeEach -> + app = quit: jasmine.createSpy('quit') + SquirrelUpdate.handleStartupEvent(app, '--squirrel-updated') + waitsFor -> + app.quit.callCount is 1 + + it "still has desktop shortcut", -> + expect(fs.existsSync(desktopShortcutPath)).toBe true describe ".restartAtom", -> it "quits the app and spawns a new one", -> From 74c0e79986c25d04bdd57f80a72840aa91747861 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 16 Nov 2015 09:34:29 -0500 Subject: [PATCH 017/971] :arrow_up: electron@0.35.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7a586cd4a..ab90b1a55 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.34.3", + "electronVersion": "0.35.0", "dependencies": { "async": "0.2.6", "atom-keymap": "^6.1.1", From 538500042c7d3878aa0afb7060343ff1e05bf400 Mon Sep 17 00:00:00 2001 From: Collin Donahue-Oponski Date: Mon, 16 Nov 2015 22:05:23 -0700 Subject: [PATCH 018/971] :bug: Autoscroll to cursor after clearing multi-cursor selection. --- src/text-editor.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e5b3bd481..501c52a43 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2445,6 +2445,7 @@ class TextEditor extends Model selections = @getSelections() if selections.length > 1 selection.destroy() for selection in selections[1...(selections.length)] + selections[0].autoscroll() true else false From 6cd480b37e486738f376a7692f609bd673831c0a Mon Sep 17 00:00:00 2001 From: Wliu Date: Tue, 17 Nov 2015 21:16:09 -0500 Subject: [PATCH 019/971] Fix Electron deprecations --- spec/atom-environment-spec.coffee | 6 +++--- spec/atom-reporter.coffee | 2 +- spec/jasmine-test-runner.coffee | 2 +- spec/text-editor-component-spec.js | 2 +- spec/window-event-handler-spec.coffee | 2 +- spec/workspace-element-spec.coffee | 2 +- src/application-delegate.coffee | 4 ++-- src/atom-environment.coffee | 6 +++--- src/browser/application-menu.coffee | 2 +- src/browser/atom-application.coffee | 2 +- src/browser/atom-portable.coffee | 2 +- src/browser/atom-window.coffee | 2 +- src/browser/auto-update-manager.coffee | 2 +- src/browser/auto-updater-win32.coffee | 2 +- src/browser/main.coffee | 4 ++-- src/initialize-test-window.coffee | 2 +- src/menu-manager.coffee | 2 +- src/module-cache.coffee | 2 +- src/register-default-commands.coffee | 2 +- src/text-editor-component.coffee | 2 +- src/workspace-element.coffee | 2 +- static/index.js | 2 +- 22 files changed, 28 insertions(+), 28 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 23f8e0e51..2d8fd70ac 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -47,7 +47,7 @@ describe "AtomEnvironment", -> describe "window onerror handler", -> beforeEach -> spyOn atom, 'openDevTools' - spyOn atom, 'executeJavaScriptInDevTools' + spyOn atom, 'webContents.executeJavaScriptInDevTools' it "will open the dev tools when an error is triggered", -> try @@ -56,7 +56,7 @@ describe "AtomEnvironment", -> window.onerror.call(window, e.toString(), 'abc', 2, 3, e) expect(atom.openDevTools).toHaveBeenCalled() - expect(atom.executeJavaScriptInDevTools).toHaveBeenCalled() + expect(atom.webContents.executeJavaScriptInDevTools).toHaveBeenCalled() describe "::onWillThrowError", -> willThrowSpy = null @@ -91,7 +91,7 @@ describe "AtomEnvironment", -> expect(willThrowSpy).toHaveBeenCalled() expect(atom.openDevTools).not.toHaveBeenCalled() - expect(atom.executeJavaScriptInDevTools).not.toHaveBeenCalled() + expect(atom.webContents.executeJavaScriptInDevTools).not.toHaveBeenCalled() describe "::onDidThrowError", -> didThrowSpy = null diff --git a/spec/atom-reporter.coffee b/spec/atom-reporter.coffee index 35caecb34..9bc4bdbf9 100644 --- a/spec/atom-reporter.coffee +++ b/spec/atom-reporter.coffee @@ -173,7 +173,7 @@ class AtomReporter listen document, 'click', '.stack-trace', (event) -> event.currentTarget.classList.toggle('expanded') - @reloadButton.addEventListener('click', -> require('ipc').send('call-window-method', 'restart')) + @reloadButton.addEventListener('click', -> require('ipc-main').send('call-window-method', 'restart')) updateSpecCounts: -> if @skippedCount diff --git a/spec/jasmine-test-runner.coffee b/spec/jasmine-test-runner.coffee index 7ef34ce54..64191c1f4 100644 --- a/spec/jasmine-test-runner.coffee +++ b/spec/jasmine-test-runner.coffee @@ -2,7 +2,7 @@ fs = require 'fs' _ = require 'underscore-plus' fs = require 'fs-plus' path = require 'path' -ipc = require 'ipc' +ipc = require 'ipc-main' module.exports = ({logFile, headless, testPaths, buildAtomEnvironment}) -> window[key] = value for key, value of require '../vendor/jasmine' diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 609d20291..a7b6818f5 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -4585,7 +4585,7 @@ describe('TextEditorComponent', function () { it('pastes the previously selected text at the clicked location', async function () { let clipboardWrittenTo = false - spyOn(require('ipc'), 'send').andCallFake(function (eventName, selectedText) { + spyOn(require('ipc-main'), 'send').andCallFake(function (eventName, selectedText) { if (eventName === 'write-text-to-selection-clipboard') { require('../src/safe-clipboard').writeText(selectedText, 'selection') clipboardWrittenTo = true diff --git a/spec/window-event-handler-spec.coffee b/spec/window-event-handler-spec.coffee index 3148942b4..9cd68d5e8 100644 --- a/spec/window-event-handler-spec.coffee +++ b/spec/window-event-handler-spec.coffee @@ -4,7 +4,7 @@ fs = require 'fs-plus' temp = require 'temp' TextEditor = require '../src/text-editor' WindowEventHandler = require '../src/window-event-handler' -ipc = require 'ipc' +ipc = require 'ipc-main' describe "WindowEventHandler", -> [projectPath, windowEventHandler] = [] diff --git a/spec/workspace-element-spec.coffee b/spec/workspace-element-spec.coffee index 09c54d5ea..110b8d562 100644 --- a/spec/workspace-element-spec.coffee +++ b/spec/workspace-element-spec.coffee @@ -1,4 +1,4 @@ -ipc = require 'ipc' +ipc = require 'ipc-main' path = require 'path' temp = require('temp').track() diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index 1999c6e83..ebd497c47 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -1,5 +1,5 @@ _ = require 'underscore-plus' -ipc = require 'ipc' +ipc = require 'ipc-main' remote = require 'remote' shell = require 'shell' webFrame = require 'web-frame' @@ -72,7 +72,7 @@ class ApplicationDelegate remote.getCurrentWindow().toggleDevTools() executeJavaScriptInWindowDevTools: (code) -> - remote.getCurrentWindow().executeJavaScriptInDevTools(code) + remote.getCurrentWindow().webContents.executeJavaScriptInDevTools(code) setWindowDocumentEdited: (edited) -> ipc.send("call-window-method", "setDocumentEdited", edited) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 938010607..6082ac4d0 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -1,6 +1,6 @@ crypto = require 'crypto' path = require 'path' -ipc = require 'ipc' +ipc = require 'ipc-main' _ = require 'underscore-plus' {deprecate} = require 'grim' @@ -671,7 +671,7 @@ class AtomEnvironment extends Model if openDevTools @openDevTools() - @executeJavaScriptInDevTools('DevToolsAPI.showConsole()') + @webContents.executeJavaScriptInDevTools('DevToolsAPI.showConsole()') @emitter.emit 'did-throw-error', {message, url, line, column, originalError} @@ -729,7 +729,7 @@ class AtomEnvironment extends Model @applicationDelegate.toggleWindowDevTools() # Extended: Execute code in dev tools. - executeJavaScriptInDevTools: (code) -> + webContents.executeJavaScriptInDevTools: (code) -> @applicationDelegate.executeJavaScriptInWindowDevTools(code) ### diff --git a/src/browser/application-menu.coffee b/src/browser/application-menu.coffee index 27b9df8e1..b9b4eed92 100644 --- a/src/browser/application-menu.coffee +++ b/src/browser/application-menu.coffee @@ -1,5 +1,5 @@ app = require 'app' -ipc = require 'ipc' +ipc = require 'ipc-main' Menu = require 'menu' _ = require 'underscore-plus' diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 8bb44349e..4001fba8e 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -9,7 +9,7 @@ app = require 'app' dialog = require 'dialog' shell = require 'shell' fs = require 'fs-plus' -ipc = require 'ipc' +ipc = require 'ipc-main' path = require 'path' os = require 'os' net = require 'net' diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index 5f8f10cf6..37324e615 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -1,6 +1,6 @@ fs = require 'fs-plus' path = require 'path' -ipc = require 'ipc' +ipc = require 'ipc-main' module.exports = class AtomPortable diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index c507b634c..4ab13ef0e 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -81,7 +81,7 @@ class AtomWindow loadSettings = _.clone(loadSettingsObj) delete loadSettings['windowState'] - @browserWindow.loadUrl url.format + @browserWindow.loadURL url.format protocol: 'file' pathname: "#{@resourcePath}/static/index.html" slashes: true diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index 55ab2462b..f4a493829 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -31,7 +31,7 @@ class AutoUpdateManager @setState(ErrorState) console.error "Error Downloading Update: #{message}" - autoUpdater.setFeedUrl @feedUrl + autoUpdater.setFeedURL @feedUrl autoUpdater.on 'checking-for-update', => @setState(CheckingState) diff --git a/src/browser/auto-updater-win32.coffee b/src/browser/auto-updater-win32.coffee index 4d043ac4e..13dcd9a1e 100644 --- a/src/browser/auto-updater-win32.coffee +++ b/src/browser/auto-updater-win32.coffee @@ -5,7 +5,7 @@ SquirrelUpdate = require './squirrel-update' class AutoUpdater _.extend @prototype, EventEmitter.prototype - setFeedUrl: (@updateUrl) -> + setFeedURL: (@updateUrl) -> quitAndInstall: -> if SquirrelUpdate.existsSync() diff --git a/src/browser/main.coffee b/src/browser/main.coffee index ca9d7e3ae..2a50cba94 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -59,7 +59,7 @@ setupCrashReporter = -> setupAtomHome = ({setPortable}) -> return if process.env.ATOM_HOME - atomHome = path.join(app.getHomeDir(), '.atom') + atomHome = path.join(app.getPath(), '.atom') AtomPortable = require './atom-portable' if setPortable and not AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME, atomHome) @@ -142,7 +142,7 @@ parseCommandLine = -> socketPath = args['socket-path'] profileStartup = args['profile-startup'] urlsToOpen = [] - devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH ? path.join(app.getHomeDir(), 'github', 'atom') + devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH ? path.join(app.getPath(), 'github', 'atom') setPortable = args.portable if args['resource-path'] diff --git a/src/initialize-test-window.coffee b/src/initialize-test-window.coffee index 375581a96..2eea1ec6a 100644 --- a/src/initialize-test-window.coffee +++ b/src/initialize-test-window.coffee @@ -14,7 +14,7 @@ module.exports = ({blobStore}) -> try path = require 'path' - ipc = require 'ipc' + ipc = require 'ipc-main' {getWindowLoadSettings} = require './window-load-settings-helpers' AtomEnvironment = require '../src/atom-environment' ApplicationDelegate = require '../src/application-delegate' diff --git a/src/menu-manager.coffee b/src/menu-manager.coffee index fa78d3cd6..7f73fdb41 100644 --- a/src/menu-manager.coffee +++ b/src/menu-manager.coffee @@ -1,7 +1,7 @@ path = require 'path' _ = require 'underscore-plus' -ipc = require 'ipc' +ipc = require 'ipc-main' CSON = require 'season' fs = require 'fs-plus' {Disposable} = require 'event-kit' diff --git a/src/module-cache.coffee b/src/module-cache.coffee index e9245cf40..a2840a864 100644 --- a/src/module-cache.coffee +++ b/src/module-cache.coffee @@ -208,7 +208,7 @@ registerBuiltins = (devMode) -> cache.builtins[builtin] = path.join(commonRoot, "#{builtin}.js") rendererRoot = path.join(atomShellRoot, 'renderer', 'api', 'lib') - rendererBuiltins = ['ipc', 'remote'] + rendererBuiltins = ['ipc-renderer', 'remote'] for builtin in rendererBuiltins cache.builtins[builtin] = path.join(rendererRoot, "#{builtin}.js") diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index 6c838b8c0..b873b0728 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -1,4 +1,4 @@ -ipc = require 'ipc' +ipc = require 'ipc-main' module.exports = ({commandRegistry, commandInstaller, config}) -> commandRegistry.add 'atom-workspace', diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 430b0c0fd..525c7a3af 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -2,7 +2,7 @@ _ = require 'underscore-plus' scrollbarStyle = require 'scrollbar-style' {Range, Point} = require 'text-buffer' {CompositeDisposable} = require 'event-kit' -ipc = require 'ipc' +ipc = require 'ipc-main' TextEditorPresenter = require './text-editor-presenter' GutterContainerComponent = require './gutter-container-component' diff --git a/src/workspace-element.coffee b/src/workspace-element.coffee index fb4b19dc3..5b734c3f0 100644 --- a/src/workspace-element.coffee +++ b/src/workspace-element.coffee @@ -1,4 +1,4 @@ -ipc = require 'ipc' +ipc = require 'ipc-main' path = require 'path' {Disposable, CompositeDisposable} = require 'event-kit' Grim = require 'grim' diff --git a/static/index.js b/static/index.js index 2a5bcad3a..d62efd4f0 100644 --- a/static/index.js +++ b/static/index.js @@ -83,7 +83,7 @@ var initialize = require(loadSettings.windowInitializationScript) initialize({blobStore: blobStore}) - require('ipc').sendChannel('window-command', 'window:loaded') + require('ipc-main').send('window-command', 'window:loaded') } function setupCsonCache (cacheDir) { From 6c811433cfe99b6d1432e263469fde77cee799c7 Mon Sep 17 00:00:00 2001 From: Wliu Date: Tue, 17 Nov 2015 23:11:55 -0500 Subject: [PATCH 020/971] Fix executeJavaScriptInDevTools The joy of Atom and Electron having identically-named functions --- spec/atom-environment-spec.coffee | 6 +++--- src/atom-environment.coffee | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 2d8fd70ac..23f8e0e51 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -47,7 +47,7 @@ describe "AtomEnvironment", -> describe "window onerror handler", -> beforeEach -> spyOn atom, 'openDevTools' - spyOn atom, 'webContents.executeJavaScriptInDevTools' + spyOn atom, 'executeJavaScriptInDevTools' it "will open the dev tools when an error is triggered", -> try @@ -56,7 +56,7 @@ describe "AtomEnvironment", -> window.onerror.call(window, e.toString(), 'abc', 2, 3, e) expect(atom.openDevTools).toHaveBeenCalled() - expect(atom.webContents.executeJavaScriptInDevTools).toHaveBeenCalled() + expect(atom.executeJavaScriptInDevTools).toHaveBeenCalled() describe "::onWillThrowError", -> willThrowSpy = null @@ -91,7 +91,7 @@ describe "AtomEnvironment", -> expect(willThrowSpy).toHaveBeenCalled() expect(atom.openDevTools).not.toHaveBeenCalled() - expect(atom.webContents.executeJavaScriptInDevTools).not.toHaveBeenCalled() + expect(atom.executeJavaScriptInDevTools).not.toHaveBeenCalled() describe "::onDidThrowError", -> didThrowSpy = null diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 6082ac4d0..5afdb1f8d 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -671,7 +671,7 @@ class AtomEnvironment extends Model if openDevTools @openDevTools() - @webContents.executeJavaScriptInDevTools('DevToolsAPI.showConsole()') + @executeJavaScriptInDevTools('DevToolsAPI.showConsole()') @emitter.emit 'did-throw-error', {message, url, line, column, originalError} @@ -729,7 +729,7 @@ class AtomEnvironment extends Model @applicationDelegate.toggleWindowDevTools() # Extended: Execute code in dev tools. - webContents.executeJavaScriptInDevTools: (code) -> + executeJavaScriptInDevTools: (code) -> @applicationDelegate.executeJavaScriptInWindowDevTools(code) ### From d2a86e2466960f49314f34a4b33d5f1beafa5792 Mon Sep 17 00:00:00 2001 From: Wliu Date: Wed, 18 Nov 2015 18:46:38 -0500 Subject: [PATCH 021/971] Add 'home' argument to app.getPath() --- src/browser/main.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 2a50cba94..df8b3b74a 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -59,7 +59,7 @@ setupCrashReporter = -> setupAtomHome = ({setPortable}) -> return if process.env.ATOM_HOME - atomHome = path.join(app.getPath(), '.atom') + atomHome = path.join(app.getPath('home'), '.atom') AtomPortable = require './atom-portable' if setPortable and not AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME, atomHome) @@ -142,7 +142,7 @@ parseCommandLine = -> socketPath = args['socket-path'] profileStartup = args['profile-startup'] urlsToOpen = [] - devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH ? path.join(app.getPath(), 'github', 'atom') + devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH ? path.join(app.getPath('home'), 'github', 'atom') setPortable = args.portable if args['resource-path'] From fec1507ff48f984ef978afb5d09eaf8d17f612ae Mon Sep 17 00:00:00 2001 From: Wliu Date: Wed, 18 Nov 2015 21:10:40 -0500 Subject: [PATCH 022/971] Require ipc-renderer where it belongs --- spec/jasmine-test-runner.coffee | 2 +- spec/text-editor-component-spec.js | 2 +- spec/window-event-handler-spec.coffee | 2 +- spec/workspace-element-spec.coffee | 2 +- src/application-delegate.coffee | 2 +- src/atom-environment.coffee | 2 +- src/browser/atom-portable.coffee | 2 +- src/initialize-test-window.coffee | 2 +- src/menu-manager.coffee | 2 +- src/register-default-commands.coffee | 2 +- src/text-editor-component.coffee | 2 +- src/workspace-element.coffee | 2 +- static/index.js | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/spec/jasmine-test-runner.coffee b/spec/jasmine-test-runner.coffee index 64191c1f4..8192d4328 100644 --- a/spec/jasmine-test-runner.coffee +++ b/spec/jasmine-test-runner.coffee @@ -2,7 +2,7 @@ fs = require 'fs' _ = require 'underscore-plus' fs = require 'fs-plus' path = require 'path' -ipc = require 'ipc-main' +ipc = require 'ipc-renderer' module.exports = ({logFile, headless, testPaths, buildAtomEnvironment}) -> window[key] = value for key, value of require '../vendor/jasmine' diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index a7b6818f5..c6062d0ec 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -4585,7 +4585,7 @@ describe('TextEditorComponent', function () { it('pastes the previously selected text at the clicked location', async function () { let clipboardWrittenTo = false - spyOn(require('ipc-main'), 'send').andCallFake(function (eventName, selectedText) { + spyOn(require('ipc-renderer'), 'send').andCallFake(function (eventName, selectedText) { if (eventName === 'write-text-to-selection-clipboard') { require('../src/safe-clipboard').writeText(selectedText, 'selection') clipboardWrittenTo = true diff --git a/spec/window-event-handler-spec.coffee b/spec/window-event-handler-spec.coffee index 9cd68d5e8..052fed8c0 100644 --- a/spec/window-event-handler-spec.coffee +++ b/spec/window-event-handler-spec.coffee @@ -4,7 +4,7 @@ fs = require 'fs-plus' temp = require 'temp' TextEditor = require '../src/text-editor' WindowEventHandler = require '../src/window-event-handler' -ipc = require 'ipc-main' +ipc = require 'ipc-renderer' describe "WindowEventHandler", -> [projectPath, windowEventHandler] = [] diff --git a/spec/workspace-element-spec.coffee b/spec/workspace-element-spec.coffee index 110b8d562..01dddc5b7 100644 --- a/spec/workspace-element-spec.coffee +++ b/spec/workspace-element-spec.coffee @@ -1,4 +1,4 @@ -ipc = require 'ipc-main' +ipc = require 'ipc-renderer' path = require 'path' temp = require('temp').track() diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index c5b65a4c8..d9a032fef 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -1,5 +1,5 @@ _ = require 'underscore-plus' -ipc = require 'ipc-main' +ipc = require 'ipc-renderer' remote = require 'remote' shell = require 'shell' webFrame = require 'web-frame' diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index d2e069a85..a0049e50b 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -1,6 +1,6 @@ crypto = require 'crypto' path = require 'path' -ipc = require 'ipc-main' +ipc = require 'ipc-renderer' _ = require 'underscore-plus' {deprecate} = require 'grim' diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index 37324e615..eeda81152 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -1,6 +1,6 @@ fs = require 'fs-plus' path = require 'path' -ipc = require 'ipc-main' +ipc = require 'ipc-renderer' module.exports = class AtomPortable diff --git a/src/initialize-test-window.coffee b/src/initialize-test-window.coffee index b90eb0ed2..ad085730e 100644 --- a/src/initialize-test-window.coffee +++ b/src/initialize-test-window.coffee @@ -14,7 +14,7 @@ module.exports = ({blobStore}) -> try path = require 'path' - ipc = require 'ipc-main' + ipc = require 'ipc-renderer' {getWindowLoadSettings} = require './window-load-settings-helpers' AtomEnvironment = require '../src/atom-environment' ApplicationDelegate = require '../src/application-delegate' diff --git a/src/menu-manager.coffee b/src/menu-manager.coffee index 7f73fdb41..9eabcb697 100644 --- a/src/menu-manager.coffee +++ b/src/menu-manager.coffee @@ -1,7 +1,7 @@ path = require 'path' _ = require 'underscore-plus' -ipc = require 'ipc-main' +ipc = require 'ipc-renderer' CSON = require 'season' fs = require 'fs-plus' {Disposable} = require 'event-kit' diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index b873b0728..7e8a728bc 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -1,4 +1,4 @@ -ipc = require 'ipc-main' +ipc = require 'ipc-renderer' module.exports = ({commandRegistry, commandInstaller, config}) -> commandRegistry.add 'atom-workspace', diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 525c7a3af..c0f1486b5 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -2,7 +2,7 @@ _ = require 'underscore-plus' scrollbarStyle = require 'scrollbar-style' {Range, Point} = require 'text-buffer' {CompositeDisposable} = require 'event-kit' -ipc = require 'ipc-main' +ipc = require 'ipc-renderer' TextEditorPresenter = require './text-editor-presenter' GutterContainerComponent = require './gutter-container-component' diff --git a/src/workspace-element.coffee b/src/workspace-element.coffee index 5b734c3f0..89e990c66 100644 --- a/src/workspace-element.coffee +++ b/src/workspace-element.coffee @@ -1,4 +1,4 @@ -ipc = require 'ipc-main' +ipc = require 'ipc-renderer' path = require 'path' {Disposable, CompositeDisposable} = require 'event-kit' Grim = require 'grim' diff --git a/static/index.js b/static/index.js index d62efd4f0..651f2b54c 100644 --- a/static/index.js +++ b/static/index.js @@ -83,7 +83,7 @@ var initialize = require(loadSettings.windowInitializationScript) initialize({blobStore: blobStore}) - require('ipc-main').send('window-command', 'window:loaded') + require('ipc-renderer').send('window-command', 'window:loaded') } function setupCsonCache (cacheDir) { From 5fca7b6582dc54c812e5ce438d06e6ff9de778eb Mon Sep 17 00:00:00 2001 From: Wliu Date: Wed, 18 Nov 2015 21:20:45 -0500 Subject: [PATCH 023/971] More ipc-renderer fixes --- spec/atom-reporter.coffee | 2 +- src/browser/application-menu.coffee | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/atom-reporter.coffee b/spec/atom-reporter.coffee index 9bc4bdbf9..931e21777 100644 --- a/spec/atom-reporter.coffee +++ b/spec/atom-reporter.coffee @@ -173,7 +173,7 @@ class AtomReporter listen document, 'click', '.stack-trace', (event) -> event.currentTarget.classList.toggle('expanded') - @reloadButton.addEventListener('click', -> require('ipc-main').send('call-window-method', 'restart')) + @reloadButton.addEventListener('click', -> require('ipc-renderer').send('call-window-method', 'restart')) updateSpecCounts: -> if @skippedCount diff --git a/src/browser/application-menu.coffee b/src/browser/application-menu.coffee index b9b4eed92..f742c3a54 100644 --- a/src/browser/application-menu.coffee +++ b/src/browser/application-menu.coffee @@ -1,5 +1,4 @@ app = require 'app' -ipc = require 'ipc-main' Menu = require 'menu' _ = require 'underscore-plus' From f6328fb5115836bd71fb6739eb6072ac091c89e0 Mon Sep 17 00:00:00 2001 From: Wliu Date: Thu, 19 Nov 2015 16:37:11 -0500 Subject: [PATCH 024/971] One more ipc fix --- src/browser/atom-portable.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index eeda81152..37324e615 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -1,6 +1,6 @@ fs = require 'fs-plus' path = require 'path' -ipc = require 'ipc-renderer' +ipc = require 'ipc-main' module.exports = class AtomPortable From 50a42af9fdfb1441e6408ffad754375415142abe Mon Sep 17 00:00:00 2001 From: Wliu Date: Thu, 19 Nov 2015 18:20:51 -0500 Subject: [PATCH 025/971] ...try this? --- src/application-delegate.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index d9a032fef..e88bc321f 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -72,7 +72,8 @@ class ApplicationDelegate remote.getCurrentWindow().toggleDevTools() executeJavaScriptInWindowDevTools: (code) -> - remote.getCurrentWindow().webContents.executeJavaScriptInDevTools(code) + webContents = remote.getCurrentWindow.webContents + webContents.executeJavaScript(code) setWindowDocumentEdited: (edited) -> ipc.send("call-window-method", "setDocumentEdited", edited) From c85cd805440c6550ee745e30dd1d1c1dfed92523 Mon Sep 17 00:00:00 2001 From: Wliu Date: Thu, 19 Nov 2015 19:49:29 -0500 Subject: [PATCH 026/971] Fix missing parentheses --- src/application-delegate.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index e88bc321f..592170a35 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -72,7 +72,7 @@ class ApplicationDelegate remote.getCurrentWindow().toggleDevTools() executeJavaScriptInWindowDevTools: (code) -> - webContents = remote.getCurrentWindow.webContents + webContents = remote.getCurrentWindow().webContents webContents.executeJavaScript(code) setWindowDocumentEdited: (edited) -> From 9a850f9cb215d84b878ce7f96b6d438b594b107d Mon Sep 17 00:00:00 2001 From: Wliu Date: Thu, 19 Nov 2015 20:11:26 -0500 Subject: [PATCH 027/971] This is ridiculous --- src/browser/atom-portable.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index 37324e615..eeda81152 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -1,6 +1,6 @@ fs = require 'fs-plus' path = require 'path' -ipc = require 'ipc-main' +ipc = require 'ipc-renderer' module.exports = class AtomPortable From dfe27ff989d12f972436dc563cd8cc798a934a8b Mon Sep 17 00:00:00 2001 From: Wliu Date: Thu, 19 Nov 2015 20:49:05 -0500 Subject: [PATCH 028/971] More deprecations --- src/browser/atom-window.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 4ab13ef0e..6a36b55eb 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -89,7 +89,7 @@ class AtomWindow getLoadSettings: -> if @browserWindow.webContents? and not @browserWindow.webContents.isLoading() - hash = url.parse(@browserWindow.webContents.getUrl()).hash.substr(1) + hash = url.parse(@browserWindow.webContents.getURL()).hash.substr(1) JSON.parse(decodeURIComponent(hash)) hasProjectPath: -> @getLoadSettings().initialPaths?.length > 0 @@ -147,7 +147,7 @@ class AtomWindow when 1 then @browserWindow.restart() @browserWindow.webContents.on 'will-navigate', (event, url) => - unless url is @browserWindow.webContents.getUrl() + unless url is @browserWindow.webContents.getURL() event.preventDefault() @setupContextMenu() From b76a00672080b1cacb6cf88e01863c135c22fdc0 Mon Sep 17 00:00:00 2001 From: fscherwi Date: Sun, 22 Nov 2015 16:56:32 +0100 Subject: [PATCH 029/971] :arrow_up: pathwatcher --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b22887bf0..e12f7785b 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "^5", - "pathwatcher": "~6.2", + "pathwatcher": "~6.3", "property-accessors": "^1.1.3", "random-words": "0.0.1", "resolve": "^1.1.6", From 991d65c107d750b78613931c9d36eefa54d1ee23 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 24 Nov 2015 19:43:06 -0500 Subject: [PATCH 030/971] :arrow_up: electron@0.35.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0d9c7b221..af1dbf7a2 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.35.0", + "electronVersion": "0.35.1", "dependencies": { "async": "0.2.6", "atom-keymap": "^6.1.1", From e36cf63500742cffd764b246cecb294903626537 Mon Sep 17 00:00:00 2001 From: Wliu Date: Fri, 27 Nov 2015 11:10:31 -0500 Subject: [PATCH 031/971] :arrow_up: electron@0.35.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f0c177b42..79cdd4bee 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.35.1", + "electronVersion": "0.35.2", "dependencies": { "async": "0.2.6", "atom-keymap": "^6.1.1", From 8e6c8b02bbeb03e7488a7a46a9ca94df983de7a4 Mon Sep 17 00:00:00 2001 From: Wliu Date: Fri, 27 Nov 2015 13:20:22 -0500 Subject: [PATCH 032/971] Switch back to ipc-main in atom-portable.coffee --- src/browser/atom-portable.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index eeda81152..37324e615 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -1,6 +1,6 @@ fs = require 'fs-plus' path = require 'path' -ipc = require 'ipc-renderer' +ipc = require 'ipc-main' module.exports = class AtomPortable From 1725b9bf54252be2a2cf81d0b76bd5cb1693d918 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 11 Dec 2015 09:30:58 +0800 Subject: [PATCH 033/971] Use require('electron') --- src/application-delegate.coffee | 71 +++++++++++++++------------- src/atom-environment.coffee | 8 ++-- src/browser/atom-application.coffee | 31 +++++------- src/browser/atom-portable.coffee | 4 +- src/browser/main.coffee | 3 +- src/initialize-test-window.coffee | 12 ++--- src/menu-manager.coffee | 4 +- src/register-default-commands.coffee | 48 +++++++++---------- src/text-editor-component.coffee | 8 ++-- src/workspace-element.coffee | 4 +- static/index.js | 2 + 11 files changed, 97 insertions(+), 98 deletions(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index f1812dd25..5e82ebd15 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -1,28 +1,25 @@ _ = require 'underscore-plus' -ipc = require 'ipc-renderer' -remote = require 'remote' -shell = require 'shell' -webFrame = require 'web-frame' +{ipcRenderer, remote, shell, webFrame} = require 'electron' {Disposable} = require 'event-kit' {getWindowLoadSettings, setWindowLoadSettings} = require './window-load-settings-helpers' module.exports = class ApplicationDelegate open: (params) -> - ipc.send('open', params) + ipcRenderer.send('open', params) pickFolder: (callback) -> responseChannel = "atom-pick-folder-response" - ipc.on responseChannel, (path) -> - ipc.removeAllListeners(responseChannel) + ipcRenderer.on responseChannel, (event, path) -> + ipcRenderer.removeAllListeners(responseChannel) callback(path) - ipc.send("pick-folder", responseChannel) + ipcRenderer.send("pick-folder", responseChannel) getCurrentWindow: -> remote.getCurrentWindow() closeWindow: -> - ipc.send("call-window-method", "close") + ipcRenderer.send("call-window-method", "close") getWindowSize: -> [width, height] = remote.getCurrentWindow().getSize() @@ -36,34 +33,34 @@ class ApplicationDelegate {x, y} setWindowPosition: (x, y) -> - ipc.send("call-window-method", "setPosition", x, y) + ipcRenderer.send("call-window-method", "setPosition", x, y) centerWindow: -> - ipc.send("call-window-method", "center") + ipcRenderer.send("call-window-method", "center") focusWindow: -> - ipc.send("call-window-method", "focus") + ipcRenderer.send("call-window-method", "focus") showWindow: -> - ipc.send("call-window-method", "show") + ipcRenderer.send("call-window-method", "show") hideWindow: -> - ipc.send("call-window-method", "hide") + ipcRenderer.send("call-window-method", "hide") restartWindow: -> - ipc.send("call-window-method", "restart") + ipcRenderer.send("call-window-method", "restart") isWindowMaximized: -> remote.getCurrentWindow().isMaximized() maximizeWindow: -> - ipc.send("call-window-method", "maximize") + ipcRenderer.send("call-window-method", "maximize") isWindowFullScreen: -> remote.getCurrentWindow().isFullScreen() setWindowFullScreen: (fullScreen=false) -> - ipc.send("call-window-method", "setFullScreen", fullScreen) + ipcRenderer.send("call-window-method", "setFullScreen", fullScreen) openWindowDevTools: -> new Promise (resolve) -> @@ -75,7 +72,7 @@ class ApplicationDelegate resolve() else remote.getCurrentWindow().once("devtools-opened", -> resolve()) - ipc.send("call-window-method", "openDevTools") + ipcRenderer.send("call-window-method", "openDevTools") closeWindowDevTools: -> new Promise (resolve) -> @@ -87,7 +84,7 @@ class ApplicationDelegate resolve() else remote.getCurrentWindow().once("devtools-closed", -> resolve()) - ipc.send("call-window-method", "closeDevTools") + ipcRenderer.send("call-window-method", "closeDevTools") toggleWindowDevTools: -> new Promise (resolve) => @@ -101,13 +98,13 @@ class ApplicationDelegate @openWindowDevTools().then(resolve) executeJavaScriptInWindowDevTools: (code) -> - ipc.send("call-window-method", "executeJavaScriptInDevTools", code) + ipcRenderer.send("call-window-method", "executeJavaScriptInDevTools", code) setWindowDocumentEdited: (edited) -> - ipc.send("call-window-method", "setDocumentEdited", edited) + ipcRenderer.send("call-window-method", "setDocumentEdited", edited) setRepresentedFilename: (filename) -> - ipc.send("call-window-method", "setRepresentedFilename", filename) + ipcRenderer.send("call-window-method", "setRepresentedFilename", filename) setRepresentedDirectoryPaths: (paths) -> loadSettings = getWindowLoadSettings() @@ -115,7 +112,7 @@ class ApplicationDelegate setWindowLoadSettings(loadSettings) setAutoHideWindowMenuBar: (autoHide) -> - ipc.send("call-window-method", "setAutoHideMenuBar", autoHide) + ipcRenderer.send("call-window-method", "setAutoHideMenuBar", autoHide) setWindowMenuBarVisibility: (visible) -> remote.getCurrentWindow().setMenuBarVisibility(visible) @@ -161,35 +158,41 @@ class ApplicationDelegate shell.beep() onDidOpenLocations: (callback) -> - outerCallback = (message, detail) -> + outerCallback = (event, message, detail) -> if message is 'open-locations' callback(detail) - ipc.on('message', outerCallback) + ipcRenderer.on('message', outerCallback) new Disposable -> - ipc.removeListener('message', outerCallback) + ipcRenderer.removeListener('message', outerCallback) onUpdateAvailable: (callback) -> - outerCallback = (message, detail) -> + outerCallback = (event, message, detail) -> if message is 'update-available' callback(detail) - ipc.on('message', outerCallback) + ipcRenderer.on('message', outerCallback) new Disposable -> - ipc.removeListener('message', outerCallback) + ipcRenderer.removeListener('message', outerCallback) onApplicationMenuCommand: (callback) -> - ipc.on('command', callback) + outerCallback = (event, args...) -> + callback(args...) + + ipcRenderer.on('command', outerCallback) new Disposable -> - ipc.removeListener('command', callback) + ipcRenderer.removeListener('command', outerCallback) onContextMenuCommand: (callback) -> - ipc.on('context-command', callback) + outerCallback = (event, args...) -> + callback(args...) + + ipcRenderer.on('context-command', outerCallback) new Disposable -> - ipc.removeListener('context-command', callback) + ipcRenderer.removeListener('context-command', outerCallback) didCancelWindowUnload: -> - ipc.send('did-cancel-window-unload') + ipcRenderer.send('did-cancel-window-unload') openExternal: (url) -> shell.openExternal(url) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 027c7b547..7c1f81bba 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -1,6 +1,6 @@ crypto = require 'crypto' path = require 'path' -ipc = require 'ipc-renderer' +{ipcRenderer} = require 'electron' _ = require 'underscore-plus' {deprecate} = require 'grim' @@ -206,10 +206,10 @@ class AtomEnvironment extends Model checkPortableHomeWritable = -> responseChannel = "check-portable-home-writable-response" - ipc.on responseChannel, (response) -> - ipc.removeAllListeners(responseChannel) + ipcRenderer.on responseChannel, (event, response) -> + ipcRenderer.removeAllListeners(responseChannel) atom.notifications.addWarning("#{response.message.replace(/([\\\.+\\-_#!])/g, '\\$1')}") if not response.writable - ipc.send('check-portable-home-writable', responseChannel) + ipcRenderer.send('check-portable-home-writable', responseChannel) checkPortableHomeWritable() diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 63e3e3cf9..f721596d8 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -2,14 +2,9 @@ AtomWindow = require './atom-window' ApplicationMenu = require './application-menu' AtomProtocolHandler = require './atom-protocol-handler' AutoUpdateManager = require './auto-update-manager' -BrowserWindow = require 'browser-window' StorageFolder = require '../storage-folder' -Menu = require 'menu' -app = require 'app' -dialog = require 'dialog' -shell = require 'shell' +{BrowserWindow, Menu, app, dialog, ipcMain, shell} = require 'electron' fs = require 'fs-plus' -ipc = require 'ipc-main' path = require 'path' os = require 'os' net = require 'net' @@ -232,7 +227,7 @@ class AtomApplication @emit('application:new-window') # A request from the associated render process to open a new render process. - ipc.on 'open', (event, options) => + ipcMain.on 'open', (event, options) => window = @windowForEvent(event) if options? if typeof options.pathsToOpen is 'string' @@ -245,39 +240,39 @@ class AtomApplication else @promptForPathToOpen('all', {window}) - ipc.on 'update-application-menu', (event, template, keystrokesByCommand) => + ipcMain.on 'update-application-menu', (event, template, keystrokesByCommand) => win = BrowserWindow.fromWebContents(event.sender) @applicationMenu.update(win, template, keystrokesByCommand) - ipc.on 'run-package-specs', (event, packageSpecPath) => + ipcMain.on 'run-package-specs', (event, packageSpecPath) => @runTests({resourcePath: @devResourcePath, pathsToOpen: [packageSpecPath], headless: false}) - ipc.on 'command', (event, command) => + ipcMain.on 'command', (event, command) => @emit(command) - ipc.on 'window-command', (event, command, args...) -> + ipcMain.on 'window-command', (event, command, args...) -> win = BrowserWindow.fromWebContents(event.sender) win.emit(command, args...) - ipc.on 'call-window-method', (event, method, args...) -> + ipcMain.on 'call-window-method', (event, method, args...) -> win = BrowserWindow.fromWebContents(event.sender) win[method](args...) - ipc.on 'pick-folder', (event, responseChannel) => + ipcMain.on 'pick-folder', (event, responseChannel) => @promptForPath "folder", (selectedPaths) -> event.sender.send(responseChannel, selectedPaths) - ipc.on 'did-cancel-window-unload', => + ipcMain.on 'did-cancel-window-unload', => @quitting = false clipboard = require '../safe-clipboard' - ipc.on 'write-text-to-selection-clipboard', (event, selectedText) -> + ipcMain.on 'write-text-to-selection-clipboard', (event, selectedText) -> clipboard.writeText(selectedText, 'selection') - ipc.on 'write-to-stdout', (event, output) -> + ipcMain.on 'write-to-stdout', (event, output) -> process.stdout.write(output) - ipc.on 'write-to-stderr', (event, output) -> + ipcMain.on 'write-to-stderr', (event, output) -> process.stderr.write(output) # Public: Executes the given command. @@ -340,7 +335,7 @@ class AtomApplication _.find @windows, (atomWindow) -> atomWindow.devMode is devMode and atomWindow.containsPaths(pathsToOpen) - # Returns the {AtomWindow} for the given ipc event. + # Returns the {AtomWindow} for the given ipcMain event. windowForEvent: ({sender}) -> window = BrowserWindow.fromWebContents(sender) _.find @windows, ({browserWindow}) -> window is browserWindow diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index 37324e615..ae4bb67ec 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -1,6 +1,6 @@ fs = require 'fs-plus' path = require 'path' -ipc = require 'ipc-main' +{ipcMain} = require 'electron' module.exports = class AtomPortable @@ -30,6 +30,6 @@ class AtomPortable catch error message = "Failed to use portable Atom home directory (#{@getPortableAtomHomePath()}). Using the default instead (#{defaultHome}). #{error.message}" - ipc.on 'check-portable-home-writable', (event) -> + ipcMain.on 'check-portable-home-writable', (event) -> event.sender.send 'check-portable-home-writable-response', {writable, message} writable diff --git a/src/browser/main.coffee b/src/browser/main.coffee index df8b3b74a..e2e9d2243 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -4,8 +4,7 @@ process.on 'uncaughtException', (error={}) -> console.log(error.message) if error.message? console.log(error.stack) if error.stack? -crashReporter = require 'crash-reporter' -app = require 'app' +{crashReporter, app} = require 'electron' fs = require 'fs-plus' path = require 'path' yargs = require 'yargs' diff --git a/src/initialize-test-window.coffee b/src/initialize-test-window.coffee index ad085730e..f54d40dc3 100644 --- a/src/initialize-test-window.coffee +++ b/src/initialize-test-window.coffee @@ -4,9 +4,9 @@ cloneObject = (object) -> clone module.exports = ({blobStore}) -> + {crashReporter, remote} = require 'electron' # Start the crash reporter before anything else. - require('crash-reporter').start(productName: 'Atom', companyName: 'GitHub') - remote = require 'remote' + crashReporter.start(productName: 'Atom', companyName: 'GitHub') exitWithStatusCode = (status) -> remote.require('app').emit('will-quit') @@ -14,7 +14,7 @@ module.exports = ({blobStore}) -> try path = require 'path' - ipc = require 'ipc-renderer' + {ipcRenderer} = require 'electron' {getWindowLoadSettings} = require './window-load-settings-helpers' AtomEnvironment = require '../src/atom-environment' ApplicationDelegate = require '../src/application-delegate' @@ -29,15 +29,15 @@ module.exports = ({blobStore}) -> handleKeydown = (event) -> # Reload: cmd-r / ctrl-r if (event.metaKey or event.ctrlKey) and event.keyCode is 82 - ipc.send('call-window-method', 'restart') + ipcRenderer.send('call-window-method', 'restart') # Toggle Dev Tools: cmd-alt-i / ctrl-alt-i if (event.metaKey or event.ctrlKey) and event.altKey and event.keyCode is 73 - ipc.send('call-window-method', 'toggleDevTools') + ipcRenderer.send('call-window-method', 'toggleDevTools') # Reload: cmd-w / ctrl-w if (event.metaKey or event.ctrlKey) and event.keyCode is 87 - ipc.send('call-window-method', 'close') + ipcRenderer.send('call-window-method', 'close') window.addEventListener('keydown', handleKeydown, true) diff --git a/src/menu-manager.coffee b/src/menu-manager.coffee index 9eabcb697..67076dbfa 100644 --- a/src/menu-manager.coffee +++ b/src/menu-manager.coffee @@ -1,7 +1,7 @@ path = require 'path' _ = require 'underscore-plus' -ipc = require 'ipc-renderer' +{ipcRenderer} = require 'electron' CSON = require 'season' fs = require 'fs-plus' {Disposable} = require 'event-kit' @@ -191,7 +191,7 @@ class MenuManager sendToBrowserProcess: (template, keystrokesByCommand) -> keystrokesByCommand = @filterMultipleKeystroke(keystrokesByCommand) - ipc.send 'update-application-menu', template, keystrokesByCommand + ipcRenderer.send 'update-application-menu', template, keystrokesByCommand # Get an {Array} of {String} classes for the given element. classesForElement: (element) -> diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index b43e0a44b..ffb49880f 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -1,4 +1,4 @@ -ipc = require 'ipc-renderer' +{ipcRenderer} = require 'electron' module.exports = ({commandRegistry, commandInstaller, config}) -> commandRegistry.add 'atom-workspace', @@ -18,30 +18,30 @@ module.exports = ({commandRegistry, commandInstaller, config}) -> 'window:increase-font-size': -> @getModel().increaseFontSize() 'window:decrease-font-size': -> @getModel().decreaseFontSize() 'window:reset-font-size': -> @getModel().resetFontSize() - 'application:about': -> ipc.send('command', 'application:about') - 'application:show-preferences': -> ipc.send('command', 'application:show-settings') - 'application:show-settings': -> ipc.send('command', 'application:show-settings') - 'application:quit': -> ipc.send('command', 'application:quit') - 'application:hide': -> ipc.send('command', 'application:hide') - 'application:hide-other-applications': -> ipc.send('command', 'application:hide-other-applications') - 'application:install-update': -> ipc.send('command', 'application:install-update') - 'application:unhide-all-applications': -> ipc.send('command', 'application:unhide-all-applications') - 'application:new-window': -> ipc.send('command', 'application:new-window') - 'application:new-file': -> ipc.send('command', 'application:new-file') - 'application:open': -> ipc.send('command', 'application:open') - 'application:open-file': -> ipc.send('command', 'application:open-file') - 'application:open-folder': -> ipc.send('command', 'application:open-folder') - 'application:open-dev': -> ipc.send('command', 'application:open-dev') - 'application:open-safe': -> ipc.send('command', 'application:open-safe') + 'application:about': -> ipcRenderer.send('command', 'application:about') + 'application:show-preferences': -> ipcRenderer.send('command', 'application:show-settings') + 'application:show-settings': -> ipcRenderer.send('command', 'application:show-settings') + 'application:quit': -> ipcRenderer.send('command', 'application:quit') + 'application:hide': -> ipcRenderer.send('command', 'application:hide') + 'application:hide-other-applications': -> ipcRenderer.send('command', 'application:hide-other-applications') + 'application:install-update': -> ipcRenderer.send('command', 'application:install-update') + 'application:unhide-all-applications': -> ipcRenderer.send('command', 'application:unhide-all-applications') + 'application:new-window': -> ipcRenderer.send('command', 'application:new-window') + 'application:new-file': -> ipcRenderer.send('command', 'application:new-file') + 'application:open': -> ipcRenderer.send('command', 'application:open') + 'application:open-file': -> ipcRenderer.send('command', 'application:open-file') + 'application:open-folder': -> ipcRenderer.send('command', 'application:open-folder') + 'application:open-dev': -> ipcRenderer.send('command', 'application:open-dev') + 'application:open-safe': -> ipcRenderer.send('command', 'application:open-safe') 'application:add-project-folder': -> atom.addProjectFolder() - 'application:minimize': -> ipc.send('command', 'application:minimize') - 'application:zoom': -> ipc.send('command', 'application:zoom') - 'application:bring-all-windows-to-front': -> ipc.send('command', 'application:bring-all-windows-to-front') - 'application:open-your-config': -> ipc.send('command', 'application:open-your-config') - 'application:open-your-init-script': -> ipc.send('command', 'application:open-your-init-script') - 'application:open-your-keymap': -> ipc.send('command', 'application:open-your-keymap') - 'application:open-your-snippets': -> ipc.send('command', 'application:open-your-snippets') - 'application:open-your-stylesheet': -> ipc.send('command', 'application:open-your-stylesheet') + 'application:minimize': -> ipcRenderer.send('command', 'application:minimize') + 'application:zoom': -> ipcRenderer.send('command', 'application:zoom') + 'application:bring-all-windows-to-front': -> ipcRenderer.send('command', 'application:bring-all-windows-to-front') + 'application:open-your-config': -> ipcRenderer.send('command', 'application:open-your-config') + 'application:open-your-init-script': -> ipcRenderer.send('command', 'application:open-your-init-script') + 'application:open-your-keymap': -> ipcRenderer.send('command', 'application:open-your-keymap') + 'application:open-your-snippets': -> ipcRenderer.send('command', 'application:open-your-snippets') + 'application:open-your-stylesheet': -> ipcRenderer.send('command', 'application:open-your-stylesheet') 'application:open-license': -> @getModel().openLicense() 'window:run-package-specs': -> @runPackageSpecs() 'window:focus-next-pane': -> @getModel().activateNextPane() diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index c0f1486b5..c6ed4e053 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -2,7 +2,7 @@ _ = require 'underscore-plus' scrollbarStyle = require 'scrollbar-style' {Range, Point} = require 'text-buffer' {CompositeDisposable} = require 'event-kit' -ipc = require 'ipc-renderer' +{ipcRenderer} = require 'electron' TextEditorPresenter = require './text-editor-presenter' GutterContainerComponent = require './gutter-container-component' @@ -263,10 +263,10 @@ class TextEditorComponent writeSelectedTextToSelectionClipboard = => return if @editor.isDestroyed() if selectedText = @editor.getSelectedText() - # This uses ipc.send instead of clipboard.writeText because - # clipboard.writeText is a sync ipc call on Linux and that + # This uses ipcRenderer.send instead of clipboard.writeText because + # clipboard.writeText is a sync ipcRenderer call on Linux and that # will slow down selections. - ipc.send('write-text-to-selection-clipboard', selectedText) + ipcRenderer.send('write-text-to-selection-clipboard', selectedText) @disposables.add @editor.onDidChangeSelectionRange -> clearTimeout(timeoutId) timeoutId = setTimeout(writeSelectedTextToSelectionClipboard) diff --git a/src/workspace-element.coffee b/src/workspace-element.coffee index 89e990c66..b3d4c8a7a 100644 --- a/src/workspace-element.coffee +++ b/src/workspace-element.coffee @@ -1,4 +1,4 @@ -ipc = require 'ipc-renderer' +{ipcRenderer} = require 'electron' path = require 'path' {Disposable, CompositeDisposable} = require 'event-kit' Grim = require 'grim' @@ -104,6 +104,6 @@ class WorkspaceElement extends HTMLElement [projectPath] = @project.relativizePath(activePath) else [projectPath] = @project.getPaths() - ipc.send('run-package-specs', path.join(projectPath, 'spec')) if projectPath + ipcRenderer.send('run-package-specs', path.join(projectPath, 'spec')) if projectPath module.exports = WorkspaceElement = document.registerElement 'atom-workspace', prototype: WorkspaceElement.prototype diff --git a/static/index.js b/static/index.js index 651f2b54c..1f2c61f7c 100644 --- a/static/index.js +++ b/static/index.js @@ -1,4 +1,6 @@ (function () { + process.throwDeprecation = true; + var path = require('path') var FileSystemBlobStore = require('../src/file-system-blob-store') var NativeCompileCache = require('../src/native-compile-cache') From d7e6b6399f761ffeadb387a1c83c75fd1a521602 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 11 Dec 2015 09:33:53 +0800 Subject: [PATCH 034/971] :arrow_up: electron@0.35.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 82211c710..1ff8721c0 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.35.2", + "electronVersion": "0.35.4", "dependencies": { "async": "0.2.6", "atom-keymap": "^6.2.0", From 1f9cfc929cdc5f57281bc3b44beacf617f134baa Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 11 Dec 2015 09:50:18 +0800 Subject: [PATCH 035/971] Update the API usages after merge --- src/application-delegate.coffee | 2 +- src/browser/atom-application.coffee | 6 +++--- static/index.js | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index 60627e5ce..2c7688de9 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -107,7 +107,7 @@ class ApplicationDelegate ipcRenderer.send("call-window-method", "setRepresentedFilename", filename) addRecentDocument: (filename) -> - ipc.send("add-recent-document", filename) + ipcRenderer.send("add-recent-document", filename) setRepresentedDirectoryPaths: (paths) -> loadSettings = getWindowLoadSettings() diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index f997c6f86..cbfe36f2d 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -273,10 +273,10 @@ class AtomApplication ipcMain.on 'write-to-stdout', (event, output) -> process.stdout.write(output) - ipcMain.on 'write-to-stderr', (event, output) -> - process.stderr.write(output) + ipcMain.on 'write-to-stderr', (event, output) -> + process.stderr.write(output) - ipc.on 'add-recent-document', (event, filename) -> + ipcMain.on 'add-recent-document', (event, filename) -> app.addRecentDocument(filename) setupDockMenu: -> diff --git a/static/index.js b/static/index.js index 1f2c61f7c..651f2b54c 100644 --- a/static/index.js +++ b/static/index.js @@ -1,6 +1,4 @@ (function () { - process.throwDeprecation = true; - var path = require('path') var FileSystemBlobStore = require('../src/file-system-blob-store') var NativeCompileCache = require('../src/native-compile-cache') From c186d70beccb3d9ee8e2d77a20383a5609b77444 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 11 Dec 2015 10:03:20 +0800 Subject: [PATCH 036/971] Also use require('electron') in specs --- spec/atom-reporter.coffee | 2 +- spec/jasmine-test-runner.coffee | 4 ++-- spec/text-editor-component-spec.js | 2 +- spec/window-event-handler-spec.coffee | 8 ++++---- spec/workspace-element-spec.coffee | 22 +++++++++++----------- src/browser/atom-application.coffee | 2 +- static/index.js | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/spec/atom-reporter.coffee b/spec/atom-reporter.coffee index 931e21777..d7ff49964 100644 --- a/spec/atom-reporter.coffee +++ b/spec/atom-reporter.coffee @@ -173,7 +173,7 @@ class AtomReporter listen document, 'click', '.stack-trace', (event) -> event.currentTarget.classList.toggle('expanded') - @reloadButton.addEventListener('click', -> require('ipc-renderer').send('call-window-method', 'restart')) + @reloadButton.addEventListener('click', -> require('electron').ipcRenderer.send('call-window-method', 'restart')) updateSpecCounts: -> if @skippedCount diff --git a/spec/jasmine-test-runner.coffee b/spec/jasmine-test-runner.coffee index 8192d4328..0ce21f76d 100644 --- a/spec/jasmine-test-runner.coffee +++ b/spec/jasmine-test-runner.coffee @@ -2,7 +2,7 @@ fs = require 'fs' _ = require 'underscore-plus' fs = require 'fs-plus' path = require 'path' -ipc = require 'ipc-renderer' +{ipcRenderer} = require 'electron' module.exports = ({logFile, headless, testPaths, buildAtomEnvironment}) -> window[key] = value for key, value of require '../vendor/jasmine' @@ -89,7 +89,7 @@ buildTerminalReporter = (logFile, resolveWithExitCode) -> if logStream? fs.writeSync(logStream, str) else - ipc.send 'write-to-stderr', str + ipcRenderer.send 'write-to-stderr', str {TerminalReporter} = require 'jasmine-tagged' new TerminalReporter diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 19f193917..a18ab06e4 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -4585,7 +4585,7 @@ describe('TextEditorComponent', function () { it('pastes the previously selected text at the clicked location', async function () { let clipboardWrittenTo = false - spyOn(require('ipc-renderer'), 'send').andCallFake(function (eventName, selectedText) { + spyOn(require('electron').ipcRenderer, 'send').andCallFake(function (eventName, selectedText) { if (eventName === 'write-text-to-selection-clipboard') { require('../src/safe-clipboard').writeText(selectedText, 'selection') clipboardWrittenTo = true diff --git a/spec/window-event-handler-spec.coffee b/spec/window-event-handler-spec.coffee index e3e76643f..bb7e1665b 100644 --- a/spec/window-event-handler-spec.coffee +++ b/spec/window-event-handler-spec.coffee @@ -4,7 +4,7 @@ fs = require 'fs-plus' temp = require 'temp' TextEditor = require '../src/text-editor' WindowEventHandler = require '../src/window-event-handler' -ipc = require 'ipc-renderer' +{ipcRenderer} = require 'electron' describe "WindowEventHandler", -> [projectPath, windowEventHandler] = [] @@ -53,7 +53,7 @@ describe "WindowEventHandler", -> describe "beforeunload event", -> beforeEach -> jasmine.unspy(TextEditor.prototype, "shouldPromptToSave") - spyOn(ipc, 'send') + spyOn(ipcRenderer, 'send') describe "when pane items are modified", -> editor = null @@ -65,13 +65,13 @@ describe "WindowEventHandler", -> spyOn(atom.workspace, 'confirmClose').andReturn(true) window.dispatchEvent(new CustomEvent('beforeunload')) expect(atom.workspace.confirmClose).toHaveBeenCalled() - expect(ipc.send).not.toHaveBeenCalledWith('did-cancel-window-unload') + expect(ipcRenderer.send).not.toHaveBeenCalledWith('did-cancel-window-unload') it "cancels the unload if the user selects cancel", -> spyOn(atom.workspace, 'confirmClose').andReturn(false) window.dispatchEvent(new CustomEvent('beforeunload')) expect(atom.workspace.confirmClose).toHaveBeenCalled() - expect(ipc.send).toHaveBeenCalledWith('did-cancel-window-unload') + expect(ipcRenderer.send).toHaveBeenCalledWith('did-cancel-window-unload') describe "when a link is clicked", -> it "opens the http/https links in an external application", -> diff --git a/spec/workspace-element-spec.coffee b/spec/workspace-element-spec.coffee index 01dddc5b7..24f8f6c3a 100644 --- a/spec/workspace-element-spec.coffee +++ b/spec/workspace-element-spec.coffee @@ -1,4 +1,4 @@ -ipc = require 'ipc-renderer' +{ipcRenderer} = require 'electron' path = require 'path' temp = require('temp').track() @@ -87,35 +87,35 @@ describe "WorkspaceElement", -> describe "the 'window:run-package-specs' command", -> it "runs the package specs for the active item's project path, or the first project path", -> workspaceElement = atom.views.getView(atom.workspace) - spyOn(ipc, 'send') + spyOn(ipcRenderer, 'send') # No project paths. Don't try to run specs. atom.commands.dispatch(workspaceElement, "window:run-package-specs") - expect(ipc.send).not.toHaveBeenCalledWith("run-package-specs") + expect(ipcRenderer.send).not.toHaveBeenCalledWith("run-package-specs") projectPaths = [temp.mkdirSync("dir1-"), temp.mkdirSync("dir2-")] atom.project.setPaths(projectPaths) # No active item. Use first project directory. atom.commands.dispatch(workspaceElement, "window:run-package-specs") - expect(ipc.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[0], "spec")) - ipc.send.reset() + expect(ipcRenderer.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[0], "spec")) + ipcRenderer.send.reset() # Active item doesn't implement ::getPath(). Use first project directory. item = document.createElement("div") atom.workspace.getActivePane().activateItem(item) atom.commands.dispatch(workspaceElement, "window:run-package-specs") - expect(ipc.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[0], "spec")) - ipc.send.reset() + expect(ipcRenderer.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[0], "spec")) + ipcRenderer.send.reset() # Active item has no path. Use first project directory. item.getPath = -> null atom.commands.dispatch(workspaceElement, "window:run-package-specs") - expect(ipc.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[0], "spec")) - ipc.send.reset() + expect(ipcRenderer.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[0], "spec")) + ipcRenderer.send.reset() # Active item has path. Use project path for item path. item.getPath = -> path.join(projectPaths[1], "a-file.txt") atom.commands.dispatch(workspaceElement, "window:run-package-specs") - expect(ipc.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[1], "spec")) - ipc.send.reset() + expect(ipcRenderer.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[1], "spec")) + ipcRenderer.send.reset() diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index cbfe36f2d..eb3d9a60c 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -277,7 +277,7 @@ class AtomApplication process.stderr.write(output) ipcMain.on 'add-recent-document', (event, filename) -> - app.addRecentDocument(filename) + app.addRecentDocument(filename) setupDockMenu: -> if process.platform is 'darwin' diff --git a/static/index.js b/static/index.js index 651f2b54c..a55f31b5c 100644 --- a/static/index.js +++ b/static/index.js @@ -83,7 +83,7 @@ var initialize = require(loadSettings.windowInitializationScript) initialize({blobStore: blobStore}) - require('ipc-renderer').send('window-command', 'window:loaded') + require('electron').ipcRenderer.send('window-command', 'window:loaded') } function setupCsonCache (cacheDir) { From 2150790596354c6317df33b9dd379e43b3f35111 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 11 Dec 2015 10:21:46 +0800 Subject: [PATCH 037/971] Use the new style of `remote` --- src/application-delegate.coffee | 9 +++------ src/context-menu-manager.coffee | 2 +- src/initialize-test-window.coffee | 2 +- src/safe-clipboard.coffee | 4 ++-- src/window-load-settings-helpers.coffee | 2 +- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index 2c7688de9..39e8efe95 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -121,8 +121,7 @@ class ApplicationDelegate remote.getCurrentWindow().setMenuBarVisibility(visible) getPrimaryDisplayWorkAreaSize: -> - screen = remote.require 'screen' - screen.getPrimaryDisplay().workAreaSize + remote.screen.getPrimaryDisplay().workAreaSize confirm: ({message, detailedMessage, buttons}) -> buttons ?= {} @@ -131,8 +130,7 @@ class ApplicationDelegate else buttonLabels = Object.keys(buttons) - dialog = remote.require('dialog') - chosen = dialog.showMessageBox(remote.getCurrentWindow(), { + chosen = remote.dialog.showMessageBox(remote.getCurrentWindow(), { type: 'info' message: message detail: detailedMessage @@ -154,8 +152,7 @@ class ApplicationDelegate params = _.clone(params) params.title ?= 'Save File' params.defaultPath ?= getWindowLoadSettings().initialPaths[0] - dialog = remote.require('dialog') - dialog.showSaveDialog remote.getCurrentWindow(), params + remote.dialog.showSaveDialog remote.getCurrentWindow(), params playBeepSound: -> shell.beep() diff --git a/src/context-menu-manager.coffee b/src/context-menu-manager.coffee index 869076beb..cce767f57 100644 --- a/src/context-menu-manager.coffee +++ b/src/context-menu-manager.coffee @@ -4,7 +4,7 @@ CSON = require 'season' fs = require 'fs-plus' {calculateSpecificity, validateSelector} = require 'clear-cut' {Disposable} = require 'event-kit' -remote = require 'remote' +{remote} = require 'electron' MenuHelpers = require './menu-helpers' platformContextMenu = require('../package.json')?._atomMenu?['context-menu'] diff --git a/src/initialize-test-window.coffee b/src/initialize-test-window.coffee index f54d40dc3..7de84cf0b 100644 --- a/src/initialize-test-window.coffee +++ b/src/initialize-test-window.coffee @@ -9,7 +9,7 @@ module.exports = ({blobStore}) -> crashReporter.start(productName: 'Atom', companyName: 'GitHub') exitWithStatusCode = (status) -> - remote.require('app').emit('will-quit') + remote.app.emit('will-quit') remote.process.exit(status) try diff --git a/src/safe-clipboard.coffee b/src/safe-clipboard.coffee index 8301f9d54..1f91803e2 100644 --- a/src/safe-clipboard.coffee +++ b/src/safe-clipboard.coffee @@ -1,6 +1,6 @@ # Using clipboard in renderer process is not safe on Linux. module.exports = if process.platform is 'linux' and process.type is 'renderer' - require('remote').require('clipboard') + require('electron').remote.clipboard else - require('clipboard') + require('electron').clipboard diff --git a/src/window-load-settings-helpers.coffee b/src/window-load-settings-helpers.coffee index 59ee2f382..4bb514301 100644 --- a/src/window-load-settings-helpers.coffee +++ b/src/window-load-settings-helpers.coffee @@ -1,4 +1,4 @@ -remote = require 'remote' +{remote} = require 'electron' _ = require 'underscore-plus' windowLoadSettings = null From 2e5c7f85736736c61f8a838f5d3abd04666565bd Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 11 Dec 2015 11:00:08 +0800 Subject: [PATCH 038/971] executeJavaScriptInDevTools is deprecated --- src/application-delegate.coffee | 2 +- src/browser/atom-application.coffee | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index 39e8efe95..8fc51582a 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -98,7 +98,7 @@ class ApplicationDelegate @openWindowDevTools().then(resolve) executeJavaScriptInWindowDevTools: (code) -> - ipcRenderer.send("call-window-method", "executeJavaScriptInDevTools", code) + ipcRenderer.send("execute-javascript-in-dev-tools", code) setWindowDocumentEdited: (edited) -> ipcRenderer.send("call-window-method", "setDocumentEdited", edited) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index eb3d9a60c..61faa5704 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -279,6 +279,9 @@ class AtomApplication ipcMain.on 'add-recent-document', (event, filename) -> app.addRecentDocument(filename) + ipcMain.on 'execute-javascript-in-dev-tools', (event, code) -> + event.sender.devToolsWebContents?.executeJavaScript(code) + setupDockMenu: -> if process.platform is 'darwin' dockMenu = Menu.buildFromTemplate [ From 678c390e2ed6f1294fc859bd6cd2d97249e3f23d Mon Sep 17 00:00:00 2001 From: Wliu Date: Thu, 10 Dec 2015 22:52:06 -0500 Subject: [PATCH 039/971] Fix indentation --- src/browser/atom-application.coffee | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 61faa5704..7b3694947 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -273,14 +273,14 @@ class AtomApplication ipcMain.on 'write-to-stdout', (event, output) -> process.stdout.write(output) - ipcMain.on 'write-to-stderr', (event, output) -> - process.stderr.write(output) + ipcMain.on 'write-to-stderr', (event, output) -> + process.stderr.write(output) - ipcMain.on 'add-recent-document', (event, filename) -> - app.addRecentDocument(filename) + ipcMain.on 'add-recent-document', (event, filename) -> + app.addRecentDocument(filename) - ipcMain.on 'execute-javascript-in-dev-tools', (event, code) -> - event.sender.devToolsWebContents?.executeJavaScript(code) + ipcMain.on 'execute-javascript-in-dev-tools', (event, code) -> + event.sender.devToolsWebContents?.executeJavaScript(code) setupDockMenu: -> if process.platform is 'darwin' From e4e200317abcd6c484ba834e4bac32ad27463452 Mon Sep 17 00:00:00 2001 From: Collin Donahue-Oponski Date: Fri, 11 Dec 2015 15:32:29 -0700 Subject: [PATCH 040/971] :white_check_mark: Autoscroll to cursor after clearing multi-cursor selection. --- spec/text-editor-spec.coffee | 32 +++++++++++++++++++++++++++----- src/text-editor.coffee | 2 +- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 02d2e4a96..a99e95ee6 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2119,20 +2119,42 @@ describe "TextEditor", -> editor.splitSelectionsIntoLines() expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]]] - describe ".consolidateSelections()", -> - it "destroys all selections but the least recent, returning true if any selections were destroyed", -> - editor.setSelectedBufferRange([[3, 16], [3, 21]]) - selection1 = editor.getLastSelection() + describe "::consolidateSelections()", -> + makeMultipleSelections = -> + selection.setBufferRange [[3, 16], [3, 21]] selection2 = editor.addSelectionForBufferRange([[3, 25], [3, 34]]) selection3 = editor.addSelectionForBufferRange([[8, 4], [8, 10]]) + selection4 = editor.addSelectionForBufferRange([[1, 6], [1, 10]]) + expect(editor.getSelections()).toEqual [selection, selection2, selection3, selection4] + [selection, selection2, selection3, selection4] + + it "destroys all selections but the least recent, returning true if any selections were destroyed", -> + [selection1] = makeMultipleSelections() - expect(editor.getSelections()).toEqual [selection1, selection2, selection3] expect(editor.consolidateSelections()).toBeTruthy() expect(editor.getSelections()).toEqual [selection1] expect(selection1.isEmpty()).toBeFalsy() expect(editor.consolidateSelections()).toBeFalsy() expect(editor.getSelections()).toEqual [selection1] + it "scrolls to the remaining selection", -> + [selection1] = makeMultipleSelections() + + atom.config.set('editor.scrollPastEnd', true) + editor.setHeight(100, true) + editor.setLineHeightInPixels(10) + editor.setFirstVisibleScreenRow(10) + expect(editor.getVisibleRowRange()[0]).toBeGreaterThan(selection1.getBufferRowRange()[1]) + + editor.consolidateSelections() + + waitsForPromise -> + new Promise((resolve) -> window.requestAnimationFrame(resolve)) + + runs -> + expect(editor.getVisibleRowRange()[0]).not.toBeGreaterThan(selection1.getBufferRowRange()[0]) + expect(editor.getVisibleRowRange()[1]).not.toBeLessThan(selection1.getBufferRowRange()[0]) + describe "when the cursor is moved while there is a selection", -> makeSelection = -> selection.setBufferRange [[1, 2], [1, 5]] diff --git a/src/text-editor.coffee b/src/text-editor.coffee index ba54b9cd4..d4d0737e2 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2437,7 +2437,7 @@ class TextEditor extends Model selections = @getSelections() if selections.length > 1 selection.destroy() for selection in selections[1...(selections.length)] - selections[0].autoscroll() + selections[0].autoscroll(center: true) true else false From d6fa46accec1191971fcf20dffa7ffd9f9bdd8bc Mon Sep 17 00:00:00 2001 From: Wliu Date: Sat, 12 Dec 2015 13:22:22 -0500 Subject: [PATCH 041/971] Update browser files to use require 'electron' --- src/browser/application-menu.coffee | 3 +-- src/browser/atom-protocol-handler.coffee | 3 +-- src/browser/atom-window.coffee | 4 +--- src/browser/auto-update-manager.coffee | 6 +++--- src/browser/auto-updater-win32.coffee | 4 ++-- src/browser/context-menu.coffee | 2 +- 6 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/browser/application-menu.coffee b/src/browser/application-menu.coffee index f742c3a54..fef9f90fa 100644 --- a/src/browser/application-menu.coffee +++ b/src/browser/application-menu.coffee @@ -1,5 +1,4 @@ -app = require 'app' -Menu = require 'menu' +{app, Menu} = require 'electron' _ = require 'underscore-plus' # Used to manage the global application menu. diff --git a/src/browser/atom-protocol-handler.coffee b/src/browser/atom-protocol-handler.coffee index 0fda8095b..3967c0525 100644 --- a/src/browser/atom-protocol-handler.coffee +++ b/src/browser/atom-protocol-handler.coffee @@ -1,7 +1,6 @@ -app = require 'app' +{app, protocol} = require 'electron' fs = require 'fs' path = require 'path' -protocol = require 'protocol' # Handles requests with 'atom' protocol. # diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 6a36b55eb..ace12239e 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -1,6 +1,4 @@ -BrowserWindow = require 'browser-window' -app = require 'app' -dialog = require 'dialog' +{BrowserWindow, app, dialog} = require 'electron' path = require 'path' fs = require 'fs' url = require 'url' diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index f4a493829..63a79b60f 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -25,7 +25,7 @@ class AutoUpdateManager if process.platform is 'win32' autoUpdater = require './auto-updater-win32' else - autoUpdater = require 'auto-updater' + {autoUpdater} = require 'electron' autoUpdater.on 'error', (event, message) => @setState(ErrorState) @@ -91,7 +91,7 @@ class AutoUpdateManager onUpdateNotAvailable: => autoUpdater.removeListener 'error', @onUpdateError - dialog = require 'dialog' + {dialog} = require 'electron' dialog.showMessageBox type: 'info' buttons: ['OK'] @@ -102,7 +102,7 @@ class AutoUpdateManager onUpdateError: (event, message) => autoUpdater.removeListener 'update-not-available', @onUpdateNotAvailable - dialog = require 'dialog' + {dialog} = require 'electron' dialog.showMessageBox type: 'warning' buttons: ['OK'] diff --git a/src/browser/auto-updater-win32.coffee b/src/browser/auto-updater-win32.coffee index 13dcd9a1e..e31578d49 100644 --- a/src/browser/auto-updater-win32.coffee +++ b/src/browser/auto-updater-win32.coffee @@ -9,9 +9,9 @@ class AutoUpdater quitAndInstall: -> if SquirrelUpdate.existsSync() - SquirrelUpdate.restartAtom(require('app')) + SquirrelUpdate.restartAtom(require('electron').app) else - require('auto-updater').quitAndInstall() + require('electron').autoUpdater.quitAndInstall() downloadUpdate: (callback) -> SquirrelUpdate.spawn ['--download', @updateUrl], (error, stdout) -> diff --git a/src/browser/context-menu.coffee b/src/browser/context-menu.coffee index 44b57cdc9..1bc9c29ba 100644 --- a/src/browser/context-menu.coffee +++ b/src/browser/context-menu.coffee @@ -1,4 +1,4 @@ -Menu = require 'menu' +{Menu} = require 'electron' module.exports = class ContextMenu From ed96ac7daf06a4abb106b54e0e70edc33da11541 Mon Sep 17 00:00:00 2001 From: Wliu Date: Sat, 12 Dec 2015 21:35:14 -0500 Subject: [PATCH 042/971] browserWindow.restart -> .reload --- src/application-delegate.coffee | 4 ++-- src/atom-environment.coffee | 2 +- src/browser/atom-window.coffee | 4 ++-- src/initialize-test-window.coffee | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index 8fc51582a..f7b04e10d 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -47,8 +47,8 @@ class ApplicationDelegate hideWindow: -> ipcRenderer.send("call-window-method", "hide") - restartWindow: -> - ipcRenderer.send("call-window-method", "restart") + reloadWindow: -> + ipcRenderer.send("call-window-method", "reload") isWindowMaximized: -> remote.getCurrentWindow().isMaximized() diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index f46df14ab..cd6834da4 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -492,7 +492,7 @@ class AtomEnvironment extends Model # Extended: Reload the current window. reload: -> - @applicationDelegate.restartWindow() + @applicationDelegate.reloadWindow() # Extended: Returns a {Boolean} that is `true` if the current window is maximized. isMaximized: -> diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index ace12239e..721eb68a6 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -142,7 +142,7 @@ class AtomWindow detail: 'Please report this issue to https://github.com/atom/atom' switch chosen when 0 then @browserWindow.destroy() - when 1 then @browserWindow.restart() + when 1 then @browserWindow.reload() @browserWindow.webContents.on 'will-navigate', (event, url) => unless url is @browserWindow.webContents.getURL() @@ -216,6 +216,6 @@ class AtomWindow isSpecWindow: -> @isSpec - reload: -> @browserWindow.restart() + reload: -> @browserWindow.reload() toggleDevTools: -> @browserWindow.toggleDevTools() diff --git a/src/initialize-test-window.coffee b/src/initialize-test-window.coffee index 7de84cf0b..d9607f8f7 100644 --- a/src/initialize-test-window.coffee +++ b/src/initialize-test-window.coffee @@ -29,7 +29,7 @@ module.exports = ({blobStore}) -> handleKeydown = (event) -> # Reload: cmd-r / ctrl-r if (event.metaKey or event.ctrlKey) and event.keyCode is 82 - ipcRenderer.send('call-window-method', 'restart') + ipcRenderer.send('call-window-method', 'reload') # Toggle Dev Tools: cmd-alt-i / ctrl-alt-i if (event.metaKey or event.ctrlKey) and event.altKey and event.keyCode is 73 From 74e830358d939eafda89bf5c7269b81756fe3f50 Mon Sep 17 00:00:00 2001 From: Wliu Date: Mon, 14 Dec 2015 17:31:27 -0500 Subject: [PATCH 043/971] Save the state before the browser window is closed --- src/browser/atom-application.coffee | 1 - src/browser/atom-window.coffee | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 7b3694947..720e0f84d 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -208,7 +208,6 @@ class AtomApplication @openPathOnEvent('application:open-license', path.join(process.resourcesPath, 'LICENSE.md')) app.on 'before-quit', => - @saveState(false) @quitting = true app.on 'will-quit', => diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 721eb68a6..8425171b7 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -119,6 +119,9 @@ class AtomWindow false handleEvents: -> + @browserWindow.on 'close', -> + global.atomApplication.saveState(false) + @browserWindow.on 'closed', => global.atomApplication.removeWindow(this) From f4f5e47aab9784ff8405d678b462ef1940c76820 Mon Sep 17 00:00:00 2001 From: Wliu Date: Mon, 14 Dec 2015 17:35:50 -0500 Subject: [PATCH 044/971] One more remote update --- spec/integration/helpers/start-atom.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/integration/helpers/start-atom.coffee b/spec/integration/helpers/start-atom.coffee index b8768f532..6d89af263 100644 --- a/spec/integration/helpers/start-atom.coffee +++ b/spec/integration/helpers/start-atom.coffee @@ -124,7 +124,7 @@ buildAtomClient = (args, env) -> .addCommand "simulateQuit", (done) -> @execute -> atom.unloadEditorWindow() - .execute -> require("remote").require("app").emit("before-quit") + .execute -> require("electron").remote.app.emit("before-quit") .call(done) module.exports = (args, env, fn) -> From 295698c085efedccf217fcdc16ade5daee547c20 Mon Sep 17 00:00:00 2001 From: Wliu Date: Tue, 22 Dec 2015 17:01:01 -0500 Subject: [PATCH 045/971] app.terminate --> app.quit --- src/browser/atom-application.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 720e0f84d..0fbeeac9e 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -46,7 +46,7 @@ class AtomApplication client = net.connect {path: options.socketPath}, -> client.write JSON.stringify(options), -> client.end() - app.terminate() + app.quit() client.on 'error', createAtomApplication From bdb11a95f762168ba2ffad070450f674b6d69e53 Mon Sep 17 00:00:00 2001 From: Wliu Date: Tue, 22 Dec 2015 17:05:27 -0500 Subject: [PATCH 046/971] :arrow_up: tabs@0.89.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1cca017b4..8bce54f82 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "status-bar": "0.80.0", "styleguide": "0.45.0", "symbols-view": "0.110.1", - "tabs": "0.88.0", + "tabs": "0.89.0", "timecop": "0.33.0", "tree-view": "0.198.0", "update-package-dependencies": "0.10.0", From dbc06e599394a7c99f79a3ddbba4133ad3a9ad2a Mon Sep 17 00:00:00 2001 From: Wliu Date: Tue, 22 Dec 2015 17:16:15 -0500 Subject: [PATCH 047/971] Initial attempt to update to Electron 0.36.1 --- package.json | 2 +- static/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8bce54f82..0a490b8ee 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.35.4", + "electronVersion": "0.36.1", "dependencies": { "async": "0.2.6", "atom-keymap": "^6.2.0", diff --git a/static/index.html b/static/index.html index 5fcb30ad2..0bd4a7954 100644 --- a/static/index.html +++ b/static/index.html @@ -1,7 +1,7 @@ - + From d75102f46d51599cdc914cda700e05b964f07397 Mon Sep 17 00:00:00 2001 From: Wliu Date: Tue, 22 Dec 2015 18:01:26 -0500 Subject: [PATCH 048/971] Set URL in the crash reporter --- src/browser/main.coffee | 2 +- src/initialize-test-window.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/main.coffee b/src/browser/main.coffee index e2e9d2243..751870573 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -53,7 +53,7 @@ handleStartupEventWithSquirrel = -> SquirrelUpdate.handleStartupEvent(app, squirrelCommand) setupCrashReporter = -> - crashReporter.start(productName: 'Atom', companyName: 'GitHub') + crashReporter.start(productName: 'Atom', companyName: 'GitHub', submitURL: 'http://54.249.141.255:1127/post') setupAtomHome = ({setPortable}) -> return if process.env.ATOM_HOME diff --git a/src/initialize-test-window.coffee b/src/initialize-test-window.coffee index d9607f8f7..9c5df337b 100644 --- a/src/initialize-test-window.coffee +++ b/src/initialize-test-window.coffee @@ -6,7 +6,7 @@ cloneObject = (object) -> module.exports = ({blobStore}) -> {crashReporter, remote} = require 'electron' # Start the crash reporter before anything else. - crashReporter.start(productName: 'Atom', companyName: 'GitHub') + crashReporter.start(productName: 'Atom', companyName: 'GitHub', submitURL: 'http://54.249.141.255:1127/post') exitWithStatusCode = (status) -> remote.app.emit('will-quit') From 612ca6ecabe63298351fd3e4c7b865ff267cfc97 Mon Sep 17 00:00:00 2001 From: Wliu Date: Tue, 22 Dec 2015 20:44:51 -0500 Subject: [PATCH 049/971] Missed one of the crash reporters Also convert even more requires... --- static/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/static/index.js b/static/index.js index d7f509635..b2646b5bc 100644 --- a/static/index.js +++ b/static/index.js @@ -54,7 +54,7 @@ } function handleSetupError (error) { - var currentWindow = require('remote').getCurrentWindow() + var currentWindow = require('electron').remote.getCurrentWindow() currentWindow.setSize(800, 600) currentWindow.center() currentWindow.show() @@ -71,9 +71,10 @@ ModuleCache.add(loadSettings.resourcePath) // Start the crash reporter before anything else. - require('crash-reporter').start({ + require('electron').crashReporter.start({ productName: 'Atom', companyName: 'GitHub', + submitURL: 'http://54.249.141.255:1127/post', // By explicitly passing the app version here, we could save the call // of "require('remote').require('app').getVersion()". extra: {_version: loadSettings.appVersion} @@ -124,7 +125,7 @@ } } - var currentWindow = require('remote').getCurrentWindow() + var currentWindow = require('electron').remote.getCurrentWindow() if (currentWindow.devToolsWebContents) { profile() } else { From dbf7bedec81653be81183db3aa6040745164f459 Mon Sep 17 00:00:00 2001 From: Wliu Date: Wed, 30 Dec 2015 10:54:34 -0500 Subject: [PATCH 050/971] :arrow_up: electron@0.36.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 872c38d87..b7123e85c 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.36.1", + "electronVersion": "0.36.2", "dependencies": { "async": "0.2.6", "atom-keymap": "^6.2.0", From 43e34d4f51a899071fb29af6cc14400113cef7ae Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Mon, 4 Jan 2016 05:59:11 -0700 Subject: [PATCH 051/971] :checkered_flag: Use PowerShell to get User path in Windows when installing via Squirrel, replacing reg.exe that fails to execute if registry editing is disabled in the group policy. This was causing the user Path to get deleted if registry editing is disabled. The workaround that was in place kept the existing path, but failed to add Atom to it. --- src/browser/squirrel-update.coffee | 50 +++++++++++++----------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/browser/squirrel-update.coffee b/src/browser/squirrel-update.coffee index 0e4743a21..4efc089d7 100644 --- a/src/browser/squirrel-update.coffee +++ b/src/browser/squirrel-update.coffee @@ -11,9 +11,11 @@ exeName = path.basename(process.execPath) if process.env.SystemRoot system32Path = path.join(process.env.SystemRoot, 'System32') regPath = path.join(system32Path, 'reg.exe') + powershellPath = path.join(system32Path, 'WindowsPowerShell', 'v1.0', 'powershell.exe') setxPath = path.join(system32Path, 'setx.exe') else regPath = 'reg.exe' + powershellPath = 'powershell.exe' setxPath = 'setx.exe' # Registry keys used for context menu @@ -43,11 +45,23 @@ spawn = (command, args, callback) -> error?.code ?= code error?.stdout ?= stdout callback?(error, stdout) + # This is necessary if using Powershell 2 on Windows 7 to get the events to raise + # http://stackoverflow.com/questions/9155289/calling-powershell-from-nodejs + spawnedProcess.stdin.end() + # Spawn reg.exe and callback when it completes spawnReg = (args, callback) -> spawn(regPath, args, callback) +# Spawn powershell.exe and callback when it completes +spawnPowershell = (args, callback) -> + args.unshift('-command') + args.unshift('RemoteSigned') + args.unshift('-ExecutionPolicy') + args.unshift('-noprofile') + spawn(powershellPath, args, callback) + # Spawn setx.exe and callback when it completes spawnSetx = (args, callback) -> spawn(setxPath, args, callback) @@ -85,37 +99,17 @@ isAscii = (text) -> # Get the user's PATH environment variable registry value. getPath = (callback) -> - spawnReg ['query', environmentKeyPath, '/v', 'Path'], (error, stdout) -> + spawnPowershell ['[environment]::GetEnvironmentVariable(\'Path\',\'User\')'], (error, stdout) -> if error? - if error.code is 1 - # FIXME Don't overwrite path when reading value is disabled - # https://github.com/atom/atom/issues/5092 - if stdout.indexOf('ERROR: Registry editing has been disabled by your administrator.') isnt -1 - return callback(error) + return callback(error) - # The query failed so the Path does not exist yet in the registry - return callback(null, '') - else - return callback(error) - - # Registry query output is in the form: - # - # HKEY_CURRENT_USER\Environment - # Path REG_SZ C:\a\folder\on\the\path;C\another\folder - # - - lines = stdout.split(/[\r\n]+/).filter (line) -> line - segments = lines[lines.length - 1]?.split(' ') - if segments[1] is 'Path' and segments.length >= 3 - pathEnv = segments?[3..].join(' ') - if isAscii(pathEnv) - callback(null, pathEnv) - else - # FIXME Don't corrupt non-ASCII PATH values - # https://github.com/atom/atom/issues/5063 - callback(new Error('PATH contains non-ASCII values')) + pathOutput = stdout.replace(/^\s+|\s+$/g, '') + if isAscii(pathOutput) + callback(null, pathOutput) else - callback(new Error('Registry query for PATH failed')) + # FIXME Don't corrupt non-ASCII PATH values + # https://github.com/atom/atom/issues/5063 + callback(new Error('PATH contains non-ASCII values')) # Uninstall the Open with Atom explorer context menu items via the registry. uninstallContextMenu = (callback) -> From 91253910ef5b966e40f57b74366da5a0cec48144 Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Wed, 6 Jan 2016 12:38:55 -0700 Subject: [PATCH 052/971] :checkered_flag: Fix corrupting Windows Paths containing non-Ascii characters Remove workaround that just didn't update the path at all - now including the Atom path update and preserving the non-Ascii characters --- src/browser/squirrel-update.coffee | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/browser/squirrel-update.coffee b/src/browser/squirrel-update.coffee index 4efc089d7..f9df3c0b5 100644 --- a/src/browser/squirrel-update.coffee +++ b/src/browser/squirrel-update.coffee @@ -56,6 +56,9 @@ spawnReg = (args, callback) -> # Spawn powershell.exe and callback when it completes spawnPowershell = (args, callback) -> + # set encoding and execute the command, capture the output, and return it via .NET's console in order to have consistent UTF-8 encoding + # http://stackoverflow.com/questions/22349139/utf-8-output-from-powershell + args[0] = "[Console]::OutputEncoding=[System.Text.Encoding]::UTF8\r\n$output=#{args[0]}\r\n[Console]::WriteLine($output)" args.unshift('-command') args.unshift('RemoteSigned') args.unshift('-ExecutionPolicy') @@ -90,13 +93,6 @@ installContextMenu = (callback) -> installMenu directoryKeyPath, '%1', -> installMenu(backgroundKeyPath, '%V', callback) -isAscii = (text) -> - index = 0 - while index < text.length - return false if text.charCodeAt(index) > 127 - index++ - true - # Get the user's PATH environment variable registry value. getPath = (callback) -> spawnPowershell ['[environment]::GetEnvironmentVariable(\'Path\',\'User\')'], (error, stdout) -> @@ -104,12 +100,7 @@ getPath = (callback) -> return callback(error) pathOutput = stdout.replace(/^\s+|\s+$/g, '') - if isAscii(pathOutput) - callback(null, pathOutput) - else - # FIXME Don't corrupt non-ASCII PATH values - # https://github.com/atom/atom/issues/5063 - callback(new Error('PATH contains non-ASCII values')) + callback(null, pathOutput) # Uninstall the Open with Atom explorer context menu items via the registry. uninstallContextMenu = (callback) -> From c34ef243d70ed02073444df07eb40646dea84395 Mon Sep 17 00:00:00 2001 From: Wliu Date: Fri, 8 Jan 2016 16:58:02 +0000 Subject: [PATCH 053/971] :arrow_up: electron@0.36.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3b67d6a5d..49b754313 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.36.2", + "electronVersion": "0.36.3", "dependencies": { "async": "0.2.6", "atom-keymap": "^6.2.0", From a26ac5b363ad4c3c187dd337c556c07997bde489 Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Mon, 11 Jan 2016 05:32:39 -0700 Subject: [PATCH 054/971] :art: Improve readability of multi-line command --- src/browser/squirrel-update.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/browser/squirrel-update.coffee b/src/browser/squirrel-update.coffee index f9df3c0b5..41e103363 100644 --- a/src/browser/squirrel-update.coffee +++ b/src/browser/squirrel-update.coffee @@ -58,7 +58,12 @@ spawnReg = (args, callback) -> spawnPowershell = (args, callback) -> # set encoding and execute the command, capture the output, and return it via .NET's console in order to have consistent UTF-8 encoding # http://stackoverflow.com/questions/22349139/utf-8-output-from-powershell - args[0] = "[Console]::OutputEncoding=[System.Text.Encoding]::UTF8\r\n$output=#{args[0]}\r\n[Console]::WriteLine($output)" + # to address https://github.com/atom/atom/issues/5063 + args[0] = """ + [Console]::OutputEncoding=[System.Text.Encoding]::UTF8 + $output=#{args[0]} + [Console]::WriteLine($output) + """ args.unshift('-command') args.unshift('RemoteSigned') args.unshift('-ExecutionPolicy') From 924d880fa804560ae98859039910d8edcede1475 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 14 Dec 2015 13:55:29 -0700 Subject: [PATCH 055/971] WIP: Start rendering lines from DisplayLayers --- package.json | 1 + src/lines-tile-component.coffee | 107 +++++++++++++++++-------------- src/text-editor-presenter.coffee | 67 ++++++++++++++----- src/text-editor.coffee | 1 + 4 files changed, 113 insertions(+), 63 deletions(-) diff --git a/package.json b/package.json index 791f63daf..159eea698 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "key-path-helpers": "^0.4.0", "less-cache": "0.22", "marked": "^0.3.4", + "marker-index": "^3.0.4", "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "^5", diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index 6b4ac80ba..cf8d37a74 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -14,7 +14,6 @@ cloneObject = (object) -> module.exports = class LinesTileComponent constructor: ({@presenter, @id, @domElementPool, @assert, grammars}) -> - @tokenIterator = new TokenIterator(grammarRegistry: grammars) @measuredLines = new Set @lineNodesByLineId = {} @screenRowsByLineId = {} @@ -125,8 +124,7 @@ class LinesTileComponent screenRowForNode: (node) -> parseInt(node.dataset.screenRow) buildLineNode: (id) -> - {width} = @newState - {screenRow, tokens, text, top, lineEnding, fold, isSoftWrapped, indentLevel, decorationClasses} = @newTileState.lines[id] + {screenRow, words, decorationClasses} = @newTileState.lines[id] lineNode = @domElementPool.buildElement("div", "line") lineNode.dataset.screenRow = screenRow @@ -136,13 +134,13 @@ class LinesTileComponent lineNode.classList.add(decorationClass) @currentLineTextNodes = [] - if text is "" - @setEmptyLineInnerNodes(id, lineNode) - else - @setLineInnerNodes(id, lineNode) + # if words.length is 0 + # @setEmptyLineInnerNodes(id, lineNode) + + @setLineInnerNodes(id, lineNode) @textNodesByLineId[id] = @currentLineTextNodes - lineNode.appendChild(@domElementPool.buildElement("span", "fold-marker")) if fold + # lineNode.appendChild(@domElementPool.buildElement("span", "fold-marker")) if fold lineNode setEmptyLineInnerNodes: (id, lineNode) -> @@ -184,48 +182,61 @@ class LinesTileComponent @currentLineTextNodes.push(textNode) setLineInnerNodes: (id, lineNode) -> - lineState = @newTileState.lines[id] - {firstNonWhitespaceIndex, firstTrailingWhitespaceIndex, invisibles} = lineState - lineIsWhitespaceOnly = firstTrailingWhitespaceIndex is 0 + {words} = @newTileState.lines[id] + lineLength = 0 + for word in words + lineLength += word.length + textNode = @domElementPool.buildText(word) + lineNode.appendChild(textNode) + @currentLineTextNodes.push(textNode) - @tokenIterator.reset(lineState) - openScopeNode = lineNode + if lineLength is 0 + textNode = @domElementPool.buildText('\u00a0') + lineNode.appendChild(textNode) + @currentLineTextNodes.push(textNode) - while @tokenIterator.next() - for scope in @tokenIterator.getScopeEnds() - openScopeNode = openScopeNode.parentElement - - for scope in @tokenIterator.getScopeStarts() - newScopeNode = @domElementPool.buildElement("span", scope.replace(/\.+/g, ' ')) - openScopeNode.appendChild(newScopeNode) - openScopeNode = newScopeNode - - tokenStart = @tokenIterator.getScreenStart() - tokenEnd = @tokenIterator.getScreenEnd() - tokenText = @tokenIterator.getText() - isHardTab = @tokenIterator.isHardTab() - - if hasLeadingWhitespace = tokenStart < firstNonWhitespaceIndex - tokenFirstNonWhitespaceIndex = firstNonWhitespaceIndex - tokenStart - else - tokenFirstNonWhitespaceIndex = null - - if hasTrailingWhitespace = tokenEnd > firstTrailingWhitespaceIndex - tokenFirstTrailingWhitespaceIndex = Math.max(0, firstTrailingWhitespaceIndex - tokenStart) - else - tokenFirstTrailingWhitespaceIndex = null - - hasIndentGuide = - @newState.indentGuidesVisible and - (hasLeadingWhitespace or lineIsWhitespaceOnly) - - hasInvisibleCharacters = - (invisibles?.tab and isHardTab) or - (invisibles?.space and (hasLeadingWhitespace or hasTrailingWhitespace)) - - @appendTokenNodes(tokenText, isHardTab, tokenFirstNonWhitespaceIndex, tokenFirstTrailingWhitespaceIndex, hasIndentGuide, hasInvisibleCharacters, openScopeNode) - - @appendEndOfLineNodes(id, lineNode) + # lineState = @newTileState.lines[id] + # {firstNonWhitespaceIndex, firstTrailingWhitespaceIndex, invisibles} = lineState + # lineIsWhitespaceOnly = firstTrailingWhitespaceIndex is 0 + # + # @tokenIterator.reset(lineState) + # openScopeNode = lineNode + # + # while @tokenIterator.next() + # for scope in @tokenIterator.getScopeEnds() + # openScopeNode = openScopeNode.parentElement + # + # for scope in @tokenIterator.getScopeStarts() + # newScopeNode = @domElementPool.buildElement("span", scope.replace(/\.+/g, ' ')) + # openScopeNode.appendChild(newScopeNode) + # openScopeNode = newScopeNode + # + # tokenStart = @tokenIterator.getScreenStart() + # tokenEnd = @tokenIterator.getScreenEnd() + # tokenText = @tokenIterator.getText() + # isHardTab = @tokenIterator.isHardTab() + # + # if hasLeadingWhitespace = tokenStart < firstNonWhitespaceIndex + # tokenFirstNonWhitespaceIndex = firstNonWhitespaceIndex - tokenStart + # else + # tokenFirstNonWhitespaceIndex = null + # + # if hasTrailingWhitespace = tokenEnd > firstTrailingWhitespaceIndex + # tokenFirstTrailingWhitespaceIndex = Math.max(0, firstTrailingWhitespaceIndex - tokenStart) + # else + # tokenFirstTrailingWhitespaceIndex = null + # + # hasIndentGuide = + # @newState.indentGuidesVisible and + # (hasLeadingWhitespace or lineIsWhitespaceOnly) + # + # hasInvisibleCharacters = + # (invisibles?.tab and isHardTab) or + # (invisibles?.space and (hasLeadingWhitespace or hasTrailingWhitespace)) + # + # @appendTokenNodes(tokenText, isHardTab, tokenFirstNonWhitespaceIndex, tokenFirstTrailingWhitespaceIndex, hasIndentGuide, hasInvisibleCharacters, openScopeNode) + # + # @appendEndOfLineNodes(id, lineNode) appendTokenNodes: (tokenText, isHardTab, firstNonWhitespaceIndex, firstTrailingWhitespaceIndex, hasIndentGuide, hasInvisibleCharacters, scopeNode) -> if isHardTab diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 40ea95514..5d2b2c335 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1,5 +1,6 @@ {CompositeDisposable, Disposable, Emitter} = require 'event-kit' {Point, Range} = require 'text-buffer' +MarkerIndex = require 'marker-index' _ = require 'underscore-plus' Decoration = require './decoration' @@ -16,6 +17,7 @@ class TextEditorPresenter {@model, @config} = params {@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @tileSize} = params {@contentFrameWidth} = params + @tokenIterator = @model.displayLayer.buildTokenIterator() @gutterWidth = 0 @tileSize ?= 6 @@ -23,6 +25,9 @@ class TextEditorPresenter @realScrollLeft = @scrollLeft @disposables = new CompositeDisposable @emitter = new Emitter + @lineIdCounter = 1 + @linesById = new Map + @lineMarkerIndex = new MarkerIndex @visibleHighlights = {} @characterWidthsByScope = {} @lineDecorationsByScreenRow = {} @@ -82,6 +87,8 @@ class TextEditorPresenter @updateCommonGutterState() @updateReflowState() + @updateLines() + if @shouldUpdateDecorations @fetchDecorations() @updateLineDecorations() @@ -126,7 +133,8 @@ class TextEditorPresenter @shouldUpdateDecorations = true observeModel: -> - @disposables.add @model.onDidChange => + @disposables.add @model.displayLayer.onDidChangeTextSync (change) => + @invalidateLines(change) @shouldUpdateDecorations = true @emitDidUpdateState() @@ -375,7 +383,7 @@ class TextEditorPresenter tileState.lines ?= {} visibleLineIds = {} for screenRow in screenRows - line = @model.tokenizedLineForScreenRow(screenRow) + line = @lineForScreenRow(screenRow) unless line? throw new Error("No line exists for row #{screenRow}. Last screen row: #{@model.getLastScreenRow()}") @@ -387,18 +395,7 @@ class TextEditorPresenter else tileState.lines[line.id] = screenRow: screenRow - text: line.text - openScopes: line.openScopes - tags: line.tags - specialTokens: line.specialTokens - firstNonWhitespaceIndex: line.firstNonWhitespaceIndex - firstTrailingWhitespaceIndex: line.firstTrailingWhitespaceIndex - invisibles: line.invisibles - endOfLineInvisibles: line.endOfLineInvisibles - isOnlyWhitespace: line.isOnlyWhitespace() - indentLevel: line.indentLevel - tabLength: line.tabLength - fold: line.fold + words: line.words decorationClasses: @lineDecorationClassesForRow(screenRow) for id, line of tileState.lines @@ -1010,6 +1007,46 @@ class TextEditorPresenter rect.height = Math.round(rect.height) rect + updateLines: -> + visibleLineIds = new Set + + for screenRow in @getScreenRows() + screenRowStart = Point(screenRow, 0) + lineIds = @lineMarkerIndex.findStartingAt(screenRowStart) + if lineIds.size is 0 + line = @buildLine(screenRow) + @lineMarkerIndex.insert(line.id, screenRowStart, Point(screenRow, Infinity)) + @linesById.set(line.id, line) + visibleLineIds.add(line.id) + else + lineIds.forEach (id) -> + visibleLineIds.add(id) + + @linesById.forEach (line, lineId) => + unless visibleLineIds.has(lineId) + @lineMarkerIndex.delete(lineId) + @linesById.delete(lineId) + + buildLine: (screenRow) -> + line = {id: @lineIdCounter++, words: []} + @tokenIterator.seekToScreenRow(screenRow) + loop + line.words.push(@tokenIterator.getText()) + break unless @tokenIterator.moveToSuccessor() + break unless @tokenIterator.getStartScreenPosition().row is screenRow + line + + invalidateLines: ({start, replacedExtent, replacementExtent}) -> + {touch} = @lineMarkerIndex.splice(start, replacedExtent, replacementExtent) + touch.forEach (lineId) => + @lineMarkerIndex.delete(lineId) + @linesById.delete(lineId) + + lineForScreenRow: (screenRow) -> + lineIds = @lineMarkerIndex.findStartingAt(Point(screenRow, 0)) + lineId = lineIds.values().next().value + @linesById.get(lineId) + fetchDecorations: -> return unless 0 <= @startRow <= @endRow <= Infinity @decorations = @model.decorationsStateForScreenRowRange(@startRow, @endRow - 1) @@ -1236,7 +1273,7 @@ class TextEditorPresenter startBlinkingCursors: -> unless @isCursorBlinking() @state.content.cursorsVisible = true - @toggleCursorBlinkHandle = setInterval(@toggleCursorBlink.bind(this), @getCursorBlinkPeriod() / 2) + # @toggleCursorBlinkHandle = setInterval(@toggleCursorBlink.bind(this), @getCursorBlinkPeriod() / 2) isCursorBlinking: -> @toggleCursorBlinkHandle? diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 983669e28..6a13439c8 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -117,6 +117,7 @@ class TextEditor extends Model @config, @assert, @grammarRegistry, @packageManager }) @buffer = @displayBuffer.buffer + @displayLayer = buffer.addDisplayLayer({tabLength}) @selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true) for marker in @selectionsMarkerLayer.getMarkers() From b5f9ed2b0e447d5f42f2ac043697e95c86a09def Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 16 Dec 2015 17:28:06 -0700 Subject: [PATCH 056/971] Fix pixelPositionForScreenPosition --- src/lines-component.coffee | 12 ++++++ src/lines-tile-component.coffee | 9 +++++ src/lines-yardstick.coffee | 69 ++++++++++----------------------- 3 files changed, 41 insertions(+), 49 deletions(-) diff --git a/src/lines-component.coffee b/src/lines-component.coffee index b5af56885..824655579 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -104,3 +104,15 @@ class LinesComponent extends TiledComponent textNodesForLineIdAndScreenRow: (lineId, screenRow) -> tile = @presenter.tileForRow(screenRow) @getComponentForTile(tile)?.textNodesForLineId(lineId) + + lineIdForScreenRow: (screenRow) -> + tile = @presenter.tileForRow(screenRow) + @getComponentForTile(tile)?.lineIdForScreenRow(screenRow) + + lineNodeForScreenRow: (screenRow) -> + tile = @presenter.tileForRow(screenRow) + @getComponentForTile(tile)?.lineNodeForScreenRow(screenRow) + + textNodesForScreenRow: (screenRow) -> + tile = @presenter.tileForRow(screenRow) + @getComponentForTile(tile)?.textNodesForScreenRow(screenRow) diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index cf8d37a74..92d020456 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -361,3 +361,12 @@ class LinesTileComponent textNodesForLineId: (lineId) -> @textNodesByLineId[lineId].slice() + + lineIdForScreenRow: (screenRow) -> + @lineIdsByScreenRow[screenRow] + + lineNodeForScreenRow: (screenRow) -> + @lineNodesByLineId[@lineIdsByScreenRow[screenRow]] + + textNodesForScreenRow: (screenRow) -> + @textNodesByLineId[@lineIdsByScreenRow[screenRow]]?.slice() diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee index 9edbbe17a..85d1e23d4 100644 --- a/src/lines-yardstick.coffee +++ b/src/lines-yardstick.coffee @@ -9,7 +9,7 @@ class LinesYardstick @invalidateCache() invalidateCache: -> - @pixelPositionsByLineIdAndColumn = {} + @leftPixelPositionCache = {} measuredRowForPixelPosition: (pixelPosition) -> targetTop = pixelPosition.top @@ -87,61 +87,32 @@ class LinesYardstick {top, left} leftPixelPositionForScreenPosition: (row, column) -> - line = @model.tokenizedLineForScreenRow(row) - lineNode = @lineNodesProvider.lineNodeForLineIdAndScreenRow(line?.id, row) + lineNode = @lineNodesProvider.lineNodeForScreenRow(row) + lineId = @lineNodesProvider.lineIdForScreenRow(row) - return 0 unless line? and lineNode? + return 0 unless lineNode? - if cachedPosition = @pixelPositionsByLineIdAndColumn[line.id]?[column] + if cachedPosition = @leftPixelPositionCache[lineId]?[column] return cachedPosition - textNodes = @lineNodesProvider.textNodesForLineIdAndScreenRow(line.id, row) - indexWithinTextNode = null - charIndex = 0 + textNodes = @lineNodesProvider.textNodesForScreenRow(row) + textNodeStartIndex = 0 - @tokenIterator.reset(line, false) - while @tokenIterator.next() - break if foundIndexWithinTextNode? - - text = @tokenIterator.getText() - - textIndex = 0 - while textIndex < text.length - if @tokenIterator.isPairedCharacter() - char = text - charLength = 2 - textIndex += 2 - else - char = text[textIndex] - charLength = 1 - textIndex++ - - unless textNode? - textNode = textNodes.shift() - textNodeLength = textNode.textContent.length - textNodeIndex = 0 - nextTextNodeIndex = textNodeLength - - while nextTextNodeIndex <= charIndex - textNode = textNodes.shift() - textNodeLength = textNode.textContent.length - textNodeIndex = nextTextNodeIndex - nextTextNodeIndex = textNodeIndex + textNodeLength - - if charIndex is column - foundIndexWithinTextNode = charIndex - textNodeIndex - break - - charIndex += charLength + for textNode in textNodes + textNodeEndIndex = textNodeStartIndex + textNode.textContent.length + if textNodeEndIndex > column + indexInTextNode = column - textNodeStartIndex + break + else + textNodeStartIndex = textNodeEndIndex if textNode? - foundIndexWithinTextNode ?= textNode.textContent.length - position = @leftPixelPositionForCharInTextNode( - lineNode, textNode, foundIndexWithinTextNode - ) - @pixelPositionsByLineIdAndColumn[line.id] ?= {} - @pixelPositionsByLineIdAndColumn[line.id][column] = position - position + indexInTextNode ?= textNode.textContent.length + leftPixelPosition = @leftPixelPositionForCharInTextNode(lineNode, textNode, indexInTextNode) + + @leftPixelPositionCache[lineId] ?= {} + @leftPixelPositionCache[lineId][column] = leftPixelPosition + leftPixelPosition else 0 From d5b204226feb83b1aabcb92d432477cc8c5b8aae Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 16 Dec 2015 19:12:31 -0700 Subject: [PATCH 057/971] Correctly pass tabLength to addDisplayLayer --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 6a13439c8..ccec60c85 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -117,7 +117,7 @@ class TextEditor extends Model @config, @assert, @grammarRegistry, @packageManager }) @buffer = @displayBuffer.buffer - @displayLayer = buffer.addDisplayLayer({tabLength}) + @displayLayer = buffer.addDisplayLayer({tabLength: @displayBuffer.getTabLength()}) @selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true) for marker in @selectionsMarkerLayer.getMarkers() From 8e1a772a2452f613adad193c6ec7aa4839d21509 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 16 Dec 2015 19:12:54 -0700 Subject: [PATCH 058/971] Replace spaces w/ non-breaking spaces when rendering text nodes --- src/lines-tile-component.coffee | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index 92d020456..f4712dfb6 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -4,6 +4,7 @@ HighlightsComponent = require './highlights-component' TokenIterator = require './token-iterator' AcceptFilter = {acceptNode: -> NodeFilter.FILTER_ACCEPT} TokenTextEscapeRegex = /[&"'<>]/g +NBSPCharacter = '\u00a0' MaxTokenLength = 20000 cloneObject = (object) -> @@ -184,14 +185,14 @@ class LinesTileComponent setLineInnerNodes: (id, lineNode) -> {words} = @newTileState.lines[id] lineLength = 0 - for word in words + for word in words when word.length > 0 lineLength += word.length - textNode = @domElementPool.buildText(word) + textNode = @domElementPool.buildText(word.replace(/\s/g, NBSPCharacter)) lineNode.appendChild(textNode) @currentLineTextNodes.push(textNode) if lineLength is 0 - textNode = @domElementPool.buildText('\u00a0') + textNode = @domElementPool.buildText(NBSPCharacter) lineNode.appendChild(textNode) @currentLineTextNodes.push(textNode) From 9fd9c0c621c773da4416951ee0d32d3bd06289bd Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 16 Dec 2015 19:13:26 -0700 Subject: [PATCH 059/971] Fix screenPositionForPixelPosition with DisplayLayers --- src/lines-yardstick.coffee | 107 +++++++++++++++---------------------- 1 file changed, 42 insertions(+), 65 deletions(-) diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee index 85d1e23d4..b78a7f317 100644 --- a/src/lines-yardstick.coffee +++ b/src/lines-yardstick.coffee @@ -1,5 +1,6 @@ TokenIterator = require './token-iterator' {Point} = require 'text-buffer' +{isPairedCharacter} = require './text-utils' module.exports = class LinesYardstick @@ -19,63 +20,45 @@ class LinesYardstick screenPositionForPixelPosition: (pixelPosition) -> targetTop = pixelPosition.top targetLeft = pixelPosition.left - defaultCharWidth = @model.getDefaultCharWidth() row = Math.floor(targetTop / @model.getLineHeightInPixels()) targetLeft = 0 if row < 0 targetLeft = Infinity if row > @model.getLastScreenRow() row = Math.min(row, @model.getLastScreenRow()) row = Math.max(0, row) - line = @model.tokenizedLineForScreenRow(row) - lineNode = @lineNodesProvider.lineNodeForLineIdAndScreenRow(line?.id, row) + lineNode = @lineNodesProvider.lineNodeForScreenRow(row) + return Point(row, 0) unless lineNode - return Point(row, 0) unless lineNode? and line? + textNodes = @lineNodesProvider.textNodesForScreenRow(row) + lineOffset = lineNode.getBoundingClientRect().left + targetLeft += lineOffset - textNodes = @lineNodesProvider.textNodesForLineIdAndScreenRow(line.id, row) - column = 0 - previousColumn = 0 - previousLeft = 0 + textNodeStartColumn = 0 + for textNode in textNodes + {length: textNodeLength, textContent: textNodeContent} = textNode + textNodeRight = @clientRectForRange(textNode, 0, textNodeLength).right - @tokenIterator.reset(line, false) - while @tokenIterator.next() - text = @tokenIterator.getText() - textIndex = 0 - while textIndex < text.length - if @tokenIterator.isPairedCharacter() - char = text - charLength = 2 - textIndex += 2 - else - char = text[textIndex] - charLength = 1 - textIndex++ + if textNodeRight > targetLeft + characterIndex = 0 + while characterIndex < textNodeLength + if isPairedCharacter(textNodeContent, characterIndex) + nextCharacterIndex = characterIndex + 2 + else + nextCharacterIndex = characterIndex + 1 - unless textNode? - textNode = textNodes.shift() - textNodeLength = textNode.textContent.length - textNodeIndex = 0 - nextTextNodeIndex = textNodeLength + rangeRect = @clientRectForRange(textNode, characterIndex, nextCharacterIndex) - while nextTextNodeIndex <= column - textNode = textNodes.shift() - textNodeLength = textNode.textContent.length - textNodeIndex = nextTextNodeIndex - nextTextNodeIndex = textNodeIndex + textNodeLength + if rangeRect.right > targetLeft + if targetLeft <= ((rangeRect.left + rangeRect.right) / 2) + return Point(row, textNodeStartColumn + characterIndex) + else + return Point(row, textNodeStartColumn + nextCharacterIndex) + else + characterIndex = nextCharacterIndex - indexWithinTextNode = column - textNodeIndex - left = @leftPixelPositionForCharInTextNode(lineNode, textNode, indexWithinTextNode) - charWidth = left - previousLeft + textNodeStartColumn += textNodeLength - return Point(row, previousColumn) if targetLeft <= previousLeft + (charWidth / 2) - - previousLeft = left - previousColumn = column - column += charLength - - if targetLeft <= previousLeft + (charWidth / 2) - Point(row, previousColumn) - else - Point(row, column) + Point(row, textNodeStartColumn) pixelPositionForScreenPosition: (screenPosition) -> targetRow = screenPosition.row @@ -96,19 +79,24 @@ class LinesYardstick return cachedPosition textNodes = @lineNodesProvider.textNodesForScreenRow(row) - textNodeStartIndex = 0 + textNodeStartColumn = 0 for textNode in textNodes - textNodeEndIndex = textNodeStartIndex + textNode.textContent.length - if textNodeEndIndex > column - indexInTextNode = column - textNodeStartIndex + textNodeEndColumn = textNodeStartColumn + textNode.textContent.length + if textNodeEndColumn > column + indexInTextNode = column - textNodeStartColumn break else - textNodeStartIndex = textNodeEndIndex + textNodeStartColumn = textNodeEndColumn if textNode? indexInTextNode ?= textNode.textContent.length - leftPixelPosition = @leftPixelPositionForCharInTextNode(lineNode, textNode, indexInTextNode) + lineOffset = lineNode.getBoundingClientRect().left + if indexInTextNode is 0 + leftPixelPosition = @clientRectForRange(textNode, 0, 1).left + else + leftPixelPosition = @clientRectForRange(textNode, 0, indexInTextNode).right + leftPixelPosition -= lineOffset @leftPixelPositionCache[lineId] ?= {} @leftPixelPositionCache[lineId][column] = leftPixelPosition @@ -116,18 +104,7 @@ class LinesYardstick else 0 - leftPixelPositionForCharInTextNode: (lineNode, textNode, charIndex) -> - if charIndex is 0 - width = 0 - else - @rangeForMeasurement.setStart(textNode, 0) - @rangeForMeasurement.setEnd(textNode, charIndex) - width = @rangeForMeasurement.getBoundingClientRect().width - - @rangeForMeasurement.setStart(textNode, 0) - @rangeForMeasurement.setEnd(textNode, textNode.textContent.length) - left = @rangeForMeasurement.getBoundingClientRect().left - - offset = lineNode.getBoundingClientRect().left - - left + width - offset + clientRectForRange: (textNode, startIndex, endIndex) -> + @rangeForMeasurement.setStart(textNode, startIndex) + @rangeForMeasurement.setEnd(textNode, endIndex) + @rangeForMeasurement.getBoundingClientRect() From 5292767084d3c7e95caa56e7282adb4cd2ffe370 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 17 Dec 2015 16:33:09 -0700 Subject: [PATCH 060/971] Use DisplayLayers for position translation and clipping in editor Markers are still translating via the DisplayBuffer, but that will change when display markers are moved into DisplayLayers. --- src/text-editor.coffee | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index ccec60c85..18fcad9c6 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1321,7 +1321,7 @@ class TextEditor extends Model # * `options` (optional) An options hash for {::clipScreenPosition}. # # Returns a {Point}. - screenPositionForBufferPosition: (bufferPosition, options) -> @displayBuffer.screenPositionForBufferPosition(bufferPosition, options) + screenPositionForBufferPosition: (bufferPosition, options) -> @displayLayer.translateBufferPosition(bufferPosition, options) # Essential: Convert a position in screen-coordinates to buffer-coordinates. # @@ -1331,21 +1331,29 @@ class TextEditor extends Model # * `options` (optional) An options hash for {::clipScreenPosition}. # # Returns a {Point}. - bufferPositionForScreenPosition: (screenPosition, options) -> @displayBuffer.bufferPositionForScreenPosition(screenPosition, options) + bufferPositionForScreenPosition: (screenPosition, options) -> @displayLayer.translateScreenPosition(screenPosition, options) # Essential: Convert a range in buffer-coordinates to screen-coordinates. # # * `bufferRange` {Range} in buffer coordinates to translate into screen coordinates. # # Returns a {Range}. - screenRangeForBufferRange: (bufferRange) -> @displayBuffer.screenRangeForBufferRange(bufferRange) + screenRangeForBufferRange: (bufferRange) -> + bufferRange = Range.fromObject(bufferRange) + start = @displayLayer.translateBufferPosition(bufferRange.start) + end = @displayLayer.translateBufferPosition(bufferRange.end) + Range(start, end) # Essential: Convert a range in screen-coordinates to buffer-coordinates. # # * `screenRange` {Range} in screen coordinates to translate into buffer coordinates. # # Returns a {Range}. - bufferRangeForScreenRange: (screenRange) -> @displayBuffer.bufferRangeForScreenRange(screenRange) + bufferRangeForScreenRange: (screenRange) -> + screenRange = Range.fromObject(screenRange) + start = @displayLayer.translateScreenPosition(screenRange.start) + end = @displayLayer.translateScreenPosition(screenRange.end) + Range(start, end) # Extended: Clip the given {Point} to a valid position in the buffer. # @@ -1394,20 +1402,26 @@ class TextEditor extends Model # # * `screenPosition` The {Point} representing the position to clip. # * `options` (optional) {Object} - # * `wrapBeyondNewlines` {Boolean} if `true`, continues wrapping past newlines - # * `wrapAtSoftNewlines` {Boolean} if `true`, continues wrapping past soft newlines - # * `screenLine` {Boolean} if `true`, indicates that you're using a line number, not a row number + # * `clipDirection` {String} If `'backward'`, returns the first valid + # position preceding an invalid position. If `'forward'`, returns the + # first valid position following a valid position. Defaults to + # `'backward'`. # # Returns a {Point}. - clipScreenPosition: (screenPosition, options) -> @displayBuffer.clipScreenPosition(screenPosition, options) + clipScreenPosition: (screenPosition, options) -> @displayLayer.clipScreenPosition(screenPosition, options) # Extended: Clip the start and end of the given range to valid positions on screen. # See {::clipScreenPosition} for more information. # # * `range` The {Range} to clip. # * `options` (optional) See {::clipScreenPosition} `options`. + # # Returns a {Range}. - clipScreenRange: (range, options) -> @displayBuffer.clipScreenRange(range, options) + clipScreenRange: (screenRange, options) -> + screenRange = Range.fromObject(screenRange) + start = @displayLayer.clipScreenPosition(screenRange.start, options) + end = @displayLayer.clipScreenPosition(screenRange.end, options) + Range(start, end) ### Section: Decorations From caf6d7f473b7ccf0064100e9e9aa73cb1e124b2b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 18 Dec 2015 19:20:09 -0700 Subject: [PATCH 061/971] Use DisplayMarkerLayers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I’m creating the DisplayLayer in the DisplayBuffer. In places where our API deviates from DisplayBuffer, I’ll use the DisplayLayer directly from as a property of TextEditor. Otherwise I’ll continue to delegate from the DisplayLayer into the DisplayLayer to minimize impact until the DisplayBuffer can be removed entirely. --- src/cursor.coffee | 8 +- src/decoration.coffee | 6 +- src/display-buffer.coffee | 31 +-- src/gutter.coffee | 4 +- src/layer-decoration.coffee | 2 +- src/text-editor-marker-layer.coffee | 192 -------------- src/text-editor-marker.coffee | 371 ---------------------------- src/text-editor.coffee | 63 +++-- 8 files changed, 54 insertions(+), 623 deletions(-) delete mode 100644 src/text-editor-marker-layer.coffee delete mode 100644 src/text-editor-marker.coffee diff --git a/src/cursor.coffee b/src/cursor.coffee index 5b3b23b73..f91a7bfd9 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -9,7 +9,7 @@ EmptyLineRegExp = /(\r\n[\t ]*\r\n)|(\n[\t ]*\n)/g # where text can be inserted. # # Cursors belong to {TextEditor}s and have some metadata attached in the form -# of a {TextEditorMarker}. +# of a {DisplayMarker}. module.exports = class Cursor extends Model screenPosition: null @@ -129,7 +129,7 @@ class Cursor extends Model Section: Cursor Position Details ### - # Public: Returns the underlying {TextEditorMarker} for the cursor. + # Public: Returns the underlying {DisplayMarker} for the cursor. # Useful with overlay {Decoration}s. getMarker: -> @marker @@ -265,7 +265,7 @@ class Cursor extends Model columnCount-- # subtract 1 for the row move column = column - columnCount - @setScreenPosition({row, column}, clip: 'backward') + @setScreenPosition({row, column}, clipDirection: 'backward') # Public: Moves the cursor right one screen column. # @@ -292,7 +292,7 @@ class Cursor extends Model columnsRemainingInLine = rowLength column = column + columnCount - @setScreenPosition({row, column}, clip: 'forward', wrapBeyondNewlines: true, wrapAtSoftNewlines: true) + @setScreenPosition({row, column}, clipDirection: 'forward') # Public: Moves the cursor to the top of the buffer. moveToTop: -> diff --git a/src/decoration.coffee b/src/decoration.coffee index f57d234d1..9387913d4 100644 --- a/src/decoration.coffee +++ b/src/decoration.coffee @@ -11,7 +11,7 @@ translateDecorationParamsOldToNew = (decorationParams) -> decorationParams.gutterName = 'line-number' decorationParams -# Essential: Represents a decoration that follows a {TextEditorMarker}. A decoration is +# Essential: Represents a decoration that follows a {DisplayMarker}. A decoration is # basically a visual representation of a marker. It allows you to add CSS # classes to line numbers in the gutter, lines, and add selection-line regions # around marked ranges of text. @@ -25,7 +25,7 @@ translateDecorationParamsOldToNew = (decorationParams) -> # decoration = editor.decorateMarker(marker, {type: 'line', class: 'my-line-class'}) # ``` # -# Best practice for destroying the decoration is by destroying the {TextEditorMarker}. +# Best practice for destroying the decoration is by destroying the {DisplayMarker}. # # ```coffee # marker.destroy() @@ -72,7 +72,7 @@ class Decoration # Essential: Destroy this marker. # - # If you own the marker, you should use {TextEditorMarker::destroy} which will destroy + # If you own the marker, you should use {DisplayMarker::destroy} which will destroy # this decoration. destroy: -> return if @destroyed diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 8b95656f9..1f8c3cc26 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -8,7 +8,6 @@ Model = require './model' Token = require './token' Decoration = require './decoration' LayerDecoration = require './layer-decoration' -TextEditorMarkerLayer = require './text-editor-marker-layer' class BufferToScreenConversionError extends Error constructor: (@message, @metadata) -> @@ -54,9 +53,9 @@ class DisplayBuffer extends Model @grammarRegistry, @packageManager, @assert }) @buffer = @tokenizedBuffer.buffer + @displayLayer = @buffer.addDisplayLayer({tabLength: @getTabLength()}) @charWidthsByScope = {} - @defaultMarkerLayer = new TextEditorMarkerLayer(this, @buffer.getDefaultMarkerLayer(), true) - @customMarkerLayersById = {} + @defaultMarkerLayer = @displayLayer.addMarkerLayer() @foldsByMarkerId = {} @decorationsById = {} @decorationsByMarkerId = {} @@ -835,17 +834,17 @@ class DisplayBuffer extends Model decorationsForMarkerId: (markerId) -> @decorationsByMarkerId[markerId] - # Retrieves a {TextEditorMarker} based on its id. + # Retrieves a {DisplayMarker} based on its id. # # id - A {Number} representing a marker id # - # Returns the {TextEditorMarker} (if it exists). + # Returns the {DisplayMarker} (if it exists). getMarker: (id) -> @defaultMarkerLayer.getMarker(id) # Retrieves the active markers in the buffer. # - # Returns an {Array} of existing {TextEditorMarker}s. + # Returns an {Array} of existing {DisplayMarker}s. getMarkers: -> @defaultMarkerLayer.getMarkers() @@ -855,7 +854,7 @@ class DisplayBuffer extends Model # Public: Constructs a new marker at the given screen range. # # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {TextEditorMarker} constructor + # options - Options to pass to the {DisplayMarker} constructor # # Returns a {Number} representing the new marker's ID. markScreenRange: (screenRange, options) -> @@ -864,7 +863,7 @@ class DisplayBuffer extends Model # Public: Constructs a new marker at the given buffer range. # # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {TextEditorMarker} constructor + # options - Options to pass to the {DisplayMarker} constructor # # Returns a {Number} representing the new marker's ID. markBufferRange: (bufferRange, options) -> @@ -873,7 +872,7 @@ class DisplayBuffer extends Model # Public: Constructs a new marker at the given screen position. # # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {TextEditorMarker} constructor + # options - Options to pass to the {DisplayMarker} constructor # # Returns a {Number} representing the new marker's ID. markScreenPosition: (screenPosition, options) -> @@ -882,7 +881,7 @@ class DisplayBuffer extends Model # Public: Constructs a new marker at the given buffer position. # # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {TextEditorMarker} constructor + # options - Options to pass to the {DisplayMarker} constructor # # Returns a {Number} representing the new marker's ID. markBufferPosition: (bufferPosition, options) -> @@ -892,7 +891,7 @@ class DisplayBuffer extends Model # # Refer to {DisplayBuffer::findMarkers} for details. # - # Returns a {TextEditorMarker} or null + # Returns a {DisplayMarker} or null findMarker: (params) -> @defaultMarkerLayer.findMarkers(params)[0] @@ -913,19 +912,15 @@ class DisplayBuffer extends Model # :containedInBufferRange - A {Range} or range-compatible {Array}. Only # returns markers contained within this range. # - # Returns an {Array} of {TextEditorMarker}s + # Returns an {Array} of {DisplayMarker}s findMarkers: (params) -> @defaultMarkerLayer.findMarkers(params) addMarkerLayer: (options) -> - bufferLayer = @buffer.addMarkerLayer(options) - @getMarkerLayer(bufferLayer.id) + @displayLayer.addMarkerLayer(options) getMarkerLayer: (id) -> - if layer = @customMarkerLayersById[id] - layer - else if bufferLayer = @buffer.getMarkerLayer(id) - @customMarkerLayersById[id] = new TextEditorMarkerLayer(this, bufferLayer) + @displayLayer.getMarkerLayer(id) getDefaultMarkerLayer: -> @defaultMarkerLayer diff --git a/src/gutter.coffee b/src/gutter.coffee index f59fa7b6e..2fc362cbd 100644 --- a/src/gutter.coffee +++ b/src/gutter.coffee @@ -71,13 +71,13 @@ class Gutter isVisible: -> @visible - # Essential: Add a decoration that tracks a {TextEditorMarker}. When the marker moves, + # Essential: Add a decoration that tracks a {DisplayMarker}. When the marker moves, # is invalidated, or is destroyed, the decoration will be updated to reflect # the marker's state. # # ## Arguments # - # * `marker` A {TextEditorMarker} you want this decoration to follow. + # * `marker` A {DisplayMarker} you want this decoration to follow. # * `decorationParams` An {Object} representing the decoration. It is passed # to {TextEditor::decorateMarker} as its `decorationParams` and so supports # all options documented there. diff --git a/src/layer-decoration.coffee b/src/layer-decoration.coffee index 1f76140a3..7d2396e0d 100644 --- a/src/layer-decoration.coffee +++ b/src/layer-decoration.coffee @@ -48,7 +48,7 @@ class LayerDecoration # Essential: Override the decoration properties for a specific marker. # - # * `marker` The {TextEditorMarker} or {Marker} for which to override + # * `marker` The {DisplayMarker} or {Marker} for which to override # properties. # * `properties` An {Object} containing properties to apply to this marker. # Pass `null` to clear the override. diff --git a/src/text-editor-marker-layer.coffee b/src/text-editor-marker-layer.coffee deleted file mode 100644 index e99ad7323..000000000 --- a/src/text-editor-marker-layer.coffee +++ /dev/null @@ -1,192 +0,0 @@ -TextEditorMarker = require './text-editor-marker' - -# Public: *Experimental:* A container for a related set of markers at the -# {TextEditor} level. Wraps an underlying {MarkerLayer} on the editor's -# {TextBuffer}. -# -# This API is experimental and subject to change on any release. -module.exports = -class TextEditorMarkerLayer - constructor: (@displayBuffer, @bufferMarkerLayer, @isDefaultLayer) -> - @id = @bufferMarkerLayer.id - @markersById = {} - - ### - Section: Lifecycle - ### - - # Essential: Destroy this layer. - destroy: -> - if @isDefaultLayer - marker.destroy() for id, marker of @markersById - else - @bufferMarkerLayer.destroy() - - ### - Section: Querying - ### - - # Essential: Get an existing marker by its id. - # - # Returns a {TextEditorMarker}. - getMarker: (id) -> - if editorMarker = @markersById[id] - editorMarker - else if bufferMarker = @bufferMarkerLayer.getMarker(id) - @markersById[id] = new TextEditorMarker(this, bufferMarker) - - # Essential: Get all markers in the layer. - # - # Returns an {Array} of {TextEditorMarker}s. - getMarkers: -> - @bufferMarkerLayer.getMarkers().map ({id}) => @getMarker(id) - - # Public: Get the number of markers in the marker layer. - # - # Returns a {Number}. - getMarkerCount: -> - @bufferMarkerLayer.getMarkerCount() - - # Public: Find markers in the layer conforming to the given parameters. - # - # See the documentation for {TextEditor::findMarkers}. - findMarkers: (params) -> - params = @translateToBufferMarkerParams(params) - @bufferMarkerLayer.findMarkers(params).map (stringMarker) => @getMarker(stringMarker.id) - - ### - Section: Marker creation - ### - - # Essential: Create a marker on this layer with the given range in buffer - # coordinates. - # - # See the documentation for {TextEditor::markBufferRange} - markBufferRange: (bufferRange, options) -> - @getMarker(@bufferMarkerLayer.markRange(bufferRange, options).id) - - # Essential: Create a marker on this layer with the given range in screen - # coordinates. - # - # See the documentation for {TextEditor::markScreenRange} - markScreenRange: (screenRange, options) -> - bufferRange = @displayBuffer.bufferRangeForScreenRange(screenRange) - @markBufferRange(bufferRange, options) - - # Public: Create a marker on this layer with the given buffer position and no - # tail. - # - # See the documentation for {TextEditor::markBufferPosition} - markBufferPosition: (bufferPosition, options) -> - @getMarker(@bufferMarkerLayer.markPosition(bufferPosition, options).id) - - # Public: Create a marker on this layer with the given screen position and no - # tail. - # - # See the documentation for {TextEditor::markScreenPosition} - markScreenPosition: (screenPosition, options) -> - bufferPosition = @displayBuffer.bufferPositionForScreenPosition(screenPosition) - @markBufferPosition(bufferPosition, options) - - ### - Section: Event Subscription - ### - - # Public: Subscribe to be notified asynchronously whenever markers are - # created, updated, or destroyed on this layer. *Prefer this method for - # optimal performance when interacting with layers that could contain large - # numbers of markers.* - # - # * `callback` A {Function} that will be called with no arguments when changes - # occur on this layer. - # - # Subscribers are notified once, asynchronously when any number of changes - # occur in a given tick of the event loop. You should re-query the layer - # to determine the state of markers in which you're interested in. It may - # be counter-intuitive, but this is much more efficient than subscribing to - # events on individual markers, which are expensive to deliver. - # - # Returns a {Disposable}. - onDidUpdate: (callback) -> - @bufferMarkerLayer.onDidUpdate(callback) - - # Public: Subscribe to be notified synchronously whenever markers are created - # on this layer. *Avoid this method for optimal performance when interacting - # with layers that could contain large numbers of markers.* - # - # * `callback` A {Function} that will be called with a {TextEditorMarker} - # whenever a new marker is created. - # - # You should prefer {onDidUpdate} when synchronous notifications aren't - # absolutely necessary. - # - # Returns a {Disposable}. - onDidCreateMarker: (callback) -> - @bufferMarkerLayer.onDidCreateMarker (bufferMarker) => - callback(@getMarker(bufferMarker.id)) - - # Public: Subscribe to be notified synchronously when this layer is destroyed. - # - # Returns a {Disposable}. - onDidDestroy: (callback) -> - @bufferMarkerLayer.onDidDestroy(callback) - - ### - Section: Private - ### - - refreshMarkerScreenPositions: -> - for marker in @getMarkers() - marker.notifyObservers(textChanged: false) - return - - didDestroyMarker: (marker) -> - delete @markersById[marker.id] - - translateToBufferMarkerParams: (params) -> - bufferMarkerParams = {} - for key, value of params - switch key - when 'startBufferPosition' - key = 'startPosition' - when 'endBufferPosition' - key = 'endPosition' - when 'startScreenPosition' - key = 'startPosition' - value = @displayBuffer.bufferPositionForScreenPosition(value) - when 'endScreenPosition' - key = 'endPosition' - value = @displayBuffer.bufferPositionForScreenPosition(value) - when 'startBufferRow' - key = 'startRow' - when 'endBufferRow' - key = 'endRow' - when 'startScreenRow' - key = 'startRow' - value = @displayBuffer.bufferRowForScreenRow(value) - when 'endScreenRow' - key = 'endRow' - value = @displayBuffer.bufferRowForScreenRow(value) - when 'intersectsBufferRowRange' - key = 'intersectsRowRange' - when 'intersectsScreenRowRange' - key = 'intersectsRowRange' - [startRow, endRow] = value - value = [@displayBuffer.bufferRowForScreenRow(startRow), @displayBuffer.bufferRowForScreenRow(endRow)] - when 'containsBufferRange' - key = 'containsRange' - when 'containsBufferPosition' - key = 'containsPosition' - when 'containedInBufferRange' - key = 'containedInRange' - when 'containedInScreenRange' - key = 'containedInRange' - value = @displayBuffer.bufferRangeForScreenRange(value) - when 'intersectsBufferRange' - key = 'intersectsRange' - when 'intersectsScreenRange' - key = 'intersectsRange' - value = @displayBuffer.bufferRangeForScreenRange(value) - bufferMarkerParams[key] = value - - bufferMarkerParams diff --git a/src/text-editor-marker.coffee b/src/text-editor-marker.coffee deleted file mode 100644 index df84700ee..000000000 --- a/src/text-editor-marker.coffee +++ /dev/null @@ -1,371 +0,0 @@ -_ = require 'underscore-plus' -{CompositeDisposable, Emitter} = require 'event-kit' - -# Essential: Represents a buffer annotation that remains logically stationary -# even as the buffer changes. This is used to represent cursors, folds, snippet -# targets, misspelled words, and anything else that needs to track a logical -# location in the buffer over time. -# -# ### TextEditorMarker Creation -# -# Use {TextEditor::markBufferRange} rather than creating Markers directly. -# -# ### Head and Tail -# -# Markers always have a *head* and sometimes have a *tail*. If you think of a -# marker as an editor selection, the tail is the part that's stationary and the -# head is the part that moves when the mouse is moved. A marker without a tail -# always reports an empty range at the head position. A marker with a head position -# greater than the tail is in a "normal" orientation. If the head precedes the -# tail the marker is in a "reversed" orientation. -# -# ### Validity -# -# Markers are considered *valid* when they are first created. Depending on the -# invalidation strategy you choose, certain changes to the buffer can cause a -# marker to become invalid, for example if the text surrounding the marker is -# deleted. The strategies, in order of descending fragility: -# -# * __never__: The marker is never marked as invalid. This is a good choice for -# markers representing selections in an editor. -# * __surround__: The marker is invalidated by changes that completely surround it. -# * __overlap__: The marker is invalidated by changes that surround the -# start or end of the marker. This is the default. -# * __inside__: The marker is invalidated by changes that extend into the -# inside of the marker. Changes that end at the marker's start or -# start at the marker's end do not invalidate the marker. -# * __touch__: The marker is invalidated by a change that touches the marked -# region in any way, including changes that end at the marker's -# start or start at the marker's end. This is the most fragile strategy. -# -# See {TextEditor::markBufferRange} for usage. -module.exports = -class TextEditorMarker - bufferMarkerSubscription: null - oldHeadBufferPosition: null - oldHeadScreenPosition: null - oldTailBufferPosition: null - oldTailScreenPosition: null - wasValid: true - hasChangeObservers: false - - ### - Section: Construction and Destruction - ### - - constructor: (@layer, @bufferMarker) -> - {@displayBuffer} = @layer - @emitter = new Emitter - @disposables = new CompositeDisposable - @id = @bufferMarker.id - - @disposables.add @bufferMarker.onDidDestroy => @destroyed() - - # Essential: Destroys the marker, causing it to emit the 'destroyed' event. Once - # destroyed, a marker cannot be restored by undo/redo operations. - destroy: -> - @bufferMarker.destroy() - @disposables.dispose() - - # Essential: Creates and returns a new {TextEditorMarker} with the same properties as - # this marker. - # - # {Selection} markers (markers with a custom property `type: "selection"`) - # should be copied with a different `type` value, for example with - # `marker.copy({type: null})`. Otherwise, the new marker's selection will - # be merged with this marker's selection, and a `null` value will be - # returned. - # - # * `properties` (optional) {Object} properties to associate with the new - # marker. The new marker's properties are computed by extending this marker's - # properties with `properties`. - # - # Returns a {TextEditorMarker}. - copy: (properties) -> - @layer.getMarker(@bufferMarker.copy(properties).id) - - ### - Section: Event Subscription - ### - - # Essential: Invoke the given callback when the state of the marker changes. - # - # * `callback` {Function} to be called when the marker changes. - # * `event` {Object} with the following keys: - # * `oldHeadBufferPosition` {Point} representing the former head buffer position - # * `newHeadBufferPosition` {Point} representing the new head buffer position - # * `oldTailBufferPosition` {Point} representing the former tail buffer position - # * `newTailBufferPosition` {Point} representing the new tail buffer position - # * `oldHeadScreenPosition` {Point} representing the former head screen position - # * `newHeadScreenPosition` {Point} representing the new head screen position - # * `oldTailScreenPosition` {Point} representing the former tail screen position - # * `newTailScreenPosition` {Point} representing the new tail screen position - # * `wasValid` {Boolean} indicating whether the marker was valid before the change - # * `isValid` {Boolean} indicating whether the marker is now valid - # * `hadTail` {Boolean} indicating whether the marker had a tail before the change - # * `hasTail` {Boolean} indicating whether the marker now has a tail - # * `oldProperties` {Object} containing the marker's custom properties before the change. - # * `newProperties` {Object} containing the marker's custom properties after the change. - # * `textChanged` {Boolean} indicating whether this change was caused by a textual change - # to the buffer or whether the marker was manipulated directly via its public API. - # - # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. - onDidChange: (callback) -> - unless @hasChangeObservers - @oldHeadBufferPosition = @getHeadBufferPosition() - @oldHeadScreenPosition = @getHeadScreenPosition() - @oldTailBufferPosition = @getTailBufferPosition() - @oldTailScreenPosition = @getTailScreenPosition() - @wasValid = @isValid() - @disposables.add @bufferMarker.onDidChange (event) => @notifyObservers(event) - @hasChangeObservers = true - @emitter.on 'did-change', callback - - # Essential: Invoke the given callback when the marker is destroyed. - # - # * `callback` {Function} to be called when the marker is destroyed. - # - # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. - onDidDestroy: (callback) -> - @emitter.on 'did-destroy', callback - - ### - Section: TextEditorMarker Details - ### - - # Essential: Returns a {Boolean} indicating whether the marker is valid. Markers can be - # invalidated when a region surrounding them in the buffer is changed. - isValid: -> - @bufferMarker.isValid() - - # Essential: Returns a {Boolean} indicating whether the marker has been destroyed. A marker - # can be invalid without being destroyed, in which case undoing the invalidating - # operation would restore the marker. Once a marker is destroyed by calling - # {TextEditorMarker::destroy}, no undo/redo operation can ever bring it back. - isDestroyed: -> - @bufferMarker.isDestroyed() - - # Essential: Returns a {Boolean} indicating whether the head precedes the tail. - isReversed: -> - @bufferMarker.isReversed() - - # Essential: Get the invalidation strategy for this marker. - # - # Valid values include: `never`, `surround`, `overlap`, `inside`, and `touch`. - # - # Returns a {String}. - getInvalidationStrategy: -> - @bufferMarker.getInvalidationStrategy() - - # Essential: Returns an {Object} containing any custom properties associated with - # the marker. - getProperties: -> - @bufferMarker.getProperties() - - # Essential: Merges an {Object} containing new properties into the marker's - # existing properties. - # - # * `properties` {Object} - setProperties: (properties) -> - @bufferMarker.setProperties(properties) - - matchesProperties: (attributes) -> - attributes = @layer.translateToBufferMarkerParams(attributes) - @bufferMarker.matchesParams(attributes) - - ### - Section: Comparing to other markers - ### - - # Essential: Returns a {Boolean} indicating whether this marker is equivalent to - # another marker, meaning they have the same range and options. - # - # * `other` {TextEditorMarker} other marker - isEqual: (other) -> - return false unless other instanceof @constructor - @bufferMarker.isEqual(other.bufferMarker) - - # Essential: Compares this marker to another based on their ranges. - # - # * `other` {TextEditorMarker} - # - # Returns a {Number} - compare: (other) -> - @bufferMarker.compare(other.bufferMarker) - - ### - Section: Managing the marker's range - ### - - # Essential: Gets the buffer range of the display marker. - # - # Returns a {Range}. - getBufferRange: -> - @bufferMarker.getRange() - - # Essential: Modifies the buffer range of the display marker. - # - # * `bufferRange` The new {Range} to use - # * `properties` (optional) {Object} properties to associate with the marker. - # * `reversed` {Boolean} If true, the marker will to be in a reversed orientation. - setBufferRange: (bufferRange, properties) -> - @bufferMarker.setRange(bufferRange, properties) - - # Essential: Gets the screen range of the display marker. - # - # Returns a {Range}. - getScreenRange: -> - @displayBuffer.screenRangeForBufferRange(@getBufferRange(), wrapAtSoftNewlines: true) - - # Essential: Modifies the screen range of the display marker. - # - # * `screenRange` The new {Range} to use - # * `properties` (optional) {Object} properties to associate with the marker. - # * `reversed` {Boolean} If true, the marker will to be in a reversed orientation. - setScreenRange: (screenRange, options) -> - @setBufferRange(@displayBuffer.bufferRangeForScreenRange(screenRange), options) - - # Essential: Retrieves the buffer position of the marker's start. This will always be - # less than or equal to the result of {TextEditorMarker::getEndBufferPosition}. - # - # Returns a {Point}. - getStartBufferPosition: -> - @bufferMarker.getStartPosition() - - # Essential: Retrieves the screen position of the marker's start. This will always be - # less than or equal to the result of {TextEditorMarker::getEndScreenPosition}. - # - # Returns a {Point}. - getStartScreenPosition: -> - @displayBuffer.screenPositionForBufferPosition(@getStartBufferPosition(), wrapAtSoftNewlines: true) - - # Essential: Retrieves the buffer position of the marker's end. This will always be - # greater than or equal to the result of {TextEditorMarker::getStartBufferPosition}. - # - # Returns a {Point}. - getEndBufferPosition: -> - @bufferMarker.getEndPosition() - - # Essential: Retrieves the screen position of the marker's end. This will always be - # greater than or equal to the result of {TextEditorMarker::getStartScreenPosition}. - # - # Returns a {Point}. - getEndScreenPosition: -> - @displayBuffer.screenPositionForBufferPosition(@getEndBufferPosition(), wrapAtSoftNewlines: true) - - # Extended: Retrieves the buffer position of the marker's head. - # - # Returns a {Point}. - getHeadBufferPosition: -> - @bufferMarker.getHeadPosition() - - # Extended: Sets the buffer position of the marker's head. - # - # * `bufferPosition` The new {Point} to use - # * `properties` (optional) {Object} properties to associate with the marker. - setHeadBufferPosition: (bufferPosition, properties) -> - @bufferMarker.setHeadPosition(bufferPosition, properties) - - # Extended: Retrieves the screen position of the marker's head. - # - # Returns a {Point}. - getHeadScreenPosition: -> - @displayBuffer.screenPositionForBufferPosition(@getHeadBufferPosition(), wrapAtSoftNewlines: true) - - # Extended: Sets the screen position of the marker's head. - # - # * `screenPosition` The new {Point} to use - # * `properties` (optional) {Object} properties to associate with the marker. - setHeadScreenPosition: (screenPosition, properties) -> - @setHeadBufferPosition(@displayBuffer.bufferPositionForScreenPosition(screenPosition, properties)) - - # Extended: Retrieves the buffer position of the marker's tail. - # - # Returns a {Point}. - getTailBufferPosition: -> - @bufferMarker.getTailPosition() - - # Extended: Sets the buffer position of the marker's tail. - # - # * `bufferPosition` The new {Point} to use - # * `properties` (optional) {Object} properties to associate with the marker. - setTailBufferPosition: (bufferPosition) -> - @bufferMarker.setTailPosition(bufferPosition) - - # Extended: Retrieves the screen position of the marker's tail. - # - # Returns a {Point}. - getTailScreenPosition: -> - @displayBuffer.screenPositionForBufferPosition(@getTailBufferPosition(), wrapAtSoftNewlines: true) - - # Extended: Sets the screen position of the marker's tail. - # - # * `screenPosition` The new {Point} to use - # * `properties` (optional) {Object} properties to associate with the marker. - setTailScreenPosition: (screenPosition, options) -> - @setTailBufferPosition(@displayBuffer.bufferPositionForScreenPosition(screenPosition, options)) - - # Extended: Returns a {Boolean} indicating whether the marker has a tail. - hasTail: -> - @bufferMarker.hasTail() - - # Extended: Plants the marker's tail at the current head position. After calling - # the marker's tail position will be its head position at the time of the - # call, regardless of where the marker's head is moved. - # - # * `properties` (optional) {Object} properties to associate with the marker. - plantTail: -> - @bufferMarker.plantTail() - - # Extended: Removes the marker's tail. After calling the marker's head position - # will be reported as its current tail position until the tail is planted - # again. - # - # * `properties` (optional) {Object} properties to associate with the marker. - clearTail: (properties) -> - @bufferMarker.clearTail(properties) - - ### - Section: Private utility methods - ### - - # Returns a {String} representation of the marker - inspect: -> - "TextEditorMarker(id: #{@id}, bufferRange: #{@getBufferRange()})" - - destroyed: -> - @layer.didDestroyMarker(this) - @emitter.emit 'did-destroy' - @emitter.dispose() - - notifyObservers: ({textChanged}) -> - textChanged ?= false - - newHeadBufferPosition = @getHeadBufferPosition() - newHeadScreenPosition = @getHeadScreenPosition() - newTailBufferPosition = @getTailBufferPosition() - newTailScreenPosition = @getTailScreenPosition() - isValid = @isValid() - - return if isValid is @wasValid and - newHeadBufferPosition.isEqual(@oldHeadBufferPosition) and - newHeadScreenPosition.isEqual(@oldHeadScreenPosition) and - newTailBufferPosition.isEqual(@oldTailBufferPosition) and - newTailScreenPosition.isEqual(@oldTailScreenPosition) - - changeEvent = { - @oldHeadScreenPosition, newHeadScreenPosition, - @oldTailScreenPosition, newTailScreenPosition, - @oldHeadBufferPosition, newHeadBufferPosition, - @oldTailBufferPosition, newTailBufferPosition, - textChanged, - isValid - } - - @oldHeadBufferPosition = newHeadBufferPosition - @oldHeadScreenPosition = newHeadScreenPosition - @oldTailBufferPosition = newTailBufferPosition - @oldTailScreenPosition = newTailScreenPosition - @wasValid = isValid - - @emitter.emit 'did-change', changeEvent diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 18fcad9c6..e5757a6e6 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -116,8 +116,7 @@ class TextEditor extends Model buffer, tabLength, softWrapped, ignoreInvisibles: @mini, largeFileMode, @config, @assert, @grammarRegistry, @packageManager }) - @buffer = @displayBuffer.buffer - @displayLayer = buffer.addDisplayLayer({tabLength: @displayBuffer.getTabLength()}) + {@buffer, @displayLayer} = @displayBuffer @selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true) for marker in @selectionsMarkerLayer.getMarkers() @@ -1427,7 +1426,7 @@ class TextEditor extends Model Section: Decorations ### - # Essential: Add a decoration that tracks a {TextEditorMarker}. When the + # Essential: Add a decoration that tracks a {DisplayMarker}. When the # marker moves, is invalidated, or is destroyed, the decoration will be # updated to reflect the marker's state. # @@ -1448,28 +1447,28 @@ class TextEditor extends Model # # ``` # * __overlay__: Positions the view associated with the given item at the head - # or tail of the given `TextEditorMarker`. - # * __gutter__: A decoration that tracks a {TextEditorMarker} in a {Gutter}. Gutter + # or tail of the given `DisplayMarker`. + # * __gutter__: A decoration that tracks a {DisplayMarker} in a {Gutter}. Gutter # decorations are created by calling {Gutter::decorateMarker} on the # desired `Gutter` instance. # # ## Arguments # - # * `marker` A {TextEditorMarker} you want this decoration to follow. + # * `marker` A {DisplayMarker} you want this decoration to follow. # * `decorationParams` An {Object} representing the decoration e.g. # `{type: 'line-number', class: 'linter-error'}` # * `type` There are several supported decoration types. The behavior of the # types are as follows: # * `line` Adds the given `class` to the lines overlapping the rows - # spanned by the `TextEditorMarker`. + # spanned by the `DisplayMarker`. # * `line-number` Adds the given `class` to the line numbers overlapping - # the rows spanned by the `TextEditorMarker`. + # the rows spanned by the `DisplayMarker`. # * `highlight` Creates a `.highlight` div with the nested class with up - # to 3 nested regions that fill the area spanned by the `TextEditorMarker`. + # to 3 nested regions that fill the area spanned by the `DisplayMarker`. # * `overlay` Positions the view associated with the given item at the - # head or tail of the given `TextEditorMarker`, depending on the `position` + # head or tail of the given `DisplayMarker`, depending on the `position` # property. - # * `gutter` Tracks a {TextEditorMarker} in a {Gutter}. Created by calling + # * `gutter` Tracks a {DisplayMarker} in a {Gutter}. Created by calling # {Gutter::decorateMarker} on the desired `Gutter` instance. # * `class` This CSS class will be applied to the decorated line number, # line, highlight, or overlay. @@ -1477,16 +1476,16 @@ class TextEditor extends Model # corresponding view registered. Only applicable to the `gutter` and # `overlay` types. # * `onlyHead` (optional) If `true`, the decoration will only be applied to - # the head of the `TextEditorMarker`. Only applicable to the `line` and + # the head of the `DisplayMarker`. Only applicable to the `line` and # `line-number` types. # * `onlyEmpty` (optional) If `true`, the decoration will only be applied if - # the associated `TextEditorMarker` is empty. Only applicable to the `gutter`, + # the associated `DisplayMarker` is empty. Only applicable to the `gutter`, # `line`, and `line-number` types. # * `onlyNonEmpty` (optional) If `true`, the decoration will only be applied - # if the associated `TextEditorMarker` is non-empty. Only applicable to the + # if the associated `DisplayMarker` is non-empty. Only applicable to the # `gutter`, `line`, and `line-number` types. # * `position` (optional) Only applicable to decorations of type `overlay`, - # controls where the overlay view is positioned relative to the `TextEditorMarker`. + # controls where the overlay view is positioned relative to the `DisplayMarker`. # Values can be `'head'` (the default), or `'tail'`. # # Returns a {Decoration} object @@ -1497,7 +1496,7 @@ class TextEditor extends Model # marker layer. Can be used to decorate a large number of markers without # having to create and manage many individual decorations. # - # * `markerLayer` A {TextEditorMarkerLayer} or {MarkerLayer} to decorate. + # * `markerLayer` A {DisplayMarkerLayer} or {MarkerLayer} to decorate. # * `decorationParams` The same parameters that are passed to # {decorateMarker}, except the `type` cannot be `overlay` or `gutter`. # @@ -1515,7 +1514,7 @@ class TextEditor extends Model # # Returns an {Object} of decorations in the form # `{1: [{id: 10, type: 'line-number', class: 'someclass'}], 2: ...}` - # where the keys are {TextEditorMarker} IDs, and the values are an array of decoration + # where the keys are {DisplayMarker} IDs, and the values are an array of decoration # params objects attached to the marker. # Returns an empty object when no decorations are found decorationsForScreenRowRange: (startScreenRow, endScreenRow) -> @@ -1610,7 +1609,7 @@ class TextEditor extends Model # region in any way, including changes that end at the marker's # start or start at the marker's end. This is the most fragile strategy. # - # Returns a {TextEditorMarker}. + # Returns a {DisplayMarker}. markBufferRange: (args...) -> @displayBuffer.markBufferRange(args...) @@ -1645,7 +1644,7 @@ class TextEditor extends Model # region in any way, including changes that end at the marker's # start or start at the marker's end. This is the most fragile strategy. # - # Returns a {TextEditorMarker}. + # Returns a {DisplayMarker}. markScreenRange: (args...) -> @displayBuffer.markScreenRange(args...) @@ -1655,7 +1654,7 @@ class TextEditor extends Model # * `position` A {Point} or {Array} of `[row, column]`. # * `options` (optional) See {TextBuffer::markRange}. # - # Returns a {TextEditorMarker}. + # Returns a {DisplayMarker}. markBufferPosition: (args...) -> @displayBuffer.markBufferPosition(args...) @@ -1665,11 +1664,11 @@ class TextEditor extends Model # * `position` A {Point} or {Array} of `[row, column]`. # * `options` (optional) See {TextBuffer::markRange}. # - # Returns a {TextEditorMarker}. + # Returns a {DisplayMarker}. markScreenPosition: (args...) -> @displayBuffer.markScreenPosition(args...) - # Essential: Find all {TextEditorMarker}s on the default marker layer that + # Essential: Find all {DisplayMarker}s on the default marker layer that # match the given properties. # # This method finds markers based on the given properties. Markers can be @@ -1692,14 +1691,14 @@ class TextEditor extends Model findMarkers: (properties) -> @displayBuffer.findMarkers(properties) - # Extended: Get the {TextEditorMarker} on the default layer for the given + # Extended: Get the {DisplayMarker} on the default layer for the given # marker id. # # * `id` {Number} id of the marker getMarker: (id) -> @displayBuffer.getMarker(id) - # Extended: Get all {TextEditorMarker}s on the default marker layer. Consider + # Extended: Get all {DisplayMarker}s on the default marker layer. Consider # using {::findMarkers} getMarkers: -> @displayBuffer.getMarkers() @@ -1721,11 +1720,11 @@ class TextEditor extends Model # # This API is experimental and subject to change on any release. # - # Returns a {TextEditorMarkerLayer}. + # Returns a {DisplayMarkerLayer}. addMarkerLayer: (options) -> @displayBuffer.addMarkerLayer(options) - # Public: *Experimental:* Get a {TextEditorMarkerLayer} by id. + # Public: *Experimental:* Get a {DisplayMarkerLayer} by id. # # * `id` The id of the marker layer to retrieve. # @@ -1736,14 +1735,14 @@ class TextEditor extends Model getMarkerLayer: (id) -> @displayBuffer.getMarkerLayer(id) - # Public: *Experimental:* Get the default {TextEditorMarkerLayer}. + # Public: *Experimental:* Get the default {DisplayMarkerLayer}. # # All marker APIs not tied to an explicit layer interact with this default # layer. # # This API is experimental and subject to change on any release. # - # Returns a {TextEditorMarkerLayer}. + # Returns a {DisplayMarkerLayer}. getDefaultMarkerLayer: -> @displayBuffer.getDefaultMarkerLayer() @@ -1950,7 +1949,7 @@ class TextEditor extends Model getCursorsOrderedByBufferPosition: -> @getCursors().sort (a, b) -> a.compare(b) - # Add a cursor based on the given {TextEditorMarker}. + # Add a cursor based on the given {DisplayMarker}. addCursor: (marker) -> cursor = new Cursor(editor: this, marker: marker, config: @config) @cursors.push(cursor) @@ -2299,7 +2298,7 @@ class TextEditor extends Model # Extended: Select the range of the given marker if it is valid. # - # * `marker` A {TextEditorMarker} + # * `marker` A {DisplayMarker} # # Returns the selected {Range} or `undefined` if the marker is invalid. selectMarker: (marker) -> @@ -2425,9 +2424,9 @@ class TextEditor extends Model _.reduce(tail, reducer, [head]) return result if fn? - # Add a {Selection} based on the given {TextEditorMarker}. + # Add a {Selection} based on the given {DisplayMarker}. # - # * `marker` The {TextEditorMarker} to highlight + # * `marker` The {DisplayMarker} to highlight # * `options` (optional) An {Object} that pertains to the {Selection} constructor. # # Returns the new {Selection}. From 972fda4ef7280423b5537e4910c53f16bcc108d4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 31 Dec 2015 15:23:05 -0700 Subject: [PATCH 062/971] Start using DisplayLayer for folds --- spec/display-buffer-spec.coffee | 78 +++++++------- spec/text-editor-spec.coffee | 58 +++++------ src/display-buffer.coffee | 177 ++++++++------------------------ src/fold.coffee | 83 --------------- src/language-mode.coffee | 12 +-- src/selection.coffee | 14 +-- src/text-editor.coffee | 53 +++------- 7 files changed, 133 insertions(+), 342 deletions(-) delete mode 100644 src/fold.coffee diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index 0246008a4..48103f3a7 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -26,7 +26,7 @@ describe "DisplayBuffer", -> marker1 = displayBuffer.markBufferRange([[1, 2], [3, 4]], id: 1) marker2 = displayBuffer.markBufferRange([[2, 3], [4, 5]], reversed: true, id: 2) marker3 = displayBuffer.markBufferPosition([5, 6], id: 3) - displayBuffer.createFold(3, 5) + displayBuffer.foldBufferRowRange(3, 5) displayBuffer2 = displayBuffer.copy() expect(displayBuffer2.id).not.toBe displayBuffer.id @@ -361,7 +361,7 @@ describe "DisplayBuffer", -> describe "when folds are created and destroyed", -> describe "when a fold spans multiple lines", -> it "replaces the lines spanned by the fold with a placeholder that references the fold object", -> - fold = displayBuffer.createFold(4, 7) + fold = displayBuffer.foldBufferRowRange(4, 7) expect(fold).toBeDefined() [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) @@ -382,7 +382,7 @@ describe "DisplayBuffer", -> describe "when a fold spans a single line", -> it "renders a fold placeholder for the folded line but does not skip any lines", -> - fold = displayBuffer.createFold(4, 4) + fold = displayBuffer.foldBufferRowRange(4, 4) [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) expect(line4.fold).toBe fold @@ -406,8 +406,8 @@ describe "DisplayBuffer", -> describe "when a fold is nested within another fold", -> it "does not render the placeholder for the inner fold until the outer fold is destroyed", -> - innerFold = displayBuffer.createFold(6, 7) - outerFold = displayBuffer.createFold(4, 8) + innerFold = displayBuffer.foldBufferRowRange(6, 7) + outerFold = displayBuffer.foldBufferRowRange(4, 8) [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) expect(line4.fold).toBe outerFold @@ -424,8 +424,8 @@ describe "DisplayBuffer", -> expect(line7.text).toBe '8' it "allows the outer fold to start at the same location as the inner fold", -> - innerFold = displayBuffer.createFold(4, 6) - outerFold = displayBuffer.createFold(4, 8) + innerFold = displayBuffer.foldBufferRowRange(4, 6) + outerFold = displayBuffer.foldBufferRowRange(4, 8) [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) expect(line4.fold).toBe outerFold @@ -434,19 +434,19 @@ describe "DisplayBuffer", -> describe "when creating a fold where one already exists", -> it "returns existing fold and does't create new fold", -> - fold = displayBuffer.createFold(0, 10) + fold = displayBuffer.foldBufferRowRange(0, 10) expect(displayBuffer.foldsMarkerLayer.getMarkers().length).toBe 1 - newFold = displayBuffer.createFold(0, 10) + newFold = displayBuffer.foldBufferRowRange(0, 10) expect(newFold).toBe fold expect(displayBuffer.foldsMarkerLayer.getMarkers().length).toBe 1 describe "when a fold is created inside an existing folded region", -> it "creates/destroys the fold, but does not trigger change event", -> - outerFold = displayBuffer.createFold(0, 10) + outerFold = displayBuffer.foldBufferRowRange(0, 10) changeHandler.reset() - innerFold = displayBuffer.createFold(2, 5) + innerFold = displayBuffer.foldBufferRowRange(2, 5) expect(changeHandler).not.toHaveBeenCalled() [line0, line1] = displayBuffer.tokenizedLinesForScreenRows(0, 1) expect(line0.fold).toBe outerFold @@ -461,8 +461,8 @@ describe "DisplayBuffer", -> describe "when a fold ends where another fold begins", -> it "continues to hide the lines inside the second fold", -> - fold2 = displayBuffer.createFold(4, 9) - fold1 = displayBuffer.createFold(0, 4) + fold2 = displayBuffer.foldBufferRowRange(4, 9) + fold1 = displayBuffer.foldBufferRowRange(0, 4) expect(displayBuffer.tokenizedLineForScreenRow(0).text).toMatch /^0/ expect(displayBuffer.tokenizedLineForScreenRow(1).text).toMatch /^10/ @@ -473,9 +473,9 @@ describe "DisplayBuffer", -> buffer, tabLength, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: -> }) - otherDisplayBuffer.createFold(1, 5) + otherDisplayBuffer.foldBufferRowRange(1, 5) - displayBuffer.createFold(2, 4) + displayBuffer.foldBufferRowRange(2, 4) expect(otherDisplayBuffer.foldsStartingAtBufferRow(2).length).toBe 0 expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2' @@ -484,8 +484,8 @@ describe "DisplayBuffer", -> describe "when the buffer changes", -> [fold1, fold2] = [] beforeEach -> - fold1 = displayBuffer.createFold(2, 4) - fold2 = displayBuffer.createFold(6, 8) + fold1 = displayBuffer.foldBufferRowRange(2, 4) + fold2 = displayBuffer.foldBufferRowRange(6, 8) changeHandler.reset() describe "when the old range surrounds a fold", -> @@ -509,7 +509,7 @@ describe "DisplayBuffer", -> describe "when the old range surrounds two nested folds", -> it "removes both folds and replaces the selection with the new text", -> - displayBuffer.createFold(2, 9) + displayBuffer.foldBufferRowRange(2, 9) changeHandler.reset() buffer.setTextInRange([[1, 0], [10, 0]], 'goodbye') @@ -634,7 +634,7 @@ describe "DisplayBuffer", -> describe "position translation", -> it "translates positions to account for folded lines and characters and the placeholder", -> - fold = displayBuffer.createFold(4, 7) + fold = displayBuffer.foldBufferRowRange(4, 7) # preceding fold: identity expect(displayBuffer.screenPositionForBufferPosition([3, 0])).toEqual [3, 0] @@ -669,11 +669,11 @@ describe "DisplayBuffer", -> describe ".unfoldBufferRow(row)", -> it "destroys all folds containing the given row", -> - displayBuffer.createFold(2, 4) - displayBuffer.createFold(2, 6) - displayBuffer.createFold(7, 8) - displayBuffer.createFold(1, 9) - displayBuffer.createFold(11, 12) + displayBuffer.foldBufferRowRange(2, 4) + displayBuffer.foldBufferRowRange(2, 6) + displayBuffer.foldBufferRowRange(7, 8) + displayBuffer.foldBufferRowRange(1, 9) + displayBuffer.foldBufferRowRange(11, 12) expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe '1' expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '10' @@ -687,11 +687,11 @@ describe "DisplayBuffer", -> describe ".outermostFoldsInBufferRowRange(startRow, endRow)", -> it "returns the outermost folds entirely contained in the given row range, exclusive of end row", -> - fold1 = displayBuffer.createFold(4, 7) - fold2 = displayBuffer.createFold(5, 6) - fold3 = displayBuffer.createFold(11, 15) - fold4 = displayBuffer.createFold(12, 13) - fold5 = displayBuffer.createFold(16, 17) + fold1 = displayBuffer.foldBufferRowRange(4, 7) + fold2 = displayBuffer.foldBufferRowRange(5, 6) + fold3 = displayBuffer.foldBufferRowRange(11, 15) + fold4 = displayBuffer.foldBufferRowRange(12, 13) + fold5 = displayBuffer.foldBufferRowRange(16, 17) expect(displayBuffer.outermostFoldsInBufferRowRange(3, 18)).toEqual [fold1, fold3, fold5] expect(displayBuffer.outermostFoldsInBufferRowRange(5, 16)).toEqual [fold3] @@ -731,7 +731,7 @@ describe "DisplayBuffer", -> expect(displayBuffer.clipScreenPosition([0, 1000], wrapBeyondNewlines: true)).toEqual [1, 0] it "wraps positions in the middle of fold lines to the next screen line", -> - displayBuffer.createFold(3, 5) + displayBuffer.foldBufferRowRange(3, 5) expect(displayBuffer.clipScreenPosition([3, 5], wrapBeyondNewlines: true)).toEqual [4, 0] describe "when skipSoftWrapIndentation is false (the default)", -> @@ -850,7 +850,7 @@ describe "DisplayBuffer", -> describe "markers", -> beforeEach -> - displayBuffer.createFold(4, 7) + displayBuffer.foldBufferRowRange(4, 7) describe "marker creation and manipulation", -> it "allows markers to be created in terms of both screen and buffer coordinates", -> @@ -947,7 +947,7 @@ describe "DisplayBuffer", -> } markerChangedHandler.reset() - displayBuffer.createFold(4, 7) + displayBuffer.foldBufferRowRange(4, 7) expect(markerChangedHandler).toHaveBeenCalled() expect(markerChangedHandler.argsForCall[0][0]).toEqual { oldHeadScreenPosition: [11, 23] @@ -1028,7 +1028,7 @@ describe "DisplayBuffer", -> } it "does not call the callback for screen changes that don't change the position of the marker", -> - displayBuffer.createFold(10, 11) + displayBuffer.foldBufferRowRange(10, 11) expect(markerChangedHandler).not.toHaveBeenCalled() it "updates markers before emitting buffer change events, but does not notify their observers until the change event", -> @@ -1151,37 +1151,37 @@ describe "DisplayBuffer", -> it "allows the startScreenRow and endScreenRow to be specified", -> marker1 = displayBuffer.markBufferRange([[6, 0], [7, 0]], class: 'a') marker2 = displayBuffer.markBufferRange([[9, 0], [10, 0]], class: 'a') - displayBuffer.createFold(4, 7) + displayBuffer.foldBufferRowRange(4, 7) expect(displayBuffer.findMarkers(class: 'a', startScreenRow: 6, endScreenRow: 7)).toEqual [marker2] it "allows intersectsBufferRowRange to be specified", -> marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a') marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a') - displayBuffer.createFold(4, 7) + displayBuffer.foldBufferRowRange(4, 7) expect(displayBuffer.findMarkers(class: 'a', intersectsBufferRowRange: [5, 6])).toEqual [marker1] it "allows intersectsScreenRowRange to be specified", -> marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a') marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a') - displayBuffer.createFold(4, 7) + displayBuffer.foldBufferRowRange(4, 7) expect(displayBuffer.findMarkers(class: 'a', intersectsScreenRowRange: [5, 10])).toEqual [marker2] it "allows containedInScreenRange to be specified", -> marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a') marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a') - displayBuffer.createFold(4, 7) + displayBuffer.foldBufferRowRange(4, 7) expect(displayBuffer.findMarkers(class: 'a', containedInScreenRange: [[5, 0], [7, 0]])).toEqual [marker2] it "allows intersectsBufferRange to be specified", -> marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a') marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a') - displayBuffer.createFold(4, 7) + displayBuffer.foldBufferRowRange(4, 7) expect(displayBuffer.findMarkers(class: 'a', intersectsBufferRange: [[5, 0], [6, 0]])).toEqual [marker1] it "allows intersectsScreenRange to be specified", -> marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a') marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a') - displayBuffer.createFold(4, 7) + displayBuffer.foldBufferRowRange(4, 7) expect(displayBuffer.findMarkers(class: 'a', intersectsScreenRange: [[5, 0], [10, 0]])).toEqual [marker2] describe "marker destruction", -> diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index ce84d2c50..76fd1a979 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -299,7 +299,7 @@ describe "TextEditor", -> editor.setSoftWrapped(true) editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(50) - editor.createFold(2, 3) + editor.foldBufferRowRange(2, 3) it "positions the cursor at the buffer position that corresponds to the given screen position", -> editor.setCursorScreenPosition([9, 0]) @@ -1787,10 +1787,10 @@ describe "TextEditor", -> describe "when the 'preserveFolds' option is false (the default)", -> it "removes folds that contain the selections", -> editor.setSelectedBufferRange([[0, 0], [0, 0]]) - editor.createFold(1, 4) - editor.createFold(2, 3) - editor.createFold(6, 8) - editor.createFold(10, 11) + editor.foldBufferRowRange(1, 4) + editor.foldBufferRowRange(2, 3) + editor.foldBufferRowRange(6, 8) + editor.foldBufferRowRange(10, 11) editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[6, 6], [7, 7]]]) expect(editor.tokenizedLineForScreenRow(1).fold).toBeUndefined() @@ -1801,8 +1801,8 @@ describe "TextEditor", -> describe "when the 'preserveFolds' option is true", -> it "does not remove folds that contain the selections", -> editor.setSelectedBufferRange([[0, 0], [0, 0]]) - editor.createFold(1, 4) - editor.createFold(6, 8) + editor.foldBufferRowRange(1, 4) + editor.foldBufferRowRange(6, 8) editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[6, 0], [6, 1]]], preserveFolds: true) expect(editor.isFoldedAtBufferRow(1)).toBeTruthy() expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() @@ -2203,7 +2203,7 @@ describe "TextEditor", -> it "moves the line to the previous row without breaking the fold", -> expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) editor.setSelectedBufferRange([[4, 2], [4, 9]], preserveFolds: true) expect(editor.getSelectedBufferRange()).toEqual [[4, 2], [4, 9]] @@ -2231,7 +2231,7 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));" expect(editor.lineTextForBufferRow(9)).toBe " };" - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() @@ -2269,7 +2269,7 @@ describe "TextEditor", -> it "moves the lines to the previous row without breaking the fold", -> expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) editor.setSelectedBufferRange([[3, 2], [4, 9]], preserveFolds: true) expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() @@ -2297,7 +2297,7 @@ describe "TextEditor", -> it "moves the lines to the previous row without breaking the fold", -> expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) editor.setSelectedBufferRange([[4, 2], [8, 9]], preserveFolds: true) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() @@ -2341,7 +2341,7 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));" expect(editor.lineTextForBufferRow(9)).toBe " };" - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() @@ -2381,7 +2381,7 @@ describe "TextEditor", -> it "moves the lines to the previous row without breaking the fold", -> expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) editor.setSelectedBufferRanges([ [[2, 2], [2, 9]], [[4, 2], [4, 9]] @@ -2419,7 +2419,7 @@ describe "TextEditor", -> describe "when there is a fold", -> it "moves all lines that spanned by a selection to preceding row, preserving all folds", -> - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() @@ -2446,8 +2446,8 @@ describe "TextEditor", -> describe 'and many selections intersects folded rows', -> it 'moves and preserves all the folds', -> - editor.createFold(2, 4) - editor.createFold(7, 9) + editor.foldBufferRowRange(2, 4) + editor.foldBufferRowRange(7, 9) editor.setSelectedBufferRanges([ [[1, 0], [5, 4]], @@ -2531,7 +2531,7 @@ describe "TextEditor", -> it "moves the line to the following row without breaking the fold", -> expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) editor.setSelectedBufferRange([[4, 2], [4, 9]], preserveFolds: true) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() @@ -2557,7 +2557,7 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() @@ -2611,7 +2611,7 @@ describe "TextEditor", -> it "moves the lines to the following row without breaking the fold", -> expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) editor.setSelectedBufferRange([[3, 2], [4, 9]], preserveFolds: true) expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() @@ -2639,7 +2639,7 @@ describe "TextEditor", -> it "moves the lines to the following row without breaking the fold", -> expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) editor.setSelectedBufferRange([[4, 2], [8, 9]], preserveFolds: true) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() @@ -2669,7 +2669,7 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() @@ -2711,8 +2711,8 @@ describe "TextEditor", -> describe 'and many selections intersects folded rows', -> it 'moves and preserves all the folds', -> - editor.createFold(2, 4) - editor.createFold(7, 9) + editor.foldBufferRowRange(2, 4) + editor.foldBufferRowRange(7, 9) editor.setSelectedBufferRanges([ [[2, 0], [2, 4]], @@ -2741,7 +2741,7 @@ describe "TextEditor", -> describe "when there is a fold below one of the selected row", -> it "moves all lines spanned by a selection to the following row, preserving the fold", -> - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() @@ -2764,7 +2764,7 @@ describe "TextEditor", -> describe "when there is a fold below a group of multiple selections without any lines with no selection in-between", -> it "moves all the lines below the fold, preserving the fold", -> - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() @@ -2789,7 +2789,7 @@ describe "TextEditor", -> it "moves the lines to the previous row without breaking the fold", -> expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" - editor.createFold(4, 7) + editor.foldBufferRowRange(4, 7) editor.setSelectedBufferRanges([ [[2, 2], [2, 9]], [[4, 2], [4, 9]] @@ -2927,7 +2927,7 @@ describe "TextEditor", -> describe "when there is a selection that ends on a folded line", -> it "destroys the selection", -> - editor.createFold(2, 4) + editor.foldBufferRowRange(2, 4) editor.setSelectedBufferRange([[1, 0], [2, 0]]) editor.insertText('holy cow') expect(editor.tokenizedLineForScreenRow(2).fold).toBeUndefined() @@ -3519,8 +3519,8 @@ describe "TextEditor", -> describe "when the cursor is on a folded line", -> it "removes the lines contained by the fold", -> editor.setSelectedBufferRange([[2, 0], [2, 0]]) - editor.createFold(2, 4) - editor.createFold(2, 6) + editor.foldBufferRowRange(2, 4) + editor.foldBufferRowRange(2, 6) oldLine7 = buffer.lineForRow(7) oldLine8 = buffer.lineForRow(8) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 1f8c3cc26..5cae0a23a 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -3,7 +3,6 @@ _ = require 'underscore-plus' {Point, Range} = require 'text-buffer' TokenizedBuffer = require './tokenized-buffer' RowMap = require './row-map' -Fold = require './fold' Model = require './model' Token = require './token' Decoration = require './decoration' @@ -30,7 +29,6 @@ class DisplayBuffer extends Model @deserialize: (state, atomEnvironment) -> state.tokenizedBuffer = TokenizedBuffer.deserialize(state.tokenizedBuffer, atomEnvironment) - state.foldsMarkerLayer = state.tokenizedBuffer.buffer.getMarkerLayer(state.foldsMarkerLayerId) state.config = atomEnvironment.config state.assert = atomEnvironment.assert state.grammarRegistry = atomEnvironment.grammars @@ -41,8 +39,8 @@ class DisplayBuffer extends Model super { - tabLength, @editorWidthInChars, @tokenizedBuffer, @foldsMarkerLayer, buffer, - ignoreInvisibles, @largeFileMode, @config, @assert, @grammarRegistry, @packageManager + tabLength, @editorWidthInChars, @tokenizedBuffer, buffer, ignoreInvisibles, + @largeFileMode, @config, @assert, @grammarRegistry, @packageManager } = params @emitter = new Emitter @@ -56,7 +54,6 @@ class DisplayBuffer extends Model @displayLayer = @buffer.addDisplayLayer({tabLength: @getTabLength()}) @charWidthsByScope = {} @defaultMarkerLayer = @displayLayer.addMarkerLayer() - @foldsByMarkerId = {} @decorationsById = {} @decorationsByMarkerId = {} @overlayDecorationsById = {} @@ -68,10 +65,7 @@ class DisplayBuffer extends Model @disposables.add @tokenizedBuffer.onDidChange @handleTokenizedBufferChange @disposables.add @buffer.onDidCreateMarker @didCreateDefaultLayerMarker - @foldsMarkerLayer ?= @buffer.addMarkerLayer() - folds = (new Fold(this, marker) for marker in @foldsMarkerLayer.getMarkers()) @updateAllScreenLines() - @decorateFold(fold) for fold in folds subscribeToScopedConfigSettings: => @scopedConfigSubscriptions?.dispose() @@ -115,13 +109,11 @@ class DisplayBuffer extends Model editorWidthInChars: @editorWidthInChars tokenizedBuffer: @tokenizedBuffer.serialize() largeFileMode: @largeFileMode - foldsMarkerLayerId: @foldsMarkerLayer.id copy: -> - foldsMarkerLayer = @foldsMarkerLayer.copy() new DisplayBuffer({ @buffer, tabLength: @getTabLength(), @largeFileMode, @config, @assert, - @grammarRegistry, @packageManager, foldsMarkerLayer + @grammarRegistry, @packageManager }) updateAllScreenLines: -> @@ -395,75 +387,20 @@ class DisplayBuffer extends Model # endRow - The row {Number} to end the fold # # Returns the new {Fold}. - createFold: (startRow, endRow) -> - unless @largeFileMode - if foldMarker = @findFoldMarker({startRow, endRow}) - @foldForMarker(foldMarker) - else - foldMarker = @foldsMarkerLayer.markRange([[startRow, 0], [endRow, Infinity]]) - fold = new Fold(this, foldMarker) - fold.updateDisplayBuffer() - @decorateFold(fold) - fold + foldBufferRowRange: (startRow, endRow) -> + @displayLayer.foldBufferRange(Range(Point(startRow, Infinity), Point(endRow, Infinity))) isFoldedAtBufferRow: (bufferRow) -> - @largestFoldContainingBufferRow(bufferRow)? + @displayLayer.foldsIntersectingBufferRange(Range(Point(bufferRow, 0), Point(bufferRow, Infinity))).length > 0 isFoldedAtScreenRow: (screenRow) -> - @largestFoldContainingBufferRow(@bufferRowForScreenRow(screenRow))? - - # Destroys the fold with the given id - destroyFoldWithId: (id) -> - @foldsByMarkerId[id]?.destroy() + @isFoldedAtBufferRow(@bufferRowForScreenRow(screenRow))? # Removes any folds found that contain the given buffer row. # # bufferRow - The buffer row {Number} to check against unfoldBufferRow: (bufferRow) -> - fold.destroy() for fold in @foldsContainingBufferRow(bufferRow) - return - - # Given a buffer row, this returns the largest fold that starts there. - # - # Largest is defined as the fold whose difference between its start and end points - # are the greatest. - # - # bufferRow - A {Number} indicating the buffer row - # - # Returns a {Fold} or null if none exists. - largestFoldStartingAtBufferRow: (bufferRow) -> - @foldsStartingAtBufferRow(bufferRow)[0] - - # Public: Given a buffer row, this returns all folds that start there. - # - # bufferRow - A {Number} indicating the buffer row - # - # Returns an {Array} of {Fold}s. - foldsStartingAtBufferRow: (bufferRow) -> - for marker in @findFoldMarkers(startRow: bufferRow) - @foldForMarker(marker) - - # Given a screen row, this returns the largest fold that starts there. - # - # Largest is defined as the fold whose difference between its start and end points - # are the greatest. - # - # screenRow - A {Number} indicating the screen row - # - # Returns a {Fold}. - largestFoldStartingAtScreenRow: (screenRow) -> - @largestFoldStartingAtBufferRow(@bufferRowForScreenRow(screenRow)) - - # Given a buffer row, this returns the largest fold that includes it. - # - # Largest is defined as the fold whose difference between its start and end rows - # is the greatest. - # - # bufferRow - A {Number} indicating the buffer row - # - # Returns a {Fold}. - largestFoldContainingBufferRow: (bufferRow) -> - @foldsContainingBufferRow(bufferRow)[0] + @displayLayer.destroyFoldsIntersectingBufferRange(Range(Point(bufferRow, 0), Point(bufferRow, Infinity))) # Returns the folds in the given row range (exclusive of end row) that are # not contained by any other folds. @@ -480,20 +417,6 @@ class DisplayBuffer extends Model folds - # Returns all the folds that intersect the given row range. - foldsIntersectingBufferRowRange: (startRow, endRow) -> - @foldForMarker(marker) for marker in @findFoldMarkers(intersectsRowRange: [startRow, endRow]) - - # Public: Given a buffer row, this returns folds that include it. - # - # - # bufferRow - A {Number} indicating the buffer row - # - # Returns an {Array} of {Fold}s. - foldsContainingBufferRow: (bufferRow) -> - for marker in @findFoldMarkers(intersectsRow: bufferRow) - @foldForMarker(marker) - # Given a buffer row, this converts it into a screen row. # # bufferRow - A {Number} representing a buffer row @@ -548,10 +471,7 @@ class DisplayBuffer extends Model # # Returns a {Number}. getLineCount: -> - if @largeFileMode - @tokenizedBuffer.getLineCount() - else - @screenLines.length + @displayLayer.getScreenLineCount() # Gets the number of the last screen line. # @@ -591,7 +511,6 @@ class DisplayBuffer extends Model unless screenLine? throw new BufferToScreenConversionError "No screen line exists when converting buffer row to screen row", softWrapEnabled: @isSoftWrapped() - foldCount: @findFoldMarkers().length lastBufferRow: @buffer.getLastRow() lastScreenRow: @getLastRow() bufferRow: row @@ -924,12 +843,6 @@ class DisplayBuffer extends Model getDefaultMarkerLayer: -> @defaultMarkerLayer - findFoldMarker: (params) -> - @findFoldMarkers(params)[0] - - findFoldMarkers: (params) -> - @foldsMarkerLayer.findMarkers(params) - refreshMarkerScreenPositions: -> @defaultMarkerLayer.refreshMarkerScreenPositions() layer.refreshMarkerScreenPositions() for id, layer of @customMarkerLayersById @@ -937,7 +850,6 @@ class DisplayBuffer extends Model destroyed: -> @defaultMarkerLayer.destroy() - @foldsMarkerLayer.destroy() @scopedConfigSubscriptions.dispose() @disposables.dispose() @tokenizedBuffer.destroy() @@ -990,49 +902,49 @@ class DisplayBuffer extends Model rectangularRegion = null foldsByStartRow = {} - for fold in @outermostFoldsInBufferRowRange(startBufferRow, endBufferRow) - foldsByStartRow[fold.getStartRow()] = fold + # for fold in @outermostFoldsInBufferRowRange(startBufferRow, endBufferRow) + # foldsByStartRow[fold.getStartRow()] = fold bufferRow = startBufferRow while bufferRow < endBufferRow tokenizedLine = @tokenizedBuffer.tokenizedLineForRow(bufferRow) - if fold = foldsByStartRow[bufferRow] - foldLine = tokenizedLine.copy() - foldLine.fold = fold - screenLines.push(foldLine) + # if fold = foldsByStartRow[bufferRow] + # foldLine = tokenizedLine.copy() + # foldLine.fold = fold + # screenLines.push(foldLine) + # + # if rectangularRegion? + # regions.push(rectangularRegion) + # rectangularRegion = null + # + # foldedRowCount = fold.getBufferRowCount() + # regions.push(bufferRows: foldedRowCount, screenRows: 1) + # bufferRow += foldedRowCount + # else + softWraps = 0 + if @isSoftWrapped() + while wrapScreenColumn = tokenizedLine.findWrapColumn(@getSoftWrapColumnForTokenizedLine(tokenizedLine)) + [wrappedLine, tokenizedLine] = tokenizedLine.softWrapAt( + wrapScreenColumn, + @configSettings.softWrapHangingIndent + ) + break if wrappedLine.hasOnlySoftWrapIndentation() + screenLines.push(wrappedLine) + softWraps++ + screenLines.push(tokenizedLine) + if softWraps > 0 if rectangularRegion? regions.push(rectangularRegion) rectangularRegion = null - - foldedRowCount = fold.getBufferRowCount() - regions.push(bufferRows: foldedRowCount, screenRows: 1) - bufferRow += foldedRowCount + regions.push(bufferRows: 1, screenRows: softWraps + 1) else - softWraps = 0 - if @isSoftWrapped() - while wrapScreenColumn = tokenizedLine.findWrapColumn(@getSoftWrapColumnForTokenizedLine(tokenizedLine)) - [wrappedLine, tokenizedLine] = tokenizedLine.softWrapAt( - wrapScreenColumn, - @configSettings.softWrapHangingIndent - ) - break if wrappedLine.hasOnlySoftWrapIndentation() - screenLines.push(wrappedLine) - softWraps++ - screenLines.push(tokenizedLine) + rectangularRegion ?= {bufferRows: 0, screenRows: 0} + rectangularRegion.bufferRows++ + rectangularRegion.screenRows++ - if softWraps > 0 - if rectangularRegion? - regions.push(rectangularRegion) - rectangularRegion = null - regions.push(bufferRows: 1, screenRows: softWraps + 1) - else - rectangularRegion ?= {bufferRows: 0, screenRows: 0} - rectangularRegion.bufferRows++ - rectangularRegion.screenRows++ - - bufferRow++ + bufferRow++ if rectangularRegion? regions.push(rectangularRegion) @@ -1076,12 +988,6 @@ class DisplayBuffer extends Model @didUpdateDecorationsEventScheduled = false @emitter.emit 'did-update-decorations' - decorateFold: (fold) -> - @decorateMarker(fold.marker, type: 'line-number', class: 'folded') - - foldForMarker: (marker) -> - @foldsByMarkerId[marker.id] - decorationDidChangeType: (decoration) -> if decoration.isType('overlay') @overlayDecorationsById[decoration.id] = decoration @@ -1126,7 +1032,6 @@ class DisplayBuffer extends Model checkScreenLinesInvariant: -> return if @isSoftWrapped() - return if _.size(@foldsByMarkerId) > 0 screenLinesCount = @screenLines.length tokenizedLinesCount = @tokenizedBuffer.getLineCount() diff --git a/src/fold.coffee b/src/fold.coffee deleted file mode 100644 index 051be9f4c..000000000 --- a/src/fold.coffee +++ /dev/null @@ -1,83 +0,0 @@ -{Point, Range} = require 'text-buffer' - -# Represents a fold that collapses multiple buffer lines into a single -# line on the screen. -# -# Their creation is managed by the {DisplayBuffer}. -module.exports = -class Fold - id: null - displayBuffer: null - marker: null - - constructor: (@displayBuffer, @marker) -> - @id = @marker.id - @displayBuffer.foldsByMarkerId[@marker.id] = this - @marker.onDidDestroy => @destroyed() - @marker.onDidChange ({isValid}) => @destroy() unless isValid - - # Returns whether this fold is contained within another fold - isInsideLargerFold: -> - largestContainingFoldMarker = @displayBuffer.findFoldMarker(containsRange: @getBufferRange()) - largestContainingFoldMarker and - not largestContainingFoldMarker.getRange().isEqual(@getBufferRange()) - - # Destroys this fold - destroy: -> - @marker.destroy() - - # Returns the fold's {Range} in buffer coordinates - # - # includeNewline - A {Boolean} which, if `true`, includes the trailing newline - # - # Returns a {Range}. - getBufferRange: ({includeNewline}={}) -> - range = @marker.getRange() - - if range.end.row > range.start.row and nextFold = @displayBuffer.largestFoldStartingAtBufferRow(range.end.row) - nextRange = nextFold.getBufferRange() - range = new Range(range.start, nextRange.end) - - if includeNewline - range = range.copy() - range.end.row++ - range.end.column = 0 - range - - getBufferRowRange: -> - {start, end} = @getBufferRange() - [start.row, end.row] - - # Returns the fold's start row as a {Number}. - getStartRow: -> - @getBufferRange().start.row - - # Returns the fold's end row as a {Number}. - getEndRow: -> - @getBufferRange().end.row - - # Returns a {String} representation of the fold. - inspect: -> - "Fold(#{@getStartRow()}, #{@getEndRow()})" - - # Retrieves the number of buffer rows spanned by the fold. - # - # Returns a {Number}. - getBufferRowCount: -> - @getEndRow() - @getStartRow() + 1 - - # Identifies if a fold is nested within a fold. - # - # fold - A {Fold} to check - # - # Returns a {Boolean}. - isContainedByFold: (fold) -> - @isContainedByRange(fold.getBufferRange()) - - updateDisplayBuffer: -> - unless @isInsideLargerFold() - @displayBuffer.updateScreenLines(@getStartRow(), @getEndRow() + 1, 0, updateMarkers: true) - - destroyed: -> - delete @displayBuffer.foldsByMarkerId[@marker.id] - @updateDisplayBuffer() diff --git a/src/language-mode.coffee b/src/language-mode.coffee index ac8a5af76..b58744558 100644 --- a/src/language-mode.coffee +++ b/src/language-mode.coffee @@ -92,14 +92,12 @@ class LanguageMode for currentRow in [0..@buffer.getLastRow()] by 1 [startRow, endRow] = @rowRangeForFoldAtBufferRow(currentRow) ? [] continue unless startRow? - @editor.createFold(startRow, endRow) + @editor.foldBufferRowRange(startRow, endRow) return # Unfolds all the foldable lines in the buffer. unfoldAll: -> - for fold in @editor.displayBuffer.foldsIntersectingBufferRowRange(0, @buffer.getLastRow()) by -1 - fold.destroy() - return + @editor.displayLayer.destroyAllFolds() # Fold all comment and code blocks at a given indentLevel # @@ -112,7 +110,7 @@ class LanguageMode # assumption: startRow will always be the min indent level for the entire range if @editor.indentationForBufferRow(startRow) is indentLevel - @editor.createFold(startRow, endRow) + @editor.foldBufferRowRange(startRow, endRow) return # Given a buffer row, creates a fold at it. @@ -124,8 +122,8 @@ class LanguageMode for currentRow in [bufferRow..0] by -1 [startRow, endRow] = @rowRangeForFoldAtBufferRow(currentRow) ? [] continue unless startRow? and startRow <= bufferRow <= endRow - fold = @editor.displayBuffer.largestFoldStartingAtBufferRow(startRow) - return @editor.createFold(startRow, endRow) unless fold + unless @editor.displayBuffer.isFoldedAtBufferRow(startRow) + return @editor.foldBufferRowRange(startRow, endRow) # Find the row range for a fold at a given bufferRow. Will handle comments # and code. diff --git a/src/selection.coffee b/src/selection.coffee index 2ba66ebb0..7afa10767 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -87,7 +87,7 @@ class Selection extends Model setBufferRange: (bufferRange, options={}) -> bufferRange = Range.fromObject(bufferRange) options.reversed ?= @isReversed() - @editor.destroyFoldsContainingBufferRange(bufferRange) unless options.preserveFolds + @editor.destroyFoldsIntersectingBufferRange(bufferRange) unless options.preserveFolds @modifySelection => needsFlash = options.flash delete options.flash if options.flash? @@ -410,7 +410,7 @@ class Selection extends Model # Public: Removes the first character before the selection if the selection # is empty otherwise it deletes the selection. backspace: -> - @selectLeft() if @isEmpty() and not @editor.isFoldedAtScreenRow(@cursor.getScreenRow()) + @selectLeft() if @isEmpty() @deleteSelectedText() # Public: Removes the selection or, if nothing is selected, then all @@ -445,11 +445,7 @@ class Selection extends Model # Public: Removes the selection or the next character after the start of the # selection if the selection is empty. delete: -> - if @isEmpty() - if @cursor.isAtEndOfLine() and fold = @editor.largestFoldStartingAtScreenRow(@cursor.getScreenRow() + 1) - @selectToBufferPosition(fold.getBufferRange().end) - else - @selectRight() + @selectRight() if @isEmpty() @deleteSelectedText() # Public: If the selection is empty, removes all text from the cursor to the @@ -482,8 +478,6 @@ class Selection extends Model # Public: Removes only the selected text. deleteSelectedText: -> bufferRange = @getBufferRange() - if bufferRange.isEmpty() and fold = @editor.largestFoldContainingBufferRow(bufferRange.start.row) - bufferRange = bufferRange.union(fold.getBufferRange(includeNewline: true)) @editor.buffer.delete(bufferRange) unless bufferRange.isEmpty() @cursor?.setBufferPosition(bufferRange.start) @@ -634,7 +628,7 @@ class Selection extends Model # Public: Creates a fold containing the current selection. fold: -> range = @getBufferRange() - @editor.createFold(range.start.row, range.end.row) + @editor.foldBufferRowRange(range.start.row, range.end.row) @cursor.setBufferPosition([range.end.row + 1, 0]) # Private: Increase the indentation level of the given text by given number diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e5757a6e6..ef8265e62 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -948,10 +948,10 @@ class TextEditor extends Model range ).map (range) -> range.translate([-insertDelta, 0]) - # Make sure the inserted text doesn't go into an existing fold - if fold = @displayBuffer.largestFoldStartingAtBufferRow(precedingBufferRow) - rangesToRefold.push(fold.getBufferRange().translate([linesRange.getRowCount() - 1, 0])) - fold.destroy() + # # Make sure the inserted text doesn't go into an existing fold + # if fold = @displayBuffer.largestFoldStartingAtBufferRow(precedingBufferRow) + # rangesToRefold.push(fold.getBufferRange().translate([linesRange.getRowCount() - 1, 0])) + # fold.destroy() # Delete lines spanned by selection and insert them on the preceding buffer row lines = @buffer.getTextInRange(linesRange) @@ -961,7 +961,7 @@ class TextEditor extends Model # Restore folds that existed before the lines were moved for rangeToRefold in rangesToRefold - @displayBuffer.createFold(rangeToRefold.start.row, rangeToRefold.end.row) + @displayBuffer.foldBufferRowRange(rangeToRefold.start.row, rangeToRefold.end.row) for selection in selectionsToMove newSelectionRanges.push(selection.translate([-insertDelta, 0])) @@ -1032,10 +1032,10 @@ class TextEditor extends Model range ).map (range) -> range.translate([insertDelta, 0]) - # Make sure the inserted text doesn't go into an existing fold - if fold = @displayBuffer.largestFoldStartingAtBufferRow(followingBufferRow) - rangesToRefold.push(fold.getBufferRange().translate([insertDelta - 1, 0])) - fold.destroy() + # # Make sure the inserted text doesn't go into an existing fold + # if fold = @displayBuffer.largestFoldStartingAtBufferRow(followingBufferRow) + # rangesToRefold.push(fold.getBufferRange().translate([insertDelta - 1, 0])) + # fold.destroy() # Delete lines spanned by selection and insert them on the following correct buffer row insertPosition = new Point(selection.translate([insertDelta, 0]).start.row, 0) @@ -1048,7 +1048,7 @@ class TextEditor extends Model # Restore folds that existed before the lines were moved for rangeToRefold in rangesToRefold - @displayBuffer.createFold(rangeToRefold.start.row, rangeToRefold.end.row) + @displayBuffer.foldBufferRowRange(rangeToRefold.start.row, rangeToRefold.end.row) for selection in selectionsToMove newSelectionRanges.push(selection.translate([insertDelta, 0])) @@ -1081,7 +1081,7 @@ class TextEditor extends Model delta = endRow - startRow selection.setBufferRange(selectedBufferRange.translate([delta, 0])) for [foldStartRow, foldEndRow] in foldedRowRanges - @createFold(foldStartRow + delta, foldEndRow + delta) + @foldBufferRowRange(foldStartRow + delta, foldEndRow + delta) return replaceSelectedText: (options={}, fn) -> @@ -2432,7 +2432,7 @@ class TextEditor extends Model # Returns the new {Selection}. addSelection: (marker, options={}) -> unless marker.getProperties().preserveFolds - @destroyFoldsContainingBufferRange(marker.getBufferRange()) + @destroyFoldsIntersectingBufferRange(marker.getBufferRange()) cursor = @addCursor(marker) selection = new Selection(_.extend({editor: this, marker, cursor, @clipboard}, options)) @selections.push(selection) @@ -2978,35 +2978,12 @@ class TextEditor extends Model isFoldedAtScreenRow: (screenRow) -> @displayBuffer.isFoldedAtScreenRow(screenRow) - # TODO: Rename to foldRowRange? - createFold: (startRow, endRow) -> - @displayBuffer.createFold(startRow, endRow) - - # {Delegates to: DisplayBuffer.destroyFoldWithId} - destroyFoldWithId: (id) -> - @displayBuffer.destroyFoldWithId(id) + foldBufferRowRange: (startRow, endRow) -> + @displayBuffer.foldBufferRowRange(startRow, endRow) # Remove any {Fold}s found that intersect the given buffer range. destroyFoldsIntersectingBufferRange: (bufferRange) -> - @destroyFoldsContainingBufferRange(bufferRange) - - for row in [bufferRange.end.row..bufferRange.start.row] - fold.destroy() for fold in @displayBuffer.foldsStartingAtBufferRow(row) - - return - - # Remove any {Fold}s found that contain the given buffer range. - destroyFoldsContainingBufferRange: (bufferRange) -> - @unfoldBufferRow(bufferRange.start.row) - @unfoldBufferRow(bufferRange.end.row) - - # {Delegates to: DisplayBuffer.largestFoldContainingBufferRow} - largestFoldContainingBufferRow: (bufferRow) -> - @displayBuffer.largestFoldContainingBufferRow(bufferRow) - - # {Delegates to: DisplayBuffer.largestFoldStartingAtScreenRow} - largestFoldStartingAtScreenRow: (screenRow) -> - @displayBuffer.largestFoldStartingAtScreenRow(screenRow) + @displayLayer.destroyFoldsIntersectingBufferRange(bufferRange) # {Delegates to: DisplayBuffer.outermostFoldsForBufferRowRange} outermostFoldsInBufferRowRange: (startRow, endRow) -> From 0d55a0bd76e1e97b18c96aafeb86646c744bec04 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 13 Jan 2016 17:54:18 -0700 Subject: [PATCH 063/971] Make TokenizedBuffer conform to text decoration layer interface --- spec/tokenized-buffer-spec.coffee | 54 ++++++++++- src/tokenized-buffer-iterator.coffee | 78 +++++++++++++++ src/tokenized-buffer.coffee | 8 ++ src/tokenized-line.coffee | 137 --------------------------- 4 files changed, 139 insertions(+), 138 deletions(-) create mode 100644 src/tokenized-buffer-iterator.coffee diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index 9c53b3c07..4fc0d1618 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -1,5 +1,5 @@ TokenizedBuffer = require '../src/tokenized-buffer' -TextBuffer = require 'text-buffer' +{Point} = TextBuffer = require 'text-buffer' _ = require 'underscore-plus' describe "TokenizedBuffer", -> @@ -1061,3 +1061,55 @@ describe "TokenizedBuffer", -> runs -> expect(coffeeCalled).toBe true + + describe "text decoration layer API", -> + describe "iterator", -> + it "iterates over the syntactic scope boundaries", -> + buffer = new TextBuffer(text: "var foo = 1 /*\nhello*/var bar = 2\n") + tokenizedBuffer = new TokenizedBuffer({ + buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert + }) + tokenizedBuffer.setGrammar(atom.grammars.selectGrammar(".js")) + fullyTokenize(tokenizedBuffer) + + iterator = tokenizedBuffer.buildIterator() + iterator.seek(Point(0, 0)) + + boundaries = [] + loop + boundaries.push({ + position: iterator.getPosition(), + closeTags: iterator.getCloseTags(), + openTags: iterator.getOpenTags() + }) + break unless iterator.moveToSuccessor() + + expect(boundaries).toEqual([ + {position: Point(0, 0), closeTags: [], openTags: ["source.js", "storage.type.var.js"]} + {position: Point(0, 3), closeTags: ["storage.type.var.js"], openTags: []} + {position: Point(0, 8), closeTags: [], openTags: ["keyword.operator.assignment.js"]} + {position: Point(0, 9), closeTags: ["keyword.operator.assignment.js"], openTags: []} + {position: Point(0, 10), closeTags: [], openTags: ["constant.numeric.js"]} + {position: Point(0, 11), closeTags: ["constant.numeric.js"], openTags: []} + {position: Point(0, 12), closeTags: [], openTags: ["comment.block.js", "punctuation.definition.comment.js"]} + {position: Point(0, 14), closeTags: ["punctuation.definition.comment.js"], openTags: []} + {position: Point(1, 5), closeTags: [], openTags: ["punctuation.definition.comment.js"]} + {position: Point(1, 7), closeTags: ["punctuation.definition.comment.js", "comment.block.js"], openTags: ["storage.type.var.js"]} + {position: Point(1, 10), closeTags: ["storage.type.var.js"], openTags: []} + {position: Point(1, 15), closeTags: [], openTags: ["keyword.operator.assignment.js"]} + {position: Point(1, 16), closeTags: ["keyword.operator.assignment.js"], openTags: []} + {position: Point(1, 17), closeTags: [], openTags: ["constant.numeric.js"]} + {position: Point(1, 18), closeTags: ["constant.numeric.js"], openTags: []} + ]) + + expect(iterator.seek(Point(0, 1))).toEqual(["source.js", "storage.type.var.js"]) + expect(iterator.getPosition()).toEqual(Point(0, 3)) + expect(iterator.seek(Point(0, 8))).toEqual(["source.js"]) + expect(iterator.getPosition()).toEqual(Point(0, 8)) + expect(iterator.seek(Point(1, 0))).toEqual(["source.js", "comment.block.js"]) + expect(iterator.getPosition()).toEqual(Point(1, 5)) + expect(iterator.seek(Point(1, 18))).toEqual(["source.js", "constant.numeric.js"]) + expect(iterator.getPosition()).toEqual(Point(1, 18)) + + expect(iterator.seek(Point(2, 0))).toEqual(["source.js"]) + iterator.moveToSuccessor() # ensure we don't infinitely loop (regression test) diff --git a/src/tokenized-buffer-iterator.coffee b/src/tokenized-buffer-iterator.coffee new file mode 100644 index 000000000..acafb8f4b --- /dev/null +++ b/src/tokenized-buffer-iterator.coffee @@ -0,0 +1,78 @@ +{Point} = require 'text-buffer' + +module.exports = +class TokenizedBufferIterator + constructor: (@tokenizedBuffer, @grammarRegistry) -> + @openTags = null + @closeTags = null + + seek: (position) -> + @openTags = [] + @closeTags = [] + @tagIndex = null + + currentLine = @tokenizedBuffer.tokenizedLineForRow(position.row) + containingTags = currentLine.openScopes.map (id) => @grammarRegistry.scopeForId(id) + @currentTags = currentLine.tags + currentColumn = 0 + for tag, index in @currentTags + if tag >= 0 + if currentColumn >= position.column and @isAtTagBoundary() + @tagIndex = index + break + else + currentColumn += tag + containingTags.pop() while @closeTags.shift() + containingTags.push(tag) while tag = @openTags.shift() + else + scopeName = @grammarRegistry.scopeForId(tag) + if tag % 2 is 0 + @closeTags.push(scopeName) + else + @openTags.push(scopeName) + + @tagIndex ?= @currentTags.length + @position = Point(position.row, currentColumn) + containingTags + + moveToSuccessor: -> + if @tagIndex is @currentTags.length + @position = Point(@position.row + 1, 0) + @currentTags = @tokenizedBuffer.tokenizedLineForRow(@position.row)?.tags + return false unless @currentTags? + @tagIndex = 0 + else + @position = Point(@position.row, @position.column + @currentTags[@tagIndex]) + @tagIndex++ + + @openTags = [] + @closeTags = [] + + loop + tag = @currentTags[@tagIndex] + if tag >= 0 or @tagIndex is @currentTags.length + if @isAtTagBoundary() + break + else + return @moveToSuccessor() + else + scopeName = @grammarRegistry.scopeForId(tag) + if tag % 2 is 0 + @closeTags.push(scopeName) + else + @openTags.push(scopeName) + @tagIndex++ + + true + + getPosition: -> + @position + + getCloseTags: -> + @closeTags.slice() + + getOpenTags: -> + @openTags.slice() + + isAtTagBoundary: -> + @closeTags.length > 0 or @openTags.length > 0 diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 0fdf4eea8..0267e02dd 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -7,6 +7,7 @@ TokenizedLine = require './tokenized-line' TokenIterator = require './token-iterator' Token = require './token' ScopeDescriptor = require './scope-descriptor' +TokenizedBufferIterator = require './tokenized-buffer-iterator' module.exports = class TokenizedBuffer extends Model @@ -58,6 +59,12 @@ class TokenizedBuffer extends Model destroyed: -> @disposables.dispose() + buildIterator: -> + new TokenizedBufferIterator(this, @grammarRegistry) + + getInvalidatedRanges: -> + [@invalidatedRange] + serialize: -> state = { deserializer: 'TokenizedBuffer' @@ -274,6 +281,7 @@ class TokenizedBuffer extends Model [start, end] = @updateFoldableStatus(start, end + delta) end -= delta + @invalidatedRange = Range(start, end) event = {start, end, delta, bufferChange: e} @emitter.emit 'did-change', event diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index c97a621ac..e063387eb 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -47,143 +47,6 @@ class TokenizedLine @startBufferColumn ?= 0 @bufferDelta = @text.length - @transformContent() - @buildEndOfLineInvisibles() if @invisibles? and @lineEnding? - - transformContent: -> - text = '' - bufferColumn = 0 - screenColumn = 0 - tokenIndex = 0 - tokenOffset = 0 - firstNonWhitespaceColumn = null - lastNonWhitespaceColumn = null - - substringStart = 0 - substringEnd = 0 - - while bufferColumn < @text.length - # advance to next token if we've iterated over its length - if tokenOffset is @tags[tokenIndex] - tokenIndex++ - tokenOffset = 0 - - # advance to next token tag - tokenIndex++ while @tags[tokenIndex] < 0 - - charCode = @text.charCodeAt(bufferColumn) - - # split out unicode surrogate pairs - if isPairedCharacter(@text, bufferColumn) - prefix = tokenOffset - suffix = @tags[tokenIndex] - tokenOffset - 2 - - i = tokenIndex - @tags.splice(i, 1) - @tags.splice(i++, 0, prefix) if prefix > 0 - @tags.splice(i++, 0, 2) - @tags.splice(i, 0, suffix) if suffix > 0 - - firstNonWhitespaceColumn ?= screenColumn - lastNonWhitespaceColumn = screenColumn + 1 - - substringEnd += 2 - screenColumn += 2 - bufferColumn += 2 - - tokenIndex++ if prefix > 0 - @specialTokens[tokenIndex] = PairedCharacter - tokenIndex++ - tokenOffset = 0 - - # split out leading soft tabs - else if charCode is SpaceCharCode - if firstNonWhitespaceColumn? - substringEnd += 1 - else - if (screenColumn + 1) % @tabLength is 0 - suffix = @tags[tokenIndex] - @tabLength - if suffix >= 0 - @specialTokens[tokenIndex] = SoftTab - @tags.splice(tokenIndex, 1, @tabLength) - @tags.splice(tokenIndex + 1, 0, suffix) if suffix > 0 - - if @invisibles?.space - if substringEnd > substringStart - text += @text.substring(substringStart, substringEnd) - substringStart = substringEnd - text += @invisibles.space - substringStart += 1 - - substringEnd += 1 - - screenColumn++ - bufferColumn++ - tokenOffset++ - - # expand hard tabs to the next tab stop - else if charCode is TabCharCode - if substringEnd > substringStart - text += @text.substring(substringStart, substringEnd) - substringStart = substringEnd - - tabLength = @tabLength - (screenColumn % @tabLength) - if @invisibles?.tab - text += @invisibles.tab - text += getTabString(tabLength - 1) if tabLength > 1 - else - text += getTabString(tabLength) - - substringStart += 1 - substringEnd += 1 - - prefix = tokenOffset - suffix = @tags[tokenIndex] - tokenOffset - 1 - - i = tokenIndex - @tags.splice(i, 1) - @tags.splice(i++, 0, prefix) if prefix > 0 - @tags.splice(i++, 0, tabLength) - @tags.splice(i, 0, suffix) if suffix > 0 - - screenColumn += tabLength - bufferColumn++ - - tokenIndex++ if prefix > 0 - @specialTokens[tokenIndex] = HardTab - tokenIndex++ - tokenOffset = 0 - - # continue past any other character - else - firstNonWhitespaceColumn ?= screenColumn - lastNonWhitespaceColumn = screenColumn - - substringEnd += 1 - screenColumn++ - bufferColumn++ - tokenOffset++ - - if substringEnd > substringStart - unless substringStart is 0 and substringEnd is @text.length - text += @text.substring(substringStart, substringEnd) - @text = text - else - @text = text - - @firstNonWhitespaceIndex = firstNonWhitespaceColumn - if lastNonWhitespaceColumn? - if lastNonWhitespaceColumn + 1 < @text.length - @firstTrailingWhitespaceIndex = lastNonWhitespaceColumn + 1 - if @invisibles?.space - @text = - @text.substring(0, @firstTrailingWhitespaceIndex) + - @text.substring(@firstTrailingWhitespaceIndex) - .replace(RepeatedSpaceRegex, @invisibles.space) - else - @lineIsWhitespaceOnly = true - @firstTrailingWhitespaceIndex = 0 - getTokenIterator: -> @tokenIterator.reset(this, arguments...) Object.defineProperty @prototype, 'tokens', get: -> From acbacae6d50b7a44cf712a170cb9216955b5476b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 13 Jan 2016 18:23:22 -0700 Subject: [PATCH 064/971] Use TokenizedBuffer as a text decoration layer and render tags --- src/display-buffer.coffee | 1 + src/lines-tile-component.coffee | 23 +++++++++++++++++------ src/text-editor-presenter.coffee | 15 ++++++++++----- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 5cae0a23a..096afaa60 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -52,6 +52,7 @@ class DisplayBuffer extends Model }) @buffer = @tokenizedBuffer.buffer @displayLayer = @buffer.addDisplayLayer({tabLength: @getTabLength()}) + @displayLayer.setTextDecorationLayer(@tokenizedBuffer) @charWidthsByScope = {} @defaultMarkerLayer = @displayLayer.addMarkerLayer() @decorationsById = {} diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index f4712dfb6..7d106f9b2 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -125,7 +125,7 @@ class LinesTileComponent screenRowForNode: (node) -> parseInt(node.dataset.screenRow) buildLineNode: (id) -> - {screenRow, words, decorationClasses} = @newTileState.lines[id] + {screenRow, decorationClasses} = @newTileState.lines[id] lineNode = @domElementPool.buildElement("div", "line") lineNode.dataset.screenRow = screenRow @@ -183,12 +183,23 @@ class LinesTileComponent @currentLineTextNodes.push(textNode) setLineInnerNodes: (id, lineNode) -> - {words} = @newTileState.lines[id] + {tokens} = @newTileState.lines[id] lineLength = 0 - for word in words when word.length > 0 - lineLength += word.length - textNode = @domElementPool.buildText(word.replace(/\s/g, NBSPCharacter)) - lineNode.appendChild(textNode) + openScopeNode = lineNode + for token in tokens when token.text.length > 0 + {closeTags, openTags, text} = token + + for scope in closeTags + openScopeNode = openScopeNode.parentElement + + for scope in openTags + newScopeNode = @domElementPool.buildElement("span", scope.replace(/\.+/g, ' ')) + openScopeNode.appendChild(newScopeNode) + openScopeNode = newScopeNode + + lineLength += text.length + textNode = @domElementPool.buildText(text.replace(/\s/g, NBSPCharacter)) + openScopeNode.appendChild(textNode) @currentLineTextNodes.push(textNode) if lineLength is 0 diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 5d2b2c335..54ea000bd 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -133,8 +133,9 @@ class TextEditorPresenter @shouldUpdateDecorations = true observeModel: -> - @disposables.add @model.displayLayer.onDidChangeTextSync (change) => - @invalidateLines(change) + @disposables.add @model.displayLayer.onDidChangeSync (changes) => + for change in changes + @invalidateLines(change) @shouldUpdateDecorations = true @emitDidUpdateState() @@ -395,7 +396,7 @@ class TextEditorPresenter else tileState.lines[line.id] = screenRow: screenRow - words: line.words + tokens: line.tokens decorationClasses: @lineDecorationClassesForRow(screenRow) for id, line of tileState.lines @@ -1028,10 +1029,14 @@ class TextEditorPresenter @linesById.delete(lineId) buildLine: (screenRow) -> - line = {id: @lineIdCounter++, words: []} + line = {id: @lineIdCounter++, tokens: []} @tokenIterator.seekToScreenRow(screenRow) loop - line.words.push(@tokenIterator.getText()) + line.tokens.push({ + text: @tokenIterator.getText(), + closeTags: @tokenIterator.getCloseTags(), + openTags: @tokenIterator.getOpenTags() + }) break unless @tokenIterator.moveToSuccessor() break unless @tokenIterator.getStartScreenPosition().row is screenRow line From a90a2e65b5c4618b25cabeeecd4e201defa6b320 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 14 Jan 2016 14:45:52 -0700 Subject: [PATCH 065/971] Implement TokenizedBuffer.prototype.onDidInvalidateRange This causes DisplayLayer to emit change events when syntax is updated asynchronously. --- src/tokenized-buffer.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 0267e02dd..96d1f4649 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -65,6 +65,9 @@ class TokenizedBuffer extends Model getInvalidatedRanges: -> [@invalidatedRange] + onDidInvalidateRange: (fn) -> + @emitter.on 'did-invalidate-range', fn + serialize: -> state = { deserializer: 'TokenizedBuffer' @@ -221,6 +224,7 @@ class TokenizedBuffer extends Model event = {start: startRow, end: endRow, delta: 0} @emitter.emit 'did-change', event + @emitter.emit 'did-invalidate-range', Range(Point(startRow, 0), Point(endRow + 1, 0)) if @firstInvalidRow()? @tokenizeInBackground() From 271f6b184dc2ba225e43356520a9e25060edeaf9 Mon Sep 17 00:00:00 2001 From: Wliu Date: Fri, 15 Jan 2016 17:21:11 -0500 Subject: [PATCH 066/971] :arrow_up: electron@0.36.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bcd36a4be..daf7a94f0 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.36.3", + "electronVersion": "0.36.4", "dependencies": { "async": "0.2.6", "atom-keymap": "^6.2.0", From ed79413de18e2f840e955f014c5cab7e3fa80731 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 19 Jan 2016 14:39:37 -0700 Subject: [PATCH 067/971] Depend on DisplayLayer for more things --- src/cursor.coffee | 6 +++--- src/text-editor-presenter.coffee | 18 ++++++++++-------- src/text-editor.coffee | 31 +++++++++++++------------------ 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/cursor.coffee b/src/cursor.coffee index f91a7bfd9..87cd73dc8 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -261,7 +261,7 @@ class Cursor extends Model while columnCount > column and row > 0 columnCount -= column - column = @editor.lineTextForScreenRow(--row).length + column = @editor.lineLengthForScreenRow(--row) columnCount-- # subtract 1 for the row move column = column - columnCount @@ -280,7 +280,7 @@ class Cursor extends Model else {row, column} = @getScreenPosition() maxLines = @editor.getScreenLineCount() - rowLength = @editor.lineTextForScreenRow(row).length + rowLength = @editor.lineLengthForScreenRow(row) columnsRemainingInLine = rowLength - column while columnCount > columnsRemainingInLine and row < maxLines - 1 @@ -288,7 +288,7 @@ class Cursor extends Model columnCount-- # subtract 1 for the row move column = 0 - rowLength = @editor.lineTextForScreenRow(++row).length + rowLength = @editor.lineLengthForScreenRow(++row) columnsRemainingInLine = rowLength column = column + columnCount diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 54ea000bd..e251fb0da 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -366,7 +366,7 @@ class TextEditorPresenter visibleTiles[tileStartRow] = true zIndex++ - if @mouseWheelScreenRow? and @model.tokenizedLineForScreenRow(@mouseWheelScreenRow)? + if @mouseWheelScreenRow? and 0 <= @mouseWheelScreenRow < @model.getScreenLineCount() mouseWheelTile = @tileForRow(@mouseWheelScreenRow) unless visibleTiles[mouseWheelTile]? @@ -581,12 +581,12 @@ class TextEditorPresenter softWrapped = false screenRow = startRow + i - line = @model.tokenizedLineForScreenRow(screenRow) + lineId = @lineIdForScreenRow(screenRow) decorationClasses = @lineNumberDecorationClassesForRow(screenRow) foldable = @model.isFoldableAtScreenRow(screenRow) - tileState.lineNumbers[line.id] = {screenRow, bufferRow, softWrapped, decorationClasses, foldable} - visibleLineNumberIds[line.id] = true + tileState.lineNumbers[lineId] = {screenRow, bufferRow, softWrapped, decorationClasses, foldable} + visibleLineNumberIds[lineId] = true for id of tileState.lineNumbers delete tileState.lineNumbers[id] unless visibleLineNumberIds[id] @@ -647,9 +647,10 @@ class TextEditorPresenter updateHorizontalDimensions: -> if @baseCharacterWidth? oldContentWidth = @contentWidth - rightmostPosition = Point(@model.getLongestScreenRow(), @model.getMaxScreenLineLength()) - if @model.tokenizedLineForScreenRow(rightmostPosition.row)?.isSoftWrapped() - rightmostPosition = @model.clipScreenPosition(rightmostPosition) + rightmostPosition = @model.getRightmostScreenPosition() + # TODO: Add some version of this back once softwrap is reintroduced + # if @model.tokenizedLineForScreenRow(rightmostPosition.row)?.isSoftWrapped() + # rightmostPosition = @model.clipScreenPosition(rightmostPosition) @contentWidth = @pixelPositionForScreenPosition(rightmostPosition).left @contentWidth += @scrollLeft @contentWidth += 1 unless @model.isSoftWrapped() # account for cursor width @@ -1421,4 +1422,5 @@ class TextEditorPresenter @startRow <= row < @endRow lineIdForScreenRow: (screenRow) -> - @model.tokenizedLineForScreenRow(screenRow)?.id + ids = @lineMarkerIndex.findStartingAt(Point(screenRow, 0)) + ids.values().next().value if ids.size > 0 diff --git a/src/text-editor.coffee b/src/text-editor.coffee index ef8265e62..9aab56bca 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -738,7 +738,7 @@ class TextEditor extends Model # Essential: Returns a {Number} representing the number of screen lines in the # editor. This accounts for folds. - getScreenLineCount: -> @displayBuffer.getLineCount() + getScreenLineCount: -> @displayLayer.getScreenLineCount() # Essential: Returns a {Number} representing the last zero-indexed buffer row # number of the editor. @@ -746,7 +746,7 @@ class TextEditor extends Model # Essential: Returns a {Number} representing the last zero-indexed screen row # number of the editor. - getLastScreenRow: -> @displayBuffer.getLastRow() + getLastScreenRow: -> @getScreenLineCount() - 1 # Essential: Returns a {String} representing the contents of the line at the # given buffer row. @@ -770,17 +770,21 @@ class TextEditor extends Model # {Delegates to: DisplayBuffer.tokenizedLinesForScreenRows} tokenizedLinesForScreenRows: (start, end) -> @displayBuffer.tokenizedLinesForScreenRows(start, end) - bufferRowForScreenRow: (row) -> @displayBuffer.bufferRowForScreenRow(row) + bufferRowForScreenRow: (row) -> @displayLayer.translateScreenPosition(Point(row, 0)).row # {Delegates to: DisplayBuffer.bufferRowsForScreenRows} bufferRowsForScreenRows: (startRow, endRow) -> @displayBuffer.bufferRowsForScreenRows(startRow, endRow) - screenRowForBufferRow: (row) -> @displayBuffer.screenRowForBufferRow(row) + screenRowForBufferRow: (row) -> @displayLayer.translateBufferPosition(Point(row, 0)).row + + getRightmostScreenPosition: -> @displayLayer.getRightmostScreenPosition() # {Delegates to: DisplayBuffer.getMaxLineLength} - getMaxScreenLineLength: -> @displayBuffer.getMaxLineLength() + getMaxScreenLineLength: -> @getRightmostScreenPosition().column - getLongestScreenRow: -> @displayBuffer.getLongestScreenRow() + getLongestScreenRow: -> @getRightmostScreenPosition().row + + lineLengthForScreenRow: (screenRow) -> @displayLayer.lineLengthForScreenRow(screenRow) # Returns the range for the given buffer row. # @@ -1337,22 +1341,14 @@ class TextEditor extends Model # * `bufferRange` {Range} in buffer coordinates to translate into screen coordinates. # # Returns a {Range}. - screenRangeForBufferRange: (bufferRange) -> - bufferRange = Range.fromObject(bufferRange) - start = @displayLayer.translateBufferPosition(bufferRange.start) - end = @displayLayer.translateBufferPosition(bufferRange.end) - Range(start, end) + screenRangeForBufferRange: (bufferRange) -> @displayLayer.translateBufferRange(bufferRange) # Essential: Convert a range in screen-coordinates to buffer-coordinates. # # * `screenRange` {Range} in screen coordinates to translate into buffer coordinates. # # Returns a {Range}. - bufferRangeForScreenRange: (screenRange) -> - screenRange = Range.fromObject(screenRange) - start = @displayLayer.translateScreenPosition(screenRange.start) - end = @displayLayer.translateScreenPosition(screenRange.end) - Range(start, end) + bufferRangeForScreenRange: (screenRange) -> @displayLayer.translateScreenRange(screenRange) # Extended: Clip the given {Point} to a valid position in the buffer. # @@ -2945,8 +2941,7 @@ class TextEditor extends Model # # Returns a {Boolean}. isFoldableAtScreenRow: (screenRow) -> - bufferRow = @displayBuffer.bufferRowForScreenRow(screenRow) - @isFoldableAtBufferRow(bufferRow) + @isFoldableAtBufferRow(@bufferRowForScreenRow(screenRow)) # Extended: Fold the given buffer row if it isn't currently folded, and unfold # it otherwise. From 21b1f79fabe7b31f668b245a6005ac5acbc52104 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 19 Jan 2016 14:51:24 -0700 Subject: [PATCH 068/971] Avoid updating screen lines in DisplayBuffer --- src/display-buffer.coffee | 15 ++++++--------- src/text-editor.coffee | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 096afaa60..51e0f6f52 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -118,6 +118,7 @@ class DisplayBuffer extends Model }) updateAllScreenLines: -> + return # TODO: After DisplayLayer is finished, delete these code paths @maxLineLength = 0 @screenLines = [] @rowMap = new RowMap @@ -376,11 +377,8 @@ class DisplayBuffer extends Model # # Returns an {Array} of buffer rows as {Numbers}s. bufferRowsForScreenRows: (startScreenRow, endScreenRow) -> - if @largeFileMode - [startScreenRow..endScreenRow] - else - for screenRow in [startScreenRow..endScreenRow] - @rowMap.bufferRowRangeForScreenRow(screenRow)[0] + for screenRow in [startScreenRow..endScreenRow] + @bufferRowForScreenRow(screenRow) # Creates a new fold between two row numbers. # @@ -441,10 +439,7 @@ class DisplayBuffer extends Model # # Returns a {Number}. bufferRowForScreenRow: (screenRow) -> - if @largeFileMode - screenRow - else - @rowMap.bufferRowRangeForScreenRow(screenRow)[0] + @displayLayer.translateScreenPosition(Point(screenRow, 0)).row # Given a buffer range, this converts it into a screen position. # @@ -870,6 +865,8 @@ class DisplayBuffer extends Model @updateScreenLines(start, end + 1, delta, refreshMarkers: false) updateScreenLines: (startBufferRow, endBufferRow, bufferDelta=0, options={}) -> + return # TODO: After DisplayLayer is finished, delete these code paths + return if @largeFileMode return if @isDestroyed() diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 9aab56bca..50b3196db 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -171,7 +171,7 @@ class TextEditor extends Model @disposables.add @selectionsMarkerLayer.onDidCreateMarker @addSelection.bind(this) @disposables.add @displayBuffer.onDidChangeGrammar @handleGrammarChange.bind(this) @disposables.add @displayBuffer.onDidTokenize @handleTokenization.bind(this) - @disposables.add @displayBuffer.onDidChange (e) => + @disposables.add @displayLayer.onDidChangeSync (e) => @mergeIntersectingSelections() @emitter.emit 'did-change', e From 3a14fa2a2e4b1d34e4c90c7a33da620c1d0acaa5 Mon Sep 17 00:00:00 2001 From: Joe Fitzgerald Date: Tue, 19 Jan 2016 17:41:27 -0800 Subject: [PATCH 069/971] Use ELECTRON_RUN_AS_NODE Variable Key - https://github.com/atom/electron/pull/3594 --- src/buffered-node-process.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buffered-node-process.coffee b/src/buffered-node-process.coffee index bb1a1c655..3b4916b24 100644 --- a/src/buffered-node-process.coffee +++ b/src/buffered-node-process.coffee @@ -46,7 +46,7 @@ class BufferedNodeProcess extends BufferedProcess options ?= {} options.env ?= Object.create(process.env) - options.env['ATOM_SHELL_INTERNAL_RUN_AS_NODE'] = 1 + options.env['ELECTRON_RUN_AS_NODE'] = 1 args = args?.slice() ? [] args.unshift(command) From be85af4bc597be5e47259d7654c72268e866a0e4 Mon Sep 17 00:00:00 2001 From: Wliu Date: Mon, 25 Jan 2016 16:39:07 -0500 Subject: [PATCH 070/971] :arrow_up: electron@0.36.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 380064d73..c0f62ebd0 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.36.4", + "electronVersion": "0.36.5", "dependencies": { "async": "0.2.6", "atom-keymap": "^6.2.0", From 47338253dbb3a5a445f3a97c4ec6d764a8de10f8 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 26 Jan 2016 12:24:48 -0700 Subject: [PATCH 071/971] Adapt to new text-buffer API --- src/text-editor-presenter.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index e251fb0da..d6112bdfe 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1042,8 +1042,8 @@ class TextEditorPresenter break unless @tokenIterator.getStartScreenPosition().row is screenRow line - invalidateLines: ({start, replacedExtent, replacementExtent}) -> - {touch} = @lineMarkerIndex.splice(start, replacedExtent, replacementExtent) + invalidateLines: ({start, oldExtent, newExtent}) -> + {touch} = @lineMarkerIndex.splice(start, oldExtent, newExtent) touch.forEach (lineId) => @lineMarkerIndex.delete(lineId) @linesById.delete(lineId) From 9db33e5f8e05ebdc7d49fbf61a6ea04caaff35dd Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 26 Jan 2016 11:42:12 -0800 Subject: [PATCH 072/971] :arrow_up: autocomplete-plus --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c0f62ebd0..a1a626636 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "autocomplete-atom-api": "0.9.2", "autocomplete-css": "0.11.0", "autocomplete-html": "0.7.2", - "autocomplete-plus": "2.25.0", + "autocomplete-plus": "2.26.0", "autocomplete-snippets": "1.9.0", "autoflow": "0.26.0", "autosave": "0.23.0", From f4be23049d537b78d58a9125d71c28b3f5b2b6f8 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 26 Jan 2016 13:26:12 -0800 Subject: [PATCH 073/971] Wait for window resize to take effect in text editor component spec --- spec/text-editor-component-spec.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 17dfa1183..15ad223fe 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -2155,6 +2155,11 @@ describe('TextEditorComponent', function () { width: windowWidth, height: windowHeight }) + + await conditionPromise(function () { + return window.innerWidth === windowWidth + }) + component.measureDimensions() component.measureWindowSize() await nextViewUpdatePromise() From d1b5e0e7e3ff8e67c58326364af4b8322b415d75 Mon Sep 17 00:00:00 2001 From: Wliu Date: Tue, 26 Jan 2016 17:22:33 -0500 Subject: [PATCH 074/971] :arrow_down: electron@0.36.4 Resolves crashes on Windows --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a1a626636..455bd0503 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.36.5", + "electronVersion": "0.36.4", "dependencies": { "async": "0.2.6", "atom-keymap": "^6.2.0", From d0e29bfc2c2dda47752a134896e4958c0f83e0cc Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 26 Jan 2016 17:42:28 -0700 Subject: [PATCH 075/971] Avoid recursive call to moveToSuccessor, which is blowing the stack --- spec/tokenized-buffer-spec.coffee | 27 +++++++++++++++++ src/tokenized-buffer-iterator.coffee | 44 ++++++++++++++++------------ 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index 4fc0d1618..39d03330a 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -1113,3 +1113,30 @@ describe "TokenizedBuffer", -> expect(iterator.seek(Point(2, 0))).toEqual(["source.js"]) iterator.moveToSuccessor() # ensure we don't infinitely loop (regression test) + + it "does not report columns beyond the length of the line", -> + waitsForPromise -> + atom.packages.activatePackage('language-coffee-script') + + runs -> + buffer = new TextBuffer(text: "# hello\n# world") + tokenizedBuffer = new TokenizedBuffer({ + buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert + }) + tokenizedBuffer.setGrammar(atom.grammars.selectGrammar(".coffee")) + fullyTokenize(tokenizedBuffer) + + iterator = tokenizedBuffer.buildIterator() + iterator.seek(Point(0, 0)) + iterator.moveToSuccessor() + iterator.moveToSuccessor() + expect(iterator.getPosition().column).toBe(7) + + iterator.moveToSuccessor() + expect(iterator.getPosition().column).toBe(0) + + iterator.seek(Point(0, 7)) + expect(iterator.getPosition().column).toBe(7) + + iterator.seek(Point(0, 8)) + expect(iterator.getPosition().column).toBe(7) diff --git a/src/tokenized-buffer-iterator.coffee b/src/tokenized-buffer-iterator.coffee index acafb8f4b..e6e129d7a 100644 --- a/src/tokenized-buffer-iterator.coffee +++ b/src/tokenized-buffer-iterator.coffee @@ -14,6 +14,7 @@ class TokenizedBufferIterator currentLine = @tokenizedBuffer.tokenizedLineForRow(position.row) containingTags = currentLine.openScopes.map (id) => @grammarRegistry.scopeForId(id) @currentTags = currentLine.tags + @currentLineLength = currentLine.text.length currentColumn = 0 for tag, index in @currentTags if tag >= 0 @@ -32,39 +33,46 @@ class TokenizedBufferIterator @openTags.push(scopeName) @tagIndex ?= @currentTags.length - @position = Point(position.row, currentColumn) + @position = Point(position.row, Math.min(@currentLineLength, currentColumn)) containingTags moveToSuccessor: -> - if @tagIndex is @currentTags.length - @position = Point(@position.row + 1, 0) - @currentTags = @tokenizedBuffer.tokenizedLineForRow(@position.row)?.tags - return false unless @currentTags? - @tagIndex = 0 - else - @position = Point(@position.row, @position.column + @currentTags[@tagIndex]) - @tagIndex++ - @openTags = [] @closeTags = [] loop - tag = @currentTags[@tagIndex] - if tag >= 0 or @tagIndex is @currentTags.length + if @tagIndex is @currentTags.length if @isAtTagBoundary() break else - return @moveToSuccessor() + return false unless @moveToNextLine() else - scopeName = @grammarRegistry.scopeForId(tag) - if tag % 2 is 0 - @closeTags.push(scopeName) + tag = @currentTags[@tagIndex] + if tag >= 0 + if @isAtTagBoundary() + break + else + @position = Point(@position.row, Math.min(@currentLineLength, @position.column + @currentTags[@tagIndex])) else - @openTags.push(scopeName) - @tagIndex++ + scopeName = @grammarRegistry.scopeForId(tag) + if tag % 2 is 0 + @closeTags.push(scopeName) + else + @openTags.push(scopeName) + @tagIndex++ true + # Private + moveToNextLine: -> + @position = Point(@position.row + 1, 0) + tokenizedLine = @tokenizedBuffer.tokenizedLineForRow(@position.row) + return false unless tokenizedLine? + @currentTags = tokenizedLine.tags + @currentLineLength = tokenizedLine.text.length + @tagIndex = 0 + true + getPosition: -> @position From 07f35818f8358a9b5767bc5a8b7779d4accc82b8 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 26 Jan 2016 17:43:36 -0700 Subject: [PATCH 076/971] Pass invisibles configuration into DisplayLayer --- src/display-buffer.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 51e0f6f52..980148229 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -51,7 +51,7 @@ class DisplayBuffer extends Model @grammarRegistry, @packageManager, @assert }) @buffer = @tokenizedBuffer.buffer - @displayLayer = @buffer.addDisplayLayer({tabLength: @getTabLength()}) + @displayLayer = @buffer.addDisplayLayer({tabLength: @getTabLength(), invisibles: @config.get('editor.invisibles')}) @displayLayer.setTextDecorationLayer(@tokenizedBuffer) @charWidthsByScope = {} @defaultMarkerLayer = @displayLayer.addMarkerLayer() From a8692f19847153ff4dc1f1256a9d6d779c423f4a Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 26 Jan 2016 22:46:17 -0700 Subject: [PATCH 077/971] Extract serialize functionality from ::deactivatePackage. Tests WIP --- spec/package-manager-spec.coffee | 79 +++++++++++++++++--------------- src/package-manager.coffee | 11 ++++- 2 files changed, 51 insertions(+), 39 deletions(-) diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 46d1d11ee..5a19a10aa 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -444,20 +444,18 @@ describe "PackageManager", -> runs -> expect(console.warn).not.toHaveBeenCalled() - it "passes the activate method the package's previously serialized state if it exists", -> - pack = null + fit "passes the activate method the package's previously serialized state if it exists", -> + pack = atom.packages.loadPackage("package-with-serialization") waitsForPromise -> - atom.packages.activatePackage("package-with-serialization").then (p) -> pack = p - + pack.activate() # require main module runs -> - expect(pack.mainModule.someNumber).not.toBe 77 - pack.mainModule.someNumber = 77 + atom.packages.setPackageState("package-with-serialization", {someNumber: 77}) atom.packages.deactivatePackage("package-with-serialization") spyOn(pack.mainModule, 'activate').andCallThrough() - waitsForPromise -> - atom.packages.activatePackage("package-with-serialization") - runs -> - expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77}) + waitsForPromise -> + atom.packages.activatePackage("package-with-serialization") + runs -> + expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77}) it "invokes ::onDidActivatePackage listeners with the activated package", -> activatedPackage = null @@ -821,6 +819,40 @@ describe "PackageManager", -> expect(atom.packages.isPackageActive("package-with-missing-provided-services")).toBe true expect(addErrorHandler.callCount).toBe 0 + describe "::serialize", -> + # TODO + + describe "::serializePackage(pack)", -> + # afterEach -> + # atom.packages.unloadPackages() + + fit "does not serialize packages that have not been activated called on their main module", -> + spyOn(console, 'warn') + badPack = null + waitsForPromise -> + atom.packages.activatePackage("package-that-throws-on-activate").then (p) -> badPack = p + + runs -> + spyOn(badPack.mainModule, 'serialize').andCallThrough() + + atom.packages.serializePackage(badPack) + expect(badPack.mainModule.serialize).not.toHaveBeenCalled() + + fit "absorbs exceptions that are thrown by the package module's serialize method", -> + spyOn(console, 'error') + + waitsForPromise -> + atom.packages.activatePackage('package-with-serialize-error') + + waitsForPromise -> + atom.packages.activatePackage('package-with-serialization') + + runs -> + atom.packages.deactivatePackages() + expect(atom.packages.packageStates['package-with-serialize-error']).toBeUndefined() + expect(atom.packages.packageStates['package-with-serialization']).toEqual someNumber: 1 + expect(console.error).toHaveBeenCalled() + describe "::deactivatePackage(id)", -> afterEach -> atom.packages.unloadPackages() @@ -852,33 +884,6 @@ describe "PackageManager", -> expect(badPack.mainModule.deactivate).not.toHaveBeenCalled() expect(atom.packages.isPackageActive("package-that-throws-on-activate")).toBeFalsy() - it "does not serialize packages that have not been activated called on their main module", -> - spyOn(console, 'warn') - badPack = null - waitsForPromise -> - atom.packages.activatePackage("package-that-throws-on-activate").then (p) -> badPack = p - - runs -> - spyOn(badPack.mainModule, 'serialize').andCallThrough() - - atom.packages.deactivatePackage("package-that-throws-on-activate") - expect(badPack.mainModule.serialize).not.toHaveBeenCalled() - - it "absorbs exceptions that are thrown by the package module's serialize method", -> - spyOn(console, 'error') - - waitsForPromise -> - atom.packages.activatePackage('package-with-serialize-error') - - waitsForPromise -> - atom.packages.activatePackage('package-with-serialization') - - runs -> - atom.packages.deactivatePackages() - expect(atom.packages.packageStates['package-with-serialize-error']).toBeUndefined() - expect(atom.packages.packageStates['package-with-serialization']).toEqual someNumber: 1 - expect(console.error).toHaveBeenCalled() - it "absorbs exceptions that are thrown by the package module's deactivate method", -> spyOn(console, 'error') diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 1ecdc5448..3a3a74711 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -467,6 +467,15 @@ class PackageManager return unless hook? and _.isString(hook) and hook.length > 0 @activationHookEmitter.on(hook, callback) + serialize: -> + for pack in @getLoadedPackages() + @serializePackage(pack) + @packageStates + + serializePackage: (pack) -> + if @isPackageActive(pack.name) + @setPackageState(pack.name, state) if state = pack.serialize?() + # Deactivate all packages deactivatePackages: -> @config.transact => @@ -478,8 +487,6 @@ class PackageManager # Deactivate the package with the given name deactivatePackage: (name) -> pack = @getLoadedPackage(name) - if @isPackageActive(name) - @setPackageState(pack.name, state) if state = pack.serialize?() pack.deactivate() delete @activePackages[pack.name] delete @activatingPackages[pack.name] From 82d584bac65b163177a5236824dfa061171cf07e Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 26 Jan 2016 22:52:30 -0700 Subject: [PATCH 078/971] Serialize in saveStateSync. Separate deserialization from deactivation. --- src/atom-environment.coffee | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index de52fe55c..110f51acb 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -636,16 +636,19 @@ class AtomEnvironment extends Model @openInitialEmptyEditorIfNecessary() + serialize: -> + @state.project = @project.serialize() + @state.workspace = @workspace.serialize() + @state.packageStates = @packages.serialize() + @state.grammars = {grammarOverridesByPath: @grammars.grammarOverridesByPath} + @state.fullScreen = @isFullScreen() + unloadEditorWindow: -> return if not @project @storeWindowBackground() - @state.grammars = {grammarOverridesByPath: @grammars.grammarOverridesByPath} - @state.project = @project.serialize() - @state.workspace = @workspace.serialize() + @serialize() @packages.deactivatePackages() - @state.packageStates = @packages.packageStates - @state.fullScreen = @isFullScreen() @saveStateSync() @saveBlobStoreSync() @@ -782,6 +785,7 @@ class AtomEnvironment extends Model saveStateSync: -> return unless @enablePersistence + @serialize() if storageKey = @getStateKey(@project?.getPaths()) @getStorageFolder().store(storageKey, @state) From 39cb52c2241a83512e2122734cb5655ededee20a Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 27 Jan 2016 11:13:29 -0700 Subject: [PATCH 079/971] Fix tests for serialization --- spec/package-manager-spec.coffee | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 5a19a10aa..969215799 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -444,7 +444,7 @@ describe "PackageManager", -> runs -> expect(console.warn).not.toHaveBeenCalled() - fit "passes the activate method the package's previously serialized state if it exists", -> + it "passes the activate method the package's previously serialized state if it exists", -> pack = atom.packages.loadPackage("package-with-serialization") waitsForPromise -> pack.activate() # require main module @@ -819,14 +819,9 @@ describe "PackageManager", -> expect(atom.packages.isPackageActive("package-with-missing-provided-services")).toBe true expect(addErrorHandler.callCount).toBe 0 - describe "::serialize", -> - # TODO + describe "serialization", -> - describe "::serializePackage(pack)", -> - # afterEach -> - # atom.packages.unloadPackages() - - fit "does not serialize packages that have not been activated called on their main module", -> + it "does not serialize packages that have not been activated called on their main module", -> spyOn(console, 'warn') badPack = null waitsForPromise -> @@ -838,7 +833,7 @@ describe "PackageManager", -> atom.packages.serializePackage(badPack) expect(badPack.mainModule.serialize).not.toHaveBeenCalled() - fit "absorbs exceptions that are thrown by the package module's serialize method", -> + it "absorbs exceptions that are thrown by the package module's serialize method", -> spyOn(console, 'error') waitsForPromise -> @@ -848,7 +843,7 @@ describe "PackageManager", -> atom.packages.activatePackage('package-with-serialization') runs -> - atom.packages.deactivatePackages() + atom.packages.serialize() expect(atom.packages.packageStates['package-with-serialize-error']).toBeUndefined() expect(atom.packages.packageStates['package-with-serialization']).toEqual someNumber: 1 expect(console.error).toHaveBeenCalled() From df83078d741164a32840396527911a0bb2e10496 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 27 Jan 2016 11:15:02 -0700 Subject: [PATCH 080/971] Save state on mousedown or keypress events (debounce 1s). Tests WIP --- spec/atom-environment-spec.coffee | 13 +++++++++++++ src/atom-environment.coffee | 3 +++ 2 files changed, 16 insertions(+) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index b5b975112..938b0eb31 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -174,6 +174,19 @@ describe "AtomEnvironment", -> atom.loadStateSync() expect(atom.state.stuff).toBe("cool") + it "saves state on keypress and mousedown events", -> + spyOn(atom, 'saveStateSync') + + keypress = new KeyboardEvent('keypress') + atom.document.dispatchEvent(keypress) + advanceClock 1100 + expect(atom.saveStateSync).toHaveBeenCalled() + + mousedown = new MouseEvent('mousedown') + atom.document.dispatchEvent(mousedown) + advanceClock 1100 + expect(atom.saveStateSync).toHaveBeenCalled() + describe "openInitialEmptyEditorIfNecessary", -> describe "when there are no paths set", -> beforeEach -> diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 110f51acb..3e1c28240 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -119,6 +119,9 @@ class AtomEnvironment extends Model constructor: (params={}) -> {@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params + @document.addEventListener('mousedown', _.debounce(@saveStateSync.bind(this), 1000), true) + @document.addEventListener('keypress', _.debounce(@saveStateSync.bind(this), 1000), true) + @state = {version: @constructor.version} @loadTime = null From 32f51491962cd0d5c95982b0abd6bdf015148571 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 27 Jan 2016 12:38:28 -0800 Subject: [PATCH 081/971] Wait for browser process to acknowledge window manipulation IPC requests We need to avoid using the module for synchronous IPC on startup, but in some cases, we need to know when our asynchronous IPC messages have taken effect. Now, methods like and return Promises that indicate when the message has been processed. --- spec/text-editor-component-spec.js | 6 +--- src/application-delegate.coffee | 13 ++++---- src/atom-environment.coffee | 28 +++++++++-------- src/browser/atom-application.coffee | 19 +++++++++++ src/initialize-application-window.coffee | 14 ++++----- src/ipc-helpers.js | 40 ++++++++++++++++++++++++ 6 files changed, 89 insertions(+), 31 deletions(-) create mode 100644 src/ipc-helpers.js diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 15ad223fe..08da07dd8 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -2151,15 +2151,11 @@ describe('TextEditorComponent', function () { item.style.height = itemHeight + 'px' wrapperNode.style.width = windowWidth + 'px' wrapperNode.style.height = windowHeight + 'px' - atom.setWindowDimensions({ + await atom.setWindowDimensions({ width: windowWidth, height: windowHeight }) - await conditionPromise(function () { - return window.innerWidth === windowWidth - }) - component.measureDimensions() component.measureWindowSize() await nextViewUpdatePromise() diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index f7b04e10d..f87827886 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -1,5 +1,6 @@ _ = require 'underscore-plus' {ipcRenderer, remote, shell, webFrame} = require 'electron' +ipcHelpers = require './ipc-helpers' {Disposable} = require 'event-kit' {getWindowLoadSettings, setWindowLoadSettings} = require './window-load-settings-helpers' @@ -26,26 +27,26 @@ class ApplicationDelegate {width, height} setWindowSize: (width, height) -> - remote.getCurrentWindow().setSize(width, height) + ipcHelpers.call('set-window-size', width, height) getWindowPosition: -> [x, y] = remote.getCurrentWindow().getPosition() {x, y} setWindowPosition: (x, y) -> - ipcRenderer.send("call-window-method", "setPosition", x, y) + ipcHelpers.call('set-window-position', x, y) centerWindow: -> - ipcRenderer.send("call-window-method", "center") + ipcHelpers.call('center-window') focusWindow: -> - ipcRenderer.send("call-window-method", "focus") + ipcHelpers.call('focus-window') showWindow: -> - ipcRenderer.send("call-window-method", "show") + ipcHelpers.call('show-window') hideWindow: -> - ipcRenderer.send("call-window-method", "hide") + ipcHelpers.call('hide-window') reloadWindow: -> ipcRenderer.send("call-window-method", "reload") diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index cd6834da4..be05ef4cf 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -519,16 +519,17 @@ class AtomEnvironment extends Model # Restore the window to its previous dimensions and show it. # - # Also restores the full screen and maximized state on the next tick to + # Restores the full screen and maximized state after the window has resized to # prevent resize glitches. displayWindow: -> - dimensions = @restoreWindowDimensions() - @show() - @focus() - - setImmediate => - @setFullScreen(true) if @workspace?.fullScreen - @maximize() if dimensions?.maximized and process.platform isnt 'darwin' + @restoreWindowDimensions().then (dimensions) => + steps = [ + @show(), + @focus() + ] + steps.push(@setFullScreen(true)) if @workspace.fullScreen + steps.push(@maximize()) if dimensions?.maximized and process.platform isnt 'darwin' + Promise.all(steps) # Get the dimensions of this window. # @@ -556,12 +557,14 @@ class AtomEnvironment extends Model # * `width` The new width. # * `height` The new height. setWindowDimensions: ({x, y, width, height}) -> + steps = [] if width? and height? - @setSize(width, height) + steps.push(@setSize(width, height)) if x? and y? - @setPosition(x, y) + steps.push(@setPosition(x, y)) else - @center() + steps.push(@center()) + Promise.all(steps) # Returns true if the dimensions are useable, false if they should be ignored. # Work around for https://github.com/atom/atom-shell/issues/473 @@ -594,8 +597,7 @@ class AtomEnvironment extends Model dimensions = @state.windowDimensions unless @isValidDimensions(dimensions) dimensions = @getDefaultWindowDimensions() - @setWindowDimensions(dimensions) - dimensions + @setWindowDimensions(dimensions).then -> dimensions storeWindowDimensions: -> dimensions = @getWindowDimensions() diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 49bf310dc..ff98c9edc 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -3,6 +3,7 @@ ApplicationMenu = require './application-menu' AtomProtocolHandler = require './atom-protocol-handler' AutoUpdateManager = require './auto-update-manager' StorageFolder = require '../storage-folder' +ipcHelpers = require '../ipc-helpers' {BrowserWindow, Menu, app, dialog, ipcMain, shell} = require 'electron' fs = require 'fs-plus' path = require 'path' @@ -261,6 +262,24 @@ class AtomApplication @promptForPath "folder", (selectedPaths) -> event.sender.send(responseChannel, selectedPaths) + ipcHelpers.respondTo 'set-window-size', (win, width, height) -> + win.setSize(width, height) + + ipcHelpers.respondTo 'set-window-position', (win, x, y) -> + win.setPosition(x, y) + + ipcHelpers.respondTo 'center-window', (win) -> + win.center() + + ipcHelpers.respondTo 'focus-window', (win) -> + win.focus() + + ipcHelpers.respondTo 'show-window', (win) -> + win.show() + + ipcHelpers.respondTo 'hide-window', (win) -> + win.hide() + ipcMain.on 'did-cancel-window-unload', => @quitting = false diff --git a/src/initialize-application-window.coffee b/src/initialize-application-window.coffee index 57aa33ce0..35c48ee0a 100644 --- a/src/initialize-application-window.coffee +++ b/src/initialize-application-window.coffee @@ -23,11 +23,11 @@ module.exports = ({blobStore}) -> enablePersistence: true }) - atom.displayWindow() - atom.startEditorWindow() + atom.displayWindow().then -> + atom.startEditorWindow() - # Workaround for focus getting cleared upon window creation - windowFocused = -> - window.removeEventListener('focus', windowFocused) - setTimeout (-> document.querySelector('atom-workspace').focus()), 0 - window.addEventListener('focus', windowFocused) + # Workaround for focus getting cleared upon window creation + windowFocused = -> + window.removeEventListener('focus', windowFocused) + setTimeout (-> document.querySelector('atom-workspace').focus()), 0 + window.addEventListener('focus', windowFocused) diff --git a/src/ipc-helpers.js b/src/ipc-helpers.js new file mode 100644 index 000000000..c0b38c50e --- /dev/null +++ b/src/ipc-helpers.js @@ -0,0 +1,40 @@ +var ipcRenderer = null +var ipcMain = null +var BrowserWindow = null + +exports.call = function (methodName, ...args) { + if (!ipcRenderer) { + ipcRenderer = require('electron').ipcRenderer + } + + var responseChannel = getResponseChannel(methodName) + + return new Promise(function (resolve) { + ipcRenderer.on(responseChannel, function (event, result) { + ipcRenderer.removeAllListeners(responseChannel) + resolve(result) + }) + + ipcRenderer.send(methodName, ...args) + }) +} + +exports.respondTo = function (methodName, callback) { + if (!ipcMain) { + var electron = require('electron') + ipcMain = electron.ipcMain + BrowserWindow = electron.BrowserWindow + } + + var responseChannel = getResponseChannel(methodName) + + ipcMain.on(methodName, function (event, ...args) { + var browserWindow = BrowserWindow.fromWebContents(event.sender) + var result = callback(browserWindow, ...args) + event.sender.send(responseChannel, result) + }) +} + +function getResponseChannel (methodName) { + return 'ipc-helpers-' + methodName + '-response' +} From 28e535ee15921e621a2b3218acc456e985a24d2b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 27 Jan 2016 13:00:18 -0800 Subject: [PATCH 082/971] Apply window background color after resizing window --- src/atom-environment.coffee | 9 +++++++++ static/index.js | 26 -------------------------- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index be05ef4cf..44579a4bc 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -524,6 +524,7 @@ class AtomEnvironment extends Model displayWindow: -> @restoreWindowDimensions().then (dimensions) => steps = [ + @restoreWindowBackground(), @show(), @focus() ] @@ -603,6 +604,13 @@ class AtomEnvironment extends Model dimensions = @getWindowDimensions() @state.windowDimensions = dimensions if @isValidDimensions(dimensions) + restoreWindowBackground: -> + if backgroundColor = window.localStorage.getItem('atom:window-background-color') + @backgroundStylesheet = document.createElement('style') + @backgroundStylesheet.type = 'text/css' + @backgroundStylesheet.innerText = 'html, body { background: ' + backgroundColor + ' !important; }' + document.head.appendChild(@backgroundStylesheet) + storeWindowBackground: -> return if @inSpecMode() @@ -627,6 +635,7 @@ class AtomEnvironment extends Model @packages.loadPackages() @loadStateSync() @document.body.appendChild(@views.getView(@workspace)) + @backgroundStylesheet?.remove() @watchProjectPath() diff --git a/static/index.js b/static/index.js index 796cda363..bedd422fb 100644 --- a/static/index.js +++ b/static/index.js @@ -146,31 +146,6 @@ } } - function setupWindowBackground () { - if (loadSettings && loadSettings.isSpec) { - return - } - - var backgroundColor = window.localStorage.getItem('atom:window-background-color') - if (!backgroundColor) { - return - } - - var backgroundStylesheet = document.createElement('style') - backgroundStylesheet.type = 'text/css' - backgroundStylesheet.innerText = 'html, body { background: ' + backgroundColor + ' !important; }' - document.head.appendChild(backgroundStylesheet) - - // Remove once the page loads - window.addEventListener('load', function loadWindow () { - window.removeEventListener('load', loadWindow, false) - setTimeout(function () { - backgroundStylesheet.remove() - backgroundStylesheet = null - }, 1000) - }, false) - } - var setupAtomHome = function () { if (process.env.ATOM_HOME) { return @@ -186,5 +161,4 @@ parseLoadSettings() setupAtomHome() - setupWindowBackground() })() From e09c7a99111c1f95da24281dd40aec19b4032be4 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 27 Jan 2016 14:58:19 -0700 Subject: [PATCH 083/971] =?UTF-8?q?Avoid=20binding=20method=20before=20it?= =?UTF-8?q?=E2=80=99s=20spied=20upon=20when=20debouncing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/atom-environment.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 3e1c28240..d13937bc6 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -119,8 +119,9 @@ class AtomEnvironment extends Model constructor: (params={}) -> {@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params - @document.addEventListener('mousedown', _.debounce(@saveStateSync.bind(this), 1000), true) - @document.addEventListener('keypress', _.debounce(@saveStateSync.bind(this), 1000), true) + debouncedSaveStateSync = _.debounce((=> @saveStateSync()), 1000) + @document.addEventListener('mousedown', debouncedSaveStateSync, true) + @document.addEventListener('keypress', debouncedSaveStateSync, true) @state = {version: @constructor.version} From 130f23166da9ec1cbc3f0e8c5e42e980b071b189 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 27 Jan 2016 14:04:50 -0800 Subject: [PATCH 084/971] Emit window:loaded event after editor window is started --- src/initialize-application-window.coffee | 1 + static/index.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/initialize-application-window.coffee b/src/initialize-application-window.coffee index 35c48ee0a..1a4a74447 100644 --- a/src/initialize-application-window.coffee +++ b/src/initialize-application-window.coffee @@ -25,6 +25,7 @@ module.exports = ({blobStore}) -> atom.displayWindow().then -> atom.startEditorWindow() + require('electron').ipcRenderer.send('window-command', 'window:loaded') # Workaround for focus getting cleared upon window creation windowFocused = -> diff --git a/static/index.js b/static/index.js index bedd422fb..6b901a215 100644 --- a/static/index.js +++ b/static/index.js @@ -85,7 +85,6 @@ var initialize = require(loadSettings.windowInitializationScript) initialize({blobStore: blobStore}) - require('electron').ipcRenderer.send('window-command', 'window:loaded') } function setupCsonCache (cacheDir) { From 8d55bbcdeadc41b07a1e5e710e45b4a939c399eb Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 27 Jan 2016 15:10:35 -0700 Subject: [PATCH 085/971] :art: clean up test --- spec/package-manager-spec.coffee | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 969215799..8710927df 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -819,9 +819,8 @@ describe "PackageManager", -> expect(atom.packages.isPackageActive("package-with-missing-provided-services")).toBe true expect(addErrorHandler.callCount).toBe 0 - describe "serialization", -> - - it "does not serialize packages that have not been activated called on their main module", -> + describe "::serialize", -> + it "does not serialize packages that threw an error during activation", -> spyOn(console, 'warn') badPack = null waitsForPromise -> @@ -830,7 +829,7 @@ describe "PackageManager", -> runs -> spyOn(badPack.mainModule, 'serialize').andCallThrough() - atom.packages.serializePackage(badPack) + atom.packages.serialize() expect(badPack.mainModule.serialize).not.toHaveBeenCalled() it "absorbs exceptions that are thrown by the package module's serialize method", -> From 126baafda39d3d6a5a0f259ccb81864c069e176c Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 27 Jan 2016 15:11:40 -0700 Subject: [PATCH 086/971] Assign debounce interval to property --- spec/atom-environment-spec.coffee | 4 ++-- src/atom-environment.coffee | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 938b0eb31..4621886dc 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -179,12 +179,12 @@ describe "AtomEnvironment", -> keypress = new KeyboardEvent('keypress') atom.document.dispatchEvent(keypress) - advanceClock 1100 + advanceClock atom.saveStateDebounceInterval expect(atom.saveStateSync).toHaveBeenCalled() mousedown = new MouseEvent('mousedown') atom.document.dispatchEvent(mousedown) - advanceClock 1100 + advanceClock atom.saveStateDebounceInterval expect(atom.saveStateSync).toHaveBeenCalled() describe "openInitialEmptyEditorIfNecessary", -> diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index d13937bc6..cabd1896b 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -111,6 +111,8 @@ class AtomEnvironment extends Model # Public: A {Workspace} instance workspace: null + saveStateDebounceInterval: 1000 + ### Section: Construction and Destruction ### @@ -119,7 +121,7 @@ class AtomEnvironment extends Model constructor: (params={}) -> {@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params - debouncedSaveStateSync = _.debounce((=> @saveStateSync()), 1000) + debouncedSaveStateSync = _.debounce((=> @saveStateSync()), @saveStateDebounceInterval) @document.addEventListener('mousedown', debouncedSaveStateSync, true) @document.addEventListener('keypress', debouncedSaveStateSync, true) From a2f9a8d32e5529b5f7ce4021104a4a836ae23b82 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 27 Jan 2016 23:52:41 -0700 Subject: [PATCH 087/971] Rename StorageFolder::store to ::storeSync --- src/atom-environment.coffee | 2 +- src/browser/atom-application.coffee | 2 +- src/storage-folder.coffee | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index cabd1896b..215bcd7ed 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -794,7 +794,7 @@ class AtomEnvironment extends Model @serialize() if storageKey = @getStateKey(@project?.getPaths()) - @getStorageFolder().store(storageKey, @state) + @getStorageFolder().storeSync(storageKey, @state) else @getCurrentWindow().loadSettings.windowState = JSON.stringify(@state) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 44848eb72..5ff24c458 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -472,7 +472,7 @@ class AtomApplication if loadSettings = window.getLoadSettings() states.push(initialPaths: loadSettings.initialPaths) if states.length > 0 or allowEmpty - @storageFolder.store('application.json', states) + @storageFolder.storeSync('application.json', states) loadState: (options) -> if (states = @storageFolder.load('application.json'))?.length > 0 diff --git a/src/storage-folder.coffee b/src/storage-folder.coffee index da8af3f2e..06beae56a 100644 --- a/src/storage-folder.coffee +++ b/src/storage-folder.coffee @@ -6,7 +6,7 @@ class StorageFolder constructor: (containingPath) -> @path = path.join(containingPath, "storage") if containingPath? - store: (name, object) -> + storeSync: (name, object) -> return unless @path? fs.writeFileSync(@pathForKey(name), JSON.stringify(object), 'utf8') From 5148b8ca3da6f36a050249751f5f6fb7f371e9f0 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 28 Jan 2016 00:47:21 -0700 Subject: [PATCH 088/971] Save state on keydown rather than on keypress --- src/atom-environment.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 215bcd7ed..fc89864eb 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -123,7 +123,7 @@ class AtomEnvironment extends Model debouncedSaveStateSync = _.debounce((=> @saveStateSync()), @saveStateDebounceInterval) @document.addEventListener('mousedown', debouncedSaveStateSync, true) - @document.addEventListener('keypress', debouncedSaveStateSync, true) + @document.addEventListener('keydown', debouncedSaveState, true) @state = {version: @constructor.version} From 54a03516bf51ddf00c6ea0c0dd057fd57e7e7222 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 28 Jan 2016 00:56:52 -0700 Subject: [PATCH 089/971] Save state asynchronously on mousedown and keydown events. Tests WIP --- spec/atom-environment-spec.coffee | 19 ++++++++++--------- src/atom-environment.coffee | 14 ++++++++------ src/storage-folder.coffee | 5 +++++ 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 4621886dc..a575b993d 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -163,7 +163,7 @@ describe "AtomEnvironment", -> atom.state.stuff = "cool" atom.project.setPaths([dir1, dir2]) - atom.saveStateSync() + atom.saveState(true) atom.state = {} atom.loadStateSync() @@ -174,18 +174,18 @@ describe "AtomEnvironment", -> atom.loadStateSync() expect(atom.state.stuff).toBe("cool") - it "saves state on keypress and mousedown events", -> - spyOn(atom, 'saveStateSync') + it "saves state on keydown and mousedown events", -> + spyOn(atom, 'saveState') - keypress = new KeyboardEvent('keypress') - atom.document.dispatchEvent(keypress) + keydown = new KeyboardEvent('keydown') + atom.document.dispatchEvent(keydown) advanceClock atom.saveStateDebounceInterval - expect(atom.saveStateSync).toHaveBeenCalled() + expect(atom.saveState).toHaveBeenCalled() mousedown = new MouseEvent('mousedown') atom.document.dispatchEvent(mousedown) advanceClock atom.saveStateDebounceInterval - expect(atom.saveStateSync).toHaveBeenCalled() + expect(atom.saveState).toHaveBeenCalled() describe "openInitialEmptyEditorIfNecessary", -> describe "when there are no paths set", -> @@ -243,9 +243,10 @@ describe "AtomEnvironment", -> atomEnvironment.destroy() + # TODO: fix failing test. devtools show that ::saveState just goes to jasmine.createSpy.spyObj function it "saves the serialized state of the window so it can be deserialized after reload", -> atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, window, document}) - spyOn(atomEnvironment, 'saveStateSync') + spyOn(atomEnvironment, 'saveState') workspaceState = atomEnvironment.workspace.serialize() grammarsState = {grammarOverridesByPath: atomEnvironment.grammars.grammarOverridesByPath} @@ -256,7 +257,7 @@ describe "AtomEnvironment", -> expect(atomEnvironment.state.workspace).toEqual workspaceState expect(atomEnvironment.state.grammars).toEqual grammarsState expect(atomEnvironment.state.project).toEqual projectState - expect(atomEnvironment.saveStateSync).toHaveBeenCalled() + expect(atomEnvironment.saveState).toHaveBeenCalled() atomEnvironment.destroy() diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index fc89864eb..247b73b40 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -121,8 +121,8 @@ class AtomEnvironment extends Model constructor: (params={}) -> {@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params - debouncedSaveStateSync = _.debounce((=> @saveStateSync()), @saveStateDebounceInterval) - @document.addEventListener('mousedown', debouncedSaveStateSync, true) + debouncedSaveState = _.debounce((=> @saveState()), @saveStateDebounceInterval) + @document.addEventListener('mousedown', debouncedSaveState, true) @document.addEventListener('keydown', debouncedSaveState, true) @state = {version: @constructor.version} @@ -653,9 +653,8 @@ class AtomEnvironment extends Model return if not @project @storeWindowBackground() - @serialize() + @saveState(true) @packages.deactivatePackages() - @saveStateSync() @saveBlobStoreSync() openInitialEmptyEditorIfNecessary: -> @@ -789,12 +788,15 @@ class AtomEnvironment extends Model @blobStore.save() - saveStateSync: -> + saveState: (synchronous) -> return unless @enablePersistence @serialize() if storageKey = @getStateKey(@project?.getPaths()) - @getStorageFolder().storeSync(storageKey, @state) + if synchronous + @getStorageFolder().storeSync(storageKey, @state) + else + @getStorageFolder().storeAsync(storageKey, @state) else @getCurrentWindow().loadSettings.windowState = JSON.stringify(@state) diff --git a/src/storage-folder.coffee b/src/storage-folder.coffee index 06beae56a..d94a6f013 100644 --- a/src/storage-folder.coffee +++ b/src/storage-folder.coffee @@ -11,6 +11,11 @@ class StorageFolder fs.writeFileSync(@pathForKey(name), JSON.stringify(object), 'utf8') + storeAsync: (name, object) -> + return unless @path? + + fs.writeFile(@pathForKey(name), JSON.stringify(object), 'utf8') + load: (name) -> return unless @path? From 29deb61d4ed084d44c32494c7e6b48c4d0a4d0cb Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 28 Jan 2016 01:29:46 -0700 Subject: [PATCH 090/971] Remove AtomEnvironment state instance variable. Tests WIP --- src/atom-environment.coffee | 43 ++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 247b73b40..e4158accd 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -308,9 +308,6 @@ class AtomEnvironment extends Model @views.clear() @registerDefaultViewProviders() - @state.packageStates = {} - delete @state.workspace - destroy: -> return if not @project @@ -597,7 +594,7 @@ class AtomEnvironment extends Model {x: 0, y: 0, width: Math.min(1024, width), height} restoreWindowDimensions: -> - dimensions = @state.windowDimensions + dimensions = @windowDimensions unless @isValidDimensions(dimensions) dimensions = @getDefaultWindowDimensions() @setWindowDimensions(dimensions) @@ -605,7 +602,7 @@ class AtomEnvironment extends Model storeWindowDimensions: -> dimensions = @getWindowDimensions() - @state.windowDimensions = dimensions if @isValidDimensions(dimensions) + @windowDimensions = dimensions if @isValidDimensions(dimensions) storeWindowBackground: -> return if @inSpecMode() @@ -643,11 +640,11 @@ class AtomEnvironment extends Model @openInitialEmptyEditorIfNecessary() serialize: -> - @state.project = @project.serialize() - @state.workspace = @workspace.serialize() - @state.packageStates = @packages.serialize() - @state.grammars = {grammarOverridesByPath: @grammars.grammarOverridesByPath} - @state.fullScreen = @isFullScreen() + project: @project.serialize() + workspace: @workspace.serialize() + packageStates: @packages.serialize() + grammars: {grammarOverridesByPath: @grammars.grammarOverridesByPath} + fullScreen: @isFullScreen() unloadEditorWindow: -> return if not @project @@ -790,15 +787,15 @@ class AtomEnvironment extends Model saveState: (synchronous) -> return unless @enablePersistence - @serialize() + state = @serialize() if storageKey = @getStateKey(@project?.getPaths()) if synchronous - @getStorageFolder().storeSync(storageKey, @state) + @getStorageFolder().storeSync(storageKey, state) else - @getStorageFolder().storeAsync(storageKey, @state) + @getStorageFolder().storeAsync(storageKey, state) else - @getCurrentWindow().loadSettings.windowState = JSON.stringify(@state) + @getCurrentWindow().loadSettings.windowState = JSON.stringify(state) loadStateSync: -> return unless @enablePersistence @@ -806,31 +803,29 @@ class AtomEnvironment extends Model startTime = Date.now() if stateKey = @getStateKey(@getLoadSettings().initialPaths) - if state = @getStorageFolder().load(stateKey) - @state = state + state = @getStorageFolder().load(stateKey) - if not @state? and windowState = @getLoadSettings().windowState + if not state? and windowState = @getLoadSettings().windowState try - if state = JSON.parse(@getLoadSettings().windowState) - @state = state + state = JSON.parse(@getLoadSettings().windowState) catch error console.warn "Error parsing window state: #{statePath} #{error.stack}", error @deserializeTimings.atom = Date.now() - startTime - if grammarOverridesByPath = @state.grammars?.grammarOverridesByPath + if grammarOverridesByPath = state.grammars?.grammarOverridesByPath @grammars.grammarOverridesByPath = grammarOverridesByPath - @setFullScreen(@state.fullScreen) + @setFullScreen(state.fullScreen) - @packages.packageStates = @state.packageStates ? {} + @packages.packageStates = state.packageStates ? {} startTime = Date.now() - @project.deserialize(@state.project, @deserializers) if @state.project? + @project.deserialize(state.project, @deserializers) if state.project? @deserializeTimings.project = Date.now() - startTime startTime = Date.now() - @workspace.deserialize(@state.workspace, @deserializers) if @state.workspace? + @workspace.deserialize(state.workspace, @deserializers) if state.workspace? @deserializeTimings.workspace = Date.now() - startTime getStateKey: (paths) -> From dff4aa8e84f37590c3ae218708c66c285ffb93b7 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 28 Jan 2016 14:03:10 -0700 Subject: [PATCH 091/971] Use method name `store` rather than `storeAsync` --- src/atom-environment.coffee | 2 +- src/storage-folder.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index e4158accd..b6ebc172f 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -793,7 +793,7 @@ class AtomEnvironment extends Model if synchronous @getStorageFolder().storeSync(storageKey, state) else - @getStorageFolder().storeAsync(storageKey, state) + @getStorageFolder().store(storageKey, state) else @getCurrentWindow().loadSettings.windowState = JSON.stringify(state) diff --git a/src/storage-folder.coffee b/src/storage-folder.coffee index d94a6f013..ff3474198 100644 --- a/src/storage-folder.coffee +++ b/src/storage-folder.coffee @@ -11,7 +11,7 @@ class StorageFolder fs.writeFileSync(@pathForKey(name), JSON.stringify(object), 'utf8') - storeAsync: (name, object) -> + store: (name, object) -> return unless @path? fs.writeFile(@pathForKey(name), JSON.stringify(object), 'utf8') From a54f4679af66792322aa35edbfb21da41546b356 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 28 Jan 2016 15:46:40 -0700 Subject: [PATCH 092/971] Fix tests --- spec/atom-environment-spec.coffee | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index a575b993d..856cd0338 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -160,19 +160,18 @@ describe "AtomEnvironment", -> spyOn(atom, 'getLoadSettings').andCallFake -> loadSettings spyOn(atom.getStorageFolder(), 'getPath').andReturn(temp.mkdirSync("storage-dir-")) + spyOn(atom, 'serialize').andReturn({stuff: 'cool'}) + spyOn(atom, 'deserialize') - atom.state.stuff = "cool" atom.project.setPaths([dir1, dir2]) atom.saveState(true) - atom.state = {} atom.loadStateSync() - expect(atom.state.stuff).toBeUndefined() + expect(atom.deserialize).not.toHaveBeenCalled() loadSettings.initialPaths = [dir2, dir1] - atom.state = {} atom.loadStateSync() - expect(atom.state.stuff).toBe("cool") + expect(atom.deserialize).toHaveBeenCalledWith({stuff: 'cool'}) it "saves state on keydown and mousedown events", -> spyOn(atom, 'saveState') @@ -243,20 +242,11 @@ describe "AtomEnvironment", -> atomEnvironment.destroy() - # TODO: fix failing test. devtools show that ::saveState just goes to jasmine.createSpy.spyObj function it "saves the serialized state of the window so it can be deserialized after reload", -> atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, window, document}) spyOn(atomEnvironment, 'saveState') - workspaceState = atomEnvironment.workspace.serialize() - grammarsState = {grammarOverridesByPath: atomEnvironment.grammars.grammarOverridesByPath} - projectState = atomEnvironment.project.serialize() - atomEnvironment.unloadEditorWindow() - - expect(atomEnvironment.state.workspace).toEqual workspaceState - expect(atomEnvironment.state.grammars).toEqual grammarsState - expect(atomEnvironment.state.project).toEqual projectState expect(atomEnvironment.saveState).toHaveBeenCalled() atomEnvironment.destroy() From 92bd5051b3f255d7035d70ea31674a14b4c70830 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 28 Jan 2016 15:47:37 -0700 Subject: [PATCH 093/971] Add env version and window dimensions to serialized state --- src/atom-environment.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index b6ebc172f..8ed254223 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -640,11 +640,13 @@ class AtomEnvironment extends Model @openInitialEmptyEditorIfNecessary() serialize: -> + version: @constructor.version project: @project.serialize() workspace: @workspace.serialize() packageStates: @packages.serialize() grammars: {grammarOverridesByPath: @grammars.grammarOverridesByPath} fullScreen: @isFullScreen() + windowDimensions: @windowDimensions unloadEditorWindow: -> return if not @project From c9012a228843df07cf5cd32a32b3329f3fb75ab8 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 28 Jan 2016 15:48:45 -0700 Subject: [PATCH 094/971] Call AtomEnvironment::deserialize in AtomEnvironment::loadStateSync --- src/atom-environment.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 8ed254223..5c61b1439 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -813,6 +813,9 @@ class AtomEnvironment extends Model catch error console.warn "Error parsing window state: #{statePath} #{error.stack}", error + @deserialize(state) if state? + + deserialize: (state) -> @deserializeTimings.atom = Date.now() - startTime if grammarOverridesByPath = state.grammars?.grammarOverridesByPath From 9cec10d9c0c8851d10818f71605a25112f968fd5 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 28 Jan 2016 22:48:31 -0700 Subject: [PATCH 095/971] Remove atom environment state instance variable --- src/atom-environment.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 5c61b1439..2ec28d230 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -125,7 +125,6 @@ class AtomEnvironment extends Model @document.addEventListener('mousedown', debouncedSaveState, true) @document.addEventListener('keydown', debouncedSaveState, true) - @state = {version: @constructor.version} @loadTime = null {devMode, safeMode, resourcePath} = @getLoadSettings() From 224b51d17c25633fe297b1dc84500e5c5d047960 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 28 Jan 2016 22:49:46 -0700 Subject: [PATCH 096/971] Add disposable for removing debouncedSaveState listener --- src/atom-environment.coffee | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 2ec28d230..4cb2e5928 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -4,7 +4,7 @@ ipc = require 'ipc' _ = require 'underscore-plus' {deprecate} = require 'grim' -{CompositeDisposable, Emitter} = require 'event-kit' +{CompositeDisposable, Disposable, Emitter} = require 'event-kit' fs = require 'fs-plus' {mapSourcePosition} = require 'source-map-support' Model = require './model' @@ -121,11 +121,6 @@ class AtomEnvironment extends Model constructor: (params={}) -> {@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params - debouncedSaveState = _.debounce((=> @saveState()), @saveStateDebounceInterval) - @document.addEventListener('mousedown', debouncedSaveState, true) - @document.addEventListener('keydown', debouncedSaveState, true) - - @loadTime = null {devMode, safeMode, resourcePath} = @getLoadSettings() @@ -205,6 +200,7 @@ class AtomEnvironment extends Model @registerDefaultViewProviders() @installUncaughtErrorHandler() + @attachSaveStateListeners() @installWindowEventHandler() @observeAutoHideMenuBar() @@ -218,6 +214,14 @@ class AtomEnvironment extends Model checkPortableHomeWritable() + attachSaveStateListeners: -> + debouncedSaveState = _.debounce((=> @saveState()), @saveStateDebounceInterval) + @document.addEventListener('mousedown', debouncedSaveState, true) + @document.addEventListener('keydown', debouncedSaveState, true) + @disposables.add new Disposable => + @document.removeEventListener('mousedown', debouncedSaveState, true) + @document.removeEventListener('keydown', debouncedSaveState, true) + setConfigSchema: -> @config.setSchema null, {type: 'object', properties: _.clone(require('./config-schema'))} From 0fd753ea8c790a2cd3955357fb55b426ed2c8761 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sat, 30 Jan 2016 12:07:36 -0500 Subject: [PATCH 097/971] :arrow_up: electron@0.36.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 455bd0503..ca569b342 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.36.4", + "electronVersion": "0.36.7", "dependencies": { "async": "0.2.6", "atom-keymap": "^6.2.0", From 5e21f7bad9efcc519d291a2b5910a5f4fd5e8ce7 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 1 Feb 2016 16:11:10 -0800 Subject: [PATCH 098/971] Serialize package upon deactivation --- spec/package-manager-spec.coffee | 7 ++++--- src/package-manager.coffee | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 8710927df..aa0b2d26f 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -445,11 +445,12 @@ describe "PackageManager", -> expect(console.warn).not.toHaveBeenCalled() it "passes the activate method the package's previously serialized state if it exists", -> - pack = atom.packages.loadPackage("package-with-serialization") + pack = null waitsForPromise -> - pack.activate() # require main module + atom.packages.activatePackage("package-with-serialization").then (p) -> pack = p runs -> - atom.packages.setPackageState("package-with-serialization", {someNumber: 77}) + expect(pack.mainModule.someNumber).not.toBe 77 + pack.mainModule.someNumber = 77 atom.packages.deactivatePackage("package-with-serialization") spyOn(pack.mainModule, 'activate').andCallThrough() waitsForPromise -> diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 3a3a74711..636286640 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -487,6 +487,7 @@ class PackageManager # Deactivate the package with the given name deactivatePackage: (name) -> pack = @getLoadedPackage(name) + @serializePackage(pack) pack.deactivate() delete @activePackages[pack.name] delete @activatingPackages[pack.name] From 81f30d49089cd7853763dc8d2a480d1ec7eca2ab Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 1 Feb 2016 16:49:21 -0800 Subject: [PATCH 099/971] Only call package serialize methods once on quit --- src/atom-environment.coffee | 2 +- src/package-manager.coffee | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 4cb2e5928..68711b260 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -655,8 +655,8 @@ class AtomEnvironment extends Model return if not @project @storeWindowBackground() - @saveState(true) @packages.deactivatePackages() + @saveState(true) @saveBlobStoreSync() openInitialEmptyEditorIfNecessary: -> diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 636286640..705755af0 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -468,13 +468,12 @@ class PackageManager @activationHookEmitter.on(hook, callback) serialize: -> - for pack in @getLoadedPackages() + for pack in @getActivePackages() @serializePackage(pack) @packageStates serializePackage: (pack) -> - if @isPackageActive(pack.name) - @setPackageState(pack.name, state) if state = pack.serialize?() + @setPackageState(pack.name, state) if state = pack.serialize?() # Deactivate all packages deactivatePackages: -> @@ -487,7 +486,7 @@ class PackageManager # Deactivate the package with the given name deactivatePackage: (name) -> pack = @getLoadedPackage(name) - @serializePackage(pack) + @serializePackage(pack) if @isPackageActive(pack.name) pack.deactivate() delete @activePackages[pack.name] delete @activatingPackages[pack.name] From 3b500daab4d552b61e882d0a99872135f5c47552 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 4 Feb 2016 21:43:44 -0800 Subject: [PATCH 100/971] Create StateStore class and specs for storing state in IndexedDB --- spec/state-store-spec.js | 37 +++++++++++++++++++++++++++++++++ src/state-store.js | 45 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 spec/state-store-spec.js create mode 100644 src/state-store.js diff --git a/spec/state-store-spec.js b/spec/state-store-spec.js new file mode 100644 index 000000000..f505cf607 --- /dev/null +++ b/spec/state-store-spec.js @@ -0,0 +1,37 @@ +/** @babel */ +import {it, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers' + +const StateStore = require('../src/state-store.js') + +describe("StateStore", () => { + it("can save and load states", () => { + const store = new StateStore() + return store.save('key', {foo:'bar'}) + .then(() => store.load('key')) + .then((state) => { + expect(state).toEqual({foo:'bar'}) + }) + }) + + describe("when there is an error reading from the database", () => { + it("rejects the promise returned by load", () => { + const store = new StateStore() + + const fakeErrorEvent = {target: {errorCode: "Something bad happened"}} + + spyOn(IDBObjectStore.prototype, 'get').andCallFake((key) => { + let request = {} + process.nextTick(() => request.onerror(fakeErrorEvent)) + return request + }) + + return store.load('nonexistentKey') + .then(() => { + throw new Error("Promise should have been rejected") + }) + .catch((event) => { + expect(event).toBe(fakeErrorEvent) + }) + }) + }) +}) diff --git a/src/state-store.js b/src/state-store.js new file mode 100644 index 000000000..7d6ee79f6 --- /dev/null +++ b/src/state-store.js @@ -0,0 +1,45 @@ +'use strict' + +module.exports = +class StateStore { + constructor () { + this.dbPromise = new Promise((resolve, reject) => { + let dbOpenRequest = indexedDB.open('AtomEnvironments', 1) + dbOpenRequest.onupgradeneeded = (event) => { + let db = event.target.result + db.createObjectStore('states') + resolve(db) + } + dbOpenRequest.onsuccess = () => { + resolve(dbOpenRequest.result) + } + dbOpenRequest.onerror = reject + }) + } + + save (key, value) { + return this.dbPromise.then(db => { + return new Promise((resolve, reject) => { + var request = db.transaction(['states'], 'readwrite') + .objectStore('states') + .put(value, key) + + request.onsuccess = resolve + request.onerror = reject + }) + }) + } + + load (key) { + return this.dbPromise.then(db => { + return new Promise((resolve, reject) => { + var request = db.transaction(['states']) + .objectStore('states') + .get(key) + + request.onsuccess = (event) => resolve(event.target.result) + request.onerror = (event) => reject(event) + }) + }) + } +} From 6e0328b048af8e9d297a81e07784e3bd5762fbdf Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 4 Feb 2016 21:45:00 -0800 Subject: [PATCH 101/971] Refactor to use StateStore instead of StorageFolder --- spec/atom-environment-spec.coffee | 34 +++++++-------- src/atom-environment.coffee | 54 ++++++++++++------------ src/initialize-application-window.coffee | 12 +++--- 3 files changed, 48 insertions(+), 52 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 856cd0338..5afb5169b 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -159,19 +159,22 @@ describe "AtomEnvironment", -> windowState: null spyOn(atom, 'getLoadSettings').andCallFake -> loadSettings - spyOn(atom.getStorageFolder(), 'getPath').andReturn(temp.mkdirSync("storage-dir-")) spyOn(atom, 'serialize').andReturn({stuff: 'cool'}) spyOn(atom, 'deserialize') atom.project.setPaths([dir1, dir2]) - atom.saveState(true) + waitsForPromise -> + atom.saveState().then -> + atom.loadState() - atom.loadStateSync() - expect(atom.deserialize).not.toHaveBeenCalled() + runs -> + expect(atom.deserialize).not.toHaveBeenCalled() - loadSettings.initialPaths = [dir2, dir1] - atom.loadStateSync() - expect(atom.deserialize).toHaveBeenCalledWith({stuff: 'cool'}) + waitsForPromise -> + loadSettings.initialPaths = [dir2, dir1] + atom.loadState() + runs -> + expect(atom.deserialize).toHaveBeenCalledWith({stuff: 'cool'}) it "saves state on keydown and mousedown events", -> spyOn(atom, 'saveState') @@ -242,15 +245,6 @@ describe "AtomEnvironment", -> atomEnvironment.destroy() - it "saves the serialized state of the window so it can be deserialized after reload", -> - atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, window, document}) - spyOn(atomEnvironment, 'saveState') - - atomEnvironment.unloadEditorWindow() - expect(atomEnvironment.saveState).toHaveBeenCalled() - - atomEnvironment.destroy() - describe "::destroy()", -> it "does not throw exceptions when unsubscribing from ipc events (regression)", -> configDirPath = temp.mkdirSync() @@ -262,9 +256,11 @@ describe "AtomEnvironment", -> } atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, window, document: fakeDocument}) spyOn(atomEnvironment.packages, 'getAvailablePackagePaths').andReturn [] - atomEnvironment.startEditorWindow() - atomEnvironment.unloadEditorWindow() - atomEnvironment.destroy() + waitsForPromise -> + atomEnvironment.startEditorWindow() + runs -> + atomEnvironment.unloadEditorWindow() + atomEnvironment.destroy() describe "::openLocations(locations) (called via IPC from browser process)", -> beforeEach -> diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 68711b260..84f1cb23e 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -10,7 +10,7 @@ fs = require 'fs-plus' Model = require './model' WindowEventHandler = require './window-event-handler' StylesElement = require './styles-element' -StorageFolder = require './storage-folder' +StateStore = require './state-store' {getWindowLoadSettings} = require './window-load-settings-helpers' registerDefaultCommands = require './register-default-commands' @@ -127,6 +127,8 @@ class AtomEnvironment extends Model @emitter = new Emitter @disposables = new CompositeDisposable + @stateStore = new StateStore + @deserializers = new DeserializerManager(this) @deserializeTimings = {} @@ -629,18 +631,18 @@ class AtomEnvironment extends Model @registerDefaultTargetForKeymaps() @packages.loadPackages() - @loadStateSync() - @document.body.appendChild(@views.getView(@workspace)) + @loadState().then => + @document.body.appendChild(@views.getView(@workspace)) - @watchProjectPath() + @watchProjectPath() - @packages.activate() - @keymaps.loadUserKeymap() - @requireUserInitScript() unless @getLoadSettings().safeMode + @packages.activate() + @keymaps.loadUserKeymap() + @requireUserInitScript() unless @getLoadSettings().safeMode - @menu.update() + @menu.update() - @openInitialEmptyEditorIfNecessary() + @openInitialEmptyEditorIfNecessary() serialize: -> version: @constructor.version @@ -656,7 +658,6 @@ class AtomEnvironment extends Model @storeWindowBackground() @packages.deactivatePackages() - @saveState(true) @saveBlobStoreSync() openInitialEmptyEditorIfNecessary: -> @@ -790,37 +791,39 @@ class AtomEnvironment extends Model @blobStore.save() - saveState: (synchronous) -> - return unless @enablePersistence + saveState: () -> + return Promise.resolve() unless @enablePersistence state = @serialize() if storageKey = @getStateKey(@project?.getPaths()) - if synchronous - @getStorageFolder().storeSync(storageKey, state) - else - @getStorageFolder().store(storageKey, state) + @stateStore.save(storageKey, state) else @getCurrentWindow().loadSettings.windowState = JSON.stringify(state) + Promise.resolve() - loadStateSync: -> - return unless @enablePersistence + loadState: -> + return Promise.resolve() unless @enablePersistence startTime = Date.now() + statePromise = null if stateKey = @getStateKey(@getLoadSettings().initialPaths) - state = @getStorageFolder().load(stateKey) + statePromise = @stateStore.load(stateKey) - if not state? and windowState = @getLoadSettings().windowState + if not statePromise? and windowState = @getLoadSettings().windowState try - state = JSON.parse(@getLoadSettings().windowState) + statePromise = Promise.resolve(JSON.parse(@getLoadSettings().windowState)) catch error console.warn "Error parsing window state: #{statePath} #{error.stack}", error - @deserialize(state) if state? + if statePromise? + statePromise.then (state) => + @deserializeTimings.atom = Date.now() - startTime + @deserialize(state) if state? + else + Promise.resolve() deserialize: (state) -> - @deserializeTimings.atom = Date.now() - startTime - if grammarOverridesByPath = state.grammars?.grammarOverridesByPath @grammars.grammarOverridesByPath = grammarOverridesByPath @@ -846,9 +849,6 @@ class AtomEnvironment extends Model getConfigDirPath: -> @configDirPath ?= process.env.ATOM_HOME - getStorageFolder: -> - @storageFolder ?= new StorageFolder(@getConfigDirPath()) - getUserInitScriptPath: -> initScriptPath = fs.resolve(@getConfigDirPath(), 'init', ['js', 'coffee']) initScriptPath ? path.join(@getConfigDirPath(), 'init.coffee') diff --git a/src/initialize-application-window.coffee b/src/initialize-application-window.coffee index 57aa33ce0..45e18163d 100644 --- a/src/initialize-application-window.coffee +++ b/src/initialize-application-window.coffee @@ -24,10 +24,10 @@ module.exports = ({blobStore}) -> }) atom.displayWindow() - atom.startEditorWindow() + atom.startEditorWindow().then -> - # Workaround for focus getting cleared upon window creation - windowFocused = -> - window.removeEventListener('focus', windowFocused) - setTimeout (-> document.querySelector('atom-workspace').focus()), 0 - window.addEventListener('focus', windowFocused) + # Workaround for focus getting cleared upon window creation + windowFocused = -> + window.removeEventListener('focus', windowFocused) + setTimeout (-> document.querySelector('atom-workspace').focus()), 0 + window.addEventListener('focus', windowFocused) From 85b32b861ed2f6463ef640a4c4bd1ffb9e55783c Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Fri, 5 Feb 2016 16:49:23 -0800 Subject: [PATCH 102/971] Add storedAt date for serialized environment state --- src/state-store.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/state-store.js b/src/state-store.js index 7d6ee79f6..f07560038 100644 --- a/src/state-store.js +++ b/src/state-store.js @@ -19,6 +19,7 @@ class StateStore { save (key, value) { return this.dbPromise.then(db => { + value.storedAt = new Date().toString() return new Promise((resolve, reject) => { var request = db.transaction(['states'], 'readwrite') .objectStore('states') From f3b39ad82e85a398b33d8a64d504ca7b289e728f Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 8 Feb 2016 14:44:12 -0800 Subject: [PATCH 103/971] =?UTF-8?q?Add=20=E2=80=98indexedDB=E2=80=99=20to?= =?UTF-8?q?=20list=20of=20known=20global=20variables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 322b3c784..94ab84a68 100644 --- a/package.json +++ b/package.json @@ -173,7 +173,8 @@ "runs", "spyOn", "waitsFor", - "waitsForPromise" + "waitsForPromise", + "indexedDB" ] } } From f3a6a1d52283b72fe9fcc23b6c7f5b88545c6096 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 8 Feb 2016 14:44:35 -0800 Subject: [PATCH 104/971] Use prerelease version of text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 94ab84a68..fced6c777 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.1.4", + "text-buffer": "8.2.2-1", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From dc355629624a7f7852a6b68079862d28eb325df7 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 8 Feb 2016 15:12:46 -0800 Subject: [PATCH 105/971] Remove empty parameter list in order to pass linter --- src/atom-environment.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 84f1cb23e..a2b56fdd4 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -791,7 +791,7 @@ class AtomEnvironment extends Model @blobStore.save() - saveState: () -> + saveState: -> return Promise.resolve() unless @enablePersistence state = @serialize() From 69df5d08362adced494a40325eaaaa87da1cf2f4 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 9 Feb 2016 13:40:36 -0500 Subject: [PATCH 106/971] 1.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6815ed9c6..f1dee8def 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "1.5.0-beta3", + "version": "1.5.0", "description": "A hackable text editor for the 21st Century.", "main": "./src/browser/main.js", "repository": { From 1b416c789e16d98617ba74aa36c0f9fcd96e02b8 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 9 Feb 2016 13:40:37 -0500 Subject: [PATCH 107/971] 1.6.0-beta0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6b430a90f..e1b386a76 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "1.6.0-dev", + "version": "1.6.0-beta0", "description": "A hackable text editor for the 21st Century.", "main": "./src/browser/main.js", "repository": { From 9b2c791c86443387c16b3d858dbdd22f070bd9cd Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 9 Feb 2016 14:28:01 -0800 Subject: [PATCH 108/971] Don't emit window:loaded event until async window initialization completes Signed-off-by: Katrina Uychaco --- src/initialize-test-window.coffee | 4 +++- static/index.js | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/initialize-test-window.coffee b/src/initialize-test-window.coffee index f3507b479..bdd94461a 100644 --- a/src/initialize-test-window.coffee +++ b/src/initialize-test-window.coffee @@ -68,7 +68,9 @@ module.exports = ({blobStore}) -> logFile, headless, testPaths, buildAtomEnvironment, buildDefaultApplicationDelegate, legacyTestRunner }) - promise.then(exitWithStatusCode) if getWindowLoadSettings().headless + promise.then (statusCode) -> + exitWithStatusCode(statusCode) if getWindowLoadSettings().headless + catch error if getWindowLoadSettings().headless console.error(error.stack ? error) diff --git a/static/index.js b/static/index.js index 6d65d3c52..651c2afe6 100644 --- a/static/index.js +++ b/static/index.js @@ -83,8 +83,9 @@ setupCsonCache(CompileCache.getCacheDirectory()) var initialize = require(loadSettings.windowInitializationScript) - initialize({blobStore: blobStore}) - require('ipc').sendChannel('window-command', 'window:loaded') + initialize({blobStore: blobStore}).then(function () { + require('ipc').sendChannel('window-command', 'window:loaded') + }) } function setupCsonCache (cacheDir) { From b0cf440f9cc294fa49a4d4c815615e1db6314288 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 9 Feb 2016 14:30:10 -0800 Subject: [PATCH 109/971] Handle database connection errors gracefully When opening a second Atom instance (e.g. when running the integration specs) indexedDB connections will fail. In this case, StateStore.prototype.save and StateStore.prototype.load will become noops. Signed-off-by: Katrina Uychaco --- spec/state-store-spec.js | 16 +++++++++++++--- src/atom-environment.coffee | 2 +- src/state-store.js | 29 +++++++++++++++++++++-------- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/spec/state-store-spec.js b/spec/state-store-spec.js index f505cf607..955ceb767 100644 --- a/spec/state-store-spec.js +++ b/spec/state-store-spec.js @@ -1,11 +1,14 @@ /** @babel */ -import {it, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers' +import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers' const StateStore = require('../src/state-store.js') describe("StateStore", () => { + let databaseName = `test-database-${Date.now()}` + let version = 1 + it("can save and load states", () => { - const store = new StateStore() + const store = new StateStore(databaseName, version) return store.save('key', {foo:'bar'}) .then(() => store.load('key')) .then((state) => { @@ -13,9 +16,16 @@ describe("StateStore", () => { }) }) + it("resolves with null when a non-existent key is loaded", () => { + const store = new StateStore(databaseName, version) + return store.load('no-such-key').then((value) => { + expect(value).toBeNull() + }) + }); + describe("when there is an error reading from the database", () => { it("rejects the promise returned by load", () => { - const store = new StateStore() + const store = new StateStore(databaseName, version) const fakeErrorEvent = {target: {errorCode: "Something bad happened"}} diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 141c1c112..c02ac59c7 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -127,7 +127,7 @@ class AtomEnvironment extends Model @emitter = new Emitter @disposables = new CompositeDisposable - @stateStore = new StateStore + @stateStore = new StateStore('AtomEnvironments', 1) @deserializers = new DeserializerManager(this) @deserializeTimings = {} diff --git a/src/state-store.js b/src/state-store.js index f07560038..817d4282f 100644 --- a/src/state-store.js +++ b/src/state-store.js @@ -2,28 +2,33 @@ module.exports = class StateStore { - constructor () { - this.dbPromise = new Promise((resolve, reject) => { - let dbOpenRequest = indexedDB.open('AtomEnvironments', 1) + constructor (databaseName, version) { + this.dbPromise = new Promise((resolve) => { + let dbOpenRequest = indexedDB.open(databaseName, version) dbOpenRequest.onupgradeneeded = (event) => { let db = event.target.result db.createObjectStore('states') - resolve(db) } dbOpenRequest.onsuccess = () => { resolve(dbOpenRequest.result) } - dbOpenRequest.onerror = reject + dbOpenRequest.onerror = (error) => { + console.error("Could not connect to indexedDB", error) + resolve(null) + } }) } save (key, value) { return this.dbPromise.then(db => { - value.storedAt = new Date().toString() + if (!db) { + return + } + return new Promise((resolve, reject) => { var request = db.transaction(['states'], 'readwrite') .objectStore('states') - .put(value, key) + .put({value: value, storedAt: new Date().toString()}, key) request.onsuccess = resolve request.onerror = reject @@ -33,12 +38,20 @@ class StateStore { load (key) { return this.dbPromise.then(db => { + if (!db) { + return null + } + return new Promise((resolve, reject) => { var request = db.transaction(['states']) .objectStore('states') .get(key) - request.onsuccess = (event) => resolve(event.target.result) + request.onsuccess = (event) => { + let result = event.target.result + resolve(result ? result.value : null) + } + request.onerror = (event) => reject(event) }) }) From 0f02663f6be31dae4f8f1871268e7bae53aad064 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 9 Feb 2016 14:30:58 -0800 Subject: [PATCH 110/971] Add --user-data-dir flag, to control indexedDB directory This way, we can still use indexedDB in the integration tests Signed-off-by: Katrina Uychaco --- spec/integration/helpers/start-atom.coffee | 4 +++- spec/integration/startup-spec.coffee | 2 ++ src/browser/main.coffee | 7 ++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/spec/integration/helpers/start-atom.coffee b/spec/integration/helpers/start-atom.coffee index 3c1016ad2..af9ce094b 100644 --- a/spec/integration/helpers/start-atom.coffee +++ b/spec/integration/helpers/start-atom.coffee @@ -15,6 +15,8 @@ ChromedriverPort = 9515 ChromedriverURLBase = "/wd/hub" ChromedriverStatusURL = "http://localhost:#{ChromedriverPort}#{ChromedriverURLBase}/status" +userDataDir = temp.mkdirSync('atom-user-data-dir') + chromeDriverUp = (done) -> checkStatus = -> http @@ -48,7 +50,7 @@ buildAtomClient = (args, env) -> "atom-env=#{map(env, (value, key) -> "#{key}=#{value}").join(" ")}" "dev" "safe" - "user-data-dir=#{temp.mkdirSync('atom-user-data-dir')}" + "user-data-dir=#{userDataDir}" "socket-path=#{SocketPath}" ]) diff --git a/spec/integration/startup-spec.coffee b/spec/integration/startup-spec.coffee index 6e8a7f55a..f22e7ae2c 100644 --- a/spec/integration/startup-spec.coffee +++ b/spec/integration/startup-spec.coffee @@ -153,6 +153,8 @@ describe "Starting Atom", -> .waitForPaneItemCount(0, 3000) .execute -> atom.workspace.open() .waitForPaneItemCount(1, 3000) + .keys("Hello!") + .waitUntil((-> Promise.resolve(false)), 1100) runAtom [tempDirPath], {ATOM_HOME: atomHome}, (client) -> client diff --git a/src/browser/main.coffee b/src/browser/main.coffee index ca9d7e3ae..bbce1d87f 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -32,6 +32,9 @@ start = -> app.on 'open-url', addUrlToOpen app.on 'will-finish-launching', setupCrashReporter + if args.userDataDir? + app.setPath('userData', args.userDataDir) + app.on 'ready', -> app.removeListener 'open-file', addPathToOpen app.removeListener 'open-url', addUrlToOpen @@ -119,6 +122,7 @@ parseCommandLine = -> options.alias('v', 'version').boolean('v').describe('v', 'Print the version.') options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.') options.string('socket-path') + options.string('user-data-dir') args = options.argv @@ -140,6 +144,7 @@ parseCommandLine = -> pidToKillWhenClosed = args['pid'] if args['wait'] logFile = args['log-file'] socketPath = args['socket-path'] + userDataDir = args['user-data-dir'] profileStartup = args['profile-startup'] urlsToOpen = [] devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH ? path.join(app.getHomeDir(), 'github', 'atom') @@ -164,6 +169,6 @@ parseCommandLine = -> {resourcePath, devResourcePath, pathsToOpen, urlsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, safeMode, newWindow, - logFile, socketPath, profileStartup, timeout, setPortable} + logFile, socketPath, userDataDir, profileStartup, timeout, setPortable} start() From fa70560eba330e2b8f31add9d93d8f4a2173607a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 9 Feb 2016 14:34:32 -0800 Subject: [PATCH 111/971] :shirt: Use single quotes in JS string Signed-off-by: Katrina Uychaco --- src/state-store.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state-store.js b/src/state-store.js index 817d4282f..f5e687d4f 100644 --- a/src/state-store.js +++ b/src/state-store.js @@ -13,7 +13,7 @@ class StateStore { resolve(dbOpenRequest.result) } dbOpenRequest.onerror = (error) => { - console.error("Could not connect to indexedDB", error) + console.error('Could not connect to indexedDB', error) resolve(null) } }) From 7b808257a60103d1e1f78fec9d8a028731ecbdd9 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 10 Feb 2016 11:04:30 -0800 Subject: [PATCH 112/971] Add test to check for indexedDB connection --- spec/atom-environment-spec.coffee | 7 +++++++ src/state-store.js | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 5afb5169b..7aaed602b 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -152,6 +152,8 @@ describe "AtomEnvironment", -> atom.enablePersistence = false it "selects the state based on the current project paths", -> + jasmine.useRealClock() + [dir1, dir2] = [temp.mkdirSync("dir1-"), temp.mkdirSync("dir2-")] loadSettings = _.extend atom.getLoadSettings(), @@ -163,6 +165,11 @@ describe "AtomEnvironment", -> spyOn(atom, 'deserialize') atom.project.setPaths([dir1, dir2]) + # State persistence will fail if other Atom instances are running + waitsForPromise -> + atom.stateStore.connect().then (isConnected) -> + expect(isConnected).toBe true + waitsForPromise -> atom.saveState().then -> atom.loadState() diff --git a/src/state-store.js b/src/state-store.js index f5e687d4f..feefbbb34 100644 --- a/src/state-store.js +++ b/src/state-store.js @@ -19,6 +19,10 @@ class StateStore { }) } + connect () { + return this.dbPromise.then(db => !!db) + } + save (key, value) { return this.dbPromise.then(db => { if (!db) { From b7da70a7e9b3c18139cbc84b8850bb3788ec6072 Mon Sep 17 00:00:00 2001 From: Josh Abernathy Date: Wed, 10 Feb 2016 15:15:10 -0500 Subject: [PATCH 113/971] Merge pull request #10758 from atom/fix-status-in-subdir Fix status in subdir --- package.json | 2 +- spec/git-spec.coffee | 30 +++++++++++++++++++++++++++++- src/git-repository.coffee | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f1dee8def..802221fb6 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "fs-plus": "^2.8.0", "fstream": "0.1.24", "fuzzaldrin": "^2.1", - "git-utils": "^4.1.0", + "git-utils": "^4.1.2", "grim": "1.5.0", "jasmine-json": "~0.0", "jasmine-tagged": "^1.1.4", diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 31ac176f7..c84ff6aa9 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -195,7 +195,7 @@ describe "GitRepository", -> expect(repo.isStatusModified(repo.getDirectoryStatus(directoryPath))).toBe true describe ".refreshStatus()", -> - [newPath, modifiedPath, cleanPath, originalModifiedPathText] = [] + [newPath, modifiedPath, cleanPath, originalModifiedPathText, workingDirectory] = [] beforeEach -> workingDirectory = copyRepository() @@ -221,6 +221,34 @@ describe "GitRepository", -> expect(repo.isStatusNew(repo.getCachedPathStatus(newPath))).toBeTruthy() expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeTruthy() + it 'caches the proper statuses when a subdir is open', -> + subDir = path.join(workingDirectory, 'dir') + fs.mkdirSync(subDir) + + filePath = path.join(subDir, 'b.txt') + fs.writeFileSync(filePath, '') + + atom.project.setPaths([subDir]) + + waitsForPromise -> + atom.workspace.open('b.txt') + + statusHandler = null + runs -> + repo = atom.project.getRepositories()[0] + + statusHandler = jasmine.createSpy('statusHandler') + repo.onDidChangeStatuses statusHandler + repo.refreshStatus() + + waitsFor -> + statusHandler.callCount > 0 + + runs -> + status = repo.getCachedPathStatus(filePath) + expect(repo.isStatusModified(status)).toBe false + expect(repo.isStatusNew(status)).toBe false + describe "buffer events", -> [editor] = [] diff --git a/src/git-repository.coffee b/src/git-repository.coffee index ee27f87a5..cf85cb076 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -466,6 +466,7 @@ class GitRepository relativeProjectPaths = @project?.getPaths() .map (path) => @relativize(path) .filter (path) -> path.length > 0 + .map (path) -> path + '/**' @statusTask?.terminate() @statusTask = Task.once @handlerPath, @getPath(), relativeProjectPaths, ({statuses, upstream, branch, submodules}) => From 88524b19ce457f83e11eec55f298cbfeb5c4935e Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 10 Feb 2016 15:17:43 -0500 Subject: [PATCH 114/971] 1.5.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 802221fb6..a92749be0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "1.5.0", + "version": "1.5.1", "description": "A hackable text editor for the 21st Century.", "main": "./src/browser/main.js", "repository": { From 285c082ac0ecaeb2464040f8d1c9b25fc77e7e87 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 10 Feb 2016 12:37:31 -0800 Subject: [PATCH 115/971] Use text-buffer 8.2.2-2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f883ee866..68f7c3bae 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.2.2-1", + "text-buffer": "8.2.2-2", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 9c0aa629d79054707580218406ce35d9dcb34154 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 10 Feb 2016 14:28:57 -0800 Subject: [PATCH 116/971] Let packages define deserializers & view providers as main module methods --- .../deserializer-1.js | 6 ---- .../deserializer-2.js | 6 ---- .../package-with-deserializers/index.js | 16 ++++++++++- .../package-with-deserializers/package.json | 4 +-- .../deserializer.js | 3 -- .../package-with-view-providers/index.js | 24 +++++++++++++++- .../package-with-view-providers/package.json | 6 ++-- .../view-provider-1.js | 9 ------ .../view-provider-2.js | 9 ------ spec/package-manager-spec.coffee | 6 ++-- src/package.coffee | 28 +++++++++---------- 11 files changed, 59 insertions(+), 58 deletions(-) delete mode 100644 spec/fixtures/packages/package-with-deserializers/deserializer-1.js delete mode 100644 spec/fixtures/packages/package-with-deserializers/deserializer-2.js delete mode 100644 spec/fixtures/packages/package-with-view-providers/deserializer.js delete mode 100644 spec/fixtures/packages/package-with-view-providers/view-provider-1.js delete mode 100644 spec/fixtures/packages/package-with-view-providers/view-provider-2.js diff --git a/spec/fixtures/packages/package-with-deserializers/deserializer-1.js b/spec/fixtures/packages/package-with-deserializers/deserializer-1.js deleted file mode 100644 index f4d7a1488..000000000 --- a/spec/fixtures/packages/package-with-deserializers/deserializer-1.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = function (state) { - return { - wasDeserializedBy: 'Deserializer1', - state: state - } -} diff --git a/spec/fixtures/packages/package-with-deserializers/deserializer-2.js b/spec/fixtures/packages/package-with-deserializers/deserializer-2.js deleted file mode 100644 index 3099d2b15..000000000 --- a/spec/fixtures/packages/package-with-deserializers/deserializer-2.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = function (state) { - return { - wasDeserializedBy: 'Deserializer2', - state: state - } -} diff --git a/spec/fixtures/packages/package-with-deserializers/index.js b/spec/fixtures/packages/package-with-deserializers/index.js index 19bba5ecb..b9be23854 100644 --- a/spec/fixtures/packages/package-with-deserializers/index.js +++ b/spec/fixtures/packages/package-with-deserializers/index.js @@ -1,3 +1,17 @@ module.exports = { - activate: function() {} + activate () {}, + + deserializeMethod1 (state) { + return { + wasDeserializedBy: 'deserializeMethod1', + state: state + } + }, + + deserializeMethod2 (state) { + return { + wasDeserializedBy: 'deserializeMethod2', + state: state + } + } } diff --git a/spec/fixtures/packages/package-with-deserializers/package.json b/spec/fixtures/packages/package-with-deserializers/package.json index daa5776bf..bae0776a6 100644 --- a/spec/fixtures/packages/package-with-deserializers/package.json +++ b/spec/fixtures/packages/package-with-deserializers/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "main": "./index", "deserializers": { - "Deserializer1": "./deserializer-1.js", - "Deserializer2": "./deserializer-2.js" + "Deserializer1": "deserializeMethod1", + "Deserializer2": "deserializeMethod2" } } diff --git a/spec/fixtures/packages/package-with-view-providers/deserializer.js b/spec/fixtures/packages/package-with-view-providers/deserializer.js deleted file mode 100644 index 334e7b2ab..000000000 --- a/spec/fixtures/packages/package-with-view-providers/deserializer.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function (state) { - return {state: state} -} diff --git a/spec/fixtures/packages/package-with-view-providers/index.js b/spec/fixtures/packages/package-with-view-providers/index.js index 19bba5ecb..66e62171d 100644 --- a/spec/fixtures/packages/package-with-view-providers/index.js +++ b/spec/fixtures/packages/package-with-view-providers/index.js @@ -1,3 +1,25 @@ +'use strict' + module.exports = { - activate: function() {} + activate () {}, + + theDeserializerMethod (state) { + return {state: state} + }, + + viewProviderMethod1 (model) { + if (model.worksWithViewProvider1) { + let element = document.createElement('div') + element.dataset['createdBy'] = 'view-provider-1' + return element + } + }, + + viewProviderMethod2 (model) { + if (model.worksWithViewProvider2) { + let element = document.createElement('div') + element.dataset['createdBy'] = 'view-provider-2' + return element + } + } } diff --git a/spec/fixtures/packages/package-with-view-providers/package.json b/spec/fixtures/packages/package-with-view-providers/package.json index f67477280..eb5c80025 100644 --- a/spec/fixtures/packages/package-with-view-providers/package.json +++ b/spec/fixtures/packages/package-with-view-providers/package.json @@ -3,10 +3,10 @@ "main": "./index", "version": "1.0.0", "deserializers": { - "DeserializerFromPackageWithViewProviders": "./deserializer" + "DeserializerFromPackageWithViewProviders": "theDeserializerMethod" }, "viewProviders": [ - "./view-provider-1", - "./view-provider-2" + "viewProviderMethod1", + "viewProviderMethod2" ] } diff --git a/spec/fixtures/packages/package-with-view-providers/view-provider-1.js b/spec/fixtures/packages/package-with-view-providers/view-provider-1.js deleted file mode 100644 index e4f0dcc0b..000000000 --- a/spec/fixtures/packages/package-with-view-providers/view-provider-1.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - -module.exports = function (model) { - if (model.worksWithViewProvider1) { - let element = document.createElement('div') - element.dataset['createdBy'] = 'view-provider-1' - return element - } -} diff --git a/spec/fixtures/packages/package-with-view-providers/view-provider-2.js b/spec/fixtures/packages/package-with-view-providers/view-provider-2.js deleted file mode 100644 index a3b58a3aa..000000000 --- a/spec/fixtures/packages/package-with-view-providers/view-provider-2.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - -module.exports = function (model) { - if (model.worksWithViewProvider2) { - let element = document.createElement('div') - element.dataset['createdBy'] = 'view-provider-2' - return element - } -} diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 46d1d11ee..5c10ed2fc 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -88,18 +88,16 @@ describe "PackageManager", -> state1 = {deserializer: 'Deserializer1', a: 'b'} expect(atom.deserializers.deserialize(state1)).toEqual { - wasDeserializedBy: 'Deserializer1' + wasDeserializedBy: 'deserializeMethod1' state: state1 } state2 = {deserializer: 'Deserializer2', c: 'd'} expect(atom.deserializers.deserialize(state2)).toEqual { - wasDeserializedBy: 'Deserializer2' + wasDeserializedBy: 'deserializeMethod2' state: state2 } - expect(pack.mainModule).toBeNull() - describe "when there are view providers specified in the package's package.json", -> model1 = {worksWithViewProvider1: true} model2 = {worksWithViewProvider2: true} diff --git a/src/package.coffee b/src/package.coffee index 8230ce4e4..ba5d34439 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -84,7 +84,7 @@ class Package @loadKeymaps() @loadMenus() @loadStylesheets() - @loadDeserializers() + @registerDeserializerMethods() @configSchemaRegisteredOnLoad = @registerConfigSchemaFromMetadata() @settingsPromise = @loadSettings() if @shouldRequireMainModuleOnLoad() and not @mainModule? @@ -277,24 +277,24 @@ class Package @stylesheets = @getStylesheetPaths().map (stylesheetPath) => [stylesheetPath, @themeManager.loadStylesheet(stylesheetPath, true)] - loadDeserializers: -> + registerDeserializerMethods: -> if @metadata.deserializers? - for name, implementationPath of @metadata.deserializers - do => - deserializePath = path.join(@path, implementationPath) - deserializeFunction = null - atom.deserializers.add - name: name, - deserialize: => - @registerViewProviders() - deserializeFunction ?= require(deserializePath) - deserializeFunction.apply(this, arguments) + Object.keys(@metadata.deserializers).forEach (deserializerName) => + methodName = @metadata.deserializers[deserializerName] + atom.deserializers.add + name: deserializerName, + deserialize: (state, atomEnvironment) => + @registerViewProviders() + @requireMainModule() + @mainModule[methodName](state, atomEnvironment) return registerViewProviders: -> if @metadata.viewProviders? and not @registeredViewProviders - for implementationPath in @metadata.viewProviders - @viewRegistry.addViewProvider(require(path.join(@path, implementationPath))) + @metadata.viewProviders.forEach (methodName) => + @viewRegistry.addViewProvider (model) => + @requireMainModule() + @mainModule[methodName](model) @registeredViewProviders = true getStylesheetsPath: -> From 262459829ccc7a91b96593ce378db134955fc345 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 10 Feb 2016 17:04:04 -0800 Subject: [PATCH 117/971] :arrow_up: tree-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7783579b2..d9187684e 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.110.1", "tabs": "0.90.0", "timecop": "0.33.0", - "tree-view": "0.201.0", + "tree-view": "0.201.1", "update-package-dependencies": "0.10.0", "welcome": "0.33.0", "whitespace": "0.32.1", From 7ed8623480b788e85762a6556c36876140ecd645 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 10 Feb 2016 17:05:47 -0800 Subject: [PATCH 118/971] 1.6.0-beta2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d9187684e..4cc92242d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "1.6.0-beta1", + "version": "1.6.0-beta2", "description": "A hackable text editor for the 21st Century.", "main": "./src/browser/main.js", "repository": { From e8693f45c902f1b371aae15ec01271d841bbc94e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 10 Feb 2016 17:40:55 -0800 Subject: [PATCH 119/971] Move lazy main module require out of loop --- src/package.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.coffee b/src/package.coffee index ba5d34439..94e759947 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -291,9 +291,9 @@ class Package registerViewProviders: -> if @metadata.viewProviders? and not @registeredViewProviders + @requireMainModule() @metadata.viewProviders.forEach (methodName) => @viewRegistry.addViewProvider (model) => - @requireMainModule() @mainModule[methodName](model) @registeredViewProviders = true From f6ced98be873739dd4233ab16192019324dc5c4e Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 11 Feb 2016 11:43:41 -0800 Subject: [PATCH 120/971] Fix bug from merge with PR#10743 --- src/atom-environment.coffee | 20 +++++++++++--------- src/initialize-application-window.coffee | 6 +++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 2d7427ba0..f7869f9af 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -615,7 +615,7 @@ class AtomEnvironment extends Model # But after that, e.g., when the window's been reloaded, we want to use the # dimensions we've saved for it. if not @isFirstLoad() - dimensions = @state.windowDimensions + dimensions = @windowDimensions unless @isValidDimensions(dimensions) dimensions = @getDefaultWindowDimensions() @@ -648,18 +648,18 @@ class AtomEnvironment extends Model @registerDefaultTargetForKeymaps() @packages.loadPackages() - @loadState().then => - @document.body.appendChild(@views.getView(@workspace)) + + @document.body.appendChild(@views.getView(@workspace)) - @watchProjectPath() + @watchProjectPath() - @packages.activate() - @keymaps.loadUserKeymap() - @requireUserInitScript() unless @getLoadSettings().safeMode + @packages.activate() + @keymaps.loadUserKeymap() + @requireUserInitScript() unless @getLoadSettings().safeMode - @menu.update() + @menu.update() - @openInitialEmptyEditorIfNecessary() + @openInitialEmptyEditorIfNecessary() serialize: -> version: @constructor.version @@ -846,6 +846,8 @@ class AtomEnvironment extends Model @setFullScreen(state.fullScreen) + @windowDimensions = state.windowDimensions if state.windowDimensions + @packages.packageStates = state.packageStates ? {} startTime = Date.now() diff --git a/src/initialize-application-window.coffee b/src/initialize-application-window.coffee index 0044a8d18..e4fa4af4d 100644 --- a/src/initialize-application-window.coffee +++ b/src/initialize-application-window.coffee @@ -23,9 +23,9 @@ module.exports = ({blobStore}) -> enablePersistence: true }) - atom.loadStateSync() - atom.displayWindow() - atom.startEditorWindow().then -> + atom.loadState().then -> + atom.displayWindow() + atom.startEditorWindow() # Workaround for focus getting cleared upon window creation windowFocused = -> From 8e33f8bf5b974a690cf291dcfad5a38b5cc12a57 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 11 Feb 2016 11:50:44 -0800 Subject: [PATCH 121/971] Wait for tab to open in webdriverio `waitForPaneItemCount` command --- spec/integration/helpers/start-atom.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/integration/helpers/start-atom.coffee b/spec/integration/helpers/start-atom.coffee index af9ce094b..9df884286 100644 --- a/spec/integration/helpers/start-atom.coffee +++ b/spec/integration/helpers/start-atom.coffee @@ -85,7 +85,8 @@ buildAtomClient = (args, env) -> .addCommand "waitForPaneItemCount", (count, timeout, cb) -> @waitUntil(-> - @execute(-> atom.workspace?.getActivePane()?.getItems().length) + @waitForExist('.tab', 10000) + .execute(-> atom.workspace?.getActivePane()?.getItems().length) .then(({value}) -> value is count) , timeout) .then (result) -> From 58ba84d9fa00a332ba9971ede827b7e59f2aaf86 Mon Sep 17 00:00:00 2001 From: Baptist BENOIST Date: Thu, 11 Feb 2016 22:55:43 +0100 Subject: [PATCH 122/971] :memo: Fix minor typo in CONTRIBUTING.md [ci skip] --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ada420a40..466355903 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -365,7 +365,7 @@ Please open an issue on `atom/atom` if you have suggestions for new labels, and | `blocked` | [search][search-atom-repo-label-blocked] | [search][search-atom-org-label-blocked] | Issues blocked on other issues. | | `duplicate` | [search][search-atom-repo-label-duplicate] | [search][search-atom-org-label-duplicate] | Issues which are duplicates of other issues, i.e. they have been reported before. | | `wontfix` | [search][search-atom-repo-label-wontfix] | [search][search-atom-org-label-wontfix] | The Atom core team has decided not to fix these issues for now, either because they're working as intended or for some other reason. | -| `invalid` | [search][search-atom-repo-label-invalid] | [search][search-atom-org-label-invalid] | Issues which are't valid (e.g. user errors). | +| `invalid` | [search][search-atom-repo-label-invalid] | [search][search-atom-org-label-invalid] | Issues which aren't valid (e.g. user errors). | | `package-idea` | [search][search-atom-repo-label-package-idea] | [search][search-atom-org-label-package-idea] | Feature request which might be good candidates for new packages, instead of extending Atom or core Atom packages. | | `wrong-repo` | [search][search-atom-repo-label-wrong-repo] | [search][search-atom-org-label-wrong-repo] | Issues reported on the wrong repository (e.g. a bug related to the [Settings View package](https://github.com/atom/settings-view) was reported on [Atom core](https://github.com/atom/atom)). | From c2ff75f94bf1a0382ea46c01e8c39c7a1676cce9 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 11 Feb 2016 13:57:20 -0800 Subject: [PATCH 123/971] Use `user-data-dir` with temp directory when running core tests This is to ensure successful database connection. Since core specs and package specs are run at the same time and both open an indexedDB connection, there were occasional core spec failures due to failed database connection. --- build/tasks/spec-task.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/tasks/spec-task.coffee b/build/tasks/spec-task.coffee index 892c92696..1d4be1e6f 100644 --- a/build/tasks/spec-task.coffee +++ b/build/tasks/spec-task.coffee @@ -1,5 +1,6 @@ fs = require 'fs' path = require 'path' +temp = require('temp').track() _ = require 'underscore-plus' async = require 'async' @@ -94,7 +95,7 @@ module.exports = (grunt) -> if process.platform in ['darwin', 'linux'] options = cmd: appPath - args: ['--test', "--resource-path=#{resourcePath}", coreSpecsPath] + args: ['--test', "--resource-path=#{resourcePath}", coreSpecsPath, "--user-data-dir=#{temp.mkdirSync('atom-user-data-dir')}"] opts: env: _.extend({}, process.env, ATOM_INTEGRATION_TESTS_ENABLED: true From 5e7f2741b43cbaacb2e5a2be1ab7505bd4d63ab7 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 11 Feb 2016 14:50:30 -0800 Subject: [PATCH 124/971] In startup-spec check pane item count after editor has been loaded --- spec/integration/startup-spec.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/integration/startup-spec.coffee b/spec/integration/startup-spec.coffee index f22e7ae2c..e82d34e57 100644 --- a/spec/integration/startup-spec.coffee +++ b/spec/integration/startup-spec.coffee @@ -28,13 +28,12 @@ describe "Starting Atom", -> it "opens the parent directory and creates an empty text editor", -> runAtom [path.join(tempDirPath, "new-file")], {ATOM_HOME: atomHome}, (client) -> client - .waitForPaneItemCount(1, 1000) - .treeViewRootDirectories() .then ({value}) -> expect(value).toEqual([tempDirPath]) .waitForExist("atom-text-editor", 5000) .then (exists) -> expect(exists).toBe true + .waitForPaneItemCount(1, 1000) .click("atom-text-editor") .keys("Hello!") .execute -> atom.workspace.getActiveTextEditor().getText() From 7cfe0b659e0e7bba6e452e99d6d5652050e37d8c Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 11 Feb 2016 14:50:51 -0800 Subject: [PATCH 125/971] Revert "Wait for tab to open in webdriverio `waitForPaneItemCount` command" This reverts commit 8e33f8bf5b974a690cf291dcfad5a38b5cc12a57. --- spec/integration/helpers/start-atom.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/integration/helpers/start-atom.coffee b/spec/integration/helpers/start-atom.coffee index 9df884286..af9ce094b 100644 --- a/spec/integration/helpers/start-atom.coffee +++ b/spec/integration/helpers/start-atom.coffee @@ -85,8 +85,7 @@ buildAtomClient = (args, env) -> .addCommand "waitForPaneItemCount", (count, timeout, cb) -> @waitUntil(-> - @waitForExist('.tab', 10000) - .execute(-> atom.workspace?.getActivePane()?.getItems().length) + @execute(-> atom.workspace?.getActivePane()?.getItems().length) .then(({value}) -> value is count) , timeout) .then (result) -> From 6f4936983e60afd1cb70b68c28a11ad08ac2364d Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 11 Feb 2016 15:40:04 -0800 Subject: [PATCH 126/971] Revert test since `startEditorWindow` no longer returns a promise --- spec/atom-environment-spec.coffee | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 7aaed602b..561ecf3b5 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -263,11 +263,9 @@ describe "AtomEnvironment", -> } atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, window, document: fakeDocument}) spyOn(atomEnvironment.packages, 'getAvailablePackagePaths').andReturn [] - waitsForPromise -> - atomEnvironment.startEditorWindow() - runs -> - atomEnvironment.unloadEditorWindow() - atomEnvironment.destroy() + atomEnvironment.startEditorWindow() + atomEnvironment.unloadEditorWindow() + atomEnvironment.destroy() describe "::openLocations(locations) (called via IPC from browser process)", -> beforeEach -> From 11e83a7d6f4fecd0dd4e33a5c5d726631bfba94b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 16:32:28 -0800 Subject: [PATCH 127/971] :arrow_up: one ui themes --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b0e4edf2a..8abbafc66 100644 --- a/package.json +++ b/package.json @@ -66,8 +66,8 @@ "atom-light-ui": "0.43.0", "base16-tomorrow-dark-theme": "1.1.0", "base16-tomorrow-light-theme": "1.1.1", - "one-dark-ui": "1.1.9", - "one-light-ui": "1.1.9", + "one-dark-ui": "1.2.0", + "one-light-ui": "1.2.0", "one-dark-syntax": "1.2.0", "one-light-syntax": "1.2.0", "solarized-dark-syntax": "1.0.0", From 6bb26c4e66f57d2c4863ae10c6a6e953dfb24db1 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 16:34:08 -0800 Subject: [PATCH 128/971] :arrow_up: about --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8abbafc66..c564abb6f 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "one-light-syntax": "1.2.0", "solarized-dark-syntax": "1.0.0", "solarized-light-syntax": "1.0.0", - "about": "1.3.0", + "about": "1.3.1", "archive-view": "0.61.0", "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.0", From e18e5e303aa4cba6c75a27eccddcaba8e2efeb78 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:22:22 -0800 Subject: [PATCH 129/971] :arrow_up: autosave --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c564abb6f..be6816b2a 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "autocomplete-plus": "2.25.0", "autocomplete-snippets": "1.10.0", "autoflow": "0.27.0", - "autosave": "0.23.0", + "autosave": "0.23.1", "background-tips": "0.26.0", "bookmarks": "0.38.2", "bracket-matcher": "0.79.0", From b0800ed78bfc04e935fbc2254f4f67d17398f5b3 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:23:57 -0800 Subject: [PATCH 130/971] :arrow_up: bracket-matcher --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index be6816b2a..7ac302eb0 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "autosave": "0.23.1", "background-tips": "0.26.0", "bookmarks": "0.38.2", - "bracket-matcher": "0.79.0", + "bracket-matcher": "0.79.1", "command-palette": "0.38.0", "deprecation-cop": "0.54.0", "dev-live-reload": "0.47.0", From a40b221bd3de88be129feeb148a4153bc02a30ea Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:24:45 -0800 Subject: [PATCH 131/971] :arrow_up: deprecation-cop --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7ac302eb0..27d9dc9be 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "bookmarks": "0.38.2", "bracket-matcher": "0.79.1", "command-palette": "0.38.0", - "deprecation-cop": "0.54.0", + "deprecation-cop": "0.54.1", "dev-live-reload": "0.47.0", "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", From 4d21e21850916762a7dd455e71f23bfa49ba1615 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:26:21 -0800 Subject: [PATCH 132/971] :arrow_up: find-and-replace --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 27d9dc9be..59f0f8198 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "dev-live-reload": "0.47.0", "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", - "find-and-replace": "0.197.1", + "find-and-replace": "0.197.2", "fuzzy-finder": "0.94.0", "git-diff": "0.57.0", "go-to-line": "0.30.0", From 41c057702a8e74ff56e0c04efad4142def8ec408 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:27:31 -0800 Subject: [PATCH 133/971] :arrow_up: fuzzy-finder --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 59f0f8198..c80504f48 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", "find-and-replace": "0.197.2", - "fuzzy-finder": "0.94.0", + "fuzzy-finder": "0.94.1", "git-diff": "0.57.0", "go-to-line": "0.30.0", "grammar-selector": "0.48.0", From 53238e2b62d57ba6f8e691d4605d535b452c3944 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:28:11 -0800 Subject: [PATCH 134/971] :arrow_up: git-diff --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c80504f48..ec61ef638 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "exception-reporting": "0.37.0", "find-and-replace": "0.197.2", "fuzzy-finder": "0.94.1", - "git-diff": "0.57.0", + "git-diff": "0.57.1", "go-to-line": "0.30.0", "grammar-selector": "0.48.0", "image-view": "0.56.0", From 118817d88a973462651fe904324ae820cdc62f53 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:29:00 -0800 Subject: [PATCH 135/971] :arrow_up: grammar-selector --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ec61ef638..4b124ecc5 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "fuzzy-finder": "0.94.1", "git-diff": "0.57.1", "go-to-line": "0.30.0", - "grammar-selector": "0.48.0", + "grammar-selector": "0.48.1", "image-view": "0.56.0", "incompatible-packages": "0.25.0", "keybinding-resolver": "0.33.0", From eed8b0000427f9c6e7768426dba2aa2639086a3b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:29:37 -0800 Subject: [PATCH 136/971] :arrow_up: incompatible-packages --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b124ecc5..540ce01dd 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "go-to-line": "0.30.0", "grammar-selector": "0.48.1", "image-view": "0.56.0", - "incompatible-packages": "0.25.0", + "incompatible-packages": "0.25.1", "keybinding-resolver": "0.33.0", "line-ending-selector": "0.3.0", "link": "0.31.0", From 3230eb2650e60b22ef7a3b2ce4d52c08bdb39920 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:31:02 -0800 Subject: [PATCH 137/971] :arrow_up: line-ending-selector --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 540ce01dd..bf9645d7b 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "image-view": "0.56.0", "incompatible-packages": "0.25.1", "keybinding-resolver": "0.33.0", - "line-ending-selector": "0.3.0", + "line-ending-selector": "0.3.1", "link": "0.31.0", "markdown-preview": "0.157.2", "metrics": "0.53.1", From 551742f211b27513539c532c22ad1f38b8819633 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:32:27 -0800 Subject: [PATCH 138/971] :arrow_up: markdown-preview --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bf9645d7b..a01758ee5 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "keybinding-resolver": "0.33.0", "line-ending-selector": "0.3.1", "link": "0.31.0", - "markdown-preview": "0.157.2", + "markdown-preview": "0.157.3", "metrics": "0.53.1", "notifications": "0.62.1", "open-on-github": "0.41.0", From bd90d8a84f06da30b8233820464224b618762c72 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:33:01 -0800 Subject: [PATCH 139/971] :arrow_up: notifications --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a01758ee5..c6fa944ee 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "link": "0.31.0", "markdown-preview": "0.157.3", "metrics": "0.53.1", - "notifications": "0.62.1", + "notifications": "0.62.2", "open-on-github": "0.41.0", "package-generator": "0.41.0", "settings-view": "0.232.3", From 27963169c92b3ac5e1a54ec8960b36ee7b984413 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:34:03 -0800 Subject: [PATCH 140/971] :arrow_up: open-on-github --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c6fa944ee..512b4a71b 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "markdown-preview": "0.157.3", "metrics": "0.53.1", "notifications": "0.62.2", - "open-on-github": "0.41.0", + "open-on-github": "0.41.1", "package-generator": "0.41.0", "settings-view": "0.232.3", "snippets": "1.0.1", From 45298d4c3c93a3ade23624b7203fd59a838282ae Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:34:56 -0800 Subject: [PATCH 141/971] :arrow_up: package-generator --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 512b4a71b..e526f24ee 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "metrics": "0.53.1", "notifications": "0.62.2", "open-on-github": "0.41.1", - "package-generator": "0.41.0", + "package-generator": "0.41.1", "settings-view": "0.232.3", "snippets": "1.0.1", "spell-check": "0.65.0", From 42397a2d01c0d3002b57be7ae94649f554db41a8 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:35:40 -0800 Subject: [PATCH 142/971] :arrow_up: status-bar --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e526f24ee..7092a0b27 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.232.3", "snippets": "1.0.1", "spell-check": "0.65.0", - "status-bar": "0.83.0", + "status-bar": "0.83.1", "styleguide": "0.45.1", "symbols-view": "0.111.0", "tabs": "0.90.0", From e0ddba741ebf2668140440c6d9018c08758ec7bf Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:37:09 -0800 Subject: [PATCH 143/971] :arrow_up: spell-check --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7092a0b27..7045ff987 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "package-generator": "0.41.1", "settings-view": "0.232.3", "snippets": "1.0.1", - "spell-check": "0.65.0", + "spell-check": "0.66.1", "status-bar": "0.83.1", "styleguide": "0.45.1", "symbols-view": "0.111.0", From eccf5e69d8365a1bd0aa89a2c9aaac1c5fe14a4e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:38:12 -0800 Subject: [PATCH 144/971] :arrow_up: styleguide --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7045ff987..c40f6738c 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "snippets": "1.0.1", "spell-check": "0.66.1", "status-bar": "0.83.1", - "styleguide": "0.45.1", + "styleguide": "0.45.2", "symbols-view": "0.111.0", "tabs": "0.90.0", "timecop": "0.33.0", From c9909e551d872512e265ef32fd5f6c795d501940 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:38:46 -0800 Subject: [PATCH 145/971] :arrow_up: symbols-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c40f6738c..6db9be317 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "spell-check": "0.66.1", "status-bar": "0.83.1", "styleguide": "0.45.2", - "symbols-view": "0.111.0", + "symbols-view": "0.111.1", "tabs": "0.90.0", "timecop": "0.33.0", "tree-view": "0.201.1", From 7f65c50c85f1acda909c6fb88e3d3e4fbbc1fdda Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:39:21 -0800 Subject: [PATCH 146/971] :arrow_up: timecop --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6db9be317..143ccbaf1 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "styleguide": "0.45.2", "symbols-view": "0.111.1", "tabs": "0.90.0", - "timecop": "0.33.0", + "timecop": "0.33.1", "tree-view": "0.201.1", "update-package-dependencies": "0.10.0", "welcome": "0.33.0", From 096af6d5cc442c1330abd858d274cd829fa76311 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:39:55 -0800 Subject: [PATCH 147/971] :arrow_up: tree-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 143ccbaf1..e7fac52fe 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.111.1", "tabs": "0.90.0", "timecop": "0.33.1", - "tree-view": "0.201.1", + "tree-view": "0.201.2", "update-package-dependencies": "0.10.0", "welcome": "0.33.0", "whitespace": "0.32.1", From aa14d6b19e8ca5e3527d6c11bbf9991134701129 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:40:37 -0800 Subject: [PATCH 148/971] :arrow_up: welcome --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e7fac52fe..9275016bd 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "timecop": "0.33.1", "tree-view": "0.201.2", "update-package-dependencies": "0.10.0", - "welcome": "0.33.0", + "welcome": "0.33.1", "whitespace": "0.32.1", "wrap-guide": "0.38.1", "language-c": "0.51.1", From 2ef3f0adc229cf869b220679683e1690c1adaea5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:41:27 -0800 Subject: [PATCH 149/971] :arrow_up: whitespace --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9275016bd..cddd51ea3 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "tree-view": "0.201.2", "update-package-dependencies": "0.10.0", "welcome": "0.33.1", - "whitespace": "0.32.1", + "whitespace": "0.32.2", "wrap-guide": "0.38.1", "language-c": "0.51.1", "language-clojure": "0.19.1", From 54d3c72cb1b0266a2bcebd3ecf76bf9d368845d8 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 Feb 2016 17:42:20 -0800 Subject: [PATCH 150/971] :arrow_up: settings-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cddd51ea3..f4e8300b8 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "notifications": "0.62.2", "open-on-github": "0.41.1", "package-generator": "0.41.1", - "settings-view": "0.232.3", + "settings-view": "0.232.4", "snippets": "1.0.1", "spell-check": "0.66.1", "status-bar": "0.83.1", From 3d13af270aeb6b7d5cece115493b6dcb4c626f81 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Thu, 11 Feb 2016 21:46:35 -0500 Subject: [PATCH 151/971] :arrow_up: language-html@0.44.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f4e8300b8..27b962063 100644 --- a/package.json +++ b/package.json @@ -125,7 +125,7 @@ "language-gfm": "0.84.0", "language-git": "0.12.1", "language-go": "0.42.0", - "language-html": "0.44.0", + "language-html": "0.44.1", "language-hyperlink": "0.16.0", "language-java": "0.17.0", "language-javascript": "0.110.0", From 255b943d45344c13596e8b3420c142d29e1d14ba Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 11 Feb 2016 19:56:25 -0800 Subject: [PATCH 152/971] Add ability to clear IndexedDB state object store --- spec/state-store-spec.js | 16 +++++++++++++++- src/state-store.js | 40 ++++++++++++++++++++++++++++++++++------ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/spec/state-store-spec.js b/spec/state-store-spec.js index 955ceb767..95fdcb71b 100644 --- a/spec/state-store-spec.js +++ b/spec/state-store-spec.js @@ -21,7 +21,21 @@ describe("StateStore", () => { return store.load('no-such-key').then((value) => { expect(value).toBeNull() }) - }); + }) + + it("can clear the state object store", () => { + const store = new StateStore(databaseName, version) + return store.save('key', {foo:'bar'}) + .then(() => store.count()) + .then((count) => + expect(count).toBe(1) + ) + .then(() => store.clear()) + .then(() => store.count()) + .then((count) => { + expect(count).toBe(0) + }) + }) describe("when there is an error reading from the database", () => { it("rejects the promise returned by load", () => { diff --git a/src/state-store.js b/src/state-store.js index feefbbb34..2175bbe4c 100644 --- a/src/state-store.js +++ b/src/state-store.js @@ -25,9 +25,7 @@ class StateStore { save (key, value) { return this.dbPromise.then(db => { - if (!db) { - return - } + if (!db) return return new Promise((resolve, reject) => { var request = db.transaction(['states'], 'readwrite') @@ -42,9 +40,7 @@ class StateStore { load (key) { return this.dbPromise.then(db => { - if (!db) { - return null - } + if (!db) return return new Promise((resolve, reject) => { var request = db.transaction(['states']) @@ -60,4 +56,36 @@ class StateStore { }) }) } + + clear () { + return this.dbPromise.then(db => { + if (!db) return + + return new Promise((resolve, reject) => { + var request = db.transaction(['states'], 'readwrite') + .objectStore('states') + .clear() + + request.onsuccess = resolve + request.onerror = reject + }) + }) + } + + count () { + return this.dbPromise.then(db => { + if (!db) return + + return new Promise((resolve, reject) => { + var request = db.transaction(['states']) + .objectStore('states') + .count() + + request.onsuccess = () => { + resolve(request.result) + } + request.onerror = reject + }) + }) + } } From 795bb72ec8fa259eb6a1b744d4a2d7835ace7398 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Feb 2016 10:17:29 +0100 Subject: [PATCH 153/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 27b962063..64dcbfb3f 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.2.1", + "text-buffer": "8.3.0", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 9cd59e8a51c0e23354ff5b79be38cf03dd2a1d1b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Feb 2016 10:18:09 +0100 Subject: [PATCH 154/971] :arrow_up: autocomplete-plus --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 64dcbfb3f..1494fc6fa 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.0", "autocomplete-html": "0.7.2", - "autocomplete-plus": "2.25.0", + "autocomplete-plus": "2.27.1", "autocomplete-snippets": "1.10.0", "autoflow": "0.27.0", "autosave": "0.23.1", From 3e7de735804ae7709ecef671b922e7fa00db5a59 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 09:58:37 -0500 Subject: [PATCH 155/971] Bump the timeout. --- spec/text-editor-presenter-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 9253ff103..90ef56da0 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -91,7 +91,7 @@ describe "TextEditorPresenter", -> expectNoStateUpdate = (presenter, fn) -> expectStateUpdatedToBe(false, presenter, fn) waitsForStateToUpdate = (presenter, fn) -> - waitsFor "presenter state to update", 1000, (done) -> + waitsFor "presenter state to update", 2000, (done) -> fn?() disposable = presenter.onDidUpdateState -> disposable.dispose() From dd6a6be8b19315b65f5d39cd75957fefc98e33f8 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 10:56:36 -0500 Subject: [PATCH 156/971] Once more, with feeling. --- spec/text-editor-presenter-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 90ef56da0..d16e8dff4 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -91,7 +91,7 @@ describe "TextEditorPresenter", -> expectNoStateUpdate = (presenter, fn) -> expectStateUpdatedToBe(false, presenter, fn) waitsForStateToUpdate = (presenter, fn) -> - waitsFor "presenter state to update", 2000, (done) -> + waitsFor "presenter state to update", 5000, (done) -> fn?() disposable = presenter.onDidUpdateState -> disposable.dispose() From a3ec51f492272fdf69e9660bec552e97773e811d Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 11:46:43 -0500 Subject: [PATCH 157/971] Let's try rearranging this. --- spec/text-editor-presenter-spec.coffee | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index d16e8dff4..0c60ce30b 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -90,6 +90,15 @@ describe "TextEditorPresenter", -> expectNoStateUpdate = (presenter, fn) -> expectStateUpdatedToBe(false, presenter, fn) + waitsForStateToUpdate2 = (presenter, fn) -> + didUpdate = false + disposable = presenter.onDidUpdateState -> + disposable.dispose() + didUpdate = true + fn?() + waitsFor "presenter state to update", 5000, (done) -> + didUpdate + waitsForStateToUpdate = (presenter, fn) -> waitsFor "presenter state to update", 5000, (done) -> fn?() @@ -1680,7 +1689,7 @@ describe "TextEditorPresenter", -> blockDecoration1 = addBlockDecorationBeforeScreenRow(0) blockDecoration2 = addBlockDecorationBeforeScreenRow(1) - waitsForStateToUpdate presenter, -> + waitsForStateToUpdate2 presenter, -> presenter.setBlockDecorationDimensions(blockDecoration1, 0, 30) presenter.setBlockDecorationDimensions(blockDecoration2, 0, 10) @@ -1691,7 +1700,7 @@ describe "TextEditorPresenter", -> expect(stateForCursor(presenter, 3)).toBeUndefined() expect(stateForCursor(presenter, 4)).toBeUndefined() - waitsForStateToUpdate presenter, -> + waitsForStateToUpdate2 presenter, -> blockDecoration2.destroy() editor.setCursorBufferPosition([0, 0]) editor.insertNewline() From 07d35245e0cc716253ba520ffeaab6c2ebf30f19 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 11:51:48 -0500 Subject: [PATCH 158/971] Move it around again. --- spec/text-editor-presenter-spec.coffee | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 0c60ce30b..d8f98afd2 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -91,13 +91,11 @@ describe "TextEditorPresenter", -> expectNoStateUpdate = (presenter, fn) -> expectStateUpdatedToBe(false, presenter, fn) waitsForStateToUpdate2 = (presenter, fn) -> - didUpdate = false - disposable = presenter.onDidUpdateState -> - disposable.dispose() - didUpdate = true - fn?() waitsFor "presenter state to update", 5000, (done) -> - didUpdate + disposable = presenter.onDidUpdateState -> + disposable.dispose() + process.nextTick(done) + fn?() waitsForStateToUpdate = (presenter, fn) -> waitsFor "presenter state to update", 5000, (done) -> From 319043c93d230eb625ef5cb1041eb03a7e77ba06 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 12:11:52 -0500 Subject: [PATCH 159/971] Update this test too. --- spec/text-editor-presenter-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index d8f98afd2..8251a9593 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -3467,9 +3467,9 @@ describe "TextEditorPresenter", -> gutterName: 'test-gutter-2' class: 'test-class' marker4 = editor.markBufferRange([[0, 0], [1, 0]]) - decoration4 = editor.decorateMarker(marker4, decorationParams) + decoration4 = null - waitsForStateToUpdate presenter + waitsForStateToUpdate2 presenter, -> decoration4 = editor.decorateMarker(marker4, decorationParams) runs -> expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'}) From ace3ae8ed8e6da7d916b8cc726e29f3f597b3a16 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 12 Feb 2016 09:33:25 -0800 Subject: [PATCH 160/971] Remove unused Core Team Project Management labels Some Core Team labels aren't used anymore. To prevent misleading our users, we are removing these labels from the CONTRIBUTING document. --- CONTRIBUTING.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 466355903..b4519bcf1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -406,10 +406,6 @@ Please open an issue on `atom/atom` if you have suggestions for new labels, and | Label name | `atom/atom` :mag_right: | `atom`‑org :mag_right: | Description | | --- | --- | --- | --- | -| `in-progress` | [search][search-atom-repo-label-in-progress] | [search][search-atom-org-label-in-progress] | Tasks which the Atom core team is working on currently. | -| `on-deck` | [search][search-atom-repo-label-on-deck] | [search][search-atom-org-label-on-deck] | Tasks which the Atom core team plans to work on next. | -| `shipping` | [search][search-atom-repo-label-shipping] | [search][search-atom-org-label-shipping] | Tasks which the Atom core team completed and will be released in one of the next releases. | -| `post-1.0-roadmap` | [search][search-atom-repo-label-post-1.0-roadmap] | [search][search-atom-org-label-post-1.0-roadmap] | The Atom core team's roadmap post version 1.0.0. | | `atom` | [search][search-atom-repo-label-atom] | [search][search-atom-org-label-atom] | Topics discussed for prioritization at the next meeting of Atom core team members. | #### Pull Request Labels @@ -498,14 +494,6 @@ Please open an issue on `atom/atom` if you have suggestions for new labels, and [search-atom-org-label-deprecation-help]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Adeprecation-help [search-atom-repo-label-electron]: https://github.com/issues?q=is%3Aissue+repo%3Aatom%2Fatom+is%3Aopen+label%3Aelectron [search-atom-org-label-electron]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aelectron -[search-atom-repo-label-on-deck]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aon-deck -[search-atom-org-label-on-deck]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aon-deck -[search-atom-repo-label-in-progress]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Ain-progress -[search-atom-org-label-in-progress]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Ain-progress -[search-atom-repo-label-shipping]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Ashipping -[search-atom-org-label-shipping]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Ashipping -[search-atom-repo-label-post-1.0-roadmap]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Apost-1.0-roadmap -[search-atom-org-label-post-1.0-roadmap]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Apost-1.0-roadmap [search-atom-repo-label-atom]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aatom [search-atom-org-label-atom]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aatom [search-atom-repo-label-work-in-progress]: https://github.com/pulls?q=is%3Aopen+is%3Apr+repo%3Aatom%2Fatom+label%3Awork-in-progress From d9f659206c215fd1fe81576fff8a95b6351f12e3 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 12 Feb 2016 10:49:21 -0800 Subject: [PATCH 161/971] :arrow_up: tabs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1494fc6fa..4f735ad42 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "0.83.1", "styleguide": "0.45.2", "symbols-view": "0.111.1", - "tabs": "0.90.0", + "tabs": "0.90.2", "timecop": "0.33.1", "tree-view": "0.201.2", "update-package-dependencies": "0.10.0", From 8b14f5afdcedc20d7688e910d782db411f5a703c Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 13:58:38 -0500 Subject: [PATCH 162/971] Let's get some deterministic failures. --- spec/text-editor-presenter-spec.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 8251a9593..b3fd46048 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -20,6 +20,7 @@ describe "TextEditorPresenter", -> buffer = new TextBuffer(filePath: require.resolve('./fixtures/sample.js')) editor = atom.workspace.buildTextEditor({buffer}) + editor.setUpdatedSynchronously(true) waitsForPromise -> buffer.load() afterEach -> From 272ff19d7ba6acf35e7abb68abaf846601f90bd6 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 13:58:54 -0500 Subject: [PATCH 163/971] Decrease the timeout interval. --- spec/text-editor-presenter-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index b3fd46048..3d775bda9 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -92,14 +92,14 @@ describe "TextEditorPresenter", -> expectNoStateUpdate = (presenter, fn) -> expectStateUpdatedToBe(false, presenter, fn) waitsForStateToUpdate2 = (presenter, fn) -> - waitsFor "presenter state to update", 5000, (done) -> + waitsFor "presenter state to update", 1000, (done) -> disposable = presenter.onDidUpdateState -> disposable.dispose() process.nextTick(done) fn?() waitsForStateToUpdate = (presenter, fn) -> - waitsFor "presenter state to update", 5000, (done) -> + waitsFor "presenter state to update", 1000, (done) -> fn?() disposable = presenter.onDidUpdateState -> disposable.dispose() From 6813bf50a058509fdc089da527d402cf3f2fc44e Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 14:00:47 -0500 Subject: [PATCH 164/971] Use waitsForStateToUpdateAsync in a bunch of places. --- spec/text-editor-presenter-spec.coffee | 63 +++++++++++++------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 3d775bda9..c3abeb062 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -91,7 +91,7 @@ describe "TextEditorPresenter", -> expectNoStateUpdate = (presenter, fn) -> expectStateUpdatedToBe(false, presenter, fn) - waitsForStateToUpdate2 = (presenter, fn) -> + waitsForStateToUpdateAsync = (presenter, fn) -> waitsFor "presenter state to update", 1000, (done) -> disposable = presenter.onDidUpdateState -> disposable.dispose() @@ -1480,9 +1480,9 @@ describe "TextEditorPresenter", -> decoration1 = editor.decorateMarker(marker1, type: 'line', class: 'a') presenter = buildPresenter() marker2 = editor.addMarkerLayer(maintainHistory: true).markBufferRange([[4, 0], [6, 2]], invalidate: 'touch') - decoration2 = editor.decorateMarker(marker2, type: 'line', class: 'b') + decoration2 = null - waitsForStateToUpdate presenter + waitsForStateToUpdateAsync presenter, -> decoration2 = editor.decorateMarker(marker2, type: 'line', class: 'b') runs -> expect(lineStateForScreenRow(presenter, 3).decorationClasses).toBeNull() expect(lineStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] @@ -1490,14 +1490,14 @@ describe "TextEditorPresenter", -> expect(lineStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] expect(lineStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdate presenter, -> editor.getBuffer().insert([5, 0], 'x') + waitsForStateToUpdateAsync presenter, -> editor.getBuffer().insert([5, 0], 'x') runs -> expect(marker1.isValid()).toBe false expect(lineStateForScreenRow(presenter, 4).decorationClasses).toBeNull() expect(lineStateForScreenRow(presenter, 5).decorationClasses).toBeNull() expect(lineStateForScreenRow(presenter, 6).decorationClasses).toBeNull() - waitsForStateToUpdate presenter, -> editor.undo() + waitsForStateToUpdateAsync presenter, -> editor.undo() runs -> expect(lineStateForScreenRow(presenter, 3).decorationClasses).toBeNull() expect(lineStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] @@ -1505,7 +1505,7 @@ describe "TextEditorPresenter", -> expect(lineStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] expect(lineStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdate presenter, -> marker1.setBufferRange([[2, 0], [4, 2]]) + waitsForStateToUpdateAsync presenter, -> marker1.setBufferRange([[2, 0], [4, 2]]) runs -> expect(lineStateForScreenRow(presenter, 1).decorationClasses).toBeNull() expect(lineStateForScreenRow(presenter, 2).decorationClasses).toEqual ['a'] @@ -1515,7 +1515,7 @@ describe "TextEditorPresenter", -> expect(lineStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] expect(lineStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdate presenter, -> decoration1.destroy() + waitsForStateToUpdateAsync presenter, -> decoration1.destroy() runs -> expect(lineStateForScreenRow(presenter, 2).decorationClasses).toBeNull() expect(lineStateForScreenRow(presenter, 3).decorationClasses).toBeNull() @@ -1524,7 +1524,7 @@ describe "TextEditorPresenter", -> expect(lineStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] expect(lineStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdate presenter, -> marker2.destroy() + waitsForStateToUpdateAsync presenter, -> marker2.destroy() runs -> expect(lineStateForScreenRow(presenter, 2).decorationClasses).toBeNull() expect(lineStateForScreenRow(presenter, 3).decorationClasses).toBeNull() @@ -1688,7 +1688,7 @@ describe "TextEditorPresenter", -> blockDecoration1 = addBlockDecorationBeforeScreenRow(0) blockDecoration2 = addBlockDecorationBeforeScreenRow(1) - waitsForStateToUpdate2 presenter, -> + waitsForStateToUpdateAsync presenter, -> presenter.setBlockDecorationDimensions(blockDecoration1, 0, 30) presenter.setBlockDecorationDimensions(blockDecoration2, 0, 10) @@ -1699,7 +1699,7 @@ describe "TextEditorPresenter", -> expect(stateForCursor(presenter, 3)).toBeUndefined() expect(stateForCursor(presenter, 4)).toBeUndefined() - waitsForStateToUpdate2 presenter, -> + waitsForStateToUpdateAsync presenter, -> blockDecoration2.destroy() editor.setCursorBufferPosition([0, 0]) editor.insertNewline() @@ -2216,8 +2216,9 @@ describe "TextEditorPresenter", -> presenter = buildPresenter(explicitHeight: 30, scrollTop: 20, tileSize: 2) marker = editor.markBufferPosition([2, 2]) - highlight = editor.decorateMarker(marker, type: 'highlight', class: 'a') - waitsForStateToUpdate presenter, -> + highlight = null + waitsForStateToUpdateAsync presenter, -> + highlight = editor.decorateMarker(marker, type: 'highlight', class: 'a') marker.setBufferRange([[2, 2], [5, 2]]) highlight.flash('b', 500) runs -> @@ -2232,7 +2233,7 @@ describe "TextEditorPresenter", -> flashCount: 1 } - waitsForStateToUpdate presenter, -> highlight.flash('c', 600) + waitsForStateToUpdateAsync presenter, -> highlight.flash('c', 600) runs -> expectValues stateForHighlightInTile(presenter, highlight, 2), { flashClass: 'c' @@ -2479,7 +2480,7 @@ describe "TextEditorPresenter", -> } # Change range - waitsForStateToUpdate presenter, -> marker.setBufferRange([[2, 13], [4, 6]]) + waitsForStateToUpdateAsync presenter, -> marker.setBufferRange([[2, 13], [4, 6]]) runs -> expectValues stateForOverlay(presenter, decoration), { item: item @@ -2487,12 +2488,12 @@ describe "TextEditorPresenter", -> } # Valid -> invalid - waitsForStateToUpdate presenter, -> editor.getBuffer().insert([2, 14], 'x') + waitsForStateToUpdateAsync presenter, -> editor.getBuffer().insert([2, 14], 'x') runs -> expect(stateForOverlay(presenter, decoration)).toBeUndefined() # Invalid -> valid - waitsForStateToUpdate presenter, -> editor.undo() + waitsForStateToUpdateAsync presenter, -> editor.undo() runs -> expectValues stateForOverlay(presenter, decoration), { item: item @@ -2500,7 +2501,7 @@ describe "TextEditorPresenter", -> } # Reverse direction - waitsForStateToUpdate presenter, -> marker.setBufferRange([[2, 13], [4, 6]], reversed: true) + waitsForStateToUpdateAsync presenter, -> marker.setBufferRange([[2, 13], [4, 6]], reversed: true) runs -> expectValues stateForOverlay(presenter, decoration), { item: item @@ -2508,13 +2509,13 @@ describe "TextEditorPresenter", -> } # Destroy - waitsForStateToUpdate presenter, -> decoration.destroy() + waitsForStateToUpdateAsync presenter, -> decoration.destroy() runs -> expect(stateForOverlay(presenter, decoration)).toBeUndefined() # Add decoration2 = null - waitsForStateToUpdate presenter, -> decoration2 = editor.decorateMarker(marker, {type: 'overlay', item}) + waitsForStateToUpdateAsync presenter, -> decoration2 = editor.decorateMarker(marker, {type: 'overlay', item}) runs -> expectValues stateForOverlay(presenter, decoration2), { item: item @@ -2979,7 +2980,7 @@ describe "TextEditorPresenter", -> presenter.setBlockDecorationDimensions(blockDecoration5, 0, 50) presenter.setBlockDecorationDimensions(blockDecoration6, 0, 60) - waitsForStateToUpdate presenter + waitsForStateToUpdateAsync presenter, -> presenter.setBlockDecorationDimensions(blockDecoration6, 0, 60) runs -> expect(lineNumberStateForScreenRow(presenter, 0).blockDecorationsHeight).toBe(10) expect(lineNumberStateForScreenRow(presenter, 1).blockDecorationsHeight).toBe(0) @@ -2994,7 +2995,7 @@ describe "TextEditorPresenter", -> expect(lineNumberStateForScreenRow(presenter, 10).blockDecorationsHeight).toBe(0) expect(lineNumberStateForScreenRow(presenter, 11).blockDecorationsHeight).toBe(60) - waitsForStateToUpdate presenter, -> + waitsForStateToUpdateAsync presenter, -> blockDecoration1.getMarker().setHeadBufferPosition([1, 0]) blockDecoration2.getMarker().setHeadBufferPosition([5, 0]) blockDecoration3.getMarker().setHeadBufferPosition([9, 0]) @@ -3013,7 +3014,7 @@ describe "TextEditorPresenter", -> expect(lineNumberStateForScreenRow(presenter, 10).blockDecorationsHeight).toBe(0) expect(lineNumberStateForScreenRow(presenter, 11).blockDecorationsHeight).toBe(60) - waitsForStateToUpdate presenter, -> + waitsForStateToUpdateAsync presenter, -> blockDecoration1.destroy() blockDecoration3.destroy() @@ -3045,14 +3046,14 @@ describe "TextEditorPresenter", -> expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdate presenter, -> editor.getBuffer().insert([5, 0], 'x') + waitsForStateToUpdateAsync presenter, -> editor.getBuffer().insert([5, 0], 'x') runs -> expect(marker1.isValid()).toBe false expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() - waitsForStateToUpdate presenter, -> editor.undo() + waitsForStateToUpdateAsync presenter, -> editor.undo() runs -> expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] @@ -3060,7 +3061,7 @@ describe "TextEditorPresenter", -> expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdate presenter, -> marker1.setBufferRange([[2, 0], [4, 2]]) + waitsForStateToUpdateAsync presenter, -> marker1.setBufferRange([[2, 0], [4, 2]]) runs -> expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull() expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toEqual ['a'] @@ -3070,7 +3071,7 @@ describe "TextEditorPresenter", -> expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdate presenter, -> decoration1.destroy() + waitsForStateToUpdateAsync presenter, -> decoration1.destroy() runs -> expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toBeNull() expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() @@ -3079,7 +3080,7 @@ describe "TextEditorPresenter", -> expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdate presenter, -> marker2.destroy() + waitsForStateToUpdateAsync presenter, -> marker2.destroy() runs -> expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toBeNull() expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() @@ -3297,7 +3298,7 @@ describe "TextEditorPresenter", -> expect(decorationState[decoration3.id].item).toBe decorationItem expect(decorationState[decoration3.id].class).toBe 'test-class' - waitsForStateToUpdate presenter, -> blockDecoration1.destroy() + waitsForStateToUpdateAsync presenter, -> blockDecoration1.destroy() runs -> decorationState = getContentForGutterWithName(presenter, 'test-gutter') expect(decorationState[decoration1.id]).toBeUndefined() @@ -3416,7 +3417,7 @@ describe "TextEditorPresenter", -> gutterName: 'test-gutter-2' class: 'new-test-class' item: decorationItem - waitsForStateToUpdate presenter, -> decoration1.setProperties(newDecorationParams) + waitsForStateToUpdateAsync presenter, -> decoration1.setProperties(newDecorationParams) runs -> decorationState = getContentForGutterWithName(presenter, 'test-gutter') @@ -3470,7 +3471,7 @@ describe "TextEditorPresenter", -> marker4 = editor.markBufferRange([[0, 0], [1, 0]]) decoration4 = null - waitsForStateToUpdate2 presenter, -> decoration4 = editor.decorateMarker(marker4, decorationParams) + waitsForStateToUpdateAsync presenter, -> decoration4 = editor.decorateMarker(marker4, decorationParams) runs -> expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'}) @@ -3537,7 +3538,7 @@ describe "TextEditorPresenter", -> blockDecorationsHeight = Math.round(35.8 + 100.3 + 95.2) expect(getStylesForGutterWithName(presenter, 'line-number').scrollHeight).toBe(linesHeight + blockDecorationsHeight) - waitsForStateToUpdate presenter, -> blockDecoration3.destroy() + waitsForStateToUpdateAsync presenter, -> blockDecoration3.destroy() runs -> blockDecorationsHeight = Math.round(35.8 + 100.3) expect(getStylesForGutterWithName(presenter, 'line-number').scrollHeight).toBe(linesHeight + blockDecorationsHeight) From 98a051080bea623d0a195028e442d4675ff8f737 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 14:43:55 -0500 Subject: [PATCH 165/971] Failing test. --- spec/git-spec.coffee | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index e6b5d4df6..f239f763a 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -259,6 +259,37 @@ describe "GitRepository", -> expect(repo.isStatusModified(status)).toBe false expect(repo.isStatusNew(status)).toBe false + fit 'caches the proper statuses when multiple project are open', -> + otherWorkingDirectory = copyRepository() + + atom.project.setPaths([workingDirectory, otherWorkingDirectory]) + + waitsForPromise -> + atom.workspace.open('b.txt') + + statusHandler = null + runs -> + repo = atom.project.getRepositories()[0] + console.log(repo.getPath()) + + statusHandler = jasmine.createSpy('statusHandler') + repo.onDidChangeStatuses statusHandler + repo.refreshStatus() + + waitsFor -> + statusHandler.callCount > 0 + + runs -> + subDir = path.join(workingDirectory, 'dir') + fs.mkdirSync(subDir) + + filePath = path.join(subDir, 'b.txt') + fs.writeFileSync(filePath, '') + + status = repo.getCachedPathStatus(filePath) + expect(repo.isStatusModified(status)).toBe true + expect(repo.isStatusNew(status)).toBe false + describe "buffer events", -> [editor] = [] From 25e58eb1a25ddf3356ebb05bcfd0589bdf15ee09 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 14:45:05 -0500 Subject: [PATCH 166/971] Stop logging. --- spec/git-spec.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index f239f763a..7d2c46729 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -270,7 +270,6 @@ describe "GitRepository", -> statusHandler = null runs -> repo = atom.project.getRepositories()[0] - console.log(repo.getPath()) statusHandler = jasmine.createSpy('statusHandler') repo.onDidChangeStatuses statusHandler From 0e8161f30edba29efe32c1f2d9c01993562799f9 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 14:45:21 -0500 Subject: [PATCH 167/971] Always search the repo itself. --- src/git-repository.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 44b86a433..0513c2293 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -484,8 +484,7 @@ class GitRepository relativeProjectPaths = @project?.getPaths() .map (path) => @relativize(path) - .filter (path) -> path.length > 0 - .map (path) -> path + '/**' + .map (path) -> if path.length > 0 then path + '/**' else '*' @statusTask?.terminate() @statusTask = Task.once @handlerPath, @getPath(), relativeProjectPaths, ({statuses, upstream, branch, submodules}) => From 72f3d0b30918a45f783ce702fa05af7e4f5c956f Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 14:45:26 -0500 Subject: [PATCH 168/971] Unfocused. --- spec/git-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 7d2c46729..22c40c19a 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -259,7 +259,7 @@ describe "GitRepository", -> expect(repo.isStatusModified(status)).toBe false expect(repo.isStatusNew(status)).toBe false - fit 'caches the proper statuses when multiple project are open', -> + it 'caches the proper statuses when multiple project are open', -> otherWorkingDirectory = copyRepository() atom.project.setPaths([workingDirectory, otherWorkingDirectory]) From 9fa766e01ac928ab7b9de682fa5459bbbb92c553 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 15:13:10 -0500 Subject: [PATCH 169/971] Don't use synchronous updates anymore. --- spec/text-editor-presenter-spec.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index c3abeb062..b0eb6d09b 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -20,7 +20,6 @@ describe "TextEditorPresenter", -> buffer = new TextBuffer(filePath: require.resolve('./fixtures/sample.js')) editor = atom.workspace.buildTextEditor({buffer}) - editor.setUpdatedSynchronously(true) waitsForPromise -> buffer.load() afterEach -> From 7ecdc390f21f0131c26b0c83f6c61cd6fee25482 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 12 Feb 2016 12:45:12 -0800 Subject: [PATCH 170/971] :arrow_up: archive-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4f735ad42..8ec83f54c 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "solarized-dark-syntax": "1.0.0", "solarized-light-syntax": "1.0.0", "about": "1.3.1", - "archive-view": "0.61.0", + "archive-view": "0.61.1", "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.0", "autocomplete-html": "0.7.2", From 82860a12a08eabe50812126e1c41e74db5f1cc8f Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 16:35:10 -0500 Subject: [PATCH 171/971] Fix waitsForStateToUpdate proper like. --- spec/text-editor-presenter-spec.coffee | 84 +++++++++++++------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index b0eb6d09b..5e51c7176 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -90,19 +90,12 @@ describe "TextEditorPresenter", -> expectNoStateUpdate = (presenter, fn) -> expectStateUpdatedToBe(false, presenter, fn) - waitsForStateToUpdateAsync = (presenter, fn) -> - waitsFor "presenter state to update", 1000, (done) -> - disposable = presenter.onDidUpdateState -> - disposable.dispose() - process.nextTick(done) - fn?() - waitsForStateToUpdate = (presenter, fn) -> waitsFor "presenter state to update", 1000, (done) -> - fn?() disposable = presenter.onDidUpdateState -> disposable.dispose() process.nextTick(done) + fn?() tiledContentContract = (stateFn) -> it "contains states for tiles that are visible on screen", -> @@ -1481,7 +1474,7 @@ describe "TextEditorPresenter", -> marker2 = editor.addMarkerLayer(maintainHistory: true).markBufferRange([[4, 0], [6, 2]], invalidate: 'touch') decoration2 = null - waitsForStateToUpdateAsync presenter, -> decoration2 = editor.decorateMarker(marker2, type: 'line', class: 'b') + waitsForStateToUpdate presenter, -> decoration2 = editor.decorateMarker(marker2, type: 'line', class: 'b') runs -> expect(lineStateForScreenRow(presenter, 3).decorationClasses).toBeNull() expect(lineStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] @@ -1489,14 +1482,14 @@ describe "TextEditorPresenter", -> expect(lineStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] expect(lineStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdateAsync presenter, -> editor.getBuffer().insert([5, 0], 'x') + waitsForStateToUpdate presenter, -> editor.getBuffer().insert([5, 0], 'x') runs -> expect(marker1.isValid()).toBe false expect(lineStateForScreenRow(presenter, 4).decorationClasses).toBeNull() expect(lineStateForScreenRow(presenter, 5).decorationClasses).toBeNull() expect(lineStateForScreenRow(presenter, 6).decorationClasses).toBeNull() - waitsForStateToUpdateAsync presenter, -> editor.undo() + waitsForStateToUpdate presenter, -> editor.undo() runs -> expect(lineStateForScreenRow(presenter, 3).decorationClasses).toBeNull() expect(lineStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] @@ -1504,7 +1497,7 @@ describe "TextEditorPresenter", -> expect(lineStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] expect(lineStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdateAsync presenter, -> marker1.setBufferRange([[2, 0], [4, 2]]) + waitsForStateToUpdate presenter, -> marker1.setBufferRange([[2, 0], [4, 2]]) runs -> expect(lineStateForScreenRow(presenter, 1).decorationClasses).toBeNull() expect(lineStateForScreenRow(presenter, 2).decorationClasses).toEqual ['a'] @@ -1514,7 +1507,7 @@ describe "TextEditorPresenter", -> expect(lineStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] expect(lineStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdateAsync presenter, -> decoration1.destroy() + waitsForStateToUpdate presenter, -> decoration1.destroy() runs -> expect(lineStateForScreenRow(presenter, 2).decorationClasses).toBeNull() expect(lineStateForScreenRow(presenter, 3).decorationClasses).toBeNull() @@ -1523,7 +1516,7 @@ describe "TextEditorPresenter", -> expect(lineStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] expect(lineStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdateAsync presenter, -> marker2.destroy() + waitsForStateToUpdate presenter, -> marker2.destroy() runs -> expect(lineStateForScreenRow(presenter, 2).decorationClasses).toBeNull() expect(lineStateForScreenRow(presenter, 3).decorationClasses).toBeNull() @@ -1687,7 +1680,7 @@ describe "TextEditorPresenter", -> blockDecoration1 = addBlockDecorationBeforeScreenRow(0) blockDecoration2 = addBlockDecorationBeforeScreenRow(1) - waitsForStateToUpdateAsync presenter, -> + waitsForStateToUpdate presenter, -> presenter.setBlockDecorationDimensions(blockDecoration1, 0, 30) presenter.setBlockDecorationDimensions(blockDecoration2, 0, 10) @@ -1698,7 +1691,7 @@ describe "TextEditorPresenter", -> expect(stateForCursor(presenter, 3)).toBeUndefined() expect(stateForCursor(presenter, 4)).toBeUndefined() - waitsForStateToUpdateAsync presenter, -> + waitsForStateToUpdate presenter, -> blockDecoration2.destroy() editor.setCursorBufferPosition([0, 0]) editor.insertNewline() @@ -2157,31 +2150,40 @@ describe "TextEditorPresenter", -> } # becoming empty - waitsForStateToUpdate presenter, -> editor.getSelections()[1].clear(autoscroll: false) + runs -> + editor.getSelections()[1].clear(autoscroll: false) + waitsForStateToUpdate presenter runs -> expectUndefinedStateForSelection(presenter, 1) # becoming non-empty - waitsForStateToUpdate presenter, -> editor.getSelections()[1].setBufferRange([[2, 4], [2, 6]], autoscroll: false) + runs -> + editor.getSelections()[1].setBufferRange([[2, 4], [2, 6]], autoscroll: false) + waitsForStateToUpdate presenter runs -> expectValues stateForSelectionInTile(presenter, 1, 2), { regions: [{top: 0, left: 4 * 10, width: 2 * 10, height: 10}] } # moving out of view - waitsForStateToUpdate presenter, -> editor.getSelections()[1].setBufferRange([[3, 4], [3, 6]], autoscroll: false) + runs -> + editor.getSelections()[1].setBufferRange([[3, 4], [3, 6]], autoscroll: false) + waitsForStateToUpdate presenter runs -> expectUndefinedStateForSelection(presenter, 1) # adding - waitsForStateToUpdate presenter, -> editor.addSelectionForBufferRange([[1, 4], [1, 6]], autoscroll: false) + runs -> editor.addSelectionForBufferRange([[1, 4], [1, 6]], autoscroll: false) + waitsForStateToUpdate presenter runs -> expectValues stateForSelectionInTile(presenter, 2, 0), { regions: [{top: 10, left: 4 * 10, width: 2 * 10, height: 10}] } # moving added selection - waitsForStateToUpdate presenter, -> editor.getSelections()[2].setBufferRange([[1, 4], [1, 8]], autoscroll: false) + runs -> + editor.getSelections()[2].setBufferRange([[1, 4], [1, 8]], autoscroll: false) + waitsForStateToUpdate presenter destroyedSelection = null runs -> @@ -2216,7 +2218,7 @@ describe "TextEditorPresenter", -> marker = editor.markBufferPosition([2, 2]) highlight = null - waitsForStateToUpdateAsync presenter, -> + waitsForStateToUpdate presenter, -> highlight = editor.decorateMarker(marker, type: 'highlight', class: 'a') marker.setBufferRange([[2, 2], [5, 2]]) highlight.flash('b', 500) @@ -2232,7 +2234,7 @@ describe "TextEditorPresenter", -> flashCount: 1 } - waitsForStateToUpdateAsync presenter, -> highlight.flash('c', 600) + waitsForStateToUpdate presenter, -> highlight.flash('c', 600) runs -> expectValues stateForHighlightInTile(presenter, highlight, 2), { flashClass: 'c' @@ -2479,7 +2481,7 @@ describe "TextEditorPresenter", -> } # Change range - waitsForStateToUpdateAsync presenter, -> marker.setBufferRange([[2, 13], [4, 6]]) + waitsForStateToUpdate presenter, -> marker.setBufferRange([[2, 13], [4, 6]]) runs -> expectValues stateForOverlay(presenter, decoration), { item: item @@ -2487,12 +2489,12 @@ describe "TextEditorPresenter", -> } # Valid -> invalid - waitsForStateToUpdateAsync presenter, -> editor.getBuffer().insert([2, 14], 'x') + waitsForStateToUpdate presenter, -> editor.getBuffer().insert([2, 14], 'x') runs -> expect(stateForOverlay(presenter, decoration)).toBeUndefined() # Invalid -> valid - waitsForStateToUpdateAsync presenter, -> editor.undo() + waitsForStateToUpdate presenter, -> editor.undo() runs -> expectValues stateForOverlay(presenter, decoration), { item: item @@ -2500,7 +2502,7 @@ describe "TextEditorPresenter", -> } # Reverse direction - waitsForStateToUpdateAsync presenter, -> marker.setBufferRange([[2, 13], [4, 6]], reversed: true) + waitsForStateToUpdate presenter, -> marker.setBufferRange([[2, 13], [4, 6]], reversed: true) runs -> expectValues stateForOverlay(presenter, decoration), { item: item @@ -2508,13 +2510,13 @@ describe "TextEditorPresenter", -> } # Destroy - waitsForStateToUpdateAsync presenter, -> decoration.destroy() + waitsForStateToUpdate presenter, -> decoration.destroy() runs -> expect(stateForOverlay(presenter, decoration)).toBeUndefined() # Add decoration2 = null - waitsForStateToUpdateAsync presenter, -> decoration2 = editor.decorateMarker(marker, {type: 'overlay', item}) + waitsForStateToUpdate presenter, -> decoration2 = editor.decorateMarker(marker, {type: 'overlay', item}) runs -> expectValues stateForOverlay(presenter, decoration2), { item: item @@ -2979,7 +2981,7 @@ describe "TextEditorPresenter", -> presenter.setBlockDecorationDimensions(blockDecoration5, 0, 50) presenter.setBlockDecorationDimensions(blockDecoration6, 0, 60) - waitsForStateToUpdateAsync presenter, -> presenter.setBlockDecorationDimensions(blockDecoration6, 0, 60) + waitsForStateToUpdate presenter, -> presenter.setBlockDecorationDimensions(blockDecoration6, 0, 60) runs -> expect(lineNumberStateForScreenRow(presenter, 0).blockDecorationsHeight).toBe(10) expect(lineNumberStateForScreenRow(presenter, 1).blockDecorationsHeight).toBe(0) @@ -2994,7 +2996,7 @@ describe "TextEditorPresenter", -> expect(lineNumberStateForScreenRow(presenter, 10).blockDecorationsHeight).toBe(0) expect(lineNumberStateForScreenRow(presenter, 11).blockDecorationsHeight).toBe(60) - waitsForStateToUpdateAsync presenter, -> + waitsForStateToUpdate presenter, -> blockDecoration1.getMarker().setHeadBufferPosition([1, 0]) blockDecoration2.getMarker().setHeadBufferPosition([5, 0]) blockDecoration3.getMarker().setHeadBufferPosition([9, 0]) @@ -3013,7 +3015,7 @@ describe "TextEditorPresenter", -> expect(lineNumberStateForScreenRow(presenter, 10).blockDecorationsHeight).toBe(0) expect(lineNumberStateForScreenRow(presenter, 11).blockDecorationsHeight).toBe(60) - waitsForStateToUpdateAsync presenter, -> + waitsForStateToUpdate presenter, -> blockDecoration1.destroy() blockDecoration3.destroy() @@ -3045,14 +3047,14 @@ describe "TextEditorPresenter", -> expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdateAsync presenter, -> editor.getBuffer().insert([5, 0], 'x') + waitsForStateToUpdate presenter, -> editor.getBuffer().insert([5, 0], 'x') runs -> expect(marker1.isValid()).toBe false expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toBeNull() expect(lineNumberStateForScreenRow(presenter, 5).decorationClasses).toBeNull() expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toBeNull() - waitsForStateToUpdateAsync presenter, -> editor.undo() + waitsForStateToUpdate presenter, -> editor.undo() runs -> expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() expect(lineNumberStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b'] @@ -3060,7 +3062,7 @@ describe "TextEditorPresenter", -> expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['a', 'b'] expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdateAsync presenter, -> marker1.setBufferRange([[2, 0], [4, 2]]) + waitsForStateToUpdate presenter, -> marker1.setBufferRange([[2, 0], [4, 2]]) runs -> expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull() expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toEqual ['a'] @@ -3070,7 +3072,7 @@ describe "TextEditorPresenter", -> expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdateAsync presenter, -> decoration1.destroy() + waitsForStateToUpdate presenter, -> decoration1.destroy() runs -> expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toBeNull() expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() @@ -3079,7 +3081,7 @@ describe "TextEditorPresenter", -> expect(lineNumberStateForScreenRow(presenter, 6).decorationClasses).toEqual ['b'] expect(lineNumberStateForScreenRow(presenter, 7).decorationClasses).toBeNull() - waitsForStateToUpdateAsync presenter, -> marker2.destroy() + waitsForStateToUpdate presenter, -> marker2.destroy() runs -> expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toBeNull() expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() @@ -3297,7 +3299,7 @@ describe "TextEditorPresenter", -> expect(decorationState[decoration3.id].item).toBe decorationItem expect(decorationState[decoration3.id].class).toBe 'test-class' - waitsForStateToUpdateAsync presenter, -> blockDecoration1.destroy() + waitsForStateToUpdate presenter, -> blockDecoration1.destroy() runs -> decorationState = getContentForGutterWithName(presenter, 'test-gutter') expect(decorationState[decoration1.id]).toBeUndefined() @@ -3416,7 +3418,7 @@ describe "TextEditorPresenter", -> gutterName: 'test-gutter-2' class: 'new-test-class' item: decorationItem - waitsForStateToUpdateAsync presenter, -> decoration1.setProperties(newDecorationParams) + waitsForStateToUpdate presenter, -> decoration1.setProperties(newDecorationParams) runs -> decorationState = getContentForGutterWithName(presenter, 'test-gutter') @@ -3470,7 +3472,7 @@ describe "TextEditorPresenter", -> marker4 = editor.markBufferRange([[0, 0], [1, 0]]) decoration4 = null - waitsForStateToUpdateAsync presenter, -> decoration4 = editor.decorateMarker(marker4, decorationParams) + waitsForStateToUpdate presenter, -> decoration4 = editor.decorateMarker(marker4, decorationParams) runs -> expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'}) @@ -3537,7 +3539,7 @@ describe "TextEditorPresenter", -> blockDecorationsHeight = Math.round(35.8 + 100.3 + 95.2) expect(getStylesForGutterWithName(presenter, 'line-number').scrollHeight).toBe(linesHeight + blockDecorationsHeight) - waitsForStateToUpdateAsync presenter, -> blockDecoration3.destroy() + waitsForStateToUpdate presenter, -> blockDecoration3.destroy() runs -> blockDecorationsHeight = Math.round(35.8 + 100.3) expect(getStylesForGutterWithName(presenter, 'line-number').scrollHeight).toBe(linesHeight + blockDecorationsHeight) From cc3f6b888f567f192dae7756c49ec2f838588e3e Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 12 Feb 2016 16:35:16 -0500 Subject: [PATCH 172/971] Don't need to duplicate this line. --- spec/text-editor-presenter-spec.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 5e51c7176..c353e21c0 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -2979,7 +2979,6 @@ describe "TextEditorPresenter", -> presenter.setBlockDecorationDimensions(blockDecoration4, 0, 35) presenter.setBlockDecorationDimensions(blockDecoration4, 0, 40) presenter.setBlockDecorationDimensions(blockDecoration5, 0, 50) - presenter.setBlockDecorationDimensions(blockDecoration6, 0, 60) waitsForStateToUpdate presenter, -> presenter.setBlockDecorationDimensions(blockDecoration6, 0, 60) runs -> From 22c9fe7cb7e8fdc76a98096c0f3212ed21ab03aa Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Fri, 12 Feb 2016 14:06:06 -0800 Subject: [PATCH 173/971] :fire: trailing white space --- src/atom-environment.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index f7869f9af..248ee7e6a 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -648,7 +648,7 @@ class AtomEnvironment extends Model @registerDefaultTargetForKeymaps() @packages.loadPackages() - + @document.body.appendChild(@views.getView(@workspace)) @watchProjectPath() From 427dba57adc771032ff2c978a2d59cf3d8e8817a Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Fri, 12 Feb 2016 14:07:21 -0800 Subject: [PATCH 174/971] Remove unused StorageFolder::store method (moved state to IndexedDB) --- src/storage-folder.coffee | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/storage-folder.coffee b/src/storage-folder.coffee index ff3474198..06beae56a 100644 --- a/src/storage-folder.coffee +++ b/src/storage-folder.coffee @@ -11,11 +11,6 @@ class StorageFolder fs.writeFileSync(@pathForKey(name), JSON.stringify(object), 'utf8') - store: (name, object) -> - return unless @path? - - fs.writeFile(@pathForKey(name), JSON.stringify(object), 'utf8') - load: (name) -> return unless @path? From 81c15bd8b41778fe6373bd34357667d4f3f4b53a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 12 Feb 2016 14:19:03 -0800 Subject: [PATCH 175/971] Make --profile-startup flag work w/ async initializion --- static/index.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/static/index.js b/static/index.js index 87f7e04af..4e5494b18 100644 --- a/static/index.js +++ b/static/index.js @@ -85,7 +85,7 @@ var initialize = require(loadSettings.windowInitializationScript) - initialize({blobStore: blobStore}).then(function () { + return initialize({blobStore: blobStore}).then(function () { require('electron').ipcRenderer.send('window-command', 'window:loaded') }) } @@ -115,16 +115,12 @@ function profileStartup (loadSettings, initialTime) { function profile () { console.profile('startup') - try { - var startTime = Date.now() - setupWindow(loadSettings) + var startTime = Date.now() + setupWindow(loadSettings).then(function () { setLoadTime(Date.now() - startTime + initialTime) - } catch (error) { - handleSetupError(error) - } finally { console.profileEnd('startup') console.log('Switch to the Profiles tab to view the created startup profile') - } + }) } var currentWindow = require('electron').remote.getCurrentWindow() From 24dc4c6c738c5b5d49506461f38b501b702a99ac Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 11 Feb 2016 19:56:50 -0800 Subject: [PATCH 176/971] Add command-line argument to clear state in IndexedDB --- src/browser/main.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/browser/main.coffee b/src/browser/main.coffee index bbce1d87f..c1df3455f 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -123,6 +123,7 @@ parseCommandLine = -> options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.') options.string('socket-path') options.string('user-data-dir') + options.boolean('clear-state').describe('clear-state', 'Delete all Atom environment state.') args = options.argv @@ -146,6 +147,7 @@ parseCommandLine = -> socketPath = args['socket-path'] userDataDir = args['user-data-dir'] profileStartup = args['profile-startup'] + clearState = args['clear-state'] urlsToOpen = [] devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH ? path.join(app.getHomeDir(), 'github', 'atom') setPortable = args.portable @@ -169,6 +171,7 @@ parseCommandLine = -> {resourcePath, devResourcePath, pathsToOpen, urlsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, safeMode, newWindow, - logFile, socketPath, userDataDir, profileStartup, timeout, setPortable} + logFile, socketPath, userDataDir, profileStartup, timeout, setPortable, + clearState} start() From 6b24f4bfa7779aa0fbeef4a6100bf309802c6fbe Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 12 Feb 2016 18:36:12 -0800 Subject: [PATCH 177/971] :memo: Fix atom.workspace.open split parameter mismatch --- src/workspace.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index ebaf1b337..79757a43b 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -394,7 +394,7 @@ class Workspace extends Model # initially. Defaults to `0`. # * `initialColumn` A {Number} indicating which column to move the cursor to # initially. Defaults to `0`. - # * `split` Either 'left', 'right', 'top' or 'bottom'. + # * `split` Either 'left', 'right', 'up' or 'down'. # If 'left', the item will be opened in leftmost pane of the current active pane's row. # If 'right', the item will be opened in the rightmost pane of the current active pane's row. # If 'up', the item will be opened in topmost pane of the current active pane's row. From 0b13cb00e1f299f3c5d7f7b81fc15d6cd7d5a8fe Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 12 Feb 2016 18:45:05 -0800 Subject: [PATCH 178/971] :memo: Make the atom.workspace.open split documentation match behavior --- src/workspace.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index 79757a43b..79cfb3e8e 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -396,9 +396,9 @@ class Workspace extends Model # initially. Defaults to `0`. # * `split` Either 'left', 'right', 'up' or 'down'. # If 'left', the item will be opened in leftmost pane of the current active pane's row. - # If 'right', the item will be opened in the rightmost pane of the current active pane's row. - # If 'up', the item will be opened in topmost pane of the current active pane's row. - # If 'down', the item will be opened in the bottommost pane of the current active pane's row. + # If 'right', the item will be opened in the rightmost pane of the current active pane's row. If only one pane exists in the row, a new pane will be created. + # If 'up', the item will be opened in topmost pane of the current active pane's column. + # If 'down', the item will be opened in the bottommost pane of the current active pane's column. If only one pane exists in the column, a new pane will be created. # * `activatePane` A {Boolean} indicating whether to call {Pane::activate} on # containing pane. Defaults to `true`. # * `activateItem` A {Boolean} indicating whether to call {Pane::activateItem} From 57ea5c0fc400cb81c22c1c2b2e8acf57eb36157e Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 12 Feb 2016 18:54:12 -0800 Subject: [PATCH 179/971] Fix typo in function call --- src/pane.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane.coffee b/src/pane.coffee index 0a5cca4c3..6c3612e5e 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -713,7 +713,7 @@ class Pane extends Model if @parent.orientation is 'vertical' bottommostSibling = last(@parent.children) if bottommostSibling instanceof PaneAxis - @splitRight() + @splitDown() else bottommostSibling else From f8ae09b1122ded5de3d4958630b485a2e1fd82a0 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Sun, 14 Feb 2016 15:10:07 -0800 Subject: [PATCH 180/971] Register Atom for file associations in Windows #6177 #4893 #6860 --- src/browser/squirrel-update.coffee | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/browser/squirrel-update.coffee b/src/browser/squirrel-update.coffee index 3660158fc..f9684ce49 100644 --- a/src/browser/squirrel-update.coffee +++ b/src/browser/squirrel-update.coffee @@ -20,6 +20,7 @@ else fileKeyPath = 'HKCU\\Software\\Classes\\*\\shell\\Atom' directoryKeyPath = 'HKCU\\Software\\Classes\\directory\\shell\\Atom' backgroundKeyPath = 'HKCU\\Software\\Classes\\directory\\background\\shell\\Atom' +applicationsKeyPath = 'HKCU\\Software\\Classes\\Applications\\atom.exe' environmentKeyPath = 'HKCU\\Environment' # Spawn a command and invoke the callback when it completes with an error @@ -64,6 +65,10 @@ installContextMenu = (callback) -> args.push('/f') spawnReg(args, callback) + installFileHandler = (callback) -> + args = ["#{applicationsKeyPath}\\shell\\open\\command", '/ve', '/d', "\"#{process.execPath}\" \"%1\""] + addToRegistry(args, callback) + installMenu = (keyPath, arg, callback) -> args = [keyPath, '/ve', '/d', 'Open with Atom'] addToRegistry args, -> @@ -74,7 +79,8 @@ installContextMenu = (callback) -> installMenu fileKeyPath, '%1', -> installMenu directoryKeyPath, '%1', -> - installMenu(backgroundKeyPath, '%V', callback) + installMenu backgroundKeyPath, '%V', -> + installFileHandler(callback) isAscii = (text) -> index = 0 @@ -124,7 +130,8 @@ uninstallContextMenu = (callback) -> deleteFromRegistry fileKeyPath, -> deleteFromRegistry directoryKeyPath, -> - deleteFromRegistry(backgroundKeyPath, callback) + deleteFromRegistry backgroundKeyPath, -> + deleteFromRegistry(applicationsKeyPath, callback) # Add atom and apm to the PATH # From 2e8569d7b38abaa49fb583eecfb711c375b67e1e Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 15 Feb 2016 17:03:09 -0500 Subject: [PATCH 181/971] Port failing tests. Bring the failing tests over from https://github.com/atom/atom/pull/10758 and https://github.com/atom/atom/pull/10797. --- spec/git-repository-async-spec.js | 48 +++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index dfc3d5803..9999afc63 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -338,10 +338,10 @@ describe('GitRepositoryAsync', () => { }) describe('.refreshStatus()', () => { - let newPath, modifiedPath, cleanPath + let newPath, modifiedPath, cleanPath, workingDirectory beforeEach(() => { - const workingDirectory = copyRepository() + workingDirectory = copyRepository() repo = GitRepositoryAsync.open(workingDirectory) modifiedPath = path.join(workingDirectory, 'file.txt') newPath = path.join(workingDirectory, 'untracked.txt') @@ -362,7 +362,7 @@ describe('GitRepositoryAsync', () => { describe('in a repository with submodules', () => { beforeEach(() => { - const workingDirectory = copySubmoduleRepository() + workingDirectory = copySubmoduleRepository() repo = GitRepositoryAsync.open(workingDirectory) modifiedPath = path.join(workingDirectory, 'jstips', 'README.md') newPath = path.join(workingDirectory, 'You-Dont-Need-jQuery', 'untracked.txt') @@ -380,6 +380,48 @@ describe('GitRepositoryAsync', () => { expect(repo.isStatusModified(await repo.getCachedPathStatus(modifiedPath))).toBe(true) }) }) + + it('caches the proper statuses when a subdir is open', async () => { + const subDir = path.join(workingDirectory, 'dir') + fs.mkdirSync(subDir) + + const filePath = path.join(subDir, 'b.txt') + fs.writeFileSync(filePath, '') + + atom.project.setPaths([subDir]) + + await atom.workspace.open('b.txt') + + const repo = atom.project.getRepositories()[0].async + + await repo.refreshStatus() + + const status = await repo.getCachedPathStatus(filePath) + expect(repo.isStatusModified(status)).toBe(false) + expect(repo.isStatusNew(status)).toBe(false) + }) + + it('caches the proper statuses when multiple project are open', async () => { + const otherWorkingDirectory = copyRepository() + + atom.project.setPaths([workingDirectory, otherWorkingDirectory]) + + await atom.workspace.open('b.txt') + + const repo = atom.project.getRepositories()[0].async + + await repo.refreshStatus() + + const subDir = path.join(workingDirectory, 'dir') + fs.mkdirSync(subDir) + + const filePath = path.join(subDir, 'b.txt') + fs.writeFileSync(filePath, 'some content!') + + const status = await repo.getCachedPathStatus(filePath) + expect(repo.isStatusModified(status)).toBe(true) + expect(repo.isStatusNew(status)).toBe(false) + }) }) describe('.isProjectAtRoot()', () => { From a87aac00e4d1efd8afe5e5631e8ab98585ac8cc3 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 15 Feb 2016 17:04:03 -0500 Subject: [PATCH 182/971] Port the fixes too. --- src/git-repository-async.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 38792e02e..cf9f9d6fe 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -456,7 +456,10 @@ export default class GitRepositoryAsync { // information. getDirectoryStatus (directoryPath) { return this.relativizeToWorkingDirectory(directoryPath) - .then(relativePath => this._getStatus([relativePath])) + .then(relativePath => { + const pathspec = relativePath + '/**' + return this._getStatus([pathspec]) + }) .then(statuses => { return Promise.all(statuses.map(s => s.statusBit())).then(bits => { return bits @@ -800,7 +803,7 @@ export default class GitRepositoryAsync { } return Promise.all(projectPathsPromises) - .then(paths => paths.filter(p => p.length > 0)) + .then(paths => paths.map(p => p.length > 0 ? p + '/**' : '*')) .then(projectPaths => { return this._getStatus(projectPaths.length > 0 ? projectPaths : null) }) @@ -1032,7 +1035,7 @@ export default class GitRepositoryAsync { return this.getRepo() .then(repo => { const opts = { - flags: Git.Status.OPT.INCLUDE_UNTRACKED | Git.Status.OPT.RECURSE_UNTRACKED_DIRS | Git.Status.OPT.DISABLE_PATHSPEC_MATCH + flags: Git.Status.OPT.INCLUDE_UNTRACKED | Git.Status.OPT.RECURSE_UNTRACKED_DIRS } if (paths) { From f0a179fdb57a4de6e915dd5541c5f8feeb0a63bf Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 15 Feb 2016 17:19:32 -0500 Subject: [PATCH 183/971] Add the decoration after subscribing to state updates Same fixe as #10792. --- spec/text-editor-presenter-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index c353e21c0..57c674033 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -1336,9 +1336,9 @@ describe "TextEditorPresenter", -> presenter = buildPresenter() blockDecoration2 = addBlockDecorationBeforeScreenRow(3) blockDecoration3 = addBlockDecorationBeforeScreenRow(7) - blockDecoration4 = addBlockDecorationAfterScreenRow(7) + blockDecoration4 = null - waitsForStateToUpdate presenter + waitsForStateToUpdate presenter, blockDecoration4 = addBlockDecorationAfterScreenRow(7) runs -> expect(lineStateForScreenRow(presenter, 0).precedingBlockDecorations).toEqual([blockDecoration1]) expect(lineStateForScreenRow(presenter, 0).followingBlockDecorations).toEqual([]) From 47459948cff0f8da55197094b1d4fe941aabdceb Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 15 Feb 2016 21:28:25 -0800 Subject: [PATCH 184/971] =?UTF-8?q?Clear=20state=20in=20indexedDB=20if=20?= =?UTF-8?q?=E2=80=98clear-state=E2=80=99=20command-line=20flag=20is=20pass?= =?UTF-8?q?ed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/atom-environment.coffee | 6 ++++-- src/browser/atom-application.coffee | 17 +++++++++-------- src/browser/atom-window.coffee | 1 + 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index f7869f9af..cdb22b40d 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -122,13 +122,15 @@ class AtomEnvironment extends Model {@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params @loadTime = null - {devMode, safeMode, resourcePath} = @getLoadSettings() + {devMode, safeMode, resourcePath, clearState} = @getLoadSettings() @emitter = new Emitter @disposables = new CompositeDisposable @stateStore = new StateStore('AtomEnvironments', 1) + @stateStore.clear() if clearState + @deserializers = new DeserializerManager(this) @deserializeTimings = {} @@ -648,7 +650,7 @@ class AtomEnvironment extends Model @registerDefaultTargetForKeymaps() @packages.loadPackages() - + @document.body.appendChild(@views.getView(@workspace)) @watchProjectPath() diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 5ff24c458..891caf19d 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -65,7 +65,7 @@ class AtomApplication exit: (status) -> app.exit(status) constructor: (options) -> - {@resourcePath, @devResourcePath, @version, @devMode, @safeMode, @socketPath, timeout} = options + {@resourcePath, @devResourcePath, @version, @devMode, @safeMode, @socketPath, timeout, clearState} = options @socketPath = null if options.test @@ -89,16 +89,16 @@ class AtomApplication else @loadState(options) or @openPath(options) - openWithOptions: ({pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout}) -> + openWithOptions: ({pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearState}) -> if test @runTests({headless: true, devMode, @resourcePath, executedFrom, pathsToOpen, logFile, timeout}) else if pathsToOpen.length > 0 - @openPaths({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup}) + @openPaths({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearState}) else if urlsToOpen.length > 0 @openUrl({urlToOpen, devMode, safeMode}) for urlToOpen in urlsToOpen else # Always open a editor window if this is the first instance of Atom. - @openPath({pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup}) + @openPath({pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearState}) # Public: Removes the {AtomWindow} from the global window list. removeWindow: (window) -> @@ -387,8 +387,8 @@ class AtomApplication # :safeMode - Boolean to control the opened window's safe mode. # :profileStartup - Boolean to control creating a profile of the startup time. # :window - {AtomWindow} to open file paths in. - openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window} = {}) -> - @openPaths({pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window}) + openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearState} = {}) -> + @openPaths({pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearState}) # Public: Opens multiple paths, in existing windows if possible. # @@ -400,9 +400,10 @@ class AtomApplication # :safeMode - Boolean to control the opened window's safe mode. # :windowDimensions - Object with height and width keys. # :window - {AtomWindow} to open file paths in. - openPaths: ({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window}={}) -> + openPaths: ({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearState}={}) -> devMode = Boolean(devMode) safeMode = Boolean(safeMode) + clearState = Boolean(clearState) locationsToOpen = (@locationForPathToOpen(pathToOpen, executedFrom) for pathToOpen in pathsToOpen) pathsToOpen = (locationToOpen.pathToOpen for locationToOpen in locationsToOpen) @@ -435,7 +436,7 @@ class AtomApplication windowInitializationScript ?= require.resolve('../initialize-application-window') resourcePath ?= @resourcePath windowDimensions ?= @getDimensionsForNewWindow() - openedWindow = new AtomWindow({locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup}) + openedWindow = new AtomWindow({locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup, clearState}) if pidToKillWhenClosed? @pidsToOpenWindows[pidToKillWhenClosed] = openedWindow diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 20ba2ad5c..35ccabb8a 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -50,6 +50,7 @@ class AtomWindow loadSettings.safeMode ?= false loadSettings.atomHome = process.env.ATOM_HOME loadSettings.firstLoad = true + loadSettings.clearState ?= false # Only send to the first non-spec window created if @constructor.includeShellLoadTime and not @isSpec From 702af8cd1635a016c1a374d621013c32f96bc292 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 15 Feb 2016 21:46:25 -0800 Subject: [PATCH 185/971] Use pre-released text-buffer version 8.3.1-0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9ccae78a3..38956f362 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.3.0-0", + "text-buffer": "8.3.1-0", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 63270f417432f12ffeacf93f42c77308ea8e6211 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 16 Feb 2016 14:35:12 +0100 Subject: [PATCH 186/971] Add TextEditor.prototype.cursorsForScreenRowRange So that `TextEditorPresenter` can avoid to scan through all the cursors to understand whether they're visible on screen. This dramatically reduces the calls to `getScreenRange` and, thus, makes `updateCursorsState` faster for multi-cursor edits. --- src/text-editor-presenter.coffee | 16 ++++++---------- src/text-editor.coffee | 10 ++++++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index b13bf9036..a36c860ed 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -432,18 +432,14 @@ class TextEditorPresenter return updateCursorsState: -> - @state.content.cursors = {} - @updateCursorState(cursor) for cursor in @model.cursors # using property directly to avoid allocation - return - - updateCursorState: (cursor) -> return unless @startRow? and @endRow? and @hasPixelRectRequirements() and @baseCharacterWidth? - screenRange = cursor.getScreenRange() - return unless cursor.isVisible() and @startRow <= screenRange.start.row < @endRow - pixelRect = @pixelRectForScreenRange(screenRange) - pixelRect.width = Math.round(@baseCharacterWidth) if pixelRect.width is 0 - @state.content.cursors[cursor.id] = pixelRect + @state.content.cursors = {} + for cursor in @model.cursorsForScreenRowRange(@startRow, @endRow - 1) when cursor.isVisible() + pixelRect = @pixelRectForScreenRange(cursor.getScreenRange()) + pixelRect.width = Math.round(@baseCharacterWidth) if pixelRect.width is 0 + @state.content.cursors[cursor.id] = pixelRect + return updateOverlaysState: -> return unless @hasOverlayPositionRequirements() diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 2ba45a3ba..55f6d84ad 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -109,6 +109,7 @@ class TextEditor extends Model @emitter = new Emitter @disposables = new CompositeDisposable @cursors = [] + @cursorsByMarkerId = new Map @selections = [] buffer ?= new TextBuffer @@ -1944,10 +1945,18 @@ class TextEditor extends Model getCursorsOrderedByBufferPosition: -> @getCursors().sort (a, b) -> a.compare(b) + cursorsForScreenRowRange: (startScreenRow, endScreenRow) -> + cursors = [] + for marker in @selectionsMarkerLayer.findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow]) + if cursor = @cursorsByMarkerId.get(marker.id) + cursors.push(cursor) + cursors + # Add a cursor based on the given {TextEditorMarker}. addCursor: (marker) -> cursor = new Cursor(editor: this, marker: marker, config: @config) @cursors.push(cursor) + @cursorsByMarkerId.set(marker.id, cursor) @decorateMarker(marker, type: 'line-number', class: 'cursor-line') @decorateMarker(marker, type: 'line-number', class: 'cursor-line-no-selection', onlyHead: true, onlyEmpty: true) @decorateMarker(marker, type: 'line', class: 'cursor-line', onlyEmpty: true) @@ -2446,6 +2455,7 @@ class TextEditor extends Model removeSelection: (selection) -> _.remove(@cursors, selection.cursor) _.remove(@selections, selection) + @cursorsByMarkerId.delete(selection.cursor.marker.id) @emitter.emit 'did-remove-cursor', selection.cursor @emitter.emit 'did-remove-selection', selection From 5d9c5c51d35adb0b0047ba8b309611c8d978bbf4 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 16 Feb 2016 10:16:10 -0500 Subject: [PATCH 187/971] We might wanna log the output. --- build/tasks/spec-task.coffee | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/build/tasks/spec-task.coffee b/build/tasks/spec-task.coffee index 892c92696..19b553412 100644 --- a/build/tasks/spec-task.coffee +++ b/build/tasks/spec-task.coffee @@ -86,7 +86,7 @@ module.exports = (grunt) -> packageSpecQueue.concurrency = Math.max(1, concurrency - 1) packageSpecQueue.drain = -> callback(null, failedPackages) - runCoreSpecs = (callback) -> + runCoreSpecs = (callback, logOutput = false) -> appPath = getAppPath() resourcePath = process.cwd() coreSpecsPath = path.resolve('spec') @@ -130,11 +130,17 @@ module.exports = (grunt) -> else async.parallel + # If we're just running the core specs then we won't have any output to + # indicate the tests actually *are* running. This upsets Travis: + # https://github.com/atom/atom/issues/10837. So pass the test output + # through. + runCoreSpecsWithLogging = (callback) -> runCoreSpecs(callback, true) + specs = if process.env.ATOM_SPECS_TASK is 'packages' [runPackageSpecs] else if process.env.ATOM_SPECS_TASK is 'core' - [runCoreSpecs] + [runCoreSpecsWithLogging] else [runCoreSpecs, runPackageSpecs] From a89257d4749403f3f6b42f3b2cc4040749b8d52c Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 16 Feb 2016 10:16:21 -0500 Subject: [PATCH 188/971] Just inherit stdio if we're logging. --- build/tasks/spec-task.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/tasks/spec-task.coffee b/build/tasks/spec-task.coffee index 19b553412..e3fa105ac 100644 --- a/build/tasks/spec-task.coffee +++ b/build/tasks/spec-task.coffee @@ -109,6 +109,9 @@ module.exports = (grunt) -> ATOM_INTEGRATION_TESTS_ENABLED: true ) + if logOutput + options.opts.stdio = 'inherit' + grunt.log.ok "Launching core specs." spawn options, (error, results, code) -> if process.platform is 'win32' From 7f239560f8efafaed1514fde181b215cb226963e Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 16 Feb 2016 10:16:39 -0500 Subject: [PATCH 189/971] Depending on the options passed through, we might not have stdout and stderr. --- build/tasks/task-helpers.coffee | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/tasks/task-helpers.coffee b/build/tasks/task-helpers.coffee index d24cdec77..b42b4dd15 100644 --- a/build/tasks/task-helpers.coffee +++ b/build/tasks/task-helpers.coffee @@ -52,8 +52,10 @@ module.exports = (grunt) -> stderr = [] error = null proc = childProcess.spawn(options.cmd, options.args, options.opts) - proc.stdout.on 'data', (data) -> stdout.push(data.toString()) - proc.stderr.on 'data', (data) -> stderr.push(data.toString()) + if proc.stdout? + proc.stdout.on 'data', (data) -> stdout.push(data.toString()) + if proc.stderr? + proc.stderr.on 'data', (data) -> stderr.push(data.toString()) proc.on 'error', (processError) -> error ?= processError proc.on 'close', (exitCode, signal) -> error ?= new Error(signal) if exitCode isnt 0 From 083f577889a232909dc2117aa18c3efe1dc674fc Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 16 Feb 2016 10:28:30 -0800 Subject: [PATCH 190/971] =?UTF-8?q?Use=20more=20descriptive=20flag=20for?= =?UTF-8?q?=20clearing=20state=20-=20=E2=80=98clear-window-state=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/atom-environment.coffee | 4 ++-- src/browser/atom-application.coffee | 18 +++++++++--------- src/browser/atom-window.coffee | 2 +- src/browser/main.coffee | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index cdb22b40d..ed36f5da2 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -122,14 +122,14 @@ class AtomEnvironment extends Model {@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params @loadTime = null - {devMode, safeMode, resourcePath, clearState} = @getLoadSettings() + {devMode, safeMode, resourcePath, clearWindowState} = @getLoadSettings() @emitter = new Emitter @disposables = new CompositeDisposable @stateStore = new StateStore('AtomEnvironments', 1) - @stateStore.clear() if clearState + @stateStore.clear() if clearWindowState @deserializers = new DeserializerManager(this) @deserializeTimings = {} diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 891caf19d..e889840e5 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -65,7 +65,7 @@ class AtomApplication exit: (status) -> app.exit(status) constructor: (options) -> - {@resourcePath, @devResourcePath, @version, @devMode, @safeMode, @socketPath, timeout, clearState} = options + {@resourcePath, @devResourcePath, @version, @devMode, @safeMode, @socketPath, timeout, clearWindowState} = options @socketPath = null if options.test @@ -89,16 +89,16 @@ class AtomApplication else @loadState(options) or @openPath(options) - openWithOptions: ({pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearState}) -> + openWithOptions: ({pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState}) -> if test @runTests({headless: true, devMode, @resourcePath, executedFrom, pathsToOpen, logFile, timeout}) else if pathsToOpen.length > 0 - @openPaths({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearState}) + @openPaths({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState}) else if urlsToOpen.length > 0 @openUrl({urlToOpen, devMode, safeMode}) for urlToOpen in urlsToOpen else # Always open a editor window if this is the first instance of Atom. - @openPath({pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearState}) + @openPath({pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState}) # Public: Removes the {AtomWindow} from the global window list. removeWindow: (window) -> @@ -387,8 +387,8 @@ class AtomApplication # :safeMode - Boolean to control the opened window's safe mode. # :profileStartup - Boolean to control creating a profile of the startup time. # :window - {AtomWindow} to open file paths in. - openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearState} = {}) -> - @openPaths({pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearState}) + openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState} = {}) -> + @openPaths({pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState}) # Public: Opens multiple paths, in existing windows if possible. # @@ -400,10 +400,10 @@ class AtomApplication # :safeMode - Boolean to control the opened window's safe mode. # :windowDimensions - Object with height and width keys. # :window - {AtomWindow} to open file paths in. - openPaths: ({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearState}={}) -> + openPaths: ({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearWindowState}={}) -> devMode = Boolean(devMode) safeMode = Boolean(safeMode) - clearState = Boolean(clearState) + clearWindowState = Boolean(clearWindowState) locationsToOpen = (@locationForPathToOpen(pathToOpen, executedFrom) for pathToOpen in pathsToOpen) pathsToOpen = (locationToOpen.pathToOpen for locationToOpen in locationsToOpen) @@ -436,7 +436,7 @@ class AtomApplication windowInitializationScript ?= require.resolve('../initialize-application-window') resourcePath ?= @resourcePath windowDimensions ?= @getDimensionsForNewWindow() - openedWindow = new AtomWindow({locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup, clearState}) + openedWindow = new AtomWindow({locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup, clearWindowState}) if pidToKillWhenClosed? @pidsToOpenWindows[pidToKillWhenClosed] = openedWindow diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 35ccabb8a..0c0f8f919 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -50,7 +50,7 @@ class AtomWindow loadSettings.safeMode ?= false loadSettings.atomHome = process.env.ATOM_HOME loadSettings.firstLoad = true - loadSettings.clearState ?= false + loadSettings.clearWindowState ?= false # Only send to the first non-spec window created if @constructor.includeShellLoadTime and not @isSpec diff --git a/src/browser/main.coffee b/src/browser/main.coffee index c1df3455f..c0613fb09 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -123,7 +123,7 @@ parseCommandLine = -> options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.') options.string('socket-path') options.string('user-data-dir') - options.boolean('clear-state').describe('clear-state', 'Delete all Atom environment state.') + options.boolean('clear-window-state').describe('clear-window-state', 'Delete all Atom environment state.') args = options.argv @@ -147,7 +147,7 @@ parseCommandLine = -> socketPath = args['socket-path'] userDataDir = args['user-data-dir'] profileStartup = args['profile-startup'] - clearState = args['clear-state'] + clearWindowState = args['clear-window-state'] urlsToOpen = [] devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH ? path.join(app.getHomeDir(), 'github', 'atom') setPortable = args.portable @@ -172,6 +172,6 @@ parseCommandLine = -> {resourcePath, devResourcePath, pathsToOpen, urlsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, socketPath, userDataDir, profileStartup, timeout, setPortable, - clearState} + clearWindowState} start() From 7c223796a2124faec4d7707bbd3102163cf2584b Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 16 Feb 2016 13:44:40 -0500 Subject: [PATCH 191/971] :arrow_up: fuzzy-finder@1.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8ec83f54c..be50c432f 100644 --- a/package.json +++ b/package.json @@ -90,8 +90,8 @@ "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", "find-and-replace": "0.197.2", - "fuzzy-finder": "0.94.1", "git-diff": "0.57.1", + "fuzzy-finder": "1.0.0", "go-to-line": "0.30.0", "grammar-selector": "0.48.1", "image-view": "0.56.0", From e4f23d47680fd7edd45f5d3c3056d1352164c3a3 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 16 Feb 2016 13:44:53 -0500 Subject: [PATCH 192/971] :arrow_up: git-diff@1.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index be50c432f..568a7641a 100644 --- a/package.json +++ b/package.json @@ -90,8 +90,8 @@ "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", "find-and-replace": "0.197.2", - "git-diff": "0.57.1", "fuzzy-finder": "1.0.0", + "git-diff": "1.0.0", "go-to-line": "0.30.0", "grammar-selector": "0.48.1", "image-view": "0.56.0", From 2637f3f174cf5cd91e1d26e2f92b44a95e77175b Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 16 Feb 2016 13:45:04 -0500 Subject: [PATCH 193/971] :arrow_up: open-on-github@1.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 568a7641a..8706c262c 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "markdown-preview": "0.157.3", "metrics": "0.53.1", "notifications": "0.62.2", - "open-on-github": "0.41.1", + "open-on-github": "1.0.0", "package-generator": "0.41.1", "settings-view": "0.232.4", "snippets": "1.0.1", From d65472719a7ae595e020406de4a6779941ecda97 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 16 Feb 2016 13:45:12 -0500 Subject: [PATCH 194/971] :arrow_up: status-bar@1.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8706c262c..41c7180c5 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.232.4", "snippets": "1.0.1", "spell-check": "0.66.1", - "status-bar": "0.83.1", + "status-bar": "1.0.0", "styleguide": "0.45.2", "symbols-view": "0.111.1", "tabs": "0.90.2", From c691dd05c57322cffd25f58bb4fec57fb9e3b31c Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 16 Feb 2016 11:57:35 -0800 Subject: [PATCH 195/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 38956f362..3977cb806 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.3.1-0", + "text-buffer": "8.3.1", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From b3e8ba350ab372ea45f9e1826176c886ef43ed10 Mon Sep 17 00:00:00 2001 From: Paul Aikman Date: Tue, 16 Feb 2016 22:04:33 +0000 Subject: [PATCH 196/971] Fix for directory provider on Windows. Checks for presence of host in URL passed in instead of protocol (false positive). --- src/default-directory-provider.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/default-directory-provider.coffee b/src/default-directory-provider.coffee index 6b05a582f..094522980 100644 --- a/src/default-directory-provider.coffee +++ b/src/default-directory-provider.coffee @@ -16,8 +16,8 @@ class DefaultDirectoryProvider # * `null` if the given URI is not compatibile with this provider. directoryForURISync: (uri) -> normalizedPath = path.normalize(uri) - {protocol} = url.parse(uri) - directoryPath = if protocol? + host = url.parse(uri).host; + directoryPath = if host uri else if not fs.isDirectorySync(normalizedPath) and fs.isDirectorySync(path.dirname(normalizedPath)) path.dirname(normalizedPath) @@ -26,7 +26,7 @@ class DefaultDirectoryProvider # TODO: Stop normalizing the path in pathwatcher's Directory. directory = new Directory(directoryPath) - if protocol? + if host directory.path = directoryPath if fs.isCaseInsensitive() directory.lowerCasePath = directoryPath.toLowerCase() From ae26e6c8ca284bb2858c65074c8d1d64564d1593 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 16 Feb 2016 14:16:28 -0800 Subject: [PATCH 197/971] Add core setting for pending tabs configuration --- src/config-schema.coffee | 4 ++++ src/workspace.coffee | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/config-schema.coffee b/src/config-schema.coffee index 15e5223b8..36c5fc5ba 100644 --- a/src/config-schema.coffee +++ b/src/config-schema.coffee @@ -108,6 +108,10 @@ module.exports = description: 'Automatically update Atom when a new release is available.' type: 'boolean' default: true + openPendingPaneItems: + description: 'Open pane items in pending state, such that only one pending item is open per pane.' + type: 'boolean' + default: true editor: type: 'object' diff --git a/src/workspace.coffee b/src/workspace.coffee index 79cfb3e8e..4ed620481 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -414,6 +414,9 @@ class Workspace extends Model split = options.split uri = @project.resolvePath(uri) + if not atom.config.get('core.openPendingPaneItems') + options.pending = false + # Avoid adding URLs as recent documents to work-around this Spotlight crash: # https://github.com/atom/atom/issues/10071 if uri? and not url.parse(uri).protocol? From c9150c3f73493760200511721ea6c4814bdb1314 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 16 Feb 2016 15:24:18 -0800 Subject: [PATCH 198/971] Add test for core.openPendingPaneItems setting --- spec/workspace-spec.coffee | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index e89e4c6bd..bb4ac1532 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -1532,3 +1532,14 @@ describe "Workspace", -> atom.workspace.closeActivePaneItemOrEmptyPaneOrWindow() expect(atom.close).toHaveBeenCalled() + + describe "when the core.openPendingPaneItems option is falsey", -> + it "does not open item with `pending: true` option as pending", -> + editor = null + atom.config.set('core.openPendingPaneItems', false) + + waitsForPromise -> + atom.workspace.open('sample.js', pending: true).then (o) -> editor = o + + runs -> + expect(editor.isPending()).toBeFalsy() From 8d27d1925c3ce53fc4edfcecd1ab6ad06dd6c70a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 12 Feb 2016 12:46:49 -0800 Subject: [PATCH 199/971] Avoid emitting config events while loading packages --- src/package-manager.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 705755af0..33f8f86a3 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -357,7 +357,9 @@ class PackageManager packagePaths = @getAvailablePackagePaths() packagePaths = packagePaths.filter (packagePath) => not @isPackageDisabled(path.basename(packagePath)) packagePaths = _.uniq packagePaths, (packagePath) -> path.basename(packagePath) - @loadPackage(packagePath) for packagePath in packagePaths + @config.transact => + @loadPackage(packagePath) for packagePath in packagePaths + return @emitter.emit 'did-load-initial-packages' loadPackage: (nameOrPath) -> From 59b34fdf2ea022bb32de4a7e8f7ad2885edc5c03 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Wed, 17 Feb 2016 00:02:39 -0800 Subject: [PATCH 200/971] Update grunt-electron-installer to latest version --- build/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/package.json b/build/package.json index 6c41d4355..2f8d88d8a 100644 --- a/build/package.json +++ b/build/package.json @@ -25,7 +25,7 @@ "grunt-contrib-less": "~0.8.0", "grunt-cson": "0.16.0", "grunt-download-electron": "^2.1.1", - "grunt-electron-installer": "1.0.6", + "grunt-electron-installer": "1.2.2", "grunt-lesslint": "0.17.0", "grunt-peg": "~1.1.0", "grunt-shell": "~0.3.1", From f5e5b830df2bdd93381a45cd0f63518afb6d7adb Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Wed, 17 Feb 2016 00:08:08 -0800 Subject: [PATCH 201/971] Avoid 260-character path limits on Windows in script\clean --- script/clean | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/script/clean b/script/clean index fd0aa5bfa..0c947baf2 100755 --- a/script/clean +++ b/script/clean @@ -4,15 +4,16 @@ var fs = require('fs'); var path = require('path'); var os = require('os'); -var removeCommand = process.platform === 'win32' ? 'rmdir /S /Q ' : 'rm -rf '; +var isWindows = process.platform === 'win32'; +var removeCommand = isWindows ? 'rmdir /S /Q ' : 'rm -rf '; var productName = require('../package.json').productName; process.chdir(path.dirname(__dirname)); -var home = process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME']; +var home = process.env[isWindows ? 'USERPROFILE' : 'HOME']; var tmpdir = os.tmpdir(); // Windows: Use START as a way to ignore error if Atom.exe isnt running -var killatom = process.platform === 'win32' ? 'START taskkill /F /IM ' + productName + '.exe' : 'pkill -9 ' + productName + ' || true'; +var killatom = isWindows ? 'START taskkill /F /IM ' + productName + '.exe' : 'pkill -9 ' + productName + ' || true'; var commands = [ killatom, @@ -39,12 +40,32 @@ var run = function() { if (Array.isArray(next)) { var pathToRemove = path.resolve.apply(path.resolve, next); - if (fs.existsSync(pathToRemove)) - next = removeCommand + pathToRemove; - else + if (fs.existsSync(pathToRemove)) { + if (isWindows) { + removeFolderRecursive(pathToRemove); + } else { + next = removeCommand + pathToRemove; + cp.safeExec(next, run); + } + } + else { return run(); + } } - - cp.safeExec(next, run); + else + cp.safeExec(next, run); }; run(); + +// Windows has a 260-char path limit for rmdir etc. Just recursively delete in Node. +var removeFolderRecursive = function(folderPath) { + fs.readdirSync(folderPath).forEach(function(entry, index) { + var entryPath = path.join(folderPath, entry); + if (fs.lstatSync(entryPath).isDirectory()) { + removeFolderRecursive(entryPath); + } else { + fs.unlinkSync(entryPath); + } + }); + fs.rmdirSync(folderPath); +}; From 0afa9bb21ccc3cae1f8f3ed6e4e6a250ef6e65be Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 17 Feb 2016 10:02:48 +0100 Subject: [PATCH 202/971] Don't update foldable status, ever --- spec/tokenized-buffer-spec.coffee | 81 ------------------------------- src/tokenized-buffer.coffee | 1 + 2 files changed, 1 insertion(+), 81 deletions(-) diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index 72b292cc2..14843ddb8 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -903,87 +903,6 @@ describe "TokenizedBuffer", -> expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 2 expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 2 # } - describe ".foldable on tokenized lines", -> - changes = null - - beforeEach -> - changes = [] - buffer = atom.project.bufferForPathSync('sample.js') - buffer.insert [10, 0], " // multi-line\n // comment\n // block\n" - buffer.insert [0, 0], "// multi-line\n// comment\n// block\n" - tokenizedBuffer = new TokenizedBuffer({ - buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert - }) - fullyTokenize(tokenizedBuffer) - tokenizedBuffer.onDidChange (change) -> - delete change.bufferChange - changes.push(change) - - it "sets .foldable to true on the first line of multi-line comments", -> - expect(tokenizedBuffer.tokenizedLineForRow(0).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe true # because of indent - expect(tokenizedBuffer.tokenizedLineForRow(13).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(14).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(15).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(16).foldable).toBe false - - buffer.insert([0, Infinity], '\n') - expect(changes).toEqual [{start: 0, end: 1, delta: 1}] - - expect(tokenizedBuffer.tokenizedLineForRow(0).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe false - - changes = [] - buffer.undo() - expect(changes).toEqual [{start: 0, end: 2, delta: -1}] - expect(tokenizedBuffer.tokenizedLineForRow(0).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe true # because of indent - - it "sets .foldable to true on non-comment lines that precede an increase in indentation", -> - buffer.insert([2, 0], ' ') # commented lines preceding an indent aren't foldable - expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(4).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(5).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false - - changes = [] - buffer.insert([7, 0], ' ') - expect(changes).toEqual [{start: 6, end: 7, delta: 0}] - expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false - - changes = [] - buffer.undo() - expect(changes).toEqual [{start: 6, end: 7, delta: 0}] - expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false - - changes = [] - buffer.insert([7, 0], " \n x\n") - expect(changes).toEqual [{start: 6, end: 7, delta: 2}] - expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false - - changes = [] - buffer.insert([9, 0], " ") - expect(changes).toEqual [{start: 9, end: 9, delta: 0}] - expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false - describe "when the buffer is configured with the null grammar", -> it "uses the placeholder tokens and does not actually tokenize using the grammar", -> spyOn(atom.grammars.nullGrammar, 'tokenizeLine').andCallThrough() diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 0fdf4eea8..5a95d9f15 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -288,6 +288,7 @@ class TokenizedBuffer extends Model row - increment updateFoldableStatus: (startRow, endRow) -> + return [startRow, endRow] return [startRow, endRow] if @largeFileMode scanStartRow = @buffer.previousNonBlankRow(startRow) ? startRow From 012fa354c41a495983588f7148c5413c28209e96 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 17 Feb 2016 10:28:38 +0100 Subject: [PATCH 203/971] Add TokenizedBuffer.prototype.foldableRowsForRowRange --- src/display-buffer.coffee | 3 +++ src/text-editor-presenter.coffee | 3 ++- src/text-editor.coffee | 5 ++++- src/tokenized-buffer.coffee | 24 +++++------------------- 4 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 8b95656f9..8015f2df2 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -413,6 +413,9 @@ class DisplayBuffer extends Model isFoldedAtScreenRow: (screenRow) -> @largestFoldContainingBufferRow(@bufferRowForScreenRow(screenRow))? + foldableBufferRowsForBufferRowRange: (startRow, endRow) -> + @tokenizedBuffer.foldableRowsForRowRange(startRow, endRow) + # Destroys the fold with the given id destroyFoldWithId: (id) -> @foldsByMarkerId[id]?.destroy() diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index a36c860ed..147eaa962 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -599,6 +599,7 @@ class TextEditorPresenter if endRow > startRow bufferRows = @model.bufferRowsForScreenRows(startRow, endRow - 1) + foldableBufferRows = @model.foldableBufferRowsForBufferRowRange(bufferRows[0], bufferRows[bufferRows.length - 1]) for bufferRow, i in bufferRows if bufferRow is lastBufferRow softWrapped = true @@ -609,7 +610,7 @@ class TextEditorPresenter screenRow = startRow + i line = @model.tokenizedLineForScreenRow(screenRow) decorationClasses = @lineNumberDecorationClassesForRow(screenRow) - foldable = @model.isFoldableAtScreenRow(screenRow) + foldable = foldableBufferRows.has(bufferRow) blockDecorationsBeforeCurrentScreenRowHeight = @lineTopIndex.pixelPositionAfterBlocksForRow(screenRow) - @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRow) blockDecorationsHeight = blockDecorationsBeforeCurrentScreenRowHeight if screenRow % @tileSize isnt 0 diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 55f6d84ad..6c80cfc01 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2940,7 +2940,7 @@ class TextEditor extends Model # Returns a {Boolean}. isFoldableAtBufferRow: (bufferRow) -> # @languageMode.isFoldableAtBufferRow(bufferRow) - @displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow)?.foldable ? false + @foldableBufferRowsForBufferRowRange(bufferRow, bufferRow).has(bufferRow) # Extended: Determine whether the given row in screen coordinates is foldable. # @@ -2953,6 +2953,9 @@ class TextEditor extends Model bufferRow = @displayBuffer.bufferRowForScreenRow(screenRow) @isFoldableAtBufferRow(bufferRow) + foldableBufferRowsForBufferRowRange: (startRow, endRow) -> + @displayBuffer.foldableBufferRowsForBufferRowRange(startRow, endRow) + # Extended: Fold the given buffer row if it isn't currently folded, and unfold # it otherwise. toggleFoldAtBufferRow: (bufferRow) -> diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 5a95d9f15..055e3fe47 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -210,8 +210,6 @@ class TokenizedBuffer extends Model @validateRow(endRow) @invalidateRow(endRow + 1) unless filledRegion - [startRow, endRow] = @updateFoldableStatus(startRow, endRow) - event = {start: startRow, end: endRow, delta: 0} @emitter.emit 'did-change', event @@ -271,9 +269,6 @@ class TokenizedBuffer extends Model if newEndStack and not _.isEqual(newEndStack, previousEndStack) @invalidateRow(end + delta + 1) - [start, end] = @updateFoldableStatus(start, end + delta) - end -= delta - event = {start, end, delta, bufferChange: e} @emitter.emit 'did-change', event @@ -287,23 +282,14 @@ class TokenizedBuffer extends Model row - increment - updateFoldableStatus: (startRow, endRow) -> - return [startRow, endRow] - return [startRow, endRow] if @largeFileMode - + foldableRowsForRowRange: (startRow, endRow) -> scanStartRow = @buffer.previousNonBlankRow(startRow) ? startRow scanStartRow-- while scanStartRow > 0 and @tokenizedLineForRow(scanStartRow).isComment() scanEndRow = @buffer.nextNonBlankRow(endRow) ? endRow - - for row in [scanStartRow..scanEndRow] by 1 - foldable = @isFoldableAtRow(row) - line = @tokenizedLineForRow(row) - unless line.foldable is foldable - line.foldable = foldable - startRow = Math.min(startRow, row) - endRow = Math.max(endRow, row) - - [startRow, endRow] + foldableRows = new Set + for row in [scanStartRow..scanEndRow] by 1 when @isFoldableAtRow(row) + foldableRows.add(row) + foldableRows isFoldableAtRow: (row) -> if @largeFileMode From d05dfa6efe2131f468cca75dd407f0a992dceb4e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 17 Feb 2016 10:29:38 +0100 Subject: [PATCH 204/971] :racehorse: Update lines state only in pre-measurement phase --- src/text-editor-presenter.coffee | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 147eaa962..48f269b9d 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -90,7 +90,7 @@ class TextEditorPresenter @updateLineDecorations() @updateBlockDecorations() - @updateTilesState() + @updateTilesState(true) @updating = false @state @@ -112,7 +112,7 @@ class TextEditorPresenter @updateHiddenInputState() @updateContentState() @updateHighlightDecorations() if @shouldUpdateDecorations - @updateTilesState() + @updateTilesState(false) @updateCursorsState() @updateOverlaysState() @updateLineNumberGutterState() @@ -327,7 +327,7 @@ class TextEditorPresenter clearScreenRowsToMeasure: -> @screenRowsToMeasure = [] - updateTilesState: -> + updateTilesState: (updateLinesState) -> return unless @startRow? and @endRow? and @lineHeight? screenRows = @getScreenRows() @@ -367,8 +367,9 @@ class TextEditorPresenter gutterTile.display = "block" gutterTile.zIndex = zIndex - @updateLinesState(tile, rowsWithinTile) - @updateLineNumbersState(gutterTile, rowsWithinTile) + if updateLinesState + @updateLinesState(tile, rowsWithinTile) + @updateLineNumbersState(gutterTile, rowsWithinTile) visibleTiles[tileStartRow] = true zIndex++ From 2c71a448b3b1397dcd3e451484750c037909e5b2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 17 Feb 2016 11:04:19 +0100 Subject: [PATCH 205/971] Put foldable specs back --- spec/tokenized-buffer-spec.coffee | 81 +++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index 14843ddb8..72b292cc2 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -903,6 +903,87 @@ describe "TokenizedBuffer", -> expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 2 expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 2 # } + describe ".foldable on tokenized lines", -> + changes = null + + beforeEach -> + changes = [] + buffer = atom.project.bufferForPathSync('sample.js') + buffer.insert [10, 0], " // multi-line\n // comment\n // block\n" + buffer.insert [0, 0], "// multi-line\n// comment\n// block\n" + tokenizedBuffer = new TokenizedBuffer({ + buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert + }) + fullyTokenize(tokenizedBuffer) + tokenizedBuffer.onDidChange (change) -> + delete change.bufferChange + changes.push(change) + + it "sets .foldable to true on the first line of multi-line comments", -> + expect(tokenizedBuffer.tokenizedLineForRow(0).foldable).toBe true + expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe true # because of indent + expect(tokenizedBuffer.tokenizedLineForRow(13).foldable).toBe true + expect(tokenizedBuffer.tokenizedLineForRow(14).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(15).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(16).foldable).toBe false + + buffer.insert([0, Infinity], '\n') + expect(changes).toEqual [{start: 0, end: 1, delta: 1}] + + expect(tokenizedBuffer.tokenizedLineForRow(0).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe true + expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe false + + changes = [] + buffer.undo() + expect(changes).toEqual [{start: 0, end: 2, delta: -1}] + expect(tokenizedBuffer.tokenizedLineForRow(0).foldable).toBe true + expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe true # because of indent + + it "sets .foldable to true on non-comment lines that precede an increase in indentation", -> + buffer.insert([2, 0], ' ') # commented lines preceding an indent aren't foldable + expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe true + expect(tokenizedBuffer.tokenizedLineForRow(4).foldable).toBe true + expect(tokenizedBuffer.tokenizedLineForRow(5).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe true + expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false + + changes = [] + buffer.insert([7, 0], ' ') + expect(changes).toEqual [{start: 6, end: 7, delta: 0}] + expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe true + expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false + + changes = [] + buffer.undo() + expect(changes).toEqual [{start: 6, end: 7, delta: 0}] + expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe true + expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false + + changes = [] + buffer.insert([7, 0], " \n x\n") + expect(changes).toEqual [{start: 6, end: 7, delta: 2}] + expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe true + expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false + + changes = [] + buffer.insert([9, 0], " ") + expect(changes).toEqual [{start: 9, end: 9, delta: 0}] + expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe true + expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe false + expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false + describe "when the buffer is configured with the null grammar", -> it "uses the placeholder tokens and does not actually tokenize using the grammar", -> spyOn(atom.grammars.nullGrammar, 'tokenizeLine').andCallThrough() From a613fa5133b4671c77aa08d2d5ce92785d0137db Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 17 Feb 2016 11:17:24 +0100 Subject: [PATCH 206/971] Adapt specs to use the new API --- spec/tokenized-buffer-spec.coffee | 106 +++++++++++++++--------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index 72b292cc2..0f5c9c52d 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -903,7 +903,7 @@ describe "TokenizedBuffer", -> expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 2 expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 2 # } - describe ".foldable on tokenized lines", -> + describe "::foldableRowsForRowRange(startRow, endRow)", -> changes = null beforeEach -> @@ -915,74 +915,74 @@ describe "TokenizedBuffer", -> buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert }) fullyTokenize(tokenizedBuffer) - tokenizedBuffer.onDidChange (change) -> - delete change.bufferChange - changes.push(change) - it "sets .foldable to true on the first line of multi-line comments", -> - expect(tokenizedBuffer.tokenizedLineForRow(0).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe true # because of indent - expect(tokenizedBuffer.tokenizedLineForRow(13).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(14).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(15).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(16).foldable).toBe false + it "includes the first line of multi-line comments", -> + foldableRows = tokenizedBuffer.foldableRowsForRowRange(0, 16) + expect(foldableRows.has(0)).toBe true + expect(foldableRows.has(1)).toBe false + expect(foldableRows.has(2)).toBe false + expect(foldableRows.has(3)).toBe true # because of indent + expect(foldableRows.has(13)).toBe true + expect(foldableRows.has(14)).toBe false + expect(foldableRows.has(15)).toBe false + expect(foldableRows.has(16)).toBe false buffer.insert([0, Infinity], '\n') - expect(changes).toEqual [{start: 0, end: 1, delta: 1}] - expect(tokenizedBuffer.tokenizedLineForRow(0).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe false + foldableRows = tokenizedBuffer.foldableRowsForRowRange(0, 3) + expect(foldableRows.has(0)).toBe false + expect(foldableRows.has(1)).toBe false + expect(foldableRows.has(2)).toBe true + expect(foldableRows.has(3)).toBe false - changes = [] buffer.undo() - expect(changes).toEqual [{start: 0, end: 2, delta: -1}] - expect(tokenizedBuffer.tokenizedLineForRow(0).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe true # because of indent - it "sets .foldable to true on non-comment lines that precede an increase in indentation", -> + foldableRows = tokenizedBuffer.foldableRowsForRowRange(0, 3) + expect(foldableRows.has(0)).toBe true + expect(foldableRows.has(1)).toBe false + expect(foldableRows.has(2)).toBe false + expect(foldableRows.has(3)).toBe true # because of indent + + it "includes non-comment lines that precede an increase in indentation", -> buffer.insert([2, 0], ' ') # commented lines preceding an indent aren't foldable - expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(4).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(5).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false - changes = [] + foldableRows = tokenizedBuffer.foldableRowsForRowRange(0, 8) + expect(foldableRows.has(1)).toBe false + expect(foldableRows.has(2)).toBe false + expect(foldableRows.has(3)).toBe true + expect(foldableRows.has(4)).toBe true + expect(foldableRows.has(5)).toBe false + expect(foldableRows.has(6)).toBe false + expect(foldableRows.has(7)).toBe true + expect(foldableRows.has(8)).toBe false + buffer.insert([7, 0], ' ') - expect(changes).toEqual [{start: 6, end: 7, delta: 0}] - expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false - changes = [] + foldableRows = tokenizedBuffer.foldableRowsForRowRange(6, 8) + expect(foldableRows.has(6)).toBe true + expect(foldableRows.has(7)).toBe false + expect(foldableRows.has(8)).toBe false + buffer.undo() - expect(changes).toEqual [{start: 6, end: 7, delta: 0}] - expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false - changes = [] + foldableRows = tokenizedBuffer.foldableRowsForRowRange(6, 8) + expect(foldableRows.has(6)).toBe false + expect(foldableRows.has(7)).toBe true + expect(foldableRows.has(8)).toBe false + buffer.insert([7, 0], " \n x\n") - expect(changes).toEqual [{start: 6, end: 7, delta: 2}] - expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false - changes = [] + foldableRows = tokenizedBuffer.foldableRowsForRowRange(6, 8) + expect(foldableRows.has(6)).toBe true + expect(foldableRows.has(7)).toBe false + expect(foldableRows.has(8)).toBe false + buffer.insert([9, 0], " ") - expect(changes).toEqual [{start: 9, end: 9, delta: 0}] - expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe true - expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe false - expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false + + foldableRows = tokenizedBuffer.foldableRowsForRowRange(6, 8) + expect(foldableRows.has(6)).toBe true + expect(foldableRows.has(7)).toBe false + expect(foldableRows.has(8)).toBe false describe "when the buffer is configured with the null grammar", -> it "uses the placeholder tokens and does not actually tokenize using the grammar", -> From 90c75fd6da268a1636278ce935285b5f298c66b7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 17 Feb 2016 14:35:22 +0100 Subject: [PATCH 207/971] Delete specs testing previous behavior When emitting the `did-change` event, `updateFoldableStatus` used to extend the change region up and down to include all the lines that changed their foldability status because of a buffer change. I assume this was supposed to invalidate folds that *contained* the change whenever a line was edited in a way that affected also the previous or subsequent ones. That information, however, is not being used by `DisplayBuffer`, which does not alter existing folded regions when they become invalid. I believe the correct behavior should be to unfold the invalid region and recompute those screen lines. Nonetheless, it seems reasonable to me to keep the original (wrong) behavior and not address it in this branch, because it strays from the original intent of this refactoring. We should probably fix it once for all in another PR or when integrating/implementing `DisplayLayer`. --- spec/tokenized-buffer-spec.coffee | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index 0f5c9c52d..ca64d2edb 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -202,8 +202,7 @@ describe "TokenizedBuffer", -> expect(tokenizedBuffer.firstInvalidRow()).toBe 3 advanceClock() - # we discover that row 2 starts a foldable region when line 3 gets tokenized - expect(changeHandler).toHaveBeenCalledWith(start: 2, end: 7, delta: 0) + expect(changeHandler).toHaveBeenCalledWith(start: 3, end: 7, delta: 0) expect(tokenizedBuffer.firstInvalidRow()).toBe 8 describe "when there is a buffer change surrounding an invalid row", -> @@ -253,7 +252,7 @@ describe "TokenizedBuffer", -> expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] delete event.bufferChange - expect(event).toEqual(start: 1, end: 2, delta: 0) + expect(event).toEqual(start: 2, end: 2, delta: 0) changeHandler.reset() advanceClock() @@ -263,8 +262,7 @@ describe "TokenizedBuffer", -> expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] delete event.bufferChange - # we discover that row 2 starts a foldable region when line 3 gets tokenized - expect(event).toEqual(start: 2, end: 5, delta: 0) + expect(event).toEqual(start: 3, end: 5, delta: 0) it "resumes highlighting with the state of the previous line", -> buffer.insert([0, 0], '/*') @@ -292,7 +290,7 @@ describe "TokenizedBuffer", -> expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] delete event.bufferChange - expect(event).toEqual(start: 0, end: 3, delta: -2) # starts at 0 because foldable on row 0 becomes false + expect(event).toEqual(start: 1, end: 3, delta: -2) describe "when the change invalidates the tokenization of subsequent lines", -> it "schedules the invalidated lines to be tokenized in the background", -> @@ -305,7 +303,7 @@ describe "TokenizedBuffer", -> expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] delete event.bufferChange - expect(event).toEqual(start: 1, end: 3, delta: -1) + expect(event).toEqual(start: 2, end: 3, delta: -1) changeHandler.reset() advanceClock() @@ -314,8 +312,7 @@ describe "TokenizedBuffer", -> expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] delete event.bufferChange - # we discover that row 2 starts a foldable region when line 3 gets tokenized - expect(event).toEqual(start: 2, end: 4, delta: 0) + expect(event).toEqual(start: 3, end: 4, delta: 0) describe "when lines are both updated and inserted", -> it "updates tokens to reflect the change", -> @@ -339,7 +336,7 @@ describe "TokenizedBuffer", -> expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] delete event.bufferChange - expect(event).toEqual(start: 0, end: 2, delta: 2) # starts at 0 because .foldable becomes false on row 0 + expect(event).toEqual(start: 1, end: 2, delta: 2) describe "when the change invalidates the tokenization of subsequent lines", -> it "schedules the invalidated lines to be tokenized in the background", -> @@ -350,7 +347,7 @@ describe "TokenizedBuffer", -> expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] delete event.bufferChange - expect(event).toEqual(start: 1, end: 2, delta: 2) + expect(event).toEqual(start: 2, end: 2, delta: 2) expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js'] expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] @@ -894,7 +891,7 @@ describe "TokenizedBuffer", -> buffer.setTextInRange([[7, 0], [8, 65]], ' ok') delete changeHandler.argsForCall[0][0].bufferChange - expect(changeHandler).toHaveBeenCalledWith(start: 4, end: 10, delta: -1) # starts at row 4 because it became foldable + expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 10, delta: -1) expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 2 expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 2 From bea324eae92017e04f8ad0006a0d0da09f8f323e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 17 Feb 2016 15:05:31 +0100 Subject: [PATCH 208/971] :fire: Use just `isFoldableAtBufferRow` --- spec/language-mode-spec.coffee | 11 ----- spec/tokenized-buffer-spec.coffee | 82 ++++++++++++++----------------- src/display-buffer.coffee | 4 +- src/text-editor-presenter.coffee | 9 +++- src/text-editor.coffee | 6 +-- src/tokenized-buffer.coffee | 9 ---- src/tokenized-line.coffee | 1 - 7 files changed, 47 insertions(+), 75 deletions(-) diff --git a/spec/language-mode-spec.coffee b/spec/language-mode-spec.coffee index 7ea4a1ae9..cd32e29c7 100644 --- a/spec/language-mode-spec.coffee +++ b/spec/language-mode-spec.coffee @@ -463,17 +463,6 @@ describe "LanguageMode", -> fold2 = editor.tokenizedLineForScreenRow(5).fold expect(fold2).toBeFalsy() - describe ".isFoldableAtBufferRow(bufferRow)", -> - it "returns true if the line starts a multi-line comment", -> - expect(languageMode.isFoldableAtBufferRow(1)).toBe true - expect(languageMode.isFoldableAtBufferRow(6)).toBe true - expect(languageMode.isFoldableAtBufferRow(17)).toBe false - - it "does not return true for a line in the middle of a comment that's followed by an indented line", -> - expect(languageMode.isFoldableAtBufferRow(7)).toBe false - editor.buffer.insert([8, 0], ' ') - expect(languageMode.isFoldableAtBufferRow(7)).toBe false - describe "css", -> beforeEach -> waitsForPromise -> diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index ca64d2edb..88c095f68 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -900,7 +900,7 @@ describe "TokenizedBuffer", -> expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 2 expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 2 # } - describe "::foldableRowsForRowRange(startRow, endRow)", -> + describe "::isFoldableAtRow(row)", -> changes = null beforeEach -> @@ -914,72 +914,64 @@ describe "TokenizedBuffer", -> fullyTokenize(tokenizedBuffer) it "includes the first line of multi-line comments", -> - foldableRows = tokenizedBuffer.foldableRowsForRowRange(0, 16) - expect(foldableRows.has(0)).toBe true - expect(foldableRows.has(1)).toBe false - expect(foldableRows.has(2)).toBe false - expect(foldableRows.has(3)).toBe true # because of indent - expect(foldableRows.has(13)).toBe true - expect(foldableRows.has(14)).toBe false - expect(foldableRows.has(15)).toBe false - expect(foldableRows.has(16)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(0)).toBe true + expect(tokenizedBuffer.isFoldableAtRow(1)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(2)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(3)).toBe true # because of indent + expect(tokenizedBuffer.isFoldableAtRow(13)).toBe true + expect(tokenizedBuffer.isFoldableAtRow(14)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(15)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(16)).toBe false buffer.insert([0, Infinity], '\n') - foldableRows = tokenizedBuffer.foldableRowsForRowRange(0, 3) - expect(foldableRows.has(0)).toBe false - expect(foldableRows.has(1)).toBe false - expect(foldableRows.has(2)).toBe true - expect(foldableRows.has(3)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(0)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(1)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(2)).toBe true + expect(tokenizedBuffer.isFoldableAtRow(3)).toBe false buffer.undo() - foldableRows = tokenizedBuffer.foldableRowsForRowRange(0, 3) - expect(foldableRows.has(0)).toBe true - expect(foldableRows.has(1)).toBe false - expect(foldableRows.has(2)).toBe false - expect(foldableRows.has(3)).toBe true # because of indent + expect(tokenizedBuffer.isFoldableAtRow(0)).toBe true + expect(tokenizedBuffer.isFoldableAtRow(1)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(2)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(3)).toBe true # because of indent it "includes non-comment lines that precede an increase in indentation", -> buffer.insert([2, 0], ' ') # commented lines preceding an indent aren't foldable - foldableRows = tokenizedBuffer.foldableRowsForRowRange(0, 8) - expect(foldableRows.has(1)).toBe false - expect(foldableRows.has(2)).toBe false - expect(foldableRows.has(3)).toBe true - expect(foldableRows.has(4)).toBe true - expect(foldableRows.has(5)).toBe false - expect(foldableRows.has(6)).toBe false - expect(foldableRows.has(7)).toBe true - expect(foldableRows.has(8)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(1)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(2)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(3)).toBe true + expect(tokenizedBuffer.isFoldableAtRow(4)).toBe true + expect(tokenizedBuffer.isFoldableAtRow(5)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(6)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(7)).toBe true + expect(tokenizedBuffer.isFoldableAtRow(8)).toBe false buffer.insert([7, 0], ' ') - foldableRows = tokenizedBuffer.foldableRowsForRowRange(6, 8) - expect(foldableRows.has(6)).toBe true - expect(foldableRows.has(7)).toBe false - expect(foldableRows.has(8)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(6)).toBe true + expect(tokenizedBuffer.isFoldableAtRow(7)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(8)).toBe false buffer.undo() - foldableRows = tokenizedBuffer.foldableRowsForRowRange(6, 8) - expect(foldableRows.has(6)).toBe false - expect(foldableRows.has(7)).toBe true - expect(foldableRows.has(8)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(6)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(7)).toBe true + expect(tokenizedBuffer.isFoldableAtRow(8)).toBe false buffer.insert([7, 0], " \n x\n") - foldableRows = tokenizedBuffer.foldableRowsForRowRange(6, 8) - expect(foldableRows.has(6)).toBe true - expect(foldableRows.has(7)).toBe false - expect(foldableRows.has(8)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(6)).toBe true + expect(tokenizedBuffer.isFoldableAtRow(7)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(8)).toBe false buffer.insert([9, 0], " ") - foldableRows = tokenizedBuffer.foldableRowsForRowRange(6, 8) - expect(foldableRows.has(6)).toBe true - expect(foldableRows.has(7)).toBe false - expect(foldableRows.has(8)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(6)).toBe true + expect(tokenizedBuffer.isFoldableAtRow(7)).toBe false + expect(tokenizedBuffer.isFoldableAtRow(8)).toBe false describe "when the buffer is configured with the null grammar", -> it "uses the placeholder tokens and does not actually tokenize using the grammar", -> diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 8015f2df2..d01ad03c9 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -413,8 +413,8 @@ class DisplayBuffer extends Model isFoldedAtScreenRow: (screenRow) -> @largestFoldContainingBufferRow(@bufferRowForScreenRow(screenRow))? - foldableBufferRowsForBufferRowRange: (startRow, endRow) -> - @tokenizedBuffer.foldableRowsForRowRange(startRow, endRow) + isFoldableAtBufferRow: (row) -> + @tokenizedBuffer.isFoldableAtRow(row) # Destroys the fold with the given id destroyFoldWithId: (id) -> diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 48f269b9d..3266eea70 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -600,8 +600,14 @@ class TextEditorPresenter if endRow > startRow bufferRows = @model.bufferRowsForScreenRows(startRow, endRow - 1) - foldableBufferRows = @model.foldableBufferRowsForBufferRowRange(bufferRows[0], bufferRows[bufferRows.length - 1]) + previousBufferRow = -1 + foldable = false for bufferRow, i in bufferRows + # don't compute foldability more than once per buffer row + if previousBufferRow isnt bufferRow + foldable = @model.isFoldableAtBufferRow(bufferRow) + previousBufferRow = bufferRow + if bufferRow is lastBufferRow softWrapped = true else @@ -611,7 +617,6 @@ class TextEditorPresenter screenRow = startRow + i line = @model.tokenizedLineForScreenRow(screenRow) decorationClasses = @lineNumberDecorationClassesForRow(screenRow) - foldable = foldableBufferRows.has(bufferRow) blockDecorationsBeforeCurrentScreenRowHeight = @lineTopIndex.pixelPositionAfterBlocksForRow(screenRow) - @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRow) blockDecorationsHeight = blockDecorationsBeforeCurrentScreenRowHeight if screenRow % @tileSize isnt 0 diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 6c80cfc01..c0a6f2057 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2939,8 +2939,7 @@ class TextEditor extends Model # # Returns a {Boolean}. isFoldableAtBufferRow: (bufferRow) -> - # @languageMode.isFoldableAtBufferRow(bufferRow) - @foldableBufferRowsForBufferRowRange(bufferRow, bufferRow).has(bufferRow) + @displayBuffer.isFoldableAtBufferRow(bufferRow) # Extended: Determine whether the given row in screen coordinates is foldable. # @@ -2953,9 +2952,6 @@ class TextEditor extends Model bufferRow = @displayBuffer.bufferRowForScreenRow(screenRow) @isFoldableAtBufferRow(bufferRow) - foldableBufferRowsForBufferRowRange: (startRow, endRow) -> - @displayBuffer.foldableBufferRowsForBufferRowRange(startRow, endRow) - # Extended: Fold the given buffer row if it isn't currently folded, and unfold # it otherwise. toggleFoldAtBufferRow: (bufferRow) -> diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 055e3fe47..5c62f9ecd 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -282,15 +282,6 @@ class TokenizedBuffer extends Model row - increment - foldableRowsForRowRange: (startRow, endRow) -> - scanStartRow = @buffer.previousNonBlankRow(startRow) ? startRow - scanStartRow-- while scanStartRow > 0 and @tokenizedLineForRow(scanStartRow).isComment() - scanEndRow = @buffer.nextNonBlankRow(endRow) ? endRow - foldableRows = new Set - for row in [scanStartRow..scanEndRow] by 1 when @isFoldableAtRow(row) - foldableRows.add(row) - foldableRows - isFoldableAtRow: (row) -> if @largeFileMode false diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index c97a621ac..372f488a6 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -33,7 +33,6 @@ class TokenizedLine endOfLineInvisibles: null lineIsWhitespaceOnly: false firstNonWhitespaceIndex: 0 - foldable: false constructor: (properties) -> @id = idCounter++ From 2a35d19dd35bd3b0d16a6da193a35f954b68d5fa Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 17 Feb 2016 16:00:35 +0100 Subject: [PATCH 209/971] :racehorse: Memoize TokenizedLine.prototype.isComment --- src/tokenized-line.coffee | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index 372f488a6..bf6a6dd2b 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -491,15 +491,20 @@ class TokenizedLine @endOfLineInvisibles.push(eol) if eol isComment: -> + return @isCommentLine if @isCommentLine? + + @isCommentLine = false iterator = @getTokenIterator() while iterator.next() scopes = iterator.getScopes() continue if scopes.length is 1 continue unless NonWhitespaceRegex.test(iterator.getText()) for scope in scopes - return true if CommentScopeRegex.test(scope) + if CommentScopeRegex.test(scope) + @isCommentLine = true + break break - false + @isCommentLine isOnlyWhitespace: -> @lineIsWhitespaceOnly From 6a32a874e9a8048db139a8f86389df053eb3d888 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 17 Feb 2016 16:09:10 +0100 Subject: [PATCH 210/971] :racehorse: Compare markers instead of ranges in Selection This largely reduces the overhead of converting ranges and points from native to javascript objects. --- src/selection.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/selection.coffee b/src/selection.coffee index 2ba66ebb0..465d76a4c 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -755,7 +755,7 @@ class Selection extends Model # # * `otherSelection` A {Selection} to compare against compare: (otherSelection) -> - @getBufferRange().compare(otherSelection.getBufferRange()) + @marker.compare(otherSelection.marker) ### Section: Private Utilities From 17a2bec3966b2614f4574795d24aecd21f0e696c Mon Sep 17 00:00:00 2001 From: Paul Aikman Date: Wed, 17 Feb 2016 16:20:45 +0000 Subject: [PATCH 211/971] Fix for linter error on CI build. --- src/default-directory-provider.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/default-directory-provider.coffee b/src/default-directory-provider.coffee index 094522980..ed4e9ba36 100644 --- a/src/default-directory-provider.coffee +++ b/src/default-directory-provider.coffee @@ -16,7 +16,7 @@ class DefaultDirectoryProvider # * `null` if the given URI is not compatibile with this provider. directoryForURISync: (uri) -> normalizedPath = path.normalize(uri) - host = url.parse(uri).host; + {host} = url.parse(uri) directoryPath = if host uri else if not fs.isDirectorySync(normalizedPath) and fs.isDirectorySync(path.dirname(normalizedPath)) From 68bf2f8dc7c3d69439aaac1cc4ac8f0ae27e7133 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 17 Feb 2016 17:20:54 +0100 Subject: [PATCH 212/971] :green_heart: Fix unrelated failing spec --- spec/text-editor-component-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 08da07dd8..8aaccbd6c 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -544,7 +544,7 @@ describe('TextEditorComponent', function () { editor.setSoftWrapped(true) await nextViewUpdatePromise() - componentNode.style.width = 16 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px' + componentNode.style.width = 17 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px' component.measureDimensions() await nextViewUpdatePromise() }) From b8677c13df3257e90ebff14cbbd3d07a70aa945a Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 17 Feb 2016 11:56:50 -0800 Subject: [PATCH 213/971] Rename config to `allowPendingPaneItems` and improve description --- spec/workspace-spec.coffee | 4 ++-- src/config-schema.coffee | 4 ++-- src/workspace.coffee | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index bb4ac1532..534f2bf61 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -1533,10 +1533,10 @@ describe "Workspace", -> atom.workspace.closeActivePaneItemOrEmptyPaneOrWindow() expect(atom.close).toHaveBeenCalled() - describe "when the core.openPendingPaneItems option is falsey", -> + describe "when the core.allowPendingPaneItems option is falsey", -> it "does not open item with `pending: true` option as pending", -> editor = null - atom.config.set('core.openPendingPaneItems', false) + atom.config.set('core.allowPendingPaneItems', false) waitsForPromise -> atom.workspace.open('sample.js', pending: true).then (o) -> editor = o diff --git a/src/config-schema.coffee b/src/config-schema.coffee index 36c5fc5ba..346551ff5 100644 --- a/src/config-schema.coffee +++ b/src/config-schema.coffee @@ -108,8 +108,8 @@ module.exports = description: 'Automatically update Atom when a new release is available.' type: 'boolean' default: true - openPendingPaneItems: - description: 'Open pane items in pending state, such that only one pending item is open per pane.' + allowPendingPaneItems: + description: 'Allow items to be previewed without adding them to a pane permanently, such as when single clicking files in the tree view.' type: 'boolean' default: true diff --git a/src/workspace.coffee b/src/workspace.coffee index 4ed620481..b26383786 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -414,7 +414,7 @@ class Workspace extends Model split = options.split uri = @project.resolvePath(uri) - if not atom.config.get('core.openPendingPaneItems') + if not atom.config.get('core.allowPendingPaneItems') options.pending = false # Avoid adding URLs as recent documents to work-around this Spotlight crash: From 9bdbe7d6d02e7f61cf7c303ac1c480a30a1bc74a Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 17 Feb 2016 13:10:26 -0800 Subject: [PATCH 214/971] Add first draft Issue template --- ISSUE_TEMPLATE.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 ISSUE_TEMPLATE.md diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..7a013bf39 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,41 @@ +### Flow + +* [ ] Bug + * [ ] Can you reproduce the problem in [safe mode](https://atom.io/docs/latest/hacking-atom-debugging#check-if-the-problem-shows-up-in-safe-mode)? + * [ ] Did you check the [debugging guide](https://atom.io/docs/latest/hacking-atom-debugging)? + * [ ] Did you check the [FAQs on Discuss](https://discuss.atom.io/c/faq)? + * [ ] Are you reporting to the [correct repository](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#atom-and-packages)? + * [ ] Did you [perform a cursory search](https://github.com/issues?q=+is%3Aissue+user%3Aatom) to see if it is already reported? + * For more information on how to write a good bug report [look at the CONTRIBUTING guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#how-do-i-submit-a-good-bug-report). +* [ ] Enhancement + * [ ] Are you running the [latest version of Atom](https://atom.io/docs/latest/hacking-atom-debugging#update-to-the-latest-version)? + * [ ] Did you check to see if [a package](https://atom.io/packages) already offers the feature? + * [ ] Are you reporting to the [correct repository](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#atom-and-packages)? + * [ ] Did you [perform a cursory search](https://github.com/issues?q=+is%3Aissue+user%3Aatom) to see if it is already requested? + * For more recommendations on how to write a good enhancement suggestion [see the CONTRIBUTING guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#how-do-i-submit-a-good-enhancement-suggestion). + +### Description + +[Description of the bug or feature] + +### Repro Steps + +1. [First Step] +2. [Second Step] +3. [and so on...] + +**Expected:** [What you expected to happen] +**Actual:** [What actually happened] + +### Versions + +You can get this information from executing `atom --version` and `apm --version` at the command line: + +* **Atom:** x.y.z +* **Electron:** x.y.z +* **OS:** OS x.y.z +* **APM** + * apm x.y.z + * npm x.y.z + * python x.y.z + * git x.y.z From dca727e3afd7808b2592f746151a03fb72e41d74 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 17 Feb 2016 13:10:42 -0800 Subject: [PATCH 215/971] Add more version information to --version arg --- src/browser/main.coffee | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 20f5bb046..0fd5cc59d 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -83,6 +83,12 @@ setupCompileCache = -> compileCache = require('../compile-cache') compileCache.setAtomHomeDirectory(process.env.ATOM_HOME) +fullVersion = -> + process.stdout.write("Atom : #{app.getVersion()}\n") + process.stdout.write("Electron: #{process.versions.electron}\n") + process.stdout.write("Chrome : #{process.versions.chrome}\n") + process.stdout.write("Node : #{process.versions.node}\n") + parseCommandLine = -> version = app.getVersion() options = yargs(process.argv[1..]).wrap(100) @@ -118,7 +124,7 @@ parseCommandLine = -> options.boolean('portable').describe('portable', 'Set portable mode. Copies the ~/.atom folder to be a sibling of the installed Atom location if a .atom folder is not already there.') options.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.') options.string('timeout').describe('timeout', 'When in test mode, waits until the specified time (in minutes) and kills the process (exit code: 130).') - options.alias('v', 'version').boolean('v').describe('v', 'Print the version.') + options.alias('v', 'version').boolean('v').describe('v', 'Print the version information.') options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.') options.string('socket-path') options.string('user-data-dir') @@ -131,7 +137,7 @@ parseCommandLine = -> process.exit(0) if args.version - process.stdout.write("#{version}\n") + fullVersion() process.exit(0) executedFrom = args['executed-from']?.toString() ? process.cwd() From c9a335df7ad57397a22c13f2964854116447e48f Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 17 Feb 2016 13:27:30 -0800 Subject: [PATCH 216/971] Exclude atom/electron from the search templates --- ISSUE_TEMPLATE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 7a013bf39..d021bb831 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -5,13 +5,13 @@ * [ ] Did you check the [debugging guide](https://atom.io/docs/latest/hacking-atom-debugging)? * [ ] Did you check the [FAQs on Discuss](https://discuss.atom.io/c/faq)? * [ ] Are you reporting to the [correct repository](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#atom-and-packages)? - * [ ] Did you [perform a cursory search](https://github.com/issues?q=+is%3Aissue+user%3Aatom) to see if it is already reported? + * [ ] Did you [perform a cursory search](https://github.com/issues?q=is%3Aissue+user%3Aatom+-repo%3Aatom%2Felectron) to see if it is already reported? * For more information on how to write a good bug report [look at the CONTRIBUTING guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#how-do-i-submit-a-good-bug-report). * [ ] Enhancement * [ ] Are you running the [latest version of Atom](https://atom.io/docs/latest/hacking-atom-debugging#update-to-the-latest-version)? * [ ] Did you check to see if [a package](https://atom.io/packages) already offers the feature? * [ ] Are you reporting to the [correct repository](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#atom-and-packages)? - * [ ] Did you [perform a cursory search](https://github.com/issues?q=+is%3Aissue+user%3Aatom) to see if it is already requested? + * [ ] Did you [perform a cursory search](https://github.com/issues?q=is%3Aissue+user%3Aatom+-repo%3Aatom%2Felectron) to see if it is already requested? * For more recommendations on how to write a good enhancement suggestion [see the CONTRIBUTING guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#how-do-i-submit-a-good-enhancement-suggestion). ### Description From 641a721ff613951e3e9e11596838b16120b9842a Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 17 Feb 2016 13:40:49 -0800 Subject: [PATCH 217/971] Add a break between expected and actual --- ISSUE_TEMPLATE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index d021bb831..7f1eefcc9 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -25,6 +25,7 @@ 3. [and so on...] **Expected:** [What you expected to happen] + **Actual:** [What actually happened] ### Versions From e16318374507dcd378695be84dba27496320c96c Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 17 Feb 2016 13:59:13 -0800 Subject: [PATCH 218/971] Repro Steps -> Steps to Reproduce --- ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 7f1eefcc9..5b0337ad2 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -18,7 +18,7 @@ [Description of the bug or feature] -### Repro Steps +### Steps to Reproduce 1. [First Step] 2. [Second Step] From a146401f21dfb7804799a9420d1b891975297022 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 17 Feb 2016 14:14:59 -0800 Subject: [PATCH 219/971] Terminate pending state for opened file if pending option is false --- spec/workspace-spec.coffee | 16 ++++++++++++++++ src/workspace.coffee | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 534f2bf61..ef89636a8 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -585,6 +585,22 @@ describe "Workspace", -> open = -> workspace.open('file1', workspace.getActivePane()) expect(open).toThrow() + describe "when the file is already open in pending state", -> + it "should terminate the pending state", -> + editor = null + + waitsForPromise -> + atom.workspace.open('sample.js', pending: true).then (o) -> editor = o + + runs -> + expect(editor.isPending()).toBe true + + waitsForPromise -> + atom.workspace.open('sample.js').then (o) -> editor = o + + runs -> + expect(editor.isPending()).toBe false + describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> pane = workspace.getActivePane() diff --git a/src/workspace.coffee b/src/workspace.coffee index b26383786..0bfff7e0f 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -476,7 +476,8 @@ class Workspace extends Model activateItem = options.activateItem ? true if uri? - item = pane.itemForURI(uri) + if item = pane.itemForURI(uri) + item.terminatePendingState?() if item.isPending?() and not options.pending item ?= opener(uri, options) for opener in @getOpeners() when not item try From cc8a0ccbf7b757132854c156524a150c49dddbb1 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 17 Feb 2016 14:30:28 -0800 Subject: [PATCH 220/971] Squish all the printing into one call --- src/browser/main.coffee | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 0fd5cc59d..0adb08e0b 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -84,10 +84,13 @@ setupCompileCache = -> compileCache.setAtomHomeDirectory(process.env.ATOM_HOME) fullVersion = -> - process.stdout.write("Atom : #{app.getVersion()}\n") - process.stdout.write("Electron: #{process.versions.electron}\n") - process.stdout.write("Chrome : #{process.versions.chrome}\n") - process.stdout.write("Node : #{process.versions.node}\n") + process.stdout.write """ + Atom : #{app.getVersion()} + Electron: #{process.versions.electron} + Chrome : #{process.versions.chrome} + Node : #{process.versions.node} + + """ parseCommandLine = -> version = app.getVersion() From e514de3eb2bb961822a397e89284fb1c183f2b6c Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 17 Feb 2016 14:40:18 -0800 Subject: [PATCH 221/971] Remove versions template --- ISSUE_TEMPLATE.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 5b0337ad2..94df389b9 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -30,13 +30,4 @@ ### Versions -You can get this information from executing `atom --version` and `apm --version` at the command line: - -* **Atom:** x.y.z -* **Electron:** x.y.z -* **OS:** OS x.y.z -* **APM** - * apm x.y.z - * npm x.y.z - * python x.y.z - * git x.y.z +You can get this information from executing `atom --version` and `apm --version` at the command line. From 6202409d494d74d1d5d4373bd935e90732d18d1c Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 17 Feb 2016 14:56:40 -0800 Subject: [PATCH 222/971] fullVersion -> writeFullVersion --- src/browser/main.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 0adb08e0b..54c06db14 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -83,7 +83,7 @@ setupCompileCache = -> compileCache = require('../compile-cache') compileCache.setAtomHomeDirectory(process.env.ATOM_HOME) -fullVersion = -> +writeFullVersion = -> process.stdout.write """ Atom : #{app.getVersion()} Electron: #{process.versions.electron} @@ -140,7 +140,7 @@ parseCommandLine = -> process.exit(0) if args.version - fullVersion() + writeFullVersion() process.exit(0) executedFrom = args['executed-from']?.toString() ? process.cwd() From 4e141aaaa9886bb866f7499f9c4eecf9207f7409 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 17 Feb 2016 15:01:55 -0800 Subject: [PATCH 223/971] Make the flow section less verbose --- ISSUE_TEMPLATE.md | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 94df389b9..596f4ecea 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,18 +1,13 @@ ### Flow -* [ ] Bug - * [ ] Can you reproduce the problem in [safe mode](https://atom.io/docs/latest/hacking-atom-debugging#check-if-the-problem-shows-up-in-safe-mode)? - * [ ] Did you check the [debugging guide](https://atom.io/docs/latest/hacking-atom-debugging)? - * [ ] Did you check the [FAQs on Discuss](https://discuss.atom.io/c/faq)? - * [ ] Are you reporting to the [correct repository](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#atom-and-packages)? - * [ ] Did you [perform a cursory search](https://github.com/issues?q=is%3Aissue+user%3Aatom+-repo%3Aatom%2Felectron) to see if it is already reported? - * For more information on how to write a good bug report [look at the CONTRIBUTING guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#how-do-i-submit-a-good-bug-report). -* [ ] Enhancement - * [ ] Are you running the [latest version of Atom](https://atom.io/docs/latest/hacking-atom-debugging#update-to-the-latest-version)? - * [ ] Did you check to see if [a package](https://atom.io/packages) already offers the feature? - * [ ] Are you reporting to the [correct repository](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#atom-and-packages)? - * [ ] Did you [perform a cursory search](https://github.com/issues?q=is%3Aissue+user%3Aatom+-repo%3Aatom%2Felectron) to see if it is already requested? - * For more recommendations on how to write a good enhancement suggestion [see the CONTRIBUTING guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#how-do-i-submit-a-good-enhancement-suggestion). +* [ ] Can you reproduce the problem in [safe mode](https://atom.io/docs/latest/hacking-atom-debugging#check-if-the-problem-shows-up-in-safe-mode)? +* [ ] Are you running the [latest version of Atom](https://atom.io/docs/latest/hacking-atom-debugging#update-to-the-latest-version)? +* [ ] Did you check the [debugging guide](https://atom.io/docs/latest/hacking-atom-debugging)? +* [ ] Did you check the [FAQs on Discuss](https://discuss.atom.io/c/faq)? +* [ ] Are you reporting to the [correct repository](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#atom-and-packages)? +* [ ] Did you [perform a cursory search](https://github.com/issues?q=is%3Aissue+user%3Aatom+-repo%3Aatom%2Felectron) to see if your bug or enhancement is already reported? + +For more information on how to write a good [bug report](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#how-do-i-submit-a-good-bug-report) or [enhancement request](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#how-do-i-submit-a-good-enhancement-suggestion), see the `CONTRIBUTING` guide ### Description From cfe63f8be222c56b8a45ebda27bcda41b3f6cf0d Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 17 Feb 2016 16:21:21 -0800 Subject: [PATCH 224/971] Flow -> Prerequisites --- ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 596f4ecea..aa8e3e1db 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,4 +1,4 @@ -### Flow +### Prerequisites * [ ] Can you reproduce the problem in [safe mode](https://atom.io/docs/latest/hacking-atom-debugging#check-if-the-problem-shows-up-in-safe-mode)? * [ ] Are you running the [latest version of Atom](https://atom.io/docs/latest/hacking-atom-debugging#update-to-the-latest-version)? From e8966ad4c6ab6cd96a89b34b4ec7c573136b89e3 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 17 Feb 2016 17:29:52 -0800 Subject: [PATCH 225/971] Add missing period --- ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index aa8e3e1db..03aae4a4c 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -7,7 +7,7 @@ * [ ] Are you reporting to the [correct repository](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#atom-and-packages)? * [ ] Did you [perform a cursory search](https://github.com/issues?q=is%3Aissue+user%3Aatom+-repo%3Aatom%2Felectron) to see if your bug or enhancement is already reported? -For more information on how to write a good [bug report](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#how-do-i-submit-a-good-bug-report) or [enhancement request](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#how-do-i-submit-a-good-enhancement-suggestion), see the `CONTRIBUTING` guide +For more information on how to write a good [bug report](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#how-do-i-submit-a-good-bug-report) or [enhancement request](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#how-do-i-submit-a-good-enhancement-suggestion), see the `CONTRIBUTING` guide. ### Description From 0088053de47b5ec0566eedcd24fd7393e7ddc1a6 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 17 Feb 2016 20:35:06 -0800 Subject: [PATCH 226/971] Allow pasting white space when `autoIndentOnPaste` is enabled --- src/selection.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/selection.coffee b/src/selection.coffee index 465d76a4c..c4046677b 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -378,7 +378,7 @@ class Selection extends Model indentAdjustment = @editor.indentLevelForLine(precedingText) - options.indentBasis @adjustIndent(remainingLines, indentAdjustment) - if options.autoIndent and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0 + if options.autoIndent and NonWhitespaceRegExp.test(text) and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0 autoIndentFirstLine = true firstLine = precedingText + firstInsertedLine desiredIndentLevel = @editor.languageMode.suggestedIndentForLineAtBufferRow(oldBufferRange.start.row, firstLine) From 60b9cf1270ee18da387a2bafcde99e91183343e2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 18 Feb 2016 14:12:44 +0100 Subject: [PATCH 227/971] :racehorse: Cache regexes in getRegexForProperty --- src/language-mode.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/language-mode.coffee b/src/language-mode.coffee index ac8a5af76..dc5003cac 100644 --- a/src/language-mode.coffee +++ b/src/language-mode.coffee @@ -10,6 +10,7 @@ class LanguageMode # editor - The {TextEditor} to associate with constructor: (@editor, @config) -> {@buffer} = @editor + @regexesByPattern = {} destroy: -> @@ -328,7 +329,8 @@ class LanguageMode getRegexForProperty: (scopeDescriptor, property) -> if pattern = @config.get(property, scope: scopeDescriptor) - new OnigRegExp(pattern) + @regexesByPattern[pattern] ?= new OnigRegExp(pattern) + @regexesByPattern[pattern] increaseIndentRegexForScopeDescriptor: (scopeDescriptor) -> @getRegexForProperty(scopeDescriptor, 'editor.increaseIndentPattern') From d6132888faf4fbcc2a7a8ebbee7b89d122140938 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 18 Feb 2016 14:18:27 +0100 Subject: [PATCH 228/971] :green_heart: Attempt to fix specs --- spec/text-editor-component-spec.js | 2 +- src/text-editor-presenter.coffee | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 8aaccbd6c..08da07dd8 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -544,7 +544,7 @@ describe('TextEditorComponent', function () { editor.setSoftWrapped(true) await nextViewUpdatePromise() - componentNode.style.width = 17 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px' + componentNode.style.width = 16 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px' component.measureDimensions() await nextViewUpdatePromise() }) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 3266eea70..a3504caa8 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -90,7 +90,7 @@ class TextEditorPresenter @updateLineDecorations() @updateBlockDecorations() - @updateTilesState(true) + @updateTilesState() @updating = false @state @@ -112,7 +112,7 @@ class TextEditorPresenter @updateHiddenInputState() @updateContentState() @updateHighlightDecorations() if @shouldUpdateDecorations - @updateTilesState(false) + @updateTilesState() @updateCursorsState() @updateOverlaysState() @updateLineNumberGutterState() @@ -327,7 +327,7 @@ class TextEditorPresenter clearScreenRowsToMeasure: -> @screenRowsToMeasure = [] - updateTilesState: (updateLinesState) -> + updateTilesState: -> return unless @startRow? and @endRow? and @lineHeight? screenRows = @getScreenRows() @@ -367,9 +367,8 @@ class TextEditorPresenter gutterTile.display = "block" gutterTile.zIndex = zIndex - if updateLinesState - @updateLinesState(tile, rowsWithinTile) - @updateLineNumbersState(gutterTile, rowsWithinTile) + @updateLinesState(tile, rowsWithinTile) + @updateLineNumbersState(gutterTile, rowsWithinTile) visibleTiles[tileStartRow] = true zIndex++ From 2fa41d1783f5330962db5bc38103185534d0ed3d Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 18 Feb 2016 09:40:41 -0500 Subject: [PATCH 229/971] :arrow_up: fuzzy-finder@1.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 41c7180c5..f5097344e 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", "find-and-replace": "0.197.2", - "fuzzy-finder": "1.0.0", + "fuzzy-finder": "1.0.1", "git-diff": "1.0.0", "go-to-line": "0.30.0", "grammar-selector": "0.48.1", From 1089647a0ce8c9224474cafc9157a8a7d9373fd0 Mon Sep 17 00:00:00 2001 From: Mandeep Singh Date: Thu, 18 Feb 2016 21:18:47 +0530 Subject: [PATCH 230/971] :memo: change atom build directory to current one [ci skip] --- docs/build-instructions/linux.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/build-instructions/linux.md b/docs/build-instructions/linux.md index fc2f73ee7..1d746bcd6 100644 --- a/docs/build-instructions/linux.md +++ b/docs/build-instructions/linux.md @@ -64,7 +64,7 @@ If you have problems with permissions don't forget to prefix with `sudo` script/build ``` - This will create the atom application at `$TMPDIR/atom-build/Atom`. + This will create the atom application at `out/Atom`. 4. Install the `atom` and `apm` commands to `/usr/local/bin` by executing: @@ -74,7 +74,7 @@ If you have problems with permissions don't forget to prefix with `sudo` To use the newly installed Atom, quit and restart all running Atom instances. -5. *Optionally*, you may generate distributable packages of Atom at `$TMPDIR/atom-build`. Currently, `.deb` and `.rpm` package types are supported. To create a `.deb` package run: +5. *Optionally*, you may generate distributable packages of Atom at `out`. Currently, `.deb` and `.rpm` package types are supported. To create a `.deb` package run: ```sh script/grunt mkdeb From 43656a98617d62560df497248a9382c269eed0f7 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 18 Feb 2016 09:17:34 -0800 Subject: [PATCH 231/971] Load packages before deserializing state --- spec/atom-environment-spec.coffee | 12 +-- src/application-delegate.coffee | 6 ++ src/atom-environment.coffee | 108 +++++++++-------------- src/browser/atom-application.coffee | 8 +- src/browser/atom-window.coffee | 11 +-- src/initialize-application-window.coffee | 14 ++- src/window-event-handler.coffee | 4 +- src/window-load-settings-helpers.coffee | 9 -- 8 files changed, 68 insertions(+), 104 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 561ecf3b5..e2ce6670a 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -162,7 +162,6 @@ describe "AtomEnvironment", -> spyOn(atom, 'getLoadSettings').andCallFake -> loadSettings spyOn(atom, 'serialize').andReturn({stuff: 'cool'}) - spyOn(atom, 'deserialize') atom.project.setPaths([dir1, dir2]) # State persistence will fail if other Atom instances are running @@ -172,16 +171,13 @@ describe "AtomEnvironment", -> waitsForPromise -> atom.saveState().then -> - atom.loadState() - - runs -> - expect(atom.deserialize).not.toHaveBeenCalled() + atom.loadState().then (state) -> + expect(state).toBeNull() waitsForPromise -> loadSettings.initialPaths = [dir2, dir1] - atom.loadState() - runs -> - expect(atom.deserialize).toHaveBeenCalledWith({stuff: 'cool'}) + atom.loadState().then (state) -> + expect(state).toEqual({stuff: 'cool'}) it "saves state on keydown and mousedown events", -> spyOn(atom, 'saveState') diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index f87827886..cc1f4c946 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -22,6 +22,12 @@ class ApplicationDelegate closeWindow: -> ipcRenderer.send("call-window-method", "close") + getTemporaryWindowState: -> + ipcHelpers.call('get-temporary-window-state') + + setTemporaryWindowState: (state) -> + ipcHelpers.call('set-temporary-window-state', state) + getWindowSize: -> [width, height] = remote.getCurrentWindow().getSize() {width, height} diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 8c2d3e73d..776640aee 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -537,21 +537,16 @@ class AtomEnvironment extends Model # Restores the full screen and maximized state after the window has resized to # prevent resize glitches. displayWindow: -> - @restoreWindowDimensions().then (dimensions) => + @restoreWindowDimensions().then => steps = [ @restoreWindowBackground(), @show(), @focus() ] steps.push(@setFullScreen(true)) if @workspace.fullScreen - steps.push(@maximize()) if dimensions?.maximized and process.platform isnt 'darwin' + steps.push(@maximize()) if @windowDimensions?.maximized and process.platform isnt 'darwin' Promise.all(steps) - if @isFirstLoad() - loadSettings = getWindowLoadSettings() - loadSettings.firstLoad = false - setWindowLoadSettings(loadSettings) - # Get the dimensions of this window. # # Returns an {Object} with the following keys: @@ -592,10 +587,10 @@ class AtomEnvironment extends Model isValidDimensions: ({x, y, width, height}={}) -> width > 0 and height > 0 and x + width > 0 and y + height > 0 - storeDefaultWindowDimensions: -> - dimensions = @getWindowDimensions() - if @isValidDimensions(dimensions) - localStorage.setItem("defaultWindowDimensions", JSON.stringify(dimensions)) + storeWindowDimensions: -> + @windowDimensions = @getWindowDimensions() + if @isValidDimensions(@windowDimensions) + localStorage.setItem("defaultWindowDimensions", JSON.stringify(@windowDimensions)) getDefaultWindowDimensions: -> {windowDimensions} = @getLoadSettings() @@ -615,21 +610,9 @@ class AtomEnvironment extends Model {x: 0, y: 0, width: Math.min(1024, width), height} restoreWindowDimensions: -> - dimensions = null - - # The first time the window's loaded we want to use the default dimensions. - # But after that, e.g., when the window's been reloaded, we want to use the - # dimensions we've saved for it. - if not @isFirstLoad() - dimensions = @windowDimensions - - unless @isValidDimensions(dimensions) - dimensions = @getDefaultWindowDimensions() - @setWindowDimensions(dimensions).then -> dimensions - - storeWindowDimensions: -> - dimensions = @getWindowDimensions() - @windowDimensions = dimensions if @isValidDimensions(dimensions) + unless @windowDimensions? and @isValidDimensions(@windowDimensions) + @windowDimensions = @getDefaultWindowDimensions() + @setWindowDimensions(@windowDimensions).then -> @windowDimensions restoreWindowBackground: -> if backgroundColor = window.localStorage.getItem('atom:window-background-color') @@ -647,32 +630,39 @@ class AtomEnvironment extends Model # Call this method when establishing a real application window. startEditorWindow: -> - @commandInstaller.installAtomCommand false, (error) -> - console.warn error.message if error? - @commandInstaller.installApmCommand false, (error) -> - console.warn error.message if error? + @loadState().then (state) => + @windowDimensions = state?.windowDimensions + @displayWindow().then => + @commandInstaller.installAtomCommand false, (error) -> + console.warn error.message if error? + @commandInstaller.installApmCommand false, (error) -> + console.warn error.message if error? - @disposables.add(@applicationDelegate.onDidOpenLocations(@openLocations.bind(this))) - @disposables.add(@applicationDelegate.onApplicationMenuCommand(@dispatchApplicationMenuCommand.bind(this))) - @disposables.add(@applicationDelegate.onContextMenuCommand(@dispatchContextMenuCommand.bind(this))) - @listenForUpdates() + @disposables.add(@applicationDelegate.onDidOpenLocations(@openLocations.bind(this))) + @disposables.add(@applicationDelegate.onApplicationMenuCommand(@dispatchApplicationMenuCommand.bind(this))) + @disposables.add(@applicationDelegate.onContextMenuCommand(@dispatchContextMenuCommand.bind(this))) + @listenForUpdates() - @registerDefaultTargetForKeymaps() + @registerDefaultTargetForKeymaps() - @packages.loadPackages() + @packages.loadPackages() - @document.body.appendChild(@views.getView(@workspace)) - @backgroundStylesheet?.remove() + startTime = Date.now() + @deserialize(state) if state? + @deserializeTimings.atom = Date.now() - startTime - @watchProjectPath() + @document.body.appendChild(@views.getView(@workspace)) + @backgroundStylesheet?.remove() - @packages.activate() - @keymaps.loadUserKeymap() - @requireUserInitScript() unless @getLoadSettings().safeMode + @watchProjectPath() - @menu.update() + @packages.activate() + @keymaps.loadUserKeymap() + @requireUserInitScript() unless @getLoadSettings().safeMode - @openInitialEmptyEditorIfNecessary() + @menu.update() + + @openInitialEmptyEditorIfNecessary() serialize: -> version: @constructor.version @@ -828,30 +818,16 @@ class AtomEnvironment extends Model if storageKey = @getStateKey(@project?.getPaths()) @stateStore.save(storageKey, state) else - @getCurrentWindow().loadSettings.windowState = JSON.stringify(state) - Promise.resolve() + @applicationDelegate.setTemporaryWindowState(state) loadState: -> - return Promise.resolve() unless @enablePersistence - - startTime = Date.now() - - statePromise = null - if stateKey = @getStateKey(@getLoadSettings().initialPaths) - statePromise = @stateStore.load(stateKey) - - if not statePromise? and windowState = @getLoadSettings().windowState - try - statePromise = Promise.resolve(JSON.parse(@getLoadSettings().windowState)) - catch error - console.warn "Error parsing window state: #{statePath} #{error.stack}", error - - if statePromise? - statePromise.then (state) => - @deserializeTimings.atom = Date.now() - startTime - @deserialize(state) if state? + if @enablePersistence + if stateKey = @getStateKey(@getLoadSettings().initialPaths) + @stateStore.load(stateKey) + else + @applicationDelegate.getTemporaryWindowState() else - Promise.resolve() + Promise.resolve(null) deserialize: (state) -> if grammarOverridesByPath = state.grammars?.grammarOverridesByPath @@ -859,8 +835,6 @@ class AtomEnvironment extends Model @setFullScreen(state.fullScreen) - @windowDimensions = state.windowDimensions if state.windowDimensions - @packages.packageStates = state.packageStates ? {} startTime = Date.now() diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 93dd428ce..7ea0342c8 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -280,6 +280,12 @@ class AtomApplication ipcHelpers.respondTo 'hide-window', (win) -> win.hide() + ipcHelpers.respondTo 'get-temporary-window-state', (win) -> + win.temporaryState + + ipcHelpers.respondTo 'set-temporary-window-state', (win, state) -> + win.temporaryState = state + ipcMain.on 'did-cancel-window-unload', => @quitting = false @@ -528,7 +534,7 @@ class AtomApplication if pack.urlMain packagePath = @packages.resolvePackagePath(packageName) windowInitializationScript = path.resolve(packagePath, pack.urlMain) - windowDimensions = @focusedWindow()?.getDimensions() + windowDimensions = @getDimensionsForNewWindow() new AtomWindow({windowInitializationScript, @resourcePath, devMode, safeMode, urlToOpen, windowDimensions}) else console.log "Package '#{pack.name}' does not have a url main: #{urlToOpen}" diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 828366c4f..634242e0d 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -41,13 +41,11 @@ class AtomWindow @handleEvents() loadSettings = _.extend({}, settings) - loadSettings.windowState ?= '{}' loadSettings.appVersion = app.getVersion() loadSettings.resourcePath = @resourcePath loadSettings.devMode ?= false loadSettings.safeMode ?= false loadSettings.atomHome = process.env.ATOM_HOME - loadSettings.firstLoad = true loadSettings.clearWindowState ?= false # Only send to the first non-spec window created @@ -64,23 +62,18 @@ class AtomWindow loadSettings.initialPaths.sort() - @browserWindow.loadSettings = loadSettings @browserWindow.once 'window:loaded', => @emit 'window:loaded' @loaded = true @setLoadSettings(loadSettings) @browserWindow.focusOnWebView() if @isSpec + @browserWindow.temporaryState = {windowDimensions} if windowDimensions? hasPathToOpen = not (locationsToOpen.length is 1 and not locationsToOpen[0].pathToOpen?) @openLocations(locationsToOpen) if hasPathToOpen and not @isSpecWindow() - setLoadSettings: (loadSettingsObj) -> - # Ignore the windowState when passing loadSettings via URL, since it could - # be quite large. - loadSettings = _.clone(loadSettingsObj) - delete loadSettings['windowState'] - + setLoadSettings: (loadSettings) -> @browserWindow.loadURL url.format protocol: 'file' pathname: "#{@resourcePath}/static/index.html" diff --git a/src/initialize-application-window.coffee b/src/initialize-application-window.coffee index 61f0c2439..cea4e1c3c 100644 --- a/src/initialize-application-window.coffee +++ b/src/initialize-application-window.coffee @@ -23,12 +23,10 @@ module.exports = ({blobStore}) -> enablePersistence: true }) - atom.loadState().then -> - atom.displayWindow().then -> - atom.startEditorWindow() + atom.startEditorWindow().then -> - # Workaround for focus getting cleared upon window creation - windowFocused = -> - window.removeEventListener('focus', windowFocused) - setTimeout (-> document.querySelector('atom-workspace').focus()), 0 - window.addEventListener('focus', windowFocused) + # Workaround for focus getting cleared upon window creation + windowFocused = -> + window.removeEventListener('focus', windowFocused) + setTimeout (-> document.querySelector('atom-workspace').focus()), 0 + window.addEventListener('focus', windowFocused) diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index d3a231f77..f0d616f19 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -133,7 +133,7 @@ class WindowEventHandler handleWindowBlur: => @document.body.classList.add('is-blurred') - @atomEnvironment.storeDefaultWindowDimensions() + @atomEnvironment.storeWindowDimensions() handleWindowBeforeunload: => confirmed = @atomEnvironment.workspace?.confirmClose(windowCloseRequested: true) @@ -141,8 +141,8 @@ class WindowEventHandler @atomEnvironment.hide() @reloadRequested = false - @atomEnvironment.storeDefaultWindowDimensions() @atomEnvironment.storeWindowDimensions() + @atomEnvironment.saveState() if confirmed @atomEnvironment.unloadEditorWindow() else diff --git a/src/window-load-settings-helpers.coffee b/src/window-load-settings-helpers.coffee index 4bb514301..73fd31a3d 100644 --- a/src/window-load-settings-helpers.coffee +++ b/src/window-load-settings-helpers.coffee @@ -5,15 +5,6 @@ windowLoadSettings = null exports.getWindowLoadSettings = -> windowLoadSettings ?= JSON.parse(window.decodeURIComponent(window.location.hash.substr(1))) - clone = _.deepClone(windowLoadSettings) - - # The windowLoadSettings.windowState could be large, request it only when needed. - clone.__defineGetter__ 'windowState', -> - remote.getCurrentWindow().loadSettings.windowState - clone.__defineSetter__ 'windowState', (value) -> - remote.getCurrentWindow().loadSettings.windowState = value - - clone exports.setWindowLoadSettings = (settings) -> windowLoadSettings = settings From f4c48b0977f5741f1b2a1be376039355c4d36425 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 18 Feb 2016 10:57:11 -0800 Subject: [PATCH 232/971] Add 'behavior' to make the fields clearer --- ISSUE_TEMPLATE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 03aae4a4c..73ff2f50d 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -19,9 +19,9 @@ For more information on how to write a good [bug report](https://github.com/atom 2. [Second Step] 3. [and so on...] -**Expected:** [What you expected to happen] +**Expected behavior:** [What you expected to happen] -**Actual:** [What actually happened] +**Actual behavior:** [What actually happened] ### Versions From 92bcd50a3c7e45e11d369e64c24875a58d60a260 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 18 Feb 2016 11:42:59 -0800 Subject: [PATCH 233/971] :arrow_up: welcome --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4adfb85f5..c0997cf17 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "timecop": "0.33.1", "tree-view": "0.201.2", "update-package-dependencies": "0.10.0", - "welcome": "0.33.1", + "welcome": "0.34.0", "whitespace": "0.32.2", "wrap-guide": "0.38.1", "language-c": "0.51.1", From 746afb98ade993c5598ab8e80413825accbcaf82 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 18 Feb 2016 12:04:08 -0800 Subject: [PATCH 234/971] Add spec for inserting white-space-only lines --- spec/selection-spec.coffee | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/selection-spec.coffee b/spec/selection-spec.coffee index ec40e32cc..319e2d438 100644 --- a/spec/selection-spec.coffee +++ b/spec/selection-spec.coffee @@ -83,3 +83,11 @@ describe "Selection", -> selection.setBufferRange([[2, 0], [2, 10]]) selection.destroy() expect(selection.marker.isDestroyed()).toBeTruthy() + + describe ".insertText(text, options)", -> + it "allows pasting white space only lines when autoIndent is enabled", -> + selection.setBufferRange [[0, 0], [0, 0]] + selection.insertText(" \n \n\n", autoIndent: true) + expect(buffer.lineForRow(0)).toBe " " + expect(buffer.lineForRow(1)).toBe " " + expect(buffer.lineForRow(2)).toBe "" From fe1e9d5b10ff7cee15cd34d3dcd19823b1a5db5a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 18 Feb 2016 13:11:11 -0800 Subject: [PATCH 235/971] Fix error in specs --- src/atom-environment.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 776640aee..74049d94d 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -543,7 +543,7 @@ class AtomEnvironment extends Model @show(), @focus() ] - steps.push(@setFullScreen(true)) if @workspace.fullScreen + steps.push(@setFullScreen(true)) if @windowDimensions?.fullScreen steps.push(@maximize()) if @windowDimensions?.maximized and process.platform isnt 'darwin' Promise.all(steps) From dec328dab7aac6f089c3a2a011589fcd0bd36947 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 18 Feb 2016 15:09:22 -0800 Subject: [PATCH 236/971] Remove stray console.log in spec --- spec/babel-spec.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/babel-spec.coffee b/spec/babel-spec.coffee index 02f0583ee..e95b000cb 100644 --- a/spec/babel-spec.coffee +++ b/spec/babel-spec.coffee @@ -15,7 +15,6 @@ describe "Babel transpiler support", -> CompileCache.setCacheDirectory(temp.mkdirSync('compile-cache')) for cacheKey in Object.keys(require.cache) if cacheKey.startsWith(path.join(__dirname, 'fixtures', 'babel')) - console.log('deleting', cacheKey) delete require.cache[cacheKey] afterEach -> From 8a24364e80765943ee5ad43997b4f66946f642c7 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 18 Feb 2016 15:10:16 -0800 Subject: [PATCH 237/971] Import `fit` in text-editor-component-spec So that you can focus a test while keeping it async --- spec/text-editor-component-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 08da07dd8..37a9751e1 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -1,6 +1,6 @@ /** @babel */ -import {it, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers' +import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers' import TextEditorElement from '../src/text-editor-element' import _, {extend, flatten, last, toArray} from 'underscore-plus' From aabaf1c2ba1abccd23fa510b2e059a2d7e4f8a76 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 18 Feb 2016 15:10:43 -0800 Subject: [PATCH 238/971] Use a temporary storage directory when running specs --- src/browser/main.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 54c06db14..a65d69306 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -7,6 +7,7 @@ process.on 'uncaughtException', (error={}) -> {crashReporter, app} = require 'electron' fs = require 'fs-plus' path = require 'path' +temp = require 'temp' yargs = require 'yargs' console.log = require 'nslog' @@ -33,6 +34,8 @@ start = -> if args.userDataDir? app.setPath('userData', args.userDataDir) + else if args.test + app.setPath('userData', temp.mkdirSync('atom-test-data')) app.on 'ready', -> app.removeListener 'open-file', addPathToOpen From 2e7101e5eb563798ad6ff2a5ef46861ca60f4468 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 18 Feb 2016 15:11:26 -0800 Subject: [PATCH 239/971] Don't display an editor window in atom-environment-spec --- spec/atom-environment-spec.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index e2ce6670a..0aefe0414 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -259,6 +259,7 @@ describe "AtomEnvironment", -> } atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, window, document: fakeDocument}) spyOn(atomEnvironment.packages, 'getAvailablePackagePaths').andReturn [] + spyOn(atomEnvironment, 'displayWindow').andReturn Promise.resolve() atomEnvironment.startEditorWindow() atomEnvironment.unloadEditorWindow() atomEnvironment.destroy() From 43dc50f9073e77c9f92f4b6b1871deca25f35a15 Mon Sep 17 00:00:00 2001 From: Wliu Date: Thu, 18 Feb 2016 22:32:00 -0500 Subject: [PATCH 240/971] Pass the notification manager when splitting panes Fixes #9587 --- src/pane.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane.coffee b/src/pane.coffee index 6c3612e5e..9dff81d5c 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -661,7 +661,7 @@ class Pane extends Model @parent.replaceChild(this, new PaneAxis({@container, orientation, children: [this], @flexScale})) @setFlexScale(1) - newPane = new Pane(extend({@applicationDelegate, @deserializerManager, @config}, params)) + newPane = new Pane(extend({@applicationDelegate, @notificationManager, @deserializerManager, @config}, params)) switch side when 'before' then @parent.insertChildBefore(this, newPane) when 'after' then @parent.insertChildAfter(this, newPane) From 4d8ec993aa318626467ebd98796d2771b3c0354d Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 18 Feb 2016 20:57:18 -0800 Subject: [PATCH 241/971] :arrow_up: language-gfm@0.85.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c0997cf17..4be8c1e78 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ "language-coffee-script": "0.46.1", "language-csharp": "0.11.0", "language-css": "0.36.0", - "language-gfm": "0.84.0", + "language-gfm": "0.85.0", "language-git": "0.12.1", "language-go": "0.42.0", "language-html": "0.44.1", From 53693b4d0f741e6246c17171e7812cc5b742b996 Mon Sep 17 00:00:00 2001 From: Arnaud Rinquin Date: Fri, 19 Feb 2016 01:22:48 +0000 Subject: [PATCH 242/971] Add the -a, --add CLI option --- spec/atom-environment-spec.coffee | 8 ++++++++ src/atom-environment.coffee | 4 ++-- src/browser/atom-application.coffee | 21 ++++++++++++--------- src/browser/main.coffee | 4 +++- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 0aefe0414..1f8eb08e7 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -275,6 +275,14 @@ describe "AtomEnvironment", -> atom.openLocations([{pathToOpen}]) expect(atom.project.getPaths()[0]).toBe __dirname + describe "then a second path is opened with forceAddToWindow", -> + it "adds the second path to the project's paths", -> + firstPathToOpen = __dirname + secondPathToOpen = path.resolve(__dirname, './fixtures') + atom.openLocations([{pathToOpen: firstPathToOpen}]) + atom.openLocations([{pathToOpen: secondPathToOpen, forceAddToWindow: true}]) + expect(atom.project.getPaths()).toEqual([firstPathToOpen, secondPathToOpen]) + describe "when the opened path does not exist but its parent directory does", -> it "adds the parent directory to the project paths", -> pathToOpen = path.join(__dirname, 'this-path-does-not-exist.txt') diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 74049d94d..304a6300f 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -897,8 +897,8 @@ class AtomEnvironment extends Model openLocations: (locations) -> needsProjectPaths = @project?.getPaths().length is 0 - for {pathToOpen, initialLine, initialColumn} in locations - if pathToOpen? and needsProjectPaths + for {pathToOpen, initialLine, initialColumn, forceAddToWindow} in locations + if pathToOpen? and (needsProjectPaths or forceAddToWindow) if fs.existsSync(pathToOpen) @project.addPath(pathToOpen) else if fs.existsSync(path.dirname(pathToOpen)) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 7ea0342c8..b6d45b2fb 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -85,16 +85,16 @@ class AtomApplication else @loadState(options) or @openPath(options) - openWithOptions: ({pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState}) -> + openWithOptions: ({pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState, addToLastWindow}) -> if test @runTests({headless: true, devMode, @resourcePath, executedFrom, pathsToOpen, logFile, timeout}) else if pathsToOpen.length > 0 - @openPaths({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState}) + @openPaths({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow}) else if urlsToOpen.length > 0 @openUrl({urlToOpen, devMode, safeMode}) for urlToOpen in urlsToOpen else # Always open a editor window if this is the first instance of Atom. - @openPath({pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState}) + @openPath({pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow}) # Public: Removes the {AtomWindow} from the global window list. removeWindow: (window) -> @@ -409,8 +409,9 @@ class AtomApplication # :safeMode - Boolean to control the opened window's safe mode. # :profileStartup - Boolean to control creating a profile of the startup time. # :window - {AtomWindow} to open file paths in. - openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState} = {}) -> - @openPaths({pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState}) + # :addToLastWindow - Boolean of whether this should be opened in last focused window. + openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow} = {}) -> + @openPaths({pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow}) # Public: Opens multiple paths, in existing windows if possible. # @@ -422,11 +423,12 @@ class AtomApplication # :safeMode - Boolean to control the opened window's safe mode. # :windowDimensions - Object with height and width keys. # :window - {AtomWindow} to open file paths in. - openPaths: ({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearWindowState}={}) -> + # :addToLastWindow - Boolean of whether this should be opened in last focused window. + openPaths: ({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearWindowState, addToLastWindow}={}) -> devMode = Boolean(devMode) safeMode = Boolean(safeMode) clearWindowState = Boolean(clearWindowState) - locationsToOpen = (@locationForPathToOpen(pathToOpen, executedFrom) for pathToOpen in pathsToOpen) + locationsToOpen = (@locationForPathToOpen(pathToOpen, executedFrom, addToLastWindow) for pathToOpen in pathsToOpen) pathsToOpen = (locationToOpen.pathToOpen for locationToOpen in locationsToOpen) unless pidToKillWhenClosed or newWindow @@ -435,6 +437,7 @@ class AtomApplication unless existingWindow? if currentWindow = window ? @lastFocusedWindow existingWindow = currentWindow if ( + addToLastWindow or currentWindow.devMode is devMode and ( stats.every((stat) -> stat.isFile?()) or @@ -603,7 +606,7 @@ class AtomApplication catch error require.resolve(path.resolve(__dirname, '..', '..', 'spec', 'jasmine-test-runner')) - locationForPathToOpen: (pathToOpen, executedFrom='') -> + locationForPathToOpen: (pathToOpen, executedFrom='', forceAddToWindow) -> return {pathToOpen} unless pathToOpen pathToOpen = pathToOpen.replace(/[:\s]+$/, '') @@ -619,7 +622,7 @@ class AtomApplication unless url.parse(pathToOpen).protocol? pathToOpen = path.resolve(executedFrom, fs.normalize(pathToOpen)) - {pathToOpen, initialLine, initialColumn} + {pathToOpen, initialLine, initialColumn, forceAddToWindow} # Opens a native dialog to prompt the user for a path. # diff --git a/src/browser/main.coffee b/src/browser/main.coffee index a65d69306..b4df62bd6 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -132,6 +132,7 @@ parseCommandLine = -> options.string('timeout').describe('timeout', 'When in test mode, waits until the specified time (in minutes) and kills the process (exit code: 130).') options.alias('v', 'version').boolean('v').describe('v', 'Print the version information.') options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.') + options.alias('a', 'add').boolean('a').describe('add', 'Open path as a new project in last used window.') options.string('socket-path') options.string('user-data-dir') options.boolean('clear-window-state').describe('clear-window-state', 'Delete all Atom environment state.') @@ -146,6 +147,7 @@ parseCommandLine = -> writeFullVersion() process.exit(0) + addToLastWindow = args['add'] executedFrom = args['executed-from']?.toString() ? process.cwd() devMode = args['dev'] safeMode = args['safe'] @@ -183,6 +185,6 @@ parseCommandLine = -> {resourcePath, devResourcePath, pathsToOpen, urlsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, socketPath, userDataDir, profileStartup, timeout, setPortable, - clearWindowState} + clearWindowState, addToLastWindow} start() From e06e708f3b5726edaa812cff24b30545df336857 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 19 Feb 2016 10:03:28 +0100 Subject: [PATCH 243/971] :arrow_up: bracket-matcher --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4be8c1e78..96c063e6f 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "autosave": "0.23.1", "background-tips": "0.26.0", "bookmarks": "0.38.2", - "bracket-matcher": "0.79.1", + "bracket-matcher": "0.80.0", "command-palette": "0.38.0", "deprecation-cop": "0.54.1", "dev-live-reload": "0.47.0", From f834ba21d3a3b679b465175a6070024573678962 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 19 Feb 2016 10:05:13 +0100 Subject: [PATCH 244/971] :arrow_up: status-bar --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 96c063e6f..cde107d15 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.232.4", "snippets": "1.0.1", "spell-check": "0.66.1", - "status-bar": "1.0.0", + "status-bar": "1.1.0", "styleguide": "0.45.2", "symbols-view": "0.111.1", "tabs": "0.90.2", From 7b83db160f5abafadceb3c61586084fd862aa838 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Fri, 19 Feb 2016 12:37:17 +0100 Subject: [PATCH 245/971] remove Open Roadmap menu item, fixes #10884 --- menus/darwin.cson | 1 - menus/linux.cson | 1 - menus/win32.cson | 1 - src/browser/atom-application.coffee | 1 - 4 files changed, 4 deletions(-) diff --git a/menus/darwin.cson b/menus/darwin.cson index a2636887d..7fa2aaf6d 100644 --- a/menus/darwin.cson +++ b/menus/darwin.cson @@ -200,7 +200,6 @@ submenu: [ { label: 'Terms of Use', command: 'application:open-terms-of-use' } { label: 'Documentation', command: 'application:open-documentation' } - { label: 'Roadmap', command: 'application:open-roadmap' } { label: 'Frequently Asked Questions', command: 'application:open-faq' } { type: 'separator' } { label: 'Community Discussions', command: 'application:open-discussions' } diff --git a/menus/linux.cson b/menus/linux.cson index 1276748d8..7cfd72885 100644 --- a/menus/linux.cson +++ b/menus/linux.cson @@ -174,7 +174,6 @@ { label: "VERSION", enabled: false } { type: 'separator' } { label: '&Documentation', command: 'application:open-documentation' } - { label: 'Roadmap', command: 'application:open-roadmap' } { label: 'Frequently Asked Questions', command: 'application:open-faq' } { type: 'separator' } { label: 'Community Discussions', command: 'application:open-discussions' } diff --git a/menus/win32.cson b/menus/win32.cson index a7d41b28f..349e3e064 100644 --- a/menus/win32.cson +++ b/menus/win32.cson @@ -177,7 +177,6 @@ { label: 'Downloading Update', enabled: false, visible: false} { type: 'separator' } { label: '&Documentation', command: 'application:open-documentation' } - { label: 'Roadmap', command: 'application:open-roadmap' } { label: 'Frequently Asked Questions', command: 'application:open-faq' } { type: 'separator' } { label: 'Community Discussions', command: 'application:open-discussions' } diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 7ea0342c8..428d03464 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -175,7 +175,6 @@ class AtomApplication @on 'application:open-documentation', -> shell.openExternal('https://atom.io/docs/latest/?app') @on 'application:open-discussions', -> shell.openExternal('https://discuss.atom.io') - @on 'application:open-roadmap', -> shell.openExternal('https://atom.io/roadmap?app') @on 'application:open-faq', -> shell.openExternal('https://atom.io/faq') @on 'application:open-terms-of-use', -> shell.openExternal('https://atom.io/terms') @on 'application:report-issue', -> shell.openExternal('https://github.com/atom/atom/blob/master/CONTRIBUTING.md#submitting-issues') From 59f261324e9ef9f2bd0b6ecb6157d0bab148fe60 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 19 Feb 2016 10:41:39 -0800 Subject: [PATCH 246/971] :arrow_up: spell-check@0.67.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cde107d15..6fb8df1ed 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "package-generator": "0.41.1", "settings-view": "0.232.4", "snippets": "1.0.1", - "spell-check": "0.66.1", + "spell-check": "0.67.0", "status-bar": "1.1.0", "styleguide": "0.45.2", "symbols-view": "0.111.1", From e39d200a77011e782275b0c4ae062c9834ea5ca9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sun, 21 Feb 2016 12:21:27 +0100 Subject: [PATCH 247/971] :arrow_up: autocomplete-plus --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6fb8df1ed..c764111bb 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.0", "autocomplete-html": "0.7.2", - "autocomplete-plus": "2.27.1", + "autocomplete-plus": "2.28.0", "autocomplete-snippets": "1.10.0", "autoflow": "0.27.0", "autosave": "0.23.1", From 0d118afb6b338f9f5b95093498455caf694fdb27 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 12 Feb 2016 15:04:49 -0800 Subject: [PATCH 248/971] Send keyup events through the `keymapManager` --- src/window-event-handler.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index f0d616f19..ce08344c6 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -15,7 +15,8 @@ class WindowEventHandler @addEventListener(@window, 'focus', @handleWindowFocus) @addEventListener(@window, 'blur', @handleWindowBlur) - @addEventListener(@document, 'keydown', @handleDocumentKeydown) + @addEventListener(@document, 'keyup', @handleDocumentKeyEvent) + @addEventListener(@document, 'keydown', @handleDocumentKeyEvent) @addEventListener(@document, 'drop', @handleDocumentDrop) @addEventListener(@document, 'dragover', @handleDocumentDragover) @addEventListener(@document, 'contextmenu', @handleDocumentContextmenu) @@ -66,7 +67,7 @@ class WindowEventHandler target.addEventListener(eventName, handler) @subscriptions.add(new Disposable(-> target.removeEventListener(eventName, handler))) - handleDocumentKeydown: (event) => + handleDocumentKeyEvent: (event) => @atomEnvironment.keymaps.handleKeyboardEvent(event) event.stopImmediatePropagation() From e3015f6d25313e73b446d236deaa9763b81747b1 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 17 Feb 2016 20:46:41 -0800 Subject: [PATCH 249/971] :arrow_up: keybinding-resolver@0.34.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c764111bb..a49def252 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "grammar-selector": "0.48.1", "image-view": "0.56.0", "incompatible-packages": "0.25.1", - "keybinding-resolver": "0.33.0", + "keybinding-resolver": "0.34.0", "line-ending-selector": "0.3.1", "link": "0.31.0", "markdown-preview": "0.157.3", From 4bd3bf4187a1cc927c6104bbbd677f581695676b Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Sun, 21 Feb 2016 13:59:15 -0800 Subject: [PATCH 250/971] :arrow_up: atom-keymap@6.3.1 to support keyup bindings --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a49def252..09bb824cc 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "electronVersion": "0.36.7", "dependencies": { "async": "0.2.6", - "atom-keymap": "^6.2.0", + "atom-keymap": "^6.3.1", "babel-core": "^5.8.21", "bootstrap": "^3.3.4", "cached-run-in-this-context": "0.4.1", From 677568d9aff4359d115b825887e477a31efab28f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 22 Feb 2016 11:30:36 +0100 Subject: [PATCH 251/971] Use `window.requestIdleCallback` in `StateStore.prototype.save` This should alleviate some of the pressure of serializing changes on the main thread. We're assuming that `deadline.timeRemaining()` is high enough to compute the serialization because there's no simple path to serialize state across many `requestIdleCallback`s (e.g. because state might change between two callbacks). --- src/state-store.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/state-store.js b/src/state-store.js index 2175bbe4c..53998d802 100644 --- a/src/state-store.js +++ b/src/state-store.js @@ -24,16 +24,18 @@ class StateStore { } save (key, value) { - return this.dbPromise.then(db => { - if (!db) return + return new Promise((resolve, reject) => { + window.requestIdleCallback(deadline => { + this.dbPromise.then(db => { + if (db == null) resolve() - return new Promise((resolve, reject) => { - var request = db.transaction(['states'], 'readwrite') - .objectStore('states') - .put({value: value, storedAt: new Date().toString()}, key) + var request = db.transaction(['states'], 'readwrite') + .objectStore('states') + .put({value: value, storedAt: new Date().toString()}, key) - request.onsuccess = resolve - request.onerror = reject + request.onsuccess = resolve + request.onerror = reject + }) }) }) } From a361cd7f40c385a8d34c8a833889f7756e155c12 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 22 Feb 2016 11:44:08 +0100 Subject: [PATCH 252/971] :racehorse: Use JSON.stringify to serialize state --- src/atom-environment.coffee | 15 ++++++++++----- src/state-store.js | 26 ++++++++++++++++---------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 74049d94d..f6fa03932 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -813,12 +813,17 @@ class AtomEnvironment extends Model saveState: -> return Promise.resolve() unless @enablePersistence - state = @serialize() - if storageKey = @getStateKey(@project?.getPaths()) - @stateStore.save(storageKey, state) - else - @applicationDelegate.setTemporaryWindowState(state) + new Promise (resolve, reject) => + window.requestIdleCallback => + state = @serialize() + savePromise = + if storageKey = @getStateKey(@project?.getPaths()) + @stateStore.save(storageKey, state) + else + @applicationDelegate.setTemporaryWindowState(state) + savePromise.catch(reject).then(resolve) + loadState: -> if @enablePersistence diff --git a/src/state-store.js b/src/state-store.js index 53998d802..13c9a0462 100644 --- a/src/state-store.js +++ b/src/state-store.js @@ -24,18 +24,19 @@ class StateStore { } save (key, value) { + // Serialize values using JSON.stringify, as it seems way faster than IndexedDB structured clone. + // (Ref.: https://bugs.chromium.org/p/chromium/issues/detail?id=536620) + let jsonValue = JSON.stringify(value) return new Promise((resolve, reject) => { - window.requestIdleCallback(deadline => { - this.dbPromise.then(db => { - if (db == null) resolve() + this.dbPromise.then(db => { + if (db == null) resolve() - var request = db.transaction(['states'], 'readwrite') - .objectStore('states') - .put({value: value, storedAt: new Date().toString()}, key) + var request = db.transaction(['states'], 'readwrite') + .objectStore('states') + .put({value: jsonValue, storedAt: new Date().toString(), isJSON: true}, key) - request.onsuccess = resolve - request.onerror = reject - }) + request.onsuccess = resolve + request.onerror = reject }) }) } @@ -51,7 +52,12 @@ class StateStore { request.onsuccess = (event) => { let result = event.target.result - resolve(result ? result.value : null) + if (result) { + // TODO: remove this when state will be serialized only via JSON. + resolve(result.isJSON ? JSON.parse(result.value) : result.value) + } else { + resolve(null) + } } request.onerror = (event) => reject(event) From 04cab58c56f02143ba77520eeb1fa943a431601e Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 22 Feb 2016 11:18:54 -0500 Subject: [PATCH 253/971] Failing test. --- spec/git-repository-async-spec.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 9999afc63..d05690911 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -590,6 +590,12 @@ describe('GitRepositoryAsync', () => { const relativizedPath = repo.relativize(`${workdir}/a/b.txt`, workdir) expect(relativizedPath).toBe('a/b.txt') }) + + it('preserves file case', () => { + const workdir = '/tmp/foo/bar/baz/' + const relativizedPath = repo.relativize(`${workdir}a/README.txt`, workdir) + expect(relativizedPath).toBe('a/README.txt') + }) }) describe('.getShortHead(path)', () => { From cfa7f99b01c063a7f74ee265399473230eda6ec5 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 22 Feb 2016 11:19:54 -0500 Subject: [PATCH 254/971] Preserve case as long as we can. --- src/git-repository-async.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index cf9f9d6fe..28a1a9df6 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -202,6 +202,7 @@ export default class GitRepositoryAsync { workingDirectory = workingDirectory.replace(/\/$/, '') + const originalPath = _path if (this.isCaseInsensitive) { _path = _path.toLowerCase() workingDirectory = workingDirectory.toLowerCase() @@ -212,7 +213,6 @@ export default class GitRepositoryAsync { _path = _path.replace(/^\/private\//, '/') workingDirectory = workingDirectory.replace(/^\/private\//, '/') - const originalPath = _path if (_path.indexOf(workingDirectory) === 0) { return originalPath.substring(workingDirectory.length + 1) } else if (_path === workingDirectory) { From 01751c038aaafd5782bcacd13ee7ab4df61a9685 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 22 Feb 2016 11:53:38 -0500 Subject: [PATCH 255/971] Ensure we're treating it as case insensitive. --- spec/git-repository-async-spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index d05690911..fa5b0d711 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -592,6 +592,8 @@ describe('GitRepositoryAsync', () => { }) it('preserves file case', () => { + repo.isCaseInsensitive = true + const workdir = '/tmp/foo/bar/baz/' const relativizedPath = repo.relativize(`${workdir}a/README.txt`, workdir) expect(relativizedPath).toBe('a/README.txt') From 8df13c584981a2c3475acdd406c59afb5e3c37e0 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 22 Feb 2016 11:54:05 -0500 Subject: [PATCH 256/971] Standardize the path before copying it. --- src/git-repository-async.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 28a1a9df6..894b1216a 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -202,32 +202,35 @@ export default class GitRepositoryAsync { workingDirectory = workingDirectory.replace(/\/$/, '') + // Depending on where the paths come from, they may have a '/private/' + // prefix. Standardize by stripping that out. + _path = _path.replace(/^\/private\//i, '/') + workingDirectory = workingDirectory.replace(/^\/private\//i, '/') + const originalPath = _path + const originalWorkingDirectory = workingDirectory if (this.isCaseInsensitive) { _path = _path.toLowerCase() workingDirectory = workingDirectory.toLowerCase() } - // Depending on where the paths come from, they may have a '/private/' - // prefix. Standardize by stripping that out. - _path = _path.replace(/^\/private\//, '/') - workingDirectory = workingDirectory.replace(/^\/private\//, '/') - if (_path.indexOf(workingDirectory) === 0) { - return originalPath.substring(workingDirectory.length + 1) + return originalPath.substring(originalWorkingDirectory.length + 1) } else if (_path === workingDirectory) { return '' } if (openedWorkingDirectory) { + openedWorkingDirectory = openedWorkingDirectory.replace(/\/$/, '') + openedWorkingDirectory = openedWorkingDirectory.replace(/^\/private\//i, '/') + + const originalOpenedWorkingDirectory = openedWorkingDirectory if (this.isCaseInsensitive) { openedWorkingDirectory = openedWorkingDirectory.toLowerCase() } - openedWorkingDirectory = openedWorkingDirectory.replace(/\/$/, '') - openedWorkingDirectory = openedWorkingDirectory.replace(/^\/private\//, '/') if (_path.indexOf(openedWorkingDirectory) === 0) { - return originalPath.substring(openedWorkingDirectory.length + 1) + return originalPath.substring(originalOpenedWorkingDirectory.length + 1) } else if (_path === openedWorkingDirectory) { return '' } From 1a7f0abbfc343a0be2d3cfa4e6bb9d5fb1ffe3e4 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 22 Feb 2016 09:02:19 -0800 Subject: [PATCH 257/971] :arrow_up: electron to fix command-backtick bug --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c764111bb..d9d4689f8 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.36.7", + "electronVersion": "0.36.8", "dependencies": { "async": "0.2.6", "atom-keymap": "^6.2.0", From bb1c048e12d5a3930b4c536d3a3ac453673ac4f2 Mon Sep 17 00:00:00 2001 From: Arnaud Rinquin Date: Mon, 22 Feb 2016 18:37:20 +0000 Subject: [PATCH 258/971] Add -a, --add specific integration tests --- spec/integration/startup-spec.coffee | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/spec/integration/startup-spec.coffee b/spec/integration/startup-spec.coffee index e82d34e57..f6b0e1cf3 100644 --- a/spec/integration/startup-spec.coffee +++ b/spec/integration/startup-spec.coffee @@ -123,6 +123,34 @@ describe "Starting Atom", -> .waitForPaneItemCount(0, 1000) .treeViewRootDirectories() .then ({value}) -> expect(value).toEqual([otherTempDirPath]) + describe "when using the -a, --add option", -> + it "reuses that window and add the folder to project paths", -> + fourthTempDir = temp.mkdirSync("a-fourth-dir") + fourthTempFilePath = path.join(fourthTempDir, "a-file") + fs.writeFileSync(fourthTempFilePath, "4 - This file was already here.") + + fifthTempDir = temp.mkdirSync("a-fifth-dir") + fifthTempFilePath = path.join(fifthTempDir, "a-file") + fs.writeFileSync(fifthTempFilePath, "5 - This file was already here.") + + runAtom [path.join(tempDirPath, "new-file")], {ATOM_HOME: atomHome}, (client) -> + client + .waitForPaneItemCount(1, 5000) + + # Opening another file reuses the same window and add parent dir to + # project paths. + .startAnotherAtom(['-a', fourthTempFilePath], ATOM_HOME: atomHome) + .waitForPaneItemCount(2, 5000) + .waitForWindowCount(1, 1000) + .treeViewRootDirectories() + .then ({value}) -> expect(value).toEqual([tempDirPath, fourthTempDir]) + .execute -> atom.workspace.getActiveTextEditor().getText() + .then ({value: text}) -> expect(text).toBe "4 - This file was already here." + + # Opening another directory resuses the same window and add the folder to project paths. + .startAnotherAtom(['--add', fifthTempDir], ATOM_HOME: atomHome) + .treeViewRootDirectories() + .then ({value}) -> expect(value).toEqual([tempDirPath, fourthTempDir, fifthTempDir]) it "opens the new window offset from the other window", -> runAtom [path.join(tempDirPath, "new-file")], {ATOM_HOME: atomHome}, (client) -> From 606dc0299c9e8f2a80a0321ef5cdcc89e933012c Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 22 Feb 2016 11:11:54 -0800 Subject: [PATCH 259/971] :arrow_up: tree-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d9d4689f8..56cb68c9d 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.111.1", "tabs": "0.90.2", "timecop": "0.33.1", - "tree-view": "0.201.2", + "tree-view": "0.201.3", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From 2d4474eb8b31af3c952bcb68543bb0540d849c5d Mon Sep 17 00:00:00 2001 From: Carl Henderson Date: Mon, 22 Feb 2016 15:13:38 -0500 Subject: [PATCH 260/971] search only new data for new lines rather than entire buffer --- src/buffered-process.coffee | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/buffered-process.coffee b/src/buffered-process.coffee index c7097d711..183a1d99a 100644 --- a/src/buffered-process.coffee +++ b/src/buffered-process.coffee @@ -111,11 +111,12 @@ class BufferedProcess stream.on 'data', (data) => return if @killed + bufferedLength = buffered.length buffered += data - lastNewlineIndex = buffered.lastIndexOf('\n') + lastNewlineIndex = data.lastIndexOf('\n') if lastNewlineIndex isnt -1 - onLines(buffered.substring(0, lastNewlineIndex + 1)) - buffered = buffered.substring(lastNewlineIndex + 1) + onLines(buffered.substring(0, lastNewlineIndex + bufferedLength + 1)) + buffered = buffered.substring(lastNewlineIndex + bufferedLength + 1) stream.on 'close', => return if @killed From 6457e0573edf783ca5d95a73e2df7b4a36ed13b6 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 22 Feb 2016 13:28:00 -0800 Subject: [PATCH 261/971] :arrow_up: keybinding-resolver to fix specs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 09bb824cc..58a17c648 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "grammar-selector": "0.48.1", "image-view": "0.56.0", "incompatible-packages": "0.25.1", - "keybinding-resolver": "0.34.0", + "keybinding-resolver": "0.35.0", "line-ending-selector": "0.3.1", "link": "0.31.0", "markdown-preview": "0.157.3", From ea5500a124945b06fc9ca70f6924f860e097643e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 22 Feb 2016 16:31:06 -0800 Subject: [PATCH 262/971] Print line number of wait timeout in presenter spec --- spec/text-editor-presenter-spec.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 57c674033..543f8349d 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -91,7 +91,9 @@ describe "TextEditorPresenter", -> expectNoStateUpdate = (presenter, fn) -> expectStateUpdatedToBe(false, presenter, fn) waitsForStateToUpdate = (presenter, fn) -> - waitsFor "presenter state to update", 1000, (done) -> + line = new Error().stack.split('\n')[2].split(':')[1] + + waitsFor "presenter state to update at line #{line}", 1000, (done) -> disposable = presenter.onDidUpdateState -> disposable.dispose() process.nextTick(done) From 98559f5d342a5a7e37ab6108ae747eab893cf9c9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 22 Feb 2016 16:31:17 -0800 Subject: [PATCH 263/971] Add missing --- spec/text-editor-presenter-spec.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 543f8349d..05ac87c0c 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -1340,7 +1340,9 @@ describe "TextEditorPresenter", -> blockDecoration3 = addBlockDecorationBeforeScreenRow(7) blockDecoration4 = null - waitsForStateToUpdate presenter, blockDecoration4 = addBlockDecorationAfterScreenRow(7) + waitsForStateToUpdate presenter, -> + blockDecoration4 = addBlockDecorationAfterScreenRow(7) + runs -> expect(lineStateForScreenRow(presenter, 0).precedingBlockDecorations).toEqual([blockDecoration1]) expect(lineStateForScreenRow(presenter, 0).followingBlockDecorations).toEqual([]) From 8896f15b5d436370edd07c4a84aae0b604a129a2 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 12:26:22 -0800 Subject: [PATCH 264/971] :arrow_up: autocomplete-plus@2.29.0 for Unicode support --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b430d4316..4c5e13a03 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.0", "autocomplete-html": "0.7.2", - "autocomplete-plus": "2.28.0", + "autocomplete-plus": "2.29.0", "autocomplete-snippets": "1.10.0", "autoflow": "0.27.0", "autosave": "0.23.1", From 70efaf4cdde1080280f15b7729887fb0e751e0a4 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 16:04:37 -0800 Subject: [PATCH 265/971] :arrow_up: notifications@0.62.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4c5e13a03..d3d2dc945 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "link": "0.31.0", "markdown-preview": "0.157.3", "metrics": "0.53.1", - "notifications": "0.62.2", + "notifications": "0.62.3", "open-on-github": "1.0.0", "package-generator": "0.41.1", "settings-view": "0.232.4", From 5612f1a46c4d3237c20f7239a9d606b27ee11e02 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 24 Feb 2016 14:42:09 +0100 Subject: [PATCH 266/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d3d2dc945..db472e5ae 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.3.1", + "text-buffer": "8.3.2", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 2853c2999ccfa60d725f7c3eba4f51caef4fcc45 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 24 Feb 2016 12:59:44 -0800 Subject: [PATCH 267/971] :arrow_up: fuzzy-finder --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index db472e5ae..ae923f99c 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", "find-and-replace": "0.197.2", - "fuzzy-finder": "1.0.1", + "fuzzy-finder": "1.0.2", "git-diff": "1.0.0", "go-to-line": "0.30.0", "grammar-selector": "0.48.1", From a487110521ef5f546f5d5b6c7be4ab3d6afe83ab Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 24 Feb 2016 18:19:13 -0800 Subject: [PATCH 268/971] Refactor pending state to live in pane instead of items * New public API `workspace.setItemNotPending` that packages can use to set an item to set an item to not pending (e.g. when the user interacts with the item) * Pending state for newly opened items with `{pending: true}` is now tracked by `Pane` instead of the item, and packages like `tabs` that query this information now get it from the Pane. --- src/pane.coffee | 37 +++++++++++++++++++++++++++---------- src/text-editor.coffee | 21 ++++++--------------- src/workspace.coffee | 10 ++++++++-- 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 9dff81d5c..0dec5cbce 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -53,7 +53,7 @@ class Pane extends Model items: compact(@items.map((item) -> item.serialize?())) activeItemURI: activeItemURI focused: @focused - flexScale: @flexScale + flexScale: @flexScale # TODO: is it okay to not serialize pending state? does it need to be restored? getParent: -> @parent @@ -342,13 +342,15 @@ class Pane extends Model # Public: Make the given item *active*, causing it to be displayed by # the pane's view. - activateItem: (item) -> + # + # * `pending` TODO + activateItem: (item, pending=false) -> if item? - if @activeItem?.isPending?() + if @isItemPending(@activeItem) index = @getActiveItemIndex() else index = @getActiveItemIndex() + 1 - @addItem(item, index, false) + @addItem(item, index, false, pending) @setActiveItem(item) # Public: Add the given item to the pane. @@ -357,19 +359,18 @@ class Pane extends Model # view. # * `index` (optional) {Number} indicating the index at which to add the item. # If omitted, the item is added after the current active item. + # * `pending` TODO # # Returns the added item. - addItem: (item, index=@getActiveItemIndex() + 1, moved=false) -> + addItem: (item, index=@getActiveItemIndex() + 1, moved=false, pending=false) -> throw new Error("Pane items must be objects. Attempted to add item #{item}.") unless item? and typeof item is 'object' throw new Error("Adding a pane item with URI '#{item.getURI?()}' that has already been destroyed") if item.isDestroyed?() return if item in @items - if item.isPending?() - for existingItem, i in @items - if existingItem.isPending?() - @destroyItem(existingItem) - break + pendingItem = @getPendingItem() + @destroyItem(pendingItem) if pendingItem? + @setPendingItem(item) if pending if typeof item.onDidDestroy is 'function' @itemSubscriptions.set item, item.onDidDestroy => @removeItem(item, false) @@ -379,6 +380,20 @@ class Pane extends Model @setActiveItem(item) unless @getActiveItem()? item + setPendingItem: (item) => + @pendingItem = item + @emitter.emit 'did-terminate-pending-state' if not item + + getPendingItem: => + @pendingItem + + isItemPending: (item) => + @pendingItem is item + + onDidTerminatePendingState: (callback) => + @emitter.on 'did-terminate-pending-state', -> + callback() + # Public: Add the given items to the pane. # # * `items` An {Array} of items to add. Items can be views or models with @@ -397,6 +412,8 @@ class Pane extends Model index = @items.indexOf(item) return if index is -1 + @pendingItem = null if @isItemPending(item) + @emitter.emit 'will-remove-item', {item, index, destroyed: not moved, moved} @unsubscribeFromItem(item) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c0a6f2057..e8207ca1e 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -92,7 +92,7 @@ class TextEditor extends Model softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, @pending + @project, @assert, @applicationDelegate } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -111,6 +111,7 @@ class TextEditor extends Model @cursors = [] @cursorsByMarkerId = new Map @selections = [] + @bufferHasChanged = false buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ @@ -151,7 +152,7 @@ class TextEditor extends Model firstVisibleScreenColumn: @getFirstVisibleScreenColumn() displayBuffer: @displayBuffer.serialize() selectionsMarkerLayerId: @selectionsMarkerLayer.id - pending: @isPending() + bufferHasChanged: @bufferHasChanged subscribeToBuffer: -> @buffer.retain() @@ -163,9 +164,9 @@ class TextEditor extends Model @disposables.add @buffer.onDidChangeEncoding => @emitter.emit 'did-change-encoding', @getEncoding() @disposables.add @buffer.onDidDestroy => @destroy() - if @pending - @disposables.add @buffer.onDidChangeModified => - @terminatePendingState() if @buffer.isModified() + @disposables.add @buffer.onDidChangeModified => + atom.workspace.setItemNotPending(this) if not @bufferHasChanged and @buffer.isModified() + @bufferHasChanged = true @preserveCursorPositionOnBufferReload() @@ -575,13 +576,6 @@ class TextEditor extends Model getEditorWidthInChars: -> @displayBuffer.getEditorWidthInChars() - onDidTerminatePendingState: (callback) -> - @emitter.on 'did-terminate-pending-state', callback - - terminatePendingState: -> - return if not @pending - @pending = false - @emitter.emit 'did-terminate-pending-state' ### Section: File Details @@ -666,9 +660,6 @@ class TextEditor extends Model # Essential: Returns {Boolean} `true` if this editor has no content. isEmpty: -> @buffer.isEmpty() - # Returns {Boolean} `true` if this editor is pending and `false` if it is permanent. - isPending: -> Boolean(@pending) - # Copies the current file path to the native clipboard. copyPathToClipboard: (relative = false) -> if filePath = @getPath() diff --git a/src/workspace.coffee b/src/workspace.coffee index 0bfff7e0f..1f6648271 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -477,7 +477,7 @@ class Workspace extends Model if uri? if item = pane.itemForURI(uri) - item.terminatePendingState?() if item.isPending?() and not options.pending + pane.setPendingItem(null) if not options.pending item ?= opener(uri, options) for opener in @getOpeners() when not item try @@ -500,7 +500,7 @@ class Workspace extends Model return item if pane.isDestroyed() @itemOpened(item) - pane.activateItem(item) if activateItem + pane.activateItem(item, options.pending) if activateItem pane.activate() if activatePane initialLine = initialColumn = 0 @@ -515,6 +515,12 @@ class Workspace extends Model @emitter.emit 'did-open', {uri, pane, item, index} item + setItemNotPending: (item) => + for pane in @getPanes() + if item is pane.getPendingItem() + pane.setPendingItem(null) + break + openTextFile: (uri, options) -> filePath = @project.resolvePath(uri) From e94653d391bfeeebf7c1f424d04930f98d0fe584 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 24 Feb 2016 20:29:49 -0800 Subject: [PATCH 269/971] :arrow_up: tree-view@0.201.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ae923f99c..a9eab6b3b 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.111.1", "tabs": "0.90.2", "timecop": "0.33.1", - "tree-view": "0.201.3", + "tree-view": "0.201.4", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From 24865fd254353b5ca854d4b8ed5807adcfcc691b Mon Sep 17 00:00:00 2001 From: Alfred UC Date: Thu, 25 Feb 2016 20:30:25 +0900 Subject: [PATCH 270/971] Fix a inconsistent getLineCount() use --- src/text-editor.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c0a6f2057..9c93f4925 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -3215,7 +3215,7 @@ class TextEditor extends Model # top of the visible area. setFirstVisibleScreenRow: (screenRow, fromView) -> unless fromView - maxScreenRow = @getLineCount() - 1 + maxScreenRow = @getScreenLineCount() - 1 unless @config.get('editor.scrollPastEnd') height = @displayBuffer.getHeight() lineHeightInPixels = @displayBuffer.getLineHeightInPixels() @@ -3233,7 +3233,7 @@ class TextEditor extends Model height = @displayBuffer.getHeight() lineHeightInPixels = @displayBuffer.getLineHeightInPixels() if height? and lineHeightInPixels? - Math.min(@firstVisibleScreenRow + Math.floor(height / lineHeightInPixels), @getLineCount() - 1) + Math.min(@firstVisibleScreenRow + Math.floor(height / lineHeightInPixels), @getScreenLineCount() - 1) else null From a62965dc62336f8b344eefc20c8a0440edf69435 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 25 Feb 2016 09:25:13 -0800 Subject: [PATCH 271/971] Default the channel based on the package.json, not the branch --- build/Gruntfile.coffee | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index a34e1fbd4..2340f8823 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -35,22 +35,8 @@ module.exports = (grunt) -> # Options installDir = grunt.option('install-dir') - buildDir = grunt.option('build-dir') - buildDir ?= 'out' - buildDir = path.resolve(buildDir) - - channel = grunt.option('channel') - releasableBranches = ['stable', 'beta'] - if process.env.APPVEYOR and not process.env.APPVEYOR_PULL_REQUEST_NUMBER - channel ?= process.env.APPVEYOR_REPO_BRANCH if process.env.APPVEYOR_REPO_BRANCH in releasableBranches - - if process.env.TRAVIS and not process.env.TRAVIS_PULL_REQUEST - channel ?= process.env.TRAVIS_BRANCH if process.env.TRAVIS_BRANCH in releasableBranches - - if process.env.JANKY_BRANCH - channel ?= process.env.JANKY_BRANCH if process.env.JANKY_BRANCH in releasableBranches - - channel ?= 'dev' + buildDir = path.resolve(grunt.option('build-dir') ? 'out') + channel = grunt.option('channel') ? getDefaultReleaseChannel() metadata = packageJson appName = packageJson.productName @@ -310,3 +296,15 @@ module.exports = (grunt) -> unless process.platform is 'linux' or grunt.option('no-install') defaultTasks.push 'install' grunt.registerTask('default', defaultTasks) + +getDefaultReleaseChannel = -> + {version} = packageJson + if version.match(/dev/) or isBuildingPR() + 'dev' + else if version.match(/beta/) + 'beta' + else + 'stable' + +isBuildingPR = -> + process.env.APPVEYOR_PULL_REQUEST_NUMBER? or process.env.TRAVIS_PULL_REQUEST? From 77bae0fad1df9be04d2f98ad05e49bf099a6dcbb Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 25 Feb 2016 10:24:41 -0800 Subject: [PATCH 272/971] When creating draft releases, choose release branch based on version number --- build/Gruntfile.coffee | 22 ++++++++++++++-------- build/tasks/publish-build-task.coffee | 13 ++++--------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index 2340f8823..d9375d05c 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -34,9 +34,10 @@ module.exports = (grunt) -> grunt.file.setBase(path.resolve('..')) # Options + [defaultChannel, releaseBranch] = getDefaultChannelAndReleaseBranch(packageJson.version) installDir = grunt.option('install-dir') buildDir = path.resolve(grunt.option('build-dir') ? 'out') - channel = grunt.option('channel') ? getDefaultReleaseChannel() + channel = grunt.option('channel') ? defaultChannel metadata = packageJson appName = packageJson.productName @@ -175,7 +176,7 @@ module.exports = (grunt) -> pkg: grunt.file.readJSON('package.json') atom: { - appName, channel, metadata, + appName, channel, metadata, releaseBranch, appFileName, apmFileName, appDir, buildDir, contentsDir, installDir, shellAppDir, symbolsDir, } @@ -297,14 +298,19 @@ module.exports = (grunt) -> defaultTasks.push 'install' grunt.registerTask('default', defaultTasks) -getDefaultReleaseChannel = -> - {version} = packageJson +getDefaultChannelAndReleaseBranch = (version) -> if version.match(/dev/) or isBuildingPR() - 'dev' - else if version.match(/beta/) - 'beta' + channel = 'dev' + releaseBranch = null else - 'stable' + if version.match(/beta/) + channel = 'beta' + else + channel = 'stable' + + minorVersion = version.match(/^\d\.\d/)[0] + releaseBranch = "#{minorVersion}-releases" + [channel, releaseBranch] isBuildingPR = -> process.env.APPVEYOR_PULL_REQUEST_NUMBER? or process.env.TRAVIS_PULL_REQUEST? diff --git a/build/tasks/publish-build-task.coffee b/build/tasks/publish-build-task.coffee index 4f8df6336..de46eb4fe 100644 --- a/build/tasks/publish-build-task.coffee +++ b/build/tasks/publish-build-task.coffee @@ -31,14 +31,9 @@ module.exports = (gruntObject) -> cp path.join(docsOutputDir, 'api.json'), path.join(buildDir, 'atom-api.json') grunt.registerTask 'upload-assets', 'Upload the assets to a GitHub release', -> - channel = grunt.config.get('atom.channel') - switch channel - when 'stable' - isPrerelease = false - when 'beta' - isPrerelease = true - else - return + releaseBranch = grunt.config.get('atom.releaseBranch') + isPrerelease = grunt.config.get('atom.channel') is 'beta' + return unless releaseBranch? doneCallback = @async() startTime = Date.now() @@ -55,7 +50,7 @@ module.exports = (gruntObject) -> zipAssets buildDir, assets, (error) -> return done(error) if error? - getAtomDraftRelease isPrerelease, channel, (error, release) -> + getAtomDraftRelease isPrerelease, releaseBranch, (error, release) -> return done(error) if error? assetNames = (asset.assetName for asset in assets) deleteExistingAssets release, assetNames, (error) -> From 9c5c171eb59b9d7a17668c6b9bf25477523b09da Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 25 Feb 2016 10:28:03 -0800 Subject: [PATCH 273/971] Don't refer to stable and beta as branches --- build/tasks/set-version-task.coffee | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/build/tasks/set-version-task.coffee b/build/tasks/set-version-task.coffee index fc2382476..c7a29b584 100644 --- a/build/tasks/set-version-task.coffee +++ b/build/tasks/set-version-task.coffee @@ -5,9 +5,7 @@ module.exports = (grunt) -> {spawn} = require('./task-helpers')(grunt) getVersion = (callback) -> - releasableBranches = ['stable', 'beta'] - channel = grunt.config.get('atom.channel') - shouldUseCommitHash = if channel in releasableBranches then false else true + shouldUseCommitHash = grunt.config.get('atom.channel') is 'dev' inRepository = fs.existsSync(path.resolve(__dirname, '..', '..', '.git')) {version} = require(path.join(grunt.config.get('atom.appDir'), 'package.json')) if shouldUseCommitHash and inRepository From d0ffbca845ca1f2f966b5f0b950c7b147c6e233a Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 25 Feb 2016 10:49:11 -0800 Subject: [PATCH 274/971] :lipstick: and :memo: for pending state --- src/pane.coffee | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 0dec5cbce..a01703ce5 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -8,6 +8,11 @@ TextEditor = require './text-editor' # Panes can contain multiple items, one of which is *active* at a given time. # The view corresponding to the active item is displayed in the interface. In # the default configuration, tabs are also displayed for each item. +# +# Each pane may also contain one *pending* item. When a pending item is added +# to a pane, it will replace the currently pending item, if any, instead of +# simply being added. In the default configuration, the text in the tab for +# pending items is shown in italics. module.exports = class Pane extends Model container: undefined @@ -380,19 +385,47 @@ class Pane extends Model @setActiveItem(item) unless @getActiveItem()? item + clearPendingItem: => + @setPendingItem(null) + setPendingItem: (item) => + # TODO: figure out events for changing/clearing pending item @pendingItem = item + @emitter.emit 'did-change-pending-item', @pendingItem @emitter.emit 'did-terminate-pending-state' if not item + # Public: Get the pending pane item in this pane, if any. + # + # Returns a pane item or `null`. getPendingItem: => - @pendingItem + @pendingItem or null isItemPending: (item) => @pendingItem is item + # Invoke the given callback when the value of {::getPendingItem} changes. + # + # * `callback` {Function} to be called with when the pending item changes. + # * `pendingItem` The current pending item, or `null`. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidChangePendingItem: (callback) => + @emitter.on 'did-change-pending-item', callback + + # Public: Invoke the given callback with the current and future values of + # {::getPendingItem}. + # + # * `callback` {Function} to be called with the current and future pending + # items. + # * `pendingItem` The current pending item. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observePendingItem: (callback) -> + callback(@getPendingItem()) + @onDidChangePendingItem(callback) + onDidTerminatePendingState: (callback) => - @emitter.on 'did-terminate-pending-state', -> - callback() + @emitter.on 'did-terminate-pending-state', callback # Public: Add the given items to the pane. # From 44dabf1e2fb6f058d7d2e024063f036bf75bc73b Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 10:22:10 -0500 Subject: [PATCH 275/971] Added .getElement to TextEditor. --- src/text-editor.coffee | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c0a6f2057..d1c092598 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -11,6 +11,7 @@ Selection = require './selection' TextMateScopeSelector = require('first-mate').ScopeSelector {Directory} = require "pathwatcher" GutterContainer = require './gutter-container' +TextEditorElement = require './text-editor-element' # Essential: This class represents all essential editing state for a single # {TextBuffer}, including cursor and selection positions, folds, and soft wraps. @@ -61,6 +62,10 @@ class TextEditor extends Model suppressSelectionMerging: false selectionFlashDuration: 500 gutterContainer: null + editorElement: null + + Object.defineProperty @prototype, "element", + get: -> @getElement() @deserialize: (state, atomEnvironment) -> try @@ -3142,6 +3147,10 @@ class TextEditor extends Model Section: TextEditor Rendering ### + # Get the Element for the editor. + getElement: -> + @editorElement ?= new TextEditorElement().initialize(this, atom) + # Essential: Retrieves the greyed out placeholder of a mini editor. # # Returns a {String}. From 10f0064a63c93b36511ce265453e6394f4e594e4 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 10:24:31 -0500 Subject: [PATCH 276/971] Call .getElement if the model has it. --- src/view-registry.coffee | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/view-registry.coffee b/src/view-registry.coffee index ef7151353..5fbfba729 100644 --- a/src/view-registry.coffee +++ b/src/view-registry.coffee @@ -171,6 +171,11 @@ class ViewRegistry if object instanceof HTMLElement return object + if typeof object?.getElement is 'function' + element = object.getElement() + if element instanceof HTMLElement + return element + if object?.element instanceof HTMLElement return object.element From 02c7bb3ddd8ef2980e5c7be69c2dfb0e78967370 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 10:24:38 -0500 Subject: [PATCH 277/971] Don't need this view provider anymore. --- src/atom-environment.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index c42bf05aa..dc1bef7d4 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -254,8 +254,6 @@ class AtomEnvironment extends Model new PaneAxisElement().initialize(model, env) @views.addViewProvider Pane, (model, env) -> new PaneElement().initialize(model, env) - @views.addViewProvider TextEditor, (model, env) -> - new TextEditorElement().initialize(model, env) @views.addViewProvider(Gutter, createGutterView) registerDefaultOpeners: -> From dfb1d1d62d091bfc6e87fb3e56bd2c9b81d36d89 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 10:24:51 -0500 Subject: [PATCH 278/971] Expose a bound buildTextEditor. --- src/workspace.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index 0bfff7e0f..e0a913f94 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -43,6 +43,12 @@ class Workspace extends Model @defaultDirectorySearcher = new DefaultDirectorySearcher() @consumeServices(@packageManager) + # One cannot simply .bind here since it could be used as a component with + # Etch, which means it'd be `new`d in which case `this` would the new + # object. + realThis = this + @buildTextEditor = (params) -> realThis.buildTextEditor_(params) + @panelContainers = top: new PanelContainer({location: 'top'}) left: new PanelContainer({location: 'left'}) @@ -550,7 +556,7 @@ class Workspace extends Model # Extended: Create a new text editor. # # Returns a {TextEditor}. - buildTextEditor: (params) -> + buildTextEditor_: (params) -> params = _.extend({ @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate From 768f5ee5ca1c9eb2a896e668661c54e6dbc1541f Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 14:00:49 -0500 Subject: [PATCH 279/971] Maybe a better comment? --- src/workspace.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index e0a913f94..15ffc6e6a 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -44,8 +44,8 @@ class Workspace extends Model @consumeServices(@packageManager) # One cannot simply .bind here since it could be used as a component with - # Etch, which means it'd be `new`d in which case `this` would the new - # object. + # Etch, in which case it'd be `new`d. And when it's `new`d, `this` is always + # the newly created object. realThis = this @buildTextEditor = (params) -> realThis.buildTextEditor_(params) From 55e1496b96b93d9cec2a48dd261081165b09632e Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 14:49:07 -0500 Subject: [PATCH 280/971] Call the prototype method directly. h/t @maxbrunsfeld --- src/workspace.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index 15ffc6e6a..31c381428 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -47,7 +47,7 @@ class Workspace extends Model # Etch, in which case it'd be `new`d. And when it's `new`d, `this` is always # the newly created object. realThis = this - @buildTextEditor = (params) -> realThis.buildTextEditor_(params) + @buildTextEditor = -> Workspace.prototype.buildTextEditor.apply(realThis, arguments) @panelContainers = top: new PanelContainer({location: 'top'}) @@ -556,7 +556,7 @@ class Workspace extends Model # Extended: Create a new text editor. # # Returns a {TextEditor}. - buildTextEditor_: (params) -> + buildTextEditor: (params) -> params = _.extend({ @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate From f3ce468a7068a09c26e2f76078ad5a820253cf9b Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 15:01:40 -0500 Subject: [PATCH 281/971] Support specifying whether to ignore invisibles and the grammar. --- src/text-editor.coffee | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index d1c092598..e1706d5f1 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -97,7 +97,7 @@ class TextEditor extends Model softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, @pending + @project, @assert, @applicationDelegate, @pending, grammarName, ignoreInvisibles } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -119,7 +119,7 @@ class TextEditor extends Model buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ - buffer, tabLength, softWrapped, ignoreInvisibles: @mini, largeFileMode, + buffer, tabLength, softWrapped, ignoreInvisibles: @mini || ignoreInvisibles, largeFileMode, @config, @assert, @grammarRegistry, @packageManager }) @buffer = @displayBuffer.buffer @@ -148,6 +148,9 @@ class TextEditor extends Model priority: 0 visible: lineNumberGutterVisible + if grammarName? + @setGrammar(@grammarRegistry.grammarForScopeName(grammarName)) + serialize: -> deserializer: 'TextEditor' id: @id From 822cd780555278b615e0da32020115b0d5fe7295 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 15:18:29 -0500 Subject: [PATCH 282/971] Use the computed style to find the height --- src/text-editor-component.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index bdd0befcd..d5103c1fb 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -731,8 +731,7 @@ class TextEditorComponent measureDimensions: -> return unless @mounted - {position} = getComputedStyle(@hostElement) - {height} = @hostElement.style + {position, height} = getComputedStyle(@hostElement) if position is 'absolute' or height @presenter.setAutoHeight(false) From 2af53231f16c602423f223fe99fb834a730d5f9f Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 15:21:56 -0500 Subject: [PATCH 283/971] Less lint. --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e1706d5f1..19b7e8c86 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -119,7 +119,7 @@ class TextEditor extends Model buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ - buffer, tabLength, softWrapped, ignoreInvisibles: @mini || ignoreInvisibles, largeFileMode, + buffer, tabLength, softWrapped, ignoreInvisibles: @mini or ignoreInvisibles, largeFileMode, @config, @assert, @grammarRegistry, @packageManager }) @buffer = @displayBuffer.buffer From 1aa0cec4115d2d3ced6f7059f23676093ac1c7bb Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 15:24:22 -0500 Subject: [PATCH 284/971] :arrow_up: nodegit@0.11.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a9eab6b3b..c73fdec8a 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "less-cache": "0.23", "line-top-index": "0.2.0", "marked": "^0.3.4", - "nodegit": "0.9.0", + "nodegit": "0.11.5", "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "^5", From dd780a7c5a415f513acae6d501df1f4f23c1cf93 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 15:41:55 -0500 Subject: [PATCH 285/971] Revert "Use the computed style to find the height" This reverts commit 822cd780555278b615e0da32020115b0d5fe7295. --- src/text-editor-component.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index d5103c1fb..bdd0befcd 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -731,7 +731,8 @@ class TextEditorComponent measureDimensions: -> return unless @mounted - {position, height} = getComputedStyle(@hostElement) + {position} = getComputedStyle(@hostElement) + {height} = @hostElement.style if position is 'absolute' or height @presenter.setAutoHeight(false) From dd83619c45a8fc0e4decf0127bfa38d0f46a128d Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 16:05:57 -0500 Subject: [PATCH 286/971] Add autoHeight setting. --- src/text-editor-element.coffee | 4 ++++ src/text-editor.coffee | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index 380417163..554f73cef 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -344,6 +344,10 @@ class TextEditorElement extends HTMLElement @style.height = height + "px" @component.measureDimensions() + disableAutoHeight: -> + @style.height = "100%" + @component.measureDimensions() + getHeight: -> @offsetHeight diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 19b7e8c86..06e58aa1e 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -97,7 +97,7 @@ class TextEditor extends Model softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, @pending, grammarName, ignoreInvisibles + @project, @assert, @applicationDelegate, @pending, grammarName, ignoreInvisibles, @autoHeight } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -116,6 +116,7 @@ class TextEditor extends Model @cursors = [] @cursorsByMarkerId = new Map @selections = [] + @autoHeight ?= true buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ @@ -3152,7 +3153,11 @@ class TextEditor extends Model # Get the Element for the editor. getElement: -> - @editorElement ?= new TextEditorElement().initialize(this, atom) + if not @editorElement? + @editorElement = new TextEditorElement().initialize(this, atom) + if not @autoHeight + @editorElement.disableAutoHeight() + @editorElement # Essential: Retrieves the greyed out placeholder of a mini editor. # From ff0b9e30a9bea281bfad8ab55e57bce4daf5e386 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 16:59:58 -0500 Subject: [PATCH 287/971] Add ignoreScrollPastEnd --- src/text-editor-component.coffee | 3 ++- src/text-editor-element.coffee | 4 +++- src/text-editor-presenter.coffee | 4 ++-- src/text-editor.coffee | 11 ++++++----- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index bdd0befcd..5a4097fc5 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -43,7 +43,7 @@ class TextEditorComponent @assert domNode?, "TextEditorComponent::domNode was set to null." @domNodeValue = domNode - constructor: ({@editor, @hostElement, @rootElement, @stylesElement, @useShadowDOM, tileSize, @views, @themes, @config, @workspace, @assert, @grammars}) -> + constructor: ({@editor, @hostElement, @rootElement, @stylesElement, @useShadowDOM, tileSize, @views, @themes, @config, @workspace, @assert, @grammars, ignoreScrollPastEnd}) -> @tileSize = tileSize if tileSize? @disposables = new CompositeDisposable @@ -61,6 +61,7 @@ class TextEditorComponent stoppedScrollingDelay: 200 config: @config lineTopIndex: lineTopIndex + ignoreScrollPastEnd: ignoreScrollPastEnd @presenter.onDidUpdateState(@requestUpdate) diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index 554f73cef..6fad33dc3 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -17,6 +17,7 @@ class TextEditorElement extends HTMLElement focusOnAttach: false hasTiledRendering: true logicalDisplayBuffer: true + ignoreScrollPastEnd: false createdCallback: -> # Use globals when the following instance variables aren't set. @@ -86,7 +87,7 @@ class TextEditorElement extends HTMLElement @subscriptions.add @component.onDidChangeScrollLeft => @emitter.emit("did-change-scroll-left", arguments...) - initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}) -> + initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}, @ignoreScrollPastEnd = false) -> throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @views? throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @config? throw new Error("Must pass a themes parameter when initializing TextEditorElements") unless @themes? @@ -143,6 +144,7 @@ class TextEditorElement extends HTMLElement workspace: @workspace assert: @assert grammars: @grammars + ignoreScrollPastEnd: @ignoreScrollPastEnd ) @rootElement.appendChild(@component.getDomNode()) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index a3504caa8..0db175c2b 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -13,7 +13,7 @@ class TextEditorPresenter minimumReflowInterval: 200 constructor: (params) -> - {@model, @config, @lineTopIndex} = params + {@model, @config, @lineTopIndex, @ignoreScrollPastEnd} = params {@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @tileSize} = params {@contentFrameWidth} = params @@ -661,7 +661,7 @@ class TextEditorPresenter return unless @contentHeight? and @clientHeight? contentHeight = @contentHeight - if @scrollPastEnd + if @scrollPastEnd and not @ignoreScrollPastEnd extraScrollHeight = @clientHeight - (@lineHeight * 3) contentHeight += extraScrollHeight if extraScrollHeight > 0 scrollHeight = Math.max(contentHeight, @height) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c1a4150e9..c22a1a73f 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -97,7 +97,7 @@ class TextEditor extends Model softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, @pending, grammarName, ignoreInvisibles, @autoHeight + @project, @assert, @applicationDelegate, @pending, grammarName, ignoreInvisibles, @autoHeight, @ignoreScrollPastEnd } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -117,6 +117,7 @@ class TextEditor extends Model @cursorsByMarkerId = new Map @selections = [] @autoHeight ?= true + @ignoreScrollPastEnd ?= false buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ @@ -3153,9 +3154,9 @@ class TextEditor extends Model # Get the Element for the editor. getElement: -> - if not @editorElement? - @editorElement = new TextEditorElement().initialize(this, atom) - if not @autoHeight + unless @editorElement? + @editorElement = new TextEditorElement().initialize(this, atom, @ignoreScrollPastEnd) + unless @autoHeight @editorElement.disableAutoHeight() @editorElement @@ -3233,7 +3234,7 @@ class TextEditor extends Model setFirstVisibleScreenRow: (screenRow, fromView) -> unless fromView maxScreenRow = @getScreenLineCount() - 1 - unless @config.get('editor.scrollPastEnd') + unless @config.get('editor.scrollPastEnd') and not @ignoreScrollPastEnd height = @displayBuffer.getHeight() lineHeightInPixels = @displayBuffer.getLineHeightInPixels() if height? and lineHeightInPixels? From dfd3e1b94840de8eecd89aa9793bc32f4260037c Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 17:11:04 -0500 Subject: [PATCH 288/971] Take autoHeight as an argument. --- src/text-editor-element.coffee | 9 ++++----- src/text-editor.coffee | 6 +----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index 6fad33dc3..02f688e2c 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -39,6 +39,9 @@ class TextEditorElement extends HTMLElement @setAttribute('tabindex', -1) initializeContent: (attributes) -> + unless @autoHeight + @style.height = "100%" + if @config.get('editor.useShadowDOM') @useShadowDOM = true @@ -87,7 +90,7 @@ class TextEditorElement extends HTMLElement @subscriptions.add @component.onDidChangeScrollLeft => @emitter.emit("did-change-scroll-left", arguments...) - initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}, @ignoreScrollPastEnd = false) -> + initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}, @autoHeight = true, @ignoreScrollPastEnd = false) -> throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @views? throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @config? throw new Error("Must pass a themes parameter when initializing TextEditorElements") unless @themes? @@ -346,10 +349,6 @@ class TextEditorElement extends HTMLElement @style.height = height + "px" @component.measureDimensions() - disableAutoHeight: -> - @style.height = "100%" - @component.measureDimensions() - getHeight: -> @offsetHeight diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c22a1a73f..2823b993e 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -3154,11 +3154,7 @@ class TextEditor extends Model # Get the Element for the editor. getElement: -> - unless @editorElement? - @editorElement = new TextEditorElement().initialize(this, atom, @ignoreScrollPastEnd) - unless @autoHeight - @editorElement.disableAutoHeight() - @editorElement + @editorElement ?= new TextEditorElement().initialize(this, atom, @autoHeight, @ignoreScrollPastEnd) # Essential: Retrieves the greyed out placeholder of a mini editor. # From b637366a58c97c0d3c98a55db8b91ed1c6482664 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 25 Feb 2016 16:09:40 -0800 Subject: [PATCH 289/971] Workspace#setItemNotPending :arrow_right: Item#onDidTerminatePendingState Signed-off-by: Michelle Tilley --- src/pane.coffee | 16 +++++++++++----- src/text-editor.coffee | 6 ++++-- src/workspace.coffee | 6 ------ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index a01703ce5..16c2f79b2 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -1,5 +1,5 @@ {find, compact, extend, last} = require 'underscore-plus' -{Emitter} = require 'event-kit' +{CompositeDisposable, Emitter} = require 'event-kit' Model = require './model' PaneAxis = require './pane-axis' TextEditor = require './text-editor' @@ -42,7 +42,7 @@ class Pane extends Model } = params @emitter = new Emitter - @itemSubscriptions = new WeakMap + @subscriptionsPerItem = new WeakMap @items = [] @addItems(compact(params?.items ? [])) @@ -265,8 +265,8 @@ class Pane extends Model getPanes: -> [this] unsubscribeFromItem: (item) -> - @itemSubscriptions.get(item)?.dispose() - @itemSubscriptions.delete(item) + @subscriptionsPerItem.get(item)?.dispose() + @subscriptionsPerItem.delete(item) ### Section: Items @@ -378,7 +378,13 @@ class Pane extends Model @setPendingItem(item) if pending if typeof item.onDidDestroy is 'function' - @itemSubscriptions.set item, item.onDidDestroy => @removeItem(item, false) + itemSubscriptions = new CompositeDisposable + itemSubscriptions.add item.onDidDestroy => @removeItem(item, false) + if typeof item.onDidTerminatePendingState is "function" + itemSubscriptions.add item.onDidTerminatePendingState => + @clearPendingItem() if @getPendingItem() is item + itemSubscriptions.add item.onDidDestroy => @removeItem(item, false) + @subscriptionsPerItem.set item, itemSubscriptions @items.splice(index, 0, item) @emitter.emit 'did-add-item', {item, index, moved} diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e8207ca1e..53889e4c9 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -165,11 +165,13 @@ class TextEditor extends Model @emitter.emit 'did-change-encoding', @getEncoding() @disposables.add @buffer.onDidDestroy => @destroy() @disposables.add @buffer.onDidChangeModified => - atom.workspace.setItemNotPending(this) if not @bufferHasChanged and @buffer.isModified() - @bufferHasChanged = true + @emitter.emit 'did-terminate-pending-state' @preserveCursorPositionOnBufferReload() + onDidTerminatePendingState: (callback) -> + @emitter.on 'did-terminate-pending-state', callback + subscribeToDisplayBuffer: -> @disposables.add @selectionsMarkerLayer.onDidCreateMarker @addSelection.bind(this) @disposables.add @displayBuffer.onDidChangeGrammar @handleGrammarChange.bind(this) diff --git a/src/workspace.coffee b/src/workspace.coffee index 1f6648271..39ec2c599 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -515,12 +515,6 @@ class Workspace extends Model @emitter.emit 'did-open', {uri, pane, item, index} item - setItemNotPending: (item) => - for pane in @getPanes() - if item is pane.getPendingItem() - pane.setPendingItem(null) - break - openTextFile: (uri, options) -> filePath = @project.resolvePath(uri) From 1c65d0e5e4acd3192e437012760145d00e650ee0 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 25 Feb 2016 16:48:16 -0800 Subject: [PATCH 290/971] Changed Pane and TextEditor specs to match new pending behavior --- spec/pane-spec.coffee | 81 ++++++++++++++++++++++++++++++------ spec/text-editor-spec.coffee | 60 -------------------------- src/text-editor.coffee | 9 ++-- 3 files changed, 75 insertions(+), 75 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 8c228e2a8..62971bfd1 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -18,8 +18,8 @@ describe "Pane", -> onDidDestroy: (fn) -> @emitter.on('did-destroy', fn) destroy: -> @destroyed = true; @emitter.emit('did-destroy') isDestroyed: -> @destroyed - isPending: -> @pending - pending: false + onDidTerminatePendingState: (callback) -> @emitter.on 'terminate-pending-state', callback + terminatePendingState: -> @emitter.emit 'terminate-pending-state' beforeEach -> confirm = spyOn(atom.applicationDelegate, 'confirm') @@ -136,10 +136,8 @@ describe "Pane", -> pane = new Pane(paneParams(items: [])) itemA = new Item("A") itemB = new Item("B") - itemA.pending = true - itemB.pending = true - pane.addItem(itemA) - pane.addItem(itemB) + pane.addItem(itemA, undefined, false, true) + pane.addItem(itemB, undefined, false, true) expect(itemA.isDestroyed()).toBe true describe "::activateItem(item)", -> @@ -172,19 +170,17 @@ describe "Pane", -> beforeEach -> itemC = new Item("C") itemD = new Item("D") - itemC.pending = true - itemD.pending = true it "replaces the active item if it is pending", -> - pane.activateItem(itemC) + pane.activateItem(itemC, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'C', 'B'] - pane.activateItem(itemD) + pane.activateItem(itemD, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'D', 'B'] it "adds the item after the active item if it is not pending", -> - pane.activateItem(itemC) + pane.activateItem(itemC, true) pane.activateItemAtIndex(2) - pane.activateItem(itemD) + pane.activateItem(itemD, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D'] describe "::activateNextItem() and ::activatePreviousItem()", -> @@ -806,6 +802,67 @@ describe "Pane", -> pane2.destroy() expect(container.root).toBe pane1 + describe "pending state", -> + editor1 = null + pane = null + eventCount = null + + beforeEach -> + waitsForPromise -> + atom.workspace.open('sample.txt', pending: true).then (o) -> + editor1 = o + pane = atom.workspace.getActivePane() + + runs -> + eventCount = 0 + editor1.onDidTerminatePendingState -> eventCount++ + + it "does not open file in pending state by default", -> + waitsForPromise -> + atom.workspace.open('sample.js').then (o) -> + editor1 = o + pane = atom.workspace.getActivePane() + + runs -> + expect(pane.getPendingItem()).toBeNull() + + it "opens file in pending state if 'pending' option is true", -> + expect(pane.getPendingItem()).toEqual editor1 + + it "terminates pending state if ::terminatePendingState is invoked", -> + editor1.terminatePendingState() + + expect(pane.getPendingItem()).toBeNull() + expect(eventCount).toBe 1 + + it "terminates pending state when buffer is changed", -> + editor1.insertText('I\'ll be back!') + advanceClock(editor1.getBuffer().stoppedChangingDelay) + + expect(pane.getPendingItem()).toBeNull() + expect(eventCount).toBe 1 + + it "only calls terminate handler once when text is modified twice", -> + editor1.insertText('Some text') + advanceClock(editor1.getBuffer().stoppedChangingDelay) + + editor1.save() + + editor1.insertText('More text') + advanceClock(editor1.getBuffer().stoppedChangingDelay) + + expect(pane.getPendingItem()).toBeNull() + expect(eventCount).toBe 1 + + it "only calls clearPendingItem if there is a pending item to clear", -> + spyOn(pane, "clearPendingItem").andCallThrough() + + editor1.terminatePendingState() + editor1.terminatePendingState() + + expect(pane.getPendingItem()).toBeNull() + expect(pane.clearPendingItem.callCount).toBe 1 + describe "serialization", -> pane = null diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 6959d4da5..6f16f0cf7 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -55,16 +55,6 @@ describe "TextEditor", -> expect(editor.tokenizedLineForScreenRow(0).invisibles.eol).toBe '?' - it "restores pending tabs in pending state", -> - expect(editor.isPending()).toBe false - editor2 = TextEditor.deserialize(editor.serialize(), atom) - expect(editor2.isPending()).toBe false - - pendingEditor = atom.workspace.buildTextEditor(pending: true) - expect(pendingEditor.isPending()).toBe true - editor3 = TextEditor.deserialize(pendingEditor.serialize(), atom) - expect(editor3.isPending()).toBe true - describe "when the editor is constructed with the largeFileMode option set to true", -> it "loads the editor but doesn't tokenize", -> editor = null @@ -5827,53 +5817,3 @@ describe "TextEditor", -> screenRange: marker1.getRange(), rangeIsReversed: false } - - describe "pending state", -> - editor1 = null - eventCount = null - - beforeEach -> - waitsForPromise -> - atom.workspace.open('sample.txt', pending: true).then (o) -> editor1 = o - - runs -> - eventCount = 0 - editor1.onDidTerminatePendingState -> eventCount++ - - it "does not open file in pending state by default", -> - expect(editor.isPending()).toBe false - - it "opens file in pending state if 'pending' option is true", -> - expect(editor1.isPending()).toBe true - - it "terminates pending state if ::terminatePendingState is invoked", -> - editor1.terminatePendingState() - - expect(editor1.isPending()).toBe false - expect(eventCount).toBe 1 - - it "terminates pending state when buffer is changed", -> - editor1.insertText('I\'ll be back!') - advanceClock(editor1.getBuffer().stoppedChangingDelay) - - expect(editor1.isPending()).toBe false - expect(eventCount).toBe 1 - - it "only calls terminate handler once when text is modified twice", -> - editor1.insertText('Some text') - advanceClock(editor1.getBuffer().stoppedChangingDelay) - - editor1.save() - - editor1.insertText('More text') - advanceClock(editor1.getBuffer().stoppedChangingDelay) - - expect(editor1.isPending()).toBe false - expect(eventCount).toBe 1 - - it "only calls terminate handler once when terminatePendingState is called twice", -> - editor1.terminatePendingState() - editor1.terminatePendingState() - - expect(editor1.isPending()).toBe false - expect(eventCount).toBe 1 diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 53889e4c9..c4d028856 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -111,7 +111,7 @@ class TextEditor extends Model @cursors = [] @cursorsByMarkerId = new Map @selections = [] - @bufferHasChanged = false + @hasTerminatedPendingState = false buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ @@ -152,7 +152,6 @@ class TextEditor extends Model firstVisibleScreenColumn: @getFirstVisibleScreenColumn() displayBuffer: @displayBuffer.serialize() selectionsMarkerLayerId: @selectionsMarkerLayer.id - bufferHasChanged: @bufferHasChanged subscribeToBuffer: -> @buffer.retain() @@ -165,10 +164,14 @@ class TextEditor extends Model @emitter.emit 'did-change-encoding', @getEncoding() @disposables.add @buffer.onDidDestroy => @destroy() @disposables.add @buffer.onDidChangeModified => - @emitter.emit 'did-terminate-pending-state' + @terminatePendingState() if @buffer.isModified() @preserveCursorPositionOnBufferReload() + terminatePendingState: -> + @emitter.emit 'did-terminate-pending-state' if not @hasTerminatedPendingState + @hasTerminatedPendingState = true + onDidTerminatePendingState: (callback) -> @emitter.on 'did-terminate-pending-state', callback From 3848da4488c5a5a00eb679fb49a3faa5bdcefeea Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 25 Feb 2016 17:21:01 -0800 Subject: [PATCH 291/971] :lipstick: and :memo: for pending API --- src/pane.coffee | 45 ++++++++++---------------------------------- src/workspace.coffee | 5 ++++- 2 files changed, 14 insertions(+), 36 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 16c2f79b2..5952352d1 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -58,7 +58,7 @@ class Pane extends Model items: compact(@items.map((item) -> item.serialize?())) activeItemURI: activeItemURI focused: @focused - flexScale: @flexScale # TODO: is it okay to not serialize pending state? does it need to be restored? + flexScale: @flexScale getParent: -> @parent @@ -348,7 +348,9 @@ class Pane extends Model # Public: Make the given item *active*, causing it to be displayed by # the pane's view. # - # * `pending` TODO + # * `pending` (optional) {Boolean} indicating that the item should be added + # in a pending state if it does not yet exist in the pane. Existing pending + # items in a pane are replaced with new pending items when they are opened. activateItem: (item, pending=false) -> if item? if @isItemPending(@activeItem) @@ -364,7 +366,9 @@ class Pane extends Model # view. # * `index` (optional) {Number} indicating the index at which to add the item. # If omitted, the item is added after the current active item. - # * `pending` TODO + # * `pending` (optional) {Boolean} indicating that the item should be + # added in a pending state. Existing pending items in a pane are replaced with + # new pending items when they are opened. # # Returns the added item. addItem: (item, index=@getActiveItemIndex() + 1, moved=false, pending=false) -> @@ -391,44 +395,15 @@ class Pane extends Model @setActiveItem(item) unless @getActiveItem()? item - clearPendingItem: => - @setPendingItem(null) - setPendingItem: (item) => - # TODO: figure out events for changing/clearing pending item - @pendingItem = item - @emitter.emit 'did-change-pending-item', @pendingItem + @pendingItem = item if @pendingItem isnt item @emitter.emit 'did-terminate-pending-state' if not item - # Public: Get the pending pane item in this pane, if any. - # - # Returns a pane item or `null`. getPendingItem: => @pendingItem or null - isItemPending: (item) => - @pendingItem is item - - # Invoke the given callback when the value of {::getPendingItem} changes. - # - # * `callback` {Function} to be called with when the pending item changes. - # * `pendingItem` The current pending item, or `null`. - # - # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. - onDidChangePendingItem: (callback) => - @emitter.on 'did-change-pending-item', callback - - # Public: Invoke the given callback with the current and future values of - # {::getPendingItem}. - # - # * `callback` {Function} to be called with the current and future pending - # items. - # * `pendingItem` The current pending item. - # - # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. - observePendingItem: (callback) -> - callback(@getPendingItem()) - @onDidChangePendingItem(callback) + clearPendingItem: => + @setPendingItem(null) onDidTerminatePendingState: (callback) => @emitter.on 'did-terminate-pending-state', callback diff --git a/src/workspace.coffee b/src/workspace.coffee index 39ec2c599..636ebfd69 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -403,6 +403,9 @@ class Workspace extends Model # containing pane. Defaults to `true`. # * `activateItem` A {Boolean} indicating whether to call {Pane::activateItem} # on containing pane. Defaults to `true`. + # * `pending` A {Boolean} indicating whether or not the item should be opened + # in a pending state. Existing pending items in a pane are replaced with + # new pending items when they are opened. # * `searchAllPanes` A {Boolean}. If `true`, the workspace will attempt to # activate an existing item for the given URI on any pane. # If `false`, only the active pane will be searched for @@ -477,7 +480,7 @@ class Workspace extends Model if uri? if item = pane.itemForURI(uri) - pane.setPendingItem(null) if not options.pending + pane.clearPendingItem() if not options.pending and pane.getPendingItem() is item item ?= opener(uri, options) for opener in @getOpeners() when not item try From 6add9ce9e48a278911a10a52e69b1b367faf878f Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 25 Feb 2016 17:25:39 -0800 Subject: [PATCH 292/971] isItemPending(item) :arrow_right: getPendingItem() --- src/pane.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane.coffee b/src/pane.coffee index 5952352d1..c37486d89 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -353,7 +353,7 @@ class Pane extends Model # items in a pane are replaced with new pending items when they are opened. activateItem: (item, pending=false) -> if item? - if @isItemPending(@activeItem) + if @getPendingItem() is @activeItem index = @getActiveItemIndex() else index = @getActiveItemIndex() + 1 From 8fff6b2dd0cc231c470f6dbf9349773298f2b546 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 25 Feb 2016 17:27:39 -0800 Subject: [PATCH 293/971] isItemPending(item) :arrow_right: getPendingItem() --- src/pane.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane.coffee b/src/pane.coffee index c37486d89..cbcb801c8 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -426,7 +426,7 @@ class Pane extends Model index = @items.indexOf(item) return if index is -1 - @pendingItem = null if @isItemPending(item) + @pendingItem = null if @getPendingItem() is item @emitter.emit 'will-remove-item', {item, index, destroyed: not moved, moved} @unsubscribeFromItem(item) From 7643fa04ed16eb0d68f001d8049317dab06b9478 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 25 Feb 2016 17:32:17 -0800 Subject: [PATCH 294/971] Small :racehorse: when editing a `TextEditor` that is no longer pending --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c4d028856..f5aa1f20b 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -164,7 +164,7 @@ class TextEditor extends Model @emitter.emit 'did-change-encoding', @getEncoding() @disposables.add @buffer.onDidDestroy => @destroy() @disposables.add @buffer.onDidChangeModified => - @terminatePendingState() if @buffer.isModified() + @terminatePendingState() if not @hasTerminatedPendingState and @buffer.isModified() @preserveCursorPositionOnBufferReload() From 84a2ef69af1f66ac9f05e718c56ddec7e2ea9db5 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Fri, 26 Feb 2016 07:21:18 -0800 Subject: [PATCH 295/971] Fix Workspace#open pending specs --- spec/workspace-spec.coffee | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index ef89636a8..78bbf2fdb 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -588,19 +588,22 @@ describe "Workspace", -> describe "when the file is already open in pending state", -> it "should terminate the pending state", -> editor = null + pane = null waitsForPromise -> - atom.workspace.open('sample.js', pending: true).then (o) -> editor = o - + atom.workspace.open('sample.js', pending: true).then (o) -> + editor = o + pane = atom.workspace.getActivePane() + runs -> - expect(editor.isPending()).toBe true - + expect(pane.getPendingItem()).toEqual editor + waitsForPromise -> - atom.workspace.open('sample.js').then (o) -> editor = o - + atom.workspace.open('sample.js') + runs -> - expect(editor.isPending()).toBe false - + expect(pane.getPendingItem()).toBeNull() + describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> pane = workspace.getActivePane() @@ -1551,11 +1554,12 @@ describe "Workspace", -> describe "when the core.allowPendingPaneItems option is falsey", -> it "does not open item with `pending: true` option as pending", -> - editor = null + pane = null atom.config.set('core.allowPendingPaneItems', false) waitsForPromise -> - atom.workspace.open('sample.js', pending: true).then (o) -> editor = o + atom.workspace.open('sample.js', pending: true).then -> + pane = atom.workspace.getActivePane() runs -> - expect(editor.isPending()).toBeFalsy() + expect(pane.getPendingItem()).toBeFalsy() From ff0942dc1f7092dfded874527069fbf22debeb9e Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Fri, 26 Feb 2016 07:40:39 -0800 Subject: [PATCH 296/971] :arrow_up: bracket-matcher --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c73fdec8a..5dc94cda9 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "autosave": "0.23.1", "background-tips": "0.26.0", "bookmarks": "0.38.2", - "bracket-matcher": "0.80.0", + "bracket-matcher": "0.80.1", "command-palette": "0.38.0", "deprecation-cop": "0.54.1", "dev-live-reload": "0.47.0", From 715686fd4e5ff41ee37f2731b820d5dd009c0af9 Mon Sep 17 00:00:00 2001 From: Ian Olsen Date: Fri, 26 Feb 2016 14:11:45 -0800 Subject: [PATCH 297/971] build only my experimental branch on circle --- circle.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 circle.yml diff --git a/circle.yml b/circle.yml new file mode 100644 index 000000000..a55900cca --- /dev/null +++ b/circle.yml @@ -0,0 +1,4 @@ +general: + branches: + only: + - io-circle-ci From 7a6c8f53a444147a02acd143731f1e9c953aaf9c Mon Sep 17 00:00:00 2001 From: natalieogle Date: Mon, 25 Jan 2016 20:18:23 -0800 Subject: [PATCH 298/971] Add activateMostRecentlyUsedItem to pane model. --- keymaps/darwin.cson | 2 +- keymaps/linux.cson | 2 +- keymaps/win32.cson | 2 +- spec/pane-spec.coffee | 21 +++++++++++++++++++++ src/pane.coffee | 28 ++++++++++++++++++++++++++-- src/register-default-commands.coffee | 1 + 6 files changed, 51 insertions(+), 5 deletions(-) diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index 4859f9b67..3fc2aecf1 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -73,7 +73,7 @@ 'cmd-alt-right': 'pane:show-next-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' - 'ctrl-tab': 'pane:show-next-item' + 'ctrl-tab': 'pane:show-most-recently-used-item' 'ctrl-shift-tab': 'pane:show-previous-item' 'cmd-=': 'window:increase-font-size' 'cmd-+': 'window:increase-font-size' diff --git a/keymaps/linux.cson b/keymaps/linux.cson index 9ddb760e2..25bfe3add 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -46,7 +46,7 @@ 'pagedown': 'core:page-down' 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' - 'ctrl-tab': 'pane:show-next-item' + 'ctrl-tab': 'pane:show-most-recently-used-item' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index e4703bac8..bde54c29f 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -52,7 +52,7 @@ 'pagedown': 'core:page-down' 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' - 'ctrl-tab': 'pane:show-next-item' + 'ctrl-tab': 'pane:show-most-recently-used-item' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 62971bfd1..9bf2306a9 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -183,6 +183,27 @@ describe "Pane", -> pane.activateItem(itemD, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D'] + fdescribe "::activateMostRecentlyUsedItem()", -> + it "sets the active item to the most recently used item", -> + pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")])) + [item1, item2, item3] = pane.getItems() + pane.itemStack = [] + + pane.activateItem(item3) + expect(pane.getActiveItem()).toBe item3 + pane.activateItem(item1) + expect(pane.getActiveItem()).toBe item1 + pane.activateMostRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item3 + pane.activateItem(item2) + expect(pane.getActiveItem()).toBe item2 + pane.activateMostRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item3 + expect(pane.itemStack[0]).toBe item1 + pane.destroyItem(item3) + pane.activateMostRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item1 + describe "::activateNextItem() and ::activatePreviousItem()", -> it "sets the active item to the next/previous item, looping around at either end", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")])) diff --git a/src/pane.coffee b/src/pane.coffee index cbcb801c8..a6a47be54 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -44,6 +44,7 @@ class Pane extends Model @emitter = new Emitter @subscriptionsPerItem = new WeakMap @items = [] + @itemStack = [] @addItems(compact(params?.items ? [])) @setActiveItem(@items[0]) unless @getActiveItem()? @@ -285,10 +286,17 @@ class Pane extends Model setActiveItem: (activeItem) -> unless activeItem is @activeItem + @addItemToStack(activeItem) @activeItem = activeItem @emitter.emit 'did-change-active-item', @activeItem @activeItem + # Add item (or move item) to the end of the itemStack + addItemToStack: (newItem) -> + index = @itemStack.indexOf(newItem) + @itemStack.splice(index, 1) unless index is -1 + @itemStack.push(newItem) + # Return an {TextEditor} if the pane item is an {TextEditor}, or null otherwise. getActiveEditor: -> @activeItem if @activeItem instanceof TextEditor @@ -301,6 +309,15 @@ class Pane extends Model itemAtIndex: (index) -> @items[index] + # Makes the most recently used item active. + activateMostRecentlyUsedItem: -> + console.log(@items[0].serialize) + if @items.length > 1 + index = @itemStack.length - 2 + mostRecentlyUsedItem = @itemStack[index] + @itemStack.splice(index, 1) + @setActiveItem(mostRecentlyUsedItem) + # Public: Makes the next item active. activateNextItem: -> index = @getActiveItemIndex() @@ -425,9 +442,8 @@ class Pane extends Model removeItem: (item, moved) -> index = @items.indexOf(item) return if index is -1 - @pendingItem = null if @getPendingItem() is item - + @removeItemFromStack(item) @emitter.emit 'will-remove-item', {item, index, destroyed: not moved, moved} @unsubscribeFromItem(item) @@ -443,6 +459,14 @@ class Pane extends Model @container?.didDestroyPaneItem({item, index, pane: this}) unless moved @destroy() if @items.length is 0 and @config.get('core.destroyEmptyPanes') + # Remove the given item from the itemStack. + # + # * `item` The item to remove. + # * `index` {Number} indicating the index to which to remove the item from the itemStack. + removeItemFromStack: (item) -> + index = @itemStack.indexOf(item) + @itemStack.splice(index, 1) unless index is -1 + # Public: Move the given item to the given index. # # * `item` The item to move. diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index 1b1aad2cf..d65f8aced 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -2,6 +2,7 @@ module.exports = ({commandRegistry, commandInstaller, config}) -> commandRegistry.add 'atom-workspace', + 'pane:show-most-recently-used-item': -> @getModel().getActivePane().activateMostRecentlyUsedItem() 'pane:show-next-item': -> @getModel().getActivePane().activateNextItem() 'pane:show-previous-item': -> @getModel().getActivePane().activatePreviousItem() 'pane:show-item-1': -> @getModel().getActivePane().activateItemAtIndex(0) From 6466cb489e349422c0845dbac5b20edf8ef5b14c Mon Sep 17 00:00:00 2001 From: natalieogle Date: Wed, 3 Feb 2016 22:21:46 -0800 Subject: [PATCH 299/971] Add serialize and deserialize functionality to the itemStack. --- spec/pane-spec.coffee | 30 +++++++++++++++++++++++++++++- src/pane.coffee | 16 ++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 9bf2306a9..b4057fb4c 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -183,7 +183,8 @@ describe "Pane", -> pane.activateItem(itemD, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D'] - fdescribe "::activateMostRecentlyUsedItem()", -> + + describe "::activateMostRecentlyUsedItem()", -> it "sets the active item to the most recently used item", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")])) [item1, item2, item3] = pane.getItems() @@ -915,3 +916,30 @@ describe "Pane", -> pane.focus() newPane = Pane.deserialize(pane.serialize(), atom) expect(newPane.focused).toBe true + + it "can serialize and deserialize the order of the items in the itemStack", -> + [item1, item2, item3] = pane.getItems() + pane.itemStack = [item3, item1, item2] + newPane = Pane.deserialize(pane.serialize(), atom) + expect(newPane.itemStack).toEqual pane.itemStack + expect(newPane.itemStack[2]).toEqual item2 + + it "builds the itemStack if the itemStack is not serialized", -> + [item1, item2, item3] = pane.getItems() + newPane = Pane.deserialize(pane.serialize(), atom) + expect(newPane.getItems()).toEqual newPane.itemStack + + it "rebuilds the itemStack if items.length does not match itemStack.length", -> + [item1, item2, item3] = pane.getItems() + pane.itemStack = [item2, item3] + newPane = Pane.deserialize(pane.serialize(), atom) + expect(newPane.getItems()).toEqual newPane.itemStack + + it "does not serialize items in the itemStack if they will not be serialized", -> + [item1, item2, item3] = pane.getItems() + pane.itemStack = [item2, item1, item3] + unserializable = {} + pane.activateItem(unserializable) + + newPane = Pane.deserialize(pane.serialize(), atom) + expect(newPane.itemStack).toEqual [item2, item1, item3] diff --git a/src/pane.coffee b/src/pane.coffee index a6a47be54..0aa1ed8fd 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -20,7 +20,7 @@ class Pane extends Model focused: false @deserialize: (state, {deserializers, applicationDelegate, config, notifications}) -> - {items, activeItemURI, activeItemUri} = state + {items, itemStack, activeItemURI, activeItemUri} = state activeItemURI ?= activeItemUri state.items = compact(items.map (itemState) -> deserializers.deserialize(itemState)) state.activeItem = find state.items, (item) -> @@ -48,15 +48,20 @@ class Pane extends Model @addItems(compact(params?.items ? [])) @setActiveItem(@items[0]) unless @getActiveItem()? + @addItemsToStack(params?.itemStack ? []) @setFlexScale(params?.flexScale ? 1) serialize: -> if typeof @activeItem?.getURI is 'function' activeItemURI = @activeItem.getURI() + itemStack = [] + for item in @items + itemStack.push(@itemStack.indexOf(item)) if typeof item.serialize is 'function' deserializer: 'Pane' id: @id items: compact(@items.map((item) -> item.serialize?())) + itemStack: itemStack activeItemURI: activeItemURI focused: @focused flexScale: @flexScale @@ -291,6 +296,14 @@ class Pane extends Model @emitter.emit 'did-change-active-item', @activeItem @activeItem + # Build the itemStack after deserializing + addItemsToStack: (itemStack) -> + if itemStack.length is 0 or itemStack.length isnt @items.length or itemStack.indexOf(-1) >= 0 + itemStack = @items.map((item) => @items.indexOf(item)) + for item, i in itemStack + index = itemStack.indexOf(i) + @addItemToStack(@items[index]) unless index is -1 + # Add item (or move item) to the end of the itemStack addItemToStack: (newItem) -> index = @itemStack.indexOf(newItem) @@ -311,7 +324,6 @@ class Pane extends Model # Makes the most recently used item active. activateMostRecentlyUsedItem: -> - console.log(@items[0].serialize) if @items.length > 1 index = @itemStack.length - 2 mostRecentlyUsedItem = @itemStack[index] From fe52ce6011ae780ec362c296630fc3a31bb57ac4 Mon Sep 17 00:00:00 2001 From: natalieogle Date: Mon, 8 Feb 2016 18:58:05 -0800 Subject: [PATCH 300/971] Modify serialize functions and add function to move through the item stack in order of most recently used. --- spec/pane-spec.coffee | 26 +++++++++++++++++++++++ src/pane.coffee | 48 ++++++++++++++++++++++++++----------------- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index b4057fb4c..d3e8dc535 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -195,16 +195,42 @@ describe "Pane", -> pane.activateItem(item1) expect(pane.getActiveItem()).toBe item1 pane.activateMostRecentlyUsedItem() + pane.stopMovingThroughStackAndMoveItemToEndOfStack() expect(pane.getActiveItem()).toBe item3 pane.activateItem(item2) expect(pane.getActiveItem()).toBe item2 pane.activateMostRecentlyUsedItem() + pane.stopMovingThroughStackAndMoveItemToEndOfStack() expect(pane.getActiveItem()).toBe item3 expect(pane.itemStack[0]).toBe item1 pane.destroyItem(item3) pane.activateMostRecentlyUsedItem() + pane.stopMovingThroughStackAndMoveItemToEndOfStack() expect(pane.getActiveItem()).toBe item1 + describe "::activateNextRecentlyUsedItem()", -> + it "sets the active item to the next item in the itemStack", -> + pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D"), new Item("E")])) + [item1, item2, item3, item4, item5] = pane.getItems() + pane.itemStack = [item3, item1, item2, item5, item4] + + pane.activateItem(item4) + expect(pane.getActiveItem()).toBe item4 + pane.activateMostRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item5 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item2 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item1 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item3 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item4 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item5 + pane.stopMovingThroughStackAndMoveItemToEndOfStack() + expect(pane.itemStack[4]).toBe item5 + describe "::activateNextItem() and ::activatePreviousItem()", -> it "sets the active item to the next/previous item, looping around at either end", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")])) diff --git a/src/pane.coffee b/src/pane.coffee index 0aa1ed8fd..93f399cd0 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -20,7 +20,7 @@ class Pane extends Model focused: false @deserialize: (state, {deserializers, applicationDelegate, config, notifications}) -> - {items, itemStack, activeItemURI, activeItemUri} = state + {items, itemStackIndices, activeItemURI, activeItemUri} = state activeItemURI ?= activeItemUri state.items = compact(items.map (itemState) -> deserializers.deserialize(itemState)) state.activeItem = find state.items, (item) -> @@ -48,20 +48,21 @@ class Pane extends Model @addItems(compact(params?.items ? [])) @setActiveItem(@items[0]) unless @getActiveItem()? - @addItemsToStack(params?.itemStack ? []) + @addItemsToStack(params?.itemStackIndices ? []) @setFlexScale(params?.flexScale ? 1) serialize: -> if typeof @activeItem?.getURI is 'function' activeItemURI = @activeItem.getURI() - itemStack = [] - for item in @items - itemStack.push(@itemStack.indexOf(item)) if typeof item.serialize is 'function' + itemsToBeSerialized = compact(@items.map((item) -> item if typeof item.serialize is 'function')) + itemStackIndices = [] + for item in @itemStack + itemStackIndices.push(itemsToBeSerialized.indexOf(item)) if typeof item.serialize is 'function' deserializer: 'Pane' id: @id - items: compact(@items.map((item) -> item.serialize?())) - itemStack: itemStack + items: itemsToBeSerialized.map((item) -> item.serialize()) + itemStackIndices: itemStackIndices activeItemURI: activeItemURI focused: @focused flexScale: @flexScale @@ -289,20 +290,20 @@ class Pane extends Model # Returns a pane item. getActiveItem: -> @activeItem - setActiveItem: (activeItem) -> + setActiveItem: (activeItem, options) -> + {modifyStack} = options if options? unless activeItem is @activeItem - @addItemToStack(activeItem) + @addItemToStack(activeItem) unless modifyStack is false @activeItem = activeItem @emitter.emit 'did-change-active-item', @activeItem @activeItem # Build the itemStack after deserializing - addItemsToStack: (itemStack) -> - if itemStack.length is 0 or itemStack.length isnt @items.length or itemStack.indexOf(-1) >= 0 - itemStack = @items.map((item) => @items.indexOf(item)) - for item, i in itemStack - index = itemStack.indexOf(i) - @addItemToStack(@items[index]) unless index is -1 + addItemsToStack: (itemStackIndices) -> + if itemStackIndices.length is 0 or itemStackIndices.length isnt @items.length or itemStackIndices.indexOf(-1) >= 0 + itemStackIndices = (i for i in [0..@items.length-1]) + for itemIndex in itemStackIndices + @addItemToStack(@items[itemIndex]) # Add item (or move item) to the end of the itemStack addItemToStack: (newItem) -> @@ -325,10 +326,19 @@ class Pane extends Model # Makes the most recently used item active. activateMostRecentlyUsedItem: -> if @items.length > 1 - index = @itemStack.length - 2 - mostRecentlyUsedItem = @itemStack[index] - @itemStack.splice(index, 1) - @setActiveItem(mostRecentlyUsedItem) + @itemStackIndex = @itemStack.length - 1 + @activateNextRecentlyUsedItem() + + # Makes the next item in the itemStack active. + activateNextRecentlyUsedItem: -> + @itemStackIndex = @itemStackIndex - 1 + nextRecentlyUsedItem = @itemStack[@itemStackIndex] + @setActiveItem(nextRecentlyUsedItem, modifyStack: false) + @itemStackIndex = @itemStack.length if @itemStackIndex is 0 + + # Moves the active item to the end of the itemStack once the ctrl key is lifted + stopMovingThroughStackAndMoveItemToEndOfStack: -> + @addItemToStack(@activeItem) # Public: Makes the next item active. activateNextItem: -> From 3641cc0296ab4720b57e3538ed6e2ccea702eeec Mon Sep 17 00:00:00 2001 From: natalieogle Date: Tue, 9 Feb 2016 20:43:02 -0800 Subject: [PATCH 301/971] Remove redundant MRU function. --- keymaps/darwin.cson | 2 +- keymaps/linux.cson | 2 +- keymaps/win32.cson | 2 +- spec/pane-spec.coffee | 41 ++++++++-------------------- src/pane.coffee | 17 +++++------- src/register-default-commands.coffee | 2 +- 6 files changed, 23 insertions(+), 43 deletions(-) diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index 3fc2aecf1..f854aaeaf 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -73,7 +73,7 @@ 'cmd-alt-right': 'pane:show-next-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' - 'ctrl-tab': 'pane:show-most-recently-used-item' + 'ctrl-tab': 'pane:show-next-recently-used-item' 'ctrl-shift-tab': 'pane:show-previous-item' 'cmd-=': 'window:increase-font-size' 'cmd-+': 'window:increase-font-size' diff --git a/keymaps/linux.cson b/keymaps/linux.cson index 25bfe3add..433975e89 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -46,7 +46,7 @@ 'pagedown': 'core:page-down' 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' - 'ctrl-tab': 'pane:show-most-recently-used-item' + 'ctrl-tab': 'pane:show-next-recently-used-item' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index bde54c29f..fb33ce09d 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -52,7 +52,7 @@ 'pagedown': 'core:page-down' 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' - 'ctrl-tab': 'pane:show-most-recently-used-item' + 'ctrl-tab': 'pane:show-next-recently-used-item' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index d3e8dc535..f17fee5b1 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -183,31 +183,6 @@ describe "Pane", -> pane.activateItem(itemD, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D'] - - describe "::activateMostRecentlyUsedItem()", -> - it "sets the active item to the most recently used item", -> - pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")])) - [item1, item2, item3] = pane.getItems() - pane.itemStack = [] - - pane.activateItem(item3) - expect(pane.getActiveItem()).toBe item3 - pane.activateItem(item1) - expect(pane.getActiveItem()).toBe item1 - pane.activateMostRecentlyUsedItem() - pane.stopMovingThroughStackAndMoveItemToEndOfStack() - expect(pane.getActiveItem()).toBe item3 - pane.activateItem(item2) - expect(pane.getActiveItem()).toBe item2 - pane.activateMostRecentlyUsedItem() - pane.stopMovingThroughStackAndMoveItemToEndOfStack() - expect(pane.getActiveItem()).toBe item3 - expect(pane.itemStack[0]).toBe item1 - pane.destroyItem(item3) - pane.activateMostRecentlyUsedItem() - pane.stopMovingThroughStackAndMoveItemToEndOfStack() - expect(pane.getActiveItem()).toBe item1 - describe "::activateNextRecentlyUsedItem()", -> it "sets the active item to the next item in the itemStack", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D"), new Item("E")])) @@ -216,20 +191,28 @@ describe "Pane", -> pane.activateItem(item4) expect(pane.getActiveItem()).toBe item4 - pane.activateMostRecentlyUsedItem() + pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item5 pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item2 pane.activateNextRecentlyUsedItem() + pane.stopMovingThroughStackAndMoveItemToEndOfStack() expect(pane.getActiveItem()).toBe item1 - pane.activateNextRecentlyUsedItem() - expect(pane.getActiveItem()).toBe item3 + expect(pane.itemStack[4]).toBe item1 pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item4 pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item5 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item2 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item3 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item1 + pane.activateNextRecentlyUsedItem() pane.stopMovingThroughStackAndMoveItemToEndOfStack() - expect(pane.itemStack[4]).toBe item5 + expect(pane.getActiveItem()).toBe item4 + expect(pane.itemStack[4]).toBe item4 describe "::activateNextItem() and ::activatePreviousItem()", -> it "sets the active item to the next/previous item, looping around at either end", -> diff --git a/src/pane.coffee b/src/pane.coffee index 93f399cd0..82b7ec4f6 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -323,21 +323,18 @@ class Pane extends Model itemAtIndex: (index) -> @items[index] - # Makes the most recently used item active. - activateMostRecentlyUsedItem: -> - if @items.length > 1 - @itemStackIndex = @itemStack.length - 1 - @activateNextRecentlyUsedItem() - # Makes the next item in the itemStack active. activateNextRecentlyUsedItem: -> - @itemStackIndex = @itemStackIndex - 1 - nextRecentlyUsedItem = @itemStack[@itemStackIndex] - @setActiveItem(nextRecentlyUsedItem, modifyStack: false) - @itemStackIndex = @itemStack.length if @itemStackIndex is 0 + if @items.length > 1 + @itemStackIndex = @itemStack.length - 1 unless @itemStackIndex? + @itemStackIndex = @itemStackIndex - 1 + nextRecentlyUsedItem = @itemStack[@itemStackIndex] + @setActiveItem(nextRecentlyUsedItem, modifyStack: false) + @itemStackIndex = @itemStack.length if @itemStackIndex is 0 # Moves the active item to the end of the itemStack once the ctrl key is lifted stopMovingThroughStackAndMoveItemToEndOfStack: -> + delete @itemStackIndex @addItemToStack(@activeItem) # Public: Makes the next item active. diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index d65f8aced..ae5cdc344 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -2,7 +2,7 @@ module.exports = ({commandRegistry, commandInstaller, config}) -> commandRegistry.add 'atom-workspace', - 'pane:show-most-recently-used-item': -> @getModel().getActivePane().activateMostRecentlyUsedItem() + 'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem() 'pane:show-next-item': -> @getModel().getActivePane().activateNextItem() 'pane:show-previous-item': -> @getModel().getActivePane().activatePreviousItem() 'pane:show-item-1': -> @getModel().getActivePane().activateItemAtIndex(0) From 69a6b9e5c5c004f0df085ec8a161107b08577218 Mon Sep 17 00:00:00 2001 From: natalieogle Date: Tue, 16 Feb 2016 20:28:58 -0800 Subject: [PATCH 302/971] Add keymap for 'ctrl-tab ^ctrl' in order to move item to top of stack when lifting ctrl. --- keymaps/darwin.cson | 1 + keymaps/linux.cson | 1 + keymaps/win32.cson | 1 + src/pane.coffee | 2 +- src/register-default-commands.coffee | 1 + 5 files changed, 5 insertions(+), 1 deletion(-) diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index f854aaeaf..c39ee6d99 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -74,6 +74,7 @@ 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' 'ctrl-tab': 'pane:show-next-recently-used-item' + 'ctrl-tab ^ctrl': 'pane:move-item-to-top-of-stack' 'ctrl-shift-tab': 'pane:show-previous-item' 'cmd-=': 'window:increase-font-size' 'cmd-+': 'window:increase-font-size' diff --git a/keymaps/linux.cson b/keymaps/linux.cson index 433975e89..1192fe052 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -47,6 +47,7 @@ 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' 'ctrl-tab': 'pane:show-next-recently-used-item' + 'ctrl-tab ^ctrl': 'pane:move-item-to-top-of-stack' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index fb33ce09d..c50a6e75a 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -53,6 +53,7 @@ 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' 'ctrl-tab': 'pane:show-next-recently-used-item' + 'ctrl-tab ^ctrl': 'pane:move-item-to-top-of-stack' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/src/pane.coffee b/src/pane.coffee index 82b7ec4f6..277835d3c 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -333,7 +333,7 @@ class Pane extends Model @itemStackIndex = @itemStack.length if @itemStackIndex is 0 # Moves the active item to the end of the itemStack once the ctrl key is lifted - stopMovingThroughStackAndMoveItemToEndOfStack: -> + moveItemToTopOfStack: -> delete @itemStackIndex @addItemToStack(@activeItem) diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index ae5cdc344..09574cedb 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -3,6 +3,7 @@ module.exports = ({commandRegistry, commandInstaller, config}) -> commandRegistry.add 'atom-workspace', 'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem() + 'pane:move-item-to-top-of-stack': -> @getModel().getActivePane().moveItemToTopOfStack() 'pane:show-next-item': -> @getModel().getActivePane().activateNextItem() 'pane:show-previous-item': -> @getModel().getActivePane().activatePreviousItem() 'pane:show-item-1': -> @getModel().getActivePane().activateItemAtIndex(0) From 463dc6955a638c943c5c67124bcdabef402df777 Mon Sep 17 00:00:00 2001 From: natalieogle Date: Tue, 16 Feb 2016 21:12:05 -0800 Subject: [PATCH 303/971] Update spec for MRU tab functionality with correct function name. --- spec/pane-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index f17fee5b1..c9d467af3 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -196,7 +196,7 @@ describe "Pane", -> pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item2 pane.activateNextRecentlyUsedItem() - pane.stopMovingThroughStackAndMoveItemToEndOfStack() + pane.moveItemToTopOfStack() expect(pane.getActiveItem()).toBe item1 expect(pane.itemStack[4]).toBe item1 pane.activateNextRecentlyUsedItem() @@ -210,7 +210,7 @@ describe "Pane", -> pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item1 pane.activateNextRecentlyUsedItem() - pane.stopMovingThroughStackAndMoveItemToEndOfStack() + pane.moveItemToTopOfStack() expect(pane.getActiveItem()).toBe item4 expect(pane.itemStack[4]).toBe item4 From 96107038746354f77494bce1b3317ea8936eb436 Mon Sep 17 00:00:00 2001 From: natalieogle Date: Sun, 21 Feb 2016 18:50:54 -0800 Subject: [PATCH 304/971] Add check to only build itemStack if there are items. --- src/pane.coffee | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 277835d3c..1504553af 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -300,10 +300,11 @@ class Pane extends Model # Build the itemStack after deserializing addItemsToStack: (itemStackIndices) -> - if itemStackIndices.length is 0 or itemStackIndices.length isnt @items.length or itemStackIndices.indexOf(-1) >= 0 - itemStackIndices = (i for i in [0..@items.length-1]) - for itemIndex in itemStackIndices - @addItemToStack(@items[itemIndex]) + if @items.length > 0 + if itemStackIndices.length is 0 or itemStackIndices.length isnt @items.length or itemStackIndices.indexOf(-1) >= 0 + itemStackIndices = (i for i in [0..@items.length-1]) unless @items.length is 0 + for itemIndex in itemStackIndices + @addItemToStack(@items[itemIndex]) # Add item (or move item) to the end of the itemStack addItemToStack: (newItem) -> From 48ef6725241ca69d40f0f439f3ed7d4b0e1230b5 Mon Sep 17 00:00:00 2001 From: natalieogle Date: Sun, 21 Feb 2016 19:11:32 -0800 Subject: [PATCH 305/971] Remove redundant items.length check. --- src/pane.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane.coffee b/src/pane.coffee index 1504553af..7b3ec515b 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -302,7 +302,7 @@ class Pane extends Model addItemsToStack: (itemStackIndices) -> if @items.length > 0 if itemStackIndices.length is 0 or itemStackIndices.length isnt @items.length or itemStackIndices.indexOf(-1) >= 0 - itemStackIndices = (i for i in [0..@items.length-1]) unless @items.length is 0 + itemStackIndices = (i for i in [0..@items.length-1]) for itemIndex in itemStackIndices @addItemToStack(@items[itemIndex]) From 553b3f33009850b3e413f57aec26b4657406e61d Mon Sep 17 00:00:00 2001 From: natalieogle Date: Tue, 23 Feb 2016 19:02:45 -0800 Subject: [PATCH 306/971] Change name of function that moves the active item to the top of the item stack. --- keymaps/darwin.cson | 2 +- keymaps/linux.cson | 2 +- keymaps/win32.cson | 2 +- src/pane.coffee | 2 +- src/register-default-commands.coffee | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index c39ee6d99..baa664da8 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -74,7 +74,7 @@ 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' 'ctrl-tab': 'pane:show-next-recently-used-item' - 'ctrl-tab ^ctrl': 'pane:move-item-to-top-of-stack' + 'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' 'ctrl-shift-tab': 'pane:show-previous-item' 'cmd-=': 'window:increase-font-size' 'cmd-+': 'window:increase-font-size' diff --git a/keymaps/linux.cson b/keymaps/linux.cson index 1192fe052..d9758e5ea 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -47,7 +47,7 @@ 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' 'ctrl-tab': 'pane:show-next-recently-used-item' - 'ctrl-tab ^ctrl': 'pane:move-item-to-top-of-stack' + 'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index c50a6e75a..3befc6b0c 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -53,7 +53,7 @@ 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' 'ctrl-tab': 'pane:show-next-recently-used-item' - 'ctrl-tab ^ctrl': 'pane:move-item-to-top-of-stack' + 'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/src/pane.coffee b/src/pane.coffee index 7b3ec515b..d405e77a7 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -334,7 +334,7 @@ class Pane extends Model @itemStackIndex = @itemStack.length if @itemStackIndex is 0 # Moves the active item to the end of the itemStack once the ctrl key is lifted - moveItemToTopOfStack: -> + moveActiveItemToTopOfStack: -> delete @itemStackIndex @addItemToStack(@activeItem) diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index 09574cedb..3d95f1cff 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -3,7 +3,7 @@ module.exports = ({commandRegistry, commandInstaller, config}) -> commandRegistry.add 'atom-workspace', 'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem() - 'pane:move-item-to-top-of-stack': -> @getModel().getActivePane().moveItemToTopOfStack() + 'pane:move-active-item-to-top-of-stack': -> @getModel().getActivePane().moveActiveItemToTopOfStack() 'pane:show-next-item': -> @getModel().getActivePane().activateNextItem() 'pane:show-previous-item': -> @getModel().getActivePane().activatePreviousItem() 'pane:show-item-1': -> @getModel().getActivePane().activateItemAtIndex(0) From bc28a91e02f9248e530168fa39a14c5ab6b6ecfe Mon Sep 17 00:00:00 2001 From: natalieogle Date: Tue, 23 Feb 2016 19:20:18 -0800 Subject: [PATCH 307/971] :art: Change the structure of a few pieces relating to serialization. --- src/pane.coffee | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index d405e77a7..f2e92480b 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -55,9 +55,7 @@ class Pane extends Model if typeof @activeItem?.getURI is 'function' activeItemURI = @activeItem.getURI() itemsToBeSerialized = compact(@items.map((item) -> item if typeof item.serialize is 'function')) - itemStackIndices = [] - for item in @itemStack - itemStackIndices.push(itemsToBeSerialized.indexOf(item)) if typeof item.serialize is 'function' + itemStackIndices = (itemsToBeSerialized.indexOf(item) for item in @itemStack when typeof item.serialize is 'function') deserializer: 'Pane' id: @id @@ -305,6 +303,7 @@ class Pane extends Model itemStackIndices = (i for i in [0..@items.length-1]) for itemIndex in itemStackIndices @addItemToStack(@items[itemIndex]) + return # Add item (or move item) to the end of the itemStack addItemToStack: (newItem) -> From f7de9052d6bef9fadb77c594f464db45c8ce9f7f Mon Sep 17 00:00:00 2001 From: natalieogle Date: Tue, 23 Feb 2016 20:04:16 -0800 Subject: [PATCH 308/971] Update specs for itemStack. --- spec/pane-spec.coffee | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index c9d467af3..d4e09e645 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -196,7 +196,7 @@ describe "Pane", -> pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item2 pane.activateNextRecentlyUsedItem() - pane.moveItemToTopOfStack() + pane.moveActiveItemToTopOfStack() expect(pane.getActiveItem()).toBe item1 expect(pane.itemStack[4]).toBe item1 pane.activateNextRecentlyUsedItem() @@ -210,7 +210,7 @@ describe "Pane", -> pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item1 pane.activateNextRecentlyUsedItem() - pane.moveItemToTopOfStack() + pane.moveActiveItemToTopOfStack() expect(pane.getActiveItem()).toBe item4 expect(pane.itemStack[4]).toBe item4 @@ -280,7 +280,7 @@ describe "Pane", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")])) [item1, item2, item3] = pane.getItems() - it "removes the item from the items list and destroyes it", -> + it "removes the item from the items list and destroys it", -> expect(pane.getActiveItem()).toBe item1 pane.destroyItem(item2) expect(item2 in pane.getItems()).toBe false @@ -291,6 +291,19 @@ describe "Pane", -> expect(item1 in pane.getItems()).toBe false expect(item1.isDestroyed()).toBe true + it "removes the item from the itemStack", -> + pane.itemStack = [item2, item3, item1] + + pane.activateItem(item1) + expect(pane.getActiveItem()).toBe item1 + pane.destroyItem(item3) + expect(pane.itemStack).toEqual [item2, item1] + expect(pane.getActiveItem()).toBe item1 + + pane.destroyItem(item1) + expect(pane.itemStack).toEqual [item2] + expect(pane.getActiveItem()).toBe item2 + it "invokes ::onWillDestroyItem() observers before destroying the item", -> events = [] pane.onWillDestroyItem (event) -> @@ -944,7 +957,7 @@ describe "Pane", -> newPane = Pane.deserialize(pane.serialize(), atom) expect(newPane.getItems()).toEqual newPane.itemStack - it "does not serialize items in the itemStack if they will not be serialized", -> + it "does not serialize the reference to the items in the itemStack for pane items that will not be serialized", -> [item1, item2, item3] = pane.getItems() pane.itemStack = [item2, item1, item3] unserializable = {} From 420a8d8692080a1d067caa936f6888ff6653a2f7 Mon Sep 17 00:00:00 2001 From: natalieogle Date: Sat, 27 Feb 2016 20:41:25 -0800 Subject: [PATCH 309/971] Add activatePreviousRecentlyUsedItem to pane model and add specs. --- keymaps/darwin.cson | 3 ++- keymaps/linux.cson | 3 ++- keymaps/win32.cson | 3 ++- spec/pane-spec.coffee | 26 ++++++++++++-------------- src/pane.coffee | 11 ++++++++++- src/register-default-commands.coffee | 1 + 6 files changed, 29 insertions(+), 18 deletions(-) diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index baa664da8..819e0079e 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -75,7 +75,8 @@ 'ctrl-pagedown': 'pane:show-next-item' 'ctrl-tab': 'pane:show-next-recently-used-item' 'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' - 'ctrl-shift-tab': 'pane:show-previous-item' + 'ctrl-shift-tab': 'pane:show-previous-recently-used-item' + 'ctrl-shift-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' 'cmd-=': 'window:increase-font-size' 'cmd-+': 'window:increase-font-size' 'cmd--': 'window:decrease-font-size' diff --git a/keymaps/linux.cson b/keymaps/linux.cson index d9758e5ea..e676ed5ab 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -48,7 +48,8 @@ 'shift-backspace': 'core:backspace' 'ctrl-tab': 'pane:show-next-recently-used-item' 'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' - 'ctrl-shift-tab': 'pane:show-previous-item' + 'ctrl-shift-tab': 'pane:show-previous-recently-used-item' + 'ctrl-shift-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' 'ctrl-up': 'core:move-up' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index 3befc6b0c..5869a3ed8 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -54,7 +54,8 @@ 'shift-backspace': 'core:backspace' 'ctrl-tab': 'pane:show-next-recently-used-item' 'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' - 'ctrl-shift-tab': 'pane:show-previous-item' + 'ctrl-shift-tab': 'pane:show-previous-recently-used-item' + 'ctrl-shift-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' 'ctrl-shift-up': 'core:move-up' diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index d4e09e645..e5cbee1a6 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -183,8 +183,8 @@ describe "Pane", -> pane.activateItem(itemD, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D'] - describe "::activateNextRecentlyUsedItem()", -> - it "sets the active item to the next item in the itemStack", -> + describe "::activateNextRecentlyUsedItem() and ::activatePreviousRecentlyUsedItem()", -> + it "sets the active item to the next/previous item in the itemStack, looping around at either end", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D"), new Item("E")])) [item1, item2, item3, item4, item5] = pane.getItems() pane.itemStack = [item3, item1, item2, item5, item4] @@ -195,24 +195,22 @@ describe "Pane", -> expect(pane.getActiveItem()).toBe item5 pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item2 - pane.activateNextRecentlyUsedItem() - pane.moveActiveItemToTopOfStack() - expect(pane.getActiveItem()).toBe item1 - expect(pane.itemStack[4]).toBe item1 - pane.activateNextRecentlyUsedItem() - expect(pane.getActiveItem()).toBe item4 - pane.activateNextRecentlyUsedItem() + pane.activatePreviousRecentlyUsedItem() expect(pane.getActiveItem()).toBe item5 - pane.activateNextRecentlyUsedItem() - expect(pane.getActiveItem()).toBe item2 + pane.activatePreviousRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item4 + pane.activatePreviousRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item3 + pane.activatePreviousRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item1 pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item3 pane.activateNextRecentlyUsedItem() - expect(pane.getActiveItem()).toBe item1 + expect(pane.getActiveItem()).toBe item4 pane.activateNextRecentlyUsedItem() pane.moveActiveItemToTopOfStack() - expect(pane.getActiveItem()).toBe item4 - expect(pane.itemStack[4]).toBe item4 + expect(pane.getActiveItem()).toBe item5 + expect(pane.itemStack[4]).toBe item5 describe "::activateNextItem() and ::activatePreviousItem()", -> it "sets the active item to the next/previous item, looping around at either end", -> diff --git a/src/pane.coffee b/src/pane.coffee index f2e92480b..12abc5448 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -327,10 +327,19 @@ class Pane extends Model activateNextRecentlyUsedItem: -> if @items.length > 1 @itemStackIndex = @itemStack.length - 1 unless @itemStackIndex? + @itemStackIndex = @itemStack.length if @itemStackIndex is 0 @itemStackIndex = @itemStackIndex - 1 nextRecentlyUsedItem = @itemStack[@itemStackIndex] @setActiveItem(nextRecentlyUsedItem, modifyStack: false) - @itemStackIndex = @itemStack.length if @itemStackIndex is 0 + + # Makes the previous item in the itemStack active. + activatePreviousRecentlyUsedItem: -> + if @items.length > 1 + if @itemStackIndex + 1 is @itemStack.length or not @itemStackIndex? + @itemStackIndex = -1 + @itemStackIndex = @itemStackIndex + 1 + previousRecentlyUsedItem = @itemStack[@itemStackIndex] + @setActiveItem(previousRecentlyUsedItem, modifyStack: false) # Moves the active item to the end of the itemStack once the ctrl key is lifted moveActiveItemToTopOfStack: -> diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index 3d95f1cff..dacb1d228 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -3,6 +3,7 @@ module.exports = ({commandRegistry, commandInstaller, config}) -> commandRegistry.add 'atom-workspace', 'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem() + 'pane:show-previous-recently-used-item': -> @getModel().getActivePane().activatePreviousRecentlyUsedItem() 'pane:move-active-item-to-top-of-stack': -> @getModel().getActivePane().moveActiveItemToTopOfStack() 'pane:show-next-item': -> @getModel().getActivePane().activateNextItem() 'pane:show-previous-item': -> @getModel().getActivePane().activatePreviousItem() From 5dfd0c9102e683b00212bbfea89d775ea6b7c457 Mon Sep 17 00:00:00 2001 From: Johnston Jiaa Date: Sun, 28 Feb 2016 11:48:27 -0500 Subject: [PATCH 310/971] Show tooltip immediately if the tooltip trigger is manual --- spec/tooltip-manager-spec.coffee | 4 ++++ src/tooltip-manager.coffee | 2 ++ src/tooltip.js | 4 +++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/spec/tooltip-manager-spec.coffee b/spec/tooltip-manager-spec.coffee index 87082504a..264d1a3bb 100644 --- a/spec/tooltip-manager-spec.coffee +++ b/spec/tooltip-manager-spec.coffee @@ -28,6 +28,10 @@ describe "TooltipManager", -> hover element, -> expect(document.body.querySelector(".tooltip")).toHaveText("Title") + it "creates a tooltip immediately if the trigger type is manual", -> + manager.add element, title: "Title", trigger: "manual" + expect(document.body.querySelector(".tooltip")).toHaveText("Title") + it "allows jQuery elements to be passed as the target", -> element2 = document.createElement('div') jasmine.attachToDOM(element2) diff --git a/src/tooltip-manager.coffee b/src/tooltip-manager.coffee index 247437535..90f0ab8e6 100644 --- a/src/tooltip-manager.coffee +++ b/src/tooltip-manager.coffee @@ -63,6 +63,8 @@ class TooltipManager # full list of options. You can also supply the following additional options: # * `title` A {String} or {Function} to use for the text in the tip. If # given a function, `this` will be set to the `target` element. + # * `trigger` A {String} that's the same as Bootstrap 'click | hover | focus + # | manual', except 'manual' will show the tooltip immediately. # * `keyBindingCommand` A {String} containing a command name. If you specify # this option and a key binding exists that matches the command, it will # be appended to the title or rendered alone if no title is specified. diff --git a/src/tooltip.js b/src/tooltip.js index 4ea952a64..ad5ce0cdd 100644 --- a/src/tooltip.js +++ b/src/tooltip.js @@ -64,7 +64,9 @@ Tooltip.prototype.init = function (element, options) { if (trigger === 'click') { this.disposables.add(listen(this.element, 'click', this.options.selector, this.toggle.bind(this))) - } else if (trigger !== 'manual') { + } else if (trigger === 'manual') { + this.show() + } else { var eventIn, eventOut if (trigger === 'hover') { From 92bbbf064508d50f75b84e4e2195f194dfd9facc Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Sun, 28 Feb 2016 15:05:07 -0800 Subject: [PATCH 311/971] Windows command line does not allow ELSE on separate line, fixes #10934 --- resources/win/atom.cmd | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/win/atom.cmd b/resources/win/atom.cmd index c9bfdd5ba..8d5e6f97a 100644 --- a/resources/win/atom.cmd +++ b/resources/win/atom.cmd @@ -35,8 +35,7 @@ IF "%EXPECT_OUTPUT%"=="YES" ( "%~dp0\..\..\atom.exe" --pid=%PID% %* rem If the wait flag is set, don't exit this process until Atom tells it to. goto waitLoop - ) - ELSE ( + ) ELSE ( "%~dp0\..\..\atom.exe" %* ) ) ELSE ( From 2dad38a7826522455feb56bd33f276f6b8eeeb49 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sun, 28 Feb 2016 17:57:59 -0800 Subject: [PATCH 312/971] onDidTerminatePendingState :arrow_right: onItemDidTerminatePendingState Signed-off-by: Katrina Uychaco --- src/pane.coffee | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 12abc5448..9905680bd 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -441,8 +441,10 @@ class Pane extends Model item setPendingItem: (item) => - @pendingItem = item if @pendingItem isnt item - @emitter.emit 'did-terminate-pending-state' if not item + if @pendingItem isnt item + mostRecentPendingItem = @pendingItem + @pendingItem = item + @emitter.emit 'item-did-terminate-pending-state', mostRecentPendingItem getPendingItem: => @pendingItem or null @@ -450,8 +452,8 @@ class Pane extends Model clearPendingItem: => @setPendingItem(null) - onDidTerminatePendingState: (callback) => - @emitter.on 'did-terminate-pending-state', callback + onItemDidTerminatePendingState: (callback) => + @emitter.on 'item-did-terminate-pending-state', callback # Public: Add the given items to the pane. # From 92dea0379a041d0c43f37df8c8068485aef02eb6 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sun, 28 Feb 2016 18:32:19 -0800 Subject: [PATCH 313/971] :arrow_up: tree-view Signed-off-by: Katrina Uychaco --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5dc94cda9..8830a3f98 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.111.1", "tabs": "0.90.2", "timecop": "0.33.1", - "tree-view": "0.201.4", + "tree-view": "0.201.5", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From d39418ad7c61626753ffc9ed4536341ef0fc0b12 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sun, 28 Feb 2016 18:15:52 -0800 Subject: [PATCH 314/971] Add specs for Pane::setPendingItem and ::onItemDidTerminatePendingState Signed-off-by: Katrina Uychaco --- spec/pane-spec.coffee | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index e5cbee1a6..b4785e113 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -183,6 +183,41 @@ describe "Pane", -> pane.activateItem(itemD, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D'] + describe "::setPendingItem", -> + pane = null + + beforeEach -> + pane = atom.workspace.getActivePane() + + it "changes the pending item", -> + expect(pane.getPendingItem()).toBeNull() + pane.setPendingItem("fake item") + expect(pane.getPendingItem()).toEqual "fake item" + + describe "::onItemDidTerminatePendingState callback", -> + pane = null + callbackCalled = false + + beforeEach -> + pane = atom.workspace.getActivePane() + callbackCalled = false + + it "is called when the pending item changes", -> + pane.setPendingItem("fake item one") + pane.onItemDidTerminatePendingState (item) -> + callbackCalled = true + expect(item).toEqual "fake item one" + pane.setPendingItem("fake item two") + expect(callbackCalled).toBeTruthy() + + it "has access to the new pending item via ::getPendingItem", -> + pane.setPendingItem("fake item one") + pane.onItemDidTerminatePendingState (item) -> + callbackCalled = true + expect(pane.getPendingItem()).toEqual "fake item two" + pane.setPendingItem("fake item two") + expect(callbackCalled).toBeTruthy() + describe "::activateNextRecentlyUsedItem() and ::activatePreviousRecentlyUsedItem()", -> it "sets the active item to the next/previous item in the itemStack, looping around at either end", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D"), new Item("E")])) From 4769e601bf9e3e0595789a276a29f0cd2e1e5b5a Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 29 Feb 2016 11:27:38 -0800 Subject: [PATCH 315/971] Move spec from tabs package See https://github.com/atom/tabs/pull/274#issuecomment-190064071 Signed-off-by: Katrina Uychaco --- spec/workspace-spec.coffee | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 78bbf2fdb..2e15431b2 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -604,6 +604,25 @@ describe "Workspace", -> runs -> expect(pane.getPendingItem()).toBeNull() + describe "when opening will switch from a pending tab to a permanent tab", -> + it "keeps the pending tab open", -> + editor1 = null + editor2 = null + + waitsForPromise -> + atom.workspace.open('sample.txt').then (o) -> + editor1 = o + + waitsForPromise -> + atom.workspace.open('sample2.txt', pending: true).then (o) -> + editor2 = o + + runs -> + pane = atom.workspace.getActivePane() + pane.activateItem(editor1) + expect(pane.getItems().length).toBe 2 + expect(pane.getItems()).toEqual [editor1, editor2] + describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> pane = workspace.getActivePane() From 920d348014ac2ddf42e54e0bed6bb2cd71465146 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 29 Feb 2016 11:32:28 -0800 Subject: [PATCH 316/971] Only move legit items to the top of the stack Fixes #11002 cc @natalieogle --- spec/pane-spec.coffee | 4 ++++ src/pane.coffee | 1 + 2 files changed, 5 insertions(+) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index b4785e113..888ccf6e2 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -337,6 +337,10 @@ describe "Pane", -> expect(pane.itemStack).toEqual [item2] expect(pane.getActiveItem()).toBe item2 + pane.destroyItem(item2) + expect(pane.itemStack).toEqual [] + expect(pane.getActiveItem()).toBeUndefined() + it "invokes ::onWillDestroyItem() observers before destroying the item", -> events = [] pane.onWillDestroyItem (event) -> diff --git a/src/pane.coffee b/src/pane.coffee index 9905680bd..25f421934 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -307,6 +307,7 @@ class Pane extends Model # Add item (or move item) to the end of the itemStack addItemToStack: (newItem) -> + return unless newItem? index = @itemStack.indexOf(newItem) @itemStack.splice(index, 1) unless index is -1 @itemStack.push(newItem) From c849216084d248402c3d9a81fe7d55cacc0b7545 Mon Sep 17 00:00:00 2001 From: Jonathan Willis Date: Mon, 29 Feb 2016 14:49:19 -0500 Subject: [PATCH 317/971] Config: Added documentation for order key in config. --- src/config.coffee | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/config.coffee b/src/config.coffee index 348b7b94f..66f07516e 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -319,6 +319,23 @@ ScopeDescriptor = require './scope-descriptor' # * line breaks - `line breaks
` # * ~~strikethrough~~ - `~~strikethrough~~` # +# #### order +# +# The settings view orders your settings alphabetically. You can override this +# ordering with the order key. +# +# ```coffee +# config: +# zSetting: +# type: 'integer' +# default: 4 +# order: 1 +# aSetting: +# type: 'integer' +# default: 4 +# order: 2 +# ``` +# # ## Best practices # # * Don't depend on (or write to) configuration keys outside of your keypath. From c99260bce75b852f60581782630813ccd5ab4e39 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 29 Feb 2016 15:04:36 -0500 Subject: [PATCH 318/971] Disable EOL diffs for diff stats too. --- src/git-repository-async.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 894b1216a..91b78e0c5 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -596,7 +596,15 @@ export default class GitRepositoryAsync { .then(([repo, headCommit]) => Promise.all([repo, headCommit.getTree()])) .then(([repo, tree]) => { const options = new Git.DiffOptions() + options.contextLines = 0 + options.flags = Git.Diff.OPTION.DISABLE_PATHSPEC_MATCH options.pathspec = this.relativize(_path, repo.workdir()) + if (process.platform === 'win32') { + // Ignore eol of line differences on windows so that files checked in + // as LF don't report every line modified when the text contains CRLF + // endings. + options.flags |= Git.Diff.OPTION.IGNORE_WHITESPACE_EOL + } return Git.Diff.treeToWorkdir(repo, tree, options) }) .then(diff => this._getDiffLines(diff)) From 9a938064cb72ecce7ebed2a795fd53727591a3f0 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Mon, 29 Feb 2016 14:09:01 -0800 Subject: [PATCH 319/971] :arrow_up: tabs@0.91.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8830a3f98..42cf22f8b 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.1.0", "styleguide": "0.45.2", "symbols-view": "0.111.1", - "tabs": "0.90.2", + "tabs": "0.91.0", "timecop": "0.33.1", "tree-view": "0.201.5", "update-package-dependencies": "0.10.0", From 26cf7f081fe84ad8d08000f4b605d6dbd11c5906 Mon Sep 17 00:00:00 2001 From: Willem Van Lint Date: Fri, 26 Feb 2016 16:20:39 -0500 Subject: [PATCH 320/971] Registry for editors --- spec/text-editor-registry-spec.coffee | 38 +++++++++++++++++++++++++++ src/atom-environment.coffee | 6 +++++ src/text-editor-registry.coffee | 33 +++++++++++++++++++++++ src/text-editor.coffee | 5 +++- src/workspace.coffee | 5 +++- 5 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 spec/text-editor-registry-spec.coffee create mode 100644 src/text-editor-registry.coffee diff --git a/spec/text-editor-registry-spec.coffee b/spec/text-editor-registry-spec.coffee new file mode 100644 index 000000000..04665bef2 --- /dev/null +++ b/spec/text-editor-registry-spec.coffee @@ -0,0 +1,38 @@ +TextEditorRegistry = require '../src/text-editor-registry' + +describe "TextEditorRegistry", -> + [registry, editor] = [] + + beforeEach -> + registry = new TextEditorRegistry + + describe "when a TextEditor is added", -> + it "gets added to the list of registered editors", -> + editor = {} + registry.add(editor) + expect(registry.editors.size).toBe 1 + expect(registry.editors.has(editor)).toBe(true) + + it "returns a Disposable that can unregister the editor", -> + editor = {} + disposable = registry.add(editor) + expect(registry.editors.size).toBe 1 + disposable.dispose() + expect(registry.editors.size).toBe 0 + + describe "when the registry is observed", -> + it "calls the callback for current and future editors until unsubscribed", -> + [editor1, editor2, editor3] = [{}, {}, {}] + + registry.add(editor1) + subscription = registry.observe spy = jasmine.createSpy() + expect(spy.calls.length).toBe 1 + + registry.add(editor2) + expect(spy.calls.length).toBe 2 + expect(spy.argsForCall[0][0]).toBe editor1 + expect(spy.argsForCall[1][0]).toBe editor2 + + subscription.dispose() + registry.add(editor3) + expect(spy.calls.length).toBe 2 diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index c42bf05aa..0ee12fe93 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -40,6 +40,7 @@ Project = require './project' TextEditor = require './text-editor' TextBuffer = require 'text-buffer' Gutter = require './gutter' +TextEditorRegistry = require './text-editor-registry' WorkspaceElement = require './workspace-element' PanelContainerElement = require './panel-container-element' @@ -111,6 +112,9 @@ class AtomEnvironment extends Model # Public: A {Workspace} instance workspace: null + # Public: A {TextEditorRegistry} instance + textEditors: null + saveStateDebounceInterval: 1000 ### @@ -183,6 +187,8 @@ class AtomEnvironment extends Model }) @themes.workspace = @workspace + @textEditors = new TextEditorRegistry + @config.load() @themes.loadBaseStylesheets() diff --git a/src/text-editor-registry.coffee b/src/text-editor-registry.coffee new file mode 100644 index 000000000..fc95f9cd7 --- /dev/null +++ b/src/text-editor-registry.coffee @@ -0,0 +1,33 @@ +{Emitter, Disposable} = require 'event-kit' + +# This global registry tracks registered `TextEditors`. +# +# Packages that provide extra functionality to `TextEditors`, such as +# autocompletion, can observe this registry to find applicable editors. +module.exports = +class TextEditorRegistry + constructor: -> + @editors = new Set + @emitter = new Emitter + + # Register a `TextEditor`. + # + # * `editor` The editor to register. + # + # Returns a {Disposable} on which `.dispose()` can be called to remove the + # added editor. To avoid any memory leaks this should be called when the + # editor is destroyed. + add: (editor) -> + @editors.add(editor) + @emitter.emit 'did-add-editor', editor + new Disposable => @editors.delete(editor) + + # Invoke the given callback with all the current and future registered + # `TextEditors`. + # + # * `callback` {Function} to be called with current and future text editors. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observe: (callback) -> + @editors.forEach (editor) -> callback(editor) + @emitter.on 'did-add-editor', callback diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 5d55e0e48..d7fae17b3 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -82,7 +82,10 @@ class TextEditor extends Model state.project = atomEnvironment.project state.assert = atomEnvironment.assert.bind(atomEnvironment) state.applicationDelegate = atomEnvironment.applicationDelegate - new this(state) + editor = new this(state) + disposable = atomEnvironment.textEditors.add(editor) + editor.onDidDestroy -> disposable.dispose() + editor constructor: (params={}) -> super diff --git a/src/workspace.coffee b/src/workspace.coffee index 636ebfd69..c925a495a 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -558,7 +558,10 @@ class Workspace extends Model @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate }, params) - new TextEditor(params) + editor = new TextEditor(params) + disposable = atom.textEditors.add(editor) + editor.onDidDestroy -> disposable.dispose() + editor # Public: Asynchronously reopens the last-closed item's URI if it hasn't already been # reopened. From 8959e414a63aea30f9fc3f7b776a4444caf93212 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 29 Feb 2016 15:54:47 -0800 Subject: [PATCH 321/971] :arrow_up: tabs Signed-off-by: Katrina Uychaco --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 42cf22f8b..e6e63d31b 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.1.0", "styleguide": "0.45.2", "symbols-view": "0.111.1", - "tabs": "0.91.0", + "tabs": "0.91.1", "timecop": "0.33.1", "tree-view": "0.201.5", "update-package-dependencies": "0.10.0", From 98516150788b9b5022019179ce0bf1b5c8db3448 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 29 Feb 2016 16:24:22 -0800 Subject: [PATCH 322/971] :arrow_up: find-and-replace Signed-off-by: Michelle Tilley --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e6e63d31b..9ba72dc83 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "dev-live-reload": "0.47.0", "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", - "find-and-replace": "0.197.2", + "find-and-replace": "0.197.3", "fuzzy-finder": "1.0.2", "git-diff": "1.0.0", "go-to-line": "0.30.0", From 6292484c971827c2137b3ce2fc02e33979cd664a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Feb 2016 17:30:03 -0700 Subject: [PATCH 323/971] Always strip git+ prefix and .git suffix from package repository URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, a guard based on the presence of the _id field (which is inserted by npm during installation) prevented a regex replacement of the git+ prefix on URLs. Now we always do this. Since the .git suffix also causes problems and we’re removing that in packages, I now remove that as well. --- .../package.json | 8 ++++++++ spec/package-manager-spec.coffee | 7 ++++++- src/package-manager.coffee | 5 +++-- 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 spec/fixtures/packages/package-with-prefixed-and-suffixed-repo-url/package.json diff --git a/spec/fixtures/packages/package-with-prefixed-and-suffixed-repo-url/package.json b/spec/fixtures/packages/package-with-prefixed-and-suffixed-repo-url/package.json new file mode 100644 index 000000000..ce57f7501 --- /dev/null +++ b/spec/fixtures/packages/package-with-prefixed-and-suffixed-repo-url/package.json @@ -0,0 +1,8 @@ +{ + "name": "package-with-a-git-prefixed-git-repo-url", + "repository": { + "type": "git", + "url": "git+https://github.com/example/repo.git" + }, + "_id": "this is here to simulate the URL being already normalized by npm. we still need to stript git+ from the beginning and .git from the end." +} diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 0a2d614b3..3b54691b2 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -55,12 +55,17 @@ describe "PackageManager", -> it "normalizes short repository urls in package.json", -> {metadata} = atom.packages.loadPackage("package-with-short-url-package-json") expect(metadata.repository.type).toBe "git" - expect(metadata.repository.url).toBe "https://github.com/example/repo.git" + expect(metadata.repository.url).toBe "https://github.com/example/repo" {metadata} = atom.packages.loadPackage("package-with-invalid-url-package-json") expect(metadata.repository.type).toBe "git" expect(metadata.repository.url).toBe "foo" + it "trims git+ from the beginning and .git from the end of repository URLs, even if npm already normalized them ", -> + {metadata} = atom.packages.loadPackage("package-with-prefixed-and-suffixed-repo-url") + expect(metadata.repository.type).toBe "git" + expect(metadata.repository.url).toBe "https://github.com/example/repo" + it "returns null if the package is not found in any package directory", -> spyOn(console, 'warn') expect(atom.packages.loadPackage("this-package-cannot-be-found")).toBeNull() diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 33f8f86a3..94b55a793 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -541,11 +541,12 @@ class PackageManager unless typeof metadata.name is 'string' and metadata.name.length > 0 metadata.name = packageName + if metadata.repository?.type is 'git' and typeof metadata.repository.url is 'string' + metadata.repository.url = metadata.repository.url.replace(/(^git\+)|(\.git$)/g, '') + metadata normalizePackageMetadata: (metadata) -> unless metadata?._id normalizePackageData ?= require 'normalize-package-data' normalizePackageData(metadata) - if metadata.repository?.type is 'git' and typeof metadata.repository.url is 'string' - metadata.repository.url = metadata.repository.url.replace(/^git\+/, '') From b95fd26cce2ced1a5b4d28f92bde8fd3a1a28f88 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Feb 2016 17:38:47 -0700 Subject: [PATCH 324/971] Adjust TextEditorRegistry docs /cc @wvanlint --- src/text-editor-registry.coffee | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/text-editor-registry.coffee b/src/text-editor-registry.coffee index fc95f9cd7..943a0d31a 100644 --- a/src/text-editor-registry.coffee +++ b/src/text-editor-registry.coffee @@ -1,9 +1,16 @@ {Emitter, Disposable} = require 'event-kit' -# This global registry tracks registered `TextEditors`. +# Experimental: This global registry tracks registered `TextEditors`. # -# Packages that provide extra functionality to `TextEditors`, such as -# autocompletion, can observe this registry to find applicable editors. +# If you want to add functionality to a wider set of text editors than just +# those appearing within workspace panes, use `atom.textEditors.observe` to +# invoke a callback for all current and future registered text editors. +# +# If you want packages to be able to add functionality to your non-pane text +# editors (such as a search field in a custom user interface element), register +# them for observation via `atom.textEditors.add`. **Important:** When you're +# done using your editor, be sure to call `dispose` on the returned disposable +# to avoid leaking editors. module.exports = class TextEditorRegistry constructor: -> From 7600c4bdf47cd17e0e54e8d620ed33ed89f21bd4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Feb 2016 17:39:11 -0700 Subject: [PATCH 325/971] Avoid wrapper closure by passing callback to forEach directly /cc @wvanlint --- src/text-editor-registry.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor-registry.coffee b/src/text-editor-registry.coffee index 943a0d31a..8a17335d4 100644 --- a/src/text-editor-registry.coffee +++ b/src/text-editor-registry.coffee @@ -36,5 +36,5 @@ class TextEditorRegistry # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. observe: (callback) -> - @editors.forEach (editor) -> callback(editor) + @editors.forEach(callback) @emitter.on 'did-add-editor', callback From b7f0f794f850aff4e1e32a18ff977002a3b365db Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 29 Feb 2016 12:57:03 -0800 Subject: [PATCH 326/971] Don't destroy pane if replacing last pending item Signed-off-by: Michelle Tilley --- spec/workspace-spec.coffee | 28 ++++++++++++++++++++++++++++ src/pane.coffee | 8 ++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 2e15431b2..b39a07a6c 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -623,6 +623,34 @@ describe "Workspace", -> expect(pane.getItems().length).toBe 2 expect(pane.getItems()).toEqual [editor1, editor2] + describe "when replacing a pending item which is the last item in a second pane", -> + it "does not destory the pane even if core.destroyEmptyPanes is on", -> + atom.config.set('core.destroyEmptyPanes', true) + editor1 = null + editor2 = null + leftPane = atom.workspace.getActivePane() + rightPane = null + + waitsForPromise -> + atom.workspace.open('sample.js', pending: true, split: 'right').then (o) -> + editor1 = o + rightPane = atom.workspace.getActivePane() + spyOn rightPane, "destroyed" + + runs -> + expect(leftPane).not.toBe rightPane + expect(atom.workspace.getActivePane()).toBe rightPane + expect(atom.workspace.getActivePane().getItems().length).toBe 1 + expect(rightPane.getPendingItem()).toBe editor1 + + waitsForPromise -> + atom.workspace.open('sample.txt', pending: true).then (o) -> + editor2 = o + + runs -> + expect(rightPane.getPendingItem()).toBe editor2 + expect(rightPane.destroyed.callCount).toBe 0 + describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> pane = workspace.getActivePane() diff --git a/src/pane.coffee b/src/pane.coffee index 25f421934..b944f763c 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -423,10 +423,6 @@ class Pane extends Model return if item in @items - pendingItem = @getPendingItem() - @destroyItem(pendingItem) if pendingItem? - @setPendingItem(item) if pending - if typeof item.onDidDestroy is 'function' itemSubscriptions = new CompositeDisposable itemSubscriptions.add item.onDidDestroy => @removeItem(item, false) @@ -437,6 +433,10 @@ class Pane extends Model @subscriptionsPerItem.set item, itemSubscriptions @items.splice(index, 0, item) + pendingItem = @getPendingItem() + @destroyItem(pendingItem) if pendingItem? + @setPendingItem(item) if pending + @emitter.emit 'did-add-item', {item, index, moved} @setActiveItem(item) unless @getActiveItem()? item From a55ad00ac1cf3995447cfed0c7ad12269fcfe7ee Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Feb 2016 18:51:12 -0700 Subject: [PATCH 327/971] Fix option pass-through in Selection:: and Cursor::autoscroll --- src/selection.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/selection.coffee b/src/selection.coffee index c4046677b..e208ea55a 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -810,11 +810,11 @@ class Selection extends Model @wordwise = false @linewise = false - autoscroll: -> + autoscroll: (options) -> if @marker.hasTail() - @editor.scrollToScreenRange(@getScreenRange(), reversed: @isReversed()) + @editor.scrollToScreenRange(@getScreenRange(), Object.assign({reversed: @isReversed()}, options)) else - @cursor.autoscroll() + @cursor.autoscroll(options) clearAutoscroll: -> From 7f744681c3c792e0bb01beca71ad5eedc3fddcb7 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Feb 2016 18:51:36 -0700 Subject: [PATCH 328/971] Simplify consolidateSelections spec to test autoscroll with events --- spec/text-editor-spec.coffee | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 74875a7f5..1c0d3ad02 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2141,32 +2141,21 @@ describe "TextEditor", -> expect(editor.getSelections()).toEqual [selection, selection2, selection3, selection4] [selection, selection2, selection3, selection4] - it "destroys all selections but the least recent, returning true if any selections were destroyed", -> + it "destroys all selections but the oldest selection and autoscrolls to it, returning true if any selections were destroyed", -> [selection1] = makeMultipleSelections() + autoscrollEvents = [] + editor.onDidRequestAutoscroll (event) -> autoscrollEvents.push(event) + expect(editor.consolidateSelections()).toBeTruthy() expect(editor.getSelections()).toEqual [selection1] expect(selection1.isEmpty()).toBeFalsy() expect(editor.consolidateSelections()).toBeFalsy() expect(editor.getSelections()).toEqual [selection1] - it "scrolls to the remaining selection", -> - [selection1] = makeMultipleSelections() - - atom.config.set('editor.scrollPastEnd', true) - editor.setHeight(100, true) - editor.setLineHeightInPixels(10) - editor.setFirstVisibleScreenRow(10) - expect(editor.getVisibleRowRange()[0]).toBeGreaterThan(selection1.getBufferRowRange()[1]) - - editor.consolidateSelections() - - waitsForPromise -> - new Promise((resolve) -> window.requestAnimationFrame(resolve)) - - runs -> - expect(editor.getVisibleRowRange()[0]).not.toBeGreaterThan(selection1.getBufferRowRange()[0]) - expect(editor.getVisibleRowRange()[1]).not.toBeLessThan(selection1.getBufferRowRange()[0]) + expect(autoscrollEvents).toEqual([ + {screenRange: selection1.getScreenRange(), options: {center: true, reversed: false}} + ]) describe "when the cursor is moved while there is a selection", -> makeSelection = -> selection.setBufferRange [[1, 2], [1, 5]] From 4e409ef44ae2d406e9e7449014aa3a8d7fc84f9e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Feb 2016 19:43:59 -0700 Subject: [PATCH 329/971] :arrow_up: image-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9ba72dc83..da7c700e5 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "git-diff": "1.0.0", "go-to-line": "0.30.0", "grammar-selector": "0.48.1", - "image-view": "0.56.0", + "image-view": "0.57.0", "incompatible-packages": "0.25.1", "keybinding-resolver": "0.35.0", "line-ending-selector": "0.3.1", From fdac1e466369c11fb72a331b645da50b9975b52b Mon Sep 17 00:00:00 2001 From: Koki Takahashi Date: Thu, 8 Jan 2015 02:07:44 +0900 Subject: [PATCH 330/971] :bug: Treat empty comment line as comment and add tests (Fix #4140) --- spec/fixtures/sample-with-comments.js | 13 +++++++- spec/language-mode-spec.coffee | 45 ++++++++++++++++++++++++--- src/language-mode.coffee | 2 -- src/tokenized-line.coffee | 1 - 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/spec/fixtures/sample-with-comments.js b/spec/fixtures/sample-with-comments.js index c10d42232..b40ddc890 100644 --- a/spec/fixtures/sample-with-comments.js +++ b/spec/fixtures/sample-with-comments.js @@ -9,12 +9,23 @@ var quicksort = function () { // Wowza if (items.length <= 1) return items; var pivot = items.shift(), current, left = [], right = []; + /* + This is a multiline comment block with + an empty line inside of it. + + Awesome. + */ while(items.length > 0) { current = items.shift(); current < pivot ? left.push(current) : right.push(current); } + // This is a collection of + // single line comments + + // ...with an empty line + // among it, geez! return sort(left).concat(pivot).concat(sort(right)); }; // this is a single-line comment return sort(Array.apply(this, arguments)); -}; \ No newline at end of file +}; diff --git a/spec/language-mode-spec.coffee b/spec/language-mode-spec.coffee index cd32e29c7..d4d97d2de 100644 --- a/spec/language-mode-spec.coffee +++ b/spec/language-mode-spec.coffee @@ -1,4 +1,4 @@ -describe "LanguageMode", -> +fdescribe "LanguageMode", -> [editor, buffer, languageMode] = [] afterEach -> @@ -430,7 +430,7 @@ describe "LanguageMode", -> languageMode.foldAll() fold1 = editor.tokenizedLineForScreenRow(0).fold - expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19] + expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 30] fold1.destroy() fold2 = editor.tokenizedLineForScreenRow(1).fold @@ -441,6 +441,14 @@ describe "LanguageMode", -> fold4 = editor.tokenizedLineForScreenRow(3).fold expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [6, 8] + fold5 = editor.tokenizedLineForScreenRow(6).fold + expect([fold5.getStartRow(), fold5.getEndRow()]).toEqual [11, 16] + fold5.destroy(); + + fold6 = editor.tokenizedLineForScreenRow(13).fold + expect([fold6.getStartRow(), fold6.getEndRow()]).toEqual [21, 22] + fold6.destroy(); + describe ".foldAllAtIndentLevel()", -> it "folds every foldable range at a given indentLevel", -> languageMode.foldAllAtIndentLevel(2) @@ -450,19 +458,48 @@ describe "LanguageMode", -> fold1.destroy() fold2 = editor.tokenizedLineForScreenRow(11).fold - expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [11, 14] + expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [11, 16] fold2.destroy() + fold3 = editor.tokenizedLineForScreenRow(17).fold + expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [17, 20] + fold3.destroy() + + fold4 = editor.tokenizedLineForScreenRow(21).fold + expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [21, 22] + fold4.destroy() + + fold5 = editor.tokenizedLineForScreenRow(24).fold + expect([fold5.getStartRow(), fold5.getEndRow()]).toEqual [24, 25] + fold5.destroy() + it "does not fold anything but the indentLevel", -> languageMode.foldAllAtIndentLevel(0) fold1 = editor.tokenizedLineForScreenRow(0).fold - expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19] + expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 30] fold1.destroy() fold2 = editor.tokenizedLineForScreenRow(5).fold expect(fold2).toBeFalsy() + describe ".isFoldableAtBufferRow(bufferRow)", -> + it "returns true if the line starts a multi-line comment", -> + expect(languageMode.isFoldableAtBufferRow(1)).toBe true + expect(languageMode.isFoldableAtBufferRow(6)).toBe true + expect(languageMode.isFoldableAtBufferRow(8)).toBe false + expect(languageMode.isFoldableAtBufferRow(11)).toBe true + expect(languageMode.isFoldableAtBufferRow(15)).toBe false + expect(languageMode.isFoldableAtBufferRow(17)).toBe true + expect(languageMode.isFoldableAtBufferRow(21)).toBe true + expect(languageMode.isFoldableAtBufferRow(24)).toBe true + expect(languageMode.isFoldableAtBufferRow(28)).toBe false + + it "does not return true for a line in the middle of a comment that's followed by an indented line", -> + expect(languageMode.isFoldableAtBufferRow(7)).toBe false + editor.buffer.insert([8, 0], ' ') + expect(languageMode.isFoldableAtBufferRow(7)).toBe false + describe "css", -> beforeEach -> waitsForPromise -> diff --git a/src/language-mode.coffee b/src/language-mode.coffee index dc5003cac..4824431bf 100644 --- a/src/language-mode.coffee +++ b/src/language-mode.coffee @@ -147,13 +147,11 @@ class LanguageMode if bufferRow > 0 for currentRow in [bufferRow-1..0] by -1 - break if @buffer.isRowBlank(currentRow) break unless @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment() startRow = currentRow if bufferRow < @buffer.getLastRow() for currentRow in [bufferRow+1..@buffer.getLastRow()] by 1 - break if @buffer.isRowBlank(currentRow) break unless @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment() endRow = currentRow diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index bf6a6dd2b..c1ac4caff 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -498,7 +498,6 @@ class TokenizedLine while iterator.next() scopes = iterator.getScopes() continue if scopes.length is 1 - continue unless NonWhitespaceRegex.test(iterator.getText()) for scope in scopes if CommentScopeRegex.test(scope) @isCommentLine = true From 6d00d5e9798e0f1e08cf2af04036f1455e15923c Mon Sep 17 00:00:00 2001 From: Koki Takahashi Date: Thu, 8 Jan 2015 02:18:23 +0900 Subject: [PATCH 331/971] Revert focus on language-mode-spec.coffee --- spec/language-mode-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/language-mode-spec.coffee b/spec/language-mode-spec.coffee index d4d97d2de..6ae21a8e6 100644 --- a/spec/language-mode-spec.coffee +++ b/spec/language-mode-spec.coffee @@ -1,4 +1,4 @@ -fdescribe "LanguageMode", -> +describe "LanguageMode", -> [editor, buffer, languageMode] = [] afterEach -> From a59ee5dec89fadbd469bbdd0595293564fd16751 Mon Sep 17 00:00:00 2001 From: Koki Takahashi Date: Wed, 3 Jun 2015 02:12:40 +0900 Subject: [PATCH 332/971] Remove trailing semicolon --- spec/language-mode-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/language-mode-spec.coffee b/spec/language-mode-spec.coffee index 6ae21a8e6..26bb19b0e 100644 --- a/spec/language-mode-spec.coffee +++ b/spec/language-mode-spec.coffee @@ -443,11 +443,11 @@ describe "LanguageMode", -> fold5 = editor.tokenizedLineForScreenRow(6).fold expect([fold5.getStartRow(), fold5.getEndRow()]).toEqual [11, 16] - fold5.destroy(); + fold5.destroy() fold6 = editor.tokenizedLineForScreenRow(13).fold expect([fold6.getStartRow(), fold6.getEndRow()]).toEqual [21, 22] - fold6.destroy(); + fold6.destroy() describe ".foldAllAtIndentLevel()", -> it "folds every foldable range at a given indentLevel", -> From 3b135c5b7f4456319847d5af89806496bdfe1b38 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 1 Mar 2016 09:43:25 +0100 Subject: [PATCH 333/971] :arrow_up: autocomplete-plus --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index da7c700e5..1c55268dd 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.0", "autocomplete-html": "0.7.2", - "autocomplete-plus": "2.29.0", + "autocomplete-plus": "2.29.1", "autocomplete-snippets": "1.10.0", "autoflow": "0.27.0", "autosave": "0.23.1", From 4cd7cbda021b8673a6a93eb31e9e8c5e1106b74d Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 1 Mar 2016 10:53:31 -0500 Subject: [PATCH 334/971] Test ignoreScrollPastEnd. --- spec/text-editor-presenter-spec.coffee | 28 ++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 05ac87c0c..62d1e4747 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -635,16 +635,28 @@ describe "TextEditorPresenter", -> expectStateUpdate presenter, -> presenter.setExplicitHeight(500) expect(getState(presenter).verticalScrollbar.scrollHeight).toBe 500 - it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", -> - presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) - expectStateUpdate presenter, -> presenter.setScrollTop(300) - expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + describe "scrollPastEnd", -> + it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", -> + presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) + expectStateUpdate presenter, -> presenter.setScrollTop(300) + expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight - expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true) - expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3) + expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true) + expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3) - expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) - expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) + expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + + it "doesn't add the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true but ignoreScrollPastEnd is true", -> + presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10, ignoreScrollPastEnd: true) + expectStateUpdate presenter, -> presenter.setScrollTop(300) + expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + + expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true) + expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + + expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) + expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight describe ".scrollTop", -> it "tracks the value of ::scrollTop", -> From d929543aa2c9d8a0d2e8a771247059ebd90b5326 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 1 Mar 2016 10:56:03 -0500 Subject: [PATCH 335/971] Test for getElement. --- spec/view-registry-spec.coffee | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spec/view-registry-spec.coffee b/spec/view-registry-spec.coffee index 16672b25d..68a482b48 100644 --- a/spec/view-registry-spec.coffee +++ b/spec/view-registry-spec.coffee @@ -23,6 +23,15 @@ describe "ViewRegistry", -> component = new TestComponent expect(registry.getView(component)).toBe component.element + describe "when passed an object with a getElement function", -> + it "returns the return value of getElement if it's an instance of HTMLElement", -> + class TestComponent + getElement: -> + @myElement ?= document.createElement('div') + + component = new TestComponent + expect(registry.getView(component)).toBe component.myElement + describe "when passed a model object", -> describe "when a view provider is registered matching the object's constructor", -> it "constructs a view element and assigns the model on it", -> From e0d44aad5df4abd92082e3f99dc1ae66001a7a00 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 1 Mar 2016 11:07:55 -0500 Subject: [PATCH 336/971] Test getElement. --- spec/text-editor-spec.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 1c0d3ad02..ae8ec54a9 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5828,3 +5828,7 @@ describe "TextEditor", -> screenRange: marker1.getRange(), rangeIsReversed: false } + + describe "::getElement", -> + it "returns an element", -> + expect(editor.getElement() instanceof HTMLElement).toBe(true) From e1c5aad0a6972ce9950363770db6af30d56677c9 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 1 Mar 2016 11:43:38 -0500 Subject: [PATCH 337/971] Test ignoreInvisibles and grammarName. --- spec/text-editor-spec.coffee | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index ae8ec54a9..c75084fd5 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5829,6 +5829,29 @@ describe "TextEditor", -> rangeIsReversed: false } + describe "when the editor is constructed with the ignoreInvisibles option set to true", -> + beforeEach -> + atom.workspace.destroyActivePane() + waitsForPromise -> + atom.workspace.open('sample.js', ignoreInvisibles: true).then (o) -> editor = o + + it "ignores invisibles even if editor.showInvisibles is true", -> + atom.config.set('editor.showInvisibles', true) + invisibles = editor.tokenizedLineForScreenRow(0).invisibles + expect(invisibles).toBe(null) + + describe "when the editor is constructed with the grammarName option set", -> + beforeEach -> + atom.workspace.destroyActivePane() + waitsForPromise -> + atom.packages.activatePackage('language-coffee-script') + + waitsForPromise -> + atom.workspace.open('sample.js', grammarName: 'source.coffee').then (o) -> editor = o + + it "sets the grammar", -> + expect(editor.getGrammar().name).toBe 'CoffeeScript' + describe "::getElement", -> it "returns an element", -> expect(editor.getElement() instanceof HTMLElement).toBe(true) From da0ce2a7bde9003b0b432247a13e9d4673729f80 Mon Sep 17 00:00:00 2001 From: Thomas Johansen Date: Tue, 1 Mar 2016 18:54:11 +0100 Subject: [PATCH 338/971] :arrow_up: language-xml --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1c55268dd..7ff3df275 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "language-text": "0.7.0", "language-todo": "0.27.0", "language-toml": "0.18.0", - "language-xml": "0.34.3", + "language-xml": "0.34.4", "language-yaml": "0.25.1" }, "private": true, From e1f7dd92238026f43bf7c4c98b29091c9e9b6260 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 1 Mar 2016 15:43:45 -0500 Subject: [PATCH 339/971] :arrow_up: nodegit@0.11.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8830a3f98..5df436fb5 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "less-cache": "0.23", "line-top-index": "0.2.0", "marked": "^0.3.4", - "nodegit": "0.11.5", + "nodegit": "0.11.6", "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "^5", From 3716aaf00bc080ba91a2743c69fc4bbf72e23c83 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Tue, 2 Feb 2016 11:56:05 -0800 Subject: [PATCH 340/971] Spike out an update wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can expose just a few event subscription methods on atom.update to take care of what most packages (e.g. About) would be interested in. Of course the updater runs on the main thread so we’re proxying them through IPC. It’s fine. --- src/application-delegate.coffee | 28 +++++++++++++ src/atom-environment.coffee | 3 ++ src/browser/auto-update-manager.coffee | 14 +++++++ src/update.js | 56 ++++++++++++++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 src/update.js diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index cc1f4c946..a4262965f 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -182,6 +182,34 @@ class ApplicationDelegate new Disposable -> ipcRenderer.removeListener('message', outerCallback) + onDidBeginCheckingForUpdate: (callback) -> + outerCallback = (message, detail) -> + if message is 'checking-for-update' + callback(detail) + + ipc.on('message', outerCallback) + new Disposable -> + ipc.removeListener('message', outerCallback) + + onDidCompleteDownloadingUpdate: (callback) -> + outerCallback = (message, detail) -> + if message is 'update-downloaded' + callback(detail) + + ipc.on('message', outerCallback) + new Disposable -> + ipc.removeListener('message', outerCallback) + + onUpdateNotAvailable: (callback) -> + outerCallback = (message, detail) -> + if message is 'update-not-available' + callback(detail) + + ipc.on('message', outerCallback) + new Disposable -> + ipc.removeListener('message', outerCallback) + + onApplicationMenuCommand: (callback) -> outerCallback = (event, args...) -> callback(args...) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 0ee12fe93..e6830ceaa 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -115,6 +115,9 @@ class AtomEnvironment extends Model # Public: A {TextEditorRegistry} instance textEditors: null + # Public: An {Update} instance + update: null + saveStateDebounceInterval: 1000 ### diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index 2df338761..c58d317cb 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -39,12 +39,19 @@ class AutoUpdateManager autoUpdater.on 'checking-for-update', => @setState(CheckingState) + @emitWindowEvent('checking-for-update') autoUpdater.on 'update-not-available', => @setState(NoUpdateAvailableState) autoUpdater.on 'update-available', => @setState(DownladingState) + # We use sendMessage to send an event called 'update-available' below + # once the update download is complete. This mismatch between the electron + # autoUpdater events is unfortunate but in the interest of not changing the + # one existing event handled by applicationDelegate + @emitWindowEvent('did-begin-downloading-update') + @emit('did-begin-download') autoUpdater.on 'update-downloaded', (event, releaseNotes, @releaseVersion) => @setState(UpdateAvailableState) @@ -66,10 +73,16 @@ class AutoUpdateManager emitUpdateAvailableEvent: (windows...) -> return unless @releaseVersion? + @emitWindowEvent('update-available', {@releaseVersion}) for atomWindow in windows atomWindow.sendMessage('update-available', {@releaseVersion}) return + emitWindowEvent: (eventName, windows, payload) -> + for atomWindow in windows + atomWindow.sendMessage(eventName, payload) + return + setState: (state) -> return if @state is state @state = state @@ -93,6 +106,7 @@ class AutoUpdateManager @checkForUpdatesIntervalID = null check: ({hidePopups}={}) -> + console.log 'checking for update' unless hidePopups autoUpdater.once 'update-not-available', @onUpdateNotAvailable autoUpdater.once 'error', @onUpdateError diff --git a/src/update.js b/src/update.js new file mode 100644 index 000000000..fcadbc21c --- /dev/null +++ b/src/update.js @@ -0,0 +1,56 @@ +'use babel' + +import {Emitter} from 'event-kit' + +export default class Update { + constructor () { + this.subscriptions = new CompositeDisposable() + this.emitter = new Emitter() + } + + initialize () { + atom.applicationDelegate.onDidBeginDownloadingUpdate(() => { + this.emitter.emit('did-begin-downloading-update') + }) + atom.applicationDelegate.onDidBeginCheckingForUpdate(() => { + this.emitter.emit('did-begin-checking-for-update') + }) + atom.applicationDelegate.onUpdateAvailable(() => { + this.emitter.emit('did-complete-downloading-update') + }) + } + + dispose () { + this.subscriptions.dispose() + } + + onDidBeginCheckingForUpdate (callback) { + this.subscriptions.add( + this.emitter.on('did-begin-checking-for-update', callback) + ) + } + + onDidBeginDownload (callback) { + this.subscriptions.add( + this.emitter.on('did-begin-downloading-update', callback) + ) + } + + onDidCompleteDownload (callback) { + this.subscriptions.add( + this.emitter.on('did-complete-downloading-update', callback) + ) + } + + onUpdateNotAvailable (callback) { + this.subscriptions.add() + } + + check () { + // TODO + } + + getState () { + // TODO + } +} From 1eaf30fae979ecda2b6cd6e5df99d31069e01f22 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Tue, 2 Feb 2016 22:34:11 -0800 Subject: [PATCH 341/971] Add a few more things before stepping aside to work on another issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …maybe rebase this away… --- spec/update-spec.js | 37 ++++++++++++++++++++++++++ src/application-delegate.coffee | 4 +-- src/browser/auto-update-manager.coffee | 11 ++++---- src/update.js | 12 +++++++-- 4 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 spec/update-spec.js diff --git a/spec/update-spec.js b/spec/update-spec.js new file mode 100644 index 000000000..9e12bab48 --- /dev/null +++ b/spec/update-spec.js @@ -0,0 +1,37 @@ +'use babel' + +import Update from '../src/update' +import remote from 'remote' + +fdescribe('Update', () => { + describe('::initialize', () => { + it('subscribes to appropriate applicationDelegate events', () => { + const update = new Update() + update.initialize() + + const downloadingSpy = jasmine.createSpy('downloadingSpy') + const checkingSpy = jasmine.createSpy('checkingSpy') + const noUpdateSpy = jasmine.createSpy('noUpdateSpy') + + update.onDidBeginCheckingForUpdate(checkingSpy) + update.onDidBeginDownload(downloadingSpy) + update.onUpdateNotAvailable(noUpdateSpy) + + const webContents = remote.getCurrentWebContents() + // AutoUpdateManager sends these from main process land + webContents.send('update-available', {releaseVersion: '1.2.3'}) + webContents.send('did-begin-downloading-update') + webContents.send('checking-for-update') + webContents.send('update-not-available') + + waitsFor(() => { + noUpdateSpy.callCount > 0 + }) + runs(() => { + expect(downloadingSpy.callCount).toBe(1) + expect(checkingSpy.callCount).toBe(1) + expect(noUpdateSpy.callCount).toBe(1) + }) + }) + }) +}) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index a4262965f..8368e10ec 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -175,7 +175,7 @@ class ApplicationDelegate onUpdateAvailable: (callback) -> outerCallback = (event, message, detail) -> - if message is 'update-available' + if message is 'did-begin-downloading-update' callback(detail) ipcRenderer.on('message', outerCallback) @@ -193,7 +193,7 @@ class ApplicationDelegate onDidCompleteDownloadingUpdate: (callback) -> outerCallback = (message, detail) -> - if message is 'update-downloaded' + if message is 'update-available' callback(detail) ipc.on('message', outerCallback) diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index c58d317cb..064ccf27c 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -43,6 +43,7 @@ class AutoUpdateManager autoUpdater.on 'update-not-available', => @setState(NoUpdateAvailableState) + @emitWindowEvent('update-not-available') autoUpdater.on 'update-available', => @setState(DownladingState) @@ -55,7 +56,7 @@ class AutoUpdateManager autoUpdater.on 'update-downloaded', (event, releaseNotes, @releaseVersion) => @setState(UpdateAvailableState) - @emitUpdateAvailableEvent(@getWindows()...) + @emitUpdateAvailableEvent() @config.onDidChange 'core.automaticallyUpdate', ({newValue}) => if newValue @@ -71,15 +72,13 @@ class AutoUpdateManager when 'linux' @setState(UnsupportedState) - emitUpdateAvailableEvent: (windows...) -> + emitUpdateAvailableEvent: -> return unless @releaseVersion? @emitWindowEvent('update-available', {@releaseVersion}) - for atomWindow in windows - atomWindow.sendMessage('update-available', {@releaseVersion}) return - emitWindowEvent: (eventName, windows, payload) -> - for atomWindow in windows + emitWindowEvent: (eventName, payload) -> + for atomWindow in @getWindows() atomWindow.sendMessage(eventName, payload) return diff --git a/src/update.js b/src/update.js index fcadbc21c..4abffc0b0 100644 --- a/src/update.js +++ b/src/update.js @@ -1,6 +1,6 @@ 'use babel' -import {Emitter} from 'event-kit' +import {Emitter, CompositeDisposable} from 'event-kit' export default class Update { constructor () { @@ -42,8 +42,16 @@ export default class Update { ) } + onUpdateAvailable (callback) { + this.subscriptions.add( + this.emitter.on('update-available', callback) + ) + } + onUpdateNotAvailable (callback) { - this.subscriptions.add() + this.subscriptions.add( + this.emitter.on('update-not-available', callback) + ) } check () { From 6505c650089a9d63b0529000603fcf4e4a4d3473 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Wed, 3 Feb 2016 14:20:24 -0800 Subject: [PATCH 342/971] add out/ to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6eec21c2a..bce6c56d3 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ debug.log docs/output docs/includes spec/fixtures/evil-files/ +out/ From 44d78327458bf65b0be90c7ec4c9bccf5ee3a385 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Wed, 3 Feb 2016 14:20:56 -0800 Subject: [PATCH 343/971] add @update to AtomEnvironment --- src/atom-environment.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index e6830ceaa..31562a392 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -41,6 +41,7 @@ TextEditor = require './text-editor' TextBuffer = require 'text-buffer' Gutter = require './gutter' TextEditorRegistry = require './text-editor-registry' +Update = require './update' WorkspaceElement = require './workspace-element' PanelContainerElement = require './panel-container-element' @@ -191,6 +192,7 @@ class AtomEnvironment extends Model @themes.workspace = @workspace @textEditors = new TextEditorRegistry + @update = new Update() @config.load() From 6fce680a2890b698eea10a056f85dc63ae0e67a3 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Wed, 3 Feb 2016 14:21:46 -0800 Subject: [PATCH 344/971] Send check-for-update message over ipc --- src/browser/atom-application.coffee | 3 +++ src/browser/auto-update-manager.coffee | 3 ++- src/update.js | 7 ++----- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 40b04c3a1..ebcc9717b 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -304,6 +304,9 @@ class AtomApplication ipcMain.on 'execute-javascript-in-dev-tools', (event, code) -> event.sender.devToolsWebContents?.executeJavaScript(code) + ipcMain.on 'check-for-update', => + @autoUpdateManager.check() + setupDockMenu: -> if process.platform is 'darwin' dockMenu = Menu.buildFromTemplate [ diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index 064ccf27c..c17d9aead 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -3,6 +3,7 @@ _ = require 'underscore-plus' Config = require '../config' {EventEmitter} = require 'events' path = require 'path' +ipc = require 'ipc' IdleState = 'idle' CheckingState = 'checking' @@ -47,7 +48,7 @@ class AutoUpdateManager autoUpdater.on 'update-available', => @setState(DownladingState) - # We use sendMessage to send an event called 'update-available' below + # We use sendMessage to send an event called 'update-available' in 'update-downloaded' # once the update download is complete. This mismatch between the electron # autoUpdater events is unfortunate but in the interest of not changing the # one existing event handled by applicationDelegate diff --git a/src/update.js b/src/update.js index 4abffc0b0..452a3647f 100644 --- a/src/update.js +++ b/src/update.js @@ -1,6 +1,7 @@ 'use babel' import {Emitter, CompositeDisposable} from 'event-kit' +import ipc from 'ipc' export default class Update { constructor () { @@ -55,10 +56,6 @@ export default class Update { } check () { - // TODO - } - - getState () { - // TODO + ipc.send('check-for-update') } } From a8a5006950c61863ad784bbf8f0a01a98b9ea1d9 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Wed, 3 Feb 2016 14:21:56 -0800 Subject: [PATCH 345/971] Add missing subscription event --- src/application-delegate.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index 8368e10ec..f70bd7d0c 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -182,6 +182,9 @@ class ApplicationDelegate new Disposable -> ipcRenderer.removeListener('message', outerCallback) + onDidBeginDownloadingUpdate: (callback) -> + @onUpdateAvailable(callback) + onDidBeginCheckingForUpdate: (callback) -> outerCallback = (message, detail) -> if message is 'checking-for-update' From bdb9866ff1a1b1fa9411e261a43357c15159e351 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Wed, 3 Feb 2016 17:15:05 -0800 Subject: [PATCH 346/971] remove errant log statement --- src/browser/auto-update-manager.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index c17d9aead..2b6539bd8 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -106,7 +106,6 @@ class AutoUpdateManager @checkForUpdatesIntervalID = null check: ({hidePopups}={}) -> - console.log 'checking for update' unless hidePopups autoUpdater.once 'update-not-available', @onUpdateNotAvailable autoUpdater.once 'error', @onUpdateError From 6d77e97901e7b304a75afacd59fec05e75c0bb14 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Wed, 3 Feb 2016 17:32:54 -0800 Subject: [PATCH 347/971] The app delegate uses the `message` channel here. --- spec/update-spec.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/spec/update-spec.js b/spec/update-spec.js index 9e12bab48..8c8247538 100644 --- a/spec/update-spec.js +++ b/spec/update-spec.js @@ -18,11 +18,10 @@ fdescribe('Update', () => { update.onUpdateNotAvailable(noUpdateSpy) const webContents = remote.getCurrentWebContents() - // AutoUpdateManager sends these from main process land - webContents.send('update-available', {releaseVersion: '1.2.3'}) - webContents.send('did-begin-downloading-update') - webContents.send('checking-for-update') - webContents.send('update-not-available') + webContents.send('message', 'update-available', {releaseVersion: '1.2.3'}) + webContents.send('message', 'did-begin-downloading-update') + webContents.send('message', 'checking-for-update') + webContents.send('message', 'update-not-available') waitsFor(() => { noUpdateSpy.callCount > 0 From e6a86d38d1d9a841fb7d7fffedab344fe8c4e5d2 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Fri, 5 Feb 2016 12:11:18 -0800 Subject: [PATCH 348/971] this doesn't work --- spec/update-spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/update-spec.js b/spec/update-spec.js index 8c8247538..4d66da666 100644 --- a/spec/update-spec.js +++ b/spec/update-spec.js @@ -17,6 +17,7 @@ fdescribe('Update', () => { update.onDidBeginDownload(downloadingSpy) update.onUpdateNotAvailable(noUpdateSpy) + // This doesn't work const webContents = remote.getCurrentWebContents() webContents.send('message', 'update-available', {releaseVersion: '1.2.3'}) webContents.send('message', 'did-begin-downloading-update') From 004a0e870d83b3b067964890c280e1118844b5c2 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 18 Feb 2016 23:40:18 -0800 Subject: [PATCH 349/971] Add ApplicationDelegate listener disposables to subscriptions --- src/update.js | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/update.js b/src/update.js index 452a3647f..b4699fbec 100644 --- a/src/update.js +++ b/src/update.js @@ -10,15 +10,26 @@ export default class Update { } initialize () { - atom.applicationDelegate.onDidBeginDownloadingUpdate(() => { - this.emitter.emit('did-begin-downloading-update') - }) - atom.applicationDelegate.onDidBeginCheckingForUpdate(() => { - this.emitter.emit('did-begin-checking-for-update') - }) - atom.applicationDelegate.onUpdateAvailable(() => { - this.emitter.emit('did-complete-downloading-update') - }) + this.subscriptions.add( + atom.applicationDelegate.onDidBeginDownloadingUpdate(() => { + this.emitter.emit('did-begin-downloading-update') + }) + ) + this.subscriptions.add( + atom.applicationDelegate.onDidBeginCheckingForUpdate(() => { + this.emitter.emit('did-begin-checking-for-update') + }) + ) + this.subscriptions.add( + atom.applicationDelegate.onDidCompleteDownloadingUpdate(() => { + this.emitter.emit('did-complete-downloading-update') + }) + ) + this.subscriptions.add( + atom.applicationDelegate.onUpdateNotAvailable(() => { + this.emitter.emit('update-not-available') + }) + ) } dispose () { From adc086d4859ed63ef031bb25caa2ec19bdfe69a8 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 18 Feb 2016 23:41:23 -0800 Subject: [PATCH 350/971] Add specs for Update methods (::check left TODO) --- spec/update-spec.js | 102 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 9 deletions(-) diff --git a/spec/update-spec.js b/spec/update-spec.js index 4d66da666..0f2f66289 100644 --- a/spec/update-spec.js +++ b/spec/update-spec.js @@ -2,36 +2,120 @@ import Update from '../src/update' import remote from 'remote' +import ipc from 'ipc' fdescribe('Update', () => { + let update + + afterEach(() => { + update.dispose() + }) + describe('::initialize', () => { it('subscribes to appropriate applicationDelegate events', () => { - const update = new Update() - update.initialize() + update = new Update() const downloadingSpy = jasmine.createSpy('downloadingSpy') const checkingSpy = jasmine.createSpy('checkingSpy') const noUpdateSpy = jasmine.createSpy('noUpdateSpy') + const completedDownloadSpy = jasmine.createSpy('completedDownloadSpy') - update.onDidBeginCheckingForUpdate(checkingSpy) - update.onDidBeginDownload(downloadingSpy) - update.onUpdateNotAvailable(noUpdateSpy) + update.emitter.on('did-begin-checking-for-update', checkingSpy) + update.emitter.on('did-begin-downloading-update', downloadingSpy) + update.emitter.on('did-complete-downloading-update', completedDownloadSpy) + update.emitter.on('update-not-available', noUpdateSpy) + + update.initialize() - // This doesn't work const webContents = remote.getCurrentWebContents() - webContents.send('message', 'update-available', {releaseVersion: '1.2.3'}) - webContents.send('message', 'did-begin-downloading-update') webContents.send('message', 'checking-for-update') + webContents.send('message', 'did-begin-downloading-update') + webContents.send('message', 'update-available', {releaseVersion: '1.2.3'}) webContents.send('message', 'update-not-available') waitsFor(() => { - noUpdateSpy.callCount > 0 + return noUpdateSpy.callCount > 0 }) + runs(() => { expect(downloadingSpy.callCount).toBe(1) expect(checkingSpy.callCount).toBe(1) expect(noUpdateSpy.callCount).toBe(1) + expect(completedDownloadSpy.callCount).toBe(1) }) }) }) + + beforeEach(() => { + update = new Update() + update.initialize() + }) + + describe('::onDidBeginCheckingForUpdate', () => { + it('subscribes to "did-begin-checking-for-update" event', () => { + const spy = jasmine.createSpy('spy') + update.onDidBeginCheckingForUpdate(spy) + update.emitter.emit('did-begin-checking-for-update') + expect(spy.callCount).toBe(1) + }) + }) + + describe('::onDidBeginDownload', () => { + it('subscribes to "did-begin-downloading-update" event', () => { + const spy = jasmine.createSpy('spy') + update.onDidBeginDownload(spy) + update.emitter.emit('did-begin-downloading-update') + expect(spy.callCount).toBe(1) + }) + }) + + describe('::onDidCompleteDownload', () => { + it('subscribes to "did-complete-downloading-update" event', () => { + const spy = jasmine.createSpy('spy') + update.onDidCompleteDownload(spy) + update.emitter.emit('did-complete-downloading-update') + expect(spy.callCount).toBe(1) + }) + }) + + describe('::onUpdateNotAvailable', () => { + it('subscribes to "update-not-available" event', () => { + const spy = jasmine.createSpy('spy') + update.onUpdateNotAvailable(spy) + update.emitter.emit('update-not-available') + expect(spy.callCount).toBe(1) + }) + }) + + describe('::onUpdateAvailable', () => { + it('subscribes to "update-available" event', () => { + const spy = jasmine.createSpy('spy') + update.onUpdateAvailable(spy) + update.emitter.emit('update-available') + expect(spy.callCount).toBe(1) + }) + }) + + // TODO: spec is timing out. spy is not called + // describe('::check', () => { + // it('sends "check-for-update" event', () => { + // const spy = jasmine.createSpy('spy') + // ipc.on('check-for-update', () => { + // spy() + // }) + // update.check() + // waitsFor(() => { + // return spy.callCount > 0 + // }) + // }) + // }) + + describe('::dispose', () => { + it('disposes of subscriptions', () => { + expect(update.subscriptions.disposables).not.toBeNull() + update.dispose() + expect(update.subscriptions.disposables).toBeNull() + }) + }) + }) From 3e29cd57626945827d6930bfea5d31760184fa8a Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 18 Feb 2016 23:50:27 -0800 Subject: [PATCH 351/971] Merge branch 'master' into dh-expose-updates --- src/application-delegate.coffee | 18 +++++++++--------- src/browser/atom-application.coffee | 3 +++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index f70bd7d0c..2aaeb35ff 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -186,31 +186,31 @@ class ApplicationDelegate @onUpdateAvailable(callback) onDidBeginCheckingForUpdate: (callback) -> - outerCallback = (message, detail) -> + outerCallback = (event, message, detail) -> if message is 'checking-for-update' callback(detail) - ipc.on('message', outerCallback) + ipcRenderer.on('message', outerCallback) new Disposable -> - ipc.removeListener('message', outerCallback) + ipcRenderer.removeListener('message', outerCallback) onDidCompleteDownloadingUpdate: (callback) -> - outerCallback = (message, detail) -> + outerCallback = (event, message, detail) -> if message is 'update-available' callback(detail) - ipc.on('message', outerCallback) + ipcRenderer.on('message', outerCallback) new Disposable -> - ipc.removeListener('message', outerCallback) + ipcRenderer.removeListener('message', outerCallback) onUpdateNotAvailable: (callback) -> - outerCallback = (message, detail) -> + outerCallback = (event, message, detail) -> if message is 'update-not-available' callback(detail) - ipc.on('message', outerCallback) + ipcRenderer.on('message', outerCallback) new Disposable -> - ipc.removeListener('message', outerCallback) + ipcRenderer.removeListener('message', outerCallback) onApplicationMenuCommand: (callback) -> diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index ebcc9717b..fad69b967 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -307,6 +307,9 @@ class AtomApplication ipcMain.on 'check-for-update', => @autoUpdateManager.check() + ipcMain.on 'execute-javascript-in-dev-tools', (event, code) -> + event.sender.devToolsWebContents?.executeJavaScript(code) + setupDockMenu: -> if process.platform is 'darwin' dockMenu = Menu.buildFromTemplate [ From 5cda45b5c73b893e1352d099aa7e3f9e3ecb611d Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Fri, 19 Feb 2016 12:36:09 -0800 Subject: [PATCH 352/971] =?UTF-8?q?Use=20ipcRenderer=20from=20=E2=80=98ele?= =?UTF-8?q?ctron=E2=80=99=20rather=20than=20=E2=80=98pic=E2=80=99=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/update.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/update.js b/src/update.js index b4699fbec..5389c35e8 100644 --- a/src/update.js +++ b/src/update.js @@ -1,7 +1,7 @@ 'use babel' import {Emitter, CompositeDisposable} from 'event-kit' -import ipc from 'ipc' +import {ipcRenderer} from 'electron' export default class Update { constructor () { @@ -67,6 +67,6 @@ export default class Update { } check () { - ipc.send('check-for-update') + ipcRenderer.send('check-for-update') } } From 16ebefca3f2a7c4fa23ac0106009ca413c7541b3 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Fri, 19 Feb 2016 12:36:38 -0800 Subject: [PATCH 353/971] =?UTF-8?q?Remove=20unnecessary=20=E2=80=98ipc'=20?= =?UTF-8?q?module=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/auto-update-manager.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index 2b6539bd8..c8c57cb01 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -3,7 +3,6 @@ _ = require 'underscore-plus' Config = require '../config' {EventEmitter} = require 'events' path = require 'path' -ipc = require 'ipc' IdleState = 'idle' CheckingState = 'checking' From 19d30c7dc955b557927571fbf60038fc446e4a6d Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Fri, 19 Feb 2016 12:40:51 -0800 Subject: [PATCH 354/971] Improve specs for `Update` class --- spec/update-spec.js | 104 ++++++++++---------------------------------- 1 file changed, 23 insertions(+), 81 deletions(-) diff --git a/spec/update-spec.js b/spec/update-spec.js index 0f2f66289..bd50113c2 100644 --- a/spec/update-spec.js +++ b/spec/update-spec.js @@ -1,62 +1,29 @@ 'use babel' import Update from '../src/update' -import remote from 'remote' -import ipc from 'ipc' +import {remote} from 'electron' +const electronAutoUpdater = remote.require('electron').autoUpdater -fdescribe('Update', () => { +describe('Update', () => { let update - afterEach(() => { - update.dispose() - }) - - describe('::initialize', () => { - it('subscribes to appropriate applicationDelegate events', () => { - update = new Update() - - const downloadingSpy = jasmine.createSpy('downloadingSpy') - const checkingSpy = jasmine.createSpy('checkingSpy') - const noUpdateSpy = jasmine.createSpy('noUpdateSpy') - const completedDownloadSpy = jasmine.createSpy('completedDownloadSpy') - - update.emitter.on('did-begin-checking-for-update', checkingSpy) - update.emitter.on('did-begin-downloading-update', downloadingSpy) - update.emitter.on('did-complete-downloading-update', completedDownloadSpy) - update.emitter.on('update-not-available', noUpdateSpy) - - update.initialize() - - const webContents = remote.getCurrentWebContents() - webContents.send('message', 'checking-for-update') - webContents.send('message', 'did-begin-downloading-update') - webContents.send('message', 'update-available', {releaseVersion: '1.2.3'}) - webContents.send('message', 'update-not-available') - - waitsFor(() => { - return noUpdateSpy.callCount > 0 - }) - - runs(() => { - expect(downloadingSpy.callCount).toBe(1) - expect(checkingSpy.callCount).toBe(1) - expect(noUpdateSpy.callCount).toBe(1) - expect(completedDownloadSpy.callCount).toBe(1) - }) - }) - }) - beforeEach(() => { update = new Update() update.initialize() }) + afterEach(() => { + update.dispose() + }) + describe('::onDidBeginCheckingForUpdate', () => { it('subscribes to "did-begin-checking-for-update" event', () => { const spy = jasmine.createSpy('spy') update.onDidBeginCheckingForUpdate(spy) - update.emitter.emit('did-begin-checking-for-update') - expect(spy.callCount).toBe(1) + electronAutoUpdater.emit('checking-for-update') + waitsFor(() => { + return spy.callCount === 1 + }) }) }) @@ -64,8 +31,10 @@ fdescribe('Update', () => { it('subscribes to "did-begin-downloading-update" event', () => { const spy = jasmine.createSpy('spy') update.onDidBeginDownload(spy) - update.emitter.emit('did-begin-downloading-update') - expect(spy.callCount).toBe(1) + electronAutoUpdater.emit('update-available') + waitsFor(() => { + return spy.callCount === 1 + }) }) }) @@ -73,8 +42,10 @@ fdescribe('Update', () => { it('subscribes to "did-complete-downloading-update" event', () => { const spy = jasmine.createSpy('spy') update.onDidCompleteDownload(spy) - update.emitter.emit('did-complete-downloading-update') - expect(spy.callCount).toBe(1) + electronAutoUpdater.emit('update-downloaded', null, null, {releaseVersion: '1.2.3'}) + waitsFor(() => { + return spy.callCount === 1 + }) }) }) @@ -82,39 +53,10 @@ fdescribe('Update', () => { it('subscribes to "update-not-available" event', () => { const spy = jasmine.createSpy('spy') update.onUpdateNotAvailable(spy) - update.emitter.emit('update-not-available') - expect(spy.callCount).toBe(1) - }) - }) - - describe('::onUpdateAvailable', () => { - it('subscribes to "update-available" event', () => { - const spy = jasmine.createSpy('spy') - update.onUpdateAvailable(spy) - update.emitter.emit('update-available') - expect(spy.callCount).toBe(1) - }) - }) - - // TODO: spec is timing out. spy is not called - // describe('::check', () => { - // it('sends "check-for-update" event', () => { - // const spy = jasmine.createSpy('spy') - // ipc.on('check-for-update', () => { - // spy() - // }) - // update.check() - // waitsFor(() => { - // return spy.callCount > 0 - // }) - // }) - // }) - - describe('::dispose', () => { - it('disposes of subscriptions', () => { - expect(update.subscriptions.disposables).not.toBeNull() - update.dispose() - expect(update.subscriptions.disposables).toBeNull() + electronAutoUpdater.emit('update-not-available') + waitsFor(() => { + return spy.callCount === 1 + }) }) }) From dd53c6f85644701cd0c6b61e7cfca5c69d098df0 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Fri, 19 Feb 2016 15:35:42 -0800 Subject: [PATCH 355/971] Use `onDidCompleteDownloadingUpdate` in `listenForUpdates` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This gets a bit confusing… It was formerly `@applicationDelegate.onUpdateAvailable`, but `::onUpdateAvailable` listens for the `did-begin-downloading-update` event and `::onDidCompleteDownloadingUpdate` listens for the `update-available` event. Note that ‘available’ here means successfully downloaded and ready to be used and NOT available to be downloaded. --- src/atom-environment.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 31562a392..b9527b947 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -891,7 +891,8 @@ class AtomEnvironment extends Model @emitter.emit 'update-available', details listenForUpdates: -> - @disposables.add(@applicationDelegate.onUpdateAvailable(@updateAvailable.bind(this))) + # listen for updates available locally (that have been successfully downloaded) + @disposables.add(@applicationDelegate.onDidCompleteDownloadingUpdate(@updateAvailable.bind(this))) setBodyPlatformClass: -> @document.body.classList.add("platform-#{process.platform}") From 342f72b6a14a8bd7280133e034ab8fc07e1b3257 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:07:11 -0800 Subject: [PATCH 356/971] Rename `Update` `AutoUpdateManager` --- ...te-spec.js => auto-update-manager-spec.js} | 21 +++++++++---------- src/atom-environment.coffee | 8 +++---- src/{update.js => auto-update-manager.js} | 2 +- 3 files changed, 15 insertions(+), 16 deletions(-) rename spec/{update-spec.js => auto-update-manager-spec.js} (75%) rename src/{update.js => auto-update-manager.js} (97%) diff --git a/spec/update-spec.js b/spec/auto-update-manager-spec.js similarity index 75% rename from spec/update-spec.js rename to spec/auto-update-manager-spec.js index bd50113c2..b78830c33 100644 --- a/spec/update-spec.js +++ b/spec/auto-update-manager-spec.js @@ -1,25 +1,25 @@ 'use babel' -import Update from '../src/update' +import AutoUpdateManager from '../src/auto-update-manager' import {remote} from 'electron' const electronAutoUpdater = remote.require('electron').autoUpdater -describe('Update', () => { - let update +fdescribe('AutoUpdateManager (renderer)', () => { + let autoUpdateManager beforeEach(() => { - update = new Update() - update.initialize() + autoUpdateManager = new AutoUpdateManager() + autoUpdateManager.initialize() }) afterEach(() => { - update.dispose() + autoUpdateManager.dispose() }) describe('::onDidBeginCheckingForUpdate', () => { it('subscribes to "did-begin-checking-for-update" event', () => { const spy = jasmine.createSpy('spy') - update.onDidBeginCheckingForUpdate(spy) + autoUpdateManager.onDidBeginCheckingForUpdate(spy) electronAutoUpdater.emit('checking-for-update') waitsFor(() => { return spy.callCount === 1 @@ -30,7 +30,7 @@ describe('Update', () => { describe('::onDidBeginDownload', () => { it('subscribes to "did-begin-downloading-update" event', () => { const spy = jasmine.createSpy('spy') - update.onDidBeginDownload(spy) + autoUpdateManager.onDidBeginDownload(spy) electronAutoUpdater.emit('update-available') waitsFor(() => { return spy.callCount === 1 @@ -41,7 +41,7 @@ describe('Update', () => { describe('::onDidCompleteDownload', () => { it('subscribes to "did-complete-downloading-update" event', () => { const spy = jasmine.createSpy('spy') - update.onDidCompleteDownload(spy) + autoUpdateManager.onDidCompleteDownload(spy) electronAutoUpdater.emit('update-downloaded', null, null, {releaseVersion: '1.2.3'}) waitsFor(() => { return spy.callCount === 1 @@ -52,12 +52,11 @@ describe('Update', () => { describe('::onUpdateNotAvailable', () => { it('subscribes to "update-not-available" event', () => { const spy = jasmine.createSpy('spy') - update.onUpdateNotAvailable(spy) + autoUpdateManager.onUpdateNotAvailable(spy) electronAutoUpdater.emit('update-not-available') waitsFor(() => { return spy.callCount === 1 }) }) }) - }) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index b9527b947..4fead0da9 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -41,7 +41,7 @@ TextEditor = require './text-editor' TextBuffer = require 'text-buffer' Gutter = require './gutter' TextEditorRegistry = require './text-editor-registry' -Update = require './update' +AutoUpdateManager = require './auto-update-manager' WorkspaceElement = require './workspace-element' PanelContainerElement = require './panel-container-element' @@ -116,8 +116,8 @@ class AtomEnvironment extends Model # Public: A {TextEditorRegistry} instance textEditors: null - # Public: An {Update} instance - update: null + # Public: An {AutoUpdateManager} instance + autoUpdater: null saveStateDebounceInterval: 1000 @@ -192,7 +192,7 @@ class AtomEnvironment extends Model @themes.workspace = @workspace @textEditors = new TextEditorRegistry - @update = new Update() + @autoUpdater = new AutoUpdateManager @config.load() diff --git a/src/update.js b/src/auto-update-manager.js similarity index 97% rename from src/update.js rename to src/auto-update-manager.js index 5389c35e8..1f0f39f29 100644 --- a/src/update.js +++ b/src/auto-update-manager.js @@ -3,7 +3,7 @@ import {Emitter, CompositeDisposable} from 'event-kit' import {ipcRenderer} from 'electron' -export default class Update { +export default class AutoUpdateManager { constructor () { this.subscriptions = new CompositeDisposable() this.emitter = new Emitter() From e40c91d3538e512004e058b8c21bd53994a36a69 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:08:59 -0800 Subject: [PATCH 357/971] All subscriptions can be in one call --- src/auto-update-manager.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 1f0f39f29..006eadf9e 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -13,19 +13,13 @@ export default class AutoUpdateManager { this.subscriptions.add( atom.applicationDelegate.onDidBeginDownloadingUpdate(() => { this.emitter.emit('did-begin-downloading-update') - }) - ) - this.subscriptions.add( + }), atom.applicationDelegate.onDidBeginCheckingForUpdate(() => { this.emitter.emit('did-begin-checking-for-update') - }) - ) - this.subscriptions.add( + }), atom.applicationDelegate.onDidCompleteDownloadingUpdate(() => { this.emitter.emit('did-complete-downloading-update') - }) - ) - this.subscriptions.add( + }), atom.applicationDelegate.onUpdateNotAvailable(() => { this.emitter.emit('update-not-available') }) From ddff53bfc56b373c479c4c50c1f4dde6a04c80d7 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:10:56 -0800 Subject: [PATCH 358/971] Postfix ifs --- src/application-delegate.coffee | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index 2aaeb35ff..a853cd8ea 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -166,8 +166,7 @@ class ApplicationDelegate onDidOpenLocations: (callback) -> outerCallback = (event, message, detail) -> - if message is 'open-locations' - callback(detail) + callback(detail) if message is 'open-locations' ipcRenderer.on('message', outerCallback) new Disposable -> @@ -175,8 +174,7 @@ class ApplicationDelegate onUpdateAvailable: (callback) -> outerCallback = (event, message, detail) -> - if message is 'did-begin-downloading-update' - callback(detail) + callback(detail) if message is 'did-begin-downloading-update' ipcRenderer.on('message', outerCallback) new Disposable -> @@ -187,8 +185,7 @@ class ApplicationDelegate onDidBeginCheckingForUpdate: (callback) -> outerCallback = (event, message, detail) -> - if message is 'checking-for-update' - callback(detail) + callback(detail) if message is 'checking-for-update' ipcRenderer.on('message', outerCallback) new Disposable -> @@ -196,8 +193,7 @@ class ApplicationDelegate onDidCompleteDownloadingUpdate: (callback) -> outerCallback = (event, message, detail) -> - if message is 'update-available' - callback(detail) + callback(detail) if message is 'update-available' ipcRenderer.on('message', outerCallback) new Disposable -> @@ -205,14 +201,12 @@ class ApplicationDelegate onUpdateNotAvailable: (callback) -> outerCallback = (event, message, detail) -> - if message is 'update-not-available' - callback(detail) + callback(detail) if message is 'update-not-available' ipcRenderer.on('message', outerCallback) new Disposable -> ipcRenderer.removeListener('message', outerCallback) - onApplicationMenuCommand: (callback) -> outerCallback = (event, args...) -> callback(args...) From b481a06cbfb7628d31c3690e9429961da2867e9a Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:14:05 -0800 Subject: [PATCH 359/971] Pass the app delegate into AutoUpdateManager --- spec/auto-update-manager-spec.js | 2 +- src/atom-environment.coffee | 1 + src/auto-update-manager.js | 10 +++++----- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index b78830c33..93df903de 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -9,7 +9,7 @@ fdescribe('AutoUpdateManager (renderer)', () => { beforeEach(() => { autoUpdateManager = new AutoUpdateManager() - autoUpdateManager.initialize() + autoUpdateManager.initialize(atom.applicationDelegate) }) afterEach(() => { diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 4fead0da9..e492c7067 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -193,6 +193,7 @@ class AtomEnvironment extends Model @textEditors = new TextEditorRegistry @autoUpdater = new AutoUpdateManager + @autoUpdater.initialize(@applicationDelegate) @config.load() diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 006eadf9e..c0f0b39bb 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -9,18 +9,18 @@ export default class AutoUpdateManager { this.emitter = new Emitter() } - initialize () { + initialize (updateEventEmitter) { this.subscriptions.add( - atom.applicationDelegate.onDidBeginDownloadingUpdate(() => { + updateEventEmitter.onDidBeginDownloadingUpdate(() => { this.emitter.emit('did-begin-downloading-update') }), - atom.applicationDelegate.onDidBeginCheckingForUpdate(() => { + updateEventEmitter.onDidBeginCheckingForUpdate(() => { this.emitter.emit('did-begin-checking-for-update') }), - atom.applicationDelegate.onDidCompleteDownloadingUpdate(() => { + updateEventEmitter.onDidCompleteDownloadingUpdate(() => { this.emitter.emit('did-complete-downloading-update') }), - atom.applicationDelegate.onUpdateNotAvailable(() => { + updateEventEmitter.onUpdateNotAvailable(() => { this.emitter.emit('update-not-available') }) ) From a876e85899cd2ebb4056d356ae5dc22866f561b8 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:14:52 -0800 Subject: [PATCH 360/971] check -> checkForUpdate --- src/auto-update-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index c0f0b39bb..05de08a3a 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -60,7 +60,7 @@ export default class AutoUpdateManager { ) } - check () { + checkForUpdate () { ipcRenderer.send('check-for-update') } } From eaba6943194153c55ad753e5e6137f8f64a254bd Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:14:59 -0800 Subject: [PATCH 361/971] =?UTF-8?q?Let=E2=80=99s=20make=20this=20private?= =?UTF-8?q?=20for=20now?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/atom-environment.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index e492c7067..481e762ca 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -116,7 +116,7 @@ class AtomEnvironment extends Model # Public: A {TextEditorRegistry} instance textEditors: null - # Public: An {AutoUpdateManager} instance + # Private: An {AutoUpdateManager} instance autoUpdater: null saveStateDebounceInterval: 1000 From 3a5c8271629f6ebd9d4b03779f3b019d1bad5bc9 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:23:11 -0800 Subject: [PATCH 362/971] Add dispose spec --- spec/auto-update-manager-spec.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 93df903de..6aa791373 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -59,4 +59,29 @@ fdescribe('AutoUpdateManager (renderer)', () => { }) }) }) + + describe('::dispose', () => { + it('subscribes to "update-not-available" event', () => { + const spy = jasmine.createSpy('spy') + const doneIndicator = jasmine.createSpy('spy') + atom.applicationDelegate.onUpdateNotAvailable(doneIndicator) + autoUpdateManager.dispose() + autoUpdateManager.onDidBeginCheckingForUpdate(spy) + autoUpdateManager.onDidBeginDownload(spy) + autoUpdateManager.onDidCompleteDownload(spy) + autoUpdateManager.onUpdateNotAvailable(spy) + electronAutoUpdater.emit('checking-for-update') + electronAutoUpdater.emit('update-available') + electronAutoUpdater.emit('update-downloaded', null, null, {releaseVersion: '1.2.3'}) + electronAutoUpdater.emit('update-not-available') + + waitsFor(() => { + return doneIndicator.callCount === 1 + }) + + runs(() => { + expect(spy.callCount).toBe(0) + }) + }) + }) }) From c9293e7733502c0551438a41633031c902ab43c6 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:34:31 -0800 Subject: [PATCH 363/971] Pass the version info through the event --- spec/auto-update-manager-spec.js | 7 +++++-- src/auto-update-manager.js | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 6aa791373..e23c1911d 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -42,10 +42,13 @@ fdescribe('AutoUpdateManager (renderer)', () => { it('subscribes to "did-complete-downloading-update" event', () => { const spy = jasmine.createSpy('spy') autoUpdateManager.onDidCompleteDownload(spy) - electronAutoUpdater.emit('update-downloaded', null, null, {releaseVersion: '1.2.3'}) + electronAutoUpdater.emit('update-downloaded', null, null, '1.2.3') waitsFor(() => { return spy.callCount === 1 }) + runs(() => { + expect(spy.mostRecentCall.args[0].releaseVersion).toBe('1.2.3') + }) }) }) @@ -72,7 +75,7 @@ fdescribe('AutoUpdateManager (renderer)', () => { autoUpdateManager.onUpdateNotAvailable(spy) electronAutoUpdater.emit('checking-for-update') electronAutoUpdater.emit('update-available') - electronAutoUpdater.emit('update-downloaded', null, null, {releaseVersion: '1.2.3'}) + electronAutoUpdater.emit('update-downloaded', null, null, '1.2.3') electronAutoUpdater.emit('update-not-available') waitsFor(() => { diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 05de08a3a..f46197f8d 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -17,8 +17,8 @@ export default class AutoUpdateManager { updateEventEmitter.onDidBeginCheckingForUpdate(() => { this.emitter.emit('did-begin-checking-for-update') }), - updateEventEmitter.onDidCompleteDownloadingUpdate(() => { - this.emitter.emit('did-complete-downloading-update') + updateEventEmitter.onDidCompleteDownloadingUpdate((details) => { + this.emitter.emit('did-complete-downloading-update', details) }), updateEventEmitter.onUpdateNotAvailable(() => { this.emitter.emit('update-not-available') From fa0f6f35258b5cbb9fb347c2858ee0ab258d1df5 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:35:35 -0800 Subject: [PATCH 364/971] =?UTF-8?q?Use=20the=20renderer=20AutoUpdateManage?= =?UTF-8?q?r=20in=20atom-environment=E2=80=99s=20update=20events?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/atom-environment.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 481e762ca..f1fe1b459 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -893,7 +893,7 @@ class AtomEnvironment extends Model listenForUpdates: -> # listen for updates available locally (that have been successfully downloaded) - @disposables.add(@applicationDelegate.onDidCompleteDownloadingUpdate(@updateAvailable.bind(this))) + @disposables.add(@autoUpdater.onDidCompleteDownload(@updateAvailable.bind(this))) setBodyPlatformClass: -> @document.body.classList.add("platform-#{process.platform}") From 341abbfc261d2b9206cf2d85cdd7bf147d4760cd Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:35:50 -0800 Subject: [PATCH 365/971] Nof. Nope. --- spec/auto-update-manager-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index e23c1911d..ff2fc0682 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -4,7 +4,7 @@ import AutoUpdateManager from '../src/auto-update-manager' import {remote} from 'electron' const electronAutoUpdater = remote.require('electron').autoUpdater -fdescribe('AutoUpdateManager (renderer)', () => { +describe('AutoUpdateManager (renderer)', () => { let autoUpdateManager beforeEach(() => { From e9e372b69e42d0ab8659bc3e446e6df57ce987a0 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:48:15 -0800 Subject: [PATCH 366/971] Dispose emitter --- spec/auto-update-manager-spec.js | 2 +- src/auto-update-manager.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index ff2fc0682..f4e7622b2 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -68,11 +68,11 @@ describe('AutoUpdateManager (renderer)', () => { const spy = jasmine.createSpy('spy') const doneIndicator = jasmine.createSpy('spy') atom.applicationDelegate.onUpdateNotAvailable(doneIndicator) - autoUpdateManager.dispose() autoUpdateManager.onDidBeginCheckingForUpdate(spy) autoUpdateManager.onDidBeginDownload(spy) autoUpdateManager.onDidCompleteDownload(spy) autoUpdateManager.onUpdateNotAvailable(spy) + autoUpdateManager.dispose() electronAutoUpdater.emit('checking-for-update') electronAutoUpdater.emit('update-available') electronAutoUpdater.emit('update-downloaded', null, null, '1.2.3') diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index f46197f8d..681978cb6 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -28,6 +28,7 @@ export default class AutoUpdateManager { dispose () { this.subscriptions.dispose() + this.emitter.dispose() } onDidBeginCheckingForUpdate (callback) { From 4afe6df2c4edc8a4bfc8b5db07ee27a88f7df908 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:48:26 -0800 Subject: [PATCH 367/971] Reorder for consistency --- src/auto-update-manager.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 681978cb6..5f37196ed 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -11,12 +11,12 @@ export default class AutoUpdateManager { initialize (updateEventEmitter) { this.subscriptions.add( - updateEventEmitter.onDidBeginDownloadingUpdate(() => { - this.emitter.emit('did-begin-downloading-update') - }), updateEventEmitter.onDidBeginCheckingForUpdate(() => { this.emitter.emit('did-begin-checking-for-update') }), + updateEventEmitter.onDidBeginDownloadingUpdate(() => { + this.emitter.emit('did-begin-downloading-update') + }), updateEventEmitter.onDidCompleteDownloadingUpdate((details) => { this.emitter.emit('did-complete-downloading-update', details) }), From b60e1957a88c0a9c2585434f94cbeadc9a51f3b6 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:48:43 -0800 Subject: [PATCH 368/971] Return the disposables! --- src/auto-update-manager.js | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 5f37196ed..0bb793b44 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -32,33 +32,23 @@ export default class AutoUpdateManager { } onDidBeginCheckingForUpdate (callback) { - this.subscriptions.add( - this.emitter.on('did-begin-checking-for-update', callback) - ) + return this.emitter.on('did-begin-checking-for-update', callback) } onDidBeginDownload (callback) { - this.subscriptions.add( - this.emitter.on('did-begin-downloading-update', callback) - ) + return this.emitter.on('did-begin-downloading-update', callback) } onDidCompleteDownload (callback) { - this.subscriptions.add( - this.emitter.on('did-complete-downloading-update', callback) - ) + return this.emitter.on('did-complete-downloading-update', callback) } onUpdateAvailable (callback) { - this.subscriptions.add( - this.emitter.on('update-available', callback) - ) + return this.emitter.on('update-available', callback) } onUpdateNotAvailable (callback) { - this.subscriptions.add( - this.emitter.on('update-not-available', callback) - ) + return this.emitter.on('update-not-available', callback) } checkForUpdate () { From d3340d070f456c6e181da7e38624cc5af9d3f062 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 18:12:04 -0800 Subject: [PATCH 369/971] =?UTF-8?q?UpdateAvailable=20isn=E2=80=99t=20reall?= =?UTF-8?q?y=20a=20thing=20in=20this=20world?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auto-update-manager.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 0bb793b44..cc3035fdf 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -43,10 +43,6 @@ export default class AutoUpdateManager { return this.emitter.on('did-complete-downloading-update', callback) } - onUpdateAvailable (callback) { - return this.emitter.on('update-available', callback) - } - onUpdateNotAvailable (callback) { return this.emitter.on('update-not-available', callback) } From e477751c1986743882d551d54fa3d2b99bd189a0 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 25 Feb 2016 16:00:48 -0800 Subject: [PATCH 370/971] Make the autoupdater functions consistently names --- spec/auto-update-manager-spec.js | 12 ++++++------ src/auto-update-manager.js | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index f4e7622b2..499e2cb3b 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -27,10 +27,10 @@ describe('AutoUpdateManager (renderer)', () => { }) }) - describe('::onDidBeginDownload', () => { + describe('::onDidBeginDownloadingUpdate', () => { it('subscribes to "did-begin-downloading-update" event', () => { const spy = jasmine.createSpy('spy') - autoUpdateManager.onDidBeginDownload(spy) + autoUpdateManager.onDidBeginDownloadingUpdate(spy) electronAutoUpdater.emit('update-available') waitsFor(() => { return spy.callCount === 1 @@ -38,10 +38,10 @@ describe('AutoUpdateManager (renderer)', () => { }) }) - describe('::onDidCompleteDownload', () => { + describe('::onDidCompleteDownloadingUpdate', () => { it('subscribes to "did-complete-downloading-update" event', () => { const spy = jasmine.createSpy('spy') - autoUpdateManager.onDidCompleteDownload(spy) + autoUpdateManager.onDidCompleteDownloadingUpdate(spy) electronAutoUpdater.emit('update-downloaded', null, null, '1.2.3') waitsFor(() => { return spy.callCount === 1 @@ -69,8 +69,8 @@ describe('AutoUpdateManager (renderer)', () => { const doneIndicator = jasmine.createSpy('spy') atom.applicationDelegate.onUpdateNotAvailable(doneIndicator) autoUpdateManager.onDidBeginCheckingForUpdate(spy) - autoUpdateManager.onDidBeginDownload(spy) - autoUpdateManager.onDidCompleteDownload(spy) + autoUpdateManager.onDidBeginDownloadingUpdate(spy) + autoUpdateManager.onDidCompleteDownloadingUpdate(spy) autoUpdateManager.onUpdateNotAvailable(spy) autoUpdateManager.dispose() electronAutoUpdater.emit('checking-for-update') diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index cc3035fdf..e7852f2d9 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -35,11 +35,11 @@ export default class AutoUpdateManager { return this.emitter.on('did-begin-checking-for-update', callback) } - onDidBeginDownload (callback) { + onDidBeginDownloadingUpdate (callback) { return this.emitter.on('did-begin-downloading-update', callback) } - onDidCompleteDownload (callback) { + onDidCompleteDownloadingUpdate (callback) { return this.emitter.on('did-complete-downloading-update', callback) } From c19296efb753167619a66532ecb6481339415b9d Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 25 Feb 2016 16:01:23 -0800 Subject: [PATCH 371/971] Add isEnabled function to AutoUpdateManager --- spec/auto-update-manager-spec.js | 32 ++++++++++++++++++++++++++++++++ src/auto-update-manager.js | 27 +++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 499e2cb3b..6e3c037ef 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -63,6 +63,38 @@ describe('AutoUpdateManager (renderer)', () => { }) }) + describe('::isEnabled', () => { + let platform, releaseChannel + it('returns true on OS X and Windows, when in stable', () => { + spyOn(autoUpdateManager, 'getPlatform').andCallFake(() => platform) + spyOn(autoUpdateManager, 'getReleaseChannel').andCallFake(() => releaseChannel) + + platform = 'win32' + releaseChannel = 'stable' + expect(autoUpdateManager.isEnabled()).toBe(true) + + platform = 'win32' + releaseChannel = 'dev' + expect(autoUpdateManager.isEnabled()).toBe(false) + + platform = 'darwin' + releaseChannel = 'stable' + expect(autoUpdateManager.isEnabled()).toBe(true) + + platform = 'darwin' + releaseChannel = 'dev' + expect(autoUpdateManager.isEnabled()).toBe(false) + + platform = 'linux' + releaseChannel = 'stable' + expect(autoUpdateManager.isEnabled()).toBe(false) + + platform = 'linux' + releaseChannel = 'dev' + expect(autoUpdateManager.isEnabled()).toBe(false) + }) + }) + describe('::dispose', () => { it('subscribes to "update-not-available" event', () => { const spy = jasmine.createSpy('spy') diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index e7852f2d9..e99ce83d9 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -31,6 +31,18 @@ export default class AutoUpdateManager { this.emitter.dispose() } + checkForUpdate () { + ipcRenderer.send('check-for-update') + } + + quitAndInstallUpdate () { + ipcRenderer.send('install-update') + } + + isEnabled () { + return this.getReleaseChannel() == 'stable' && (this.getPlatform() === 'darwin' || this.getPlatform() === 'win32') + } + onDidBeginCheckingForUpdate (callback) { return this.emitter.on('did-begin-checking-for-update', callback) } @@ -47,7 +59,18 @@ export default class AutoUpdateManager { return this.emitter.on('update-not-available', callback) } - checkForUpdate () { - ipcRenderer.send('check-for-update') + getPlatform () { + return process.platform + } + + // TODO: We should move this into atom env or something. + getReleaseChannel () { + let version = atom.getVersion() + if (version.indexOf('beta') > -1) { + return 'beta' + } else if (version.indexOf('dev') > -1) { + return 'dev' + } + return 'stable' } } From 1cd530cdf06e4865a91f159fffc0e687ff78610c Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 25 Feb 2016 16:01:44 -0800 Subject: [PATCH 372/971] Add some todo code --- src/auto-update-manager.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index e99ce83d9..06b437c3b 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -55,6 +55,12 @@ export default class AutoUpdateManager { return this.emitter.on('did-complete-downloading-update', callback) } + // TODO: When https://github.com/atom/electron/issues/4587 is closed, we can + // add an update-available event. + // onUpdateAvailable (callback) { + // return this.emitter.on('update-available', callback) + // } + onUpdateNotAvailable (callback) { return this.emitter.on('update-not-available', callback) } From 418b1bd8f1bb95c79620d9847518aa33abdb4142 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 25 Feb 2016 16:33:39 -0800 Subject: [PATCH 373/971] Get rid of the initialize method --- spec/auto-update-manager-spec.js | 5 +++-- src/atom-environment.coffee | 3 +-- src/auto-update-manager.js | 12 +++++------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 6e3c037ef..2b51ed658 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -8,8 +8,9 @@ describe('AutoUpdateManager (renderer)', () => { let autoUpdateManager beforeEach(() => { - autoUpdateManager = new AutoUpdateManager() - autoUpdateManager.initialize(atom.applicationDelegate) + autoUpdateManager = new AutoUpdateManager({ + applicationDelegate: atom.applicationDelegate + }) }) afterEach(() => { diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index f1fe1b459..04074cfcb 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -192,8 +192,7 @@ class AtomEnvironment extends Model @themes.workspace = @workspace @textEditors = new TextEditorRegistry - @autoUpdater = new AutoUpdateManager - @autoUpdater.initialize(@applicationDelegate) + @autoUpdater = new AutoUpdateManager({@applicationDelegate}) @config.load() diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 06b437c3b..d811996c1 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -4,23 +4,21 @@ import {Emitter, CompositeDisposable} from 'event-kit' import {ipcRenderer} from 'electron' export default class AutoUpdateManager { - constructor () { + constructor ({applicationDelegate}) { this.subscriptions = new CompositeDisposable() this.emitter = new Emitter() - } - initialize (updateEventEmitter) { this.subscriptions.add( - updateEventEmitter.onDidBeginCheckingForUpdate(() => { + applicationDelegate.onDidBeginCheckingForUpdate(() => { this.emitter.emit('did-begin-checking-for-update') }), - updateEventEmitter.onDidBeginDownloadingUpdate(() => { + applicationDelegate.onDidBeginDownloadingUpdate(() => { this.emitter.emit('did-begin-downloading-update') }), - updateEventEmitter.onDidCompleteDownloadingUpdate((details) => { + applicationDelegate.onDidCompleteDownloadingUpdate((details) => { this.emitter.emit('did-complete-downloading-update', details) }), - updateEventEmitter.onUpdateNotAvailable(() => { + applicationDelegate.onUpdateNotAvailable(() => { this.emitter.emit('update-not-available') }) ) From bc138f727e1af484f0110661eafafae6f4861e95 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 25 Feb 2016 16:33:51 -0800 Subject: [PATCH 374/971] Fix usage of the onDidCompleteDownload() method --- src/atom-environment.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 04074cfcb..2122abbbe 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -892,7 +892,7 @@ class AtomEnvironment extends Model listenForUpdates: -> # listen for updates available locally (that have been successfully downloaded) - @disposables.add(@autoUpdater.onDidCompleteDownload(@updateAvailable.bind(this))) + @disposables.add(@autoUpdater.onDidCompleteDownloadingUpdate(@updateAvailable.bind(this))) setBodyPlatformClass: -> @document.body.classList.add("platform-#{process.platform}") From 68951494d2e6c4becfa44be38d90a49f79f7c3b9 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 25 Feb 2016 17:32:02 -0800 Subject: [PATCH 375/971] isEnabled -> platformSupportsUpdates --- spec/auto-update-manager-spec.js | 14 +++++++------- src/auto-update-manager.js | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 2b51ed658..e12f85c5b 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -64,7 +64,7 @@ describe('AutoUpdateManager (renderer)', () => { }) }) - describe('::isEnabled', () => { + describe('::platformSupportsUpdates', () => { let platform, releaseChannel it('returns true on OS X and Windows, when in stable', () => { spyOn(autoUpdateManager, 'getPlatform').andCallFake(() => platform) @@ -72,27 +72,27 @@ describe('AutoUpdateManager (renderer)', () => { platform = 'win32' releaseChannel = 'stable' - expect(autoUpdateManager.isEnabled()).toBe(true) + expect(autoUpdateManager.platformSupportsUpdates()).toBe(true) platform = 'win32' releaseChannel = 'dev' - expect(autoUpdateManager.isEnabled()).toBe(false) + expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) platform = 'darwin' releaseChannel = 'stable' - expect(autoUpdateManager.isEnabled()).toBe(true) + expect(autoUpdateManager.platformSupportsUpdates()).toBe(true) platform = 'darwin' releaseChannel = 'dev' - expect(autoUpdateManager.isEnabled()).toBe(false) + expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) platform = 'linux' releaseChannel = 'stable' - expect(autoUpdateManager.isEnabled()).toBe(false) + expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) platform = 'linux' releaseChannel = 'dev' - expect(autoUpdateManager.isEnabled()).toBe(false) + expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) }) }) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index d811996c1..497097864 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -37,7 +37,7 @@ export default class AutoUpdateManager { ipcRenderer.send('install-update') } - isEnabled () { + platformSupportsUpdates () { return this.getReleaseChannel() == 'stable' && (this.getPlatform() === 'darwin' || this.getPlatform() === 'win32') } From 7a1ad8263aa63438df16247663cf99e5073d4d72 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 26 Feb 2016 16:37:59 -0800 Subject: [PATCH 376/971] Rename `quitAndInstallUpdate` to `restartAndInstallUpdate` --- src/auto-update-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 497097864..eae979d9d 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -33,7 +33,7 @@ export default class AutoUpdateManager { ipcRenderer.send('check-for-update') } - quitAndInstallUpdate () { + restartAndInstallUpdate () { ipcRenderer.send('install-update') } From 3a32b30d5a98f704a310c96fa1d7bc8309d7d412 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 26 Feb 2016 16:38:56 -0800 Subject: [PATCH 377/971] Add mechanism to get the AutoUpdateManager's state --- src/auto-update-manager.js | 4 ++++ src/browser/atom-application.coffee | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index eae979d9d..14ec44c29 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -37,6 +37,10 @@ export default class AutoUpdateManager { ipcRenderer.send('install-update') } + getState () { + return ipcRenderer.sendSync('get-auto-update-manager-state') + } + platformSupportsUpdates () { return this.getReleaseChannel() == 'stable' && (this.getPlatform() === 'darwin' || this.getPlatform() === 'win32') } diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index fad69b967..fdfea5474 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -307,6 +307,9 @@ class AtomApplication ipcMain.on 'check-for-update', => @autoUpdateManager.check() + ipcMain.on 'get-auto-update-manager-state', (event) => + event.returnValue = @autoUpdateManager.getState() + ipcMain.on 'execute-javascript-in-dev-tools', (event, code) -> event.sender.devToolsWebContents?.executeJavaScript(code) From 8307fb84265509bcaaed99f9c6b8b452f9a56bfb Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 26 Feb 2016 16:39:42 -0800 Subject: [PATCH 378/971] Change logic for `platformSupportsUpdates --- src/auto-update-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 14ec44c29..c1446ed5d 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -42,7 +42,7 @@ export default class AutoUpdateManager { } platformSupportsUpdates () { - return this.getReleaseChannel() == 'stable' && (this.getPlatform() === 'darwin' || this.getPlatform() === 'win32') + return this.getReleaseChannel() !== 'dev' && this.getState() !== 'unsupported' } onDidBeginCheckingForUpdate (callback) { From d831ec73ed6adeccc1b355f96167daf1630031af Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 29 Feb 2016 16:42:25 -0800 Subject: [PATCH 379/971] Fix platformSupportsUpdates() specs --- spec/auto-update-manager-spec.js | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index e12f85c5b..2b65d09e5 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -65,32 +65,24 @@ describe('AutoUpdateManager (renderer)', () => { }) describe('::platformSupportsUpdates', () => { - let platform, releaseChannel + let state, releaseChannel it('returns true on OS X and Windows, when in stable', () => { - spyOn(autoUpdateManager, 'getPlatform').andCallFake(() => platform) + spyOn(autoUpdateManager, 'getState').andCallFake(() => state) spyOn(autoUpdateManager, 'getReleaseChannel').andCallFake(() => releaseChannel) - platform = 'win32' + state = 'idle' releaseChannel = 'stable' expect(autoUpdateManager.platformSupportsUpdates()).toBe(true) - platform = 'win32' + state = 'idle' releaseChannel = 'dev' expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) - platform = 'darwin' - releaseChannel = 'stable' - expect(autoUpdateManager.platformSupportsUpdates()).toBe(true) - - platform = 'darwin' - releaseChannel = 'dev' - expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) - - platform = 'linux' + state = 'unsupported' releaseChannel = 'stable' expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) - platform = 'linux' + state = 'unsupported' releaseChannel = 'dev' expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) }) From 8eb1326d4c7170768cb0d71908803220c28aab0f Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 29 Feb 2016 16:42:37 -0800 Subject: [PATCH 380/971] Add some TODOs --- src/application-delegate.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index a853cd8ea..dff496fdd 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -174,6 +174,9 @@ class ApplicationDelegate onUpdateAvailable: (callback) -> outerCallback = (event, message, detail) -> + # TODO: Yes, this is strange that `onUpdateAvailable` is listening for + # `did-begin-downloading-update`. We currently have no mechanism to know + # if there is an update, so begin of downloading is a good proxy. callback(detail) if message is 'did-begin-downloading-update' ipcRenderer.on('message', outerCallback) @@ -193,6 +196,7 @@ class ApplicationDelegate onDidCompleteDownloadingUpdate: (callback) -> outerCallback = (event, message, detail) -> + # TODO: We could rename this event to `did-complete-downloading-update` callback(detail) if message is 'update-available' ipcRenderer.on('message', outerCallback) From 75aca7ee0c9e1a61349114f5d3d78b676d58b2b2 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 29 Feb 2016 16:47:53 -0800 Subject: [PATCH 381/971] Add a deprecation TODO --- src/atom-environment.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 2122abbbe..df9130453 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -884,6 +884,7 @@ class AtomEnvironment extends Model detail: error.message dismissable: true + # TODO: We should deprecate the update events here, and use `atom.autoUpdater` instead onUpdateAvailable: (callback) -> @emitter.on 'update-available', callback From 58bf090724bd9b0f5917d9ce1dde2746c53e38d4 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 14:41:38 -0800 Subject: [PATCH 382/971] Rename `dispose` -> `destroy` --- spec/auto-update-manager-spec.js | 8 ++++---- src/atom-environment.coffee | 1 + src/auto-update-manager.js | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 2b65d09e5..9c82400ef 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -14,7 +14,7 @@ describe('AutoUpdateManager (renderer)', () => { }) afterEach(() => { - autoUpdateManager.dispose() + autoUpdateManager.destroy() }) describe('::onDidBeginCheckingForUpdate', () => { @@ -88,8 +88,8 @@ describe('AutoUpdateManager (renderer)', () => { }) }) - describe('::dispose', () => { - it('subscribes to "update-not-available" event', () => { + describe('::destroy', () => { + it('unsubscribes from all events', () => { const spy = jasmine.createSpy('spy') const doneIndicator = jasmine.createSpy('spy') atom.applicationDelegate.onUpdateNotAvailable(doneIndicator) @@ -97,7 +97,7 @@ describe('AutoUpdateManager (renderer)', () => { autoUpdateManager.onDidBeginDownloadingUpdate(spy) autoUpdateManager.onDidCompleteDownloadingUpdate(spy) autoUpdateManager.onUpdateNotAvailable(spy) - autoUpdateManager.dispose() + autoUpdateManager.destroy() electronAutoUpdater.emit('checking-for-update') electronAutoUpdater.emit('update-available') electronAutoUpdater.emit('update-downloaded', null, null, '1.2.3') diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index df9130453..4fecd01aa 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -338,6 +338,7 @@ class AtomEnvironment extends Model @commands.clear() @stylesElement.remove() @config.unobserveUserConfig() + @autoUpdater.destroy() @uninstallWindowEventHandler() diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index c1446ed5d..3b2d27439 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -24,7 +24,7 @@ export default class AutoUpdateManager { ) } - dispose () { + destroy () { this.subscriptions.dispose() this.emitter.dispose() } From 81c07a2af9322e32b758c088663887b233db1808 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 14:42:53 -0800 Subject: [PATCH 383/971] :art: Cleanup spec desc --- spec/auto-update-manager-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 9c82400ef..7436be291 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -66,7 +66,7 @@ describe('AutoUpdateManager (renderer)', () => { describe('::platformSupportsUpdates', () => { let state, releaseChannel - it('returns true on OS X and Windows, when in stable', () => { + it('returns true on OS X and Windows when in stable', () => { spyOn(autoUpdateManager, 'getState').andCallFake(() => state) spyOn(autoUpdateManager, 'getReleaseChannel').andCallFake(() => releaseChannel) From 02368a9fc680f658dacbd0b06e1531959dd6c340 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 14:51:45 -0800 Subject: [PATCH 384/971] Add `atom.getReleaseChannel` --- spec/atom-environment-spec.coffee | 15 +++++++++++++++ src/atom-environment.coffee | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 1f8eb08e7..6685d4060 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -328,3 +328,18 @@ describe "AtomEnvironment", -> runs -> {releaseVersion} = updateAvailableHandler.mostRecentCall.args[0] expect(releaseVersion).toBe 'version' + + describe "::getReleaseChannel()", -> + [version] = [] + beforeEach -> + spyOn(atom, 'getVersion').andCallFake -> version + + it "returns the correct channel based on the version number", -> + version = '1.5.6' + expect(atom.getReleaseChannel()).toBe 'stable' + + version = '1.5.0-beta10' + expect(atom.getReleaseChannel()).toBe 'beta' + + version = '1.7.0-dev-5340c91' + expect(atom.getReleaseChannel()).toBe 'dev' diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 4fecd01aa..14d3f78c9 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -417,6 +417,16 @@ class AtomEnvironment extends Model getVersion: -> @appVersion ?= @getLoadSettings().appVersion + # Returns the release channel as a {String}. Will return one of `'dev', 'beta', 'stable'` + getReleaseChannel: -> + version = @getVersion() + if version.indexOf('beta') > -1 + 'beta' + else if version.indexOf('dev') > -1 + 'dev' + else + 'stable' + # Public: Returns a {Boolean} that is `true` if the current version is an official release. isReleasedVersion: -> not /\w{7}/.test(@getVersion()) # Check if the release is a 7-character SHA prefix From 0b36d85bdac5679097f48cefa3f0bd44fe367b4c Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 14:51:57 -0800 Subject: [PATCH 385/971] :art: Remove whitespace --- src/atom-environment.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 14d3f78c9..06296fbaf 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -846,7 +846,6 @@ class AtomEnvironment extends Model @applicationDelegate.setTemporaryWindowState(state) savePromise.catch(reject).then(resolve) - loadState: -> if @enablePersistence if stateKey = @getStateKey(@getLoadSettings().initialPaths) From 4d11ff25d0953c1debdacaef1b05f9a736b164f4 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 14:59:02 -0800 Subject: [PATCH 386/971] Use atom.getReleaseChannel() --- spec/auto-update-manager-spec.js | 2 +- src/auto-update-manager.js | 13 +------------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 7436be291..6f7dbbb1a 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -68,7 +68,7 @@ describe('AutoUpdateManager (renderer)', () => { let state, releaseChannel it('returns true on OS X and Windows when in stable', () => { spyOn(autoUpdateManager, 'getState').andCallFake(() => state) - spyOn(autoUpdateManager, 'getReleaseChannel').andCallFake(() => releaseChannel) + spyOn(atom, 'getReleaseChannel').andCallFake(() => releaseChannel) state = 'idle' releaseChannel = 'stable' diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 3b2d27439..4a184faf8 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -42,7 +42,7 @@ export default class AutoUpdateManager { } platformSupportsUpdates () { - return this.getReleaseChannel() !== 'dev' && this.getState() !== 'unsupported' + return atom.getReleaseChannel() !== 'dev' && this.getState() !== 'unsupported' } onDidBeginCheckingForUpdate (callback) { @@ -70,15 +70,4 @@ export default class AutoUpdateManager { getPlatform () { return process.platform } - - // TODO: We should move this into atom env or something. - getReleaseChannel () { - let version = atom.getVersion() - if (version.indexOf('beta') > -1) { - return 'beta' - } else if (version.indexOf('dev') > -1) { - return 'dev' - } - return 'stable' - } } From 492e89c8fff3d9fa96634a8510eab1f3222dfe42 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 14:59:41 -0800 Subject: [PATCH 387/971] Move raw ipc calls into the applicationDelegate --- src/application-delegate.coffee | 9 +++++++++ src/auto-update-manager.js | 7 ++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index dff496fdd..3aff9e457 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -235,3 +235,12 @@ class ApplicationDelegate disablePinchToZoom: -> webFrame.setZoomLevelLimits(1, 1) + + checkForUpdate: -> + ipcRenderer.send('check-for-update') + + restartAndInstallUpdate: -> + ipcRenderer.send('install-update') + + getAutoUpdateManagerState: -> + ipcRenderer.sendSync('get-auto-update-manager-state') diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 4a184faf8..01ead9bbe 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -5,6 +5,7 @@ import {ipcRenderer} from 'electron' export default class AutoUpdateManager { constructor ({applicationDelegate}) { + this.applicationDelegate = applicationDelegate this.subscriptions = new CompositeDisposable() this.emitter = new Emitter() @@ -30,15 +31,15 @@ export default class AutoUpdateManager { } checkForUpdate () { - ipcRenderer.send('check-for-update') + this.applicationDelegate.checkForUpdate() } restartAndInstallUpdate () { - ipcRenderer.send('install-update') + this.applicationDelegate.restartAndInstallUpdate() } getState () { - return ipcRenderer.sendSync('get-auto-update-manager-state') + return this.applicationDelegate.getAutoUpdateManagerState() } platformSupportsUpdates () { From f884c987e29acef83a40164b681950a7fd8bf047 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 15:12:38 -0800 Subject: [PATCH 388/971] Remove unnecessary import --- src/auto-update-manager.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 01ead9bbe..62cc03f85 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -1,7 +1,6 @@ 'use babel' import {Emitter, CompositeDisposable} from 'event-kit' -import {ipcRenderer} from 'electron' export default class AutoUpdateManager { constructor ({applicationDelegate}) { From f26c0c0b15231f853f82a8fc6c7e527a27154d89 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 1 Mar 2016 18:03:50 -0700 Subject: [PATCH 389/971] Bump timeouts as a possible workaround for flaky specs --- spec/async-spec-helpers.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/async-spec-helpers.coffee b/spec/async-spec-helpers.coffee index 5f8e03ca3..6ed8a5a2b 100644 --- a/spec/async-spec-helpers.coffee +++ b/spec/async-spec-helpers.coffee @@ -19,7 +19,9 @@ exports.afterEach = (fn) -> waitsForPromise = (fn) -> promise = fn() - waitsFor 'spec promise to resolve', 30000, (done) -> + # This timeout is 3 minutes. We need to bump it back down once we fix backgrounding + # of the renderer process on CI. See https://github.com/atom/electron/issues/4317 + waitsFor 'spec promise to resolve', 3 * 60 * 1000, (done) -> promise.then( done, (error) -> From 47a348f557bce3c6518e572d9ad45f181c3ffde9 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 1 Mar 2016 18:22:43 -0700 Subject: [PATCH 390/971] Filter out non-directory project paths when deserializing Project Closes #10628 --- spec/project-spec.coffee | 8 ++++++++ src/project.coffee | 1 + 2 files changed, 9 insertions(+) diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 9d42f9a7e..70022426a 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -21,6 +21,14 @@ describe "Project", -> afterEach -> deserializedProject?.destroy() + it "does not deserialize paths to non directories", -> + deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) + state = atom.project.serialize() + state.paths.push('/directory/that/does/not/exist') + state.paths.push(path.join(__dirname, 'fixtures', 'sample.js')) + deserializedProject.deserialize(state, atom.deserializers) + expect(deserializedProject.getPaths()).toEqual(atom.project.getPaths()) + it "does not include unretained buffers in the serialized state", -> waitsForPromise -> atom.project.bufferForPath('a') diff --git a/src/project.coffee b/src/project.coffee index 008d81e3e..24a634cb1 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -56,6 +56,7 @@ class Project extends Model deserialize: (state, deserializerManager) -> state.paths = [state.path] if state.path? # backward compatibility + state.paths = state.paths.filter (directoryPath) -> fs.isDirectorySync(directoryPath) @buffers = _.compact state.buffers.map (bufferState) -> # Check that buffer's file path is accessible From ea2d72f314e24f1aee8c336064986520e1877895 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 18:13:53 -0800 Subject: [PATCH 391/971] :arrow_up: about@1.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7ff3df275..005b066ee 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "one-light-syntax": "1.2.0", "solarized-dark-syntax": "1.0.0", "solarized-light-syntax": "1.0.0", - "about": "1.3.1", + "about": "1.4.0", "archive-view": "0.61.1", "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.0", From f6908f80cdfa8699c98d808d8c22fc4e2dd4b077 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 2 Mar 2016 15:38:29 -0700 Subject: [PATCH 392/971] Rename watchProjectPath to watchProjectPaths --- src/atom-environment.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 06296fbaf..661201125 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -676,7 +676,7 @@ class AtomEnvironment extends Model @document.body.appendChild(@views.getView(@workspace)) @backgroundStylesheet?.remove() - @watchProjectPath() + @watchProjectPaths() @packages.activate() @keymaps.loadUserKeymap() @@ -808,7 +808,7 @@ class AtomEnvironment extends Model @themes.load() # Notify the browser project of the window's current project path - watchProjectPath: -> + watchProjectPaths: -> @disposables.add @project.onDidChangePaths => @applicationDelegate.setRepresentedDirectoryPaths(@project.getPaths()) From 51a2609bc90a937507b7e226dad67a3a7ea6b8c6 Mon Sep 17 00:00:00 2001 From: Jason Koenig Date: Sat, 30 Jan 2016 23:28:43 -0800 Subject: [PATCH 393/971] Properly restore initialPaths so project paths are not loaded as files If project folders no longer exist or are not mounted, then Atom would open them as buffers, which were empty because the corresponding file did not exist. This threads the right state through the startup sequence so that "initialPaths", which actually are project roots, is restored correctly. --- src/browser/atom-application.coffee | 16 ++++++++-------- src/browser/atom-window.coffee | 13 ++++--------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index fdfea5474..b2d66af87 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -85,16 +85,16 @@ class AtomApplication else @loadState(options) or @openPath(options) - openWithOptions: ({pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState, addToLastWindow}) -> + openWithOptions: ({initialPaths, pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState, addToLastWindow}) -> if test @runTests({headless: true, devMode, @resourcePath, executedFrom, pathsToOpen, logFile, timeout}) else if pathsToOpen.length > 0 - @openPaths({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow}) + @openPaths({initialPaths, pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow}) else if urlsToOpen.length > 0 @openUrl({urlToOpen, devMode, safeMode}) for urlToOpen in urlsToOpen else # Always open a editor window if this is the first instance of Atom. - @openPath({pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow}) + @openPath({initialPaths, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow}) # Public: Removes the {AtomWindow} from the global window list. removeWindow: (window) -> @@ -418,8 +418,8 @@ class AtomApplication # :profileStartup - Boolean to control creating a profile of the startup time. # :window - {AtomWindow} to open file paths in. # :addToLastWindow - Boolean of whether this should be opened in last focused window. - openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow} = {}) -> - @openPaths({pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow}) + openPath: ({initialPaths, pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow} = {}) -> + @openPaths({initialPaths, pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow}) # Public: Opens multiple paths, in existing windows if possible. # @@ -432,7 +432,7 @@ class AtomApplication # :windowDimensions - Object with height and width keys. # :window - {AtomWindow} to open file paths in. # :addToLastWindow - Boolean of whether this should be opened in last focused window. - openPaths: ({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearWindowState, addToLastWindow}={}) -> + openPaths: ({initialPaths, pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearWindowState, addToLastWindow}={}) -> devMode = Boolean(devMode) safeMode = Boolean(safeMode) clearWindowState = Boolean(clearWindowState) @@ -469,7 +469,7 @@ class AtomApplication windowInitializationScript ?= require.resolve('../initialize-application-window') resourcePath ?= @resourcePath windowDimensions ?= @getDimensionsForNewWindow() - openedWindow = new AtomWindow({locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup, clearWindowState}) + openedWindow = new AtomWindow({initialPaths, locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup, clearWindowState}) if pidToKillWhenClosed? @pidsToOpenWindows[pidToKillWhenClosed] = openedWindow @@ -512,7 +512,7 @@ class AtomApplication if (states = @storageFolder.load('application.json'))?.length > 0 for state in states @openWithOptions(_.extend(options, { - pathsToOpen: state.initialPaths + initialPaths: state.initialPaths urlsToOpen: [] devMode: @devMode safeMode: @safeMode diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 634242e0d..24730c944 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -17,7 +17,7 @@ class AtomWindow isSpec: null constructor: (settings={}) -> - {@resourcePath, pathToOpen, locationsToOpen, @isSpec, @headless, @safeMode, @devMode} = settings + {@resourcePath, initialPaths, pathToOpen, locationsToOpen, @isSpec, @headless, @safeMode, @devMode} = settings locationsToOpen ?= [{pathToOpen}] if pathToOpen locationsToOpen ?= [] @@ -47,20 +47,15 @@ class AtomWindow loadSettings.safeMode ?= false loadSettings.atomHome = process.env.ATOM_HOME loadSettings.clearWindowState ?= false + loadSettings.initialPaths ?= [] + loadSettings.initialPaths.sort() # Only send to the first non-spec window created if @constructor.includeShellLoadTime and not @isSpec @constructor.includeShellLoadTime = false loadSettings.shellLoadTime ?= Date.now() - global.shellStartTime - loadSettings.initialPaths = - for {pathToOpen} in locationsToOpen when pathToOpen - if fs.statSyncNoException(pathToOpen).isFile?() - path.dirname(pathToOpen) - else - pathToOpen - - loadSettings.initialPaths.sort() + @browserWindow.loadSettings = loadSettings @browserWindow.once 'window:loaded', => @emit 'window:loaded' From 64ed1a0e43aa30f03504755739b8c10d6f6fbbc8 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 2 Mar 2016 17:00:27 -0700 Subject: [PATCH 394/971] Populate initialPaths based on pathsToOpen if not specified This ensures that we deserialize projects correctly when specifying command line arguments. --- src/browser/atom-window.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 24730c944..33c64da7d 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -47,7 +47,13 @@ class AtomWindow loadSettings.safeMode ?= false loadSettings.atomHome = process.env.ATOM_HOME loadSettings.clearWindowState ?= false - loadSettings.initialPaths ?= [] + loadSettings.initialPaths ?= + for {pathToOpen} in locationsToOpen when pathToOpen + if fs.statSyncNoException(pathToOpen).isFile?() + path.dirname(pathToOpen) + else + pathToOpen + loadSettings.initialPaths.sort() # Only send to the first non-spec window created From 2bdf4be904ceecbb974d13930285fa4eb6a87db4 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Wed, 2 Mar 2016 16:56:06 -0800 Subject: [PATCH 395/971] :arrow_up: atom-package-manager Signed-off-by: Katrina Uychaco --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index 2e6b0b8ea..4b599bc39 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "1.6.0" + "atom-package-manager": "1.7.1" } } From db20cecfc08b262aad2ca01e98d7e5943007d2ec Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 3 Mar 2016 09:59:45 -0500 Subject: [PATCH 396/971] s/ignoreInvisibles/showInvisibles --- spec/text-editor-spec.coffee | 4 ++-- src/text-editor.coffee | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index c75084fd5..637244964 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5829,11 +5829,11 @@ describe "TextEditor", -> rangeIsReversed: false } - describe "when the editor is constructed with the ignoreInvisibles option set to true", -> + describe "when the editor is constructed with the showInvisibles option set to false", -> beforeEach -> atom.workspace.destroyActivePane() waitsForPromise -> - atom.workspace.open('sample.js', ignoreInvisibles: true).then (o) -> editor = o + atom.workspace.open('sample.js', showInvisibles: false).then (o) -> editor = o it "ignores invisibles even if editor.showInvisibles is true", -> atom.config.set('editor.showInvisibles', true) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 6ce96088b..6eeb2cff5 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -100,7 +100,7 @@ class TextEditor extends Model softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, grammarName, ignoreInvisibles, @autoHeight, @ignoreScrollPastEnd + @project, @assert, @applicationDelegate, grammarName, showInvisibles, @autoHeight, @ignoreScrollPastEnd } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -123,9 +123,11 @@ class TextEditor extends Model @ignoreScrollPastEnd ?= false @hasTerminatedPendingState = false + showInvisibles ?= true + buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ - buffer, tabLength, softWrapped, ignoreInvisibles: @mini or ignoreInvisibles, largeFileMode, + buffer, tabLength, softWrapped, ignoreInvisibles: @mini or !showInvisibles, largeFileMode, @config, @assert, @grammarRegistry, @packageManager }) @buffer = @displayBuffer.buffer From 98c8a08ac3837dc59c00e77a913af5f1358f6a14 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 3 Mar 2016 10:06:15 -0500 Subject: [PATCH 397/971] s/ignoreScrollPastEnd/scrollPastEnd --- spec/text-editor-presenter-spec.coffee | 4 ++-- src/text-editor-component.coffee | 4 ++-- src/text-editor-element.coffee | 6 +++--- src/text-editor-presenter.coffee | 6 ++++-- src/text-editor.coffee | 8 ++++---- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 62d1e4747..f8117af09 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -647,8 +647,8 @@ describe "TextEditorPresenter", -> expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight - it "doesn't add the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true but ignoreScrollPastEnd is true", -> - presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10, ignoreScrollPastEnd: true) + it "doesn't add the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true but the presenter is created with scrollPastEnd as false", -> + presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10, scrollPastEnd: false) expectStateUpdate presenter, -> presenter.setScrollTop(300) expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 5a4097fc5..9b091100d 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -43,7 +43,7 @@ class TextEditorComponent @assert domNode?, "TextEditorComponent::domNode was set to null." @domNodeValue = domNode - constructor: ({@editor, @hostElement, @rootElement, @stylesElement, @useShadowDOM, tileSize, @views, @themes, @config, @workspace, @assert, @grammars, ignoreScrollPastEnd}) -> + constructor: ({@editor, @hostElement, @rootElement, @stylesElement, @useShadowDOM, tileSize, @views, @themes, @config, @workspace, @assert, @grammars, scrollPastEnd}) -> @tileSize = tileSize if tileSize? @disposables = new CompositeDisposable @@ -61,7 +61,7 @@ class TextEditorComponent stoppedScrollingDelay: 200 config: @config lineTopIndex: lineTopIndex - ignoreScrollPastEnd: ignoreScrollPastEnd + scrollPastEnd: scrollPastEnd @presenter.onDidUpdateState(@requestUpdate) diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index 02f688e2c..df13f2a15 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -17,7 +17,7 @@ class TextEditorElement extends HTMLElement focusOnAttach: false hasTiledRendering: true logicalDisplayBuffer: true - ignoreScrollPastEnd: false + scrollPastEnd: true createdCallback: -> # Use globals when the following instance variables aren't set. @@ -90,7 +90,7 @@ class TextEditorElement extends HTMLElement @subscriptions.add @component.onDidChangeScrollLeft => @emitter.emit("did-change-scroll-left", arguments...) - initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}, @autoHeight = true, @ignoreScrollPastEnd = false) -> + initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}, @autoHeight = true, @scrollPastEnd = true) -> throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @views? throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @config? throw new Error("Must pass a themes parameter when initializing TextEditorElements") unless @themes? @@ -147,7 +147,7 @@ class TextEditorElement extends HTMLElement workspace: @workspace assert: @assert grammars: @grammars - ignoreScrollPastEnd: @ignoreScrollPastEnd + scrollPastEnd: @scrollPastEnd ) @rootElement.appendChild(@component.getDomNode()) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 0db175c2b..1fa662c59 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -13,7 +13,7 @@ class TextEditorPresenter minimumReflowInterval: 200 constructor: (params) -> - {@model, @config, @lineTopIndex, @ignoreScrollPastEnd} = params + {@model, @config, @lineTopIndex, scrollPastEnd} = params {@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @tileSize} = params {@contentFrameWidth} = params @@ -42,6 +42,8 @@ class TextEditorPresenter @startReflowing() if @continuousReflow @updating = false + @scrollPastEndOverride = scrollPastEnd || true + setLinesYardstick: (@linesYardstick) -> getLinesYardstick: -> @linesYardstick @@ -661,7 +663,7 @@ class TextEditorPresenter return unless @contentHeight? and @clientHeight? contentHeight = @contentHeight - if @scrollPastEnd and not @ignoreScrollPastEnd + if @scrollPastEnd and @scrollPastEndOverride extraScrollHeight = @clientHeight - (@lineHeight * 3) contentHeight += extraScrollHeight if extraScrollHeight > 0 scrollHeight = Math.max(contentHeight, @height) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 6eeb2cff5..54ea283e9 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -100,7 +100,7 @@ class TextEditor extends Model softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, grammarName, showInvisibles, @autoHeight, @ignoreScrollPastEnd + @project, @assert, @applicationDelegate, grammarName, showInvisibles, @autoHeight, @scrollPastEnd } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -120,7 +120,7 @@ class TextEditor extends Model @cursorsByMarkerId = new Map @selections = [] @autoHeight ?= true - @ignoreScrollPastEnd ?= false + @scrollPastEnd ?= true @hasTerminatedPendingState = false showInvisibles ?= true @@ -3156,7 +3156,7 @@ class TextEditor extends Model # Get the Element for the editor. getElement: -> - @editorElement ?= new TextEditorElement().initialize(this, atom, @autoHeight, @ignoreScrollPastEnd) + @editorElement ?= new TextEditorElement().initialize(this, atom, @autoHeight, @scrollPastEnd) # Essential: Retrieves the greyed out placeholder of a mini editor. # @@ -3232,7 +3232,7 @@ class TextEditor extends Model setFirstVisibleScreenRow: (screenRow, fromView) -> unless fromView maxScreenRow = @getScreenLineCount() - 1 - unless @config.get('editor.scrollPastEnd') and not @ignoreScrollPastEnd + unless @config.get('editor.scrollPastEnd') and @scrollPastEnd height = @displayBuffer.getHeight() lineHeightInPixels = @displayBuffer.getLineHeightInPixels() if height? and lineHeightInPixels? From 928205a44ae7b18d7257a6853e34e33d5ed562c9 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 3 Mar 2016 10:09:18 -0500 Subject: [PATCH 398/971] s/grammarName/grammar --- spec/text-editor-spec.coffee | 4 ++-- src/text-editor.coffee | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 637244964..9d2a2a58c 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5840,14 +5840,14 @@ describe "TextEditor", -> invisibles = editor.tokenizedLineForScreenRow(0).invisibles expect(invisibles).toBe(null) - describe "when the editor is constructed with the grammarName option set", -> + describe "when the editor is constructed with the grammar option set", -> beforeEach -> atom.workspace.destroyActivePane() waitsForPromise -> atom.packages.activatePackage('language-coffee-script') waitsForPromise -> - atom.workspace.open('sample.js', grammarName: 'source.coffee').then (o) -> editor = o + atom.workspace.open('sample.js', grammar: atom.grammars.grammarForScopeName('source.coffee')).then (o) -> editor = o it "sets the grammar", -> expect(editor.getGrammar().name).toBe 'CoffeeScript' diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 54ea283e9..b981dcbb2 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -100,7 +100,7 @@ class TextEditor extends Model softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, grammarName, showInvisibles, @autoHeight, @scrollPastEnd + @project, @assert, @applicationDelegate, grammar, showInvisibles, @autoHeight, @scrollPastEnd } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -156,8 +156,8 @@ class TextEditor extends Model priority: 0 visible: lineNumberGutterVisible - if grammarName? - @setGrammar(@grammarRegistry.grammarForScopeName(grammarName)) + if grammar? + @setGrammar(grammar) serialize: -> deserializer: 'TextEditor' From 10acfd057fbf994fe2ddd5dd12d66b7dba5cee94 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 3 Mar 2016 10:13:26 -0500 Subject: [PATCH 399/971] Err, yeah, we care about undefined, not false. --- src/text-editor-presenter.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 1fa662c59..ef1b403c3 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -42,7 +42,7 @@ class TextEditorPresenter @startReflowing() if @continuousReflow @updating = false - @scrollPastEndOverride = scrollPastEnd || true + @scrollPastEndOverride = scrollPastEnd ? true setLinesYardstick: (@linesYardstick) -> From dba1fbd40845304960b447bf0fc3c15a3ad2ef71 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 3 Mar 2016 10:15:13 -0500 Subject: [PATCH 400/971] De-lint. --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index b981dcbb2..d5937d307 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -127,7 +127,7 @@ class TextEditor extends Model buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ - buffer, tabLength, softWrapped, ignoreInvisibles: @mini or !showInvisibles, largeFileMode, + buffer, tabLength, softWrapped, ignoreInvisibles: @mini or not showInvisibles, largeFileMode, @config, @assert, @grammarRegistry, @packageManager }) @buffer = @displayBuffer.buffer From a218582aeb3186d4237bd3a4341e5b621b5d2c3b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Mar 2016 18:02:08 -0700 Subject: [PATCH 401/971] =?UTF-8?q?Supply=20pathsToOpen=20in=20case=20ther?= =?UTF-8?q?e=E2=80=99s=20no=20window=20state?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/atom-application.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index b2d66af87..230e1bb9f 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -513,6 +513,7 @@ class AtomApplication for state in states @openWithOptions(_.extend(options, { initialPaths: state.initialPaths + pathsToOpen: state.initialPaths.filter (directoryPath) -> fs.isDirectorySync(directoryPath) urlsToOpen: [] devMode: @devMode safeMode: @safeMode From e620232d355b81afcf2c9607314c3600198bab0b Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 3 Mar 2016 17:46:02 -0800 Subject: [PATCH 402/971] Add new item before destroying pending item Fixes atom/tabs#278 Signed-off-by: Michelle Tilley --- spec/pane-spec.coffee | 30 +++++++++++++++++++++++++++--- src/pane.coffee | 4 ++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 888ccf6e2..873749a19 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -132,13 +132,37 @@ describe "Pane", -> expect(-> pane.addItem('foo')).toThrow() expect(-> pane.addItem(1)).toThrow() - it "destroys any existing pending item if the new item is pending", -> + it "destroys any existing pending item", -> + pane = new Pane(paneParams(items: [])) + itemA = new Item("A") + itemB = new Item("B") + itemC = new Item("C") + pane.addItem(itemA, undefined, false, false) + pane.addItem(itemB, undefined, false, true) + pane.addItem(itemC, undefined, false, false) + expect(itemB.isDestroyed()).toBe true + + it "adds the new item before destroying any existing pending item", -> + eventOrder = [] + pane = new Pane(paneParams(items: [])) itemA = new Item("A") itemB = new Item("B") pane.addItem(itemA, undefined, false, true) - pane.addItem(itemB, undefined, false, true) - expect(itemA.isDestroyed()).toBe true + + pane.onDidAddItem ({item}) -> + eventOrder.push("add") if item is itemB + + pane.onDidRemoveItem ({item}) -> + eventOrder.push("remove") if item is itemA + + pane.addItem(itemB) + + waitsFor -> + eventOrder.length is 2 + + runs -> + expect(eventOrder).toEqual ["add", "remove"] describe "::activateItem(item)", -> pane = null diff --git a/src/pane.coffee b/src/pane.coffee index b944f763c..70f8b4bab 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -433,11 +433,11 @@ class Pane extends Model @subscriptionsPerItem.set item, itemSubscriptions @items.splice(index, 0, item) - pendingItem = @getPendingItem() - @destroyItem(pendingItem) if pendingItem? + lastPendingItem = @getPendingItem() @setPendingItem(item) if pending @emitter.emit 'did-add-item', {item, index, moved} + @destroyItem(lastPendingItem) if lastPendingItem? @setActiveItem(item) unless @getActiveItem()? item From 4d9e2b865b1f136bd1400eb3c67cb98dfc7571f2 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Thu, 3 Mar 2016 20:03:44 -0800 Subject: [PATCH 403/971] Ensure atom.cmd --wait correctly waits in Windows cmd & powershell --- resources/win/atom.cmd | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/resources/win/atom.cmd b/resources/win/atom.cmd index 8d5e6f97a..a1af5cd53 100644 --- a/resources/win/atom.cmd +++ b/resources/win/atom.cmd @@ -22,30 +22,13 @@ FOR %%a IN (%*) DO ( ) ) -rem Getting the process ID in cmd of the current cmd process: http://superuser.com/questions/881789/identify-and-kill-batch-script-started-before -set T=%TEMP%\atomCmdProcessId-%time::=%.tmp -wmic process where (Name="WMIC.exe" AND CommandLine LIKE "%%%TIME%%%") get ParentProcessId /value | find "ParentProcessId" >%T% -set /P A=<%T% -set PID=%A:~16% -del %T% - IF "%EXPECT_OUTPUT%"=="YES" ( SET ELECTRON_ENABLE_LOGGING=YES IF "%WAIT%"=="YES" ( - "%~dp0\..\..\atom.exe" --pid=%PID% %* - rem If the wait flag is set, don't exit this process until Atom tells it to. - goto waitLoop + powershell -noexit "%~dp0\..\..\atom.exe" --pid=$pid %* ; wait-event ) ELSE ( "%~dp0\..\..\atom.exe" %* ) ) ELSE ( "%~dp0\..\app\apm\bin\node.exe" "%~dp0\atom.js" %* ) - -goto end - -:waitLoop - sleep 1 - goto waitLoop - -:end From 1043dc083982c63a23d8d13f7653e34bd4453c50 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 3 Mar 2016 22:43:19 -0800 Subject: [PATCH 404/971] :arrow_up: bracket-matcher@0.81.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8907f16b9..f4c6475be 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "autosave": "0.23.1", "background-tips": "0.26.0", "bookmarks": "0.38.2", - "bracket-matcher": "0.80.1", + "bracket-matcher": "0.81.0", "command-palette": "0.38.0", "deprecation-cop": "0.54.1", "dev-live-reload": "0.47.0", From 7176da7614fb3b457497e639908795948df5dc64 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 15:17:10 +0100 Subject: [PATCH 405/971] Use structured cloning --- src/state-store.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/state-store.js b/src/state-store.js index 13c9a0462..a2d3b476b 100644 --- a/src/state-store.js +++ b/src/state-store.js @@ -24,16 +24,13 @@ class StateStore { } save (key, value) { - // Serialize values using JSON.stringify, as it seems way faster than IndexedDB structured clone. - // (Ref.: https://bugs.chromium.org/p/chromium/issues/detail?id=536620) - let jsonValue = JSON.stringify(value) return new Promise((resolve, reject) => { this.dbPromise.then(db => { if (db == null) resolve() var request = db.transaction(['states'], 'readwrite') .objectStore('states') - .put({value: jsonValue, storedAt: new Date().toString(), isJSON: true}, key) + .put({value: value, storedAt: new Date().toString()}, key) request.onsuccess = resolve request.onerror = reject @@ -52,9 +49,8 @@ class StateStore { request.onsuccess = (event) => { let result = event.target.result - if (result) { - // TODO: remove this when state will be serialized only via JSON. - resolve(result.isJSON ? JSON.parse(result.value) : result.value) + if (result && !result.isJSON) { + resolve(result.value) } else { resolve(null) } From 011fe380e9727a0f0d49230e15da25fa506835a3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 16:57:02 +0100 Subject: [PATCH 406/971] Pass markerLayer: false to buffer.serialize when not quitting --- spec/project-spec.coffee | 25 +++++++++++++++++++++---- src/project.coffee | 8 ++++---- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 9d42f9a7e..cecfe4bd4 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -29,7 +29,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize(), atom.deserializers) + deserializedProject.deserialize(atom.project.serialize()) expect(deserializedProject.getBuffers().length).toBe 0 it "listens for destroyed events on deserialized buffers and removes them when they are destroyed", -> @@ -39,7 +39,7 @@ describe "Project", -> runs -> expect(atom.project.getBuffers().length).toBe 1 deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize(), atom.deserializers) + deserializedProject.deserialize(atom.project.serialize()) expect(deserializedProject.getBuffers().length).toBe 1 deserializedProject.getBuffers()[0].destroy() @@ -56,7 +56,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 fs.mkdirSync(pathToOpen) deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize(), atom.deserializers) + deserializedProject.deserialize(atom.project.serialize()) expect(deserializedProject.getBuffers().length).toBe 0 it "does not deserialize buffers when their path is inaccessible", -> @@ -70,9 +70,26 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 fs.chmodSync(pathToOpen, '000') deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize(), atom.deserializers) + deserializedProject.deserialize(atom.project.serialize()) expect(deserializedProject.getBuffers().length).toBe 0 + it "serializes marker / marker only if Atom is quitting", -> + waitsForPromise -> + atom.workspace.open('a') + + runs -> + bufferA = atom.project.getBuffers()[0] + layerA = bufferA.addMarkerLayer(maintainHistory: true) + markerA = layerA.markPosition([0, 3]) + + notQuittingProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) + notQuittingProject.deserialize(atom.project.serialize({isQuitting: false})) + expect(notQuittingProject.getBuffers()[0].getMarkerLayer(layerA.id)?.getMarker(markerA.id)).toBeUndefined() + + quittingProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) + quittingProject.deserialize(atom.project.serialize({isQuitting: true})) + expect(quittingProject.getBuffers()[0].getMarkerLayer(layerA.id)?.getMarker(markerA.id)).not.toBeUndefined() + describe "when an editor is saved and the project has no path", -> it "sets the project's path to the saved file's parent directory", -> tempFile = temp.openSync().path diff --git a/src/project.coffee b/src/project.coffee index 008d81e3e..538c4acb4 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -54,7 +54,7 @@ class Project extends Model Section: Serialization ### - deserialize: (state, deserializerManager) -> + deserialize: (state) -> state.paths = [state.path] if state.path? # backward compatibility @buffers = _.compact state.buffers.map (bufferState) -> @@ -65,15 +65,15 @@ class Project extends Model fs.closeSync(fs.openSync(bufferState.filePath, 'r')) catch error return unless error.code is 'ENOENT' - deserializerManager.deserialize(bufferState) + TextBuffer.deserialize(bufferState) @subscribeToBuffer(buffer) for buffer in @buffers @setPaths(state.paths) - serialize: -> + serialize: (options) -> deserializer: 'Project' paths: @getPaths() - buffers: _.compact(@buffers.map (buffer) -> buffer.serialize() if buffer.isRetained()) + buffers: _.compact(@buffers.map (buffer) -> buffer.serialize({markerLayers: options.isQuitting is true}) if buffer.isRetained()) ### Section: Event Subscription From 311cde36c963ee85c9bc011baf38061cd7b01212 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 17:06:53 +0100 Subject: [PATCH 407/971] Call saveState(isQuitting: true) on beforeUnload --- spec/window-event-handler-spec.coffee | 5 +++++ src/window-event-handler.coffee | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/window-event-handler-spec.coffee b/spec/window-event-handler-spec.coffee index bb7e1665b..b9f102c3f 100644 --- a/spec/window-event-handler-spec.coffee +++ b/spec/window-event-handler-spec.coffee @@ -55,6 +55,11 @@ describe "WindowEventHandler", -> jasmine.unspy(TextEditor.prototype, "shouldPromptToSave") spyOn(ipcRenderer, 'send') + it "saves AtomEnvironment's state with the {isQuitting: true} option", -> + spyOn(atom, 'saveState') + window.dispatchEvent(new CustomEvent('beforeunload')) + expect(atom.saveState).toHaveBeenCalledWith({isQuitting: true}) + describe "when pane items are modified", -> editor = null beforeEach -> diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index ce08344c6..10bebbf1e 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -143,7 +143,7 @@ class WindowEventHandler @reloadRequested = false @atomEnvironment.storeWindowDimensions() - @atomEnvironment.saveState() + @atomEnvironment.saveState({isQuitting: true}) if confirmed @atomEnvironment.unloadEditorWindow() else From 0fdc19098936aed7597425352f19e8a1726ae046 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 17:08:32 +0100 Subject: [PATCH 408/971] Use isQuitting: false when saving state on key/mouse down --- spec/atom-environment-spec.coffee | 4 ++-- src/atom-environment.coffee | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 1f8eb08e7..d8d700dd7 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -185,12 +185,12 @@ describe "AtomEnvironment", -> keydown = new KeyboardEvent('keydown') atom.document.dispatchEvent(keydown) advanceClock atom.saveStateDebounceInterval - expect(atom.saveState).toHaveBeenCalled() + expect(atom.saveState).toHaveBeenCalledWith({isQuitting: false}) mousedown = new MouseEvent('mousedown') atom.document.dispatchEvent(mousedown) advanceClock atom.saveStateDebounceInterval - expect(atom.saveState).toHaveBeenCalled() + expect(atom.saveState).toHaveBeenCalledWith({isQuitting: false}) describe "openInitialEmptyEditorIfNecessary", -> describe "when there are no paths set", -> diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 0ee12fe93..983148ca4 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -225,7 +225,7 @@ class AtomEnvironment extends Model checkPortableHomeWritable() attachSaveStateListeners: -> - debouncedSaveState = _.debounce((=> @saveState()), @saveStateDebounceInterval) + debouncedSaveState = _.debounce((=> @saveState({isQuitting: false})), @saveStateDebounceInterval) @document.addEventListener('mousedown', debouncedSaveState, true) @document.addEventListener('keydown', debouncedSaveState, true) @disposables.add new Disposable => From 3bdb83f97ec1018749695313e3c8a38da1f6a5af Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 17:11:34 +0100 Subject: [PATCH 409/971] Pass saveState options to project.serialize --- src/atom-environment.coffee | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 983148ca4..748e73ef9 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -670,9 +670,9 @@ class AtomEnvironment extends Model @openInitialEmptyEditorIfNecessary() - serialize: -> + serialize: (options) -> version: @constructor.version - project: @project.serialize() + project: @project.serialize(options) workspace: @workspace.serialize() packageStates: @packages.serialize() grammars: {grammarOverridesByPath: @grammars.grammarOverridesByPath} @@ -817,9 +817,12 @@ class AtomEnvironment extends Model @blobStore.save() - saveState: -> + saveState: (options) -> return Promise.resolve() unless @enablePersistence + options ?= {} + options.isQuitting ?= false + new Promise (resolve, reject) => window.requestIdleCallback => state = @serialize() From 41c293ef9d79e5ae1d1268e8cf932d59c5664f9d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 17:23:19 +0100 Subject: [PATCH 410/971] Always pass {isQuitting} in tests --- spec/git-repository-async-spec.js | 2 +- spec/git-spec.coffee | 2 +- spec/project-spec.coffee | 8 ++++---- spec/workspace-spec.coffee | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index fa5b0d711..80ba2ebb1 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -541,7 +541,7 @@ describe('GitRepositoryAsync', () => { await atom.workspace.open('file.txt') project2 = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - project2.deserialize(atom.project.serialize(), atom.deserializers) + project2.deserialize(atom.project.serialize({isQuitting: true})) const repo = project2.getRepositories()[0].async waitsForPromise(() => repo.refreshStatus()) diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 22c40c19a..9124f78cc 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -347,7 +347,7 @@ describe "GitRepository", -> runs -> project2 = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - project2.deserialize(atom.project.serialize(), atom.deserializers) + project2.deserialize(atom.project.serialize({isQuitting: false})) buffer = project2.getBuffers()[0] waitsFor -> diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index cecfe4bd4..9a392dd62 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -29,7 +29,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize()) + deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) expect(deserializedProject.getBuffers().length).toBe 0 it "listens for destroyed events on deserialized buffers and removes them when they are destroyed", -> @@ -39,7 +39,7 @@ describe "Project", -> runs -> expect(atom.project.getBuffers().length).toBe 1 deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize()) + deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) expect(deserializedProject.getBuffers().length).toBe 1 deserializedProject.getBuffers()[0].destroy() @@ -56,7 +56,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 fs.mkdirSync(pathToOpen) deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize()) + deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) expect(deserializedProject.getBuffers().length).toBe 0 it "does not deserialize buffers when their path is inaccessible", -> @@ -70,7 +70,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 fs.chmodSync(pathToOpen, '000') deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize()) + deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) expect(deserializedProject.getBuffers().length).toBe 0 it "serializes marker / marker only if Atom is quitting", -> diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 2e15431b2..214fc477f 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -22,11 +22,11 @@ describe "Workspace", -> describe "serialization", -> simulateReload = -> workspaceState = atom.workspace.serialize() - projectState = atom.project.serialize() + projectState = atom.project.serialize({isQuitting: true}) atom.workspace.destroy() atom.project.destroy() atom.project = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm.bind(atom)}) - atom.project.deserialize(projectState, atom.deserializers) + atom.project.deserialize(projectState) atom.workspace = new Workspace({ config: atom.config, project: atom.project, packageManager: atom.packages, grammarRegistry: atom.grammars, deserializerManager: atom.deserializers, From 575065f3e92aadc944d3c749ae834bd8f22fa5c2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 17:37:53 +0100 Subject: [PATCH 411/971] Don't forget to pass the option during saveState --- src/atom-environment.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 748e73ef9..40cbe0089 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -825,7 +825,7 @@ class AtomEnvironment extends Model new Promise (resolve, reject) => window.requestIdleCallback => - state = @serialize() + state = @serialize(options) savePromise = if storageKey = @getStateKey(@project?.getPaths()) @stateStore.save(storageKey, state) From 046cdddefe58db79c7deafdc180ce40697a6bfe4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 17:53:14 +0100 Subject: [PATCH 412/971] :memo: --- spec/project-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 9a392dd62..0c76ad9bb 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -73,7 +73,7 @@ describe "Project", -> deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) expect(deserializedProject.getBuffers().length).toBe 0 - it "serializes marker / marker only if Atom is quitting", -> + it "serializes marker layers only if Atom is quitting", -> waitsForPromise -> atom.workspace.open('a') From d9ee94031f31abccfdda23d528f8f42ded48fbf0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 4 Mar 2016 13:40:16 -0700 Subject: [PATCH 413/971] Test disposal of manually-created tooltips This also prevents subsequent tests from failing due to the manual tooltip never being removed from document.body. --- spec/tooltip-manager-spec.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/tooltip-manager-spec.coffee b/spec/tooltip-manager-spec.coffee index 264d1a3bb..d4bfc1bd6 100644 --- a/spec/tooltip-manager-spec.coffee +++ b/spec/tooltip-manager-spec.coffee @@ -29,8 +29,10 @@ describe "TooltipManager", -> expect(document.body.querySelector(".tooltip")).toHaveText("Title") it "creates a tooltip immediately if the trigger type is manual", -> - manager.add element, title: "Title", trigger: "manual" + disposable = manager.add element, title: "Title", trigger: "manual" expect(document.body.querySelector(".tooltip")).toHaveText("Title") + disposable.dispose() + expect(document.body.querySelector(".tooltip")).toBeNull() it "allows jQuery elements to be passed as the target", -> element2 = document.createElement('div') From 6cc80e05ff4a98512f0882912c446dfdb7d4027b Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 4 Mar 2016 16:36:47 -0500 Subject: [PATCH 414/971] Default to auto height being true. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise TextEditorElements created through the tag wouldn’t have a setting and be wrong. --- src/text-editor-element.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index df13f2a15..2a9b5e262 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -18,6 +18,7 @@ class TextEditorElement extends HTMLElement hasTiledRendering: true logicalDisplayBuffer: true scrollPastEnd: true + autoHeight: true createdCallback: -> # Use globals when the following instance variables aren't set. From b4f335d9a18ceb719fa47e1c2c54a43993e6d5aa Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 4 Mar 2016 17:01:26 -0700 Subject: [PATCH 415/971] :arrow_up: tree-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f4c6475be..6a5f47479 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.111.1", "tabs": "0.91.1", "timecop": "0.33.1", - "tree-view": "0.201.5", + "tree-view": "0.202.0", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From 341afed0ab7e5c9fbccd69030dce6e77a4bb4e75 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Fri, 4 Mar 2016 19:40:00 -0500 Subject: [PATCH 416/971] :arrow_up: language-json@0.17.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6a5f47479..3a6d0910c 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "language-hyperlink": "0.16.0", "language-java": "0.17.0", "language-javascript": "0.110.0", - "language-json": "0.17.4", + "language-json": "0.17.5", "language-less": "0.29.0", "language-make": "0.21.0", "language-mustache": "0.13.0", From 42a696cf28f00b9cfe33107ea57e33c4e38ea3f4 Mon Sep 17 00:00:00 2001 From: Rodrigo Espinosa Curbelo Date: Sat, 5 Mar 2016 21:28:06 -0300 Subject: [PATCH 417/971] :bug: Scroll to cursor when unfold all Fixes #11066 --- src/text-editor.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index d5937d307..e858d8c80 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2936,6 +2936,7 @@ class TextEditor extends Model # Extended: Unfold all existing folds. unfoldAll: -> @languageMode.unfoldAll() + @scrollToCursorPosition() # Extended: Fold all foldable lines at the given indent level. # From f88051e5e624f5519932a626d135e23dfa7449bd Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sun, 6 Mar 2016 00:56:35 -0800 Subject: [PATCH 418/971] :arrow_up: find-and-replace (cherry picked from commit 2d5d0d21bde88fcc04b6b28ded6ad84b5a85fe39) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3a6d0910c..3a36478bd 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "dev-live-reload": "0.47.0", "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", - "find-and-replace": "0.197.3", + "find-and-replace": "0.197.4", "fuzzy-finder": "1.0.2", "git-diff": "1.0.0", "go-to-line": "0.30.0", From 95dbbea8f536753c7952f231fe479f3cc7e74d20 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 7 Mar 2016 10:32:19 +0100 Subject: [PATCH 419/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1c55268dd..aa74e94f2 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.3.2", + "text-buffer": "8.4.0", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 12587073d2eca35079c860c8c42fcb9b2065f6e7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 7 Mar 2016 10:52:16 +0100 Subject: [PATCH 420/971] :art: isQuitting -> isUnloading --- spec/atom-environment-spec.coffee | 4 ++-- spec/git-repository-async-spec.js | 2 +- spec/git-spec.coffee | 2 +- spec/project-spec.coffee | 12 ++++++------ spec/window-event-handler-spec.coffee | 4 ++-- spec/workspace-spec.coffee | 2 +- src/atom-environment.coffee | 4 ++-- src/project.coffee | 2 +- src/window-event-handler.coffee | 2 +- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 82a38942c..556312082 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -185,12 +185,12 @@ describe "AtomEnvironment", -> keydown = new KeyboardEvent('keydown') atom.document.dispatchEvent(keydown) advanceClock atom.saveStateDebounceInterval - expect(atom.saveState).toHaveBeenCalledWith({isQuitting: false}) + expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) mousedown = new MouseEvent('mousedown') atom.document.dispatchEvent(mousedown) advanceClock atom.saveStateDebounceInterval - expect(atom.saveState).toHaveBeenCalledWith({isQuitting: false}) + expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) describe "openInitialEmptyEditorIfNecessary", -> describe "when there are no paths set", -> diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 80ba2ebb1..e7bffad3a 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -541,7 +541,7 @@ describe('GitRepositoryAsync', () => { await atom.workspace.open('file.txt') project2 = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - project2.deserialize(atom.project.serialize({isQuitting: true})) + project2.deserialize(atom.project.serialize({isUnloading: true})) const repo = project2.getRepositories()[0].async waitsForPromise(() => repo.refreshStatus()) diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 9124f78cc..3afd4da75 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -347,7 +347,7 @@ describe "GitRepository", -> runs -> project2 = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - project2.deserialize(atom.project.serialize({isQuitting: false})) + project2.deserialize(atom.project.serialize({isUnloading: false})) buffer = project2.getBuffers()[0] waitsFor -> diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 7f13cda40..499efd017 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -37,7 +37,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) + deserializedProject.deserialize(atom.project.serialize({isUnloading: false})) expect(deserializedProject.getBuffers().length).toBe 0 it "listens for destroyed events on deserialized buffers and removes them when they are destroyed", -> @@ -47,7 +47,7 @@ describe "Project", -> runs -> expect(atom.project.getBuffers().length).toBe 1 deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) + deserializedProject.deserialize(atom.project.serialize({isUnloading: false})) expect(deserializedProject.getBuffers().length).toBe 1 deserializedProject.getBuffers()[0].destroy() @@ -64,7 +64,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 fs.mkdirSync(pathToOpen) deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) + deserializedProject.deserialize(atom.project.serialize({isUnloading: false})) expect(deserializedProject.getBuffers().length).toBe 0 it "does not deserialize buffers when their path is inaccessible", -> @@ -78,7 +78,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 fs.chmodSync(pathToOpen, '000') deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) + deserializedProject.deserialize(atom.project.serialize({isUnloading: false})) expect(deserializedProject.getBuffers().length).toBe 0 it "serializes marker layers only if Atom is quitting", -> @@ -91,11 +91,11 @@ describe "Project", -> markerA = layerA.markPosition([0, 3]) notQuittingProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - notQuittingProject.deserialize(atom.project.serialize({isQuitting: false})) + notQuittingProject.deserialize(atom.project.serialize({isUnloading: false})) expect(notQuittingProject.getBuffers()[0].getMarkerLayer(layerA.id)?.getMarker(markerA.id)).toBeUndefined() quittingProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - quittingProject.deserialize(atom.project.serialize({isQuitting: true})) + quittingProject.deserialize(atom.project.serialize({isUnloading: true})) expect(quittingProject.getBuffers()[0].getMarkerLayer(layerA.id)?.getMarker(markerA.id)).not.toBeUndefined() describe "when an editor is saved and the project has no path", -> diff --git a/spec/window-event-handler-spec.coffee b/spec/window-event-handler-spec.coffee index b9f102c3f..7f093aeff 100644 --- a/spec/window-event-handler-spec.coffee +++ b/spec/window-event-handler-spec.coffee @@ -55,10 +55,10 @@ describe "WindowEventHandler", -> jasmine.unspy(TextEditor.prototype, "shouldPromptToSave") spyOn(ipcRenderer, 'send') - it "saves AtomEnvironment's state with the {isQuitting: true} option", -> + it "saves AtomEnvironment's state with the {isUnloading: true} option", -> spyOn(atom, 'saveState') window.dispatchEvent(new CustomEvent('beforeunload')) - expect(atom.saveState).toHaveBeenCalledWith({isQuitting: true}) + expect(atom.saveState).toHaveBeenCalledWith({isUnloading: true}) describe "when pane items are modified", -> editor = null diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index bf24f25cf..38d4839b0 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -22,7 +22,7 @@ describe "Workspace", -> describe "serialization", -> simulateReload = -> workspaceState = atom.workspace.serialize() - projectState = atom.project.serialize({isQuitting: true}) + projectState = atom.project.serialize({isUnloading: true}) atom.workspace.destroy() atom.project.destroy() atom.project = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm.bind(atom)}) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 2db72270c..f546fd63d 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -230,7 +230,7 @@ class AtomEnvironment extends Model checkPortableHomeWritable() attachSaveStateListeners: -> - debouncedSaveState = _.debounce((=> @saveState({isQuitting: false})), @saveStateDebounceInterval) + debouncedSaveState = _.debounce((=> @saveState({isUnloading: false})), @saveStateDebounceInterval) @document.addEventListener('mousedown', debouncedSaveState, true) @document.addEventListener('keydown', debouncedSaveState, true) @disposables.add new Disposable => @@ -835,7 +835,7 @@ class AtomEnvironment extends Model return Promise.resolve() unless @enablePersistence options ?= {} - options.isQuitting ?= false + options.isUnloading ?= false new Promise (resolve, reject) => window.requestIdleCallback => diff --git a/src/project.coffee b/src/project.coffee index d6d36e644..5f3420b08 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -74,7 +74,7 @@ class Project extends Model serialize: (options) -> deserializer: 'Project' paths: @getPaths() - buffers: _.compact(@buffers.map (buffer) -> buffer.serialize({markerLayers: options.isQuitting is true}) if buffer.isRetained()) + buffers: _.compact(@buffers.map (buffer) -> buffer.serialize({markerLayers: options.isUnloading is true}) if buffer.isRetained()) ### Section: Event Subscription diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index 10bebbf1e..490b2b416 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -143,7 +143,7 @@ class WindowEventHandler @reloadRequested = false @atomEnvironment.storeWindowDimensions() - @atomEnvironment.saveState({isQuitting: true}) + @atomEnvironment.saveState({isUnloading: true}) if confirmed @atomEnvironment.unloadEditorWindow() else From 914015e4ebf9ba6fff1bbac816662fd46c71dc99 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 7 Mar 2016 10:52:28 +0100 Subject: [PATCH 421/971] :fire: Remove default parameters --- src/atom-environment.coffee | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index f546fd63d..f99ec3bcc 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -834,9 +834,6 @@ class AtomEnvironment extends Model saveState: (options) -> return Promise.resolve() unless @enablePersistence - options ?= {} - options.isUnloading ?= false - new Promise (resolve, reject) => window.requestIdleCallback => state = @serialize(options) From 4f9bc0c06ba7bc115b09a4ed0899e407bf0206bd Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 7 Mar 2016 11:45:54 +0100 Subject: [PATCH 422/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2c517fedd..ff30c0d07 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.4.0", + "text-buffer": "8.4.1", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From a3bed908d7ca5b58bf61c8b9c3381d7f04c62a24 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 7 Mar 2016 16:58:03 +0100 Subject: [PATCH 423/971] Don't partially serialize after unloading editor window --- spec/atom-environment-spec.coffee | 11 ++++++++++- spec/window-event-handler-spec.coffee | 5 ----- src/atom-environment.coffee | 8 +++++++- src/window-event-handler.coffee | 1 - 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 556312082..8e7af40d5 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -179,7 +179,7 @@ describe "AtomEnvironment", -> atom.loadState().then (state) -> expect(state).toEqual({stuff: 'cool'}) - it "saves state on keydown and mousedown events", -> + it "saves state on keydown and mousedown events when the editor window hasn't been unloaded", -> spyOn(atom, 'saveState') keydown = new KeyboardEvent('keydown') @@ -187,11 +187,20 @@ describe "AtomEnvironment", -> advanceClock atom.saveStateDebounceInterval expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) + atom.saveState.reset() mousedown = new MouseEvent('mousedown') atom.document.dispatchEvent(mousedown) advanceClock atom.saveStateDebounceInterval expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) + atom.saveState.reset() + atom.unloadEditorWindow() + mousedown = new MouseEvent('mousedown') + atom.document.dispatchEvent(mousedown) + advanceClock atom.saveStateDebounceInterval + expect(atom.saveState).toHaveBeenCalledWith({isUnloading: true}) + expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: false}) + describe "openInitialEmptyEditorIfNecessary", -> describe "when there are no paths set", -> beforeEach -> diff --git a/spec/window-event-handler-spec.coffee b/spec/window-event-handler-spec.coffee index 7f093aeff..bb7e1665b 100644 --- a/spec/window-event-handler-spec.coffee +++ b/spec/window-event-handler-spec.coffee @@ -55,11 +55,6 @@ describe "WindowEventHandler", -> jasmine.unspy(TextEditor.prototype, "shouldPromptToSave") spyOn(ipcRenderer, 'send') - it "saves AtomEnvironment's state with the {isUnloading: true} option", -> - spyOn(atom, 'saveState') - window.dispatchEvent(new CustomEvent('beforeunload')) - expect(atom.saveState).toHaveBeenCalledWith({isUnloading: true}) - describe "when pane items are modified", -> editor = null beforeEach -> diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index f99ec3bcc..8c79d66f6 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -129,6 +129,7 @@ class AtomEnvironment extends Model constructor: (params={}) -> {@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params + @unloaded = false @loadTime = null {devMode, safeMode, resourcePath, clearWindowState} = @getLoadSettings() @@ -230,7 +231,8 @@ class AtomEnvironment extends Model checkPortableHomeWritable() attachSaveStateListeners: -> - debouncedSaveState = _.debounce((=> @saveState({isUnloading: false})), @saveStateDebounceInterval) + saveState = => @saveState({isUnloading: false}) unless @unloaded + debouncedSaveState = _.debounce(saveState, @saveStateDebounceInterval) @document.addEventListener('mousedown', debouncedSaveState, true) @document.addEventListener('keydown', debouncedSaveState, true) @disposables.add new Disposable => @@ -696,9 +698,11 @@ class AtomEnvironment extends Model unloadEditorWindow: -> return if not @project + @saveState({isUnloading: true}) @storeWindowBackground() @packages.deactivatePackages() @saveBlobStoreSync() + @unloaded = true openInitialEmptyEditorIfNecessary: -> return unless @config.get('core.openEmptyEditorOnStart') @@ -836,6 +840,8 @@ class AtomEnvironment extends Model new Promise (resolve, reject) => window.requestIdleCallback => + return if not @project + state = @serialize(options) savePromise = if storageKey = @getStateKey(@project?.getPaths()) diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index 490b2b416..6c338320d 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -143,7 +143,6 @@ class WindowEventHandler @reloadRequested = false @atomEnvironment.storeWindowDimensions() - @atomEnvironment.saveState({isUnloading: true}) if confirmed @atomEnvironment.unloadEditorWindow() else From 8caa9d0a9571d48d812c5290abe97b56d154f750 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 7 Mar 2016 17:21:09 +0100 Subject: [PATCH 424/971] :art: Better wording on specs --- spec/atom-environment-spec.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 8e7af40d5..7488c8e05 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -179,19 +179,21 @@ describe "AtomEnvironment", -> atom.loadState().then (state) -> expect(state).toEqual({stuff: 'cool'}) - it "saves state on keydown and mousedown events when the editor window hasn't been unloaded", -> + it "saves state on keydown, mousedown, and when the editor window unloads", -> spyOn(atom, 'saveState') keydown = new KeyboardEvent('keydown') atom.document.dispatchEvent(keydown) advanceClock atom.saveStateDebounceInterval expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) + expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: true}) atom.saveState.reset() mousedown = new MouseEvent('mousedown') atom.document.dispatchEvent(mousedown) advanceClock atom.saveStateDebounceInterval expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) + expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: true}) atom.saveState.reset() atom.unloadEditorWindow() From f6d419c2f4563dae446592eb928d4540bb841af7 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 7 Mar 2016 10:01:28 -0800 Subject: [PATCH 425/971] Merge pull request #11057 from atom/mkt-improve-pane-add-item-options Move Pane::addItem 'pending' option to options object (cherry picked from commit 53a9c22554177c787751e20c7e7b38bd4eb8e69b) --- package.json | 2 +- spec/pane-spec.coffee | 57 +++++++++++++++++++++++++++++++++++-------- src/pane.coffee | 41 ++++++++++++++++++++----------- src/workspace.coffee | 2 +- 4 files changed, 76 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index ff30c0d07..5cd1367fa 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.1.0", "styleguide": "0.45.2", "symbols-view": "0.111.1", - "tabs": "0.91.1", + "tabs": "0.91.3", "timecop": "0.33.1", "tree-view": "0.202.0", "update-package-dependencies": "0.10.0", diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 873749a19..d0b191f38 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -1,5 +1,6 @@ {extend} = require 'underscore-plus' {Emitter} = require 'event-kit' +Grim = require 'grim' Pane = require '../src/pane' PaneAxis = require '../src/pane-axis' PaneContainer = require '../src/pane-container' @@ -92,7 +93,7 @@ describe "Pane", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B")])) [item1, item2] = pane.getItems() item3 = new Item("C") - pane.addItem(item3, 1) + pane.addItem(item3, index: 1) expect(pane.getItems()).toEqual [item1, item3, item2] it "adds the item after the active item if no index is provided", -> @@ -115,7 +116,7 @@ describe "Pane", -> pane.onDidAddItem (event) -> events.push(event) item = new Item("C") - pane.addItem(item, 1) + pane.addItem(item, index: 1) expect(events).toEqual [{item, index: 1, moved: false}] it "throws an exception if the item is already present on a pane", -> @@ -137,9 +138,9 @@ describe "Pane", -> itemA = new Item("A") itemB = new Item("B") itemC = new Item("C") - pane.addItem(itemA, undefined, false, false) - pane.addItem(itemB, undefined, false, true) - pane.addItem(itemC, undefined, false, false) + pane.addItem(itemA, pending: false) + pane.addItem(itemB, pending: true) + pane.addItem(itemC, pending: false) expect(itemB.isDestroyed()).toBe true it "adds the new item before destroying any existing pending item", -> @@ -148,7 +149,7 @@ describe "Pane", -> pane = new Pane(paneParams(items: [])) itemA = new Item("A") itemB = new Item("B") - pane.addItem(itemA, undefined, false, true) + pane.addItem(itemA, pending: true) pane.onDidAddItem ({item}) -> eventOrder.push("add") if item is itemB @@ -164,6 +165,25 @@ describe "Pane", -> runs -> expect(eventOrder).toEqual ["add", "remove"] + describe "when using the old API of ::addItem(item, index)", -> + beforeEach -> + spyOn Grim, "deprecate" + + it "supports the older public API", -> + pane = new Pane(paneParams(items: [])) + itemA = new Item("A") + itemB = new Item("B") + itemC = new Item("C") + pane.addItem(itemA, 0) + pane.addItem(itemB, 0) + pane.addItem(itemC, 0) + expect(pane.getItems()).toEqual [itemC, itemB, itemA] + + it "shows a deprecation warning", -> + pane = new Pane(paneParams(items: [])) + pane.addItem(new Item(), 2) + expect(Grim.deprecate).toHaveBeenCalledWith "Pane::addItem(item, 2) is deprecated in favor of Pane::addItem(item, {index: 2})" + describe "::activateItem(item)", -> pane = null @@ -196,15 +216,15 @@ describe "Pane", -> itemD = new Item("D") it "replaces the active item if it is pending", -> - pane.activateItem(itemC, true) + pane.activateItem(itemC, pending: true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'C', 'B'] - pane.activateItem(itemD, true) + pane.activateItem(itemD, pending: true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'D', 'B'] it "adds the item after the active item if it is not pending", -> - pane.activateItem(itemC, true) + pane.activateItem(itemC, pending: true) pane.activateItemAtIndex(2) - pane.activateItem(itemD, true) + pane.activateItem(itemD, pending: true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D'] describe "::setPendingItem", -> @@ -706,6 +726,23 @@ describe "Pane", -> expect(pane2.isDestroyed()).toBe true expect(item4.isDestroyed()).toBe false + describe "when the item being moved is pending", -> + it "is made permanent in the new pane", -> + item6 = new Item("F") + pane1.addItem(item6, pending: true) + expect(pane1.getPendingItem()).toEqual item6 + pane1.moveItemToPane(item6, pane2, 0) + expect(pane2.getPendingItem()).not.toEqual item6 + + describe "when the target pane has a pending item", -> + it "does not destroy the pending item", -> + item6 = new Item("F") + pane1.addItem(item6, pending: true) + expect(pane1.getPendingItem()).toEqual item6 + pane2.moveItemToPane(item5, pane1, 0) + expect(pane1.getPendingItem()).toEqual item6 + + describe "split methods", -> [pane1, item1, container] = [] diff --git a/src/pane.coffee b/src/pane.coffee index 70f8b4bab..3ff62993c 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -1,3 +1,4 @@ +Grim = require 'grim' {find, compact, extend, last} = require 'underscore-plus' {CompositeDisposable, Emitter} = require 'event-kit' Model = require './model' @@ -394,30 +395,42 @@ class Pane extends Model # Public: Make the given item *active*, causing it to be displayed by # the pane's view. # - # * `pending` (optional) {Boolean} indicating that the item should be added - # in a pending state if it does not yet exist in the pane. Existing pending - # items in a pane are replaced with new pending items when they are opened. - activateItem: (item, pending=false) -> + # * `options` (optional) {Object} + # * `pending` (optional) {Boolean} indicating that the item should be added + # in a pending state if it does not yet exist in the pane. Existing pending + # items in a pane are replaced with new pending items when they are opened. + activateItem: (item, options={}) -> if item? if @getPendingItem() is @activeItem index = @getActiveItemIndex() else index = @getActiveItemIndex() + 1 - @addItem(item, index, false, pending) + @addItem(item, extend({}, options, {index: index})) @setActiveItem(item) # Public: Add the given item to the pane. # # * `item` The item to add. It can be a model with an associated view or a # view. - # * `index` (optional) {Number} indicating the index at which to add the item. - # If omitted, the item is added after the current active item. - # * `pending` (optional) {Boolean} indicating that the item should be - # added in a pending state. Existing pending items in a pane are replaced with - # new pending items when they are opened. + # * `options` (optional) {Object} + # * `index` (optional) {Number} indicating the index at which to add the item. + # If omitted, the item is added after the current active item. + # * `pending` (optional) {Boolean} indicating that the item should be + # added in a pending state. Existing pending items in a pane are replaced with + # new pending items when they are opened. # # Returns the added item. - addItem: (item, index=@getActiveItemIndex() + 1, moved=false, pending=false) -> + addItem: (item, options={}) -> + # Backward compat with old API: + # addItem(item, index=@getActiveItemIndex() + 1) + if typeof options is "number" + Grim.deprecate("Pane::addItem(item, #{options}) is deprecated in favor of Pane::addItem(item, {index: #{options}})") + options = index: options + + index = options.index ? @getActiveItemIndex() + 1 + moved = options.moved ? false + pending = options.pending ? false + throw new Error("Pane items must be objects. Attempted to add item #{item}.") unless item? and typeof item is 'object' throw new Error("Adding a pane item with URI '#{item.getURI?()}' that has already been destroyed") if item.isDestroyed?() @@ -437,7 +450,7 @@ class Pane extends Model @setPendingItem(item) if pending @emitter.emit 'did-add-item', {item, index, moved} - @destroyItem(lastPendingItem) if lastPendingItem? + @destroyItem(lastPendingItem) if lastPendingItem? and not moved @setActiveItem(item) unless @getActiveItem()? item @@ -467,7 +480,7 @@ class Pane extends Model # Returns an {Array} of added items. addItems: (items, index=@getActiveItemIndex() + 1) -> items = items.filter (item) => not (item in @items) - @addItem(item, index + i, false) for item, i in items + @addItem(item, {index: index + i}) for item, i in items items removeItem: (item, moved) -> @@ -516,7 +529,7 @@ class Pane extends Model # given pane. moveItemToPane: (item, pane, index) -> @removeItem(item, true) - pane.addItem(item, index, true) + pane.addItem(item, {index: index, moved: true}) # Public: Destroy the active item and activate the next item. destroyActiveItem: -> diff --git a/src/workspace.coffee b/src/workspace.coffee index 873d90370..9b1bf14fc 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -509,7 +509,7 @@ class Workspace extends Model return item if pane.isDestroyed() @itemOpened(item) - pane.activateItem(item, options.pending) if activateItem + pane.activateItem(item, {pending: options.pending}) if activateItem pane.activate() if activatePane initialLine = initialColumn = 0 From 56d82ccac28467c6c7c5f1114af09ac420bccb24 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 7 Mar 2016 14:44:51 -0500 Subject: [PATCH 426/971] :arrow_up: nodegit@0.11.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5cd1367fa..83864702d 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "less-cache": "0.23", "line-top-index": "0.2.0", "marked": "^0.3.4", - "nodegit": "0.11.6", + "nodegit": "0.11.7", "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "^5", From 56b23a4a10b01a97497b08c9909f98dbf15867f9 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 7 Mar 2016 14:01:05 -0800 Subject: [PATCH 427/971] Make cli atom --wait work on Cygwin --- atom.sh | 2 -- resources/win/atom.sh | 19 ++++++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/atom.sh b/atom.sh index ef8dbcdc4..b68716bf4 100755 --- a/atom.sh +++ b/atom.sh @@ -4,8 +4,6 @@ if [ "$(uname)" == 'Darwin' ]; then OS='Mac' elif [ "$(expr substr $(uname -s) 1 5)" == 'Linux' ]; then OS='Linux' -elif [ "$(expr substr $(uname -s) 1 10)" == 'MINGW32_NT' ]; then - OS='Cygwin' else echo "Your platform ($(uname -a)) is not supported." exit 1 diff --git a/resources/win/atom.sh b/resources/win/atom.sh index 0eaf193c0..7f1b1e093 100644 --- a/resources/win/atom.sh +++ b/resources/win/atom.sh @@ -6,6 +6,7 @@ while getopts ":fhtvw-:" opt; do case "${OPTARG}" in wait) WAIT=1 + EXPECT_OUTPUT=1 ;; help|version) REDIRECT_STDERR=1 @@ -18,6 +19,7 @@ while getopts ":fhtvw-:" opt; do ;; w) WAIT=1 + EXPECT_OUTPUT=1 ;; h|v) REDIRECT_STDERR=1 @@ -31,19 +33,14 @@ done directory=$(dirname "$0") -WINPS=`ps | grep -i $$` -PID=`echo $WINPS | cut -d' ' -f 4` - if [ $EXPECT_OUTPUT ]; then export ELECTRON_ENABLE_LOGGING=1 - "$directory/../../atom.exe" --executed-from="$(pwd)" --pid=$PID "$@" + if [ $WAIT == 'YES' ]; then + powershell -noexit "%~dp0\..\..\atom.exe" --pid=$pid "$@" ; +wait-event + else + "$directory/../../atom.exe" "$@" + fi else "$directory/../app/apm/bin/node.exe" "$directory/atom.js" "$@" fi - -# If the wait flag is set, don't exit this process until Atom tells it to. -if [ $WAIT ]; then - while true; do - sleep 1 - done -fi From b53c5a10d00db421abf84116d824addbd9a7c9ee Mon Sep 17 00:00:00 2001 From: Rowan Bottema Date: Tue, 8 Mar 2016 10:18:43 +0100 Subject: [PATCH 428/971] Add zero to hexadecimal numbers below F (16) --- spec/config-spec.coffee | 10 ++++++++++ src/color.coffee | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index c7485ea65..acd9b112b 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -1621,6 +1621,16 @@ describe "Config", -> expect(color.toHexString()).toBe '#ff0000' expect(color.toRGBAString()).toBe 'rgba(255, 0, 0, 1)' + color.red = 11 + color.green = 11 + color.blue = 124 + color.alpha = 1 + atom.config.set('foo.bar.aColor', color) + + color = atom.config.get('foo.bar.aColor') + expect(color.toHexString()).toBe '#0b0b7c' + expect(color.toRGBAString()).toBe 'rgba(11, 11, 124, 1)' + it 'coerces various types to a color object', -> atom.config.set('foo.bar.aColor', 'red') expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1} diff --git a/src/color.coffee b/src/color.coffee index fc751ce42..b413b9e2c 100644 --- a/src/color.coffee +++ b/src/color.coffee @@ -85,5 +85,5 @@ parseAlpha = (alpha) -> numberToHexString = (number) -> hex = number.toString(16) - hex = "0#{hex}" if number < 10 + hex = "0#{hex}" if number < 16 hex From 2f401c7116ee7103adbdc39c0968c1328e9e81e5 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 8 Mar 2016 12:14:26 -0500 Subject: [PATCH 429/971] We should emit changes when anything changes. --- spec/git-repository-async-spec.js | 38 +++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index e7bffad3a..9144f8611 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -422,6 +422,44 @@ describe('GitRepositoryAsync', () => { expect(repo.isStatusModified(status)).toBe(true) expect(repo.isStatusNew(status)).toBe(false) }) + + it('emits did-change-statuses if the status changes', async () => { + const someNewPath = path.join(workingDirectory, 'MyNewJSFramework.md') + fs.writeFileSync(someNewPath, '') + + const statusHandler = jasmine.createSpy('statusHandler') + repo.onDidChangeStatuses(statusHandler) + + await repo.refreshStatus() + + waitsFor('the onDidChangeStatuses handler to be called', () => statusHandler.callCount > 0) + }) + + it('emits did-change-statuses if the branch changes', async () => { + const statusHandler = jasmine.createSpy('statusHandler') + repo.onDidChangeStatuses(statusHandler) + + repo._refreshBranch = jasmine.createSpy('_refreshBranch').andCallFake(() => { + return Promise.resolve(true) + }) + + await repo.refreshStatus() + + waitsFor('the onDidChangeStatuses handler to be called', () => statusHandler.callCount > 0) + }) + + it('emits did-change-statuses if the ahead/behind changes', async () => { + const statusHandler = jasmine.createSpy('statusHandler') + repo.onDidChangeStatuses(statusHandler) + + repo._refreshAheadBehindCount = jasmine.createSpy('_refreshAheadBehindCount').andCallFake(() => { + return Promise.resolve(true) + }) + + await repo.refreshStatus() + + waitsFor('the onDidChangeStatuses handler to be called', () => statusHandler.callCount > 0) + }) }) describe('.isProjectAtRoot()', () => { From f2be54bf299355142b4a9d79a3ea8fda19964986 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 8 Mar 2016 12:14:42 -0500 Subject: [PATCH 430/971] But actually do it tho. --- src/git-repository-async.js | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 91b78e0c5..642ed5a98 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -783,12 +783,17 @@ export default class GitRepositoryAsync { // Get the current branch and update this.branch. // - // Returns a {Promise} which resolves to the {String} branch name. + // Returns a {Promise} which resolves to a {boolean} indicating whether the + // branch name changed. _refreshBranch () { return this.getRepo() .then(repo => repo.getCurrentBranch()) .then(ref => ref.name()) - .then(branchName => this.branch = branchName) + .then(branchName => { + const changed = branchName !== this.branch + this.branch = branchName + return changed + }) } // Refresh the cached ahead/behind count with the given branch. @@ -796,10 +801,15 @@ export default class GitRepositoryAsync { // * `branchName` The {String} name of the branch whose ahead/behind should be // used for the refresh. // - // Returns a {Promise} which will resolve to {null}. + // Returns a {Promise} which will resolve to a {boolean} indicating whether + // the ahead/behind count changed. _refreshAheadBehindCount (branchName) { return this.getAheadBehindCount(branchName) - .then(counts => this.upstream = counts) + .then(counts => { + const changed = !_.isEqual(counts, this.upstream) + this.upstream = counts + return changed + }) } // Get the status for this repository. @@ -905,15 +915,15 @@ export default class GitRepositoryAsync { // Refresh the cached status. // - // Returns a {Promise} which will resolve to {null}. + // Returns a {Promise} which will resolve to a {boolean} indicating whether + // any statuses changed. _refreshStatus () { return Promise.all([this._getRepositoryStatus(), this._getSubmoduleStatuses()]) .then(([repositoryStatus, submoduleStatus]) => { const statusesByPath = _.extend({}, repositoryStatus, submoduleStatus) - if (!_.isEqual(this.pathStatusCache, statusesByPath) && this.emitter != null) { - this.emitter.emit('did-change-statuses') - } + const changed = !_.isEqual(this.pathStatusCache, statusesByPath) this.pathStatusCache = statusesByPath + return changed }) } @@ -927,7 +937,13 @@ export default class GitRepositoryAsync { this._refreshingPromise = this._refreshingPromise.then(_ => { return Promise.all([status, branch, aheadBehind]) - .then(_ => null) + .then(([statusChanged, branchChanged, aheadBehindChanged]) => { + if (this.emitter && (statusChanged || branchChanged || aheadBehindChanged)) { + this.emitter.emit('did-change-statuses') + } + + return null + }) // Because all these refresh steps happen asynchronously, it's entirely // possible the repository was destroyed while we were working. In which // case we should just swallow the error. From cb44450e923ec6040c4b7151d0106fd2dbd52f41 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 8 Mar 2016 12:40:29 -0500 Subject: [PATCH 431/971] Correct the spec name. --- spec/git-repository-async-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 9144f8611..900d81bfb 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -714,7 +714,7 @@ describe('GitRepositoryAsync', () => { repo = GitRepositoryAsync.open(workingDirectory) }) - it('returns 0, 0 for a branch with no upstream', async () => { + it('returns 1, 0 for a branch which is ahead by 1', async () => { await repo.refreshStatus() const {ahead, behind} = await repo.getCachedUpstreamAheadBehindCount('You-Dont-Need-jQuery') From fc62398d62b950c6d84f5a6836a5e781a868d9f7 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 8 Mar 2016 12:40:38 -0500 Subject: [PATCH 432/971] Use the new branch name. --- src/git-repository-async.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 642ed5a98..f80f46a13 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -933,7 +933,7 @@ export default class GitRepositoryAsync { refreshStatus () { const status = this._refreshStatus() const branch = this._refreshBranch() - const aheadBehind = branch.then(branchName => this._refreshAheadBehindCount(branchName)) + const aheadBehind = branch.then(() => this._refreshAheadBehindCount(this.branch)) this._refreshingPromise = this._refreshingPromise.then(_ => { return Promise.all([status, branch, aheadBehind]) From 42c6d5c2bcb3c6b1d91a03bf92dd015b0b2cd4f6 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Tue, 8 Mar 2016 18:47:42 +0100 Subject: [PATCH 433/971] :arrow_up:symbols-view@0.112.0 - adds es7 async functions to js --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 83864702d..0353c9957 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "spell-check": "0.67.0", "status-bar": "1.1.0", "styleguide": "0.45.2", - "symbols-view": "0.111.1", + "symbols-view": "0.112.0", "tabs": "0.91.3", "timecop": "0.33.1", "tree-view": "0.202.0", From 52d6fbde1b9a4f869c8063972357ea19abce060f Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 8 Mar 2016 12:12:52 -0800 Subject: [PATCH 434/971] More reliable to just call atom.cmd for Cygwin --- resources/win/atom.sh | 46 +------------------------------------------ 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/resources/win/atom.sh b/resources/win/atom.sh index 7f1b1e093..7380bf122 100644 --- a/resources/win/atom.sh +++ b/resources/win/atom.sh @@ -1,46 +1,2 @@ #!/bin/sh - -while getopts ":fhtvw-:" opt; do - case "$opt" in - -) - case "${OPTARG}" in - wait) - WAIT=1 - EXPECT_OUTPUT=1 - ;; - help|version) - REDIRECT_STDERR=1 - EXPECT_OUTPUT=1 - ;; - foreground|test) - EXPECT_OUTPUT=1 - ;; - esac - ;; - w) - WAIT=1 - EXPECT_OUTPUT=1 - ;; - h|v) - REDIRECT_STDERR=1 - EXPECT_OUTPUT=1 - ;; - f|t) - EXPECT_OUTPUT=1 - ;; - esac -done - -directory=$(dirname "$0") - -if [ $EXPECT_OUTPUT ]; then - export ELECTRON_ENABLE_LOGGING=1 - if [ $WAIT == 'YES' ]; then - powershell -noexit "%~dp0\..\..\atom.exe" --pid=$pid "$@" ; -wait-event - else - "$directory/../../atom.exe" "$@" - fi -else - "$directory/../app/apm/bin/node.exe" "$directory/atom.js" "$@" -fi +$(dirname "$0")/atom.cmd "$@" From bf5dea0a27953beafa40c12943b1fff6d7c2b09f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Mar 2016 19:25:52 -0700 Subject: [PATCH 435/971] :arrow_up: fuzzy-finder --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0353c9957..59264c3a5 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", "find-and-replace": "0.197.4", - "fuzzy-finder": "1.0.2", + "fuzzy-finder": "1.0.3", "git-diff": "1.0.0", "go-to-line": "0.30.0", "grammar-selector": "0.48.1", From 152e370a15e65a09039ddf82f1528563d9eb94c4 Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Wed, 9 Mar 2016 02:16:10 -0800 Subject: [PATCH 436/971] Default the options parameter to an empty object --- src/project.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/project.coffee b/src/project.coffee index 5f3420b08..93a3ed496 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -71,7 +71,7 @@ class Project extends Model @subscribeToBuffer(buffer) for buffer in @buffers @setPaths(state.paths) - serialize: (options) -> + serialize: (options={}) -> deserializer: 'Project' paths: @getPaths() buffers: _.compact(@buffers.map (buffer) -> buffer.serialize({markerLayers: options.isUnloading is true}) if buffer.isRetained()) From 129139f03a2846506d87dfa393d9d0b525e75757 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 9 Mar 2016 12:43:28 -0500 Subject: [PATCH 437/971] :arrow_up: nodegit@0.11.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 59264c3a5..a1fa3e820 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "less-cache": "0.23", "line-top-index": "0.2.0", "marked": "^0.3.4", - "nodegit": "0.11.7", + "nodegit": "0.11.9", "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "^5", From a04c552fb710c3b147a55d824f260beac6cb3e34 Mon Sep 17 00:00:00 2001 From: Thomas Johansen Date: Wed, 9 Mar 2016 19:22:23 +0100 Subject: [PATCH 438/971] :arrow_up: language-text --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 59264c3a5..c96d52b72 100644 --- a/package.json +++ b/package.json @@ -144,7 +144,7 @@ "language-shellscript": "0.21.0", "language-source": "0.9.0", "language-sql": "0.20.0", - "language-text": "0.7.0", + "language-text": "0.7.1", "language-todo": "0.27.0", "language-toml": "0.18.0", "language-xml": "0.34.4", From ac35fec0cd882394d0ba22a98d1d7280e96a1f5d Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 9 Mar 2016 17:26:47 -0800 Subject: [PATCH 439/971] :arrow_up: tree-view@0.203.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8bae63604..6b449d0de 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.112.0", "tabs": "0.91.3", "timecop": "0.33.1", - "tree-view": "0.202.0", + "tree-view": "0.203.0", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From 4b017759c9b053595e056d9ca409efee8dc81ff9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 10 Mar 2016 09:40:26 +0100 Subject: [PATCH 440/971] Ensure project.serialize is called with atom.saveState options This is because we have made the `project.serialize(options)` parameter optional for backwards compatibility (i.e. #11111), and we want to make sure we don't make the mistake of not passing it internally. --- spec/atom-environment-spec.coffee | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 7488c8e05..3283b63d6 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -203,6 +203,14 @@ describe "AtomEnvironment", -> expect(atom.saveState).toHaveBeenCalledWith({isUnloading: true}) expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: false}) + it "serializes the project state with all the options supplied in saveState", -> + spyOn(atom.project, 'serialize').andReturn({foo: 42}) + + waitsForPromise -> atom.saveState({anyOption: 'any option'}) + runs -> + expect(atom.project.serialize.calls.length).toBe(1) + expect(atom.project.serialize.mostRecentCall.args[0]).toEqual({anyOption: 'any option'}) + describe "openInitialEmptyEditorIfNecessary", -> describe "when there are no paths set", -> beforeEach -> From 40beb0bd1e4a65ec9fea84b3674b471b197b3796 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 10 Mar 2016 16:00:56 +0100 Subject: [PATCH 441/971] Use new TextBuffer APIs --- src/display-buffer.coffee | 164 ++++++++++++++++--------------- src/text-editor-presenter.coffee | 80 +++++---------- 2 files changed, 111 insertions(+), 133 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index cba8a6551..500cffcfb 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -428,13 +428,13 @@ class DisplayBuffer extends Model if @largeFileMode bufferRow else - @rowMap.screenRowRangeForBufferRow(bufferRow)[0] + @displayLayer.translateScreenPosition(Point(screenRow, 0)).row lastScreenRowForBufferRow: (bufferRow) -> if @largeFileMode bufferRow else - @rowMap.screenRowRangeForBufferRow(bufferRow)[1] - 1 + @displayLayer.translateScreenPosition(Point(screenRow, 0), clip: 'forward').row # Given a screen row, this converts it into a buffer row. # @@ -502,33 +502,35 @@ class DisplayBuffer extends Model screenPositionForBufferPosition: (bufferPosition, options) -> throw new Error("This TextEditor has been destroyed") if @isDestroyed() - {row, column} = @buffer.clipPosition(bufferPosition) - [startScreenRow, endScreenRow] = @rowMap.screenRowRangeForBufferRow(row) - for screenRow in [startScreenRow...endScreenRow] - screenLine = @tokenizedLineForScreenRow(screenRow) - - unless screenLine? - throw new BufferToScreenConversionError "No screen line exists when converting buffer row to screen row", - softWrapEnabled: @isSoftWrapped() - lastBufferRow: @buffer.getLastRow() - lastScreenRow: @getLastRow() - bufferRow: row - screenRow: screenRow - displayBufferChangeCount: @changeCount - tokenizedBufferChangeCount: @tokenizedBuffer.changeCount - bufferChangeCount: @buffer.changeCount - - maxBufferColumn = screenLine.getMaxBufferColumn() - if screenLine.isSoftWrapped() and column > maxBufferColumn - continue - else - if column <= maxBufferColumn - screenColumn = screenLine.screenColumnForBufferColumn(column) - else - screenColumn = Infinity - break - - @clipScreenPosition([screenRow, screenColumn], options) + return @displayLayer.translateBufferPosition(bufferPosition, options) + # TODO: should DisplayLayer deal with options.wrapBeyondNewlines / options.wrapAtSoftNewlines? + # {row, column} = @buffer.clipPosition(bufferPosition) + # [startScreenRow, endScreenRow] = @rowMap.screenRowRangeForBufferRow(row) + # for screenRow in [startScreenRow...endScreenRow] + # screenLine = @tokenizedLineForScreenRow(screenRow) + # + # unless screenLine? + # throw new BufferToScreenConversionError "No screen line exists when converting buffer row to screen row", + # softWrapEnabled: @isSoftWrapped() + # lastBufferRow: @buffer.getLastRow() + # lastScreenRow: @getLastRow() + # bufferRow: row + # screenRow: screenRow + # displayBufferChangeCount: @changeCount + # tokenizedBufferChangeCount: @tokenizedBuffer.changeCount + # bufferChangeCount: @buffer.changeCount + # + # maxBufferColumn = screenLine.getMaxBufferColumn() + # if screenLine.isSoftWrapped() and column > maxBufferColumn + # continue + # else + # if column <= maxBufferColumn + # screenColumn = screenLine.screenColumnForBufferColumn(column) + # else + # screenColumn = Infinity + # break + # + # @clipScreenPosition([screenRow, screenColumn], options) # Given a buffer position, this converts it into a screen position. # @@ -540,9 +542,11 @@ class DisplayBuffer extends Model # # Returns a {Point}. bufferPositionForScreenPosition: (screenPosition, options) -> - {row, column} = @clipScreenPosition(Point.fromObject(screenPosition), options) - [bufferRow] = @rowMap.bufferRowRangeForScreenRow(row) - new Point(bufferRow, @tokenizedLineForScreenRow(row).bufferColumnForScreenColumn(column)) + return @displayLayer.translateScreenPosition(screenPosition, options) + # TODO: should DisplayLayer deal with options.wrapBeyondNewlines / options.wrapAtSoftNewlines? + # {row, column} = @clipScreenPosition(Point.fromObject(screenPosition), options) + # [bufferRow] = @rowMap.bufferRowRangeForScreenRow(row) + # new Point(bufferRow, @tokenizedLineForScreenRow(row).bufferColumnForScreenColumn(column)) # Retrieves the grammar's token scopeDescriptor for a buffer position. # @@ -594,53 +598,55 @@ class DisplayBuffer extends Model # # Returns the new, clipped {Point}. Note that this could be the same as `position` if no clipping was performed. clipScreenPosition: (screenPosition, options={}) -> - {wrapBeyondNewlines, wrapAtSoftNewlines, skipSoftWrapIndentation} = options - {row, column} = Point.fromObject(screenPosition) - - if row < 0 - row = 0 - column = 0 - else if row > @getLastRow() - row = @getLastRow() - column = Infinity - else if column < 0 - column = 0 - - screenLine = @tokenizedLineForScreenRow(row) - unless screenLine? - error = new Error("Undefined screen line when clipping screen position") - Error.captureStackTrace(error) - error.metadata = { - screenRow: row - screenColumn: column - maxScreenRow: @getLastRow() - screenLinesDefined: @screenLines.map (sl) -> sl? - displayBufferChangeCount: @changeCount - tokenizedBufferChangeCount: @tokenizedBuffer.changeCount - bufferChangeCount: @buffer.changeCount - } - throw error - - maxScreenColumn = screenLine.getMaxScreenColumn() - - if screenLine.isSoftWrapped() and column >= maxScreenColumn - if wrapAtSoftNewlines - row++ - column = @tokenizedLineForScreenRow(row).clipScreenColumn(0) - else - column = screenLine.clipScreenColumn(maxScreenColumn - 1) - else if screenLine.isColumnInsideSoftWrapIndentation(column) - if skipSoftWrapIndentation - column = screenLine.clipScreenColumn(0) - else - row-- - column = @tokenizedLineForScreenRow(row).getMaxScreenColumn() - 1 - else if wrapBeyondNewlines and column > maxScreenColumn and row < @getLastRow() - row++ - column = 0 - else - column = screenLine.clipScreenColumn(column, options) - new Point(row, column) + return @displayLayer.clipScreenPosition(screenPosition, options) + # TODO: should DisplayLayer deal with options.wrapBeyondNewlines / options.wrapAtSoftNewlines? + # {wrapBeyondNewlines, wrapAtSoftNewlines, skipSoftWrapIndentation} = options + # {row, column} = Point.fromObject(screenPosition) + # + # if row < 0 + # row = 0 + # column = 0 + # else if row > @getLastRow() + # row = @getLastRow() + # column = Infinity + # else if column < 0 + # column = 0 + # + # screenLine = @tokenizedLineForScreenRow(row) + # unless screenLine? + # error = new Error("Undefined screen line when clipping screen position") + # Error.captureStackTrace(error) + # error.metadata = { + # screenRow: row + # screenColumn: column + # maxScreenRow: @getLastRow() + # screenLinesDefined: @screenLines.map (sl) -> sl? + # displayBufferChangeCount: @changeCount + # tokenizedBufferChangeCount: @tokenizedBuffer.changeCount + # bufferChangeCount: @buffer.changeCount + # } + # throw error + # + # maxScreenColumn = screenLine.getMaxScreenColumn() + # + # if screenLine.isSoftWrapped() and column >= maxScreenColumn + # if wrapAtSoftNewlines + # row++ + # column = @tokenizedLineForScreenRow(row).clipScreenColumn(0) + # else + # column = screenLine.clipScreenColumn(maxScreenColumn - 1) + # else if screenLine.isColumnInsideSoftWrapIndentation(column) + # if skipSoftWrapIndentation + # column = screenLine.clipScreenColumn(0) + # else + # row-- + # column = @tokenizedLineForScreenRow(row).getMaxScreenColumn() - 1 + # else if wrapBeyondNewlines and column > maxScreenColumn and row < @getLastRow() + # row++ + # column = 0 + # else + # column = screenLine.clipScreenColumn(column, options) + # new Point(row, column) # Clip the start and end of the given range to valid positions on screen. # See {::clipScreenPosition} for more information. diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index ab471684e..2fef4ca14 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -17,7 +17,7 @@ class TextEditorPresenter {@model, @config, @lineTopIndex, scrollPastEnd} = params {@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @tileSize} = params {@contentFrameWidth} = params - @tokenIterator = @model.displayLayer.buildTokenIterator() + {@displayLayer} = @model @gutterWidth = 0 @tileSize ?= 6 @@ -25,9 +25,7 @@ class TextEditorPresenter @realScrollLeft = @scrollLeft @disposables = new CompositeDisposable @emitter = new Emitter - @lineIdCounter = 1 - @linesById = new Map - @lineMarkerIndex = new MarkerIndex + @linesByScreenRow = new Map @visibleHighlights = {} @characterWidthsByScope = {} @lineDecorationsByScreenRow = {} @@ -141,7 +139,6 @@ class TextEditorPresenter observeModel: -> @disposables.add @model.displayLayer.onDidChangeSync (changes) => for change in changes - @invalidateLines(change) startRow = change.start.row endRow = startRow + change.oldExtent.row @spliceBlockDecorationsInRange(startRow, endRow, change.newExtent.row - change.oldExtent.row) @@ -316,7 +313,7 @@ class TextEditorPresenter isValidScreenRow: (screenRow) -> screenRow >= 0 and screenRow < @model.getScreenLineCount() - getScreenRows: -> + getScreenRowsToRender: -> startRow = @getStartTileRow() endRow = @constrainRow(@getEndTileRow() + @tileSize) @@ -331,6 +328,22 @@ class TextEditorPresenter screenRows.sort (a, b) -> a - b _.uniq(screenRows, true) + getScreenRangesToRender: -> + screenRows = @getScreenRowsToRender() + screenRows.push(Infinity) # makes the loop below inclusive + + startRow = screenRows[0] + endRow = startRow - 1 + screenRanges = [] + for row in screenRows + if row is endRow + 1 + endRow++ + else + screenRanges.push([startRow, endRow]) + startRow = endRow = row + + screenRanges + setScreenRowsToMeasure: (screenRows) -> return if not screenRows? or screenRows.length is 0 @@ -343,7 +356,7 @@ class TextEditorPresenter updateTilesState: -> return unless @startRow? and @endRow? and @lineHeight? - screenRows = @getScreenRows() + screenRows = @getScreenRowsToRender() visibleTiles = {} startRow = screenRows[0] endRow = screenRows[screenRows.length - 1] @@ -404,7 +417,7 @@ class TextEditorPresenter tileState.lines ?= {} visibleLineIds = {} for screenRow in screenRows - line = @lineForScreenRow(screenRow) + line = @linesByScreenRow.get(screenRow) unless line? throw new Error("No line exists for row #{screenRow}. Last screen row: #{@model.getLastScreenRow()}") @@ -616,7 +629,7 @@ class TextEditorPresenter softWrapped = false screenRow = startRow + i - lineId = @lineIdForScreenRow(screenRow) + lineId = @linesByScreenRow.get(screenRow).id decorationClasses = @lineNumberDecorationClassesForRow(screenRow) blockDecorationsBeforeCurrentScreenRowHeight = @lineTopIndex.pixelPositionAfterBlocksForRow(screenRow) - @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRow) blockDecorationsHeight = blockDecorationsBeforeCurrentScreenRowHeight @@ -1057,48 +1070,11 @@ class TextEditorPresenter rect updateLines: -> - visibleLineIds = new Set + @linesByScreenRow.clear() - for screenRow in @getScreenRows() - screenRowStart = Point(screenRow, 0) - lineIds = @lineMarkerIndex.findStartingAt(screenRowStart) - if lineIds.size is 0 - line = @buildLine(screenRow) - @lineMarkerIndex.insert(line.id, screenRowStart, Point(screenRow, Infinity)) - @linesById.set(line.id, line) - visibleLineIds.add(line.id) - else - lineIds.forEach (id) -> - visibleLineIds.add(id) - - @linesById.forEach (line, lineId) => - unless visibleLineIds.has(lineId) - @lineMarkerIndex.delete(lineId) - @linesById.delete(lineId) - - buildLine: (screenRow) -> - line = {id: @lineIdCounter++, tokens: []} - @tokenIterator.seekToScreenRow(screenRow) - loop - line.tokens.push({ - text: @tokenIterator.getText(), - closeTags: @tokenIterator.getCloseTags(), - openTags: @tokenIterator.getOpenTags() - }) - break unless @tokenIterator.moveToSuccessor() - break unless @tokenIterator.getStartScreenPosition().row is screenRow - line - - invalidateLines: ({start, oldExtent, newExtent}) -> - {touch} = @lineMarkerIndex.splice(start, oldExtent, newExtent) - touch.forEach (lineId) => - @lineMarkerIndex.delete(lineId) - @linesById.delete(lineId) - - lineForScreenRow: (screenRow) -> - lineIds = @lineMarkerIndex.findStartingAt(Point(screenRow, 0)) - lineId = lineIds.values().next().value - @linesById.get(lineId) + for [startRow, endRow] in @getScreenRangesToRender() + for line, index in @displayLayer.getScreenLines(startRow, endRow + 1) + @linesByScreenRow.set(startRow + index, line) fetchDecorations: -> return unless 0 <= @startRow <= @endRow <= Infinity @@ -1571,7 +1547,3 @@ class TextEditorPresenter isRowVisible: (row) -> @startRow <= row < @endRow - - lineIdForScreenRow: (screenRow) -> - ids = @lineMarkerIndex.findStartingAt(Point(screenRow, 0)) - ids.values().next().value if ids.size > 0 From 0d7b182c2dba7daedc605ee4a65ba2c892842c5a Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 10 Mar 2016 10:31:24 -0500 Subject: [PATCH 442/971] :arrow_up: status-bar@1.1.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6b449d0de..353e470be 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.232.4", "snippets": "1.0.1", "spell-check": "0.67.0", - "status-bar": "1.1.0", + "status-bar": "1.1.1", "styleguide": "0.45.2", "symbols-view": "0.112.0", "tabs": "0.91.3", From 8c3ab52b6436ec22aa515d3b1ff32e349339f46d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 10 Mar 2016 19:17:28 +0100 Subject: [PATCH 443/971] Pass showIndentGuides config to DisplayLayer --- src/display-buffer.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 500cffcfb..ee4e9be77 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -51,7 +51,7 @@ class DisplayBuffer extends Model @grammarRegistry, @packageManager, @assert }) @buffer = @tokenizedBuffer.buffer - @displayLayer = @buffer.addDisplayLayer({tabLength: @getTabLength(), invisibles: @config.get('editor.invisibles')}) + @displayLayer = @buffer.addDisplayLayer({tabLength: @getTabLength(), invisibles: @config.get('editor.invisibles'), showIndentGuides: @config.get('editor.showIndentGuide')}) @displayLayer.setTextDecorationLayer(@tokenizedBuffer) @charWidthsByScope = {} @defaultMarkerLayer = @displayLayer.addMarkerLayer() From 501eadccca0c8aaad9f7b027c596a87e2bcd873a Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 10 Mar 2016 11:00:48 -0800 Subject: [PATCH 444/971] :arrow_up: notifications@0.62.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 353e470be..d2cb82195 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "link": "0.31.0", "markdown-preview": "0.157.3", "metrics": "0.53.1", - "notifications": "0.62.3", + "notifications": "0.62.4", "open-on-github": "1.0.0", "package-generator": "0.41.1", "settings-view": "0.232.4", From 3201d7f7469811f7ca7d38810e0bf2900dd90cd6 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 3 Mar 2016 21:15:52 -0800 Subject: [PATCH 445/971] Add basic atom.project.getEnv API --- spec/fixtures/sample.txt | 2 +- spec/project-spec.coffee | 53 ++++++++++++++++++++++++++++++++++++++++ src/project.coffee | 39 +++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/spec/fixtures/sample.txt b/spec/fixtures/sample.txt index 9701a96c5..0a2b6a502 100644 --- a/spec/fixtures/sample.txt +++ b/spec/fixtures/sample.txt @@ -1 +1 @@ -Some textSome textSome text. +Some textSome textSome textSome text. diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 499efd017..61dd244e8 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -537,3 +537,56 @@ describe "Project", -> randomPath = path.join("some", "random", "path") expect(atom.project.contains(randomPath)).toBe false + + describe ".getEnv", -> + afterEach -> + delete atom.project.env + + it "returns a copy of the environment", -> + env = atom.project.getEnv() + + env.PROJECT_GET_ENV_TESTING = "foo" + expect(process.env.PROJECT_GET_ENV_TESTING).not.toEqual "foo" + expect(atom.project.getEnv().PROJECT_GET_ENV_TESTING).not.toEqual "foo" + + describe "on platforms other than OS X", -> + beforeEach -> + spyOn(process, "platform").andReturn("foo") + + describe "when TERM is not set", -> + it "returns the PATH unchanged", -> + spyOn(process.env, "TERM").andReturn(undefined) + + expect(atom.project.getEnv().PATH).toEqual process.env.PATH + + describe "when TERM is set", -> + it "returns the PATH unchanged", -> + spyOn(process.env, "TERM").andReturn("foo") + + expect(atom.project.getEnv().PATH).toEqual process.env.PATH + + describe "on OS X", -> + beforeEach -> + spyOn(process, "platform").andReturn("darwin") + + describe "when TERM is not set", -> + it "replaces the PATH with the one obtained from the shell", -> + env = _.clone(process.env) + delete env.TERM + + spyOn(process, "env").andReturn(env) + + spyOn(atom.project, "getShellEnv").andReturn """ + FOO=BAR + TERM=xterm-something + PATH=/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist + """ + + expect(atom.project.getShellPath()).toEqual "/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist" + expect(atom.project.getEnv().PATH).toEqual "/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist" + + describe "when TERM is set", -> + it "returns the PATH unchanged", -> + spyOn(process.env, "TERM").andReturn("foo") + + expect(atom.project.getEnv().PATH).toEqual process.env.PATH diff --git a/src/project.coffee b/src/project.coffee index 93a3ed496..f2ace97e2 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -12,6 +12,9 @@ TextEditor = require './text-editor' Task = require './task' GitRepositoryProvider = require './git-repository-provider' +child_process = require 'child_process' +os = require 'os' + # Extended: Represents a project that's opened in Atom. # # An instance of this class is always available as the `atom.project` global. @@ -271,6 +274,42 @@ class Project extends Model contains: (pathToCheck) -> @rootDirectories.some (dir) -> dir.contains(pathToCheck) + ### + Section: Environment + ### + + getEnv: -> + unless @env? + @env = _.clone(process.env) + if process.platform is "darwin" and not process.env.TERM? + @env.PATH = @getShellPath() + + _.clone(@env) + + getShellPath: -> + shellEnvText = @getShellEnv() + env = {} + + for line in shellEnvText.split(os.EOL) + if line.includes("=") + components = line.split("=") + if components.length is 2 + env[components[0]] = components[1] + else + k = components.shift() + v = components.join("=") + env[k] = v + + env.PATH + + getShellEnv: -> + shell = process.env.SHELL ? "/bin/bash" + results = child_process.spawnSync shell, ["--login"], input: "env", encoding: "utf8" + return if results.error? + return unless results.stdout and results.stdout.length > 0 + + results.stdout + ### Section: Private ### From a46740522d06ee9aa2af5ebde920c09a6871aa9a Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 3 Mar 2016 21:21:26 -0800 Subject: [PATCH 446/971] :memo: Add documentation for atom.project.getEnv --- src/project.coffee | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/project.coffee b/src/project.coffee index f2ace97e2..4f5dc37bc 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -278,6 +278,12 @@ class Project extends Model Section: Environment ### + # Public: Retrieves a normalized copy of the environment. + # + # On OS X, the `PATH` can be different depending on whether Atom is launched + # from the Dock, Spotlight or the terminal. This detects how Atom was started + # and corrects the `PATH` environment variable before returning a copy of the + # environment. getEnv: -> unless @env? @env = _.clone(process.env) From 2e64be6b000e7aecba237763baf69147c71441a5 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 3 Mar 2016 22:18:39 -0800 Subject: [PATCH 447/971] Move new functions to private section --- src/project.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/project.coffee b/src/project.coffee index 4f5dc37bc..d91ec3a39 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -292,6 +292,10 @@ class Project extends Model _.clone(@env) + ### + Section: Private + ### + getShellPath: -> shellEnvText = @getShellEnv() env = {} @@ -316,10 +320,6 @@ class Project extends Model results.stdout - ### - Section: Private - ### - consumeServices: ({serviceHub}) -> serviceHub.consume( 'atom.directory-provider', From 7f6fd92c7a859b573677b8fbacc907ba63c76c1d Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Mon, 7 Mar 2016 14:19:32 -0800 Subject: [PATCH 448/971] :fire: Revert change to test fixture --- spec/fixtures/sample.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/fixtures/sample.txt b/spec/fixtures/sample.txt index 0a2b6a502..9701a96c5 100644 --- a/spec/fixtures/sample.txt +++ b/spec/fixtures/sample.txt @@ -1 +1 @@ -Some textSome textSome textSome text. +Some textSome textSome text. From 9916c78ce23ffb8dde835517d9b37c2e20f38259 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Mon, 7 Mar 2016 14:27:19 -0800 Subject: [PATCH 449/971] Add --interactive option to get the user's full PATH with all the trimmings --- src/project.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/project.coffee b/src/project.coffee index d91ec3a39..e13942f28 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -314,7 +314,7 @@ class Project extends Model getShellEnv: -> shell = process.env.SHELL ? "/bin/bash" - results = child_process.spawnSync shell, ["--login"], input: "env", encoding: "utf8" + results = child_process.spawnSync shell, ["--login", "--interactive"], input: "env", encoding: "utf8" return if results.error? return unless results.stdout and results.stdout.length > 0 From 96b8f0ce38d4870672f89276450ae27da82dd043 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Mon, 7 Mar 2016 14:28:30 -0800 Subject: [PATCH 450/971] :memo: Add Finder to the list of ways an app can be launched --- src/project.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/project.coffee b/src/project.coffee index e13942f28..5d5448dc7 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -281,9 +281,9 @@ class Project extends Model # Public: Retrieves a normalized copy of the environment. # # On OS X, the `PATH` can be different depending on whether Atom is launched - # from the Dock, Spotlight or the terminal. This detects how Atom was started - # and corrects the `PATH` environment variable before returning a copy of the - # environment. + # from the Dock, Finder, Spotlight or the terminal. This detects how Atom was + # started and corrects the `PATH` environment variable before returning a copy + # of the environment. getEnv: -> unless @env? @env = _.clone(process.env) From 6bf785faa0b5f795e33d53b76eeec93b504f7c72 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Mon, 7 Mar 2016 15:42:11 -0800 Subject: [PATCH 451/971] :white_check_mark: Add test for error handling --- spec/project-spec.coffee | 37 ++++++++++++++++++++++++++----------- src/project.coffee | 19 +++++++++++++++++-- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 61dd244e8..35cd0157f 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -539,7 +539,13 @@ describe "Project", -> expect(atom.project.contains(randomPath)).toBe false describe ".getEnv", -> + [originalTerm] = [] + + beforeEach -> + originalTerm = process.env.TERM + afterEach -> + process.env.TERM = originalTerm delete atom.project.env it "returns a copy of the environment", -> @@ -554,15 +560,17 @@ describe "Project", -> spyOn(process, "platform").andReturn("foo") describe "when TERM is not set", -> - it "returns the PATH unchanged", -> - spyOn(process.env, "TERM").andReturn(undefined) + beforeEach -> + delete process.env.TERM + it "returns the PATH unchanged", -> expect(atom.project.getEnv().PATH).toEqual process.env.PATH describe "when TERM is set", -> - it "returns the PATH unchanged", -> - spyOn(process.env, "TERM").andReturn("foo") + beforeEach -> + process.env.TERM = "foo" + it "returns the PATH unchanged", -> expect(atom.project.getEnv().PATH).toEqual process.env.PATH describe "on OS X", -> @@ -570,12 +578,10 @@ describe "Project", -> spyOn(process, "platform").andReturn("darwin") describe "when TERM is not set", -> + beforeEach -> + delete process.env.TERM + it "replaces the PATH with the one obtained from the shell", -> - env = _.clone(process.env) - delete env.TERM - - spyOn(process, "env").andReturn(env) - spyOn(atom.project, "getShellEnv").andReturn """ FOO=BAR TERM=xterm-something @@ -584,9 +590,18 @@ describe "Project", -> expect(atom.project.getShellPath()).toEqual "/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist" expect(atom.project.getEnv().PATH).toEqual "/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist" + expect(atom.project.getEnv().FOO).not.toEqual "BAR" + + it "does the best it can when there is an error retrieving the shell environment", -> + spyOn(atom.project, "getShellEnv").andReturn(undefined) + + expect(atom.project.getShellPath()).toBeUndefined() + expect(atom.project.getEnv().PATH).not.toBeUndefined() + expect(atom.project.getEnv().PATH).toEqual process.env.PATH describe "when TERM is set", -> - it "returns the PATH unchanged", -> - spyOn(process.env, "TERM").andReturn("foo") + beforeEach -> + process.env.TERM = "foo" + it "returns the PATH unchanged", -> expect(atom.project.getEnv().PATH).toEqual process.env.PATH diff --git a/src/project.coffee b/src/project.coffee index 5d5448dc7..c8fd3ddbe 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -288,7 +288,8 @@ class Project extends Model unless @env? @env = _.clone(process.env) if process.platform is "darwin" and not process.env.TERM? - @env.PATH = @getShellPath() + shellPath = @getShellPath() + @env.PATH = shellPath if shellPath? _.clone(@env) @@ -296,8 +297,13 @@ class Project extends Model Section: Private ### + # Gets the user's configured shell `PATH`. + # + # Returns the value of `PATH` or `undefined` if there was an error. getShellPath: -> shellEnvText = @getShellEnv() + return unless shellEnvText? + env = {} for line in shellEnvText.split(os.EOL) @@ -312,9 +318,18 @@ class Project extends Model env.PATH + # Gets a dump of the user's configured shell environment. + # + # Returns the output of the `env` command or `undefined` if there was an error. getShellEnv: -> shell = process.env.SHELL ? "/bin/bash" - results = child_process.spawnSync shell, ["--login", "--interactive"], input: "env", encoding: "utf8" + + # The `-ilc` set of options was tested to work with the OS X v10.11 + # default-installed versions of bash, zsh, sh, and ksh. It *does not* + # work with csh or tcsh. Given that bash and zsh should cover the + # vast majority of users and it gracefully falls back to prior behavior, + # this should be safe. + results = child_process.spawnSync shell, ["-ilc"], input: "env", encoding: "utf8" return if results.error? return unless results.stdout and results.stdout.length > 0 From 8c53e25f802f7e5e6311a61087f78bbf4e246b20 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Mon, 7 Mar 2016 16:43:20 -0800 Subject: [PATCH 452/971] Patch process.env on startup --- spec/atom-environment-spec.coffee | 13 +++++++++++++ src/atom-environment.coffee | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 3283b63d6..6271ea5d4 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -362,3 +362,16 @@ describe "AtomEnvironment", -> version = '1.7.0-dev-5340c91' expect(atom.getReleaseChannel()).toBe 'dev' + + describe "environment patching", -> + it "patches process.env on startup", -> + configDirPath = temp.mkdirSync() + fakeDocument = { + addEventListener: -> + removeEventListener: -> + head: document.createElement('head') + body: document.createElement('body') + } + atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, window, document: fakeDocument}) + + expect(process.env).toEqual atomEnvironment.project.getEnv() diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 8c79d66f6..d1e921d54 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -230,6 +230,10 @@ class AtomEnvironment extends Model checkPortableHomeWritable() + # Patch the `process.env` on startup to fix the problem first documented + # in #4126 + process.env = @project.getEnv() + attachSaveStateListeners: -> saveState = => @saveState({isUnloading: false}) unless @unloaded debouncedSaveState = _.debounce(saveState, @saveStateDebounceInterval) From c7465f5f7e747b5d0b182a3c66f0bd0f67bf052d Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Tue, 8 Mar 2016 15:29:48 -0800 Subject: [PATCH 453/971] Add environment module for getting environment info --- spec/environment-spec.coffee | 27 ++++++++++++++++++++++++ src/environment.coffee | 41 ++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 spec/environment-spec.coffee create mode 100644 src/environment.coffee diff --git a/spec/environment-spec.coffee b/spec/environment-spec.coffee new file mode 100644 index 000000000..484bdcc1a --- /dev/null +++ b/spec/environment-spec.coffee @@ -0,0 +1,27 @@ +child_process = require('child_process') +{getShellEnv} = require("../src/environment") + +describe "Environment handling", -> + describe "when things are configured properly", -> + beforeEach -> + spyOn(child_process, "spawnSync").andReturn + stdout: """ + FOO=BAR + TERM=xterm-something + PATH=/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist + """ + + it "returns an object containing the information from the user's shell environment", -> + env = getShellEnv() + + expect(env.FOO).toEqual "BAR" + expect(env.TERM).toEqual "xterm-something" + expect(env.PATH).toEqual "/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist" + + describe "when an error occurs", -> + beforeEach -> + spyOn(child_process, "spawnSync").andReturn + error: new Error + + it "returns undefined", -> + expect(getShellEnv()).toBeUndefined() diff --git a/src/environment.coffee b/src/environment.coffee new file mode 100644 index 000000000..f2b9bbf47 --- /dev/null +++ b/src/environment.coffee @@ -0,0 +1,41 @@ +child_process = require('child_process') +os = require('os') + +# Gets a dump of the user's configured shell environment. +# +# Returns the output of the `env` command or `undefined` if there was an error. +getRawShellEnv = -> + shell = process.env.SHELL ? "/bin/bash" + + # The `-ilc` set of options was tested to work with the OS X v10.11 + # default-installed versions of bash, zsh, sh, and ksh. It *does not* + # work with csh or tcsh. Given that bash and zsh should cover the + # vast majority of users and it gracefully falls back to prior behavior, + # this should be safe. + results = child_process.spawnSync shell, ["-ilc"], input: "env", encoding: "utf8" + return if results.error? + return unless results.stdout and results.stdout.length > 0 + + results.stdout + +module.exports = + # Gets the user's configured shell environment. + # + # Returns a copy of the user's shell enviroment. + getShellEnv: -> + shellEnvText = getRawShellEnv() + return unless shellEnvText? + + env = {} + + for line in shellEnvText.split(os.EOL) + if line.includes("=") + components = line.split("=") + if components.length is 2 + env[components[0]] = components[1] + else + k = components.shift() + v = components.join("=") + env[k] = v + + env From a3ba15c8a13c5cc61155df9b33abdf6c5c58ddbf Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Tue, 8 Mar 2016 15:50:53 -0800 Subject: [PATCH 454/971] Update Project to use new environment module --- spec/project-spec.coffee | 20 +++++++++---------- src/project.coffee | 43 +++------------------------------------- 2 files changed, 13 insertions(+), 50 deletions(-) diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 35cd0157f..f5fcad8e3 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -8,6 +8,8 @@ BufferedProcess = require '../src/buffered-process' {Directory} = require 'pathwatcher' GitRepository = require '../src/git-repository' +environment = require '../src/environment' + describe "Project", -> beforeEach -> atom.project.setPaths([atom.project.getDirectories()[0]?.resolve('dir')]) @@ -581,21 +583,19 @@ describe "Project", -> beforeEach -> delete process.env.TERM - it "replaces the PATH with the one obtained from the shell", -> - spyOn(atom.project, "getShellEnv").andReturn """ - FOO=BAR - TERM=xterm-something - PATH=/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist - """ + it "replaces the environment with the one obtained from the shell", -> + spyOn(environment, "getShellEnv").andReturn + FOO: "BAR" + TERM: "xterm-something" + PATH: "/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist" - expect(atom.project.getShellPath()).toEqual "/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist" + expect(atom.project.getEnv().TERM).toEqual "xterm-something" expect(atom.project.getEnv().PATH).toEqual "/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist" - expect(atom.project.getEnv().FOO).not.toEqual "BAR" + expect(atom.project.getEnv().FOO).toEqual "BAR" it "does the best it can when there is an error retrieving the shell environment", -> - spyOn(atom.project, "getShellEnv").andReturn(undefined) + spyOn(environment, "getShellEnv").andReturn(undefined) - expect(atom.project.getShellPath()).toBeUndefined() expect(atom.project.getEnv().PATH).not.toBeUndefined() expect(atom.project.getEnv().PATH).toEqual process.env.PATH diff --git a/src/project.coffee b/src/project.coffee index c8fd3ddbe..2a21aa2d8 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -288,8 +288,9 @@ class Project extends Model unless @env? @env = _.clone(process.env) if process.platform is "darwin" and not process.env.TERM? - shellPath = @getShellPath() - @env.PATH = shellPath if shellPath? + {getShellEnv} = require("../src/environment") + shellEnv = getShellEnv() + @env = shellEnv if shellEnv? _.clone(@env) @@ -297,44 +298,6 @@ class Project extends Model Section: Private ### - # Gets the user's configured shell `PATH`. - # - # Returns the value of `PATH` or `undefined` if there was an error. - getShellPath: -> - shellEnvText = @getShellEnv() - return unless shellEnvText? - - env = {} - - for line in shellEnvText.split(os.EOL) - if line.includes("=") - components = line.split("=") - if components.length is 2 - env[components[0]] = components[1] - else - k = components.shift() - v = components.join("=") - env[k] = v - - env.PATH - - # Gets a dump of the user's configured shell environment. - # - # Returns the output of the `env` command or `undefined` if there was an error. - getShellEnv: -> - shell = process.env.SHELL ? "/bin/bash" - - # The `-ilc` set of options was tested to work with the OS X v10.11 - # default-installed versions of bash, zsh, sh, and ksh. It *does not* - # work with csh or tcsh. Given that bash and zsh should cover the - # vast majority of users and it gracefully falls back to prior behavior, - # this should be safe. - results = child_process.spawnSync shell, ["-ilc"], input: "env", encoding: "utf8" - return if results.error? - return unless results.stdout and results.stdout.length > 0 - - results.stdout - consumeServices: ({serviceHub}) -> serviceHub.consume( 'atom.directory-provider', From b98388fa762e27c357613b9f8f8e2f2b0e74bd95 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Tue, 8 Mar 2016 16:23:48 -0800 Subject: [PATCH 455/971] Fix broken command parameter --- spec/environment-spec.coffee | 4 ++-- src/atom-environment.coffee | 3 ++- src/environment.coffee | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/spec/environment-spec.coffee b/spec/environment-spec.coffee index 484bdcc1a..9eac5b071 100644 --- a/spec/environment-spec.coffee +++ b/spec/environment-spec.coffee @@ -1,7 +1,7 @@ child_process = require('child_process') {getShellEnv} = require("../src/environment") -describe "Environment handling", -> +fdescribe "Environment handling", -> describe "when things are configured properly", -> beforeEach -> spyOn(child_process, "spawnSync").andReturn @@ -21,7 +21,7 @@ describe "Environment handling", -> describe "when an error occurs", -> beforeEach -> spyOn(child_process, "spawnSync").andReturn - error: new Error + error: new Error("testing when an error occurs") it "returns undefined", -> expect(getShellEnv()).toBeUndefined() diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index d1e921d54..e43051571 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -231,7 +231,8 @@ class AtomEnvironment extends Model checkPortableHomeWritable() # Patch the `process.env` on startup to fix the problem first documented - # in #4126 + # in #4126. Retain the original in case someone needs it. + process._originalEnv = process.env process.env = @project.getEnv() attachSaveStateListeners: -> diff --git a/src/environment.coffee b/src/environment.coffee index f2b9bbf47..56de0c194 100644 --- a/src/environment.coffee +++ b/src/environment.coffee @@ -12,7 +12,7 @@ getRawShellEnv = -> # work with csh or tcsh. Given that bash and zsh should cover the # vast majority of users and it gracefully falls back to prior behavior, # this should be safe. - results = child_process.spawnSync shell, ["-ilc"], input: "env", encoding: "utf8" + results = child_process.spawnSync(shell, ["-ilc", "env"], encoding: "utf8") return if results.error? return unless results.stdout and results.stdout.length > 0 From 6b38049b8d10deb9abf66fbdb19d2280e04075f2 Mon Sep 17 00:00:00 2001 From: Joe Fitzgerald Date: Thu, 10 Mar 2016 15:16:41 -0700 Subject: [PATCH 456/971] Remove Project API, Work With process.env Directly - Convert environment.coffee and environment-spec.coffee to JavaScript - Pass the process's environment across the wire when launching atom multiple times from the command line --- spec/atom-environment-spec.coffee | 13 -- spec/environment-spec.coffee | 27 ---- spec/environment-spec.js | 161 +++++++++++++++++++++++ spec/project-spec.coffee | 68 ---------- src/atom-environment.coffee | 7 +- src/browser/atom-application.coffee | 29 ++-- src/browser/main.coffee | 1 + src/environment.coffee | 41 ------ src/environment.js | 94 +++++++++++++ src/initialize-application-window.coffee | 3 +- src/project.coffee | 20 --- 11 files changed, 275 insertions(+), 189 deletions(-) delete mode 100644 spec/environment-spec.coffee create mode 100644 spec/environment-spec.js delete mode 100644 src/environment.coffee create mode 100644 src/environment.js diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 6271ea5d4..3283b63d6 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -362,16 +362,3 @@ describe "AtomEnvironment", -> version = '1.7.0-dev-5340c91' expect(atom.getReleaseChannel()).toBe 'dev' - - describe "environment patching", -> - it "patches process.env on startup", -> - configDirPath = temp.mkdirSync() - fakeDocument = { - addEventListener: -> - removeEventListener: -> - head: document.createElement('head') - body: document.createElement('body') - } - atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, window, document: fakeDocument}) - - expect(process.env).toEqual atomEnvironment.project.getEnv() diff --git a/spec/environment-spec.coffee b/spec/environment-spec.coffee deleted file mode 100644 index 9eac5b071..000000000 --- a/spec/environment-spec.coffee +++ /dev/null @@ -1,27 +0,0 @@ -child_process = require('child_process') -{getShellEnv} = require("../src/environment") - -fdescribe "Environment handling", -> - describe "when things are configured properly", -> - beforeEach -> - spyOn(child_process, "spawnSync").andReturn - stdout: """ - FOO=BAR - TERM=xterm-something - PATH=/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist - """ - - it "returns an object containing the information from the user's shell environment", -> - env = getShellEnv() - - expect(env.FOO).toEqual "BAR" - expect(env.TERM).toEqual "xterm-something" - expect(env.PATH).toEqual "/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist" - - describe "when an error occurs", -> - beforeEach -> - spyOn(child_process, "spawnSync").andReturn - error: new Error("testing when an error occurs") - - it "returns undefined", -> - expect(getShellEnv()).toBeUndefined() diff --git a/spec/environment-spec.js b/spec/environment-spec.js new file mode 100644 index 000000000..ae36d7d2c --- /dev/null +++ b/spec/environment-spec.js @@ -0,0 +1,161 @@ +'use babel' +/* eslint-env jasmine */ + +import child_process from 'child_process' +import environment from '../src/environment' +import os from 'os' +import _ from 'underscore-plus' + +fdescribe('Environment handling', () => { + let originalEnv + let options + + beforeEach(() => { + originalEnv = process.env + delete process._originalEnv + options = { + platform: process.platform, + env: _.clone(process.env) + } + }) + + afterEach(() => { + process.env = originalEnv + delete process._originalEnv + }) + + describe('on OSX, when PWD is not set', () => { + beforeEach(() => { + options.platform = 'darwin' + }) + + describe('needsPatching', () => { + it('returns true if PWD is unset', () => { + delete options.env.PWD + expect(environment.needsPatching(options)).toBe(true) + options.env.PWD = undefined + expect(environment.needsPatching(options)).toBe(true) + options.env.PWD = null + expect(environment.needsPatching(options)).toBe(true) + options.env.PWD = false + expect(environment.needsPatching(options)).toBe(true) + }) + + it('returns false if PWD is set', () => { + options.env.PWD = 'xterm' + expect(environment.needsPatching(options)).toBe(false) + }) + }) + + describe('normalize', () => { + it('changes process.env if PWD is unset', () => { + if (process.platform === 'win32') { + return + } + delete options.env.PWD + environment.normalize(options) + expect(process._originalEnv).toBeDefined() + expect(process._originalEnv).toBeTruthy() + expect(process.env).toBeDefined() + expect(process.env).toBeTruthy() + expect(process.env.PWD).toBeDefined() + expect(process.env.PWD).toBeTruthy() + expect(process.env.PATH).toBeDefined() + expect(process.env.PATH).toBeTruthy() + expect(process.env.ATOM_HOME).toBeDefined() + expect(process.env.ATOM_HOME).toBeTruthy() + }) + }) + }) + + describe('on a platform other than OSX', () => { + beforeEach(() => { + options.platform = 'penguin' + }) + + describe('needsPatching', () => { + it('returns false if PWD is set or unset', () => { + delete options.env.PWD + expect(environment.needsPatching(options)).toBe(false) + options.env.PWD = undefined + expect(environment.needsPatching(options)).toBe(false) + options.env.PWD = null + expect(environment.needsPatching(options)).toBe(false) + options.env.PWD = false + expect(environment.needsPatching(options)).toBe(false) + options.env.PWD = '/' + expect(environment.needsPatching(options)).toBe(false) + }) + + it('returns false for linux', () => { + options.platform = 'linux' + options.PWD = '/' + expect(environment.needsPatching(options)).toBe(false) + }) + + it('returns false for windows', () => { + options.platform = 'win32' + options.PWD = 'c:\\' + expect(environment.needsPatching(options)).toBe(false) + }) + }) + + describe('normalize', () => { + it('does not change the environment', () => { + if (process.platform === 'win32') { + return + } + delete options.env.PWD + environment.normalize(options) + expect(process._originalEnv).toBeUndefined() + expect(process.env).toBeDefined() + expect(process.env).toBeTruthy() + expect(process.env.PATH).toBeDefined() + expect(process.env.PATH).toBeTruthy() + expect(process.env.PWD).toBeUndefined() + expect(process.env.PATH).toBe(originalEnv.PATH) + expect(process.env.ATOM_HOME).toBeDefined() + expect(process.env.ATOM_HOME).toBeTruthy() + }) + }) + }) + + describe('getFromShell', () => { + describe('when things are configured properly', () => { + beforeEach(() => { + spyOn(child_process, 'spawnSync').andReturn({ + stdout: 'FOO=BAR' + os.EOL + 'TERM=xterm-something' + os.EOL + + 'PATH=/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path' + }) + }) + + it('returns an object containing the information from the user\'s shell environment', () => { + let env = environment.getFromShell() + expect(env.FOO).toEqual('BAR') + expect(env.TERM).toEqual('xterm-something') + expect(env.PATH).toEqual('/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path') + }) + }) + + describe('when an error occurs launching the shell', () => { + beforeEach(() => { + spyOn(child_process, 'spawnSync').andReturn({ + error: new Error('testing when an error occurs') + }) + }) + + it('returns undefined', () => { + expect(environment.getFromShell()).toBeUndefined() + }) + + it('leaves the environment as-is when normalize() is called', () => { + options.platform = 'darwin' + delete options.env.PWD + expect(environment.needsPatching(options)).toBe(true) + environment.normalize(options) + expect(process.env).toBeDefined() + expect(process._originalEnv).toBeUndefined() + }) + }) + }) +}) diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index f5fcad8e3..499efd017 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -8,8 +8,6 @@ BufferedProcess = require '../src/buffered-process' {Directory} = require 'pathwatcher' GitRepository = require '../src/git-repository' -environment = require '../src/environment' - describe "Project", -> beforeEach -> atom.project.setPaths([atom.project.getDirectories()[0]?.resolve('dir')]) @@ -539,69 +537,3 @@ describe "Project", -> randomPath = path.join("some", "random", "path") expect(atom.project.contains(randomPath)).toBe false - - describe ".getEnv", -> - [originalTerm] = [] - - beforeEach -> - originalTerm = process.env.TERM - - afterEach -> - process.env.TERM = originalTerm - delete atom.project.env - - it "returns a copy of the environment", -> - env = atom.project.getEnv() - - env.PROJECT_GET_ENV_TESTING = "foo" - expect(process.env.PROJECT_GET_ENV_TESTING).not.toEqual "foo" - expect(atom.project.getEnv().PROJECT_GET_ENV_TESTING).not.toEqual "foo" - - describe "on platforms other than OS X", -> - beforeEach -> - spyOn(process, "platform").andReturn("foo") - - describe "when TERM is not set", -> - beforeEach -> - delete process.env.TERM - - it "returns the PATH unchanged", -> - expect(atom.project.getEnv().PATH).toEqual process.env.PATH - - describe "when TERM is set", -> - beforeEach -> - process.env.TERM = "foo" - - it "returns the PATH unchanged", -> - expect(atom.project.getEnv().PATH).toEqual process.env.PATH - - describe "on OS X", -> - beforeEach -> - spyOn(process, "platform").andReturn("darwin") - - describe "when TERM is not set", -> - beforeEach -> - delete process.env.TERM - - it "replaces the environment with the one obtained from the shell", -> - spyOn(environment, "getShellEnv").andReturn - FOO: "BAR" - TERM: "xterm-something" - PATH: "/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist" - - expect(atom.project.getEnv().TERM).toEqual "xterm-something" - expect(atom.project.getEnv().PATH).toEqual "/usr/bin:/bin:/usr/sbin:/sbin:/some/crazy/path/entry/that/should/not/exist" - expect(atom.project.getEnv().FOO).toEqual "BAR" - - it "does the best it can when there is an error retrieving the shell environment", -> - spyOn(environment, "getShellEnv").andReturn(undefined) - - expect(atom.project.getEnv().PATH).not.toBeUndefined() - expect(atom.project.getEnv().PATH).toEqual process.env.PATH - - describe "when TERM is set", -> - beforeEach -> - process.env.TERM = "foo" - - it "returns the PATH unchanged", -> - expect(atom.project.getEnv().PATH).toEqual process.env.PATH diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index e43051571..97ec3c5d4 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -4,6 +4,7 @@ path = require 'path' _ = require 'underscore-plus' {deprecate} = require 'grim' +environment = require('./environment') {CompositeDisposable, Disposable, Emitter} = require 'event-kit' fs = require 'fs-plus' {mapSourcePosition} = require 'source-map-support' @@ -127,6 +128,7 @@ class AtomEnvironment extends Model # Call .loadOrCreate instead constructor: (params={}) -> + environment.normalize(params) {@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params @unloaded = false @@ -230,11 +232,6 @@ class AtomEnvironment extends Model checkPortableHomeWritable() - # Patch the `process.env` on startup to fix the problem first documented - # in #4126. Retain the original in case someone needs it. - process._originalEnv = process.env - process.env = @project.getEnv() - attachSaveStateListeners: -> saveState = => @saveState({isUnloading: false}) unless @unloaded debouncedSaveState = _.debounce(saveState, @saveStateDebounceInterval) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 230e1bb9f..4767c9065 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -85,16 +85,16 @@ class AtomApplication else @loadState(options) or @openPath(options) - openWithOptions: ({initialPaths, pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState, addToLastWindow}) -> + openWithOptions: ({initialPaths, pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState, addToLastWindow, env}) -> if test - @runTests({headless: true, devMode, @resourcePath, executedFrom, pathsToOpen, logFile, timeout}) + @runTests({headless: true, devMode, @resourcePath, executedFrom, pathsToOpen, logFile, timeout, env}) else if pathsToOpen.length > 0 - @openPaths({initialPaths, pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow}) + @openPaths({initialPaths, pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow, env}) else if urlsToOpen.length > 0 - @openUrl({urlToOpen, devMode, safeMode}) for urlToOpen in urlsToOpen + @openUrl({urlToOpen, devMode, safeMode, env}) for urlToOpen in urlsToOpen else # Always open a editor window if this is the first instance of Atom. - @openPath({initialPaths, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow}) + @openPath({initialPaths, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow, env}) # Public: Removes the {AtomWindow} from the global window list. removeWindow: (window) -> @@ -134,7 +134,8 @@ class AtomApplication @deleteSocketFile() server = net.createServer (connection) => connection.on 'data', (data) => - @openWithOptions(JSON.parse(data)) + options = JSON.parse(data) + @openWithOptions(options) server.listen @socketPath server.on 'error', (error) -> console.error 'Application server failed', error @@ -418,8 +419,8 @@ class AtomApplication # :profileStartup - Boolean to control creating a profile of the startup time. # :window - {AtomWindow} to open file paths in. # :addToLastWindow - Boolean of whether this should be opened in last focused window. - openPath: ({initialPaths, pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow} = {}) -> - @openPaths({initialPaths, pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow}) + openPath: ({initialPaths, pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow, env} = {}) -> + @openPaths({initialPaths, pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow, env}) # Public: Opens multiple paths, in existing windows if possible. # @@ -432,7 +433,7 @@ class AtomApplication # :windowDimensions - Object with height and width keys. # :window - {AtomWindow} to open file paths in. # :addToLastWindow - Boolean of whether this should be opened in last focused window. - openPaths: ({initialPaths, pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearWindowState, addToLastWindow}={}) -> + openPaths: ({initialPaths, pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearWindowState, addToLastWindow, env}={}) -> devMode = Boolean(devMode) safeMode = Boolean(safeMode) clearWindowState = Boolean(clearWindowState) @@ -469,7 +470,7 @@ class AtomApplication windowInitializationScript ?= require.resolve('../initialize-application-window') resourcePath ?= @resourcePath windowDimensions ?= @getDimensionsForNewWindow() - openedWindow = new AtomWindow({initialPaths, locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup, clearWindowState}) + openedWindow = new AtomWindow({initialPaths, locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup, clearWindowState, env}) if pidToKillWhenClosed? @pidsToOpenWindows[pidToKillWhenClosed] = openedWindow @@ -532,7 +533,7 @@ class AtomApplication # :urlToOpen - The atom:// url to open. # :devMode - Boolean to control the opened window's dev mode. # :safeMode - Boolean to control the opened window's safe mode. - openUrl: ({urlToOpen, devMode, safeMode}) -> + openUrl: ({urlToOpen, devMode, safeMode, env}) -> unless @packages? PackageManager = require '../package-manager' @packages = new PackageManager @@ -547,7 +548,7 @@ class AtomApplication packagePath = @packages.resolvePackagePath(packageName) windowInitializationScript = path.resolve(packagePath, pack.urlMain) windowDimensions = @getDimensionsForNewWindow() - new AtomWindow({windowInitializationScript, @resourcePath, devMode, safeMode, urlToOpen, windowDimensions}) + new AtomWindow({windowInitializationScript, @resourcePath, devMode, safeMode, urlToOpen, windowDimensions, env}) else console.log "Package '#{pack.name}' does not have a url main: #{urlToOpen}" else @@ -562,7 +563,7 @@ class AtomApplication # :specPath - The directory to load specs from. # :safeMode - A Boolean that, if true, won't run specs from ~/.atom/packages # and ~/.atom/dev/packages, defaults to false. - runTests: ({headless, resourcePath, executedFrom, pathsToOpen, logFile, safeMode, timeout}) -> + runTests: ({headless, resourcePath, executedFrom, pathsToOpen, logFile, safeMode, timeout, env}) -> if resourcePath isnt @resourcePath and not fs.existsSync(resourcePath) resourcePath = @resourcePath @@ -592,7 +593,7 @@ class AtomApplication devMode = true isSpec = true safeMode ?= false - new AtomWindow({windowInitializationScript, resourcePath, headless, isSpec, devMode, testRunnerPath, legacyTestRunnerPath, testPaths, logFile, safeMode}) + new AtomWindow({windowInitializationScript, resourcePath, headless, isSpec, devMode, testRunnerPath, legacyTestRunnerPath, testPaths, logFile, safeMode, env}) resolveTestRunnerPath: (testPath) -> FindParentDir ?= require 'find-parent-dir' diff --git a/src/browser/main.coffee b/src/browser/main.coffee index b4df62bd6..6bf8817f9 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -13,6 +13,7 @@ console.log = require 'nslog' start = -> args = parseCommandLine() + args.env = process.env setupAtomHome(args) setupCompileCache() return if handleStartupEventWithSquirrel() diff --git a/src/environment.coffee b/src/environment.coffee deleted file mode 100644 index 56de0c194..000000000 --- a/src/environment.coffee +++ /dev/null @@ -1,41 +0,0 @@ -child_process = require('child_process') -os = require('os') - -# Gets a dump of the user's configured shell environment. -# -# Returns the output of the `env` command or `undefined` if there was an error. -getRawShellEnv = -> - shell = process.env.SHELL ? "/bin/bash" - - # The `-ilc` set of options was tested to work with the OS X v10.11 - # default-installed versions of bash, zsh, sh, and ksh. It *does not* - # work with csh or tcsh. Given that bash and zsh should cover the - # vast majority of users and it gracefully falls back to prior behavior, - # this should be safe. - results = child_process.spawnSync(shell, ["-ilc", "env"], encoding: "utf8") - return if results.error? - return unless results.stdout and results.stdout.length > 0 - - results.stdout - -module.exports = - # Gets the user's configured shell environment. - # - # Returns a copy of the user's shell enviroment. - getShellEnv: -> - shellEnvText = getRawShellEnv() - return unless shellEnvText? - - env = {} - - for line in shellEnvText.split(os.EOL) - if line.includes("=") - components = line.split("=") - if components.length is 2 - env[components[0]] = components[1] - else - k = components.shift() - v = components.join("=") - env[k] = v - - env diff --git a/src/environment.js b/src/environment.js new file mode 100644 index 000000000..00c112bda --- /dev/null +++ b/src/environment.js @@ -0,0 +1,94 @@ +'use babel' + +import {spawnSync} from 'child_process' +import os from 'os' + +// Gets a dump of the user's configured shell environment. +// +// Returns the output of the `env` command or `undefined` if there was an error. +function getRawShellEnv () { + let shell = getUserShell() + + // The `-ilc` set of options was tested to work with the OS X v10.11 + // default-installed versions of bash, zsh, sh, and ksh. It *does not* + // work with csh or tcsh. + let results = spawnSync(shell, ['-ilc', 'env'], {encoding: 'utf8'}) + if (results.error || !results.stdout || results.stdout.length <= 0) { + return + } + + return results.stdout +} + +function getUserShell () { + if (process.env.SHELL) { + return process.env.SHELL + } + + return '/bin/bash' +} + +// Gets the user's configured shell environment. +// +// Returns a copy of the user's shell enviroment. +function getFromShell () { + let shellEnvText = getRawShellEnv() + if (!shellEnvText) { + return + } + + let env = {} + + for (let line of shellEnvText.split(os.EOL)) { + if (line.includes('=')) { + let components = line.split('=') + if (components.length === 2) { + env[components[0]] = components[1] + } else { + let k = components.shift() + let v = components.join('=') + env[k] = v + } + } + } + + return env +} + +function needsPatching (options = { platform: process.platform, env: process.env }) { + if (options.platform === 'darwin' && !options.env.PWD) { + let shell = getUserShell() + if (shell.endsWith('csh') || shell.endsWith('tcsh')) { + return false + } + return true + } + + return false +} + +function normalize (options = {}) { + if (options && options.env) { + process.env = options.env + } + + if (!options.env) { + options.env = process.env + } + + if (!options.platform) { + options.platform = process.platform + } + + if (needsPatching(options)) { + // Patch the `process.env` on startup to fix the problem first documented + // in #4126. Retain the original in case someone needs it. + let shellEnv = getFromShell() + if (shellEnv && shellEnv.PATH) { + process._originalEnv = process.env + process.env = shellEnv + } + } +} + +export default { getFromShell, needsPatching, normalize } diff --git a/src/initialize-application-window.coffee b/src/initialize-application-window.coffee index cea4e1c3c..10f321ded 100644 --- a/src/initialize-application-window.coffee +++ b/src/initialize-application-window.coffee @@ -4,7 +4,7 @@ module.exports = ({blobStore}) -> require './window' {getWindowLoadSettings} = require './window-load-settings-helpers' - {resourcePath, isSpec, devMode} = getWindowLoadSettings() + {resourcePath, isSpec, devMode, env} = getWindowLoadSettings() # Add application-specific exports to module search path. exportsPath = path.join(resourcePath, 'exports') @@ -21,6 +21,7 @@ module.exports = ({blobStore}) -> applicationDelegate: new ApplicationDelegate, configDirPath: process.env.ATOM_HOME enablePersistence: true + env: env }) atom.startEditorWindow().then -> diff --git a/src/project.coffee b/src/project.coffee index 2a21aa2d8..340075a1b 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -274,26 +274,6 @@ class Project extends Model contains: (pathToCheck) -> @rootDirectories.some (dir) -> dir.contains(pathToCheck) - ### - Section: Environment - ### - - # Public: Retrieves a normalized copy of the environment. - # - # On OS X, the `PATH` can be different depending on whether Atom is launched - # from the Dock, Finder, Spotlight or the terminal. This detects how Atom was - # started and corrects the `PATH` environment variable before returning a copy - # of the environment. - getEnv: -> - unless @env? - @env = _.clone(process.env) - if process.platform is "darwin" and not process.env.TERM? - {getShellEnv} = require("../src/environment") - shellEnv = getShellEnv() - @env = shellEnv if shellEnv? - - _.clone(@env) - ### Section: Private ### From 87c20bd0958f410ac0a7b05e4ad48a77043edd8b Mon Sep 17 00:00:00 2001 From: Joe Fitzgerald Date: Thu, 10 Mar 2016 15:46:25 -0700 Subject: [PATCH 457/971] Remove Cruft --- src/project.coffee | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/project.coffee b/src/project.coffee index 340075a1b..93a3ed496 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -12,9 +12,6 @@ TextEditor = require './text-editor' Task = require './task' GitRepositoryProvider = require './git-repository-provider' -child_process = require 'child_process' -os = require 'os' - # Extended: Represents a project that's opened in Atom. # # An instance of this class is always available as the `atom.project` global. From 4e2db07dd96c13c966f84a86c01ec3ca9d7048cd Mon Sep 17 00:00:00 2001 From: simurai Date: Fri, 11 Mar 2016 10:13:31 +0900 Subject: [PATCH 458/971] :arrow_up: markdown-preview@v0.158.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d2cb82195..0194a5ad4 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "keybinding-resolver": "0.35.0", "line-ending-selector": "0.3.1", "link": "0.31.0", - "markdown-preview": "0.157.3", + "markdown-preview": "0.158.0", "metrics": "0.53.1", "notifications": "0.62.4", "open-on-github": "1.0.0", From 61fb12bf9aae6729c6eb2220b3f689382777a4e5 Mon Sep 17 00:00:00 2001 From: Joe Fitzgerald Date: Thu, 10 Mar 2016 19:59:26 -0700 Subject: [PATCH 459/971] Unfocus Spec --- spec/environment-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/environment-spec.js b/spec/environment-spec.js index ae36d7d2c..8d845a3f8 100644 --- a/spec/environment-spec.js +++ b/spec/environment-spec.js @@ -6,7 +6,7 @@ import environment from '../src/environment' import os from 'os' import _ from 'underscore-plus' -fdescribe('Environment handling', () => { +describe('Environment handling', () => { let originalEnv let options From bd4ef0a44381a6445d17063ff944bc91fd54a1ce Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 10 Mar 2016 20:17:19 -0800 Subject: [PATCH 460/971] :arrow_up: tabs@0.92.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d2cb82195..0b823926b 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.1.1", "styleguide": "0.45.2", "symbols-view": "0.112.0", - "tabs": "0.91.3", + "tabs": "0.92.0", "timecop": "0.33.1", "tree-view": "0.203.0", "update-package-dependencies": "0.10.0", From 383174d3803ef5e5ce658be1a94529516836b0b6 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 10 Mar 2016 21:27:10 -0800 Subject: [PATCH 461/971] Load apm path from config Signed-off-by: Katrina Uychaco --- spec/package-manager-spec.coffee | 14 ++++++++++++++ src/package-manager.coffee | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 3b54691b2..6a1610a8a 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -17,6 +17,20 @@ describe "PackageManager", -> beforeEach -> workspaceElement = atom.views.getView(atom.workspace) + describe "::getApmPath()", -> + it "returns the path to the apm command", -> + apmPath = path.join(process.resourcesPath, "app", "apm", "bin", "apm") + if process.platform is 'win32' + apmPath += ".cmd" + expect(atom.packages.getApmPath()).toBe apmPath + + describe "when the core.apmPath setting is set", -> + beforeEach -> + atom.config.set("core.apmPath", "/path/to/apm") + + it "returns the value of the core.apmPath config setting", -> + expect(atom.packages.getApmPath()).toBe "/path/to/apm" + describe "::loadPackage(name)", -> beforeEach -> atom.config.set("core.disabledPackages", []) diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 94b55a793..0e76a762f 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -128,8 +128,12 @@ class PackageManager # Public: Get the path to the apm command. # + # Uses the value of the `core.apmPath` config setting if it exists. + # # Return a {String} file path to apm. getApmPath: -> + configPath = atom.config.get('core.apmPath') + return configPath if configPath return @apmPath if @apmPath? commandName = 'apm' From 466e554ee19cb90e3b0eb5f23924721b6ac4bb9f Mon Sep 17 00:00:00 2001 From: Willem Van Lint Date: Wed, 2 Mar 2016 22:35:33 -0800 Subject: [PATCH 462/971] Add TextEditors to the registry on opt-in only --- src/text-editor.coffee | 6 ++++-- src/workspace.coffee | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 5e384fde1..8b503f16e 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -83,8 +83,9 @@ class TextEditor extends Model state.assert = atomEnvironment.assert.bind(atomEnvironment) state.applicationDelegate = atomEnvironment.applicationDelegate editor = new this(state) - disposable = atomEnvironment.textEditors.add(editor) - editor.onDidDestroy -> disposable.dispose() + if state.registered + disposable = atomEnvironment.textEditors.add(editor) + editor.onDidDestroy -> disposable.dispose() editor constructor: (params={}) -> @@ -155,6 +156,7 @@ class TextEditor extends Model firstVisibleScreenColumn: @getFirstVisibleScreenColumn() displayBuffer: @displayBuffer.serialize() selectionsMarkerLayerId: @selectionsMarkerLayer.id + registered: atom.textEditors.editors.has this subscribeToBuffer: -> @buffer.retain() diff --git a/src/workspace.coffee b/src/workspace.coffee index c925a495a..bdaf19eee 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -542,7 +542,10 @@ class Workspace extends Model throw error @project.bufferForPath(filePath, options).then (buffer) => - @buildTextEditor(_.extend({buffer, largeFileMode}, options)) + editor = @buildTextEditor(_.extend({buffer, largeFileMode}, options)) + disposable = atom.textEditors.add(editor) + editor.onDidDestroy -> disposable.dispose() + editor # Public: Returns a {Boolean} that is `true` if `object` is a `TextEditor`. # @@ -558,10 +561,7 @@ class Workspace extends Model @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate }, params) - editor = new TextEditor(params) - disposable = atom.textEditors.add(editor) - editor.onDidDestroy -> disposable.dispose() - editor + new TextEditor(params) # Public: Asynchronously reopens the last-closed item's URI if it hasn't already been # reopened. From edd91839a86dd9498a32a1ad2991a8f925f19685 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 11 Mar 2016 13:34:03 -0800 Subject: [PATCH 463/971] :arrow_up: language-ruby@0.68.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 417a46c76..77f54798b 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ "language-php": "0.37.0", "language-property-list": "0.8.0", "language-python": "0.43.0", - "language-ruby": "0.68.1", + "language-ruby": "0.68.2", "language-ruby-on-rails": "0.25.0", "language-sass": "0.45.0", "language-shellscript": "0.21.0", From f671d5c5cdc6921c24bd73aea9c6edd30a1ce253 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Fri, 11 Mar 2016 17:32:13 -0500 Subject: [PATCH 464/971] :arrow_up: language-ruby@0.68.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 77f54798b..4612c9745 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ "language-php": "0.37.0", "language-property-list": "0.8.0", "language-python": "0.43.0", - "language-ruby": "0.68.2", + "language-ruby": "0.68.3", "language-ruby-on-rails": "0.25.0", "language-sass": "0.45.0", "language-shellscript": "0.21.0", From 63ec05416165bb75179b30562a900133c03bcf53 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 11 Mar 2016 16:51:50 -0800 Subject: [PATCH 465/971] Rename environment -> environmentHelpers --- ...nt-spec.js => environment-helpers-spec.js} | 38 +++++++++---------- src/atom-environment.coffee | 4 +- ...{environment.js => environment-helpers.js} | 0 3 files changed, 21 insertions(+), 21 deletions(-) rename spec/{environment-spec.js => environment-helpers-spec.js} (76%) rename src/{environment.js => environment-helpers.js} (100%) diff --git a/spec/environment-spec.js b/spec/environment-helpers-spec.js similarity index 76% rename from spec/environment-spec.js rename to spec/environment-helpers-spec.js index 8d845a3f8..4d0e7d4aa 100644 --- a/spec/environment-spec.js +++ b/spec/environment-helpers-spec.js @@ -2,7 +2,7 @@ /* eslint-env jasmine */ import child_process from 'child_process' -import environment from '../src/environment' +import environmentHelpers from '../src/environment-helpers' import os from 'os' import _ from 'underscore-plus' @@ -32,18 +32,18 @@ describe('Environment handling', () => { describe('needsPatching', () => { it('returns true if PWD is unset', () => { delete options.env.PWD - expect(environment.needsPatching(options)).toBe(true) + expect(environmentHelpers.needsPatching(options)).toBe(true) options.env.PWD = undefined - expect(environment.needsPatching(options)).toBe(true) + expect(environmentHelpers.needsPatching(options)).toBe(true) options.env.PWD = null - expect(environment.needsPatching(options)).toBe(true) + expect(environmentHelpers.needsPatching(options)).toBe(true) options.env.PWD = false - expect(environment.needsPatching(options)).toBe(true) + expect(environmentHelpers.needsPatching(options)).toBe(true) }) it('returns false if PWD is set', () => { options.env.PWD = 'xterm' - expect(environment.needsPatching(options)).toBe(false) + expect(environmentHelpers.needsPatching(options)).toBe(false) }) }) @@ -53,7 +53,7 @@ describe('Environment handling', () => { return } delete options.env.PWD - environment.normalize(options) + environmentHelpers.normalize(options) expect(process._originalEnv).toBeDefined() expect(process._originalEnv).toBeTruthy() expect(process.env).toBeDefined() @@ -76,27 +76,27 @@ describe('Environment handling', () => { describe('needsPatching', () => { it('returns false if PWD is set or unset', () => { delete options.env.PWD - expect(environment.needsPatching(options)).toBe(false) + expect(environmentHelpers.needsPatching(options)).toBe(false) options.env.PWD = undefined - expect(environment.needsPatching(options)).toBe(false) + expect(environmentHelpers.needsPatching(options)).toBe(false) options.env.PWD = null - expect(environment.needsPatching(options)).toBe(false) + expect(environmentHelpers.needsPatching(options)).toBe(false) options.env.PWD = false - expect(environment.needsPatching(options)).toBe(false) + expect(environmentHelpers.needsPatching(options)).toBe(false) options.env.PWD = '/' - expect(environment.needsPatching(options)).toBe(false) + expect(environmentHelpers.needsPatching(options)).toBe(false) }) it('returns false for linux', () => { options.platform = 'linux' options.PWD = '/' - expect(environment.needsPatching(options)).toBe(false) + expect(environmentHelpers.needsPatching(options)).toBe(false) }) it('returns false for windows', () => { options.platform = 'win32' options.PWD = 'c:\\' - expect(environment.needsPatching(options)).toBe(false) + expect(environmentHelpers.needsPatching(options)).toBe(false) }) }) @@ -106,7 +106,7 @@ describe('Environment handling', () => { return } delete options.env.PWD - environment.normalize(options) + environmentHelpers.normalize(options) expect(process._originalEnv).toBeUndefined() expect(process.env).toBeDefined() expect(process.env).toBeTruthy() @@ -130,7 +130,7 @@ describe('Environment handling', () => { }) it('returns an object containing the information from the user\'s shell environment', () => { - let env = environment.getFromShell() + let env = environmentHelpers.getFromShell() expect(env.FOO).toEqual('BAR') expect(env.TERM).toEqual('xterm-something') expect(env.PATH).toEqual('/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path') @@ -145,14 +145,14 @@ describe('Environment handling', () => { }) it('returns undefined', () => { - expect(environment.getFromShell()).toBeUndefined() + expect(environmentHelpers.getFromShell()).toBeUndefined() }) it('leaves the environment as-is when normalize() is called', () => { options.platform = 'darwin' delete options.env.PWD - expect(environment.needsPatching(options)).toBe(true) - environment.normalize(options) + expect(environmentHelpers.needsPatching(options)).toBe(true) + environmentHelpers.normalize(options) expect(process.env).toBeDefined() expect(process._originalEnv).toBeUndefined() }) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 97ec3c5d4..a173443ce 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -4,7 +4,7 @@ path = require 'path' _ = require 'underscore-plus' {deprecate} = require 'grim' -environment = require('./environment') +environmentHelpers = require('./environment-helpers') {CompositeDisposable, Disposable, Emitter} = require 'event-kit' fs = require 'fs-plus' {mapSourcePosition} = require 'source-map-support' @@ -128,7 +128,7 @@ class AtomEnvironment extends Model # Call .loadOrCreate instead constructor: (params={}) -> - environment.normalize(params) + environmentHelpers.normalize(params) {@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params @unloaded = false diff --git a/src/environment.js b/src/environment-helpers.js similarity index 100% rename from src/environment.js rename to src/environment-helpers.js From 01a1a1d80c104300dc57842a54caddbb69433424 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 11 Mar 2016 16:57:02 -0800 Subject: [PATCH 466/971] Swap _.clone for Object.assign --- spec/environment-helpers-spec.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/environment-helpers-spec.js b/spec/environment-helpers-spec.js index 4d0e7d4aa..20ec15d9f 100644 --- a/spec/environment-helpers-spec.js +++ b/spec/environment-helpers-spec.js @@ -4,7 +4,6 @@ import child_process from 'child_process' import environmentHelpers from '../src/environment-helpers' import os from 'os' -import _ from 'underscore-plus' describe('Environment handling', () => { let originalEnv @@ -15,7 +14,7 @@ describe('Environment handling', () => { delete process._originalEnv options = { platform: process.platform, - env: _.clone(process.env) + env: Object.assign({}, process.env) } }) From c99a272f0166c5063ab281cc54c0921da96e911a Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sat, 12 Mar 2016 16:10:45 -0500 Subject: [PATCH 467/971] :arrow_up: language-json@0.17.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4612c9745..748cf9840 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "language-hyperlink": "0.16.0", "language-java": "0.17.0", "language-javascript": "0.110.0", - "language-json": "0.17.5", + "language-json": "0.17.6", "language-less": "0.29.0", "language-make": "0.21.0", "language-mustache": "0.13.0", From 8ffcea381ed76a45259c1f5786767902b925c7bf Mon Sep 17 00:00:00 2001 From: Jordan Tucker Date: Sat, 12 Mar 2016 01:48:59 -0800 Subject: [PATCH 468/971] load config in atom-application and pass it to auto-update-manager --- src/browser/atom-application.coffee | 7 ++++++- src/browser/auto-update-manager.coffee | 6 +----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 4767c9065..416202cc6 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -3,6 +3,7 @@ ApplicationMenu = require './application-menu' AtomProtocolHandler = require './atom-protocol-handler' AutoUpdateManager = require './auto-update-manager' StorageFolder = require '../storage-folder' +Config = require '../config' ipcHelpers = require '../ipc-helpers' {BrowserWindow, Menu, app, dialog, ipcMain, shell} = require 'electron' fs = require 'fs-plus' @@ -70,7 +71,11 @@ class AtomApplication @pidsToOpenWindows = {} @windows = [] - @autoUpdateManager = new AutoUpdateManager(@version, options.test, @resourcePath) + @config = new Config({configDirPath: process.env.ATOM_HOME, @resourcePath, enablePersistence: true}) + @config.setSchema null, {type: 'object', properties: _.clone(require('../config-schema'))} + @config.load() + + @autoUpdateManager = new AutoUpdateManager(@version, options.test, @resourcePath, @config) @applicationMenu = new ApplicationMenu(@version, @autoUpdateManager) @atomProtocolHandler = new AtomProtocolHandler(@resourcePath, @safeMode) diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index c8c57cb01..8cf06b833 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -1,6 +1,5 @@ autoUpdater = null _ = require 'underscore-plus' -Config = require '../config' {EventEmitter} = require 'events' path = require 'path' @@ -16,13 +15,10 @@ module.exports = class AutoUpdateManager _.extend @prototype, EventEmitter.prototype - constructor: (@version, @testMode, resourcePath) -> + constructor: (@version, @testMode, resourcePath, @config) -> @state = IdleState @iconPath = path.resolve(__dirname, '..', '..', 'resources', 'atom.png') @feedUrl = "https://atom.io/api/updates?version=#{@version}" - @config = new Config({configDirPath: process.env.ATOM_HOME, resourcePath, enablePersistence: true}) - @config.setSchema null, {type: 'object', properties: _.clone(require('../config-schema'))} - @config.load() process.nextTick => @setupAutoUpdater() setupAutoUpdater: -> From 4e4c85970bf93a0524af90f0331e9bb0f645fbf7 Mon Sep 17 00:00:00 2001 From: Jordan Tucker Date: Sat, 12 Mar 2016 01:49:38 -0800 Subject: [PATCH 469/971] add option to choose whether atom rememebers your last windows --- spec/integration/startup-spec.coffee | 30 ++++++++++++++++++++++++++++ src/browser/atom-application.coffee | 12 ++++++----- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/spec/integration/startup-spec.coffee b/spec/integration/startup-spec.coffee index f6b0e1cf3..136ca9676 100644 --- a/spec/integration/startup-spec.coffee +++ b/spec/integration/startup-spec.coffee @@ -268,6 +268,36 @@ describe "Starting Atom", -> [otherTempDirPath] ].sort() + it "doesn't reopen any previously opened windows if restorePreviousWindowsOnStart is disabled", -> + configPath = path.join(atomHome, 'config.cson') + config = CSON.readFileSync(configPath) + config['*'].core = {restorePreviousWindowsOnStart: false} + CSON.writeFileSync(configPath, config) + + runAtom [tempDirPath], {ATOM_HOME: atomHome}, (client) -> + client + .waitForExist("atom-workspace") + .waitForNewWindow(-> + @startAnotherAtom([otherTempDirPath], ATOM_HOME: atomHome) + , 5000) + .waitForExist("atom-workspace") + + runAtom [], {ATOM_HOME: atomHome}, (client) -> + windowProjectPaths = [] + + client + .waitForWindowCount(1, 10000) + .then ({value: windowHandles}) -> + @window(windowHandles[0]) + .waitForExist("atom-workspace") + .treeViewRootDirectories() + .then ({value: directories}) -> windowProjectPaths.push(directories) + + .call -> + expect(windowProjectPaths).toEqual [ + [] + ] + describe "opening a remote directory", -> it "opens the parent directory and creates an empty text editor", -> remoteDirectory = 'remote://server:3437/some/directory/path' diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 416202cc6..d057aef52 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -506,12 +506,14 @@ class AtomApplication saveState: (allowEmpty=false) -> return if @quitting + restorePreviousState = @config.get('core.restorePreviousWindowsOnStart') ? true states = [] - for window in @windows - unless window.isSpec - if loadSettings = window.getLoadSettings() - states.push(initialPaths: loadSettings.initialPaths) - if states.length > 0 or allowEmpty + if restorePreviousState + for window in @windows + unless window.isSpec + if loadSettings = window.getLoadSettings() + states.push(initialPaths: loadSettings.initialPaths) + if states.length > 0 or allowEmpty or not restorePreviousState @storageFolder.storeSync('application.json', states) loadState: (options) -> From 4798d6d7ce5e44ec16f0121e344bee94f7cdfc80 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sun, 13 Mar 2016 13:18:14 -0400 Subject: [PATCH 470/971] :arrow_up: tree-view@0.203.1 Refs #10983 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 748cf9840..efaca8685 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.112.0", "tabs": "0.92.0", "timecop": "0.33.1", - "tree-view": "0.203.0", + "tree-view": "0.203.1", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From ad91f0ffc20331962099793ac4f757122f5fffd5 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sun, 13 Mar 2016 15:55:39 -0400 Subject: [PATCH 471/971] :arrow_up: packages to fix Electron deprecations about@1.4.1 link@0.31.1 open-on-github@1.0.1 settings-view@0.232.5 Refs atom/atom#10983 --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index efaca8685..8d1c6371a 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "one-light-syntax": "1.2.0", "solarized-dark-syntax": "1.0.0", "solarized-light-syntax": "1.0.0", - "about": "1.4.0", + "about": "1.4.1", "archive-view": "0.61.1", "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.0", @@ -98,13 +98,13 @@ "incompatible-packages": "0.25.1", "keybinding-resolver": "0.35.0", "line-ending-selector": "0.3.1", - "link": "0.31.0", + "link": "0.31.1", "markdown-preview": "0.158.0", "metrics": "0.53.1", "notifications": "0.62.4", - "open-on-github": "1.0.0", + "open-on-github": "1.0.1", "package-generator": "0.41.1", - "settings-view": "0.232.4", + "settings-view": "0.232.5", "snippets": "1.0.1", "spell-check": "0.67.0", "status-bar": "1.1.1", From 579b6b7a04180d0476b849a808f6c0002f194682 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sun, 13 Mar 2016 18:02:50 -0400 Subject: [PATCH 472/971] :arrow_up: settings-view@0.232.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8d1c6371a..cbff93802 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "notifications": "0.62.4", "open-on-github": "1.0.1", "package-generator": "0.41.1", - "settings-view": "0.232.5", + "settings-view": "0.232.6", "snippets": "1.0.1", "spell-check": "0.67.0", "status-bar": "1.1.1", From ed744c12a9d9d0a7945504f07919216b5f5346a0 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Mon, 14 Mar 2016 08:43:16 -0700 Subject: [PATCH 473/971] Move location of call to environmentHelpers.normalize --- src/atom-environment.coffee | 2 -- src/initialize-application-window.coffee | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index a173443ce..8c79d66f6 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -4,7 +4,6 @@ path = require 'path' _ = require 'underscore-plus' {deprecate} = require 'grim' -environmentHelpers = require('./environment-helpers') {CompositeDisposable, Disposable, Emitter} = require 'event-kit' fs = require 'fs-plus' {mapSourcePosition} = require 'source-map-support' @@ -128,7 +127,6 @@ class AtomEnvironment extends Model # Call .loadOrCreate instead constructor: (params={}) -> - environmentHelpers.normalize(params) {@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params @unloaded = false diff --git a/src/initialize-application-window.coffee b/src/initialize-application-window.coffee index 10f321ded..ea811f515 100644 --- a/src/initialize-application-window.coffee +++ b/src/initialize-application-window.coffee @@ -1,11 +1,15 @@ # Like sands through the hourglass, so are the days of our lives. module.exports = ({blobStore}) -> + environmentHelpers = require('./environment-helpers') path = require 'path' require './window' {getWindowLoadSettings} = require './window-load-settings-helpers' {resourcePath, isSpec, devMode, env} = getWindowLoadSettings() + # Set baseline environment + environmentHelpers.normalize({env: env}) + # Add application-specific exports to module search path. exportsPath = path.join(resourcePath, 'exports') require('module').globalPaths.push(exportsPath) From d1c8fff86c89e326847385da623f13c64534c2b7 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 14 Mar 2016 13:42:05 -0400 Subject: [PATCH 474/971] Pull line length out into its own variable. --- src/buffered-process.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/buffered-process.coffee b/src/buffered-process.coffee index 183a1d99a..53934c02d 100644 --- a/src/buffered-process.coffee +++ b/src/buffered-process.coffee @@ -115,8 +115,9 @@ class BufferedProcess buffered += data lastNewlineIndex = data.lastIndexOf('\n') if lastNewlineIndex isnt -1 - onLines(buffered.substring(0, lastNewlineIndex + bufferedLength + 1)) - buffered = buffered.substring(lastNewlineIndex + bufferedLength + 1) + lineLength = lastNewlineIndex + bufferedLength + 1 + onLines(buffered.substring(0, lineLength)) + buffered = buffered.substring(lineLength) stream.on 'close', => return if @killed From e513ed3a118baa99acf96f4516f7297ce01167b6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 14 Mar 2016 18:43:27 +0100 Subject: [PATCH 475/971] WIP: Always enable soft-wrapping --- src/display-buffer.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index ee4e9be77..b2b00f009 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -51,7 +51,11 @@ class DisplayBuffer extends Model @grammarRegistry, @packageManager, @assert }) @buffer = @tokenizedBuffer.buffer - @displayLayer = @buffer.addDisplayLayer({tabLength: @getTabLength(), invisibles: @config.get('editor.invisibles'), showIndentGuides: @config.get('editor.showIndentGuide')}) + @displayLayer = @buffer.addDisplayLayer({ + tabLength: @getTabLength(), invisibles: @config.get('editor.invisibles'), + showIndentGuides: @config.get('editor.showIndentGuide'), + softWrapColumn: @config.get('editor.preferredLineLength') + }) @displayLayer.setTextDecorationLayer(@tokenizedBuffer) @charWidthsByScope = {} @defaultMarkerLayer = @displayLayer.addMarkerLayer() From 66971abe25e9ec1aed48d7bcbfabe4d8f0ba7bee Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 14 Mar 2016 14:12:31 -0400 Subject: [PATCH 476/971] Don't need that trailing space. --- spec/buffered-process-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/buffered-process-spec.coffee b/spec/buffered-process-spec.coffee index 04cff0b6d..dd166f0dc 100644 --- a/spec/buffered-process-spec.coffee +++ b/spec/buffered-process-spec.coffee @@ -97,7 +97,7 @@ describe "BufferedProcess", -> expect(ChildProcess.spawn.argsForCall[0][1][1]).toBe '/c' expect(ChildProcess.spawn.argsForCall[0][1][2]).toBe '"dir"' - it "calls the specified stdout, stderr, and exit callbacks ", -> + it "calls the specified stdout, stderr, and exit callbacks", -> stdout = '' stderr = '' exitCallback = jasmine.createSpy('exit callback') From d441da9c38a0629ce022f4e5e4013ad1b41daf9e Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 14 Mar 2016 14:12:47 -0400 Subject: [PATCH 477/971] Test it with a lot of content. --- spec/buffered-process-spec.coffee | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/spec/buffered-process-spec.coffee b/spec/buffered-process-spec.coffee index dd166f0dc..3d1a66bf8 100644 --- a/spec/buffered-process-spec.coffee +++ b/spec/buffered-process-spec.coffee @@ -114,3 +114,26 @@ describe "BufferedProcess", -> runs -> expect(stderr).toContain 'apm - Atom Package Manager' expect(stdout).toEqual '' + + it "calls the specified stdout callback only with whole lines", -> + exitCallback = jasmine.createSpy('exit callback') + baseContent = "There are dozens of us! Dozens! It's as Ann as the nose on Plain's face. Can you believe that the only reason the club is going under is because it's in a terrifying neighborhood? She calls it a Mayonegg. Waiting for the Emmys. BTW did you know won 6 Emmys and was still canceled early by Fox? COME ON. I'll buy you a hundred George Michaels that you can teach to drive! Never once touched my per diem. I'd go to Craft Service, get some raw veggies, bacon, Cup-A-Soup…baby, I got a stew goin'" + content = (baseContent for _ in [1..200]).join('\n') + outputAlwaysEndsWithStew = true + process = new BufferedProcess + command: '/bin/echo' + args: [content] + options: {} + stdout: (lines) -> + endLength = 10 + end = baseContent.substr(baseContent.length - endLength, endLength) + lineEndsWithStew = lines.substr(lines.length - endLength, endLength) is end + expect(lineEndsWithStew).toBeTrue + + outputAlwaysEndsWithStew = outputAlwaysEndsWithStew && lineEndsWithStew + exit: exitCallback + + waitsFor -> exitCallback.callCount is 1 + + runs -> + expect(outputAlwaysEndsWithStew).toBeTrue From 3601c1c2a869ac1d1d5fc73e9873384c6dde0b86 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 14 Mar 2016 14:18:06 -0400 Subject: [PATCH 478/971] Test the whole output too. --- spec/buffered-process-spec.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/buffered-process-spec.coffee b/spec/buffered-process-spec.coffee index 3d1a66bf8..fbaee801f 100644 --- a/spec/buffered-process-spec.coffee +++ b/spec/buffered-process-spec.coffee @@ -119,13 +119,16 @@ describe "BufferedProcess", -> exitCallback = jasmine.createSpy('exit callback') baseContent = "There are dozens of us! Dozens! It's as Ann as the nose on Plain's face. Can you believe that the only reason the club is going under is because it's in a terrifying neighborhood? She calls it a Mayonegg. Waiting for the Emmys. BTW did you know won 6 Emmys and was still canceled early by Fox? COME ON. I'll buy you a hundred George Michaels that you can teach to drive! Never once touched my per diem. I'd go to Craft Service, get some raw veggies, bacon, Cup-A-Soup…baby, I got a stew goin'" content = (baseContent for _ in [1..200]).join('\n') + stdout = '' + endLength = 10 outputAlwaysEndsWithStew = true process = new BufferedProcess command: '/bin/echo' args: [content] options: {} stdout: (lines) -> - endLength = 10 + stdout += lines + end = baseContent.substr(baseContent.length - endLength, endLength) lineEndsWithStew = lines.substr(lines.length - endLength, endLength) is end expect(lineEndsWithStew).toBeTrue @@ -137,3 +140,4 @@ describe "BufferedProcess", -> runs -> expect(outputAlwaysEndsWithStew).toBeTrue + expect(stdout).toBe content += '\n' From e69b3bb839e534bed55f2128a5e0eb4be99c12ae Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 14 Mar 2016 11:17:59 -0700 Subject: [PATCH 479/971] :arrow_up: tree-view Signed-off-by: Katrina Uychaco (cherry picked from commit 70728d8e94b06bb7fc98f5a67e54511fe2208363) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cbff93802..6798d6d02 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.112.0", "tabs": "0.92.0", "timecop": "0.33.1", - "tree-view": "0.203.1", + "tree-view": "0.203.2", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From 0c67fd61ffb0250f556a317496c6a2588aeae1fe Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 14 Mar 2016 14:57:58 -0400 Subject: [PATCH 480/971] Hi lint. --- spec/buffered-process-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/buffered-process-spec.coffee b/spec/buffered-process-spec.coffee index fbaee801f..1f524d66a 100644 --- a/spec/buffered-process-spec.coffee +++ b/spec/buffered-process-spec.coffee @@ -133,7 +133,7 @@ describe "BufferedProcess", -> lineEndsWithStew = lines.substr(lines.length - endLength, endLength) is end expect(lineEndsWithStew).toBeTrue - outputAlwaysEndsWithStew = outputAlwaysEndsWithStew && lineEndsWithStew + outputAlwaysEndsWithStew = outputAlwaysEndsWithStew and lineEndsWithStew exit: exitCallback waitsFor -> exitCallback.callCount is 1 From 50f8f8e7e9770a857ec534a7cfb9d6837ffb56ff Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 14 Mar 2016 16:28:36 -0400 Subject: [PATCH 481/971] Match GitRepository's responses to null paths. Fixes https://github.com/atom/git-diff/issues/93. --- src/git-repository-async.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index f80f46a13..3b33a5003 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -442,6 +442,9 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {Boolean} that's true if the `path` // is ignored. isPathIgnored (_path) { + // NB: We're matching the behavior of `GitRepository` here. + if (!_path) return Promise.resolve(false) + return this.getRepo() .then(repo => { const relativePath = this.relativize(_path, repo.workdir()) @@ -518,6 +521,10 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a status {Number} or null if the // path is not in the cache. getCachedPathStatus (_path) { + // NB: I don't love this, but we're matching the behavior of + // `GitRepository` here for API compatibility. + if (!_path) return null + return this.relativizeToWorkingDirectory(_path) .then(relativePath => this.pathStatusCache[relativePath]) } From a2a6ed05c5dcafa37668884f4b00bba9873b644e Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 14 Mar 2016 16:40:55 -0400 Subject: [PATCH 482/971] And again. --- src/git-repository-async.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 3b33a5003..d0903acd8 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -660,6 +660,11 @@ export default class GitRepositoryAsync { } return this._diffBlobToBuffer(blob, text, options) }) + .catch(e => { + // NB: I don't love this, but we're matching the behavior of + // `GitRepository` here for API compatibility. + return {} + }) } // Checking Out From 88882030059bc9503dbf3b9e07ef1529a9841294 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 14 Mar 2016 17:14:27 -0600 Subject: [PATCH 483/971] Drop marker-index dependency --- build/tasks/build-task.coffee | 1 - package.json | 1 - src/text-editor-presenter.coffee | 1 - 3 files changed, 3 deletions(-) diff --git a/build/tasks/build-task.coffee b/build/tasks/build-task.coffee index 9164f8dab..94fb00e2c 100644 --- a/build/tasks/build-task.coffee +++ b/build/tasks/build-task.coffee @@ -133,7 +133,6 @@ module.exports = (grunt) -> ignoredPaths.push "#{_.escapeRegExp(path.join('scrollbar-style', 'src') + path.sep)}.*\\.(cc|h)*" ignoredPaths.push "#{_.escapeRegExp(path.join('spellchecker', 'src') + path.sep)}.*\\.(cc|h)*" ignoredPaths.push "#{_.escapeRegExp(path.join('cached-run-in-this-context', 'src') + path.sep)}.*\\.(cc|h)?" - ignoredPaths.push "#{_.escapeRegExp(path.join('marker-index', 'src') + path.sep)}.*\\.(cc|h)?" ignoredPaths.push "#{_.escapeRegExp(path.join('keyboard-layout', 'src') + path.sep)}.*\\.(cc|h|mm)*" # Ignore build files diff --git a/package.json b/package.json index 4093ee7d0..6b449d0de 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ "less-cache": "0.23", "line-top-index": "0.2.0", "marked": "^0.3.4", - "marker-index": "^3.0.4", "nodegit": "0.11.9", "normalize-package-data": "^2.0.0", "nslog": "^3", diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 2fef4ca14..8192442dd 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1,6 +1,5 @@ {CompositeDisposable, Disposable, Emitter} = require 'event-kit' {Point, Range} = require 'text-buffer' -MarkerIndex = require 'marker-index' _ = require 'underscore-plus' Decoration = require './decoration' From 164b363a32494b6adc14c43707546db692a78e05 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 14 Mar 2016 20:26:37 -0400 Subject: [PATCH 484/971] :arrow_up: language-csharp@0.12.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6798d6d02..e98962865 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "language-c": "0.51.1", "language-clojure": "0.19.1", "language-coffee-script": "0.46.1", - "language-csharp": "0.11.0", + "language-csharp": "0.12.0", "language-css": "0.36.0", "language-gfm": "0.85.0", "language-git": "0.12.1", From e56c2addea76694d0143789f2edba9d68d3080c7 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 14 Mar 2016 20:13:00 -0600 Subject: [PATCH 485/971] Pass softWrapHangingIndent option --- src/display-buffer.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index b2b00f009..37dbd81b1 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -55,6 +55,7 @@ class DisplayBuffer extends Model tabLength: @getTabLength(), invisibles: @config.get('editor.invisibles'), showIndentGuides: @config.get('editor.showIndentGuide'), softWrapColumn: @config.get('editor.preferredLineLength') + softWrapHangingIndent: @config.get('editor.softWrapHangingIndent') }) @displayLayer.setTextDecorationLayer(@tokenizedBuffer) @charWidthsByScope = {} From 9c8d947628ea186eb4c98ffe80c1e0bd1c824831 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 14 Mar 2016 22:33:56 -0400 Subject: [PATCH 486/971] :arrow_up: language-clojure@0.20.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e98962865..e0332ffa6 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "whitespace": "0.32.2", "wrap-guide": "0.38.1", "language-c": "0.51.1", - "language-clojure": "0.19.1", + "language-clojure": "0.20.0", "language-coffee-script": "0.46.1", "language-csharp": "0.12.0", "language-css": "0.36.0", From 1457e27249252668a81291a39cbd97e7d39c4f0c Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 15 Mar 2016 09:56:03 -0400 Subject: [PATCH 487/971] :arrow_up: language-sass@0.46.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e0332ffa6..8d42f8d5a 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "language-python": "0.43.0", "language-ruby": "0.68.3", "language-ruby-on-rails": "0.25.0", - "language-sass": "0.45.0", + "language-sass": "0.46.0", "language-shellscript": "0.21.0", "language-source": "0.9.0", "language-sql": "0.20.0", From 0a6e51f665910c2d36e40dbda978b77e49c2e549 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 15 Mar 2016 10:35:44 -0400 Subject: [PATCH 488/971] :arrow_up: package-generator@1.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8d42f8d5a..398caecf9 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "metrics": "0.53.1", "notifications": "0.62.4", "open-on-github": "1.0.1", - "package-generator": "0.41.1", + "package-generator": "1.0.0", "settings-view": "0.232.6", "snippets": "1.0.1", "spell-check": "0.67.0", From 34698d57687a78ffe76136c3e69f95026c8fe400 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 15 Mar 2016 10:43:57 -0400 Subject: [PATCH 489/971] Revert "And again." This reverts commit a2a6ed05c5dcafa37668884f4b00bba9873b644e. --- src/git-repository-async.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index d0903acd8..3b33a5003 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -660,11 +660,6 @@ export default class GitRepositoryAsync { } return this._diffBlobToBuffer(blob, text, options) }) - .catch(e => { - // NB: I don't love this, but we're matching the behavior of - // `GitRepository` here for API compatibility. - return {} - }) } // Checking Out From 8f9ab771a79d7b9f26604ed5333943de03e0b690 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 15 Mar 2016 10:44:00 -0400 Subject: [PATCH 490/971] Revert "Match GitRepository's responses to null paths." This reverts commit 50f8f8e7e9770a857ec534a7cfb9d6837ffb56ff. --- src/git-repository-async.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 3b33a5003..f80f46a13 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -442,9 +442,6 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {Boolean} that's true if the `path` // is ignored. isPathIgnored (_path) { - // NB: We're matching the behavior of `GitRepository` here. - if (!_path) return Promise.resolve(false) - return this.getRepo() .then(repo => { const relativePath = this.relativize(_path, repo.workdir()) @@ -521,10 +518,6 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a status {Number} or null if the // path is not in the cache. getCachedPathStatus (_path) { - // NB: I don't love this, but we're matching the behavior of - // `GitRepository` here for API compatibility. - if (!_path) return null - return this.relativizeToWorkingDirectory(_path) .then(relativePath => this.pathStatusCache[relativePath]) } From 15b13e3ddc15d608d5c839a9ee160a3d1a6b56a8 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 15 Mar 2016 10:49:36 -0400 Subject: [PATCH 491/971] Note the changes to GitRepository. --- src/git-repository-async.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index f80f46a13..c5984eed4 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -15,6 +15,12 @@ const submoduleMode = 57344 // TODO: compose this from libgit2 constants // Just using this for _.isEqual and _.object, we should impl our own here import _ from 'underscore-plus' +// For the most part, this class behaves the same as `GitRepository`, with a few +// notable differences: +// * Errors are generally propagated out to the caller instead of being +// swallowed within `GitRepositoryAsync`. +// * Methods accepting a path shouldn't be given a null path, unless it is +// specifically allowed as noted in the method's documentation. export default class GitRepositoryAsync { static open (path, options = {}) { // QUESTION: Should this wrap Git.Repository and reject with a nicer message? From 047a62f9e15608e5d71b42966d96682530a25c6e Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 15 Mar 2016 12:05:16 -0400 Subject: [PATCH 492/971] :arrow_up: git-diff@1.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 398caecf9..b36a3614b 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "exception-reporting": "0.37.0", "find-and-replace": "0.197.4", "fuzzy-finder": "1.0.3", - "git-diff": "1.0.0", + "git-diff": "1.0.1", "go-to-line": "0.30.0", "grammar-selector": "0.48.1", "image-view": "0.57.0", From 147518d86dcf6a413eb31d8502465a433a1a3c3c Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 15 Mar 2016 12:05:24 -0400 Subject: [PATCH 493/971] :arrow_up: status-bar@1.1.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b36a3614b..d9a22fd07 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.232.6", "snippets": "1.0.1", "spell-check": "0.67.0", - "status-bar": "1.1.1", + "status-bar": "1.1.2", "styleguide": "0.45.2", "symbols-view": "0.112.0", "tabs": "0.92.0", From 7703240bdff0f0aad5640556ace0bd5e11eae700 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 15 Mar 2016 13:59:07 -0400 Subject: [PATCH 494/971] :arrow_up: notifications@0.63.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d9a22fd07..351d91cd4 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "link": "0.31.1", "markdown-preview": "0.158.0", "metrics": "0.53.1", - "notifications": "0.62.4", + "notifications": "0.63.0", "open-on-github": "1.0.1", "package-generator": "1.0.0", "settings-view": "0.232.6", From 1d149c8f92f37934c7bdd9e1501b15f3eb6905e4 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 15 Mar 2016 16:36:54 -0700 Subject: [PATCH 495/971] :arrow_up: apm Signed-off-by: Michelle Tilley --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index 4b599bc39..2f0fcf519 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "1.7.1" + "atom-package-manager": "1.8.0" } } From 627adc673934254345dbe9d73a97acb9df6a72bf Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 15 Mar 2016 16:41:11 -0700 Subject: [PATCH 496/971] :arrow_up: settings-view Signed-off-by: Michelle Tilley --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 351d91cd4..0ec40757d 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "notifications": "0.63.0", "open-on-github": "1.0.1", "package-generator": "1.0.0", - "settings-view": "0.232.6", + "settings-view": "0.233.0", "snippets": "1.0.1", "spell-check": "0.67.0", "status-bar": "1.1.2", From 4c8f43f41b37361e92f158cbad05553225dcad74 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 16 Mar 2016 15:15:48 +0100 Subject: [PATCH 497/971] Use new APIs in FakeLinesYardstick --- spec/fake-lines-yardstick.coffee | 38 ++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/spec/fake-lines-yardstick.coffee b/spec/fake-lines-yardstick.coffee index 38716ab3e..288032d84 100644 --- a/spec/fake-lines-yardstick.coffee +++ b/spec/fake-lines-yardstick.coffee @@ -1,8 +1,10 @@ {Point} = require 'text-buffer' +{isPairedCharacter} = require '../src/text-utils' module.exports = class FakeLinesYardstick constructor: (@model, @lineTopIndex) -> + {@displayLayer} = @model @characterWidthsByScope = {} getScopedCharacterWidth: (scopeNames, char) -> @@ -30,25 +32,27 @@ class FakeLinesYardstick left = 0 column = 0 - iterator = @model.tokenizedLineForScreenRow(targetRow).getTokenIterator() - while iterator.next() - characterWidths = @getScopedCharacterWidths(iterator.getScopes()) + for {tokens} in @displayLayer.getScreenLines(targetRow, targetRow + 1)[0] + scopes = [] + for {text, closeTags, openTags} in tokens + scopes.splice(scopes.lastIndexOf(closeTag), 1) for closeTag in closeTags + scopes.push(openTag) for openTag in openTags - valueIndex = 0 - text = iterator.getText() - while valueIndex < text.length - if iterator.isPairedCharacter() - char = text - charLength = 2 - valueIndex += 2 - else - char = text[valueIndex] - charLength = 1 - valueIndex++ + characterWidths = @getScopedCharacterWidths(iterator.getScopes()) + valueIndex = 0 + while valueIndex < text.length + if isPairedCharacter(text, valueIndex) + char = text[valueIndex...valueIndex + 2] + charLength = 2 + valueIndex += 2 + else + char = text[valueIndex] + charLength = 1 + valueIndex++ - break if column is targetColumn + break if column is targetColumn - left += characterWidths[char] ? baseCharacterWidth unless char is '\0' - column += charLength + left += characterWidths[char] ? baseCharacterWidth unless char is '\0' + column += charLength {top, left} From 690397c91c3e3d485f2ef71de894871d02d2bd56 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 16 Mar 2016 11:57:49 -0400 Subject: [PATCH 498/971] :arrow_up: notifications@0.63.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ec40757d..b766d9b4f 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "link": "0.31.1", "markdown-preview": "0.158.0", "metrics": "0.53.1", - "notifications": "0.63.0", + "notifications": "0.63.1", "open-on-github": "1.0.1", "package-generator": "1.0.0", "settings-view": "0.233.0", From f48069c5f848b5ae624b110a1df7800e919eb142 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 16 Mar 2016 12:40:40 -0400 Subject: [PATCH 499/971] :arrow_up: settings@0.234.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b766d9b4f..a3246926f 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "notifications": "0.63.1", "open-on-github": "1.0.1", "package-generator": "1.0.0", - "settings-view": "0.233.0", + "settings-view": "0.234.0", "snippets": "1.0.1", "spell-check": "0.67.0", "status-bar": "1.1.2", From 2e41e9ead47850acf87bce546f4bdb2d89a55db5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 16 Mar 2016 19:19:00 +0100 Subject: [PATCH 500/971] Reset DisplayLayer every time config changes --- src/display-buffer.coffee | 80 ++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 37dbd81b1..4db1c922e 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -51,12 +51,7 @@ class DisplayBuffer extends Model @grammarRegistry, @packageManager, @assert }) @buffer = @tokenizedBuffer.buffer - @displayLayer = @buffer.addDisplayLayer({ - tabLength: @getTabLength(), invisibles: @config.get('editor.invisibles'), - showIndentGuides: @config.get('editor.showIndentGuide'), - softWrapColumn: @config.get('editor.preferredLineLength') - softWrapHangingIndent: @config.get('editor.softWrapHangingIndent') - }) + @displayLayer = @buffer.addDisplayLayer() @displayLayer.setTextDecorationLayer(@tokenizedBuffer) @charWidthsByScope = {} @defaultMarkerLayer = @displayLayer.addMarkerLayer() @@ -78,35 +73,16 @@ class DisplayBuffer extends Model @scopedConfigSubscriptions = subscriptions = new CompositeDisposable scopeDescriptor = @getRootScopeDescriptor() + subscriptions.add @config.onDidChange 'editor.tabLength', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + subscriptions.add @config.onDidChange 'editor.invisibles', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + subscriptions.add @config.onDidChange 'editor.showInvisibles', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + subscriptions.add @config.onDidChange 'editor.showIndentGuide', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + subscriptions.add @config.onDidChange 'editor.softWrap', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + subscriptions.add @config.onDidChange 'editor.softWrapHangingIndent', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + subscriptions.add @config.onDidChange 'editor.softWrapAtPreferredLineLength', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + subscriptions.add @config.onDidChange 'editor.preferredLineLength', scope: scopeDescriptor, @resetDisplayLayer.bind(this) - oldConfigSettings = @configSettings - @configSettings = - scrollPastEnd: @config.get('editor.scrollPastEnd', scope: scopeDescriptor) - softWrap: @config.get('editor.softWrap', scope: scopeDescriptor) - softWrapAtPreferredLineLength: @config.get('editor.softWrapAtPreferredLineLength', scope: scopeDescriptor) - softWrapHangingIndent: @config.get('editor.softWrapHangingIndent', scope: scopeDescriptor) - preferredLineLength: @config.get('editor.preferredLineLength', scope: scopeDescriptor) - - subscriptions.add @config.onDidChange 'editor.softWrap', scope: scopeDescriptor, ({newValue}) => - @configSettings.softWrap = newValue - @updateWrappedScreenLines() - - subscriptions.add @config.onDidChange 'editor.softWrapHangingIndent', scope: scopeDescriptor, ({newValue}) => - @configSettings.softWrapHangingIndent = newValue - @updateWrappedScreenLines() - - subscriptions.add @config.onDidChange 'editor.softWrapAtPreferredLineLength', scope: scopeDescriptor, ({newValue}) => - @configSettings.softWrapAtPreferredLineLength = newValue - @updateWrappedScreenLines() if @isSoftWrapped() - - subscriptions.add @config.onDidChange 'editor.preferredLineLength', scope: scopeDescriptor, ({newValue}) => - @configSettings.preferredLineLength = newValue - @updateWrappedScreenLines() if @isSoftWrapped() and @config.get('editor.softWrapAtPreferredLineLength', scope: scopeDescriptor) - - subscriptions.add @config.observe 'editor.scrollPastEnd', scope: scopeDescriptor, (value) => - @configSettings.scrollPastEnd = value - - @updateWrappedScreenLines() if oldConfigSettings? and not _.isEqual(oldConfigSettings, @configSettings) + @resetDisplayLayer() serialize: -> deserializer: 'DisplayBuffer' @@ -122,6 +98,31 @@ class DisplayBuffer extends Model @grammarRegistry, @packageManager }) + resetDisplayLayer: -> + scopeDescriptor = @getRootScopeDescriptor() + invisibles = + if @config.get('editor.showInvisibles', scope: scopeDescriptor) + @config.get('editor.invisibles', scope: scopeDescriptor) + else + {} + + softWrapColumn = + if @isSoftWrapped() + if @config.get('editor.softWrapAtPreferredLineLength', scope: scopeDescriptor) + @config.get('editor.preferredLineLength', scope: scopeDescriptor) + else + @getEditorWidthInChars() + else + Infinity + + @displayLayer.reset({ + invisibles: invisibles + softWrapColumn: softWrapColumn + showIndentGuides: @config.get('editor.showIndentGuide', scope: scopeDescriptor) + tabLength: @config.get('editor.tabLength', scope: scopeDescriptor), + ratioForCharacter: -> 1.0 # TODO: replace this with korean/double/half width characters. + }) + updateAllScreenLines: -> return # TODO: After DisplayLayer is finished, delete these code paths @maxLineLength = 0 @@ -208,7 +209,7 @@ class DisplayBuffer extends Model setWidth: (newWidth) -> oldWidth = @width @width = newWidth - @updateWrappedScreenLines() if newWidth isnt oldWidth and @isSoftWrapped() + @resetDisplayLayer() if newWidth isnt oldWidth and @isSoftWrapped() @width getLineHeightInPixels: -> @lineHeightInPixels @@ -231,7 +232,7 @@ class DisplayBuffer extends Model @doubleWidthCharWidth = doubleWidthCharWidth @halfWidthCharWidth = halfWidthCharWidth @koreanCharWidth = koreanCharWidth - @updateWrappedScreenLines() if @isSoftWrapped() and @getEditorWidthInChars()? + @resetDisplayLayer() if @isSoftWrapped() and @getEditorWidthInChars()? defaultCharWidth getCursorWidth: -> 1 @@ -264,7 +265,7 @@ class DisplayBuffer extends Model setSoftWrapped: (softWrapped) -> if softWrapped isnt @softWrapped @softWrapped = softWrapped - @updateWrappedScreenLines() + @resetDisplayLayer() softWrapped = @isSoftWrapped() @emitter.emit 'did-change-soft-wrapped', softWrapped softWrapped @@ -275,7 +276,8 @@ class DisplayBuffer extends Model if @largeFileMode false else - @softWrapped ? @configSettings.softWrap ? false + scopeDescriptor = @getRootScopeDescriptor() + @softWrapped ? @config.get('editor.softWrap', scope: scopeDescriptor) ? false # Set the number of characters that fit horizontally in the editor. # @@ -285,7 +287,7 @@ class DisplayBuffer extends Model previousWidthInChars = @editorWidthInChars @editorWidthInChars = editorWidthInChars if editorWidthInChars isnt previousWidthInChars and @isSoftWrapped() - @updateWrappedScreenLines() + @resetDisplayLayer() # Returns the editor width in characters for soft wrap. getEditorWidthInChars: -> From 670123a9f75a5e59ebf65b35a8a8d8894be4a60f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 17 Mar 2016 09:28:48 +0100 Subject: [PATCH 501/971] More fixes in FakeLinesYardstick --- spec/fake-lines-yardstick.coffee | 38 +++++++++++++++----------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/spec/fake-lines-yardstick.coffee b/spec/fake-lines-yardstick.coffee index 288032d84..085ab4bde 100644 --- a/spec/fake-lines-yardstick.coffee +++ b/spec/fake-lines-yardstick.coffee @@ -26,33 +26,31 @@ class FakeLinesYardstick targetRow = screenPosition.row targetColumn = screenPosition.column - baseCharacterWidth = @model.getDefaultCharWidth() top = @lineTopIndex.pixelPositionAfterBlocksForRow(targetRow) left = 0 column = 0 - for {tokens} in @displayLayer.getScreenLines(targetRow, targetRow + 1)[0] - scopes = [] - for {text, closeTags, openTags} in tokens - scopes.splice(scopes.lastIndexOf(closeTag), 1) for closeTag in closeTags - scopes.push(openTag) for openTag in openTags + scopes = [] + for {text, closeTags, openTags} in @displayLayer.getScreenLines(targetRow, targetRow + 1)[0].tokens + scopes.splice(scopes.lastIndexOf(closeTag), 1) for closeTag in closeTags + scopes.push(openTag) for openTag in openTags + characterWidths = @getScopedCharacterWidths(scopes) - characterWidths = @getScopedCharacterWidths(iterator.getScopes()) - valueIndex = 0 - while valueIndex < text.length - if isPairedCharacter(text, valueIndex) - char = text[valueIndex...valueIndex + 2] - charLength = 2 - valueIndex += 2 - else - char = text[valueIndex] - charLength = 1 - valueIndex++ + valueIndex = 0 + while valueIndex < text.length + if isPairedCharacter(text, valueIndex) + char = text[valueIndex...valueIndex + 2] + charLength = 2 + valueIndex += 2 + else + char = text[valueIndex] + charLength = 1 + valueIndex++ - break if column is targetColumn + break if column is targetColumn - left += characterWidths[char] ? baseCharacterWidth unless char is '\0' - column += charLength + left += characterWidths[char] ? @model.getDefaultCharWidth() unless char is '\0' + column += charLength {top, left} From 661417e3627afe30d090829ae99f9920581b3127 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 17 Mar 2016 09:42:36 +0100 Subject: [PATCH 502/971] Update lines in TextEditorPresenter.prototype.getPostMeasurementState() Calling ::updateHorizontalDimensions might cause the editor vertical coordinates (e.g. height, scroll top) to change, so we need to fetch lines again from `DisplayLayer`. --- src/text-editor-presenter.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 8192442dd..7381369f9 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -110,6 +110,8 @@ class TextEditorPresenter @clearPendingScrollPosition() @updateRowsPerPage() + @updateLines() + @updateFocusedState() @updateHeightState() @updateVerticalScrollState() From 708da39355ded0c79e907ffbdda4caebc4530e2f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 17 Mar 2016 10:35:54 +0100 Subject: [PATCH 503/971] Avoid using tokenizedLineForScreenRow in TextEditorPresenter specs --- spec/text-editor-presenter-spec.coffee | 149 ++++++++++++------------- 1 file changed, 69 insertions(+), 80 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index f8117af09..d40828d77 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -1229,9 +1229,16 @@ describe "TextEditorPresenter", -> describe ".tiles", -> lineStateForScreenRow = (presenter, row) -> - lineId = presenter.model.tokenizedLineForScreenRow(row).id - tileRow = presenter.tileForRow(row) - getState(presenter).content.tiles[tileRow]?.lines[lineId] + tilesState = getState(presenter).content.tiles + lineId = presenter.linesByScreenRow.get(row)?.id + tilesState[presenter.tileForRow(row)]?.lines[lineId] + + tokensIncludeTag = (tokens, tag) -> + includeTag = false + for {openTags, closeTags} in tokens + includeTag = true for openTag in openTags when openTag.indexOf(tag) isnt -1 + includeTag = true for closeTag in closeTags when closeTag.indexOf(tag) isnt -1 + includeTag tiledContentContract (presenter) -> getState(presenter).content @@ -1241,73 +1248,56 @@ describe "TextEditorPresenter", -> presenter.setExplicitHeight(3) expect(lineStateForScreenRow(presenter, 2)).toBeUndefined() - - line3 = editor.tokenizedLineForScreenRow(3) expectValues lineStateForScreenRow(presenter, 3), { - screenRow: 3 - text: line3.text - tags: line3.tags - specialTokens: line3.specialTokens - firstNonWhitespaceIndex: line3.firstNonWhitespaceIndex - firstTrailingWhitespaceIndex: line3.firstTrailingWhitespaceIndex - invisibles: line3.invisibles + screenRow: 3, tokens: [ + {closeTags: [], openTags: ['text.plain.null-grammar', 'leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: [], text: 'var pivot = items.shift(), current, left = [], right = [];'}, + {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} + ] } - - line4 = editor.tokenizedLineForScreenRow(4) expectValues lineStateForScreenRow(presenter, 4), { - screenRow: 4 - text: line4.text - tags: line4.tags - specialTokens: line4.specialTokens - firstNonWhitespaceIndex: line4.firstNonWhitespaceIndex - firstTrailingWhitespaceIndex: line4.firstTrailingWhitespaceIndex - invisibles: line4.invisibles + screenRow: 4, tokens: [ + {closeTags: [], openTags: ['text.plain.null-grammar', 'leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: [], text: 'while(items.length > 0) {'}, + {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} + ] } - - line5 = editor.tokenizedLineForScreenRow(5) expectValues lineStateForScreenRow(presenter, 5), { - screenRow: 5 - text: line5.text - tags: line5.tags - specialTokens: line5.specialTokens - firstNonWhitespaceIndex: line5.firstNonWhitespaceIndex - firstTrailingWhitespaceIndex: line5.firstTrailingWhitespaceIndex - invisibles: line5.invisibles + screenRow: 5, tokens: [ + {closeTags: [], openTags: ['text.plain.null-grammar', 'leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: [], text: 'current = items.shift();'}, + {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} + ] } - - line6 = editor.tokenizedLineForScreenRow(6) expectValues lineStateForScreenRow(presenter, 6), { - screenRow: 6 - text: line6.text - tags: line6.tags - specialTokens: line6.specialTokens - firstNonWhitespaceIndex: line6.firstNonWhitespaceIndex - firstTrailingWhitespaceIndex: line6.firstTrailingWhitespaceIndex - invisibles: line6.invisibles + screenRow: 6, tokens: [ + {closeTags: [], openTags: ['text.plain.null-grammar', 'leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: [], text: 'current < pivot ? left.push(current) : right.push(current);'}, + {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} + ] } - - line7 = editor.tokenizedLineForScreenRow(7) expectValues lineStateForScreenRow(presenter, 7), { - screenRow: 7 - text: line7.text - tags: line7.tags - specialTokens: line7.specialTokens - firstNonWhitespaceIndex: line7.firstNonWhitespaceIndex - firstTrailingWhitespaceIndex: line7.firstTrailingWhitespaceIndex - invisibles: line7.invisibles + screenRow: 7, tokens: [ + {closeTags: [], openTags: ['text.plain.null-grammar', 'leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: [], text: '}'}, + {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} + ] } - - line8 = editor.tokenizedLineForScreenRow(8) expectValues lineStateForScreenRow(presenter, 8), { - screenRow: 8 - text: line8.text - tags: line8.tags - specialTokens: line8.specialTokens - firstNonWhitespaceIndex: line8.firstNonWhitespaceIndex - firstTrailingWhitespaceIndex: line8.firstTrailingWhitespaceIndex - invisibles: line8.invisibles + screenRow: 8, tokens: [ + {closeTags: [], openTags: ['text.plain.null-grammar', 'leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: [], text: 'return sort(left).concat(pivot).concat(sort(right));'}, + {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} + ] } - expect(lineStateForScreenRow(presenter, 9)).toBeUndefined() it "updates when the editor's content changes", -> @@ -1315,34 +1305,36 @@ describe "TextEditorPresenter", -> expectStateUpdate presenter, -> buffer.insert([2, 0], "hello\nworld\n") - line1 = editor.tokenizedLineForScreenRow(1) expectValues lineStateForScreenRow(presenter, 1), { - text: line1.text - tags: line1.tags + screenRow: 1, tokens: [ + {closeTags: [], openTags: ['text.plain.null-grammar', 'leading-whitespace'], text: ' '}, + {closeTags: ['leading-whitespace'], openTags: [], text: 'var sort = function(items) {'}, + {closeTags: ['text.plain.null-grammar'], openTags: [], text : ''} + ] } - - line2 = editor.tokenizedLineForScreenRow(2) expectValues lineStateForScreenRow(presenter, 2), { - text: line2.text - tags: line2.tags + screenRow: 2, tokens: [ + {closeTags: [], openTags: ['text.plain.null-grammar'], text: 'hello'}, + {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} + ] } - - line3 = editor.tokenizedLineForScreenRow(3) expectValues lineStateForScreenRow(presenter, 3), { - text: line3.text - tags: line3.tags + screenRow: 3, tokens: [ + {closeTags: [], openTags: ['text.plain.null-grammar'], text: 'world'}, + {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} + ] } it "includes the .endOfLineInvisibles if the editor.showInvisibles config option is true", -> editor.setText("hello\nworld\r\n") presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, lineHeight: 10) - expect(lineStateForScreenRow(presenter, 0).endOfLineInvisibles).toBeNull() - expect(lineStateForScreenRow(presenter, 1).endOfLineInvisibles).toBeNull() + expect(tokensIncludeTag(lineStateForScreenRow(presenter, 0).tokens, 'eol')).toBe(false) + expect(tokensIncludeTag(lineStateForScreenRow(presenter, 1).tokens, 'eol')).toBe(false) atom.config.set('editor.showInvisibles', true) presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, lineHeight: 10) - expect(lineStateForScreenRow(presenter, 0).endOfLineInvisibles).toEqual [atom.config.get('editor.invisibles.eol')] - expect(lineStateForScreenRow(presenter, 1).endOfLineInvisibles).toEqual [atom.config.get('editor.invisibles.cr'), atom.config.get('editor.invisibles.eol')] + expect(tokensIncludeTag(lineStateForScreenRow(presenter, 0).tokens, 'eol')).toBe(true) + expect(tokensIncludeTag(lineStateForScreenRow(presenter, 1).tokens, 'eol')).toBe(true) describe ".blockDecorations", -> it "contains all block decorations that are present before/after a line, both initially and when decorations change", -> @@ -2905,12 +2897,9 @@ describe "TextEditorPresenter", -> describe ".content.tiles", -> lineNumberStateForScreenRow = (presenter, screenRow) -> - editor = presenter.model - tileRow = presenter.tileForRow(screenRow) - line = editor.tokenizedLineForScreenRow(screenRow) - - gutterState = getLineNumberGutterState(presenter) - gutterState.content.tiles[tileRow]?.lineNumbers[line?.id] + tilesState = getLineNumberGutterState(presenter).content.tiles + line = presenter.linesByScreenRow.get(screenRow) + tilesState[presenter.tileForRow(screenRow)]?.lineNumbers[line?.id] tiledContentContract (presenter) -> getLineNumberGutterState(presenter).content @@ -2919,7 +2908,7 @@ describe "TextEditorPresenter", -> editor.foldBufferRow(4) editor.setSoftWrapped(true) editor.setDefaultCharWidth(1) - editor.setEditorWidthInChars(50) + editor.setEditorWidthInChars(51) presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineHeight: 10, tileSize: 2) expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() From 23ddeb7f087b7c2a4da7ea7493ff7df7ae5b9a1f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 17 Mar 2016 10:38:23 +0100 Subject: [PATCH 504/971] :fire: Put back commented out LOC --- src/text-editor-presenter.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 7381369f9..a6cd0d0b4 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1407,7 +1407,7 @@ class TextEditorPresenter startBlinkingCursors: -> unless @isCursorBlinking() @state.content.cursorsVisible = true - # @toggleCursorBlinkHandle = setInterval(@toggleCursorBlink.bind(this), @getCursorBlinkPeriod() / 2) + @toggleCursorBlinkHandle = setInterval(@toggleCursorBlink.bind(this), @getCursorBlinkPeriod() / 2) isCursorBlinking: -> @toggleCursorBlinkHandle? From f57fb3176ae1b6b21e615687f7df59030accf609 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 17 Mar 2016 11:19:05 +0100 Subject: [PATCH 505/971] Add back `lineIdForScreenRow` --- src/text-editor-presenter.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index a6cd0d0b4..6240f4a37 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -700,9 +700,6 @@ class TextEditorPresenter if @baseCharacterWidth? oldContentWidth = @contentWidth rightmostPosition = @model.getRightmostScreenPosition() - # TODO: Add some version of this back once softwrap is reintroduced - # if @model.tokenizedLineForScreenRow(rightmostPosition.row)?.isSoftWrapped() - # rightmostPosition = @model.clipScreenPosition(rightmostPosition) @contentWidth = @pixelPositionForScreenPosition(rightmostPosition).left @contentWidth += @scrollLeft @contentWidth += 1 unless @model.isSoftWrapped() # account for cursor width @@ -1077,6 +1074,9 @@ class TextEditorPresenter for line, index in @displayLayer.getScreenLines(startRow, endRow + 1) @linesByScreenRow.set(startRow + index, line) + lineIdForScreenRow: (screenRow) -> + @linesByScreenRow.get(screenRow).id + fetchDecorations: -> return unless 0 <= @startRow <= @endRow <= Infinity @decorations = @model.decorationsStateForScreenRowRange(@startRow, @endRow - 1) From 5efb969a633e13c647c77f53b6ab2eb212e12f4c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 17 Mar 2016 11:19:54 +0100 Subject: [PATCH 506/971] :green_heart: Start fixing TextEditorComponent specs --- spec/text-editor-component-spec.js | 15 +++++++-------- src/text-editor.coffee | 4 +++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 37a9751e1..d806b872e 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -69,13 +69,12 @@ describe('TextEditorComponent', function () { describe('line rendering', async function () { function expectTileContainsRow (tileNode, screenRow, {top}) { let lineNode = tileNode.querySelector('[data-screen-row="' + screenRow + '"]') - let tokenizedLine = editor.tokenizedLineForScreenRow(screenRow) - + let text = editor.lineTextForScreenRow(screenRow) expect(lineNode.offsetTop).toBe(top) - if (tokenizedLine.text === '') { + if (text === '') { expect(lineNode.innerHTML).toBe(' ') } else { - expect(lineNode.textContent).toBe(tokenizedLine.text) + expect(lineNode.textContent).toBe(text) } } @@ -294,12 +293,12 @@ describe('TextEditorComponent', function () { await nextViewUpdatePromise() - expect(component.lineNodeForScreenRow(3).textContent).toBe(editor.tokenizedLineForScreenRow(3).text) + expect(component.lineNodeForScreenRow(3).textContent).toBe(editor.lineTextForScreenRow(3)) buffer.delete([[0, 0], [3, 0]]) await nextViewUpdatePromise() - expect(component.lineNodeForScreenRow(3).textContent).toBe(editor.tokenizedLineForScreenRow(3).text) + expect(component.lineNodeForScreenRow(3).textContent).toBe(editor.lineTextForScreenRow(3)) }) it('updates the top position of lines when the line height changes', async function () { @@ -550,8 +549,8 @@ describe('TextEditorComponent', function () { }) it('does not show end of line invisibles at the end of wrapped lines', function () { - expect(component.lineNodeForScreenRow(0).textContent).toBe('a line that ') - expect(component.lineNodeForScreenRow(1).textContent).toBe('wraps' + invisibles.space + invisibles.eol) + expect(component.lineNodeForScreenRow(0).textContent).toBe('a line ') + expect(component.lineNodeForScreenRow(1).textContent).toBe('that wraps' + invisibles.space + invisibles.eol) }) }) }) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index b2ea7985e..52d5ffd47 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -773,7 +773,9 @@ class TextEditor extends Model # given screen row. # # * `screenRow` A {Number} representing a zero-indexed screen row. - lineTextForScreenRow: (screenRow) -> @displayBuffer.tokenizedLineForScreenRow(screenRow)?.text + lineTextForScreenRow: (screenRow) -> + line = @displayLayer.getScreenLines(screenRow, screenRow + 1)[0] + line?.tokens.map((t) -> t.text).join('') # Gets the screen line for the given screen row. # From 43f27780fd25f7cd7e00c61a057402a3d91742ad Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 17 Mar 2016 14:56:23 +0100 Subject: [PATCH 507/971] Fix specs related to the produced HTML line output This verifies that with the new DisplayLayer the produced output is cleaner when tags interleave. --- spec/text-editor-component-spec.js | 18 +++++++++--------- src/text-editor-presenter.coffee | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index d806b872e..ffcf38ea2 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -483,7 +483,7 @@ describe('TextEditorComponent', function () { it('displays newlines as their own token outside of the other tokens\' scopeDescriptor', async function () { editor.setText('let\n') await nextViewUpdatePromise() - expect(component.lineNodeForScreenRow(0).innerHTML).toBe('let' + invisibles.eol + '') + expect(component.lineNodeForScreenRow(0).innerHTML).toBe('let' + invisibles.eol + '') }) it('displays trailing carriage returns using a visible, non-empty value', async function () { @@ -522,19 +522,19 @@ describe('TextEditorComponent', function () { }) await nextViewUpdatePromise() - expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE') + expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE ') editor.setTabLength(3) await nextViewUpdatePromise() - expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE ') + expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE ') editor.setTabLength(1) await nextViewUpdatePromise() - expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE') + expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE ') editor.setTextInBufferRange([[9, 0], [9, Infinity]], ' ') editor.setTextInBufferRange([[11, 0], [11, Infinity]], ' ') await nextViewUpdatePromise() - expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE') + expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE') }) describe('when soft wrapping is enabled', function () { @@ -1198,10 +1198,10 @@ describe('TextEditorComponent', function () { let cursor = componentNode.querySelector('.cursor') let cursorRect = cursor.getBoundingClientRect() - let cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.source.js').childNodes[2] - let range = document.createRange() - range.setStart(cursorLocationTextNode, 0) - range.setEnd(cursorLocationTextNode, 1) + let cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.source.js').childNodes[0] + let range = document.createRange(cursorLocationTextNode) + range.setStart(cursorLocationTextNode, 3) + range.setEnd(cursorLocationTextNode, 4) let rangeRect = range.getBoundingClientRect() expect(cursorRect.left).toBeCloseTo(rangeRect.left, 0) expect(cursorRect.width).toBeCloseTo(rangeRect.width, 0) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 6240f4a37..9ff1e5970 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1075,7 +1075,7 @@ class TextEditorPresenter @linesByScreenRow.set(startRow + index, line) lineIdForScreenRow: (screenRow) -> - @linesByScreenRow.get(screenRow).id + @linesByScreenRow.get(screenRow)?.id fetchDecorations: -> return unless 0 <= @startRow <= @endRow <= Infinity From bf5a0d8c8cca9c4c2d51006ef0727c64accf60d8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 17 Mar 2016 15:11:27 +0100 Subject: [PATCH 508/971] Adjust assertions based on the new soft-wrap logic --- spec/text-editor-component-spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index ffcf38ea2..ea908a64b 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -3095,7 +3095,7 @@ describe('TextEditorComponent', function () { gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(11), { shiftKey: true })) - expect(editor.getSelectedScreenRange()).toEqual([[7, 4], [16, 0]]) + expect(editor.getSelectedScreenRange()).toEqual([[7, 4], [17, 0]]) }) }) }) @@ -3169,7 +3169,7 @@ describe('TextEditorComponent', function () { gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(11), { metaKey: true })) - expect(editor.getSelectedScreenRanges()).toEqual([[[7, 4], [7, 6]], [[11, 4], [19, 0]]]) + expect(editor.getSelectedScreenRanges()).toEqual([[[7, 4], [7, 6]], [[11, 4], [20, 0]]]) }) it('merges overlapping selections on mouseup', async function () { @@ -3183,7 +3183,7 @@ describe('TextEditorComponent', function () { gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(5), { metaKey: true })) - expect(editor.getSelectedScreenRanges()).toEqual([[[5, 0], [19, 0]]]) + expect(editor.getSelectedScreenRanges()).toEqual([[[5, 0], [20, 0]]]) }) }) }) @@ -3198,7 +3198,7 @@ describe('TextEditorComponent', function () { })) gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(11))) await nextAnimationFramePromise() - expect(editor.getSelectedScreenRange()).toEqual([[1, 4], [11, 14]]) + expect(editor.getSelectedScreenRange()).toEqual([[1, 4], [11, 5]]) }) }) From 215f1207897cb9faa4de7c187b155be1a20e869d Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 17 Mar 2016 10:34:02 -0400 Subject: [PATCH 509/971] :arrow_up: status-bar@1.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a3246926f..6a5c18986 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.234.0", "snippets": "1.0.1", "spell-check": "0.67.0", - "status-bar": "1.1.2", + "status-bar": "1.2.0", "styleguide": "0.45.2", "symbols-view": "0.112.0", "tabs": "0.92.0", From e3a5a93b88e4ff9bbee47c26d72b8cd24fea87e1 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Thu, 17 Mar 2016 15:45:24 +0100 Subject: [PATCH 510/971] :arrow_up: settings@0.235.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a3246926f..29df10850 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "notifications": "0.63.1", "open-on-github": "1.0.1", "package-generator": "1.0.0", - "settings-view": "0.234.0", + "settings-view": "0.235.0", "snippets": "1.0.1", "spell-check": "0.67.0", "status-bar": "1.1.2", From 292233f6cba59e1133cf41e297a9b1a5ce7e711a Mon Sep 17 00:00:00 2001 From: Hubot Date: Thu, 17 Mar 2016 12:11:31 -0500 Subject: [PATCH 511/971] 1.8.0-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c75ef505d..4a6f1504d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "1.7.0-dev", + "version": "1.8.0-dev", "description": "A hackable text editor for the 21st Century.", "main": "./src/browser/main.js", "repository": { From acbd34931259f6d0364409c0520d4ae037fdcbe4 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 17 Mar 2016 13:43:06 -0400 Subject: [PATCH 512/971] Revert ":arrow_up: status-bar@1.2.0" This reverts commit 215f1207897cb9faa4de7c187b155be1a20e869d. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4a6f1504d..4d1a1e7e8 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.235.0", "snippets": "1.0.1", "spell-check": "0.67.0", - "status-bar": "1.2.0", + "status-bar": "1.1.2", "styleguide": "0.45.2", "symbols-view": "0.112.0", "tabs": "0.92.0", From ffe13db768c8d8df52005737ccc8fa44ada14d51 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 17 Mar 2016 15:51:33 -0400 Subject: [PATCH 513/971] Revert "Revert ":arrow_up: status-bar@1.2.0"" This reverts commit acbd34931259f6d0364409c0520d4ae037fdcbe4. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4d1a1e7e8..4a6f1504d 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.235.0", "snippets": "1.0.1", "spell-check": "0.67.0", - "status-bar": "1.1.2", + "status-bar": "1.2.0", "styleguide": "0.45.2", "symbols-view": "0.112.0", "tabs": "0.92.0", From 4e9fa72fd115294cbd3292ecbfe321c9928d51f8 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 17 Mar 2016 13:06:53 -0700 Subject: [PATCH 514/971] :arrow_up: about --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4d1a1e7e8..8160c0bae 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "one-light-syntax": "1.2.0", "solarized-dark-syntax": "1.0.0", "solarized-light-syntax": "1.0.0", - "about": "1.4.1", + "about": "1.4.2", "archive-view": "0.61.1", "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.0", From 7863ffbac7162f5f61ee011aa0a0a16d03e7dca1 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 17 Mar 2016 22:03:16 -0400 Subject: [PATCH 515/971] :arrow_up: incompatible-packages@0.26 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4a6f1504d..30f0d8281 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "go-to-line": "0.30.0", "grammar-selector": "0.48.1", "image-view": "0.57.0", - "incompatible-packages": "0.25.1", + "incompatible-packages": "0.26", "keybinding-resolver": "0.35.0", "line-ending-selector": "0.3.1", "link": "0.31.1", From 85526198f7a31fa742ca087b428476b362f164ee Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 17 Mar 2016 22:03:26 -0400 Subject: [PATCH 516/971] :arrow_up: line-ending-selector@0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 30f0d8281..6cc55f0f8 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "image-view": "0.57.0", "incompatible-packages": "0.26", "keybinding-resolver": "0.35.0", - "line-ending-selector": "0.3.1", + "line-ending-selector": "0.4", "link": "0.31.1", "markdown-preview": "0.158.0", "metrics": "0.53.1", From 145396a73f6068a2179f029d21fd082c2bd63732 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 17 Mar 2016 22:05:15 -0400 Subject: [PATCH 517/971] Hwoops --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6cc55f0f8..bf59741d0 100644 --- a/package.json +++ b/package.json @@ -95,9 +95,9 @@ "go-to-line": "0.30.0", "grammar-selector": "0.48.1", "image-view": "0.57.0", - "incompatible-packages": "0.26", + "incompatible-packages": "0.26.0", "keybinding-resolver": "0.35.0", - "line-ending-selector": "0.4", + "line-ending-selector": "0.4.0", "link": "0.31.1", "markdown-preview": "0.158.0", "metrics": "0.53.1", From 1c5ad8db0fa56bf689a66d6dafb7417866007859 Mon Sep 17 00:00:00 2001 From: Robert Fruchtman Date: Thu, 17 Mar 2016 19:11:07 -0700 Subject: [PATCH 518/971] destory -> destroy --- spec/workspace-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 38d4839b0..97139f6bb 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -624,7 +624,7 @@ describe "Workspace", -> expect(pane.getItems()).toEqual [editor1, editor2] describe "when replacing a pending item which is the last item in a second pane", -> - it "does not destory the pane even if core.destroyEmptyPanes is on", -> + it "does not destroy the pane even if core.destroyEmptyPanes is on", -> atom.config.set('core.destroyEmptyPanes', true) editor1 = null editor2 = null From 543015af9795a42790ebf6594ee82506474956a8 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 17 Mar 2016 22:19:38 -0400 Subject: [PATCH 519/971] :arrow_up: incompatible-packages@0.26.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bf59741d0..27ebb38a6 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "go-to-line": "0.30.0", "grammar-selector": "0.48.1", "image-view": "0.57.0", - "incompatible-packages": "0.26.0", + "incompatible-packages": "0.26.1", "keybinding-resolver": "0.35.0", "line-ending-selector": "0.4.0", "link": "0.31.1", From dd5886e135369689094946d35463483039b6afd9 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 17 Mar 2016 22:19:48 -0400 Subject: [PATCH 520/971] :arrow_up: line-ending-selector@0.4.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 27ebb38a6..16e84fc62 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "image-view": "0.57.0", "incompatible-packages": "0.26.1", "keybinding-resolver": "0.35.0", - "line-ending-selector": "0.4.0", + "line-ending-selector": "0.4.1", "link": "0.31.1", "markdown-preview": "0.158.0", "metrics": "0.53.1", From 1d1bdf587200e91f531a089305827a4d7760d927 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 18 Mar 2016 10:51:45 +0100 Subject: [PATCH 521/971] Improve folds behavior when duplicating lines Now we will select the entire screen line (which could contain some free-form fold), and we duplicate its contents preserving the structure of the existing folds. --- src/text-editor.coffee | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 52d5ffd47..d64b7107c 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1085,15 +1085,12 @@ class TextEditor extends Model selectedBufferRange = selection.getBufferRange() if selection.isEmpty() {start} = selection.getScreenRange() - selection.selectToScreenPosition([start.row + 1, 0]) + selection.setScreenRange([[start.row, 0], [start.row + 1, 0]], preserveFolds: true) [startRow, endRow] = selection.getBufferRowRange() endRow++ - foldedRowRanges = - @outermostFoldsInBufferRowRange(startRow, endRow) - .map (fold) -> fold.getBufferRowRange() - + outermostFolds = @displayLayer.outermostFoldsInBufferRowRange(startRow, endRow) rangeToDuplicate = [[startRow, 0], [endRow, 0]] textToDuplicate = @getTextInBufferRange(rangeToDuplicate) textToDuplicate = '\n' + textToDuplicate if endRow > @getLastBufferRow() @@ -1101,8 +1098,9 @@ class TextEditor extends Model delta = endRow - startRow selection.setBufferRange(selectedBufferRange.translate([delta, 0])) - for [foldStartRow, foldEndRow] in foldedRowRanges - @foldBufferRowRange(foldStartRow + delta, foldEndRow + delta) + for fold in outermostFolds + foldRange = @displayLayer.bufferRangeForFold(fold) + @displayLayer.foldBufferRange(foldRange.translate([delta, 0])) return replaceSelectedText: (options={}, fn) -> @@ -3013,9 +3011,9 @@ class TextEditor extends Model destroyFoldsIntersectingBufferRange: (bufferRange) -> @displayLayer.destroyFoldsIntersectingBufferRange(bufferRange) - # {Delegates to: DisplayBuffer.outermostFoldsForBufferRowRange} + # {Delegates to: DisplayLayer.outermostFoldsForBufferRowRange} outermostFoldsInBufferRowRange: (startRow, endRow) -> - @displayBuffer.outermostFoldsInBufferRowRange(startRow, endRow) + @displayLayer.outermostFoldsInBufferRowRange(startRow, endRow) ### Section: Gutters From 83da3ca440afa6e3f9e39f04dabdaa68587d34cc Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 18 Mar 2016 11:24:22 +0100 Subject: [PATCH 522/971] Fix moveLineUp and moveLineDown --- src/text-editor.coffee | 46 ++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index d64b7107c..f0f4ee12f 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -945,14 +945,14 @@ class TextEditor extends Model # insert delta is know. selectionFoldRanges = [] foldAtSelectionStart = - @displayBuffer.largestFoldContainingBufferRow(selection.start.row) + @displayLayer.largestFoldContainingBufferRow(selection.start.row) foldAtSelectionEnd = - @displayBuffer.largestFoldContainingBufferRow(selection.end.row) + @displayLayer.largestFoldContainingBufferRow(selection.end.row) if fold = foldAtSelectionStart ? foldAtSelectionEnd - selectionFoldRanges.push range = fold.getBufferRange() + selectionFoldRanges.push range = @displayLayer.bufferRangeForFold(fold) newEndRow = range.end.row + 1 linesRange.end.row = newEndRow if newEndRow > linesRange.end.row - fold.destroy() + @displayLayer.destroyFold(fold) # If selected line range is preceded by a fold, one line above on screen # could be multiple lines in the buffer. @@ -963,16 +963,18 @@ class TextEditor extends Model # Any folds in the text that is moved will need to be re-created. # It includes the folds that were intersecting with the selection. rangesToRefold = selectionFoldRanges.concat( - @outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> - range = fold.getBufferRange() - fold.destroy() + @displayLayer.outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> + range = @displayLayer.bufferRangeForFold(fold) + @displayLayer.destroyFold(fold) range ).map (range) -> range.translate([-insertDelta, 0]) - # # Make sure the inserted text doesn't go into an existing fold - # if fold = @displayBuffer.largestFoldStartingAtBufferRow(precedingBufferRow) - # rangesToRefold.push(fold.getBufferRange().translate([linesRange.getRowCount() - 1, 0])) - # fold.destroy() + # Make sure the inserted text doesn't go into an existing fold + if fold = @displayLayer.largestFoldStartingAtBufferRow(precedingBufferRow) + rangesToRefold.push( + @displayLayer.bufferRangeForFold(fold).translate([linesRange.getRowCount() - 1, 0]) + ) + @displayLayer.destroyFold(fold) # Delete lines spanned by selection and insert them on the preceding buffer row lines = @buffer.getTextInRange(linesRange) @@ -982,7 +984,7 @@ class TextEditor extends Model # Restore folds that existed before the lines were moved for rangeToRefold in rangesToRefold - @displayBuffer.foldBufferRowRange(rangeToRefold.start.row, rangeToRefold.end.row) + @displayLayer.foldBufferRange(rangeToRefold) for selection in selectionsToMove newSelectionRanges.push(selection.translate([-insertDelta, 0])) @@ -1027,14 +1029,14 @@ class TextEditor extends Model # insert delta is know. selectionFoldRanges = [] foldAtSelectionStart = - @displayBuffer.largestFoldContainingBufferRow(selection.start.row) + @displayLayer.largestFoldContainingBufferRow(selection.start.row) foldAtSelectionEnd = - @displayBuffer.largestFoldContainingBufferRow(selection.end.row) + @displayLayer.largestFoldContainingBufferRow(selection.end.row) if fold = foldAtSelectionStart ? foldAtSelectionEnd - selectionFoldRanges.push range = fold.getBufferRange() + selectionFoldRanges.push range = @displayLayer.bufferRangeForFold(fold) newEndRow = range.end.row + 1 linesRange.end.row = newEndRow if newEndRow > linesRange.end.row - fold.destroy() + @displayLayer.destroyFold(fold) # If selected line range is followed by a fold, one line below on screen # could be multiple lines in the buffer. But at the same time, if the @@ -1047,16 +1049,16 @@ class TextEditor extends Model # Any folds in the text that is moved will need to be re-created. # It includes the folds that were intersecting with the selection. rangesToRefold = selectionFoldRanges.concat( - @outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> + @displayLayer.outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> range = fold.getBufferRange() - fold.destroy() + @displayLayer.destroyFold(fold) range ).map (range) -> range.translate([insertDelta, 0]) # # Make sure the inserted text doesn't go into an existing fold - # if fold = @displayBuffer.largestFoldStartingAtBufferRow(followingBufferRow) - # rangesToRefold.push(fold.getBufferRange().translate([insertDelta - 1, 0])) - # fold.destroy() + if fold = @displayLayer.largestFoldStartingAtBufferRow(followingBufferRow) + rangesToRefold.push(@displayLayer.bufferRangeForFold(fold).translate([insertDelta - 1, 0])) + @displayLayer.destroyFold(fold) # Delete lines spanned by selection and insert them on the following correct buffer row insertPosition = new Point(selection.translate([insertDelta, 0]).start.row, 0) @@ -1069,7 +1071,7 @@ class TextEditor extends Model # Restore folds that existed before the lines were moved for rangeToRefold in rangesToRefold - @displayBuffer.foldBufferRowRange(rangeToRefold.start.row, rangeToRefold.end.row) + @displayLayer.foldBufferRange(rangeToRefold) for selection in selectionsToMove newSelectionRanges.push(selection.translate([insertDelta, 0])) From d86309e46b835ce41f7e5d650acb45a695465311 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 18 Mar 2016 11:33:29 +0100 Subject: [PATCH 523/971] Use DisplayLayer.prototype.foldBufferRange in Selection.prototype.fold --- src/selection.coffee | 4 ++-- src/text-editor.coffee | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/selection.coffee b/src/selection.coffee index faee09742..b90bb6854 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -628,8 +628,8 @@ class Selection extends Model # Public: Creates a fold containing the current selection. fold: -> range = @getBufferRange() - @editor.foldBufferRowRange(range.start.row, range.end.row) - @cursor.setBufferPosition([range.end.row + 1, 0]) + @editor.foldBufferRange(range) + @cursor.setBufferPosition(range.end) # Private: Increase the indentation level of the given text by given number # of levels. Leaves the first line unchanged. diff --git a/src/text-editor.coffee b/src/text-editor.coffee index f0f4ee12f..44eab56ea 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -3009,6 +3009,9 @@ class TextEditor extends Model foldBufferRowRange: (startRow, endRow) -> @displayBuffer.foldBufferRowRange(startRow, endRow) + foldBufferRange: (range) -> + @displayLayer.foldBufferRange(range) + # Remove any {Fold}s found that intersect the given buffer range. destroyFoldsIntersectingBufferRange: (bufferRange) -> @displayLayer.destroyFoldsIntersectingBufferRange(bufferRange) From 5fc699f791b3474531a8b8502310b956c225059c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 18 Mar 2016 11:44:23 +0100 Subject: [PATCH 524/971] Use intersecting folds when duplicating lines --- src/text-editor.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 44eab56ea..8220e09c2 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1092,7 +1092,7 @@ class TextEditor extends Model [startRow, endRow] = selection.getBufferRowRange() endRow++ - outermostFolds = @displayLayer.outermostFoldsInBufferRowRange(startRow, endRow) + intersectingFolds = @displayLayer.foldsIntersectingBufferRange([[startRow, 0], [endRow, 0]]) rangeToDuplicate = [[startRow, 0], [endRow, 0]] textToDuplicate = @getTextInBufferRange(rangeToDuplicate) textToDuplicate = '\n' + textToDuplicate if endRow > @getLastBufferRow() @@ -1100,7 +1100,7 @@ class TextEditor extends Model delta = endRow - startRow selection.setBufferRange(selectedBufferRange.translate([delta, 0])) - for fold in outermostFolds + for fold in intersectingFolds foldRange = @displayLayer.bufferRangeForFold(fold) @displayLayer.foldBufferRange(foldRange.translate([delta, 0])) return From d666e4c008f44ad95b5423aa9ab6cca0c0a6d5a0 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 18 Mar 2016 11:48:52 +0100 Subject: [PATCH 525/971] Fix lastScreenRowForBufferRow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …which was mistakenly translating screen positions to buffer positions, and not vice versa. --- src/display-buffer.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 4db1c922e..b16bc9e24 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -441,7 +441,7 @@ class DisplayBuffer extends Model if @largeFileMode bufferRow else - @displayLayer.translateScreenPosition(Point(screenRow, 0), clip: 'forward').row + @displayLayer.translateBufferPosition(Point(bufferRow, 0), clip: 'forward').row # Given a screen row, this converts it into a buffer row. # From d1306ae9442b0140bc3d54ceb2efeaeeaed39cdf Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 18 Mar 2016 14:06:46 +0100 Subject: [PATCH 526/971] Remove invisibles handling from TokenizedBuffer --- spec/text-editor-spec.coffee | 3 +-- src/display-buffer.coffee | 11 +++++++---- src/text-editor.coffee | 2 +- src/tokenized-buffer.coffee | 24 +++--------------------- 4 files changed, 12 insertions(+), 28 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 852e7b50a..f52bb1bb3 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5837,8 +5837,7 @@ describe "TextEditor", -> it "ignores invisibles even if editor.showInvisibles is true", -> atom.config.set('editor.showInvisibles', true) - invisibles = editor.tokenizedLineForScreenRow(0).invisibles - expect(invisibles).toBe(null) + expect(editor.lineTextForScreenRow(0).indexOf(atom.config.get('editor.invisibles.eol'))).toBe(-1) describe "when the editor is constructed with the grammar option set", -> beforeEach -> diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index b16bc9e24..a96aba529 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -39,7 +39,7 @@ class DisplayBuffer extends Model super { - tabLength, @editorWidthInChars, @tokenizedBuffer, buffer, ignoreInvisibles, + tabLength, @editorWidthInChars, @tokenizedBuffer, buffer, @ignoreInvisibles, @largeFileMode, @config, @assert, @grammarRegistry, @packageManager } = params @@ -47,7 +47,7 @@ class DisplayBuffer extends Model @disposables = new CompositeDisposable @tokenizedBuffer ?= new TokenizedBuffer({ - tabLength, buffer, ignoreInvisibles, @largeFileMode, @config, + tabLength, buffer, @largeFileMode, @config, @grammarRegistry, @packageManager, @assert }) @buffer = @tokenizedBuffer.buffer @@ -101,7 +101,7 @@ class DisplayBuffer extends Model resetDisplayLayer: -> scopeDescriptor = @getRootScopeDescriptor() invisibles = - if @config.get('editor.showInvisibles', scope: scopeDescriptor) + if @config.get('editor.showInvisibles', scope: scopeDescriptor) and not @ignoreInvisibles @config.get('editor.invisibles', scope: scopeDescriptor) else {} @@ -260,7 +260,10 @@ class DisplayBuffer extends Model @tokenizedBuffer.setTabLength(tabLength) setIgnoreInvisibles: (ignoreInvisibles) -> - @tokenizedBuffer.setIgnoreInvisibles(ignoreInvisibles) + return if ignoreInvisibles is @ignoreInvisibles + + @ignoreInvisibles = ignoreInvisibles + @resetDisplayLayer() setSoftWrapped: (softWrapped) -> if softWrapped isnt @softWrapped diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 8220e09c2..f3082814e 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1055,7 +1055,7 @@ class TextEditor extends Model range ).map (range) -> range.translate([insertDelta, 0]) - # # Make sure the inserted text doesn't go into an existing fold + # Make sure the inserted text doesn't go into an existing fold if fold = @displayLayer.largestFoldStartingAtBufferRow(followingBufferRow) rangesToRefold.push(@displayLayer.bufferRangeForFold(fold).translate([insertDelta - 1, 0])) @displayLayer.destroyFold(fold) diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index eb2230e37..3bb0f8bfc 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -36,7 +36,7 @@ class TokenizedBuffer extends Model constructor: (params) -> { - @buffer, @tabLength, @ignoreInvisibles, @largeFileMode, @config, + @buffer, @tabLength, @largeFileMode, @config, @grammarRegistry, @packageManager, @assert, grammarScopeName } = params @@ -74,7 +74,6 @@ class TokenizedBuffer extends Model bufferPath: @buffer.getPath() bufferId: @buffer.getId() tabLength: @tabLength - ignoreInvisibles: @ignoreInvisibles largeFileMode: @largeFileMode } state.grammarScopeName = @grammar?.scopeName unless @buffer.getPath() @@ -128,11 +127,6 @@ class TokenizedBuffer extends Model @configSubscriptions.add @config.onDidChange 'editor.tabLength', scopeOptions, ({newValue}) => @configSettings.tabLength = newValue @retokenizeLines() - ['invisibles', 'showInvisibles'].forEach (key) => - @configSubscriptions.add @config.onDidChange "editor.#{key}", scopeOptions, ({newValue}) => - oldInvisibles = @getInvisiblesToShow() - @configSettings[key] = newValue - @retokenizeLines() unless _.isEqual(@getInvisiblesToShow(), oldInvisibles) @disposables.add(@configSubscriptions) @retokenizeLines() @@ -175,12 +169,6 @@ class TokenizedBuffer extends Model @tabLength = tabLength @retokenizeLines() - setIgnoreInvisibles: (ignoreInvisibles) -> - if ignoreInvisibles isnt @ignoreInvisibles - @ignoreInvisibles = ignoreInvisibles - if @configSettings.showInvisibles and @configSettings.invisibles? - @retokenizeLines() - tokenizeInBackground: -> return if not @visible or @pendingChunk or not @isAlive() @@ -362,7 +350,7 @@ class TokenizedBuffer extends Model tabLength = @getTabLength() indentLevel = @indentLevelForRow(row) lineEnding = @buffer.lineEndingForRow(row) - new TokenizedLine({openScopes, text, tags, tabLength, indentLevel, invisibles: @getInvisiblesToShow(), lineEnding, @tokenIterator}) + new TokenizedLine({openScopes, text, tags, tabLength, indentLevel, lineEnding, @tokenIterator}) buildTokenizedLineForRow: (row, ruleStack, openScopes) -> @buildTokenizedLineForRowWithText(row, @buffer.lineForRow(row), ruleStack, openScopes) @@ -372,13 +360,7 @@ class TokenizedBuffer extends Model tabLength = @getTabLength() indentLevel = @indentLevelForRow(row) {tags, ruleStack} = @grammar.tokenizeLine(text, ruleStack, row is 0, false) - new TokenizedLine({openScopes, text, tags, ruleStack, tabLength, lineEnding, indentLevel, invisibles: @getInvisiblesToShow(), @tokenIterator}) - - getInvisiblesToShow: -> - if @configSettings.showInvisibles and not @ignoreInvisibles - @configSettings.invisibles - else - null + new TokenizedLine({openScopes, text, tags, ruleStack, tabLength, lineEnding, indentLevel, @tokenIterator}) tokenizedLineForRow: (bufferRow) -> if 0 <= bufferRow < @tokenizedLines.length From 3c70ff92f4acfe173511210502642e20d81b5bcf Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 18 Mar 2016 14:25:33 +0100 Subject: [PATCH 527/971] :green_heart: Add TextEditor.prototype.screenLineForScreenRow This is going to supplant our internal usage of TextEditor.prototype.tokenizedLineForScreenRow(). --- spec/text-editor-spec.coffee | 21 ++++++++++++--------- src/text-editor.coffee | 14 +++----------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index f52bb1bb3..d4f28d9cc 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5436,25 +5436,28 @@ describe "TextEditor", -> runs -> editor.setText("// SELECT * FROM OCTOCATS") - {tokens} = editor.tokenizedLineForScreenRow(0) - expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS" - expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"] + {tokens} = editor.screenLineForScreenRow(0) + expect(tokens[2].closeTags).toEqual ['comment.line.double-slash.js', 'source.js'] + expect(tokens[2].openTags).toEqual [] + expect(tokens[2].text).toBe "" waitsForPromise -> atom.packages.activatePackage('package-with-injection-selector') runs -> - {tokens} = editor.tokenizedLineForScreenRow(0) - expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS" - expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"] + {tokens} = editor.screenLineForScreenRow(0) + expect(tokens[2].closeTags).toEqual ['comment.line.double-slash.js', 'source.js'] + expect(tokens[2].openTags).toEqual [] + expect(tokens[2].text).toBe "" waitsForPromise -> atom.packages.activatePackage('language-sql') runs -> - {tokens} = editor.tokenizedLineForScreenRow(0) - expect(tokens[2].value).toBe "SELECT" - expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "keyword.other.DML.sql"] + {tokens} = editor.screenLineForScreenRow(2) + expect(tokens[2].closeTags).toEqual [] + expect(tokens[2].openTags).toEqual ["keyword.other.DML.sql"] + expect(tokens[2].text).toBe "SELECT" describe ".normalizeTabsInBufferRange()", -> it "normalizes tabs depending on the editor's soft tab/tab length settings", -> diff --git a/src/text-editor.coffee b/src/text-editor.coffee index f3082814e..a27d62f5c 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -774,18 +774,10 @@ class TextEditor extends Model # # * `screenRow` A {Number} representing a zero-indexed screen row. lineTextForScreenRow: (screenRow) -> - line = @displayLayer.getScreenLines(screenRow, screenRow + 1)[0] - line?.tokens.map((t) -> t.text).join('') + @screenLineForScreenRow(screenRow)?.tokens.map((t) -> t.text).join('') - # Gets the screen line for the given screen row. - # - # * `screenRow` - A {Number} indicating the screen row. - # - # Returns {TokenizedLine} - tokenizedLineForScreenRow: (screenRow) -> @displayBuffer.tokenizedLineForScreenRow(screenRow) - - # {Delegates to: DisplayBuffer.tokenizedLinesForScreenRows} - tokenizedLinesForScreenRows: (start, end) -> @displayBuffer.tokenizedLinesForScreenRows(start, end) + screenLineForScreenRow: (screenRow) -> + @displayLayer.getScreenLines(screenRow, screenRow + 1)[0] bufferRowForScreenRow: (row) -> @displayLayer.translateScreenPosition(Point(row, 0)).row From bbcbe9e809826ae45cbfb7ca0aa695ff459dcb3b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 18 Mar 2016 14:58:13 +0100 Subject: [PATCH 528/971] Implement ratioForCharacter --- src/display-buffer.coffee | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index a96aba529..4b869233a 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -7,6 +7,7 @@ Model = require './model' Token = require './token' Decoration = require './decoration' LayerDecoration = require './layer-decoration' +{isDoubleWidthCharacter, isHalfWidthCharacter, isKoreanCharacter} = require './text-utils' class BufferToScreenConversionError extends Error constructor: (@message, @metadata) -> @@ -120,7 +121,7 @@ class DisplayBuffer extends Model softWrapColumn: softWrapColumn showIndentGuides: @config.get('editor.showIndentGuide', scope: scopeDescriptor) tabLength: @config.get('editor.tabLength', scope: scopeDescriptor), - ratioForCharacter: -> 1.0 # TODO: replace this with korean/double/half width characters. + ratioForCharacter: @ratioForCharacter.bind(this) }) updateAllScreenLines: -> @@ -215,6 +216,16 @@ class DisplayBuffer extends Model getLineHeightInPixels: -> @lineHeightInPixels setLineHeightInPixels: (@lineHeightInPixels) -> @lineHeightInPixels + ratioForCharacter: (character) -> + if isKoreanCharacter(character) + @getKoreanCharWidth() / @getDefaultCharWidth() + else if isHalfWidthCharacter(character) + @getHalfWidthCharWidth() / @getDefaultCharWidth() + else if isDoubleWidthCharacter(character) + @getDoubleWidthCharWidth() / @getDefaultCharWidth() + else + 1 + getKoreanCharWidth: -> @koreanCharWidth getHalfWidthCharWidth: -> @halfWidthCharWidth From bb8f114dcb432f9424db1654e6cb4e6c37283c33 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Fri, 18 Mar 2016 09:56:33 -0700 Subject: [PATCH 529/971] Remove an unnecessary call to then(). I see that this file has a little use of async/await and many uses of `then()`. Things would be much less verbose and much more linear if async/await were used throughout. Would Atom be open to a PR that changes this? For bonus points, we could also replace `'use babel'` with `/* @flow */` at the top of the file :) --- src/git-repository-async.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index c5984eed4..82546b525 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -158,8 +158,7 @@ export default class GitRepositoryAsync { if (!this.projectAtRoot) { this.projectAtRoot = this.getRepo() - .then(repo => this.project.relativize(repo.workdir())) - .then(relativePath => relativePath === '') + .then(repo => this.project.relativize(repo.workdir()) === '') } return this.projectAtRoot From 0cd6bd19af144b53fac95be7e588e836e596fdef Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 18 Mar 2016 18:30:35 +0100 Subject: [PATCH 530/971] Implement isWrapBoundary for DisplayLayer So that we can correctly soft wrap CJK characters. --- spec/text-utils-spec.coffee | 37 +++++++++++++++++++------------------ src/display-buffer.coffee | 3 ++- src/text-utils.coffee | 13 ++++++++++++- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/spec/text-utils-spec.coffee b/spec/text-utils-spec.coffee index aa36c5003..bae7f5997 100644 --- a/spec/text-utils-spec.coffee +++ b/spec/text-utils-spec.coffee @@ -75,22 +75,23 @@ describe 'text utilities', -> expect(textUtils.isKoreanCharacter("O")).toBe(false) - describe ".isCJKCharacter(character)", -> - it "returns true when the character is either a korean, half-width or double-width character", -> - expect(textUtils.isCJKCharacter("我")).toBe(true) - expect(textUtils.isCJKCharacter("私")).toBe(true) - expect(textUtils.isCJKCharacter("B")).toBe(true) - expect(textUtils.isCJKCharacter(",")).toBe(true) - expect(textUtils.isCJKCharacter("¢")).toBe(true) - expect(textUtils.isCJKCharacter("ハ")).toBe(true) - expect(textUtils.isCJKCharacter("ヒ")).toBe(true) - expect(textUtils.isCJKCharacter("ᆲ")).toBe(true) - expect(textUtils.isCJKCharacter("■")).toBe(true) - expect(textUtils.isCJKCharacter("우")).toBe(true) - expect(textUtils.isCJKCharacter("가")).toBe(true) - expect(textUtils.isCJKCharacter("ㅢ")).toBe(true) - expect(textUtils.isCJKCharacter("ㄼ")).toBe(true) + describe ".isWrapBoundary(previousCharacter, character)", -> + it "returns true when the character is CJK or when the previous character is a space/tab", -> + anyCharacter = 'x' + expect(textUtils.isWrapBoundary(anyCharacter, "我")).toBe(true) + expect(textUtils.isWrapBoundary(anyCharacter, "私")).toBe(true) + expect(textUtils.isWrapBoundary(anyCharacter, "B")).toBe(true) + expect(textUtils.isWrapBoundary(anyCharacter, ",")).toBe(true) + expect(textUtils.isWrapBoundary(anyCharacter, "¢")).toBe(true) + expect(textUtils.isWrapBoundary(anyCharacter, "ハ")).toBe(true) + expect(textUtils.isWrapBoundary(anyCharacter, "ヒ")).toBe(true) + expect(textUtils.isWrapBoundary(anyCharacter, "ᆲ")).toBe(true) + expect(textUtils.isWrapBoundary(anyCharacter, "■")).toBe(true) + expect(textUtils.isWrapBoundary(anyCharacter, "우")).toBe(true) + expect(textUtils.isWrapBoundary(anyCharacter, "가")).toBe(true) + expect(textUtils.isWrapBoundary(anyCharacter, "ㅢ")).toBe(true) + expect(textUtils.isWrapBoundary(anyCharacter, "ㄼ")).toBe(true) - expect(textUtils.isDoubleWidthCharacter("a")).toBe(false) - expect(textUtils.isDoubleWidthCharacter("O")).toBe(false) - expect(textUtils.isDoubleWidthCharacter("z")).toBe(false) + expect(textUtils.isWrapBoundary(' ', 'h')).toBe(true) + expect(textUtils.isWrapBoundary('\t', 'h')).toBe(true) + expect(textUtils.isWrapBoundary('a', 'h')).toBe(false) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 4b869233a..eb4faa1a5 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -7,7 +7,7 @@ Model = require './model' Token = require './token' Decoration = require './decoration' LayerDecoration = require './layer-decoration' -{isDoubleWidthCharacter, isHalfWidthCharacter, isKoreanCharacter} = require './text-utils' +{isDoubleWidthCharacter, isHalfWidthCharacter, isKoreanCharacter, isWrapBoundary} = require './text-utils' class BufferToScreenConversionError extends Error constructor: (@message, @metadata) -> @@ -122,6 +122,7 @@ class DisplayBuffer extends Model showIndentGuides: @config.get('editor.showIndentGuide', scope: scopeDescriptor) tabLength: @config.get('editor.tabLength', scope: scopeDescriptor), ratioForCharacter: @ratioForCharacter.bind(this) + isWrapBoundary: isWrapBoundary }) updateAllScreenLines: -> diff --git a/src/text-utils.coffee b/src/text-utils.coffee index af17335aa..f4d62772e 100644 --- a/src/text-utils.coffee +++ b/src/text-utils.coffee @@ -94,6 +94,13 @@ isCJKCharacter = (character) -> isHalfWidthCharacter(character) or isKoreanCharacter(character) +isWordStart = (previousCharacter, character) -> + (previousCharacter is ' ' or previousCharacter is '\t') and + (character isnt ' ' and character isnt '\t') + +isWrapBoundary = (previousCharacter, character) -> + isWordStart(previousCharacter, character) or isCJKCharacter(character) + # Does the given string contain at least surrogate pair, variation sequence, # or combined character? # @@ -107,4 +114,8 @@ hasPairedCharacter = (string) -> index++ false -module.exports = {isPairedCharacter, hasPairedCharacter, isDoubleWidthCharacter, isHalfWidthCharacter, isKoreanCharacter, isCJKCharacter} +module.exports = { + isPairedCharacter, hasPairedCharacter, + isDoubleWidthCharacter, isHalfWidthCharacter, isKoreanCharacter, + isWrapBoundary +} From 1a2f306db3225fb22b7b1f088c5340cc876f83c3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 18 Mar 2016 15:57:03 -0600 Subject: [PATCH 531/971] :shower: Remove commented code --- src/lines-tile-component.coffee | 43 --------------------------------- 1 file changed, 43 deletions(-) diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index 13a267055..03af7333b 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -277,49 +277,6 @@ class LinesTileComponent lineNode.appendChild(textNode) @currentLineTextNodes.push(textNode) - # lineState = @newTileState.lines[id] - # {firstNonWhitespaceIndex, firstTrailingWhitespaceIndex, invisibles} = lineState - # lineIsWhitespaceOnly = firstTrailingWhitespaceIndex is 0 - # - # @tokenIterator.reset(lineState) - # openScopeNode = lineNode - # - # while @tokenIterator.next() - # for scope in @tokenIterator.getScopeEnds() - # openScopeNode = openScopeNode.parentElement - # - # for scope in @tokenIterator.getScopeStarts() - # newScopeNode = @domElementPool.buildElement("span", scope.replace(/\.+/g, ' ')) - # openScopeNode.appendChild(newScopeNode) - # openScopeNode = newScopeNode - # - # tokenStart = @tokenIterator.getScreenStart() - # tokenEnd = @tokenIterator.getScreenEnd() - # tokenText = @tokenIterator.getText() - # isHardTab = @tokenIterator.isHardTab() - # - # if hasLeadingWhitespace = tokenStart < firstNonWhitespaceIndex - # tokenFirstNonWhitespaceIndex = firstNonWhitespaceIndex - tokenStart - # else - # tokenFirstNonWhitespaceIndex = null - # - # if hasTrailingWhitespace = tokenEnd > firstTrailingWhitespaceIndex - # tokenFirstTrailingWhitespaceIndex = Math.max(0, firstTrailingWhitespaceIndex - tokenStart) - # else - # tokenFirstTrailingWhitespaceIndex = null - # - # hasIndentGuide = - # @newState.indentGuidesVisible and - # (hasLeadingWhitespace or lineIsWhitespaceOnly) - # - # hasInvisibleCharacters = - # (invisibles?.tab and isHardTab) or - # (invisibles?.space and (hasLeadingWhitespace or hasTrailingWhitespace)) - # - # @appendTokenNodes(tokenText, isHardTab, tokenFirstNonWhitespaceIndex, tokenFirstTrailingWhitespaceIndex, hasIndentGuide, hasInvisibleCharacters, openScopeNode) - # - # @appendEndOfLineNodes(id, lineNode) - appendTokenNodes: (tokenText, isHardTab, firstNonWhitespaceIndex, firstTrailingWhitespaceIndex, hasIndentGuide, hasInvisibleCharacters, scopeNode) -> if isHardTab textNode = @domElementPool.buildText(tokenText) From d62ef599cd5f543d65e3e62163692696d3a2a687 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 18 Mar 2016 15:57:49 -0600 Subject: [PATCH 532/971] Replace tokens with tagCodes in DisplayLayer.prototype.getScreenLines --- src/lines-tile-component.coffee | 26 +++++++++++++------------- src/text-editor-presenter.coffee | 12 +++++++++++- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index 03af7333b..a815c5be5 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -253,26 +253,26 @@ class LinesTileComponent @currentLineTextNodes.push(textNode) setLineInnerNodes: (id, lineNode) -> - {tokens} = @newTileState.lines[id] + {lineText, tagCodes} = @newTileState.lines[id] + lineLength = 0 + startIndex = 0 openScopeNode = lineNode - for token in tokens when token.text.length > 0 - {closeTags, openTags, text} = token - - for scope in closeTags + for tagCode in tagCodes when tagCode isnt 0 + if @presenter.isCloseTagCode(tagCode) openScopeNode = openScopeNode.parentElement - - for scope in openTags + else if @presenter.isOpenTagCode(tagCode) + scope = @presenter.tagForCode(tagCode) newScopeNode = @domElementPool.buildElement("span", scope.replace(/\.+/g, ' ')) openScopeNode.appendChild(newScopeNode) openScopeNode = newScopeNode + else + textNode = @domElementPool.buildText(lineText.substr(startIndex, tagCode).replace(/\s/g, NBSPCharacter)) + startIndex += tagCode + openScopeNode.appendChild(textNode) + @currentLineTextNodes.push(textNode) - lineLength += text.length - textNode = @domElementPool.buildText(text.replace(/\s/g, NBSPCharacter)) - openScopeNode.appendChild(textNode) - @currentLineTextNodes.push(textNode) - - if lineLength is 0 + if startIndex is 0 textNode = @domElementPool.buildText(NBSPCharacter) lineNode.appendChild(textNode) @currentLineTextNodes.push(textNode) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 9ff1e5970..3d69606aa 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -436,7 +436,8 @@ class TextEditorPresenter else tileState.lines[line.id] = screenRow: screenRow - tokens: line.tokens + lineText: line.lineText + tagCodes: line.tagCodes decorationClasses: @lineDecorationClassesForRow(screenRow) precedingBlockDecorations: precedingBlockDecorations followingBlockDecorations: followingBlockDecorations @@ -1548,3 +1549,12 @@ class TextEditorPresenter isRowVisible: (row) -> @startRow <= row < @endRow + + isOpenTagCode: (tagCode) -> + @displayLayer.isOpenTagCode(tagCode) + + isCloseTagCode: (tagCode) -> + @displayLayer.isCloseTagCode(tagCode) + + tagForCode: (tagCode) -> + @displayLayer.tagForCode(tagCode) From 462157039b5f8d8f63f860095901021cbb3c1abe Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 18 Mar 2016 18:09:23 -0600 Subject: [PATCH 533/971] Drop indentLevel and soft wrap support from TokenizedLine --- src/tokenized-buffer.coffee | 19 +---- src/tokenized-line.coffee | 154 +----------------------------------- 2 files changed, 3 insertions(+), 170 deletions(-) diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 3bb0f8bfc..7a943d78c 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -261,9 +261,6 @@ class TokenizedBuffer extends Model newTokenizedLines = @buildTokenizedLinesForRows(start, end + delta, @stackForRow(start - 1), @openScopesForRow(start)) _.spliceWithArray(@tokenizedLines, start, end - start + 1, newTokenizedLines) - start = @retokenizeWhitespaceRowsIfIndentLevelChanged(start - 1, -1) - end = @retokenizeWhitespaceRowsIfIndentLevelChanged(newRange.end.row + 1, 1) - delta - newEndStack = @stackForRow(end + delta) if newEndStack and not _.isEqual(newEndStack, previousEndStack) @invalidateRow(end + delta + 1) @@ -273,16 +270,6 @@ class TokenizedBuffer extends Model event = {start, end, delta, bufferChange: e} @emitter.emit 'did-change', event - retokenizeWhitespaceRowsIfIndentLevelChanged: (row, increment) -> - line = @tokenizedLineForRow(row) - if line?.isOnlyWhitespace() and @indentLevelForRow(row) isnt line.indentLevel - while line?.isOnlyWhitespace() - @tokenizedLines[row] = @buildTokenizedLineForRow(row, @stackForRow(row - 1), @openScopesForRow(row)) - row += increment - line = @tokenizedLineForRow(row) - - row - increment - isFoldableAtRow: (row) -> if @largeFileMode false @@ -348,9 +335,8 @@ class TokenizedBuffer extends Model text = @buffer.lineForRow(row) tags = [text.length] tabLength = @getTabLength() - indentLevel = @indentLevelForRow(row) lineEnding = @buffer.lineEndingForRow(row) - new TokenizedLine({openScopes, text, tags, tabLength, indentLevel, lineEnding, @tokenIterator}) + new TokenizedLine({openScopes, text, tags, tabLength, lineEnding, @tokenIterator}) buildTokenizedLineForRow: (row, ruleStack, openScopes) -> @buildTokenizedLineForRowWithText(row, @buffer.lineForRow(row), ruleStack, openScopes) @@ -358,9 +344,8 @@ class TokenizedBuffer extends Model buildTokenizedLineForRowWithText: (row, text, ruleStack = @stackForRow(row - 1), openScopes = @openScopesForRow(row)) -> lineEnding = @buffer.lineEndingForRow(row) tabLength = @getTabLength() - indentLevel = @indentLevelForRow(row) {tags, ruleStack} = @grammar.tokenizeLine(text, ruleStack, row is 0, false) - new TokenizedLine({openScopes, text, tags, ruleStack, tabLength, lineEnding, indentLevel, @tokenIterator}) + new TokenizedLine({openScopes, text, tags, ruleStack, tabLength, lineEnding, @tokenIterator}) tokenizedLineForRow: (bufferRow) -> if 0 <= bufferRow < @tokenizedLines.length diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index 5d092ab82..cca1a3543 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -41,7 +41,7 @@ class TokenizedLine @specialTokens = {} {@openScopes, @text, @tags, @lineEnding, @ruleStack, @tokenIterator} = properties - {@startBufferColumn, @fold, @tabLength, @indentLevel, @invisibles} = properties + {@startBufferColumn, @fold, @tabLength, @invisibles} = properties @startBufferColumn ?= 0 @bufferDelta = @text.length @@ -93,7 +93,6 @@ class TokenizedLine copy.lineEnding = @lineEnding copy.invisibles = @invisibles copy.endOfLineInvisibles = @endOfLineInvisibles - copy.indentLevel = @indentLevel copy.tabLength = @tabLength copy.firstNonWhitespaceIndex = @firstNonWhitespaceIndex copy.firstTrailingWhitespaceIndex = @firstTrailingWhitespaceIndex @@ -173,157 +172,6 @@ class TokenizedLine getMaxBufferColumn: -> @startBufferColumn + @bufferDelta - # Given a boundary column, finds the point where this line would wrap. - # - # maxColumn - The {Number} where you want soft wrapping to occur - # - # Returns a {Number} representing the `line` position where the wrap would take place. - # Returns `null` if a wrap wouldn't occur. - findWrapColumn: (maxColumn) -> - return unless maxColumn? - return unless @text.length > maxColumn - - if /\s/.test(@text[maxColumn]) - # search forward for the start of a word past the boundary - for column in [maxColumn..@text.length] - return column if /\S/.test(@text[column]) - - return @text.length - else if isCJKCharacter(@text[maxColumn]) - maxColumn - else - # search backward for the start of the word on the boundary - for column in [maxColumn..@firstNonWhitespaceIndex] - if /\s/.test(@text[column]) or isCJKCharacter(@text[column]) - return column + 1 - - return maxColumn - - softWrapAt: (column, hangingIndent) -> - return [null, this] if column is 0 - - leftText = @text.substring(0, column) - rightText = @text.substring(column) - - leftTags = [] - rightTags = [] - - leftSpecialTokens = {} - rightSpecialTokens = {} - - rightOpenScopes = @openScopes.slice() - - screenColumn = 0 - - for tag, index in @tags - # tag represents a token - if tag >= 0 - # token ends before the soft wrap column - if screenColumn + tag <= column - if specialToken = @specialTokens[index] - leftSpecialTokens[index] = specialToken - leftTags.push(tag) - screenColumn += tag - - # token starts before and ends after the split column - else if screenColumn <= column - leftSuffix = column - screenColumn - rightPrefix = screenColumn + tag - column - - leftTags.push(leftSuffix) if leftSuffix > 0 - - softWrapIndent = @indentLevel * @tabLength + (hangingIndent ? 0) - for i in [0...softWrapIndent] by 1 - rightText = ' ' + rightText - remainingSoftWrapIndent = softWrapIndent - while remainingSoftWrapIndent > 0 - indentToken = Math.min(remainingSoftWrapIndent, @tabLength) - rightSpecialTokens[rightTags.length] = SoftWrapIndent - rightTags.push(indentToken) - remainingSoftWrapIndent -= indentToken - - rightTags.push(rightPrefix) if rightPrefix > 0 - - screenColumn += tag - - # token is after split column - else - if specialToken = @specialTokens[index] - rightSpecialTokens[rightTags.length] = specialToken - rightTags.push(tag) - - # tag represents the start of a scope - else if (tag % 2) is -1 - if screenColumn < column - leftTags.push(tag) - rightOpenScopes.push(tag) - else - rightTags.push(tag) - - # tag represents the end of a scope - else - if screenColumn <= column - leftTags.push(tag) - rightOpenScopes.pop() - else - rightTags.push(tag) - - splitBufferColumn = @bufferColumnForScreenColumn(column) - - leftFragment = new TokenizedLine - leftFragment.tokenIterator = @tokenIterator - leftFragment.openScopes = @openScopes - leftFragment.text = leftText - leftFragment.tags = leftTags - leftFragment.specialTokens = leftSpecialTokens - leftFragment.startBufferColumn = @startBufferColumn - leftFragment.bufferDelta = splitBufferColumn - @startBufferColumn - leftFragment.ruleStack = @ruleStack - leftFragment.invisibles = @invisibles - leftFragment.lineEnding = null - leftFragment.indentLevel = @indentLevel - leftFragment.tabLength = @tabLength - leftFragment.firstNonWhitespaceIndex = Math.min(column, @firstNonWhitespaceIndex) - leftFragment.firstTrailingWhitespaceIndex = Math.min(column, @firstTrailingWhitespaceIndex) - - rightFragment = new TokenizedLine - rightFragment.tokenIterator = @tokenIterator - rightFragment.openScopes = rightOpenScopes - rightFragment.text = rightText - rightFragment.tags = rightTags - rightFragment.specialTokens = rightSpecialTokens - rightFragment.startBufferColumn = splitBufferColumn - rightFragment.bufferDelta = @startBufferColumn + @bufferDelta - splitBufferColumn - rightFragment.ruleStack = @ruleStack - rightFragment.invisibles = @invisibles - rightFragment.lineEnding = @lineEnding - rightFragment.indentLevel = @indentLevel - rightFragment.tabLength = @tabLength - rightFragment.endOfLineInvisibles = @endOfLineInvisibles - rightFragment.firstNonWhitespaceIndex = Math.max(softWrapIndent, @firstNonWhitespaceIndex - column + softWrapIndent) - rightFragment.firstTrailingWhitespaceIndex = Math.max(softWrapIndent, @firstTrailingWhitespaceIndex - column + softWrapIndent) - - [leftFragment, rightFragment] - - isSoftWrapped: -> - @lineEnding is null - - isColumnInsideSoftWrapIndentation: (targetColumn) -> - targetColumn < @getSoftWrapIndentationDelta() - - getSoftWrapIndentationDelta: -> - delta = 0 - for tag, index in @tags - if tag >= 0 - if @specialTokens[index] is SoftWrapIndent - delta += tag - else - break - delta - - hasOnlySoftWrapIndentation: -> - @getSoftWrapIndentationDelta() is @text.length - tokenAtBufferColumn: (bufferColumn) -> @tokens[@tokenIndexAtBufferColumn(bufferColumn)] From 1994e3b404a7221c6db6a1a765e03ddc720b6fa3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 18 Mar 2016 18:09:57 -0600 Subject: [PATCH 534/971] Remove TokenizedLine::copy --- src/tokenized-line.coffee | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index cca1a3543..402c95eb2 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -80,25 +80,6 @@ class TokenizedLine tokens - copy: -> - copy = new TokenizedLine - copy.tokenIterator = @tokenIterator - copy.openScopes = @openScopes - copy.text = @text - copy.tags = @tags - copy.specialTokens = @specialTokens - copy.startBufferColumn = @startBufferColumn - copy.bufferDelta = @bufferDelta - copy.ruleStack = @ruleStack - copy.lineEnding = @lineEnding - copy.invisibles = @invisibles - copy.endOfLineInvisibles = @endOfLineInvisibles - copy.tabLength = @tabLength - copy.firstNonWhitespaceIndex = @firstNonWhitespaceIndex - copy.firstTrailingWhitespaceIndex = @firstTrailingWhitespaceIndex - copy.fold = @fold - copy - # This clips a given screen column to a valid column that's within the line # and not in the middle of any atomic tokens. # From b0c5870425d5e56cf3e010d848ff362b58ca013d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 18 Mar 2016 18:15:41 -0600 Subject: [PATCH 535/971] Remove dead code from TokenizedLine --- src/tokenized-line.coffee | 92 --------------------------------------- 1 file changed, 92 deletions(-) diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index 402c95eb2..62dadc836 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -20,14 +20,6 @@ TabStringsByLength = { idCounter = 1 -getTabString = (length) -> - TabStringsByLength[length] ?= buildTabString(length) - -buildTabString = (length) -> - string = SpaceString - string += SpaceString for i in [1...length] by 1 - string - module.exports = class TokenizedLine endOfLineInvisibles: null @@ -80,79 +72,6 @@ class TokenizedLine tokens - # This clips a given screen column to a valid column that's within the line - # and not in the middle of any atomic tokens. - # - # column - A {Number} representing the column to clip - # options - A hash with the key clip. Valid values for this key: - # 'closest' (default): clip to the closest edge of an atomic token. - # 'forward': clip to the forward edge. - # 'backward': clip to the backward edge. - # - # Returns a {Number} representing the clipped column. - clipScreenColumn: (column, options={}) -> - return 0 if @tags.length is 0 - - {clip} = options - column = Math.min(column, @getMaxScreenColumn()) - - tokenStartColumn = 0 - - iterator = @getTokenIterator() - while iterator.next() - break if iterator.getScreenEnd() > column - - if iterator.isSoftWrapIndentation() - iterator.next() while iterator.isSoftWrapIndentation() - iterator.getScreenStart() - else if iterator.isAtomic() and iterator.getScreenStart() < column - if clip is 'forward' - iterator.getScreenEnd() - else if clip is 'backward' - iterator.getScreenStart() - else #'closest' - if column > ((iterator.getScreenStart() + iterator.getScreenEnd()) / 2) - iterator.getScreenEnd() - else - iterator.getScreenStart() - else - column - - screenColumnForBufferColumn: (targetBufferColumn, options) -> - iterator = @getTokenIterator() - while iterator.next() - tokenBufferStart = iterator.getBufferStart() - tokenBufferEnd = iterator.getBufferEnd() - if tokenBufferStart <= targetBufferColumn < tokenBufferEnd - overshoot = targetBufferColumn - tokenBufferStart - return Math.min( - iterator.getScreenStart() + overshoot, - iterator.getScreenEnd() - ) - iterator.getScreenEnd() - - bufferColumnForScreenColumn: (targetScreenColumn) -> - iterator = @getTokenIterator() - while iterator.next() - tokenScreenStart = iterator.getScreenStart() - tokenScreenEnd = iterator.getScreenEnd() - if tokenScreenStart <= targetScreenColumn < tokenScreenEnd - overshoot = targetScreenColumn - tokenScreenStart - return Math.min( - iterator.getBufferStart() + overshoot, - iterator.getBufferEnd() - ) - iterator.getBufferEnd() - - getMaxScreenColumn: -> - if @fold - 0 - else - @text.length - - getMaxBufferColumn: -> - @startBufferColumn + @bufferDelta - tokenAtBufferColumn: (bufferColumn) -> @tokens[@tokenIndexAtBufferColumn(bufferColumn)] @@ -171,17 +90,6 @@ class TokenizedLine delta = nextDelta delta - buildEndOfLineInvisibles: -> - @endOfLineInvisibles = [] - {cr, eol} = @invisibles - - switch @lineEnding - when '\r\n' - @endOfLineInvisibles.push(cr) if cr - @endOfLineInvisibles.push(eol) if eol - when '\n' - @endOfLineInvisibles.push(eol) if eol - isComment: -> return @isCommentLine if @isCommentLine? From 5aba734a412e484d573f52daa4f7dc187a489dc3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 18 Mar 2016 19:19:38 -0600 Subject: [PATCH 536/971] Remove specialTokens object from TokenizedLine --- src/token-iterator.coffee | 38 ++------------------------------------ src/tokenized-line.coffee | 16 ---------------- 2 files changed, 2 insertions(+), 52 deletions(-) diff --git a/src/token-iterator.coffee b/src/token-iterator.coffee index 8f0fe202f..259bfd346 100644 --- a/src/token-iterator.coffee +++ b/src/token-iterator.coffee @@ -1,6 +1,3 @@ -{SoftTab, HardTab, PairedCharacter, SoftWrapIndent} = require './special-token-symbols' -{isDoubleWidthCharacter, isHalfWidthCharacter, isKoreanCharacter} = require './text-utils' - module.exports = class TokenIterator constructor: ({@grammarRegistry}, line, enableScopes) -> @@ -32,15 +29,8 @@ class TokenIterator @handleScopeForTag(tag) if @enableScopes @index++ else - if @isHardTab() - @screenEnd = @screenStart + tag - @bufferEnd = @bufferStart + 1 - else if @isSoftWrapIndentation() - @screenEnd = @screenStart + tag - @bufferEnd = @bufferStart + 0 - else - @screenEnd = @screenStart + tag - @bufferEnd = @bufferStart + tag + @screenEnd = @screenStart + tag + @bufferEnd = @bufferStart + tag @text = @line.text.substring(@screenStart, @screenEnd) return true @@ -80,27 +70,3 @@ class TokenIterator getScopes: -> @scopes getText: -> @text - - isSoftTab: -> - @line.specialTokens[@index] is SoftTab - - isHardTab: -> - @line.specialTokens[@index] is HardTab - - isSoftWrapIndentation: -> - @line.specialTokens[@index] is SoftWrapIndent - - isPairedCharacter: -> - @line.specialTokens[@index] is PairedCharacter - - hasDoubleWidthCharacterAt: (charIndex) -> - isDoubleWidthCharacter(@getText()[charIndex]) - - hasHalfWidthCharacterAt: (charIndex) -> - isHalfWidthCharacter(@getText()[charIndex]) - - hasKoreanCharacterAt: (charIndex) -> - isKoreanCharacter(@getText()[charIndex]) - - isAtomic: -> - @isSoftTab() or @isHardTab() or @isSoftWrapIndentation() or @isPairedCharacter() diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index 62dadc836..39a551586 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -1,22 +1,7 @@ _ = require 'underscore-plus' {isPairedCharacter, isCJKCharacter} = require './text-utils' Token = require './token' -{SoftTab, HardTab, PairedCharacter, SoftWrapIndent} = require './special-token-symbols' - -NonWhitespaceRegex = /\S/ -LeadingWhitespaceRegex = /^\s*/ -TrailingWhitespaceRegex = /\s*$/ -RepeatedSpaceRegex = /[ ]/g CommentScopeRegex = /(\b|\.)comment/ -TabCharCode = 9 -SpaceCharCode = 32 -SpaceString = ' ' -TabStringsByLength = { - 1: ' ' - 2: ' ' - 3: ' ' - 4: ' ' -} idCounter = 1 @@ -31,7 +16,6 @@ class TokenizedLine return unless properties? - @specialTokens = {} {@openScopes, @text, @tags, @lineEnding, @ruleStack, @tokenIterator} = properties {@startBufferColumn, @fold, @tabLength, @invisibles} = properties From 5c03894227dca96e6a630acb07da6c9596f910c9 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sun, 20 Mar 2016 22:22:33 -0700 Subject: [PATCH 537/971] Revert "Allow pasting white space when `autoIndentOnPaste` is enabled" This reverts commit 0088053de47b5ec0566eedcd24fd7393e7ddc1a6. --- src/selection.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/selection.coffee b/src/selection.coffee index e208ea55a..b89772f57 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -378,7 +378,7 @@ class Selection extends Model indentAdjustment = @editor.indentLevelForLine(precedingText) - options.indentBasis @adjustIndent(remainingLines, indentAdjustment) - if options.autoIndent and NonWhitespaceRegExp.test(text) and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0 + if options.autoIndent and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0 autoIndentFirstLine = true firstLine = precedingText + firstInsertedLine desiredIndentLevel = @editor.languageMode.suggestedIndentForLineAtBufferRow(oldBufferRange.start.row, firstLine) From 8f2ebe8b799324a54e2e193e6efa36ef4091b4b3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 21 Mar 2016 17:46:58 +0100 Subject: [PATCH 538/971] :fire: --- src/tokenized-line.coffee | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index 39a551586..cbf64633b 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -29,30 +29,10 @@ class TokenizedLine tokens = [] while iterator.next() - properties = { + tokens.push(new Token({ value: iterator.getText() scopes: iterator.getScopes().slice() - isAtomic: iterator.isAtomic() - isHardTab: iterator.isHardTab() - hasPairedCharacter: iterator.isPairedCharacter() - isSoftWrapIndentation: iterator.isSoftWrapIndentation() - } - - if iterator.isHardTab() - properties.bufferDelta = 1 - properties.hasInvisibleCharacters = true if @invisibles?.tab - - if iterator.getScreenStart() < @firstNonWhitespaceIndex - properties.firstNonWhitespaceIndex = - Math.min(@firstNonWhitespaceIndex, iterator.getScreenEnd()) - iterator.getScreenStart() - properties.hasInvisibleCharacters = true if @invisibles?.space - - if @lineEnding? and iterator.getScreenEnd() > @firstTrailingWhitespaceIndex - properties.firstTrailingWhitespaceIndex = - Math.max(0, @firstTrailingWhitespaceIndex - iterator.getScreenStart()) - properties.hasInvisibleCharacters = true if @invisibles?.space - - tokens.push(new Token(properties)) + })) tokens From 32a1d21a0debe14f159b3cbf945827d4fb7bdf30 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 21 Mar 2016 14:03:25 -0700 Subject: [PATCH 539/971] :arrow_up: apm --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index 2f0fcf519..8ab76e484 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "1.8.0" + "atom-package-manager": "1.9.0" } } From 439825ea37b3e420328cd8d61c15014865a1ad5a Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 21 Mar 2016 14:32:45 -0700 Subject: [PATCH 540/971] :arrow_up: settings-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1c0d8f7ba..b6752d56e 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "notifications": "0.63.1", "open-on-github": "1.0.1", "package-generator": "1.0.0", - "settings-view": "0.235.0", + "settings-view": "0.235.1", "snippets": "1.0.1", "spell-check": "0.67.0", "status-bar": "1.2.0", From c5ed25a1315c4b66593415077ef84248ecf8a2a4 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 21 Mar 2016 15:32:42 -0700 Subject: [PATCH 541/971] :arrow_up: apm --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index 8ab76e484..ba3415e1d 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "1.9.0" + "atom-package-manager": "1.9.1" } } From fd17457c172838c0a919ff74944ba68ab9770391 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 Mar 2016 17:15:27 -0600 Subject: [PATCH 542/971] Revert "Add spec for inserting white-space-only lines" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 746afb98ade993c5598ab8e80413825accbcaf82. @BinaryMuse Heads up this is the test associated with the other commit you reverted. Had to revert it as well to get the build green. Didn’t see a revert on beta so I’m assuming this is good enough? --- spec/selection-spec.coffee | 8 -------- 1 file changed, 8 deletions(-) diff --git a/spec/selection-spec.coffee b/spec/selection-spec.coffee index 319e2d438..ec40e32cc 100644 --- a/spec/selection-spec.coffee +++ b/spec/selection-spec.coffee @@ -83,11 +83,3 @@ describe "Selection", -> selection.setBufferRange([[2, 0], [2, 10]]) selection.destroy() expect(selection.marker.isDestroyed()).toBeTruthy() - - describe ".insertText(text, options)", -> - it "allows pasting white space only lines when autoIndent is enabled", -> - selection.setBufferRange [[0, 0], [0, 0]] - selection.insertText(" \n \n\n", autoIndent: true) - expect(buffer.lineForRow(0)).toBe " " - expect(buffer.lineForRow(1)).toBe " " - expect(buffer.lineForRow(2)).toBe "" From 449abd73646af35e7ef4830450963ca1c2b7fb38 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 21 Mar 2016 16:27:41 -0700 Subject: [PATCH 543/971] Revert "Revert "Add spec for inserting white-space-only lines"" This reverts commit fd17457c172838c0a919ff74944ba68ab9770391. --- spec/selection-spec.coffee | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/selection-spec.coffee b/spec/selection-spec.coffee index ec40e32cc..319e2d438 100644 --- a/spec/selection-spec.coffee +++ b/spec/selection-spec.coffee @@ -83,3 +83,11 @@ describe "Selection", -> selection.setBufferRange([[2, 0], [2, 10]]) selection.destroy() expect(selection.marker.isDestroyed()).toBeTruthy() + + describe ".insertText(text, options)", -> + it "allows pasting white space only lines when autoIndent is enabled", -> + selection.setBufferRange [[0, 0], [0, 0]] + selection.insertText(" \n \n\n", autoIndent: true) + expect(buffer.lineForRow(0)).toBe " " + expect(buffer.lineForRow(1)).toBe " " + expect(buffer.lineForRow(2)).toBe "" From 64308bbacb5f253562a059cfce6853859a859cd1 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 21 Mar 2016 16:27:48 -0700 Subject: [PATCH 544/971] Revert "Revert "Allow pasting white space when `autoIndentOnPaste` is enabled"" This reverts commit 5c03894227dca96e6a630acb07da6c9596f910c9. --- src/selection.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/selection.coffee b/src/selection.coffee index b89772f57..e208ea55a 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -378,7 +378,7 @@ class Selection extends Model indentAdjustment = @editor.indentLevelForLine(precedingText) - options.indentBasis @adjustIndent(remainingLines, indentAdjustment) - if options.autoIndent and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0 + if options.autoIndent and NonWhitespaceRegExp.test(text) and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0 autoIndentFirstLine = true firstLine = precedingText + firstInsertedLine desiredIndentLevel = @editor.languageMode.suggestedIndentForLineAtBufferRow(oldBufferRange.start.row, firstLine) From 34f9ad8710293505a74d3ea6d8c7431ad480c102 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 22 Mar 2016 11:20:55 +0100 Subject: [PATCH 545/971] Add top/bottom ruler before/after a block decoration --- spec/text-editor-component-spec.js | 11 +++++++--- src/block-decorations-component.coffee | 29 ++++++++++++++++++-------- src/lines-tile-component.coffee | 4 ++-- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 37a9751e1..1d1e4eb9f 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -1840,17 +1840,22 @@ describe('TextEditorComponent', function () { expect(component.lineNodeForScreenRow(2).dataset.screenRow).toBe("2") }) - it('measures block decorations taking into account both top and bottom margins', async function () { + it('measures block decorations taking into account both top and bottom margins of the element and its children', async function () { let [item, blockDecoration] = createBlockDecorationBeforeScreenRow(0, {className: "decoration-1"}) + let child = document.createElement("div") + child.style.height = "7px" + child.style.width = "30px" + child.style.marginBottom = "20px" + item.appendChild(child) atom.styles.addStyleSheet( - 'atom-text-editor .decoration-1 { width: 30px; height: 30px; margin-top: 10px; margin-bottom: 5px; }', + 'atom-text-editor .decoration-1 { width: 30px; margin-top: 10px; }', {context: 'atom-text-editor'} ) await nextAnimationFramePromise() // causes the DOM to update and to retrieve new styles await nextAnimationFramePromise() // applies the changes - expect(component.tileNodesForLines()[0].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 30 + 10 + 5 + "px") + expect(component.tileNodesForLines()[0].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + 10 + 7 + 20 + "px") expect(component.tileNodesForLines()[0].style.webkitTransform).toBe("translate3d(0px, 0px, 0px)") expect(component.tileNodesForLines()[1].style.height).toBe(TILE_SIZE * editor.getLineHeightInPixels() + "px") expect(component.tileNodesForLines()[1].style.webkitTransform).toBe(`translate3d(0px, ${component.tileNodesForLines()[0].offsetHeight}px, 0px)`) diff --git a/src/block-decorations-component.coffee b/src/block-decorations-component.coffee index 0cfa7974f..6e0a2091c 100644 --- a/src/block-decorations-component.coffee +++ b/src/block-decorations-component.coffee @@ -26,7 +26,10 @@ class BlockDecorationsComponent for id, blockDecorationState of @oldState.blockDecorations unless @newState.blockDecorations.hasOwnProperty(id) - @blockDecorationNodesById[id].remove() + blockDecorationNode = @blockDecorationNodesById[id] + blockDecorationNode.previousSibling.remove() + blockDecorationNode.nextSibling.remove() + blockDecorationNode.remove() delete @blockDecorationNodesById[id] delete @oldState.blockDecorations[id] @@ -41,19 +44,27 @@ class BlockDecorationsComponent for decorationId, blockDecorationNode of @blockDecorationNodesById style = getComputedStyle(blockDecorationNode) decoration = @newState.blockDecorations[decorationId].decoration - marginBottom = parseInt(style.marginBottom) ? 0 - marginTop = parseInt(style.marginTop) ? 0 - @presenter.setBlockDecorationDimensions( - decoration, - blockDecorationNode.offsetWidth, - blockDecorationNode.offsetHeight + marginTop + marginBottom - ) + topRuler = blockDecorationNode.previousSibling + bottomRuler = blockDecorationNode.nextSibling + + width = blockDecorationNode.offsetWidth + height = bottomRuler.offsetTop - topRuler.offsetTop + @presenter.setBlockDecorationDimensions(decoration, width, height) createAndAppendBlockDecorationNode: (id) -> blockDecorationState = @newState.blockDecorations[id] + blockDecorationClass = "atom--block-decoration-#{id}" + topRuler = document.createElement("div") blockDecorationNode = @views.getView(blockDecorationState.decoration.getProperties().item) - blockDecorationNode.id = "atom--block-decoration-#{id}" + bottomRuler = document.createElement("div") + topRuler.classList.add(blockDecorationClass) + blockDecorationNode.classList.add(blockDecorationClass) + bottomRuler.classList.add(blockDecorationClass) + + @container.appendChild(topRuler) @container.appendChild(blockDecorationNode) + @container.appendChild(bottomRuler) + @blockDecorationNodesById[id] = blockDecorationNode @updateBlockDecorationNode(id) diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index defcc0d8a..f4a7313ca 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -149,7 +149,7 @@ class LinesTileComponent if newLineState.screenRow isnt oldLineState.screenRow insertionPoint.dataset.screenRow = newLineState.screenRow - precedingBlockDecorationsSelector = newLineState.precedingBlockDecorations.map((d) -> "#atom--block-decoration-#{d.id}").join(',') + precedingBlockDecorationsSelector = newLineState.precedingBlockDecorations.map((d) -> ".atom--block-decoration-#{d.id}").join(',') if precedingBlockDecorationsSelector isnt oldLineState.precedingBlockDecorationsSelector insertionPoint.setAttribute("select", precedingBlockDecorationsSelector) @@ -180,7 +180,7 @@ class LinesTileComponent if newLineState.screenRow isnt oldLineState.screenRow insertionPoint.dataset.screenRow = newLineState.screenRow - followingBlockDecorationsSelector = newLineState.followingBlockDecorations.map((d) -> "#atom--block-decoration-#{d.id}").join(',') + followingBlockDecorationsSelector = newLineState.followingBlockDecorations.map((d) -> ".atom--block-decoration-#{d.id}").join(',') if followingBlockDecorationsSelector isnt oldLineState.followingBlockDecorationsSelector insertionPoint.setAttribute("select", followingBlockDecorationsSelector) From cf2233445586dad1d090f238345ed288f0a177cc Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 22 Mar 2016 11:50:54 +0100 Subject: [PATCH 546/971] Add invisible class to invisible block decorations --- src/block-decorations-component.coffee | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/block-decorations-component.coffee b/src/block-decorations-component.coffee index 6e0a2091c..c63fbdd2b 100644 --- a/src/block-decorations-component.coffee +++ b/src/block-decorations-component.coffee @@ -69,15 +69,13 @@ class BlockDecorationsComponent @updateBlockDecorationNode(id) updateBlockDecorationNode: (id) -> - newBlockDecorationState = @newState.blockDecorations[id] - oldBlockDecorationState = @oldState.blockDecorations[id] blockDecorationNode = @blockDecorationNodesById[id] - if newBlockDecorationState.isVisible + if @newState.blockDecorations[id].isVisible + blockDecorationNode.previousSibling.classList.remove("atom--invisible-block-decoration") blockDecorationNode.classList.remove("atom--invisible-block-decoration") + blockDecorationNode.nextSibling.classList.remove("atom--invisible-block-decoration") else + blockDecorationNode.previousSibling.classList.add("atom--invisible-block-decoration") blockDecorationNode.classList.add("atom--invisible-block-decoration") - - if oldBlockDecorationState.screenRow isnt newBlockDecorationState.screenRow - blockDecorationNode.dataset.screenRow = newBlockDecorationState.screenRow - oldBlockDecorationState.screenRow = newBlockDecorationState.screenRow + blockDecorationNode.nextSibling.classList.add("atom--invisible-block-decoration") From 4e237486601c3afddac2883834b87911c879554a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 22 Mar 2016 14:02:13 +0100 Subject: [PATCH 547/971] Fix `moveLineUp` and `moveLineDown` to work with free-form folds --- src/text-editor.coffee | 99 ++++++++++++------------------------------ 1 file changed, 28 insertions(+), 71 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 578529ade..f9ebb80e3 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -903,8 +903,7 @@ class TextEditor extends Model # Move lines intersecting the most recent selection or multiple selections # up by one row in screen coordinates. moveLineUp: -> - selections = @getSelectedBufferRanges() - selections.sort (a, b) -> a.compare(b) + selections = @getSelectedBufferRanges().sort((a, b) -> a.compare(b)) if selections[0].start.row is 0 return @@ -925,56 +924,34 @@ class TextEditor extends Model selection.end.row = selections[0].end.row selections.shift() - # Compute the range spanned by all these selections... - linesRangeStart = [selection.start.row, 0] + # Compute the buffer range spanned by all these selections, expanding it + # so that it includes any folded region that intersects them. + startRow = selection.start.row + endRow = selection.end.row if selection.end.row > selection.start.row and selection.end.column is 0 # Don't move the last line of a multi-line selection if the selection ends at column 0 - linesRange = new Range(linesRangeStart, selection.end) - else - linesRange = new Range(linesRangeStart, [selection.end.row + 1, 0]) + endRow-- - # If there's a fold containing either the starting row or the end row - # of the selection then the whole fold needs to be moved and restored. - # The initial fold range is stored and will be translated once the - # insert delta is know. - selectionFoldRanges = [] - foldAtSelectionStart = - @displayLayer.largestFoldContainingBufferRow(selection.start.row) - foldAtSelectionEnd = - @displayLayer.largestFoldContainingBufferRow(selection.end.row) - if fold = foldAtSelectionStart ? foldAtSelectionEnd - selectionFoldRanges.push range = @displayLayer.bufferRangeForFold(fold) - newEndRow = range.end.row + 1 - linesRange.end.row = newEndRow if newEndRow > linesRange.end.row - @displayLayer.destroyFold(fold) + {bufferRow: startRow} = @displayLayer.lineStartBoundaryForBufferRow(startRow) + {bufferRow: endRow} = @displayLayer.lineEndBoundaryForBufferRow(endRow) + linesRange = new Range(Point(startRow, 0), Point(endRow, 0)) # If selected line range is preceded by a fold, one line above on screen # could be multiple lines in the buffer. - precedingScreenRow = @screenRowForBufferRow(linesRange.start.row) - 1 - precedingBufferRow = @bufferRowForScreenRow(precedingScreenRow) - insertDelta = linesRange.start.row - precedingBufferRow + {bufferRow: precedingRow} = @displayLayer.lineStartBoundaryForBufferRow(startRow - 1) + insertDelta = linesRange.start.row - precedingRow # Any folds in the text that is moved will need to be re-created. # It includes the folds that were intersecting with the selection. - rangesToRefold = selectionFoldRanges.concat( - @displayLayer.outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> - range = @displayLayer.bufferRangeForFold(fold) - @displayLayer.destroyFold(fold) - range - ).map (range) -> range.translate([-insertDelta, 0]) - - # Make sure the inserted text doesn't go into an existing fold - if fold = @displayLayer.largestFoldStartingAtBufferRow(precedingBufferRow) - rangesToRefold.push( - @displayLayer.bufferRangeForFold(fold).translate([linesRange.getRowCount() - 1, 0]) - ) - @displayLayer.destroyFold(fold) + rangesToRefold = @displayLayer + .destroyFoldsIntersectingBufferRange(linesRange) + .map((range) -> range.translate([-insertDelta, 0])) # Delete lines spanned by selection and insert them on the preceding buffer row lines = @buffer.getTextInRange(linesRange) lines += @buffer.lineEndingForRow(linesRange.end.row - 1) unless lines[lines.length - 1] is '\n' @buffer.delete(linesRange) - @buffer.insert([precedingBufferRow, 0], lines) + @buffer.insert([precedingRow, 0], lines) # Restore folds that existed before the lines were moved for rangeToRefold in rangesToRefold @@ -1009,50 +986,30 @@ class TextEditor extends Model selection.start.row = selections[0].start.row selections.shift() - # Compute the range spanned by all these selections... - linesRangeStart = [selection.start.row, 0] + # Compute the buffer range spanned by all these selections, expanding it + # so that it includes any folded region that intersects them. + startRow = selection.start.row + endRow = selection.end.row if selection.end.row > selection.start.row and selection.end.column is 0 # Don't move the last line of a multi-line selection if the selection ends at column 0 - linesRange = new Range(linesRangeStart, selection.end) - else - linesRange = new Range(linesRangeStart, [selection.end.row + 1, 0]) + endRow-- - # If there's a fold containing either the starting row or the end row - # of the selection then the whole fold needs to be moved and restored. - # The initial fold range is stored and will be translated once the - # insert delta is know. - selectionFoldRanges = [] - foldAtSelectionStart = - @displayLayer.largestFoldContainingBufferRow(selection.start.row) - foldAtSelectionEnd = - @displayLayer.largestFoldContainingBufferRow(selection.end.row) - if fold = foldAtSelectionStart ? foldAtSelectionEnd - selectionFoldRanges.push range = @displayLayer.bufferRangeForFold(fold) - newEndRow = range.end.row + 1 - linesRange.end.row = newEndRow if newEndRow > linesRange.end.row - @displayLayer.destroyFold(fold) + {bufferRow: startRow} = @displayLayer.lineStartBoundaryForBufferRow(startRow) + {bufferRow: endRow} = @displayLayer.lineEndBoundaryForBufferRow(endRow) + linesRange = new Range(Point(startRow, 0), Point(endRow, 0)) # If selected line range is followed by a fold, one line below on screen # could be multiple lines in the buffer. But at the same time, if the # next buffer row is wrapped, one line in the buffer can represent many # screen rows. - followingScreenRow = @displayBuffer.lastScreenRowForBufferRow(linesRange.end.row) + 1 - followingBufferRow = @bufferRowForScreenRow(followingScreenRow) - insertDelta = followingBufferRow - linesRange.end.row + {bufferRow: followingRow} = @displayLayer.lineEndBoundaryForBufferRow(endRow) + insertDelta = followingRow - linesRange.end.row # Any folds in the text that is moved will need to be re-created. # It includes the folds that were intersecting with the selection. - rangesToRefold = selectionFoldRanges.concat( - @displayLayer.outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> - range = fold.getBufferRange() - @displayLayer.destroyFold(fold) - range - ).map (range) -> range.translate([insertDelta, 0]) - - # Make sure the inserted text doesn't go into an existing fold - if fold = @displayLayer.largestFoldStartingAtBufferRow(followingBufferRow) - rangesToRefold.push(@displayLayer.bufferRangeForFold(fold).translate([insertDelta - 1, 0])) - @displayLayer.destroyFold(fold) + rangesToRefold = @displayLayer + .destroyFoldsIntersectingBufferRange(linesRange) + .map((range) -> range.translate([insertDelta, 0])) # Delete lines spanned by selection and insert them on the following correct buffer row insertPosition = new Point(selection.translate([insertDelta, 0]).start.row, 0) From 137af3879807a9bcf72e3677be0af1044ec90bb2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 22 Mar 2016 14:08:25 +0100 Subject: [PATCH 548/971] Add back screen row to block decorations nodes --- src/block-decorations-component.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/block-decorations-component.coffee b/src/block-decorations-component.coffee index c63fbdd2b..35aec3921 100644 --- a/src/block-decorations-component.coffee +++ b/src/block-decorations-component.coffee @@ -69,9 +69,11 @@ class BlockDecorationsComponent @updateBlockDecorationNode(id) updateBlockDecorationNode: (id) -> + newBlockDecorationState = @newState.blockDecorations[id] + oldBlockDecorationState = @oldState.blockDecorations[id] blockDecorationNode = @blockDecorationNodesById[id] - if @newState.blockDecorations[id].isVisible + if newBlockDecorationState.isVisible blockDecorationNode.previousSibling.classList.remove("atom--invisible-block-decoration") blockDecorationNode.classList.remove("atom--invisible-block-decoration") blockDecorationNode.nextSibling.classList.remove("atom--invisible-block-decoration") @@ -79,3 +81,7 @@ class BlockDecorationsComponent blockDecorationNode.previousSibling.classList.add("atom--invisible-block-decoration") blockDecorationNode.classList.add("atom--invisible-block-decoration") blockDecorationNode.nextSibling.classList.add("atom--invisible-block-decoration") + + if oldBlockDecorationState.screenRow isnt newBlockDecorationState.screenRow + blockDecorationNode.dataset.screenRow = newBlockDecorationState.screenRow + oldBlockDecorationState.screenRow = newBlockDecorationState.screenRow From 27799d30f624590245556f04e4720ac80ada48f0 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 22 Mar 2016 14:27:23 +0100 Subject: [PATCH 549/971] Use deserialized display layer when possible --- src/display-buffer.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index eb4faa1a5..e025ce03d 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -52,7 +52,7 @@ class DisplayBuffer extends Model @grammarRegistry, @packageManager, @assert }) @buffer = @tokenizedBuffer.buffer - @displayLayer = @buffer.addDisplayLayer() + @displayLayer = @buffer.getDisplayLayer() ? @buffer.addDisplayLayer() @displayLayer.setTextDecorationLayer(@tokenizedBuffer) @charWidthsByScope = {} @defaultMarkerLayer = @displayLayer.addMarkerLayer() From b1e07c0cfe0a38e2a7af499d0037db0cf1142822 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 22 Mar 2016 14:36:28 +0100 Subject: [PATCH 550/971] Use the new `lineText` property --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index f9ebb80e3..b06ab5194 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -776,7 +776,7 @@ class TextEditor extends Model # # * `screenRow` A {Number} representing a zero-indexed screen row. lineTextForScreenRow: (screenRow) -> - @screenLineForScreenRow(screenRow)?.tokens.map((t) -> t.text).join('') + @screenLineForScreenRow(screenRow)?.lineText screenLineForScreenRow: (screenRow) -> @displayLayer.getScreenLines(screenRow, screenRow + 1)[0] From 326f2c6a9e2cd3b2ccf39eeda4b3d9a13085daf8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 22 Mar 2016 14:49:29 +0100 Subject: [PATCH 551/971] Add `TextEditor.prototype.tokensForScreenRow` for testing purposes --- spec/text-editor-spec.coffee | 25 +++++++++++-------------- src/text-editor.coffee | 4 ++++ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index d4f28d9cc..1526dfe3b 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -39,21 +39,19 @@ describe "TextEditor", -> it "preserves the invisibles setting", -> atom.config.set('editor.showInvisibles', true) - previousInvisibles = editor.tokenizedLineForScreenRow(0).invisibles - + previousLineText = editor.lineTextForScreenRow(0) editor2 = TextEditor.deserialize(editor.serialize(), atom) - - expect(previousInvisibles).toBeDefined() - expect(editor2.displayBuffer.tokenizedLineForScreenRow(0).invisibles).toEqual previousInvisibles + expect(editor2.lineTextForScreenRow(0)).toBe(previousLineText) it "updates invisibles if the settings have changed between serialization and deserialization", -> atom.config.set('editor.showInvisibles', true) - + previousLineText = editor.lineTextForScreenRow(0) state = editor.serialize() atom.config.set('editor.invisibles', eol: '?') editor2 = TextEditor.deserialize(state, atom) - expect(editor.tokenizedLineForScreenRow(0).invisibles.eol).toBe '?' + expect(editor2.lineTextForScreenRow(0)).not.toBe(previousLineText) + expect(editor2.lineTextForScreenRow(0).endsWith('?')).toBe(true) describe "when the editor is constructed with the largeFileMode option set to true", -> it "loads the editor but doesn't tokenize", -> @@ -64,15 +62,14 @@ describe "TextEditor", -> runs -> buffer = editor.getBuffer() - expect(editor.tokenizedLineForScreenRow(0).text).toBe buffer.lineForRow(0) - expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBe 1 - expect(editor.tokenizedLineForScreenRow(1).tokens.length).toBe 2 # soft tab - expect(editor.tokenizedLineForScreenRow(12).text).toBe buffer.lineForRow(12) - expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBe 1 + expect(editor.lineTextForScreenRow(0)).toBe buffer.lineForRow(0) + expect(editor.tokensForScreenRow(0).length).toBe 1 + expect(editor.tokensForScreenRow(1).length).toBe 2 # soft tab + expect(editor.lineTextForScreenRow(12)).toBe buffer.lineForRow(12) expect(editor.getCursorScreenPosition()).toEqual [0, 0] editor.insertText('hey"') - expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBe 1 - expect(editor.tokenizedLineForScreenRow(1).tokens.length).toBe 2 # sof tab + expect(editor.tokensForScreenRow(0).length).toBe 1 + expect(editor.tokensForScreenRow(1).length).toBe 2 # soft tab describe ".copy()", -> it "returns a different edit session with the same initial state", -> diff --git a/src/text-editor.coffee b/src/text-editor.coffee index b06ab5194..1f9893132 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -778,6 +778,10 @@ class TextEditor extends Model lineTextForScreenRow: (screenRow) -> @screenLineForScreenRow(screenRow)?.lineText + tokensForScreenRow: (screenRow) -> + for tagCode in @screenLineForScreenRow(screenRow).tagCodes when @displayLayer.isOpenTagCode(tagCode) + @displayLayer.tagForCode(tagCode) + screenLineForScreenRow: (screenRow) -> @displayLayer.getScreenLines(screenRow, screenRow + 1)[0] From 9abc547bfb7df52c0d967caa95ec85ce017b4d77 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 22 Mar 2016 14:56:22 +0100 Subject: [PATCH 552/971] Copy also DisplayLayer --- src/display-buffer.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index e025ce03d..b10ffb0c4 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -52,7 +52,7 @@ class DisplayBuffer extends Model @grammarRegistry, @packageManager, @assert }) @buffer = @tokenizedBuffer.buffer - @displayLayer = @buffer.getDisplayLayer() ? @buffer.addDisplayLayer() + @displayLayer ?= @buffer.getDisplayLayer() ? @buffer.addDisplayLayer() @displayLayer.setTextDecorationLayer(@tokenizedBuffer) @charWidthsByScope = {} @defaultMarkerLayer = @displayLayer.addMarkerLayer() @@ -96,7 +96,7 @@ class DisplayBuffer extends Model copy: -> new DisplayBuffer({ @buffer, tabLength: @getTabLength(), @largeFileMode, @config, @assert, - @grammarRegistry, @packageManager + @grammarRegistry, @packageManager, displayLayer: @displayLayer.copy() }) resetDisplayLayer: -> From 756db7588b706996c71f61bbfb8d969c7b21798b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 22 Mar 2016 14:59:26 +0100 Subject: [PATCH 553/971] Return an invalidated range only when TokenizedBuffer has one --- src/tokenized-buffer.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 7a943d78c..470aa59b9 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -63,7 +63,10 @@ class TokenizedBuffer extends Model new TokenizedBufferIterator(this, @grammarRegistry) getInvalidatedRanges: -> - [@invalidatedRange] + if @invalidatedRange? + [@invalidatedRange] + else + [] onDidInvalidateRange: (fn) -> @emitter.on 'did-invalidate-range', fn From 3d9835bbbd6b78f8d88fe4244ac810216d8ef508 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 22 Mar 2016 16:05:38 +0100 Subject: [PATCH 554/971] :green_heart: WIP: Continue fixing TextEditor specs --- spec/text-editor-spec.coffee | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 1526dfe3b..cce47b926 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -490,7 +490,7 @@ describe "TextEditor", -> it "wraps to the end of the previous line", -> editor.setCursorScreenPosition([4, 4]) editor.moveLeft() - expect(editor.getCursorScreenPosition()).toEqual [3, 50] + expect(editor.getCursorScreenPosition()).toEqual [3, 46] describe "when the cursor is on the first line", -> it "remains in the same position (0,0)", -> @@ -678,7 +678,7 @@ describe "TextEditor", -> editor.setCursorScreenPosition([0, 2]) editor.moveToEndOfLine() cursor = editor.getLastCursor() - expect(cursor.getScreenPosition()).toEqual [3, 4] + expect(cursor.getScreenPosition()).toEqual [4, 4] describe ".moveToFirstCharacterOfLine()", -> describe "when soft wrap is on", -> @@ -1803,10 +1803,10 @@ describe "TextEditor", -> editor.foldBufferRowRange(10, 11) editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[6, 6], [7, 7]]]) - expect(editor.tokenizedLineForScreenRow(1).fold).toBeUndefined() - expect(editor.tokenizedLineForScreenRow(2).fold).toBeUndefined() - expect(editor.tokenizedLineForScreenRow(6).fold).toBeUndefined() - expect(editor.tokenizedLineForScreenRow(10).fold).toBeDefined() + expect(editor.isFoldedAtScreenRow(1)).toBeFalsy() + expect(editor.isFoldedAtScreenRow(2)).toBeFalsy() + expect(editor.isFoldedAtScreenRow(6)).toBeFalsy() + expect(editor.isFoldedAtScreenRow(10)).toBeTruthy() describe "when the 'preserveFolds' option is true", -> it "does not remove folds that contain the selections", -> @@ -2951,7 +2951,7 @@ describe "TextEditor", -> editor.foldBufferRowRange(2, 4) editor.setSelectedBufferRange([[1, 0], [2, 0]]) editor.insertText('holy cow') - expect(editor.tokenizedLineForScreenRow(2).fold).toBeUndefined() + expect(editor.isFoldedAtScreenRow(2)).toBeFalsy() describe "when there are ::onWillInsertText and ::onDidInsertText observers", -> beforeEach -> From d6933af487993250020024c652aebdd923154a21 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 22 Mar 2016 14:38:04 -0600 Subject: [PATCH 555/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b6752d56e..4b0d92b89 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.4.1", + "text-buffer": "8.4.2", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From dbf7214670b9f625a718f8f5a71b8ffff63e22a5 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 22 Mar 2016 17:26:30 -0700 Subject: [PATCH 556/971] Fix clean command to actually work when paths missing --- script/clean | 50 +++++++++++++++++++++----------------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/script/clean b/script/clean index 0c947baf2..cc4933f95 100755 --- a/script/clean +++ b/script/clean @@ -1,11 +1,10 @@ #!/usr/bin/env node -var cp = require('./utils/child-process-wrapper.js'); +var childProcess = require('./utils/child-process-wrapper.js'); var fs = require('fs'); var path = require('path'); var os = require('os'); var isWindows = process.platform === 'win32'; -var removeCommand = isWindows ? 'rmdir /S /Q ' : 'rm -rf '; var productName = require('../package.json').productName; process.chdir(path.dirname(__dirname)); @@ -13,10 +12,10 @@ var home = process.env[isWindows ? 'USERPROFILE' : 'HOME']; var tmpdir = os.tmpdir(); // Windows: Use START as a way to ignore error if Atom.exe isnt running -var killatom = isWindows ? 'START taskkill /F /IM ' + productName + '.exe' : 'pkill -9 ' + productName + ' || true'; +var killAtomCommand = isWindows ? 'START taskkill /F /IM ' + productName + '.exe' : 'pkill -9 ' + productName + ' || true'; +//childProcess.safeExec(killAtomCommand); -var commands = [ - killatom, +var pathsToRemove = [ [__dirname, '..', 'node_modules'], [__dirname, '..', 'build', 'node_modules'], [__dirname, '..', 'apm', 'node_modules'], @@ -32,37 +31,30 @@ var commands = [ [home, '.atom', 'electron'], [tmpdir, 'atom-build'], [tmpdir, 'atom-cached-atom-shells'], -]; -var run = function() { - var next = commands.shift(); - if (!next) - process.exit(0); +].map(function(pathSegments) { + return path.resolve.apply(null, pathSegments); +}); - if (Array.isArray(next)) { - var pathToRemove = path.resolve.apply(path.resolve, next); - if (fs.existsSync(pathToRemove)) { - if (isWindows) { - removeFolderRecursive(pathToRemove); - } else { - next = removeCommand + pathToRemove; - cp.safeExec(next, run); - } - } - else { - return run(); - } +pathsToRemove.forEach(function(pathToRemove) { + if (fs.existsSync(pathToRemove)) { + removePath(pathToRemove); } - else - cp.safeExec(next, run); -}; -run(); +}); + +function removePath(pathToRemove) { + if (isWindows) { + removePathOnWindows(pathToRemove); + } else { + childProcess.safeExec('rm -rf ' + pathToRemove); + } +} // Windows has a 260-char path limit for rmdir etc. Just recursively delete in Node. -var removeFolderRecursive = function(folderPath) { +function removePathOnWindows(folderPath) { fs.readdirSync(folderPath).forEach(function(entry, index) { var entryPath = path.join(folderPath, entry); if (fs.lstatSync(entryPath).isDirectory()) { - removeFolderRecursive(entryPath); + removePathOnWindows(entryPath); } else { fs.unlinkSync(entryPath); } From de6ab3b814cd305523f0ae95314fc2bc19e28f51 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 22 Mar 2016 17:26:41 -0700 Subject: [PATCH 557/971] Exclude PATH entries with msbuild.exe to fix node-gyp on Windows --- script/build | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/script/build b/script/build index a40a02e13..ca5569d0f 100755 --- a/script/build +++ b/script/build @@ -2,9 +2,24 @@ var cp = require('./utils/child-process-wrapper.js'); var runGrunt = require('./utils/run-grunt.js'); var path = require('path'); +var fs = require('fs'); process.chdir(path.dirname(__dirname)); +if (process.platform === 'win32') { + process.env['PATH'] = process.env['PATH'] + .split(';') + .filter(function(p) { + if (fs.existsSync(path.resolve(p, 'msbuild.exe'))) { + console.log('Excluding "' + p + '" from PATH to avoid msbuild.exe mismatch') + return false; + } else { + return true; + } + }) + .join(';'); +} + cp.safeExec('node script/bootstrap', function() { // build/node_modules/.bin/grunt "$@" var args = process.argv.slice(2); From fc564873c28e51aeb38ab23383bb6e4485003842 Mon Sep 17 00:00:00 2001 From: simurai Date: Wed, 23 Mar 2016 13:43:42 +0900 Subject: [PATCH 558/971] :arrow_up: one-dark/light-ui@v1.3.0 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4b0d92b89..8b1a6187a 100644 --- a/package.json +++ b/package.json @@ -66,8 +66,8 @@ "atom-light-ui": "0.43.0", "base16-tomorrow-dark-theme": "1.1.0", "base16-tomorrow-light-theme": "1.1.1", - "one-dark-ui": "1.2.0", - "one-light-ui": "1.2.0", + "one-dark-ui": "1.3.0", + "one-light-ui": "1.3.0", "one-dark-syntax": "1.2.0", "one-light-syntax": "1.2.0", "solarized-dark-syntax": "1.0.0", From c2242e46c24def201b2a5d3eff26f429391543c1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 23 Mar 2016 10:31:26 +0100 Subject: [PATCH 559/971] =?UTF-8?q?Read=20state=20from=20StorageFolder=20w?= =?UTF-8?q?hen=20it=20can=E2=80=99t=20be=20found=20in=20StateStore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/atom-environment-spec.coffee | 28 ++++++++++++++++++++++++++++ src/atom-environment.coffee | 11 ++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 3283b63d6..2ba0164e6 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -4,6 +4,7 @@ temp = require 'temp' Package = require '../src/package' ThemeManager = require '../src/theme-manager' AtomEnvironment = require '../src/atom-environment' +StorageFolder = require '../src/storage-folder' describe "AtomEnvironment", -> describe 'window sizing methods', -> @@ -179,6 +180,33 @@ describe "AtomEnvironment", -> atom.loadState().then (state) -> expect(state).toEqual({stuff: 'cool'}) + it "loads state from the storage folder when it can't be found in atom.stateStore", -> + jasmine.useRealClock() + + storageFolderState = {foo: 1, bar: 2} + serializedState = {someState: 42} + loadSettings = _.extend(atom.getLoadSettings(), {initialPaths: [temp.mkdirSync("project-directory")]}) + spyOn(atom, 'getLoadSettings').andReturn(loadSettings) + spyOn(atom, 'serialize').andReturn(serializedState) + spyOn(atom, 'getConfigDirPath').andReturn(temp.mkdirSync("config-directory")) + atom.project.setPaths(atom.getLoadSettings().initialPaths) + + waitsForPromise -> + atom.stateStore.connect() + + runs -> + storageFolder = new StorageFolder(atom.getConfigDirPath()) + storageFolder.storeSync(atom.getStateKey(loadSettings.initialPaths), storageFolderState) + + waitsForPromise -> + atom.loadState().then (state) -> expect(state).toEqual(storageFolderState) + + waitsForPromise -> + atom.saveState() + + waitsForPromise -> + atom.loadState().then (state) -> expect(state).toEqual(serializedState) + it "saves state on keydown, mousedown, and when the editor window unloads", -> spyOn(atom, 'saveState') diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 8c79d66f6..a34065bbd 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -11,6 +11,7 @@ Model = require './model' WindowEventHandler = require './window-event-handler' StylesElement = require './styles-element' StateStore = require './state-store' +StorageFolder = require './storage-folder' {getWindowLoadSettings, setWindowLoadSettings} = require './window-load-settings-helpers' registerDefaultCommands = require './register-default-commands' @@ -853,7 +854,12 @@ class AtomEnvironment extends Model loadState: -> if @enablePersistence if stateKey = @getStateKey(@getLoadSettings().initialPaths) - @stateStore.load(stateKey) + @stateStore.load(stateKey).then (state) => + if state + state + else + # TODO: remove this when every user has migrated to the IndexedDb state store. + @getStorageFolder().load(stateKey) else @applicationDelegate.getTemporaryWindowState() else @@ -882,6 +888,9 @@ class AtomEnvironment extends Model else null + getStorageFolder: -> + @storageFolder ?= new StorageFolder(@getConfigDirPath()) + getConfigDirPath: -> @configDirPath ?= process.env.ATOM_HOME From 80146ae631411e0fd5d6d276c2c4a105c1c147ff Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 23 Mar 2016 10:52:34 +0100 Subject: [PATCH 560/971] Assign the supplied configDirPath to an instance variable --- src/atom-environment.coffee | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index a34065bbd..f4dc49060 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -128,7 +128,7 @@ class AtomEnvironment extends Model # Call .loadOrCreate instead constructor: (params={}) -> - {@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params + {@blobStore, @applicationDelegate, @window, @document, @configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params @unloaded = false @loadTime = null @@ -148,10 +148,10 @@ class AtomEnvironment extends Model @notifications = new NotificationManager - @config = new Config({configDirPath, resourcePath, notificationManager: @notifications, @enablePersistence}) + @config = new Config({@configDirPath, resourcePath, notificationManager: @notifications, @enablePersistence}) @setConfigSchema() - @keymaps = new KeymapManager({configDirPath, resourcePath, notificationManager: @notifications}) + @keymaps = new KeymapManager({@configDirPath, resourcePath, notificationManager: @notifications}) @tooltips = new TooltipManager(keymapManager: @keymaps) @@ -160,16 +160,16 @@ class AtomEnvironment extends Model @grammars = new GrammarRegistry({@config}) - @styles = new StyleManager({configDirPath}) + @styles = new StyleManager({@configDirPath}) @packages = new PackageManager({ - devMode, configDirPath, resourcePath, safeMode, @config, styleManager: @styles, + devMode, @configDirPath, resourcePath, safeMode, @config, styleManager: @styles, commandRegistry: @commands, keymapManager: @keymaps, notificationManager: @notifications, grammarRegistry: @grammars, deserializerManager: @deserializers, viewRegistry: @views }) @themes = new ThemeManager({ - packageManager: @packages, configDirPath, resourcePath, safeMode, @config, + packageManager: @packages, @configDirPath, resourcePath, safeMode, @config, styleManager: @styles, notificationManager: @notifications, viewRegistry: @views }) From 25a4c4c293e61ca0971ff1f5a86af68f299c8cfe Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 23 Mar 2016 10:53:37 +0100 Subject: [PATCH 561/971] Clear StorageFolder when --clear-window-state is supplied --- src/atom-environment.coffee | 4 +++- src/storage-folder.coffee | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index f4dc49060..6ef46dc2a 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -139,7 +139,9 @@ class AtomEnvironment extends Model @stateStore = new StateStore('AtomEnvironments', 1) - @stateStore.clear() if clearWindowState + if clearWindowState + @getStorageFolder().clear() + @stateStore.clear() @deserializers = new DeserializerManager(this) @deserializeTimings = {} diff --git a/src/storage-folder.coffee b/src/storage-folder.coffee index 06beae56a..327697672 100644 --- a/src/storage-folder.coffee +++ b/src/storage-folder.coffee @@ -6,6 +6,14 @@ class StorageFolder constructor: (containingPath) -> @path = path.join(containingPath, "storage") if containingPath? + clear: -> + return unless @path? + + try + fs.removeSync(@path) + catch error + console.warn "Error deleting #{statePath}", error.stack, error + storeSync: (name, object) -> return unless @path? From f69c5bdee436f44ac5b71f0761968a180570b726 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 23 Mar 2016 12:11:27 +0100 Subject: [PATCH 562/971] Oops. --- src/storage-folder.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storage-folder.coffee b/src/storage-folder.coffee index 327697672..280eb8b5c 100644 --- a/src/storage-folder.coffee +++ b/src/storage-folder.coffee @@ -12,7 +12,7 @@ class StorageFolder try fs.removeSync(@path) catch error - console.warn "Error deleting #{statePath}", error.stack, error + console.warn "Error deleting #{@path}", error.stack, error storeSync: (name, object) -> return unless @path? From cbb911cde8652d7aff6c2a0080d7ca9e5bac7641 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 23 Mar 2016 13:43:54 +0100 Subject: [PATCH 563/971] :green_heart: --- spec/atom-environment-spec.coffee | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 2ba0164e6..5fd4b11f1 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -173,7 +173,7 @@ describe "AtomEnvironment", -> waitsForPromise -> atom.saveState().then -> atom.loadState().then (state) -> - expect(state).toBeNull() + expect(state).toBeFalsy() waitsForPromise -> loadSettings.initialPaths = [dir2, dir1] @@ -188,15 +188,14 @@ describe "AtomEnvironment", -> loadSettings = _.extend(atom.getLoadSettings(), {initialPaths: [temp.mkdirSync("project-directory")]}) spyOn(atom, 'getLoadSettings').andReturn(loadSettings) spyOn(atom, 'serialize').andReturn(serializedState) - spyOn(atom, 'getConfigDirPath').andReturn(temp.mkdirSync("config-directory")) + spyOn(atom, 'getStorageFolder').andReturn(new StorageFolder(temp.mkdirSync("config-directory"))) atom.project.setPaths(atom.getLoadSettings().initialPaths) waitsForPromise -> atom.stateStore.connect() runs -> - storageFolder = new StorageFolder(atom.getConfigDirPath()) - storageFolder.storeSync(atom.getStateKey(loadSettings.initialPaths), storageFolderState) + atom.getStorageFolder().storeSync(atom.getStateKey(loadSettings.initialPaths), storageFolderState) waitsForPromise -> atom.loadState().then (state) -> expect(state).toEqual(storageFolderState) From 21831a4e436203a6d0a9e213c11ca89ce1f41559 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 23 Mar 2016 16:16:51 +0100 Subject: [PATCH 564/971] :bug: Fix inaccurate logic in moveLineDown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of deleting and then inserting, it’s better to do the opposite so that we don’t have to translate points at all. --- src/text-editor.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 1f9893132..2200ce827 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1016,13 +1016,12 @@ class TextEditor extends Model .map((range) -> range.translate([insertDelta, 0])) # Delete lines spanned by selection and insert them on the following correct buffer row - insertPosition = new Point(selection.translate([insertDelta, 0]).start.row, 0) lines = @buffer.getTextInRange(linesRange) if linesRange.end.row is @buffer.getLastRow() lines = "\n#{lines}" + @buffer.insert([followingRow, 0], lines) @buffer.delete(linesRange) - @buffer.insert(insertPosition, lines) # Restore folds that existed before the lines were moved for rangeToRefold in rangesToRefold From 6acbbb3a5c6002dc6136750be1be1a62ca11ea8b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 23 Mar 2016 17:20:08 +0100 Subject: [PATCH 565/971] Improve DisplayLayer management --- src/display-buffer.coffee | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index b10ffb0c4..b17600226 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -30,6 +30,7 @@ class DisplayBuffer extends Model @deserialize: (state, atomEnvironment) -> state.tokenizedBuffer = TokenizedBuffer.deserialize(state.tokenizedBuffer, atomEnvironment) + state.displayLayer = state.tokenizedBuffer.buffer.getDisplayLayer(state.displayLayerId) state.config = atomEnvironment.config state.assert = atomEnvironment.assert state.grammarRegistry = atomEnvironment.grammars @@ -41,7 +42,7 @@ class DisplayBuffer extends Model { tabLength, @editorWidthInChars, @tokenizedBuffer, buffer, @ignoreInvisibles, - @largeFileMode, @config, @assert, @grammarRegistry, @packageManager + @largeFileMode, @config, @assert, @grammarRegistry, @packageManager, @displayLayer } = params @emitter = new Emitter @@ -52,7 +53,7 @@ class DisplayBuffer extends Model @grammarRegistry, @packageManager, @assert }) @buffer = @tokenizedBuffer.buffer - @displayLayer ?= @buffer.getDisplayLayer() ? @buffer.addDisplayLayer() + @displayLayer ?= @buffer.addDisplayLayer() @displayLayer.setTextDecorationLayer(@tokenizedBuffer) @charWidthsByScope = {} @defaultMarkerLayer = @displayLayer.addMarkerLayer() @@ -92,11 +93,12 @@ class DisplayBuffer extends Model editorWidthInChars: @editorWidthInChars tokenizedBuffer: @tokenizedBuffer.serialize() largeFileMode: @largeFileMode + displayLayerId: @displayLayer.id copy: -> new DisplayBuffer({ @buffer, tabLength: @getTabLength(), @largeFileMode, @config, @assert, - @grammarRegistry, @packageManager, displayLayer: @displayLayer.copy() + @grammarRegistry, @packageManager, displayLayer: @buffer.copyDisplayLayer(@displayLayer.id) }) resetDisplayLayer: -> From 986822aef628c532a76883f4ac26891317f24c91 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 23 Mar 2016 16:14:18 -0400 Subject: [PATCH 566/971] :arrow_up: language-shellscript@0.21.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8b1a6187a..331986a93 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "language-ruby": "0.68.3", "language-ruby-on-rails": "0.25.0", "language-sass": "0.46.0", - "language-shellscript": "0.21.0", + "language-shellscript": "0.21.1", "language-source": "0.9.0", "language-sql": "0.20.0", "language-text": "0.7.1", From 53684f665fbadefdb5f85bfc485444fe3cbacc86 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 23 Mar 2016 16:19:03 -0400 Subject: [PATCH 567/971] :arrow_up: language-yaml@0.25.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 331986a93..743d6bb9d 100644 --- a/package.json +++ b/package.json @@ -148,7 +148,7 @@ "language-todo": "0.27.0", "language-toml": "0.18.0", "language-xml": "0.34.4", - "language-yaml": "0.25.1" + "language-yaml": "0.25.2" }, "private": true, "scripts": { From a8d6a0036d08363c69a4df9eb8e31658cfffec86 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 24 Mar 2016 13:40:24 +0100 Subject: [PATCH 568/971] =?UTF-8?q?:sparkles:=20Don=E2=80=99t=20unfold=20b?= =?UTF-8?q?uffer=20rows=20when=20editing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/selection.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/selection.coffee b/src/selection.coffee index b90bb6854..7f77ad6c0 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -365,7 +365,6 @@ class Selection extends Model # * `undo` if `skip`, skips the undo stack for this operation. insertText: (text, options={}) -> oldBufferRange = @getBufferRange() - @editor.unfoldBufferRow(oldBufferRange.end.row) wasReversed = @isReversed() @clear() From 4136e27d4439df8331fa1fe2515ddefadc873f9e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 24 Mar 2016 13:43:42 +0100 Subject: [PATCH 569/971] Fix TextEditor backspace() and delete() specs --- spec/text-editor-spec.coffee | 53 ++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index cce47b926..51a8bc150 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3245,15 +3245,14 @@ describe "TextEditor", -> editor.setCursorScreenPosition(row: 0, column: 0) editor.backspace() - describe "when the cursor is on the first column of a line below a fold", -> - it "deletes the folded lines", -> - editor.setCursorScreenPosition([4, 0]) - editor.foldCurrentRow() - editor.setCursorScreenPosition([5, 0]) + describe "when the cursor is after a fold", -> + it "deletes the folded range", -> + editor.foldBufferRange([[4, 7], [5, 8]]) + editor.setCursorBufferPosition([5, 8]) editor.backspace() - expect(buffer.lineForRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" - expect(buffer.lineForRow(4).fold).toBeUndefined() + expect(buffer.lineForRow(4)).toBe " whirrent = items.shift();" + expect(editor.isFoldedAtBufferRow(4)).toBe(false) describe "when the cursor is in the middle of a line below a fold", -> it "backspaces as normal", -> @@ -3266,14 +3265,13 @@ describe "TextEditor", -> expect(buffer.lineForRow(8)).toBe " eturn sort(left).concat(pivot).concat(sort(right));" describe "when the cursor is on a folded screen line", -> - it "deletes all of the folded lines along with the fold", -> + it "deletes the contents of the fold before the cursor", -> editor.setCursorBufferPosition([3, 0]) editor.foldCurrentRow() editor.backspace() - expect(buffer.lineForRow(1)).toBe "" - expect(buffer.lineForRow(2)).toBe " return sort(Array.apply(this, arguments));" - expect(editor.getCursorScreenPosition()).toEqual [1, 0] + expect(buffer.lineForRow(1)).toBe " var sort = function(items) var pivot = items.shift(), current, left = [], right = [];" + expect(editor.getCursorScreenPosition()).toEqual [1, 29] describe "when there are multiple cursors", -> describe "when cursors are on the same line", -> @@ -3340,7 +3338,7 @@ describe "TextEditor", -> editor.backspace() expect(buffer.lineForRow(3)).toBe " while(items.length > 0) {" - expect(editor.tokenizedLineForScreenRow(3).fold).toBeDefined() + expect(editor.isFoldedAtScreenRow(3)).toBe(true) describe "when there are multiple selections", -> it "removes all selected text", -> @@ -3513,16 +3511,16 @@ describe "TextEditor", -> editor.delete() expect(buffer.lineForRow(12)).toBe '};' - describe "when the cursor is on the end of a line above a fold", -> + describe "when the cursor is before a fold", -> it "only deletes the lines inside the fold", -> - editor.foldBufferRow(4) - editor.setCursorScreenPosition([3, Infinity]) + editor.foldBufferRange([[3, 6], [4, 8]]) + editor.setCursorScreenPosition([3, 6]) cursorPositionBefore = editor.getCursorScreenPosition() editor.delete() - expect(buffer.lineForRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" - expect(buffer.lineForRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(buffer.lineForRow(3)).toBe " vae(items.length > 0) {" + expect(buffer.lineForRow(4)).toBe " current = items.shift();" expect(editor.getCursorScreenPosition()).toEqual cursorPositionBefore describe "when the cursor is in the middle a line above a fold", -> @@ -3534,20 +3532,21 @@ describe "TextEditor", -> editor.delete() expect(buffer.lineForRow(3)).toBe " ar pivot = items.shift(), current, left = [], right = [];" - expect(editor.tokenizedLineForScreenRow(4).fold).toBeDefined() + expect(editor.isFoldedAtScreenRow(4)).toBe(true) expect(editor.getCursorScreenPosition()).toEqual [3, 4] - describe "when the cursor is on a folded line", -> - it "removes the lines contained by the fold", -> - editor.setSelectedBufferRange([[2, 0], [2, 0]]) - editor.foldBufferRowRange(2, 4) - editor.foldBufferRowRange(2, 6) - oldLine7 = buffer.lineForRow(7) - oldLine8 = buffer.lineForRow(8) + describe "when the cursor is inside a fold", -> + it "removes the folded content after the cursor", -> + editor.foldBufferRange([[2, 6], [6, 21]]) + editor.setCursorBufferPosition([4, 9]) editor.delete() - expect(editor.tokenizedLineForScreenRow(2).text).toBe oldLine7 - expect(editor.tokenizedLineForScreenRow(3).text).toBe oldLine8 + + expect(buffer.lineForRow(2)).toBe ' if (items.length <= 1) return items;' + expect(buffer.lineForRow(3)).toBe ' var pivot = items.shift(), current, left = [], right = [];' + expect(buffer.lineForRow(4)).toBe ' while ? left.push(current) : right.push(current);' + expect(buffer.lineForRow(5)).toBe ' }' + expect(editor.getCursorBufferPosition()).toEqual [4, 9] describe "when there are multiple cursors", -> describe "when cursors are on the same line", -> From 87489d4b0b25a05e9df7f4c65744668798abe8cd Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 24 Mar 2016 14:15:06 +0100 Subject: [PATCH 570/971] Fix TextEditor cutToEndOfLine() test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …which was failing due to the different soft-wrapping rules. --- spec/text-editor-spec.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 51a8bc150..9c9cf1475 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3803,10 +3803,10 @@ describe "TextEditor", -> it "cuts up to the end of the line", -> editor.setSoftWrapped(true) editor.setDefaultCharWidth(1) - editor.setEditorWidthInChars(10) - editor.setCursorScreenPosition([2, 2]) + editor.setEditorWidthInChars(25) + editor.setCursorScreenPosition([2, 6]) editor.cutToEndOfLine() - expect(editor.tokenizedLineForScreenRow(2).text).toBe '= () {' + expect(editor.lineTextForScreenRow(2)).toBe ' var function(items) {' describe "when soft wrap is off", -> describe "when nothing is selected", -> From e6cb5c8e89aab9ac347891d95fadc4137f1f9d69 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 24 Mar 2016 15:57:24 +0100 Subject: [PATCH 571/971] :bug: Guard against unexisting screen rows --- spec/text-editor-spec.coffee | 3 ++- src/text-editor.coffee | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 9c9cf1475..f16298029 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -4561,7 +4561,8 @@ describe "TextEditor", -> it '.lineTextForScreenRow(row)', -> editor.foldBufferRow(4) expect(editor.lineTextForScreenRow(5)).toEqual ' return sort(left).concat(pivot).concat(sort(right));' - expect(editor.lineTextForScreenRow(100)).not.toBeDefined() + expect(editor.lineTextForScreenRow(9)).toEqual '};' + expect(editor.lineTextForScreenRow(10)).toBeUndefined() describe ".deleteLine()", -> it "deletes the first line when the cursor is there", -> diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 2200ce827..7b77e4403 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -783,6 +783,7 @@ class TextEditor extends Model @displayLayer.tagForCode(tagCode) screenLineForScreenRow: (screenRow) -> + return if screenRow < 0 or screenRow > @getLastScreenRow() @displayLayer.getScreenLines(screenRow, screenRow + 1)[0] bufferRowForScreenRow: (row) -> @displayLayer.translateScreenPosition(Point(row, 0)).row From 1cb89634023134f86cd82fbdbd11cfe0d6d77a25 Mon Sep 17 00:00:00 2001 From: ardhipoetra Date: Thu, 24 Mar 2016 18:03:07 +0100 Subject: [PATCH 572/971] fix link in ISSUE_TEMPLATE --- ISSUE_TEMPLATE.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 73ff2f50d..8144a4b62 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,8 +1,8 @@ ### Prerequisites -* [ ] Can you reproduce the problem in [safe mode](https://atom.io/docs/latest/hacking-atom-debugging#check-if-the-problem-shows-up-in-safe-mode)? -* [ ] Are you running the [latest version of Atom](https://atom.io/docs/latest/hacking-atom-debugging#update-to-the-latest-version)? -* [ ] Did you check the [debugging guide](https://atom.io/docs/latest/hacking-atom-debugging)? +* [ ] Can you reproduce the problem in [safe mode](http://flight-manual.atom.io/hacking-atom/sections/debugging/#check-if-the-problem-shows-up-in-safe-mode)? +* [ ] Are you running the [latest version of Atom](http://flight-manual.atom.io/hacking-atom/sections/debugging/#update-to-the-latest-version)? +* [ ] Did you check the [debugging guide](flight-manual.atom.io/hacking-atom/sections/debugging/)? * [ ] Did you check the [FAQs on Discuss](https://discuss.atom.io/c/faq)? * [ ] Are you reporting to the [correct repository](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#atom-and-packages)? * [ ] Did you [perform a cursory search](https://github.com/issues?q=is%3Aissue+user%3Aatom+-repo%3Aatom%2Felectron) to see if your bug or enhancement is already reported? From 2016d0b6134f6efabbc270afd7e1b1d978d23009 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 24 Mar 2016 12:36:16 -0700 Subject: [PATCH 573/971] :arrow_up: snippets@1.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 743d6bb9d..267aed7ed 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "open-on-github": "1.0.1", "package-generator": "1.0.0", "settings-view": "0.235.1", - "snippets": "1.0.1", + "snippets": "1.0.2", "spell-check": "0.67.0", "status-bar": "1.2.0", "styleguide": "0.45.2", From e0f41d1af4b48b273ee113f6a5aa9f2cb4657bf3 Mon Sep 17 00:00:00 2001 From: "Mark H. Wilkinson" Date: Fri, 25 Mar 2016 00:05:10 +0000 Subject: [PATCH 574/971] Fix typo in error message. --- src/text-editor-element.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index 2a9b5e262..a0ec1b7fa 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -92,7 +92,7 @@ class TextEditorElement extends HTMLElement @emitter.emit("did-change-scroll-left", arguments...) initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}, @autoHeight = true, @scrollPastEnd = true) -> - throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @views? + throw new Error("Must pass a views parameter when initializing TextEditorElements") unless @views? throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @config? throw new Error("Must pass a themes parameter when initializing TextEditorElements") unless @themes? throw new Error("Must pass a workspace parameter when initializing TextEditorElements") unless @workspace? From 27aad426442e67a1a8ca124625a16823a0d71bc1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 25 Mar 2016 10:33:12 +0100 Subject: [PATCH 575/971] Handle tab length retokenization in DisplayLayer We still want to keep the tab length in TokenizedBuffer, because we need it to understand if a certain buffer row is foldable or not (due to the indent level) --- spec/text-editor-spec.coffee | 18 ++++++++++++------ src/display-buffer.coffee | 13 ++++++++++--- src/tokenized-buffer.coffee | 16 ++++------------ src/tokenized-line.coffee | 6 +----- 4 files changed, 27 insertions(+), 26 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index f16298029..138061a8f 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -4919,11 +4919,13 @@ describe "TextEditor", -> it 'retokenizes when the tab length is updated via .setTabLength()', -> expect(editor.getTabLength()).toBe 2 - expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2 + leadingWhitespaceTokens = editor.tokensForScreenRow(5).filter (token) -> token is 'leading-whitespace' + expect(leadingWhitespaceTokens.length).toBe(3) editor.setTabLength(6) expect(editor.getTabLength()).toBe 6 - expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 6 + leadingWhitespaceTokens = editor.tokensForScreenRow(5).filter (token) -> token is 'leading-whitespace' + expect(leadingWhitespaceTokens.length).toBe(1) changeHandler = jasmine.createSpy('changeHandler') editor.onDidChange(changeHandler) @@ -4932,21 +4934,25 @@ describe "TextEditor", -> it 'retokenizes when the editor.tabLength setting is updated', -> expect(editor.getTabLength()).toBe 2 - expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2 + leadingWhitespaceTokens = editor.tokensForScreenRow(5).filter (token) -> token is 'leading-whitespace' + expect(leadingWhitespaceTokens.length).toBe(3) atom.config.set 'editor.tabLength', 6, scopeSelector: '.source.js' expect(editor.getTabLength()).toBe 6 - expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 6 + leadingWhitespaceTokens = editor.tokensForScreenRow(5).filter (token) -> token is 'leading-whitespace' + expect(leadingWhitespaceTokens.length).toBe(1) it 'updates the tab length when the grammar changes', -> atom.config.set 'editor.tabLength', 6, scopeSelector: '.source.coffee' expect(editor.getTabLength()).toBe 2 - expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2 + leadingWhitespaceTokens = editor.tokensForScreenRow(5).filter (token) -> token is 'leading-whitespace' + expect(leadingWhitespaceTokens.length).toBe(3) editor.setGrammar(coffeeEditor.getGrammar()) expect(editor.getTabLength()).toBe 6 - expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 6 + leadingWhitespaceTokens = editor.tokensForScreenRow(5).filter (token) -> token is 'leading-whitespace' + expect(leadingWhitespaceTokens.length).toBe(1) describe ".indentLevelForLine(line)", -> it "returns the indent level when the line has only leading whitespace", -> diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index b17600226..a59ccc8f5 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -122,7 +122,7 @@ class DisplayBuffer extends Model invisibles: invisibles softWrapColumn: softWrapColumn showIndentGuides: @config.get('editor.showIndentGuide', scope: scopeDescriptor) - tabLength: @config.get('editor.tabLength', scope: scopeDescriptor), + tabLength: @getTabLength(), ratioForCharacter: @ratioForCharacter.bind(this) isWrapBoundary: isWrapBoundary }) @@ -265,13 +265,20 @@ class DisplayBuffer extends Model # # Returns a {Number}. getTabLength: -> - @tokenizedBuffer.getTabLength() + if @tabLength? + @tabLength + else + @config.get('editor.tabLength', scope: @getRootScopeDescriptor()) # Specifies the tab length. # # tabLength - A {Number} that defines the new tab length. setTabLength: (tabLength) -> - @tokenizedBuffer.setTabLength(tabLength) + return if tabLength is @tabLength + + @tabLength = tabLength + @tokenizedBuffer.setTabLength(@tabLength) + @resetDisplayLayer() setIgnoreInvisibles: (ignoreInvisibles) -> return if ignoreInvisibles is @ignoreInvisibles diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 470aa59b9..00d4cdf63 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -117,19 +117,14 @@ class TokenizedBuffer extends Model @grammarUpdateDisposable = @grammar.onDidUpdate => @retokenizeLines() @disposables.add(@grammarUpdateDisposable) - scopeOptions = {scope: @rootScopeDescriptor} - @configSettings = - tabLength: @config.get('editor.tabLength', scopeOptions) - invisibles: @config.get('editor.invisibles', scopeOptions) - showInvisibles: @config.get('editor.showInvisibles', scopeOptions) + @configSettings = {tabLength: @config.get('editor.tabLength', {scope: @rootScopeDescriptor})} if @configSubscriptions? @configSubscriptions.dispose() @disposables.remove(@configSubscriptions) @configSubscriptions = new CompositeDisposable - @configSubscriptions.add @config.onDidChange 'editor.tabLength', scopeOptions, ({newValue}) => + @configSubscriptions.add @config.onDidChange 'editor.tabLength', {scope: @rootScopeDescriptor}, ({newValue}) => @configSettings.tabLength = newValue - @retokenizeLines() @disposables.add(@configSubscriptions) @retokenizeLines() @@ -170,7 +165,6 @@ class TokenizedBuffer extends Model return if tabLength is @tabLength @tabLength = tabLength - @retokenizeLines() tokenizeInBackground: -> return if not @visible or @pendingChunk or not @isAlive() @@ -337,18 +331,16 @@ class TokenizedBuffer extends Model openScopes = [@grammar.startIdForScope(@grammar.scopeName)] text = @buffer.lineForRow(row) tags = [text.length] - tabLength = @getTabLength() lineEnding = @buffer.lineEndingForRow(row) - new TokenizedLine({openScopes, text, tags, tabLength, lineEnding, @tokenIterator}) + new TokenizedLine({openScopes, text, tags, lineEnding, @tokenIterator}) buildTokenizedLineForRow: (row, ruleStack, openScopes) -> @buildTokenizedLineForRowWithText(row, @buffer.lineForRow(row), ruleStack, openScopes) buildTokenizedLineForRowWithText: (row, text, ruleStack = @stackForRow(row - 1), openScopes = @openScopesForRow(row)) -> lineEnding = @buffer.lineEndingForRow(row) - tabLength = @getTabLength() {tags, ruleStack} = @grammar.tokenizeLine(text, ruleStack, row is 0, false) - new TokenizedLine({openScopes, text, tags, ruleStack, tabLength, lineEnding, @tokenIterator}) + new TokenizedLine({openScopes, text, tags, ruleStack, lineEnding, @tokenIterator}) tokenizedLineForRow: (bufferRow) -> if 0 <= bufferRow < @tokenizedLines.length diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index cbf64633b..8574cb960 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -16,11 +16,7 @@ class TokenizedLine return unless properties? - {@openScopes, @text, @tags, @lineEnding, @ruleStack, @tokenIterator} = properties - {@startBufferColumn, @fold, @tabLength, @invisibles} = properties - - @startBufferColumn ?= 0 - @bufferDelta = @text.length + {@openScopes, @text, @tags, @ruleStack, @tokenIterator} = properties getTokenIterator: -> @tokenIterator.reset(this, arguments...) From 0a634a5870ee9302a6c39f824e57efd9892c8e21 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 25 Mar 2016 10:49:32 +0100 Subject: [PATCH 576/971] :green_heart: Fix more tests using tokenizedLineForScreenRow --- spec/text-editor-spec.coffee | 40 +++++++++++++++--------------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 138061a8f..2121bb2da 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -4988,11 +4988,11 @@ describe "TextEditor", -> runs -> expect(editor.getGrammar()).toBe atom.grammars.nullGrammar - expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBe 1 + expect(editor.tokensForScreenRow(0).length).toBe(1) atom.grammars.addGrammar(jsGrammar) expect(editor.getGrammar()).toBe jsGrammar - expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBeGreaterThan 1 + expect(editor.tokensForScreenRow(0).length).toBeGreaterThan 1 describe "editor.autoIndent", -> describe "when editor.autoIndent is false (default)", -> @@ -5220,10 +5220,10 @@ describe "TextEditor", -> expect(editor.getSelectedBufferRanges()).toEqual [[[3, 5], [3, 5]], [[9, 0], [14, 0]]] # folds are also duplicated - expect(editor.tokenizedLineForScreenRow(5).fold).toBeDefined() - expect(editor.tokenizedLineForScreenRow(7).fold).toBeDefined() - expect(editor.tokenizedLineForScreenRow(7).text).toBe " while(items.length > 0) {" - expect(editor.tokenizedLineForScreenRow(8).text).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.isFoldedAtScreenRow(5)).toBe(true) + expect(editor.isFoldedAtScreenRow(7)).toBe(true) + expect(editor.lineTextForScreenRow(7)).toBe " while(items.length > 0) {⋯" + expect(editor.lineTextForScreenRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));" it "duplicates all folded lines for empty selections on folded lines", -> editor.foldBufferRow(4) @@ -5419,17 +5419,15 @@ describe "TextEditor", -> runs -> editor.setText("// http://github.com") - {tokens} = editor.tokenizedLineForScreenRow(0) - expect(tokens[1].value).toBe " http://github.com" - expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"] + tokens = editor.tokensForScreenRow(0) + expect(tokens).toEqual ['source.js', 'comment.line.double-slash.js', 'punctuation.definition.comment.js'] waitsForPromise -> atom.packages.activatePackage('language-hyperlink') runs -> - {tokens} = editor.tokenizedLineForScreenRow(0) - expect(tokens[2].value).toBe "http://github.com" - expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"] + tokens = editor.tokensForScreenRow(0) + expect(tokens).toEqual ['source.js', 'comment.line.double-slash.js', 'punctuation.definition.comment.js', 'markup.underline.link.http.hyperlink'] describe "when the grammar is updated", -> it "retokenizes existing buffers that contain tokens that match the injection selector", -> @@ -5439,28 +5437,22 @@ describe "TextEditor", -> runs -> editor.setText("// SELECT * FROM OCTOCATS") - {tokens} = editor.screenLineForScreenRow(0) - expect(tokens[2].closeTags).toEqual ['comment.line.double-slash.js', 'source.js'] - expect(tokens[2].openTags).toEqual [] - expect(tokens[2].text).toBe "" + tokens = editor.tokensForScreenRow(0) + expect(tokens).toEqual ['source.js', 'comment.line.double-slash.js', 'punctuation.definition.comment.js'] waitsForPromise -> atom.packages.activatePackage('package-with-injection-selector') runs -> - {tokens} = editor.screenLineForScreenRow(0) - expect(tokens[2].closeTags).toEqual ['comment.line.double-slash.js', 'source.js'] - expect(tokens[2].openTags).toEqual [] - expect(tokens[2].text).toBe "" + tokens = editor.tokensForScreenRow(0) + expect(tokens).toEqual ['source.js', 'comment.line.double-slash.js', 'punctuation.definition.comment.js'] waitsForPromise -> atom.packages.activatePackage('language-sql') runs -> - {tokens} = editor.screenLineForScreenRow(2) - expect(tokens[2].closeTags).toEqual [] - expect(tokens[2].openTags).toEqual ["keyword.other.DML.sql"] - expect(tokens[2].text).toBe "SELECT" + tokens = editor.tokensForScreenRow(0) + expect(tokens).toEqual ['source.js', 'comment.line.double-slash.js', 'punctuation.definition.comment.js', 'keyword.other.DML.sql', 'keyword.operator.star.sql', 'keyword.other.DML.sql'] describe ".normalizeTabsInBufferRange()", -> it "normalizes tabs depending on the editor's soft tab/tab length settings", -> From c609f6c967c5434b34b4549121a486e8d6e3c53c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 25 Mar 2016 10:53:02 +0100 Subject: [PATCH 577/971] Destroy `DisplayLayer` upon `DisplayBuffer` destruction --- spec/text-editor-spec.coffee | 2 +- src/display-buffer.coffee | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 2121bb2da..8c8fdf2e2 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5136,7 +5136,7 @@ describe "TextEditor", -> describe ".destroy()", -> it "destroys marker layers associated with the text editor", -> selectionsMarkerLayerId = editor.selectionsMarkerLayer.id - foldsMarkerLayerId = editor.displayBuffer.foldsMarkerLayer.id + foldsMarkerLayerId = editor.displayLayer.foldsMarkerLayer.id editor.destroy() expect(buffer.getMarkerLayer(selectionsMarkerLayerId)).toBeUndefined() expect(buffer.getMarkerLayer(foldsMarkerLayerId)).toBeUndefined() diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index a59ccc8f5..560aefb20 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -885,6 +885,7 @@ class DisplayBuffer extends Model return destroyed: -> + @displayLayer.destroy() @defaultMarkerLayer.destroy() @scopedConfigSubscriptions.dispose() @disposables.dispose() From 694d288e1669309cf03eba20e58447edf18d652b Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 10:31:13 -0400 Subject: [PATCH 578/971] Cache the workdir and path. --- src/git-repository-async.js | 59 ++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 82546b525..943a98841 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -43,6 +43,8 @@ export default class GitRepositoryAsync { this.emitter = new Emitter() this.subscriptions = new CompositeDisposable() this.pathStatusCache = {} + this.workdir = null + this.path = null // NB: These needs to happen before the following .openRepository call. this.openedPath = _path @@ -142,13 +144,24 @@ export default class GitRepositoryAsync { // Public: Returns a {Promise} which resolves to the {String} path of the // repository. getPath () { - return this.getRepo().then(repo => repo.path().replace(/\/$/, '')) + return this.getRepo().then(repo => { + if (!this.path) { + this.path = repo.path().replace(/\/$/, '') + } + return this.path + }) } // Public: Returns a {Promise} which resolves to the {String} working // directory path of the repository. getWorkingDirectory () { - return this.getRepo().then(repo => repo.workdir()) + return this.getRepo().then(repo => { + if (!this.workdir) { + this.workdir = repo.workdir() + } + + return this.workdir + }) } // Public: Returns a {Promise} that resolves to true if at the root, false if @@ -157,8 +170,8 @@ export default class GitRepositoryAsync { if (!this.project) return Promise.resolve(false) if (!this.projectAtRoot) { - this.projectAtRoot = this.getRepo() - .then(repo => this.project.relativize(repo.workdir()) === '') + this.projectAtRoot = this.getWorkingDirectory() + .then(wd => this.project.relativize(wd) === '') } return this.projectAtRoot @@ -170,8 +183,8 @@ export default class GitRepositoryAsync { // // Returns a {Promise} which resolves to the relative {String} path. relativizeToWorkingDirectory (_path) { - return this.getRepo() - .then(repo => this.relativize(_path, repo.workdir())) + return this.getWorkingDirectory() + .then(wd => this.relativize(_path, wd)) } // Public: Makes a path relative to the repository's working directory. @@ -447,10 +460,9 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {Boolean} that's true if the `path` // is ignored. isPathIgnored (_path) { - return this.getRepo() - .then(repo => { - const relativePath = this.relativize(_path, repo.workdir()) - return Git.Ignore.pathIsIgnored(repo, relativePath) + return Promise.all([this.getRepo(), this.getWorkingDirectory()]) + .then(([repo, wd]) => { + const relativePath = this.relativize(_path, wd) }) .then(ignored => Boolean(ignored)) } @@ -488,9 +500,9 @@ export default class GitRepositoryAsync { // status bit for the path. refreshStatusForPath (_path) { let relativePath - return this.getRepo() - .then(repo => { - relativePath = this.relativize(_path, repo.workdir()) + return Promise.all([this.getRepo(), this.getWorkingDirectory()]) + .then(([repo, wd]) => { + relativePath = this.relativize(_path, wd) return this._getStatus([relativePath]) }) .then(statuses => { @@ -598,12 +610,12 @@ export default class GitRepositoryAsync { getDiffStats (_path) { return this.getRepo() .then(repo => Promise.all([repo, repo.getHeadCommit()])) - .then(([repo, headCommit]) => Promise.all([repo, headCommit.getTree()])) - .then(([repo, tree]) => { + .then(([repo, headCommit]) => Promise.all([repo, headCommit.getTree(), this.getWorkingDirectory()])) + .then(([repo, tree, wd]) => { const options = new Git.DiffOptions() options.contextLines = 0 options.flags = Git.Diff.OPTION.DISABLE_PATHSPEC_MATCH - options.pathspec = this.relativize(_path, repo.workdir()) + options.pathspec = this.relativize(_path, wd) if (process.platform === 'win32') { // Ignore eol of line differences on windows so that files checked in // as LF don't report every line modified when the text contains CRLF @@ -640,9 +652,9 @@ export default class GitRepositoryAsync { // * `newLines` The {Number} of lines in the new hunk getLineDiffs (_path, text) { let relativePath = null - return this.getRepo() - .then(repo => { - relativePath = this.relativize(_path, repo.workdir()) + return Promise.all([this.getRepo(), this.getWorkingDirectory()]) + .then(([repo, wd]) => { + relativePath = this.relativize(_path, wd) return repo.getHeadCommit() }) .then(commit => commit.getEntry(relativePath)) @@ -678,10 +690,10 @@ export default class GitRepositoryAsync { // Returns a {Promise} that resolves or rejects depending on whether the // method was successful. checkoutHead (_path) { - return this.getRepo() - .then(repo => { + return Promise.all([this.getRepo(), this.getWorkingDirectory()]) + .then(([repo, wd]) => { const checkoutOptions = new Git.CheckoutOptions() - checkoutOptions.paths = [this.relativize(_path, repo.workdir())] + checkoutOptions.paths = [this.relativize(_path, wd)] checkoutOptions.checkoutStrategy = Git.Checkout.STRATEGY.FORCE | Git.Checkout.STRATEGY.DISABLE_PATHSPEC_MATCH return Git.Checkout.head(repo, checkoutOptions) }) @@ -873,13 +885,14 @@ export default class GitRepositoryAsync { // submodule names with {GitRepositoryAsync} values. async _refreshSubmodules () { const repo = await this.getRepo() + const wd = await this.getWorkingDirectory() const submoduleNames = await repo.getSubmoduleNames() for (const name of submoduleNames) { const alreadyExists = Boolean(this.submodules[name]) if (alreadyExists) continue const submodule = await Git.Submodule.lookup(repo, name) - const absolutePath = path.join(repo.workdir(), submodule.path()) + const absolutePath = path.join(wd, submodule.path()) const submoduleRepo = GitRepositoryAsync.open(absolutePath, {openExactPath: true, refreshOnWindowFocus: false}) this.submodules[name] = submoduleRepo } From 7294b95b70dd2b4be55a970e5e399eda5289b9fd Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 10:49:22 -0400 Subject: [PATCH 579/971] Skip the first reload event. --- src/git-repository-async.js | 8 +++++++- src/git-repository.coffee | 9 ++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 943a98841..668183ba7 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -1056,9 +1056,15 @@ export default class GitRepositoryAsync { } } + // TextBuffers will emit a reload event when they're first loaded. We don't + // need to refresh in that case. + let firstReload = true bufferSubscriptions.add( buffer.onDidSave(refreshStatusForBuffer), - buffer.onDidReload(refreshStatusForBuffer), + buffer.onDidReload(() => { + if (!firstReload) refreshStatusForBuffer() + firstReload = false + }), buffer.onDidChangePath(refreshStatusForBuffer), buffer.onDidDestroy(() => { bufferSubscriptions.dispose() diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 0513c2293..bc4f57161 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -448,7 +448,14 @@ class GitRepository bufferSubscriptions = new CompositeDisposable bufferSubscriptions.add buffer.onDidSave(getBufferPathStatus) - bufferSubscriptions.add buffer.onDidReload(getBufferPathStatus) + + # TextBuffers will emit a reload event when they're first loaded. We don't + # need to refresh in that case. + firstReload = true + bufferSubscriptions.add buffer.onDidReload(-> + getBufferPathStatus() unless firstReload + firstReload = false + ) bufferSubscriptions.add buffer.onDidChangePath(getBufferPathStatus) bufferSubscriptions.add buffer.onDidDestroy => bufferSubscriptions.dispose() From f9fb93f214b11db61dccdfabde0b25f24d3d7a18 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 25 Mar 2016 16:37:01 +0100 Subject: [PATCH 580/971] :art: --- spec/fake-lines-yardstick.coffee | 41 +++++++++++++++++++------------- src/text-editor.coffee | 4 ---- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/spec/fake-lines-yardstick.coffee b/spec/fake-lines-yardstick.coffee index 085ab4bde..c3396ff9f 100644 --- a/spec/fake-lines-yardstick.coffee +++ b/spec/fake-lines-yardstick.coffee @@ -32,25 +32,32 @@ class FakeLinesYardstick column = 0 scopes = [] - for {text, closeTags, openTags} in @displayLayer.getScreenLines(targetRow, targetRow + 1)[0].tokens - scopes.splice(scopes.lastIndexOf(closeTag), 1) for closeTag in closeTags - scopes.push(openTag) for openTag in openTags - characterWidths = @getScopedCharacterWidths(scopes) + startIndex = 0 + {tagCodes, lineText} = @model.screenLineForScreenRow(targetRow) + for tagCode in tagCodes + if @displayLayer.isOpenTagCode(tagCode) + scopes.push(@displayLayer.tagForCode(tagCode)) + else if @displayLayer.isCloseTagCode(tagCode) + scopes.splice(scopes.lastIndexOf(@displayLayer.tagForCode(tagCode)), 1) + else + text = lineText.substr(startIndex, tagCode) + startIndex += tagCode + characterWidths = @getScopedCharacterWidths(scopes) - valueIndex = 0 - while valueIndex < text.length - if isPairedCharacter(text, valueIndex) - char = text[valueIndex...valueIndex + 2] - charLength = 2 - valueIndex += 2 - else - char = text[valueIndex] - charLength = 1 - valueIndex++ + valueIndex = 0 + while valueIndex < text.length + if isPairedCharacter(text, valueIndex) + char = text[valueIndex...valueIndex + 2] + charLength = 2 + valueIndex += 2 + else + char = text[valueIndex] + charLength = 1 + valueIndex++ - break if column is targetColumn + break if column is targetColumn - left += characterWidths[char] ? @model.getDefaultCharWidth() unless char is '\0' - column += charLength + left += characterWidths[char] ? @model.getDefaultCharWidth() unless char is '\0' + column += charLength {top, left} diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 7b77e4403..958c1d8ec 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2971,10 +2971,6 @@ class TextEditor extends Model destroyFoldsIntersectingBufferRange: (bufferRange) -> @displayLayer.destroyFoldsIntersectingBufferRange(bufferRange) - # {Delegates to: DisplayLayer.outermostFoldsForBufferRowRange} - outermostFoldsInBufferRowRange: (startRow, endRow) -> - @displayLayer.outermostFoldsInBufferRowRange(startRow, endRow) - ### Section: Gutters ### From aee053dc10f0382244e6b030c4b486e08b0d1c00 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 12:46:49 -0400 Subject: [PATCH 581/971] Whoops. Bring back pathIsIgnored. --- src/git-repository-async.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 668183ba7..edd85390b 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -148,6 +148,7 @@ export default class GitRepositoryAsync { if (!this.path) { this.path = repo.path().replace(/\/$/, '') } + return this.path }) } @@ -463,6 +464,7 @@ export default class GitRepositoryAsync { return Promise.all([this.getRepo(), this.getWorkingDirectory()]) .then(([repo, wd]) => { const relativePath = this.relativize(_path, wd) + return Git.Ignore.pathIsIgnored(repo, relativePath) }) .then(ignored => Boolean(ignored)) } From 39dcd59994d11e1cefde849bb5080f47460eb69f Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 13:36:38 -0400 Subject: [PATCH 582/971] Fix the specs. --- spec/git-repository-async-spec.js | 6 +++--- spec/git-spec.coffee | 22 +++++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 900d81bfb..25623ae27 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -483,9 +483,10 @@ describe('GitRepositoryAsync', () => { describe('buffer events', () => { let repo + let workingDirectory beforeEach(() => { - const workingDirectory = copyRepository() + workingDirectory = copyRepository() atom.project.setPaths([workingDirectory]) // When the path is added to the project, the repository is refreshed. We @@ -512,10 +513,9 @@ describe('GitRepositoryAsync', () => { }) it('emits a status-changed event when a buffer is reloaded', async () => { + fs.writeFileSync(path.join(workingDirectory, 'other.txt'), 'changed') const editor = await atom.workspace.open('other.txt') - fs.writeFileSync(editor.getPath(), 'changed') - const statusHandler = jasmine.createSpy('statusHandler') repo.onDidChangeStatus(statusHandler) editor.getBuffer().reload() diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 3afd4da75..db42f3fc8 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -290,10 +290,11 @@ describe "GitRepository", -> expect(repo.isStatusNew(status)).toBe false describe "buffer events", -> - [editor] = [] + [editor, workingDirectory] = [] beforeEach -> - atom.project.setPaths([copyRepository()]) + workingDirectory = copyRepository() + atom.project.setPaths([workingDirectory]) waitsForPromise -> atom.workspace.open('other.txt').then (o) -> editor = o @@ -310,13 +311,16 @@ describe "GitRepository", -> it "emits a status-changed event when a buffer is reloaded", -> fs.writeFileSync(editor.getPath(), 'changed') - statusHandler = jasmine.createSpy('statusHandler') - atom.project.getRepositories()[0].onDidChangeStatus statusHandler - editor.getBuffer().reload() - expect(statusHandler.callCount).toBe 1 - expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256} - editor.getBuffer().reload() - expect(statusHandler.callCount).toBe 1 + waitsForPromise -> + atom.workspace.open(path.join(workingDirectory, 'other.txt')).then (o) -> editor = o + runs -> + statusHandler = jasmine.createSpy('statusHandler') + atom.project.getRepositories()[0].onDidChangeStatus statusHandler + editor.getBuffer().reload() + expect(statusHandler.callCount).toBe 1 + expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256} + editor.getBuffer().reload() + expect(statusHandler.callCount).toBe 1 it "emits a status-changed event when a buffer's path changes", -> fs.writeFileSync(editor.getPath(), 'changed') From 952f4aae0e4f3be06b5896c06a22973dbb19e5c9 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 14:33:43 -0400 Subject: [PATCH 583/971] Revert "Skip the first reload event." This reverts commit 7294b95b70dd2b4be55a970e5e399eda5289b9fd. --- src/git-repository-async.js | 8 +------- src/git-repository.coffee | 9 +-------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index edd85390b..56b8ae117 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -1058,15 +1058,9 @@ export default class GitRepositoryAsync { } } - // TextBuffers will emit a reload event when they're first loaded. We don't - // need to refresh in that case. - let firstReload = true bufferSubscriptions.add( buffer.onDidSave(refreshStatusForBuffer), - buffer.onDidReload(() => { - if (!firstReload) refreshStatusForBuffer() - firstReload = false - }), + buffer.onDidReload(refreshStatusForBuffer), buffer.onDidChangePath(refreshStatusForBuffer), buffer.onDidDestroy(() => { bufferSubscriptions.dispose() diff --git a/src/git-repository.coffee b/src/git-repository.coffee index bc4f57161..0513c2293 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -448,14 +448,7 @@ class GitRepository bufferSubscriptions = new CompositeDisposable bufferSubscriptions.add buffer.onDidSave(getBufferPathStatus) - - # TextBuffers will emit a reload event when they're first loaded. We don't - # need to refresh in that case. - firstReload = true - bufferSubscriptions.add buffer.onDidReload(-> - getBufferPathStatus() unless firstReload - firstReload = false - ) + bufferSubscriptions.add buffer.onDidReload(getBufferPathStatus) bufferSubscriptions.add buffer.onDidChangePath(getBufferPathStatus) bufferSubscriptions.add buffer.onDidDestroy => bufferSubscriptions.dispose() From 1548ceff4add8e4ac6e87964046dc847436319d3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 25 Mar 2016 12:57:47 -0600 Subject: [PATCH 584/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 267aed7ed..5a8b87319 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.4.2", + "text-buffer": "8.4.3", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 2f58c404726e7e8949a8263be58f1f811ad1b4e4 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 15:12:06 -0400 Subject: [PATCH 585/971] Pass subscriptions through to the async layer. --- src/git-repository.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 0513c2293..87fa488da 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -136,7 +136,7 @@ class GitRepository # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidDestroy: (callback) -> - @emitter.on 'did-destroy', callback + @async.onDidDestroy callback ### Section: Event Subscription @@ -154,7 +154,7 @@ class GitRepository # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidChangeStatus: (callback) -> - @emitter.on 'did-change-status', callback + @async.onDidChangeStatus callback # Public: Invoke the given callback when a multiple files' statuses have # changed. For example, on window focus, the status of all the paths in the @@ -165,7 +165,7 @@ class GitRepository # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidChangeStatuses: (callback) -> - @emitter.on 'did-change-statuses', callback + @async.onDidChangeStatuses callback ### Section: Repository Details From 3aae2054f7a88bc167384afe687a72013a4dc5dd Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 15:14:12 -0400 Subject: [PATCH 586/971] Grab status from the underlying async layer. --- src/git-repository.coffee | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 87fa488da..6547557d8 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -83,7 +83,6 @@ class GitRepository asyncOptions.subscribeToBuffers = false @async = GitRepositoryAsync.open(path, asyncOptions) - @statuses = {} @upstream = {ahead: 0, behind: 0} for submodulePath, submoduleRepo of @repo.submodules submoduleRepo.upstream = {ahead: 0, behind: 0} @@ -317,7 +316,7 @@ class GitRepository getDirectoryStatus: (directoryPath) -> directoryPath = "#{@relativize(directoryPath)}/" directoryStatus = 0 - for path, status of @statuses + for path, status of @async.getCachedPathStatuses() directoryStatus |= status if path.indexOf(directoryPath) is 0 directoryStatus @@ -333,15 +332,8 @@ class GitRepository repo = @getRepo(path) relativePath = @relativize(path) - currentPathStatus = @statuses[relativePath] ? 0 pathStatus = repo.getStatus(repo.relativize(path)) ? 0 pathStatus = 0 if repo.isStatusIgnored(pathStatus) - if pathStatus > 0 - @statuses[relativePath] = pathStatus - else - delete @statuses[relativePath] - if currentPathStatus isnt pathStatus - @emitter.emit 'did-change-status', {path, pathStatus} pathStatus @@ -351,7 +343,7 @@ class GitRepository # # Returns a status {Number} or null if the path is not in the cache. getCachedPathStatus: (path) -> - @statuses[@relativize(path)] + @async.getCachedPathStatuses()[@relativize(path)] # Public: Returns true if the given status indicates modification. # From 4ecc6aac90961ab8bd3c8211c0acecdc6428f2ee Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 15:14:32 -0400 Subject: [PATCH 587/971] Grab the branch from the async layer. --- src/git-repository.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 6547557d8..0e1d3980a 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -470,7 +470,9 @@ class GitRepository # # Returns a promise that resolves when the repository has been refreshed. refreshStatus: -> - asyncRefresh = @async.refreshStatus() + asyncRefresh = @async.refreshStatus().then => + @branch = @async.branch + syncRefresh = new Promise (resolve, reject) => @handlerPath ?= require.resolve('./repository-status-handler') @@ -487,7 +489,6 @@ class GitRepository @statuses = statuses @upstream = upstream - @branch = branch @submodules = submodules for submodulePath, submoduleRepo of @getRepo().submodules From c9ff5db064a4150701530cb959a7b20864524a42 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 15:15:10 -0400 Subject: [PATCH 588/971] Don't update status anymore in the sync layer. --- src/git-repository.coffee | 12 +----------- src/repository-status-handler.coffee | 19 +------------------ 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 0e1d3980a..a40ee69d1 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -476,18 +476,8 @@ class GitRepository syncRefresh = new Promise (resolve, reject) => @handlerPath ?= require.resolve('./repository-status-handler') - relativeProjectPaths = @project?.getPaths() - .map (path) => @relativize(path) - .map (path) -> if path.length > 0 then path + '/**' else '*' - @statusTask?.terminate() - @statusTask = Task.once @handlerPath, @getPath(), relativeProjectPaths, ({statuses, upstream, branch, submodules}) => - statusesUnchanged = _.isEqual(statuses, @statuses) and - _.isEqual(upstream, @upstream) and - _.isEqual(branch, @branch) and - _.isEqual(submodules, @submodules) - - @statuses = statuses + @statusTask = Task.once @handlerPath, @getPath(), ({upstream, submodules}) => @upstream = upstream @submodules = submodules diff --git a/src/repository-status-handler.coffee b/src/repository-status-handler.coffee index 2fda9a335..adae7bc4f 100644 --- a/src/repository-status-handler.coffee +++ b/src/repository-status-handler.coffee @@ -5,32 +5,15 @@ module.exports = (repoPath, paths = []) -> repo = Git.open(repoPath) upstream = {} - statuses = {} submodules = {} - branch = null if repo? - # Statuses in main repo - workingDirectoryPath = repo.getWorkingDirectory() - repoStatus = (if paths.length > 0 then repo.getStatusForPaths(paths) else repo.getStatus()) - for filePath, status of repoStatus - statuses[filePath] = status - - # Statuses in submodules for submodulePath, submoduleRepo of repo.submodules submodules[submodulePath] = branch: submoduleRepo.getHead() upstream: submoduleRepo.getAheadBehindCount() - workingDirectoryPath = submoduleRepo.getWorkingDirectory() - for filePath, status of submoduleRepo.getStatus() - absolutePath = path.join(workingDirectoryPath, filePath) - # Make path relative to parent repository - relativePath = repo.relativize(absolutePath) - statuses[relativePath] = status - upstream = repo.getAheadBehindCount() - branch = repo.getHead() repo.release() - {statuses, upstream, branch, submodules} + {upstream, submodules} From 380df728084035b6a05a9ed5ef211b45f917306d Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 15:15:24 -0400 Subject: [PATCH 589/971] All emissions will propagate out from the async layer. --- src/git-repository.coffee | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index a40ee69d1..6ab6f3e85 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -1,7 +1,7 @@ {basename, join} = require 'path' _ = require 'underscore-plus' -{Emitter, Disposable, CompositeDisposable} = require 'event-kit' +{Disposable, CompositeDisposable} = require 'event-kit' fs = require 'fs-plus' GitRepositoryAsync = require './git-repository-async' GitUtils = require 'git-utils' @@ -69,7 +69,6 @@ class GitRepository null constructor: (path, options={}) -> - @emitter = new Emitter @subscriptions = new CompositeDisposable @repo = GitUtils.open(path) @@ -107,11 +106,6 @@ class GitRepository # This destroys any tasks and subscriptions and releases the underlying # libgit2 repository handle. This method is idempotent. destroy: -> - if @emitter? - @emitter.emit 'did-destroy' - @emitter.dispose() - @emitter = null - if @statusTask? @statusTask.terminate() @statusTask = null @@ -486,7 +480,4 @@ class GitRepository resolve() - unless statusesUnchanged - @emitter.emit 'did-change-statuses' - return Promise.all([asyncRefresh, syncRefresh]) From f001c832632793ce5a0b7f9f8a2e7271bb43b1c3 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 15:26:18 -0400 Subject: [PATCH 590/971] Bring back some synchronous event emitting. Preserve the previous behavior of emitting synchronously with observed change. --- src/git-repository.coffee | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 6ab6f3e85..793efd08d 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -1,7 +1,7 @@ {basename, join} = require 'path' _ = require 'underscore-plus' -{Disposable, CompositeDisposable} = require 'event-kit' +{Emitter, Disposable, CompositeDisposable} = require 'event-kit' fs = require 'fs-plus' GitRepositoryAsync = require './git-repository-async' GitUtils = require 'git-utils' @@ -69,6 +69,7 @@ class GitRepository null constructor: (path, options={}) -> + @emitter = new Emitter @subscriptions = new CompositeDisposable @repo = GitUtils.open(path) @@ -86,6 +87,8 @@ class GitRepository for submodulePath, submoduleRepo of @repo.submodules submoduleRepo.upstream = {ahead: 0, behind: 0} + @statusesByPath = {} + {@project, @config, refreshOnWindowFocus} = options refreshOnWindowFocus ?= true @@ -106,6 +109,11 @@ class GitRepository # This destroys any tasks and subscriptions and releases the underlying # libgit2 repository handle. This method is idempotent. destroy: -> + if @emitter? + @emitter.emit 'did-destroy' + @emitter.dispose() + @emitter = null + if @statusTask? @statusTask.terminate() @statusTask = null @@ -129,7 +137,7 @@ class GitRepository # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidDestroy: (callback) -> - @async.onDidDestroy callback + @emitter.on 'did-destroy', callback ### Section: Event Subscription @@ -147,7 +155,7 @@ class GitRepository # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidChangeStatus: (callback) -> - @async.onDidChangeStatus callback + @emitter.on 'did-change-status', callback # Public: Invoke the given callback when a multiple files' statuses have # changed. For example, on window focus, the status of all the paths in the @@ -321,13 +329,22 @@ class GitRepository # Returns a {Number} representing the status. This value can be passed to # {::isStatusModified} or {::isStatusNew} to get more information. getPathStatus: (path) -> + repo = @getRepo(path) + relativePath = @relativize(path) + currentPathStatus = @statusesByPath[relativePath] ? @async.getCachedPathStatuses()[relativePath] ? 0 + # Trigger events emitted on the async repo as well @async.refreshStatusForPath(path) - repo = @getRepo(path) - relativePath = @relativize(path) pathStatus = repo.getStatus(repo.relativize(path)) ? 0 pathStatus = 0 if repo.isStatusIgnored(pathStatus) + if pathStatus > 0 + @statusesByPath[relativePath] = pathStatus + else + delete @statusesByPath[relativePath] + + if currentPathStatus isnt pathStatus + @emitter.emit 'did-change-status', {path, pathStatus} pathStatus @@ -465,6 +482,7 @@ class GitRepository # Returns a promise that resolves when the repository has been refreshed. refreshStatus: -> asyncRefresh = @async.refreshStatus().then => + @statusesByPath = {} @branch = @async.branch syncRefresh = new Promise (resolve, reject) => From fff1e8f3d1a3ef4b99f0625ec24f803412e4d788 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 15:36:44 -0400 Subject: [PATCH 591/971] Cache the results of calling getPathStatus so we're consistent across calls. --- src/git-repository.coffee | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 793efd08d..27f7d7b7d 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -318,7 +318,7 @@ class GitRepository getDirectoryStatus: (directoryPath) -> directoryPath = "#{@relativize(directoryPath)}/" directoryStatus = 0 - for path, status of @async.getCachedPathStatuses() + for path, status of _.extend({}, @async.getCachedPathStatuses(), @statusesByPath) directoryStatus |= status if path.indexOf(directoryPath) is 0 directoryStatus @@ -331,7 +331,16 @@ class GitRepository getPathStatus: (path) -> repo = @getRepo(path) relativePath = @relativize(path) - currentPathStatus = @statusesByPath[relativePath] ? @async.getCachedPathStatuses()[relativePath] ? 0 + + # This is a bit particular. If a package calls `getPathStatus` like this: + # - change the file + # - getPathStatus + # - change the file + # - getPathStatus + # We need to preserve the guarantee that each call to `getPathStatus` will + # synchronously emit 'did-change-status'. So we need to keep a cache of the + # statuses found from this call. + currentPathStatus = @getCachedRelativePathStatus(relativePath) ? 0 # Trigger events emitted on the async repo as well @async.refreshStatusForPath(path) @@ -354,7 +363,11 @@ class GitRepository # # Returns a status {Number} or null if the path is not in the cache. getCachedPathStatus: (path) -> - @async.getCachedPathStatuses()[@relativize(path)] + relativePath = @relativize(path) + @getCachedRelativePathStatus(relativePath) + + getCachedRelativePathStatus: (relativePath) -> + @statusesByPath[relativePath] ? @async.getCachedPathStatuses()[relativePath] # Public: Returns true if the given status indicates modification. # From 6b26863012905187d199d34fb755c080c5384858 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 15:41:05 -0400 Subject: [PATCH 592/971] Revert "Fix the specs." This reverts commit 39dcd59994d11e1cefde849bb5080f47460eb69f. --- spec/git-repository-async-spec.js | 6 +++--- spec/git-spec.coffee | 22 +++++++++------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 25623ae27..900d81bfb 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -483,10 +483,9 @@ describe('GitRepositoryAsync', () => { describe('buffer events', () => { let repo - let workingDirectory beforeEach(() => { - workingDirectory = copyRepository() + const workingDirectory = copyRepository() atom.project.setPaths([workingDirectory]) // When the path is added to the project, the repository is refreshed. We @@ -513,9 +512,10 @@ describe('GitRepositoryAsync', () => { }) it('emits a status-changed event when a buffer is reloaded', async () => { - fs.writeFileSync(path.join(workingDirectory, 'other.txt'), 'changed') const editor = await atom.workspace.open('other.txt') + fs.writeFileSync(editor.getPath(), 'changed') + const statusHandler = jasmine.createSpy('statusHandler') repo.onDidChangeStatus(statusHandler) editor.getBuffer().reload() diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index db42f3fc8..3afd4da75 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -290,11 +290,10 @@ describe "GitRepository", -> expect(repo.isStatusNew(status)).toBe false describe "buffer events", -> - [editor, workingDirectory] = [] + [editor] = [] beforeEach -> - workingDirectory = copyRepository() - atom.project.setPaths([workingDirectory]) + atom.project.setPaths([copyRepository()]) waitsForPromise -> atom.workspace.open('other.txt').then (o) -> editor = o @@ -311,16 +310,13 @@ describe "GitRepository", -> it "emits a status-changed event when a buffer is reloaded", -> fs.writeFileSync(editor.getPath(), 'changed') - waitsForPromise -> - atom.workspace.open(path.join(workingDirectory, 'other.txt')).then (o) -> editor = o - runs -> - statusHandler = jasmine.createSpy('statusHandler') - atom.project.getRepositories()[0].onDidChangeStatus statusHandler - editor.getBuffer().reload() - expect(statusHandler.callCount).toBe 1 - expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256} - editor.getBuffer().reload() - expect(statusHandler.callCount).toBe 1 + statusHandler = jasmine.createSpy('statusHandler') + atom.project.getRepositories()[0].onDidChangeStatus statusHandler + editor.getBuffer().reload() + expect(statusHandler.callCount).toBe 1 + expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256} + editor.getBuffer().reload() + expect(statusHandler.callCount).toBe 1 it "emits a status-changed event when a buffer's path changes", -> fs.writeFileSync(editor.getPath(), 'changed') From 8ee1c3274ba9ee9f2ef9388ea0aa27ec5a9de4cd Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 17:13:06 -0400 Subject: [PATCH 593/971] Always update the cached status path. --- src/git-repository.coffee | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 27f7d7b7d..2821ea42f 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -347,10 +347,7 @@ class GitRepository pathStatus = repo.getStatus(repo.relativize(path)) ? 0 pathStatus = 0 if repo.isStatusIgnored(pathStatus) - if pathStatus > 0 - @statusesByPath[relativePath] = pathStatus - else - delete @statusesByPath[relativePath] + @statusesByPath[relativePath] = pathStatus if currentPathStatus isnt pathStatus @emitter.emit 'did-change-status', {path, pathStatus} From fdebbf12acd266600d6564eb2bdf1fa612e28643 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 25 Mar 2016 17:14:09 -0400 Subject: [PATCH 594/971] If we're been destroyed then we won't have an async anymore. --- src/git-repository.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 2821ea42f..30d99791d 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -493,7 +493,7 @@ class GitRepository refreshStatus: -> asyncRefresh = @async.refreshStatus().then => @statusesByPath = {} - @branch = @async.branch + @branch = @async?.branch syncRefresh = new Promise (resolve, reject) => @handlerPath ?= require.resolve('./repository-status-handler') From 5fc111a104e389959a94acd4cb6a31619e073814 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Sat, 26 Mar 2016 15:50:07 -0700 Subject: [PATCH 595/971] :memo: Add standard global notation --- src/notification-manager.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/notification-manager.coffee b/src/notification-manager.coffee index 46c781c20..3d8b1895c 100644 --- a/src/notification-manager.coffee +++ b/src/notification-manager.coffee @@ -3,6 +3,9 @@ Notification = require '../src/notification' # Public: A notification manager used to create {Notification}s to be shown # to the user. +# +# An instance of this class is always available as the `atom.notifications` +# global. module.exports = class NotificationManager constructor: -> From b5f866b6fc5f3110d263c2ae323d1588d84885e9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 28 Mar 2016 14:49:26 +0200 Subject: [PATCH 596/971] Correctly (un)subscribe to model events on PaneAxisElement attach/detach --- spec/pane-axis-element-spec.coffee | 34 +++++++++++++++++++++ spec/pane-container-element-spec.coffee | 6 ++++ src/pane-axis-element.coffee | 39 ++++++++++++++++--------- 3 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 spec/pane-axis-element-spec.coffee diff --git a/spec/pane-axis-element-spec.coffee b/spec/pane-axis-element-spec.coffee new file mode 100644 index 000000000..702e9c5fc --- /dev/null +++ b/spec/pane-axis-element-spec.coffee @@ -0,0 +1,34 @@ +PaneAxis = require '../src/pane-axis' +PaneContainer = require '../src/pane-container' +Pane = require '../src/pane' + +buildPane = -> + new Pane({ + applicationDelegate: atom.applicationDelegate, + config: atom.config, + deserializerManager: atom.deserializers, + notificationManager: atom.notifications + }) + +describe "PaneAxisElement", -> + it "correctly subscribes and unsubscribes to the underlying model events on attach/detach", -> + container = new PaneContainer(config: atom.config, applicationDelegate: atom.applicationDelegate) + axis = new PaneAxis + axis.setContainer(container) + axisElement = atom.views.getView(axis) + + panes = [buildPane(), buildPane(), buildPane()] + + jasmine.attachToDOM(axisElement) + axis.addChild(panes[0]) + expect(axisElement.children[0]).toBe(atom.views.getView(panes[0])) + + axisElement.remove() + axis.addChild(panes[1]) + expect(axisElement.children[2]).toBeUndefined() + + jasmine.attachToDOM(axisElement) + expect(axisElement.children[2]).toBe(atom.views.getView(panes[1])) + + axis.addChild(panes[2]) + expect(axisElement.children[4]).toBe(atom.views.getView(panes[2])) diff --git a/spec/pane-container-element-spec.coffee b/spec/pane-container-element-spec.coffee index fe57e89af..f56d12fe6 100644 --- a/spec/pane-container-element-spec.coffee +++ b/spec/pane-container-element-spec.coffee @@ -10,6 +10,7 @@ describe "PaneContainerElement", -> paneAxis = new PaneAxis paneAxisElement = new PaneAxisElement().initialize(paneAxis, atom) + jasmine.attachToDOM(paneAxisElement) expect(childTagNames()).toEqual [] @@ -41,6 +42,8 @@ describe "PaneContainerElement", -> 'atom-pane-axis' ] + paneAxisElement.remove() + it "transfers focus to the next pane if a focused pane is removed", -> container = new PaneContainer(config: atom.config, confirm: atom.confirm.bind(atom)) containerElement = atom.views.getView(container) @@ -60,6 +63,7 @@ describe "PaneContainerElement", -> it "builds appropriately-oriented atom-pane-axis elements", -> container = new PaneContainer(config: atom.config, confirm: atom.confirm.bind(atom)) containerElement = atom.views.getView(container) + jasmine.attachToDOM(containerElement) pane1 = container.getActivePane() pane2 = pane1.splitRight() @@ -80,6 +84,8 @@ describe "PaneContainerElement", -> expect(verticalPanes[0]).toBe atom.views.getView(pane2) expect(verticalPanes[1]).toBe atom.views.getView(pane3) + containerElement.remove() + describe "when the resize element is dragged ", -> [container, containerElement] = [] diff --git a/src/pane-axis-element.coffee b/src/pane-axis-element.coffee index eaa26a9fe..363f895c6 100644 --- a/src/pane-axis-element.coffee +++ b/src/pane-axis-element.coffee @@ -2,20 +2,8 @@ PaneResizeHandleElement = require './pane-resize-handle-element' class PaneAxisElement extends HTMLElement - createdCallback: -> - @subscriptions = new CompositeDisposable - - detachedCallback: -> - @subscriptions.dispose() - - initialize: (@model, {@views}) -> - throw new Error("Must pass a views parameter when initializing TextEditorElements") unless @views? - - @subscriptions.add @model.onDidAddChild(@childAdded.bind(this)) - @subscriptions.add @model.onDidRemoveChild(@childRemoved.bind(this)) - @subscriptions.add @model.onDidReplaceChild(@childReplaced.bind(this)) - @subscriptions.add @model.observeFlexScale(@flexScaleChanged.bind(this)) - + attachedCallback: -> + @subscriptions ?= @subscribeToModel() @childAdded({child, index}) for child, index in @model.getChildren() switch @model.getOrientation() @@ -23,8 +11,31 @@ class PaneAxisElement extends HTMLElement @classList.add('horizontal', 'pane-row') when 'vertical' @classList.add('vertical', 'pane-column') + + detachedCallback: -> + @subscriptions.dispose() + @subscriptions = null + @childRemoved({child}) for child in @model.getChildren() + + switch @model.getOrientation() + when 'horizontal' + @classList.remove('horizontal', 'pane-row') + when 'vertical' + @classList.remove('vertical', 'pane-column') + + initialize: (@model, {@views}) -> + throw new Error("Must pass a views parameter when initializing TextEditorElements") unless @views? + this + subscribeToModel: -> + subscriptions = new CompositeDisposable + subscriptions.add @model.onDidAddChild(@childAdded.bind(this)) + subscriptions.add @model.onDidRemoveChild(@childRemoved.bind(this)) + subscriptions.add @model.onDidReplaceChild(@childReplaced.bind(this)) + subscriptions.add @model.observeFlexScale(@flexScaleChanged.bind(this)) + subscriptions + isPaneResizeHandleElement: (element) -> element?.nodeName.toLowerCase() is 'atom-pane-resize-handle' From 3d4611ce4ca8597cd10c8700d22248cd25590e44 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 28 Mar 2016 14:52:40 +0200 Subject: [PATCH 597/971] Remove conditional assignment HTML nodes are guaranteed to be detached before being re-attached, so we are always sure that `subscriptions` is null on attach and non-null on detach. --- src/pane-axis-element.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane-axis-element.coffee b/src/pane-axis-element.coffee index 363f895c6..ee8019bb1 100644 --- a/src/pane-axis-element.coffee +++ b/src/pane-axis-element.coffee @@ -3,7 +3,7 @@ PaneResizeHandleElement = require './pane-resize-handle-element' class PaneAxisElement extends HTMLElement attachedCallback: -> - @subscriptions ?= @subscribeToModel() + @subscriptions = @subscribeToModel() @childAdded({child, index}) for child, index in @model.getChildren() switch @model.getOrientation() From 8adf47b1c2fb902ab2f60b0feaf6790680c37b70 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 28 Mar 2016 16:40:39 +0200 Subject: [PATCH 598/971] Allow element to work without attaching it to DOM --- spec/pane-container-element-spec.coffee | 6 ------ src/pane-axis-element.coffee | 27 ++++++++++--------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/spec/pane-container-element-spec.coffee b/spec/pane-container-element-spec.coffee index f56d12fe6..fe57e89af 100644 --- a/spec/pane-container-element-spec.coffee +++ b/spec/pane-container-element-spec.coffee @@ -10,7 +10,6 @@ describe "PaneContainerElement", -> paneAxis = new PaneAxis paneAxisElement = new PaneAxisElement().initialize(paneAxis, atom) - jasmine.attachToDOM(paneAxisElement) expect(childTagNames()).toEqual [] @@ -42,8 +41,6 @@ describe "PaneContainerElement", -> 'atom-pane-axis' ] - paneAxisElement.remove() - it "transfers focus to the next pane if a focused pane is removed", -> container = new PaneContainer(config: atom.config, confirm: atom.confirm.bind(atom)) containerElement = atom.views.getView(container) @@ -63,7 +60,6 @@ describe "PaneContainerElement", -> it "builds appropriately-oriented atom-pane-axis elements", -> container = new PaneContainer(config: atom.config, confirm: atom.confirm.bind(atom)) containerElement = atom.views.getView(container) - jasmine.attachToDOM(containerElement) pane1 = container.getActivePane() pane2 = pane1.splitRight() @@ -84,8 +80,6 @@ describe "PaneContainerElement", -> expect(verticalPanes[0]).toBe atom.views.getView(pane2) expect(verticalPanes[1]).toBe atom.views.getView(pane3) - containerElement.remove() - describe "when the resize element is dragged ", -> [container, containerElement] = [] diff --git a/src/pane-axis-element.coffee b/src/pane-axis-element.coffee index ee8019bb1..07439b914 100644 --- a/src/pane-axis-element.coffee +++ b/src/pane-axis-element.coffee @@ -3,7 +3,17 @@ PaneResizeHandleElement = require './pane-resize-handle-element' class PaneAxisElement extends HTMLElement attachedCallback: -> - @subscriptions = @subscribeToModel() + @subscriptions ?= @subscribeToModel() + @childAdded({child, index}) for child, index in @model.getChildren() + + detachedCallback: -> + @subscriptions.dispose() + @subscriptions = null + @childRemoved({child}) for child in @model.getChildren() + + initialize: (@model, {@views}) -> + throw new Error("Must pass a views parameter when initializing TextEditorElements") unless @views? + @subscriptions ?= @subscribeToModel() @childAdded({child, index}) for child, index in @model.getChildren() switch @model.getOrientation() @@ -11,21 +21,6 @@ class PaneAxisElement extends HTMLElement @classList.add('horizontal', 'pane-row') when 'vertical' @classList.add('vertical', 'pane-column') - - detachedCallback: -> - @subscriptions.dispose() - @subscriptions = null - @childRemoved({child}) for child in @model.getChildren() - - switch @model.getOrientation() - when 'horizontal' - @classList.remove('horizontal', 'pane-row') - when 'vertical' - @classList.remove('vertical', 'pane-column') - - initialize: (@model, {@views}) -> - throw new Error("Must pass a views parameter when initializing TextEditorElements") unless @views? - this subscribeToModel: -> From 22665c24d1fd87e050b61d994b145941b79c2183 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 28 Mar 2016 13:38:25 -0400 Subject: [PATCH 599/971] :arrow_up: tree-view@0.203.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5a8b87319..c0d50aba4 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.112.0", "tabs": "0.92.0", "timecop": "0.33.1", - "tree-view": "0.203.2", + "tree-view": "0.203.3", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From 51831b332d3296db2c8b31bc0a1d8f01b9e0b4e7 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 28 Mar 2016 14:50:04 -0400 Subject: [PATCH 600/971] Take the submodule into account in more instances. Fixes https://github.com/atom/git-diff/issues/97. --- src/git-repository-async.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 82546b525..fe81890d3 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -596,7 +596,7 @@ export default class GitRepositoryAsync { // * `added` The {Number} of added lines. // * `deleted` The {Number} of deleted lines. getDiffStats (_path) { - return this.getRepo() + return this.getRepo(_path) .then(repo => Promise.all([repo, repo.getHeadCommit()])) .then(([repo, headCommit]) => Promise.all([repo, headCommit.getTree()])) .then(([repo, tree]) => { @@ -640,7 +640,7 @@ export default class GitRepositoryAsync { // * `newLines` The {Number} of lines in the new hunk getLineDiffs (_path, text) { let relativePath = null - return this.getRepo() + return this.getRepo(_path) .then(repo => { relativePath = this.relativize(_path, repo.workdir()) return repo.getHeadCommit() @@ -678,7 +678,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} that resolves or rejects depending on whether the // method was successful. checkoutHead (_path) { - return this.getRepo() + return this.getRepo(_path) .then(repo => { const checkoutOptions = new Git.CheckoutOptions() checkoutOptions.paths = [this.relativize(_path, repo.workdir())] From 5b722fe0b742c582be52a5a10db29b65bc285d23 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 28 Mar 2016 14:54:27 -0400 Subject: [PATCH 601/971] :arrow_up: nodegit@0.12.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c0d50aba4..57374cc62 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "less-cache": "0.23", "line-top-index": "0.2.0", "marked": "^0.3.4", - "nodegit": "0.11.9", + "nodegit": "0.12.0", "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "^5", From 76f9f43e6aec3d53b1b23b893648eac238090a60 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 22 Mar 2016 17:26:30 -0700 Subject: [PATCH 602/971] Fix clean command to actually work when paths missing --- script/clean | 50 +++++++++++++++++++++----------------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/script/clean b/script/clean index 0c947baf2..cc4933f95 100755 --- a/script/clean +++ b/script/clean @@ -1,11 +1,10 @@ #!/usr/bin/env node -var cp = require('./utils/child-process-wrapper.js'); +var childProcess = require('./utils/child-process-wrapper.js'); var fs = require('fs'); var path = require('path'); var os = require('os'); var isWindows = process.platform === 'win32'; -var removeCommand = isWindows ? 'rmdir /S /Q ' : 'rm -rf '; var productName = require('../package.json').productName; process.chdir(path.dirname(__dirname)); @@ -13,10 +12,10 @@ var home = process.env[isWindows ? 'USERPROFILE' : 'HOME']; var tmpdir = os.tmpdir(); // Windows: Use START as a way to ignore error if Atom.exe isnt running -var killatom = isWindows ? 'START taskkill /F /IM ' + productName + '.exe' : 'pkill -9 ' + productName + ' || true'; +var killAtomCommand = isWindows ? 'START taskkill /F /IM ' + productName + '.exe' : 'pkill -9 ' + productName + ' || true'; +//childProcess.safeExec(killAtomCommand); -var commands = [ - killatom, +var pathsToRemove = [ [__dirname, '..', 'node_modules'], [__dirname, '..', 'build', 'node_modules'], [__dirname, '..', 'apm', 'node_modules'], @@ -32,37 +31,30 @@ var commands = [ [home, '.atom', 'electron'], [tmpdir, 'atom-build'], [tmpdir, 'atom-cached-atom-shells'], -]; -var run = function() { - var next = commands.shift(); - if (!next) - process.exit(0); +].map(function(pathSegments) { + return path.resolve.apply(null, pathSegments); +}); - if (Array.isArray(next)) { - var pathToRemove = path.resolve.apply(path.resolve, next); - if (fs.existsSync(pathToRemove)) { - if (isWindows) { - removeFolderRecursive(pathToRemove); - } else { - next = removeCommand + pathToRemove; - cp.safeExec(next, run); - } - } - else { - return run(); - } +pathsToRemove.forEach(function(pathToRemove) { + if (fs.existsSync(pathToRemove)) { + removePath(pathToRemove); } - else - cp.safeExec(next, run); -}; -run(); +}); + +function removePath(pathToRemove) { + if (isWindows) { + removePathOnWindows(pathToRemove); + } else { + childProcess.safeExec('rm -rf ' + pathToRemove); + } +} // Windows has a 260-char path limit for rmdir etc. Just recursively delete in Node. -var removeFolderRecursive = function(folderPath) { +function removePathOnWindows(folderPath) { fs.readdirSync(folderPath).forEach(function(entry, index) { var entryPath = path.join(folderPath, entry); if (fs.lstatSync(entryPath).isDirectory()) { - removeFolderRecursive(entryPath); + removePathOnWindows(entryPath); } else { fs.unlinkSync(entryPath); } From df4e552596663b066927dd88db06a801f96c4185 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 22 Mar 2016 17:26:41 -0700 Subject: [PATCH 603/971] Exclude PATH entries with msbuild.exe to fix node-gyp on Windows --- script/build | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/script/build b/script/build index a40a02e13..ca5569d0f 100755 --- a/script/build +++ b/script/build @@ -2,9 +2,24 @@ var cp = require('./utils/child-process-wrapper.js'); var runGrunt = require('./utils/run-grunt.js'); var path = require('path'); +var fs = require('fs'); process.chdir(path.dirname(__dirname)); +if (process.platform === 'win32') { + process.env['PATH'] = process.env['PATH'] + .split(';') + .filter(function(p) { + if (fs.existsSync(path.resolve(p, 'msbuild.exe'))) { + console.log('Excluding "' + p + '" from PATH to avoid msbuild.exe mismatch') + return false; + } else { + return true; + } + }) + .join(';'); +} + cp.safeExec('node script/bootstrap', function() { // build/node_modules/.bin/grunt "$@" var args = process.argv.slice(2); From 8d2de5fe73e95f216a28c46f16cefc1a88c2b446 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 22 Mar 2016 18:10:12 -0700 Subject: [PATCH 604/971] :art: --- build/tasks/codesign-task.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/tasks/codesign-task.coffee b/build/tasks/codesign-task.coffee index 6c99795c0..2a061742b 100644 --- a/build/tasks/codesign-task.coffee +++ b/build/tasks/codesign-task.coffee @@ -3,7 +3,7 @@ path = require 'path' module.exports = (grunt) -> {spawn} = require('./task-helpers')(grunt) - grunt.registerTask 'codesign:exe', 'Codesign atom.exe and Update.exe', -> + grunt.registerTask 'codesign:exe', 'CodeSign Atom.exe and Update.exe', -> done = @async() spawn {cmd: 'taskkill', args: ['/F', '/IM', 'atom.exe']}, -> cmd = process.env.JANKY_SIGNTOOL ? 'signtool' @@ -14,13 +14,13 @@ module.exports = (grunt) -> updateExePath = path.resolve(__dirname, '..', 'node_modules', 'grunt-electron-installer', 'vendor', 'Update.exe') spawn {cmd, args: [updateExePath]}, (error) -> done(error) - grunt.registerTask 'codesign:installer', 'Codesign AtomSetup.exe', -> + grunt.registerTask 'codesign:installer', 'CodeSign AtomSetup.exe', -> done = @async() cmd = process.env.JANKY_SIGNTOOL ? 'signtool' atomSetupExePath = path.resolve(grunt.config.get('atom.buildDir'), 'installer', 'AtomSetup.exe') spawn {cmd, args: [atomSetupExePath]}, (error) -> done(error) - grunt.registerTask 'codesign:app', 'Codesign Atom.app', -> + grunt.registerTask 'codesign:app', 'CodeSign Atom.app', -> done = @async() unlockKeychain (error) -> From 36c6e892969d24312da0ac5381dd1c53dbe4a99f Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 28 Mar 2016 18:49:11 -0400 Subject: [PATCH 605/971] :arrow_up: language-c@0.51.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 57374cc62..5c6d08e66 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "welcome": "0.34.0", "whitespace": "0.32.2", "wrap-guide": "0.38.1", - "language-c": "0.51.1", + "language-c": "0.51.2", "language-clojure": "0.20.0", "language-coffee-script": "0.46.1", "language-csharp": "0.12.0", From e547cac79587ad095fba71ab9b98d2cadbc61911 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 28 Mar 2016 18:49:29 -0400 Subject: [PATCH 606/971] :arrow_up: language-less@0.29.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5c6d08e66..0234a4196 100644 --- a/package.json +++ b/package.json @@ -130,7 +130,7 @@ "language-java": "0.17.0", "language-javascript": "0.110.0", "language-json": "0.17.6", - "language-less": "0.29.0", + "language-less": "0.29.1", "language-make": "0.21.0", "language-mustache": "0.13.0", "language-objective-c": "0.15.1", From 9063173ba30ba3dcab3af773b32530cf524e2be6 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 28 Mar 2016 18:49:47 -0400 Subject: [PATCH 607/971] :arrow_up: language-python@0.43.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0234a4196..32d66ebf2 100644 --- a/package.json +++ b/package.json @@ -137,7 +137,7 @@ "language-perl": "0.32.0", "language-php": "0.37.0", "language-property-list": "0.8.0", - "language-python": "0.43.0", + "language-python": "0.43.1", "language-ruby": "0.68.3", "language-ruby-on-rails": "0.25.0", "language-sass": "0.46.0", From ce26b2f7d815327fbc2a96246561a6652e9a7e21 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 28 Mar 2016 18:50:07 -0400 Subject: [PATCH 608/971] :arrow_up: language-ruby@0.68.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 32d66ebf2..896cbe363 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ "language-php": "0.37.0", "language-property-list": "0.8.0", "language-python": "0.43.1", - "language-ruby": "0.68.3", + "language-ruby": "0.68.4", "language-ruby-on-rails": "0.25.0", "language-sass": "0.46.0", "language-shellscript": "0.21.1", From 435dcc7bdbea89e18020a90aac0bdcf1ed97e648 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 28 Mar 2016 21:15:16 -0400 Subject: [PATCH 609/971] :arrow_up: language-json@0.18.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 896cbe363..dbeb5c61c 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "language-hyperlink": "0.16.0", "language-java": "0.17.0", "language-javascript": "0.110.0", - "language-json": "0.17.6", + "language-json": "0.18.0", "language-less": "0.29.1", "language-make": "0.21.0", "language-mustache": "0.13.0", From 3b222dbee82a2d98351296bdde0d874c4ed14eb3 Mon Sep 17 00:00:00 2001 From: Machiste Quintana Date: Mon, 28 Mar 2016 23:26:28 -0700 Subject: [PATCH 610/971] Don't merge selections if at start or end of line --- spec/text-editor-spec.coffee | 44 ++++++++++++++++++++++++++++++++++++ src/text-editor.coffee | 28 +++++++++++------------ 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 261d311c1..4de7168b7 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -4565,7 +4565,9 @@ describe "TextEditor", -> it "moves one active selection on one line one column to the left", -> editor.setSelectedBufferRange [[0, 4], [0, 13]] expect(editor.getSelectedText()).toBe 'quicksort' + editor.moveSelectionLeft() + expect(editor.getSelectedText()).toBe 'quicksort' expect(editor.getSelectedBufferRange()).toEqual [[0, 3], [0, 12]] @@ -4575,7 +4577,9 @@ describe "TextEditor", -> expect(selections[0].getText()).toBe 'quicksort' expect(selections[1].getText()).toBe 'function' + editor.moveSelectionLeft() + expect(selections[0].getText()).toBe 'quicksort' expect(selections[1].getText()).toBe 'function' expect(editor.getSelectedBufferRanges()).toEqual [[[0, 3], [0, 12]], [[0, 15], [0, 23]]] @@ -4586,7 +4590,9 @@ describe "TextEditor", -> expect(selections[0].getText()).toBe 'quicksort' expect(selections[1].getText()).toBe 'sort' + editor.moveSelectionLeft() + expect(selections[0].getText()).toBe 'quicksort' expect(selections[1].getText()).toBe 'sort' expect(editor.getSelectedBufferRanges()).toEqual [[[0, 3], [0, 12]], [[1, 5], [1, 9]]] @@ -4598,17 +4604,35 @@ describe "TextEditor", -> expect(selections[0].getText()).toBe 'var' expect(selections[1].getText()).toBe ' v' + editor.moveSelectionLeft() editor.moveSelectionLeft() + expect(selections[0].getText()).toBe 'var' expect(selections[1].getText()).toBe ' v' expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]], [[1, 0], [1, 3]]] + describe "when multiple selections are active on one line", -> + it "does not change the selection", -> + editor.setSelectedBufferRanges([[[0, 0], [0, 3]], [[0, 4], [0, 13]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'var' + expect(selections[1].getText()).toBe 'quicksort' + + editor.moveSelectionLeft() + + expect(selections[0].getText()).toBe 'var' + expect(selections[1].getText()).toBe 'quicksort' + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]], [[0, 4], [0, 13]]] + describe ".moveSelectionRight()", -> it "moves one active selection on one line one column to the right", -> editor.setSelectedBufferRange [[0, 4], [0, 13]] expect(editor.getSelectedText()).toBe 'quicksort' + editor.moveSelectionRight() + expect(editor.getSelectedText()).toBe 'quicksort' expect(editor.getSelectedBufferRange()).toEqual [[0, 5], [0, 14]] @@ -4618,7 +4642,9 @@ describe "TextEditor", -> expect(selections[0].getText()).toBe 'quicksort' expect(selections[1].getText()).toBe 'function' + editor.moveSelectionRight() + expect(selections[0].getText()).toBe 'quicksort' expect(selections[1].getText()).toBe 'function' expect(editor.getSelectedBufferRanges()).toEqual [[[0, 5], [0, 14]], [[0, 17], [0, 25]]] @@ -4629,7 +4655,9 @@ describe "TextEditor", -> expect(selections[0].getText()).toBe 'quicksort' expect(selections[1].getText()).toBe 'sort' + editor.moveSelectionRight() + expect(selections[0].getText()).toBe 'quicksort' expect(selections[1].getText()).toBe 'sort' expect(editor.getSelectedBufferRanges()).toEqual [[[0, 5], [0, 14]], [[1, 7], [1, 11]]] @@ -4641,12 +4669,28 @@ describe "TextEditor", -> expect(selections[0].getText()).toBe 'items;' expect(selections[1].getText()).toBe 'shift();' + editor.moveSelectionRight() editor.moveSelectionRight() + expect(selections[0].getText()).toBe 'items;' expect(selections[1].getText()).toBe 'shift();' expect(editor.getSelectedBufferRanges()).toEqual [[[2, 34], [2, 40]], [[5, 22], [5, 30]]] + describe "when multiple selections are active on one line", -> + it "does not change the selection", -> + editor.setSelectedBufferRanges([[[2, 27], [2, 33]], [[2, 34], [2, 40]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'return' + expect(selections[1].getText()).toBe 'items;' + + editor.moveSelectionRight() + + expect(selections[0].getText()).toBe 'return' + expect(selections[1].getText()).toBe 'items;' + expect(editor.getSelectedBufferRanges()).toEqual [[[2, 27], [2, 33]], [[2, 34], [2, 40]]] + describe 'reading text', -> it '.lineTextForScreenRow(row)', -> editor.foldBufferRow(4) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 6f075b579..b6be33071 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1077,46 +1077,46 @@ class TextEditor extends Model # Move any active selections one column to the left. moveSelectionLeft: -> selections = @getSelectedBufferRanges() + noSelectionAtStartOfLine = selections.every((selection) -> + selection.start.column isnt 0 + ) translationDelta = [0, -1] translatedRanges = [] @transact => - for selection in selections - charToLeftOfSelection = new Range(selection.start.translate(translationDelta), selection.start) - - if charToLeftOfSelection.start.column < 0 - translatedRanges.push(selection) - else + if noSelectionAtStartOfLine + for selection in selections + charToLeftOfSelection = new Range(selection.start.translate(translationDelta), selection.start) charTextToLeftOfSelection = @buffer.getTextInRange(charToLeftOfSelection) @buffer.insert(selection.end, charTextToLeftOfSelection) @buffer.delete(charToLeftOfSelection) translatedRanges.push(selection.translate(translationDelta)) - @setSelectedBufferRanges(translatedRanges) + @setSelectedBufferRanges(translatedRanges) # Move any active selections one column to the right. moveSelectionRight: -> selections = @getSelectedBufferRanges() + noSelectionAtEndOfLine = selections.every((selection) => + selection.end.column isnt @buffer.lineLengthForRow(selection.end.row) + ) translationDelta = [0, 1] translatedRanges = [] @transact => - for selection in selections - charToRightOfSelection = new Range(selection.end, selection.end.translate(translationDelta)) - - if charToRightOfSelection.end.column > @buffer.lineLengthForRow(charToRightOfSelection.end.row) - translatedRanges.push(selection) - else + if noSelectionAtEndOfLine + for selection in selections + charToRightOfSelection = new Range(selection.end, selection.end.translate(translationDelta)) charTextToRightOfSelection = @buffer.getTextInRange(charToRightOfSelection) @buffer.delete(charToRightOfSelection) @buffer.insert(selection.start, charTextToRightOfSelection) translatedRanges.push(selection.translate(translationDelta)) - @setSelectedBufferRanges(translatedRanges) + @setSelectedBufferRanges(translatedRanges) # Duplicate the most recent cursor's current line. duplicateLines: -> From 4b3ca21e2909802b6ba86de40c0d56548f811793 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 29 Mar 2016 11:26:39 -0400 Subject: [PATCH 611/971] Cache the workdir per-repo. --- src/git-repository-async.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 931bbb949..17d293c14 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -43,7 +43,6 @@ export default class GitRepositoryAsync { this.emitter = new Emitter() this.subscriptions = new CompositeDisposable() this.pathStatusCache = {} - this.workdir = null this.path = null // NB: These needs to happen before the following .openRepository call. @@ -157,11 +156,11 @@ export default class GitRepositoryAsync { // directory path of the repository. getWorkingDirectory (_path) { return this.getRepo(_path).then(repo => { - if (!this.workdir) { - this.workdir = repo.workdir() + if (!repo.cachedWorkdir) { + repo.cachedWorkdir = repo.workdir() } - return this.workdir + return repo.cachedWorkdir }) } From f4b94fef30a952efda6997e28d4c53973196fd3d Mon Sep 17 00:00:00 2001 From: Jordan Tucker Date: Tue, 29 Mar 2016 08:30:18 -0700 Subject: [PATCH 612/971] always save state on close, check config on open --- src/browser/atom-application.coffee | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index d057aef52..eea2fdc57 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -506,18 +506,17 @@ class AtomApplication saveState: (allowEmpty=false) -> return if @quitting - restorePreviousState = @config.get('core.restorePreviousWindowsOnStart') ? true states = [] - if restorePreviousState - for window in @windows - unless window.isSpec - if loadSettings = window.getLoadSettings() - states.push(initialPaths: loadSettings.initialPaths) - if states.length > 0 or allowEmpty or not restorePreviousState + for window in @windows + unless window.isSpec + if loadSettings = window.getLoadSettings() + states.push(initialPaths: loadSettings.initialPaths) + if states.length > 0 or allowEmpty @storageFolder.storeSync('application.json', states) loadState: (options) -> - if (states = @storageFolder.load('application.json'))?.length > 0 + restorePreviousState = @config.get('core.restorePreviousWindowsOnStart') ? true + if (states = @storageFolder.load('application.json'))?.length > 0 and restorePreviousState for state in states @openWithOptions(_.extend(options, { initialPaths: state.initialPaths From edcceeed5bd845bbcaa8a4a0bd838802eb78e6e0 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Tue, 29 Mar 2016 14:30:07 -0700 Subject: [PATCH 613/971] :arrow_up: apm (cherry picked from commit c6319ea60284fb8fe65f6cbd10873d59cfe697cf) --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index ba3415e1d..6623876f9 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "1.9.1" + "atom-package-manager": "1.9.2" } } From 265aa2f6c8ba1040858fa19daa498eb0b67db3cd Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 29 Mar 2016 16:46:57 -0600 Subject: [PATCH 614/971] Warn rather than failing if we detect leaked pathwatcher subscriptions --- spec/git-repository-async-spec.js | 4 +--- spec/spec-helper.coffee | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 900d81bfb..0442248e1 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -230,9 +230,7 @@ describe('GitRepositoryAsync', () => { }) }) - // @joshaber: Disabling for now. There seems to be some race with path - // subscriptions leading to intermittent test failures, e.g.: https://travis-ci.org/atom/atom/jobs/102702554 - xdescribe('.checkoutHeadForEditor(editor)', () => { + describe('.checkoutHeadForEditor(editor)', () => { let filePath let editor diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index 758a232aa..1194f2f76 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -112,14 +112,14 @@ afterEach -> document.getElementById('jasmine-content').innerHTML = '' unless window.debugContent - ensureNoPathSubscriptions() + warnIfLeakingPathSubscriptions() waits(0) # yield to ui thread to make screen update more frequently -ensureNoPathSubscriptions = -> +warnIfLeakingPathSubscriptions = -> watchedPaths = pathwatcher.getWatchedPaths() - pathwatcher.closeAllWatchers() if watchedPaths.length > 0 - throw new Error("Leaking subscriptions for paths: " + watchedPaths.join(", ")) + console.error("WARNING: Leaking subscriptions for paths: " + watchedPaths.join(", ")) + pathwatcher.closeAllWatchers() ensureNoDeprecatedFunctionsCalled = -> deprecations = Grim.getDeprecations() From 36121e3bf87f895e7c0846e8c92af1efe2f02f14 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 29 Mar 2016 22:59:24 -0400 Subject: [PATCH 615/971] First pass at the git work queue. --- spec/git-work-queue-spec.js | 68 +++++++++++++++++++++++++++++++++++++ src/git-repository-async.js | 6 +++- src/git-work-queue.js | 61 +++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 spec/git-work-queue-spec.js create mode 100644 src/git-work-queue.js diff --git a/spec/git-work-queue-spec.js b/spec/git-work-queue-spec.js new file mode 100644 index 000000000..18ce63f0b --- /dev/null +++ b/spec/git-work-queue-spec.js @@ -0,0 +1,68 @@ +/** @babel */ + +import GitWorkQueue from '../src/git-work-queue' + +import {it} from './async-spec-helpers' + +fdescribe('GitWorkQueue', () => { + let queue + + beforeEach(() => { + queue = new GitWorkQueue() + }) + + describe('.enqueue', () => { + it('calls the enqueued function', async () => { + let called = false + await queue.enqueue(() => { + called = true + return Promise.resolve() + }) + expect(called).toBe(true) + }) + + it('forwards values from the inner promise', async () => { + const result = await queue.enqueue(() => Promise.resolve(42)) + expect(result).toBe(42) + }) + + it('forwards errors from the inner promise', async () => { + let threw = false + try { + await queue.enqueue(() => Promise.reject(new Error('down with the sickness'))) + } catch (e) { + threw = true + } + expect(threw).toBe(true) + }) + + it('continues to dequeue work after a promise has been rejected', async () => { + try { + await queue.enqueue(() => Promise.reject(new Error('down with the sickness'))) + } catch (e) {} + + const result = await queue.enqueue(() => Promise.resolve(42)) + expect(result).toBe(42) + }) + + it('queues up work', async () => { + let resolve = null + queue.enqueue(() => { + return new Promise((resolve_, reject) => { + resolve = resolve_ + }) + }) + + expect(queue.getQueueDepth()).toBe(0) + + queue.enqueue(() => { + return new Promise((resolve, reject) => {}) + }) + + expect(queue.getQueueDepth()).toBe(1) + resolve() + + waitsFor(() => queue.getQueueDepth() === 0) + }) + }) +}) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 17d293c14..c7f05e68d 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -3,6 +3,7 @@ import fs from 'fs-plus' import path from 'path' import Git from 'nodegit' +import GitWorkQueue from './git-work-queue' import {Emitter, CompositeDisposable, Disposable} from 'event-kit' const modifiedStatusFlags = Git.Status.STATUS.WT_MODIFIED | Git.Status.STATUS.INDEX_MODIFIED | Git.Status.STATUS.WT_DELETED | Git.Status.STATUS.INDEX_DELETED | Git.Status.STATUS.WT_TYPECHANGE | Git.Status.STATUS.INDEX_TYPECHANGE @@ -38,8 +39,10 @@ export default class GitRepositoryAsync { } constructor (_path, options = {}) { - Git.enableThreadSafety() + // We'll serialize our access manually. + Git.disableThreadSafety() + this.workQueue = new GitWorkQueue() this.emitter = new Emitter() this.subscriptions = new CompositeDisposable() this.pathStatusCache = {} @@ -81,6 +84,7 @@ export default class GitRepositoryAsync { this.emitter.dispose() this.emitter = null } + if (this.subscriptions) { this.subscriptions.dispose() this.subscriptions = null diff --git a/src/git-work-queue.js b/src/git-work-queue.js new file mode 100644 index 000000000..1d69e654a --- /dev/null +++ b/src/git-work-queue.js @@ -0,0 +1,61 @@ +/** @babel */ + +// A queue used to manage git work. +export default class GitWorkQueue { + constructor () { + this.queue = [] + this.working = false + } + + // Enqueue the given function. The function must return a {Promise} when + // called. + enqueue (fn) { + let resolve = null + let reject = null + const wrapperPromise = new Promise((resolve_, reject_) => { + resolve = resolve_ + reject = reject_ + }) + + this.queue.push(this.wrapFunction(fn, resolve, reject)) + + this.startNext() + + return wrapperPromise + } + + wrapFunction (fn, resolve, reject) { + return () => { + const promise = fn() + promise + .then(result => { + resolve(result) + this.taskDidComplete() + }, error => { + reject(error) + this.taskDidComplete() + }) + } + } + + taskDidComplete () { + this.working = false + + this.startNext() + } + + shouldStartNext () { + return !this.working && this.queue.length > 0 + } + + startNext () { + if (!this.shouldStartNext()) return + + this.working = true + + const fn = this.queue.shift() + fn() + } + + getQueueDepth () { return this.queue.length } +} From f1516f7de4a2a1bee64804ea0319c3d06fd6df3a Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 29 Mar 2016 23:54:39 -0400 Subject: [PATCH 616/971] First pass at using the work queue. --- spec/git-work-queue-spec.js | 4 +- src/git-repository-async.js | 128 ++++++++++++++++++++---------------- src/git-work-queue.js | 10 +-- 3 files changed, 79 insertions(+), 63 deletions(-) diff --git a/spec/git-work-queue-spec.js b/spec/git-work-queue-spec.js index 18ce63f0b..9d8e8cea0 100644 --- a/spec/git-work-queue-spec.js +++ b/spec/git-work-queue-spec.js @@ -55,9 +55,7 @@ fdescribe('GitWorkQueue', () => { expect(queue.getQueueDepth()).toBe(0) - queue.enqueue(() => { - return new Promise((resolve, reject) => {}) - }) + queue.enqueue(() => new Promise((resolve, reject) => {})) expect(queue.getQueueDepth()).toBe(1) resolve() diff --git a/src/git-repository-async.js b/src/git-repository-async.js index c7f05e68d..f7c4f1b5b 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -264,10 +264,12 @@ export default class GitRepositoryAsync { // Public: Returns a {Promise} which resolves to whether the given branch // exists. hasBranch (branch) { - return this.getRepo() - .then(repo => repo.getBranch(branch)) - .then(branch => branch != null) - .catch(_ => false) + return this.workQueue.enqueue(() => { + return this.getRepo() + .then(repo => repo.getBranch(branch)) + .then(branch => branch != null) + .catch(_ => false) + }) } // Public: Retrieves a shortened version of the HEAD reference value. @@ -281,9 +283,11 @@ export default class GitRepositoryAsync { // // Returns a {Promise} which resolves to a {String}. getShortHead (_path) { - return this.getRepo(_path) - .then(repo => repo.getCurrentBranch()) - .then(branch => branch.shorthand()) + return this.workQueue.enqueue(() => { + return this.getRepo(_path) + .then(repo => repo.getCurrentBranch()) + .then(branch => branch.shorthand()) + }) } // Public: Is the given path a submodule in the repository? @@ -315,16 +319,18 @@ export default class GitRepositoryAsync { // * `ahead` The {Number} of commits ahead. // * `behind` The {Number} of commits behind. getAheadBehindCount (reference, _path) { - return this.getRepo(_path) - .then(repo => Promise.all([repo, repo.getBranch(reference)])) - .then(([repo, local]) => { - const upstream = Git.Branch.upstream(local) - return Promise.all([repo, local, upstream]) - }) - .then(([repo, local, upstream]) => { - return Git.Graph.aheadBehind(repo, local.target(), upstream.target()) - }) - .catch(_ => ({ahead: 0, behind: 0})) + return this.workQueue.enqueue(() => { + return this.getRepo(_path) + .then(repo => Promise.all([repo, repo.getBranch(reference)])) + .then(([repo, local]) => { + const upstream = Git.Branch.upstream(local) + return Promise.all([repo, local, upstream]) + }) + .then(([repo, local, upstream]) => { + return Git.Graph.aheadBehind(repo, local.target(), upstream.target()) + }) + .catch(_ => ({ahead: 0, behind: 0})) + }) } // Public: Get the cached ahead/behind commit counts for the current branch's @@ -356,10 +362,12 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to the {String} git configuration value // specified by the key. getConfigValue (key, _path) { - return this.getRepo(_path) - .then(repo => repo.configSnapshot()) - .then(config => config.getStringBuf(key)) - .catch(_ => null) + return this.workQueue.enqueue(() => { + return this.getRepo(_path) + .then(repo => repo.configSnapshot()) + .then(config => config.getStringBuf(key)) + .catch(_ => null) + }) } // Public: Get the URL for the 'origin' remote. @@ -382,9 +390,11 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {String} branch name such as // `refs/remotes/origin/master`. getUpstreamBranch (_path) { - return this.getRepo(_path) - .then(repo => repo.getCurrentBranch()) - .then(branch => Git.Branch.upstream(branch)) + return this.workQueue.enqueue(() => { + return this.getRepo(_path) + .then(repo => repo.getCurrentBranch()) + .then(branch => Git.Branch.upstream(branch)) + }) } // Public: Gets all the local and remote references. @@ -397,23 +407,25 @@ export default class GitRepositoryAsync { // * `remotes` An {Array} of remote reference names. // * `tags` An {Array} of tag reference names. getReferences (_path) { - return this.getRepo(_path) - .then(repo => repo.getReferences(Git.Reference.TYPE.LISTALL)) - .then(refs => { - const heads = [] - const remotes = [] - const tags = [] - for (const ref of refs) { - if (ref.isTag()) { - tags.push(ref.name()) - } else if (ref.isRemote()) { - remotes.push(ref.name()) - } else if (ref.isBranch()) { - heads.push(ref.name()) + return this.workQueue.enqueue(() => { + return this.getRepo(_path) + .then(repo => repo.getReferences(Git.Reference.TYPE.LISTALL)) + .then(refs => { + const heads = [] + const remotes = [] + const tags = [] + for (const ref of refs) { + if (ref.isTag()) { + tags.push(ref.name()) + } else if (ref.isRemote()) { + remotes.push(ref.name()) + } else if (ref.isBranch()) { + heads.push(ref.name()) + } } - } - return {heads, remotes, tags} - }) + return {heads, remotes, tags} + }) + }) } // Public: Get the SHA for the given reference. @@ -425,9 +437,11 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to the current {String} SHA for the // given reference. getReferenceTarget (reference, _path) { - return this.getRepo(_path) - .then(repo => Git.Reference.nameToId(repo, reference)) - .then(oid => oid.tostrS()) + return this.workQueue.enqueue(() => { + return this.getRepo(_path) + .then(repo => Git.Reference.nameToId(repo, reference)) + .then(oid => oid.tostrS()) + }) } // Reading Status @@ -505,8 +519,8 @@ export default class GitRepositoryAsync { // status bit for the path. refreshStatusForPath (_path) { let relativePath - return Promise.all([this.getRepo(), this.getWorkingDirectory()]) - .then(([repo, wd]) => { + return this.getWorkingDirectory() + .then(wd => { relativePath = this.relativize(_path, wd) return this._getStatus([relativePath]) }) @@ -1081,18 +1095,20 @@ export default class GitRepositoryAsync { // // Returns a {Promise} which resolves to an {Array} of {NodeGit.StatusFile} // statuses for the paths. - _getStatus (paths, repo) { - return this.getRepo() - .then(repo => { - const opts = { - flags: Git.Status.OPT.INCLUDE_UNTRACKED | Git.Status.OPT.RECURSE_UNTRACKED_DIRS - } + _getStatus (paths) { + return this.workQueue.enqueue(() => { + return this.getRepo() + .then(repo => { + const opts = { + flags: Git.Status.OPT.INCLUDE_UNTRACKED | Git.Status.OPT.RECURSE_UNTRACKED_DIRS + } - if (paths) { - opts.pathspec = paths - } + if (paths) { + opts.pathspec = paths + } - return repo.getStatusExt(opts) - }) + return repo.getStatusExt(opts) + }) + }) } } diff --git a/src/git-work-queue.js b/src/git-work-queue.js index 1d69e654a..6e8c2af67 100644 --- a/src/git-work-queue.js +++ b/src/git-work-queue.js @@ -19,7 +19,9 @@ export default class GitWorkQueue { this.queue.push(this.wrapFunction(fn, resolve, reject)) - this.startNext() + if (this.shouldStartNext()) { + this.startNext() + } return wrapperPromise } @@ -41,7 +43,9 @@ export default class GitWorkQueue { taskDidComplete () { this.working = false - this.startNext() + if (this.shouldStartNext()) { + this.startNext() + } } shouldStartNext () { @@ -49,8 +53,6 @@ export default class GitWorkQueue { } startNext () { - if (!this.shouldStartNext()) return - this.working = true const fn = this.queue.shift() From fb2a08f1f730bfc193de0d7fbd9c5197a460d1bf Mon Sep 17 00:00:00 2001 From: simurai Date: Wed, 30 Mar 2016 14:38:44 +0900 Subject: [PATCH 617/971] :arrow_up: solarized-dark/light-syntax@v1.0.1 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index dbeb5c61c..5b314ffc1 100644 --- a/package.json +++ b/package.json @@ -70,8 +70,8 @@ "one-light-ui": "1.3.0", "one-dark-syntax": "1.2.0", "one-light-syntax": "1.2.0", - "solarized-dark-syntax": "1.0.0", - "solarized-light-syntax": "1.0.0", + "solarized-dark-syntax": "1.0.1", + "solarized-light-syntax": "1.0.1", "about": "1.4.2", "archive-view": "0.61.1", "autocomplete-atom-api": "0.10.0", From 1a124853f032624516540dd138b030f05d0f8b12 Mon Sep 17 00:00:00 2001 From: Christian Oliff Date: Wed, 30 Mar 2016 17:23:50 +0900 Subject: [PATCH 618/971] https link to nodejs.org https link to nodejs.org --- docs/build-instructions/os-x.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build-instructions/os-x.md b/docs/build-instructions/os-x.md index 76da080c9..54d0c2ff8 100644 --- a/docs/build-instructions/os-x.md +++ b/docs/build-instructions/os-x.md @@ -3,7 +3,7 @@ ## Requirements * OS X 10.8 or later - * [Node.js](http://nodejs.org/download/) (0.10.x or above) + * [Node.js](https://nodejs.org/download/) (0.10.x or above) * Command Line Tools for [Xcode](https://developer.apple.com/xcode/downloads/) (run `xcode-select --install` to install) ## Instructions From 0442aac9a3cd762f62680a49b7842df7b9d225aa Mon Sep 17 00:00:00 2001 From: Christian Oliff Date: Wed, 30 Mar 2016 17:25:59 +0900 Subject: [PATCH 619/971] https link to nodejs.org and visualstudio.com https links to nodejs.org and visualstudio.com --- docs/build-instructions/windows.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/build-instructions/windows.md b/docs/build-instructions/windows.md index fdd55fdde..db2fb56ee 100644 --- a/docs/build-instructions/windows.md +++ b/docs/build-instructions/windows.md @@ -3,7 +3,7 @@ ## Requirements ### General - * [Node.js](http://nodejs.org/en/download/) v4.x + * [Node.js](https://nodejs.org/en/download/) v4.x * [Python](https://www.python.org/downloads/) v2.7.x * The python.exe must be available at `%SystemDrive%\Python27\python.exe`. If it is installed elsewhere, you can create a symbolic link to the @@ -14,8 +14,8 @@ You can use either: - * [Visual Studio 2013 Update 5](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs) (Express or better) on Windows 7, 8 or 10 - * [Visual Studio 2015](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs) (Community or better) with Windows 8 or 10 + * [Visual Studio 2013 Update 5](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs) (Express or better) on Windows 7, 8 or 10 + * [Visual Studio 2015](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs) (Community or better) with Windows 8 or 10 Whichever version you use, ensure that: From 2d5e6970164ccfc71535a4bf3235ceac6ceabed3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 30 Mar 2016 15:05:57 +0200 Subject: [PATCH 620/971] Add AutoUpdateManager.prototype.onUpdateError --- spec/auto-update-manager-spec.js | 10 ++++++++++ src/application-delegate.coffee | 8 ++++++++ src/auto-update-manager.js | 7 +++++++ src/browser/auto-update-manager.coffee | 1 + 4 files changed, 26 insertions(+) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 6f7dbbb1a..12fe0c825 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -64,6 +64,16 @@ describe('AutoUpdateManager (renderer)', () => { }) }) + describe('::onUpdateError', () => { + it('subscribes to "update-error" event', () => { + const spy = jasmine.createSpy('spy') + autoUpdateManager.onUpdateError(spy) + electronAutoUpdater.emit('error', {}, 'an error') + waitsFor(() => spy.callCount === 1) + runs(() => expect(spy).toHaveBeenCalledWith('an error')) + }) + }) + describe('::platformSupportsUpdates', () => { let state, releaseChannel it('returns true on OS X and Windows when in stable', () => { diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index 3aff9e457..ecefc4b67 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -211,6 +211,14 @@ class ApplicationDelegate new Disposable -> ipcRenderer.removeListener('message', outerCallback) + onUpdateError: (callback) -> + outerCallback = (event, message, detail) -> + callback(detail) if message is 'update-error' + + ipcRenderer.on('message', outerCallback) + new Disposable -> + ipcRenderer.removeListener('message', outerCallback) + onApplicationMenuCommand: (callback) -> outerCallback = (event, args...) -> callback(args...) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 62cc03f85..07e0db4be 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -20,6 +20,9 @@ export default class AutoUpdateManager { }), applicationDelegate.onUpdateNotAvailable(() => { this.emitter.emit('update-not-available') + }), + applicationDelegate.onUpdateError((message) => { + this.emitter.emit('update-error', message) }) ) } @@ -67,6 +70,10 @@ export default class AutoUpdateManager { return this.emitter.on('update-not-available', callback) } + onUpdateError (callback) { + return this.emitter.on('update-error', callback) + } + getPlatform () { return process.platform } diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index c8c57cb01..391d1f0b4 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -33,6 +33,7 @@ class AutoUpdateManager autoUpdater.on 'error', (event, message) => @setState(ErrorState) + @emitWindowEvent('update-error', message) console.error "Error Downloading Update: #{message}" autoUpdater.setFeedURL @feedUrl From e8e2370ed102ae6994739b178744fcf37266b31d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 30 Mar 2016 15:06:26 +0200 Subject: [PATCH 621/971] Log an error message to console as well --- src/auto-update-manager.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 07e0db4be..a6cea92fe 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -22,6 +22,7 @@ export default class AutoUpdateManager { this.emitter.emit('update-not-available') }), applicationDelegate.onUpdateError((message) => { + console.error(message) this.emitter.emit('update-error', message) }) ) From cea3ac64a53e959b1b0796d93bb09fe528d15b8c Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 30 Mar 2016 09:37:38 -0400 Subject: [PATCH 622/971] :arrow_up: language-less@0.29.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5b314ffc1..438c7a22a 100644 --- a/package.json +++ b/package.json @@ -130,7 +130,7 @@ "language-java": "0.17.0", "language-javascript": "0.110.0", "language-json": "0.18.0", - "language-less": "0.29.1", + "language-less": "0.29.2", "language-make": "0.21.0", "language-mustache": "0.13.0", "language-objective-c": "0.15.1", From 1d4ba76756e74ab371b59921a4ab1f2f26a9b2ca Mon Sep 17 00:00:00 2001 From: Christian Oliff Date: Wed, 30 Mar 2016 22:53:04 +0900 Subject: [PATCH 623/971] Updated link to NodeJS with nicer landing page Updated link to NodeJS with nicer landing page --- docs/build-instructions/os-x.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build-instructions/os-x.md b/docs/build-instructions/os-x.md index 54d0c2ff8..d9e15808b 100644 --- a/docs/build-instructions/os-x.md +++ b/docs/build-instructions/os-x.md @@ -3,7 +3,7 @@ ## Requirements * OS X 10.8 or later - * [Node.js](https://nodejs.org/download/) (0.10.x or above) + * [Node.js](https://nodejs.org/en/download/) (0.10.x or above) * Command Line Tools for [Xcode](https://developer.apple.com/xcode/downloads/) (run `xcode-select --install` to install) ## Instructions From 2a6e4110944e91d5be165a8c79bf93a6934ef43f Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Wed, 30 Mar 2016 07:42:18 -0700 Subject: [PATCH 624/971] Add troubleshooting for no Visual C++ installed --- docs/build-instructions/windows.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/build-instructions/windows.md b/docs/build-instructions/windows.md index db2fb56ee..29f258d59 100644 --- a/docs/build-instructions/windows.md +++ b/docs/build-instructions/windows.md @@ -67,6 +67,9 @@ If none of this works, do install Github Desktop and use its Git Shell as it mak * If you just installed Node.js, you'll need to restart your PowerShell/Command Prompt/Git Shell before the node command is available on your Path. +* `msbuild.exe failed with exit code: 1` + * Ensure you have Visual C++ support installed. Go into Add/Remove Programs, select Visual Studio and press Modify and then check the Visual C++ box. + * `script/build` outputs only the Node.js and Python versions before returning * Try moving the repository to `C:\atom`. Most likely, the path is too long. From eb1afced744d1b68afb9640ea7f34e3d1f551c83 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Wed, 30 Mar 2016 08:05:39 -0700 Subject: [PATCH 625/971] More Windows build clarifications. --- docs/build-instructions/windows.md | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/docs/build-instructions/windows.md b/docs/build-instructions/windows.md index 29f258d59..0a999ee3b 100644 --- a/docs/build-instructions/windows.md +++ b/docs/build-instructions/windows.md @@ -21,8 +21,8 @@ Whichever version you use, ensure that: * The default installation folder is chosen so the build tools can find it * Visual C++ support is installed - * You set the `GYP_MSVS_VERSION` environment variable to the Visual Studio version (`2013` or `2015`), e.g. , e.g. ``[Environment]::SetEnvironmentVariable("GYP_MSVS_VERSION", "2015", "User")`` in PowerShell or set it in Windows advanced system settings control panel. - * The git command is in your path + * A `git` command is in your path + * If you have both VS2013 and VS2015 installed set the `GYP_MSVS_VERSION` environment variable to the Visual Studio version (`2013` or `2015`) you wish to use, e.g. ``[Environment]::SetEnvironmentVariable("GYP_MSVS_VERSION", "2015", "User")`` in PowerShell or set it in Windows advanced system settings control panel. ## Instructions @@ -63,7 +63,6 @@ If none of this works, do install Github Desktop and use its Git Shell as it mak ### Common Errors * `node is not recognized` - * If you just installed Node.js, you'll need to restart your PowerShell/Command Prompt/Git Shell before the node command is available on your Path. @@ -71,33 +70,25 @@ If none of this works, do install Github Desktop and use its Git Shell as it mak * Ensure you have Visual C++ support installed. Go into Add/Remove Programs, select Visual Studio and press Modify and then check the Visual C++ box. * `script/build` outputs only the Node.js and Python versions before returning - * Try moving the repository to `C:\atom`. Most likely, the path is too long. See [issue #2200](https://github.com/atom/atom/issues/2200). * `error MSB4025: The project file could not be loaded. Invalid character in the given encoding.` - * This can occur because your home directory (`%USERPROFILE%`) has non-ASCII characters in it. This is a bug in [gyp](https://code.google.com/p/gyp/) which is used to build native Node.js modules and there is no known workaround. * https://github.com/TooTallNate/node-gyp/issues/297 * https://code.google.com/p/gyp/issues/detail?id=393 -* `script/build` stops at installing runas with `Failed at the runas@x.y.z install script.` +* `'node_modules\.bin\npm' is not recognized as an internal or external command, operable program or batch file.` + * This occurs if the previous build left things in a bad state. Run `script\clean` and then `script\build` again. +* `script/build` stops at installing runas with `Failed at the runas@x.y.z install script.` * See the next item. * `error MSB8020: The build tools for Visual Studio 201? (Platform Toolset = 'v1?0') cannot be found.` - - * If you're building Atom with Visual Studio 2013 or above make sure the `GYP_MSVS_VERSION` environment variable is set, and then re-run `script/build` after a clean: - - ```bash - $env:GYP_MSVS_VERSION='2013' # '2015' if using Visual Studio 2015, and so on - script/clean - script/build - ``` - * If you are using Visual Studio 2013 or above and the build fails with some other error message this environment variable might still be required and ensure you have Visual C++ language support installed. - + * If you're building Atom with Visual Studio 2013 try setting the `GYP_MSVS_VERSION` environment variable to 2013 and then `script/clean` followed by `script/build` (re-open your command prompt or Powershell window if you set it using the GUI) + * * Other `node-gyp` errors on first build attempt, even though the right Node.js and Python versions are installed. * Do try the build command one more time, as experience shows it often works on second try in many of these cases. From c0e9fde59066d1eb9ded8f2a9e3b2745daa6b140 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 30 Mar 2016 11:17:58 -0400 Subject: [PATCH 626/971] Re-organize to prevent recursive work queueing. --- src/git-repository-async.js | 204 +++++++++++++++++++++--------------- 1 file changed, 117 insertions(+), 87 deletions(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index f7c4f1b5b..29788da08 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -297,14 +297,18 @@ export default class GitRepositoryAsync { // Returns a {Promise} that resolves true if the given path is a submodule in // the repository. isSubmodule (_path) { - return this.getRepo() - .then(repo => repo.openIndex()) - .then(index => Promise.all([index, this.relativizeToWorkingDirectory(_path)])) - .then(([index, relativePath]) => { - const entry = index.getByPath(relativePath) - if (!entry) return false + return this.relativizeToWorkingDirectory(_path) + .then(relativePath => { + return this.workQueue.enqueue(() => { + return this.getRepo() + .then(repo => repo.openIndex()) + .then(index => { + const entry = index.getByPath(relativePath) + if (!entry) return false - return entry.mode === submoduleMode + return entry.mode === submoduleMode + }) + }) }) } @@ -478,12 +482,17 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {Boolean} that's true if the `path` // is ignored. isPathIgnored (_path) { - return Promise.all([this.getRepo(), this.getWorkingDirectory()]) - .then(([repo, wd]) => { - const relativePath = this.relativize(_path, wd) - return Git.Ignore.pathIsIgnored(repo, relativePath) + return this.getWorkingDirectory() + .then(wd => { + return this.workQueue.enqueue(() => { + return this.getRepo() + .then(repo => { + const relativePath = this.relativize(_path, wd) + return Git.Ignore.pathIsIgnored(repo, relativePath) + }) + .then(ignored => Boolean(ignored)) + }) }) - .then(ignored => Boolean(ignored)) } // Get the status of a directory in the repository's working directory. @@ -627,34 +636,39 @@ export default class GitRepositoryAsync { // * `added` The {Number} of added lines. // * `deleted` The {Number} of deleted lines. getDiffStats (_path) { - return this.getRepo(_path) - .then(repo => Promise.all([repo, repo.getHeadCommit()])) - .then(([repo, headCommit]) => Promise.all([repo, headCommit.getTree(), this.getWorkingDirectory(_path)])) - .then(([repo, tree, wd]) => { - const options = new Git.DiffOptions() - options.contextLines = 0 - options.flags = Git.Diff.OPTION.DISABLE_PATHSPEC_MATCH - options.pathspec = this.relativize(_path, wd) - if (process.platform === 'win32') { - // Ignore eol of line differences on windows so that files checked in - // as LF don't report every line modified when the text contains CRLF - // endings. - options.flags |= Git.Diff.OPTION.IGNORE_WHITESPACE_EOL - } - return Git.Diff.treeToWorkdir(repo, tree, options) - }) - .then(diff => this._getDiffLines(diff)) - .then(lines => { - const stats = {added: 0, deleted: 0} - for (const line of lines) { - const origin = line.origin() - if (origin === Git.Diff.LINE.ADDITION) { - stats.added++ - } else if (origin === Git.Diff.LINE.DELETION) { - stats.deleted++ - } - } - return stats + return this.getWorkingDirectory(_path) + .then(wd => { + return this.workQueue.enqueue(() => { + return this.getRepo(_path) + .then(repo => Promise.all([repo, repo.getHeadCommit()])) + .then(([repo, headCommit]) => Promise.all([repo, headCommit.getTree()])) + .then(([repo, tree]) => { + const options = new Git.DiffOptions() + options.contextLines = 0 + options.flags = Git.Diff.OPTION.DISABLE_PATHSPEC_MATCH + options.pathspec = this.relativize(_path, wd) + if (process.platform === 'win32') { + // Ignore eol of line differences on windows so that files checked in + // as LF don't report every line modified when the text contains CRLF + // endings. + options.flags |= Git.Diff.OPTION.IGNORE_WHITESPACE_EOL + } + return Git.Diff.treeToWorkdir(repo, tree, options) + }) + .then(diff => this._getDiffLines(diff)) + .then(lines => { + const stats = {added: 0, deleted: 0} + for (const line of lines) { + const origin = line.origin() + if (origin === Git.Diff.LINE.ADDITION) { + stats.added++ + } else if (origin === Git.Diff.LINE.DELETION) { + stats.deleted++ + } + } + return stats + }) + }) }) } @@ -670,24 +684,29 @@ export default class GitRepositoryAsync { // * `oldLines` The {Number} of lines in the old hunk. // * `newLines` The {Number} of lines in the new hunk getLineDiffs (_path, text) { - let relativePath = null - return Promise.all([this.getRepo(_path), this.getWorkingDirectory(_path)]) - .then(([repo, wd]) => { - relativePath = this.relativize(_path, wd) - return repo.getHeadCommit() - }) - .then(commit => commit.getEntry(relativePath)) - .then(entry => entry.getBlob()) - .then(blob => { - const options = new Git.DiffOptions() - options.contextLines = 0 - if (process.platform === 'win32') { - // Ignore eol of line differences on windows so that files checked in - // as LF don't report every line modified when the text contains CRLF - // endings. - options.flags = Git.Diff.OPTION.IGNORE_WHITESPACE_EOL - } - return this._diffBlobToBuffer(blob, text, options) + return this.getWorkingDirectory(_path) + .then(wd => { + let relativePath = null + return this.workQueue.enqueue(() => { + return this.getRepo(_path) + .then(repo => { + relativePath = this.relativize(_path, wd) + return repo.getHeadCommit() + }) + .then(commit => commit.getEntry(relativePath)) + .then(entry => entry.getBlob()) + .then(blob => { + const options = new Git.DiffOptions() + options.contextLines = 0 + if (process.platform === 'win32') { + // Ignore eol of line differences on windows so that files checked in + // as LF don't report every line modified when the text contains CRLF + // endings. + options.flags = Git.Diff.OPTION.IGNORE_WHITESPACE_EOL + } + return this._diffBlobToBuffer(blob, text, options) + }) + }) }) } @@ -709,14 +728,19 @@ export default class GitRepositoryAsync { // Returns a {Promise} that resolves or rejects depending on whether the // method was successful. checkoutHead (_path) { - return Promise.all([this.getRepo(_path), this.getWorkingDirectory(_path)]) - .then(([repo, wd]) => { - const checkoutOptions = new Git.CheckoutOptions() - checkoutOptions.paths = [this.relativize(_path, wd)] - checkoutOptions.checkoutStrategy = Git.Checkout.STRATEGY.FORCE | Git.Checkout.STRATEGY.DISABLE_PATHSPEC_MATCH - return Git.Checkout.head(repo, checkoutOptions) + return this.getWorkingDirectory(_path) + .then(wd => { + return this.workQueue.enqueue(() => { + return this.getRepo(_path) + .then(repo => { + const checkoutOptions = new Git.CheckoutOptions() + checkoutOptions.paths = [this.relativize(_path, wd)] + checkoutOptions.checkoutStrategy = Git.Checkout.STRATEGY.FORCE | Git.Checkout.STRATEGY.DISABLE_PATHSPEC_MATCH + return Git.Checkout.head(repo, checkoutOptions) + }) + .then(() => this.refreshStatusForPath(_path)) + }) }) - .then(() => this.refreshStatusForPath(_path)) } // Public: Checks out a branch in your repository. @@ -727,17 +751,19 @@ export default class GitRepositoryAsync { // // Returns a {Promise} that resolves if the method was successful. checkoutReference (reference, create) { - return this.getRepo() - .then(repo => repo.checkoutBranch(reference)) - .catch(error => { - if (create) { - return this._createBranch(reference) - .then(_ => this.checkoutReference(reference, false)) - } else { - throw error - } - }) - .then(_ => null) + return this.workQueue.enqueue(() => { + return this.getRepo() + .then(repo => repo.checkoutBranch(reference)) + }) + .catch(error => { + if (create) { + return this._createBranch(reference) + .then(_ => this.checkoutReference(reference, false)) + } else { + throw error + } + }) + .then(_ => null) } // Private @@ -763,9 +789,11 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {NodeGit.Ref} reference to the // created branch. _createBranch (name) { - return this.getRepo() - .then(repo => Promise.all([repo, repo.getHeadCommit()])) - .then(([repo, commit]) => repo.createBranch(name, commit)) + return this.workQueue.enqueue(() => { + return this.getRepo() + .then(repo => Promise.all([repo, repo.getHeadCommit()])) + .then(([repo, commit]) => repo.createBranch(name, commit)) + }) } // Get all the hunks in the diff. @@ -822,14 +850,16 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {boolean} indicating whether the // branch name changed. _refreshBranch () { - return this.getRepo() - .then(repo => repo.getCurrentBranch()) - .then(ref => ref.name()) - .then(branchName => { - const changed = branchName !== this.branch - this.branch = branchName - return changed - }) + return this.workQueue.enqueue(() => { + return this.getRepo() + .then(repo => repo.getCurrentBranch()) + .then(ref => ref.name()) + .then(branchName => { + const changed = branchName !== this.branch + this.branch = branchName + return changed + }) + }) } // Refresh the cached ahead/behind count with the given branch. From 6ba2f6d4b8da38b60399fd4a7d14df109dd3e41a Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 30 Mar 2016 11:23:54 -0400 Subject: [PATCH 627/971] Pull refresh outside the work function. Otherwise we deadlock lolololol --- src/git-repository-async.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 29788da08..86db4a574 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -738,9 +738,9 @@ export default class GitRepositoryAsync { checkoutOptions.checkoutStrategy = Git.Checkout.STRATEGY.FORCE | Git.Checkout.STRATEGY.DISABLE_PATHSPEC_MATCH return Git.Checkout.head(repo, checkoutOptions) }) - .then(() => this.refreshStatusForPath(_path)) }) }) + .then(() => this.refreshStatusForPath(_path)) } // Public: Checks out a branch in your repository. From f028c779b16aa9a5986502f35d7e1941ca0abd88 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 30 Mar 2016 11:44:24 -0400 Subject: [PATCH 628/971] Treat it more like a pool. --- spec/git-work-queue-spec.js | 2 +- src/git-work-queue.js | 32 +++++++++++++------------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/spec/git-work-queue-spec.js b/spec/git-work-queue-spec.js index 9d8e8cea0..cd38ddd72 100644 --- a/spec/git-work-queue-spec.js +++ b/spec/git-work-queue-spec.js @@ -8,7 +8,7 @@ fdescribe('GitWorkQueue', () => { let queue beforeEach(() => { - queue = new GitWorkQueue() + queue = new GitWorkQueue([{}]) }) describe('.enqueue', () => { diff --git a/src/git-work-queue.js b/src/git-work-queue.js index 6e8c2af67..d73c7153a 100644 --- a/src/git-work-queue.js +++ b/src/git-work-queue.js @@ -2,9 +2,10 @@ // A queue used to manage git work. export default class GitWorkQueue { - constructor () { + constructor (pool) { + this.pool = pool + this.queue = [] - this.working = false } // Enqueue the given function. The function must return a {Promise} when @@ -19,41 +20,34 @@ export default class GitWorkQueue { this.queue.push(this.wrapFunction(fn, resolve, reject)) - if (this.shouldStartNext()) { - this.startNext() - } + this.startNextIfAble() return wrapperPromise } wrapFunction (fn, resolve, reject) { return () => { - const promise = fn() + const repo = this.pool.shift() + const promise = fn(repo) promise .then(result => { resolve(result) - this.taskDidComplete() + this.taskDidComplete(repo) }, error => { reject(error) - this.taskDidComplete() + this.taskDidComplete(repo) }) } } - taskDidComplete () { - this.working = false + taskDidComplete (repo) { + this.pool.push(repo) - if (this.shouldStartNext()) { - this.startNext() - } + this.startNextIfAble() } - shouldStartNext () { - return !this.working && this.queue.length > 0 - } - - startNext () { - this.working = true + startNextIfAble () { + if (!this.pool.length || !this.queue.length) return const fn = this.queue.shift() fn() From e701fcc2926d261e102279c3cdf0589d039ece39 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 30 Mar 2016 11:47:05 -0400 Subject: [PATCH 629/971] Rename work queue to resource pool. --- ...rk-queue-spec.js => resource-pool-spec.js} | 6 +-- src/git-repository-async.js | 37 ++++++++++--------- src/{git-work-queue.js => resource-pool.js} | 4 +- 3 files changed, 24 insertions(+), 23 deletions(-) rename spec/{git-work-queue-spec.js => resource-pool-spec.js} (92%) rename src/{git-work-queue.js => resource-pool.js} (93%) diff --git a/spec/git-work-queue-spec.js b/spec/resource-pool-spec.js similarity index 92% rename from spec/git-work-queue-spec.js rename to spec/resource-pool-spec.js index cd38ddd72..3a5d79bcd 100644 --- a/spec/git-work-queue-spec.js +++ b/spec/resource-pool-spec.js @@ -1,14 +1,14 @@ /** @babel */ -import GitWorkQueue from '../src/git-work-queue' +import ResourcePool from '../src/resource-pool' import {it} from './async-spec-helpers' -fdescribe('GitWorkQueue', () => { +fdescribe('ResourcePool', () => { let queue beforeEach(() => { - queue = new GitWorkQueue([{}]) + queue = new ResourcePool([{}]) }) describe('.enqueue', () => { diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 86db4a574..f700ab049 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -3,7 +3,7 @@ import fs from 'fs-plus' import path from 'path' import Git from 'nodegit' -import GitWorkQueue from './git-work-queue' +import ResourcePool from './resource-pool' import {Emitter, CompositeDisposable, Disposable} from 'event-kit' const modifiedStatusFlags = Git.Status.STATUS.WT_MODIFIED | Git.Status.STATUS.INDEX_MODIFIED | Git.Status.STATUS.WT_DELETED | Git.Status.STATUS.INDEX_DELETED | Git.Status.STATUS.WT_TYPECHANGE | Git.Status.STATUS.INDEX_TYPECHANGE @@ -42,7 +42,6 @@ export default class GitRepositoryAsync { // We'll serialize our access manually. Git.disableThreadSafety() - this.workQueue = new GitWorkQueue() this.emitter = new Emitter() this.subscriptions = new CompositeDisposable() this.pathStatusCache = {} @@ -53,6 +52,8 @@ export default class GitRepositoryAsync { this._openExactPath = options.openExactPath || false this.repoPromise = this.openRepository() + this.repoPool = new ResourcePool([this.repoPromise]) + this.isCaseInsensitive = fs.isCaseInsensitive() this.upstream = {} this.submodules = {} @@ -264,7 +265,7 @@ export default class GitRepositoryAsync { // Public: Returns a {Promise} which resolves to whether the given branch // exists. hasBranch (branch) { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo() .then(repo => repo.getBranch(branch)) .then(branch => branch != null) @@ -283,7 +284,7 @@ export default class GitRepositoryAsync { // // Returns a {Promise} which resolves to a {String}. getShortHead (_path) { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo(_path) .then(repo => repo.getCurrentBranch()) .then(branch => branch.shorthand()) @@ -299,7 +300,7 @@ export default class GitRepositoryAsync { isSubmodule (_path) { return this.relativizeToWorkingDirectory(_path) .then(relativePath => { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo() .then(repo => repo.openIndex()) .then(index => { @@ -323,7 +324,7 @@ export default class GitRepositoryAsync { // * `ahead` The {Number} of commits ahead. // * `behind` The {Number} of commits behind. getAheadBehindCount (reference, _path) { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo(_path) .then(repo => Promise.all([repo, repo.getBranch(reference)])) .then(([repo, local]) => { @@ -366,7 +367,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to the {String} git configuration value // specified by the key. getConfigValue (key, _path) { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo(_path) .then(repo => repo.configSnapshot()) .then(config => config.getStringBuf(key)) @@ -394,7 +395,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {String} branch name such as // `refs/remotes/origin/master`. getUpstreamBranch (_path) { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo(_path) .then(repo => repo.getCurrentBranch()) .then(branch => Git.Branch.upstream(branch)) @@ -411,7 +412,7 @@ export default class GitRepositoryAsync { // * `remotes` An {Array} of remote reference names. // * `tags` An {Array} of tag reference names. getReferences (_path) { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo(_path) .then(repo => repo.getReferences(Git.Reference.TYPE.LISTALL)) .then(refs => { @@ -441,7 +442,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to the current {String} SHA for the // given reference. getReferenceTarget (reference, _path) { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo(_path) .then(repo => Git.Reference.nameToId(repo, reference)) .then(oid => oid.tostrS()) @@ -484,7 +485,7 @@ export default class GitRepositoryAsync { isPathIgnored (_path) { return this.getWorkingDirectory() .then(wd => { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo() .then(repo => { const relativePath = this.relativize(_path, wd) @@ -638,7 +639,7 @@ export default class GitRepositoryAsync { getDiffStats (_path) { return this.getWorkingDirectory(_path) .then(wd => { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo(_path) .then(repo => Promise.all([repo, repo.getHeadCommit()])) .then(([repo, headCommit]) => Promise.all([repo, headCommit.getTree()])) @@ -687,7 +688,7 @@ export default class GitRepositoryAsync { return this.getWorkingDirectory(_path) .then(wd => { let relativePath = null - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo(_path) .then(repo => { relativePath = this.relativize(_path, wd) @@ -730,7 +731,7 @@ export default class GitRepositoryAsync { checkoutHead (_path) { return this.getWorkingDirectory(_path) .then(wd => { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo(_path) .then(repo => { const checkoutOptions = new Git.CheckoutOptions() @@ -751,7 +752,7 @@ export default class GitRepositoryAsync { // // Returns a {Promise} that resolves if the method was successful. checkoutReference (reference, create) { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo() .then(repo => repo.checkoutBranch(reference)) }) @@ -789,7 +790,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {NodeGit.Ref} reference to the // created branch. _createBranch (name) { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo() .then(repo => Promise.all([repo, repo.getHeadCommit()])) .then(([repo, commit]) => repo.createBranch(name, commit)) @@ -850,7 +851,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {boolean} indicating whether the // branch name changed. _refreshBranch () { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo() .then(repo => repo.getCurrentBranch()) .then(ref => ref.name()) @@ -1126,7 +1127,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to an {Array} of {NodeGit.StatusFile} // statuses for the paths. _getStatus (paths) { - return this.workQueue.enqueue(() => { + return this.repoPool.enqueue(() => { return this.getRepo() .then(repo => { const opts = { diff --git a/src/git-work-queue.js b/src/resource-pool.js similarity index 93% rename from src/git-work-queue.js rename to src/resource-pool.js index d73c7153a..2a40b4304 100644 --- a/src/git-work-queue.js +++ b/src/resource-pool.js @@ -1,7 +1,7 @@ /** @babel */ -// A queue used to manage git work. -export default class GitWorkQueue { +// Manages a pool of some resource. +export default class ResourcePool { constructor (pool) { this.pool = pool From dea119ef3ed1934a4c8055237d74d38fc99aa583 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 30 Mar 2016 11:50:05 -0400 Subject: [PATCH 630/971] Less repo-centric naming. --- src/resource-pool.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/resource-pool.js b/src/resource-pool.js index 2a40b4304..ae7cb71d0 100644 --- a/src/resource-pool.js +++ b/src/resource-pool.js @@ -8,8 +8,8 @@ export default class ResourcePool { this.queue = [] } - // Enqueue the given function. The function must return a {Promise} when - // called. + // Enqueue the given function. The function will be given an object from the + // pool. The function must return a {Promise}. enqueue (fn) { let resolve = null let reject = null @@ -20,37 +20,37 @@ export default class ResourcePool { this.queue.push(this.wrapFunction(fn, resolve, reject)) - this.startNextIfAble() + this.dequeueIfAble() return wrapperPromise } wrapFunction (fn, resolve, reject) { - return () => { - const repo = this.pool.shift() - const promise = fn(repo) + return (resource) => { + const promise = fn(resource) promise .then(result => { resolve(result) - this.taskDidComplete(repo) + this.taskDidComplete(resource) }, error => { reject(error) - this.taskDidComplete(repo) + this.taskDidComplete(resource) }) } } - taskDidComplete (repo) { - this.pool.push(repo) + taskDidComplete (resource) { + this.pool.push(resource) - this.startNextIfAble() + this.dequeueIfAble() } - startNextIfAble () { + dequeueIfAble () { if (!this.pool.length || !this.queue.length) return const fn = this.queue.shift() - fn() + const resource = this.pool.shift() + fn(resource) } getQueueDepth () { return this.queue.length } From f59a86b2b9360a5219685335f49712e7767caaf3 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 30 Mar 2016 11:51:49 -0400 Subject: [PATCH 631/971] Note that we're not using this yet. --- src/git-repository-async.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index f700ab049..e45c1e47e 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -52,6 +52,9 @@ export default class GitRepositoryAsync { this._openExactPath = options.openExactPath || false this.repoPromise = this.openRepository() + // NB: We don't currently _use_ the pooled object. But by giving it one + // thing, we're really just serializing all the work. Down the road, we + // could open multiple connections to the repository. this.repoPool = new ResourcePool([this.repoPromise]) this.isCaseInsensitive = fs.isCaseInsensitive() From f19d3a2bce88d03ebf3fe7604b57a25a815f8d8e Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 30 Mar 2016 11:59:35 -0400 Subject: [PATCH 632/971] Unfocus. --- spec/resource-pool-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/resource-pool-spec.js b/spec/resource-pool-spec.js index 3a5d79bcd..27893360a 100644 --- a/spec/resource-pool-spec.js +++ b/spec/resource-pool-spec.js @@ -4,7 +4,7 @@ import ResourcePool from '../src/resource-pool' import {it} from './async-spec-helpers' -fdescribe('ResourcePool', () => { +describe('ResourcePool', () => { let queue beforeEach(() => { From ceaeef86755b50cf2e04ecc0ae1441d1d7c9c19e Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 30 Mar 2016 09:34:20 -0700 Subject: [PATCH 633/971] Add request for OS and version --- ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 8144a4b62..e142ec930 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -25,4 +25,4 @@ For more information on how to write a good [bug report](https://github.com/atom ### Versions -You can get this information from executing `atom --version` and `apm --version` at the command line. +You can get this information from executing `atom --version` and `apm --version` at the command line. Also, please include the OS and what version of the OS you're running. From 3068631d19b500162e3eb633cc36da5c20321597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Machist=C3=A9=20N=2E=20Quintana?= Date: Wed, 30 Mar 2016 12:48:11 -0700 Subject: [PATCH 634/971] Don't open a transaction if there's a selection at the start / end of line --- src/text-editor.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index b6be33071..58ecf4712 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1084,8 +1084,8 @@ class TextEditor extends Model translationDelta = [0, -1] translatedRanges = [] - @transact => - if noSelectionAtStartOfLine + if noSelectionAtStartOfLine + @transact => for selection in selections charToLeftOfSelection = new Range(selection.start.translate(translationDelta), selection.start) charTextToLeftOfSelection = @buffer.getTextInRange(charToLeftOfSelection) @@ -1106,8 +1106,8 @@ class TextEditor extends Model translationDelta = [0, 1] translatedRanges = [] - @transact => - if noSelectionAtEndOfLine + if noSelectionAtEndOfLine + @transact => for selection in selections charToRightOfSelection = new Range(selection.end, selection.end.translate(translationDelta)) charTextToRightOfSelection = @buffer.getTextInRange(charToRightOfSelection) From b3a2680fabc758f7c7c87f361a6c40ac2fc8fb08 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 30 Mar 2016 16:33:25 -0400 Subject: [PATCH 635/971] :arrow_up: nodegit@0.12.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 438c7a22a..bdaf6a834 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "less-cache": "0.23", "line-top-index": "0.2.0", "marked": "^0.3.4", - "nodegit": "0.12.0", + "nodegit": "0.12.1", "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "^5", From 81d3a23c95752ac8d22c2d8033d6ace540c9b61f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 30 Mar 2016 16:13:21 -0600 Subject: [PATCH 636/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 438c7a22a..391ab1b3c 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.4.3", + "text-buffer": "8.4.6", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 4dd6f4acd7e12fe4ad5c6ee3f229af8f4ca3d027 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 30 Mar 2016 16:26:56 -0600 Subject: [PATCH 637/971] :arrow_up: atom-keymap --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 391ab1b3c..4ca6be99e 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "electronVersion": "0.36.8", "dependencies": { "async": "0.2.6", - "atom-keymap": "^6.3.1", + "atom-keymap": "6.3.2", "babel-core": "^5.8.21", "bootstrap": "^3.3.4", "cached-run-in-this-context": "0.4.1", From 8b6940c46352d56f84b47026d5beca1cde8ad8a8 Mon Sep 17 00:00:00 2001 From: Nick Smith Date: Wed, 30 Mar 2016 22:53:42 -0400 Subject: [PATCH 638/971] Update text-editor-element.coffee --- src/text-editor-element.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index a0ec1b7fa..0c9fa6123 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -96,7 +96,7 @@ class TextEditorElement extends HTMLElement throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @config? throw new Error("Must pass a themes parameter when initializing TextEditorElements") unless @themes? throw new Error("Must pass a workspace parameter when initializing TextEditorElements") unless @workspace? - throw new Error("Must pass a assert parameter when initializing TextEditorElements") unless @assert? + throw new Error("Must pass an assert parameter when initializing TextEditorElements") unless @assert? throw new Error("Must pass a styles parameter when initializing TextEditorElements") unless @styles? throw new Error("Must pass a grammars parameter when initializing TextEditorElements") unless @grammars? From 6f6b14dbf11bd57e225cad9238c059eb43008088 Mon Sep 17 00:00:00 2001 From: Nick Smith Date: Wed, 30 Mar 2016 22:56:34 -0400 Subject: [PATCH 639/971] Update text-editor-spec.coffee Grammar Update --- spec/text-editor-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 9d2a2a58c..7a5b6041e 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3176,7 +3176,7 @@ describe "TextEditor", -> expect(editor.indentationForBufferRow(0)).toBe 1 expect(editor.indentationForBufferRow(1)).toBe 1 - it "indents the new line to the correct level when editor.autoIndent is true and using a off-side rule language", -> + it "indents the new line to the correct level when editor.autoIndent is true and using an off-side rule language", -> waitsForPromise -> atom.packages.activatePackage('language-coffee-script') @@ -5025,7 +5025,7 @@ describe "TextEditor", -> expect(editor.indentationForBufferRow(2)).toBe editor.indentationForBufferRow(1) + 1 describe "when the line preceding the newline does't add a level of indentation", -> - it "indents the new line to the same level a as the preceding line", -> + it "indents the new line to the same level as the preceding line", -> editor.setCursorBufferPosition([5, 14]) editor.insertText('\n') expect(editor.indentationForBufferRow(6)).toBe editor.indentationForBufferRow(5) From 4d4ee6bf3b6fe3467dc67d7427f156d99ef72ffd Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 31 Mar 2016 10:13:41 +0200 Subject: [PATCH 640/971] Add `applicationDelegate.getAutoUpdateManagerErrorMessage()` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes, the error event gets emitted before the renderer process has the chance to subscribe. Therefore, we expose an `autoUpdateManager.getErrorMessage()` in the browser process, so that we don’t lose that information. --- spec/auto-update-manager-spec.js | 1 - src/application-delegate.coffee | 3 +++ src/auto-update-manager.js | 9 ++++++--- src/browser/atom-application.coffee | 3 +++ src/browser/auto-update-manager.coffee | 10 +++++++--- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 12fe0c825..581e5e7c3 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -70,7 +70,6 @@ describe('AutoUpdateManager (renderer)', () => { autoUpdateManager.onUpdateError(spy) electronAutoUpdater.emit('error', {}, 'an error') waitsFor(() => spy.callCount === 1) - runs(() => expect(spy).toHaveBeenCalledWith('an error')) }) }) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index ecefc4b67..8bfae13fc 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -252,3 +252,6 @@ class ApplicationDelegate getAutoUpdateManagerState: -> ipcRenderer.sendSync('get-auto-update-manager-state') + + getAutoUpdateManagerErrorMessage: -> + ipcRenderer.sendSync('get-auto-update-manager-error') diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index a6cea92fe..fb6325a26 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -21,9 +21,8 @@ export default class AutoUpdateManager { applicationDelegate.onUpdateNotAvailable(() => { this.emitter.emit('update-not-available') }), - applicationDelegate.onUpdateError((message) => { - console.error(message) - this.emitter.emit('update-error', message) + applicationDelegate.onUpdateError(() => { + this.emitter.emit('update-error') }) ) } @@ -45,6 +44,10 @@ export default class AutoUpdateManager { return this.applicationDelegate.getAutoUpdateManagerState() } + getErrorMessage () { + return this.applicationDelegate.getAutoUpdateManagerErrorMessage() + } + platformSupportsUpdates () { return atom.getReleaseChannel() !== 'dev' && this.getState() !== 'unsupported' } diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 4767c9065..e58b31e10 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -311,6 +311,9 @@ class AtomApplication ipcMain.on 'get-auto-update-manager-state', (event) => event.returnValue = @autoUpdateManager.getState() + ipcMain.on 'get-auto-update-manager-error', (event) => + event.returnValue = @autoUpdateManager.getErrorMessage() + ipcMain.on 'execute-javascript-in-dev-tools', (event, code) -> event.sender.devToolsWebContents?.executeJavaScript(code) diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index 391d1f0b4..06a71698d 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -32,8 +32,8 @@ class AutoUpdateManager {autoUpdater} = require 'electron' autoUpdater.on 'error', (event, message) => - @setState(ErrorState) - @emitWindowEvent('update-error', message) + @setState(ErrorState, message) + @emitWindowEvent('update-error') console.error "Error Downloading Update: #{message}" autoUpdater.setFeedURL @feedUrl @@ -83,14 +83,18 @@ class AutoUpdateManager atomWindow.sendMessage(eventName, payload) return - setState: (state) -> + setState: (state, errorMessage) -> return if @state is state @state = state + @errorMessage = errorMessage @emit 'state-changed', @state getState: -> @state + getErrorMessage: -> + @errorMessage + scheduleUpdateCheck: -> # Only schedule update check periodically if running in release version and # and there is no existing scheduled update check. From cd41b1a0cad765506974ffec8b453e26984e094b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 31 Mar 2016 10:40:39 +0200 Subject: [PATCH 641/971] Improve assertion This allows us to test that the configuration can be changed outside of Atom. --- spec/integration/startup-spec.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/integration/startup-spec.coffee b/spec/integration/startup-spec.coffee index 136ca9676..7c2503e69 100644 --- a/spec/integration/startup-spec.coffee +++ b/spec/integration/startup-spec.coffee @@ -269,11 +269,6 @@ describe "Starting Atom", -> ].sort() it "doesn't reopen any previously opened windows if restorePreviousWindowsOnStart is disabled", -> - configPath = path.join(atomHome, 'config.cson') - config = CSON.readFileSync(configPath) - config['*'].core = {restorePreviousWindowsOnStart: false} - CSON.writeFileSync(configPath, config) - runAtom [tempDirPath], {ATOM_HOME: atomHome}, (client) -> client .waitForExist("atom-workspace") @@ -282,6 +277,11 @@ describe "Starting Atom", -> , 5000) .waitForExist("atom-workspace") + configPath = path.join(atomHome, 'config.cson') + config = CSON.readFileSync(configPath) + config['*'].core = {restorePreviousWindowsOnStart: false} + CSON.writeFileSync(configPath, config) + runAtom [], {ATOM_HOME: atomHome}, (client) -> windowProjectPaths = [] From 296c016bf866943589954c92d58132195c55a51a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 31 Mar 2016 10:41:23 +0200 Subject: [PATCH 642/971] :racehorse: Don't load state when restorePreviousState is false --- src/browser/atom-application.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index eea2fdc57..14713ee18 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -516,7 +516,7 @@ class AtomApplication loadState: (options) -> restorePreviousState = @config.get('core.restorePreviousWindowsOnStart') ? true - if (states = @storageFolder.load('application.json'))?.length > 0 and restorePreviousState + if restorePreviousState and (states = @storageFolder.load('application.json'))?.length > 0 for state in states @openWithOptions(_.extend(options, { initialPaths: state.initialPaths From d568c76b0b5ab7c246fbc0c5640988b7b64e1a41 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 31 Mar 2016 11:44:32 +0200 Subject: [PATCH 643/971] :apple: Fix emoji rendering --- src/workspace-element.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/workspace-element.coffee b/src/workspace-element.coffee index 8f6dca48a..02ff2e5b4 100644 --- a/src/workspace-element.coffee +++ b/src/workspace-element.coffee @@ -44,10 +44,15 @@ class WorkspaceElement extends HTMLElement @subscriptions.add @config.onDidChange 'editor.lineHeight', @updateGlobalTextEditorStyleSheet.bind(this) updateGlobalTextEditorStyleSheet: -> + fontFamily = @config.get('editor.fontFamily') + # TODO: There is a bug in how some emojis (e.g. ❤️) are rendered on OSX. + # This workaround should be removed once we update to Chromium 51, where the + # problem was fixed. + fontFamily += ', "Apple Color Emoji"' if process.platform is 'darwin' styleSheetSource = """ atom-text-editor { font-size: #{@config.get('editor.fontSize')}px; - font-family: #{@config.get('editor.fontFamily')}; + font-family: #{fontFamily}; line-height: #{@config.get('editor.lineHeight')}; } """ From dea5c5560e7a98977cf1d265f55686c1efc1d04b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 31 Mar 2016 12:03:15 +0200 Subject: [PATCH 644/971] :arrow_up: bracket-matcher --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b62c8add0..050f6a139 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "autosave": "0.23.1", "background-tips": "0.26.0", "bookmarks": "0.38.2", - "bracket-matcher": "0.81.0", + "bracket-matcher": "0.82.0", "command-palette": "0.38.0", "deprecation-cop": "0.54.1", "dev-live-reload": "0.47.0", From d89d34f4ef5d819e517548bda70d8486531dc3be Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 31 Mar 2016 13:02:52 +0200 Subject: [PATCH 645/971] :green_heart: --- spec/workspace-element-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/workspace-element-spec.coffee b/spec/workspace-element-spec.coffee index cf773b4ef..186a33258 100644 --- a/spec/workspace-element-spec.coffee +++ b/spec/workspace-element-spec.coffee @@ -47,9 +47,9 @@ describe "WorkspaceElement", -> it "updates the font-family based on the 'editor.fontFamily' config value", -> initialCharWidth = editor.getDefaultCharWidth() - expect(getComputedStyle(editorElement).fontFamily).toBe atom.config.get('editor.fontFamily') + expect(getComputedStyle(editorElement).fontFamily).toBe atom.config.get('editor.fontFamily') + ", 'Apple Color Emoji'" atom.config.set('editor.fontFamily', 'sans-serif') - expect(getComputedStyle(editorElement).fontFamily).toBe atom.config.get('editor.fontFamily') + expect(getComputedStyle(editorElement).fontFamily).toBe atom.config.get('editor.fontFamily') + ", 'Apple Color Emoji'" expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth it "updates the line-height based on the 'editor.lineHeight' config value", -> From 22acdb76f6a4b7f5640d90873094fca2b3911dfa Mon Sep 17 00:00:00 2001 From: Thomas Johansen Date: Thu, 31 Mar 2016 13:24:14 +0200 Subject: [PATCH 646/971] :bug: Fix potential null reference callback invokation Fixes #11328 --- script/utils/child-process-wrapper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/utils/child-process-wrapper.js b/script/utils/child-process-wrapper.js index 2afc57934..55e6733d1 100644 --- a/script/utils/child-process-wrapper.js +++ b/script/utils/child-process-wrapper.js @@ -17,7 +17,7 @@ exports.safeExec = function(command, options, callback) { var child = childProcess.exec(command, options, function(error, stdout, stderr) { if (error) process.exit(error.code || 1); - else + else if (callback) callback(null); }); child.stderr.pipe(process.stderr); From 47bbd8b4bba38ba8a51eda7ddc8d66b5c442aafc Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 31 Mar 2016 17:45:32 +0200 Subject: [PATCH 647/971] Ensure we test for emojis only on Darwin --- spec/workspace-element-spec.coffee | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/spec/workspace-element-spec.coffee b/spec/workspace-element-spec.coffee index 186a33258..efb1d1b26 100644 --- a/spec/workspace-element-spec.coffee +++ b/spec/workspace-element-spec.coffee @@ -47,9 +47,14 @@ describe "WorkspaceElement", -> it "updates the font-family based on the 'editor.fontFamily' config value", -> initialCharWidth = editor.getDefaultCharWidth() - expect(getComputedStyle(editorElement).fontFamily).toBe atom.config.get('editor.fontFamily') + ", 'Apple Color Emoji'" + fontFamily = atom.config.get('editor.fontFamily') + fontFamily += ", 'Apple Color Emoji'" if process.platform is 'darwin' + expect(getComputedStyle(editorElement).fontFamily).toBe fontFamily + atom.config.set('editor.fontFamily', 'sans-serif') - expect(getComputedStyle(editorElement).fontFamily).toBe atom.config.get('editor.fontFamily') + ", 'Apple Color Emoji'" + fontFamily = atom.config.get('editor.fontFamily') + fontFamily += ", 'Apple Color Emoji'" if process.platform is 'darwin' + expect(getComputedStyle(editorElement).fontFamily).toBe fontFamily expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth it "updates the line-height based on the 'editor.lineHeight' config value", -> From 34e2bf13cec7969ecbc705adeacdf901ff0e0c5e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 31 Mar 2016 16:52:36 -0600 Subject: [PATCH 648/971] Add native profiling instructions --- docs/native-profiling.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 docs/native-profiling.md diff --git a/docs/native-profiling.md b/docs/native-profiling.md new file mode 100644 index 000000000..f9f9160a5 --- /dev/null +++ b/docs/native-profiling.md @@ -0,0 +1,18 @@ +# Profiling the Atom Render Process on OS X with Instruments + +![Instruments](https://cloud.githubusercontent.com/assets/1789/14193295/d503db7a-f760-11e5-88bf-fe417c0cd913.png) + +* Determine the version of Electron for your version of Atom. + * Open the dev tools with `alt-cmd-i` + * Evaluate `process.versions.electron` in the console. +* Based on this version, download the appropriate Electron symbols from the [releases](https://github.com/atom/electron/releases) page. + * The file name should look like `electron-v0.X.Y-darwin-x64-dsym.zip`. + * Decompress these symbols in your `~/Downloads` directory. +* Now create a time profile in Instruments. + * Open `Instruments.app`. + * Select `Time Profiler` + * In Atom, determine the pid to attach to by evaluating `process.pid` in the dev tools console. + * Attach to this pid via the menu at the upper left corner of the Instruments profiler. + * Click record, do your thing. + * Click stop. + * The symbols should have been automatically located by Instruments (via Spotlight or something?), giving you a readable profile. From 6f1c1fd0e0242b096f4781698066788b26af2fa4 Mon Sep 17 00:00:00 2001 From: simurai Date: Fri, 1 Apr 2016 11:17:57 +0900 Subject: [PATCH 649/971] :arrow_up: one-dark/light-ui@v1.3.1 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 050f6a139..33faff84f 100644 --- a/package.json +++ b/package.json @@ -66,8 +66,8 @@ "atom-light-ui": "0.43.0", "base16-tomorrow-dark-theme": "1.1.0", "base16-tomorrow-light-theme": "1.1.1", - "one-dark-ui": "1.3.0", - "one-light-ui": "1.3.0", + "one-dark-ui": "1.3.1", + "one-light-ui": "1.3.1", "one-dark-syntax": "1.2.0", "one-light-syntax": "1.2.0", "solarized-dark-syntax": "1.0.1", From bb11c4e5bd7a98f05215e853b2142af6252ab99e Mon Sep 17 00:00:00 2001 From: Christian Oliff Date: Fri, 1 Apr 2016 15:20:00 +0900 Subject: [PATCH 650/971] HTTPS a couple of links HTTPS a couple of links --- docs/build-instructions/linux.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/build-instructions/linux.md b/docs/build-instructions/linux.md index 1d746bcd6..c6a9e74eb 100644 --- a/docs/build-instructions/linux.md +++ b/docs/build-instructions/linux.md @@ -6,8 +6,8 @@ Ubuntu LTS 12.04 64-bit is the recommended platform. * OS with 64-bit or 32-bit architecture * C++ toolchain - * [Git](http://git-scm.com/) - * [Node.js](http://nodejs.org/download/) (0.10.x or above) + * [Git](https://git-scm.com/) + * [Node.js](https://nodejs.org/en/download/) (0.10.x or above) * [npm](https://www.npmjs.com/) v1.4.x or above (automatically bundled with Node.js) * `npm -v` to check the version. * `npm config set python /usr/bin/python2 -g` to ensure that gyp uses python2. From fa469121d8a9e4d45bccb0f2b4ddf9316364ce8a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 1 Apr 2016 09:06:32 +0200 Subject: [PATCH 651/971] Ensure getErrorMessage() works properly --- spec/auto-update-manager-spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 581e5e7c3..91b52192c 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -68,8 +68,9 @@ describe('AutoUpdateManager (renderer)', () => { it('subscribes to "update-error" event', () => { const spy = jasmine.createSpy('spy') autoUpdateManager.onUpdateError(spy) - electronAutoUpdater.emit('error', {}, 'an error') + electronAutoUpdater.emit('error', {}, 'an error message') waitsFor(() => spy.callCount === 1) + runs(() => expect(autoUpdateManager.getErrorMessage()).toBe('an error message')) }) }) From 2b05995b1cd4b07ac76f3ba3f838144ff6fb917e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 1 Apr 2016 09:12:01 +0200 Subject: [PATCH 652/971] :arrow_up: about --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 33faff84f..1d3bf647e 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "one-light-syntax": "1.2.0", "solarized-dark-syntax": "1.0.1", "solarized-light-syntax": "1.0.1", - "about": "1.4.2", + "about": "1.5.0", "archive-view": "0.61.1", "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.0", From 9b4e45133379ea74e9330aa097c71dbdd402e26c Mon Sep 17 00:00:00 2001 From: simurai Date: Fri, 1 Apr 2016 17:27:14 +0900 Subject: [PATCH 653/971] :arrow_up: solarized-dark/light-syntax@v1.0.2 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1d3bf647e..8f893203c 100644 --- a/package.json +++ b/package.json @@ -70,8 +70,8 @@ "one-light-ui": "1.3.1", "one-dark-syntax": "1.2.0", "one-light-syntax": "1.2.0", - "solarized-dark-syntax": "1.0.1", - "solarized-light-syntax": "1.0.1", + "solarized-dark-syntax": "1.0.2", + "solarized-light-syntax": "1.0.2", "about": "1.5.0", "archive-view": "0.61.1", "autocomplete-atom-api": "0.10.0", From 158d0193716f9c157cfaa7ec72b84146dec1a181 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 1 Apr 2016 10:56:20 +0200 Subject: [PATCH 654/971] Use application-level events to control updates in the browser process --- src/application-delegate.coffee | 4 ++-- src/browser/atom-application.coffee | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index 8bfae13fc..8ac6ceb5e 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -245,10 +245,10 @@ class ApplicationDelegate webFrame.setZoomLevelLimits(1, 1) checkForUpdate: -> - ipcRenderer.send('check-for-update') + ipcRenderer.send('command', 'application:check-for-update') restartAndInstallUpdate: -> - ipcRenderer.send('install-update') + ipcRenderer.send('command', 'application:install-update') getAutoUpdateManagerState: -> ipcRenderer.sendSync('get-auto-update-manager-state') diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 772afc0e0..a579fb762 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -310,9 +310,6 @@ class AtomApplication ipcMain.on 'execute-javascript-in-dev-tools', (event, code) -> event.sender.devToolsWebContents?.executeJavaScript(code) - ipcMain.on 'check-for-update', => - @autoUpdateManager.check() - ipcMain.on 'get-auto-update-manager-state', (event) => event.returnValue = @autoUpdateManager.getState() From 793f5f96924c356f5d00fd2ca26f0eee6d36bb81 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 1 Apr 2016 15:33:48 +0200 Subject: [PATCH 655/971] Disable zoom every time a display gets added or removed --- src/application-delegate.coffee | 17 ++++++++++++++--- src/atom-environment.coffee | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index 8ac6ceb5e..aee02ee8e 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -1,5 +1,5 @@ _ = require 'underscore-plus' -{ipcRenderer, remote, shell, webFrame} = require 'electron' +{screen, ipcRenderer, remote, shell, webFrame} = require 'electron' ipcHelpers = require './ipc-helpers' {Disposable} = require 'event-kit' {getWindowLoadSettings, setWindowLoadSettings} = require './window-load-settings-helpers' @@ -241,8 +241,19 @@ class ApplicationDelegate openExternal: (url) -> shell.openExternal(url) - disablePinchToZoom: -> - webFrame.setZoomLevelLimits(1, 1) + disableZoom: -> + outerCallback = -> + webFrame.setZoomLevelLimits(1, 1) + + outerCallback() + # Set the limits every time a display is added or removed, otherwise the + # configuration gets reset to the default, which allows zooming the + # webframe. + screen.on('display-added', outerCallback) + screen.on('display-removed', outerCallback) + new Disposable -> + screen.removeListener('display-added', outerCallback) + screen.removeListener('display-removed', outerCallback) checkForUpdate: -> ipcRenderer.send('command', 'application:check-for-update') diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 6ef46dc2a..ffff564ba 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -208,7 +208,7 @@ class AtomEnvironment extends Model @stylesElement = @styles.buildStylesElement() @document.head.appendChild(@stylesElement) - @applicationDelegate.disablePinchToZoom() + @disposables.add(@applicationDelegate.disableZoom()) @keymaps.subscribeToFileReadFailure() @keymaps.loadBundledKeymaps() From 0330e3366794bb9925b095aec84f980e5c5639d8 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Fri, 1 Apr 2016 21:56:56 -0400 Subject: [PATCH 656/971] :arrow_up: language-c@0.51.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8f893203c..be1bb2f51 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "welcome": "0.34.0", "whitespace": "0.32.2", "wrap-guide": "0.38.1", - "language-c": "0.51.2", + "language-c": "0.51.3", "language-clojure": "0.20.0", "language-coffee-script": "0.46.1", "language-csharp": "0.12.0", From bc2e6742347c444f3e99cba025e57af269cfe734 Mon Sep 17 00:00:00 2001 From: Drew Noel Date: Fri, 26 Feb 2016 12:32:55 -0500 Subject: [PATCH 657/971] :arrow_up: Electron --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index be1bb2f51..98afe224f 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.36.8", + "electronVersion": "0.36.9", "dependencies": { "async": "0.2.6", "atom-keymap": "6.3.2", From 81d0874fa5433c033ed276c02d7a3bcb2c7fed7e Mon Sep 17 00:00:00 2001 From: Drew Noel Date: Sat, 12 Mar 2016 23:57:24 -0500 Subject: [PATCH 658/971] :arrow_up: electron v0.36.11 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 98afe224f..89b79fc45 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.36.9", + "electronVersion": "0.36.11", "dependencies": { "async": "0.2.6", "atom-keymap": "6.3.2", From fb5bfe1b3a30a0643cf020aa1daee7700d780fbc Mon Sep 17 00:00:00 2001 From: Drew Noel Date: Sun, 13 Mar 2016 13:25:27 -0400 Subject: [PATCH 659/971] Fix old electron require syntax in specs --- spec/atom-environment-spec.coffee | 2 +- spec/window-event-handler-spec.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 5fd4b11f1..2d431cc26 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -365,7 +365,7 @@ describe "AtomEnvironment", -> updateAvailableHandler = jasmine.createSpy("update-available-handler") subscription = atom.onUpdateAvailable updateAvailableHandler - autoUpdater = require('remote').require('auto-updater') + autoUpdater = require('electron').remote.require('auto-updater') autoUpdater.emit 'update-downloaded', null, "notes", "version" waitsFor -> diff --git a/spec/window-event-handler-spec.coffee b/spec/window-event-handler-spec.coffee index bb7e1665b..22f43c90f 100644 --- a/spec/window-event-handler-spec.coffee +++ b/spec/window-event-handler-spec.coffee @@ -75,7 +75,7 @@ describe "WindowEventHandler", -> describe "when a link is clicked", -> it "opens the http/https links in an external application", -> - shell = require 'shell' + {shell} = require 'electron' spyOn(shell, 'openExternal') link = document.createElement('a') From 8694ea3c94225dda985e50c69804a916da76bd14 Mon Sep 17 00:00:00 2001 From: Wliu Date: Fri, 1 Apr 2016 22:21:39 -0400 Subject: [PATCH 660/971] Update require paths for deprecated Electron syntax --- src/module-cache.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/module-cache.coffee b/src/module-cache.coffee index a2840a864..c8e3b9105 100644 --- a/src/module-cache.coffee +++ b/src/module-cache.coffee @@ -202,12 +202,12 @@ registerBuiltins = (devMode) -> atomShellRoot = path.join(process.resourcesPath, 'atom.asar') - commonRoot = path.join(atomShellRoot, 'common', 'api', 'lib') + commonRoot = path.join(atomShellRoot, 'lib', 'common', 'api') commonBuiltins = ['callbacks-registry', 'clipboard', 'crash-reporter', 'screen', 'shell'] for builtin in commonBuiltins cache.builtins[builtin] = path.join(commonRoot, "#{builtin}.js") - rendererRoot = path.join(atomShellRoot, 'renderer', 'api', 'lib') + rendererRoot = path.join(atomShellRoot, 'lib', 'renderer', 'api') rendererBuiltins = ['ipc-renderer', 'remote'] for builtin in rendererBuiltins cache.builtins[builtin] = path.join(rendererRoot, "#{builtin}.js") From 3d8ce38e6b30f4d33fc8bae0d5b86dafa59fbe70 Mon Sep 17 00:00:00 2001 From: Florian Kinder Date: Sat, 2 Apr 2016 15:37:59 +0200 Subject: [PATCH 661/971] :arrow_up: language-perl@0.33.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index be1bb2f51..55900a980 100644 --- a/package.json +++ b/package.json @@ -134,7 +134,7 @@ "language-make": "0.21.0", "language-mustache": "0.13.0", "language-objective-c": "0.15.1", - "language-perl": "0.32.0", + "language-perl": "0.33.0", "language-php": "0.37.0", "language-property-list": "0.8.0", "language-python": "0.43.1", From 37ea3c913f4acbc7af875bb7b64ec4e7aff93b94 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sat, 2 Apr 2016 10:21:52 -0400 Subject: [PATCH 662/971] :arrow_up: electron@0.36.12 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 89b79fc45..e23ab4b54 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.36.11", + "electronVersion": "0.36.12", "dependencies": { "async": "0.2.6", "atom-keymap": "6.3.2", From f1cf66ba32a8d277cd098a9ea8595e12e108660d Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sat, 2 Apr 2016 10:42:55 -0400 Subject: [PATCH 663/971] :arrow_up: language-csharp@0.12.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 55900a980..a630dabfc 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "language-c": "0.51.3", "language-clojure": "0.20.0", "language-coffee-script": "0.46.1", - "language-csharp": "0.12.0", + "language-csharp": "0.12.1", "language-css": "0.36.0", "language-gfm": "0.85.0", "language-git": "0.12.1", From 8575b38c7b674e5d1e4be99b54c8322154b5673d Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sat, 2 Apr 2016 17:48:24 -0700 Subject: [PATCH 664/971] Pending pane items shouldn't be made permanent before being replaced Previously, when a Pane would replace a pending item with another pending item, it would emit `onItemDidTerminatePendingState` for that item, which was not true because the item was actually being destroyed. --- spec/pane-spec.coffee | 20 ++++++++++++++++++++ src/pane.coffee | 8 +++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index d0b191f38..9969b1dcc 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -262,6 +262,26 @@ describe "Pane", -> pane.setPendingItem("fake item two") expect(callbackCalled).toBeTruthy() + it "isn't called when a pending item is replaced with a new one", -> + pane = null + pendingSpy = jasmine.createSpy("onItemDidTerminatePendingState") + destroySpy = jasmine.createSpy("onWillDestroyItem") + + waitsForPromise -> + atom.workspace.open('sample.txt', pending: true).then -> + pane = atom.workspace.getActivePane() + + runs -> + pane.onItemDidTerminatePendingState pendingSpy + pane.onWillDestroyItem destroySpy + + waitsForPromise -> + atom.workspace.open('sample.js', pending: true) + + runs -> + expect(destroySpy).toHaveBeenCalled() + expect(pendingSpy).not.toHaveBeenCalled() + describe "::activateNextRecentlyUsedItem() and ::activatePreviousRecentlyUsedItem()", -> it "sets the active item to the next/previous item in the itemStack, looping around at either end", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D"), new Item("E")])) diff --git a/src/pane.coffee b/src/pane.coffee index 3ff62993c..e8858a2b9 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -442,15 +442,16 @@ class Pane extends Model if typeof item.onDidTerminatePendingState is "function" itemSubscriptions.add item.onDidTerminatePendingState => @clearPendingItem() if @getPendingItem() is item - itemSubscriptions.add item.onDidDestroy => @removeItem(item, false) @subscriptionsPerItem.set item, itemSubscriptions @items.splice(index, 0, item) lastPendingItem = @getPendingItem() + replacingPendingItem = lastPendingItem? and not moved + @pendingItem = null if replacingPendingItem @setPendingItem(item) if pending @emitter.emit 'did-add-item', {item, index, moved} - @destroyItem(lastPendingItem) if lastPendingItem? and not moved + @destroyItem(lastPendingItem) if replacingPendingItem @setActiveItem(item) unless @getActiveItem()? item @@ -458,7 +459,8 @@ class Pane extends Model if @pendingItem isnt item mostRecentPendingItem = @pendingItem @pendingItem = item - @emitter.emit 'item-did-terminate-pending-state', mostRecentPendingItem + if mostRecentPendingItem? + @emitter.emit 'item-did-terminate-pending-state', mostRecentPendingItem getPendingItem: => @pendingItem or null From bbef4c67c3171569b249f236812577bce787021f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 4 Apr 2016 10:37:54 -0600 Subject: [PATCH 665/971] Get the TextEditorComponent specs green --- spec/text-editor-component-spec.js | 49 ++++++++++++++++-------------- src/lines-tile-component.coffee | 5 ++- src/text-editor.coffee | 2 ++ 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 79936311b..c7c9f9277 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -72,7 +72,7 @@ describe('TextEditorComponent', function () { let text = editor.lineTextForScreenRow(screenRow) expect(lineNode.offsetTop).toBe(top) if (text === '') { - expect(lineNode.innerHTML).toBe(' ') + expect(lineNode.textContent).toBe(' ') } else { expect(lineNode.textContent).toBe(text) } @@ -360,9 +360,9 @@ describe('TextEditorComponent', function () { } }) - it('renders an nbsp on empty lines when no line-ending character is defined', function () { + it('renders an placeholder space on empty lines when no line-ending character is defined', function () { atom.config.set('editor.showInvisibles', false) - expect(component.lineNodeForScreenRow(10).textContent).toBe(NBSP) + expect(component.lineNodeForScreenRow(10).textContent).toBe(' ') }) it('gives the lines and tiles divs the same background color as the editor to improve GPU performance', async function () { @@ -428,13 +428,14 @@ describe('TextEditorComponent', function () { expect(leafNodes[0].classList.contains('leading-whitespace')).toBe(false) }) - it('keeps rebuilding lines when continuous reflow is on', function () { + it('keeps rebuilding lines when continuous reflow is on', async function () { wrapperNode.setContinuousReflow(true) - let oldLineNode = componentNode.querySelector('.line') + let oldLineNode = componentNode.querySelectorAll('.line')[1] - waitsFor(function () { - return componentNode.querySelector('.line') !== oldLineNode - }) + while (true) { + await nextViewUpdatePromise() + if (componentNode.querySelectorAll('.line')[1] !== oldLineNode) break + } }) describe('when showInvisibles is enabled', function () { @@ -496,20 +497,20 @@ describe('TextEditorComponent', function () { expect(component.lineNodeForScreenRow(10).textContent).toBe(invisibles.eol) }) - it('renders an nbsp on empty lines when the line-ending character is an empty string', async function () { + it('renders a placeholder space on empty lines when the line-ending character is an empty string', async function () { atom.config.set('editor.invisibles', { eol: '' }) await nextViewUpdatePromise() - expect(component.lineNodeForScreenRow(10).textContent).toBe(NBSP) + expect(component.lineNodeForScreenRow(10).textContent).toBe(' ') }) - it('renders an nbsp on empty lines when the line-ending character is false', async function () { + it('renders an placeholder space on empty lines when the line-ending character is false', async function () { atom.config.set('editor.invisibles', { eol: false }) await nextViewUpdatePromise() - expect(component.lineNodeForScreenRow(10).textContent).toBe(NBSP) + expect(component.lineNodeForScreenRow(10).textContent).toBe(' ') }) it('interleaves invisible line-ending characters with indent guides on empty lines', async function () { @@ -517,24 +518,25 @@ describe('TextEditorComponent', function () { await nextViewUpdatePromise() + editor.setTabLength(2) editor.setTextInBufferRange([[10, 0], [11, 0]], '\r\n', { normalizeLineEndings: false }) await nextViewUpdatePromise() + expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE') - expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE ') editor.setTabLength(3) await nextViewUpdatePromise() + expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE') - expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE ') editor.setTabLength(1) await nextViewUpdatePromise() + expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE') - expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE ') editor.setTextInBufferRange([[9, 0], [9, Infinity]], ' ') editor.setTextInBufferRange([[11, 0], [11, Infinity]], ' ') await nextViewUpdatePromise() - expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE') + expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE') }) describe('when soft wrapping is enabled', function () { @@ -985,13 +987,14 @@ describe('TextEditorComponent', function () { expect(component.lineNumberNodeForScreenRow(3) != null).toBe(true) }) - it('keeps rebuilding line numbers when continuous reflow is on', function () { + it('keeps rebuilding line numbers when continuous reflow is on', async function () { wrapperNode.setContinuousReflow(true) let oldLineNode = componentNode.querySelectorAll('.line-number')[1] - waitsFor(function () { - return componentNode.querySelectorAll('.line-number')[1] !== oldLineNode - }) + while (true) { + await nextViewUpdatePromise() + if (componentNode.querySelectorAll('.line-number')[1] !== oldLineNode) break + } }) describe('fold decorations', function () { @@ -1198,10 +1201,10 @@ describe('TextEditorComponent', function () { let cursor = componentNode.querySelector('.cursor') let cursorRect = cursor.getBoundingClientRect() - let cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.source.js').childNodes[0] + let cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.source.js').childNodes[2] let range = document.createRange(cursorLocationTextNode) - range.setStart(cursorLocationTextNode, 3) - range.setEnd(cursorLocationTextNode, 4) + range.setStart(cursorLocationTextNode, 0) + range.setEnd(cursorLocationTextNode, 1) let rangeRect = range.getBoundingClientRect() expect(cursorRect.left).toBeCloseTo(rangeRect.left, 0) expect(cursorRect.width).toBeCloseTo(rangeRect.width, 0) diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index 544a58040..6e9a15e6b 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -4,7 +4,6 @@ HighlightsComponent = require './highlights-component' TokenIterator = require './token-iterator' AcceptFilter = {acceptNode: -> NodeFilter.FILTER_ACCEPT} TokenTextEscapeRegex = /[&"'<>]/g -NBSPCharacter = '\u00a0' MaxTokenLength = 20000 cloneObject = (object) -> @@ -267,13 +266,13 @@ class LinesTileComponent openScopeNode.appendChild(newScopeNode) openScopeNode = newScopeNode else - textNode = @domElementPool.buildText(lineText.substr(startIndex, tagCode).replace(/\s/g, NBSPCharacter)) + textNode = @domElementPool.buildText(lineText.substr(startIndex, tagCode)) startIndex += tagCode openScopeNode.appendChild(textNode) @currentLineTextNodes.push(textNode) if startIndex is 0 - textNode = @domElementPool.buildText(NBSPCharacter) + textNode = @domElementPool.buildText(' ') lineNode.appendChild(textNode) @currentLineTextNodes.push(textNode) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 06229bff2..ffb5a4706 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -132,6 +132,8 @@ class TextEditor extends Model @config, @assert, @grammarRegistry, @packageManager }) {@buffer, @displayLayer} = @displayBuffer + @decorateMarkerLayer(@displayLayer.foldsMarkerLayer, {type: 'line-number', class: 'folded'}) + @selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true) for marker in @selectionsMarkerLayer.getMarkers() From 2a7344091d74de66f77a366ee963eb82bba00cb4 Mon Sep 17 00:00:00 2001 From: Isaac Salier-Hellendag Date: Wed, 23 Mar 2016 19:18:02 -0700 Subject: [PATCH 666/971] Avoid setting hidden input value on textInput Atom currently sets the `value` of the input on every `textInput` event, in an effort to appropriately handle changes made via the OSX diacritic menu (for accents, umlauts, etc). The drawback of this is approach is that updating the value of the input will trigger layout and a subsequent layer tree update. To resolve this, here is my proposal: - Track a flag for `keypress` events. When the diacritic menu is used, there are two `textInput` events, with no `keypress` in between. Therefore, when no `keypress` has occurred just prior to a `textInput`, the editor model can select the previous character to be replaced by the new accented character. - Track a flag for `compositionstart` events. When a user is in IME mode, the diacritic menu cannot be used, so the editor can skip the backward selection. Test Plan: Tested in a plaintext file. - Type Latin characters, verify proper character insertion. - Press and hold a. Diacritic menu appears. Select an option using the keyboard or mouse. Verify that the `a` is replaced by an accented `a`, with no extra characters. - Type test strings in Katakana, 2-Set Korean, Telex (Vietnamese), Simplified Pinyin. Verify that characters are inserted correctly while composing, and after committing strings. --- src/text-editor-component.coffee | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 9b091100d..50851279b 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -244,6 +244,7 @@ class TextEditorComponent listenForDOMEvents: -> @domNode.addEventListener 'mousewheel', @onMouseWheel @domNode.addEventListener 'textInput', @onTextInput + @domNode.addEventListener 'keypress', @onKeyPress @scrollViewNode.addEventListener 'mousedown', @onMouseDown @scrollViewNode.addEventListener 'scroll', @onScrollViewScroll @@ -266,6 +267,7 @@ class TextEditorComponent checkpoint = null @domNode.addEventListener 'compositionstart', => + @imeMode = true checkpoint = @editor.createCheckpoint() @domNode.addEventListener 'compositionupdate', (event) => @editor.insertText(event.data, select: true) @@ -319,26 +321,27 @@ class TextEditorComponent if @mounted @presenter.setFocused(false) + onKeyPress: (event) => + @detectedKeyPress = true + onTextInput: (event) => event.stopPropagation() - - # If we prevent the insertion of a space character, then the browser - # interprets the spacebar keypress as a page-down command. - event.preventDefault() unless event.data is ' ' + event.preventDefault() return unless @isInputEnabled() - inputNode = event.target + # Workaround of the accented character suggestion feature in OS X. + # This will only occur when the user is not composing in IME mode. + # When the user selects a modified character from the OSX menu, `textInput` + # will occur twice, once for the initial character, and once for the + # modified character. However, only a single keypress will have fired. If + # this is the case, select backward to replace the original character. + if not @imeMode and not @detectedKeyPress + @editor.selectLeft() - # Work around of the accented character suggestion feature in OS X. - # Text input fires before a character is inserted, and if the browser is - # replacing the previous un-accented character with an accented variant, it - # will select backward over it. - selectedLength = inputNode.selectionEnd - inputNode.selectionStart - @editor.selectLeft() if selectedLength is 1 - - insertedRange = @editor.insertText(event.data, groupUndo: true) - inputNode.value = event.data if insertedRange + @editor.insertText(event.data, groupUndo: true) + @detectedKeyPress = false + @imeMode = false onVerticalScroll: (scrollTop) => return if @updateRequested or scrollTop is @presenter.getScrollTop() From a99ee14ac0be502d6e18b56b8b9b84a5c1c472e9 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 4 Apr 2016 17:47:36 -0600 Subject: [PATCH 667/971] Make accented character menu detection work with left/right arrow keys --- spec/text-editor-component-spec.js | 3 ++- src/text-editor-component.coffee | 29 +++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 1d1e4eb9f..30bc9251c 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -3767,11 +3767,12 @@ describe('TextEditorComponent', function () { expect(editor.lineTextForBufferRow(0)).toBe('xyvar quicksort = function () {') }) - it('replaces the last character if the length of the input\'s value does not increase, as occurs with the accented character menu', async function () { + it('replaces the last character if the textInput event is followed by one or more additional keydown events, which occurs when the accented character menu is shown', async function () { componentNode.dispatchEvent(buildTextInputEvent({ data: 'u', target: inputNode })) + componentNode.dispatchEvent(new KeyboardEvent('keydown')) await nextViewUpdatePromise() expect(editor.lineTextForBufferRow(0)).toBe('uvar quicksort = function () {') diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 50851279b..8e91557c8 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -244,13 +244,28 @@ class TextEditorComponent listenForDOMEvents: -> @domNode.addEventListener 'mousewheel', @onMouseWheel @domNode.addEventListener 'textInput', @onTextInput - @domNode.addEventListener 'keypress', @onKeyPress @scrollViewNode.addEventListener 'mousedown', @onMouseDown @scrollViewNode.addEventListener 'scroll', @onScrollViewScroll + @detectAccentedCharacterMenu() @listenForIMEEvents() @trackSelectionClipboard() if process.platform is 'linux' + detectAccentedCharacterMenu: -> + # We need to get clever to detect when the accented character menu is + # opened on OS X. Usually, every keydown event that could cause input is + # paired with a corresponding keypress. However, when pressing and holding + # long enough to open the accented character menu causes additional keydown + # events to fire that aren't followed by their own keypress and textInput + # events. We exploit this by assuming the accented character menu is open + # until a subsequent keypress event proves otherwise. + @domNode.addEventListener 'keydown', (event) => + unless event.keyCode is 229 # Skip keydown events associated with IME compositionupdate events + @openedAccentedCharacterMenu = true + + @domNode.addEventListener 'keypress', => + @openedAccentedCharacterMenu = false + listenForIMEEvents: -> # The IME composition events work like this: # @@ -267,7 +282,9 @@ class TextEditorComponent checkpoint = null @domNode.addEventListener 'compositionstart', => - @imeMode = true + if @openedAccentedCharacterMenu + @editor.selectLeft() + @openedAccentedCharacterMenu = false checkpoint = @editor.createCheckpoint() @domNode.addEventListener 'compositionupdate', (event) => @editor.insertText(event.data, select: true) @@ -321,9 +338,6 @@ class TextEditorComponent if @mounted @presenter.setFocused(false) - onKeyPress: (event) => - @detectedKeyPress = true - onTextInput: (event) => event.stopPropagation() event.preventDefault() @@ -336,12 +350,11 @@ class TextEditorComponent # will occur twice, once for the initial character, and once for the # modified character. However, only a single keypress will have fired. If # this is the case, select backward to replace the original character. - if not @imeMode and not @detectedKeyPress + if @openedAccentedCharacterMenu @editor.selectLeft() + @openedAccentedCharacterMenu = false @editor.insertText(event.data, groupUndo: true) - @detectedKeyPress = false - @imeMode = false onVerticalScroll: (scrollTop) => return if @updateRequested or scrollTop is @presenter.getScrollTop() From 244f117d9504bbac439ce3cc44c475931c595493 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 4 Apr 2016 18:08:34 -0600 Subject: [PATCH 668/971] Handle empty client rects in LinesYardstick getBoundingClientRect returns garbage values if the range has zero width, which it does in the case of a fold placeholder or any other zero-width character. Sometimes getClientRects() returns an empty list, so we fall back to the bounding rect in these cases. --- src/lines-yardstick.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee index 4bdd43603..e90f267f1 100644 --- a/src/lines-yardstick.coffee +++ b/src/lines-yardstick.coffee @@ -108,4 +108,4 @@ class LinesYardstick clientRectForRange: (textNode, startIndex, endIndex) -> @rangeForMeasurement.setStart(textNode, startIndex) @rangeForMeasurement.setEnd(textNode, endIndex) - @rangeForMeasurement.getBoundingClientRect() + @rangeForMeasurement.getClientRects()[0] ? @rangeForMeasurement.getBoundingClientRect() From 9eff3a952b90900f106604ade3e52828e083b0e1 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 4 Apr 2016 20:18:59 -0400 Subject: [PATCH 669/971] :arrow_up: language-ruby@0.68.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a630dabfc..3089c4418 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ "language-php": "0.37.0", "language-property-list": "0.8.0", "language-python": "0.43.1", - "language-ruby": "0.68.4", + "language-ruby": "0.68.5", "language-ruby-on-rails": "0.25.0", "language-sass": "0.46.0", "language-shellscript": "0.21.1", From f638bcbb6d97a27a90d9d8b780659eb61d87de55 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 4 Apr 2016 18:56:08 -0600 Subject: [PATCH 670/971] =?UTF-8?q?Don=E2=80=99t=20assume=20the=20accented?= =?UTF-8?q?=20character=20menu=20on=20every=20IME=20event?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/text-editor-component.coffee | 35 +++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 8e91557c8..b76a23ddc 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -254,18 +254,43 @@ class TextEditorComponent detectAccentedCharacterMenu: -> # We need to get clever to detect when the accented character menu is # opened on OS X. Usually, every keydown event that could cause input is - # paired with a corresponding keypress. However, when pressing and holding + # followed by a corresponding keypress. However, pressing and holding # long enough to open the accented character menu causes additional keydown # events to fire that aren't followed by their own keypress and textInput - # events. We exploit this by assuming the accented character menu is open - # until a subsequent keypress event proves otherwise. + # events. + # + # Therefore, we assume the accented character menu has been deployed if, + # before observing any keyup event, we observe events in the following + # sequence: + # + # keydown(keyCode: X), keypress, keydown(codeCode: X) + # + # The keyCode X must be the same in the keydown events that bracket the + # keypress, meaning we're *holding* the _same_ key we intially pressed. + # Got that? + lastKeydown = null + lastKeydownBeforeKeypress = null + @domNode.addEventListener 'keydown', (event) => - unless event.keyCode is 229 # Skip keydown events associated with IME compositionupdate events - @openedAccentedCharacterMenu = true + if lastKeydownBeforeKeypress + if lastKeydownBeforeKeypress.keyCode is event.keyCode + @openedAccentedCharacterMenu = true + lastKeydownBeforeKeypress = null + else + lastKeydown = event @domNode.addEventListener 'keypress', => + lastKeydownBeforeKeypress = lastKeydown + lastKeydown = null + + # This cancels the accented character behavior if we type a key normally + # with the menu open. @openedAccentedCharacterMenu = false + @domNode.addEventListener 'keyup', => + lastKeydownBeforeKeypress = null + lastKeydown = null + listenForIMEEvents: -> # The IME composition events work like this: # From 9833e54ec349d9503c1facac44ad8b7e6bc09bc0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 4 Apr 2016 19:22:44 -0600 Subject: [PATCH 671/971] Fix typo --- src/text-editor-component.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index b76a23ddc..12f481be5 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -263,7 +263,7 @@ class TextEditorComponent # before observing any keyup event, we observe events in the following # sequence: # - # keydown(keyCode: X), keypress, keydown(codeCode: X) + # keydown(keyCode: X), keypress, keydown(keyCode: X) # # The keyCode X must be the same in the keydown events that bracket the # keypress, meaning we're *holding* the _same_ key we intially pressed. From 402a335eefa8c15b95c917496d29c740a4a1535f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 4 Apr 2016 19:50:39 -0600 Subject: [PATCH 672/971] Fix accented character menu spec --- spec/text-editor-component-spec.js | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 30bc9251c..b031cba33 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -3745,6 +3745,21 @@ describe('TextEditorComponent', function () { return event } + function buildKeydownEvent ({keyCode, target}) { + let event = new KeyboardEvent('keydown') + Object.defineProperty(event, 'keyCode', { + get: function () { + return keyCode + } + }) + Object.defineProperty(event, 'target', { + get: function () { + return target + } + }) + return event + } + let inputNode beforeEach(function () { @@ -3767,12 +3782,12 @@ describe('TextEditorComponent', function () { expect(editor.lineTextForBufferRow(0)).toBe('xyvar quicksort = function () {') }) - it('replaces the last character if the textInput event is followed by one or more additional keydown events, which occurs when the accented character menu is shown', async function () { - componentNode.dispatchEvent(buildTextInputEvent({ - data: 'u', - target: inputNode - })) - componentNode.dispatchEvent(new KeyboardEvent('keydown')) + it('replaces the last character if a keypress event is bracketed by keydown events with matching keyCodes, which occurs when the accented character menu is shown', async function () { + componentNode.dispatchEvent(buildKeydownEvent({keyCode: 85, target: inputNode})) + componentNode.dispatchEvent(buildTextInputEvent({data: 'u', target: inputNode})) + componentNode.dispatchEvent(new KeyboardEvent('keypress')) + componentNode.dispatchEvent(buildKeydownEvent({keyCode: 85, target: inputNode})) + componentNode.dispatchEvent(new KeyboardEvent('keyup')) await nextViewUpdatePromise() expect(editor.lineTextForBufferRow(0)).toBe('uvar quicksort = function () {') From fd3789223c16307925511c1c586b130980aac317 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 4 Apr 2016 19:53:41 -0600 Subject: [PATCH 673/971] :arrow_up: status-bar --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3089c4418..4c03b3916 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.235.1", "snippets": "1.0.2", "spell-check": "0.67.0", - "status-bar": "1.2.0", + "status-bar": "1.2.1", "styleguide": "0.45.2", "symbols-view": "0.112.0", "tabs": "0.92.0", From 108513f994d81ae611d43c5f2d81ac3e6933f951 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 5 Apr 2016 10:47:25 +0200 Subject: [PATCH 674/971] Fix LinesYardstick specs to use the new tagCode-based scope structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, remove specs testing RTL behavior. They don’t work as of today, and I think we need a better approach to handle them, that doesn’t solely rely on the DOM, but actually takes into account that e.g. (0, 0) is the rightmost character on a right-to-left string. --- spec/lines-yardstick-spec.coffee | 69 ++++++++++++++------------------ src/lines-component.coffee | 8 ---- src/lines-yardstick.coffee | 1 - src/text-editor-component.coffee | 7 +--- 4 files changed, 32 insertions(+), 53 deletions(-) diff --git a/spec/lines-yardstick-spec.coffee b/spec/lines-yardstick-spec.coffee index 46935510f..15ec2a533 100644 --- a/spec/lines-yardstick-spec.coffee +++ b/spec/lines-yardstick-spec.coffee @@ -19,36 +19,44 @@ describe "LinesYardstick", -> screenRowsToMeasure = [] buildLineNode = (screenRow) -> - tokenizedLine = editor.tokenizedLineForScreenRow(screenRow) - iterator = tokenizedLine.getTokenIterator() + startIndex = 0 + scopes = [] + screenLine = editor.screenLineForScreenRow(screenRow) lineNode = document.createElement("div") lineNode.style.whiteSpace = "pre" - while iterator.next() - span = document.createElement("span") - span.className = iterator.getScopes().join(' ').replace(/\.+/g, ' ') - span.textContent = iterator.getText() - lineNode.appendChild(span) + for tagCode in screenLine.tagCodes when tagCode isnt 0 + if editor.displayLayer.isCloseTagCode(tagCode) + scopes.pop() + else if editor.displayLayer.isOpenTagCode(tagCode) + scopes.push(editor.displayLayer.tagForCode(tagCode)) + else + text = screenLine.lineText.substr(startIndex, tagCode) + startIndex += tagCode + span = document.createElement("span") + span.className = scopes.join(' ').replace(/\.+/g, ' ') + span.textContent = text + lineNode.appendChild(span) jasmine.attachToDOM(lineNode) createdLineNodes.push(lineNode) lineNode mockLineNodesProvider = - lineNodeForLineIdAndScreenRow: (lineId, screenRow) -> + lineIdForScreenRow: (screenRow) -> + editor.screenLineForScreenRow(screenRow).id + + lineNodeForScreenRow: (screenRow) -> buildLineNode(screenRow) - textNodesForLineIdAndScreenRow: (lineId, screenRow) -> - lineNode = @lineNodeForLineIdAndScreenRow(lineId, screenRow) + textNodesForScreenRow: (screenRow) -> + lineNode = @lineNodeForScreenRow(screenRow) iterator = document.createNodeIterator(lineNode, NodeFilter.SHOW_TEXT) textNodes = [] - while textNode = iterator.nextNode() - textNodes.push(textNode) + textNodes.push(textNode) while textNode = iterator.nextNode() textNodes editor.setLineHeightInPixels(14) - lineTopIndex = new LineTopIndex({ - defaultLineHeight: editor.getLineHeightInPixels() - }) + lineTopIndex = new LineTopIndex({defaultLineHeight: editor.getLineHeightInPixels()}) linesYardstick = new LinesYardstick(editor, mockLineNodesProvider, lineTopIndex, atom.grammars) afterEach -> @@ -69,9 +77,9 @@ describe "LinesYardstick", -> expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 0))).toEqual({left: 0, top: 0}) expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 1))).toEqual({left: 7, top: 0}) - expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 5))).toEqual({left: 37.78125, top: 0}) - expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 6))).toEqual({left: 43.171875, top: 14}) - expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 9))).toEqual({left: 72.171875, top: 14}) + expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 5))).toEqual({left: 38, top: 0}) + expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 6))).toEqual({left: 43, top: 14}) + expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 9))).toEqual({left: 72, top: 14}) expect(linesYardstick.pixelPositionForScreenPosition(Point(2, Infinity))).toEqual({left: 287.859375, top: 28}) it "reuses already computed pixel positions unless it is invalidated", -> @@ -82,9 +90,9 @@ describe "LinesYardstick", -> } """ - expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 2))).toEqual({left: 19.203125, top: 14}) + expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 2))).toEqual({left: 19, top: 14}) expect(linesYardstick.pixelPositionForScreenPosition(Point(2, 6))).toEqual({left: 57.609375, top: 28}) - expect(linesYardstick.pixelPositionForScreenPosition(Point(5, 10))).toEqual({left: 95.609375, top: 70}) + expect(linesYardstick.pixelPositionForScreenPosition(Point(5, 10))).toEqual({left: 96, top: 70}) atom.styles.addStyleSheet """ * { @@ -92,9 +100,9 @@ describe "LinesYardstick", -> } """ - expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 2))).toEqual({left: 19.203125, top: 14}) + expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 2))).toEqual({left: 19, top: 14}) expect(linesYardstick.pixelPositionForScreenPosition(Point(2, 6))).toEqual({left: 57.609375, top: 28}) - expect(linesYardstick.pixelPositionForScreenPosition(Point(5, 10))).toEqual({left: 95.609375, top: 70}) + expect(linesYardstick.pixelPositionForScreenPosition(Point(5, 10))).toEqual({left: 96, top: 70}) linesYardstick.invalidateCache() @@ -102,23 +110,6 @@ describe "LinesYardstick", -> expect(linesYardstick.pixelPositionForScreenPosition(Point(2, 6))).toEqual({left: 72, top: 28}) expect(linesYardstick.pixelPositionForScreenPosition(Point(5, 10))).toEqual({left: 120, top: 70}) - it "correctly handles RTL characters", -> - atom.styles.addStyleSheet """ - * { - font-size: 14px; - font-family: monospace; - } - """ - - editor.setText("السلام عليكم") - expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 0)).left).toBe 0 - expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 1)).left).toBe 8 - expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 2)).left).toBe 16 - expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 5)).left).toBe 33 - expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 7)).left).toBe 50 - expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 9)).left).toBe 67 - expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 11)).left).toBe 84 - it "doesn't report a width greater than 0 when the character to measure is at the beginning of a text node", -> # This spec documents what seems to be a bug in Chromium, because we'd # expect that Range(0, 0).getBoundingClientRect().width to always be zero. diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 824655579..9a7ce7f44 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -97,14 +97,6 @@ class LinesComponent extends TiledComponent @presenter.setLineHeight(lineHeightInPixels) @presenter.setBaseCharacterWidth(defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth) - lineNodeForLineIdAndScreenRow: (lineId, screenRow) -> - tile = @presenter.tileForRow(screenRow) - @getComponentForTile(tile)?.lineNodeForLineId(lineId) - - textNodesForLineIdAndScreenRow: (lineId, screenRow) -> - tile = @presenter.tileForRow(screenRow) - @getComponentForTile(tile)?.textNodesForLineId(lineId) - lineIdForScreenRow: (screenRow) -> tile = @presenter.tileForRow(screenRow) @getComponentForTile(tile)?.lineIdForScreenRow(screenRow) diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee index e90f267f1..f53b6348e 100644 --- a/src/lines-yardstick.coffee +++ b/src/lines-yardstick.coffee @@ -5,7 +5,6 @@ TokenIterator = require './token-iterator' module.exports = class LinesYardstick constructor: (@model, @lineNodesProvider, @lineTopIndex, grammarRegistry) -> - @tokenIterator = new TokenIterator({grammarRegistry}) @rangeForMeasurement = document.createRange() @invalidateCache() diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 9b091100d..c5dc2929d 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -453,7 +453,7 @@ class TextEditorComponent unless @presenter.isRowVisible(screenPosition.row) @presenter.setScreenRowsToMeasure([screenPosition.row]) - unless @linesComponent.lineNodeForLineIdAndScreenRow(@presenter.lineIdForScreenRow(screenPosition.row), screenPosition.row)? + unless @linesComponent.lineNodeForScreenRow(screenPosition.row)? @updateSyncPreMeasurement() pixelPosition = @linesYardstick.pixelPositionForScreenPosition(screenPosition) @@ -849,10 +849,7 @@ class TextEditorComponent e.abortKeyBinding() unless @editor.consolidateSelections() lineNodeForScreenRow: (screenRow) -> - tileRow = @presenter.tileForRow(screenRow) - tileComponent = @linesComponent.getComponentForTile(tileRow) - - tileComponent?.lineNodeForScreenRow(screenRow) + @linesComponent.lineNodeForScreenRow(screenRow) lineNumberNodeForScreenRow: (screenRow) -> tileRow = @presenter.tileForRow(screenRow) From a083a754a57d21851406a50c6237b78277fedb6d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 5 Apr 2016 11:12:51 +0200 Subject: [PATCH 675/971] :green_heart: Fix TextEditorPresenter specs --- spec/text-editor-presenter-spec.coffee | 101 ++++++------------------- 1 file changed, 22 insertions(+), 79 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index d40828d77..a7beb3c57 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -1233,12 +1233,15 @@ describe "TextEditorPresenter", -> lineId = presenter.linesByScreenRow.get(row)?.id tilesState[presenter.tileForRow(row)]?.lines[lineId] - tokensIncludeTag = (tokens, tag) -> - includeTag = false - for {openTags, closeTags} in tokens - includeTag = true for openTag in openTags when openTag.indexOf(tag) isnt -1 - includeTag = true for closeTag in closeTags when closeTag.indexOf(tag) isnt -1 - includeTag + tagsForCodes = (presenter, tagCodes) -> + openTags = [] + closeTags = [] + for tagCode in tagCodes when tagCode < 0 # skip text codes + if presenter.isOpenTagCode(tagCode) + openTags.push(presenter.tagForCode(tagCode)) + else + closeTags.push(presenter.tagForCode(tagCode)) + {openTags, closeTags} tiledContentContract (presenter) -> getState(presenter).content @@ -1248,56 +1251,12 @@ describe "TextEditorPresenter", -> presenter.setExplicitHeight(3) expect(lineStateForScreenRow(presenter, 2)).toBeUndefined() - expectValues lineStateForScreenRow(presenter, 3), { - screenRow: 3, tokens: [ - {closeTags: [], openTags: ['text.plain.null-grammar', 'leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: [], text: 'var pivot = items.shift(), current, left = [], right = [];'}, - {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} - ] - } - expectValues lineStateForScreenRow(presenter, 4), { - screenRow: 4, tokens: [ - {closeTags: [], openTags: ['text.plain.null-grammar', 'leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: [], text: 'while(items.length > 0) {'}, - {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} - ] - } - expectValues lineStateForScreenRow(presenter, 5), { - screenRow: 5, tokens: [ - {closeTags: [], openTags: ['text.plain.null-grammar', 'leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: [], text: 'current = items.shift();'}, - {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} - ] - } - expectValues lineStateForScreenRow(presenter, 6), { - screenRow: 6, tokens: [ - {closeTags: [], openTags: ['text.plain.null-grammar', 'leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: [], text: 'current < pivot ? left.push(current) : right.push(current);'}, - {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} - ] - } - expectValues lineStateForScreenRow(presenter, 7), { - screenRow: 7, tokens: [ - {closeTags: [], openTags: ['text.plain.null-grammar', 'leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: [], text: '}'}, - {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} - ] - } - expectValues lineStateForScreenRow(presenter, 8), { - screenRow: 8, tokens: [ - {closeTags: [], openTags: ['text.plain.null-grammar', 'leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: ['leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: [], text: 'return sort(left).concat(pivot).concat(sort(right));'}, - {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} - ] - } + expectValues lineStateForScreenRow(presenter, 3), {screenRow: 3, tagCodes: editor.screenLineForScreenRow(3).tagCodes} + expectValues lineStateForScreenRow(presenter, 4), {screenRow: 4, tagCodes: editor.screenLineForScreenRow(4).tagCodes} + expectValues lineStateForScreenRow(presenter, 5), {screenRow: 5, tagCodes: editor.screenLineForScreenRow(5).tagCodes} + expectValues lineStateForScreenRow(presenter, 6), {screenRow: 6, tagCodes: editor.screenLineForScreenRow(6).tagCodes} + expectValues lineStateForScreenRow(presenter, 7), {screenRow: 7, tagCodes: editor.screenLineForScreenRow(7).tagCodes} + expectValues lineStateForScreenRow(presenter, 8), {screenRow: 8, tagCodes: editor.screenLineForScreenRow(8).tagCodes} expect(lineStateForScreenRow(presenter, 9)).toBeUndefined() it "updates when the editor's content changes", -> @@ -1305,36 +1264,20 @@ describe "TextEditorPresenter", -> expectStateUpdate presenter, -> buffer.insert([2, 0], "hello\nworld\n") - expectValues lineStateForScreenRow(presenter, 1), { - screenRow: 1, tokens: [ - {closeTags: [], openTags: ['text.plain.null-grammar', 'leading-whitespace'], text: ' '}, - {closeTags: ['leading-whitespace'], openTags: [], text: 'var sort = function(items) {'}, - {closeTags: ['text.plain.null-grammar'], openTags: [], text : ''} - ] - } - expectValues lineStateForScreenRow(presenter, 2), { - screenRow: 2, tokens: [ - {closeTags: [], openTags: ['text.plain.null-grammar'], text: 'hello'}, - {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} - ] - } - expectValues lineStateForScreenRow(presenter, 3), { - screenRow: 3, tokens: [ - {closeTags: [], openTags: ['text.plain.null-grammar'], text: 'world'}, - {closeTags: ['text.plain.null-grammar'], openTags: [], text: ''} - ] - } + expectValues lineStateForScreenRow(presenter, 1), {screenRow: 1, tagCodes: editor.screenLineForScreenRow(1).tagCodes} + expectValues lineStateForScreenRow(presenter, 2), {screenRow: 2, tagCodes: editor.screenLineForScreenRow(2).tagCodes} + expectValues lineStateForScreenRow(presenter, 3), {screenRow: 3, tagCodes: editor.screenLineForScreenRow(3).tagCodes} it "includes the .endOfLineInvisibles if the editor.showInvisibles config option is true", -> editor.setText("hello\nworld\r\n") presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, lineHeight: 10) - expect(tokensIncludeTag(lineStateForScreenRow(presenter, 0).tokens, 'eol')).toBe(false) - expect(tokensIncludeTag(lineStateForScreenRow(presenter, 1).tokens, 'eol')).toBe(false) + expect(tagsForCodes(presenter, lineStateForScreenRow(presenter, 0).tagCodes).openTags).not.toContain('invisible-character eol') + expect(tagsForCodes(presenter, lineStateForScreenRow(presenter, 1).tagCodes).openTags).not.toContain('invisible-character eol') atom.config.set('editor.showInvisibles', true) presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, lineHeight: 10) - expect(tokensIncludeTag(lineStateForScreenRow(presenter, 0).tokens, 'eol')).toBe(true) - expect(tokensIncludeTag(lineStateForScreenRow(presenter, 1).tokens, 'eol')).toBe(true) + expect(tagsForCodes(presenter, lineStateForScreenRow(presenter, 0).tagCodes).openTags).toContain('invisible-character eol') + expect(tagsForCodes(presenter, lineStateForScreenRow(presenter, 1).tagCodes).openTags).toContain('invisible-character eol') describe ".blockDecorations", -> it "contains all block decorations that are present before/after a line, both initially and when decorations change", -> From e6cfb8d5874c375b4f8116ceba8fa766d1d80a15 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 5 Apr 2016 11:28:16 +0200 Subject: [PATCH 676/971] Use a zero-width nbsp as our fold character --- spec/text-editor-spec.coffee | 2 +- src/display-buffer.coffee | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 6b551cf55..95003a498 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5352,7 +5352,7 @@ describe "TextEditor", -> # folds are also duplicated expect(editor.isFoldedAtScreenRow(5)).toBe(true) expect(editor.isFoldedAtScreenRow(7)).toBe(true) - expect(editor.lineTextForScreenRow(7)).toBe " while(items.length > 0) {⋯" + expect(editor.lineTextForScreenRow(7)).toBe " while(items.length > 0) {" + editor.displayLayer.foldCharacter expect(editor.lineTextForScreenRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));" it "duplicates all folded lines for empty selections on folded lines", -> diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 560aefb20..cfc352baf 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -9,6 +9,8 @@ Decoration = require './decoration' LayerDecoration = require './layer-decoration' {isDoubleWidthCharacter, isHalfWidthCharacter, isKoreanCharacter, isWrapBoundary} = require './text-utils' +ZERO_WIDTH_NBSP = '\ufeff' + class BufferToScreenConversionError extends Error constructor: (@message, @metadata) -> super @@ -123,8 +125,9 @@ class DisplayBuffer extends Model softWrapColumn: softWrapColumn showIndentGuides: @config.get('editor.showIndentGuide', scope: scopeDescriptor) tabLength: @getTabLength(), - ratioForCharacter: @ratioForCharacter.bind(this) - isWrapBoundary: isWrapBoundary + ratioForCharacter: @ratioForCharacter.bind(this), + isWrapBoundary: isWrapBoundary, + foldCharacter: ZERO_WIDTH_NBSP }) updateAllScreenLines: -> From 80b956e996aa62937d4f241e6bb0d3ce7d8268b3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 5 Apr 2016 11:39:06 +0200 Subject: [PATCH 677/971] :green_heart: Fix TextDecorationLayer API specs in TokenizedBuffer --- spec/tokenized-buffer-spec.coffee | 33 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index 4dec5bdf5..b90ae8779 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -1064,22 +1064,13 @@ describe "TokenizedBuffer", -> iterator = tokenizedBuffer.buildIterator() iterator.seek(Point(0, 0)) - boundaries = [] - loop - boundaries.push({ - position: iterator.getPosition(), - closeTags: iterator.getCloseTags(), - openTags: iterator.getOpenTags() - }) - break unless iterator.moveToSuccessor() - - expect(boundaries).toEqual([ + expectedBoundaries = [ {position: Point(0, 0), closeTags: [], openTags: ["source.js", "storage.type.var.js"]} {position: Point(0, 3), closeTags: ["storage.type.var.js"], openTags: []} {position: Point(0, 8), closeTags: [], openTags: ["keyword.operator.assignment.js"]} {position: Point(0, 9), closeTags: ["keyword.operator.assignment.js"], openTags: []} - {position: Point(0, 10), closeTags: [], openTags: ["constant.numeric.js"]} - {position: Point(0, 11), closeTags: ["constant.numeric.js"], openTags: []} + {position: Point(0, 10), closeTags: [], openTags: ["constant.numeric.decimal.js"]} + {position: Point(0, 11), closeTags: ["constant.numeric.decimal.js"], openTags: []} {position: Point(0, 12), closeTags: [], openTags: ["comment.block.js", "punctuation.definition.comment.js"]} {position: Point(0, 14), closeTags: ["punctuation.definition.comment.js"], openTags: []} {position: Point(1, 5), closeTags: [], openTags: ["punctuation.definition.comment.js"]} @@ -1087,9 +1078,19 @@ describe "TokenizedBuffer", -> {position: Point(1, 10), closeTags: ["storage.type.var.js"], openTags: []} {position: Point(1, 15), closeTags: [], openTags: ["keyword.operator.assignment.js"]} {position: Point(1, 16), closeTags: ["keyword.operator.assignment.js"], openTags: []} - {position: Point(1, 17), closeTags: [], openTags: ["constant.numeric.js"]} - {position: Point(1, 18), closeTags: ["constant.numeric.js"], openTags: []} - ]) + {position: Point(1, 17), closeTags: [], openTags: ["constant.numeric.decimal.js"]} + {position: Point(1, 18), closeTags: ["constant.numeric.decimal.js"], openTags: []} + ] + + loop + boundary = { + position: iterator.getPosition(), + closeTags: iterator.getCloseTags(), + openTags: iterator.getOpenTags() + } + + expect(boundary).toEqual(expectedBoundaries.shift()) + break unless iterator.moveToSuccessor() expect(iterator.seek(Point(0, 1))).toEqual(["source.js", "storage.type.var.js"]) expect(iterator.getPosition()).toEqual(Point(0, 3)) @@ -1097,7 +1098,7 @@ describe "TokenizedBuffer", -> expect(iterator.getPosition()).toEqual(Point(0, 8)) expect(iterator.seek(Point(1, 0))).toEqual(["source.js", "comment.block.js"]) expect(iterator.getPosition()).toEqual(Point(1, 5)) - expect(iterator.seek(Point(1, 18))).toEqual(["source.js", "constant.numeric.js"]) + expect(iterator.seek(Point(1, 18))).toEqual(["source.js", "constant.numeric.decimal.js"]) expect(iterator.getPosition()).toEqual(Point(1, 18)) expect(iterator.seek(Point(2, 0))).toEqual(["source.js"]) From 544b75c7b0829be2ee6ff5caeffc97ae54d12c2e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 5 Apr 2016 12:19:45 +0200 Subject: [PATCH 678/971] :fire: :green_heart: Fix TokenizedBuffer specs --- spec/tokenized-buffer-spec.coffee | 359 ++++-------------------------- src/token.coffee | 23 +- src/tokenized-line.coffee | 13 +- 3 files changed, 43 insertions(+), 352 deletions(-) diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index b90ae8779..f8746a511 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -134,13 +134,10 @@ describe "TokenizedBuffer", -> describe "on construction", -> it "initially creates un-tokenized screen lines, then tokenizes lines chunk at a time in the background", -> line0 = tokenizedBuffer.tokenizedLineForRow(0) - expect(line0.tokens.length).toBe 1 - expect(line0.tokens[0]).toEqual(value: line0.text, scopes: ['source.js']) + expect(line0.tokens).toEqual([value: line0.text, scopes: ['source.js']]) line11 = tokenizedBuffer.tokenizedLineForRow(11) - expect(line11.tokens.length).toBe 2 - expect(line11.tokens[0]).toEqual(value: " ", scopes: ['source.js'], isAtomic: true) - expect(line11.tokens[1]).toEqual(value: "return sort(Array.apply(this, arguments));", scopes: ['source.js']) + expect(line11.tokens).toEqual([value: " return sort(Array.apply(this, arguments));", scopes: ['source.js']]) # background tokenization has not begun expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack).toBeUndefined() @@ -236,7 +233,7 @@ describe "TokenizedBuffer", -> expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1]).toEqual(value: '(', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js', 'punctuation.definition.arguments.begin.bracket.round.js']) expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: '7', scopes: ['source.js', 'constant.numeric.decimal.js']) # line 2 is unchanged - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js']) + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js']) expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] @@ -283,9 +280,9 @@ describe "TokenizedBuffer", -> expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js']) # lines below deleted regions should be shifted upward - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js']) - expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js']) - expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.comparison.js']) + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js']) + expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[1]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js']) + expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[1]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.comparison.js']) expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] @@ -331,7 +328,7 @@ describe "TokenizedBuffer", -> expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js']) # previous line 3 is pushed down to become line 5 - expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js']) + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[3]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js']) expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] @@ -377,32 +374,6 @@ describe "TokenizedBuffer", -> expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeTruthy() expect(tokenizedBuffer.tokenizedLineForRow(6).ruleStack?).toBeTruthy() - it "tokenizes leading whitespace based on the new tab length", -> - expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].isAtomic).toBeTruthy() - expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].value).toBe " " - expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].isAtomic).toBeTruthy() - expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].value).toBe " " - - tokenizedBuffer.setTabLength(4) - fullyTokenize(tokenizedBuffer) - - expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].isAtomic).toBeTruthy() - expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].value).toBe " " - expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].isAtomic).toBeFalsy() - expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].value).toBe " current " - - it "does not tokenize whitespaces followed by combining characters as leading whitespace", -> - buffer.setText(" \u030b") - fullyTokenize(tokenizedBuffer) - - {tokens} = tokenizedBuffer.tokenizedLineForRow(0) - expect(tokens[0].value).toBe " " - expect(tokens[0].hasLeadingWhitespace()).toBe true - expect(tokens[1].value).toBe " " - expect(tokens[1].hasLeadingWhitespace()).toBe true - expect(tokens[2].value).toBe " \u030b" - expect(tokens[2].hasLeadingWhitespace()).toBe false - it "does not break out soft tabs across a scope boundary", -> waitsForPromise -> atom.packages.activatePackage('language-gfm') @@ -439,133 +410,6 @@ describe "TokenizedBuffer", -> beforeEach -> fullyTokenize(tokenizedBuffer) - it "renders each tab as its own atomic token with a value of size tabLength", -> - tabAsSpaces = _.multiplyString(' ', tokenizedBuffer.getTabLength()) - screenLine0 = tokenizedBuffer.tokenizedLineForRow(0) - expect(screenLine0.text).toBe "# Econ 101#{tabAsSpaces}" - {tokens} = screenLine0 - - expect(tokens.length).toBe 4 - expect(tokens[0].value).toBe "#" - expect(tokens[1].value).toBe " Econ 101" - expect(tokens[2].value).toBe tabAsSpaces - expect(tokens[2].scopes).toEqual tokens[1].scopes - expect(tokens[2].isAtomic).toBeTruthy() - expect(tokens[3].value).toBe "" - - expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "#{tabAsSpaces} buy()#{tabAsSpaces}while supply > demand" - - it "aligns the hard tabs to the correct tab stop column", -> - buffer.setText """ - 1\t2 \t3\t4 - 12\t3 \t4\t5 - 123\t4 \t5\t6 - """ - - tokenizedBuffer.setTabLength(4) - fullyTokenize(tokenizedBuffer) - - expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4" - expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 3 - - expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5" - expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 2 - - expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6" - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 1 - - tokenizedBuffer.setTabLength(3) - fullyTokenize(tokenizedBuffer) - - expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4" - expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 2 - - expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5" - expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 1 - - expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6" - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 3 - - tokenizedBuffer.setTabLength(2) - fullyTokenize(tokenizedBuffer) - - expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4" - expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 1 - - expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5" - expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 2 - - expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6" - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 1 - - tokenizedBuffer.setTabLength(1) - fullyTokenize(tokenizedBuffer) - - expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4" - expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 1 - - expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5" - expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 1 - - expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6" - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 1 - - describe "when the buffer contains UTF-8 surrogate pairs", -> - beforeEach -> - waitsForPromise -> - atom.packages.activatePackage('language-javascript') - - runs -> - buffer = atom.project.bufferForPathSync 'sample-with-pairs.js' - buffer.setText """ - 'abc\uD835\uDF97def' - //\uD835\uDF97xyz - """ - tokenizedBuffer = new TokenizedBuffer({ - buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert - }) - fullyTokenize(tokenizedBuffer) - - afterEach -> - tokenizedBuffer.destroy() - buffer.release() - - it "renders each UTF-8 surrogate pair as its own atomic token", -> - screenLine0 = tokenizedBuffer.tokenizedLineForRow(0) - expect(screenLine0.text).toBe "'abc\uD835\uDF97def'" - {tokens} = screenLine0 - - expect(tokens.length).toBe 5 - expect(tokens[0].value).toBe "'" - expect(tokens[1].value).toBe "abc" - expect(tokens[2].value).toBe "\uD835\uDF97" - expect(tokens[2].isAtomic).toBeTruthy() - expect(tokens[3].value).toBe "def" - expect(tokens[4].value).toBe "'" - - screenLine1 = tokenizedBuffer.tokenizedLineForRow(1) - expect(screenLine1.text).toBe "//\uD835\uDF97xyz" - {tokens} = screenLine1 - - expect(tokens.length).toBe 4 - expect(tokens[0].value).toBe '//' - expect(tokens[1].value).toBe '\uD835\uDF97' - expect(tokens[1].value).toBeTruthy() - expect(tokens[2].value).toBe 'xyz' - expect(tokens[3].value).toBe '' - describe "when the grammar is tokenized", -> it "emits the `tokenized` event", -> editor = null @@ -683,132 +527,7 @@ describe "TokenizedBuffer", -> it "returns the range covered by all contigous tokens (within a single line)", -> expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.function', [1, 18])).toEqual [[1, 6], [1, 28]] - describe "when the editor.tabLength config value changes", -> - it "updates the tab length of the tokenized lines", -> - buffer = atom.project.bufferForPathSync('sample.js') - buffer.setText('\ttest') - tokenizedBuffer = new TokenizedBuffer({ - buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert - }) - fullyTokenize(tokenizedBuffer) - expect(tokenizedBuffer.tokenForPosition([0, 0]).value).toBe ' ' - atom.config.set('editor.tabLength', 6) - expect(tokenizedBuffer.tokenForPosition([0, 0]).value).toBe ' ' - - it "does not allow the tab length to be less than 1", -> - buffer = atom.project.bufferForPathSync('sample.js') - buffer.setText('\ttest') - tokenizedBuffer = new TokenizedBuffer({ - buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert - }) - fullyTokenize(tokenizedBuffer) - expect(tokenizedBuffer.tokenForPosition([0, 0]).value).toBe ' ' - atom.config.set('editor.tabLength', 1) - expect(tokenizedBuffer.tokenForPosition([0, 0]).value).toBe ' ' - atom.config.set('editor.tabLength', 0) - expect(tokenizedBuffer.tokenForPosition([0, 0]).value).toBe ' ' - - describe "when the invisibles value changes", -> - beforeEach -> - - it "updates the tokens with the appropriate invisible characters", -> - buffer = new TextBuffer(text: " \t a line with tabs\tand \tspaces \t ") - tokenizedBuffer = new TokenizedBuffer({ - buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert - }) - fullyTokenize(tokenizedBuffer) - - atom.config.set("editor.showInvisibles", true) - atom.config.set("editor.invisibles", space: 'S', tab: 'T') - fullyTokenize(tokenizedBuffer) - - expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "SST Sa line with tabsTand T spacesSTS" - # Also needs to work for copies - expect(tokenizedBuffer.tokenizedLineForRow(0).copy().text).toBe "SST Sa line with tabsTand T spacesSTS" - - it "assigns endOfLineInvisibles to tokenized lines", -> - buffer = new TextBuffer(text: "a line that ends in a carriage-return-line-feed \r\na line that ends in just a line-feed\na line with no ending") - tokenizedBuffer = new TokenizedBuffer({ - buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert - }) - - atom.config.set('editor.showInvisibles', true) - atom.config.set("editor.invisibles", cr: 'R', eol: 'N') - fullyTokenize(tokenizedBuffer) - - expect(tokenizedBuffer.tokenizedLineForRow(0).endOfLineInvisibles).toEqual ['R', 'N'] - expect(tokenizedBuffer.tokenizedLineForRow(1).endOfLineInvisibles).toEqual ['N'] - - # Lines ending in soft wraps get no invisibles - [left, right] = tokenizedBuffer.tokenizedLineForRow(0).softWrapAt(20) - expect(left.endOfLineInvisibles).toBe null - expect(right.endOfLineInvisibles).toEqual ['R', 'N'] - - atom.config.set("editor.invisibles", cr: 'R', eol: false) - expect(tokenizedBuffer.tokenizedLineForRow(0).endOfLineInvisibles).toEqual ['R'] - expect(tokenizedBuffer.tokenizedLineForRow(1).endOfLineInvisibles).toEqual [] - - describe "leading and trailing whitespace", -> - beforeEach -> - buffer = atom.project.bufferForPathSync('sample.js') - tokenizedBuffer = new TokenizedBuffer({ - buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert - }) - fullyTokenize(tokenizedBuffer) - - it "assigns ::firstNonWhitespaceIndex on tokens that have leading whitespace", -> - expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0].firstNonWhitespaceIndex).toBe null - expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].firstNonWhitespaceIndex).toBe 2 - expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].firstNonWhitespaceIndex).toBe null - - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].firstNonWhitespaceIndex).toBe 2 - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].firstNonWhitespaceIndex).toBe 2 - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2].firstNonWhitespaceIndex).toBe null - - # The 4th token *has* leading whitespace, but isn't entirely whitespace - buffer.insert([5, 0], ' ') - expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[3].firstNonWhitespaceIndex).toBe 1 - expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[4].firstNonWhitespaceIndex).toBe null - - # Lines that are *only* whitespace are not considered to have leading whitespace - buffer.insert([10, 0], ' ') - expect(tokenizedBuffer.tokenizedLineForRow(10).tokens[0].firstNonWhitespaceIndex).toBe null - - it "assigns ::firstTrailingWhitespaceIndex on tokens that have trailing whitespace", -> - buffer.insert([0, Infinity], ' ') - expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[11].firstTrailingWhitespaceIndex).toBe null - expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[12].firstTrailingWhitespaceIndex).toBe 0 - - # The last token *has* trailing whitespace, but isn't entirely whitespace - buffer.setTextInRange([[2, 39], [2, 40]], ' ') - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[14].firstTrailingWhitespaceIndex).toBe null - expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[15].firstTrailingWhitespaceIndex).toBe 6 - - # Lines that are *only* whitespace are considered to have trailing whitespace - buffer.insert([10, 0], ' ') - expect(tokenizedBuffer.tokenizedLineForRow(10).tokens[0].firstTrailingWhitespaceIndex).toBe 0 - - it "only marks trailing whitespace on the last segment of a soft-wrapped line", -> - buffer.insert([0, Infinity], ' ') - tokenizedLine = tokenizedBuffer.tokenizedLineForRow(0) - [segment1, segment2] = tokenizedLine.softWrapAt(16) - expect(segment1.tokens[5].value).toBe ' ' - expect(segment1.tokens[5].firstTrailingWhitespaceIndex).toBe null - expect(segment2.tokens[6].value).toBe ' ' - expect(segment2.tokens[6].firstTrailingWhitespaceIndex).toBe 0 - - it "sets leading and trailing whitespace correctly on a line with invisible characters that is copied", -> - buffer.setText(" \t a line with tabs\tand \tspaces \t ") - - atom.config.set("editor.showInvisibles", true) - atom.config.set("editor.invisibles", space: 'S', tab: 'T') - fullyTokenize(tokenizedBuffer) - - line = tokenizedBuffer.tokenizedLineForRow(0).copy() - expect(line.tokens[0].firstNonWhitespaceIndex).toBe 2 - expect(line.tokens[line.tokens.length - 1].firstTrailingWhitespaceIndex).toBe 0 - - describe ".indentLevel on tokenized lines", -> + describe ".indentLevelForRow(row)", -> beforeEach -> buffer = atom.project.bufferForPathSync('sample.js') tokenizedBuffer = new TokenizedBuffer({ @@ -818,43 +537,43 @@ describe "TokenizedBuffer", -> describe "when the line is non-empty", -> it "has an indent level based on the leading whitespace on the line", -> - expect(tokenizedBuffer.tokenizedLineForRow(0).indentLevel).toBe 0 - expect(tokenizedBuffer.tokenizedLineForRow(1).indentLevel).toBe 1 - expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2 + expect(tokenizedBuffer.indentLevelForRow(0)).toBe 0 + expect(tokenizedBuffer.indentLevelForRow(1)).toBe 1 + expect(tokenizedBuffer.indentLevelForRow(2)).toBe 2 buffer.insert([2, 0], ' ') - expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2.5 + expect(tokenizedBuffer.indentLevelForRow(2)).toBe 2.5 describe "when the line is empty", -> it "assumes the indentation level of the first non-empty line below or above if one exists", -> buffer.insert([12, 0], ' ') buffer.insert([12, Infinity], '\n\n') - expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2 - expect(tokenizedBuffer.tokenizedLineForRow(14).indentLevel).toBe 2 + expect(tokenizedBuffer.indentLevelForRow(13)).toBe 2 + expect(tokenizedBuffer.indentLevelForRow(14)).toBe 2 buffer.insert([1, Infinity], '\n\n') - expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2 - expect(tokenizedBuffer.tokenizedLineForRow(3).indentLevel).toBe 2 + expect(tokenizedBuffer.indentLevelForRow(2)).toBe 2 + expect(tokenizedBuffer.indentLevelForRow(3)).toBe 2 buffer.setText('\n\n\n') - expect(tokenizedBuffer.tokenizedLineForRow(1).indentLevel).toBe 0 + expect(tokenizedBuffer.indentLevelForRow(1)).toBe 0 describe "when the changed lines are surrounded by whitespace-only lines", -> it "updates the indentLevel of empty lines that precede the change", -> - expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 0 + expect(tokenizedBuffer.indentLevelForRow(12)).toBe 0 buffer.insert([12, 0], '\n') buffer.insert([13, 0], ' ') - expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 1 + expect(tokenizedBuffer.indentLevelForRow(12)).toBe 1 it "updates empty line indent guides when the empty line is the last line", -> buffer.insert([12, 2], '\n') # The newline and the tab need to be in two different operations to surface the bug buffer.insert([12, 0], ' ') - expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 1 + expect(tokenizedBuffer.indentLevelForRow(13)).toBe 1 buffer.insert([12, 0], ' ') - expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2 + expect(tokenizedBuffer.indentLevelForRow(13)).toBe 2 expect(tokenizedBuffer.tokenizedLineForRow(14)).not.toBeDefined() it "updates the indentLevel of empty lines surrounding a change that inserts lines", -> @@ -862,24 +581,24 @@ describe "TokenizedBuffer", -> buffer.insert([7, 0], '\n\n') buffer.insert([5, 0], '\n\n') - expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 3 - expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 3 - expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 3 - expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 3 - expect(tokenizedBuffer.tokenizedLineForRow(11).indentLevel).toBe 2 + expect(tokenizedBuffer.indentLevelForRow(5)).toBe 3 + expect(tokenizedBuffer.indentLevelForRow(6)).toBe 3 + expect(tokenizedBuffer.indentLevelForRow(9)).toBe 3 + expect(tokenizedBuffer.indentLevelForRow(10)).toBe 3 + expect(tokenizedBuffer.indentLevelForRow(11)).toBe 2 tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler') buffer.setTextInRange([[7, 0], [8, 65]], ' one\n two\n three\n four') delete changeHandler.argsForCall[0][0].bufferChange - expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 10, delta: 2) + expect(changeHandler).toHaveBeenCalledWith(start: 7, end: 8, delta: 2) - expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 4 - expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 4 - expect(tokenizedBuffer.tokenizedLineForRow(11).indentLevel).toBe 4 - expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 4 - expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2 + expect(tokenizedBuffer.indentLevelForRow(5)).toBe 4 + expect(tokenizedBuffer.indentLevelForRow(6)).toBe 4 + expect(tokenizedBuffer.indentLevelForRow(11)).toBe 4 + expect(tokenizedBuffer.indentLevelForRow(12)).toBe 4 + expect(tokenizedBuffer.indentLevelForRow(13)).toBe 2 it "updates the indentLevel of empty lines surrounding a change that removes lines", -> # create some new lines @@ -891,14 +610,14 @@ describe "TokenizedBuffer", -> buffer.setTextInRange([[7, 0], [8, 65]], ' ok') delete changeHandler.argsForCall[0][0].bufferChange - expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 10, delta: -1) + expect(changeHandler).toHaveBeenCalledWith(start: 7, end: 8, delta: -1) - expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 2 - expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 2 - expect(tokenizedBuffer.tokenizedLineForRow(7).indentLevel).toBe 2 # new text - expect(tokenizedBuffer.tokenizedLineForRow(8).indentLevel).toBe 2 - expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 2 - expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 2 # } + expect(tokenizedBuffer.indentLevelForRow(5)).toBe 2 + expect(tokenizedBuffer.indentLevelForRow(6)).toBe 2 + expect(tokenizedBuffer.indentLevelForRow(7)).toBe 2 # new text + expect(tokenizedBuffer.indentLevelForRow(8)).toBe 2 + expect(tokenizedBuffer.indentLevelForRow(9)).toBe 2 + expect(tokenizedBuffer.indentLevelForRow(10)).toBe 2 # } describe "::isFoldableAtRow(row)", -> changes = null diff --git a/src/token.coffee b/src/token.coffee index 60e8194f8..5e1f1e811 100644 --- a/src/token.coffee +++ b/src/token.coffee @@ -7,22 +7,10 @@ WhitespaceRegex = /\S/ module.exports = class Token value: null - hasPairedCharacter: false scopes: null - isAtomic: null - isHardTab: null - firstNonWhitespaceIndex: null - firstTrailingWhitespaceIndex: null - hasInvisibleCharacters: false constructor: (properties) -> - {@value, @scopes, @isAtomic, @isHardTab, @bufferDelta} = properties - {@hasInvisibleCharacters, @hasPairedCharacter, @isSoftWrapIndentation} = properties - @firstNonWhitespaceIndex = properties.firstNonWhitespaceIndex ? null - @firstTrailingWhitespaceIndex = properties.firstTrailingWhitespaceIndex ? null - - @screenDelta = @value.length - @bufferDelta ?= @screenDelta + {@value, @scopes} = properties isEqual: (other) -> # TODO: scopes is deprecated. This is here for the sake of lang package tests @@ -31,17 +19,8 @@ class Token isBracket: -> /^meta\.brace\b/.test(_.last(@scopes)) - isOnlyWhitespace: -> - not WhitespaceRegex.test(@value) - matchesScopeSelector: (selector) -> targetClasses = selector.replace(StartDotRegex, '').split('.') _.any @scopes, (scope) -> scopeClasses = scope.split('.') _.isSubset(targetClasses, scopeClasses) - - hasLeadingWhitespace: -> - @firstNonWhitespaceIndex? and @firstNonWhitespaceIndex > 0 - - hasTrailingWhitespace: -> - @firstTrailingWhitespaceIndex? and @firstTrailingWhitespaceIndex < @value.length diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index 8574cb960..f8faad865 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -7,10 +7,6 @@ idCounter = 1 module.exports = class TokenizedLine - endOfLineInvisibles: null - lineIsWhitespaceOnly: false - firstNonWhitespaceIndex: 0 - constructor: (properties) -> @id = idCounter++ @@ -36,10 +32,10 @@ class TokenizedLine @tokens[@tokenIndexAtBufferColumn(bufferColumn)] tokenIndexAtBufferColumn: (bufferColumn) -> - delta = 0 + column = 0 for token, index in @tokens - delta += token.bufferDelta - return index if delta > bufferColumn + column += token.value.length + return index if column > bufferColumn index - 1 tokenStartColumnForBufferColumn: (bufferColumn) -> @@ -65,9 +61,6 @@ class TokenizedLine break @isCommentLine - isOnlyWhitespace: -> - @lineIsWhitespaceOnly - tokenAtIndex: (index) -> @tokens[index] From 712b1f1f88d758486f83c17134f1f3cd0000b722 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 5 Apr 2016 14:19:45 +0200 Subject: [PATCH 679/971] Fix LanguageMode specs Also, avoid creating folds twice for the same position when calling `foldAll`. --- spec/language-mode-spec.coffee | 108 +++++++++++---------------------- src/language-mode.coffee | 12 +++- 2 files changed, 47 insertions(+), 73 deletions(-) diff --git a/spec/language-mode-spec.coffee b/spec/language-mode-spec.coffee index 26bb19b0e..d8f545abe 100644 --- a/spec/language-mode-spec.coffee +++ b/spec/language-mode-spec.coffee @@ -334,66 +334,56 @@ describe "LanguageMode", -> it "folds every foldable line", -> languageMode.foldAll() - fold1 = editor.tokenizedLineForScreenRow(0).fold - expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 12] - fold1.destroy() - - fold2 = editor.tokenizedLineForScreenRow(1).fold - expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 9] - fold2.destroy() - - fold3 = editor.tokenizedLineForScreenRow(4).fold - expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [4, 7] + [fold1, fold2, fold3] = languageMode.unfoldAll() + expect([fold1.start.row, fold1.end.row]).toEqual [0, 12] + expect([fold2.start.row, fold2.end.row]).toEqual [1, 9] + expect([fold3.start.row, fold3.end.row]).toEqual [4, 7] describe ".foldBufferRow(bufferRow)", -> describe "when bufferRow can be folded", -> it "creates a fold based on the syntactic region starting at the given row", -> languageMode.foldBufferRow(1) - fold = editor.tokenizedLineForScreenRow(1).fold - expect(fold.getStartRow()).toBe 1 - expect(fold.getEndRow()).toBe 9 + [fold] = languageMode.unfoldAll() + expect([fold.start.row, fold.end.row]).toEqual [1, 9] describe "when bufferRow can't be folded", -> it "searches upward for the first row that begins a syntatic region containing the given buffer row (and folds it)", -> languageMode.foldBufferRow(8) - fold = editor.tokenizedLineForScreenRow(1).fold - expect(fold.getStartRow()).toBe 1 - expect(fold.getEndRow()).toBe 9 + [fold] = languageMode.unfoldAll() + expect([fold.start.row, fold.end.row]).toEqual [1, 9] describe "when the bufferRow is already folded", -> it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", -> languageMode.foldBufferRow(2) - expect(editor.tokenizedLineForScreenRow(1).fold).toBeDefined() - expect(editor.tokenizedLineForScreenRow(0).fold).not.toBeDefined() + expect(editor.isFoldedAtBufferRow(0)).toBe(false) + expect(editor.isFoldedAtBufferRow(1)).toBe(true) languageMode.foldBufferRow(1) - expect(editor.tokenizedLineForScreenRow(0).fold).toBeDefined() + expect(editor.isFoldedAtBufferRow(0)).toBe(true) describe "when the bufferRow is in a multi-line comment", -> it "searches upward and downward for surrounding comment lines and folds them as a single fold", -> buffer.insert([1, 0], " //this is a comment\n // and\n //more docs\n\n//second comment") languageMode.foldBufferRow(1) - fold = editor.tokenizedLineForScreenRow(1).fold - expect(fold.getStartRow()).toBe 1 - expect(fold.getEndRow()).toBe 3 + [fold] = languageMode.unfoldAll() + expect([fold.start.row, fold.end.row]).toEqual [1, 3] describe "when the bufferRow is a single-line comment", -> it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", -> buffer.insert([1, 0], " //this is a single line comment\n") languageMode.foldBufferRow(1) - fold = editor.tokenizedLineForScreenRow(0).fold - expect(fold.getStartRow()).toBe 0 - expect(fold.getEndRow()).toBe 13 + [fold] = languageMode.unfoldAll() + expect([fold.start.row, fold.end.row]).toEqual [0, 13] describe ".foldAllAtIndentLevel(indentLevel)", -> it "folds blocks of text at the given indentation level", -> languageMode.foldAllAtIndentLevel(0) - expect(editor.lineTextForScreenRow(0)).toBe "var quicksort = function () {" + expect(editor.lineTextForScreenRow(0)).toBe "var quicksort = function () {" + editor.displayLayer.foldCharacter expect(editor.getLastScreenRow()).toBe 0 languageMode.foldAllAtIndentLevel(1) expect(editor.lineTextForScreenRow(0)).toBe "var quicksort = function () {" - expect(editor.lineTextForScreenRow(1)).toBe " var sort = function(items) {" + expect(editor.lineTextForScreenRow(1)).toBe " var sort = function(items) {" + editor.displayLayer.foldCharacter expect(editor.getLastScreenRow()).toBe 4 languageMode.foldAllAtIndentLevel(2) @@ -429,59 +419,35 @@ describe "LanguageMode", -> it "folds every foldable line", -> languageMode.foldAll() - fold1 = editor.tokenizedLineForScreenRow(0).fold - expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 30] - fold1.destroy() - - fold2 = editor.tokenizedLineForScreenRow(1).fold - expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 4] - - fold3 = editor.tokenizedLineForScreenRow(2).fold.destroy() - - fold4 = editor.tokenizedLineForScreenRow(3).fold - expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [6, 8] - - fold5 = editor.tokenizedLineForScreenRow(6).fold - expect([fold5.getStartRow(), fold5.getEndRow()]).toEqual [11, 16] - fold5.destroy() - - fold6 = editor.tokenizedLineForScreenRow(13).fold - expect([fold6.getStartRow(), fold6.getEndRow()]).toEqual [21, 22] - fold6.destroy() + folds = languageMode.unfoldAll() + expect(folds.length).toBe 8 + expect([folds[0].start.row, folds[0].end.row]).toEqual [0, 30] + expect([folds[1].start.row, folds[1].end.row]).toEqual [1, 4] + expect([folds[2].start.row, folds[2].end.row]).toEqual [5, 27] + expect([folds[3].start.row, folds[3].end.row]).toEqual [6, 8] + expect([folds[4].start.row, folds[4].end.row]).toEqual [11, 16] + expect([folds[5].start.row, folds[5].end.row]).toEqual [17, 20] + expect([folds[6].start.row, folds[6].end.row]).toEqual [21, 22] + expect([folds[7].start.row, folds[7].end.row]).toEqual [24, 25] describe ".foldAllAtIndentLevel()", -> it "folds every foldable range at a given indentLevel", -> languageMode.foldAllAtIndentLevel(2) - fold1 = editor.tokenizedLineForScreenRow(6).fold - expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [6, 8] - fold1.destroy() - - fold2 = editor.tokenizedLineForScreenRow(11).fold - expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [11, 16] - fold2.destroy() - - fold3 = editor.tokenizedLineForScreenRow(17).fold - expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [17, 20] - fold3.destroy() - - fold4 = editor.tokenizedLineForScreenRow(21).fold - expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [21, 22] - fold4.destroy() - - fold5 = editor.tokenizedLineForScreenRow(24).fold - expect([fold5.getStartRow(), fold5.getEndRow()]).toEqual [24, 25] - fold5.destroy() + folds = languageMode.unfoldAll() + expect(folds.length).toBe 5 + expect([folds[0].start.row, folds[0].end.row]).toEqual [6, 8] + expect([folds[1].start.row, folds[1].end.row]).toEqual [11, 16] + expect([folds[2].start.row, folds[2].end.row]).toEqual [17, 20] + expect([folds[3].start.row, folds[3].end.row]).toEqual [21, 22] + expect([folds[4].start.row, folds[4].end.row]).toEqual [24, 25] it "does not fold anything but the indentLevel", -> languageMode.foldAllAtIndentLevel(0) - fold1 = editor.tokenizedLineForScreenRow(0).fold - expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 30] - fold1.destroy() - - fold2 = editor.tokenizedLineForScreenRow(5).fold - expect(fold2).toBeFalsy() + folds = languageMode.unfoldAll() + expect(folds.length).toBe 1 + expect([folds[0].start.row, folds[0].end.row]).toEqual [0, 30] describe ".isFoldableAtBufferRow(bufferRow)", -> it "returns true if the line starts a multi-line comment", -> diff --git a/src/language-mode.coffee b/src/language-mode.coffee index e60c661b2..605f8454b 100644 --- a/src/language-mode.coffee +++ b/src/language-mode.coffee @@ -90,10 +90,15 @@ class LanguageMode # Folds all the foldable lines in the buffer. foldAll: -> + @unfoldAll() + foldedRowRanges = {} for currentRow in [0..@buffer.getLastRow()] by 1 - [startRow, endRow] = @rowRangeForFoldAtBufferRow(currentRow) ? [] + rowRange = [startRow, endRow] = @rowRangeForFoldAtBufferRow(currentRow) ? [] continue unless startRow? + continue if foldedRowRanges[rowRange] + @editor.foldBufferRowRange(startRow, endRow) + foldedRowRanges[rowRange] = true return # Unfolds all the foldable lines in the buffer. @@ -105,13 +110,16 @@ class LanguageMode # indentLevel - A {Number} indicating indentLevel; 0 based. foldAllAtIndentLevel: (indentLevel) -> @unfoldAll() + foldedRowRanges = {} for currentRow in [0..@buffer.getLastRow()] by 1 - [startRow, endRow] = @rowRangeForFoldAtBufferRow(currentRow) ? [] + rowRange = [startRow, endRow] = @rowRangeForFoldAtBufferRow(currentRow) ? [] continue unless startRow? + continue if foldedRowRanges[rowRange] # assumption: startRow will always be the min indent level for the entire range if @editor.indentationForBufferRow(startRow) is indentLevel @editor.foldBufferRowRange(startRow, endRow) + foldedRowRanges[rowRange] = true return # Given a buffer row, creates a fold at it. From 0cf0d6f587b9febf377b2caf596b161aab833514 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 5 Apr 2016 15:38:52 +0200 Subject: [PATCH 680/971] :fire: Remove unused code Now that `DisplayLayer` was fully implemented, we can start deleting those codepaths in `DisplayBuffer` that are now covered by this new abstraction. --- src/display-buffer.coffee | 340 +------------------------------------- 1 file changed, 2 insertions(+), 338 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index cfc352baf..63508e4cb 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -67,11 +67,8 @@ class DisplayBuffer extends Model @layerUpdateDisposablesByLayerId = {} @disposables.add @tokenizedBuffer.observeGrammar @subscribeToScopedConfigSettings - @disposables.add @tokenizedBuffer.onDidChange @handleTokenizedBufferChange @disposables.add @buffer.onDidCreateMarker @didCreateDefaultLayerMarker - @updateAllScreenLines() - subscribeToScopedConfigSettings: => @scopedConfigSubscriptions?.dispose() @scopedConfigSubscriptions = subscriptions = new CompositeDisposable @@ -130,13 +127,6 @@ class DisplayBuffer extends Model foldCharacter: ZERO_WIDTH_NBSP }) - updateAllScreenLines: -> - return # TODO: After DisplayLayer is finished, delete these code paths - @maxLineLength = 0 - @screenLines = [] - @rowMap = new RowMap - @updateScreenLines(0, @buffer.getLineCount(), null, suppressChangeEvent: true) - onDidChangeSoftWrapped: (callback) -> @emitter.on 'did-change-soft-wrapped', callback @@ -174,20 +164,6 @@ class DisplayBuffer extends Model onDidUpdateDecorations: (callback) -> @emitter.on 'did-update-decorations', callback - emitDidChange: (eventProperties, refreshMarkers=true) -> - @emitter.emit 'did-change', eventProperties - if refreshMarkers - @refreshMarkerScreenPositions() - @emitter.emit 'did-update-markers' - - updateWrappedScreenLines: -> - start = 0 - end = @getLastRow() - @updateAllScreenLines() - screenDelta = @getLastRow() - end - bufferDelta = 0 - @emitDidChange({start, end, screenDelta, bufferDelta}) - # Sets the visibility of the tokenized buffer. # # visible - A {Boolean} indicating of the tokenized buffer is shown @@ -324,82 +300,6 @@ class DisplayBuffer extends Model else @editorWidthInChars - getSoftWrapColumn: -> - if @configSettings.softWrapAtPreferredLineLength - Math.min(@getEditorWidthInChars(), @configSettings.preferredLineLength) - else - @getEditorWidthInChars() - - getSoftWrapColumnForTokenizedLine: (tokenizedLine) -> - lineMaxWidth = @getSoftWrapColumn() * @getDefaultCharWidth() - - return if Number.isNaN(lineMaxWidth) - return 0 if lineMaxWidth is 0 - - iterator = tokenizedLine.getTokenIterator(false) - column = 0 - currentWidth = 0 - while iterator.next() - textIndex = 0 - text = iterator.getText() - while textIndex < text.length - if iterator.isPairedCharacter() - charLength = 2 - else - charLength = 1 - - if iterator.hasDoubleWidthCharacterAt(textIndex) - charWidth = @getDoubleWidthCharWidth() - else if iterator.hasHalfWidthCharacterAt(textIndex) - charWidth = @getHalfWidthCharWidth() - else if iterator.hasKoreanCharacterAt(textIndex) - charWidth = @getKoreanCharWidth() - else - charWidth = @getDefaultCharWidth() - - return column if currentWidth + charWidth > lineMaxWidth - - currentWidth += charWidth - column += charLength - textIndex += charLength - column - - # Gets the screen line for the given screen row. - # - # * `screenRow` - A {Number} indicating the screen row. - # - # Returns {TokenizedLine} - tokenizedLineForScreenRow: (screenRow) -> - if @largeFileMode - if line = @tokenizedBuffer.tokenizedLineForRow(screenRow) - if line.text.length > @maxLineLength - @maxLineLength = line.text.length - @longestScreenRow = screenRow - line - else - @screenLines[screenRow] - - # Gets the screen lines for the given screen row range. - # - # startRow - A {Number} indicating the beginning screen row. - # endRow - A {Number} indicating the ending screen row. - # - # Returns an {Array} of {TokenizedLine}s. - tokenizedLinesForScreenRows: (startRow, endRow) -> - if @largeFileMode - @tokenizedBuffer.tokenizedLinesForRows(startRow, endRow) - else - @screenLines[startRow..endRow] - - # Gets all the screen lines. - # - # Returns an {Array} of {TokenizedLine}s. - getTokenizedLines: -> - if @largeFileMode - @tokenizedBuffer.tokenizedLinesForRows(0, @getLastRow()) - else - new Array(@screenLines...) - indentLevelForLine: (line) -> @tokenizedBuffer.indentLevelForLine(line) @@ -438,21 +338,6 @@ class DisplayBuffer extends Model unfoldBufferRow: (bufferRow) -> @displayLayer.destroyFoldsIntersectingBufferRange(Range(Point(bufferRow, 0), Point(bufferRow, Infinity))) - # Returns the folds in the given row range (exclusive of end row) that are - # not contained by any other folds. - outermostFoldsInBufferRowRange: (startRow, endRow) -> - folds = [] - lastFoldEndRow = -1 - - for marker in @findFoldMarkers(intersectsRowRange: [startRow, endRow]) - range = marker.getRange() - if range.start.row > lastFoldEndRow - lastFoldEndRow = range.end.row - if startRow <= range.start.row <= range.end.row < endRow - folds.push(@foldForMarker(marker)) - - folds - # Given a buffer row, this converts it into a screen row. # # bufferRow - A {Number} representing a buffer row @@ -512,18 +397,6 @@ class DisplayBuffer extends Model getLastRow: -> @getLineCount() - 1 - # Gets the length of the longest screen line. - # - # Returns a {Number}. - getMaxLineLength: -> - @maxLineLength - - # Gets the row number of the longest screen line. - # - # Return a {} - getLongestScreenRow: -> - @longestScreenRow - # Given a buffer position, this converts it into a screen position. # # bufferPosition - An object that represents a buffer position. It can be either @@ -536,35 +409,7 @@ class DisplayBuffer extends Model screenPositionForBufferPosition: (bufferPosition, options) -> throw new Error("This TextEditor has been destroyed") if @isDestroyed() - return @displayLayer.translateBufferPosition(bufferPosition, options) - # TODO: should DisplayLayer deal with options.wrapBeyondNewlines / options.wrapAtSoftNewlines? - # {row, column} = @buffer.clipPosition(bufferPosition) - # [startScreenRow, endScreenRow] = @rowMap.screenRowRangeForBufferRow(row) - # for screenRow in [startScreenRow...endScreenRow] - # screenLine = @tokenizedLineForScreenRow(screenRow) - # - # unless screenLine? - # throw new BufferToScreenConversionError "No screen line exists when converting buffer row to screen row", - # softWrapEnabled: @isSoftWrapped() - # lastBufferRow: @buffer.getLastRow() - # lastScreenRow: @getLastRow() - # bufferRow: row - # screenRow: screenRow - # displayBufferChangeCount: @changeCount - # tokenizedBufferChangeCount: @tokenizedBuffer.changeCount - # bufferChangeCount: @buffer.changeCount - # - # maxBufferColumn = screenLine.getMaxBufferColumn() - # if screenLine.isSoftWrapped() and column > maxBufferColumn - # continue - # else - # if column <= maxBufferColumn - # screenColumn = screenLine.screenColumnForBufferColumn(column) - # else - # screenColumn = Infinity - # break - # - # @clipScreenPosition([screenRow, screenColumn], options) + @displayLayer.translateBufferPosition(bufferPosition, options) # Given a buffer position, this converts it into a screen position. # @@ -577,10 +422,6 @@ class DisplayBuffer extends Model # Returns a {Point}. bufferPositionForScreenPosition: (screenPosition, options) -> return @displayLayer.translateScreenPosition(screenPosition, options) - # TODO: should DisplayLayer deal with options.wrapBeyondNewlines / options.wrapAtSoftNewlines? - # {row, column} = @clipScreenPosition(Point.fromObject(screenPosition), options) - # [bufferRow] = @rowMap.bufferRowRangeForScreenRow(row) - # new Point(bufferRow, @tokenizedLineForScreenRow(row).bufferColumnForScreenColumn(column)) # Retrieves the grammar's token scopeDescriptor for a buffer position. # @@ -632,55 +473,7 @@ class DisplayBuffer extends Model # # Returns the new, clipped {Point}. Note that this could be the same as `position` if no clipping was performed. clipScreenPosition: (screenPosition, options={}) -> - return @displayLayer.clipScreenPosition(screenPosition, options) - # TODO: should DisplayLayer deal with options.wrapBeyondNewlines / options.wrapAtSoftNewlines? - # {wrapBeyondNewlines, wrapAtSoftNewlines, skipSoftWrapIndentation} = options - # {row, column} = Point.fromObject(screenPosition) - # - # if row < 0 - # row = 0 - # column = 0 - # else if row > @getLastRow() - # row = @getLastRow() - # column = Infinity - # else if column < 0 - # column = 0 - # - # screenLine = @tokenizedLineForScreenRow(row) - # unless screenLine? - # error = new Error("Undefined screen line when clipping screen position") - # Error.captureStackTrace(error) - # error.metadata = { - # screenRow: row - # screenColumn: column - # maxScreenRow: @getLastRow() - # screenLinesDefined: @screenLines.map (sl) -> sl? - # displayBufferChangeCount: @changeCount - # tokenizedBufferChangeCount: @tokenizedBuffer.changeCount - # bufferChangeCount: @buffer.changeCount - # } - # throw error - # - # maxScreenColumn = screenLine.getMaxScreenColumn() - # - # if screenLine.isSoftWrapped() and column >= maxScreenColumn - # if wrapAtSoftNewlines - # row++ - # column = @tokenizedLineForScreenRow(row).clipScreenColumn(0) - # else - # column = screenLine.clipScreenColumn(maxScreenColumn - 1) - # else if screenLine.isColumnInsideSoftWrapIndentation(column) - # if skipSoftWrapIndentation - # column = screenLine.clipScreenColumn(0) - # else - # row-- - # column = @tokenizedLineForScreenRow(row).getMaxScreenColumn() - 1 - # else if wrapBeyondNewlines and column > maxScreenColumn and row < @getLastRow() - # row++ - # column = 0 - # else - # column = screenLine.clipScreenColumn(column, options) - # new Point(row, column) + @displayLayer.clipScreenPosition(screenPosition, options) # Clip the start and end of the given range to valid positions on screen. # See {::clipScreenPosition} for more information. @@ -894,125 +687,9 @@ class DisplayBuffer extends Model @disposables.dispose() @tokenizedBuffer.destroy() - logLines: (start=0, end=@getLastRow()) -> - for row in [start..end] - line = @tokenizedLineForScreenRow(row).text - console.log row, @bufferRowForScreenRow(row), line, line.length - return - getRootScopeDescriptor: -> @tokenizedBuffer.rootScopeDescriptor - handleTokenizedBufferChange: (tokenizedBufferChange) => - @changeCount = @tokenizedBuffer.changeCount - {start, end, delta, bufferChange} = tokenizedBufferChange - @updateScreenLines(start, end + 1, delta, refreshMarkers: false) - - updateScreenLines: (startBufferRow, endBufferRow, bufferDelta=0, options={}) -> - return # TODO: After DisplayLayer is finished, delete these code paths - - return if @largeFileMode - return if @isDestroyed() - - startBufferRow = @rowMap.bufferRowRangeForBufferRow(startBufferRow)[0] - endBufferRow = @rowMap.bufferRowRangeForBufferRow(endBufferRow - 1)[1] - startScreenRow = @rowMap.screenRowRangeForBufferRow(startBufferRow)[0] - endScreenRow = @rowMap.screenRowRangeForBufferRow(endBufferRow - 1)[1] - {screenLines, regions} = @buildScreenLines(startBufferRow, endBufferRow + bufferDelta) - screenDelta = screenLines.length - (endScreenRow - startScreenRow) - - _.spliceWithArray(@screenLines, startScreenRow, endScreenRow - startScreenRow, screenLines, 10000) - - @checkScreenLinesInvariant() - - @rowMap.spliceRegions(startBufferRow, endBufferRow - startBufferRow, regions) - @findMaxLineLength(startScreenRow, endScreenRow, screenLines, screenDelta) - - return if options.suppressChangeEvent - - changeEvent = - start: startScreenRow - end: endScreenRow - 1 - screenDelta: screenDelta - bufferDelta: bufferDelta - - @emitDidChange(changeEvent, options.refreshMarkers) - - buildScreenLines: (startBufferRow, endBufferRow) -> - screenLines = [] - regions = [] - rectangularRegion = null - - foldsByStartRow = {} - # for fold in @outermostFoldsInBufferRowRange(startBufferRow, endBufferRow) - # foldsByStartRow[fold.getStartRow()] = fold - - bufferRow = startBufferRow - while bufferRow < endBufferRow - tokenizedLine = @tokenizedBuffer.tokenizedLineForRow(bufferRow) - - # if fold = foldsByStartRow[bufferRow] - # foldLine = tokenizedLine.copy() - # foldLine.fold = fold - # screenLines.push(foldLine) - # - # if rectangularRegion? - # regions.push(rectangularRegion) - # rectangularRegion = null - # - # foldedRowCount = fold.getBufferRowCount() - # regions.push(bufferRows: foldedRowCount, screenRows: 1) - # bufferRow += foldedRowCount - # else - softWraps = 0 - if @isSoftWrapped() - while wrapScreenColumn = tokenizedLine.findWrapColumn(@getSoftWrapColumnForTokenizedLine(tokenizedLine)) - [wrappedLine, tokenizedLine] = tokenizedLine.softWrapAt( - wrapScreenColumn, - @configSettings.softWrapHangingIndent - ) - break if wrappedLine.hasOnlySoftWrapIndentation() - screenLines.push(wrappedLine) - softWraps++ - screenLines.push(tokenizedLine) - - if softWraps > 0 - if rectangularRegion? - regions.push(rectangularRegion) - rectangularRegion = null - regions.push(bufferRows: 1, screenRows: softWraps + 1) - else - rectangularRegion ?= {bufferRows: 0, screenRows: 0} - rectangularRegion.bufferRows++ - rectangularRegion.screenRows++ - - bufferRow++ - - if rectangularRegion? - regions.push(rectangularRegion) - - {screenLines, regions} - - findMaxLineLength: (startScreenRow, endScreenRow, newScreenLines, screenDelta) -> - oldMaxLineLength = @maxLineLength - - if startScreenRow <= @longestScreenRow < endScreenRow - @longestScreenRow = 0 - @maxLineLength = 0 - maxLengthCandidatesStartRow = 0 - maxLengthCandidates = @screenLines - else - @longestScreenRow += screenDelta if endScreenRow <= @longestScreenRow - maxLengthCandidatesStartRow = startScreenRow - maxLengthCandidates = newScreenLines - - for screenLine, i in maxLengthCandidates - screenRow = maxLengthCandidatesStartRow + i - length = screenLine.text.length - if length > @maxLineLength - @longestScreenRow = screenRow - @maxLineLength = length - didCreateDefaultLayerMarker: (textBufferMarker) => if marker = @getMarker(textBufferMarker.id) # The marker might have been removed in some other handler called before @@ -1071,16 +748,3 @@ class DisplayBuffer extends Model @layerUpdateDisposablesByLayerId[layer.id].dispose() delete @decorationCountsByLayerId[layer.id] delete @layerUpdateDisposablesByLayerId[layer.id] - - checkScreenLinesInvariant: -> - return if @isSoftWrapped() - - screenLinesCount = @screenLines.length - tokenizedLinesCount = @tokenizedBuffer.getLineCount() - bufferLinesCount = @buffer.getLineCount() - - @assert screenLinesCount is tokenizedLinesCount, "Display buffer line count out of sync with tokenized buffer", (error) -> - error.metadata = {screenLinesCount, tokenizedLinesCount, bufferLinesCount} - - @assert screenLinesCount is bufferLinesCount, "Display buffer line count out of sync with buffer", (error) -> - error.metadata = {screenLinesCount, tokenizedLinesCount, bufferLinesCount} From 47d374a09aac493e220327c6e6ecf6e96af0bc50 Mon Sep 17 00:00:00 2001 From: Didier Roche Date: Thu, 12 Nov 2015 16:09:31 +0100 Subject: [PATCH 681/971] :penguin: Add "mktar" gulp task to create an Linux binary archive This archive in tar.gz format contains the whole Atom binary and resources to enable multiple channels and versions to be installed on the same distribution. --- build/tasks/mktar-task.coffee | 30 +++++++++++++++++++++++++++ script/mktar | 39 +++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 build/tasks/mktar-task.coffee create mode 100755 script/mktar diff --git a/build/tasks/mktar-task.coffee b/build/tasks/mktar-task.coffee new file mode 100644 index 000000000..f5edbb4be --- /dev/null +++ b/build/tasks/mktar-task.coffee @@ -0,0 +1,30 @@ +path = require 'path' + +module.exports = (grunt) -> + {spawn, fillTemplate} = require('./task-helpers')(grunt) + + grunt.registerTask 'mktar', 'Create an archive', -> + done = @async() + + appFileName = grunt.config.get('atom.appFileName') + buildDir = grunt.config.get('atom.buildDir') + shellAppDir = grunt.config.get('atom.shellAppDir') + {version, description} = grunt.config.get('atom.metadata') + + if process.arch is 'ia32' + arch = 'i386' + else if process.arch is 'x64' + arch = 'amd64' + else + return done("Unsupported arch #{process.arch}") + + iconPath = path.join(shellAppDir, 'resources', 'app.asar.unpacked', 'resources', 'atom.png') + + cmd = path.join('script', 'mktar') + args = [appFileName, version, arch, iconPath, buildDir] + spawn {cmd, args}, (error) -> + if error? + done(error) + else + grunt.log.ok "Created " + path.join(buildDir, "#{appFileName}-#{version}-#{arch}.tar.gz") + done() diff --git a/script/mktar b/script/mktar new file mode 100755 index 000000000..986063f9a --- /dev/null +++ b/script/mktar @@ -0,0 +1,39 @@ +#!/bin/bash +# mktar name version arch icon-path build-root-path + +set -e + +SCRIPT=`readlink -f "$0"` +ROOT=`readlink -f $(dirname $SCRIPT)/..` +cd $ROOT + +NAME="$1" +VERSION="$2" +ARCH="$3" +ICON_FILE="$4" +BUILD_ROOT_PATH="$5" +FILE_MODE=755 + +TAR_PATH=$BUILD_ROOT_PATH +ATOM_PATH="$BUILD_ROOT_PATH/Atom" + +TARGET_ROOT="`mktemp -d`" +chmod $FILE_MODE "$TARGET_ROOT" +NAME_IN_TAR="$NAME-$VERSION-$ARCH" +TARGET="$TARGET_ROOT/$NAME_IN_TAR" + +# Copy executable and resources +cp -a "$ATOM_PATH" "$TARGET" + +# Copy icon file +cp "$ICON_FILE" "$TARGET/$NAME.png" + +# Remove executable bit from .node files +find "$TARGET" -type f -name "*.node" -exec chmod a-x {} \; + +# Create the archive +pushd "$TARGET_ROOT" +tar caf "$TAR_PATH/$NAME_IN_TAR.tar.gz" "$NAME_IN_TAR" +popd + +rm -rf "$TARGET_ROOT" From 4a51841159b16e312949c23ebf83e59b4264ceba Mon Sep 17 00:00:00 2001 From: Didier Roche Date: Thu, 12 Nov 2015 16:12:06 +0100 Subject: [PATCH 682/971] Add the mktar task to linux CI --- build/Gruntfile.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index d9375d05c..f8ee607e5 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -285,6 +285,7 @@ module.exports = (grunt) -> ciTasks.push('dump-symbols') if process.platform is 'darwin' ciTasks.push('set-version', 'check-licenses', 'lint', 'generate-asar') ciTasks.push('mkdeb') if process.platform is 'linux' + ciTasks.push('mktar') if process.platform is 'linux' ciTasks.push('codesign:exe') if process.platform is 'win32' and not process.env.CI ciTasks.push('create-windows-installer:installer') if process.platform is 'win32' ciTasks.push('test') if process.platform is 'darwin' From 6a17b2dee83180106fca108189dc87bd58b2388a Mon Sep 17 00:00:00 2001 From: Didier Roche Date: Thu, 12 Nov 2015 16:13:11 +0100 Subject: [PATCH 683/971] Add the newly created archive to the publish-build task This archive is created on an Ubuntu 64 bits machine, publish it if present in the assets. The version contains the channel name, so don't append channel name to it. --- build/tasks/publish-build-task.coffee | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/build/tasks/publish-build-task.coffee b/build/tasks/publish-build-task.coffee index de46eb4fe..19061db02 100644 --- a/build/tasks/publish-build-task.coffee +++ b/build/tasks/publish-build-task.coffee @@ -85,13 +85,13 @@ getAssets = -> arch = 'amd64' # Check for a Debian build - sourcePath = "#{buildDir}/#{appFileName}-#{version}-#{arch}.deb" + sourcePath = path.join(buildDir, "#{appFileName}-#{version}-#{arch}.deb") assetName = "atom-#{arch}.deb" # Check for a Fedora build unless fs.isFileSync(sourcePath) rpmName = fs.readdirSync("#{buildDir}/rpm")[0] - sourcePath = "#{buildDir}/rpm/#{rpmName}" + sourcePath = path.join(buildDir, "rpm", rpmName) if process.arch is 'ia32' arch = 'i386' else @@ -99,10 +99,17 @@ getAssets = -> assetName = "atom.#{arch}.rpm" cp sourcePath, path.join(buildDir, assetName) + assets = [{assetName, sourcePath}] - [ - {assetName, sourcePath} - ] + # Check for an archive build on a debian build machine. + # We could provide a Fedora version if some libraries are not compatible + sourcePath = path.join(buildDir, "#{appFileName}-#{version}-#{arch}.tar.gz") + if fs.isFileSync(sourcePath) + assetName = "atom-#{arch}.tar.gz" + cp sourcePath, path.join(buildDir, assetName) + assets.push({assetName, sourcePath}) + + assets logError = (message, error, details) -> grunt.log.error(message) From d50da12bca3daa188a9817ce6adf777f1f13310d Mon Sep 17 00:00:00 2001 From: Didier Roche Date: Thu, 12 Nov 2015 16:15:19 +0100 Subject: [PATCH 684/971] Add Linux archive installation and build instructions --- README.md | 16 ++++++++++++++++ docs/build-instructions/linux.md | 8 +++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dcda2146c..20e940689 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,22 @@ Currently only a 64-bit version is available. The Linux version does not currently automatically update so you will need to repeat these steps to upgrade to future releases. +### Archive extraction + +An archive is available for people who don't want to install `atom` as root. + +This version enables you to install multiple Atom versions in parallel. It has been built on Ubuntu 64-bit, +but should be compatible with other Linux distributions. + +1. Install dependencies (on Ubuntu): `sudo apt install git gconf2 gconf-service libgtk2.0-0 libudev1 libgcrypt20 +libnotify4 libxtst6 libnss3 python gvfs-bin xdg-utils libcap2` +2. Download `atom-amd64.tar.gz` from the [Atom releases page](https://github.com/atom/atom/releases/latest). +3. Run `tar xf atom-amd64.tar.gz` in the directory where you want to extract the Atom folder. +4. Launch Atom using the installed `atom` command from the newly extracted directory. + +The Linux version does not currently automatically update so you will need to +repeat these steps to upgrade to future releases. + ## Building * [Linux](docs/build-instructions/linux.md) diff --git a/docs/build-instructions/linux.md b/docs/build-instructions/linux.md index c6a9e74eb..99b9b5afc 100644 --- a/docs/build-instructions/linux.md +++ b/docs/build-instructions/linux.md @@ -74,7 +74,7 @@ If you have problems with permissions don't forget to prefix with `sudo` To use the newly installed Atom, quit and restart all running Atom instances. -5. *Optionally*, you may generate distributable packages of Atom at `out`. Currently, `.deb` and `.rpm` package types are supported. To create a `.deb` package run: +5. *Optionally*, you may generate distributable packages of Atom at `out`. Currently, `.deb` and `.rpm` package types are supported, as well as a `tar gz` archive. To create a `.deb` package run: ```sh script/grunt mkdeb @@ -86,6 +86,12 @@ If you have problems with permissions don't forget to prefix with `sudo` script/grunt mkrpm ``` + To create a `.tar.gz` package run + + ```sh + script/grunt mktar + ``` + ## Advanced Options ### Custom build directory From bef7539e3453c049c91c052a3539784eb6fa2694 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 5 Apr 2016 17:40:24 +0200 Subject: [PATCH 685/971] Refactor DisplayBuffer to DecorationManager This commit moves all the remaining concerns not related to decorations out of `DisplayBuffer` and into `TextEditor`. This means the `DisplayBuffer` is now free to be renamed to `DecorationManager`. --- spec/decoration-manager-spec.coffee | 85 ++ spec/display-buffer-spec.coffee | 1312 -------------------------- spec/random-editor-spec.coffee | 2 +- spec/text-editor-component-spec.js | 14 +- spec/text-editor-spec.coffee | 13 + spec/tokenized-buffer-spec.coffee | 6 +- spec/workspace-spec.coffee | 4 +- src/decoration-manager.coffee | 180 ++++ src/decoration.coffee | 10 +- src/display-buffer.coffee | 750 --------------- src/language-mode.coffee | 16 +- src/layer-decoration.coffee | 8 +- src/marker-observation-window.coffee | 4 +- src/text-editor.coffee | 401 +++++--- 14 files changed, 583 insertions(+), 2222 deletions(-) create mode 100644 spec/decoration-manager-spec.coffee delete mode 100644 spec/display-buffer-spec.coffee create mode 100644 src/decoration-manager.coffee delete mode 100644 src/display-buffer.coffee diff --git a/spec/decoration-manager-spec.coffee b/spec/decoration-manager-spec.coffee new file mode 100644 index 000000000..c428df8cf --- /dev/null +++ b/spec/decoration-manager-spec.coffee @@ -0,0 +1,85 @@ +DecorationManager = require '../src/decoration-manager' +_ = require 'underscore-plus' + +describe "DecorationManager", -> + [decorationManager, buffer, defaultMarkerLayer] = [] + + beforeEach -> + buffer = atom.project.bufferForPathSync('sample.js') + displayLayer = buffer.addDisplayLayer() + defaultMarkerLayer = displayLayer.addMarkerLayer() + decorationManager = new DecorationManager(displayLayer, defaultMarkerLayer) + + waitsForPromise -> + atom.packages.activatePackage('language-javascript') + + afterEach -> + decorationManager.destroy() + buffer.release() + + describe "decorations", -> + [marker, decoration, decorationProperties] = [] + beforeEach -> + marker = defaultMarkerLayer.markBufferRange([[2, 13], [3, 15]]) + decorationProperties = {type: 'line-number', class: 'one'} + decoration = decorationManager.decorateMarker(marker, decorationProperties) + + it "can add decorations associated with markers and remove them", -> + expect(decoration).toBeDefined() + expect(decoration.getProperties()).toBe decorationProperties + expect(decorationManager.decorationForId(decoration.id)).toBe decoration + expect(decorationManager.decorationsForScreenRowRange(2, 3)[marker.id][0]).toBe decoration + + decoration.destroy() + expect(decorationManager.decorationsForScreenRowRange(2, 3)[marker.id]).not.toBeDefined() + expect(decorationManager.decorationForId(decoration.id)).not.toBeDefined() + + it "will not fail if the decoration is removed twice", -> + decoration.destroy() + decoration.destroy() + expect(decorationManager.decorationForId(decoration.id)).not.toBeDefined() + + it "does not allow destroyed markers to be decorated", -> + marker.destroy() + expect(-> + decorationManager.decorateMarker(marker, {type: 'overlay', item: document.createElement('div')}) + ).toThrow("Cannot decorate a destroyed marker") + expect(decorationManager.getOverlayDecorations()).toEqual [] + + describe "when a decoration is updated via Decoration::update()", -> + it "emits an 'updated' event containing the new and old params", -> + decoration.onDidChangeProperties updatedSpy = jasmine.createSpy() + decoration.setProperties type: 'line-number', class: 'two' + + {oldProperties, newProperties} = updatedSpy.mostRecentCall.args[0] + expect(oldProperties).toEqual decorationProperties + expect(newProperties).toEqual {type: 'line-number', gutterName: 'line-number', class: 'two'} + + describe "::getDecorations(properties)", -> + it "returns decorations matching the given optional properties", -> + expect(decorationManager.getDecorations()).toEqual [decoration] + expect(decorationManager.getDecorations(class: 'two').length).toEqual 0 + expect(decorationManager.getDecorations(class: 'one').length).toEqual 1 + + describe "::decorateMarker", -> + describe "when decorating gutters", -> + [marker] = [] + + beforeEach -> + marker = defaultMarkerLayer.markBufferRange([[1, 0], [1, 0]]) + + it "creates a decoration that is both of 'line-number' and 'gutter' type when called with the 'line-number' type", -> + decorationProperties = {type: 'line-number', class: 'one'} + decoration = decorationManager.decorateMarker(marker, decorationProperties) + expect(decoration.isType('line-number')).toBe true + expect(decoration.isType('gutter')).toBe true + expect(decoration.getProperties().gutterName).toBe 'line-number' + expect(decoration.getProperties().class).toBe 'one' + + it "creates a decoration that is only of 'gutter' type if called with the 'gutter' type and a 'gutterName'", -> + decorationProperties = {type: 'gutter', gutterName: 'test-gutter', class: 'one'} + decoration = decorationManager.decorateMarker(marker, decorationProperties) + expect(decoration.isType('gutter')).toBe true + expect(decoration.isType('line-number')).toBe false + expect(decoration.getProperties().gutterName).toBe 'test-gutter' + expect(decoration.getProperties().class).toBe 'one' diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee deleted file mode 100644 index 48103f3a7..000000000 --- a/spec/display-buffer-spec.coffee +++ /dev/null @@ -1,1312 +0,0 @@ -DisplayBuffer = require '../src/display-buffer' -_ = require 'underscore-plus' - -describe "DisplayBuffer", -> - [displayBuffer, buffer, changeHandler, tabLength] = [] - beforeEach -> - tabLength = 2 - - buffer = atom.project.bufferForPathSync('sample.js') - displayBuffer = new DisplayBuffer({ - buffer, tabLength, config: atom.config, grammarRegistry: atom.grammars, - packageManager: atom.packages, assert: -> - }) - changeHandler = jasmine.createSpy 'changeHandler' - displayBuffer.onDidChange changeHandler - - waitsForPromise -> - atom.packages.activatePackage('language-javascript') - - afterEach -> - displayBuffer.destroy() - buffer.release() - - describe "::copy()", -> - it "creates a new DisplayBuffer with the same initial state", -> - marker1 = displayBuffer.markBufferRange([[1, 2], [3, 4]], id: 1) - marker2 = displayBuffer.markBufferRange([[2, 3], [4, 5]], reversed: true, id: 2) - marker3 = displayBuffer.markBufferPosition([5, 6], id: 3) - displayBuffer.foldBufferRowRange(3, 5) - - displayBuffer2 = displayBuffer.copy() - expect(displayBuffer2.id).not.toBe displayBuffer.id - expect(displayBuffer2.buffer).toBe displayBuffer.buffer - expect(displayBuffer2.getTabLength()).toBe displayBuffer.getTabLength() - - expect(displayBuffer2.getMarkerCount()).toEqual displayBuffer.getMarkerCount() - expect(displayBuffer2.findMarker(id: 1)).toEqual marker1 - expect(displayBuffer2.findMarker(id: 2)).toEqual marker2 - expect(displayBuffer2.findMarker(id: 3)).toEqual marker3 - expect(displayBuffer2.isFoldedAtBufferRow(3)).toBeTruthy() - - # can diverge from origin - displayBuffer2.unfoldBufferRow(3) - expect(displayBuffer2.isFoldedAtBufferRow(3)).not.toBe displayBuffer.isFoldedAtBufferRow(3) - - describe "when the buffer changes", -> - it "renders line numbers correctly", -> - originalLineCount = displayBuffer.getLineCount() - oneHundredLines = [0..100].join("\n") - buffer.insert([0, 0], oneHundredLines) - expect(displayBuffer.getLineCount()).toBe 100 + originalLineCount - - it "updates the display buffer prior to invoking change handlers registered on the buffer", -> - buffer.onDidChange -> expect(displayBuffer2.tokenizedLineForScreenRow(0).text).toBe "testing" - displayBuffer2 = new DisplayBuffer({ - buffer, tabLength, config: atom.config, grammarRegistry: atom.grammars, - packageManager: atom.packages, assert: -> - }) - buffer.setText("testing") - - describe "soft wrapping", -> - beforeEach -> - displayBuffer.setEditorWidthInChars(50) - displayBuffer.setSoftWrapped(true) - displayBuffer.setDefaultCharWidth(1) - changeHandler.reset() - - describe "rendering of soft-wrapped lines", -> - describe "when there are double width characters", -> - it "takes them into account when finding the soft wrap column", -> - buffer.setText("私たちのフ是一个地方,数千名学生12345业余爱们的板作为hello world this is a pretty long latin line") - displayBuffer.setDefaultCharWidth(1, 5, 0, 0) - - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("私たちのフ是一个地方") - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe(",数千名学生12345业余爱") - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("们的板作为hello world this is a ") - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe("pretty long latin line") - - describe "when there are half width characters", -> - it "takes them into account when finding the soft wrap column", -> - displayBuffer.setDefaultCharWidth(1, 0, 5, 0) - buffer.setText("abcᆰᆱᆲネヌネノハヒフヒフヌᄡ○○○hello world this is a pretty long line") - - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("abcᆰᆱᆲネヌネノハヒ") - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe("フヒフヌᄡ○○○hello ") - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("world this is a pretty long line") - - describe "when there are korean characters", -> - it "takes them into account when finding the soft wrap column", -> - displayBuffer.setDefaultCharWidth(1, 0, 0, 10) - buffer.setText("1234세계를향한대화,유니코제10회유니코드국제") - - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("1234세계를향") - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe("한대화,유") - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("니코제10회") - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe("유니코드국") - expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe("제") - - describe "when editor.softWrapAtPreferredLineLength is set", -> - it "uses the preferred line length as the soft wrap column when it is less than the configured soft wrap column", -> - atom.config.set('editor.preferredLineLength', 100) - atom.config.set('editor.softWrapAtPreferredLineLength', true) - expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' return ' - - atom.config.set('editor.preferredLineLength', 5) - expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' fun' - - atom.config.set('editor.softWrapAtPreferredLineLength', false) - expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' return ' - - describe "when editor width is negative", -> - it "does not hang while wrapping", -> - displayBuffer.setDefaultCharWidth(1) - displayBuffer.setWidth(-1) - - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe " " - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe " var sort = function(items) {" - - describe "when the line is shorter than the max line length", -> - it "renders the line unchanged", -> - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe buffer.lineForRow(0) - - describe "when the line is empty", -> - it "renders the empty line", -> - expect(displayBuffer.tokenizedLineForScreenRow(13).text).toBe '' - - describe "when there is a non-whitespace character at the max length boundary", -> - describe "when there is whitespace before the boundary", -> - it "wraps the line at the end of the first whitespace preceding the boundary", -> - expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' return ' - expect(displayBuffer.tokenizedLineForScreenRow(11).text).toBe ' sort(left).concat(pivot).concat(sort(right));' - - it "wraps the line at the first CJK character before the boundary", -> - displayBuffer.setEditorWidthInChars(10) - - buffer.setTextInRange([[0, 0], [1, 0]], 'abcd efg유私フ业余爱\n') - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe 'abcd efg유私' - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe 'フ业余爱' - - buffer.setTextInRange([[0, 0], [1, 0]], 'abcd ef유gef业余爱\n') - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe 'abcd ef유' - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe 'gef业余爱' - - describe "when there is no whitespace before the boundary", -> - it "wraps the line at the first CJK character before the boundary", -> - buffer.setTextInRange([[0, 0], [1, 0]], '私たちのabcdefghij\n') - displayBuffer.setEditorWidthInChars(10) - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe '私たちの' - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe 'abcdefghij' - - it "wraps the line exactly at the boundary when no CJK character is found, since there's no more graceful place to wrap it", -> - buffer.setTextInRange([[0, 0], [1, 0]], 'abcdefghijklmnopqrstuvwxyz\n') - displayBuffer.setEditorWidthInChars(10) - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe 'abcdefghij' - expect(displayBuffer.tokenizedLineForScreenRow(0).bufferDelta).toBe 'abcdefghij'.length - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe 'klmnopqrst' - expect(displayBuffer.tokenizedLineForScreenRow(1).bufferDelta).toBe 'klmnopqrst'.length - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe 'uvwxyz' - expect(displayBuffer.tokenizedLineForScreenRow(2).bufferDelta).toBe 'uvwxyz'.length - - it "closes all scopes at the wrap boundary", -> - displayBuffer.setEditorWidthInChars(10) - buffer.setText("`aaa${1+2}aaa`") - iterator = displayBuffer.tokenizedLineForScreenRow(1).getTokenIterator() - scopes = iterator.getScopes() - expect(scopes[scopes.length - 1]).not.toBe 'punctuation.section.embedded.js' - - describe "when there is a whitespace character at the max length boundary", -> - it "wraps the line at the first non-whitespace character following the boundary", -> - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe ' var pivot = items.shift(), current, left = [], ' - expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe ' right = [];' - - describe "when the only whitespace characters are at the beginning of the line", -> - beforeEach -> - displayBuffer.setEditorWidthInChars(10) - - it "wraps the line at the max length when indented with tabs", -> - buffer.setTextInRange([[0, 0], [1, 0]], '\t\tabcdefghijklmnopqrstuvwxyz') - - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe ' abcdef' - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe ' ghijkl' - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe ' mnopqr' - - it "wraps the line at the max length when indented with spaces", -> - buffer.setTextInRange([[0, 0], [1, 0]], ' abcdefghijklmnopqrstuvwxyz') - - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe ' abcdef' - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe ' ghijkl' - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe ' mnopqr' - - describe "when there are hard tabs", -> - beforeEach -> - buffer.setText(buffer.getText().replace(new RegExp(' ', 'g'), '\t')) - - it "correctly tokenizes the hard tabs", -> - expect(displayBuffer.tokenizedLineForScreenRow(3).tokens[0].isHardTab).toBeTruthy() - expect(displayBuffer.tokenizedLineForScreenRow(3).tokens[1].isHardTab).toBeTruthy() - - describe "when a line is wrapped", -> - it "breaks soft-wrap indentation into a token for each indentation level to support indent guides", -> - tokenizedLine = displayBuffer.tokenizedLineForScreenRow(4) - - expect(tokenizedLine.tokens[0].value).toBe(" ") - expect(tokenizedLine.tokens[0].isSoftWrapIndentation).toBeTruthy() - - expect(tokenizedLine.tokens[1].value).toBe(" ") - expect(tokenizedLine.tokens[1].isSoftWrapIndentation).toBeTruthy() - - expect(tokenizedLine.tokens[2].isSoftWrapIndentation).toBeFalsy() - - describe "when editor.softWrapHangingIndent is set", -> - beforeEach -> - atom.config.set('editor.softWrapHangingIndent', 3) - - it "further indents wrapped lines", -> - expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe " return " - expect(displayBuffer.tokenizedLineForScreenRow(11).text).toBe " sort(left).concat(pivot).concat(sort(right)" - expect(displayBuffer.tokenizedLineForScreenRow(12).text).toBe " );" - - it "includes hanging indent when breaking soft-wrap indentation into tokens", -> - tokenizedLine = displayBuffer.tokenizedLineForScreenRow(4) - - expect(tokenizedLine.tokens[0].value).toBe(" ") - expect(tokenizedLine.tokens[0].isSoftWrapIndentation).toBeTruthy() - - expect(tokenizedLine.tokens[1].value).toBe(" ") - expect(tokenizedLine.tokens[1].isSoftWrapIndentation).toBeTruthy() - - expect(tokenizedLine.tokens[2].value).toBe(" ") # hanging indent - expect(tokenizedLine.tokens[2].isSoftWrapIndentation).toBeTruthy() - - expect(tokenizedLine.tokens[3].value).toBe(" ") # odd space - expect(tokenizedLine.tokens[3].isSoftWrapIndentation).toBeTruthy() - - expect(tokenizedLine.tokens[4].isSoftWrapIndentation).toBeFalsy() - - describe "when the buffer changes", -> - describe "when buffer lines are updated", -> - describe "when whitespace is added after the max line length", -> - it "adds whitespace to the end of the current line and wraps an empty line", -> - fiftyCharacters = _.multiplyString("x", 50) - buffer.setText(fiftyCharacters) - buffer.insert([0, 51], " ") - - describe "when the update makes a soft-wrapped line shorter than the max line length", -> - it "rewraps the line and emits a change event", -> - buffer.delete([[6, 24], [6, 42]]) - expect(displayBuffer.tokenizedLineForScreenRow(7).text).toBe ' current < pivot ? : right.push(current);' - expect(displayBuffer.tokenizedLineForScreenRow(8).text).toBe ' }' - - expect(changeHandler).toHaveBeenCalled() - [[event]]= changeHandler.argsForCall - - expect(event).toEqual(start: 7, end: 8, screenDelta: -1, bufferDelta: 0) - - describe "when the update causes a line to soft wrap an additional time", -> - it "rewraps the line and emits a change event", -> - buffer.insert([6, 28], '1234567890') - expect(displayBuffer.tokenizedLineForScreenRow(7).text).toBe ' current < pivot ? ' - expect(displayBuffer.tokenizedLineForScreenRow(8).text).toBe ' left1234567890.push(current) : ' - expect(displayBuffer.tokenizedLineForScreenRow(9).text).toBe ' right.push(current);' - expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' }' - - expect(changeHandler).toHaveBeenCalledWith(start: 7, end: 8, screenDelta: 1, bufferDelta: 0) - - describe "when buffer lines are inserted", -> - it "inserts / updates wrapped lines and emits a change event", -> - buffer.insert([6, 21], '1234567890 abcdefghij 1234567890\nabcdefghij') - expect(displayBuffer.tokenizedLineForScreenRow(7).text).toBe ' current < pivot1234567890 abcdefghij ' - expect(displayBuffer.tokenizedLineForScreenRow(8).text).toBe ' 1234567890' - expect(displayBuffer.tokenizedLineForScreenRow(9).text).toBe 'abcdefghij ? left.push(current) : ' - expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe 'right.push(current);' - - expect(changeHandler).toHaveBeenCalledWith(start: 7, end: 8, screenDelta: 2, bufferDelta: 1) - - describe "when buffer lines are removed", -> - it "removes lines and emits a change event", -> - buffer.setTextInRange([[3, 21], [7, 5]], ';') - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe ' var pivot = items;' - expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe ' return ' - expect(displayBuffer.tokenizedLineForScreenRow(5).text).toBe ' sort(left).concat(pivot).concat(sort(right));' - expect(displayBuffer.tokenizedLineForScreenRow(6).text).toBe ' };' - - expect(changeHandler).toHaveBeenCalledWith(start: 3, end: 9, screenDelta: -6, bufferDelta: -4) - - describe "when a newline is inserted, deleted, and re-inserted at the end of a wrapped line (regression)", -> - it "correctly renders the original wrapped line", -> - buffer = atom.project.buildBufferSync(null, '') - displayBuffer = new DisplayBuffer({ - buffer, tabLength, editorWidthInChars: 30, config: atom.config, - grammarRegistry: atom.grammars, packageManager: atom.packages, assert: -> - }) - displayBuffer.setDefaultCharWidth(1) - displayBuffer.setSoftWrapped(true) - - buffer.insert([0, 0], "the quick brown fox jumps over the lazy dog.") - buffer.insert([0, Infinity], '\n') - buffer.delete([[0, Infinity], [1, 0]]) - buffer.insert([0, Infinity], '\n') - - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "the quick brown fox jumps over " - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "the lazy dog." - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "" - - describe "position translation", -> - it "translates positions accounting for wrapped lines", -> - # before any wrapped lines - expect(displayBuffer.screenPositionForBufferPosition([0, 5])).toEqual([0, 5]) - expect(displayBuffer.bufferPositionForScreenPosition([0, 5])).toEqual([0, 5]) - expect(displayBuffer.screenPositionForBufferPosition([0, 29])).toEqual([0, 29]) - expect(displayBuffer.bufferPositionForScreenPosition([0, 29])).toEqual([0, 29]) - - # on a wrapped line - expect(displayBuffer.screenPositionForBufferPosition([3, 5])).toEqual([3, 5]) - expect(displayBuffer.bufferPositionForScreenPosition([3, 5])).toEqual([3, 5]) - expect(displayBuffer.screenPositionForBufferPosition([3, 50])).toEqual([3, 50]) - expect(displayBuffer.screenPositionForBufferPosition([3, 51])).toEqual([3, 50]) - expect(displayBuffer.bufferPositionForScreenPosition([4, 0])).toEqual([3, 50]) - expect(displayBuffer.bufferPositionForScreenPosition([3, 50])).toEqual([3, 50]) - expect(displayBuffer.screenPositionForBufferPosition([3, 62])).toEqual([4, 15]) - expect(displayBuffer.bufferPositionForScreenPosition([4, 11])).toEqual([3, 58]) - - # following a wrapped line - expect(displayBuffer.screenPositionForBufferPosition([4, 5])).toEqual([5, 5]) - expect(displayBuffer.bufferPositionForScreenPosition([5, 5])).toEqual([4, 5]) - - # clip screen position inputs before translating - expect(displayBuffer.bufferPositionForScreenPosition([-5, -5])).toEqual([0, 0]) - expect(displayBuffer.bufferPositionForScreenPosition([Infinity, Infinity])).toEqual([12, 2]) - expect(displayBuffer.bufferPositionForScreenPosition([3, -5])).toEqual([3, 0]) - expect(displayBuffer.bufferPositionForScreenPosition([3, Infinity])).toEqual([3, 50]) - - describe ".setEditorWidthInChars(length)", -> - it "changes the length at which lines are wrapped and emits a change event for all screen lines", -> - tokensText = (tokens) -> - _.pluck(tokens, 'value').join('') - - displayBuffer.setEditorWidthInChars(40) - expect(tokensText displayBuffer.tokenizedLineForScreenRow(4).tokens).toBe ' left = [], right = [];' - expect(tokensText displayBuffer.tokenizedLineForScreenRow(5).tokens).toBe ' while(items.length > 0) {' - expect(tokensText displayBuffer.tokenizedLineForScreenRow(12).tokens).toBe ' sort(left).concat(pivot).concat(sort' - expect(changeHandler).toHaveBeenCalledWith(start: 0, end: 15, screenDelta: 3, bufferDelta: 0) - - it "only allows positive widths to be assigned", -> - displayBuffer.setEditorWidthInChars(0) - expect(displayBuffer.editorWidthInChars).not.toBe 0 - displayBuffer.setEditorWidthInChars(-1) - expect(displayBuffer.editorWidthInChars).not.toBe -1 - - describe "primitive folding", -> - beforeEach -> - displayBuffer.destroy() - buffer.release() - buffer = atom.project.bufferForPathSync('two-hundred.txt') - displayBuffer = new DisplayBuffer({ - buffer, tabLength, config: atom.config, grammarRegistry: atom.grammars, - packageManager: atom.packages, assert: -> - }) - displayBuffer.onDidChange changeHandler - - describe "when folds are created and destroyed", -> - describe "when a fold spans multiple lines", -> - it "replaces the lines spanned by the fold with a placeholder that references the fold object", -> - fold = displayBuffer.foldBufferRowRange(4, 7) - expect(fold).toBeDefined() - - [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) - expect(line4.fold).toBe fold - expect(line4.text).toMatch /^4-+/ - expect(line5.text).toBe '8' - - expect(changeHandler).toHaveBeenCalledWith(start: 4, end: 7, screenDelta: -3, bufferDelta: 0) - changeHandler.reset() - - fold.destroy() - [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) - expect(line4.fold).toBeUndefined() - expect(line4.text).toMatch /^4-+/ - expect(line5.text).toBe '5' - - expect(changeHandler).toHaveBeenCalledWith(start: 4, end: 4, screenDelta: 3, bufferDelta: 0) - - describe "when a fold spans a single line", -> - it "renders a fold placeholder for the folded line but does not skip any lines", -> - fold = displayBuffer.foldBufferRowRange(4, 4) - - [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) - expect(line4.fold).toBe fold - expect(line4.text).toMatch /^4-+/ - expect(line5.text).toBe '5' - - expect(changeHandler).toHaveBeenCalledWith(start: 4, end: 4, screenDelta: 0, bufferDelta: 0) - - # Line numbers don't actually change, but it's not worth the complexity to have this - # be false for single line folds since they are so rare - changeHandler.reset() - - fold.destroy() - - [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) - expect(line4.fold).toBeUndefined() - expect(line4.text).toMatch /^4-+/ - expect(line5.text).toBe '5' - - expect(changeHandler).toHaveBeenCalledWith(start: 4, end: 4, screenDelta: 0, bufferDelta: 0) - - describe "when a fold is nested within another fold", -> - it "does not render the placeholder for the inner fold until the outer fold is destroyed", -> - innerFold = displayBuffer.foldBufferRowRange(6, 7) - outerFold = displayBuffer.foldBufferRowRange(4, 8) - - [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) - expect(line4.fold).toBe outerFold - expect(line4.text).toMatch /4-+/ - expect(line5.text).toMatch /9-+/ - - outerFold.destroy() - [line4, line5, line6, line7] = displayBuffer.tokenizedLinesForScreenRows(4, 7) - expect(line4.fold).toBeUndefined() - expect(line4.text).toMatch /^4-+/ - expect(line5.text).toBe '5' - expect(line6.fold).toBe innerFold - expect(line6.text).toBe '6' - expect(line7.text).toBe '8' - - it "allows the outer fold to start at the same location as the inner fold", -> - innerFold = displayBuffer.foldBufferRowRange(4, 6) - outerFold = displayBuffer.foldBufferRowRange(4, 8) - - [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) - expect(line4.fold).toBe outerFold - expect(line4.text).toMatch /4-+/ - expect(line5.text).toMatch /9-+/ - - describe "when creating a fold where one already exists", -> - it "returns existing fold and does't create new fold", -> - fold = displayBuffer.foldBufferRowRange(0, 10) - expect(displayBuffer.foldsMarkerLayer.getMarkers().length).toBe 1 - - newFold = displayBuffer.foldBufferRowRange(0, 10) - expect(newFold).toBe fold - expect(displayBuffer.foldsMarkerLayer.getMarkers().length).toBe 1 - - describe "when a fold is created inside an existing folded region", -> - it "creates/destroys the fold, but does not trigger change event", -> - outerFold = displayBuffer.foldBufferRowRange(0, 10) - changeHandler.reset() - - innerFold = displayBuffer.foldBufferRowRange(2, 5) - expect(changeHandler).not.toHaveBeenCalled() - [line0, line1] = displayBuffer.tokenizedLinesForScreenRows(0, 1) - expect(line0.fold).toBe outerFold - expect(line1.fold).toBeUndefined() - - changeHandler.reset() - innerFold.destroy() - expect(changeHandler).not.toHaveBeenCalled() - [line0, line1] = displayBuffer.tokenizedLinesForScreenRows(0, 1) - expect(line0.fold).toBe outerFold - expect(line1.fold).toBeUndefined() - - describe "when a fold ends where another fold begins", -> - it "continues to hide the lines inside the second fold", -> - fold2 = displayBuffer.foldBufferRowRange(4, 9) - fold1 = displayBuffer.foldBufferRowRange(0, 4) - - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toMatch /^0/ - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toMatch /^10/ - - describe "when there is another display buffer pointing to the same buffer", -> - it "does not consider folds to be nested inside of folds from the other display buffer", -> - otherDisplayBuffer = new DisplayBuffer({ - buffer, tabLength, config: atom.config, grammarRegistry: atom.grammars, - packageManager: atom.packages, assert: -> - }) - otherDisplayBuffer.foldBufferRowRange(1, 5) - - displayBuffer.foldBufferRowRange(2, 4) - expect(otherDisplayBuffer.foldsStartingAtBufferRow(2).length).toBe 0 - - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2' - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe '5' - - describe "when the buffer changes", -> - [fold1, fold2] = [] - beforeEach -> - fold1 = displayBuffer.foldBufferRowRange(2, 4) - fold2 = displayBuffer.foldBufferRowRange(6, 8) - changeHandler.reset() - - describe "when the old range surrounds a fold", -> - beforeEach -> - buffer.setTextInRange([[1, 0], [5, 1]], 'party!') - - it "removes the fold and replaces the selection with the new text", -> - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "0" - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "party!" - expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold2 - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch /^9-+/ - - expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 3, screenDelta: -2, bufferDelta: -4) - - describe "when the changes is subsequently undone", -> - xit "restores destroyed folds", -> - buffer.undo() - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2' - expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1 - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe '5' - - describe "when the old range surrounds two nested folds", -> - it "removes both folds and replaces the selection with the new text", -> - displayBuffer.foldBufferRowRange(2, 9) - changeHandler.reset() - - buffer.setTextInRange([[1, 0], [10, 0]], 'goodbye') - - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "0" - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "goodbye10" - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "11" - - expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 3, screenDelta: -2, bufferDelta: -9) - - describe "when multiple changes happen above the fold", -> - it "repositions folds correctly", -> - buffer.delete([[1, 1], [2, 0]]) - buffer.insert([0, 1], "\nnew") - - expect(fold1.getStartRow()).toBe 2 - expect(fold1.getEndRow()).toBe 4 - - describe "when the old range precedes lines with a fold", -> - describe "when the new range precedes lines with a fold", -> - it "updates the buffer and re-positions subsequent folds", -> - buffer.setTextInRange([[0, 0], [1, 1]], 'abc') - - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "abc" - expect(displayBuffer.tokenizedLineForScreenRow(1).fold).toBe fold1 - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "5" - expect(displayBuffer.tokenizedLineForScreenRow(3).fold).toBe fold2 - expect(displayBuffer.tokenizedLineForScreenRow(4).text).toMatch /^9-+/ - - expect(changeHandler).toHaveBeenCalledWith(start: 0, end: 1, screenDelta: -1, bufferDelta: -1) - changeHandler.reset() - - fold1.destroy() - expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "abc" - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "2" - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch /^4-+/ - expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe "5" - expect(displayBuffer.tokenizedLineForScreenRow(5).fold).toBe fold2 - expect(displayBuffer.tokenizedLineForScreenRow(6).text).toMatch /^9-+/ - - expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 1, screenDelta: 2, bufferDelta: 0) - - describe "when the old range straddles the beginning of a fold", -> - it "destroys the fold", -> - buffer.setTextInRange([[1, 1], [3, 0]], "a\nb\nc\nd\n") - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe '1a' - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe 'b' - expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBeUndefined() - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe 'c' - - describe "when the old range follows a fold", -> - it "re-positions the screen ranges for the change event based on the preceding fold", -> - buffer.setTextInRange([[10, 0], [11, 0]], 'abc') - - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1" - expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1 - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe "5" - expect(displayBuffer.tokenizedLineForScreenRow(4).fold).toBe fold2 - expect(displayBuffer.tokenizedLineForScreenRow(5).text).toMatch /^9-+/ - - expect(changeHandler).toHaveBeenCalledWith(start: 6, end: 7, screenDelta: -1, bufferDelta: -1) - - describe "when the old range is inside a fold", -> - describe "when the end of the new range precedes the end of the fold", -> - it "updates the fold and ensures the change is present when the fold is destroyed", -> - buffer.insert([3, 0], '\n') - expect(fold1.getStartRow()).toBe 2 - expect(fold1.getEndRow()).toBe 5 - - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1" - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "2" - expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1 - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch "5" - expect(displayBuffer.tokenizedLineForScreenRow(4).fold).toBe fold2 - expect(displayBuffer.tokenizedLineForScreenRow(5).text).toMatch /^9-+/ - - expect(changeHandler).toHaveBeenCalledWith(start: 2, end: 2, screenDelta: 0, bufferDelta: 1) - - describe "when the end of the new range exceeds the end of the fold", -> - it "expands the fold to contain all the inserted lines", -> - buffer.setTextInRange([[3, 0], [4, 0]], 'a\nb\nc\nd\n') - expect(fold1.getStartRow()).toBe 2 - expect(fold1.getEndRow()).toBe 7 - - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1" - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "2" - expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1 - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch "5" - expect(displayBuffer.tokenizedLineForScreenRow(4).fold).toBe fold2 - expect(displayBuffer.tokenizedLineForScreenRow(5).text).toMatch /^9-+/ - - expect(changeHandler).toHaveBeenCalledWith(start: 2, end: 2, screenDelta: 0, bufferDelta: 3) - - describe "when the old range straddles the end of the fold", -> - describe "when the end of the new range precedes the end of the fold", -> - it "destroys the fold", -> - fold2.destroy() - buffer.setTextInRange([[3, 0], [6, 0]], 'a\n') - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2' - expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBeUndefined() - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe 'a' - expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe '6' - - describe "when the old range is contained to a single line in-between two folds", -> - it "re-renders the line with the placeholder and re-positions the second fold", -> - buffer.insert([5, 0], 'abc\n') - - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1" - expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1 - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch "abc" - expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe "5" - expect(displayBuffer.tokenizedLineForScreenRow(5).fold).toBe fold2 - expect(displayBuffer.tokenizedLineForScreenRow(6).text).toMatch /^9-+/ - - expect(changeHandler).toHaveBeenCalledWith(start: 3, end: 3, screenDelta: 1, bufferDelta: 1) - - describe "when the change starts at the beginning of a fold but does not extend to the end (regression)", -> - it "preserves a proper mapping between buffer and screen coordinates", -> - expect(displayBuffer.screenPositionForBufferPosition([8, 0])).toEqual [4, 0] - buffer.setTextInRange([[2, 0], [3, 0]], "\n") - expect(displayBuffer.screenPositionForBufferPosition([8, 0])).toEqual [4, 0] - - describe "position translation", -> - it "translates positions to account for folded lines and characters and the placeholder", -> - fold = displayBuffer.foldBufferRowRange(4, 7) - - # preceding fold: identity - expect(displayBuffer.screenPositionForBufferPosition([3, 0])).toEqual [3, 0] - expect(displayBuffer.screenPositionForBufferPosition([4, 0])).toEqual [4, 0] - - expect(displayBuffer.bufferPositionForScreenPosition([3, 0])).toEqual [3, 0] - expect(displayBuffer.bufferPositionForScreenPosition([4, 0])).toEqual [4, 0] - - # inside of fold: translate to the start of the fold - expect(displayBuffer.screenPositionForBufferPosition([4, 35])).toEqual [4, 0] - expect(displayBuffer.screenPositionForBufferPosition([5, 5])).toEqual [4, 0] - - # following fold - expect(displayBuffer.screenPositionForBufferPosition([8, 0])).toEqual [5, 0] - expect(displayBuffer.screenPositionForBufferPosition([11, 2])).toEqual [8, 2] - - expect(displayBuffer.bufferPositionForScreenPosition([5, 0])).toEqual [8, 0] - expect(displayBuffer.bufferPositionForScreenPosition([9, 2])).toEqual [12, 2] - - # clip screen positions before translating - expect(displayBuffer.bufferPositionForScreenPosition([-5, -5])).toEqual([0, 0]) - expect(displayBuffer.bufferPositionForScreenPosition([Infinity, Infinity])).toEqual([200, 0]) - - # after fold is destroyed - fold.destroy() - - expect(displayBuffer.screenPositionForBufferPosition([8, 0])).toEqual [8, 0] - expect(displayBuffer.screenPositionForBufferPosition([11, 2])).toEqual [11, 2] - - expect(displayBuffer.bufferPositionForScreenPosition([5, 0])).toEqual [5, 0] - expect(displayBuffer.bufferPositionForScreenPosition([9, 2])).toEqual [9, 2] - - describe ".unfoldBufferRow(row)", -> - it "destroys all folds containing the given row", -> - displayBuffer.foldBufferRowRange(2, 4) - displayBuffer.foldBufferRowRange(2, 6) - displayBuffer.foldBufferRowRange(7, 8) - displayBuffer.foldBufferRowRange(1, 9) - displayBuffer.foldBufferRowRange(11, 12) - - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe '1' - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '10' - - displayBuffer.unfoldBufferRow(2) - expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe '1' - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2' - expect(displayBuffer.tokenizedLineForScreenRow(7).fold).toBeDefined() - expect(displayBuffer.tokenizedLineForScreenRow(8).text).toMatch /^9-+/ - expect(displayBuffer.tokenizedLineForScreenRow(10).fold).toBeDefined() - - describe ".outermostFoldsInBufferRowRange(startRow, endRow)", -> - it "returns the outermost folds entirely contained in the given row range, exclusive of end row", -> - fold1 = displayBuffer.foldBufferRowRange(4, 7) - fold2 = displayBuffer.foldBufferRowRange(5, 6) - fold3 = displayBuffer.foldBufferRowRange(11, 15) - fold4 = displayBuffer.foldBufferRowRange(12, 13) - fold5 = displayBuffer.foldBufferRowRange(16, 17) - - expect(displayBuffer.outermostFoldsInBufferRowRange(3, 18)).toEqual [fold1, fold3, fold5] - expect(displayBuffer.outermostFoldsInBufferRowRange(5, 16)).toEqual [fold3] - - describe "::clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, clip: 'closest')", -> - beforeEach -> - tabLength = 4 - - displayBuffer.setDefaultCharWidth(1) - displayBuffer.setTabLength(tabLength) - displayBuffer.setSoftWrapped(true) - displayBuffer.setEditorWidthInChars(50) - - it "allows valid positions", -> - expect(displayBuffer.clipScreenPosition([4, 5])).toEqual [4, 5] - expect(displayBuffer.clipScreenPosition([4, 11])).toEqual [4, 11] - - it "disallows negative positions", -> - expect(displayBuffer.clipScreenPosition([-1, -1])).toEqual [0, 0] - expect(displayBuffer.clipScreenPosition([-1, 10])).toEqual [0, 0] - expect(displayBuffer.clipScreenPosition([0, -1])).toEqual [0, 0] - - it "disallows positions beyond the last row", -> - expect(displayBuffer.clipScreenPosition([1000, 0])).toEqual [15, 2] - expect(displayBuffer.clipScreenPosition([1000, 1000])).toEqual [15, 2] - - describe "when wrapBeyondNewlines is false (the default)", -> - it "wraps positions beyond the end of hard newlines to the end of the line", -> - expect(displayBuffer.clipScreenPosition([1, 10000])).toEqual [1, 30] - expect(displayBuffer.clipScreenPosition([4, 30])).toEqual [4, 15] - expect(displayBuffer.clipScreenPosition([4, 1000])).toEqual [4, 15] - - describe "when wrapBeyondNewlines is true", -> - it "wraps positions past the end of hard newlines to the next line", -> - expect(displayBuffer.clipScreenPosition([0, 29], wrapBeyondNewlines: true)).toEqual [0, 29] - expect(displayBuffer.clipScreenPosition([0, 30], wrapBeyondNewlines: true)).toEqual [1, 0] - expect(displayBuffer.clipScreenPosition([0, 1000], wrapBeyondNewlines: true)).toEqual [1, 0] - - it "wraps positions in the middle of fold lines to the next screen line", -> - displayBuffer.foldBufferRowRange(3, 5) - expect(displayBuffer.clipScreenPosition([3, 5], wrapBeyondNewlines: true)).toEqual [4, 0] - - describe "when skipSoftWrapIndentation is false (the default)", -> - it "wraps positions at the end of previous soft-wrapped line", -> - expect(displayBuffer.clipScreenPosition([4, 0])).toEqual [3, 50] - expect(displayBuffer.clipScreenPosition([4, 1])).toEqual [3, 50] - expect(displayBuffer.clipScreenPosition([4, 3])).toEqual [3, 50] - - describe "when skipSoftWrapIndentation is true", -> - it "clips positions to the beginning of the line", -> - expect(displayBuffer.clipScreenPosition([4, 0], skipSoftWrapIndentation: true)).toEqual [4, 4] - expect(displayBuffer.clipScreenPosition([4, 1], skipSoftWrapIndentation: true)).toEqual [4, 4] - expect(displayBuffer.clipScreenPosition([4, 3], skipSoftWrapIndentation: true)).toEqual [4, 4] - - describe "when wrapAtSoftNewlines is false (the default)", -> - it "clips positions at the end of soft-wrapped lines to the character preceding the end of the line", -> - expect(displayBuffer.clipScreenPosition([3, 50])).toEqual [3, 50] - expect(displayBuffer.clipScreenPosition([3, 51])).toEqual [3, 50] - expect(displayBuffer.clipScreenPosition([3, 58])).toEqual [3, 50] - expect(displayBuffer.clipScreenPosition([3, 1000])).toEqual [3, 50] - - describe "when wrapAtSoftNewlines is true", -> - it "wraps positions at the end of soft-wrapped lines to the next screen line", -> - expect(displayBuffer.clipScreenPosition([3, 50], wrapAtSoftNewlines: true)).toEqual [3, 50] - expect(displayBuffer.clipScreenPosition([3, 51], wrapAtSoftNewlines: true)).toEqual [4, 4] - expect(displayBuffer.clipScreenPosition([3, 58], wrapAtSoftNewlines: true)).toEqual [4, 4] - expect(displayBuffer.clipScreenPosition([3, 1000], wrapAtSoftNewlines: true)).toEqual [4, 4] - - describe "when clip is 'closest' (the default)", -> - it "clips screen positions in the middle of atomic tab characters to the closest edge of the character", -> - buffer.insert([0, 0], '\t') - expect(displayBuffer.clipScreenPosition([0, 0])).toEqual [0, 0] - expect(displayBuffer.clipScreenPosition([0, 1])).toEqual [0, 0] - expect(displayBuffer.clipScreenPosition([0, 2])).toEqual [0, 0] - expect(displayBuffer.clipScreenPosition([0, tabLength-1])).toEqual [0, tabLength] - expect(displayBuffer.clipScreenPosition([0, tabLength])).toEqual [0, tabLength] - - describe "when clip is 'backward'", -> - it "clips screen positions in the middle of atomic tab characters to the beginning of the character", -> - buffer.insert([0, 0], '\t') - expect(displayBuffer.clipScreenPosition([0, 0], clip: 'backward')).toEqual [0, 0] - expect(displayBuffer.clipScreenPosition([0, tabLength-1], clip: 'backward')).toEqual [0, 0] - expect(displayBuffer.clipScreenPosition([0, tabLength], clip: 'backward')).toEqual [0, tabLength] - - describe "when clip is 'forward'", -> - it "clips screen positions in the middle of atomic tab characters to the end of the character", -> - buffer.insert([0, 0], '\t') - expect(displayBuffer.clipScreenPosition([0, 0], clip: 'forward')).toEqual [0, 0] - expect(displayBuffer.clipScreenPosition([0, 1], clip: 'forward')).toEqual [0, tabLength] - expect(displayBuffer.clipScreenPosition([0, tabLength], clip: 'forward')).toEqual [0, tabLength] - - describe "::screenPositionForBufferPosition(bufferPosition, options)", -> - it "clips the specified buffer position", -> - expect(displayBuffer.screenPositionForBufferPosition([0, 2])).toEqual [0, 2] - expect(displayBuffer.screenPositionForBufferPosition([0, 100000])).toEqual [0, 29] - expect(displayBuffer.screenPositionForBufferPosition([100000, 0])).toEqual [12, 2] - expect(displayBuffer.screenPositionForBufferPosition([100000, 100000])).toEqual [12, 2] - - it "clips to the (left or right) edge of an atomic token without simply rounding up", -> - tabLength = 4 - displayBuffer.setTabLength(tabLength) - - buffer.insert([0, 0], '\t') - expect(displayBuffer.screenPositionForBufferPosition([0, 0])).toEqual [0, 0] - expect(displayBuffer.screenPositionForBufferPosition([0, 1])).toEqual [0, tabLength] - - it "clips to the edge closest to the given position when it's inside a soft tab", -> - tabLength = 4 - displayBuffer.setTabLength(tabLength) - - buffer.insert([0, 0], ' ') - expect(displayBuffer.screenPositionForBufferPosition([0, 0])).toEqual [0, 0] - expect(displayBuffer.screenPositionForBufferPosition([0, 1])).toEqual [0, 0] - expect(displayBuffer.screenPositionForBufferPosition([0, 2])).toEqual [0, 0] - expect(displayBuffer.screenPositionForBufferPosition([0, 3])).toEqual [0, 4] - expect(displayBuffer.screenPositionForBufferPosition([0, 4])).toEqual [0, 4] - - describe "position translation in the presence of hard tabs", -> - it "correctly translates positions on either side of a tab", -> - buffer.setText('\t') - expect(displayBuffer.screenPositionForBufferPosition([0, 1])).toEqual [0, 2] - expect(displayBuffer.bufferPositionForScreenPosition([0, 2])).toEqual [0, 1] - - it "correctly translates positions on soft wrapped lines containing tabs", -> - buffer.setText('\t\taa bb cc dd ee ff gg') - displayBuffer.setSoftWrapped(true) - displayBuffer.setDefaultCharWidth(1) - displayBuffer.setEditorWidthInChars(10) - expect(displayBuffer.screenPositionForBufferPosition([0, 10], wrapAtSoftNewlines: true)).toEqual [1, 4] - expect(displayBuffer.bufferPositionForScreenPosition([1, 0])).toEqual [0, 9] - - describe "::getMaxLineLength()", -> - it "returns the length of the longest screen line", -> - expect(displayBuffer.getMaxLineLength()).toBe 65 - buffer.delete([[6, 0], [6, 65]]) - expect(displayBuffer.getMaxLineLength()).toBe 62 - - it "correctly updates the location of the longest screen line when changes occur", -> - expect(displayBuffer.getLongestScreenRow()).toBe 6 - buffer.delete([[3, 0], [5, 0]]) - expect(displayBuffer.getLongestScreenRow()).toBe 4 - - buffer.delete([[4, 0], [5, 0]]) - expect(displayBuffer.getLongestScreenRow()).toBe 5 - expect(displayBuffer.getMaxLineLength()).toBe 56 - - buffer.delete([[6, 0], [8, 0]]) - expect(displayBuffer.getLongestScreenRow()).toBe 5 - expect(displayBuffer.getMaxLineLength()).toBe 56 - - describe "::destroy()", -> - it "unsubscribes all display buffer markers from their underlying buffer marker (regression)", -> - marker = displayBuffer.markBufferPosition([12, 2]) - displayBuffer.destroy() - expect( -> buffer.insert([12, 2], '\n')).not.toThrow() - - describe "markers", -> - beforeEach -> - displayBuffer.foldBufferRowRange(4, 7) - - describe "marker creation and manipulation", -> - it "allows markers to be created in terms of both screen and buffer coordinates", -> - marker1 = displayBuffer.markScreenRange([[5, 4], [5, 10]]) - marker2 = displayBuffer.markBufferRange([[8, 4], [8, 10]]) - expect(marker1.getBufferRange()).toEqual [[8, 4], [8, 10]] - expect(marker2.getScreenRange()).toEqual [[5, 4], [5, 10]] - - it "emits a 'marker-created' event on the DisplayBuffer whenever a marker is created", -> - displayBuffer.onDidCreateMarker markerCreatedHandler = jasmine.createSpy("markerCreatedHandler") - - marker1 = displayBuffer.markScreenRange([[5, 4], [5, 10]]) - expect(markerCreatedHandler).toHaveBeenCalledWith(marker1) - markerCreatedHandler.reset() - - marker2 = buffer.markRange([[5, 4], [5, 10]]) - expect(markerCreatedHandler).toHaveBeenCalledWith(displayBuffer.getMarker(marker2.id)) - - it "allows marker head and tail positions to be manipulated in both screen and buffer coordinates", -> - marker = displayBuffer.markScreenRange([[5, 4], [5, 10]]) - marker.setHeadScreenPosition([5, 4]) - marker.setTailBufferPosition([5, 4]) - expect(marker.isReversed()).toBeFalsy() - expect(marker.getBufferRange()).toEqual [[5, 4], [8, 4]] - marker.setHeadBufferPosition([5, 4]) - marker.setTailScreenPosition([5, 4]) - expect(marker.isReversed()).toBeTruthy() - expect(marker.getBufferRange()).toEqual [[5, 4], [8, 4]] - - it "returns whether a position changed when it is assigned", -> - marker = displayBuffer.markScreenRange([[0, 0], [0, 0]]) - expect(marker.setHeadScreenPosition([5, 4])).toBeTruthy() - expect(marker.setHeadScreenPosition([5, 4])).toBeFalsy() - expect(marker.setHeadBufferPosition([1, 0])).toBeTruthy() - expect(marker.setHeadBufferPosition([1, 0])).toBeFalsy() - expect(marker.setTailScreenPosition([5, 4])).toBeTruthy() - expect(marker.setTailScreenPosition([5, 4])).toBeFalsy() - expect(marker.setTailBufferPosition([1, 0])).toBeTruthy() - expect(marker.setTailBufferPosition([1, 0])).toBeFalsy() - - describe "marker change events", -> - [markerChangedHandler, marker] = [] - - beforeEach -> - marker = displayBuffer.addMarkerLayer(maintainHistory: true).markScreenRange([[5, 4], [5, 10]]) - marker.onDidChange markerChangedHandler = jasmine.createSpy("markerChangedHandler") - - it "triggers the 'changed' event whenever the markers head's screen position changes in the buffer or on screen", -> - marker.setHeadScreenPosition([8, 20]) - expect(markerChangedHandler).toHaveBeenCalled() - expect(markerChangedHandler.argsForCall[0][0]).toEqual { - oldHeadScreenPosition: [5, 10] - oldHeadBufferPosition: [8, 10] - newHeadScreenPosition: [8, 20] - newHeadBufferPosition: [11, 20] - oldTailScreenPosition: [5, 4] - oldTailBufferPosition: [8, 4] - newTailScreenPosition: [5, 4] - newTailBufferPosition: [8, 4] - textChanged: false - isValid: true - } - markerChangedHandler.reset() - - buffer.insert([11, 0], '...') - expect(markerChangedHandler).toHaveBeenCalled() - expect(markerChangedHandler.argsForCall[0][0]).toEqual { - oldHeadScreenPosition: [8, 20] - oldHeadBufferPosition: [11, 20] - newHeadScreenPosition: [8, 23] - newHeadBufferPosition: [11, 23] - oldTailScreenPosition: [5, 4] - oldTailBufferPosition: [8, 4] - newTailScreenPosition: [5, 4] - newTailBufferPosition: [8, 4] - textChanged: true - isValid: true - } - markerChangedHandler.reset() - - displayBuffer.unfoldBufferRow(4) - expect(markerChangedHandler).toHaveBeenCalled() - expect(markerChangedHandler.argsForCall[0][0]).toEqual { - oldHeadScreenPosition: [8, 23] - oldHeadBufferPosition: [11, 23] - newHeadScreenPosition: [11, 23] - newHeadBufferPosition: [11, 23] - oldTailScreenPosition: [5, 4] - oldTailBufferPosition: [8, 4] - newTailScreenPosition: [8, 4] - newTailBufferPosition: [8, 4] - textChanged: false - isValid: true - } - markerChangedHandler.reset() - - displayBuffer.foldBufferRowRange(4, 7) - expect(markerChangedHandler).toHaveBeenCalled() - expect(markerChangedHandler.argsForCall[0][0]).toEqual { - oldHeadScreenPosition: [11, 23] - oldHeadBufferPosition: [11, 23] - newHeadScreenPosition: [8, 23] - newHeadBufferPosition: [11, 23] - oldTailScreenPosition: [8, 4] - oldTailBufferPosition: [8, 4] - newTailScreenPosition: [5, 4] - newTailBufferPosition: [8, 4] - textChanged: false - isValid: true - } - - it "triggers the 'changed' event whenever the marker tail's position changes in the buffer or on screen", -> - marker.setTailScreenPosition([8, 20]) - expect(markerChangedHandler).toHaveBeenCalled() - expect(markerChangedHandler.argsForCall[0][0]).toEqual { - oldHeadScreenPosition: [5, 10] - oldHeadBufferPosition: [8, 10] - newHeadScreenPosition: [5, 10] - newHeadBufferPosition: [8, 10] - oldTailScreenPosition: [5, 4] - oldTailBufferPosition: [8, 4] - newTailScreenPosition: [8, 20] - newTailBufferPosition: [11, 20] - textChanged: false - isValid: true - } - markerChangedHandler.reset() - - buffer.insert([11, 0], '...') - expect(markerChangedHandler).toHaveBeenCalled() - expect(markerChangedHandler.argsForCall[0][0]).toEqual { - oldHeadScreenPosition: [5, 10] - oldHeadBufferPosition: [8, 10] - newHeadScreenPosition: [5, 10] - newHeadBufferPosition: [8, 10] - oldTailScreenPosition: [8, 20] - oldTailBufferPosition: [11, 20] - newTailScreenPosition: [8, 23] - newTailBufferPosition: [11, 23] - textChanged: true - isValid: true - } - - it "triggers the 'changed' event whenever the marker is invalidated or revalidated", -> - buffer.deleteRow(8) - expect(markerChangedHandler).toHaveBeenCalled() - expect(markerChangedHandler.argsForCall[0][0]).toEqual { - oldHeadScreenPosition: [5, 10] - oldHeadBufferPosition: [8, 10] - newHeadScreenPosition: [5, 0] - newHeadBufferPosition: [8, 0] - oldTailScreenPosition: [5, 4] - oldTailBufferPosition: [8, 4] - newTailScreenPosition: [5, 0] - newTailBufferPosition: [8, 0] - textChanged: true - isValid: false - } - - markerChangedHandler.reset() - buffer.undo() - - expect(markerChangedHandler).toHaveBeenCalled() - expect(markerChangedHandler.argsForCall[0][0]).toEqual { - oldHeadScreenPosition: [5, 0] - oldHeadBufferPosition: [8, 0] - newHeadScreenPosition: [5, 10] - newHeadBufferPosition: [8, 10] - oldTailScreenPosition: [5, 0] - oldTailBufferPosition: [8, 0] - newTailScreenPosition: [5, 4] - newTailBufferPosition: [8, 4] - textChanged: true - isValid: true - } - - it "does not call the callback for screen changes that don't change the position of the marker", -> - displayBuffer.foldBufferRowRange(10, 11) - expect(markerChangedHandler).not.toHaveBeenCalled() - - it "updates markers before emitting buffer change events, but does not notify their observers until the change event", -> - marker2 = displayBuffer.addMarkerLayer(maintainHistory: true).markBufferRange([[8, 1], [8, 1]]) - marker2.onDidChange marker2ChangedHandler = jasmine.createSpy("marker2ChangedHandler") - displayBuffer.onDidChange changeHandler = jasmine.createSpy("changeHandler").andCallFake -> onDisplayBufferChange() - - # New change ---- - - onDisplayBufferChange = -> - # calls change handler first - expect(markerChangedHandler).not.toHaveBeenCalled() - expect(marker2ChangedHandler).not.toHaveBeenCalled() - # but still updates the markers - expect(marker.getScreenRange()).toEqual [[5, 7], [5, 13]] - expect(marker.getHeadScreenPosition()).toEqual [5, 13] - expect(marker.getTailScreenPosition()).toEqual [5, 7] - expect(marker2.isValid()).toBeFalsy() - - buffer.setTextInRange([[8, 0], [8, 2]], ".....") - expect(changeHandler).toHaveBeenCalled() - expect(markerChangedHandler).toHaveBeenCalled() - expect(marker2ChangedHandler).toHaveBeenCalled() - - # Undo change ---- - - changeHandler.reset() - markerChangedHandler.reset() - marker2ChangedHandler.reset() - - marker3 = displayBuffer.markBufferRange([[8, 1], [8, 2]]) - marker3.onDidChange marker3ChangedHandler = jasmine.createSpy("marker3ChangedHandler") - - onDisplayBufferChange = -> - # calls change handler first - expect(markerChangedHandler).not.toHaveBeenCalled() - expect(marker2ChangedHandler).not.toHaveBeenCalled() - expect(marker3ChangedHandler).not.toHaveBeenCalled() - - # markers positions are updated based on the text change - expect(marker.getScreenRange()).toEqual [[5, 4], [5, 10]] - expect(marker.getHeadScreenPosition()).toEqual [5, 10] - expect(marker.getTailScreenPosition()).toEqual [5, 4] - - buffer.undo() - expect(changeHandler).toHaveBeenCalled() - expect(markerChangedHandler).toHaveBeenCalled() - expect(marker2ChangedHandler).toHaveBeenCalled() - expect(marker3ChangedHandler).toHaveBeenCalled() - expect(marker2.isValid()).toBeTruthy() - expect(marker3.isValid()).toBeFalsy() - - # Redo change ---- - - changeHandler.reset() - markerChangedHandler.reset() - marker2ChangedHandler.reset() - marker3ChangedHandler.reset() - - onDisplayBufferChange = -> - # calls change handler first - expect(markerChangedHandler).not.toHaveBeenCalled() - expect(marker2ChangedHandler).not.toHaveBeenCalled() - expect(marker3ChangedHandler).not.toHaveBeenCalled() - - # markers positions are updated based on the text change - expect(marker.getScreenRange()).toEqual [[5, 7], [5, 13]] - expect(marker.getHeadScreenPosition()).toEqual [5, 13] - expect(marker.getTailScreenPosition()).toEqual [5, 7] - - # but marker snapshots are not restored until the end of the undo. - expect(marker2.isValid()).toBeFalsy() - expect(marker3.isValid()).toBeFalsy() - - buffer.redo() - expect(changeHandler).toHaveBeenCalled() - expect(markerChangedHandler).toHaveBeenCalled() - expect(marker2ChangedHandler).toHaveBeenCalled() - expect(marker3ChangedHandler).toHaveBeenCalled() - - it "updates the position of markers before emitting change events that aren't caused by a buffer change", -> - displayBuffer.onDidChange changeHandler = jasmine.createSpy("changeHandler").andCallFake -> - # calls change handler first - expect(markerChangedHandler).not.toHaveBeenCalled() - # but still updates the markers - expect(marker.getScreenRange()).toEqual [[8, 4], [8, 10]] - expect(marker.getHeadScreenPosition()).toEqual [8, 10] - expect(marker.getTailScreenPosition()).toEqual [8, 4] - - displayBuffer.unfoldBufferRow(4) - - expect(changeHandler).toHaveBeenCalled() - expect(markerChangedHandler).toHaveBeenCalled() - - it "emits the correct events when markers are mutated inside event listeners", -> - marker.onDidChange -> - if marker.getHeadScreenPosition().isEqual([5, 9]) - marker.setHeadScreenPosition([5, 8]) - - marker.setHeadScreenPosition([5, 9]) - - headChanges = for [event] in markerChangedHandler.argsForCall - {old: event.oldHeadScreenPosition, new: event.newHeadScreenPosition} - - expect(headChanges).toEqual [ - {old: [5, 10], new: [5, 9]} - {old: [5, 9], new: [5, 8]} - ] - - describe "::findMarkers(attributes)", -> - it "allows the startBufferRow and endBufferRow to be specified", -> - marker1 = displayBuffer.markBufferRange([[0, 0], [3, 0]], class: 'a') - marker2 = displayBuffer.markBufferRange([[0, 0], [5, 0]], class: 'a') - marker3 = displayBuffer.markBufferRange([[9, 0], [10, 0]], class: 'b') - - expect(displayBuffer.findMarkers(class: 'a', startBufferRow: 0)).toEqual [marker2, marker1] - expect(displayBuffer.findMarkers(class: 'a', startBufferRow: 0, endBufferRow: 3)).toEqual [marker1] - expect(displayBuffer.findMarkers(endBufferRow: 10)).toEqual [marker3] - - it "allows the startScreenRow and endScreenRow to be specified", -> - marker1 = displayBuffer.markBufferRange([[6, 0], [7, 0]], class: 'a') - marker2 = displayBuffer.markBufferRange([[9, 0], [10, 0]], class: 'a') - displayBuffer.foldBufferRowRange(4, 7) - expect(displayBuffer.findMarkers(class: 'a', startScreenRow: 6, endScreenRow: 7)).toEqual [marker2] - - it "allows intersectsBufferRowRange to be specified", -> - marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a') - marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a') - displayBuffer.foldBufferRowRange(4, 7) - expect(displayBuffer.findMarkers(class: 'a', intersectsBufferRowRange: [5, 6])).toEqual [marker1] - - it "allows intersectsScreenRowRange to be specified", -> - marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a') - marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a') - displayBuffer.foldBufferRowRange(4, 7) - expect(displayBuffer.findMarkers(class: 'a', intersectsScreenRowRange: [5, 10])).toEqual [marker2] - - it "allows containedInScreenRange to be specified", -> - marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a') - marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a') - displayBuffer.foldBufferRowRange(4, 7) - expect(displayBuffer.findMarkers(class: 'a', containedInScreenRange: [[5, 0], [7, 0]])).toEqual [marker2] - - it "allows intersectsBufferRange to be specified", -> - marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a') - marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a') - displayBuffer.foldBufferRowRange(4, 7) - expect(displayBuffer.findMarkers(class: 'a', intersectsBufferRange: [[5, 0], [6, 0]])).toEqual [marker1] - - it "allows intersectsScreenRange to be specified", -> - marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a') - marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a') - displayBuffer.foldBufferRowRange(4, 7) - expect(displayBuffer.findMarkers(class: 'a', intersectsScreenRange: [[5, 0], [10, 0]])).toEqual [marker2] - - describe "marker destruction", -> - it "allows markers to be destroyed", -> - marker = displayBuffer.markScreenRange([[5, 4], [5, 10]]) - marker.destroy() - expect(marker.isValid()).toBeFalsy() - expect(displayBuffer.getMarker(marker.id)).toBeUndefined() - - it "notifies ::onDidDestroy observers when markers are destroyed", -> - destroyedHandler = jasmine.createSpy("destroyedHandler") - marker = displayBuffer.markScreenRange([[5, 4], [5, 10]]) - marker.onDidDestroy destroyedHandler - marker.destroy() - expect(destroyedHandler).toHaveBeenCalled() - destroyedHandler.reset() - - marker2 = displayBuffer.markScreenRange([[5, 4], [5, 10]]) - marker2.onDidDestroy destroyedHandler - buffer.getMarker(marker2.id).destroy() - expect(destroyedHandler).toHaveBeenCalled() - - describe "Marker::copy(attributes)", -> - it "creates a copy of the marker with the given attributes merged in", -> - initialMarkerCount = displayBuffer.getMarkerCount() - marker1 = displayBuffer.markScreenRange([[5, 4], [5, 10]], a: 1, b: 2) - expect(displayBuffer.getMarkerCount()).toBe initialMarkerCount + 1 - - marker2 = marker1.copy(b: 3) - expect(marker2.getBufferRange()).toEqual marker1.getBufferRange() - expect(displayBuffer.getMarkerCount()).toBe initialMarkerCount + 2 - expect(marker1.getProperties()).toEqual a: 1, b: 2 - expect(marker2.getProperties()).toEqual a: 1, b: 3 - - describe 'when there are multiple DisplayBuffers for a buffer', -> - describe 'when a marker is created', -> - it 'the second display buffer will not emit a marker-created event when the marker has been deleted in the first marker-created event', -> - displayBuffer2 = new DisplayBuffer({ - buffer, tabLength, config: atom.config, grammarRegistry: atom.grammars, - packageManager: atom.packages, assert: -> - }) - displayBuffer.onDidCreateMarker markerCreated1 = jasmine.createSpy().andCallFake (marker) -> marker.destroy() - displayBuffer2.onDidCreateMarker markerCreated2 = jasmine.createSpy() - - displayBuffer.markBufferRange([[0, 0], [1, 5]], {}) - - expect(markerCreated1).toHaveBeenCalled() - expect(markerCreated2).not.toHaveBeenCalled() - - describe "decorations", -> - [marker, decoration, decorationProperties] = [] - beforeEach -> - marker = displayBuffer.markBufferRange([[2, 13], [3, 15]]) - decorationProperties = {type: 'line-number', class: 'one'} - decoration = displayBuffer.decorateMarker(marker, decorationProperties) - - it "can add decorations associated with markers and remove them", -> - expect(decoration).toBeDefined() - expect(decoration.getProperties()).toBe decorationProperties - expect(displayBuffer.decorationForId(decoration.id)).toBe decoration - expect(displayBuffer.decorationsForScreenRowRange(2, 3)[marker.id][0]).toBe decoration - - decoration.destroy() - expect(displayBuffer.decorationsForScreenRowRange(2, 3)[marker.id]).not.toBeDefined() - expect(displayBuffer.decorationForId(decoration.id)).not.toBeDefined() - - it "will not fail if the decoration is removed twice", -> - decoration.destroy() - decoration.destroy() - expect(displayBuffer.decorationForId(decoration.id)).not.toBeDefined() - - it "does not allow destroyed markers to be decorated", -> - marker.destroy() - expect(-> - displayBuffer.decorateMarker(marker, {type: 'overlay', item: document.createElement('div')}) - ).toThrow("Cannot decorate a destroyed marker") - expect(displayBuffer.getOverlayDecorations()).toEqual [] - - describe "when a decoration is updated via Decoration::update()", -> - it "emits an 'updated' event containing the new and old params", -> - decoration.onDidChangeProperties updatedSpy = jasmine.createSpy() - decoration.setProperties type: 'line-number', class: 'two' - - {oldProperties, newProperties} = updatedSpy.mostRecentCall.args[0] - expect(oldProperties).toEqual decorationProperties - expect(newProperties).toEqual {type: 'line-number', gutterName: 'line-number', class: 'two'} - - describe "::getDecorations(properties)", -> - it "returns decorations matching the given optional properties", -> - expect(displayBuffer.getDecorations()).toEqual [decoration] - expect(displayBuffer.getDecorations(class: 'two').length).toEqual 0 - expect(displayBuffer.getDecorations(class: 'one').length).toEqual 1 - - describe "::scrollToScreenPosition(position, [options])", -> - it "triggers ::onDidRequestAutoscroll with the logical coordinates along with the options", -> - scrollSpy = jasmine.createSpy("::onDidRequestAutoscroll") - displayBuffer.onDidRequestAutoscroll(scrollSpy) - - displayBuffer.scrollToScreenPosition([8, 20]) - displayBuffer.scrollToScreenPosition([8, 20], center: true) - displayBuffer.scrollToScreenPosition([8, 20], center: false, reversed: true) - - expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {}) - expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {center: true}) - expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {center: false, reversed: true}) - - describe "::decorateMarker", -> - describe "when decorating gutters", -> - [marker] = [] - - beforeEach -> - marker = displayBuffer.markBufferRange([[1, 0], [1, 0]]) - - it "creates a decoration that is both of 'line-number' and 'gutter' type when called with the 'line-number' type", -> - decorationProperties = {type: 'line-number', class: 'one'} - decoration = displayBuffer.decorateMarker(marker, decorationProperties) - expect(decoration.isType('line-number')).toBe true - expect(decoration.isType('gutter')).toBe true - expect(decoration.getProperties().gutterName).toBe 'line-number' - expect(decoration.getProperties().class).toBe 'one' - - it "creates a decoration that is only of 'gutter' type if called with the 'gutter' type and a 'gutterName'", -> - decorationProperties = {type: 'gutter', gutterName: 'test-gutter', class: 'one'} - decoration = displayBuffer.decorateMarker(marker, decorationProperties) - expect(decoration.isType('gutter')).toBe true - expect(decoration.isType('line-number')).toBe false - expect(decoration.getProperties().gutterName).toBe 'test-gutter' - expect(decoration.getProperties().class).toBe 'one' diff --git a/spec/random-editor-spec.coffee b/spec/random-editor-spec.coffee index 3924a8412..06c50e80f 100644 --- a/spec/random-editor-spec.coffee +++ b/spec/random-editor-spec.coffee @@ -17,7 +17,7 @@ describe "TextEditor", -> buffer = new TextBuffer editor = atom.workspace.buildTextEditor({buffer}) editor.setEditorWidthInChars(80) - tokenizedBuffer = editor.displayBuffer.tokenizedBuffer + tokenizedBuffer = editor.tokenizedBuffer steps = [] times 30, -> diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index c7c9f9277..43f473e9d 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -1477,7 +1477,7 @@ describe('TextEditorComponent', function () { component.measureDimensions() await nextViewUpdatePromise() - let marker2 = editor.displayBuffer.markBufferRange([[9, 0], [9, 0]]) + let marker2 = editor.markBufferRange([[9, 0], [9, 0]]) editor.decorateMarker(marker2, { type: ['line-number', 'line'], 'class': 'b' @@ -1889,7 +1889,7 @@ describe('TextEditorComponent', function () { component.measureDimensions() await nextViewUpdatePromise() - marker = editor.displayBuffer.markBufferRange([[9, 2], [9, 4]], { + marker = editor.markBufferRange([[9, 2], [9, 4]], { invalidate: 'inside' }) editor.decorateMarker(marker, { @@ -2084,7 +2084,7 @@ describe('TextEditorComponent', function () { describe('when the marker is empty', function () { it('renders an overlay decoration when added and removes the overlay when the decoration is destroyed', async function () { - let marker = editor.displayBuffer.markBufferRange([[2, 13], [2, 13]], { + let marker = editor.markBufferRange([[2, 13], [2, 13]], { invalidate: 'never' }) let decoration = editor.decorateMarker(marker, { @@ -2106,7 +2106,7 @@ describe('TextEditorComponent', function () { }) it('renders the overlay element with the CSS class specified by the decoration', async function () { - let marker = editor.displayBuffer.markBufferRange([[2, 13], [2, 13]], { + let marker = editor.markBufferRange([[2, 13], [2, 13]], { invalidate: 'never' }) let decoration = editor.decorateMarker(marker, { @@ -2127,7 +2127,7 @@ describe('TextEditorComponent', function () { describe('when the marker is not empty', function () { it('renders at the head of the marker by default', async function () { - let marker = editor.displayBuffer.markBufferRange([[2, 5], [2, 10]], { + let marker = editor.markBufferRange([[2, 5], [2, 10]], { invalidate: 'never' }) let decoration = editor.decorateMarker(marker, { @@ -2173,7 +2173,7 @@ describe('TextEditorComponent', function () { }) it('slides horizontally left when near the right edge on #win32 and #darwin', async function () { - let marker = editor.displayBuffer.markBufferRange([[0, 26], [0, 26]], { + let marker = editor.markBufferRange([[0, 26], [0, 26]], { invalidate: 'never' }) let decoration = editor.decorateMarker(marker, { @@ -4934,7 +4934,7 @@ describe('TextEditorComponent', function () { function lineNumberForBufferRowHasClass (bufferRow, klass) { let screenRow - screenRow = editor.displayBuffer.screenRowForBufferRow(bufferRow) + screenRow = editor.screenRowForBufferRow(bufferRow) return component.lineNumberNodeForScreenRow(screenRow).classList.contains(klass) } diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 95003a498..39124bb17 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5710,6 +5710,19 @@ describe "TextEditor", -> expect(editor.getFirstVisibleScreenRow()).toEqual 89 expect(editor.getVisibleRowRange()).toEqual [89, 99] + describe "::scrollToScreenPosition(position, [options])", -> + it "triggers ::onDidRequestAutoscroll with the logical coordinates along with the options", -> + scrollSpy = jasmine.createSpy("::onDidRequestAutoscroll") + editor.onDidRequestAutoscroll(scrollSpy) + + editor.scrollToScreenPosition([8, 20]) + editor.scrollToScreenPosition([8, 20], center: true) + editor.scrollToScreenPosition([8, 20], center: false, reversed: true) + + expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {}) + expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {center: true}) + expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {center: false, reversed: true}) + describe '.get/setPlaceholderText()', -> it 'can be created with placeholderText', -> newEditor = atom.workspace.buildTextEditor( diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index f8746a511..cb9378c4e 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -419,7 +419,7 @@ describe "TokenizedBuffer", -> atom.workspace.open('sample.js').then (o) -> editor = o runs -> - tokenizedBuffer = editor.displayBuffer.tokenizedBuffer + tokenizedBuffer = editor.tokenizedBuffer tokenizedBuffer.onDidTokenize tokenizedHandler fullyTokenize(tokenizedBuffer) expect(tokenizedHandler.callCount).toBe(1) @@ -432,7 +432,7 @@ describe "TokenizedBuffer", -> atom.workspace.open('sample.js').then (o) -> editor = o runs -> - tokenizedBuffer = editor.displayBuffer.tokenizedBuffer + tokenizedBuffer = editor.tokenizedBuffer fullyTokenize(tokenizedBuffer) tokenizedBuffer.onDidTokenize tokenizedHandler @@ -450,7 +450,7 @@ describe "TokenizedBuffer", -> atom.workspace.open('coffee.coffee').then (o) -> editor = o runs -> - tokenizedBuffer = editor.displayBuffer.tokenizedBuffer + tokenizedBuffer = editor.tokenizedBuffer tokenizedBuffer.onDidTokenize tokenizedHandler fullyTokenize(tokenizedBuffer) tokenizedHandler.reset() diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 97139f6bb..4aeeb4183 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -428,7 +428,7 @@ describe "Workspace", -> workspace.open('sample.js').then (e) -> editor = e runs -> - expect(editor.displayBuffer.largeFileMode).toBe true + expect(editor.largeFileMode).toBe true describe "when the file is over 20MB", -> it "prompts the user to make sure they want to open a file this big", -> @@ -453,7 +453,7 @@ describe "Workspace", -> runs -> expect(atom.applicationDelegate.confirm).toHaveBeenCalled() - expect(editor.displayBuffer.largeFileMode).toBe true + expect(editor.largeFileMode).toBe true describe "when passed a path that matches a custom opener", -> it "returns the resource returned by the custom opener", -> diff --git a/src/decoration-manager.coffee b/src/decoration-manager.coffee new file mode 100644 index 000000000..e96727fe3 --- /dev/null +++ b/src/decoration-manager.coffee @@ -0,0 +1,180 @@ +{Emitter} = require 'event-kit' +Model = require './model' +Decoration = require './decoration' +LayerDecoration = require './layer-decoration' + +module.exports = +class DecorationManager extends Model + didUpdateDecorationsEventScheduled: false + updatedSynchronously: false + + constructor: (@displayLayer, @defaultMarkerLayer) -> + super + + @emitter = new Emitter + @decorationsById = {} + @decorationsByMarkerId = {} + @overlayDecorationsById = {} + @layerDecorationsByMarkerLayerId = {} + @decorationCountsByLayerId = {} + @layerUpdateDisposablesByLayerId = {} + + observeDecorations: (callback) -> + callback(decoration) for decoration in @getDecorations() + @onDidAddDecoration(callback) + + onDidAddDecoration: (callback) -> + @emitter.on 'did-add-decoration', callback + + onDidRemoveDecoration: (callback) -> + @emitter.on 'did-remove-decoration', callback + + onDidUpdateDecorations: (callback) -> + @emitter.on 'did-update-decorations', callback + + setUpdatedSynchronously: (@updatedSynchronously) -> + + decorationForId: (id) -> + @decorationsById[id] + + getDecorations: (propertyFilter) -> + allDecorations = [] + for markerId, decorations of @decorationsByMarkerId + allDecorations.push(decorations...) if decorations? + if propertyFilter? + allDecorations = allDecorations.filter (decoration) -> + for key, value of propertyFilter + return false unless decoration.properties[key] is value + true + allDecorations + + getLineDecorations: (propertyFilter) -> + @getDecorations(propertyFilter).filter (decoration) -> decoration.isType('line') + + getLineNumberDecorations: (propertyFilter) -> + @getDecorations(propertyFilter).filter (decoration) -> decoration.isType('line-number') + + getHighlightDecorations: (propertyFilter) -> + @getDecorations(propertyFilter).filter (decoration) -> decoration.isType('highlight') + + getOverlayDecorations: (propertyFilter) -> + result = [] + for id, decoration of @overlayDecorationsById + result.push(decoration) + if propertyFilter? + result.filter (decoration) -> + for key, value of propertyFilter + return false unless decoration.properties[key] is value + true + else + result + + decorationsForScreenRowRange: (startScreenRow, endScreenRow) -> + decorationsByMarkerId = {} + for marker in @defaultMarkerLayer.findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow]) + if decorations = @decorationsByMarkerId[marker.id] + decorationsByMarkerId[marker.id] = decorations + decorationsByMarkerId + + decorationsStateForScreenRowRange: (startScreenRow, endScreenRow) -> + decorationsState = {} + + for layerId of @decorationCountsByLayerId + layer = @displayLayer.getMarkerLayer(layerId) + + for marker in layer.findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow]) when marker.isValid() + screenRange = marker.getScreenRange() + rangeIsReversed = marker.isReversed() + + if decorations = @decorationsByMarkerId[marker.id] + for decoration in decorations + decorationsState[decoration.id] = { + properties: decoration.properties + screenRange, rangeIsReversed + } + + if layerDecorations = @layerDecorationsByMarkerLayerId[layerId] + for layerDecoration in layerDecorations + decorationsState["#{layerDecoration.id}-#{marker.id}"] = { + properties: layerDecoration.overridePropertiesByMarkerId[marker.id] ? layerDecoration.properties + screenRange, rangeIsReversed + } + + decorationsState + + decorateMarker: (marker, decorationParams) -> + throw new Error("Cannot decorate a destroyed marker") if marker.isDestroyed() + marker = @displayLayer.getMarkerLayer(marker.layer.id).getMarker(marker.id) + decoration = new Decoration(marker, this, decorationParams) + @decorationsByMarkerId[marker.id] ?= [] + @decorationsByMarkerId[marker.id].push(decoration) + @overlayDecorationsById[decoration.id] = decoration if decoration.isType('overlay') + @decorationsById[decoration.id] = decoration + @observeDecoratedLayer(marker.layer) + @scheduleUpdateDecorationsEvent() + @emitter.emit 'did-add-decoration', decoration + decoration + + decorateMarkerLayer: (markerLayer, decorationParams) -> + decoration = new LayerDecoration(markerLayer, this, decorationParams) + @layerDecorationsByMarkerLayerId[markerLayer.id] ?= [] + @layerDecorationsByMarkerLayerId[markerLayer.id].push(decoration) + @observeDecoratedLayer(markerLayer) + @scheduleUpdateDecorationsEvent() + decoration + + decorationsForMarkerId: (markerId) -> + @decorationsByMarkerId[markerId] + + scheduleUpdateDecorationsEvent: -> + if @updatedSynchronously + @emitter.emit 'did-update-decorations' + return + + unless @didUpdateDecorationsEventScheduled + @didUpdateDecorationsEventScheduled = true + process.nextTick => + @didUpdateDecorationsEventScheduled = false + @emitter.emit 'did-update-decorations' + + decorationDidChangeType: (decoration) -> + if decoration.isType('overlay') + @overlayDecorationsById[decoration.id] = decoration + else + delete @overlayDecorationsById[decoration.id] + + didDestroyDecoration: (decoration) -> + {marker} = decoration + return unless decorations = @decorationsByMarkerId[marker.id] + index = decorations.indexOf(decoration) + + if index > -1 + decorations.splice(index, 1) + delete @decorationsById[decoration.id] + @emitter.emit 'did-remove-decoration', decoration + delete @decorationsByMarkerId[marker.id] if decorations.length is 0 + delete @overlayDecorationsById[decoration.id] + @unobserveDecoratedLayer(marker.layer) + @scheduleUpdateDecorationsEvent() + + didDestroyLayerDecoration: (decoration) -> + {markerLayer} = decoration + return unless decorations = @layerDecorationsByMarkerLayerId[markerLayer.id] + index = decorations.indexOf(decoration) + + if index > -1 + decorations.splice(index, 1) + delete @layerDecorationsByMarkerLayerId[markerLayer.id] if decorations.length is 0 + @unobserveDecoratedLayer(markerLayer) + @scheduleUpdateDecorationsEvent() + + observeDecoratedLayer: (layer) -> + @decorationCountsByLayerId[layer.id] ?= 0 + if ++@decorationCountsByLayerId[layer.id] is 1 + @layerUpdateDisposablesByLayerId[layer.id] = layer.onDidUpdate(@scheduleUpdateDecorationsEvent.bind(this)) + + unobserveDecoratedLayer: (layer) -> + if --@decorationCountsByLayerId[layer.id] is 0 + @layerUpdateDisposablesByLayerId[layer.id].dispose() + delete @decorationCountsByLayerId[layer.id] + delete @layerUpdateDisposablesByLayerId[layer.id] diff --git a/src/decoration.coffee b/src/decoration.coffee index b5273d57b..63be29d86 100644 --- a/src/decoration.coffee +++ b/src/decoration.coffee @@ -62,7 +62,7 @@ class Decoration Section: Construction and Destruction ### - constructor: (@marker, @displayBuffer, properties) -> + constructor: (@marker, @decorationManager, properties) -> @emitter = new Emitter @id = nextId() @setProperties properties @@ -78,7 +78,7 @@ class Decoration @markerDestroyDisposable.dispose() @markerDestroyDisposable = null @destroyed = true - @displayBuffer.didDestroyDecoration(this) + @decorationManager.didDestroyDecoration(this) @emitter.emit 'did-destroy' @emitter.dispose() @@ -149,8 +149,8 @@ class Decoration oldProperties = @properties @properties = translateDecorationParamsOldToNew(newProperties) if newProperties.type? - @displayBuffer.decorationDidChangeType(this) - @displayBuffer.scheduleUpdateDecorationsEvent() + @decorationManager.decorationDidChangeType(this) + @decorationManager.scheduleUpdateDecorationsEvent() @emitter.emit 'did-change-properties', {oldProperties, newProperties} ### @@ -175,5 +175,5 @@ class Decoration @properties.flashCount++ @properties.flashClass = klass @properties.flashDuration = duration - @displayBuffer.scheduleUpdateDecorationsEvent() + @decorationManager.scheduleUpdateDecorationsEvent() @emitter.emit 'did-flash' diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee deleted file mode 100644 index 63508e4cb..000000000 --- a/src/display-buffer.coffee +++ /dev/null @@ -1,750 +0,0 @@ -_ = require 'underscore-plus' -{CompositeDisposable, Emitter} = require 'event-kit' -{Point, Range} = require 'text-buffer' -TokenizedBuffer = require './tokenized-buffer' -RowMap = require './row-map' -Model = require './model' -Token = require './token' -Decoration = require './decoration' -LayerDecoration = require './layer-decoration' -{isDoubleWidthCharacter, isHalfWidthCharacter, isKoreanCharacter, isWrapBoundary} = require './text-utils' - -ZERO_WIDTH_NBSP = '\ufeff' - -class BufferToScreenConversionError extends Error - constructor: (@message, @metadata) -> - super - Error.captureStackTrace(this, BufferToScreenConversionError) - -module.exports = -class DisplayBuffer extends Model - verticalScrollMargin: 2 - horizontalScrollMargin: 6 - changeCount: 0 - softWrapped: null - editorWidthInChars: null - lineHeightInPixels: null - defaultCharWidth: null - height: null - width: null - didUpdateDecorationsEventScheduled: false - updatedSynchronously: false - - @deserialize: (state, atomEnvironment) -> - state.tokenizedBuffer = TokenizedBuffer.deserialize(state.tokenizedBuffer, atomEnvironment) - state.displayLayer = state.tokenizedBuffer.buffer.getDisplayLayer(state.displayLayerId) - state.config = atomEnvironment.config - state.assert = atomEnvironment.assert - state.grammarRegistry = atomEnvironment.grammars - state.packageManager = atomEnvironment.packages - new this(state) - - constructor: (params={}) -> - super - - { - tabLength, @editorWidthInChars, @tokenizedBuffer, buffer, @ignoreInvisibles, - @largeFileMode, @config, @assert, @grammarRegistry, @packageManager, @displayLayer - } = params - - @emitter = new Emitter - @disposables = new CompositeDisposable - - @tokenizedBuffer ?= new TokenizedBuffer({ - tabLength, buffer, @largeFileMode, @config, - @grammarRegistry, @packageManager, @assert - }) - @buffer = @tokenizedBuffer.buffer - @displayLayer ?= @buffer.addDisplayLayer() - @displayLayer.setTextDecorationLayer(@tokenizedBuffer) - @charWidthsByScope = {} - @defaultMarkerLayer = @displayLayer.addMarkerLayer() - @decorationsById = {} - @decorationsByMarkerId = {} - @overlayDecorationsById = {} - @layerDecorationsByMarkerLayerId = {} - @decorationCountsByLayerId = {} - @layerUpdateDisposablesByLayerId = {} - - @disposables.add @tokenizedBuffer.observeGrammar @subscribeToScopedConfigSettings - @disposables.add @buffer.onDidCreateMarker @didCreateDefaultLayerMarker - - subscribeToScopedConfigSettings: => - @scopedConfigSubscriptions?.dispose() - @scopedConfigSubscriptions = subscriptions = new CompositeDisposable - - scopeDescriptor = @getRootScopeDescriptor() - subscriptions.add @config.onDidChange 'editor.tabLength', scope: scopeDescriptor, @resetDisplayLayer.bind(this) - subscriptions.add @config.onDidChange 'editor.invisibles', scope: scopeDescriptor, @resetDisplayLayer.bind(this) - subscriptions.add @config.onDidChange 'editor.showInvisibles', scope: scopeDescriptor, @resetDisplayLayer.bind(this) - subscriptions.add @config.onDidChange 'editor.showIndentGuide', scope: scopeDescriptor, @resetDisplayLayer.bind(this) - subscriptions.add @config.onDidChange 'editor.softWrap', scope: scopeDescriptor, @resetDisplayLayer.bind(this) - subscriptions.add @config.onDidChange 'editor.softWrapHangingIndent', scope: scopeDescriptor, @resetDisplayLayer.bind(this) - subscriptions.add @config.onDidChange 'editor.softWrapAtPreferredLineLength', scope: scopeDescriptor, @resetDisplayLayer.bind(this) - subscriptions.add @config.onDidChange 'editor.preferredLineLength', scope: scopeDescriptor, @resetDisplayLayer.bind(this) - - @resetDisplayLayer() - - serialize: -> - deserializer: 'DisplayBuffer' - id: @id - softWrapped: @isSoftWrapped() - editorWidthInChars: @editorWidthInChars - tokenizedBuffer: @tokenizedBuffer.serialize() - largeFileMode: @largeFileMode - displayLayerId: @displayLayer.id - - copy: -> - new DisplayBuffer({ - @buffer, tabLength: @getTabLength(), @largeFileMode, @config, @assert, - @grammarRegistry, @packageManager, displayLayer: @buffer.copyDisplayLayer(@displayLayer.id) - }) - - resetDisplayLayer: -> - scopeDescriptor = @getRootScopeDescriptor() - invisibles = - if @config.get('editor.showInvisibles', scope: scopeDescriptor) and not @ignoreInvisibles - @config.get('editor.invisibles', scope: scopeDescriptor) - else - {} - - softWrapColumn = - if @isSoftWrapped() - if @config.get('editor.softWrapAtPreferredLineLength', scope: scopeDescriptor) - @config.get('editor.preferredLineLength', scope: scopeDescriptor) - else - @getEditorWidthInChars() - else - Infinity - - @displayLayer.reset({ - invisibles: invisibles - softWrapColumn: softWrapColumn - showIndentGuides: @config.get('editor.showIndentGuide', scope: scopeDescriptor) - tabLength: @getTabLength(), - ratioForCharacter: @ratioForCharacter.bind(this), - isWrapBoundary: isWrapBoundary, - foldCharacter: ZERO_WIDTH_NBSP - }) - - onDidChangeSoftWrapped: (callback) -> - @emitter.on 'did-change-soft-wrapped', callback - - onDidChangeGrammar: (callback) -> - @tokenizedBuffer.onDidChangeGrammar(callback) - - onDidTokenize: (callback) -> - @tokenizedBuffer.onDidTokenize(callback) - - onDidChange: (callback) -> - @emitter.on 'did-change', callback - - onDidChangeCharacterWidths: (callback) -> - @emitter.on 'did-change-character-widths', callback - - onDidRequestAutoscroll: (callback) -> - @emitter.on 'did-request-autoscroll', callback - - observeDecorations: (callback) -> - callback(decoration) for decoration in @getDecorations() - @onDidAddDecoration(callback) - - onDidAddDecoration: (callback) -> - @emitter.on 'did-add-decoration', callback - - onDidRemoveDecoration: (callback) -> - @emitter.on 'did-remove-decoration', callback - - onDidCreateMarker: (callback) -> - @emitter.on 'did-create-marker', callback - - onDidUpdateMarkers: (callback) -> - @emitter.on 'did-update-markers', callback - - onDidUpdateDecorations: (callback) -> - @emitter.on 'did-update-decorations', callback - - # Sets the visibility of the tokenized buffer. - # - # visible - A {Boolean} indicating of the tokenized buffer is shown - setVisible: (visible) -> @tokenizedBuffer.setVisible(visible) - - setUpdatedSynchronously: (@updatedSynchronously) -> - - getVerticalScrollMargin: -> - maxScrollMargin = Math.floor(((@getHeight() / @getLineHeightInPixels()) - 1) / 2) - Math.min(@verticalScrollMargin, maxScrollMargin) - - setVerticalScrollMargin: (@verticalScrollMargin) -> @verticalScrollMargin - - getHorizontalScrollMargin: -> Math.min(@horizontalScrollMargin, Math.floor(((@getWidth() / @getDefaultCharWidth()) - 1) / 2)) - setHorizontalScrollMargin: (@horizontalScrollMargin) -> @horizontalScrollMargin - - getHeight: -> - @height - - setHeight: (@height) -> - @height - - getWidth: -> - @width - - setWidth: (newWidth) -> - oldWidth = @width - @width = newWidth - @resetDisplayLayer() if newWidth isnt oldWidth and @isSoftWrapped() - @width - - getLineHeightInPixels: -> @lineHeightInPixels - setLineHeightInPixels: (@lineHeightInPixels) -> @lineHeightInPixels - - ratioForCharacter: (character) -> - if isKoreanCharacter(character) - @getKoreanCharWidth() / @getDefaultCharWidth() - else if isHalfWidthCharacter(character) - @getHalfWidthCharWidth() / @getDefaultCharWidth() - else if isDoubleWidthCharacter(character) - @getDoubleWidthCharWidth() / @getDefaultCharWidth() - else - 1 - - getKoreanCharWidth: -> @koreanCharWidth - - getHalfWidthCharWidth: -> @halfWidthCharWidth - - getDoubleWidthCharWidth: -> @doubleWidthCharWidth - - getDefaultCharWidth: -> @defaultCharWidth - - setDefaultCharWidth: (defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth) -> - doubleWidthCharWidth ?= defaultCharWidth - halfWidthCharWidth ?= defaultCharWidth - koreanCharWidth ?= defaultCharWidth - if defaultCharWidth isnt @defaultCharWidth or doubleWidthCharWidth isnt @doubleWidthCharWidth and halfWidthCharWidth isnt @halfWidthCharWidth and koreanCharWidth isnt @koreanCharWidth - @defaultCharWidth = defaultCharWidth - @doubleWidthCharWidth = doubleWidthCharWidth - @halfWidthCharWidth = halfWidthCharWidth - @koreanCharWidth = koreanCharWidth - @resetDisplayLayer() if @isSoftWrapped() and @getEditorWidthInChars()? - defaultCharWidth - - getCursorWidth: -> 1 - - scrollToScreenRange: (screenRange, options = {}) -> - scrollEvent = {screenRange, options} - @emitter.emit "did-request-autoscroll", scrollEvent - - scrollToScreenPosition: (screenPosition, options) -> - @scrollToScreenRange(new Range(screenPosition, screenPosition), options) - - scrollToBufferPosition: (bufferPosition, options) -> - @scrollToScreenPosition(@screenPositionForBufferPosition(bufferPosition), options) - - # Retrieves the current tab length. - # - # Returns a {Number}. - getTabLength: -> - if @tabLength? - @tabLength - else - @config.get('editor.tabLength', scope: @getRootScopeDescriptor()) - - # Specifies the tab length. - # - # tabLength - A {Number} that defines the new tab length. - setTabLength: (tabLength) -> - return if tabLength is @tabLength - - @tabLength = tabLength - @tokenizedBuffer.setTabLength(@tabLength) - @resetDisplayLayer() - - setIgnoreInvisibles: (ignoreInvisibles) -> - return if ignoreInvisibles is @ignoreInvisibles - - @ignoreInvisibles = ignoreInvisibles - @resetDisplayLayer() - - setSoftWrapped: (softWrapped) -> - if softWrapped isnt @softWrapped - @softWrapped = softWrapped - @resetDisplayLayer() - softWrapped = @isSoftWrapped() - @emitter.emit 'did-change-soft-wrapped', softWrapped - softWrapped - else - @isSoftWrapped() - - isSoftWrapped: -> - if @largeFileMode - false - else - scopeDescriptor = @getRootScopeDescriptor() - @softWrapped ? @config.get('editor.softWrap', scope: scopeDescriptor) ? false - - # Set the number of characters that fit horizontally in the editor. - # - # editorWidthInChars - A {Number} of characters. - setEditorWidthInChars: (editorWidthInChars) -> - if editorWidthInChars > 0 - previousWidthInChars = @editorWidthInChars - @editorWidthInChars = editorWidthInChars - if editorWidthInChars isnt previousWidthInChars and @isSoftWrapped() - @resetDisplayLayer() - - # Returns the editor width in characters for soft wrap. - getEditorWidthInChars: -> - width = @getWidth() - if width? and @defaultCharWidth > 0 - Math.max(0, Math.floor(width / @defaultCharWidth)) - else - @editorWidthInChars - - indentLevelForLine: (line) -> - @tokenizedBuffer.indentLevelForLine(line) - - # Given starting and ending screen rows, this returns an array of the - # buffer rows corresponding to every screen row in the range - # - # startScreenRow - The screen row {Number} to start at - # endScreenRow - The screen row {Number} to end at (default: the last screen row) - # - # Returns an {Array} of buffer rows as {Numbers}s. - bufferRowsForScreenRows: (startScreenRow, endScreenRow) -> - for screenRow in [startScreenRow..endScreenRow] - @bufferRowForScreenRow(screenRow) - - # Creates a new fold between two row numbers. - # - # startRow - The row {Number} to start folding at - # endRow - The row {Number} to end the fold - # - # Returns the new {Fold}. - foldBufferRowRange: (startRow, endRow) -> - @displayLayer.foldBufferRange(Range(Point(startRow, Infinity), Point(endRow, Infinity))) - - isFoldedAtBufferRow: (bufferRow) -> - @displayLayer.foldsIntersectingBufferRange(Range(Point(bufferRow, 0), Point(bufferRow, Infinity))).length > 0 - - isFoldedAtScreenRow: (screenRow) -> - @isFoldedAtBufferRow(@bufferRowForScreenRow(screenRow)) - - isFoldableAtBufferRow: (row) -> - @tokenizedBuffer.isFoldableAtRow(row) - - # Removes any folds found that contain the given buffer row. - # - # bufferRow - The buffer row {Number} to check against - unfoldBufferRow: (bufferRow) -> - @displayLayer.destroyFoldsIntersectingBufferRange(Range(Point(bufferRow, 0), Point(bufferRow, Infinity))) - - # Given a buffer row, this converts it into a screen row. - # - # bufferRow - A {Number} representing a buffer row - # - # Returns a {Number}. - screenRowForBufferRow: (bufferRow) -> - if @largeFileMode - bufferRow - else - @displayLayer.translateScreenPosition(Point(screenRow, 0)).row - - lastScreenRowForBufferRow: (bufferRow) -> - if @largeFileMode - bufferRow - else - @displayLayer.translateBufferPosition(Point(bufferRow, 0), clip: 'forward').row - - # Given a screen row, this converts it into a buffer row. - # - # screenRow - A {Number} representing a screen row - # - # Returns a {Number}. - bufferRowForScreenRow: (screenRow) -> - @displayLayer.translateScreenPosition(Point(screenRow, 0)).row - - # Given a buffer range, this converts it into a screen position. - # - # bufferRange - The {Range} to convert - # - # Returns a {Range}. - screenRangeForBufferRange: (bufferRange, options) -> - bufferRange = Range.fromObject(bufferRange) - start = @screenPositionForBufferPosition(bufferRange.start, options) - end = @screenPositionForBufferPosition(bufferRange.end, options) - new Range(start, end) - - # Given a screen range, this converts it into a buffer position. - # - # screenRange - The {Range} to convert - # - # Returns a {Range}. - bufferRangeForScreenRange: (screenRange) -> - screenRange = Range.fromObject(screenRange) - start = @bufferPositionForScreenPosition(screenRange.start) - end = @bufferPositionForScreenPosition(screenRange.end) - new Range(start, end) - - # Gets the number of screen lines. - # - # Returns a {Number}. - getLineCount: -> - @displayLayer.getScreenLineCount() - - # Gets the number of the last screen line. - # - # Returns a {Number}. - getLastRow: -> - @getLineCount() - 1 - - # Given a buffer position, this converts it into a screen position. - # - # bufferPosition - An object that represents a buffer position. It can be either - # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} - # options - A hash of options with the following keys: - # wrapBeyondNewlines: - # wrapAtSoftNewlines: - # - # Returns a {Point}. - screenPositionForBufferPosition: (bufferPosition, options) -> - throw new Error("This TextEditor has been destroyed") if @isDestroyed() - - @displayLayer.translateBufferPosition(bufferPosition, options) - - # Given a buffer position, this converts it into a screen position. - # - # screenPosition - An object that represents a buffer position. It can be either - # an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point} - # options - A hash of options with the following keys: - # wrapBeyondNewlines: - # wrapAtSoftNewlines: - # - # Returns a {Point}. - bufferPositionForScreenPosition: (screenPosition, options) -> - return @displayLayer.translateScreenPosition(screenPosition, options) - - # Retrieves the grammar's token scopeDescriptor for a buffer position. - # - # bufferPosition - A {Point} in the {TextBuffer} - # - # Returns a {ScopeDescriptor}. - scopeDescriptorForBufferPosition: (bufferPosition) -> - @tokenizedBuffer.scopeDescriptorForPosition(bufferPosition) - - bufferRangeForScopeAtPosition: (selector, position) -> - @tokenizedBuffer.bufferRangeForScopeAtPosition(selector, position) - - # Retrieves the grammar's token for a buffer position. - # - # bufferPosition - A {Point} in the {TextBuffer}. - # - # Returns a {Token}. - tokenForBufferPosition: (bufferPosition) -> - @tokenizedBuffer.tokenForPosition(bufferPosition) - - # Get the grammar for this buffer. - # - # Returns the current {Grammar} or the {NullGrammar}. - getGrammar: -> - @tokenizedBuffer.grammar - - # Sets the grammar for the buffer. - # - # grammar - Sets the new grammar rules - setGrammar: (grammar) -> - @tokenizedBuffer.setGrammar(grammar) - - # Reloads the current grammar. - reloadGrammar: -> - @tokenizedBuffer.reloadGrammar() - - # Given a position, this clips it to a real position. - # - # For example, if `position`'s row exceeds the row count of the buffer, - # or if its column goes beyond a line's length, this "sanitizes" the value - # to a real position. - # - # position - The {Point} to clip - # options - A hash with the following values: - # wrapBeyondNewlines: if `true`, continues wrapping past newlines - # wrapAtSoftNewlines: if `true`, continues wrapping past soft newlines - # skipSoftWrapIndentation: if `true`, skips soft wrap indentation without wrapping to the previous line - # screenLine: if `true`, indicates that you're using a line number, not a row number - # - # Returns the new, clipped {Point}. Note that this could be the same as `position` if no clipping was performed. - clipScreenPosition: (screenPosition, options={}) -> - @displayLayer.clipScreenPosition(screenPosition, options) - - # Clip the start and end of the given range to valid positions on screen. - # See {::clipScreenPosition} for more information. - # - # * `range` The {Range} to clip. - # * `options` (optional) See {::clipScreenPosition} `options`. - # Returns a {Range}. - clipScreenRange: (range, options) -> - start = @clipScreenPosition(range.start, options) - end = @clipScreenPosition(range.end, options) - - new Range(start, end) - - # Calculates a {Range} representing the start of the {TextBuffer} until the end. - # - # Returns a {Range}. - rangeForAllLines: -> - new Range([0, 0], @clipScreenPosition([Infinity, Infinity])) - - decorationForId: (id) -> - @decorationsById[id] - - getDecorations: (propertyFilter) -> - allDecorations = [] - for markerId, decorations of @decorationsByMarkerId - allDecorations.push(decorations...) if decorations? - if propertyFilter? - allDecorations = allDecorations.filter (decoration) -> - for key, value of propertyFilter - return false unless decoration.properties[key] is value - true - allDecorations - - getLineDecorations: (propertyFilter) -> - @getDecorations(propertyFilter).filter (decoration) -> decoration.isType('line') - - getLineNumberDecorations: (propertyFilter) -> - @getDecorations(propertyFilter).filter (decoration) -> decoration.isType('line-number') - - getHighlightDecorations: (propertyFilter) -> - @getDecorations(propertyFilter).filter (decoration) -> decoration.isType('highlight') - - getOverlayDecorations: (propertyFilter) -> - result = [] - for id, decoration of @overlayDecorationsById - result.push(decoration) - if propertyFilter? - result.filter (decoration) -> - for key, value of propertyFilter - return false unless decoration.properties[key] is value - true - else - result - - decorationsForScreenRowRange: (startScreenRow, endScreenRow) -> - decorationsByMarkerId = {} - for marker in @findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow]) - if decorations = @decorationsByMarkerId[marker.id] - decorationsByMarkerId[marker.id] = decorations - decorationsByMarkerId - - decorationsStateForScreenRowRange: (startScreenRow, endScreenRow) -> - decorationsState = {} - - for layerId of @decorationCountsByLayerId - layer = @getMarkerLayer(layerId) - - for marker in layer.findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow]) when marker.isValid() - screenRange = marker.getScreenRange() - rangeIsReversed = marker.isReversed() - - if decorations = @decorationsByMarkerId[marker.id] - for decoration in decorations - decorationsState[decoration.id] = { - properties: decoration.properties - screenRange, rangeIsReversed - } - - if layerDecorations = @layerDecorationsByMarkerLayerId[layerId] - for layerDecoration in layerDecorations - decorationsState["#{layerDecoration.id}-#{marker.id}"] = { - properties: layerDecoration.overridePropertiesByMarkerId[marker.id] ? layerDecoration.properties - screenRange, rangeIsReversed - } - - decorationsState - - decorateMarker: (marker, decorationParams) -> - throw new Error("Cannot decorate a destroyed marker") if marker.isDestroyed() - marker = @getMarkerLayer(marker.layer.id).getMarker(marker.id) - decoration = new Decoration(marker, this, decorationParams) - @decorationsByMarkerId[marker.id] ?= [] - @decorationsByMarkerId[marker.id].push(decoration) - @overlayDecorationsById[decoration.id] = decoration if decoration.isType('overlay') - @decorationsById[decoration.id] = decoration - @observeDecoratedLayer(marker.layer) - @scheduleUpdateDecorationsEvent() - @emitter.emit 'did-add-decoration', decoration - decoration - - decorateMarkerLayer: (markerLayer, decorationParams) -> - decoration = new LayerDecoration(markerLayer, this, decorationParams) - @layerDecorationsByMarkerLayerId[markerLayer.id] ?= [] - @layerDecorationsByMarkerLayerId[markerLayer.id].push(decoration) - @observeDecoratedLayer(markerLayer) - @scheduleUpdateDecorationsEvent() - decoration - - decorationsForMarkerId: (markerId) -> - @decorationsByMarkerId[markerId] - - # Retrieves a {DisplayMarker} based on its id. - # - # id - A {Number} representing a marker id - # - # Returns the {DisplayMarker} (if it exists). - getMarker: (id) -> - @defaultMarkerLayer.getMarker(id) - - # Retrieves the active markers in the buffer. - # - # Returns an {Array} of existing {DisplayMarker}s. - getMarkers: -> - @defaultMarkerLayer.getMarkers() - - getMarkerCount: -> - @buffer.getMarkerCount() - - # Public: Constructs a new marker at the given screen range. - # - # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {DisplayMarker} constructor - # - # Returns a {Number} representing the new marker's ID. - markScreenRange: (screenRange, options) -> - @defaultMarkerLayer.markScreenRange(screenRange, options) - - # Public: Constructs a new marker at the given buffer range. - # - # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {DisplayMarker} constructor - # - # Returns a {Number} representing the new marker's ID. - markBufferRange: (bufferRange, options) -> - @defaultMarkerLayer.markBufferRange(bufferRange, options) - - # Public: Constructs a new marker at the given screen position. - # - # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {DisplayMarker} constructor - # - # Returns a {Number} representing the new marker's ID. - markScreenPosition: (screenPosition, options) -> - @defaultMarkerLayer.markScreenPosition(screenPosition, options) - - # Public: Constructs a new marker at the given buffer position. - # - # range - The marker {Range} (representing the distance between the head and tail) - # options - Options to pass to the {DisplayMarker} constructor - # - # Returns a {Number} representing the new marker's ID. - markBufferPosition: (bufferPosition, options) -> - @defaultMarkerLayer.markBufferPosition(bufferPosition, options) - - # Finds the first marker satisfying the given attributes - # - # Refer to {DisplayBuffer::findMarkers} for details. - # - # Returns a {DisplayMarker} or null - findMarker: (params) -> - @defaultMarkerLayer.findMarkers(params)[0] - - # Public: Find all markers satisfying a set of parameters. - # - # params - An {Object} containing parameters that all returned markers must - # satisfy. Unreserved keys will be compared against the markers' custom - # properties. There are also the following reserved keys with special - # meaning for the query: - # :startBufferRow - A {Number}. Only returns markers starting at this row in - # buffer coordinates. - # :endBufferRow - A {Number}. Only returns markers ending at this row in - # buffer coordinates. - # :containsBufferRange - A {Range} or range-compatible {Array}. Only returns - # markers containing this range in buffer coordinates. - # :containsBufferPosition - A {Point} or point-compatible {Array}. Only - # returns markers containing this position in buffer coordinates. - # :containedInBufferRange - A {Range} or range-compatible {Array}. Only - # returns markers contained within this range. - # - # Returns an {Array} of {DisplayMarker}s - findMarkers: (params) -> - @defaultMarkerLayer.findMarkers(params) - - addMarkerLayer: (options) -> - @displayLayer.addMarkerLayer(options) - - getMarkerLayer: (id) -> - @displayLayer.getMarkerLayer(id) - - getDefaultMarkerLayer: -> @defaultMarkerLayer - - refreshMarkerScreenPositions: -> - @defaultMarkerLayer.refreshMarkerScreenPositions() - layer.refreshMarkerScreenPositions() for id, layer of @customMarkerLayersById - return - - destroyed: -> - @displayLayer.destroy() - @defaultMarkerLayer.destroy() - @scopedConfigSubscriptions.dispose() - @disposables.dispose() - @tokenizedBuffer.destroy() - - getRootScopeDescriptor: -> - @tokenizedBuffer.rootScopeDescriptor - - didCreateDefaultLayerMarker: (textBufferMarker) => - if marker = @getMarker(textBufferMarker.id) - # The marker might have been removed in some other handler called before - # this one. Only emit when the marker still exists. - @emitter.emit 'did-create-marker', marker - - scheduleUpdateDecorationsEvent: -> - if @updatedSynchronously - @emitter.emit 'did-update-decorations' - return - - unless @didUpdateDecorationsEventScheduled - @didUpdateDecorationsEventScheduled = true - process.nextTick => - @didUpdateDecorationsEventScheduled = false - @emitter.emit 'did-update-decorations' - - decorationDidChangeType: (decoration) -> - if decoration.isType('overlay') - @overlayDecorationsById[decoration.id] = decoration - else - delete @overlayDecorationsById[decoration.id] - - didDestroyDecoration: (decoration) -> - {marker} = decoration - return unless decorations = @decorationsByMarkerId[marker.id] - index = decorations.indexOf(decoration) - - if index > -1 - decorations.splice(index, 1) - delete @decorationsById[decoration.id] - @emitter.emit 'did-remove-decoration', decoration - delete @decorationsByMarkerId[marker.id] if decorations.length is 0 - delete @overlayDecorationsById[decoration.id] - @unobserveDecoratedLayer(marker.layer) - @scheduleUpdateDecorationsEvent() - - didDestroyLayerDecoration: (decoration) -> - {markerLayer} = decoration - return unless decorations = @layerDecorationsByMarkerLayerId[markerLayer.id] - index = decorations.indexOf(decoration) - - if index > -1 - decorations.splice(index, 1) - delete @layerDecorationsByMarkerLayerId[markerLayer.id] if decorations.length is 0 - @unobserveDecoratedLayer(markerLayer) - @scheduleUpdateDecorationsEvent() - - observeDecoratedLayer: (layer) -> - @decorationCountsByLayerId[layer.id] ?= 0 - if ++@decorationCountsByLayerId[layer.id] is 1 - @layerUpdateDisposablesByLayerId[layer.id] = layer.onDidUpdate(@scheduleUpdateDecorationsEvent.bind(this)) - - unobserveDecoratedLayer: (layer) -> - if --@decorationCountsByLayerId[layer.id] is 0 - @layerUpdateDisposablesByLayerId[layer.id].dispose() - delete @decorationCountsByLayerId[layer.id] - delete @layerUpdateDisposablesByLayerId[layer.id] diff --git a/src/language-mode.coffee b/src/language-mode.coffee index 605f8454b..a0392acf6 100644 --- a/src/language-mode.coffee +++ b/src/language-mode.coffee @@ -131,7 +131,7 @@ class LanguageMode for currentRow in [bufferRow..0] by -1 [startRow, endRow] = @rowRangeForFoldAtBufferRow(currentRow) ? [] continue unless startRow? and startRow <= bufferRow <= endRow - unless @editor.displayBuffer.isFoldedAtBufferRow(startRow) + unless @editor.isFoldedAtBufferRow(startRow) return @editor.foldBufferRowRange(startRow, endRow) # Find the row range for a fold at a given bufferRow. Will handle comments @@ -146,19 +146,19 @@ class LanguageMode rowRange rowRangeForCommentAtBufferRow: (bufferRow) -> - return unless @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment() + return unless @editor.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment() startRow = bufferRow endRow = bufferRow if bufferRow > 0 for currentRow in [bufferRow-1..0] by -1 - break unless @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment() + break unless @editor.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment() startRow = currentRow if bufferRow < @buffer.getLastRow() for currentRow in [bufferRow+1..@buffer.getLastRow()] by 1 - break unless @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment() + break unless @editor.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment() endRow = currentRow return [startRow, endRow] if startRow isnt endRow @@ -181,13 +181,13 @@ class LanguageMode [bufferRow, foldEndRow] isFoldableAtBufferRow: (bufferRow) -> - @editor.displayBuffer.tokenizedBuffer.isFoldableAtRow(bufferRow) + @editor.tokenizedBuffer.isFoldableAtRow(bufferRow) # Returns a {Boolean} indicating whether the line at the given buffer # row is a comment. isLineCommentedAtBufferRow: (bufferRow) -> return false unless 0 <= bufferRow <= @editor.getLastBufferRow() - @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment() + @editor.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment() # Find a row range for a 'paragraph' around specified bufferRow. A paragraph # is a block of text bounded by and empty line or a block of text that is not @@ -240,11 +240,11 @@ class LanguageMode # Returns a {Number}. suggestedIndentForBufferRow: (bufferRow, options) -> line = @buffer.lineForRow(bufferRow) - tokenizedLine = @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow) + tokenizedLine = @editor.tokenizedBuffer.tokenizedLineForRow(bufferRow) @suggestedIndentForTokenizedLineAtBufferRow(bufferRow, line, tokenizedLine, options) suggestedIndentForLineAtBufferRow: (bufferRow, line, options) -> - tokenizedLine = @editor.displayBuffer.tokenizedBuffer.buildTokenizedLineForRowWithText(bufferRow, line) + tokenizedLine = @editor.tokenizedBuffer.buildTokenizedLineForRowWithText(bufferRow, line) @suggestedIndentForTokenizedLineAtBufferRow(bufferRow, line, tokenizedLine, options) suggestedIndentForTokenizedLineAtBufferRow: (bufferRow, line, tokenizedLine, options) -> diff --git a/src/layer-decoration.coffee b/src/layer-decoration.coffee index 7d2396e0d..e00e024cb 100644 --- a/src/layer-decoration.coffee +++ b/src/layer-decoration.coffee @@ -7,7 +7,7 @@ nextId = -> idCounter++ # layer. Created via {TextEditor::decorateMarkerLayer}. module.exports = class LayerDecoration - constructor: (@markerLayer, @displayBuffer, @properties) -> + constructor: (@markerLayer, @decorationManager, @properties) -> @id = nextId() @destroyed = false @markerLayerDestroyedDisposable = @markerLayer.onDidDestroy => @destroy() @@ -19,7 +19,7 @@ class LayerDecoration @markerLayerDestroyedDisposable.dispose() @markerLayerDestroyedDisposable = null @destroyed = true - @displayBuffer.didDestroyLayerDecoration(this) + @decorationManager.didDestroyLayerDecoration(this) # Essential: Determine whether this decoration is destroyed. # @@ -44,7 +44,7 @@ class LayerDecoration setProperties: (newProperties) -> return if @destroyed @properties = newProperties - @displayBuffer.scheduleUpdateDecorationsEvent() + @decorationManager.scheduleUpdateDecorationsEvent() # Essential: Override the decoration properties for a specific marker. # @@ -58,4 +58,4 @@ class LayerDecoration @overridePropertiesByMarkerId[marker.id] = properties else delete @overridePropertiesByMarkerId[marker.id] - @displayBuffer.scheduleUpdateDecorationsEvent() + @decorationManager.scheduleUpdateDecorationsEvent() diff --git a/src/marker-observation-window.coffee b/src/marker-observation-window.coffee index aa7b71f69..ffb92c0ab 100644 --- a/src/marker-observation-window.coffee +++ b/src/marker-observation-window.coffee @@ -1,9 +1,9 @@ module.exports = class MarkerObservationWindow - constructor: (@displayBuffer, @bufferWindow) -> + constructor: (@decorationManager, @bufferWindow) -> setScreenRange: (range) -> - @bufferWindow.setRange(@displayBuffer.bufferRangeForScreenRange(range)) + @bufferWindow.setRange(@decorationManager.bufferRangeForScreenRange(range)) setBufferRange: (range) -> @bufferWindow.setRange(range) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index ffb5a4706..40dd451d7 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -4,7 +4,8 @@ Grim = require 'grim' {CompositeDisposable, Emitter} = require 'event-kit' {Point, Range} = TextBuffer = require 'text-buffer' LanguageMode = require './language-mode' -DisplayBuffer = require './display-buffer' +DecorationManager = require './decoration-manager' +TokenizedBuffer = require './tokenized-buffer' Cursor = require './cursor' Model = require './model' Selection = require './selection' @@ -12,6 +13,9 @@ TextMateScopeSelector = require('first-mate').ScopeSelector {Directory} = require "pathwatcher" GutterContainer = require './gutter-container' TextEditorElement = require './text-editor-element' +{isDoubleWidthCharacter, isHalfWidthCharacter, isKoreanCharacter, isWrapBoundary} = require './text-utils' + +ZERO_WIDTH_NBSP = '\ufeff' # Essential: This class represents all essential editing state for a single # {TextBuffer}, including cursor and selection positions, folds, and soft wraps. @@ -63,21 +67,30 @@ class TextEditor extends Model selectionFlashDuration: 500 gutterContainer: null editorElement: null + verticalScrollMargin: 2 + horizontalScrollMargin: 6 + softWrapped: null + editorWidthInChars: null + lineHeightInPixels: null + defaultCharWidth: null + height: null + width: null Object.defineProperty @prototype, "element", get: -> @getElement() @deserialize: (state, atomEnvironment) -> try - displayBuffer = DisplayBuffer.deserialize(state.displayBuffer, atomEnvironment) + state.tokenizedBuffer = TokenizedBuffer.deserialize(state.tokenizedBuffer, atomEnvironment) catch error if error.syscall is 'read' return # Error reading the file, don't deserialize an editor for it else throw error - state.displayBuffer = displayBuffer - state.selectionsMarkerLayer = displayBuffer.getMarkerLayer(state.selectionsMarkerLayerId) + state.buffer = state.tokenizedBuffer.buffer + state.displayLayer = state.buffer.getDisplayLayer(state.displayLayerId) + state.selectionsMarkerLayer = state.displayLayer.getMarkerLayer(state.selectionsMarkerLayerId) state.config = atomEnvironment.config state.notificationManager = atomEnvironment.notifications state.packageManager = atomEnvironment.packages @@ -97,13 +110,19 @@ class TextEditor extends Model super { - @softTabs, @firstVisibleScreenRow, @firstVisibleScreenColumn, initialLine, initialColumn, tabLength, - softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, - @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, + @softTabs, @firstVisibleScreenRow, @firstVisibleScreenColumn, initialLine, initialColumn, @tabLength, + @softWrapped, @decorationManager, @selectionsMarkerLayer, @buffer, suppressCursorCreation, + @mini, @placeholderText, lineNumberGutterVisible, @largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, grammar, showInvisibles, @autoHeight, @scrollPastEnd + @project, @assert, @applicationDelegate, grammar, @showInvisibles, @autoHeight, @scrollPastEnd } = params + { + tabLength, @editorWidthInChars, @tokenizedBuffer, buffer, @ignoreInvisibles, + @largeFileMode, @config, @assert, @grammarRegistry, @packageManager, @displayLayer + } = params + + throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? throw new Error("Must pass a notificationManager parameter when constructing TextEditors") unless @notificationManager? throw new Error("Must pass a packageManager parameter when constructing TextEditors") unless @packageManager? @@ -124,17 +143,23 @@ class TextEditor extends Model @scrollPastEnd ?= true @hasTerminatedPendingState = false - showInvisibles ?= true + @showInvisibles ?= true - buffer ?= new TextBuffer - @displayBuffer ?= new DisplayBuffer({ - buffer, tabLength, softWrapped, ignoreInvisibles: @mini or not showInvisibles, largeFileMode, - @config, @assert, @grammarRegistry, @packageManager + @buffer ?= new TextBuffer + @tokenizedBuffer ?= new TokenizedBuffer({ + tabLength, @buffer, @largeFileMode, @config, + @grammarRegistry, @packageManager, @assert }) - {@buffer, @displayLayer} = @displayBuffer + @displayLayer ?= @buffer.addDisplayLayer() + @displayLayer.setTextDecorationLayer(@tokenizedBuffer) + @defaultMarkerLayer = @displayLayer.addMarkerLayer() + @selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true) + + @decorationManager = new DecorationManager(@displayLayer, @defaultMarkerLayer) + @decorateMarkerLayer(@displayLayer.foldsMarkerLayer, {type: 'line-number', class: 'folded'}) - @selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true) + @disposables.add @tokenizedBuffer.observeGrammar @subscribeToScopedConfigSettings for marker in @selectionsMarkerLayer.getMarkers() marker.setProperties(preserveFolds: true) @@ -142,7 +167,7 @@ class TextEditor extends Model @subscribeToTabTypeConfig() @subscribeToBuffer() - @subscribeToDisplayBuffer() + @subscribeToDisplayLayer() if @cursors.length is 0 and not suppressCursorCreation initialLine = Math.max(parseInt(initialLine) or 0, 0) @@ -168,8 +193,12 @@ class TextEditor extends Model softTabs: @softTabs firstVisibleScreenRow: @getFirstVisibleScreenRow() firstVisibleScreenColumn: @getFirstVisibleScreenColumn() - displayBuffer: @displayBuffer.serialize() selectionsMarkerLayerId: @selectionsMarkerLayer.id + softWrapped: @isSoftWrapped() + editorWidthInChars: @editorWidthInChars + tokenizedBuffer: @tokenizedBuffer.serialize() + largeFileMode: @largeFileMode + displayLayerId: @displayLayer.id registered: atom.textEditors.editors.has this subscribeToBuffer: -> @@ -194,10 +223,26 @@ class TextEditor extends Model onDidTerminatePendingState: (callback) -> @emitter.on 'did-terminate-pending-state', callback - subscribeToDisplayBuffer: -> + subscribeToScopedConfigSettings: => + @scopedConfigSubscriptions?.dispose() + @scopedConfigSubscriptions = subscriptions = new CompositeDisposable + + scopeDescriptor = @getRootScopeDescriptor() + subscriptions.add @config.onDidChange 'editor.tabLength', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + subscriptions.add @config.onDidChange 'editor.invisibles', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + subscriptions.add @config.onDidChange 'editor.showInvisibles', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + subscriptions.add @config.onDidChange 'editor.showIndentGuide', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + subscriptions.add @config.onDidChange 'editor.softWrap', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + subscriptions.add @config.onDidChange 'editor.softWrapHangingIndent', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + subscriptions.add @config.onDidChange 'editor.softWrapAtPreferredLineLength', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + subscriptions.add @config.onDidChange 'editor.preferredLineLength', scope: scopeDescriptor, @resetDisplayLayer.bind(this) + + @resetDisplayLayer() + + subscribeToDisplayLayer: -> @disposables.add @selectionsMarkerLayer.onDidCreateMarker @addSelection.bind(this) - @disposables.add @displayBuffer.onDidChangeGrammar @handleGrammarChange.bind(this) - @disposables.add @displayBuffer.onDidTokenize @handleTokenization.bind(this) + @disposables.add @tokenizedBuffer.onDidChangeGrammar @handleGrammarChange.bind(this) + @disposables.add @tokenizedBuffer.onDidTokenize @handleTokenization.bind(this) @disposables.add @displayLayer.onDidChangeSync (e) => @mergeIntersectingSelections() @emitter.emit 'did-change', e @@ -207,13 +252,27 @@ class TextEditor extends Model @tabTypeSubscription = @config.observe 'editor.tabType', scope: @getRootScopeDescriptor(), => @softTabs = @shouldUseSoftTabs(defaultValue: @softTabs) + resetDisplayLayer: -> + @displayLayer.reset({ + invisibles: @getInvisibles(), + softWrapColumn: @getSoftWrapColumn(), + showIndentGuides: @config.get('editor.showIndentGuide', scope: @getRootScopeDescriptor()), + tabLength: @getTabLength(), + ratioForCharacter: @ratioForCharacter.bind(this), + isWrapBoundary: isWrapBoundary, + foldCharacter: ZERO_WIDTH_NBSP + }) + destroyed: -> @disposables.dispose() + @displayLayer.destroy() + @scopedConfigSubscriptions.dispose() + @disposables.dispose() + @tokenizedBuffer.destroy() @tabTypeSubscription.dispose() selection.destroy() for selection in @selections.slice() @selectionsMarkerLayer.destroy() @buffer.release() - @displayBuffer.destroy() @languageMode.destroy() @gutterContainer.destroy() @emitter.emit 'did-destroy' @@ -297,7 +356,7 @@ class TextEditor extends Model # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidChangeSoftWrapped: (callback) -> - @displayBuffer.onDidChangeSoftWrapped(callback) + @emitter.on 'did-change-soft-wrapped', callback # Extended: Calls your `callback` when the buffer's encoding has changed. # @@ -451,7 +510,7 @@ class TextEditor extends Model # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. observeDecorations: (callback) -> - @displayBuffer.observeDecorations(callback) + @decorationManager.observeDecorations(callback) # Extended: Calls your `callback` when a {Decoration} is added to the editor. # @@ -460,7 +519,7 @@ class TextEditor extends Model # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidAddDecoration: (callback) -> - @displayBuffer.onDidAddDecoration(callback) + @decorationManager.onDidAddDecoration(callback) # Extended: Calls your `callback` when a {Decoration} is removed from the editor. # @@ -469,7 +528,7 @@ class TextEditor extends Model # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidRemoveDecoration: (callback) -> - @displayBuffer.onDidRemoveDecoration(callback) + @decorationManager.onDidRemoveDecoration(callback) # Extended: Calls your `callback` when the placeholder text is changed. # @@ -480,9 +539,6 @@ class TextEditor extends Model onDidChangePlaceholderText: (callback) -> @emitter.on 'did-change-placeholder-text', callback - onDidChangeCharacterWidths: (callback) -> - @displayBuffer.onDidChangeCharacterWidths(callback) - onDidChangeFirstVisibleScreenRow: (callback, fromView) -> @emitter.on 'did-change-first-visible-screen-row', callback @@ -497,17 +553,14 @@ class TextEditor extends Model @viewRegistry.getView(this).onDidChangeScrollLeft(callback) onDidRequestAutoscroll: (callback) -> - @displayBuffer.onDidRequestAutoscroll(callback) + @emitter.on 'did-request-autoscroll', callback # TODO Remove once the tabs package no longer uses .on subscriptions onDidChangeIcon: (callback) -> @emitter.on 'did-change-icon', callback - onDidUpdateMarkers: (callback) -> - @displayBuffer.onDidUpdateMarkers(callback) - onDidUpdateDecorations: (callback) -> - @displayBuffer.onDidUpdateDecorations(callback) + @decorationManager.onDidUpdateDecorations(callback) # Essential: Retrieves the current {TextBuffer}. getBuffer: -> @buffer @@ -517,31 +570,31 @@ class TextEditor extends Model # Create an {TextEditor} with its initial state based on this object copy: -> - displayBuffer = @displayBuffer.copy() - selectionsMarkerLayer = displayBuffer.getMarkerLayer(@buffer.getMarkerLayer(@selectionsMarkerLayer.id).copy().id) + selectionsMarkerLayer = @getMarkerLayer(@buffer.getMarkerLayer(@selectionsMarkerLayer.id).copy().id) softTabs = @getSoftTabs() newEditor = new TextEditor({ - @buffer, displayBuffer, selectionsMarkerLayer, @tabLength, softTabs, + @buffer, selectionsMarkerLayer, @tabLength, softTabs, suppressCursorCreation: true, @config, @notificationManager, @packageManager, @firstVisibleScreenRow, @firstVisibleScreenColumn, - @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate + @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate, + displayLayer: @buffer.copyDisplayLayer(@displayLayer.id) }) newEditor # Controls visibility based on the given {Boolean}. - setVisible: (visible) -> @displayBuffer.setVisible(visible) + setVisible: (visible) -> @tokenizedBuffer.setVisible(visible) setMini: (mini) -> if mini isnt @mini @mini = mini - @displayBuffer.setIgnoreInvisibles(@mini) + @setIgnoreInvisibles(@mini) @emitter.emit 'did-change-mini', @mini @mini isMini: -> @mini setUpdatedSynchronously: (updatedSynchronously) -> - @displayBuffer.setUpdatedSynchronously(updatedSynchronously) + @decorationManager.setUpdatedSynchronously(updatedSynchronously) onDidChangeMini: (callback) -> @emitter.on 'did-change-mini', callback @@ -594,12 +647,19 @@ class TextEditor extends Model # * `editorWidthInChars` A {Number} representing the width of the # {TextEditorElement} in characters. setEditorWidthInChars: (editorWidthInChars) -> - @displayBuffer.setEditorWidthInChars(editorWidthInChars) + if editorWidthInChars > 0 + previousWidthInChars = @editorWidthInChars + @editorWidthInChars = editorWidthInChars + if editorWidthInChars isnt previousWidthInChars and @isSoftWrapped() + @resetDisplayLayer() # Returns the editor width in characters. getEditorWidthInChars: -> - @displayBuffer.getEditorWidthInChars() - + width = @getWidth() + if width? and @defaultCharWidth > 0 + Math.max(0, Math.floor(width / @defaultCharWidth)) + else + @editorWidthInChars ### Section: File Details @@ -788,16 +848,21 @@ class TextEditor extends Model return if screenRow < 0 or screenRow > @getLastScreenRow() @displayLayer.getScreenLines(screenRow, screenRow + 1)[0] - bufferRowForScreenRow: (row) -> @displayLayer.translateScreenPosition(Point(row, 0)).row + bufferRowForScreenRow: (screenRow) -> + @displayLayer.translateScreenPosition(Point(screenRow, 0)).row - # {Delegates to: DisplayBuffer.bufferRowsForScreenRows} - bufferRowsForScreenRows: (startRow, endRow) -> @displayBuffer.bufferRowsForScreenRows(startRow, endRow) + bufferRowsForScreenRows: (startScreenRow, endScreenRow) -> + for screenRow in [startScreenRow..endScreenRow] + @bufferRowForScreenRow(screenRow) - screenRowForBufferRow: (row) -> @displayLayer.translateBufferPosition(Point(row, 0)).row + screenRowForBufferRow: (row) -> + if @largeFileMode + bufferRow + else + @displayLayer.translateScreenPosition(Point(screenRow, 0)).row getRightmostScreenPosition: -> @displayLayer.getRightmostScreenPosition() - # {Delegates to: DisplayBuffer.getMaxLineLength} getMaxScreenLineLength: -> @getRightmostScreenPosition().column getLongestScreenRow: -> @getRightmostScreenPosition().row @@ -1342,7 +1407,10 @@ class TextEditor extends Model # * `options` (optional) An options hash for {::clipScreenPosition}. # # Returns a {Point}. - screenPositionForBufferPosition: (bufferPosition, options) -> @displayLayer.translateBufferPosition(bufferPosition, options) + screenPositionForBufferPosition: (bufferPosition, options) -> + throw new Error("This TextEditor has been destroyed") if @isDestroyed() + + @displayLayer.translateBufferPosition(bufferPosition, options) # Essential: Convert a position in screen-coordinates to buffer-coordinates. # @@ -1352,21 +1420,30 @@ class TextEditor extends Model # * `options` (optional) An options hash for {::clipScreenPosition}. # # Returns a {Point}. - bufferPositionForScreenPosition: (screenPosition, options) -> @displayLayer.translateScreenPosition(screenPosition, options) + bufferPositionForScreenPosition: (screenPosition, options) -> + @displayLayer.translateScreenPosition(screenPosition, options) # Essential: Convert a range in buffer-coordinates to screen-coordinates. # # * `bufferRange` {Range} in buffer coordinates to translate into screen coordinates. # # Returns a {Range}. - screenRangeForBufferRange: (bufferRange) -> @displayLayer.translateBufferRange(bufferRange) + screenRangeForBufferRange: (bufferRange, options) -> + bufferRange = Range.fromObject(bufferRange) + start = @screenPositionForBufferPosition(bufferRange.start, options) + end = @screenPositionForBufferPosition(bufferRange.end, options) + new Range(start, end) # Essential: Convert a range in screen-coordinates to buffer-coordinates. # # * `screenRange` {Range} in screen coordinates to translate into buffer coordinates. # # Returns a {Range}. - bufferRangeForScreenRange: (screenRange) -> @displayLayer.translateScreenRange(screenRange) + bufferRangeForScreenRange: (screenRange) -> + screenRange = Range.fromObject(screenRange) + start = @bufferPositionForScreenPosition(screenRange.start) + end = @bufferPositionForScreenPosition(screenRange.end) + new Range(start, end) # Extended: Clip the given {Point} to a valid position in the buffer. # @@ -1510,7 +1587,7 @@ class TextEditor extends Model # # Returns a {Decoration} object decorateMarker: (marker, decorationParams) -> - @displayBuffer.decorateMarker(marker, decorationParams) + @decorationManager.decorateMarker(marker, decorationParams) # Essential: *Experimental:* Add a decoration to every marker in the given # marker layer. Can be used to decorate a large number of markers without @@ -1524,7 +1601,7 @@ class TextEditor extends Model # # Returns a {LayerDecoration}. decorateMarkerLayer: (markerLayer, decorationParams) -> - @displayBuffer.decorateMarkerLayer(markerLayer, decorationParams) + @decorationManager.decorateMarkerLayer(markerLayer, decorationParams) # Deprecated: Get all the decorations within a screen row range on the default # layer. @@ -1538,10 +1615,10 @@ class TextEditor extends Model # params objects attached to the marker. # Returns an empty object when no decorations are found decorationsForScreenRowRange: (startScreenRow, endScreenRow) -> - @displayBuffer.decorationsForScreenRowRange(startScreenRow, endScreenRow) + @decorationManager.decorationsForScreenRowRange(startScreenRow, endScreenRow) decorationsStateForScreenRowRange: (startScreenRow, endScreenRow) -> - @displayBuffer.decorationsStateForScreenRowRange(startScreenRow, endScreenRow) + @decorationManager.decorationsStateForScreenRowRange(startScreenRow, endScreenRow) # Extended: Get all decorations. # @@ -1550,7 +1627,7 @@ class TextEditor extends Model # # Returns an {Array} of {Decoration}s. getDecorations: (propertyFilter) -> - @displayBuffer.getDecorations(propertyFilter) + @decorationManager.getDecorations(propertyFilter) # Extended: Get all decorations of type 'line'. # @@ -1559,7 +1636,7 @@ class TextEditor extends Model # # Returns an {Array} of {Decoration}s. getLineDecorations: (propertyFilter) -> - @displayBuffer.getLineDecorations(propertyFilter) + @decorationManager.getLineDecorations(propertyFilter) # Extended: Get all decorations of type 'line-number'. # @@ -1568,7 +1645,7 @@ class TextEditor extends Model # # Returns an {Array} of {Decoration}s. getLineNumberDecorations: (propertyFilter) -> - @displayBuffer.getLineNumberDecorations(propertyFilter) + @decorationManager.getLineNumberDecorations(propertyFilter) # Extended: Get all decorations of type 'highlight'. # @@ -1577,7 +1654,7 @@ class TextEditor extends Model # # Returns an {Array} of {Decoration}s. getHighlightDecorations: (propertyFilter) -> - @displayBuffer.getHighlightDecorations(propertyFilter) + @decorationManager.getHighlightDecorations(propertyFilter) # Extended: Get all decorations of type 'overlay'. # @@ -1586,13 +1663,13 @@ class TextEditor extends Model # # Returns an {Array} of {Decoration}s. getOverlayDecorations: (propertyFilter) -> - @displayBuffer.getOverlayDecorations(propertyFilter) + @decorationManager.getOverlayDecorations(propertyFilter) decorationForId: (id) -> - @displayBuffer.decorationForId(id) + @decorationManager.decorationForId(id) decorationsForMarkerId: (id) -> - @displayBuffer.decorationsForMarkerId(id) + @decorationManager.decorationsForMarkerId(id) ### Section: Markers @@ -1630,8 +1707,8 @@ class TextEditor extends Model # start or start at the marker's end. This is the most fragile strategy. # # Returns a {DisplayMarker}. - markBufferRange: (args...) -> - @displayBuffer.markBufferRange(args...) + markBufferRange: (bufferRange, options) -> + @defaultMarkerLayer.markBufferRange(bufferRange, options) # Essential: Create a marker on the default marker layer with the given range # in screen coordinates. This marker will maintain its logical location as the @@ -1665,8 +1742,8 @@ class TextEditor extends Model # start or start at the marker's end. This is the most fragile strategy. # # Returns a {DisplayMarker}. - markScreenRange: (args...) -> - @displayBuffer.markScreenRange(args...) + markScreenRange: (screenRange, options) -> + @defaultMarkerLayer.markScreenRange(screenRange, options) # Essential: Mark the given position in buffer coordinates on the default # marker layer. @@ -1675,8 +1752,8 @@ class TextEditor extends Model # * `options` (optional) See {TextBuffer::markRange}. # # Returns a {DisplayMarker}. - markBufferPosition: (args...) -> - @displayBuffer.markBufferPosition(args...) + markBufferPosition: (bufferPosition, options) -> + @defaultMarkerLayer.markBufferPosition(bufferPosition, options) # Essential: Mark the given position in screen coordinates on the default # marker layer. @@ -1685,8 +1762,8 @@ class TextEditor extends Model # * `options` (optional) See {TextBuffer::markRange}. # # Returns a {DisplayMarker}. - markScreenPosition: (args...) -> - @displayBuffer.markScreenPosition(args...) + markScreenPosition: (screenPosition, options) -> + @defaultMarkerLayer.markScreenPosition(screenPosition, options) # Essential: Find all {DisplayMarker}s on the default marker layer that # match the given properties. @@ -1708,20 +1785,22 @@ class TextEditor extends Model # in range-compatible {Array} in buffer coordinates. # * `containsBufferPosition` Only include markers containing this {Point} # or {Array} of `[row, column]` in buffer coordinates. - findMarkers: (properties) -> - @displayBuffer.findMarkers(properties) + # + # Returns an {Array} of {DisplayMarker}s + findMarkers: (params) -> + @defaultMarkerLayer.findMarkers(params) # Extended: Get the {DisplayMarker} on the default layer for the given # marker id. # # * `id` {Number} id of the marker getMarker: (id) -> - @displayBuffer.getMarker(id) + @defaultMarkerLayer.getMarker(id) # Extended: Get all {DisplayMarker}s on the default marker layer. Consider # using {::findMarkers} getMarkers: -> - @displayBuffer.getMarkers() + @defaultMarkerLayer.getMarkers() # Extended: Get the number of markers in the default marker layer. # @@ -1742,7 +1821,7 @@ class TextEditor extends Model # # Returns a {DisplayMarkerLayer}. addMarkerLayer: (options) -> - @displayBuffer.addMarkerLayer(options) + @displayLayer.addMarkerLayer(options) # Public: *Experimental:* Get a {DisplayMarkerLayer} by id. # @@ -1753,7 +1832,7 @@ class TextEditor extends Model # Returns a {MarkerLayer} or `undefined` if no layer exists with the given # id. getMarkerLayer: (id) -> - @displayBuffer.getMarkerLayer(id) + @displayLayer.getMarkerLayer(id) # Public: *Experimental:* Get the default {DisplayMarkerLayer}. # @@ -1764,7 +1843,7 @@ class TextEditor extends Model # # Returns a {DisplayMarkerLayer}. getDefaultMarkerLayer: -> - @displayBuffer.getDefaultMarkerLayer() + @defaultMarkerLayer ### Section: Cursors @@ -2576,14 +2655,36 @@ class TextEditor extends Model # Essential: Get the on-screen length of tab characters. # # Returns a {Number}. - getTabLength: -> @displayBuffer.getTabLength() + getTabLength: -> + if @tabLength? + @tabLength + else + @config.get('editor.tabLength', scope: @getRootScopeDescriptor()) # Essential: Set the on-screen length of tab characters. Setting this to a # {Number} This will override the `editor.tabLength` setting. # # * `tabLength` {Number} length of a single tab. Setting to `null` will # fallback to using the `editor.tabLength` config setting - setTabLength: (tabLength) -> @displayBuffer.setTabLength(tabLength) + setTabLength: (tabLength) -> + return if tabLength is @tabLength + + @tabLength = tabLength + @tokenizedBuffer.setTabLength(@tabLength) + @resetDisplayLayer() + + setIgnoreInvisibles: (ignoreInvisibles) -> + return if ignoreInvisibles is @ignoreInvisibles + + @ignoreInvisibles = ignoreInvisibles + @resetDisplayLayer() + + getInvisibles: -> + scopeDescriptor = @getRootScopeDescriptor() + if @config.get('editor.showInvisibles', scope: scopeDescriptor) and not @ignoreInvisibles and @showInvisibles + @config.get('editor.invisibles', scope: scopeDescriptor) + else + {} # Extended: Determine if the buffer uses hard or soft tabs. # @@ -2594,7 +2695,7 @@ class TextEditor extends Model # whitespace. usesSoftTabs: -> for bufferRow in [0..@buffer.getLastRow()] - continue if @displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment() + continue if @tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment() line = @buffer.lineForRow(bufferRow) return true if line[0] is ' ' @@ -2637,14 +2738,27 @@ class TextEditor extends Model # Essential: Determine whether lines in this editor are soft-wrapped. # # Returns a {Boolean}. - isSoftWrapped: (softWrapped) -> @displayBuffer.isSoftWrapped() + isSoftWrapped: -> + if @largeFileMode + false + else + scopeDescriptor = @getRootScopeDescriptor() + @softWrapped ? @config.get('editor.softWrap', scope: scopeDescriptor) ? false # Essential: Enable or disable soft wrapping for this editor. # # * `softWrapped` A {Boolean} # # Returns a {Boolean}. - setSoftWrapped: (softWrapped) -> @displayBuffer.setSoftWrapped(softWrapped) + setSoftWrapped: (softWrapped) -> + if softWrapped isnt @softWrapped + @softWrapped = softWrapped + @resetDisplayLayer() + softWrapped = @isSoftWrapped() + @emitter.emit 'did-change-soft-wrapped', softWrapped + softWrapped + else + @isSoftWrapped() # Essential: Toggle soft wrapping for this editor # @@ -2652,7 +2766,15 @@ class TextEditor extends Model toggleSoftWrapped: -> @setSoftWrapped(not @isSoftWrapped()) # Essential: Gets the column at which column will soft wrap - getSoftWrapColumn: -> @displayBuffer.getSoftWrapColumn() + getSoftWrapColumn: -> + scopeDescriptor = @getRootScopeDescriptor() + if @isSoftWrapped() + if @config.get('editor.softWrapAtPreferredLineLength', scope: scopeDescriptor) + @config.get('editor.preferredLineLength', scope: scopeDescriptor) + else + @getEditorWidthInChars() + else + Infinity ### Section: Indentation @@ -2710,7 +2832,7 @@ class TextEditor extends Model # # Returns a {Number}. indentLevelForLine: (line) -> - @displayBuffer.indentLevelForLine(line) + @tokenizedBuffer.indentLevelForLine(line) # Extended: Indent rows intersecting selections based on the grammar's suggested # indent level. @@ -2738,7 +2860,7 @@ class TextEditor extends Model # Essential: Get the current {Grammar} of this editor. getGrammar: -> - @displayBuffer.getGrammar() + @tokenizedBuffer.grammar # Essential: Set the current {Grammar} of this editor. # @@ -2747,11 +2869,11 @@ class TextEditor extends Model # # * `grammar` {Grammar} setGrammar: (grammar) -> - @displayBuffer.setGrammar(grammar) + @tokenizedBuffer.setGrammar(grammar) # Reload the grammar based on the file name. reloadGrammar: -> - @displayBuffer.reloadGrammar() + @tokenizedBuffer.reloadGrammar() ### Section: Managing Syntax Scopes @@ -2761,7 +2883,7 @@ class TextEditor extends Model # e.g. `['.source.ruby']`, or `['.source.coffee']`. You can use this with # {Config::get} to get language specific config values. getRootScopeDescriptor: -> - @displayBuffer.getRootScopeDescriptor() + @tokenizedBuffer.rootScopeDescriptor # Essential: Get the syntactic scopeDescriptor for the given position in buffer # coordinates. Useful with {Config::get}. @@ -2774,7 +2896,7 @@ class TextEditor extends Model # # Returns a {ScopeDescriptor}. scopeDescriptorForBufferPosition: (bufferPosition) -> - @displayBuffer.scopeDescriptorForBufferPosition(bufferPosition) + @tokenizedBuffer.scopeDescriptorForPosition(bufferPosition) # Extended: Get the range in buffer coordinates of all tokens surrounding the # cursor that match the given scope selector. @@ -2786,7 +2908,7 @@ class TextEditor extends Model # # Returns a {Range}. bufferRangeForScopeAtCursor: (scopeSelector) -> - @displayBuffer.bufferRangeForScopeAtPosition(scopeSelector, @getCursorBufferPosition()) + @tokenizedBuffer.bufferRangeForScopeAtPosition(scopeSelector, @getCursorBufferPosition()) # Extended: Determine if the given row is entirely a comment isBufferRowCommented: (bufferRow) -> @@ -2802,8 +2924,8 @@ class TextEditor extends Model @notificationManager.addInfo(content, dismissable: true) - # {Delegates to: DisplayBuffer.tokenForBufferPosition} - tokenForBufferPosition: (bufferPosition) -> @displayBuffer.tokenForBufferPosition(bufferPosition) + tokenForBufferPosition: (bufferPosition) -> + @tokenizedBuffer.tokenForPosition(bufferPosition) ### Section: Clipboard Operations @@ -2935,7 +3057,7 @@ class TextEditor extends Model # # * `bufferRow` A {Number} unfoldBufferRow: (bufferRow) -> - @displayBuffer.unfoldBufferRow(bufferRow) + @displayLayer.destroyFoldsIntersectingBufferRange(Range(Point(bufferRow, 0), Point(bufferRow, Infinity))) # Extended: For each selection, fold the rows it intersects. foldSelectedLines: -> @@ -2965,7 +3087,7 @@ class TextEditor extends Model # # Returns a {Boolean}. isFoldableAtBufferRow: (bufferRow) -> - @displayBuffer.isFoldableAtBufferRow(bufferRow) + @tokenizedBuffer.isFoldableAtRow(bufferRow) # Extended: Determine whether the given row in screen coordinates is foldable. # @@ -2997,7 +3119,7 @@ class TextEditor extends Model # # Returns a {Boolean}. isFoldedAtBufferRow: (bufferRow) -> - @displayBuffer.isFoldedAtBufferRow(bufferRow) + @displayLayer.foldsIntersectingBufferRange(Range(Point(bufferRow, 0), Point(bufferRow, Infinity))).length > 0 # Extended: Determine whether the given row in screen coordinates is folded. # @@ -3005,10 +3127,16 @@ class TextEditor extends Model # # Returns a {Boolean}. isFoldedAtScreenRow: (screenRow) -> - @displayBuffer.isFoldedAtScreenRow(screenRow) + @isFoldedAtBufferRow(@bufferRowForScreenRow(screenRow)) + # Creates a new fold between two row numbers. + # + # startRow - The row {Number} to start folding at + # endRow - The row {Number} to end the fold + # + # Returns the new {Fold}. foldBufferRowRange: (startRow, endRow) -> - @displayBuffer.foldBufferRowRange(startRow, endRow) + @foldBufferRange(Range(Point(startRow, Infinity), Point(endRow, Infinity))) foldBufferRange: (range) -> @displayLayer.foldBufferRange(range) @@ -3066,7 +3194,7 @@ class TextEditor extends Model # * `options` (optional) {Object} # * `center` Center the editor around the position if possible. (default: false) scrollToBufferPosition: (bufferPosition, options) -> - @displayBuffer.scrollToBufferPosition(bufferPosition, options) + @scrollToScreenPosition(@screenPositionForBufferPosition(bufferPosition), options) # Essential: Scrolls the editor to the given screen position. # @@ -3075,7 +3203,7 @@ class TextEditor extends Model # * `options` (optional) {Object} # * `center` Center the editor around the position if possible. (default: false) scrollToScreenPosition: (screenPosition, options) -> - @displayBuffer.scrollToScreenPosition(screenPosition, options) + @scrollToScreenRange(new Range(screenPosition, screenPosition), options) scrollToTop: -> Grim.deprecate("This is now a view method. Call TextEditorElement::scrollToTop instead.") @@ -3087,7 +3215,9 @@ class TextEditor extends Model @viewRegistry.getView(this).scrollToBottom() - scrollToScreenRange: (screenRange, options) -> @displayBuffer.scrollToScreenRange(screenRange, options) + scrollToScreenRange: (screenRange, options = {}) -> + scrollEvent = {screenRange, options} + @emitter.emit "did-request-autoscroll", scrollEvent getHorizontalScrollbarHeight: -> Grim.deprecate("This is now a view method. Call TextEditorElement::getHorizontalScrollbarHeight instead.") @@ -3173,48 +3303,69 @@ class TextEditor extends Model getSelectionMarkerAttributes: -> {type: 'selection', invalidate: 'never'} - getVerticalScrollMargin: -> @displayBuffer.getVerticalScrollMargin() - setVerticalScrollMargin: (verticalScrollMargin) -> @displayBuffer.setVerticalScrollMargin(verticalScrollMargin) + getVerticalScrollMargin: -> + maxScrollMargin = Math.floor(((@getHeight() / @getLineHeightInPixels()) - 1) / 2) + Math.min(@verticalScrollMargin, maxScrollMargin) - getHorizontalScrollMargin: -> @displayBuffer.getHorizontalScrollMargin() - setHorizontalScrollMargin: (horizontalScrollMargin) -> @displayBuffer.setHorizontalScrollMargin(horizontalScrollMargin) + setVerticalScrollMargin: (@verticalScrollMargin) -> @verticalScrollMargin - getLineHeightInPixels: -> @displayBuffer.getLineHeightInPixels() - setLineHeightInPixels: (lineHeightInPixels) -> @displayBuffer.setLineHeightInPixels(lineHeightInPixels) + getHorizontalScrollMargin: -> Math.min(@horizontalScrollMargin, Math.floor(((@getWidth() / @getDefaultCharWidth()) - 1) / 2)) + setHorizontalScrollMargin: (@horizontalScrollMargin) -> @horizontalScrollMargin - getKoreanCharWidth: -> @displayBuffer.getKoreanCharWidth() + getLineHeightInPixels: -> @lineHeightInPixels + setLineHeightInPixels: (@lineHeightInPixels) -> @lineHeightInPixels - getHalfWidthCharWidth: -> @displayBuffer.getHalfWidthCharWidth() + getKoreanCharWidth: -> @koreanCharWidth + getHalfWidthCharWidth: -> @halfWidthCharWidth + getDoubleWidthCharWidth: -> @doubleWidthCharWidth + getDefaultCharWidth: -> @defaultCharWidth - getDoubleWidthCharWidth: -> @displayBuffer.getDoubleWidthCharWidth() + ratioForCharacter: (character) -> + if isKoreanCharacter(character) + @getKoreanCharWidth() / @getDefaultCharWidth() + else if isHalfWidthCharacter(character) + @getHalfWidthCharWidth() / @getDefaultCharWidth() + else if isDoubleWidthCharacter(character) + @getDoubleWidthCharWidth() / @getDefaultCharWidth() + else + 1 - getDefaultCharWidth: -> @displayBuffer.getDefaultCharWidth() setDefaultCharWidth: (defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth) -> - @displayBuffer.setDefaultCharWidth(defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth) + doubleWidthCharWidth ?= defaultCharWidth + halfWidthCharWidth ?= defaultCharWidth + koreanCharWidth ?= defaultCharWidth + if defaultCharWidth isnt @defaultCharWidth or doubleWidthCharWidth isnt @doubleWidthCharWidth and halfWidthCharWidth isnt @halfWidthCharWidth and koreanCharWidth isnt @koreanCharWidth + @defaultCharWidth = defaultCharWidth + @doubleWidthCharWidth = doubleWidthCharWidth + @halfWidthCharWidth = halfWidthCharWidth + @koreanCharWidth = koreanCharWidth + @resetDisplayLayer() if @isSoftWrapped() and @getEditorWidthInChars()? + defaultCharWidth setHeight: (height, reentrant=false) -> if reentrant - @displayBuffer.setHeight(height) + @height = height else Grim.deprecate("This is now a view method. Call TextEditorElement::setHeight instead.") @viewRegistry.getView(this).setHeight(height) getHeight: -> Grim.deprecate("This is now a view method. Call TextEditorElement::getHeight instead.") - @displayBuffer.getHeight() - - getClientHeight: -> @displayBuffer.getClientHeight() + @height setWidth: (width, reentrant=false) -> if reentrant - @displayBuffer.setWidth(width) + oldWidth = @width + @width = width + @resetDisplayLayer() if width isnt oldWidth and @isSoftWrapped() + @width else Grim.deprecate("This is now a view method. Call TextEditorElement::setWidth instead.") @viewRegistry.getView(this).setWidth(width) getWidth: -> Grim.deprecate("This is now a view method. Call TextEditorElement::getWidth instead.") - @displayBuffer.getWidth() + @width # Experimental: Scroll the editor such that the given screen row is at the # top of the visible area. @@ -3222,10 +3373,8 @@ class TextEditor extends Model unless fromView maxScreenRow = @getScreenLineCount() - 1 unless @config.get('editor.scrollPastEnd') and @scrollPastEnd - height = @displayBuffer.getHeight() - lineHeightInPixels = @displayBuffer.getLineHeightInPixels() - if height? and lineHeightInPixels? - maxScreenRow -= Math.floor(height / lineHeightInPixels) + if @height? and @lineHeightInPixels? + maxScreenRow -= Math.floor(@height / @lineHeightInPixels) screenRow = Math.max(Math.min(screenRow, maxScreenRow), 0) unless screenRow is @firstVisibleScreenRow @@ -3235,10 +3384,8 @@ class TextEditor extends Model getFirstVisibleScreenRow: -> @firstVisibleScreenRow getLastVisibleScreenRow: -> - height = @displayBuffer.getHeight() - lineHeightInPixels = @displayBuffer.getLineHeightInPixels() - if height? and lineHeightInPixels? - Math.min(@firstVisibleScreenRow + Math.floor(height / lineHeightInPixels), @getScreenLineCount() - 1) + if @height? and @lineHeightInPixels? + Math.min(@firstVisibleScreenRow + Math.floor(@height / @lineHeightInPixels), @getScreenLineCount() - 1) else null @@ -3333,8 +3480,6 @@ class TextEditor extends Model inspect: -> "" - logScreenLines: (start, end) -> @displayBuffer.logLines(start, end) - emitWillInsertTextEvent: (text) -> result = true cancel = -> result = false From ebd9e71b0bb501c2c404a552473f2bed185030df Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 5 Apr 2016 18:48:04 +0200 Subject: [PATCH 686/971] Make serialization format backwards/forwards compatible Also, introduce a serialization version so we can drop the compatibility fallbacks in the future. --- src/text-editor.coffee | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 40dd451d7..6f39c0699 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -59,6 +59,8 @@ ZERO_WIDTH_NBSP = '\ufeff' # soft wraps and folds to ensure your code interacts with them correctly. module.exports = class TextEditor extends Model + serializationVersion: 1 + buffer: null languageMode: null cursors: null @@ -80,6 +82,10 @@ class TextEditor extends Model get: -> @getElement() @deserialize: (state, atomEnvironment) -> + # TODO: Return null on version mismatch when 1.8.0 has been out for a while + if state.version isnt @prototype.serializationVersion and state.displayBuffer? + state.tokenizedBuffer = state.displayBuffer.tokenizedBuffer + try state.tokenizedBuffer = TokenizedBuffer.deserialize(state.tokenizedBuffer, atomEnvironment) catch error @@ -89,7 +95,7 @@ class TextEditor extends Model throw error state.buffer = state.tokenizedBuffer.buffer - state.displayLayer = state.buffer.getDisplayLayer(state.displayLayerId) + state.displayLayer = state.buffer.getDisplayLayer(state.displayLayerId) ? state.buffer.addDisplayLayer() state.selectionsMarkerLayer = state.displayLayer.getMarkerLayer(state.selectionsMarkerLayerId) state.config = atomEnvironment.config state.notificationManager = atomEnvironment.notifications @@ -188,7 +194,10 @@ class TextEditor extends Model @setGrammar(grammar) serialize: -> + tokenizedBufferState = @tokenizedBuffer.serialize() + deserializer: 'TextEditor' + version: @serializationVersion id: @id softTabs: @softTabs firstVisibleScreenRow: @getFirstVisibleScreenRow() @@ -196,7 +205,9 @@ class TextEditor extends Model selectionsMarkerLayerId: @selectionsMarkerLayer.id softWrapped: @isSoftWrapped() editorWidthInChars: @editorWidthInChars - tokenizedBuffer: @tokenizedBuffer.serialize() + # TODO: Remove this forward-compatible fallback once 1.8 reaches stable. + displayBuffer: {tokenizedBuffer: tokenizedBufferState} + tokenizedBuffer: tokenizedBufferState largeFileMode: @largeFileMode displayLayerId: @displayLayer.id registered: atom.textEditors.editors.has this From 117a3fb1b57465e3164c378219eefcca4b5c22a5 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 5 Apr 2016 12:11:11 -0600 Subject: [PATCH 687/971] Provide deprecated TextEditor.prototype.displayBuffer shim Even though it was a private field, we used it in some of our packages and it is widespread enough to warrant some effort to smooth the transition. Also, we added a couple methods that were formerly implemented on DisplayBuffer to TextEditor itself because they were used in packages. --- src/text-editor.coffee | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 6f39c0699..641e9f9da 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -81,6 +81,16 @@ class TextEditor extends Model Object.defineProperty @prototype, "element", get: -> @getElement() + Object.defineProperty(@prototype, 'displayBuffer', get: -> + Grim.deprecate(""" + `TextEditor.prototype.displayBuffer` has always been private, but now + it is gone. Reading the `displayBuffer` property now returns a reference + to the containing `TextEditor`, which now provides *some* of the API of + the defunct `DisplayBuffer` class. + """) + this + ) + @deserialize: (state, atomEnvironment) -> # TODO: Return null on version mismatch when 1.8.0 has been out for a while if state.version isnt @prototype.serializationVersion and state.displayBuffer? @@ -2886,6 +2896,10 @@ class TextEditor extends Model reloadGrammar: -> @tokenizedBuffer.reloadGrammar() + # Experimental: Get a notification when async tokenization is completed. + onDidTokenize: (callback) -> + @tokenizedBuffer.onDidTokenize(callback) + ### Section: Managing Syntax Scopes ### @@ -2919,7 +2933,10 @@ class TextEditor extends Model # # Returns a {Range}. bufferRangeForScopeAtCursor: (scopeSelector) -> - @tokenizedBuffer.bufferRangeForScopeAtPosition(scopeSelector, @getCursorBufferPosition()) + @bufferRangeForScopeAtPosition(scopeSelector, @getCursorBufferPosition()) + + bufferRangeForScopeAtPosition: (scopeSelector, position) -> + @tokenizedBuffer.bufferRangeForScopeAtPosition(scopeSelector, position) # Extended: Determine if the given row is entirely a comment isBufferRowCommented: (bufferRow) -> From a790ae21586826ff4adc95b6a89281300ead9fd0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 5 Apr 2016 12:12:14 -0600 Subject: [PATCH 688/971] Avoid deprecation warnings for methods that are now on TextEditor --- src/text-editor.coffee | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 641e9f9da..20d09b590 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -676,9 +676,8 @@ class TextEditor extends Model # Returns the editor width in characters. getEditorWidthInChars: -> - width = @getWidth() - if width? and @defaultCharWidth > 0 - Math.max(0, Math.floor(width / @defaultCharWidth)) + if @width? and @defaultCharWidth > 0 + Math.max(0, Math.floor(@width / @defaultCharWidth)) else @editorWidthInChars @@ -3337,7 +3336,7 @@ class TextEditor extends Model setVerticalScrollMargin: (@verticalScrollMargin) -> @verticalScrollMargin - getHorizontalScrollMargin: -> Math.min(@horizontalScrollMargin, Math.floor(((@getWidth() / @getDefaultCharWidth()) - 1) / 2)) + getHorizontalScrollMargin: -> Math.min(@horizontalScrollMargin, Math.floor(((@width / @getDefaultCharWidth()) - 1) / 2)) setHorizontalScrollMargin: (@horizontalScrollMargin) -> @horizontalScrollMargin getLineHeightInPixels: -> @lineHeightInPixels From b3ea0a6494be5da732bce61bc76946c43149ecbb Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 5 Apr 2016 16:40:06 -0600 Subject: [PATCH 689/971] :arrow_up: bookmarks --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4c03b3916..4e5fce85a 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "autoflow": "0.27.0", "autosave": "0.23.1", "background-tips": "0.26.0", - "bookmarks": "0.38.2", + "bookmarks": "0.38.3", "bracket-matcher": "0.82.0", "command-palette": "0.38.0", "deprecation-cop": "0.54.1", From 26ddee4a05a4539d89dd03a26bfdb7968454a19b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 5 Apr 2016 16:40:15 -0600 Subject: [PATCH 690/971] :arrow_up: autocomplete-plus --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4e5fce85a..6c796a711 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.0", "autocomplete-html": "0.7.2", - "autocomplete-plus": "2.29.1", + "autocomplete-plus": "2.29.2", "autocomplete-snippets": "1.10.0", "autoflow": "0.27.0", "autosave": "0.23.1", From b9222628718a806f1f3f24c4d2bd8565bb1cd096 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 5 Apr 2016 17:18:24 -0600 Subject: [PATCH 691/971] Avoid internal deprecation warning on getHeight --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 20d09b590..a7638afe9 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -3331,7 +3331,7 @@ class TextEditor extends Model {type: 'selection', invalidate: 'never'} getVerticalScrollMargin: -> - maxScrollMargin = Math.floor(((@getHeight() / @getLineHeightInPixels()) - 1) / 2) + maxScrollMargin = Math.floor(((@height / @getLineHeightInPixels()) - 1) / 2) Math.min(@verticalScrollMargin, maxScrollMargin) setVerticalScrollMargin: (@verticalScrollMargin) -> @verticalScrollMargin From af28fa075238cc58d721df5a5004ea807698eede Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 5 Apr 2016 18:47:11 -0600 Subject: [PATCH 692/971] When copying, create selections marker layer on the *new* display layer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, we were copying the selections marker layer into the current editor’s display layer. This would work fine until the spatial mapping drifted in the copied editor, and would then have counterintuitive results. --- src/text-editor.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index a7638afe9..7d4ad14ef 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -591,14 +591,15 @@ class TextEditor extends Model # Create an {TextEditor} with its initial state based on this object copy: -> - selectionsMarkerLayer = @getMarkerLayer(@buffer.getMarkerLayer(@selectionsMarkerLayer.id).copy().id) + displayLayer = @displayLayer.copy() + selectionsMarkerLayer = displayLayer.getMarkerLayer(@buffer.getMarkerLayer(@selectionsMarkerLayer.id).copy().id) softTabs = @getSoftTabs() newEditor = new TextEditor({ @buffer, selectionsMarkerLayer, @tabLength, softTabs, suppressCursorCreation: true, @config, @notificationManager, @packageManager, @firstVisibleScreenRow, @firstVisibleScreenColumn, @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate, - displayLayer: @buffer.copyDisplayLayer(@displayLayer.id) + displayLayer }) newEditor From 50c1bd34ca41efd3e7ec02f17d3237dcf6b8c6a6 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Tue, 5 Apr 2016 19:00:34 -0700 Subject: [PATCH 693/971] :arrow_up: tabs@0.92.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6c796a711..b087667bf 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.2.1", "styleguide": "0.45.2", "symbols-view": "0.112.0", - "tabs": "0.92.0", + "tabs": "0.92.1", "timecop": "0.33.1", "tree-view": "0.203.3", "update-package-dependencies": "0.10.0", From 66b9383c4e088327c4ee82c87bb1556fccee75f9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 6 Apr 2016 14:20:27 +0200 Subject: [PATCH 694/971] Use clipDirection instead of the clip option --- src/selection.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/selection.coffee b/src/selection.coffee index 7f77ad6c0..69aee901a 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -392,7 +392,7 @@ class Selection extends Model if options.select @setBufferRange(newBufferRange, reversed: wasReversed) else - @cursor.setBufferPosition(newBufferRange.end, clip: 'forward') if wasReversed + @cursor.setBufferPosition(newBufferRange.end, clipDirection: 'forward') if wasReversed if autoIndentFirstLine @editor.setIndentationForBufferRow(oldBufferRange.start.row, desiredIndentLevel) From 00f03fd614c32ee88c4ebbb946506fbfd22c8e27 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 6 Apr 2016 14:21:06 +0200 Subject: [PATCH 695/971] :memo: Document {clipDirection: 'closest'} --- src/text-editor.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 7d4ad14ef..f828b0e84 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1515,8 +1515,9 @@ class TextEditor extends Model # * `options` (optional) {Object} # * `clipDirection` {String} If `'backward'`, returns the first valid # position preceding an invalid position. If `'forward'`, returns the - # first valid position following a valid position. Defaults to - # `'backward'`. + # first valid position following an invalid position. If `'closest'`, + # returns the first valid position closest to an invalid position. + # Defaults to `'closest'`. # # Returns a {Point}. clipScreenPosition: (screenPosition, options) -> @displayLayer.clipScreenPosition(screenPosition, options) From 3ec1027d5ca2b878008ef0b4da827f9209b24bc6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 6 Apr 2016 14:38:53 +0200 Subject: [PATCH 696/971] Remove clipDirection parameter in a call to cursor.setBufferPosition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We aren’t clipping in that code path, so we might as well avoid to pass the parameter. --- src/selection.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/selection.coffee b/src/selection.coffee index 69aee901a..bd0963974 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -392,7 +392,7 @@ class Selection extends Model if options.select @setBufferRange(newBufferRange, reversed: wasReversed) else - @cursor.setBufferPosition(newBufferRange.end, clipDirection: 'forward') if wasReversed + @cursor.setBufferPosition(newBufferRange.end) if wasReversed if autoIndentFirstLine @editor.setIndentationForBufferRow(oldBufferRange.start.row, desiredIndentLevel) From 25b71b804df0d7f304067f7988e0a0232c21225c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 6 Apr 2016 14:44:26 +0200 Subject: [PATCH 697/971] Un-document passing clip options to setCursorBufferPosition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …the marker’s screen position will be clipped automatically when translating coordinates. --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index f828b0e84..a4f0ce096 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1889,7 +1889,7 @@ class TextEditor extends Model # If there are multiple cursors, they will be consolidated to a single cursor. # # * `position` A {Point} or {Array} of `[row, column]` - # * `options` (optional) An {Object} combining options for {::clipScreenPosition} with: + # * `options` (optional) An {Object} containing the following keys: # * `autoscroll` Determines whether the editor scrolls to the new cursor's # position. Defaults to true. setCursorBufferPosition: (position, options) -> From ffd3b1829d741c187eaeb921219405e8b74709bf Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 6 Apr 2016 15:42:53 +0200 Subject: [PATCH 698/971] Provide deprecation warnings for clipScreenPosition parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on a cursory search, there’s only one package using that API. Even though we could submit a PR, we’re introducing deprecations to ensure we're not missing any other package. --- src/text-editor.coffee | 43 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index a4f0ce096..79f2107ac 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1431,6 +1431,16 @@ class TextEditor extends Model screenPositionForBufferPosition: (bufferPosition, options) -> throw new Error("This TextEditor has been destroyed") if @isDestroyed() + if options?.clip? + Grim.deprecate("The `clip` parameter has been deprecated and will be removed soon. Please, use `clipDirection` instead.") + options.clipDirection ?= options.clip + if options?.wrapAtSoftNewlines? + Grim.deprecate("The `wrapAtSoftNewlines` parameter has been deprecated and will be removed soon. Please, use `clipDirection: 'forward'` instead.") + options.clipDirection ?= if options.wrapAtSoftNewlines then 'forward' else 'backward' + if options?.wrapBeyondNewlines? + Grim.deprecate("The `wrapBeyondNewlines` parameter has been deprecated and will be removed soon. Please, use `clipDirection: 'forward'` instead.") + options.clipDirection ?= if options.wrapBeyondNewlines then 'forward' else 'backward' + @displayLayer.translateBufferPosition(bufferPosition, options) # Essential: Convert a position in screen-coordinates to buffer-coordinates. @@ -1442,6 +1452,16 @@ class TextEditor extends Model # # Returns a {Point}. bufferPositionForScreenPosition: (screenPosition, options) -> + if options?.clip? + Grim.deprecate("The `clip` parameter has been deprecated and will be removed soon. Please, use `clipDirection` instead.") + options.clipDirection ?= options.clip + if options?.wrapAtSoftNewlines? + Grim.deprecate("The `wrapAtSoftNewlines` parameter has been deprecated and will be removed soon. Please, use `clipDirection: 'forward'` instead.") + options.clipDirection ?= if options.wrapAtSoftNewlines then 'forward' else 'backward' + if options?.wrapBeyondNewlines? + Grim.deprecate("The `wrapBeyondNewlines` parameter has been deprecated and will be removed soon. Please, use `clipDirection: 'forward'` instead.") + options.clipDirection ?= if options.wrapBeyondNewlines then 'forward' else 'backward' + @displayLayer.translateScreenPosition(screenPosition, options) # Essential: Convert a range in buffer-coordinates to screen-coordinates. @@ -1520,7 +1540,18 @@ class TextEditor extends Model # Defaults to `'closest'`. # # Returns a {Point}. - clipScreenPosition: (screenPosition, options) -> @displayLayer.clipScreenPosition(screenPosition, options) + clipScreenPosition: (screenPosition, options) -> + if options?.clip? + Grim.deprecate("The `clip` parameter has been deprecated and will be removed soon. Please, use `clipDirection` instead.") + options.clipDirection ?= options.clip + if options?.wrapAtSoftNewlines? + Grim.deprecate("The `wrapAtSoftNewlines` parameter has been deprecated and will be removed soon. Please, use `clipDirection: 'forward'` instead.") + options.clipDirection ?= if options.wrapAtSoftNewlines then 'forward' else 'backward' + if options?.wrapBeyondNewlines? + Grim.deprecate("The `wrapBeyondNewlines` parameter has been deprecated and will be removed soon. Please, use `clipDirection: 'forward'` instead.") + options.clipDirection ?= if options.wrapBeyondNewlines then 'forward' else 'backward' + + @displayLayer.clipScreenPosition(screenPosition, options) # Extended: Clip the start and end of the given range to valid positions on screen. # See {::clipScreenPosition} for more information. @@ -1927,6 +1958,16 @@ class TextEditor extends Model # * `autoscroll` Determines whether the editor scrolls to the new cursor's # position. Defaults to true. setCursorScreenPosition: (position, options) -> + if options?.clip? + Grim.deprecate("The `clip` parameter has been deprecated and will be removed soon. Please, use `clipDirection` instead.") + options.clipDirection ?= options.clip + if options?.wrapAtSoftNewlines? + Grim.deprecate("The `wrapAtSoftNewlines` parameter has been deprecated and will be removed soon. Please, use `clipDirection: 'forward'` instead.") + options.clipDirection ?= if options.wrapAtSoftNewlines then 'forward' else 'backward' + if options?.wrapBeyondNewlines? + Grim.deprecate("The `wrapBeyondNewlines` parameter has been deprecated and will be removed soon. Please, use `clipDirection: 'forward'` instead.") + options.clipDirection ?= if options.wrapBeyondNewlines then 'forward' else 'backward' + @moveCursors (cursor) -> cursor.setScreenPosition(position, options) # Essential: Add a cursor at the given position in buffer coordinates. From 4f28f9f3f84c08fe352a24d1a7b0584c699306c1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 6 Apr 2016 15:48:44 +0200 Subject: [PATCH 699/971] :fire: Remove defensive assertion We're not throwing this exception anywhere else, so we might as well delete it from here. /cc: @nathansobo --- src/text-editor.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 79f2107ac..1ebbf97bf 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1429,8 +1429,6 @@ class TextEditor extends Model # # Returns a {Point}. screenPositionForBufferPosition: (bufferPosition, options) -> - throw new Error("This TextEditor has been destroyed") if @isDestroyed() - if options?.clip? Grim.deprecate("The `clip` parameter has been deprecated and will be removed soon. Please, use `clipDirection` instead.") options.clipDirection ?= options.clip From c2396396a5fd46bd0e7ee4fb81b2fa26289457c5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 6 Apr 2016 19:20:05 +0200 Subject: [PATCH 700/971] :memo: Update documentation for mark*Position --- src/text-editor.coffee | 51 +++++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 1ebbf97bf..85f16152a 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1796,21 +1796,56 @@ class TextEditor extends Model markScreenRange: (screenRange, options) -> @defaultMarkerLayer.markScreenRange(screenRange, options) - # Essential: Mark the given position in buffer coordinates on the default - # marker layer. + # Essential: Create a marker on the default marker layer with the given buffer + # position and no tail. To group multiple markers together in their own + # private layer, see {::addMarkerLayer}. # - # * `position` A {Point} or {Array} of `[row, column]`. - # * `options` (optional) See {TextBuffer::markRange}. + # * `bufferPosition` A {Point} or point-compatible {Array} + # * `options` (optional) An {Object} with the following keys: + # * `invalidate` (optional) {String} Determines the rules by which changes + # to the buffer *invalidate* the marker. (default: 'overlap') It can be + # any of the following strategies, in order of fragility: + # * __never__: The marker is never marked as invalid. This is a good choice for + # markers representing selections in an editor. + # * __surround__: The marker is invalidated by changes that completely surround it. + # * __overlap__: The marker is invalidated by changes that surround the + # start or end of the marker. This is the default. + # * __inside__: The marker is invalidated by changes that extend into the + # inside of the marker. Changes that end at the marker's start or + # start at the marker's end do not invalidate the marker. + # * __touch__: The marker is invalidated by a change that touches the marked + # region in any way, including changes that end at the marker's + # start or start at the marker's end. This is the most fragile strategy. # # Returns a {DisplayMarker}. markBufferPosition: (bufferPosition, options) -> @defaultMarkerLayer.markBufferPosition(bufferPosition, options) - # Essential: Mark the given position in screen coordinates on the default - # marker layer. + # Essential: Create a marker on the default marker layer with the given screen + # position and no tail. To group multiple markers together in their own + # private layer, see {::addMarkerLayer}. # - # * `position` A {Point} or {Array} of `[row, column]`. - # * `options` (optional) See {TextBuffer::markRange}. + # * `screenPosition` A {Point} or point-compatible {Array} + # * `options` (optional) An {Object} with the following keys: + # * `invalidate` (optional) {String} Determines the rules by which changes + # to the buffer *invalidate* the marker. (default: 'overlap') It can be + # any of the following strategies, in order of fragility: + # * __never__: The marker is never marked as invalid. This is a good choice for + # markers representing selections in an editor. + # * __surround__: The marker is invalidated by changes that completely surround it. + # * __overlap__: The marker is invalidated by changes that surround the + # start or end of the marker. This is the default. + # * __inside__: The marker is invalidated by changes that extend into the + # inside of the marker. Changes that end at the marker's start or + # start at the marker's end do not invalidate the marker. + # * __touch__: The marker is invalidated by a change that touches the marked + # region in any way, including changes that end at the marker's + # start or start at the marker's end. This is the most fragile strategy. + # * `clipDirection` {String} If `'backward'`, returns the first valid + # position preceding an invalid position. If `'forward'`, returns the + # first valid position following an invalid position. If `'closest'`, + # returns the first valid position closest to an invalid position. + # Defaults to `'closest'`. # # Returns a {DisplayMarker}. markScreenPosition: (screenPosition, options) -> From e236b8c3be6eca2088fed58c4d54e921f11c6c97 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 6 Apr 2016 20:00:08 +0200 Subject: [PATCH 701/971] Avoid storing properties in selection markers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously we were storing a {type: ‘selection’} property, probably leftover from the “pre-marker-layer” world. We were also storing `Selection`’s `goalScreenRange` on the marker, which is now just an instance variable. --- src/selection.coffee | 22 +++++++++++++--------- src/text-editor.coffee | 11 ++++------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/selection.coffee b/src/selection.coffee index bd0963974..bd15d1543 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -174,7 +174,7 @@ class Selection extends Model # range. Defaults to `true` if this is the most recently added selection, # `false` otherwise. clear: (options) -> - @marker.setProperties(goalScreenRange: null) + @goalScreenRange = null @marker.clearTail() unless @retainSelection @autoscroll() if options?.autoscroll ? @isLastSelection() @finalize() @@ -682,7 +682,7 @@ class Selection extends Model # Public: Moves the selection down one row. addSelectionBelow: -> - range = (@getGoalScreenRange() ? @getScreenRange()).copy() + range = @getGoalScreenRange().copy() nextRow = range.end.row + 1 for row in [nextRow..@editor.getLastScreenRow()] @@ -695,14 +695,15 @@ class Selection extends Model else continue if clippedRange.isEmpty() - @editor.addSelectionForScreenRange(clippedRange, goalScreenRange: range) + selection = @editor.addSelectionForScreenRange(clippedRange) + selection.setGoalScreenRange(range) break return # Public: Moves the selection up one row. addSelectionAbove: -> - range = (@getGoalScreenRange() ? @getScreenRange()).copy() + range = @getGoalScreenRange().copy() previousRow = range.end.row - 1 for row in [previousRow..0] @@ -715,7 +716,8 @@ class Selection extends Model else continue if clippedRange.isEmpty() - @editor.addSelectionForScreenRange(clippedRange, goalScreenRange: range) + selection = @editor.addSelectionForScreenRange(clippedRange) + selection.setGoalScreenRange(range) break return @@ -754,6 +756,12 @@ class Selection extends Model Section: Private Utilities ### + setGoalScreenRange: (range) -> + @goalScreenRange = Range.fromObject(range) + + getGoalScreenRange: -> + @goalScreenRange ? @getScreenRange() + markerDidChange: (e) -> {oldHeadBufferPosition, oldTailBufferPosition, newHeadBufferPosition} = e {oldHeadScreenPosition, oldTailScreenPosition, newHeadScreenPosition} = e @@ -824,7 +832,3 @@ class Selection extends Model # Returns a {Point} representing the new tail position. plantTail: -> @marker.plantTail() - - getGoalScreenRange: -> - if goalScreenRange = @marker.getProperties().goalScreenRange - Range.fromObject(goalScreenRange) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 85f16152a..cd960f2e8 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2009,7 +2009,7 @@ class TextEditor extends Model # # Returns a {Cursor}. addCursorAtBufferPosition: (bufferPosition, options) -> - @selectionsMarkerLayer.markBufferPosition(bufferPosition, @getSelectionMarkerAttributes()) + @selectionsMarkerLayer.markBufferPosition(bufferPosition, {invalidate: 'never'}) @getLastSelection().cursor.autoscroll() unless options?.autoscroll is false @getLastSelection().cursor @@ -2019,7 +2019,7 @@ class TextEditor extends Model # # Returns a {Cursor}. addCursorAtScreenPosition: (screenPosition, options) -> - @selectionsMarkerLayer.markScreenPosition(screenPosition, @getSelectionMarkerAttributes()) + @selectionsMarkerLayer.markScreenPosition(screenPosition, {invalidate: 'never'}) @getLastSelection().cursor.autoscroll() unless options?.autoscroll is false @getLastSelection().cursor @@ -2305,7 +2305,7 @@ class TextEditor extends Model # # Returns the added {Selection}. addSelectionForBufferRange: (bufferRange, options={}) -> - @selectionsMarkerLayer.markBufferRange(bufferRange, _.defaults(@getSelectionMarkerAttributes(), options)) + @selectionsMarkerLayer.markBufferRange(bufferRange, _.defaults({invalidate: 'never'}, options)) @getLastSelection().autoscroll() unless options.autoscroll is false @getLastSelection() @@ -2318,7 +2318,7 @@ class TextEditor extends Model # # Returns the added {Selection}. addSelectionForScreenRange: (screenRange, options={}) -> - @selectionsMarkerLayer.markScreenRange(screenRange, _.defaults(@getSelectionMarkerAttributes(), options)) + @selectionsMarkerLayer.markScreenRange(screenRange, _.defaults({invalidate: 'never'}, options)) @getLastSelection().autoscroll() unless options.autoscroll is false @getLastSelection() @@ -3403,9 +3403,6 @@ class TextEditor extends Model Grim.deprecate("This method is deprecated on the model layer. Use `TextEditorElement::pixelPositionForScreenPosition` instead") @viewRegistry.getView(this).pixelPositionForScreenPosition(screenPosition) - getSelectionMarkerAttributes: -> - {type: 'selection', invalidate: 'never'} - getVerticalScrollMargin: -> maxScrollMargin = Math.floor(((@height / @getLineHeightInPixels()) - 1) / 2) Math.min(@verticalScrollMargin, maxScrollMargin) From 917f20d1c4c5f436522e45e8087975f95b4aac29 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 6 Apr 2016 16:05:43 -0600 Subject: [PATCH 702/971] :arrow_up: tree-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b087667bf..d48b1f41e 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.112.0", "tabs": "0.92.1", "timecop": "0.33.1", - "tree-view": "0.203.3", + "tree-view": "0.203.4", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From a084882cb130fa6f7ffe51a88ebd140292bf4f03 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 6 Apr 2016 19:23:26 -0400 Subject: [PATCH 703/971] :memo: Minor cleanup for the new .tar.gz archive [ci skip] --- docs/build-instructions/linux.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/build-instructions/linux.md b/docs/build-instructions/linux.md index 99b9b5afc..126604c49 100644 --- a/docs/build-instructions/linux.md +++ b/docs/build-instructions/linux.md @@ -74,19 +74,19 @@ If you have problems with permissions don't forget to prefix with `sudo` To use the newly installed Atom, quit and restart all running Atom instances. -5. *Optionally*, you may generate distributable packages of Atom at `out`. Currently, `.deb` and `.rpm` package types are supported, as well as a `tar gz` archive. To create a `.deb` package run: +5. *Optionally*, you may generate distributable packages of Atom at `out`. Currently, `.deb` and `.rpm` package types are supported, as well as a `.tar.gz` archive. To create a `.deb` package run: ```sh script/grunt mkdeb ``` - To create an `.rpm` package run + To create a `.rpm` package run ```sh script/grunt mkrpm ``` - To create a `.tar.gz` package run + To create a `.tar.gz` archive run ```sh script/grunt mktar From ffe33cb3791e1b541de5a21892ee05affbaa5b34 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 6 Apr 2016 18:31:13 -0700 Subject: [PATCH 704/971] :arrow_up: language-perl@0.34.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d48b1f41e..c0191b551 100644 --- a/package.json +++ b/package.json @@ -134,7 +134,7 @@ "language-make": "0.21.0", "language-mustache": "0.13.0", "language-objective-c": "0.15.1", - "language-perl": "0.33.0", + "language-perl": "0.34.0", "language-php": "0.37.0", "language-property-list": "0.8.0", "language-python": "0.43.1", From b5f0f846a9cd2be8e323a104c84542563cc79aa4 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Thu, 7 Apr 2016 10:06:13 +0200 Subject: [PATCH 705/971] :arrow_up: exception-reporting@0.38.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c0191b551..19739ea2f 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "deprecation-cop": "0.54.1", "dev-live-reload": "0.47.0", "encoding-selector": "0.21.0", - "exception-reporting": "0.37.0", + "exception-reporting": "0.38.0", "find-and-replace": "0.197.4", "fuzzy-finder": "1.0.3", "git-diff": "1.0.1", From 3bff9515f79a35588e216b2fbce926e3e7fc2e10 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 7 Apr 2016 10:30:01 +0200 Subject: [PATCH 706/971] :arrow_up: find-and-replace --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 19739ea2f..f2d518d8d 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "dev-live-reload": "0.47.0", "encoding-selector": "0.21.0", "exception-reporting": "0.38.0", - "find-and-replace": "0.197.4", + "find-and-replace": "0.197.5", "fuzzy-finder": "1.0.3", "git-diff": "1.0.1", "go-to-line": "0.30.0", From c08a5c3f54e1557e7717882f17f76a0dd9f64cd7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 7 Apr 2016 10:57:51 +0200 Subject: [PATCH 707/971] :arrow_up: spell-check --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f2d518d8d..a1f33742b 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "package-generator": "1.0.0", "settings-view": "0.235.1", "snippets": "1.0.2", - "spell-check": "0.67.0", + "spell-check": "0.67.1", "status-bar": "1.2.1", "styleguide": "0.45.2", "symbols-view": "0.112.0", From b0c485c4ea4a3355e0ba866f0f1f447a5beb48c8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 7 Apr 2016 11:48:18 +0200 Subject: [PATCH 708/971] :fire: Remove TokenizedLine specs --- spec/tokenized-line-spec.coffee | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 spec/tokenized-line-spec.coffee diff --git a/spec/tokenized-line-spec.coffee b/spec/tokenized-line-spec.coffee deleted file mode 100644 index f1dce7b9e..000000000 --- a/spec/tokenized-line-spec.coffee +++ /dev/null @@ -1,19 +0,0 @@ -describe "TokenizedLine", -> - editor = null - - beforeEach -> - waitsForPromise -> atom.packages.activatePackage('language-coffee-script') - - describe "::isOnlyWhitespace()", -> - beforeEach -> - waitsForPromise -> - atom.workspace.open('coffee.coffee').then (o) -> editor = o - - it "returns true when the line is only whitespace", -> - expect(editor.tokenizedLineForScreenRow(3).isOnlyWhitespace()).toBe true - expect(editor.tokenizedLineForScreenRow(7).isOnlyWhitespace()).toBe true - expect(editor.tokenizedLineForScreenRow(23).isOnlyWhitespace()).toBe true - - it "returns false when the line is not only whitespace", -> - expect(editor.tokenizedLineForScreenRow(0).isOnlyWhitespace()).toBe false - expect(editor.tokenizedLineForScreenRow(2).isOnlyWhitespace()).toBe false From a532000af45cef34f9bed752f25a29fc4e6ace70 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 7 Apr 2016 12:28:41 +0200 Subject: [PATCH 709/971] Handle only buffer coordinates in TokenIterator --- spec/tokenized-buffer-spec.coffee | 24 ++++++++++ src/lines-tile-component.coffee | 1 - src/lines-yardstick.coffee | 1 - src/token-iterator.coffee | 77 +++++++++++++------------------ src/token.coffee | 2 +- 5 files changed, 56 insertions(+), 49 deletions(-) diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index cb9378c4e..ee418a386 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -849,3 +849,27 @@ describe "TokenizedBuffer", -> iterator.seek(Point(0, 8)) expect(iterator.getPosition().column).toBe(7) + + it "correctly terminates scopes at the beginning of the line (regression)", -> + grammar = atom.grammars.createGrammar('test', { + 'scopeName': 'text.broken' + 'name': 'Broken grammar' + 'patterns': [ + {'begin': 'start', 'end': '(?=end)', 'name': 'blue.broken'}, + {'match': '.', 'name': 'yellow.broken'} + ] + }) + + buffer = new TextBuffer(text: 'start x\nend x\nx') + tokenizedBuffer = new TokenizedBuffer({ + buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert + }) + tokenizedBuffer.setGrammar(grammar) + fullyTokenize(tokenizedBuffer) + + iterator = tokenizedBuffer.buildIterator() + iterator.seek(Point(1, 0)) + + expect(iterator.getPosition()).toEqual([1, 0]) + expect(iterator.getCloseTags()).toEqual ['blue.broken'] + expect(iterator.getOpenTags()).toEqual ['yellow.broken'] diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index 6e9a15e6b..d006daa86 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -1,7 +1,6 @@ _ = require 'underscore-plus' HighlightsComponent = require './highlights-component' -TokenIterator = require './token-iterator' AcceptFilter = {acceptNode: -> NodeFilter.FILTER_ACCEPT} TokenTextEscapeRegex = /[&"'<>]/g MaxTokenLength = 20000 diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee index f53b6348e..17af35f74 100644 --- a/src/lines-yardstick.coffee +++ b/src/lines-yardstick.coffee @@ -1,4 +1,3 @@ -TokenIterator = require './token-iterator' {Point} = require 'text-buffer' {isPairedCharacter} = require './text-utils' diff --git a/src/token-iterator.coffee b/src/token-iterator.coffee index 259bfd346..f9af1e4ca 100644 --- a/src/token-iterator.coffee +++ b/src/token-iterator.coffee @@ -1,72 +1,57 @@ module.exports = class TokenIterator - constructor: ({@grammarRegistry}, line, enableScopes) -> - @reset(line, enableScopes) if line? + constructor: ({@grammarRegistry}, line) -> + @reset(line) if line? - reset: (@line, @enableScopes=true) -> + reset: (@line) -> @index = null - @bufferStart = @line.startBufferColumn - @bufferEnd = @bufferStart - @screenStart = 0 - @screenEnd = 0 - @resetScopes() if @enableScopes + @startColumn = 0 + @endColumn = 0 + @scopes = @line.openScopes.map (id) => @grammarRegistry.scopeForId(id) + @scopeStarts = @scopes.slice() + @scopeEnds = [] this next: -> {tags} = @line if @index? + @startColumn = @endColumn + @scopeEnds.length = 0 + @scopeStarts.length = 0 @index++ - @bufferStart = @bufferEnd - @screenStart = @screenEnd - @clearScopeStartsAndEnds() if @enableScopes else @index = 0 while @index < tags.length tag = tags[@index] if tag < 0 - @handleScopeForTag(tag) if @enableScopes + scope = @grammarRegistry.scopeForId(tag) + if tag % 2 is 0 + if @scopeStarts[@scopeStarts.length - 1] is scope + @scopeStarts.pop() + else + @scopeEnds.push(scope) + @scopes.pop() + else + @scopeStarts.push(scope) + @scopes.push(scope) @index++ else - @screenEnd = @screenStart + tag - @bufferEnd = @bufferStart + tag - - @text = @line.text.substring(@screenStart, @screenEnd) + @endColumn += tag + @text = @line.text.substring(@startColumn, @endColumn) return true false - resetScopes: -> - @scopes = @line.openScopes.map (id) => @grammarRegistry.scopeForId(id) - @scopeStarts = @scopes.slice() - @scopeEnds = [] - - clearScopeStartsAndEnds: -> - @scopeEnds.length = 0 - @scopeStarts.length = 0 - - handleScopeForTag: (tag) -> - scope = @grammarRegistry.scopeForId(tag) - if tag % 2 is 0 - if @scopeStarts[@scopeStarts.length - 1] is scope - @scopeStarts.pop() - else - @scopeEnds.push(scope) - @scopes.pop() - else - @scopeStarts.push(scope) - @scopes.push(scope) - - getBufferStart: -> @bufferStart - getBufferEnd: -> @bufferEnd - - getScreenStart: -> @screenStart - getScreenEnd: -> @screenEnd - - getScopeStarts: -> @scopeStarts - getScopeEnds: -> @scopeEnds - getScopes: -> @scopes + getScopeStarts: -> @scopeStarts + + getScopeEnds: -> @scopeEnds + getText: -> @text + + getBufferStart: -> @startColumn + + getBufferEnd: -> @endColumn diff --git a/src/token.coffee b/src/token.coffee index 5e1f1e811..d531ba04a 100644 --- a/src/token.coffee +++ b/src/token.coffee @@ -14,7 +14,7 @@ class Token isEqual: (other) -> # TODO: scopes is deprecated. This is here for the sake of lang package tests - @value is other.value and _.isEqual(@scopes, other.scopes) and !!@isAtomic is !!other.isAtomic + @value is other.value and _.isEqual(@scopes, other.scopes) isBracket: -> /^meta\.brace\b/.test(_.last(@scopes)) From c23ef9a168c7f05655f8c3c944c117013c83d898 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 7 Apr 2016 13:54:13 +0200 Subject: [PATCH 710/971] Stop using tokenizedLineForScreenRow in random-editor-spec.coffee --- spec/random-editor-spec.coffee | 7 ++++--- src/text-editor.coffee | 6 ++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/spec/random-editor-spec.coffee b/spec/random-editor-spec.coffee index 06c50e80f..cada2fe22 100644 --- a/spec/random-editor-spec.coffee +++ b/spec/random-editor-spec.coffee @@ -33,8 +33,8 @@ describe "TextEditor", -> logLines() throw new Error("Invalid buffer row #{actualBufferRow} for screen row #{screenRow}", ) - actualScreenLine = editor.tokenizedLineForScreenRow(screenRow) - unless actualScreenLine.text is referenceScreenLine.text + actualScreenLine = editor.lineTextForScreenRow(screenRow) + unless actualScreenLine is referenceScreenLine logLines() throw new Error("Invalid line text at screen row #{screenRow}") @@ -84,7 +84,8 @@ describe "TextEditor", -> referenceEditor.setEditorWidthInChars(80) referenceEditor.setText(editor.getText()) referenceEditor.setSoftWrapped(editor.isSoftWrapped()) - screenLines = referenceEditor.tokenizedLinesForScreenRows(0, referenceEditor.getLastScreenRow()) + + screenLines = [0..referenceEditor.getLastScreenRow()].map (row) => referenceEditor.lineTextForScreenRow(row) bufferRows = referenceEditor.bufferRowsForScreenRows(0, referenceEditor.getLastScreenRow()) {screenLines, bufferRows} diff --git a/src/text-editor.coffee b/src/text-editor.coffee index cd960f2e8..142405487 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -861,6 +861,12 @@ class TextEditor extends Model lineTextForScreenRow: (screenRow) -> @screenLineForScreenRow(screenRow)?.lineText + logScreenLines: (start=0, end=@getLastScreenRow()) -> + for row in [start..end] + line = @lineTextForScreenRow(row) + console.log row, @bufferRowForScreenRow(row), line, line.length + return + tokensForScreenRow: (screenRow) -> for tagCode in @screenLineForScreenRow(screenRow).tagCodes when @displayLayer.isOpenTagCode(tagCode) @displayLayer.tagForCode(tagCode) From c3d03f4d5721a536c4b97ba58e56c1a04cd58798 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 7 Apr 2016 14:50:57 +0200 Subject: [PATCH 711/971] :arrow_up: find-and-replace --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a1f33742b..20847be99 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "dev-live-reload": "0.47.0", "encoding-selector": "0.21.0", "exception-reporting": "0.38.0", - "find-and-replace": "0.197.5", + "find-and-replace": "0.197.6", "fuzzy-finder": "1.0.3", "git-diff": "1.0.1", "go-to-line": "0.30.0", From db8e62315c13b97ff6cebb419042115502229304 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 7 Apr 2016 11:19:27 -0400 Subject: [PATCH 712/971] Defer the callback to the next tick. This gives GitRepository the chance to clear its path cache before the callback is invoked. Otherwise reads from the cached status state within the callback would be wrong. --- src/git-repository.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 30d99791d..a04124b78 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -166,7 +166,12 @@ class GitRepository # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidChangeStatuses: (callback) -> - @async.onDidChangeStatuses callback + @async.onDidChangeStatuses -> + # Defer the callback to the next tick so that we've reset + # `@statusesByPath` by the time it's called. Otherwise reads from within + # the callback could be inconsistent. + # See https://github.com/atom/atom/issues/11396 + process.nextTick callback ### Section: Repository Details From 130c40075815e63e15da826d6419e123cc45f61f Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 7 Apr 2016 11:25:00 -0400 Subject: [PATCH 713/971] Remove unnecessary fat arrow. Looks like this was introduced in https://github.com/atom/atom/pull/11369. :see_no_evil: --- src/text-editor-component.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 12f481be5..488b74d09 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -287,7 +287,7 @@ class TextEditorComponent # with the menu open. @openedAccentedCharacterMenu = false - @domNode.addEventListener 'keyup', => + @domNode.addEventListener 'keyup', -> lastKeydownBeforeKeypress = null lastKeydown = null From 9c601aad6f697780b812bc4325f53ede195a67ad Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 7 Apr 2016 14:49:40 -0600 Subject: [PATCH 714/971] :arrow-up: status-bar --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 20847be99..13e06062a 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.235.1", "snippets": "1.0.2", "spell-check": "0.67.1", - "status-bar": "1.2.1", + "status-bar": "1.2.2", "styleguide": "0.45.2", "symbols-view": "0.112.0", "tabs": "0.92.1", From 421dbec1edd6751ae355112a147471662e139cbf Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Thu, 7 Apr 2016 14:30:33 -0700 Subject: [PATCH 715/971] Git Shell now works fine, avoid paths with spaces --- docs/build-instructions/windows.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/build-instructions/windows.md b/docs/build-instructions/windows.md index 0a999ee3b..13e3be590 100644 --- a/docs/build-instructions/windows.md +++ b/docs/build-instructions/windows.md @@ -28,15 +28,13 @@ Whichever version you use, ensure that: You can run these commands using Command Prompt, PowerShell or Git Shell via [GitHub Desktop](https://desktop.github.com/). These instructions will assume the use of Bash from Git Shell - if you are using Command Prompt use a backslash instead: i.e. `script\build`. -**VS2015 + Git Shell users** should note that the default path supplied with Git Shell includes reference to an older version of msbuild that will fail. It is recommended you use a PowerShell window that has git in the path at this time. - ```bash cd C:\ git clone https://github.com/atom/atom/ cd atom script/build ``` -This will create the Atom application in the `Program Files` folder. +This will create the Atom application in the `out\Atom` folder as well as copy it to a folder named `Atom` within `Program Files`. ### `script/build` Options * `--install-dir` - Creates the final built application in this directory. Example (trailing slash is optional): @@ -69,6 +67,9 @@ If none of this works, do install Github Desktop and use its Git Shell as it mak * `msbuild.exe failed with exit code: 1` * Ensure you have Visual C++ support installed. Go into Add/Remove Programs, select Visual Studio and press Modify and then check the Visual C++ box. +* `script/build` stops with no error or warning shortly after displaying the versions of node, npm and Python + * Make sure that the path where you have checked out Atom does not include a space. e.g. use `c:\atom` and not `c:\my stuff\atom` + * `script/build` outputs only the Node.js and Python versions before returning * Try moving the repository to `C:\atom`. Most likely, the path is too long. See [issue #2200](https://github.com/atom/atom/issues/2200). @@ -88,7 +89,7 @@ If none of this works, do install Github Desktop and use its Git Shell as it mak * `error MSB8020: The build tools for Visual Studio 201? (Platform Toolset = 'v1?0') cannot be found.` * If you're building Atom with Visual Studio 2013 try setting the `GYP_MSVS_VERSION` environment variable to 2013 and then `script/clean` followed by `script/build` (re-open your command prompt or Powershell window if you set it using the GUI) - * + * Other `node-gyp` errors on first build attempt, even though the right Node.js and Python versions are installed. * Do try the build command one more time, as experience shows it often works on second try in many of these cases. From 5d8cd7bcc8237e642f212cf12685f531152b74e0 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Thu, 7 Apr 2016 17:38:16 -0400 Subject: [PATCH 716/971] :arrow_up: language-make@0.21.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 13e06062a..79eeda981 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,7 @@ "language-javascript": "0.110.0", "language-json": "0.18.0", "language-less": "0.29.2", - "language-make": "0.21.0", + "language-make": "0.21.1", "language-mustache": "0.13.0", "language-objective-c": "0.15.1", "language-perl": "0.34.0", From 822e0c95108aadb91631570e386d8cf96d7cfdd0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 7 Apr 2016 15:51:38 -0600 Subject: [PATCH 717/971] :arrow_up: fuzzy-finder --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 79eeda981..689b6f9df 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "encoding-selector": "0.21.0", "exception-reporting": "0.38.0", "find-and-replace": "0.197.6", - "fuzzy-finder": "1.0.3", + "fuzzy-finder": "1.0.4", "git-diff": "1.0.1", "go-to-line": "0.30.0", "grammar-selector": "0.48.1", From 6dd37761fedff7cbb8ed666e7a890d5b4eb19b1d Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Thu, 7 Apr 2016 21:35:35 -0400 Subject: [PATCH 718/971] :memo: Fix dead atom.io/docs links [ci skip] --- CONTRIBUTING.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b4519bcf1..0aa249c18 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,7 +50,7 @@ To get a sense for the packages that are bundled with Atom, you can go to Settin Here's a list of the big ones: -* [atom/atom](https://github.com/atom/atom) - Atom Core! The core editor component is responsible for basic text editing (e.g. cursors, selections, scrolling), text indentation, wrapping, and folding, text rendering, editor rendering, file system operations (e.g. saving), and installation and auto-updating. You should also use this repository for feedback related to the [core API](https://atom.io/docs/api/latest/Notification) and for large, overarching design proposals. +* [atom/atom](https://github.com/atom/atom) - Atom Core! The core editor component is responsible for basic text editing (e.g. cursors, selections, scrolling), text indentation, wrapping, and folding, text rendering, editor rendering, file system operations (e.g. saving), and installation and auto-updating. You should also use this repository for feedback related to the [core API](https://atom.io/docs/api/latest) and for large, overarching design proposals. * [tree-view](https://github.com/atom/tree-view) - file and directory listing on the left of the UI. * [fuzzy-finder](https://github.com/atom/fuzzy-finder) - the quick file opener. * [find-and-replace](https://github.com/atom/find-and-replace) - all search and replace functionality. @@ -82,7 +82,7 @@ Before creating bug reports, please check [this list](#before-submitting-a-bug-r #### Before Submitting A Bug Report -* **Check the [debugging guide](https://atom.io/docs/latest/hacking-atom-debugging).** You might be able to find the cause of the problem and fix things yourself. Most importantly, check if you can reproduce the problem [in the latest version of Atom](https://atom.io/docs/latest/hacking-atom-debugging#update-to-the-latest-version), if the problem happens when you run Atom in [safe mode](https://atom.io/docs/latest/hacking-atom-debugging#check-if-the-problem-shows-up-in-safe-mode), and if you can get the desired behavior by changing [Atom's or packages' config settings](https://atom.io/docs/latest/hacking-atom-debugging#check-atom-and-package-settings). +* **Check the [debugging guide](http://flight-manual.atom.io/hacking-atom/sections/debugging/).** You might be able to find the cause of the problem and fix things yourself. Most importantly, check if you can reproduce the problem [in the latest version of Atom](http://flight-manual.atom.io/hacking-atom/sections/debugging/#update-to-the-latest-version), if the problem happens when you run Atom in [safe mode](http://flight-manual.atom.io/hacking-atom/sections/debugging/#check-if-the-problem-shows-up-in-safe-mode), and if you can get the desired behavior by changing [Atom's or packages' config settings](http://flight-manual.atom.io/hacking-atom/sections/debugging/#check-atom-and-package-settings). * **Check the [FAQs on the forum](https://discuss.atom.io/c/faq)** for a list of common questions and problems. * **Determine [which repository the problem should be reported in](#atom-and-packages)**. * **Perform a [cursory search](https://github.com/issues?q=+is%3Aissue+user%3Aatom)** to see if the problem has already been reported. If it has, add a comment to the existing issue instead of opening a new one. @@ -100,13 +100,13 @@ Explain the problem and include additional details to help maintainers reproduce * **Explain which behavior you expected to see instead and why.** * **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem. If you use the keyboard while following the steps, **record the GIF with the [Keybinding Resolver](https://github.com/atom/keybinding-resolver) shown**. You can use [this tool](http://www.cockos.com/licecap/) to record GIFs on OSX and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux. * **If you're reporting that Atom crashed**, include a crash report with a stack trace from the operating system. On OSX, the crash report will be available in `Console.app` under "Diagnostic and usage information" > "User diagnostic reports". Include the crash report in the issue in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines), a [file attachment](https://help.github.com/articles/file-attachments-on-issues-and-pull-requests/), or put it in a [gist](https://gist.github.com/) and provide link to that gist. -* **If the problem is related to performance**, include a [CPU profile capture and a screenshot](https://atom.io/docs/latest/hacking-atom-debugging#diagnose-performance-problems-with-the-dev-tools-cpu-profiler) with your report. +* **If the problem is related to performance**, include a [CPU profile capture and a screenshot](http://flight-manual.atom.io/hacking-atom/sections/debugging/#diagnose-performance-problems-with-the-dev-tools-cpu-profiler) with your report. * **If the Chrome's developer tools pane is shown without you triggering it**, that normally means that an exception was thrown. The Console tab will include an entry for the exception. Expand the exception so that the stack trace is visible, and provide the full exception and stack trace in a [code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines) and as a screenshot. * **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below. Provide more context by answering these questions: -* **Can you reproduce the problem in [safe mode](https://atom.io/docs/latest/hacking-atom-debugging#check-if-the-problem-shows-up-in-safe-mode)?** +* **Can you reproduce the problem in [safe mode](http://flight-manual.atom.io/hacking-atom/sections/debugging/#diagnose-runtime-performance-problems-with-the-dev-tools-cpu-profiler)?** * **Did the problem start happening recently** (e.g. after updating to a new version of Atom) or was this always a problem? * If the problem started happening recently, **can you reproduce the problem in an older version of Atom?** What's the most recent version in which the problem doesn't happen? You can download older versions of Atom from [the releases page](https://github.com/atom/atom/releases). * **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. @@ -118,7 +118,7 @@ Include details about your configuration and environment: * **What's the name and version of the OS you're using**? * **Are you running Atom in a virtual machine?** If so, which VM software are you using and which operating systems and versions are used for the host and the guest? * **Which [packages](#atom-and-packages) do you have installed?** You can get that list by running `apm list --installed`. -* **Are you using [local configuration files](https://atom.io/docs/latest/using-atom-basic-customization)** `config.cson`, `keymap.cson`, `snippets.cson`, `styles.less` and `init.coffee` to customize Atom? If so, provide the contents of those files, preferably in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines) or with a link to a [gist](https://gist.github.com/). +* **Are you using [local configuration files](http://flight-manual.atom.io/using-atom/sections/basic-customization/)** `config.cson`, `keymap.cson`, `snippets.cson`, `styles.less` and `init.coffee` to customize Atom? If so, provide the contents of those files, preferably in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines) or with a link to a [gist](https://gist.github.com/). * **Are you using Atom with multiple monitors?** If so, can you reproduce the problem when you use a single monitor? * **Which keyboard layout are you using?** Are you using a US layout or some other layout? @@ -166,7 +166,7 @@ Before creating enhancement suggestions, please check [this list](#before-submit #### Before Submitting An Enhancement Suggestion -* **Check the [debugging guide](https://atom.io/docs/latest/hacking-atom-debugging)** for tips — you might discover that the enhancement is already available. Most importantly, check if you're using [the latest version of Atom](https://atom.io/docs/latest/hacking-atom-debugging#update-to-the-latest-version) and if you can get the desired behavior by changing [Atom's or packages' config settings](https://atom.io/docs/latest/hacking-atom-debugging#check-atom-and-package-settings). +* **Check the [debugging guide](http://flight-manual.atom.io/hacking-atom/sections/debugging/)** for tips — you might discover that the enhancement is already available. Most importantly, check if you're using [the latest version of Atom](http://flight-manual.atom.io/hacking-atom/sections/debugging/#update-to-the-latest-version) and if you can get the desired behavior by changing [Atom's or packages' config settings](http://flight-manual.atom.io/hacking-atom/sections/debugging/#check-atom-and-package-settings). * **Check if there's already [a package](https://atom.io/packages) which provides that enhancement.** * **Determine [which repository the enhancement should be suggested in](#atom-and-packages).** * **Perform a [cursory search](https://github.com/issues?q=+is%3Aissue+user%3Aatom)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. @@ -376,7 +376,7 @@ Please open an issue on `atom/atom` if you have suggestions for new labels, and | `windows` | [search][search-atom-repo-label-windows] | [search][search-atom-org-label-windows] | Related to Atom running on Windows. | | `linux` | [search][search-atom-repo-label-linux] | [search][search-atom-org-label-linux] | Related to Atom running on Linux. | | `mac` | [search][search-atom-repo-label-mac] | [search][search-atom-org-label-mac] | Related to Atom running on OSX. | -| `documentation` | [search][search-atom-repo-label-documentation] | [search][search-atom-org-label-documentation] | Related to any type of documentation (e.g. [API documentation](https://atom.io/docs/api/latest/Atom) and the [flight manual](https://atom.io/docs/latest/)). | +| `documentation` | [search][search-atom-repo-label-documentation] | [search][search-atom-org-label-documentation] | Related to any type of documentation (e.g. [API documentation](https://atom.io/docs/api/latest/) and the [flight manual](http://flight-manual.atom.io/)). | | `performance` | [search][search-atom-repo-label-performance] | [search][search-atom-org-label-performance] | Related to performance. | | `security` | [search][search-atom-repo-label-security] | [search][search-atom-org-label-security] | Related to security. | | `ui` | [search][search-atom-repo-label-ui] | [search][search-atom-org-label-ui] | Related to visual design. | From afdd8d2b6dce2de309ceafb02dfd5dc20c4f89fb Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 8 Apr 2016 16:09:33 +0200 Subject: [PATCH 719/971] Avoid creating line nodes twice in lines-yardstick-spec.coffee --- spec/lines-yardstick-spec.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/lines-yardstick-spec.coffee b/spec/lines-yardstick-spec.coffee index 15ec2a533..2f7233e2f 100644 --- a/spec/lines-yardstick-spec.coffee +++ b/spec/lines-yardstick-spec.coffee @@ -42,11 +42,12 @@ describe "LinesYardstick", -> lineNode mockLineNodesProvider = + lineNodesById: {} lineIdForScreenRow: (screenRow) -> editor.screenLineForScreenRow(screenRow).id lineNodeForScreenRow: (screenRow) -> - buildLineNode(screenRow) + @lineNodesById[@lineIdForScreenRow(screenRow)] ?= buildLineNode(screenRow) textNodesForScreenRow: (screenRow) -> lineNode = @lineNodeForScreenRow(screenRow) From e587a67f3bd4b3bf479820e9c11ba6ebdd50d756 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 8 Apr 2016 10:22:50 -0600 Subject: [PATCH 720/971] Document persistent option in TextEditor::addMarkerLayer --- src/text-editor.coffee | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 142405487..e99ebafb3 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1908,6 +1908,11 @@ class TextEditor extends Model # * `options` An {Object} containing the following keys: # * `maintainHistory` A {Boolean} indicating whether marker state should be # restored on undo/redo. Defaults to `false`. + # * `persistent` A {Boolean} indicating whether or not this marker layer + # should be serialized and deserialized along with the rest of the + # buffer. Defaults to `false`. If `true`, the marker layer's id will be + # maintained across the serialization boundary, allowing you to retrieve + # it via {::getMarkerLayer}. # # This API is experimental and subject to change on any release. # From 37bfbe8538063c2afbe6666a1b88abbc77dacb8f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 8 Apr 2016 10:23:01 -0600 Subject: [PATCH 721/971] Document return value correctly --- src/text-editor.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e99ebafb3..5e0141d02 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1926,8 +1926,8 @@ class TextEditor extends Model # # This API is experimental and subject to change on any release. # - # Returns a {MarkerLayer} or `undefined` if no layer exists with the given - # id. + # Returns a {DisplayMarkerLayer} or `undefined` if no layer exists with the + # given id. getMarkerLayer: (id) -> @displayLayer.getMarkerLayer(id) From 5e5cea96003121a5aa18017eb1f4ed4c07f5d626 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 8 Apr 2016 10:25:25 -0600 Subject: [PATCH 722/971] =?UTF-8?q?Drop=20=E2=80=9Cexperimental=E2=80=9D?= =?UTF-8?q?=20warnings=20from=20marker=20layer=20APIs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/text-editor.coffee | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 5e0141d02..403191030 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1646,16 +1646,14 @@ class TextEditor extends Model decorateMarker: (marker, decorationParams) -> @decorationManager.decorateMarker(marker, decorationParams) - # Essential: *Experimental:* Add a decoration to every marker in the given - # marker layer. Can be used to decorate a large number of markers without - # having to create and manage many individual decorations. + # Essential: Add a decoration to every marker in the given marker layer. Can + # be used to decorate a large number of markers without having to create and + # manage many individual decorations. # # * `markerLayer` A {DisplayMarkerLayer} or {MarkerLayer} to decorate. # * `decorationParams` The same parameters that are passed to # {decorateMarker}, except the `type` cannot be `overlay` or `gutter`. # - # This API is experimental and subject to change on any release. - # # Returns a {LayerDecoration}. decorateMarkerLayer: (markerLayer, decorationParams) -> @decorationManager.decorateMarkerLayer(markerLayer, decorationParams) @@ -1903,7 +1901,7 @@ class TextEditor extends Model destroyMarker: (id) -> @getMarker(id)?.destroy() - # Extended: *Experimental:* Create a marker layer to group related markers. + # Essential: Create a marker layer to group related markers. # # * `options` An {Object} containing the following keys: # * `maintainHistory` A {Boolean} indicating whether marker state should be @@ -1914,30 +1912,24 @@ class TextEditor extends Model # maintained across the serialization boundary, allowing you to retrieve # it via {::getMarkerLayer}. # - # This API is experimental and subject to change on any release. - # # Returns a {DisplayMarkerLayer}. addMarkerLayer: (options) -> @displayLayer.addMarkerLayer(options) - # Public: *Experimental:* Get a {DisplayMarkerLayer} by id. + # Essential: Get a {DisplayMarkerLayer} by id. # # * `id` The id of the marker layer to retrieve. # - # This API is experimental and subject to change on any release. - # # Returns a {DisplayMarkerLayer} or `undefined` if no layer exists with the # given id. getMarkerLayer: (id) -> @displayLayer.getMarkerLayer(id) - # Public: *Experimental:* Get the default {DisplayMarkerLayer}. + # Essential: Get the default {DisplayMarkerLayer}. # # All marker APIs not tied to an explicit layer interact with this default # layer. # - # This API is experimental and subject to change on any release. - # # Returns a {DisplayMarkerLayer}. getDefaultMarkerLayer: -> @defaultMarkerLayer From 14afb4967694f90497823568869e39c74054d35f Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 8 Apr 2016 15:58:23 -0400 Subject: [PATCH 723/971] Use #index instead of #openIndex. #openIndex is going away: https://github.com/nodegit/nodegit/pull/989 --- src/git-repository-async.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index e45c1e47e..373ee44bc 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -305,7 +305,7 @@ export default class GitRepositoryAsync { .then(relativePath => { return this.repoPool.enqueue(() => { return this.getRepo() - .then(repo => repo.openIndex()) + .then(repo => repo.index()) .then(index => { const entry = index.getByPath(relativePath) if (!entry) return false From effe882a8d602ad4ab4a5762a772a0a4083c5ecc Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 8 Apr 2016 15:30:33 -0600 Subject: [PATCH 724/971] Make atomic leading whitespace optional (defaults to enabled) Closes #3174 --- src/config-schema.coffee | 4 ++++ src/text-editor.coffee | 1 + 2 files changed, 5 insertions(+) diff --git a/src/config-schema.coffee b/src/config-schema.coffee index 346551ff5..ed6691380 100644 --- a/src/config-schema.coffee +++ b/src/config-schema.coffee @@ -155,6 +155,10 @@ module.exports = type: 'boolean' default: true description: 'Show line numbers in the editor\'s gutter.' + atomicSoftTabs: + type: 'boolean' + default: true + description: 'Skip over tab-length runs of leading whitespace when moving the cursor.' autoIndent: type: 'boolean' default: true diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 403191030..cb5524e65 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -278,6 +278,7 @@ class TextEditor extends Model invisibles: @getInvisibles(), softWrapColumn: @getSoftWrapColumn(), showIndentGuides: @config.get('editor.showIndentGuide', scope: @getRootScopeDescriptor()), + atomicSoftTabs: @config.get('editor.atomicSoftTabs', scope: @getRootScopeDescriptor()), tabLength: @getTabLength(), ratioForCharacter: @ratioForCharacter.bind(this), isWrapBoundary: isWrapBoundary, From 6bbf0d3271d73c51ff009bf6f081ebd47a699c55 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Fri, 8 Apr 2016 14:36:51 -0700 Subject: [PATCH 725/971] Signing support on Windows with P12 keys --- build/certs/AtomDevTestSignKey.p12 | Bin 0 -> 1765 bytes build/tasks/codesign-task.coffee | 49 +++++++++++++++++++++++------ 2 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 build/certs/AtomDevTestSignKey.p12 diff --git a/build/certs/AtomDevTestSignKey.p12 b/build/certs/AtomDevTestSignKey.p12 new file mode 100644 index 0000000000000000000000000000000000000000..a93e9a9f042a29411a05cdf832c99041fd6d6180 GIT binary patch literal 1765 zcmb7FX;c$e6n--kgF*JN2!|yK2+B4g2@((#0yaktvM6vs!xADON+3jBs7iveDWJuQ ziqXi9R0Tv-tY8rX9wVq&WDBAqu2rcDiWT~>Ew$(Pr{~U_x%a#8yLaZ!d}nY8@d1ip zxP&Mrk`|=1)5p{i6+|W><`N{tYzU|0670%s?L4xjU5^!8sZU|RO&n#=eOfsq7hlf{6-QGPd zN^vc!Deho{doNAf#RQ#~XIp7Y+8y#MRqlJ#@k~9(=3BFrSs$#) zd`02jljrXL(%+Ok-0#^`u^d}9vO^@U?$q;@+In8I=`m4T+c+6{px0}~J+FPmzwWKd zeqEGpwK$VJ=x>-|q7c1Z>Z@?RU+;Qj!l?Sy>E;50oa3e~tZE=*0IBDrJ#O-}|*FF1AK)**vT^=o;o>j|mW?snuqjixmXK+hj zN`w|G?ec{)iIhv?jK*-|9AqM^Vty+n$s*?M^*z01XKSLas@&W;l4t|sxH_ykg=>tq zJauamQN71Cm)ck|s}z;iEmyc*NWY9^=YRI)fgR7S$F-Y{lp@`H_OeBq0@|Ltn-k)* zY|gGPcyIM=vN>qU@9t3+^O`z4l|E8#wm-#l6fPLKK>k$+XUFRb%gxJb=dK=IM?({ReW?q|0{~WUz_};=g+EdmI z%~zF1YgcX0vs|$&wUuEpX4qnt?UI|rxP+=jW?p$3Js4y7v!yUb{fJz!cb_<+>%_rs zzZa!hvWnHpxx-E4f$5{RZ7Dv|P;{C|cVM%XBR%1#4nD zyfS9>7clzBqO!n&>r`+hasWA(R7OfFaA>eEdSwHW}%srI-n5nD~b$d(==!A6P~KbgNlkiZvGR`HWWlz`ai z_}KVpipM5Vj6fJG+KQVRs8gMAnj;Oz=}vU#5Ce4@lj=x=2ZPCAhTsdS44n2?McZ#x zWC^19(`8wFp(u7;EH6UD$Hm9LzByzDz{EKK7iXbjF=&~`>Fe^F`*2|UZnM}0-M1$` z-C>80Z8Tu`sQA51JQq}$U{aEztr3=Z>@;=3HY3}*5)~5z>H0WEuEc^xk87-OXUm39 zI-FX1Ap5$3z;Q4^(OY?A_vmN#4}^St@y>)4JqB})=foL*_Z7tlU$>lR8&*B`ST>;1 znWNM66Q}zziXi|gPQnv$HTY;IWCWQcpg6!IrXqw8f(NH+HQ$wNeG`1b_l=|>53PA0 ze!}tpPgkN9IIoAB_GDron={)vb6_Il>zTk8hQVQwf}aD$Ctgs4^jMjTB9x-aCA>ho zt*I++$>lTUbY-dRWN5h?=_unYJMFDp=aqUgXY}Lz{oV}QXGra6QO7P$M0ZbzfrI@} zx#8^F^6}#|{STkpTccWTF7c+|)+J_FG5*2W(K(vdXj>ugqjGf8CGMcvfboa1uCTo8cd_e`A@*-rK0Jqhg6_rrc^3ot+O=)Q RYtV|ry%{EHmBgtZ&Yvnyq?G^w literal 0 HcmV?d00001 diff --git a/build/tasks/codesign-task.coffee b/build/tasks/codesign-task.coffee index 2a061742b..a00e2d358 100644 --- a/build/tasks/codesign-task.coffee +++ b/build/tasks/codesign-task.coffee @@ -1,24 +1,46 @@ path = require 'path' +fs = require 'fs' +request = require 'request' module.exports = (grunt) -> {spawn} = require('./task-helpers')(grunt) + signUsingWindowsSDK = (exeToSign, callback) -> + {WIN_P12KEY_PASSWORD, WIN_P12KEY_URL} = process.env + if WIN_P12KEY_URL? + grunt.log.ok("Obtaining signing key") + downloadedKeyFile = path.resolve(__dirname, 'DownloadedSignKey.p12') + downloadFile WIN_P12KEY_URL, downloadedKeyFile, (done) -> + signUsingWindowsSDKTool exeToSign, downloadedKeyFile, WIN_P12KEY_PASSWORD, (done) -> + fs.unlinkSync(downloadedKeyFile) + callback() + else + signUsingWindowsSDKTool exeToSign, path.resolve(__dirname, '..', 'certs', 'AtomDevTestSignKey.p12'), 'password', callback + + signUsingWindowsSDKTool = (exeToSign, keyFilePath, password, callback) -> + grunt.log.ok("Signing #{exeToSign}") + args = ['sign', '/v', '/p', password, '/f', keyFilePath, exeToSign] + spawn {cmd: 'C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.1A\\bin\\signtool.exe', args: args}, callback + + signUsingJanky = (exeToSign, callback) -> + spawn {cmd: 'signtool', args: [exeToSign]}, callback + + signWindowsExecutable = if process.env.JANKY_SIGNTOOL then signUsingJanky else signUsingWindowsSDK + grunt.registerTask 'codesign:exe', 'CodeSign Atom.exe and Update.exe', -> done = @async() spawn {cmd: 'taskkill', args: ['/F', '/IM', 'atom.exe']}, -> - cmd = process.env.JANKY_SIGNTOOL ? 'signtool' atomExePath = path.join(grunt.config.get('atom.shellAppDir'), 'atom.exe') - spawn {cmd, args: [atomExePath]}, (error) -> + signWindowsExecutable atomExePath, (error) -> return done(error) if error? updateExePath = path.resolve(__dirname, '..', 'node_modules', 'grunt-electron-installer', 'vendor', 'Update.exe') - spawn {cmd, args: [updateExePath]}, (error) -> done(error) + signWindowsExecutable updateExePath, (error) -> done(error) grunt.registerTask 'codesign:installer', 'CodeSign AtomSetup.exe', -> done = @async() - cmd = process.env.JANKY_SIGNTOOL ? 'signtool' atomSetupExePath = path.resolve(grunt.config.get('atom.buildDir'), 'installer', 'AtomSetup.exe') - spawn {cmd, args: [atomSetupExePath]}, (error) -> done(error) + signWindowsExecutable atomSetupExePath, (error) -> done(error) grunt.registerTask 'codesign:app', 'CodeSign Atom.app', -> done = @async() @@ -26,14 +48,23 @@ module.exports = (grunt) -> unlockKeychain (error) -> return done(error) if error? - cmd = 'codesign' args = ['--deep', '--force', '--verbose', '--sign', 'Developer ID Application: GitHub', grunt.config.get('atom.shellAppDir')] - spawn {cmd, args}, (error) -> done(error) + spawn {cmd: 'codesign', args: args}, (error) -> done(error) unlockKeychain = (callback) -> return callback() unless process.env.XCODE_KEYCHAIN - cmd = 'security' {XCODE_KEYCHAIN_PASSWORD, XCODE_KEYCHAIN} = process.env args = ['unlock-keychain', '-p', XCODE_KEYCHAIN_PASSWORD, XCODE_KEYCHAIN] - spawn {cmd, args}, (error) -> callback(error) + spawn {cmd: 'security', args: args}, (error) -> callback(error) + + downloadFile = (sourceUrl, targetPath, callback) -> + options = { + url: sourceUrl + headers: { + 'User-Agent': 'Atom Signing Key build task', + 'Accept': 'application/vnd.github.VERSION.raw' } + } + request(options) + .pipe(fs.createWriteStream(targetPath)) + .on('finish', callback) From d691eb0e3422970209f63649145886350b1c36cf Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 8 Apr 2016 16:10:13 -0600 Subject: [PATCH 726/971] :arrow_up: text-buffer@9.0.0-beta2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 689b6f9df..81bfdcbb3 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.4.6", + "text-buffer": "9.0.0-beta2", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 913183417101b99428da357240471e078b87b2dd Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Fri, 8 Apr 2016 20:05:00 -0400 Subject: [PATCH 727/971] :arrow_up: language-css@0.36.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 689b6f9df..5c472966c 100644 --- a/package.json +++ b/package.json @@ -121,7 +121,7 @@ "language-clojure": "0.20.0", "language-coffee-script": "0.46.1", "language-csharp": "0.12.1", - "language-css": "0.36.0", + "language-css": "0.36.1", "language-gfm": "0.85.0", "language-git": "0.12.1", "language-go": "0.42.0", From 99ede51e75b71eb085e5cebf0af615529171c677 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 8 Apr 2016 18:16:28 -0600 Subject: [PATCH 728/971] Delete duplicated method --- src/lines-tile-component.coffee | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index d006daa86..3cc3accee 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -418,8 +418,5 @@ class LinesTileComponent lineIdForScreenRow: (screenRow) -> @lineIdsByScreenRow[screenRow] - lineNodeForScreenRow: (screenRow) -> - @lineNodesByLineId[@lineIdsByScreenRow[screenRow]] - textNodesForScreenRow: (screenRow) -> @textNodesByLineId[@lineIdsByScreenRow[screenRow]]?.slice() From f3c6a779542ffe67c81fb86768d169a7e78a8d14 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 9 Apr 2016 09:30:47 +0200 Subject: [PATCH 729/971] :green_heart: Use persistent instead of maintainHistory in specs --- spec/project-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 499efd017..7a6168ad5 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -87,7 +87,7 @@ describe "Project", -> runs -> bufferA = atom.project.getBuffers()[0] - layerA = bufferA.addMarkerLayer(maintainHistory: true) + layerA = bufferA.addMarkerLayer(persistent: true) markerA = layerA.markPosition([0, 3]) notQuittingProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) From ab0d69ce90594c8db38d41e385fa11d73484a3f3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 9 Apr 2016 09:50:12 +0200 Subject: [PATCH 730/971] Make selectionsMarkerLayer also persistent --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index cb5524e65..a580aa7db 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -169,7 +169,7 @@ class TextEditor extends Model @displayLayer ?= @buffer.addDisplayLayer() @displayLayer.setTextDecorationLayer(@tokenizedBuffer) @defaultMarkerLayer = @displayLayer.addMarkerLayer() - @selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true) + @selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true, persistent: true) @decorationManager = new DecorationManager(@displayLayer, @defaultMarkerLayer) From 88aa5cc68e7af9cead465ecfab1ef41cd1eca513 Mon Sep 17 00:00:00 2001 From: Cole R Lawrence Date: Sat, 9 Apr 2016 13:58:31 -0500 Subject: [PATCH 731/971] :memo: Add the --no-install flag to the windows build readme --- docs/build-instructions/windows.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/build-instructions/windows.md b/docs/build-instructions/windows.md index 13e3be590..3ec28f139 100644 --- a/docs/build-instructions/windows.md +++ b/docs/build-instructions/windows.md @@ -45,6 +45,7 @@ This will create the Atom application in the `out\Atom` folder as well as copy i ```bash ./script/build --build-dir Z:\Some\Temporary\Directory\ ``` + * `--no-install` - Skips the installation task after building. * `--verbose` - Verbose mode. A lot more information output. ## Do I have to use GitHub Desktop? From 7decbf0d06f36b540d66627250e182f40762d1c1 Mon Sep 17 00:00:00 2001 From: Cole R Lawrence Date: Sat, 9 Apr 2016 14:07:37 -0500 Subject: [PATCH 732/971] :memo: Fix linking the decorateMarker Error happens at this place in the docs https://atom.io/docs/api/v1.6.2/TextEditor#instance-decorateMarkerLayer --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 58ecf4712..0d1b3795e 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1551,7 +1551,7 @@ class TextEditor extends Model # # * `markerLayer` A {TextEditorMarkerLayer} or {MarkerLayer} to decorate. # * `decorationParams` The same parameters that are passed to - # {decorateMarker}, except the `type` cannot be `overlay` or `gutter`. + # {TextEditor::decorateMarker}, except the `type` cannot be `overlay` or `gutter`. # # This API is experimental and subject to change on any release. # From 8cd48004dd68d5ea3e4d4f0fada256aa1b83186d Mon Sep 17 00:00:00 2001 From: Willem Van Lint Date: Sat, 9 Apr 2016 18:42:18 -0700 Subject: [PATCH 733/971] Fixed positioning for overlay --- src/text-editor-presenter.coffee | 12 +++++++----- static/text-editor-light.less | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index ef1b403c3..e5a9cd589 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -459,19 +459,21 @@ class TextEditorPresenter pixelPosition = @pixelPositionForScreenPosition(screenPosition) - top = pixelPosition.top + @lineHeight - left = pixelPosition.left + @gutterWidth + # Fixed positioning. + top = @boundingClientRect.top + pixelPosition.top + @lineHeight + left = @boundingClientRect.left + pixelPosition.left + @gutterWidth if overlayDimensions = @overlayDimensions[decoration.id] {itemWidth, itemHeight, contentMargin} = overlayDimensions - rightDiff = left + @boundingClientRect.left + itemWidth + contentMargin - @windowWidth + rightDiff = left + itemWidth + contentMargin - @windowWidth left -= rightDiff if rightDiff > 0 - leftDiff = left + @boundingClientRect.left + contentMargin + leftDiff = left + contentMargin left -= leftDiff if leftDiff < 0 - if top + @boundingClientRect.top + itemHeight > @windowHeight and top - (itemHeight + @lineHeight) >= 0 + if top + itemHeight > @windowHeight and + top - (itemHeight + @lineHeight) >= 0 top -= itemHeight + @lineHeight pixelPosition.top = top diff --git a/static/text-editor-light.less b/static/text-editor-light.less index 7fafade1e..f5429fd7f 100644 --- a/static/text-editor-light.less +++ b/static/text-editor-light.less @@ -15,7 +15,7 @@ atom-text-editor[mini] { } atom-overlay { - position: absolute; + position: fixed; display: block; z-index: 4; } From 4c80c7721055f298aecb9baf05c4e3ae29cf4a0b Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sun, 10 Apr 2016 11:58:04 -0400 Subject: [PATCH 734/971] :arrow_up: autocomplete-css@0.11.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5c472966c..27725f915 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "about": "1.5.0", "archive-view": "0.61.1", "autocomplete-atom-api": "0.10.0", - "autocomplete-css": "0.11.0", + "autocomplete-css": "0.11.1", "autocomplete-html": "0.7.2", "autocomplete-plus": "2.29.2", "autocomplete-snippets": "1.10.0", From 038640a65829015c200b3a049bc10520b9a5ffd1 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sun, 10 Apr 2016 12:00:20 -0400 Subject: [PATCH 735/971] :arrow_up: language-less@0.29.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 27725f915..7e6fa0838 100644 --- a/package.json +++ b/package.json @@ -130,7 +130,7 @@ "language-java": "0.17.0", "language-javascript": "0.110.0", "language-json": "0.18.0", - "language-less": "0.29.2", + "language-less": "0.29.3", "language-make": "0.21.1", "language-mustache": "0.13.0", "language-objective-c": "0.15.1", From 852d8d71a401333fbc60ccd37ca4b58956f411d4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 11 Apr 2016 13:14:30 +0200 Subject: [PATCH 736/971] :arrow_up: bookmarks --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7e6fa0838..2917a512b 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "autoflow": "0.27.0", "autosave": "0.23.1", "background-tips": "0.26.0", - "bookmarks": "0.38.3", + "bookmarks": "0.39.0", "bracket-matcher": "0.82.0", "command-palette": "0.38.0", "deprecation-cop": "0.54.1", From a6e90ba7ee082a056929e01855609d584077283a Mon Sep 17 00:00:00 2001 From: SEAPUNK Date: Thu, 7 Apr 2016 19:09:20 -0500 Subject: [PATCH 737/971] :arrow_up: nodegit@0.12.2 This adds support for 32-bit linux systems --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2917a512b..80c3cdce7 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "less-cache": "0.23", "line-top-index": "0.2.0", "marked": "^0.3.4", - "nodegit": "0.12.1", + "nodegit": "0.12.2", "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "^5", From 26206bb9c076be653fdb7cd3c4eb443d0fcb1784 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 11 Apr 2016 10:55:13 -0400 Subject: [PATCH 738/971] Update license override for tweetnacl. --- build/tasks/license-overrides.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/tasks/license-overrides.coffee b/build/tasks/license-overrides.coffee index aa136eb37..be56934a2 100644 --- a/build/tasks/license-overrides.coffee +++ b/build/tasks/license-overrides.coffee @@ -123,10 +123,10 @@ module.exports = (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ - 'tweetnacl@0.13.2': + 'tweetnacl@0.14.3': repository: 'https://github.com/dchest/tweetnacl-js' license: 'Public Domain' - source: 'https://github.com/dchest/tweetnacl-js/blob/2f328394f74d83564634fb89ea2798caa3a4edb9/README.md says public domain.' + source: 'https://github.com/dchest/tweetnacl-js/blob/1042c9c65dc8f1dcb9e981d962d7dbbcf58f1fdc/COPYING.txt says public domain.' 'json-schema@0.2.2': repository: 'https://github.com/kriszyp/json-schema' license: 'BSD' From 041906cdae53a22867fd34742fc970994a665f78 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 11 Apr 2016 11:42:21 -0400 Subject: [PATCH 739/971] Update nodegit API usage. This changed in https://github.com/nodegit/nodegit/pull/968. --- src/git-repository-async.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 373ee44bc..aacd482f7 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -40,7 +40,7 @@ export default class GitRepositoryAsync { constructor (_path, options = {}) { // We'll serialize our access manually. - Git.disableThreadSafety() + Git.setThreadSafetyStatus(Git.THREAD_SAFETY.DISABLED) this.emitter = new Emitter() this.subscriptions = new CompositeDisposable() From 35982bc6ed421bf658287a607a9a4c3e6585a7bc Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 11 Apr 2016 13:47:06 -0400 Subject: [PATCH 740/971] :arrow_up: status-bar@1.2.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 80c3cdce7..e99251778 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.235.1", "snippets": "1.0.2", "spell-check": "0.67.1", - "status-bar": "1.2.2", + "status-bar": "1.2.3", "styleguide": "0.45.2", "symbols-view": "0.112.0", "tabs": "0.92.1", From d6e00bb53b3460d1a1aad7b19ebd4bb022411e8c Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 11 Apr 2016 16:03:11 -0400 Subject: [PATCH 741/971] Correctly link the debugging guide It was being interpreted as a relative link --- ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index e142ec930..5b3ea42f4 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -2,7 +2,7 @@ * [ ] Can you reproduce the problem in [safe mode](http://flight-manual.atom.io/hacking-atom/sections/debugging/#check-if-the-problem-shows-up-in-safe-mode)? * [ ] Are you running the [latest version of Atom](http://flight-manual.atom.io/hacking-atom/sections/debugging/#update-to-the-latest-version)? -* [ ] Did you check the [debugging guide](flight-manual.atom.io/hacking-atom/sections/debugging/)? +* [ ] Did you check the [debugging guide](http://flight-manual.atom.io/hacking-atom/sections/debugging/)? * [ ] Did you check the [FAQs on Discuss](https://discuss.atom.io/c/faq)? * [ ] Are you reporting to the [correct repository](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#atom-and-packages)? * [ ] Did you [perform a cursory search](https://github.com/issues?q=is%3Aissue+user%3Aatom+-repo%3Aatom%2Felectron) to see if your bug or enhancement is already reported? From 38fdbac450335a8e3128efeaf2ebe4e090861bd7 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 11 Apr 2016 14:30:25 -0600 Subject: [PATCH 742/971] Fix lint error --- build/tasks/codesign-task.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/tasks/codesign-task.coffee b/build/tasks/codesign-task.coffee index a00e2d358..d084028a7 100644 --- a/build/tasks/codesign-task.coffee +++ b/build/tasks/codesign-task.coffee @@ -63,7 +63,8 @@ module.exports = (grunt) -> url: sourceUrl headers: { 'User-Agent': 'Atom Signing Key build task', - 'Accept': 'application/vnd.github.VERSION.raw' } + 'Accept': 'application/vnd.github.VERSION.raw' + } } request(options) .pipe(fs.createWriteStream(targetPath)) From 639ee963a0aec8368fdb5ff8d9d93d77e27976ab Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 11 Apr 2016 14:35:47 -0600 Subject: [PATCH 743/971] Revert "Merge pull request #11412 from damieng/dg-ns-codesign" This reverts commit 91b0726c9edc8937a7d97069a6494ddb209f8f0d, reversing changes made to d6e00bb53b3460d1a1aad7b19ebd4bb022411e8c. --- build/certs/AtomDevTestSignKey.p12 | Bin 1765 -> 0 bytes build/tasks/codesign-task.coffee | 50 ++++++----------------------- 2 files changed, 9 insertions(+), 41 deletions(-) delete mode 100644 build/certs/AtomDevTestSignKey.p12 diff --git a/build/certs/AtomDevTestSignKey.p12 b/build/certs/AtomDevTestSignKey.p12 deleted file mode 100644 index a93e9a9f042a29411a05cdf832c99041fd6d6180..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1765 zcmb7FX;c$e6n--kgF*JN2!|yK2+B4g2@((#0yaktvM6vs!xADON+3jBs7iveDWJuQ ziqXi9R0Tv-tY8rX9wVq&WDBAqu2rcDiWT~>Ew$(Pr{~U_x%a#8yLaZ!d}nY8@d1ip zxP&Mrk`|=1)5p{i6+|W><`N{tYzU|0670%s?L4xjU5^!8sZU|RO&n#=eOfsq7hlf{6-QGPd zN^vc!Deho{doNAf#RQ#~XIp7Y+8y#MRqlJ#@k~9(=3BFrSs$#) zd`02jljrXL(%+Ok-0#^`u^d}9vO^@U?$q;@+In8I=`m4T+c+6{px0}~J+FPmzwWKd zeqEGpwK$VJ=x>-|q7c1Z>Z@?RU+;Qj!l?Sy>E;50oa3e~tZE=*0IBDrJ#O-}|*FF1AK)**vT^=o;o>j|mW?snuqjixmXK+hj zN`w|G?ec{)iIhv?jK*-|9AqM^Vty+n$s*?M^*z01XKSLas@&W;l4t|sxH_ykg=>tq zJauamQN71Cm)ck|s}z;iEmyc*NWY9^=YRI)fgR7S$F-Y{lp@`H_OeBq0@|Ltn-k)* zY|gGPcyIM=vN>qU@9t3+^O`z4l|E8#wm-#l6fPLKK>k$+XUFRb%gxJb=dK=IM?({ReW?q|0{~WUz_};=g+EdmI z%~zF1YgcX0vs|$&wUuEpX4qnt?UI|rxP+=jW?p$3Js4y7v!yUb{fJz!cb_<+>%_rs zzZa!hvWnHpxx-E4f$5{RZ7Dv|P;{C|cVM%XBR%1#4nD zyfS9>7clzBqO!n&>r`+hasWA(R7OfFaA>eEdSwHW}%srI-n5nD~b$d(==!A6P~KbgNlkiZvGR`HWWlz`ai z_}KVpipM5Vj6fJG+KQVRs8gMAnj;Oz=}vU#5Ce4@lj=x=2ZPCAhTsdS44n2?McZ#x zWC^19(`8wFp(u7;EH6UD$Hm9LzByzDz{EKK7iXbjF=&~`>Fe^F`*2|UZnM}0-M1$` z-C>80Z8Tu`sQA51JQq}$U{aEztr3=Z>@;=3HY3}*5)~5z>H0WEuEc^xk87-OXUm39 zI-FX1Ap5$3z;Q4^(OY?A_vmN#4}^St@y>)4JqB})=foL*_Z7tlU$>lR8&*B`ST>;1 znWNM66Q}zziXi|gPQnv$HTY;IWCWQcpg6!IrXqw8f(NH+HQ$wNeG`1b_l=|>53PA0 ze!}tpPgkN9IIoAB_GDron={)vb6_Il>zTk8hQVQwf}aD$Ctgs4^jMjTB9x-aCA>ho zt*I++$>lTUbY-dRWN5h?=_unYJMFDp=aqUgXY}Lz{oV}QXGra6QO7P$M0ZbzfrI@} zx#8^F^6}#|{STkpTccWTF7c+|)+J_FG5*2W(K(vdXj>ugqjGf8CGMcvfboa1uCTo8cd_e`A@*-rK0Jqhg6_rrc^3ot+O=)Q RYtV|ry%{EHmBgtZ&Yvnyq?G^w diff --git a/build/tasks/codesign-task.coffee b/build/tasks/codesign-task.coffee index d084028a7..2a061742b 100644 --- a/build/tasks/codesign-task.coffee +++ b/build/tasks/codesign-task.coffee @@ -1,46 +1,24 @@ path = require 'path' -fs = require 'fs' -request = require 'request' module.exports = (grunt) -> {spawn} = require('./task-helpers')(grunt) - signUsingWindowsSDK = (exeToSign, callback) -> - {WIN_P12KEY_PASSWORD, WIN_P12KEY_URL} = process.env - if WIN_P12KEY_URL? - grunt.log.ok("Obtaining signing key") - downloadedKeyFile = path.resolve(__dirname, 'DownloadedSignKey.p12') - downloadFile WIN_P12KEY_URL, downloadedKeyFile, (done) -> - signUsingWindowsSDKTool exeToSign, downloadedKeyFile, WIN_P12KEY_PASSWORD, (done) -> - fs.unlinkSync(downloadedKeyFile) - callback() - else - signUsingWindowsSDKTool exeToSign, path.resolve(__dirname, '..', 'certs', 'AtomDevTestSignKey.p12'), 'password', callback - - signUsingWindowsSDKTool = (exeToSign, keyFilePath, password, callback) -> - grunt.log.ok("Signing #{exeToSign}") - args = ['sign', '/v', '/p', password, '/f', keyFilePath, exeToSign] - spawn {cmd: 'C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.1A\\bin\\signtool.exe', args: args}, callback - - signUsingJanky = (exeToSign, callback) -> - spawn {cmd: 'signtool', args: [exeToSign]}, callback - - signWindowsExecutable = if process.env.JANKY_SIGNTOOL then signUsingJanky else signUsingWindowsSDK - grunt.registerTask 'codesign:exe', 'CodeSign Atom.exe and Update.exe', -> done = @async() spawn {cmd: 'taskkill', args: ['/F', '/IM', 'atom.exe']}, -> + cmd = process.env.JANKY_SIGNTOOL ? 'signtool' atomExePath = path.join(grunt.config.get('atom.shellAppDir'), 'atom.exe') - signWindowsExecutable atomExePath, (error) -> + spawn {cmd, args: [atomExePath]}, (error) -> return done(error) if error? updateExePath = path.resolve(__dirname, '..', 'node_modules', 'grunt-electron-installer', 'vendor', 'Update.exe') - signWindowsExecutable updateExePath, (error) -> done(error) + spawn {cmd, args: [updateExePath]}, (error) -> done(error) grunt.registerTask 'codesign:installer', 'CodeSign AtomSetup.exe', -> done = @async() + cmd = process.env.JANKY_SIGNTOOL ? 'signtool' atomSetupExePath = path.resolve(grunt.config.get('atom.buildDir'), 'installer', 'AtomSetup.exe') - signWindowsExecutable atomSetupExePath, (error) -> done(error) + spawn {cmd, args: [atomSetupExePath]}, (error) -> done(error) grunt.registerTask 'codesign:app', 'CodeSign Atom.app', -> done = @async() @@ -48,24 +26,14 @@ module.exports = (grunt) -> unlockKeychain (error) -> return done(error) if error? + cmd = 'codesign' args = ['--deep', '--force', '--verbose', '--sign', 'Developer ID Application: GitHub', grunt.config.get('atom.shellAppDir')] - spawn {cmd: 'codesign', args: args}, (error) -> done(error) + spawn {cmd, args}, (error) -> done(error) unlockKeychain = (callback) -> return callback() unless process.env.XCODE_KEYCHAIN + cmd = 'security' {XCODE_KEYCHAIN_PASSWORD, XCODE_KEYCHAIN} = process.env args = ['unlock-keychain', '-p', XCODE_KEYCHAIN_PASSWORD, XCODE_KEYCHAIN] - spawn {cmd: 'security', args: args}, (error) -> callback(error) - - downloadFile = (sourceUrl, targetPath, callback) -> - options = { - url: sourceUrl - headers: { - 'User-Agent': 'Atom Signing Key build task', - 'Accept': 'application/vnd.github.VERSION.raw' - } - } - request(options) - .pipe(fs.createWriteStream(targetPath)) - .on('finish', callback) + spawn {cmd, args}, (error) -> callback(error) From 95adf1616067a27387c72acae768a0d8d1df6063 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Fri, 8 Apr 2016 14:36:51 -0700 Subject: [PATCH 744/971] Signing support on Windows with P12 keys --- build/certs/AtomDevTestSignKey.p12 | Bin 0 -> 1765 bytes build/tasks/codesign-task.coffee | 49 +++++++++++++++++++++++------ 2 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 build/certs/AtomDevTestSignKey.p12 diff --git a/build/certs/AtomDevTestSignKey.p12 b/build/certs/AtomDevTestSignKey.p12 new file mode 100644 index 0000000000000000000000000000000000000000..a93e9a9f042a29411a05cdf832c99041fd6d6180 GIT binary patch literal 1765 zcmb7FX;c$e6n--kgF*JN2!|yK2+B4g2@((#0yaktvM6vs!xADON+3jBs7iveDWJuQ ziqXi9R0Tv-tY8rX9wVq&WDBAqu2rcDiWT~>Ew$(Pr{~U_x%a#8yLaZ!d}nY8@d1ip zxP&Mrk`|=1)5p{i6+|W><`N{tYzU|0670%s?L4xjU5^!8sZU|RO&n#=eOfsq7hlf{6-QGPd zN^vc!Deho{doNAf#RQ#~XIp7Y+8y#MRqlJ#@k~9(=3BFrSs$#) zd`02jljrXL(%+Ok-0#^`u^d}9vO^@U?$q;@+In8I=`m4T+c+6{px0}~J+FPmzwWKd zeqEGpwK$VJ=x>-|q7c1Z>Z@?RU+;Qj!l?Sy>E;50oa3e~tZE=*0IBDrJ#O-}|*FF1AK)**vT^=o;o>j|mW?snuqjixmXK+hj zN`w|G?ec{)iIhv?jK*-|9AqM^Vty+n$s*?M^*z01XKSLas@&W;l4t|sxH_ykg=>tq zJauamQN71Cm)ck|s}z;iEmyc*NWY9^=YRI)fgR7S$F-Y{lp@`H_OeBq0@|Ltn-k)* zY|gGPcyIM=vN>qU@9t3+^O`z4l|E8#wm-#l6fPLKK>k$+XUFRb%gxJb=dK=IM?({ReW?q|0{~WUz_};=g+EdmI z%~zF1YgcX0vs|$&wUuEpX4qnt?UI|rxP+=jW?p$3Js4y7v!yUb{fJz!cb_<+>%_rs zzZa!hvWnHpxx-E4f$5{RZ7Dv|P;{C|cVM%XBR%1#4nD zyfS9>7clzBqO!n&>r`+hasWA(R7OfFaA>eEdSwHW}%srI-n5nD~b$d(==!A6P~KbgNlkiZvGR`HWWlz`ai z_}KVpipM5Vj6fJG+KQVRs8gMAnj;Oz=}vU#5Ce4@lj=x=2ZPCAhTsdS44n2?McZ#x zWC^19(`8wFp(u7;EH6UD$Hm9LzByzDz{EKK7iXbjF=&~`>Fe^F`*2|UZnM}0-M1$` z-C>80Z8Tu`sQA51JQq}$U{aEztr3=Z>@;=3HY3}*5)~5z>H0WEuEc^xk87-OXUm39 zI-FX1Ap5$3z;Q4^(OY?A_vmN#4}^St@y>)4JqB})=foL*_Z7tlU$>lR8&*B`ST>;1 znWNM66Q}zziXi|gPQnv$HTY;IWCWQcpg6!IrXqw8f(NH+HQ$wNeG`1b_l=|>53PA0 ze!}tpPgkN9IIoAB_GDron={)vb6_Il>zTk8hQVQwf}aD$Ctgs4^jMjTB9x-aCA>ho zt*I++$>lTUbY-dRWN5h?=_unYJMFDp=aqUgXY}Lz{oV}QXGra6QO7P$M0ZbzfrI@} zx#8^F^6}#|{STkpTccWTF7c+|)+J_FG5*2W(K(vdXj>ugqjGf8CGMcvfboa1uCTo8cd_e`A@*-rK0Jqhg6_rrc^3ot+O=)Q RYtV|ry%{EHmBgtZ&Yvnyq?G^w literal 0 HcmV?d00001 diff --git a/build/tasks/codesign-task.coffee b/build/tasks/codesign-task.coffee index 2a061742b..a00e2d358 100644 --- a/build/tasks/codesign-task.coffee +++ b/build/tasks/codesign-task.coffee @@ -1,24 +1,46 @@ path = require 'path' +fs = require 'fs' +request = require 'request' module.exports = (grunt) -> {spawn} = require('./task-helpers')(grunt) + signUsingWindowsSDK = (exeToSign, callback) -> + {WIN_P12KEY_PASSWORD, WIN_P12KEY_URL} = process.env + if WIN_P12KEY_URL? + grunt.log.ok("Obtaining signing key") + downloadedKeyFile = path.resolve(__dirname, 'DownloadedSignKey.p12') + downloadFile WIN_P12KEY_URL, downloadedKeyFile, (done) -> + signUsingWindowsSDKTool exeToSign, downloadedKeyFile, WIN_P12KEY_PASSWORD, (done) -> + fs.unlinkSync(downloadedKeyFile) + callback() + else + signUsingWindowsSDKTool exeToSign, path.resolve(__dirname, '..', 'certs', 'AtomDevTestSignKey.p12'), 'password', callback + + signUsingWindowsSDKTool = (exeToSign, keyFilePath, password, callback) -> + grunt.log.ok("Signing #{exeToSign}") + args = ['sign', '/v', '/p', password, '/f', keyFilePath, exeToSign] + spawn {cmd: 'C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.1A\\bin\\signtool.exe', args: args}, callback + + signUsingJanky = (exeToSign, callback) -> + spawn {cmd: 'signtool', args: [exeToSign]}, callback + + signWindowsExecutable = if process.env.JANKY_SIGNTOOL then signUsingJanky else signUsingWindowsSDK + grunt.registerTask 'codesign:exe', 'CodeSign Atom.exe and Update.exe', -> done = @async() spawn {cmd: 'taskkill', args: ['/F', '/IM', 'atom.exe']}, -> - cmd = process.env.JANKY_SIGNTOOL ? 'signtool' atomExePath = path.join(grunt.config.get('atom.shellAppDir'), 'atom.exe') - spawn {cmd, args: [atomExePath]}, (error) -> + signWindowsExecutable atomExePath, (error) -> return done(error) if error? updateExePath = path.resolve(__dirname, '..', 'node_modules', 'grunt-electron-installer', 'vendor', 'Update.exe') - spawn {cmd, args: [updateExePath]}, (error) -> done(error) + signWindowsExecutable updateExePath, (error) -> done(error) grunt.registerTask 'codesign:installer', 'CodeSign AtomSetup.exe', -> done = @async() - cmd = process.env.JANKY_SIGNTOOL ? 'signtool' atomSetupExePath = path.resolve(grunt.config.get('atom.buildDir'), 'installer', 'AtomSetup.exe') - spawn {cmd, args: [atomSetupExePath]}, (error) -> done(error) + signWindowsExecutable atomSetupExePath, (error) -> done(error) grunt.registerTask 'codesign:app', 'CodeSign Atom.app', -> done = @async() @@ -26,14 +48,23 @@ module.exports = (grunt) -> unlockKeychain (error) -> return done(error) if error? - cmd = 'codesign' args = ['--deep', '--force', '--verbose', '--sign', 'Developer ID Application: GitHub', grunt.config.get('atom.shellAppDir')] - spawn {cmd, args}, (error) -> done(error) + spawn {cmd: 'codesign', args: args}, (error) -> done(error) unlockKeychain = (callback) -> return callback() unless process.env.XCODE_KEYCHAIN - cmd = 'security' {XCODE_KEYCHAIN_PASSWORD, XCODE_KEYCHAIN} = process.env args = ['unlock-keychain', '-p', XCODE_KEYCHAIN_PASSWORD, XCODE_KEYCHAIN] - spawn {cmd, args}, (error) -> callback(error) + spawn {cmd: 'security', args: args}, (error) -> callback(error) + + downloadFile = (sourceUrl, targetPath, callback) -> + options = { + url: sourceUrl + headers: { + 'User-Agent': 'Atom Signing Key build task', + 'Accept': 'application/vnd.github.VERSION.raw' } + } + request(options) + .pipe(fs.createWriteStream(targetPath)) + .on('finish', callback) From 77657aca0d19348efd56cd2240f066b267552092 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 11 Apr 2016 15:13:20 -0600 Subject: [PATCH 745/971] Fix linter error --- build/tasks/codesign-task.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/tasks/codesign-task.coffee b/build/tasks/codesign-task.coffee index a00e2d358..d084028a7 100644 --- a/build/tasks/codesign-task.coffee +++ b/build/tasks/codesign-task.coffee @@ -63,7 +63,8 @@ module.exports = (grunt) -> url: sourceUrl headers: { 'User-Agent': 'Atom Signing Key build task', - 'Accept': 'application/vnd.github.VERSION.raw' } + 'Accept': 'application/vnd.github.VERSION.raw' + } } request(options) .pipe(fs.createWriteStream(targetPath)) From 1721938057ee732583d26d66808564689fbb70b0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 11 Apr 2016 15:13:45 -0600 Subject: [PATCH 746/971] Use signtool from environment variable --- build/tasks/codesign-task.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/tasks/codesign-task.coffee b/build/tasks/codesign-task.coffee index d084028a7..559d41bbf 100644 --- a/build/tasks/codesign-task.coffee +++ b/build/tasks/codesign-task.coffee @@ -23,7 +23,7 @@ module.exports = (grunt) -> spawn {cmd: 'C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.1A\\bin\\signtool.exe', args: args}, callback signUsingJanky = (exeToSign, callback) -> - spawn {cmd: 'signtool', args: [exeToSign]}, callback + spawn {cmd: process.env.JANKY_SIGNTOOL, args: [exeToSign]}, callback signWindowsExecutable = if process.env.JANKY_SIGNTOOL then signUsingJanky else signUsingWindowsSDK From 0f5841376c6d179c20f4c94d34f2d6291fab0ccc Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 11 Apr 2016 21:00:16 -0700 Subject: [PATCH 747/971] :arrow_up: tree view (cherry picked from commit a3b844ee003f6af6739a515ae34338883a211493) --- changes.md | 0 changes2.md | 397 ++++++++++++++++++++++++++++++++++++++++++++++++ changes3.md | 414 +++++++++++++++++++++++++++++++++++++++++++++++++++ fsck.txt | 84 +++++++++++ package.json | 1 + 5 files changed, 896 insertions(+) create mode 100644 changes.md create mode 100644 changes2.md create mode 100644 changes3.md create mode 100644 fsck.txt diff --git a/changes.md b/changes.md new file mode 100644 index 000000000..e69de29bb diff --git a/changes2.md b/changes2.md new file mode 100644 index 000000000..e8224e628 --- /dev/null +++ b/changes2.md @@ -0,0 +1,397 @@ +### [Atom Core](https://github.com/atom/atom) + +v1.6.0...v1.7.0-beta0 + +* [atom/atom#10747 - Clarify Windows build docs for VS2015 etc.](https://github.com/atom/atom/pull/10747) +* [atom/atom#10743 - Don't cascade on reload](https://github.com/atom/atom/pull/10743) +* [atom/atom#9198 - permit any whole number for tabLength](https://github.com/atom/atom/pull/9198) +* [atom/atom#10758 - Fix status in subdir](https://github.com/atom/atom/pull/10758) +* [atom/atom#10768 - Temporarily disable deserializers & viewProviders metadata fields](https://github.com/atom/atom/pull/10768) +* [atom/atom#10764 - Let packages define deserializers & view providers as main module methods](https://github.com/atom/atom/pull/10764) +* [atom/atom#10778 - Fix minor typo in CONTRIBUTING.md](https://github.com/atom/atom/pull/10778) +* [atom/atom#10789 - Upgrade autocomplete-plus to use new text-buffer API](https://github.com/atom/atom/pull/10789) +* [atom/atom#10797 - Fix status with multiple paths](https://github.com/atom/atom/pull/10797) +* [atom/atom#10797 - Fix status with multiple paths](https://github.com/atom/atom/pull/10797) +* [atom/atom#10792 - Fix TextEditorPresenter spec timeout.](https://github.com/atom/atom/pull/10792) +* [atom/atom#10803 - Fix typo in function call](https://github.com/atom/atom/pull/10803) +* [atom/atom#10828 - Fix another TextEditorPresenter spec race](https://github.com/atom/atom/pull/10828) +* [atom/atom#10827 - Port repository subdirectory status fixes to async repo](https://github.com/atom/atom/pull/10827) +* [atom/atom#10827 - Port repository subdirectory status fixes to async repo](https://github.com/atom/atom/pull/10827) +* [atom/atom#10838 - Log output for core specs](https://github.com/atom/atom/pull/10838) +* [atom/atom#10818 - Register Atom for file associations in Windows](https://github.com/atom/atom/pull/10818) +* [atom/atom#10605 - Periodically save state and store in indexedDB](https://github.com/atom/atom/pull/10605) +* [atom/atom#9627 - Update to Electron 0.36](https://github.com/atom/atom/pull/9627) +* [atom/atom#10835 - Add TextEditor.prototype.cursorsForScreenRowRange](https://github.com/atom/atom/pull/10835) +* [atom/atom#10795 - Remove unused Core Team Project Management labels](https://github.com/atom/atom/pull/10795) +* [atom/atom#10858 - Update grunt-electron-installer to latest version](https://github.com/atom/atom/pull/10858) +* [atom/atom#10846 - Add core setting for pending tabs configuration](https://github.com/atom/atom/pull/10846) +* [atom/atom#10846 - Add core setting for pending tabs configuration](https://github.com/atom/atom/pull/10846) +* [atom/atom#10873 - Avoid emitting config events while loading packages](https://github.com/atom/atom/pull/10873) +* [atom/atom#10872 - Terminate pending state for opened file if pending option is false](https://github.com/atom/atom/pull/10872) +* [atom/atom#10872 - Terminate pending state for opened file if pending option is false](https://github.com/atom/atom/pull/10872) +* [atom/atom#10863 - Compare markers instead of ranges in Selection](https://github.com/atom/atom/pull/10863) +* [atom/atom#10874 - Avoid Windows 260-character path limits in script\clean](https://github.com/atom/atom/pull/10874) +* [atom/atom#10861 - Compute line foldability lazily](https://github.com/atom/atom/pull/10861) +* [atom/atom#10843 - Bump packages to use async git](https://github.com/atom/atom/pull/10843) +* [atom/atom#10886 - Update Atom build directory in build instructions](https://github.com/atom/atom/pull/10886) +* [atom/atom#10885 - Cache regexes in LanguageMode.prototype.getRegexForProperty](https://github.com/atom/atom/pull/10885) +* [atom/atom#10888 - Load packages before deserializing state](https://github.com/atom/atom/pull/10888) +* [atom/atom#10870 - Add Issue template and extra version info](https://github.com/atom/atom/pull/10870) +* [atom/atom#10878 - Allow pasting white space when `autoIndentOnPaste` is enabled](https://github.com/atom/atom/pull/10878) +* [atom/atom#10895 - Fix some spec issues](https://github.com/atom/atom/pull/10895) +* [atom/atom#10901 - remove Open Roadmap menu item, fixes #10884](https://github.com/atom/atom/pull/10901) +* [atom/atom#10898 - Pass the notification manager when splitting panes](https://github.com/atom/atom/pull/10898) +* [atom/atom#10927 - Upgrade electron to fix command-backtick bug](https://github.com/atom/atom/pull/10927) +* [atom/atom#10926 - Fix case-preserving path relativization](https://github.com/atom/atom/pull/10926) +* [atom/atom#10926 - Fix case-preserving path relativization](https://github.com/atom/atom/pull/10926) +* [atom/atom#10921 - Add support for keybindings with keyup keystrokes](https://github.com/atom/atom/pull/10921) +* [atom/atom#10841 - Add the -a, --add CLI option](https://github.com/atom/atom/pull/10841) +* [atom/atom#10933 - Fix block decorations spec in text editor presenter](https://github.com/atom/atom/pull/10933) +* [atom/atom#10925 - Faster state serialization](https://github.com/atom/atom/pull/10925) +* [atom/atom#10967 - Fix a inconsistent getLineCount() use](https://github.com/atom/atom/pull/10967) +* [atom/atom#10967 - Fix a inconsistent getLineCount() use](https://github.com/atom/atom/pull/10967) +* [atom/atom#10975 - Update nodegit](https://github.com/atom/atom/pull/10975) +* [atom/atom#10975 - Update nodegit](https://github.com/atom/atom/pull/10975) +* [atom/atom#10326 - Fix Windows installer path update woes](https://github.com/atom/atom/pull/10326) +* [atom/atom#10959 - Refactor pending state to live in pane instead of items](https://github.com/atom/atom/pull/10959) +* [atom/atom#10959 - Refactor pending state to live in pane instead of items](https://github.com/atom/atom/pull/10959) +* [atom/atom#10984 - [WIP] experiment with circle ci](https://github.com/atom/atom/pull/10984) +* [atom/atom#10737 - Add MRU tab switching functionality](https://github.com/atom/atom/pull/10737) +* [atom/atom#11001 - onDidTerminatePendingState ➡︎ onItemDidTerminatePendingState](https://github.com/atom/atom/pull/11001) +* [atom/atom#11001 - onDidTerminatePendingState ➡︎ onItemDidTerminatePendingState](https://github.com/atom/atom/pull/11001) +* [atom/atom#10972 - Update build scripts to use the new railcar branching model](https://github.com/atom/atom/pull/10972) +* [atom/atom#10972 - Update build scripts to use the new railcar branching model](https://github.com/atom/atom/pull/10972) +* [atom/atom#11006 - Move spec from tabs package](https://github.com/atom/atom/pull/11006) +* [atom/atom#10851 - Registry for TextEditors](https://github.com/atom/atom/pull/10851) +* [atom/atom#11010 - Always strip git+ prefix and .git suffix from package repository URLs](https://github.com/atom/atom/pull/11010) +* [atom/atom#11011 - Autoscroll after consolidating selections](https://github.com/atom/atom/pull/11011) +* [atom/atom#11008 - Add documentation for order key in config.](https://github.com/atom/atom/pull/11008) +* [atom/atom#11009 - Don't destroy pane if replacing last pending item](https://github.com/atom/atom/pull/11009) +* [atom/atom#11009 - Don't destroy pane if replacing last pending item](https://github.com/atom/atom/pull/11009) +* [atom/atom#10675 - Expose application updater lifecycle events to packages](https://github.com/atom/atom/pull/10675) +* [atom/atom#11022 - Windows git fixes](https://github.com/atom/atom/pull/11022) +* [atom/atom#11022 - Windows git fixes](https://github.com/atom/atom/pull/11022) +* [atom/atom#11051 - Add new item before destroying pending item](https://github.com/atom/atom/pull/11051) +* [atom/atom#11051 - Add new item before destroying pending item](https://github.com/atom/atom/pull/11051) +* [atom/atom#11036 - Skip deleted directories when restoring application windows](https://github.com/atom/atom/pull/11036) +* [atom/atom#11063 - Show tooltip immediately if the tooltip trigger is manual](https://github.com/atom/atom/pull/11063) +* [atom/atom#10511 - Use ELECTRON_RUN_AS_NODE Variable Key](https://github.com/atom/atom/pull/10511) +* [atom/atom#10955 - TextEditor customization](https://github.com/atom/atom/pull/10955) +* [atom/atom#11065 - Default to auto height being true.](https://github.com/atom/atom/pull/11065) +* [atom/atom#11053 - Ensure atom.cmd --wait correctly waits in Windows cmd & powershell](https://github.com/atom/atom/pull/11053) +* [atom/atom#11060 - Serialize MarkerLayers only on quit](https://github.com/atom/atom/pull/11060) +* [atom/atom#11057 - Move Pane::addItem 'pending' option to options object](https://github.com/atom/atom/pull/11057) +* [atom/atom#11057 - Move Pane::addItem 'pending' option to options object](https://github.com/atom/atom/pull/11057) +* [atom/atom#11089 - Update nodegit](https://github.com/atom/atom/pull/11089) +* [atom/atom#11089 - Update nodegit](https://github.com/atom/atom/pull/11089) +* [atom/atom#11099 - Add zero to hexadecimal numbers below F (16)](https://github.com/atom/atom/pull/11099) +* [atom/atom#11101 - Emit status changes when anything changes](https://github.com/atom/atom/pull/11101) +* [atom/atom#11101 - Emit status changes when anything changes](https://github.com/atom/atom/pull/11101) +* [atom/atom#11077 - Scroll to cursor on unfold all](https://github.com/atom/atom/pull/11077) +* [atom/atom#11103 - Make cli atom --wait work on Cygwin](https://github.com/atom/atom/pull/11103) +* [atom/atom#11115 - Update nodegit](https://github.com/atom/atom/pull/11115) +* [atom/atom#11115 - Update nodegit](https://github.com/atom/atom/pull/11115) +* [atom/atom#11111 - Default the options parameter to an empty object](https://github.com/atom/atom/pull/11111) +* [atom/atom#11127 - Bump markdown-preview@v0.158.0](https://github.com/atom/atom/pull/11127) +* [atom/atom#8793 - squirrel-update test on desktop shortcut groups too many assertions](https://github.com/atom/atom/pull/8793) +* [atom/atom#11078 - Add TextEditors to the registry only when opting in](https://github.com/atom/atom/pull/11078) +* [atom/atom#11054 - Patch Environment On OSX And Allow A Different Environment Per Window](https://github.com/atom/atom/pull/11054) +* [atom/atom#11153 - Fix node env](https://github.com/atom/atom/pull/11153) +* [atom/atom#11162 - BufferedProcess: search only new data for new lines rather than entire buffer, take 2](https://github.com/atom/atom/pull/11162) +* [atom/atom#11166 - Note where GitRepositoryAsync deviates from its synchronous predecessor](https://github.com/atom/atom/pull/11166) + +### [one-dark-ui](https://github.com/atom/one-dark-ui) + +v1.1.9...v1.2.0 + +* [atom/one-dark-ui#113 - Specify config schema in package.json](https://github.com/atom/one-dark-ui/pull/113) +* [atom/one-dark-ui#121 - Fix typo in comment for ui-variables.less](https://github.com/atom/one-dark-ui/pull/121) + +### [one-light-ui](https://github.com/atom/one-light-ui) + +v1.1.9...v1.2.0 + +* [atom/one-light-ui#48 - Specify config schema in package.json](https://github.com/atom/one-light-ui/pull/48) + +### [about](https://github.com/atom/about) + +v1.3.0...v1.4.1 + +* [atom/about#6 - Specify deserializer in package.json](https://github.com/atom/about/pull/6) +* [atom/about#6 - Specify deserializer in package.json](https://github.com/atom/about/pull/6) +* [atom/about#13 - Specify deserializer method in package.json, the new way](https://github.com/atom/about/pull/13) +* [atom/about#12 - Show update information on the about page](https://github.com/atom/about/pull/12) +* [atom/about#15 - Move away from deprecated Electron require syntax](https://github.com/atom/about/pull/15) + +### [archive-view](https://github.com/atom/archive-view) + +v0.61.0...v0.61.1 + +* [atom/archive-view#32 - Specify deserializer in package.json](https://github.com/atom/archive-view/pull/32) + +### [autocomplete-plus](https://github.com/atom/autocomplete-plus) + +v2.25.0...v2.29.1 + +* [atom/autocomplete-plus#612 - bugfix: auto indentation after suggestion confirm](https://github.com/atom/autocomplete-plus/pull/612) +* [atom/autocomplete-plus#641 - Stop flickering when adjusting margins](https://github.com/atom/autocomplete-plus/pull/641) +* [atom/autocomplete-plus#637 - Move config schema to package.json](https://github.com/atom/autocomplete-plus/pull/637) +* [atom/autocomplete-plus#659 - Batch autocompletion show/hide](https://github.com/atom/autocomplete-plus/pull/659) +* [atom/autocomplete-plus#675 - :art: Clean up formatting of fileBlacklist setting description](https://github.com/atom/autocomplete-plus/pull/675) +* [atom/autocomplete-plus#672 - Redesign SymbolStore and SymbolProvider](https://github.com/atom/autocomplete-plus/pull/672) +* [atom/autocomplete-plus#667 - Add unicode support for \w regexps](https://github.com/atom/autocomplete-plus/pull/667) +* [atom/autocomplete-plus#681 - Fix maximum call stack size exceeded error](https://github.com/atom/autocomplete-plus/pull/681) + +### [autosave](https://github.com/atom/autosave) + +v0.23.0...v0.23.1 + +* [atom/autosave#56 - Move config schema to package.json](https://github.com/atom/autosave/pull/56) + +### [bracket-matcher](https://github.com/atom/bracket-matcher) + +v0.79.0...v0.81.0 + +* [atom/bracket-matcher#196 - Fix spelling in settings](https://github.com/atom/bracket-matcher/pull/196) +* [atom/bracket-matcher#197 - Move config schema to package.json](https://github.com/atom/bracket-matcher/pull/197) +* [atom/bracket-matcher#212 - Make updating matches faster for multi-cursor editing](https://github.com/atom/bracket-matcher/pull/212) +* [atom/bracket-matcher#219 - Clean up view subscriptions when editor is destroyed](https://github.com/atom/bracket-matcher/pull/219) +* [atom/bracket-matcher#221 - Enable bracket matching for elixir](https://github.com/atom/bracket-matcher/pull/221) + +### [deprecation-cop](https://github.com/atom/deprecation-cop) + +v0.54.0...v0.54.1 + +* [atom/deprecation-cop#65 - Move deserializer into package.json](https://github.com/atom/deprecation-cop/pull/65) +* [atom/deprecation-cop#65 - Move deserializer into package.json](https://github.com/atom/deprecation-cop/pull/65) +* [atom/deprecation-cop#67 - Specify deserializer method in package.json](https://github.com/atom/deprecation-cop/pull/67) + +### [fuzzy-finder](https://github.com/atom/fuzzy-finder) + +v0.94.0...v1.0.3 + +* [atom/fuzzy-finder#160 - Move config schema to package.json](https://github.com/atom/fuzzy-finder/pull/160) +* [atom/fuzzy-finder#167 - Async git](https://github.com/atom/fuzzy-finder/pull/167) +* [atom/fuzzy-finder#174 - Fix spec race](https://github.com/atom/fuzzy-finder/pull/174) +* [atom/fuzzy-finder#178 - Handle symlink project paths](https://github.com/atom/fuzzy-finder/pull/178) +* [atom/fuzzy-finder#180 - Return project paths correctly if last-opened path is not in project](https://github.com/atom/fuzzy-finder/pull/180) + +### [git-diff](https://github.com/atom/git-diff) + +v0.57.0...v1.0.1 + +* [atom/git-diff#81 - Move config schema to package.json](https://github.com/atom/git-diff/pull/81) +* [atom/git-diff#82 - Async git](https://github.com/atom/git-diff/pull/82) +* [atom/git-diff#95 - Catch errors from new files.](https://github.com/atom/git-diff/pull/95) + +### [grammar-selector](https://github.com/atom/grammar-selector) + +v0.48.0...v0.48.1 + +* [atom/grammar-selector#25 - Add description for config setting](https://github.com/atom/grammar-selector/pull/25) +* [atom/grammar-selector#29 - Move config schema to package.json](https://github.com/atom/grammar-selector/pull/29) + +### [image-view](https://github.com/atom/image-view) + +v0.56.0...v0.57.0 + +* [atom/image-view#40 - Zoom to fit](https://github.com/atom/image-view/pull/40) + +### [incompatible-packages](https://github.com/atom/incompatible-packages) + +v0.25.0...v0.25.1 + +* [atom/incompatible-packages#11 - Move deserializer to package.json](https://github.com/atom/incompatible-packages/pull/11) +* [atom/incompatible-packages#11 - Move deserializer to package.json](https://github.com/atom/incompatible-packages/pull/11) +* [atom/incompatible-packages#12 - Specify deserializer method in package.json](https://github.com/atom/incompatible-packages/pull/12) + +### [keybinding-resolver](https://github.com/atom/keybinding-resolver) + +v0.33.0...v0.35.0 + +* [atom/keybinding-resolver#23 - Update coffeelint support](https://github.com/atom/keybinding-resolver/pull/23) +* [atom/keybinding-resolver#37 - Show keyup events that match a binding](https://github.com/atom/keybinding-resolver/pull/37) + +### [line-ending-selector](https://github.com/atom/line-ending-selector) + +v0.3.0...v0.3.1 + +* [atom/line-ending-selector#17 - Move config schema to package.json](https://github.com/atom/line-ending-selector/pull/17) + +### [link](https://github.com/atom/link) + +v0.31.0...v0.31.1 + +* [atom/link#14 - Move away from deprecated Electron require syntax](https://github.com/atom/link/pull/14) + +### [markdown-preview](https://github.com/atom/markdown-preview) + +v0.157.2...v0.158.0 + +* [atom/markdown-preview#349 - Use new package.json fields (configSchema and deserializers)](https://github.com/atom/markdown-preview/pull/349) +* [atom/markdown-preview#349 - Use new package.json fields (configSchema and deserializers)](https://github.com/atom/markdown-preview/pull/349) +* [atom/markdown-preview#367 - Use new package.json fields to allow deferred loading](https://github.com/atom/markdown-preview/pull/367) +* [atom/markdown-preview#335 - Use GitHub style when "Save as HTML"](https://github.com/atom/markdown-preview/pull/335) + +### [notifications](https://github.com/atom/notifications) + +v0.62.1...v0.63.1 + +* [atom/notifications#105 - Move config schema to package.json](https://github.com/atom/notifications/pull/105) +* [atom/notifications#111 - Use https://git.io insead of http](https://github.com/atom/notifications/pull/111) +* [atom/notifications#113 - replace ATOM_HOME in issue title with generic placeholder](https://github.com/atom/notifications/pull/113) +* [atom/notifications#114 - Use bit.ly instead of git.io.](https://github.com/atom/notifications/pull/114) +* [atom/notifications#115 - URL shortening, take 2](https://github.com/atom/notifications/pull/115) + +### [open-on-github](https://github.com/atom/open-on-github) + +v0.41.0...v1.0.1 + +* [atom/open-on-github#59 - Move config schema to package.json](https://github.com/atom/open-on-github/pull/59) +* [atom/open-on-github#60 - Async git](https://github.com/atom/open-on-github/pull/60) +* [atom/open-on-github#66 - Move away from deprecated Electron require syntax](https://github.com/atom/open-on-github/pull/66) + +### [package-generator](https://github.com/atom/package-generator) + +v0.41.0...v1.0.0 + +* [atom/package-generator#37 - Move config schema to package.json](https://github.com/atom/package-generator/pull/37) +* [atom/package-generator#36 - Support JS package generation](https://github.com/atom/package-generator/pull/36) + +### [settings-view](https://github.com/atom/settings-view) + +v0.232.3...v0.235.0 + +* [atom/settings-view#731 - Specify deserializer in package.json](https://github.com/atom/settings-view/pull/731) +* [atom/settings-view#749 - Move away from deprecated Electron require syntax](https://github.com/atom/settings-view/pull/749) +* [atom/settings-view#750 - Another require fix for remote](https://github.com/atom/settings-view/pull/750) +* [atom/settings-view#743 - Display and manage Git-based packages](https://github.com/atom/settings-view/pull/743) +* [atom/settings-view#748 - Add defaults on focus](https://github.com/atom/settings-view/pull/748) +* [atom/settings-view#736 - Add collapsable section for option groups](https://github.com/atom/settings-view/pull/736) + +### [spell-check](https://github.com/atom/spell-check) + +v0.65.0...v0.67.0 + +* [atom/spell-check#103 - Update README.md](https://github.com/atom/spell-check/pull/103) +* [atom/spell-check#33 - Add feature: toggle on/off](https://github.com/atom/spell-check/pull/33) +* [atom/spell-check#108 - subscriptionsOfCommands -> commandSubscription](https://github.com/atom/spell-check/pull/108) +* [atom/spell-check#112 - Move config schema to package.json](https://github.com/atom/spell-check/pull/112) +* [atom/spell-check#114 - updates spellchecker to use system language](https://github.com/atom/spell-check/pull/114) + +### [status-bar](https://github.com/atom/status-bar) + +v0.83.0...v1.2.0 + +* [atom/status-bar#121 - Move config schema to package.json](https://github.com/atom/status-bar/pull/121) +* [atom/status-bar#114 - Async git](https://github.com/atom/status-bar/pull/114) +* [atom/status-bar#122 - Make updating info faster for multi-cursor edits](https://github.com/atom/status-bar/pull/122) +* [atom/status-bar#129 - Hide diff stats for new files](https://github.com/atom/status-bar/pull/129) +* [atom/status-bar#131 - Fix error with no active item](https://github.com/atom/status-bar/pull/131) +* [atom/status-bar#133 - Move to the footer](https://github.com/atom/status-bar/pull/133) + +### [styleguide](https://github.com/atom/styleguide) + +v0.45.1...v0.45.2 + +* [atom/styleguide#34 - Specify deserializer in package.json](https://github.com/atom/styleguide/pull/34) + +### [symbols-view](https://github.com/atom/symbols-view) + +v0.110.1...v0.112.0 + +* [atom/symbols-view#147 - Add support for ES2015 static methods](https://github.com/atom/symbols-view/pull/147) +* [atom/symbols-view#151 - Specify config schema in package.json](https://github.com/atom/symbols-view/pull/151) +* [atom/symbols-view#157 - Add es7 async functions to ctags](https://github.com/atom/symbols-view/pull/157) + +### [tabs](https://github.com/atom/tabs) + +v0.91.3...v0.92.0 + +* [atom/tabs#134 - Add "open in new window" option to tabs context menu](https://github.com/atom/tabs/pull/134) + +### [timecop](https://github.com/atom/timecop) + +v0.33.0...v0.33.1 + +* [atom/timecop#15 - Specify deserializer in package.json](https://github.com/atom/timecop/pull/15) + +### [welcome](https://github.com/atom/welcome) + +v0.33.0...v0.34.0 + +* [atom/welcome#45 - Move config schema to package.json](https://github.com/atom/welcome/pull/45) +* [atom/welcome#47 - Change menu names for different platforms](https://github.com/atom/welcome/pull/47) + +### [whitespace](https://github.com/atom/whitespace) + +v0.32.1...v0.32.2 + +* [atom/whitespace#107 - Move config schema to package.json](https://github.com/atom/whitespace/pull/107) + +### [language-clojure](https://github.com/atom/language-clojure) + +v0.19.1...v0.20.0 + +* [atom/language-clojure#39 - Fix tokenization of sexp and map nested at beginning of another sexp](https://github.com/atom/language-clojure/pull/39) + +### [language-coffee-script](https://github.com/atom/language-coffee-script) + +v0.46.0...v0.46.1 + +* [atom/language-coffee-script#85 - Check for word boundaries when attempting to auto-indent](https://github.com/atom/language-coffee-script/pull/85) + +### [language-csharp](https://github.com/atom/language-csharp) + +v0.11.0...v0.12.0 + +* [atom/language-csharp#53 - Make nameof a keyword](https://github.com/atom/language-csharp/pull/53) +* [atom/language-csharp#54 - Make C# 6's when a keyword](https://github.com/atom/language-csharp/pull/54) + +### [language-gfm](https://github.com/atom/language-gfm) + +v0.84.0...v0.85.0 + +* [atom/language-gfm#140 - Highlight HTML entities inside bold, italic, and strikethrough text](https://github.com/atom/language-gfm/pull/140) + +### [language-html](https://github.com/atom/language-html) + +v0.44.0...v0.44.1 + +* [atom/language-html#108 - Fixes #102 - ng-template highlighting for script tags](https://github.com/atom/language-html/pull/108) + +### [language-json](https://github.com/atom/language-json) + +v0.17.4...v0.17.6 + +* [atom/language-json#42 - Add .jsonld file support](https://github.com/atom/language-json/pull/42) +* [atom/language-json#43 - add composer.lock files](https://github.com/atom/language-json/pull/43) +* [atom/language-json#44 - Add .tern-project and .tern-config to file types](https://github.com/atom/language-json/pull/44) + +### [language-ruby](https://github.com/atom/language-ruby) + +v0.68.0...v0.68.3 + +* [atom/language-ruby#135 - Adds cr (Crystal lang) to fileTypes for Ruby grammar](https://github.com/atom/language-ruby/pull/135) +* [atom/language-ruby#137 - Changes dop and do order priority](https://github.com/atom/language-ruby/pull/137) +* [atom/language-ruby#136 - Fixes for tokenization of Kernel methods ending in ? or !](https://github.com/atom/language-ruby/pull/136) +* [atom/language-ruby#140 - Revert do/dop order change](https://github.com/atom/language-ruby/pull/140) + +### [language-sass](https://github.com/atom/language-sass) + +v0.45.0...v0.46.0 + +* [atom/language-sass#101 - Add individual border-radius properties](https://github.com/atom/language-sass/pull/101) + +### [language-text](https://github.com/atom/language-text) + +v0.7.0...v0.7.1 + +* [atom/language-text#5 - Add travis.yml](https://github.com/atom/language-text/pull/5) +* [atom/language-text#6 - Update legal date to 2016](https://github.com/atom/language-text/pull/6) + +### [language-xml](https://github.com/atom/language-xml) + +v0.34.2...v0.34.4 + +* [atom/language-xml#43 - Fix incorrect highlighting for empty element](https://github.com/atom/language-xml/pull/43) diff --git a/changes3.md b/changes3.md new file mode 100644 index 000000000..60d70731a --- /dev/null +++ b/changes3.md @@ -0,0 +1,414 @@ +### Notable Changes + +* Crash Recovery +* Most Recently Used Tab Switching +* Windows Improvements +* Environment Patching on OS X +* Electron Update + +### [Atom Core](https://github.com/atom/atom) + +v1.6.0-beta8...v1.7.0-beta0 + +* [atom/atom#10747 - Clarify Windows build docs for VS2015 etc.](https://github.com/atom/atom/pull/10747) +* [atom/atom#10743 - Don't cascade on reload](https://github.com/atom/atom/pull/10743) +* [atom/atom#9198 - permit any whole number for tabLength](https://github.com/atom/atom/pull/9198) +* [atom/atom#10758 - Fix status in subdir](https://github.com/atom/atom/pull/10758) +* [atom/atom#10768 - Temporarily disable deserializers & viewProviders metadata fields](https://github.com/atom/atom/pull/10768) +* [atom/atom#10764 - Let packages define deserializers & view providers as main module methods](https://github.com/atom/atom/pull/10764) +* [atom/atom#10778 - Fix minor typo in CONTRIBUTING.md](https://github.com/atom/atom/pull/10778) +* [atom/atom#10789 - Upgrade autocomplete-plus to use new text-buffer API](https://github.com/atom/atom/pull/10789) +* [atom/atom#10797 - Fix status with multiple paths](https://github.com/atom/atom/pull/10797) +* [atom/atom#10797 - Fix status with multiple paths](https://github.com/atom/atom/pull/10797) +* [atom/atom#10792 - Fix TextEditorPresenter spec timeout.](https://github.com/atom/atom/pull/10792) +* [atom/atom#10803 - Fix typo in function call](https://github.com/atom/atom/pull/10803) +* [atom/atom#10828 - Fix another TextEditorPresenter spec race](https://github.com/atom/atom/pull/10828) +* [atom/atom#10827 - Port repository subdirectory status fixes to async repo](https://github.com/atom/atom/pull/10827) +* [atom/atom#10827 - Port repository subdirectory status fixes to async repo](https://github.com/atom/atom/pull/10827) +* [atom/atom#10838 - Log output for core specs](https://github.com/atom/atom/pull/10838) +* [atom/atom#10818 - Register Atom for file associations in Windows](https://github.com/atom/atom/pull/10818) +* [atom/atom#10605 - Periodically save state and store in indexedDB](https://github.com/atom/atom/pull/10605) +* [atom/atom#9627 - Update to Electron 0.36](https://github.com/atom/atom/pull/9627) +* [atom/atom#10835 - Add TextEditor.prototype.cursorsForScreenRowRange](https://github.com/atom/atom/pull/10835) +* [atom/atom#10795 - Remove unused Core Team Project Management labels](https://github.com/atom/atom/pull/10795) +* [atom/atom#10858 - Update grunt-electron-installer to latest version](https://github.com/atom/atom/pull/10858) +* [atom/atom#10846 - Add core setting for pending tabs configuration](https://github.com/atom/atom/pull/10846) +* [atom/atom#10846 - Add core setting for pending tabs configuration](https://github.com/atom/atom/pull/10846) +* [atom/atom#10873 - Avoid emitting config events while loading packages](https://github.com/atom/atom/pull/10873) +* [atom/atom#10872 - Terminate pending state for opened file if pending option is false](https://github.com/atom/atom/pull/10872) +* [atom/atom#10872 - Terminate pending state for opened file if pending option is false](https://github.com/atom/atom/pull/10872) +* [atom/atom#10863 - Compare markers instead of ranges in Selection](https://github.com/atom/atom/pull/10863) +* [atom/atom#10874 - Avoid Windows 260-character path limits in script\clean](https://github.com/atom/atom/pull/10874) +* [atom/atom#10861 - Compute line foldability lazily](https://github.com/atom/atom/pull/10861) +* [atom/atom#10843 - Bump packages to use async git](https://github.com/atom/atom/pull/10843) +* [atom/atom#10886 - Update Atom build directory in build instructions](https://github.com/atom/atom/pull/10886) +* [atom/atom#10885 - Cache regexes in LanguageMode.prototype.getRegexForProperty](https://github.com/atom/atom/pull/10885) +* [atom/atom#10888 - Load packages before deserializing state](https://github.com/atom/atom/pull/10888) +* [atom/atom#10870 - Add Issue template and extra version info](https://github.com/atom/atom/pull/10870) +* [atom/atom#10878 - Allow pasting white space when `autoIndentOnPaste` is enabled](https://github.com/atom/atom/pull/10878) +* [atom/atom#10895 - Fix some spec issues](https://github.com/atom/atom/pull/10895) +* [atom/atom#10901 - remove Open Roadmap menu item, fixes #10884](https://github.com/atom/atom/pull/10901) +* [atom/atom#10898 - Pass the notification manager when splitting panes](https://github.com/atom/atom/pull/10898) +* [atom/atom#10927 - Upgrade electron to fix command-backtick bug](https://github.com/atom/atom/pull/10927) +* [atom/atom#10926 - Fix case-preserving path relativization](https://github.com/atom/atom/pull/10926) +* [atom/atom#10926 - Fix case-preserving path relativization](https://github.com/atom/atom/pull/10926) +* [atom/atom#10921 - Add support for keybindings with keyup keystrokes](https://github.com/atom/atom/pull/10921) +* [atom/atom#10841 - Add the -a, --add CLI option](https://github.com/atom/atom/pull/10841) +* [atom/atom#10933 - Fix block decorations spec in text editor presenter](https://github.com/atom/atom/pull/10933) +* [atom/atom#10925 - Faster state serialization](https://github.com/atom/atom/pull/10925) +* [atom/atom#10967 - Fix a inconsistent getLineCount() use](https://github.com/atom/atom/pull/10967) +* [atom/atom#10967 - Fix a inconsistent getLineCount() use](https://github.com/atom/atom/pull/10967) +* [atom/atom#10975 - Update nodegit](https://github.com/atom/atom/pull/10975) +* [atom/atom#10975 - Update nodegit](https://github.com/atom/atom/pull/10975) +* [atom/atom#10326 - Fix Windows installer path update woes](https://github.com/atom/atom/pull/10326) +* [atom/atom#10959 - Refactor pending state to live in pane instead of items](https://github.com/atom/atom/pull/10959) +* [atom/atom#10959 - Refactor pending state to live in pane instead of items](https://github.com/atom/atom/pull/10959) +* [atom/atom#10984 - [WIP] experiment with circle ci](https://github.com/atom/atom/pull/10984) +* [atom/atom#10737 - Add MRU tab switching functionality](https://github.com/atom/atom/pull/10737) +* [atom/atom#11001 - onDidTerminatePendingState ➡︎ onItemDidTerminatePendingState](https://github.com/atom/atom/pull/11001) +* [atom/atom#11001 - onDidTerminatePendingState ➡︎ onItemDidTerminatePendingState](https://github.com/atom/atom/pull/11001) +* [atom/atom#10972 - Update build scripts to use the new railcar branching model](https://github.com/atom/atom/pull/10972) +* [atom/atom#10972 - Update build scripts to use the new railcar branching model](https://github.com/atom/atom/pull/10972) +* [atom/atom#11006 - Move spec from tabs package](https://github.com/atom/atom/pull/11006) +* [atom/atom#10851 - Registry for TextEditors](https://github.com/atom/atom/pull/10851) +* [atom/atom#11010 - Always strip git+ prefix and .git suffix from package repository URLs](https://github.com/atom/atom/pull/11010) +* [atom/atom#11011 - Autoscroll after consolidating selections](https://github.com/atom/atom/pull/11011) +* [atom/atom#11008 - Add documentation for order key in config.](https://github.com/atom/atom/pull/11008) +* [atom/atom#11009 - Don't destroy pane if replacing last pending item](https://github.com/atom/atom/pull/11009) +* [atom/atom#11009 - Don't destroy pane if replacing last pending item](https://github.com/atom/atom/pull/11009) +* [atom/atom#10675 - Expose application updater lifecycle events to packages](https://github.com/atom/atom/pull/10675) +* [atom/atom#11022 - Windows git fixes](https://github.com/atom/atom/pull/11022) +* [atom/atom#11022 - Windows git fixes](https://github.com/atom/atom/pull/11022) +* [atom/atom#11051 - Add new item before destroying pending item](https://github.com/atom/atom/pull/11051) +* [atom/atom#11051 - Add new item before destroying pending item](https://github.com/atom/atom/pull/11051) +* [atom/atom#11036 - Skip deleted directories when restoring application windows](https://github.com/atom/atom/pull/11036) +* [atom/atom#11063 - Show tooltip immediately if the tooltip trigger is manual](https://github.com/atom/atom/pull/11063) +* [atom/atom#10511 - Use ELECTRON_RUN_AS_NODE Variable Key](https://github.com/atom/atom/pull/10511) +* [atom/atom#10955 - TextEditor customization](https://github.com/atom/atom/pull/10955) +* [atom/atom#11065 - Default to auto height being true.](https://github.com/atom/atom/pull/11065) +* [atom/atom#11053 - Ensure atom.cmd --wait correctly waits in Windows cmd & powershell](https://github.com/atom/atom/pull/11053) +* [atom/atom#11060 - Serialize MarkerLayers only on quit](https://github.com/atom/atom/pull/11060) +* [atom/atom#11057 - Move Pane::addItem 'pending' option to options object](https://github.com/atom/atom/pull/11057) +* [atom/atom#11057 - Move Pane::addItem 'pending' option to options object](https://github.com/atom/atom/pull/11057) +* [atom/atom#11089 - Update nodegit](https://github.com/atom/atom/pull/11089) +* [atom/atom#11089 - Update nodegit](https://github.com/atom/atom/pull/11089) +* [atom/atom#11099 - Add zero to hexadecimal numbers below F (16)](https://github.com/atom/atom/pull/11099) +* [atom/atom#11101 - Emit status changes when anything changes](https://github.com/atom/atom/pull/11101) +* [atom/atom#11101 - Emit status changes when anything changes](https://github.com/atom/atom/pull/11101) +* [atom/atom#11077 - Scroll to cursor on unfold all](https://github.com/atom/atom/pull/11077) +* [atom/atom#11103 - Make cli atom --wait work on Cygwin](https://github.com/atom/atom/pull/11103) +* [atom/atom#11115 - Update nodegit](https://github.com/atom/atom/pull/11115) +* [atom/atom#11111 - Default the options parameter to an empty object](https://github.com/atom/atom/pull/11111) +* [atom/atom#11127 - Bump markdown-preview@v0.158.0](https://github.com/atom/atom/pull/11127) +* [atom/atom#8793 - squirrel-update test on desktop shortcut groups too many assertions](https://github.com/atom/atom/pull/8793) +* [atom/atom#11078 - Add TextEditors to the registry only when opting in](https://github.com/atom/atom/pull/11078) +* [atom/atom#11054 - Patch Environment On OSX And Allow A Different Environment Per Window](https://github.com/atom/atom/pull/11054) +* [atom/atom#11153 - Fix node env](https://github.com/atom/atom/pull/11153) +* [atom/atom#11162 - BufferedProcess: search only new data for new lines rather than entire buffer, take 2](https://github.com/atom/atom/pull/11162) +* [atom/atom#11166 - Note where GitRepositoryAsync deviates from its synchronous predecessor](https://github.com/atom/atom/pull/11166) + +### [one-dark-ui](https://github.com/atom/one-dark-ui) + +v1.1.9...v1.2.0 + +* [atom/one-dark-ui#113 - Specify config schema in package.json](https://github.com/atom/one-dark-ui/pull/113) +* [atom/one-dark-ui#121 - Fix typo in comment for ui-variables.less](https://github.com/atom/one-dark-ui/pull/121) + +### [one-light-ui](https://github.com/atom/one-light-ui) + +v1.1.9...v1.2.0 + +* [atom/one-light-ui#48 - Specify config schema in package.json](https://github.com/atom/one-light-ui/pull/48) + +### [about](https://github.com/atom/about) + +v1.3.0...v1.4.1 + +* [atom/about#6 - Specify deserializer in package.json](https://github.com/atom/about/pull/6) +* [atom/about#6 - Specify deserializer in package.json](https://github.com/atom/about/pull/6) +* [atom/about#13 - Specify deserializer method in package.json, the new way](https://github.com/atom/about/pull/13) +* [atom/about#12 - Show update information on the about page](https://github.com/atom/about/pull/12) +* [atom/about#15 - Move away from deprecated Electron require syntax](https://github.com/atom/about/pull/15) + +### [archive-view](https://github.com/atom/archive-view) + +v0.61.0...v0.61.1 + +* [atom/archive-view#32 - Specify deserializer in package.json](https://github.com/atom/archive-view/pull/32) + +### [autocomplete-plus](https://github.com/atom/autocomplete-plus) + +v2.25.0...v2.29.1 + +* [atom/autocomplete-plus#612 - bugfix: auto indentation after suggestion confirm](https://github.com/atom/autocomplete-plus/pull/612) +* [atom/autocomplete-plus#641 - Stop flickering when adjusting margins](https://github.com/atom/autocomplete-plus/pull/641) +* [atom/autocomplete-plus#637 - Move config schema to package.json](https://github.com/atom/autocomplete-plus/pull/637) +* [atom/autocomplete-plus#659 - Batch autocompletion show/hide](https://github.com/atom/autocomplete-plus/pull/659) +* [atom/autocomplete-plus#675 - :art: Clean up formatting of fileBlacklist setting description](https://github.com/atom/autocomplete-plus/pull/675) +* [atom/autocomplete-plus#672 - Redesign SymbolStore and SymbolProvider](https://github.com/atom/autocomplete-plus/pull/672) +* [atom/autocomplete-plus#667 - Add unicode support for \w regexps](https://github.com/atom/autocomplete-plus/pull/667) +* [atom/autocomplete-plus#681 - Fix maximum call stack size exceeded error](https://github.com/atom/autocomplete-plus/pull/681) + +### [autosave](https://github.com/atom/autosave) + +v0.23.0...v0.23.1 + +* [atom/autosave#56 - Move config schema to package.json](https://github.com/atom/autosave/pull/56) + +### [bracket-matcher](https://github.com/atom/bracket-matcher) + +v0.79.0...v0.81.0 + +* [atom/bracket-matcher#196 - Fix spelling in settings](https://github.com/atom/bracket-matcher/pull/196) +* [atom/bracket-matcher#197 - Move config schema to package.json](https://github.com/atom/bracket-matcher/pull/197) +* [atom/bracket-matcher#212 - Make updating matches faster for multi-cursor editing](https://github.com/atom/bracket-matcher/pull/212) +* [atom/bracket-matcher#219 - Clean up view subscriptions when editor is destroyed](https://github.com/atom/bracket-matcher/pull/219) +* [atom/bracket-matcher#221 - Enable bracket matching for elixir](https://github.com/atom/bracket-matcher/pull/221) + +### [deprecation-cop](https://github.com/atom/deprecation-cop) + +v0.54.0...v0.54.1 + +* [atom/deprecation-cop#65 - Move deserializer into package.json](https://github.com/atom/deprecation-cop/pull/65) +* [atom/deprecation-cop#65 - Move deserializer into package.json](https://github.com/atom/deprecation-cop/pull/65) +* [atom/deprecation-cop#67 - Specify deserializer method in package.json](https://github.com/atom/deprecation-cop/pull/67) + +### [fuzzy-finder](https://github.com/atom/fuzzy-finder) + +v0.94.0...v1.0.3 + +* [atom/fuzzy-finder#160 - Move config schema to package.json](https://github.com/atom/fuzzy-finder/pull/160) +* [atom/fuzzy-finder#167 - Async git](https://github.com/atom/fuzzy-finder/pull/167) +* [atom/fuzzy-finder#174 - Fix spec race](https://github.com/atom/fuzzy-finder/pull/174) +* [atom/fuzzy-finder#178 - Handle symlink project paths](https://github.com/atom/fuzzy-finder/pull/178) +* [atom/fuzzy-finder#180 - Return project paths correctly if last-opened path is not in project](https://github.com/atom/fuzzy-finder/pull/180) + +### [git-diff](https://github.com/atom/git-diff) + +v0.57.0...v1.0.1 + +* [atom/git-diff#81 - Move config schema to package.json](https://github.com/atom/git-diff/pull/81) +* [atom/git-diff#82 - Async git](https://github.com/atom/git-diff/pull/82) +* [atom/git-diff#95 - Catch errors from new files.](https://github.com/atom/git-diff/pull/95) + +### [grammar-selector](https://github.com/atom/grammar-selector) + +v0.48.0...v0.48.1 + +* [atom/grammar-selector#25 - Add description for config setting](https://github.com/atom/grammar-selector/pull/25) +* [atom/grammar-selector#29 - Move config schema to package.json](https://github.com/atom/grammar-selector/pull/29) + +### [image-view](https://github.com/atom/image-view) + +v0.56.0...v0.57.0 + +* [atom/image-view#40 - Zoom to fit](https://github.com/atom/image-view/pull/40) + +### [incompatible-packages](https://github.com/atom/incompatible-packages) + +v0.25.0...v0.25.1 + +* [atom/incompatible-packages#11 - Move deserializer to package.json](https://github.com/atom/incompatible-packages/pull/11) +* [atom/incompatible-packages#11 - Move deserializer to package.json](https://github.com/atom/incompatible-packages/pull/11) +* [atom/incompatible-packages#12 - Specify deserializer method in package.json](https://github.com/atom/incompatible-packages/pull/12) + +### [keybinding-resolver](https://github.com/atom/keybinding-resolver) + +v0.33.0...v0.35.0 + +* [atom/keybinding-resolver#23 - Update coffeelint support](https://github.com/atom/keybinding-resolver/pull/23) +* [atom/keybinding-resolver#37 - Show keyup events that match a binding](https://github.com/atom/keybinding-resolver/pull/37) + +### [line-ending-selector](https://github.com/atom/line-ending-selector) + +v0.3.0...v0.3.1 + +* [atom/line-ending-selector#17 - Move config schema to package.json](https://github.com/atom/line-ending-selector/pull/17) + +### [link](https://github.com/atom/link) + +v0.31.0...v0.31.1 + +* [atom/link#14 - Move away from deprecated Electron require syntax](https://github.com/atom/link/pull/14) + +### [markdown-preview](https://github.com/atom/markdown-preview) + +v0.157.2...v0.158.0 + +* [atom/markdown-preview#349 - Use new package.json fields (configSchema and deserializers)](https://github.com/atom/markdown-preview/pull/349) +* [atom/markdown-preview#349 - Use new package.json fields (configSchema and deserializers)](https://github.com/atom/markdown-preview/pull/349) +* [atom/markdown-preview#367 - Use new package.json fields to allow deferred loading](https://github.com/atom/markdown-preview/pull/367) +* [atom/markdown-preview#335 - Use GitHub style when "Save as HTML"](https://github.com/atom/markdown-preview/pull/335) + +### [notifications](https://github.com/atom/notifications) + +v0.62.1...v0.63.1 + +* [atom/notifications#105 - Move config schema to package.json](https://github.com/atom/notifications/pull/105) +* [atom/notifications#111 - Use https://git.io insead of http](https://github.com/atom/notifications/pull/111) +* [atom/notifications#113 - replace ATOM_HOME in issue title with generic placeholder](https://github.com/atom/notifications/pull/113) +* [atom/notifications#114 - Use bit.ly instead of git.io.](https://github.com/atom/notifications/pull/114) +* [atom/notifications#115 - URL shortening, take 2](https://github.com/atom/notifications/pull/115) + +### [open-on-github](https://github.com/atom/open-on-github) + +v0.41.0...v1.0.1 + +* [atom/open-on-github#59 - Move config schema to package.json](https://github.com/atom/open-on-github/pull/59) +* [atom/open-on-github#60 - Async git](https://github.com/atom/open-on-github/pull/60) +* [atom/open-on-github#66 - Move away from deprecated Electron require syntax](https://github.com/atom/open-on-github/pull/66) + +### [package-generator](https://github.com/atom/package-generator) + +v0.41.0...v1.0.0 + +* [atom/package-generator#37 - Move config schema to package.json](https://github.com/atom/package-generator/pull/37) +* [atom/package-generator#36 - Support JS package generation](https://github.com/atom/package-generator/pull/36) + +### [settings-view](https://github.com/atom/settings-view) + +v0.232.3...v0.235.0 + +* [atom/settings-view#731 - Specify deserializer in package.json](https://github.com/atom/settings-view/pull/731) +* [atom/settings-view#749 - Move away from deprecated Electron require syntax](https://github.com/atom/settings-view/pull/749) +* [atom/settings-view#750 - Another require fix for remote](https://github.com/atom/settings-view/pull/750) +* [atom/settings-view#743 - Display and manage Git-based packages](https://github.com/atom/settings-view/pull/743) +* [atom/settings-view#748 - Add defaults on focus](https://github.com/atom/settings-view/pull/748) +* [atom/settings-view#736 - Add collapsable section for option groups](https://github.com/atom/settings-view/pull/736) + +### [spell-check](https://github.com/atom/spell-check) + +v0.65.0...v0.67.0 + +* [atom/spell-check#103 - Update README.md](https://github.com/atom/spell-check/pull/103) +* [atom/spell-check#33 - Add feature: toggle on/off](https://github.com/atom/spell-check/pull/33) +* [atom/spell-check#108 - subscriptionsOfCommands -> commandSubscription](https://github.com/atom/spell-check/pull/108) +* [atom/spell-check#112 - Move config schema to package.json](https://github.com/atom/spell-check/pull/112) +* [atom/spell-check#114 - updates spellchecker to use system language](https://github.com/atom/spell-check/pull/114) + +### [status-bar](https://github.com/atom/status-bar) + +v0.83.0...v1.2.0 + +* [atom/status-bar#121 - Move config schema to package.json](https://github.com/atom/status-bar/pull/121) +* [atom/status-bar#114 - Async git](https://github.com/atom/status-bar/pull/114) +* [atom/status-bar#122 - Make updating info faster for multi-cursor edits](https://github.com/atom/status-bar/pull/122) +* [atom/status-bar#129 - Hide diff stats for new files](https://github.com/atom/status-bar/pull/129) +* [atom/status-bar#131 - Fix error with no active item](https://github.com/atom/status-bar/pull/131) +* [atom/status-bar#133 - Move to the footer](https://github.com/atom/status-bar/pull/133) + +### [styleguide](https://github.com/atom/styleguide) + +v0.45.1...v0.45.2 + +* [atom/styleguide#34 - Specify deserializer in package.json](https://github.com/atom/styleguide/pull/34) + +### [symbols-view](https://github.com/atom/symbols-view) + +v0.110.1...v0.112.0 + +* [atom/symbols-view#147 - Add support for ES2015 static methods](https://github.com/atom/symbols-view/pull/147) +* [atom/symbols-view#151 - Specify config schema in package.json](https://github.com/atom/symbols-view/pull/151) +* [atom/symbols-view#157 - Add es7 async functions to ctags](https://github.com/atom/symbols-view/pull/157) + +### [tabs](https://github.com/atom/tabs) + +v0.91.3...v0.92.0 + +* [atom/tabs#134 - Add "open in new window" option to tabs context menu](https://github.com/atom/tabs/pull/134) + +### [timecop](https://github.com/atom/timecop) + +v0.33.0...v0.33.1 + +* [atom/timecop#15 - Specify deserializer in package.json](https://github.com/atom/timecop/pull/15) + +### [tree-view](https://github.com/atom/tree-view) + +v0.201.5...v0.203.2 + +* [atom/tree-view#754 - Add option to auto-reveal tree view entries when they become the active pane item](https://github.com/atom/tree-view/pull/754) +* [atom/tree-view#755 - Add `focusOnReveal` option](https://github.com/atom/tree-view/pull/755) +* [atom/tree-view#695 - Make 'Move in trash' more verbose on failure, add a note for Linux](https://github.com/atom/tree-view/pull/695) +* [atom/tree-view#769 - Move away from deprecated Electron require syntax](https://github.com/atom/tree-view/pull/769) +* [atom/tree-view#768 - Fix exception when double clicking opened file after activation](https://github.com/atom/tree-view/pull/768) + +### [welcome](https://github.com/atom/welcome) + +v0.33.0...v0.34.0 + +* [atom/welcome#45 - Move config schema to package.json](https://github.com/atom/welcome/pull/45) +* [atom/welcome#47 - Change menu names for different platforms](https://github.com/atom/welcome/pull/47) + +### [whitespace](https://github.com/atom/whitespace) + +v0.32.1...v0.32.2 + +* [atom/whitespace#107 - Move config schema to package.json](https://github.com/atom/whitespace/pull/107) + +### [language-clojure](https://github.com/atom/language-clojure) + +v0.19.1...v0.20.0 + +* [atom/language-clojure#39 - Fix tokenization of sexp and map nested at beginning of another sexp](https://github.com/atom/language-clojure/pull/39) + +### [language-coffee-script](https://github.com/atom/language-coffee-script) + +v0.46.0...v0.46.1 + +* [atom/language-coffee-script#85 - Check for word boundaries when attempting to auto-indent](https://github.com/atom/language-coffee-script/pull/85) + +### [language-csharp](https://github.com/atom/language-csharp) + +v0.11.0...v0.12.0 + +* [atom/language-csharp#53 - Make nameof a keyword](https://github.com/atom/language-csharp/pull/53) +* [atom/language-csharp#54 - Make C# 6's when a keyword](https://github.com/atom/language-csharp/pull/54) + +### [language-gfm](https://github.com/atom/language-gfm) + +v0.84.0...v0.85.0 + +* [atom/language-gfm#140 - Highlight HTML entities inside bold, italic, and strikethrough text](https://github.com/atom/language-gfm/pull/140) + +### [language-html](https://github.com/atom/language-html) + +v0.44.0...v0.44.1 + +* [atom/language-html#108 - Fixes #102 - ng-template highlighting for script tags](https://github.com/atom/language-html/pull/108) + +### [language-json](https://github.com/atom/language-json) + +v0.17.4...v0.17.6 + +* [atom/language-json#42 - Add .jsonld file support](https://github.com/atom/language-json/pull/42) +* [atom/language-json#43 - add composer.lock files](https://github.com/atom/language-json/pull/43) +* [atom/language-json#44 - Add .tern-project and .tern-config to file types](https://github.com/atom/language-json/pull/44) + +### [language-ruby](https://github.com/atom/language-ruby) + +v0.68.0...v0.68.3 + +* [atom/language-ruby#135 - Adds cr (Crystal lang) to fileTypes for Ruby grammar](https://github.com/atom/language-ruby/pull/135) +* [atom/language-ruby#137 - Changes dop and do order priority](https://github.com/atom/language-ruby/pull/137) +* [atom/language-ruby#136 - Fixes for tokenization of Kernel methods ending in ? or !](https://github.com/atom/language-ruby/pull/136) +* [atom/language-ruby#140 - Revert do/dop order change](https://github.com/atom/language-ruby/pull/140) + +### [language-sass](https://github.com/atom/language-sass) + +v0.45.0...v0.46.0 + +* [atom/language-sass#101 - Add individual border-radius properties](https://github.com/atom/language-sass/pull/101) + +### [language-text](https://github.com/atom/language-text) + +v0.7.0...v0.7.1 + +* [atom/language-text#5 - Add travis.yml](https://github.com/atom/language-text/pull/5) +* [atom/language-text#6 - Update legal date to 2016](https://github.com/atom/language-text/pull/6) + +### [language-xml](https://github.com/atom/language-xml) + +v0.34.2...v0.34.4 + +* [atom/language-xml#43 - Fix incorrect highlighting for empty element](https://github.com/atom/language-xml/pull/43) diff --git a/fsck.txt b/fsck.txt new file mode 100644 index 000000000..2b4444696 --- /dev/null +++ b/fsck.txt @@ -0,0 +1,84 @@ +dangling blob 8e1e50c470eb9e341f51dd728c371ec9e6ba967f +dangling blob 312880903e5988a36556c71047cfab48f7cabb0a +dangling commit d14e40a5f4e51d329bf51eed9c5ac405cef8a4e7 +dangling blob ed4f3083f21ee505b90e49bc719126b3a0ca8717 +dangling commit 1981b88bcacc6a68408a7f21c840ebe50a3f76a1 +dangling commit b490508111afcabff3afd35b877f6afa0dd9ed57 +dangling commit 629c78769aa70249e6aceaed24ca5ed02a2b24e2 +dangling blob 3db2f0aa0e9ffeda9f614085d0b55ad3669439f8 +dangling commit 34f8200a8eecf014d43defaf8c6dd4e50e99d2ee +dangling commit 5d1009b9be44874de85d5cea8bfa8692fb1c9e93 +dangling commit 9b1e51e67ebc5de354858b555eb9b4e4c92bd4dd +dangling commit 4b8039b1d3f06b0f4e0f00ca195748ee8c1e32ed +dangling blob 7ca269d19d59fa66955e17e0861eb45652d1ba2d +dangling blob c8a8a9065ef35c949d666f7e4d7f743218ec6bc7 +dangling blob 9cb1c9364187f4a7665f1c7b5dee3c3fbd8f1c99 +dangling commit eec8e92d898d645a6ce442ae7c35ec9584396c58 +dangling blob dcc9712690f1c859158aebc40a56fbf27e833d76 +dangling blob 1fe2e195f16e0d750d8d995b87b8035a44b381d2 +dangling blob 50e29981374b4dfe0c4262a4cd73aea35ca76315 +dangling blob 8ce7099a661bb3a8f4918ecb57c0279b2bbc5830 +dangling commit 08e8d1fe6d81c1ef46bee56003c93b267bae2bd8 +dangling commit 07f9710adffbd72b6142892662135540826304f4 +dangling blob 52f9e94a0f8773639c2b934c0f6dc1480b857d84 +dangling commit e813da3e669e6ff94efa7ece2b0ff72781b4989d +dangling commit 0b30da3c7e93250e5b4ad07cf70a1d6477f14f1c +dangling commit 5d5c9a487e098214b4f956c20b6f6a07aec44954 +dangling commit c5916ad2075a90dc188e612c369affdf1dc0bc23 +dangling commit fe9efa90b53d85c6ee8a7d455f61cab0f1495bb4 +dangling blob 0e9f6a422bd61e3ac86b1317d6b12356328b6d66 +dangling commit 86d8bade0a8e08537f2c6a5f278bc426e3136310 +dangling commit 44fb9a4521e7f05cfb80fcd7057fdf75a9cd57f0 +dangling blob 0efdb238e664cf22a41e19fa327ab5582fba88f9 +dangling commit 5d0aabf92aa8606b08619ee7fc3654f14aa68bad +dangling commit 2d19dbc0fc42216b8892e261201cc53a378b5d7e +dangling commit 952263ff6ad07751137586a10b7766d292d1d249 +dangling commit 6d266b46d74b6acab92a3c8787973b46936d9fee +dangling commit 8346a391ecc5c8cbccecf1da359f3b975173dcf4 +dangling commit 0554dbd09d5d199382f612e9587e9044135e1266 +dangling commit 0f667bdaa3156942835a7f55bca7d116f02bc678 +dangling blob e27e1b3bbda2e7c75cfae7715ece1adec8b7b044 +dangling blob 9b9bbba7c0ef471ef195119db8f3bc2a903c5d5e +dangling commit 4fa8734a6537b8265064e31be40e1635996a543f +dangling blob 97b793cd3612ec045881b27477927cfc2a4a2083 +dangling blob 5ceaabe3ccb8c6b2c4ffde8dacb506601431ddbb +dangling commit eb13c4a2b1ee9854a238463ecc99d27dda8641d5 +dangling commit 14421ccec537f5b034d35bd3a6578c848532d06d +dangling commit 295d1c1d8df1ebf9c8477686d5a294135c9a413c +dangling commit 4e96d4ca9770282c120e6ab909f101fb7604999f +dangling commit e6b82450a1065c2be1313376be900014f98fb340 +dangling blob 4fc1849c928074155f28caa53b06ad61ad5b140e +dangling commit f7fe343d0a2eb0016b8fdad61512921dabd188db +dangling commit 0107a55520447e6b24ce224f5c2269c6e9e09f5b +dangling commit 2f2395435ea382bdf2aaa6f8a9d313bf337dbf96 +dangling commit 7c2ca56b3b5702e3a59c85540bd6c278415071ce +dangling commit 2d59bdd296fc06664c84ff6aeb98af3346b7b1fb +dangling blob 3c6a0514c057cd275aeb9df47f7ebbc3da1bcd1f +dangling blob b08cdddd620140a4bf79f6af455ce11979ecf2c2 +dangling commit a38e850b7648a7cef9d57725a27e298bc7d03cd4 +dangling blob b6af9d9e202c61bda1ac21b66f5d576805c617f6 +dangling commit 40c3bd9d3e7e23e1d6032180b37d5e49d16c0d5c +dangling commit 34d42d33ccd9cfec6c0aacbf145f4ef37353db98 +dangling commit 1ffdbd581d994dc633c1344747b4da809c400a08 +dangling blob 490d76d4f5acb1b44d7840bc8a3c7bda62d11d07 +dangling commit b72a56ec43568db6d299c1eba54726a6e3cc1c97 +dangling commit 693c0657285beff0d4aaca0641375283c5a56c74 +dangling blob c66026146ba927fc352451448472982845a3727e +dangling blob 1671d6b83241b80fa4ba87ac8a3644e8dd9dbb23 +dangling blob 6175d60e2dd97a0a67321ca2eeccf567b6c2d988 +dangling commit 9a9deedebdea0ae0b731791ecc08caea0623fa50 +dangling blob d0aa062f7df257a3120025cbbba8aa6b0b31d1bb +dangling blob 28b82ef47e6226d52364a2a675af3145987d0710 +dangling commit 43ea466643b314b20fe374b64dbd7a47816b962a +dangling blob 52ebb6f16e8b667cd321dea15461e0d8eb690ad8 +dangling blob 7bf4fec5c7b11c2dbd64556c3422cd4e838e6690 +dangling blob 45f506658f41781d33ec9b975a73c75199ef6757 +dangling blob ee27c707653877b4f644aecce296e68c23438d0f +dangling commit a357b773b0601eb14cdc921233fd50bb1c3e3286 +dangling commit cd598fcd5927deb5ad8443614c8bd6383ff3a765 +dangling commit 8960cfeed612451e272e9da34ac831c81b7b324c +dangling commit 3e6def8d1b7eac9fa9b3053d8824440770d01ecf +dangling commit f0782fb3b7b108201648ddc895e61cee26a2070d +dangling commit a87a2781ff62ef0810740dba02d70669dc1fa315 +dangling blob 3a7e57e783324bf9b95cf66502675f450c4fb6d0 +dangling blob 1ae10fac3e2c076e3053305ec2785a63a04101d2 diff --git a/package.json b/package.json index e99251778..01b6d061e 100644 --- a/package.json +++ b/package.json @@ -113,6 +113,7 @@ "tabs": "0.92.1", "timecop": "0.33.1", "tree-view": "0.203.4", + "tree-view": "0.204.0", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From dcefde8838377d85d499bedf8488bc1495bc61fc Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 11 Apr 2016 21:01:36 -0700 Subject: [PATCH 748/971] :arrow_up: find-and-replace (cherry picked from commit 0676cdd863bdfe645a0aa00e10ab718b7f2ba4e8) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 01b6d061e..bb0a0deee 100644 --- a/package.json +++ b/package.json @@ -89,9 +89,9 @@ "dev-live-reload": "0.47.0", "encoding-selector": "0.21.0", "exception-reporting": "0.38.0", - "find-and-replace": "0.197.6", "fuzzy-finder": "1.0.4", "git-diff": "1.0.1", + "find-and-replace": "0.198.0", "go-to-line": "0.30.0", "grammar-selector": "0.48.1", "image-view": "0.57.0", From 82fa61e54d88799788ab4125dd8152974d8496c3 Mon Sep 17 00:00:00 2001 From: Joe Fitzgerald Date: Tue, 12 Apr 2016 11:29:33 -0600 Subject: [PATCH 749/971] Allow Multiple Launches Of Atom To Result In An Updated Environment --- src/browser/atom-application.coffee | 1 + src/browser/atom-window.coffee | 4 ++++ src/environment-helpers.js | 10 +++++++++- src/initialize-application-window.coffee | 8 ++++++-- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index a579fb762..69eff1845 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -466,6 +466,7 @@ class AtomApplication openedWindow.restore() else openedWindow.focus() + openedWindow.replaceEnvironment(env) else if devMode try diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 33c64da7d..60e6d0553 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -68,6 +68,7 @@ class AtomWindow @loaded = true @setLoadSettings(loadSettings) + @env = loadSettings.env if loadSettings.env? @browserWindow.focusOnWebView() if @isSpec @browserWindow.temporaryState = {windowDimensions} if windowDimensions? @@ -169,6 +170,9 @@ class AtomWindow else @browserWindow.once 'window:loaded', => @openLocations(locationsToOpen) + replaceEnvironment: (env) -> + @browserWindow.webContents.send 'environment', env + sendMessage: (message, detail) -> @browserWindow.webContents.send 'message', message, detail diff --git a/src/environment-helpers.js b/src/environment-helpers.js index 00c112bda..e2baeb26b 100644 --- a/src/environment-helpers.js +++ b/src/environment-helpers.js @@ -91,4 +91,12 @@ function normalize (options = {}) { } } -export default { getFromShell, needsPatching, normalize } +function replace (env) { + if (!env || !env.PATH) { + return + } + + process.env = env +} + +export default { getFromShell, needsPatching, normalize, replace } diff --git a/src/initialize-application-window.coffee b/src/initialize-application-window.coffee index ea811f515..463bdd48e 100644 --- a/src/initialize-application-window.coffee +++ b/src/initialize-application-window.coffee @@ -4,11 +4,12 @@ module.exports = ({blobStore}) -> path = require 'path' require './window' {getWindowLoadSettings} = require './window-load-settings-helpers' - + {ipcRenderer} = require 'electron' {resourcePath, isSpec, devMode, env} = getWindowLoadSettings() # Set baseline environment environmentHelpers.normalize({env: env}) + env = process.env # Add application-specific exports to module search path. exportsPath = path.join(resourcePath, 'exports') @@ -25,7 +26,7 @@ module.exports = ({blobStore}) -> applicationDelegate: new ApplicationDelegate, configDirPath: process.env.ATOM_HOME enablePersistence: true - env: env + env: process.env }) atom.startEditorWindow().then -> @@ -35,3 +36,6 @@ module.exports = ({blobStore}) -> window.removeEventListener('focus', windowFocused) setTimeout (-> document.querySelector('atom-workspace').focus()), 0 window.addEventListener('focus', windowFocused) + ipcRenderer.on('environment', (event, env) -> + environmentHelpers.replace(env) + ) From 2d173911b22a588f10467e723d0736c85c49bea0 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 12 Apr 2016 12:02:20 -0700 Subject: [PATCH 750/971] Ignore autorun on our buffered process commands. Fixes #10082 --- spec/buffered-process-spec.coffee | 7 ++++--- src/buffered-process.coffee | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/spec/buffered-process-spec.coffee b/spec/buffered-process-spec.coffee index 1f524d66a..643a2d411 100644 --- a/spec/buffered-process-spec.coffee +++ b/spec/buffered-process-spec.coffee @@ -88,14 +88,15 @@ describe "BufferedProcess", -> describe "when the explorer command is spawned on Windows", -> it "doesn't quote arguments of the form /root,C...", -> new BufferedProcess({command: 'explorer.exe', args: ['/root,C:\\foo']}) - expect(ChildProcess.spawn.argsForCall[0][1][2]).toBe '"explorer.exe /root,C:\\foo"' + expect(ChildProcess.spawn.argsForCall[0][1][3]).toBe '"explorer.exe /root,C:\\foo"' it "spawns the command using a cmd.exe wrapper", -> new BufferedProcess({command: 'dir'}) expect(path.basename(ChildProcess.spawn.argsForCall[0][0])).toBe 'cmd.exe' expect(ChildProcess.spawn.argsForCall[0][1][0]).toBe '/s' - expect(ChildProcess.spawn.argsForCall[0][1][1]).toBe '/c' - expect(ChildProcess.spawn.argsForCall[0][1][2]).toBe '"dir"' + expect(ChildProcess.spawn.argsForCall[0][1][1]).toBe '/d' + expect(ChildProcess.spawn.argsForCall[0][1][2]).toBe '/c' + expect(ChildProcess.spawn.argsForCall[0][1][3]).toBe '"dir"' it "calls the specified stdout, stderr, and exit callbacks", -> stdout = '' diff --git a/src/buffered-process.coffee b/src/buffered-process.coffee index 53934c02d..59f2a7e9a 100644 --- a/src/buffered-process.coffee +++ b/src/buffered-process.coffee @@ -67,7 +67,7 @@ class BufferedProcess cmdArgs.unshift("\"#{command}\"") else cmdArgs.unshift(command) - cmdArgs = ['/s', '/c', "\"#{cmdArgs.join(' ')}\""] + cmdArgs = ['/s', '/d', '/c', "\"#{cmdArgs.join(' ')}\""] cmdOptions = _.clone(options) cmdOptions.windowsVerbatimArguments = true @spawn(@getCmdPath(), cmdArgs, cmdOptions) From 2a9bc0fa1281bdb7027fdcfbff967ef47dcfd309 Mon Sep 17 00:00:00 2001 From: Wliu Date: Tue, 12 Apr 2016 17:08:01 -0400 Subject: [PATCH 751/971] Actually fix require paths --- src/module-cache.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/module-cache.coffee b/src/module-cache.coffee index c8e3b9105..a84251a93 100644 --- a/src/module-cache.coffee +++ b/src/module-cache.coffee @@ -202,12 +202,12 @@ registerBuiltins = (devMode) -> atomShellRoot = path.join(process.resourcesPath, 'atom.asar') - commonRoot = path.join(atomShellRoot, 'lib', 'common', 'api') + commonRoot = path.join(atomShellRoot, 'common', 'api') commonBuiltins = ['callbacks-registry', 'clipboard', 'crash-reporter', 'screen', 'shell'] for builtin in commonBuiltins cache.builtins[builtin] = path.join(commonRoot, "#{builtin}.js") - rendererRoot = path.join(atomShellRoot, 'lib', 'renderer', 'api') + rendererRoot = path.join(atomShellRoot, 'renderer', 'api') rendererBuiltins = ['ipc-renderer', 'remote'] for builtin in rendererBuiltins cache.builtins[builtin] = path.join(rendererRoot, "#{builtin}.js") From 7d13ba5d4e7f7fa9d00a9ae39ab05e55be02a172 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 12 Apr 2016 15:48:37 -0700 Subject: [PATCH 752/971] :arrow_up: tree-view --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index bb0a0deee..4d24f87ae 100644 --- a/package.json +++ b/package.json @@ -112,8 +112,7 @@ "symbols-view": "0.112.0", "tabs": "0.92.1", "timecop": "0.33.1", - "tree-view": "0.203.4", - "tree-view": "0.204.0", + "tree-view": "0.205.0", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From c936ed96dc2cbedc025c898ebb5f00c7c6072407 Mon Sep 17 00:00:00 2001 From: Hubot Date: Tue, 12 Apr 2016 19:16:53 -0500 Subject: [PATCH 753/971] 1.9.0-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4d24f87ae..59a0302c0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "1.8.0-dev", + "version": "1.9.0-dev", "description": "A hackable text editor for the 21st Century.", "main": "./src/browser/main.js", "repository": { From 49fb28759292541c3d60224db5d12392c6d0c359 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 12 Apr 2016 20:35:30 -0700 Subject: [PATCH 754/971] Don't load packages starting with dot. Fixes #9805 --- spec/package-manager-spec.coffee | 3 +++ src/package-manager.coffee | 2 ++ 2 files changed, 5 insertions(+) diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 6a1610a8a..f68005cf5 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -66,6 +66,9 @@ describe "PackageManager", -> expect(addErrorHandler.argsForCall[0][0].message).toContain("Failed to load the package-with-broken-package-json package") expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual "package-with-broken-package-json" + it "returns null if the package name or path starts with a dot", -> + expect(atom.packages.loadPackage("/Users/user/.atom/packages/.git")).toBeNull() + it "normalizes short repository urls in package.json", -> {metadata} = atom.packages.loadPackage("package-with-short-url-package-json") expect(metadata.repository.type).toBe "git" diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 0e76a762f..8f2924358 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -367,6 +367,8 @@ class PackageManager @emitter.emit 'did-load-initial-packages' loadPackage: (nameOrPath) -> + return null if path.basename(nameOrPath)[0].match /^\./ # primarily to skip .git folder + return pack if pack = @getLoadedPackage(nameOrPath) if packagePath = @resolvePackagePath(nameOrPath) From 373b931c5123917e3d2c368298ce143b0ed48ec9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 13 Apr 2016 13:52:28 +0200 Subject: [PATCH 755/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0933e8be4..6256f5927 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.0.0-beta2", + "text-buffer": "9.0.0-beta3", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 3270abe1f6ca5653ba962b7f85d4a88e8a6d026e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 13 Apr 2016 18:42:52 +0200 Subject: [PATCH 756/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 382e7ed3e..fadd361e9 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.0.0-beta3", + "text-buffer": "9.0.0-beta4", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 043982ca3d6b2debeb0f1f542c64bcfc0c1c824d Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 13 Apr 2016 11:23:55 -0700 Subject: [PATCH 757/971] :fire: files accidentally committed --- changes.md | 0 changes2.md | 397 ------------------------------------------------- changes3.md | 414 ---------------------------------------------------- fsck.txt | 84 ----------- 4 files changed, 895 deletions(-) delete mode 100644 changes.md delete mode 100644 changes2.md delete mode 100644 changes3.md delete mode 100644 fsck.txt diff --git a/changes.md b/changes.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/changes2.md b/changes2.md deleted file mode 100644 index e8224e628..000000000 --- a/changes2.md +++ /dev/null @@ -1,397 +0,0 @@ -### [Atom Core](https://github.com/atom/atom) - -v1.6.0...v1.7.0-beta0 - -* [atom/atom#10747 - Clarify Windows build docs for VS2015 etc.](https://github.com/atom/atom/pull/10747) -* [atom/atom#10743 - Don't cascade on reload](https://github.com/atom/atom/pull/10743) -* [atom/atom#9198 - permit any whole number for tabLength](https://github.com/atom/atom/pull/9198) -* [atom/atom#10758 - Fix status in subdir](https://github.com/atom/atom/pull/10758) -* [atom/atom#10768 - Temporarily disable deserializers & viewProviders metadata fields](https://github.com/atom/atom/pull/10768) -* [atom/atom#10764 - Let packages define deserializers & view providers as main module methods](https://github.com/atom/atom/pull/10764) -* [atom/atom#10778 - Fix minor typo in CONTRIBUTING.md](https://github.com/atom/atom/pull/10778) -* [atom/atom#10789 - Upgrade autocomplete-plus to use new text-buffer API](https://github.com/atom/atom/pull/10789) -* [atom/atom#10797 - Fix status with multiple paths](https://github.com/atom/atom/pull/10797) -* [atom/atom#10797 - Fix status with multiple paths](https://github.com/atom/atom/pull/10797) -* [atom/atom#10792 - Fix TextEditorPresenter spec timeout.](https://github.com/atom/atom/pull/10792) -* [atom/atom#10803 - Fix typo in function call](https://github.com/atom/atom/pull/10803) -* [atom/atom#10828 - Fix another TextEditorPresenter spec race](https://github.com/atom/atom/pull/10828) -* [atom/atom#10827 - Port repository subdirectory status fixes to async repo](https://github.com/atom/atom/pull/10827) -* [atom/atom#10827 - Port repository subdirectory status fixes to async repo](https://github.com/atom/atom/pull/10827) -* [atom/atom#10838 - Log output for core specs](https://github.com/atom/atom/pull/10838) -* [atom/atom#10818 - Register Atom for file associations in Windows](https://github.com/atom/atom/pull/10818) -* [atom/atom#10605 - Periodically save state and store in indexedDB](https://github.com/atom/atom/pull/10605) -* [atom/atom#9627 - Update to Electron 0.36](https://github.com/atom/atom/pull/9627) -* [atom/atom#10835 - Add TextEditor.prototype.cursorsForScreenRowRange](https://github.com/atom/atom/pull/10835) -* [atom/atom#10795 - Remove unused Core Team Project Management labels](https://github.com/atom/atom/pull/10795) -* [atom/atom#10858 - Update grunt-electron-installer to latest version](https://github.com/atom/atom/pull/10858) -* [atom/atom#10846 - Add core setting for pending tabs configuration](https://github.com/atom/atom/pull/10846) -* [atom/atom#10846 - Add core setting for pending tabs configuration](https://github.com/atom/atom/pull/10846) -* [atom/atom#10873 - Avoid emitting config events while loading packages](https://github.com/atom/atom/pull/10873) -* [atom/atom#10872 - Terminate pending state for opened file if pending option is false](https://github.com/atom/atom/pull/10872) -* [atom/atom#10872 - Terminate pending state for opened file if pending option is false](https://github.com/atom/atom/pull/10872) -* [atom/atom#10863 - Compare markers instead of ranges in Selection](https://github.com/atom/atom/pull/10863) -* [atom/atom#10874 - Avoid Windows 260-character path limits in script\clean](https://github.com/atom/atom/pull/10874) -* [atom/atom#10861 - Compute line foldability lazily](https://github.com/atom/atom/pull/10861) -* [atom/atom#10843 - Bump packages to use async git](https://github.com/atom/atom/pull/10843) -* [atom/atom#10886 - Update Atom build directory in build instructions](https://github.com/atom/atom/pull/10886) -* [atom/atom#10885 - Cache regexes in LanguageMode.prototype.getRegexForProperty](https://github.com/atom/atom/pull/10885) -* [atom/atom#10888 - Load packages before deserializing state](https://github.com/atom/atom/pull/10888) -* [atom/atom#10870 - Add Issue template and extra version info](https://github.com/atom/atom/pull/10870) -* [atom/atom#10878 - Allow pasting white space when `autoIndentOnPaste` is enabled](https://github.com/atom/atom/pull/10878) -* [atom/atom#10895 - Fix some spec issues](https://github.com/atom/atom/pull/10895) -* [atom/atom#10901 - remove Open Roadmap menu item, fixes #10884](https://github.com/atom/atom/pull/10901) -* [atom/atom#10898 - Pass the notification manager when splitting panes](https://github.com/atom/atom/pull/10898) -* [atom/atom#10927 - Upgrade electron to fix command-backtick bug](https://github.com/atom/atom/pull/10927) -* [atom/atom#10926 - Fix case-preserving path relativization](https://github.com/atom/atom/pull/10926) -* [atom/atom#10926 - Fix case-preserving path relativization](https://github.com/atom/atom/pull/10926) -* [atom/atom#10921 - Add support for keybindings with keyup keystrokes](https://github.com/atom/atom/pull/10921) -* [atom/atom#10841 - Add the -a, --add CLI option](https://github.com/atom/atom/pull/10841) -* [atom/atom#10933 - Fix block decorations spec in text editor presenter](https://github.com/atom/atom/pull/10933) -* [atom/atom#10925 - Faster state serialization](https://github.com/atom/atom/pull/10925) -* [atom/atom#10967 - Fix a inconsistent getLineCount() use](https://github.com/atom/atom/pull/10967) -* [atom/atom#10967 - Fix a inconsistent getLineCount() use](https://github.com/atom/atom/pull/10967) -* [atom/atom#10975 - Update nodegit](https://github.com/atom/atom/pull/10975) -* [atom/atom#10975 - Update nodegit](https://github.com/atom/atom/pull/10975) -* [atom/atom#10326 - Fix Windows installer path update woes](https://github.com/atom/atom/pull/10326) -* [atom/atom#10959 - Refactor pending state to live in pane instead of items](https://github.com/atom/atom/pull/10959) -* [atom/atom#10959 - Refactor pending state to live in pane instead of items](https://github.com/atom/atom/pull/10959) -* [atom/atom#10984 - [WIP] experiment with circle ci](https://github.com/atom/atom/pull/10984) -* [atom/atom#10737 - Add MRU tab switching functionality](https://github.com/atom/atom/pull/10737) -* [atom/atom#11001 - onDidTerminatePendingState ➡︎ onItemDidTerminatePendingState](https://github.com/atom/atom/pull/11001) -* [atom/atom#11001 - onDidTerminatePendingState ➡︎ onItemDidTerminatePendingState](https://github.com/atom/atom/pull/11001) -* [atom/atom#10972 - Update build scripts to use the new railcar branching model](https://github.com/atom/atom/pull/10972) -* [atom/atom#10972 - Update build scripts to use the new railcar branching model](https://github.com/atom/atom/pull/10972) -* [atom/atom#11006 - Move spec from tabs package](https://github.com/atom/atom/pull/11006) -* [atom/atom#10851 - Registry for TextEditors](https://github.com/atom/atom/pull/10851) -* [atom/atom#11010 - Always strip git+ prefix and .git suffix from package repository URLs](https://github.com/atom/atom/pull/11010) -* [atom/atom#11011 - Autoscroll after consolidating selections](https://github.com/atom/atom/pull/11011) -* [atom/atom#11008 - Add documentation for order key in config.](https://github.com/atom/atom/pull/11008) -* [atom/atom#11009 - Don't destroy pane if replacing last pending item](https://github.com/atom/atom/pull/11009) -* [atom/atom#11009 - Don't destroy pane if replacing last pending item](https://github.com/atom/atom/pull/11009) -* [atom/atom#10675 - Expose application updater lifecycle events to packages](https://github.com/atom/atom/pull/10675) -* [atom/atom#11022 - Windows git fixes](https://github.com/atom/atom/pull/11022) -* [atom/atom#11022 - Windows git fixes](https://github.com/atom/atom/pull/11022) -* [atom/atom#11051 - Add new item before destroying pending item](https://github.com/atom/atom/pull/11051) -* [atom/atom#11051 - Add new item before destroying pending item](https://github.com/atom/atom/pull/11051) -* [atom/atom#11036 - Skip deleted directories when restoring application windows](https://github.com/atom/atom/pull/11036) -* [atom/atom#11063 - Show tooltip immediately if the tooltip trigger is manual](https://github.com/atom/atom/pull/11063) -* [atom/atom#10511 - Use ELECTRON_RUN_AS_NODE Variable Key](https://github.com/atom/atom/pull/10511) -* [atom/atom#10955 - TextEditor customization](https://github.com/atom/atom/pull/10955) -* [atom/atom#11065 - Default to auto height being true.](https://github.com/atom/atom/pull/11065) -* [atom/atom#11053 - Ensure atom.cmd --wait correctly waits in Windows cmd & powershell](https://github.com/atom/atom/pull/11053) -* [atom/atom#11060 - Serialize MarkerLayers only on quit](https://github.com/atom/atom/pull/11060) -* [atom/atom#11057 - Move Pane::addItem 'pending' option to options object](https://github.com/atom/atom/pull/11057) -* [atom/atom#11057 - Move Pane::addItem 'pending' option to options object](https://github.com/atom/atom/pull/11057) -* [atom/atom#11089 - Update nodegit](https://github.com/atom/atom/pull/11089) -* [atom/atom#11089 - Update nodegit](https://github.com/atom/atom/pull/11089) -* [atom/atom#11099 - Add zero to hexadecimal numbers below F (16)](https://github.com/atom/atom/pull/11099) -* [atom/atom#11101 - Emit status changes when anything changes](https://github.com/atom/atom/pull/11101) -* [atom/atom#11101 - Emit status changes when anything changes](https://github.com/atom/atom/pull/11101) -* [atom/atom#11077 - Scroll to cursor on unfold all](https://github.com/atom/atom/pull/11077) -* [atom/atom#11103 - Make cli atom --wait work on Cygwin](https://github.com/atom/atom/pull/11103) -* [atom/atom#11115 - Update nodegit](https://github.com/atom/atom/pull/11115) -* [atom/atom#11115 - Update nodegit](https://github.com/atom/atom/pull/11115) -* [atom/atom#11111 - Default the options parameter to an empty object](https://github.com/atom/atom/pull/11111) -* [atom/atom#11127 - Bump markdown-preview@v0.158.0](https://github.com/atom/atom/pull/11127) -* [atom/atom#8793 - squirrel-update test on desktop shortcut groups too many assertions](https://github.com/atom/atom/pull/8793) -* [atom/atom#11078 - Add TextEditors to the registry only when opting in](https://github.com/atom/atom/pull/11078) -* [atom/atom#11054 - Patch Environment On OSX And Allow A Different Environment Per Window](https://github.com/atom/atom/pull/11054) -* [atom/atom#11153 - Fix node env](https://github.com/atom/atom/pull/11153) -* [atom/atom#11162 - BufferedProcess: search only new data for new lines rather than entire buffer, take 2](https://github.com/atom/atom/pull/11162) -* [atom/atom#11166 - Note where GitRepositoryAsync deviates from its synchronous predecessor](https://github.com/atom/atom/pull/11166) - -### [one-dark-ui](https://github.com/atom/one-dark-ui) - -v1.1.9...v1.2.0 - -* [atom/one-dark-ui#113 - Specify config schema in package.json](https://github.com/atom/one-dark-ui/pull/113) -* [atom/one-dark-ui#121 - Fix typo in comment for ui-variables.less](https://github.com/atom/one-dark-ui/pull/121) - -### [one-light-ui](https://github.com/atom/one-light-ui) - -v1.1.9...v1.2.0 - -* [atom/one-light-ui#48 - Specify config schema in package.json](https://github.com/atom/one-light-ui/pull/48) - -### [about](https://github.com/atom/about) - -v1.3.0...v1.4.1 - -* [atom/about#6 - Specify deserializer in package.json](https://github.com/atom/about/pull/6) -* [atom/about#6 - Specify deserializer in package.json](https://github.com/atom/about/pull/6) -* [atom/about#13 - Specify deserializer method in package.json, the new way](https://github.com/atom/about/pull/13) -* [atom/about#12 - Show update information on the about page](https://github.com/atom/about/pull/12) -* [atom/about#15 - Move away from deprecated Electron require syntax](https://github.com/atom/about/pull/15) - -### [archive-view](https://github.com/atom/archive-view) - -v0.61.0...v0.61.1 - -* [atom/archive-view#32 - Specify deserializer in package.json](https://github.com/atom/archive-view/pull/32) - -### [autocomplete-plus](https://github.com/atom/autocomplete-plus) - -v2.25.0...v2.29.1 - -* [atom/autocomplete-plus#612 - bugfix: auto indentation after suggestion confirm](https://github.com/atom/autocomplete-plus/pull/612) -* [atom/autocomplete-plus#641 - Stop flickering when adjusting margins](https://github.com/atom/autocomplete-plus/pull/641) -* [atom/autocomplete-plus#637 - Move config schema to package.json](https://github.com/atom/autocomplete-plus/pull/637) -* [atom/autocomplete-plus#659 - Batch autocompletion show/hide](https://github.com/atom/autocomplete-plus/pull/659) -* [atom/autocomplete-plus#675 - :art: Clean up formatting of fileBlacklist setting description](https://github.com/atom/autocomplete-plus/pull/675) -* [atom/autocomplete-plus#672 - Redesign SymbolStore and SymbolProvider](https://github.com/atom/autocomplete-plus/pull/672) -* [atom/autocomplete-plus#667 - Add unicode support for \w regexps](https://github.com/atom/autocomplete-plus/pull/667) -* [atom/autocomplete-plus#681 - Fix maximum call stack size exceeded error](https://github.com/atom/autocomplete-plus/pull/681) - -### [autosave](https://github.com/atom/autosave) - -v0.23.0...v0.23.1 - -* [atom/autosave#56 - Move config schema to package.json](https://github.com/atom/autosave/pull/56) - -### [bracket-matcher](https://github.com/atom/bracket-matcher) - -v0.79.0...v0.81.0 - -* [atom/bracket-matcher#196 - Fix spelling in settings](https://github.com/atom/bracket-matcher/pull/196) -* [atom/bracket-matcher#197 - Move config schema to package.json](https://github.com/atom/bracket-matcher/pull/197) -* [atom/bracket-matcher#212 - Make updating matches faster for multi-cursor editing](https://github.com/atom/bracket-matcher/pull/212) -* [atom/bracket-matcher#219 - Clean up view subscriptions when editor is destroyed](https://github.com/atom/bracket-matcher/pull/219) -* [atom/bracket-matcher#221 - Enable bracket matching for elixir](https://github.com/atom/bracket-matcher/pull/221) - -### [deprecation-cop](https://github.com/atom/deprecation-cop) - -v0.54.0...v0.54.1 - -* [atom/deprecation-cop#65 - Move deserializer into package.json](https://github.com/atom/deprecation-cop/pull/65) -* [atom/deprecation-cop#65 - Move deserializer into package.json](https://github.com/atom/deprecation-cop/pull/65) -* [atom/deprecation-cop#67 - Specify deserializer method in package.json](https://github.com/atom/deprecation-cop/pull/67) - -### [fuzzy-finder](https://github.com/atom/fuzzy-finder) - -v0.94.0...v1.0.3 - -* [atom/fuzzy-finder#160 - Move config schema to package.json](https://github.com/atom/fuzzy-finder/pull/160) -* [atom/fuzzy-finder#167 - Async git](https://github.com/atom/fuzzy-finder/pull/167) -* [atom/fuzzy-finder#174 - Fix spec race](https://github.com/atom/fuzzy-finder/pull/174) -* [atom/fuzzy-finder#178 - Handle symlink project paths](https://github.com/atom/fuzzy-finder/pull/178) -* [atom/fuzzy-finder#180 - Return project paths correctly if last-opened path is not in project](https://github.com/atom/fuzzy-finder/pull/180) - -### [git-diff](https://github.com/atom/git-diff) - -v0.57.0...v1.0.1 - -* [atom/git-diff#81 - Move config schema to package.json](https://github.com/atom/git-diff/pull/81) -* [atom/git-diff#82 - Async git](https://github.com/atom/git-diff/pull/82) -* [atom/git-diff#95 - Catch errors from new files.](https://github.com/atom/git-diff/pull/95) - -### [grammar-selector](https://github.com/atom/grammar-selector) - -v0.48.0...v0.48.1 - -* [atom/grammar-selector#25 - Add description for config setting](https://github.com/atom/grammar-selector/pull/25) -* [atom/grammar-selector#29 - Move config schema to package.json](https://github.com/atom/grammar-selector/pull/29) - -### [image-view](https://github.com/atom/image-view) - -v0.56.0...v0.57.0 - -* [atom/image-view#40 - Zoom to fit](https://github.com/atom/image-view/pull/40) - -### [incompatible-packages](https://github.com/atom/incompatible-packages) - -v0.25.0...v0.25.1 - -* [atom/incompatible-packages#11 - Move deserializer to package.json](https://github.com/atom/incompatible-packages/pull/11) -* [atom/incompatible-packages#11 - Move deserializer to package.json](https://github.com/atom/incompatible-packages/pull/11) -* [atom/incompatible-packages#12 - Specify deserializer method in package.json](https://github.com/atom/incompatible-packages/pull/12) - -### [keybinding-resolver](https://github.com/atom/keybinding-resolver) - -v0.33.0...v0.35.0 - -* [atom/keybinding-resolver#23 - Update coffeelint support](https://github.com/atom/keybinding-resolver/pull/23) -* [atom/keybinding-resolver#37 - Show keyup events that match a binding](https://github.com/atom/keybinding-resolver/pull/37) - -### [line-ending-selector](https://github.com/atom/line-ending-selector) - -v0.3.0...v0.3.1 - -* [atom/line-ending-selector#17 - Move config schema to package.json](https://github.com/atom/line-ending-selector/pull/17) - -### [link](https://github.com/atom/link) - -v0.31.0...v0.31.1 - -* [atom/link#14 - Move away from deprecated Electron require syntax](https://github.com/atom/link/pull/14) - -### [markdown-preview](https://github.com/atom/markdown-preview) - -v0.157.2...v0.158.0 - -* [atom/markdown-preview#349 - Use new package.json fields (configSchema and deserializers)](https://github.com/atom/markdown-preview/pull/349) -* [atom/markdown-preview#349 - Use new package.json fields (configSchema and deserializers)](https://github.com/atom/markdown-preview/pull/349) -* [atom/markdown-preview#367 - Use new package.json fields to allow deferred loading](https://github.com/atom/markdown-preview/pull/367) -* [atom/markdown-preview#335 - Use GitHub style when "Save as HTML"](https://github.com/atom/markdown-preview/pull/335) - -### [notifications](https://github.com/atom/notifications) - -v0.62.1...v0.63.1 - -* [atom/notifications#105 - Move config schema to package.json](https://github.com/atom/notifications/pull/105) -* [atom/notifications#111 - Use https://git.io insead of http](https://github.com/atom/notifications/pull/111) -* [atom/notifications#113 - replace ATOM_HOME in issue title with generic placeholder](https://github.com/atom/notifications/pull/113) -* [atom/notifications#114 - Use bit.ly instead of git.io.](https://github.com/atom/notifications/pull/114) -* [atom/notifications#115 - URL shortening, take 2](https://github.com/atom/notifications/pull/115) - -### [open-on-github](https://github.com/atom/open-on-github) - -v0.41.0...v1.0.1 - -* [atom/open-on-github#59 - Move config schema to package.json](https://github.com/atom/open-on-github/pull/59) -* [atom/open-on-github#60 - Async git](https://github.com/atom/open-on-github/pull/60) -* [atom/open-on-github#66 - Move away from deprecated Electron require syntax](https://github.com/atom/open-on-github/pull/66) - -### [package-generator](https://github.com/atom/package-generator) - -v0.41.0...v1.0.0 - -* [atom/package-generator#37 - Move config schema to package.json](https://github.com/atom/package-generator/pull/37) -* [atom/package-generator#36 - Support JS package generation](https://github.com/atom/package-generator/pull/36) - -### [settings-view](https://github.com/atom/settings-view) - -v0.232.3...v0.235.0 - -* [atom/settings-view#731 - Specify deserializer in package.json](https://github.com/atom/settings-view/pull/731) -* [atom/settings-view#749 - Move away from deprecated Electron require syntax](https://github.com/atom/settings-view/pull/749) -* [atom/settings-view#750 - Another require fix for remote](https://github.com/atom/settings-view/pull/750) -* [atom/settings-view#743 - Display and manage Git-based packages](https://github.com/atom/settings-view/pull/743) -* [atom/settings-view#748 - Add defaults on focus](https://github.com/atom/settings-view/pull/748) -* [atom/settings-view#736 - Add collapsable section for option groups](https://github.com/atom/settings-view/pull/736) - -### [spell-check](https://github.com/atom/spell-check) - -v0.65.0...v0.67.0 - -* [atom/spell-check#103 - Update README.md](https://github.com/atom/spell-check/pull/103) -* [atom/spell-check#33 - Add feature: toggle on/off](https://github.com/atom/spell-check/pull/33) -* [atom/spell-check#108 - subscriptionsOfCommands -> commandSubscription](https://github.com/atom/spell-check/pull/108) -* [atom/spell-check#112 - Move config schema to package.json](https://github.com/atom/spell-check/pull/112) -* [atom/spell-check#114 - updates spellchecker to use system language](https://github.com/atom/spell-check/pull/114) - -### [status-bar](https://github.com/atom/status-bar) - -v0.83.0...v1.2.0 - -* [atom/status-bar#121 - Move config schema to package.json](https://github.com/atom/status-bar/pull/121) -* [atom/status-bar#114 - Async git](https://github.com/atom/status-bar/pull/114) -* [atom/status-bar#122 - Make updating info faster for multi-cursor edits](https://github.com/atom/status-bar/pull/122) -* [atom/status-bar#129 - Hide diff stats for new files](https://github.com/atom/status-bar/pull/129) -* [atom/status-bar#131 - Fix error with no active item](https://github.com/atom/status-bar/pull/131) -* [atom/status-bar#133 - Move to the footer](https://github.com/atom/status-bar/pull/133) - -### [styleguide](https://github.com/atom/styleguide) - -v0.45.1...v0.45.2 - -* [atom/styleguide#34 - Specify deserializer in package.json](https://github.com/atom/styleguide/pull/34) - -### [symbols-view](https://github.com/atom/symbols-view) - -v0.110.1...v0.112.0 - -* [atom/symbols-view#147 - Add support for ES2015 static methods](https://github.com/atom/symbols-view/pull/147) -* [atom/symbols-view#151 - Specify config schema in package.json](https://github.com/atom/symbols-view/pull/151) -* [atom/symbols-view#157 - Add es7 async functions to ctags](https://github.com/atom/symbols-view/pull/157) - -### [tabs](https://github.com/atom/tabs) - -v0.91.3...v0.92.0 - -* [atom/tabs#134 - Add "open in new window" option to tabs context menu](https://github.com/atom/tabs/pull/134) - -### [timecop](https://github.com/atom/timecop) - -v0.33.0...v0.33.1 - -* [atom/timecop#15 - Specify deserializer in package.json](https://github.com/atom/timecop/pull/15) - -### [welcome](https://github.com/atom/welcome) - -v0.33.0...v0.34.0 - -* [atom/welcome#45 - Move config schema to package.json](https://github.com/atom/welcome/pull/45) -* [atom/welcome#47 - Change menu names for different platforms](https://github.com/atom/welcome/pull/47) - -### [whitespace](https://github.com/atom/whitespace) - -v0.32.1...v0.32.2 - -* [atom/whitespace#107 - Move config schema to package.json](https://github.com/atom/whitespace/pull/107) - -### [language-clojure](https://github.com/atom/language-clojure) - -v0.19.1...v0.20.0 - -* [atom/language-clojure#39 - Fix tokenization of sexp and map nested at beginning of another sexp](https://github.com/atom/language-clojure/pull/39) - -### [language-coffee-script](https://github.com/atom/language-coffee-script) - -v0.46.0...v0.46.1 - -* [atom/language-coffee-script#85 - Check for word boundaries when attempting to auto-indent](https://github.com/atom/language-coffee-script/pull/85) - -### [language-csharp](https://github.com/atom/language-csharp) - -v0.11.0...v0.12.0 - -* [atom/language-csharp#53 - Make nameof a keyword](https://github.com/atom/language-csharp/pull/53) -* [atom/language-csharp#54 - Make C# 6's when a keyword](https://github.com/atom/language-csharp/pull/54) - -### [language-gfm](https://github.com/atom/language-gfm) - -v0.84.0...v0.85.0 - -* [atom/language-gfm#140 - Highlight HTML entities inside bold, italic, and strikethrough text](https://github.com/atom/language-gfm/pull/140) - -### [language-html](https://github.com/atom/language-html) - -v0.44.0...v0.44.1 - -* [atom/language-html#108 - Fixes #102 - ng-template highlighting for script tags](https://github.com/atom/language-html/pull/108) - -### [language-json](https://github.com/atom/language-json) - -v0.17.4...v0.17.6 - -* [atom/language-json#42 - Add .jsonld file support](https://github.com/atom/language-json/pull/42) -* [atom/language-json#43 - add composer.lock files](https://github.com/atom/language-json/pull/43) -* [atom/language-json#44 - Add .tern-project and .tern-config to file types](https://github.com/atom/language-json/pull/44) - -### [language-ruby](https://github.com/atom/language-ruby) - -v0.68.0...v0.68.3 - -* [atom/language-ruby#135 - Adds cr (Crystal lang) to fileTypes for Ruby grammar](https://github.com/atom/language-ruby/pull/135) -* [atom/language-ruby#137 - Changes dop and do order priority](https://github.com/atom/language-ruby/pull/137) -* [atom/language-ruby#136 - Fixes for tokenization of Kernel methods ending in ? or !](https://github.com/atom/language-ruby/pull/136) -* [atom/language-ruby#140 - Revert do/dop order change](https://github.com/atom/language-ruby/pull/140) - -### [language-sass](https://github.com/atom/language-sass) - -v0.45.0...v0.46.0 - -* [atom/language-sass#101 - Add individual border-radius properties](https://github.com/atom/language-sass/pull/101) - -### [language-text](https://github.com/atom/language-text) - -v0.7.0...v0.7.1 - -* [atom/language-text#5 - Add travis.yml](https://github.com/atom/language-text/pull/5) -* [atom/language-text#6 - Update legal date to 2016](https://github.com/atom/language-text/pull/6) - -### [language-xml](https://github.com/atom/language-xml) - -v0.34.2...v0.34.4 - -* [atom/language-xml#43 - Fix incorrect highlighting for empty element](https://github.com/atom/language-xml/pull/43) diff --git a/changes3.md b/changes3.md deleted file mode 100644 index 60d70731a..000000000 --- a/changes3.md +++ /dev/null @@ -1,414 +0,0 @@ -### Notable Changes - -* Crash Recovery -* Most Recently Used Tab Switching -* Windows Improvements -* Environment Patching on OS X -* Electron Update - -### [Atom Core](https://github.com/atom/atom) - -v1.6.0-beta8...v1.7.0-beta0 - -* [atom/atom#10747 - Clarify Windows build docs for VS2015 etc.](https://github.com/atom/atom/pull/10747) -* [atom/atom#10743 - Don't cascade on reload](https://github.com/atom/atom/pull/10743) -* [atom/atom#9198 - permit any whole number for tabLength](https://github.com/atom/atom/pull/9198) -* [atom/atom#10758 - Fix status in subdir](https://github.com/atom/atom/pull/10758) -* [atom/atom#10768 - Temporarily disable deserializers & viewProviders metadata fields](https://github.com/atom/atom/pull/10768) -* [atom/atom#10764 - Let packages define deserializers & view providers as main module methods](https://github.com/atom/atom/pull/10764) -* [atom/atom#10778 - Fix minor typo in CONTRIBUTING.md](https://github.com/atom/atom/pull/10778) -* [atom/atom#10789 - Upgrade autocomplete-plus to use new text-buffer API](https://github.com/atom/atom/pull/10789) -* [atom/atom#10797 - Fix status with multiple paths](https://github.com/atom/atom/pull/10797) -* [atom/atom#10797 - Fix status with multiple paths](https://github.com/atom/atom/pull/10797) -* [atom/atom#10792 - Fix TextEditorPresenter spec timeout.](https://github.com/atom/atom/pull/10792) -* [atom/atom#10803 - Fix typo in function call](https://github.com/atom/atom/pull/10803) -* [atom/atom#10828 - Fix another TextEditorPresenter spec race](https://github.com/atom/atom/pull/10828) -* [atom/atom#10827 - Port repository subdirectory status fixes to async repo](https://github.com/atom/atom/pull/10827) -* [atom/atom#10827 - Port repository subdirectory status fixes to async repo](https://github.com/atom/atom/pull/10827) -* [atom/atom#10838 - Log output for core specs](https://github.com/atom/atom/pull/10838) -* [atom/atom#10818 - Register Atom for file associations in Windows](https://github.com/atom/atom/pull/10818) -* [atom/atom#10605 - Periodically save state and store in indexedDB](https://github.com/atom/atom/pull/10605) -* [atom/atom#9627 - Update to Electron 0.36](https://github.com/atom/atom/pull/9627) -* [atom/atom#10835 - Add TextEditor.prototype.cursorsForScreenRowRange](https://github.com/atom/atom/pull/10835) -* [atom/atom#10795 - Remove unused Core Team Project Management labels](https://github.com/atom/atom/pull/10795) -* [atom/atom#10858 - Update grunt-electron-installer to latest version](https://github.com/atom/atom/pull/10858) -* [atom/atom#10846 - Add core setting for pending tabs configuration](https://github.com/atom/atom/pull/10846) -* [atom/atom#10846 - Add core setting for pending tabs configuration](https://github.com/atom/atom/pull/10846) -* [atom/atom#10873 - Avoid emitting config events while loading packages](https://github.com/atom/atom/pull/10873) -* [atom/atom#10872 - Terminate pending state for opened file if pending option is false](https://github.com/atom/atom/pull/10872) -* [atom/atom#10872 - Terminate pending state for opened file if pending option is false](https://github.com/atom/atom/pull/10872) -* [atom/atom#10863 - Compare markers instead of ranges in Selection](https://github.com/atom/atom/pull/10863) -* [atom/atom#10874 - Avoid Windows 260-character path limits in script\clean](https://github.com/atom/atom/pull/10874) -* [atom/atom#10861 - Compute line foldability lazily](https://github.com/atom/atom/pull/10861) -* [atom/atom#10843 - Bump packages to use async git](https://github.com/atom/atom/pull/10843) -* [atom/atom#10886 - Update Atom build directory in build instructions](https://github.com/atom/atom/pull/10886) -* [atom/atom#10885 - Cache regexes in LanguageMode.prototype.getRegexForProperty](https://github.com/atom/atom/pull/10885) -* [atom/atom#10888 - Load packages before deserializing state](https://github.com/atom/atom/pull/10888) -* [atom/atom#10870 - Add Issue template and extra version info](https://github.com/atom/atom/pull/10870) -* [atom/atom#10878 - Allow pasting white space when `autoIndentOnPaste` is enabled](https://github.com/atom/atom/pull/10878) -* [atom/atom#10895 - Fix some spec issues](https://github.com/atom/atom/pull/10895) -* [atom/atom#10901 - remove Open Roadmap menu item, fixes #10884](https://github.com/atom/atom/pull/10901) -* [atom/atom#10898 - Pass the notification manager when splitting panes](https://github.com/atom/atom/pull/10898) -* [atom/atom#10927 - Upgrade electron to fix command-backtick bug](https://github.com/atom/atom/pull/10927) -* [atom/atom#10926 - Fix case-preserving path relativization](https://github.com/atom/atom/pull/10926) -* [atom/atom#10926 - Fix case-preserving path relativization](https://github.com/atom/atom/pull/10926) -* [atom/atom#10921 - Add support for keybindings with keyup keystrokes](https://github.com/atom/atom/pull/10921) -* [atom/atom#10841 - Add the -a, --add CLI option](https://github.com/atom/atom/pull/10841) -* [atom/atom#10933 - Fix block decorations spec in text editor presenter](https://github.com/atom/atom/pull/10933) -* [atom/atom#10925 - Faster state serialization](https://github.com/atom/atom/pull/10925) -* [atom/atom#10967 - Fix a inconsistent getLineCount() use](https://github.com/atom/atom/pull/10967) -* [atom/atom#10967 - Fix a inconsistent getLineCount() use](https://github.com/atom/atom/pull/10967) -* [atom/atom#10975 - Update nodegit](https://github.com/atom/atom/pull/10975) -* [atom/atom#10975 - Update nodegit](https://github.com/atom/atom/pull/10975) -* [atom/atom#10326 - Fix Windows installer path update woes](https://github.com/atom/atom/pull/10326) -* [atom/atom#10959 - Refactor pending state to live in pane instead of items](https://github.com/atom/atom/pull/10959) -* [atom/atom#10959 - Refactor pending state to live in pane instead of items](https://github.com/atom/atom/pull/10959) -* [atom/atom#10984 - [WIP] experiment with circle ci](https://github.com/atom/atom/pull/10984) -* [atom/atom#10737 - Add MRU tab switching functionality](https://github.com/atom/atom/pull/10737) -* [atom/atom#11001 - onDidTerminatePendingState ➡︎ onItemDidTerminatePendingState](https://github.com/atom/atom/pull/11001) -* [atom/atom#11001 - onDidTerminatePendingState ➡︎ onItemDidTerminatePendingState](https://github.com/atom/atom/pull/11001) -* [atom/atom#10972 - Update build scripts to use the new railcar branching model](https://github.com/atom/atom/pull/10972) -* [atom/atom#10972 - Update build scripts to use the new railcar branching model](https://github.com/atom/atom/pull/10972) -* [atom/atom#11006 - Move spec from tabs package](https://github.com/atom/atom/pull/11006) -* [atom/atom#10851 - Registry for TextEditors](https://github.com/atom/atom/pull/10851) -* [atom/atom#11010 - Always strip git+ prefix and .git suffix from package repository URLs](https://github.com/atom/atom/pull/11010) -* [atom/atom#11011 - Autoscroll after consolidating selections](https://github.com/atom/atom/pull/11011) -* [atom/atom#11008 - Add documentation for order key in config.](https://github.com/atom/atom/pull/11008) -* [atom/atom#11009 - Don't destroy pane if replacing last pending item](https://github.com/atom/atom/pull/11009) -* [atom/atom#11009 - Don't destroy pane if replacing last pending item](https://github.com/atom/atom/pull/11009) -* [atom/atom#10675 - Expose application updater lifecycle events to packages](https://github.com/atom/atom/pull/10675) -* [atom/atom#11022 - Windows git fixes](https://github.com/atom/atom/pull/11022) -* [atom/atom#11022 - Windows git fixes](https://github.com/atom/atom/pull/11022) -* [atom/atom#11051 - Add new item before destroying pending item](https://github.com/atom/atom/pull/11051) -* [atom/atom#11051 - Add new item before destroying pending item](https://github.com/atom/atom/pull/11051) -* [atom/atom#11036 - Skip deleted directories when restoring application windows](https://github.com/atom/atom/pull/11036) -* [atom/atom#11063 - Show tooltip immediately if the tooltip trigger is manual](https://github.com/atom/atom/pull/11063) -* [atom/atom#10511 - Use ELECTRON_RUN_AS_NODE Variable Key](https://github.com/atom/atom/pull/10511) -* [atom/atom#10955 - TextEditor customization](https://github.com/atom/atom/pull/10955) -* [atom/atom#11065 - Default to auto height being true.](https://github.com/atom/atom/pull/11065) -* [atom/atom#11053 - Ensure atom.cmd --wait correctly waits in Windows cmd & powershell](https://github.com/atom/atom/pull/11053) -* [atom/atom#11060 - Serialize MarkerLayers only on quit](https://github.com/atom/atom/pull/11060) -* [atom/atom#11057 - Move Pane::addItem 'pending' option to options object](https://github.com/atom/atom/pull/11057) -* [atom/atom#11057 - Move Pane::addItem 'pending' option to options object](https://github.com/atom/atom/pull/11057) -* [atom/atom#11089 - Update nodegit](https://github.com/atom/atom/pull/11089) -* [atom/atom#11089 - Update nodegit](https://github.com/atom/atom/pull/11089) -* [atom/atom#11099 - Add zero to hexadecimal numbers below F (16)](https://github.com/atom/atom/pull/11099) -* [atom/atom#11101 - Emit status changes when anything changes](https://github.com/atom/atom/pull/11101) -* [atom/atom#11101 - Emit status changes when anything changes](https://github.com/atom/atom/pull/11101) -* [atom/atom#11077 - Scroll to cursor on unfold all](https://github.com/atom/atom/pull/11077) -* [atom/atom#11103 - Make cli atom --wait work on Cygwin](https://github.com/atom/atom/pull/11103) -* [atom/atom#11115 - Update nodegit](https://github.com/atom/atom/pull/11115) -* [atom/atom#11111 - Default the options parameter to an empty object](https://github.com/atom/atom/pull/11111) -* [atom/atom#11127 - Bump markdown-preview@v0.158.0](https://github.com/atom/atom/pull/11127) -* [atom/atom#8793 - squirrel-update test on desktop shortcut groups too many assertions](https://github.com/atom/atom/pull/8793) -* [atom/atom#11078 - Add TextEditors to the registry only when opting in](https://github.com/atom/atom/pull/11078) -* [atom/atom#11054 - Patch Environment On OSX And Allow A Different Environment Per Window](https://github.com/atom/atom/pull/11054) -* [atom/atom#11153 - Fix node env](https://github.com/atom/atom/pull/11153) -* [atom/atom#11162 - BufferedProcess: search only new data for new lines rather than entire buffer, take 2](https://github.com/atom/atom/pull/11162) -* [atom/atom#11166 - Note where GitRepositoryAsync deviates from its synchronous predecessor](https://github.com/atom/atom/pull/11166) - -### [one-dark-ui](https://github.com/atom/one-dark-ui) - -v1.1.9...v1.2.0 - -* [atom/one-dark-ui#113 - Specify config schema in package.json](https://github.com/atom/one-dark-ui/pull/113) -* [atom/one-dark-ui#121 - Fix typo in comment for ui-variables.less](https://github.com/atom/one-dark-ui/pull/121) - -### [one-light-ui](https://github.com/atom/one-light-ui) - -v1.1.9...v1.2.0 - -* [atom/one-light-ui#48 - Specify config schema in package.json](https://github.com/atom/one-light-ui/pull/48) - -### [about](https://github.com/atom/about) - -v1.3.0...v1.4.1 - -* [atom/about#6 - Specify deserializer in package.json](https://github.com/atom/about/pull/6) -* [atom/about#6 - Specify deserializer in package.json](https://github.com/atom/about/pull/6) -* [atom/about#13 - Specify deserializer method in package.json, the new way](https://github.com/atom/about/pull/13) -* [atom/about#12 - Show update information on the about page](https://github.com/atom/about/pull/12) -* [atom/about#15 - Move away from deprecated Electron require syntax](https://github.com/atom/about/pull/15) - -### [archive-view](https://github.com/atom/archive-view) - -v0.61.0...v0.61.1 - -* [atom/archive-view#32 - Specify deserializer in package.json](https://github.com/atom/archive-view/pull/32) - -### [autocomplete-plus](https://github.com/atom/autocomplete-plus) - -v2.25.0...v2.29.1 - -* [atom/autocomplete-plus#612 - bugfix: auto indentation after suggestion confirm](https://github.com/atom/autocomplete-plus/pull/612) -* [atom/autocomplete-plus#641 - Stop flickering when adjusting margins](https://github.com/atom/autocomplete-plus/pull/641) -* [atom/autocomplete-plus#637 - Move config schema to package.json](https://github.com/atom/autocomplete-plus/pull/637) -* [atom/autocomplete-plus#659 - Batch autocompletion show/hide](https://github.com/atom/autocomplete-plus/pull/659) -* [atom/autocomplete-plus#675 - :art: Clean up formatting of fileBlacklist setting description](https://github.com/atom/autocomplete-plus/pull/675) -* [atom/autocomplete-plus#672 - Redesign SymbolStore and SymbolProvider](https://github.com/atom/autocomplete-plus/pull/672) -* [atom/autocomplete-plus#667 - Add unicode support for \w regexps](https://github.com/atom/autocomplete-plus/pull/667) -* [atom/autocomplete-plus#681 - Fix maximum call stack size exceeded error](https://github.com/atom/autocomplete-plus/pull/681) - -### [autosave](https://github.com/atom/autosave) - -v0.23.0...v0.23.1 - -* [atom/autosave#56 - Move config schema to package.json](https://github.com/atom/autosave/pull/56) - -### [bracket-matcher](https://github.com/atom/bracket-matcher) - -v0.79.0...v0.81.0 - -* [atom/bracket-matcher#196 - Fix spelling in settings](https://github.com/atom/bracket-matcher/pull/196) -* [atom/bracket-matcher#197 - Move config schema to package.json](https://github.com/atom/bracket-matcher/pull/197) -* [atom/bracket-matcher#212 - Make updating matches faster for multi-cursor editing](https://github.com/atom/bracket-matcher/pull/212) -* [atom/bracket-matcher#219 - Clean up view subscriptions when editor is destroyed](https://github.com/atom/bracket-matcher/pull/219) -* [atom/bracket-matcher#221 - Enable bracket matching for elixir](https://github.com/atom/bracket-matcher/pull/221) - -### [deprecation-cop](https://github.com/atom/deprecation-cop) - -v0.54.0...v0.54.1 - -* [atom/deprecation-cop#65 - Move deserializer into package.json](https://github.com/atom/deprecation-cop/pull/65) -* [atom/deprecation-cop#65 - Move deserializer into package.json](https://github.com/atom/deprecation-cop/pull/65) -* [atom/deprecation-cop#67 - Specify deserializer method in package.json](https://github.com/atom/deprecation-cop/pull/67) - -### [fuzzy-finder](https://github.com/atom/fuzzy-finder) - -v0.94.0...v1.0.3 - -* [atom/fuzzy-finder#160 - Move config schema to package.json](https://github.com/atom/fuzzy-finder/pull/160) -* [atom/fuzzy-finder#167 - Async git](https://github.com/atom/fuzzy-finder/pull/167) -* [atom/fuzzy-finder#174 - Fix spec race](https://github.com/atom/fuzzy-finder/pull/174) -* [atom/fuzzy-finder#178 - Handle symlink project paths](https://github.com/atom/fuzzy-finder/pull/178) -* [atom/fuzzy-finder#180 - Return project paths correctly if last-opened path is not in project](https://github.com/atom/fuzzy-finder/pull/180) - -### [git-diff](https://github.com/atom/git-diff) - -v0.57.0...v1.0.1 - -* [atom/git-diff#81 - Move config schema to package.json](https://github.com/atom/git-diff/pull/81) -* [atom/git-diff#82 - Async git](https://github.com/atom/git-diff/pull/82) -* [atom/git-diff#95 - Catch errors from new files.](https://github.com/atom/git-diff/pull/95) - -### [grammar-selector](https://github.com/atom/grammar-selector) - -v0.48.0...v0.48.1 - -* [atom/grammar-selector#25 - Add description for config setting](https://github.com/atom/grammar-selector/pull/25) -* [atom/grammar-selector#29 - Move config schema to package.json](https://github.com/atom/grammar-selector/pull/29) - -### [image-view](https://github.com/atom/image-view) - -v0.56.0...v0.57.0 - -* [atom/image-view#40 - Zoom to fit](https://github.com/atom/image-view/pull/40) - -### [incompatible-packages](https://github.com/atom/incompatible-packages) - -v0.25.0...v0.25.1 - -* [atom/incompatible-packages#11 - Move deserializer to package.json](https://github.com/atom/incompatible-packages/pull/11) -* [atom/incompatible-packages#11 - Move deserializer to package.json](https://github.com/atom/incompatible-packages/pull/11) -* [atom/incompatible-packages#12 - Specify deserializer method in package.json](https://github.com/atom/incompatible-packages/pull/12) - -### [keybinding-resolver](https://github.com/atom/keybinding-resolver) - -v0.33.0...v0.35.0 - -* [atom/keybinding-resolver#23 - Update coffeelint support](https://github.com/atom/keybinding-resolver/pull/23) -* [atom/keybinding-resolver#37 - Show keyup events that match a binding](https://github.com/atom/keybinding-resolver/pull/37) - -### [line-ending-selector](https://github.com/atom/line-ending-selector) - -v0.3.0...v0.3.1 - -* [atom/line-ending-selector#17 - Move config schema to package.json](https://github.com/atom/line-ending-selector/pull/17) - -### [link](https://github.com/atom/link) - -v0.31.0...v0.31.1 - -* [atom/link#14 - Move away from deprecated Electron require syntax](https://github.com/atom/link/pull/14) - -### [markdown-preview](https://github.com/atom/markdown-preview) - -v0.157.2...v0.158.0 - -* [atom/markdown-preview#349 - Use new package.json fields (configSchema and deserializers)](https://github.com/atom/markdown-preview/pull/349) -* [atom/markdown-preview#349 - Use new package.json fields (configSchema and deserializers)](https://github.com/atom/markdown-preview/pull/349) -* [atom/markdown-preview#367 - Use new package.json fields to allow deferred loading](https://github.com/atom/markdown-preview/pull/367) -* [atom/markdown-preview#335 - Use GitHub style when "Save as HTML"](https://github.com/atom/markdown-preview/pull/335) - -### [notifications](https://github.com/atom/notifications) - -v0.62.1...v0.63.1 - -* [atom/notifications#105 - Move config schema to package.json](https://github.com/atom/notifications/pull/105) -* [atom/notifications#111 - Use https://git.io insead of http](https://github.com/atom/notifications/pull/111) -* [atom/notifications#113 - replace ATOM_HOME in issue title with generic placeholder](https://github.com/atom/notifications/pull/113) -* [atom/notifications#114 - Use bit.ly instead of git.io.](https://github.com/atom/notifications/pull/114) -* [atom/notifications#115 - URL shortening, take 2](https://github.com/atom/notifications/pull/115) - -### [open-on-github](https://github.com/atom/open-on-github) - -v0.41.0...v1.0.1 - -* [atom/open-on-github#59 - Move config schema to package.json](https://github.com/atom/open-on-github/pull/59) -* [atom/open-on-github#60 - Async git](https://github.com/atom/open-on-github/pull/60) -* [atom/open-on-github#66 - Move away from deprecated Electron require syntax](https://github.com/atom/open-on-github/pull/66) - -### [package-generator](https://github.com/atom/package-generator) - -v0.41.0...v1.0.0 - -* [atom/package-generator#37 - Move config schema to package.json](https://github.com/atom/package-generator/pull/37) -* [atom/package-generator#36 - Support JS package generation](https://github.com/atom/package-generator/pull/36) - -### [settings-view](https://github.com/atom/settings-view) - -v0.232.3...v0.235.0 - -* [atom/settings-view#731 - Specify deserializer in package.json](https://github.com/atom/settings-view/pull/731) -* [atom/settings-view#749 - Move away from deprecated Electron require syntax](https://github.com/atom/settings-view/pull/749) -* [atom/settings-view#750 - Another require fix for remote](https://github.com/atom/settings-view/pull/750) -* [atom/settings-view#743 - Display and manage Git-based packages](https://github.com/atom/settings-view/pull/743) -* [atom/settings-view#748 - Add defaults on focus](https://github.com/atom/settings-view/pull/748) -* [atom/settings-view#736 - Add collapsable section for option groups](https://github.com/atom/settings-view/pull/736) - -### [spell-check](https://github.com/atom/spell-check) - -v0.65.0...v0.67.0 - -* [atom/spell-check#103 - Update README.md](https://github.com/atom/spell-check/pull/103) -* [atom/spell-check#33 - Add feature: toggle on/off](https://github.com/atom/spell-check/pull/33) -* [atom/spell-check#108 - subscriptionsOfCommands -> commandSubscription](https://github.com/atom/spell-check/pull/108) -* [atom/spell-check#112 - Move config schema to package.json](https://github.com/atom/spell-check/pull/112) -* [atom/spell-check#114 - updates spellchecker to use system language](https://github.com/atom/spell-check/pull/114) - -### [status-bar](https://github.com/atom/status-bar) - -v0.83.0...v1.2.0 - -* [atom/status-bar#121 - Move config schema to package.json](https://github.com/atom/status-bar/pull/121) -* [atom/status-bar#114 - Async git](https://github.com/atom/status-bar/pull/114) -* [atom/status-bar#122 - Make updating info faster for multi-cursor edits](https://github.com/atom/status-bar/pull/122) -* [atom/status-bar#129 - Hide diff stats for new files](https://github.com/atom/status-bar/pull/129) -* [atom/status-bar#131 - Fix error with no active item](https://github.com/atom/status-bar/pull/131) -* [atom/status-bar#133 - Move to the footer](https://github.com/atom/status-bar/pull/133) - -### [styleguide](https://github.com/atom/styleguide) - -v0.45.1...v0.45.2 - -* [atom/styleguide#34 - Specify deserializer in package.json](https://github.com/atom/styleguide/pull/34) - -### [symbols-view](https://github.com/atom/symbols-view) - -v0.110.1...v0.112.0 - -* [atom/symbols-view#147 - Add support for ES2015 static methods](https://github.com/atom/symbols-view/pull/147) -* [atom/symbols-view#151 - Specify config schema in package.json](https://github.com/atom/symbols-view/pull/151) -* [atom/symbols-view#157 - Add es7 async functions to ctags](https://github.com/atom/symbols-view/pull/157) - -### [tabs](https://github.com/atom/tabs) - -v0.91.3...v0.92.0 - -* [atom/tabs#134 - Add "open in new window" option to tabs context menu](https://github.com/atom/tabs/pull/134) - -### [timecop](https://github.com/atom/timecop) - -v0.33.0...v0.33.1 - -* [atom/timecop#15 - Specify deserializer in package.json](https://github.com/atom/timecop/pull/15) - -### [tree-view](https://github.com/atom/tree-view) - -v0.201.5...v0.203.2 - -* [atom/tree-view#754 - Add option to auto-reveal tree view entries when they become the active pane item](https://github.com/atom/tree-view/pull/754) -* [atom/tree-view#755 - Add `focusOnReveal` option](https://github.com/atom/tree-view/pull/755) -* [atom/tree-view#695 - Make 'Move in trash' more verbose on failure, add a note for Linux](https://github.com/atom/tree-view/pull/695) -* [atom/tree-view#769 - Move away from deprecated Electron require syntax](https://github.com/atom/tree-view/pull/769) -* [atom/tree-view#768 - Fix exception when double clicking opened file after activation](https://github.com/atom/tree-view/pull/768) - -### [welcome](https://github.com/atom/welcome) - -v0.33.0...v0.34.0 - -* [atom/welcome#45 - Move config schema to package.json](https://github.com/atom/welcome/pull/45) -* [atom/welcome#47 - Change menu names for different platforms](https://github.com/atom/welcome/pull/47) - -### [whitespace](https://github.com/atom/whitespace) - -v0.32.1...v0.32.2 - -* [atom/whitespace#107 - Move config schema to package.json](https://github.com/atom/whitespace/pull/107) - -### [language-clojure](https://github.com/atom/language-clojure) - -v0.19.1...v0.20.0 - -* [atom/language-clojure#39 - Fix tokenization of sexp and map nested at beginning of another sexp](https://github.com/atom/language-clojure/pull/39) - -### [language-coffee-script](https://github.com/atom/language-coffee-script) - -v0.46.0...v0.46.1 - -* [atom/language-coffee-script#85 - Check for word boundaries when attempting to auto-indent](https://github.com/atom/language-coffee-script/pull/85) - -### [language-csharp](https://github.com/atom/language-csharp) - -v0.11.0...v0.12.0 - -* [atom/language-csharp#53 - Make nameof a keyword](https://github.com/atom/language-csharp/pull/53) -* [atom/language-csharp#54 - Make C# 6's when a keyword](https://github.com/atom/language-csharp/pull/54) - -### [language-gfm](https://github.com/atom/language-gfm) - -v0.84.0...v0.85.0 - -* [atom/language-gfm#140 - Highlight HTML entities inside bold, italic, and strikethrough text](https://github.com/atom/language-gfm/pull/140) - -### [language-html](https://github.com/atom/language-html) - -v0.44.0...v0.44.1 - -* [atom/language-html#108 - Fixes #102 - ng-template highlighting for script tags](https://github.com/atom/language-html/pull/108) - -### [language-json](https://github.com/atom/language-json) - -v0.17.4...v0.17.6 - -* [atom/language-json#42 - Add .jsonld file support](https://github.com/atom/language-json/pull/42) -* [atom/language-json#43 - add composer.lock files](https://github.com/atom/language-json/pull/43) -* [atom/language-json#44 - Add .tern-project and .tern-config to file types](https://github.com/atom/language-json/pull/44) - -### [language-ruby](https://github.com/atom/language-ruby) - -v0.68.0...v0.68.3 - -* [atom/language-ruby#135 - Adds cr (Crystal lang) to fileTypes for Ruby grammar](https://github.com/atom/language-ruby/pull/135) -* [atom/language-ruby#137 - Changes dop and do order priority](https://github.com/atom/language-ruby/pull/137) -* [atom/language-ruby#136 - Fixes for tokenization of Kernel methods ending in ? or !](https://github.com/atom/language-ruby/pull/136) -* [atom/language-ruby#140 - Revert do/dop order change](https://github.com/atom/language-ruby/pull/140) - -### [language-sass](https://github.com/atom/language-sass) - -v0.45.0...v0.46.0 - -* [atom/language-sass#101 - Add individual border-radius properties](https://github.com/atom/language-sass/pull/101) - -### [language-text](https://github.com/atom/language-text) - -v0.7.0...v0.7.1 - -* [atom/language-text#5 - Add travis.yml](https://github.com/atom/language-text/pull/5) -* [atom/language-text#6 - Update legal date to 2016](https://github.com/atom/language-text/pull/6) - -### [language-xml](https://github.com/atom/language-xml) - -v0.34.2...v0.34.4 - -* [atom/language-xml#43 - Fix incorrect highlighting for empty element](https://github.com/atom/language-xml/pull/43) diff --git a/fsck.txt b/fsck.txt deleted file mode 100644 index 2b4444696..000000000 --- a/fsck.txt +++ /dev/null @@ -1,84 +0,0 @@ -dangling blob 8e1e50c470eb9e341f51dd728c371ec9e6ba967f -dangling blob 312880903e5988a36556c71047cfab48f7cabb0a -dangling commit d14e40a5f4e51d329bf51eed9c5ac405cef8a4e7 -dangling blob ed4f3083f21ee505b90e49bc719126b3a0ca8717 -dangling commit 1981b88bcacc6a68408a7f21c840ebe50a3f76a1 -dangling commit b490508111afcabff3afd35b877f6afa0dd9ed57 -dangling commit 629c78769aa70249e6aceaed24ca5ed02a2b24e2 -dangling blob 3db2f0aa0e9ffeda9f614085d0b55ad3669439f8 -dangling commit 34f8200a8eecf014d43defaf8c6dd4e50e99d2ee -dangling commit 5d1009b9be44874de85d5cea8bfa8692fb1c9e93 -dangling commit 9b1e51e67ebc5de354858b555eb9b4e4c92bd4dd -dangling commit 4b8039b1d3f06b0f4e0f00ca195748ee8c1e32ed -dangling blob 7ca269d19d59fa66955e17e0861eb45652d1ba2d -dangling blob c8a8a9065ef35c949d666f7e4d7f743218ec6bc7 -dangling blob 9cb1c9364187f4a7665f1c7b5dee3c3fbd8f1c99 -dangling commit eec8e92d898d645a6ce442ae7c35ec9584396c58 -dangling blob dcc9712690f1c859158aebc40a56fbf27e833d76 -dangling blob 1fe2e195f16e0d750d8d995b87b8035a44b381d2 -dangling blob 50e29981374b4dfe0c4262a4cd73aea35ca76315 -dangling blob 8ce7099a661bb3a8f4918ecb57c0279b2bbc5830 -dangling commit 08e8d1fe6d81c1ef46bee56003c93b267bae2bd8 -dangling commit 07f9710adffbd72b6142892662135540826304f4 -dangling blob 52f9e94a0f8773639c2b934c0f6dc1480b857d84 -dangling commit e813da3e669e6ff94efa7ece2b0ff72781b4989d -dangling commit 0b30da3c7e93250e5b4ad07cf70a1d6477f14f1c -dangling commit 5d5c9a487e098214b4f956c20b6f6a07aec44954 -dangling commit c5916ad2075a90dc188e612c369affdf1dc0bc23 -dangling commit fe9efa90b53d85c6ee8a7d455f61cab0f1495bb4 -dangling blob 0e9f6a422bd61e3ac86b1317d6b12356328b6d66 -dangling commit 86d8bade0a8e08537f2c6a5f278bc426e3136310 -dangling commit 44fb9a4521e7f05cfb80fcd7057fdf75a9cd57f0 -dangling blob 0efdb238e664cf22a41e19fa327ab5582fba88f9 -dangling commit 5d0aabf92aa8606b08619ee7fc3654f14aa68bad -dangling commit 2d19dbc0fc42216b8892e261201cc53a378b5d7e -dangling commit 952263ff6ad07751137586a10b7766d292d1d249 -dangling commit 6d266b46d74b6acab92a3c8787973b46936d9fee -dangling commit 8346a391ecc5c8cbccecf1da359f3b975173dcf4 -dangling commit 0554dbd09d5d199382f612e9587e9044135e1266 -dangling commit 0f667bdaa3156942835a7f55bca7d116f02bc678 -dangling blob e27e1b3bbda2e7c75cfae7715ece1adec8b7b044 -dangling blob 9b9bbba7c0ef471ef195119db8f3bc2a903c5d5e -dangling commit 4fa8734a6537b8265064e31be40e1635996a543f -dangling blob 97b793cd3612ec045881b27477927cfc2a4a2083 -dangling blob 5ceaabe3ccb8c6b2c4ffde8dacb506601431ddbb -dangling commit eb13c4a2b1ee9854a238463ecc99d27dda8641d5 -dangling commit 14421ccec537f5b034d35bd3a6578c848532d06d -dangling commit 295d1c1d8df1ebf9c8477686d5a294135c9a413c -dangling commit 4e96d4ca9770282c120e6ab909f101fb7604999f -dangling commit e6b82450a1065c2be1313376be900014f98fb340 -dangling blob 4fc1849c928074155f28caa53b06ad61ad5b140e -dangling commit f7fe343d0a2eb0016b8fdad61512921dabd188db -dangling commit 0107a55520447e6b24ce224f5c2269c6e9e09f5b -dangling commit 2f2395435ea382bdf2aaa6f8a9d313bf337dbf96 -dangling commit 7c2ca56b3b5702e3a59c85540bd6c278415071ce -dangling commit 2d59bdd296fc06664c84ff6aeb98af3346b7b1fb -dangling blob 3c6a0514c057cd275aeb9df47f7ebbc3da1bcd1f -dangling blob b08cdddd620140a4bf79f6af455ce11979ecf2c2 -dangling commit a38e850b7648a7cef9d57725a27e298bc7d03cd4 -dangling blob b6af9d9e202c61bda1ac21b66f5d576805c617f6 -dangling commit 40c3bd9d3e7e23e1d6032180b37d5e49d16c0d5c -dangling commit 34d42d33ccd9cfec6c0aacbf145f4ef37353db98 -dangling commit 1ffdbd581d994dc633c1344747b4da809c400a08 -dangling blob 490d76d4f5acb1b44d7840bc8a3c7bda62d11d07 -dangling commit b72a56ec43568db6d299c1eba54726a6e3cc1c97 -dangling commit 693c0657285beff0d4aaca0641375283c5a56c74 -dangling blob c66026146ba927fc352451448472982845a3727e -dangling blob 1671d6b83241b80fa4ba87ac8a3644e8dd9dbb23 -dangling blob 6175d60e2dd97a0a67321ca2eeccf567b6c2d988 -dangling commit 9a9deedebdea0ae0b731791ecc08caea0623fa50 -dangling blob d0aa062f7df257a3120025cbbba8aa6b0b31d1bb -dangling blob 28b82ef47e6226d52364a2a675af3145987d0710 -dangling commit 43ea466643b314b20fe374b64dbd7a47816b962a -dangling blob 52ebb6f16e8b667cd321dea15461e0d8eb690ad8 -dangling blob 7bf4fec5c7b11c2dbd64556c3422cd4e838e6690 -dangling blob 45f506658f41781d33ec9b975a73c75199ef6757 -dangling blob ee27c707653877b4f644aecce296e68c23438d0f -dangling commit a357b773b0601eb14cdc921233fd50bb1c3e3286 -dangling commit cd598fcd5927deb5ad8443614c8bd6383ff3a765 -dangling commit 8960cfeed612451e272e9da34ac831c81b7b324c -dangling commit 3e6def8d1b7eac9fa9b3053d8824440770d01ecf -dangling commit f0782fb3b7b108201648ddc895e61cee26a2070d -dangling commit a87a2781ff62ef0810740dba02d70669dc1fa315 -dangling blob 3a7e57e783324bf9b95cf66502675f450c4fb6d0 -dangling blob 1ae10fac3e2c076e3053305ec2785a63a04101d2 From 07822b975a2096f9c28d6282b29da27a1b08ad39 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 13 Apr 2016 20:34:28 -0700 Subject: [PATCH 758/971] :arrow_up: open-on-github --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 59a0302c0..e2a809872 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "markdown-preview": "0.158.0", "metrics": "0.53.1", "notifications": "0.63.1", - "open-on-github": "1.0.1", + "open-on-github": "1.1.0", "package-generator": "1.0.0", "settings-view": "0.235.1", "snippets": "1.0.2", From e230841b57f13607ec6f38d8576b2ddf3dfa7315 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 13 Apr 2016 20:46:48 -0700 Subject: [PATCH 759/971] :arrow_up: tree-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e2a809872..4d43459ba 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.112.0", "tabs": "0.92.1", "timecop": "0.33.1", - "tree-view": "0.205.0", + "tree-view": "0.206.0", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From bbef0b45518ad20edf6ba426f72e1efc77adcf19 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 14 Apr 2016 11:19:45 +0200 Subject: [PATCH 760/971] :green_heart: --- src/text-editor-component.coffee | 2 +- src/text-editor.coffee | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 3faeab139..52a43db54 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -607,7 +607,7 @@ class TextEditorComponent clickedScreenRow = @screenPositionForMouseEvent(event).row clickedBufferRow = @editor.bufferRowForScreenRow(clickedScreenRow) initialScreenRange = @editor.screenRangeForBufferRange([[clickedBufferRow, 0], [clickedBufferRow + 1, 0]]) - @editor.addSelectionForScreenRange(initialScreenRange, preserveFolds: true, autoscroll: false) + @editor.addSelectionForScreenRange(initialScreenRange, autoscroll: false) @handleGutterDrag(initialScreenRange) onGutterShiftClick: (event) => diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 08694ec32..3311c0f7e 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2309,7 +2309,7 @@ class TextEditor extends Model # # Returns the added {Selection}. addSelectionForBufferRange: (bufferRange, options={}) -> - @selectionsMarkerLayer.markBufferRange(bufferRange, _.defaults({invalidate: 'never'}, options)) + @selectionsMarkerLayer.markBufferRange(bufferRange, {invalidate: 'never', reversed: options.reversed ? false}) @getLastSelection().autoscroll() unless options.autoscroll is false @getLastSelection() @@ -2322,7 +2322,7 @@ class TextEditor extends Model # # Returns the added {Selection}. addSelectionForScreenRange: (screenRange, options={}) -> - @selectionsMarkerLayer.markScreenRange(screenRange, _.defaults({invalidate: 'never'}, options)) + @selectionsMarkerLayer.markScreenRange(screenRange, {invalidate: 'never', reversed: options.reversed ? false}) @getLastSelection().autoscroll() unless options.autoscroll is false @getLastSelection() From 4adf23a5956e55ab113631f6652fda25438d032f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 14 Apr 2016 11:24:01 +0200 Subject: [PATCH 761/971] Unconditionally report deprecations on every test environment --- build/tasks/spec-task.coffee | 1 - spec/jasmine-test-runner.coffee | 12 +++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/build/tasks/spec-task.coffee b/build/tasks/spec-task.coffee index 40b7ad5ce..2e319a19c 100644 --- a/build/tasks/spec-task.coffee +++ b/build/tasks/spec-task.coffee @@ -18,7 +18,6 @@ module.exports = (grunt) -> packageSpecQueue = null logDeprecations = (label, {stderr}={}) -> - return unless process.env.JANKY_SHA1 or process.env.CI stderr ?= '' deprecatedStart = stderr.indexOf('Calls to deprecated functions') return if deprecatedStart is -1 diff --git a/spec/jasmine-test-runner.coffee b/spec/jasmine-test-runner.coffee index 5b5d4e225..dd8386f5d 100644 --- a/spec/jasmine-test-runner.coffee +++ b/spec/jasmine-test-runner.coffee @@ -1,3 +1,4 @@ +Grim = require 'grim' _ = require 'underscore-plus' fs = require 'fs-plus' path = require 'path' @@ -96,13 +97,10 @@ buildTerminalReporter = (logFile, resolveWithExitCode) -> log(str) onComplete: (runner) -> fs.closeSync(logStream) if logStream? - if process.env.JANKY_SHA1 or process.env.CI - grim = require 'grim' - - if grim.getDeprecationsLength() > 0 - grim.logDeprecations() - resolveWithExitCode(1) - return + if Grim.getDeprecationsLength() > 0 + Grim.logDeprecations() + resolveWithExitCode(1) + return if runner.results().failedCount > 0 resolveWithExitCode(1) From 303bb8c74e5274fcc2585acd53ca0d0df9459d95 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 14 Apr 2016 11:24:59 +0200 Subject: [PATCH 762/971] Remove this commit if it works on travis --- spec/fake-spec.coffee | 3 +++ spec/jasmine-test-runner.coffee | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 spec/fake-spec.coffee diff --git a/spec/fake-spec.coffee b/spec/fake-spec.coffee new file mode 100644 index 000000000..c14cdbb94 --- /dev/null +++ b/spec/fake-spec.coffee @@ -0,0 +1,3 @@ +fdescribe "remove this", -> + it "remove me", -> + require("grim").deprecate("foo") diff --git a/spec/jasmine-test-runner.coffee b/spec/jasmine-test-runner.coffee index dd8386f5d..fd0187298 100644 --- a/spec/jasmine-test-runner.coffee +++ b/spec/jasmine-test-runner.coffee @@ -25,7 +25,7 @@ module.exports = ({logFile, headless, testPaths, buildAtomEnvironment}) -> }) require './spec-helper' - disableFocusMethods() if process.env.JANKY_SHA1 or process.env.CI + # disableFocusMethods() if process.env.JANKY_SHA1 or process.env.CI requireSpecs(testPath) for testPath in testPaths setSpecType('user') From 61d8c6852c1577a439f87f57f87ddfad32052424 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 14 Apr 2016 15:07:50 +0200 Subject: [PATCH 763/971] Remove non-Mac run-specs code and use ELECTRON_ENABLE_LOGGING=true * Remove Windows/Linux run-specs code. There is little value in keeping those code paths, given that we don't run specs on those platforms; if we ever need it again, we can restore it from the git history. * Use ELECTRON_ENABLE_LOGGING=true. This allows us to capture the output of calls to `console.warn` and `console.log`, which are useful to log out Grim deprecations. * Remove `logDeprecations` in build/spec-task.coffee. This method used to format the output captured on stderr to strip out "[Console]" noise from deprecations. This code path was not running anymore because we started using stdio: 'inherit' in #10838, which prevents stderr output to be captured. This doesn't seem a huge deal, so long as those deprecations get logged to screen. --- build/tasks/spec-task.coffee | 121 ++++++----------------------------- 1 file changed, 21 insertions(+), 100 deletions(-) diff --git a/build/tasks/spec-task.coffee b/build/tasks/spec-task.coffee index 2e319a19c..b806ffcb7 100644 --- a/build/tasks/spec-task.coffee +++ b/build/tasks/spec-task.coffee @@ -5,40 +5,14 @@ temp = require('temp').track() _ = require 'underscore-plus' async = require 'async' -# TODO: This should really be parallel on every platform, however: -# - On Windows, our fixtures step on each others toes. -if process.platform is 'win32' - concurrency = 1 -else - concurrency = 2 - module.exports = (grunt) -> {isAtomPackage, spawn} = require('./task-helpers')(grunt) packageSpecQueue = null - logDeprecations = (label, {stderr}={}) -> - stderr ?= '' - deprecatedStart = stderr.indexOf('Calls to deprecated functions') - return if deprecatedStart is -1 - - grunt.log.error(label) - stderr = stderr.substring(deprecatedStart) - stderr = stderr.replace(/^\s*\[[^\]]+\]\s+/gm, '') - stderr = stderr.replace(/source: .*$/gm, '') - stderr = stderr.replace(/^"/gm, '') - stderr = stderr.replace(/",\s*$/gm, '') - grunt.log.error(stderr) - getAppPath = -> contentsDir = grunt.config.get('atom.contentsDir') - switch process.platform - when 'darwin' - path.join(contentsDir, 'MacOS', 'Atom') - when 'linux' - path.join(contentsDir, 'atom') - when 'win32' - path.join(contentsDir, 'atom.exe') + path.join(contentsDir, 'MacOS', 'Atom') runPackageSpecs = (callback) -> failedPackages = [] @@ -46,34 +20,17 @@ module.exports = (grunt) -> resourcePath = process.cwd() appPath = getAppPath() - # Ensure application is executable on Linux - fs.chmodSync(appPath, '755') if process.platform is 'linux' - packageSpecQueue = async.queue (packagePath, callback) -> - if process.platform in ['darwin', 'linux'] - options = - cmd: appPath - args: ['--test', "--resource-path=#{resourcePath}", path.join(packagePath, 'spec')] - opts: - cwd: packagePath - env: _.extend({}, process.env, ATOM_PATH: rootDir) - else if process.platform is 'win32' - options = - cmd: process.env.comspec - args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", "--log-file=ci.log", path.join(packagePath, 'spec')] - opts: - cwd: packagePath - env: _.extend({}, process.env, ATOM_PATH: rootDir) + options = + cmd: appPath + args: ['--test', "--resource-path=#{resourcePath}", path.join(packagePath, 'spec')] + opts: + cwd: packagePath + env: _.extend({}, process.env, ELECTRON_ENABLE_LOGGING: true, ATOM_PATH: rootDir) grunt.log.ok "Launching #{path.basename(packagePath)} specs." - spawn options, (error, results, code) -> - if process.platform is 'win32' - if error - process.stderr.write(fs.readFileSync(path.join(packagePath, 'ci.log'))) - fs.unlinkSync(path.join(packagePath, 'ci.log')) - + spawn options, (error) -> failedPackages.push path.basename(packagePath) if error - logDeprecations("#{path.basename(packagePath)} Specs", results) callback() modulesDirectory = path.resolve('node_modules') @@ -83,71 +40,38 @@ module.exports = (grunt) -> continue unless isAtomPackage(packagePath) packageSpecQueue.push(packagePath) - packageSpecQueue.concurrency = Math.max(1, concurrency - 1) + packageSpecQueue.concurrency = 1 packageSpecQueue.drain = -> callback(null, failedPackages) - runCoreSpecs = (callback, logOutput = false) -> + runCoreSpecs = (callback) -> appPath = getAppPath() resourcePath = process.cwd() coreSpecsPath = path.resolve('spec') - if process.platform in ['darwin', 'linux'] - options = - cmd: appPath - args: ['--test', "--resource-path=#{resourcePath}", coreSpecsPath, "--user-data-dir=#{temp.mkdirSync('atom-user-data-dir')}"] - opts: - env: _.extend({}, process.env, - ATOM_INTEGRATION_TESTS_ENABLED: true - ) - - else if process.platform is 'win32' - options = - cmd: process.env.comspec - args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", '--log-file=ci.log', coreSpecsPath] - opts: - env: _.extend({}, process.env, - ATOM_INTEGRATION_TESTS_ENABLED: true - ) - - if logOutput - options.opts.stdio = 'inherit' + options = + cmd: appPath + args: ['--test', "--resource-path=#{resourcePath}", coreSpecsPath, "--user-data-dir=#{temp.mkdirSync('atom-user-data-dir')}"] + opts: + env: _.extend({}, process.env, {ATOM_INTEGRATION_TESTS_ENABLED: true, ELECTRON_ENABLE_LOGGING: true}) + stdio: 'inherit' grunt.log.ok "Launching core specs." - spawn options, (error, results, code) -> - if process.platform is 'win32' - process.stderr.write(fs.readFileSync('ci.log')) if error - fs.unlinkSync('ci.log') - else - # TODO: Restore concurrency on Windows - packageSpecQueue?.concurrency = concurrency - logDeprecations('Core Specs', results) - + spawn options, (error, results) -> callback(null, error) grunt.registerTask 'run-specs', 'Run the specs', -> done = @async() startTime = Date.now() - method = - if concurrency is 1 - async.series - else - async.parallel - - # If we're just running the core specs then we won't have any output to - # indicate the tests actually *are* running. This upsets Travis: - # https://github.com/atom/atom/issues/10837. So pass the test output - # through. - runCoreSpecsWithLogging = (callback) -> runCoreSpecs(callback, true) specs = if process.env.ATOM_SPECS_TASK is 'packages' [runPackageSpecs] else if process.env.ATOM_SPECS_TASK is 'core' - [runCoreSpecsWithLogging] + [runCoreSpecs] else [runCoreSpecs, runPackageSpecs] - method specs, (error, results) -> + async.series specs, (error, results) -> failedPackages = [] coreSpecFailed = null @@ -159,13 +83,10 @@ module.exports = (grunt) -> [coreSpecFailed, failedPackages] = results elapsedTime = Math.round((Date.now() - startTime) / 100) / 10 - grunt.log.ok("Total spec time: #{elapsedTime}s using #{concurrency} cores") + grunt.log.ok("Total spec time: #{elapsedTime}s") failures = failedPackages failures.push "atom core" if coreSpecFailed grunt.log.error("[Error]".red + " #{failures.join(', ')} spec(s) failed") if failures.length > 0 - if process.platform is 'win32' and process.env.JANKY_SHA1 - done() - else - done(not coreSpecFailed and failedPackages.length is 0) + done(not coreSpecFailed and failedPackages.length is 0) From 147fed31234c6ed74d7f12aa6c5f3fc73dcd1a25 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 14 Apr 2016 15:46:50 +0200 Subject: [PATCH 764/971] Revert "Remove this commit if it works on travis" This reverts commit 303bb8c74e5274fcc2585acd53ca0d0df9459d95. --- spec/fake-spec.coffee | 3 --- spec/jasmine-test-runner.coffee | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 spec/fake-spec.coffee diff --git a/spec/fake-spec.coffee b/spec/fake-spec.coffee deleted file mode 100644 index c14cdbb94..000000000 --- a/spec/fake-spec.coffee +++ /dev/null @@ -1,3 +0,0 @@ -fdescribe "remove this", -> - it "remove me", -> - require("grim").deprecate("foo") diff --git a/spec/jasmine-test-runner.coffee b/spec/jasmine-test-runner.coffee index fd0187298..dd8386f5d 100644 --- a/spec/jasmine-test-runner.coffee +++ b/spec/jasmine-test-runner.coffee @@ -25,7 +25,7 @@ module.exports = ({logFile, headless, testPaths, buildAtomEnvironment}) -> }) require './spec-helper' - # disableFocusMethods() if process.env.JANKY_SHA1 or process.env.CI + disableFocusMethods() if process.env.JANKY_SHA1 or process.env.CI requireSpecs(testPath) for testPath in testPaths setSpecType('user') From 740e371e75b29f5aa777a084f979c8e344109303 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 14 Apr 2016 16:04:30 +0200 Subject: [PATCH 765/971] Put back multi-platform specs --- build/tasks/spec-task.coffee | 91 ++++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 19 deletions(-) diff --git a/build/tasks/spec-task.coffee b/build/tasks/spec-task.coffee index b806ffcb7..c1067231c 100644 --- a/build/tasks/spec-task.coffee +++ b/build/tasks/spec-task.coffee @@ -5,6 +5,13 @@ temp = require('temp').track() _ = require 'underscore-plus' async = require 'async' +# TODO: This should really be parallel on every platform, however: +# - On Windows, our fixtures step on each others toes. +if process.platform is 'win32' + concurrency = 1 +else + concurrency = 2 + module.exports = (grunt) -> {isAtomPackage, spawn} = require('./task-helpers')(grunt) @@ -12,7 +19,13 @@ module.exports = (grunt) -> getAppPath = -> contentsDir = grunt.config.get('atom.contentsDir') - path.join(contentsDir, 'MacOS', 'Atom') + switch process.platform + when 'darwin' + path.join(contentsDir, 'MacOS', 'Atom') + when 'linux' + path.join(contentsDir, 'atom') + when 'win32' + path.join(contentsDir, 'atom.exe') runPackageSpecs = (callback) -> failedPackages = [] @@ -20,16 +33,32 @@ module.exports = (grunt) -> resourcePath = process.cwd() appPath = getAppPath() + # Ensure application is executable on Linux + fs.chmodSync(appPath, '755') if process.platform is 'linux' + packageSpecQueue = async.queue (packagePath, callback) -> - options = - cmd: appPath - args: ['--test', "--resource-path=#{resourcePath}", path.join(packagePath, 'spec')] - opts: - cwd: packagePath - env: _.extend({}, process.env, ELECTRON_ENABLE_LOGGING: true, ATOM_PATH: rootDir) + if process.platform in ['darwin', 'linux'] + options = + cmd: appPath + args: ['--test', "--resource-path=#{resourcePath}", path.join(packagePath, 'spec')] + opts: + cwd: packagePath + env: _.extend({}, process.env, ELECTRON_ENABLE_LOGGING: true, ATOM_PATH: rootDir) + else if process.platform is 'win32' + options = + cmd: process.env.comspec + args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", "--log-file=ci.log", path.join(packagePath, 'spec')] + opts: + cwd: packagePath + env: _.extend({}, process.env, ELECTRON_ENABLE_LOGGING: true, ATOM_PATH: rootDir) grunt.log.ok "Launching #{path.basename(packagePath)} specs." - spawn options, (error) -> + spawn options, (error, results, code) -> + if process.platform is 'win32' + if error + process.stderr.write(fs.readFileSync(path.join(packagePath, 'ci.log'))) + fs.unlinkSync(path.join(packagePath, 'ci.log')) + failedPackages.push path.basename(packagePath) if error callback() @@ -40,7 +69,7 @@ module.exports = (grunt) -> continue unless isAtomPackage(packagePath) packageSpecQueue.push(packagePath) - packageSpecQueue.concurrency = 1 + packageSpecQueue.concurrency = Math.max(1, concurrency - 1) packageSpecQueue.drain = -> callback(null, failedPackages) runCoreSpecs = (callback) -> @@ -48,20 +77,41 @@ module.exports = (grunt) -> resourcePath = process.cwd() coreSpecsPath = path.resolve('spec') - options = - cmd: appPath - args: ['--test', "--resource-path=#{resourcePath}", coreSpecsPath, "--user-data-dir=#{temp.mkdirSync('atom-user-data-dir')}"] - opts: - env: _.extend({}, process.env, {ATOM_INTEGRATION_TESTS_ENABLED: true, ELECTRON_ENABLE_LOGGING: true}) - stdio: 'inherit' + if process.platform in ['darwin', 'linux'] + options = + cmd: appPath + args: ['--test', "--resource-path=#{resourcePath}", coreSpecsPath, "--user-data-dir=#{temp.mkdirSync('atom-user-data-dir')}"] + opts: + env: _.extend({}, process.env, {ELECTRON_ENABLE_LOGGING: true, ATOM_INTEGRATION_TESTS_ENABLED: true}) + stdio: 'inherit' + + else if process.platform is 'win32' + options = + cmd: process.env.comspec + args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", '--log-file=ci.log', coreSpecsPath] + opts: + env: _.extend({}, process.env, {ELECTRON_ENABLE_LOGGING: true, ATOM_INTEGRATION_TESTS_ENABLED: true}) + stdio: 'inherit' grunt.log.ok "Launching core specs." - spawn options, (error, results) -> + spawn options, (error, results, code) -> + if process.platform is 'win32' + process.stderr.write(fs.readFileSync('ci.log')) if error + fs.unlinkSync('ci.log') + else + # TODO: Restore concurrency on Windows + packageSpecQueue?.concurrency = concurrency + callback(null, error) grunt.registerTask 'run-specs', 'Run the specs', -> done = @async() startTime = Date.now() + method = + if concurrency is 1 + async.series + else + async.parallel specs = if process.env.ATOM_SPECS_TASK is 'packages' @@ -71,7 +121,7 @@ module.exports = (grunt) -> else [runCoreSpecs, runPackageSpecs] - async.series specs, (error, results) -> + method specs, (error, results) -> failedPackages = [] coreSpecFailed = null @@ -83,10 +133,13 @@ module.exports = (grunt) -> [coreSpecFailed, failedPackages] = results elapsedTime = Math.round((Date.now() - startTime) / 100) / 10 - grunt.log.ok("Total spec time: #{elapsedTime}s") + grunt.log.ok("Total spec time: #{elapsedTime}s using #{concurrency} cores") failures = failedPackages failures.push "atom core" if coreSpecFailed grunt.log.error("[Error]".red + " #{failures.join(', ')} spec(s) failed") if failures.length > 0 - done(not coreSpecFailed and failedPackages.length is 0) + if process.platform is 'win32' and process.env.JANKY_SHA1 + done() + else + done(not coreSpecFailed and failedPackages.length is 0) From 09b88cd1d5bf6da90e572741788e344f8cbfd7e1 Mon Sep 17 00:00:00 2001 From: Wliu Date: Thu, 14 Apr 2016 14:11:25 +0000 Subject: [PATCH 766/971] Fix defective spec and associate 'screen' as a renderer module --- spec/module-cache-spec.coffee | 2 +- src/module-cache.coffee | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/module-cache-spec.coffee b/spec/module-cache-spec.coffee index 3a995aec7..e70257103 100644 --- a/spec/module-cache-spec.coffee +++ b/spec/module-cache-spec.coffee @@ -14,7 +14,7 @@ describe 'ModuleCache', -> for builtinName, builtinPath of builtins expect(require.resolve(builtinName)).toBe builtinPath - expect(fs.isFileSync(require.resolve(builtinName))) + expect(fs.isFileSync(require.resolve(builtinName))).toBeTruthy() expect(Module._findPath.callCount).toBe 0 diff --git a/src/module-cache.coffee b/src/module-cache.coffee index a84251a93..9a2961bf6 100644 --- a/src/module-cache.coffee +++ b/src/module-cache.coffee @@ -203,12 +203,12 @@ registerBuiltins = (devMode) -> atomShellRoot = path.join(process.resourcesPath, 'atom.asar') commonRoot = path.join(atomShellRoot, 'common', 'api') - commonBuiltins = ['callbacks-registry', 'clipboard', 'crash-reporter', 'screen', 'shell'] + commonBuiltins = ['callbacks-registry', 'clipboard', 'crash-reporter', 'shell'] for builtin in commonBuiltins cache.builtins[builtin] = path.join(commonRoot, "#{builtin}.js") rendererRoot = path.join(atomShellRoot, 'renderer', 'api') - rendererBuiltins = ['ipc-renderer', 'remote'] + rendererBuiltins = ['ipc-renderer', 'remote', 'screen'] for builtin in rendererBuiltins cache.builtins[builtin] = path.join(rendererRoot, "#{builtin}.js") From 58f029e92d9434f52bf2353b598c831962bb60b0 Mon Sep 17 00:00:00 2001 From: Wliu Date: Thu, 14 Apr 2016 14:19:09 +0000 Subject: [PATCH 767/971] :memo: --- spec/module-cache-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/module-cache-spec.coffee b/spec/module-cache-spec.coffee index e70257103..4c0a549aa 100644 --- a/spec/module-cache-spec.coffee +++ b/spec/module-cache-spec.coffee @@ -8,7 +8,7 @@ describe 'ModuleCache', -> beforeEach -> spyOn(Module, '_findPath').andCallThrough() - it 'resolves atom shell module paths without hitting the filesystem', -> + it 'resolves Electron module paths without hitting the filesystem', -> builtins = ModuleCache.cache.builtins expect(Object.keys(builtins).length).toBeGreaterThan 0 From 04ff6e461108e0488cda1a731d5041b3148d8fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20L=C3=A1zaro=20Gallego?= Date: Thu, 14 Apr 2016 16:14:39 +0200 Subject: [PATCH 768/971] Fixes #4126 for fish-shell --- src/environment-helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/environment-helpers.js b/src/environment-helpers.js index e2baeb26b..151796f06 100644 --- a/src/environment-helpers.js +++ b/src/environment-helpers.js @@ -58,7 +58,7 @@ function getFromShell () { function needsPatching (options = { platform: process.platform, env: process.env }) { if (options.platform === 'darwin' && !options.env.PWD) { let shell = getUserShell() - if (shell.endsWith('csh') || shell.endsWith('tcsh')) { + if (shell.endsWith('csh') || shell.endsWith('tcsh') || shell.endsWith('fish')) { return false } return true From c95412b451c332d09513893e3562fe5f0ba5406f Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 14 Apr 2016 14:39:59 -0700 Subject: [PATCH 769/971] Add ELECTRON_NO_ATTACH_CONSOLE in BufferedNodeProcess (cherry picked from commit 5f0ad9e015a3e36b337182bca152783b9f9f637e) --- src/buffered-node-process.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/buffered-node-process.coffee b/src/buffered-node-process.coffee index 3b4916b24..406775277 100644 --- a/src/buffered-node-process.coffee +++ b/src/buffered-node-process.coffee @@ -47,6 +47,7 @@ class BufferedNodeProcess extends BufferedProcess options ?= {} options.env ?= Object.create(process.env) options.env['ELECTRON_RUN_AS_NODE'] = 1 + options.env['ELECTRON_NO_ATTACH_CONSOLE'] = 1 args = args?.slice() ? [] args.unshift(command) From 698854679072603f7d564fbab36ad6df8c7653be Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 14 Apr 2016 22:02:35 -0700 Subject: [PATCH 770/971] Update to use current APIs --- spec/pane-spec.coffee | 140 ++++++++++++++++++------------------------ src/pane.coffee | 32 +--------- 2 files changed, 62 insertions(+), 110 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 5b4830dd6..8abbb0ece 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -917,103 +917,81 @@ describe "Pane", -> expect(item1.save).not.toHaveBeenCalled() expect(pane.isDestroyed()).toBe false - it "does not destroy the pane if save fails and user clicks cancel", -> - pane = new Pane(items: [new Item("A"), new Item("B")]) - [item1, item2] = pane.getItems() + describe "when item fails to save", -> + [pane, item1, item2] = [] - item1.shouldPromptToSave = -> true - item1.getURI = -> "/test/path" + beforeEach -> + pane = new Pane({items: [new Item("A"), new Item("B")], applicationDelegate: atom.applicationDelegate, config: atom.config}) + [item1, item2] = pane.getItems() - item1.save = jasmine.createSpy("save").andCallFake -> - error = new Error("EACCES, permission denied '/test/path'") - error.path = '/test/path' - error.code = 'EACCES' - throw error + item1.shouldPromptToSave = -> true + item1.getURI = -> "/test/path" - confirmations = 0 - spyOn(atom, 'confirm').andCallFake -> - confirmations++ - if confirmations is 1 - return 0 - else - return 1 + item1.save = jasmine.createSpy("save").andCallFake -> + error = new Error("EACCES, permission denied '/test/path'") + error.path = '/test/path' + error.code = 'EACCES' + throw error - pane.close() + it "does not destroy the pane if save fails and user clicks cancel", -> + confirmations = 0 + confirm.andCallFake -> + confirmations++ + if confirmations is 1 + return 0 # click save + else + return 1 # click cancel - expect(atom.confirm).toHaveBeenCalled() - expect(confirmations).toBe(2) - expect(item1.save).toHaveBeenCalled() - expect(pane.isDestroyed()).toBe false + pane.close() - it "does destroy the pane if the user saves the file under a new name", -> - pane = new Pane(items: [new Item("A"), new Item("B")]) - [item1, item2] = pane.getItems() + expect(atom.applicationDelegate.confirm).toHaveBeenCalled() + expect(confirmations).toBe(2) + expect(item1.save).toHaveBeenCalled() + expect(pane.isDestroyed()).toBe false - item1.shouldPromptToSave = -> true - item1.getURI = -> "/test/path" + it "does destroy the pane if the user saves the file under a new name", -> + item1.saveAs = jasmine.createSpy("saveAs").andReturn(true) - item1.save = jasmine.createSpy("save").andCallFake -> - error = new Error("EACCES, permission denied '/test/path'") - error.path = '/test/path' - error.code = 'EACCES' - throw error + confirmations = 0 + confirm.andCallFake -> + confirmations++ + return 0 # save and then save as - item1.saveAs = jasmine.createSpy("saveAs").andReturn(true) + showSaveDialog.andReturn("new/path") - confirmations = 0 - spyOn(atom, 'confirm').andCallFake -> - confirmations++ - return 0 + pane.close() - spyOn(atom, 'showSaveDialogSync').andReturn("new/path") + expect(atom.applicationDelegate.confirm).toHaveBeenCalled() + expect(confirmations).toBe(2) + expect(atom.applicationDelegate.showSaveDialog).toHaveBeenCalled() + expect(item1.save).toHaveBeenCalled() + expect(item1.saveAs).toHaveBeenCalled() + expect(pane.isDestroyed()).toBe true - pane.close() + it "asks again if the saveAs also fails", -> + item1.saveAs = jasmine.createSpy("saveAs").andCallFake -> + error = new Error("EACCES, permission denied '/test/path'") + error.path = '/test/path' + error.code = 'EACCES' + throw error - expect(atom.confirm).toHaveBeenCalled() - expect(confirmations).toBe(2) - expect(atom.showSaveDialogSync).toHaveBeenCalled() - expect(item1.save).toHaveBeenCalled() - expect(item1.saveAs).toHaveBeenCalled() - expect(pane.isDestroyed()).toBe true + confirmations = 0 + confirm.andCallFake -> + confirmations++ + if confirmations < 3 + return 0 # save, save as, save as + return 2 # don't save - it "asks again if the saveAs also fails", -> - pane = new Pane(items: [new Item("A"), new Item("B")]) - [item1, item2] = pane.getItems() + showSaveDialog.andReturn("new/path") - item1.shouldPromptToSave = -> true - item1.getURI = -> "/test/path" - - item1.save = jasmine.createSpy("save").andCallFake -> - error = new Error("EACCES, permission denied '/test/path'") - error.path = '/test/path' - error.code = 'EACCES' - throw error - - item1.saveAs = jasmine.createSpy("saveAs").andCallFake -> - error = new Error("EACCES, permission denied '/test/path'") - error.path = '/test/path' - error.code = 'EACCES' - throw error - - - confirmations = 0 - spyOn(atom, 'confirm').andCallFake -> - confirmations++ - if confirmations < 3 - return 0 - return 2 - - spyOn(atom, 'showSaveDialogSync').andReturn("new/path") - - pane.close() - - expect(atom.confirm).toHaveBeenCalled() - expect(confirmations).toBe(3) - expect(atom.showSaveDialogSync).toHaveBeenCalled() - expect(item1.save).toHaveBeenCalled() - expect(item1.saveAs).toHaveBeenCalled() - expect(pane.isDestroyed()).toBe true + pane.close() + expect(atom.applicationDelegate.confirm).toHaveBeenCalled() + expect(confirmations).toBe(3) + expect(atom.applicationDelegate.showSaveDialog).toHaveBeenCalled() + expect(item1.save).toHaveBeenCalled() + expect(item1.saveAs).toHaveBeenCalled() + expect(pane.isDestroyed()).toBe true describe "::destroy()", -> [container, pane1, pane2] = [] diff --git a/src/pane.coffee b/src/pane.coffee index 0f33ef218..e1dbc8596 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -579,8 +579,8 @@ class Pane extends Model saveError = (error) => if error - chosen = atom.confirm - message: @getSaveErrorMessage(error) + chosen = @applicationDelegate.confirm + message: @getMessageForErrorCode(error) detailedMessage: "Your changes will be lost if you close this item without saving." buttons: ["Save as", "Cancel", "Don't save"] switch chosen @@ -657,7 +657,7 @@ class Pane extends Model nextAction?() catch error if nextAction - nextAction() + nextAction(error) else @handleSaveError(error, item) @@ -851,7 +851,6 @@ class Pane extends Model return false unless @promptToSaveItem(item) true -# <<<<<<< HEAD handleSaveError: (error, item) -> itemPath = error.path ? item?.getPath?() addWarningWithPath = (message, options) => @@ -868,31 +867,6 @@ class Pane extends Model else if errorMatch = /ENOTDIR, not a directory '([^']+)'/.exec(error.message) fileName = errorMatch[1] @notificationManager.addWarning("Unable to save file: A directory in the path '#{fileName}' could not be written to") -# ======= -# # Translate an error object to a human readable string -# getSaveErrorMessage: (error) -> -# if error.code is 'EISDIR' or error.message.endsWith('is a directory') -# "Unable to save file: #{error.message}." -# else if error.code is 'EACCES' and error.path? -# "Unable to save file: Permission denied '#{error.path}'." -# else if error.code in ['EPERM', 'EBUSY', 'UNKNOWN', 'EEXIST'] and error.path? -# "Unable to save file '#{error.path}': #{error.message}." -# else if error.code is 'EROFS' and error.path? -# "Unable to save file: Read-only file system '#{error.path}'." -# else if error.code is 'ENOSPC' and error.path? -# "Unable to save file: No space left on device '#{error.path}'." -# else if error.code is 'ENXIO' and error.path? -# "Unable to save file: No such device or address '#{error.path}'." -# else if errorMatch = /ENOTDIR, not a directory '([^']+)'/.exec(error.message) -# fileName = errorMatch[1] -# "Unable to save file: A directory in the path '#{fileName}' could not be written to." -# -# # Display a popup warning to the user -# handleSaveError: (error) -> -# errorMessage = Pane::getSaveErrorMessage(error) -# if errorMessage? -# atom.notifications.addWarning(errorMessage) -# >>>>>>> b5c9a90ae00ec44e91782b0d6e30be7ca2fea11c else throw error From 4e9048d22de5fce1fb4bce20515fcee96aae786d Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 14 Apr 2016 23:25:27 -0700 Subject: [PATCH 771/971] Create saveDialog helper function and name file in message --- spec/fixtures/sample.txt | 2 +- src/pane.coffee | 33 ++++++++++++++------------------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/spec/fixtures/sample.txt b/spec/fixtures/sample.txt index 9701a96c5..34dad6adf 100644 --- a/spec/fixtures/sample.txt +++ b/spec/fixtures/sample.txt @@ -1 +1 @@ -Some textSome textSome text. +Some textSome textSome textSome textSome textSome textSome textSome textSome textSome textSome text. diff --git a/src/pane.coffee b/src/pane.coffee index e1dbc8596..add6a365b 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -577,28 +577,23 @@ class Pane extends Model else return true + saveDialog = (saveButtonText, saveFn, message) => + chosen = @applicationDelegate.confirm + message: message + detailedMessage: "Your changes will be lost if you close this item without saving." + buttons: [saveButtonText, "Cancel", "Don't save"] + switch chosen + when 0 then saveFn(item, saveError) + when 1 then false + when 2 then true + saveError = (error) => if error - chosen = @applicationDelegate.confirm - message: @getMessageForErrorCode(error) - detailedMessage: "Your changes will be lost if you close this item without saving." - buttons: ["Save as", "Cancel", "Don't save"] - switch chosen - when 0 then @saveItemAs item, saveError - when 1 then false - when 2 then true + saveDialog("Save as", @saveItemAs, "'#{item.getTitle?() ? uri}' could not be saved.\nError: #{@getMessageForErrorCode(error.code)}") else true - chosen = @applicationDelegate.confirm - message: "'#{item.getTitle?() ? uri}' has changes, do you want to save them?" - detailedMessage: "Your changes will be lost if you close this item without saving." - buttons: ["Save", "Cancel", "Don't Save"] - - switch chosen - when 0 then @saveItem(item, saveError) - when 1 then false - when 2 then true + saveDialog("Save", @saveItem, "'#{item.getTitle?() ? uri}' has changes, do you want to save them?") # Public: Save the active item. saveActiveItem: (nextAction) -> @@ -619,7 +614,7 @@ class Pane extends Model # after the item is successfully saved, or with the error if it failed. # The return value will be that of `nextAction` or `undefined` if it was not # provided - saveItem: (item, nextAction) -> + saveItem: (item, nextAction) => if typeof item?.getURI is 'function' itemURI = item.getURI() else if typeof item?.getUri is 'function' @@ -645,7 +640,7 @@ class Pane extends Model # after the item is successfully saved, or with the error if it failed. # The return value will be that of `nextAction` or `undefined` if it was not # provided - saveItemAs: (item, nextAction) -> + saveItemAs: (item, nextAction) => return unless item?.saveAs? saveOptions = item.getSaveDialogOptions?() ? {} From b790716caae25587dc8f5bdc811c90c8304c0f40 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 14 Apr 2016 23:28:13 -0700 Subject: [PATCH 772/971] Revert sample.txt file --- spec/fixtures/sample.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/fixtures/sample.txt b/spec/fixtures/sample.txt index 34dad6adf..9701a96c5 100644 --- a/spec/fixtures/sample.txt +++ b/spec/fixtures/sample.txt @@ -1 +1 @@ -Some textSome textSome textSome textSome textSome textSome textSome textSome textSome textSome text. +Some textSome textSome text. From 7f77c677614d17ff56f9cf0a45c120b94a5500ca Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 15 Apr 2016 15:40:22 +0200 Subject: [PATCH 773/971] :fire: preserveFolds from marker's properties --- src/text-editor.coffee | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 3311c0f7e..19fddb2e1 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -178,7 +178,6 @@ class TextEditor extends Model @disposables.add @tokenizedBuffer.observeGrammar @subscribeToScopedConfigSettings for marker in @selectionsMarkerLayer.getMarkers() - marker.setProperties(preserveFolds: true) @addSelection(marker) @subscribeToTabTypeConfig() @@ -2306,9 +2305,13 @@ class TextEditor extends Model # * `options` (optional) An options {Object}: # * `reversed` A {Boolean} indicating whether to create the selection in a # reversed orientation. + # * `preserveFolds` A {Boolean}, which if `true` preserves the fold settings after the + # selection is set. # # Returns the added {Selection}. addSelectionForBufferRange: (bufferRange, options={}) -> + unless options.preserveFolds + @destroyFoldsIntersectingBufferRange(bufferRange) @selectionsMarkerLayer.markBufferRange(bufferRange, {invalidate: 'never', reversed: options.reversed ? false}) @getLastSelection().autoscroll() unless options.autoscroll is false @getLastSelection() @@ -2319,12 +2322,11 @@ class TextEditor extends Model # * `options` (optional) An options {Object}: # * `reversed` A {Boolean} indicating whether to create the selection in a # reversed orientation. - # + # * `preserveFolds` A {Boolean}, which if `true` preserves the fold settings after the + # selection is set. # Returns the added {Selection}. addSelectionForScreenRange: (screenRange, options={}) -> - @selectionsMarkerLayer.markScreenRange(screenRange, {invalidate: 'never', reversed: options.reversed ? false}) - @getLastSelection().autoscroll() unless options.autoscroll is false - @getLastSelection() + @addSelectionForBufferRange(@bufferRangeForScreenRange(screenRange), options) # Essential: Select from the current cursor position to the given position in # buffer coordinates. @@ -2638,13 +2640,11 @@ class TextEditor extends Model # # Returns the new {Selection}. addSelection: (marker, options={}) -> - unless marker.getProperties().preserveFolds - @destroyFoldsIntersectingBufferRange(marker.getBufferRange()) cursor = @addCursor(marker) selection = new Selection(_.extend({editor: this, marker, cursor, @clipboard}, options)) @selections.push(selection) selectionBufferRange = selection.getBufferRange() - @mergeIntersectingSelections(preserveFolds: marker.getProperties().preserveFolds) + @mergeIntersectingSelections(preserveFolds: options.preserveFolds) if selection.destroyed for selection in @getSelections() From b05ec8f6839fbe3ab0863faa6f777662efb39c49 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Fri, 15 Apr 2016 11:14:47 -0400 Subject: [PATCH 774/971] :arrow_up: exception-reporting@0.38.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4d43459ba..62bb8d5f1 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "deprecation-cop": "0.54.1", "dev-live-reload": "0.47.0", "encoding-selector": "0.21.0", - "exception-reporting": "0.38.0", + "exception-reporting": "0.38.1", "fuzzy-finder": "1.0.4", "git-diff": "1.0.1", "find-and-replace": "0.198.0", From fd483cb918c3e1d1fa89a6f9ca78596cf386e4f5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 15 Apr 2016 17:45:04 +0200 Subject: [PATCH 775/971] Fix deprecation --- src/selection.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/selection.coffee b/src/selection.coffee index bd15d1543..cee88caf2 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -508,7 +508,7 @@ class Selection extends Model if selectedRange.isEmpty() return if selectedRange.start.row is @editor.buffer.getLastRow() else - joinMarker = @editor.markBufferRange(selectedRange, invalidationStrategy: 'never') + joinMarker = @editor.markBufferRange(selectedRange, invalidate: 'never') rowCount = Math.max(1, selectedRange.getRowCount() - 1) for row in [0...rowCount] From 9b8cb237574bcec33101260334175622d5535d61 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 15 Apr 2016 10:48:35 -0700 Subject: [PATCH 776/971] Preserve the process.env magic for Windows --- src/environment-helpers.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/environment-helpers.js b/src/environment-helpers.js index e2baeb26b..8039e8537 100644 --- a/src/environment-helpers.js +++ b/src/environment-helpers.js @@ -67,9 +67,19 @@ function needsPatching (options = { platform: process.platform, env: process.env return false } +// Fix for #11302 because `process.env` on Windows is a magic object that offers case-insensitive +// environment variable matching. +function cloneEnv (env) { + for (var key in process.env) { + delete process.env[key] + } + + Object.assign(process.env, env) +} + function normalize (options = {}) { if (options && options.env) { - process.env = options.env + cloneEnv(options.env) } if (!options.env) { @@ -85,8 +95,8 @@ function normalize (options = {}) { // in #4126. Retain the original in case someone needs it. let shellEnv = getFromShell() if (shellEnv && shellEnv.PATH) { - process._originalEnv = process.env - process.env = shellEnv + process._originalEnv = Object.assign({}, process.env) + cloneEnv(shellEnv) } } } @@ -96,7 +106,7 @@ function replace (env) { return } - process.env = env + cloneEnv(env) } export default { getFromShell, needsPatching, normalize, replace } From 99e716f9ed2142e83c9d73b45aba42735d40ad94 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Fri, 15 Apr 2016 11:25:05 -0700 Subject: [PATCH 777/971] Fix specs failures on Windows including paths --- spec/atom-environment-spec.coffee | 6 ++++-- spec/git-repository-async-spec.js | 2 +- spec/git-spec.coffee | 2 +- spec/project-spec.coffee | 2 +- spec/spec-helper.coffee | 26 ++++++++++++++++---------- spec/text-editor-component-spec.js | 4 ++-- spec/text-editor-spec.coffee | 14 ++++++++------ spec/theme-manager-spec.coffee | 8 ++++---- spec/workspace-spec.coffee | 21 +++++++++++++++------ 9 files changed, 52 insertions(+), 33 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 5fd4b11f1..846083b0e 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -28,8 +28,10 @@ describe "AtomEnvironment", -> atom.setSize(originalSize.width, originalSize.height) it 'sets the size of the window, and can retrieve the size just set', -> - atom.setSize(100, 400) - expect(atom.getSize()).toEqual width: 100, height: 400 + newWidth = originalSize.width + 12 + newHeight = originalSize.height + 23 + atom.setSize(newWidth, newHeight) + expect(atom.getSize()).toEqual width: newWidth, height: newHeight describe ".isReleasedVersion()", -> it "returns false if the version is a SHA and true otherwise", -> diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 0442248e1..251734448 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -103,7 +103,7 @@ describe('GitRepositoryAsync', () => { it('returns the repository path for a repository path', async () => { repo = openFixture('master.git') const repoPath = await repo.getPath() - expect(repoPath).toBe(path.join(__dirname, 'fixtures', 'git', 'master.git')) + expect(repoPath).toBePath(path.join(__dirname, 'fixtures', 'git', 'master.git')) }) }) diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 3afd4da75..87aeab12a 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -33,7 +33,7 @@ describe "GitRepository", -> waitsForPromise -> repo.async.getPath().then(onSuccess) runs -> - expect(onSuccess.mostRecentCall.args[0]).toBe(repoPath) + expect(onSuccess.mostRecentCall.args[0]).toBePath(repoPath) describe "new GitRepository(path)", -> it "throws an exception when no repository is found", -> diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 499efd017..953cf103e 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -526,7 +526,7 @@ describe "Project", -> expect(atom.project.getDirectories()[1].contains(inputPath)).toBe true expect(atom.project.relativizePath(inputPath)).toEqual [ atom.project.getPaths()[1], - 'somewhere/something.txt' + path.join('somewhere', 'something.txt') ] describe ".contains(path)", -> diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index 1194f2f76..78a47ab0b 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -172,8 +172,8 @@ jasmine.useRealClock = -> addCustomMatchers = (spec) -> spec.addMatchers toBeInstanceOf: (expected) -> - notText = if @isNot then " not" else "" - this.message = => "Expected #{jasmine.pp(@actual)} to#{notText} be instance of #{expected.name} class" + beOrNotBe = if @isNot then "not be" else "be" + this.message = => "Expected #{jasmine.pp(@actual)} to #{beOrNotBe} instance of #{expected.name} class" @actual instanceof expected toHaveLength: (expected) -> @@ -181,32 +181,38 @@ addCustomMatchers = (spec) -> this.message = => "Expected object #{@actual} has no length method" false else - notText = if @isNot then " not" else "" - this.message = => "Expected object with length #{@actual.length} to#{notText} have length #{expected}" + haveOrNotHave = if @isNot then "not have" else "have" + this.message = => "Expected object with length #{@actual.length} to #{haveOrNotHave} length #{expected}" @actual.length is expected toExistOnDisk: (expected) -> - notText = this.isNot and " not" or "" - @message = -> return "Expected path '" + @actual + "'" + notText + " to exist." + toOrNotTo = this.isNot and "not to" or "to" + @message = -> return "Expected path '#{@actual}' #{toOrNotTo} exist." fs.existsSync(@actual) toHaveFocus: -> - notText = this.isNot and " not" or "" + toOrNotTo = this.isNot and "not to" or "to" if not document.hasFocus() console.error "Specs will fail because the Dev Tools have focus. To fix this close the Dev Tools or click the spec runner." - @message = -> return "Expected element '" + @actual + "' or its descendants" + notText + " to have focus." + @message = -> return "Expected element '#{@actual}' or its descendants #{toOrNotTo} have focus." element = @actual element = element.get(0) if element.jquery element is document.activeElement or element.contains(document.activeElement) toShow: -> - notText = if @isNot then " not" else "" + toOrNotTo = this.isNot and "not to" or "to" element = @actual element = element.get(0) if element.jquery - @message = -> return "Expected element '#{element}' or its descendants#{notText} to show." + @message = -> return "Expected element '#{element}' or its descendants #{toOrNotTo} show." element.style.display in ['block', 'inline-block', 'static', 'fixed'] + toBePath: (expected) -> + actualPath = path.normalize(@actual) + expectedPath = path.normalize(expected) + @message = -> return "Expected path '#{actualPath}' to be equal to '#{expectedPath}'." + actualPath is expectedPath + window.waitsForPromise = (args...) -> label = null if args.length > 1 diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index b031cba33..c1b2f8f46 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -4028,9 +4028,9 @@ describe('TextEditorComponent', function () { component.setFontSize(10) await nextViewUpdatePromise() expect(editor.getDefaultCharWidth()).toBeCloseTo(6, 0) - expect(editor.getKoreanCharWidth()).toBeCloseTo(9, 0) + expect(editor.getKoreanCharWidth()).toBeCloseTo(10, 0) expect(editor.getDoubleWidthCharWidth()).toBe(10) - expect(editor.getHalfWidthCharWidth()).toBe(5) + expect(editor.getHalfWidthCharWidth()).toBeCloseTo(6, 5) }) }) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 4de7168b7..7d85f1cad 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -182,17 +182,19 @@ describe "TextEditor", -> expect(editor1.getLongTitle()).toBe "readme \u2014 sample-theme-1" expect(editor2.getLongTitle()).toBe "readme \u2014 sample-theme-2" - it "returns '' when opened files have identical file and dir names", -> + it "returns '' when opened files have identical file names in subdirectories", -> editor1 = null editor2 = null + path1 = path.join('sample-theme-1', 'src', 'js') + path2 = path.join('sample-theme-2', 'src', 'js') waitsForPromise -> - atom.workspace.open(path.join('sample-theme-1', 'src', 'js', 'main.js')).then (o) -> + atom.workspace.open(path.join(path1, 'main.js')).then (o) -> editor1 = o - atom.workspace.open(path.join('sample-theme-2', 'src', 'js', 'main.js')).then (o) -> + atom.workspace.open(path.join(path2, 'main.js')).then (o) -> editor2 = o runs -> - expect(editor1.getLongTitle()).toBe "main.js \u2014 sample-theme-1/src/js" - expect(editor2.getLongTitle()).toBe "main.js \u2014 sample-theme-2/src/js" + expect(editor1.getLongTitle()).toBe "main.js \u2014 #{path1}" + expect(editor2.getLongTitle()).toBe "main.js \u2014 #{path2}" it "returns '' when opened files have identical file and same parent dir name", -> editor1 = null @@ -204,7 +206,7 @@ describe "TextEditor", -> editor2 = o runs -> expect(editor1.getLongTitle()).toBe "main.js \u2014 js" - expect(editor2.getLongTitle()).toBe "main.js \u2014 js/plugin" + expect(editor2.getLongTitle()).toBe "main.js \u2014 " + path.join('js', 'plugin') it "notifies ::onDidChangeTitle observers when the underlying buffer path changes", -> observed = [] diff --git a/spec/theme-manager-spec.coffee b/spec/theme-manager-spec.coffee index ea0ca19e6..0ab8a58b3 100644 --- a/spec/theme-manager-spec.coffee +++ b/spec/theme-manager-spec.coffee @@ -175,7 +175,7 @@ describe "atom.themes", -> expect(styleElementAddedHandler).toHaveBeenCalled() element = document.querySelector('head style[source-path*="css.css"]') - expect(element.getAttribute('source-path')).toBe atom.themes.stringToId(cssPath) + expect(element.getAttribute('source-path')).toBePath atom.themes.stringToId(cssPath) expect(element.textContent).toBe fs.readFileSync(cssPath, 'utf8') # doesn't append twice @@ -194,7 +194,7 @@ describe "atom.themes", -> expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1 element = document.querySelector('head style[source-path*="sample.less"]') - expect(element.getAttribute('source-path')).toBe atom.themes.stringToId(lessPath) + expect(element.getAttribute('source-path')).toBePath atom.themes.stringToId(lessPath) expect(element.textContent).toBe """ #header { color: #4d926f; @@ -213,9 +213,9 @@ describe "atom.themes", -> it "supports requiring css and less stylesheets without an explicit extension", -> atom.themes.requireStylesheet path.join(__dirname, 'fixtures', 'css') - expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path')).toBe atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('css.css')) + expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path')).toBePath atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('css.css')) atom.themes.requireStylesheet path.join(__dirname, 'fixtures', 'sample') - expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path')).toBe atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('sample.less')) + expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path')).toBePath atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('sample.less')) document.querySelector('head style[source-path*="css.css"]').remove() document.querySelector('head style[source-path*="sample.less"]').remove() diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 97139f6bb..fc22f07c3 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -80,7 +80,8 @@ describe "Workspace", -> expect(untitledEditor.getText()).toBe("An untitled editor.") expect(atom.workspace.getActiveTextEditor().getPath()).toBe editor3.getPath() - expect(document.title).toMatch ///^#{path.basename(editor3.getLongTitle())}\ \u2014\ #{atom.project.getPaths()[0]}/// + pathEscaped = escapeStringRegex(atom.project.getPaths()[0]) + expect(document.title).toMatch ///^#{path.basename(editor3.getLongTitle())}\ \u2014\ #{pathEscaped}/// describe "where there are no open panes or editors", -> it "constructs the view with no open editors", -> @@ -833,25 +834,29 @@ describe "Workspace", -> describe "when there is an active pane item", -> it "sets the title to the pane item's title plus the project path", -> item = atom.workspace.getActivePaneItem() - expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{atom.project.getPaths()[0]}/// + pathEscaped = escapeStringRegex(atom.project.getPaths()[0]) + expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{pathEscaped}/// describe "when the title of the active pane item changes", -> it "updates the window title based on the item's new title", -> editor = atom.workspace.getActivePaneItem() editor.buffer.setPath(path.join(temp.dir, 'hi')) - expect(document.title).toMatch ///^#{editor.getTitle()}\ \u2014\ #{atom.project.getPaths()[0]}/// + pathEscaped = escapeStringRegex(atom.project.getPaths()[0]) + expect(document.title).toMatch ///^#{editor.getTitle()}\ \u2014\ #{pathEscaped}/// describe "when the active pane's item changes", -> it "updates the title to the new item's title plus the project path", -> atom.workspace.getActivePane().activateNextItem() item = atom.workspace.getActivePaneItem() - expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{atom.project.getPaths()[0]}/// + pathEscaped = escapeStringRegex(atom.project.getPaths()[0]) + expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{pathEscaped}/// describe "when the last pane item is removed", -> it "updates the title to contain the project's path", -> atom.workspace.getActivePane().destroy() expect(atom.workspace.getActivePaneItem()).toBeUndefined() - expect(document.title).toMatch ///^#{atom.project.getPaths()[0]}/// + pathEscaped = escapeStringRegex(atom.project.getPaths()[0]) + expect(document.title).toMatch ///^#{pathEscaped}/// describe "when an inactive pane's item changes", -> it "does not update the title", -> @@ -875,7 +880,8 @@ describe "Workspace", -> }) workspace2.deserialize(atom.workspace.serialize(), atom.deserializers) item = workspace2.getActivePaneItem() - expect(document.title).toMatch ///^#{item.getLongTitle()}\ \u2014\ #{atom.project.getPaths()[0]}/// + pathEscaped = escapeStringRegex(atom.project.getPaths()[0]) + expect(document.title).toMatch ///^#{item.getLongTitle()}\ \u2014\ #{pathEscaped}/// workspace2.destroy() describe "document edited status", -> @@ -1610,3 +1616,6 @@ describe "Workspace", -> runs -> expect(pane.getPendingItem()).toBeFalsy() + + escapeStringRegex = (str) -> + str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') From 6d80dfe2280d782ade6ec43c04792fdeb2960624 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 15 Apr 2016 12:59:57 -0700 Subject: [PATCH 778/971] Make the clone function more generic --- src/environment-helpers.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/environment-helpers.js b/src/environment-helpers.js index 8039e8537..eac2b9c0a 100644 --- a/src/environment-helpers.js +++ b/src/environment-helpers.js @@ -68,18 +68,19 @@ function needsPatching (options = { platform: process.platform, env: process.env } // Fix for #11302 because `process.env` on Windows is a magic object that offers case-insensitive -// environment variable matching. -function cloneEnv (env) { - for (var key in process.env) { - delete process.env[key] +// environment variable matching. By always cloning to `process.env` we prevent breaking the +// underlying functionality. +function clone (to, from) { + for (var key in to) { + delete to[key] } - Object.assign(process.env, env) + Object.assign(to, from) } function normalize (options = {}) { if (options && options.env) { - cloneEnv(options.env) + clone(process.env, options.env) } if (!options.env) { @@ -96,7 +97,7 @@ function normalize (options = {}) { let shellEnv = getFromShell() if (shellEnv && shellEnv.PATH) { process._originalEnv = Object.assign({}, process.env) - cloneEnv(shellEnv) + clone(process.env, shellEnv) } } } @@ -106,7 +107,7 @@ function replace (env) { return } - cloneEnv(env) + clone(process.env, env) } export default { getFromShell, needsPatching, normalize, replace } From 30d4b937571c9e1fdbf46d0d0c71d284e56dc788 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 15 Apr 2016 19:56:25 -0700 Subject: [PATCH 779/971] :arrow_up: notifications@0.63.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 62bb8d5f1..8c0abfbf6 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "link": "0.31.1", "markdown-preview": "0.158.0", "metrics": "0.53.1", - "notifications": "0.63.1", + "notifications": "0.63.2", "open-on-github": "1.1.0", "package-generator": "1.0.0", "settings-view": "0.235.1", From 8ece1ee90929d7635d25bc76ea5f3aca442e977e Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 15 Apr 2016 20:00:21 -0700 Subject: [PATCH 780/971] :arrow_up: tabs@0.92.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8c0abfbf6..a44453cf0 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.2.3", "styleguide": "0.45.2", "symbols-view": "0.112.0", - "tabs": "0.92.1", + "tabs": "0.92.2", "timecop": "0.33.1", "tree-view": "0.206.0", "update-package-dependencies": "0.10.0", From e41b9f00fb7dc58299ab83aa111480960ab62ded Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Fri, 15 Apr 2016 21:23:00 -0700 Subject: [PATCH 781/971] Correctly autoindent single newline in Selection#insertText --- spec/selection-spec.coffee | 5 +++++ src/selection.coffee | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/selection-spec.coffee b/spec/selection-spec.coffee index 319e2d438..8830f4188 100644 --- a/spec/selection-spec.coffee +++ b/spec/selection-spec.coffee @@ -91,3 +91,8 @@ describe "Selection", -> expect(buffer.lineForRow(0)).toBe " " expect(buffer.lineForRow(1)).toBe " " expect(buffer.lineForRow(2)).toBe "" + + it "auto-indents if only a newline is inserted", -> + selection.setBufferRange [[2, 0], [3, 0]] + selection.insertText("\n", autoIndent: true) + expect(buffer.lineForRow(2)).toBe " " diff --git a/src/selection.coffee b/src/selection.coffee index e208ea55a..8d93d2fa1 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -378,7 +378,7 @@ class Selection extends Model indentAdjustment = @editor.indentLevelForLine(precedingText) - options.indentBasis @adjustIndent(remainingLines, indentAdjustment) - if options.autoIndent and NonWhitespaceRegExp.test(text) and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0 + if options.autoIndent and (text is '\n' or NonWhitespaceRegExp.test(text)) and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0 autoIndentFirstLine = true firstLine = precedingText + firstInsertedLine desiredIndentLevel = @editor.languageMode.suggestedIndentForLineAtBufferRow(oldBufferRange.start.row, firstLine) From 7160461ebcf8f3638476aeb13f728f4166d0bfa9 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sat, 16 Apr 2016 11:00:46 -0700 Subject: [PATCH 782/971] Copy active item when splitting from TextEditor context menu --- menus/darwin.cson | 8 ++++---- menus/linux.cson | 8 ++++---- menus/win32.cson | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/menus/darwin.cson b/menus/darwin.cson index 53cc4cbc4..bb3ce0acf 100644 --- a/menus/darwin.cson +++ b/menus/darwin.cson @@ -228,10 +228,10 @@ {label: 'Delete', command: 'core:delete'} {label: 'Select All', command: 'core:select-all'} {type: 'separator'} - {label: 'Split Up', command: 'pane:split-up'} - {label: 'Split Down', command: 'pane:split-down'} - {label: 'Split Left', command: 'pane:split-left'} - {label: 'Split Right', command: 'pane:split-right'} + {label: 'Split Up', command: 'pane:split-up-and-copy-active-item'} + {label: 'Split Down', command: 'pane:split-down-and-copy-active-item'} + {label: 'Split Left', command: 'pane:split-left-and-copy-active-item'} + {label: 'Split Right', command: 'pane:split-right-and-copy-active-item'} {label: 'Close Pane', command: 'pane:close'} {type: 'separator'} ] diff --git a/menus/linux.cson b/menus/linux.cson index be11c1430..b84fc8053 100644 --- a/menus/linux.cson +++ b/menus/linux.cson @@ -204,10 +204,10 @@ {label: 'Delete', command: 'core:delete'} {label: 'Select All', command: 'core:select-all'} {type: 'separator'} - {label: 'Split Up', command: 'pane:split-up'} - {label: 'Split Down', command: 'pane:split-down'} - {label: 'Split Left', command: 'pane:split-left'} - {label: 'Split Right', command: 'pane:split-right'} + {label: 'Split Up', command: 'pane:split-up-and-copy-active-item'} + {label: 'Split Down', command: 'pane:split-down-and-copy-active-item'} + {label: 'Split Left', command: 'pane:split-left-and-copy-active-item'} + {label: 'Split Right', command: 'pane:split-right-and-copy-active-item'} {label: 'Close Pane', command: 'pane:close'} {type: 'separator'} ] diff --git a/menus/win32.cson b/menus/win32.cson index 738b52f00..323db5d18 100644 --- a/menus/win32.cson +++ b/menus/win32.cson @@ -207,10 +207,10 @@ {label: 'Delete', command: 'core:delete'} {label: 'Select All', command: 'core:select-all'} {type: 'separator'} - {label: 'Split Up', command: 'pane:split-up'} - {label: 'Split Down', command: 'pane:split-down'} - {label: 'Split Left', command: 'pane:split-left'} - {label: 'Split Right', command: 'pane:split-right'} + {label: 'Split Up', command: 'pane:split-up-and-copy-active-item'} + {label: 'Split Down', command: 'pane:split-down-and-copy-active-item'} + {label: 'Split Left', command: 'pane:split-left-and-copy-active-item'} + {label: 'Split Right', command: 'pane:split-right-and-copy-active-item'} {label: 'Close Pane', command: 'pane:close'} {type: 'separator'} ] From af5cb863de32089ddf9ab9b89f28c61e1ff3653c Mon Sep 17 00:00:00 2001 From: Riley Dallas Date: Mon, 18 Apr 2016 10:12:05 -0500 Subject: [PATCH 783/971] :memo: Update links in keymap.cson comments --- dot-atom/keymap.cson | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dot-atom/keymap.cson b/dot-atom/keymap.cson index 10ad345d4..fd7c4f96e 100644 --- a/dot-atom/keymap.cson +++ b/dot-atom/keymap.cson @@ -18,15 +18,15 @@ # 'ctrl-p': 'core:move-down' # # You can find more information about keymaps in these guides: -# * https://atom.io/docs/latest/using-atom-basic-customization#customizing-key-bindings -# * https://atom.io/docs/latest/behind-atom-keymaps-in-depth +# * http://flight-manual.atom.io/using-atom/sections/basic-customization/#_customizing_keybindings +# * http://flight-manual.atom.io/behind-atom/sections/keymaps-in-depth/ # # If you're having trouble with your keybindings not working, try the # Keybinding Resolver: `Cmd+.` on OS X and `Ctrl+.` on other platforms. See the # Debugging Guide for more information: -# * https://atom.io/docs/latest/hacking-atom-debugging#check-the-keybindings +# * http://flight-manual.atom.io/hacking-atom/sections/debugging/#check-the-keybindings # # This file uses CoffeeScript Object Notation (CSON). # If you are unfamiliar with CSON, you can read more about it in the # Atom Flight Manual: -# https://atom.io/docs/latest/using-atom-basic-customization#cson +# http://flight-manual.atom.io/using-atom/sections/basic-customization/#_cson From b2aad098e118e6d1ba637a6088b1c831e5c0e402 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sat, 16 Apr 2016 11:14:21 -0700 Subject: [PATCH 784/971] Correctly autoindent \r\n in Selection#insertText --- spec/selection-spec.coffee | 5 +++++ src/selection.coffee | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/spec/selection-spec.coffee b/spec/selection-spec.coffee index 8830f4188..18095d6f8 100644 --- a/spec/selection-spec.coffee +++ b/spec/selection-spec.coffee @@ -96,3 +96,8 @@ describe "Selection", -> selection.setBufferRange [[2, 0], [3, 0]] selection.insertText("\n", autoIndent: true) expect(buffer.lineForRow(2)).toBe " " + + it "auto-indents if only a carriage return + newline is inserted", -> + selection.setBufferRange [[2, 0], [3, 0]] + selection.insertText("\r\n", autoIndent: true) + expect(buffer.lineForRow(2)).toBe " " diff --git a/src/selection.coffee b/src/selection.coffee index 8d93d2fa1..2937baaee 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -378,7 +378,8 @@ class Selection extends Model indentAdjustment = @editor.indentLevelForLine(precedingText) - options.indentBasis @adjustIndent(remainingLines, indentAdjustment) - if options.autoIndent and (text is '\n' or NonWhitespaceRegExp.test(text)) and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0 + textIsAutoIndentable = text is '\n' or text is '\r\n' or NonWhitespaceRegExp.test(text) + if options.autoIndent and textIsAutoIndentable and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0 autoIndentFirstLine = true firstLine = precedingText + firstInsertedLine desiredIndentLevel = @editor.languageMode.suggestedIndentForLineAtBufferRow(oldBufferRange.start.row, firstLine) From 92fd313a6ac5f19a52494139ec0921b6cff88d4c Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 18 Apr 2016 16:29:58 -0400 Subject: [PATCH 785/971] :arrow_up: language-gfm@0.86.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a44453cf0..b4b228009 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ "language-coffee-script": "0.46.1", "language-csharp": "0.12.1", "language-css": "0.36.1", - "language-gfm": "0.85.0", + "language-gfm": "0.86.0", "language-git": "0.12.1", "language-go": "0.42.0", "language-html": "0.44.1", From 73f65fc8218bd5f16eabb602fc8228290308004c Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 18 Apr 2016 16:30:43 -0400 Subject: [PATCH 786/971] :arrow_up: language-xml@0.34.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b4b228009..ef5835306 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "language-text": "0.7.1", "language-todo": "0.27.0", "language-toml": "0.18.0", - "language-xml": "0.34.4", + "language-xml": "0.34.5", "language-yaml": "0.25.2" }, "private": true, From ee4ca3fd6cd7cee084a9afb8f1e280a165394844 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 18 Apr 2016 16:32:20 -0400 Subject: [PATCH 787/971] :arrow_up: language-sql@0.21.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ef5835306..128c40e43 100644 --- a/package.json +++ b/package.json @@ -143,7 +143,7 @@ "language-sass": "0.46.0", "language-shellscript": "0.21.1", "language-source": "0.9.0", - "language-sql": "0.20.0", + "language-sql": "0.21.0", "language-text": "0.7.1", "language-todo": "0.27.0", "language-toml": "0.18.0", From 89bc3c888800711e0dbd3ca82d8b5c272d86d54d Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 18 Apr 2016 17:31:35 -0400 Subject: [PATCH 788/971] :arrow_up: language-git@0.13.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 128c40e43..4bad84f77 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ "language-csharp": "0.12.1", "language-css": "0.36.1", "language-gfm": "0.86.0", - "language-git": "0.12.1", + "language-git": "0.13.0", "language-go": "0.42.0", "language-html": "0.44.1", "language-hyperlink": "0.16.0", From 8516f1914e676a3daf4d47ac7537a2c779aa49da Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 18 Apr 2016 15:20:18 -0700 Subject: [PATCH 789/971] :arrow_up: fuzzy-finder@1.0.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4bad84f77..1dc192378 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "dev-live-reload": "0.47.0", "encoding-selector": "0.21.0", "exception-reporting": "0.38.1", - "fuzzy-finder": "1.0.4", + "fuzzy-finder": "1.0.5", "git-diff": "1.0.1", "find-and-replace": "0.198.0", "go-to-line": "0.30.0", From 53b516a6ec21b7f7e26b4577e1e3fd1759520eb5 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 18 Apr 2016 20:48:34 -0700 Subject: [PATCH 790/971] Quote spaces in paths on Win cmd line --- resources/win/atom.cmd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/win/atom.cmd b/resources/win/atom.cmd index a1af5cd53..8a4fed05c 100644 --- a/resources/win/atom.cmd +++ b/resources/win/atom.cmd @@ -2,6 +2,7 @@ SET EXPECT_OUTPUT= SET WAIT= +SET PSARGS=%* FOR %%a IN (%*) DO ( IF /I "%%a"=="-f" SET EXPECT_OUTPUT=YES @@ -25,7 +26,7 @@ FOR %%a IN (%*) DO ( IF "%EXPECT_OUTPUT%"=="YES" ( SET ELECTRON_ENABLE_LOGGING=YES IF "%WAIT%"=="YES" ( - powershell -noexit "%~dp0\..\..\atom.exe" --pid=$pid %* ; wait-event + powershell -noexit "Start-Process -FilePath \"%~dp0\..\..\atom.exe\" -ArgumentList \"--pid=$pid $env:PSARGS\" ; wait-event" ) ELSE ( "%~dp0\..\..\atom.exe" %* ) From 1500381ac9216bd533199f5b59460b5ac596527c Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 19 Apr 2016 14:25:44 -0700 Subject: [PATCH 791/971] Tweaks to the specs improvements from feedback --- spec/git-repository-async-spec.js | 2 +- spec/git-spec.coffee | 2 +- spec/spec-helper.coffee | 2 +- spec/text-editor-component-spec.js | 32 +++++++++++++++++++++++------- spec/theme-manager-spec.coffee | 8 ++++---- 5 files changed, 32 insertions(+), 14 deletions(-) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 251734448..224280b39 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -103,7 +103,7 @@ describe('GitRepositoryAsync', () => { it('returns the repository path for a repository path', async () => { repo = openFixture('master.git') const repoPath = await repo.getPath() - expect(repoPath).toBePath(path.join(__dirname, 'fixtures', 'git', 'master.git')) + expect(repoPath).toEqualPath(path.join(__dirname, 'fixtures', 'git', 'master.git')) }) }) diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 87aeab12a..7d9e9bbd4 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -33,7 +33,7 @@ describe "GitRepository", -> waitsForPromise -> repo.async.getPath().then(onSuccess) runs -> - expect(onSuccess.mostRecentCall.args[0]).toBePath(repoPath) + expect(onSuccess.mostRecentCall.args[0]).toEqualPath(repoPath) describe "new GitRepository(path)", -> it "throws an exception when no repository is found", -> diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index 78a47ab0b..9c4e09da0 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -207,7 +207,7 @@ addCustomMatchers = (spec) -> @message = -> return "Expected element '#{element}' or its descendants #{toOrNotTo} show." element.style.display in ['block', 'inline-block', 'static', 'fixed'] - toBePath: (expected) -> + toEqualPath: (expected) -> actualPath = path.normalize(@actual) expectedPath = path.normalize(expected) @message = -> return "Expected path '#{actualPath}' to be equal to '#{expectedPath}'." diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index c1b2f8f46..1b3a7b5c8 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -4022,15 +4022,33 @@ describe('TextEditorComponent', function () { }) }) - describe('when changing the font', async function () { - it('measures the default char, the korean char, the double width char and the half width char widths', async function () { - expect(editor.getDefaultCharWidth()).toBeCloseTo(12, 0) + describe('when decreasing the fontSize', async function () { + it('decreases the widths of the korean char, the double width char and the half width char', async function () { + originalDefaultCharWidth = editor.getDefaultCharWidth() + koreanDefaultCharWidth = editor.getKoreanCharWidth() + doubleWidthDefaultCharWidth = editor.getDoubleWidthCharWidth() + halfWidthDefaultCharWidth = editor.getHalfWidthCharWidth() component.setFontSize(10) await nextViewUpdatePromise() - expect(editor.getDefaultCharWidth()).toBeCloseTo(6, 0) - expect(editor.getKoreanCharWidth()).toBeCloseTo(10, 0) - expect(editor.getDoubleWidthCharWidth()).toBe(10) - expect(editor.getHalfWidthCharWidth()).toBeCloseTo(6, 5) + expect(editor.getDefaultCharWidth()).toBeLessThan(originalDefaultCharWidth) + expect(editor.getKoreanCharWidth()).toBeLessThan(koreanDefaultCharWidth) + expect(editor.getDoubleWidthCharWidth()).toBeLessThan(doubleWidthDefaultCharWidth) + expect(editor.getHalfWidthCharWidth()).toBeLessThan(halfWidthDefaultCharWidth) + }) + }) + + describe('when increasing the fontSize', function() { + it('increases the widths of the korean char, the double width char and the half width char', async function () { + originalDefaultCharWidth = editor.getDefaultCharWidth() + koreanDefaultCharWidth = editor.getKoreanCharWidth() + doubleWidthDefaultCharWidth = editor.getDoubleWidthCharWidth() + halfWidthDefaultCharWidth = editor.getHalfWidthCharWidth() + component.setFontSize(25) + await nextViewUpdatePromise() + expect(editor.getDefaultCharWidth()).toBeGreaterThan(originalDefaultCharWidth) + expect(editor.getKoreanCharWidth()).toBeGreaterThan(koreanDefaultCharWidth) + expect(editor.getDoubleWidthCharWidth()).toBeGreaterThan(doubleWidthDefaultCharWidth) + expect(editor.getHalfWidthCharWidth()).toBeGreaterThan(halfWidthDefaultCharWidth) }) }) diff --git a/spec/theme-manager-spec.coffee b/spec/theme-manager-spec.coffee index 0ab8a58b3..47b848809 100644 --- a/spec/theme-manager-spec.coffee +++ b/spec/theme-manager-spec.coffee @@ -175,7 +175,7 @@ describe "atom.themes", -> expect(styleElementAddedHandler).toHaveBeenCalled() element = document.querySelector('head style[source-path*="css.css"]') - expect(element.getAttribute('source-path')).toBePath atom.themes.stringToId(cssPath) + expect(element.getAttribute('source-path')).toEqualPath atom.themes.stringToId(cssPath) expect(element.textContent).toBe fs.readFileSync(cssPath, 'utf8') # doesn't append twice @@ -194,7 +194,7 @@ describe "atom.themes", -> expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1 element = document.querySelector('head style[source-path*="sample.less"]') - expect(element.getAttribute('source-path')).toBePath atom.themes.stringToId(lessPath) + expect(element.getAttribute('source-path')).toEqualPath atom.themes.stringToId(lessPath) expect(element.textContent).toBe """ #header { color: #4d926f; @@ -213,9 +213,9 @@ describe "atom.themes", -> it "supports requiring css and less stylesheets without an explicit extension", -> atom.themes.requireStylesheet path.join(__dirname, 'fixtures', 'css') - expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path')).toBePath atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('css.css')) + expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path')).toEqualPath atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('css.css')) atom.themes.requireStylesheet path.join(__dirname, 'fixtures', 'sample') - expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path')).toBePath atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('sample.less')) + expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path')).toEqualPath atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('sample.less')) document.querySelector('head style[source-path*="css.css"]').remove() document.querySelector('head style[source-path*="sample.less"]').remove() From 405a16ecdc2e08ed1ce044a5c5992655ade2301e Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 19 Apr 2016 20:38:25 -0400 Subject: [PATCH 792/971] :arrow_up: language-java@0.18.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1dc192378..84ec5a36e 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ "language-go": "0.42.0", "language-html": "0.44.1", "language-hyperlink": "0.16.0", - "language-java": "0.17.0", + "language-java": "0.18.0", "language-javascript": "0.110.0", "language-json": "0.18.0", "language-less": "0.29.3", From 9feb27e10775dca6f6722c5b1e7ddc771bd8086c Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 19 Apr 2016 20:39:24 -0400 Subject: [PATCH 793/971] :arrow_up: language-sass@0.47.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 84ec5a36e..7c9e37363 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "language-python": "0.43.1", "language-ruby": "0.68.5", "language-ruby-on-rails": "0.25.0", - "language-sass": "0.46.0", + "language-sass": "0.47.0", "language-shellscript": "0.21.1", "language-source": "0.9.0", "language-sql": "0.21.0", From 89a5d01968a6615558d15706ded86dec29652cdc Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 19 Apr 2016 20:40:12 -0400 Subject: [PATCH 794/971] :arrow_up: language-coffee-script@0.47.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7c9e37363..436534d72 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ "wrap-guide": "0.38.1", "language-c": "0.51.3", "language-clojure": "0.20.0", - "language-coffee-script": "0.46.1", + "language-coffee-script": "0.47.0", "language-csharp": "0.12.1", "language-css": "0.36.1", "language-gfm": "0.86.0", From 25895e1364c83325407213f545e513ac3b5d3a5a Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 19 Apr 2016 20:40:49 -0400 Subject: [PATCH 795/971] :arrow_up: language-yaml@0.26.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 436534d72..19cc758e7 100644 --- a/package.json +++ b/package.json @@ -148,7 +148,7 @@ "language-todo": "0.27.0", "language-toml": "0.18.0", "language-xml": "0.34.5", - "language-yaml": "0.25.2" + "language-yaml": "0.26.0" }, "private": true, "scripts": { From ea5ad4ae59833789ebbfb7d60e8d9c6a895284cc Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 19 Apr 2016 21:31:53 -0400 Subject: [PATCH 796/971] Coffeescript comment tokenization no longer matches the newline character --- spec/tokenized-buffer-spec.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index 88c095f68..7a1f4d221 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -445,13 +445,12 @@ describe "TokenizedBuffer", -> expect(screenLine0.text).toBe "# Econ 101#{tabAsSpaces}" {tokens} = screenLine0 - expect(tokens.length).toBe 4 + expect(tokens.length).toBe 3 expect(tokens[0].value).toBe "#" expect(tokens[1].value).toBe " Econ 101" expect(tokens[2].value).toBe tabAsSpaces expect(tokens[2].scopes).toEqual tokens[1].scopes expect(tokens[2].isAtomic).toBeTruthy() - expect(tokens[3].value).toBe "" expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "#{tabAsSpaces} buy()#{tabAsSpaces}while supply > demand" From 1e147fbaf161c27d4382f63b32303a6b2250321b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 20 Apr 2016 09:33:04 +0200 Subject: [PATCH 797/971] :arrow_up: about --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 19cc758e7..8fbc557d4 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "one-light-syntax": "1.2.0", "solarized-dark-syntax": "1.0.2", "solarized-light-syntax": "1.0.2", - "about": "1.5.0", + "about": "1.5.1", "archive-view": "0.61.1", "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.1", From 9ff5fb21fb0cccea293e43a36d3c04edd989df6c Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 20 Apr 2016 07:30:34 -0400 Subject: [PATCH 798/971] :arrow_up: language-make@0.22.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8fbc557d4..0cd63e918 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,7 @@ "language-javascript": "0.110.0", "language-json": "0.18.0", "language-less": "0.29.3", - "language-make": "0.21.1", + "language-make": "0.22.0", "language-mustache": "0.13.0", "language-objective-c": "0.15.1", "language-perl": "0.34.0", From 196bc53e6bef729c4925e0ae3789c3360def8dfd Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Wed, 20 Apr 2016 09:57:41 -0700 Subject: [PATCH 799/971] Ensure Windows Bash script works on all versions --- resources/win/atom.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/resources/win/atom.sh b/resources/win/atom.sh index 7380bf122..cd90ff8fb 100644 --- a/resources/win/atom.sh +++ b/resources/win/atom.sh @@ -1,2 +1,5 @@ #!/bin/sh -$(dirname "$0")/atom.cmd "$@" +pushd $(dirname "$0") > /dev/null +ATOMCMD=""$(pwd -W)"/atom.cmd" +popd > /dev/null +cmd.exe //c "$ATOMCMD" "$@" From eb739cd025b7914b97a1476e1140f6866563c0b6 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 20 Apr 2016 12:40:45 -0700 Subject: [PATCH 800/971] Add `build-and-sign` grunt task for codesigning Atom --- build/Gruntfile.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index f8ee607e5..73279779c 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -298,6 +298,7 @@ module.exports = (grunt) -> unless process.platform is 'linux' or grunt.option('no-install') defaultTasks.push 'install' grunt.registerTask('default', defaultTasks) + grunt.registerTask('build-and-sign', ['download-electron', 'download-electron-chromedriver', 'build', 'set-version', 'generate-asar', 'codesign:app', 'install']) getDefaultChannelAndReleaseBranch = (version) -> if version.match(/dev/) or isBuildingPR() From aa96d471609e6c804222252ea13e3fd1fca3bfe3 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 21 Apr 2016 12:36:45 -0400 Subject: [PATCH 801/971] First pass --- package.json | 2 +- src/git-repository-async.js | 689 +++--------------------------------- 2 files changed, 46 insertions(+), 645 deletions(-) diff --git a/package.json b/package.json index 0cd63e918..3fa4a72d3 100644 --- a/package.json +++ b/package.json @@ -37,9 +37,9 @@ "less-cache": "0.23", "line-top-index": "0.2.0", "marked": "^0.3.4", - "nodegit": "0.12.2", "normalize-package-data": "^2.0.0", "nslog": "^3", + "ohnogit": "0.0.8", "oniguruma": "^5", "pathwatcher": "~6.2", "property-accessors": "^1.1.3", diff --git a/src/git-repository-async.js b/src/git-repository-async.js index aacd482f7..6b91572d2 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -1,20 +1,7 @@ 'use babel' -import fs from 'fs-plus' -import path from 'path' -import Git from 'nodegit' -import ResourcePool from './resource-pool' -import {Emitter, CompositeDisposable, Disposable} from 'event-kit' - -const modifiedStatusFlags = Git.Status.STATUS.WT_MODIFIED | Git.Status.STATUS.INDEX_MODIFIED | Git.Status.STATUS.WT_DELETED | Git.Status.STATUS.INDEX_DELETED | Git.Status.STATUS.WT_TYPECHANGE | Git.Status.STATUS.INDEX_TYPECHANGE -const newStatusFlags = Git.Status.STATUS.WT_NEW | Git.Status.STATUS.INDEX_NEW -const deletedStatusFlags = Git.Status.STATUS.WT_DELETED | Git.Status.STATUS.INDEX_DELETED -const indexStatusFlags = Git.Status.STATUS.INDEX_NEW | Git.Status.STATUS.INDEX_MODIFIED | Git.Status.STATUS.INDEX_DELETED | Git.Status.STATUS.INDEX_RENAMED | Git.Status.STATUS.INDEX_TYPECHANGE -const ignoredStatusFlags = 1 << 14 // TODO: compose this from libgit2 constants -const submoduleMode = 57344 // TODO: compose this from libgit2 constants - -// Just using this for _.isEqual and _.object, we should impl our own here -import _ from 'underscore-plus' +import {Repository} from 'ohnogit' +import {CompositeDisposable, Disposable} from 'event-kit' // For the most part, this class behaves the same as `GitRepository`, with a few // notable differences: @@ -29,7 +16,7 @@ export default class GitRepositoryAsync { } static get Git () { - return Git + return Repository.Git } // The name of the error thrown when an action is attempted on a destroyed @@ -39,29 +26,9 @@ export default class GitRepositoryAsync { } constructor (_path, options = {}) { - // We'll serialize our access manually. - Git.setThreadSafetyStatus(Git.THREAD_SAFETY.DISABLED) + this.repo = Repository.open(_path, options) - this.emitter = new Emitter() this.subscriptions = new CompositeDisposable() - this.pathStatusCache = {} - this.path = null - - // NB: These needs to happen before the following .openRepository call. - this.openedPath = _path - this._openExactPath = options.openExactPath || false - - this.repoPromise = this.openRepository() - // NB: We don't currently _use_ the pooled object. But by giving it one - // thing, we're really just serializing all the work. Down the road, we - // could open multiple connections to the repository. - this.repoPool = new ResourcePool([this.repoPromise]) - - this.isCaseInsensitive = fs.isCaseInsensitive() - this.upstream = {} - this.submodules = {} - - this._refreshingPromise = Promise.resolve() let {refreshOnWindowFocus = true} = options if (refreshOnWindowFocus) { @@ -83,18 +50,12 @@ export default class GitRepositoryAsync { // This destroys any tasks and subscriptions and releases the underlying // libgit2 repository handle. This method is idempotent. destroy () { - if (this.emitter) { - this.emitter.emit('did-destroy') - this.emitter.dispose() - this.emitter = null - } + this.repo.destroy() if (this.subscriptions) { this.subscriptions.dispose() this.subscriptions = null } - - this.repoPromise = null } // Event subscription @@ -107,7 +68,7 @@ export default class GitRepositoryAsync { // // Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidDestroy (callback) { - return this.emitter.on('did-destroy', callback) + return this.repo.onDidDestroy(callback) } // Public: Invoke the given callback when a specific file's status has @@ -122,7 +83,7 @@ export default class GitRepositoryAsync { // // Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidChangeStatus (callback) { - return this.emitter.on('did-change-status', callback) + return this.repo.onDidChangeStatus(callback) } // Public: Invoke the given callback when a multiple files' statuses have @@ -134,7 +95,7 @@ export default class GitRepositoryAsync { // // Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidChangeStatuses (callback) { - return this.emitter.on('did-change-statuses', callback) + return this.repo.onDidChangeStatuses(callback) } // Repository details @@ -151,25 +112,13 @@ export default class GitRepositoryAsync { // Public: Returns a {Promise} which resolves to the {String} path of the // repository. getPath () { - return this.getRepo().then(repo => { - if (!this.path) { - this.path = repo.path().replace(/\/$/, '') - } - - return this.path - }) + return this.repo.getPath() } // Public: Returns a {Promise} which resolves to the {String} working // directory path of the repository. getWorkingDirectory (_path) { - return this.getRepo(_path).then(repo => { - if (!repo.cachedWorkdir) { - repo.cachedWorkdir = repo.workdir() - } - - return repo.cachedWorkdir - }) + return this.repo.getWorkingDirectory() } // Public: Returns a {Promise} that resolves to true if at the root, false if @@ -191,8 +140,7 @@ export default class GitRepositoryAsync { // // Returns a {Promise} which resolves to the relative {String} path. relativizeToWorkingDirectory (_path) { - return this.getWorkingDirectory() - .then(wd => this.relativize(_path, wd)) + return this.repo.relativizeToWorkingDirectory(_path) } // Public: Makes a path relative to the repository's working directory. @@ -202,78 +150,13 @@ export default class GitRepositoryAsync { // // Returns the relative {String} path. relativize (_path, workingDirectory) { - // The original implementation also handled null workingDirectory as it - // pulled it from a sync function that could return null. We require it - // to be passed here. - let openedWorkingDirectory - if (!_path || !workingDirectory) { - return _path - } - - // If the opened directory and the workdir differ, this is a symlinked repo - // root, so we have to do all the checks below twice--once against the realpath - // and one against the opened path - const opened = this.openedPath.replace(/\/\.git$/, '') - if (path.relative(opened, workingDirectory) !== '') { - openedWorkingDirectory = opened - } - - if (process.platform === 'win32') { - _path = _path.replace(/\\/g, '/') - } else { - if (_path[0] !== '/') { - return _path - } - } - - workingDirectory = workingDirectory.replace(/\/$/, '') - - // Depending on where the paths come from, they may have a '/private/' - // prefix. Standardize by stripping that out. - _path = _path.replace(/^\/private\//i, '/') - workingDirectory = workingDirectory.replace(/^\/private\//i, '/') - - const originalPath = _path - const originalWorkingDirectory = workingDirectory - if (this.isCaseInsensitive) { - _path = _path.toLowerCase() - workingDirectory = workingDirectory.toLowerCase() - } - - if (_path.indexOf(workingDirectory) === 0) { - return originalPath.substring(originalWorkingDirectory.length + 1) - } else if (_path === workingDirectory) { - return '' - } - - if (openedWorkingDirectory) { - openedWorkingDirectory = openedWorkingDirectory.replace(/\/$/, '') - openedWorkingDirectory = openedWorkingDirectory.replace(/^\/private\//i, '/') - - const originalOpenedWorkingDirectory = openedWorkingDirectory - if (this.isCaseInsensitive) { - openedWorkingDirectory = openedWorkingDirectory.toLowerCase() - } - - if (_path.indexOf(openedWorkingDirectory) === 0) { - return originalPath.substring(originalOpenedWorkingDirectory.length + 1) - } else if (_path === openedWorkingDirectory) { - return '' - } - } - - return _path + return this.repo.relativize(_path, workingDirectory) } // Public: Returns a {Promise} which resolves to whether the given branch // exists. hasBranch (branch) { - return this.repoPool.enqueue(() => { - return this.getRepo() - .then(repo => repo.getBranch(branch)) - .then(branch => branch != null) - .catch(_ => false) - }) + return this.repo.hasBranch(branch) } // Public: Retrieves a shortened version of the HEAD reference value. @@ -287,11 +170,7 @@ export default class GitRepositoryAsync { // // Returns a {Promise} which resolves to a {String}. getShortHead (_path) { - return this.repoPool.enqueue(() => { - return this.getRepo(_path) - .then(repo => repo.getCurrentBranch()) - .then(branch => branch.shorthand()) - }) + return this.repo.getShortHead(_path) } // Public: Is the given path a submodule in the repository? @@ -301,19 +180,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} that resolves true if the given path is a submodule in // the repository. isSubmodule (_path) { - return this.relativizeToWorkingDirectory(_path) - .then(relativePath => { - return this.repoPool.enqueue(() => { - return this.getRepo() - .then(repo => repo.index()) - .then(index => { - const entry = index.getByPath(relativePath) - if (!entry) return false - - return entry.mode === submoduleMode - }) - }) - }) + return this.repo.isSubmodule(_path) } // Public: Returns the number of commits behind the current branch is from the @@ -327,18 +194,7 @@ export default class GitRepositoryAsync { // * `ahead` The {Number} of commits ahead. // * `behind` The {Number} of commits behind. getAheadBehindCount (reference, _path) { - return this.repoPool.enqueue(() => { - return this.getRepo(_path) - .then(repo => Promise.all([repo, repo.getBranch(reference)])) - .then(([repo, local]) => { - const upstream = Git.Branch.upstream(local) - return Promise.all([repo, local, upstream]) - }) - .then(([repo, local, upstream]) => { - return Git.Graph.aheadBehind(repo, local.target(), upstream.target()) - }) - .catch(_ => ({ahead: 0, behind: 0})) - }) + return this.repo.getAheadBehindCount(reference, _path) } // Public: Get the cached ahead/behind commit counts for the current branch's @@ -351,15 +207,7 @@ export default class GitRepositoryAsync { // * `ahead` The {Number} of commits ahead. // * `behind` The {Number} of commits behind. getCachedUpstreamAheadBehindCount (_path) { - return this.relativizeToWorkingDirectory(_path) - .then(relativePath => this._submoduleForPath(_path)) - .then(submodule => { - if (submodule) { - return submodule.getCachedUpstreamAheadBehindCount(_path) - } else { - return this.upstream - } - }) + return this.repo.getCachedUpstreamAheadBehindCount(_path) } // Public: Returns the git configuration value specified by the key. @@ -370,12 +218,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to the {String} git configuration value // specified by the key. getConfigValue (key, _path) { - return this.repoPool.enqueue(() => { - return this.getRepo(_path) - .then(repo => repo.configSnapshot()) - .then(config => config.getStringBuf(key)) - .catch(_ => null) - }) + return this.repo.getConfigValue(key, _path) } // Public: Get the URL for the 'origin' remote. @@ -386,7 +229,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to the {String} origin url of the // repository. getOriginURL (_path) { - return this.getConfigValue('remote.origin.url', _path) + return this.repo.getOriginalURL(_path) } // Public: Returns the upstream branch for the current HEAD, or null if there @@ -398,11 +241,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {String} branch name such as // `refs/remotes/origin/master`. getUpstreamBranch (_path) { - return this.repoPool.enqueue(() => { - return this.getRepo(_path) - .then(repo => repo.getCurrentBranch()) - .then(branch => Git.Branch.upstream(branch)) - }) + return this.getUpstreamBranch(_path) } // Public: Gets all the local and remote references. @@ -415,25 +254,7 @@ export default class GitRepositoryAsync { // * `remotes` An {Array} of remote reference names. // * `tags` An {Array} of tag reference names. getReferences (_path) { - return this.repoPool.enqueue(() => { - return this.getRepo(_path) - .then(repo => repo.getReferences(Git.Reference.TYPE.LISTALL)) - .then(refs => { - const heads = [] - const remotes = [] - const tags = [] - for (const ref of refs) { - if (ref.isTag()) { - tags.push(ref.name()) - } else if (ref.isRemote()) { - remotes.push(ref.name()) - } else if (ref.isBranch()) { - heads.push(ref.name()) - } - } - return {heads, remotes, tags} - }) - }) + return this.repo.getReferences(_path) } // Public: Get the SHA for the given reference. @@ -445,11 +266,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to the current {String} SHA for the // given reference. getReferenceTarget (reference, _path) { - return this.repoPool.enqueue(() => { - return this.getRepo(_path) - .then(repo => Git.Reference.nameToId(repo, reference)) - .then(oid => oid.tostrS()) - }) + return this.repo.getReferenceTarget(reference, _path) } // Reading Status @@ -462,9 +279,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {Boolean} that's true if the `path` // is modified. isPathModified (_path) { - return this.relativizeToWorkingDirectory(_path) - .then(relativePath => this._getStatus([relativePath])) - .then(statuses => statuses.some(status => status.isModified())) + return this.repo.isPathModified(_path) } // Public: Resolves true if the given path is new. @@ -474,9 +289,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {Boolean} that's true if the `path` // is new. isPathNew (_path) { - return this.relativizeToWorkingDirectory(_path) - .then(relativePath => this._getStatus([relativePath])) - .then(statuses => statuses.some(status => status.isNew())) + return this.repo.isPathNew(_path) } // Public: Is the given path ignored? @@ -486,17 +299,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {Boolean} that's true if the `path` // is ignored. isPathIgnored (_path) { - return this.getWorkingDirectory() - .then(wd => { - return this.repoPool.enqueue(() => { - return this.getRepo() - .then(repo => { - const relativePath = this.relativize(_path, wd) - return Git.Ignore.pathIsIgnored(repo, relativePath) - }) - .then(ignored => Boolean(ignored)) - }) - }) + return this.repo.isPathIgnored(_path) } // Get the status of a directory in the repository's working directory. @@ -507,18 +310,7 @@ export default class GitRepositoryAsync { // value can be passed to {::isStatusModified} or {::isStatusNew} to get more // information. getDirectoryStatus (directoryPath) { - return this.relativizeToWorkingDirectory(directoryPath) - .then(relativePath => { - const pathspec = relativePath + '/**' - return this._getStatus([pathspec]) - }) - .then(statuses => { - return Promise.all(statuses.map(s => s.statusBit())).then(bits => { - return bits - .filter(b => b > 0) - .reduce((status, bit) => status | bit, 0) - }) - }) + return this.repo.getDirectoryStatus(directoryPath) } // Refresh the status bit for the given path. @@ -531,27 +323,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {Number} which is the refreshed // status bit for the path. refreshStatusForPath (_path) { - let relativePath - return this.getWorkingDirectory() - .then(wd => { - relativePath = this.relativize(_path, wd) - return this._getStatus([relativePath]) - }) - .then(statuses => { - const cachedStatus = this.pathStatusCache[relativePath] || 0 - const status = statuses[0] ? statuses[0].statusBit() : Git.Status.STATUS.CURRENT - if (status !== cachedStatus) { - if (status === Git.Status.STATUS.CURRENT) { - delete this.pathStatusCache[relativePath] - } else { - this.pathStatusCache[relativePath] = status - } - - this.emitter.emit('did-change-status', {path: _path, pathStatus: status}) - } - - return status - }) + return this.repo.refreshStatusForPath(_path) } // Returns a Promise that resolves to the status bit of a given path if it has @@ -567,8 +339,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a status {Number} or null if the // path is not in the cache. getCachedPathStatus (_path) { - return this.relativizeToWorkingDirectory(_path) - .then(relativePath => this.pathStatusCache[relativePath]) + return this.repo.getCachedPathStatus(_path) } // Public: Get the cached statuses for the repository. @@ -576,7 +347,7 @@ export default class GitRepositoryAsync { // Returns an {Object} of {Number} statuses, keyed by {String} working // directory-relative file names. getCachedPathStatuses () { - return this.pathStatusCache + return this.repo.pathStatusCache } // Public: Returns true if the given status indicates modification. @@ -585,7 +356,7 @@ export default class GitRepositoryAsync { // // Returns a {Boolean} that's true if the `statusBit` indicates modification. isStatusModified (statusBit) { - return (statusBit & modifiedStatusFlags) > 0 + return this.repo.isStatusModified(statusBit) } // Public: Returns true if the given status indicates a new path. @@ -594,7 +365,7 @@ export default class GitRepositoryAsync { // // Returns a {Boolean} that's true if the `statusBit` indicates a new path. isStatusNew (statusBit) { - return (statusBit & newStatusFlags) > 0 + return this.repo.isStatusNew(statusBit) } // Public: Returns true if the given status indicates the path is staged. @@ -604,7 +375,7 @@ export default class GitRepositoryAsync { // Returns a {Boolean} that's true if the `statusBit` indicates the path is // staged. isStatusStaged (statusBit) { - return (statusBit & indexStatusFlags) > 0 + return this.repo.isStatusStaged(statusBit) } // Public: Returns true if the given status indicates the path is ignored. @@ -614,7 +385,7 @@ export default class GitRepositoryAsync { // Returns a {Boolean} that's true if the `statusBit` indicates the path is // ignored. isStatusIgnored (statusBit) { - return (statusBit & ignoredStatusFlags) > 0 + return this.repo.isStatusIgnored(statusBit) } // Public: Returns true if the given status indicates the path is deleted. @@ -624,7 +395,7 @@ export default class GitRepositoryAsync { // Returns a {Boolean} that's true if the `statusBit` indicates the path is // deleted. isStatusDeleted (statusBit) { - return (statusBit & deletedStatusFlags) > 0 + return this.repo.isStatusDeleted(statusBit) } // Retrieving Diffs @@ -640,40 +411,7 @@ export default class GitRepositoryAsync { // * `added` The {Number} of added lines. // * `deleted` The {Number} of deleted lines. getDiffStats (_path) { - return this.getWorkingDirectory(_path) - .then(wd => { - return this.repoPool.enqueue(() => { - return this.getRepo(_path) - .then(repo => Promise.all([repo, repo.getHeadCommit()])) - .then(([repo, headCommit]) => Promise.all([repo, headCommit.getTree()])) - .then(([repo, tree]) => { - const options = new Git.DiffOptions() - options.contextLines = 0 - options.flags = Git.Diff.OPTION.DISABLE_PATHSPEC_MATCH - options.pathspec = this.relativize(_path, wd) - if (process.platform === 'win32') { - // Ignore eol of line differences on windows so that files checked in - // as LF don't report every line modified when the text contains CRLF - // endings. - options.flags |= Git.Diff.OPTION.IGNORE_WHITESPACE_EOL - } - return Git.Diff.treeToWorkdir(repo, tree, options) - }) - .then(diff => this._getDiffLines(diff)) - .then(lines => { - const stats = {added: 0, deleted: 0} - for (const line of lines) { - const origin = line.origin() - if (origin === Git.Diff.LINE.ADDITION) { - stats.added++ - } else if (origin === Git.Diff.LINE.DELETION) { - stats.deleted++ - } - } - return stats - }) - }) - }) + return this.repo.getDiffStats(_path) } // Public: Retrieves the line diffs comparing the `HEAD` version of the given @@ -688,30 +426,7 @@ export default class GitRepositoryAsync { // * `oldLines` The {Number} of lines in the old hunk. // * `newLines` The {Number} of lines in the new hunk getLineDiffs (_path, text) { - return this.getWorkingDirectory(_path) - .then(wd => { - let relativePath = null - return this.repoPool.enqueue(() => { - return this.getRepo(_path) - .then(repo => { - relativePath = this.relativize(_path, wd) - return repo.getHeadCommit() - }) - .then(commit => commit.getEntry(relativePath)) - .then(entry => entry.getBlob()) - .then(blob => { - const options = new Git.DiffOptions() - options.contextLines = 0 - if (process.platform === 'win32') { - // Ignore eol of line differences on windows so that files checked in - // as LF don't report every line modified when the text contains CRLF - // endings. - options.flags = Git.Diff.OPTION.IGNORE_WHITESPACE_EOL - } - return this._diffBlobToBuffer(blob, text, options) - }) - }) - }) + return this.repo.getLineDiffs(_path, text) } // Checking Out @@ -732,19 +447,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} that resolves or rejects depending on whether the // method was successful. checkoutHead (_path) { - return this.getWorkingDirectory(_path) - .then(wd => { - return this.repoPool.enqueue(() => { - return this.getRepo(_path) - .then(repo => { - const checkoutOptions = new Git.CheckoutOptions() - checkoutOptions.paths = [this.relativize(_path, wd)] - checkoutOptions.checkoutStrategy = Git.Checkout.STRATEGY.FORCE | Git.Checkout.STRATEGY.DISABLE_PATHSPEC_MATCH - return Git.Checkout.head(repo, checkoutOptions) - }) - }) - }) - .then(() => this.refreshStatusForPath(_path)) + return this.repo.checkoutHead(_path) } // Public: Checks out a branch in your repository. @@ -755,19 +458,7 @@ export default class GitRepositoryAsync { // // Returns a {Promise} that resolves if the method was successful. checkoutReference (reference, create) { - return this.repoPool.enqueue(() => { - return this.getRepo() - .then(repo => repo.checkoutBranch(reference)) - }) - .catch(error => { - if (create) { - return this._createBranch(reference) - .then(_ => this.checkoutReference(reference, false)) - } else { - throw error - } - }) - .then(_ => null) + return this.repo.checkoutReference(reference, create) } // Private @@ -786,107 +477,10 @@ export default class GitRepositoryAsync { return this.checkoutHead(filePath) } - // Create a new branch with the given name. + // Refreshes the git status. // - // * `name` The {String} name of the new branch. - // - // Returns a {Promise} which resolves to a {NodeGit.Ref} reference to the - // created branch. - _createBranch (name) { - return this.repoPool.enqueue(() => { - return this.getRepo() - .then(repo => Promise.all([repo, repo.getHeadCommit()])) - .then(([repo, commit]) => repo.createBranch(name, commit)) - }) - } - - // Get all the hunks in the diff. - // - // * `diff` The {NodeGit.Diff} whose hunks should be retrieved. - // - // Returns a {Promise} which resolves to an {Array} of {NodeGit.Hunk}. - _getDiffHunks (diff) { - return diff.patches() - .then(patches => Promise.all(patches.map(p => p.hunks()))) // patches :: Array - .then(hunks => _.flatten(hunks)) // hunks :: Array> - } - - // Get all the lines contained in the diff. - // - // * `diff` The {NodeGit.Diff} use lines should be retrieved. - // - // Returns a {Promise} which resolves to an {Array} of {NodeGit.Line}. - _getDiffLines (diff) { - return this._getDiffHunks(diff) - .then(hunks => Promise.all(hunks.map(h => h.lines()))) - .then(lines => _.flatten(lines)) // lines :: Array> - } - - // Diff the given blob and buffer with the provided options. - // - // * `blob` The {NodeGit.Blob} - // * `buffer` The {String} buffer. - // * `options` The {NodeGit.DiffOptions} - // - // Returns a {Promise} which resolves to an {Array} of {Object}s which have - // the following keys: - // * `oldStart` The {Number} of the old starting line. - // * `newStart` The {Number} of the new starting line. - // * `oldLines` The {Number} of old lines. - // * `newLines` The {Number} of new lines. - _diffBlobToBuffer (blob, buffer, options) { - const hunks = [] - const hunkCallback = (delta, hunk, payload) => { - hunks.push({ - oldStart: hunk.oldStart(), - newStart: hunk.newStart(), - oldLines: hunk.oldLines(), - newLines: hunk.newLines() - }) - } - - return Git.Diff.blobToBuffer(blob, null, buffer, null, options, null, null, hunkCallback, null) - .then(_ => hunks) - } - - // Get the current branch and update this.branch. - // - // Returns a {Promise} which resolves to a {boolean} indicating whether the - // branch name changed. - _refreshBranch () { - return this.repoPool.enqueue(() => { - return this.getRepo() - .then(repo => repo.getCurrentBranch()) - .then(ref => ref.name()) - .then(branchName => { - const changed = branchName !== this.branch - this.branch = branchName - return changed - }) - }) - } - - // Refresh the cached ahead/behind count with the given branch. - // - // * `branchName` The {String} name of the branch whose ahead/behind should be - // used for the refresh. - // - // Returns a {Promise} which will resolve to a {boolean} indicating whether - // the ahead/behind count changed. - _refreshAheadBehindCount (branchName) { - return this.getAheadBehindCount(branchName) - .then(counts => { - const changed = !_.isEqual(counts, this.upstream) - this.upstream = counts - return changed - }) - } - - // Get the status for this repository. - // - // Returns a {Promise} that will resolve to an object of {String} paths to the - // {Number} status. - _getRepositoryStatus () { + // Returns a {Promise} which will resolve to {null} when refresh is complete. + refreshStatus () { let projectPathsPromises = [Promise.resolve('')] if (this.project) { projectPathsPromises = this.project.getPaths() @@ -895,163 +489,7 @@ export default class GitRepositoryAsync { return Promise.all(projectPathsPromises) .then(paths => paths.map(p => p.length > 0 ? p + '/**' : '*')) - .then(projectPaths => { - return this._getStatus(projectPaths.length > 0 ? projectPaths : null) - }) - .then(statuses => { - const statusPairs = statuses.map(status => [status.path(), status.statusBit()]) - return _.object(statusPairs) - }) - } - - // Get the status for the given submodule. - // - // * `submodule` The {GitRepositoryAsync} for the submodule. - // - // Returns a {Promise} which resolves to an {Object}, keyed by {String} - // repo-relative {Number} statuses. - async _getSubmoduleStatus (submodule) { - // At this point, we've called submodule._refreshSubmodules(), which would - // have refreshed the status on *its* submodules, etc. So we know that its - // cached path statuses are up-to-date. - // - // Now we just need to hoist those statuses into our repository by changing - // their paths to be relative to us. - - const statuses = submodule.getCachedPathStatuses() - const repoRelativeStatuses = {} - const submoduleRepo = await submodule.getRepo() - const submoduleWorkDir = submoduleRepo.workdir() - for (const relativePath in statuses) { - const statusBit = statuses[relativePath] - const absolutePath = path.join(submoduleWorkDir, relativePath) - const repoRelativePath = await this.relativizeToWorkingDirectory(absolutePath) - repoRelativeStatuses[repoRelativePath] = statusBit - } - - return repoRelativeStatuses - } - - // Refresh the list of submodules in the repository. - // - // Returns a {Promise} which resolves to an {Object} keyed by {String} - // submodule names with {GitRepositoryAsync} values. - async _refreshSubmodules () { - const repo = await this.getRepo() - const wd = await this.getWorkingDirectory() - const submoduleNames = await repo.getSubmoduleNames() - for (const name of submoduleNames) { - const alreadyExists = Boolean(this.submodules[name]) - if (alreadyExists) continue - - const submodule = await Git.Submodule.lookup(repo, name) - const absolutePath = path.join(wd, submodule.path()) - const submoduleRepo = GitRepositoryAsync.open(absolutePath, {openExactPath: true, refreshOnWindowFocus: false}) - this.submodules[name] = submoduleRepo - } - - for (const name in this.submodules) { - const repo = this.submodules[name] - const gone = submoduleNames.indexOf(name) < 0 - if (gone) { - repo.destroy() - delete this.submodules[name] - } else { - try { - await repo.refreshStatus() - } catch (e) { - // libgit2 will sometimes report submodules that aren't actually valid - // (https://github.com/libgit2/libgit2/issues/3580). So check the - // validity of the submodules by removing any that fail. - repo.destroy() - delete this.submodules[name] - } - } - } - - return _.values(this.submodules) - } - - // Get the status for the submodules in the repository. - // - // Returns a {Promise} that will resolve to an object of {String} paths to the - // {Number} status. - _getSubmoduleStatuses () { - return this._refreshSubmodules() - .then(repos => { - return Promise.all(repos.map(repo => this._getSubmoduleStatus(repo))) - }) - .then(statuses => _.extend({}, ...statuses)) - } - - // Refresh the cached status. - // - // Returns a {Promise} which will resolve to a {boolean} indicating whether - // any statuses changed. - _refreshStatus () { - return Promise.all([this._getRepositoryStatus(), this._getSubmoduleStatuses()]) - .then(([repositoryStatus, submoduleStatus]) => { - const statusesByPath = _.extend({}, repositoryStatus, submoduleStatus) - const changed = !_.isEqual(this.pathStatusCache, statusesByPath) - this.pathStatusCache = statusesByPath - return changed - }) - } - - // Refreshes the git status. - // - // Returns a {Promise} which will resolve to {null} when refresh is complete. - refreshStatus () { - const status = this._refreshStatus() - const branch = this._refreshBranch() - const aheadBehind = branch.then(() => this._refreshAheadBehindCount(this.branch)) - - this._refreshingPromise = this._refreshingPromise.then(_ => { - return Promise.all([status, branch, aheadBehind]) - .then(([statusChanged, branchChanged, aheadBehindChanged]) => { - if (this.emitter && (statusChanged || branchChanged || aheadBehindChanged)) { - this.emitter.emit('did-change-statuses') - } - - return null - }) - // Because all these refresh steps happen asynchronously, it's entirely - // possible the repository was destroyed while we were working. In which - // case we should just swallow the error. - .catch(e => { - if (this._isDestroyed()) { - return null - } else { - return Promise.reject(e) - } - }) - .catch(e => { - console.error('Error refreshing repository status:') - console.error(e) - return Promise.reject(e) - }) - }) - return this._refreshingPromise - } - - // Get the submodule for the given path. - // - // Returns a {Promise} which resolves to the {GitRepositoryAsync} submodule or - // null if it isn't a submodule path. - async _submoduleForPath (_path) { - let relativePath = await this.relativizeToWorkingDirectory(_path) - for (const submodulePath in this.submodules) { - const submoduleRepo = this.submodules[submodulePath] - if (relativePath === submodulePath) { - return submoduleRepo - } else if (relativePath.indexOf(`${submodulePath}/`) === 0) { - relativePath = relativePath.substring(submodulePath.length + 1) - const innerSubmodule = await submoduleRepo._submoduleForPath(relativePath) - return innerSubmodule || submoduleRepo - } - } - - return null + .then(pathspecs => this.repo.refreshStatus(pathspecs)) } // Get the NodeGit repository for the given path. @@ -1062,16 +500,7 @@ export default class GitRepositoryAsync { // // Returns a {Promise} which resolves to the {NodeGit.Repository}. getRepo (_path) { - if (this._isDestroyed()) { - const error = new Error('Repository has been destroyed') - error.name = GitRepositoryAsync.DestroyedErrorName - return Promise.reject(error) - } - - if (!_path) return this.repoPromise - - return this._submoduleForPath(_path) - .then(submodule => submodule ? submodule.getRepo() : this.repoPromise) + return this.repo.getRepo(_path) } // Open a new instance of the underlying {NodeGit.Repository}. @@ -1081,11 +510,7 @@ export default class GitRepositoryAsync { // // Returns the new {NodeGit.Repository}. openRepository () { - if (this._openExactPath) { - return Git.Repository.open(this.openedPath) - } else { - return Git.Repository.openExt(this.openedPath, 0, '') - } + return this.repo.openRepository() } // Section: Private @@ -1095,7 +520,7 @@ export default class GitRepositoryAsync { // // Returns a {Boolean}. _isDestroyed () { - return this.repoPromise == null + return this.repo._isDestroyed() } // Subscribe to events on the given buffer. @@ -1121,28 +546,4 @@ export default class GitRepositoryAsync { this.subscriptions.add(bufferSubscriptions) } - - // Get the status for the given paths. - // - // * `paths` The {String} paths whose status is wanted. If undefined, get the - // status for the whole repository. - // - // Returns a {Promise} which resolves to an {Array} of {NodeGit.StatusFile} - // statuses for the paths. - _getStatus (paths) { - return this.repoPool.enqueue(() => { - return this.getRepo() - .then(repo => { - const opts = { - flags: Git.Status.OPT.INCLUDE_UNTRACKED | Git.Status.OPT.RECURSE_UNTRACKED_DIRS - } - - if (paths) { - opts.pathspec = paths - } - - return repo.getStatusExt(opts) - }) - }) - } } From 5ed30f910c788077621bc193eea334c3c6a5af90 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 21 Apr 2016 10:43:21 -0600 Subject: [PATCH 802/971] Create synthetic iterator boundaries between open and close tags If no non-negative integer exists between an open and close tag code, we still need to report a boundary there, since close tags always have to come first at any given iterator position. :pear:ed with @as-cii --- spec/tokenized-buffer-iterator-spec.js | 40 ++++++++++++++++++++++++++ src/tokenized-buffer-iterator.coffee | 11 +++++-- 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 spec/tokenized-buffer-iterator-spec.js diff --git a/spec/tokenized-buffer-iterator-spec.js b/spec/tokenized-buffer-iterator-spec.js new file mode 100644 index 000000000..639714136 --- /dev/null +++ b/spec/tokenized-buffer-iterator-spec.js @@ -0,0 +1,40 @@ +/** @babel */ + +import TokenizedBufferIterator from '../src/tokenized-buffer-iterator' +import {Point} from 'text-buffer' + +describe('TokenizedBufferIterator', () => { + it('reports two boundaries at the same position when tags close, open, then close again without a non-negative integer separating them (regression)', () => { + const tokenizedBuffer = { + tokenizedLineForRow () { + return { + tags: [-1, -2, -1, -2], + text: '', + openScopes: [] + } + } + } + + const grammarRegistry = { + scopeForId () { + return 'foo' + } + } + + const iterator = new TokenizedBufferIterator(tokenizedBuffer, grammarRegistry) + + iterator.seek(Point(0, 0)) + expect(iterator.getPosition()).toEqual(Point(0, 0)) + expect(iterator.getCloseTags()).toEqual([]) + expect(iterator.getOpenTags()).toEqual(['foo']) + + iterator.moveToSuccessor() + expect(iterator.getPosition()).toEqual(Point(0, 0)) + expect(iterator.getCloseTags()).toEqual(['foo']) + expect(iterator.getOpenTags()).toEqual(['foo']) + + iterator.moveToSuccessor() + expect(iterator.getCloseTags()).toEqual(['foo']) + expect(iterator.getOpenTags()).toEqual([]) + }) +}) diff --git a/src/tokenized-buffer-iterator.coffee b/src/tokenized-buffer-iterator.coffee index e6e129d7a..725041def 100644 --- a/src/tokenized-buffer-iterator.coffee +++ b/src/tokenized-buffer-iterator.coffee @@ -28,7 +28,11 @@ class TokenizedBufferIterator else scopeName = @grammarRegistry.scopeForId(tag) if tag % 2 is 0 - @closeTags.push(scopeName) + if @openTags.length > 0 + @tagIndex = index + break + else + @closeTags.push(scopeName) else @openTags.push(scopeName) @@ -56,7 +60,10 @@ class TokenizedBufferIterator else scopeName = @grammarRegistry.scopeForId(tag) if tag % 2 is 0 - @closeTags.push(scopeName) + if @openTags.length > 0 + break + else + @closeTags.push(scopeName) else @openTags.push(scopeName) @tagIndex++ From ea57a25a3fd865be35c053710ba0b75d4bf85bc2 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 21 Apr 2016 13:08:10 -0400 Subject: [PATCH 803/971] :arrow_up: ohnogit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3fa4a72d3..d50a888ed 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "marked": "^0.3.4", "normalize-package-data": "^2.0.0", "nslog": "^3", - "ohnogit": "0.0.8", + "ohnogit": "0.0.9", "oniguruma": "^5", "pathwatcher": "~6.2", "property-accessors": "^1.1.3", From 809d194f316e0a966a8d13c38ff57d8ec94d6f9a Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 21 Apr 2016 13:17:35 -0400 Subject: [PATCH 804/971] Update the tests --- spec/git-repository-async-spec.js | 11 +++++------ src/git-repository-async.js | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 224280b39..d36b9fd58 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -3,7 +3,6 @@ import fs from 'fs-plus' import path from 'path' import temp from 'temp' -import Git from 'nodegit' import {it, beforeEach, afterEach} from './async-spec-helpers' @@ -47,7 +46,7 @@ describe('GitRepositoryAsync', () => { let threw = false try { - await repo.repoPromise + await repo.getRepo() } catch (e) { threw = true } @@ -64,19 +63,19 @@ describe('GitRepositoryAsync', () => { }) it('returns the repository when not given a path', async () => { - const nodeGitRepo1 = await repo.repoPromise + const nodeGitRepo1 = await repo.getRepo() const nodeGitRepo2 = await repo.getRepo() expect(nodeGitRepo1.workdir()).toBe(nodeGitRepo2.workdir()) }) it('returns the repository when given a non-submodule path', async () => { - const nodeGitRepo1 = await repo.repoPromise + const nodeGitRepo1 = await repo.getRepo() const nodeGitRepo2 = await repo.getRepo('README') expect(nodeGitRepo1.workdir()).toBe(nodeGitRepo2.workdir()) }) it('returns the submodule repository when given a submodule path', async () => { - const nodeGitRepo1 = await repo.repoPromise + const nodeGitRepo1 = await repo.getRepo() const nodeGitRepo2 = await repo.getRepo('jstips') expect(nodeGitRepo1.workdir()).not.toBe(nodeGitRepo2.workdir()) @@ -303,7 +302,7 @@ describe('GitRepositoryAsync', () => { await repo.getPathStatus(filePath) expect(statusHandler.callCount).toBe(1) - const status = Git.Status.STATUS.WT_MODIFIED + const status = GitRepositoryAsync.Git.Status.STATUS.WT_MODIFIED expect(statusHandler.argsForCall[0][0]).toEqual({path: filePath, pathStatus: status}) fs.writeFileSync(filePath, 'abc') diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 6b91572d2..8201b6f6f 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -22,7 +22,7 @@ export default class GitRepositoryAsync { // The name of the error thrown when an action is attempted on a destroyed // repository. static get DestroyedErrorName () { - return 'GitRepositoryAsync.destroyed' + return Repository.DestroyedErrorName } constructor (_path, options = {}) { From 866a26a754ad87b2977a5ac27ed4a01be82b2c7c Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 21 Apr 2016 13:25:14 -0400 Subject: [PATCH 805/971] Update the ignore paths for ohnogit. --- build/tasks/build-task.coffee | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build/tasks/build-task.coffee b/build/tasks/build-task.coffee index 9164f8dab..544b09753 100644 --- a/build/tasks/build-task.coffee +++ b/build/tasks/build-task.coffee @@ -54,9 +54,9 @@ module.exports = (grunt) -> # so that it doesn't becomes larger than it needs to be. ignoredPaths = [ path.join('git-utils', 'deps') - path.join('nodegit', 'vendor') - path.join('nodegit', 'node_modules', 'node-pre-gyp') - path.join('nodegit', 'node_modules', '.bin') + path.join('ohnogit', 'node_modules', 'nodegit', 'vendor') + path.join('ohnogit', 'node_modules', 'nodegit', 'node_modules', 'node-pre-gyp') + path.join('ohnogit', 'node_modules', 'nodegit', 'node_modules', '.bin') path.join('oniguruma', 'deps') path.join('less', 'dist') path.join('bootstrap', 'docs') @@ -122,9 +122,9 @@ module.exports = (grunt) -> # Ignore *.cc and *.h files from native modules ignoredPaths.push "#{_.escapeRegExp(path.join('ctags', 'src') + path.sep)}.*\\.(cc|h)*" ignoredPaths.push "#{_.escapeRegExp(path.join('git-utils', 'src') + path.sep)}.*\\.(cc|h)*" - ignoredPaths.push "#{_.escapeRegExp(path.join('nodegit', 'src') + path.sep)}.*\\.(cc|h)?" - ignoredPaths.push "#{_.escapeRegExp(path.join('nodegit', 'generate') + path.sep)}.*\\.(cc|h)?" - ignoredPaths.push "#{_.escapeRegExp(path.join('nodegit', 'include') + path.sep)}.*\\.(cc|h)?" + ignoredPaths.push "#{_.escapeRegExp(path.join('ohnogit', 'node_modules', 'nodegit', 'src') + path.sep)}.*\\.(cc|h)?" + ignoredPaths.push "#{_.escapeRegExp(path.join('ohnogit', 'node_modules', 'nodegit', 'generate') + path.sep)}.*\\.(cc|h)?" + ignoredPaths.push "#{_.escapeRegExp(path.join('ohnogit', 'node_modules', 'nodegit', 'include') + path.sep)}.*\\.(cc|h)?" ignoredPaths.push "#{_.escapeRegExp(path.join('keytar', 'src') + path.sep)}.*\\.(cc|h)*" ignoredPaths.push "#{_.escapeRegExp(path.join('nslog', 'src') + path.sep)}.*\\.(cc|h)*" ignoredPaths.push "#{_.escapeRegExp(path.join('oniguruma', 'src') + path.sep)}.*\\.(cc|h)*" From c0eb8baae0c16a0cd015f5bfa4a2a2b0c501f486 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 21 Apr 2016 13:55:43 -0400 Subject: [PATCH 806/971] Provide _refreshingPromise. --- src/git-repository-async.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 8201b6f6f..37131dd24 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -45,6 +45,11 @@ export default class GitRepositoryAsync { } } + // This exists to provide backwards compatibility. + get _refreshingPromise () { + return this.repo._refreshingPromise + } + // Public: Destroy this {GitRepositoryAsync} object. // // This destroys any tasks and subscriptions and releases the underlying From 3574613fa32242ef72016785f2333d9dc5d1ce4c Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 21 Apr 2016 14:19:24 -0400 Subject: [PATCH 807/971] Don't need resource pool anymore. --- spec/resource-pool-spec.js | 66 -------------------------------------- src/resource-pool.js | 57 -------------------------------- 2 files changed, 123 deletions(-) delete mode 100644 spec/resource-pool-spec.js delete mode 100644 src/resource-pool.js diff --git a/spec/resource-pool-spec.js b/spec/resource-pool-spec.js deleted file mode 100644 index 27893360a..000000000 --- a/spec/resource-pool-spec.js +++ /dev/null @@ -1,66 +0,0 @@ -/** @babel */ - -import ResourcePool from '../src/resource-pool' - -import {it} from './async-spec-helpers' - -describe('ResourcePool', () => { - let queue - - beforeEach(() => { - queue = new ResourcePool([{}]) - }) - - describe('.enqueue', () => { - it('calls the enqueued function', async () => { - let called = false - await queue.enqueue(() => { - called = true - return Promise.resolve() - }) - expect(called).toBe(true) - }) - - it('forwards values from the inner promise', async () => { - const result = await queue.enqueue(() => Promise.resolve(42)) - expect(result).toBe(42) - }) - - it('forwards errors from the inner promise', async () => { - let threw = false - try { - await queue.enqueue(() => Promise.reject(new Error('down with the sickness'))) - } catch (e) { - threw = true - } - expect(threw).toBe(true) - }) - - it('continues to dequeue work after a promise has been rejected', async () => { - try { - await queue.enqueue(() => Promise.reject(new Error('down with the sickness'))) - } catch (e) {} - - const result = await queue.enqueue(() => Promise.resolve(42)) - expect(result).toBe(42) - }) - - it('queues up work', async () => { - let resolve = null - queue.enqueue(() => { - return new Promise((resolve_, reject) => { - resolve = resolve_ - }) - }) - - expect(queue.getQueueDepth()).toBe(0) - - queue.enqueue(() => new Promise((resolve, reject) => {})) - - expect(queue.getQueueDepth()).toBe(1) - resolve() - - waitsFor(() => queue.getQueueDepth() === 0) - }) - }) -}) diff --git a/src/resource-pool.js b/src/resource-pool.js deleted file mode 100644 index ae7cb71d0..000000000 --- a/src/resource-pool.js +++ /dev/null @@ -1,57 +0,0 @@ -/** @babel */ - -// Manages a pool of some resource. -export default class ResourcePool { - constructor (pool) { - this.pool = pool - - this.queue = [] - } - - // Enqueue the given function. The function will be given an object from the - // pool. The function must return a {Promise}. - enqueue (fn) { - let resolve = null - let reject = null - const wrapperPromise = new Promise((resolve_, reject_) => { - resolve = resolve_ - reject = reject_ - }) - - this.queue.push(this.wrapFunction(fn, resolve, reject)) - - this.dequeueIfAble() - - return wrapperPromise - } - - wrapFunction (fn, resolve, reject) { - return (resource) => { - const promise = fn(resource) - promise - .then(result => { - resolve(result) - this.taskDidComplete(resource) - }, error => { - reject(error) - this.taskDidComplete(resource) - }) - } - } - - taskDidComplete (resource) { - this.pool.push(resource) - - this.dequeueIfAble() - } - - dequeueIfAble () { - if (!this.pool.length || !this.queue.length) return - - const fn = this.queue.shift() - const resource = this.pool.shift() - fn(resource) - } - - getQueueDepth () { return this.queue.length } -} From 4a0c9467854016c6ee20e8906e9b9c9dceb3179c Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 21 Apr 2016 15:29:50 -0400 Subject: [PATCH 808/971] :arrow_up: status-bar@1.2.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d50a888ed..a9a5314fd 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.235.1", "snippets": "1.0.2", "spell-check": "0.67.1", - "status-bar": "1.2.3", + "status-bar": "1.2.4", "styleguide": "0.45.2", "symbols-view": "0.112.0", "tabs": "0.92.2", From 53c52342e5eac36a3dbe6020c3798b6ffb535289 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 21 Apr 2016 13:36:50 -0700 Subject: [PATCH 809/971] Add Ctrl+F4 keybinding for Linux --- keymaps/linux.cson | 1 + 1 file changed, 1 insertion(+) diff --git a/keymaps/linux.cson b/keymaps/linux.cson index 7d67e2ce5..1f78739a9 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -27,6 +27,7 @@ 'ctrl-n': 'application:new-file' 'ctrl-s': 'core:save' 'ctrl-S': 'core:save-as' + 'ctrl-f4': 'core:close' 'ctrl-w': 'core:close' 'ctrl-z': 'core:undo' 'ctrl-y': 'core:redo' From d276f93a233423375496da79682afa838387bc75 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 22 Apr 2016 11:12:15 +0200 Subject: [PATCH 810/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0cd63e918..9b79835b0 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.4.6", + "text-buffer": "8.5.0", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 630cbb7bb72a9c59b2a87300545df4737bda2a57 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 22 Apr 2016 11:14:24 +0200 Subject: [PATCH 811/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f51f22895..d20e7636a 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.0.0-beta4", + "text-buffer": "9.0.0-beta5", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 6fc3a364e1c43921fe0987e4e91ca254b364d33e Mon Sep 17 00:00:00 2001 From: Riley Dallas Date: Fri, 22 Apr 2016 09:30:45 -0500 Subject: [PATCH 812/971] :memo: Update CSON documentation link in snippets.cson --- dot-atom/snippets.cson | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dot-atom/snippets.cson b/dot-atom/snippets.cson index eb8f1b22a..cd66bba04 100644 --- a/dot-atom/snippets.cson +++ b/dot-atom/snippets.cson @@ -18,4 +18,4 @@ # This file uses CoffeeScript Object Notation (CSON). # If you are unfamiliar with CSON, you can read more about it in the # Atom Flight Manual: -# https://atom.io/docs/latest/using-atom-basic-customization#cson +# http://flight-manual.atom.io/using-atom/sections/basic-customization/#_cson From 58e1953e43e206788706087a813396915f470316 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Fri, 22 Apr 2016 12:07:19 -0700 Subject: [PATCH 813/971] Win32 build now installs to localappdata. Admin elevation now smart. --- build/Gruntfile.coffee | 2 +- build/tasks/install-task.coffee | 20 ++++++++++++++++---- docs/build-instructions/windows.md | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index 73279779c..16e0ed5d6 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -57,7 +57,7 @@ module.exports = (grunt) -> homeDir = process.env.USERPROFILE contentsDir = shellAppDir appDir = path.join(shellAppDir, 'resources', 'app') - installDir ?= path.join(process.env.ProgramFiles, appName) + installDir ?= path.join(process.env.LOCALAPPDATA, appName, 'app-dev') killCommand = 'taskkill /F /IM atom.exe' else if process.platform is 'darwin' homeDir = process.env.HOME diff --git a/build/tasks/install-task.coffee b/build/tasks/install-task.coffee index 2d9054385..19fd3d383 100644 --- a/build/tasks/install-task.coffee +++ b/build/tasks/install-task.coffee @@ -16,10 +16,22 @@ module.exports = (grunt) -> {description} = grunt.config.get('atom.metadata') if process.platform is 'win32' - runas ?= require 'runas' - copyFolder = path.resolve 'script', 'copy-folder.cmd' - if runas('cmd', ['/c', copyFolder, shellAppDir, installDir], admin: true) isnt 0 - grunt.log.error("Failed to copy #{shellAppDir} to #{installDir}") + done = @async() + fs.access(installDir, fs.W_OK, (err) -> + adminRequired = true if err + if adminRequired + grunt.log.ok("User does not have write access to #{installDir}, elevating to admin") + runas ?= require 'runas' + copyFolder = path.resolve 'script', 'copy-folder.cmd' + + if runas('cmd', ['/c', copyFolder, shellAppDir, installDir], admin: adminRequired) isnt 0 + grunt.log.error("Failed to copy #{shellAppDir} to #{installDir}") + else + grunt.log.ok("Installed into #{installDir}") + + done() + ) + else if process.platform is 'darwin' rm installDir mkdir path.dirname(installDir) diff --git a/docs/build-instructions/windows.md b/docs/build-instructions/windows.md index 3ec28f139..d0e101ba0 100644 --- a/docs/build-instructions/windows.md +++ b/docs/build-instructions/windows.md @@ -34,7 +34,7 @@ git clone https://github.com/atom/atom/ cd atom script/build ``` -This will create the Atom application in the `out\Atom` folder as well as copy it to a folder named `Atom` within `Program Files`. +This will create the Atom application in the `out\Atom` folder as well as copy it to a subfolder of your user profile (e.g. `c:\Users\Bob`) called `AppData\Local\atom\app-dev`. ### `script/build` Options * `--install-dir` - Creates the final built application in this directory. Example (trailing slash is optional): From 11170696d54f28ac8c7bbc67c89030610ee3e4a9 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Fri, 22 Apr 2016 18:10:53 -0400 Subject: [PATCH 814/971] :arrow_up: tree-view@0.206.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b07b4190f..1b6994fed 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.112.0", "tabs": "0.92.2", "timecop": "0.33.1", - "tree-view": "0.206.0", + "tree-view": "0.206.1", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From 8e78441e3287a781cda6e95a785557c22f3f183f Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 22 Apr 2016 15:16:08 -0700 Subject: [PATCH 815/971] :arrow_up: tabs@0.92.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1b6994fed..9317866ef 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.2.4", "styleguide": "0.45.2", "symbols-view": "0.112.0", - "tabs": "0.92.2", + "tabs": "0.92.3", "timecop": "0.33.1", "tree-view": "0.206.1", "update-package-dependencies": "0.10.0", From e5e1226ac062ca67cb291e2a3cfe72a1ac6122be Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sat, 23 Apr 2016 11:49:20 -0400 Subject: [PATCH 816/971] :arrow_up: language-c@0.51.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9317866ef..98dbd7385 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "welcome": "0.34.0", "whitespace": "0.32.2", "wrap-guide": "0.38.1", - "language-c": "0.51.3", + "language-c": "0.51.4", "language-clojure": "0.20.0", "language-coffee-script": "0.47.0", "language-csharp": "0.12.1", From cf79619292fcad749fccf56c8179c656c04727dc Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Sat, 23 Apr 2016 18:31:07 -0700 Subject: [PATCH 817/971] :arrow_up: tree-view@0.206.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 98dbd7385..94478dbf0 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.112.0", "tabs": "0.92.3", "timecop": "0.33.1", - "tree-view": "0.206.1", + "tree-view": "0.206.2", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From f6f5c93b01a5a867b048f3da2baa8fac931b653a Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Sat, 23 Apr 2016 18:31:27 -0700 Subject: [PATCH 818/971] :arrow_up: tabs@0.93.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 94478dbf0..9dd47b12b 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.2.4", "styleguide": "0.45.2", "symbols-view": "0.112.0", - "tabs": "0.92.3", + "tabs": "0.93.0", "timecop": "0.33.1", "tree-view": "0.206.2", "update-package-dependencies": "0.10.0", From 4825d7ee4b6fcf53b8a9df674a91b07fd4c7edcd Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Sat, 23 Apr 2016 20:15:31 -0700 Subject: [PATCH 819/971] :arrow_up: tabs@0.93.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9dd47b12b..957d262f0 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.2.4", "styleguide": "0.45.2", "symbols-view": "0.112.0", - "tabs": "0.93.0", + "tabs": "0.93.1", "timecop": "0.33.1", "tree-view": "0.206.2", "update-package-dependencies": "0.10.0", From 8bf34d45d7f4857788b63ea094d12dc7f95eb8ee Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 14:12:07 -0400 Subject: [PATCH 820/971] Don't require notification manager in TextEditor Instead expose getCursorScope and let users outside the text editor show the notification. --- src/text-editor.coffee | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 0d1b3795e..9d81f7dd0 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -79,7 +79,6 @@ class TextEditor extends Model state.displayBuffer = displayBuffer state.selectionsMarkerLayer = displayBuffer.getMarkerLayer(state.selectionsMarkerLayerId) state.config = atomEnvironment.config - state.notificationManager = atomEnvironment.notifications state.packageManager = atomEnvironment.packages state.clipboard = atomEnvironment.clipboard state.viewRegistry = atomEnvironment.views @@ -100,12 +99,11 @@ class TextEditor extends Model @softTabs, @firstVisibleScreenRow, @firstVisibleScreenColumn, initialLine, initialColumn, tabLength, softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, - @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, + @packageManager, @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate, grammar, showInvisibles, @autoHeight, @scrollPastEnd } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? - throw new Error("Must pass a notificationManager parameter when constructing TextEditors") unless @notificationManager? throw new Error("Must pass a packageManager parameter when constructing TextEditors") unless @packageManager? throw new Error("Must pass a clipboard parameter when constructing TextEditors") unless @clipboard? throw new Error("Must pass a viewRegistry parameter when constructing TextEditors") unless @viewRegistry? @@ -520,7 +518,7 @@ class TextEditor extends Model softTabs = @getSoftTabs() newEditor = new TextEditor({ @buffer, displayBuffer, selectionsMarkerLayer, @tabLength, softTabs, - suppressCursorCreation: true, @config, @notificationManager, @packageManager, + suppressCursorCreation: true, @config, @packageManager, @firstVisibleScreenRow, @firstVisibleScreenColumn, @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate }) @@ -2827,13 +2825,9 @@ class TextEditor extends Model @commentScopeSelector ?= new TextMateScopeSelector('comment.*') @commentScopeSelector.matches(@scopeDescriptorForBufferPosition([bufferRow, match.index]).scopes) - logCursorScope: -> - scopeDescriptor = @getLastCursor().getScopeDescriptor() - list = scopeDescriptor.scopes.toString().split(',') - list = list.map (item) -> "* #{item}" - content = "Scopes at Cursor\n#{list.join('\n')}" - - @notificationManager.addInfo(content, dismissable: true) + # Get the scope descriptor at the cursor. + getCursorScope: -> + @getLastCursor().getScopeDescriptor() # {Delegates to: DisplayBuffer.tokenForBufferPosition} tokenForBufferPosition: (bufferPosition) -> @displayBuffer.tokenForBufferPosition(bufferPosition) From 48e49061e6bdece8d2f128987a71649aecbea7c9 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 14:12:20 -0400 Subject: [PATCH 821/971] Don't need to pass in the notification manager anymore. --- src/workspace.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index b8ed79fd6..838a1dcec 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -564,7 +564,7 @@ class Workspace extends Model # Returns a {TextEditor}. buildTextEditor: (params) -> params = _.extend({ - @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, + @config, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate }, params) new TextEditor(params) From 694af93009b5339171cb8f5f98b572a99bc11024 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 14:12:38 -0400 Subject: [PATCH 822/971] Implement the show cursor scope functionality in the default commands. --- src/atom-environment.coffee | 2 +- src/register-default-commands.coffee | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index ffff564ba..cf54ee0bc 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -255,7 +255,7 @@ class AtomEnvironment extends Model @deserializers.add(TextBuffer) registerDefaultCommands: -> - registerDefaultCommands({commandRegistry: @commands, @config, @commandInstaller}) + registerDefaultCommands({commandRegistry: @commands, @config, @commandInstaller, notificationManager: @notifications}) registerDefaultViewProviders: -> @views.addViewProvider Workspace, (model, env) -> diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index bb3630117..4f329e943 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -1,6 +1,6 @@ {ipcRenderer} = require 'electron' -module.exports = ({commandRegistry, commandInstaller, config}) -> +module.exports = ({commandRegistry, commandInstaller, config, notificationManager}) -> commandRegistry.add 'atom-workspace', 'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem() 'pane:show-previous-recently-used-item': -> @getModel().getActivePane().activatePreviousRecentlyUsedItem() @@ -187,7 +187,7 @@ module.exports = ({commandRegistry, commandInstaller, config}) -> 'editor:fold-at-indent-level-7': -> @foldAllAtIndentLevel(6) 'editor:fold-at-indent-level-8': -> @foldAllAtIndentLevel(7) 'editor:fold-at-indent-level-9': -> @foldAllAtIndentLevel(8) - 'editor:log-cursor-scope': -> @logCursorScope() + 'editor:log-cursor-scope': -> showCursorScope(@getCursorScope(), notificationManager) 'editor:copy-path': -> @copyPathToClipboard(false) 'editor:copy-project-path': -> @copyPathToClipboard(true) 'editor:toggle-indent-guide': -> config.set('editor.showIndentGuide', not config.get('editor.showIndentGuide')) @@ -232,3 +232,10 @@ stopEventPropagationAndGroupUndo = (config, commandListeners) -> model.transact config.get('editor.undoGroupingInterval'), -> commandListener.call(model, event) newCommandListeners + +showCursorScope = (descriptor, notificationManager) -> + list = descriptor.scopes.toString().split(',') + list = list.map (item) -> "* #{item}" + content = "Scopes at Cursor\n#{list.join('\n')}" + + notificationManager.addInfo(content, dismissable: true) From d494f065df90065c0b784ded343b1baac1bede4d Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 14:12:42 -0400 Subject: [PATCH 823/971] Update the spec. --- spec/text-editor-spec.coffee | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 7d85f1cad..9dcb61285 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1189,14 +1189,10 @@ describe "TextEditor", -> cursor2 = editor.addCursorAtBufferPosition([1, 4]) expect(cursor2.marker).toBe cursor1.marker - describe '.logCursorScope()', -> - beforeEach -> - spyOn(atom.notifications, 'addInfo') - - it 'opens a notification', -> - editor.logCursorScope() - - expect(atom.notifications.addInfo).toHaveBeenCalled() + describe '.getCursorScope()', -> + it 'returns the current scope', -> + descriptor = editor.getCursorScope() + expect(descriptor.scopes).toContain ('source.js') describe "selection", -> selection = null From 956e037681d023877147a15da1916f5d1737963f Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 14:20:54 -0400 Subject: [PATCH 824/971] But without the space. --- spec/text-editor-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 9dcb61285..bc08b3f0e 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1192,7 +1192,7 @@ describe "TextEditor", -> describe '.getCursorScope()', -> it 'returns the current scope', -> descriptor = editor.getCursorScope() - expect(descriptor.scopes).toContain ('source.js') + expect(descriptor.scopes).toContain('source.js') describe "selection", -> selection = null From 6b309be6dac0ee8e2ac3028a36f7d92526f8ec11 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 14:49:43 -0400 Subject: [PATCH 825/971] Propagate a did-use-grammar event out of the tokenized buffer. --- src/display-buffer.coffee | 3 +++ src/text-editor.coffee | 3 +++ src/tokenized-buffer.coffee | 5 ++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index d01ad03c9..717ab5e4f 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -137,6 +137,9 @@ class DisplayBuffer extends Model onDidChangeGrammar: (callback) -> @tokenizedBuffer.onDidChangeGrammar(callback) + onDidUseGrammar: (callback) -> + @tokenizedBuffer.onDidUseGrammar(callback) + onDidTokenize: (callback) -> @tokenizedBuffer.onDidTokenize(callback) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 9d81f7dd0..aaa63b2e5 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -325,6 +325,9 @@ class TextEditor extends Model onDidChangeGrammar: (callback) -> @emitter.on 'did-change-grammar', callback + onDidUseGrammar: (callback) -> + @displayBuffer.onDidUseGrammar(callback) + # Extended: Calls your `callback` when the result of {::isModified} changes. # # * `callback` {Function} diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 5c62f9ecd..1188aeaf0 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -126,9 +126,12 @@ class TokenizedBuffer extends Model @disposables.add(@configSubscriptions) @retokenizeLines() - @packageManager.triggerActivationHook("#{grammar.packageName}:grammar-used") + @emitter.emit 'did-use-grammar', grammar @emitter.emit 'did-change-grammar', grammar + onDidUseGrammar: (callback) -> + @emitter.on 'did-use-grammar', callback + getGrammarSelectionContent: -> @buffer.getTextInRange([[0, 0], [10, 0]]) From e8a4f38c6945f0f50691a46f3c73b3582fb781c5 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 14:50:14 -0400 Subject: [PATCH 826/971] Don't need to pass the package manager through anymore. --- src/display-buffer.coffee | 7 +++---- src/text-editor.coffee | 8 +++----- src/tokenized-buffer.coffee | 3 +-- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 717ab5e4f..ccc68535f 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -35,7 +35,6 @@ class DisplayBuffer extends Model state.config = atomEnvironment.config state.assert = atomEnvironment.assert state.grammarRegistry = atomEnvironment.grammars - state.packageManager = atomEnvironment.packages new this(state) constructor: (params={}) -> @@ -43,7 +42,7 @@ class DisplayBuffer extends Model { tabLength, @editorWidthInChars, @tokenizedBuffer, @foldsMarkerLayer, buffer, - ignoreInvisibles, @largeFileMode, @config, @assert, @grammarRegistry, @packageManager + ignoreInvisibles, @largeFileMode, @config, @assert, @grammarRegistry } = params @emitter = new Emitter @@ -51,7 +50,7 @@ class DisplayBuffer extends Model @tokenizedBuffer ?= new TokenizedBuffer({ tabLength, buffer, ignoreInvisibles, @largeFileMode, @config, - @grammarRegistry, @packageManager, @assert + @grammarRegistry, @assert }) @buffer = @tokenizedBuffer.buffer @charWidthsByScope = {} @@ -122,7 +121,7 @@ class DisplayBuffer extends Model foldsMarkerLayer = @foldsMarkerLayer.copy() new DisplayBuffer({ @buffer, tabLength: @getTabLength(), @largeFileMode, @config, @assert, - @grammarRegistry, @packageManager, foldsMarkerLayer + @grammarRegistry, foldsMarkerLayer }) updateAllScreenLines: -> diff --git a/src/text-editor.coffee b/src/text-editor.coffee index aaa63b2e5..865026e20 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -79,7 +79,6 @@ class TextEditor extends Model state.displayBuffer = displayBuffer state.selectionsMarkerLayer = displayBuffer.getMarkerLayer(state.selectionsMarkerLayerId) state.config = atomEnvironment.config - state.packageManager = atomEnvironment.packages state.clipboard = atomEnvironment.clipboard state.viewRegistry = atomEnvironment.views state.grammarRegistry = atomEnvironment.grammars @@ -99,12 +98,11 @@ class TextEditor extends Model @softTabs, @firstVisibleScreenRow, @firstVisibleScreenColumn, initialLine, initialColumn, tabLength, softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, - @packageManager, @clipboard, @viewRegistry, @grammarRegistry, + @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate, grammar, showInvisibles, @autoHeight, @scrollPastEnd } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? - throw new Error("Must pass a packageManager parameter when constructing TextEditors") unless @packageManager? throw new Error("Must pass a clipboard parameter when constructing TextEditors") unless @clipboard? throw new Error("Must pass a viewRegistry parameter when constructing TextEditors") unless @viewRegistry? throw new Error("Must pass a grammarRegistry parameter when constructing TextEditors") unless @grammarRegistry? @@ -127,7 +125,7 @@ class TextEditor extends Model buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ buffer, tabLength, softWrapped, ignoreInvisibles: @mini or not showInvisibles, largeFileMode, - @config, @assert, @grammarRegistry, @packageManager + @config, @assert, @grammarRegistry }) @buffer = @displayBuffer.buffer @selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true) @@ -521,7 +519,7 @@ class TextEditor extends Model softTabs = @getSoftTabs() newEditor = new TextEditor({ @buffer, displayBuffer, selectionsMarkerLayer, @tabLength, softTabs, - suppressCursorCreation: true, @config, @packageManager, + suppressCursorCreation: true, @config, @firstVisibleScreenRow, @firstVisibleScreenColumn, @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate }) diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 1188aeaf0..f01498b68 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -29,14 +29,13 @@ class TokenizedBuffer extends Model state.buffer = atomEnvironment.project.bufferForPathSync(state.bufferPath) state.config = atomEnvironment.config state.grammarRegistry = atomEnvironment.grammars - state.packageManager = atomEnvironment.packages state.assert = atomEnvironment.assert new this(state) constructor: (params) -> { @buffer, @tabLength, @ignoreInvisibles, @largeFileMode, @config, - @grammarRegistry, @packageManager, @assert, grammarScopeName + @grammarRegistry, @assert, grammarScopeName } = params @emitter = new Emitter From b1301a5f7416236a99cbe6203c91284f7d486355 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 14:50:34 -0400 Subject: [PATCH 827/971] Trigger the activation hook outside the text editor. --- src/workspace.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index 838a1dcec..a54ace2a2 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -550,9 +550,15 @@ class Workspace extends Model @project.bufferForPath(filePath, options).then (buffer) => editor = @buildTextEditor(_.extend({buffer, largeFileMode}, options)) disposable = atom.textEditors.add(editor) - editor.onDidDestroy -> disposable.dispose() + grammarSubscription = editor.onDidUseGrammar(@handleDidUseGrammar.bind(this)) + editor.onDidDestroy -> + grammarSubscription.dispose() + disposable.dispose() editor + handleDidUseGrammar: (grammar) -> + @packageManager.triggerActivationHook("#{grammar.packageName}:grammar-used") + # Public: Returns a {Boolean} that is `true` if `object` is a `TextEditor`. # # * `object` An {Object} you want to perform the check against. From 02bbb140529357db559610011a4ba60a5b19771c Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 15:02:16 -0400 Subject: [PATCH 828/971] Better method name --- src/workspace.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index a54ace2a2..c8567489d 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -550,13 +550,13 @@ class Workspace extends Model @project.bufferForPath(filePath, options).then (buffer) => editor = @buildTextEditor(_.extend({buffer, largeFileMode}, options)) disposable = atom.textEditors.add(editor) - grammarSubscription = editor.onDidUseGrammar(@handleDidUseGrammar.bind(this)) + grammarSubscription = editor.onDidUseGrammar(@activateGrammar.bind(this)) editor.onDidDestroy -> grammarSubscription.dispose() disposable.dispose() editor - handleDidUseGrammar: (grammar) -> + activateGrammar: (grammar) -> @packageManager.triggerActivationHook("#{grammar.packageName}:grammar-used") # Public: Returns a {Boolean} that is `true` if `object` is a `TextEditor`. From 9014634b3a7d3591c68b66c3259cb410fbaabfd3 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 15:09:03 -0400 Subject: [PATCH 829/971] Just use the already existing change grammar event. --- src/display-buffer.coffee | 3 --- src/text-editor.coffee | 3 --- src/tokenized-buffer.coffee | 4 ---- src/workspace.coffee | 2 +- 4 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index ccc68535f..109b791a1 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -136,9 +136,6 @@ class DisplayBuffer extends Model onDidChangeGrammar: (callback) -> @tokenizedBuffer.onDidChangeGrammar(callback) - onDidUseGrammar: (callback) -> - @tokenizedBuffer.onDidUseGrammar(callback) - onDidTokenize: (callback) -> @tokenizedBuffer.onDidTokenize(callback) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 865026e20..aeb5ebe5c 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -323,9 +323,6 @@ class TextEditor extends Model onDidChangeGrammar: (callback) -> @emitter.on 'did-change-grammar', callback - onDidUseGrammar: (callback) -> - @displayBuffer.onDidUseGrammar(callback) - # Extended: Calls your `callback` when the result of {::isModified} changes. # # * `callback` {Function} diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index f01498b68..38e92f247 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -125,12 +125,8 @@ class TokenizedBuffer extends Model @disposables.add(@configSubscriptions) @retokenizeLines() - @emitter.emit 'did-use-grammar', grammar @emitter.emit 'did-change-grammar', grammar - onDidUseGrammar: (callback) -> - @emitter.on 'did-use-grammar', callback - getGrammarSelectionContent: -> @buffer.getTextInRange([[0, 0], [10, 0]]) diff --git a/src/workspace.coffee b/src/workspace.coffee index c8567489d..96ba00259 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -550,7 +550,7 @@ class Workspace extends Model @project.bufferForPath(filePath, options).then (buffer) => editor = @buildTextEditor(_.extend({buffer, largeFileMode}, options)) disposable = atom.textEditors.add(editor) - grammarSubscription = editor.onDidUseGrammar(@activateGrammar.bind(this)) + grammarSubscription = editor.onDidChangeGrammar(@activateGrammar.bind(this)) editor.onDidDestroy -> grammarSubscription.dispose() disposable.dispose() From 2266f1e3b67a054f2a816dfe9a21876f4f1ebd82 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 15:15:31 -0400 Subject: [PATCH 830/971] Revert "Just use the already existing change grammar event." This reverts commit 9014634b3a7d3591c68b66c3259cb410fbaabfd3. --- src/display-buffer.coffee | 3 +++ src/text-editor.coffee | 3 +++ src/tokenized-buffer.coffee | 4 ++++ src/workspace.coffee | 2 +- 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 109b791a1..ccc68535f 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -136,6 +136,9 @@ class DisplayBuffer extends Model onDidChangeGrammar: (callback) -> @tokenizedBuffer.onDidChangeGrammar(callback) + onDidUseGrammar: (callback) -> + @tokenizedBuffer.onDidUseGrammar(callback) + onDidTokenize: (callback) -> @tokenizedBuffer.onDidTokenize(callback) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index aeb5ebe5c..865026e20 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -323,6 +323,9 @@ class TextEditor extends Model onDidChangeGrammar: (callback) -> @emitter.on 'did-change-grammar', callback + onDidUseGrammar: (callback) -> + @displayBuffer.onDidUseGrammar(callback) + # Extended: Calls your `callback` when the result of {::isModified} changes. # # * `callback` {Function} diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 38e92f247..f01498b68 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -125,8 +125,12 @@ class TokenizedBuffer extends Model @disposables.add(@configSubscriptions) @retokenizeLines() + @emitter.emit 'did-use-grammar', grammar @emitter.emit 'did-change-grammar', grammar + onDidUseGrammar: (callback) -> + @emitter.on 'did-use-grammar', callback + getGrammarSelectionContent: -> @buffer.getTextInRange([[0, 0], [10, 0]]) diff --git a/src/workspace.coffee b/src/workspace.coffee index 96ba00259..c8567489d 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -550,7 +550,7 @@ class Workspace extends Model @project.bufferForPath(filePath, options).then (buffer) => editor = @buildTextEditor(_.extend({buffer, largeFileMode}, options)) disposable = atom.textEditors.add(editor) - grammarSubscription = editor.onDidChangeGrammar(@activateGrammar.bind(this)) + grammarSubscription = editor.onDidUseGrammar(@activateGrammar.bind(this)) editor.onDidDestroy -> grammarSubscription.dispose() disposable.dispose() From eaf6036a2c76bc94ed01e93a24f400fce65c018e Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 15:34:37 -0400 Subject: [PATCH 831/971] Wait a tick before sending the event. --- src/tokenized-buffer.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index f01498b68..b1b0749b4 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -125,9 +125,14 @@ class TokenizedBuffer extends Model @disposables.add(@configSubscriptions) @retokenizeLines() - @emitter.emit 'did-use-grammar', grammar + @emitter.emit 'did-change-grammar', grammar + # Delay this to the next tick to ensure whoever created the buffer has the + # change to listen for this event before we send it. + process.nextTick => + @emitter.emit 'did-use-grammar', grammar + onDidUseGrammar: (callback) -> @emitter.on 'did-use-grammar', callback From cf1b4e22172c9bd512fba0d0947b064eaa0be10c Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 15:34:44 -0400 Subject: [PATCH 832/971] Another new name. --- src/workspace.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index c8567489d..975300ae4 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -550,13 +550,13 @@ class Workspace extends Model @project.bufferForPath(filePath, options).then (buffer) => editor = @buildTextEditor(_.extend({buffer, largeFileMode}, options)) disposable = atom.textEditors.add(editor) - grammarSubscription = editor.onDidUseGrammar(@activateGrammar.bind(this)) + grammarSubscription = editor.onDidUseGrammar(@handleGrammarUsed.bind(this)) editor.onDidDestroy -> grammarSubscription.dispose() disposable.dispose() editor - activateGrammar: (grammar) -> + handleGrammarUsed: (grammar) -> @packageManager.triggerActivationHook("#{grammar.packageName}:grammar-used") # Public: Returns a {Boolean} that is `true` if `object` is a `TextEditor`. From 6852d6e91c290e104b3f10ad66116e9660210781 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 15:34:46 -0400 Subject: [PATCH 833/971] Test it. --- spec/workspace-spec.coffee | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index fc22f07c3..c61a57bf4 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -1619,3 +1619,15 @@ describe "Workspace", -> escapeStringRegex = (str) -> str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') + + describe "grammar activation", -> + it "activates grammars", -> + editor = null + + atom.workspace.handleGrammarUsed = jasmine.createSpy() + + waitsForPromise -> atom.workspace.open('sample-with-comments.js').then (o) -> editor = o + runs -> + atom.grammars.setGrammarOverrideForPath(editor.getPath(), 'source.coffee') + editor.reloadGrammar() + waitsFor -> atom.workspace.handleGrammarUsed.callCount is 1 From 759d64501d1b1b80611b29a435ef14ef45f9919e Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 16:07:49 -0400 Subject: [PATCH 834/971] Better test. --- spec/workspace-spec.coffee | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index c61a57bf4..b84e873da 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -1617,17 +1617,21 @@ describe "Workspace", -> runs -> expect(pane.getPendingItem()).toBeFalsy() - escapeStringRegex = (str) -> - str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') - describe "grammar activation", -> - it "activates grammars", -> + beforeEach -> + waitsForPromise -> + atom.packages.activatePackage('language-javascript') + + it "notifies the workspace of which grammar is used", -> editor = null - atom.workspace.handleGrammarUsed = jasmine.createSpy() + grammarUsed = jasmine.createSpy() + atom.workspace.handleGrammarUsed = grammarUsed waitsForPromise -> atom.workspace.open('sample-with-comments.js').then (o) -> editor = o + waitsFor -> grammarUsed.callCount is 1 runs -> - atom.grammars.setGrammarOverrideForPath(editor.getPath(), 'source.coffee') - editor.reloadGrammar() - waitsFor -> atom.workspace.handleGrammarUsed.callCount is 1 + expect(grammarUsed.argsForCall[0][0].name).toBe 'JavaScript' + + escapeStringRegex = (str) -> + str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') From 58d8e6bca8d5f0c6e2bd6e26b48c3d98d5612b20 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 16:11:38 -0400 Subject: [PATCH 835/971] Typo --- src/tokenized-buffer.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index b1b0749b4..7e3c4fe49 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -129,7 +129,7 @@ class TokenizedBuffer extends Model @emitter.emit 'did-change-grammar', grammar # Delay this to the next tick to ensure whoever created the buffer has the - # change to listen for this event before we send it. + # chance to listen for this event before we send it. process.nextTick => @emitter.emit 'did-use-grammar', grammar From 3a63f90ab2469a61975ecc084c36d41270b26938 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 16:24:27 -0400 Subject: [PATCH 836/971] We just say getElement now. --- src/text-editor.coffee | 50 +++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 865026e20..bd0c9f9f8 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -486,12 +486,12 @@ class TextEditor extends Model onDidChangeScrollTop: (callback) -> Grim.deprecate("This is now a view method. Call TextEditorElement::onDidChangeScrollTop instead.") - @viewRegistry.getView(this).onDidChangeScrollTop(callback) + @getElement().onDidChangeScrollTop(callback) onDidChangeScrollLeft: (callback) -> Grim.deprecate("This is now a view method. Call TextEditorElement::onDidChangeScrollLeft instead.") - @viewRegistry.getView(this).onDidChangeScrollLeft(callback) + @getElement().onDidChangeScrollLeft(callback) onDidRequestAutoscroll: (callback) -> @displayBuffer.onDidRequestAutoscroll(callback) @@ -3133,24 +3133,24 @@ class TextEditor extends Model scrollToTop: -> Grim.deprecate("This is now a view method. Call TextEditorElement::scrollToTop instead.") - @viewRegistry.getView(this).scrollToTop() + @getElement().scrollToTop() scrollToBottom: -> Grim.deprecate("This is now a view method. Call TextEditorElement::scrollToTop instead.") - @viewRegistry.getView(this).scrollToBottom() + @getElement().scrollToBottom() scrollToScreenRange: (screenRange, options) -> @displayBuffer.scrollToScreenRange(screenRange, options) getHorizontalScrollbarHeight: -> Grim.deprecate("This is now a view method. Call TextEditorElement::getHorizontalScrollbarHeight instead.") - @viewRegistry.getView(this).getHorizontalScrollbarHeight() + @getElement().getHorizontalScrollbarHeight() getVerticalScrollbarWidth: -> Grim.deprecate("This is now a view method. Call TextEditorElement::getVerticalScrollbarWidth instead.") - @viewRegistry.getView(this).getVerticalScrollbarWidth() + @getElement().getVerticalScrollbarWidth() pageUp: -> @moveUp(@getRowsPerPage()) @@ -3217,11 +3217,11 @@ class TextEditor extends Model pixelPositionForBufferPosition: (bufferPosition) -> Grim.deprecate("This method is deprecated on the model layer. Use `TextEditorElement::pixelPositionForBufferPosition` instead") - @viewRegistry.getView(this).pixelPositionForBufferPosition(bufferPosition) + @getElement().pixelPositionForBufferPosition(bufferPosition) pixelPositionForScreenPosition: (screenPosition) -> Grim.deprecate("This method is deprecated on the model layer. Use `TextEditorElement::pixelPositionForScreenPosition` instead") - @viewRegistry.getView(this).pixelPositionForScreenPosition(screenPosition) + @getElement().pixelPositionForScreenPosition(screenPosition) getSelectionMarkerAttributes: -> {type: 'selection', invalidate: 'never'} @@ -3250,7 +3250,7 @@ class TextEditor extends Model @displayBuffer.setHeight(height) else Grim.deprecate("This is now a view method. Call TextEditorElement::setHeight instead.") - @viewRegistry.getView(this).setHeight(height) + @getElement().setHeight(height) getHeight: -> Grim.deprecate("This is now a view method. Call TextEditorElement::getHeight instead.") @@ -3263,7 +3263,7 @@ class TextEditor extends Model @displayBuffer.setWidth(width) else Grim.deprecate("This is now a view method. Call TextEditorElement::setWidth instead.") - @viewRegistry.getView(this).setWidth(width) + @getElement().setWidth(width) getWidth: -> Grim.deprecate("This is now a view method. Call TextEditorElement::getWidth instead.") @@ -3307,77 +3307,77 @@ class TextEditor extends Model getScrollTop: -> Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollTop instead.") - @viewRegistry.getView(this).getScrollTop() + @getElement().getScrollTop() setScrollTop: (scrollTop) -> Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollTop instead.") - @viewRegistry.getView(this).setScrollTop(scrollTop) + @getElement().setScrollTop(scrollTop) getScrollBottom: -> Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollBottom instead.") - @viewRegistry.getView(this).getScrollBottom() + @getElement().getScrollBottom() setScrollBottom: (scrollBottom) -> Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollBottom instead.") - @viewRegistry.getView(this).setScrollBottom(scrollBottom) + @getElement().setScrollBottom(scrollBottom) getScrollLeft: -> Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollLeft instead.") - @viewRegistry.getView(this).getScrollLeft() + @getElement().getScrollLeft() setScrollLeft: (scrollLeft) -> Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollLeft instead.") - @viewRegistry.getView(this).setScrollLeft(scrollLeft) + @getElement().setScrollLeft(scrollLeft) getScrollRight: -> Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollRight instead.") - @viewRegistry.getView(this).getScrollRight() + @getElement().getScrollRight() setScrollRight: (scrollRight) -> Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollRight instead.") - @viewRegistry.getView(this).setScrollRight(scrollRight) + @getElement().setScrollRight(scrollRight) getScrollHeight: -> Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollHeight instead.") - @viewRegistry.getView(this).getScrollHeight() + @getElement().getScrollHeight() getScrollWidth: -> Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollWidth instead.") - @viewRegistry.getView(this).getScrollWidth() + @getElement().getScrollWidth() getMaxScrollTop: -> Grim.deprecate("This is now a view method. Call TextEditorElement::getMaxScrollTop instead.") - @viewRegistry.getView(this).getMaxScrollTop() + @getElement().getMaxScrollTop() intersectsVisibleRowRange: (startRow, endRow) -> Grim.deprecate("This is now a view method. Call TextEditorElement::intersectsVisibleRowRange instead.") - @viewRegistry.getView(this).intersectsVisibleRowRange(startRow, endRow) + @getElement().intersectsVisibleRowRange(startRow, endRow) selectionIntersectsVisibleRowRange: (selection) -> Grim.deprecate("This is now a view method. Call TextEditorElement::selectionIntersectsVisibleRowRange instead.") - @viewRegistry.getView(this).selectionIntersectsVisibleRowRange(selection) + @getElement().selectionIntersectsVisibleRowRange(selection) screenPositionForPixelPosition: (pixelPosition) -> Grim.deprecate("This is now a view method. Call TextEditorElement::screenPositionForPixelPosition instead.") - @viewRegistry.getView(this).screenPositionForPixelPosition(pixelPosition) + @getElement().screenPositionForPixelPosition(pixelPosition) pixelRectForScreenRange: (screenRange) -> Grim.deprecate("This is now a view method. Call TextEditorElement::pixelRectForScreenRange instead.") - @viewRegistry.getView(this).pixelRectForScreenRange(screenRange) + @getElement().pixelRectForScreenRange(screenRange) ### Section: Utility From 1c6d9728c49c754a459c50af20eab4bc5f93b576 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 16:25:36 -0400 Subject: [PATCH 837/971] Don't need to pass packageManager in anymore. --- src/workspace.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index 975300ae4..9a52dc937 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -570,8 +570,8 @@ class Workspace extends Model # Returns a {TextEditor}. buildTextEditor: (params) -> params = _.extend({ - @config, @packageManager, @clipboard, @viewRegistry, - @grammarRegistry, @project, @assert, @applicationDelegate + @config, @clipboard, @viewRegistry, @grammarRegistry, + @project, @assert, @applicationDelegate }, params) new TextEditor(params) From 8d7f1b8fba62694677aeeb16d3c6870a6ddeb323 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 16:26:28 -0400 Subject: [PATCH 838/971] Don't need to pass view registry through anymore. --- src/text-editor.coffee | 7 ++----- src/workspace.coffee | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index bd0c9f9f8..e30696479 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -80,7 +80,6 @@ class TextEditor extends Model state.selectionsMarkerLayer = displayBuffer.getMarkerLayer(state.selectionsMarkerLayerId) state.config = atomEnvironment.config state.clipboard = atomEnvironment.clipboard - state.viewRegistry = atomEnvironment.views state.grammarRegistry = atomEnvironment.grammars state.project = atomEnvironment.project state.assert = atomEnvironment.assert.bind(atomEnvironment) @@ -97,14 +96,12 @@ class TextEditor extends Model { @softTabs, @firstVisibleScreenRow, @firstVisibleScreenColumn, initialLine, initialColumn, tabLength, softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, - @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, - @clipboard, @viewRegistry, @grammarRegistry, + @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @clipboard, @grammarRegistry, @project, @assert, @applicationDelegate, grammar, showInvisibles, @autoHeight, @scrollPastEnd } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? throw new Error("Must pass a clipboard parameter when constructing TextEditors") unless @clipboard? - throw new Error("Must pass a viewRegistry parameter when constructing TextEditors") unless @viewRegistry? throw new Error("Must pass a grammarRegistry parameter when constructing TextEditors") unless @grammarRegistry? throw new Error("Must pass a project parameter when constructing TextEditors") unless @project? throw new Error("Must pass an assert parameter when constructing TextEditors") unless @assert? @@ -521,7 +518,7 @@ class TextEditor extends Model @buffer, displayBuffer, selectionsMarkerLayer, @tabLength, softTabs, suppressCursorCreation: true, @config, @firstVisibleScreenRow, @firstVisibleScreenColumn, - @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate + @clipboard, @grammarRegistry, @project, @assert, @applicationDelegate }) newEditor diff --git a/src/workspace.coffee b/src/workspace.coffee index 9a52dc937..c6ab13faf 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -570,7 +570,7 @@ class Workspace extends Model # Returns a {TextEditor}. buildTextEditor: (params) -> params = _.extend({ - @config, @clipboard, @viewRegistry, @grammarRegistry, + @config, @packageManager, @clipboard, @grammarRegistry, @project, @assert, @applicationDelegate }, params) new TextEditor(params) From 856697e55f7815cb78b7138c3b2fb59d93c8a403 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 16:31:28 -0400 Subject: [PATCH 839/971] Uh we seriously don't need package manager. Not sure how that slipped back in there. --- src/workspace.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index c6ab13faf..8f973d2fa 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -570,7 +570,7 @@ class Workspace extends Model # Returns a {TextEditor}. buildTextEditor: (params) -> params = _.extend({ - @config, @packageManager, @clipboard, @grammarRegistry, + @config, @clipboard, @grammarRegistry, @project, @assert, @applicationDelegate }, params) new TextEditor(params) From 5cf532ebe889b809a465c85333cb5e89643d756b Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 17:02:36 -0400 Subject: [PATCH 840/971] Move checkoutHeadRevision to Workspace. --- src/register-default-commands.coffee | 2 +- src/text-editor.coffee | 19 ------------------- src/workspace.coffee | 19 +++++++++++++++++++ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index 4f329e943..035750363 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -204,7 +204,7 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage 'editor:newline-below': -> @insertNewlineBelow() 'editor:newline-above': -> @insertNewlineAbove() 'editor:toggle-line-comments': -> @toggleLineCommentsInSelection() - 'editor:checkout-head-revision': -> @checkoutHeadRevision() + 'editor:checkout-head-revision': -> atom.workspace.checkoutHeadRevision(this) 'editor:move-line-up': -> @moveLineUp() 'editor:move-line-down': -> @moveLineDown() 'editor:move-selection-left': -> @moveSelectionLeft() diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e30696479..85e285f8a 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -712,25 +712,6 @@ class TextEditor extends Model # via {Pane::saveItemAs}. getSaveDialogOptions: -> {} - checkoutHeadRevision: -> - if @getPath() - checkoutHead = => - @project.repositoryForDirectory(new Directory(@getDirectoryPath())) - .then (repository) => - repository?.async.checkoutHeadForEditor(this) - - if @config.get('editor.confirmCheckoutHeadRevision') - @applicationDelegate.confirm - message: 'Confirm Checkout HEAD Revision' - detailedMessage: "Are you sure you want to discard all changes to \"#{@getFileName()}\" since the last Git commit?" - buttons: - OK: checkoutHead - Cancel: null - else - checkoutHead() - else - Promise.resolve(false) - ### Section: Reading Text ### diff --git a/src/workspace.coffee b/src/workspace.coffee index 8f973d2fa..9e662c984 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -1085,3 +1085,22 @@ class Workspace extends Model inProcessFinished = true checkFinished() + + checkoutHeadRevision: (editor) -> + if editor.getPath() + checkoutHead = => + @project.repositoryForDirectory(new Directory(editor.getDirectoryPath())) + .then (repository) => + repository?.async.checkoutHeadForEditor(editor) + + if @config.get('editor.confirmCheckoutHeadRevision') + @applicationDelegate.confirm + message: 'Confirm Checkout HEAD Revision' + detailedMessage: "Are you sure you want to discard all changes to \"#{editor.getFileName()}\" since the last Git commit?" + buttons: + OK: checkoutHead + Cancel: null + else + checkoutHead() + else + Promise.resolve(false) From 9fa669b2937a7d985a80fa16e96853ba98578889 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 17:02:51 -0400 Subject: [PATCH 841/971] Set the initial path after saving in Project. --- src/project.coffee | 4 ++++ src/text-editor.coffee | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/project.coffee b/src/project.coffee index 93a3ed496..70d5b93a2 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -391,6 +391,10 @@ class Project extends Model subscribeToBuffer: (buffer) -> buffer.onDidDestroy => @removeBuffer(buffer) + buffer.onDidChangePath => + console.log('did change path! ' + buffer.getPath()) + unless @getPaths().length > 0 + @setPaths([path.dirname(buffer.getPath())]) buffer.onWillThrowWatchError ({error, handle}) => handle() @notificationManager.addWarning """ diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 85e285f8a..4ed926848 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -166,8 +166,6 @@ class TextEditor extends Model subscribeToBuffer: -> @buffer.retain() @disposables.add @buffer.onDidChangePath => - unless @project.getPaths().length > 0 - @project.setPaths([path.dirname(@getPath())]) @emitter.emit 'did-change-title', @getTitle() @emitter.emit 'did-change-path', @getPath() @disposables.add @buffer.onDidChangeEncoding => From 7ed4d60967277a4dc890b2066522f52df7a09e0d Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 17:03:26 -0400 Subject: [PATCH 842/971] Don't need to pass Project around anymore. --- src/text-editor.coffee | 6 ++---- src/workspace.coffee | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 4ed926848..b9996bd8d 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -81,7 +81,6 @@ class TextEditor extends Model state.config = atomEnvironment.config state.clipboard = atomEnvironment.clipboard state.grammarRegistry = atomEnvironment.grammars - state.project = atomEnvironment.project state.assert = atomEnvironment.assert.bind(atomEnvironment) state.applicationDelegate = atomEnvironment.applicationDelegate editor = new this(state) @@ -97,13 +96,12 @@ class TextEditor extends Model @softTabs, @firstVisibleScreenRow, @firstVisibleScreenColumn, initialLine, initialColumn, tabLength, softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @clipboard, @grammarRegistry, - @project, @assert, @applicationDelegate, grammar, showInvisibles, @autoHeight, @scrollPastEnd + @assert, @applicationDelegate, grammar, showInvisibles, @autoHeight, @scrollPastEnd } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? throw new Error("Must pass a clipboard parameter when constructing TextEditors") unless @clipboard? throw new Error("Must pass a grammarRegistry parameter when constructing TextEditors") unless @grammarRegistry? - throw new Error("Must pass a project parameter when constructing TextEditors") unless @project? throw new Error("Must pass an assert parameter when constructing TextEditors") unless @assert? @firstVisibleScreenRow ?= 0 @@ -516,7 +514,7 @@ class TextEditor extends Model @buffer, displayBuffer, selectionsMarkerLayer, @tabLength, softTabs, suppressCursorCreation: true, @config, @firstVisibleScreenRow, @firstVisibleScreenColumn, - @clipboard, @grammarRegistry, @project, @assert, @applicationDelegate + @clipboard, @grammarRegistry, @assert, @applicationDelegate }) newEditor diff --git a/src/workspace.coffee b/src/workspace.coffee index 9e662c984..29c497b99 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -571,7 +571,7 @@ class Workspace extends Model buildTextEditor: (params) -> params = _.extend({ @config, @clipboard, @grammarRegistry, - @project, @assert, @applicationDelegate + @assert, @applicationDelegate }, params) new TextEditor(params) From fdb439be9c6c99da9c1edeb8d456487a4ccb9a0f Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 17:09:07 -0400 Subject: [PATCH 843/971] Call through to the underlying repo. --- src/git-repository-async.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 37131dd24..26840b822 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -246,7 +246,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to a {String} branch name such as // `refs/remotes/origin/master`. getUpstreamBranch (_path) { - return this.getUpstreamBranch(_path) + return this.repo.getUpstreamBranch(_path) } // Public: Gets all the local and remote references. From 8d26fe133aeaf9682027c36ed0736b75476020f7 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 25 Apr 2016 17:10:39 -0400 Subject: [PATCH 844/971] s/original/origin --- src/git-repository-async.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 26840b822..b691994bc 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -234,7 +234,7 @@ export default class GitRepositoryAsync { // Returns a {Promise} which resolves to the {String} origin url of the // repository. getOriginURL (_path) { - return this.repo.getOriginalURL(_path) + return this.repo.getOriginURL(_path) } // Public: Returns the upstream branch for the current HEAD, or null if there From 891071196f35de2edfd3fd712937a90f0ceb427b Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 25 Apr 2016 15:28:22 -0700 Subject: [PATCH 845/971] Ensure atom.cmd with --wait returns exit code of 0 for git commit usage #11605 --- resources/win/atom.cmd | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/win/atom.cmd b/resources/win/atom.cmd index 8a4fed05c..73c4ddb01 100644 --- a/resources/win/atom.cmd +++ b/resources/win/atom.cmd @@ -27,6 +27,7 @@ IF "%EXPECT_OUTPUT%"=="YES" ( SET ELECTRON_ENABLE_LOGGING=YES IF "%WAIT%"=="YES" ( powershell -noexit "Start-Process -FilePath \"%~dp0\..\..\atom.exe\" -ArgumentList \"--pid=$pid $env:PSARGS\" ; wait-event" + exit 0 ) ELSE ( "%~dp0\..\..\atom.exe" %* ) From e3773f24fc81667d0041e1a80da87cad79e18640 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 26 Apr 2016 10:05:35 +0200 Subject: [PATCH 846/971] :fire: Delete obsolete code --- src/lines-tile-component.coffee | 147 ++------------------------------ 1 file changed, 6 insertions(+), 141 deletions(-) diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index 3cc3accee..c9ecd0982 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -193,7 +193,7 @@ class LinesTileComponent screenRowForNode: (node) -> parseInt(node.dataset.screenRow) buildLineNode: (id) -> - {screenRow, decorationClasses} = @newTileState.lines[id] + {lineText, tagCodes, screenRow, decorationClasses} = @newTileState.lines[id] lineNode = @domElementPool.buildElement("div", "line") lineNode.dataset.screenRow = screenRow @@ -202,57 +202,7 @@ class LinesTileComponent for decorationClass in decorationClasses lineNode.classList.add(decorationClass) - @currentLineTextNodes = [] - # if words.length is 0 - # @setEmptyLineInnerNodes(id, lineNode) - - @setLineInnerNodes(id, lineNode) - @textNodesByLineId[id] = @currentLineTextNodes - - # lineNode.appendChild(@domElementPool.buildElement("span", "fold-marker")) if fold - lineNode - - setEmptyLineInnerNodes: (id, lineNode) -> - {indentGuidesVisible} = @newState - {indentLevel, tabLength, endOfLineInvisibles} = @newTileState.lines[id] - - if indentGuidesVisible and indentLevel > 0 - invisibleIndex = 0 - for i in [0...indentLevel] - indentGuide = @domElementPool.buildElement("span", "indent-guide") - for j in [0...tabLength] - if invisible = endOfLineInvisibles?[invisibleIndex++] - invisibleSpan = @domElementPool.buildElement("span", "invisible-character") - textNode = @domElementPool.buildText(invisible) - invisibleSpan.appendChild(textNode) - indentGuide.appendChild(invisibleSpan) - - @currentLineTextNodes.push(textNode) - else - textNode = @domElementPool.buildText(" ") - indentGuide.appendChild(textNode) - - @currentLineTextNodes.push(textNode) - lineNode.appendChild(indentGuide) - - while invisibleIndex < endOfLineInvisibles?.length - invisible = endOfLineInvisibles[invisibleIndex++] - invisibleSpan = @domElementPool.buildElement("span", "invisible-character") - textNode = @domElementPool.buildText(invisible) - invisibleSpan.appendChild(textNode) - lineNode.appendChild(invisibleSpan) - - @currentLineTextNodes.push(textNode) - else - unless @appendEndOfLineNodes(id, lineNode) - textNode = @domElementPool.buildText("\u00a0") - lineNode.appendChild(textNode) - - @currentLineTextNodes.push(textNode) - - setLineInnerNodes: (id, lineNode) -> - {lineText, tagCodes} = @newTileState.lines[id] - + textNodes = [] lineLength = 0 startIndex = 0 openScopeNode = lineNode @@ -268,100 +218,15 @@ class LinesTileComponent textNode = @domElementPool.buildText(lineText.substr(startIndex, tagCode)) startIndex += tagCode openScopeNode.appendChild(textNode) - @currentLineTextNodes.push(textNode) + textNodes.push(textNode) if startIndex is 0 textNode = @domElementPool.buildText(' ') lineNode.appendChild(textNode) - @currentLineTextNodes.push(textNode) + textNodes.push(textNode) - appendTokenNodes: (tokenText, isHardTab, firstNonWhitespaceIndex, firstTrailingWhitespaceIndex, hasIndentGuide, hasInvisibleCharacters, scopeNode) -> - if isHardTab - textNode = @domElementPool.buildText(tokenText) - hardTabNode = @domElementPool.buildElement("span", "hard-tab") - hardTabNode.classList.add("leading-whitespace") if firstNonWhitespaceIndex? - hardTabNode.classList.add("trailing-whitespace") if firstTrailingWhitespaceIndex? - hardTabNode.classList.add("indent-guide") if hasIndentGuide - hardTabNode.classList.add("invisible-character") if hasInvisibleCharacters - hardTabNode.appendChild(textNode) - - scopeNode.appendChild(hardTabNode) - @currentLineTextNodes.push(textNode) - else - startIndex = 0 - endIndex = tokenText.length - - leadingWhitespaceNode = null - leadingWhitespaceTextNode = null - trailingWhitespaceNode = null - trailingWhitespaceTextNode = null - - if firstNonWhitespaceIndex? - leadingWhitespaceTextNode = - @domElementPool.buildText(tokenText.substring(0, firstNonWhitespaceIndex)) - leadingWhitespaceNode = @domElementPool.buildElement("span", "leading-whitespace") - leadingWhitespaceNode.classList.add("indent-guide") if hasIndentGuide - leadingWhitespaceNode.classList.add("invisible-character") if hasInvisibleCharacters - leadingWhitespaceNode.appendChild(leadingWhitespaceTextNode) - - startIndex = firstNonWhitespaceIndex - - if firstTrailingWhitespaceIndex? - tokenIsOnlyWhitespace = firstTrailingWhitespaceIndex is 0 - - trailingWhitespaceTextNode = - @domElementPool.buildText(tokenText.substring(firstTrailingWhitespaceIndex)) - trailingWhitespaceNode = @domElementPool.buildElement("span", "trailing-whitespace") - trailingWhitespaceNode.classList.add("indent-guide") if hasIndentGuide and not firstNonWhitespaceIndex? and tokenIsOnlyWhitespace - trailingWhitespaceNode.classList.add("invisible-character") if hasInvisibleCharacters - trailingWhitespaceNode.appendChild(trailingWhitespaceTextNode) - - endIndex = firstTrailingWhitespaceIndex - - if leadingWhitespaceNode? - scopeNode.appendChild(leadingWhitespaceNode) - @currentLineTextNodes.push(leadingWhitespaceTextNode) - - if tokenText.length > MaxTokenLength - while startIndex < endIndex - textNode = @domElementPool.buildText( - @sliceText(tokenText, startIndex, startIndex + MaxTokenLength) - ) - textSpan = @domElementPool.buildElement("span") - - textSpan.appendChild(textNode) - scopeNode.appendChild(textSpan) - startIndex += MaxTokenLength - @currentLineTextNodes.push(textNode) - else - textNode = @domElementPool.buildText(@sliceText(tokenText, startIndex, endIndex)) - scopeNode.appendChild(textNode) - @currentLineTextNodes.push(textNode) - - if trailingWhitespaceNode? - scopeNode.appendChild(trailingWhitespaceNode) - @currentLineTextNodes.push(trailingWhitespaceTextNode) - - sliceText: (tokenText, startIndex, endIndex) -> - if startIndex? and endIndex? and startIndex > 0 or endIndex < tokenText.length - tokenText = tokenText.slice(startIndex, endIndex) - tokenText - - appendEndOfLineNodes: (id, lineNode) -> - {endOfLineInvisibles} = @newTileState.lines[id] - - hasInvisibles = false - if endOfLineInvisibles? - for invisible in endOfLineInvisibles - hasInvisibles = true - invisibleSpan = @domElementPool.buildElement("span", "invisible-character") - textNode = @domElementPool.buildText(invisible) - invisibleSpan.appendChild(textNode) - lineNode.appendChild(invisibleSpan) - - @currentLineTextNodes.push(textNode) - - hasInvisibles + @textNodesByLineId[id] = textNodes + lineNode updateLineNode: (id) -> oldLineState = @oldTileState.lines[id] From ebfd821237ab06a7060fe47371ab29724a962c2f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 26 Apr 2016 10:47:42 +0200 Subject: [PATCH 847/971] Destroy folds corresponding to fold-markers and not the whole buffer row --- spec/text-editor-component-spec.js | 62 ++++++++++++++++++++++++------ src/text-editor-component.coffee | 4 +- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 0fe1da5b2..6b4e86540 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -2755,20 +2755,60 @@ describe('TextEditorComponent', function () { }) }) - describe('when a line is folded', function () { - beforeEach(async function () { - editor.foldBufferRow(4) + describe('when a fold marker is clicked', function () { + function clickElementAtPosition (marker, position) { + linesNode.dispatchEvent( + buildMouseEvent('mousedown', clientCoordinatesForScreenPosition(position), {target: marker}) + ) + } + + it('unfolds only the selected fold when other folds are on the same line', async function () { + editor.foldBufferRange([[4, 6], [4, 10]]) + editor.foldBufferRange([[4, 15], [4, 20]]) await nextViewUpdatePromise() + let foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker') + expect(foldMarkers.length).toBe(2) + expect(editor.isFoldedAtBufferRow(4)).toBe(true) + + clickElementAtPosition(foldMarkers[0], [4, 6]) + await nextViewUpdatePromise() + foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker') + expect(foldMarkers.length).toBe(1) + expect(editor.isFoldedAtBufferRow(4)).toBe(true) + + clickElementAtPosition(foldMarkers[0], [4, 15]) + await nextViewUpdatePromise() + foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker') + expect(foldMarkers.length).toBe(0) + expect(editor.isFoldedAtBufferRow(4)).toBe(false) }) - describe('when the folded line\'s fold-marker is clicked', function () { - it('unfolds the buffer row', function () { - let target = component.lineNodeForScreenRow(4).querySelector('.fold-marker') - linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([4, 8]), { - target: target - })) - expect(editor.isFoldedAtBufferRow(4)).toBe(false) - }) + it('unfolds only the selected fold when other folds are inside it', async function () { + editor.foldBufferRange([[4, 10], [4, 15]]) + editor.foldBufferRange([[4, 4], [4, 5]]) + editor.foldBufferRange([[4, 4], [4, 20]]) + await nextViewUpdatePromise() + let foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker') + expect(foldMarkers.length).toBe(1) + expect(editor.isFoldedAtBufferRow(4)).toBe(true) + + clickElementAtPosition(foldMarkers[0], [4, 4]) + await nextViewUpdatePromise() + foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker') + expect(foldMarkers.length).toBe(1) + expect(editor.isFoldedAtBufferRow(4)).toBe(true) + + clickElementAtPosition(foldMarkers[0], [4, 4]) + await nextViewUpdatePromise() + foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker') + expect(foldMarkers.length).toBe(1) + expect(editor.isFoldedAtBufferRow(4)).toBe(true) + + clickElementAtPosition(foldMarkers[0], [4, 10]) + await nextViewUpdatePromise() + foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker') + expect(foldMarkers.length).toBe(0) + expect(editor.isFoldedAtBufferRow(4)).toBe(false) }) }) diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 52a43db54..3f9bb2029 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -560,8 +560,8 @@ class TextEditorComponent screenPosition = @screenPositionForMouseEvent(event) if event.target?.classList.contains('fold-marker') - bufferRow = @editor.bufferRowForScreenRow(screenPosition.row) - @editor.unfoldBufferRow(bufferRow) + bufferPosition = @editor.bufferPositionForScreenPosition(screenPosition) + @editor.destroyFoldsIntersectingBufferRange([bufferPosition, bufferPosition]) return switch detail From f85c72978e445951b0986b7deebc18e4a791ef0f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 26 Apr 2016 12:06:41 +0200 Subject: [PATCH 848/971] Fix wrong implementation in TextEditor.prototype.screenRowForBufferRow --- src/text-editor.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 710a9e342..ce53cc3ed 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -877,9 +877,9 @@ class TextEditor extends Model screenRowForBufferRow: (row) -> if @largeFileMode - bufferRow + row else - @displayLayer.translateScreenPosition(Point(screenRow, 0)).row + @displayLayer.translateBufferPosition(Point(row, 0)).row getRightmostScreenPosition: -> @displayLayer.getRightmostScreenPosition() From 4f6687324e394e7f3f067206ad9ce338c842c551 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 26 Apr 2016 12:09:38 +0200 Subject: [PATCH 849/971] Include bufferRange in `decorationsStateForScreenRowRange` --- spec/text-editor-spec.coffee | 11 +++++++++++ src/decoration-manager.coffee | 5 +++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 466d1b501..565f92430 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5888,6 +5888,7 @@ describe "TextEditor", -> expect(editor.decorationsStateForScreenRowRange(0, 5)[decoration.id]).toEqual { properties: {type: 'highlight', class: 'foo'} screenRange: marker.getScreenRange(), + bufferRange: marker.getBufferRange(), rangeIsReversed: false } @@ -5908,26 +5909,31 @@ describe "TextEditor", -> expect(decorationState["#{layer1Decoration1.id}-#{marker1.id}"]).toEqual { properties: {type: 'highlight', class: 'foo'}, screenRange: marker1.getRange(), + bufferRange: marker1.getRange(), rangeIsReversed: false } expect(decorationState["#{layer1Decoration1.id}-#{marker2.id}"]).toEqual { properties: {type: 'highlight', class: 'foo'}, screenRange: marker2.getRange(), + bufferRange: marker2.getRange(), rangeIsReversed: false } expect(decorationState["#{layer1Decoration2.id}-#{marker1.id}"]).toEqual { properties: {type: 'highlight', class: 'bar'}, screenRange: marker1.getRange(), + bufferRange: marker1.getRange(), rangeIsReversed: false } expect(decorationState["#{layer1Decoration2.id}-#{marker2.id}"]).toEqual { properties: {type: 'highlight', class: 'bar'}, screenRange: marker2.getRange(), + bufferRange: marker2.getRange(), rangeIsReversed: false } expect(decorationState["#{layer2Decoration.id}-#{marker3.id}"]).toEqual { properties: {type: 'highlight', class: 'baz'}, screenRange: marker3.getRange(), + bufferRange: marker3.getRange(), rangeIsReversed: false } @@ -5939,16 +5945,19 @@ describe "TextEditor", -> expect(decorationState["#{layer1Decoration2.id}-#{marker1.id}"]).toEqual { properties: {type: 'highlight', class: 'bar'}, screenRange: marker1.getRange(), + bufferRange: marker1.getRange(), rangeIsReversed: false } expect(decorationState["#{layer1Decoration2.id}-#{marker2.id}"]).toEqual { properties: {type: 'highlight', class: 'bar'}, screenRange: marker2.getRange(), + bufferRange: marker2.getRange(), rangeIsReversed: false } expect(decorationState["#{layer2Decoration.id}-#{marker3.id}"]).toEqual { properties: {type: 'highlight', class: 'baz'}, screenRange: marker3.getRange(), + bufferRange: marker3.getRange(), rangeIsReversed: false } @@ -5957,6 +5966,7 @@ describe "TextEditor", -> expect(decorationState["#{layer1Decoration2.id}-#{marker1.id}"]).toEqual { properties: {type: 'highlight', class: 'quux'}, screenRange: marker1.getRange(), + bufferRange: marker1.getRange(), rangeIsReversed: false } @@ -5965,6 +5975,7 @@ describe "TextEditor", -> expect(decorationState["#{layer1Decoration2.id}-#{marker1.id}"]).toEqual { properties: {type: 'highlight', class: 'bar'}, screenRange: marker1.getRange(), + bufferRange: marker1.getRange(), rangeIsReversed: false } diff --git a/src/decoration-manager.coffee b/src/decoration-manager.coffee index e96727fe3..edb9dfb33 100644 --- a/src/decoration-manager.coffee +++ b/src/decoration-manager.coffee @@ -84,20 +84,21 @@ class DecorationManager extends Model for marker in layer.findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow]) when marker.isValid() screenRange = marker.getScreenRange() + bufferRange = marker.getBufferRange() rangeIsReversed = marker.isReversed() if decorations = @decorationsByMarkerId[marker.id] for decoration in decorations decorationsState[decoration.id] = { properties: decoration.properties - screenRange, rangeIsReversed + screenRange, bufferRange, rangeIsReversed } if layerDecorations = @layerDecorationsByMarkerLayerId[layerId] for layerDecoration in layerDecorations decorationsState["#{layerDecoration.id}-#{marker.id}"] = { properties: layerDecoration.overridePropertiesByMarkerId[marker.id] ? layerDecoration.properties - screenRange, rangeIsReversed + screenRange, bufferRange, rangeIsReversed } decorationsState From f81f54e08a1543762101fce5a60ffb38eba16956 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 26 Apr 2016 12:11:05 +0200 Subject: [PATCH 850/971] Apply 'folded' decoration only to 1st screen row of a wrapped buffer row --- spec/text-editor-component-spec.js | 10 +++++++- spec/text-editor-presenter-spec.coffee | 10 ++++++++ src/text-editor-presenter.coffee | 35 +++++++++++++++----------- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 6b4e86540..4d2a68cac 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -1053,7 +1053,7 @@ describe('TextEditorComponent', function () { beforeEach(async function () { editor.setSoftWrapped(true) await nextViewUpdatePromise() - componentNode.style.width = 16 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px' + componentNode.style.width = 20 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px' component.measureDimensions() await nextViewUpdatePromise() }) @@ -1062,6 +1062,14 @@ describe('TextEditorComponent', function () { expect(lineNumberHasClass(0, 'foldable')).toBe(true) expect(lineNumberHasClass(1, 'foldable')).toBe(false) }) + + it('does not add the folded class for soft-wrapped lines that contain a fold', async function () { + editor.foldBufferRange([[3, 19], [3, 21]]) + await nextViewUpdatePromise() + + expect(lineNumberHasClass(11, 'folded')).toBe(true) + expect(lineNumberHasClass(12, 'folded')).toBe(false) + }) }) }) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index a7beb3c57..9318809d7 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -3116,6 +3116,16 @@ describe "TextEditorPresenter", -> expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'a' expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toContain 'a' + it "applies the 'folded' decoration only to the initial screen row of a soft-wrapped buffer row", -> + editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) + editor.setEditorWidthInChars(15) + editor.foldBufferRange([[0, 20], [0, 22]]) + presenter = buildPresenter(explicitHeight: 35, scrollTop: 0, tileSize: 2) + + expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'folded' + expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull() + describe ".foldable", -> it "marks line numbers at the start of a foldable region as foldable", -> presenter = buildPresenter() diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 9b66c6312..85c85c655 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1127,9 +1127,9 @@ class TextEditorPresenter @customGutterDecorationsByGutterName = {} for decorationId, decorationState of @decorations - {properties, screenRange, rangeIsReversed} = decorationState + {properties, bufferRange, screenRange, rangeIsReversed} = decorationState if Decoration.isType(properties, 'line') or Decoration.isType(properties, 'line-number') - @addToLineDecorationCaches(decorationId, properties, screenRange, rangeIsReversed) + @addToLineDecorationCaches(decorationId, properties, bufferRange, screenRange, rangeIsReversed) else if Decoration.isType(properties, 'gutter') and properties.gutterName? @customGutterDecorationsByGutterName[properties.gutterName] ?= {} @@ -1150,7 +1150,7 @@ class TextEditorPresenter return - addToLineDecorationCaches: (decorationId, properties, screenRange, rangeIsReversed) -> + addToLineDecorationCaches: (decorationId, properties, bufferRange, screenRange, rangeIsReversed) -> if screenRange.isEmpty() return if properties.onlyNonEmpty else @@ -1158,21 +1158,28 @@ class TextEditorPresenter omitLastRow = screenRange.end.column is 0 if rangeIsReversed - headPosition = screenRange.start + headScreenPosition = screenRange.start + headBufferPosition = bufferRange.start else - headPosition = screenRange.end + headScreenPosition = screenRange.end + headBufferPosition = bufferRange.end - for row in [screenRange.start.row..screenRange.end.row] by 1 - continue if properties.onlyHead and row isnt headPosition.row - continue if omitLastRow and row is screenRange.end.row + if properties.class is 'folded' and Decoration.isType(properties, 'line-number') + screenRow = @model.screenRowForBufferRow(headBufferPosition.row) + @lineNumberDecorationsByScreenRow[screenRow] ?= {} + @lineNumberDecorationsByScreenRow[screenRow][decorationId] = properties + else + for row in [screenRange.start.row..screenRange.end.row] by 1 + continue if properties.onlyHead and row isnt headScreenPosition.row + continue if omitLastRow and row is screenRange.end.row - if Decoration.isType(properties, 'line') - @lineDecorationsByScreenRow[row] ?= {} - @lineDecorationsByScreenRow[row][decorationId] = properties + if Decoration.isType(properties, 'line') + @lineDecorationsByScreenRow[row] ?= {} + @lineDecorationsByScreenRow[row][decorationId] = properties - if Decoration.isType(properties, 'line-number') - @lineNumberDecorationsByScreenRow[row] ?= {} - @lineNumberDecorationsByScreenRow[row][decorationId] = properties + if Decoration.isType(properties, 'line-number') + @lineNumberDecorationsByScreenRow[row] ?= {} + @lineNumberDecorationsByScreenRow[row][decorationId] = properties return From 38bd120fb1595b253290cf7bb195a2c0bf08a334 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 26 Apr 2016 12:11:13 +0200 Subject: [PATCH 851/971] :art: --- spec/text-editor-component-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 4d2a68cac..9b1922494 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -1092,7 +1092,7 @@ describe('TextEditorComponent', function () { component.destroy() lineNumber = component.lineNumberNodeForScreenRow(1) target = lineNumber.querySelector('.icon-right') - return target.dispatchEvent(buildClickEvent(target)) + target.dispatchEvent(buildClickEvent(target)) }) }) From 7ba9cc6329e649a267e7153a8ca6260d83e8af6f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 26 Apr 2016 13:00:23 +0200 Subject: [PATCH 852/971] Unfold all the folds intersecting the row when clicking fold indicators --- spec/text-editor-component-spec.js | 31 +++++++++++++++++++++++++ src/line-number-gutter-component.coffee | 4 ++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 9b1922494..1b29cbd7b 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -1116,6 +1116,37 @@ describe('TextEditorComponent', function () { expect(lineNumberHasClass(1, 'folded')).toBe(false) }) + it('unfolds all the free-form folds intersecting the buffer row when clicked', async function () { + expect(lineNumberHasClass(3, 'foldable')).toBe(false) + + editor.foldBufferRange([[3, 4], [5, 4]]) + editor.foldBufferRange([[5, 5], [8, 10]]) + await nextViewUpdatePromise() + expect(lineNumberHasClass(3, 'folded')).toBe(true) + expect(lineNumberHasClass(5, 'folded')).toBe(false) + + let lineNumber = component.lineNumberNodeForScreenRow(3) + let target = lineNumber.querySelector('.icon-right') + target.dispatchEvent(buildClickEvent(target)) + await nextViewUpdatePromise() + expect(lineNumberHasClass(3, 'folded')).toBe(false) + expect(lineNumberHasClass(5, 'folded')).toBe(true) + + editor.setSoftWrapped(true) + componentNode.style.width = 20 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px' + component.measureDimensions() + await nextViewUpdatePromise() + editor.foldBufferRange([[3, 19], [3, 21]]) // fold starting on a soft-wrapped portion of the line + await nextViewUpdatePromise() + expect(lineNumberHasClass(11, 'folded')).toBe(true) + + lineNumber = component.lineNumberNodeForScreenRow(11) + target = lineNumber.querySelector('.icon-right') + target.dispatchEvent(buildClickEvent(target)) + await nextViewUpdatePromise() + expect(lineNumberHasClass(11, 'folded')).toBe(false) + }) + it('does not fold when the line number componentNode is clicked', function () { let lineNumber = component.lineNumberNodeForScreenRow(1) lineNumber.dispatchEvent(buildClickEvent(lineNumber)) diff --git a/src/line-number-gutter-component.coffee b/src/line-number-gutter-component.coffee index bb66ff144..3a3c199c2 100644 --- a/src/line-number-gutter-component.coffee +++ b/src/line-number-gutter-component.coffee @@ -93,9 +93,9 @@ class LineNumberGutterComponent extends TiledComponent {target} = event lineNumber = target.parentNode - if target.classList.contains('icon-right') and lineNumber.classList.contains('foldable') + if target.classList.contains('icon-right') bufferRow = parseInt(lineNumber.getAttribute('data-buffer-row')) if lineNumber.classList.contains('folded') @editor.unfoldBufferRow(bufferRow) - else + else if lineNumber.classList.contains('foldable') @editor.foldBufferRow(bufferRow) From f589bdd8d9cd5d7b0d05c5ab5c4ccc55482a44d4 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 09:22:38 -0400 Subject: [PATCH 853/971] Add a remote to the fixture. --- spec/fixtures/git/repo-with-submodules/git.git/config | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/fixtures/git/repo-with-submodules/git.git/config b/spec/fixtures/git/repo-with-submodules/git.git/config index ab57cc5f1..209e37653 100644 --- a/spec/fixtures/git/repo-with-submodules/git.git/config +++ b/spec/fixtures/git/repo-with-submodules/git.git/config @@ -5,6 +5,9 @@ logallrefupdates = true ignorecase = true precomposeunicode = true +[remote "origin"] + url = git@github.com:atom/some-repo-i-guess.git + fetch = +refs/heads/*:refs/remotes/origin/* [submodule "jstips"] url = https://github.com/loverajoel/jstips [submodule "You-Dont-Need-jQuery"] From f86a15e5fca34cee1351dbeeb5108719b6915282 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 09:22:56 -0400 Subject: [PATCH 854/971] Spec for getOriginURL --- spec/git-repository-async-spec.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index d36b9fd58..77ff1a4b5 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -877,4 +877,16 @@ describe('GitRepositoryAsync', () => { }) }) }) + + describe('.getOriginURL()', () => { + beforeEach(() => { + const workingDirectory = copyRepository('repo-with-submodules') + repo = GitRepositoryAsync.open(workingDirectory) + }) + + it('returns the origin URL', async () => { + const URL = await repo.getOriginURL() + expect(URL).toBe('git@github.com:atom/some-repo-i-guess.git') + }) + }) }) From ff43d917be636f486de059e07a02bd636180fb5b Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 09:23:54 -0400 Subject: [PATCH 855/971] Lower case --- spec/git-repository-async-spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 77ff1a4b5..75f6848aa 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -885,8 +885,8 @@ describe('GitRepositoryAsync', () => { }) it('returns the origin URL', async () => { - const URL = await repo.getOriginURL() - expect(URL).toBe('git@github.com:atom/some-repo-i-guess.git') + const url = await repo.getOriginURL() + expect(url).toBe('git@github.com:atom/some-repo-i-guess.git') }) }) }) From 33a9240fe18ef01b5e223d9029da87e2f563c57f Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 09:45:56 -0400 Subject: [PATCH 856/971] :arrow_up: ohnogit@0.0.11 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 957d262f0..89a042915 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "marked": "^0.3.4", "normalize-package-data": "^2.0.0", "nslog": "^3", - "ohnogit": "0.0.9", + "ohnogit": "0.0.11", "oniguruma": "^5", "pathwatcher": "~6.2", "property-accessors": "^1.1.3", From e16e987e08ba30f5853c8ab9428a061663304862 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 09:46:04 -0400 Subject: [PATCH 857/971] Give the fixture a remote --- spec/fixtures/git/repo-with-submodules/git.git/config | 3 +++ .../repo-with-submodules/git.git/refs/remotes/origin/master | 1 + 2 files changed, 4 insertions(+) create mode 100644 spec/fixtures/git/repo-with-submodules/git.git/refs/remotes/origin/master diff --git a/spec/fixtures/git/repo-with-submodules/git.git/config b/spec/fixtures/git/repo-with-submodules/git.git/config index 209e37653..ff94b83d6 100644 --- a/spec/fixtures/git/repo-with-submodules/git.git/config +++ b/spec/fixtures/git/repo-with-submodules/git.git/config @@ -5,6 +5,9 @@ logallrefupdates = true ignorecase = true precomposeunicode = true +[branch "master"] + remote = origin + merge = refs/heads/master [remote "origin"] url = git@github.com:atom/some-repo-i-guess.git fetch = +refs/heads/*:refs/remotes/origin/* diff --git a/spec/fixtures/git/repo-with-submodules/git.git/refs/remotes/origin/master b/spec/fixtures/git/repo-with-submodules/git.git/refs/remotes/origin/master new file mode 100644 index 000000000..3507a23dc --- /dev/null +++ b/spec/fixtures/git/repo-with-submodules/git.git/refs/remotes/origin/master @@ -0,0 +1 @@ +d2b0ad9cbc6f6c4372e8956e5cc5af771b2342e5 From 67c7c60dcc874c22e86282a87bc302f1de06e7f0 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 09:49:03 -0400 Subject: [PATCH 858/971] Test getUpstreamBranch --- spec/git-repository-async-spec.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 75f6848aa..1fbe537d5 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -889,4 +889,22 @@ describe('GitRepositoryAsync', () => { expect(url).toBe('git@github.com:atom/some-repo-i-guess.git') }) }) + + describe('.getUpstreamBranch()', () => { + it('returns null when there is no upstream branch', async () => { + const workingDirectory = copyRepository() + repo = GitRepositoryAsync.open(workingDirectory) + + const upstream = await repo.getUpstreamBranch() + expect(upstream).toBe(null) + }) + + it('returns the upstream branch', async () => { + const workingDirectory = copyRepository('repo-with-submodules') + repo = GitRepositoryAsync.open(workingDirectory) + + const upstream = await repo.getUpstreamBranch() + expect(upstream).toBe('refs/remotes/origin/master') + }) + }) }) From 0541755ac8cf5f16792e081213234b5e868fbb84 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 09:49:38 -0400 Subject: [PATCH 859/971] Consistent indentation. --- spec/fixtures/git/repo-with-submodules/git.git/config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/fixtures/git/repo-with-submodules/git.git/config b/spec/fixtures/git/repo-with-submodules/git.git/config index ff94b83d6..323ba7d9b 100644 --- a/spec/fixtures/git/repo-with-submodules/git.git/config +++ b/spec/fixtures/git/repo-with-submodules/git.git/config @@ -9,8 +9,8 @@ remote = origin merge = refs/heads/master [remote "origin"] - url = git@github.com:atom/some-repo-i-guess.git - fetch = +refs/heads/*:refs/remotes/origin/* + url = git@github.com:atom/some-repo-i-guess.git + fetch = +refs/heads/*:refs/remotes/origin/* [submodule "jstips"] url = https://github.com/loverajoel/jstips [submodule "You-Dont-Need-jQuery"] From 5d7c2fc8bae0b48cf35581ec613f42ff81a72481 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 10:32:41 -0400 Subject: [PATCH 860/971] Just observe the grammar. --- src/display-buffer.coffee | 3 --- src/text-editor.coffee | 3 --- src/tokenized-buffer.coffee | 8 -------- src/workspace.coffee | 2 +- 4 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index ccc68535f..109b791a1 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -136,9 +136,6 @@ class DisplayBuffer extends Model onDidChangeGrammar: (callback) -> @tokenizedBuffer.onDidChangeGrammar(callback) - onDidUseGrammar: (callback) -> - @tokenizedBuffer.onDidUseGrammar(callback) - onDidTokenize: (callback) -> @tokenizedBuffer.onDidTokenize(callback) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 865026e20..aeb5ebe5c 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -323,9 +323,6 @@ class TextEditor extends Model onDidChangeGrammar: (callback) -> @emitter.on 'did-change-grammar', callback - onDidUseGrammar: (callback) -> - @displayBuffer.onDidUseGrammar(callback) - # Extended: Calls your `callback` when the result of {::isModified} changes. # # * `callback` {Function} diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 7e3c4fe49..065715806 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -128,14 +128,6 @@ class TokenizedBuffer extends Model @emitter.emit 'did-change-grammar', grammar - # Delay this to the next tick to ensure whoever created the buffer has the - # chance to listen for this event before we send it. - process.nextTick => - @emitter.emit 'did-use-grammar', grammar - - onDidUseGrammar: (callback) -> - @emitter.on 'did-use-grammar', callback - getGrammarSelectionContent: -> @buffer.getTextInRange([[0, 0], [10, 0]]) diff --git a/src/workspace.coffee b/src/workspace.coffee index 9a52dc937..7e67de97d 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -550,7 +550,7 @@ class Workspace extends Model @project.bufferForPath(filePath, options).then (buffer) => editor = @buildTextEditor(_.extend({buffer, largeFileMode}, options)) disposable = atom.textEditors.add(editor) - grammarSubscription = editor.onDidUseGrammar(@handleGrammarUsed.bind(this)) + grammarSubscription = editor.observeGrammar(@handleGrammarUsed.bind(this)) editor.onDidDestroy -> grammarSubscription.dispose() disposable.dispose() From 97328749995349305a6f25725584f2afd6d167d8 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 11:02:01 -0400 Subject: [PATCH 861/971] Don't log anymore. --- src/project.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/project.coffee b/src/project.coffee index 70d5b93a2..bf64753cf 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -392,7 +392,6 @@ class Project extends Model subscribeToBuffer: (buffer) -> buffer.onDidDestroy => @removeBuffer(buffer) buffer.onDidChangePath => - console.log('did change path! ' + buffer.getPath()) unless @getPaths().length > 0 @setPaths([path.dirname(buffer.getPath())]) buffer.onWillThrowWatchError ({error, handle}) => From e1c17ed8563b6ce0c20a65634c5a3f14d7539f3a Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 11:17:20 -0400 Subject: [PATCH 862/971] :arrow_up: status-bar@1.2.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 89a042915..627a411e1 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.235.1", "snippets": "1.0.2", "spell-check": "0.67.1", - "status-bar": "1.2.4", + "status-bar": "1.2.5", "styleguide": "0.45.2", "symbols-view": "0.112.0", "tabs": "0.93.1", From 53b7a20ad7e45960365b01d31f309be5f178b8cd Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 11:40:56 -0400 Subject: [PATCH 863/971] :arrow_up: status-bar@1.2.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 627a411e1..2874eaeb0 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.235.1", "snippets": "1.0.2", "spell-check": "0.67.1", - "status-bar": "1.2.5", + "status-bar": "1.2.6", "styleguide": "0.45.2", "symbols-view": "0.112.0", "tabs": "0.93.1", From 40d77613502f01b11c2ba36115acdab6aeb29080 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 11:48:52 -0400 Subject: [PATCH 864/971] Bail if we don't have a grammar yet. --- src/workspace.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/workspace.coffee b/src/workspace.coffee index 7e67de97d..c8be01fef 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -557,6 +557,8 @@ class Workspace extends Model editor handleGrammarUsed: (grammar) -> + return unless grammar? + @packageManager.triggerActivationHook("#{grammar.packageName}:grammar-used") # Public: Returns a {Boolean} that is `true` if `object` is a `TextEditor`. From 5fd556ac58f8e2406f6943bec25628b5d0f67169 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 14:55:37 -0400 Subject: [PATCH 865/971] We need Directory now. --- src/workspace.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/workspace.coffee b/src/workspace.coffee index 1458ac9cd..1cc79f1ce 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -4,6 +4,7 @@ path = require 'path' {join} = path {Emitter, Disposable, CompositeDisposable} = require 'event-kit' fs = require 'fs-plus' +{Directory} = require 'pathwatcher' DefaultDirectorySearcher = require './default-directory-searcher' Model = require './model' TextEditor = require './text-editor' From a7606710c012e23d342210d66a25c4741eef1bee Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 14:55:43 -0400 Subject: [PATCH 866/971] Don't need Directory anymore. --- src/text-editor.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index a2ecb3663..a61afea17 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -9,7 +9,6 @@ Cursor = require './cursor' Model = require './model' Selection = require './selection' TextMateScopeSelector = require('first-mate').ScopeSelector -{Directory} = require "pathwatcher" GutterContainer = require './gutter-container' TextEditorElement = require './text-editor-element' From aa9e8ab620d55de235b1b7bb097b70a64903bca5 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 14:56:00 -0400 Subject: [PATCH 867/971] Update the spec. --- spec/text-editor-spec.coffee | 22 ---------------------- spec/workspace-spec.coffee | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index bc08b3f0e..e5e58a5cc 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5727,28 +5727,6 @@ describe "TextEditor", -> expect(handler).toHaveBeenCalledWith 'OK' expect(editor.getPlaceholderText()).toBe 'OK' - describe ".checkoutHeadRevision()", -> - it "reverts to the version of its file checked into the project repository", -> - atom.config.set("editor.confirmCheckoutHeadRevision", false) - - editor.setCursorBufferPosition([0, 0]) - editor.insertText("---\n") - expect(editor.lineTextForBufferRow(0)).toBe "---" - - waitsForPromise -> - editor.checkoutHeadRevision() - - runs -> - expect(editor.lineTextForBufferRow(0)).toBe "var quicksort = function () {" - - describe "when there's no repository for the editor's file", -> - it "doesn't do anything", -> - editor = atom.workspace.buildTextEditor() - editor.setText("stuff") - editor.checkoutHeadRevision() - - waitsForPromise -> editor.checkoutHeadRevision() - describe 'gutters', -> describe 'the TextEditor constructor', -> it 'creates a line-number gutter', -> diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index b84e873da..6fa8001aa 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -1633,5 +1633,31 @@ describe "Workspace", -> runs -> expect(grammarUsed.argsForCall[0][0].name).toBe 'JavaScript' + describe ".checkoutHeadRevision()", -> + editor = null + beforeEach -> + atom.config.set("editor.confirmCheckoutHeadRevision", false) + + waitsForPromise -> atom.workspace.open('sample-with-comments.js').then (o) -> editor = o + + it "reverts to the version of its file checked into the project repository", -> + editor.setCursorBufferPosition([0, 0]) + editor.insertText("---\n") + expect(editor.lineTextForBufferRow(0)).toBe "---" + + waitsForPromise -> + atom.workspace.checkoutHeadRevision(editor) + + runs -> + expect(editor.lineTextForBufferRow(0)).toBe "" + + describe "when there's no repository for the editor's file", -> + it "doesn't do anything", -> + editor = atom.workspace.buildTextEditor() + editor.setText("stuff") + atom.workspace.checkoutHeadRevision(editor) + + waitsForPromise -> atom.workspace.checkoutHeadRevision(editor) + escapeStringRegex = (str) -> str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') From e038ff326085317cc55e07782cd2be3f2da313eb Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 16:04:41 -0400 Subject: [PATCH 868/971] Remove applicationDelegate from TextEditor --- src/text-editor.coffee | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index a61afea17..4c5b8a956 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -81,7 +81,6 @@ class TextEditor extends Model state.clipboard = atomEnvironment.clipboard state.grammarRegistry = atomEnvironment.grammars state.assert = atomEnvironment.assert.bind(atomEnvironment) - state.applicationDelegate = atomEnvironment.applicationDelegate editor = new this(state) if state.registered disposable = atomEnvironment.textEditors.add(editor) @@ -95,7 +94,7 @@ class TextEditor extends Model @softTabs, @firstVisibleScreenRow, @firstVisibleScreenColumn, initialLine, initialColumn, tabLength, softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @clipboard, @grammarRegistry, - @assert, @applicationDelegate, grammar, showInvisibles, @autoHeight, @scrollPastEnd + @assert, grammar, showInvisibles, @autoHeight, @scrollPastEnd } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -510,7 +509,7 @@ class TextEditor extends Model @buffer, displayBuffer, selectionsMarkerLayer, @tabLength, softTabs, suppressCursorCreation: true, @config, @firstVisibleScreenRow, @firstVisibleScreenColumn, - @clipboard, @grammarRegistry, @assert, @applicationDelegate + @clipboard, @grammarRegistry, @assert }) newEditor From 46c97ee2b2fbfeb1210118087dcd754cc8f793b4 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 16:14:25 -0400 Subject: [PATCH 869/971] Provide a default assert implementation if one isn't passed in. --- src/text-editor.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index a61afea17..6ceedf943 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -101,8 +101,8 @@ class TextEditor extends Model throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? throw new Error("Must pass a clipboard parameter when constructing TextEditors") unless @clipboard? throw new Error("Must pass a grammarRegistry parameter when constructing TextEditors") unless @grammarRegistry? - throw new Error("Must pass an assert parameter when constructing TextEditors") unless @assert? + @assert ?= @defaultAssert.bind(this) @firstVisibleScreenRow ?= 0 @firstVisibleScreenColumn ?= 0 @emitter = new Emitter @@ -3365,6 +3365,9 @@ class TextEditor extends Model @emitter.emit 'will-insert-text', willInsertEvent result + defaultAssert: (condition, message, callback) -> + condition + ### Section: Language Mode Delegated Methods ### From c3bfba0e53f06d2e6b68d8df07d03b9a614babe9 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 26 Apr 2016 16:15:30 -0400 Subject: [PATCH 870/971] We don't need to pass in an app delegate here anymore. --- src/workspace.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index 1cc79f1ce..f75f00bc6 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -573,8 +573,7 @@ class Workspace extends Model # Returns a {TextEditor}. buildTextEditor: (params) -> params = _.extend({ - @config, @clipboard, @grammarRegistry, - @assert, @applicationDelegate + @config, @clipboard, @grammarRegistry, @assert }, params) new TextEditor(params) From 6bb5e09af92f18ad101bcbee42b50f68d9da741f Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 26 Apr 2016 16:46:00 -0400 Subject: [PATCH 871/971] :arrow_up: language-shellscript@0.22.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2874eaeb0..51d1f06e8 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "language-ruby": "0.68.5", "language-ruby-on-rails": "0.25.0", "language-sass": "0.47.0", - "language-shellscript": "0.21.1", + "language-shellscript": "0.22.0", "language-source": "0.9.0", "language-sql": "0.21.0", "language-text": "0.7.1", From f97d197a60e59ed5e0e42eef5a913a1620e49286 Mon Sep 17 00:00:00 2001 From: Rahat Ahmed Date: Tue, 26 Apr 2016 14:22:47 -0500 Subject: [PATCH 872/971] =?UTF-8?q?=F0=9F=8F=87=20Improve=20perf=20for=20c?= =?UTF-8?q?licking=20on=20long=20buffer=20rows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reimplements the `screenPositionforPixelPosition` method in `lines-yardstick.coffee` to use two binary searches to find the position in order to minimize the number of `getBoundingClientRect` calls. The first stage performs a binary search on the text nodes of a row to find the node containing the pixelPosition. The second stage performs a binary search within the text node to find the character that contains the pixelPosition. This improves responsiveness when clicking near the end of a very long line of text. Resolves #10769 --- package.json | 1 + src/lines-yardstick.coffee | 59 ++++++++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index ffe6d30e0..31b8bb722 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "async": "0.2.6", "atom-keymap": "6.3.2", "babel-core": "^5.8.21", + "binary-search-with-index": "^1.3.0", "bootstrap": "^3.3.4", "cached-run-in-this-context": "0.4.1", "clear-cut": "^2.0.1", diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee index 17af35f74..f865a039c 100644 --- a/src/lines-yardstick.coffee +++ b/src/lines-yardstick.coffee @@ -1,5 +1,6 @@ {Point} = require 'text-buffer' {isPairedCharacter} = require './text-utils' +binarySearch = require 'binary-search-with-index' module.exports = class LinesYardstick @@ -19,8 +20,8 @@ class LinesYardstick targetTop = pixelPosition.top targetLeft = pixelPosition.left defaultCharWidth = @model.getDefaultCharWidth() + return Point(0, 0) if targetTop < 0 row = @lineTopIndex.rowForPixelPosition(targetTop) - targetLeft = 0 if targetTop < 0 targetLeft = Infinity if row > @model.getLastScreenRow() row = Math.min(row, @model.getLastScreenRow()) row = Math.max(0, row) @@ -32,32 +33,27 @@ class LinesYardstick lineOffset = lineNode.getBoundingClientRect().left targetLeft += lineOffset - textNodeStartColumn = 0 - for textNode in textNodes - {length: textNodeLength, textContent: textNodeContent} = textNode - textNodeRight = @clientRectForRange(textNode, 0, textNodeLength).right + textNodeComparator = (textNode, position) => + {length: textNodeLength} = textNode + rangeRect = @clientRectForRange(textNode, 0, textNodeLength) + return -1 if rangeRect.right < position + return 1 if rangeRect.left > position + return 0 - if textNodeRight > targetLeft - characterIndex = 0 - while characterIndex < textNodeLength - if isPairedCharacter(textNodeContent, characterIndex) - nextCharacterIndex = characterIndex + 2 - else - nextCharacterIndex = characterIndex + 1 + textNodeIndex = binarySearch(textNodes, targetLeft, textNodeComparator) - rangeRect = @clientRectForRange(textNode, characterIndex, nextCharacterIndex) + if textNodeIndex >= 0 + textNodeStartColumn = textNodes + .slice(0, textNodeIndex) + .reduce(((totalLength, node) -> totalLength + node.length), 0) + charIndex = @charIndexForScreenPosition(textNodes[textNodeIndex], targetLeft) - if rangeRect.right > targetLeft - if targetLeft <= ((rangeRect.left + rangeRect.right) / 2) - return Point(row, textNodeStartColumn + characterIndex) - else - return Point(row, textNodeStartColumn + nextCharacterIndex) - else - characterIndex = nextCharacterIndex + return Point(row, textNodeStartColumn + charIndex) - textNodeStartColumn += textNodeLength + textNodeStartColumn = textNodes + .reduce(((totalLength, node) -> totalLength + node.length), 0) - Point(row, textNodeStartColumn) + return Point(row, textNodeStartColumn) pixelPositionForScreenPosition: (screenPosition) -> targetRow = screenPosition.row @@ -107,3 +103,22 @@ class LinesYardstick @rangeForMeasurement.setStart(textNode, startIndex) @rangeForMeasurement.setEnd(textNode, endIndex) @rangeForMeasurement.getClientRects()[0] ? @rangeForMeasurement.getBoundingClientRect() + + charIndexForScreenPosition: (textNode, targetLeft) -> + {textContent: textNodeContent} = textNode + rangeRect = null + nextCharIndex = -1 + characterComparator = (char, position, charIndex) => + if isPairedCharacter(textNodeContent, charIndex) + nextCharIndex = charIndex + 2 + else + nextCharIndex = charIndex + 1 + rangeRect = @clientRectForRange(textNode, charIndex, nextCharIndex) + return -1 if rangeRect.right < position + return 1 if rangeRect.left > position + return 0 + + characterIndex = binarySearch(textNodeContent, targetLeft, characterComparator) + if targetLeft <= ((rangeRect.left + rangeRect.right) / 2) + return characterIndex + return nextCharIndex From 3b204d404f4cb6216bedc0afa9c4035f8245e4a8 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Tue, 26 Apr 2016 15:46:47 -0700 Subject: [PATCH 873/971] :arrow_up: apm@1.9.3 --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index 6623876f9..4ddb4dabe 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "1.9.2" + "atom-package-manager": "1.9.3" } } From e0688bef1ab96e75baa417c52dcaee01590c49c0 Mon Sep 17 00:00:00 2001 From: David Elliott Date: Tue, 26 Apr 2016 16:15:41 -0700 Subject: [PATCH 874/971] Ctrl-o opens last directory where you opened a file --- src/browser/atom-application.coffee | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 69eff1845..38c2a354a 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -58,6 +58,7 @@ class AtomApplication resourcePath: null version: null quitting: false + lastFilePathOpened: null exit: (status) -> app.exit(status) @@ -655,6 +656,7 @@ class AtomApplication # :window - An {AtomWindow} to use for opening a selected file path. promptForPathToOpen: (type, {devMode, safeMode, window}) -> @promptForPath type, (pathsToOpen) => + @lastFilePathOpened = pathsToOpen[0] if pathsToOpen.length > 0 @openPaths({pathsToOpen, devMode, safeMode, window}) promptForPath: (type, callback) -> @@ -680,8 +682,8 @@ class AtomApplication when 'folder' then 'Open Folder' else 'Open' - if process.platform is 'linux' - if projectPath = @lastFocusedWindow?.projectPath - openOptions.defaultPath = projectPath + # If a file was previously opened, set default path to open there again. + if @lastFilePathOpened? and type is 'file' + openOptions.defaultPath = @lastFilePathOpened dialog.showOpenDialog(parentWindow, openOptions, callback) From 1dc518050a8e38818b3f689f6ac431b50d617c2d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 27 Apr 2016 10:56:50 +0200 Subject: [PATCH 875/971] Missing `break` statement when encountering an unmatched scope end tag --- src/tokenized-buffer.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 00d4cdf63..4636ceddc 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -381,6 +381,7 @@ class TokenizedBuffer extends Model filePath: @buffer.getPath() fileContents: @buffer.getText() } + break scopes indentLevelForRow: (bufferRow) -> From 5db3280bf04dce58684ecaec26ee044f05de9780 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 27 Apr 2016 11:59:05 -0400 Subject: [PATCH 876/971] Provide openedPath For backward compatibility with GitRepoAsync before moving to ohnogit. --- src/git-repository-async.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index b691994bc..66b73ba77 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -50,6 +50,10 @@ export default class GitRepositoryAsync { return this.repo._refreshingPromise } + get openedPath () { + return this.repo.openedPath + } + // Public: Destroy this {GitRepositoryAsync} object. // // This destroys any tasks and subscriptions and releases the underlying From 30d6353fd8e46af780bd6b47da585b62b10eddfa Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 27 Apr 2016 12:03:29 -0400 Subject: [PATCH 877/971] Spec for openedPath. --- spec/git-repository-async-spec.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 1fbe537d5..fcb528819 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -55,6 +55,14 @@ describe('GitRepositoryAsync', () => { }) }) + describe('openedPath', () => { + it('is the path passed to .open', () => { + const workingDirPath = copyRepository() + repo = GitRepositoryAsync.open(workingDirPath) + expect(repo.openedPath).toBe(workingDirPath) + }) + }) + describe('.getRepo()', () => { beforeEach(() => { const workingDirectory = copySubmoduleRepository() From 5cfe97160d766170293770975127f4d9e11682d2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 27 Apr 2016 17:47:49 +0200 Subject: [PATCH 878/971] Report boundary when next line's `openScopes` don't match containingTags Sometimes, when performing an edit, a change on some row can cause another row's tokenization to be affected: the classic example is opening a multi-line comment on a line, thereby causing subsequent lines to become commented out without changing the buffer's contents at those locations. We call this technique "spill detection". Since the amount of affected lines can grow quite large, Atom tokenizes synchronously only those lines where the edit occurred, triggering background (i.e. `setInterval`) tokenization for all the other lines that need to be refreshed because of a "spill". As predictable, this approach causes a temporary inconsistency in the stored tokenized lines. In particular, suppose we had two tokenized lines, and that there's an open tag in the middle of the first one which closes on the second one. If we perform an edit that causes that tag to be deleted, when reading the second tokenized line we now have a dangling close tag. This didn't matter much in the `DisplayBuffer` version, because for each line we reopened all the tags found in the stored `openScopes` property, and closed all the tags starting on such line right at the end of it. In the `DisplayLayer` world, however, we don't read tags from each tokenized line, but we let `TokenizedBufferIterator` report tag boundaries and their respective location: since this is an iterator-based approach, we were not reading `openScopes` for each `TokenizedLine`, making the dangling close tag example showed above evident (e.g. close and open tags didn't match anymore, and exceptions were being thrown all over the place). To solve this issue I have considered several approaches: 1. Recompute all the lines where a spill occurs synchronously when the buffer changes. For large files, this can be pretty onerous, and we don't want to regress in terms of performance. 2. Let `TokenizedBuffer.tokenizedLineForRow(bufferRow)` recompute potential invalid lines lazily (starting from the first invalid line, down to the requested buffer row). When editing the first lines of a long file and causing a spill to occur, Atom (or any other package, for that matter) could request a line down in the file, causing this method to recompute lots and lots of lines. 3. Let `DisplayLayer` deal with closing an un-opened tag. This is nice because we already keep track of containing tags there. However, it also feels like the wrong spot where to put this logic, as display layers shouldn't deal with grammar-related stuff. 4. Keep track of containing tags in `TokenizedBufferIterator`, and report a boundary at the end of the line when the subsequent one's `openScopes` property doesn't match the `containingTags` that the iterator has been keeping track of. Of all these solutions I've chosen 4), because it's the most performant and clean in terms of code. --- spec/tokenized-buffer-iterator-spec.js | 63 ++++++++++++++++++++++++++ src/tokenized-buffer-iterator.coffee | 56 +++++++++++++++++------ 2 files changed, 104 insertions(+), 15 deletions(-) diff --git a/spec/tokenized-buffer-iterator-spec.js b/spec/tokenized-buffer-iterator-spec.js index 639714136..8d0e458f4 100644 --- a/spec/tokenized-buffer-iterator-spec.js +++ b/spec/tokenized-buffer-iterator-spec.js @@ -37,4 +37,67 @@ describe('TokenizedBufferIterator', () => { expect(iterator.getCloseTags()).toEqual(['foo']) expect(iterator.getOpenTags()).toEqual([]) }) + + it("reports a boundary at line end if the next line's open scopes don't match the containing tags for the current line", () => { + const tokenizedBuffer = { + tokenizedLineForRow (row) { + if (row === 0) { + return { + tags: [-1, 3, -2, -3], + text: 'bar', + openScopes: [] + } + } else if (row === 1) { + return { + tags: [3], + text: 'baz', + openScopes: [-1] + } + } else if (row === 2) { + return { + tags: [-2], + text: '', + openScopes: [-1] + } + } + } + } + + const grammarRegistry = { + scopeForId (id) { + if (id === -2 || id === -1) { + return 'foo' + } else if (id === -3) { + return 'qux' + } + } + } + + const iterator = new TokenizedBufferIterator(tokenizedBuffer, grammarRegistry) + + iterator.seek(Point(0, 0)) + expect(iterator.getPosition()).toEqual(Point(0, 0)) + expect(iterator.getCloseTags()).toEqual([]) + expect(iterator.getOpenTags()).toEqual(['foo']) + + iterator.moveToSuccessor() + expect(iterator.getPosition()).toEqual(Point(0, 3)) + expect(iterator.getCloseTags()).toEqual(['foo']) + expect(iterator.getOpenTags()).toEqual(['qux']) + + iterator.moveToSuccessor() + expect(iterator.getPosition()).toEqual(Point(0, 3)) + expect(iterator.getCloseTags()).toEqual(['qux']) + expect(iterator.getOpenTags()).toEqual([]) + + iterator.moveToSuccessor() + expect(iterator.getPosition()).toEqual(Point(1, 0)) + expect(iterator.getCloseTags()).toEqual([]) + expect(iterator.getOpenTags()).toEqual(['foo']) + + iterator.moveToSuccessor() + expect(iterator.getPosition()).toEqual(Point(2, 0)) + expect(iterator.getCloseTags()).toEqual(['foo']) + expect(iterator.getOpenTags()).toEqual([]) + }) }) diff --git a/src/tokenized-buffer-iterator.coffee b/src/tokenized-buffer-iterator.coffee index 725041def..ad1834f09 100644 --- a/src/tokenized-buffer-iterator.coffee +++ b/src/tokenized-buffer-iterator.coffee @@ -1,10 +1,12 @@ {Point} = require 'text-buffer' +{isEqual} = require 'underscore-plus' module.exports = class TokenizedBufferIterator constructor: (@tokenizedBuffer, @grammarRegistry) -> @openTags = null @closeTags = null + @containingTags = null seek: (position) -> @openTags = [] @@ -12,9 +14,10 @@ class TokenizedBufferIterator @tagIndex = null currentLine = @tokenizedBuffer.tokenizedLineForRow(position.row) - containingTags = currentLine.openScopes.map (id) => @grammarRegistry.scopeForId(id) @currentTags = currentLine.tags + @currentLineOpenTags = currentLine.openScopes @currentLineLength = currentLine.text.length + @containingTags = @currentLineOpenTags.map (id) => @grammarRegistry.scopeForId(id) currentColumn = 0 for tag, index in @currentTags if tag >= 0 @@ -23,8 +26,8 @@ class TokenizedBufferIterator break else currentColumn += tag - containingTags.pop() while @closeTags.shift() - containingTags.push(tag) while tag = @openTags.shift() + @containingTags.pop() while @closeTags.shift() + @containingTags.push(tag) while tag = @openTags.shift() else scopeName = @grammarRegistry.scopeForId(tag) if tag % 2 is 0 @@ -38,9 +41,11 @@ class TokenizedBufferIterator @tagIndex ?= @currentTags.length @position = Point(position.row, Math.min(@currentLineLength, currentColumn)) - containingTags + @containingTags.slice() moveToSuccessor: -> + @containingTags.pop() for tag in @closeTags + @containingTags.push(tag) for tag in @openTags @openTags = [] @closeTags = [] @@ -49,7 +54,16 @@ class TokenizedBufferIterator if @isAtTagBoundary() break else - return false unless @moveToNextLine() + if @shouldMoveToNextLine + @moveToNextLine() + @openTags = @currentLineOpenTags.map (id) => @grammarRegistry.scopeForId(id) + @shouldMoveToNextLine = false + else if @hasNextLine() and not isEqual(@containingTags, @nextLineOpeningScopes()) + @closeTags = @containingTags.slice().reverse() + @containingTags = [] + @shouldMoveToNextLine = true + else + return false unless @moveToNextLine() else tag = @currentTags[@tagIndex] if tag >= 0 @@ -70,16 +84,6 @@ class TokenizedBufferIterator true - # Private - moveToNextLine: -> - @position = Point(@position.row + 1, 0) - tokenizedLine = @tokenizedBuffer.tokenizedLineForRow(@position.row) - return false unless tokenizedLine? - @currentTags = tokenizedLine.tags - @currentLineLength = tokenizedLine.text.length - @tagIndex = 0 - true - getPosition: -> @position @@ -89,5 +93,27 @@ class TokenizedBufferIterator getOpenTags: -> @openTags.slice() + ### + Section: Private Methods + ### + + hasNextLine: -> + @tokenizedBuffer.tokenizedLineForRow(@position.row + 1)? + + nextLineOpeningScopes: -> + line = @tokenizedBuffer.tokenizedLineForRow(@position.row + 1) + line.openScopes.map (id) => @grammarRegistry.scopeForId(id) + + moveToNextLine: -> + @position = Point(@position.row + 1, 0) + if tokenizedLine = @tokenizedBuffer.tokenizedLineForRow(@position.row) + @currentTags = tokenizedLine.tags + @currentLineLength = tokenizedLine.text.length + @currentLineOpenTags = tokenizedLine.openScopes + @tagIndex = 0 + true + else + false + isAtTagBoundary: -> @closeTags.length > 0 or @openTags.length > 0 From a279db5568707e5c17009735eea4d72295a0eed8 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 27 Apr 2016 12:50:13 -0400 Subject: [PATCH 879/971] Failing test. --- spec/git-spec.coffee | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 7d9e9bbd4..82e371146 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -289,6 +289,16 @@ describe "GitRepository", -> expect(repo.isStatusModified(status)).toBe true expect(repo.isStatusNew(status)).toBe false + it 'caches statuses that were looked up synchronously', -> + originalContent = 'undefined' + fs.writeFileSync(modifiedPath, 'making this path modified') + repo.getPathStatus('file.txt') + + fs.writeFileSync(modifiedPath, originalContent) + waitsForPromise -> repo.refreshStatus() + runs -> + expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeFalsy() + describe "buffer events", -> [editor] = [] From 69e97204d51ec412f0b81acf1eb89369aed08a15 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 27 Apr 2016 12:50:27 -0400 Subject: [PATCH 880/971] Test for undefinedness instead of 0. --- src/git-repository.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index a04124b78..cea000efc 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -369,7 +369,10 @@ class GitRepository @getCachedRelativePathStatus(relativePath) getCachedRelativePathStatus: (relativePath) -> - @statusesByPath[relativePath] ? @async.getCachedPathStatuses()[relativePath] + cachedStatus = @statusesByPath[relativePath] + return cachedStatus if cachedStatus? + + @async.getCachedPathStatuses()[relativePath] # Public: Returns true if the given status indicates modification. # From b4733732dcf45546e976160508e4100339f7f7f8 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 27 Apr 2016 10:35:39 -0700 Subject: [PATCH 881/971] :arrow_up: autocomplete-plus@2.30.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 51d1f06e8..1802e8bd1 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.1", "autocomplete-html": "0.7.2", - "autocomplete-plus": "2.29.2", + "autocomplete-plus": "2.30.0", "autocomplete-snippets": "1.10.0", "autoflow": "0.27.0", "autosave": "0.23.1", From 6ec5cf497b0357657cffd30b5ad89bc6b909c7a2 Mon Sep 17 00:00:00 2001 From: David Elliott Date: Wed, 27 Apr 2016 10:52:57 -0700 Subject: [PATCH 882/971] :bug: Ctrl-O opens file dialog in directory of currently active editor --- src/browser/atom-application.coffee | 32 ++++++++++++++++------------ src/register-default-commands.coffee | 12 ++++++----- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 38c2a354a..b40dbfaad 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -58,7 +58,6 @@ class AtomApplication resourcePath: null version: null quitting: false - lastFilePathOpened: null exit: (status) -> app.exit(status) @@ -171,11 +170,6 @@ class AtomApplication @on 'application:quit', -> app.quit() @on 'application:new-window', -> @openPath(getLoadSettings()) @on 'application:new-file', -> (@focusedWindow() ? this).openPath() - @on 'application:open', -> @promptForPathToOpen('all', getLoadSettings()) - @on 'application:open-file', -> @promptForPathToOpen('file', getLoadSettings()) - @on 'application:open-folder', -> @promptForPathToOpen('folder', getLoadSettings()) - @on 'application:open-dev', -> @promptForPathToOpen('all', devMode: true) - @on 'application:open-safe', -> @promptForPathToOpen('all', safeMode: true) @on 'application:inspect', ({x, y, atomWindow}) -> atomWindow ?= @focusedWindow() atomWindow?.browserWindow.inspectElement(x, y) @@ -256,6 +250,15 @@ class AtomApplication ipcMain.on 'command', (event, command) => @emit(command) + ipcMain.on 'open-command', (event, command, args...) => + switch command + when 'application:open' then @promptForPathToOpen('all', getLoadSettings()) + when 'application:open-file' then @promptForPathToOpen('file', getLoadSettings(), args[0]) + when 'application:open-folder' then @promptForPathToOpen('folder', getLoadSettings()) + when 'application:open-dev' then @promptForPathToOpen('all', devMode: true) + when 'application:open-safe' then @promptForPathToOpen('all', safeMode: true) + else console.log "Invalid open-command received: " + command + ipcMain.on 'window-command', (event, command, args...) -> win = BrowserWindow.fromWebContents(event.sender) win.emit(command, args...) @@ -654,12 +657,13 @@ class AtomApplication # :safeMode - A Boolean which controls whether any newly opened windows # should be in safe mode or not. # :window - An {AtomWindow} to use for opening a selected file path. - promptForPathToOpen: (type, {devMode, safeMode, window}) -> - @promptForPath type, (pathsToOpen) => - @lastFilePathOpened = pathsToOpen[0] if pathsToOpen.length > 0 - @openPaths({pathsToOpen, devMode, safeMode, window}) + # :path - An optional String which controls the default path to which the + # file dialog opens. + promptForPathToOpen: (type, {devMode, safeMode, window}, path=null) -> + @promptForPath type, ((pathsToOpen) => + @openPaths({pathsToOpen, devMode, safeMode, window})), path - promptForPath: (type, callback) -> + promptForPath: (type, callback, path) -> properties = switch type when 'file' then ['openFile'] @@ -682,8 +686,8 @@ class AtomApplication when 'folder' then 'Open Folder' else 'Open' - # If a file was previously opened, set default path to open there again. - if @lastFilePathOpened? and type is 'file' - openOptions.defaultPath = @lastFilePathOpened + # File dialog defaults to project directory of currently active editor + if path? and type is 'file' + openOptions.defaultPath = path dialog.showOpenDialog(parentWindow, openOptions, callback) diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index 035750363..e0b08abdc 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -31,11 +31,13 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage 'application:unhide-all-applications': -> ipcRenderer.send('command', 'application:unhide-all-applications') 'application:new-window': -> ipcRenderer.send('command', 'application:new-window') 'application:new-file': -> ipcRenderer.send('command', 'application:new-file') - 'application:open': -> ipcRenderer.send('command', 'application:open') - 'application:open-file': -> ipcRenderer.send('command', 'application:open-file') - 'application:open-folder': -> ipcRenderer.send('command', 'application:open-folder') - 'application:open-dev': -> ipcRenderer.send('command', 'application:open-dev') - 'application:open-safe': -> ipcRenderer.send('command', 'application:open-safe') + 'application:open': -> ipcRenderer.send('open-command', 'application:open') + 'application:open-file': -> + defaultPath = atom.workspace.getActiveTextEditor()?.getPath() ? atom.project.getPaths()?[0] + ipcRenderer.send('open-command', 'application:open-file', defaultPath) + 'application:open-folder': -> ipcRenderer.send('open-command', 'application:open-folder') + 'application:open-dev': -> ipcRenderer.send('open-command', 'application:open-dev') + 'application:open-safe': -> ipcRenderer.send('open-command', 'application:open-safe') 'application:add-project-folder': -> atom.addProjectFolder() 'application:minimize': -> ipcRenderer.send('command', 'application:minimize') 'application:zoom': -> ipcRenderer.send('command', 'application:zoom') From c0adf125ecaf2100a4e3bd584565d5c6c5435fad Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 27 Apr 2016 16:05:00 -0400 Subject: [PATCH 883/971] Reset the cache before calling the callback. --- src/git-repository.coffee | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index cea000efc..28455c983 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -166,12 +166,10 @@ class GitRepository # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidChangeStatuses: (callback) -> - @async.onDidChangeStatuses -> - # Defer the callback to the next tick so that we've reset - # `@statusesByPath` by the time it's called. Otherwise reads from within - # the callback could be inconsistent. - # See https://github.com/atom/atom/issues/11396 - process.nextTick callback + @async.onDidChangeStatuses => + @branch = @async?.branch + @statusesByPath = {} + callback() ### Section: Repository Details @@ -369,10 +367,7 @@ class GitRepository @getCachedRelativePathStatus(relativePath) getCachedRelativePathStatus: (relativePath) -> - cachedStatus = @statusesByPath[relativePath] - return cachedStatus if cachedStatus? - - @async.getCachedPathStatuses()[relativePath] + @statusesByPath[relativePath] ? @async.getCachedPathStatuses()[relativePath] # Public: Returns true if the given status indicates modification. # @@ -499,10 +494,7 @@ class GitRepository # # Returns a promise that resolves when the repository has been refreshed. refreshStatus: -> - asyncRefresh = @async.refreshStatus().then => - @statusesByPath = {} - @branch = @async?.branch - + asyncRefresh = @async.refreshStatus() syncRefresh = new Promise (resolve, reject) => @handlerPath ?= require.resolve('./repository-status-handler') From 630b8c69a603e69351864bb6fd851e1e9b4ef063 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 27 Apr 2016 16:24:29 -0400 Subject: [PATCH 884/971] Fix the test. --- spec/git-spec.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 82e371146..88c5c9a86 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -295,7 +295,12 @@ describe "GitRepository", -> repo.getPathStatus('file.txt') fs.writeFileSync(modifiedPath, originalContent) - waitsForPromise -> repo.refreshStatus() + + statusHandler = jasmine.createSpy('statusHandler') + repo.onDidChangeStatuses statusHandler + repo.refreshStatus() + + waitsFor -> statusHandler.callCount > 0 runs -> expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeFalsy() From 4727d6611e53cb8454a77ad607973a2c8efa8885 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 27 Apr 2016 17:06:37 -0400 Subject: [PATCH 885/971] Revert "Fix the test." This reverts commit 630b8c69a603e69351864bb6fd851e1e9b4ef063. --- spec/git-spec.coffee | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 88c5c9a86..82e371146 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -295,12 +295,7 @@ describe "GitRepository", -> repo.getPathStatus('file.txt') fs.writeFileSync(modifiedPath, originalContent) - - statusHandler = jasmine.createSpy('statusHandler') - repo.onDidChangeStatuses statusHandler - repo.refreshStatus() - - waitsFor -> statusHandler.callCount > 0 + waitsForPromise -> repo.refreshStatus() runs -> expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeFalsy() From d11e30579b3203bc179018dacd91441ce37a4fff Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 27 Apr 2016 17:17:09 -0400 Subject: [PATCH 886/971] Manually emit the change event. --- src/git-repository.coffee | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 28455c983..a92a03a89 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -166,10 +166,7 @@ class GitRepository # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidChangeStatuses: (callback) -> - @async.onDidChangeStatuses => - @branch = @async?.branch - @statusesByPath = {} - callback() + @emitter.on 'did-change-statuses', callback ### Section: Repository Details @@ -494,7 +491,23 @@ class GitRepository # # Returns a promise that resolves when the repository has been refreshed. refreshStatus: -> - asyncRefresh = @async.refreshStatus() + statusesChanged = false + subscription = @async.onDidChangeStatuses -> + subscription?.dispose() + subscription = null + + statusesChanged = true + + asyncRefresh = @async.refreshStatus().then => + subscription?.dispose() + subscription = null + + @branch = @async?.branch + @statusesByPath = {} + + if statusesChanged + @emitter.emit 'did-change-statuses' + syncRefresh = new Promise (resolve, reject) => @handlerPath ?= require.resolve('./repository-status-handler') From 77dcf37ee3587f18356943f46c28462ef9bd93a5 Mon Sep 17 00:00:00 2001 From: Giuseppe Piscopo Date: Thu, 21 Apr 2016 01:53:11 +0200 Subject: [PATCH 887/971] extract process spawning from squirrel-update --- spec/spawner-spec.coffee | 57 +++++++++++++++++++++++++ spec/squirrel-update-spec.coffee | 68 +++++++++++++----------------- src/browser/spawner.coffee | 36 ++++++++++++++++ src/browser/squirrel-update.coffee | 38 +++-------------- 4 files changed, 128 insertions(+), 71 deletions(-) create mode 100644 spec/spawner-spec.coffee create mode 100644 src/browser/spawner.coffee diff --git a/spec/spawner-spec.coffee b/spec/spawner-spec.coffee new file mode 100644 index 000000000..4ae2149e5 --- /dev/null +++ b/spec/spawner-spec.coffee @@ -0,0 +1,57 @@ +ChildProcess = require 'child_process' +Spawner = require '../src/browser/spawner' + +describe "Spawner", -> + beforeEach -> + # Prevent any commands from actually running and affecting the host + originalSpawn = ChildProcess.spawn + + harmlessSpawn = + # Just spawn something that won't actually modify the host + if process.platform is 'win32' + originalSpawn('dir') + else + originalSpawn('ls') + + spyOn(ChildProcess, 'spawn').andCallFake (command, args, callback) -> + harmlessSpawn + + it "invokes passed callback", -> + someCallback = jasmine.createSpy('someCallback') + + Spawner.spawn('some-command', 'some-args', someCallback) + + waitsFor -> + someCallback.callCount is 1 + + it "spawns passed command with arguments", -> + actualCommand = null + actualArgs = null + + # Redefine fake invocation, so to remember passed arguments + jasmine.unspy(ChildProcess, 'spawn') + spyOn(ChildProcess, 'spawn').andCallFake (command, args) -> + actualCommand = command + actualArgs = args + harmlessSpawn + + expectedCommand = 'some-command' + expectedArgs = 'some-args' + someCallback = jasmine.createSpy('someCallback') + + Spawner.spawn(expectedCommand, expectedArgs, someCallback) + + expect(actualCommand).toBe expectedCommand + expect(actualArgs).toBe expectedArgs + + it "ignores errors by spawned process", -> + # Redefine fake invocation, so to cause an error + jasmine.unspy(ChildProcess, 'spawn') + spyOn(ChildProcess, 'spawn').andCallFake -> throw new Error("EBUSY") + + someCallback = jasmine.createSpy('someCallback') + + expect(Spawner.spawn('some-command', 'some-args', someCallback)).toBe undefined + + waitsFor -> + someCallback.callCount is 1 diff --git a/spec/squirrel-update-spec.coffee b/spec/squirrel-update-spec.coffee index e3891ed1c..e11fb1f00 100644 --- a/spec/squirrel-update-spec.coffee +++ b/spec/squirrel-update-spec.coffee @@ -1,39 +1,22 @@ -ChildProcess = require 'child_process' {EventEmitter} = require 'events' fs = require 'fs-plus' path = require 'path' temp = require 'temp' SquirrelUpdate = require '../src/browser/squirrel-update' +Spawner = require '../src/browser/spawner' describe "Windows Squirrel Update", -> tempHomeDirectory = null - originalSpawn = ChildProcess.spawn - - harmlessSpawn = -> - # Just spawn something that won't actually modify the host - if process.platform is 'win32' - originalSpawn('dir') - else - originalSpawn('ls') beforeEach -> # Prevent the actual home directory from being manipulated tempHomeDirectory = temp.mkdirSync('atom-temp-home-') spyOn(fs, 'getHomeDirectory').andReturn(tempHomeDirectory) - # Prevent any commands from actually running and affecting the host - spyOn(ChildProcess, 'spawn').andCallFake (command, args) -> - harmlessSpawn() - - it "ignores errors spawning Squirrel", -> - jasmine.unspy(ChildProcess, 'spawn') - spyOn(ChildProcess, 'spawn').andCallFake -> throw new Error("EBUSY") - - app = quit: jasmine.createSpy('quit') - expect(SquirrelUpdate.handleStartupEvent(app, '--squirrel-install')).toBe true - - waitsFor -> - app.quit.callCount is 1 + # Prevent any spawned command from actually running and affecting the host + spyOn(Spawner, 'spawn').andCallFake (command, args, callback) -> + # do nothing on command, just run passed callback + invokeCallback callback it "quits the app on all squirrel events", -> app = quit: jasmine.createSpy('quit') @@ -69,51 +52,52 @@ describe "Windows Squirrel Update", -> describe "Desktop shortcut", -> desktopShortcutPath = '/non/existing/path' - + beforeEach -> desktopShortcutPath = path.join(tempHomeDirectory, 'Desktop', 'Atom.lnk') - jasmine.unspy(ChildProcess, 'spawn') - spyOn(ChildProcess, 'spawn').andCallFake (command, args) -> + jasmine.unspy(Spawner, 'spawn') + spyOn(Spawner, 'spawn').andCallFake (command, args, callback) -> if path.basename(command) is 'Update.exe' and args?[0] is '--createShortcut' - fs.writeFileSync(path.join(tempHomeDirectory, 'Desktop', 'Atom.lnk'), '') - harmlessSpawn() + fs.writeFileSync(desktopShortcutPath, '') else - throw new Error("API not mocked") - + # simply ignore other commands + + invokeCallback callback + it "does not exist before install", -> expect(fs.existsSync(desktopShortcutPath)).toBe false - + describe "on install", -> beforeEach -> app = quit: jasmine.createSpy('quit') SquirrelUpdate.handleStartupEvent(app, '--squirrel-install') waitsFor -> app.quit.callCount is 1 - + it "creates desktop shortcut", -> expect(fs.existsSync(desktopShortcutPath)).toBe true - + describe "when shortcut is deleted and then app is updated", -> beforeEach -> fs.removeSync(desktopShortcutPath) expect(fs.existsSync(desktopShortcutPath)).toBe false - + app = quit: jasmine.createSpy('quit') SquirrelUpdate.handleStartupEvent(app, '--squirrel-updated') waitsFor -> app.quit.callCount is 1 - + it "does not recreate shortcut", -> expect(fs.existsSync(desktopShortcutPath)).toBe false - + describe "when shortcut is kept and app is updated", -> beforeEach -> app = quit: jasmine.createSpy('quit') SquirrelUpdate.handleStartupEvent(app, '--squirrel-updated') waitsFor -> app.quit.callCount is 1 - + it "still has desktop shortcut", -> expect(fs.existsSync(desktopShortcutPath)).toBe true @@ -125,7 +109,13 @@ describe "Windows Squirrel Update", -> SquirrelUpdate.restartAtom(app) expect(app.quit.callCount).toBe 1 - expect(ChildProcess.spawn.callCount).toBe 0 + expect(Spawner.spawn.callCount).toBe 0 app.emit('will-quit') - expect(ChildProcess.spawn.callCount).toBe 1 - expect(path.basename(ChildProcess.spawn.argsForCall[0][0])).toBe 'atom.cmd' + expect(Spawner.spawn.callCount).toBe 1 + expect(path.basename(Spawner.spawn.argsForCall[0][0])).toBe 'atom.cmd' + +# Run passed callback as Spawner.spawn() would do +invokeCallback = (callback) -> + error = null + stdout = '' + callback?(error, stdout) diff --git a/src/browser/spawner.coffee b/src/browser/spawner.coffee new file mode 100644 index 000000000..edf93182e --- /dev/null +++ b/src/browser/spawner.coffee @@ -0,0 +1,36 @@ +ChildProcess = require 'child_process' + +# Spawn a command and invoke the callback when it completes with an error +# and the output from standard out. +# +# * `command` The underlying OS command {String} to execute. +# * `args` (optional) The {Array} with arguments to be passed to command. +# * `callback` (optional) The {Function} to call after the command has run. It will be invoked with arguments: +# * `error` (optional) An {Error} object returned by the command, `null` if no error was thrown. +# * `code` Error code returned by the command. +# * `stdout` The {String} output text generated by the command. +# * `stdout` The {String} output text generated by the command. +# +# Returns `undefined`. +exports.spawn = (command, args, callback) -> + stdout = '' + + try + spawnedProcess = ChildProcess.spawn(command, args) + catch error + # Spawn can throw an error + process.nextTick -> callback?(error, stdout) + return + + spawnedProcess.stdout.on 'data', (data) -> stdout += data + + error = null + spawnedProcess.on 'error', (processError) -> error ?= processError + spawnedProcess.on 'close', (code, signal) -> + error ?= new Error("Command failed: #{signal ? code}") if code isnt 0 + error?.code ?= code + error?.stdout ?= stdout + callback?(error, stdout) + # This is necessary if using Powershell 2 on Windows 7 to get the events to raise + # http://stackoverflow.com/questions/9155289/calling-powershell-from-nodejs + spawnedProcess.stdin.end() diff --git a/src/browser/squirrel-update.coffee b/src/browser/squirrel-update.coffee index 9d6f772db..7afc7641f 100644 --- a/src/browser/squirrel-update.coffee +++ b/src/browser/squirrel-update.coffee @@ -1,6 +1,6 @@ -ChildProcess = require 'child_process' fs = require 'fs-plus' path = require 'path' +Spawner = require './spawner' appFolder = path.resolve(process.execPath, '..') rootAtomFolder = path.resolve(appFolder, '..') @@ -25,35 +25,9 @@ backgroundKeyPath = 'HKCU\\Software\\Classes\\directory\\background\\shell\\Atom applicationsKeyPath = 'HKCU\\Software\\Classes\\Applications\\atom.exe' environmentKeyPath = 'HKCU\\Environment' -# Spawn a command and invoke the callback when it completes with an error -# and the output from standard out. -spawn = (command, args, callback) -> - stdout = '' - - try - spawnedProcess = ChildProcess.spawn(command, args) - catch error - # Spawn can throw an error - process.nextTick -> callback?(error, stdout) - return - - spawnedProcess.stdout.on 'data', (data) -> stdout += data - - error = null - spawnedProcess.on 'error', (processError) -> error ?= processError - spawnedProcess.on 'close', (code, signal) -> - error ?= new Error("Command failed: #{signal ? code}") if code isnt 0 - error?.code ?= code - error?.stdout ?= stdout - callback?(error, stdout) - # This is necessary if using Powershell 2 on Windows 7 to get the events to raise - # http://stackoverflow.com/questions/9155289/calling-powershell-from-nodejs - spawnedProcess.stdin.end() - - # Spawn reg.exe and callback when it completes spawnReg = (args, callback) -> - spawn(regPath, args, callback) + Spawner.spawn(regPath, args, callback) # Spawn powershell.exe and callback when it completes spawnPowershell = (args, callback) -> @@ -69,16 +43,16 @@ spawnPowershell = (args, callback) -> args.unshift('RemoteSigned') args.unshift('-ExecutionPolicy') args.unshift('-noprofile') - spawn(powershellPath, args, callback) + Spawner.spawn(powershellPath, args, callback) # Spawn setx.exe and callback when it completes spawnSetx = (args, callback) -> - spawn(setxPath, args, callback) + Spawner.spawn(setxPath, args, callback) # Spawn the Update.exe with the given arguments and invoke the callback when # the command completes. spawnUpdate = (args, callback) -> - spawn(updateDotExe, args, callback) + Spawner.spawn(updateDotExe, args, callback) # Install the Open with Atom explorer context menu items via the registry. installContextMenu = (callback) -> @@ -220,7 +194,7 @@ exports.existsSync = -> exports.restartAtom = (app) -> if projectPath = global.atomApplication?.lastFocusedWindow?.projectPath args = [projectPath] - app.once 'will-quit', -> spawn(path.join(binFolder, 'atom.cmd'), args) + app.once 'will-quit', -> Spawner.spawn(path.join(binFolder, 'atom.cmd'), args) app.quit() # Handle squirrel events denoted by --squirrel-* command line arguments. From 63c45cc8fd7d37903178c2e994b0e6e24a247046 Mon Sep 17 00:00:00 2001 From: Giuseppe Piscopo Date: Thu, 21 Apr 2016 03:25:09 +0200 Subject: [PATCH 888/971] extract Windows registry operations from squirrel-update --- spec/spawner-spec.coffee | 2 +- spec/squirrel-update-spec.coffee | 24 ++++++++---- src/browser/squirrel-update.coffee | 54 ++------------------------ src/browser/win-registry.coffee | 62 ++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 59 deletions(-) create mode 100644 src/browser/win-registry.coffee diff --git a/spec/spawner-spec.coffee b/spec/spawner-spec.coffee index 4ae2149e5..daa804bbb 100644 --- a/spec/spawner-spec.coffee +++ b/spec/spawner-spec.coffee @@ -13,7 +13,7 @@ describe "Spawner", -> else originalSpawn('ls') - spyOn(ChildProcess, 'spawn').andCallFake (command, args, callback) -> + spyOn(ChildProcess, 'spawn').andCallFake (command, args, callback) -> harmlessSpawn it "invokes passed callback", -> diff --git a/spec/squirrel-update-spec.coffee b/spec/squirrel-update-spec.coffee index e11fb1f00..ecfd780d6 100644 --- a/spec/squirrel-update-spec.coffee +++ b/spec/squirrel-update-spec.coffee @@ -4,8 +4,15 @@ path = require 'path' temp = require 'temp' SquirrelUpdate = require '../src/browser/squirrel-update' Spawner = require '../src/browser/spawner' +WinRegistry = require '../src/browser/win-registry' -describe "Windows Squirrel Update", -> +# Run passed callback as Spawner.spawn() would do +invokeCallback = (callback) -> + error = null + stdout = '' + callback?(error, stdout) + +fdescribe "Windows Squirrel Update", -> tempHomeDirectory = null beforeEach -> @@ -14,10 +21,17 @@ describe "Windows Squirrel Update", -> spyOn(fs, 'getHomeDirectory').andReturn(tempHomeDirectory) # Prevent any spawned command from actually running and affecting the host - spyOn(Spawner, 'spawn').andCallFake (command, args, callback) -> + spyOn(Spawner, 'spawn').andCallFake (command, args, callback) -> # do nothing on command, just run passed callback invokeCallback callback + # Prevent any actual change to Windows registry + for own method of WinRegistry + # all WinRegistry APIs share the same signature + spyOn(WinRegistry, method).andCallFake (callback) -> + # do nothing on registry, just run passed callback + invokeCallback callback + it "quits the app on all squirrel events", -> app = quit: jasmine.createSpy('quit') @@ -113,9 +127,3 @@ describe "Windows Squirrel Update", -> app.emit('will-quit') expect(Spawner.spawn.callCount).toBe 1 expect(path.basename(Spawner.spawn.argsForCall[0][0])).toBe 'atom.cmd' - -# Run passed callback as Spawner.spawn() would do -invokeCallback = (callback) -> - error = null - stdout = '' - callback?(error, stdout) diff --git a/src/browser/squirrel-update.coffee b/src/browser/squirrel-update.coffee index 7afc7641f..58bcf2db9 100644 --- a/src/browser/squirrel-update.coffee +++ b/src/browser/squirrel-update.coffee @@ -1,6 +1,7 @@ fs = require 'fs-plus' path = require 'path' Spawner = require './spawner' +WinRegistry = require './win-registry' appFolder = path.resolve(process.execPath, '..') rootAtomFolder = path.resolve(appFolder, '..') @@ -10,25 +11,12 @@ exeName = path.basename(process.execPath) if process.env.SystemRoot system32Path = path.join(process.env.SystemRoot, 'System32') - regPath = path.join(system32Path, 'reg.exe') powershellPath = path.join(system32Path, 'WindowsPowerShell', 'v1.0', 'powershell.exe') setxPath = path.join(system32Path, 'setx.exe') else - regPath = 'reg.exe' powershellPath = 'powershell.exe' setxPath = 'setx.exe' -# Registry keys used for context menu -fileKeyPath = 'HKCU\\Software\\Classes\\*\\shell\\Atom' -directoryKeyPath = 'HKCU\\Software\\Classes\\directory\\shell\\Atom' -backgroundKeyPath = 'HKCU\\Software\\Classes\\directory\\background\\shell\\Atom' -applicationsKeyPath = 'HKCU\\Software\\Classes\\Applications\\atom.exe' -environmentKeyPath = 'HKCU\\Environment' - -# Spawn reg.exe and callback when it completes -spawnReg = (args, callback) -> - Spawner.spawn(regPath, args, callback) - # Spawn powershell.exe and callback when it completes spawnPowershell = (args, callback) -> # set encoding and execute the command, capture the output, and return it via .NET's console in order to have consistent UTF-8 encoding @@ -54,30 +42,6 @@ spawnSetx = (args, callback) -> spawnUpdate = (args, callback) -> Spawner.spawn(updateDotExe, args, callback) -# Install the Open with Atom explorer context menu items via the registry. -installContextMenu = (callback) -> - addToRegistry = (args, callback) -> - args.unshift('add') - args.push('/f') - spawnReg(args, callback) - - installFileHandler = (callback) -> - args = ["#{applicationsKeyPath}\\shell\\open\\command", '/ve', '/d', "\"#{process.execPath}\" \"%1\""] - addToRegistry(args, callback) - - installMenu = (keyPath, arg, callback) -> - args = [keyPath, '/ve', '/d', 'Open with Atom'] - addToRegistry args, -> - args = [keyPath, '/v', 'Icon', '/d', "\"#{process.execPath}\""] - addToRegistry args, -> - args = ["#{keyPath}\\command", '/ve', '/d', "\"#{process.execPath}\" \"#{arg}\""] - addToRegistry(args, callback) - - installMenu fileKeyPath, '%1', -> - installMenu directoryKeyPath, '%1', -> - installMenu backgroundKeyPath, '%V', -> - installFileHandler(callback) - # Get the user's PATH environment variable registry value. getPath = (callback) -> spawnPowershell ['[environment]::GetEnvironmentVariable(\'Path\',\'User\')'], (error, stdout) -> @@ -87,16 +51,6 @@ getPath = (callback) -> pathOutput = stdout.replace(/^\s+|\s+$/g, '') callback(null, pathOutput) -# Uninstall the Open with Atom explorer context menu items via the registry. -uninstallContextMenu = (callback) -> - deleteFromRegistry = (keyPath, callback) -> - spawnReg(['delete', keyPath, '/f'], callback) - - deleteFromRegistry fileKeyPath, -> - deleteFromRegistry directoryKeyPath, -> - deleteFromRegistry backgroundKeyPath, -> - deleteFromRegistry(applicationsKeyPath, callback) - # Add atom and apm to the PATH # # This is done by adding .cmd shims to the root bin folder in the Atom @@ -202,19 +156,19 @@ exports.handleStartupEvent = (app, squirrelCommand) -> switch squirrelCommand when '--squirrel-install' createShortcuts -> - installContextMenu -> + WinRegistry.installContextMenu -> addCommandsToPath -> app.quit() true when '--squirrel-updated' updateShortcuts -> - installContextMenu -> + WinRegistry.installContextMenu -> addCommandsToPath -> app.quit() true when '--squirrel-uninstall' removeShortcuts -> - uninstallContextMenu -> + WinRegistry.uninstallContextMenu -> removeCommandsFromPath -> app.quit() true diff --git a/src/browser/win-registry.coffee b/src/browser/win-registry.coffee new file mode 100644 index 000000000..f4b81b377 --- /dev/null +++ b/src/browser/win-registry.coffee @@ -0,0 +1,62 @@ +path = require 'path' +Spawner = require './spawner' + +if process.env.SystemRoot + system32Path = path.join(process.env.SystemRoot, 'System32') + regPath = path.join(system32Path, 'reg.exe') +else + regPath = 'reg.exe' + +# Registry keys used for context menu +fileKeyPath = 'HKCU\\Software\\Classes\\*\\shell\\Atom' +directoryKeyPath = 'HKCU\\Software\\Classes\\directory\\shell\\Atom' +backgroundKeyPath = 'HKCU\\Software\\Classes\\directory\\background\\shell\\Atom' +applicationsKeyPath = 'HKCU\\Software\\Classes\\Applications\\atom.exe' + +# Spawn reg.exe and callback when it completes +spawnReg = (args, callback) -> + Spawner.spawn(regPath, args, callback) + +# Install the Open with Atom explorer context menu items via the registry. +# +# * `callback` The {Function} to call after registry operation is done. +# It will be invoked with the same arguments provided by {Spawner.spawn}. +# +# Returns `undefined`. +exports.installContextMenu = (callback) -> + addToRegistry = (args, callback) -> + args.unshift('add') + args.push('/f') + spawnReg(args, callback) + + installFileHandler = (callback) -> + args = ["#{applicationsKeyPath}\\shell\\open\\command", '/ve', '/d', "\"#{process.execPath}\" \"%1\""] + addToRegistry(args, callback) + + installMenu = (keyPath, arg, callback) -> + args = [keyPath, '/ve', '/d', 'Open with Atom'] + addToRegistry args, -> + args = [keyPath, '/v', 'Icon', '/d', "\"#{process.execPath}\""] + addToRegistry args, -> + args = ["#{keyPath}\\command", '/ve', '/d', "\"#{process.execPath}\" \"#{arg}\""] + addToRegistry(args, callback) + + installMenu fileKeyPath, '%1', -> + installMenu directoryKeyPath, '%1', -> + installMenu backgroundKeyPath, '%V', -> + installFileHandler(callback) + +# Uninstall the Open with Atom explorer context menu items via the registry. +# +# * `callback` The {Function} to call after registry operation is done. +# It will be invoked with the same arguments provided by {Spawner.spawn}. +# +# Returns `undefined`. +exports.uninstallContextMenu = (callback) -> + deleteFromRegistry = (keyPath, callback) -> + spawnReg(['delete', keyPath, '/f'], callback) + + deleteFromRegistry fileKeyPath, -> + deleteFromRegistry directoryKeyPath, -> + deleteFromRegistry backgroundKeyPath, -> + deleteFromRegistry(applicationsKeyPath, callback) From cd8b28da4da588b4bf63d410da6a14f96adbc8d9 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 27 Apr 2016 22:40:34 -0400 Subject: [PATCH 889/971] Document why --- src/git-repository.coffee | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index a92a03a89..fcbc40830 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -492,6 +492,11 @@ class GitRepository # Returns a promise that resolves when the repository has been refreshed. refreshStatus: -> statusesChanged = false + + # Listen for `did-change-statuses` so we know if something changed. But we + # need to wait to propagate it until after we've set the branch and cleared + # the `statusesByPath` cache. So just set a flag, and we'll emit the event + # after refresh is done. subscription = @async.onDidChangeStatuses -> subscription?.dispose() subscription = null From 3b5bcf1c0dc71eaccb0ee29b648d8285706ecf37 Mon Sep 17 00:00:00 2001 From: livelazily Date: Mon, 25 Apr 2016 22:23:40 +0800 Subject: [PATCH 890/971] :bug: Wait for connection end to get completed data; --- src/browser/atom-application.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 69eff1845..3255b0a19 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -138,7 +138,11 @@ class AtomApplication return unless @socketPath? @deleteSocketFile() server = net.createServer (connection) => - connection.on 'data', (data) => + data = '' + connection.on 'data', (chunk) -> + data = data + chunk + + connection.on 'end', => options = JSON.parse(data) @openWithOptions(options) From bebaf1bdb08db56a527c625d6c537e356a33cf16 Mon Sep 17 00:00:00 2001 From: Giuseppe Piscopo Date: Thu, 28 Apr 2016 09:24:37 +0200 Subject: [PATCH 891/971] extract Windows PowerShell operations from squirrel-update --- spec/squirrel-update-spec.coffee | 3 ++- src/browser/squirrel-update.coffee | 32 +++--------------------- src/browser/win-powershell.coffee | 39 ++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 30 deletions(-) create mode 100644 src/browser/win-powershell.coffee diff --git a/spec/squirrel-update-spec.coffee b/spec/squirrel-update-spec.coffee index ecfd780d6..4a6936a50 100644 --- a/spec/squirrel-update-spec.coffee +++ b/spec/squirrel-update-spec.coffee @@ -4,6 +4,7 @@ path = require 'path' temp = require 'temp' SquirrelUpdate = require '../src/browser/squirrel-update' Spawner = require '../src/browser/spawner' +WinPowerShell = require '../src/browser/win-powershell' WinRegistry = require '../src/browser/win-registry' # Run passed callback as Spawner.spawn() would do @@ -12,7 +13,7 @@ invokeCallback = (callback) -> stdout = '' callback?(error, stdout) -fdescribe "Windows Squirrel Update", -> +describe "Windows Squirrel Update", -> tempHomeDirectory = null beforeEach -> diff --git a/src/browser/squirrel-update.coffee b/src/browser/squirrel-update.coffee index 58bcf2db9..a1bfc5359 100644 --- a/src/browser/squirrel-update.coffee +++ b/src/browser/squirrel-update.coffee @@ -2,6 +2,7 @@ fs = require 'fs-plus' path = require 'path' Spawner = require './spawner' WinRegistry = require './win-registry' +WinPowerShell = require './win-powershell' appFolder = path.resolve(process.execPath, '..') rootAtomFolder = path.resolve(appFolder, '..') @@ -11,28 +12,10 @@ exeName = path.basename(process.execPath) if process.env.SystemRoot system32Path = path.join(process.env.SystemRoot, 'System32') - powershellPath = path.join(system32Path, 'WindowsPowerShell', 'v1.0', 'powershell.exe') setxPath = path.join(system32Path, 'setx.exe') else - powershellPath = 'powershell.exe' setxPath = 'setx.exe' -# Spawn powershell.exe and callback when it completes -spawnPowershell = (args, callback) -> - # set encoding and execute the command, capture the output, and return it via .NET's console in order to have consistent UTF-8 encoding - # http://stackoverflow.com/questions/22349139/utf-8-output-from-powershell - # to address https://github.com/atom/atom/issues/5063 - args[0] = """ - [Console]::OutputEncoding=[System.Text.Encoding]::UTF8 - $output=#{args[0]} - [Console]::WriteLine($output) - """ - args.unshift('-command') - args.unshift('RemoteSigned') - args.unshift('-ExecutionPolicy') - args.unshift('-noprofile') - Spawner.spawn(powershellPath, args, callback) - # Spawn setx.exe and callback when it completes spawnSetx = (args, callback) -> Spawner.spawn(setxPath, args, callback) @@ -42,15 +25,6 @@ spawnSetx = (args, callback) -> spawnUpdate = (args, callback) -> Spawner.spawn(updateDotExe, args, callback) -# Get the user's PATH environment variable registry value. -getPath = (callback) -> - spawnPowershell ['[environment]::GetEnvironmentVariable(\'Path\',\'User\')'], (error, stdout) -> - if error? - return callback(error) - - pathOutput = stdout.replace(/^\s+|\s+$/g, '') - callback(null, pathOutput) - # Add atom and apm to the PATH # # This is done by adding .cmd shims to the root bin folder in the Atom @@ -88,7 +62,7 @@ addCommandsToPath = (callback) -> installCommands (error) -> return callback(error) if error? - getPath (error, pathEnv) -> + WinPowerShell.getPath (error, pathEnv) -> return callback(error) if error? pathSegments = pathEnv.split(/;+/).filter (pathSegment) -> pathSegment @@ -99,7 +73,7 @@ addCommandsToPath = (callback) -> # Remove atom and apm from the PATH removeCommandsFromPath = (callback) -> - getPath (error, pathEnv) -> + WinPowerShell.getPath (error, pathEnv) -> return callback(error) if error? pathSegments = pathEnv.split(/;+/).filter (pathSegment) -> diff --git a/src/browser/win-powershell.coffee b/src/browser/win-powershell.coffee new file mode 100644 index 000000000..f3e73bc8a --- /dev/null +++ b/src/browser/win-powershell.coffee @@ -0,0 +1,39 @@ +path = require 'path' +Spawner = require './spawner' + +if process.env.SystemRoot + system32Path = path.join(process.env.SystemRoot, 'System32') + powershellPath = path.join(system32Path, 'WindowsPowerShell', 'v1.0', 'powershell.exe') +else + powershellPath = 'powershell.exe' + +# Spawn powershell.exe and callback when it completes +spawnPowershell = (args, callback) -> + # Set encoding and execute the command, capture the output, and return it + # via .NET's console in order to have consistent UTF-8 encoding. + # See http://stackoverflow.com/questions/22349139/utf-8-output-from-powershell + # to address https://github.com/atom/atom/issues/5063 + args[0] = """ + [Console]::OutputEncoding=[System.Text.Encoding]::UTF8 + $output=#{args[0]} + [Console]::WriteLine($output) + """ + args.unshift('-command') + args.unshift('RemoteSigned') + args.unshift('-ExecutionPolicy') + args.unshift('-noprofile') + Spawner.spawn(powershellPath, args, callback) + +# Get the user's PATH environment variable registry value. +# +# * `callback` The {Function} to call after registry operation is done. +# It will be invoked with the same arguments provided by {Spawner.spawn}. +# +# Returns the user's path {String}. +exports.getPath = (callback) -> + spawnPowershell ['[environment]::GetEnvironmentVariable(\'Path\',\'User\')'], (error, stdout) -> + if error? + return callback(error) + + pathOutput = stdout.replace(/^\s+|\s+$/g, '') + callback(null, pathOutput) From 57442781ec6979e66bb5819a0782b75dfa15cd02 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 28 Apr 2016 12:05:58 +0200 Subject: [PATCH 892/971] Fix bug when positioning cursors after the fold-marker --- package.json | 2 +- spec/text-editor-component-spec.js | 11 +++++++++++ src/lines-tile-component.coffee | 10 ++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index bff11c2f2..abbcd7b70 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.0.0-beta5", + "text-buffer": "9.0.0-beta6", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 1b29cbd7b..83f5d1b80 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -1249,6 +1249,17 @@ describe('TextEditorComponent', function () { expect(cursorRect.width).toBeCloseTo(rangeRect.width, 0) }) + it('positions cursors after the fold-marker when a fold ends the line', async function () { + editor.foldBufferRow(0) + await nextViewUpdatePromise() + editor.setCursorScreenPosition([0, 30]) + await nextViewUpdatePromise() + + let cursorRect = componentNode.querySelector('.cursor').getBoundingClientRect() + let foldMarkerRect = componentNode.querySelector('.fold-marker').getBoundingClientRect() + expect(cursorRect.left).toBeCloseTo(foldMarkerRect.right, 0) + }) + it('positions cursors correctly after character widths are changed via a stylesheet change', async function () { atom.config.set('editor.fontFamily', 'sans-serif') editor.setCursorScreenPosition([0, 16]) diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index c9ecd0982..3d82d9d9f 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -4,6 +4,7 @@ HighlightsComponent = require './highlights-component' AcceptFilter = {acceptNode: -> NodeFilter.FILTER_ACCEPT} TokenTextEscapeRegex = /[&"'<>]/g MaxTokenLength = 20000 +ZERO_WIDTH_NBSP = '\ufeff' cloneObject = (object) -> clone = {} @@ -225,6 +226,15 @@ class LinesTileComponent lineNode.appendChild(textNode) textNodes.push(textNode) + if lineText.endsWith(@presenter.displayLayer.foldCharacter) + # Insert a zero-width non-breaking whitespace, so that + # LinesYardstick can take the fold-marker::after pseudo-element + # into account during measurements when such marker is the last + # character on the line. + textNode = @domElementPool.buildText(ZERO_WIDTH_NBSP) + lineNode.appendChild(textNode) + textNodes.push(textNode) + @textNodesByLineId[id] = textNodes lineNode From f2a497d591654bcce4d9727efa6796be012935e8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 28 Apr 2016 13:43:00 +0200 Subject: [PATCH 893/971] Don't create folds for empty ranges --- spec/selection-spec.coffee | 19 +++++++++++++++++++ src/selection.coffee | 5 +++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/spec/selection-spec.coffee b/spec/selection-spec.coffee index 18095d6f8..7511c4b39 100644 --- a/spec/selection-spec.coffee +++ b/spec/selection-spec.coffee @@ -101,3 +101,22 @@ describe "Selection", -> selection.setBufferRange [[2, 0], [3, 0]] selection.insertText("\r\n", autoIndent: true) expect(buffer.lineForRow(2)).toBe " " + + describe ".fold()", -> + it "folds the buffer range spanned by the selection", -> + selection.setBufferRange([[0, 3], [1, 6]]) + selection.fold() + + expect(selection.getScreenRange()).toEqual([[0, 4], [0, 4]]) + expect(selection.getBufferRange()).toEqual([[1, 6], [1, 6]]) + expect(editor.lineTextForScreenRow(0)).toBe "var#{editor.displayLayer.foldCharacter}sort = function(items) {" + expect(editor.isFoldedAtBufferRow(0)).toBe(true) + + it "doesn't create a fold when the selection is empty", -> + selection.setBufferRange([[0, 3], [0, 3]]) + selection.fold() + + expect(selection.getScreenRange()).toEqual([[0, 3], [0, 3]]) + expect(selection.getBufferRange()).toEqual([[0, 3], [0, 3]]) + expect(editor.lineTextForScreenRow(0)).toBe "var quicksort = function () {" + expect(editor.isFoldedAtBufferRow(0)).toBe(false) diff --git a/src/selection.coffee b/src/selection.coffee index ea8ca187a..7ecbb3fbc 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -628,8 +628,9 @@ class Selection extends Model # Public: Creates a fold containing the current selection. fold: -> range = @getBufferRange() - @editor.foldBufferRange(range) - @cursor.setBufferPosition(range.end) + unless range.isEmpty() + @editor.foldBufferRange(range) + @cursor.setBufferPosition(range.end) # Private: Increase the indentation level of the given text by given number # of levels. Leaves the first line unchanged. From 76d8421963e1712f13c2808f1a2725011322080e Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 28 Apr 2016 09:50:01 -0400 Subject: [PATCH 894/971] Inline the no-op assert. --- src/text-editor.coffee | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e3300a3b2..8779cd973 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -101,7 +101,7 @@ class TextEditor extends Model throw new Error("Must pass a clipboard parameter when constructing TextEditors") unless @clipboard? throw new Error("Must pass a grammarRegistry parameter when constructing TextEditors") unless @grammarRegistry? - @assert ?= @defaultAssert.bind(this) + @assert ?= (condition) -> condition @firstVisibleScreenRow ?= 0 @firstVisibleScreenColumn ?= 0 @emitter = new Emitter @@ -3364,9 +3364,6 @@ class TextEditor extends Model @emitter.emit 'will-insert-text', willInsertEvent result - defaultAssert: (condition, message, callback) -> - condition - ### Section: Language Mode Delegated Methods ### From 2c5340c70ff3261af9e78addb5c1c94d4c7eede4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 28 Apr 2016 17:29:59 +0200 Subject: [PATCH 895/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index abbcd7b70..be2613aac 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.0.0-beta6", + "text-buffer": "9.0.0-beta7", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 2f1268dc7ca9ab773837499d4a938064978919ac Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 28 Apr 2016 11:36:17 -0400 Subject: [PATCH 896/971] Move copyPathToClipboard to the default commands. --- src/atom-environment.coffee | 2 +- src/register-default-commands.coffee | 11 ++++++++--- src/text-editor.coffee | 6 ------ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index cf54ee0bc..f50ae9d5b 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -255,7 +255,7 @@ class AtomEnvironment extends Model @deserializers.add(TextBuffer) registerDefaultCommands: -> - registerDefaultCommands({commandRegistry: @commands, @config, @commandInstaller, notificationManager: @notifications}) + registerDefaultCommands({commandRegistry: @commands, @config, @commandInstaller, notificationManager: @notifications, @project, @clipboard}) registerDefaultViewProviders: -> @views.addViewProvider Workspace, (model, env) -> diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index 035750363..1cb3d99b5 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -1,6 +1,6 @@ {ipcRenderer} = require 'electron' -module.exports = ({commandRegistry, commandInstaller, config, notificationManager}) -> +module.exports = ({commandRegistry, commandInstaller, config, notificationManager, project, clipboard}) -> commandRegistry.add 'atom-workspace', 'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem() 'pane:show-previous-recently-used-item': -> @getModel().getActivePane().activatePreviousRecentlyUsedItem() @@ -188,8 +188,8 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage 'editor:fold-at-indent-level-8': -> @foldAllAtIndentLevel(7) 'editor:fold-at-indent-level-9': -> @foldAllAtIndentLevel(8) 'editor:log-cursor-scope': -> showCursorScope(@getCursorScope(), notificationManager) - 'editor:copy-path': -> @copyPathToClipboard(false) - 'editor:copy-project-path': -> @copyPathToClipboard(true) + 'editor:copy-path': -> copyPathToClipboard(this, project, clipboard, false) + 'editor:copy-project-path': -> copyPathToClipboard(this, project, clipboard, true) 'editor:toggle-indent-guide': -> config.set('editor.showIndentGuide', not config.get('editor.showIndentGuide')) 'editor:toggle-line-numbers': -> config.set('editor.showLineNumbers', not config.get('editor.showLineNumbers')) 'editor:scroll-to-cursor': -> @scrollToCursorPosition() @@ -239,3 +239,8 @@ showCursorScope = (descriptor, notificationManager) -> content = "Scopes at Cursor\n#{list.join('\n')}" notificationManager.addInfo(content, dismissable: true) + +copyPathToClipboard = (editor, project, clipboard, relative) -> + if filePath = editor.getPath() + filePath = project.relativize(filePath) if relative + clipboard.write(filePath) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 8779cd973..9976e5906 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -669,12 +669,6 @@ class TextEditor extends Model # Essential: Returns {Boolean} `true` if this editor has no content. isEmpty: -> @buffer.isEmpty() - # Copies the current file path to the native clipboard. - copyPathToClipboard: (relative = false) -> - if filePath = @getPath() - filePath = atom.project.relativize(filePath) if relative - @clipboard.write(filePath) - ### Section: File Operations ### From 07bf40879d0526f4255726e5c63d3720dcb75037 Mon Sep 17 00:00:00 2001 From: Giuseppe Piscopo Date: Thu, 28 Apr 2016 19:25:54 +0200 Subject: [PATCH 897/971] Fix coffeelint error on trailing whitespace --- src/browser/win-powershell.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/win-powershell.coffee b/src/browser/win-powershell.coffee index f3e73bc8a..29925a7c1 100644 --- a/src/browser/win-powershell.coffee +++ b/src/browser/win-powershell.coffee @@ -9,7 +9,7 @@ else # Spawn powershell.exe and callback when it completes spawnPowershell = (args, callback) -> - # Set encoding and execute the command, capture the output, and return it + # Set encoding and execute the command, capture the output, and return it # via .NET's console in order to have consistent UTF-8 encoding. # See http://stackoverflow.com/questions/22349139/utf-8-output-from-powershell # to address https://github.com/atom/atom/issues/5063 From 398ae4491ec8b4a03f3d47343c410c46788975ef Mon Sep 17 00:00:00 2001 From: David Elliott Date: Thu, 28 Apr 2016 11:13:07 -0700 Subject: [PATCH 898/971] :art: Removed application:open-dev and application:open-safe from new method. --- src/browser/atom-application.coffee | 7 ++++--- src/register-default-commands.coffee | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index b40dbfaad..f39c50ce4 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -170,6 +170,8 @@ class AtomApplication @on 'application:quit', -> app.quit() @on 'application:new-window', -> @openPath(getLoadSettings()) @on 'application:new-file', -> (@focusedWindow() ? this).openPath() + @on 'application:open-dev', -> @promptForPathToOpen('all', devMode: true) + @on 'application:open-safe', -> @promptForPathToOpen('all', safeMode: true) @on 'application:inspect', ({x, y, atomWindow}) -> atomWindow ?= @focusedWindow() atomWindow?.browserWindow.inspectElement(x, y) @@ -251,12 +253,11 @@ class AtomApplication @emit(command) ipcMain.on 'open-command', (event, command, args...) => + defaultPath = args[0] if args.length > 0 switch command when 'application:open' then @promptForPathToOpen('all', getLoadSettings()) - when 'application:open-file' then @promptForPathToOpen('file', getLoadSettings(), args[0]) + when 'application:open-file' then @promptForPathToOpen('file', getLoadSettings(), defaultPath) when 'application:open-folder' then @promptForPathToOpen('folder', getLoadSettings()) - when 'application:open-dev' then @promptForPathToOpen('all', devMode: true) - when 'application:open-safe' then @promptForPathToOpen('all', safeMode: true) else console.log "Invalid open-command received: " + command ipcMain.on 'window-command', (event, command, args...) -> diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index e0b08abdc..b2dc1fd68 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -36,8 +36,8 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage defaultPath = atom.workspace.getActiveTextEditor()?.getPath() ? atom.project.getPaths()?[0] ipcRenderer.send('open-command', 'application:open-file', defaultPath) 'application:open-folder': -> ipcRenderer.send('open-command', 'application:open-folder') - 'application:open-dev': -> ipcRenderer.send('open-command', 'application:open-dev') - 'application:open-safe': -> ipcRenderer.send('open-command', 'application:open-safe') + 'application:open-dev': -> ipcRenderer.send('command', 'application:open-dev') + 'application:open-safe': -> ipcRenderer.send('command', 'application:open-safe') 'application:add-project-folder': -> atom.addProjectFolder() 'application:minimize': -> ipcRenderer.send('command', 'application:minimize') 'application:zoom': -> ipcRenderer.send('command', 'application:zoom') From d3ee21941a78c16695efe9b8e634a8f2921acf47 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Thu, 28 Apr 2016 12:23:44 -0700 Subject: [PATCH 899/971] Fix some specs on Windows and honor options.shell --- spec/buffered-process-spec.coffee | 23 +++++++++-------------- src/buffered-process.coffee | 2 +- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/spec/buffered-process-spec.coffee b/spec/buffered-process-spec.coffee index 643a2d411..ec01b40d7 100644 --- a/spec/buffered-process-spec.coffee +++ b/spec/buffered-process-spec.coffee @@ -16,9 +16,9 @@ describe "BufferedProcess", -> describe "when an error event is emitted by the process", -> it "calls the error handler and does not throw an exception", -> process = new BufferedProcess - command: 'bad-command-nope' + command: 'bad-command-nope1' args: ['nothing'] - options: {} + options: {shell: false} errorSpy = jasmine.createSpy().andCallFake (error) -> error.handle() process.onWillThrowError(errorSpy) @@ -28,7 +28,7 @@ describe "BufferedProcess", -> runs -> expect(window.onerror).not.toHaveBeenCalled() expect(errorSpy).toHaveBeenCalled() - expect(errorSpy.mostRecentCall.args[0].error.message).toContain 'spawn bad-command-nope ENOENT' + expect(errorSpy.mostRecentCall.args[0].error.message).toContain 'spawn bad-command-nope1 ENOENT' describe "when an error is thrown spawning the process", -> it "calls the error handler and does not throw an exception", -> @@ -53,17 +53,17 @@ describe "BufferedProcess", -> expect(errorSpy.mostRecentCall.args[0].error.message).toContain 'Something is really wrong' describe "when there is not an error handler specified", -> - it "calls the error handler and does not throw an exception", -> + it "does throw an exception", -> process = new BufferedProcess - command: 'bad-command-nope' + command: 'bad-command-nope2' args: ['nothing'] - options: {} + options: {shell: false} waitsFor -> window.onerror.callCount > 0 runs -> expect(window.onerror).toHaveBeenCalled() - expect(window.onerror.mostRecentCall.args[0]).toContain 'Failed to spawn command `bad-command-nope`' + expect(window.onerror.mostRecentCall.args[0]).toContain 'Failed to spawn command `bad-command-nope2`' expect(window.onerror.mostRecentCall.args[4].name).toBe 'BufferedProcessError' describe "on Windows", -> @@ -72,13 +72,7 @@ describe "BufferedProcess", -> beforeEach -> # Prevent any commands from actually running and affecting the host originalSpawn = ChildProcess.spawn - spyOn(ChildProcess, 'spawn').andCallFake -> - # Just spawn something that won't actually modify the host - if originalPlatform is 'win32' - originalSpawn('dir') - else - originalSpawn('ls') - + spyOn(ChildProcess, 'spawn') originalPlatform = process.platform Object.defineProperty process, 'platform', value: 'win32' @@ -117,6 +111,7 @@ describe "BufferedProcess", -> expect(stdout).toEqual '' it "calls the specified stdout callback only with whole lines", -> + # WINSPEC: Fails because command is too long, /bin/echo is *nix only, odd newlining and quoting exitCallback = jasmine.createSpy('exit callback') baseContent = "There are dozens of us! Dozens! It's as Ann as the nose on Plain's face. Can you believe that the only reason the club is going under is because it's in a terrifying neighborhood? She calls it a Mayonegg. Waiting for the Emmys. BTW did you know won 6 Emmys and was still canceled early by Fox? COME ON. I'll buy you a hundred George Michaels that you can teach to drive! Never once touched my per diem. I'd go to Craft Service, get some raw veggies, bacon, Cup-A-Soup…baby, I got a stew goin'" content = (baseContent for _ in [1..200]).join('\n') diff --git a/src/buffered-process.coffee b/src/buffered-process.coffee index 59f2a7e9a..07fcfb664 100644 --- a/src/buffered-process.coffee +++ b/src/buffered-process.coffee @@ -50,7 +50,7 @@ class BufferedProcess options ?= {} @command = command # Related to joyent/node#2318 - if process.platform is 'win32' + if process.platform is 'win32' and not options.shell? # Quote all arguments and escapes inner quotes if args? cmdArgs = args.filter (arg) -> arg? From c6bd9bc8c0c8079044ea5c057ce4adebe3249f68 Mon Sep 17 00:00:00 2001 From: Rahat Ahmed Date: Thu, 28 Apr 2016 15:28:54 -0500 Subject: [PATCH 900/971] Adjustments for @as-cii --- package.json | 1 - src/lines-yardstick.coffee | 67 +++++++++++++++++++++----------------- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 31b8bb722..ffe6d30e0 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "async": "0.2.6", "atom-keymap": "6.3.2", "babel-core": "^5.8.21", - "binary-search-with-index": "^1.3.0", "bootstrap": "^3.3.4", "cached-run-in-this-context": "0.4.1", "clear-cut": "^2.0.1", diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee index f865a039c..4642a53a6 100644 --- a/src/lines-yardstick.coffee +++ b/src/lines-yardstick.coffee @@ -1,6 +1,5 @@ {Point} = require 'text-buffer' {isPairedCharacter} = require './text-utils' -binarySearch = require 'binary-search-with-index' module.exports = class LinesYardstick @@ -20,8 +19,8 @@ class LinesYardstick targetTop = pixelPosition.top targetLeft = pixelPosition.left defaultCharWidth = @model.getDefaultCharWidth() - return Point(0, 0) if targetTop < 0 row = @lineTopIndex.rowForPixelPosition(targetTop) + targetLeft = 0 if targetTop < 0 targetLeft = Infinity if row > @model.getLastScreenRow() row = Math.min(row, @model.getLastScreenRow()) row = Math.max(0, row) @@ -33,26 +32,40 @@ class LinesYardstick lineOffset = lineNode.getBoundingClientRect().left targetLeft += lineOffset - textNodeComparator = (textNode, position) => + textNodeIndex = @_binarySearch(textNodes, (textNode) => {length: textNodeLength} = textNode rangeRect = @clientRectForRange(textNode, 0, textNodeLength) - return -1 if rangeRect.right < position - return 1 if rangeRect.left > position + return -1 if rangeRect.right < targetLeft + return 1 if rangeRect.left > targetLeft return 0 + ) - textNodeIndex = binarySearch(textNodes, targetLeft, textNodeComparator) + textNodeStartColumn = 0 if textNodeIndex >= 0 - textNodeStartColumn = textNodes - .slice(0, textNodeIndex) - .reduce(((totalLength, node) -> totalLength + node.length), 0) - charIndex = @charIndexForScreenPosition(textNodes[textNodeIndex], targetLeft) + textNodeStartColumn += textNodes[i].length for i in [0...textNodeIndex] - return Point(row, textNodeStartColumn + charIndex) + textNode = textNodes[textNodeIndex] + {textContent: textNodeContent} = textNode + rangeRect = null + nextCharIndex = -1 - textNodeStartColumn = textNodes - .reduce(((totalLength, node) -> totalLength + node.length), 0) + characterIndex = @_binarySearch(textNodeContent, (char, charIndex) => + if isPairedCharacter(textNodeContent, charIndex) + nextCharIndex = charIndex + 2 + else + nextCharIndex = charIndex + 1 + rangeRect = @clientRectForRange(textNode, charIndex, nextCharIndex) + return -1 if rangeRect.right < targetLeft + return 1 if rangeRect.left > targetLeft + return 0 + ) + if targetLeft <= ((rangeRect.left + rangeRect.right) / 2) + return Point(row, textNodeStartColumn + characterIndex) + return Point(row, textNodeStartColumn + nextCharIndex) + + textNodeStartColumn += node.length for node in textNodes return Point(row, textNodeStartColumn) pixelPositionForScreenPosition: (screenPosition) -> @@ -104,21 +117,17 @@ class LinesYardstick @rangeForMeasurement.setEnd(textNode, endIndex) @rangeForMeasurement.getClientRects()[0] ? @rangeForMeasurement.getBoundingClientRect() - charIndexForScreenPosition: (textNode, targetLeft) -> - {textContent: textNodeContent} = textNode - rangeRect = null - nextCharIndex = -1 - characterComparator = (char, position, charIndex) => - if isPairedCharacter(textNodeContent, charIndex) - nextCharIndex = charIndex + 2 + _binarySearch: (array, compare) -> + low = 0 + high = array.length - 1 + while low <= high + mid = low + (high - low >> 1) + comparison = compare(array[mid], mid) + if comparison < 0 + low = mid + 1 + else if comparison > 0 + high = mid - 1 else - nextCharIndex = charIndex + 1 - rangeRect = @clientRectForRange(textNode, charIndex, nextCharIndex) - return -1 if rangeRect.right < position - return 1 if rangeRect.left > position - return 0 + return mid - characterIndex = binarySearch(textNodeContent, targetLeft, characterComparator) - if targetLeft <= ((rangeRect.left + rangeRect.right) / 2) - return characterIndex - return nextCharIndex + return -1 From 7d463d9dd02fb60990690b8e32d16b75ea820137 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 28 Apr 2016 15:30:38 -0700 Subject: [PATCH 901/971] :arrow_up: about@1.5.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1802e8bd1..87083ddd7 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "one-light-syntax": "1.2.0", "solarized-dark-syntax": "1.0.2", "solarized-light-syntax": "1.0.2", - "about": "1.5.1", + "about": "1.5.2", "archive-view": "0.61.1", "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.1", From 50e3f1f789d5432153ce41f2eae5752dc95c2abc Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 29 Apr 2016 14:06:05 +0200 Subject: [PATCH 902/971] :arrow_up: text-bufffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index be2613aac..6901d930c 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.0.0-beta7", + "text-buffer": "9.0.0-beta8", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From c5a76d4a7d9b7de9c78a534091a1b775bc5e9c9a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 29 Apr 2016 14:41:34 +0200 Subject: [PATCH 903/971] Persist the entire state on reload This fixes an annoying problem that prevented the state of marker layers from being saved when the window was reloaded either via `Cmd+R` in DevTools or via `Ctrl+Option+Cmd+L` in Atom. The issue was that we were *always* scheduling `saveState` on an idle callback: `window.onbeforeunload`, however, doesn't wait for that event before closing the window, and thus that state was never saved in those situations. The solution is to use idle callbacks only during the critical code path (i.e. on mousedown and keydown), but save it synchronously otherwise. Saving something to IndexedDB is actually asynchronous too, but it seems like Chrome fulfills `put` requests that get executed right during `onbeforeunload`. --- spec/atom-environment-spec.coffee | 29 ++++++++++++++++++++++++----- src/atom-environment.coffee | 31 ++++++++++++++++--------------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 846083b0e..a78190ab7 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -208,12 +208,15 @@ describe "AtomEnvironment", -> waitsForPromise -> atom.loadState().then (state) -> expect(state).toEqual(serializedState) - it "saves state on keydown, mousedown, and when the editor window unloads", -> + it "saves state when the CPU is idle after a keydown or mousedown event", -> spyOn(atom, 'saveState') + idleCallbacks = [] + spyOn(window, 'requestIdleCallback').andCallFake (callback) -> idleCallbacks.push(callback) keydown = new KeyboardEvent('keydown') atom.document.dispatchEvent(keydown) advanceClock atom.saveStateDebounceInterval + idleCallbacks.shift()() expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: true}) @@ -221,17 +224,33 @@ describe "AtomEnvironment", -> mousedown = new MouseEvent('mousedown') atom.document.dispatchEvent(mousedown) advanceClock atom.saveStateDebounceInterval + idleCallbacks.shift()() expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: true}) - atom.saveState.reset() + it "saves state immediately when unloading the editor window, ignoring pending and successive mousedown/keydown events", -> + spyOn(atom, 'saveState') + idleCallbacks = [] + spyOn(window, 'requestIdleCallback').andCallFake (callback) -> idleCallbacks.push(callback) + + mousedown = new MouseEvent('mousedown') + atom.document.dispatchEvent(mousedown) atom.unloadEditorWindow() - mousedown = new MouseEvent('mousedown') - atom.document.dispatchEvent(mousedown) - advanceClock atom.saveStateDebounceInterval expect(atom.saveState).toHaveBeenCalledWith({isUnloading: true}) expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: false}) + atom.saveState.reset() + advanceClock atom.saveStateDebounceInterval + idleCallbacks.shift()() + expect(atom.saveState).not.toHaveBeenCalled() + + atom.saveState.reset() + mousedown = new MouseEvent('mousedown') + atom.document.dispatchEvent(mousedown) + advanceClock atom.saveStateDebounceInterval + idleCallbacks.shift()() + expect(atom.saveState).not.toHaveBeenCalled() + it "serializes the project state with all the options supplied in saveState", -> spyOn(atom.project, 'serialize').andReturn({foo: 42}) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index f50ae9d5b..da3e989f2 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -234,13 +234,14 @@ class AtomEnvironment extends Model checkPortableHomeWritable() attachSaveStateListeners: -> - saveState = => @saveState({isUnloading: false}) unless @unloaded - debouncedSaveState = _.debounce(saveState, @saveStateDebounceInterval) - @document.addEventListener('mousedown', debouncedSaveState, true) - @document.addEventListener('keydown', debouncedSaveState, true) + saveState = _.debounce((=> + window.requestIdleCallback => @saveState({isUnloading: false}) unless @unloaded + ), @saveStateDebounceInterval) + @document.addEventListener('mousedown', saveState, true) + @document.addEventListener('keydown', saveState, true) @disposables.add new Disposable => - @document.removeEventListener('mousedown', debouncedSaveState, true) - @document.removeEventListener('keydown', debouncedSaveState, true) + @document.removeEventListener('mousedown', saveState, true) + @document.removeEventListener('keydown', saveState, true) setConfigSchema: -> @config.setSchema null, {type: 'object', properties: _.clone(require('./config-schema'))} @@ -655,6 +656,7 @@ class AtomEnvironment extends Model # Call this method when establishing a real application window. startEditorWindow: -> + @unloaded = false @loadState().then (state) => @windowDimensions = state?.windowDimensions @displayWindow().then => @@ -842,16 +844,15 @@ class AtomEnvironment extends Model return Promise.resolve() unless @enablePersistence new Promise (resolve, reject) => - window.requestIdleCallback => - return if not @project + return if not @project - state = @serialize(options) - savePromise = - if storageKey = @getStateKey(@project?.getPaths()) - @stateStore.save(storageKey, state) - else - @applicationDelegate.setTemporaryWindowState(state) - savePromise.catch(reject).then(resolve) + state = @serialize(options) + savePromise = + if storageKey = @getStateKey(@project?.getPaths()) + @stateStore.save(storageKey, state) + else + @applicationDelegate.setTemporaryWindowState(state) + savePromise.catch(reject).then(resolve) loadState: -> if @enablePersistence From 7b2f049cbb0a0550f4b9b29d730814ff4e3f18e1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 29 Apr 2016 16:44:12 +0200 Subject: [PATCH 904/971] :art: Refine binary search algorithm --- spec/lines-yardstick-spec.coffee | 4 ++ src/lines-yardstick.coffee | 84 +++++++++++++++----------------- src/text-editor-component.coffee | 2 +- 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/spec/lines-yardstick-spec.coffee b/spec/lines-yardstick-spec.coffee index 2f7233e2f..72609143e 100644 --- a/spec/lines-yardstick-spec.coffee +++ b/spec/lines-yardstick-spec.coffee @@ -170,3 +170,7 @@ describe "LinesYardstick", -> expect(linesYardstick.screenPositionForPixelPosition(top: Infinity, left: Infinity)).toEqual [12, 2] expect(linesYardstick.screenPositionForPixelPosition(top: (editor.getLastScreenRow() + 1) * 14, left: 0)).toEqual [12, 2] expect(linesYardstick.screenPositionForPixelPosition(top: editor.getLastScreenRow() * 14, left: 0)).toEqual [12, 0] + + it "clips negative horizontal pixel positions", -> + expect(linesYardstick.screenPositionForPixelPosition(top: 0, left: -10)).toEqual [0, 0] + expect(linesYardstick.screenPositionForPixelPosition(top: 1 * 14, left: -10)).toEqual [1, 0] diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee index 4642a53a6..39e16a8e0 100644 --- a/src/lines-yardstick.coffee +++ b/src/lines-yardstick.coffee @@ -20,7 +20,7 @@ class LinesYardstick targetLeft = pixelPosition.left defaultCharWidth = @model.getDefaultCharWidth() row = @lineTopIndex.rowForPixelPosition(targetTop) - targetLeft = 0 if targetTop < 0 + targetLeft = 0 if targetTop < 0 or targetLeft < 0 targetLeft = Infinity if row > @model.getLastScreenRow() row = Math.min(row, @model.getLastScreenRow()) row = Math.max(0, row) @@ -32,41 +32,52 @@ class LinesYardstick lineOffset = lineNode.getBoundingClientRect().left targetLeft += lineOffset - textNodeIndex = @_binarySearch(textNodes, (textNode) => - {length: textNodeLength} = textNode - rangeRect = @clientRectForRange(textNode, 0, textNodeLength) - return -1 if rangeRect.right < targetLeft - return 1 if rangeRect.left > targetLeft - return 0 - ) - - textNodeStartColumn = 0 - - if textNodeIndex >= 0 - textNodeStartColumn += textNodes[i].length for i in [0...textNodeIndex] + textNodeIndex = -1 + low = 0 + high = textNodes.length - 1 + while low <= high + mid = low + (high - low >> 1) + textNode = textNodes[mid] + rangeRect = @clientRectForRange(textNode, 0, textNode.length) + if targetLeft < rangeRect.left + high = mid - 1 + else if targetLeft > rangeRect.right + low = mid + 1 + else + textNodeIndex = mid + break + if textNodeIndex is -1 + textNodesExtent = 0 + textNodesExtent += textContent.length for {textContent} in textNodes + Point(row, textNodesExtent) + else textNode = textNodes[textNodeIndex] - {textContent: textNodeContent} = textNode - rangeRect = null - nextCharIndex = -1 - - characterIndex = @_binarySearch(textNodeContent, (char, charIndex) => - if isPairedCharacter(textNodeContent, charIndex) + characterIndex = -1 + low = 0 + high = textNode.textContent.length - 1 + while low <= high + charIndex = low + (high - low >> 1) + if isPairedCharacter(textNode.textContent, charIndex) nextCharIndex = charIndex + 2 else nextCharIndex = charIndex + 1 + rangeRect = @clientRectForRange(textNode, charIndex, nextCharIndex) - return -1 if rangeRect.right < targetLeft - return 1 if rangeRect.left > targetLeft - return 0 - ) + if targetLeft < rangeRect.left + high = charIndex - 1 + else if targetLeft > rangeRect.right + low = nextCharIndex + else + if targetLeft <= ((rangeRect.left + rangeRect.right) / 2) + characterIndex = charIndex + else + characterIndex = nextCharIndex + break - if targetLeft <= ((rangeRect.left + rangeRect.right) / 2) - return Point(row, textNodeStartColumn + characterIndex) - return Point(row, textNodeStartColumn + nextCharIndex) - - textNodeStartColumn += node.length for node in textNodes - return Point(row, textNodeStartColumn) + textNodeStartColumn = 0 + textNodeStartColumn += textNodes[i].length for i in [0...textNodeIndex] by 1 + Point(row, textNodeStartColumn + characterIndex) pixelPositionForScreenPosition: (screenPosition) -> targetRow = screenPosition.row @@ -116,18 +127,3 @@ class LinesYardstick @rangeForMeasurement.setStart(textNode, startIndex) @rangeForMeasurement.setEnd(textNode, endIndex) @rangeForMeasurement.getClientRects()[0] ? @rangeForMeasurement.getBoundingClientRect() - - _binarySearch: (array, compare) -> - low = 0 - high = array.length - 1 - while low <= high - mid = low + (high - low >> 1) - comparison = compare(array[mid], mid) - if comparison < 0 - low = mid + 1 - else if comparison > 0 - high = mid - 1 - else - return mid - - return -1 diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 3f9bb2029..8c20055ed 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -947,7 +947,7 @@ class TextEditorComponent screenPositionForMouseEvent: (event, linesClientRect) -> pixelPosition = @pixelPositionForMouseEvent(event, linesClientRect) - @screenPositionForPixelPosition(pixelPosition, true) + @screenPositionForPixelPosition(pixelPosition) pixelPositionForMouseEvent: (event, linesClientRect) -> {clientX, clientY} = event From e2fa948ac8e541ecefe7fcdfac4f87535486a004 Mon Sep 17 00:00:00 2001 From: David Elliott Date: Fri, 29 Apr 2016 08:20:52 -0700 Subject: [PATCH 905/971] :bug: Add support for Mac and Open Folder dialog. --- src/browser/atom-application.coffee | 6 +++--- src/register-default-commands.coffee | 11 ++++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index f39c50ce4..f90afad83 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -255,9 +255,9 @@ class AtomApplication ipcMain.on 'open-command', (event, command, args...) => defaultPath = args[0] if args.length > 0 switch command - when 'application:open' then @promptForPathToOpen('all', getLoadSettings()) + when 'application:open' then @promptForPathToOpen('all', getLoadSettings(), defaultPath) when 'application:open-file' then @promptForPathToOpen('file', getLoadSettings(), defaultPath) - when 'application:open-folder' then @promptForPathToOpen('folder', getLoadSettings()) + when 'application:open-folder' then @promptForPathToOpen('folder', getLoadSettings(), defaultPath) else console.log "Invalid open-command received: " + command ipcMain.on 'window-command', (event, command, args...) -> @@ -688,7 +688,7 @@ class AtomApplication else 'Open' # File dialog defaults to project directory of currently active editor - if path? and type is 'file' + if path? openOptions.defaultPath = path dialog.showOpenDialog(parentWindow, openOptions, callback) diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index b2dc1fd68..74da2786e 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -1,6 +1,7 @@ {ipcRenderer} = require 'electron' module.exports = ({commandRegistry, commandInstaller, config, notificationManager}) -> + commandRegistry.add 'atom-workspace', 'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem() 'pane:show-previous-recently-used-item': -> @getModel().getActivePane().activatePreviousRecentlyUsedItem() @@ -31,11 +32,15 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage 'application:unhide-all-applications': -> ipcRenderer.send('command', 'application:unhide-all-applications') 'application:new-window': -> ipcRenderer.send('command', 'application:new-window') 'application:new-file': -> ipcRenderer.send('command', 'application:new-file') - 'application:open': -> ipcRenderer.send('open-command', 'application:open') - 'application:open-file': -> + 'application:open': -> + defaultPath = atom.workspace.getActiveTextEditor()?.getPath() ? atom.project.getPaths()?[0] + ipcRenderer.send('open-command', 'application:open', defaultPath) + 'application:open-file': -> defaultPath = atom.workspace.getActiveTextEditor()?.getPath() ? atom.project.getPaths()?[0] ipcRenderer.send('open-command', 'application:open-file', defaultPath) - 'application:open-folder': -> ipcRenderer.send('open-command', 'application:open-folder') + 'application:open-folder': -> + defaultPath = atom.workspace.getActiveTextEditor()?.getPath() ? atom.project.getPaths()?[0] + ipcRenderer.send('open-command', 'application:open-folder', defaultPath) 'application:open-dev': -> ipcRenderer.send('command', 'application:open-dev') 'application:open-safe': -> ipcRenderer.send('command', 'application:open-safe') 'application:add-project-folder': -> atom.addProjectFolder() From 6c9d7ecc2884075655e1583129afc99aa75dea47 Mon Sep 17 00:00:00 2001 From: David Elliott Date: Fri, 29 Apr 2016 08:39:14 -0700 Subject: [PATCH 906/971] Remove random space --- src/register-default-commands.coffee | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index 74da2786e..086a55497 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -1,7 +1,6 @@ {ipcRenderer} = require 'electron' module.exports = ({commandRegistry, commandInstaller, config, notificationManager}) -> - commandRegistry.add 'atom-workspace', 'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem() 'pane:show-previous-recently-used-item': -> @getModel().getActivePane().activatePreviousRecentlyUsedItem() @@ -32,13 +31,13 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage 'application:unhide-all-applications': -> ipcRenderer.send('command', 'application:unhide-all-applications') 'application:new-window': -> ipcRenderer.send('command', 'application:new-window') 'application:new-file': -> ipcRenderer.send('command', 'application:new-file') - 'application:open': -> + 'application:open': -> defaultPath = atom.workspace.getActiveTextEditor()?.getPath() ? atom.project.getPaths()?[0] ipcRenderer.send('open-command', 'application:open', defaultPath) - 'application:open-file': -> + 'application:open-file': -> defaultPath = atom.workspace.getActiveTextEditor()?.getPath() ? atom.project.getPaths()?[0] ipcRenderer.send('open-command', 'application:open-file', defaultPath) - 'application:open-folder': -> + 'application:open-folder': -> defaultPath = atom.workspace.getActiveTextEditor()?.getPath() ? atom.project.getPaths()?[0] ipcRenderer.send('open-command', 'application:open-folder', defaultPath) 'application:open-dev': -> ipcRenderer.send('command', 'application:open-dev') From b975c1ffc188a3b3fcee112a7b22a54c76089325 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Fri, 29 Apr 2016 18:15:41 -0400 Subject: [PATCH 907/971] :arrow_up: language-sass@0.48.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 87083ddd7..e19a1c8f8 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "language-python": "0.43.1", "language-ruby": "0.68.5", "language-ruby-on-rails": "0.25.0", - "language-sass": "0.47.0", + "language-sass": "0.48.0", "language-shellscript": "0.22.0", "language-source": "0.9.0", "language-sql": "0.21.0", From 8d9324e234d8a73ff5681cab1129f9f796f6a529 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Fri, 29 Apr 2016 18:30:19 -0400 Subject: [PATCH 908/971] :arrow_up: language-sass@0.49.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e19a1c8f8..8aad494e4 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "language-python": "0.43.1", "language-ruby": "0.68.5", "language-ruby-on-rails": "0.25.0", - "language-sass": "0.48.0", + "language-sass": "0.49.0", "language-shellscript": "0.22.0", "language-source": "0.9.0", "language-sql": "0.21.0", From 539fc4090bbdb1269dfdd56633d8ba7b4365975b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 30 Apr 2016 01:34:49 +0200 Subject: [PATCH 909/971] :arrow_up: bookmarks --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8aad494e4..8142a3e5f 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "autoflow": "0.27.0", "autosave": "0.23.1", "background-tips": "0.26.0", - "bookmarks": "0.39.0", + "bookmarks": "0.41.0", "bracket-matcher": "0.82.0", "command-palette": "0.38.0", "deprecation-cop": "0.54.1", From 334b4c110434d88e1cc0e4bef2e9fc43fb330170 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 30 Apr 2016 11:51:54 +0200 Subject: [PATCH 910/971] Overshoot to the end of the text node when the position cannot be found ...because the only possible scenario when a logical position in a text node cannot be found is when the requested pixel position is exactly at the end of the node. --- spec/lines-yardstick-spec.coffee | 7 ++++--- src/lines-yardstick.coffee | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/lines-yardstick-spec.coffee b/spec/lines-yardstick-spec.coffee index 72609143e..0cbb6e312 100644 --- a/spec/lines-yardstick-spec.coffee +++ b/spec/lines-yardstick-spec.coffee @@ -155,9 +155,10 @@ describe "LinesYardstick", -> expect(linesYardstick.screenPositionForPixelPosition({top: 28, left: 100})).toEqual([2, 14]) expect(linesYardstick.screenPositionForPixelPosition({top: 32, left: 24.3})).toEqual([2, 3]) expect(linesYardstick.screenPositionForPixelPosition({top: 46, left: 66.5})).toEqual([3, 9]) - expect(linesYardstick.screenPositionForPixelPosition({top: 80, left: 99.9})).toEqual([5, 14]) - expect(linesYardstick.screenPositionForPixelPosition({top: 80, left: 224.2365234375})).toEqual([5, 29]) - expect(linesYardstick.screenPositionForPixelPosition({top: 80, left: 225})).toEqual([5, 30]) + expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 99.9})).toEqual([5, 14]) + expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 224.2365234375})).toEqual([5, 29]) + expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 225})).toEqual([5, 30]) + expect(linesYardstick.screenPositionForPixelPosition({top: 84, left: 247.1})).toEqual([6, 33]) it "clips pixel positions above buffer start", -> expect(linesYardstick.screenPositionForPixelPosition(top: -Infinity, left: -Infinity)).toEqual [0, 0] diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee index 39e16a8e0..af12242a3 100644 --- a/src/lines-yardstick.coffee +++ b/src/lines-yardstick.coffee @@ -75,6 +75,7 @@ class LinesYardstick characterIndex = nextCharIndex break + characterIndex = textNode.textContent.length if characterIndex is -1 textNodeStartColumn = 0 textNodeStartColumn += textNodes[i].length for i in [0...textNodeIndex] by 1 Point(row, textNodeStartColumn + characterIndex) From 4f5efe98ffb049b982de1ce506b882d880439c21 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sun, 1 May 2016 11:05:14 +0200 Subject: [PATCH 911/971] Overshoot to the nearest character when text nodes are not contiguous --- spec/lines-yardstick-spec.coffee | 28 +++++++++++++++ src/lines-yardstick.coffee | 58 +++++++++++++++----------------- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/spec/lines-yardstick-spec.coffee b/spec/lines-yardstick-spec.coffee index 0cbb6e312..bb0294b54 100644 --- a/spec/lines-yardstick-spec.coffee +++ b/spec/lines-yardstick-spec.coffee @@ -160,6 +160,34 @@ describe "LinesYardstick", -> expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 225})).toEqual([5, 30]) expect(linesYardstick.screenPositionForPixelPosition({top: 84, left: 247.1})).toEqual([6, 33]) + it "overshoots to the nearest character when text nodes are not spatially contiguous", -> + atom.styles.addStyleSheet """ + * { + font-size: 12px; + font-family: monospace; + } + """ + + buildLineNode = (screenRow) -> + lineNode = document.createElement("div") + lineNode.style.whiteSpace = "pre" + lineNode.innerHTML = 'foobar' + jasmine.attachToDOM(lineNode) + createdLineNodes.push(lineNode) + lineNode + editor.setText("foobar") + + expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 7})).toEqual([0, 1]) + expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 14})).toEqual([0, 2]) + expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 21})).toEqual([0, 3]) + expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 30})).toEqual([0, 3]) + expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 50})).toEqual([0, 3]) + expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 62})).toEqual([0, 3]) + expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 69})).toEqual([0, 4]) + expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 76})).toEqual([0, 5]) + expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 100})).toEqual([0, 6]) + expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 200})).toEqual([0, 6]) + it "clips pixel positions above buffer start", -> expect(linesYardstick.screenPositionForPixelPosition(top: -Infinity, left: -Infinity)).toEqual [0, 0] expect(linesYardstick.screenPositionForPixelPosition(top: -Infinity, left: Infinity)).toEqual [0, 0] diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee index af12242a3..cfc954cf2 100644 --- a/src/lines-yardstick.coffee +++ b/src/lines-yardstick.coffee @@ -32,7 +32,7 @@ class LinesYardstick lineOffset = lineNode.getBoundingClientRect().left targetLeft += lineOffset - textNodeIndex = -1 + textNodeIndex = 0 low = 0 high = textNodes.length - 1 while low <= high @@ -41,44 +41,42 @@ class LinesYardstick rangeRect = @clientRectForRange(textNode, 0, textNode.length) if targetLeft < rangeRect.left high = mid - 1 + textNodeIndex = Math.max(0, mid - 1) else if targetLeft > rangeRect.right low = mid + 1 + textNodeIndex = Math.min(textNodes.length - 1, mid + 1) else textNodeIndex = mid break - if textNodeIndex is -1 - textNodesExtent = 0 - textNodesExtent += textContent.length for {textContent} in textNodes - Point(row, textNodesExtent) - else - textNode = textNodes[textNodeIndex] - characterIndex = -1 - low = 0 - high = textNode.textContent.length - 1 - while low <= high - charIndex = low + (high - low >> 1) - if isPairedCharacter(textNode.textContent, charIndex) - nextCharIndex = charIndex + 2 - else - nextCharIndex = charIndex + 1 + textNode = textNodes[textNodeIndex] + characterIndex = 0 + low = 0 + high = textNode.textContent.length - 1 + while low <= high + charIndex = low + (high - low >> 1) + if isPairedCharacter(textNode.textContent, charIndex) + nextCharIndex = charIndex + 2 + else + nextCharIndex = charIndex + 1 - rangeRect = @clientRectForRange(textNode, charIndex, nextCharIndex) - if targetLeft < rangeRect.left - high = charIndex - 1 - else if targetLeft > rangeRect.right - low = nextCharIndex + rangeRect = @clientRectForRange(textNode, charIndex, nextCharIndex) + if targetLeft < rangeRect.left + high = charIndex - 1 + characterIndex = Math.max(0, charIndex - 1) + else if targetLeft > rangeRect.right + low = nextCharIndex + characterIndex = Math.min(textNode.textContent.length, nextCharIndex) + else + if targetLeft <= ((rangeRect.left + rangeRect.right) / 2) + characterIndex = charIndex else - if targetLeft <= ((rangeRect.left + rangeRect.right) / 2) - characterIndex = charIndex - else - characterIndex = nextCharIndex - break + characterIndex = nextCharIndex + break - characterIndex = textNode.textContent.length if characterIndex is -1 - textNodeStartColumn = 0 - textNodeStartColumn += textNodes[i].length for i in [0...textNodeIndex] by 1 - Point(row, textNodeStartColumn + characterIndex) + textNodeStartColumn = 0 + textNodeStartColumn += textNodes[i].length for i in [0...textNodeIndex] by 1 + Point(row, textNodeStartColumn + characterIndex) pixelPositionForScreenPosition: (screenPosition) -> targetRow = screenPosition.row From 1c694df03b5c8a9852433c0d3ea464a79e0107c7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sun, 1 May 2016 11:42:28 +0200 Subject: [PATCH 912/971] Don't show indent guides for mini editors --- spec/text-editor-spec.coffee | 14 ++++++++++++++ src/text-editor.coffee | 5 +++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 1298727a9..8affb9b5f 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5967,6 +5967,20 @@ describe "TextEditor", -> atom.config.set('editor.showInvisibles', true) expect(editor.lineTextForScreenRow(0).indexOf(atom.config.get('editor.invisibles.eol'))).toBe(-1) + describe "indent guides", -> + it "shows indent guides when `editor.showIndentGuide` is set to true and the editor is not mini", -> + editor.setText(" foo") + atom.config.set('editor.tabLength', 2) + + atom.config.set('editor.showIndentGuide', false) + expect(editor.tokensForScreenRow(0)).toEqual ['source.js', 'leading-whitespace'] + + atom.config.set('editor.showIndentGuide', true) + expect(editor.tokensForScreenRow(0)).toEqual ['source.js', 'leading-whitespace indent-guide'] + + editor.setMini(true) + expect(editor.tokensForScreenRow(0)).toEqual ['source.js', 'leading-whitespace'] + describe "when the editor is constructed with the grammar option set", -> beforeEach -> atom.workspace.destroyActivePane() diff --git a/src/text-editor.coffee b/src/text-editor.coffee index a0d384946..e3902b88c 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -257,7 +257,7 @@ class TextEditor extends Model @displayLayer.reset({ invisibles: @getInvisibles(), softWrapColumn: @getSoftWrapColumn(), - showIndentGuides: @config.get('editor.showIndentGuide', scope: @getRootScopeDescriptor()), + showIndentGuides: not @isMini() and @config.get('editor.showIndentGuide', scope: @getRootScopeDescriptor()), atomicSoftTabs: @config.get('editor.atomicSoftTabs', scope: @getRootScopeDescriptor()), tabLength: @getTabLength(), ratioForCharacter: @ratioForCharacter.bind(this), @@ -589,7 +589,8 @@ class TextEditor extends Model setMini: (mini) -> if mini isnt @mini @mini = mini - @setIgnoreInvisibles(@mini) + @ignoreInvisibles = @mini + @resetDisplayLayer() @emitter.emit 'did-change-mini', @mini @mini From f4a31261d09de94502339e8efad7775a05eb4178 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sun, 1 May 2016 11:48:39 +0200 Subject: [PATCH 913/971] Delete indent guides code from the presenter and the component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …because we're handling that behavior in `TextEditor` and `DisplayLayer` now. --- spec/text-editor-presenter-spec.coffee | 47 -------------------------- src/lines-component.coffee | 4 +-- src/lines-tile-component.coffee | 3 -- src/text-editor-presenter.coffee | 6 ---- 4 files changed, 1 insertion(+), 59 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 9318809d7..8cdc4e61a 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -1143,53 +1143,6 @@ describe "TextEditorPresenter", -> expectStateUpdate presenter, -> presenter.setScrollLeft(-300) expect(getState(presenter).content.scrollLeft).toBe 0 - describe ".indentGuidesVisible", -> - it "is initialized based on the editor.showIndentGuide config setting", -> - presenter = buildPresenter() - expect(getState(presenter).content.indentGuidesVisible).toBe false - - atom.config.set('editor.showIndentGuide', true) - presenter = buildPresenter() - expect(getState(presenter).content.indentGuidesVisible).toBe true - - it "updates when the editor.showIndentGuide config setting changes", -> - presenter = buildPresenter() - expect(getState(presenter).content.indentGuidesVisible).toBe false - - expectStateUpdate presenter, -> atom.config.set('editor.showIndentGuide', true) - expect(getState(presenter).content.indentGuidesVisible).toBe true - - expectStateUpdate presenter, -> atom.config.set('editor.showIndentGuide', false) - expect(getState(presenter).content.indentGuidesVisible).toBe false - - it "updates when the editor's grammar changes", -> - atom.config.set('editor.showIndentGuide', true, scopeSelector: ".source.js") - - presenter = buildPresenter() - expect(getState(presenter).content.indentGuidesVisible).toBe false - - stateUpdated = false - presenter.onDidUpdateState -> stateUpdated = true - - waitsForPromise -> atom.packages.activatePackage('language-javascript') - - runs -> - expect(stateUpdated).toBe true - expect(getState(presenter).content.indentGuidesVisible).toBe true - - expectStateUpdate presenter, -> editor.setGrammar(atom.grammars.selectGrammar('.txt')) - expect(getState(presenter).content.indentGuidesVisible).toBe false - - it "is always false when the editor is mini", -> - atom.config.set('editor.showIndentGuide', true) - editor.setMini(true) - presenter = buildPresenter() - expect(getState(presenter).content.indentGuidesVisible).toBe false - editor.setMini(false) - expect(getState(presenter).content.indentGuidesVisible).toBe true - editor.setMini(true) - expect(getState(presenter).content.indentGuidesVisible).toBe false - describe ".backgroundColor", -> it "is assigned to ::backgroundColor unless the editor is mini", -> presenter = buildPresenter() diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 9a7ce7f44..88645589a 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -43,7 +43,7 @@ class LinesComponent extends TiledComponent @domNode shouldRecreateAllTilesOnUpdate: -> - @oldState.indentGuidesVisible isnt @newState.indentGuidesVisible or @newState.continuousReflow + @newState.continuousReflow beforeUpdateSync: (state) -> if @newState.maxHeight isnt @oldState.maxHeight @@ -70,8 +70,6 @@ class LinesComponent extends TiledComponent @cursorsComponent.updateSync(state) - @oldState.indentGuidesVisible = @newState.indentGuidesVisible - buildComponentForTile: (id) -> new LinesTileComponent({id, @presenter, @domElementPool, @assert, @grammars}) buildEmptyState: -> diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee index 3d82d9d9f..6844f21de 100644 --- a/src/lines-tile-component.coffee +++ b/src/lines-tile-component.coffee @@ -68,13 +68,10 @@ class LinesTileComponent @oldTileState.top = @newTileState.top @oldTileState.left = @newTileState.left - @removeLineNodes() unless @oldState.indentGuidesVisible is @newState.indentGuidesVisible @updateLineNodes() @highlightsComponent.updateSync(@newTileState) - @oldState.indentGuidesVisible = @newState.indentGuidesVisible - removeLineNodes: -> @removeLineNode(id) for id of @oldTileState.lines return diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 85c85c655..01a0293f6 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -175,7 +175,6 @@ class TextEditorPresenter @scrollPastEnd = @config.get('editor.scrollPastEnd', configParams) @showLineNumbers = @config.get('editor.showLineNumbers', configParams) - @showIndentGuide = @config.get('editor.showIndentGuide', configParams) if @configDisposables? @configDisposables?.dispose() @@ -184,10 +183,6 @@ class TextEditorPresenter @configDisposables = new CompositeDisposable @disposables.add(@configDisposables) - @configDisposables.add @config.onDidChange 'editor.showIndentGuide', configParams, ({newValue}) => - @showIndentGuide = newValue - - @emitDidUpdateState() @configDisposables.add @config.onDidChange 'editor.scrollPastEnd', configParams, ({newValue}) => @scrollPastEnd = newValue @updateScrollHeight() @@ -295,7 +290,6 @@ class TextEditorPresenter @state.content.width = Math.max(@contentWidth + @verticalScrollbarWidth, @contentFrameWidth) @state.content.scrollWidth = @scrollWidth @state.content.scrollLeft = @scrollLeft - @state.content.indentGuidesVisible = not @model.isMini() and @showIndentGuide @state.content.backgroundColor = if @model.isMini() then null else @backgroundColor @state.content.placeholderText = if @model.isEmpty() then @model.getPlaceholderText() else null From 57bf8f797bf15cf25189acb709c4a78d9b2597fc Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Sun, 1 May 2016 10:32:21 -0700 Subject: [PATCH 914/971] :arrow_up: encoding-selector@0.22.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 535294933..af906359f 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "command-palette": "0.38.0", "deprecation-cop": "0.54.1", "dev-live-reload": "0.47.0", - "encoding-selector": "0.21.0", + "encoding-selector": "0.22.0", "exception-reporting": "0.38.1", "fuzzy-finder": "1.0.5", "git-diff": "1.0.1", From 73748ef768f8691c0f22a9d7a846370de4ec4dfb Mon Sep 17 00:00:00 2001 From: Willem Van Lint Date: Sun, 17 Apr 2016 17:06:22 -0700 Subject: [PATCH 915/971] Order listeners by reverse registration order --- spec/command-registry-spec.coffee | 7 +++++++ src/command-registry.coffee | 11 +++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/spec/command-registry-spec.coffee b/spec/command-registry-spec.coffee index ecdd42fd6..aaf044b1d 100644 --- a/spec/command-registry-spec.coffee +++ b/spec/command-registry-spec.coffee @@ -74,6 +74,13 @@ describe "CommandRegistry", -> grandchild.dispatchEvent(new CustomEvent('command', bubbles: true)) expect(calls).toEqual ['.foo.bar', '.bar', '.foo'] + it "orders inline listeners by reverse registration order", -> + calls = [] + registry.add child, 'command', -> calls.push('child1') + registry.add child, 'command', -> calls.push('child2') + child.dispatchEvent(new CustomEvent('command', bubbles: true)) + expect(calls).toEqual ['child2', 'child1'] + it "stops bubbling through ancestors when .stopPropagation() is called on the event", -> calls = [] diff --git a/src/command-registry.coffee b/src/command-registry.coffee index db2cf498d..955a1b540 100644 --- a/src/command-registry.coffee +++ b/src/command-registry.coffee @@ -244,11 +244,14 @@ class CommandRegistry (@selectorBasedListenersByCommandName[event.type] ? []) .filter (listener) -> currentTarget.webkitMatchesSelector(listener.selector) .sort (a, b) -> a.compare(b) - listeners = listeners.concat(selectorBasedListeners) + listeners = selectorBasedListeners.concat(listeners) matched = true if listeners.length > 0 - for listener in listeners + # Call inline listeners first in reverse registration order, + # and selector-based listeners by specificity and reverse + # registration order. + for listener in listeners by -1 break if immediatePropagationStopped listener.callback.call(currentTarget, dispatchedEvent) @@ -271,8 +274,8 @@ class SelectorBasedListener @sequenceNumber = SequenceCount++ compare: (other) -> - other.specificity - @specificity or - other.sequenceNumber - @sequenceNumber + @specificity - other.specificity or + @sequenceNumber - other.sequenceNumber class InlineListener constructor: (@callback) -> From 671334993f5984646f47755e783272440c6bb6ab Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sun, 1 May 2016 22:16:36 -0700 Subject: [PATCH 916/971] :arrow_up: apm@1.10.0 --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index 4ddb4dabe..d4fcc851a 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "1.9.3" + "atom-package-manager": "1.10.0" } } From 874be7d242ce6e88d32f63d5e7455ac9c20c9331 Mon Sep 17 00:00:00 2001 From: Timothy Cyrus Date: Mon, 2 May 2016 10:47:49 -0400 Subject: [PATCH 917/971] Update package.json Update `marked` to fix sanitization issue on david-dm --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index af906359f..04ee2034c 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "key-path-helpers": "^0.4.0", "less-cache": "0.23", "line-top-index": "0.2.0", - "marked": "^0.3.4", + "marked": "^0.3.5", "normalize-package-data": "^2.0.0", "nslog": "^3", "ohnogit": "0.0.11", From 9adc822822d42877151bda2fe1f3051205d76b98 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 2 May 2016 11:32:29 -0400 Subject: [PATCH 918/971] Mark text editors as being registered. --- src/text-editor-registry.coffee | 14 +++++++++++++- src/text-editor.coffee | 3 ++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/text-editor-registry.coffee b/src/text-editor-registry.coffee index 8a17335d4..e31630fee 100644 --- a/src/text-editor-registry.coffee +++ b/src/text-editor-registry.coffee @@ -26,8 +26,20 @@ class TextEditorRegistry # editor is destroyed. add: (editor) -> @editors.add(editor) + editor.registered = true + @emitter.emit 'did-add-editor', editor - new Disposable => @editors.delete(editor) + new Disposable => @remove(editor) + + # Remove a `TextEditor`. + # + # * `editor` The editor to remove. + # + # Returns a {Boolean} indicating whether the editor was successfully removed. + remove: (editor) -> + removed = @editors.delete(editor) + editor.registered = false + removed # Invoke the given callback with all the current and future registered # `TextEditors`. diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 9976e5906..bdc283d8f 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -62,6 +62,7 @@ class TextEditor extends Model selectionFlashDuration: 500 gutterContainer: null editorElement: null + registered: false Object.defineProperty @prototype, "element", get: -> @getElement() @@ -157,7 +158,7 @@ class TextEditor extends Model firstVisibleScreenColumn: @getFirstVisibleScreenColumn() displayBuffer: @displayBuffer.serialize() selectionsMarkerLayerId: @selectionsMarkerLayer.id - registered: atom.textEditors.editors.has this + registered: @registered subscribeToBuffer: -> @buffer.retain() From d325e02def5e7819030e09eab09fcb62e5d701b9 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 2 May 2016 11:32:38 -0400 Subject: [PATCH 919/971] Test it. --- spec/text-editor-registry-spec.coffee | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spec/text-editor-registry-spec.coffee b/spec/text-editor-registry-spec.coffee index 04665bef2..80f29f897 100644 --- a/spec/text-editor-registry-spec.coffee +++ b/spec/text-editor-registry-spec.coffee @@ -10,6 +10,7 @@ describe "TextEditorRegistry", -> it "gets added to the list of registered editors", -> editor = {} registry.add(editor) + expect(editor.registered).toBe true expect(registry.editors.size).toBe 1 expect(registry.editors.has(editor)).toBe(true) @@ -19,6 +20,16 @@ describe "TextEditorRegistry", -> expect(registry.editors.size).toBe 1 disposable.dispose() expect(registry.editors.size).toBe 0 + expect(editor.registered).toBe false + + it "can be removed", -> + editor = {} + registry.add(editor) + expect(registry.editors.size).toBe 1 + success = registry.remove(editor) + expect(success).toBe true + expect(registry.editors.size).toBe 0 + expect(editor.registered).toBe false describe "when the registry is observed", -> it "calls the callback for current and future editors until unsubscribed", -> From dd24e3b22304b495625140f74be9d221238074ab Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Mon, 2 May 2016 13:19:19 -0700 Subject: [PATCH 920/971] :arrow_up: tabs@0.93.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index af906359f..8b4d137ff 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.2.6", "styleguide": "0.45.2", "symbols-view": "0.112.0", - "tabs": "0.93.1", + "tabs": "0.93.2", "timecop": "0.33.1", "tree-view": "0.206.2", "update-package-dependencies": "0.10.0", From 7542a401fff99d6ca93e7b1254bd8fc4d3672559 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 2 May 2016 15:37:36 -0700 Subject: [PATCH 921/971] BufferedProcess whole-string spec now cross-platform --- spec/buffered-process-spec.coffee | 29 +++++++++++++---------------- spec/fixtures/lorem.txt | 3 +++ spec/fixtures/shebang | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) create mode 100644 spec/fixtures/lorem.txt diff --git a/spec/buffered-process-spec.coffee b/spec/buffered-process-spec.coffee index ec01b40d7..40b96e3e1 100644 --- a/spec/buffered-process-spec.coffee +++ b/spec/buffered-process-spec.coffee @@ -1,5 +1,7 @@ ChildProcess = require 'child_process' path = require 'path' +fs = require 'fs-plus' +platform = require './spec-helper-platform' BufferedProcess = require '../src/buffered-process' describe "BufferedProcess", -> @@ -110,30 +112,25 @@ describe "BufferedProcess", -> expect(stderr).toContain 'apm - Atom Package Manager' expect(stdout).toEqual '' - it "calls the specified stdout callback only with whole lines", -> - # WINSPEC: Fails because command is too long, /bin/echo is *nix only, odd newlining and quoting + it "calls the specified stdout callback with whole lines", -> exitCallback = jasmine.createSpy('exit callback') - baseContent = "There are dozens of us! Dozens! It's as Ann as the nose on Plain's face. Can you believe that the only reason the club is going under is because it's in a terrifying neighborhood? She calls it a Mayonegg. Waiting for the Emmys. BTW did you know won 6 Emmys and was still canceled early by Fox? COME ON. I'll buy you a hundred George Michaels that you can teach to drive! Never once touched my per diem. I'd go to Craft Service, get some raw veggies, bacon, Cup-A-Soup…baby, I got a stew goin'" - content = (baseContent for _ in [1..200]).join('\n') + loremPath = require.resolve("./fixtures/lorem.txt") + content = fs.readFileSync(loremPath).toString() + baseContent = content.split('\n') stdout = '' - endLength = 10 - outputAlwaysEndsWithStew = true + allLinesEndWithNewline = true process = new BufferedProcess - command: '/bin/echo' - args: [content] + command: if platform.isWindows() then 'type' else 'cat' + args: [loremPath] options: {} stdout: (lines) -> + endsWithNewline = (lines.charAt lines.length - 1) is '\n' + if not endsWithNewline then allLinesEndWithNewline = false stdout += lines - - end = baseContent.substr(baseContent.length - endLength, endLength) - lineEndsWithStew = lines.substr(lines.length - endLength, endLength) is end - expect(lineEndsWithStew).toBeTrue - - outputAlwaysEndsWithStew = outputAlwaysEndsWithStew and lineEndsWithStew exit: exitCallback waitsFor -> exitCallback.callCount is 1 runs -> - expect(outputAlwaysEndsWithStew).toBeTrue - expect(stdout).toBe content += '\n' + expect(allLinesEndWithNewline).toBeTrue + expect(stdout).toBe content diff --git a/spec/fixtures/lorem.txt b/spec/fixtures/lorem.txt new file mode 100644 index 000000000..be8db8ab8 --- /dev/null +++ b/spec/fixtures/lorem.txt @@ -0,0 +1,3 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ultricies nulla id nibh aliquam, vitae euismod ipsum scelerisque. Vestibulum vulputate facilisis nisi, eu rhoncus turpis pretium ut. Curabitur facilisis urna in diam efficitur, vel maximus tellus consectetur. Suspendisse pulvinar felis sed metus tristique, a posuere dui suscipit. Ut vehicula, tellus ac blandit consequat, libero dui hendrerit elit, non pretium metus odio sed dolor. Vivamus quis volutpat ipsum. In convallis magna nec nunc tristique malesuada. Sed sed hendrerit lacus. Etiam arcu dui, consequat vel neque vitae, iaculis egestas justo. Donec lacinia odio nulla, condimentum porta erat accumsan at. Nunc vulputate nulla vel nunc fermentum egestas. +Duis ultricies libero elit, nec facilisis mi rhoncus ornare. Aliquam aliquet libero vitae arcu porttitor mattis. Vestibulum ultricies consectetur arcu, non gravida magna eleifend vel. Phasellus varius mattis ultricies. Vestibulum placerat lacus non consectetur fringilla. Duis congue, arcu iaculis vehicula hendrerit, purus odio faucibus ipsum, et fermentum massa tellus euismod nulla. Vivamus pellentesque blandit massa, sit amet hendrerit turpis congue eu. Suspendisse diam dui, vestibulum nec semper varius, maximus eu nunc. Vivamus facilisis pulvinar viverra. Praesent luctus lectus id est porttitor volutpat. Suspendisse est augue, mattis a tincidunt id, condimentum in turpis. Curabitur at erat commodo orci interdum tincidunt. Sed sodales elit odio, a placerat ipsum luctus nec. Sed maximus, justo ut pharetra pellentesque, orci mi faucibus enim, quis viverra arcu dui sed nisl. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Praesent quis velit libero. +Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Phasellus a rutrum tortor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce bibendum odio et neque vestibulum rutrum. Vestibulum commodo, nibh non sodales lobortis, dui ex consectetur leo, a finibus libero lectus ac diam. Etiam dui nunc, bibendum a tempor vel, vestibulum lacinia neque. Mauris consectetur odio sit amet maximus pretium. Sed rutrum nunc at ante ullamcorper fermentum. Proin at quam a mauris pellentesque viverra. Nunc pretium pulvinar ipsum. Vestibulum eu nibh ut ex gravida tempus. Praesent ut elit ut ligula tristique dapibus ut sit amet leo. Proin non molestie erat. diff --git a/spec/fixtures/shebang b/spec/fixtures/shebang index f15429b13..f343f6833 100644 --- a/spec/fixtures/shebang +++ b/spec/fixtures/shebang @@ -1,3 +1,3 @@ #!/usr/bin/ruby -puts "America – fuck yeah!" \ No newline at end of file +puts "Atom fixture test" From 36bcb542a81bb75d3ea1653976115c57c618384f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 3 May 2016 11:50:02 +0200 Subject: [PATCH 922/971] Don't move down a line if it's the last buffer row --- spec/text-editor-spec.coffee | 7 +++++++ src/text-editor.coffee | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 8affb9b5f..c8156cdd0 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2875,6 +2875,13 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(1)).toBe "1" expect(editor.lineTextForBufferRow(2)).toBe "2" + describe "when the line is the last buffer row", -> + it "doesn't move it", -> + editor.setText("abc\ndef") + editor.setCursorBufferPosition([1, 0]) + editor.moveLineDown() + expect(editor.getText()).toBe("abc\ndef") + describe ".insertText(text)", -> describe "when there is a single selection", -> beforeEach -> diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e3902b88c..b778491d1 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1068,7 +1068,7 @@ class TextEditor extends Model # Delete lines spanned by selection and insert them on the following correct buffer row lines = @buffer.getTextInRange(linesRange) - if linesRange.end.row is @buffer.getLastRow() + if followingRow - 1 is @buffer.getLastRow() lines = "\n#{lines}" @buffer.insert([followingRow, 0], lines) From 6b4c082bbf3162735cd7f904be8ba057171f92d8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 3 May 2016 13:06:16 +0200 Subject: [PATCH 923/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f3fb2c2b5..6d390354d 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.0.0-beta8", + "text-buffer": "9.0.0-beta9", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 004bb6122cd28fd89f1f867160d44404664c0646 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 3 May 2016 13:24:28 +0200 Subject: [PATCH 924/971] :art: :racehorse: --- src/tokenized-buffer-iterator.coffee | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/tokenized-buffer-iterator.coffee b/src/tokenized-buffer-iterator.coffee index ad1834f09..780156e42 100644 --- a/src/tokenized-buffer-iterator.coffee +++ b/src/tokenized-buffer-iterator.coffee @@ -1,5 +1,4 @@ {Point} = require 'text-buffer' -{isEqual} = require 'underscore-plus' module.exports = class TokenizedBufferIterator @@ -58,7 +57,7 @@ class TokenizedBufferIterator @moveToNextLine() @openTags = @currentLineOpenTags.map (id) => @grammarRegistry.scopeForId(id) @shouldMoveToNextLine = false - else if @hasNextLine() and not isEqual(@containingTags, @nextLineOpeningScopes()) + else if @nextLineHasMismatchedContainingTags() @closeTags = @containingTags.slice().reverse() @containingTags = [] @shouldMoveToNextLine = true @@ -97,12 +96,16 @@ class TokenizedBufferIterator Section: Private Methods ### - hasNextLine: -> - @tokenizedBuffer.tokenizedLineForRow(@position.row + 1)? + nextLineHasMismatchedContainingTags: -> + if line = @tokenizedBuffer.tokenizedLineForRow(@position.row + 1) + return true if line.openScopes.length isnt @containingTags.length - nextLineOpeningScopes: -> - line = @tokenizedBuffer.tokenizedLineForRow(@position.row + 1) - line.openScopes.map (id) => @grammarRegistry.scopeForId(id) + for i in [0...@containingTags.length] by 1 + if @containingTags[i] isnt @grammarRegistry.scopeForId(line.openScopes[i]) + return true + false + else + false moveToNextLine: -> @position = Point(@position.row + 1, 0) From d6d47c422c07b71421471720f7d96b52890feeb0 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Tue, 3 May 2016 09:20:47 -0700 Subject: [PATCH 925/971] :arrow_up: settings-view@0.236.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d007eefa8..5a752cd6d 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "notifications": "0.63.2", "open-on-github": "1.1.0", "package-generator": "1.0.0", - "settings-view": "0.235.1", + "settings-view": "0.236.0", "snippets": "1.0.2", "spell-check": "0.67.1", "status-bar": "1.2.6", From 48703864c87c231eb1eb3675254f7a44a9c57d9c Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 3 May 2016 15:22:13 -0700 Subject: [PATCH 926/971] Switch to process.platform, stop clobbering process var and move windows block --- spec/buffered-process-spec.coffee | 69 +++++++++++++++---------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/spec/buffered-process-spec.coffee b/spec/buffered-process-spec.coffee index 40b96e3e1..84d8b0440 100644 --- a/spec/buffered-process-spec.coffee +++ b/spec/buffered-process-spec.coffee @@ -1,7 +1,6 @@ ChildProcess = require 'child_process' path = require 'path' fs = require 'fs-plus' -platform = require './spec-helper-platform' BufferedProcess = require '../src/buffered-process' describe "BufferedProcess", -> @@ -17,13 +16,13 @@ describe "BufferedProcess", -> describe "when there is an error handler specified", -> describe "when an error event is emitted by the process", -> it "calls the error handler and does not throw an exception", -> - process = new BufferedProcess + bufferedProcess = new BufferedProcess command: 'bad-command-nope1' args: ['nothing'] options: {shell: false} errorSpy = jasmine.createSpy().andCallFake (error) -> error.handle() - process.onWillThrowError(errorSpy) + bufferedProcess.onWillThrowError(errorSpy) waitsFor -> errorSpy.callCount > 0 @@ -39,13 +38,13 @@ describe "BufferedProcess", -> error.code = 'EAGAIN' throw error - process = new BufferedProcess + bufferedProcess = new BufferedProcess command: 'ls' args: [] options: {} errorSpy = jasmine.createSpy().andCallFake (error) -> error.handle() - process.onWillThrowError(errorSpy) + bufferedProcess.onWillThrowError(errorSpy) waitsFor -> errorSpy.callCount > 0 @@ -56,7 +55,7 @@ describe "BufferedProcess", -> describe "when there is not an error handler specified", -> it "does throw an exception", -> - process = new BufferedProcess + new BufferedProcess command: 'bad-command-nope2' args: ['nothing'] options: {shell: false} @@ -68,37 +67,11 @@ describe "BufferedProcess", -> expect(window.onerror.mostRecentCall.args[0]).toContain 'Failed to spawn command `bad-command-nope2`' expect(window.onerror.mostRecentCall.args[4].name).toBe 'BufferedProcessError' - describe "on Windows", -> - originalPlatform = null - - beforeEach -> - # Prevent any commands from actually running and affecting the host - originalSpawn = ChildProcess.spawn - spyOn(ChildProcess, 'spawn') - originalPlatform = process.platform - Object.defineProperty process, 'platform', value: 'win32' - - afterEach -> - Object.defineProperty process, 'platform', value: originalPlatform - - describe "when the explorer command is spawned on Windows", -> - it "doesn't quote arguments of the form /root,C...", -> - new BufferedProcess({command: 'explorer.exe', args: ['/root,C:\\foo']}) - expect(ChildProcess.spawn.argsForCall[0][1][3]).toBe '"explorer.exe /root,C:\\foo"' - - it "spawns the command using a cmd.exe wrapper", -> - new BufferedProcess({command: 'dir'}) - expect(path.basename(ChildProcess.spawn.argsForCall[0][0])).toBe 'cmd.exe' - expect(ChildProcess.spawn.argsForCall[0][1][0]).toBe '/s' - expect(ChildProcess.spawn.argsForCall[0][1][1]).toBe '/d' - expect(ChildProcess.spawn.argsForCall[0][1][2]).toBe '/c' - expect(ChildProcess.spawn.argsForCall[0][1][3]).toBe '"dir"' - it "calls the specified stdout, stderr, and exit callbacks", -> stdout = '' stderr = '' exitCallback = jasmine.createSpy('exit callback') - process = new BufferedProcess + new BufferedProcess command: atom.packages.getApmPath() args: ['-h'] options: {} @@ -119,8 +92,8 @@ describe "BufferedProcess", -> baseContent = content.split('\n') stdout = '' allLinesEndWithNewline = true - process = new BufferedProcess - command: if platform.isWindows() then 'type' else 'cat' + new BufferedProcess + command: if process.platform is 'win32' then 'type' else 'cat' args: [loremPath] options: {} stdout: (lines) -> @@ -134,3 +107,29 @@ describe "BufferedProcess", -> runs -> expect(allLinesEndWithNewline).toBeTrue expect(stdout).toBe content + + describe "on Windows", -> + originalPlatform = null + + beforeEach -> + # Prevent any commands from actually running and affecting the host + originalSpawn = ChildProcess.spawn + spyOn(ChildProcess, 'spawn') + originalPlatform = process.platform + Object.defineProperty process, 'platform', value: 'win32' + + afterEach -> + Object.defineProperty process, 'platform', value: originalPlatform + + describe "when the explorer command is spawned on Windows", -> + it "doesn't quote arguments of the form /root,C...", -> + new BufferedProcess({command: 'explorer.exe', args: ['/root,C:\\foo']}) + expect(ChildProcess.spawn.argsForCall[0][1][3]).toBe '"explorer.exe /root,C:\\foo"' + + it "spawns the command using a cmd.exe wrapper when options.shell is undefined", -> + new BufferedProcess({command: 'dir'}) + expect(path.basename(ChildProcess.spawn.argsForCall[0][0])).toBe 'cmd.exe' + expect(ChildProcess.spawn.argsForCall[0][1][0]).toBe '/s' + expect(ChildProcess.spawn.argsForCall[0][1][1]).toBe '/d' + expect(ChildProcess.spawn.argsForCall[0][1][2]).toBe '/c' + expect(ChildProcess.spawn.argsForCall[0][1][3]).toBe '"dir"' From 0e194f1021935deb7acc7237bda43a63382ae5a1 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Tue, 3 May 2016 16:04:04 -0700 Subject: [PATCH 927/971] :arrow_up: line-ending-selector@0.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5a752cd6d..e76dce85e 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "image-view": "0.57.0", "incompatible-packages": "0.26.1", "keybinding-resolver": "0.35.0", - "line-ending-selector": "0.4.1", + "line-ending-selector": "0.5.0", "link": "0.31.1", "markdown-preview": "0.158.0", "metrics": "0.53.1", From c56cbe3ce364ac0e2d6ad4c4818bdbca935889db Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 3 May 2016 16:50:06 -0700 Subject: [PATCH 928/971] :shirt: Change fat arrow to thin arrow for linter warning --- src/workspace.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index f75f00bc6..5e9de93dd 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -1092,7 +1092,7 @@ class Workspace extends Model if editor.getPath() checkoutHead = => @project.repositoryForDirectory(new Directory(editor.getDirectoryPath())) - .then (repository) => + .then (repository) -> repository?.async.checkoutHeadForEditor(editor) if @config.get('editor.confirmCheckoutHeadRevision') From d29e96e358970ccea1aa0bd5305ba61b04559afb Mon Sep 17 00:00:00 2001 From: Wliu Date: Tue, 3 May 2016 20:47:02 -0400 Subject: [PATCH 929/971] :arrow_up: language-sass@0.50.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e76dce85e..29d08589b 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "language-python": "0.43.1", "language-ruby": "0.68.5", "language-ruby-on-rails": "0.25.0", - "language-sass": "0.49.0", + "language-sass": "0.50.0", "language-shellscript": "0.22.0", "language-source": "0.9.0", "language-sql": "0.21.0", From b9dbb81bd5404294bb33b2b30ffe1b464a2d2eac Mon Sep 17 00:00:00 2001 From: simurai Date: Wed, 4 May 2016 12:05:04 +0900 Subject: [PATCH 930/971] Revert ":arrow_up: language-sass@0.50.0" This reverts commit d29e96e358970ccea1aa0bd5305ba61b04559afb. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 29d08589b..e76dce85e 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "language-python": "0.43.1", "language-ruby": "0.68.5", "language-ruby-on-rails": "0.25.0", - "language-sass": "0.50.0", + "language-sass": "0.49.0", "language-shellscript": "0.22.0", "language-source": "0.9.0", "language-sql": "0.21.0", From 6c6f8fc34154f2900e003c5956465119c66618c8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 4 May 2016 10:15:02 +0200 Subject: [PATCH 931/971] :arrow_up: autocomplete-plus --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e76dce85e..7f7a5dfe8 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.1", "autocomplete-html": "0.7.2", - "autocomplete-plus": "2.30.0", + "autocomplete-plus": "2.31.0", "autocomplete-snippets": "1.10.0", "autoflow": "0.27.0", "autosave": "0.23.1", From 512f1fb540700ed455e4aca3821bf6e0ee6ed6f9 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Wed, 4 May 2016 11:56:17 +0200 Subject: [PATCH 932/971] :arrow_up: symbols-view@0.113.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7f7a5dfe8..f5a432a2d 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "spell-check": "0.67.1", "status-bar": "1.2.6", "styleguide": "0.45.2", - "symbols-view": "0.112.0", + "symbols-view": "0.113.0", "tabs": "0.93.2", "timecop": "0.33.1", "tree-view": "0.206.2", From 89d6b1fccec244ca1223f8aa091611c1e99f33b4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 4 May 2016 18:57:07 +0200 Subject: [PATCH 933/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 814ba0033..4a8591ae5 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.0.0-beta9", + "text-buffer": "9.0.0", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From c929bb687421241ed3df1d7b68288a5086074ceb Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 4 May 2016 22:18:34 -0400 Subject: [PATCH 934/971] :arrow_up: language-xml@0.34.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4a8591ae5..0a3c8e1bc 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "language-text": "0.7.1", "language-todo": "0.27.0", "language-toml": "0.18.0", - "language-xml": "0.34.5", + "language-xml": "0.34.6", "language-yaml": "0.26.0" }, "private": true, From d06da3f47046643292c0a451be5ca51c67436a1b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 5 May 2016 09:10:36 +0200 Subject: [PATCH 935/971] Reset display layer when editor.atomicSoftTabs changes --- spec/text-editor-spec.coffee | 24 ++++++++++++++++++++++++ src/text-editor.coffee | 1 + 2 files changed, 25 insertions(+) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 8f4231323..be24baa01 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5268,6 +5268,30 @@ describe "TextEditor", -> coffeeEditor.insertText("\n") expect(coffeeEditor.lineTextForBufferRow(2)).toBe "" + describe "editor.atomicSoftTabs", -> + it "skips tab-length runs of leading whitespace when moving the cursor", -> + atom.config.set('editor.tabLength', 4) + + atom.config.set('editor.atomicSoftTabs', true) + editor.setCursorScreenPosition([2, 3]) + expect(editor.getCursorScreenPosition()).toEqual [2, 4] + + atom.config.set('editor.atomicSoftTabs', false) + editor.setCursorScreenPosition([2, 3]) + expect(editor.getCursorScreenPosition()).toEqual [2, 3] + + atom.config.set('editor.atomicSoftTabs', true) + editor.setCursorScreenPosition([2, 3]) + expect(editor.getCursorScreenPosition()).toEqual [2, 4] + + atom.config.set('editor.atomicSoftTabs', false, scopeSelector: '.source.foo') + editor.setCursorScreenPosition([2, 3]) + expect(editor.getCursorScreenPosition()).toEqual [2, 4] + + atom.config.set('editor.atomicSoftTabs', false, scopeSelector: '.source.js') + editor.setCursorScreenPosition([2, 3]) + expect(editor.getCursorScreenPosition()).toEqual [2, 3] + describe ".destroy()", -> it "destroys marker layers associated with the text editor", -> selectionsMarkerLayerId = editor.selectionsMarkerLayer.id diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 9607f3437..54f65ceda 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -230,6 +230,7 @@ class TextEditor extends Model @scopedConfigSubscriptions = subscriptions = new CompositeDisposable scopeDescriptor = @getRootScopeDescriptor() + subscriptions.add @config.onDidChange 'editor.atomicSoftTabs', scope: scopeDescriptor, @resetDisplayLayer.bind(this) subscriptions.add @config.onDidChange 'editor.tabLength', scope: scopeDescriptor, @resetDisplayLayer.bind(this) subscriptions.add @config.onDidChange 'editor.invisibles', scope: scopeDescriptor, @resetDisplayLayer.bind(this) subscriptions.add @config.onDidChange 'editor.showInvisibles', scope: scopeDescriptor, @resetDisplayLayer.bind(this) From 87c75ddfc4b4e9bf4767617f5ea97f6c93637f2c Mon Sep 17 00:00:00 2001 From: fscherwi Date: Thu, 5 May 2016 18:40:57 +0200 Subject: [PATCH 936/971] :arrow_up: pathwatcher@6.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e12f7785b..b739d9dc5 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "^5", - "pathwatcher": "~6.3", + "pathwatcher": "~6.5", "property-accessors": "^1.1.3", "random-words": "0.0.1", "resolve": "^1.1.6", From 32fcadd82264ae3218844f82ee14e263b0f02a40 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Mon, 1 Feb 2016 21:40:43 -0800 Subject: [PATCH 937/971] :memo: Update to v1.4 of Contributor Covenent Code of Conduct --- CODE_OF_CONDUCT.md | 48 +++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 444ce0b4c..c7d7eeb14 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,24 +1,46 @@ -# Contributor Code of Conduct +# Contributor Covenant Code of Conduct -As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. +## Our Pledge -We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members Examples of unacceptable behavior by participants include: -- The use of sexualized language or imagery -- Personal attacks -- Trolling or insulting/derogatory comments -- Public or private harassment -- Publishing other's private information, such as physical or electronic addresses, without explicit permission -- Other unethical or unprofessional conduct +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. -By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. +## Scope -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at [atom@github.com](mailto:atom@github.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident. +## Enforcement -This Code of Conduct is adapted from the Contributor Covenant, version 1.3.0, available from http://contributor-covenant.org/version/1/3/0/ +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [atom@github.com](mailto:atom@github.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ From 8d59f39a2aa93e446c14d1841636160fae68a432 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 5 May 2016 14:09:53 -0700 Subject: [PATCH 938/971] :arrow_up: notifications@0.64.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0a3c8e1bc..31500e4fb 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "link": "0.31.1", "markdown-preview": "0.158.0", "metrics": "0.53.1", - "notifications": "0.63.2", + "notifications": "0.64.0", "open-on-github": "1.1.0", "package-generator": "1.0.0", "settings-view": "0.236.0", From c31ade978cf3f8035b51ebc368fee0a344462865 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 5 May 2016 18:39:31 -0700 Subject: [PATCH 939/971] :arrow_up: bracket-matcher@0.82.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f63c34b21..5f5a62c6d 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "autosave": "0.23.1", "background-tips": "0.26.0", "bookmarks": "0.41.0", - "bracket-matcher": "0.82.0", + "bracket-matcher": "0.82.1", "command-palette": "0.38.0", "deprecation-cop": "0.54.1", "dev-live-reload": "0.47.0", From e3790b8f20f96e17c2ad5cad3ed53b41e4f23377 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 6 May 2016 14:11:47 +0200 Subject: [PATCH 940/971] :memo: Do not mention persistent option for mark...Range methods --- src/text-editor.coffee | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 54f65ceda..051fd76b8 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1702,8 +1702,6 @@ class TextEditor extends Model # operations, but uses more time and memory. (default: false) # * `reversed` (optional) {Boolean} Creates the marker in a reversed # orientation. (default: false) - # * `persistent` (optional) {Boolean} Whether to include this marker when - # serializing the buffer. (default: true) # * `invalidate` (optional) {String} Determines the rules by which changes # to the buffer *invalidate* the marker. (default: 'overlap') It can be # any of the following strategies, in order of fragility: @@ -1737,8 +1735,6 @@ class TextEditor extends Model # operations, but uses more time and memory. (default: false) # * `reversed` (optional) {Boolean} Creates the marker in a reversed # orientation. (default: false) - # * `persistent` (optional) {Boolean} Whether to include this marker when - # serializing the buffer. (default: true) # * `invalidate` (optional) {String} Determines the rules by which changes # to the buffer *invalidate* the marker. (default: 'overlap') It can be # any of the following strategies, in order of fragility: From aab92787efeca711022d7a228cea2d0d7b794773 Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 6 May 2016 13:19:53 -0400 Subject: [PATCH 941/971] :arrow_up: fuzzy-finder@1.0.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5f5a62c6d..b1d0e3ba9 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "dev-live-reload": "0.47.0", "encoding-selector": "0.22.0", "exception-reporting": "0.38.1", - "fuzzy-finder": "1.0.5", + "fuzzy-finder": "1.0.6", "git-diff": "1.0.1", "find-and-replace": "0.198.0", "go-to-line": "0.30.0", From 5907a190ebce46390b7d1771636f352313fc18ed Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sat, 7 May 2016 17:23:19 -0400 Subject: [PATCH 942/971] :arrow_up: language-c@0.51.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b1d0e3ba9..482e8d61c 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "welcome": "0.34.0", "whitespace": "0.32.2", "wrap-guide": "0.38.1", - "language-c": "0.51.4", + "language-c": "0.51.5", "language-clojure": "0.20.0", "language-coffee-script": "0.47.0", "language-csharp": "0.12.1", From 72905f1895405db0b5bc893e8df5f7fad078cc63 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sat, 7 May 2016 17:23:42 -0400 Subject: [PATCH 943/971] :arrow_up: language-perl@0.35.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 482e8d61c..82686b0ec 100644 --- a/package.json +++ b/package.json @@ -134,7 +134,7 @@ "language-make": "0.22.0", "language-mustache": "0.13.0", "language-objective-c": "0.15.1", - "language-perl": "0.34.0", + "language-perl": "0.35.0", "language-php": "0.37.0", "language-property-list": "0.8.0", "language-python": "0.43.1", From 7ec70514895cc9ef0187b20c65e0426a1e959dfb Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sat, 7 May 2016 17:24:06 -0400 Subject: [PATCH 944/971] :arrow_up: language-python@0.43.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 82686b0ec..02a9775c7 100644 --- a/package.json +++ b/package.json @@ -137,7 +137,7 @@ "language-perl": "0.35.0", "language-php": "0.37.0", "language-property-list": "0.8.0", - "language-python": "0.43.1", + "language-python": "0.43.2", "language-ruby": "0.68.5", "language-ruby-on-rails": "0.25.0", "language-sass": "0.49.0", From edcd33841719f10ae332af1c720e8825415bffdf Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sat, 7 May 2016 17:24:28 -0400 Subject: [PATCH 945/971] :arrow_up: language-shellscript@0.22.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 02a9775c7..45c757e12 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "language-ruby": "0.68.5", "language-ruby-on-rails": "0.25.0", "language-sass": "0.49.0", - "language-shellscript": "0.22.0", + "language-shellscript": "0.22.1", "language-source": "0.9.0", "language-sql": "0.21.0", "language-text": "0.7.1", From e3c0e12ce70471985621228b43bae3a8983ef030 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 9 May 2016 22:05:26 -0400 Subject: [PATCH 946/971] :arrow_up: language-shellscript@0.22.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 45c757e12..a56d3ad63 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "language-ruby": "0.68.5", "language-ruby-on-rails": "0.25.0", "language-sass": "0.49.0", - "language-shellscript": "0.22.1", + "language-shellscript": "0.22.2", "language-source": "0.9.0", "language-sql": "0.21.0", "language-text": "0.7.1", From ffd0c586bf133b1481dda1885e2cfacb88c9fb57 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 9 May 2016 22:06:28 -0400 Subject: [PATCH 947/971] :arrow_up: language-sass@0.51.1 Closes #11695 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a56d3ad63..9d3fc7f18 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "language-python": "0.43.2", "language-ruby": "0.68.5", "language-ruby-on-rails": "0.25.0", - "language-sass": "0.49.0", + "language-sass": "0.51.1", "language-shellscript": "0.22.2", "language-source": "0.9.0", "language-sql": "0.21.0", From 405f28f726f018a378e5f336abdbcbfd58d6d013 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 10 May 2016 16:32:13 -0700 Subject: [PATCH 948/971] Handle spaces in relative path to atom.sh on bash/win, fixes #11732 --- resources/win/atom.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/win/atom.sh b/resources/win/atom.sh index cd90ff8fb..99dd90bea 100644 --- a/resources/win/atom.sh +++ b/resources/win/atom.sh @@ -1,5 +1,5 @@ #!/bin/sh -pushd $(dirname "$0") > /dev/null +pushd "$(dirname "$0")" > /dev/null ATOMCMD=""$(pwd -W)"/atom.cmd" popd > /dev/null cmd.exe //c "$ATOMCMD" "$@" From a33757c5f7ff198fa4c57baaffe7f995227efa89 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Wed, 11 May 2016 13:57:51 +0200 Subject: [PATCH 949/971] attach msi to release --- build/tasks/publish-build-task.coffee | 2 +- spec/fixtures/sample.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/tasks/publish-build-task.coffee b/build/tasks/publish-build-task.coffee index 19061db02..0a18c9c23 100644 --- a/build/tasks/publish-build-task.coffee +++ b/build/tasks/publish-build-task.coffee @@ -74,7 +74,7 @@ getAssets = -> ] when 'win32' assets = [{assetName: 'atom-windows.zip', sourcePath: appName}] - for squirrelAsset in ['AtomSetup.exe', 'RELEASES', "atom-#{version}-full.nupkg", "atom-#{version}-delta.nupkg"] + for squirrelAsset in ['AtomSetup.exe', 'AtomSetup.msi', 'RELEASES', "atom-#{version}-full.nupkg", "atom-#{version}-delta.nupkg"] cp path.join(buildDir, 'installer', squirrelAsset), path.join(buildDir, squirrelAsset) assets.push({assetName: squirrelAsset, sourcePath: assetName}) assets diff --git a/spec/fixtures/sample.txt b/spec/fixtures/sample.txt index 9701a96c5..0a2b6a502 100644 --- a/spec/fixtures/sample.txt +++ b/spec/fixtures/sample.txt @@ -1 +1 @@ -Some textSome textSome text. +Some textSome textSome textSome text. From aebacfcfc64d89457ac14e3f5a17f299563ab185 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Wed, 11 May 2016 17:05:43 +0200 Subject: [PATCH 950/971] not sure how that got in there --- spec/fixtures/sample.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/fixtures/sample.txt b/spec/fixtures/sample.txt index 0a2b6a502..9701a96c5 100644 --- a/spec/fixtures/sample.txt +++ b/spec/fixtures/sample.txt @@ -1 +1 @@ -Some textSome textSome textSome text. +Some textSome textSome text. From a81ffbf3b2fef408445c9f52c6a0843863a32432 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Thu, 12 May 2016 17:21:33 +0200 Subject: [PATCH 951/971] :arrow_up:fuzzy-finder@1.1.0 https://github.com/atom/fuzzy-finder/pull/204 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9d3fc7f18..69196e059 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "dev-live-reload": "0.47.0", "encoding-selector": "0.22.0", "exception-reporting": "0.38.1", - "fuzzy-finder": "1.0.6", + "fuzzy-finder": "1.1.0", "git-diff": "1.0.1", "find-and-replace": "0.198.0", "go-to-line": "0.30.0", From 0cdbb0928fb66ad1e83d0391a0b705b9c856937f Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 12 May 2016 10:42:24 -0700 Subject: [PATCH 952/971] :arrow_up: fuzzy-finder@1.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 69196e059..08b8c527c 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "dev-live-reload": "0.47.0", "encoding-selector": "0.22.0", "exception-reporting": "0.38.1", - "fuzzy-finder": "1.1.0", + "fuzzy-finder": "1.2.0", "git-diff": "1.0.1", "find-and-replace": "0.198.0", "go-to-line": "0.30.0", From ad7e839bfc7ac4faa578961850f9e593feb1a3bc Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 12 May 2016 13:21:46 -0700 Subject: [PATCH 953/971] :arrow_up: settings-view@0.236.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 08b8c527c..75d2abf90 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "notifications": "0.64.0", "open-on-github": "1.1.0", "package-generator": "1.0.0", - "settings-view": "0.236.0", + "settings-view": "0.236.1", "snippets": "1.0.2", "spell-check": "0.67.1", "status-bar": "1.2.6", From e1a200a7b4434910793e0a6760822d501705beb0 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 12 May 2016 13:53:41 -0700 Subject: [PATCH 954/971] :arrow_up: autocomplete-snippets@1.11.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 75d2abf90..c8ab489ab 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "autocomplete-css": "0.11.1", "autocomplete-html": "0.7.2", "autocomplete-plus": "2.31.0", - "autocomplete-snippets": "1.10.0", + "autocomplete-snippets": "1.11.0", "autoflow": "0.27.0", "autosave": "0.23.1", "background-tips": "0.26.0", From b6891b2860708376509189665f22e95c32d42963 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Fri, 13 May 2016 16:57:43 +0200 Subject: [PATCH 955/971] :arrow_up:tabs@0.94.0 https://github.com/atom/tabs/pull/231 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c8ab489ab..83826480e 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.2.6", "styleguide": "0.45.2", "symbols-view": "0.113.0", - "tabs": "0.93.2", + "tabs": "0.94.0", "timecop": "0.33.1", "tree-view": "0.206.2", "update-package-dependencies": "0.10.0", From 8beb4c238b1e0b30e4a25b1628a07ea073c6b650 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 13 May 2016 11:37:49 -0700 Subject: [PATCH 956/971] :arrow_up: tabs@0.95.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 83826480e..d512e3ddf 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.2.6", "styleguide": "0.45.2", "symbols-view": "0.113.0", - "tabs": "0.94.0", + "tabs": "0.95.0", "timecop": "0.33.1", "tree-view": "0.206.2", "update-package-dependencies": "0.10.0", From a5c6f2746a32634a2cb850f873ed9b3b0f06645b Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Fri, 13 May 2016 13:50:09 -0700 Subject: [PATCH 957/971] Add Devtron to the Developer Tools window --- package.json | 1 + src/atom-environment.coffee | 1 + 2 files changed, 2 insertions(+) diff --git a/package.json b/package.json index d512e3ddf..526857445 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "clear-cut": "^2.0.1", "coffee-script": "1.8.0", "color": "^0.7.3", + "devtron": "1.1.0", "event-kit": "^1.5.0", "find-parent-dir": "^0.3.0", "first-mate": "^5.1.1", diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index da3e989f2..1d60872df 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -790,6 +790,7 @@ class AtomEnvironment extends Model # Returns a {Promise} that resolves when the DevTools have been opened or # closed. toggleDevTools: -> + require("devtron").install() @applicationDelegate.toggleWindowDevTools() # Extended: Execute code in dev tools. From 16fb2da8953344b95531dbf1e92c8a752b234261 Mon Sep 17 00:00:00 2001 From: Daniel Mountford Date: Fri, 13 May 2016 22:30:33 +0100 Subject: [PATCH 958/971] Update link to solarized-dark-syntax The existing link to https://github.com/atom/solarized-dark 404s --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0aa249c18..4b5e4db9a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,7 +62,7 @@ Here's a list of the big ones: * [git-diff](https://github.com/atom/git-diff) - Git change indicators shown in the editor's gutter. * [language-javascript](https://github.com/atom/language-javascript) - all bundled languages are packages too, and each one has a separate package `language-[name]`. Use these for feedback on syntax highlighting issues that only appear for a specific language. * [one-dark-ui](https://github.com/atom/one-dark-ui) - the default UI styling for anything but the text editor. UI theme packages (i.e. packages with a `-ui` suffix) provide only styling and it's possible that a bundled package is responsible for a UI issue. There are other other bundled UI themes, such as [one-light-ui](https://github.com/atom/one-light-ui). -* [one-dark-syntax](https://github.com/atom/one-dark-syntax) - the default syntax highlighting styles applied for all languages. There are other other bundled syntax themes, such as [solarized-dark](https://github.com/atom/solarized-dark). You should use these packages for reporting issues that appear in many languages, but disappear if you change to another syntax theme. +* [one-dark-syntax](https://github.com/atom/one-dark-syntax) - the default syntax highlighting styles applied for all languages. There are other other bundled syntax themes, such as [solarized-dark](https://github.com/atom/solarized-dark-syntax). You should use these packages for reporting issues that appear in many languages, but disappear if you change to another syntax theme. * [apm](https://github.com/atom/apm) - the `apm` command line tool (Atom Package Manager). You should use this repository for any contributions related to the `apm` tool and to publishing packages. * [atom.io](https://github.com/atom/atom.io) - the repository for feedback on the [Atom.io website](https://atom.io) and the [Atom.io package API](https://github.com/atom/atom/blob/master/docs/apm-rest-api.md) used by [apm](https://github.com/atom/apm). From f9039a35f6ba14e6ba334b86a3a839a92eb27d2e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 14 May 2016 17:28:54 +0200 Subject: [PATCH 959/971] Refactor `isRowVisible` to `isRowRendered` There's a distinction to make between rendered and visible rows, and we were using the former as if it was the latter. In particular, if a tile is visible, all its rows get rendered on screen, even though they might not necessarily be visible by the user. --- src/text-editor-component.coffee | 8 ++++---- src/text-editor-presenter.coffee | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 8c20055ed..048033a20 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -491,7 +491,7 @@ class TextEditorComponent screenPosition = Point.fromObject(screenPosition) screenPosition = @editor.clipScreenPosition(screenPosition) if clip - unless @presenter.isRowVisible(screenPosition.row) + unless @presenter.isRowRendered(screenPosition.row) @presenter.setScreenRowsToMeasure([screenPosition.row]) unless @linesComponent.lineNodeForScreenRow(screenPosition.row)? @@ -503,7 +503,7 @@ class TextEditorComponent screenPositionForPixelPosition: (pixelPosition) -> row = @linesYardstick.measuredRowForPixelPosition(pixelPosition) - if row? and not @presenter.isRowVisible(row) + if row? and not @presenter.isRowRendered(row) @presenter.setScreenRowsToMeasure([row]) @updateSyncPreMeasurement() @@ -513,9 +513,9 @@ class TextEditorComponent pixelRectForScreenRange: (screenRange) -> rowsToMeasure = [] - unless @presenter.isRowVisible(screenRange.start.row) + unless @presenter.isRowRendered(screenRange.start.row) rowsToMeasure.push(screenRange.start.row) - unless @presenter.isRowVisible(screenRange.end.row) + unless @presenter.isRowRendered(screenRange.end.row) rowsToMeasure.push(screenRange.end.row) if rowsToMeasure.length > 0 diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 01a0293f6..1f48bc4fa 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1550,8 +1550,8 @@ class TextEditorPresenter getVisibleRowRange: -> [@startRow, @endRow] - isRowVisible: (row) -> - @startRow <= row < @endRow + isRowRendered: (row) -> + @getStartTileRow() <= row < @constrainRow(@getEndTileRow() + @tileSize) isOpenTagCode: (tagCode) -> @displayLayer.isOpenTagCode(tagCode) From 78cc959280cc43c1ec45150e9dd9625c271f8c50 Mon Sep 17 00:00:00 2001 From: Daniel Mountford Date: Sun, 15 May 2016 13:08:27 +0100 Subject: [PATCH 960/971] Update link text to match URL --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4b5e4db9a..ff48740b2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,7 +62,7 @@ Here's a list of the big ones: * [git-diff](https://github.com/atom/git-diff) - Git change indicators shown in the editor's gutter. * [language-javascript](https://github.com/atom/language-javascript) - all bundled languages are packages too, and each one has a separate package `language-[name]`. Use these for feedback on syntax highlighting issues that only appear for a specific language. * [one-dark-ui](https://github.com/atom/one-dark-ui) - the default UI styling for anything but the text editor. UI theme packages (i.e. packages with a `-ui` suffix) provide only styling and it's possible that a bundled package is responsible for a UI issue. There are other other bundled UI themes, such as [one-light-ui](https://github.com/atom/one-light-ui). -* [one-dark-syntax](https://github.com/atom/one-dark-syntax) - the default syntax highlighting styles applied for all languages. There are other other bundled syntax themes, such as [solarized-dark](https://github.com/atom/solarized-dark-syntax). You should use these packages for reporting issues that appear in many languages, but disappear if you change to another syntax theme. +* [one-dark-syntax](https://github.com/atom/one-dark-syntax) - the default syntax highlighting styles applied for all languages. There are other other bundled syntax themes, such as [solarized-dark-syntax](https://github.com/atom/solarized-dark-syntax). You should use these packages for reporting issues that appear in many languages, but disappear if you change to another syntax theme. * [apm](https://github.com/atom/apm) - the `apm` command line tool (Atom Package Manager). You should use this repository for any contributions related to the `apm` tool and to publishing packages. * [atom.io](https://github.com/atom/atom.io) - the repository for feedback on the [Atom.io website](https://atom.io) and the [Atom.io package API](https://github.com/atom/atom/blob/master/docs/apm-rest-api.md) used by [apm](https://github.com/atom/apm). From f36e148accd332f1bdff549af66ac3e1b1921c23 Mon Sep 17 00:00:00 2001 From: Robert Stanfield Date: Sun, 15 May 2016 20:45:45 -0400 Subject: [PATCH 961/971] When using the event editor:copy-selection, it pastes at the cursor rather than the beginning of the line (see #11542) --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 051fd76b8..740f9e5f3 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -3007,7 +3007,7 @@ class TextEditor extends Model maintainClipboard = false for selection in @getSelectionsOrderedByBufferPosition() if not selection.isEmpty() - selection.copy(maintainClipboard, true) + selection.copy(maintainClipboard, false) maintainClipboard = true return From b5b324875e000809e06574ce8f8b0d17f04d0bc3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 16 May 2016 10:41:55 +0200 Subject: [PATCH 962/971] Don't render line-numbers corresponding to lines that need measuring Rendering those line numbers in the gutter isn't useful, and it puts unneeded pressure to the DOM. In the process of changing `updateLineNumbersState`, we have also refactored it to stop relying on row ranges being contiguous. This allows that code path to be: 1. Less error-prone, because we were trying to access rows that weren't actually rendered, thus potentially throwing errors when measuring non-contiguous screen rows that weren't visible. 2. Tighter, because we can just iterate over each screen row and ask for its soft-wrap descriptor. --- spec/text-editor-presenter-spec.coffee | 12 ++++--- src/text-editor-presenter.coffee | 47 +++++++------------------- 2 files changed, 20 insertions(+), 39 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 8cdc4e61a..5a91c74f5 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -2805,16 +2805,20 @@ describe "TextEditorPresenter", -> editor.setSoftWrapped(true) editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(51) - presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineHeight: 10, tileSize: 2) + presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineHeight: 10, tileSize: 3) + presenter.setScreenRowsToMeasure([9, 11]) - expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined() - expectValues lineNumberStateForScreenRow(presenter, 2), {screenRow: 2, bufferRow: 2, softWrapped: false} + expect(lineNumberStateForScreenRow(presenter, 2)).toBeUndefined() expectValues lineNumberStateForScreenRow(presenter, 3), {screenRow: 3, bufferRow: 3, softWrapped: false} expectValues lineNumberStateForScreenRow(presenter, 4), {screenRow: 4, bufferRow: 3, softWrapped: true} expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 4, softWrapped: false} expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 7, softWrapped: false} expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 8, softWrapped: false} - expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined() + expectValues lineNumberStateForScreenRow(presenter, 8), {screenRow: 8, bufferRow: 8, softWrapped: true} + expect(lineNumberStateForScreenRow(presenter, 9)).toBeUndefined() + expect(lineNumberStateForScreenRow(presenter, 10)).toBeUndefined() + expect(lineNumberStateForScreenRow(presenter, 11)).toBeUndefined() + expect(lineNumberStateForScreenRow(presenter, 12)).toBeUndefined() it "updates when the editor's content changes", -> editor.foldBufferRow(4) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 1f48bc4fa..95c46e5bf 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -601,42 +601,19 @@ class TextEditorPresenter tileState.lineNumbers ?= {} visibleLineNumberIds = {} - startRow = screenRows[screenRows.length - 1] - endRow = Math.min(screenRows[0] + 1, @model.getScreenLineCount()) + for screenRow in screenRows when @isRowRendered(screenRow) + lineId = @linesByScreenRow.get(screenRow).id + {bufferRow, softWrappedAtStart: softWrapped} = @displayLayer.softWrapDescriptorForScreenRow(screenRow) + foldable = not softWrapped and @model.isFoldableAtBufferRow(bufferRow) + decorationClasses = @lineNumberDecorationClassesForRow(screenRow) + blockDecorationsBeforeCurrentScreenRowHeight = @lineTopIndex.pixelPositionAfterBlocksForRow(screenRow) - @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRow) + blockDecorationsHeight = blockDecorationsBeforeCurrentScreenRowHeight + if screenRow % @tileSize isnt 0 + blockDecorationsAfterPreviousScreenRowHeight = @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRow) - @lineHeight - @lineTopIndex.pixelPositionAfterBlocksForRow(screenRow - 1) + blockDecorationsHeight += blockDecorationsAfterPreviousScreenRowHeight - if startRow > 0 - rowBeforeStartRow = startRow - 1 - lastBufferRow = @model.bufferRowForScreenRow(rowBeforeStartRow) - else - lastBufferRow = null - - if endRow > startRow - bufferRows = @model.bufferRowsForScreenRows(startRow, endRow - 1) - previousBufferRow = -1 - foldable = false - for bufferRow, i in bufferRows - # don't compute foldability more than once per buffer row - if previousBufferRow isnt bufferRow - foldable = @model.isFoldableAtBufferRow(bufferRow) - previousBufferRow = bufferRow - - if bufferRow is lastBufferRow - softWrapped = true - else - lastBufferRow = bufferRow - softWrapped = false - - screenRow = startRow + i - lineId = @linesByScreenRow.get(screenRow).id - decorationClasses = @lineNumberDecorationClassesForRow(screenRow) - blockDecorationsBeforeCurrentScreenRowHeight = @lineTopIndex.pixelPositionAfterBlocksForRow(screenRow) - @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRow) - blockDecorationsHeight = blockDecorationsBeforeCurrentScreenRowHeight - if screenRow % @tileSize isnt 0 - blockDecorationsAfterPreviousScreenRowHeight = @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRow) - @lineHeight - @lineTopIndex.pixelPositionAfterBlocksForRow(screenRow - 1) - blockDecorationsHeight += blockDecorationsAfterPreviousScreenRowHeight - - tileState.lineNumbers[lineId] = {screenRow, bufferRow, softWrapped, decorationClasses, foldable, blockDecorationsHeight} - visibleLineNumberIds[lineId] = true + tileState.lineNumbers[lineId] = {screenRow, bufferRow, softWrapped, decorationClasses, foldable, blockDecorationsHeight} + visibleLineNumberIds[lineId] = true for id of tileState.lineNumbers delete tileState.lineNumbers[id] unless visibleLineNumberIds[id] From c13346a455043f317b5a85e77543419f168ed9fd Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 16 May 2016 11:23:54 +0200 Subject: [PATCH 963/971] Show folded marker in the first screen row of a soft-wrapped buffer row --- spec/text-editor-presenter-spec.coffee | 29 +++++++++++++++++++------- src/text-editor-presenter.coffee | 4 +--- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 8cdc4e61a..af0260b95 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -3069,15 +3069,28 @@ describe "TextEditorPresenter", -> expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'a' expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toContain 'a' - it "applies the 'folded' decoration only to the initial screen row of a soft-wrapped buffer row", -> - editor.setSoftWrapped(true) - editor.setDefaultCharWidth(1) - editor.setEditorWidthInChars(15) - editor.foldBufferRange([[0, 20], [0, 22]]) - presenter = buildPresenter(explicitHeight: 35, scrollTop: 0, tileSize: 2) + describe "when a fold spans a single soft-wrapped buffer row", -> + it "applies the 'folded' decoration only to its initial screen row", -> + editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) + editor.setEditorWidthInChars(20) + editor.foldBufferRange([[0, 20], [0, 22]]) + editor.foldBufferRange([[0, 10], [0, 14]]) + presenter = buildPresenter(explicitHeight: 35, scrollTop: 0, tileSize: 2) - expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'folded' - expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull() + expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain('folded') + expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull() + + describe "when a fold is at the end of a soft-wrapped buffer row", -> + it "applies the 'folded' decoration only to its initial screen row", -> + editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) + editor.setEditorWidthInChars(25) + editor.foldBufferRow(1) + presenter = buildPresenter(explicitHeight: 35, scrollTop: 0, tileSize: 2) + + expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toContain('folded') + expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull() describe ".foldable", -> it "marks line numbers at the start of a foldable region as foldable", -> diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 01a0293f6..1a7723128 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1153,13 +1153,11 @@ class TextEditorPresenter if rangeIsReversed headScreenPosition = screenRange.start - headBufferPosition = bufferRange.start else headScreenPosition = screenRange.end - headBufferPosition = bufferRange.end if properties.class is 'folded' and Decoration.isType(properties, 'line-number') - screenRow = @model.screenRowForBufferRow(headBufferPosition.row) + screenRow = @model.screenRowForBufferRow(bufferRange.start.row) @lineNumberDecorationsByScreenRow[screenRow] ?= {} @lineNumberDecorationsByScreenRow[screenRow][decorationId] = properties else From a18369586096191b4ceb22db123d4dbc9fe154d7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 16 May 2016 13:36:46 +0200 Subject: [PATCH 964/971] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d512e3ddf..80aa134e3 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.0.0", + "text-buffer": "9.1.0", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 655b332a967bf6f302364ba29c1dac3d458303a8 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Mon, 16 May 2016 11:36:35 -0700 Subject: [PATCH 965/971] :arrow_up: settings-view@0.237.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4400d8f15..ef05af5d3 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "notifications": "0.64.0", "open-on-github": "1.1.0", "package-generator": "1.0.0", - "settings-view": "0.236.1", + "settings-view": "0.237.0", "snippets": "1.0.2", "spell-check": "0.67.1", "status-bar": "1.2.6", From 7bcd7f58f94a91c8940258a88a717bfb03d50177 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 17 May 2016 09:25:42 +0200 Subject: [PATCH 966/971] :arrow_up: tree-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ef05af5d3..36321e369 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,7 @@ "symbols-view": "0.113.0", "tabs": "0.95.0", "timecop": "0.33.1", - "tree-view": "0.206.2", + "tree-view": "0.207.0", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From 69072e5616a4214365497db4ee9439830d4ee74b Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 17 May 2016 18:37:09 -0400 Subject: [PATCH 967/971] :arrow_up: language-c@0.52.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 36321e369..044870da7 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "welcome": "0.34.0", "whitespace": "0.32.2", "wrap-guide": "0.38.1", - "language-c": "0.51.5", + "language-c": "0.52.0", "language-clojure": "0.20.0", "language-coffee-script": "0.47.0", "language-csharp": "0.12.1", From be41991e8d7007bdec55a2062d62384fe89b0600 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 17 May 2016 18:38:11 -0400 Subject: [PATCH 968/971] :arrow_up: language-java@0.19.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 044870da7..bbed2d987 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "language-go": "0.42.0", "language-html": "0.44.1", "language-hyperlink": "0.16.0", - "language-java": "0.18.0", + "language-java": "0.19.0", "language-javascript": "0.110.0", "language-json": "0.18.0", "language-less": "0.29.3", From d41443170347953e4d0b6cb41eb84d9e4f59a38c Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 17 May 2016 18:39:05 -0400 Subject: [PATCH 969/971] :arrow_up: language-python@0.44.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bbed2d987..762d35d0c 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ "language-perl": "0.35.0", "language-php": "0.37.0", "language-property-list": "0.8.0", - "language-python": "0.43.2", + "language-python": "0.44.0", "language-ruby": "0.68.5", "language-ruby-on-rails": "0.25.0", "language-sass": "0.51.1", From 1c4032626dadd82c5c68bc5ad23959ec8f3d5974 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 17 May 2016 18:39:58 -0400 Subject: [PATCH 970/971] :arrow_up: language-sass@0.52.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 762d35d0c..1a25f0208 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "language-python": "0.44.0", "language-ruby": "0.68.5", "language-ruby-on-rails": "0.25.0", - "language-sass": "0.51.1", + "language-sass": "0.52.0", "language-shellscript": "0.22.2", "language-source": "0.9.0", "language-sql": "0.21.0", From 346d998c761631c9d0ef2f8097fb971b72a7f979 Mon Sep 17 00:00:00 2001 From: simurai Date: Wed, 18 May 2016 08:38:12 +0900 Subject: [PATCH 971/971] :arrow_up: notifications@v0.64.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1a25f0208..596b8c36e 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "link": "0.31.1", "markdown-preview": "0.158.0", "metrics": "0.53.1", - "notifications": "0.64.0", + "notifications": "0.64.1", "open-on-github": "1.1.0", "package-generator": "1.0.0", "settings-view": "0.237.0",