glimmer-cs-gladiator 0.8.1 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +43 -0
- data/LICENSE.txt +20 -20
- data/README.md +65 -31
- data/VERSION +1 -1
- data/bin/gladiator +31 -34
- data/bin/gladiator-setup +80 -80
- data/bin/glimmer-cs-gladiator +22 -22
- data/glimmer-cs-gladiator.gemspec +17 -22
- data/images/glimmer-cs-gladiator-logo.png +0 -0
- data/lib/glimmer-cs-gladiator/launch.rb +6 -6
- data/lib/glimmer-cs-gladiator.rb +30 -30
- data/lib/models/glimmer/gladiator/command.rb +112 -112
- data/lib/models/glimmer/gladiator/dir.rb +17 -4
- data/lib/models/glimmer/gladiator/file.rb +729 -662
- data/lib/views/glimmer/gladiator/file_edit_menu.rb +67 -0
- data/lib/views/glimmer/gladiator/file_explorer_tree.rb +244 -244
- data/lib/views/glimmer/gladiator/file_lookup_list.rb +63 -64
- data/lib/views/glimmer/gladiator/gladiator_menu_bar.rb +35 -8
- data/lib/views/glimmer/gladiator/progress_shell.rb +29 -31
- data/lib/views/glimmer/gladiator/text_editor.rb +11 -0
- data/lib/views/glimmer/gladiator.rb +159 -138
- metadata +29 -20
@@ -1,662 +1,729 @@
|
|
1
|
-
module Glimmer
|
2
|
-
class Gladiator
|
3
|
-
class File
|
4
|
-
include Glimmer
|
5
|
-
|
6
|
-
attr_accessor :line_numbers_content, :line_number, :find_text, :replace_text, :top_pixel, :display_path, :case_sensitive, :caret_position, :selection_count, :last_caret_position, :last_selection_count, :line_position
|
7
|
-
attr_reader :name, :path, :project_dir
|
8
|
-
|
9
|
-
def initialize(path='', project_dir=nil)
|
10
|
-
raise "Not a file path: #{path}" if path.nil? || (!path.empty? && !::File.file?(path))
|
11
|
-
@project_dir = project_dir
|
12
|
-
@command_history = []
|
13
|
-
@name = path.empty? ? 'Scratchpad' : ::File.basename(path)
|
14
|
-
self.path = ::File.expand_path(path) unless path.empty?
|
15
|
-
@top_pixel = 0
|
16
|
-
@caret_position = 0
|
17
|
-
@selection_count = 0
|
18
|
-
@last_selection_count = 0
|
19
|
-
@line_number = 1
|
20
|
-
@init = nil
|
21
|
-
end
|
22
|
-
|
23
|
-
def language
|
24
|
-
# TODO consider using Rouge::Lexer.guess_by_filename instead and perhaps guess_by_source when it fails
|
25
|
-
extension = path.split('.').last if path.to_s.include?('.')
|
26
|
-
return 'ruby' if scratchpad?
|
27
|
-
return 'ruby' if path.to_s.end_with?('Gemfile') || path.to_s.end_with?('Rakefile')
|
28
|
-
return 'ruby' if dirty_content.start_with?('#!/usr/bin/env ruby') || dirty_content.start_with?('#!/usr/bin/env jruby')
|
29
|
-
return 'yaml' if path.to_s.end_with?('Gemfile.lock')
|
30
|
-
return 'shell' if extension.nil? && path.to_s.include?('/bin/')
|
31
|
-
case extension
|
32
|
-
# TODO extract case statement to an external config file
|
33
|
-
when 'rb'
|
34
|
-
'ruby'
|
35
|
-
when 'md', 'markdown'
|
36
|
-
'markdown'
|
37
|
-
when 'js', 'es6'
|
38
|
-
'javascript'
|
39
|
-
when 'json'
|
40
|
-
'json'
|
41
|
-
when 'yaml'
|
42
|
-
'yaml'
|
43
|
-
when 'html'
|
44
|
-
'html'
|
45
|
-
when 'h', 'c'
|
46
|
-
'c'
|
47
|
-
when 'hs'
|
48
|
-
'haskell'
|
49
|
-
when 'gradle'
|
50
|
-
'gradle'
|
51
|
-
when 'cpp'
|
52
|
-
'cpp'
|
53
|
-
when 'css'
|
54
|
-
'css'
|
55
|
-
when 'java'
|
56
|
-
'java'
|
57
|
-
when 'jsp'
|
58
|
-
'jsp'
|
59
|
-
when 'plist'
|
60
|
-
'plist'
|
61
|
-
when 'haml'
|
62
|
-
'haml'
|
63
|
-
when 'xml'
|
64
|
-
'xml'
|
65
|
-
when 'ini'
|
66
|
-
'ini'
|
67
|
-
when 'pl'
|
68
|
-
'perl'
|
69
|
-
when 'tcl'
|
70
|
-
'tcl'
|
71
|
-
when 'sass'
|
72
|
-
'sass'
|
73
|
-
when 'scss'
|
74
|
-
'scss'
|
75
|
-
when 'sql'
|
76
|
-
'sql'
|
77
|
-
when 'sh'
|
78
|
-
'shell'
|
79
|
-
when 'vue'
|
80
|
-
'vue'
|
81
|
-
when 'txt', nil
|
82
|
-
'plain_text'
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def init_content
|
87
|
-
unless @init
|
88
|
-
@init = true
|
89
|
-
begin
|
90
|
-
# test read dirty content
|
91
|
-
observe(self, :dirty_content) do
|
92
|
-
line_count = lines.empty? ? 1 : lines.size
|
93
|
-
lines_text_size = [line_count.to_s.size, 4].max
|
94
|
-
old_top_pixel = top_pixel
|
95
|
-
self.line_numbers_content = line_count.times.map {|n| (' ' * (lines_text_size - (n+1).to_s.size)) + (n+1).to_s }.join("\n")
|
96
|
-
self.top_pixel = old_top_pixel
|
97
|
-
end
|
98
|
-
the_dirty_content = read_dirty_content
|
99
|
-
the_dirty_content.split("\n") # test that it is not a binary file (crashes to rescue block otherwise)
|
100
|
-
self.dirty_content = the_dirty_content
|
101
|
-
observe(self, :caret_position) do |new_caret_position|
|
102
|
-
update_line_number_from_caret_position(new_caret_position)
|
103
|
-
end
|
104
|
-
observe(self, :line_number) do |new_line_number|
|
105
|
-
line_index = line_number - 1
|
106
|
-
new_caret_position = caret_position_for_line_index(line_index)
|
107
|
-
current_caret_position = caret_position
|
108
|
-
line_index_for_new_caret_position = line_index_for_caret_position(new_caret_position)
|
109
|
-
line_index_for_current_caret_position = line_index_for_caret_position(current_caret_position)
|
110
|
-
self.caret_position = new_caret_position unless (current_caret_position && line_index_for_new_caret_position == line_index_for_current_caret_position)
|
111
|
-
end
|
112
|
-
rescue # in case of a binary file
|
113
|
-
stop_filewatcher
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def update_line_number_from_caret_position(new_caret_position)
|
119
|
-
new_line_number = line_index_for_caret_position(caret_position) + 1
|
120
|
-
current_line_number = line_number
|
121
|
-
unless (current_line_number && current_line_number == new_line_number)
|
122
|
-
self.line_number = new_line_number
|
123
|
-
# TODO check if the following line is needed
|
124
|
-
self.line_position = caret_position - caret_position_for_line_index(line_number - 1) + 1
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def path=(the_path)
|
129
|
-
@path = the_path
|
130
|
-
generate_display_path
|
131
|
-
end
|
132
|
-
|
133
|
-
def generate_display_path
|
134
|
-
return if @path.empty?
|
135
|
-
@display_path = @path.sub(project_dir.path, '').sub(/^\//, '')
|
136
|
-
end
|
137
|
-
|
138
|
-
def name=(the_name)
|
139
|
-
new_path = path.sub(/#{Regexp.escape(@name)}$/, the_name) unless scratchpad?
|
140
|
-
@name = the_name
|
141
|
-
if !scratchpad? && ::File.exist?(path)
|
142
|
-
FileUtils.mv(path, new_path)
|
143
|
-
self.path = new_path
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def scratchpad?
|
148
|
-
path.to_s.empty?
|
149
|
-
end
|
150
|
-
|
151
|
-
def
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
end
|
156
|
-
|
157
|
-
def
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
@
|
172
|
-
@
|
173
|
-
end
|
174
|
-
|
175
|
-
def
|
176
|
-
|
177
|
-
@
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
end
|
228
|
-
|
229
|
-
def
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
self.
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
self.
|
537
|
-
end
|
538
|
-
|
539
|
-
def
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
self.
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
self.
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
dirty_content
|
600
|
-
end
|
601
|
-
|
602
|
-
def
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
end
|
652
|
-
|
653
|
-
def
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
end
|
660
|
-
|
661
|
-
|
662
|
-
|
1
|
+
module Glimmer
|
2
|
+
class Gladiator
|
3
|
+
class File
|
4
|
+
include Glimmer
|
5
|
+
|
6
|
+
attr_accessor :line_numbers_content, :line_number, :find_text, :replace_text, :top_pixel, :display_path, :case_sensitive, :caret_position, :selection_count, :last_caret_position, :last_selection_count, :line_position
|
7
|
+
attr_reader :name, :path, :project_dir
|
8
|
+
|
9
|
+
def initialize(path='', project_dir=nil)
|
10
|
+
raise "Not a file path: #{path}" if path.nil? || (!path.empty? && !::File.file?(path))
|
11
|
+
@project_dir = project_dir
|
12
|
+
@command_history = []
|
13
|
+
@name = path.empty? ? 'Scratchpad' : ::File.basename(path)
|
14
|
+
self.path = ::File.expand_path(path) unless path.empty?
|
15
|
+
@top_pixel = 0
|
16
|
+
@caret_position = 0
|
17
|
+
@selection_count = 0
|
18
|
+
@last_selection_count = 0
|
19
|
+
@line_number = 1
|
20
|
+
@init = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def language
|
24
|
+
# TODO consider using Rouge::Lexer.guess_by_filename instead and perhaps guess_by_source when it fails
|
25
|
+
extension = path.split('.').last if path.to_s.include?('.')
|
26
|
+
return 'ruby' if scratchpad?
|
27
|
+
return 'ruby' if path.to_s.end_with?('Gemfile') || path.to_s.end_with?('Rakefile')
|
28
|
+
return 'ruby' if dirty_content.start_with?('#!/usr/bin/env ruby') || dirty_content.start_with?('#!/usr/bin/env jruby')
|
29
|
+
return 'yaml' if path.to_s.end_with?('Gemfile.lock')
|
30
|
+
return 'shell' if extension.nil? && path.to_s.include?('/bin/')
|
31
|
+
case extension
|
32
|
+
# TODO extract case statement to an external config file
|
33
|
+
when 'rb'
|
34
|
+
'ruby'
|
35
|
+
when 'md', 'markdown'
|
36
|
+
'markdown'
|
37
|
+
when 'js', 'es6'
|
38
|
+
'javascript'
|
39
|
+
when 'json'
|
40
|
+
'json'
|
41
|
+
when 'yaml'
|
42
|
+
'yaml'
|
43
|
+
when 'html'
|
44
|
+
'html'
|
45
|
+
when 'h', 'c'
|
46
|
+
'c'
|
47
|
+
when 'hs'
|
48
|
+
'haskell'
|
49
|
+
when 'gradle'
|
50
|
+
'gradle'
|
51
|
+
when 'cpp'
|
52
|
+
'cpp'
|
53
|
+
when 'css'
|
54
|
+
'css'
|
55
|
+
when 'java'
|
56
|
+
'java'
|
57
|
+
when 'jsp'
|
58
|
+
'jsp'
|
59
|
+
when 'plist'
|
60
|
+
'plist'
|
61
|
+
when 'haml'
|
62
|
+
'haml'
|
63
|
+
when 'xml'
|
64
|
+
'xml'
|
65
|
+
when 'ini'
|
66
|
+
'ini'
|
67
|
+
when 'pl'
|
68
|
+
'perl'
|
69
|
+
when 'tcl'
|
70
|
+
'tcl'
|
71
|
+
when 'sass'
|
72
|
+
'sass'
|
73
|
+
when 'scss'
|
74
|
+
'scss'
|
75
|
+
when 'sql'
|
76
|
+
'sql'
|
77
|
+
when 'sh'
|
78
|
+
'shell'
|
79
|
+
when 'vue'
|
80
|
+
'vue'
|
81
|
+
when 'txt', nil
|
82
|
+
'plain_text'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def init_content
|
87
|
+
unless @init
|
88
|
+
@init = true
|
89
|
+
begin
|
90
|
+
# test read dirty content
|
91
|
+
observe(self, :dirty_content) do
|
92
|
+
line_count = lines.empty? ? 1 : lines.size
|
93
|
+
lines_text_size = [line_count.to_s.size, 4].max
|
94
|
+
old_top_pixel = top_pixel
|
95
|
+
self.line_numbers_content = line_count.times.map {|n| (' ' * (lines_text_size - (n+1).to_s.size)) + (n+1).to_s }.join("\n")
|
96
|
+
self.top_pixel = old_top_pixel
|
97
|
+
end
|
98
|
+
the_dirty_content = read_dirty_content
|
99
|
+
the_dirty_content.split("\n") # test that it is not a binary file (crashes to rescue block otherwise)
|
100
|
+
self.dirty_content = the_dirty_content
|
101
|
+
observe(self, :caret_position) do |new_caret_position|
|
102
|
+
update_line_number_from_caret_position(new_caret_position)
|
103
|
+
end
|
104
|
+
observe(self, :line_number) do |new_line_number|
|
105
|
+
line_index = line_number - 1
|
106
|
+
new_caret_position = caret_position_for_line_index(line_index)
|
107
|
+
current_caret_position = caret_position
|
108
|
+
line_index_for_new_caret_position = line_index_for_caret_position(new_caret_position)
|
109
|
+
line_index_for_current_caret_position = line_index_for_caret_position(current_caret_position)
|
110
|
+
self.caret_position = new_caret_position unless (current_caret_position && line_index_for_new_caret_position == line_index_for_current_caret_position)
|
111
|
+
end
|
112
|
+
rescue # in case of a binary file
|
113
|
+
stop_filewatcher
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def update_line_number_from_caret_position(new_caret_position)
|
119
|
+
new_line_number = line_index_for_caret_position(caret_position) + 1
|
120
|
+
current_line_number = line_number
|
121
|
+
unless (current_line_number && current_line_number == new_line_number)
|
122
|
+
self.line_number = new_line_number
|
123
|
+
# TODO check if the following line is needed
|
124
|
+
self.line_position = caret_position - caret_position_for_line_index(line_number - 1) + 1
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def path=(the_path)
|
129
|
+
@path = the_path
|
130
|
+
generate_display_path
|
131
|
+
end
|
132
|
+
|
133
|
+
def generate_display_path
|
134
|
+
return if @path.empty?
|
135
|
+
@display_path = @path.sub(project_dir.path, '').sub(/^\//, '')
|
136
|
+
end
|
137
|
+
|
138
|
+
def name=(the_name)
|
139
|
+
new_path = path.sub(/#{Regexp.escape(@name)}$/, the_name) unless scratchpad?
|
140
|
+
@name = the_name
|
141
|
+
if !scratchpad? && ::File.exist?(path)
|
142
|
+
FileUtils.mv(path, new_path)
|
143
|
+
self.path = new_path
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def scratchpad?
|
148
|
+
path.to_s.empty? # TODO consider updating to set the .gladiator-scratchpad path directly
|
149
|
+
end
|
150
|
+
|
151
|
+
def scratchpad_file
|
152
|
+
scratchpad_file = ::File.join(project_dir.path, '.gladiator-scratchpad')
|
153
|
+
::File.write(scratchpad_file, content)
|
154
|
+
scratchpad_file
|
155
|
+
end
|
156
|
+
|
157
|
+
def backup_properties
|
158
|
+
[:find_text, :replace_text, :case_sensitive, :top_pixel, :caret_position, :selection_count].reduce({}) do |hash, property|
|
159
|
+
hash.merge(property => send(property))
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def restore_properties(properties_hash)
|
164
|
+
return if properties_hash[:caret_position] == 0 && properties_hash[:selection_count] == 0 && properties_hash[:find_text].nil? && properties_hash[:replace_text].nil? && properties_hash[:top_pixel] == 0 && properties_hash[:case_sensitive].nil?
|
165
|
+
properties_hash.each do |property, value|
|
166
|
+
send("#{property}=", value)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def caret_position=(value)
|
171
|
+
@last_caret_position = @caret_position
|
172
|
+
@caret_position = value
|
173
|
+
end
|
174
|
+
|
175
|
+
def selection_count=(value)
|
176
|
+
#@last_selection_count = @selection_count
|
177
|
+
@selection_count = value
|
178
|
+
@last_selection_count = @selection_count
|
179
|
+
end
|
180
|
+
|
181
|
+
def dirty_content
|
182
|
+
init_content
|
183
|
+
@dirty_content
|
184
|
+
end
|
185
|
+
|
186
|
+
def dirty_content=(the_content)
|
187
|
+
# TODO set partial dirty content by line(s) for enhanced performance
|
188
|
+
@dirty_content = the_content
|
189
|
+
old_caret_position = caret_position
|
190
|
+
old_top_pixel = top_pixel
|
191
|
+
|
192
|
+
notify_observers(:content)
|
193
|
+
if @formatting_dirty_content_for_writing
|
194
|
+
self.caret_position = old_caret_position
|
195
|
+
self.top_pixel = old_top_pixel
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def content
|
200
|
+
dirty_content
|
201
|
+
end
|
202
|
+
|
203
|
+
# to use for widget data-binding
|
204
|
+
def content=(value)
|
205
|
+
value = value.gsub("\t", ' ')
|
206
|
+
if dirty_content != value
|
207
|
+
Command.do(self, :change_content!, value)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def change_content!(value)
|
212
|
+
self.dirty_content = value
|
213
|
+
format_dirty_content_for_writing!(force: true) if value.to_s.include?("\r\n")
|
214
|
+
update_line_number_from_caret_position(caret_position)
|
215
|
+
end
|
216
|
+
|
217
|
+
def start_command
|
218
|
+
@commmand_in_progress = true
|
219
|
+
end
|
220
|
+
|
221
|
+
def end_command
|
222
|
+
@commmand_in_progress = false
|
223
|
+
end
|
224
|
+
|
225
|
+
def command_in_progress?
|
226
|
+
@commmand_in_progress
|
227
|
+
end
|
228
|
+
|
229
|
+
def close
|
230
|
+
@closed = true
|
231
|
+
stop_filewatcher
|
232
|
+
remove_all_observers
|
233
|
+
initialize(path, project_dir)
|
234
|
+
Command.clear(self)
|
235
|
+
end
|
236
|
+
|
237
|
+
def closed?
|
238
|
+
@closed
|
239
|
+
end
|
240
|
+
|
241
|
+
def read_dirty_content
|
242
|
+
path.empty? ? '' : ::File.read(path)
|
243
|
+
end
|
244
|
+
|
245
|
+
def start_filewatcher
|
246
|
+
return if scratchpad?
|
247
|
+
@filewatcher ||= Filewatcher.new(@path)
|
248
|
+
@filewatcher_thread ||= Thread.new(@filewatcher) do |fw|
|
249
|
+
fw.watch do |filename, event|
|
250
|
+
async_exec do
|
251
|
+
begin
|
252
|
+
self.dirty_content = read_dirty_content if read_dirty_content != dirty_content
|
253
|
+
rescue StandardError, Errno::ENOENT
|
254
|
+
# in case of a binary file
|
255
|
+
stop_filewatcher
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def stop_filewatcher
|
263
|
+
@filewatcher&.stop
|
264
|
+
@filewatcher_thread&.kill
|
265
|
+
@filewatcher_thread = nil
|
266
|
+
@filewatcher&.finalize
|
267
|
+
@filewatcher = nil
|
268
|
+
end
|
269
|
+
|
270
|
+
def write_dirty_content
|
271
|
+
# TODO write partial dirty content by line(s) for enhanced performance
|
272
|
+
return if scratchpad? || !::File.exist?(path) || !::File.exists?(path) || read_dirty_content == dirty_content
|
273
|
+
format_dirty_content_for_writing!
|
274
|
+
::File.write(path, dirty_content)
|
275
|
+
rescue StandardError, ArgumentError => e
|
276
|
+
puts "Error in writing dirty content for #{path}"
|
277
|
+
puts e.full_message
|
278
|
+
end
|
279
|
+
|
280
|
+
def format_dirty_content_for_writing!(force: false)
|
281
|
+
return if !force && @commmand_in_progress
|
282
|
+
new_dirty_content = dirty_content.to_s.split("\n").map {|line| line.strip.empty? ? line : line.rstrip }.join("\n")
|
283
|
+
new_dirty_content = "#{new_dirty_content.gsub("\r\n", "\n").gsub("\r", "\n").sub(/\n+\z/, '')}\n"
|
284
|
+
if new_dirty_content != self.dirty_content
|
285
|
+
@formatting_dirty_content_for_writing = true
|
286
|
+
caret_position_diff = dirty_content.to_s.size - new_dirty_content.size
|
287
|
+
self.dirty_content = new_dirty_content
|
288
|
+
self.caret_position = caret_position - caret_position_diff
|
289
|
+
@formatting_dirty_content_for_writing = false
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def write_raw_dirty_content
|
294
|
+
return if scratchpad? || !::File.exist?(path)
|
295
|
+
::File.write(path, dirty_content) if ::File.exists?(path)
|
296
|
+
rescue => e
|
297
|
+
puts "Error in writing raw dirty content for #{path}"
|
298
|
+
puts e.full_message
|
299
|
+
end
|
300
|
+
|
301
|
+
def current_line_indentation
|
302
|
+
current_line.to_s.match(/^(\s+)/).to_a[1].to_s
|
303
|
+
end
|
304
|
+
|
305
|
+
def current_line
|
306
|
+
lines[line_number - 1]
|
307
|
+
end
|
308
|
+
|
309
|
+
def delete!
|
310
|
+
FileUtils.rm(path) unless scratchpad?
|
311
|
+
end
|
312
|
+
|
313
|
+
def prefix_new_line!
|
314
|
+
the_lines = lines
|
315
|
+
the_lines[line_number-1...line_number-1] = [current_line_indentation]
|
316
|
+
self.dirty_content = the_lines.join("\n")
|
317
|
+
self.caret_position = caret_position_for_line_index(line_number-1) + current_line_indentation.size
|
318
|
+
self.selection_count = 0
|
319
|
+
end
|
320
|
+
|
321
|
+
def insert_new_line!
|
322
|
+
the_lines = lines
|
323
|
+
the_lines[line_number...line_number] = [current_line_indentation]
|
324
|
+
self.dirty_content = the_lines.join("\n")
|
325
|
+
self.caret_position = caret_position_for_line_index(line_number) + current_line_indentation.size
|
326
|
+
self.selection_count = 0
|
327
|
+
end
|
328
|
+
|
329
|
+
def comment_line!
|
330
|
+
old_lines = lines
|
331
|
+
return if old_lines.size < 1
|
332
|
+
old_selection_count = self.selection_count
|
333
|
+
old_caret_position = self.caret_position
|
334
|
+
old_caret_position_line_index = line_index_for_caret_position(old_caret_position)
|
335
|
+
old_caret_position_line_caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
336
|
+
old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
|
337
|
+
new_lines = lines
|
338
|
+
delta = 0
|
339
|
+
line_indices_for_selection(caret_position, selection_count).reverse.each do | the_line_index |
|
340
|
+
delta = 0
|
341
|
+
the_line = old_lines[the_line_index]
|
342
|
+
return if the_line.nil?
|
343
|
+
if the_line.strip.start_with?('# ')
|
344
|
+
new_lines[the_line_index] = the_line.sub(/# /, '')
|
345
|
+
delta -= 2
|
346
|
+
elsif the_line.strip.start_with?('#')
|
347
|
+
new_lines[the_line_index] = the_line.sub(/#/, '')
|
348
|
+
delta -= 1
|
349
|
+
else
|
350
|
+
new_lines[the_line_index] = "# #{the_line}"
|
351
|
+
delta += 2
|
352
|
+
end
|
353
|
+
end
|
354
|
+
self.dirty_content = new_lines.join("\n")
|
355
|
+
if old_selection_count.to_i > 0
|
356
|
+
self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
357
|
+
self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
|
358
|
+
else
|
359
|
+
new_caret_position = old_caret_position + delta
|
360
|
+
new_caret_position = [new_caret_position, old_caret_position_line_caret_position].max
|
361
|
+
self.caret_position = new_caret_position
|
362
|
+
self.selection_count = 0
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def indent!
|
367
|
+
new_lines = lines
|
368
|
+
old_lines = lines
|
369
|
+
return if old_lines.size < 1
|
370
|
+
old_selection_count = self.selection_count
|
371
|
+
old_caret_position = self.caret_position
|
372
|
+
old_caret_position_line_index = line_index_for_caret_position(old_caret_position)
|
373
|
+
old_caret_position_line_caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
374
|
+
old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
|
375
|
+
delta = 2
|
376
|
+
line_indices_for_selection(caret_position, selection_count).each do |the_line_index|
|
377
|
+
the_line = old_lines[the_line_index]
|
378
|
+
new_lines[the_line_index] = " #{the_line}"
|
379
|
+
end
|
380
|
+
old_caret_position = self.caret_position
|
381
|
+
self.dirty_content = new_lines.join("\n")
|
382
|
+
if old_selection_count.to_i > 0
|
383
|
+
self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
384
|
+
self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
|
385
|
+
else
|
386
|
+
self.caret_position = old_caret_position + delta
|
387
|
+
self.selection_count = 0
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
def outdent!
|
392
|
+
new_lines = lines
|
393
|
+
old_lines = lines
|
394
|
+
return if old_lines.size < 1
|
395
|
+
old_selection_count = self.selection_count
|
396
|
+
old_caret_position = self.caret_position
|
397
|
+
old_caret_position_line_index = line_index_for_caret_position(old_caret_position)
|
398
|
+
old_caret_position_line_caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
399
|
+
old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
|
400
|
+
delta = 0
|
401
|
+
line_indices_for_selection(caret_position, selection_count).each do |the_line_index|
|
402
|
+
the_line = old_lines[the_line_index]
|
403
|
+
if the_line.to_s.start_with?(' ')
|
404
|
+
new_lines[the_line_index] = the_line.sub(/ /, '')
|
405
|
+
delta = -2
|
406
|
+
elsif the_line&.start_with?(' ')
|
407
|
+
new_lines[the_line_index] = the_line.sub(/ /, '')
|
408
|
+
delta = -1
|
409
|
+
end
|
410
|
+
end
|
411
|
+
self.dirty_content = new_lines.join("\n")
|
412
|
+
if old_selection_count.to_i > 0
|
413
|
+
self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
414
|
+
self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
|
415
|
+
else
|
416
|
+
new_caret_position = old_caret_position + delta
|
417
|
+
new_caret_position = [new_caret_position, old_caret_position_line_caret_position].max
|
418
|
+
self.caret_position = new_caret_position
|
419
|
+
self.selection_count = 0
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
def kill_line!
|
424
|
+
new_lines = lines
|
425
|
+
return if new_lines.size < 1
|
426
|
+
line_indices = line_indices_for_selection(caret_position, selection_count)
|
427
|
+
new_lines = new_lines[0...line_indices.first] + new_lines[(line_indices.last+1)...new_lines.size]
|
428
|
+
old_caret_position = self.caret_position
|
429
|
+
old_line_index = self.line_number - 1
|
430
|
+
line_position = line_position_for_caret_position(old_caret_position)
|
431
|
+
self.dirty_content = "#{new_lines.join("\n")}\n"
|
432
|
+
self.caret_position = caret_position_for_line_index(old_line_index) + [line_position, lines[old_line_index].to_s.size].min
|
433
|
+
self.selection_count = 0
|
434
|
+
end
|
435
|
+
|
436
|
+
def duplicate_line!
|
437
|
+
new_lines = lines
|
438
|
+
old_lines = lines
|
439
|
+
return if old_lines.size < 1
|
440
|
+
old_selection_count = self.selection_count
|
441
|
+
old_caret_position = self.caret_position
|
442
|
+
old_caret_position_line_index = line_index_for_caret_position(old_caret_position)
|
443
|
+
old_caret_position_line_caret_position = caret_position_for_caret_position_start_of_line(old_caret_position_line_index)
|
444
|
+
old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
|
445
|
+
the_line_indices = line_indices_for_selection(caret_position, selection_count)
|
446
|
+
the_lines = lines_for_selection(caret_position, selection_count)
|
447
|
+
delta = the_lines.join("\n").size + 1
|
448
|
+
the_lines.each_with_index do |the_line, i|
|
449
|
+
new_lines.insert(the_line_indices.first + i, the_line)
|
450
|
+
end
|
451
|
+
self.dirty_content = new_lines.join("\n")
|
452
|
+
if old_selection_count.to_i > 0
|
453
|
+
self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
454
|
+
self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
|
455
|
+
else
|
456
|
+
self.caret_position = old_caret_position + delta
|
457
|
+
self.selection_count = 0
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
def find_next
|
462
|
+
return if find_text.to_s.empty?
|
463
|
+
all_lines = lines
|
464
|
+
the_line_index = line_index_for_caret_position(caret_position)
|
465
|
+
line_position = line_position_for_caret_position(caret_position)
|
466
|
+
found = found_text?(caret_position)
|
467
|
+
2.times do |i|
|
468
|
+
rotation = the_line_index
|
469
|
+
all_lines.rotate(rotation).each_with_index do |the_line, the_index|
|
470
|
+
the_index = (the_index + rotation)%all_lines.size
|
471
|
+
start_position = 0
|
472
|
+
start_position = line_position + find_text.to_s.size if i == 0 && the_index == the_line_index && found_text?(caret_position)
|
473
|
+
text_to_find_in = the_line[start_position..-1]
|
474
|
+
occurrence_index = case_sensitive ? text_to_find_in&.index(find_text.to_s) : text_to_find_in&.downcase&.index(find_text.to_s.downcase)
|
475
|
+
if occurrence_index
|
476
|
+
self.caret_position = caret_position_for_line_index(the_index) + start_position + occurrence_index
|
477
|
+
self.selection_count = find_text.to_s.size
|
478
|
+
return
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
def find_previous
|
485
|
+
return if find_text.to_s.empty?
|
486
|
+
all_lines = lines
|
487
|
+
the_line_index = line_index_for_caret_position(caret_position)
|
488
|
+
line_position = line_position_for_caret_position(caret_position)
|
489
|
+
2.times do |i|
|
490
|
+
rotation = - the_line_index - 1 + all_lines.size
|
491
|
+
all_lines.reverse.rotate(rotation).each_with_index do |the_line, the_index|
|
492
|
+
the_index = all_lines.size - 1 - (the_index + rotation)%all_lines.size
|
493
|
+
if the_index == the_line_index
|
494
|
+
start_position = i > 0 ? 0 : (the_line.size - line_position)
|
495
|
+
else
|
496
|
+
start_position = 0
|
497
|
+
end
|
498
|
+
text_to_find_in = the_line.downcase.reverse[start_position...the_line.size].to_s
|
499
|
+
occurrence_index = text_to_find_in.index(find_text.to_s.downcase.reverse)
|
500
|
+
if occurrence_index
|
501
|
+
self.caret_position = caret_position_for_line_index(the_index) + (the_line.size - (start_position + occurrence_index + find_text.to_s.size))
|
502
|
+
self.selection_count = find_text.to_s.size
|
503
|
+
return
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
def ensure_find_next
|
510
|
+
return if find_text.to_s.empty? || dirty_content.to_s.strip.size < 1
|
511
|
+
find_next unless found_text?(self.caret_position)
|
512
|
+
end
|
513
|
+
|
514
|
+
def found_text?(caret_position)
|
515
|
+
dirty_content[caret_position.to_i, find_text.to_s.size].to_s.downcase == find_text.to_s.downcase
|
516
|
+
end
|
517
|
+
|
518
|
+
def replace_next!
|
519
|
+
return if find_text.to_s.empty? || dirty_content.to_s.strip.size < 1
|
520
|
+
ensure_find_next
|
521
|
+
new_dirty_content = dirty_content
|
522
|
+
new_dirty_content[caret_position, find_text.size] = replace_text.to_s
|
523
|
+
self.dirty_content = new_dirty_content
|
524
|
+
find_next
|
525
|
+
find_next if replace_text.to_s.include?(find_text) && !replace_text.to_s.start_with?(find_text)
|
526
|
+
write_dirty_content
|
527
|
+
end
|
528
|
+
|
529
|
+
def page_up
|
530
|
+
self.line_number = [(self.line_number - 15), 1].max
|
531
|
+
self.selection_count = 0
|
532
|
+
end
|
533
|
+
|
534
|
+
def page_down
|
535
|
+
self.line_number = [(self.line_number + 15), lines.size].min
|
536
|
+
self.selection_count = 0
|
537
|
+
end
|
538
|
+
|
539
|
+
def home
|
540
|
+
self.line_number = 1
|
541
|
+
self.selection_count = 0
|
542
|
+
end
|
543
|
+
|
544
|
+
def end
|
545
|
+
self.line_number = lines.size
|
546
|
+
self.selection_count = 0
|
547
|
+
end
|
548
|
+
|
549
|
+
def start_of_line
|
550
|
+
self.caret_position = caret_position_for_line_index(self.line_number - 1)
|
551
|
+
self.selection_count = 0
|
552
|
+
end
|
553
|
+
|
554
|
+
def end_of_line
|
555
|
+
self.caret_position = caret_position_for_line_index(self.line_number) - 1
|
556
|
+
self.selection_count = 0
|
557
|
+
end
|
558
|
+
|
559
|
+
def select_to_start_of_line
|
560
|
+
old_caret_position = caret_position
|
561
|
+
self.caret_position = caret_position_for_line_index(self.line_number - 1)
|
562
|
+
self.selection_count = old_caret_position - caret_position
|
563
|
+
end
|
564
|
+
|
565
|
+
def select_to_end_of_line
|
566
|
+
self.caret_position = selection_count == 0 ? caret_position : caret_position + selection_count
|
567
|
+
self.selection_count = (caret_position_for_line_index(self.line_number) - 1) - caret_position
|
568
|
+
end
|
569
|
+
|
570
|
+
def delete!
|
571
|
+
new_dirty_content = dirty_content
|
572
|
+
new_dirty_content[caret_position...(caret_position + selection_count)] = ''
|
573
|
+
old_caret_position = caret_position
|
574
|
+
self.dirty_content = new_dirty_content
|
575
|
+
self.caret_position = old_caret_position
|
576
|
+
end
|
577
|
+
|
578
|
+
def cut!
|
579
|
+
Clipboard.copy(selected_text)
|
580
|
+
delete!
|
581
|
+
end
|
582
|
+
|
583
|
+
def copy
|
584
|
+
Clipboard.copy(selected_text)
|
585
|
+
end
|
586
|
+
|
587
|
+
def paste!
|
588
|
+
new_dirty_content = dirty_content
|
589
|
+
pasted_text = Clipboard.paste
|
590
|
+
new_dirty_content[caret_position...(caret_position + selection_count)] = pasted_text
|
591
|
+
old_caret_position = caret_position
|
592
|
+
self.dirty_content = new_dirty_content
|
593
|
+
self.caret_position = old_caret_position + pasted_text.to_s.size
|
594
|
+
self.selection_count = 0
|
595
|
+
end
|
596
|
+
|
597
|
+
def select_all
|
598
|
+
self.caret_position = 0
|
599
|
+
self.selection_count = dirty_content.to_s.size
|
600
|
+
end
|
601
|
+
|
602
|
+
def selected_text
|
603
|
+
dirty_content.to_s[caret_position, selection_count]
|
604
|
+
end
|
605
|
+
|
606
|
+
def move_up!
|
607
|
+
old_lines = lines
|
608
|
+
return if old_lines.size < 2
|
609
|
+
old_selection_count = self.selection_count
|
610
|
+
old_caret_position = self.caret_position
|
611
|
+
old_caret_position_line_caret_position = caret_position_for_caret_position_start_of_line(old_caret_position)
|
612
|
+
old_caret_position_line_position = old_caret_position - old_caret_position_line_caret_position
|
613
|
+
old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
|
614
|
+
new_lines = lines
|
615
|
+
the_line_indices = line_indices_for_selection(caret_position, selection_count)
|
616
|
+
the_lines = lines_for_selection(caret_position, selection_count)
|
617
|
+
new_line_index = [the_line_indices.first - 1, 0].max
|
618
|
+
new_lines[the_line_indices.first..the_line_indices.last] = []
|
619
|
+
new_lines[new_line_index...new_line_index] = the_lines
|
620
|
+
self.dirty_content = new_lines.join("\n")
|
621
|
+
self.caret_position = caret_position_for_line_index(new_line_index) + [old_caret_position_line_position, new_lines[new_line_index].size].min
|
622
|
+
self.selection_count = old_selection_count.to_i if old_selection_count.to_i > 0
|
623
|
+
end
|
624
|
+
|
625
|
+
def move_down!
|
626
|
+
old_lines = lines
|
627
|
+
return if old_lines.size < 2
|
628
|
+
old_selection_count = self.selection_count
|
629
|
+
old_caret_position = self.caret_position
|
630
|
+
old_caret_position_line_caret_position = caret_position_for_caret_position_start_of_line(old_caret_position)
|
631
|
+
old_caret_position_line_position = old_caret_position - old_caret_position_line_caret_position
|
632
|
+
old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
|
633
|
+
new_lines = lines
|
634
|
+
the_line_indices = line_indices_for_selection(caret_position, selection_count)
|
635
|
+
the_lines = lines_for_selection(caret_position, selection_count)
|
636
|
+
new_line_index = [the_line_indices.first + 1, new_lines.size - 1].min
|
637
|
+
new_lines[the_line_indices.first..the_line_indices.last] = []
|
638
|
+
new_lines[new_line_index...new_line_index] = the_lines
|
639
|
+
self.dirty_content = new_lines.join("\n")
|
640
|
+
self.caret_position = caret_position_for_line_index(new_line_index) + [old_caret_position_line_position, new_lines[new_line_index].size].min
|
641
|
+
self.selection_count = old_selection_count.to_i if old_selection_count.to_i > 0
|
642
|
+
end
|
643
|
+
|
644
|
+
def run
|
645
|
+
if scratchpad?
|
646
|
+
load scratchpad_file
|
647
|
+
else
|
648
|
+
write_dirty_content
|
649
|
+
load path
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
def lines
|
654
|
+
need_padding = dirty_content.to_s.end_with?("\n")
|
655
|
+
splittable_content = need_padding ? "#{dirty_content} " : dirty_content
|
656
|
+
the_lines = splittable_content.split("\n")
|
657
|
+
the_lines[-1] = the_lines[-1].strip if need_padding
|
658
|
+
the_lines
|
659
|
+
end
|
660
|
+
|
661
|
+
def line_for_caret_position(caret_position)
|
662
|
+
lines[line_index_for_caret_position(caret_position.to_i)]
|
663
|
+
end
|
664
|
+
|
665
|
+
def line_index_for_caret_position(caret_position)
|
666
|
+
dirty_content[0...caret_position.to_i].count("\n")
|
667
|
+
end
|
668
|
+
|
669
|
+
def caret_position_for_line_index(line_index)
|
670
|
+
cp = lines[0...line_index].join("\n").size
|
671
|
+
cp += 1 if line_index > 0
|
672
|
+
cp
|
673
|
+
end
|
674
|
+
|
675
|
+
def caret_position_for_caret_position_start_of_line(caret_position)
|
676
|
+
caret_position_for_line_index(line_index_for_caret_position(caret_position))
|
677
|
+
end
|
678
|
+
|
679
|
+
# position within line containing "caret position" (e.g. for caret position 5 in 1st line, they match as 5, for 15 in line 2 with line 1 having 10 characters, line position is 4)
|
680
|
+
# TODO consider renaming to line_character_position_for_caret_position
|
681
|
+
def line_position_for_caret_position(caret_position)
|
682
|
+
caret_position = caret_position.to_i
|
683
|
+
caret_position - caret_position_for_caret_position_start_of_line(caret_position)
|
684
|
+
end
|
685
|
+
|
686
|
+
def line_caret_positions_for_selection(caret_position, selection_count)
|
687
|
+
line_indices = line_indices_for_selection(caret_position, selection_count)
|
688
|
+
line_caret_positions = line_indices.map { |line_index| caret_position_for_line_index(line_index) }.to_a
|
689
|
+
end
|
690
|
+
|
691
|
+
def end_caret_position_line_index(caret_position, selection_count)
|
692
|
+
end_caret_position = caret_position + selection_count.to_i
|
693
|
+
end_caret_position -= 1 if dirty_content[end_caret_position - 1] == "\n"
|
694
|
+
end_line_index = line_index_for_caret_position(end_caret_position)
|
695
|
+
end
|
696
|
+
|
697
|
+
def lines_for_selection(caret_position, selection_count)
|
698
|
+
line_indices = line_indices_for_selection(caret_position, selection_count)
|
699
|
+
lines[line_indices.first..line_indices.last]
|
700
|
+
end
|
701
|
+
|
702
|
+
def line_indices_for_selection(caret_position, selection_count)
|
703
|
+
start_line_index = line_index_for_caret_position(caret_position)
|
704
|
+
if selection_count.to_i > 0
|
705
|
+
end_line_index = end_caret_position_line_index(caret_position, selection_count)
|
706
|
+
else
|
707
|
+
end_line_index = start_line_index
|
708
|
+
end
|
709
|
+
(start_line_index..end_line_index).to_a
|
710
|
+
end
|
711
|
+
|
712
|
+
def children
|
713
|
+
[]
|
714
|
+
end
|
715
|
+
|
716
|
+
def to_s
|
717
|
+
path
|
718
|
+
end
|
719
|
+
|
720
|
+
def eql?(other)
|
721
|
+
self.path.eql?(other&.path)
|
722
|
+
end
|
723
|
+
|
724
|
+
def hash
|
725
|
+
self.path.hash
|
726
|
+
end
|
727
|
+
end
|
728
|
+
end
|
729
|
+
end
|