textbringer 0.1.6 → 0.1.7
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.
- checksums.yaml +4 -4
- data/.editorconfig +9 -0
- data/.travis.yml +3 -0
- data/CHANGES.md +8 -0
- data/README.md +42 -8
- data/appveyor.yml +3 -0
- data/lib/textbringer.rb +2 -0
- data/lib/textbringer/buffer.rb +46 -28
- data/lib/textbringer/commands/buffers.rb +4 -8
- data/lib/textbringer/commands/clipboard.rb +8 -2
- data/lib/textbringer/commands/files.rb +26 -0
- data/lib/textbringer/commands/misc.rb +56 -14
- data/lib/textbringer/config.rb +1 -0
- data/lib/textbringer/controller.rb +34 -0
- data/lib/textbringer/modes/c_mode.rb +286 -0
- data/lib/textbringer/modes/completion_list_mode.rb +34 -0
- data/lib/textbringer/modes/programming_mode.rb +37 -0
- data/lib/textbringer/modes/ruby_mode.rb +9 -36
- data/lib/textbringer/utils.rb +17 -26
- data/lib/textbringer/version.rb +1 -1
- data/lib/textbringer/window.rb +47 -29
- data/textbringer.gemspec +2 -1
- metadata +21 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: acc4214c3d7c5b5535435505b2504db1f80d46ba
|
4
|
+
data.tar.gz: e0ad741989368ad91ce5d2b5100896297d28a059
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4adfeefebf234a5329c2bfda93f8d9ffe4a83fc029a57579fa8e72d58802aa92be479949a1243f540047ad495efa6507dbbd1d1f61cd5c27e8e3f17225160f3
|
7
|
+
data.tar.gz: 8edab0edf73fbff79c8a16b62a9863a6aa5ee5867abbc494e9bb261c2885fc5e277ac9957731e80adf8806fbcfa15a8e11096094259f0e4ebf7390ba16e3a21e
|
data/.editorconfig
ADDED
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
data/README.md
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# Textbringer
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/textbringer)
|
4
|
+
[](https://gemnasium.com/shugo/textbringer)
|
4
5
|
[](https://travis-ci.org/shugo/textbringer)
|
5
6
|
[](https://ci.appveyor.com/project/shugo31737/textbringer)
|
6
7
|
[](https://codecov.io/gh/shugo/textbringer)
|
7
8
|
|
8
|
-
Textbringer is a member of a demon race that takes on the form of
|
9
|
-
editor.
|
9
|
+
Textbringer is a member of a demon race that takes on the form of an Emacs-like
|
10
|
+
text editor.
|
10
11
|
|
11
12
|
## Demo
|
12
13
|
|
@@ -30,27 +31,60 @@ minor versions.
|
|
30
31
|
|
31
32
|
You can quit the editor by C-x C-c.
|
32
33
|
|
33
|
-
## Configuration
|
34
|
+
## Configuration
|
34
35
|
|
35
|
-
###
|
36
|
+
### Meta key
|
37
|
+
|
38
|
+
You need the following configuration of terminal emulators to use meta key.
|
39
|
+
|
40
|
+
#### xterm
|
36
41
|
|
37
42
|
Add the following line to ~/.Xresources.
|
38
43
|
|
39
44
|
XTerm*metaSendsEscape: true
|
40
45
|
|
41
|
-
|
46
|
+
#### mlterm
|
42
47
|
|
43
48
|
Add the following lines to ~/.mlterm/main.
|
44
49
|
|
45
50
|
mod_meta_key = alt
|
46
51
|
mod_meta_mode = esc
|
47
|
-
col_size_of_width_a = 1
|
48
52
|
|
49
|
-
###
|
53
|
+
### East asian ambiguous width
|
54
|
+
|
55
|
+
Add the following line to ~/.textbringer.rb to treat
|
56
|
+
[ambiguous characters](http://unicode.org/reports/tr11/#Ambiguous)
|
57
|
+
as fullwidth.
|
58
|
+
|
59
|
+
CONFIG[:east_asian_ambiguous_width] = 2
|
60
|
+
|
61
|
+
You also need a LD_PRELOAD hack or a modified locale charmap because ncursesw
|
62
|
+
uses wcwidth(3).
|
63
|
+
|
64
|
+
* https://github.com/fumiyas/wcwidth-cjk
|
65
|
+
* https://github.com/hamano/locale-eaw
|
66
|
+
|
67
|
+
xterm, mlterm and screen have their own configuration options.
|
68
|
+
|
69
|
+
#### xterm
|
70
|
+
|
71
|
+
Add the following lines to ~/.Xresources.
|
72
|
+
|
73
|
+
xterm*utf8: 1
|
74
|
+
xterm*locale: true
|
75
|
+
xterm*cjkWidth: true
|
76
|
+
|
77
|
+
#### mlterm
|
78
|
+
|
79
|
+
Add the following line to ~/.mlterm/main.
|
80
|
+
|
81
|
+
col_size_of_width_a = 2
|
82
|
+
|
83
|
+
#### screen
|
50
84
|
|
51
85
|
Add the following line to ~/.screenrc.
|
52
86
|
|
53
|
-
cjkwidth
|
87
|
+
cjkwidth on
|
54
88
|
|
55
89
|
## Development
|
56
90
|
|
data/appveyor.yml
CHANGED
@@ -4,10 +4,13 @@ install:
|
|
4
4
|
- SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
|
5
5
|
- gem install bundler --no-document
|
6
6
|
- bundle install
|
7
|
+
- ps: Invoke-WebRequest -Uri http://curl.haxx.se/ca/cacert.pem -OutFile C:\projects\textbringer\cacert.pem
|
7
8
|
build: off
|
8
9
|
test_script:
|
9
10
|
- rake
|
10
11
|
deploy: off
|
11
12
|
environment:
|
13
|
+
UPLOAD_TO_CODECOV: 1
|
14
|
+
SSL_CERT_FILE: C:\projects\textbringer\cacert.pem
|
12
15
|
matrix:
|
13
16
|
- ruby_version: "23-x64"
|
data/lib/textbringer.rb
CHANGED
@@ -19,5 +19,7 @@ require_relative "textbringer/mode"
|
|
19
19
|
require_relative "textbringer/modes/fundamental_mode"
|
20
20
|
require_relative "textbringer/modes/programming_mode"
|
21
21
|
require_relative "textbringer/modes/ruby_mode"
|
22
|
+
require_relative "textbringer/modes/c_mode"
|
22
23
|
require_relative "textbringer/modes/backtrace_mode"
|
24
|
+
require_relative "textbringer/modes/completion_list_mode"
|
23
25
|
require_relative "textbringer/controller"
|
data/lib/textbringer/buffer.rb
CHANGED
@@ -158,8 +158,20 @@ module Textbringer
|
|
158
158
|
end
|
159
159
|
|
160
160
|
def self.display_width(s)
|
161
|
-
|
162
|
-
|
161
|
+
Unicode::DisplayWidth.of(s, CONFIG[:east_asian_ambiguous_width])
|
162
|
+
end
|
163
|
+
|
164
|
+
def expand_tab(s)
|
165
|
+
# TOOD: Support multibyte characters
|
166
|
+
tw = self[:tab_width]
|
167
|
+
fmt = "A#{tw}"
|
168
|
+
s.b.gsub(/([^\t]{#{tw}})|([^\t]*)\t/n) {
|
169
|
+
[$+].pack(fmt)
|
170
|
+
}.force_encoding(Encoding::UTF_8)
|
171
|
+
end
|
172
|
+
|
173
|
+
def display_width(s)
|
174
|
+
Buffer.display_width(expand_tab(s))
|
163
175
|
end
|
164
176
|
|
165
177
|
# s might not be copied.
|
@@ -615,42 +627,22 @@ module Textbringer
|
|
615
627
|
end
|
616
628
|
|
617
629
|
def next_line(n = 1)
|
618
|
-
|
619
|
-
column = @goal_column
|
620
|
-
else
|
621
|
-
prev_point = @point
|
622
|
-
beginning_of_line
|
623
|
-
column = Buffer.display_width(substring(@point, prev_point))
|
624
|
-
end
|
630
|
+
column = get_goal_column
|
625
631
|
n.times do
|
626
632
|
end_of_line
|
627
633
|
forward_char
|
628
|
-
|
629
|
-
while !end_of_line? &&
|
630
|
-
Buffer.display_width(substring(s, @point)) < column
|
631
|
-
forward_char
|
632
|
-
end
|
634
|
+
adjust_column(column)
|
633
635
|
end
|
634
636
|
@goal_column = column
|
635
637
|
end
|
636
638
|
|
637
639
|
def previous_line(n = 1)
|
638
|
-
|
639
|
-
column = @goal_column
|
640
|
-
else
|
641
|
-
prev_point = @point
|
642
|
-
beginning_of_line
|
643
|
-
column = Buffer.display_width(substring(@point, prev_point))
|
644
|
-
end
|
640
|
+
column = get_goal_column
|
645
641
|
n.times do
|
646
642
|
beginning_of_line
|
647
643
|
backward_char
|
648
644
|
beginning_of_line
|
649
|
-
|
650
|
-
while !end_of_line? &&
|
651
|
-
Buffer.display_width(substring(s, @point)) < column
|
652
|
-
forward_char
|
653
|
-
end
|
645
|
+
adjust_column(column)
|
654
646
|
end
|
655
647
|
@goal_column = column
|
656
648
|
end
|
@@ -968,8 +960,12 @@ module Textbringer
|
|
968
960
|
end
|
969
961
|
|
970
962
|
def looking_at?(re)
|
971
|
-
|
972
|
-
|
963
|
+
if re.is_a?(Regexp)
|
964
|
+
r = Regexp.new("\\G(?:#{re.source})", re.options)
|
965
|
+
else
|
966
|
+
r = "\\G(?:#{re})"
|
967
|
+
end
|
968
|
+
byteindex(true, r, @point) == @point
|
973
969
|
end
|
974
970
|
|
975
971
|
def byteindex(forward, re, pos)
|
@@ -1300,6 +1296,28 @@ module Textbringer
|
|
1300
1296
|
end
|
1301
1297
|
end
|
1302
1298
|
|
1299
|
+
def get_goal_column
|
1300
|
+
if @goal_column
|
1301
|
+
@goal_column
|
1302
|
+
else
|
1303
|
+
prev_point = @point
|
1304
|
+
beginning_of_line
|
1305
|
+
display_width(substring(@point, prev_point))
|
1306
|
+
end
|
1307
|
+
end
|
1308
|
+
|
1309
|
+
def adjust_column(column)
|
1310
|
+
s = @point
|
1311
|
+
w = 0
|
1312
|
+
while !end_of_line? &&
|
1313
|
+
(w = display_width(substring(s, @point))) < column
|
1314
|
+
forward_char
|
1315
|
+
end
|
1316
|
+
if w > column
|
1317
|
+
backward_char
|
1318
|
+
end
|
1319
|
+
end
|
1320
|
+
|
1303
1321
|
def write_to_file(f)
|
1304
1322
|
[@contents[0...@gap_start], @contents[@gap_end..-1]].each do |s|
|
1305
1323
|
s.force_encoding(Encoding::UTF_8) unless @binary
|
@@ -55,9 +55,7 @@ module Textbringer
|
|
55
55
|
define_command(:self_insert) do |n = number_prefix_arg|
|
56
56
|
c = Controller.current.last_key
|
57
57
|
merge_undo = Controller.current.last_command == :self_insert
|
58
|
-
|
59
|
-
Buffer.current.insert(c, merge_undo)
|
60
|
-
end
|
58
|
+
Buffer.current.insert(c * n, merge_undo)
|
61
59
|
end
|
62
60
|
|
63
61
|
define_command(:quoted_insert) do |n = number_prefix_arg|
|
@@ -65,9 +63,7 @@ module Textbringer
|
|
65
63
|
if !c.is_a?(String)
|
66
64
|
raise EditorError, "Invalid key"
|
67
65
|
end
|
68
|
-
|
69
|
-
Buffer.current.insert(c)
|
70
|
-
end
|
66
|
+
Buffer.current.insert(c * n)
|
71
67
|
end
|
72
68
|
|
73
69
|
define_command(:kill_line) do
|
@@ -90,12 +86,12 @@ module Textbringer
|
|
90
86
|
|
91
87
|
define_command(:undo) do
|
92
88
|
Buffer.current.undo
|
93
|
-
message("Undo!")
|
89
|
+
message("Undo!") unless Window.echo_area.current?
|
94
90
|
end
|
95
91
|
|
96
92
|
define_command(:redo) do
|
97
93
|
Buffer.current.redo
|
98
|
-
message("Redo!")
|
94
|
+
message("Redo!") unless Window.echo_area.current?
|
99
95
|
end
|
100
96
|
end
|
101
97
|
end
|
@@ -5,7 +5,7 @@ require "clipboard"
|
|
5
5
|
module Textbringer
|
6
6
|
module Commands
|
7
7
|
CLIPBOARD_AVAILABLE =
|
8
|
-
Clipboard.implementation.name !=
|
8
|
+
Clipboard.implementation.name != "Clipboard::Linux" ||
|
9
9
|
(ENV["DISPLAY"] && system("which xclip > /dev/null 2>&1"))
|
10
10
|
|
11
11
|
if CLIPBOARD_AVAILABLE
|
@@ -14,6 +14,7 @@ module Textbringer
|
|
14
14
|
GLOBAL_MAP.define_key(?\C-k, :clipboard_kill_line)
|
15
15
|
GLOBAL_MAP.define_key("\ed", :clipboard_kill_word)
|
16
16
|
GLOBAL_MAP.define_key("\C-y", :clipboard_yank)
|
17
|
+
GLOBAL_MAP.define_key("\ey", :clipboard_yank_pop)
|
17
18
|
end
|
18
19
|
|
19
20
|
define_command(:clipboard_copy_region) do
|
@@ -37,12 +38,17 @@ module Textbringer
|
|
37
38
|
end
|
38
39
|
|
39
40
|
define_command(:clipboard_yank) do
|
40
|
-
s = Clipboard.paste.encode(Encoding::UTF_8)
|
41
|
+
s = Clipboard.paste.encode(Encoding::UTF_8).gsub(/\r\n/, "\n")
|
41
42
|
if !s.empty? && (KILL_RING.empty? || KILL_RING.current != s)
|
42
43
|
KILL_RING.push(s)
|
43
44
|
end
|
44
45
|
yank
|
45
46
|
Controller.current.this_command = :yank
|
46
47
|
end
|
48
|
+
|
49
|
+
define_command(:clipboard_yank_pop) do
|
50
|
+
yank_pop
|
51
|
+
Clipboard.copy(KILL_RING.current)
|
52
|
+
end
|
47
53
|
end
|
48
54
|
end
|
@@ -1,9 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "editorconfig"
|
4
|
+
|
3
5
|
module Textbringer
|
4
6
|
module Commands
|
5
7
|
define_command(:find_file) do
|
6
8
|
|file_name = read_file_name("Find file: ")|
|
9
|
+
config = EditorConfig.load_file(file_name)
|
7
10
|
buffer = Buffer.find_file(file_name)
|
8
11
|
if buffer.new_file?
|
9
12
|
message("New file")
|
@@ -20,6 +23,29 @@ module Textbringer
|
|
20
23
|
m.interpreter_name_pattern =~ shebang)
|
21
24
|
} || FundamentalMode
|
22
25
|
send(mode.command_name)
|
26
|
+
if config.key?("charset")
|
27
|
+
Buffer.current.file_encoding = config["charset"]
|
28
|
+
end
|
29
|
+
if config.key?("end_of_line")
|
30
|
+
Buffer.current.file_format =
|
31
|
+
case config["end_of_line"]
|
32
|
+
when "lf"
|
33
|
+
:unix
|
34
|
+
when "crlf"
|
35
|
+
:dos
|
36
|
+
when "cr"
|
37
|
+
:mac
|
38
|
+
end
|
39
|
+
end
|
40
|
+
if config.key?("indent_style")
|
41
|
+
Buffer.current[:indent_tabs_mode] = config["indent_style"] == "tab"
|
42
|
+
end
|
43
|
+
if config.key?("indent_size")
|
44
|
+
Buffer.current[:indent_level] = config["indent_size"].to_i
|
45
|
+
end
|
46
|
+
if config.key?("tab_width")
|
47
|
+
Buffer.current[:tab_width] = config["tab_width"].to_i
|
48
|
+
end
|
23
49
|
end
|
24
50
|
|
25
51
|
define_command(:save_buffer) do
|
@@ -77,15 +77,66 @@ module Textbringer
|
|
77
77
|
raise Quit
|
78
78
|
end
|
79
79
|
|
80
|
+
def update_completions(xs)
|
81
|
+
if xs.size > 1
|
82
|
+
if COMPLETION[:original_buffer].nil?
|
83
|
+
COMPLETION[:completions_window] = Window.windows[-2]
|
84
|
+
COMPLETION[:original_buffer] =
|
85
|
+
COMPLETION[:completions_window].buffer
|
86
|
+
end
|
87
|
+
completions = Buffer.find_or_new("*Completions*", undo_limit: 0)
|
88
|
+
if !completions.mode.is_a?(CompletionListMode)
|
89
|
+
completions.apply_mode(CompletionListMode)
|
90
|
+
end
|
91
|
+
completions.read_only = false
|
92
|
+
begin
|
93
|
+
completions.clear
|
94
|
+
xs.each do |x|
|
95
|
+
completions.insert(x + "\n")
|
96
|
+
end
|
97
|
+
COMPLETION[:completions_window].buffer = completions
|
98
|
+
ensure
|
99
|
+
completions.read_only = true
|
100
|
+
end
|
101
|
+
else
|
102
|
+
if COMPLETION[:original_buffer]
|
103
|
+
COMPLETION[:completions_window].buffer =
|
104
|
+
COMPLETION[:original_buffer]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
private :update_completions
|
109
|
+
|
110
|
+
def complete_minibuffer_with_string(s)
|
111
|
+
minibuffer = Buffer.minibuffer
|
112
|
+
if s.start_with?(minibuffer.to_s)
|
113
|
+
minibuffer.insert(s[minibuffer.to_s.size..-1])
|
114
|
+
else
|
115
|
+
minibuffer.delete_region(minibuffer.point_min,
|
116
|
+
minibuffer.point_max)
|
117
|
+
minibuffer.insert(s)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
private :complete_minibuffer_with_string
|
121
|
+
|
80
122
|
define_command(:complete_minibuffer) do
|
81
123
|
minibuffer = Buffer.minibuffer
|
82
124
|
completion_proc = minibuffer[:completion_proc]
|
83
125
|
if completion_proc
|
84
|
-
|
126
|
+
xs = completion_proc.call(minibuffer.to_s)
|
127
|
+
update_completions(xs)
|
128
|
+
if xs.empty?
|
129
|
+
message("No match", sit_for: 1)
|
130
|
+
return
|
131
|
+
end
|
132
|
+
y, *ys = xs
|
133
|
+
s = y.size.downto(1).lazy.map { |i|
|
134
|
+
y[0, i]
|
135
|
+
}.find { |i|
|
136
|
+
ys.all? { |j| j.start_with?(i) }
|
137
|
+
}
|
85
138
|
if s
|
86
|
-
|
87
|
-
minibuffer.point_max)
|
88
|
-
minibuffer.insert(s)
|
139
|
+
complete_minibuffer_with_string(s)
|
89
140
|
end
|
90
141
|
end
|
91
142
|
end
|
@@ -184,18 +235,11 @@ module Textbringer
|
|
184
235
|
Window.redisplay
|
185
236
|
signals = [:INT, :TERM, :KILL]
|
186
237
|
begin
|
187
|
-
|
188
|
-
opts = {}
|
189
|
-
else
|
190
|
-
opts = {pgroup: true}
|
191
|
-
end
|
238
|
+
opts = /mswin32|mingw32/ =~ RUBY_PLATFORM ? {} : {pgroup: true}
|
192
239
|
Open3.popen2e(cmd, opts) do |input, output, wait_thread|
|
193
240
|
input.close
|
194
241
|
loop do
|
195
242
|
status = output.wait_readable(0.5)
|
196
|
-
if status == false
|
197
|
-
break # EOF
|
198
|
-
end
|
199
243
|
if status
|
200
244
|
begin
|
201
245
|
s = output.read_nonblock(1024).force_encoding("utf-8").
|
@@ -228,8 +272,6 @@ module Textbringer
|
|
228
272
|
elsif status.signaled?
|
229
273
|
signame = Signal.signame(status.termsig)
|
230
274
|
message("Process #{pid} was killed by #{signame}")
|
231
|
-
else
|
232
|
-
message("Process #{pid} exited")
|
233
275
|
end
|
234
276
|
end
|
235
277
|
ensure
|