Skip to content

Commit

Permalink
Merge pull request atom#14695 from atom/jr-editors-live-in-workspace-…
Browse files Browse the repository at this point in the history
…center

Provide API for observing the active text editor
  • Loading branch information
jasonrudolph authored Jun 6, 2017
2 parents b2c2c52 + e1719a8 commit 268f94b
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 112 deletions.
11 changes: 11 additions & 0 deletions spec/dock-spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/** @babel */

const Grim = require('grim')

import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'

describe('Dock', () => {
Expand Down Expand Up @@ -329,4 +331,13 @@ describe('Dock', () => {
expect(() => atom.workspace.getElement().handleDragStart(dragEvent)).not.toThrow()
})
})

describe('::getActiveTextEditor()', () => {
it('is deprecated', () => {
spyOn(Grim, 'deprecate')

atom.workspace.getLeftDock().getActiveTextEditor()
expect(Grim.deprecate.callCount).toBe(1)
})
})
})
241 changes: 157 additions & 84 deletions spec/workspace-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,35 +31,35 @@ describe('Workspace', () => {

afterEach(() => temp.cleanupSync())

describe('serialization', () => {
const simulateReload = () => {
const workspaceState = atom.workspace.serialize()
const projectState = atom.project.serialize({isUnloading: true})
atom.workspace.destroy()
atom.project.destroy()
atom.project = new Project({
notificationManager: atom.notifications,
packageManager: atom.packages,
confirm: atom.confirm.bind(atom),
applicationDelegate: atom.applicationDelegate
})
atom.project.deserialize(projectState)
atom.workspace = new Workspace({
config: atom.config,
project: atom.project,
packageManager: atom.packages,
grammarRegistry: atom.grammars,
styleManager: atom.styles,
deserializerManager: atom.deserializers,
notificationManager: atom.notifications,
applicationDelegate: atom.applicationDelegate,
viewRegistry: atom.views,
assert: atom.assert.bind(atom),
textEditorRegistry: atom.textEditors
})
return atom.workspace.deserialize(workspaceState, atom.deserializers)
}
const simulateReload = () => {
const workspaceState = workspace.serialize()
const projectState = atom.project.serialize({isUnloading: true})
workspace.destroy()
atom.project.destroy()
atom.project = new Project({
notificationManager: atom.notifications,
packageManager: atom.packages,
confirm: atom.confirm.bind(atom),
applicationDelegate: atom.applicationDelegate
})
atom.project.deserialize(projectState)
workspace = atom.workspace = new Workspace({
config: atom.config,
project: atom.project,
packageManager: atom.packages,
grammarRegistry: atom.grammars,
styleManager: atom.styles,
deserializerManager: atom.deserializers,
notificationManager: atom.notifications,
applicationDelegate: atom.applicationDelegate,
viewRegistry: atom.views,
assert: atom.assert.bind(atom),
textEditorRegistry: atom.textEditors
})
return workspace.deserialize(workspaceState, atom.deserializers)
}

describe('serialization', () => {
describe('when the workspace contains text editors', () => {
it('constructs the view with the same panes', () => {
const pane1 = atom.workspace.getActivePane()
Expand Down Expand Up @@ -120,62 +120,6 @@ describe('Workspace', () => {
expect(atom.workspace.getTextEditors().length).toBe(0)
})
})

describe('where a dock contains an editor', () => {
afterEach(() => {
atom.workspace.getRightDock().paneContainer.destroy()
})

it('constructs the view with the same panes', () => {
const pane1 = atom.workspace.getRightDock().getActivePane()
const pane2 = pane1.splitRight({copyActiveItem: true})
const pane3 = pane2.splitRight({copyActiveItem: true})
let pane4 = null

waitsForPromise(() =>
atom.workspace.open(null, {location: 'right'}).then(editor => editor.setText('An untitled editor.'))
)

waitsForPromise(() =>
atom.workspace.open('b', {location: 'right'}).then(editor => pane2.activateItem(editor.copy()))
)

waitsForPromise(() =>
atom.workspace.open('../sample.js', {location: 'right'}).then(editor => pane3.activateItem(editor))
)

runs(() => {
pane3.activeItem.setCursorScreenPosition([2, 4])
pane4 = pane2.splitDown()
})

waitsForPromise(() =>
atom.workspace.open('../sample.txt', {location: 'right'}).then(editor => pane4.activateItem(editor))
)

runs(() => {
pane4.getActiveItem().setCursorScreenPosition([0, 2])
pane2.activate()

simulateReload()

expect(atom.workspace.getTextEditors().length).toBe(5)
const [editor1, editor2, untitledEditor, editor3, editor4] = atom.workspace.getTextEditors()
const firstDirectory = atom.project.getDirectories()[0]
expect(firstDirectory).toBeDefined()
expect(editor1.getPath()).toBe(firstDirectory.resolve('b'))
expect(editor2.getPath()).toBe(firstDirectory.resolve('../sample.txt'))
expect(editor2.getCursorScreenPosition()).toEqual([0, 2])
expect(editor3.getPath()).toBe(firstDirectory.resolve('b'))
expect(editor4.getPath()).toBe(firstDirectory.resolve('../sample.js'))
expect(editor4.getCursorScreenPosition()).toEqual([2, 4])
expect(untitledEditor.getPath()).toBeUndefined()
expect(untitledEditor.getText()).toBe('An untitled editor.')

expect(atom.workspace.getRightDock().getActiveTextEditor().getPath()).toBe(editor3.getPath())
})
})
})
})

describe('::open(itemOrURI, options)', () => {
Expand Down Expand Up @@ -429,6 +373,13 @@ describe('Workspace', () => {
})
})

describe('when attempting to open an editor in a dock', () => {
it('opens the editor in the workspace center', async () => {
await atom.workspace.open('sample.txt', {location: 'right'})
expect(atom.workspace.getCenter().getActivePaneItem().getFileName()).toEqual('sample.txt')
})
})

describe('when called with an item rather than a URI', () => {
it('adds the item itself to the workspace', async () => {
const item = document.createElement('div')
Expand Down Expand Up @@ -1419,6 +1370,42 @@ describe('Workspace', () => {
})
})

describe('::getActiveTextEditor()', () => {
describe("when the workspace center's active pane item is a text editor", () => {
describe('when the workspace center has focus', function () {
it('returns the text editor', () => {
const workspaceCenter = workspace.getCenter()
const editor = new TextEditor()
workspaceCenter.getActivePane().activateItem(editor)
workspaceCenter.activate()

expect(workspace.getActiveTextEditor()).toBe(editor)
})
})

describe('when a dock has focus', function () {
it('returns the text editor', () => {
const workspaceCenter = workspace.getCenter()
const editor = new TextEditor()
workspaceCenter.getActivePane().activateItem(editor)
workspace.getLeftDock().activate()

expect(workspace.getActiveTextEditor()).toBe(editor)
})
})
})

describe("when the workspace center's active pane item is not a text editor", () => {
it('returns undefined', () => {
const workspaceCenter = workspace.getCenter()
const nonEditorItem = document.createElement('div')
workspaceCenter.getActivePane().activateItem(nonEditorItem)

expect(workspace.getActiveTextEditor()).toBeUndefined()
})
})
})

describe('::observeTextEditors()', () => {
it('invokes the observer with current and future text editors', () => {
const observed = []
Expand All @@ -1435,6 +1422,92 @@ describe('Workspace', () => {
})
})

describe('::observeActiveTextEditor()', () => {
it('invokes the observer with current active text editor and each time a different text editor becomes active', () => {
const pane = workspace.getCenter().getActivePane()
observed = []

const inactiveEditorBeforeRegisteringObserver = new TextEditor()
const activeEditorBeforeRegisteringObserver = new TextEditor()
pane.activateItem(inactiveEditorBeforeRegisteringObserver)
pane.activateItem(activeEditorBeforeRegisteringObserver)

workspace.observeActiveTextEditor(editor => observed.push(editor))

const editorAddedAfterRegisteringObserver = new TextEditor()
const nonEditorItemAddedAfterRegisteringObserver = document.createElement('div')
pane.activateItem(editorAddedAfterRegisteringObserver)

expect(observed).toEqual(
[activeEditorBeforeRegisteringObserver, editorAddedAfterRegisteringObserver]
)
})
})

describe('::onDidChangeActiveTextEditor()', () => {
let center, pane, observed

beforeEach(() => {
center = workspace.getCenter()
pane = center.getActivePane()
observed = []
})

it("invokes the observer when a text editor becomes the workspace center's active pane item while a dock has focus", () => {
workspace.onDidChangeActiveTextEditor(editor => observed.push(editor))

const dock = workspace.getLeftDock()
dock.activate()
expect(atom.workspace.getActivePaneContainer()).toBe(dock)

const editor = new TextEditor()
center.getActivePane().activateItem(editor)
expect(atom.workspace.getActivePaneContainer()).toBe(dock)

expect(observed).toEqual([editor])
})

it('invokes the observer when the last text editor is closed', () => {
const editor = new TextEditor()
pane.activateItem(editor)

workspace.onDidChangeActiveTextEditor(editor => observed.push(editor))
pane.destroyItem(editor)
expect(observed).toEqual([undefined])
})

it("invokes the observer when the workspace center's active pane item changes from an editor item to a non-editor item", () => {
const editor = new TextEditor()
const nonEditorItem = document.createElement('div')
pane.activateItem(editor)

workspace.onDidChangeActiveTextEditor(editor => observed.push(editor))
pane.activateItem(nonEditorItem)
expect(observed).toEqual([undefined])
})

it("does not invoke the observer when the workspace center's active pane item changes from a non-editor item to another non-editor item", () => {
workspace.onDidChangeActiveTextEditor(editor => observed.push(editor))

const nonEditorItem1 = document.createElement('div')
const nonEditorItem2 = document.createElement('div')
pane.activateItem(nonEditorItem1)
pane.activateItem(nonEditorItem1)

expect(observed).toEqual([])
})

it('invokes the observer when closing the one and only text editor after deserialization', async () => {
pane.activateItem(new TextEditor())

simulateReload()

workspace.onDidChangeActiveTextEditor(editor => observed.push(editor))
workspace.closeActivePaneItemOrEmptyPaneOrWindow()
expect(observed).toEqual([undefined])
})
})

describe('when an editor is destroyed', () => {
it('removes the editor', () => {
let editor = null
Expand Down
29 changes: 5 additions & 24 deletions src/dock.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const _ = require('underscore-plus')
const {CompositeDisposable} = require('event-kit')
const PaneContainer = require('./pane-container')
const TextEditor = require('./text-editor')
const Grim = require('grim')

const MINIMUM_SIZE = 100
const DEFAULT_INITIAL_SIZE = 300
Expand Down Expand Up @@ -384,21 +385,6 @@ module.exports = class Dock {
Section: Event Subscription
*/

// Essential: Invoke the given callback with all current and future text
// editors in the dock.
//
// * `callback` {Function} to be called with current and future text editors.
// * `editor` An {TextEditor} that is present in {::getTextEditors} at the time
// of subscription or that is added at some later time.
//
// Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
observeTextEditors (callback) {
for (const textEditor of this.getTextEditors()) {
callback(textEditor)
}
return this.onDidAddTextEditor(({textEditor}) => callback(textEditor))
}

// Essential: Invoke the given callback with all current and future panes items
// in the dock.
//
Expand Down Expand Up @@ -583,18 +569,13 @@ module.exports = class Dock {
return this.paneContainer.getActivePaneItem()
}

// Essential: Get all text editors in the dock.
// Deprecated: Get the active item if it is a {TextEditor}.
//
// Returns an {Array} of {TextEditor}s.
getTextEditors () {
return this.paneContainer.getTextEditors()
}

// Essential: Get the active item if it is an {TextEditor}.
//
// Returns an {TextEditor} or `undefined` if the current active item is not an
// Returns a {TextEditor} or `undefined` if the current active item is not a
// {TextEditor}.
getActiveTextEditor () {
Grim.deprecate('Text editors are not allowed in docks. Use atom.workspace.getActiveTextEditor() instead.')

const activeItem = this.getActivePaneItem()
if (activeItem instanceof TextEditor) { return activeItem }
}
Expand Down
3 changes: 3 additions & 0 deletions src/text-editor.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3628,6 +3628,9 @@ class TextEditor extends Model
})
@component.element

getAllowedLocations: ->
['center']

# Essential: Retrieves the greyed out placeholder of a mini editor.
#
# Returns a {String}.
Expand Down
Loading

0 comments on commit 268f94b

Please sign in to comment.