textbringer 0.1.8 → 0.1.9

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
  SHA1:
3
- metadata.gz: b6dafe6a94443fe1e79c7a9316973fc43ec01525
4
- data.tar.gz: 8332b5d2ef6dd7cbb4df030b9e2ce79a4551be83
3
+ metadata.gz: 68977203d454a58e4191f2b74aba7239793f01ed
4
+ data.tar.gz: fc172a9d8466197a4cd2383d1fd25a04b5f31056
5
5
  SHA512:
6
- metadata.gz: 5d7687449b608e273a1a0a11155e80e07543095e0bf73deab13740767166274590803d28e203a5816a6526d44a25bf49ac36855deecc1833a007b4a84525b933
7
- data.tar.gz: 4cf790621ce7c55960cc7e0087fb6dac140280d60adf20e2e0b9b88f69eef252226cf9bdbf975614bdf56658cbc90de074f568c4a9775fb0c983206fa5951293
6
+ metadata.gz: 428028614b59f7c75b9740ce2567f7f40887e2df3b13f4a860416c0b742ffcc41c117011a77d5b31f9138bb79f79e8b5142c288f72d803ee676881ceaf1b4eeb
7
+ data.tar.gz: 5da641c9f7a42d4535a3f3889200fb97c83987f8223fba81a2c7d5e7324649804281c4965dcefce897762ba7dac400a406d93f483601ec9745f201fafe5e5ded
data/.travis.yml CHANGED
@@ -1,13 +1,27 @@
1
- sudo: false
2
- os:
3
- - linux
4
- - osx
5
1
  language: ruby
6
- rvm:
7
- - 2.3.3
8
- - 2.4.0
9
- - ruby-head
10
2
  matrix:
3
+ include:
4
+ - os: linux
5
+ dist: trusty
6
+ sudo: false
7
+ rvm: 2.3.3
8
+ - os: linux
9
+ dist: trusty
10
+ sudo: false
11
+ rvm: 2.4.0
12
+ - os: linux
13
+ dist: trusty
14
+ sudo: false
15
+ rvm: ruby-head
16
+ - os: osx
17
+ osx_image: xcode8.2
18
+ rvm: 2.3.3
19
+ - os: osx
20
+ osx_image: xcode8.2
21
+ rvm: 2.4.0
22
+ - os: osx
23
+ osx_image: xcode8.2
24
+ rvm: ruby-head
11
25
  allow_failures:
12
26
  - os: osx
13
27
  - rvm: ruby-head
data/CHANGES.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## 0.1.9
2
+
3
+ * Support registers.
4
+ * Support plugins.
5
+ * Support global mark ring.
6
+ * Support keyboard macro.
7
+ * Support help (describe_bindings, describe_command, and describe_key).
8
+ * Add tbtags.
9
+ * Add the commands back_to_indentation, indent_region, delete_indentation,
10
+ shrink_window, and shrink_window_if_larger_than_buffer.
11
+
1
12
  ## 0.1.8
2
13
 
3
14
  * Support syntax highlighting.
data/Guardfile CHANGED
@@ -17,6 +17,6 @@
17
17
 
18
18
  guard :shell do
19
19
  watch(%r'^(lib|test)/.+\.rb$') do
20
- `ripper-tags -R`
20
+ `bundle exec tbtags -R`
21
21
  end
22
22
  end
data/exe/tbtags ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "ripper-tags"
4
+
5
+ module Textbringer
6
+ module TagParser
7
+ def on_method_add_arg(call, args)
8
+ call_name = call&.slice(0)
9
+ first_arg = args&.slice(0) == :args && args[1]
10
+ if call_name == :fcall && first_arg && call[1][0] == "define_command"
11
+ [:def, args[1][0], call[1][1]]
12
+ else
13
+ super(call, args)
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ RipperTags::Parser.prepend(Textbringer::TagParser)
20
+
21
+ begin
22
+ RipperTags.process_args(ARGV)
23
+ rescue => e
24
+ STDERR.printf("%s: %s\n", File.basename($0), e)
25
+ exit(1)
26
+ end
data/exe/textbringer CHANGED
@@ -18,6 +18,7 @@ $VERBOSE = nil
18
18
  Controller.current = Controller.new
19
19
  Window.start do
20
20
  begin
21
+ Plugin.load_plugins
21
22
  load_user_config
22
23
  ruby_mode
23
24
  if ARGV.size > 0
data/lib/textbringer.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require_relative "textbringer/version"
2
2
  require_relative "textbringer/config"
3
3
  require_relative "textbringer/errors"
4
+ require_relative "textbringer/ring"
4
5
  require_relative "textbringer/buffer"
5
6
  require_relative "textbringer/window"
6
7
  require_relative "textbringer/keymap"
@@ -17,6 +18,9 @@ require_relative "textbringer/commands/replace"
17
18
  require_relative "textbringer/commands/dabbrev"
18
19
  require_relative "textbringer/commands/ctags"
19
20
  require_relative "textbringer/commands/clipboard"
21
+ require_relative "textbringer/commands/register"
22
+ require_relative "textbringer/commands/keyboard_macro"
23
+ require_relative "textbringer/commands/help"
20
24
  require_relative "textbringer/mode"
21
25
  require_relative "textbringer/modes/fundamental_mode"
22
26
  require_relative "textbringer/modes/programming_mode"
@@ -24,4 +28,6 @@ require_relative "textbringer/modes/ruby_mode"
24
28
  require_relative "textbringer/modes/c_mode"
25
29
  require_relative "textbringer/modes/backtrace_mode"
26
30
  require_relative "textbringer/modes/completion_list_mode"
31
+ require_relative "textbringer/modes/help_mode"
32
+ require_relative "textbringer/plugin"
27
33
  require_relative "textbringer/controller"
@@ -52,6 +52,7 @@ module Textbringer
52
52
  @@list = []
53
53
  @@current = nil
54
54
  @@minibuffer = nil
55
+ @@global_mark_ring = nil
55
56
 
56
57
  def self.auto_detect_encodings
57
58
  @@auto_detect_encodings
@@ -90,6 +91,10 @@ module Textbringer
90
91
  @@minibuffer ||= Buffer.new(name: "*Minibuffer*")
91
92
  end
92
93
 
94
+ def self.global_mark_ring
95
+ @@global_mark_ring ||= Ring.new(CONFIG[:global_mark_ring_max])
96
+ end
97
+
93
98
  def self.last
94
99
  if @@list.last == @@current
95
100
  @@list[-2]
@@ -210,13 +215,16 @@ module Textbringer
210
215
  @gap_end = 0
211
216
  @marks = []
212
217
  @mark = nil
218
+ @mark_ring = Ring.new(CONFIG[:mark_ring_max],
219
+ on_delete: ->(mark) { mark.delete })
213
220
  @current_line = 1
214
221
  @current_column = 1 # One-based character count
215
222
  @goal_column = nil # Zero-based display width count
216
- @yank_start = new_mark
217
223
  @undo_stack = []
218
224
  @redo_stack = []
219
225
  @undoing = false
226
+ @composite_edit_level = 0
227
+ @composite_edit_actions = []
220
228
  @version = 0
221
229
  @modified = false
222
230
  @mode = FundamentalMode.new(self)
@@ -283,12 +291,24 @@ module Textbringer
283
291
  end
284
292
  end
285
293
 
294
+ def read_only_edit
295
+ self.read_only = false
296
+ begin
297
+ yield
298
+ ensure
299
+ self.read_only = true
300
+ end
301
+ end
302
+
286
303
  def kill
287
304
  @@table.delete(@name)
288
305
  @@list.delete(self)
289
306
  if @@current == self
290
307
  @@current = nil
291
308
  end
309
+ @marks.each do |mark|
310
+ mark.detach
311
+ end
292
312
  end
293
313
 
294
314
  def current?
@@ -767,8 +787,63 @@ module Textbringer
767
787
  end
768
788
 
769
789
  def set_mark(pos = @point)
770
- @mark ||= new_mark
790
+ if @mark
791
+ @mark.location = pos
792
+ else
793
+ push_mark(pos)
794
+ end
795
+ end
796
+
797
+ # Set mark at pos, and push the mark on the mark ring.
798
+ # Unlike Emacs, the new mark is pushed on the mark ring instead of
799
+ # the old one.
800
+ def push_mark(pos = @point)
801
+ @mark = new_mark
771
802
  @mark.location = pos
803
+ @mark_ring.push(@mark)
804
+ if self != Buffer.minibuffer
805
+ global_mark_ring = Buffer.global_mark_ring
806
+ if global_mark_ring.empty? || global_mark_ring.current.buffer != self
807
+ push_global_mark(pos)
808
+ end
809
+ end
810
+ end
811
+
812
+ def on_global_mark_ring?
813
+ mark_ring = Buffer.global_mark_ring
814
+ if mark_ring.empty?
815
+ return false
816
+ end
817
+ current = mark_ring.current
818
+ if current&.buffer == self
819
+ return true
820
+ end
821
+ next_mark = mark_ring[-1]
822
+ if next_mark&.buffer == self
823
+ return true
824
+ end
825
+ false
826
+ end
827
+
828
+ def push_global_mark(pos = @point, force: false)
829
+ if force || !on_global_mark_ring?
830
+ mark = new_mark
831
+ mark.location = pos
832
+ Buffer.global_mark_ring.push(mark)
833
+ true
834
+ else
835
+ false
836
+ end
837
+ end
838
+
839
+ def pop_mark
840
+ return if @mark_ring.empty?
841
+ @mark = @mark_ring.rotate(1)
842
+ end
843
+
844
+ def pop_to_mark
845
+ goto_char(mark)
846
+ pop_mark
772
847
  end
773
848
 
774
849
  def set_visible_mark(pos = @point)
@@ -783,8 +858,17 @@ module Textbringer
783
858
  end
784
859
  end
785
860
 
861
+ def self.region_boundaries(s, e)
862
+ if s > e
863
+ [e, s]
864
+ else
865
+ [s, e]
866
+ end
867
+ end
868
+
786
869
  def copy_region(s = @point, e = mark, append = false)
787
- str = s <= e ? substring(s, e) : substring(e, s)
870
+ s, e = Buffer.region_boundaries(s, e)
871
+ str = substring(s, e)
788
872
  if append && !KILL_RING.empty?
789
873
  KILL_RING.current.concat(str)
790
874
  else
@@ -800,9 +884,7 @@ module Textbringer
800
884
  def delete_region(s = @point, e = mark)
801
885
  check_read_only_flag
802
886
  old_pos = @point
803
- if s > e
804
- s, e = e, s
805
- end
887
+ s, e = Buffer.region_boundaries(s, e)
806
888
  update_line_and_column(old_pos, s)
807
889
  save_point do
808
890
  str = substring(s, e)
@@ -868,7 +950,9 @@ module Textbringer
868
950
  end
869
951
 
870
952
  def insert_for_yank(s)
871
- mark_to_point(@yank_start)
953
+ if @mark.nil? || !point_at_mark?(@mark)
954
+ push_mark
955
+ end
872
956
  insert(s)
873
957
  end
874
958
 
@@ -877,8 +961,8 @@ module Textbringer
877
961
  end
878
962
 
879
963
  def yank_pop
880
- delete_region(@yank_start.location, @point)
881
- insert_for_yank(KILL_RING.current(1))
964
+ delete_region
965
+ insert_for_yank(KILL_RING.rotate(1))
882
966
  end
883
967
 
884
968
  def undo
@@ -963,7 +1047,7 @@ module Textbringer
963
1047
 
964
1048
  def looking_at?(re)
965
1049
  if re.is_a?(Regexp)
966
- r = Regexp.new("\\G(?:#{re.source})", re.options)
1050
+ r = /\G#{re}/
967
1051
  else
968
1052
  r = "\\G(?:#{re})"
969
1053
  end
@@ -1049,19 +1133,21 @@ module Textbringer
1049
1133
  b = match_beginning(0)
1050
1134
  e = match_end(0)
1051
1135
  goto_char(b)
1052
- delete_region(b, e)
1053
- insert(new_str)
1054
- merge_undo(2)
1136
+ composite_edit do
1137
+ delete_region(b, e)
1138
+ insert(new_str)
1139
+ end
1055
1140
  end
1056
1141
 
1057
1142
  def replace_regexp_forward(regexp, to_str)
1058
1143
  result = 0
1059
1144
  rest = substring(point, point_max)
1060
- delete_region(point, point_max)
1061
- new_str = rest.gsub(new_regexp(regexp)) {
1062
- result += 1
1063
- m = Regexp.last_match
1064
- to_str.gsub(/\\(?:([0-9]+)|(&)|(\\))/) { |s|
1145
+ composite_edit do
1146
+ delete_region(point, point_max)
1147
+ new_str = rest.gsub(new_regexp(regexp)) {
1148
+ result += 1
1149
+ m = Regexp.last_match
1150
+ to_str.gsub(/\\(?:([0-9]+)|(&)|(\\))/) { |s|
1065
1151
  case
1066
1152
  when $1
1067
1153
  m[$1.to_i]
@@ -1070,10 +1156,10 @@ module Textbringer
1070
1156
  when $3
1071
1157
  "\\"
1072
1158
  end
1159
+ }
1073
1160
  }
1074
- }
1075
- insert(new_str)
1076
- merge_undo(2)
1161
+ insert(new_str)
1162
+ end
1077
1163
  result
1078
1164
  end
1079
1165
 
@@ -1095,16 +1181,22 @@ module Textbringer
1095
1181
  /\A\0*\z/ =~ @contents[@gap_start...@gap_end] ? true : false
1096
1182
  end
1097
1183
 
1098
- def merge_undo(n)
1099
- return if @undoing || @undo_limit == 0
1100
- actions = @undo_stack.pop(n)
1101
- if actions
1102
- action = CompositeAction.new(self, actions.first.location)
1103
- actions.each do |i|
1104
- action.add_action(i)
1184
+ def composite_edit
1185
+ @composite_edit_level += 1
1186
+ begin
1187
+ yield
1188
+ ensure
1189
+ @composite_edit_level -= 1
1190
+ if @composite_edit_level == 0 && !@composite_edit_actions.empty?
1191
+ action = CompositeAction.new(self,
1192
+ @composite_edit_actions.first.location)
1193
+ @composite_edit_actions.each do |i|
1194
+ action.add_action(i)
1195
+ end
1196
+ action.version = @composite_edit_actions.first.version
1197
+ push_undo(action)
1198
+ @composite_edit_actions.clear
1105
1199
  end
1106
- action.version = actions.first.version
1107
- @undo_stack.push(action)
1108
1200
  end
1109
1201
  end
1110
1202
 
@@ -1189,9 +1281,10 @@ module Textbringer
1189
1281
 
1190
1282
  def gsub(*args, &block)
1191
1283
  s = to_s.gsub(*args, &block)
1192
- delete_region(point_min, point_max)
1193
- insert(s)
1194
- merge_undo(2)
1284
+ composite_edit do
1285
+ delete_region(point_min, point_max)
1286
+ insert(s)
1287
+ end
1195
1288
  self
1196
1289
  end
1197
1290
 
@@ -1343,14 +1436,18 @@ module Textbringer
1343
1436
 
1344
1437
  def push_undo(action)
1345
1438
  return if @undoing || @undo_limit == 0
1346
- if @undo_stack.size >= @undo_limit
1347
- @undo_stack[0, @undo_stack.size + 1 - @undo_limit] = []
1348
- end
1349
- if !modified?
1350
- action.version = @version
1439
+ if @composite_edit_level > 0
1440
+ @composite_edit_actions.push(action)
1441
+ else
1442
+ if @undo_stack.size >= @undo_limit
1443
+ @undo_stack[0, @undo_stack.size + 1 - @undo_limit] = []
1444
+ end
1445
+ if !modified?
1446
+ action.version = @version
1447
+ end
1448
+ @undo_stack.push(action)
1449
+ @redo_stack.clear
1351
1450
  end
1352
- @undo_stack.push(action)
1353
- @redo_stack.clear
1354
1451
  end
1355
1452
 
1356
1453
  def new_regexp(s)
@@ -1369,70 +1466,48 @@ module Textbringer
1369
1466
  end
1370
1467
 
1371
1468
  class Mark
1372
- attr_reader :buffer
1469
+ attr_reader :buffer, :file_name
1373
1470
  attr_accessor :location
1374
1471
 
1375
1472
  def initialize(buffer, location)
1376
1473
  @buffer = buffer
1474
+ @file_name = nil
1377
1475
  @location = location
1378
1476
  end
1379
1477
 
1380
- def delete
1381
- @buffer.marks.delete(self)
1382
- end
1383
-
1384
- def dup
1385
- mark = @buffer.new_mark
1386
- mark.location = @location
1387
- mark
1388
- end
1389
- end
1390
-
1391
- class KillRing
1392
- def initialize(max = 30)
1393
- @max = max
1394
- @ring = []
1395
- @current = -1
1478
+ def inspect
1479
+ "#<Mark:#{@buffer&.name || @file_name}:#{@location}>"
1396
1480
  end
1397
1481
 
1398
- def clear
1399
- @ring.clear
1400
- @current = -1
1482
+ def delete
1483
+ if @buffer
1484
+ @buffer.marks.delete(self)
1485
+ end
1401
1486
  end
1402
1487
 
1403
- def push(str)
1404
- @current += 1
1405
- if @ring.size < @max
1406
- @ring.insert(@current, str)
1407
- else
1408
- if @current == @max
1409
- @current = 0
1410
- end
1411
- @ring[@current] = str
1412
- end
1488
+ def deleted?
1489
+ !@buffer.marks.include?(self)
1413
1490
  end
1414
1491
 
1415
- def current(n = 0)
1416
- if @ring.empty?
1417
- raise EditorError, "Kill ring is empty"
1418
- end
1419
- @current -= n
1420
- if @current < 0
1421
- @current += @ring.size
1492
+ def detach
1493
+ if @buffer
1494
+ @file_name = @buffer.file_name
1495
+ @buffer = nil
1422
1496
  end
1423
- @ring[@current]
1424
1497
  end
1425
1498
 
1426
- def empty?
1427
- @ring.empty?
1499
+ def detached?
1500
+ @buffer.nil?
1428
1501
  end
1429
1502
 
1430
- def size
1431
- @ring.size
1503
+ def dup
1504
+ mark = @buffer.new_mark
1505
+ mark.location = @location
1506
+ mark
1432
1507
  end
1433
1508
  end
1434
1509
 
1435
- KILL_RING = KillRing.new
1510
+ KILL_RING = Ring.new
1436
1511
 
1437
1512
  class UndoableAction
1438
1513
  attr_accessor :version