glimmer-cs-gladiator 0.1.2 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 62d1ccef9ac560b19d5acff7ff5513145fa26fef6f326a9e5d0679890783a0c6
4
- data.tar.gz: f23b2e85d6158c3e1fa7b3314939042a0fc13a0a2707800ecc3233907ebc4640
3
+ metadata.gz: 18bd05231ab3815f4432ee79395284dbe6e5cd34244b1642b7bff4f7a3921c29
4
+ data.tar.gz: f2b2a7d6faf0154d878a3c25944a9b3a59c256a45feaf5b2b975379b357f598b
5
5
  SHA512:
6
- metadata.gz: 0b46d9f5617ef754cc5b791a8e57b60581a355f6f267300dc3282c8dbe937b78adf6cfe55efb5045dd9d7c46653a5bf0cd7fb79bac6451054c588aef088a7784
7
- data.tar.gz: b4dbe7eabd3345ce74ea585584ba5e0bf6acd6b0192289f885418cc51ef6ec5fa3ab9f98fd5223a89cd0efbe09a00974f5bf4bb3cbc4038a8f3a3ab08e86e903
6
+ metadata.gz: e82f4d0a5306bb03d767fa3dd1f7b23b970ba5fc5fbc87010c3388d2fcaec010134d3780d0fcc7d29b0c85113bb9f5c8daf8ef6284d4fc6b561064ccdd6ba88d
7
+ data.tar.gz: fb7a4b16181113d96478f0458a6abebe7c85c6be6d1b3568b39f412d56e278641a58d61e6aea1c977246aa21433f6084da7787db38ae3ee322d1ff5b1f1109f9
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Gladiator (Glimmer Editor) 0.1.2 - Glimmer Custom Shell
1
+ # Gladiator (Glimmer Editor) 0.1.7 - Glimmer Custom Shell
2
2
  [![Gem Version](https://badge.fury.io/rb/glimmer-cs-gladiator.svg)](http://badge.fury.io/rb/glimmer-cs-gladiator)
3
3
 
4
4
  ![Gladiator](images/glimmer-gladiator.png)
@@ -9,19 +9,29 @@ Gladiator is also a personal tool for shaping an editor exactly the way I like.
9
9
  I leave building truly professional text editors to software tooling experts who would hopefully use Glimmer one day.
10
10
 
11
11
  Gladiator currently supports the following text editing features:
12
- - File explorer navigation to open file
13
- - File lookup by name
14
- - Find & Replace
12
+ - File explorer navigation to open file, rename, delete, add new file, add new directory, or refresh tree (CMD+T)
13
+ - File lookup by name ignoring slashes, underscores, and dots to ease lookup (CMD+R)
14
+ - Watch open file for external changes to automatically refresh in editor
15
+ - Watch project subdirectories for changes to automatically refresh in file explorer/file lookup
16
+ - Find & Replace (CMD+F)
15
17
  - Show Line Numbers
16
- - Jump to Line
17
- - Remember last opened file, caret position, and top line
18
- - Autosave on focus out/quit/open new file
19
- - Watch open file for external changes to reflect in editor
20
- - Watch subdirectories for changes to reflect in file explorer/file lookup
21
- - Duplicate Line(s)
22
- - Kill Line(s)
23
- - Move up one line
24
- - Move down one line
18
+ - Jump to Line (CMD+L)
19
+ - Multiple tab support (CMD+SHIFT+[ & CMD+SHIFT+] for tab navigation. CMD+1-9 to jump to a specific tab)
20
+ - Remember opened tabs, caret position, top line, window size, and window location
21
+ - Autosave on focus out/quit/open new file
22
+ - Duplicate Line(s)/selection (CMD+D)
23
+ - Kill Line(s)/selection (CMD+K)
24
+ - Move line/selection up (CMD+UP)
25
+ - Move line/selection down (CMD+DOWN)
26
+ - Comment/Uncomment line/selection (CMD+/)
27
+ - Indent/Unindent line/selection (CMD+] & CMD+[)
28
+ - Insert/Prefix New Line (CMD+ENTER & CMD+SHIFT+ENTER)
29
+
30
+ ## Platforms
31
+
32
+ - Mac: Gladiator works best on the Mac.
33
+ - Linux: Gladiator works with handicaps on Linux (performing text editing operations causes scroll jitter)
34
+ - Windows: Not tested on Windows yet, but should theoretically work there too.
25
35
 
26
36
  ## Pre-requisites
27
37
 
@@ -50,7 +60,7 @@ To reuse Gladiator as a Glimmer Custom Shell inside another Glimmer application,
50
60
  following to the application's `Gemfile`:
51
61
 
52
62
  ```
53
- gem 'glimmer-cs-gladiator', '0.1.2'
63
+ gem 'glimmer-cs-gladiator', '0.1.7'
54
64
  ```
55
65
 
56
66
  Run:
@@ -3,4 +3,11 @@
3
3
  require 'glimmer/launcher'
4
4
 
5
5
  gladiator_runner = File.expand_path('../gladiator_runner.rb', __FILE__)
6
- Glimmer::Launcher.new([gladiator_runner, '-J-Xrs'] + ARGV).launch
6
+ launcher = Glimmer::Launcher.new([gladiator_runner, '-J-Xrs'] + ARGV)
7
+ launcher.application_paths.to_a.each do |file|
8
+ if file != gladiator_runner
9
+ launcher.application_paths.delete(file)
10
+ ENV['LOCAL_DIR'] ||= file
11
+ end
12
+ end
13
+ launcher.launch
@@ -15,41 +15,66 @@ module Glimmer
15
15
  @thread = Thread.new(@filewatcher) do |fw|
16
16
  fw.watch do |filename, event|
17
17
  if @last_update.nil? || (Time.now.to_f - @last_update) > REFRESH_DELAY
18
- dir.refresh if filename != dir.selected_child_path
18
+ dir.refresh if !filename.include?('new_file') && !dir.selected_child_path_history.include?(filename) && filename != dir.selected_child_path
19
19
  end
20
20
  @last_update = Time.now.to_f
21
21
  end
22
22
  end
23
23
  end
24
- end
24
+ end
25
25
  end
26
26
 
27
- attr_accessor :selected_child, :filter, :children, :filtered_path_options
28
- attr_reader :path, :display_path
27
+ attr_accessor :selected_child, :filter, :children, :filtered_path_options, :filtered_path, :path, :display_path
28
+ attr_reader :name, :parent
29
+ attr_writer :all_children, :children
29
30
 
30
31
  def initialize(path)
31
- @path = @display_path = path
32
+ @display_path = path
33
+ @path = ::File.expand_path(@display_path)
34
+ @name = ::File.basename(::File.expand_path(path))
32
35
  self.filtered_path_options = []
33
36
  end
37
+
38
+ def name=(the_name)
39
+ self.display_path = display_path.sub(/#{Regexp.escape(@name)}$/, the_name)
40
+ @name = the_name
41
+ new_path = ::File.expand_path(display_path)
42
+ FileUtils.mv(path, new_path)
43
+ self.path = display_path
44
+ end
34
45
 
35
46
  def children
36
47
  @children ||= retrieve_children
37
48
  end
38
-
49
+
39
50
  def retrieve_children
40
- ::Dir.glob(::File.join(@path, '*')).map {|p| ::File.file?(p) ? Gladiator::File.new(p) : Gladiator::Dir.new(p)}.sort_by {|c| c.path.to_s.downcase }.sort_by {|c| c.class.name }
51
+ ::Dir.glob(::File.join(@display_path, '*')).map {|p| ::File.file?(p) ? Gladiator::File.new(p) : Gladiator::Dir.new(p)}.sort_by {|c| c.path.to_s.downcase }.sort_by {|c| c.class.name }
41
52
  end
42
53
 
43
- def refresh
54
+ def selected_child_path_history
55
+ @selected_child_path_history ||= []
56
+ end
57
+
58
+ def pause_refresh
59
+ @refresh_paused = true
60
+ end
61
+
62
+ def resume_refresh
63
+ @refresh_paused = false
64
+ end
65
+
66
+ def refresh(async: true, force: false)
67
+ return if @refresh_paused && !force
44
68
  new_all_children = retrieve_all_children
45
- new_children = retrieve_children
46
- async_exec do
47
- @all_children = new_all_children
48
- @children ||= []
49
- @children.clear
50
- new_children.each do |child|
51
- @children << child
52
- end
69
+ new_children = retrieve_children
70
+ refresh_operation = lambda do
71
+ self.all_children = new_all_children
72
+ self.children = new_children
73
+ end
74
+ if async
75
+ async_exec(&refresh_operation)
76
+ else
77
+ sync_exec(&refresh_operation)
53
78
  end
54
79
  end
55
80
 
@@ -66,7 +91,7 @@ module Glimmer
66
91
  return if filter.nil?
67
92
  all_children_files.select do |child|
68
93
  child.path.downcase.include?(filter.downcase) ||
69
- child.path.downcase.gsub(/[_\/]/, '').include?(filter.downcase)
94
+ child.path.downcase.gsub(/[_\/\.]/, '').include?(filter.downcase)
70
95
  end.sort_by {|c| c.path.to_s.downcase}
71
96
  end
72
97
 
@@ -75,7 +100,7 @@ module Glimmer
75
100
  end
76
101
 
77
102
  def retrieve_all_children
78
- ::Dir.glob(::File.join(@path, '**', '*')).map {|p| ::File.file?(p) ? Gladiator::File.new(p) : Gladiator::Dir.new(p)}
103
+ ::Dir.glob(::File.join(@display_path, '**', '*')).map {|p| ::File.file?(p) ? Gladiator::File.new(p) : Gladiator::Dir.new(p)}
79
104
  end
80
105
 
81
106
  def all_children_files
@@ -83,31 +108,46 @@ module Glimmer
83
108
  end
84
109
 
85
110
  def selected_child_path=(selected_path)
86
- if selected_path && ::File.file?(selected_path)
111
+ return if selected_path.nil? ||
112
+ ::Dir.exist?(selected_path) ||
113
+ (selected_child && ::File.expand_path(selected_child.path) == ::File.expand_path(selected_path))
114
+ if ::File.file?(selected_path)
87
115
  @selected_child&.write_dirty_content
88
116
  new_child = Gladiator::File.new(selected_path)
89
117
  begin
90
118
  unless new_child.dirty_content.nil?
91
119
  self.selected_child&.stop_filewatcher
120
+ selected_child_path_history << new_child.path if new_child && !selected_child_path_history.include?(new_child.path)
92
121
  self.selected_child = new_child
93
122
  self.selected_child.start_filewatcher
94
123
  end
95
124
  rescue
96
125
  # no op
97
126
  end
127
+ else
128
+ refresh
98
129
  end
99
130
  end
100
131
 
101
132
  def selected_child_path
102
133
  @selected_child&.path
103
134
  end
104
-
105
- alias filtered_path selected_child_path
106
- alias filtered_path= selected_child_path=
135
+
136
+ def delete!
137
+ FileUtils.rm_rf(path)
138
+ end
107
139
 
108
140
  def to_s
109
141
  path
110
142
  end
143
+
144
+ def eql?(other)
145
+ self.path.eql?(other&.path)
146
+ end
147
+
148
+ def hash
149
+ self.path.hash
150
+ end
111
151
  end
112
152
  end
113
153
  end
@@ -5,13 +5,17 @@ module Glimmer
5
5
  class File
6
6
  include Glimmer
7
7
 
8
- attr_accessor :dirty_content, :line_numbers_content, :caret_position, :selection_count, :line_number, :find_text, :replace_text, :top_index
9
- attr_reader :path, :display_path
8
+ attr_accessor :line_numbers_content, :selection, :selection_count, :line_number, :find_text, :replace_text, :top_index, :path, :display_path
9
+ attr_reader :name, :dirty_content
10
10
 
11
11
  def initialize(path)
12
12
  raise "Not a file path: #{path}" unless ::File.file?(path)
13
13
  @display_path = path
14
+ @name = ::File.basename(path)
14
15
  @path = ::File.expand_path(path)
16
+ @top_index = 0
17
+ @selection_count = 0
18
+ @selection = Point.new(0, 0 + @selection_count)
15
19
  read_dirty_content = ::File.read(path)
16
20
  begin
17
21
  # test read dirty content
@@ -20,21 +24,47 @@ module Glimmer
20
24
  lines_text_size = lines.size.to_s.size
21
25
  self.line_numbers_content = lines.size.times.map {|n| (' ' * (lines_text_size - (n+1).to_s.size)) + (n+1).to_s }.join("\n")
22
26
  end
27
+ @line_number = 1
23
28
  self.dirty_content = read_dirty_content
24
- observe(self, :caret_position) do
29
+ observe(self, :selection) do
25
30
  self.line_number = line_index_for_caret_position(caret_position) + 1
26
31
  end
27
32
  observe(self, :line_number) do
28
33
  if line_number
29
34
  line_index = line_number - 1
30
35
  new_caret_position = caret_position_for_line_index(line_index)
31
- self.caret_position = new_caret_position unless line_index_for_caret_position(new_caret_position) == line_index_for_caret_position(caret_position)
36
+ self.caret_position = new_caret_position unless self.caret_position && line_index_for_caret_position(new_caret_position) == line_index_for_caret_position(caret_position)
32
37
  end
33
38
  end
34
39
  rescue
35
40
  # no op in case of a binary file
36
41
  end
37
42
  end
43
+
44
+ def caret_position=(value)
45
+ self.selection = Point.new(value, value + selection_count.to_i)
46
+ if OS.linux?
47
+ async_exec do
48
+ self.top_index = line_index_for_caret_position(value)
49
+ end
50
+ end
51
+ end
52
+
53
+ def caret_position
54
+ selection.x
55
+ end
56
+
57
+ def name=(the_name)
58
+ self.display_path = display_path.sub(/#{Regexp.escape(@name)}$/, the_name)
59
+ @name = the_name
60
+ new_path = ::File.expand_path(display_path)
61
+ FileUtils.mv(path, new_path)
62
+ self.path = new_path
63
+ end
64
+
65
+ def dirty_content=(the_content)
66
+ @dirty_content = the_content if ::File.exist?(path)
67
+ end
38
68
 
39
69
  def start_filewatcher
40
70
  @filewatcher = Filewatcher.new(@path)
@@ -57,23 +87,55 @@ module Glimmer
57
87
  def stop_filewatcher
58
88
  @filewatcher&.stop
59
89
  end
60
-
61
- def write_dirty_content
90
+
91
+ def format_dirty_content_for_writing!
62
92
  new_dirty_content = "#{dirty_content.gsub("\r\n", "\n").gsub("\r", "\n").sub(/\n+\z/, '')}\n"
63
93
  self.dirty_content = new_dirty_content if new_dirty_content != self.dirty_content
64
- ::File.write(path, dirty_content) if ::File.exists?(path) && dirty_content.to_s.strip.size > 0
94
+ end
95
+
96
+ def write_dirty_content
97
+ return unless ::File.exist?(path)
98
+ format_dirty_content_for_writing!
99
+ ::File.write(path, dirty_content) if ::File.exists?(path)
65
100
  rescue => e
66
101
  puts "Error in writing dirty content for #{path}"
67
102
  puts e.full_message
68
103
  end
69
104
 
70
105
  def write_raw_dirty_content
71
- ::File.write(path, dirty_content) if ::File.exists?(path) && dirty_content.to_s.strip.size > 0
106
+ return unless ::File.exist?(path)
107
+ ::File.write(path, dirty_content) if ::File.exists?(path)
72
108
  rescue => e
73
109
  puts "Error in writing raw dirty content for #{path}"
74
110
  puts e.full_message
75
111
  end
76
-
112
+
113
+ def current_line_indentation
114
+ current_line.to_s.match(/^(\s+)/).to_a[1].to_s
115
+ end
116
+
117
+ def current_line
118
+ lines[line_number - 1]
119
+ end
120
+
121
+ def delete!
122
+ FileUtils.rm(path)
123
+ end
124
+
125
+ def prefix_new_line!
126
+ the_lines = lines
127
+ the_lines[line_number-1...line_number-1] = [current_line_indentation]
128
+ self.dirty_content = the_lines.join("\n")
129
+ self.caret_position = caret_position_for_line_index(line_number-1) + current_line_indentation.size
130
+ end
131
+
132
+ def insert_new_line!
133
+ the_lines = lines
134
+ the_lines[line_number...line_number] = [current_line_indentation]
135
+ self.dirty_content = the_lines.join("\n")
136
+ self.caret_position = caret_position_for_line_index(line_number) + current_line_indentation.size
137
+ end
138
+
77
139
  def comment_line!
78
140
  old_lines = lines
79
141
  return if old_lines.size < 1
@@ -99,7 +161,7 @@ module Glimmer
99
161
  end
100
162
  end
101
163
  self.dirty_content = new_lines.join("\n")
102
- if old_selection_count > 0
164
+ if old_selection_count.to_i > 0
103
165
  self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
104
166
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
105
167
  else
@@ -125,7 +187,7 @@ module Glimmer
125
187
  end
126
188
  old_caret_position = self.caret_position
127
189
  self.dirty_content = new_lines.join("\n")
128
- if old_selection_count > 0
190
+ if old_selection_count.to_i > 0
129
191
  self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
130
192
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
131
193
  else
@@ -145,7 +207,7 @@ module Glimmer
145
207
  delta = 0
146
208
  line_indices_for_selection(caret_position, selection_count).each do |the_line_index|
147
209
  the_line = old_lines[the_line_index]
148
- if the_line.start_with?(' ')
210
+ if the_line.to_s.start_with?(' ')
149
211
  new_lines[the_line_index] = the_line.sub(/ /, '')
150
212
  delta = -2
151
213
  elsif the_line.start_with?(' ')
@@ -154,7 +216,7 @@ module Glimmer
154
216
  end
155
217
  end
156
218
  self.dirty_content = new_lines.join("\n")
157
- if old_selection_count > 0
219
+ if old_selection_count.to_i > 0
158
220
  self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
159
221
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
160
222
  else
@@ -166,12 +228,15 @@ module Glimmer
166
228
 
167
229
  def kill_line!
168
230
  new_lines = lines
169
- return if new_lines.size < 2
231
+ return if new_lines.size < 1
170
232
  line_indices = line_indices_for_selection(caret_position, selection_count)
171
233
  new_lines = new_lines[0...line_indices.first] + new_lines[(line_indices.last+1)...new_lines.size]
172
234
  old_caret_position = self.caret_position
173
- self.dirty_content = new_lines.join("\n")
174
- self.caret_position = old_caret_position
235
+ old_line_index = self.line_number - 1
236
+ line_position = line_position_for_caret_position(old_caret_position)
237
+ self.dirty_content = "#{new_lines.join("\n")}\n"
238
+ self.caret_position = caret_position_for_line_index(old_line_index) + [line_position, lines[old_line_index].to_s.size].min
239
+ self.selection_count = 0
175
240
  end
176
241
 
177
242
  def duplicate_line!
@@ -190,7 +255,7 @@ module Glimmer
190
255
  new_lines.insert(the_line_indices.first + i, the_line)
191
256
  end
192
257
  self.dirty_content = new_lines.join("\n")
193
- if old_selection_count > 0
258
+ if old_selection_count.to_i > 0
194
259
  self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
195
260
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
196
261
  else
@@ -202,12 +267,21 @@ module Glimmer
202
267
  return if find_text.to_s.empty?
203
268
  all_lines = lines
204
269
  the_line_index = line_index_for_caret_position(caret_position)
205
- all_lines.rotate(the_line_index + 1).each_with_index do |the_line, the_index|
206
- the_index = (the_index + the_line_index + 1)%all_lines.size
207
- if the_line.downcase.include?(find_text.to_s.downcase)
208
- self.caret_position = the_line.downcase.index(find_text.to_s.downcase) + caret_position_for_line_index(the_index)
209
- self.selection_count = find_text.to_s.size
210
- return
270
+ line_position = line_position_for_caret_position(caret_position)
271
+ found =
272
+ 2.times do |i|
273
+ rotation = the_line_index
274
+ all_lines.rotate(rotation).each_with_index do |the_line, the_index|
275
+ the_index = (the_index + rotation)%all_lines.size
276
+ start_position = 0
277
+ start_position = line_position + find_text.to_s.size if i == 0 && the_index == the_line_index && found_text?(caret_position)
278
+ text_to_find_in = the_line.downcase[start_position..-1]
279
+ occurrence_index = text_to_find_in&.index(find_text.to_s.downcase)
280
+ if occurrence_index
281
+ self.caret_position = caret_position_for_line_index(the_index) + start_position + occurrence_index
282
+ self.selection_count = find_text.to_s.size
283
+ return
284
+ end
211
285
  end
212
286
  end
213
287
  end
@@ -216,21 +290,34 @@ module Glimmer
216
290
  return if find_text.to_s.empty?
217
291
  all_lines = lines
218
292
  the_line_index = line_index_for_caret_position(caret_position)
219
- all_lines.rotate(the_line_index).each_with_index.map do |the_line, the_index|
220
- the_index = (the_index + the_line_index)%all_lines.size
221
- [the_line, the_index]
222
- end.reverse.each do |the_line, the_index|
223
- if the_line.downcase.include?(find_text.to_s.downcase)
224
- self.caret_position = the_line.downcase.index(find_text.to_s.downcase) + caret_position_for_line_index(the_index)
225
- self.selection_count = find_text.to_s.size
226
- return
293
+ line_position = line_position_for_caret_position(caret_position)
294
+ 2.times do |i|
295
+ rotation = - the_line_index - 1 + all_lines.size
296
+ all_lines.reverse.rotate(rotation).each_with_index do |the_line, the_index|
297
+ the_index = all_lines.size - 1 - (the_index + rotation)%all_lines.size
298
+ if the_index == the_line_index
299
+ start_position = i > 0 ? 0 : (the_line.size - line_position)
300
+ else
301
+ start_position = 0
302
+ end
303
+ text_to_find_in = the_line.downcase.reverse[start_position...the_line.size].to_s
304
+ occurrence_index = text_to_find_in.index(find_text.to_s.downcase.reverse)
305
+ if occurrence_index
306
+ self.caret_position = caret_position_for_line_index(the_index) + (the_line.size - (start_position + occurrence_index + find_text.to_s.size))
307
+ self.selection_count = find_text.to_s.size
308
+ return
309
+ end
227
310
  end
228
311
  end
229
312
  end
230
313
 
231
314
  def ensure_find_next
232
315
  return if find_text.to_s.empty? || dirty_content.to_s.strip.size < 1
233
- find_next unless dirty_content[caret_position.to_i, find_text.to_s.size] == find_text
316
+ find_next unless found_text?(self.caret_position)
317
+ end
318
+
319
+ def found_text?(caret_position)
320
+ dirty_content[caret_position.to_i, find_text.to_s.size].to_s.downcase == find_text.to_s.downcase
234
321
  end
235
322
 
236
323
  def replace_next!
@@ -243,10 +330,12 @@ module Glimmer
243
330
  end
244
331
 
245
332
  def page_up
333
+ self.selection_count = 0
246
334
  self.line_number = [(self.line_number - 15), 1].max
247
335
  end
248
336
 
249
337
  def page_down
338
+ self.selection_count = 0
250
339
  self.line_number = [(self.line_number + 15), lines.size].min
251
340
  end
252
341
 
@@ -263,23 +352,18 @@ module Glimmer
263
352
  return if old_lines.size < 2
264
353
  old_selection_count = self.selection_count
265
354
  old_caret_position = self.caret_position
266
- old_caret_position_line_index = line_index_for_caret_position(old_caret_position)
267
- old_caret_position_line_caret_position = caret_position_for_caret_position_start_of_line(old_caret_position_line_index)
355
+ old_caret_position_line_caret_position = caret_position_for_caret_position_start_of_line(old_caret_position)
356
+ old_caret_position_line_position = old_caret_position - old_caret_position_line_caret_position
268
357
  old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
269
358
  new_lines = lines
270
359
  the_line_indices = line_indices_for_selection(caret_position, selection_count)
271
360
  the_lines = lines_for_selection(caret_position, selection_count)
272
361
  new_line_index = [the_line_indices.first - 1, 0].max
273
- delta = -1 * (new_lines[new_line_index].size + 1)
274
362
  new_lines[the_line_indices.first..the_line_indices.last] = []
275
363
  new_lines[new_line_index...new_line_index] = the_lines
276
364
  self.dirty_content = new_lines.join("\n")
277
- if old_selection_count > 0
278
- self.caret_position = caret_position_for_line_index(old_caret_position_line_index) + delta
279
- self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position + delta)
280
- else
281
- self.caret_position = old_caret_position + delta
282
- end
365
+ self.caret_position = caret_position_for_line_index(new_line_index) + [old_caret_position_line_position, new_lines[new_line_index].size].min
366
+ self.selection_count = old_selection_count.to_i if old_selection_count.to_i > 0
283
367
  end
284
368
 
285
369
  def move_down!
@@ -287,23 +371,18 @@ module Glimmer
287
371
  return if old_lines.size < 2
288
372
  old_selection_count = self.selection_count
289
373
  old_caret_position = self.caret_position
290
- old_caret_position_line_index = line_index_for_caret_position(old_caret_position)
291
- old_caret_position_line_caret_position = caret_position_for_caret_position_start_of_line(old_caret_position_line_index)
374
+ old_caret_position_line_caret_position = caret_position_for_caret_position_start_of_line(old_caret_position)
375
+ old_caret_position_line_position = old_caret_position - old_caret_position_line_caret_position
292
376
  old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
293
377
  new_lines = lines
294
378
  the_line_indices = line_indices_for_selection(caret_position, selection_count)
295
379
  the_lines = lines_for_selection(caret_position, selection_count)
296
380
  new_line_index = [the_line_indices.first + 1, new_lines.size - 1].min
297
- delta = new_lines[new_line_index].size + 1
298
381
  new_lines[the_line_indices.first..the_line_indices.last] = []
299
382
  new_lines[new_line_index...new_line_index] = the_lines
300
383
  self.dirty_content = new_lines.join("\n")
301
- if old_selection_count > 0
302
- self.caret_position = caret_position_for_line_index(old_caret_position_line_index) + delta
303
- self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position + delta)
304
- else
305
- self.caret_position = old_caret_position + delta
306
- end
384
+ self.caret_position = caret_position_for_line_index(new_line_index) + [old_caret_position_line_position, new_lines[new_line_index].size].min
385
+ self.selection_count = old_selection_count.to_i if old_selection_count.to_i > 0
307
386
  end
308
387
 
309
388
  def lines
@@ -327,6 +406,13 @@ module Glimmer
327
406
  def caret_position_for_caret_position_start_of_line(caret_position)
328
407
  caret_position_for_line_index(line_index_for_caret_position(caret_position))
329
408
  end
409
+
410
+ # 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)
411
+ # TODO consider renaming to line_character_position_for_caret_position
412
+ def line_position_for_caret_position(caret_position)
413
+ caret_position = caret_position.to_i
414
+ caret_position - caret_position_for_caret_position_start_of_line(caret_position)
415
+ end
330
416
 
331
417
  def line_caret_positions_for_selection(caret_position, selection_count)
332
418
  line_indices = line_indices_for_selection(caret_position, selection_count)
@@ -361,6 +447,14 @@ module Glimmer
361
447
  def to_s
362
448
  path
363
449
  end
450
+
451
+ def eql?(other)
452
+ self.path.eql?(other&.path)
453
+ end
454
+
455
+ def hash
456
+ self.path.hash
457
+ end
364
458
  end
365
459
  end
366
460
  end