textbringer 0.1.8 → 0.1.9

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.
@@ -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