mirror of
https://github.com/atom/atom.git
synced 2026-02-12 23:55:10 -05:00
Merge branch 'master' into as-double-reflow-measurements
This commit is contained in:
@@ -11,7 +11,6 @@ _ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
{mapSourcePosition} = require 'source-map-support'
|
||||
Model = require './model'
|
||||
{$} = require './space-pen-extensions'
|
||||
WindowEventHandler = require './window-event-handler'
|
||||
StylesElement = require './styles-element'
|
||||
StorageFolder = require './storage-folder'
|
||||
@@ -35,28 +34,12 @@ class Atom extends Model
|
||||
atom.deserializeTimings.atom = Date.now() - startTime
|
||||
|
||||
if includeDeprecatedAPIs
|
||||
workspaceViewDeprecationMessage = """
|
||||
atom.workspaceView is no longer available.
|
||||
In most cases you will not need the view. See the Workspace docs for
|
||||
alternatives: https://atom.io/docs/api/latest/Workspace.
|
||||
If you do need the view, please use `atom.views.getView(atom.workspace)`,
|
||||
which returns an HTMLElement.
|
||||
"""
|
||||
|
||||
serviceHubDeprecationMessage = """
|
||||
atom.services is no longer available. To register service providers and
|
||||
consumers, use the `providedServices` and `consumedServices` fields in
|
||||
your package's package.json.
|
||||
"""
|
||||
|
||||
Object.defineProperty atom, 'workspaceView',
|
||||
get: ->
|
||||
deprecate(workspaceViewDeprecationMessage)
|
||||
atom.__workspaceView
|
||||
set: (newValue) ->
|
||||
deprecate(workspaceViewDeprecationMessage)
|
||||
atom.__workspaceView = newValue
|
||||
|
||||
Object.defineProperty atom, 'services',
|
||||
get: ->
|
||||
deprecate(serviceHubDeprecationMessage)
|
||||
@@ -123,7 +106,7 @@ class Atom extends Model
|
||||
@getCurrentWindow: ->
|
||||
remote.getCurrentWindow()
|
||||
|
||||
workspaceViewParentSelector: 'body'
|
||||
workspaceParentSelectorctor: 'body'
|
||||
lastUncaughtError: null
|
||||
|
||||
###
|
||||
@@ -484,7 +467,7 @@ class Atom extends Model
|
||||
# Extended: Focus the current window.
|
||||
focus: ->
|
||||
ipc.send('call-window-method', 'focus')
|
||||
$(window).focus()
|
||||
window.focus()
|
||||
|
||||
# Extended: Show the current window.
|
||||
show: ->
|
||||
@@ -680,7 +663,6 @@ class Atom extends Model
|
||||
# Essential: Visually and audibly trigger a beep.
|
||||
beep: ->
|
||||
shell.beep() if @config.get('core.audioBeep')
|
||||
@__workspaceView?.trigger 'beep'
|
||||
@emitter.emit 'did-beep'
|
||||
|
||||
# Essential: A flexible way to open a dialog akin to an alert dialog.
|
||||
@@ -761,24 +743,18 @@ class Atom extends Model
|
||||
@project ?= @deserializers.deserialize(@state.project) ? new Project()
|
||||
@deserializeTimings.project = Date.now() - startTime
|
||||
|
||||
deserializeWorkspaceView: ->
|
||||
deserializeWorkspace: ->
|
||||
Workspace = require './workspace'
|
||||
|
||||
if includeDeprecatedAPIs
|
||||
WorkspaceView = require './workspace-view'
|
||||
|
||||
startTime = Date.now()
|
||||
@workspace = Workspace.deserialize(@state.workspace) ? new Workspace
|
||||
|
||||
workspaceElement = @views.getView(@workspace)
|
||||
|
||||
if includeDeprecatedAPIs
|
||||
@__workspaceView = workspaceElement.__spacePenView
|
||||
|
||||
@deserializeTimings.workspace = Date.now() - startTime
|
||||
|
||||
@keymaps.defaultTarget = workspaceElement
|
||||
document.querySelector(@workspaceViewParentSelector).appendChild(workspaceElement)
|
||||
document.querySelector(@workspaceParentSelectorctor).appendChild(workspaceElement)
|
||||
|
||||
deserializePackageStates: ->
|
||||
@packages.packageStates = @state.packageStates ? {}
|
||||
@@ -787,7 +763,7 @@ class Atom extends Model
|
||||
deserializeEditorWindow: ->
|
||||
@deserializePackageStates()
|
||||
@deserializeProject()
|
||||
@deserializeWorkspaceView()
|
||||
@deserializeWorkspace()
|
||||
|
||||
loadConfig: ->
|
||||
@config.setSchema null, {type: 'object', properties: _.clone(require('./config-schema'))}
|
||||
@@ -897,6 +873,11 @@ class Atom extends Model
|
||||
ipc.send('call-window-method', 'setAutoHideMenuBar', autoHide)
|
||||
ipc.send('call-window-method', 'setMenuBarVisibility', not autoHide)
|
||||
|
||||
# Preserve this deprecation until 2.0. Sorry. Should have removed Q sooner.
|
||||
Promise.prototype.done = (callback) ->
|
||||
deprecate("Atom now uses ES6 Promises instead of Q. Call promise.then instead of promise.done")
|
||||
@then(callback)
|
||||
|
||||
if includeDeprecatedAPIs
|
||||
# Deprecated: Callers should be converted to use atom.deserializers
|
||||
Atom::registerRepresentationClass = ->
|
||||
|
||||
@@ -162,7 +162,6 @@ class AtomApplication
|
||||
safeMode: @focusedWindow()?.safeMode
|
||||
|
||||
@on 'application:run-all-specs', -> @runSpecs(exitWhenDone: false, resourcePath: @devResourcePath, safeMode: @focusedWindow()?.safeMode)
|
||||
@on 'application:run-benchmarks', -> @runBenchmarks()
|
||||
@on 'application:quit', -> app.quit()
|
||||
@on 'application:new-window', -> @openPath(_.extend(windowDimensions: @focusedWindow()?.getDimensions(), getLoadSettings()))
|
||||
@on 'application:new-file', -> (@focusedWindow() ? this).openPath()
|
||||
@@ -505,18 +504,6 @@ class AtomApplication
|
||||
safeMode ?= false
|
||||
new AtomWindow({bootstrapScript, resourcePath, exitWhenDone, isSpec, devMode, specDirectory, logFile, safeMode})
|
||||
|
||||
runBenchmarks: ({exitWhenDone, specDirectory}={}) ->
|
||||
try
|
||||
bootstrapScript = require.resolve(path.resolve(@devResourcePath, 'benchmark', 'benchmark-bootstrap'))
|
||||
catch error
|
||||
bootstrapScript = require.resolve(path.resolve(__dirname, '..', '..', 'benchmark', 'benchmark-bootstrap'))
|
||||
|
||||
specDirectory ?= path.dirname(bootstrapScript)
|
||||
|
||||
isSpec = true
|
||||
devMode = true
|
||||
new AtomWindow({bootstrapScript, @resourcePath, exitWhenDone, isSpec, specDirectory, devMode})
|
||||
|
||||
locationForPathToOpen: (pathToOpen, executedFrom='') ->
|
||||
return {pathToOpen} unless pathToOpen
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{Emitter, Disposable, CompositeDisposable} = require 'event-kit'
|
||||
{calculateSpecificity, validateSelector} = require 'clear-cut'
|
||||
_ = require 'underscore-plus'
|
||||
{$} = require './space-pen-extensions'
|
||||
|
||||
SequenceCount = 0
|
||||
|
||||
@@ -138,8 +137,6 @@ class CommandRegistry
|
||||
# * `name` The name of the command. For example, `user:insert-date`.
|
||||
# * `displayName` The display name of the command. For example,
|
||||
# `User: Insert Date`.
|
||||
# * `jQuery` Present if the command was registered with the legacy
|
||||
# `$::command` method.
|
||||
findCommands: ({target}) ->
|
||||
commandNames = new Set
|
||||
commands = []
|
||||
@@ -227,6 +224,9 @@ class CommandRegistry
|
||||
Object.defineProperty dispatchedEvent, 'abortKeyBinding', value: ->
|
||||
event.abortKeyBinding?()
|
||||
|
||||
for key in Object.keys(event)
|
||||
dispatchedEvent[key] = event[key]
|
||||
|
||||
@emitter.emit 'will-dispatch', dispatchedEvent
|
||||
|
||||
loop
|
||||
|
||||
@@ -98,7 +98,6 @@ class CustomGutterComponent
|
||||
delete @decorationItemsById[decorationId]
|
||||
|
||||
if newItem
|
||||
# `item` should be either an HTMLElement or a space-pen View.
|
||||
newItemNode = null
|
||||
if newItem instanceof HTMLElement
|
||||
newItemNode = newItem
|
||||
|
||||
41
src/delegated-listener.js
Normal file
41
src/delegated-listener.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const EventKit = require('event-kit')
|
||||
|
||||
module.exports =
|
||||
function listen (element, eventName, selector, handler) {
|
||||
var innerHandler = function (event) {
|
||||
if (selector) {
|
||||
var currentTarget = event.target
|
||||
while (true) {
|
||||
if (currentTarget.matches && currentTarget.matches(selector)) {
|
||||
handler({
|
||||
type: event.type,
|
||||
currentTarget: currentTarget,
|
||||
target: event.target,
|
||||
preventDefault: function () {
|
||||
event.preventDefault()
|
||||
},
|
||||
originalEvent: event
|
||||
})
|
||||
}
|
||||
if (currentTarget === element) break
|
||||
currentTarget = currentTarget.parentNode
|
||||
}
|
||||
} else {
|
||||
handler({
|
||||
type: event.type,
|
||||
currentTarget: event.currentTarget,
|
||||
target: event.target,
|
||||
preventDefault: function () {
|
||||
event.preventDefault()
|
||||
},
|
||||
originalEvent: event
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
element.addEventListener(eventName, innerHandler)
|
||||
|
||||
return new EventKit.Disposable(function () {
|
||||
element.removeEventListener(eventName, innerHandler)
|
||||
})
|
||||
}
|
||||
@@ -2,7 +2,6 @@ fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
KeymapManager = require 'atom-keymap'
|
||||
CSON = require 'season'
|
||||
{jQuery} = require 'space-pen'
|
||||
Grim = require 'grim'
|
||||
|
||||
bundledKeymaps = require('../package.json')?._atomKeymaps
|
||||
@@ -61,9 +60,4 @@ KeymapManager::subscribeToFileReadFailure = ->
|
||||
|
||||
atom.notifications.addError(message, {detail, dismissable: true})
|
||||
|
||||
# This enables command handlers registered via jQuery to call
|
||||
# `.abortKeyBinding()` on the `jQuery.Event` object passed to the handler.
|
||||
jQuery.Event::abortKeyBinding = ->
|
||||
@originalEvent?.abortKeyBinding?()
|
||||
|
||||
module.exports = KeymapManager
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
{$$} = require 'space-pen'
|
||||
|
||||
CursorsComponent = require './cursors-component'
|
||||
LinesTileComponent = require './lines-tile-component'
|
||||
TiledComponent = require './tiled-component'
|
||||
|
||||
DummyLineNode = $$(-> @div className: 'line', style: 'position: absolute; visibility: hidden;', => @span 'x')[0]
|
||||
DummyLineNode = document.createElement('div')
|
||||
DummyLineNode.className = 'line'
|
||||
DummyLineNode.style.position = 'absolute'
|
||||
DummyLineNode.style.visibility = 'hidden'
|
||||
DummyLineNode.appendChild(document.createElement('span'))
|
||||
DummyLineNode.firstChild.textContent = 'x'
|
||||
|
||||
module.exports =
|
||||
class LinesComponent extends TiledComponent
|
||||
|
||||
@@ -3,7 +3,6 @@ path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
{Emitter} = require 'event-kit'
|
||||
fs = require 'fs-plus'
|
||||
Q = require 'q'
|
||||
Grim = require 'grim'
|
||||
|
||||
ServiceHub = require 'service-hub'
|
||||
@@ -392,7 +391,7 @@ class PackageManager
|
||||
for [activator, types] in @packageActivators
|
||||
packages = @getLoadedPackagesForTypes(types)
|
||||
promises = promises.concat(activator.activatePackages(packages))
|
||||
Q.all(promises).then =>
|
||||
Promise.all(promises).then =>
|
||||
@emit 'activated' if Grim.includeDeprecatedAPIs
|
||||
@emitter.emit 'did-activate-initial-packages'
|
||||
|
||||
@@ -415,14 +414,14 @@ class PackageManager
|
||||
# Activate a single package by name
|
||||
activatePackage: (name) ->
|
||||
if pack = @getActivePackage(name)
|
||||
Q(pack)
|
||||
Promise.resolve(pack)
|
||||
else if pack = @loadPackage(name)
|
||||
pack.activate().then =>
|
||||
@activePackages[pack.name] = pack
|
||||
@emitter.emit 'did-activate-package', pack
|
||||
pack
|
||||
else
|
||||
Q.reject(new Error("Failed to load package '#{name}'"))
|
||||
Promise.reject(new Error("Failed to load package '#{name}'"))
|
||||
|
||||
triggerActivationHook: (hook) ->
|
||||
return new Error("Cannot trigger an empty activation hook") unless hook? and _.isString(hook) and hook.length > 0
|
||||
|
||||
@@ -6,7 +6,6 @@ async = require 'async'
|
||||
CSON = require 'season'
|
||||
fs = require 'fs-plus'
|
||||
{Emitter, CompositeDisposable} = require 'event-kit'
|
||||
Q = require 'q'
|
||||
{includeDeprecatedAPIs, deprecate} = require 'grim'
|
||||
|
||||
ModuleCache = require './module-cache'
|
||||
@@ -139,20 +138,21 @@ class Package
|
||||
|
||||
activate: ->
|
||||
@grammarsPromise ?= @loadGrammars()
|
||||
@activationPromise ?=
|
||||
new Promise (resolve, reject) =>
|
||||
@resolveActivationPromise = resolve
|
||||
@rejectActivationPromise = reject
|
||||
@measure 'activateTime', =>
|
||||
try
|
||||
@activateResources()
|
||||
if @activationShouldBeDeferred()
|
||||
@subscribeToDeferredActivation()
|
||||
else
|
||||
@activateNow()
|
||||
catch error
|
||||
@handleError("Failed to activate the #{@name} package", error)
|
||||
|
||||
unless @activationDeferred?
|
||||
@activationDeferred = Q.defer()
|
||||
@measure 'activateTime', =>
|
||||
try
|
||||
@activateResources()
|
||||
if @activationShouldBeDeferred()
|
||||
@subscribeToDeferredActivation()
|
||||
else
|
||||
@activateNow()
|
||||
catch error
|
||||
@handleError("Failed to activate the #{@name} package", error)
|
||||
|
||||
Q.all([@grammarsPromise, @settingsPromise, @activationDeferred.promise])
|
||||
Promise.all([@grammarsPromise, @settingsPromise, @activationPromise])
|
||||
|
||||
activateNow: ->
|
||||
try
|
||||
@@ -165,7 +165,7 @@ class Package
|
||||
catch error
|
||||
@handleError("Failed to activate the #{@name} package", error)
|
||||
|
||||
@activationDeferred?.resolve()
|
||||
@resolveActivationPromise?()
|
||||
|
||||
activateConfig: ->
|
||||
return if @configActivated
|
||||
@@ -345,7 +345,7 @@ class Package
|
||||
@grammarsActivated = true
|
||||
|
||||
loadGrammars: ->
|
||||
return Q() if @grammarsLoaded
|
||||
return Promise.resolve() if @grammarsLoaded
|
||||
|
||||
loadGrammar = (grammarPath, callback) =>
|
||||
atom.grammars.readGrammar grammarPath, (error, grammar) =>
|
||||
@@ -360,14 +360,13 @@ class Package
|
||||
grammar.activate() if @grammarsActivated
|
||||
callback()
|
||||
|
||||
deferred = Q.defer()
|
||||
grammarsDirPath = path.join(@path, 'grammars')
|
||||
fs.exists grammarsDirPath, (grammarsDirExists) ->
|
||||
return deferred.resolve() unless grammarsDirExists
|
||||
new Promise (resolve) =>
|
||||
grammarsDirPath = path.join(@path, 'grammars')
|
||||
fs.exists grammarsDirPath, (grammarsDirExists) ->
|
||||
return resolve() unless grammarsDirExists
|
||||
|
||||
fs.list grammarsDirPath, ['json', 'cson'], (error, grammarPaths=[]) ->
|
||||
async.each grammarPaths, loadGrammar, -> deferred.resolve()
|
||||
deferred.promise
|
||||
fs.list grammarsDirPath, ['json', 'cson'], (error, grammarPaths=[]) ->
|
||||
async.each grammarPaths, loadGrammar, -> resolve()
|
||||
|
||||
loadSettings: ->
|
||||
@settings = []
|
||||
@@ -383,20 +382,18 @@ class Package
|
||||
settings.activate() if @settingsActivated
|
||||
callback()
|
||||
|
||||
deferred = Q.defer()
|
||||
new Promise (resolve) =>
|
||||
if fs.isDirectorySync(path.join(@path, 'scoped-properties'))
|
||||
settingsDirPath = path.join(@path, 'scoped-properties')
|
||||
deprecate("Store package settings files in the `settings/` directory instead of `scoped-properties/`", packageName: @name)
|
||||
else
|
||||
settingsDirPath = path.join(@path, 'settings')
|
||||
|
||||
if fs.isDirectorySync(path.join(@path, 'scoped-properties'))
|
||||
settingsDirPath = path.join(@path, 'scoped-properties')
|
||||
deprecate("Store package settings files in the `settings/` directory instead of `scoped-properties/`", packageName: @name)
|
||||
else
|
||||
settingsDirPath = path.join(@path, 'settings')
|
||||
fs.exists settingsDirPath, (settingsDirExists) ->
|
||||
return resolve() unless settingsDirExists
|
||||
|
||||
fs.exists settingsDirPath, (settingsDirExists) ->
|
||||
return deferred.resolve() unless settingsDirExists
|
||||
|
||||
fs.list settingsDirPath, ['json', 'cson'], (error, settingsPaths=[]) ->
|
||||
async.each settingsPaths, loadSettingsFile, -> deferred.resolve()
|
||||
deferred.promise
|
||||
fs.list settingsDirPath, ['json', 'cson'], (error, settingsPaths=[]) ->
|
||||
async.each settingsPaths, loadSettingsFile, -> resolve()
|
||||
|
||||
serialize: ->
|
||||
if @mainActivated
|
||||
@@ -406,8 +403,10 @@ class Package
|
||||
console.error "Error serializing package '#{@name}'", e.stack
|
||||
|
||||
deactivate: ->
|
||||
@activationDeferred?.reject()
|
||||
@activationDeferred = null
|
||||
@rejectActivationPromise?()
|
||||
@activationPromise = null
|
||||
@resolveActivationPromise = null
|
||||
@rejectActivationPromise = null
|
||||
@activationCommandSubscriptions?.dispose()
|
||||
@deactivateResources()
|
||||
@deactivateConfig()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{CompositeDisposable} = require 'event-kit'
|
||||
{callAttachHooks} = require './space-pen-extensions'
|
||||
PaneResizeHandleElement = require './pane-resize-handle-element'
|
||||
|
||||
class PaneAxisElement extends HTMLElement
|
||||
@@ -43,8 +42,6 @@ class PaneAxisElement extends HTMLElement
|
||||
resizeHandle = document.createElement('atom-pane-resize-handle')
|
||||
@insertBefore(resizeHandle, nextElement)
|
||||
|
||||
callAttachHooks(view) # for backward compatibility with SpacePen views
|
||||
|
||||
childRemoved: ({child}) ->
|
||||
view = atom.views.getView(child)
|
||||
siblingView = view.previousSibling
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
{CompositeDisposable} = require 'event-kit'
|
||||
Grim = require 'grim'
|
||||
{callAttachHooks} = require './space-pen-extensions'
|
||||
PaneContainerView = null
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
module.exports =
|
||||
@@ -10,13 +8,8 @@ class PaneContainerElement extends HTMLElement
|
||||
@subscriptions = new CompositeDisposable
|
||||
@classList.add 'panes'
|
||||
|
||||
if Grim.includeDeprecatedAPIs
|
||||
PaneContainerView ?= require './pane-container-view'
|
||||
@__spacePenView = new PaneContainerView(this)
|
||||
|
||||
initialize: (@model) ->
|
||||
@subscriptions.add @model.observeRoot(@rootChanged.bind(this))
|
||||
@__spacePenView.setModel(@model) if Grim.includeDeprecatedAPIs
|
||||
this
|
||||
|
||||
rootChanged: (root) ->
|
||||
@@ -25,7 +18,6 @@ class PaneContainerElement extends HTMLElement
|
||||
if root?
|
||||
view = atom.views.getView(root)
|
||||
@appendChild(view)
|
||||
callAttachHooks(view)
|
||||
focusedElement?.focus()
|
||||
|
||||
hasFocus: ->
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
{deprecate} = require 'grim'
|
||||
Delegator = require 'delegato'
|
||||
{CompositeDisposable} = require 'event-kit'
|
||||
{$, View, callAttachHooks} = require './space-pen-extensions'
|
||||
PaneView = require './pane-view'
|
||||
PaneContainer = require './pane-container'
|
||||
|
||||
# Manages the list of panes within a {WorkspaceView}
|
||||
module.exports =
|
||||
class PaneContainerView extends View
|
||||
Delegator.includeInto(this)
|
||||
|
||||
@delegatesMethod 'saveAll', toProperty: 'model'
|
||||
|
||||
@content: ->
|
||||
@div class: 'panes'
|
||||
|
||||
constructor: (@element) ->
|
||||
super
|
||||
@subscriptions = new CompositeDisposable
|
||||
|
||||
setModel: (@model) ->
|
||||
@subscriptions.add @model.onDidChangeActivePaneItem(@onActivePaneItemChanged)
|
||||
|
||||
getRoot: ->
|
||||
view = atom.views.getView(@model.getRoot())
|
||||
view.__spacePenView ? view
|
||||
|
||||
onActivePaneItemChanged: (activeItem) =>
|
||||
@trigger 'pane-container:active-pane-item-changed', [activeItem]
|
||||
|
||||
confirmClose: ->
|
||||
@model.confirmClose()
|
||||
|
||||
getPaneViews: ->
|
||||
@find('atom-pane').views()
|
||||
|
||||
indexOfPane: (paneView) ->
|
||||
@getPaneViews().indexOf(paneView.view())
|
||||
|
||||
paneAtIndex: (index) ->
|
||||
@getPaneViews()[index]
|
||||
|
||||
eachPaneView: (callback) ->
|
||||
callback(paneView) for paneView in @getPaneViews()
|
||||
paneViewAttached = (e) -> callback($(e.target).view())
|
||||
@on 'pane:attached', paneViewAttached
|
||||
off: => @off 'pane:attached', paneViewAttached
|
||||
|
||||
getFocusedPane: ->
|
||||
@find('atom-pane:has(:focus)').view()
|
||||
|
||||
getActivePane: ->
|
||||
deprecate("Use PaneContainerView::getActivePaneView instead.")
|
||||
@getActivePaneView()
|
||||
|
||||
getActivePaneView: ->
|
||||
atom.views.getView(@model.getActivePane()).__spacePenView
|
||||
|
||||
getActivePaneItem: ->
|
||||
@model.getActivePaneItem()
|
||||
|
||||
getActiveView: ->
|
||||
@getActivePaneView()?.activeView
|
||||
|
||||
paneForUri: (uri) ->
|
||||
atom.views.getView(@model.paneForURI(uri)).__spacePenView
|
||||
|
||||
focusNextPaneView: ->
|
||||
@model.activateNextPane()
|
||||
|
||||
focusPreviousPaneView: ->
|
||||
@model.activatePreviousPane()
|
||||
|
||||
focusPaneViewAbove: ->
|
||||
@element.focusPaneViewAbove()
|
||||
|
||||
focusPaneViewBelow: ->
|
||||
@element.focusPaneViewBelow()
|
||||
|
||||
focusPaneViewOnLeft: ->
|
||||
@element.focusPaneViewOnLeft()
|
||||
|
||||
focusPaneViewOnRight: ->
|
||||
@element.focusPaneViewOnRight()
|
||||
|
||||
getPanes: ->
|
||||
deprecate("Use PaneContainerView::getPaneViews() instead")
|
||||
@getPaneViews()
|
||||
@@ -1,8 +1,6 @@
|
||||
path = require 'path'
|
||||
{CompositeDisposable} = require 'event-kit'
|
||||
Grim = require 'grim'
|
||||
{$, callAttachHooks, callRemoveHooks} = require './space-pen-extensions'
|
||||
PaneView = null
|
||||
|
||||
class PaneElement extends HTMLElement
|
||||
attached: false
|
||||
@@ -14,7 +12,6 @@ class PaneElement extends HTMLElement
|
||||
|
||||
@initializeContent()
|
||||
@subscribeToDOMEvents()
|
||||
@createSpacePenShim() if Grim.includeDeprecatedAPIs
|
||||
|
||||
attachedCallback: ->
|
||||
@attached = true
|
||||
@@ -55,10 +52,6 @@ class PaneElement extends HTMLElement
|
||||
@addEventListener 'dragover', handleDragOver
|
||||
@addEventListener 'drop', handleDrop
|
||||
|
||||
createSpacePenShim: ->
|
||||
PaneView ?= require './pane-view'
|
||||
@__spacePenView = new PaneView(this)
|
||||
|
||||
initialize: (@model) ->
|
||||
@subscriptions.add @model.onDidActivate(@activated.bind(this))
|
||||
@subscriptions.add @model.observeActive(@activeStatusChanged.bind(this))
|
||||
@@ -66,8 +59,6 @@ class PaneElement extends HTMLElement
|
||||
@subscriptions.add @model.onDidRemoveItem(@itemRemoved.bind(this))
|
||||
@subscriptions.add @model.onDidDestroy(@paneDestroyed.bind(this))
|
||||
@subscriptions.add @model.observeFlexScale(@flexScaleChanged.bind(this))
|
||||
|
||||
@__spacePenView.setModel(@model) if Grim.includeDeprecatedAPIs
|
||||
this
|
||||
|
||||
getModel: -> @model
|
||||
@@ -96,7 +87,6 @@ class PaneElement extends HTMLElement
|
||||
|
||||
unless @itemViews.contains(itemView)
|
||||
@itemViews.appendChild(itemView)
|
||||
callAttachHooks(itemView)
|
||||
|
||||
for child in @itemViews.children
|
||||
if child is itemView
|
||||
@@ -121,7 +111,6 @@ class PaneElement extends HTMLElement
|
||||
|
||||
itemRemoved: ({item, index, destroyed}) ->
|
||||
if viewToRemove = atom.views.getView(item)
|
||||
callRemoveHooks(viewToRemove) if destroyed
|
||||
viewToRemove.remove()
|
||||
|
||||
paneDestroyed: ->
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
{$, View} = require './space-pen-extensions'
|
||||
Delegator = require 'delegato'
|
||||
{deprecate} = require 'grim'
|
||||
{CompositeDisposable} = require 'event-kit'
|
||||
PropertyAccessors = require 'property-accessors'
|
||||
|
||||
Pane = require './pane'
|
||||
|
||||
# A container which can contains multiple items to be switched between.
|
||||
#
|
||||
# Items can be almost anything however most commonly they're {TextEditorView}s.
|
||||
#
|
||||
# Most packages won't need to use this class, unless you're interested in
|
||||
# building a package that deals with switching between panes or items.
|
||||
module.exports =
|
||||
class PaneView extends View
|
||||
Delegator.includeInto(this)
|
||||
PropertyAccessors.includeInto(this)
|
||||
|
||||
@delegatesProperties 'items', 'activeItem', toProperty: 'model'
|
||||
@delegatesMethods 'getItems', 'activateNextItem', 'activatePreviousItem', 'getActiveItemIndex',
|
||||
'activateItemAtIndex', 'activateItem', 'addItem', 'itemAtIndex', 'moveItem', 'moveItemToPane',
|
||||
'destroyItem', 'destroyItems', 'destroyActiveItem', 'destroyInactiveItems',
|
||||
'saveActiveItem', 'saveActiveItemAs', 'saveItem', 'saveItemAs', 'saveItems',
|
||||
'itemForUri', 'activateItemForUri', 'promptToSaveItem', 'copyActiveItem', 'isActive',
|
||||
'activate', 'getActiveItem', toProperty: 'model'
|
||||
|
||||
previousActiveItem: null
|
||||
attached: false
|
||||
|
||||
constructor: (@element) ->
|
||||
@itemViews = $(element.itemViews)
|
||||
super
|
||||
|
||||
setModel: (@model) ->
|
||||
@subscriptions = new CompositeDisposable
|
||||
@subscriptions.add @model.observeActiveItem(@onActiveItemChanged)
|
||||
@subscriptions.add @model.onDidAddItem(@onItemAdded)
|
||||
@subscriptions.add @model.onDidRemoveItem(@onItemRemoved)
|
||||
@subscriptions.add @model.onDidMoveItem(@onItemMoved)
|
||||
@subscriptions.add @model.onWillDestroyItem(@onBeforeItemDestroyed)
|
||||
@subscriptions.add @model.observeActive(@onActiveStatusChanged)
|
||||
@subscriptions.add @model.onDidDestroy(@onPaneDestroyed)
|
||||
|
||||
afterAttach: ->
|
||||
@container ?= @closest('atom-pane-container').view()
|
||||
@trigger('pane:attached', [this]) unless @attached
|
||||
@attached = true
|
||||
|
||||
onPaneDestroyed: =>
|
||||
@container?.trigger 'pane:removed', [this]
|
||||
@subscriptions.dispose()
|
||||
|
||||
remove: ->
|
||||
@model.destroy() unless @model.isDestroyed()
|
||||
|
||||
# Essential: Returns the {Pane} model underlying this pane view
|
||||
getModel: -> @model
|
||||
|
||||
# Deprecated: Use ::destroyItem
|
||||
removeItem: (item) ->
|
||||
deprecate("Use PaneView::destroyItem instead")
|
||||
@destroyItem(item)
|
||||
|
||||
# Deprecated: Use ::activateItem
|
||||
showItem: (item) ->
|
||||
deprecate("Use PaneView::activateItem instead")
|
||||
@activateItem(item)
|
||||
|
||||
# Deprecated: Use ::activateItemForUri
|
||||
showItemForUri: (item) ->
|
||||
deprecate("Use PaneView::activateItemForUri instead")
|
||||
@activateItemForUri(item)
|
||||
|
||||
# Deprecated: Use ::activateItemAtIndex
|
||||
showItemAtIndex: (index) ->
|
||||
deprecate("Use PaneView::activateItemAtIndex instead")
|
||||
@activateItemAtIndex(index)
|
||||
|
||||
# Deprecated: Use ::activateNextItem
|
||||
showNextItem: ->
|
||||
deprecate("Use PaneView::activateNextItem instead")
|
||||
@activateNextItem()
|
||||
|
||||
# Deprecated: Use ::activatePreviousItem
|
||||
showPreviousItem: ->
|
||||
deprecate("Use PaneView::activatePreviousItem instead")
|
||||
@activatePreviousItem()
|
||||
|
||||
onActiveStatusChanged: (active) =>
|
||||
if active
|
||||
@trigger 'pane:became-active'
|
||||
else
|
||||
@trigger 'pane:became-inactive'
|
||||
|
||||
# Public: Returns the next pane, ordered by creation.
|
||||
getNextPane: ->
|
||||
panes = @container?.getPaneViews()
|
||||
return unless panes.length > 1
|
||||
nextIndex = (panes.indexOf(this) + 1) % panes.length
|
||||
panes[nextIndex]
|
||||
|
||||
getActivePaneItem: ->
|
||||
@activeItem
|
||||
|
||||
onActiveItemChanged: (item) =>
|
||||
@activeItemDisposables.dispose() if @activeItemDisposables?
|
||||
@activeItemDisposables = new CompositeDisposable()
|
||||
|
||||
if @previousActiveItem?.off?
|
||||
@previousActiveItem.off 'title-changed', @activeItemTitleChanged
|
||||
@previousActiveItem.off 'modified-status-changed', @activeItemModifiedChanged
|
||||
@previousActiveItem = item
|
||||
|
||||
return unless item?
|
||||
|
||||
if item.onDidChangeTitle?
|
||||
disposable = item.onDidChangeTitle(@activeItemTitleChanged)
|
||||
@activeItemDisposables.add(disposable) if disposable?.dispose?
|
||||
else if item.on?
|
||||
disposable = item.on('title-changed', @activeItemTitleChanged)
|
||||
@activeItemDisposables.add(disposable) if disposable?.dispose?
|
||||
|
||||
if item.onDidChangeModified?
|
||||
disposable = item.onDidChangeModified(@activeItemModifiedChanged)
|
||||
@activeItemDisposables.add(disposable) if disposable?.dispose?
|
||||
else if item.on?
|
||||
item.on('modified-status-changed', @activeItemModifiedChanged)
|
||||
@activeItemDisposables.add(disposable) if disposable?.dispose?
|
||||
|
||||
@trigger 'pane:active-item-changed', [item]
|
||||
|
||||
onItemAdded: ({item, index}) =>
|
||||
@trigger 'pane:item-added', [item, index]
|
||||
|
||||
onItemRemoved: ({item, index, destroyed}) =>
|
||||
@trigger 'pane:item-removed', [item, index]
|
||||
|
||||
onItemMoved: ({item, newIndex}) =>
|
||||
@trigger 'pane:item-moved', [item, newIndex]
|
||||
|
||||
onBeforeItemDestroyed: ({item}) =>
|
||||
@unsubscribe(item) if typeof item.off is 'function'
|
||||
@trigger 'pane:before-item-destroyed', [item]
|
||||
|
||||
activeItemTitleChanged: =>
|
||||
@trigger 'pane:active-item-title-changed'
|
||||
|
||||
activeItemModifiedChanged: =>
|
||||
@trigger 'pane:active-item-modified-status-changed'
|
||||
|
||||
@::accessor 'activeView', ->
|
||||
element = atom.views.getView(@activeItem)
|
||||
$(element).view() ? element
|
||||
|
||||
splitLeft: (items...) -> atom.views.getView(@model.splitLeft({items})).__spacePenView
|
||||
|
||||
splitRight: (items...) -> atom.views.getView(@model.splitRight({items})).__spacePenView
|
||||
|
||||
splitUp: (items...) -> atom.views.getView(@model.splitUp({items})).__spacePenView
|
||||
|
||||
splitDown: (items...) -> atom.views.getView(@model.splitDown({items})).__spacePenView
|
||||
|
||||
getContainer: -> @closest('atom-pane-container').view()
|
||||
|
||||
focus: ->
|
||||
@element.focus()
|
||||
@@ -1,5 +1,4 @@
|
||||
{CompositeDisposable} = require 'event-kit'
|
||||
{callAttachHooks} = require './space-pen-extensions'
|
||||
Panel = require './panel'
|
||||
|
||||
class PanelElement extends HTMLElement
|
||||
@@ -21,7 +20,6 @@ class PanelElement extends HTMLElement
|
||||
atom.views.getView(@getModel().getItem())
|
||||
|
||||
attachedCallback: ->
|
||||
callAttachHooks(@getItemView()) # for backward compatibility with SpacePen views
|
||||
@visibleChanged(@getModel().isVisible())
|
||||
|
||||
visibleChanged: (visible) ->
|
||||
|
||||
@@ -3,7 +3,6 @@ url = require 'url'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
Q = require 'q'
|
||||
{includeDeprecatedAPIs, deprecate} = require 'grim'
|
||||
{Emitter} = require 'event-kit'
|
||||
Serializable = require 'serializable'
|
||||
@@ -367,8 +366,11 @@ class Project extends Model
|
||||
#
|
||||
# Returns a promise that resolves to the {TextBuffer}.
|
||||
bufferForPath: (absoluteFilePath) ->
|
||||
existingBuffer = @findBufferForPath(absoluteFilePath) if absoluteFilePath
|
||||
Q(existingBuffer ? @buildBuffer(absoluteFilePath))
|
||||
existingBuffer = @findBufferForPath(absoluteFilePath) if absoluteFilePath?
|
||||
if existingBuffer
|
||||
Promise.resolve(existingBuffer)
|
||||
else
|
||||
@buildBuffer(absoluteFilePath)
|
||||
|
||||
bufferForId: (id) ->
|
||||
_.find @buffers, (buffer) -> buffer.id is id
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
{View} = require './space-pen-extensions'
|
||||
|
||||
# Deprecated: Represents a view that scrolls.
|
||||
#
|
||||
# Handles several core events to update scroll position:
|
||||
#
|
||||
# * `core:move-up` Scrolls the view up
|
||||
# * `core:move-down` Scrolls the view down
|
||||
# * `core:page-up` Scrolls the view up by the height of the page
|
||||
# * `core:page-down` Scrolls the view down by the height of the page
|
||||
# * `core:move-to-top` Scrolls the editor to the top
|
||||
# * `core:move-to-bottom` Scroll the editor to the bottom
|
||||
#
|
||||
# Subclasses must call `super` if overriding the `initialize` method.
|
||||
#
|
||||
# ## Examples
|
||||
#
|
||||
# ```coffee
|
||||
# {ScrollView} = require 'atom'
|
||||
#
|
||||
# class MyView extends ScrollView
|
||||
# @content: ->
|
||||
# @div()
|
||||
#
|
||||
# initialize: ->
|
||||
# super
|
||||
# @text('super long content that will scroll')
|
||||
# ```
|
||||
#
|
||||
module.exports =
|
||||
class ScrollView extends View
|
||||
initialize: ->
|
||||
@on 'core:move-up', => @scrollUp()
|
||||
@on 'core:move-down', => @scrollDown()
|
||||
@on 'core:page-up', => @pageUp()
|
||||
@on 'core:page-down', => @pageDown()
|
||||
@on 'core:move-to-top', => @scrollToTop()
|
||||
@on 'core:move-to-bottom', => @scrollToBottom()
|
||||
@@ -1,312 +0,0 @@
|
||||
{$, View} = require './space-pen-extensions'
|
||||
TextEditorView = require './text-editor-view'
|
||||
fuzzyFilter = require('fuzzaldrin').filter
|
||||
|
||||
# Deprecated: Provides a view that renders a list of items with an editor that
|
||||
# filters the items. Used by many packages such as the fuzzy-finder,
|
||||
# command-palette, symbols-view and autocomplete.
|
||||
#
|
||||
# Subclasses must implement the following methods:
|
||||
#
|
||||
# * {::viewForItem}
|
||||
# * {::confirmed}
|
||||
#
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {SelectListView} = require 'atom'
|
||||
#
|
||||
# class MySelectListView extends SelectListView
|
||||
# initialize: ->
|
||||
# super
|
||||
# @addClass('overlay from-top')
|
||||
# @setItems(['Hello', 'World'])
|
||||
# atom.workspaceView.append(this)
|
||||
# @focusFilterEditor()
|
||||
#
|
||||
# viewForItem: (item) ->
|
||||
# "<li>#{item}</li>"
|
||||
#
|
||||
# confirmed: (item) ->
|
||||
# console.log("#{item} was selected")
|
||||
# ```
|
||||
module.exports =
|
||||
class SelectListView extends View
|
||||
@content: ->
|
||||
@div class: 'select-list', =>
|
||||
@subview 'filterEditorView', new TextEditorView(mini: true)
|
||||
@div class: 'error-message', outlet: 'error'
|
||||
@div class: 'loading', outlet: 'loadingArea', =>
|
||||
@span class: 'loading-message', outlet: 'loading'
|
||||
@span class: 'badge', outlet: 'loadingBadge'
|
||||
@ol class: 'list-group', outlet: 'list'
|
||||
|
||||
maxItems: Infinity
|
||||
scheduleTimeout: null
|
||||
inputThrottle: 50
|
||||
cancelling: false
|
||||
|
||||
###
|
||||
Section: Construction
|
||||
###
|
||||
|
||||
# Essential: Initialize the select list view.
|
||||
#
|
||||
# This method can be overridden by subclasses but `super` should always
|
||||
# be called.
|
||||
initialize: ->
|
||||
@filterEditorView.getEditor().getBuffer().onDidChange =>
|
||||
@schedulePopulateList()
|
||||
@filterEditorView.on 'blur', =>
|
||||
@cancel() unless @cancelling
|
||||
|
||||
# This prevents the focusout event from firing on the filter editor view
|
||||
# when the list is scrolled by clicking the scrollbar and dragging.
|
||||
@list.on 'mousedown', ({target}) =>
|
||||
false if target is @list[0]
|
||||
|
||||
@on 'core:move-up', =>
|
||||
@selectPreviousItemView()
|
||||
@on 'core:move-down', =>
|
||||
@selectNextItemView()
|
||||
@on 'core:move-to-top', =>
|
||||
@selectItemView(@list.find('li:first'))
|
||||
@list.scrollToTop()
|
||||
false
|
||||
@on 'core:move-to-bottom', =>
|
||||
@selectItemView(@list.find('li:last'))
|
||||
@list.scrollToBottom()
|
||||
false
|
||||
|
||||
@on 'core:confirm', => @confirmSelection()
|
||||
@on 'core:cancel', => @cancel()
|
||||
|
||||
@list.on 'mousedown', 'li', (e) =>
|
||||
@selectItemView($(e.target).closest('li'))
|
||||
e.preventDefault()
|
||||
|
||||
@list.on 'mouseup', 'li', (e) =>
|
||||
@confirmSelection() if $(e.target).closest('li').hasClass('selected')
|
||||
e.preventDefault()
|
||||
|
||||
###
|
||||
Section: Methods that must be overridden
|
||||
###
|
||||
|
||||
# Essential: Create a view for the given model item.
|
||||
#
|
||||
# This method must be overridden by subclasses.
|
||||
#
|
||||
# This is called when the item is about to appended to the list view.
|
||||
#
|
||||
# * `item` The model item being rendered. This will always be one of the items
|
||||
# previously passed to {::setItems}.
|
||||
#
|
||||
# Returns a String of HTML, DOM element, jQuery object, or View.
|
||||
viewForItem: (item) ->
|
||||
throw new Error("Subclass must implement a viewForItem(item) method")
|
||||
|
||||
# Essential: Callback function for when an item is selected.
|
||||
#
|
||||
# This method must be overridden by subclasses.
|
||||
#
|
||||
# * `item` The selected model item. This will always be one of the items
|
||||
# previously passed to {::setItems}.
|
||||
#
|
||||
# Returns a DOM element, jQuery object, or {View}.
|
||||
confirmed: (item) ->
|
||||
throw new Error("Subclass must implement a confirmed(item) method")
|
||||
|
||||
###
|
||||
Section: Managing the list of items
|
||||
###
|
||||
|
||||
# Essential: Set the array of items to display in the list.
|
||||
#
|
||||
# This should be model items not actual views. {::viewForItem} will be
|
||||
# called to render the item when it is being appended to the list view.
|
||||
#
|
||||
# * `items` The {Array} of model items to display in the list (default: []).
|
||||
setItems: (@items=[]) ->
|
||||
@populateList()
|
||||
@setLoading()
|
||||
|
||||
# Essential: Get the model item that is currently selected in the list view.
|
||||
#
|
||||
# Returns a model item.
|
||||
getSelectedItem: ->
|
||||
@getSelectedItemView().data('select-list-item')
|
||||
|
||||
# Extended: Get the property name to use when filtering items.
|
||||
#
|
||||
# This method may be overridden by classes to allow fuzzy filtering based
|
||||
# on a specific property of the item objects.
|
||||
#
|
||||
# For example if the objects you pass to {::setItems} are of the type
|
||||
# `{"id": 3, "name": "Atom"}` then you would return `"name"` from this method
|
||||
# to fuzzy filter by that property when text is entered into this view's
|
||||
# editor.
|
||||
#
|
||||
# Returns the property name to fuzzy filter by.
|
||||
getFilterKey: ->
|
||||
|
||||
# Extended: Get the filter query to use when fuzzy filtering the visible
|
||||
# elements.
|
||||
#
|
||||
# By default this method returns the text in the mini editor but it can be
|
||||
# overridden by subclasses if needed.
|
||||
#
|
||||
# Returns a {String} to use when fuzzy filtering the elements to display.
|
||||
getFilterQuery: ->
|
||||
@filterEditorView.getEditor().getText()
|
||||
|
||||
# Extended: Set the maximum numbers of items to display in the list.
|
||||
#
|
||||
# * `maxItems` The maximum {Number} of items to display.
|
||||
setMaxItems: (@maxItems) ->
|
||||
|
||||
# Extended: Populate the list view with the model items previously set by
|
||||
# calling {::setItems}.
|
||||
#
|
||||
# Subclasses may override this method but should always call `super`.
|
||||
populateList: ->
|
||||
return unless @items?
|
||||
|
||||
filterQuery = @getFilterQuery()
|
||||
if filterQuery.length
|
||||
filteredItems = fuzzyFilter(@items, filterQuery, key: @getFilterKey())
|
||||
else
|
||||
filteredItems = @items
|
||||
|
||||
@list.empty()
|
||||
if filteredItems.length
|
||||
@setError(null)
|
||||
|
||||
for i in [0...Math.min(filteredItems.length, @maxItems)]
|
||||
item = filteredItems[i]
|
||||
itemView = $(@viewForItem(item))
|
||||
itemView.data('select-list-item', item)
|
||||
@list.append(itemView)
|
||||
|
||||
@selectItemView(@list.find('li:first'))
|
||||
else
|
||||
@setError(@getEmptyMessage(@items.length, filteredItems.length))
|
||||
|
||||
###
|
||||
Section: Messages to the user
|
||||
###
|
||||
|
||||
# Essential: Set the error message to display.
|
||||
#
|
||||
# * `message` The {String} error message (default: '').
|
||||
setError: (message='') ->
|
||||
if message.length is 0
|
||||
@error.text('').hide()
|
||||
else
|
||||
@setLoading()
|
||||
@error.text(message).show()
|
||||
|
||||
# Essential: Set the loading message to display.
|
||||
#
|
||||
# * `message` The {String} loading message (default: '').
|
||||
setLoading: (message='') ->
|
||||
if message.length is 0
|
||||
@loading.text("")
|
||||
@loadingBadge.text("")
|
||||
@loadingArea.hide()
|
||||
else
|
||||
@setError()
|
||||
@loading.text(message)
|
||||
@loadingArea.show()
|
||||
|
||||
# Extended: Get the message to display when there are no items.
|
||||
#
|
||||
# Subclasses may override this method to customize the message.
|
||||
#
|
||||
# * `itemCount` The {Number} of items in the array specified to {::setItems}
|
||||
# * `filteredItemCount` The {Number} of items that pass the fuzzy filter test.
|
||||
#
|
||||
# Returns a {String} message (default: 'No matches found').
|
||||
getEmptyMessage: (itemCount, filteredItemCount) -> 'No matches found'
|
||||
|
||||
###
|
||||
Section: View Actions
|
||||
###
|
||||
|
||||
# Essential: Cancel and close this select list view.
|
||||
#
|
||||
# This restores focus to the previously focused element if
|
||||
# {::storeFocusedElement} was called prior to this view being attached.
|
||||
cancel: ->
|
||||
@list.empty()
|
||||
@cancelling = true
|
||||
filterEditorViewFocused = @filterEditorView.isFocused
|
||||
@cancelled()
|
||||
@detach()
|
||||
@restoreFocus() if filterEditorViewFocused
|
||||
@cancelling = false
|
||||
clearTimeout(@scheduleTimeout)
|
||||
|
||||
# Extended: Focus the fuzzy filter editor view.
|
||||
focusFilterEditor: ->
|
||||
@filterEditorView.focus()
|
||||
|
||||
# Extended: Store the currently focused element. This element will be given
|
||||
# back focus when {::cancel} is called.
|
||||
storeFocusedElement: ->
|
||||
@previouslyFocusedElement = $(document.activeElement)
|
||||
|
||||
###
|
||||
Section: Private
|
||||
###
|
||||
|
||||
selectPreviousItemView: ->
|
||||
view = @getSelectedItemView().prev()
|
||||
view = @list.find('li:last') unless view.length
|
||||
@selectItemView(view)
|
||||
|
||||
selectNextItemView: ->
|
||||
view = @getSelectedItemView().next()
|
||||
view = @list.find('li:first') unless view.length
|
||||
@selectItemView(view)
|
||||
|
||||
selectItemView: (view) ->
|
||||
return unless view.length
|
||||
@list.find('.selected').removeClass('selected')
|
||||
view.addClass('selected')
|
||||
@scrollToItemView(view)
|
||||
|
||||
scrollToItemView: (view) ->
|
||||
scrollTop = @list.scrollTop()
|
||||
desiredTop = view.position().top + scrollTop
|
||||
desiredBottom = desiredTop + view.outerHeight()
|
||||
|
||||
if desiredTop < scrollTop
|
||||
@list.scrollTop(desiredTop)
|
||||
else if desiredBottom > @list.scrollBottom()
|
||||
@list.scrollBottom(desiredBottom)
|
||||
|
||||
restoreFocus: ->
|
||||
if @previouslyFocusedElement?.isOnDom()
|
||||
@previouslyFocusedElement.focus()
|
||||
else
|
||||
atom.workspaceView.focus()
|
||||
|
||||
cancelled: ->
|
||||
@filterEditorView.getEditor().setText('')
|
||||
|
||||
getSelectedItemView: ->
|
||||
@list.find('li.selected')
|
||||
|
||||
confirmSelection: ->
|
||||
item = @getSelectedItem()
|
||||
if item?
|
||||
@confirmed(item)
|
||||
else
|
||||
@cancel()
|
||||
|
||||
schedulePopulateList: ->
|
||||
clearTimeout(@scheduleTimeout)
|
||||
populateCallback = =>
|
||||
@populateList() if @isOnDom()
|
||||
@scheduleTimeout = setTimeout(populateCallback, @inputThrottle)
|
||||
@@ -257,10 +257,15 @@ class Selection extends Model
|
||||
@modifySelection => @cursor.moveToFirstCharacterOfLine()
|
||||
|
||||
# Public: Selects all the text from the current cursor position to the end of
|
||||
# the line.
|
||||
# the screen line.
|
||||
selectToEndOfLine: ->
|
||||
@modifySelection => @cursor.moveToEndOfScreenLine()
|
||||
|
||||
# Public: Selects all the text from the current cursor position to the end of
|
||||
# the buffer line.
|
||||
selectToEndOfBufferLine: ->
|
||||
@modifySelection => @cursor.moveToEndOfLine()
|
||||
|
||||
# Public: Selects all the text from the current cursor position to the
|
||||
# beginning of the word.
|
||||
selectToBeginningOfWord: ->
|
||||
@@ -572,11 +577,16 @@ class Selection extends Model
|
||||
toggleLineComments: ->
|
||||
@editor.toggleLineCommentsForBufferRows(@getBufferRowRange()...)
|
||||
|
||||
# Public: Cuts the selection until the end of the line.
|
||||
# Public: Cuts the selection until the end of the screen line.
|
||||
cutToEndOfLine: (maintainClipboard) ->
|
||||
@selectToEndOfLine() if @isEmpty()
|
||||
@cut(maintainClipboard)
|
||||
|
||||
# Public: Cuts the selection until the end of the buffer line.
|
||||
cutToEndOfBufferLine: (maintainClipboard) ->
|
||||
@selectToEndOfBufferLine() if @isEmpty()
|
||||
@cut(maintainClipboard)
|
||||
|
||||
# Public: Copies the selection to the clipboard and then deletes it.
|
||||
#
|
||||
# * `maintainClipboard` {Boolean} (default: false) See {::copy}
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
_ = require 'underscore-plus'
|
||||
SpacePen = require 'space-pen'
|
||||
{Subscriber} = require 'emissary'
|
||||
|
||||
Subscriber.includeInto(SpacePen.View)
|
||||
|
||||
jQuery = SpacePen.jQuery
|
||||
JQueryCleanData = jQuery.cleanData
|
||||
jQuery.cleanData = (elements) ->
|
||||
jQuery(element).view()?.unsubscribe?() for element in elements
|
||||
JQueryCleanData(elements)
|
||||
|
||||
SpacePenCallRemoveHooks = SpacePen.callRemoveHooks
|
||||
SpacePen.callRemoveHooks = (element) ->
|
||||
view.unsubscribe?() for view in SpacePen.viewsForElement(element)
|
||||
SpacePenCallRemoveHooks(element)
|
||||
|
||||
NativeEventNames = new Set
|
||||
NativeEventNames.add(nativeEvent) for nativeEvent in ["blur", "focus", "focusin",
|
||||
"focusout", "load", "resize", "scroll", "unload", "click", "dblclick", "mousedown",
|
||||
"mouseup", "mousemove", "mouseover", "mouseout", "mouseenter", "mouseleave", "change",
|
||||
"select", "submit", "keydown", "keypress", "keyup", "error", "contextmenu", "textInput",
|
||||
"textinput", "beforeunload"]
|
||||
|
||||
JQueryTrigger = jQuery.fn.trigger
|
||||
jQuery.fn.trigger = (eventName, data) ->
|
||||
if NativeEventNames.has(eventName) or typeof eventName is 'object'
|
||||
JQueryTrigger.call(this, eventName, data)
|
||||
else
|
||||
data ?= {}
|
||||
data.jQueryTrigger = true
|
||||
|
||||
for element in this
|
||||
atom.commands.dispatch(element, eventName, data)
|
||||
this
|
||||
|
||||
HandlersByOriginalHandler = new WeakMap
|
||||
CommandDisposablesByElement = new WeakMap
|
||||
|
||||
AddEventListener = (element, type, listener) ->
|
||||
if NativeEventNames.has(type)
|
||||
element.addEventListener(type, listener)
|
||||
else
|
||||
disposable = atom.commands.add(element, type, listener)
|
||||
|
||||
unless disposablesByType = CommandDisposablesByElement.get(element)
|
||||
disposablesByType = {}
|
||||
CommandDisposablesByElement.set(element, disposablesByType)
|
||||
|
||||
unless disposablesByListener = disposablesByType[type]
|
||||
disposablesByListener = new WeakMap
|
||||
disposablesByType[type] = disposablesByListener
|
||||
|
||||
disposablesByListener.set(listener, disposable)
|
||||
|
||||
RemoveEventListener = (element, type, listener) ->
|
||||
if NativeEventNames.has(type)
|
||||
element.removeEventListener(type, listener)
|
||||
else
|
||||
CommandDisposablesByElement.get(element)?[type]?.get(listener)?.dispose()
|
||||
|
||||
JQueryEventAdd = jQuery.event.add
|
||||
jQuery.event.add = (elem, types, originalHandler, data, selector) ->
|
||||
handler = (event) ->
|
||||
if arguments.length is 1 and event.originalEvent?.detail?
|
||||
{detail} = event.originalEvent
|
||||
if Array.isArray(detail)
|
||||
originalHandler.apply(this, [event].concat(detail))
|
||||
else
|
||||
originalHandler.call(this, event, detail)
|
||||
else
|
||||
originalHandler.apply(this, arguments)
|
||||
|
||||
HandlersByOriginalHandler.set(originalHandler, handler)
|
||||
|
||||
JQueryEventAdd.call(this, elem, types, handler, data, selector, AddEventListener if atom?.commands?)
|
||||
|
||||
JQueryEventRemove = jQuery.event.remove
|
||||
jQuery.event.remove = (elem, types, originalHandler, selector, mappedTypes) ->
|
||||
if originalHandler?
|
||||
handler = HandlersByOriginalHandler.get(originalHandler) ? originalHandler
|
||||
JQueryEventRemove(elem, types, handler, selector, mappedTypes, RemoveEventListener if atom?.commands?)
|
||||
|
||||
JQueryContains = jQuery.contains
|
||||
|
||||
jQuery.contains = (a, b) ->
|
||||
shadowRoot = null
|
||||
currentNode = b
|
||||
while currentNode
|
||||
if currentNode instanceof ShadowRoot and a.contains(currentNode.host)
|
||||
return true
|
||||
currentNode = currentNode.parentNode
|
||||
|
||||
JQueryContains.call(this, a, b)
|
||||
|
||||
tooltipDefaults =
|
||||
delay:
|
||||
show: 1000
|
||||
hide: 100
|
||||
container: 'body'
|
||||
html: true
|
||||
placement: 'auto top'
|
||||
viewportPadding: 2
|
||||
|
||||
humanizeKeystrokes = (keystroke) ->
|
||||
keystrokes = keystroke.split(' ')
|
||||
keystrokes = (_.humanizeKeystroke(stroke) for stroke in keystrokes)
|
||||
keystrokes.join(' ')
|
||||
|
||||
getKeystroke = (bindings) ->
|
||||
if bindings?.length
|
||||
"<span class=\"keystroke\">#{humanizeKeystrokes(bindings[0].keystrokes)}</span>"
|
||||
else
|
||||
''
|
||||
|
||||
requireBootstrapTooltip = _.once ->
|
||||
atom.requireWithGlobals('bootstrap/js/tooltip', {jQuery})
|
||||
|
||||
# options from http://getbootstrap.com/javascript/#tooltips
|
||||
jQuery.fn.setTooltip = (tooltipOptions, {command, commandElement}={}) ->
|
||||
requireBootstrapTooltip()
|
||||
|
||||
tooltipOptions = {title: tooltipOptions} if _.isString(tooltipOptions)
|
||||
|
||||
if commandElement
|
||||
bindings = atom.keymaps.findKeyBindings(command: command, target: commandElement[0])
|
||||
else if command
|
||||
bindings = atom.keymaps.findKeyBindings(command: command)
|
||||
|
||||
tooltipOptions.title = "#{tooltipOptions.title} #{getKeystroke(bindings)}"
|
||||
|
||||
@tooltip(jQuery.extend({}, tooltipDefaults, tooltipOptions))
|
||||
|
||||
jQuery.fn.hideTooltip = ->
|
||||
tip = @data('bs.tooltip')
|
||||
if tip
|
||||
tip.leave(currentTarget: this)
|
||||
tip.hide()
|
||||
|
||||
jQuery.fn.destroyTooltip = ->
|
||||
@hideTooltip()
|
||||
requireBootstrapTooltip()
|
||||
@tooltip('destroy')
|
||||
|
||||
# Hide tooltips when window is resized
|
||||
jQuery(document.body).on 'show.bs.tooltip', ({target}) ->
|
||||
windowHandler = -> jQuery(target).hideTooltip()
|
||||
jQuery(window).one('resize', windowHandler)
|
||||
jQuery(target).one 'hide.bs.tooltip', ->
|
||||
jQuery(window).off('resize', windowHandler)
|
||||
|
||||
jQuery.fn.setTooltip.getKeystroke = getKeystroke
|
||||
jQuery.fn.setTooltip.humanizeKeystrokes = humanizeKeystrokes
|
||||
|
||||
Object.defineProperty jQuery.fn, 'element', get: -> @[0]
|
||||
|
||||
module.exports = SpacePen
|
||||
@@ -165,10 +165,6 @@ class TextEditorComponent
|
||||
if @editor.isAlive()
|
||||
@updateParentViewFocusedClassIfNeeded()
|
||||
@updateParentViewMiniClass()
|
||||
if grim.includeDeprecatedAPIs
|
||||
@hostElement.__spacePenView.trigger 'cursor:moved' if cursorMoved
|
||||
@hostElement.__spacePenView.trigger 'selection:changed' if selectionChanged
|
||||
@hostElement.__spacePenView.trigger 'editor:display-updated'
|
||||
|
||||
readAfterUpdateSync: =>
|
||||
@linesComponent.measureCharactersInNewLines() if @isVisible() and not @newState.content.scrollingVertically
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
{Emitter} = require 'event-kit'
|
||||
{View, $, callRemoveHooks} = require 'space-pen'
|
||||
Path = require 'path'
|
||||
{defaults} = require 'underscore-plus'
|
||||
TextBuffer = require 'text-buffer'
|
||||
Grim = require 'grim'
|
||||
TextEditor = require './text-editor'
|
||||
TextEditorComponent = require './text-editor-component'
|
||||
TextEditorView = null
|
||||
|
||||
ShadowStyleSheet = null
|
||||
|
||||
@@ -22,7 +20,6 @@ class TextEditorElement extends HTMLElement
|
||||
createdCallback: ->
|
||||
@emitter = new Emitter
|
||||
@initializeContent()
|
||||
@createSpacePenShim() if Grim.includeDeprecatedAPIs
|
||||
@addEventListener 'focus', @focused.bind(this)
|
||||
@addEventListener 'blur', @blurred.bind(this)
|
||||
|
||||
@@ -56,10 +53,6 @@ class TextEditorElement extends HTMLElement
|
||||
@stylesElement = document.head.querySelector('atom-styles')
|
||||
@rootElement = this
|
||||
|
||||
createSpacePenShim: ->
|
||||
TextEditorView ?= require './text-editor-view'
|
||||
@__spacePenView = new TextEditorView(this)
|
||||
|
||||
attachedCallback: ->
|
||||
@buildModel() unless @getModel()?
|
||||
atom.assert(@model.isAlive(), "Attaching a view for a destroyed editor")
|
||||
@@ -90,7 +83,6 @@ class TextEditorElement extends HTMLElement
|
||||
@model.onDidChangeEncoding => @addEncodingAttribute()
|
||||
@model.onDidDestroy => @unmountComponent()
|
||||
@model.onDidChangeMini (mini) => if mini then @addMiniAttribute() else @removeMiniAttribute()
|
||||
@__spacePenView.setModel(@model) if Grim.includeDeprecatedAPIs
|
||||
@model
|
||||
|
||||
getModel: ->
|
||||
@@ -126,7 +118,6 @@ class TextEditorElement extends HTMLElement
|
||||
inputNode.addEventListener 'blur', => @dispatchEvent(new FocusEvent('blur', bubbles: false))
|
||||
|
||||
unmountComponent: ->
|
||||
callRemoveHooks(this)
|
||||
if @component?
|
||||
@component.destroy()
|
||||
@component.getDomNode().remove()
|
||||
@@ -305,6 +296,7 @@ atom.commands.add 'atom-text-editor', stopEventPropagationAndGroupUndo(
|
||||
'editor:delete-to-end-of-subword': -> @deleteToEndOfSubword()
|
||||
'editor:delete-line': -> @deleteLine()
|
||||
'editor:cut-to-end-of-line': -> @cutToEndOfLine()
|
||||
'editor:cut-to-end-of-buffer-line': -> @cutToEndOfBufferLine()
|
||||
'editor:transpose': -> @transpose()
|
||||
'editor:upper-case': -> @upperCase()
|
||||
'editor:lower-case': -> @lowerCase()
|
||||
|
||||
@@ -579,7 +579,7 @@ class TextEditorPresenter
|
||||
# decoration.id : {
|
||||
# top: # of pixels from top
|
||||
# height: # of pixels height of this decoration
|
||||
# item (optional): HTMLElement or space-pen View
|
||||
# item (optional): HTMLElement
|
||||
# class (optional): {String} class
|
||||
# }
|
||||
# }
|
||||
|
||||
@@ -1,324 +0,0 @@
|
||||
{View, $} = require 'space-pen'
|
||||
{defaults} = require 'underscore-plus'
|
||||
TextBuffer = require 'text-buffer'
|
||||
TextEditor = require './text-editor'
|
||||
TextEditorElement = require './text-editor-element'
|
||||
TextEditorComponent = require './text-editor-component'
|
||||
{deprecate} = require 'grim'
|
||||
|
||||
# Deprecated: Represents the entire visual pane in Atom.
|
||||
#
|
||||
# The TextEditorView manages the {TextEditor}, which manages the file buffers.
|
||||
# `TextEditorView` is intentionally sparse. Most of the things you'll want
|
||||
# to do are on {TextEditor}.
|
||||
#
|
||||
# ## Examples
|
||||
#
|
||||
# Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {TextEditorView} = require 'atom'
|
||||
#
|
||||
# miniEditorView = new TextEditorView(mini: true)
|
||||
# ```
|
||||
#
|
||||
# Iterating over the open editor views
|
||||
#
|
||||
# ```coffee
|
||||
# for editorView in atom.workspaceView.getEditorViews()
|
||||
# console.log(editorView.getModel().getPath())
|
||||
# ```
|
||||
#
|
||||
# Subscribing to every current and future editor
|
||||
#
|
||||
# ```coffee
|
||||
# atom.workspace.eachEditorView (editorView) ->
|
||||
# console.log(editorView.getModel().getPath())
|
||||
# ```
|
||||
module.exports =
|
||||
class TextEditorView extends View
|
||||
# The constructor for setting up an `TextEditorView` instance.
|
||||
#
|
||||
# * `modelOrParams` Either an {TextEditor}, or an object with one property, `mini`.
|
||||
# If `mini` is `true`, a "miniature" `TextEditor` is constructed.
|
||||
# Typically, this is ideal for scenarios where you need an Atom editor,
|
||||
# but without all the chrome, like scrollbars, gutter, _e.t.c._.
|
||||
#
|
||||
constructor: (modelOrParams, props) ->
|
||||
# Handle direct construction with an editor or params
|
||||
unless modelOrParams instanceof HTMLElement
|
||||
if modelOrParams instanceof TextEditor
|
||||
model = modelOrParams
|
||||
else
|
||||
{editor, mini, placeholderText, attributes} = modelOrParams
|
||||
model = editor ? new TextEditor
|
||||
buffer: new TextBuffer
|
||||
softWrapped: false
|
||||
tabLength: 2
|
||||
softTabs: true
|
||||
mini: mini
|
||||
placeholderText: placeholderText
|
||||
|
||||
element = new TextEditorElement
|
||||
element.tileSize = props?.tileSize
|
||||
element.setAttribute(name, value) for name, value of attributes if attributes?
|
||||
element.setModel(model)
|
||||
return element.__spacePenView
|
||||
|
||||
# Handle construction with an element
|
||||
@element = modelOrParams
|
||||
super
|
||||
|
||||
setModel: (@model) ->
|
||||
@editor = @model
|
||||
|
||||
@root = $(@element.rootElement)
|
||||
|
||||
@scrollView = @root.find('.scroll-view')
|
||||
|
||||
if atom.config.get('editor.useShadowDOM')
|
||||
@underlayer = $("<div class='underlayer'></div>").appendTo(this)
|
||||
@overlayer = $("<div class='overlayer'></div>").appendTo(this)
|
||||
else
|
||||
@underlayer = @find('.highlights').addClass('underlayer')
|
||||
@overlayer = @find('.lines').addClass('overlayer')
|
||||
|
||||
@hiddenInput = @root.find('.hidden-input')
|
||||
|
||||
@hiddenInput.on = (args...) =>
|
||||
args[0] = 'blur' if args[0] is 'focusout'
|
||||
$::on.apply(this, args)
|
||||
|
||||
@subscribe atom.config.observe 'editor.showLineNumbers', =>
|
||||
@gutter = @root.find('.gutter')
|
||||
|
||||
@gutter.removeClassFromAllLines = (klass) =>
|
||||
deprecate('Use decorations instead: http://blog.atom.io/2014/07/24/decorations.html')
|
||||
@gutter.find('.line-number').removeClass(klass)
|
||||
|
||||
@gutter.getLineNumberElement = (bufferRow) =>
|
||||
deprecate('Use decorations instead: http://blog.atom.io/2014/07/24/decorations.html')
|
||||
@gutter.find("[data-buffer-row='#{bufferRow}']")
|
||||
|
||||
@gutter.addClassToLine = (bufferRow, klass) =>
|
||||
deprecate('Use decorations instead: http://blog.atom.io/2014/07/24/decorations.html')
|
||||
lines = @gutter.find("[data-buffer-row='#{bufferRow}']")
|
||||
lines.addClass(klass)
|
||||
lines.length > 0
|
||||
|
||||
find: ->
|
||||
shadowResult = @root.find.apply(@root, arguments)
|
||||
if shadowResult.length > 0
|
||||
shadowResult
|
||||
else
|
||||
super
|
||||
|
||||
# Public: Get the underlying editor model for this view.
|
||||
#
|
||||
# Returns an {TextEditor}
|
||||
getModel: -> @model
|
||||
|
||||
getEditor: -> @model
|
||||
|
||||
Object.defineProperty @prototype, 'lineHeight', get: -> @model.getLineHeightInPixels()
|
||||
Object.defineProperty @prototype, 'charWidth', get: -> @model.getDefaultCharWidth()
|
||||
Object.defineProperty @prototype, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0]
|
||||
Object.defineProperty @prototype, 'lastRenderedScreenRow', get: -> @component.getRenderedRowRange()[1]
|
||||
Object.defineProperty @prototype, 'active', get: -> @is(@getPaneView()?.activeView)
|
||||
Object.defineProperty @prototype, 'isFocused', get: -> document.activeElement is @element or document.activeElement is @element.component?.hiddenInputComponent?.getDomNode()
|
||||
Object.defineProperty @prototype, 'mini', get: -> @model?.isMini()
|
||||
Object.defineProperty @prototype, 'component', get: -> @element?.component
|
||||
|
||||
afterAttach: (onDom) ->
|
||||
return unless onDom
|
||||
return if @attached
|
||||
@attached = true
|
||||
@trigger 'editor:attached', [this]
|
||||
|
||||
beforeRemove: ->
|
||||
@trigger 'editor:detached', [this]
|
||||
@trigger 'editor:will-be-removed', [this]
|
||||
@attached = false
|
||||
|
||||
remove: (selector, keepData) ->
|
||||
@model.destroy() unless keepData
|
||||
super
|
||||
|
||||
scrollTop: (scrollTop) ->
|
||||
if scrollTop?
|
||||
@model.setScrollTop(scrollTop)
|
||||
else
|
||||
@model.getScrollTop()
|
||||
|
||||
scrollLeft: (scrollLeft) ->
|
||||
if scrollLeft?
|
||||
@model.setScrollLeft(scrollLeft)
|
||||
else
|
||||
@model.getScrollLeft()
|
||||
|
||||
scrollToBottom: ->
|
||||
deprecate 'Use TextEditor::scrollToBottom instead. You can get the editor via editorView.getModel()'
|
||||
@model.setScrollBottom(Infinity)
|
||||
|
||||
scrollToScreenPosition: (screenPosition, options) ->
|
||||
deprecate 'Use TextEditor::scrollToScreenPosition instead. You can get the editor via editorView.getModel()'
|
||||
@model.scrollToScreenPosition(screenPosition, options)
|
||||
|
||||
scrollToBufferPosition: (bufferPosition, options) ->
|
||||
deprecate 'Use TextEditor::scrollToBufferPosition instead. You can get the editor via editorView.getModel()'
|
||||
@model.scrollToBufferPosition(bufferPosition, options)
|
||||
|
||||
scrollToCursorPosition: ->
|
||||
deprecate 'Use TextEditor::scrollToCursorPosition instead. You can get the editor via editorView.getModel()'
|
||||
@model.scrollToCursorPosition()
|
||||
|
||||
pixelPositionForBufferPosition: (bufferPosition) ->
|
||||
deprecate 'Use TextEditorElement::pixelPositionForBufferPosition instead. You can get the editor via editorView.getModel()'
|
||||
@model.pixelPositionForBufferPosition(bufferPosition, true)
|
||||
|
||||
pixelPositionForScreenPosition: (screenPosition) ->
|
||||
deprecate 'Use TextEditorElement::pixelPositionForScreenPosition instead. You can get the editor via editorView.getModel()'
|
||||
@model.pixelPositionForScreenPosition(screenPosition, true)
|
||||
|
||||
appendToLinesView: (view) ->
|
||||
view.css('position', 'absolute')
|
||||
view.css('z-index', 1)
|
||||
@overlayer.append(view)
|
||||
|
||||
splitLeft: ->
|
||||
deprecate """
|
||||
Use Pane::splitLeft instead.
|
||||
To duplicate this editor into the split use:
|
||||
editorView.getPaneView().getModel().splitLeft(copyActiveItem: true)
|
||||
"""
|
||||
pane = @getPaneView()
|
||||
pane?.splitLeft(pane?.copyActiveItem()).activeView
|
||||
|
||||
splitRight: ->
|
||||
deprecate """
|
||||
Use Pane::splitRight instead.
|
||||
To duplicate this editor into the split use:
|
||||
editorView.getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
"""
|
||||
pane = @getPaneView()
|
||||
pane?.splitRight(pane?.copyActiveItem()).activeView
|
||||
|
||||
splitUp: ->
|
||||
deprecate """
|
||||
Use Pane::splitUp instead.
|
||||
To duplicate this editor into the split use:
|
||||
editorView.getPaneView().getModel().splitUp(copyActiveItem: true)
|
||||
"""
|
||||
pane = @getPaneView()
|
||||
pane?.splitUp(pane?.copyActiveItem()).activeView
|
||||
|
||||
splitDown: ->
|
||||
deprecate """
|
||||
Use Pane::splitDown instead.
|
||||
To duplicate this editor into the split use:
|
||||
editorView.getPaneView().getModel().splitDown(copyActiveItem: true)
|
||||
"""
|
||||
pane = @getPaneView()
|
||||
pane?.splitDown(pane?.copyActiveItem()).activeView
|
||||
|
||||
# Public: Get this {TextEditorView}'s {PaneView}.
|
||||
#
|
||||
# Returns a {PaneView}
|
||||
getPaneView: ->
|
||||
@parent('.item-views').parents('atom-pane').view()
|
||||
getPane: ->
|
||||
deprecate 'Use TextEditorView::getPaneView() instead'
|
||||
@getPaneView()
|
||||
|
||||
show: ->
|
||||
super
|
||||
@component?.checkForVisibilityChange()
|
||||
|
||||
hide: ->
|
||||
super
|
||||
@component?.checkForVisibilityChange()
|
||||
|
||||
pageDown: ->
|
||||
deprecate('Use editorView.getModel().pageDown()')
|
||||
@model.pageDown()
|
||||
|
||||
pageUp: ->
|
||||
deprecate('Use editorView.getModel().pageUp()')
|
||||
@model.pageUp()
|
||||
|
||||
getFirstVisibleScreenRow: ->
|
||||
deprecate 'Use TextEditorElement::getFirstVisibleScreenRow instead.'
|
||||
@model.getFirstVisibleScreenRow(true)
|
||||
|
||||
getLastVisibleScreenRow: ->
|
||||
deprecate 'Use TextEditor::getLastVisibleScreenRow instead. You can get the editor via editorView.getModel()'
|
||||
@model.getLastVisibleScreenRow()
|
||||
|
||||
getFontFamily: ->
|
||||
deprecate 'This is going away. Use atom.config.get("editor.fontFamily") instead'
|
||||
@component?.getFontFamily()
|
||||
|
||||
setFontFamily: (fontFamily) ->
|
||||
deprecate 'This is going away. Use atom.config.set("editor.fontFamily", "my-font") instead'
|
||||
@component?.setFontFamily(fontFamily)
|
||||
|
||||
getFontSize: ->
|
||||
deprecate 'This is going away. Use atom.config.get("editor.fontSize") instead'
|
||||
@component?.getFontSize()
|
||||
|
||||
setFontSize: (fontSize) ->
|
||||
deprecate 'This is going away. Use atom.config.set("editor.fontSize", 12) instead'
|
||||
@component?.setFontSize(fontSize)
|
||||
|
||||
setLineHeight: (lineHeight) ->
|
||||
deprecate 'This is going away. Use atom.config.set("editor.lineHeight", 1.5) instead'
|
||||
@component.setLineHeight(lineHeight)
|
||||
|
||||
setWidthInChars: (widthInChars) ->
|
||||
@component.getDOMNode().style.width = (@model.getDefaultCharWidth() * widthInChars) + 'px'
|
||||
|
||||
setShowIndentGuide: (showIndentGuide) ->
|
||||
deprecate 'This is going away. Use atom.config.set("editor.showIndentGuide", true|false) instead'
|
||||
atom.config.set("editor.showIndentGuide", showIndentGuide)
|
||||
|
||||
setSoftWrap: (softWrapped) ->
|
||||
deprecate 'Use TextEditor::setSoftWrapped instead. You can get the editor via editorView.getModel()'
|
||||
@model.setSoftWrapped(softWrapped)
|
||||
|
||||
setShowInvisibles: (showInvisibles) ->
|
||||
deprecate 'This is going away. Use atom.config.set("editor.showInvisibles", true|false) instead'
|
||||
@component.setShowInvisibles(showInvisibles)
|
||||
|
||||
getText: ->
|
||||
@model.getText()
|
||||
|
||||
setText: (text) ->
|
||||
@model.setText(text)
|
||||
|
||||
insertText: (text) ->
|
||||
@model.insertText(text)
|
||||
|
||||
isInputEnabled: ->
|
||||
@component.isInputEnabled()
|
||||
|
||||
setInputEnabled: (inputEnabled) ->
|
||||
@component.setInputEnabled(inputEnabled)
|
||||
|
||||
requestDisplayUpdate: ->
|
||||
deprecate('Please remove from your code. ::requestDisplayUpdate no longer does anything')
|
||||
|
||||
updateDisplay: ->
|
||||
deprecate('Please remove from your code. ::updateDisplay no longer does anything')
|
||||
|
||||
resetDisplay: ->
|
||||
deprecate('Please remove from your code. ::resetDisplay no longer does anything')
|
||||
|
||||
redraw: ->
|
||||
deprecate('Please remove from your code. ::redraw no longer does anything')
|
||||
|
||||
setPlaceholderText: (placeholderText) ->
|
||||
deprecate('Use TextEditor::setPlaceholderText instead. eg. editorView.getModel().setPlaceholderText(text)')
|
||||
@model.setPlaceholderText(placeholderText)
|
||||
|
||||
lineElementForScreenRow: (screenRow) ->
|
||||
$(@component.lineNodeForScreenRow(screenRow))
|
||||
@@ -17,7 +17,8 @@ GutterContainer = require './gutter-container'
|
||||
# Essential: This class represents all essential editing state for a single
|
||||
# {TextBuffer}, including cursor and selection positions, folds, and soft wraps.
|
||||
# If you're manipulating the state of an editor, use this class. If you're
|
||||
# interested in the visual appearance of editors, use {TextEditorView} instead.
|
||||
# interested in the visual appearance of editors, use {TextEditorElement}
|
||||
# instead.
|
||||
#
|
||||
# A single {TextBuffer} can belong to multiple editors. For example, if the
|
||||
# same file is open in two different panes, Atom creates a separate editor for
|
||||
@@ -545,8 +546,8 @@ class TextEditor extends Model
|
||||
# Set the number of characters that can be displayed horizontally in the
|
||||
# editor.
|
||||
#
|
||||
# * `editorWidthInChars` A {Number} representing the width of the {TextEditorView}
|
||||
# in characters.
|
||||
# * `editorWidthInChars` A {Number} representing the width of the
|
||||
# {TextEditorElement} in characters.
|
||||
setEditorWidthInChars: (editorWidthInChars) ->
|
||||
@displayBuffer.setEditorWidthInChars(editorWidthInChars)
|
||||
|
||||
@@ -2664,7 +2665,7 @@ class TextEditor extends Model
|
||||
@emitter.emit 'did-insert-text', didInsertEvent
|
||||
|
||||
# Essential: For each selection, if the selection is empty, cut all characters
|
||||
# of the containing line following the cursor. Otherwise cut the selected
|
||||
# of the containing screen line following the cursor. Otherwise cut the selected
|
||||
# text.
|
||||
cutToEndOfLine: ->
|
||||
maintainClipboard = false
|
||||
@@ -2672,6 +2673,15 @@ class TextEditor extends Model
|
||||
selection.cutToEndOfLine(maintainClipboard)
|
||||
maintainClipboard = true
|
||||
|
||||
# Essential: For each selection, if the selection is empty, cut all characters
|
||||
# of the containing buffer line following the cursor. Otherwise cut the
|
||||
# selected text.
|
||||
cutToEndOfBufferLine: ->
|
||||
maintainClipboard = false
|
||||
@mutateSelectedText (selection) ->
|
||||
selection.cutToEndOfBufferLine(maintainClipboard)
|
||||
maintainClipboard = true
|
||||
|
||||
###
|
||||
Section: Folds
|
||||
###
|
||||
@@ -3051,9 +3061,6 @@ if includeDeprecatedAPIs
|
||||
'$verticalScrollbarWidth', '$horizontalScrollbarHeight', '$scrollTop', '$scrollLeft',
|
||||
toProperty: 'displayBuffer'
|
||||
|
||||
TextEditor::getViewClass = ->
|
||||
require './text-editor-view'
|
||||
|
||||
TextEditor::joinLine = ->
|
||||
deprecate("Use TextEditor::joinLines() instead")
|
||||
@joinLines()
|
||||
|
||||
@@ -3,7 +3,6 @@ _ = require 'underscore-plus'
|
||||
{Emitter, Disposable, CompositeDisposable} = require 'event-kit'
|
||||
{File} = require 'pathwatcher'
|
||||
fs = require 'fs-plus'
|
||||
Q = require 'q'
|
||||
Grim = require 'grim'
|
||||
|
||||
# Extended: Handles loading and activating available themes.
|
||||
@@ -260,32 +259,29 @@ class ThemeManager
|
||||
string.replace(/\\/g, '/')
|
||||
|
||||
activateThemes: ->
|
||||
deferred = Q.defer()
|
||||
new Promise (resolve) =>
|
||||
# atom.config.observe runs the callback once, then on subsequent changes.
|
||||
atom.config.observe 'core.themes', =>
|
||||
@deactivateThemes()
|
||||
|
||||
# atom.config.observe runs the callback once, then on subsequent changes.
|
||||
atom.config.observe 'core.themes', =>
|
||||
@deactivateThemes()
|
||||
@refreshLessCache() # Update cache for packages in core.themes config
|
||||
|
||||
@refreshLessCache() # Update cache for packages in core.themes config
|
||||
promises = []
|
||||
for themeName in @getEnabledThemeNames()
|
||||
if @packageManager.resolvePackagePath(themeName)
|
||||
promises.push(@packageManager.activatePackage(themeName))
|
||||
else
|
||||
console.warn("Failed to activate theme '#{themeName}' because it isn't installed.")
|
||||
|
||||
promises = []
|
||||
for themeName in @getEnabledThemeNames()
|
||||
if @packageManager.resolvePackagePath(themeName)
|
||||
promises.push(@packageManager.activatePackage(themeName))
|
||||
else
|
||||
console.warn("Failed to activate theme '#{themeName}' because it isn't installed.")
|
||||
|
||||
Q.all(promises).then =>
|
||||
@addActiveThemeClasses()
|
||||
@refreshLessCache() # Update cache again now that @getActiveThemes() is populated
|
||||
@loadUserStylesheet()
|
||||
@reloadBaseStylesheets()
|
||||
@initialLoadComplete = true
|
||||
@emit 'reloaded' if Grim.includeDeprecatedAPIs
|
||||
@emitter.emit 'did-change-active-themes'
|
||||
deferred.resolve()
|
||||
|
||||
deferred.promise
|
||||
Promise.all(promises).then =>
|
||||
@addActiveThemeClasses()
|
||||
@refreshLessCache() # Update cache again now that @getActiveThemes() is populated
|
||||
@loadUserStylesheet()
|
||||
@reloadBaseStylesheets()
|
||||
@initialLoadComplete = true
|
||||
@emit 'reloaded' if Grim.includeDeprecatedAPIs
|
||||
@emitter.emit 'did-change-active-themes'
|
||||
resolve()
|
||||
|
||||
deactivateThemes: ->
|
||||
@removeActiveThemeClasses()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
Q = require 'q'
|
||||
Package = require './package'
|
||||
|
||||
module.exports =
|
||||
@@ -18,14 +17,12 @@ class ThemePackage extends Package
|
||||
this
|
||||
|
||||
activate: ->
|
||||
return @activationDeferred.promise if @activationDeferred?
|
||||
|
||||
@activationDeferred = Q.defer()
|
||||
@measure 'activateTime', =>
|
||||
try
|
||||
@loadStylesheets()
|
||||
@activateNow()
|
||||
catch error
|
||||
@handleError("Failed to activate the #{@name} theme", error)
|
||||
|
||||
@activationDeferred.promise
|
||||
@activationPromise ?= new Promise (resolve, reject) =>
|
||||
@resolveActivationPromise = resolve
|
||||
@rejectActivationPromise = reject
|
||||
@measure 'activateTime', =>
|
||||
try
|
||||
@loadStylesheets()
|
||||
@activateNow()
|
||||
catch error
|
||||
@handleError("Failed to activate the #{@name} theme", error)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
_ = require 'underscore-plus'
|
||||
{Disposable} = require 'event-kit'
|
||||
{$} = require './space-pen-extensions'
|
||||
{Disposable, CompositeDisposable} = require 'event-kit'
|
||||
Tooltip = null
|
||||
|
||||
# Essential: Associates tooltips with HTML elements or selectors.
|
||||
#
|
||||
@@ -71,7 +71,12 @@ class TooltipManager
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to remove the
|
||||
# tooltip.
|
||||
add: (target, options) ->
|
||||
requireBootstrapTooltip()
|
||||
if target.jquery
|
||||
disposable = new CompositeDisposable
|
||||
disposable.add @add(element, options) for element in target
|
||||
return disposable
|
||||
|
||||
Tooltip ?= require './tooltip'
|
||||
|
||||
{keyBindingCommand, keyBindingTarget} = options
|
||||
|
||||
@@ -83,15 +88,20 @@ class TooltipManager
|
||||
else if keystroke?
|
||||
options.title = getKeystroke(bindings)
|
||||
|
||||
$target = $(target)
|
||||
$target.tooltip(_.defaults(options, @defaults))
|
||||
tooltip = new Tooltip(target, _.defaults(options, @defaults))
|
||||
|
||||
new Disposable ->
|
||||
tooltip = $target.data('bs.tooltip')
|
||||
if tooltip?
|
||||
tooltip.leave(currentTarget: target)
|
||||
tooltip.hide()
|
||||
$target.tooltip('destroy')
|
||||
hideTooltip = ->
|
||||
tooltip.leave(currentTarget: target)
|
||||
tooltip.hide()
|
||||
|
||||
window.addEventListener('resize', hideTooltip)
|
||||
|
||||
disposable = new Disposable ->
|
||||
window.removeEventListener('resize', hideTooltip)
|
||||
hideTooltip()
|
||||
tooltip.destroy()
|
||||
|
||||
disposable
|
||||
|
||||
humanizeKeystrokes = (keystroke) ->
|
||||
keystrokes = keystroke.split(' ')
|
||||
@@ -101,7 +111,3 @@ humanizeKeystrokes = (keystroke) ->
|
||||
getKeystroke = (bindings) ->
|
||||
if bindings?.length
|
||||
"<span class=\"keystroke\">#{humanizeKeystrokes(bindings[0].keystrokes)}</span>"
|
||||
else
|
||||
|
||||
requireBootstrapTooltip = _.once ->
|
||||
atom.requireWithGlobals('bootstrap/js/tooltip', {jQuery: $})
|
||||
|
||||
456
src/tooltip.js
Normal file
456
src/tooltip.js
Normal file
@@ -0,0 +1,456 @@
|
||||
'use strict'
|
||||
|
||||
const EventKit = require('event-kit')
|
||||
const tooltipComponentsByElement = new WeakMap()
|
||||
const listen = require('./delegated-listener')
|
||||
|
||||
// This tooltip class is derived from Bootstrap 3, but modified to not require
|
||||
// jQuery, which is an expensive dependency we want to eliminate.
|
||||
|
||||
var Tooltip = function (element, options) {
|
||||
this.options = null
|
||||
this.enabled = null
|
||||
this.timeout = null
|
||||
this.hoverState = null
|
||||
this.element = null
|
||||
this.inState = null
|
||||
|
||||
this.init(element, options)
|
||||
}
|
||||
|
||||
Tooltip.VERSION = '3.3.5'
|
||||
|
||||
Tooltip.TRANSITION_DURATION = 150
|
||||
|
||||
Tooltip.DEFAULTS = {
|
||||
animation: true,
|
||||
placement: 'top',
|
||||
selector: false,
|
||||
template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
|
||||
trigger: 'hover focus',
|
||||
title: '',
|
||||
delay: 0,
|
||||
html: false,
|
||||
container: false,
|
||||
viewport: {
|
||||
selector: 'body',
|
||||
padding: 0
|
||||
}
|
||||
}
|
||||
|
||||
Tooltip.prototype.init = function (element, options) {
|
||||
this.enabled = true
|
||||
this.element = element
|
||||
this.options = this.getOptions(options)
|
||||
this.disposables = new EventKit.CompositeDisposable()
|
||||
|
||||
if (this.options.viewport) {
|
||||
if (typeof this.options.viewport === 'function') {
|
||||
this.viewport = this.options.viewport.call(this, this.element)
|
||||
} else {
|
||||
this.viewport = document.querySelector(this.options.viewport.selector || this.options.viewport)
|
||||
}
|
||||
}
|
||||
this.inState = {click: false, hover: false, focus: false}
|
||||
|
||||
if (this.element instanceof document.constructor && !this.options.selector) {
|
||||
throw new Error('`selector` option must be specified when initializing tooltip on the window.document object!')
|
||||
}
|
||||
|
||||
var triggers = this.options.trigger.split(' ')
|
||||
|
||||
for (var i = triggers.length; i--;) {
|
||||
var trigger = triggers[i]
|
||||
|
||||
if (trigger === 'click') {
|
||||
this.disposables.add(listen(this.element, 'click', this.options.selector, this.toggle.bind(this)))
|
||||
} else if (trigger !== 'manual') {
|
||||
var eventIn, eventOut
|
||||
|
||||
if (trigger === 'hover') {
|
||||
if (this.options.selector) {
|
||||
eventIn = 'mouseover'
|
||||
eventOut = 'mouseout'
|
||||
} else {
|
||||
eventIn = 'mouseenter'
|
||||
eventOut = 'mouseleave'
|
||||
}
|
||||
} else {
|
||||
eventIn = 'focusin'
|
||||
eventOut = 'focusout'
|
||||
}
|
||||
|
||||
this.disposables.add(listen(this.element, eventIn, this.options.selector, this.enter.bind(this)))
|
||||
this.disposables.add(listen(this.element, eventOut, this.options.selector, this.leave.bind(this)))
|
||||
}
|
||||
}
|
||||
|
||||
this.options.selector ?
|
||||
(this._options = extend({}, this.options, { trigger: 'manual', selector: '' })) :
|
||||
this.fixTitle()
|
||||
}
|
||||
|
||||
Tooltip.prototype.getDefaults = function () {
|
||||
return Tooltip.DEFAULTS
|
||||
}
|
||||
|
||||
Tooltip.prototype.getOptions = function (options) {
|
||||
options = extend({}, this.getDefaults(), options)
|
||||
|
||||
if (options.delay && typeof options.delay === 'number') {
|
||||
options.delay = {
|
||||
show: options.delay,
|
||||
hide: options.delay
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
Tooltip.prototype.getDelegateOptions = function () {
|
||||
var options = {}
|
||||
var defaults = this.getDefaults()
|
||||
|
||||
if (this._options) {
|
||||
for (var key of Object.getOwnPropertyNames(this._options)) {
|
||||
var value = this._options[key]
|
||||
if (defaults[key] !== value) options[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
Tooltip.prototype.enter = function (event) {
|
||||
if (event) {
|
||||
if (event.currentTarget !== this.element) {
|
||||
this.getDelegateComponent(event.currentTarget).enter(event)
|
||||
return
|
||||
}
|
||||
|
||||
this.inState[event.type === 'focusin' ? 'focus' : 'hover'] = true
|
||||
}
|
||||
|
||||
if (this.getTooltipElement().classList.contains('in') || this.hoverState === 'in') {
|
||||
this.hoverState = 'in'
|
||||
return
|
||||
}
|
||||
|
||||
clearTimeout(this.timeout)
|
||||
|
||||
this.hoverState = 'in'
|
||||
|
||||
if (!this.options.delay || !this.options.delay.show) return this.show()
|
||||
|
||||
this.timeout = setTimeout(function () {
|
||||
if (this.hoverState === 'in') this.show()
|
||||
}.bind(this), this.options.delay.show)
|
||||
}
|
||||
|
||||
Tooltip.prototype.isInStateTrue = function () {
|
||||
for (var key in this.inState) {
|
||||
if (this.inState[key]) return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
Tooltip.prototype.leave = function (event) {
|
||||
if (event) {
|
||||
if (event.currentTarget !== this.element) {
|
||||
this.getDelegateComponent(event.currentTarget).leave(event)
|
||||
return
|
||||
}
|
||||
|
||||
this.inState[event.type === 'focusout' ? 'focus' : 'hover'] = false
|
||||
}
|
||||
|
||||
if (this.isInStateTrue()) return
|
||||
|
||||
clearTimeout(this.timeout)
|
||||
|
||||
this.hoverState = 'out'
|
||||
|
||||
if (!this.options.delay || !this.options.delay.hide) return this.hide()
|
||||
|
||||
this.timeout = setTimeout(function () {
|
||||
if (this.hoverState === 'out') this.hide()
|
||||
}.bind(this), this.options.delay.hide)
|
||||
}
|
||||
|
||||
Tooltip.prototype.show = function () {
|
||||
if (this.hasContent() && this.enabled) {
|
||||
var tip = this.getTooltipElement()
|
||||
|
||||
var tipId = this.getUID('tooltip')
|
||||
|
||||
this.setContent()
|
||||
tip.setAttribute('id', tipId)
|
||||
this.element.setAttribute('aria-describedby', tipId)
|
||||
|
||||
if (this.options.animation) tip.classList.add('fade')
|
||||
|
||||
var placement = typeof this.options.placement === 'function' ?
|
||||
this.options.placement.call(this, tip, this.element) :
|
||||
this.options.placement
|
||||
|
||||
var autoToken = /\s?auto?\s?/i
|
||||
var autoPlace = autoToken.test(placement)
|
||||
if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
|
||||
|
||||
tip.remove()
|
||||
tip.style.top = '0px'
|
||||
tip.style.left = '0px'
|
||||
tip.style.display = 'block'
|
||||
tip.classList.add(placement)
|
||||
|
||||
document.body.appendChild(tip)
|
||||
|
||||
var pos = this.element.getBoundingClientRect()
|
||||
var actualWidth = tip.offsetWidth
|
||||
var actualHeight = tip.offsetHeight
|
||||
|
||||
if (autoPlace) {
|
||||
var orgPlacement = placement
|
||||
var viewportDim = this.viewport.getBoundingClientRect()
|
||||
|
||||
placement = placement === 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' :
|
||||
placement === 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' :
|
||||
placement === 'right' && pos.right + actualWidth > viewportDim.width ? 'left' :
|
||||
placement === 'left' && pos.left - actualWidth < viewportDim.left ? 'right' :
|
||||
placement
|
||||
|
||||
tip.classList.remove(orgPlacement)
|
||||
tip.classList.add(placement)
|
||||
}
|
||||
|
||||
var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
|
||||
|
||||
this.applyPlacement(calculatedOffset, placement)
|
||||
|
||||
var prevHoverState = this.hoverState
|
||||
this.hoverState = null
|
||||
|
||||
if (prevHoverState === 'out') this.leave()
|
||||
}
|
||||
}
|
||||
|
||||
Tooltip.prototype.applyPlacement = function (offset, placement) {
|
||||
var tip = this.getTooltipElement()
|
||||
|
||||
var width = tip.offsetWidth
|
||||
var height = tip.offsetHeight
|
||||
|
||||
// manually read margins because getBoundingClientRect includes difference
|
||||
var computedStyle = window.getComputedStyle(tip)
|
||||
var marginTop = parseInt(computedStyle.marginTop, 10)
|
||||
var marginLeft = parseInt(computedStyle.marginLeft, 10)
|
||||
|
||||
offset.top += marginTop
|
||||
offset.left += marginLeft
|
||||
|
||||
tip.style.top = offset.top + 'px'
|
||||
tip.style.left = offset.left + 'px'
|
||||
|
||||
tip.classList.add('in')
|
||||
|
||||
// check to see if placing tip in new offset caused the tip to resize itself
|
||||
var actualWidth = tip.offsetWidth
|
||||
var actualHeight = tip.offsetHeight
|
||||
|
||||
if (placement === 'top' && actualHeight !== height) {
|
||||
offset.top = offset.top + height - actualHeight
|
||||
}
|
||||
|
||||
var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
|
||||
|
||||
if (delta.left) offset.left += delta.left
|
||||
else offset.top += delta.top
|
||||
|
||||
var isVertical = /top|bottom/.test(placement)
|
||||
var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
|
||||
var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
|
||||
|
||||
tip.style.top = offset.top + 'px'
|
||||
tip.style.left = offset.left + 'px'
|
||||
|
||||
this.replaceArrow(arrowDelta, tip[arrowOffsetPosition], isVertical)
|
||||
}
|
||||
|
||||
Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
|
||||
var arrow = this.getArrowElement()
|
||||
var amount = 50 * (1 - delta / dimension) + '%'
|
||||
|
||||
if (isVertical) {
|
||||
arrow.style.left = amount
|
||||
arrow.style.top = ''
|
||||
} else {
|
||||
arrow.style.top = amount
|
||||
arrow.style.left = ''
|
||||
}
|
||||
}
|
||||
|
||||
Tooltip.prototype.setContent = function () {
|
||||
var tip = this.getTooltipElement()
|
||||
var title = this.getTitle()
|
||||
|
||||
var inner = tip.querySelector('.tooltip-inner')
|
||||
if (this.options.html) {
|
||||
inner.innerHTML = title
|
||||
} else {
|
||||
inner.textContent = title
|
||||
}
|
||||
|
||||
tip.classList.remove('fade', 'in', 'top', 'bottom', 'left', 'right')
|
||||
}
|
||||
|
||||
Tooltip.prototype.hide = function (callback) {
|
||||
this.tip && this.tip.classList.remove('in')
|
||||
|
||||
if (this.hoverState !== 'in') this.tip && this.tip.remove()
|
||||
|
||||
this.element.removeAttribute('aria-describedby')
|
||||
|
||||
callback && callback()
|
||||
|
||||
this.hoverState = null
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
Tooltip.prototype.fixTitle = function () {
|
||||
if (this.element.getAttribute('title') || typeof this.element.getAttribute('data-original-title') !== 'string') {
|
||||
this.element.setAttribute('data-original-title', this.element.getAttribute('title') || '')
|
||||
this.element.setAttribute('title', '')
|
||||
}
|
||||
}
|
||||
|
||||
Tooltip.prototype.hasContent = function () {
|
||||
return this.getTitle()
|
||||
}
|
||||
|
||||
Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
|
||||
return placement === 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
|
||||
placement === 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
|
||||
placement === 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
|
||||
/* placement === 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
|
||||
}
|
||||
|
||||
Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
|
||||
var delta = { top: 0, left: 0 }
|
||||
if (!this.viewport) return delta
|
||||
|
||||
var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
|
||||
var viewportDimensions = this.viewport.getBoundingClientRect()
|
||||
|
||||
if (/right|left/.test(placement)) {
|
||||
var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
|
||||
var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
|
||||
if (topEdgeOffset < viewportDimensions.top) { // top overflow
|
||||
delta.top = viewportDimensions.top - topEdgeOffset
|
||||
} else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
|
||||
delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
|
||||
}
|
||||
} else {
|
||||
var leftEdgeOffset = pos.left - viewportPadding
|
||||
var rightEdgeOffset = pos.left + viewportPadding + actualWidth
|
||||
if (leftEdgeOffset < viewportDimensions.left) { // left overflow
|
||||
delta.left = viewportDimensions.left - leftEdgeOffset
|
||||
} else if (rightEdgeOffset > viewportDimensions.right) { // right overflow
|
||||
delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
|
||||
}
|
||||
}
|
||||
|
||||
return delta
|
||||
}
|
||||
|
||||
Tooltip.prototype.getTitle = function () {
|
||||
var title = this.element.getAttribute('data-original-title')
|
||||
if (title) {
|
||||
return title
|
||||
} else {
|
||||
return (typeof this.options.title === 'function')
|
||||
? this.options.title.call(this.element)
|
||||
: this.options.title
|
||||
}
|
||||
}
|
||||
|
||||
Tooltip.prototype.getUID = function (prefix) {
|
||||
do prefix += ~~(Math.random() * 1000000)
|
||||
while (document.getElementById(prefix))
|
||||
return prefix
|
||||
}
|
||||
|
||||
Tooltip.prototype.getTooltipElement = function () {
|
||||
if (!this.tip) {
|
||||
let div = document.createElement('div')
|
||||
div.innerHTML = this.options.template
|
||||
if (div.children.length !== 1) {
|
||||
throw new Error('Tooltip `template` option must consist of exactly 1 top-level element!')
|
||||
}
|
||||
this.tip = div.firstChild
|
||||
}
|
||||
return this.tip
|
||||
}
|
||||
|
||||
Tooltip.prototype.getArrowElement = function () {
|
||||
this.arrow = this.arrow || this.getTooltipElement().querySelector('.tooltip-arrow')
|
||||
return this.arrow
|
||||
}
|
||||
|
||||
Tooltip.prototype.enable = function () {
|
||||
this.enabled = true
|
||||
}
|
||||
|
||||
Tooltip.prototype.disable = function () {
|
||||
this.enabled = false
|
||||
}
|
||||
|
||||
Tooltip.prototype.toggleEnabled = function () {
|
||||
this.enabled = !this.enabled
|
||||
}
|
||||
|
||||
Tooltip.prototype.toggle = function (event) {
|
||||
if (event) {
|
||||
if (event.currentTarget !== this.element) {
|
||||
this.getDelegateComponent(event.currentTarget).toggle(event)
|
||||
return
|
||||
}
|
||||
|
||||
this.inState.click = !this.inState.click
|
||||
if (this.isInStateTrue()) this.enter()
|
||||
else this.leave()
|
||||
} else {
|
||||
this.getTooltipElement().classList.contains('in') ? this.leave() : this.enter()
|
||||
}
|
||||
}
|
||||
|
||||
Tooltip.prototype.destroy = function () {
|
||||
clearTimeout(this.timeout)
|
||||
this.tip && this.tip.remove()
|
||||
this.disposables.dispose()
|
||||
}
|
||||
|
||||
Tooltip.prototype.getDelegateComponent = function (element) {
|
||||
var component = tooltipComponentsByElement.get(element)
|
||||
if (!component) {
|
||||
component = new Tooltip(element, this.getDelegateOptions())
|
||||
tooltipComponentsByElement.set(element, component)
|
||||
}
|
||||
return component
|
||||
}
|
||||
|
||||
function extend () {
|
||||
var args = Array.prototype.slice.apply(arguments)
|
||||
var target = args.shift()
|
||||
var source = args.shift()
|
||||
while (source) {
|
||||
for (var key of Object.getOwnPropertyNames(source)) {
|
||||
target[key] = source[key]
|
||||
}
|
||||
source = args.shift()
|
||||
}
|
||||
return target
|
||||
}
|
||||
|
||||
module.exports = Tooltip
|
||||
@@ -1,132 +1,56 @@
|
||||
path = require 'path'
|
||||
{$} = require './space-pen-extensions'
|
||||
{Disposable} = require 'event-kit'
|
||||
{Disposable, CompositeDisposable} = require 'event-kit'
|
||||
ipc = require 'ipc'
|
||||
shell = require 'shell'
|
||||
{Subscriber} = require 'emissary'
|
||||
fs = require 'fs-plus'
|
||||
listen = require './delegated-listener'
|
||||
|
||||
# Handles low-level events related to the window.
|
||||
module.exports =
|
||||
class WindowEventHandler
|
||||
Subscriber.includeInto(this)
|
||||
|
||||
constructor: ->
|
||||
@reloadRequested = false
|
||||
@subscriptions = new CompositeDisposable
|
||||
|
||||
@subscribe ipc, 'message', (message, detail) ->
|
||||
switch message
|
||||
when 'open-locations'
|
||||
needsProjectPaths = atom.project?.getPaths().length is 0
|
||||
@on(ipc, 'message', @handleIPCMessage)
|
||||
@on(ipc, 'command', @handleIPCCommand)
|
||||
@on(ipc, 'context-command', @handleIPCContextCommand)
|
||||
|
||||
for {pathToOpen, initialLine, initialColumn} in detail
|
||||
if pathToOpen? and needsProjectPaths
|
||||
if fs.existsSync(pathToOpen)
|
||||
atom.project.addPath(pathToOpen)
|
||||
else if fs.existsSync(path.dirname(pathToOpen))
|
||||
atom.project.addPath(path.dirname(pathToOpen))
|
||||
else
|
||||
atom.project.addPath(pathToOpen)
|
||||
@addEventListener(window, 'focus', @handleWindowFocus)
|
||||
@addEventListener(window, 'blur', @handleWindowBlur)
|
||||
@addEventListener(window, 'beforeunload', @handleWindowBeforeunload)
|
||||
@addEventListener(window, 'unload', @handleWindowUnload)
|
||||
|
||||
unless fs.isDirectorySync(pathToOpen)
|
||||
atom.workspace?.open(pathToOpen, {initialLine, initialColumn})
|
||||
@addEventListener(document, 'keydown', @handleDocumentKeydown)
|
||||
@addEventListener(document, 'drop', @handleDocumentDrop)
|
||||
@addEventListener(document, 'dragover', @handleDocumentDragover)
|
||||
@addEventListener(document, 'contextmenu', @handleDocumentContextmenu)
|
||||
@subscriptions.add listen(document, 'click', 'a', @handleLinkClick)
|
||||
@subscriptions.add listen(document, 'submit', 'form', @handleFormSubmit)
|
||||
|
||||
return
|
||||
|
||||
when 'update-available'
|
||||
atom.updateAvailable(detail)
|
||||
|
||||
# FIXME: Remove this when deprecations are removed
|
||||
{releaseVersion} = detail
|
||||
detail = [releaseVersion]
|
||||
if workspaceElement = atom.views.getView(atom.workspace)
|
||||
atom.commands.dispatch workspaceElement, "window:update-available", detail
|
||||
|
||||
@subscribe ipc, 'command', (command, args...) ->
|
||||
activeElement = document.activeElement
|
||||
# Use the workspace element view if body has focus
|
||||
if activeElement is document.body and workspaceElement = atom.views.getView(atom.workspace)
|
||||
activeElement = workspaceElement
|
||||
|
||||
atom.commands.dispatch(activeElement, command, args[0])
|
||||
|
||||
@subscribe ipc, 'context-command', (command, args...) ->
|
||||
$(atom.contextMenu.activeElement).trigger(command, args...)
|
||||
|
||||
@subscribe $(window), 'focus', -> document.body.classList.remove('is-blurred')
|
||||
|
||||
@subscribe $(window), 'blur', -> document.body.classList.add('is-blurred')
|
||||
|
||||
@subscribe $(window), 'beforeunload', =>
|
||||
confirmed = atom.workspace?.confirmClose(windowCloseRequested: true)
|
||||
atom.hide() if confirmed and not @reloadRequested and atom.getCurrentWindow().isWebViewFocused()
|
||||
@reloadRequested = false
|
||||
|
||||
atom.storeDefaultWindowDimensions()
|
||||
atom.storeWindowDimensions()
|
||||
if confirmed
|
||||
atom.unloadEditorWindow()
|
||||
else
|
||||
ipc.send('cancel-window-close')
|
||||
|
||||
confirmed
|
||||
|
||||
@subscribe $(window), 'blur', -> atom.storeDefaultWindowDimensions()
|
||||
|
||||
@subscribe $(window), 'unload', -> atom.removeEditorWindow()
|
||||
|
||||
@subscribeToCommand $(window), 'window:toggle-full-screen', -> atom.toggleFullScreen()
|
||||
|
||||
@subscribeToCommand $(window), 'window:close', -> atom.close()
|
||||
|
||||
@subscribeToCommand $(window), 'window:reload', =>
|
||||
@reloadRequested = true
|
||||
atom.reload()
|
||||
|
||||
@subscribeToCommand $(window), 'window:toggle-dev-tools', -> atom.toggleDevTools()
|
||||
@subscriptions.add atom.commands.add window,
|
||||
'window:toggle-full-screen': @handleWindowToggleFullScreen
|
||||
'window:close': @handleWindowClose
|
||||
'window:reload': @handleWindowReload
|
||||
'window:toggle-dev-tools': @handleWindowToggleDevTools
|
||||
|
||||
if process.platform in ['win32', 'linux']
|
||||
@subscribeToCommand $(window), 'window:toggle-menu-bar', ->
|
||||
atom.config.set('core.autoHideMenuBar', not atom.config.get('core.autoHideMenuBar'))
|
||||
@subscriptions.add atom.commands.add window,
|
||||
'window:toggle-menu-bar': @handleWindowToggleMenuBar
|
||||
|
||||
if atom.config.get('core.autoHideMenuBar')
|
||||
detail = "To toggle, press the Alt key or execute the window:toggle-menu-bar command"
|
||||
atom.notifications.addInfo('Menu bar hidden', {detail})
|
||||
|
||||
@subscribeToCommand $(document), 'core:focus-next', @focusNext
|
||||
|
||||
@subscribeToCommand $(document), 'core:focus-previous', @focusPrevious
|
||||
|
||||
document.addEventListener 'keydown', @onKeydown
|
||||
|
||||
document.addEventListener 'drop', @onDrop
|
||||
@subscribe new Disposable =>
|
||||
document.removeEventListener('drop', @onDrop)
|
||||
|
||||
document.addEventListener 'dragover', @onDragOver
|
||||
@subscribe new Disposable =>
|
||||
document.removeEventListener('dragover', @onDragOver)
|
||||
|
||||
@subscribe $(document), 'click', 'a', @openLink
|
||||
|
||||
# Prevent form submits from changing the current window's URL
|
||||
@subscribe $(document), 'submit', 'form', (e) -> e.preventDefault()
|
||||
|
||||
@subscribe $(document), 'contextmenu', (e) ->
|
||||
e.preventDefault()
|
||||
atom.contextMenu.showForEvent(e)
|
||||
@subscriptions.add atom.commands.add document,
|
||||
'core:focus-next': @handleFocusNext
|
||||
'core:focus-previous': @handleFocusPrevious
|
||||
|
||||
@handleNativeKeybindings()
|
||||
|
||||
# Wire commands that should be handled by Chromium for elements with the
|
||||
# `.native-key-bindings` class.
|
||||
handleNativeKeybindings: ->
|
||||
menu = null
|
||||
bindCommandToAction = (command, action) =>
|
||||
@subscribe $(document), command, (event) ->
|
||||
@addEventListener document, command, (event) ->
|
||||
if event.target.webkitMatchesSelector('.native-key-bindings')
|
||||
atom.getCurrentWindow().webContents[action]()
|
||||
true
|
||||
|
||||
bindCommandToAction('core:copy', 'copy')
|
||||
bindCommandToAction('core:paste', 'paste')
|
||||
@@ -135,38 +59,41 @@ class WindowEventHandler
|
||||
bindCommandToAction('core:select-all', 'selectAll')
|
||||
bindCommandToAction('core:cut', 'cut')
|
||||
|
||||
onKeydown: (event) ->
|
||||
unsubscribe: ->
|
||||
@subscriptions.dispose()
|
||||
|
||||
on: (target, eventName, handler) ->
|
||||
target.on(eventName, handler)
|
||||
@subscriptions.add(new Disposable ->
|
||||
target.removeListener(eventName, handler)
|
||||
)
|
||||
|
||||
addEventListener: (target, eventName, handler) ->
|
||||
target.addEventListener(eventName, handler)
|
||||
@subscriptions.add(new Disposable(-> target.removeEventListener(eventName, handler)))
|
||||
|
||||
handleDocumentKeydown: (event) ->
|
||||
atom.keymaps.handleKeyboardEvent(event)
|
||||
event.stopImmediatePropagation()
|
||||
|
||||
onDrop: (event) ->
|
||||
handleDrop: (evenDocumentt) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
onDragOver: (event) ->
|
||||
handleDragover: (Documentevent) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
event.dataTransfer.dropEffect = 'none'
|
||||
|
||||
openLink: ({target, currentTarget}) ->
|
||||
location = target?.getAttribute('href') or currentTarget?.getAttribute('href')
|
||||
if location and location[0] isnt '#' and /^https?:\/\//.test(location)
|
||||
shell.openExternal(location)
|
||||
false
|
||||
|
||||
eachTabIndexedElement: (callback) ->
|
||||
for element in $('[tabindex]')
|
||||
element = $(element)
|
||||
continue if element.isDisabled()
|
||||
|
||||
tabIndex = parseInt(element.attr('tabindex'))
|
||||
continue unless tabIndex >= 0
|
||||
|
||||
callback(element, tabIndex)
|
||||
for element in document.querySelectorAll('[tabindex]')
|
||||
continue if element.disabled
|
||||
continue unless element.tabIndex >= 0
|
||||
callback(element, element.tabIndex)
|
||||
return
|
||||
|
||||
focusNext: =>
|
||||
focusedTabIndex = parseInt($(':focus').attr('tabindex')) or -Infinity
|
||||
handleFocusNext: =>
|
||||
focusedTabIndex = document.activeElement.tabIndex ? -Infinity
|
||||
|
||||
nextElement = null
|
||||
nextTabIndex = Infinity
|
||||
@@ -186,8 +113,8 @@ class WindowEventHandler
|
||||
else if lowestElement?
|
||||
lowestElement.focus()
|
||||
|
||||
focusPrevious: =>
|
||||
focusedTabIndex = parseInt($(':focus').attr('tabindex')) or Infinity
|
||||
handleFocusPrevious: =>
|
||||
focusedTabIndex = document.activeElement.tabIndex ? Infinity
|
||||
|
||||
previousElement = null
|
||||
previousTabIndex = -Infinity
|
||||
@@ -206,3 +133,92 @@ class WindowEventHandler
|
||||
previousElement.focus()
|
||||
else if highestElement?
|
||||
highestElement.focus()
|
||||
|
||||
handleIPCMessage: (message, detail) ->
|
||||
switch message
|
||||
when 'open-locations'
|
||||
needsProjectPaths = atom.project?.getPaths().length is 0
|
||||
|
||||
for {pathToOpen, initialLine, initialColumn} in detail
|
||||
if pathToOpen? and needsProjectPaths
|
||||
if fs.existsSync(pathToOpen)
|
||||
atom.project.addPath(pathToOpen)
|
||||
else if fs.existsSync(path.dirname(pathToOpen))
|
||||
atom.project.addPath(path.dirname(pathToOpen))
|
||||
else
|
||||
atom.project.addPath(pathToOpen)
|
||||
|
||||
unless fs.isDirectorySync(pathToOpen)
|
||||
atom.workspace?.open(pathToOpen, {initialLine, initialColumn})
|
||||
return
|
||||
when 'update-available'
|
||||
atom.updateAvailable(detail)
|
||||
|
||||
handleIPCCommand: (command, args...) ->
|
||||
activeElement = document.activeElement
|
||||
# Use the workspace element view if body has focus
|
||||
if activeElement is document.body and workspaceElement = atom.views.getView(atom.workspace)
|
||||
activeElement = workspaceElement
|
||||
|
||||
atom.commands.dispatch(activeElement, command, args[0])
|
||||
|
||||
handleIPCContextCommand: (command, args...) ->
|
||||
atom.commands.dispatch(atom.contextMenu.activeElement, command, args)
|
||||
|
||||
handleWindowFocus: ->
|
||||
document.body.classList.remove('is-blurred')
|
||||
|
||||
handleWindowBlur: ->
|
||||
document.body.classList.add('is-blurred')
|
||||
atom.storeDefaultWindowDimensions()
|
||||
|
||||
handleWindowBeforeunload: =>
|
||||
confirmed = atom.workspace?.confirmClose(windowCloseRequested: true)
|
||||
atom.hide() if confirmed and not @reloadRequested and atom.getCurrentWindow().isWebViewFocused()
|
||||
@reloadRequested = false
|
||||
|
||||
atom.storeDefaultWindowDimensions()
|
||||
atom.storeWindowDimensions()
|
||||
if confirmed
|
||||
atom.unloadEditorWindow()
|
||||
else
|
||||
ipc.send('cancel-window-close')
|
||||
|
||||
confirmed
|
||||
|
||||
handleWindowUnload: ->
|
||||
atom.removeEditorWindow()
|
||||
|
||||
handleWindowToggleFullScreen: ->
|
||||
atom.toggleFullScreen()
|
||||
|
||||
handleWindowClose: ->
|
||||
atom.close()
|
||||
|
||||
handleWindowReload: ->
|
||||
@reloadRequested = true
|
||||
atom.reload()
|
||||
|
||||
handleWindowToggleDevTools: ->
|
||||
atom.toggleDevTools()
|
||||
|
||||
handleWindowToggleMenuBar: ->
|
||||
atom.config.set('core.autoHideMenuBar', not atom.config.get('core.autoHideMenuBar'))
|
||||
|
||||
if atom.config.get('core.autoHideMenuBar')
|
||||
detail = "To toggle, press the Alt key or execute the window:toggle-menu-bar command"
|
||||
atom.notifications.addInfo('Menu bar hidden', {detail})
|
||||
|
||||
handleLinkClick: (event) ->
|
||||
event.preventDefault()
|
||||
location = event.currentTarget?.getAttribute('href')
|
||||
if location and location[0] isnt '#' and /^https?:\/\//.test(location)
|
||||
shell.openExternal(location)
|
||||
|
||||
handleFormSubmit: (event) ->
|
||||
# Prevent form submits from changing the current window's URL
|
||||
event.preventDefault()
|
||||
|
||||
handleDocumentContextmenu: (event) ->
|
||||
event.preventDefault()
|
||||
atom.contextMenu.showForEvent(event)
|
||||
|
||||
@@ -3,8 +3,6 @@ path = require 'path'
|
||||
{Disposable, CompositeDisposable} = require 'event-kit'
|
||||
Grim = require 'grim'
|
||||
scrollbarStyle = require 'scrollbar-style'
|
||||
{callAttachHooks} = require 'space-pen'
|
||||
WorkspaceView = null
|
||||
|
||||
module.exports =
|
||||
class WorkspaceElement extends HTMLElement
|
||||
@@ -15,10 +13,8 @@ class WorkspaceElement extends HTMLElement
|
||||
@initializeContent()
|
||||
@observeScrollbarStyle()
|
||||
@observeTextEditorFontConfig()
|
||||
@createSpacePenShim() if Grim.includeDeprecatedAPIs
|
||||
|
||||
attachedCallback: ->
|
||||
callAttachHooks(this) if Grim.includeDeprecatedAPIs
|
||||
@focus()
|
||||
|
||||
detachedCallback: ->
|
||||
@@ -64,10 +60,6 @@ class WorkspaceElement extends HTMLElement
|
||||
"""
|
||||
atom.styles.addStyleSheet(styleSheetSource, sourcePath: 'global-text-editor-styles')
|
||||
|
||||
createSpacePenShim: ->
|
||||
WorkspaceView ?= require './workspace-view'
|
||||
@__spacePenView = new WorkspaceView(this)
|
||||
|
||||
initialize: (@model) ->
|
||||
@paneContainer = atom.views.getView(@model.paneContainer)
|
||||
@verticalAxis.appendChild(@paneContainer)
|
||||
@@ -88,7 +80,6 @@ class WorkspaceElement extends HTMLElement
|
||||
|
||||
@appendChild(@panelContainers.modal)
|
||||
|
||||
@__spacePenView.setModel(@model) if Grim.includeDeprecatedAPIs
|
||||
this
|
||||
|
||||
getModel: -> @model
|
||||
@@ -117,7 +108,6 @@ atom.commands.add 'atom-workspace',
|
||||
'window:reset-font-size': -> @getModel().resetFontSize()
|
||||
'application:about': -> ipc.send('command', 'application:about')
|
||||
'application:run-all-specs': -> ipc.send('command', 'application:run-all-specs')
|
||||
'application:run-benchmarks': -> ipc.send('command', 'application:run-benchmarks')
|
||||
'application:show-preferences': -> ipc.send('command', 'application:show-settings')
|
||||
'application:show-settings': -> ipc.send('command', 'application:show-settings')
|
||||
'application:quit': -> ipc.send('command', 'application:quit')
|
||||
|
||||
@@ -1,340 +0,0 @@
|
||||
ipc = require 'ipc'
|
||||
path = require 'path'
|
||||
Q = require 'q'
|
||||
_ = require 'underscore-plus'
|
||||
Delegator = require 'delegato'
|
||||
{deprecate, logDeprecationWarnings} = require 'grim'
|
||||
{$, $$, View} = require './space-pen-extensions'
|
||||
fs = require 'fs-plus'
|
||||
Workspace = require './workspace'
|
||||
PaneView = require './pane-view'
|
||||
PaneContainerView = require './pane-container-view'
|
||||
TextEditor = require './text-editor'
|
||||
|
||||
# Deprecated: The top-level view for the entire window. An instance of this class is
|
||||
# available via the `atom.workspaceView` global.
|
||||
#
|
||||
# It is backed by a model object, an instance of {Workspace}, which is available
|
||||
# via the `atom.workspace` global or {::getModel}. You should prefer to interact
|
||||
# with the model object when possible, but it won't always be possible with the
|
||||
# current API.
|
||||
#
|
||||
# ## Adding Perimeter Panels
|
||||
#
|
||||
# Use the following methods if possible to attach panels to the perimeter of the
|
||||
# workspace rather than manipulating the DOM directly to better insulate you to
|
||||
# changes in the workspace markup:
|
||||
#
|
||||
# * {::prependToTop}
|
||||
# * {::appendToTop}
|
||||
# * {::prependToBottom}
|
||||
# * {::appendToBottom}
|
||||
# * {::prependToLeft}
|
||||
# * {::appendToLeft}
|
||||
# * {::prependToRight}
|
||||
# * {::appendToRight}
|
||||
#
|
||||
# ## Requiring in package specs
|
||||
#
|
||||
# If you need a `WorkspaceView` instance to test your package, require it via
|
||||
# the built-in `atom` module.
|
||||
#
|
||||
# ```coffee
|
||||
# {WorkspaceView} = require 'atom'
|
||||
# ```
|
||||
#
|
||||
# You can assign it to the `atom.workspaceView` global in the spec or just use
|
||||
# it as a local, depending on what you're trying to accomplish. Building the
|
||||
# `WorkspaceView` is currently expensive, so you should try build a {Workspace}
|
||||
# instead if possible.
|
||||
module.exports =
|
||||
class WorkspaceView extends View
|
||||
Delegator.includeInto(this)
|
||||
|
||||
@delegatesProperty 'fullScreen', 'destroyedItemURIs', toProperty: 'model'
|
||||
@delegatesMethods 'open', 'openSync',
|
||||
'saveActivePaneItem', 'saveActivePaneItemAs', 'saveAll', 'destroyActivePaneItem',
|
||||
'destroyActivePane', 'increaseFontSize', 'decreaseFontSize', toProperty: 'model'
|
||||
|
||||
constructor: (@element) ->
|
||||
unless @element?
|
||||
return atom.views.getView(atom.workspace).__spacePenView
|
||||
super
|
||||
@deprecateViewEvents()
|
||||
@attachedEditorViews = new WeakSet
|
||||
|
||||
setModel: (@model) ->
|
||||
@horizontal = @find('atom-workspace-axis.horizontal')
|
||||
@vertical = @find('atom-workspace-axis.vertical')
|
||||
@panes = @find('atom-pane-container').view()
|
||||
@subscribe @model.onDidOpen => @trigger 'uri-opened'
|
||||
|
||||
beforeRemove: ->
|
||||
@model?.destroy()
|
||||
|
||||
###
|
||||
Section: Accessing the Workspace Model
|
||||
###
|
||||
|
||||
# Essential: Get the underlying model object.
|
||||
#
|
||||
# Returns a {Workspace}.
|
||||
getModel: -> @model
|
||||
|
||||
###
|
||||
Section: Accessing Views
|
||||
###
|
||||
|
||||
# Essential: Register a function to be called for every current and future
|
||||
# editor view in the workspace (only includes {TextEditorView}s that are pane
|
||||
# items).
|
||||
#
|
||||
# * `callback` A {Function} with an {TextEditorView} as its only argument.
|
||||
# * `editorView` {TextEditorView}
|
||||
#
|
||||
# Returns a subscription object with an `.off` method that you can call to
|
||||
# unregister the callback.
|
||||
eachEditorView: (callback) ->
|
||||
for editorView in @getEditorViews()
|
||||
@attachedEditorViews.add(editorView)
|
||||
callback(editorView)
|
||||
|
||||
attachedCallback = (e, editorView) =>
|
||||
unless @attachedEditorViews.has(editorView)
|
||||
@attachedEditorViews.add(editorView)
|
||||
callback(editorView) unless editorView.mini
|
||||
|
||||
@on('editor:attached', attachedCallback)
|
||||
|
||||
off: => @off('editor:attached', attachedCallback)
|
||||
|
||||
# Essential: Register a function to be called for every current and future
|
||||
# pane view in the workspace.
|
||||
#
|
||||
# * `callback` A {Function} with a {PaneView} as its only argument.
|
||||
# * `paneView` {PaneView}
|
||||
#
|
||||
# Returns a subscription object with an `.off` method that you can call to
|
||||
# unregister the callback.
|
||||
eachPaneView: (callback) ->
|
||||
@panes.eachPaneView(callback)
|
||||
|
||||
# Essential: Get all existing pane views.
|
||||
#
|
||||
# Prefer {Workspace::getPanes} if you don't need access to the view objects.
|
||||
# Also consider using {::eachPaneView} if you want to register a callback for
|
||||
# all current and *future* pane views.
|
||||
#
|
||||
# Returns an Array of all open {PaneView}s.
|
||||
getPaneViews: ->
|
||||
@panes.getPaneViews()
|
||||
|
||||
# Essential: Get the active pane view.
|
||||
#
|
||||
# Prefer {Workspace::getActivePane} if you don't actually need access to the
|
||||
# view.
|
||||
#
|
||||
# Returns a {PaneView}.
|
||||
getActivePaneView: ->
|
||||
@panes.getActivePaneView()
|
||||
|
||||
# Essential: Get the view associated with the active pane item.
|
||||
#
|
||||
# Returns a view.
|
||||
getActiveView: ->
|
||||
@panes.getActiveView()
|
||||
|
||||
###
|
||||
Section: Adding elements to the workspace
|
||||
###
|
||||
|
||||
prependToTop: (element) ->
|
||||
deprecate 'Please use Workspace::addTopPanel() instead'
|
||||
@vertical.prepend(element)
|
||||
|
||||
appendToTop: (element) ->
|
||||
deprecate 'Please use Workspace::addTopPanel() instead'
|
||||
@panes.before(element)
|
||||
|
||||
prependToBottom: (element) ->
|
||||
deprecate 'Please use Workspace::addBottomPanel() instead'
|
||||
@panes.after(element)
|
||||
|
||||
appendToBottom: (element) ->
|
||||
deprecate 'Please use Workspace::addBottomPanel() instead'
|
||||
@vertical.append(element)
|
||||
|
||||
prependToLeft: (element) ->
|
||||
deprecate 'Please use Workspace::addLeftPanel() instead'
|
||||
@horizontal.prepend(element)
|
||||
|
||||
appendToLeft: (element) ->
|
||||
deprecate 'Please use Workspace::addLeftPanel() instead'
|
||||
@vertical.before(element)
|
||||
|
||||
prependToRight: (element) ->
|
||||
deprecate 'Please use Workspace::addRightPanel() instead'
|
||||
@vertical.after(element)
|
||||
|
||||
appendToRight: (element) ->
|
||||
deprecate 'Please use Workspace::addRightPanel() instead'
|
||||
@horizontal.append(element)
|
||||
|
||||
###
|
||||
Section: Focusing pane views
|
||||
###
|
||||
|
||||
# Focus the previous pane by id.
|
||||
focusPreviousPaneView: -> @model.activatePreviousPane()
|
||||
|
||||
# Focus the next pane by id.
|
||||
focusNextPaneView: -> @model.activateNextPane()
|
||||
|
||||
# Focus the pane directly above the active pane.
|
||||
focusPaneViewAbove: -> @panes.focusPaneViewAbove()
|
||||
|
||||
# Focus the pane directly below the active pane.
|
||||
focusPaneViewBelow: -> @panes.focusPaneViewBelow()
|
||||
|
||||
# Focus the pane directly to the left of the active pane.
|
||||
focusPaneViewOnLeft: -> @panes.focusPaneViewOnLeft()
|
||||
|
||||
# Focus the pane directly to the right of the active pane.
|
||||
focusPaneViewOnRight: -> @panes.focusPaneViewOnRight()
|
||||
|
||||
###
|
||||
Section: Private
|
||||
###
|
||||
|
||||
# Prompts to save all unsaved items
|
||||
confirmClose: ->
|
||||
@model.confirmClose()
|
||||
|
||||
# Get all editor views.
|
||||
#
|
||||
# You should prefer {Workspace::getEditors} unless you absolutely need access
|
||||
# to the view objects. Also consider using {::eachEditorView}, which will call
|
||||
# a callback for all current and *future* editor views.
|
||||
#
|
||||
# Returns an {Array} of {TextEditorView}s.
|
||||
getEditorViews: ->
|
||||
for editorElement in @panes.element.querySelectorAll('atom-pane > .item-views > atom-text-editor')
|
||||
$(editorElement).view()
|
||||
|
||||
###
|
||||
Section: Deprecated
|
||||
###
|
||||
|
||||
deprecateViewEvents: ->
|
||||
originalWorkspaceViewOn = @on
|
||||
|
||||
@on = (eventName) =>
|
||||
switch eventName
|
||||
when 'beep'
|
||||
deprecate('Use Atom::onDidBeep instead')
|
||||
when 'cursor:moved'
|
||||
deprecate('Use TextEditor::onDidChangeCursorPosition instead')
|
||||
when 'editor:attached'
|
||||
deprecate('Use Workspace::onDidAddTextEditor instead')
|
||||
when 'editor:detached'
|
||||
deprecate('Use TextEditor::onDidDestroy instead')
|
||||
when 'editor:will-be-removed'
|
||||
deprecate('Use TextEditor::onDidDestroy instead')
|
||||
when 'pane:active-item-changed'
|
||||
deprecate('Use Pane::onDidChangeActiveItem instead')
|
||||
when 'pane:active-item-modified-status-changed'
|
||||
deprecate('Use Pane::onDidChangeActiveItem and call onDidChangeModified on the active item instead')
|
||||
when 'pane:active-item-title-changed'
|
||||
deprecate('Use Pane::onDidChangeActiveItem and call onDidChangeTitle on the active item instead')
|
||||
when 'pane:attached'
|
||||
deprecate('Use Workspace::onDidAddPane instead')
|
||||
when 'pane:became-active'
|
||||
deprecate('Use Pane::onDidActivate instead')
|
||||
when 'pane:became-inactive'
|
||||
deprecate('Use Pane::onDidChangeActive instead')
|
||||
when 'pane:item-added'
|
||||
deprecate('Use Pane::onDidAddItem instead')
|
||||
when 'pane:item-moved'
|
||||
deprecate('Use Pane::onDidMoveItem instead')
|
||||
when 'pane:item-removed'
|
||||
deprecate('Use Pane::onDidRemoveItem instead')
|
||||
when 'pane:removed'
|
||||
deprecate('Use Pane::onDidDestroy instead')
|
||||
when 'pane-container:active-pane-item-changed'
|
||||
deprecate('Use Workspace::onDidChangeActivePaneItem instead')
|
||||
when 'selection:changed'
|
||||
deprecate('Use TextEditor::onDidChangeSelectionRange instead')
|
||||
when 'uri-opened'
|
||||
deprecate('Use Workspace::onDidOpen instead')
|
||||
originalWorkspaceViewOn.apply(this, arguments)
|
||||
|
||||
TextEditorView = require './text-editor-view'
|
||||
originalEditorViewOn = TextEditorView::on
|
||||
TextEditorView::on = (eventName) ->
|
||||
switch eventName
|
||||
when 'cursor:moved'
|
||||
deprecate('Use TextEditor::onDidChangeCursorPosition instead')
|
||||
when 'editor:attached'
|
||||
deprecate('Use TextEditor::onDidAddTextEditor instead')
|
||||
when 'editor:detached'
|
||||
deprecate('Use TextEditor::onDidDestroy instead')
|
||||
when 'editor:will-be-removed'
|
||||
deprecate('Use TextEditor::onDidDestroy instead')
|
||||
when 'selection:changed'
|
||||
deprecate('Use TextEditor::onDidChangeSelectionRange instead')
|
||||
originalEditorViewOn.apply(this, arguments)
|
||||
|
||||
originalPaneViewOn = PaneView::on
|
||||
PaneView::on = (eventName) ->
|
||||
switch eventName
|
||||
when 'cursor:moved'
|
||||
deprecate('Use TextEditor::onDidChangeCursorPosition instead')
|
||||
when 'editor:attached'
|
||||
deprecate('Use TextEditor::onDidAddTextEditor instead')
|
||||
when 'editor:detached'
|
||||
deprecate('Use TextEditor::onDidDestroy instead')
|
||||
when 'editor:will-be-removed'
|
||||
deprecate('Use TextEditor::onDidDestroy instead')
|
||||
when 'pane:active-item-changed'
|
||||
deprecate('Use Pane::onDidChangeActiveItem instead')
|
||||
when 'pane:active-item-modified-status-changed'
|
||||
deprecate('Use Pane::onDidChangeActiveItem and call onDidChangeModified on the active item instead')
|
||||
when 'pane:active-item-title-changed'
|
||||
deprecate('Use Pane::onDidChangeActiveItem and call onDidChangeTitle on the active item instead')
|
||||
when 'pane:attached'
|
||||
deprecate('Use Workspace::onDidAddPane instead')
|
||||
when 'pane:became-active'
|
||||
deprecate('Use Pane::onDidActivate instead')
|
||||
when 'pane:became-inactive'
|
||||
deprecate('Use Pane::onDidChangeActive instead')
|
||||
when 'pane:item-added'
|
||||
deprecate('Use Pane::onDidAddItem instead')
|
||||
when 'pane:item-moved'
|
||||
deprecate('Use Pane::onDidMoveItem instead')
|
||||
when 'pane:item-removed'
|
||||
deprecate('Use Pane::onDidRemoveItem instead')
|
||||
when 'pane:removed'
|
||||
deprecate('Use Pane::onDidDestroy instead')
|
||||
when 'selection:changed'
|
||||
deprecate('Use TextEditor::onDidChangeSelectionRange instead')
|
||||
originalPaneViewOn.apply(this, arguments)
|
||||
|
||||
# Deprecated
|
||||
eachPane: (callback) ->
|
||||
deprecate("Use WorkspaceView::eachPaneView instead")
|
||||
@eachPaneView(callback)
|
||||
|
||||
# Deprecated
|
||||
getPanes: ->
|
||||
deprecate("Use WorkspaceView::getPaneViews instead")
|
||||
@getPaneViews()
|
||||
|
||||
# Deprecated
|
||||
getActivePane: ->
|
||||
deprecate("Use WorkspaceView::getActivePaneView instead")
|
||||
@getActivePaneView()
|
||||
|
||||
# Deprecated: Call {Workspace::getActivePaneItem} instead.
|
||||
getActivePaneItem: ->
|
||||
deprecate("Use Workspace::getActivePaneItem instead")
|
||||
@model.getActivePaneItem()
|
||||
@@ -2,7 +2,6 @@
|
||||
_ = require 'underscore-plus'
|
||||
path = require 'path'
|
||||
{join} = path
|
||||
Q = require 'q'
|
||||
Serializable = require 'serializable'
|
||||
{Emitter, Disposable, CompositeDisposable} = require 'event-kit'
|
||||
Grim = require 'grim'
|
||||
@@ -448,17 +447,17 @@ class Workspace extends Model
|
||||
catch error
|
||||
switch error.code
|
||||
when 'CANCELLED'
|
||||
return Q()
|
||||
return Promise.resolve()
|
||||
when 'EACCES'
|
||||
atom.notifications.addWarning("Permission denied '#{error.path}'")
|
||||
return Q()
|
||||
return Promise.resolve()
|
||||
when 'EPERM', 'EBUSY', 'ENXIO', 'EIO', 'ENOTCONN', 'UNKNOWN', 'ECONNRESET', 'EINVAL'
|
||||
atom.notifications.addWarning("Unable to open '#{error.path ? uri}'", detail: error.message)
|
||||
return Q()
|
||||
return Promise.resolve()
|
||||
else
|
||||
throw error
|
||||
|
||||
Q(item)
|
||||
Promise.resolve(item)
|
||||
.then (item) =>
|
||||
if not pane
|
||||
pane = new Pane(items: [item])
|
||||
@@ -488,7 +487,7 @@ class Workspace extends Model
|
||||
if uri = @destroyedItemURIs.pop()
|
||||
@open(uri)
|
||||
else
|
||||
Q()
|
||||
Promise.resolve()
|
||||
|
||||
# Public: Register an opener for a uri.
|
||||
#
|
||||
@@ -904,7 +903,12 @@ class Workspace extends Model
|
||||
resolve('cancelled')
|
||||
else
|
||||
resolve(null)
|
||||
searchPromise.then(onSuccess, reject)
|
||||
|
||||
onFailure = ->
|
||||
promise.cancel() for promise in allSearches
|
||||
reject()
|
||||
|
||||
searchPromise.then(onSuccess, onFailure)
|
||||
cancellablePromise.cancel = ->
|
||||
isCancelled = true
|
||||
# Note that cancelling all of the members of allSearches will cause all of the searches
|
||||
@@ -929,36 +933,33 @@ class Workspace extends Model
|
||||
#
|
||||
# Returns a `Promise`.
|
||||
replace: (regex, replacementText, filePaths, iterator) ->
|
||||
deferred = Q.defer()
|
||||
new Promise (resolve, reject) =>
|
||||
openPaths = (buffer.getPath() for buffer in atom.project.getBuffers())
|
||||
outOfProcessPaths = _.difference(filePaths, openPaths)
|
||||
|
||||
openPaths = (buffer.getPath() for buffer in atom.project.getBuffers())
|
||||
outOfProcessPaths = _.difference(filePaths, openPaths)
|
||||
inProcessFinished = not openPaths.length
|
||||
outOfProcessFinished = not outOfProcessPaths.length
|
||||
checkFinished = ->
|
||||
resolve() if outOfProcessFinished and inProcessFinished
|
||||
|
||||
inProcessFinished = not openPaths.length
|
||||
outOfProcessFinished = not outOfProcessPaths.length
|
||||
checkFinished = ->
|
||||
deferred.resolve() if outOfProcessFinished and inProcessFinished
|
||||
unless outOfProcessFinished.length
|
||||
flags = 'g'
|
||||
flags += 'i' if regex.ignoreCase
|
||||
|
||||
unless outOfProcessFinished.length
|
||||
flags = 'g'
|
||||
flags += 'i' if regex.ignoreCase
|
||||
task = Task.once require.resolve('./replace-handler'), outOfProcessPaths, regex.source, flags, replacementText, ->
|
||||
outOfProcessFinished = true
|
||||
checkFinished()
|
||||
|
||||
task = Task.once require.resolve('./replace-handler'), outOfProcessPaths, regex.source, flags, replacementText, ->
|
||||
outOfProcessFinished = true
|
||||
checkFinished()
|
||||
task.on 'replace:path-replaced', iterator
|
||||
task.on 'replace:file-error', (error) -> iterator(null, error)
|
||||
|
||||
task.on 'replace:path-replaced', iterator
|
||||
task.on 'replace:file-error', (error) -> iterator(null, error)
|
||||
for buffer in atom.project.getBuffers()
|
||||
continue unless buffer.getPath() in filePaths
|
||||
replacements = buffer.replace(regex, replacementText, iterator)
|
||||
iterator({filePath: buffer.getPath(), replacements}) if replacements
|
||||
|
||||
for buffer in atom.project.getBuffers()
|
||||
continue unless buffer.getPath() in filePaths
|
||||
replacements = buffer.replace(regex, replacementText, iterator)
|
||||
iterator({filePath: buffer.getPath(), replacements}) if replacements
|
||||
|
||||
inProcessFinished = true
|
||||
checkFinished()
|
||||
|
||||
deferred.promise
|
||||
inProcessFinished = true
|
||||
checkFinished()
|
||||
|
||||
if includeDeprecatedAPIs
|
||||
Workspace.properties
|
||||
|
||||
Reference in New Issue
Block a user