textbringer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,277 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Textbringer
4
+ module Utils
5
+ def message(msg, log: true, sit_for: nil, sleep_for: nil)
6
+ if log && Buffer.current.name != "*Messages*"
7
+ buffer = Buffer["*Messages*"] ||
8
+ Buffer.new_buffer("*Messages*", undo_limit: 0).tap { |b|
9
+ b[:top_of_window] = b.new_mark
10
+ }
11
+ buffer.read_only = false
12
+ begin
13
+ buffer.end_of_buffer
14
+ buffer.insert(msg + "\n")
15
+ if buffer.current_line > 1000
16
+ buffer.beginning_of_buffer
17
+ 10.times do
18
+ buffer.next_line
19
+ end
20
+ buffer.delete_region(buffer.point_min, buffer.point)
21
+ buffer.end_of_buffer
22
+ end
23
+ ensure
24
+ buffer.read_only = true
25
+ end
26
+ end
27
+ Window.echo_area.show(msg)
28
+ if sit_for
29
+ sit_for(sit_for)
30
+ Window.echo_area.clear_message
31
+ end
32
+ if sleep_for
33
+ sleep_for(sleep_for)
34
+ Window.echo_area.clear_message
35
+ end
36
+ end
37
+
38
+ def sit_for(secs, no_redisplay = false)
39
+ Window.redisplay unless no_redisplay
40
+ Controller.current.wait_input((secs * 1000).to_i)
41
+ end
42
+
43
+ def sleep_for(secs)
44
+ sleep(secs)
45
+ end
46
+
47
+ def read_char
48
+ Controller.current.read_char
49
+ end
50
+
51
+ def received_keyboard_quit?
52
+ Controller.current.received_keyboard_quit?
53
+ end
54
+
55
+ def handle_exception(e)
56
+ if e.is_a?(SystemExit)
57
+ raise
58
+ end
59
+ if Buffer.current.name != "*Backtrace*"
60
+ buffer = Buffer.find_or_new("*Backtrace*", undo_limit: 0)
61
+ if !buffer.mode.is_a?(BacktraceMode)
62
+ buffer.apply_mode(BacktraceMode)
63
+ end
64
+ buffer.read_only = false
65
+ begin
66
+ buffer.delete_region(buffer.point_min, buffer.point_max)
67
+ buffer.insert("#{e.class}: #{e}\n")
68
+ e.backtrace.each do |line|
69
+ buffer.insert(line + "\n")
70
+ end
71
+ buffer.beginning_of_buffer
72
+ ensure
73
+ buffer.read_only = true
74
+ end
75
+ end
76
+ message(e.to_s.chomp)
77
+ Window.beep
78
+ end
79
+
80
+ def read_from_minibuffer(prompt, completion_proc: nil, default: nil,
81
+ keymap: MINIBUFFER_LOCAL_MAP)
82
+ if Window.echo_area.active?
83
+ raise EditorError,
84
+ "Command attempted to use minibuffer while in minibuffer"
85
+ end
86
+ old_buffer = Buffer.current
87
+ old_window = Window.current
88
+ old_completion_proc = Buffer.minibuffer[:completion_proc]
89
+ old_current_prefix_arg = Controller.current.current_prefix_arg
90
+ old_minibuffer_map = Buffer.minibuffer.keymap
91
+ Buffer.minibuffer.keymap = keymap
92
+ Buffer.minibuffer[:completion_proc] = completion_proc
93
+ Window.echo_area.active = true
94
+ begin
95
+ Buffer.minibuffer.delete_region(Buffer.minibuffer.point_min,
96
+ Buffer.minibuffer.point_max)
97
+ Window.current = Window.echo_area
98
+ if default
99
+ prompt = prompt.sub(/:/, " (default #{default}):")
100
+ end
101
+ Window.echo_area.prompt = prompt
102
+ Window.echo_area.redisplay
103
+ Window.update
104
+ recursive_edit
105
+ s = Buffer.minibuffer.to_s.chomp
106
+ if default && s.empty?
107
+ default
108
+ else
109
+ s
110
+ end
111
+ ensure
112
+ Window.echo_area.clear
113
+ Window.echo_area.redisplay
114
+ Window.update
115
+ Window.echo_area.active = false
116
+ Window.current = old_window
117
+ # Just in case old_window has been deleted by resize,
118
+ # in which case Window.current is set to the first window.
119
+ Window.current.buffer = Buffer.current = old_buffer
120
+ Buffer.minibuffer[:completion_proc] = old_completion_proc
121
+ Buffer.minibuffer.keymap = old_minibuffer_map
122
+ Controller.current.current_prefix_arg = old_current_prefix_arg
123
+ end
124
+ end
125
+
126
+ def read_file_name(prompt, default: nil)
127
+ f = ->(s) {
128
+ s = File.expand_path(s) if s.start_with?("~")
129
+ files = Dir.glob(s + "*")
130
+ if files.size > 0
131
+ x, *xs = files
132
+ file = x.size.downto(1).lazy.map { |i|
133
+ x[0, i]
134
+ }.find { |i|
135
+ xs.all? { |j| j.start_with?(i) }
136
+ }
137
+ if file && files.size == 1 &&
138
+ File.directory?(file) && !file.end_with?(?/)
139
+ file + "/"
140
+ else
141
+ file
142
+ end
143
+ else
144
+ nil
145
+ end
146
+ }
147
+ file = read_from_minibuffer(prompt, completion_proc: f, default: default)
148
+ File.expand_path(file)
149
+ end
150
+
151
+ def complete(s, candidates)
152
+ xs = candidates.select { |i| i.start_with?(s) }
153
+ if xs.size > 0
154
+ y, *ys = xs
155
+ y.size.downto(1).lazy.map { |i|
156
+ y[0, i]
157
+ }.find { |i|
158
+ ys.all? { |j| j.start_with?(i) }
159
+ }
160
+ else
161
+ nil
162
+ end
163
+ end
164
+
165
+ def read_buffer(prompt, default: (Buffer.last || Buffer.current)&.name)
166
+ f = ->(s) { complete(s, Buffer.names) }
167
+ read_from_minibuffer(prompt, completion_proc: f, default: default)
168
+ end
169
+
170
+ def read_command_name(prompt)
171
+ f = ->(s) {
172
+ complete(s.tr("-", "_"), Commands.list.map(&:to_s))
173
+ }
174
+ read_from_minibuffer(prompt, completion_proc: f)
175
+ end
176
+
177
+ def yes_or_no?(prompt)
178
+ loop {
179
+ s = read_from_minibuffer(prompt + " (yes or no) ")
180
+ case s
181
+ when "yes"
182
+ return true
183
+ when "no"
184
+ return false
185
+ else
186
+ message("Please answer yes or no.", sit_for: 2)
187
+ end
188
+ }
189
+ end
190
+
191
+ Y_OR_N_MAP = Keymap.new
192
+ Y_OR_N_MAP.define_key(?y, :self_insert_and_exit_minibuffer)
193
+ Y_OR_N_MAP.define_key(?n, :self_insert_and_exit_minibuffer)
194
+ Y_OR_N_MAP.define_key(?\C-g, :abort_recursive_edit)
195
+ Y_OR_N_MAP.handle_undefined_key do |key|
196
+ :exit_recursive_edit
197
+ end
198
+
199
+ def self_insert_and_exit_minibuffer
200
+ self_insert
201
+ exit_recursive_edit
202
+ end
203
+
204
+ def y_or_n?(prompt)
205
+ new_prompt = prompt + " (y or n) "
206
+ prompt_modified = false
207
+ loop do
208
+ s = read_from_minibuffer(new_prompt, keymap: Y_OR_N_MAP)
209
+ case s
210
+ when ?y
211
+ break true
212
+ when ?n
213
+ break false
214
+ else
215
+ unless prompt_modified
216
+ new_prompt.prepend("Answer y or n. ")
217
+ prompt_modified = true
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+ def read_single_char(prompt, chars)
224
+ map = Keymap.new
225
+ chars.each do |c|
226
+ map.define_key(c, :self_insert_and_exit_minibuffer)
227
+ end
228
+ map.define_key(?\C-g, :abort_recursive_edit)
229
+ char_options = chars.join(?/)
230
+ map.handle_undefined_key do |key|
231
+ -> { message("Invalid key. Type C-g to quit.", sit_for: 2) }
232
+ end
233
+ read_from_minibuffer(prompt + " (#{char_options}) ", keymap: map)
234
+ end
235
+
236
+ HOOKS = Hash.new { |h, k| h[k] = [] }
237
+
238
+ def add_hook(name, func)
239
+ HOOKS[name].unshift(func)
240
+ end
241
+
242
+ def remove_hook(name, func)
243
+ HOOKS[name].delete(func)
244
+ end
245
+
246
+ def run_hooks(name, remove_on_error: false)
247
+ HOOKS[name].delete_if do |func|
248
+ begin
249
+ case func
250
+ when Symbol
251
+ send(func)
252
+ else
253
+ func.call
254
+ end
255
+ false
256
+ rescue Exception => e
257
+ raise if e.is_a?(SystemExit)
258
+ if remove_on_error
259
+ true
260
+ else
261
+ raise
262
+ end
263
+ end
264
+ end
265
+ end
266
+
267
+ def set_transient_map(map)
268
+ old_overriding_map = Controller.current.overriding_map
269
+ hook = -> {
270
+ Controller.current.overriding_map = old_overriding_map
271
+ remove_hook(:pre_command_hook, hook)
272
+ }
273
+ add_hook(:pre_command_hook, hook)
274
+ Controller.current.overriding_map = map
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,3 @@
1
+ module Textbringer
2
+ VERSION = "0.1.0"
3
+ end