aboutsummaryrefslogtreecommitdiff
path: root/atom/packages/vim-mode-visual-block/lib/main.coffee
blob: dc5a5c21218401e89003f5144ab11415e5427f78 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
_ = require 'underscore-plus'
{CompositeDisposable} = require 'atom'

Config =
  debug:
    type: 'boolean'
    default: false

module.exports =
  disposables: null
  active:      false
  prefix:      'vim-mode-visual-block'

  activate: (state) ->
    @disposables = new CompositeDisposable
    blockwiseCommands = {}
    commands = 'jkoDCIA'.split('')
    commands.push 'escape', 'ctrl-v'
    for command in commands
      do (command) =>
        name = "#{@prefix}:#{command}"
        blockwiseCommands[name] = (event) => @blockOperation(event, command)

    # blockwiseCommands["#{@prefix}:toggle-debug"] = => @toggleDebug()
    @disposables.add atom.commands.add('atom-text-editor', blockwiseCommands)
    @reset()

  deactivate: ->
    @disposables.dispose()

  consumeVimMode: (@vimModeService) ->

  reset: ->
    @startRow = null

  getEditor: ->
    atom.workspace.getActiveTextEditor()

  isVisualBlockMode: (vimState) ->
    (vimState.mode is 'visual') and (vimState.submode is 'blockwise')

  getVimEditorState: (editor) ->
    @vimModeService.getEditorState editor

  adjustSelections: (editor, options) ->
    for selection in editor.getSelections()
      range = selection.getBufferRange()
      selection.setBufferRange range, options

  blockOperation: (event, command) ->
    editor   = @getEditor()
    vimState = @getVimEditorState editor

    unless @isVisualBlockMode vimState
      event.abortKeyBinding()
      @reset()
      return

    # May be non-continuous execution.
    if editor.getCursors().length is 1
      @reset()

    currentRow  = editor.getLastCursor()?.getBufferRow()
    @startRow  ?= currentRow

    switch command
      when 'o'
        @startRow = currentRow
      when 'D', 'C'
        vimState.activateNormalMode()
        event.abortKeyBinding()
      when 'escape', 'ctrl-v'
        vimState.activateNormalMode()
        editor.clearSelections()
      when 'j', 'k'
        cursors      = editor.getCursorsOrderedByBufferPosition()
        cursorTop    = _.first cursors
        cursorBottom = _.last cursors

        if (command is 'j' and cursorTop.getBufferRow() >= @startRow) or
            (command is 'k' and cursorBottom.getBufferRow() <= @startRow)
          lastSelection = editor.getLastSelection()

          if command is 'j'
            editor.addSelectionBelow()
          else
            editor.addSelectionAbove()

          # [FIXME]
          # When addSelectionAbove(), addSelectionBelow() doesn't respect
          # reversed stated, need improved.
          #
          # and one more..
          #
          # When selection is NOT empty and add selection by addSelectionAbove()
          # and then move right, selection range got wrong, maybe this is bug..
          @adjustSelections editor, reversed: lastSelection.isReversed()
        else
          # [FIXME]
          # Guard to not destroying last cursor
          # This guard is no longer needed
          # Remove unnecessary code after re-think.
          if (editor.getCursors().length < 2)
            @reset()
            return

          if command is 'j'
            cursorTop.destroy()
          else
            cursorBottom.destroy()
      when 'I', 'A'
        cursorsAdjusted = []

        adjustCursor = (selection) ->
          {start, end} = selection.getBufferRange()
          pointEndOfLine = editor.bufferRangeForBufferRow(start.row).end
          pointTarget    = {'I': start, 'A': end}[command]
          {cursor}       = selection

          if pointTarget.isGreaterThanOrEqual(pointEndOfLine)
            pointTarget = pointEndOfLine
            cursorsAdjusted.push cursor
          cursor.setBufferPosition(pointTarget)

        adjustCursor(selection) for selection in editor.getSelections()
        vimState.activateNormalMode()
        vimState.activateInsertMode()

        if command is 'A' and  cursorsAdjusted.length
          cursor.moveRight() for cursor in cursorsAdjusted

    unless @isVisualBlockMode vimState
      @reset()

  toggleDebug: ->
    oldState = atom.config.get("#{@prefix}.debug")
    atom.config.set("#{@prefix}.debug", not oldState)
    state = atom.config.get("#{@prefix}.debug") and "enabled" or "disabled"
    console.log "#{@prefix}: debug #{state}"

  debug: (msg) ->
    return unless atom.config.get("#{@prefix}.debug")
    console.log msg

  # dump: ->
  #   @debug "@startRow = #{@startRow}"