From 5b990cf571159e5b833cd174e2e1ba2a944a5e9d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki & Nathan Sobo Date: Tue, 26 Feb 2013 17:42:47 -0700 Subject: [PATCH] Add the 'between' invalidation strategy for markers This invalidates markers when the start or end point of the changed range is between the head and tail position of the marker. --- spec/app/buffer-spec.coffee | 56 +++++++++++++++++++++++++++++++++++- src/app/buffer-marker.coffee | 42 ++++++++++++++++----------- src/app/range.coffee | 6 +++- 3 files changed, 85 insertions(+), 19 deletions(-) diff --git a/spec/app/buffer-spec.coffee b/spec/app/buffer-spec.coffee index c6ea99a3c..3510d46f4 100644 --- a/spec/app/buffer-spec.coffee +++ b/spec/app/buffer-spec.coffee @@ -869,11 +869,12 @@ describe 'Buffer', -> expect(buffer.getMarkerRange(marker2)).toBeUndefined() describe "marker updates due to buffer changes", -> - [marker1, marker2] = [] + [marker1, marker2, marker3] = [] beforeEach -> marker1 = buffer.markRange([[4, 20], [4, 23]]) marker2 = buffer.markRange([[4, 20], [4, 23]], invalidationStrategy: 'never') + marker3 = buffer.markRange([[4, 20], [4, 23]], invalidationStrategy: 'between') describe "when the buffer changes due to a new operation", -> describe "when the change precedes the marker range", -> @@ -905,11 +906,21 @@ describe 'Buffer', -> buffer.insert([4, 20], '...') expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 26]] + describe "when the invalidation strategy is 'between'", -> + it "invalidates the marker", -> + buffer.insert([4, 20], '...') + expect(buffer.getMarkerRange(marker3)).toBeUndefined() + describe "when the change is an insertion at the end of the marker range", -> it "moves the end point", -> buffer.insert([4, 23], '...') expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 26]] + describe "when the invalidation strategy is 'between'", -> + it "invalidates the marker", -> + buffer.insert([4, 23], '...') + expect(buffer.getMarkerRange(marker3)).toBeUndefined() + describe "when the change surrounds the marker range", -> describe "when the marker's invalidation strategy is 'contains' (the default)", -> it "invalidates the marker", -> @@ -918,6 +929,13 @@ describe 'Buffer', -> buffer.undo() expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]] + describe "when the marker's invalidation strategy is 'between'", -> + it "invalidates the marker", -> + buffer.delete([[4, 15], [4, 25]]) + expect(buffer.getMarkerRange(marker3)).toBeUndefined() + buffer.undo() + expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]] + describe "when the marker's invalidation strategy is 'never'", -> it "does not invalidate the marker, but sets it to an empty range at the end of the change", -> buffer.change([[4, 15], [4, 25]], "...") @@ -933,6 +951,13 @@ describe 'Buffer', -> buffer.undo() expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]] + describe "when the marker's invalidation strategy is 'between'", -> + it "invalidates the marker", -> + buffer.delete([[4, 15], [4, 22]]) + expect(buffer.getMarkerRange(marker3)).toBeUndefined() + buffer.undo() + expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]] + describe "when the marker's invalidation strategy is 'never'", -> it "moves the start of the marker range to the end of the change", -> buffer.delete([[4, 15], [4, 22]]) @@ -948,6 +973,13 @@ describe 'Buffer', -> buffer.undo() expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]] + describe "when the marker's invalidation strategy is 'between'", -> + it "invalidates the marker", -> + buffer.delete([[4, 22], [4, 25]]) + expect(buffer.getMarkerRange(marker3)).toBeUndefined() + buffer.undo() + expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]] + describe "when the marker's invalidation strategy is 'never'", -> it "moves the end of the marker range to the start of the change", -> buffer.delete([[4, 22], [4, 25]]) @@ -955,6 +987,28 @@ describe 'Buffer', -> buffer.undo() expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]] + describe "when the change is between the start and the end of the marker range", -> + describe "when the marker's invalidation strategy is 'contains' (the default)", -> + it "does not invalidate the marker", -> + buffer.insert([4, 21], 'x') + expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 24]] + buffer.undo() + expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]] + + describe "when the marker's invalidation strategy is 'between'", -> + it "invalidates the marker", -> + buffer.insert([4, 21], 'x') + expect(buffer.getMarkerRange(marker3)).toBeUndefined() + buffer.undo() + expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]] + + describe "when the marker's invalidation strategy is 'never'", -> + it "moves the end of the marker range to the start of the change", -> + buffer.insert([4, 21], 'x') + expect(buffer.getMarkerRange(marker2)).toEqual [[4, 20], [4, 24]] + buffer.undo() + expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]] + describe "when the buffer changes due to the undo or redo of a previous operation", -> it "restores invalidated markers when undoing/redoing in the other direction", -> buffer.change([[4, 21], [4, 24]], "foo") diff --git a/src/app/buffer-marker.coffee b/src/app/buffer-marker.coffee index 55a9f2545..199171595 100644 --- a/src/app/buffer-marker.coffee +++ b/src/app/buffer-marker.coffee @@ -8,9 +8,10 @@ class BufferMarker headPosition: null tailPosition: null suppressObserverNotification: false - invalidationStrategy: 'contains' + invalidationStrategy: null constructor: ({@id, @buffer, range, @invalidationStrategy, noTail, reverse}) -> + @invalidationStrategy ?= 'contains' @setRange(range, {noTail, reverse}) setRange: (range, options={}) -> @@ -71,23 +72,30 @@ class BufferMarker newTailPosition = @getTailPosition() @notifyObservers({oldTailPosition, newTailPosition, bufferChanged: false}) - tryToInvalidate: (oldRange) -> - containsStart = oldRange.containsPoint(@getStartPosition(), exclusive: true) - containsEnd = oldRange.containsPoint(@getEndPosition(), exclusive: true) - return unless containsEnd or containsStart + tryToInvalidate: (changedRange) -> + betweenStartAndEnd = @getRange().containsRange(changedRange, exclusive: false) + containsStart = changedRange.containsPoint(@getStartPosition(), exclusive: true) + containsEnd = changedRange.containsPoint(@getEndPosition(), exclusive: true) - if @invalidationStrategy is 'never' - previousRange = @getRange() - if containsStart and containsEnd - @setRange([oldRange.end, oldRange.end]) - else if containsStart - @setRange([oldRange.end, @getEndPosition()]) - else - @setRange([@getStartPosition(), oldRange.start]) - [@id, previousRange] - else - @invalidate() - [@id] + switch @invalidationStrategy + when 'between' + if betweenStartAndEnd or containsStart or containsEnd + @invalidate() + [@id] + when 'contains' + if containsStart or containsEnd + @invalidate() + [@id] + when 'never' + if containsStart or containsEnd + previousRange = @getRange() + if containsStart and containsEnd + @setRange([changedRange.end, changedRange.end]) + else if containsStart + @setRange([changedRange.end, @getEndPosition()]) + else + @setRange([@getStartPosition(), changedRange.start]) + [@id, previousRange] handleBufferChange: (bufferChange) -> @consolidateObserverNotifications true, => diff --git a/src/app/range.coffee b/src/app/range.coffee index 136f52f15..a3870ea66 100644 --- a/src/app/range.coffee +++ b/src/app/range.coffee @@ -57,7 +57,11 @@ class Range else otherRange.intersectsWith(this) - containsPoint: (point, { exclusive } = {}) -> + containsRange: (otherRange, {exclusive} = {}) -> + { start, end } = Range.fromObject(otherRange) + @containsPoint(start, {exclusive}) and @containsPoint(end, {exclusive}) + + containsPoint: (point, {exclusive} = {}) -> point = Point.fromObject(point) if exclusive point.isGreaterThan(@start) and point.isLessThan(@end)