]> git.r.bdr.sh - rbdr/dotfiles/blame - atom/packages/vim-mode/lib/operators/input.coffee
Update atoms
[rbdr/dotfiles] / atom / packages / vim-mode / lib / operators / input.coffee
CommitLineData
455f099b 1Motions = require '../motions/index'
24c7594d
BB
2{Operator, Delete} = require './general-operators'
3_ = require 'underscore-plus'
4settings = require '../settings'
5
6# The operation for text entered in input mode. Broadly speaking, input
7# operators manage an undo transaction and set a @typingCompleted variable when
8# it's done. When the input operation is completed, the typingCompleted variable
9# tells the operation to repeat itself instead of enter insert mode.
10class Insert extends Operator
11 standalone: true
12
13 isComplete: -> @standalone or super
14
15 confirmChanges: (changes) ->
16 bundler = new TransactionBundler(changes, @editor)
17 @typedText = bundler.buildInsertText()
18
19 execute: ->
20 if @typingCompleted
21 return unless @typedText? and @typedText.length > 0
455f099b 22 @editor.insertText(@typedText, normalizeLineEndings: true, autoIndent: true)
24c7594d
BB
23 for cursor in @editor.getCursors()
24 cursor.moveLeft() unless cursor.isAtBeginningOfLine()
25 else
26 @vimState.activateInsertMode()
27 @typingCompleted = true
28 return
29
30 inputOperator: -> true
31
455f099b
BB
32class ReplaceMode extends Insert
33
34 execute: ->
35 if @typingCompleted
36 return unless @typedText? and @typedText.length > 0
37 @editor.transact =>
38 @editor.insertText(@typedText, normalizeLineEndings: true)
39 toDelete = @typedText.length - @countChars('\n', @typedText)
40 for selection in @editor.getSelections()
41 count = toDelete
42 selection.delete() while count-- and not selection.cursor.isAtEndOfLine()
43 for cursor in @editor.getCursors()
44 cursor.moveLeft() unless cursor.isAtBeginningOfLine()
45 else
46 @vimState.activateReplaceMode()
47 @typingCompleted = true
48
49 countChars: (char, string) ->
50 string.split(char).length - 1
51
24c7594d
BB
52class InsertAfter extends Insert
53 execute: ->
54 @editor.moveRight() unless @editor.getLastCursor().isAtEndOfLine()
55 super
56
57class InsertAfterEndOfLine extends Insert
58 execute: ->
59 @editor.moveToEndOfLine()
60 super
61
62class InsertAtBeginningOfLine extends Insert
63 execute: ->
64 @editor.moveToBeginningOfLine()
65 @editor.moveToFirstCharacterOfLine()
66 super
67
68class InsertAboveWithNewline extends Insert
455f099b 69 execute: ->
24c7594d
BB
70 @vimState.setInsertionCheckpoint() unless @typingCompleted
71 @editor.insertNewlineAbove()
72 @editor.getLastCursor().skipLeadingWhitespace()
73
74 if @typingCompleted
75 # We'll have captured the inserted newline, but we want to do that
76 # over again by hand, or differing indentations will be wrong.
77 @typedText = @typedText.trimLeft()
78 return super
79
80 @vimState.activateInsertMode()
81 @typingCompleted = true
82
83class InsertBelowWithNewline extends Insert
455f099b 84 execute: ->
24c7594d
BB
85 @vimState.setInsertionCheckpoint() unless @typingCompleted
86 @editor.insertNewlineBelow()
87 @editor.getLastCursor().skipLeadingWhitespace()
88
89 if @typingCompleted
90 # We'll have captured the inserted newline, but we want to do that
91 # over again by hand, or differing indentations will be wrong.
92 @typedText = @typedText.trimLeft()
93 return super
94
95 @vimState.activateInsertMode()
96 @typingCompleted = true
97
98#
99# Delete the following motion and enter insert mode to replace it.
100#
101class Change extends Insert
102 standalone: false
103 register: null
104
455f099b 105 constructor: (@editor, @vimState) ->
24c7594d
BB
106 @register = settings.defaultRegister()
107
108 # Public: Changes the text selected by the given motion.
109 #
110 # count - The number of times to execute.
111 #
112 # Returns nothing.
113 execute: (count) ->
24c7594d 114 if _.contains(@motion.select(count, excludeWhitespace: true), true)
455f099b
BB
115 # If we've typed, we're being repeated. If we're being repeated,
116 # undo transactions are already handled.
117 @vimState.setInsertionCheckpoint() unless @typingCompleted
118
24c7594d 119 @setTextRegister(@register, @editor.getSelectedText())
455f099b
BB
120 if @motion.isLinewise?() and not @typingCompleted
121 for selection in @editor.getSelections()
122 if selection.getBufferRange().end.row is 0
123 selection.deleteSelectedText()
124 else
125 selection.insertText("\n", autoIndent: true)
126 selection.cursor.moveLeft()
24c7594d
BB
127 else
128 for selection in @editor.getSelections()
129 selection.deleteSelectedText()
130
455f099b 131 return super if @typingCompleted
24c7594d 132
455f099b
BB
133 @vimState.activateInsertMode()
134 @typingCompleted = true
135 else
136 @vimState.activateNormalMode()
24c7594d
BB
137
138# Takes a transaction and turns it into a string of what was typed.
139# This class is an implementation detail of Insert
140class TransactionBundler
141 constructor: (@changes, @editor) ->
142 @start = null
143 @end = null
144
145 buildInsertText: ->
146 @addChange(change) for change in @changes
147 if @start?
148 @editor.getTextInBufferRange [@start, @end]
149 else
150 ""
151
152 addChange: (change) ->
153 return unless change.newRange?
154 if @isRemovingFromPrevious(change)
155 @subtractRange change.oldRange
156 if @isAddingWithinPrevious(change)
157 @addRange change.newRange
158
159 isAddingWithinPrevious: (change) ->
160 return false unless @isAdding(change)
161
162 return true if @start is null
163
164 @start.isLessThanOrEqual(change.newRange.start) and
165 @end.isGreaterThanOrEqual(change.newRange.start)
166
167 isRemovingFromPrevious: (change) ->
168 return false unless @isRemoving(change) and @start?
169
170 @start.isLessThanOrEqual(change.oldRange.start) and
171 @end.isGreaterThanOrEqual(change.oldRange.end)
172
173 isAdding: (change) ->
174 change.newText.length > 0
175
176 isRemoving: (change) ->
177 change.oldText.length > 0
178
179 addRange: (range) ->
180 if @start is null
181 {@start, @end} = range
182 return
183
184 rows = range.end.row - range.start.row
185
186 if (range.start.row is @end.row)
187 cols = range.end.column - range.start.column
188 else
189 cols = 0
190
191 @end = @end.translate [rows, cols]
192
193 subtractRange: (range) ->
194 rows = range.end.row - range.start.row
195
196 if (range.end.row is @end.row)
197 cols = range.end.column - range.start.column
198 else
199 cols = 0
200
201 @end = @end.translate [-rows, -cols]
202
203
204module.exports = {
205 Insert,
206 InsertAfter,
207 InsertAfterEndOfLine,
208 InsertAtBeginningOfLine,
209 InsertAboveWithNewline,
210 InsertBelowWithNewline,
455f099b
BB
211 ReplaceMode,
212 Change
24c7594d 213}