textbringer 0.1.8 → 0.1.9

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