mirror of
https://github.com/atom/atom.git
synced 2026-01-24 14:28:14 -05:00
RootView constructor can be called with serialized view state data
Move the saving of serialized root view data to window.coffee. The window.startup method looks for window state on the atom object and instantiates the root view with that if it is present.
This commit is contained in:
@@ -11,55 +11,99 @@ describe "RootView", ->
|
||||
|
||||
beforeEach ->
|
||||
path = require.resolve 'fixtures/dir/a'
|
||||
rootView = new RootView({path})
|
||||
rootView = new RootView(pathToOpen: path)
|
||||
rootView.enableKeymap()
|
||||
project = rootView.project
|
||||
|
||||
describe "initialize", ->
|
||||
describe "when called with a path that references a file", ->
|
||||
it "creates a project for the file's parent directory and opens it in the editor", ->
|
||||
expect(rootView.project.path).toBe fs.directory(path)
|
||||
expect(rootView.activeEditor().buffer.getPath()).toBe path
|
||||
describe "initialize(viewState)", ->
|
||||
describe "when called with a pathToOpen", ->
|
||||
describe "when pathToOpen references a file", ->
|
||||
it "creates a project for the file's parent directory and opens it in the editor", ->
|
||||
expect(rootView.project.path).toBe fs.directory(path)
|
||||
expect(rootView.editors().length).toBe 1
|
||||
expect(rootView.editors()[0]).toHaveClass 'active'
|
||||
expect(rootView.activeEditor().buffer.getPath()).toBe path
|
||||
|
||||
describe "when called with a path that references a directory", ->
|
||||
it "creates a project for the directory and opens an empty buffer", ->
|
||||
path = require.resolve 'fixtures/dir/'
|
||||
rootView = new RootView({path})
|
||||
describe "when pathToOpen references a directory", ->
|
||||
it "creates a project for the directory does not open an editor", ->
|
||||
path = require.resolve 'fixtures/dir/'
|
||||
rootView = new RootView(pathToOpen: path)
|
||||
|
||||
expect(rootView.project.path).toBe path
|
||||
expect(rootView.activeEditor().buffer.path).toBeUndefined()
|
||||
expect(rootView.project.path).toBe path
|
||||
expect(rootView.editors().length).toBe 0
|
||||
|
||||
describe "when not called with a path", ->
|
||||
describe "when called with view state data returned from a previous call to RootView.prototype.serialize", ->
|
||||
viewState = null
|
||||
|
||||
describe "when the serialized RootView does not have a project, only an unsaved buffer", ->
|
||||
buffer = null
|
||||
|
||||
beforeEach ->
|
||||
rootView = new RootView
|
||||
editor1 = rootView.activeEditor()
|
||||
buffer = editor1.buffer
|
||||
editor1.splitRight()
|
||||
viewState = rootView.serialize()
|
||||
|
||||
it "constructs the view with the same panes", ->
|
||||
rootView = new RootView(viewState)
|
||||
expect(rootView.project).toBeUndefined()
|
||||
expect(rootView.editors().length).toBe 2
|
||||
|
||||
describe "when the serialized RootView has a project", ->
|
||||
beforeEach ->
|
||||
editor1 = rootView.activeEditor()
|
||||
editor2 = editor1.splitRight()
|
||||
editor3 = editor2.splitRight()
|
||||
editor4 = editor2.splitDown()
|
||||
editor2.setBuffer(new Buffer(require.resolve 'fixtures/dir/b'))
|
||||
editor3.setBuffer(new Buffer(require.resolve 'fixtures/sample.js'))
|
||||
editor3.setCursorScreenPosition([2, 3])
|
||||
editor4.setBuffer(new Buffer(require.resolve 'fixtures/sample.txt'))
|
||||
editor4.setCursorScreenPosition([0, 2])
|
||||
rootView.attachToDom()
|
||||
editor2.focus()
|
||||
viewState = rootView.serialize()
|
||||
rootView.remove()
|
||||
|
||||
it "constructs the view with the same project and panes", ->
|
||||
rootView = new RootView(viewState)
|
||||
rootView.attachToDom()
|
||||
|
||||
expect(rootView.editors().length).toBe 4
|
||||
editor1 = rootView.panes.find('.row > .pane .editor:eq(0)').view()
|
||||
editor3 = rootView.panes.find('.row > .pane .editor:eq(1)').view()
|
||||
editor2 = rootView.panes.find('.row > .column > .pane .editor:eq(0)').view()
|
||||
editor4 = rootView.panes.find('.row > .column > .pane .editor:eq(1)').view()
|
||||
|
||||
expect(editor1.buffer.path).toBe require.resolve('fixtures/dir/a')
|
||||
expect(editor2.buffer.path).toBe require.resolve('fixtures/dir/b')
|
||||
expect(editor3.buffer.path).toBe require.resolve('fixtures/sample.js')
|
||||
expect(editor3.getCursorScreenPosition()).toEqual [2, 3]
|
||||
expect(editor4.buffer.path).toBe require.resolve('fixtures/sample.txt')
|
||||
expect(editor4.getCursorScreenPosition()).toEqual [0, 2]
|
||||
|
||||
# ensure adjustSplitPanes is called
|
||||
expect(editor1.width()).toBeGreaterThan 0
|
||||
expect(editor2.width()).toBeGreaterThan 0
|
||||
expect(editor3.width()).toBeGreaterThan 0
|
||||
expect(editor4.width()).toBeGreaterThan 0
|
||||
|
||||
# ensure correct editor is focused again
|
||||
expect(editor2.isFocused).toBeTruthy()
|
||||
expect(editor1.isFocused).toBeFalsy()
|
||||
expect(editor3.isFocused).toBeFalsy()
|
||||
expect(editor4.isFocused).toBeFalsy()
|
||||
|
||||
describe "when called with no state data", ->
|
||||
it "opens an empty buffer", ->
|
||||
rootView = new RootView
|
||||
expect(rootView.editors().length).toBe 1
|
||||
expect(rootView.activeEditor().buffer.path).toBeUndefined()
|
||||
|
||||
describe "when there is a window state for the current window stored on the atom object", ->
|
||||
it "sets the window state on the root view", ->
|
||||
spyOn(RootView.prototype, 'setWindowState')
|
||||
|
||||
describe "when the window is reloaded", ->
|
||||
afterEach ->
|
||||
delete atom.windowStatesByWindowNumber[$windowNumber]
|
||||
|
||||
it "stores its window state on the atom object by window number, then reassigns it next time the root view is constructed", ->
|
||||
rootView.activeEditor().splitLeft()
|
||||
expectedWindowState = rootView.getWindowState()
|
||||
|
||||
# simulate unload
|
||||
$(window).trigger 'beforeunload'
|
||||
expect(atom.windowStatesByWindowNumber[$windowNumber]).toEqual expectedWindowState
|
||||
|
||||
# simulate reload
|
||||
newRootView = new RootView
|
||||
|
||||
expect(newRootView.getWindowState()).toEqual expectedWindowState
|
||||
expect(newRootView.editors().length).toBe 2
|
||||
|
||||
describe "focus", ->
|
||||
it "can receive focus if there is no active editor, but otherwise hands off focus to the active editor", ->
|
||||
rootView = new RootView({path: require.resolve 'fixtures'})
|
||||
rootView = new RootView(pathToOpen: require.resolve 'fixtures')
|
||||
rootView.attachToDom()
|
||||
expect(rootView).toMatchSelector(':focus')
|
||||
|
||||
@@ -71,59 +115,6 @@ describe "RootView", ->
|
||||
expect(rootView).not.toMatchSelector(':focus')
|
||||
expect(rootView.activeEditor().isFocused).toBeTruthy()
|
||||
|
||||
describe "windowState getter and setter", ->
|
||||
[editor1, editor2, editor3, editor4, panesHtml] = []
|
||||
|
||||
beforeEach ->
|
||||
editor1 = rootView.activeEditor()
|
||||
editor2 = editor1.splitRight()
|
||||
editor3 = editor2.splitRight()
|
||||
editor4 = editor2.splitDown()
|
||||
editor2.setBuffer(new Buffer(require.resolve 'fixtures/dir/b'))
|
||||
editor3.setBuffer(new Buffer(require.resolve 'fixtures/sample.js'))
|
||||
editor3.setCursorScreenPosition([2, 3])
|
||||
editor4.setBuffer(new Buffer(require.resolve 'fixtures/sample.txt'))
|
||||
editor4.setCursorScreenPosition([0, 2])
|
||||
rootView.attachToDom()
|
||||
editor2.focus()
|
||||
panesHtml = rootView.panes.html()
|
||||
|
||||
it "can reconstruct the split pane arrangement from the window state hash returned by getWindowState", ->
|
||||
windowState = rootView.getWindowState()
|
||||
|
||||
editor2.remove()
|
||||
editor3.remove()
|
||||
editor4.remove()
|
||||
expect(rootView.panes.find('.editor').length).toBe 1
|
||||
|
||||
rootView.setWindowState(windowState)
|
||||
expect(rootView.editors().length).toBe 4
|
||||
|
||||
editor1 = rootView.panes.find('.row > .pane .editor:eq(0)').view()
|
||||
editor3 = rootView.panes.find('.row > .pane .editor:eq(1)').view()
|
||||
editor2 = rootView.panes.find('.row > .column > .pane .editor:eq(0)').view()
|
||||
editor4 = rootView.panes.find('.row > .column > .pane .editor:eq(1)').view()
|
||||
|
||||
expect(editor1.buffer.path).toBe require.resolve('fixtures/dir/a')
|
||||
expect(editor2.buffer.path).toBe require.resolve('fixtures/dir/b')
|
||||
expect(editor3.buffer.path).toBe require.resolve('fixtures/sample.js')
|
||||
expect(editor3.getCursorScreenPosition()).toEqual [2, 3]
|
||||
expect(editor4.buffer.path).toBe require.resolve('fixtures/sample.txt')
|
||||
expect(editor4.getCursorScreenPosition()).toEqual [0, 2]
|
||||
|
||||
# ensure adjustSplitPanes is called
|
||||
expect(editor1.width()).toBeGreaterThan 0
|
||||
expect(editor2.width()).toBeGreaterThan 0
|
||||
expect(editor3.width()).toBeGreaterThan 0
|
||||
expect(editor4.width()).toBeGreaterThan 0
|
||||
|
||||
# ensure correct editor is focused again
|
||||
expect(editor2.isFocused).toBeTruthy()
|
||||
expect(editor1.isFocused).toBeFalsy()
|
||||
expect(editor3.isFocused).toBeFalsy()
|
||||
expect(editor4.isFocused).toBeFalsy()
|
||||
|
||||
|
||||
describe "split editor panes", ->
|
||||
editor1 = null
|
||||
|
||||
|
||||
@@ -30,3 +30,14 @@ describe "Window", ->
|
||||
|
||||
requireStylesheet('atom.css')
|
||||
expect($('head style').length).toBe 1
|
||||
|
||||
describe "before the window is unloaded", ->
|
||||
afterEach ->
|
||||
delete atom.rootViewStates[$windowNumber]
|
||||
|
||||
it "saves the serialized state of the root view to the atom object so it can be rehydrated after reload", ->
|
||||
expect(atom.rootViewStates[$windowNumber]).toBeUndefined()
|
||||
$(window).trigger 'beforeunload'
|
||||
expect(atom.rootViewStates[$windowNumber]).toEqual window.rootView.serialize()
|
||||
|
||||
|
||||
|
||||
@@ -9,11 +9,9 @@ window.showConsole()
|
||||
|
||||
beforeEach ->
|
||||
window.resetTimeouts()
|
||||
delete atom.windowStatesByWindowNumber[$windowNumber]
|
||||
|
||||
afterEach ->
|
||||
$('#jasmine-content').empty()
|
||||
$(window).off 'beforeunload'
|
||||
|
||||
window.keymap.bindKeys '*', 'meta-w': 'close'
|
||||
$(document).on 'close', -> window.close()
|
||||
|
||||
@@ -10,12 +10,13 @@ class Atom
|
||||
keymap: null
|
||||
windows: null
|
||||
userConfigurationPath: null
|
||||
rootViewStates: null
|
||||
|
||||
constructor: (@loadPath, nativeMethods)->
|
||||
@windows = []
|
||||
@setUpKeymap()
|
||||
@userConfigurationPath = fs.absolute "~/.atom/atom.coffee"
|
||||
@windowStatesByWindowNumber = {}
|
||||
@rootViewStates = {}
|
||||
|
||||
setUpKeymap: ->
|
||||
@keymap = new Keymap()
|
||||
|
||||
@@ -18,10 +18,7 @@ class RootView extends View
|
||||
@div id: 'root-view', tabindex: -1, =>
|
||||
@div id: 'panes', outlet: 'panes'
|
||||
|
||||
initialize: (params) ->
|
||||
{path} = params
|
||||
@createProject(path)
|
||||
|
||||
initialize: ({ pathToOpen, projectPath, panesViewState }) ->
|
||||
@on 'toggle-file-finder', => @toggleFileFinder()
|
||||
@on 'show-console', => window.showConsole()
|
||||
@on 'find-in-file', => @commandPanel.show("/")
|
||||
@@ -34,18 +31,53 @@ class RootView extends View
|
||||
|
||||
@commandPanel = new CommandPanel({rootView: this})
|
||||
|
||||
$(window).on 'beforeunload', =>
|
||||
atom.windowStatesByWindowNumber[$windowNumber] = @getWindowState()
|
||||
if projectPath?
|
||||
@project = new Project(projectPath)
|
||||
else if pathToOpen?
|
||||
@project = new Project(fs.directory(pathToOpen))
|
||||
@open(pathToOpen) if fs.isFile(pathToOpen)
|
||||
else if not panesViewState
|
||||
@activeEditor().setBuffer(new Buffer)
|
||||
|
||||
if windowState = atom.windowStatesByWindowNumber[$windowNumber]
|
||||
@setWindowState(windowState)
|
||||
@deserializePanes(panesViewState) if panesViewState
|
||||
|
||||
createProject: (path) ->
|
||||
if path
|
||||
@project = new Project(fs.directory(path))
|
||||
@open(path) if fs.isFile(path)
|
||||
else
|
||||
@activeEditor().setBuffer(new Buffer())
|
||||
serialize: ->
|
||||
projectPath: @project?.path
|
||||
panesViewState: @serializePanes()
|
||||
|
||||
serializePanes: (element = @panes.children(':eq(0)')) ->
|
||||
if element.hasClass('pane')
|
||||
['editor', element.view().content.getEditorState()]
|
||||
else if element.hasClass('row')
|
||||
['row'].concat element.children().toArray().map (elt) =>
|
||||
@serializePanes($(elt))
|
||||
else if element.hasClass('column')
|
||||
['column'].concat element.children().toArray().map (elt) =>
|
||||
@serializePanes($(elt))
|
||||
|
||||
deserializePanes: (panesViewState, parent) ->
|
||||
adjustSplitPanes = false
|
||||
unless parent
|
||||
@panes.empty()
|
||||
adjustSplitPanes = true
|
||||
parent = @panes
|
||||
|
||||
switch panesViewState.shift()
|
||||
when 'editor'
|
||||
editor = new Editor(panesViewState...)
|
||||
parent.append(new Pane(editor))
|
||||
when 'row'
|
||||
row = $$ -> @div class: 'row'
|
||||
parent.append row
|
||||
for child in panesViewState
|
||||
@deserializePanes(child, row)
|
||||
when 'column'
|
||||
column = $$ -> @div class: 'column'
|
||||
parent.append column
|
||||
for child in panesViewState
|
||||
@deserializePanes(child, column)
|
||||
|
||||
@adjustSplitPanes() if adjustSplitPanes
|
||||
|
||||
open: (path) ->
|
||||
@activeEditor().setBuffer(@project.open(path))
|
||||
@@ -80,45 +112,16 @@ class RootView extends View
|
||||
if editor.length
|
||||
editor.view()
|
||||
else
|
||||
editor = new Editor
|
||||
pane = new Pane(editor)
|
||||
@panes.append(pane)
|
||||
editor.focus()
|
||||
editor
|
||||
editor = @panes.find('.editor:first')
|
||||
if editor.length
|
||||
editor.view()
|
||||
else
|
||||
editor = new Editor
|
||||
pane = new Pane(editor)
|
||||
@panes.append(pane)
|
||||
editor.focus()
|
||||
editor
|
||||
|
||||
getWindowState: (element = @panes.children(':eq(0)')) ->
|
||||
if element.hasClass('pane')
|
||||
['editor', element.view().content.getEditorState()]
|
||||
else if element.hasClass('row')
|
||||
['row'].concat element.children().toArray().map (elt) =>
|
||||
@getWindowState($(elt))
|
||||
else if element.hasClass('column')
|
||||
['column'].concat element.children().toArray().map (elt) =>
|
||||
@getWindowState($(elt))
|
||||
|
||||
setWindowState: (windowState, parent) ->
|
||||
adjustSplitPanes = false
|
||||
unless parent
|
||||
@panes.empty()
|
||||
adjustSplitPanes = true
|
||||
parent = @panes
|
||||
|
||||
switch windowState.shift()
|
||||
when 'editor'
|
||||
editor = new Editor(windowState...)
|
||||
parent.append(new Pane(editor))
|
||||
when 'row'
|
||||
row = $$ -> @div class: 'row'
|
||||
parent.append row
|
||||
for child in windowState
|
||||
@setWindowState(child, row)
|
||||
when 'column'
|
||||
column = $$ -> @div class: 'column'
|
||||
parent.append column
|
||||
for child in windowState
|
||||
@setWindowState(child, column)
|
||||
|
||||
@adjustSplitPanes() if adjustSplitPanes
|
||||
|
||||
addPane: (view, sibling, axis, side) ->
|
||||
unless sibling.parent().hasClass(axis)
|
||||
|
||||
@@ -24,6 +24,7 @@ windowAdditions =
|
||||
@attachRootView(path)
|
||||
@loadUserConfiguration()
|
||||
$(window).on 'close', => @close()
|
||||
$(window).on 'beforeunload', => @saveRootViewState()
|
||||
$(window).focus()
|
||||
atom.windowOpened this
|
||||
|
||||
@@ -31,12 +32,17 @@ windowAdditions =
|
||||
@rootView.remove()
|
||||
$(window).unbind('focus')
|
||||
$(window).unbind('blur')
|
||||
$(window).off('before')
|
||||
atom.windowClosed this
|
||||
|
||||
attachRootView: (path) ->
|
||||
@rootView = new RootView {path}
|
||||
attachRootView: (pathToOpen) ->
|
||||
rootViewState = atom.rootViewStates[$windowNumber] or { pathToOpen }
|
||||
@rootView = new RootView(rootViewState)
|
||||
$(@rootViewParentSelector).append @rootView
|
||||
|
||||
saveRootViewState: ->
|
||||
atom.rootViewStates[$windowNumber] = @rootView.serialize()
|
||||
|
||||
loadUserConfiguration: ->
|
||||
try
|
||||
require atom.userConfigurationPath if fs.exists(atom.userConfigurationPath)
|
||||
|
||||
Reference in New Issue
Block a user