textbringer 1.0.9 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/macos.yml +1 -0
- data/.github/workflows/ubuntu.yml +2 -4
- data/.github/workflows/windows.yml +1 -0
- data/CHANGES.md +13 -0
- data/README.md +0 -1
- data/bin/merge_mazegaki_dic +2 -0
- data/exe/textbringer +5 -2
- data/lib/textbringer/buffer.rb +196 -81
- data/lib/textbringer/commands/files.rb +5 -1
- data/lib/textbringer/commands/isearch.rb +12 -1
- data/lib/textbringer/commands/misc.rb +12 -2
- data/lib/textbringer/commands/server.rb +1 -1
- data/lib/textbringer/commands/windows.rb +2 -2
- data/lib/textbringer/config.rb +1 -0
- data/lib/textbringer/controller.rb +9 -8
- data/lib/textbringer/modes/ruby_mode.rb +2 -1
- data/lib/textbringer/utils.rb +10 -3
- data/lib/textbringer/version.rb +1 -1
- data/lib/textbringer/window.rb +2 -0
- data/textbringer.gemspec +4 -4
- metadata +19 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9641c360dd5bb038429536d85e666ff3518e37413eeece13898fa1a730665193
|
4
|
+
data.tar.gz: e2e9f71f90e060e30aac0bfa273fe65349eacc633f83c7c6dda034aa160436c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 285df14ab50ac3b27e5c61a3c24503c7ce724b84a918072ada9e920c323f94c46564b329a3217950bddeee5e1c4b5e83f426ad577bb8cb6596004c2b374fd12d
|
7
|
+
data.tar.gz: 6ed66854359cdd89310f77cb79c2b14cdafe4b5c7636aee662c7d1a4103563388e183bf11b5b7aab45fd691b70fa16c5d33f66231f109c6a733d05c98f1087e2
|
data/.github/workflows/macos.yml
CHANGED
@@ -6,8 +6,9 @@ jobs:
|
|
6
6
|
test:
|
7
7
|
strategy:
|
8
8
|
matrix:
|
9
|
-
ruby: [ head, 3.0, 2.7, 2.6 ]
|
9
|
+
ruby: [ head, 3.1, 3.0, 2.7, 2.6 ]
|
10
10
|
runs-on: ubuntu-latest
|
11
|
+
timeout-minutes: 10
|
11
12
|
steps:
|
12
13
|
- uses: actions/checkout@v2
|
13
14
|
- uses: ruby/setup-ruby@v1
|
@@ -20,6 +21,3 @@ jobs:
|
|
20
21
|
bundle install
|
21
22
|
- name: Run test
|
22
23
|
run: xvfb-run bundle exec rake test
|
23
|
-
env:
|
24
|
-
UPLOAD_TO_CODECOV: 1
|
25
|
-
CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}}
|
data/CHANGES.md
CHANGED
data/README.md
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
[![ubuntu](https://github.com/shugo/textbringer/workflows/ubuntu/badge.svg)](https://github.com/shugo/textbringer/actions?query=workflow%3Aubuntu)
|
5
5
|
[![windows](https://github.com/shugo/textbringer/workflows/windows/badge.svg)](https://github.com/shugo/textbringer/actions?query=workflow%3Awindows)
|
6
6
|
[![macos](https://github.com/shugo/textbringer/workflows/macos/badge.svg)](https://github.com/shugo/textbringer/actions?query=workflow%3Amacos)
|
7
|
-
[![codecov](https://codecov.io/gh/shugo/textbringer/branch/master/graph/badge.svg)](https://codecov.io/gh/shugo/textbringer)
|
8
7
|
|
9
8
|
Textbringer is a member of a demon race that takes on the form of an Emacs-like
|
10
9
|
text editor.
|
data/bin/merge_mazegaki_dic
CHANGED
data/exe/textbringer
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require "warning"
|
4
|
+
|
5
|
+
Warning.ignore(/already initialized constant /)
|
6
|
+
Warning.ignore(/previous definition of /)
|
7
|
+
|
3
8
|
require "textbringer"
|
4
9
|
|
5
10
|
include Textbringer
|
@@ -13,8 +18,6 @@ def load_user_config(path)
|
|
13
18
|
end
|
14
19
|
end
|
15
20
|
|
16
|
-
$VERBOSE = nil
|
17
|
-
|
18
21
|
unless STDIN.tty?
|
19
22
|
STDERR.puts("textbringer: standard input is not a tty")
|
20
23
|
exit 1
|
data/lib/textbringer/buffer.rb
CHANGED
@@ -58,6 +58,8 @@ module Textbringer
|
|
58
58
|
end
|
59
59
|
}
|
60
60
|
|
61
|
+
STRING_HAS_BYTE_BASED_METHODS = String.instance_methods.include?(:bytesplice)
|
62
|
+
|
61
63
|
if !defined?(@@detect_encoding_proc)
|
62
64
|
@@detect_encoding_proc = DEFAULT_DETECT_ENCODING
|
63
65
|
|
@@ -268,7 +270,14 @@ module Textbringer
|
|
268
270
|
|
269
271
|
def file_encoding=(enc)
|
270
272
|
@file_encoding = Encoding.find(enc)
|
271
|
-
@binary =
|
273
|
+
@binary = @file_encoding == Encoding::ASCII_8BIT
|
274
|
+
if STRING_HAS_BYTE_BASED_METHODS
|
275
|
+
if @binary
|
276
|
+
@contents.force_encoding(Encoding::ASCII_8BIT)
|
277
|
+
else
|
278
|
+
@contents.force_encoding(Encoding::UTF_8)
|
279
|
+
end
|
280
|
+
end
|
272
281
|
end
|
273
282
|
|
274
283
|
def binary?
|
@@ -437,20 +446,26 @@ module Textbringer
|
|
437
446
|
end
|
438
447
|
|
439
448
|
def to_s
|
440
|
-
result =
|
441
|
-
|
449
|
+
result = @contents.byteslice(0...@gap_start) +
|
450
|
+
@contents.byteslice(@gap_end..-1)
|
451
|
+
if !@binary && !STRING_HAS_BYTE_BASED_METHODS
|
452
|
+
result.force_encoding(Encoding::UTF_8)
|
453
|
+
end
|
442
454
|
result
|
443
455
|
end
|
444
456
|
|
445
457
|
def substring(s, e)
|
446
458
|
result =
|
447
459
|
if s > @gap_start || e <= @gap_start
|
448
|
-
@contents
|
460
|
+
@contents.byteslice(user_to_gap(s)...user_to_gap(e))
|
449
461
|
else
|
450
462
|
len = @gap_start - s
|
451
|
-
@contents
|
463
|
+
@contents.byteslice(user_to_gap(s), len) +
|
464
|
+
@contents.byteslice(@gap_end, e - s - len)
|
452
465
|
end
|
453
|
-
|
466
|
+
if !@binary && !STRING_HAS_BYTE_BASED_METHODS
|
467
|
+
result.force_encoding(Encoding::UTF_8)
|
468
|
+
end
|
454
469
|
result
|
455
470
|
end
|
456
471
|
|
@@ -459,7 +474,7 @@ module Textbringer
|
|
459
474
|
@contents.byteslice(location)
|
460
475
|
else
|
461
476
|
@contents.byteslice(location + gap_size)
|
462
|
-
end
|
477
|
+
end&.force_encoding(Encoding::ASCII_8BIT)
|
463
478
|
end
|
464
479
|
|
465
480
|
def byte_before(location = @point)
|
@@ -506,11 +521,19 @@ module Textbringer
|
|
506
521
|
end
|
507
522
|
|
508
523
|
def get_line_and_column(pos)
|
509
|
-
line = 1 + @contents
|
524
|
+
line = 1 + @contents.byteslice(0...user_to_gap(pos)).count("\n")
|
510
525
|
if pos == point_min
|
511
526
|
column = 1
|
512
527
|
else
|
513
|
-
|
528
|
+
if STRING_HAS_BYTE_BASED_METHODS
|
529
|
+
begin
|
530
|
+
i = @contents.byterindex("\n", user_to_gap(get_pos(pos, -1)))
|
531
|
+
rescue RangeError
|
532
|
+
i = nil
|
533
|
+
end
|
534
|
+
else
|
535
|
+
i = @contents.rindex("\n", user_to_gap(pos - 1))
|
536
|
+
end
|
514
537
|
if i
|
515
538
|
i += 1
|
516
539
|
else
|
@@ -539,7 +562,11 @@ module Textbringer
|
|
539
562
|
pos = point_min
|
540
563
|
i = 1
|
541
564
|
while i < n && pos < @contents.bytesize
|
542
|
-
|
565
|
+
if STRING_HAS_BYTE_BASED_METHODS
|
566
|
+
pos = @contents.byteindex("\n", pos)
|
567
|
+
else
|
568
|
+
pos = @contents.index("\n", pos)
|
569
|
+
end
|
543
570
|
break if pos.nil?
|
544
571
|
i += 1
|
545
572
|
pos += 1
|
@@ -552,11 +579,14 @@ module Textbringer
|
|
552
579
|
|
553
580
|
def insert(x, merge_undo = false)
|
554
581
|
s = x.to_s
|
582
|
+
if !binary? && !s.valid_encoding?
|
583
|
+
raise EditorError, "Invalid encoding: #{s.dump}"
|
584
|
+
end
|
555
585
|
check_read_only_flag
|
556
586
|
pos = @point
|
557
587
|
size = s.bytesize
|
558
588
|
adjust_gap(size)
|
559
|
-
@
|
589
|
+
splice_contents(@point, size, STRING_HAS_BYTE_BASED_METHODS ? s : s.b)
|
560
590
|
@marks.each do |m|
|
561
591
|
if m.location > @point
|
562
592
|
m.location += size
|
@@ -604,7 +634,7 @@ module Textbringer
|
|
604
634
|
if n > 0
|
605
635
|
str = substring(s, pos)
|
606
636
|
# fill the gap with NUL to avoid invalid byte sequence in UTF-8
|
607
|
-
@
|
637
|
+
splice_contents(@gap_end...user_to_gap(pos), "\0" * (pos - @point))
|
608
638
|
@gap_end += pos - @point
|
609
639
|
@marks.each do |m|
|
610
640
|
if m.location > pos
|
@@ -619,7 +649,7 @@ module Textbringer
|
|
619
649
|
str = substring(pos, s)
|
620
650
|
update_line_and_column(@point, pos)
|
621
651
|
# fill the gap with NUL to avoid invalid byte sequence in UTF-8
|
622
|
-
|
652
|
+
splice_contents(user_to_gap(pos)...@gap_start, "\0" * (@point - pos))
|
623
653
|
@marks.each do |m|
|
624
654
|
if m.location >= @point
|
625
655
|
m.location -= @point - pos
|
@@ -941,7 +971,7 @@ module Textbringer
|
|
941
971
|
adjust_gap
|
942
972
|
len = e - s
|
943
973
|
# fill the gap with NUL to avoid invalid byte sequence in UTF-8
|
944
|
-
@
|
974
|
+
splice_contents(@gap_end, len, "\0" * len)
|
945
975
|
@gap_end += len
|
946
976
|
@marks.each do |m|
|
947
977
|
if m.location > e
|
@@ -966,6 +996,9 @@ module Textbringer
|
|
966
996
|
def clear
|
967
997
|
check_read_only_flag
|
968
998
|
@contents = +""
|
999
|
+
if binary? || !STRING_HAS_BYTE_BASED_METHODS
|
1000
|
+
@contents.force_encoding(Encoding::ASCII_8BIT)
|
1001
|
+
end
|
969
1002
|
@point = @gap_start = @gap_end = 0
|
970
1003
|
@marks.each do |m|
|
971
1004
|
m.location = 0
|
@@ -1125,49 +1158,71 @@ module Textbringer
|
|
1125
1158
|
byteindex(true, r, @point) == @point
|
1126
1159
|
end
|
1127
1160
|
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
s.
|
1138
|
-
end
|
1139
|
-
begin
|
1140
|
-
i = s.send(method, re, offset)
|
1161
|
+
if STRING_HAS_BYTE_BASED_METHODS
|
1162
|
+
def byteindex(forward, re, pos)
|
1163
|
+
@match_offsets = []
|
1164
|
+
method = forward ? :byteindex : :byterindex
|
1165
|
+
adjust_gap(0, 0)
|
1166
|
+
s = @contents.byteslice(@gap_end..-1)
|
1167
|
+
unless binary?
|
1168
|
+
s.force_encoding(Encoding::UTF_8)
|
1169
|
+
end
|
1170
|
+
i = s.send(method, re, pos)
|
1141
1171
|
if i
|
1142
1172
|
m = Regexp.last_match
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1173
|
+
(0 .. m.size - 1).each do |j|
|
1174
|
+
@match_offsets.push(m.byteoffset(j))
|
1175
|
+
end
|
1176
|
+
i
|
1177
|
+
else
|
1178
|
+
nil
|
1179
|
+
end
|
1180
|
+
end
|
1181
|
+
else
|
1182
|
+
def byteindex(forward, re, pos)
|
1183
|
+
@match_offsets = []
|
1184
|
+
method = forward ? :index : :rindex
|
1185
|
+
adjust_gap(0, 0)
|
1186
|
+
s = @contents[@gap_end..-1]
|
1187
|
+
if @binary
|
1188
|
+
offset = pos
|
1189
|
+
else
|
1190
|
+
offset = s.byteslice(0, pos).force_encoding(Encoding::UTF_8).size
|
1191
|
+
s.force_encoding(Encoding::UTF_8)
|
1192
|
+
end
|
1193
|
+
begin
|
1194
|
+
i = s.send(method, re, offset)
|
1195
|
+
if i
|
1196
|
+
m = Regexp.last_match
|
1197
|
+
if m.nil?
|
1198
|
+
# A bug of rindex
|
1199
|
+
@match_offsets.push([pos, pos])
|
1200
|
+
pos
|
1201
|
+
else
|
1202
|
+
b = m.pre_match.bytesize
|
1203
|
+
e = b + m.to_s.bytesize
|
1204
|
+
if e <= bytesize
|
1205
|
+
@match_offsets.push([b, e])
|
1206
|
+
match_beg = m.begin(0)
|
1207
|
+
match_str = m.to_s
|
1208
|
+
(1 .. m.size - 1).each do |j|
|
1209
|
+
cb, ce = m.offset(j)
|
1210
|
+
if cb.nil?
|
1211
|
+
@match_offsets.push([nil, nil])
|
1212
|
+
else
|
1213
|
+
bb = b + match_str[0, cb - match_beg].bytesize
|
1214
|
+
be = b + match_str[0, ce - match_beg].bytesize
|
1215
|
+
@match_offsets.push([bb, be])
|
1216
|
+
end
|
1162
1217
|
end
|
1218
|
+
b
|
1219
|
+
else
|
1220
|
+
nil
|
1163
1221
|
end
|
1164
|
-
b
|
1165
|
-
else
|
1166
|
-
nil
|
1167
1222
|
end
|
1223
|
+
else
|
1224
|
+
nil
|
1168
1225
|
end
|
1169
|
-
else
|
1170
|
-
nil
|
1171
1226
|
end
|
1172
1227
|
end
|
1173
1228
|
end
|
@@ -1248,7 +1303,7 @@ module Textbringer
|
|
1248
1303
|
end
|
1249
1304
|
|
1250
1305
|
def gap_filled_with_nul?
|
1251
|
-
@contents
|
1306
|
+
@contents.byteslice(@gap_start...@gap_end)&.match?(/\A\0*\z/)
|
1252
1307
|
end
|
1253
1308
|
|
1254
1309
|
def composite_edit
|
@@ -1425,40 +1480,79 @@ module Textbringer
|
|
1425
1480
|
else
|
1426
1481
|
@contents = s.encode(Encoding::UTF_8)
|
1427
1482
|
end
|
1428
|
-
|
1483
|
+
if !STRING_HAS_BYTE_BASED_METHODS
|
1484
|
+
@contents.force_encoding(Encoding::ASCII_8BIT)
|
1485
|
+
end
|
1429
1486
|
self.file_encoding = enc
|
1430
|
-
|
1431
|
-
|
1487
|
+
begin
|
1488
|
+
case @contents
|
1489
|
+
when /(?<!\r)\n/
|
1490
|
+
@file_format = :unix
|
1491
|
+
when /\r(?!\n)/
|
1492
|
+
@file_format = :mac
|
1493
|
+
@contents.gsub!(/\r/, "\n")
|
1494
|
+
when /\r\n/
|
1495
|
+
@file_format = :dos
|
1496
|
+
@contents.gsub!(/\r/, "")
|
1497
|
+
else
|
1498
|
+
@file_format = CONFIG[:default_file_format]
|
1499
|
+
end
|
1500
|
+
rescue ArgumentError
|
1432
1501
|
@file_format = :unix
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
@contents.
|
1439
|
-
|
1440
|
-
|
1502
|
+
end
|
1503
|
+
end
|
1504
|
+
|
1505
|
+
if STRING_HAS_BYTE_BASED_METHODS
|
1506
|
+
def splice_contents(*args)
|
1507
|
+
@contents.bytesplice(*args)
|
1508
|
+
end
|
1509
|
+
else
|
1510
|
+
def splice_contents(*args)
|
1511
|
+
@contents.[]=(*args)
|
1441
1512
|
end
|
1442
1513
|
end
|
1443
1514
|
|
1444
1515
|
def adjust_gap(min_size = 0, pos = @point)
|
1445
1516
|
if @gap_start < pos
|
1446
1517
|
len = user_to_gap(pos) - @gap_end
|
1447
|
-
|
1448
|
-
@gap_start
|
1449
|
-
@gap_end
|
1518
|
+
s = @contents.byteslice(@gap_end, len)
|
1519
|
+
new_gap_start = @gap_start + len
|
1520
|
+
new_gap_end = @gap_end + len
|
1521
|
+
nul_filling_start = new_gap_start > @gap_end ? new_gap_start : @gap_end
|
1522
|
+
unless @binary
|
1523
|
+
# find the character boundary
|
1524
|
+
while nul_filling_start > @gap_end &&
|
1525
|
+
@contents.byteslice(nul_filling_start)&.b&.match?(/[\x80-\xbf]/n)
|
1526
|
+
nul_filling_start -= 1
|
1527
|
+
end
|
1528
|
+
end
|
1529
|
+
splice_contents(nul_filling_start...new_gap_end,
|
1530
|
+
"\0" * (new_gap_end - nul_filling_start))
|
1531
|
+
splice_contents(@gap_start...new_gap_start, s)
|
1532
|
+
@gap_start = new_gap_start
|
1533
|
+
@gap_end = new_gap_end
|
1450
1534
|
elsif @gap_start > pos
|
1451
1535
|
len = @gap_start - pos
|
1452
|
-
|
1453
|
-
@gap_start
|
1454
|
-
@gap_end
|
1536
|
+
s = @contents.byteslice(pos, len)
|
1537
|
+
new_gap_start = @gap_start - len
|
1538
|
+
new_gap_end = @gap_end - len
|
1539
|
+
nul_filling_end = new_gap_end < @gap_start ? new_gap_end : @gap_start
|
1540
|
+
unless @binary
|
1541
|
+
# find the character boundary
|
1542
|
+
while nul_filling_end < @gap_start &&
|
1543
|
+
@contents.byteslice(nul_filling_end)&.b&.match?(/[\x80-\xbf]/n)
|
1544
|
+
nul_filling_end += 1
|
1545
|
+
end
|
1546
|
+
end
|
1547
|
+
splice_contents(pos...nul_filling_end, "\0" * (nul_filling_end - pos))
|
1548
|
+
splice_contents(new_gap_end...@gap_end, s)
|
1549
|
+
@gap_start = new_gap_start
|
1550
|
+
@gap_end = new_gap_end
|
1455
1551
|
end
|
1456
|
-
# fill the gap with NUL to avoid invalid byte sequence in UTF-8
|
1457
|
-
@contents[@gap_start...@gap_end] = "\0" * (@gap_end - @gap_start)
|
1458
1552
|
if gap_size < min_size
|
1459
1553
|
new_gap_size = GAP_SIZE + min_size
|
1460
1554
|
extended_size = new_gap_size - gap_size
|
1461
|
-
@
|
1555
|
+
splice_contents(@gap_end, 0, "\0" * extended_size)
|
1462
1556
|
@gap_end += extended_size
|
1463
1557
|
end
|
1464
1558
|
end
|
@@ -1520,12 +1614,20 @@ module Textbringer
|
|
1520
1614
|
def update_line_and_column(pos, new_pos)
|
1521
1615
|
return if @save_point_level > 0
|
1522
1616
|
if pos < new_pos
|
1523
|
-
n = @contents
|
1617
|
+
n = @contents.byteslice(user_to_gap(pos)...user_to_gap(new_pos)).count("\n")
|
1524
1618
|
if n == 0
|
1525
1619
|
@current_column += substring(pos, new_pos).size
|
1526
1620
|
else
|
1527
1621
|
@current_line += n
|
1528
|
-
|
1622
|
+
if STRING_HAS_BYTE_BASED_METHODS
|
1623
|
+
begin
|
1624
|
+
i = @contents.byterindex("\n", user_to_gap(get_pos(new_pos, -1)))
|
1625
|
+
rescue RangeError
|
1626
|
+
i = nil
|
1627
|
+
end
|
1628
|
+
else
|
1629
|
+
i = @contents.rindex("\n", user_to_gap(new_pos - 1))
|
1630
|
+
end
|
1529
1631
|
if i
|
1530
1632
|
i += 1
|
1531
1633
|
else
|
@@ -1534,12 +1636,20 @@ module Textbringer
|
|
1534
1636
|
@current_column = 1 + substring(gap_to_user(i), new_pos).size
|
1535
1637
|
end
|
1536
1638
|
elsif pos > new_pos
|
1537
|
-
n = @contents
|
1639
|
+
n = @contents.byteslice(user_to_gap(new_pos)...user_to_gap(pos)).count("\n")
|
1538
1640
|
if n == 0
|
1539
1641
|
@current_column -= substring(new_pos, pos).size
|
1540
1642
|
else
|
1541
1643
|
@current_line -= n
|
1542
|
-
|
1644
|
+
if STRING_HAS_BYTE_BASED_METHODS
|
1645
|
+
begin
|
1646
|
+
i = @contents.byterindex("\n", user_to_gap(get_pos(new_pos, - 1)))
|
1647
|
+
rescue RangeError
|
1648
|
+
i = nil
|
1649
|
+
end
|
1650
|
+
else
|
1651
|
+
i = @contents.rindex("\n", user_to_gap(new_pos - 1))
|
1652
|
+
end
|
1543
1653
|
if i
|
1544
1654
|
i += 1
|
1545
1655
|
else
|
@@ -1573,8 +1683,13 @@ module Textbringer
|
|
1573
1683
|
end
|
1574
1684
|
|
1575
1685
|
def write_to_file(f)
|
1576
|
-
[
|
1577
|
-
|
1686
|
+
[
|
1687
|
+
@contents.byteslice(0...@gap_start),
|
1688
|
+
@contents.byteslice(@gap_end..-1)
|
1689
|
+
].each do |s|
|
1690
|
+
if !STRING_HAS_BYTE_BASED_METHODS
|
1691
|
+
s.force_encoding(Encoding::UTF_8) unless @binary
|
1692
|
+
end
|
1578
1693
|
case @file_format
|
1579
1694
|
when :dos
|
1580
1695
|
s.gsub!(/\n/, "\r\n")
|
@@ -1587,15 +1702,15 @@ module Textbringer
|
|
1587
1702
|
|
1588
1703
|
def push_undo(action)
|
1589
1704
|
return if @undoing || @undo_limit == 0
|
1705
|
+
if !modified?
|
1706
|
+
action.version = @version
|
1707
|
+
end
|
1590
1708
|
if @composite_edit_level > 0
|
1591
1709
|
@composite_edit_actions.push(action)
|
1592
1710
|
else
|
1593
1711
|
if @undo_stack.size >= @undo_limit
|
1594
1712
|
@undo_stack[0, @undo_stack.size + 1 - @undo_limit] = []
|
1595
1713
|
end
|
1596
|
-
if !modified?
|
1597
|
-
action.version = @version
|
1598
|
-
end
|
1599
1714
|
@undo_stack.push(action)
|
1600
1715
|
@redo_stack.clear
|
1601
1716
|
end
|
@@ -72,7 +72,11 @@ module Textbringer
|
|
72
72
|
|
73
73
|
define_command(:save_buffer, doc: "Save the current buffer to a file.") do
|
74
74
|
if Buffer.current.file_name.nil?
|
75
|
-
|
75
|
+
file_name = read_file_name("File to save in: ")
|
76
|
+
if File.directory?(file_name)
|
77
|
+
file_name = File.expand_path(Buffer.current.name, file_name)
|
78
|
+
end
|
79
|
+
Buffer.current.file_name = file_name
|
76
80
|
next if Buffer.current.file_name.nil?
|
77
81
|
end
|
78
82
|
if Buffer.current.file_modified?
|
@@ -138,7 +138,18 @@ module Textbringer
|
|
138
138
|
end
|
139
139
|
re = Regexp.new(Regexp.quote(ISEARCH_STATUS[:string]), options)
|
140
140
|
last_pos = ISEARCH_STATUS[:last_pos]
|
141
|
-
|
141
|
+
if forward
|
142
|
+
offset = last_pos
|
143
|
+
else
|
144
|
+
Buffer.current.save_excursion do
|
145
|
+
pos = last_pos - ISEARCH_STATUS[:string].bytesize
|
146
|
+
goto_char(last_pos)
|
147
|
+
while Buffer.current.point > pos
|
148
|
+
backward_char
|
149
|
+
end
|
150
|
+
offset = Buffer.current.point
|
151
|
+
end
|
152
|
+
end
|
142
153
|
if offset >= 0 && Buffer.current.byteindex(forward, re, offset)
|
143
154
|
if Buffer.current != Buffer.minibuffer
|
144
155
|
message(isearch_prompt + ISEARCH_STATUS[:string], log: false)
|
@@ -6,7 +6,12 @@ module Textbringer
|
|
6
6
|
end
|
7
7
|
|
8
8
|
define_command(:exit_textbringer) do |status = 0|
|
9
|
-
|
9
|
+
unsaved_buffers = Buffer.filter { |buffer|
|
10
|
+
/\A\*/ !~ buffer.name && buffer.modified?
|
11
|
+
}
|
12
|
+
if !unsaved_buffers.empty?
|
13
|
+
list_buffers(unsaved_buffers)
|
14
|
+
Window.redisplay
|
10
15
|
return unless yes_or_no?("Unsaved buffers exist; exit anyway?")
|
11
16
|
end
|
12
17
|
exit(status)
|
@@ -120,6 +125,7 @@ module Textbringer
|
|
120
125
|
define_command(:complete_minibuffer) do
|
121
126
|
minibuffer = Buffer.minibuffer
|
122
127
|
completion_proc = minibuffer[:completion_proc]
|
128
|
+
ignore_case = minibuffer[:completion_ignore_case]
|
123
129
|
if completion_proc
|
124
130
|
xs = completion_proc.call(minibuffer.to_s)
|
125
131
|
update_completions(xs)
|
@@ -131,7 +137,11 @@ module Textbringer
|
|
131
137
|
s = y.size.downto(1).lazy.map { |i|
|
132
138
|
y[0, i]
|
133
139
|
}.find { |i|
|
134
|
-
|
140
|
+
i = i.downcase if ignore_case
|
141
|
+
ys.all? { |j|
|
142
|
+
j = j.downcase if ignore_case
|
143
|
+
j.start_with?(i)
|
144
|
+
}
|
135
145
|
}
|
136
146
|
if s
|
137
147
|
complete_minibuffer_with_string(s)
|
@@ -6,7 +6,7 @@ module Textbringer
|
|
6
6
|
doc: "Start Textbringer server.") do
|
7
7
|
uri = CONFIG[:server_uri] ||
|
8
8
|
"drbunix:" + File.expand_path("server.sock", "~/.textbringer")
|
9
|
-
options = CONFIG[:server_options] || {
|
9
|
+
options = { UNIXFileMode: 0600 }.merge(CONFIG[:server_options] || {})
|
10
10
|
DRb.start_service(uri, Server.new, options)
|
11
11
|
end
|
12
12
|
|
@@ -84,7 +84,7 @@ module Textbringer
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
-
define_command(:list_buffers, doc: <<~EOD) do
|
87
|
+
define_command(:list_buffers, doc: <<~EOD) do |buffers = Buffer.list|
|
88
88
|
List the existing buffers.
|
89
89
|
EOD
|
90
90
|
buffer = Buffer.find_or_new("*Buffer List*",
|
@@ -92,7 +92,7 @@ module Textbringer
|
|
92
92
|
buffer.apply_mode(BufferListMode)
|
93
93
|
buffer.read_only_edit do
|
94
94
|
buffer.clear
|
95
|
-
buffer.insert(
|
95
|
+
buffer.insert(buffers.map(&:name).join("\n"))
|
96
96
|
buffer.beginning_of_buffer
|
97
97
|
end
|
98
98
|
switch_to_buffer(buffer)
|
data/lib/textbringer/config.rb
CHANGED
@@ -55,7 +55,15 @@ module Textbringer
|
|
55
55
|
@last_key = c
|
56
56
|
@key_sequence << @last_key
|
57
57
|
cmd = key_binding(@key_sequence)
|
58
|
-
if cmd.
|
58
|
+
if cmd.nil?
|
59
|
+
keys = Keymap.key_sequence_string(@key_sequence)
|
60
|
+
@key_sequence.clear
|
61
|
+
@prefix_arg = nil
|
62
|
+
message("#{keys} is undefined")
|
63
|
+
Window.beep
|
64
|
+
elsif cmd.is_a?(Keymap)
|
65
|
+
# multi-stroke key binding?
|
66
|
+
else
|
59
67
|
@this_command_keys = @key_sequence
|
60
68
|
@key_sequence = []
|
61
69
|
@this_command = cmd
|
@@ -73,13 +81,6 @@ module Textbringer
|
|
73
81
|
@last_command = @this_command
|
74
82
|
@this_command = nil
|
75
83
|
end
|
76
|
-
else
|
77
|
-
if cmd.nil?
|
78
|
-
keys = Keymap.key_sequence_string(@key_sequence)
|
79
|
-
@key_sequence.clear
|
80
|
-
@prefix_arg = nil
|
81
|
-
message("#{keys} is undefined")
|
82
|
-
end
|
83
84
|
end
|
84
85
|
Window.redisplay
|
85
86
|
rescue Exception => e
|
@@ -276,7 +276,8 @@ module Textbringer
|
|
276
276
|
(last_event == :on_kw && /\A(and|or)\z/.match?(last_text)) ||
|
277
277
|
last_event == :on_period ||
|
278
278
|
(last_event == :on_comma && event != :on_lbrace &&
|
279
|
-
event != :on_lparen && event != :on_lbracket)
|
279
|
+
event != :on_lparen && event != :on_lbracket) ||
|
280
|
+
last_event == :on_label
|
280
281
|
indentation += @buffer[:indent_level]
|
281
282
|
end
|
282
283
|
indentation
|
data/lib/textbringer/utils.rb
CHANGED
@@ -137,7 +137,7 @@ module Textbringer
|
|
137
137
|
}
|
138
138
|
|
139
139
|
def read_from_minibuffer(prompt, completion_proc: nil, default: nil,
|
140
|
-
initial_value: nil,
|
140
|
+
initial_value: nil, completion_ignore_case: false,
|
141
141
|
keymap: MINIBUFFER_LOCAL_MAP)
|
142
142
|
if Window.echo_area.active?
|
143
143
|
raise EditorError,
|
@@ -146,10 +146,12 @@ module Textbringer
|
|
146
146
|
old_buffer = Buffer.current
|
147
147
|
old_window = Window.current
|
148
148
|
old_completion_proc = Buffer.minibuffer[:completion_proc]
|
149
|
+
old_completion_ignore_case = Buffer.minibuffer[:completion_ignore_case]
|
149
150
|
old_current_prefix_arg = Controller.current.current_prefix_arg
|
150
151
|
old_minibuffer_map = Buffer.minibuffer.keymap
|
151
152
|
Buffer.minibuffer.keymap = keymap
|
152
153
|
Buffer.minibuffer[:completion_proc] = completion_proc
|
154
|
+
Buffer.minibuffer[:completion_ignore_case] = completion_ignore_case
|
153
155
|
Window.echo_area.active = true
|
154
156
|
begin
|
155
157
|
Window.current = Window.echo_area
|
@@ -177,6 +179,7 @@ module Textbringer
|
|
177
179
|
# Just in case old_window has been deleted by resize,
|
178
180
|
# in which case Window.current is set to the first window.
|
179
181
|
Window.current.buffer = Buffer.current = old_buffer
|
182
|
+
Buffer.minibuffer[:completion_ignore_case] = old_completion_ignore_case
|
180
183
|
Buffer.minibuffer[:completion_proc] = old_completion_proc
|
181
184
|
Buffer.minibuffer.keymap = old_minibuffer_map
|
182
185
|
Buffer.minibuffer.disable_input_method
|
@@ -201,8 +204,10 @@ module Textbringer
|
|
201
204
|
}
|
202
205
|
}
|
203
206
|
initial_value = default&.sub(%r"\A#{Regexp.quote(Dir.pwd)}/", "")
|
207
|
+
ignore_case = CONFIG[:read_file_name_completion_ignore_case]
|
204
208
|
file = read_from_minibuffer(prompt, completion_proc: f,
|
205
|
-
initial_value: initial_value
|
209
|
+
initial_value: initial_value,
|
210
|
+
completion_ignore_case: ignore_case)
|
206
211
|
File.expand_path(file)
|
207
212
|
end
|
208
213
|
|
@@ -223,8 +228,10 @@ module Textbringer
|
|
223
228
|
end
|
224
229
|
|
225
230
|
def read_encoding(prompt, **opts)
|
231
|
+
encoding_names = (Encoding.list.map(&:name) + Encoding.aliases.keys).
|
232
|
+
map(&:downcase).uniq
|
226
233
|
f = ->(s) {
|
227
|
-
complete_for_minibuffer(s.
|
234
|
+
complete_for_minibuffer(s.downcase, encoding_names)
|
228
235
|
}
|
229
236
|
read_from_minibuffer(prompt, completion_proc: f, **opts)
|
230
237
|
end
|
data/lib/textbringer/version.rb
CHANGED
data/lib/textbringer/window.rb
CHANGED
data/textbringer.gemspec
CHANGED
@@ -22,15 +22,15 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.required_ruby_version = '>= 2.6'
|
23
23
|
|
24
24
|
spec.add_runtime_dependency "curses", ">= 1.2.7"
|
25
|
-
spec.add_runtime_dependency "unicode-display_width", "
|
26
|
-
spec.add_runtime_dependency "clipboard", "
|
25
|
+
spec.add_runtime_dependency "unicode-display_width", ">= 1.1"
|
26
|
+
spec.add_runtime_dependency "clipboard", ">= 1.1"
|
27
27
|
spec.add_runtime_dependency "fiddley", ">= 0.0.5"
|
28
28
|
spec.add_runtime_dependency "editorconfig"
|
29
|
+
spec.add_runtime_dependency "warning"
|
29
30
|
|
30
31
|
spec.add_development_dependency "bundler"
|
31
|
-
spec.add_development_dependency "rake", "
|
32
|
+
spec.add_development_dependency "rake", ">= 12.0"
|
32
33
|
spec.add_development_dependency "test-unit"
|
33
34
|
spec.add_development_dependency "simplecov"
|
34
|
-
spec.add_development_dependency "codecov"
|
35
35
|
spec.add_development_dependency "ripper-tags"
|
36
36
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: textbringer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shugo Maeda
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-09-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: curses
|
@@ -28,28 +28,28 @@ dependencies:
|
|
28
28
|
name: unicode-display_width
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '1.1'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.1'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: clipboard
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '1.1'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.1'
|
55
55
|
- !ruby/object:Gem::Dependency
|
@@ -81,13 +81,13 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: warning
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '0'
|
90
|
-
type: :
|
90
|
+
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
@@ -95,35 +95,35 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: bundler
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
110
|
+
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: rake
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - ">="
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
117
|
+
version: '12.0'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
124
|
+
version: '12.0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: test-unit
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - ">="
|
@@ -137,7 +137,7 @@ dependencies:
|
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
140
|
+
name: simplecov
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
143
|
- - ">="
|
@@ -258,7 +258,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
258
258
|
- !ruby/object:Gem::Version
|
259
259
|
version: '0'
|
260
260
|
requirements: []
|
261
|
-
rubygems_version: 3.
|
261
|
+
rubygems_version: 3.4.0.dev
|
262
262
|
signing_key:
|
263
263
|
specification_version: 4
|
264
264
|
summary: An Emacs-like text editor
|