Skip to content

ExtendVimModePlusInInitFile

t9md edited this page Oct 5, 2017 · 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 consumeService utility functions(one time).
  2. Define your own operation class and register command.
  3. Configure keymap if necessary

1. Define consumeService utility functions

As preparation, define following function in your init.js or init.coffee

  • init.coffee
consumeService = (packageName, functionName, fn) ->
  consume = (pack) -> fn(pack.mainModule[functionName]())

  if atom.packages.isPackageActive(packageName)
    consume(atom.packages.getActivePackage(packageName))
  else
    disposable = atom.packages.onDidActivatePackage (pack) ->
      if pack.name is packageName
        disposable.dispose()
        consume(pack)
  • init.js
function consumeService(packageName, functionName, fn) {
  const consume = pack => fn(pack.mainModule[functionName]())

  if (atom.packages.isPackageActive(packageName)) {
    consume(atom.packages.getActivePackage(packageName))
  } else {
    const disposable = atom.packages.onDidActivatePackage(pack => {
      if (pack.name === packageName) {
        disposable.dispose()
        consume(pack)
      }
    })
  }
}

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.
  • Comand 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.

You have two option for how to load vmp command.

  • Use init.js and define vmp-commands directly in init.js
  • Use init.coffee and require js file to load vmp-commands.

Directly define in init.js

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

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

require js file from init.cofee

  • init.coffee
consumeService 'vim-mode-plus', 'provideVimModePlus', (service) ->
  commands = require('./load-vmp-commands')(service)
  for name, klass of commands
    klass.commandPrefix = "vim-mode-plus-user"
    klass.registerCommand()
  • load-vmp-commands.js
module.exports = function loadVmpCommands({Base}) {
  class MoveUp extends Base.getClass("Motion") {
    moveCursor(cursor) {
      cursor.moveUp()
    }
  }

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

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"

class InsertSpaces extends Base.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"

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

class MoveFiveLinesDown extends Base.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()
class MoveUpToSameIndent Base.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.

const TransformStringByExternalCommand = Base.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

class DeleteWithBackholeRegister extends Base.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`.
Clone this wiki locally