From 7c483f989fc95ccd2dff95dead4cd0b0673f4158 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 18 Sep 2014 16:43:00 -0700 Subject: [PATCH 01/13] Clean up marker --- src/marker.coffee | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/marker.coffee b/src/marker.coffee index de28e3f10..e54fc5248 100644 --- a/src/marker.coffee +++ b/src/marker.coffee @@ -12,7 +12,7 @@ Grim = require 'grim' # # ### Marker Creation # -# You will use {Editor::markBufferRange} rather than creating Markers directly. +# Use {Editor::markBufferRange} rather than creating Markers directly. # # ### Head and Tail # @@ -128,7 +128,7 @@ class Marker EmitterMixin::on.apply(this, arguments) ### - Section: Marker Metadata + Section: Marker Details ### # Essential: Returns a {Boolean} indicating whether the marker is valid. Markers can be @@ -143,11 +143,11 @@ class Marker isDestroyed: -> @bufferMarker.isDestroyed() - # Extended: Returns a {Boolean} indicating whether the head precedes the tail. + # Essential: Returns a {Boolean} indicating whether the head precedes the tail. isReversed: -> @bufferMarker.isReversed() - # Extended: Get the invalidation strategy for this marker. + # Essential: Get the invalidation strategy for this marker. # # Valid values include: `never`, `surround`, `overlap`, `inside`, and `touch`. # @@ -155,7 +155,7 @@ class Marker getInvalidationStrategy: -> @bufferMarker.getInvalidationStrategy() - # Extended: Returns an {Object} containing any custom properties associated with + # Essential: Returns an {Object} containing any custom properties associated with # the marker. getProperties: -> @bufferMarker.getProperties() @@ -163,7 +163,7 @@ class Marker deprecate 'Use Marker::getProperties instead' @getProperties() - # Extended: Merges an {Object} containing new properties into the marker's + # Essential: Merges an {Object} containing new properties into the marker's # existing properties. # # * `properties` {Object} From 69f54b90dc10b4b99a45aef275d476899087b340 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 18 Sep 2014 16:43:13 -0700 Subject: [PATCH 02/13] Clean up atom details section --- src/atom.coffee | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/atom.coffee b/src/atom.coffee index 04361bf36..40b52b9d6 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -226,40 +226,34 @@ class Atom extends Model @emitter.on 'did-beep', callback ### - Section: Atom Metadata + Section: Atom Details ### - # Essential: Is the current window in development mode? + # Public: Is the current window in development mode? inDevMode: -> @getLoadSettings().devMode - # Essential: Is the current window running specs? + # Public: Is the current window running specs? inSpecMode: -> @getLoadSettings().isSpec - # Essential: Get the version of the Atom application. + # Public: Get the version of the Atom application. # # Returns the version text {String}. getVersion: -> @appVersion ?= @getLoadSettings().appVersion - # Essential: Determine whether the current version is an official release. + # Public: Determine whether the current version is an official release. isReleasedVersion: -> not /\w{7}/.test(@getVersion()) # Check if the release is a 7-character SHA prefix - # Essential: Get the directory path to Atom's configuration area. + # Public: Get the directory path to Atom's configuration area. # # Returns the absolute path to `~/.atom`. getConfigDirPath: -> @constructor.getConfigDirPath() - # Extended: Get the load settings for the current window. - # - # Returns an {Object} containing all the load setting key/value pairs. - getLoadSettings: -> - @constructor.getLoadSettings() - - # Extended: Get the time taken to completely load the current window. + # Public: Get the time taken to completely load the current window. # # This time include things like loading and activating packages, creating # DOM elements for the editor, and reading the config. @@ -269,6 +263,12 @@ class Atom extends Model getWindowLoadTime: -> @loadTime + # Public: Get the load settings for the current window. + # + # Returns an {Object} containing all the load setting key/value pairs. + getLoadSettings: -> + @constructor.getLoadSettings() + ### Section: Managing The Atom Window ### From e2d9e5bd743decb391c6b9ba6462f90f35393d27 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 18 Sep 2014 16:44:26 -0700 Subject: [PATCH 03/13] Make focusing other pane views from workspaceView private --- src/workspace-view.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 547d8daa9..ce4bca28f 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -303,16 +303,16 @@ class WorkspaceView extends View # Focus the next pane by id. focusNextPaneView: -> @model.activateNextPane() - # Essential: Focus the pane directly above the active pane. + # Focus the pane directly above the active pane. focusPaneViewAbove: -> @panes.focusPaneViewAbove() - # Essential: Focus the pane directly below the active pane. + # Focus the pane directly below the active pane. focusPaneViewBelow: -> @panes.focusPaneViewBelow() - # Essential: Focus the pane directly to the left of the active pane. + # Focus the pane directly to the left of the active pane. focusPaneViewOnLeft: -> @panes.focusPaneViewOnLeft() - # Essential: Focus the pane directly to the right of the active pane. + # Focus the pane directly to the right of the active pane. focusPaneViewOnRight: -> @panes.focusPaneViewOnRight() ### From d02c3e0d62960dbde74897a2848b0701bbdce9f8 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 18 Sep 2014 16:45:13 -0700 Subject: [PATCH 04/13] WorkspaceView is extended --- src/workspace-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index ce4bca28f..4c5c2fe3e 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -20,7 +20,7 @@ atom.commands.add '.workspace', 'window:decrease-font-size': -> @getModel().decreaseFontSize() 'window:reset-font-size': -> @getModel().resetFontSize() -# Essential: The top-level view for the entire window. An instance of this class is +# Extended: The top-level view for the entire window. An instance of this class is # available via the `atom.workspaceView` global. # # It is backed by a model object, an instance of {Workspace}, which is available From 97931ff259d538c00ef54841ad19e72704e23109 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 18 Sep 2014 16:51:30 -0700 Subject: [PATCH 05/13] Move observePaneItems and onDidChangePaneItem to essential --- src/workspace.coffee | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index db97e6732..da2b7bf10 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -103,6 +103,25 @@ class Workspace extends Model callback(textEditor) for textEditor in @getTextEditors() @onDidAddTextEditor ({textEditor}) -> callback(textEditor) + # Essential: Invoke the given callback with all current and future panes items in + # the workspace. + # + # * `callback` {Function} to be called with current and future pane items. + # * `item` An item that is present in {::getPaneItems} at the time of + # subscription or that is added at some later time. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observePaneItems: (callback) -> @paneContainer.observePaneItems(callback) + + # Essential: Invoke the given callback when the active pane item changes. + # + # * `callback` {Function} to be called when the active pane item changes. + # * `event` {Object} with the following keys: + # * `activeItem` The active pane item. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidChangeActivePaneItem: (callback) -> @paneContainer.onDidChangeActivePaneItem(callback) + # Essential: Invoke the given callback whenever an item is opened. Unlike # {::onDidAddPaneItem}, observers will be notified for items that are already # present in the workspace when they are reopened. @@ -167,25 +186,6 @@ class Workspace extends Model # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidAddPaneItem: (callback) -> @paneContainer.onDidAddPaneItem(callback) - # Extended: Invoke the given callback when the active pane item changes. - # - # * `callback` {Function} to be called when the active pane item changes. - # * `event` {Object} with the following keys: - # * `activeItem` The active pane item. - # - # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. - onDidChangeActivePaneItem: (callback) -> @paneContainer.onDidChangeActivePaneItem(callback) - - # Extended: Invoke the given callback with all current and future panes items in - # the workspace. - # - # * `callback` {Function} to be called with current and future pane items. - # * `item` An item that is present in {::getPaneItems} at the time of - # subscription or that is added at some later time. - # - # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. - observePaneItems: (callback) -> @paneContainer.observePaneItems(callback) - # Extended: Invoke the given callback when a text editor is added to the # workspace. # From 2d3ea244ee36d22d170e8030b89c935a85826251 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 18 Sep 2014 16:51:59 -0700 Subject: [PATCH 06/13] Opener stuff is essential --- src/workspace.coffee | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index da2b7bf10..11989e834 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -323,7 +323,7 @@ class Workspace extends Model .catch (error) -> console.error(error.stack ? error) - # Extended: Asynchronously reopens the last-closed item's URI if it hasn't already been + # Public: Asynchronously reopens the last-closed item's URI if it hasn't already been # reopened. # # Returns a promise that is resolved when the item is opened @@ -339,7 +339,9 @@ class Workspace extends Model if uri = @destroyedItemUris.pop() @openSync(uri) - # Extended: Register an opener for a uri. + # TODO: make ::registerOpener() return a disposable + + # Public: Register an opener for a uri. # # An {Editor} will be used if no openers return a value. # @@ -355,7 +357,7 @@ class Workspace extends Model registerOpener: (opener) -> @openers.push(opener) - # Extended: Unregister an opener registered with {::registerOpener}. + # Unregister an opener registered with {::registerOpener}. unregisterOpener: (opener) -> _.remove(@openers, opener) From f407ca3a0c15d63baa8089f63ec1d6002d37aba2 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 18 Sep 2014 16:52:11 -0700 Subject: [PATCH 07/13] saveAll is private --- src/workspace.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index 11989e834..76450181b 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -398,7 +398,7 @@ class Workspace extends Model getActiveEditor: -> @activePane?.getActiveEditor() - # Extended: Save all pane items. + # Save all pane items. saveAll: -> @paneContainer.saveAll() From 29f53d4432bf65da0a1a308fd672d38cf3b192b8 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 18 Sep 2014 16:52:56 -0700 Subject: [PATCH 08/13] PaneView is private --- src/pane-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane-view.coffee b/src/pane-view.coffee index ddeab69af..0dca0bf6c 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -6,7 +6,7 @@ PropertyAccessors = require 'property-accessors' Pane = require './pane' -# Extended: A container which can contains multiple items to be switched between. +# A container which can contains multiple items to be switched between. # # Items can be almost anything however most commonly they're {EditorView}s. # From 33827d1dc890b1e079a2e5cc7ca68ba2726a804a Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 18 Sep 2014 16:59:06 -0700 Subject: [PATCH 09/13] Upgrade text-buffer for better docs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 782e98884..093fb8e9f 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "serializable": "^1", "space-pen": "3.4.7", "temp": "0.7.0", - "text-buffer": "^3.2.5", + "text-buffer": "^3.2.6", "theorist": "^1.0.2", "underscore-plus": "^1.5.1", "vm-compatibility-layer": "0.1.0" From 9f1aabed0a3438331590365cadc7ae2120a32b22 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 18 Sep 2014 17:20:18 -0700 Subject: [PATCH 10/13] Reorg Editor a bit based on conversations Move heavier used sections near the top of the file. Make some of the single extended methods public. --- src/editor.coffee | 1091 ++++++++++++++++++++++----------------------- 1 file changed, 543 insertions(+), 548 deletions(-) diff --git a/src/editor.coffee b/src/editor.coffee index fa38b5444..a4226afaa 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -545,7 +545,7 @@ class Editor extends Model atom.clipboard.write(filePath) ### - Section: Saving + Section: File Operations ### # Essential: Saves the editor's text buffer. @@ -553,14 +553,14 @@ class Editor extends Model # See {TextBuffer::save} for more details. save: -> @buffer.save() - # Extended: Saves the editor's text buffer as the given path. + # Public: Saves the editor's text buffer as the given path. # # See {TextBuffer::saveAs} for more details. # # * `filePath` A {String} path. saveAs: (filePath) -> @buffer.saveAs(filePath) - # Extended: Determine whether the user should be prompted to save before closing + # Determine whether the user should be prompted to save before closing # this editor. shouldPromptToSave: -> @isModified() and not @buffer.hasMultipleEditors() @@ -663,7 +663,7 @@ class Editor extends Model # {Delegates to: TextBuffer.getEndPosition} getEofBufferPosition: -> @buffer.getEndPosition() - # Extended: Get the {Range} of the paragraph surrounding the most recently added + # Public: Get the {Range} of the paragraph surrounding the most recently added # cursor. # # Returns a {Range}. @@ -686,6 +686,46 @@ class Editor extends Model # Returns the {Range} of the newly-inserted text. setTextInBufferRange: (range, text, normalizeLineEndings) -> @getBuffer().setTextInRange(range, text, normalizeLineEndings) + # Essential: For each selection, replace the selected text with the given text. + # + # * `text` A {String} representing the text to insert. + # * `options` (optional) See {Selection::insertText}. + # + # Returns a {Range} when the text has been inserted + # Returns a {Bool} false when the text has not been inserted + insertText: (text, options={}) -> + willInsert = true + cancel = -> willInsert = false + willInsertEvent = {cancel, text} + @emit('will-insert-text', willInsertEvent) + @emitter.emit 'will-insert-text', willInsertEvent + + if willInsert + options.autoIndentNewline ?= @shouldAutoIndent() + options.autoDecreaseIndent ?= @shouldAutoIndent() + @mutateSelectedText (selection) => + range = selection.insertText(text, options) + didInsertEvent = {text, range} + @emit('did-insert-text', didInsertEvent) + @emitter.emit 'did-insert-text', didInsertEvent + range + else + false + + # Essential: For each selection, replace the selected text with a newline. + insertNewline: -> + @insertText('\n') + + # Essential: For each selection, if the selection is empty, delete the character + # preceding the cursor. Otherwise delete the selected text. + delete: -> + @mutateSelectedText (selection) -> selection.delete() + + # Essential: For each selection, if the selection is empty, delete the character + # preceding the cursor. Otherwise delete the selected text. + backspace: -> + @mutateSelectedText (selection) -> selection.backspace() + # Extended: Mutate the text of all the selections in a single transaction. # # All the changes made inside the given {Function} can be reverted with a @@ -914,68 +954,6 @@ class Editor extends Model joinLines: -> @mutateSelectedText (selection) -> selection.joinLines() - # Extended: Batch multiple operations as a single undo/redo step. - # - # Any group of operations that are logically grouped from the perspective of - # undoing and redoing should be performed in a transaction. If you want to - # abort the transaction, call {::abortTransaction} to terminate the function's - # execution and revert any changes performed up to the abortion. - # - # * `fn` A {Function} to call inside the transaction. - transact: (fn) -> @buffer.transact(fn) - - # Extended: Start an open-ended transaction. - # - # Call {::commitTransaction} or {::abortTransaction} to terminate the - # transaction. If you nest calls to transactions, only the outermost - # transaction is considered. You must match every begin with a matching - # commit, but a single call to abort will cancel all nested transactions. - beginTransaction: -> @buffer.beginTransaction() - - # Extended: Commit an open-ended transaction started with {::beginTransaction} - # and push it to the undo stack. - # - # If transactions are nested, only the outermost commit takes effect. - commitTransaction: -> @buffer.commitTransaction() - - # Extended: Abort an open transaction, undoing any operations performed so far - # within the transaction. - abortTransaction: -> @buffer.abortTransaction() - - ### - Section: Inserting Text - ### - - # Essential: For each selection, replace the selected text with the given text. - # - # * `text` A {String} representing the text to insert. - # * `options` (optional) See {Selection::insertText}. - # - # Returns a {Range} when the text has been inserted - # Returns a {Bool} false when the text has not been inserted - insertText: (text, options={}) -> - willInsert = true - cancel = -> willInsert = false - willInsertEvent = {cancel, text} - @emit('will-insert-text', willInsertEvent) - @emitter.emit 'will-insert-text', willInsertEvent - - if willInsert - options.autoIndentNewline ?= @shouldAutoIndent() - options.autoDecreaseIndent ?= @shouldAutoIndent() - @mutateSelectedText (selection) => - range = selection.insertText(text, options) - didInsertEvent = {text, range} - @emit('did-insert-text', didInsertEvent) - @emitter.emit 'did-insert-text', didInsertEvent - range - else - false - - # Essential: For each selection, replace the selected text with a newline. - insertNewline: -> - @insertText('\n') - # Extended: For each cursor, insert a newline at beginning the following line. insertNewlineBelow: -> @transact => @@ -1000,20 +978,6 @@ class Editor extends Model @moveUp() @moveToEndOfLine() - ### - Section: Removing Text - ### - - # Essential: For each selection, if the selection is empty, delete the character - # preceding the cursor. Otherwise delete the selected text. - delete: -> - @mutateSelectedText (selection) -> selection.delete() - - # Essential: For each selection, if the selection is empty, delete the character - # preceding the cursor. Otherwise delete the selected text. - backspace: -> - @mutateSelectedText (selection) -> selection.backspace() - # Extended: For each selection, if the selection is empty, delete all characters # of the containing word that precede the cursor. Otherwise delete the # selected text. @@ -1054,222 +1018,7 @@ class Editor extends Model @deleteToBeginningOfLine() ### - Section: Searching Text - ### - - # Essential: Scan regular expression matches in the entire buffer, calling the - # given iterator function on each match. - # - # If you're programmatically modifying the results, you may want to try - # {::backwardsScanInBufferRange} to avoid tripping over your own changes. - # - # * `regex` A {RegExp} to search for. - # * `iterator` A {Function} that's called on each match with an {Object} - # containing the following keys: - # * `match` The current regular expression match. - # * `matchText` A {String} with the text of the match. - # * `range` The {Range} of the match. - # * `stop` Call this {Function} to terminate the scan. - # * `replace` Call this {Function} with a {String} to replace the match. - scan: (regex, iterator) -> @buffer.scan(regex, iterator) - - # Extended: Scan regular expression matches in a given range, calling the given - # iterator function on each match. - # - # * `regex` A {RegExp} to search for. - # * `range` A {Range} in which to search. - # * `iterator` A {Function} that's called on each match with an {Object} - # containing the following keys: - # * `match` The current regular expression match. - # * `matchText` A {String} with the text of the match. - # * `range` The {Range} of the match. - # * `stop` Call this {Function} to terminate the scan. - # * `replace` Call this {Function} with a {String} to replace the match. - scanInBufferRange: (regex, range, iterator) -> @buffer.scanInRange(regex, range, iterator) - - # Extended: Scan regular expression matches in a given range in reverse order, - # calling the given iterator function on each match. - # - # * `regex` A {RegExp} to search for. - # * `range` A {Range} in which to search. - # * `iterator` A {Function} that's called on each match with an {Object} - # containing the following keys: - # * `match` The current regular expression match. - # * `matchText` A {String} with the text of the match. - # * `range` The {Range} of the match. - # * `stop` Call this {Function} to terminate the scan. - # * `replace` Call this {Function} with a {String} to replace the match. - backwardsScanInBufferRange: (regex, range, iterator) -> @buffer.backwardsScanInRange(regex, range, iterator) - - ### - Section: Tab Behavior - ### - - # Essential: Returns a {Boolean} indicating whether softTabs are enabled for this - # editor. - getSoftTabs: -> @softTabs - - # Essential: Enable or disable soft tabs for this editor. - # - # * `softTabs` A {Boolean} - setSoftTabs: (@softTabs) -> @softTabs - - # Essential: Toggle soft tabs for this editor - toggleSoftTabs: -> @setSoftTabs(not @getSoftTabs()) - - # Essential: Get the on-screen length of tab characters. - # - # Returns a {Number}. - getTabLength: -> @displayBuffer.getTabLength() - - # Essential: Set the on-screen length of tab characters. - # - # * `tabLength` {Number} length of a single tab - setTabLength: (tabLength) -> @displayBuffer.setTabLength(tabLength) - - # Extended: Determine if the buffer uses hard or soft tabs. - # - # Returns `true` if the first non-comment line with leading whitespace starts - # with a space character. Returns `false` if it starts with a hard tab (`\t`). - # - # Returns a {Boolean} or undefined if no non-comment lines had leading - # whitespace. - usesSoftTabs: -> - for bufferRow in [0..@buffer.getLastRow()] - continue if @displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment() - - line = @buffer.lineForRow(bufferRow) - return true if line[0] is ' ' - return false if line[0] is '\t' - - undefined - - # Extended: Get the text representing a single level of indent. - # - # If soft tabs are enabled, the text is composed of N spaces, where N is the - # tab length. Otherwise the text is a tab character (`\t`). - # - # Returns a {String}. - getTabText: -> @buildIndentString(1) - - # If soft tabs are enabled, convert all hard tabs to soft tabs in the given - # {Range}. - normalizeTabsInBufferRange: (bufferRange) -> - return unless @getSoftTabs() - @scanInBufferRange /\t/g, bufferRange, ({replace}) => replace(@getTabText()) - - ### - Section: Soft Wrap Behavior - ### - - # Essential: Determine whether lines in this editor are soft-wrapped. - # - # Returns a {Boolean}. - isSoftWrapped: (softWrapped) -> @displayBuffer.isSoftWrapped() - getSoftWrapped: -> - deprecate("Use Editor::isSoftWrapped instead") - @displayBuffer.isSoftWrapped() - - # Essential: Enable or disable soft wrapping for this editor. - # - # * `softWrapped` A {Boolean} - # - # Returns a {Boolean}. - setSoftWrapped: (softWrapped) -> @displayBuffer.setSoftWrapped(softWrapped) - setSoftWrap: (softWrapped) -> - deprecate("Use Editor::setSoftWrapped instead") - @setSoftWrapped(softWrapped) - - # Essential: Toggle soft wrapping for this editor - # - # Returns a {Boolean}. - toggleSoftWrapped: -> @setSoftWrapped(not @isSoftWrapped()) - toggleSoftWrap: -> - deprecate("Use Editor::toggleSoftWrapped instead") - @toggleSoftWrapped() - - # Extended: Gets the column at which column will soft wrap - getSoftWrapColumn: -> @displayBuffer.getSoftWrapColumn() - - ### - Section: Indentation - ### - - # Essential: Get the indentation level of the given a buffer row. - # - # Returns how deeply the given row is indented based on the soft tabs and - # tab length settings of this editor. Note that if soft tabs are enabled and - # the tab length is 2, a row with 4 leading spaces would have an indentation - # level of 2. - # - # * `bufferRow` A {Number} indicating the buffer row. - # - # Returns a {Number}. - indentationForBufferRow: (bufferRow) -> - @indentLevelForLine(@lineTextForBufferRow(bufferRow)) - - # Essential: Set the indentation level for the given buffer row. - # - # Inserts or removes hard tabs or spaces based on the soft tabs and tab length - # settings of this editor in order to bring it to the given indentation level. - # Note that if soft tabs are enabled and the tab length is 2, a row with 4 - # leading spaces would have an indentation level of 2. - # - # * `bufferRow` A {Number} indicating the buffer row. - # * `newLevel` A {Number} indicating the new indentation level. - # * `options` (optional) An {Object} with the following keys: - # * `preserveLeadingWhitespace` `true` to preserve any whitespace already at - # the beginning of the line (default: false). - setIndentationForBufferRow: (bufferRow, newLevel, {preserveLeadingWhitespace}={}) -> - if preserveLeadingWhitespace - endColumn = 0 - else - endColumn = @lineTextForBufferRow(bufferRow).match(/^\s*/)[0].length - newIndentString = @buildIndentString(newLevel) - @buffer.setTextInRange([[bufferRow, 0], [bufferRow, endColumn]], newIndentString) - - # Extended: Indent rows intersecting selections by one level. - indentSelectedRows: -> - @mutateSelectedText (selection) -> selection.indentSelectedRows() - - # Extended: Outdent rows intersecting selections by one level. - outdentSelectedRows: -> - @mutateSelectedText (selection) -> selection.outdentSelectedRows() - - # Extended: Get the indentation level of the given line of text. - # - # Returns how deeply the given line is indented based on the soft tabs and - # tab length settings of this editor. Note that if soft tabs are enabled and - # the tab length is 2, a row with 4 leading spaces would have an indentation - # level of 2. - # - # * `line` A {String} representing a line of text. - # - # Returns a {Number}. - indentLevelForLine: (line) -> - @displayBuffer.indentLevelForLine(line) - - # Extended: Indent rows intersecting selections based on the grammar's suggested - # indent level. - autoIndentSelectedRows: -> - @mutateSelectedText (selection) -> selection.autoIndentSelectedRows() - - # Indent all lines intersecting selections. See {Selection::indent} for more - # information. - indent: (options={}) -> - options.autoIndent ?= @shouldAutoIndent() - @mutateSelectedText (selection) -> selection.indent(options) - - # Constructs the string used for tabs. - buildIndentString: (number, column=0) -> - if @getSoftTabs() - tabStopViolation = column % @getTabLength() - _.multiplyString(" ", Math.floor(number * @getTabLength()) - tabStopViolation) - else - _.multiplyString("\t", Math.floor(number)) - - ### - Section: Undo Operations + Section: History ### # Essential: Undo the last change. @@ -1282,6 +1031,34 @@ class Editor extends Model @getLastCursor().needsAutoscroll = true @buffer.redo(this) + # Extended: Batch multiple operations as a single undo/redo step. + # + # Any group of operations that are logically grouped from the perspective of + # undoing and redoing should be performed in a transaction. If you want to + # abort the transaction, call {::abortTransaction} to terminate the function's + # execution and revert any changes performed up to the abortion. + # + # * `fn` A {Function} to call inside the transaction. + transact: (fn) -> @buffer.transact(fn) + + # Extended: Start an open-ended transaction. + # + # Call {::commitTransaction} or {::abortTransaction} to terminate the + # transaction. If you nest calls to transactions, only the outermost + # transaction is considered. You must match every begin with a matching + # commit, but a single call to abort will cancel all nested transactions. + beginTransaction: -> @buffer.beginTransaction() + + # Extended: Commit an open-ended transaction started with {::beginTransaction} + # and push it to the undo stack. + # + # If transactions are nested, only the outermost commit takes effect. + commitTransaction: -> @buffer.commitTransaction() + + # Extended: Abort an open transaction, undoing any operations performed so far + # within the transaction. + abortTransaction: -> @buffer.abortTransaction() + ### Section: Editor Coordinates ### @@ -1376,256 +1153,6 @@ class Editor extends Model # Returns a {Point}. clipScreenPosition: (screenPosition, options) -> @displayBuffer.clipScreenPosition(screenPosition, options) - ### - Section: Grammars - ### - - # Essential: Get the current {Grammar} of this editor. - getGrammar: -> - @displayBuffer.getGrammar() - - # Essential: Set the current {Grammar} of this editor. - # - # Assigning a grammar will cause the editor to re-tokenize based on the new - # grammar. - # - # * `grammar` {Grammar} - setGrammar: (grammar) -> - @displayBuffer.setGrammar(grammar) - - # Reload the grammar based on the file name. - reloadGrammar: -> - @displayBuffer.reloadGrammar() - - ### - Section: Managing Syntax Scopes - ### - - # Public: Get the syntactic scopes for the most recently added cursor's - # position. See {::scopesForBufferPosition} for more information. - # - # Returns an {Array} of {String}s. - scopesAtCursor: -> @getLastCursor().getScopes() - getCursorScopes: -> - deprecate 'Use Editor::scopesAtCursor() instead' - @scopesAtCursor() - - # Essential: Get the syntactic scopes for the given position in buffer - # coordinates. - # - # For example, if called with a position inside the parameter list of an - # anonymous CoffeeScript function, the method returns the following array: - # `["source.coffee", "meta.inline.function.coffee", "variable.parameter.function.coffee"]` - # - # * `bufferPosition` A {Point} or {Array} of [row, column]. - # - # Returns an {Array} of {String}s. - scopesForBufferPosition: (bufferPosition) -> @displayBuffer.scopesForBufferPosition(bufferPosition) - - # Extended: Get the range in buffer coordinates of all tokens surrounding the - # cursor that match the given scope selector. - # - # For example, if you wanted to find the string surrounding the cursor, you - # could call `editor.bufferRangeForScopeAtCursor(".string.quoted")`. - # - # Returns a {Range}. - bufferRangeForScopeAtCursor: (selector) -> - @displayBuffer.bufferRangeForScopeAtPosition(selector, @getCursorBufferPosition()) - - logCursorScope: -> - console.log @scopesAtCursor() - - # {Delegates to: DisplayBuffer.tokenForBufferPosition} - tokenForBufferPosition: (bufferPosition) -> @displayBuffer.tokenForBufferPosition(bufferPosition) - - # Extended: Determine if the given row is entirely a comment - isBufferRowCommented: (bufferRow) -> - if match = @lineTextForBufferRow(bufferRow).match(/\S/) - scopes = @tokenForBufferPosition([bufferRow, match.index]).scopes - @commentScopeSelector ?= new TextMateScopeSelector('comment.*') - @commentScopeSelector.matches(scopes) - - ### - Section: Clipboard Operations - ### - - # Essential: For each selection, copy the selected text. - copySelectedText: -> - maintainClipboard = false - for selection in @getSelections() - selection.copy(maintainClipboard) - maintainClipboard = true - - # Essential: For each selection, cut the selected text. - cutSelectedText: -> - maintainClipboard = false - @mutateSelectedText (selection) -> - selection.cut(maintainClipboard) - maintainClipboard = true - - # Essential: For each selection, replace the selected text with the contents of - # the clipboard. - # - # If the clipboard contains the same number of selections as the current - # editor, each selection will be replaced with the content of the - # corresponding clipboard selection text. - # - # * `options` (optional) See {Selection::insertText}. - pasteText: (options={}) -> - {text, metadata} = atom.clipboard.readWithMetadata() - - containsNewlines = text.indexOf('\n') isnt -1 - - if metadata?.selections? and metadata.selections.length is @getSelections().length - @mutateSelectedText (selection, index) -> - text = metadata.selections[index] - selection.insertText(text, options) - - return - - else if atom.config.get("editor.normalizeIndentOnPaste") and metadata?.indentBasis? - if !@getLastCursor().hasPrecedingCharactersOnLine() or containsNewlines - options.indentBasis ?= metadata.indentBasis - - @insertText(text, options) - - # Extended: For each selection, if the selection is empty, cut all characters - # of the containing line following the cursor. Otherwise cut the selected - # text. - cutToEndOfLine: -> - maintainClipboard = false - @mutateSelectedText (selection) -> - selection.cutToEndOfLine(maintainClipboard) - maintainClipboard = true - - ### - Section: Folds - ### - - # Essential: Fold the most recent cursor's row based on its indentation level. - # - # The fold will extend from the nearest preceding line with a lower - # indentation level up to the nearest following row with a lower indentation - # level. - foldCurrentRow: -> - bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row - @foldBufferRow(bufferRow) - - # Essential: Unfold the most recent cursor's row by one level. - unfoldCurrentRow: -> - bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row - @unfoldBufferRow(bufferRow) - - # Essential: Fold the given row in buffer coordinates based on its indentation - # level. - # - # If the given row is foldable, the fold will begin there. Otherwise, it will - # begin at the first foldable row preceding the given row. - # - # * `bufferRow` A {Number}. - foldBufferRow: (bufferRow) -> - @languageMode.foldBufferRow(bufferRow) - - # Essential: Unfold all folds containing the given row in buffer coordinates. - # - # * `bufferRow` A {Number} - unfoldBufferRow: (bufferRow) -> - @displayBuffer.unfoldBufferRow(bufferRow) - - # Extended: For each selection, fold the rows it intersects. - foldSelectedLines: -> - selection.fold() for selection in @getSelections() - - # Extended: Fold all foldable lines. - foldAll: -> - @languageMode.foldAll() - - # Extended: Unfold all existing folds. - unfoldAll: -> - @languageMode.unfoldAll() - - # Extended: Fold all foldable lines at the given indent level. - # - # * `level` A {Number}. - foldAllAtIndentLevel: (level) -> - @languageMode.foldAllAtIndentLevel(level) - - # Extended: Determine whether the given row in buffer coordinates is foldable. - # - # A *foldable* row is a row that *starts* a row range that can be folded. - # - # * `bufferRow` A {Number} - # - # Returns a {Boolean}. - isFoldableAtBufferRow: (bufferRow) -> - @languageMode.isFoldableAtBufferRow(bufferRow) - - # Extended: Determine whether the given row in screen coordinates is foldable. - # - # A *foldable* row is a row that *starts* a row range that can be folded. - # - # * `bufferRow` A {Number} - # - # Returns a {Boolean}. - isFoldableAtScreenRow: (screenRow) -> - bufferRow = @displayBuffer.bufferRowForScreenRow(screenRow) - @isFoldableAtBufferRow(bufferRow) - - # Extended: Fold the given buffer row if it isn't currently folded, and unfold - # it otherwise. - toggleFoldAtBufferRow: (bufferRow) -> - if @isFoldedAtBufferRow(bufferRow) - @unfoldBufferRow(bufferRow) - else - @foldBufferRow(bufferRow) - - # Extended: Determine whether the most recently added cursor's row is folded. - # - # Returns a {Boolean}. - isFoldedAtCursorRow: -> - @isFoldedAtScreenRow(@getCursorScreenPosition().row) - - # Extended: Determine whether the given row in buffer coordinates is folded. - # - # * `bufferRow` A {Number} - # - # Returns a {Boolean}. - isFoldedAtBufferRow: (bufferRow) -> - @displayBuffer.isFoldedAtBufferRow(bufferRow) - - # Extended: Determine whether the given row in screen coordinates is folded. - # - # * `screenRow` A {Number} - # - # Returns a {Boolean}. - isFoldedAtScreenRow: (screenRow) -> - @displayBuffer.isFoldedAtScreenRow(screenRow) - - # TODO: Rename to foldRowRange? - createFold: (startRow, endRow) -> - @displayBuffer.createFold(startRow, endRow) - - # {Delegates to: DisplayBuffer.destroyFoldWithId} - destroyFoldWithId: (id) -> - @displayBuffer.destroyFoldWithId(id) - - # Remove any {Fold}s found that intersect the given buffer row. - destroyFoldsIntersectingBufferRange: (bufferRange) -> - for row in [bufferRange.start.row..bufferRange.end.row] - @unfoldBufferRow(row) - - # {Delegates to: DisplayBuffer.largestFoldContainingBufferRow} - largestFoldContainingBufferRow: (bufferRow) -> - @displayBuffer.largestFoldContainingBufferRow(bufferRow) - - # {Delegates to: DisplayBuffer.largestFoldStartingAtScreenRow} - largestFoldStartingAtScreenRow: (screenRow) -> - @displayBuffer.largestFoldStartingAtScreenRow(screenRow) - - # {Delegates to: DisplayBuffer.outermostFoldsForBufferRowRange} - outermostFoldsInBufferRowRange: (startRow, endRow) -> - @displayBuffer.outermostFoldsInBufferRowRange(startRow, endRow) - ### Section: Decorations ### @@ -1671,7 +1198,7 @@ class Editor extends Model decorateMarker: (marker, decorationParams) -> @displayBuffer.decorateMarker(marker, decorationParams) - # Extended: Get all the decorations within a screen row range. + # Public: Get all the decorations within a screen row range. # # * `startScreenRow` the {Number} beginning screen row # * `endScreenRow` the {Number} end screen row (inclusive) @@ -1790,10 +1317,12 @@ class Editor extends Model @displayBuffer.findMarkers(properties) # Extended: Get the {Marker} for the given marker id. + # + # * `id` {Number} id of the marker getMarker: (id) -> @displayBuffer.getMarker(id) - # Extended: Get all {Marker}s. + # Extended: Get all {Marker}s. Consider using {::findMarkers} getMarkers: -> @displayBuffer.getMarkers() @@ -2562,6 +2091,472 @@ class Editor extends Model @emit 'selection-screen-range-changed', selection @emitter.emit 'did-change-selection-range', selection + ### + Section: Searching and Replacing + ### + + # Essential: Scan regular expression matches in the entire buffer, calling the + # given iterator function on each match. + # + # `::scan` functions as the replace method as well via the `replace` + # + # If you're programmatically modifying the results, you may want to try + # {::backwardsScanInBufferRange} to avoid tripping over your own changes. + # + # * `regex` A {RegExp} to search for. + # * `iterator` A {Function} that's called on each match + # * `object` {Object} + # * `match` The current regular expression match. + # * `matchText` A {String} with the text of the match. + # * `range` The {Range} of the match. + # * `stop` Call this {Function} to terminate the scan. + # * `replace` Call this {Function} with a {String} to replace the match. + scan: (regex, iterator) -> @buffer.scan(regex, iterator) + + # Public: Scan regular expression matches in a given range, calling the given + # iterator function on each match. + # + # * `regex` A {RegExp} to search for. + # * `range` A {Range} in which to search. + # * `iterator` A {Function} that's called on each match with an {Object} + # containing the following keys: + # * `match` The current regular expression match. + # * `matchText` A {String} with the text of the match. + # * `range` The {Range} of the match. + # * `stop` Call this {Function} to terminate the scan. + # * `replace` Call this {Function} with a {String} to replace the match. + scanInBufferRange: (regex, range, iterator) -> @buffer.scanInRange(regex, range, iterator) + + # Public: Scan regular expression matches in a given range in reverse order, + # calling the given iterator function on each match. + # + # * `regex` A {RegExp} to search for. + # * `range` A {Range} in which to search. + # * `iterator` A {Function} that's called on each match with an {Object} + # containing the following keys: + # * `match` The current regular expression match. + # * `matchText` A {String} with the text of the match. + # * `range` The {Range} of the match. + # * `stop` Call this {Function} to terminate the scan. + # * `replace` Call this {Function} with a {String} to replace the match. + backwardsScanInBufferRange: (regex, range, iterator) -> @buffer.backwardsScanInRange(regex, range, iterator) + + ### + Section: Tab Behavior + ### + + # Essential: Returns a {Boolean} indicating whether softTabs are enabled for this + # editor. + getSoftTabs: -> @softTabs + + # Essential: Enable or disable soft tabs for this editor. + # + # * `softTabs` A {Boolean} + setSoftTabs: (@softTabs) -> @softTabs + + # Essential: Toggle soft tabs for this editor + toggleSoftTabs: -> @setSoftTabs(not @getSoftTabs()) + + # Essential: Get the on-screen length of tab characters. + # + # Returns a {Number}. + getTabLength: -> @displayBuffer.getTabLength() + + # Essential: Set the on-screen length of tab characters. + # + # * `tabLength` {Number} length of a single tab + setTabLength: (tabLength) -> @displayBuffer.setTabLength(tabLength) + + # Extended: Determine if the buffer uses hard or soft tabs. + # + # Returns `true` if the first non-comment line with leading whitespace starts + # with a space character. Returns `false` if it starts with a hard tab (`\t`). + # + # Returns a {Boolean} or undefined if no non-comment lines had leading + # whitespace. + usesSoftTabs: -> + for bufferRow in [0..@buffer.getLastRow()] + continue if @displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment() + + line = @buffer.lineForRow(bufferRow) + return true if line[0] is ' ' + return false if line[0] is '\t' + + undefined + + # Extended: Get the text representing a single level of indent. + # + # If soft tabs are enabled, the text is composed of N spaces, where N is the + # tab length. Otherwise the text is a tab character (`\t`). + # + # Returns a {String}. + getTabText: -> @buildIndentString(1) + + # If soft tabs are enabled, convert all hard tabs to soft tabs in the given + # {Range}. + normalizeTabsInBufferRange: (bufferRange) -> + return unless @getSoftTabs() + @scanInBufferRange /\t/g, bufferRange, ({replace}) => replace(@getTabText()) + + ### + Section: Soft Wrap Behavior + ### + + # Essential: Determine whether lines in this editor are soft-wrapped. + # + # Returns a {Boolean}. + isSoftWrapped: (softWrapped) -> @displayBuffer.isSoftWrapped() + getSoftWrapped: -> + deprecate("Use Editor::isSoftWrapped instead") + @displayBuffer.isSoftWrapped() + + # Essential: Enable or disable soft wrapping for this editor. + # + # * `softWrapped` A {Boolean} + # + # Returns a {Boolean}. + setSoftWrapped: (softWrapped) -> @displayBuffer.setSoftWrapped(softWrapped) + setSoftWrap: (softWrapped) -> + deprecate("Use Editor::setSoftWrapped instead") + @setSoftWrapped(softWrapped) + + # Essential: Toggle soft wrapping for this editor + # + # Returns a {Boolean}. + toggleSoftWrapped: -> @setSoftWrapped(not @isSoftWrapped()) + toggleSoftWrap: -> + deprecate("Use Editor::toggleSoftWrapped instead") + @toggleSoftWrapped() + + # Public: Gets the column at which column will soft wrap + getSoftWrapColumn: -> @displayBuffer.getSoftWrapColumn() + + ### + Section: Indentation + ### + + # Essential: Get the indentation level of the given a buffer row. + # + # Returns how deeply the given row is indented based on the soft tabs and + # tab length settings of this editor. Note that if soft tabs are enabled and + # the tab length is 2, a row with 4 leading spaces would have an indentation + # level of 2. + # + # * `bufferRow` A {Number} indicating the buffer row. + # + # Returns a {Number}. + indentationForBufferRow: (bufferRow) -> + @indentLevelForLine(@lineTextForBufferRow(bufferRow)) + + # Essential: Set the indentation level for the given buffer row. + # + # Inserts or removes hard tabs or spaces based on the soft tabs and tab length + # settings of this editor in order to bring it to the given indentation level. + # Note that if soft tabs are enabled and the tab length is 2, a row with 4 + # leading spaces would have an indentation level of 2. + # + # * `bufferRow` A {Number} indicating the buffer row. + # * `newLevel` A {Number} indicating the new indentation level. + # * `options` (optional) An {Object} with the following keys: + # * `preserveLeadingWhitespace` `true` to preserve any whitespace already at + # the beginning of the line (default: false). + setIndentationForBufferRow: (bufferRow, newLevel, {preserveLeadingWhitespace}={}) -> + if preserveLeadingWhitespace + endColumn = 0 + else + endColumn = @lineTextForBufferRow(bufferRow).match(/^\s*/)[0].length + newIndentString = @buildIndentString(newLevel) + @buffer.setTextInRange([[bufferRow, 0], [bufferRow, endColumn]], newIndentString) + + # Extended: Indent rows intersecting selections by one level. + indentSelectedRows: -> + @mutateSelectedText (selection) -> selection.indentSelectedRows() + + # Extended: Outdent rows intersecting selections by one level. + outdentSelectedRows: -> + @mutateSelectedText (selection) -> selection.outdentSelectedRows() + + # Extended: Get the indentation level of the given line of text. + # + # Returns how deeply the given line is indented based on the soft tabs and + # tab length settings of this editor. Note that if soft tabs are enabled and + # the tab length is 2, a row with 4 leading spaces would have an indentation + # level of 2. + # + # * `line` A {String} representing a line of text. + # + # Returns a {Number}. + indentLevelForLine: (line) -> + @displayBuffer.indentLevelForLine(line) + + # Extended: Indent rows intersecting selections based on the grammar's suggested + # indent level. + autoIndentSelectedRows: -> + @mutateSelectedText (selection) -> selection.autoIndentSelectedRows() + + # Indent all lines intersecting selections. See {Selection::indent} for more + # information. + indent: (options={}) -> + options.autoIndent ?= @shouldAutoIndent() + @mutateSelectedText (selection) -> selection.indent(options) + + # Constructs the string used for tabs. + buildIndentString: (number, column=0) -> + if @getSoftTabs() + tabStopViolation = column % @getTabLength() + _.multiplyString(" ", Math.floor(number * @getTabLength()) - tabStopViolation) + else + _.multiplyString("\t", Math.floor(number)) + + ### + Section: Grammars + ### + + # Essential: Get the current {Grammar} of this editor. + getGrammar: -> + @displayBuffer.getGrammar() + + # Essential: Set the current {Grammar} of this editor. + # + # Assigning a grammar will cause the editor to re-tokenize based on the new + # grammar. + # + # * `grammar` {Grammar} + setGrammar: (grammar) -> + @displayBuffer.setGrammar(grammar) + + # Reload the grammar based on the file name. + reloadGrammar: -> + @displayBuffer.reloadGrammar() + + ### + Section: Managing Syntax Scopes + ### + + # Public: Get the syntactic scopes for the most recently added cursor's + # position. See {::scopesForBufferPosition} for more information. + # + # Returns an {Array} of {String}s. + scopesAtCursor: -> @getLastCursor().getScopes() + getCursorScopes: -> + deprecate 'Use Editor::scopesAtCursor() instead' + @scopesAtCursor() + + # Essential: Get the syntactic scopes for the given position in buffer + # coordinates. + # + # For example, if called with a position inside the parameter list of an + # anonymous CoffeeScript function, the method returns the following array: + # `["source.coffee", "meta.inline.function.coffee", "variable.parameter.function.coffee"]` + # + # * `bufferPosition` A {Point} or {Array} of [row, column]. + # + # Returns an {Array} of {String}s. + scopesForBufferPosition: (bufferPosition) -> @displayBuffer.scopesForBufferPosition(bufferPosition) + + # Extended: Get the range in buffer coordinates of all tokens surrounding the + # cursor that match the given scope selector. + # + # For example, if you wanted to find the string surrounding the cursor, you + # could call `editor.bufferRangeForScopeAtCursor(".string.quoted")`. + # + # Returns a {Range}. + bufferRangeForScopeAtCursor: (selector) -> + @displayBuffer.bufferRangeForScopeAtPosition(selector, @getCursorBufferPosition()) + + logCursorScope: -> + console.log @scopesAtCursor() + + # {Delegates to: DisplayBuffer.tokenForBufferPosition} + tokenForBufferPosition: (bufferPosition) -> @displayBuffer.tokenForBufferPosition(bufferPosition) + + # Extended: Determine if the given row is entirely a comment + isBufferRowCommented: (bufferRow) -> + if match = @lineTextForBufferRow(bufferRow).match(/\S/) + scopes = @tokenForBufferPosition([bufferRow, match.index]).scopes + @commentScopeSelector ?= new TextMateScopeSelector('comment.*') + @commentScopeSelector.matches(scopes) + + ### + Section: Clipboard Operations + ### + + # Essential: For each selection, copy the selected text. + copySelectedText: -> + maintainClipboard = false + for selection in @getSelections() + selection.copy(maintainClipboard) + maintainClipboard = true + + # Essential: For each selection, cut the selected text. + cutSelectedText: -> + maintainClipboard = false + @mutateSelectedText (selection) -> + selection.cut(maintainClipboard) + maintainClipboard = true + + # Essential: For each selection, replace the selected text with the contents of + # the clipboard. + # + # If the clipboard contains the same number of selections as the current + # editor, each selection will be replaced with the content of the + # corresponding clipboard selection text. + # + # * `options` (optional) See {Selection::insertText}. + pasteText: (options={}) -> + {text, metadata} = atom.clipboard.readWithMetadata() + + containsNewlines = text.indexOf('\n') isnt -1 + + if metadata?.selections? and metadata.selections.length is @getSelections().length + @mutateSelectedText (selection, index) -> + text = metadata.selections[index] + selection.insertText(text, options) + + return + + else if atom.config.get("editor.normalizeIndentOnPaste") and metadata?.indentBasis? + if !@getLastCursor().hasPrecedingCharactersOnLine() or containsNewlines + options.indentBasis ?= metadata.indentBasis + + @insertText(text, options) + + # Public: For each selection, if the selection is empty, cut all characters + # of the containing line following the cursor. Otherwise cut the selected + # text. + cutToEndOfLine: -> + maintainClipboard = false + @mutateSelectedText (selection) -> + selection.cutToEndOfLine(maintainClipboard) + maintainClipboard = true + + ### + Section: Folds + ### + + # Essential: Fold the most recent cursor's row based on its indentation level. + # + # The fold will extend from the nearest preceding line with a lower + # indentation level up to the nearest following row with a lower indentation + # level. + foldCurrentRow: -> + bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row + @foldBufferRow(bufferRow) + + # Essential: Unfold the most recent cursor's row by one level. + unfoldCurrentRow: -> + bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row + @unfoldBufferRow(bufferRow) + + # Essential: Fold the given row in buffer coordinates based on its indentation + # level. + # + # If the given row is foldable, the fold will begin there. Otherwise, it will + # begin at the first foldable row preceding the given row. + # + # * `bufferRow` A {Number}. + foldBufferRow: (bufferRow) -> + @languageMode.foldBufferRow(bufferRow) + + # Essential: Unfold all folds containing the given row in buffer coordinates. + # + # * `bufferRow` A {Number} + unfoldBufferRow: (bufferRow) -> + @displayBuffer.unfoldBufferRow(bufferRow) + + # Extended: For each selection, fold the rows it intersects. + foldSelectedLines: -> + selection.fold() for selection in @getSelections() + + # Extended: Fold all foldable lines. + foldAll: -> + @languageMode.foldAll() + + # Extended: Unfold all existing folds. + unfoldAll: -> + @languageMode.unfoldAll() + + # Extended: Fold all foldable lines at the given indent level. + # + # * `level` A {Number}. + foldAllAtIndentLevel: (level) -> + @languageMode.foldAllAtIndentLevel(level) + + # Extended: Determine whether the given row in buffer coordinates is foldable. + # + # A *foldable* row is a row that *starts* a row range that can be folded. + # + # * `bufferRow` A {Number} + # + # Returns a {Boolean}. + isFoldableAtBufferRow: (bufferRow) -> + @languageMode.isFoldableAtBufferRow(bufferRow) + + # Extended: Determine whether the given row in screen coordinates is foldable. + # + # A *foldable* row is a row that *starts* a row range that can be folded. + # + # * `bufferRow` A {Number} + # + # Returns a {Boolean}. + isFoldableAtScreenRow: (screenRow) -> + bufferRow = @displayBuffer.bufferRowForScreenRow(screenRow) + @isFoldableAtBufferRow(bufferRow) + + # Extended: Fold the given buffer row if it isn't currently folded, and unfold + # it otherwise. + toggleFoldAtBufferRow: (bufferRow) -> + if @isFoldedAtBufferRow(bufferRow) + @unfoldBufferRow(bufferRow) + else + @foldBufferRow(bufferRow) + + # Extended: Determine whether the most recently added cursor's row is folded. + # + # Returns a {Boolean}. + isFoldedAtCursorRow: -> + @isFoldedAtScreenRow(@getCursorScreenPosition().row) + + # Extended: Determine whether the given row in buffer coordinates is folded. + # + # * `bufferRow` A {Number} + # + # Returns a {Boolean}. + isFoldedAtBufferRow: (bufferRow) -> + @displayBuffer.isFoldedAtBufferRow(bufferRow) + + # Extended: Determine whether the given row in screen coordinates is folded. + # + # * `screenRow` A {Number} + # + # Returns a {Boolean}. + isFoldedAtScreenRow: (screenRow) -> + @displayBuffer.isFoldedAtScreenRow(screenRow) + + # TODO: Rename to foldRowRange? + createFold: (startRow, endRow) -> + @displayBuffer.createFold(startRow, endRow) + + # {Delegates to: DisplayBuffer.destroyFoldWithId} + destroyFoldWithId: (id) -> + @displayBuffer.destroyFoldWithId(id) + + # Remove any {Fold}s found that intersect the given buffer row. + destroyFoldsIntersectingBufferRange: (bufferRange) -> + for row in [bufferRange.start.row..bufferRange.end.row] + @unfoldBufferRow(row) + + # {Delegates to: DisplayBuffer.largestFoldContainingBufferRow} + largestFoldContainingBufferRow: (bufferRow) -> + @displayBuffer.largestFoldContainingBufferRow(bufferRow) + + # {Delegates to: DisplayBuffer.largestFoldStartingAtScreenRow} + largestFoldStartingAtScreenRow: (screenRow) -> + @displayBuffer.largestFoldStartingAtScreenRow(screenRow) + + # {Delegates to: DisplayBuffer.outermostFoldsForBufferRowRange} + outermostFoldsInBufferRowRange: (startRow, endRow) -> + @displayBuffer.outermostFoldsInBufferRowRange(startRow, endRow) ### Section: Scrolling the Editor From 7632e5dd40361d05c48b5a507c9480115096fe06 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 18 Sep 2014 17:24:24 -0700 Subject: [PATCH 11/13] Reorg decoration --- src/decoration.coffee | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/decoration.coffee b/src/decoration.coffee index 1728ce225..45e265044 100644 --- a/src/decoration.coffee +++ b/src/decoration.coffee @@ -59,6 +59,19 @@ class Decoration @markerDestroyDisposable = @marker.onDidDestroy => @destroy() + # Essential: Destroy this marker. + # + # If you own the marker, you should use {Marker::destroy} which will destroy + # this decoration. + destroy: -> + return if @destroyed + @markerDestroyDisposable.dispose() + @markerDestroyDisposable = null + @destroyed = true + @emit 'destroyed' + @emitter.emit 'did-destroy' + @emitter.dispose() + ### Section: Event Subscription ### @@ -83,7 +96,7 @@ class Decoration @emitter.on 'did-destroy', callback ### - Section: Methods + Section: Decoration Details ### # Essential: An id unique across all {Decoration} objects @@ -102,6 +115,10 @@ class Decoration isType: (type) -> Decoration.isType(@properties, type) + ### + Section: Properties + ### + # Essential: Returns the {Decoration}'s properties. getProperties: -> @properties @@ -129,18 +146,9 @@ class Decoration Grim.deprecate 'Use Decoration::setProperties instead' @setProperties(newProperties) - # Essential: Destroy this marker. - # - # If you own the marker, you should use {Marker::destroy} which will destroy - # this decoration. - destroy: -> - return if @destroyed - @markerDestroyDisposable.dispose() - @markerDestroyDisposable = null - @destroyed = true - @emit 'destroyed' - @emitter.emit 'did-destroy' - @emitter.dispose() + ### + Section: Private methods + ### matchesPattern: (decorationPattern) -> return false unless decorationPattern? From 2af699f35ead0737d6b6246f6526173c6562fc14 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 18 Sep 2014 17:27:08 -0700 Subject: [PATCH 12/13] Rename cursor sections --- src/cursor.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cursor.coffee b/src/cursor.coffee index 1848182db..112065ff1 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -166,7 +166,7 @@ class Cursor extends Model @getBufferPosition().isEqual(@getCurrentLineBufferRange().end) ### - Section: Info about the cursor position + Section: Cursor Position Details ### # Public: Identifies if the cursor is surrounded by whitespace. @@ -412,7 +412,7 @@ class Cursor extends Model @setBufferPosition(position) ### - Section: Retrieving Positions and Ranges of local boundaries + Section: Local Positions and Ranges ### # Public: Retrieves the buffer position of where the current word starts. From e5096d8190aedc096a65547cef53369f096bfd3b Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 18 Sep 2014 18:40:46 -0700 Subject: [PATCH 13/13] Data -> Details --- src/git.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/git.coffee b/src/git.coffee index 4093f8890..339debf3b 100644 --- a/src/git.coffee +++ b/src/git.coffee @@ -151,7 +151,7 @@ class Git EmitterMixin::on.apply(this, arguments) ### - Section: Repository Data + Section: Repository Details ### # Public: Returns the {String} path of the repository.