Merge branch 'master' into as-tiled-rendering

Conflicts:
	spec/text-editor-presenter-spec.coffee
This commit is contained in:
Antonio Scandurra
2015-05-18 11:49:12 +02:00
22 changed files with 886 additions and 774 deletions

View File

@@ -56,6 +56,7 @@ class AtomApplication
atomProtocolHandler: null
resourcePath: null
version: null
quitting: false
exit: (status) -> app.exit(status)
@@ -104,7 +105,7 @@ class AtomApplication
if process.platform in ['win32', 'linux']
app.quit()
return
@saveState() unless window.isSpec
@saveState() unless window.isSpec or @quitting
# Public: Adds the {AtomWindow} to the global window list.
addWindow: (window) ->
@@ -208,6 +209,9 @@ class AtomApplication
@openPathOnEvent('application:open-your-stylesheet', 'atom://.atom/stylesheet')
@openPathOnEvent('application:open-license', path.join(process.resourcesPath, 'LICENSE.md'))
app.on 'before-quit', =>
@quitting = true
app.on 'will-quit', =>
@killAllProcesses()
@deleteSocketFile()

View File

@@ -29,13 +29,14 @@ class CustomGutterComponent
@domNode.style.removeProperty('display')
@visible = true
# `state` is a subset of the TextEditorPresenter state that is specific
# to this line number gutter.
updateSync: (state) ->
@oldDimensionsAndBackgroundState ?= {}
newDimensionsAndBackgroundState = state.gutters
setDimensionsAndBackground(@oldDimensionsAndBackgroundState, newDimensionsAndBackgroundState, @decorationsNode)
setDimensionsAndBackground(@oldDimensionsAndBackgroundState, state.styles, @decorationsNode)
@oldDecorationPositionState ?= {}
decorationState = state.gutters.customDecorations[@gutter.name]
decorationState = state.content
updatedDecorationIds = new Set
for decorationId, decorationInfo of decorationState

View File

@@ -39,7 +39,6 @@ class DisplayBuffer extends Model
@decorationsByMarkerId = {}
@disposables.add @tokenizedBuffer.observeGrammar @subscribeToScopedConfigSettings
@disposables.add @tokenizedBuffer.onDidChange @handleTokenizedBufferChange
@disposables.add @buffer.onDidUpdateMarkers @handleBufferMarkersUpdated
@disposables.add @buffer.onDidCreateMarker @handleBufferMarkerCreated
@updateAllScreenLines()
@foldMarkerAttributes = Object.freeze({class: 'fold', displayBufferId: @id})
@@ -153,12 +152,12 @@ class DisplayBuffer extends Model
@emitter.on 'did-update-markers', callback
emitDidChange: (eventProperties, refreshMarkers=true) ->
if refreshMarkers
@pauseMarkerChangeEvents()
@refreshMarkerScreenPositions()
@emit 'changed', eventProperties if Grim.includeDeprecatedAPIs
@emitter.emit 'did-change', eventProperties
@resumeMarkerChangeEvents()
if refreshMarkers
@refreshMarkerScreenPositions()
@emit 'markers-updated' if Grim.includeDeprecatedAPIs
@emitter.emit 'did-update-markers'
updateWrappedScreenLines: ->
start = 0
@@ -1081,15 +1080,6 @@ class DisplayBuffer extends Model
else
@foldMarkerAttributes
pauseMarkerChangeEvents: ->
marker.pauseChangeEvents() for marker in @getMarkers()
return
resumeMarkerChangeEvents: ->
marker.resumeChangeEvents() for marker in @getMarkers()
@emit 'markers-updated' if Grim.includeDeprecatedAPIs
@emitter.emit 'did-update-markers'
refreshMarkerScreenPositions: ->
for marker in @getMarkers()
marker.notifyObservers(textChanged: false)
@@ -1112,7 +1102,7 @@ class DisplayBuffer extends Model
handleTokenizedBufferChange: (tokenizedBufferChange) =>
{start, end, delta, bufferChange} = tokenizedBufferChange
@updateScreenLines(start, end + 1, delta, delayChangeEvent: bufferChange?)
@updateScreenLines(start, end + 1, delta, refreshMarkers: false)
@setScrollTop(Math.min(@getScrollTop(), @getMaxScrollTop())) if delta < 0
updateScreenLines: (startBufferRow, endBufferRow, bufferDelta=0, options={}) ->
@@ -1135,11 +1125,7 @@ class DisplayBuffer extends Model
screenDelta: screenDelta
bufferDelta: bufferDelta
if options.delayChangeEvent
@pauseMarkerChangeEvents()
@pendingChangeEvent = changeEvent
else
@emitDidChange(changeEvent, options.refreshMarkers)
@emitDidChange(changeEvent, options.refreshMarkers)
buildScreenLines: (startBufferRow, endBufferRow) ->
screenLines = []
@@ -1219,11 +1205,6 @@ class DisplayBuffer extends Model
@scrollWidth += 1 unless @isSoftWrapped()
@setScrollLeft(Math.min(@getScrollLeft(), @getMaxScrollLeft()))
handleBufferMarkersUpdated: =>
if event = @pendingChangeEvent
@pendingChangeEvent = null
@emitDidChange(event, false)
handleBufferMarkerCreated: (textBufferMarker) =>
@createFoldForMarker(textBufferMarker) if textBufferMarker.matchesParams(@getFoldMarkerAttributes())
if marker = @getMarker(textBufferMarker.id)

View File

@@ -1,3 +1,4 @@
_ = require 'underscore-plus'
CustomGutterComponent = require './custom-gutter-component'
LineNumberGutterComponent = require './line-number-gutter-component'
@@ -26,11 +27,11 @@ class GutterContainerComponent
updateSync: (state) ->
# The GutterContainerComponent expects the gutters to be sorted in the order
# they should appear.
newState = state.gutters.sortedDescriptions
newState = state.gutters
newGutterComponents = []
newGutterComponentsByGutterName = {}
for {gutter, visible} in newState
for {gutter, visible, styles, content} in newState
gutterComponent = @gutterComponentsByGutterName[gutter.name]
if not gutterComponent
if gutter.name is 'line-number'
@@ -38,8 +39,20 @@ class GutterContainerComponent
@lineNumberGutterComponent = gutterComponent
else
gutterComponent = new CustomGutterComponent({gutter})
if visible then gutterComponent.showNode() else gutterComponent.hideNode()
gutterComponent.updateSync(state)
# Pass the gutter only the state that it needs.
if gutter.name is 'line-number'
# For ease of use in the line number gutter component, set the shared
# 'styles' as a field under the 'content'.
gutterSubstate = _.clone(content)
gutterSubstate.styles = styles
else
# Custom gutter 'content' is keyed on gutter name, so we cannot set
# 'styles' as a subfield directly under it.
gutterSubstate = {content, styles}
gutterComponent.updateSync(gutterSubstate)
newGutterComponents.push({
name: gutter.name,
component: gutterComponent,

View File

@@ -31,19 +31,25 @@ class LineNumberGutterComponent
@domNode.style.removeProperty('display')
@visible = true
# `state` is a subset of the TextEditorPresenter state that is specific
# to this line number gutter.
updateSync: (state) ->
@newState = state.gutters.lineNumberGutter
@oldState ?= {lineNumbers: {}}
@newState = state
@oldState ?=
lineNumbers: {}
styles: {}
@appendDummyLineNumber() unless @dummyLineNumberNode?
newDimensionsAndBackgroundState = state.gutters
setDimensionsAndBackground(@oldState, newDimensionsAndBackgroundState, @lineNumbersNode)
setDimensionsAndBackground(@oldState.styles, @newState.styles, @lineNumbersNode)
if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits
@updateDummyLineNumber()
node.remove() for id, node of @lineNumberNodesById
@oldState = {maxLineNumberDigits: @newState.maxLineNumberDigits, lineNumbers: {}}
@oldState =
maxLineNumberDigits: @newState.maxLineNumberDigits
lineNumbers: {}
styles: {}
@lineNumberNodesById = {}
@updateLineNumbers()

View File

@@ -48,7 +48,6 @@ class Marker
oldTailBufferPosition: null
oldTailScreenPosition: null
wasValid: true
deferredChangeEvents: null
###
Section: Construction and Destruction
@@ -332,11 +331,11 @@ class Marker
newTailScreenPosition = @getTailScreenPosition()
isValid = @isValid()
return if _.isEqual(isValid, @wasValid) and
_.isEqual(newHeadBufferPosition, @oldHeadBufferPosition) and
_.isEqual(newHeadScreenPosition, @oldHeadScreenPosition) and
_.isEqual(newTailBufferPosition, @oldTailBufferPosition) and
_.isEqual(newTailScreenPosition, @oldTailScreenPosition)
return if isValid is @wasValid and
newHeadBufferPosition.isEqual(@oldHeadBufferPosition) and
newHeadScreenPosition.isEqual(@oldHeadScreenPosition) and
newTailBufferPosition.isEqual(@oldTailBufferPosition) and
newTailScreenPosition.isEqual(@oldTailScreenPosition)
changeEvent = {
@oldHeadScreenPosition, newHeadScreenPosition,
@@ -347,11 +346,8 @@ class Marker
isValid
}
if @deferredChangeEvents?
@deferredChangeEvents.push(changeEvent)
else
@emit 'changed', changeEvent if Grim.includeDeprecatedAPIs
@emitter.emit 'did-change', changeEvent
@emit 'changed', changeEvent if Grim.includeDeprecatedAPIs
@emitter.emit 'did-change', changeEvent
@oldHeadBufferPosition = newHeadBufferPosition
@oldHeadScreenPosition = newHeadScreenPosition
@@ -359,18 +355,6 @@ class Marker
@oldTailScreenPosition = newTailScreenPosition
@wasValid = isValid
pauseChangeEvents: ->
@deferredChangeEvents = []
resumeChangeEvents: ->
if deferredChangeEvents = @deferredChangeEvents
@deferredChangeEvents = null
for event in deferredChangeEvents
@emit 'changed', event if Grim.includeDeprecatedAPIs
@emitter.emit 'did-change', event
return
getPixelRange: ->
@displayBuffer.pixelRangeForScreenRange(@getScreenRange(), false)

View File

@@ -427,7 +427,6 @@ class PackageManager
path.join(packageDir, 'autocomplete-atom-api')
path.join(packageDir, 'autocomplete-css')
path.join(packageDir, 'autocomplete-html')
path.join(packageDir, 'autocomplete-emojis')
path.join(packageDir, 'autocomplete-snippets')
]
for dirToRemove in dirsToRemove

View File

@@ -1,5 +1,5 @@
path = require 'path'
hostedGitInfo = require 'hosted-git-info'
normalizePackageData = null
_ = require 'underscore-plus'
async = require 'async'
@@ -26,16 +26,11 @@ class Package
packagePath?.startsWith(@resourcePathWithTrailingSlash)
@normalizeMetadata: (metadata) ->
if typeof metadata.repository is 'string'
metadata.repository =
type: 'git'
url: metadata.repository
repoUrl = metadata.repository?.url
if repoUrl
info = hostedGitInfo.fromUrl(repoUrl)
if info?.getDefaultRepresentation() is 'shortcut'
metadata.repository.url = info.https().replace(/^git\+/, '')
unless metadata?._id
normalizePackageData ?= require 'normalize-package-data'
normalizePackageData(metadata)
if metadata.repository?.type is 'git' and typeof metadata.repository.url is 'string'
metadata.repository.url = metadata.repository.url.replace(/^git\+/, '')
@loadMetadata: (packagePath, ignoreErrors=false) ->
packageName = path.basename(packagePath)
@@ -45,12 +40,12 @@ class Package
if metadataPath = CSON.resolve(path.join(packagePath, 'package'))
try
metadata = CSON.readFileSync(metadataPath)
@normalizeMetadata(metadata)
catch error
throw error unless ignoreErrors
metadata ?= {}
metadata.name = packageName
@normalizeMetadata(metadata)
if includeDeprecatedAPIs and metadata.stylesheetMain?
deprecate("Use the `mainStyleSheet` key instead of `stylesheetMain` in the `package.json` of `#{packageName}`", {packageName})

View File

@@ -70,7 +70,7 @@ class TextEditorComponent
@scrollViewNode.classList.add('scroll-view')
@domNode.appendChild(@scrollViewNode)
@mountGutterContainerComponent() if @presenter.getState().gutters.sortedDescriptions.length
@mountGutterContainerComponent() if @presenter.getState().gutters.length
@hiddenInputComponent = new InputComponent
@scrollViewNode.appendChild(@hiddenInputComponent.getDomNode())
@@ -137,7 +137,7 @@ class TextEditorComponent
else
@domNode.style.height = ''
if @newState.gutters.sortedDescriptions.length
if @newState.gutters.length
@mountGutterContainerComponent() unless @gutterContainerComponent?
@gutterContainerComponent.updateSync(@newState)
else

View File

@@ -210,11 +210,13 @@ class TextEditorPresenter
tiles: {}
highlights: {}
overlays: {}
gutters:
sortedDescriptions: []
customDecorations: {}
lineNumberGutter:
lineNumbers: {}
gutters: []
# Shared state that is copied into ``@state.gutters`.
@sharedGutterStyles = {}
@customGutterDecorations = {}
@lineNumberGutter =
lineNumbers: {}
@updateState()
updateState: ->
@@ -254,11 +256,11 @@ class TextEditorPresenter
updateVerticalScrollState: ->
@state.content.scrollHeight = @scrollHeight
@state.gutters.scrollHeight = @scrollHeight
@sharedGutterStyles.scrollHeight = @scrollHeight
@state.verticalScrollbar.scrollHeight = @scrollHeight
@state.content.scrollTop = @scrollTop
@state.gutters.scrollTop = @scrollTop
@sharedGutterStyles.scrollTop = @scrollTop
@state.verticalScrollbar.scrollTop = @scrollTop
updateHorizontalScrollState: ->
@@ -438,10 +440,10 @@ class TextEditorPresenter
return
updateLineNumberGutterState: ->
@state.gutters.lineNumberGutter.maxLineNumberDigits = @model.getLineCount().toString().length
@lineNumberGutter.maxLineNumberDigits = @model.getLineCount().toString().length
updateCommonGutterState: ->
@state.gutters.backgroundColor = if @gutterBackgroundColor isnt "rgba(0, 0, 0, 0)"
@sharedGutterStyles.backgroundColor = if @gutterBackgroundColor isnt "rgba(0, 0, 0, 0)"
@gutterBackgroundColor
else
@backgroundColor
@@ -469,15 +471,25 @@ class TextEditorPresenter
@emitDidUpdateState()
updateGutterOrderState: ->
@state.gutters.sortedDescriptions = []
@state.gutters = []
if @model.isMini()
return
for gutter in @model.getGutters()
isVisible = @gutterIsVisible(gutter)
@state.gutters.sortedDescriptions.push({gutter, visible: isVisible})
if gutter.name is 'line-number'
content = @lineNumberGutter
else
@customGutterDecorations[gutter.name] ?= {}
content = @customGutterDecorations[gutter.name]
@state.gutters.push({
gutter,
visible: isVisible,
styles: @sharedGutterStyles,
content,
})
# Updates the decoration state for the gutter with the given gutterName.
# @state.gutters.customDecorations is an {Object}, with the form:
# @customGutterDecorations is an {Object}, with the form:
# * gutterName : {
# decoration.id : {
# top: # of pixels from top
@@ -489,23 +501,43 @@ class TextEditorPresenter
updateCustomGutterDecorationState: ->
return unless @startRow? and @endRow? and @lineHeight?
@state.gutters.customDecorations = {}
return if @model.isMini()
if @model.isMini()
# Mini editors have no gutter decorations.
# We clear instead of reassigning to preserve the reference.
@clearAllCustomGutterDecorations()
for gutter in @model.getGutters()
gutterName = gutter.name
@state.gutters.customDecorations[gutterName] = {}
gutterDecorations = @customGutterDecorations[gutterName]
if gutterDecorations
# Clear the gutter decorations; they are rebuilt.
# We clear instead of reassigning to preserve the reference.
@clearDecorationsForCustomGutterName(gutterName)
else
@customGutterDecorations[gutterName] = {}
return if not @gutterIsVisible(gutter)
relevantDecorations = @customGutterDecorationsInRange(gutterName, @startRow, @endRow - 1)
relevantDecorations.forEach (decoration) =>
decorationRange = decoration.getMarker().getScreenRange()
@state.gutters.customDecorations[gutterName][decoration.id] =
@customGutterDecorations[gutterName][decoration.id] =
top: @lineHeight * decorationRange.start.row
height: @lineHeight * decorationRange.getRowCount()
item: decoration.getProperties().item
class: decoration.getProperties().class
clearAllCustomGutterDecorations: ->
allGutterNames = Object.keys(@customGutterDecorations)
for gutterName in allGutterNames
@clearDecorationsForCustomGutterName(gutterName)
clearDecorationsForCustomGutterName: (gutterName) ->
gutterDecorations = @customGutterDecorations[gutterName]
if gutterDecorations
allDecorationIds = Object.keys(gutterDecorations)
for decorationId in allDecorationIds
delete gutterDecorations[decorationId]
gutterIsVisible: (gutterModel) ->
isVisible = gutterModel.isVisible()
if gutterModel.name is 'line-number'
@@ -542,7 +574,7 @@ class TextEditorPresenter
decorationClasses = @lineNumberDecorationClassesForRow(screenRow)
foldable = @model.isFoldableAtScreenRow(screenRow)
@state.gutters.lineNumberGutter.lineNumbers[id] = {screenRow, bufferRow, softWrapped, top, decorationClasses, foldable}
@lineNumberGutter.lineNumbers[id] = {screenRow, bufferRow, softWrapped, top, decorationClasses, foldable}
visibleLineNumberIds[id] = true
# FIXME: We should either rely on @mouseWheelScreenRow or convert this to use tiles.
@@ -553,8 +585,8 @@ class TextEditorPresenter
id += '-' + wrapCount if wrapCount > 0
visibleLineNumberIds[id] = true
for id of @state.gutters.lineNumberGutter.lineNumbers
delete @state.gutters.lineNumberGutter.lineNumbers[id] unless visibleLineNumberIds[id]
for id of @lineNumberGutter.lineNumbers
delete @lineNumberGutter.lineNumbers[id] unless visibleLineNumberIds[id]
return

View File

@@ -113,7 +113,10 @@ class WorkspaceElement extends HTMLElement
focusPaneViewOnRight: -> @paneContainer.focusPaneViewOnRight()
runPackageSpecs: ->
[projectPath] = atom.project.getPaths()
if activePath = atom.workspace.getActivePaneItem()?.getPath?()
[projectPath] = atom.project.relativizePath(activePath)
else
[projectPath] = atom.project.getPaths()
ipc.send('run-package-specs', path.join(projectPath, 'spec')) if projectPath
atom.commands.add 'atom-workspace',