vimamsa 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/custom_example.rb +12 -0
- data/lib/vimamsa/ack.rb +3 -4
- data/lib/vimamsa/actions.rb +1 -2
- data/lib/vimamsa/audio.rb +25 -1
- data/lib/vimamsa/buffer.rb +116 -591
- data/lib/vimamsa/buffer_changetext.rb +272 -0
- data/lib/vimamsa/buffer_cursor.rb +303 -0
- data/lib/vimamsa/buffer_list.rb +137 -133
- data/lib/vimamsa/buffer_manager.rb +15 -15
- data/lib/vimamsa/clipboard.rb +36 -0
- data/lib/vimamsa/conf.rb +136 -5
- data/lib/vimamsa/constants.rb +0 -10
- data/lib/vimamsa/debug.rb +9 -8
- data/lib/vimamsa/editor.rb +57 -84
- data/lib/vimamsa/encrypt.rb +6 -11
- data/lib/vimamsa/file_history.rb +0 -8
- data/lib/vimamsa/file_manager.rb +142 -10
- data/lib/vimamsa/gui.rb +106 -85
- data/lib/vimamsa/gui_dialog.rb +113 -0
- data/lib/vimamsa/gui_menu.rb +5 -1
- data/lib/vimamsa/gui_sourceview.rb +46 -29
- data/lib/vimamsa/gui_sourceview_autocomplete.rb +141 -0
- data/lib/vimamsa/gui_text.rb +49 -0
- data/lib/vimamsa/hyper_plain_text.rb +19 -5
- data/lib/vimamsa/key_actions.rb +41 -202
- data/lib/vimamsa/key_binding_tree.rb +129 -41
- data/lib/vimamsa/key_bindings_vimlike.rb +58 -48
- data/lib/vimamsa/langservp.rb +23 -3
- data/lib/vimamsa/macro.rb +35 -25
- data/lib/vimamsa/main.rb +7 -10
- data/lib/vimamsa/rbvma.rb +13 -11
- data/lib/vimamsa/search.rb +1 -1
- data/lib/vimamsa/search_replace.rb +106 -160
- data/lib/vimamsa/terminal.rb +34 -0
- data/lib/vimamsa/tests.rb +122 -0
- data/lib/vimamsa/util.rb +43 -4
- data/lib/vimamsa/version.rb +1 -1
- data/vimamsa.gemspec +5 -2
- metadata +59 -9
- /data/lib/vimamsa/{form_generator.rb → gui_form_generator.rb} +0 -0
@@ -0,0 +1,272 @@
|
|
1
|
+
# Operations that change the content of the buffer
|
2
|
+
# e.g. insert, delete
|
3
|
+
|
4
|
+
class Buffer < String
|
5
|
+
|
6
|
+
#TODO: change to apply=true as default
|
7
|
+
def add_delta(delta, apply = false, auto_update_cpos = false)
|
8
|
+
return if !is_delta_ok(delta)
|
9
|
+
if delta[1] == DELETE
|
10
|
+
return if delta[0] >= self.size
|
11
|
+
# If go over length of buffer
|
12
|
+
if delta[0] + delta[2] >= self.size
|
13
|
+
delta[2] = self.size - delta[0]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
@edit_version += 1
|
18
|
+
@redo_stack = []
|
19
|
+
if apply
|
20
|
+
delta = run_delta(delta, auto_update_cpos)
|
21
|
+
else
|
22
|
+
@deltas << delta
|
23
|
+
end
|
24
|
+
@edit_history << delta
|
25
|
+
if self[-1] != "\n"
|
26
|
+
add_delta([self.size, INSERT, 1, "\n"], true)
|
27
|
+
end
|
28
|
+
reset_larger_cpos #TODO: correct here?
|
29
|
+
end
|
30
|
+
|
31
|
+
# TODO: rename ot auto-format. separate module?
|
32
|
+
# Indents whole buffer using external program
|
33
|
+
def indent()
|
34
|
+
file = Tempfile.new("out")
|
35
|
+
infile = Tempfile.new("in")
|
36
|
+
file.write(self.to_s)
|
37
|
+
file.flush
|
38
|
+
bufc = "FOO"
|
39
|
+
|
40
|
+
tmppos = @pos
|
41
|
+
|
42
|
+
message("Auto format #{@fname}")
|
43
|
+
|
44
|
+
ftype = get_file_type()
|
45
|
+
if ["chdr", "c", "cpp", "cpphdr"].include?(ftype)
|
46
|
+
|
47
|
+
#C/C++/Java/JavaScript/Objective-C/Protobuf code
|
48
|
+
system("clang-format -style='{BasedOnStyle: LLVM, ColumnLimit: 100, SortIncludes: false}' #{file.path} > #{infile.path}")
|
49
|
+
bufc = IO.read(infile.path)
|
50
|
+
elsif ftype == "Javascript"
|
51
|
+
cmd = "clang-format #{file.path} > #{infile.path}'"
|
52
|
+
debug cmd
|
53
|
+
system(cmd)
|
54
|
+
bufc = IO.read(infile.path)
|
55
|
+
elsif ftype == "ruby"
|
56
|
+
cmd = "rufo #{file.path}"
|
57
|
+
debug cmd
|
58
|
+
system(cmd)
|
59
|
+
bufc = IO.read(file.path)
|
60
|
+
else
|
61
|
+
message("No auto-format handler for file of type: #{ftype}")
|
62
|
+
return
|
63
|
+
end
|
64
|
+
self.update_content(bufc)
|
65
|
+
center_on_current_line #TODO: needed?
|
66
|
+
file.close; file.unlink
|
67
|
+
infile.close; infile.unlink
|
68
|
+
end
|
69
|
+
|
70
|
+
# Create a new line after current line and insert text on that line
|
71
|
+
def put_to_new_next_line(txt)
|
72
|
+
l = current_line_range()
|
73
|
+
insert_txt_at(txt, l.end + 1)
|
74
|
+
set_pos(l.end + 1)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Start asynchronous read of system clipboard
|
78
|
+
def paste_start(at, register)
|
79
|
+
@clipboard_paste_running = true
|
80
|
+
clipboard = vma.gui.window.display.clipboard
|
81
|
+
clipboard.read_text_async do |_clipboard, result|
|
82
|
+
begin
|
83
|
+
text = clipboard.read_text_finish(result)
|
84
|
+
rescue Gio::IOError::NotSupported
|
85
|
+
# Happens when pasting from KeePassX and clipboard cleared
|
86
|
+
debug Gio::IOError::NotSupported
|
87
|
+
else
|
88
|
+
paste_finish(text, at, register)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def paste_finish(text, at, register)
|
94
|
+
debug "PASTE: #{text}"
|
95
|
+
|
96
|
+
# If we did not put this text to clipboard
|
97
|
+
if text != vma.clipboard[-1]
|
98
|
+
@paste_lines = false
|
99
|
+
end
|
100
|
+
|
101
|
+
text = sanitize_input(text)
|
102
|
+
|
103
|
+
vma.clipboard << text
|
104
|
+
|
105
|
+
return if text == ""
|
106
|
+
|
107
|
+
if @paste_lines
|
108
|
+
debug "PASTE LINES"
|
109
|
+
put_to_new_next_line(text)
|
110
|
+
else
|
111
|
+
debug "PASTE !LINES"
|
112
|
+
if at_end_of_buffer? or at_end_of_line? or at == BEFORE
|
113
|
+
pos = @pos
|
114
|
+
else
|
115
|
+
pos = @pos + 1
|
116
|
+
end
|
117
|
+
insert_txt_at(text, pos)
|
118
|
+
set_pos(pos + text.size)
|
119
|
+
end
|
120
|
+
set_pos(@pos)
|
121
|
+
@clipboard_paste_running = false
|
122
|
+
end
|
123
|
+
|
124
|
+
def paste(at = AFTER, register = nil)
|
125
|
+
# Macro's don't work with asynchronous call using GTK
|
126
|
+
# TODO: implement as synchronous?
|
127
|
+
# Use internal clipboard
|
128
|
+
if vma.macro.running_macro
|
129
|
+
text = vma.clipboard.get()
|
130
|
+
paste_finish(text, at, register)
|
131
|
+
else
|
132
|
+
# Get clipboard using GUI
|
133
|
+
paste_start(at, register)
|
134
|
+
end
|
135
|
+
return true
|
136
|
+
end
|
137
|
+
|
138
|
+
def complete_current_word(rep)
|
139
|
+
debug "complete_current_word", 2
|
140
|
+
p = @pos - 1
|
141
|
+
return if !is_legal_pos(p)
|
142
|
+
(word, range) = get_word_in_pos(p, boundary: :word)
|
143
|
+
debug [word, range].to_s, 2
|
144
|
+
endpos = range.begin+rep.size
|
145
|
+
replace_range(range, rep)
|
146
|
+
set_pos(endpos)
|
147
|
+
end
|
148
|
+
|
149
|
+
def replace_range(range, text)
|
150
|
+
delete_range(range.first, range.last)
|
151
|
+
insert_txt_at(text, range.begin)
|
152
|
+
end
|
153
|
+
|
154
|
+
def delete2(range_id, mark = nil)
|
155
|
+
@paste_lines = false
|
156
|
+
range = get_range(range_id, mark: mark)
|
157
|
+
return false if range == nil
|
158
|
+
debug "RANGE"
|
159
|
+
debug range.inspect
|
160
|
+
debug range.inspect
|
161
|
+
debug "------"
|
162
|
+
delete_range(range.first, range.last)
|
163
|
+
pos = [range.first, @pos].min
|
164
|
+
set_pos(pos)
|
165
|
+
return true
|
166
|
+
end
|
167
|
+
|
168
|
+
def delete(op, x = nil)
|
169
|
+
@paste_lines = false
|
170
|
+
# Delete selection
|
171
|
+
if op == SELECTION && visual_mode?
|
172
|
+
(startpos, endpos) = get_visual_mode_range2
|
173
|
+
delete_range(startpos, endpos, x)
|
174
|
+
@pos = [@pos, @selection_start].min
|
175
|
+
end_visual_mode
|
176
|
+
#return
|
177
|
+
|
178
|
+
# Delete current char
|
179
|
+
elsif op == CURRENT_CHAR_FORWARD
|
180
|
+
return if @pos >= self.size - 1 # May not delete last '\n'
|
181
|
+
add_delta([@pos, DELETE, 1], true)
|
182
|
+
|
183
|
+
# Delete current char and then move backward
|
184
|
+
elsif op == CURRENT_CHAR_BACKWARD
|
185
|
+
add_delta([@pos, DELETE, 1], true)
|
186
|
+
@pos -= 1
|
187
|
+
|
188
|
+
# Delete the char before current char and move backward
|
189
|
+
elsif op == BACKWARD_CHAR and @pos > 0
|
190
|
+
add_delta([@pos - 1, DELETE, 1], true)
|
191
|
+
@pos -= 1
|
192
|
+
elsif op == FORWARD_CHAR #TODO: ok?
|
193
|
+
add_delta([@pos + 1, DELETE, 1], true)
|
194
|
+
end
|
195
|
+
set_pos(@pos)
|
196
|
+
#recalc_line_ends
|
197
|
+
calculate_line_and_column_pos
|
198
|
+
#need_redraw!
|
199
|
+
end
|
200
|
+
|
201
|
+
def delete_range(startpos, endpos, x = nil)
|
202
|
+
s = self[startpos..endpos]
|
203
|
+
if startpos == endpos or s == ""
|
204
|
+
return
|
205
|
+
end
|
206
|
+
if x == :append
|
207
|
+
debug "APPEND"
|
208
|
+
s += "\n" + vma.clipboard.get()
|
209
|
+
end
|
210
|
+
vma.clipboard.set(s)
|
211
|
+
add_delta([startpos, DELETE, (endpos - startpos + 1)], true)
|
212
|
+
#recalc_line_ends
|
213
|
+
calculate_line_and_column_pos
|
214
|
+
end
|
215
|
+
|
216
|
+
def is_delta_ok(delta)
|
217
|
+
ret = true
|
218
|
+
pos = delta[0]
|
219
|
+
if pos < 0
|
220
|
+
ret = false
|
221
|
+
debug "pos=#{pos} < 0"
|
222
|
+
elsif pos > self.size
|
223
|
+
debug "pos=#{pos} > self.size=#{self.size}"
|
224
|
+
ret = false
|
225
|
+
end
|
226
|
+
if ret == false
|
227
|
+
# crash("DELTA OK=#{ret}")
|
228
|
+
end
|
229
|
+
return ret
|
230
|
+
end
|
231
|
+
|
232
|
+
def delete_line()
|
233
|
+
vma.kbd.method_handles_repeat = true
|
234
|
+
num_lines = 1
|
235
|
+
if !vma.kbd.next_command_count.nil? and vma.kbd.next_command_count > 0
|
236
|
+
num_lines = vma.kbd.next_command_count
|
237
|
+
debug "copy num_lines:#{num_lines}"
|
238
|
+
end
|
239
|
+
lrange = line_range(@lpos, num_lines)
|
240
|
+
s = self[lrange]
|
241
|
+
add_delta([lrange.begin, DELETE, lrange.end - lrange.begin + 1], true)
|
242
|
+
vma.clipboard.set(s)
|
243
|
+
update_pos(lrange.begin)
|
244
|
+
@paste_lines = true
|
245
|
+
#recalc_line_ends
|
246
|
+
end
|
247
|
+
|
248
|
+
def insert_tab
|
249
|
+
convert = conf(:tab_to_spaces_default)
|
250
|
+
convert = true if conf(:tab_to_spaces_languages).include?(@lang)
|
251
|
+
convert = false if conf(:tab_to_spaces_not_languages).include?(@lang)
|
252
|
+
tw = conf(:tab_width)
|
253
|
+
if convert
|
254
|
+
indent_to = (@cpos / tw) * tw + tw
|
255
|
+
indentdiff = indent_to - @cpos
|
256
|
+
insert_txt(" " * indentdiff)
|
257
|
+
else
|
258
|
+
insert_txt("\t")
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def insert_image_after_current_line(fname)
|
263
|
+
lr = current_line_range()
|
264
|
+
a = "⟦img:#{fname}⟧\n"
|
265
|
+
b = " \n"
|
266
|
+
txt = a + b
|
267
|
+
insert_txt_at(txt, lr.end + 1)
|
268
|
+
buf.view.handle_deltas
|
269
|
+
imgpos = lr.end + 1 + a.size
|
270
|
+
add_image(fname, imgpos)
|
271
|
+
end
|
272
|
+
end
|
@@ -0,0 +1,303 @@
|
|
1
|
+
# Buffer operations related to cursor position, e.g. moving the cursor (backward, forward, next line etc.)
|
2
|
+
class Buffer < String
|
3
|
+
|
4
|
+
def line(lpos)
|
5
|
+
if @line_ends.size == 0
|
6
|
+
return self
|
7
|
+
end
|
8
|
+
|
9
|
+
#TODO: implement using line_range()
|
10
|
+
if lpos >= @line_ends.size
|
11
|
+
debug("lpos too large") #TODO
|
12
|
+
return ""
|
13
|
+
elsif lpos == @line_ends.size
|
14
|
+
end
|
15
|
+
start = @line_ends[lpos - 1] + 1 if lpos > 0
|
16
|
+
start = 0 if lpos == 0
|
17
|
+
_end = @line_ends[lpos]
|
18
|
+
debug "start: _#{start}, end: #{_end}"
|
19
|
+
return self[start.._end]
|
20
|
+
end
|
21
|
+
|
22
|
+
def at_end_of_line?()
|
23
|
+
return (self[@pos] == "\n" or at_end_of_buffer?)
|
24
|
+
end
|
25
|
+
|
26
|
+
def at_end_of_buffer?()
|
27
|
+
return @pos == self.size
|
28
|
+
end
|
29
|
+
|
30
|
+
def refresh_cursor
|
31
|
+
self.view.set_cursor_pos(@pos)
|
32
|
+
end
|
33
|
+
|
34
|
+
def set_pos(new_pos)
|
35
|
+
if new_pos >= self.size
|
36
|
+
@pos = self.size - 1 # TODO:??right side of last char
|
37
|
+
elsif new_pos >= 0
|
38
|
+
@pos = new_pos
|
39
|
+
end
|
40
|
+
self.view.set_cursor_pos(pos)
|
41
|
+
# gui_set_cursor_pos(@id, @pos)
|
42
|
+
calculate_line_and_column_pos
|
43
|
+
|
44
|
+
check_if_modified_outside
|
45
|
+
end
|
46
|
+
|
47
|
+
# Get the line number of character position
|
48
|
+
def get_line_pos(pos)
|
49
|
+
lpos = @line_ends.bsearch_index { |x, _| x >= pos }
|
50
|
+
return lpos
|
51
|
+
end
|
52
|
+
|
53
|
+
# Calculate the two dimensional column and line positions based on
|
54
|
+
# (one dimensional) position in the buffer.
|
55
|
+
def get_line_and_col_pos(pos)
|
56
|
+
pos = self.size if pos > self.size
|
57
|
+
pos = 0 if pos < 0
|
58
|
+
|
59
|
+
lpos = get_line_pos(pos)
|
60
|
+
|
61
|
+
lpos = @line_ends.size if lpos == nil
|
62
|
+
cpos = pos
|
63
|
+
cpos -= @line_ends[lpos - 1] + 1 if lpos > 0
|
64
|
+
|
65
|
+
return [lpos, cpos]
|
66
|
+
end
|
67
|
+
|
68
|
+
def calculate_line_and_column_pos(reset = true)
|
69
|
+
@lpos, @cpos = get_line_and_col_pos(@pos)
|
70
|
+
reset_larger_cpos if reset
|
71
|
+
end
|
72
|
+
|
73
|
+
def set_line_and_column_pos(lpos, cpos, _reset_larger_cpos = true)
|
74
|
+
@lpos = lpos if !lpos.nil?
|
75
|
+
@cpos = cpos if !cpos.nil?
|
76
|
+
if @lpos > 0
|
77
|
+
new_pos = @line_ends[@lpos - 1] + 1
|
78
|
+
else
|
79
|
+
new_pos = 0
|
80
|
+
end
|
81
|
+
|
82
|
+
if @cpos > (line(@lpos).size - 1)
|
83
|
+
debug("$cpos too large: #{@cpos} #{@lpos}")
|
84
|
+
if @larger_cpos < @cpos
|
85
|
+
@larger_cpos = @cpos
|
86
|
+
end
|
87
|
+
@cpos = line(@lpos).size - 1
|
88
|
+
end
|
89
|
+
new_pos += @cpos
|
90
|
+
set_pos(new_pos)
|
91
|
+
reset_larger_cpos if _reset_larger_cpos
|
92
|
+
end
|
93
|
+
|
94
|
+
# Calculate the one dimensional array index based on column and line positions
|
95
|
+
def calculate_pos_from_cpos_lpos(reset = true)
|
96
|
+
set_line_and_column_pos(nil, nil)
|
97
|
+
end
|
98
|
+
|
99
|
+
def update_pos(pos)
|
100
|
+
@pos = pos
|
101
|
+
calculate_line_and_column_pos
|
102
|
+
end
|
103
|
+
|
104
|
+
def is_legal_pos(pos, op = :read)
|
105
|
+
return false if pos < 0
|
106
|
+
if op == :add
|
107
|
+
return false if pos > self.size
|
108
|
+
elsif op == :read
|
109
|
+
return false if pos >= self.size
|
110
|
+
end
|
111
|
+
return true
|
112
|
+
end
|
113
|
+
|
114
|
+
def jump_to_last_edit()
|
115
|
+
return if @edit_pos_history.empty?
|
116
|
+
@edit_pos_history_i += 1
|
117
|
+
|
118
|
+
if @edit_pos_history_i > @edit_pos_history.size
|
119
|
+
@edit_pos_history_i = 0
|
120
|
+
end
|
121
|
+
|
122
|
+
# if @edit_pos_history.size >= @edit_pos_history_i
|
123
|
+
set_pos(@edit_pos_history[-@edit_pos_history_i])
|
124
|
+
center_on_current_line
|
125
|
+
return true
|
126
|
+
# end
|
127
|
+
end
|
128
|
+
|
129
|
+
def jump_to_next_edit()
|
130
|
+
return if @edit_pos_history.empty?
|
131
|
+
@edit_pos_history_i -= 1
|
132
|
+
@edit_pos_history_i = @edit_pos_history.size - 1 if @edit_pos_history_i < 0
|
133
|
+
debug "@edit_pos_history_i=#{@edit_pos_history_i}"
|
134
|
+
set_pos(@edit_pos_history[-@edit_pos_history_i])
|
135
|
+
center_on_current_line
|
136
|
+
return true
|
137
|
+
end
|
138
|
+
|
139
|
+
def jump_to_random_pos()
|
140
|
+
set_pos(rand(self.size))
|
141
|
+
end
|
142
|
+
|
143
|
+
def jump_to_next_instance_of_word()
|
144
|
+
if $kbd.last_action == $kbd.cur_action and @current_word != nil
|
145
|
+
# debug "REPEATING *"
|
146
|
+
else
|
147
|
+
start_search = [@pos - 150, 0].max
|
148
|
+
|
149
|
+
search_str1 = self[start_search..(@pos)]
|
150
|
+
wsmarks = scan_indexes(search_str1, /(?<=[^\p{Word}])\p{Word}/)
|
151
|
+
a = wsmarks[-1]
|
152
|
+
a = 0 if a == nil
|
153
|
+
|
154
|
+
search_str2 = self[(@pos)..(@pos + 150)]
|
155
|
+
wemarks = scan_indexes(search_str2, /(?<=\p{Word})[^\p{Word}]/)
|
156
|
+
b = wemarks[0]
|
157
|
+
word_start = (@pos - search_str1.size + a + 1)
|
158
|
+
word_start = 0 if !(word_start >= 0)
|
159
|
+
@current_word = self[word_start..(@pos + b - 1)]
|
160
|
+
end
|
161
|
+
|
162
|
+
#TODO: search for /[^\p{Word}]WORD[^\p{Word}]/
|
163
|
+
position_of_next_word = self.index(@current_word, @pos + 1)
|
164
|
+
if position_of_next_word != nil
|
165
|
+
set_pos(position_of_next_word)
|
166
|
+
else #Search from beginning
|
167
|
+
position_of_next_word = self.index(@current_word)
|
168
|
+
set_pos(position_of_next_word) if position_of_next_word != nil
|
169
|
+
end
|
170
|
+
center_on_current_line
|
171
|
+
return true
|
172
|
+
end
|
173
|
+
|
174
|
+
def jump_word(direction, wordpos)
|
175
|
+
offset = 0
|
176
|
+
if direction == FORWARD
|
177
|
+
debug "POS: #{@pos},"
|
178
|
+
search_str = self[(@pos)..(@pos + 250)]
|
179
|
+
return if search_str == nil
|
180
|
+
if wordpos == WORD_START # vim 'w'
|
181
|
+
wsmarks = scan_indexes(search_str, /(?<=[^\p{Word}])\p{Word}|\Z/) # \Z = end of string, just before last newline.
|
182
|
+
wsmarks2 = scan_indexes(search_str, /\n[ \t]*\n/) # "empty" lines that have whitespace
|
183
|
+
wsmarks2 = wsmarks2.collect { |x| x + 1 }
|
184
|
+
wsmarks = (wsmarks2 + wsmarks).sort.uniq
|
185
|
+
offset = 0
|
186
|
+
if wsmarks.any?
|
187
|
+
next_pos = @pos + wsmarks[0] + offset
|
188
|
+
set_pos(next_pos)
|
189
|
+
end
|
190
|
+
elsif wordpos == WORD_END
|
191
|
+
search_str = self[(@pos + 1)..(@pos + 150)]
|
192
|
+
wsmarks = scan_indexes(search_str, /(?<=\p{Word})[^\p{Word}]/)
|
193
|
+
offset = -1
|
194
|
+
if wsmarks.any?
|
195
|
+
next_pos = @pos + 1 + wsmarks[0] + offset
|
196
|
+
set_pos(next_pos)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
if direction == BACKWARD # vim 'b'
|
201
|
+
start_search = @pos - 150 #TODO 150 length limit
|
202
|
+
start_search = 0 if start_search < 0
|
203
|
+
search_str = self[start_search..(@pos - 1)]
|
204
|
+
return if search_str == nil
|
205
|
+
wsmarks = scan_indexes(search_str,
|
206
|
+
#/(^|(\W)\w|\n)/) #TODO 150 length limit
|
207
|
+
#/^|(?<=[^\p{Word}])\p{Word}|(?<=\n)\n/) #include empty lines?
|
208
|
+
/\A|(?<=[^\p{Word}])\p{Word}/) # Start of string or nonword,word.
|
209
|
+
|
210
|
+
offset = 0
|
211
|
+
|
212
|
+
if wsmarks.any?
|
213
|
+
next_pos = start_search + wsmarks.last + offset
|
214
|
+
set_pos(next_pos)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def jump_to_mark(mark_char)
|
220
|
+
p = @marks[mark_char]
|
221
|
+
set_pos(p) if p
|
222
|
+
center_on_current_line
|
223
|
+
return true
|
224
|
+
end
|
225
|
+
|
226
|
+
def jump(target)
|
227
|
+
if target == START_OF_BUFFER
|
228
|
+
set_pos(0)
|
229
|
+
end
|
230
|
+
if target == END_OF_BUFFER
|
231
|
+
set_pos(self.size - 1)
|
232
|
+
end
|
233
|
+
if target == BEGINNING_OF_LINE
|
234
|
+
@cpos = 0
|
235
|
+
calculate_pos_from_cpos_lpos
|
236
|
+
end
|
237
|
+
if target == END_OF_LINE
|
238
|
+
@cpos = line(@lpos).size - 1
|
239
|
+
calculate_pos_from_cpos_lpos
|
240
|
+
end
|
241
|
+
|
242
|
+
if target == FIRST_NON_WHITESPACE
|
243
|
+
l = current_line()
|
244
|
+
debug l.inspect
|
245
|
+
@cpos = line(@lpos).size - 1
|
246
|
+
a = scan_indexes(l, /\S/)
|
247
|
+
debug a.inspect
|
248
|
+
if a.any?
|
249
|
+
@cpos = a[0]
|
250
|
+
else
|
251
|
+
@cpos = 0
|
252
|
+
end
|
253
|
+
calculate_pos_from_cpos_lpos
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def jump_to_line(line_n = 1)
|
258
|
+
|
259
|
+
# $method_handles_repeat = true
|
260
|
+
# if !$next_command_count.nil? and $next_command_count > 0
|
261
|
+
# line_n = $next_command_count
|
262
|
+
# debug "jump to line:#{line_n}"
|
263
|
+
# end
|
264
|
+
debug "jump to line:#{line_n}"
|
265
|
+
line_n = get_repeat_num() if line_n == 1
|
266
|
+
|
267
|
+
if line_n > @line_ends.size
|
268
|
+
debug("lpos too large") #TODO
|
269
|
+
return
|
270
|
+
end
|
271
|
+
if line_n == 1
|
272
|
+
set_pos(0)
|
273
|
+
else
|
274
|
+
set_pos(@line_ends[line_n - 2] + 1)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def jump_to_next_instance_of_char(char, direction = FORWARD)
|
279
|
+
if direction == FORWARD
|
280
|
+
position_of_next_char = self.index(char, @pos + 1)
|
281
|
+
if position_of_next_char != nil
|
282
|
+
@pos = position_of_next_char
|
283
|
+
end
|
284
|
+
elsif direction == BACKWARD
|
285
|
+
start_search = @pos - 250
|
286
|
+
start_search = 0 if start_search < 0
|
287
|
+
search_substr = self[start_search..(@pos - 1)]
|
288
|
+
_pos = search_substr.reverse.index(char)
|
289
|
+
if _pos != nil
|
290
|
+
@pos -= (_pos + 1)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
m = method("jump_to_next_instance_of_char")
|
294
|
+
set_last_command({ method: m, params: [char, direction] })
|
295
|
+
$last_find_command = { char: char, direction: direction }
|
296
|
+
set_pos(@pos)
|
297
|
+
return true
|
298
|
+
end
|
299
|
+
|
300
|
+
def jump_to_pos(new_pos)
|
301
|
+
set_pos(new_pos)
|
302
|
+
end
|
303
|
+
end
|