textbringer 0.1.8 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +22 -8
- data/CHANGES.md +11 -0
- data/Guardfile +1 -1
- data/exe/tbtags +26 -0
- data/exe/textbringer +1 -0
- data/lib/textbringer.rb +6 -0
- data/lib/textbringer/buffer.rb +158 -83
- data/lib/textbringer/commands.rb +18 -6
- data/lib/textbringer/commands/buffers.rb +187 -43
- data/lib/textbringer/commands/clipboard.rb +23 -6
- data/lib/textbringer/commands/ctags.rb +3 -23
- data/lib/textbringer/commands/files.rb +10 -7
- data/lib/textbringer/commands/help.rb +114 -0
- data/lib/textbringer/commands/isearch.rb +13 -7
- data/lib/textbringer/commands/keyboard_macro.rb +86 -0
- data/lib/textbringer/commands/misc.rb +43 -2
- data/lib/textbringer/commands/register.rb +128 -0
- data/lib/textbringer/commands/replace.rb +4 -3
- data/lib/textbringer/commands/windows.rb +49 -22
- data/lib/textbringer/config.rb +2 -1
- data/lib/textbringer/controller.rb +89 -23
- data/lib/textbringer/keymap.rb +60 -2
- data/lib/textbringer/modes/help_mode.rb +42 -0
- data/lib/textbringer/modes/programming_mode.rb +35 -28
- data/lib/textbringer/modes/ruby_mode.rb +28 -10
- data/lib/textbringer/plugin.rb +21 -0
- data/lib/textbringer/ring.rb +84 -0
- data/lib/textbringer/utils.rb +28 -0
- data/lib/textbringer/version.rb +1 -1
- data/lib/textbringer/window.rb +59 -5
- data/textbringer.gemspec +1 -1
- metadata +12 -4
@@ -55,9 +55,10 @@ module Textbringer
|
|
55
55
|
when ?n
|
56
56
|
# do nothing
|
57
57
|
when ?!
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
Buffer.current.composite_edit do
|
59
|
+
replace_match(to_str)
|
60
|
+
n += 1 + Buffer.current.replace_regexp_forward(regexp, to_str)
|
61
|
+
end
|
61
62
|
break
|
62
63
|
when ?q
|
63
64
|
break
|
@@ -2,63 +2,90 @@
|
|
2
2
|
|
3
3
|
module Textbringer
|
4
4
|
module Commands
|
5
|
-
define_command(:resize_window
|
5
|
+
define_command(:resize_window,
|
6
|
+
doc: "Resize windows to fit the terminal size.") do
|
6
7
|
Window.resize
|
7
8
|
end
|
8
9
|
|
9
|
-
define_command(:recenter
|
10
|
+
define_command(:recenter,
|
11
|
+
doc: "Center point in the current window.") do
|
10
12
|
Window.current.recenter
|
11
13
|
Window.redraw
|
12
14
|
end
|
13
15
|
|
14
|
-
define_command(:scroll_up
|
16
|
+
define_command(:scroll_up,
|
17
|
+
doc: "Scroll text of the current window upward.") do
|
15
18
|
Window.current.scroll_up
|
16
19
|
end
|
17
20
|
|
18
|
-
define_command(:scroll_down
|
21
|
+
define_command(:scroll_down,
|
22
|
+
doc: "Scroll text of the current window downward.") do
|
19
23
|
Window.current.scroll_down
|
20
24
|
end
|
21
25
|
|
22
|
-
define_command(:delete_window
|
26
|
+
define_command(:delete_window,
|
27
|
+
doc: "Delete the current window.") do
|
23
28
|
Window.delete_window
|
24
29
|
end
|
25
30
|
|
26
|
-
define_command(:delete_other_windows
|
31
|
+
define_command(:delete_other_windows,
|
32
|
+
doc: "Delete windows other than the current one.") do
|
27
33
|
Window.delete_other_windows
|
28
34
|
end
|
29
35
|
|
30
|
-
define_command(:split_window
|
36
|
+
define_command(:split_window,
|
37
|
+
doc: "Split the current window vertically.") do
|
31
38
|
Window.current.split
|
32
39
|
end
|
33
40
|
|
34
|
-
define_command(:other_window
|
41
|
+
define_command(:other_window,
|
42
|
+
doc: "Switch to another window.") do
|
35
43
|
Window.other_window
|
36
44
|
end
|
37
45
|
|
38
|
-
define_command(:enlarge_window) do
|
46
|
+
define_command(:enlarge_window, doc: <<~EOD) do
|
47
|
+
Make the current window n lines taller.
|
48
|
+
|
49
|
+
If n is negative, shrink the window -n lines.
|
50
|
+
See [shrink_window] for details.
|
51
|
+
EOD
|
52
|
+
|n = number_prefix_arg|
|
39
53
|
Window.current.enlarge(n)
|
40
54
|
end
|
41
55
|
|
42
|
-
define_command(:
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
56
|
+
define_command(:shrink_window, doc: <<~EOD) do |n = number_prefix_arg|
|
57
|
+
Make the current window n lines smaller.
|
58
|
+
|
59
|
+
If n is negative, enlarge the window -n lines.
|
60
|
+
See [enlarge_window] for details.
|
61
|
+
EOD
|
62
|
+
Window.current.shrink(n)
|
63
|
+
end
|
64
|
+
|
65
|
+
define_command(:shrink_window_if_larger_than_buffer, doc: <<~EOD) do
|
66
|
+
Shrink the current window if it's larger than the buffer.
|
67
|
+
EOD
|
68
|
+
Window.current.shrink_if_larger_than_buffer
|
69
|
+
end
|
70
|
+
|
71
|
+
define_command(:switch_to_buffer, doc: <<~EOD) do
|
72
|
+
Display buffer in the current window.
|
73
|
+
EOD
|
74
|
+
|buffer = read_buffer("Switch to buffer: ")|
|
75
|
+
if buffer.is_a?(String)
|
76
|
+
buffer = Buffer[buffer]
|
48
77
|
end
|
49
78
|
if buffer
|
50
79
|
Window.current.buffer = Buffer.current = buffer
|
51
80
|
else
|
52
|
-
raise EditorError, "No such buffer: #{
|
81
|
+
raise EditorError, "No such buffer: #{buffer}"
|
53
82
|
end
|
54
83
|
end
|
55
84
|
|
56
|
-
define_command(:kill_buffer) do
|
57
|
-
|
|
58
|
-
if
|
59
|
-
buffer =
|
60
|
-
else
|
61
|
-
buffer = Buffer[name]
|
85
|
+
define_command(:kill_buffer, doc: "Kill buffer.") do
|
86
|
+
|buffer = read_buffer("Kill buffer: ", default: Buffer.current.name)|
|
87
|
+
if buffer.is_a?(String)
|
88
|
+
buffer = Buffer[buffer]
|
62
89
|
end
|
63
90
|
if buffer.modified?
|
64
91
|
next unless yes_or_no?("The last change is not saved; kill anyway?")
|
data/lib/textbringer/config.rb
CHANGED
@@ -9,7 +9,8 @@ module Textbringer
|
|
9
9
|
indent_tabs_mode: false,
|
10
10
|
case_fold_search: true,
|
11
11
|
buffer_dump_dir: File.expand_path("~/.textbringer/buffer_dump"),
|
12
|
-
|
12
|
+
mark_ring_max: 16,
|
13
|
+
global_mark_ring_max: 16,
|
13
14
|
window_min_height: 4,
|
14
15
|
syntax_highlight: true,
|
15
16
|
highlight_buffer_size_limit: 102400,
|
@@ -5,9 +5,11 @@ module Textbringer
|
|
5
5
|
RECURSIVE_EDIT_TAG = Object.new
|
6
6
|
|
7
7
|
class Controller
|
8
|
+
attr_reader :this_command_keys
|
8
9
|
attr_accessor :this_command, :last_command, :overriding_map
|
9
10
|
attr_accessor :prefix_arg, :current_prefix_arg
|
10
11
|
attr_reader :key_sequence, :last_key, :recursive_edit_level
|
12
|
+
attr_reader :last_keyboard_macro
|
11
13
|
|
12
14
|
@@current = nil
|
13
15
|
|
@@ -20,15 +22,20 @@ module Textbringer
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def initialize
|
25
|
+
@top_self = eval("self", TOPLEVEL_BINDING)
|
23
26
|
@key_sequence = []
|
24
27
|
@last_key = nil
|
25
28
|
@recursive_edit_level = 0
|
29
|
+
@this_command_keys = nil
|
26
30
|
@this_command = nil
|
27
31
|
@last_command = nil
|
28
32
|
@overriding_map = nil
|
29
33
|
@prefix_arg = nil
|
30
34
|
@current_prefix_arg = nil
|
31
35
|
@echo_immediately = false
|
36
|
+
@recording_keyboard_macro = nil
|
37
|
+
@last_keyboard_macro = nil
|
38
|
+
@executing_keyboard_macro = nil
|
32
39
|
end
|
33
40
|
|
34
41
|
def command_loop(tag)
|
@@ -43,14 +50,15 @@ module Textbringer
|
|
43
50
|
@key_sequence << @last_key
|
44
51
|
cmd = key_binding(@key_sequence)
|
45
52
|
if cmd.is_a?(Symbol) || cmd.respond_to?(:call)
|
46
|
-
@key_sequence
|
53
|
+
@this_command_keys = @key_sequence
|
54
|
+
@key_sequence = []
|
47
55
|
@this_command = cmd
|
48
56
|
@current_prefix_arg = @prefix_arg
|
49
57
|
@prefix_arg = nil
|
50
58
|
begin
|
51
59
|
run_hooks(:pre_command_hook, remove_on_error: true)
|
52
60
|
if cmd.is_a?(Symbol)
|
53
|
-
send(cmd)
|
61
|
+
@top_self.send(cmd)
|
54
62
|
else
|
55
63
|
cmd.call
|
56
64
|
end
|
@@ -61,31 +69,41 @@ module Textbringer
|
|
61
69
|
end
|
62
70
|
else
|
63
71
|
if cmd.nil?
|
64
|
-
keys = @key_sequence
|
72
|
+
keys = Keymap.key_sequence_string(@key_sequence)
|
65
73
|
@key_sequence.clear
|
66
74
|
@prefix_arg = nil
|
67
75
|
message("#{keys} is undefined")
|
68
76
|
end
|
69
77
|
end
|
78
|
+
Window.redisplay
|
70
79
|
rescue Exception => e
|
71
80
|
show_exception(e)
|
72
81
|
@prefix_arg = nil
|
82
|
+
@recording_keyboard_macro = nil
|
83
|
+
Window.redisplay
|
84
|
+
if Window.echo_area.active?
|
85
|
+
wait_input(2000)
|
86
|
+
Window.echo_area.clear_message
|
87
|
+
Window.redisplay
|
88
|
+
end
|
73
89
|
end
|
74
|
-
Window.redisplay
|
75
90
|
end
|
76
91
|
end
|
77
92
|
end
|
78
93
|
|
79
94
|
def wait_input(msecs)
|
95
|
+
if executing_keyboard_macro?
|
96
|
+
return @executing_keyboard_macro.first
|
97
|
+
end
|
80
98
|
Window.current.wait_input(msecs)
|
81
99
|
end
|
82
100
|
|
83
101
|
def read_char
|
84
|
-
|
102
|
+
read_char_with_keyboard_macro(:read_char)
|
85
103
|
end
|
86
104
|
|
87
105
|
def read_char_nonblock
|
88
|
-
|
106
|
+
read_char_with_keyboard_macro(:read_char_nonblock)
|
89
107
|
end
|
90
108
|
|
91
109
|
def received_keyboard_quit?
|
@@ -108,22 +126,8 @@ module Textbringer
|
|
108
126
|
end
|
109
127
|
end
|
110
128
|
|
111
|
-
def key_name(key)
|
112
|
-
case key
|
113
|
-
when Symbol
|
114
|
-
"<#{key}>"
|
115
|
-
when "\e"
|
116
|
-
"ESC"
|
117
|
-
when "\C-m"
|
118
|
-
"RET"
|
119
|
-
when /\A[\0-\b\v-\x1f\x7f]\z/
|
120
|
-
"C-" + (key.ord ^ 0x40).chr.downcase
|
121
|
-
else
|
122
|
-
key.to_s
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
129
|
def echo_input
|
130
|
+
return if executing_keyboard_macro?
|
127
131
|
if @prefix_arg || !@key_sequence.empty?
|
128
132
|
if !@echo_immediately
|
129
133
|
return if wait_input(1000)
|
@@ -138,7 +142,7 @@ module Textbringer
|
|
138
142
|
end
|
139
143
|
if !@key_sequence.empty?
|
140
144
|
s << " " if !s.empty?
|
141
|
-
s << @key_sequence
|
145
|
+
s << Keymap.key_sequence_string(@key_sequence)
|
142
146
|
end
|
143
147
|
s << "-"
|
144
148
|
Window.echo_area.show(s)
|
@@ -150,12 +154,74 @@ module Textbringer
|
|
150
154
|
end
|
151
155
|
end
|
152
156
|
|
153
|
-
|
157
|
+
def start_keyboard_macro
|
158
|
+
if @recording_keyboard_macro
|
159
|
+
@recording_keyboard_macro = nil
|
160
|
+
raise EditorError, "Already recording keyboard macro"
|
161
|
+
end
|
162
|
+
@recording_keyboard_macro = []
|
163
|
+
end
|
164
|
+
|
165
|
+
def end_keyboard_macro
|
166
|
+
if @recording_keyboard_macro.nil?
|
167
|
+
raise EditorError, "Not recording keyboard macro"
|
168
|
+
end
|
169
|
+
if @recording_keyboard_macro.empty?
|
170
|
+
raise EditorError, "Empty keyboard macro"
|
171
|
+
end
|
172
|
+
@recording_keyboard_macro.pop(@this_command_keys.size)
|
173
|
+
@last_keyboard_macro = @recording_keyboard_macro
|
174
|
+
@recording_keyboard_macro = nil
|
175
|
+
end
|
176
|
+
|
177
|
+
def execute_keyboard_macro(macro, n = 1)
|
178
|
+
n.times do
|
179
|
+
@executing_keyboard_macro = macro.dup
|
180
|
+
begin
|
181
|
+
recursive_edit
|
182
|
+
ensure
|
183
|
+
@executing_keyboard_macro = nil
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def call_last_keyboard_macro(n)
|
189
|
+
if @last_keyboard_macro.nil?
|
190
|
+
raise EditorError, "Keyboard macro not defined"
|
191
|
+
end
|
192
|
+
execute_keyboard_macro(@last_keyboard_macro, n)
|
193
|
+
end
|
194
|
+
|
195
|
+
def recording_keyboard_macro?
|
196
|
+
!@recording_keyboard_macro.nil?
|
197
|
+
end
|
198
|
+
|
199
|
+
def executing_keyboard_macro?
|
200
|
+
!@executing_keyboard_macro.nil?
|
201
|
+
end
|
154
202
|
|
155
203
|
def key_binding(key_sequence)
|
156
204
|
@overriding_map&.lookup(key_sequence) ||
|
157
205
|
Buffer.current&.keymap&.lookup(key_sequence) ||
|
158
206
|
GLOBAL_MAP.lookup(key_sequence)
|
159
207
|
end
|
208
|
+
|
209
|
+
private
|
210
|
+
|
211
|
+
def read_char_with_keyboard_macro(read_char_method)
|
212
|
+
if !executing_keyboard_macro?
|
213
|
+
c = call_read_char_method(read_char_method)
|
214
|
+
if @recording_keyboard_macro
|
215
|
+
@recording_keyboard_macro.push(c)
|
216
|
+
end
|
217
|
+
c
|
218
|
+
else
|
219
|
+
@executing_keyboard_macro.shift
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def call_read_char_method(read_char_method)
|
224
|
+
Window.current.send(read_char_method)
|
225
|
+
end
|
160
226
|
end
|
161
227
|
end
|
data/lib/textbringer/keymap.rb
CHANGED
@@ -4,6 +4,8 @@ require "curses"
|
|
4
4
|
|
5
5
|
module Textbringer
|
6
6
|
class Keymap
|
7
|
+
include Enumerable
|
8
|
+
|
7
9
|
def initialize
|
8
10
|
@map = {}
|
9
11
|
end
|
@@ -35,10 +37,43 @@ module Textbringer
|
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
40
|
+
def each(prefixes = [], &block)
|
41
|
+
@map.each do |key, val|
|
42
|
+
if val.is_a?(Keymap)
|
43
|
+
val.each([*prefixes, key], &block)
|
44
|
+
else
|
45
|
+
yield([*prefixes, key], val)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
38
50
|
def handle_undefined_key
|
39
51
|
@map.default_proc = Proc.new { |h, k| yield(k) }
|
40
52
|
end
|
41
53
|
|
54
|
+
def self.key_name(key)
|
55
|
+
case key
|
56
|
+
when Symbol
|
57
|
+
"<#{key}>"
|
58
|
+
when " "
|
59
|
+
"SPC"
|
60
|
+
when "\t"
|
61
|
+
"TAB"
|
62
|
+
when "\e"
|
63
|
+
"ESC"
|
64
|
+
when "\C-m"
|
65
|
+
"RET"
|
66
|
+
when /\A[\0-\x1f\x7f]\z/
|
67
|
+
"C-" + (key.ord ^ 0x40).chr.downcase
|
68
|
+
else
|
69
|
+
key.to_s
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.key_sequence_string(key_sequence)
|
74
|
+
key_sequence.map { |key| key_name(key) }.join(" ")
|
75
|
+
end
|
76
|
+
|
42
77
|
private
|
43
78
|
|
44
79
|
def kbd(key)
|
@@ -85,7 +120,10 @@ module Textbringer
|
|
85
120
|
end
|
86
121
|
GLOBAL_MAP.define_key(?\t, :self_insert)
|
87
122
|
GLOBAL_MAP.define_key(?\C-q, :quoted_insert)
|
88
|
-
GLOBAL_MAP.define_key("\C
|
123
|
+
GLOBAL_MAP.define_key("\C-@", :set_mark_command)
|
124
|
+
GLOBAL_MAP.define_key("\C-x\C-@", :pop_global_mark)
|
125
|
+
GLOBAL_MAP.define_key("\e*", :next_global_mark)
|
126
|
+
GLOBAL_MAP.define_key("\e?", :previous_global_mark)
|
89
127
|
GLOBAL_MAP.define_key("\C-x\C-x", :exchange_point_and_mark)
|
90
128
|
GLOBAL_MAP.define_key("\ew", :copy_region)
|
91
129
|
GLOBAL_MAP.define_key(?\C-w, :kill_region)
|
@@ -94,10 +132,12 @@ module Textbringer
|
|
94
132
|
GLOBAL_MAP.define_key(?\C-y, :yank)
|
95
133
|
GLOBAL_MAP.define_key("\ey", :yank_pop)
|
96
134
|
GLOBAL_MAP.define_key(?\C-_, :undo)
|
97
|
-
GLOBAL_MAP.define_key("\C-x\C-_", :
|
135
|
+
GLOBAL_MAP.define_key("\C-x\C-_", :redo_command)
|
98
136
|
GLOBAL_MAP.define_key("\C-t", :transpose_chars)
|
99
137
|
GLOBAL_MAP.define_key("\C-j", :newline)
|
100
138
|
GLOBAL_MAP.define_key("\C-m", :newline)
|
139
|
+
GLOBAL_MAP.define_key("\em", :back_to_indentation)
|
140
|
+
GLOBAL_MAP.define_key("\e^", :delete_indentation)
|
101
141
|
GLOBAL_MAP.define_key("\C-l", :recenter)
|
102
142
|
GLOBAL_MAP.define_key("\C-v", :scroll_up)
|
103
143
|
GLOBAL_MAP.define_key(:npage, :scroll_up)
|
@@ -108,6 +148,7 @@ module Textbringer
|
|
108
148
|
GLOBAL_MAP.define_key("\C-x2", :split_window)
|
109
149
|
GLOBAL_MAP.define_key("\C-xo", :other_window)
|
110
150
|
GLOBAL_MAP.define_key("\C-x^", :enlarge_window)
|
151
|
+
GLOBAL_MAP.define_key("\C-x-", :shrink_window_if_larger_than_buffer)
|
111
152
|
GLOBAL_MAP.define_key("\C-x\C-c", :exit_textbringer)
|
112
153
|
GLOBAL_MAP.define_key("\C-z", :suspend_textbringer)
|
113
154
|
GLOBAL_MAP.define_key("\C-x\C-f", :find_file)
|
@@ -117,6 +158,7 @@ module Textbringer
|
|
117
158
|
GLOBAL_MAP.define_key("\C-xk", :kill_buffer)
|
118
159
|
GLOBAL_MAP.define_key("\C-x\C-mf", :set_buffer_file_encoding)
|
119
160
|
GLOBAL_MAP.define_key("\C-x\C-mn", :set_buffer_file_format)
|
161
|
+
GLOBAL_MAP.define_key("\e.", :find_tag)
|
120
162
|
GLOBAL_MAP.define_key("\ex", :execute_command)
|
121
163
|
GLOBAL_MAP.define_key("\e:", :eval_expression)
|
122
164
|
GLOBAL_MAP.define_key(?\C-u, :universal_argument)
|
@@ -125,6 +167,22 @@ module Textbringer
|
|
125
167
|
GLOBAL_MAP.define_key(?\C-r, :isearch_backward)
|
126
168
|
GLOBAL_MAP.define_key("\e%", :query_replace_regexp)
|
127
169
|
GLOBAL_MAP.define_key("\e!", :shell_execute)
|
170
|
+
GLOBAL_MAP.define_key("\C-xr ", :point_to_register)
|
171
|
+
GLOBAL_MAP.define_key("\C-xrj", :jump_to_register)
|
172
|
+
GLOBAL_MAP.define_key("\C-xrx", :copy_to_register)
|
173
|
+
GLOBAL_MAP.define_key("\C-xrs", :copy_to_register)
|
174
|
+
GLOBAL_MAP.define_key("\C-xrg", :insert_register)
|
175
|
+
GLOBAL_MAP.define_key("\C-xri", :insert_register)
|
176
|
+
GLOBAL_MAP.define_key("\C-xrn", :number_to_register)
|
177
|
+
GLOBAL_MAP.define_key("\C-xr+", :increment_register)
|
178
|
+
GLOBAL_MAP.define_key("\C-x(", :start_keyboard_macro)
|
179
|
+
GLOBAL_MAP.define_key(:f3, :start_keyboard_macro)
|
180
|
+
GLOBAL_MAP.define_key("\C-x)", :end_keyboard_macro)
|
181
|
+
GLOBAL_MAP.define_key("\C-xe", :end_and_call_keyboard_macro)
|
182
|
+
GLOBAL_MAP.define_key(:f4, :end_or_call_keyboard_macro)
|
183
|
+
GLOBAL_MAP.define_key([:f1, "b"], :describe_bindings)
|
184
|
+
GLOBAL_MAP.define_key([:f1, "f"], :describe_command)
|
185
|
+
GLOBAL_MAP.define_key([:f1, "k"], :describe_key)
|
128
186
|
GLOBAL_MAP.handle_undefined_key do |key|
|
129
187
|
if key.is_a?(String) && /[\0-\x7f]/ !~ key
|
130
188
|
:self_insert
|