Moved payment widget to vmPayment branch

error: pathspec 'client/landing/app/CommonViews/paymentwidgets.coffee' did not match any file(s) known to git

--- client/landing/app/CommonViews/paymentwidgets.coffee
+++ client/landing/app/CommonViews/paymentwidgets.coffee
@@ -0,0 +1,112 @@
+class PaymentWidget extends KDView
+  constructor:(options, data)->
+    super options, data
+
+    @planCode = options.planCode
+
+    @loader = new KDLoaderView
+      size:
+        width: 20
+
+    @buttonSubscribe = new KDButtonView
+      title    : "Subscribe"
+      cssClass : "hidden"
+      callback : => @confirmSubscription ->
+                      @subscribe (err, sub)=>
+                        @updateButtons()
+
+    @buttonBilling = new KDButtonView
+      title    : "Billing Info"
+      cssClass : "hidden"
+      callback : => @askBillingInfo (status)=>
+                      @updateButtons()
+
+    @widgetContent = options.content
+    @widgetContent.setClass "hidden"
+
+    @buttonSubscribe.setClass options.contentCssClass
+    @buttonBilling.setClass options.contentCssClass
+
+    @updateButtons()
+
+  updateButtons:->
+    @loader.show()
+    @checkBilling (needBilling)=>
+      if needBilling
+        @loader.hide()
+        @buttonBilling.show()
+      else
+        @checkSubscription (status)=>
+          @loader.hide()
+          if status
+            @widgetContent.show()
+          else
+            @buttonSubscribe.show()
+
+  checkBilling:(callback)->
+    paymentController = KD.getSingleton('paymentController')
+    group             = KD.getSingleton("groupsController").getCurrentGroup()
+
+    paymentController.getBillingInfo 'user', group, (err, account)=>
+      callback err or not account or not account.cardNumber
+
+  checkSubscription:(callback)->
+    KD.remote.api.JRecurlySubscription.checkUserSubscription
+      planCode: @planCode
+    , (err, subs)=>
+      subscribed = no
+      subs.forEach (sub)=>
+        if sub.status in ['canceled', 'active']
+          subscribed = yes
+      callback subscribed
+
+  pistachio:->
+    """
+    {{> @loader}}
+    {{> @buttonBilling}}
+    {{> @buttonSubscribe}}
+    {{> @widgetContent}}
+    """
+
+  viewAppended:->
+    @setTemplate @pistachio()
+    @template.update()
+    @loader.show()
+
+  subscribe:(callback)->
+    KD.remote.api.JRecurlyPlan.getPlanWithCode @planCode, (err, plan)=>
+      if not err and plan
+        plan.subscribe callback
+
+  askBillingInfo:(callback)->
+    paymentController = KD.getSingleton('paymentController')
+    group             = KD.getSingleton("groupsController").getCurrentGroup()
+
+    paymentController.setBillingInfo 'user', group, callback
+
+  confirmSubscription:(callback)->
+    KD.remote.api.JRecurlyPlan.getPlanWithCode @planCode, (err,plan)=>
+      console.log plan
+
+      title = plan.title
+      price = plan.feeMonthly / 100
+
+      content = """
Are you sure you want to subscribe to this plan?

+

+
#{title} - $#{price.toFixed(2)}

+                """
+
+      modal           = new KDModalView
+        title         : "Confirm Subcription"
+        content       : "
#{content}
"
+        overlay       : yes
+        buttons       :
+          No          :
+            title     : "No"
+            cssClass  : "modal-clean-gray"
+            callback  : =>
+              modal.destroy()
+          Yes         :
+            title     : "Yes, subscribe"
+            cssClass  : "modal-clean-green"
+            callback  : callback


Basic undo/redo tests

Basic undo/redo tests

error: pathspec 'test/src/integration/undo_test.coffee' did not match any file(s) known to git

--- test/src/integration/undo_test.coffee
+++ test/src/integration/undo_test.coffee
@@ -0,0 +1,26 @@
+editorModule "Undo/Redo", template: "editor_empty"
+
+editorTest "typing and undoing", (done) ->
+  first = editor.document.copy()
+  typeCharacters "abc", ->
+    ok not editor.document.isEqualTo(first)
+    clickToolbarButton action: "undo", ->
+      ok editor.document.isEqualTo(first)
+      done()
+
+editorTest "typing, formatting, typing, and undoing", (done) ->
+  first = editor.document.copy()
+  typeCharacters "abc", ->
+    second = editor.document.copy()
+    clickToolbarButton attribute: "bold", ->
+      typeCharacters "def", ->
+        third = editor.document.copy()
+        clickToolbarButton action: "undo", ->
+          ok editor.document.isEqualTo(second)
+          clickToolbarButton action: "undo", ->
+            ok editor.document.isEqualTo(first)
+            clickToolbarButton action: "redo", ->
+              ok editor.document.isEqualTo(second)
+              clickToolbarButton action: "redo", ->
+                ok editor.document.isEqualTo(third)
+                done()


Use ::getMarker for fold decorations instead of new DisplayBufferMarker

--- src/display-buffer.coffee
+++ src/display-buffer.coffee
@@ -1052,8 +1052,7 @@
     @emit 'marker-created', @getMarker(marker.id)

   createFoldForMarker: (marker) ->
-    bufferMarker = new DisplayBufferMarker({bufferMarker: marker, displayBuffer: this})
-    @addDecorationForMarker(bufferMarker, type: 'gutter', class: 'folded')
+    @addDecorationForMarker(@getMarker(marker.id), type: 'gutter', class: 'folded')
     new Fold(this, marker)

   foldForMarker: (marker) ->


Created setClocks, added Docco.

Created setClocks, added Docco.

Renamed `utc` to `posix` to bring it in line with the documentation. At the same
time, created a `setClocks` function to make the clock adjustment logic
consistent. Clocks are now set based on a `clock` property of an actualized rule
record or zone record. The `clock` property has a value of `posix`, `standard`
or `wallclock`.

Also, `extra` has become `carryover`, which is a better description of the
concept. We're carrying over a rule from the rules for the subsequent zone
record.

Added Docco to try to capture what's going on here. Some code tidy to delete
unused variables.

--- adjustments.coffee
+++ adjustments.coffee
@@ -128,15 +128,15 @@
       # subsequent rule's savings. Therefore, we need
       fields = new Date date.getTime() + offset
       posix = date.getTime()
-      clock = "utc"
+      clock = "posix"
     else if /s$/.test rule.time
       standard = date.getTime()
       fields = new Date standard
       clock = "standard"
     else
-      clock = "wallclock"
       wallclock = date.getTime()
       fields = new Date wallclock
+      clock = "wallclock"

     # Sortable only works if there are no rules on the same day.
     sortable = Date.UTC(fields.getUTCFullYear(), fields.getUTCMonth(), fields.getUTCDate())
@@ -151,7 +151,6 @@
     sortable = Date.UTC(fields.getUTCFullYear(), fields.getUTCMonth(), fields.getUTCDate())
     year = fields.getUTCFullYear()
     { clock: "wallclock", entry, sortable, wallclock, year, save: 0, ruleIndex: -1, offset, entryIndex }
-

 # Convert offset, read from our time zone database, or from an ISO date, into
 # milliseconds so we can use it to adjust milliseconds since the epoch.
@@ -201,7 +200,6 @@
 # Future dates would dispose of the simplicity of generating a table.

 # Maximum year for purpose of sorting rules is 50 years hence.
-MAX_YEAR = new Date(Date.UTC(new Date().getUTCFullYear() + 50, 0, 1)).getUTCFullYear()

 count = 2
 isoize = (object) ->
@@ -209,36 +207,77 @@
   object.iso.posix = iso8601(object.posix) if object.posix
   object.iso.wallclock = iso8601(object.wallclock) if object.wallclock

+# Set all the clocks for the start time of the given interval using the zone
+# offset of the given previous interval.
+setClocks = (interval, effective) ->
+  offset = parseOffset(effective.entry.offset)
+  save = parseOffset(effective.rule.save)
+  switch interval.clock
+    when "wallclock"
+      interval.posix = interval.wallclock - offset - save
+      interval.standard = interval.wallclock - offset
+    when "posix"
+      interval.wallclock = interval.posix + offset + save
+      interval.standard = interval.posix + offset
+    when "standard"
+      interval.posix = interval.standard - offset
+      interval.wallclock = interval.standard - save
+    else
+      die interval
+
 # Remember that we have pseudo-POSIX for wallclock.
-shifts = (say, data, name) ->
+shifts = (say, data, name, startYear) ->
   table = []
-  wallclock = new Date(Date.UTC(START, 11, 31))
-  startYear = wallclock.getUTCFullYear()
+  # Some time well in the future, for ranges.
+  future = new Date(Date.UTC(new Date().getUTCFullYear() + 50, 0, 1))
+  # We use a pseudo-POSIX timestamp to represent our wallclock.
+  wallclock = new Date(Date.UTC(startYear, 11, 31))
+  # Fetch the zone records.
   zone = data.zones[name]
+  # Our initial maximum interval date is absurdly futuristic.
+  max = Number.MAX_VALUE
+  # Loop through the zones records. They are ordered from the most recent to the
+  # start of standard time.
+  #
+  # This is a **bizarro** loop. We move backwards through time so the subsequent
+  # interval is the one we've just visited, while the previous interval is next
+  # one we'll visit. In **bizarro** low ***forward*** mean ***previous*** and
+  # ***backward*** mean ***subsequent***.
   entryIndex = 0
-  max = Number.MAX_VALUE
   while entryIndex < zone.length
+    # Fetch the current entry and the previous entry.
     entry = zone[entryIndex]
-    next = zone[entryIndex + 1]
-    offset = formatOffset entry.offset
-    # Assume this is always pseudo-wallclock, but if it is UTC, I believe we can
-    # simply convert it to wallclock, because the bounaries for Zones include,
-    # wait, no. I've not encountered this yet. NO IDEA!
-    min = if next then convertUntil next else Number.MIN_VALUE
-    minYear = new Date(min).getUTCFullYear()
+
+    # The minimum initerval start time for a daylight savings time shift must be
+    # greater than the end of the previous zone record.
+    #
+    # TODO What do do if the previous zone until clock is posix or standard?
+    min = convertUntil zone[entryIndex + 1] or { stop: Number.MIN_VALUE }
+
+    # We wrap this in a `do` so we can break from rule record processing to
+    # proceed to the previous zone record. It returns the starting year to use
+    # with the next zone record, a year that will have been moved back in time
+    # past years that have already been calculated. It also returns a subsequent
+    # interval, the last interval processed, which cannot be fed to the callback
+    # until we obtain the previous record.
+    #
+    # We need the previous interval because intervals, for the most part, begin
+    # at a wallclock time. That is, the time of the clock on the wall tells us
+    # when the clock on the wall will be adjusted. The POSIX time to adjust the
+    # clock so that it is correct for the subsequent interval must be determined by
+    # applying the offset of the interval previous to it.
+
+    #
     [ startYear, subsequent ] = do (subsequent) ->
       say "entry - startYear: %d, until: %s, subsequent: %s, rules: %s",
-        startYear, entry.until or "init", subsequent?.entry.until or (subsequent? and "init"), subsequent?.index isnt -1
-      extras = []
-      # TODO Change prev to next where because for of we're going backward.
-      # Our entry might signify an adjustment in itself.
-      #
-      # Glarg! Glarg! Glarg! Molgar says this is unacceptable.
-      #
+        startYear, entry.until or "init",
+        subsequent?.entry.until or (subsequent? and "init"),
+        subsequent?.index isnt -1
+      # The intervals carried over from the last time we went through the loop.
+      carryover = []
       # When trying to find the seam when there are rules, the thing that starts
       # is the subsequent zone entry, but the wallclock time is the start of the
       # current zone enty.
-      #
       if subsequent
         actual = actualize.entry entry, entryIndex
         if subsequent.ruleIndex is -1
@@ -251,9 +290,9 @@
         if entry.rules
           subsequent.wallclock = actual.wallclock
           if subsequent.index is -1 or true
-            extras.push subsequent
+            carryover.push subsequent
           else
-            extras.push subsequent, actual
+            carryover.push subsequent, actual
         else
           # Now we have our previous year, so we can calcuate the wallclock time
           # and posix time of this transition.
@@ -270,14 +309,17 @@
             say "rule - #{iso8601 wallclock} #{iso8601 posix}"
             table.push posix, wallclock, subsequent.entryIndex, subsequent.ruleIndex

-      return [ startYear, actualize.entry entry, entryIndex ] if not entry.rules
-
-      # Uncomment this to get all seams.
-      #extras.push actualize.entry entry
-
-      # Get a copy of our rules.
+      # If no rules, return the entry as the subsequent interval.
+      if not entry.rules
+        return [ startYear, actualize.entry(entry, entryIndex) ]
+
+      # Actualize our rules for the current zone entry and year.
+      #
+      # TODO Yes, yes, yes. You could make this list an already sorted list by
+      # sorting this in the Olson file preprocessor.
       actuals = for rule, index in data.rules[entry.rules] or []
-        actualize.rule entry, entryIndex, rule, index, Math.min(MAX_YEAR, rule.to)
+        ruleYear = Math.min(future.getUTCFullYear(), rule.to)
+        actualize.rule entry, entryIndex, rule, index, ruleYear

       # Now order them by the date they ended descending, this will make it
       # easier to probe them, as they will be in the correct order.
@@ -290,13 +332,18 @@
           if actuals[index].sortable is actuals[index - 1].sortable
             throw new Error "two rules occur on the same day."

-      # We move an index as we move through the years.
+      # We move a start index as we move through the years, so that ever time
+      # through the while loop, we can skip rule records whose ranges begin in
+      # years after the current year.
       startIndex = 0
+      # Start with out start year. TODO Superfluous. Also, `loop`.
       year = startYear
       while year >= 0
+        # Start with our carryover, emptying the carryover list.
+        adjustments = carryover.splice(0)
+
+        # Index into the list of intervals.
         index = startIndex
-        adjustments = extras.slice(0)
-        extras.length = 0

         # We gather up any rules applying to this year. We want all rules at
         # once so we can sort them and push them onto the table in order. They
@@ -329,7 +376,6 @@
         # Oldest first.
         adjustments.sort (a, b) -> b.sortable - a.sortable

-        pushed = 0
         # Convert our adjustments into posix and pseudo-posix wallclock.
         for actual in adjustments
           # Local namespace for the previous year.
@@ -382,11 +428,16 @@
             # standard time, but we are meek. We actually search for a rule
             # record who's savings is zero.
             else
+              if actual.ruleIndex is -1
+                say "before first rule - %s %s",
+                  iso8601(actual.wallclock), actual.entry.until or "init"
+              else
+                say "before first rule - %s %s %s %s %s",
+                  iso8601(actual.wallclock), actual.entry.until or "init",
+                  actual.rule.from, actual.rule.to, actual.rule.month + 1
               for standard in actuals.slice(0).reverse()
                 if parseOffset(standard.rule.save) is 0
-                  say "WE MAKE OUR OWN RULES!"
                   prev.actual = actualize.actual standard, year
-                  say { actual, prev: prev.actual }
                   break
               prev.rule = prev.actual.rule
               prev.year = year
@@ -396,12 +447,7 @@
               preceeding = prev.actual
           # Now we have our previous year, so we can calcuate the wallclock
           # time and posix time of this transition.
-          #
-          # TODO Wait? Isn't this the offset of the previous? I'm confused.
-          if actual.clock is "wallclock"
-            actual.posix = actual.wallclock - parseOffset(entry.offset) - parseOffset(prev.rule.save)
-          else
-            actual.wallclock = actual.posix + parseOffset(entry.offset) + parseOffset(prev.rule.save)
+          setClocks actual, prev.actual
           # Hit the end of the previous new zone entry. We have figured out
           # the rule that will apply when the new entry begins, but we don't
           # know the wallclock time because the previous rules are not in the
@@ -426,13 +472,15 @@
               actual.rule.from, actual.rule.to, actual.rule.month + 1
             continue
           # Huzzah! Add an entry to our table.
-          pushed++
           say "rule - #{iso8601 actual.wallclock} #{iso8601 actual.posix}"
           table.push actual.posix, actual.wallclock, actual.entryIndex, actual.ruleIndex
         # If we had to make our own initial standard rule record, we feed that
         # to the next zone entry as the subsequent interval.
         return [ year, preceeding ] if preceeding
+    # The minimum time for an interval start becomes the  maximum time for an
+    # interval end.
     max = min
+    # Onward to the next zone record.
     entryIndex++
   table.push Number.MIN_VALUE, Number.MIN_VALUE, zone.length - 1, -1
   for index in [0...table.length - 4] by 4
@@ -460,4 +508,4 @@
     offset += parseOffset rule.save or "0"
   offset

-shifts(say, data, process.argv[2])
+shifts(say, data, process.argv[2], START)


Update configuration

Update configuration

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/ehmicky/myos.git/'


Comment drawer config

Comment drawer config

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/AppGyver/kitchensink.git/'


Improve the node.js support

Improve the node.js support

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/nanoko-project/h-ubu.git/'


GOB. :raised_hands: :fire: :scream: :v:

GOB. :raised_hands: :fire: :scream: :v:

error: pathspec 'src/scripts/gob.coffee' did not match any file(s) known to git

--- src/scripts/gob.coffee
+++ src/scripts/gob.coffee
@@ -0,0 +1,16 @@
+# gob it - Display a GOB
+
+gobs = [
+  "http://bite-prod.s3.amazonaws.com/wp-content/uploads/2012/05/chicken-dance-2.gif",
+  "http://bite-prod.s3.amazonaws.com/wp-content/uploads/2012/05/Gob.gif",
+  "http://bite-prod.s3.amazonaws.com/wp-content/uploads/2012/05/pants.gif",
+  "http://bite-prod.s3.amazonaws.com/wp-content/uploads/2012/05/pennies1.gif",
+  "http://bite-prod.s3.amazonaws.com/wp-content/uploads/2012/05/queen_reveal.gif",
+  "http://bite-prod.s3.amazonaws.com/wp-content/uploads/2012/05/scotch.gif",
+  "http://farm1.static.flickr.com/223/511011836_56ae92ec1a_o.gif",
+  "http://cdn.nextround.net/wp-content/uploads/2010/03/gob-bluth-gif-6.gif"
+]
+
+module.exports = (robot) ->
+  robot.hear /gob it/i, (msg) ->
+    msg.send msg.random gobs


:memo:

:memo:

--- src/tokenized-line.coffee
+++ src/tokenized-line.coffee
@@ -114,7 +114,7 @@
   # For a given `indentLevel`, calculates how many trailing spaces cannot fit in a single tab.
   #
   # indentLevel - {Number}
-  # Returns a {Number} representing the odd indentation spaces for `indentLevel`.
+  # Returns a {Number} representing the odd indentation spaces in `indentLevel`.
   oddIndentationSpacesForIndentLevel: (indentLevel) ->
     oddIndentLevel = indentLevel - Math.floor(indentLevel)
     Math.round(@tabLength * oddIndentLevel)


Adds a large file as fixture

Adds a large file as fixture

It’s the Editor class source!

OS IS GOOD \o/

error: pathspec 'spec/fixtures/large-file.coffee' did not match any file(s) known to git

--- spec/fixtures/large-file.coffee
+++ spec/fixtures/large-file.coffee
@@ -0,0 +1,1894 @@
+_ = require 'underscore-plus'
+path = require 'path'
+Serializable = require 'serializable'
+Delegator = require 'delegato'
+{deprecate} = require 'grim'
+{Model} = require 'theorist'
+{Point, Range} = require 'text-buffer'
+LanguageMode = require './language-mode'
+DisplayBuffer = require './display-buffer'
+Cursor = require './cursor'
+
+Selection = require './selection'
+TextMateScopeSelector = require('first-mate').ScopeSelector
+
+# Public: This class represents all essential editing state for a single
+# {TextBuffer}, including cursor and selection positions, folds, and soft wraps.
+# If you're manipulating the state of an editor, use this class. If you're
+# interested in the visual appearance of editors, use {EditorView} instead.
+#
+# A single {TextBuffer} can belong to multiple editors. For example, if the
+# same file is open in two different panes, Atom creates a separate editor for
+# each pane. If the buffer is manipulated the changes are reflected in both
+# editors, but each maintains its own cursor position, folded lines, etc.
+#
+# ## Accessing Editor Instances
+#
+# The easiest way to get hold of `Editor` objects is by registering a callback
+# with `::eachEditor` on the `atom.workspace` global. Your callback will then
+# be called with all current editor instances and also when any editor is
+# created in the future.
+#
+# ```coffeescript
+#   atom.workspace.eachEditor (editor) ->
+#     editor.insertText('Hello World')
+# ```
+#
+# ## Buffer vs. Screen Coordinates
+#
+# Because editors support folds and soft-wrapping, the lines on screen don't
+# always match the lines in the buffer. For example, a long line that soft wraps
+# twice renders as three lines on screen, but only represents one line in the
+# buffer. Similarly, if rows 5-10 are folded, then row 6 on screen corresponds
+# to row 11 in the buffer.
+#
+# Your choice of coordinates systems will depend on what you're trying to
+# achieve. For example, if you're writing a command that jumps the cursor up or
+# down by 10 lines, you'll want to use screen coordinates because the user
+# probably wants to skip lines *on screen*. However, if you're writing a package
+# that jumps between method definitions, you'll want to work in buffer
+# coordinates.
+#
+# **When in doubt, just default to buffer coordinates**, then experiment with
+# soft wraps and folds to ensure your code interacts with them correctly.
+#
+# ## Common Tasks
+#
+# This is a subset of methods on this class. Refer to the complete summary for
+# its full capabilities.
+#
+# ### Cursors
+# - {::setCursorBufferPosition}
+# - {::setCursorScreenPosition}
+# - {::moveCursorUp}
+# - {::moveCursorDown}
+# - {::moveCursorLeft}
+# - {::moveCursorRight}
+# - {::moveCursorToBeginningOfWord}
+# - {::moveCursorToEndOfWord}
+# - {::moveCursorToPreviousWordBoundary}
+# - {::moveCursorToNextWordBoundary}
+# - {::moveCursorToBeginningOfNextWord}
+# - {::moveCursorToBeginningOfLine}
+# - {::moveCursorToEndOfLine}
+# - {::moveCursorToFirstCharacterOfLine}
+# - {::moveCursorToTop}
+# - {::moveCursorToBottom}
+#
+# ### Selections
+# - {::getSelectedBufferRange}
+# - {::getSelectedBufferRanges}
+# - {::setSelectedBufferRange}
+# - {::setSelectedBufferRanges}
+# - {::selectUp}
+# - {::selectDown}
+# - {::selectLeft}
+# - {::selectRight}
+# - {::selectToBeginningOfWord}
+# - {::selectToEndOfWord}
+# - {::selectToPreviousWordBoundary}
+# - {::selectToNextWordBoundary}
+# - {::selectWord}
+# - {::selectToBeginningOfLine}
+# - {::selectToEndOfLine}
+# - {::selectToFirstCharacterOfLine}
+# - {::selectToTop}
+# - {::selectToBottom}
+# - {::selectAll}
+# - {::addSelectionForBufferRange}
+# - {::addSelectionAbove}
+# - {::addSelectionBelow}
+# - {::splitSelectionsIntoLines}
+#
+# ### Manipulating Text
+# - {::getText}
+# - {::getSelectedText}
+# - {::setText}
+# - {::setTextInBufferRange}
+# - {::insertText}
+# - {::insertNewline}
+# - {::insertNewlineAbove}
+# - {::insertNewlineBelow}
+# - {::backspace}
+# - {::backspaceToBeginningOfWord}
+# - {::backspaceToBeginningOfLine}
+# - {::delete}
+# - {::deleteToEndOfWord}
+# - {::deleteLine}
+# - {::cutSelectedText}
+# - {::cutToEndOfLine}
+# - {::copySelectedText}
+# - {::pasteText}
+#
+# ### Undo, Redo, and Transactions
+# - {::undo}
+# - {::redo}
+# - {::transact}
+# - {::abortTransaction}
+#
+# ### Markers
+# - {::markBufferRange}
+# - {::markScreenRange}
+# - {::getMarker}
+# - {::findMarkers}
+module.exports =
+class Editor extends Model
+  Serializable.includeInto(this)
+  atom.deserializers.add(this)
+  Delegator.includeInto(this)
+
+  deserializing: false
+  callDisplayBufferCreatedHook: false
+  registerEditor: false
+  buffer: null
+  languageMode: null
+  cursors: null
+  selections: null
+  suppressSelectionMerging: false
+
+  @delegatesMethods 'suggestedIndentForBufferRow', 'autoIndentBufferRow', 'autoIndentBufferRows',
+    'autoDecreaseIndentForBufferRow', 'toggleLineCommentForBufferRow', 'toggleLineCommentsForBufferRows',
+    toProperty: 'languageMode'
+
+  @delegatesProperties '$lineHeight', '$defaultCharWidth', '$height', '$width',
+    '$scrollTop', '$scrollLeft', 'manageScrollPosition', toProperty: 'displayBuffer'
+
+  constructor: ({@softTabs, initialLine, initialColumn, tabLength, softWrap, @displayBuffer, buffer, registerEditor, suppressCursorCreation}) ->
+    super
+
+    @cursors = []
+    @selections = []
+
+    @displayBuffer ?= new DisplayBuffer({buffer, tabLength, softWrap})
+    @buffer = @displayBuffer.buffer
+    @softTabs = @buffer.usesSoftTabs() ? @softTabs ? atom.config.get('editor.softTabs') ? true
+
+    for marker in @findMarkers(@getSelectionMarkerAttributes())
+      marker.setAttributes(preserveFolds: true)
+      @addSelection(marker)
+
+    @subscribeToBuffer()
+    @subscribeToDisplayBuffer()
+
+    if @getCursors().length is 0 and not suppressCursorCreation
+      initialLine = Math.max(parseInt(initialLine) or 0, 0)
+      initialColumn = Math.max(parseInt(initialColumn) or 0, 0)
+      @addCursorAtBufferPosition([initialLine, initialColumn])
+
+    @languageMode = new LanguageMode(this)
+
+    @subscribe @$scrollTop, (scrollTop) => @emit 'scroll-top-changed', scrollTop
+    @subscribe @$scrollLeft, (scrollLeft) => @emit 'scroll-left-changed', scrollLeft
+
+    atom.workspace?.editorAdded(this) if registerEditor
+
+  serializeParams: ->
+    id: @id
+    softTabs: @softTabs
+    scrollTop: @scrollTop
+    scrollLeft: @scrollLeft
+    displayBuffer: @displayBuffer.serialize()
+
+  deserializeParams: (params) ->
+    params.displayBuffer = DisplayBuffer.deserialize(params.displayBuffer)
+    params.registerEditor = true
+    params
+
+  subscribeToBuffer: ->
+    @buffer.retain()
+    @subscribe @buffer, "path-changed", =>
+      unless atom.project.getPath()?
+        atom.project.setPath(path.dirname(@getPath()))
+      @emit "title-changed"
+      @emit "path-changed"
+    @subscribe @buffer, "contents-modified", => @emit "contents-modified"
+    @subscribe @buffer, "contents-conflicted", => @emit "contents-conflicted"
+    @subscribe @buffer, "modified-status-changed", => @emit "modified-status-changed"
+    @subscribe @buffer, "destroyed", => @destroy()
+    @preserveCursorPositionOnBufferReload()
+
+  subscribeToDisplayBuffer: ->
+    @subscribe @displayBuffer, 'marker-created', @handleMarkerCreated
+    @subscribe @displayBuffer, "changed", (e) => @emit 'screen-lines-changed', e
+    @subscribe @displayBuffer, "markers-updated", => @mergeIntersectingSelections()
+    @subscribe @displayBuffer, 'grammar-changed', => @handleGrammarChange()
+    @subscribe @displayBuffer, 'soft-wrap-changed', (args...) => @emit 'soft-wrap-changed', args...
+
+  getViewClass: ->
+    if atom.config.get('core.useReactEditor')
+      require './react-editor-view'
+    else
+      require './editor-view'
+
+  destroyed: ->
+    @unsubscribe()
+    selection.destroy() for selection in @getSelections()
+    @buffer.release()
+    @displayBuffer.destroy()
+    @languageMode.destroy()
+
+  # Create an {Editor} with its initial state based on this object
+  copy: ->
+    tabLength = @getTabLength()
+    displayBuffer = @displayBuffer.copy()
+    softTabs = @getSoftTabs()
+    newEditor = new Editor({@buffer, displayBuffer, tabLength, softTabs, suppressCursorCreation: true, registerEditor: true})
+    for marker in @findMarkers(editorId: @id)
+      marker.copy(editorId: newEditor.id, preserveFolds: true)
+    newEditor
+
+  # Public: Get the title the editor's title for display in other parts of the
+  # UI such as the tabs.
+  #
+  # If the editor's buffer is saved, its title is the file name. If it is
+  # unsaved, its title is "untitled".
+  #
+  # Returns a {String}.
+  getTitle: ->
+    if sessionPath = @getPath()
+      path.basename(sessionPath)
+    else
+      'untitled'
+
+  # Public: Get the editor's long title for display in other parts of the UI
+  # such as the window title.
+  #
+  # If the editor's buffer is saved, its long title is formatted as
+  # "<filename> - <directory>". If it is unsaved, its title is "untitled"
+  #
+  # Returns a {String}.
+  getLongTitle: ->
+    if sessionPath = @getPath()
+      fileName = path.basename(sessionPath)
+      directory = path.basename(path.dirname(sessionPath))
+      "#{fileName} - #{directory}"
+    else
+      'untitled'
+
+  # Controls visiblity based on the given {Boolean}.
+  setVisible: (visible) -> @displayBuffer.setVisible(visible)
+
+  # Set the number of characters that can be displayed horizontally in the
+  # editor.
+  #
+  # editorWidthInChars - A {Number} representing the width of the {EditorView}
+  # in characters.
+  setEditorWidthInChars: (editorWidthInChars) ->
+    @displayBuffer.setEditorWidthInChars(editorWidthInChars)
+
+  # Public: Sets the column at which columsn will soft wrap
+  getSoftWrapColumn: -> @displayBuffer.getSoftWrapColumn()
+
+  # Public: Returns a {Boolean} indicating whether softTabs are enabled for this
+  # editor.
+  getSoftTabs: -> @softTabs
+
+  # Public: Enable or disable soft tabs for this editor.
+  #
+  # softTabs - A {Boolean}
+  setSoftTabs: (@softTabs) -> @softTabs
+
+  # Public: Toggle soft tabs for this editor
+  toggleSoftTabs: -> @setSoftTabs(not @getSoftTabs())
+
+  # Public: Get whether soft wrap is enabled for this editor.
+  getSoftWrap: -> @displayBuffer.getSoftWrap()
+
+  # Public: Enable or disable soft wrap for this editor.
+  #
+  # softWrap - A {Boolean}
+  setSoftWrap: (softWrap) -> @displayBuffer.setSoftWrap(softWrap)
+
+  # Public: Toggle soft wrap for this editor
+  toggleSoftWrap: -> @setSoftWrap(not @getSoftWrap())
+
+  # Public: 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)
+
+  # Public: Get the on-screen length of tab characters.
+  #
+  # Returns a {Number}.
+  getTabLength: -> @displayBuffer.getTabLength()
+
+  # Public: Set the on-screen length of tab characters.
+  setTabLength: (tabLength) -> @displayBuffer.setTabLength(tabLength)
+
+  # Public: Clip the given {Point} to a valid position in the buffer.
+  #
+  # If the given {Point} describes a position that is actually reachable by the
+  # cursor based on the current contents of the buffer, it is returned
+  # unchanged. If the {Point} does not describe a valid position, the closest
+  # valid position is returned instead.
+  #
+  # For example:
+  #   * `[-1, -1]` is converted to `[0, 0]`.
+  #   * If the line at row 2 is 10 long, `[2, Infinity]` is converted to
+  #     `[2, 10]`.
+  #
+  # bufferPosition - The {Point} representing the position to clip.
+  #
+  # Returns a {Point}.
+  clipBufferPosition: (bufferPosition) -> @buffer.clipPosition(bufferPosition)
+
+  # Public: Clip the start and end of the given range to valid positions in the
+  # buffer. See {::clipBufferPosition} for more information.
+  #
+  # range - The {Range} to clip.
+  #
+  # Returns a {Range}.
+  clipBufferRange: (range) -> @buffer.clipRange(range)
+
+  # Public: 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(@lineForBufferRow(bufferRow))
+
+  # Public: 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 - 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 = @lineForBufferRow(bufferRow).match(/^\s*/)[0].length
+    newIndentString = @buildIndentString(newLevel)
+    @buffer.setTextInRange([[bufferRow, 0], [bufferRow, endColumn]], newIndentString)
+
+  # Public: 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)
+
+  # Constructs the string used for tabs.
+  buildIndentString: (number) ->
+    if @getSoftTabs()
+      _.multiplyString(" ", Math.floor(number * @getTabLength()))
+    else
+      _.multiplyString("\t", Math.floor(number))
+
+  # Public: Saves the editor's text buffer.
+  #
+  # See {TextBuffer::save} for more details.
+  save: -> @buffer.save()
+
+  # 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)
+
+  checkoutHead: ->
+    if filePath = @getPath()
+      atom.project.getRepo()?.checkoutHead(filePath)
+
+  # Copies the current file path to the native clipboard.
+  copyPathToClipboard: ->
+    if filePath = @getPath()
+      atom.clipboard.write(filePath)
+
+  # Public: Returns the {String} path of this editor's text buffer.
+  getPath: -> @buffer.getPath()
+
+  # Public: Returns a {String} representing the entire contents of the editor.
+  getText: -> @buffer.getText()
+
+  # Public: Replaces the entire contents of the buffer with the given {String}.
+  setText: (text) -> @buffer.setText(text)
+
+  # Get the text in the given {Range}.
+  #
+  # Returns a {String}.
+  getTextInRange: (range) -> @buffer.getTextInRange(range)
+
+  # Public: Returns a {Number} representing the number of lines in the editor.
+  getLineCount: -> @buffer.getLineCount()
+
+  # Retrieves the current {TextBuffer}.
+  getBuffer: -> @buffer
+
+  # Public: Retrieves the current buffer's URI.
+  getUri: -> @buffer.getUri()
+
+  # {Delegates to: TextBuffer.isRowBlank}
+  isBufferRowBlank: (bufferRow) -> @buffer.isRowBlank(bufferRow)
+
+  # Public: Determine if the given row is entirely a comment
+  isBufferRowCommented: (bufferRow) ->
+    if match = @lineForBufferRow(bufferRow).match(/\S/)
+      scopes = @tokenForBufferPosition([bufferRow, match.index]).scopes
+      new TextMateScopeSelector('comment.*').matches(scopes)
+
+  # {Delegates to: TextBuffer.nextNonBlankRow}
+  nextNonBlankBufferRow: (bufferRow) -> @buffer.nextNonBlankRow(bufferRow)
+
+  # {Delegates to: TextBuffer.getEndPosition}
+  getEofBufferPosition: -> @buffer.getEndPosition()
+
+  # Public: Returns a {Number} representing the last zero-indexed buffer row
+  # number of the editor.
+  getLastBufferRow: -> @buffer.getLastRow()
+
+  # Returns the range for the given buffer row.
+  #
+  # row - A row {Number}.
+  # options - An options hash with an `includeNewline` key.
+  #
+  # Returns a {Range}.
+  bufferRangeForBufferRow: (row, {includeNewline}={}) -> @buffer.rangeForRow(row, includeNewline)
+
+  # Public: Returns a {String} representing the contents of the line at the
+  # given buffer row.
+  #
+  # row - A {Number} representing a zero-indexed buffer row.
+  lineForBufferRow: (row) -> @buffer.lineForRow(row)
+
+  # Public: Returns a {Number} representing the line length for the given
+  # buffer row, exclusive of its line-ending character(s).
+  #
+  # row - A {Number} indicating the buffer row.
+  lineLengthForBufferRow: (row) -> @buffer.lineLengthForRow(row)
+
+  # {Delegates to: TextBuffer.scan}
+  scan: (args...) -> @buffer.scan(args...)
+
+  # {Delegates to: TextBuffer.scanInRange}
+  scanInBufferRange: (args...) -> @buffer.scanInRange(args...)
+
+  # {Delegates to: TextBuffer.backwardsScanInRange}
+  backwardsScanInBufferRange: (args...) -> @buffer.backwardsScanInRange(args...)
+
+  # {Delegates to: TextBuffer.isModified}
+  isModified: -> @buffer.isModified()
+
+  # Public: Determine whether the user should be prompted to save before closing
+  # this editor.
+  shouldPromptToSave: -> @isModified() and not @buffer.hasMultipleEditors()
+
+  # Public: Convert a position in buffer-coordinates to screen-coordinates.
+  #
+  # The position is clipped via {::clipBufferPosition} prior to the conversion.
+  # The position is also clipped via {::clipScreenPosition} following the
+  # conversion, which only makes a difference when `options` are supplied.
+  #
+  # bufferPosition - A {Point} or {Array} of [row, column].
+  # options - An options hash for {::clipScreenPosition}.
+  #
+  # Returns a {Point}.
+  screenPositionForBufferPosition: (bufferPosition, options) -> @displayBuffer.screenPositionForBufferPosition(bufferPosition, options)
+
+  # Public: Convert a position in screen-coordinates to buffer-coordinates.
+  #
+  # The position is clipped via {::clipScreenPosition} prior to the conversion.
+  #
+  # bufferPosition - A {Point} or {Array} of [row, column].
+  # options - An options hash for {::clipScreenPosition}.
+  #
+  # Returns a {Point}.
+  bufferPositionForScreenPosition: (screenPosition, options) -> @displayBuffer.bufferPositionForScreenPosition(screenPosition, options)
+
+  # Public: Convert a range in buffer-coordinates to screen-coordinates.
+  #
+  # Returns a {Range}.
+  screenRangeForBufferRange: (bufferRange) -> @displayBuffer.screenRangeForBufferRange(bufferRange)
+
+  # Public: Convert a range in screen-coordinates to buffer-coordinates.
+  #
+  # Returns a {Range}.
+  bufferRangeForScreenRange: (screenRange) -> @displayBuffer.bufferRangeForScreenRange(screenRange)
+
+  # Public: Clip the given {Point} to a valid position on screen.
+  #
+  # If the given {Point} describes a position that is actually reachable by the
+  # cursor based on the current contents of the screen, it is returned
+  # unchanged. If the {Point} does not describe a valid position, the closest
+  # valid position is returned instead.
+  #
+  # For example:
+  #   * `[-1, -1]` is converted to `[0, 0]`.
+  #   * If the line at screen row 2 is 10 long, `[2, Infinity]` is converted to
+  #     `[2, 10]`.
+  #
+  # bufferPosition - The {Point} representing the position to clip.
+  #
+  # Returns a {Point}.
+  clipScreenPosition: (screenPosition, options) -> @displayBuffer.clipScreenPosition(screenPosition, options)
+
+  # {Delegates to: DisplayBuffer.lineForRow}
+  lineForScreenRow: (row) -> @displayBuffer.lineForRow(row)
+
+  # {Delegates to: DisplayBuffer.linesForRows}
+  linesForScreenRows: (start, end) -> @displayBuffer.linesForRows(start, end)
+
+  # {Delegates to: DisplayBuffer.getLineCount}
+  getScreenLineCount: -> @displayBuffer.getLineCount()
+
+  # {Delegates to: DisplayBuffer.getMaxLineLength}
+  getMaxScreenLineLength: -> @displayBuffer.getMaxLineLength()
+
+  # {Delegates to: DisplayBuffer.getLastRow}
+  getLastScreenRow: -> @displayBuffer.getLastRow()
+
+  # {Delegates to: DisplayBuffer.bufferRowsForScreenRows}
+  bufferRowsForScreenRows: (startRow, endRow) -> @displayBuffer.bufferRowsForScreenRows(startRow, endRow)
+
+  bufferRowForScreenRow: (row) -> @displayBuffer.bufferRowForScreenRow(row)
+
+  # Public: Get the syntactic scopes for the most the given position in buffer
+  # coorditanates.
+  #
+  # 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)
+
+  # Public: 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())
+
+  # {Delegates to: DisplayBuffer.tokenForBufferPosition}
+  tokenForBufferPosition: (bufferPosition) -> @displayBuffer.tokenForBufferPosition(bufferPosition)
+
+  # Public: Get the syntactic scopes for the most recently added cursor's
+  # position. See {::scopesForBufferPosition} for more information.
+  #
+  # Returns an {Array} of {String}s.
+  getCursorScopes: -> @getCursor().getScopes()
+
+  logCursorScope: ->
+    console.log @getCursorScopes()
+
+  # Public: For each selection, replace the selected text with the given text.
+  #
+  # text - A {String} representing the text to insert.
+  # options - See {Selection::insertText}.
+  insertText: (text, options={}) ->
+    options.autoIndentNewline ?= @shouldAutoIndent()
+    options.autoDecreaseIndent ?= @shouldAutoIndent()
+    @mutateSelectedText (selection) -> selection.insertText(text, options)
+
+  # Public: For each selection, replace the selected text with a newline.
+  insertNewline: ->
+    @insertText('\n')
+
+  # Public: For each cursor, insert a newline at beginning the following line.
+  insertNewlineBelow: ->
+    @transact =>
+      @moveCursorToEndOfLine()
+      @insertNewline()
+
+  # Public: For each cursor, insert a newline at the end of the preceding line.
+  insertNewlineAbove: ->
+    @transact =>
+      bufferRow = @getCursorBufferPosition().row
+      indentLevel = @indentationForBufferRow(bufferRow)
+      onFirstLine = bufferRow is 0
+
+      @moveCursorToBeginningOfLine()
+      @moveCursorLeft()
+      @insertNewline()
+
+      if @shouldAutoIndent() and @indentationForBufferRow(bufferRow) < indentLevel
+        @setIndentationForBufferRow(bufferRow, indentLevel)
+
+      if onFirstLine
+        @moveCursorUp()
+        @moveCursorToEndOfLine()
+
+  # Indent all lines intersecting selections. See {Selection::indent} for more
+  # information.
+  indent: (options={})->
+    options.autoIndent ?= @shouldAutoIndent()
+    @mutateSelectedText (selection) -> selection.indent(options)
+
+  # Public: For each selection, if the selection is empty, delete the character
+  # preceding the cursor. Otherwise delete the selected text.
+  backspace: ->
+    @mutateSelectedText (selection) -> selection.backspace()
+
+  # Public: For each selection, if the selection is empty, delete all characters
+  # of the containing word that precede the cursor. Otherwise delete the
+  # selected text.
+  backspaceToBeginningOfWord: ->
+    @mutateSelectedText (selection) -> selection.backspaceToBeginningOfWord()
+
+  # Public: For each selection, if the selection is empty, delete all characters
+  # of the containing line that precede the cursor. Otherwise delete the
+  # selected text.
+  backspaceToBeginningOfLine: ->
+    @mutateSelectedText (selection) -> selection.backspaceToBeginningOfLine()
+
+  # Public: For each selection, if the selection is empty, delete the character
+  # preceding the cursor. Otherwise delete the selected text.
+  delete: ->
+    @mutateSelectedText (selection) -> selection.delete()
+
+  # Public: For each selection, if the selection is empty, delete all characters
+  # of the containing word following the cursor. Otherwise delete the selected
+  # text.
+  deleteToEndOfWord: ->
+    @mutateSelectedText (selection) -> selection.deleteToEndOfWord()
+
+  # Public: Delete all lines intersecting selections.
+  deleteLine: ->
+    @mutateSelectedText (selection) -> selection.deleteLine()
+
+  # Public: Indent rows intersecting selections by one level.
+  indentSelectedRows: ->
+    @mutateSelectedText (selection) -> selection.indentSelectedRows()
+
+  # Public: Outdent rows intersecting selections by one level.
+  outdentSelectedRows: ->
+    @mutateSelectedText (selection) -> selection.outdentSelectedRows()
+
+  # Public: Toggle line comments for rows intersecting selections.
+  #
+  # If the current grammar doesn't support comments, does nothing.
+  #
+  # Returns an {Array} of the commented {Range}s.
+  toggleLineCommentsInSelection: ->
+    @mutateSelectedText (selection) -> selection.toggleLineComments()
+
+  # Public: Indent rows intersecting selections based on the grammar's suggested
+  # indent level.
+  autoIndentSelectedRows: ->
+    @mutateSelectedText (selection) -> selection.autoIndentSelectedRows()
+
+  # 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())
+
+  # 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
+
+  # Public: For each selection, cut the selected text.
+  cutSelectedText: ->
+    maintainClipboard = false
+    @mutateSelectedText (selection) ->
+      selection.cut(maintainClipboard)
+      maintainClipboard = true
+
+  # Public: For each selection, copy the selected text.
+  copySelectedText: ->
+    maintainClipboard = false
+    for selection in @getSelections()
+      selection.copy(maintainClipboard)
+      maintainClipboard = true
+
+  # Public: 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 - 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 !@getCursor().hasPrecedingCharactersOnLine() or containsNewlines
+        options.indentBasis ?= metadata.indentBasis
+
+    @insertText(text, options)
+
+  # Public: Undo the last change.
+  undo: ->
+    @getCursor().needsAutoscroll = true
+    @buffer.undo(this)
+
+  # Public: Redo the last change.
+  redo: ->
+    @getCursor().needsAutoscroll = true
+    @buffer.redo(this)
+
+  # Public: 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)
+
+  # Public: Unfold the most recent cursor's row by one level.
+  unfoldCurrentRow: ->
+    bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row
+    @unfoldBufferRow(bufferRow)
+
+  # Public: For each selection, fold the rows it intersects.
+  foldSelectedLines: ->
+    selection.fold() for selection in @getSelections()
+
+  # Public: Fold all foldable lines.
+  foldAll: ->
+    @languageMode.foldAll()
+
+  # Public: Unfold all existing folds.
+  unfoldAll: ->
+    @languageMode.unfoldAll()
+
+  # Public: Fold all foldable lines at the given indent level.
+  #
+  # level - A {Number}.
+  foldAllAtIndentLevel: (level) ->
+    @languageMode.foldAllAtIndentLevel(level)
+
+  # Public: 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)
+
+  # Public: Unfold all folds containing the given row in buffer coordinates.
+  #
+  # bufferRow - A {Number}
+  unfoldBufferRow: (bufferRow) ->
+    @displayBuffer.unfoldBufferRow(bufferRow)
+
+  # Public: 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)
+
+  # 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)
+
+  # Public: 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)
+
+  # Public: Determine whether the most recently added cursor's row is folded.
+  #
+  # Returns a {Boolean}.
+  isFoldedAtCursorRow: ->
+    @isFoldedAtScreenRow(@getCursorScreenRow())
+
+  # Public: Determine whether the given row in buffer coordinates is folded.
+  #
+  # bufferRow - A {Number}
+  #
+  # Returns a {Boolean}.
+  isFoldedAtBufferRow: (bufferRow) ->
+    @displayBuffer.isFoldedAtBufferRow(bufferRow)
+
+  # Public: Determine whether the given row in screen coordinates is folded.
+  #
+  # screenRow - A {Number}
+  #
+  # Returns a {Boolean}.
+  isFoldedAtScreenRow: (screenRow) ->
+    @displayBuffer.isFoldedAtScreenRow(screenRow)
+
+  # {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)
+
+  # Move lines intersection the most recent selection up by one row in screen
+  # coordinates.
+  moveLineUp: ->
+    selection = @getSelectedBufferRange()
+    return if selection.start.row is 0
+    lastRow = @buffer.getLastRow()
+    return if selection.isEmpty() and selection.start.row is lastRow and @buffer.getLastLine() is ''
+
+    @transact =>
+      foldedRows = []
+      rows = [selection.start.row..selection.end.row]
+      if selection.start.row isnt selection.end.row and selection.end.column is 0
+        rows.pop() unless @isFoldedAtBufferRow(selection.end.row)
+
+      # Move line around the fold that is directly above the selection
+      precedingScreenRow = @screenPositionForBufferPosition([selection.start.row]).translate([-1])
+      precedingBufferRow = @bufferPositionForScreenPosition(precedingScreenRow).row
+      if fold = @largestFoldContainingBufferRow(precedingBufferRow)
+        insertDelta = fold.getBufferRange().getRowCount()
+      else
+        insertDelta = 1
+
+      for row in rows
+        if fold = @displayBuffer.largestFoldStartingAtBufferRow(row)
+          bufferRange = fold.getBufferRange()
+          startRow = bufferRange.start.row
+          endRow = bufferRange.end.row
+          foldedRows.push(startRow - insertDelta)
+        else
+          startRow = row
+          endRow = row
+
+        insertPosition = Point.fromObject([startRow - insertDelta])
+        endPosition = Point.min([endRow + 1], @buffer.getEndPosition())
+        lines = @buffer.getTextInRange([[startRow], endPosition])
+        if endPosition.row is lastRow and endPosition.column > 0 and not @buffer.lineEndingForRow(endPosition.row)
+          lines = "#{lines}\n"
+
+        @buffer.deleteRows(startRow, endRow)
+
+        # Make sure the inserted text doesn't go into an existing fold
+        if fold = @displayBuffer.largestFoldStartingAtBufferRow(insertPosition.row)
+          @unfoldBufferRow(insertPosition.row)
+          foldedRows.push(insertPosition.row + endRow - startRow + fold.getBufferRange().getRowCount())
+
+        @buffer.insert(insertPosition, lines)
+
+      # Restore folds that existed before the lines were moved
+      for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow()
+        @foldBufferRow(foldedRow)
+
+      @setSelectedBufferRange(selection.translate([-insertDelta]), preserveFolds: true, autoscroll: true)
+
+  # Move lines intersecting the most recent selection down by one row in screen
+  # coordinates.
+  moveLineDown: ->
+    selection = @getSelectedBufferRange()
+    lastRow = @buffer.getLastRow()
+    return if selection.end.row is lastRow
+    return if selection.end.row is lastRow - 1 and @buffer.getLastLine() is ''
+
+    @transact =>
+      foldedRows = []
+      rows = [selection.end.row..selection.start.row]
+      if selection.start.row isnt selection.end.row and selection.end.column is 0
+        rows.shift() unless @isFoldedAtBufferRow(selection.end.row)
+
+      # Move line around the fold that is directly below the selection
+      followingScreenRow = @screenPositionForBufferPosition([selection.end.row]).translate([1])
+      followingBufferRow = @bufferPositionForScreenPosition(followingScreenRow).row
+      if fold = @largestFoldContainingBufferRow(followingBufferRow)
+        insertDelta = fold.getBufferRange().getRowCount()
+      else
+        insertDelta = 1
+
+      for row in rows
+        if fold = @displayBuffer.largestFoldStartingAtBufferRow(row)
+          bufferRange = fold.getBufferRange()
+          startRow = bufferRange.start.row
+          endRow = bufferRange.end.row
+          foldedRows.push(endRow + insertDelta)
+        else
+          startRow = row
+          endRow = row
+
+        if endRow + 1 is lastRow
+          endPosition = [endRow, @buffer.lineLengthForRow(endRow)]
+        else
+          endPosition = [endRow + 1]
+        lines = @buffer.getTextInRange([[startRow], endPosition])
+        @buffer.deleteRows(startRow, endRow)
+
+        insertPosition = Point.min([startRow + insertDelta], @buffer.getEndPosition())
+        if insertPosition.row is @buffer.getLastRow() and insertPosition.column > 0
+          lines = "\n#{lines}"
+
+        # Make sure the inserted text doesn't go into an existing fold
+        if fold = @displayBuffer.largestFoldStartingAtBufferRow(insertPosition.row)
+          @unfoldBufferRow(insertPosition.row)
+          foldedRows.push(insertPosition.row + fold.getBufferRange().getRowCount())
+
+        @buffer.insert(insertPosition, lines)
+
+      # Restore folds that existed before the lines were moved
+      for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow()
+        @foldBufferRow(foldedRow)
+
+      @setSelectedBufferRange(selection.translate([insertDelta]), preserveFolds: true, autoscroll: true)
+
+  # Duplicate the most recent cursor's current line.
+  duplicateLines: ->
+    @transact =>
+      for selection in @getSelectionsOrderedByBufferPosition().reverse()
+        selectedBufferRange = selection.getBufferRange()
+        if selection.isEmpty()
+          {start} = selection.getScreenRange()
+          selection.selectToScreenPosition([start.row + 1, 0])
+
+        [startRow, endRow] = selection.getBufferRowRange()
+        endRow++
+
+        foldedRowRanges =
+          @outermostFoldsInBufferRowRange(startRow, endRow)
+            .map (fold) -> fold.getBufferRowRange()
+
+        rangeToDuplicate = [[startRow, 0], [endRow, 0]]
+        textToDuplicate = @getTextInBufferRange(rangeToDuplicate)
+        textToDuplicate = '\n' + textToDuplicate if endRow > @getLastBufferRow()
+        @buffer.insert([endRow, 0], textToDuplicate)
+
+        delta = endRow - startRow
+        selection.setBufferRange(selectedBufferRange.translate([delta, 0]))
+        for [foldStartRow, foldEndRow] in foldedRowRanges
+          @createFold(foldStartRow + delta, foldEndRow + delta)
+
+  # Deprecated: Use {::duplicateLines} instead.
+  duplicateLine: ->
+    deprecate("Use Editor::duplicateLines() instead")
+    @duplicateLines()
+
+  # Public: Mutate the text of all the selections in a single transaction.
+  #
+  # All the changes made inside the given {Function} can be reverted with a
+  # single call to {::undo}.
+  #
+  # fn - A {Function} that will be called with each {Selection}.
+  mutateSelectedText: (fn) ->
+    @transact => fn(selection,index) for selection,index in @getSelections()
+
+  replaceSelectedText: (options={}, fn) ->
+    {selectWordIfEmpty} = options
+    @mutateSelectedText (selection) ->
+      range = selection.getBufferRange()
+      if selectWordIfEmpty and selection.isEmpty()
+        selection.selectWord()
+      text = selection.getText()
+      selection.deleteSelectedText()
+      selection.insertText(fn(text))
+      selection.setBufferRange(range)
+
+  # Public: Get the {DisplayBufferMarker} for the given marker id.
+  getMarker: (id) ->
+    @displayBuffer.getMarker(id)
+
+  # Public: Get all {DisplayBufferMarker}s.
+  getMarkers: ->
+    @displayBuffer.getMarkers()
+
+  # Public: Find all {DisplayBufferMarker}s that match the given properties.
+  #
+  # This method finds markers based on the given properties. Markers can be
+  # associated with custom properties that will be compared with basic equality.
+  # In addition, there are several special properties that will be compared
+  # with the range of the markers rather than their properties.
+  #
+  # properties - An {Object} containing properties that each returned marker
+  #   must satisfy. Markers can be associated with custom properties, which are
+  #   compared with basic equality. In addition, several reserved properties
+  #   can be used to filter markers based on their current range:
+  #     :startBufferRow - Only include markers starting at this row in buffer
+  #       coordinates.
+  #     :endBufferRow - Only include markers ending at this row in buffer
+  #       coordinates.
+  #     :containsBufferRange - Only include markers containing this {Range} or
+  #       in range-compatible {Array} in buffer coordinates.
+  #     :containsBufferPosition - Only include markers containing this {Point}
+  #       or {Array} of `[row, column]` in buffer coordinates.
+  findMarkers: (properties) ->
+    @displayBuffer.findMarkers(properties)
+
+  # Public: Mark the given range in screen coordinates.
+  #
+  # range - A {Range} or range-compatible {Array}.
+  # options - See {TextBuffer::markRange}.
+  #
+  # Returns a {DisplayBufferMarker}.
+  markScreenRange: (args...) ->
+    @displayBuffer.markScreenRange(args...)
+
+  # Public: Mark the given range in buffer coordinates.
+  #
+  # range - A {Range} or range-compatible {Array}.
+  # options - See {TextBuffer::markRange}.
+  #
+  # Returns a {DisplayBufferMarker}.
+  markBufferRange: (args...) ->
+    @displayBuffer.markBufferRange(args...)
+
+  # Public: Mark the given position in screen coordinates.
+  #
+  # position - A {Point} or {Array} of `[row, column]`.
+  # options - See {TextBuffer::markRange}.
+  #
+  # Returns a {DisplayBufferMarker}.
+  markScreenPosition: (args...) ->
+    @displayBuffer.markScreenPosition(args...)
+
+  # Public: Mark the given position in buffer coordinates.
+  #
+  # position - A {Point} or {Array} of `[row, column]`.
+  # options - See {TextBuffer::markRange}.
+  #
+  # Returns a {DisplayBufferMarker}.
+  markBufferPosition: (args...) ->
+    @displayBuffer.markBufferPosition(args...)
+
+  # {Delegates to: DisplayBuffer.destroyMarker}
+  destroyMarker: (args...) ->
+    @displayBuffer.destroyMarker(args...)
+
+  # Public: Get the number of markers in this editor's buffer.
+  #
+  # Returns a {Number}.
+  getMarkerCount: ->
+    @buffer.getMarkerCount()
+
+  # Public: Determine if there are multiple cursors.
+  hasMultipleCursors: ->
+    @getCursors().length > 1
+
+  # Public: Get an Array of all {Cursor}s.
+  getCursors: -> new Array(@cursors...)
+
+  # Public: Get the most recently added {Cursor}.
+  getCursor: ->
+    _.last(@cursors)
+
+  # Public: Add a cursor at the position in screen coordinates.
+  #
+  # Returns a {Cursor}.
+  addCursorAtScreenPosition: (screenPosition) ->
+    @markScreenPosition(screenPosition, @getSelectionMarkerAttributes())
+    @getLastSelection().cursor
+
+  # Public: Add a cursor at the given position in buffer coordinates.
+  #
+  # Returns a {Cursor}.
+  addCursorAtBufferPosition: (bufferPosition) ->
+    @markBufferPosition(bufferPosition, @getSelectionMarkerAttributes())
+    @getLastSelection().cursor
+
+  # Add a cursor based on the given {DisplayBufferMarker}.
+  addCursor: (marker) ->
+    cursor = new Cursor(editor: this, marker: marker)
+    @cursors.push(cursor)
+    @emit 'cursor-added', cursor
+    cursor
+
+  # Remove the given cursor from this editor.
+  removeCursor: (cursor) ->
+    _.remove(@cursors, cursor)
+
+  # Add a {Selection} based on the given {DisplayBufferMarker}.
+  #
+  # marker  - The {DisplayBufferMarker} to highlight
+  # options - An {Object} that pertains to the {Selection} constructor.
+  #
+  # Returns the new {Selection}.
+  addSelection: (marker, options={}) ->
+    unless marker.getAttributes().preserveFolds
+      @destroyFoldsIntersectingBufferRange(marker.getBufferRange())
+    cursor = @addCursor(marker)
+    selection = new Selection(_.extend({editor: this, marker, cursor}, options))
+    @selections.push(selection)
+    selectionBufferRange = selection.getBufferRange()
+    @mergeIntersectingSelections()
+    if selection.destroyed
+      for selection in @getSelections()
+        if selection.intersectsBufferRange(selectionBufferRange)
+          return selection
+    else
+      @emit 'selection-added', selection
+      selection
+
+  # Public: Add a selection for the given range in buffer coordinates.
+  #
+  # bufferRange - A {Range}
+  # options - An options {Object}:
+  #   :reversed - A {Boolean} indicating whether to create the selection in a
+  #     reversed orientation.
+  #
+  # Returns the added {Selection}.
+  addSelectionForBufferRange: (bufferRange, options={}) ->
+    @markBufferRange(bufferRange, _.defaults(@getSelectionMarkerAttributes(), options))
+    @getLastSelection()
+
+  # Public: Set the selected range in buffer coordinates. If there are multiple
+  # selections, they are reduced to a single selection with the given range.
+  #
+  # bufferRange - A {Range} or range-compatible {Array}.
+  # options - An options {Object}:
+  #   :reversed - A {Boolean} indicating whether to create the selection in a
+  #     reversed orientation.
+  setSelectedBufferRange: (bufferRange, options) ->
+    @setSelectedBufferRanges([bufferRange], options)
+
+  # Public: Set the selected range in screen coordinates. If there are multiple
+  # selections, they are reduced to a single selection with the given range.
+  #
+  # screenRange - A {Range} or range-compatible {Array}.
+  # options - An options {Object}:
+  #   :reversed - A {Boolean} indicating whether to create the selection in a
+  #     reversed orientation.
+  setSelectedScreenRange: (screenRange, options) ->
+    @setSelectedBufferRange(@bufferRangeForScreenRange(screenRange, options), options)
+
+  # Public: Set the selected ranges in buffer coordinates. If there are multiple
+  # selections, they are replaced by new selections with the given ranges.
+  #
+  # bufferRanges - An {Array} of {Range}s or range-compatible {Array}s.
+  # options - An options {Object}:
+  #   :reversed - A {Boolean} indicating whether to create the selection in a
+  #     reversed orientation.
+  setSelectedBufferRanges: (bufferRanges, options={}) ->
+    throw new Error("Passed an empty array to setSelectedBufferRanges") unless bufferRanges.length
+
+    selections = @getSelections()
+    selection.destroy() for selection in selections[bufferRanges.length...]
+
+    @mergeIntersectingSelections options, =>
+      for bufferRange, i in bufferRanges
+        bufferRange = Range.fromObject(bufferRange)
+        if selections[i]
+          selections[i].setBufferRange(bufferRange, options)
+        else
+          @addSelectionForBufferRange(bufferRange, options)
+
+  # Remove the given selection.
+  removeSelection: (selection) ->
+    _.remove(@selections, selection)
+    @emit 'selection-removed', selection
+
+  # Reduce one or more selections to a single empty selection based on the most
+  # recently added cursor.
+  clearSelections: ->
+    @consolidateSelections()
+    @getSelection().clear()
+
+  # Reduce multiple selections to the most recently added selection.
+  consolidateSelections: ->
+    selections = @getSelections()
+    if selections.length > 1
+      selection.destroy() for selection in selections[0...-1]
+      true
+    else
+      false
+
+  selectionScreenRangeChanged: (selection) ->
+    @emit 'selection-screen-range-changed', selection
+
+  # Public: Get current {Selection}s.
+  #
+  # Returns: An {Array} of {Selection}s.
+  getSelections: -> new Array(@selections...)
+
+  # Public: Get the most recent {Selection} or the selection at the given
+  # index.
+  #
+  # index - Optional. The index of the selection to return, based on the order
+  #   in which the selections were added.
+  #
+  # Returns a {Selection}.
+  # or the  at the specified index.
+  getSelection: (index) ->
+    index ?= @selections.length - 1
+    @selections[index]
+
+  # Public: Get the most recently added {Selection}.
+  #
+  # Returns a {Selection}.
+  getLastSelection: ->
+    _.last(@selections)
+
+  # Public: Get all {Selection}s, ordered by their position in the buffer
+  # instead of the order in which they were added.
+  #
+  # Returns an {Array} of {Selection}s.
+  getSelectionsOrderedByBufferPosition: ->
+    @getSelections().sort (a, b) -> a.compare(b)
+
+  # Public: Get the last {Selection} based on its position in the buffer.
+  #
+  # Returns a {Selection}.
+  getLastSelectionInBuffer: ->
+    _.last(@getSelectionsOrderedByBufferPosition())
+
+  # Public: Determine if a given range in buffer coordinates intersects a
+  # selection.
+  #
+  # bufferRange - A {Range} or range-comptatible {Array}.
+  #
+  # Returns a {Boolean}.
+  selectionIntersectsBufferRange: (bufferRange) ->
+    _.any @getSelections(), (selection) ->
+      selection.intersectsBufferRange(bufferRange)
+
+  # Public: Move the cursor to the given position in screen coordinates.
+  #
+  # If there are multiple cursors, they will be consolidated to a single cursor.
+  #
+  # position - A {Point} or {Array} of `[row, column]`
+  # options  - An {Object} combining options for {::clipScreenPosition} with:
+  #   :autoscroll - Determines whether the editor scrolls to the new cursor's
+  #     position. Defaults to true.
+  setCursorScreenPosition: (position, options) ->
+    @moveCursors (cursor) -> cursor.setScreenPosition(position, options)
+
+  # Public: Get the position of the most recently added cursor in screen
+  # coordinates.
+  #
+  # Returns a {Point}.
+  getCursorScreenPosition: ->
+    @getCursor().getScreenPosition()
+
+  # Public: Get the row of the most recently added cursor in screen coordinates.
+  #
+  # Returns the screen row {Number}.
+  getCursorScreenRow: ->
+    @getCursor().getScreenRow()
+
+  # Public: Move the cursor to the given position in buffer coordinates.
+  #
+  # If there are multiple cursors, they will be consolidated to a single cursor.
+  #
+  # position - A {Point} or {Array} of `[row, column]`
+  # options  - An {Object} combining options for {::clipScreenPosition} with:
+  #   :autoscroll - Determines whether the editor scrolls to the new cursor's
+  #     position. Defaults to true.
+  setCursorBufferPosition: (position, options) ->
+    @moveCursors (cursor) -> cursor.setBufferPosition(position, options)
+
+  # Public: Get the position of the most recently added cursor in buffer
+  # coordinates.
+  #
+  # Returns a {Point}.
+  getCursorBufferPosition: ->
+    @getCursor().getBufferPosition()
+
+  # Public: Get the {Range} of the most recently added selection in screen
+  # coordinates.
+  #
+  # Returns a {Range}.
+  getSelectedScreenRange: ->
+    @getLastSelection().getScreenRange()
+
+  # Public: Get the {Range} of the most recently added selection in buffer
+  # coordinates.
+  #
+  # Returns a {Range}.
+  getSelectedBufferRange: ->
+    @getLastSelection().getBufferRange()
+
+  # Public: Get the {Range}s of all selections in buffer coordinates.
+  #
+  # The ranges are sorted by their position in the buffer.
+  #
+  # Returns an {Array} of {Range}s.
+  getSelectedBufferRanges: ->
+    selection.getBufferRange() for selection in @getSelectionsOrderedByBufferPosition()
+
+  # Public: Get the {Range}s of all selections in screen coordinates.
+  #
+  # The ranges are sorted by their position in the buffer.
+  #
+  # Returns an {Array} of {Range}s.
+  getSelectedScreenRanges: ->
+    selection.getScreenRange() for selection in @getSelectionsOrderedByBufferPosition()
+
+  # Public: Get the selected text of the most recently added selection.
+  #
+  # Returns a {String}.
+  getSelectedText: ->
+    @getLastSelection().getText()
+
+  # Public: Get the text in the given {Range} in buffer coordinates.
+  #
+  # range - A {Range} or range-compatible {Array}.
+  #
+  # Returns a {String}.
+  getTextInBufferRange: (range) ->
+    @buffer.getTextInRange(range)
+
+  # Public: Set the text in the given {Range} in buffer coordinates.
+  #
+  # range - A {Range} or range-compatible {Array}.
+  # text - A {String}
+  #
+  # Returns the {Range} of the newly-inserted text.
+  setTextInBufferRange: (range, text) -> @getBuffer().setTextInRange(range, text)
+
+  # Public: Get the {Range} of the paragraph surrounding the most recently added
+  # cursor.
+  #
+  # Returns a {Range}.
+  getCurrentParagraphBufferRange: ->
+    @getCursor().getCurrentParagraphBufferRange()
+
+  # Public: Returns the word surrounding the most recently added cursor.
+  #
+  # options - See {Cursor::getBeginningOfCurrentWordBufferPosition}.
+  getWordUnderCursor: (options) ->
+    @getTextInBufferRange(@getCursor().getCurrentWordBufferRange(options))
+
+  # Public: Move every cursor up one row in screen coordinates.
+  moveCursorUp: (lineCount) ->
+    @moveCursors (cursor) -> cursor.moveUp(lineCount, moveToEndOfSelection: true)
+
+  # Public: Move every cursor down one row in screen coordinates.
+  moveCursorDown: (lineCount) ->
+    @moveCursors (cursor) -> cursor.moveDown(lineCount, moveToEndOfSelection: true)
+
+  # Public: Move every cursor left one column.
+  moveCursorLeft: ->
+    @moveCursors (cursor) -> cursor.moveLeft(moveToEndOfSelection: true)
+
+  # Public: Move every cursor right one column.
+  moveCursorRight: ->
+    @moveCursors (cursor) -> cursor.moveRight(moveToEndOfSelection: true)
+
+  # Public: Move every cursor to the top of the buffer.
+  #
+  # If there are multiple cursors, they will be merged into a single cursor.
+  moveCursorToTop: ->
+    @moveCursors (cursor) -> cursor.moveToTop()
+
+  # Public: Move every cursor to the bottom of the buffer.
+  #
+  # If there are multiple cursors, they will be merged into a single cursor.
+  moveCursorToBottom: ->
+    @moveCursors (cursor) -> cursor.moveToBottom()
+
+  # Public: Move every cursor to the beginning of its line in screen coordinates.
+  moveCursorToBeginningOfScreenLine: ->
+    @moveCursors (cursor) -> cursor.moveToBeginningOfScreenLine()
+
+  # Public: Move every cursor to the beginning of its line in buffer coordinates.
+  moveCursorToBeginningOfLine: ->
+    @moveCursors (cursor) -> cursor.moveToBeginningOfLine()
+
+  # Public: Move every cursor to the first non-whitespace character of its line.
+  moveCursorToFirstCharacterOfLine: ->
+    @moveCursors (cursor) -> cursor.moveToFirstCharacterOfLine()
+
+  # Public: Move every cursor to the end of its line in screen coordinates.
+  moveCursorToEndOfScreenLine: ->
+    @moveCursors (cursor) -> cursor.moveToEndOfScreenLine()
+
+  # Public: Move every cursor to the end of its line in buffer coordinates.
+  moveCursorToEndOfLine: ->
+    @moveCursors (cursor) -> cursor.moveToEndOfLine()
+
+  # Public: Move every cursor to the beginning of its surrounding word.
+  moveCursorToBeginningOfWord: ->
+    @moveCursors (cursor) -> cursor.moveToBeginningOfWord()
+
+  # Public: Move every cursor to the end of its surrounding word.
+  moveCursorToEndOfWord: ->
+    @moveCursors (cursor) -> cursor.moveToEndOfWord()
+
+  # Public: Move every cursor to the beginning of the next word.
+  moveCursorToBeginningOfNextWord: ->
+    @moveCursors (cursor) -> cursor.moveToBeginningOfNextWord()
+
+  # Public: Move every cursor to the previous word boundary.
+  moveCursorToPreviousWordBoundary: ->
+    @moveCursors (cursor) -> cursor.moveToPreviousWordBoundary()
+
+  # Public: Move every cursor to the next word boundary.
+  moveCursorToNextWordBoundary: ->
+    @moveCursors (cursor) -> cursor.moveToNextWordBoundary()
+
+  scrollToCursorPosition: ->
+    @getCursor().autoscroll()
+
+  pageUp: ->
+    @setScrollTop(@getScrollTop() - @getHeight())
+
+  pageDown: ->
+    @setScrollTop(@getScrollTop() + @getHeight())
+
+  moveCursors: (fn) ->
+    @movingCursors = true
+    @batchUpdates =>
+      fn(cursor) for cursor in @getCursors()
+      @mergeCursors()
+      @movingCursors = false
+      @emit 'cursors-moved'
+
+  cursorMoved: (event) ->
+    @emit 'cursor-moved', event
+    @emit 'cursors-moved' unless @movingCursors
+
+  # Public: Select from the current cursor position to the given position in
+  # screen coordinates.
+  #
+  # This method may merge selections that end up intesecting.
+  #
+  # position - An instance of {Point}, with a given `row` and `column`.
+  selectToScreenPosition: (position) ->
+    lastSelection = @getLastSelection()
+    lastSelection.selectToScreenPosition(position)
+    @mergeIntersectingSelections(reversed: lastSelection.isReversed())
+
+  # Public: Move the cursor of each selection one character rightward while
+  # preserving the selection's tail position.
+  #
+  # This method may merge selections that end up intesecting.
+  selectRight: ->
+    @expandSelectionsForward (selection) => selection.selectRight()
+
+  # Public: Move the cursor of each selection one character leftward while
+  # preserving the selection's tail position.
+  #
+  # This method may merge selections that end up intesecting.
+  selectLeft: ->
+    @expandSelectionsBackward (selection) => selection.selectLeft()
+
+  # Public: Move the cursor of each selection one character upward while
+  # preserving the selection's tail position.
+  #
+  # This method may merge selections that end up intesecting.
+  selectUp: (rowCount) ->
+    @expandSelectionsBackward (selection) => selection.selectUp(rowCount)
+
+  # Public: Move the cursor of each selection one character downward while
+  # preserving the selection's tail position.
+  #
+  # This method may merge selections that end up intesecting.
+  selectDown: (rowCount) ->
+    @expandSelectionsForward (selection) => selection.selectDown(rowCount)
+
+  # Public: Select from the top of the buffer to the end of the last selection
+  # in the buffer.
+  #
+  # This method merges multiple selections into a single selection.
+  selectToTop: ->
+    @expandSelectionsBackward (selection) => selection.selectToTop()
+
+  # Public: Select all text in the buffer.
+  #
+  # This method merges multiple selections into a single selection.
+  selectAll: ->
+    @expandSelectionsForward (selection) => selection.selectAll()
+
+  # Public: Selects from the top of the first selection in the buffer to the end
+  # of the buffer.
+  #
+  # This method merges multiple selections into a single selection.
+  selectToBottom: ->
+    @expandSelectionsForward (selection) => selection.selectToBottom()
+
+  # Public: Move the cursor of each selection to the beginning of its line
+  # while preserving the selection's tail position.
+  #
+  # This method may merge selections that end up intesecting.
+  selectToBeginningOfLine: ->
+    @expandSelectionsBackward (selection) => selection.selectToBeginningOfLine()
+
+  # Public: Move the cursor of each selection to the first non-whitespace
+  # character of its line while preserving the selection's tail position. If the
+  # cursor is already on the first character of the line, move it to the
+  # beginning of the line.
+  #
+  # This method may merge selections that end up intesecting.
+  selectToFirstCharacterOfLine: ->
+    @expandSelectionsBackward (selection) => selection.selectToFirstCharacterOfLine()
+
+  # Public: Move the cursor of each selection to the end of its line while
+  # preserving the selection's tail position.
+  #
+  # This method may merge selections that end up intesecting.
+  selectToEndOfLine: ->
+    @expandSelectionsForward (selection) => selection.selectToEndOfLine()
+
+  # Public: For each selection, move its cursor to the preceding word boundary
+  # while maintaining the selection's tail position.
+  #
+  # This method may merge selections that end up intesecting.
+  selectToPreviousWordBoundary: ->
+    @expandSelectionsBackward (selection) => selection.selectToPreviousWordBoundary()
+
+  # Public: For each selection, move its cursor to the next word boundary while
+  # maintaining the selection's tail position.
+  #
+  # This method may merge selections that end up intesecting.
+  selectToNextWordBoundary: ->
+    @expandSelectionsForward (selection) => selection.selectToNextWordBoundary()
+
+  # Public: For each cursor, select the containing line.
+  #
+  # This method merges selections on successive lines.
+  selectLine: ->
+    @expandSelectionsForward (selection) => selection.selectLine()
+
+  # Public: Add a similarly-shaped selection to the next elibible line below
+  # each selection.
+  #
+  # Operates on all selections. If the selection is empty, adds an empty
+  # selection to the next following non-empty line as close to the current
+  # selection's column as possible. If the selection is non-empty, adds a
+  # selection to the next line that is long enough for a non-empty selection
+  # starting at the same column as the current selection to be added to it.
+  addSelectionBelow: ->
+    @expandSelectionsForward (selection) => selection.addSelectionBelow()
+
+  # Public: Add a similarly-shaped selection to the next elibible line above
+  # each selection.
+  #
+  # Operates on all selections. If the selection is empty, adds an empty
+  # selection to the next preceding non-empty line as close to the current
+  # selection's column as possible. If the selection is non-empty, adds a
+  # selection to the next line that is long enough for a non-empty selection
+  # starting at the same column as the current selection to be added to it.
+  addSelectionAbove: ->
+    @expandSelectionsBackward (selection) => selection.addSelectionAbove()
+
+  # Public: Split multi-line selections into one selection per line.
+  #
+  # Operates on all selections. This method breaks apart all multi-line
+  # selections to create multiple single-line selections that cumulatively cover
+  # the same original area.
+  splitSelectionsIntoLines: ->
+    for selection in @getSelections()
+      range = selection.getBufferRange()
+      continue if range.isSingleLine()
+
+      selection.destroy()
+      {start, end} = range
+      @addSelectionForBufferRange([start, [start.row, Infinity]])
+      {row} = start
+      while ++row < end.row
+        @addSelectionForBufferRange([[row, 0], [row, Infinity]])
+      @addSelectionForBufferRange([[end.row, 0], [end.row, end.column]])
+
+  # Public: For each selection, transpose the selected text.
+  #
+  # If the selection is empty, the characters preceding and following the cursor
+  # are swapped. Otherwise, the selected characters are reversed.
+  transpose: ->
+    @mutateSelectedText (selection) =>
+      if selection.isEmpty()
+        selection.selectRight()
+        text = selection.getText()
+        selection.delete()
+        selection.cursor.moveLeft()
+        selection.insertText text
+      else
+        selection.insertText selection.getText().split('').reverse().join('')
+
+  # Public: Convert the selected text to upper case.
+  #
+  # For each selection, if the selection is empty, converts the containing word
+  # to upper case. Otherwise convert the selected text to upper case.
+  upperCase: ->
+    @replaceSelectedText selectWordIfEmpty:true, (text) => text.toUpperCase()
+
+  # Public: Convert the selected text to lower case.
+  #
+  # For each selection, if the selection is empty, converts the containing word
+  # to upper case. Otherwise convert the selected text to upper case.
+  lowerCase: ->
+    @replaceSelectedText selectWordIfEmpty:true, (text) => text.toLowerCase()
+
+  # Convert multiple lines to a single line.
+  #
+  # Operates on all selections. If the selection is empty, joins the current
+  # line with the next line. Otherwise it joins all lines that intersect the
+  # selection.
+  #
+  # Joining a line means that multiple lines are converted to a single line with
+  # the contents of each of the original non-empty lines separated by a space.
+  joinLines: ->
+    @mutateSelectedText (selection) -> selection.joinLines()
+
+  # Public: Expand selections to the beginning of their containing word.
+  #
+  # Operates on all selections. Moves the cursor to the beginning of the
+  # containing word while preserving the selection's tail position.
+  selectToBeginningOfWord: ->
+    @expandSelectionsBackward (selection) => selection.selectToBeginningOfWord()
+
+  # Public: Expand selections to the end of their containing word.
+  #
+  # Operates on all selections. Moves the cursor to the end of the containing
+  # word while preserving the selection's tail position.
+  selectToEndOfWord: ->
+    @expandSelectionsForward (selection) => selection.selectToEndOfWord()
+
+  # Public: Expand selections to the beginning of the next word.
+  #
+  # Operates on all selections. Moves the cursor to the beginning of the next
+  # word while preserving the selection's tail position.
+  selectToBeginningOfNextWord: ->
+    @expandSelectionsForward (selection) => selection.selectToBeginningOfNextWord()
+
+  # Public: Select the word containing each cursor.
+  selectWord: ->
+    @expandSelectionsForward (selection) => selection.selectWord()
+
+  # Public: Select the range of the given marker if it is valid.
+  #
+  # marker - A {DisplayBufferMarker}
+  #
+  # Returns the selected {Range} or `undefined` if the marker is invalid.
+  selectMarker: (marker) ->
+    if marker.isValid()
+      range = marker.getBufferRange()
+      @setSelectedBufferRange(range)
+      range
+
+  # Merge cursors that have the same screen position
+  mergeCursors: ->
+    positions = []
+    for cursor in @getCursors()
+      position = cursor.getBufferPosition().toString()
+      if position in positions
+        cursor.destroy()
+      else
+        positions.push(position)
+
+  # Calls the given function with each selection, then merges selections
+  expandSelectionsForward: (fn) ->
+    @mergeIntersectingSelections =>
+      fn(selection) for selection in @getSelections()
+
+  # Calls the given function with each selection, then merges selections in the
+  # reversed orientation
+  expandSelectionsBackward: (fn) ->
+    @mergeIntersectingSelections reversed: true, =>
+      fn(selection) for selection in @getSelections()
+
+  finalizeSelections: ->
+    selection.finalize() for selection in @getSelections()
+
+  # Merges intersecting selections. If passed a function, it executes
+  # the function with merging suppressed, then merges intersecting selections
+  # afterward.
+  mergeIntersectingSelections: (args...) ->
+    fn = args.pop() if _.isFunction(_.last(args))
+    options = args.pop() ? {}
+
+    return fn?() if @suppressSelectionMerging
+
+    if fn?
+      @suppressSelectionMerging = true
+      result = fn()
+      @suppressSelectionMerging = false
+
+    reducer = (disjointSelections, selection) ->
+      intersectingSelection = _.find(disjointSelections, (s) -> s.intersectsWith(selection))
+      if intersectingSelection?
+        intersectingSelection.merge(selection, options)
+        disjointSelections
+      else
+        disjointSelections.concat([selection])
+
+    _.reduce(@getSelections(), reducer, [])
+
+  preserveCursorPositionOnBufferReload: ->
+    cursorPosition = null
+    @subscribe @buffer, "will-reload", =>
+      cursorPosition = @getCursorBufferPosition()
+    @subscribe @buffer, "reloaded", =>
+      @setCursorBufferPosition(cursorPosition) if cursorPosition
+      cursorPosition = null
+
+  # Public: Get the current {Grammar} of this editor.
+  getGrammar: ->
+    @displayBuffer.getGrammar()
+
+  # Public: Set the current {Grammar} of this editor.
+  #
+  # Assigning a grammar will cause the editor to re-tokenize based on the new
+  # grammar.
+  setGrammar: (grammar) ->
+    @displayBuffer.setGrammar(grammar)
+
+  # Reload the grammar based on the file name.
+  reloadGrammar: ->
+    @displayBuffer.reloadGrammar()
+
+  shouldAutoIndent: ->
+    atom.config.get("editor.autoIndent")
+
+  # Public: 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) ->
+    @batchUpdates =>
+      @buffer.transact(fn)
+
+  # Public: 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()
+
+  # Public: 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()
+
+  # Public: Abort an open transaction, undoing any operations performed so far
+  # within the transaction.
+  abortTransaction: -> @buffer.abortTransaction()
+
+  batchUpdates: (fn) ->
+    @emit 'batched-updates-started'
+    result = fn()
+    @emit 'batched-updates-ended'
+    result
+
+  inspect: ->
+    "<Editor #{@id}>"
+
+  logScreenLines: (start, end) -> @displayBuffer.logLines(start, end)
+
+  handleGrammarChange: ->
+    @unfoldAll()
+    @emit 'grammar-changed'
+
+  handleMarkerCreated: (marker) =>
+    if marker.matchesAttributes(@getSelectionMarkerAttributes())
+      @addSelection(marker)
+
+  getSelectionMarkerAttributes: ->
+    type: 'selection', editorId: @id, invalidate: 'never'
+
+  getVerticalScrollMargin: -> @displayBuffer.getVerticalScrollMargin()
+  setVerticalScrollMargin: (verticalScrollMargin) -> @displayBuffer.setVerticalScrollMargin(verticalScrollMargin)
+
+  getHorizontalScrollMargin: -> @displayBuffer.getHorizontalScrollMargin()
+  setHorizontalScrollMargin: (horizontalScrollMargin) -> @displayBuffer.setHorizontalScrollMargin(horizontalScrollMargin)
+
+  getLineHeight: -> @displayBuffer.getLineHeight()
+  setLineHeight: (lineHeight) -> @displayBuffer.setLineHeight(lineHeight)
+
+  getScopedCharWidth: (scopeNames, char) -> @displayBuffer.getScopedCharWidth(scopeNames, char)
+  setScopedCharWidth: (scopeNames, char, width) -> @displayBuffer.setScopedCharWidth(scopeNames, char, width)
+
+  getScopedCharWidths: (scopeNames) -> @displayBuffer.getScopedCharWidths(scopeNames)
+
+  clearScopedCharWidths: -> @displayBuffer.clearScopedCharWidths()
+
+  getDefaultCharWidth: -> @displayBuffer.getDefaultCharWidth()
+  setDefaultCharWidth: (defaultCharWidth) -> @displayBuffer.setDefaultCharWidth(defaultCharWidth)
+
+  setHeight: (height) -> @displayBuffer.setHeight(height)
+  getHeight: -> @displayBuffer.getHeight()
+
+  setWidth: (width) -> @displayBuffer.setWidth(width)
+  getWidth: -> @displayBuffer.getWidth()
+
+  getScrollTop: -> @displayBuffer.getScrollTop()
+  setScrollTop: (scrollTop) -> @displayBuffer.setScrollTop(scrollTop)
+
+  getScrollBottom: -> @displayBuffer.getScrollBottom()
+  setScrollBottom: (scrollBottom) -> @displayBuffer.setScrollBottom(scrollBottom)
+
+  getScrollLeft: -> @displayBuffer.getScrollLeft()
+  setScrollLeft: (scrollLeft) -> @displayBuffer.setScrollLeft(scrollLeft)
+
+  getScrollRight: -> @displayBuffer.getScrollRight()
+  setScrollRight: (scrollRight) -> @displayBuffer.setScrollRight(scrollRight)
+
+  getScrollHeight: -> @displayBuffer.getScrollHeight()
+  getScrollWidth: (scrollWidth) -> @displayBuffer.getScrollWidth(scrollWidth)
+
+  getVisibleRowRange: -> @displayBuffer.getVisibleRowRange()
+
+  intersectsVisibleRowRange: (startRow, endRow) -> @displayBuffer.intersectsVisibleRowRange(startRow, endRow)
+
+  selectionIntersectsVisibleRowRange: (selection) -> @displayBuffer.selectionIntersectsVisibleRowRange(selection)
+
+  pixelPositionForScreenPosition: (screenPosition) -> @displayBuffer.pixelPositionForScreenPosition(screenPosition)
+
+  pixelPositionForBufferPosition: (bufferPosition) -> @displayBuffer.pixelPositionForBufferPosition(bufferPosition)
+
+  screenPositionForPixelPosition: (pixelPosition) -> @displayBuffer.screenPositionForPixelPosition(pixelPosition)
+
+  pixelRectForScreenRange: (screenRange) -> @displayBuffer.pixelRectForScreenRange(screenRange)
+
+  scrollToScreenRange: (screenRange) -> @displayBuffer.scrollToScreenRange(screenRange)
+
+  scrollToScreenPosition: (screenPosition) -> @displayBuffer.scrollToScreenPosition(screenPosition)
+
+  scrollToBufferPosition: (bufferPosition) -> @displayBuffer.scrollToBufferPosition(bufferPosition)
+
+  # Deprecated: Call {::joinLines} instead.
+  joinLine: ->
+    deprecate("Use Editor::joinLines() instead")
+    @joinLines()


add unsubscribe

add unsubscribe

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/zzzev/insta.git/'


Refactor when values

Refactor when values

--- app/index.coffee
+++ app/index.coffee
@@ -58,9 +58,8 @@
                 name: 'revealTheme'
                 type: 'list'
                 message: 'What Reveal.js theme would you like to use?'
-                when: (props) ->
-                      return !props.useSass
-                choices: JSON.parse @readFileAsString path.join __dirname, './theme_choices.json'
+                when: (props) -> props.useSass is off
+                choices: @fs.readJSON path.join __dirname, './theme_choices.json'
                 default: @config.get 'revealTheme'
             }
             {
@@ -73,15 +72,13 @@
                 name: 'githubUsername'
                 message: 'What is your Github username?'
                 default:  @config.get 'githubUsername'
-                when: (props) ->
-                    return props.deployToGithubPages
+                when: (props) -> props.deployToGithubPages is on
             }
             {
                 name: 'githubRepository'
                 message: 'What is the Github repository name?'
                 default: @config.get 'githubRepository'
-                when: (props) ->
-                    return props.deployToGithubPages
+                when: (props) -> props.deployToGithubPages is on
             }
         ]
         @prompt prompts, (props) =>


Fix undo accelerator

Fix undo accelerator

'+' is the proper join string, not '-'.

--- menus/darwin.cson
+++ menus/darwin.cson
@@ -19,7 +19,7 @@
     label: 'Edit'
     submenu: [
       { label: 'Undo', selector: 'undo:', accelerator: 'Command+Z' }
-      { label: 'Redo', selector: 'redo:', accelerator: 'Shift-Command+Z' }
+      { label: 'Redo', selector: 'redo:', accelerator: 'Shift+Command+Z' }
       { type: 'separator' }
       { label: 'Cut', selector: 'cut:', accelerator: 'Command+X' }
       { label: 'Copy', selector: 'copy:', accelerator: 'Command+C' }


[master] bgfx

[master] bgfx

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/hell0again/mario.git/'


Add bookmarklet

Add bookmarklet

error: pathspec 'app/assets/javascripts/bookmarklet.coffee' did not match any file(s) known to git

--- app/assets/javascripts/bookmarklet.coffee
+++ app/assets/javascripts/bookmarklet.coffee
@@ -0,0 +1 @@
+location.href = "http://localhost:3000/issues/new?url=" + encodeURIComponent(location.href)


popularpost: remove hardcoded channel name

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/andrewjcasal/koding.git/'


Fix month

Fix month

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/NatLee/plugin-Akashic-records.git/'


Add test directory

Add test directory

error: pathspec 'template/test/test_sample.coffee' did not match any file(s) known to git

--- template/test/test_sample.coffee
+++ template/test/test_sample.coffee
@@ -0,0 +1,4 @@
+describe 'Array', ->
+  describe '#indexOf()', ->
+    it 'should return -1 when not present', ->
+      [1,2,3].indexOf(4).should.equal -1


Properly escaped and removed pedantic condition.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/picoCTF/picoCTF-web.git/'


SocialWorker: use nodejs profiler

SocialWorker: use nodejs profiler

--- workers/social/lib/social/main.coffee
+++ workers/social/lib/social/main.coffee
@@ -7,6 +7,7 @@
 { exec }           = require 'child_process'
 { extend }         = require 'underscore'
 { join: joinPath } = require 'path'
+{ NodejsProfiler } = require 'koding-datadog'

 usertracker = require '../../../usertracker'
 datadog     = require '../../../datadog'
@@ -143,7 +144,10 @@

 do ->
   usertracker.start()
-  datadog.startMonitoringNodejs()
+
+  # start monitoring nodejs metrics (memory, gc, cpu etc...)
+  nodejsProfiler = new NodejsProfiler 'socialWorker'
+  nodejsProfiler.startMonitoring()

   compression = require 'compression'
   bodyParser = require 'body-parser'