Skip to content

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.

🚨 🚨 🚨 🚨 🚨 🚨
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.
🚨 🚨 🚨 🚨 🚨 🚨

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()
})