Apply text to buffer via diff on reload

Previously, it would blindly read from disk on reload, and set the text 
into the editor. This was problematic as it would mess with markers and 
folds. No longer. 

Fixes #1285 and fixes atom/bookmarks#3
This commit is contained in:
Ben Ogle
2013-12-11 17:42:35 -08:00
parent 9fbfeb970b
commit 1f69963982
3 changed files with 125 additions and 3 deletions

View File

@@ -23,6 +23,7 @@
"clear-cut": "0.2.0",
"coffee-script": "1.6.3",
"coffeestack": "0.6.0",
"diff": "git://github.com/benogle/jsdiff.git",
"emissary": "0.19.0",
"first-mate": "0.5.0",
"fs-plus": "0.11.0",

View File

@@ -113,10 +113,17 @@ describe 'TextBuffer', ->
runs ->
[event] = changeHandler.argsForCall[0]
expect(event.oldRange).toEqual [[0, 0], [0, 5]]
expect(event.oldRange).toEqual [[0, 0], [0, 0]]
expect(event.newRange).toEqual [[0, 0], [0, 6]]
expect(event.oldText).toBe "first"
expect(event.oldText).toBe ""
expect(event.newText).toBe "second"
[event] = changeHandler.argsForCall[1]
expect(event.oldRange).toEqual [[0, 6], [0, 11]]
expect(event.newRange).toEqual [[0, 6], [0, 6]]
expect(event.oldText).toBe "first"
expect(event.newText).toBe ""
expect(buffer.isModified()).toBeFalsy()
describe "when the buffer's memory contents differ from the *previous* disk contents", ->
@@ -454,6 +461,67 @@ describe 'TextBuffer', ->
expect(event.oldRange).toEqual expectedPreRange
expect(event.newRange).toEqual [[0, 0], [1, 14]]
describe ".setTextViaDiff(text)", ->
it "changes the entire contents of the buffer with smaller content with no newline at the end", ->
newText = "I know you are.\nBut what am I?"
buffer.setTextViaDiff(newText)
expect(buffer.getText()).toBe newText
it "changes the entire contents of the buffer with smaller content with newline at the end", ->
newText = "I know you are.\nBut what am I?\n"
buffer.setTextViaDiff(newText)
expect(buffer.getText()).toBe newText
it "changes a few lines at the beginning in the buffer", ->
newText = buffer.getText().replace(/function/g, 'omgwow')
buffer.setTextViaDiff(newText)
expect(buffer.getText()).toBe newText
it "changes a few lines in the middle of the buffer", ->
newText = buffer.getText().replace(/shift/g, 'omgwow')
buffer.setTextViaDiff(newText)
expect(buffer.getText()).toBe newText
it "adds a newline at the end", ->
newText = buffer.getText() + '\n'
buffer.setTextViaDiff(newText)
expect(buffer.getText()).toBe newText
it "changes all with no newlines", ->
buffer.setText('BUFFER CHANGE')
newText = 'DISK CHANGE'
buffer.setTextViaDiff(newText)
expect(buffer.getText()).toBe newText
describe "with windows newlines", ->
beforeEach ->
buffer.setText(buffer.getText().replace(/\n/g, '\r\n'))
it "adds a newline at the end", ->
newText = buffer.getText() + '\r\n'
buffer.setTextViaDiff(newText)
expect(buffer.getText()).toBe newText
it "changes the entire contents of the buffer with smaller content with no newline at the end", ->
newText = "I know you are.\r\nBut what am I?"
buffer.setTextViaDiff(newText)
expect(buffer.getText()).toBe newText
it "changes the entire contents of the buffer with smaller content with newline at the end", ->
newText = "I know you are.\r\nBut what am I?\r\n"
buffer.setTextViaDiff(newText)
expect(buffer.getText()).toBe newText
it "changes a few lines at the beginning in the buffer", ->
newText = buffer.getText().replace(/function/g, 'omgwow')
buffer.setTextViaDiff(newText)
expect(buffer.getText()).toBe newText
it "changes a few lines in the middle of the buffer", ->
newText = buffer.getText().replace(/shift/g, 'omgwow')
buffer.setTextViaDiff(newText)
expect(buffer.getText()).toBe newText
describe ".save()", ->
saveBuffer = null

View File

@@ -1,4 +1,5 @@
_ = require 'underscore-plus'
diff = require 'diff'
Q = require 'q'
{P} = require 'scandal'
telepath = require 'telepath'
@@ -133,7 +134,7 @@ class TextBuffer extends telepath.Model
# Sets the buffer's content to the cached disk contents
reload: ->
@emit 'will-reload'
@setText(@cachedDiskContents)
@setTextViaDiff(@cachedDiskContents)
@emitModifiedStatusChanged(false)
@emit 'reloaded'
@@ -198,6 +199,12 @@ class TextBuffer extends telepath.Model
setText: (text) ->
@change(@getRange(), text, normalizeLineEndings: false)
# Replaces the current buffer contents. Only apply the differences.
#
# text - A {String} containing the new buffer contents.
setTextViaDiff: (text) ->
@applyDifferences(text)
# Gets the range of the buffer contents.
#
# Returns a new {Range}, from `[0, 0]` to the end of the buffer.
@@ -667,3 +674,49 @@ class TextBuffer extends telepath.Model
for row in [start..end]
line = @lineForRow(row)
console.log row, line, line.length
applyDifferences: (newText) ->
currentText = @getText()
return if currentText == newText
endsWithNewline = (str) ->
/[\r\n]+$/g.test(str)
computeBufferColumn = (str) ->
newlineIndex = Math.max(str.lastIndexOf('\n'), str.lastIndexOf('\r'))
if endsWithNewline(str)
0
else if newlineIndex == -1
str.length
else
str.length - newlineIndex - 1
@transact =>
bufferRow = 0
bufferColumn = 0
startPosition = [0, 0]
lineDiff = diff.diffLines(currentText, newText)
changeOptions = normalizeLineEndings: false
for change in lineDiff
numberLines = change.value.match(/\n/g)?.length ? 0
startPosition[0] = bufferRow
startPosition[1] = bufferColumn
if change.added
@change([startPosition, startPosition], change.value, changeOptions)
bufferRow += numberLines
bufferColumn = computeBufferColumn(change.value)
else if change.removed
endBufferRow = bufferRow + numberLines
endBufferColumn = bufferColumn + computeBufferColumn(change.value)
@change([startPosition, [endBufferRow, endBufferColumn]], '', changeOptions)
else
bufferRow += numberLines
bufferColumn = computeBufferColumn(change.value)
# console.log 'after', bufferRow, bufferColumn, change
# console.log @getText()