     else
       @editor.clearSelections()
 
-  # Public: Marks this as complete and saves the motion.
-  #
-  # motion - The motion used to select what to copy.
-  #
-  # Returns nothing.
-  compose: (motion) ->
-    if not motion.select
-      throw new OperatorError("Yank must compose with a motion")
-
-    @motion = motion
-    @complete = true
-
 #
 # It indents everything selected by the following motion.
 #
-class Indent
-  motion: null
-  complete: null
-
-  constructor: (@editor) ->
-    @complete = false
-
-  isComplete: -> @complete
-
+class Indent extends Operator
   # Public: Indents the text selected by the given motion.
   #
   # count - The number of times to execute.
   #
   # Returns nothing.
   execute: (count=1) ->
+    @indent(count)
+
+  # Protected: Indents or outdents the text selected by the given motion.
+  #
+  # count  - The number of times to execute.
+  # direction - Either 'indent' or 'outdent'
+  #
+  # Returns nothing.
+  indent: (count, direction='indent') ->
     row = @editor.getCursorScreenRow()
 
     @motion.select(count)
-    @editor.indentSelectedRows()
+    if direction == 'indent'
+      @editor.indentSelectedRows()
+    else if direction == 'outdent'
+      @editor.outdentSelectedRows()
 
     @editor.setCursorScreenPosition([row, 0])
     @editor.moveCursorToFirstCharacterOfLine()
 
-  # Public: Marks this as complete and saves the motion.
-  #
-  # motion - The motion used to select what to indent.
-  #
-  # Returns nothing.
-  compose: (motion) ->
-    if not motion.select
-      throw new OperatorError("Indent must compose with a motion")
-
-    @motion = motion
-    @complete = true
-
 #
 # It outdents everything selected by the following motion.
 #
-class Outdent
-  motion: null
-  complete: null
-
-  constructor: (@editor) ->
-    @complete = false
-
-  isComplete: -> @complete
-
-  # Public: Outdents the text selected by the given motion.
-  #
-  # count - The number of times to execute.
-  #
-  # Returns nothing.
-  execute: (count=1) ->
-    row = @editor.getCursorScreenRow()
-
-    @motion.select(count)
-    @editor.outdentSelectedRows()
-
-    @editor.setCursorScreenPosition([row, 0])
-    @editor.moveCursorToFirstCharacterOfLine()
-
-  # Public: Marks this as complete and saves the motion.
-  #
-  # motion - The motion used to select what to outdent.
-  #
-  # Returns nothing.
-  compose: (motion) ->
-    if not motion.select
-      throw new OperatorError("Outdent must compose with a motion")
-
-    @motion = motion
-    @complete = true
+class Outdent extends Indent
+  # Public: Indents the text selected by the given motion.
+  #
+  # count - The number of times to execute.
+  #
+  # Returns nothing.
+  execute: (count=1) ->
+    @indent(count, 'outdent')
 
 #
 # It pastes everything contained within the specifed register
 #
-class Put
-  motion: null
-  direction: null
-  register: null
-
-  constructor: (@editor, @vimState, {@location}={}) ->
-    @direction ?= 'after'
-    @register ?= '"'
-
-  isComplete: -> true
+class Put extends Operator
+  direction: 'after'
+  register: '"'
+  location: null
+
+  constructor: (@editor, @vimState, {@location, @selectOptions}={}) -> @complete = true
 
   # Public: Pastes the text in the given register.
   #

      if @motion.isLinewise?()
        @editor.setCursorScreenPosition([cursor.getScreenRow(), 0])

  # Public: Marks this as complete and saves the motion.
  #
  # motion - The motion used to select what to delete.
  #
  # Returns nothing.
  compose: (motion) ->
    if not motion.select
      throw new OperatorError("Delete must compose with a motion")

    @motion = motion
    @complete = true

#
# It changes everything selected by the following motion.
#
class Change
  motion: null
  complete: null
  vimState: null

  constructor: (@editor, @vimState) ->
    @complete = false

  isComplete: -> @complete

  # Public: Changes the text selected by the given motion.
  #
  # count - The number of times to execute.
  #
  # Returns nothing.
  execute: (count=1) ->
    operator = new Delete(@editor, @vimState,
      motion: @motion, allowEOL: true, selectOptions: {excludeWhitespace: true})
    operator.execute(count)

    @vimState.activateInsertMode()

  # Public: Marks this as complete and saves the motion.
  #
  # motion - The motion used to select what to change.
  #
  # Returns nothing.
  compose: (motion) ->
    if not motion.select
      throw new OperatorError("Change must compose with a motion")

    @motion = motion
    @complete = true

#
# It copies everything selected by the following motion.
#
class Yank
  motion: null
  complete: null
  register: null
  vimState: null

  constructor: (@editor, @vimState) ->
    @complete = false
    @register ?= '"'

  isComplete: -> @complete

  # Public: Copies the text selected by the given motion.
  #

  execute: (count=1) ->
    {text, type} = @vimState.getRegister(@register) || {}
    return unless text

    @undoTransaction =>
      _.times count, =>
        if type == 'linewise' and @location == 'after'
          @editor.moveCursorDown()
