mirror of
https://github.com/atom/atom.git
synced 2026-01-24 06:18:03 -05:00
Merge pull request #498 from github/api/docs
Add API documentation for src/app
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,3 +12,4 @@ tags
|
||||
/cef/
|
||||
/sources.gypi
|
||||
/node/
|
||||
docs/api
|
||||
|
||||
19
Rakefile
19
Rakefile
@@ -116,6 +116,25 @@ task :tags do
|
||||
system %{find src native cef vendor -not -name "*spec.coffee" -type f -print0 | xargs -0 ctags}
|
||||
end
|
||||
|
||||
namespace :docs do
|
||||
namespace :app do
|
||||
desc "Builds the API docs in src/app"
|
||||
task :build do
|
||||
system %{./node_modules/biscotto/bin/biscotto -o docs/api src/app/}
|
||||
end
|
||||
|
||||
desc "Lists the stats for API doc coverage in src/app"
|
||||
task :stats do
|
||||
system %{./node_modules/biscotto/bin/biscotto --statsOnly src/app/}
|
||||
end
|
||||
|
||||
desc "Show which docs are missing"
|
||||
task :missing do
|
||||
system %{./node_modules/biscotto/bin/biscotto --listMissing src/app/}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def application_path
|
||||
applications = FileList["#{BUILD_DIR}/**/Atom.app"]
|
||||
if applications.size == 0
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
"less": "git://github.com/nathansobo/less.js.git"
|
||||
},
|
||||
|
||||
"devDependencies" : {
|
||||
"biscotto" : "0.0.8"
|
||||
},
|
||||
|
||||
"private": true,
|
||||
|
||||
"scripts": {
|
||||
|
||||
@@ -2,6 +2,9 @@ $ = require 'jquery'
|
||||
RootView = require 'root-view'
|
||||
{$$} = require 'space-pen'
|
||||
fsUtils = require 'fs-utils'
|
||||
Exec = require('child_process').exec
|
||||
_ = require 'underscore'
|
||||
Project = require 'project'
|
||||
|
||||
describe "the `atom` global", ->
|
||||
beforeEach ->
|
||||
@@ -321,3 +324,26 @@ describe "the `atom` global", ->
|
||||
expect(atom.sendMessageToBrowserProcess.callCount).toBe 1
|
||||
expect(atom.sendMessageToBrowserProcess.argsForCall[0][1][0]).toBe "A2"
|
||||
atom.sendMessageToBrowserProcess.simulateConfirmation('Next')
|
||||
|
||||
describe "API documentation", ->
|
||||
it "meets a minimum threshold for /app (with no errors)", ->
|
||||
docRunner = jasmine.createSpy("docRunner")
|
||||
Exec "cd #{project.resolve('../..')} && rake docs:app:stats", docRunner
|
||||
waitsFor ->
|
||||
docRunner.callCount > 0
|
||||
|
||||
runs ->
|
||||
# error
|
||||
expect(docRunner.argsForCall[0][0]).toBeNull()
|
||||
|
||||
results = docRunner.argsForCall[0][1].split("\n")
|
||||
results.pop()
|
||||
|
||||
errors = parseInt results.pop().match(/\d+/)
|
||||
expect(errors).toBe 0
|
||||
|
||||
coverage = parseFloat results.pop().match(/.+?%/)
|
||||
expect(coverage).toBeGreaterThan 80
|
||||
|
||||
# stderr
|
||||
expect(docRunner.argsForCall[0][2]).toBe ''
|
||||
@@ -5,6 +5,11 @@ _ = require 'underscore'
|
||||
$ = require 'jquery'
|
||||
CSON = require 'cson'
|
||||
|
||||
|
||||
###
|
||||
# Internal: Loads and resolves packages. #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class AtomPackage extends Package
|
||||
metadata: null
|
||||
|
||||
@@ -2,12 +2,17 @@ fsUtils = require 'fs-utils'
|
||||
Theme = require 'theme'
|
||||
CSON = require 'cson'
|
||||
|
||||
# Internal: Represents a theme that Atom can use.
|
||||
module.exports =
|
||||
class AtomTheme extends Theme
|
||||
|
||||
# Internal: Given a path, this loads it as a stylesheet.
|
||||
#
|
||||
# stylesheetPath - A {String} to a stylesheet
|
||||
loadStylesheet: (stylesheetPath)->
|
||||
@stylesheets[stylesheetPath] = window.loadStylesheet(stylesheetPath)
|
||||
|
||||
# Internal: Loads the stylesheets found in a `package.cson` file.
|
||||
load: ->
|
||||
if fsUtils.extension(@path) in ['.css', '.less']
|
||||
@loadStylesheet(@path)
|
||||
|
||||
@@ -5,6 +5,10 @@ fsUtils = require 'fs-utils'
|
||||
Specificity = require 'specificity'
|
||||
PEG = require 'pegjs'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class BindingSet
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
Range = require 'range'
|
||||
_ = require 'underscore'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class BufferChangeOperation
|
||||
buffer: null
|
||||
|
||||
@@ -10,10 +10,23 @@ class BufferMarker
|
||||
suppressObserverNotification: false
|
||||
invalidationStrategy: null
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
constructor: ({@id, @buffer, range, @invalidationStrategy, noTail, reverse}) ->
|
||||
@invalidationStrategy ?= 'contains'
|
||||
@setRange(range, {noTail, reverse})
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
# Public: Sets the marker's range, potentialy modifying both its head and tail.
|
||||
#
|
||||
# range - The new {Range} the marker should cover
|
||||
# options - A hash of options with the following keys:
|
||||
# :reverse - if `true`, the marker is reversed; that is, its tail is "above" the head
|
||||
# :noTail - if `true`, the marker doesn't have a tail
|
||||
setRange: (range, options={}) ->
|
||||
@consolidateObserverNotifications false, =>
|
||||
range = Range.fromObject(range)
|
||||
@@ -24,22 +37,47 @@ class BufferMarker
|
||||
@setTailPosition(range.start) unless options.noTail
|
||||
@setHeadPosition(range.end)
|
||||
|
||||
# Public: Identifies if the ending position of a marker is greater than the starting position.
|
||||
#
|
||||
# This can happen when, for example, you highlight text "up" in a {Buffer}.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isReversed: ->
|
||||
@tailPosition? and @headPosition.isLessThan(@tailPosition)
|
||||
|
||||
# Public: Identifies if the marker's head position is equal to its tail.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isRangeEmpty: ->
|
||||
@getHeadPosition().isEqual(@getTailPosition())
|
||||
|
||||
# Public: Retrieves the {Range} between a marker's head and its tail.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getRange: ->
|
||||
if @tailPosition
|
||||
new Range(@tailPosition, @headPosition)
|
||||
else
|
||||
new Range(@headPosition, @headPosition)
|
||||
|
||||
# Public: Retrieves the position of the marker's head.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getHeadPosition: -> @headPosition
|
||||
|
||||
# Public: Retrieves the position of the marker's tail.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getTailPosition: -> @tailPosition ? @getHeadPosition()
|
||||
|
||||
# Public: Sets the position of the marker's head.
|
||||
#
|
||||
# newHeadPosition - The new {Point} to place the head
|
||||
# options - A hash with the following keys:
|
||||
# :clip - if `true`, the point is [clipped]{Buffer.clipPosition}
|
||||
# :bufferChanged - if `true`, indicates that the {Buffer} should trigger an event that it's changed
|
||||
#
|
||||
# Returns a {Point} representing the new head position.
|
||||
setHeadPosition: (newHeadPosition, options={}) ->
|
||||
oldHeadPosition = @getHeadPosition()
|
||||
newHeadPosition = Point.fromObject(newHeadPosition)
|
||||
@@ -50,6 +88,14 @@ class BufferMarker
|
||||
@notifyObservers({oldHeadPosition, newHeadPosition, bufferChanged})
|
||||
@headPosition
|
||||
|
||||
# Public: Sets the position of the marker's tail.
|
||||
#
|
||||
# newHeadPosition - The new {Point} to place the tail
|
||||
# options - A hash with the following keys:
|
||||
# :clip - if `true`, the point is [clipped]{Buffer.clipPosition}
|
||||
# :bufferChanged - if `true`, indicates that the {Buffer} should trigger an event that it's changed
|
||||
#
|
||||
# Returns a {Point} representing the new tail position.
|
||||
setTailPosition: (newTailPosition, options={}) ->
|
||||
oldTailPosition = @getTailPosition()
|
||||
newTailPosition = Point.fromObject(newTailPosition)
|
||||
@@ -60,21 +106,52 @@ class BufferMarker
|
||||
@notifyObservers({oldTailPosition, newTailPosition, bufferChanged})
|
||||
@tailPosition
|
||||
|
||||
# Public: Retrieves the starting position of the marker.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getStartPosition: ->
|
||||
@getRange().start
|
||||
|
||||
# Public: Retrieves the ending position of the marker.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getEndPosition: ->
|
||||
@getRange().end
|
||||
|
||||
# Public: Sets the marker's tail to the same position as the marker's head.
|
||||
#
|
||||
# This only works if there isn't already a tail position.
|
||||
#
|
||||
# Returns a {Point} representing the new tail position.
|
||||
placeTail: ->
|
||||
@setTailPosition(@getHeadPosition()) unless @tailPosition
|
||||
|
||||
# Public: Removes the tail from the marker.
|
||||
clearTail: ->
|
||||
oldTailPosition = @getTailPosition()
|
||||
@tailPosition = null
|
||||
newTailPosition = @getTailPosition()
|
||||
@notifyObservers({oldTailPosition, newTailPosition, bufferChanged: false})
|
||||
|
||||
# Public: Identifies if a {Point} is within the marker.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
containsPoint: (point) ->
|
||||
@getRange().containsPoint(point)
|
||||
|
||||
# Public: Sets a callback to be fired whenever a marker is changed.
|
||||
observe: (callback) ->
|
||||
@on 'changed', callback
|
||||
cancel: => @unobserve(callback)
|
||||
|
||||
# Public: Removes the fired callback whenever a marker changes.
|
||||
unobserve: (callback) ->
|
||||
@off 'changed', callback
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
tryToInvalidate: (changedRange) ->
|
||||
betweenStartAndEnd = @getRange().containsRange(changedRange, exclusive: false)
|
||||
containsStart = changedRange.containsPoint(@getStartPosition(), exclusive: true)
|
||||
@@ -122,16 +199,6 @@ class BufferMarker
|
||||
|
||||
[newRow, newColumn]
|
||||
|
||||
observe: (callback) ->
|
||||
@on 'changed', callback
|
||||
cancel: => @unobserve(callback)
|
||||
|
||||
unobserve: (callback) ->
|
||||
@off 'changed', callback
|
||||
|
||||
containsPoint: (point) ->
|
||||
@getRange().containsPoint(point)
|
||||
|
||||
notifyObservers: ({oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged} = {}) ->
|
||||
return if @suppressObserverNotification
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ vendoredThemesDirPath = fsUtils.join(resourcePath, "vendor/themes")
|
||||
userThemesDirPath = fsUtils.join(configDirPath, "themes")
|
||||
userPackagesDirPath = fsUtils.join(configDirPath, "packages")
|
||||
|
||||
# Public: Handles all of Atom's configuration details.
|
||||
#
|
||||
# This includes loading and setting default options, as well as reading from the
|
||||
# user's configuration file.
|
||||
module.exports =
|
||||
class Config
|
||||
configDirPath: configDirPath
|
||||
@@ -22,6 +26,10 @@ class Config
|
||||
settings: null
|
||||
configFileHasErrors: null
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
constructor: ->
|
||||
@defaultSettings =
|
||||
core: _.clone(require('root-view').configDefaults)
|
||||
@@ -63,10 +71,24 @@ class Config
|
||||
console.error "Failed to load user config '#{@configFilePath}'", e.message
|
||||
console.error e.stack
|
||||
|
||||
# Public: Retrieves the setting for the given key.
|
||||
#
|
||||
# keyPath - The {String} name of the key to retrieve
|
||||
#
|
||||
# Returns the value from Atom's default settings, the user's configuration file,
|
||||
# or `null` if the key doesn't exist in either.
|
||||
get: (keyPath) ->
|
||||
_.valueForKeyPath(@settings, keyPath) ?
|
||||
_.valueForKeyPath(@defaultSettings, keyPath)
|
||||
|
||||
# Public: Sets the value for a configuration setting.
|
||||
#
|
||||
# This value is stored in Atom's internal configuration file.
|
||||
#
|
||||
# keyPath - The {String} name of the key
|
||||
# value - The value of the setting
|
||||
#
|
||||
# Returns the `value`.
|
||||
set: (keyPath, value) ->
|
||||
_.setValueForKeyPath(@settings, keyPath, value)
|
||||
@update()
|
||||
@@ -82,6 +104,13 @@ class Config
|
||||
_.extend hash, defaults
|
||||
@update()
|
||||
|
||||
# Public: Establishes an event listener for a given key.
|
||||
#
|
||||
# Whenever the value of the key is changed, a callback is fired.
|
||||
#
|
||||
# keyPath - The {String} name of the key to watch
|
||||
# callback - The {Function} that fires when the. It is given a single argument, `value`,
|
||||
# which is the new value of `keyPath`.
|
||||
observe: (keyPath, callback) ->
|
||||
value = @get(keyPath)
|
||||
previousValue = _.clone(value)
|
||||
|
||||
@@ -3,6 +3,7 @@ Point = require 'point'
|
||||
Range = require 'range'
|
||||
_ = require 'underscore'
|
||||
|
||||
# Internal:
|
||||
module.exports =
|
||||
class CursorView extends View
|
||||
@content: ->
|
||||
|
||||
@@ -3,6 +3,9 @@ Range = require 'range'
|
||||
EventEmitter = require 'event-emitter'
|
||||
_ = require 'underscore'
|
||||
|
||||
# Public: The `Cursor` class represents the little blinking line identifying where text can be inserted.
|
||||
#
|
||||
# Cursors have some metadata attached in the form of a {BufferMarker}.
|
||||
module.exports =
|
||||
class Cursor
|
||||
screenPosition: null
|
||||
@@ -11,6 +14,9 @@ class Cursor
|
||||
visible: true
|
||||
needsAutoscroll: null
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
constructor: ({@editSession, @marker}) ->
|
||||
@updateVisibility()
|
||||
@editSession.observeMarker @marker, (e) =>
|
||||
@@ -39,20 +45,6 @@ class Cursor
|
||||
@editSession.removeCursor(this)
|
||||
@trigger 'destroyed'
|
||||
|
||||
setScreenPosition: (screenPosition, options={}) ->
|
||||
@changePosition options, =>
|
||||
@editSession.setMarkerHeadScreenPosition(@marker, screenPosition, options)
|
||||
|
||||
getScreenPosition: ->
|
||||
@editSession.getMarkerHeadScreenPosition(@marker)
|
||||
|
||||
setBufferPosition: (bufferPosition, options={}) ->
|
||||
@changePosition options, =>
|
||||
@editSession.setMarkerHeadBufferPosition(@marker, bufferPosition, options)
|
||||
|
||||
getBufferPosition: ->
|
||||
@editSession.getMarkerHeadBufferPosition(@marker)
|
||||
|
||||
changePosition: (options, fn) ->
|
||||
@goalColumn = null
|
||||
@clearSelection()
|
||||
@@ -60,82 +52,161 @@ class Cursor
|
||||
unless fn()
|
||||
@trigger 'autoscrolled' if @needsAutoscroll
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
# Public: Moves a cursor to a given screen position.
|
||||
#
|
||||
# screenPosition - An {Array} of two numbers: the screen row, and the screen column.
|
||||
# options - An object with the following keys:
|
||||
# :autoscroll - A {Boolean} which, if `true`, scrolls the {EditSession} to wherever the cursor moves to
|
||||
#
|
||||
setScreenPosition: (screenPosition, options={}) ->
|
||||
@changePosition options, =>
|
||||
@editSession.setMarkerHeadScreenPosition(@marker, screenPosition, options)
|
||||
|
||||
# Public: Gets the screen position of the cursor.
|
||||
#
|
||||
# Returns an {Array} of two numbers: the screen row, and the screen column.
|
||||
getScreenPosition: ->
|
||||
@editSession.getMarkerHeadScreenPosition(@marker)
|
||||
|
||||
# Public: Moves a cursor to a given buffer position.
|
||||
#
|
||||
# bufferPosition - An {Array} of two numbers: the buffer row, and the buffer column.
|
||||
# options - An object with the following keys:
|
||||
# :autoscroll - A {Boolean} which, if `true`, scrolls the {EditSession} to wherever the cursor moves to
|
||||
#
|
||||
setBufferPosition: (bufferPosition, options={}) ->
|
||||
@changePosition options, =>
|
||||
@editSession.setMarkerHeadBufferPosition(@marker, bufferPosition, options)
|
||||
|
||||
# Public: Gets the current buffer position.
|
||||
#
|
||||
# Returns an {Array} of two numbers: the buffer row, and the buffer column.
|
||||
getBufferPosition: ->
|
||||
@editSession.getMarkerHeadBufferPosition(@marker)
|
||||
|
||||
# Public: If the marker range is empty, the cursor is marked as being visible.
|
||||
updateVisibility: ->
|
||||
@setVisible(@editSession.isMarkerRangeEmpty(@marker))
|
||||
|
||||
# Public: Sets the visibility of the cursor.
|
||||
#
|
||||
# visible - A {Boolean} indicating whether the cursor should be visible
|
||||
setVisible: (visible) ->
|
||||
if @visible != visible
|
||||
@visible = visible
|
||||
@needsAutoscroll ?= true if @visible and @isLastCursor()
|
||||
@trigger 'visibility-changed', @visible
|
||||
|
||||
# Public: Retrieves the visibility of the cursor.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isVisible: -> @visible
|
||||
|
||||
# Public: Identifies what the cursor considers a "word" RegExp.
|
||||
#
|
||||
# Returns a {RegExp}.
|
||||
wordRegExp: ->
|
||||
nonWordCharacters = config.get("editor.nonWordCharacters")
|
||||
new RegExp("^[\t ]*$|[^\\s#{_.escapeRegExp(nonWordCharacters)}]+|[#{_.escapeRegExp(nonWordCharacters)}]+", "g")
|
||||
|
||||
# Public: Identifies if this cursor is the last in the {EditSession}.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isLastCursor: ->
|
||||
this == @editSession.getCursor()
|
||||
|
||||
# Public: Identifies if the cursor is surrounded by whitespace.
|
||||
#
|
||||
# "Surrounded" here means that all characters before and after the cursor is whitespace.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isSurroundedByWhitespace: ->
|
||||
{row, column} = @getBufferPosition()
|
||||
range = [[row, Math.min(0, column - 1)], [row, Math.max(0, column + 1)]]
|
||||
/^\s+$/.test @editSession.getTextInBufferRange(range)
|
||||
|
||||
# Public: Removes the setting for auto-scroll.
|
||||
clearAutoscroll: ->
|
||||
@needsAutoscroll = null
|
||||
|
||||
# Public: Deselects whatever the cursor is selecting.
|
||||
clearSelection: ->
|
||||
if @selection
|
||||
@selection.goalBufferRange = null
|
||||
@selection.clear() unless @selection.retainSelection
|
||||
|
||||
# Public: Retrieves the cursor's screen row.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getScreenRow: ->
|
||||
@getScreenPosition().row
|
||||
|
||||
# Public: Retrieves the cursor's screen column.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getScreenColumn: ->
|
||||
@getScreenPosition().column
|
||||
|
||||
# Public: Retrieves the cursor's buffer row.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getBufferRow: ->
|
||||
@getBufferPosition().row
|
||||
|
||||
# Public: Retrieves the cursor's buffer column.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getBufferColumn: ->
|
||||
@getBufferPosition().column
|
||||
|
||||
# Public: Retrieves the cursor's buffer row text.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getCurrentBufferLine: ->
|
||||
@editSession.lineForBufferRow(@getBufferRow())
|
||||
|
||||
# Public: Moves the cursor up one screen row.
|
||||
moveUp: (rowCount = 1) ->
|
||||
{ row, column } = @getScreenPosition()
|
||||
column = @goalColumn if @goalColumn?
|
||||
@setScreenPosition({row: row - rowCount, column: column})
|
||||
@goalColumn = column
|
||||
|
||||
# Public: Moves the cursor down one screen row.
|
||||
moveDown: (rowCount = 1) ->
|
||||
{ row, column } = @getScreenPosition()
|
||||
column = @goalColumn if @goalColumn?
|
||||
@setScreenPosition({row: row + rowCount, column: column})
|
||||
@goalColumn = column
|
||||
|
||||
# Public: Moves the cursor left one screen column.
|
||||
moveLeft: ->
|
||||
{ row, column } = @getScreenPosition()
|
||||
[row, column] = if column > 0 then [row, column - 1] else [row - 1, Infinity]
|
||||
@setScreenPosition({row, column})
|
||||
|
||||
# Public: Moves the cursor right one screen column.
|
||||
moveRight: ->
|
||||
{ row, column } = @getScreenPosition()
|
||||
@setScreenPosition([row, column + 1], skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true)
|
||||
|
||||
# Public: Moves the cursor to the top of the buffer.
|
||||
moveToTop: ->
|
||||
@setBufferPosition([0,0])
|
||||
|
||||
# Public: Moves the cursor to the bottom of the buffer.
|
||||
moveToBottom: ->
|
||||
@setBufferPosition(@editSession.getEofBufferPosition())
|
||||
|
||||
# Public: Moves the cursor to the beginning of the buffer line.
|
||||
moveToBeginningOfLine: ->
|
||||
@setBufferPosition([@getBufferRow(), 0])
|
||||
|
||||
# Public: Moves the cursor to the beginning of the first character in the line.
|
||||
moveToFirstCharacterOfLine: ->
|
||||
position = @getBufferPosition()
|
||||
scanRange = @getCurrentLineBufferRange()
|
||||
@@ -146,6 +217,7 @@ class Cursor
|
||||
newPosition = [position.row, 0] if newPosition.isEqual(position)
|
||||
@setBufferPosition(newPosition)
|
||||
|
||||
# Public: Moves the cursor to the beginning of the buffer line, skipping all whitespace.
|
||||
skipLeadingWhitespace: ->
|
||||
position = @getBufferPosition()
|
||||
scanRange = @getCurrentLineBufferRange()
|
||||
@@ -155,20 +227,30 @@ class Cursor
|
||||
|
||||
@setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position)
|
||||
|
||||
# Public: Moves the cursor to the end of the buffer line.
|
||||
moveToEndOfLine: ->
|
||||
@setBufferPosition([@getBufferRow(), Infinity])
|
||||
|
||||
# Public: Moves the cursor to the beginning of the word.
|
||||
moveToBeginningOfWord: ->
|
||||
@setBufferPosition(@getBeginningOfCurrentWordBufferPosition())
|
||||
|
||||
# Public: Moves the cursor to the end of the word.
|
||||
moveToEndOfWord: ->
|
||||
if position = @getEndOfCurrentWordBufferPosition()
|
||||
@setBufferPosition(position)
|
||||
|
||||
# Public: Moves the cursor to the beginning of the next word.
|
||||
moveToBeginningOfNextWord: ->
|
||||
if position = @getBeginningOfNextWordBufferPosition()
|
||||
@setBufferPosition(position)
|
||||
|
||||
# Public: Retrieves the buffer position of where the current word starts.
|
||||
#
|
||||
# options - A hash with one option:
|
||||
# :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp})
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getBeginningOfCurrentWordBufferPosition: (options = {}) ->
|
||||
allowPrevious = options.allowPrevious ? true
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
@@ -184,6 +266,12 @@ class Cursor
|
||||
|
||||
beginningOfWordPosition or currentBufferPosition
|
||||
|
||||
# Public: Retrieves the buffer position of where the current word ends.
|
||||
#
|
||||
# options - A hash with one option:
|
||||
# :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp})
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getEndOfCurrentWordBufferPosition: (options = {}) ->
|
||||
allowNext = options.allowNext ? true
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
@@ -198,6 +286,12 @@ class Cursor
|
||||
|
||||
endOfWordPosition ? currentBufferPosition
|
||||
|
||||
# Public: Retrieves the buffer position of where the next word starts.
|
||||
#
|
||||
# options - A hash with one option:
|
||||
# :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp})
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getBeginningOfNextWordBufferPosition: (options = {}) ->
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
start = if @isSurroundedByWhitespace() then currentBufferPosition else @getEndOfCurrentWordBufferPosition()
|
||||
@@ -210,14 +304,29 @@ class Cursor
|
||||
|
||||
beginningOfNextWordPosition or currentBufferPosition
|
||||
|
||||
# Public: Gets the word located under the cursor.
|
||||
#
|
||||
# options - An object with properties based on {.getBeginningOfCurrentWordBufferPosition}.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getCurrentWordBufferRange: (options={}) ->
|
||||
startOptions = _.extend(_.clone(options), allowPrevious: false)
|
||||
endOptions = _.extend(_.clone(options), allowNext: false)
|
||||
new Range(@getBeginningOfCurrentWordBufferPosition(startOptions), @getEndOfCurrentWordBufferPosition(endOptions))
|
||||
|
||||
# Public: Retrieves the range for the current line.
|
||||
#
|
||||
# options - A hash with the same keys as {EditSession.bufferRangeForBufferRow}
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getCurrentLineBufferRange: (options) ->
|
||||
@editSession.bufferRangeForBufferRow(@getBufferRow(), options)
|
||||
|
||||
# Public: Retrieves the range for the current paragraph.
|
||||
#
|
||||
# A paragraph is defined as a block of text surrounded by empty lines.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getCurrentParagraphBufferRange: ->
|
||||
row = @getBufferRow()
|
||||
return unless /\w/.test(@editSession.lineForBufferRow(row))
|
||||
@@ -235,21 +344,36 @@ class Cursor
|
||||
|
||||
new Range([startRow, 0], [endRow, @editSession.lineLengthForBufferRow(endRow)])
|
||||
|
||||
# Public: Retrieves the characters that constitute a word preceeding the current cursor position.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getCurrentWordPrefix: ->
|
||||
@editSession.getTextInBufferRange([@getBeginningOfCurrentWordBufferPosition(), @getBufferPosition()])
|
||||
|
||||
# Public: Identifies if the cursor is at the start of a line.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isAtBeginningOfLine: ->
|
||||
@getBufferPosition().column == 0
|
||||
|
||||
# Public: Retrieves the indentation level of the current line.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getIndentLevel: ->
|
||||
if @editSession.softTabs
|
||||
@getBufferColumn() / @editSession.getTabLength()
|
||||
else
|
||||
@getBufferColumn()
|
||||
|
||||
# Public: Identifies if the cursor is at the end of a line.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isAtEndOfLine: ->
|
||||
@getBufferPosition().isEqual(@getCurrentLineBufferRange().end)
|
||||
|
||||
# Public: Retrieves the grammar's token scopes for the line.
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
getScopes: ->
|
||||
@editSession.scopesForBufferPosition(@getBufferPosition())
|
||||
|
||||
|
||||
@@ -5,17 +5,35 @@ pathWatcher = require 'pathwatcher'
|
||||
File = require 'file'
|
||||
EventEmitter = require 'event-emitter'
|
||||
|
||||
# Public: Represents a directory in the project.
|
||||
#
|
||||
# Directories contain an array of {File}s.
|
||||
module.exports =
|
||||
class Directory
|
||||
path: null
|
||||
|
||||
# Public: Creates a new directory.
|
||||
#
|
||||
# path - A {String} representing the file directory
|
||||
# symlink - A {Boolean} indicating if the path is a symlink (default: false)
|
||||
constructor: (@path, @symlink=false) ->
|
||||
|
||||
# Public: Retrieves the basename of the directory.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getBaseName: ->
|
||||
fsUtils.base(@path)
|
||||
|
||||
# Public: Retrieves the directory's path.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getPath: -> @path
|
||||
|
||||
# Public: Retrieves the file entries in the directory.
|
||||
#
|
||||
# This does follow symlinks.
|
||||
#
|
||||
# Returns an {Array} of {Files}.
|
||||
getEntries: ->
|
||||
directories = []
|
||||
files = []
|
||||
@@ -33,6 +51,10 @@ class Directory
|
||||
|
||||
directories.concat(files)
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
afterSubscribe: ->
|
||||
@subscribeToNativeChangeEvents() if @subscriptionCount() == 1
|
||||
|
||||
@@ -42,7 +64,7 @@ class Directory
|
||||
subscribeToNativeChangeEvents: ->
|
||||
@watchSubscription = pathWatcher.watch @path, (eventType) =>
|
||||
@trigger "contents-changed" if eventType is "change"
|
||||
|
||||
|
||||
unsubscribeFromNativeChangeEvents: ->
|
||||
if @watchSubscription?
|
||||
@watchSubscription.close()
|
||||
|
||||
@@ -9,62 +9,128 @@ class DisplayBufferMarker
|
||||
tailScreenPosition: null
|
||||
valid: true
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
constructor: ({@id, @displayBuffer}) ->
|
||||
@buffer = @displayBuffer.buffer
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
# Public: Gets the screen range of the display marker.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getScreenRange: ->
|
||||
@displayBuffer.screenRangeForBufferRange(@getBufferRange(), wrapAtSoftNewlines: true)
|
||||
|
||||
# Public: Modifies the screen range of the display marker.
|
||||
#
|
||||
# screenRange - The new {Range} to use
|
||||
# options - A hash of options matching those found in {BufferMarker.setRange}
|
||||
setScreenRange: (screenRange, options) ->
|
||||
@setBufferRange(@displayBuffer.bufferRangeForScreenRange(screenRange, options), options)
|
||||
@setBufferRange(@displayBuffer.bufferRangeForScreenRange(screenRange), options)
|
||||
|
||||
# Public: Gets the buffer range of the display marker.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getBufferRange: ->
|
||||
@buffer.getMarkerRange(@id)
|
||||
|
||||
# Public: Modifies the buffer range of the display marker.
|
||||
#
|
||||
# screenRange - The new {Range} to use
|
||||
# options - A hash of options matching those found in {BufferMarker.setRange}
|
||||
setBufferRange: (bufferRange, options) ->
|
||||
@buffer.setMarkerRange(@id, bufferRange, options)
|
||||
|
||||
# Public: Retrieves the screen position of the marker's head.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getHeadScreenPosition: ->
|
||||
@headScreenPosition ?= @displayBuffer.screenPositionForBufferPosition(@getHeadBufferPosition(), wrapAtSoftNewlines: true)
|
||||
|
||||
# Public: Sets the screen position of the marker's head.
|
||||
#
|
||||
# screenRange - The new {Point} to use
|
||||
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
|
||||
setHeadScreenPosition: (screenPosition, options) ->
|
||||
screenPosition = @displayBuffer.clipScreenPosition(screenPosition, options)
|
||||
@setHeadBufferPosition(@displayBuffer.bufferPositionForScreenPosition(screenPosition, options))
|
||||
|
||||
# Public: Retrieves the buffer position of the marker's head.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getHeadBufferPosition: ->
|
||||
@buffer.getMarkerHeadPosition(@id)
|
||||
|
||||
# Public: Sets the buffer position of the marker's head.
|
||||
#
|
||||
# screenRange - The new {Point} to use
|
||||
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
|
||||
setHeadBufferPosition: (bufferPosition) ->
|
||||
@buffer.setMarkerHeadPosition(@id, bufferPosition)
|
||||
|
||||
# Public: Retrieves the screen position of the marker's tail.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getTailScreenPosition: ->
|
||||
@tailScreenPosition ?= @displayBuffer.screenPositionForBufferPosition(@getTailBufferPosition(), wrapAtSoftNewlines: true)
|
||||
|
||||
|
||||
# Public: Sets the screen position of the marker's tail.
|
||||
#
|
||||
# screenRange - The new {Point} to use
|
||||
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
|
||||
setTailScreenPosition: (screenPosition, options) ->
|
||||
screenPosition = @displayBuffer.clipScreenPosition(screenPosition, options)
|
||||
@setTailBufferPosition(@displayBuffer.bufferPositionForScreenPosition(screenPosition, options))
|
||||
|
||||
# Public: Retrieves the buffer position of the marker's tail.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getTailBufferPosition: ->
|
||||
@buffer.getMarkerTailPosition(@id)
|
||||
|
||||
|
||||
# Public: Sets the buffer position of the marker's tail.
|
||||
#
|
||||
# screenRange - The new {Point} to use
|
||||
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
|
||||
setTailBufferPosition: (bufferPosition) ->
|
||||
@buffer.setMarkerTailPosition(@id, bufferPosition)
|
||||
|
||||
# Public: Sets the marker's tail to the same position as the marker's head.
|
||||
#
|
||||
# This only works if there isn't already a tail position.
|
||||
#
|
||||
# Returns a {Point} representing the new tail position.
|
||||
placeTail: ->
|
||||
@buffer.placeMarkerTail(@id)
|
||||
|
||||
# Public: Removes the tail from the marker.
|
||||
clearTail: ->
|
||||
@buffer.clearMarkerTail(@id)
|
||||
|
||||
# Public: Sets a callback to be fired whenever the marker is changed.
|
||||
#
|
||||
# callback - A {Function} to execute
|
||||
observe: (callback) ->
|
||||
@observeBufferMarkerIfNeeded()
|
||||
@on 'changed', callback
|
||||
cancel: => @unobserve(callback)
|
||||
|
||||
# Public: Removes the callback that's fired whenever the marker changes.
|
||||
#
|
||||
# callback - A {Function} to remove
|
||||
unobserve: (callback) ->
|
||||
@off 'changed', callback
|
||||
@unobserveBufferMarkerIfNeeded()
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
observeBufferMarkerIfNeeded: ->
|
||||
return if @subscriptionCount()
|
||||
@getHeadScreenPosition() # memoize current value
|
||||
|
||||
@@ -19,6 +19,10 @@ class DisplayBuffer
|
||||
foldsById: null
|
||||
markers: null
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
constructor: (@buffer, options={}) ->
|
||||
@id = @constructor.idCounter++
|
||||
@languageMode = options.languageMode
|
||||
@@ -31,8 +35,6 @@ class DisplayBuffer
|
||||
@tokenizedBuffer.on 'changed', @handleTokenizedBufferChange
|
||||
@buffer.on 'markers-updated', @handleMarkersUpdated
|
||||
|
||||
setVisible: (visible) -> @tokenizedBuffer.setVisible(visible)
|
||||
|
||||
buildLineMap: ->
|
||||
@lineMap = new LineMap
|
||||
@lineMap.insertAtScreenRow 0, @buildLinesForBufferRows(0, @buffer.getLastRow())
|
||||
@@ -44,6 +46,15 @@ class DisplayBuffer
|
||||
@trigger 'changed', eventProperties
|
||||
@resumeMarkerObservers()
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
setVisible: (visible) -> @tokenizedBuffer.setVisible(visible)
|
||||
|
||||
# Public: Defines the limit at which the buffer begins to soft wrap text.
|
||||
#
|
||||
# softWrapColumn - A {Number} defining the soft wrap limit.
|
||||
setSoftWrapColumn: (@softWrapColumn) ->
|
||||
start = 0
|
||||
end = @getLastRow()
|
||||
@@ -51,19 +62,40 @@ class DisplayBuffer
|
||||
screenDelta = @getLastRow() - end
|
||||
bufferDelta = 0
|
||||
@triggerChanged({ start, end, screenDelta, bufferDelta })
|
||||
|
||||
|
||||
# Public: Gets the line for the given screen row.
|
||||
#
|
||||
# screenRow - A {Number} indicating the screen row.
|
||||
#
|
||||
# Returns a {String}.
|
||||
lineForRow: (row) ->
|
||||
@lineMap.lineForScreenRow(row)
|
||||
|
||||
# Public: Gets the lines for the given screen row boundaries.
|
||||
#
|
||||
# startRow - A {Number} indicating the beginning screen row.
|
||||
# endRow - A {Number} indicating the ending screen row.
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
linesForRows: (startRow, endRow) ->
|
||||
@lineMap.linesForScreenRows(startRow, endRow)
|
||||
|
||||
# Public: Gets the lines in the buffer.
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
getLines: ->
|
||||
@lineMap.linesForScreenRows(0, @lineMap.lastScreenRow())
|
||||
|
||||
# Public: Given a starting and ending row, this converts every row into a buffer position.
|
||||
#
|
||||
# startRow - The row {Number} to start at
|
||||
# endRow - The row {Number} to end at (default: {.getLastRow})
|
||||
#
|
||||
# Returns an {Array} of {Range}s.
|
||||
bufferRowsForScreenRows: (startRow, endRow) ->
|
||||
@lineMap.bufferRowsForScreenRows(startRow, endRow)
|
||||
|
||||
# Public: Folds all the foldable lines in the buffer.
|
||||
foldAll: ->
|
||||
for currentRow in [0..@buffer.getLastRow()]
|
||||
[startRow, endRow] = @languageMode.rowRangeForFoldAtBufferRow(currentRow) ? []
|
||||
@@ -71,6 +103,7 @@ class DisplayBuffer
|
||||
|
||||
@createFold(startRow, endRow)
|
||||
|
||||
# Public: Unfolds all the foldable lines in the buffer.
|
||||
unfoldAll: ->
|
||||
for row in [@buffer.getLastRow()..0]
|
||||
@activeFolds[row]?.forEach (fold) => @destroyFold(fold)
|
||||
@@ -90,6 +123,9 @@ class DisplayBuffer
|
||||
endRow = currentRow
|
||||
return [startRow, endRow] if startRow isnt endRow
|
||||
|
||||
# Public: Given a buffer row, this folds it.
|
||||
#
|
||||
# bufferRow - A {Number} indicating the buffer row
|
||||
foldBufferRow: (bufferRow) ->
|
||||
for currentRow in [bufferRow..0]
|
||||
rowRange = @rowRangeForCommentAtBufferRow(currentRow)
|
||||
@@ -103,9 +139,18 @@ class DisplayBuffer
|
||||
|
||||
return
|
||||
|
||||
# Public: Given a buffer row, this unfolds it.
|
||||
#
|
||||
# bufferRow - A {Number} indicating the buffer row
|
||||
unfoldBufferRow: (bufferRow) ->
|
||||
@largestFoldContainingBufferRow(bufferRow)?.destroy()
|
||||
|
||||
# Public: Creates a new fold between two row numbers.
|
||||
#
|
||||
# startRow - The row {Number} to start folding at
|
||||
# endRow - The row {Number} to end the fold
|
||||
#
|
||||
# Returns the new {Fold}.
|
||||
createFold: (startRow, endRow) ->
|
||||
return fold if fold = @foldFor(startRow, endRow)
|
||||
fold = new Fold(this, startRow, endRow)
|
||||
@@ -127,15 +172,210 @@ class DisplayBuffer
|
||||
|
||||
fold
|
||||
|
||||
# Public: Given a {Fold}, determines if it is contained within another fold.
|
||||
#
|
||||
# fold - The {Fold} to check
|
||||
#
|
||||
# Returns the contaiing {Fold} (if it exists), `null` otherwise.
|
||||
isFoldContainedByActiveFold: (fold) ->
|
||||
for row, folds of @activeFolds
|
||||
for otherFold in folds
|
||||
return otherFold if fold != otherFold and fold.isContainedByFold(otherFold)
|
||||
|
||||
# Public: Given a starting and ending row, tries to find an existing fold.
|
||||
#
|
||||
# startRow - A {Number} representing a fold's starting row
|
||||
# endRow - A {Number} representing a fold's ending row
|
||||
#
|
||||
# Returns a {Fold} (if it exists).
|
||||
foldFor: (startRow, endRow) ->
|
||||
_.find @activeFolds[startRow] ? [], (fold) ->
|
||||
fold.startRow == startRow and fold.endRow == endRow
|
||||
|
||||
# Public: Removes any folds found that contain the given buffer row.
|
||||
#
|
||||
# bufferRow - The buffer row {Number} to check against
|
||||
destroyFoldsContainingBufferRow: (bufferRow) ->
|
||||
for row, folds of @activeFolds
|
||||
for fold in new Array(folds...)
|
||||
fold.destroy() if fold.getBufferRange().containsRow(bufferRow)
|
||||
|
||||
# Public: Given a buffer row, this returns the largest fold that starts there.
|
||||
#
|
||||
# Largest is defined as the fold whose difference between its start and end points
|
||||
# are the greatest.
|
||||
#
|
||||
# bufferRow - A {Number} indicating the buffer row
|
||||
#
|
||||
# Returns a {Fold}.
|
||||
largestFoldStartingAtBufferRow: (bufferRow) ->
|
||||
return unless folds = @activeFolds[bufferRow]
|
||||
(folds.sort (a, b) -> b.endRow - a.endRow)[0]
|
||||
|
||||
# Public: Given a screen row, this returns the largest fold that starts there.
|
||||
#
|
||||
# Largest is defined as the fold whose difference between its start and end points
|
||||
# are the greatest.
|
||||
#
|
||||
# screenRow - A {Number} indicating the screen row
|
||||
#
|
||||
# Returns a {Fold}.
|
||||
largestFoldStartingAtScreenRow: (screenRow) ->
|
||||
@largestFoldStartingAtBufferRow(@bufferRowForScreenRow(screenRow))
|
||||
|
||||
# Public: Given a buffer row, this returns the largest fold that includes it.
|
||||
#
|
||||
# Largest is defined as the fold whose difference between its start and end points
|
||||
# are the greatest.
|
||||
#
|
||||
# bufferRow - A {Number} indicating the buffer row
|
||||
#
|
||||
# Returns a {Fold}.
|
||||
largestFoldContainingBufferRow: (bufferRow) ->
|
||||
largestFold = null
|
||||
for currentBufferRow in [bufferRow..0]
|
||||
if fold = @largestFoldStartingAtBufferRow(currentBufferRow)
|
||||
largestFold = fold if fold.endRow >= bufferRow
|
||||
largestFold
|
||||
|
||||
# Public: Given a buffer range, this converts it into a screen range.
|
||||
#
|
||||
# bufferRange - A {Range} consisting of buffer positions
|
||||
#
|
||||
# Returns a {Range}.
|
||||
screenLineRangeForBufferRange: (bufferRange) ->
|
||||
@expandScreenRangeToLineEnds(
|
||||
@lineMap.screenRangeForBufferRange(
|
||||
@expandBufferRangeToLineEnds(bufferRange)))
|
||||
|
||||
# Public: Given a buffer row, this converts it into a screen row.
|
||||
#
|
||||
# bufferRow - A {Number} representing a buffer row
|
||||
#
|
||||
# Returns a {Number}.
|
||||
screenRowForBufferRow: (bufferRow) ->
|
||||
@lineMap.screenPositionForBufferPosition([bufferRow, 0]).row
|
||||
|
||||
lastScreenRowForBufferRow: (bufferRow) ->
|
||||
@lineMap.screenPositionForBufferPosition([bufferRow, Infinity]).row
|
||||
|
||||
# Public: Given a screen row, this converts it into a buffer row.
|
||||
#
|
||||
# screenRow - A {Number} representing a screen row
|
||||
#
|
||||
# Returns a {Number}.
|
||||
bufferRowForScreenRow: (screenRow) ->
|
||||
@lineMap.bufferPositionForScreenPosition([screenRow, 0]).row
|
||||
|
||||
# Public: Given a buffer range, this converts it into a screen position.
|
||||
#
|
||||
# bufferRange - The {Range} to convert
|
||||
#
|
||||
# Returns a {Range}.
|
||||
screenRangeForBufferRange: (bufferRange) ->
|
||||
@lineMap.screenRangeForBufferRange(bufferRange)
|
||||
|
||||
# Public: Given a screen range, this converts it into a buffer position.
|
||||
#
|
||||
# screenRange - The {Range} to convert
|
||||
#
|
||||
# Returns a {Range}.
|
||||
bufferRangeForScreenRange: (screenRange) ->
|
||||
@lineMap.bufferRangeForScreenRange(screenRange)
|
||||
|
||||
# Public: Gets the number of lines in the buffer.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
lineCount: ->
|
||||
@lineMap.screenLineCount()
|
||||
|
||||
# Public: Gets the number of the last row in the buffer.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getLastRow: ->
|
||||
@lineCount() - 1
|
||||
|
||||
# Public: Gets the length of the longest screen line.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
maxLineLength: ->
|
||||
@lineMap.maxScreenLineLength
|
||||
|
||||
# Public: Given a buffer position, this converts it into a screen position.
|
||||
#
|
||||
# bufferPosition - An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
# options - A hash of options with the following keys:
|
||||
# :wrapBeyondNewlines -
|
||||
# :wrapAtSoftNewlines -
|
||||
#
|
||||
# Returns a {Point}.
|
||||
screenPositionForBufferPosition: (position, options) ->
|
||||
@lineMap.screenPositionForBufferPosition(position, options)
|
||||
|
||||
# Public: Given a buffer range, this converts it into a screen position.
|
||||
#
|
||||
# screenPosition - An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
# options - A hash of options with the following keys:
|
||||
# :wrapBeyondNewlines -
|
||||
# :wrapAtSoftNewlines -
|
||||
#
|
||||
# Returns a {Point}.
|
||||
bufferPositionForScreenPosition: (position, options) ->
|
||||
@lineMap.bufferPositionForScreenPosition(position, options)
|
||||
|
||||
# Public: Retrieves the grammar's token scopes for a buffer position.
|
||||
#
|
||||
# bufferPosition - A {Point} in the {Buffer}
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
scopesForBufferPosition: (bufferPosition) ->
|
||||
@tokenizedBuffer.scopesForPosition(bufferPosition)
|
||||
|
||||
# Public: Retrieves the current tab length.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getTabLength: ->
|
||||
@tokenizedBuffer.getTabLength()
|
||||
|
||||
# Public: Specifies the tab length.
|
||||
#
|
||||
# tabLength - A {Number} that defines the new tab length.
|
||||
setTabLength: (tabLength) ->
|
||||
@tokenizedBuffer.setTabLength(tabLength)
|
||||
|
||||
# Public: Given a position, this clips it to a real position.
|
||||
#
|
||||
# For example, if `position`'s row exceeds the row count of the buffer,
|
||||
# or if its column goes beyond a line's length, this "sanitizes" the value
|
||||
# to a real position.
|
||||
#
|
||||
# position - The {Point} to clip
|
||||
# options - A hash with the following values:
|
||||
# :wrapBeyondNewlines - if `true`, continues wrapping past newlines
|
||||
# :wrapAtSoftNewlines - if `true`, continues wrapping past soft newlines
|
||||
# :screenLine - if `true`, indicates that you're using a line number, not a row number
|
||||
#
|
||||
# Returns the new, clipped {Point}. Note that this could be the same as `position` if no clipping was performed.
|
||||
clipScreenPosition: (position, options) ->
|
||||
@lineMap.clipScreenPosition(position, options)
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
registerFold: (fold) ->
|
||||
@activeFolds[fold.startRow] ?= []
|
||||
@activeFolds[fold.startRow].push(fold)
|
||||
@foldsById[fold.id] = fold
|
||||
|
||||
unregisterFold: (bufferRow, fold) ->
|
||||
folds = @activeFolds[bufferRow]
|
||||
_.remove(folds, fold)
|
||||
delete @foldsById[fold.id]
|
||||
delete @activeFolds[bufferRow] if folds.length == 0
|
||||
|
||||
destroyFold: (fold) ->
|
||||
@unregisterFold(fold.startRow, fold)
|
||||
|
||||
@@ -154,83 +394,6 @@ class DisplayBuffer
|
||||
|
||||
@triggerChanged({ start, end, screenDelta, bufferDelta })
|
||||
|
||||
destroyFoldsContainingBufferRow: (bufferRow) ->
|
||||
for row, folds of @activeFolds
|
||||
for fold in new Array(folds...)
|
||||
fold.destroy() if fold.getBufferRange().containsRow(bufferRow)
|
||||
|
||||
registerFold: (fold) ->
|
||||
@activeFolds[fold.startRow] ?= []
|
||||
@activeFolds[fold.startRow].push(fold)
|
||||
@foldsById[fold.id] = fold
|
||||
|
||||
unregisterFold: (bufferRow, fold) ->
|
||||
folds = @activeFolds[bufferRow]
|
||||
_.remove(folds, fold)
|
||||
delete @foldsById[fold.id]
|
||||
delete @activeFolds[bufferRow] if folds.length == 0
|
||||
|
||||
largestFoldStartingAtBufferRow: (bufferRow) ->
|
||||
return unless folds = @activeFolds[bufferRow]
|
||||
(folds.sort (a, b) -> b.endRow - a.endRow)[0]
|
||||
|
||||
largestFoldStartingAtScreenRow: (screenRow) ->
|
||||
@largestFoldStartingAtBufferRow(@bufferRowForScreenRow(screenRow))
|
||||
|
||||
largestFoldContainingBufferRow: (bufferRow) ->
|
||||
largestFold = null
|
||||
for currentBufferRow in [bufferRow..0]
|
||||
if fold = @largestFoldStartingAtBufferRow(currentBufferRow)
|
||||
largestFold = fold if fold.endRow >= bufferRow
|
||||
largestFold
|
||||
|
||||
screenLineRangeForBufferRange: (bufferRange) ->
|
||||
@expandScreenRangeToLineEnds(
|
||||
@lineMap.screenRangeForBufferRange(
|
||||
@expandBufferRangeToLineEnds(bufferRange)))
|
||||
|
||||
screenRowForBufferRow: (bufferRow) ->
|
||||
@lineMap.screenPositionForBufferPosition([bufferRow, 0]).row
|
||||
|
||||
lastScreenRowForBufferRow: (bufferRow) ->
|
||||
@lineMap.screenPositionForBufferPosition([bufferRow, Infinity]).row
|
||||
|
||||
bufferRowForScreenRow: (screenRow) ->
|
||||
@lineMap.bufferPositionForScreenPosition([screenRow, 0]).row
|
||||
|
||||
screenRangeForBufferRange: (bufferRange) ->
|
||||
@lineMap.screenRangeForBufferRange(bufferRange)
|
||||
|
||||
bufferRangeForScreenRange: (screenRange) ->
|
||||
@lineMap.bufferRangeForScreenRange(screenRange)
|
||||
|
||||
lineCount: ->
|
||||
@lineMap.screenLineCount()
|
||||
|
||||
getLastRow: ->
|
||||
@lineCount() - 1
|
||||
|
||||
maxLineLength: ->
|
||||
@lineMap.maxScreenLineLength
|
||||
|
||||
screenPositionForBufferPosition: (position, options) ->
|
||||
@lineMap.screenPositionForBufferPosition(position, options)
|
||||
|
||||
bufferPositionForScreenPosition: (position, options) ->
|
||||
@lineMap.bufferPositionForScreenPosition(position, options)
|
||||
|
||||
scopesForBufferPosition: (bufferPosition) ->
|
||||
@tokenizedBuffer.scopesForPosition(bufferPosition)
|
||||
|
||||
getTabLength: ->
|
||||
@tokenizedBuffer.getTabLength()
|
||||
|
||||
setTabLength: (tabLength) ->
|
||||
@tokenizedBuffer.setTabLength(tabLength)
|
||||
|
||||
clipScreenPosition: (position, options) ->
|
||||
@lineMap.clipScreenPosition(position, options)
|
||||
|
||||
handleBufferChange: (e) ->
|
||||
allFolds = [] # Folds can modify @activeFolds, so first make sure we have a stable array of folds
|
||||
allFolds.push(folds...) for row, folds of @activeFolds
|
||||
@@ -299,6 +462,17 @@ class DisplayBuffer
|
||||
|
||||
lineFragments
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
# Public: Given a line, finds the point where it would wrap.
|
||||
#
|
||||
# line - The {String} to check
|
||||
# softWrapColumn - The {Number} where you want soft wrapping to occur
|
||||
#
|
||||
# Returns a {Number} representing the `line` position where the wrap would take place.
|
||||
# Returns `null` if a wrap wouldn't occur.
|
||||
findWrapColumn: (line, softWrapColumn) ->
|
||||
return unless line.length > softWrapColumn
|
||||
|
||||
@@ -313,99 +487,247 @@ class DisplayBuffer
|
||||
return column + 1 if /\s/.test(line[column])
|
||||
return softWrapColumn
|
||||
|
||||
# Public: Given a range in screen coordinates, this expands it to the start and end of a line
|
||||
#
|
||||
# screenRange - The {Range} to expand
|
||||
#
|
||||
# Returns a new {Range}.
|
||||
expandScreenRangeToLineEnds: (screenRange) ->
|
||||
screenRange = Range.fromObject(screenRange)
|
||||
{ start, end } = screenRange
|
||||
new Range([start.row, 0], [end.row, @lineMap.lineForScreenRow(end.row).text.length])
|
||||
|
||||
# Public: Given a range in buffer coordinates, this expands it to the start and end of a line
|
||||
#
|
||||
# screenRange - The {Range} to expand
|
||||
#
|
||||
# Returns a new {Range}.
|
||||
expandBufferRangeToLineEnds: (bufferRange) ->
|
||||
bufferRange = Range.fromObject(bufferRange)
|
||||
{ start, end } = bufferRange
|
||||
new Range([start.row, 0], [end.row, Infinity])
|
||||
|
||||
# Public: Calculates a {Range} representing the start of the {Buffer} until the end.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
rangeForAllLines: ->
|
||||
new Range([0, 0], @clipScreenPosition([Infinity, Infinity]))
|
||||
|
||||
# Public: Retrieves a {DisplayBufferMarker} based on its id.
|
||||
#
|
||||
# id - A {Number} representing a marker id
|
||||
#
|
||||
# Returns the {DisplayBufferMarker} (if it exists).
|
||||
getMarker: (id) ->
|
||||
@markers[id] ? new DisplayBufferMarker({id, displayBuffer: this})
|
||||
|
||||
# Public: Retrieves the active markers in the buffer.
|
||||
#
|
||||
# Returns an {Array} of existing {DisplayBufferMarker}s.
|
||||
getMarkers: ->
|
||||
_.values(@markers)
|
||||
|
||||
# Public: Constructs a new marker at the given screen range.
|
||||
#
|
||||
# range - The marker {Range} (representing the distance between the head and tail)
|
||||
# options - Options to pass to the {BufferMarker} constructor
|
||||
#
|
||||
# Returns a {Number} representing the new marker's ID.
|
||||
markScreenRange: (args...) ->
|
||||
bufferRange = @bufferRangeForScreenRange(args.shift())
|
||||
@markBufferRange(bufferRange, args...)
|
||||
|
||||
# Public: Constructs a new marker at the given buffer range.
|
||||
#
|
||||
# range - The marker {Range} (representing the distance between the head and tail)
|
||||
# options - Options to pass to the {BufferMarker} constructor
|
||||
#
|
||||
# Returns a {Number} representing the new marker's ID.
|
||||
markBufferRange: (args...) ->
|
||||
@buffer.markRange(args...)
|
||||
|
||||
# Public: Constructs a new marker at the given screen position.
|
||||
#
|
||||
# range - The marker {Range} (representing the distance between the head and tail)
|
||||
# options - Options to pass to the {BufferMarker} constructor
|
||||
#
|
||||
# Returns a {Number} representing the new marker's ID.
|
||||
markScreenPosition: (screenPosition, options) ->
|
||||
@markBufferPosition(@bufferPositionForScreenPosition(screenPosition), options)
|
||||
|
||||
# Public: Constructs a new marker at the given buffer position.
|
||||
#
|
||||
# range - The marker {Range} (representing the distance between the head and tail)
|
||||
# options - Options to pass to the {BufferMarker} constructor
|
||||
#
|
||||
# Returns a {Number} representing the new marker's ID.
|
||||
markBufferPosition: (bufferPosition, options) ->
|
||||
@buffer.markPosition(bufferPosition, options)
|
||||
|
||||
# Public: Removes the marker with the given id.
|
||||
#
|
||||
# id - The {Number} of the ID to remove
|
||||
destroyMarker: (id) ->
|
||||
@buffer.destroyMarker(id)
|
||||
delete @markers[id]
|
||||
|
||||
# Public: Gets the screen range of the display marker.
|
||||
#
|
||||
# id - The {Number} of the ID to check
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getMarkerScreenRange: (id) ->
|
||||
@getMarker(id).getScreenRange()
|
||||
|
||||
# Public: Modifies the screen range of the display marker.
|
||||
#
|
||||
# id - The {Number} of the ID to change
|
||||
# screenRange - The new {Range} to use
|
||||
# options - A hash of options matching those found in {BufferMarker.setRange}
|
||||
setMarkerScreenRange: (id, screenRange, options) ->
|
||||
@getMarker(id).setScreenRange(screenRange, options)
|
||||
|
||||
# Public: Gets the buffer range of the display marker.
|
||||
#
|
||||
# id - The {Number} of the ID to check
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getMarkerBufferRange: (id) ->
|
||||
@getMarker(id).getBufferRange()
|
||||
|
||||
# Public: Modifies the buffer range of the display marker.
|
||||
#
|
||||
# id - The {Number} of the ID to change
|
||||
# screenRange - The new {Range} to use
|
||||
# options - A hash of options matching those found in {BufferMarker.setRange}
|
||||
setMarkerBufferRange: (id, bufferRange, options) ->
|
||||
@getMarker(id).setBufferRange(bufferRange, options)
|
||||
|
||||
# Public: Retrieves the screen position of the marker's head.
|
||||
#
|
||||
# id - The {Number} of the ID to check
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getMarkerScreenPosition: (id) ->
|
||||
@getMarkerHeadScreenPosition(id)
|
||||
|
||||
# Public: Retrieves the buffer position of the marker's head.
|
||||
#
|
||||
# id - The {Number} of the ID to check
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getMarkerBufferPosition: (id) ->
|
||||
@getMarkerHeadBufferPosition(id)
|
||||
|
||||
# Public: Retrieves the screen position of the marker's head.
|
||||
#
|
||||
# id - The {Number} of the ID to check
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getMarkerHeadScreenPosition: (id) ->
|
||||
@getMarker(id).getHeadScreenPosition()
|
||||
|
||||
# Public: Sets the screen position of the marker's head.
|
||||
#
|
||||
# id - The {Number} of the ID to change
|
||||
# screenRange - The new {Point} to use
|
||||
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
|
||||
setMarkerHeadScreenPosition: (id, screenPosition, options) ->
|
||||
@getMarker(id).setHeadScreenPosition(screenPosition, options)
|
||||
|
||||
# Public: Retrieves the buffer position of the marker's head.
|
||||
#
|
||||
# id - The {Number} of the ID to check
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getMarkerHeadBufferPosition: (id) ->
|
||||
@getMarker(id).getHeadBufferPosition()
|
||||
|
||||
# Public: Sets the buffer position of the marker's head.
|
||||
#
|
||||
# id - The {Number} of the ID to check
|
||||
# screenRange - The new {Point} to use
|
||||
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
|
||||
setMarkerHeadBufferPosition: (id, bufferPosition) ->
|
||||
@getMarker(id).setHeadBufferPosition(bufferPosition)
|
||||
|
||||
|
||||
# Public: Retrieves the screen position of the marker's tail.
|
||||
#
|
||||
# id - The {Number} of the ID to check
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getMarkerTailScreenPosition: (id) ->
|
||||
@getMarker(id).getTailScreenPosition()
|
||||
|
||||
# Public: Sets the screen position of the marker's tail.
|
||||
#
|
||||
# id - The {Number} of the ID to change
|
||||
# screenRange - The new {Point} to use
|
||||
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
|
||||
setMarkerTailScreenPosition: (id, screenPosition, options) ->
|
||||
@getMarker(id).setTailScreenPosition(screenPosition, options)
|
||||
|
||||
# Public: Retrieves the buffer position of the marker's tail.
|
||||
#
|
||||
# id - The {Number} of the ID to check
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getMarkerTailBufferPosition: (id) ->
|
||||
@getMarker(id).getTailBufferPosition()
|
||||
|
||||
# Public: Sets the buffer position of the marker's tail.
|
||||
#
|
||||
# id - The {Number} of the ID to check
|
||||
# screenRange - The new {Point} to use
|
||||
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
|
||||
setMarkerTailBufferPosition: (id, bufferPosition) ->
|
||||
@getMarker(id).setTailBufferPosition(bufferPosition)
|
||||
|
||||
# Public: Sets the marker's tail to the same position as the marker's head.
|
||||
#
|
||||
# This only works if there isn't already a tail position.
|
||||
#
|
||||
# id - A {Number} representing the marker to change
|
||||
#
|
||||
# Returns a {Point} representing the new tail position.
|
||||
placeMarkerTail: (id) ->
|
||||
@getMarker(id).placeTail()
|
||||
|
||||
# Public: Removes the tail from the marker.
|
||||
#
|
||||
# id - A {Number} representing the marker to change
|
||||
clearMarkerTail: (id) ->
|
||||
@getMarker(id).clearTail()
|
||||
|
||||
# Public: Identifies if the ending position of a marker is greater than the starting position.
|
||||
#
|
||||
# This can happen when, for example, you highlight text "up" in a {Buffer}.
|
||||
#
|
||||
# id - A {Number} representing the marker to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isMarkerReversed: (id) ->
|
||||
@buffer.isMarkerReversed(id)
|
||||
|
||||
# Public: Identifies if the marker's head position is equal to its tail.
|
||||
#
|
||||
# id - A {Number} representing the marker to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isMarkerRangeEmpty: (id) ->
|
||||
@buffer.isMarkerRangeEmpty(id)
|
||||
|
||||
# Public: Sets a callback to be fired whenever a marker is changed.
|
||||
#
|
||||
# id - A {Number} representing the marker to watch
|
||||
# callback - A {Function} to execute
|
||||
observeMarker: (id, callback) ->
|
||||
@getMarker(id).observe(callback)
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
pauseMarkerObservers: ->
|
||||
marker.pauseEvents() for marker in @getMarkers()
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,9 @@ fsUtils = require 'fs-utils'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
|
||||
# Public: Represents the entire visual pane in Atom.
|
||||
#
|
||||
# The Editor manages the {EditSession}, which manages the file buffers.
|
||||
module.exports =
|
||||
class Editor extends View
|
||||
@configDefaults:
|
||||
@@ -23,6 +26,10 @@ class Editor extends View
|
||||
|
||||
@nextEditorId: 1
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
@content: (params) ->
|
||||
@div class: @classes(params), tabindex: -1, =>
|
||||
@subview 'gutter', new Gutter
|
||||
@@ -56,6 +63,13 @@ class Editor extends View
|
||||
newSelections: null
|
||||
redrawOnReattach: false
|
||||
|
||||
# Public: The constructor for setting up an `Editor` instance.
|
||||
#
|
||||
# editSessionOrOptions - Either an {EditSession}, or an object with one property, `mini`.
|
||||
# If `mini` is `true`, a "miniature" `EditSession` 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._.
|
||||
#
|
||||
initialize: (editSessionOrOptions) ->
|
||||
if editSessionOrOptions instanceof EditSession
|
||||
editSession = editSessionOrOptions
|
||||
@@ -87,6 +101,9 @@ class Editor extends View
|
||||
else
|
||||
throw new Error("Must supply an EditSession or mini: true")
|
||||
|
||||
# Internal: Sets up the core Atom commands.
|
||||
#
|
||||
# Some commands are excluded from mini-editors.
|
||||
bindKeys: ->
|
||||
editorBindings =
|
||||
'core:move-left': @moveCursorLeft
|
||||
@@ -171,139 +188,418 @@ class Editor extends View
|
||||
do (name, method) =>
|
||||
@command name, (e) => method.call(this, e); false
|
||||
|
||||
getCursor: -> @activeEditSession.getCursor()
|
||||
getCursors: -> @activeEditSession.getCursors()
|
||||
addCursorAtScreenPosition: (screenPosition) -> @activeEditSession.addCursorAtScreenPosition(screenPosition)
|
||||
addCursorAtBufferPosition: (bufferPosition) -> @activeEditSession.addCursorAtBufferPosition(bufferPosition)
|
||||
moveCursorUp: -> @activeEditSession.moveCursorUp()
|
||||
moveCursorDown: -> @activeEditSession.moveCursorDown()
|
||||
moveCursorLeft: -> @activeEditSession.moveCursorLeft()
|
||||
moveCursorRight: -> @activeEditSession.moveCursorRight()
|
||||
moveCursorToBeginningOfWord: -> @activeEditSession.moveCursorToBeginningOfWord()
|
||||
moveCursorToEndOfWord: -> @activeEditSession.moveCursorToEndOfWord()
|
||||
moveCursorToBeginningOfNextWord: -> @activeEditSession.moveCursorToBeginningOfNextWord()
|
||||
moveCursorToTop: -> @activeEditSession.moveCursorToTop()
|
||||
moveCursorToBottom: -> @activeEditSession.moveCursorToBottom()
|
||||
moveCursorToBeginningOfLine: -> @activeEditSession.moveCursorToBeginningOfLine()
|
||||
moveCursorToFirstCharacterOfLine: -> @activeEditSession.moveCursorToFirstCharacterOfLine()
|
||||
moveCursorToEndOfLine: -> @activeEditSession.moveCursorToEndOfLine()
|
||||
moveLineUp: -> @activeEditSession.moveLineUp()
|
||||
moveLineDown: -> @activeEditSession.moveLineDown()
|
||||
setCursorScreenPosition: (position, options) -> @activeEditSession.setCursorScreenPosition(position, options)
|
||||
duplicateLine: -> @activeEditSession.duplicateLine()
|
||||
joinLine: -> @activeEditSession.joinLine()
|
||||
getCursorScreenPosition: -> @activeEditSession.getCursorScreenPosition()
|
||||
getCursorScreenRow: -> @activeEditSession.getCursorScreenRow()
|
||||
setCursorBufferPosition: (position, options) -> @activeEditSession.setCursorBufferPosition(position, options)
|
||||
getCursorBufferPosition: -> @activeEditSession.getCursorBufferPosition()
|
||||
getCurrentParagraphBufferRange: -> @activeEditSession.getCurrentParagraphBufferRange()
|
||||
getWordUnderCursor: (options) -> @activeEditSession.getWordUnderCursor(options)
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
# Public: Retrieves a single cursor
|
||||
#
|
||||
# Returns a {Cursor}.
|
||||
getCursor: -> @activeEditSession.getCursor()
|
||||
# Public: Retrieves an array of all the cursors.
|
||||
#
|
||||
# Returns a {[Cursor]}.
|
||||
getCursors: -> @activeEditSession.getCursors()
|
||||
# Public: Adds a cursor at the provided `screenPosition`.
|
||||
#
|
||||
# screenPosition - An {Array} of two numbers: the screen row, and the screen column.
|
||||
#
|
||||
# Returns the new {Cursor}.
|
||||
addCursorAtScreenPosition: (screenPosition) -> @activeEditSession.addCursorAtScreenPosition(screenPosition)
|
||||
# Public: Adds a cursor at the provided `bufferPosition`.
|
||||
#
|
||||
# bufferPosition - An {Array} of two numbers: the buffer row, and the buffer column.
|
||||
#
|
||||
# Returns the new {Cursor}.
|
||||
addCursorAtBufferPosition: (bufferPosition) -> @activeEditSession.addCursorAtBufferPosition(bufferPosition)
|
||||
# Public: Moves every cursor up one row.
|
||||
moveCursorUp: -> @activeEditSession.moveCursorUp()
|
||||
# Public: Moves every cursor down one row.
|
||||
moveCursorDown: -> @activeEditSession.moveCursorDown()
|
||||
# Public: Moves every cursor left one column.
|
||||
moveCursorLeft: -> @activeEditSession.moveCursorLeft()
|
||||
# Public: Moves every cursor right one column.
|
||||
moveCursorRight: -> @activeEditSession.moveCursorRight()
|
||||
# Public: Moves every cursor to the beginning of the current word.
|
||||
moveCursorToBeginningOfWord: -> @activeEditSession.moveCursorToBeginningOfWord()
|
||||
# Public: Moves every cursor to the end of the current word.
|
||||
moveCursorToEndOfWord: -> @activeEditSession.moveCursorToEndOfWord()
|
||||
# Public: Moves the cursor to the beginning of the next word.
|
||||
moveCursorToBeginningOfNextWord: -> @activeEditSession.moveCursorToBeginningOfNextWord()
|
||||
# Public: Moves every cursor to the top of the buffer.
|
||||
moveCursorToTop: -> @activeEditSession.moveCursorToTop()
|
||||
# Public: Moves every cursor to the bottom of the buffer.
|
||||
moveCursorToBottom: -> @activeEditSession.moveCursorToBottom()
|
||||
# Public: Moves every cursor to the beginning of the line.
|
||||
moveCursorToBeginningOfLine: -> @activeEditSession.moveCursorToBeginningOfLine()
|
||||
# Public: Moves every cursor to the first non-whitespace character of the line.
|
||||
moveCursorToFirstCharacterOfLine: -> @activeEditSession.moveCursorToFirstCharacterOfLine()
|
||||
# Public: Moves every cursor to the end of the line.
|
||||
moveCursorToEndOfLine: -> @activeEditSession.moveCursorToEndOfLine()
|
||||
# Public: Moves the selected line up one row.
|
||||
moveLineUp: -> @activeEditSession.moveLineUp()
|
||||
# Public: Moves the selected line down one row.
|
||||
moveLineDown: -> @activeEditSession.moveLineDown()
|
||||
# Public: Sets the cursor based on a given screen position.
|
||||
#
|
||||
# position - An {Array} of two numbers: the screen row, and the screen column.
|
||||
# options - An object with properties based on {Cursor.setScreenPosition}.
|
||||
#
|
||||
setCursorScreenPosition: (position, options) -> @activeEditSession.setCursorScreenPosition(position, options)
|
||||
# Public: Duplicates the current line.
|
||||
#
|
||||
# If more than one cursor is present, only the most recently added one is considered.
|
||||
duplicateLine: -> @activeEditSession.duplicateLine()
|
||||
# Public: Joins the current line with the one below it.
|
||||
#
|
||||
# Multiple cursors are considered equally. If there's a selection in the editor,
|
||||
# all the lines are joined together.
|
||||
joinLine: -> @activeEditSession.joinLine()
|
||||
# Public: Gets the current screen position.
|
||||
#
|
||||
# Returns an {Array} of two numbers: the screen row, and the screen column.
|
||||
getCursorScreenPosition: -> @activeEditSession.getCursorScreenPosition()
|
||||
# Public: Gets the current screen row.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getCursorScreenRow: -> @activeEditSession.getCursorScreenRow()
|
||||
# Public: Sets the cursor based on a given buffer position.
|
||||
#
|
||||
# position - An {Array} of two numbers: the buffer row, and the buffer column.
|
||||
# options - An object with properties based on {Cursor.setBufferPosition}.
|
||||
#
|
||||
setCursorBufferPosition: (position, options) -> @activeEditSession.setCursorBufferPosition(position, options)
|
||||
# Public: Gets the current buffer position of the cursor.
|
||||
#
|
||||
# Returns an {Array} of two numbers: the buffer row, and the buffer column.
|
||||
getCursorBufferPosition: -> @activeEditSession.getCursorBufferPosition()
|
||||
# Public: Retrieves the range for the current paragraph.
|
||||
#
|
||||
# A paragraph is defined as a block of text surrounded by empty lines.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getCurrentParagraphBufferRange: -> @activeEditSession.getCurrentParagraphBufferRange()
|
||||
# Public: Gets the word located under the cursor.
|
||||
#
|
||||
# options - An object with properties based on {Cursor.getBeginningOfCurrentWordBufferPosition}.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getWordUnderCursor: (options) -> @activeEditSession.getWordUnderCursor(options)
|
||||
# Public: Gets the selection at the specified index.
|
||||
#
|
||||
# index - The id {Number} of the selection
|
||||
#
|
||||
# Returns a {Selection}.
|
||||
getSelection: (index) -> @activeEditSession.getSelection(index)
|
||||
# Public: Gets the last selection, _i.e._ the most recently added.
|
||||
#
|
||||
# Returns a {Selection}.
|
||||
getSelections: -> @activeEditSession.getSelections()
|
||||
# Public: Gets all selections, ordered by their position in the buffer.
|
||||
#
|
||||
# Returns an {Array} of {Selection}s.
|
||||
getSelectionsOrderedByBufferPosition: -> @activeEditSession.getSelectionsOrderedByBufferPosition()
|
||||
# Public: Gets the very last selection, as it's ordered in the buffer.
|
||||
#
|
||||
# Returns a {Selection}.
|
||||
getLastSelectionInBuffer: -> @activeEditSession.getLastSelectionInBuffer()
|
||||
# Public: Gets the currently selected text.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getSelectedText: -> @activeEditSession.getSelectedText()
|
||||
# Public: Gets the buffer ranges of all the {Selection}s.
|
||||
#
|
||||
# This is ordered by their buffer position.
|
||||
#
|
||||
# Returns an {Array} of {Range}s.
|
||||
getSelectedBufferRanges: -> @activeEditSession.getSelectedBufferRanges()
|
||||
# Public: Gets the buffer range of the most recently added {Selection}.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getSelectedBufferRange: -> @activeEditSession.getSelectedBufferRange()
|
||||
setSelectedBufferRange: (bufferRange, options) -> @activeEditSession.setSelectedBufferRange(bufferRange, options)
|
||||
# Public: Given a buffer range, this removes all previous selections and creates a new selection for it.
|
||||
#
|
||||
# bufferRange - A {Range} in the buffer
|
||||
# options - A hash of options
|
||||
setSelectedBufferRange: (bufferRange, options) -> @activeEditSession.setSelectedBufferRange(bufferRange, options)
|
||||
# Public: Given an array of buffer ranges, this removes all previous selections and creates new selections for them.
|
||||
#
|
||||
# bufferRanges - An {Array} of {Range}s in the buffer
|
||||
# options - A hash of options
|
||||
setSelectedBufferRanges: (bufferRanges, options) -> @activeEditSession.setSelectedBufferRanges(bufferRanges, options)
|
||||
# Public: Given a buffer range, this adds a new selection for it.
|
||||
#
|
||||
# bufferRange - A {Range} in the buffer
|
||||
# options - A hash of options
|
||||
#
|
||||
# Returns the new {Selection}.
|
||||
addSelectionForBufferRange: (bufferRange, options) -> @activeEditSession.addSelectionForBufferRange(bufferRange, options)
|
||||
# Public: Selects the text one position right of the cursor.
|
||||
selectRight: -> @activeEditSession.selectRight()
|
||||
# Public: Selects the text one position left of the cursor.
|
||||
selectLeft: -> @activeEditSession.selectLeft()
|
||||
# Public: Selects all the text one position above the cursor.
|
||||
selectUp: -> @activeEditSession.selectUp()
|
||||
# Public: Selects all the text one position below the cursor.
|
||||
selectDown: -> @activeEditSession.selectDown()
|
||||
# Public: Selects all the text from the current cursor position to the top of the buffer.
|
||||
selectToTop: -> @activeEditSession.selectToTop()
|
||||
# Public: Selects all the text from the current cursor position to the bottom of the buffer.
|
||||
selectToBottom: -> @activeEditSession.selectToBottom()
|
||||
# Public: Selects all the text in the buffer.
|
||||
selectAll: -> @activeEditSession.selectAll()
|
||||
# Public: Selects all the text from the current cursor position to the beginning of the line.
|
||||
selectToBeginningOfLine: -> @activeEditSession.selectToBeginningOfLine()
|
||||
# Public: Selects all the text from the current cursor position to the end of the line.
|
||||
selectToEndOfLine: -> @activeEditSession.selectToEndOfLine()
|
||||
# Public: Moves the current selection down one row.
|
||||
addSelectionBelow: -> @activeEditSession.addSelectionBelow()
|
||||
# Public: Moves the current selection up one row.
|
||||
addSelectionAbove: -> @activeEditSession.addSelectionAbove()
|
||||
# Public: Selects all the text from the current cursor position to the beginning of the word.
|
||||
selectToBeginningOfWord: -> @activeEditSession.selectToBeginningOfWord()
|
||||
# Public: Selects all the text from the current cursor position to the end of the word.
|
||||
selectToEndOfWord: -> @activeEditSession.selectToEndOfWord()
|
||||
# Public: Selects all the text from the current cursor position to the beginning of the next word.
|
||||
selectToBeginningOfNextWord: -> @activeEditSession.selectToBeginningOfNextWord()
|
||||
# Public: Selects the current word.
|
||||
selectWord: -> @activeEditSession.selectWord()
|
||||
# Public: Selects the current line.
|
||||
selectLine: -> @activeEditSession.selectLine()
|
||||
# Public: Selects the text from the current cursor position to a given position.
|
||||
#
|
||||
# position - An instance of {Point}, with a given `row` and `column`.
|
||||
selectToScreenPosition: (position) -> @activeEditSession.selectToScreenPosition(position)
|
||||
# Public: Transposes the current text selections.
|
||||
#
|
||||
# This only works if there is more than one selection. Each selection is transferred
|
||||
# to the position of the selection after it. The last selection is transferred to the
|
||||
# position of the first.
|
||||
transpose: -> @activeEditSession.transpose()
|
||||
# Public: Turns the current selection into upper case.
|
||||
upperCase: -> @activeEditSession.upperCase()
|
||||
# Public: Turns the current selection into lower case.
|
||||
lowerCase: -> @activeEditSession.lowerCase()
|
||||
# Public: Clears every selection. TODO
|
||||
clearSelections: -> @activeEditSession.clearSelections()
|
||||
|
||||
# Public: Performs a backspace, removing the character found behind the cursor position.
|
||||
backspace: -> @activeEditSession.backspace()
|
||||
# Public: Performs a backspace to the beginning of the current word, removing characters found there.
|
||||
backspaceToBeginningOfWord: -> @activeEditSession.backspaceToBeginningOfWord()
|
||||
# Public: Performs a backspace to the beginning of the current line, removing characters found there.
|
||||
backspaceToBeginningOfLine: -> @activeEditSession.backspaceToBeginningOfLine()
|
||||
# Public: Performs a delete, removing the character found ahead the cursor position.
|
||||
delete: -> @activeEditSession.delete()
|
||||
# Public: Performs a delete to the end of the current word, removing characters found there.
|
||||
deleteToEndOfWord: -> @activeEditSession.deleteToEndOfWord()
|
||||
# Public: Performs a delete to the end of the current line, removing characters found there.
|
||||
deleteLine: -> @activeEditSession.deleteLine()
|
||||
# Public: Performs a cut to the end of the current line.
|
||||
#
|
||||
# Characters are removed, but the text remains in the clipboard.
|
||||
cutToEndOfLine: -> @activeEditSession.cutToEndOfLine()
|
||||
# Public: Inserts text at the current cursor positions.
|
||||
#
|
||||
# text - A {String} representing the text to insert.
|
||||
# options - A set of options equivalent to {Selection.insertText}.
|
||||
insertText: (text, options) -> @activeEditSession.insertText(text, options)
|
||||
# Public: Inserts a new line at the current cursor positions.
|
||||
insertNewline: -> @activeEditSession.insertNewline()
|
||||
# Internal:
|
||||
consolidateSelections: (e) -> e.abortKeyBinding() unless @activeEditSession.consolidateSelections()
|
||||
# Public: Inserts a new line below the current cursor positions.
|
||||
insertNewlineBelow: -> @activeEditSession.insertNewlineBelow()
|
||||
# Public: Inserts a new line above the current cursor positions.
|
||||
insertNewlineAbove: -> @activeEditSession.insertNewlineAbove()
|
||||
# Public: Indents the current line.
|
||||
#
|
||||
# options - A set of options equivalent to {Selection.indent}.
|
||||
indent: (options) -> @activeEditSession.indent(options)
|
||||
autoIndent: (options) -> @activeEditSession.autoIndentSelectedRows(options)
|
||||
# Public: TODO
|
||||
autoIndent: (options) -> @activeEditSession.autoIndentSelectedRows()
|
||||
# Public: Indents the selected rows.
|
||||
indentSelectedRows: -> @activeEditSession.indentSelectedRows()
|
||||
# Public: Outdents the selected rows.
|
||||
outdentSelectedRows: -> @activeEditSession.outdentSelectedRows()
|
||||
# Public: Cuts the selected text.
|
||||
cutSelection: -> @activeEditSession.cutSelectedText()
|
||||
# Public: Copies the selected text.
|
||||
copySelection: -> @activeEditSession.copySelectedText()
|
||||
paste: -> @activeEditSession.pasteText()
|
||||
# Public: Pastes the text in the clipboard.
|
||||
#
|
||||
# options - A set of options equivalent to {Selection.insertText}.
|
||||
paste: (options) -> @activeEditSession.pasteText(options)
|
||||
# Public: Undos the last {Buffer} change.
|
||||
undo: -> @activeEditSession.undo()
|
||||
# Public: Redos the last {Buffer} change.
|
||||
redo: -> @activeEditSession.redo()
|
||||
transact: (fn) -> @activeEditSession.transact(fn)
|
||||
commit: -> @activeEditSession.commit()
|
||||
abort: -> @activeEditSession.abort()
|
||||
# Public: Creates a new fold between two row numbers.
|
||||
#
|
||||
# startRow - The row {Number} to start folding at
|
||||
# endRow - The row {Number} to end the fold
|
||||
#
|
||||
# Returns the new {Fold}.
|
||||
createFold: (startRow, endRow) -> @activeEditSession.createFold(startRow, endRow)
|
||||
# Public: Folds the current row.
|
||||
foldCurrentRow: -> @activeEditSession.foldCurrentRow()
|
||||
# Public: Unfolds the current row.
|
||||
unfoldCurrentRow: -> @activeEditSession.unfoldCurrentRow()
|
||||
# Public: Folds all the rows.
|
||||
foldAll: -> @activeEditSession.foldAll()
|
||||
# Public: Unfolds all the rows.
|
||||
unfoldAll: -> @activeEditSession.unfoldAll()
|
||||
# Public: Folds the most recent selection.
|
||||
foldSelection: -> @activeEditSession.foldSelection()
|
||||
# Public: Given the id of a {Fold}, this removes it.
|
||||
#
|
||||
# foldId - The fold id {Number} to remove
|
||||
destroyFold: (foldId) -> @activeEditSession.destroyFold(foldId)
|
||||
# Public: Removes any {Fold}s found that contain the given buffer row.
|
||||
#
|
||||
# bufferRow - The buffer row {Number} to check against
|
||||
destroyFoldsContainingBufferRow: (bufferRow) -> @activeEditSession.destroyFoldsContainingBufferRow(bufferRow)
|
||||
# Public: Determines if the given screen row is folded.
|
||||
#
|
||||
# screenRow - A {Number} indicating the screen row.
|
||||
#
|
||||
# Returns `true` if the screen row is folded, `false` otherwise.
|
||||
isFoldedAtScreenRow: (screenRow) -> @activeEditSession.isFoldedAtScreenRow(screenRow)
|
||||
# Public: Determines if the given buffer row is folded.
|
||||
#
|
||||
# screenRow - A {Number} indicating the buffer row.
|
||||
#
|
||||
# Returns `true` if the buffer row is folded, `false` otherwise.
|
||||
isFoldedAtBufferRow: (bufferRow) -> @activeEditSession.isFoldedAtBufferRow(bufferRow)
|
||||
# Public: Determines if the given row that the cursor is at is folded.
|
||||
#
|
||||
# Returns `true` if the row is folded, `false` otherwise.
|
||||
isFoldedAtCursorRow: -> @activeEditSession.isFoldedAtCursorRow()
|
||||
|
||||
# Public: Gets the line for the given screen row.
|
||||
#
|
||||
# screenRow - A {Number} indicating the screen row.
|
||||
#
|
||||
# Returns a {String}.
|
||||
lineForScreenRow: (screenRow) -> @activeEditSession.lineForScreenRow(screenRow)
|
||||
# Public: Gets the lines for the given screen row boundaries.
|
||||
#
|
||||
# start - A {Number} indicating the beginning screen row.
|
||||
# end - A {Number} indicating the ending screen row.
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
linesForScreenRows: (start, end) -> @activeEditSession.linesForScreenRows(start, end)
|
||||
# Public: Gets the number of screen rows.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
screenLineCount: -> @activeEditSession.screenLineCount()
|
||||
# Public: Defines the limit at which the buffer begins to soft wrap text.
|
||||
#
|
||||
# softWrapColumn - A {Number} defining the soft wrap limit
|
||||
setSoftWrapColumn: (softWrapColumn) ->
|
||||
softWrapColumn ?= @calcSoftWrapColumn()
|
||||
@activeEditSession.setSoftWrapColumn(softWrapColumn) if softWrapColumn
|
||||
|
||||
# Public: Gets the length of the longest screen line.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
maxScreenLineLength: -> @activeEditSession.maxScreenLineLength()
|
||||
# Public: Gets the text in the last screen row.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getLastScreenRow: -> @activeEditSession.getLastScreenRow()
|
||||
# Public: Given a position, this clips it to a real position.
|
||||
#
|
||||
# For example, if `position`'s row exceeds the row count of the buffer,
|
||||
# or if its column goes beyond a line's length, this "sanitizes" the value
|
||||
# to a real position.
|
||||
#
|
||||
# position - The {Point} to clip
|
||||
# options - A hash with the following values:
|
||||
# :wrapBeyondNewlines - if `true`, continues wrapping past newlines
|
||||
# :wrapAtSoftNewlines - if `true`, continues wrapping past soft newlines
|
||||
# :screenLine - if `true`, indicates that you're using a line number, not a row number
|
||||
#
|
||||
# Returns the new, clipped {Point}. Note that this could be the same as `position` if no clipping was performed.
|
||||
clipScreenPosition: (screenPosition, options={}) -> @activeEditSession.clipScreenPosition(screenPosition, options)
|
||||
screenPositionForBufferPosition: (position, options) -> @activeEditSession.screenPositionForBufferPosition(position, options)
|
||||
bufferPositionForScreenPosition: (position, options) -> @activeEditSession.bufferPositionForScreenPosition(position, options)
|
||||
screenRangeForBufferRange: (range) -> @activeEditSession.screenRangeForBufferRange(range)
|
||||
bufferRangeForScreenRange: (range) -> @activeEditSession.bufferRangeForScreenRange(range)
|
||||
bufferRowsForScreenRows: (startRow, endRow) -> @activeEditSession.bufferRowsForScreenRows(startRow, endRow)
|
||||
getLastScreenRow: -> @activeEditSession.getLastScreenRow()
|
||||
|
||||
# Public: Given a buffer position, this converts it into a screen position.
|
||||
#
|
||||
# bufferPosition - An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
# options - The same options available to {DisplayBuffer.screenPositionForBufferPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
screenPositionForBufferPosition: (position, options) -> @activeEditSession.screenPositionForBufferPosition(position, options)
|
||||
|
||||
# Public: Given a buffer range, this converts it into a screen position.
|
||||
#
|
||||
# screenPosition - An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
# options - The same options available to {DisplayBuffer.bufferPositionForScreenPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
bufferPositionForScreenPosition: (position, options) -> @activeEditSession.bufferPositionForScreenPosition(position, options)
|
||||
|
||||
# Public: Given a buffer range, this converts it into a screen position.
|
||||
#
|
||||
# bufferRange - The {Range} to convert
|
||||
#
|
||||
# Returns a {Range}.
|
||||
screenRangeForBufferRange: (range) -> @activeEditSession.screenRangeForBufferRange(range)
|
||||
|
||||
# Public: Given a screen range, this converts it into a buffer position.
|
||||
#
|
||||
# screenRange - The {Range} to convert
|
||||
#
|
||||
# Returns a {Range}.
|
||||
bufferRangeForScreenRange: (range) -> @activeEditSession.bufferRangeForScreenRange(range)
|
||||
# Public: Given a starting and ending row, this converts every row into a buffer position.
|
||||
#
|
||||
# startRow - The row {Number} to start at
|
||||
# endRow - The row {Number} to end at (default: {.getLastScreenRow})
|
||||
#
|
||||
# Returns an {Array} of {Range}s.
|
||||
bufferRowsForScreenRows: (startRow, endRow) -> @activeEditSession.bufferRowsForScreenRows(startRow, endRow)
|
||||
# Public: Gets the number of the last row in the buffer.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getLastScreenRow: -> @activeEditSession.getLastScreenRow()
|
||||
# Internal:
|
||||
logCursorScope: ->
|
||||
console.log @activeEditSession.getCursorScopes()
|
||||
|
||||
# Public: Emulates the "page down" key, where the last row of a buffer scrolls to become the first.
|
||||
pageDown: ->
|
||||
newScrollTop = @scrollTop() + @scrollView[0].clientHeight
|
||||
@activeEditSession.moveCursorDown(@getPageRows())
|
||||
@scrollTop(newScrollTop, adjustVerticalScrollbar: true)
|
||||
|
||||
# Public: Emulates the "page up" key, where the frst row of a buffer scrolls to become the last.
|
||||
pageUp: ->
|
||||
newScrollTop = @scrollTop() - @scrollView[0].clientHeight
|
||||
@activeEditSession.moveCursorUp(@getPageRows())
|
||||
@scrollTop(newScrollTop, adjustVerticalScrollbar: true)
|
||||
|
||||
# Public: Gets the number of actual page rows existing in an editor.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getPageRows: ->
|
||||
Math.max(1, Math.ceil(@scrollView[0].clientHeight / @lineHeight))
|
||||
|
||||
# Public: Set whether invisible characters are shown.
|
||||
#
|
||||
# showInvisibles - A {Boolean} which, if `true`, show invisible characters
|
||||
setShowInvisibles: (showInvisibles) ->
|
||||
return if showInvisibles == @showInvisibles
|
||||
@showInvisibles = showInvisibles
|
||||
@resetDisplay()
|
||||
|
||||
# Public: Defines which characters are invisible.
|
||||
#
|
||||
# invisibles - A hash defining the invisible characters: The defaults are:
|
||||
# :eol - `\u00ac`
|
||||
# :space - `\u00b7`
|
||||
# :tab - `\u00bb`
|
||||
# :cr - `\u00a4`
|
||||
setInvisibles: (@invisibles={}) ->
|
||||
_.defaults @invisibles,
|
||||
eol: '\u00ac'
|
||||
@@ -312,25 +608,80 @@ class Editor extends View
|
||||
cr: '\u00a4'
|
||||
@resetDisplay()
|
||||
|
||||
# Public: Sets whether you want to show the indentation guides.
|
||||
#
|
||||
# showIndentGuide - A {Boolean} you can set to `true` if you want to see the indentation guides.
|
||||
setShowIndentGuide: (showIndentGuide) ->
|
||||
return if showIndentGuide == @showIndentGuide
|
||||
@showIndentGuide = showIndentGuide
|
||||
@resetDisplay()
|
||||
|
||||
# Public: Checks out the current HEAD revision of the file.
|
||||
checkoutHead: -> @getBuffer().checkoutHead()
|
||||
# Public: Replaces the current buffer contents.
|
||||
#
|
||||
# text - A {String} containing the new buffer contents.
|
||||
setText: (text) -> @getBuffer().setText(text)
|
||||
# Public: Retrieves the current buffer contents.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getText: -> @getBuffer().getText()
|
||||
# Public: Retrieves the current buffer's file path.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getPath: -> @activeEditSession?.getPath()
|
||||
# Public: Gets the number of lines in a file.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getLineCount: -> @getBuffer().getLineCount()
|
||||
# Public: Gets the row number of the last line.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getLastBufferRow: -> @getBuffer().getLastRow()
|
||||
# Public: Given a range, returns the lines of text within it.
|
||||
#
|
||||
# range - A {Range} object specifying your points of interest
|
||||
#
|
||||
# Returns a {String} of the combined lines.
|
||||
getTextInRange: (range) -> @getBuffer().getTextInRange(range)
|
||||
# Public: Finds the last point in the current buffer.
|
||||
#
|
||||
# Returns a {Point} representing the last position.
|
||||
getEofPosition: -> @getBuffer().getEofPosition()
|
||||
# Public: Given a row, returns the line of text.
|
||||
#
|
||||
# row - A {Number} indicating the row.
|
||||
#
|
||||
# Returns a {String}.
|
||||
lineForBufferRow: (row) -> @getBuffer().lineForRow(row)
|
||||
# Public: Given a row, returns the length of the line of text.
|
||||
#
|
||||
# row - A {Number} indicating the row
|
||||
#
|
||||
# Returns a {Number}.
|
||||
lineLengthForBufferRow: (row) -> @getBuffer().lineLengthForRow(row)
|
||||
# Public: Given a buffer row, this retrieves the range for that line.
|
||||
#
|
||||
# row - A {Number} identifying the row
|
||||
# options - A hash with one key, `includeNewline`, which specifies whether you
|
||||
# want to include the trailing newline
|
||||
#
|
||||
# Returns a {Range}.
|
||||
rangeForBufferRow: (row) -> @getBuffer().rangeForRow(row)
|
||||
# Public: Scans for text in the buffer, calling a function on each match.
|
||||
#
|
||||
# regex - A {RegExp} representing the text to find
|
||||
# range - A {Range} in the buffer to search within
|
||||
# iterator - A {Function} that's called on each match
|
||||
scanInBufferRange: (args...) -> @getBuffer().scanInRange(args...)
|
||||
# Public: Scans for text in the buffer _backwards_, calling a function on each match.
|
||||
#
|
||||
# regex - A {RegExp} representing the text to find
|
||||
# range - A {Range} in the buffer to search within
|
||||
# iterator - A {Function} that's called on each match
|
||||
backwardsScanInBufferRange: (args...) -> @getBuffer().backwardsScanInRange(args...)
|
||||
|
||||
# Internal:
|
||||
configure: ->
|
||||
@observeConfig 'editor.showLineNumbers', (showLineNumbers) => @gutter.setShowLineNumbers(showLineNumbers)
|
||||
@observeConfig 'editor.showInvisibles', (showInvisibles) => @setShowInvisibles(showInvisibles)
|
||||
@@ -339,6 +690,7 @@ class Editor extends View
|
||||
@observeConfig 'editor.fontSize', (fontSize) => @setFontSize(fontSize)
|
||||
@observeConfig 'editor.fontFamily', (fontFamily) => @setFontFamily(fontFamily)
|
||||
|
||||
# Internal: Responsible for handling events made to the editor.
|
||||
handleEvents: ->
|
||||
@on 'focus', =>
|
||||
@hiddenInput.focus()
|
||||
@@ -410,6 +762,7 @@ class Editor extends View
|
||||
else
|
||||
@gutter.addClass('drop-shadow')
|
||||
|
||||
# Internal:
|
||||
selectOnMousemoveUntilMouseup: ->
|
||||
lastMoveEvent = null
|
||||
moveHandler = (event = lastMoveEvent) =>
|
||||
@@ -428,6 +781,7 @@ class Editor extends View
|
||||
@activeEditSession.finalizeSelections()
|
||||
@syncCursorAnimations()
|
||||
|
||||
# Internal:
|
||||
afterAttach: (onDom) ->
|
||||
return unless onDom
|
||||
@redraw() if @redrawOnReattach
|
||||
@@ -449,6 +803,7 @@ class Editor extends View
|
||||
|
||||
@trigger 'editor:attached', [this]
|
||||
|
||||
# Internal:
|
||||
edit: (editSession) ->
|
||||
return if editSession is @activeEditSession
|
||||
|
||||
@@ -478,14 +833,24 @@ class Editor extends View
|
||||
if @attached and @activeEditSession.buffer.isInConflict()
|
||||
_.defer => @showBufferConflictAlert(@activeEditSession) # Display after editSession has a chance to display
|
||||
|
||||
# Internal: Retrieves the currently active session.
|
||||
#
|
||||
# Returns an {EditSession}.
|
||||
getModel: ->
|
||||
@activeEditSession
|
||||
|
||||
# Internal: Set the new active session.
|
||||
#
|
||||
# editSession - The new {EditSession} to use.
|
||||
setModel: (editSession) ->
|
||||
@edit(editSession)
|
||||
|
||||
# Public: Retrieves the {EditSession}'s buffer.
|
||||
#
|
||||
# Returns the current {Buffer}.
|
||||
getBuffer: -> @activeEditSession.buffer
|
||||
|
||||
# Internal:
|
||||
showBufferConflictAlert: (editSession) ->
|
||||
atom.confirm(
|
||||
editSession.getPath(),
|
||||
@@ -494,6 +859,7 @@ class Editor extends View
|
||||
"Cancel"
|
||||
)
|
||||
|
||||
# Internal:
|
||||
scrollTop: (scrollTop, options={}) ->
|
||||
return @cachedScrollTop or 0 unless scrollTop?
|
||||
maxScrollTop = @verticalScrollbar.prop('scrollHeight') - @verticalScrollbar.height()
|
||||
@@ -510,29 +876,51 @@ class Editor extends View
|
||||
if options?.adjustVerticalScrollbar ? true
|
||||
@verticalScrollbar.scrollTop(scrollTop)
|
||||
|
||||
# Internal:
|
||||
scrollBottom: (scrollBottom) ->
|
||||
if scrollBottom?
|
||||
@scrollTop(scrollBottom - @scrollView.height())
|
||||
else
|
||||
@scrollTop() + @scrollView.height()
|
||||
|
||||
# Public: Scrolls the editor to the bottom.
|
||||
scrollToBottom: ->
|
||||
@scrollBottom(@screenLineCount() * @lineHeight)
|
||||
|
||||
# Public: Scrolls the editor to the position of the most recently added cursor.
|
||||
#
|
||||
# The editor is also centered.
|
||||
scrollToCursorPosition: ->
|
||||
@scrollToBufferPosition(@getCursorBufferPosition(), center: true)
|
||||
|
||||
# Public: Scrolls the editor to the given buffer position.
|
||||
#
|
||||
# bufferPosition - An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
# options - A hash matching the options available to {.scrollToPixelPosition}
|
||||
scrollToBufferPosition: (bufferPosition, options) ->
|
||||
@scrollToPixelPosition(@pixelPositionForBufferPosition(bufferPosition), options)
|
||||
|
||||
# Public: Scrolls the editor to the given screen position.
|
||||
#
|
||||
# screenPosition - An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
# options - A hash matching the options available to {.scrollToPixelPosition}
|
||||
scrollToScreenPosition: (screenPosition, options) ->
|
||||
@scrollToPixelPosition(@pixelPositionForScreenPosition(screenPosition), options)
|
||||
|
||||
# Public: Scrolls the editor to the given pixel position.
|
||||
#
|
||||
# pixelPosition - An object that represents a pixel position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
# options - A hash with the following keys:
|
||||
# :center - if `true`, the position is scrolled such that it's in the center of the editor
|
||||
scrollToPixelPosition: (pixelPosition, options) ->
|
||||
return unless @attached
|
||||
@scrollVertically(pixelPosition, options)
|
||||
@scrollHorizontally(pixelPosition)
|
||||
|
||||
# Internal: Scrolls the editor vertically to a given position.
|
||||
scrollVertically: (pixelPosition, {center}={}) ->
|
||||
scrollViewHeight = @scrollView.height()
|
||||
scrollTop = @scrollTop()
|
||||
@@ -553,6 +941,7 @@ class Editor extends View
|
||||
else if desiredTop < scrollTop
|
||||
@scrollTop(desiredTop)
|
||||
|
||||
# Internal: Scrolls the editor horizontally to a given position.
|
||||
scrollHorizontally: (pixelPosition) ->
|
||||
return if @activeEditSession.getSoftWrap()
|
||||
|
||||
@@ -568,6 +957,11 @@ class Editor extends View
|
||||
else if desiredLeft < @scrollView.scrollLeft()
|
||||
@scrollView.scrollLeft(desiredLeft)
|
||||
|
||||
# Public: Given a buffer range, this highlights all the folds within that range
|
||||
#
|
||||
# "Highlighting" essentially just adds the `selected` class to the line
|
||||
#
|
||||
# bufferRange - The {Range} to check
|
||||
highlightFoldsContainingBufferRange: (bufferRange) ->
|
||||
screenLines = @linesForScreenRows(@firstRenderedScreenRow, @lastRenderedScreenRow)
|
||||
for screenLine, i in screenLines
|
||||
@@ -588,9 +982,11 @@ class Editor extends View
|
||||
@activeEditSession.setScrollTop(@scrollTop())
|
||||
@activeEditSession.setScrollLeft(@scrollView.scrollLeft())
|
||||
|
||||
# Public: Activates soft tabs in the editor.
|
||||
toggleSoftTabs: ->
|
||||
@activeEditSession.setSoftTabs(not @activeEditSession.softTabs)
|
||||
|
||||
# Public: Activates soft wraps in the editor.
|
||||
toggleSoftWrap: ->
|
||||
@setSoftWrap(not @activeEditSession.getSoftWrap())
|
||||
|
||||
@@ -600,6 +996,11 @@ class Editor extends View
|
||||
else
|
||||
Infinity
|
||||
|
||||
# Public: Sets the soft wrap column for the editor.
|
||||
#
|
||||
# softWrap - A {Boolean} which, if `true`, sets soft wraps
|
||||
# softWrapColumn - A {Number} indicating the length of a line in the editor when soft
|
||||
# wrapping turns on
|
||||
setSoftWrap: (softWrap, softWrapColumn=undefined) ->
|
||||
@activeEditSession.setSoftWrap(softWrap)
|
||||
@setSoftWrapColumn(softWrapColumn) if @attached
|
||||
@@ -612,6 +1013,9 @@ class Editor extends View
|
||||
@removeClass 'soft-wrap'
|
||||
$(window).off 'resize', @_setSoftWrapColumn
|
||||
|
||||
# Public: Sets the font size for the editor.
|
||||
#
|
||||
# fontSize - A {Number} indicating the font size in pixels.
|
||||
setFontSize: (fontSize) ->
|
||||
headTag = $("head")
|
||||
styleTag = headTag.find("style.font-size")
|
||||
@@ -626,9 +1030,15 @@ class Editor extends View
|
||||
else
|
||||
@redrawOnReattach = @attached
|
||||
|
||||
# Public: Retrieves the font size for the editor.
|
||||
#
|
||||
# Returns a {Number} indicating the font size in pixels.
|
||||
getFontSize: ->
|
||||
parseInt(@css("font-size"))
|
||||
|
||||
# Public: Sets the font family for the editor.
|
||||
#
|
||||
# fontFamily - A {String} identifying the CSS `font-family`,
|
||||
setFontFamily: (fontFamily) ->
|
||||
return if fontFamily == undefined
|
||||
headTag = $("head")
|
||||
@@ -640,11 +1050,16 @@ class Editor extends View
|
||||
styleTag.text(".editor {font-family: #{fontFamily}}")
|
||||
@redraw()
|
||||
|
||||
# Public: Gets the font family for the editor.
|
||||
#
|
||||
# Returns a {String} identifying the CSS `font-family`,
|
||||
getFontFamily: -> @css("font-family")
|
||||
|
||||
# Public: Clears the CSS `font-family` property from the editor.
|
||||
clearFontFamily: ->
|
||||
$('head style.editor-font-family').remove()
|
||||
|
||||
# Public: Clears the CSS `font-family` property from the editor.
|
||||
redraw: ->
|
||||
return unless @hasParent()
|
||||
return unless @attached
|
||||
@@ -666,6 +1081,9 @@ class Editor extends View
|
||||
splitDown: (items...) ->
|
||||
@getPane()?.splitDown(items...).activeView
|
||||
|
||||
# Public: Retrieve's the `Editor`'s pane.
|
||||
#
|
||||
# Returns a {Pane}.
|
||||
getPane: ->
|
||||
@parent('.item-views').parent('.pane').view()
|
||||
|
||||
@@ -720,6 +1138,10 @@ class Editor extends View
|
||||
appendToLinesView: (view) ->
|
||||
@overlayer.append(view)
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
calculateDimensions: ->
|
||||
fragment = $('<div class="line" style="position: absolute; visibility: hidden;"><span>x</span></div>')
|
||||
@renderedLines.append(fragment)
|
||||
@@ -969,15 +1391,34 @@ class Editor extends View
|
||||
@renderedLines.css('padding-bottom', paddingBottom)
|
||||
@gutter.lineNumbers.css('padding-bottom', paddingBottom)
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
# Public: Retrieves the number of the row that is visible and currently at the top of the editor.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getFirstVisibleScreenRow: ->
|
||||
Math.floor(@scrollTop() / @lineHeight)
|
||||
|
||||
# Public: Retrieves the number of the row that is visible and currently at the top of the editor.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getLastVisibleScreenRow: ->
|
||||
Math.max(0, Math.ceil((@scrollTop() + @scrollView.height()) / @lineHeight) - 1)
|
||||
|
||||
# Public: Given a row number, identifies if it is currently visible.
|
||||
#
|
||||
# row - A row {Number} to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isScreenRowVisible: (row) ->
|
||||
@getFirstVisibleScreenRow() <= row <= @getLastVisibleScreenRow()
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
handleScreenLinesChange: (change) ->
|
||||
@pendingChanges.push(change)
|
||||
@requestDisplayUpdate()
|
||||
@@ -1084,9 +1525,25 @@ class Editor extends View
|
||||
toggleLineCommentsInSelection: ->
|
||||
@activeEditSession.toggleLineCommentsInSelection()
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
# Public: Converts a buffer position to a pixel position.
|
||||
#
|
||||
# position - An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
#
|
||||
# Returns an object with two values: `top` and `left`, representing the pixel positions.
|
||||
pixelPositionForBufferPosition: (position) ->
|
||||
@pixelPositionForScreenPosition(@screenPositionForBufferPosition(position))
|
||||
|
||||
# Public: Converts a screen position to a pixel position.
|
||||
#
|
||||
# position - An object that represents a screen position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
#
|
||||
# Returns an object with two values: `top` and `left`, representing the pixel positions.
|
||||
pixelPositionForScreenPosition: (position) ->
|
||||
return { top: 0, left: 0 } unless @isOnDom() and @isVisible()
|
||||
{row, column} = Point.fromObject(position)
|
||||
@@ -1152,6 +1609,7 @@ class Editor extends View
|
||||
|
||||
new Point(row, column)
|
||||
|
||||
# Public: Highlights the current line the cursor is on.
|
||||
highlightCursorLine: ->
|
||||
return if @mini
|
||||
|
||||
@@ -1162,13 +1620,20 @@ class Editor extends View
|
||||
else
|
||||
@highlightedLine = null
|
||||
|
||||
# Public: Retrieves the current {EditSession}'s grammar.
|
||||
#
|
||||
# Returns a {String} indicating the language's grammar rules.
|
||||
getGrammar: ->
|
||||
@activeEditSession.getGrammar()
|
||||
|
||||
# Public: Sets the current {EditSession}'s grammar. This only works for mini-editors.
|
||||
#
|
||||
# grammar - A {String} indicating the language's grammar rules.
|
||||
setGrammar: (grammar) ->
|
||||
throw new Error("Only mini-editors can explicity set their grammar") unless @mini
|
||||
@activeEditSession.setGrammar(grammar)
|
||||
|
||||
# Public: Reloads the current grammar.
|
||||
reloadGrammar: ->
|
||||
@activeEditSession.reloadGrammar()
|
||||
|
||||
@@ -1179,6 +1644,7 @@ class Editor extends View
|
||||
@on event, =>
|
||||
callback(this, event)
|
||||
|
||||
# Internal: Replaces all the currently selected text.
|
||||
replaceSelectedText: (replaceFn) ->
|
||||
selection = @getSelection()
|
||||
return false if selection.isEmpty()
|
||||
@@ -1189,10 +1655,19 @@ class Editor extends View
|
||||
@insertText(text, select: true)
|
||||
true
|
||||
|
||||
# Public: Copies the current file path to the native clipboard.
|
||||
copyPathToPasteboard: ->
|
||||
path = @getPath()
|
||||
pasteboard.write(path) if path?
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
transact: (fn) -> @activeEditSession.transact(fn)
|
||||
commit: -> @activeEditSession.commit()
|
||||
abort: -> @activeEditSession.abort()
|
||||
|
||||
saveDebugSnapshot: ->
|
||||
atom.showSaveDialog (path) =>
|
||||
fsUtils.write(path, @getDebugSnapshot()) if path
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
_ = require 'underscore'
|
||||
|
||||
# Public: Provides a list of functions that can be used in Atom for event management.
|
||||
#
|
||||
# Each event can have more than one handler; that is, an event can trigger multiple functions.
|
||||
module.exports =
|
||||
# Public: Associates an event name with a function to perform.
|
||||
#
|
||||
# This is called endlessly, until the event is turned {.off}. The {.on} method
|
||||
# calls an `eventName` only once.
|
||||
#
|
||||
# eventName - A {String} name identifying an event
|
||||
# handler - A {Function} that's executed when the event is triggered
|
||||
on: (eventName, handler) ->
|
||||
[eventName, namespace] = eventName.split('.')
|
||||
|
||||
@@ -17,6 +27,10 @@ module.exports =
|
||||
@afterSubscribe?()
|
||||
|
||||
|
||||
# Public: Associates an event name with a function to perform only once.
|
||||
#
|
||||
# eventName - A {String} name identifying an event
|
||||
# handler - A {Function} that's executed when the event is triggered
|
||||
one: (eventName, handler) ->
|
||||
oneShotHandler = (args...) =>
|
||||
@off(eventName, oneShotHandler)
|
||||
@@ -24,6 +38,10 @@ module.exports =
|
||||
|
||||
@on eventName, oneShotHandler
|
||||
|
||||
# Public: Triggers a registered event.
|
||||
#
|
||||
# eventName - A {String} name identifying an event
|
||||
# args - Any additional arguments to pass over to the event `handler`
|
||||
trigger: (eventName, args...) ->
|
||||
if @queuedEvents
|
||||
@queuedEvents.push [eventName, args...]
|
||||
@@ -37,6 +55,10 @@ module.exports =
|
||||
if handlers = @eventHandlersByEventName?[eventName]
|
||||
handlers.forEach (handler) -> handler(args...)
|
||||
|
||||
# Public: Stops executing handlers for a registered event.
|
||||
#
|
||||
# eventName - A {String} name identifying an event
|
||||
# handler - The {Function} to remove from the event. If not provided, all handlers are removed.
|
||||
off: (eventName='', handler) ->
|
||||
[eventName, namespace] = eventName.split('.')
|
||||
eventName = undefined if eventName == ''
|
||||
@@ -67,17 +89,22 @@ module.exports =
|
||||
|
||||
@afterUnsubscribe?() if @subscriptionCount() < subscriptionCountBefore
|
||||
|
||||
# Public: When called, stops triggering any event.
|
||||
pauseEvents: ->
|
||||
@pauseCount ?= 0
|
||||
if @pauseCount++ == 0
|
||||
@queuedEvents ?= []
|
||||
|
||||
# Public: When called, resumes triggering events.
|
||||
resumeEvents: ->
|
||||
if --@pauseCount == 0
|
||||
queuedEvents = @queuedEvents
|
||||
@queuedEvents = null
|
||||
@trigger(event...) for event in queuedEvents
|
||||
|
||||
# Public: Identifies how many events are registered.
|
||||
#
|
||||
# Returns a `number`.
|
||||
subscriptionCount: ->
|
||||
count = 0
|
||||
for name, handlers of @eventHandlersByEventName
|
||||
|
||||
@@ -5,29 +5,55 @@ fsUtils = require 'fs-utils'
|
||||
pathWatcher = require 'pathwatcher'
|
||||
_ = require 'underscore'
|
||||
|
||||
# Public: Represents an individual file in the editor.
|
||||
#
|
||||
# The entry point for this class is in two locations:
|
||||
# * {Buffer}, which associates text contents with a file
|
||||
# * {Directory}, which associcates the children of a directory as files
|
||||
module.exports =
|
||||
class File
|
||||
path: null
|
||||
cachedContents: null
|
||||
|
||||
# Public: Creates a new file.
|
||||
#
|
||||
# path - A {String} representing the file path
|
||||
# symlink - A {Boolean} indicating if the path is a symlink (default: false)
|
||||
constructor: (@path, @symlink=false) ->
|
||||
try
|
||||
if fs.statSync(@path).isDirectory()
|
||||
throw new Error("#{@path} is a directory")
|
||||
|
||||
# Public: Sets the path for the file.
|
||||
#
|
||||
# path - A {String} representing the new file path
|
||||
setPath: (@path) ->
|
||||
|
||||
# Public: Retrieves the path for the file.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getPath: -> @path
|
||||
|
||||
# Public: Gets the file's basename--that is, the file without any directory information.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getBaseName: ->
|
||||
fsUtils.base(@path)
|
||||
|
||||
# Public: Writes (and saves) new contents to the file.
|
||||
#
|
||||
# text - A {String} representing the new contents.
|
||||
write: (text) ->
|
||||
previouslyExisted = @exists()
|
||||
@cachedContents = text
|
||||
fsUtils.write(@getPath(), text)
|
||||
@subscribeToNativeChangeEvents() if not previouslyExisted and @subscriptionCount() > 0
|
||||
|
||||
# Public: Reads the file.
|
||||
#
|
||||
# flushCache - A {Boolean} indicating if the cache should be erased--_i.e._, a force read is performed
|
||||
#
|
||||
# Returns a {String}.
|
||||
read: (flushCache)->
|
||||
if not @exists()
|
||||
@cachedContents = null
|
||||
@@ -36,12 +62,19 @@ class File
|
||||
else
|
||||
@cachedContents
|
||||
|
||||
# Public: Checks to see if a file exists.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
exists: ->
|
||||
fsUtils.exists(@getPath())
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
afterSubscribe: ->
|
||||
@subscribeToNativeChangeEvents() if @exists() and @subscriptionCount() == 1
|
||||
|
||||
|
||||
afterUnsubscribe: ->
|
||||
@unsubscribeFromNativeChangeEvents() if @subscriptionCount() == 0
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
Range = require 'range'
|
||||
Point = require 'point'
|
||||
|
||||
# Public: Represents a fold that's hiding text from the screen.
|
||||
#
|
||||
# Folds are the primary reason that screen ranges and buffer ranges vary. Their
|
||||
# creation is managed by the {DisplayBuffer}.
|
||||
module.exports =
|
||||
class Fold
|
||||
@idCounter: 1
|
||||
@@ -9,6 +13,10 @@ class Fold
|
||||
startRow: null
|
||||
endRow: null
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
constructor: (@displayBuffer, @startRow, @endRow) ->
|
||||
@id = @constructor.idCounter++
|
||||
|
||||
@@ -18,6 +26,11 @@ class Fold
|
||||
inspect: ->
|
||||
"Fold(#{@startRow}, #{@endRow})"
|
||||
|
||||
# Public: Retrieves the buffer row range that a fold occupies.
|
||||
#
|
||||
# includeNewline - A {Boolean} which, if `true`, includes the trailing newline
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getBufferRange: ({includeNewline}={}) ->
|
||||
if includeNewline
|
||||
end = [@endRow + 1, 0]
|
||||
@@ -26,6 +39,9 @@ class Fold
|
||||
|
||||
new Range([@startRow, 0], end)
|
||||
|
||||
# Public: Retrieves the number of buffer rows a fold occupies.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getBufferRowCount: ->
|
||||
@endRow - @startRow + 1
|
||||
|
||||
@@ -43,9 +59,19 @@ class Fold
|
||||
@displayBuffer.unregisterFold(oldStartRow, this)
|
||||
@displayBuffer.registerFold(this)
|
||||
|
||||
# Public: Identifies if a {Range} occurs within a fold.
|
||||
#
|
||||
# range - A {Range} to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isContainedByRange: (range) ->
|
||||
range.start.row <= @startRow and @endRow <= range.end.row
|
||||
|
||||
# Public: Identifies if a fold is nested within a fold.
|
||||
#
|
||||
# fold - A {Fold} to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isContainedByFold: (fold) ->
|
||||
@isContainedByRange(fold.getBufferRange())
|
||||
|
||||
|
||||
@@ -5,8 +5,18 @@ EventEmitter = require 'event-emitter'
|
||||
RepositoryStatusTask = require 'repository-status-task'
|
||||
GitUtils = require 'git-utils'
|
||||
|
||||
# Public: Represents the underlying git operations performed by Atom.
|
||||
#
|
||||
# Ultimately, this is an overlay to the native [git-utils](https://github.com/atom/node-git) model.
|
||||
module.exports =
|
||||
class Git
|
||||
# Public: Creates a new `Git` instance.
|
||||
#
|
||||
# path - The git repository to open
|
||||
# options - A hash with one key:
|
||||
# :refreshOnWindowFocus - A {Boolean} that identifies if the windows should refresh
|
||||
#
|
||||
# Returns a new {Git} object.
|
||||
@open: (path, options) ->
|
||||
return null unless path
|
||||
try
|
||||
@@ -18,6 +28,15 @@ class Git
|
||||
upstream: null
|
||||
statusTask: null
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
# Internal: Creates a new `Git` object.
|
||||
#
|
||||
# path - The {String} representing the path to your git working directory
|
||||
# options - A hash with the following keys:
|
||||
# :refreshOnWindowFocus - If `true`, {#refreshIndex} and {#refreshStatus} are called on focus
|
||||
constructor: (path, options={}) ->
|
||||
@repo = GitUtils.open(path)
|
||||
unless @repo?
|
||||
@@ -40,16 +59,6 @@ class Git
|
||||
@subscribe buffer, 'saved', bufferStatusHandler
|
||||
@subscribe buffer, 'reloaded', bufferStatusHandler
|
||||
|
||||
getRepo: ->
|
||||
unless @repo?
|
||||
throw new Error("Repository has been destroyed")
|
||||
@repo
|
||||
|
||||
refreshIndex: -> @getRepo().refreshIndex()
|
||||
|
||||
getPath: ->
|
||||
@path ?= fsUtils.absolute(@getRepo().getPath())
|
||||
|
||||
destroy: ->
|
||||
if @statusTask?
|
||||
@statusTask.abort()
|
||||
@@ -62,12 +71,46 @@ class Git
|
||||
|
||||
@unsubscribe()
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
# Public: Retrieves the git repository.
|
||||
#
|
||||
# Returns a new `Repository`.
|
||||
getRepo: ->
|
||||
unless @repo?
|
||||
throw new Error("Repository has been destroyed")
|
||||
@repo
|
||||
|
||||
# Public: Reread the index to update any values that have changed since the last time the index was read.
|
||||
refreshIndex: -> @getRepo().refreshIndex()
|
||||
|
||||
# Public: Retrieves the path of the repository.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getPath: ->
|
||||
@path ?= fsUtils.absolute(@getRepo().getPath())
|
||||
|
||||
# Public: Retrieves the working directory of the repository.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getWorkingDirectory: ->
|
||||
@getRepo().getWorkingDirectory()
|
||||
|
||||
# Public: Retrieves the reference or SHA-1 that `HEAD` points to.
|
||||
#
|
||||
# This can be `refs/heads/master`, or a full SHA-1 if the repository is in a detached `HEAD` state.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getHead: ->
|
||||
@getRepo().getHead() ? ''
|
||||
|
||||
# Public: Retrieves the status of a single path in the repository.
|
||||
#
|
||||
# path - An {String} defining a relative path
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getPathStatus: (path) ->
|
||||
currentPathStatus = @statuses[path] ? 0
|
||||
pathStatus = @getRepo().getStatus(@relativize(path)) ? 0
|
||||
@@ -79,38 +122,132 @@ class Git
|
||||
@trigger 'status-changed', path, pathStatus
|
||||
pathStatus
|
||||
|
||||
# Public: Identifies if a path is ignored.
|
||||
#
|
||||
# path - The {String} path to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isPathIgnored: (path) ->
|
||||
@getRepo().isIgnored(@relativize(path))
|
||||
|
||||
# Public: Identifies if a value represents a status code.
|
||||
#
|
||||
# status - The code {Number} to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isStatusModified: (status) ->
|
||||
@getRepo().isStatusModified(status)
|
||||
|
||||
# Public: Identifies if a path was modified.
|
||||
#
|
||||
# path - The {String} path to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isPathModified: (path) ->
|
||||
@isStatusModified(@getPathStatus(path))
|
||||
|
||||
# Public: Identifies if a status code represents a new path.
|
||||
#
|
||||
# status - The code {Number} to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isStatusNew: (status) ->
|
||||
@getRepo().isStatusNew(status)
|
||||
|
||||
# Public: Identifies if a path is new.
|
||||
#
|
||||
# path - The {String} path to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isPathNew: (path) ->
|
||||
@isStatusNew(@getPathStatus(path))
|
||||
|
||||
# Public: Makes a path relative to the repository's working directory.
|
||||
#
|
||||
# path - The {String} path to convert
|
||||
#
|
||||
# Returns a {String}.
|
||||
relativize: (path) ->
|
||||
@getRepo().relativize(path)
|
||||
|
||||
# Public: Retrieves a shortened version of {.getHead}.
|
||||
#
|
||||
# This removes the leading segments of `refs/heads`, `refs/tags`, or `refs/remotes`.
|
||||
# It also shortenes the SHA-1 of a detached `HEAD` to 7 characters.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getShortHead: ->
|
||||
@getRepo().getShortHead()
|
||||
|
||||
# Public: Restore the contents of a path in the working directory and index to the version at `HEAD`.
|
||||
#
|
||||
# This is essentially the same as running:
|
||||
# ```
|
||||
# git reset HEAD -- <path>
|
||||
# git checkout HEAD -- <path>
|
||||
# ```
|
||||
#
|
||||
# path - The {String} path to checkout
|
||||
#
|
||||
# Returns a {Boolean} that's `true` if the method was successful.
|
||||
checkoutHead: (path) ->
|
||||
headCheckedOut = @getRepo().checkoutHead(@relativize(path))
|
||||
@getPathStatus(path) if headCheckedOut
|
||||
headCheckedOut
|
||||
|
||||
# Public: Retrieves the number of lines added and removed to a path.
|
||||
#
|
||||
# This compares the working directory contents of the path to the `HEAD` version.
|
||||
#
|
||||
# path - The {String} path to check
|
||||
#
|
||||
# Returns an object with two keys, `added` and `deleted`. These will always be greater than 0.
|
||||
getDiffStats: (path) ->
|
||||
@getRepo().getDiffStats(@relativize(path))
|
||||
|
||||
# Public: Identifies if a path is a submodule.
|
||||
#
|
||||
# path - The {String} path to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isSubmodule: (path) ->
|
||||
@getRepo().isSubmodule(@relativize(path))
|
||||
|
||||
# Public: Retrieves the status of a directory.
|
||||
#
|
||||
# path - The {String} path to check
|
||||
#
|
||||
# Returns a {Number} representing the status.
|
||||
getDirectoryStatus: (directoryPath) ->
|
||||
directoryPath = "#{directoryPath}/"
|
||||
directoryStatus = 0
|
||||
for path, status of @statuses
|
||||
directoryStatus |= status if path.indexOf(directoryPath) is 0
|
||||
directoryStatus
|
||||
|
||||
# Public: Retrieves the number of commits the `HEAD` branch is ahead/behind the remote branch it is tracking.
|
||||
#
|
||||
# This is similar to the commit numbers reported by `git status` when a remote tracking branch exists.
|
||||
#
|
||||
# Returns an object with two keys, `ahead` and `behind`. These will always be greater than zero.
|
||||
getAheadBehindCounts: ->
|
||||
@getRepo().getAheadBehindCount()
|
||||
|
||||
# Public: Retrieves the line diffs comparing the `HEAD` version of the given path and the given text.
|
||||
#
|
||||
# This is similar to the commit numbers reported by `git status` when a remote tracking branch exists.
|
||||
#
|
||||
# path - The {String} path (relative to the repository)
|
||||
# text - The {String} to compare against the `HEAD` contents
|
||||
#
|
||||
# Returns an object with two keys, `ahead` and `behind`. These will always be greater than zero.
|
||||
getLineDiffs: (path, text) ->
|
||||
@getRepo().getLineDiffs(@relativize(path), text)
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
refreshStatus: ->
|
||||
if @statusTask?
|
||||
@statusTask.off()
|
||||
@@ -122,19 +259,6 @@ class Git
|
||||
@statusTask.one 'task-completed', =>
|
||||
@statusTask = null
|
||||
@statusTask.start()
|
||||
|
||||
getDirectoryStatus: (directoryPath) ->
|
||||
directoryPath = "#{directoryPath}/"
|
||||
directoryStatus = 0
|
||||
for path, status of @statuses
|
||||
directoryStatus |= status if path.indexOf(directoryPath) is 0
|
||||
directoryStatus
|
||||
|
||||
getAheadBehindCounts: ->
|
||||
@getRepo().getAheadBehindCount()
|
||||
|
||||
getLineDiffs: (path, text) ->
|
||||
@getRepo().getLineDiffs(@relativize(path), text)
|
||||
|
||||
|
||||
_.extend Git.prototype, Subscriber
|
||||
_.extend Git.prototype, EventEmitter
|
||||
|
||||
@@ -3,8 +3,16 @@ Range = require 'range'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
|
||||
# Public: Represents the portion of the {Editor} containing row numbers.
|
||||
#
|
||||
# The gutter also indicates if rows are folded.
|
||||
module.exports =
|
||||
class Gutter extends View
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
@content: ->
|
||||
@div class: 'gutter', =>
|
||||
@div outlet: 'lineNumbers', class: 'line-numbers'
|
||||
@@ -21,9 +29,6 @@ class Gutter extends View
|
||||
@getEditor().on 'selection:changed', highlightLines
|
||||
@on 'mousedown', (e) => @handleMouseEvents(e)
|
||||
|
||||
getEditor: ->
|
||||
@parentView
|
||||
|
||||
beforeRemove: ->
|
||||
$(document).off(".gutter-#{@getEditor().id}")
|
||||
|
||||
@@ -45,9 +50,26 @@ class Gutter extends View
|
||||
$(document).on "mousemove.gutter-#{@getEditor().id}", moveHandler
|
||||
$(document).one "mouseup.gutter-#{@getEditor().id}", => $(document).off 'mousemove', moveHandler
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
# Public: Retrieves the containing {Editor}.
|
||||
#
|
||||
# Returns an {Editor}.
|
||||
getEditor: ->
|
||||
@parentView
|
||||
|
||||
# Public: Defines whether to show the gutter or not.
|
||||
#
|
||||
# showLineNumbers - A {Boolean} which, if `false`, hides the gutter
|
||||
setShowLineNumbers: (showLineNumbers) ->
|
||||
if showLineNumbers then @lineNumbers.show() else @lineNumbers.hide()
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
updateLineNumbers: (changes, renderFrom, renderTo) ->
|
||||
if renderFrom < @firstScreenRow or renderTo > @lastScreenRow
|
||||
performUpdate = true
|
||||
@@ -60,7 +82,7 @@ class Gutter extends View
|
||||
break
|
||||
|
||||
@renderLineNumbers(renderFrom, renderTo) if performUpdate
|
||||
|
||||
|
||||
renderLineNumbers: (startScreenRow, endScreenRow) ->
|
||||
editor = @getEditor()
|
||||
maxDigits = editor.getLineCount().toString().length
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
fsUtils = require 'fs-utils'
|
||||
_ = require 'underscore'
|
||||
|
||||
# Public: Manages the states between {Editor}s, images, and the project as a whole.
|
||||
#
|
||||
# Essentially, the graphical version of a {EditSession}.
|
||||
module.exports=
|
||||
class ImageEditSession
|
||||
registerDeserializer(this)
|
||||
|
||||
# Public: Identifies if a path can be opened by the image viewer.
|
||||
#
|
||||
# path - The {String} name of the path to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
@canOpen: (path) ->
|
||||
_.indexOf([
|
||||
'.gif'
|
||||
@@ -13,6 +21,10 @@ class ImageEditSession
|
||||
'.png'
|
||||
], fsUtils.extension(path), true) >= 0
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
@deserialize: (state) ->
|
||||
if fsUtils.exists(state.path)
|
||||
project.buildEditSession(state.path)
|
||||
@@ -28,15 +40,31 @@ class ImageEditSession
|
||||
getViewClass: ->
|
||||
require 'image-view'
|
||||
|
||||
# Public: Retrieves the filename of the open file.
|
||||
#
|
||||
# This is `'untitled'` if the file is new and not saved to the disk.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getTitle: ->
|
||||
if path = @getPath()
|
||||
fsUtils.base(path)
|
||||
else
|
||||
'untitled'
|
||||
|
||||
# Public: Retrieves the URI of the current image.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getUri: -> @path
|
||||
|
||||
# Public: Retrieves the path of the current image.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getPath: -> @path
|
||||
|
||||
# Public: Compares two `ImageEditSession`s to determine equality.
|
||||
#
|
||||
# Equality is based on the condition that the two URIs are the same.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isEqual: (other) ->
|
||||
other instanceof ImageEditSession and @getUri() is other.getUri()
|
||||
|
||||
@@ -2,12 +2,16 @@ ScrollView = require 'scroll-view'
|
||||
_ = require 'underscore'
|
||||
$ = require 'jquery'
|
||||
|
||||
# Public: Renders images in the {Editor}.
|
||||
module.exports =
|
||||
class ImageView extends ScrollView
|
||||
|
||||
# Internal:
|
||||
@content: ->
|
||||
@div class: 'image-view', tabindex: -1, =>
|
||||
@img outlet: 'image'
|
||||
|
||||
# Internal:
|
||||
initialize: (imageEditSession) ->
|
||||
super
|
||||
|
||||
@@ -25,6 +29,7 @@ class ImageView extends ScrollView
|
||||
@command 'image-view:zoom-out', => @zoomOut()
|
||||
@command 'image-view:reset-zoom', => @resetZoom()
|
||||
|
||||
# Internal:
|
||||
afterAttach: (onDom) ->
|
||||
return unless onDom
|
||||
|
||||
@@ -35,6 +40,7 @@ class ImageView extends ScrollView
|
||||
@active = @is(pane.activeView)
|
||||
@centerImage() if @active and not wasActive
|
||||
|
||||
# Public: Places the image in the center of the {Editor}.
|
||||
centerImage: ->
|
||||
return unless @loaded and @isVisible()
|
||||
|
||||
@@ -43,6 +49,9 @@ class ImageView extends ScrollView
|
||||
'left': Math.max((@width() - @image.outerWidth()) / 2, 0)
|
||||
@image.show()
|
||||
|
||||
# Public: Indicates the path of the image.
|
||||
#
|
||||
# path - A {String} for the new image path.
|
||||
setPath: (path) ->
|
||||
if path?
|
||||
if @image.attr('src') isnt path
|
||||
@@ -51,12 +60,36 @@ class ImageView extends ScrollView
|
||||
else
|
||||
@image.hide()
|
||||
|
||||
setModel: (imageEditSession) ->
|
||||
@setPath(imageEditSession?.getPath())
|
||||
|
||||
# Public: Retrieve's the {Editor}'s pane.
|
||||
#
|
||||
# Returns a {Pane}.
|
||||
getPane: ->
|
||||
@parent('.item-views').parent('.pane').view()
|
||||
|
||||
# Public: Zooms the image out.
|
||||
#
|
||||
# This is done by a factor of `0.9`.
|
||||
zoomOut: ->
|
||||
@adjustSize(0.9)
|
||||
|
||||
# Public: Zooms the image in.
|
||||
#
|
||||
# This is done by a factor of `1.1`.
|
||||
zoomIn: ->
|
||||
@adjustSize(1.1)
|
||||
|
||||
# Public: Zooms the image to its normal width and height.
|
||||
resetZoom: ->
|
||||
return unless @loaded and @isVisible()
|
||||
|
||||
@image.width(@originalWidth)
|
||||
@image.height(@originalHeight)
|
||||
@centerImage()
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
adjustSize: (factor) ->
|
||||
return unless @loaded and @isVisible()
|
||||
|
||||
@@ -66,15 +99,5 @@ class ImageView extends ScrollView
|
||||
@image.height(newHeight)
|
||||
@centerImage()
|
||||
|
||||
zoomOut: ->
|
||||
@adjustSize(0.9)
|
||||
|
||||
zoomIn: ->
|
||||
@adjustSize(1.1)
|
||||
|
||||
resetZoom: ->
|
||||
return unless @loaded and @isVisible()
|
||||
|
||||
@image.width(@originalWidth)
|
||||
@image.height(@originalHeight)
|
||||
@centerImage()
|
||||
setModel: (imageEditSession) ->
|
||||
@setPath(imageEditSession?.getPath())
|
||||
@@ -5,6 +5,19 @@ CSON = require 'cson'
|
||||
|
||||
BindingSet = require 'binding-set'
|
||||
|
||||
# Internal: Associates keymaps with actions.
|
||||
#
|
||||
# Keymaps are defined in a CSON format. A typical keymap looks something like this:
|
||||
#
|
||||
# ```cson
|
||||
# 'body':
|
||||
# 'ctrl-l': 'package:do-something'
|
||||
#'.someClass':
|
||||
# 'enter': 'package:confirm'
|
||||
# ```
|
||||
#
|
||||
# As a key, you define the DOM element you want to work on, using CSS notation. For that
|
||||
# key, you define one or more key:value pairs, associating keystrokes with a command to execute.
|
||||
module.exports =
|
||||
class Keymap
|
||||
bindingSets: null
|
||||
@@ -39,23 +52,23 @@ class Keymap
|
||||
|
||||
loadDirectory: (directoryPath) ->
|
||||
@load(filePath) for filePath in fsUtils.list(directoryPath, ['.cson', '.json'])
|
||||
|
||||
|
||||
load: (path) ->
|
||||
@add(path, CSON.readObject(path))
|
||||
|
||||
|
||||
add: (args...) ->
|
||||
name = args.shift() if args.length > 1
|
||||
keymap = args.shift()
|
||||
for selector, bindings of keymap
|
||||
@bindKeys(name, selector, bindings)
|
||||
|
||||
|
||||
remove: (name) ->
|
||||
for bindingSet in @bindingSets.filter((bindingSet) -> bindingSet.name is name)
|
||||
_.remove(@bindingSets, bindingSet)
|
||||
for keystrokes of bindingSet.commandsByKeystrokes
|
||||
keystroke = keystrokes.split(' ')[0]
|
||||
_.remove(@bindingSetsByFirstKeystroke[keystroke], bindingSet)
|
||||
|
||||
|
||||
bindKeys: (args...) ->
|
||||
name = args.shift() if args.length > 2
|
||||
[selector, bindings] = args
|
||||
@@ -65,7 +78,7 @@ class Keymap
|
||||
keystroke = keystrokes.split(' ')[0] # only index by first keystroke
|
||||
@bindingSetsByFirstKeystroke[keystroke] ?= []
|
||||
@bindingSetsByFirstKeystroke[keystroke].push(bindingSet)
|
||||
|
||||
|
||||
unbindKeys: (selector, bindings) ->
|
||||
bindingSet = _.detect @bindingSets, (bindingSet) ->
|
||||
bindingSet.selector is selector and bindingSet.bindings is bindings
|
||||
@@ -73,7 +86,7 @@ class Keymap
|
||||
if bindingSet
|
||||
console.log "binding set", bindingSet
|
||||
_.remove(@bindingSets, bindingSet)
|
||||
|
||||
|
||||
bindingsForElement: (element) ->
|
||||
keystrokeMap = {}
|
||||
currentNode = $(element)
|
||||
|
||||
@@ -5,6 +5,10 @@ require 'underscore-extensions'
|
||||
EventEmitter = require 'event-emitter'
|
||||
Subscriber = require 'subscriber'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class LanguageMode
|
||||
buffer = null
|
||||
@@ -12,13 +16,17 @@ class LanguageMode
|
||||
editSession = null
|
||||
currentGrammarScore: null
|
||||
|
||||
# Public: Sets up a `LanguageMode` for the given {EditSession}.
|
||||
#
|
||||
# editSession - The {EditSession} to associate with
|
||||
constructor: (@editSession) ->
|
||||
@buffer = @editSession.buffer
|
||||
@reloadGrammar()
|
||||
@subscribe syntax, 'grammar-added', (grammar) =>
|
||||
newScore = grammar.getScore(@buffer.getPath(), @buffer.getText())
|
||||
@setGrammar(grammar, newScore) if newScore > @currentGrammarScore
|
||||
|
||||
|
||||
# Internal:
|
||||
destroy: ->
|
||||
@unsubscribe()
|
||||
|
||||
@@ -34,6 +42,14 @@ class LanguageMode
|
||||
else
|
||||
throw new Error("No grammar found for path: #{path}")
|
||||
|
||||
# Public: Wraps the lines between two rows in comments.
|
||||
#
|
||||
# If the language doesn't have comment, nothing happens.
|
||||
#
|
||||
# startRow - The row {Number} to start at
|
||||
# endRow - The row {Number} to end at
|
||||
#
|
||||
# Returns an {Array} of the commented {Ranges}.
|
||||
toggleLineCommentsForBufferRows: (start, end) ->
|
||||
scopes = @editSession.scopesForBufferPosition([start, 0])
|
||||
return unless commentStartString = syntax.getProperty(scopes, "editor.commentStart")
|
||||
@@ -96,6 +112,13 @@ class LanguageMode
|
||||
|
||||
[bufferRow, foldEndRow]
|
||||
|
||||
# Public: Given a buffer row, this returns a suggested indentation level.
|
||||
#
|
||||
# The indentation level provided is based on the current {LanguageMode}.
|
||||
#
|
||||
# bufferRow - A {Number} indicating the buffer row
|
||||
#
|
||||
# Returns a {Number}.
|
||||
suggestedIndentForBufferRow: (bufferRow) ->
|
||||
currentIndentLevel = @editSession.indentationForBufferRow(bufferRow)
|
||||
scopes = @editSession.scopesForBufferPosition([bufferRow, 0])
|
||||
@@ -115,13 +138,23 @@ class LanguageMode
|
||||
|
||||
Math.max(desiredIndentLevel, currentIndentLevel)
|
||||
|
||||
# Public: Indents all the rows between two buffer row numbers.
|
||||
#
|
||||
# startRow - The row {Number} to start at
|
||||
# endRow - The row {Number} to end at
|
||||
autoIndentBufferRows: (startRow, endRow) ->
|
||||
@autoIndentBufferRow(row) for row in [startRow..endRow]
|
||||
|
||||
# Public: Given a buffer row, this indents it.
|
||||
#
|
||||
# bufferRow - The row {Number}
|
||||
autoIndentBufferRow: (bufferRow) ->
|
||||
@autoIncreaseIndentForBufferRow(bufferRow)
|
||||
@autoDecreaseIndentForBufferRow(bufferRow)
|
||||
|
||||
# Public: Given a buffer row, this increases the indentation.
|
||||
#
|
||||
# bufferRow - The row {Number}
|
||||
autoIncreaseIndentForBufferRow: (bufferRow) ->
|
||||
precedingRow = @buffer.previousNonBlankRow(bufferRow)
|
||||
return unless precedingRow?
|
||||
@@ -137,6 +170,9 @@ class LanguageMode
|
||||
if desiredIndentLevel > currentIndentLevel
|
||||
@editSession.setIndentationForBufferRow(bufferRow, desiredIndentLevel)
|
||||
|
||||
# Public: Given a buffer row, this decreases the indentation.
|
||||
#
|
||||
# bufferRow - The row {Number}
|
||||
autoDecreaseIndentForBufferRow: (bufferRow) ->
|
||||
scopes = @editSession.scopesForBufferPosition([bufferRow, 0])
|
||||
increaseIndentRegex = @increaseIndentRegexForScopes(scopes)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
Point = require 'point'
|
||||
Range = require 'range'
|
||||
|
||||
# Internal: Responsible for doing the translations between screen positions and buffer positions.
|
||||
module.exports =
|
||||
class LineMap
|
||||
maxScreenLineLength: 0
|
||||
@@ -25,13 +26,30 @@ class LineMap
|
||||
|
||||
for screenLine in maxLengthCandidates
|
||||
@maxScreenLineLength = Math.max(@maxScreenLineLength, screenLine.text.length)
|
||||
|
||||
|
||||
# Public: Gets the line for the given screen row.
|
||||
#
|
||||
# screenRow - A {Number} indicating the screen row.
|
||||
#
|
||||
# Returns a {String}.
|
||||
lineForScreenRow: (row) ->
|
||||
@screenLines[row]
|
||||
|
||||
# Public: Gets the lines for the given screen row boundaries.
|
||||
#
|
||||
# start - A {Number} indicating the beginning screen row.
|
||||
# end - A {Number} indicating the ending screen row.
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
linesForScreenRows: (startRow, endRow) ->
|
||||
@screenLines[startRow..endRow]
|
||||
|
||||
|
||||
# Public: Given a starting and ending row, this converts every row into a buffer position.
|
||||
#
|
||||
# startRow - The row {Number} to start at
|
||||
# endRow - The row {Number} to end at (default: {.lastScreenRow})
|
||||
#
|
||||
# Returns an {Array} of {Range}s.
|
||||
bufferRowsForScreenRows: (startRow, endRow=@lastScreenRow()) ->
|
||||
bufferRows = []
|
||||
bufferRow = 0
|
||||
@@ -44,7 +62,10 @@ class LineMap
|
||||
|
||||
screenLineCount: ->
|
||||
@screenLines.length
|
||||
|
||||
|
||||
# Retrieves the last row number in the buffer.
|
||||
#
|
||||
# Returns an {Integer}.
|
||||
lastScreenRow: ->
|
||||
@screenLineCount() - 1
|
||||
|
||||
@@ -76,7 +97,14 @@ class LineMap
|
||||
else
|
||||
column = screenLine.clipScreenColumn(column, options)
|
||||
new Point(row, column)
|
||||
|
||||
|
||||
# Public: Given a buffer position, this converts it into a screen position.
|
||||
#
|
||||
# bufferPosition - An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
# options - The same options available to {.clipScreenPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
screenPositionForBufferPosition: (bufferPosition, options={}) ->
|
||||
{ row, column } = Point.fromObject(bufferPosition)
|
||||
[screenRow, screenLines] = @screenRowAndScreenLinesForBufferRow(row)
|
||||
@@ -109,9 +137,15 @@ class LineMap
|
||||
currentBufferRow = nextBufferRow
|
||||
|
||||
[screenRow, screenLines]
|
||||
|
||||
# Public: Given a buffer range, this converts it into a screen position.
|
||||
#
|
||||
# screenPosition - An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
# options - The same options available to {.clipScreenPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
bufferPositionForScreenPosition: (screenPosition, options) ->
|
||||
{ row, column } = @clipScreenPosition(Point.fromObject(screenPosition))
|
||||
{ row, column } = @clipScreenPosition(Point.fromObject(screenPosition), options)
|
||||
[bufferRow, screenLine] = @bufferRowAndScreenLineForScreenRow(row)
|
||||
bufferColumn = screenLine.bufferColumnForScreenColumn(column)
|
||||
new Point(bufferRow, bufferColumn)
|
||||
@@ -125,19 +159,29 @@ class LineMap
|
||||
bufferRow += screenLine.bufferRows
|
||||
|
||||
[bufferRow, screenLine]
|
||||
|
||||
|
||||
# Public: Given a buffer range, this converts it into a screen position.
|
||||
#
|
||||
# bufferRange - The {Range} to convert
|
||||
#
|
||||
# Returns a {Range}.
|
||||
screenRangeForBufferRange: (bufferRange) ->
|
||||
bufferRange = Range.fromObject(bufferRange)
|
||||
start = @screenPositionForBufferPosition(bufferRange.start)
|
||||
end = @screenPositionForBufferPosition(bufferRange.end)
|
||||
new Range(start, end)
|
||||
|
||||
# Public: Given a screen range, this converts it into a buffer position.
|
||||
#
|
||||
# screenRange - The {Range} to convert
|
||||
#
|
||||
# Returns a {Range}.
|
||||
bufferRangeForScreenRange: (screenRange) ->
|
||||
screenRange = Range.fromObject(screenRange)
|
||||
start = @bufferPositionForScreenPosition(screenRange.start)
|
||||
end = @bufferPositionForScreenPosition(screenRange.end)
|
||||
new Range(start, end)
|
||||
|
||||
# Internal:
|
||||
logLines: (start=0, end=@screenLineCount() - 1)->
|
||||
for row in [start..end]
|
||||
line = @lineForScreenRow(row).text
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
Token = require 'token'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
module.exports =
|
||||
class NullGrammar
|
||||
name: 'Null Grammar'
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
fsUtils = require 'fs-utils'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
module.exports =
|
||||
class Package
|
||||
@build: (path) ->
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
$ = require 'jquery'
|
||||
{View} = require 'space-pen'
|
||||
|
||||
# Internal:
|
||||
module.exports =
|
||||
class PaneAxis extends View
|
||||
|
||||
@deserialize: ({children}) ->
|
||||
childViews = children.map (child) -> deserialize(child)
|
||||
new this(childViews)
|
||||
|
||||
@@ -2,8 +2,10 @@ $ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
PaneAxis = require 'pane-axis'
|
||||
|
||||
# Internal:
|
||||
module.exports =
|
||||
class PaneColumn extends PaneAxis
|
||||
|
||||
@content: ->
|
||||
@div class: 'column'
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ module.exports =
|
||||
class PaneContainer extends View
|
||||
registerDeserializer(this)
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
@deserialize: ({root}) ->
|
||||
container = new PaneContainer
|
||||
container.append(deserialize(root)) if root
|
||||
@@ -21,6 +25,10 @@ class PaneContainer extends View
|
||||
serialize: ->
|
||||
deserializer: 'PaneContainer'
|
||||
root: @getRoot()?.serialize()
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
focusNextPane: ->
|
||||
panes = @getPanes()
|
||||
|
||||
@@ -2,6 +2,10 @@ $ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
PaneAxis = require 'pane-axis'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class PaneRow extends PaneAxis
|
||||
@content: ->
|
||||
|
||||
@@ -6,6 +6,11 @@ PaneColumn = require 'pane-column'
|
||||
|
||||
module.exports =
|
||||
class Pane extends View
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
@content: (wrappedView) ->
|
||||
@div class: 'pane', =>
|
||||
@div class: 'item-views', outlet: 'itemViews'
|
||||
@@ -60,6 +65,10 @@ class Pane extends View
|
||||
@attached = true
|
||||
@trigger 'pane:attached', [this]
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
makeActive: ->
|
||||
for pane in @getContainer().getPanes() when pane isnt this
|
||||
pane.makeInactive()
|
||||
|
||||
@@ -1,17 +1,30 @@
|
||||
crypto = require 'crypto'
|
||||
|
||||
# Internal: Represents the clipboard used for copying and pasting in Atom.
|
||||
module.exports =
|
||||
class Pasteboard
|
||||
signatureForMetadata: null
|
||||
|
||||
# Internal: Creates an `md5` hash of some text.
|
||||
#
|
||||
# text - A {String} to encrypt.
|
||||
#
|
||||
# Returns an encrypted {String}.
|
||||
md5: (text) ->
|
||||
crypto.createHash('md5').update(text, 'utf8').digest('hex')
|
||||
|
||||
# Saves from the clipboard.
|
||||
#
|
||||
# text - A {String} to store
|
||||
# metadata - An object of additional info to associate with the text
|
||||
write: (text, metadata) ->
|
||||
@signatureForMetadata = @md5(text)
|
||||
@metadata = metadata
|
||||
$native.writeToPasteboard(text)
|
||||
|
||||
# Loads from the clipboard.
|
||||
#
|
||||
# Returns an {Array}. The first index is the saved text, and the second is any metadata associated with the text.
|
||||
read: ->
|
||||
text = $native.readFromPasteboard()
|
||||
value = [text]
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
_ = require 'underscore'
|
||||
|
||||
# Public: Represents a coordinate in the editor.
|
||||
#
|
||||
# Each `Point` is actually an object with two properties: `row` and `column`.
|
||||
module.exports =
|
||||
class Point
|
||||
|
||||
# Public: Constructs a `Point` from a given object.
|
||||
#
|
||||
# object - This can be an {Array} (`[startRow, startColumn, endRow, endColumn]`) or an object `{row, column}`
|
||||
#
|
||||
# Returns the new {Point}.
|
||||
@fromObject: (object) ->
|
||||
if object instanceof Point
|
||||
object
|
||||
@@ -13,6 +22,15 @@ class Point
|
||||
|
||||
new Point(row, column)
|
||||
|
||||
# Public: Identifies which `Point` is smaller.
|
||||
#
|
||||
# "Smaller" means that both the `row` and `column` values of one `Point` are less than or equal
|
||||
# to the other.
|
||||
#
|
||||
# point1 - The first {Point} to check
|
||||
# point2 - The second {Point} to check
|
||||
#
|
||||
# Returns the smaller {Point}.
|
||||
@min: (point1, point2) ->
|
||||
point1 = @fromObject(point1)
|
||||
point2 = @fromObject(point2)
|
||||
@@ -21,11 +39,25 @@ class Point
|
||||
else
|
||||
point2
|
||||
|
||||
# Public: Creates a new `Point` object.
|
||||
#
|
||||
# row - A {Number} indicating the row (default: 0)
|
||||
# column - A {Number} indicating the column (default: 0)
|
||||
#
|
||||
# Returns a {Point},
|
||||
constructor: (@row=0, @column=0) ->
|
||||
|
||||
# Public: Creates an identical copy of the `Point`.
|
||||
#
|
||||
# Returns a duplicate {Point}.
|
||||
copy: ->
|
||||
new Point(@row, @column)
|
||||
|
||||
# Public: Adds the `column`s of two `Point`s together.
|
||||
#
|
||||
# other - The {Point} to add with
|
||||
#
|
||||
# Returns the new {Point}.
|
||||
add: (other) ->
|
||||
other = Point.fromObject(other)
|
||||
row = @row + other.row
|
||||
@@ -36,10 +68,24 @@ class Point
|
||||
|
||||
new Point(row, column)
|
||||
|
||||
# Public: Moves a `Point`.
|
||||
#
|
||||
# In other words, the `row` values and `column` values are added to each other.
|
||||
#
|
||||
# other - The {Point} to add with
|
||||
#
|
||||
# Returns the new {Point}.
|
||||
translate: (other) ->
|
||||
other = Point.fromObject(other)
|
||||
new Point(@row + other.row, @column + other.column)
|
||||
|
||||
# Public: Creates two new `Point`s, split down a `column` value.
|
||||
#
|
||||
# In other words, given a point, this creates `Point(0, column)` and `Point(row, column)`.
|
||||
#
|
||||
# column - The {Number} to split at
|
||||
#
|
||||
# Returns an {Array} of two {Point}s.
|
||||
splitAt: (column) ->
|
||||
if @row == 0
|
||||
rightColumn = @column - column
|
||||
@@ -48,6 +94,17 @@ class Point
|
||||
|
||||
[new Point(0, column), new Point(@row, rightColumn)]
|
||||
|
||||
# Internal: Compares two `Point`s.
|
||||
#
|
||||
# other - The {Point} to compare against
|
||||
#
|
||||
# Returns a {Number} matching the following rules:
|
||||
# * If the first `row` is greater than `other.row`, returns `1`.
|
||||
# * If the first `row` is less than `other.row`, returns `-1`.
|
||||
# * If the first `column` is greater than `other.column`, returns `1`.
|
||||
# * If the first `column` is less than `other.column`, returns `-1`.
|
||||
#
|
||||
# Otherwise, returns `0`.
|
||||
compare: (other) ->
|
||||
if @row > other.row
|
||||
1
|
||||
@@ -61,31 +118,67 @@ class Point
|
||||
else
|
||||
0
|
||||
|
||||
# Public: Identifies if two `Point`s are equal.
|
||||
#
|
||||
# other - The {Point} to compare against
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isEqual: (other) ->
|
||||
return false unless other
|
||||
other = Point.fromObject(other)
|
||||
@row == other.row and @column == other.column
|
||||
|
||||
# Public: Identifies if one `Point` is less than another.
|
||||
#
|
||||
# other - The {Point} to compare against
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isLessThan: (other) ->
|
||||
@compare(other) < 0
|
||||
|
||||
# Public: Identifies if one `Point` is less than or equal to another.
|
||||
#
|
||||
# other - The {Point} to compare against
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isLessThanOrEqual: (other) ->
|
||||
@compare(other) <= 0
|
||||
|
||||
# Public: Identifies if one `Point` is greater than another.
|
||||
#
|
||||
# other - The {Point} to compare against
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isGreaterThan: (other) ->
|
||||
@compare(other) > 0
|
||||
|
||||
# Public: Identifies if one `Point` is greater than or equal to another.
|
||||
#
|
||||
# other - The {Point} to compare against
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isGreaterThanOrEqual: (other) ->
|
||||
@compare(other) >= 0
|
||||
|
||||
inspect: ->
|
||||
"(#{@row}, #{@column})"
|
||||
|
||||
# Public: Converts the {Point} to a String.
|
||||
#
|
||||
# Returns a {String}.
|
||||
toString: ->
|
||||
"#{@row},#{@column}"
|
||||
|
||||
# Public: Converts the {Point} to an Array.
|
||||
#
|
||||
# Returns an {Array}.
|
||||
toArray: ->
|
||||
[@row, @column]
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
inspect: ->
|
||||
"(#{@row}, #{@column})"
|
||||
|
||||
# Internal:
|
||||
serialize: ->
|
||||
@toArray()
|
||||
|
||||
@@ -9,13 +9,14 @@ EventEmitter = require 'event-emitter'
|
||||
Directory = require 'directory'
|
||||
BufferedProcess = require 'buffered-process'
|
||||
|
||||
# Public: Represents a project that's opened in Atom.
|
||||
#
|
||||
# Ultimately, a project is a git directory that's been opened. It's a collection
|
||||
# of directories and files that you can operate on.
|
||||
module.exports =
|
||||
class Project
|
||||
registerDeserializer(this)
|
||||
|
||||
@deserialize: (state) ->
|
||||
new Project(state.path)
|
||||
|
||||
tabLength: 2
|
||||
softTabs: true
|
||||
softWrap: false
|
||||
@@ -23,21 +24,41 @@ class Project
|
||||
editSessions: null
|
||||
ignoredPathRegexes: null
|
||||
|
||||
# Public: Establishes a new project at a given path.
|
||||
#
|
||||
# path - The {String} name of the path
|
||||
constructor: (path) ->
|
||||
@setPath(path)
|
||||
@editSessions = []
|
||||
@buffers = []
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
serialize: ->
|
||||
deserializer: 'Project'
|
||||
path: @getPath()
|
||||
|
||||
@deserialize: (state) ->
|
||||
new Project(state.path)
|
||||
|
||||
destroy: ->
|
||||
editSession.destroy() for editSession in @getEditSessions()
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
# Public: Retrieves the project path.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getPath: ->
|
||||
@rootDirectory?.path
|
||||
|
||||
# Public: Sets the project path.
|
||||
#
|
||||
# path - A {String} representing the new path
|
||||
setPath: (path) ->
|
||||
@rootDirectory?.off()
|
||||
|
||||
@@ -49,9 +70,15 @@ class Project
|
||||
|
||||
@trigger "path-changed"
|
||||
|
||||
# Public: Retrieves the name of the root directory.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getRootDirectory: ->
|
||||
@rootDirectory
|
||||
|
||||
# Public: Retrieves the names of every file (that's not `git ignore`d) in the project.
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
getFilePaths: ->
|
||||
deferred = $.Deferred()
|
||||
paths = []
|
||||
@@ -61,6 +88,11 @@ class Project
|
||||
deferred.resolve(paths)
|
||||
deferred.promise()
|
||||
|
||||
# Public: Identifies if a path is ignored.
|
||||
#
|
||||
# path - The {String} name of the path to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isPathIgnored: (path) ->
|
||||
for segment in path.split("/")
|
||||
ignoredNames = config.get("core.ignoredNames") or []
|
||||
@@ -68,29 +100,74 @@ class Project
|
||||
|
||||
@ignoreRepositoryPath(path)
|
||||
|
||||
# Public: Identifies if a path is ignored.
|
||||
#
|
||||
# path - The {String} name of the path to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
ignoreRepositoryPath: (path) ->
|
||||
config.get("core.hideGitIgnoredFiles") and git?.isPathIgnored(fsUtils.join(@getPath(), path))
|
||||
|
||||
# Public: Given a path, this resolves it relative to the project directory.
|
||||
#
|
||||
# filePath - The {String} name of the path to convert
|
||||
#
|
||||
# Returns a {String}.
|
||||
resolve: (filePath) ->
|
||||
filePath = fsUtils.join(@getPath(), filePath) unless filePath[0] == '/'
|
||||
fsUtils.absolute filePath
|
||||
|
||||
# Public: Given a path, this makes it relative to the project directory.
|
||||
#
|
||||
# filePath - The {String} name of the path to convert
|
||||
#
|
||||
# Returns a {String}.
|
||||
relativize: (fullPath) ->
|
||||
return fullPath unless fullPath.lastIndexOf(@getPath()) is 0
|
||||
fullPath.replace(@getPath(), "").replace(/^\//, '')
|
||||
|
||||
# Public: Identifies if the project is using soft tabs.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
getSoftTabs: -> @softTabs
|
||||
|
||||
# Public: Sets the project to use soft tabs.
|
||||
#
|
||||
# softTabs - A {Boolean} which, if `true`, sets soft tabs
|
||||
setSoftTabs: (@softTabs) ->
|
||||
|
||||
# Public: Identifies if the project is using soft wrapping.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
getSoftWrap: -> @softWrap
|
||||
|
||||
# Public: Sets the project to use soft wrapping.
|
||||
#
|
||||
# softTabs - A {Boolean} which, if `true`, sets soft wrapping
|
||||
setSoftWrap: (@softWrap) ->
|
||||
|
||||
# Public: Given a path to a file, this constructs and associates a new `EditSession`, showing the file.
|
||||
#
|
||||
# filePath - The {String} path of the file to associate with
|
||||
# editSessionOptions - Options that you can pass to the `EditSession` constructor
|
||||
#
|
||||
# Returns either an {EditSession} (for text) or {ImageEditSession} (for images).
|
||||
buildEditSession: (filePath, editSessionOptions={}) ->
|
||||
if ImageEditSession.canOpen(filePath)
|
||||
new ImageEditSession(filePath)
|
||||
else
|
||||
@buildEditSessionForBuffer(@bufferForPath(filePath), editSessionOptions)
|
||||
|
||||
# Public: Retrieves all the {EditSession}s in the project; that is, the `EditSession`s for all open files.
|
||||
#
|
||||
# Returns an {Array} of {EditSession}s.
|
||||
getEditSessions: ->
|
||||
new Array(@editSessions...)
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
buildEditSessionForBuffer: (buffer, editSessionOptions) ->
|
||||
options = _.extend(@defaultEditSessionOptions(), editSessionOptions)
|
||||
options.project = this
|
||||
@@ -105,22 +182,10 @@ class Project
|
||||
softTabs: @getSoftTabs()
|
||||
softWrap: @getSoftWrap()
|
||||
|
||||
getEditSessions: ->
|
||||
new Array(@editSessions...)
|
||||
|
||||
eachEditSession: (callback) ->
|
||||
callback(editSession) for editSession in @getEditSessions()
|
||||
@on 'edit-session-created', (editSession) -> callback(editSession)
|
||||
|
||||
removeEditSession: (editSession) ->
|
||||
_.remove(@editSessions, editSession)
|
||||
|
||||
getBuffers: ->
|
||||
buffers = []
|
||||
for editSession in @editSessions when not _.include(buffers, editSession.buffer)
|
||||
buffers.push editSession.buffer
|
||||
buffers
|
||||
|
||||
|
||||
eachBuffer: (args...) ->
|
||||
subscriber = args.shift() if args.length > 1
|
||||
callback = args.shift()
|
||||
@@ -131,6 +196,34 @@ class Project
|
||||
else
|
||||
@on 'buffer-created', (buffer) -> callback(buffer)
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
# Public: Removes an {EditSession} association from the project.
|
||||
#
|
||||
# Returns the removed {EditSession}.
|
||||
removeEditSession: (editSession) ->
|
||||
_.remove(@editSessions, editSession)
|
||||
|
||||
# Public: Retrieves all the {Buffer}s in the project; that is, the buffers for all open files.
|
||||
#
|
||||
# Returns an {Array} of {Buffer}s.
|
||||
getBuffers: ->
|
||||
buffers = []
|
||||
for editSession in @editSessions when not _.include(buffers, editSession.buffer)
|
||||
buffers.push editSession.buffer
|
||||
buffers
|
||||
|
||||
# Public: Given a file path, this retrieves or creates a new {Buffer}.
|
||||
#
|
||||
# If the `filePath` already has a `buffer`, that value is used instead. Otherwise,
|
||||
# `text` is used as the contents of the new buffer.
|
||||
#
|
||||
# filePath - A {String} representing a path. If `null`, an "Untitled" buffer is created.
|
||||
# text - The {String} text to use as a buffer, if the file doesn't have any contents
|
||||
#
|
||||
# Returns the {Buffer}.
|
||||
bufferForPath: (filePath, text) ->
|
||||
if filePath?
|
||||
filePath = @resolve(filePath)
|
||||
@@ -140,15 +233,28 @@ class Project
|
||||
else
|
||||
@buildBuffer(null, text)
|
||||
|
||||
# Public: Given a file path, this sets its {Buffer}.
|
||||
#
|
||||
# filePath - A {String} representing a path
|
||||
# text - The {String} text to use as a buffer
|
||||
#
|
||||
# Returns the {Buffer}.
|
||||
buildBuffer: (filePath, text) ->
|
||||
buffer = new Buffer(filePath, text)
|
||||
@buffers.push buffer
|
||||
@trigger 'buffer-created', buffer
|
||||
buffer
|
||||
|
||||
# Public: Removes a {Buffer} association from the project.
|
||||
#
|
||||
# Returns the removed {Buffer}.
|
||||
removeBuffer: (buffer) ->
|
||||
_.remove(@buffers, buffer)
|
||||
|
||||
# Public: Performs a search across all the files in the project.
|
||||
#
|
||||
# regex - A {RegExp} to search with
|
||||
# iterator - A {Function} callback on each file found
|
||||
scan: (regex, iterator) ->
|
||||
bufferedData = ""
|
||||
state = 'readingPath'
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
Point = require 'point'
|
||||
_ = require 'underscore'
|
||||
|
||||
# Public: Indicates a region within the editor.
|
||||
#
|
||||
# To better visualize how this works, imagine a rectangle.
|
||||
# Each quadrant of the rectangle is analogus to a range, as ranges contain a
|
||||
# starting row and a starting column, as well as an ending row, and an ending column.
|
||||
#
|
||||
# Each `Range` is actually constructed of two `Point` objects, labelled `start` and `end`.
|
||||
module.exports =
|
||||
class Range
|
||||
# Public: Constructs a `Range` from a given object.
|
||||
#
|
||||
# object - This can be an {Array} (`[startRow, startColumn, endRow, endColumn]`) or an object `{start: Point, end: Point}`
|
||||
#
|
||||
# Returns the new {Range}.
|
||||
@fromObject: (object) ->
|
||||
if _.isArray(object)
|
||||
new Range(object...)
|
||||
@@ -11,11 +23,22 @@ class Range
|
||||
else
|
||||
new Range(object.start, object.end)
|
||||
|
||||
# Public: Constructs a `Range` from a {Point}, and the delta values beyond that point.
|
||||
#
|
||||
# point - A {Point} to start with
|
||||
# rowDelta - A {Number} indicating how far from the starting {Point} the range's row should be
|
||||
# columnDelta - A {Number} indicating how far from the starting {Point} the range's column should be
|
||||
#
|
||||
# Returns the new {Range}.
|
||||
@fromPointWithDelta: (point, rowDelta, columnDelta) ->
|
||||
pointA = Point.fromObject(point)
|
||||
pointB = new Point(point.row + rowDelta, point.column + columnDelta)
|
||||
new Range(pointA, pointB)
|
||||
|
||||
# Public: Creates a new `Range` object based on two {Point}s.
|
||||
#
|
||||
# pointA - The first {Point} (default: `0, 0`)
|
||||
# pointB - The second {Point} (default: `0, 0`)
|
||||
constructor: (pointA = new Point(0, 0), pointB = new Point(0, 0)) ->
|
||||
pointA = Point.fromObject(pointA)
|
||||
pointB = Point.fromObject(pointB)
|
||||
@@ -27,40 +50,95 @@ class Range
|
||||
@start = pointB
|
||||
@end = pointA
|
||||
|
||||
# Public: Creates an identical copy of the `Range`.
|
||||
#
|
||||
# Returns a duplicate {Range}.
|
||||
copy: ->
|
||||
new Range(@start.copy(), @end.copy())
|
||||
|
||||
# Public: Identifies if two `Range`s are equal.
|
||||
#
|
||||
# All four points (`start.row`, `start.column`, `end.row`, `end.column`) must be
|
||||
# equal for this method to return `true`.
|
||||
#
|
||||
# other - A different {Range} to check against
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isEqual: (other) ->
|
||||
if _.isArray(other) and other.length == 2
|
||||
other = new Range(other...)
|
||||
|
||||
other.start.isEqual(@start) and other.end.isEqual(@end)
|
||||
|
||||
# Public: Identifies if the `Range` is on the same line.
|
||||
#
|
||||
# In other words, if `start.row` is equal to `end.row`.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isSingleLine: ->
|
||||
@start.row == @end.row
|
||||
|
||||
# Public: Identifies if two `Range`s are on the same line.
|
||||
#
|
||||
# other - A different {Range} to check against
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
coversSameRows: (other) ->
|
||||
@start.row == other.start.row && @end.row == other.end.row
|
||||
|
||||
# Internal:
|
||||
inspect: ->
|
||||
"[#{@start.inspect()} - #{@end.inspect()}]"
|
||||
|
||||
# Public: Adds a new point to the `Range`s `start` and `end`.
|
||||
#
|
||||
# point - A new {Point} to add
|
||||
#
|
||||
# Returns the new {Range}.
|
||||
add: (point) ->
|
||||
new Range(@start.add(point), @end.add(point))
|
||||
|
||||
# Public: Moves a `Range`.
|
||||
#
|
||||
# In other words, the starting and ending `row` values, and the starting and ending
|
||||
# `column` values, are added to each other.
|
||||
#
|
||||
# startPoint - The {Point} to move the `Range`s `start` by
|
||||
# endPoint - The {Point} to move the `Range`s `end` by
|
||||
#
|
||||
# Returns the new {Range}.
|
||||
translate: (startPoint, endPoint=startPoint) ->
|
||||
new Range(@start.translate(startPoint), @end.translate(endPoint))
|
||||
|
||||
# Public: Identifies if two `Range`s intersect each other.
|
||||
#
|
||||
# otherRange - A different {Range} to check against
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
intersectsWith: (otherRange) ->
|
||||
if @start.isLessThanOrEqual(otherRange.start)
|
||||
@end.isGreaterThanOrEqual(otherRange.start)
|
||||
else
|
||||
otherRange.intersectsWith(this)
|
||||
|
||||
# Public: Identifies if a second `Range` is contained within a first.
|
||||
#
|
||||
# otherRange - A different {Range} to check against
|
||||
# options - A hash with a single option:
|
||||
# :exclusive - A {Boolean} which, if `true`, indicates that no {Point}s in the `Range` can be equal
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
containsRange: (otherRange, {exclusive} = {}) ->
|
||||
{ start, end } = Range.fromObject(otherRange)
|
||||
@containsPoint(start, {exclusive}) and @containsPoint(end, {exclusive})
|
||||
|
||||
# Public: Identifies if a `Range` contains a {Point}.
|
||||
#
|
||||
# point - A {Point} to check against
|
||||
# options - A hash with a single option:
|
||||
# :exclusive - A {Boolean} which, if `true`, indicates that no {Point}s in the `Range` can be equal
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
containsPoint: (point, {exclusive} = {}) ->
|
||||
point = Point.fromObject(point)
|
||||
if exclusive
|
||||
@@ -68,17 +146,36 @@ class Range
|
||||
else
|
||||
point.isGreaterThanOrEqual(@start) and point.isLessThanOrEqual(@end)
|
||||
|
||||
# Public: Identifies if a `Range` contains a row.
|
||||
#
|
||||
# row - A row {Number} to check against
|
||||
# options - A hash with a single option:
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
containsRow: (row) ->
|
||||
@start.row <= row <= @end.row
|
||||
|
||||
# Public: Constructs a union between two `Range`s.
|
||||
#
|
||||
# otherRange - A different {Range} to unionize with
|
||||
#
|
||||
# Returns the new {Range}.
|
||||
union: (otherRange) ->
|
||||
start = if @start.isLessThan(otherRange.start) then @start else otherRange.start
|
||||
end = if @end.isGreaterThan(otherRange.end) then @end else otherRange.end
|
||||
new Range(start, end)
|
||||
|
||||
# Public: Identifies if a `Range` is empty.
|
||||
#
|
||||
# A `Range` is empty if its start {Point} matches its end.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isEmpty: ->
|
||||
@start.isEqual(@end)
|
||||
|
||||
# Public: Calculates the difference between a `Range`s `start` and `end` points.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
toDelta: ->
|
||||
rows = @end.row - @start.row
|
||||
if rows == 0
|
||||
@@ -87,5 +184,8 @@ class Range
|
||||
columns = @end.column
|
||||
new Point(rows, columns)
|
||||
|
||||
# Public: Calculates the number of rows a `Range`s contains.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getRowCount: ->
|
||||
@end.row - @start.row + 1
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
Task = require 'task'
|
||||
_ = require 'underscore'
|
||||
|
||||
# Internal:
|
||||
module.exports =
|
||||
class RepositoryStatusTask extends Task
|
||||
|
||||
constructor: (@repo) ->
|
||||
super('repository-status-handler')
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ PaneRow = require 'pane-row'
|
||||
PaneContainer = require 'pane-container'
|
||||
EditSession = require 'edit-session'
|
||||
|
||||
# Public: The container for the entire Atom application.
|
||||
module.exports =
|
||||
class RootView extends View
|
||||
registerDeserializers(this, Pane, PaneRow, PaneColumn, Editor)
|
||||
@@ -23,12 +24,16 @@ class RootView extends View
|
||||
ignoredNames: [".git", ".svn", ".DS_Store"]
|
||||
disabledPackages: []
|
||||
|
||||
###
|
||||
# Internal:
|
||||
###
|
||||
|
||||
@content: ({panes}={}) ->
|
||||
@div id: 'root-view', =>
|
||||
@div id: 'horizontal', outlet: 'horizontal', =>
|
||||
@div id: 'vertical', outlet: 'vertical', =>
|
||||
@subview 'panes', panes ? new PaneContainer
|
||||
|
||||
|
||||
@deserialize: ({ panes }) ->
|
||||
panes = deserialize(panes) if panes?.deserializer is 'PaneContainer'
|
||||
new RootView({panes})
|
||||
@@ -74,9 +79,6 @@ class RootView extends View
|
||||
deserializer: 'RootView'
|
||||
panes: @panes.serialize()
|
||||
|
||||
confirmClose: ->
|
||||
@panes.confirmClose()
|
||||
|
||||
handleFocus: (e) ->
|
||||
if @getActivePane()
|
||||
@getActivePane().focus()
|
||||
@@ -93,6 +95,17 @@ class RootView extends View
|
||||
afterAttach: (onDom) ->
|
||||
@focus() if onDom
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
# Public: Shows a dialog asking if the pane was _really_ meant to be closed.
|
||||
confirmClose: ->
|
||||
@panes.confirmClose()
|
||||
|
||||
# Public: Given a filepath, this opens it in Atom.
|
||||
#
|
||||
# Returns the `EditSession` for the file URI.
|
||||
open: (path, options = {}) ->
|
||||
changeFocus = options.changeFocus ? true
|
||||
path = project.resolve(path) if path?
|
||||
@@ -107,6 +120,7 @@ class RootView extends View
|
||||
activePane.focus() if changeFocus
|
||||
editSession
|
||||
|
||||
# Public: Updates the application's title, based on whichever file is open.
|
||||
updateTitle: ->
|
||||
if projectPath = project.getPath()
|
||||
if item = @getActivePaneItem()
|
||||
@@ -116,12 +130,21 @@ class RootView extends View
|
||||
else
|
||||
@setTitle('untitled')
|
||||
|
||||
# Public: Sets the application's title.
|
||||
#
|
||||
# Returns a {String}.
|
||||
setTitle: (title) ->
|
||||
document.title = title
|
||||
|
||||
# Public: Retrieves all of the application's {Editor}s.
|
||||
#
|
||||
# Returns an {Array} of {Editor}s.
|
||||
getEditors: ->
|
||||
@panes.find('.pane > .item-views > .editor').map(-> $(this).view()).toArray()
|
||||
|
||||
# Public: Retrieves all of the modified buffers that are open and unsaved.
|
||||
#
|
||||
# Returns an {Array} of {Buffer}s.
|
||||
getModifiedBuffers: ->
|
||||
modifiedBuffers = []
|
||||
for pane in @getPanes()
|
||||
@@ -129,9 +152,15 @@ class RootView extends View
|
||||
modifiedBuffers.push item.buffer if item.buffer.isModified()
|
||||
modifiedBuffers
|
||||
|
||||
# Public: Retrieves all of the paths to open files.
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
getOpenBufferPaths: ->
|
||||
_.uniq(_.flatten(@getEditors().map (editor) -> editor.getOpenBufferPaths()))
|
||||
|
||||
# Public: Retrieves the pane that's currently open.
|
||||
#
|
||||
# Returns an {Pane}.
|
||||
getActivePane: ->
|
||||
@panes.getActivePane()
|
||||
|
||||
@@ -145,29 +174,51 @@ class RootView extends View
|
||||
focusNextPane: -> @panes.focusNextPane()
|
||||
getFocusedPane: -> @panes.getFocusedPane()
|
||||
|
||||
# Internal: Destroys everything.
|
||||
remove: ->
|
||||
editor.remove() for editor in @getEditors()
|
||||
project.destroy()
|
||||
super
|
||||
|
||||
# Public: Saves all of the open buffers.
|
||||
saveAll: ->
|
||||
@panes.saveAll()
|
||||
|
||||
# Public: Fires a callback on each open {Pane}.
|
||||
#
|
||||
# callback - A {Function} to call
|
||||
eachPane: (callback) ->
|
||||
@panes.eachPane(callback)
|
||||
|
||||
# Public: Retrieves all of the open {Pane}s.
|
||||
#
|
||||
# Returns an {Array} of {Pane}.
|
||||
getPanes: ->
|
||||
@panes.getPanes()
|
||||
|
||||
# Public: Given a {Pane}, this fetches its ID.
|
||||
#
|
||||
# pane - An open {Pane}
|
||||
#
|
||||
# Returns a {Number}.
|
||||
indexOfPane: (pane) ->
|
||||
@panes.indexOfPane(pane)
|
||||
|
||||
# Public: Fires a callback on each open {Editor}.
|
||||
#
|
||||
# callback - A {Function} to call
|
||||
eachEditor: (callback) ->
|
||||
callback(editor) for editor in @getEditors()
|
||||
@on 'editor:attached', (e, editor) -> callback(editor)
|
||||
|
||||
# Public: Fires a callback on each open {EditSession}.
|
||||
#
|
||||
# callback - A {Function} to call
|
||||
eachEditSession: (callback) ->
|
||||
project.eachEditSession(callback)
|
||||
|
||||
# Public: Fires a callback on each open {Buffer}.
|
||||
#
|
||||
# callback - A {Function} to call
|
||||
eachBuffer: (callback) ->
|
||||
project.eachBuffer(callback)
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
_ = require 'underscore'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class ScreenLine
|
||||
constructor: ({tokens, @lineEnding, @ruleStack, @bufferRows, @startBufferColumn, @fold, tabLength}) ->
|
||||
@@ -104,6 +108,9 @@ class ScreenLine
|
||||
breakOutLeadingWhitespace = token.isOnlyWhitespace() if breakOutLeadingWhitespace
|
||||
outputTokens
|
||||
|
||||
# Public: Determins if the current line is commented.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isComment: ->
|
||||
for token in @tokens
|
||||
continue if token.scopes.length is 1
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
{View} = require 'space-pen'
|
||||
|
||||
# Public: Represents a view that scrolls.
|
||||
#
|
||||
# This `View` subclass listens to events such as `page-up`, `page-down`,
|
||||
# `move-to-top`, and `move-to-bottom`.
|
||||
module.exports =
|
||||
class ScrollView extends View
|
||||
|
||||
# Internal: The constructor.
|
||||
initialize: ->
|
||||
@on 'core:page-up', => @pageUp()
|
||||
@on 'core:page-down', => @pageDown()
|
||||
|
||||
@@ -5,6 +5,11 @@ fuzzyFilter = require 'fuzzy-filter'
|
||||
|
||||
module.exports =
|
||||
class SelectList extends View
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
@content: ->
|
||||
@div class: @viewClass(), =>
|
||||
@subview 'miniEditor', new Editor(mini: true)
|
||||
|
||||
@@ -4,6 +4,8 @@ Range = require 'range'
|
||||
|
||||
module.exports =
|
||||
class SelectionView extends View
|
||||
|
||||
# Internal: Establishes the DOM for the selection view.
|
||||
@content: ->
|
||||
@div class: 'selection'
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ Range = require 'range'
|
||||
EventEmitter = require 'event-emitter'
|
||||
_ = require 'underscore'
|
||||
|
||||
# Public: Represents a selection in the {EditSession}.
|
||||
module.exports =
|
||||
class Selection
|
||||
cursor: null
|
||||
@@ -12,6 +13,10 @@ class Selection
|
||||
wordwise: false
|
||||
needsAutoscroll: null
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
constructor: ({@cursor, @marker, @editSession, @goalBufferRange}) ->
|
||||
@cursor.selection = this
|
||||
@editSession.observeMarker @marker, => @screenRangeChanged()
|
||||
@@ -32,27 +37,56 @@ class Selection
|
||||
@wordwise = false
|
||||
@linewise = false
|
||||
|
||||
clearAutoscroll: ->
|
||||
@needsAutoscroll = null
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
# Public: Identifies if the selection is highlighting anything.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isEmpty: ->
|
||||
@getBufferRange().isEmpty()
|
||||
|
||||
# Public: Identifies if the selection is reversed, that is, it is highlighting "up."
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isReversed: ->
|
||||
@editSession.isMarkerReversed(@marker)
|
||||
|
||||
# Public: Identifies if the selection is a single line.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isSingleScreenLine: ->
|
||||
@getScreenRange().isSingleLine()
|
||||
|
||||
clearAutoscroll: ->
|
||||
@needsAutoscroll = null
|
||||
|
||||
# Public: Retrieves the screen range for the selection.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getScreenRange: ->
|
||||
@editSession.getMarkerScreenRange(@marker)
|
||||
|
||||
# Public: Modifies the screen range for the selection.
|
||||
#
|
||||
# screenRange - The new {Range} to use
|
||||
# options - A hash of options matching those found in {.setBufferRange}
|
||||
setScreenRange: (screenRange, options) ->
|
||||
@setBufferRange(@editSession.bufferRangeForScreenRange(screenRange), options)
|
||||
|
||||
# Public: Retrieves the buffer range for the selection.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getBufferRange: ->
|
||||
@editSession.getMarkerBufferRange(@marker)
|
||||
|
||||
# Public: Modifies the buffer range for the selection.
|
||||
#
|
||||
# screenRange - The new {Range} to select
|
||||
# options - A hash of options with the following keys:
|
||||
# :preserveFolds - if `true`, the fold settings are preserved after the selection moves
|
||||
# :autoscroll - if `true`, the {EditSession} scrolls to the new selection
|
||||
setBufferRange: (bufferRange, options={}) ->
|
||||
bufferRange = Range.fromObject(bufferRange)
|
||||
@needsAutoscroll = options.autoscroll
|
||||
@@ -62,6 +96,9 @@ class Selection
|
||||
@cursor.needsAutoscroll = false if options.autoscroll?
|
||||
@editSession.setMarkerBufferRange(@marker, bufferRange, options)
|
||||
|
||||
# Public: Retrieves the starting and ending buffer rows the selection is highlighting.
|
||||
#
|
||||
# Returns an {Array} of two {Number}s: the starting row, and the ending row.
|
||||
getBufferRowRange: ->
|
||||
range = @getBufferRange()
|
||||
start = range.start.row
|
||||
@@ -69,16 +106,24 @@ class Selection
|
||||
end = Math.max(start, end - 1) if range.end.column == 0
|
||||
[start, end]
|
||||
|
||||
# Internal:
|
||||
screenRangeChanged: ->
|
||||
screenRange = @getScreenRange()
|
||||
@trigger 'screen-range-changed', screenRange
|
||||
|
||||
# Public: Retrieves the text in the selection.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getText: ->
|
||||
@editSession.buffer.getTextInRange(@getBufferRange())
|
||||
|
||||
# Public: Clears the selection, moving the marker to move to the head.
|
||||
clear: ->
|
||||
@editSession.clearMarkerTail(@marker)
|
||||
|
||||
# Public: Modifies the selection to mark the current word.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
selectWord: ->
|
||||
options = {}
|
||||
options.wordRegex = /[\t ]*/ if @cursor.isSurroundedByWhitespace()
|
||||
@@ -90,6 +135,9 @@ class Selection
|
||||
expandOverWord: ->
|
||||
@setBufferRange(@getBufferRange().union(@cursor.getCurrentWordBufferRange()))
|
||||
|
||||
# Public: Selects an entire line in the {Buffer}.
|
||||
#
|
||||
# row - The line {Number} to select (default: the row of the cursor)
|
||||
selectLine: (row=@cursor.getBufferPosition().row) ->
|
||||
range = @editSession.bufferRangeForBufferRow(row, includeNewline: true)
|
||||
@setBufferRange(range)
|
||||
@@ -101,6 +149,9 @@ class Selection
|
||||
range = @getBufferRange().union(@cursor.getCurrentLineBufferRange(includeNewline: true))
|
||||
@setBufferRange(range)
|
||||
|
||||
# Public: Selects the text from the current cursor position to a given screen position.
|
||||
#
|
||||
# position - An instance of {Point}, with a given `row` and `column`.
|
||||
selectToScreenPosition: (position) ->
|
||||
@modifySelection =>
|
||||
if @initialScreenRange
|
||||
@@ -116,45 +167,61 @@ class Selection
|
||||
else if @wordwise
|
||||
@expandOverWord()
|
||||
|
||||
# Public: Selects the text from the current cursor position to a given buffer position.
|
||||
#
|
||||
# position - An instance of {Point}, with a given `row` and `column`.
|
||||
selectToBufferPosition: (position) ->
|
||||
@modifySelection => @cursor.setBufferPosition(position)
|
||||
|
||||
# Public: Selects the text one position right of the cursor.
|
||||
selectRight: ->
|
||||
@modifySelection => @cursor.moveRight()
|
||||
|
||||
# Public: Selects the text one position left of the cursor.
|
||||
selectLeft: ->
|
||||
@modifySelection => @cursor.moveLeft()
|
||||
|
||||
# Public: Selects all the text one position above the cursor.
|
||||
selectUp: ->
|
||||
@modifySelection => @cursor.moveUp()
|
||||
|
||||
# Public: Selects all the text one position below the cursor.
|
||||
selectDown: ->
|
||||
@modifySelection => @cursor.moveDown()
|
||||
|
||||
# Public: Selects all the text from the current cursor position to the top of the buffer.
|
||||
selectToTop: ->
|
||||
@modifySelection => @cursor.moveToTop()
|
||||
|
||||
# Public: Selects all the text from the current cursor position to the bottom of the buffer.
|
||||
selectToBottom: ->
|
||||
@modifySelection => @cursor.moveToBottom()
|
||||
|
||||
# Public: Selects all the text in the buffer.
|
||||
selectAll: ->
|
||||
@setBufferRange(@editSession.buffer.getRange(), autoscroll: false)
|
||||
|
||||
# Public: Selects all the text from the current cursor position to the beginning of the line.
|
||||
selectToBeginningOfLine: ->
|
||||
@modifySelection => @cursor.moveToBeginningOfLine()
|
||||
|
||||
# Public: Selects all the text from the current cursor position to the end of the line.
|
||||
selectToEndOfLine: ->
|
||||
@modifySelection => @cursor.moveToEndOfLine()
|
||||
|
||||
# Public: Selects all the text from the current cursor position to the beginning of the word.
|
||||
selectToBeginningOfWord: ->
|
||||
@modifySelection => @cursor.moveToBeginningOfWord()
|
||||
|
||||
# Public: Selects all the text from the current cursor position to the end of the word.
|
||||
selectToEndOfWord: ->
|
||||
@modifySelection => @cursor.moveToEndOfWord()
|
||||
|
||||
# Public: Selects all the text from the current cursor position to the beginning of the next word.
|
||||
selectToBeginningOfNextWord: ->
|
||||
@modifySelection => @cursor.moveToBeginningOfNextWord()
|
||||
|
||||
# Public: Moves the selection down one row.
|
||||
addSelectionBelow: ->
|
||||
range = (@goalBufferRange ? @getBufferRange()).copy()
|
||||
nextRow = range.end.row + 1
|
||||
@@ -172,6 +239,7 @@ class Selection
|
||||
@editSession.addSelectionForBufferRange(range, goalBufferRange: range, suppressMerge: true)
|
||||
break
|
||||
|
||||
# Public: Moves the selection up one row.
|
||||
addSelectionAbove: ->
|
||||
range = (@goalBufferRange ? @getBufferRange()).copy()
|
||||
previousRow = range.end.row - 1
|
||||
@@ -189,6 +257,13 @@ class Selection
|
||||
@editSession.addSelectionForBufferRange(range, goalBufferRange: range, suppressMerge: true)
|
||||
break
|
||||
|
||||
# Public: Replaces text at the current selection.
|
||||
#
|
||||
# text - A {String} representing the text to add
|
||||
# options - A hash containing the following options:
|
||||
# :normalizeIndent - TODO
|
||||
# :select - if `true`, selects the newly added text
|
||||
# :autoIndent - if `true`, indents the newly added text appropriately
|
||||
insertText: (text, options={}) ->
|
||||
oldBufferRange = @getBufferRange()
|
||||
@editSession.destroyFoldsContainingBufferRow(oldBufferRange.end.row)
|
||||
@@ -210,6 +285,10 @@ class Selection
|
||||
|
||||
newBufferRange
|
||||
|
||||
# Public: Indents the selection.
|
||||
#
|
||||
# options - A hash with one key, `autoIndent`. If `true`, the indentation is
|
||||
# performed appropriately. Otherwise, {EditSession#getTabText} is used
|
||||
indent: ({ autoIndent }={})->
|
||||
{ row, column } = @cursor.getBufferPosition()
|
||||
|
||||
@@ -225,6 +304,7 @@ class Selection
|
||||
else
|
||||
@indentSelectedRows()
|
||||
|
||||
# Public: If the selection spans multiple rows, indents all of them.
|
||||
indentSelectedRows: ->
|
||||
[start, end] = @getBufferRowRange()
|
||||
for row in [start..end]
|
||||
@@ -270,6 +350,7 @@ class Selection
|
||||
desiredIndentString = @editSession.buildIndentString(desiredIndentLevel)
|
||||
line.replace(/^[\t ]*/, desiredIndentString)
|
||||
|
||||
# Public: Performs a backspace, removing the character found behind the selection.
|
||||
backspace: ->
|
||||
if @isEmpty() and not @editSession.isFoldedAtScreenRow(@cursor.getScreenRow())
|
||||
if @cursor.isAtBeginningOfLine() and @editSession.isFoldedAtScreenRow(@cursor.getScreenRow() - 1)
|
||||
@@ -279,10 +360,12 @@ class Selection
|
||||
|
||||
@deleteSelectedText()
|
||||
|
||||
# Public: Performs a backspace to the beginning of the current word, removing characters found there.
|
||||
backspaceToBeginningOfWord: ->
|
||||
@selectToBeginningOfWord() if @isEmpty()
|
||||
@deleteSelectedText()
|
||||
|
||||
# Public: Performs a backspace to the beginning of the current line, removing characters found there.
|
||||
backspaceToBeginningOfLine: ->
|
||||
if @isEmpty() and @cursor.isAtBeginningOfLine()
|
||||
@selectLeft()
|
||||
@@ -290,6 +373,7 @@ class Selection
|
||||
@selectToBeginningOfLine()
|
||||
@deleteSelectedText()
|
||||
|
||||
# Public: Performs a delete, removing the character found ahead of the cursor position.
|
||||
delete: ->
|
||||
if @isEmpty()
|
||||
if @cursor.isAtEndOfLine() and fold = @editSession.largestFoldStartingAtScreenRow(@cursor.getScreenRow() + 1)
|
||||
@@ -298,10 +382,12 @@ class Selection
|
||||
@selectRight()
|
||||
@deleteSelectedText()
|
||||
|
||||
# Public: Performs a delete to the end of the current word, removing characters found there.
|
||||
deleteToEndOfWord: ->
|
||||
@selectToEndOfWord() if @isEmpty()
|
||||
@deleteSelectedText()
|
||||
|
||||
# Public: Deletes the selected text.
|
||||
deleteSelectedText: ->
|
||||
bufferRange = @getBufferRange()
|
||||
if fold = @editSession.largestFoldContainingBufferRow(bufferRange.end.row)
|
||||
@@ -311,6 +397,7 @@ class Selection
|
||||
@editSession.buffer.delete(bufferRange) unless bufferRange.isEmpty()
|
||||
@cursor?.setBufferPosition(bufferRange.start)
|
||||
|
||||
# Public: Deletes the line.
|
||||
deleteLine: ->
|
||||
if @isEmpty()
|
||||
start = @cursor.getScreenRow()
|
||||
@@ -327,6 +414,9 @@ class Selection
|
||||
end--
|
||||
@editSession.buffer.deleteRows(start, end)
|
||||
|
||||
# Public: Joins the current line with the one below it.
|
||||
#
|
||||
# If there selection spans more than one line, all the lines are joined together.
|
||||
joinLine: ->
|
||||
selectedRange = @getBufferRange()
|
||||
if selectedRange.isEmpty()
|
||||
@@ -364,17 +454,29 @@ class Selection
|
||||
[start, end] = @getBufferRowRange()
|
||||
@editSession.autoIndentBufferRows(start, end)
|
||||
|
||||
# Public: Wraps the selected lines in comments.
|
||||
#
|
||||
# Returns an {Array} of the commented {Ranges}.
|
||||
toggleLineComments: ->
|
||||
@editSession.toggleLineCommentsForBufferRows(@getBufferRowRange()...)
|
||||
|
||||
# Public: Performs a cut operation on the selection, until the end of the line.
|
||||
#
|
||||
# maintainPasteboard - A {Boolean} indicating TODO
|
||||
cutToEndOfLine: (maintainPasteboard) ->
|
||||
@selectToEndOfLine() if @isEmpty()
|
||||
@cut(maintainPasteboard)
|
||||
|
||||
# Public: Performs a cut operation on the selection.
|
||||
#
|
||||
# maintainPasteboard - A {Boolean} indicating TODO
|
||||
cut: (maintainPasteboard=false) ->
|
||||
@copy(maintainPasteboard)
|
||||
@delete()
|
||||
|
||||
# Public: Performs a copy operation on the selection.
|
||||
#
|
||||
# maintainPasteboard - A {Boolean} indicating TODO
|
||||
copy: (maintainPasteboard=false) ->
|
||||
return if @isEmpty()
|
||||
text = @editSession.buffer.getTextInRange(@getBufferRange())
|
||||
@@ -386,6 +488,7 @@ class Selection
|
||||
|
||||
pasteboard.write(text, metadata)
|
||||
|
||||
# Public: Folds the selection.
|
||||
fold: ->
|
||||
range = @getBufferRange()
|
||||
@editSession.createFold(range.start.row, range.end.row)
|
||||
@@ -406,12 +509,26 @@ class Selection
|
||||
placeTail: ->
|
||||
@editSession.placeMarkerTail(@marker)
|
||||
|
||||
# Public: Identifies if a selection intersects with a given buffer range.
|
||||
#
|
||||
# bufferRange - A {Range} to check against
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
intersectsBufferRange: (bufferRange) ->
|
||||
@getBufferRange().intersectsWith(bufferRange)
|
||||
|
||||
# Public: Identifies if a selection intersects with another selection.
|
||||
#
|
||||
# otherSelection - A `Selection` to check against
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
intersectsWith: (otherSelection) ->
|
||||
@getBufferRange().intersectsWith(otherSelection.getBufferRange())
|
||||
|
||||
# Public: Merges two selections together.
|
||||
#
|
||||
# otherSelection - A `Selection` to merge with
|
||||
# options - A hash of options matching those found in {.setBufferRange}
|
||||
merge: (otherSelection, options) ->
|
||||
@setBufferRange(@getBufferRange().union(otherSelection.getBufferRange()), options)
|
||||
if @goalBufferRange and otherSelection.goalBufferRange
|
||||
|
||||
@@ -6,6 +6,10 @@ fsUtils = require 'fs-utils'
|
||||
EventEmitter = require 'event-emitter'
|
||||
NullGrammar = require 'null-grammar'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class Syntax
|
||||
registerDeserializer(this)
|
||||
|
||||
@@ -8,6 +8,10 @@ UndoManager = require 'undo-manager'
|
||||
BufferChangeOperation = require 'buffer-change-operation'
|
||||
BufferMarker = require 'buffer-marker'
|
||||
|
||||
# Public: Represents the contents of a file.
|
||||
#
|
||||
# The `Buffer` is often associated with a {File}. However, this is not always
|
||||
# the case, as a `Buffer` could be an unsaved chunk of text.
|
||||
module.exports =
|
||||
class Buffer
|
||||
@idCounter = 1
|
||||
@@ -25,9 +29,10 @@ class Buffer
|
||||
invalidMarkers: null
|
||||
refcount: 0
|
||||
|
||||
@deserialize: ({path, text}) ->
|
||||
project.bufferForPath(path, text)
|
||||
|
||||
# Public: Creates a new buffer.
|
||||
#
|
||||
# path - A {String} representing the file path
|
||||
# initialText - A {String} setting the starting text
|
||||
constructor: (path, initialText) ->
|
||||
@id = @constructor.idCounter++
|
||||
@nextMarkerId = 1
|
||||
@@ -50,6 +55,10 @@ class Buffer
|
||||
|
||||
@undoManager = new UndoManager(this)
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
destroy: ->
|
||||
throw new Error("Destroying buffer twice with path '#{@getPath()}'") if @destroyed
|
||||
@file?.off()
|
||||
@@ -70,7 +79,8 @@ class Buffer
|
||||
path: @getPath()
|
||||
text: @getText() if @isModified()
|
||||
|
||||
hasMultipleEditors: -> @refcount > 1
|
||||
@deserialize: ({path, text}) ->
|
||||
project.bufferForPath(path, text)
|
||||
|
||||
subscribeToFile: ->
|
||||
@file.on "contents-changed", =>
|
||||
@@ -87,7 +97,21 @@ class Buffer
|
||||
|
||||
@file.on "moved", =>
|
||||
@trigger "path-changed", this
|
||||
|
||||
###
|
||||
# Public #
|
||||
###
|
||||
|
||||
# Public: Identifies if the buffer belongs to multiple editors.
|
||||
#
|
||||
# For example, if the {Editor} was split.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
hasMultipleEditors: -> @refcount > 1
|
||||
|
||||
# Public: Reloads a file in the {EditSession}.
|
||||
#
|
||||
# Essentially, this performs a force read of the file.
|
||||
reload: ->
|
||||
@trigger 'will-reload'
|
||||
@updateCachedDiskContents()
|
||||
@@ -95,15 +119,27 @@ class Buffer
|
||||
@triggerModifiedStatusChanged(false)
|
||||
@trigger 'reloaded'
|
||||
|
||||
# Public: Rereads the contents of the file, and stores them in the cache.
|
||||
#
|
||||
# Essentially, this performs a force read of the file on disk.
|
||||
updateCachedDiskContents: ->
|
||||
@cachedDiskContents = @file.read()
|
||||
|
||||
# Public: Gets the file's basename--that is, the file without any directory information.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getBaseName: ->
|
||||
@file?.getBaseName()
|
||||
|
||||
# Public: Retrieves the path for the file.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getPath: ->
|
||||
@file?.getPath()
|
||||
|
||||
# Public: Sets the path for the file.
|
||||
#
|
||||
# path - A {String} representing the new file path
|
||||
setPath: (path) ->
|
||||
return if path == @getPath()
|
||||
|
||||
@@ -114,21 +150,38 @@ class Buffer
|
||||
|
||||
@trigger "path-changed", this
|
||||
|
||||
# Public: Retrieves the current buffer's file extension.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getExtension: ->
|
||||
if @getPath()
|
||||
@getPath().split('/').pop().split('.').pop()
|
||||
else
|
||||
null
|
||||
|
||||
# Public: Retrieves the cached buffer contents.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getText: ->
|
||||
@cachedMemoryContents ?= @getTextInRange(@getRange())
|
||||
|
||||
# Public: Replaces the current buffer contents.
|
||||
#
|
||||
# text - A {String} containing the new buffer contents.
|
||||
setText: (text) ->
|
||||
@change(@getRange(), text, normalizeLineEndings: false)
|
||||
|
||||
# Public: Gets the range of the buffer contents.
|
||||
#
|
||||
# Returns a new {Range}, from `[0, 0]` to the end of the buffer.
|
||||
getRange: ->
|
||||
new Range([0, 0], [@getLastRow(), @getLastLine().length])
|
||||
|
||||
# Public: Given a range, returns the lines of text within it.
|
||||
#
|
||||
# range - A {Range} object specifying your points of interest
|
||||
#
|
||||
# Returns a {String} of the combined lines.
|
||||
getTextInRange: (range) ->
|
||||
range = @clipRange(range)
|
||||
if range.start.row == range.end.row
|
||||
@@ -144,9 +197,17 @@ class Buffer
|
||||
|
||||
return multipleLines.join ''
|
||||
|
||||
# Public: Gets all the lines in a file.
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
getLines: ->
|
||||
@lines
|
||||
|
||||
# Public: Given a row, returns the line of text.
|
||||
#
|
||||
# row - A {Number} indicating the row.
|
||||
#
|
||||
# Returns a {String}.
|
||||
lineForRow: (row) ->
|
||||
@lines[row]
|
||||
|
||||
@@ -156,27 +217,51 @@ class Buffer
|
||||
suggestedLineEndingForRow: (row) ->
|
||||
@lineEndingForRow(row) ? @lineEndingForRow(row - 1)
|
||||
|
||||
# Public: Given a row, returns the length of the line of text.
|
||||
#
|
||||
# row - A {Number} indicating the row.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
lineLengthForRow: (row) ->
|
||||
@lines[row].length
|
||||
|
||||
lineEndingLengthForRow: (row) ->
|
||||
(@lineEndingForRow(row) ? '').length
|
||||
|
||||
# Public: Given a buffer row, this retrieves the range for that line.
|
||||
#
|
||||
# row - A {Number} identifying the row
|
||||
# options - A hash with one key, `includeNewline`, which specifies whether you
|
||||
# want to include the trailing newline
|
||||
#
|
||||
# Returns a {Range}.
|
||||
rangeForRow: (row, { includeNewline } = {}) ->
|
||||
if includeNewline and row < @getLastRow()
|
||||
new Range([row, 0], [row + 1, 0])
|
||||
else
|
||||
new Range([row, 0], [row, @lineLengthForRow(row)])
|
||||
|
||||
# Public: Gets the number of lines in a file.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getLineCount: ->
|
||||
@getLines().length
|
||||
|
||||
# Public: Gets the row number of the last line.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getLastRow: ->
|
||||
@getLines().length - 1
|
||||
|
||||
# Public: Finds the last line in the current buffer.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getLastLine: ->
|
||||
@lineForRow(@getLastRow())
|
||||
|
||||
# Public: Finds the last point in the current buffer.
|
||||
#
|
||||
# Returns a {Point} representing the last position.
|
||||
getEofPosition: ->
|
||||
lastRow = @getLastRow()
|
||||
new Point(lastRow, @lineLengthForRow(lastRow))
|
||||
@@ -197,9 +282,16 @@ class Buffer
|
||||
|
||||
new Point(row, index)
|
||||
|
||||
# Public: Given a row, this deletes it from the buffer.
|
||||
#
|
||||
# row - A {Number} representing the row to delete
|
||||
deleteRow: (row) ->
|
||||
@deleteRows(row, row)
|
||||
|
||||
# Public: Deletes a range of rows from the buffer.
|
||||
#
|
||||
# start - A {Number} representing the starting row
|
||||
# end - A {Number} representing the ending row
|
||||
deleteRows: (start, end) ->
|
||||
startPoint = null
|
||||
endPoint = null
|
||||
@@ -215,21 +307,39 @@ class Buffer
|
||||
|
||||
@delete(new Range(startPoint, endPoint))
|
||||
|
||||
# Public: Adds text to the end of the buffer.
|
||||
#
|
||||
# text - A {String} of text to add
|
||||
append: (text) ->
|
||||
@insert(@getEofPosition(), text)
|
||||
|
||||
# Public: Adds text to a specific point in the buffer
|
||||
#
|
||||
# point - A {Point} in the buffer to insert into
|
||||
# text - A {String} of text to add
|
||||
insert: (point, text) ->
|
||||
@change(new Range(point, point), text)
|
||||
|
||||
# Public: Deletes text from the buffer
|
||||
#
|
||||
# range - A {Range} whose text to delete
|
||||
delete: (range) ->
|
||||
@change(range, '')
|
||||
|
||||
# Internal:
|
||||
change: (oldRange, newText, options) ->
|
||||
oldRange = Range.fromObject(oldRange)
|
||||
operation = new BufferChangeOperation({buffer: this, oldRange, newText, options})
|
||||
range = @pushOperation(operation)
|
||||
range
|
||||
|
||||
# Public: Given a position, this clips it to a real position.
|
||||
#
|
||||
# For example, if `position`'s row exceeds the row count of the buffer,
|
||||
# or if its column goes beyond a line's length, this "sanitizes" the value
|
||||
# to a real position.
|
||||
#
|
||||
# Returns the new, clipped {Point}. Note that this could be the same as `position` if no clipping was performed.
|
||||
clipPosition: (position) ->
|
||||
position = Point.fromObject(position)
|
||||
eofPosition = @getEofPosition()
|
||||
@@ -240,7 +350,16 @@ class Buffer
|
||||
column = Math.max(position.column, 0)
|
||||
column = Math.min(@lineLengthForRow(row), column)
|
||||
new Point(row, column)
|
||||
|
||||
|
||||
# Public: Given a range, this clips it to a real range.
|
||||
#
|
||||
# For example, if `range`'s row exceeds the row count of the buffer,
|
||||
# or if its column goes beyond a line's length, this "sanitizes" the value
|
||||
# to a real range.
|
||||
#
|
||||
# range - The {Point} to clip
|
||||
#
|
||||
# Returns the new, clipped {Point}. Note that this could be the same as `range` if no clipping was performed.
|
||||
clipRange: (range) ->
|
||||
range = Range.fromObject(range)
|
||||
new Range(@clipPosition(range.start), @clipPosition(range.end))
|
||||
@@ -249,21 +368,33 @@ class Buffer
|
||||
prefix: @lines[range.start.row][0...range.start.column]
|
||||
suffix: @lines[range.end.row][range.end.column..]
|
||||
|
||||
# Internal:
|
||||
pushOperation: (operation, editSession) ->
|
||||
if @undoManager
|
||||
@undoManager.pushOperation(operation, editSession)
|
||||
else
|
||||
operation.do()
|
||||
|
||||
# Internal:
|
||||
transact: (fn) -> @undoManager.transact(fn)
|
||||
# Public: Undos the last operation.
|
||||
#
|
||||
# editSession - The {EditSession} associated with the buffer.
|
||||
undo: (editSession) -> @undoManager.undo(editSession)
|
||||
# Public: Redos the last operation.
|
||||
#
|
||||
# editSession - The {EditSession} associated with the buffer.
|
||||
redo: (editSession) -> @undoManager.redo(editSession)
|
||||
commit: -> @undoManager.commit()
|
||||
abort: -> @undoManager.abort()
|
||||
|
||||
# Public: Saves the buffer.
|
||||
save: ->
|
||||
@saveAs(@getPath()) if @isModified()
|
||||
|
||||
# Public: Saves the buffer at a specific path.
|
||||
#
|
||||
# path - The path to save at.
|
||||
saveAs: (path) ->
|
||||
unless path then throw new Error("Can't save buffer with no file path")
|
||||
|
||||
@@ -274,22 +405,40 @@ class Buffer
|
||||
@triggerModifiedStatusChanged(false)
|
||||
@trigger 'saved'
|
||||
|
||||
# Public: Identifies if the buffer was modified.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isModified: ->
|
||||
if @file
|
||||
@getText() != @cachedDiskContents
|
||||
else
|
||||
not @isEmpty()
|
||||
|
||||
# Public: Identifies if a buffer is in a git conflict with `HEAD`.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isInConflict: -> @conflict
|
||||
|
||||
# Public: Identifies if a buffer is empty.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isEmpty: -> @lines.length is 1 and @lines[0].length is 0
|
||||
|
||||
getMarkers: ->
|
||||
_.values(@validMarkers)
|
||||
|
||||
# Public: Retrieves the quantity of markers in a buffer.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getMarkerCount: ->
|
||||
_.size(@validMarkers)
|
||||
|
||||
# Public: Constructs a new marker at a given range.
|
||||
#
|
||||
# range - The marker {Range} (representing the distance between the head and tail)
|
||||
# options - Options to pass to the {BufferMarker} constructor
|
||||
#
|
||||
# Returns a {Number} representing the new marker's ID.
|
||||
markRange: (range, options={}) ->
|
||||
marker = new BufferMarker(_.defaults({
|
||||
id: (@nextMarkerId++).toString()
|
||||
@@ -299,9 +448,18 @@ class Buffer
|
||||
@validMarkers[marker.id] = marker
|
||||
marker.id
|
||||
|
||||
# Public: Constructs a new marker at a given position.
|
||||
#
|
||||
# position - The marker {Point}; there won't be a tail
|
||||
# options - Options to pass to the {BufferMarker} constructor
|
||||
#
|
||||
# Returns a {Number} representing the new marker's ID.
|
||||
markPosition: (position, options) ->
|
||||
@markRange([position, position], _.defaults({noTail: true}, options))
|
||||
|
||||
# Public: Removes the marker with the given id.
|
||||
#
|
||||
# id - The {Number} of the ID to remove
|
||||
destroyMarker: (id) ->
|
||||
delete @validMarkers[id]
|
||||
delete @invalidMarkers[id]
|
||||
@@ -312,39 +470,110 @@ class Buffer
|
||||
setMarkerPosition: (args...) ->
|
||||
@setMarkerHeadPosition(args...)
|
||||
|
||||
# Public: Retrieves the position of the marker's head.
|
||||
#
|
||||
# id - A {Number} representing the marker to check
|
||||
#
|
||||
# Returns a {Point}, or `null` if the marker does not exist.
|
||||
getMarkerHeadPosition: (id) ->
|
||||
@validMarkers[id]?.getHeadPosition()
|
||||
|
||||
# Public: Sets the position of the marker's head.
|
||||
#
|
||||
# id - A {Number} representing the marker to change
|
||||
# position - The new {Point} to place the head
|
||||
# options - A hash with the following keys:
|
||||
# :clip - if `true`, the point is [clipped]{Buffer.clipPosition}
|
||||
# :bufferChanged - if `true`, indicates that the {Buffer} should trigger an event that it's changed
|
||||
#
|
||||
# Returns a {Point} representing the new head position.
|
||||
setMarkerHeadPosition: (id, position, options) ->
|
||||
@validMarkers[id]?.setHeadPosition(position)
|
||||
|
||||
# Public: Retrieves the position of the marker's tail.
|
||||
#
|
||||
# id - A {Number} representing the marker to check
|
||||
#
|
||||
# Returns a {Point}, or `null` if the marker does not exist.
|
||||
getMarkerTailPosition: (id) ->
|
||||
@validMarkers[id]?.getTailPosition()
|
||||
|
||||
# Public: Sets the position of the marker's tail.
|
||||
#
|
||||
# id - A {Number} representing the marker to change
|
||||
# position - The new {Point} to place the tail
|
||||
# options - A hash with the following keys:
|
||||
# :clip - if `true`, the point is [clipped]{Buffer.clipPosition}
|
||||
# :bufferChanged - if `true`, indicates that the {Buffer} should trigger an event that it's changed
|
||||
#
|
||||
# Returns a {Point} representing the new tail position.
|
||||
setMarkerTailPosition: (id, position, options) ->
|
||||
@validMarkers[id]?.setTailPosition(position)
|
||||
|
||||
# Public: Retrieves the {Range} between a marker's head and its tail.
|
||||
#
|
||||
# id - A {Number} representing the marker to check
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getMarkerRange: (id) ->
|
||||
@validMarkers[id]?.getRange()
|
||||
|
||||
# Public: Sets the marker's range, potentialy modifying both its head and tail.
|
||||
#
|
||||
# id - A {Number} representing the marker to change
|
||||
# range - The new {Range} the marker should cover
|
||||
# options - A hash of options with the following keys:
|
||||
# :reverse - if `true`, the marker is reversed; that is, its tail is "above" the head
|
||||
# :noTail - if `true`, the marker doesn't have a tail
|
||||
setMarkerRange: (id, range, options) ->
|
||||
@validMarkers[id]?.setRange(range, options)
|
||||
|
||||
# Public: Sets the marker's tail to the same position as the marker's head.
|
||||
#
|
||||
# This only works if there isn't already a tail position.
|
||||
#
|
||||
# id - A {Number} representing the marker to change
|
||||
#
|
||||
# Returns a {Point} representing the new tail position.
|
||||
placeMarkerTail: (id) ->
|
||||
@validMarkers[id]?.placeTail()
|
||||
|
||||
# Public: Removes the tail from the marker.
|
||||
#
|
||||
# id - A {Number} representing the marker to change
|
||||
clearMarkerTail: (id) ->
|
||||
@validMarkers[id]?.clearTail()
|
||||
|
||||
# Public: Identifies if the ending position of a marker is greater than the starting position.
|
||||
#
|
||||
# This can happen when, for example, you highlight text "up" in a {Buffer}.
|
||||
#
|
||||
# id - A {Number} representing the marker to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isMarkerReversed: (id) ->
|
||||
@validMarkers[id]?.isReversed()
|
||||
|
||||
# Public: Identifies if the marker's head position is equal to its tail.
|
||||
#
|
||||
# id - A {Number} representing the marker to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isMarkerRangeEmpty: (id) ->
|
||||
@validMarkers[id]?.isRangeEmpty()
|
||||
|
||||
# Public: Sets a callback to be fired whenever a marker is changed.
|
||||
#
|
||||
# id - A {Number} representing the marker to watch
|
||||
# callback - A {Function} to execute
|
||||
observeMarker: (id, callback) ->
|
||||
@validMarkers[id]?.observe(callback)
|
||||
|
||||
# Public: Given a buffer position, this finds all markers that contain the position.
|
||||
#
|
||||
# bufferPosition - A {Point} to check
|
||||
#
|
||||
# Returns an {Array} of {Numbers}, representing marker IDs containing `bufferPosition`.
|
||||
markersForPosition: (bufferPosition) ->
|
||||
bufferPosition = Point.fromObject(bufferPosition)
|
||||
ids = []
|
||||
@@ -352,6 +581,13 @@ class Buffer
|
||||
ids.push(id) if marker.containsPoint(bufferPosition)
|
||||
ids
|
||||
|
||||
# Public: Identifies if a character sequence is within a certain range.
|
||||
#
|
||||
# regex - The {RegExp} to check
|
||||
# startIndex - The starting row {Number}
|
||||
# endIndex - The ending row {Number}
|
||||
#
|
||||
# Returns an {Array} of {RegExp}s, representing the matches
|
||||
matchesInCharacterRange: (regex, startIndex, endIndex) ->
|
||||
text = @getText()
|
||||
matches = []
|
||||
@@ -375,9 +611,19 @@ class Buffer
|
||||
|
||||
matches
|
||||
|
||||
# Public: Scans for text in the buffer, calling a function on each match.
|
||||
#
|
||||
# regex - A {RegExp} representing the text to find
|
||||
# iterator - A {Function} that's called on each match
|
||||
scan: (regex, iterator) ->
|
||||
@scanInRange(regex, @getRange(), iterator)
|
||||
|
||||
# Public: Scans for text in a given range, calling a function on each match.
|
||||
#
|
||||
# regex - A {RegExp} representing the text to find
|
||||
# range - A {Range} in the buffer to search within
|
||||
# iterator - A {Function} that's called on each match
|
||||
# reverse - A {Boolean} indicating if the search should be backwards (default: `false`)
|
||||
scanInRange: (regex, range, iterator, reverse=false) ->
|
||||
range = @clipRange(range)
|
||||
global = regex.global
|
||||
@@ -415,12 +661,28 @@ class Buffer
|
||||
|
||||
break unless global and keepLooping
|
||||
|
||||
# Public: Scans for text in a given range _backwards_, calling a function on each match.
|
||||
#
|
||||
# regex - A {RegExp} representing the text to find
|
||||
# range - A {Range} in the buffer to search within
|
||||
# iterator - A {Function} that's called on each match
|
||||
backwardsScanInRange: (regex, range, iterator) ->
|
||||
@scanInRange regex, range, iterator, true
|
||||
|
||||
# Public: Given a row, identifies if it is blank.
|
||||
#
|
||||
# row - A row {Number} to check
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isRowBlank: (row) ->
|
||||
not /\S/.test @lineForRow(row)
|
||||
|
||||
# Public: Given a row, this finds the next row above it that's empty.
|
||||
#
|
||||
# startRow - A {Number} identifying the row to start checking at
|
||||
#
|
||||
# Returns the row {Number} of the first blank row.
|
||||
# Returns `null` if there's no other blank row.
|
||||
previousNonBlankRow: (startRow) ->
|
||||
return null if startRow == 0
|
||||
|
||||
@@ -429,6 +691,12 @@ class Buffer
|
||||
return row unless @isRowBlank(row)
|
||||
null
|
||||
|
||||
# Public: Given a row, this finds the next row that's blank.
|
||||
#
|
||||
# startRow - A row {Number} to check
|
||||
#
|
||||
# Returns the row {Number} of the next blank row.
|
||||
# Returns `null` if there's no other blank row.
|
||||
nextNonBlankRow: (startRow) ->
|
||||
lastRow = @getLastRow()
|
||||
if startRow < lastRow
|
||||
@@ -436,17 +704,32 @@ class Buffer
|
||||
return row unless @isRowBlank(row)
|
||||
null
|
||||
|
||||
# Public: Identifies if the buffer has soft tabs anywhere.
|
||||
#
|
||||
# Returns a {Boolean},
|
||||
usesSoftTabs: ->
|
||||
for line in @getLines()
|
||||
if match = line.match(/^\s/)
|
||||
return match[0][0] != '\t'
|
||||
undefined
|
||||
|
||||
# Public: Checks out the current `HEAD` revision of the file.
|
||||
checkoutHead: ->
|
||||
path = @getPath()
|
||||
return unless path
|
||||
git?.checkoutHead(path)
|
||||
|
||||
# Public: Checks to see if a file exists.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
fileExists: ->
|
||||
@file? && @file.exists()
|
||||
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
scheduleModifiedEvents: ->
|
||||
clearTimeout(@stoppedChangingTimeout) if @stoppedChangingTimeout
|
||||
stoppedChangingCallback = =>
|
||||
@@ -461,9 +744,6 @@ class Buffer
|
||||
@previousModifiedStatus = modifiedStatus
|
||||
@trigger 'modified-status-changed', modifiedStatus
|
||||
|
||||
fileExists: ->
|
||||
@file? && @file.exists()
|
||||
|
||||
logLines: (start=0, end=@getLastRow())->
|
||||
for row in [start..end]
|
||||
line = @lineForRow(row)
|
||||
|
||||
@@ -6,6 +6,10 @@ Token = require 'token'
|
||||
nodePath = require 'path'
|
||||
pathSplitRegex = new RegExp("[#{nodePath.sep}.]")
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class TextMateGrammar
|
||||
@readFromPath: (path) ->
|
||||
@@ -346,6 +350,10 @@ class Pattern
|
||||
|
||||
tokens
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
shiftCapture = (captureIndices) ->
|
||||
[captureIndices.shift(), captureIndices.shift(), captureIndices.shift()]
|
||||
|
||||
|
||||
@@ -5,6 +5,10 @@ _ = require 'underscore'
|
||||
TextMateGrammar = require 'text-mate-grammar'
|
||||
async = require 'async'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class TextMatePackage extends Package
|
||||
@testName: (packageName) ->
|
||||
|
||||
@@ -3,6 +3,10 @@ fsUtils = require 'fs-utils'
|
||||
plist = require 'plist'
|
||||
Theme = require 'theme'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class TextMateTheme extends Theme
|
||||
@testPath: (path) ->
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
fsUtils = require 'fs-utils'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class Theme
|
||||
@stylesheets: null
|
||||
|
||||
@@ -5,6 +5,10 @@ Token = require 'token'
|
||||
Range = require 'range'
|
||||
Point = require 'point'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class TokenizedBuffer
|
||||
@idCounter: 1
|
||||
@@ -33,9 +37,15 @@ class TokenizedBuffer
|
||||
setVisible: (@visible) ->
|
||||
@tokenizeInBackground() if @visible
|
||||
|
||||
# Public: Retrieves the current tab length.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getTabLength: ->
|
||||
@tabLength
|
||||
|
||||
# Public: Specifies the tab length.
|
||||
#
|
||||
# tabLength - A {Number} that defines the new tab length.
|
||||
setTabLength: (@tabLength) ->
|
||||
lastRow = @buffer.getLastRow()
|
||||
@screenLines = @buildPlaceholderScreenLinesForRows(0, lastRow)
|
||||
@@ -219,6 +229,9 @@ class TokenizedBuffer
|
||||
stop()
|
||||
position
|
||||
|
||||
# Public: Gets the row number of the last line.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getLastRow: ->
|
||||
@buffer.getLastRow()
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
_ = require 'underscore'
|
||||
|
||||
# Internal: The object in charge of managing redo and undo operations.
|
||||
module.exports =
|
||||
|
||||
class UndoManager
|
||||
undoHistory: null
|
||||
redoHistory: null
|
||||
@@ -79,4 +79,4 @@ class UndoManager
|
||||
batch.newSelectionRanges
|
||||
catch e
|
||||
@clear()
|
||||
throw e
|
||||
throw e
|
||||
@@ -10,6 +10,10 @@ require 'space-pen-extensions'
|
||||
deserializers = {}
|
||||
deferredDeserializers = {}
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
# This method is called in any window needing a general environment, including specs
|
||||
window.setUpEnvironment = ->
|
||||
Config = require 'config'
|
||||
@@ -221,5 +225,6 @@ window.profile = (description, fn) ->
|
||||
console.profileEnd(description)
|
||||
value
|
||||
|
||||
# Public: Shows a dialog asking if the window was _really_ meant to be closed.
|
||||
confirmClose = ->
|
||||
rootView.confirmClose().done -> window.close()
|
||||
|
||||
Reference in New Issue
Block a user