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.
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Textbringer
4
+ module Commands
5
+ HELP_RING = Ring.new
6
+
7
+ def push_help_command(cmd)
8
+ if HELP_RING.empty? || HELP_RING.current != cmd
9
+ HELP_RING.push(cmd)
10
+ end
11
+ end
12
+ private :push_help_command
13
+
14
+ def show_help
15
+ help = Buffer.find_or_new("*Help*", undo_limit: 0)
16
+ help.read_only_edit do
17
+ help.clear
18
+ yield(help)
19
+ help.beginning_of_buffer
20
+ switch_to_buffer(help)
21
+ help_mode
22
+ end
23
+ end
24
+ private :show_help
25
+
26
+ define_command(:describe_bindings,
27
+ doc: "Display the key bindings.") do
28
+ show_help do |help|
29
+ s = format("%-16s %s\n", "Key", "Binding")
30
+ s << format("%-16s %s\n", "---", "-------")
31
+ s << "\n"
32
+ bindings = {}
33
+ [
34
+ GLOBAL_MAP,
35
+ Buffer.current.keymap,
36
+ Controller.current.overriding_map
37
+ ].each do |map|
38
+ map&.each do |key_sequence, command|
39
+ bindings[key_sequence] = command
40
+ end
41
+ end
42
+ bindings.each do |key_sequence, command|
43
+ s << format("%-16s [%s]\n",
44
+ Keymap.key_sequence_string(key_sequence),
45
+ command)
46
+ end
47
+ help.insert(s)
48
+ end
49
+ push_help_command([:describe_bindings])
50
+ end
51
+
52
+ def command_help(cmd)
53
+ s = format("%s:%d\n", *cmd.block.source_location)
54
+ s << "-" * (Window.columns - 2) + "\n"
55
+ s << "#{cmd.name}"
56
+ if !cmd.block.parameters.empty?
57
+ s << "("
58
+ s << cmd.block.parameters.map { |_, param| param }.join(", ")
59
+ s << ")"
60
+ end
61
+ s << "\n\n"
62
+ s << "-" * (Window.columns - 2) + "\n\n"
63
+ s << cmd.doc
64
+ s << "\n"
65
+ s
66
+ end
67
+
68
+ define_command(:describe_command,
69
+ doc: "Display the documentation of the command.") do
70
+ |name = read_command_name("Describe command: ")|
71
+ cmd = Commands[name]
72
+ if cmd.nil?
73
+ raise EditorError, "No such command: #{name}"
74
+ end
75
+ show_help do |help|
76
+ help.insert(command_help(cmd))
77
+ end
78
+ push_help_command([:describe_command, name])
79
+ end
80
+
81
+ define_command(:describe_key,
82
+ doc: <<~EOD) do
83
+ Display the documentation of the command invoked by key.
84
+ EOD
85
+ |key = read_key_sequence("Describe key: ")|
86
+ name = Buffer.current.keymap&.lookup(key) ||
87
+ GLOBAL_MAP.lookup(key)
88
+ cmd = Commands[name]
89
+ show_help do |help|
90
+ s = Keymap.key_sequence_string(key)
91
+ s << " runs the command #{name}, which is defined in\n"
92
+ s << command_help(cmd)
93
+ help.insert(s)
94
+ end
95
+ push_help_command([:describe_key, key])
96
+ end
97
+
98
+ define_command(:help_go_back, doc: "Go back to the previous help.") do
99
+ if !HELP_RING.empty?
100
+ HELP_RING.rotate(1)
101
+ cmd, *args = HELP_RING.current
102
+ send(cmd, *args)
103
+ end
104
+ end
105
+
106
+ define_command(:help_go_forward, doc: "Go back to the next help.") do
107
+ if !HELP_RING.empty?
108
+ HELP_RING.rotate(-1)
109
+ cmd, *args = HELP_RING.current
110
+ send(cmd, *args)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -18,7 +18,7 @@ module Textbringer
18
18
  ISEARCH_MODE_MAP.define_key(?\C-h, :isearch_delete_char)
19
19
  ISEARCH_MODE_MAP.define_key(?\C-s, :isearch_repeat_forward)
20
20
  ISEARCH_MODE_MAP.define_key(?\C-r, :isearch_repeat_backward)
21
- ISEARCH_MODE_MAP.define_key(?\n, :isearch_exit)
21
+ ISEARCH_MODE_MAP.define_key(?\C-m, :isearch_exit)
22
22
  ISEARCH_MODE_MAP.define_key(?\C-g, :isearch_abort)
23
23
 
24
24
  ISEARCH_STATUS = {
@@ -30,11 +30,13 @@ module Textbringer
30
30
  recursive_edit: false
31
31
  }
32
32
 
33
- define_command(:isearch_forward) do |**options|
33
+ define_command(:isearch_forward,
34
+ doc: "Incrementally search forward.") do |**options|
34
35
  isearch_mode(true, **options)
35
36
  end
36
37
 
37
- define_command(:isearch_backward) do |**options|
38
+ define_command(:isearch_backward,
39
+ doc: "Incrementally search backward.") do |**options|
38
40
  isearch_mode(false, **options)
39
41
  end
40
42
 
@@ -78,23 +80,27 @@ module Textbringer
78
80
  end
79
81
  end
80
82
 
81
- define_command(:isearch_exit) do
83
+ define_command(:isearch_exit, doc: "Exit incremental search.") do
82
84
  isearch_done
83
85
  end
84
86
 
85
- define_command(:isearch_abort) do
87
+ define_command(:isearch_abort, doc: "Abort incremental search.") do
86
88
  goto_char(Buffer.current[:isearch_start])
87
89
  isearch_done
88
90
  raise Quit
89
91
  end
90
92
 
91
- define_command(:isearch_printing_char) do
93
+ define_command(:isearch_printing_char, doc: <<~EOD) do
94
+ Add the typed character to the search string and search.
95
+ EOD
92
96
  c = Controller.current.last_key
93
97
  ISEARCH_STATUS[:string].concat(c)
94
98
  isearch_search
95
99
  end
96
100
 
97
- define_command(:isearch_delete_char) do
101
+ define_command(:isearch_delete_char, doc: <<~EOD) do
102
+ Delete the last character from the search string and search.
103
+ EOD
98
104
  ISEARCH_STATUS[:string].chop!
99
105
  isearch_search
100
106
  end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Textbringer
4
+ module Commands
5
+ KEYBOARD_MACROS = {}
6
+
7
+ define_command(:start_keyboard_macro) do
8
+ message("Recording keyboard macro...")
9
+ Controller.current.start_keyboard_macro
10
+ end
11
+
12
+ define_command(:end_keyboard_macro) do
13
+ Controller.current.end_keyboard_macro
14
+ message("Keyboard macro defined")
15
+ end
16
+
17
+ define_command(:call_last_keyboard_macro) do |n = number_prefix_arg|
18
+ key = Controller.current.last_key
19
+ Controller.current.call_last_keyboard_macro(n)
20
+ map = Keymap.new
21
+ map.define_key(key, :call_last_keyboard_macro)
22
+ set_transient_map(map)
23
+ end
24
+
25
+ define_command(:end_and_call_keyboard_macro) do |n = number_prefix_arg|
26
+ if Controller.current.recording_keyboard_macro?
27
+ end_keyboard_macro
28
+ end
29
+ call_last_keyboard_macro(n)
30
+ end
31
+
32
+ define_command(:end_or_call_keyboard_macro) do |n = number_prefix_arg|
33
+ if Controller.current.recording_keyboard_macro?
34
+ end_keyboard_macro
35
+ else
36
+ call_last_keyboard_macro(n)
37
+ end
38
+ end
39
+
40
+ def execute_keyboard_macro(macro, n = 1)
41
+ Controller.current.execute_keyboard_macro(macro, n)
42
+ end
43
+
44
+ define_command(:name_last_keyboard_macro) do
45
+ |name = read_from_minibuffer("Name for last keyboard macro: ")|
46
+ last_keyboard_macro = Controller.current.last_keyboard_macro
47
+ if last_keyboard_macro.nil?
48
+ raise EditorError, "Keyboard macro not defined"
49
+ end
50
+ KEYBOARD_MACROS[name] = last_keyboard_macro
51
+ define_command(name) do |n = number_prefix_arg|
52
+ execute_keyboard_macro(last_keyboard_macro, n)
53
+ end
54
+ end
55
+
56
+ def read_keyboard_macro(prompt)
57
+ macros = KEYBOARD_MACROS.keys.map(&:to_s)
58
+ f = ->(s) { complete_for_minibuffer(s, macros) }
59
+ read_from_minibuffer(prompt, completion_proc: f)
60
+ end
61
+
62
+ module SymbolDump
63
+ refine Symbol do
64
+ def dump
65
+ ":" + to_s.dump
66
+ end
67
+ end
68
+ end
69
+
70
+ using SymbolDump
71
+
72
+ define_command(:insert_keyboard_macro) do
73
+ |name = read_keyboard_macro("Insert keyboard macro: ")|
74
+ macro = KEYBOARD_MACROS[name]
75
+ if macro.nil?
76
+ raise EditorError, "No such macro: #{name}"
77
+ end
78
+ macro_literal = "[" + macro.map { |key| key.dump }.join(",") + "]"
79
+ insert(<<~EOF)
80
+ define_command(:#{name}) do |n = number_prefix_arg|
81
+ execute_keyboard_macro(#{macro_literal}, n)
82
+ end
83
+ EOF
84
+ end
85
+ end
86
+ end
@@ -162,8 +162,7 @@ module Textbringer
162
162
  Controller.current.current_prefix_arg
163
163
  end
164
164
 
165
- def number_prefix_arg
166
- arg = current_prefix_arg
165
+ def prefix_numeric_value(arg)
167
166
  case arg
168
167
  when Integer
169
168
  arg
@@ -176,6 +175,10 @@ module Textbringer
176
175
  end
177
176
  end
178
177
 
178
+ def number_prefix_arg
179
+ prefix_numeric_value(current_prefix_arg)
180
+ end
181
+
179
182
  define_command(:digit_argument) do
180
183
  |arg = current_prefix_arg|
181
184
  n = Controller.current.last_key.to_i
@@ -225,6 +228,44 @@ module Textbringer
225
228
  Controller.current.recursive_edit
226
229
  end
227
230
 
231
+ def goto_global_mark
232
+ global_mark_ring = Buffer.global_mark_ring
233
+ mark = yield(global_mark_ring)
234
+ if mark.buffer&.current? && Buffer.current.point_at_mark?(mark)
235
+ mark = yield(global_mark_ring)
236
+ end
237
+ if mark.detached?
238
+ find_file(mark.file_name)
239
+ goto_char(mark.location)
240
+ else
241
+ switch_to_buffer(mark.buffer)
242
+ mark.buffer.point_to_mark(mark)
243
+ end
244
+ end
245
+ private :goto_global_mark
246
+
247
+ define_command(:next_global_mark) do
248
+ if Buffer.global_mark_ring.empty?
249
+ raise EditorError, "Global mark ring is empty"
250
+ end
251
+ if Buffer.current.push_global_mark
252
+ Buffer.global_mark_ring.pop
253
+ end
254
+ goto_global_mark do |mark_ring|
255
+ mark_ring.pop
256
+ end
257
+ end
258
+
259
+ define_command(:previous_global_mark) do
260
+ if Buffer.global_mark_ring.empty?
261
+ raise EditorError, "Global mark ring is empty"
262
+ end
263
+ Buffer.current.push_global_mark
264
+ goto_global_mark do |mark_ring|
265
+ mark_ring.rotate(-1)
266
+ end
267
+ end
268
+
228
269
  define_command(:shell_execute) do
229
270
  |cmd = read_from_minibuffer("Shell execute: "),
230
271
  buffer_name: "*Shell output*",
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Textbringer
4
+ module Commands
5
+ class BufferPosition
6
+ attr_reader :buffer, :mark
7
+
8
+ def initialize(buffer, mark)
9
+ @buffer = buffer
10
+ @mark = mark
11
+ end
12
+
13
+ def to_s
14
+ @mark.location.to_s
15
+ end
16
+ end
17
+
18
+ REGISTERS = {}
19
+
20
+ def REGISTERS.[]=(name, val)
21
+ old_val = REGISTERS[name]
22
+ if old_val.is_a?(BufferPosition)
23
+ old_val.mark.delete
24
+ end
25
+ super(name, val)
26
+ end
27
+
28
+ def read_register(prompt)
29
+ Window.echo_area.show(prompt)
30
+ Window.redisplay
31
+ begin
32
+ register = read_char
33
+ register
34
+ ensure
35
+ Window.echo_area.clear
36
+ Window.redisplay
37
+ end
38
+ end
39
+
40
+ define_command(:point_to_register) do
41
+ |register = read_register("Point to register:")|
42
+ unless register.is_a?(String)
43
+ raise ArgumentError, "Invalid register: #{register}"
44
+ end
45
+ buffer = Buffer.current
46
+ mark = buffer.new_mark
47
+ REGISTERS[register] = BufferPosition.new(buffer, mark)
48
+ end
49
+
50
+ define_command(:jump_to_register) do
51
+ |register = read_register("Jump to register:")|
52
+ if !register.is_a?(String)
53
+ raise ArgumentError, "Invalid register: #{register}"
54
+ end
55
+ position = REGISTERS[register]
56
+ if !position.is_a?(BufferPosition)
57
+ raise ArgumentError, "Register doesn't contain a buffer position"
58
+ end
59
+ switch_to_buffer(position.buffer)
60
+ position.buffer.point_to_mark(position.mark)
61
+ end
62
+
63
+ define_command(:copy_to_register) do
64
+ |register = read_register("Copy to register:"),
65
+ s = Buffer.current.mark, e = Buffer.current.point,
66
+ delete_flag = current_prefix_arg|
67
+ buffer = Buffer.current
68
+ str = s <= e ? buffer.substring(s, e) : buffer.substring(e, s)
69
+ REGISTERS[register] = str
70
+ if delete_flag
71
+ buffer.delete_region(s, e)
72
+ end
73
+ end
74
+
75
+ define_command(:append_to_register) do
76
+ |register = read_register("Append to register:"),
77
+ s = Buffer.current.mark, e = Buffer.current.point,
78
+ delete_flag = current_prefix_arg|
79
+ buffer = Buffer.current
80
+ str = s <= e ? buffer.substring(s, e) : buffer.substring(e, s)
81
+ val = REGISTERS[register]
82
+ if !val.is_a?(String)
83
+ raise ArgumentError, "Register doesn't contain text"
84
+ end
85
+ REGISTERS[register] = val + str
86
+ if delete_flag
87
+ buffer.delete_region(s, e)
88
+ end
89
+ end
90
+
91
+ define_command(:insert_register) do
92
+ |register = read_register("Insert register:"),
93
+ arg = current_prefix_arg|
94
+ buffer = Buffer.current
95
+ str = REGISTERS[register]
96
+ if arg
97
+ buffer.push_mark
98
+ end
99
+ pos = buffer.point
100
+ insert(str)
101
+ if !arg
102
+ buffer.push_mark
103
+ buffer.goto_char(pos)
104
+ end
105
+ end
106
+
107
+ define_command(:number_to_register) do
108
+ |n = number_prefix_arg,
109
+ register = read_register("Number to register:")|
110
+ REGISTERS[register] = n
111
+ end
112
+
113
+ define_command(:increment_register) do
114
+ |n = current_prefix_arg,
115
+ register = read_register("Increment register:")|
116
+ i = REGISTERS[register]
117
+ case i
118
+ when Integer
119
+ REGISTERS[register] = i + prefix_numeric_value(n)
120
+ when String
121
+ append_to_register(register,
122
+ Buffer.current.mark, Buffer.current.point, n)
123
+ else
124
+ raise ArgumentError, "Register doesn't contain a number or text"
125
+ end
126
+ end
127
+ end
128
+ end