Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

Commit c185463

Browse files
author
Max Brunsfeld
authored
Merge pull request #274 from atom/mb-one-display-layer-event-per-tx
Only emit one display layer change event per buffer transaction
2 parents 3d8e692 + ea313af commit c185463

File tree

8 files changed

+202
-68
lines changed

8 files changed

+202
-68
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "text-buffer",
3-
"version": "13.7.2",
3+
"version": "13.8.0-display-layer-change-event-2",
44
"description": "A container for large mutable strings with annotated regions",
55
"main": "./lib/text-buffer",
66
"scripts": {

spec/display-layer-spec.js

Lines changed: 132 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1989,14 +1989,13 @@ describe('DisplayLayer', () => {
19891989
displayLayer.setTextDecorationLayer(decorationLayer)
19901990
const allChanges = []
19911991

1992-
displayLayer.onDidChangeSync((changes) => allChanges.push(...changes))
1992+
displayLayer.onDidChange((changes) => allChanges.push(...changes))
19931993

19941994
decorationLayer.emitInvalidateRangeEvent([[2, 1], [3, 2]])
19951995

19961996
expect(allChanges).toEqual([{
1997-
start: Point(1, 0),
1998-
oldExtent: Point(2, 0),
1999-
newExtent: Point(2, 0)
1997+
oldRange: Range(Point(1, 0), Point(3, 0)),
1998+
newRange: Range(Point(1, 0), Point(3, 0))
20001999
}])
20012000
})
20022001

@@ -2167,6 +2166,128 @@ describe('DisplayLayer', () => {
21672166
})
21682167
})
21692168

2169+
describe('.onDidChange', () => {
2170+
it('calls the given callback when the display layer\'s content changes', () => {
2171+
const buffer = new TextBuffer({
2172+
text: 'abc\ndef\nghi\njkl\nmno'
2173+
})
2174+
2175+
const displayLayer = buffer.addDisplayLayer({tabLength: 4})
2176+
2177+
const events = []
2178+
displayLayer.onDidChange((changes) => events.push(...changes))
2179+
2180+
displayLayer.foldBufferRange(Range(Point(1, 1), Point(2, 2)))
2181+
expect(events).toEqual([
2182+
{
2183+
oldRange: Range(Point(1, 0), Point(3, 0)),
2184+
newRange: Range(Point(1, 0), Point(2, 0))
2185+
}
2186+
])
2187+
2188+
events.length = 0
2189+
displayLayer.foldBufferRange(Range(Point(3, 1), Point(4, 2)))
2190+
expect(events).toEqual([
2191+
{
2192+
oldRange: Range(Point(2, 0), Point(4, 0)),
2193+
newRange: Range(Point(2, 0), Point(3, 0))
2194+
}
2195+
])
2196+
2197+
events.length = 0
2198+
displayLayer.destroyAllFolds()
2199+
expect(events).toEqual([
2200+
{
2201+
oldRange: Range(Point(1, 0), Point(3, 0)),
2202+
newRange: Range(Point(1, 0), Point(5, 0))
2203+
}
2204+
])
2205+
2206+
// When multiple changes occur in a transaction, the changes are combined.
2207+
events.length = 0
2208+
buffer.transact(() => {
2209+
displayLayer.foldBufferRange(Range(Point(1, 1), Point(2, 2)))
2210+
displayLayer.foldBufferRange(Range(Point(3, 1), Point(4, 2)))
2211+
})
2212+
expect(events).toEqual([
2213+
{
2214+
oldRange: Range(Point(1, 0), Point(5, 0)),
2215+
newRange: Range(Point(1, 0), Point(3, 0))
2216+
}
2217+
])
2218+
})
2219+
2220+
it('calls the callback only one time per text buffer transaction', () => {
2221+
const buffer = new TextBuffer({
2222+
text: 'abc\ndef\nghi\njkl\nmno'
2223+
})
2224+
2225+
const displayLayer = buffer.addDisplayLayer({tabLength: 4})
2226+
2227+
const events = []
2228+
displayLayer.onDidChange((changes) => events.push(changes))
2229+
2230+
const checkpoint = buffer.createCheckpoint()
2231+
2232+
buffer.transact(() => {
2233+
buffer.setTextInRange([[0, 1], [0, 1]], '\n')
2234+
buffer.setTextInRange([[4, 2], [4, 2]], '\n')
2235+
buffer.setTextInRange([[4, 2], [4, 2]], '.')
2236+
buffer.setTextInRange([[4, 3], [4, 3]], '.')
2237+
buffer.setTextInRange([[4, 4], [4, 4]], '.')
2238+
})
2239+
expect(events).toEqual([[
2240+
{
2241+
oldRange: Range(Point(0, 0), Point(1, 0)),
2242+
newRange: Range(Point(0, 0), Point(2, 0))
2243+
},
2244+
{
2245+
oldRange: Range(Point(3, 0), Point(4, 0)),
2246+
newRange: Range(Point(4, 0), Point(6, 0))
2247+
}
2248+
]])
2249+
2250+
events.length = 0
2251+
buffer.undo()
2252+
expect(events).toEqual([[
2253+
{
2254+
oldRange: Range(Point(0, 0), Point(2, 0)),
2255+
newRange: Range(Point(0, 0), Point(1, 0))
2256+
},
2257+
{
2258+
oldRange: Range(Point(4, 0), Point(6, 0)),
2259+
newRange: Range(Point(3, 0), Point(4, 0))
2260+
}
2261+
]])
2262+
2263+
events.length = 0
2264+
buffer.redo()
2265+
expect(events).toEqual([[
2266+
{
2267+
oldRange: Range(Point(0, 0), Point(1, 0)),
2268+
newRange: Range(Point(0, 0), Point(2, 0))
2269+
},
2270+
{
2271+
oldRange: Range(Point(3, 0), Point(4, 0)),
2272+
newRange: Range(Point(4, 0), Point(6, 0))
2273+
}
2274+
]])
2275+
2276+
events.length = 0
2277+
buffer.revertToCheckpoint(checkpoint)
2278+
expect(events).toEqual([[
2279+
{
2280+
oldRange: Range(Point(0, 0), Point(2, 0)),
2281+
newRange: Range(Point(0, 0), Point(1, 0))
2282+
},
2283+
{
2284+
oldRange: Range(Point(4, 0), Point(6, 0)),
2285+
newRange: Range(Point(3, 0), Point(4, 0))
2286+
}
2287+
]])
2288+
})
2289+
})
2290+
21702291
describe('.getApproximateScreenLineCount()', () => {
21712292
it('estimates the screen line count based on the currently-indexed portion of the buffer', () => {
21722293
const buffer = new TextBuffer({
@@ -2458,18 +2579,18 @@ function verifyChangeEvent (displayLayer, fn) {
24582579
displayLayerCopy.setTextDecorationLayer(displayLayer.getTextDecorationLayer())
24592580
const previousTokenLines = getTokens(displayLayerCopy)
24602581
displayLayerCopy.destroy()
2461-
let lastChanges = null
2582+
let changes = []
24622583

2463-
const disposable = displayLayer.onDidChangeSync(function (changes) {
2464-
lastChanges = changes
2584+
const disposable = displayLayer.onDidChange((event) => {
2585+
changes.push(...event)
24652586
})
24662587

24672588
fn()
24682589
disposable.dispose()
24692590
displayLayerCopy = displayLayer.copy()
24702591
displayLayerCopy.setTextDecorationLayer(displayLayer.getTextDecorationLayer())
24712592
const expectedTokenLines = getTokens(displayLayerCopy)
2472-
updateTokenLines(previousTokenLines, displayLayerCopy, lastChanges)
2593+
updateTokenLines(previousTokenLines, displayLayerCopy, changes)
24732594
displayLayerCopy.destroy()
24742595
expect(previousTokenLines).toEqual(expectedTokenLines)
24752596
}
@@ -2700,9 +2821,9 @@ function getTokenBoundaries (displayLayer, startRow = 0, endRow = displayLayer.g
27002821
}
27012822

27022823
function updateTokenLines (tokenLines, displayLayer, changes) {
2703-
for (const {start, oldExtent, newExtent} of changes || []) {
2704-
const newTokenLines = getTokens(displayLayer, start.row, start.row + newExtent.row)
2705-
tokenLines.splice(start.row, oldExtent.row, ...newTokenLines)
2824+
for (const {oldRange, newRange} of changes || []) {
2825+
const newTokenLines = getTokens(displayLayer, newRange.start.row, newRange.end.row)
2826+
tokenLines.splice(newRange.start.row, oldRange.end.row - oldRange.start.row, ...newTokenLines)
27062827
}
27072828
}
27082829

spec/marker-layer-spec.coffee

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ describe "MarkerLayer", ->
219219
expect(updateCount).toBe(6)
220220

221221
# update events happen after updating display layers when there is no parent transaction.
222-
displayLayer.onDidChangeSync ->
222+
displayLayer.onDidChange ->
223223
displayLayerDidChange = true
224224
buffer.undo()
225225
expect(updateCount).toBe(7)

spec/text-buffer-io-spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ describe('TextBuffer IO', () => {
165165
const events = []
166166

167167
const displayLayer = buffer.addDisplayLayer()
168-
displayLayer.onDidChangeSync((event) => events.push(['display-layer', event]))
168+
displayLayer.onDidChange((event) => events.push(['display-layer', event]))
169169

170170
buffer.registerTextDecorationLayer({
171171
bufferDidChange ({oldRange, newRange}) { events.push(['decoration-layer', {oldRange, newRange}]) }
@@ -174,7 +174,7 @@ describe('TextBuffer IO', () => {
174174
buffer.reload().then(() => {
175175
expect(events).toEqual([
176176
['decoration-layer', {oldRange: Range(Point(0, 7), Point(0, 7)), newRange: Range(Point(0, 7), Point(0, 11))}],
177-
['display-layer', [{start: Point(0, 0), oldExtent: Point(1, 0), newExtent: Point(1, 0)}]]
177+
['display-layer', [{oldRange: Range(Point(0, 0), Point(1, 0)), newRange: Range(Point(0, 0), Point(1, 0))}]]
178178
])
179179
done()
180180
})

spec/text-buffer-spec.coffee

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ describe "TextBuffer", ->
9797
expect(buffer.getText()).toEqual "hey\nyou're old\r\nhow are you doing?"
9898

9999
describe "after a change", ->
100-
it "notifies, in order, decoration layers, display layers, and display layer ::onDidChangeSync observers with the relevant details", ->
100+
it "notifies, in order, decoration layers, display layers, and display layer ::onDidChange observers with the relevant details", ->
101+
buffer = new TextBuffer("hello\nworld\r\nhow are you doing?")
102+
101103
events = []
102104
textDecorationLayer1 = {
103105
bufferDidChange: (e) -> events.push({source: 'decoration-layer-1', event: e})
@@ -118,11 +120,11 @@ describe "TextBuffer", ->
118120
buffer.registerTextDecorationLayer(textDecorationLayer1) # insert a duplicate decoration layer
119121
buffer.registerTextDecorationLayer(textDecorationLayer2)
120122
buffer.onDidChange (e) -> events.push({source: 'buffer', event: JSON.parse(JSON.stringify(e))})
123+
displayLayer1.onDidChange (e) -> events.push({source: 'display-layer-event', event: e})
121124

122-
disposable = displayLayer1.onDidChangeSync ->
123-
disposable.dispose()
125+
buffer.transact ->
126+
buffer.setTextInRange([[0, 2], [2, 3]], "y there\r\ncat\nwhat", normalizeLineEndings: false)
124127
buffer.setTextInRange([[1, 1], [1, 2]], "abc", normalizeLineEndings: false)
125-
buffer.setTextInRange([[0, 2], [2, 3]], "y there\r\ncat\nwhat", normalizeLineEndings: false)
126128

127129
changeEvent1 = {
128130
oldRange: [[0, 2], [2, 3]], newRange: [[0, 2], [2, 4]]
@@ -157,6 +159,13 @@ describe "TextBuffer", ->
157159
}
158160
]
159161
}
162+
},
163+
{
164+
source: 'display-layer-event',
165+
event: [{
166+
oldRange: Range(Point(0, 0), Point(3, 0)),
167+
newRange: Range(Point(0, 0), Point(3, 0))
168+
}]
160169
}
161170
]
162171

src/display-layer.js

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class DisplayLayer {
2424
this.textDecorationLayer = new EmptyDecorationLayer()
2525
this.displayMarkerLayersById = new Map()
2626
this.destroyed = false
27+
this.changesSinceLastEvent = new Patch()
2728

2829
this.invisibles = params.invisibles != null ? params.invisibles : {}
2930
this.tabLength = params.tabLength != null ? params.tabLength : 4
@@ -153,11 +154,7 @@ class DisplayLayer {
153154
const endRow = this.translateBufferPositionWithSpatialIndex(Point(endBufferRow, 0), 'backward').row
154155
const extent = Point(endRow - startRow, 0)
155156
spliceArray(this.cachedScreenLines, startRow, extent.row, new Array(extent.row))
156-
this.emitDidChangeSyncEvent([{
157-
start: Point(startRow, 0),
158-
oldExtent: extent,
159-
newExtent: extent
160-
}])
157+
this.didChange({start: Point(startRow, 0), oldExtent: extent, newExtent: extent})
161158
})
162159
}
163160
}
@@ -185,8 +182,8 @@ class DisplayLayer {
185182
this.displayMarkerLayersById.delete(id)
186183
}
187184

188-
onDidChangeSync (callback) {
189-
return this.emitter.on('did-change-sync', callback)
185+
onDidChange (callback) {
186+
return this.emitter.on('did-change', callback)
190187
}
191188

192189
onDidReset (callback) {
@@ -207,9 +204,7 @@ class DisplayLayer {
207204
if (containingFoldMarkers.length === 0) {
208205
const foldStartRow = bufferRange.start.row
209206
const foldEndRow = bufferRange.end.row + 1
210-
this.emitDidChangeSyncEvent([
211-
this.updateSpatialIndex(foldStartRow, foldEndRow, foldEndRow, Infinity)
212-
])
207+
this.didChange(this.updateSpatialIndex(foldStartRow, foldEndRow, foldEndRow, Infinity))
213208
this.notifyObserversIfMarkerScreenPositionsChanged()
214209
}
215210
return foldId
@@ -269,12 +264,12 @@ class DisplayLayer {
269264
foldMarker.destroy()
270265
}
271266

272-
this.emitDidChangeSyncEvent([this.updateSpatialIndex(
267+
this.didChange(this.updateSpatialIndex(
273268
combinedRangeStart.row,
274269
combinedRangeEnd.row + 1,
275270
combinedRangeEnd.row + 1,
276271
Infinity
277-
)])
272+
))
278273
this.notifyObserversIfMarkerScreenPositionsChanged()
279274

280275
return foldedRanges
@@ -807,10 +802,8 @@ class DisplayLayer {
807802
}
808803
}
809804

810-
const combinedChanges = new Patch()
811805
this.indexedBufferRowCount += newEndRow - oldEndRow
812-
const {start, oldExtent, newExtent} = this.updateSpatialIndex(startRow, oldEndRow + 1, newEndRow + 1, Infinity)
813-
combinedChanges.splice(start, oldExtent, newExtent)
806+
this.didChange(this.updateSpatialIndex(startRow, oldEndRow + 1, newEndRow + 1, Infinity))
814807

815808
for (let bufferRange of this.textDecorationLayer.getInvalidatedRanges()) {
816809
bufferRange = Range.fromObject(bufferRange)
@@ -821,20 +814,25 @@ class DisplayLayer {
821814
const endRow = this.translateBufferPositionWithSpatialIndex(Point(endBufferRow, 0), 'backward').row
822815
const extent = Point(endRow - startRow, 0)
823816
spliceArray(this.cachedScreenLines, startRow, extent.row, new Array(extent.row))
824-
combinedChanges.splice(Point(startRow, 0), extent, extent)
817+
this.didChange({start: Point(startRow, 0), oldExtent: extent, newExtent: extent})
825818
}
819+
}
826820

827-
return Object.freeze(combinedChanges.getChanges().map((hunk) => {
828-
return {
829-
start: Point.fromObject(hunk.newStart),
830-
oldExtent: traversal(hunk.oldEnd, hunk.oldStart),
831-
newExtent: traversal(hunk.newEnd, hunk.newStart)
832-
}
833-
}))
821+
didChange ({start, oldExtent, newExtent}) {
822+
this.changesSinceLastEvent.splice(start, oldExtent, newExtent)
823+
if (this.buffer.transactCallDepth === 0) this.emitDeferredChangeEvents()
834824
}
835825

836-
emitDidChangeSyncEvent (event) {
837-
this.emitter.emit('did-change-sync', event)
826+
emitDeferredChangeEvents () {
827+
if (this.changesSinceLastEvent.getChangeCount() > 0) {
828+
this.emitter.emit('did-change', this.changesSinceLastEvent.getChanges().map((change) => {
829+
return {
830+
oldRange: new Range(change.oldStart, change.oldEnd),
831+
newRange: new Range(change.newStart, change.newEnd)
832+
}
833+
}))
834+
this.changesSinceLastEvent = new Patch()
835+
}
838836
}
839837

840838
notifyObserversIfMarkerScreenPositionsChanged () {

0 commit comments

Comments
 (0)