"Retire" buffer IDs when the buffer can't be deserialized

This commit is contained in:
Ash Wilson 2017-09-19 14:17:04 -04:00
parent 46fc5ffc8c
commit 0782f0f4d0
No known key found for this signature in database
GPG Key ID: 81B1DDB704F69D2A
4 changed files with 31 additions and 23 deletions

View File

@ -76,6 +76,15 @@ describe "TextEditor", ->
expect(editor2.displayLayer.tabLength).toBe(editor2.getTabLength()) expect(editor2.displayLayer.tabLength).toBe(editor2.getTabLength())
expect(editor2.displayLayer.softWrapColumn).toBe(editor2.getSoftWrapColumn()) expect(editor2.displayLayer.softWrapColumn).toBe(editor2.getSoftWrapColumn())
it "ignores buffers with retired IDs", ->
editor2 = TextEditor.deserialize(editor.serialize(), {
assert: atom.assert,
textEditors: atom.textEditors,
project: {bufferForIdSync: -> null}
})
expect(editor2).toBeNull()
describe "when the editor is constructed with the largeFileMode option set to true", -> describe "when the editor is constructed with the largeFileMode option set to true", ->
it "loads the editor but doesn't tokenize", -> it "loads the editor but doesn't tokenize", ->
editor = null editor = null

View File

@ -30,6 +30,8 @@ class Project extends Model
@repositoryProviders = [new GitRepositoryProvider(this, config)] @repositoryProviders = [new GitRepositoryProvider(this, config)]
@loadPromisesByPath = {} @loadPromisesByPath = {}
@watcherPromisesByPath = {} @watcherPromisesByPath = {}
@retiredBufferIDs = new Set()
@retiredBufferPaths = new Set()
@consumeServices(packageManager) @consumeServices(packageManager)
destroyed: -> destroyed: ->
@ -58,29 +60,17 @@ class Project extends Model
### ###
deserialize: (state) -> deserialize: (state) ->
checkNotDirectory = (filePath) -> @retiredBufferIDs = new Set()
new Promise (resolve, reject) -> @retiredBufferPaths = new Set()
fs.isDirectory filePath, (isDir) ->
if isDir then reject() else resolve()
checkAccess = (filePath) -> handleBufferState = (bufferState) =>
new Promise (resolve, reject) ->
fs.open filePath, 'r', (err, fd) ->
return reject() if err?
fs.close fd, () -> resolve()
handleBufferState = (bufferState) ->
bufferState.shouldDestroyOnFileDelete ?= -> atom.config.get('core.closeDeletedFileTabs') bufferState.shouldDestroyOnFileDelete ?= -> atom.config.get('core.closeDeletedFileTabs')
bufferState.mustExist = true
promise = Promise.resolve() TextBuffer.deserialize(bufferState).catch (err) =>
if bufferState.filePath? @retiredBufferIDs.add(bufferState.id)
promise = promise.then () -> Promise.all([ @retiredBufferPaths.add(bufferState.filePath)
checkNotDirectory(bufferState.filePath), null
checkAccess(bufferState.filePath)
])
promise = promise.then () -> TextBuffer.deserialize(bufferState)
promise = promise.catch (err) -> null
promise
bufferPromises = (handleBufferState(bufferState) for bufferState in state.buffers) bufferPromises = (handleBufferState(bufferState) for bufferState in state.buffers)
@ -465,11 +455,13 @@ class Project extends Model
# Only to be used in specs # Only to be used in specs
bufferForPathSync: (filePath) -> bufferForPathSync: (filePath) ->
absoluteFilePath = @resolvePath(filePath) absoluteFilePath = @resolvePath(filePath)
return null if @retiredBufferPaths.has absoluteFilePath
existingBuffer = @findBufferForPath(absoluteFilePath) if filePath existingBuffer = @findBufferForPath(absoluteFilePath) if filePath
existingBuffer ? @buildBufferSync(absoluteFilePath) existingBuffer ? @buildBufferSync(absoluteFilePath)
# Only to be used when deserializing # Only to be used when deserializing
bufferForIdSync: (id) -> bufferForIdSync: (id) ->
return null if @retiredBufferIDs.has id
existingBuffer = @findBufferForId(id) if id existingBuffer = @findBufferForId(id) if id
existingBuffer ? @buildBufferSync() existingBuffer ? @buildBufferSync()

View File

@ -128,7 +128,10 @@ class TextEditor extends Model
state.tokenizedBuffer = state.displayBuffer.tokenizedBuffer state.tokenizedBuffer = state.displayBuffer.tokenizedBuffer
try try
state.tokenizedBuffer = TokenizedBuffer.deserialize(state.tokenizedBuffer, atomEnvironment) tokenizedBuffer = TokenizedBuffer.deserialize(state.tokenizedBuffer, atomEnvironment)
return null unless tokenizedBuffer?
state.tokenizedBuffer = tokenizedBuffer
state.tabLength = state.tokenizedBuffer.getTabLength() state.tabLength = state.tokenizedBuffer.getTabLength()
catch error catch error
if error.syscall is 'read' if error.syscall is 'read'

View File

@ -23,11 +23,15 @@ class TokenizedBuffer extends Model
changeCount: 0 changeCount: 0
@deserialize: (state, atomEnvironment) -> @deserialize: (state, atomEnvironment) ->
buffer = null
if state.bufferId if state.bufferId
state.buffer = atomEnvironment.project.bufferForIdSync(state.bufferId) buffer = atomEnvironment.project.bufferForIdSync(state.bufferId)
else else
# TODO: remove this fallback after everyone transitions to the latest version. # TODO: remove this fallback after everyone transitions to the latest version.
state.buffer = atomEnvironment.project.bufferForPathSync(state.bufferPath) buffer = atomEnvironment.project.bufferForPathSync(state.bufferPath)
return null unless buffer?
state.buffer = buffer
state.assert = atomEnvironment.assert state.assert = atomEnvironment.assert
new this(state) new this(state)