vimamsa 0.1.13 → 0.1.15
Sign up to get free protection for your applications and to get access to all the features.
- 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
|