glimmer-cs-gladiator 0.1.2 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
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