diff --git a/.atom/snippets/coffee.cson b/.atom/snippets/coffee.cson index f960ce1a5..ff978671c 100644 --- a/.atom/snippets/coffee.cson +++ b/.atom/snippets/coffee.cson @@ -35,6 +35,10 @@ "Point array": prefix: "pt" body: "[$1, $2]" + + "Key-value pair": + prefix: ":" + body: '${1:"${2:key}"}: ${3:value}' "Create Jasmine spy": prefix: "pt" body: 'jasmine.createSpy("${1:description}")$2' diff --git a/src/app/anchor-range.coffee b/src/app/anchor-range.coffee index 7adfa1cf2..1f1ad5151 100644 --- a/src/app/anchor-range.coffee +++ b/src/app/anchor-range.coffee @@ -1,4 +1,7 @@ Range = require 'range' +EventEmitter = require 'event-emitter' +Subscriber = require 'subscriber' +_ = require 'underscore' module.exports = class AnchorRange @@ -6,11 +9,14 @@ class AnchorRange end: null buffer: null editSession: null # optional + destroyed: false constructor: (bufferRange, @buffer, @editSession) -> bufferRange = Range.fromObject(bufferRange) @startAnchor = @buffer.addAnchorAtPosition(bufferRange.start, ignoreChangesStartingOnAnchor: true) @endAnchor = @buffer.addAnchorAtPosition(bufferRange.end) + @subscribe @startAnchor, 'destroyed', => @destroy() + @subscribe @endAnchor, 'destroyed', => @destroy() getBufferRange: -> new Range(@startAnchor.getBufferPosition(), @endAnchor.getBufferPosition()) @@ -22,7 +28,14 @@ class AnchorRange @getBufferRange().containsPoint(bufferPosition) destroy: -> + return if @destroyed + @unsubscribe() @startAnchor.destroy() @endAnchor.destroy() @buffer.removeAnchorRange(this) @editSession?.removeAnchorRange(this) + @destroyed = true + @trigger 'destroyed' + +_.extend(AnchorRange.prototype, EventEmitter) +_.extend(AnchorRange.prototype, Subscriber) diff --git a/src/app/anchor.coffee b/src/app/anchor.coffee index 42932c97c..f0b26b10b 100644 --- a/src/app/anchor.coffee +++ b/src/app/anchor.coffee @@ -10,6 +10,7 @@ class Anchor screenPosition: null ignoreChangesStartingOnAnchor: false strong: false + destroyed: false constructor: (@buffer, options = {}) -> { @editSession, @ignoreChangesStartingOnAnchor, @strong } = options @@ -81,8 +82,10 @@ class Anchor @setScreenPosition(screenPosition, bufferChange: options.bufferChange, clip: false, assignBufferPosition: false, autoscroll: options.autoscroll) destroy: -> + return if @destroyed @buffer.removeAnchor(this) @editSession?.removeAnchor(this) + @destroyed = true @trigger 'destroyed' _.extend(Anchor.prototype, EventEmitter) diff --git a/src/packages/snippets/spec/snippets-spec.coffee b/src/packages/snippets/spec/snippets-spec.coffee index 53598e1fe..38a711489 100644 --- a/src/packages/snippets/spec/snippets-spec.coffee +++ b/src/packages/snippets/spec/snippets-spec.coffee @@ -52,12 +52,9 @@ describe "Snippets extension", -> """ - "multi-line placeholders": + "nested tab stops": prefix: "t5" - body: """ - behold ${1:my multi- - line placeholder}. amazing. - """ + body: '${1:"${2:key}"}: ${3:value}' "caused problems with undo": prefix: "t6" @@ -122,6 +119,17 @@ describe "Snippets extension", -> editor.trigger keydownEvent('tab', target: editor[0]) expect(editor.getSelectedBufferRange()).toEqual [[1, 29], [1, 35]] + describe "when tab stops are nested", -> + it "destroys the inner tab stop if the outer tab stop is modified", -> + buffer.setText('') + editor.insertText 't5' + editor.trigger 'snippets:expand' + expect(buffer.lineForRow(0)).toBe '"key": value' + expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 5]] + editor.insertText("foo") + editor.trigger keydownEvent('tab', target: editor[0]) + expect(editor.getSelectedBufferRange()).toEqual [[0, 5], [0, 10]] + describe "when the cursor is moved beyond the bounds of a tab stop", -> it "terminates the snippet", -> editor.setCursorScreenPosition([2, 0]) @@ -182,7 +190,7 @@ describe "Snippets extension", -> editor.trigger keydownEvent('tab', target: editor[0]) expect(buffer.lineForRow(0)).toBe "first line" - describe "when a snippet expansion is undone and redone", -> + ffdescribe "when a snippet expansion is undone and redone", -> it "recreates the snippet's tab stops", -> editor.insertText ' t6\n' editor.setCursorBufferPosition [0, 6] diff --git a/src/packages/snippets/src/snippet-expansion.coffee b/src/packages/snippets/src/snippet-expansion.coffee index b0e80f83d..dd7a5d11f 100644 --- a/src/packages/snippets/src/snippet-expansion.coffee +++ b/src/packages/snippets/src/snippet-expansion.coffee @@ -25,7 +25,9 @@ class SnippetExpansion placeTabStopAnchorRanges: (startPosition, tabStopRanges) -> @tabStopAnchorRanges = tabStopRanges.map ({start, end}) => - @editSession.addAnchorRange([startPosition.add(start), startPosition.add(end)]) + anchorRange = @editSession.addAnchorRange([startPosition.add(start), startPosition.add(end)]) + anchorRange.on 'destroyed', => _.remove(@tabStopAnchorRanges, anchorRange) + anchorRange @setTabStopIndex(0) indentSubsequentLines: (startRow, snippet) -> @@ -68,7 +70,7 @@ class SnippetExpansion _.intersection(@tabStopAnchorRanges, @editSession.anchorRangesForBufferPosition(bufferPosition)) destroy: -> - anchorRange.destroy() for anchorRange in @tabStopAnchorRanges + anchorRange.destroy() for anchorRange in new Array(@tabStopAnchorRanges...) @editSession.off '.snippet-expansion' @editSession.snippetExpansion = null