Merge branch 'master' into jlks-missing-services-fix

This commit is contained in:
Jessica Lord
2015-03-19 10:19:56 -07:00
26 changed files with 323 additions and 88 deletions

View File

@@ -48,7 +48,7 @@ For more information on how to work with Atom's official packages, see
[JavaScript](https://github.com/styleguide/javascript),
and [CSS](https://github.com/styleguide/css) styleguides.
* Include thoughtfully-worded, well-structured
[Jasmine](http://jasmine.github.io/) specs.
[Jasmine](http://jasmine.github.io/) specs in the `./spec` folder. Run them using `apm test`.
* Document new code based on the
[Documentation Styleguide](#documentation-styleguide)
* End files with a newline.

View File

@@ -6,6 +6,6 @@
"url": "https://github.com/atom/atom.git"
},
"dependencies": {
"atom-package-manager": "0.143.0"
"atom-package-manager": "0.145.0"
}
}

View File

@@ -8,7 +8,7 @@ _ = require 'underscore-plus'
temp = require 'temp'
directoryToOpen = temp.mkdirSync('browser-process-startup-')
socketPath = path.join(os.tmpdir(), 'atom.sock')
socketPath = path.join(os.tmpdir(), "atom-#{process.env.USER}.sock")
numberOfRuns = 10
deleteSocketFile = ->

View File

@@ -65,7 +65,7 @@ module.exports =
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""
'jschardet@1.1.0':
'jschardet@1.1.1':
license: 'LGPL'
source: 'README.md in the repository'
sourceText: """

View File

@@ -2,6 +2,6 @@
'fontSize': 16
'core':
'themes': [
'atom-dark-ui'
'atom-dark-syntax'
'one-dark-ui'
'one-dark-syntax'
]

View File

@@ -217,6 +217,7 @@
{label: 'Split Down', command: 'pane:split-down'}
{label: 'Split Left', command: 'pane:split-left'}
{label: 'Split Right', command: 'pane:split-right'}
{label: 'Close Pane', command: 'pane:close'}
{type: 'separator'}
]
'atom-pane': [
@@ -225,5 +226,6 @@
{label: 'Split Down', command: 'pane:split-down'}
{label: 'Split Left', command: 'pane:split-left'}
{label: 'Split Right', command: 'pane:split-right'}
{label: 'Close Pane', command: 'pane:close'}
{type: 'separator'}
]

View File

@@ -174,6 +174,7 @@
{label: 'Split Down', command: 'pane:split-down'}
{label: 'Split Left', command: 'pane:split-left'}
{label: 'Split Right', command: 'pane:split-right'}
{label: 'Close Pane', command: 'pane:close'}
{type: 'separator'}
]
'atom-pane': [
@@ -182,5 +183,6 @@
{label: 'Split Down', command: 'pane:split-down'}
{label: 'Split Left', command: 'pane:split-left'}
{label: 'Split Right', command: 'pane:split-right'}
{label: 'Close Pane', command: 'pane:close'}
{type: 'separator'}
]

View File

@@ -196,6 +196,7 @@
{label: 'Split Down', command: 'pane:split-down'}
{label: 'Split Left', command: 'pane:split-left'}
{label: 'Split Right', command: 'pane:split-right'}
{label: 'Close Pane', command: 'pane:close'}
{type: 'separator'}
]
'atom-pane': [
@@ -204,5 +205,6 @@
{label: 'Split Down', command: 'pane:split-down'}
{label: 'Split Left', command: 'pane:split-left'}
{label: 'Split Right', command: 'pane:split-right'}
{label: 'Close Pane', command: 'pane:close'}
{type: 'separator'}
]

View File

@@ -20,7 +20,7 @@
"atomShellVersion": "0.21.3",
"dependencies": {
"async": "0.2.6",
"atom-keymap": "^3.1.5",
"atom-keymap": "^4",
"atom-space-pen-views": "^2.0.4",
"babel-core": "^4.0.2",
"bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
@@ -47,7 +47,7 @@
"nslog": "^2.0.0",
"oniguruma": "^4.0.0",
"optimist": "0.4.0",
"pathwatcher": "^3.3.3",
"pathwatcher": "^4.3.1",
"property-accessors": "^1.1.3",
"q": "^1.1.2",
"random-words": "0.0.1",
@@ -64,13 +64,13 @@
"space-pen": "3.8.2",
"stacktrace-parser": "0.1.1",
"temp": "0.8.1",
"text-buffer": "^4.1.5",
"text-buffer": "^5",
"theorist": "^1.0.2",
"underscore-plus": "^1.6.6"
},
"packageDependencies": {
"atom-dark-syntax": "0.26.0",
"atom-dark-ui": "0.47.0",
"atom-dark-ui": "0.49.0",
"atom-light-syntax": "0.26.0",
"atom-light-ui": "0.41.0",
"base16-tomorrow-dark-theme": "0.25.0",
@@ -81,16 +81,16 @@
"one-light-ui": "0.4.0",
"solarized-dark-syntax": "0.32.0",
"solarized-light-syntax": "0.19.0",
"archive-view": "0.52.0",
"archive-view": "0.53.0",
"autocomplete": "0.44.0",
"autoflow": "0.22.0",
"autosave": "0.20.0",
"background-tips": "0.23.0",
"bookmarks": "0.35.0",
"bracket-matcher": "0.71.0",
"bracket-matcher": "0.73.0",
"command-palette": "0.34.0",
"deprecation-cop": "0.37.0",
"dev-live-reload": "0.44.0",
"dev-live-reload": "0.45.0",
"encoding-selector": "0.19.0",
"exception-reporting": "0.24.0",
"feedback": "0.34.0",
@@ -99,25 +99,25 @@
"git-diff": "0.54.0",
"go-to-line": "0.30.0",
"grammar-selector": "0.46.0",
"image-view": "0.51.0",
"image-view": "0.52.0",
"incompatible-packages": "0.24.0",
"keybinding-resolver": "0.29.0",
"link": "0.30.0",
"markdown-preview": "0.141.0",
"markdown-preview": "0.143.0",
"metrics": "0.45.0",
"notifications": "0.32.0",
"notifications": "0.33.0",
"open-on-github": "0.34.0",
"package-generator": "0.38.0",
"release-notes": "0.52.0",
"settings-view": "0.184.0",
"snippets": "0.82.0",
"settings-view": "0.185.0",
"snippets": "0.84.0",
"spell-check": "0.55.0",
"status-bar": "0.63.0",
"styleguide": "0.44.0",
"symbols-view": "0.90.0",
"symbols-view": "0.91.0",
"tabs": "0.67.0",
"timecop": "0.31.0",
"tree-view": "0.167.0",
"tree-view": "0.168.0",
"update-package-dependencies": "0.9.0",
"welcome": "0.25.0",
"whitespace": "0.29.0",
@@ -129,7 +129,7 @@
"language-css": "0.28.0",
"language-gfm": "0.67.0",
"language-git": "0.10.0",
"language-go": "0.21.0",
"language-go": "0.22.0",
"language-html": "0.29.0",
"language-hyperlink": "0.12.2",
"language-java": "0.14.0",
@@ -139,12 +139,12 @@
"language-make": "0.14.0",
"language-mustache": "0.11.0",
"language-objective-c": "0.15.0",
"language-perl": "0.16.0",
"language-perl": "0.19.0",
"language-php": "0.21.0",
"language-property-list": "0.8.0",
"language-python": "0.32.0",
"language-ruby": "0.49.0",
"language-ruby-on-rails": "0.20.0",
"language-ruby-on-rails": "0.21.0",
"language-sass": "0.36.0",
"language-shellscript": "0.13.0",
"language-source": "0.9.0",

View File

@@ -115,6 +115,24 @@ describe "DisplayBuffer", ->
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe ' var pivot = items.shift(), current, left = [], '
expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe ' right = [];'
describe "when the only whitespace characters are at the beginning of the line", ->
beforeEach ->
displayBuffer.setEditorWidthInChars(10)
it "wraps the line at the max length when indented with tabs", ->
buffer.setTextInRange([[0, 0], [1, 0]], '\t\tabcdefghijklmnopqrstuvwxyz')
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe ' abcdef'
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe ' ghijkl'
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe ' mnopqr'
it "wraps the line at the max length when indented with spaces", ->
buffer.setTextInRange([[0, 0], [1, 0]], ' abcdefghijklmnopqrstuvwxyz')
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe ' abcdef'
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe ' ghijkl'
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe ' mnopqr'
describe "when there are hard tabs", ->
beforeEach ->
buffer.setText(buffer.getText().replace(new RegExp(' ', 'g'), '\t'))
@@ -124,9 +142,42 @@ describe "DisplayBuffer", ->
expect(displayBuffer.tokenizedLineForScreenRow(3).tokens[1].isHardTab).toBeTruthy()
describe "when a line is wrapped", ->
it "correctly tokenizes soft wrap indentation tokens", ->
expect(displayBuffer.tokenizedLineForScreenRow(4).tokens[0].isSoftWrapIndentation).toBeTruthy()
expect(displayBuffer.tokenizedLineForScreenRow(4).tokens[1].isSoftWrapIndentation).toBeTruthy()
it "breaks soft-wrap indentation into a token for each indentation level to support indent guides", ->
tokenizedLine = displayBuffer.tokenizedLineForScreenRow(4)
expect(tokenizedLine.tokens[0].value).toBe(" ")
expect(tokenizedLine.tokens[0].isSoftWrapIndentation).toBeTruthy()
expect(tokenizedLine.tokens[1].value).toBe(" ")
expect(tokenizedLine.tokens[1].isSoftWrapIndentation).toBeTruthy()
expect(tokenizedLine.tokens[2].isSoftWrapIndentation).toBeFalsy()
describe "when editor.softWrapHangingIndent is set", ->
beforeEach ->
atom.config.set('editor.softWrapHangingIndent', 3)
it "further indents wrapped lines", ->
expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe " return "
expect(displayBuffer.tokenizedLineForScreenRow(11).text).toBe " sort(left).concat(pivot).concat(sort(right)"
expect(displayBuffer.tokenizedLineForScreenRow(12).text).toBe " );"
it "includes hanging indent when breaking soft-wrap indentation into tokens", ->
tokenizedLine = displayBuffer.tokenizedLineForScreenRow(4)
expect(tokenizedLine.tokens[0].value).toBe(" ")
expect(tokenizedLine.tokens[0].isSoftWrapIndentation).toBeTruthy()
expect(tokenizedLine.tokens[1].value).toBe(" ")
expect(tokenizedLine.tokens[1].isSoftWrapIndentation).toBeTruthy()
expect(tokenizedLine.tokens[2].value).toBe(" ") # hanging indent
expect(tokenizedLine.tokens[2].isSoftWrapIndentation).toBeTruthy()
expect(tokenizedLine.tokens[3].value).toBe(" ") # odd space
expect(tokenizedLine.tokens[3].isSoftWrapIndentation).toBeTruthy()
expect(tokenizedLine.tokens[4].isSoftWrapIndentation).toBeFalsy()
describe "when the buffer changes", ->
describe "when buffer lines are updated", ->

View File

@@ -9,7 +9,7 @@ webdriverio = require "../../../build/node_modules/webdriverio"
AtomPath = remote.process.argv[0]
AtomLauncherPath = path.join(__dirname, "..", "helpers", "atom-launcher.sh")
ChromedriverPath = path.resolve(__dirname, '..', '..', '..', 'atom-shell', 'chromedriver', 'chromedriver')
SocketPath = path.join(temp.mkdirSync("socket-dir"), "atom.sock")
SocketPath = path.join(temp.mkdirSync("socket-dir"), "atom-#{process.env.USER}.sock")
ChromedriverPort = 9515
buildAtomClient = (args, env) ->

View File

@@ -1937,14 +1937,14 @@ describe "TextEditorComponent", ->
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(11), metaKey: true))
nextAnimationFrame()
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(11), metaKey: true))
expect(editor.getSelectedScreenRanges()).toEqual [[[7, 4], [7, 6]], [[10, 0], [20, 0]]]
expect(editor.getSelectedScreenRanges()).toEqual [[[7, 4], [7, 6]], [[10, 0], [19, 0]]]
it "merges overlapping selections", ->
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(17), metaKey: true))
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(9), metaKey: true))
nextAnimationFrame()
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(9), metaKey: true))
expect(editor.getSelectedScreenRanges()).toEqual [[[5, 0], [20, 0]]]
expect(editor.getSelectedScreenRanges()).toEqual [[[5, 0], [19, 0]]]
describe "when the gutter is shift-clicked and dragged", ->
describe "when the shift-click is below the existing selection's tail", ->

View File

@@ -1626,7 +1626,54 @@ describe "TextEditor", ->
[[6, 22], [6, 28]]
]
it "can add selections to soft-wrapped line segments", ->
editor.setSoftWrapped(true)
editor.setEditorWidthInChars(40)
editor.setSelectedScreenRange([[3, 10], [3, 15]])
editor.addSelectionBelow()
expect(editor.getSelectedScreenRanges()).toEqual [
[[3, 10], [3, 15]]
[[4, 10], [4, 15]]
]
it "takes atomic tokens into account", ->
waitsForPromise ->
atom.project.open('sample-with-tabs-and-leading-comment.coffee', autoIndent: false).then (o) -> editor = o
runs ->
editor.setSelectedBufferRange([[2, 1], [2, 3]])
editor.addSelectionBelow()
expect(editor.getSelectedBufferRanges()).toEqual [
[[2, 1], [2, 3]]
[[3, 1], [3, 2]]
]
describe "when the selection is empty", ->
describe "when lines are soft-wrapped", ->
beforeEach ->
editor.setSoftWrapped(true)
editor.setEditorWidthInChars(40)
it "skips soft-wrap indentation tokens", ->
editor.setCursorScreenPosition([3, 0])
editor.addSelectionBelow()
expect(editor.getSelectedScreenRanges()).toEqual [
[[3, 0], [3, 0]]
[[4, 4], [4, 4]]
]
it "does not skip them if they're shorter than the current column", ->
editor.setCursorScreenPosition([3, 37])
editor.addSelectionBelow()
expect(editor.getSelectedScreenRanges()).toEqual [
[[3, 37], [3, 37]]
[[4, 26], [4, 26]]
]
it "does not skip lines that are shorter than the current column", ->
editor.setCursorBufferPosition([3, 36])
editor.addSelectionBelow()
@@ -1690,7 +1737,54 @@ describe "TextEditor", ->
[[3, 22], [3, 38]]
]
it "can add selections to soft-wrapped line segments", ->
editor.setSoftWrapped(true)
editor.setEditorWidthInChars(40)
editor.setSelectedScreenRange([[4, 10], [4, 15]])
editor.addSelectionAbove()
expect(editor.getSelectedScreenRanges()).toEqual [
[[4, 10], [4, 15]]
[[3, 10], [3, 15]]
]
it "takes atomic tokens into account", ->
waitsForPromise ->
atom.project.open('sample-with-tabs-and-leading-comment.coffee', autoIndent: false).then (o) -> editor = o
runs ->
editor.setSelectedBufferRange([[3, 1], [3, 2]])
editor.addSelectionAbove()
expect(editor.getSelectedBufferRanges()).toEqual [
[[3, 1], [3, 2]]
[[2, 1], [2, 3]]
]
describe "when the selection is empty", ->
describe "when lines are soft-wrapped", ->
beforeEach ->
editor.setSoftWrapped(true)
editor.setEditorWidthInChars(40)
it "skips soft-wrap indentation tokens", ->
editor.setCursorScreenPosition([5, 0])
editor.addSelectionAbove()
expect(editor.getSelectedScreenRanges()).toEqual [
[[5, 0], [5, 0]]
[[4, 4], [4, 4]]
]
it "does not skip them if they're shorter than the current column", ->
editor.setCursorScreenPosition([5, 29])
editor.addSelectionAbove()
expect(editor.getSelectedScreenRanges()).toEqual [
[[5, 29], [5, 29]]
[[4, 26], [4, 26]]
]
it "does not skip lines that are shorter than the current column", ->
editor.setCursorBufferPosition([6, 36])
editor.addSelectionAbove()

View File

@@ -129,6 +129,7 @@ describe "ThemeManager", ->
expect(importPaths[0]).toContain 'atom-dark-ui'
it 'adds theme-* classes to the workspace for each active theme', ->
atom.config.set('core.themes', ['atom-dark-ui', 'atom-dark-syntax'])
workspaceElement = atom.views.getView(atom.workspace)
themeManager.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy()

View File

@@ -878,3 +878,25 @@ describe "TokenizedBuffer", ->
expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe true
expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe false
expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false
describe "when the buffer is configured with the null grammar", ->
it "uses the placeholder tokens and does not actually tokenize using the grammar", ->
spyOn(atom.grammars.nullGrammar, 'tokenizeLine').andCallThrough()
buffer = atom.project.bufferForPathSync('sample.will-use-the-null-grammar')
buffer.setText('a\nb\nc')
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizeCallback = jasmine.createSpy('onDidTokenize')
tokenizedBuffer.onDidTokenize(tokenizeCallback)
fullyTokenize(tokenizedBuffer)
expect(tokenizeCallback.callCount).toBe 1
expect(atom.grammars.nullGrammar.tokenizeLine.callCount).toBe 0
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens.length).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0].value).toBe 'a'
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens.length).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].value).toBe 'b'
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens.length).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].value).toBe 'c'

View File

@@ -18,7 +18,7 @@ DefaultSocketPath =
if process.platform is 'win32'
'\\\\.\\pipe\\atom-sock'
else
path.join(os.tmpdir(), 'atom.sock')
path.join(os.tmpdir(), "atom-#{process.env.USER}.sock")
# The application's singleton class.
#

View File

@@ -28,7 +28,7 @@ module.exports =
type: 'string'
themes:
type: 'array'
default: ['atom-dark-ui', 'atom-dark-syntax']
default: ['one-dark-ui', 'one-dark-syntax']
items:
type: 'string'
projectHome:
@@ -149,6 +149,10 @@ module.exports =
softWrapAtPreferredLineLength:
type: 'boolean'
default: false
softWrapHangingIndent:
type: 'integer'
default: 0
minimum: 0
scrollSensitivity:
type: 'integer'
default: 40

View File

@@ -69,12 +69,17 @@ class DisplayBuffer extends Model
scrollPastEnd: atom.config.get('editor.scrollPastEnd', scope: scopeDescriptor)
softWrap: atom.config.get('editor.softWrap', scope: scopeDescriptor)
softWrapAtPreferredLineLength: atom.config.get('editor.softWrapAtPreferredLineLength', scope: scopeDescriptor)
softWrapHangingIndent: atom.config.get('editor.softWrapHangingIndent', scope: scopeDescriptor)
preferredLineLength: atom.config.get('editor.preferredLineLength', scope: scopeDescriptor)
subscriptions.add atom.config.onDidChange 'editor.softWrap', scope: scopeDescriptor, ({newValue}) =>
@configSettings.softWrap = newValue
@updateWrappedScreenLines()
subscriptions.add atom.config.onDidChange 'editor.softWrapHangingIndent', scope: scopeDescriptor, ({newValue}) =>
@configSettings.softWrapHangingIndent = newValue
@updateWrappedScreenLines()
subscriptions.add atom.config.onDidChange 'editor.softWrapAtPreferredLineLength', scope: scopeDescriptor, ({newValue}) =>
@configSettings.softWrapAtPreferredLineLength = newValue
@updateWrappedScreenLines() if @isSoftWrapped()
@@ -859,6 +864,18 @@ class DisplayBuffer extends Model
column = screenLine.clipScreenColumn(column, options)
new Point(row, column)
# Clip the start and end of the given range to valid positions on screen.
# See {::clipScreenPosition} for more information.
#
# * `range` The {Range} to clip.
# * `options` (optional) See {::clipScreenPosition} `options`.
# Returns a {Range}.
clipScreenRange: (range, options) ->
start = @clipScreenPosition(range.start, options)
end = @clipScreenPosition(range.end, options)
new Range(start, end)
# Calculates a {Range} representing the start of the {TextBuffer} until the end.
#
# Returns a {Range}.
@@ -1145,7 +1162,10 @@ class DisplayBuffer extends Model
softWraps = 0
if @isSoftWrapped()
while wrapScreenColumn = tokenizedLine.findWrapColumn(@getSoftWrapColumn())
[wrappedLine, tokenizedLine] = tokenizedLine.softWrapAt(wrapScreenColumn)
[wrappedLine, tokenizedLine] = tokenizedLine.softWrapAt(
wrapScreenColumn,
@configSettings.softWrapHangingIndent
)
break if wrappedLine.hasOnlySoftWrapIndentation()
screenLines.push(wrappedLine)
softWraps++

View File

@@ -96,7 +96,8 @@ class GitRepository
@subscriptions.add new Disposable(-> window.removeEventListener 'focus', onWindowFocus)
if @project?
@subscriptions.add @project.eachBuffer (buffer) => @subscribeToBuffer(buffer)
@project.getBuffers().forEach (buffer) => @subscribeToBuffer(buffer)
@subscriptions.add @project.onDidAddBuffer (buffer) => @subscribeToBuffer(buffer)
# Public: Destroy this {GitRepository} object.
#

View File

@@ -124,6 +124,8 @@ class Marker
Grim.deprecate("Use Marker::onDidChange instead")
when 'destroyed'
Grim.deprecate("Use Marker::onDidDestroy instead")
else
Grim.deprecate("Marker::on is deprecated. Use documented event subscription methods instead.")
EmitterMixin::on.apply(this, arguments)

View File

@@ -34,6 +34,7 @@ class Pane extends Model
super
@emitter = new Emitter
@itemSubscriptions = new WeakMap
@items = []
@addItems(compact(params?.items ? []))
@@ -249,6 +250,10 @@ class Pane extends Model
getPanes: -> [this]
unsubscribeFromItem: (item) ->
@itemSubscriptions.get(item)?.dispose()
@itemSubscriptions.delete(item)
###
Section: Items
###
@@ -340,7 +345,9 @@ class Pane extends Model
addItem: (item, index=@getActiveItemIndex() + 1) ->
return if item in @items
if typeof item.on is 'function'
if typeof item.onDidDestroy is 'function'
@itemSubscriptions.set item, item.onDidDestroy => @removeItem(item, true)
else if typeof item.on is 'function'
@subscribe item, 'destroyed', => @removeItem(item, true)
@items.splice(index, 0, item)
@@ -369,6 +376,7 @@ class Pane extends Model
if typeof item.on is 'function'
@unsubscribe item
@unsubscribeFromItem(item)
if item is @activeItem
if @items.length is 1

View File

@@ -118,9 +118,14 @@ class Project extends Model
onDidChangePaths: (callback) ->
@emitter.on 'did-change-paths', callback
onDidAddBuffer: (callback) ->
@emitter.on 'did-add-buffer', callback
on: (eventName) ->
if eventName is 'path-changed'
Grim.deprecate("Use Project::onDidChangePaths instead")
else
Grim.deprecate("Project::on is deprecated. Use documented event subscription methods instead.")
super
###
@@ -434,6 +439,7 @@ class Project extends Model
@buffers.splice(index, 0, buffer)
@subscribeToBuffer(buffer)
@emit 'buffer-created', buffer
@emitter.emit 'did-add-buffer', buffer
buffer
# Removes a {TextBuffer} association from the project.

View File

@@ -67,6 +67,8 @@ class Selection extends Model
Grim.deprecate("Use Selection::onDidChangeRange instead. Call ::getScreenRange() yourself in your callback if you need the range.")
when 'destroyed'
Grim.deprecate("Use Selection::onDidDestroy instead.")
else
Grim.deprecate("Selection::on is deprecated. Use documented event subscription methods instead.")
super
@@ -183,7 +185,7 @@ class Selection extends Model
# Public: Clears the selection, moving the marker to the head.
clear: ->
@marker.setProperties(goalBufferRange: null)
@marker.setProperties(goalScreenRange: null)
@marker.clearTail() unless @retainSelection
@finalize()
@@ -656,38 +658,38 @@ class Selection extends Model
# Public: Moves the selection down one row.
addSelectionBelow: ->
range = (@getGoalBufferRange() ? @getBufferRange()).copy()
range = (@getGoalScreenRange() ? @getScreenRange()).copy()
nextRow = range.end.row + 1
for row in [nextRow..@editor.getLastBufferRow()]
for row in [nextRow..@editor.getLastScreenRow()]
range.start.row = row
range.end.row = row
clippedRange = @editor.clipBufferRange(range)
clippedRange = @editor.clipScreenRange(range, skipSoftWrapIndentation: true)
if range.isEmpty()
continue if range.end.column > 0 and clippedRange.end.column is 0
else
continue if clippedRange.isEmpty()
@editor.addSelectionForBufferRange(range, goalBufferRange: range)
@editor.addSelectionForScreenRange(clippedRange, goalScreenRange: range)
break
# Public: Moves the selection up one row.
addSelectionAbove: ->
range = (@getGoalBufferRange() ? @getBufferRange()).copy()
range = (@getGoalScreenRange() ? @getScreenRange()).copy()
previousRow = range.end.row - 1
for row in [previousRow..0]
range.start.row = row
range.end.row = row
clippedRange = @editor.clipBufferRange(range)
clippedRange = @editor.clipScreenRange(range, skipSoftWrapIndentation: true)
if range.isEmpty()
continue if range.end.column > 0 and clippedRange.end.column is 0
else
continue if clippedRange.isEmpty()
@editor.addSelectionForBufferRange(range, goalBufferRange: range)
@editor.addSelectionForScreenRange(clippedRange, goalScreenRange: range)
break
# Public: Combines the given selection into this selection and then destroys
@@ -696,12 +698,14 @@ class Selection extends Model
# * `otherSelection` A {Selection} to merge with.
# * `options` (optional) {Object} options matching those found in {::setBufferRange}.
merge: (otherSelection, options) ->
myGoalBufferRange = @getGoalBufferRange()
otherGoalBufferRange = otherSelection.getGoalBufferRange()
if myGoalBufferRange? and otherGoalBufferRange?
options.goalBufferRange = myGoalBufferRange.union(otherGoalBufferRange)
myGoalScreenRange = @getGoalScreenRange()
otherGoalScreenRange = otherSelection.getGoalScreenRange()
if myGoalScreenRange? and otherGoalScreenRange?
options.goalScreenRange = myGoalScreenRange.union(otherGoalScreenRange)
else
options.goalBufferRange = myGoalBufferRange ? otherGoalBufferRange
options.goalScreenRange = myGoalScreenRange ? otherGoalScreenRange
@setBufferRange(@getBufferRange().union(otherSelection.getBufferRange()), options)
otherSelection.destroy()
@@ -763,6 +767,6 @@ class Selection extends Model
plantTail: ->
@marker.plantTail()
getGoalBufferRange: ->
if goalBufferRange = @marker.getProperties().goalBufferRange
Range.fromObject(goalBufferRange)
getGoalScreenRange: ->
if goalScreenRange = @marker.getProperties().goalScreenRange
Range.fromObject(goalScreenRange)

View File

@@ -457,6 +457,10 @@ class TextEditor extends Model
onDidChangeScrollLeft: (callback) ->
@emitter.on 'did-change-scroll-left', callback
# TODO Remove once the tabs package no longer uses .on subscriptions
onDidChangeIcon: (callback) ->
@emitter.on 'did-change-icon', callback
on: (eventName) ->
switch eventName
when 'title-changed'
@@ -512,6 +516,9 @@ class TextEditor extends Model
when 'scroll-left-changed'
deprecate("Use TextEditor::onDidChangeScrollLeft instead")
else
deprecate("TextEditor::on is deprecated. Use documented event subscription methods instead.")
EmitterMixin::on.apply(this, arguments)
# Retrieves the current {TextBuffer}.
@@ -1275,6 +1282,14 @@ class TextEditor extends Model
# Returns a {Point}.
clipScreenPosition: (screenPosition, options) -> @displayBuffer.clipScreenPosition(screenPosition, options)
# Extended: Clip the start and end of the given range to valid positions on screen.
# See {::clipScreenPosition} for more information.
#
# * `range` The {Range} to clip.
# * `options` (optional) See {::clipScreenPosition} `options`.
# Returns a {Range}.
clipScreenRange: (range, options) -> @displayBuffer.clipScreenRange(range, options)
###
Section: Decorations
###

View File

@@ -138,12 +138,19 @@ class TokenizedBuffer extends Model
tokenizeInBackground: ->
return if not @visible or @pendingChunk or not @isAlive()
@pendingChunk = true
_.defer =>
@pendingChunk = false
@tokenizeNextChunk() if @isAlive() and @buffer.isAlive()
tokenizeNextChunk: ->
# Short circuit null grammar which can just use the placeholder tokens
if @grammar is atom.grammars.nullGrammar and @firstInvalidRow()?
@invalidRows = []
@markTokenizationComplete()
return
rowsRemaining = @chunkSize
while @firstInvalidRow()? and rowsRemaining > 0
@@ -177,10 +184,13 @@ class TokenizedBuffer extends Model
if @firstInvalidRow()?
@tokenizeInBackground()
else
unless @fullyTokenized
@emit 'tokenized'
@emitter.emit 'did-tokenize'
@fullyTokenized = true
@markTokenizationComplete()
markTokenizationComplete: ->
unless @fullyTokenized
@emit 'tokenized'
@emitter.emit 'did-tokenize'
@fullyTokenized = true
firstInvalidRow: ->
@invalidRows[0]

View File

@@ -11,6 +11,7 @@ module.exports =
class TokenizedLine
endOfLineInvisibles: null
lineIsWhitespaceOnly: false
firstNonWhitespaceIndex: 0
foldable: false
constructor: ({tokens, @lineEnding, @ruleStack, @startBufferColumn, @fold, @tabLength, @indentLevel, @invisibles}) ->
@@ -96,6 +97,7 @@ class TokenizedLine
# Returns a {Number} representing the `line` position where the wrap would take place.
# Returns `null` if a wrap wouldn't occur.
findWrapColumn: (maxColumn) ->
return unless maxColumn?
return unless @text.length > maxColumn
if /\s/.test(@text[maxColumn])
@@ -106,43 +108,37 @@ class TokenizedLine
return @text.length
else
# search backward for the start of the word on the boundary
for column in [maxColumn..0] when @isColumnOutsideSoftWrapIndentation(column)
for column in [maxColumn..@firstNonWhitespaceIndex]
return column + 1 if /\s/.test(@text[column])
return maxColumn
# Calculates how many trailing spaces in this line's indentation cannot fit in a single tab.
#
# Returns a {Number} representing the odd indentation spaces in this line.
getOddIndentationSpaces: ->
oddIndentLevel = @indentLevel - Math.floor(@indentLevel)
Math.round(@tabLength * oddIndentLevel)
buildSoftWrapIndentationTokens: (token, hangingIndent) ->
totalIndentSpaces = (@indentLevel * @tabLength) + hangingIndent
indentTokens = []
while totalIndentSpaces > 0
tokenLength = Math.min(@tabLength, totalIndentSpaces)
indentToken = token.buildSoftWrapIndentationToken(tokenLength)
indentTokens.push(indentToken)
totalIndentSpaces -= tokenLength
buildSoftWrapIndentationTokens: (token) ->
indentTokens = [0...Math.floor(@indentLevel)].map =>
token.buildSoftWrapIndentationToken(@tabLength)
indentTokens
if @getOddIndentationSpaces()
indentTokens.concat(
token.buildSoftWrapIndentationToken @getOddIndentationSpaces()
)
else
indentTokens
softWrapAt: (column) ->
softWrapAt: (column, hangingIndent) ->
return [new TokenizedLine([], '', [0, 0], [0, 0]), this] if column == 0
rightTokens = new Array(@tokens...)
leftTokens = []
leftTextLength = 0
while leftTextLength < column
if leftTextLength + rightTokens[0].value.length > column
rightTokens[0..0] = rightTokens[0].splitAt(column - leftTextLength)
leftScreenColumn = 0
while leftScreenColumn < column
if leftScreenColumn + rightTokens[0].screenDelta > column
rightTokens[0..0] = rightTokens[0].splitAt(column - leftScreenColumn)
nextToken = rightTokens.shift()
leftTextLength += nextToken.value.length
leftScreenColumn += nextToken.screenDelta
leftTokens.push nextToken
indentationTokens = @buildSoftWrapIndentationTokens(leftTokens[0])
indentationTokens = @buildSoftWrapIndentationTokens(leftTokens[0], hangingIndent)
leftFragment = new TokenizedLine(
tokens: leftTokens
@@ -167,11 +163,6 @@ class TokenizedLine
isSoftWrapped: ->
@lineEnding is null
isColumnOutsideSoftWrapIndentation: (column) ->
return true if @softWrapIndentationTokens.length == 0
column > @softWrapIndentationDelta
isColumnInsideSoftWrapIndentation: (column) ->
return false if @softWrapIndentationTokens.length == 0
@@ -216,15 +207,15 @@ class TokenizedLine
outputTokens
markLeadingAndTrailingWhitespaceTokens: ->
firstNonWhitespaceIndex = @text.search(NonWhitespaceRegex)
if firstNonWhitespaceIndex > 0 and isPairedCharacter(@text, firstNonWhitespaceIndex - 1)
firstNonWhitespaceIndex--
@firstNonWhitespaceIndex = @text.search(NonWhitespaceRegex)
if @firstNonWhitespaceIndex > 0 and isPairedCharacter(@text, @firstNonWhitespaceIndex - 1)
@firstNonWhitespaceIndex--
firstTrailingWhitespaceIndex = @text.search(TrailingWhitespaceRegex)
@lineIsWhitespaceOnly = firstTrailingWhitespaceIndex is 0
index = 0
for token in @tokens
if index < firstNonWhitespaceIndex
token.firstNonWhitespaceIndex = Math.min(index + token.value.length, firstNonWhitespaceIndex - index)
if index < @firstNonWhitespaceIndex
token.firstNonWhitespaceIndex = Math.min(index + token.value.length, @firstNonWhitespaceIndex - index)
# Only the *last* segment of a soft-wrapped line can have trailing whitespace
if @lineEnding? and (index + token.value.length > firstTrailingWhitespaceIndex)
token.firstTrailingWhitespaceIndex = Math.max(0, firstTrailingWhitespaceIndex - index)