textbringer 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,26 +4,38 @@ require "open3"
4
4
  require "io/wait"
5
5
 
6
6
  module Textbringer
7
+ Command = Struct.new(:name, :block, :doc)
8
+
7
9
  module Commands
8
10
  include Utils
9
11
 
10
- @@command_list = []
12
+ @command_table = {}
11
13
 
12
14
  def self.list
13
- @@command_list
15
+ @command_table.keys
16
+ end
17
+
18
+ def self.command_table
19
+ @command_table
20
+ end
21
+
22
+ def self.[](name)
23
+ @command_table[name.intern]
14
24
  end
15
25
 
16
- def define_command(name, &block)
26
+ def define_command(name, doc: "No documentation", &block)
27
+ name = name.intern
17
28
  Commands.send(:define_method, name, &block)
18
- @@command_list << name if !@@command_list.include?(name)
29
+ Commands.command_table[name] = Command.new(name, block, doc)
19
30
  name
20
31
  end
21
32
  module_function :define_command
22
33
 
23
34
  def undefine_command(name)
24
- if @@command_list.include?(name)
35
+ name = name.intern
36
+ if Commands.command_table.key?(name)
25
37
  Commands.send(:undef_method, name)
26
- @@command_list.delete(name)
38
+ Commands.command_table.delete(name)
27
39
  end
28
40
  end
29
41
  module_function :undefine_command
@@ -2,63 +2,175 @@
2
2
 
3
3
  module Textbringer
4
4
  module Commands
5
- [
6
- :forward_char,
7
- :backward_char,
8
- :forward_word,
9
- :backward_word,
10
- :next_line,
11
- :previous_line,
12
- :delete_char,
13
- :backward_delete_char,
14
- ].each do |name|
15
- define_command(name) do |n = number_prefix_arg|
16
- Buffer.current.send(name, n)
17
- end
5
+ define_command(:forward_char,
6
+ doc: "Move point n characters forward.") do
7
+ |n = number_prefix_arg|
8
+ Buffer.current.forward_char(n)
9
+ end
10
+
11
+ define_command(:backward_char,
12
+ doc: "Move point n characters backward.") do
13
+ |n = number_prefix_arg|
14
+ Buffer.current.backward_char(n)
18
15
  end
19
16
 
20
- [
21
- :beginning_of_line,
22
- :end_of_line,
23
- :beginning_of_buffer,
24
- :end_of_buffer,
25
- :exchange_point_and_mark,
26
- :copy_region,
27
- :kill_region,
28
- :yank,
29
- :newline,
30
- :delete_region,
31
- :transpose_chars
32
- ].each do |name|
33
- define_command(name) do
34
- Buffer.current.send(name)
35
- end
17
+ define_command(:forward_word,
18
+ doc: "Move point n words forward.") do
19
+ |n = number_prefix_arg|
20
+ Buffer.current.forward_word(n)
21
+ end
22
+
23
+ define_command(:backward_word,
24
+ doc: "Move point n words backward.") do
25
+ |n = number_prefix_arg|
26
+ Buffer.current.backward_word(n)
27
+ end
28
+
29
+
30
+ define_command(:next_line,
31
+ doc: "Move point n lines forward.") do
32
+ |n = number_prefix_arg|
33
+ Buffer.current.next_line(n)
34
+ end
35
+
36
+ define_command(:previous_line,
37
+ doc: "Move point n lines backward.") do
38
+ |n = number_prefix_arg|
39
+ Buffer.current.previous_line(n)
40
+ end
41
+
42
+ define_command(:delete_char,
43
+ doc: "Delete n characters forward.") do
44
+ |n = number_prefix_arg|
45
+ Buffer.current.delete_char(n)
46
+ end
47
+
48
+ define_command(:backward_delete_char,
49
+ doc: "Delete n characters backward.") do
50
+ |n = number_prefix_arg|
51
+ Buffer.current.backward_delete_char(n)
52
+ end
53
+
54
+ define_command(:beginning_of_line,
55
+ doc: "Move point to the beginning of the current line.") do
56
+ Buffer.current.beginning_of_line
36
57
  end
37
58
 
38
- define_command(:set_mark_command) do
39
- Buffer.current.set_mark
40
- message("Mark set")
59
+ define_command(:end_of_line,
60
+ doc: "Move point to the end of the current line.") do
61
+ Buffer.current.end_of_line
41
62
  end
42
63
 
43
- define_command(:goto_char) do
44
- |n = read_from_minibuffer("Go to char: ")|
45
- Buffer.current.goto_char(n.to_i)
64
+ define_command(:beginning_of_buffer,
65
+ doc: "Move point to the beginning of the buffer.") do
66
+ Buffer.current.beginning_of_buffer
67
+ end
68
+
69
+ define_command(:end_of_buffer,
70
+ doc: "Move point to the end of the buffer.") do
71
+ Buffer.current.end_of_buffer
72
+ end
73
+
74
+ define_command(:push_mark,
75
+ doc: <<~EOD) do
76
+ Set mark at pos, and push the mark on the mark ring.
77
+ Unlike Emacs, the new mark is pushed on the mark ring instead of
78
+ the old one.
79
+ EOD
80
+ |pos = Buffer.current.point|
81
+ Buffer.current.push_mark(pos)
82
+ end
83
+
84
+ define_command(:pop_mark,
85
+ doc: "Pop the mark from the mark ring.") do
86
+ Buffer.current.pop_mark
87
+ end
88
+
89
+ define_command(:pop_to_mark,
90
+ doc: <<~EOD) do
91
+ Move point to where the mark is, and pop the mark from
92
+ the mark ring.
93
+ EOD
94
+ Buffer.current.pop_to_mark
95
+ end
96
+
97
+ define_command(:exchange_point_and_mark,
98
+ doc: "Exchange the positions of point and mark.") do
99
+ Buffer.current.exchange_point_and_mark
100
+ end
101
+
102
+ define_command(:copy_region,
103
+ doc: "Copy the region to the kill ring.") do
104
+ Buffer.current.copy_region
105
+ end
106
+
107
+ define_command(:kill_region,
108
+ doc: "Copy and delete the region.") do
109
+ Buffer.current.kill_region
110
+ end
111
+
112
+ define_command(:yank,
113
+ doc: "Insert the last text copied in the kill ring.") do
114
+ Buffer.current.yank
115
+ end
116
+
117
+ define_command(:newline,
118
+ doc: "Insert a newline.") do
119
+ Buffer.current.newline
120
+ end
121
+
122
+ define_command(:delete_region,
123
+ doc: "Delete the region without copying.") do
124
+ Buffer.current.delete_region
125
+ end
126
+
127
+ define_command(:transpose_chars,
128
+ doc: "Transpose characters.") do
129
+ Buffer.current.transpose_chars
130
+ end
131
+
132
+ define_command(:set_mark_command,
133
+ doc: <<~EOD) do
134
+ Set the mark at point.
135
+
136
+ With C-u, move point to where the mark is, and pop the mark from
137
+ the mark ring.
138
+ EOD
139
+ |arg = current_prefix_arg|
140
+ buffer = Buffer.current
141
+ if arg
142
+ buffer.pop_to_mark
143
+ else
144
+ buffer.push_mark
145
+ message("Mark set")
146
+ end
147
+ end
148
+
149
+ define_command(:goto_char,
150
+ doc: "Move point to pos.") do
151
+ |pos = read_from_minibuffer("Go to char: ")|
152
+ Buffer.current.goto_char(pos.to_i)
46
153
  Window.current.recenter_if_needed
47
154
  end
48
155
 
49
- define_command(:goto_line) do
156
+ define_command(:goto_line,
157
+ doc: "Move point to line n.") do
50
158
  |n = read_from_minibuffer("Go to line: ")|
51
159
  Buffer.current.goto_line(n.to_i)
52
160
  Window.current.recenter_if_needed
53
161
  end
54
162
 
55
- define_command(:self_insert) do |n = number_prefix_arg|
163
+ define_command(:self_insert,
164
+ doc: "Insert the character typed.") do
165
+ |n = number_prefix_arg|
56
166
  c = Controller.current.last_key
57
167
  merge_undo = Controller.current.last_command == :self_insert
58
168
  Buffer.current.insert(c * n, merge_undo)
59
169
  end
60
170
 
61
- define_command(:quoted_insert) do |n = number_prefix_arg|
171
+ define_command(:quoted_insert,
172
+ doc: "Read a character, and insert it.") do
173
+ |n = number_prefix_arg|
62
174
  c = Controller.current.read_char
63
175
  if !c.is_a?(String)
64
176
  raise EditorError, "Invalid key"
@@ -66,17 +178,22 @@ module Textbringer
66
178
  Buffer.current.insert(c * n)
67
179
  end
68
180
 
69
- define_command(:kill_line) do
181
+ define_command(:kill_line,
182
+ doc: "Kill the rest of the current line.") do
70
183
  Buffer.current.kill_line(Controller.current.last_command == :kill_region)
71
184
  Controller.current.this_command = :kill_region
72
185
  end
73
186
 
74
- define_command(:kill_word) do
187
+ define_command(:kill_word,
188
+ doc: "Kill a word.") do
75
189
  Buffer.current.kill_word(Controller.current.last_command == :kill_region)
76
190
  Controller.current.this_command = :kill_region
77
191
  end
78
192
 
79
- define_command(:yank_pop) do
193
+ define_command(:yank_pop,
194
+ doc: <<~EOD) do
195
+ Rotate the kill ring, and replace the yanked text.
196
+ EOD
80
197
  if Controller.current.last_command != :yank
81
198
  raise EditorError, "Previous command was not a yank"
82
199
  end
@@ -84,14 +201,41 @@ module Textbringer
84
201
  Controller.current.this_command = :yank
85
202
  end
86
203
 
87
- define_command(:undo) do
204
+ define_command(:undo,
205
+ doc: "Undo changes.") do
88
206
  Buffer.current.undo
89
207
  message("Undo!") unless Window.echo_area.current?
90
208
  end
91
209
 
92
- define_command(:redo) do
210
+ define_command(:redo_command,
211
+ doc: "Redo changes reverted by undo.") do
93
212
  Buffer.current.redo
94
213
  message("Redo!") unless Window.echo_area.current?
95
214
  end
215
+
216
+ define_command(:back_to_indentation,
217
+ doc: "Move point to the first non-space character.") do
218
+ buffer = Buffer.current
219
+ buffer.beginning_of_line
220
+ while /[ \t]/ =~ buffer.char_after
221
+ buffer.forward_char
222
+ end
223
+ end
224
+
225
+ define_command(:delete_indentation,
226
+ doc: <<~EOD) do
227
+ Delete indentation and join the current line to the previous line.
228
+ EOD
229
+ buffer = Buffer.current
230
+ back_to_indentation
231
+ pos = buffer.point
232
+ buffer.skip_re_backward(/[^\n]/)
233
+ return if buffer.beginning_of_buffer?
234
+ buffer.backward_char
235
+ buffer.skip_re_backward(/[ \t]/)
236
+ buffer.delete_region(buffer.point, pos)
237
+ buffer.insert(" ")
238
+ buffer.backward_char
239
+ end
96
240
  end
97
241
  end
@@ -21,27 +21,41 @@ module Textbringer
21
21
  GLOBAL_MAP.define_key("\ey", :clipboard_yank_pop)
22
22
  end
23
23
 
24
- define_command(:clipboard_copy_region) do
24
+ define_command(:clipboard_copy_region, doc: <<~EOD) do
25
+ Copy the region to the kill ring and the clipboard.
26
+ EOD
25
27
  copy_region
26
28
  Clipboard.copy(KILL_RING.current)
27
29
  end
28
30
 
29
- define_command(:clipboard_kill_region) do
31
+ define_command(:clipboard_kill_region, doc: <<~EOD) do
32
+ Copy the region to the kill ring and the clipboard, and delete
33
+ the region.
34
+ EOD
30
35
  kill_region
31
36
  Clipboard.copy(KILL_RING.current)
32
37
  end
33
38
 
34
- define_command(:clipboard_kill_line) do
39
+ define_command(:clipboard_kill_line, doc: <<~EOD) do
40
+ Kill the rest of the current line, and copy the killed text to
41
+ the clipboard.
42
+ EOD
35
43
  kill_line
36
44
  Clipboard.copy(KILL_RING.current)
37
45
  end
38
46
 
39
- define_command(:clipboard_kill_word) do
47
+ define_command(:clipboard_kill_word, doc: <<~EOD) do
48
+ Kill a word, and copy the word to the clipboard.
49
+ EOD
40
50
  kill_word
41
51
  Clipboard.copy(KILL_RING.current)
42
52
  end
43
53
 
44
- define_command(:clipboard_yank) do
54
+ define_command(:clipboard_yank, doc: <<~EOD) do
55
+ If the clipboard contents are different from the last killed text,
56
+ push the contents to the kill ring, and insert it.
57
+ Otherwise, just insert the last text copied in the kill ring.
58
+ EOD
45
59
  s = Clipboard.paste.encode(Encoding::UTF_8).gsub(/\r\n/, "\n")
46
60
  if !s.empty? && (KILL_RING.empty? || KILL_RING.current != s)
47
61
  KILL_RING.push(s)
@@ -50,7 +64,10 @@ module Textbringer
50
64
  Controller.current.this_command = :yank
51
65
  end
52
66
 
53
- define_command(:clipboard_yank_pop) do
67
+ define_command(:clipboard_yank_pop, doc: <<~EOD) do
68
+ Rotate the kill ring, and replace the yanked text, and copy
69
+ the text to the clipboard.
70
+ EOD
54
71
  yank_pop
55
72
  Clipboard.copy(KILL_RING.current)
56
73
  end
@@ -2,9 +2,6 @@
2
2
 
3
3
  module Textbringer
4
4
  module Commands
5
- GLOBAL_MAP.define_key("\e.", :find_tag)
6
- GLOBAL_MAP.define_key("\e*", :pop_tag_mark)
7
-
8
5
  CTAGS = {
9
6
  path: nil,
10
7
  tags: nil,
@@ -58,7 +55,8 @@ module Textbringer
58
55
  push_tag_mark_and_find_file(file)
59
56
  beginning_of_buffer
60
57
  n.times do
61
- re_search_forward("^" + Regexp.quote($1) + "$")
58
+ s = Regexp.quote($1.gsub(/\\([\\\/])/, "\\1"))
59
+ re_search_forward("^" + s + "$")
62
60
  end
63
61
  beginning_of_line
64
62
  when %r'\A\?\^(.*)\$\?\z'
@@ -92,28 +90,10 @@ module Textbringer
92
90
  CTAGS[:tags]
93
91
  end
94
92
 
95
- define_command(:pop_tag_mark) do
96
- tag_mark_stack = CTAGS[:tag_mark_stack]
97
- if tag_mark_stack.empty?
98
- raise EditorError, "No previous locations"
99
- end
100
- mark = tag_mark_stack.pop
101
- begin
102
- switch_to_buffer(mark.buffer)
103
- mark.buffer.point_to_mark(mark)
104
- ensure
105
- mark.delete
106
- end
107
- end
108
-
109
93
  private
110
94
 
111
95
  def push_tag_mark_and_find_file(file)
112
- tag_mark_stack = CTAGS[:tag_mark_stack]
113
- if tag_mark_stack.size == CONFIG[:tag_mark_limit]
114
- tag_mark_stack.shift.delete
115
- end
116
- tag_mark_stack.push(Buffer.current.new_mark)
96
+ Buffer.current.push_global_mark(force: true)
117
97
  find_file(file)
118
98
  end
119
99
  end
@@ -4,7 +4,7 @@ require "editorconfig"
4
4
 
5
5
  module Textbringer
6
6
  module Commands
7
- define_command(:find_file) do
7
+ define_command(:find_file, doc: "Open or create a file.") do
8
8
  |file_name = read_file_name("Find file: ")|
9
9
  config = EditorConfig.load_file(file_name)
10
10
  buffer = Buffer.find_file(file_name)
@@ -48,7 +48,7 @@ module Textbringer
48
48
  end
49
49
  end
50
50
 
51
- define_command(:save_buffer) do
51
+ define_command(:save_buffer, doc: "Save the current buffer to a file.") do
52
52
  if Buffer.current.file_name.nil?
53
53
  Buffer.current.file_name = read_file_name("File to save in: ")
54
54
  next if Buffer.current.file_name.nil?
@@ -63,7 +63,8 @@ module Textbringer
63
63
  message("Wrote #{Buffer.current.file_name}")
64
64
  end
65
65
 
66
- define_command(:write_file) do
66
+ define_command(:write_file,
67
+ doc: "Save the current buffer as the specified file.") do
67
68
  |file_name = read_file_name("Write file: ")|
68
69
  if File.directory?(file_name)
69
70
  file_name = File.expand_path(Buffer.current.name, file_name)
@@ -78,23 +79,25 @@ module Textbringer
78
79
  message("Wrote #{Buffer.current.file_name}")
79
80
  end
80
81
 
81
- define_command(:set_buffer_file_encoding) do
82
+ define_command(:set_buffer_file_encoding,
83
+ doc: "Set the file encoding of the current buffer.") do
82
84
  |enc = read_from_minibuffer("File encoding: ",
83
85
  default: Buffer.current.file_encoding.name)|
84
86
  Buffer.current.file_encoding = enc
85
87
  end
86
88
 
87
- define_command(:set_buffer_file_format) do
89
+ define_command(:set_buffer_file_format,
90
+ doc: "Set the file format of the current buffer.") do
88
91
  |format = read_from_minibuffer("File format: ",
89
92
  default: Buffer.current.file_format.to_s)|
90
93
  Buffer.current.file_format = format
91
94
  end
92
95
 
93
- define_command(:pwd) do
96
+ define_command(:pwd, doc: "Show the current working directory.") do
94
97
  message(Dir.pwd)
95
98
  end
96
99
 
97
- define_command(:chdir) do
100
+ define_command(:chdir, doc: "Change the current working directory.") do
98
101
  |dir_name = read_file_name("Change directory: ")|
99
102
  Dir.chdir(dir_name)
100
103
  end