]> git.r.bdr.sh - rbdr/dotfiles/blob - atom/packages/vim-mode/lib/operators/input.coffee
Update atoms
[rbdr/dotfiles] / atom / packages / vim-mode / lib / operators / input.coffee
1 Motions = require '../motions/index'
2 {Operator, Delete} = require './general-operators'
3 _ = require 'underscore-plus'
4 settings = 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.
10 class 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
22 @editor.insertText(@typedText, normalizeLineEndings: true, autoIndent: true)
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
32 class 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
52 class InsertAfter extends Insert
53 execute: ->
54 @editor.moveRight() unless @editor.getLastCursor().isAtEndOfLine()
55 super
56
57 class InsertAfterEndOfLine extends Insert
58 execute: ->
59 @editor.moveToEndOfLine()
60 super
61
62 class InsertAtBeginningOfLine extends Insert
63 execute: ->
64 @editor.moveToBeginningOfLine()
65 @editor.moveToFirstCharacterOfLine()
66 super
67
68 class InsertAboveWithNewline extends Insert
69 execute: ->
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
83 class InsertBelowWithNewline extends Insert
84 execute: ->
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 #
101 class Change extends Insert
102 standalone: false
103 register: null
104
105 constructor: (@editor, @vimState) ->
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) ->
114 if _.contains(@motion.select(count, excludeWhitespace: true), true)
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
119 @setTextRegister(@register, @editor.getSelectedText())
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()
127 else
128 for selection in @editor.getSelections()
129 selection.deleteSelectedText()
130
131 return super if @typingCompleted
132
133 @vimState.activateInsertMode()
134 @typingCompleted = true
135 else
136 @vimState.activateNormalMode()
137
138 # Takes a transaction and turns it into a string of what was typed.
139 # This class is an implementation detail of Insert
140 class 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
204 module.exports = {
205 Insert,
206 InsertAfter,
207 InsertAfterEndOfLine,
208 InsertAtBeginningOfLine,
209 InsertAboveWithNewline,
210 InsertBelowWithNewline,
211 ReplaceMode,
212 Change
213 }