textbringer 3 → 4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66be90097685aaa387bc495b1ec0480eeaa4f7015480c5358fd07268d3e14fdb
4
- data.tar.gz: f848eefb51793d46065411ef40b5c8c96f04ebb490a1919c3497e44ed7f0c574
3
+ metadata.gz: 98c29dc1578dcd0acdfe058e2465e610d9803d687ac9e37ab0e2cc2935c6cc9f
4
+ data.tar.gz: 0feb32743ea8c276cb5a30e76f9725508481ca239a0f29960b8712642477ab80
5
5
  SHA512:
6
- metadata.gz: 33afdb5cdc3c255a5910e0757393a650dce8ab09b705a58725f391a70e8c46bf7a1b1c31959bf4ed0e96b435c0ba745d3f13c353f5c869f64afa764b23551f9a
7
- data.tar.gz: 9b9cc0408ab6d79038d080ebfce6fe3c6c11fe600b88a267c0d2b75c35d12eec02e9a8d95aa82169919fdb49da13c973274b7a9a05edba1a570641167e09c094
6
+ metadata.gz: 6d16b6eb9feb3679a1297dad604da419c3afb59a963e720d8f09c546a7accda2333732b7170edfa8db8eaef18b69d65aed87a8ed58610192fd6951021d61514b
7
+ data.tar.gz: 8b532a2c38278c08677be9bfb2cee271e0af00e832326859551d623a3daff8ad5207b4350cfe7642fbfe476fb3b7532a9671994db98355fdf8e179083c81042d
@@ -7,7 +7,7 @@ jobs:
7
7
  runs-on: macos-latest
8
8
  strategy:
9
9
  matrix:
10
- ruby: [ head, 3.3 ]
10
+ ruby: [ head, 3.4 ]
11
11
  timeout-minutes: 10
12
12
  env:
13
13
  RUBYOPT: --enable-frozen-string-literal --debug-frozen-string-literal
@@ -7,7 +7,7 @@ jobs:
7
7
  runs-on: ubuntu-latest
8
8
  strategy:
9
9
  matrix:
10
- ruby: [ head, 3.3, 3.2, 3.1 ]
10
+ ruby: [ head, 3.4, 3.3, 3.2 ]
11
11
  timeout-minutes: 10
12
12
  env:
13
13
  RUBYOPT: --enable-frozen-string-literal --debug-frozen-string-literal
@@ -7,7 +7,7 @@ jobs:
7
7
  runs-on: windows-latest
8
8
  strategy:
9
9
  matrix:
10
- ruby: [ 'mingw', 'mswin', '3.3' ]
10
+ ruby: [ 'mingw', 'mswin', '3.4' ]
11
11
  timeout-minutes: 10
12
12
  env:
13
13
  RUBYOPT: --enable-frozen-string-literal --debug-frozen-string-literal
@@ -62,8 +62,6 @@ module Textbringer
62
62
  end
63
63
  }
64
64
 
65
- HAS_BYTEINDEX = String.instance_methods.include?(:byteindex)
66
- HAS_BYTESPLICE = String.instance_methods.include?(:bytesplice)
67
65
  BYTESPLICE_SUPPORTS_PARTIAL_COPY =
68
66
  begin
69
67
  (+"foo").bytesplice(0, 2, "bar", 1, 2) == "aro"
@@ -249,6 +247,7 @@ module Textbringer
249
247
  @version = 0
250
248
  @modified = false
251
249
  @mode = FundamentalMode.new(self)
250
+ @minor_modes = []
252
251
  @keymap = nil
253
252
  @attributes = {}
254
253
  @save_point_level = 0
@@ -531,14 +530,10 @@ module Textbringer
531
530
  if pos == point_min
532
531
  column = 1
533
532
  else
534
- if HAS_BYTEINDEX
535
- begin
536
- i = @contents.byterindex("\n", user_to_gap(get_pos(pos, -1)))
537
- rescue RangeError
538
- i = nil
539
- end
540
- else
541
- i = @contents.rindex("\n", user_to_gap(pos - 1))
533
+ begin
534
+ i = @contents.byterindex("\n", user_to_gap(get_pos(pos, -1)))
535
+ rescue RangeError
536
+ i = nil
542
537
  end
543
538
  if i
544
539
  i += 1
@@ -568,11 +563,7 @@ module Textbringer
568
563
  pos = point_min
569
564
  i = 1
570
565
  while i < n && pos < @contents.bytesize
571
- if HAS_BYTEINDEX
572
- pos = @contents.byteindex("\n", pos)
573
- else
574
- pos = @contents.index("\n", pos)
575
- end
566
+ pos = @contents.byteindex("\n", pos)
576
567
  break if pos.nil?
577
568
  i += 1
578
569
  pos += 1
@@ -592,7 +583,7 @@ module Textbringer
592
583
  pos = @point
593
584
  size = s.bytesize
594
585
  adjust_gap(size)
595
- splice_contents(@point, size, s.b)
586
+ @contents.bytesplice(@point, size, s.b)
596
587
  @marks.each do |m|
597
588
  if m.location > @point
598
589
  m.location += size
@@ -640,7 +631,7 @@ module Textbringer
640
631
  if n > 0
641
632
  str = substring(s, pos)
642
633
  # fill the gap with NUL to avoid invalid byte sequence in UTF-8
643
- splice_contents(@gap_end...user_to_gap(pos), "\0" * (pos - @point))
634
+ @contents.bytesplice(@gap_end...user_to_gap(pos), "\0" * (pos - @point))
644
635
  @gap_end += pos - @point
645
636
  @marks.each do |m|
646
637
  if m.location > pos
@@ -655,7 +646,7 @@ module Textbringer
655
646
  str = substring(pos, s)
656
647
  update_line_and_column(@point, pos)
657
648
  # fill the gap with NUL to avoid invalid byte sequence in UTF-8
658
- splice_contents(user_to_gap(pos)...@gap_start, "\0" * (@point - pos))
649
+ @contents.bytesplice(user_to_gap(pos)...@gap_start, "\0" * (@point - pos))
659
650
  @marks.each do |m|
660
651
  if m.location >= @point
661
652
  m.location -= @point - pos
@@ -983,7 +974,7 @@ module Textbringer
983
974
  adjust_gap
984
975
  len = e - s
985
976
  # fill the gap with NUL to avoid invalid byte sequence in UTF-8
986
- splice_contents(@gap_end, len, "\0" * len)
977
+ @contents.bytesplice(@gap_end, len, "\0" * len)
987
978
  @gap_end += len
988
979
  @marks.each do |m|
989
980
  if m.location > e
@@ -1161,72 +1152,23 @@ module Textbringer
1161
1152
  byteindex(true, r, @point) == @point
1162
1153
  end
1163
1154
 
1164
- if HAS_BYTEINDEX
1165
- def byteindex(forward, re, pos)
1166
- @match_offsets = []
1167
- method = forward ? :byteindex : :byterindex
1168
- adjust_gap(0, 0)
1169
- s = @contents.byteslice(@gap_end..-1)
1170
- unless binary?
1171
- s.force_encoding(Encoding::UTF_8)
1172
- end
1173
- i = s.send(method, re, pos)
1174
- if i
1175
- m = Regexp.last_match
1176
- (0 .. m.size - 1).each do |j|
1177
- @match_offsets.push(m.byteoffset(j))
1178
- end
1179
- i
1180
- else
1181
- nil
1182
- end
1155
+ def byteindex(forward, re, pos)
1156
+ @match_offsets = []
1157
+ method = forward ? :byteindex : :byterindex
1158
+ adjust_gap(0, 0)
1159
+ s = @contents.byteslice(@gap_end..-1)
1160
+ unless binary?
1161
+ s.force_encoding(Encoding::UTF_8)
1183
1162
  end
1184
- else
1185
- def byteindex(forward, re, pos)
1186
- @match_offsets = []
1187
- method = forward ? :index : :rindex
1188
- adjust_gap(0, 0)
1189
- s = @contents[@gap_end..-1]
1190
- if @binary
1191
- offset = pos
1192
- else
1193
- offset = s.byteslice(0, pos).force_encoding(Encoding::UTF_8).size
1194
- s.force_encoding(Encoding::UTF_8)
1195
- end
1196
- begin
1197
- i = s.send(method, re, offset)
1198
- if i
1199
- m = Regexp.last_match
1200
- if m.nil?
1201
- # A bug of rindex
1202
- @match_offsets.push([pos, pos])
1203
- pos
1204
- else
1205
- b = m.pre_match.bytesize
1206
- e = b + m.to_s.bytesize
1207
- if e <= bytesize
1208
- @match_offsets.push([b, e])
1209
- match_beg = m.begin(0)
1210
- match_str = m.to_s
1211
- (1 .. m.size - 1).each do |j|
1212
- cb, ce = m.offset(j)
1213
- if cb.nil?
1214
- @match_offsets.push([nil, nil])
1215
- else
1216
- bb = b + match_str[0, cb - match_beg].bytesize
1217
- be = b + match_str[0, ce - match_beg].bytesize
1218
- @match_offsets.push([bb, be])
1219
- end
1220
- end
1221
- b
1222
- else
1223
- nil
1224
- end
1225
- end
1226
- else
1227
- nil
1228
- end
1163
+ i = s.send(method, re, pos)
1164
+ if i
1165
+ m = Regexp.last_match
1166
+ (0 .. m.size - 1).each do |j|
1167
+ @match_offsets.push(m.byteoffset(j))
1229
1168
  end
1169
+ i
1170
+ else
1171
+ nil
1230
1172
  end
1231
1173
  end
1232
1174
 
@@ -1335,6 +1277,27 @@ module Textbringer
1335
1277
  Utils.run_hooks(mode_class.hook_name)
1336
1278
  end
1337
1279
 
1280
+ def toggle_minor_mode(mode_class)
1281
+ mode = @minor_modes.find { |mode| mode.instance_of?(mode_class) }
1282
+ if mode
1283
+ mode.disable
1284
+ @minor_modes.delete(mode)
1285
+ else
1286
+ mode = mode_class.new(self)
1287
+ @minor_modes.push(mode)
1288
+ mode.enable
1289
+ end
1290
+ end
1291
+
1292
+ def mode_names
1293
+ names = []
1294
+ names.push(mode&.name || 'None')
1295
+ @minor_modes.each do |mode|
1296
+ names.push(mode.name)
1297
+ end
1298
+ names
1299
+ end
1300
+
1338
1301
  def indent_to(column)
1339
1302
  s = if self[:indent_tabs_mode]
1340
1303
  "\t" * (column / self[:tab_width]) + " " * (column % self[:tab_width])
@@ -1503,16 +1466,6 @@ module Textbringer
1503
1466
  end
1504
1467
  end
1505
1468
 
1506
- if HAS_BYTESPLICE
1507
- def splice_contents(*args)
1508
- @contents.bytesplice(*args)
1509
- end
1510
- else
1511
- def splice_contents(*args)
1512
- @contents.[]=(*args)
1513
- end
1514
- end
1515
-
1516
1469
  def adjust_gap(min_size = 0, pos = @point)
1517
1470
  if @gap_start < pos
1518
1471
  len = user_to_gap(pos) - @gap_end
@@ -1526,9 +1479,9 @@ module Textbringer
1526
1479
  "\0" * (new_gap_end - nul_filling_start))
1527
1480
  else
1528
1481
  s = @contents.byteslice(@gap_end, len)
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)
1482
+ @contents.bytesplice(nul_filling_start...new_gap_end,
1483
+ "\0" * (new_gap_end - nul_filling_start))
1484
+ @contents.bytesplice(@gap_start...new_gap_start, s)
1532
1485
  end
1533
1486
  @gap_start = new_gap_start
1534
1487
  @gap_end = new_gap_end
@@ -1544,8 +1497,8 @@ module Textbringer
1544
1497
  "\0" * (nul_filling_end - pos))
1545
1498
  else
1546
1499
  s = @contents.byteslice(pos, len)
1547
- splice_contents(pos...nul_filling_end, "\0" * (nul_filling_end - pos))
1548
- splice_contents(new_gap_end...@gap_end, s)
1500
+ @contents.bytesplice(pos...nul_filling_end, "\0" * (nul_filling_end - pos))
1501
+ @contents.bytesplice(new_gap_end...@gap_end, s)
1549
1502
  end
1550
1503
  @gap_start = new_gap_start
1551
1504
  @gap_end = new_gap_end
@@ -1553,7 +1506,7 @@ module Textbringer
1553
1506
  if gap_size < min_size
1554
1507
  new_gap_size = GAP_SIZE + min_size
1555
1508
  extended_size = new_gap_size - gap_size
1556
- splice_contents(@gap_end, 0, "\0" * extended_size)
1509
+ @contents.bytesplice(@gap_end, 0, "\0" * extended_size)
1557
1510
  @gap_end += extended_size
1558
1511
  end
1559
1512
  end
@@ -1620,14 +1573,10 @@ module Textbringer
1620
1573
  @current_column += substring(pos, new_pos).size
1621
1574
  else
1622
1575
  @current_line += n
1623
- if HAS_BYTEINDEX
1624
- begin
1625
- i = @contents.byterindex("\n", user_to_gap(get_pos(new_pos, -1)))
1626
- rescue RangeError
1627
- i = nil
1628
- end
1629
- else
1630
- i = @contents.rindex("\n", user_to_gap(new_pos - 1))
1576
+ begin
1577
+ i = @contents.byterindex("\n", user_to_gap(get_pos(new_pos, -1)))
1578
+ rescue RangeError
1579
+ i = nil
1631
1580
  end
1632
1581
  if i
1633
1582
  i += 1
@@ -1642,14 +1591,10 @@ module Textbringer
1642
1591
  @current_column -= substring(new_pos, pos).size
1643
1592
  else
1644
1593
  @current_line -= n
1645
- if HAS_BYTEINDEX
1646
- begin
1647
- i = @contents.byterindex("\n", user_to_gap(get_pos(new_pos, - 1)))
1648
- rescue RangeError
1649
- i = nil
1650
- end
1651
- else
1652
- i = @contents.rindex("\n", user_to_gap(new_pos - 1))
1594
+ begin
1595
+ i = @contents.byterindex("\n", user_to_gap(get_pos(new_pos, - 1)))
1596
+ rescue RangeError
1597
+ i = nil
1653
1598
  end
1654
1599
  if i
1655
1600
  i += 1
@@ -171,6 +171,7 @@ module Textbringer
171
171
  c = Controller.current.last_key
172
172
  merge_undo = Controller.current.last_command == :self_insert
173
173
  Buffer.current.insert(c * n, merge_undo)
174
+ run_hooks(:post_self_insert_hook)
174
175
  end
175
176
 
176
177
  define_command(:quoted_insert,
@@ -220,6 +220,7 @@ module Textbringer
220
220
  GLOBAL_MAP.define_key("\M-c", :capitalize_word)
221
221
  GLOBAL_MAP.define_key("\C-x8\C-m", :insert_char)
222
222
  GLOBAL_MAP.define_key("\C-x=", :what_cursor_position)
223
+ GLOBAL_MAP.define_key(:ic, :overwrite_mode)
223
224
  GLOBAL_MAP.handle_undefined_key do |key|
224
225
  if key.is_a?(String) && /[\0-\x7f]/ !~ key
225
226
  :self_insert
@@ -0,0 +1,35 @@
1
+ module Textbringer
2
+ class MinorMode
3
+ extend Commands
4
+ include Commands
5
+
6
+ class << self
7
+ attr_accessor :mode_name
8
+ attr_accessor :command_name
9
+ end
10
+
11
+ def self.inherited(child)
12
+ base_name = child.name.slice(/[^:]*\z/)
13
+ child.mode_name = base_name.sub(/Mode\z/, "")
14
+ command_name = base_name.sub(/\A[A-Z]/) { |s| s.downcase }.
15
+ gsub(/(?<=[a-z])([A-Z])/) {
16
+ "_" + $1.downcase
17
+ }
18
+ command = command_name.intern
19
+ child.command_name = command
20
+ define_command(command) do
21
+ Buffer.current.toggle_minor_mode(child)
22
+ end
23
+ end
24
+
25
+ attr_reader :buffer
26
+
27
+ def initialize(buffer)
28
+ @buffer = buffer
29
+ end
30
+
31
+ def name
32
+ self.class.mode_name
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ module Textbringer
2
+ class OverwriteMode < MinorMode
3
+ self.mode_name = "Ovwrt"
4
+
5
+ POST_INSERT_HOOK = -> {
6
+ buffer = Buffer.current
7
+ s = Controller.current.last_key * number_prefix_arg
8
+ begin
9
+ buffer.delete_char(s.size)
10
+ rescue RangeError
11
+ buffer.save_excursion do
12
+ pos = buffer.point
13
+ buffer.end_of_buffer
14
+ buffer.delete_region(pos, buffer.point)
15
+ end
16
+ end
17
+ }
18
+
19
+ def enable
20
+ add_hook(:post_self_insert_hook, POST_INSERT_HOOK, local: true)
21
+ end
22
+
23
+ def disable
24
+ remove_hook(:post_self_insert_hook, POST_INSERT_HOOK, local: true)
25
+ end
26
+ end
27
+ end
@@ -352,16 +352,33 @@ module Textbringer
352
352
 
353
353
  HOOKS = Hash.new { |h, k| h[k] = [] }
354
354
 
355
- def add_hook(name, func = nil, &block)
356
- HOOKS[name].unshift(func || block)
355
+ def add_hook(name, func = nil, local: false, &block)
356
+ hooks = get_hooks(local)
357
+ return if hooks[name].include?(func)
358
+ hooks[name].unshift(func || block)
357
359
  end
358
360
 
359
- def remove_hook(name, func)
360
- HOOKS[name].delete(func)
361
+ def remove_hook(name, func, local: false)
362
+ hooks = get_hooks(local)
363
+ hooks[name].delete(func)
364
+ end
365
+
366
+ def get_hooks(local)
367
+ if local
368
+ Buffer.current[:hooks] ||= Hash.new { |h, k| h[k] = [] }
369
+ else
370
+ HOOKS
371
+ end
361
372
  end
362
373
 
363
374
  def run_hooks(name, remove_on_error: false)
364
- HOOKS[name].delete_if do |func|
375
+ hooks = Buffer.current[:hooks]
376
+ run_hooks_in(hooks, name, remove_on_error:) if hooks
377
+ run_hooks_in(HOOKS, name, remove_on_error:)
378
+ end
379
+
380
+ def run_hooks_in(hooks, name, remove_on_error: false)
381
+ hooks[name].delete_if do |func|
365
382
  begin
366
383
  case func
367
384
  when Symbol
@@ -1,3 +1,3 @@
1
1
  module Textbringer
2
- VERSION = "3"
2
+ VERSION = "4"
3
3
  end
@@ -659,7 +659,7 @@ module Textbringer
659
659
  end
660
660
  @mode_line.addstr(unicode_codepoint(c))
661
661
  @mode_line.addstr(" #{line},#{column}")
662
- @mode_line.addstr(" (#{@buffer.mode&.name || 'None'})")
662
+ @mode_line.addstr(" (#{@buffer.mode_names.join(' ')})")
663
663
  @mode_line.addstr(" " * (columns - @mode_line.curx))
664
664
  @mode_line.attrset(0)
665
665
  @mode_line.noutrefresh
data/lib/textbringer.rb CHANGED
@@ -34,6 +34,8 @@ require_relative "textbringer/modes/backtrace_mode"
34
34
  require_relative "textbringer/modes/completion_list_mode"
35
35
  require_relative "textbringer/modes/buffer_list_mode"
36
36
  require_relative "textbringer/modes/help_mode"
37
+ require_relative "textbringer/minor_mode"
38
+ require_relative "textbringer/modes/overwrite_mode"
37
39
  require_relative "textbringer/input_method"
38
40
  require_relative "textbringer/input_methods/t_code_input_method"
39
41
  require_relative "textbringer/input_methods/hiragana_input_method"
data/textbringer.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.required_ruby_version = '>= 3.1'
22
+ spec.required_ruby_version = '>= 3.2'
23
23
 
24
24
  spec.add_runtime_dependency "rdoc"
25
25
  spec.add_runtime_dependency "ostruct"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: textbringer
3
3
  version: !ruby/object:Gem::Version
4
- version: '3'
4
+ version: '4'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-04-10 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rdoc
@@ -379,6 +379,7 @@ files:
379
379
  - lib/textbringer/input_methods/t_code_input_method.rb
380
380
  - lib/textbringer/input_methods/t_code_input_method/tables.rb
381
381
  - lib/textbringer/keymap.rb
382
+ - lib/textbringer/minor_mode.rb
382
383
  - lib/textbringer/mode.rb
383
384
  - lib/textbringer/modes/backtrace_mode.rb
384
385
  - lib/textbringer/modes/buffer_list_mode.rb
@@ -386,6 +387,7 @@ files:
386
387
  - lib/textbringer/modes/completion_list_mode.rb
387
388
  - lib/textbringer/modes/fundamental_mode.rb
388
389
  - lib/textbringer/modes/help_mode.rb
390
+ - lib/textbringer/modes/overwrite_mode.rb
389
391
  - lib/textbringer/modes/programming_mode.rb
390
392
  - lib/textbringer/modes/ruby_mode.rb
391
393
  - lib/textbringer/plugin.rb
@@ -409,14 +411,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
409
411
  requirements:
410
412
  - - ">="
411
413
  - !ruby/object:Gem::Version
412
- version: '3.1'
414
+ version: '3.2'
413
415
  required_rubygems_version: !ruby/object:Gem::Requirement
414
416
  requirements:
415
417
  - - ">="
416
418
  - !ruby/object:Gem::Version
417
419
  version: '0'
418
420
  requirements: []
419
- rubygems_version: 3.6.2
421
+ rubygems_version: 3.6.7
420
422
  specification_version: 4
421
423
  summary: An Emacs-like text editor
422
424
  test_files: []