ExtendVimModePlusInInitFile

t9md edited this page Feb 26, 2018 · 17 revisions

Overview

You can create original Motion/Operator/TextObject by extending existing operation class defined in vmp-core.

:rotating_light: :rotating_light: :rotating_light: :rotating_light: :rotating_light: :rotating_light:
From vmp v1.9.0, you must use JavaScript(ES6 class) to extend vmp, CoffeeScript-v1 is no longer supported to extend vmp.
Why? vmp's operations are implemented as ES6 class which is incompatible with CoffeeScript's class.
:rotating_light: :rotating_light: :rotating_light: :rotating_light: :rotating_light: :rotating_light:

Here is the steps.

  1. Define your own operation in your init.js
  2. Configure keymap if necessary

1. Define consumeService utility functions

See this template

function consumeVimModePlusService(callback) {
  const consume = (pack) => callback(pack.mainModule.provideVimModePlus())

  const pack = atom.packages.getActivePackage('vim-mode-plus')
  if (pack) {
    consume(pack)
  } else {
    const disposable = atom.packages.onDidActivatePackage(pack => {
      if (pack.name === 'vim-mode-plus') {
        disposable.dispose()
        consume(pack)
      }
    })
  }
}

consumeVimModePlusService(service => {
  // Define your own operation FROM HERE
  class MyMotion extends service.getClass("Motion") {
    execute() {
      console.log("my motion!");
    }
  }
  MyMotion.commandPrefix = "vim-mode-plus-user"

  // `registerCommand()` here register "vim-mode-plus-user:my-motion" command
  // Which log "my motion" to console when invoked.
  MyMotion.registerCommand()

  // Define your own operation TO HERE
})

2. Define your own operation class and register command

As simple example we will define our own version of move-up/move-down motion.

  • All vmp command class must inherit Base class directly or indirectly.
  • You can register command by calling Base.registerCommand() static fucntion.
  • Command name is derived from class name by klass.commandPrefix + ':' + _.dasherize(klass.name).
    • When klass.commandPrefix is vim-mode-plus-user
      • MoveUp.registerCommand() register vim-mode-plus-user:move-up
      • MoveDown.registerCommand() register vim-mode-plus-user:move-down

You must define vmp operation in ES6 class, you cannot use CoffeeScript, since vmp-core class is defined in ES6 class which is incompatible with CoffeScript-v1 used in Atom.

  • init.js
consumeVimModePlusService(service => {
  class MoveUp extends service.getClass("Motion") {
    moveCursor(cursor) {
      cursor.moveUp()
    }
  }
  MoveUp.commandPrefix = "vim-mode-plus-user"
  MoveUp.registerCommand()

  class MoveDown extends MoveUp {
    moveCursor(cursor) {
      cursor.moveDown()
    }
  }
  MoveDown.registerCommand()
})

3. Configure keymap if necessary

  • keymap.cson
'atom-text-editor.vim-mode-plus:not(.insert-mode)':
  'j': 'vim-mode-plus-user:move-down'
  'k': 'vim-mode-plus-user:move-up'

Examples

[Basic] InsertSpaces operator which insert specified count of spaces

  • init.js
"use babel"

consumeVimModePlusService(service => {
  class InsertSpaces extends service.getClass('Operator') {
    static commandPrefix = 'vim-mode-plus-user'
    requireTarget = false

    execute() {
      this.editor.insertText(" ".repeat(this.getCount()))
    }
  }
  InsertSpaces.registerCommand()
})

// keymap.cson
//  'atom-text-editor.vim-mode-plus.normal-mode':
//    'g space': 'vim-mode-plus-user:insert-spaces'
//
// Description
//   keystroke '3 g space' insert three spaces at cursor position
//   multi-selection support, can repeat by `.`

[Basic] 5 lines moveUp/moveDown motion

  • init.js
"use babel"

consumeVimModePlusService(service => {
  class MoveFiveLinesUp extends service.getClass("MoveUp") {
    static commandPrefix = "vim-mode-plus-user"
    defaultCount = 5
  }
  MoveFiveLinesUp.registerCommand()

  class MoveFiveLinesDown extends service.getClass("MoveDown") {
    static commandPrefix = "vim-mode-plus-user"
    defaultCount = 5
  }
  MoveFiveLinesDown.registerCommand()
})
  • keymap.cson
'atom-text-editor.vim-mode-plus:not(.insert-mode)':
  'J': 'vim-mode-plus-user:move-five-lines-down'
  'K': 'vim-mode-plus-user:move-five-lines-up'

[Advanced] move-up(down)-to-same-indent

MoveUp/MoveDown to row which have same level of indentation.

  • init.js
"use babel"

// borrow MoveUpToEdge.prototype.getScanRows()
consumeVimModePlusService(service => {
  class MoveUpToSameIndent service.getClass("MoveUpToEdge") {
    static commandPrefix = "vim-mode-plus-user"

    moveCursor(cursor) {
      const cursorRow = cursor.getBufferRow()
      const baseIndentLevel = this.utils.getIndentLevelForBufferRow(this.editor, cursorRow)
      const column = cursor.getBufferColumn()
      this.countTimes(() => {
        const newRow = this.getScanRows(cursor).find(
          row => this.utils.getIndentLevelForBufferRow(this.editor, row) === baseIndentLevel
        )
        if (newRow != null) cursor.setBufferPosition([newRow, column])
      })
    }
  }
  MoveUpToSameIndent.registerCommand()

  class MoveDownToSameIndent extends MoveUpToSameIndent {
    direction = "down"
  }
  MoveDownToSameIndent.registerCommand()
})
  • keymap.cson
'atom-text-editor.vim-mode-plus:not(.insert-mode)':
  '(': 'vim-mode-plus-user:move-up-to-same-indent'
  ')': 'vim-mode-plus-user:move-down-to-same-indent'

[Advanced] TransformString by external command

By extending TransformStringByExternalCommand, user can add string transformer via external command.

consumeVimModePlusService(service => {
  const TransformStringByExternalCommand = service.getClass("TransformStringByExternalCommand")
  class CoffeeCompile extends TransformStringByExternalCommand {
    command = "coffee"
    args = ["-csb", "--no-header"]
  }

  class CoffeeEval extends TransformStringByExternalCommand {
    command = "coffee"
    args = ["-se"]
    getStdin(selection) {
      return `console.log ${selection.getText()}`
    }
  }

  class CoffeeInspect extends TransformStringByExternalCommand {
    command = "coffee"
    args = ["-se"]
    getStdin(selection) {
      return `{inspect} = require 'util';console.log ${selection.getText()}`
    }
  }

  for (const klass of [CoffeeCompile, CoffeeEval, CoffeeInspect]) {
    klass.commandPrefix = "vim-mode-plus-user"
    klass.registerCommand()
  }
})

[Advanced] DeleteWithBackholeRegister

consumeVimModePlusService(service => {
  class DeleteWithBackholeRegister extends service.getClass("Delete") {
    execute() {
      this.vimState.register.name = "_"
      super.execute()
    }
  }
  DeleteWithBackholeRegister.commandPrefix = "vim-mode-plus-user"
  DeleteWithBackholeRegister.registerCommand()
})
  • keymap.cson
'atom-text-editor.vim-mode-plus:not(.insert-mode)':
  '\\ d': 'vim-mode-plus-user:delete-with-backhole-register'

'atom-text-editor.vim-mode-plus.delete-with-backhole-register-pending':
  'd': 'vim-mode-plus-user:delete-with-backhole-register' # to support `\ d d`.

[Advanced] InsertCharacter

"use babel" // This must be at top of file

consumeVimModePlusService(service => {
  class InsertCharacter extends service.getClass("Operator") {
    static commandPrefix = "vim-mode-plus-user"
    target = "Empty"
    readInputAfterSelect = true

    mutateSelection(selection) {
      const point = selection.getHeadBufferPosition()
      this.editor.setTextInBufferRange([point, point], this.input.repeat(this.getCount()))
    }
  }
  InsertCharacter.registerCommand()
})
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.