textbringer 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.travis.yml +1 -1
- data/CHANGES.md +6 -0
- data/README.md +1 -1
- data/lib/textbringer.rb +12 -1
- data/lib/textbringer/buffer.rb +7 -2
- data/lib/textbringer/commands.rb +0 -677
- data/lib/textbringer/commands/buffers.rb +101 -0
- data/lib/textbringer/commands/clipboard.rb +35 -0
- data/lib/textbringer/commands/ctags.rb +14 -7
- data/lib/textbringer/commands/dabbrev.rb +39 -32
- data/lib/textbringer/commands/files.rb +76 -0
- data/lib/textbringer/commands/isearch.rb +144 -0
- data/lib/textbringer/commands/misc.rb +240 -0
- data/lib/textbringer/commands/replace.rb +82 -0
- data/lib/textbringer/commands/windows.rb +72 -0
- data/lib/textbringer/controller.rb +17 -19
- data/lib/textbringer/errors.rb +6 -0
- data/lib/textbringer/modes/ruby_mode.rb +73 -53
- data/lib/textbringer/utils.rb +21 -7
- data/lib/textbringer/version.rb +1 -1
- data/lib/textbringer/window.rb +27 -9
- data/textbringer.gemspec +6 -3
- metadata +57 -8
- data/lib/textbringer/modes.rb +0 -7
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Textbringer
|
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
|
18
|
+
end
|
19
|
+
|
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
|
36
|
+
end
|
37
|
+
|
38
|
+
define_command(:set_mark_command) do
|
39
|
+
Buffer.current.set_mark
|
40
|
+
message("Mark set")
|
41
|
+
end
|
42
|
+
|
43
|
+
define_command(:goto_char) do
|
44
|
+
|n = read_from_minibuffer("Go to char: ")|
|
45
|
+
Buffer.current.goto_char(n.to_i)
|
46
|
+
Window.current.recenter_if_needed
|
47
|
+
end
|
48
|
+
|
49
|
+
define_command(:goto_line) do
|
50
|
+
|n = read_from_minibuffer("Go to line: ")|
|
51
|
+
Buffer.current.goto_line(n.to_i)
|
52
|
+
Window.current.recenter_if_needed
|
53
|
+
end
|
54
|
+
|
55
|
+
define_command(:self_insert) do |n = number_prefix_arg|
|
56
|
+
c = Controller.current.last_key
|
57
|
+
merge_undo = Controller.current.last_command == :self_insert
|
58
|
+
n.times do
|
59
|
+
Buffer.current.insert(c, merge_undo)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
define_command(:quoted_insert) do |n = number_prefix_arg|
|
64
|
+
c = Controller.current.read_char
|
65
|
+
if !c.is_a?(String)
|
66
|
+
raise EditorError, "Invalid key"
|
67
|
+
end
|
68
|
+
n.times do
|
69
|
+
Buffer.current.insert(c)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
define_command(:kill_line) do
|
74
|
+
Buffer.current.kill_line(Controller.current.last_command == :kill_region)
|
75
|
+
Controller.current.this_command = :kill_region
|
76
|
+
end
|
77
|
+
|
78
|
+
define_command(:kill_word) do
|
79
|
+
Buffer.current.kill_word(Controller.current.last_command == :kill_region)
|
80
|
+
Controller.current.this_command = :kill_region
|
81
|
+
end
|
82
|
+
|
83
|
+
define_command(:yank_pop) do
|
84
|
+
if Controller.current.last_command != :yank
|
85
|
+
raise EditorError, "Previous command was not a yank"
|
86
|
+
end
|
87
|
+
Buffer.current.yank_pop
|
88
|
+
Controller.current.this_command = :yank
|
89
|
+
end
|
90
|
+
|
91
|
+
define_command(:undo) do
|
92
|
+
Buffer.current.undo
|
93
|
+
message("Undo!")
|
94
|
+
end
|
95
|
+
|
96
|
+
define_command(:redo) do
|
97
|
+
Buffer.current.redo
|
98
|
+
message("Redo!")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "clipboard"
|
4
|
+
|
5
|
+
module Textbringer
|
6
|
+
module Commands
|
7
|
+
CLIPBOARD_AVAILABLE =
|
8
|
+
Clipboard.implementation.name != :Linux || ENV["DISPLAY"]
|
9
|
+
|
10
|
+
if CLIPBOARD_AVAILABLE
|
11
|
+
GLOBAL_MAP.define_key("\ew", :clipboard_copy_region)
|
12
|
+
GLOBAL_MAP.define_key("\C-w", :clipboard_kill_region)
|
13
|
+
GLOBAL_MAP.define_key("\C-y", :clipboard_yank)
|
14
|
+
end
|
15
|
+
|
16
|
+
define_command(:clipboard_copy_region) do
|
17
|
+
copy_region
|
18
|
+
Clipboard.copy(KILL_RING.current)
|
19
|
+
end
|
20
|
+
|
21
|
+
define_command(:clipboard_kill_region) do
|
22
|
+
kill_region
|
23
|
+
Clipboard.copy(KILL_RING.current)
|
24
|
+
end
|
25
|
+
|
26
|
+
define_command(:clipboard_yank) do
|
27
|
+
s = Clipboard.paste.encode(Encoding::UTF_8)
|
28
|
+
if KILL_RING.empty? || KILL_RING.current != s
|
29
|
+
KILL_RING.push(s)
|
30
|
+
end
|
31
|
+
yank
|
32
|
+
Controller.current.this_command = :yank
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -50,24 +50,20 @@ module Textbringer
|
|
50
50
|
CTAGS[:candidates] = candidates
|
51
51
|
index = 0
|
52
52
|
end
|
53
|
-
tag_mark_stack = CTAGS[:tag_mark_stack]
|
54
|
-
if tag_mark_stack.size == TAG_MARK_LIMIT
|
55
|
-
mark = tag_mark_stack.shift
|
56
|
-
mark.delete
|
57
|
-
end
|
58
|
-
tag_mark_stack.push(Buffer.current.new_mark)
|
59
53
|
file, addr, n = candidates[index]
|
60
|
-
find_file(file)
|
61
54
|
case addr
|
62
55
|
when /\A\d+\z/
|
56
|
+
push_tag_mark_and_find_file(file)
|
63
57
|
goto_line(addr.to_i)
|
64
58
|
when %r'\A/\^(.*)\$/\z'
|
59
|
+
push_tag_mark_and_find_file(file)
|
65
60
|
beginning_of_buffer
|
66
61
|
n.times do
|
67
62
|
re_search_forward("^" + Regexp.quote($1) + "$")
|
68
63
|
end
|
69
64
|
beginning_of_line
|
70
65
|
when %r'\A\?\^(.*)\$\?\z'
|
66
|
+
push_tag_mark_and_find_file(file)
|
71
67
|
end_of_buffer
|
72
68
|
n.times do
|
73
69
|
re_search_backward("^" + Regexp.quote($1) + "$")
|
@@ -110,5 +106,16 @@ module Textbringer
|
|
110
106
|
mark.delete
|
111
107
|
end
|
112
108
|
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def push_tag_mark_and_find_file(file)
|
113
|
+
tag_mark_stack = CTAGS[:tag_mark_stack]
|
114
|
+
if tag_mark_stack.size == TAG_MARK_LIMIT
|
115
|
+
tag_mark_stack.shift.delete
|
116
|
+
end
|
117
|
+
tag_mark_stack.push(Buffer.current.new_mark)
|
118
|
+
find_file(file)
|
119
|
+
end
|
113
120
|
end
|
114
121
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Textbringer
|
4
|
-
|
4
|
+
module DabbrevExtension
|
5
5
|
refine Buffer do
|
6
|
-
def dabbrev_expand(contd)
|
6
|
+
def dabbrev_expand(contd = false)
|
7
7
|
if contd && self[:dabbrev_stem]
|
8
8
|
buffers = self[:dabbrev_buffers]
|
9
9
|
buffer = self[:dabbrev_buffer]
|
@@ -13,43 +13,26 @@ module Textbringer
|
|
13
13
|
candidates = self[:dabbrev_candidates]
|
14
14
|
else
|
15
15
|
buffers = Buffer.to_a
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
backward_word(regexp: /[\p{Letter}\p{Number}_\-]/)
|
20
|
-
substring(point, pos)
|
21
|
-
}
|
22
|
-
if stem.empty?
|
23
|
-
raise "No possible abbreviation"
|
24
|
-
end
|
16
|
+
buffers.delete(self)
|
17
|
+
buffer = self
|
18
|
+
stem = get_stem_at_point
|
25
19
|
pos = point
|
26
20
|
direction = :backward
|
27
21
|
candidates = []
|
28
22
|
end
|
29
|
-
|
30
|
-
"(?!(?:" + candidates.map { |s|
|
31
|
-
Regexp.quote(s)
|
32
|
-
}.join("|") + ")\\b)"
|
33
|
-
re = /\b#{Regexp.quote(stem)}#{candidates_exclusion}
|
34
|
-
([\p{Letter}\p{Number}_\-]+)/x
|
23
|
+
re = dabbrev_regexp(stem, candidates)
|
35
24
|
candidate = nil
|
36
25
|
loop do
|
37
26
|
pos, candidate = buffer.dabbrev_search(re, pos, direction)
|
38
|
-
if pos
|
39
|
-
|
27
|
+
break if pos
|
28
|
+
if direction == :backward
|
29
|
+
pos = buffer.point
|
30
|
+
direction = :forward
|
40
31
|
else
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
buffer = buffers.pop
|
46
|
-
if buffer
|
47
|
-
pos = buffer.point
|
48
|
-
direction = :backward
|
49
|
-
else
|
50
|
-
break
|
51
|
-
end
|
52
|
-
end
|
32
|
+
buffer = buffers.pop
|
33
|
+
break if buffer.nil?
|
34
|
+
pos = buffer.point
|
35
|
+
direction = :backward
|
53
36
|
end
|
54
37
|
end
|
55
38
|
if !candidates.empty?
|
@@ -82,8 +65,32 @@ module Textbringer
|
|
82
65
|
end
|
83
66
|
end
|
84
67
|
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def get_stem_at_point
|
72
|
+
save_excursion {
|
73
|
+
pos = point
|
74
|
+
backward_word(regexp: /[\p{Letter}\p{Number}_\-]/)
|
75
|
+
if point == pos
|
76
|
+
raise EditorError, "No possible abbreviation"
|
77
|
+
end
|
78
|
+
substring(point, pos)
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def dabbrev_regexp(stem, candidates)
|
83
|
+
candidates_exclusion = candidates.empty? ? "" :
|
84
|
+
"(?!(?:" + candidates.map { |s|
|
85
|
+
Regexp.quote(s)
|
86
|
+
}.join("|") + ")\\b)"
|
87
|
+
/\b#{Regexp.quote(stem)}#{candidates_exclusion}
|
88
|
+
([\p{Letter}\p{Number}_\-]+)/x
|
89
|
+
end
|
85
90
|
end
|
86
|
-
|
91
|
+
end
|
92
|
+
|
93
|
+
using DabbrevExtension
|
87
94
|
|
88
95
|
module Commands
|
89
96
|
GLOBAL_MAP.define_key("\e/", :dabbrev_expand_command)
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Textbringer
|
4
|
+
module Commands
|
5
|
+
define_command(:find_file) do
|
6
|
+
|file_name = read_file_name("Find file: ")|
|
7
|
+
buffer = Buffer.find_file(file_name)
|
8
|
+
if buffer.new_file?
|
9
|
+
message("New file")
|
10
|
+
end
|
11
|
+
switch_to_buffer(buffer)
|
12
|
+
shebang = buffer.save_excursion {
|
13
|
+
buffer.beginning_of_buffer
|
14
|
+
buffer.looking_at?(/#!.*$/) ? buffer.match_string(0) : nil
|
15
|
+
}
|
16
|
+
mode = Mode.list.find { |m|
|
17
|
+
(m.file_name_pattern &&
|
18
|
+
m.file_name_pattern =~ File.basename(buffer.file_name)) ||
|
19
|
+
(m.interpreter_name_pattern &&
|
20
|
+
m.interpreter_name_pattern =~ shebang)
|
21
|
+
} || FundamentalMode
|
22
|
+
send(mode.command_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
define_command(:save_buffer) do
|
26
|
+
if Buffer.current.file_name.nil?
|
27
|
+
Buffer.current.file_name = read_file_name("File to save in: ")
|
28
|
+
next if Buffer.current.file_name.nil?
|
29
|
+
end
|
30
|
+
if Buffer.current.file_modified?
|
31
|
+
unless yes_or_no?("File changed on disk. Save anyway?")
|
32
|
+
message("Cancelled")
|
33
|
+
next
|
34
|
+
end
|
35
|
+
end
|
36
|
+
Buffer.current.save
|
37
|
+
message("Wrote #{Buffer.current.file_name}")
|
38
|
+
end
|
39
|
+
|
40
|
+
define_command(:write_file) do
|
41
|
+
|file_name = read_file_name("Write file: ")|
|
42
|
+
if File.directory?(file_name)
|
43
|
+
file_name = File.expand_path(Buffer.current.name, file_name)
|
44
|
+
end
|
45
|
+
if File.exist?(file_name)
|
46
|
+
unless y_or_n?("File `#{file_name}' exists; overwrite?")
|
47
|
+
message("Cancelled")
|
48
|
+
next
|
49
|
+
end
|
50
|
+
end
|
51
|
+
Buffer.current.save(file_name)
|
52
|
+
message("Wrote #{Buffer.current.file_name}")
|
53
|
+
end
|
54
|
+
|
55
|
+
define_command(:set_buffer_file_encoding) do
|
56
|
+
|enc = read_from_minibuffer("File encoding: ",
|
57
|
+
default: Buffer.current.file_encoding.name)|
|
58
|
+
Buffer.current.file_encoding = enc
|
59
|
+
end
|
60
|
+
|
61
|
+
define_command(:set_buffer_file_format) do
|
62
|
+
|format = read_from_minibuffer("File format: ",
|
63
|
+
default: Buffer.current.file_format.to_s)|
|
64
|
+
Buffer.current.file_format = format
|
65
|
+
end
|
66
|
+
|
67
|
+
define_command(:pwd) do
|
68
|
+
message(Dir.pwd)
|
69
|
+
end
|
70
|
+
|
71
|
+
define_command(:chdir) do
|
72
|
+
|dir_name = read_file_name("Change directory: ")|
|
73
|
+
Dir.chdir(dir_name)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Textbringer
|
4
|
+
module Commands
|
5
|
+
ISEARCH_MODE_MAP = Keymap.new
|
6
|
+
(?\x20..?\x7e).each do |c|
|
7
|
+
ISEARCH_MODE_MAP.define_key(c, :isearch_printing_char)
|
8
|
+
end
|
9
|
+
ISEARCH_MODE_MAP.define_key(?\t, :isearch_printing_char)
|
10
|
+
ISEARCH_MODE_MAP.handle_undefined_key do |key|
|
11
|
+
if key.is_a?(String) && /[\0-\x7f]/ !~ key
|
12
|
+
:isearch_printing_char
|
13
|
+
else
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
ISEARCH_MODE_MAP.define_key(:backspace, :isearch_delete_char)
|
18
|
+
ISEARCH_MODE_MAP.define_key(?\C-h, :isearch_delete_char)
|
19
|
+
ISEARCH_MODE_MAP.define_key(?\C-s, :isearch_repeat_forward)
|
20
|
+
ISEARCH_MODE_MAP.define_key(?\C-r, :isearch_repeat_backward)
|
21
|
+
ISEARCH_MODE_MAP.define_key(?\n, :isearch_exit)
|
22
|
+
ISEARCH_MODE_MAP.define_key(?\C-g, :isearch_abort)
|
23
|
+
|
24
|
+
ISEARCH_STATUS = {
|
25
|
+
forward: true,
|
26
|
+
string: "",
|
27
|
+
last_string: "",
|
28
|
+
start: 0,
|
29
|
+
last_pos: 0,
|
30
|
+
recursive_edit: false
|
31
|
+
}
|
32
|
+
|
33
|
+
define_command(:isearch_forward) do |**options|
|
34
|
+
isearch_mode(true, **options)
|
35
|
+
end
|
36
|
+
|
37
|
+
define_command(:isearch_backward) do |**options|
|
38
|
+
isearch_mode(false, **options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def isearch_mode(forward, recursive_edit: false)
|
42
|
+
ISEARCH_STATUS[:forward] = forward
|
43
|
+
ISEARCH_STATUS[:string] = String.new
|
44
|
+
ISEARCH_STATUS[:recursive_edit] = recursive_edit
|
45
|
+
Controller.current.overriding_map = ISEARCH_MODE_MAP
|
46
|
+
run_hooks(:isearch_mode_hook)
|
47
|
+
add_hook(:pre_command_hook, :isearch_pre_command_hook)
|
48
|
+
ISEARCH_STATUS[:start] = ISEARCH_STATUS[:last_pos] = Buffer.current.point
|
49
|
+
if Buffer.current != Buffer.minibuffer
|
50
|
+
message(isearch_prompt, log: false)
|
51
|
+
end
|
52
|
+
if recursive_edit
|
53
|
+
recursive_edit()
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def isearch_prompt
|
58
|
+
if ISEARCH_STATUS[:forward]
|
59
|
+
"I-search: "
|
60
|
+
else
|
61
|
+
"I-search backward: "
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def isearch_pre_command_hook
|
66
|
+
if /\Aisearch_/ !~ Controller.current.this_command
|
67
|
+
isearch_done
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def isearch_done
|
72
|
+
Buffer.current.delete_visible_mark
|
73
|
+
Controller.current.overriding_map = nil
|
74
|
+
remove_hook(:pre_command_hook, :isearch_pre_command_hook)
|
75
|
+
ISEARCH_STATUS[:last_string] = ISEARCH_STATUS[:string]
|
76
|
+
if ISEARCH_STATUS[:recursive_edit]
|
77
|
+
exit_recursive_edit
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
define_command(:isearch_exit) do
|
82
|
+
isearch_done
|
83
|
+
end
|
84
|
+
|
85
|
+
define_command(:isearch_abort) do
|
86
|
+
goto_char(Buffer.current[:isearch_start])
|
87
|
+
isearch_done
|
88
|
+
raise Quit
|
89
|
+
end
|
90
|
+
|
91
|
+
define_command(:isearch_printing_char) do
|
92
|
+
c = Controller.current.last_key
|
93
|
+
ISEARCH_STATUS[:string].concat(c)
|
94
|
+
isearch_search
|
95
|
+
end
|
96
|
+
|
97
|
+
define_command(:isearch_delete_char) do
|
98
|
+
ISEARCH_STATUS[:string].chop!
|
99
|
+
isearch_search
|
100
|
+
end
|
101
|
+
|
102
|
+
def isearch_search
|
103
|
+
forward = ISEARCH_STATUS[:forward]
|
104
|
+
options = if /\A[A-Z]/ =~ ISEARCH_STATUS[:string]
|
105
|
+
nil
|
106
|
+
else
|
107
|
+
Regexp::IGNORECASE
|
108
|
+
end
|
109
|
+
re = Regexp.new(Regexp.quote(ISEARCH_STATUS[:string]), options)
|
110
|
+
last_pos = ISEARCH_STATUS[:last_pos]
|
111
|
+
offset = forward ? last_pos : last_pos - ISEARCH_STATUS[:string].bytesize
|
112
|
+
if Buffer.current.byteindex(forward, re, offset)
|
113
|
+
if Buffer.current != Buffer.minibuffer
|
114
|
+
message(isearch_prompt + ISEARCH_STATUS[:string], log: false)
|
115
|
+
end
|
116
|
+
Buffer.current.set_visible_mark(forward ? match_beginning(0) :
|
117
|
+
match_end(0))
|
118
|
+
goto_char(forward ? match_end(0) : match_beginning(0))
|
119
|
+
else
|
120
|
+
if Buffer.current != Buffer.minibuffer
|
121
|
+
message("Failing " + isearch_prompt + ISEARCH_STATUS[:string],
|
122
|
+
log: false)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def isearch_repeat_forward
|
128
|
+
isearch_repeat(true)
|
129
|
+
end
|
130
|
+
|
131
|
+
def isearch_repeat_backward
|
132
|
+
isearch_repeat(false)
|
133
|
+
end
|
134
|
+
|
135
|
+
def isearch_repeat(forward)
|
136
|
+
ISEARCH_STATUS[:forward] = forward
|
137
|
+
ISEARCH_STATUS[:last_pos] = Buffer.current.point
|
138
|
+
if ISEARCH_STATUS[:string].empty?
|
139
|
+
ISEARCH_STATUS[:string] = ISEARCH_STATUS[:last_string]
|
140
|
+
end
|
141
|
+
isearch_search
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|