mirror of
https://github.com/atom/atom.git
synced 2026-02-05 20:25:04 -05:00
Merge branch 'master' into wl-electron-35
This commit is contained in:
@@ -106,6 +106,9 @@ class ApplicationDelegate
|
||||
setRepresentedFilename: (filename) ->
|
||||
ipcRenderer.send("call-window-method", "setRepresentedFilename", filename)
|
||||
|
||||
addRecentDocument: (filename) ->
|
||||
ipc.send("add-recent-document", filename)
|
||||
|
||||
setRepresentedDirectoryPaths: (paths) ->
|
||||
loadSettings = getWindowLoadSettings()
|
||||
loadSettings['initialPaths'] = paths
|
||||
|
||||
@@ -151,7 +151,7 @@ class AtomEnvironment extends Model
|
||||
@packages = new PackageManager({
|
||||
devMode, configDirPath, resourcePath, safeMode, @config, styleManager: @styles,
|
||||
commandRegistry: @commands, keymapManager: @keymaps, notificationManager: @notifications,
|
||||
grammarRegistry: @grammars
|
||||
grammarRegistry: @grammars, deserializerManager: @deserializers, viewRegistry: @views
|
||||
})
|
||||
|
||||
@themes = new ThemeManager({
|
||||
@@ -885,6 +885,8 @@ class AtomEnvironment extends Model
|
||||
else
|
||||
@project.addPath(pathToOpen)
|
||||
|
||||
@applicationDelegate.addRecentDocument(pathToOpen)
|
||||
|
||||
unless fs.isDirectorySync(pathToOpen)
|
||||
@workspace?.open(pathToOpen, {initialLine, initialColumn})
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ class AtomApplication
|
||||
@listenForArgumentsFromNewProcess()
|
||||
@setupJavaScriptArguments()
|
||||
@handleEvents()
|
||||
@setupDockMenu()
|
||||
@storageFolder = new StorageFolder(process.env.ATOM_HOME)
|
||||
|
||||
if options.pathsToOpen?.length > 0 or options.urlsToOpen?.length > 0 or options.test
|
||||
@@ -275,6 +276,16 @@ class AtomApplication
|
||||
ipcMain.on 'write-to-stderr', (event, output) ->
|
||||
process.stderr.write(output)
|
||||
|
||||
ipc.on 'add-recent-document', (event, filename) ->
|
||||
app.addRecentDocument(filename)
|
||||
|
||||
setupDockMenu: ->
|
||||
if process.platform is 'darwin'
|
||||
dockMenu = Menu.buildFromTemplate [
|
||||
{label: 'New Window', click: => @emit('application:new-window')}
|
||||
]
|
||||
app.dock.setMenu dockMenu
|
||||
|
||||
# Public: Executes the given command.
|
||||
#
|
||||
# If it isn't handled globally, delegate to the currently focused window.
|
||||
|
||||
@@ -163,10 +163,16 @@ var prepareStackTraceWithSourceMapping = Error.prepareStackTrace
|
||||
let prepareStackTrace = prepareStackTraceWithSourceMapping
|
||||
|
||||
function prepareStackTraceWithRawStackAssignment (error, frames) {
|
||||
error.rawStack = frames
|
||||
return prepareStackTrace(error, frames)
|
||||
if (error.rawStack) { // avoid infinite recursion
|
||||
return prepareStackTraceWithSourceMapping(error, frames)
|
||||
} else {
|
||||
error.rawStack = frames
|
||||
return prepareStackTrace(error, frames)
|
||||
}
|
||||
}
|
||||
|
||||
Error.stackTraceLimit = 30
|
||||
|
||||
Object.defineProperty(Error, 'prepareStackTrace', {
|
||||
get: function () {
|
||||
return prepareStackTraceWithRawStackAssignment
|
||||
|
||||
@@ -381,8 +381,8 @@ class Config
|
||||
# ```
|
||||
#
|
||||
# * `keyPath` {String} name of the key to observe
|
||||
# * `options` {Object}
|
||||
# * `scopeDescriptor` (optional) {ScopeDescriptor} describing a path from
|
||||
# * `options` (optional) {Object}
|
||||
# * `scope` (optional) {ScopeDescriptor} describing a path from
|
||||
# the root of the syntax tree to a token. Get one by calling
|
||||
# {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples.
|
||||
# See [the scopes docs](https://atom.io/docs/latest/behind-atom-scoped-settings-scopes-and-scope-descriptors)
|
||||
@@ -412,8 +412,8 @@ class Config
|
||||
#
|
||||
# * `keyPath` (optional) {String} name of the key to observe. Must be
|
||||
# specified if `scopeDescriptor` is specified.
|
||||
# * `optional` (optional) {Object}
|
||||
# * `scopeDescriptor` (optional) {ScopeDescriptor} describing a path from
|
||||
# * `options` (optional) {Object}
|
||||
# * `scope` (optional) {ScopeDescriptor} describing a path from
|
||||
# the root of the syntax tree to a token. Get one by calling
|
||||
# {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples.
|
||||
# See [the scopes docs](https://atom.io/docs/latest/behind-atom-scoped-settings-scopes-and-scope-descriptors)
|
||||
@@ -827,6 +827,7 @@ class Config
|
||||
|
||||
allSettings = {'*': @settings}
|
||||
allSettings = _.extend allSettings, @scopedSettingsStore.propertiesForSource(@getUserConfigPath())
|
||||
allSettings = sortObject(allSettings)
|
||||
try
|
||||
CSON.writeFileSync(@configFilePath, allSettings)
|
||||
catch error
|
||||
@@ -1190,6 +1191,13 @@ Config.addSchemaEnforcers
|
||||
isPlainObject = (value) ->
|
||||
_.isObject(value) and not _.isArray(value) and not _.isFunction(value) and not _.isString(value) and not (value instanceof Color)
|
||||
|
||||
sortObject = (value) ->
|
||||
return value unless isPlainObject(value)
|
||||
result = {}
|
||||
for key in Object.keys(value).sort()
|
||||
result[key] = sortObject(value[key])
|
||||
result
|
||||
|
||||
withoutEmptyObjects = (object) ->
|
||||
resultObject = undefined
|
||||
if isPlainObject(object)
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
_ = require 'underscore-plus'
|
||||
Model = require './model'
|
||||
|
||||
EmptyLineRegExp = /(\r\n[\t ]*\r\n)|(\n[\t ]*\n)/g
|
||||
|
||||
# Extended: The `Cursor` class represents the little blinking line identifying
|
||||
# where text can be inserted.
|
||||
#
|
||||
@@ -467,10 +469,13 @@ class Cursor extends Model
|
||||
scanRange = [[previousNonBlankRow, 0], currentBufferPosition]
|
||||
|
||||
beginningOfWordPosition = null
|
||||
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) ->
|
||||
if range.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious
|
||||
beginningOfWordPosition = range.start
|
||||
if not beginningOfWordPosition?.isEqual(currentBufferPosition)
|
||||
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, matchText, stop}) ->
|
||||
# Ignore 'empty line' matches between '\r' and '\n'
|
||||
return if matchText is '' and range.start.column isnt 0
|
||||
|
||||
if range.start.isLessThan(currentBufferPosition)
|
||||
if range.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious
|
||||
beginningOfWordPosition = range.start
|
||||
stop()
|
||||
|
||||
if beginningOfWordPosition?
|
||||
@@ -496,13 +501,12 @@ class Cursor extends Model
|
||||
scanRange = [currentBufferPosition, @editor.getEofBufferPosition()]
|
||||
|
||||
endOfWordPosition = null
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) ->
|
||||
if allowNext
|
||||
if range.end.isGreaterThan(currentBufferPosition)
|
||||
endOfWordPosition = range.end
|
||||
stop()
|
||||
else
|
||||
if range.start.isLessThanOrEqual(currentBufferPosition)
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, matchText, stop}) ->
|
||||
# Ignore 'empty line' matches between '\r' and '\n'
|
||||
return if matchText is '' and range.start.column isnt 0
|
||||
|
||||
if range.end.isGreaterThan(currentBufferPosition)
|
||||
if allowNext or range.start.isLessThanOrEqual(currentBufferPosition)
|
||||
endOfWordPosition = range.end
|
||||
stop()
|
||||
|
||||
@@ -603,14 +607,14 @@ class Cursor extends Model
|
||||
# non-word characters in the regex. (default: true)
|
||||
#
|
||||
# Returns a {RegExp}.
|
||||
wordRegExp: ({includeNonWordCharacters}={}) ->
|
||||
includeNonWordCharacters ?= true
|
||||
nonWordCharacters = @config.get('editor.nonWordCharacters', scope: @getScopeDescriptor())
|
||||
segments = ["^[\t ]*$"]
|
||||
segments.push("[^\\s#{_.escapeRegExp(nonWordCharacters)}]+")
|
||||
if includeNonWordCharacters
|
||||
segments.push("[#{_.escapeRegExp(nonWordCharacters)}]+")
|
||||
new RegExp(segments.join("|"), "g")
|
||||
wordRegExp: (options) ->
|
||||
scope = @getScopeDescriptor()
|
||||
nonWordCharacters = _.escapeRegExp(@config.get('editor.nonWordCharacters', {scope}))
|
||||
|
||||
source = "^[\t ]*$|[^\\s#{nonWordCharacters}]+"
|
||||
if options?.includeNonWordCharacters ? true
|
||||
source += "|" + "[#{nonWordCharacters}]+"
|
||||
new RegExp(source, "g")
|
||||
|
||||
# Public: Get the RegExp used by the cursor to determine what a "subword" is.
|
||||
#
|
||||
@@ -666,10 +670,9 @@ class Cursor extends Model
|
||||
{row, column} = eof
|
||||
position = new Point(row, column - 1)
|
||||
|
||||
@editor.scanInBufferRange /^\n*$/g, scanRange, ({range, stop}) ->
|
||||
unless range.start.isEqual(start)
|
||||
position = range.start
|
||||
stop()
|
||||
@editor.scanInBufferRange EmptyLineRegExp, scanRange, ({range, stop}) ->
|
||||
position = range.start.traverse(Point(1, 0))
|
||||
stop() unless position.isEqual(start)
|
||||
position
|
||||
|
||||
getBeginningOfPreviousParagraphBufferPosition: ->
|
||||
@@ -679,8 +682,7 @@ class Cursor extends Model
|
||||
scanRange = [[row-1, column], [0, 0]]
|
||||
position = new Point(0, 0)
|
||||
zero = new Point(0, 0)
|
||||
@editor.backwardsScanInBufferRange /^\n*$/g, scanRange, ({range, stop}) ->
|
||||
unless range.start.isEqual(zero)
|
||||
position = range.start
|
||||
stop()
|
||||
@editor.backwardsScanInBufferRange EmptyLineRegExp, scanRange, ({range, stop}) ->
|
||||
position = range.start.traverse(Point(1, 0))
|
||||
stop() unless position.isEqual(start)
|
||||
position
|
||||
|
||||
@@ -39,6 +39,9 @@ class DeserializerManager
|
||||
delete @deserializers[deserializer.name] for deserializer in deserializers
|
||||
return
|
||||
|
||||
getDeserializerCount: ->
|
||||
Object.keys(@deserializers).length
|
||||
|
||||
# Public: Deserialize the state and params.
|
||||
#
|
||||
# * `state` The state {Object} to deserialize.
|
||||
|
||||
@@ -32,7 +32,7 @@ KeymapManager::loadUserKeymap = ->
|
||||
return unless fs.isFileSync(userKeymapPath)
|
||||
|
||||
try
|
||||
@loadKeymap(userKeymapPath, watch: true, suppressErrors: true)
|
||||
@loadKeymap(userKeymapPath, watch: true, suppressErrors: true, priority: 100)
|
||||
catch error
|
||||
if error.message.indexOf('Unable to watch path') > -1
|
||||
message = """
|
||||
|
||||
@@ -3,7 +3,7 @@ TokenIterator = require './token-iterator'
|
||||
|
||||
module.exports =
|
||||
class LinesYardstick
|
||||
constructor: (@model, @presenter, @lineNodesProvider, grammarRegistry) ->
|
||||
constructor: (@model, @lineNodesProvider, grammarRegistry) ->
|
||||
@tokenIterator = new TokenIterator({grammarRegistry})
|
||||
@rangeForMeasurement = document.createRange()
|
||||
@invalidateCache()
|
||||
@@ -11,14 +11,12 @@ class LinesYardstick
|
||||
invalidateCache: ->
|
||||
@pixelPositionsByLineIdAndColumn = {}
|
||||
|
||||
prepareScreenRowsForMeasurement: (screenRows) ->
|
||||
@presenter.setScreenRowsToMeasure(screenRows)
|
||||
@lineNodesProvider.updateSync(@presenter.getPreMeasurementState())
|
||||
measuredRowForPixelPosition: (pixelPosition) ->
|
||||
targetTop = pixelPosition.top
|
||||
row = Math.floor(targetTop / @model.getLineHeightInPixels())
|
||||
row if 0 <= row <= @model.getLastScreenRow()
|
||||
|
||||
clearScreenRowsForMeasurement: ->
|
||||
@presenter.clearScreenRowsToMeasure()
|
||||
|
||||
screenPositionForPixelPosition: (pixelPosition, measureVisibleLinesOnly) ->
|
||||
screenPositionForPixelPosition: (pixelPosition) ->
|
||||
targetTop = pixelPosition.top
|
||||
targetLeft = pixelPosition.left
|
||||
defaultCharWidth = @model.getDefaultCharWidth()
|
||||
@@ -28,12 +26,10 @@ class LinesYardstick
|
||||
row = Math.min(row, @model.getLastScreenRow())
|
||||
row = Math.max(0, row)
|
||||
|
||||
@prepareScreenRowsForMeasurement([row]) unless measureVisibleLinesOnly
|
||||
|
||||
line = @model.tokenizedLineForScreenRow(row)
|
||||
lineNode = @lineNodesProvider.lineNodeForLineIdAndScreenRow(line?.id, row)
|
||||
|
||||
return new Point(row, 0) unless lineNode? and line?
|
||||
return Point(row, 0) unless lineNode? and line?
|
||||
|
||||
textNodes = @lineNodesProvider.textNodesForLineIdAndScreenRow(line.id, row)
|
||||
column = 0
|
||||
@@ -70,33 +66,27 @@ class LinesYardstick
|
||||
left = @leftPixelPositionForCharInTextNode(lineNode, textNode, indexWithinTextNode)
|
||||
charWidth = left - previousLeft
|
||||
|
||||
return new Point(row, previousColumn) if targetLeft <= previousLeft + (charWidth / 2)
|
||||
return Point(row, previousColumn) if targetLeft <= previousLeft + (charWidth / 2)
|
||||
|
||||
previousLeft = left
|
||||
previousColumn = column
|
||||
column += charLength
|
||||
|
||||
@clearScreenRowsForMeasurement() unless measureVisibleLinesOnly
|
||||
|
||||
if targetLeft <= previousLeft + (charWidth / 2)
|
||||
new Point(row, previousColumn)
|
||||
Point(row, previousColumn)
|
||||
else
|
||||
new Point(row, column)
|
||||
Point(row, column)
|
||||
|
||||
pixelPositionForScreenPosition: (screenPosition, clip=true, measureVisibleLinesOnly) ->
|
||||
pixelPositionForScreenPosition: (screenPosition, clip=true) ->
|
||||
screenPosition = Point.fromObject(screenPosition)
|
||||
screenPosition = @model.clipScreenPosition(screenPosition) if clip
|
||||
|
||||
targetRow = screenPosition.row
|
||||
targetColumn = screenPosition.column
|
||||
|
||||
@prepareScreenRowsForMeasurement([targetRow]) unless measureVisibleLinesOnly
|
||||
|
||||
top = targetRow * @model.getLineHeightInPixels()
|
||||
left = @leftPixelPositionForScreenPosition(targetRow, targetColumn)
|
||||
|
||||
@clearScreenRowsForMeasurement() unless measureVisibleLinesOnly
|
||||
|
||||
{top, left}
|
||||
|
||||
leftPixelPositionForScreenPosition: (row, column) ->
|
||||
@@ -173,18 +163,3 @@ class LinesYardstick
|
||||
offset = lineNode.getBoundingClientRect().left
|
||||
|
||||
left + width - offset
|
||||
|
||||
pixelRectForScreenRange: (screenRange, measureVisibleLinesOnly) ->
|
||||
lineHeight = @model.getLineHeightInPixels()
|
||||
|
||||
if screenRange.end.row > screenRange.start.row
|
||||
top = @pixelPositionForScreenPosition(screenRange.start, true, measureVisibleLinesOnly).top
|
||||
left = 0
|
||||
height = (screenRange.end.row - screenRange.start.row + 1) * lineHeight
|
||||
width = @presenter.getScrollWidth()
|
||||
else
|
||||
{top, left} = @pixelPositionForScreenPosition(screenRange.start, false, measureVisibleLinesOnly)
|
||||
height = lineHeight
|
||||
width = @pixelPositionForScreenPosition(screenRange.end, false, measureVisibleLinesOnly).left - left
|
||||
|
||||
{top, left, width, height}
|
||||
|
||||
@@ -31,7 +31,8 @@ class PackageManager
|
||||
constructor: (params) ->
|
||||
{
|
||||
configDirPath, @devMode, safeMode, @resourcePath, @config, @styleManager,
|
||||
@notificationManager, @keymapManager, @commandRegistry, @grammarRegistry
|
||||
@notificationManager, @keymapManager, @commandRegistry, @grammarRegistry,
|
||||
@deserializerManager, @viewRegistry
|
||||
} = params
|
||||
|
||||
@emitter = new Emitter
|
||||
@@ -46,6 +47,7 @@ class PackageManager
|
||||
@packagesCache = require('../package.json')?._atomPackages ? {}
|
||||
@loadedPackages = {}
|
||||
@activePackages = {}
|
||||
@activatingPackages = {}
|
||||
@packageStates = {}
|
||||
@serviceHub = new ServiceHub
|
||||
|
||||
@@ -61,6 +63,7 @@ class PackageManager
|
||||
reset: ->
|
||||
@serviceHub.clear()
|
||||
@deactivatePackages()
|
||||
@loadedPackages = {}
|
||||
@packageStates = {}
|
||||
|
||||
###
|
||||
@@ -375,7 +378,8 @@ class PackageManager
|
||||
options = {
|
||||
path: packagePath, metadata, packageManager: this, @config, @styleManager,
|
||||
@commandRegistry, @keymapManager, @devMode, @notificationManager,
|
||||
@grammarRegistry, @themeManager, @menuManager, @contextMenuManager
|
||||
@grammarRegistry, @themeManager, @menuManager, @contextMenuManager,
|
||||
@deserializerManager, @viewRegistry
|
||||
}
|
||||
if metadata.theme
|
||||
pack = new ThemePackage(options)
|
||||
@@ -434,9 +438,12 @@ class PackageManager
|
||||
if pack = @getActivePackage(name)
|
||||
Promise.resolve(pack)
|
||||
else if pack = @loadPackage(name)
|
||||
@activatingPackages[pack.name] = pack
|
||||
pack.activate().then =>
|
||||
@activePackages[pack.name] = pack
|
||||
@emitter.emit 'did-activate-package', pack
|
||||
if @activatingPackages[pack.name]?
|
||||
delete @activatingPackages[pack.name]
|
||||
@activePackages[pack.name] = pack
|
||||
@emitter.emit 'did-activate-package', pack
|
||||
pack
|
||||
else
|
||||
Promise.reject(new Error("Failed to load package '#{name}'"))
|
||||
@@ -472,6 +479,7 @@ class PackageManager
|
||||
@setPackageState(pack.name, state) if state = pack.serialize?()
|
||||
pack.deactivate()
|
||||
delete @activePackages[pack.name]
|
||||
delete @activatingPackages[pack.name]
|
||||
@emitter.emit 'did-deactivate-package', pack
|
||||
|
||||
handleMetadataError: (error, packagePath) ->
|
||||
|
||||
@@ -33,7 +33,7 @@ class Package
|
||||
{
|
||||
@path, @metadata, @packageManager, @config, @styleManager, @commandRegistry,
|
||||
@keymapManager, @devMode, @notificationManager, @grammarRegistry, @themeManager,
|
||||
@menuManager, @contextMenuManager
|
||||
@menuManager, @contextMenuManager, @deserializerManager, @viewRegistry
|
||||
} = params
|
||||
|
||||
@emitter = new Emitter
|
||||
@@ -84,12 +84,24 @@ class Package
|
||||
@loadKeymaps()
|
||||
@loadMenus()
|
||||
@loadStylesheets()
|
||||
@loadDeserializers()
|
||||
@configSchemaRegisteredOnLoad = @registerConfigSchemaFromMetadata()
|
||||
@settingsPromise = @loadSettings()
|
||||
@requireMainModule() unless @mainModule? or @activationShouldBeDeferred()
|
||||
if @shouldRequireMainModuleOnLoad() and not @mainModule?
|
||||
@requireMainModule()
|
||||
catch error
|
||||
@handleError("Failed to load the #{@name} package", error)
|
||||
this
|
||||
|
||||
shouldRequireMainModuleOnLoad: ->
|
||||
not (
|
||||
@metadata.deserializers? or
|
||||
@metadata.viewProviders? or
|
||||
@metadata.configSchema? or
|
||||
@activationShouldBeDeferred() or
|
||||
localStorage.getItem(@getCanDeferMainModuleRequireStorageKey()) is 'true'
|
||||
)
|
||||
|
||||
reset: ->
|
||||
@stylesheets = []
|
||||
@keymaps = []
|
||||
@@ -117,9 +129,12 @@ class Package
|
||||
|
||||
activateNow: ->
|
||||
try
|
||||
@activateConfig()
|
||||
@requireMainModule() unless @mainModule?
|
||||
@configSchemaRegisteredOnActivate = @registerConfigSchemaFromMainModule()
|
||||
@registerViewProviders()
|
||||
@activateStylesheets()
|
||||
if @mainModule? and not @mainActivated
|
||||
@mainModule.activateConfig?()
|
||||
@mainModule.activate?(@packageManager.getPackageState(@name) ? {})
|
||||
@mainActivated = true
|
||||
@activateServices()
|
||||
@@ -128,15 +143,22 @@ class Package
|
||||
|
||||
@resolveActivationPromise?()
|
||||
|
||||
activateConfig: ->
|
||||
return if @configActivated
|
||||
registerConfigSchemaFromMetadata: ->
|
||||
if configSchema = @metadata.configSchema
|
||||
@config.setSchema @name, {type: 'object', properties: configSchema}
|
||||
true
|
||||
else
|
||||
false
|
||||
|
||||
@requireMainModule() unless @mainModule?
|
||||
if @mainModule?
|
||||
registerConfigSchemaFromMainModule: ->
|
||||
if @mainModule? and not @configSchemaRegisteredOnLoad
|
||||
if @mainModule.config? and typeof @mainModule.config is 'object'
|
||||
@config.setSchema @name, {type: 'object', properties: @mainModule.config}
|
||||
@mainModule.activateConfig?()
|
||||
@configActivated = true
|
||||
return true
|
||||
false
|
||||
|
||||
# TODO: Remove. Settings view calls this method currently.
|
||||
activateConfig: -> @registerConfigSchemaFromMainModule()
|
||||
|
||||
activateStylesheets: ->
|
||||
return if @stylesheetsActivated
|
||||
@@ -253,6 +275,26 @@ class Package
|
||||
@stylesheets = @getStylesheetPaths().map (stylesheetPath) =>
|
||||
[stylesheetPath, @themeManager.loadStylesheet(stylesheetPath, true)]
|
||||
|
||||
loadDeserializers: ->
|
||||
if @metadata.deserializers?
|
||||
for name, implementationPath of @metadata.deserializers
|
||||
do =>
|
||||
deserializePath = path.join(@path, implementationPath)
|
||||
deserializeFunction = null
|
||||
atom.deserializers.add
|
||||
name: name,
|
||||
deserialize: =>
|
||||
@registerViewProviders()
|
||||
deserializeFunction ?= require(deserializePath)
|
||||
deserializeFunction.apply(this, arguments)
|
||||
return
|
||||
|
||||
registerViewProviders: ->
|
||||
if @metadata.viewProviders? and not @registeredViewProviders
|
||||
for implementationPath in @metadata.viewProviders
|
||||
@viewRegistry.addViewProvider(require(path.join(@path, implementationPath)))
|
||||
@registeredViewProviders = true
|
||||
|
||||
getStylesheetsPath: ->
|
||||
path.join(@path, 'styles')
|
||||
|
||||
@@ -343,21 +385,18 @@ class Package
|
||||
@activationPromise = null
|
||||
@resolveActivationPromise = null
|
||||
@activationCommandSubscriptions?.dispose()
|
||||
@configSchemaRegisteredOnActivate = false
|
||||
@deactivateResources()
|
||||
@deactivateConfig()
|
||||
@deactivateKeymaps()
|
||||
if @mainActivated
|
||||
try
|
||||
@mainModule?.deactivate?()
|
||||
@mainModule?.deactivateConfig?()
|
||||
@mainActivated = false
|
||||
catch e
|
||||
console.error "Error deactivating package '#{@name}'", e.stack
|
||||
@emitter.emit 'did-deactivate'
|
||||
|
||||
deactivateConfig: ->
|
||||
@mainModule?.deactivateConfig?()
|
||||
@configActivated = false
|
||||
|
||||
deactivateResources: ->
|
||||
grammar.deactivate() for grammar in @grammars
|
||||
settings.deactivate() for settings in @settings
|
||||
@@ -392,7 +431,13 @@ class Package
|
||||
mainModulePath = @getMainModulePath()
|
||||
if fs.isFileSync(mainModulePath)
|
||||
@mainModuleRequired = true
|
||||
|
||||
previousViewProviderCount = @viewRegistry.getViewProviderCount()
|
||||
previousDeserializerCount = @deserializerManager.getDeserializerCount()
|
||||
@mainModule = require(mainModulePath)
|
||||
if (@viewRegistry.getViewProviderCount() is previousViewProviderCount and
|
||||
@deserializerManager.getDeserializerCount() is previousDeserializerCount)
|
||||
localStorage.setItem(@getCanDeferMainModuleRequireStorageKey(), 'true')
|
||||
|
||||
getMainModulePath: ->
|
||||
return @mainModulePath if @resolvedMainModulePath
|
||||
@@ -586,6 +631,9 @@ class Package
|
||||
electronVersion = process.versions['electron'] ? process.versions['atom-shell']
|
||||
"installed-packages:#{@name}:#{@metadata.version}:electron-#{electronVersion}:incompatible-native-modules"
|
||||
|
||||
getCanDeferMainModuleRequireStorageKey: ->
|
||||
"installed-packages:#{@name}:#{@metadata.version}:can-defer-main-module-require"
|
||||
|
||||
# Get the incompatible native modules that this package depends on.
|
||||
# This recurses through all dependencies and requires all modules that
|
||||
# contain a `.node` file.
|
||||
|
||||
@@ -308,12 +308,20 @@ class Project extends Model
|
||||
findBufferForPath: (filePath) ->
|
||||
_.find @buffers, (buffer) -> buffer.getPath() is filePath
|
||||
|
||||
findBufferForId: (id) ->
|
||||
_.find @buffers, (buffer) -> buffer.getId() is id
|
||||
|
||||
# Only to be used in specs
|
||||
bufferForPathSync: (filePath) ->
|
||||
absoluteFilePath = @resolvePath(filePath)
|
||||
existingBuffer = @findBufferForPath(absoluteFilePath) if filePath
|
||||
existingBuffer ? @buildBufferSync(absoluteFilePath)
|
||||
|
||||
# Only to be used when deserializing
|
||||
bufferForIdSync: (id) ->
|
||||
existingBuffer = @findBufferForId(id) if id
|
||||
existingBuffer ? @buildBufferSync()
|
||||
|
||||
# Given a file path, this retrieves or creates a new {TextBuffer}.
|
||||
#
|
||||
# If the `filePath` already has a `buffer`, that value is used instead. Otherwise,
|
||||
@@ -329,9 +337,6 @@ class Project extends Model
|
||||
else
|
||||
@buildBuffer(absoluteFilePath)
|
||||
|
||||
bufferForId: (id) ->
|
||||
_.find @buffers, (buffer) -> buffer.id is id
|
||||
|
||||
# Still needed when deserializing a tokenized buffer
|
||||
buildBufferSync: (absoluteFilePath) ->
|
||||
buffer = new TextBuffer({filePath: absoluteFilePath})
|
||||
|
||||
@@ -82,7 +82,7 @@ class TextEditorComponent
|
||||
@linesComponent = new LinesComponent({@presenter, @hostElement, @useShadowDOM, @domElementPool, @assert, @grammars})
|
||||
@scrollViewNode.appendChild(@linesComponent.getDomNode())
|
||||
|
||||
@linesYardstick = new LinesYardstick(@editor, @presenter, @linesComponent, @grammars)
|
||||
@linesYardstick = new LinesYardstick(@editor, @linesComponent, @grammars)
|
||||
@presenter.setLinesYardstick(@linesYardstick)
|
||||
|
||||
@horizontalScrollbarComponent = new ScrollbarComponent({orientation: 'horizontal', onScroll: @onHorizontalScroll})
|
||||
@@ -127,8 +127,10 @@ class TextEditorComponent
|
||||
@domNode
|
||||
|
||||
updateSync: ->
|
||||
@updateSyncPreMeasurement()
|
||||
|
||||
@oldState ?= {}
|
||||
@newState = @presenter.getState()
|
||||
@newState = @presenter.getPostMeasurementState()
|
||||
|
||||
if @editor.getLastSelection()? and not @editor.getLastSelection().isEmpty()
|
||||
@domNode.classList.add('has-selection')
|
||||
@@ -170,6 +172,9 @@ class TextEditorComponent
|
||||
@updateParentViewFocusedClassIfNeeded()
|
||||
@updateParentViewMiniClass()
|
||||
|
||||
updateSyncPreMeasurement: ->
|
||||
@linesComponent.updateSync(@presenter.getPreMeasurementState())
|
||||
|
||||
readAfterUpdateSync: =>
|
||||
@overlayManager?.measureOverlays()
|
||||
|
||||
@@ -429,14 +434,42 @@ class TextEditorComponent
|
||||
getVisibleRowRange: ->
|
||||
@presenter.getVisibleRowRange()
|
||||
|
||||
pixelPositionForScreenPosition: ->
|
||||
@linesYardstick.pixelPositionForScreenPosition(arguments...)
|
||||
pixelPositionForScreenPosition: (screenPosition, clip) ->
|
||||
unless @presenter.isRowVisible(screenPosition.row)
|
||||
@presenter.setScreenRowsToMeasure([screenPosition.row])
|
||||
@updateSyncPreMeasurement()
|
||||
|
||||
screenPositionForPixelPosition: ->
|
||||
@linesYardstick.screenPositionForPixelPosition(arguments...)
|
||||
pixelPosition = @linesYardstick.pixelPositionForScreenPosition(screenPosition, clip)
|
||||
@presenter.clearScreenRowsToMeasure()
|
||||
pixelPosition
|
||||
|
||||
pixelRectForScreenRange: ->
|
||||
@linesYardstick.pixelRectForScreenRange(arguments...)
|
||||
screenPositionForPixelPosition: (pixelPosition) ->
|
||||
row = @linesYardstick.measuredRowForPixelPosition(pixelPosition)
|
||||
if row? and not @presenter.isRowVisible(row)
|
||||
@presenter.setScreenRowsToMeasure([row])
|
||||
@updateSyncPreMeasurement()
|
||||
|
||||
position = @linesYardstick.screenPositionForPixelPosition(pixelPosition)
|
||||
@presenter.clearScreenRowsToMeasure()
|
||||
position
|
||||
|
||||
pixelRectForScreenRange: (screenRange) ->
|
||||
rowsToMeasure = []
|
||||
unless @presenter.isRowVisible(screenRange.start.row)
|
||||
rowsToMeasure.push(screenRange.start.row)
|
||||
unless @presenter.isRowVisible(screenRange.end.row)
|
||||
rowsToMeasure.push(screenRange.end.row)
|
||||
|
||||
if rowsToMeasure.length > 0
|
||||
@presenter.setScreenRowsToMeasure(rowsToMeasure)
|
||||
@updateSyncPreMeasurement()
|
||||
|
||||
rect = @presenter.absolutePixelRectForScreenRange(screenRange)
|
||||
|
||||
if rowsToMeasure.length > 0
|
||||
@presenter.clearScreenRowsToMeasure()
|
||||
|
||||
rect
|
||||
|
||||
pixelRangeForScreenRange: (screenRange, clip=true) ->
|
||||
{start, end} = Range.fromObject(screenRange)
|
||||
|
||||
@@ -121,14 +121,6 @@ class TextEditorPresenter
|
||||
@updating = false
|
||||
|
||||
@resetTrackedUpdates()
|
||||
|
||||
# Public: Gets this presenter's state, updating it just in time before returning from this function.
|
||||
# Returns a state {Object}, useful for rendering to screen.
|
||||
getState: ->
|
||||
@linesYardstick.prepareScreenRowsForMeasurement()
|
||||
|
||||
@getPostMeasurementState()
|
||||
|
||||
@state
|
||||
|
||||
resetTrackedUpdates: ->
|
||||
@@ -1155,16 +1147,29 @@ class TextEditorPresenter
|
||||
hasOverlayPositionRequirements: ->
|
||||
@hasPixelRectRequirements() and @boundingClientRect? and @windowWidth and @windowHeight
|
||||
|
||||
absolutePixelRectForScreenRange: (screenRange) ->
|
||||
lineHeight = @model.getLineHeightInPixels()
|
||||
|
||||
if screenRange.end.row > screenRange.start.row
|
||||
top = @linesYardstick.pixelPositionForScreenPosition(screenRange.start, true).top
|
||||
left = 0
|
||||
height = (screenRange.end.row - screenRange.start.row + 1) * lineHeight
|
||||
width = @getScrollWidth()
|
||||
else
|
||||
{top, left} = @linesYardstick.pixelPositionForScreenPosition(screenRange.start, false)
|
||||
height = lineHeight
|
||||
width = @linesYardstick.pixelPositionForScreenPosition(screenRange.end, false).left - left
|
||||
|
||||
{top, left, width, height}
|
||||
|
||||
pixelRectForScreenRange: (screenRange) ->
|
||||
rect = @linesYardstick.pixelRectForScreenRange(screenRange, true)
|
||||
rect = @absolutePixelRectForScreenRange(screenRange)
|
||||
rect.top -= @getScrollTop()
|
||||
rect.left -= @getScrollLeft()
|
||||
|
||||
rect.top = Math.round(rect.top)
|
||||
rect.left = Math.round(rect.left)
|
||||
rect.width = Math.round(rect.width)
|
||||
rect.height = Math.round(rect.height)
|
||||
|
||||
rect
|
||||
|
||||
fetchDecorations: ->
|
||||
@@ -1550,3 +1555,6 @@ class TextEditorPresenter
|
||||
|
||||
getVisibleRowRange: ->
|
||||
[@startRow, @endRow]
|
||||
|
||||
isRowVisible: (row) ->
|
||||
@startRow <= row < @endRow
|
||||
|
||||
@@ -677,7 +677,7 @@ class TextEditor extends Model
|
||||
# this editor.
|
||||
shouldPromptToSave: ({windowCloseRequested}={}) ->
|
||||
if windowCloseRequested
|
||||
@isModified()
|
||||
false
|
||||
else
|
||||
@isModified() and not @buffer.hasMultipleEditors()
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ class ThemePackage extends Package
|
||||
|
||||
load: ->
|
||||
@loadTime = 0
|
||||
@configSchemaRegisteredOnLoad = @registerConfigSchemaFromMetadata()
|
||||
this
|
||||
|
||||
activate: ->
|
||||
|
||||
@@ -22,7 +22,11 @@ class TokenizedBuffer extends Model
|
||||
changeCount: 0
|
||||
|
||||
@deserialize: (state, atomEnvironment) ->
|
||||
state.buffer = atomEnvironment.project.bufferForPathSync(state.bufferPath)
|
||||
if state.bufferId
|
||||
state.buffer = atomEnvironment.project.bufferForIdSync(state.bufferId)
|
||||
else
|
||||
# TODO: remove this fallback after everyone transitions to the latest version.
|
||||
state.buffer = atomEnvironment.project.bufferForPathSync(state.bufferPath)
|
||||
state.config = atomEnvironment.config
|
||||
state.grammarRegistry = atomEnvironment.grammars
|
||||
state.packageManager = atomEnvironment.packages
|
||||
@@ -53,6 +57,7 @@ class TokenizedBuffer extends Model
|
||||
serialize: ->
|
||||
deserializer: 'TokenizedBuffer'
|
||||
bufferPath: @buffer.getPath()
|
||||
bufferId: @buffer.getId()
|
||||
tabLength: @tabLength
|
||||
ignoreInvisibles: @ignoreInvisibles
|
||||
largeFileMode: @largeFileMode
|
||||
|
||||
@@ -3,6 +3,8 @@ Grim = require 'grim'
|
||||
{Disposable} = require 'event-kit'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
AnyConstructor = Symbol('any-constructor')
|
||||
|
||||
# Essential: `ViewRegistry` handles the association between model and view
|
||||
# types in Atom. We call this association a View Provider. As in, for a given
|
||||
# model, this class can provide a view via {::getView}, as long as the
|
||||
@@ -76,16 +78,27 @@ class ViewRegistry
|
||||
# textEditorElement
|
||||
# ```
|
||||
#
|
||||
# * `modelConstructor` Constructor {Function} for your model.
|
||||
# * `modelConstructor` (optional) Constructor {Function} for your model. If
|
||||
# a constructor is given, the `createView` function will only be used
|
||||
# for model objects inheriting from that constructor. Otherwise, it will
|
||||
# will be called for any object.
|
||||
# * `createView` Factory {Function} that is passed an instance of your model
|
||||
# and must return a subclass of `HTMLElement` or `undefined`.
|
||||
# and must return a subclass of `HTMLElement` or `undefined`. If it returns
|
||||
# `undefined`, then the registry will continue to search for other view
|
||||
# providers.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to remove the
|
||||
# added provider.
|
||||
addViewProvider: (modelConstructor, createView) ->
|
||||
if arguments.length is 1
|
||||
Grim.deprecate("atom.views.addViewProvider now takes 2 arguments: a model constructor and a createView function. See docs for details.")
|
||||
provider = modelConstructor
|
||||
switch typeof modelConstructor
|
||||
when 'function'
|
||||
provider = {createView: modelConstructor, modelConstructor: AnyConstructor}
|
||||
when 'object'
|
||||
Grim.deprecate("atom.views.addViewProvider now takes 2 arguments: a model constructor and a createView function. See docs for details.")
|
||||
provider = modelConstructor
|
||||
else
|
||||
throw new TypeError("Arguments to addViewProvider must be functions")
|
||||
else
|
||||
provider = {modelConstructor, createView}
|
||||
|
||||
@@ -93,6 +106,9 @@ class ViewRegistry
|
||||
new Disposable =>
|
||||
@providers = @providers.filter (p) -> p isnt provider
|
||||
|
||||
getViewProviderCount: ->
|
||||
@providers.length
|
||||
|
||||
# Essential: Get the view associated with an object in the workspace.
|
||||
#
|
||||
# If you're just *using* the workspace, you shouldn't need to access the view
|
||||
@@ -153,25 +169,34 @@ class ViewRegistry
|
||||
|
||||
createView: (object) ->
|
||||
if object instanceof HTMLElement
|
||||
object
|
||||
else if object?.element instanceof HTMLElement
|
||||
object.element
|
||||
else if object?.jquery
|
||||
object[0]
|
||||
else if provider = @findProvider(object)
|
||||
element = provider.createView?(object, @atomEnvironment)
|
||||
unless element?
|
||||
element = new provider.viewConstructor
|
||||
element.initialize?(object) ? element.setModel?(object)
|
||||
element
|
||||
else if viewConstructor = object?.getViewClass?()
|
||||
view = new viewConstructor(object)
|
||||
view[0]
|
||||
else
|
||||
throw new Error("Can't create a view for #{object.constructor.name} instance. Please register a view provider.")
|
||||
return object
|
||||
|
||||
findProvider: (object) ->
|
||||
find @providers, ({modelConstructor}) -> object instanceof modelConstructor
|
||||
if object?.element instanceof HTMLElement
|
||||
return object.element
|
||||
|
||||
if object?.jquery
|
||||
return object[0]
|
||||
|
||||
for provider in @providers
|
||||
if provider.modelConstructor is AnyConstructor
|
||||
if element = provider.createView(object, @atomEnvironment)
|
||||
return element
|
||||
continue
|
||||
|
||||
if object instanceof provider.modelConstructor
|
||||
if element = provider.createView?(object, @atomEnvironment)
|
||||
return element
|
||||
|
||||
if viewConstructor = provider.viewConstructor
|
||||
element = new viewConstructor
|
||||
element.initialize?(object) ? element.setModel?(object)
|
||||
return element
|
||||
|
||||
if viewConstructor = object?.getViewClass?()
|
||||
view = new viewConstructor(object)
|
||||
return view[0]
|
||||
|
||||
throw new Error("Can't create a view for #{object.constructor.name} instance. Please register a view provider.")
|
||||
|
||||
updateDocument: (fn) ->
|
||||
@documentWriters.push(fn)
|
||||
|
||||
@@ -161,15 +161,22 @@ class Workspace extends Model
|
||||
itemTitle ?= "untitled"
|
||||
projectPath ?= projectPaths[0]
|
||||
|
||||
titleParts = []
|
||||
if item? and projectPath?
|
||||
document.title = "#{itemTitle} - #{projectPath} - #{appName}"
|
||||
@applicationDelegate.setRepresentedFilename(itemPath ? projectPath)
|
||||
titleParts.push itemTitle, projectPath
|
||||
representedPath = itemPath ? projectPath
|
||||
else if projectPath?
|
||||
document.title = "#{projectPath} - #{appName}"
|
||||
@applicationDelegate.setRepresentedFilename(projectPath)
|
||||
titleParts.push projectPath
|
||||
representedPath = projectPath
|
||||
else
|
||||
document.title = "#{itemTitle} - #{appName}"
|
||||
@applicationDelegate.setRepresentedFilename("")
|
||||
titleParts.push itemTitle
|
||||
representedPath = ""
|
||||
|
||||
unless process.platform is 'darwin'
|
||||
titleParts.push appName
|
||||
|
||||
document.title = titleParts.join(" \u2014 ")
|
||||
@applicationDelegate.setRepresentedFilename(representedPath)
|
||||
|
||||
# On OS X, fades the application window's proxy icon when the current file
|
||||
# has been modified.
|
||||
|
||||
Reference in New Issue
Block a user