1 _ = require 'underscore-plus'
2 {CompositeDisposable} = require 'atom'
12 prefix: 'vim-mode-visual-block'
15 @disposables = new CompositeDisposable
16 blockwiseCommands = {}
17 # [TODO] remove 'h', 'l' after some period.
18 commands = 'jkhloDCIA'.split('')
19 commands.push 'escape', 'ctrl-v'
20 for command in commands
22 name = "#{@prefix}:#{command}"
23 blockwiseCommands[name] = (event) => @blockOperation(event, command)
25 blockwiseCommands["#{@prefix}:toggle-debug"] = => @toggleDebug()
26 @disposables.add atom.commands.add('atom-text-editor', blockwiseCommands)
30 @disposables.dispose()
32 consumeVimMode: (@vimModeService) ->
38 atom.workspace.getActiveTextEditor()
40 isVisualBlockMode: (vimState) ->
41 (vimState.mode is 'visual') and (vimState.submode is 'blockwise')
43 getVimEditorState: (editor) ->
44 @vimModeService.getEditorState editor
46 adjustSelections: (editor, options) ->
47 for selection in editor.getSelections()
48 range = selection.getBufferRange()
49 selection.setBufferRange range, options
51 blockOperation: (event, command) ->
53 vimState = @getVimEditorState editor
55 unless @isVisualBlockMode vimState
56 event.abortKeyBinding()
60 # May be non-continuous execution.
61 if editor.getCursors().length is 1
64 currentRow = editor.getLastCursor()?.getBufferRow()
65 @startRow ?= currentRow
66 # @debug "@startRow = #{@startRow}"
70 @startRow = currentRow
72 vimState.activateCommandMode()
73 event.abortKeyBinding()
74 when 'escape', 'ctrl-v'
75 vimState.activateCommandMode()
76 editor.clearSelections()
78 cursorPositions = editor.getCursorsOrderedByBufferPosition()
79 cursorTop = _.first cursorPositions
80 cursorBottom = _.last cursorPositions
82 if (command is 'j' and cursorTop.getBufferRow() >= @startRow) or
83 (command is 'k' and cursorBottom.getBufferRow() <= @startRow)
84 lastSelection = editor.getLastSelection()
87 editor.addSelectionBelow()
89 editor.addSelectionAbove()
92 # When addSelectionAbove(), addSelectionBelow() doesn't respect
93 # reversed stated, need improved.
97 # When selection is NOT empty and add selection by addSelectionAbove()
98 # and then move right, selection range got wrong, maybe this is bug..
99 @adjustSelections editor, reversed: lastSelection.isReversed()
102 # Guard to not destroying last cursor
103 # This guard is no longer needed
104 # Remove unnecessary code after re-think.
105 if (editor.getCursors().length < 2)
112 cursorBottom.destroy()
116 adjustCursor = (selection) ->
117 {start, end} = selection.getBufferRange()
118 pointEndOfLine = editor.bufferRangeForBufferRow(start.row).end
119 pointTarget = {'I': start, 'A': end}[command]
122 if pointTarget.isGreaterThanOrEqual(pointEndOfLine)
123 pointTarget = pointEndOfLine
124 cursorsAdjusted.push cursor
125 cursor.setBufferPosition(pointTarget)
127 adjustCursor(selection) for selection in editor.getSelections()
128 vimState.activateCommandMode()
129 vimState.activateInsertMode()
131 if command is 'A' and cursorsAdjusted.length
132 cursor.moveRight() for cursor in cursorsAdjusted
135 event.abortKeyBinding()
138 * From version 0.2.5, `#{@prefix}` provide default keymap.
139 * And `h`, `l` command become obsolete.
140 * Remove all explicit keymap from `keymap.cson`.
142 atom.notifications.addWarning content, dismissable: true
144 unless @isVisualBlockMode vimState
148 oldState = atom.config.get("#{@prefix}.debug")
149 atom.config.set("#{@prefix}.debug", not oldState)
150 state = atom.config.get("#{@prefix}.debug") and "enabled" or "disabled"
151 console.log "#{@prefix}: debug #{state}"
154 return unless atom.config.get("#{@prefix}.debug")
158 # @debug "@startRow = #{@startRow}"