glimmer-cs-gladiator 0.5.3 → 0.6.3

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.
@@ -1,6 +1,8 @@
1
1
  module Glimmer
2
2
  class Gladiator
3
3
  class Command
4
+ include Glimmer
5
+
4
6
  class << self
5
7
  include Glimmer
6
8
 
@@ -13,11 +15,18 @@ module Glimmer
13
15
  command_history[file] ||= [Command.new(file)]
14
16
  end
15
17
 
16
- def do(file, method = nil, command: nil)
17
- command ||= Command.new(file, method)
18
- command_history_for(file)&.last&.next_command = command
19
- command.do
20
- command_history_for(file) << command
18
+ def do(file, method = nil, *args, command: nil)
19
+ if command.nil?
20
+ command ||= Command.new(file, method, *args)
21
+ command.previous_command = command_history_for(file).last
22
+ unless command_history_for(file).last.method == :change_content! && method == :change_content!
23
+ command_history_for(file).last.next_command = command
24
+ end
25
+ command.do
26
+ command_history_for(file) << command unless command_history_for(file).last.method == :change_content! && method == :change_content!
27
+ else
28
+ command_history_for(file) << command
29
+ end
21
30
  end
22
31
 
23
32
  def undo(file)
@@ -30,15 +39,22 @@ module Glimmer
30
39
  command = command_history_for(file).last
31
40
  command&.redo
32
41
  end
42
+
43
+ def clear(file)
44
+ command_history[file] = [Command.new(file)]
45
+ end
33
46
  end
34
47
 
35
- attr_accessor :file, :method, :next_command, :previous_file_content, :previous_file_caret_position, :previous_file_selection_count
48
+ attr_accessor :file, :method, :args, :previous_command, :next_command,
49
+ :file_dirty_content, :file_caret_position, :file_selection_count, :previous_file_dirty_content, :previous_file_caret_position, :previous_file_selection_count
36
50
 
37
- def initialize(file, method = nil)
51
+ def initialize(file, method = nil, *args)
38
52
  @file = file
39
53
  @method = method
54
+ @args = args
40
55
  end
41
56
 
57
+
42
58
  def native?
43
59
  @method.nil?
44
60
  end
@@ -56,6 +72,9 @@ module Glimmer
56
72
 
57
73
  def redo
58
74
  return if next_command.nil?# || next_command.native?
75
+ @file.dirty_content = next_command.file_dirty_content.clone
76
+ @file.caret_position = next_command.file_caret_position
77
+ @file.selection_count = next_command.file_selection_count
59
78
  Command.do(next_command.file, command: next_command)
60
79
  end
61
80
 
@@ -63,6 +82,10 @@ module Glimmer
63
82
  @previous_file_dirty_content = @file.dirty_content.clone
64
83
  @previous_file_caret_position = @file.caret_position
65
84
  @previous_file_selection_count = @file.selection_count
85
+ if @method == :change_content!
86
+ @previous_file_caret_position = @file.last_caret_position
87
+ @previous_file_selection_count = @file.last_selection_count
88
+ end
66
89
  end
67
90
 
68
91
  def restore
@@ -72,7 +95,17 @@ module Glimmer
72
95
  end
73
96
 
74
97
  def execute
75
- @file.send(@method)
98
+ @file.start_command
99
+ @file.send(@method, *@args)
100
+ @file.end_command
101
+ @file_dirty_content = @file.dirty_content.clone
102
+ @file_caret_position = @file.caret_position
103
+ @file_selection_count = @file.selection_count
104
+ if previous_command.method == :change_content! && @method == :change_content!
105
+ previous_command.file_dirty_content = @file_dirty_content
106
+ previous_command.file_caret_position = @file_caret_position
107
+ previous_command.file_selection_count = @file_selection_count
108
+ end
76
109
  end
77
110
  end
78
111
  end
@@ -6,8 +6,6 @@ module Glimmer
6
6
  include Glimmer
7
7
  include Glimmer::DataBinding::ObservableModel
8
8
 
9
- REFRESH_DELAY = 7
10
-
11
9
  attr_accessor :selected_child, :filter, :children, :filtered_path_options, :filtered_path, :display_path, :ignore_paths
12
10
  attr_reader :name, :parent, :path
13
11
  attr_writer :all_children
@@ -15,19 +13,30 @@ module Glimmer
15
13
  def initialize(path, project_dir = nil)
16
14
  @project_dir = project_dir
17
15
  if is_local_dir
18
- @filewatcher = Filewatcher.new(path)
19
- @thread = Thread.new(@filewatcher) do |fw|
20
- fw.watch do |filename, event|
21
- if @last_update.nil? || (Time.now.to_f - @last_update) > REFRESH_DELAY
22
- refresh if !filename.include?('new_file') && !selected_child_path_history.include?(filename) && filename != selected_child_path
23
- end
24
- @last_update = Time.now.to_f
25
- end
26
- end
16
+ @filewatcher = Filewatcher.new(path)
17
+ Thread.new(@filewatcher) do |fw|
18
+ begin
19
+ fw.watch do |filename, event|
20
+ # TODO do fine grained processing of events for enhanced performance (e.g. dir refresh vs file change)
21
+ # TODO do fine grained file change only without a refresh delay for enhanced performance
22
+ begin
23
+ if !@refresh_in_progress && !filename.include?('new_file') && (event != :updated || find_child_file(filename).nil?)
24
+ Thread.new {
25
+ refresh
26
+ }
27
+ end
28
+ rescue => e
29
+ puts e.full_message
30
+ end
31
+ end
32
+ rescue => e
33
+ puts e.full_message
34
+ end
35
+ end
27
36
  end
28
37
  self.path = ::File.expand_path(path)
29
38
  @name = ::File.basename(::File.expand_path(path))
30
- @ignore_paths = ['.gladiator', '.git', 'coverage', 'packages', 'tmp', 'vendor']
39
+ @ignore_paths = ['.gladiator', '.git', 'coverage', 'packages', 'node_modules', 'tmp', 'vendor']
31
40
  self.filtered_path_options = []
32
41
  end
33
42
 
@@ -67,7 +76,7 @@ module Glimmer
67
76
  result || p.include?(ignore_path)
68
77
  end
69
78
  end.map do |p|
70
- ::File.file?(p) ? Gladiator::File.new(p, project_dir) : Gladiator::Dir.new(p, project_dir)
79
+ ::File.file?(p) ? File.new(p, project_dir) : Dir.new(p, project_dir)
71
80
  end.sort_by do |c|
72
81
  c.path.to_s.downcase
73
82
  end.sort_by do |c|
@@ -76,6 +85,22 @@ module Glimmer
76
85
  child.retrieve_children if child.is_a?(Dir)
77
86
  end
78
87
  end
88
+
89
+ def find_child_file(child_path)
90
+ depth_first_search_file(self, child_path)
91
+ end
92
+
93
+ def depth_first_search_file(dir, file_path)
94
+ dir.children.each do |child|
95
+ if child.is_a?(File)
96
+ return child if child.path.include?(file_path)
97
+ else
98
+ result = depth_first_search_file(child, file_path)
99
+ return result unless result.nil?
100
+ end
101
+ end
102
+ nil
103
+ end
79
104
 
80
105
  def selected_child_path_history
81
106
  @selected_child_path_history ||= []
@@ -91,6 +116,7 @@ module Glimmer
91
116
 
92
117
  def refresh(async: true, force: false)
93
118
  return if @refresh_paused && !force
119
+ @refresh_in_progress = true
94
120
  retrieve_children
95
121
  collect_all_children
96
122
  refresh_operation = lambda do
@@ -102,6 +128,7 @@ module Glimmer
102
128
  else
103
129
  sync_exec(&refresh_operation)
104
130
  end
131
+ @refresh_in_progress = false
105
132
  end
106
133
 
107
134
  def filter=(value)
@@ -144,7 +171,7 @@ module Glimmer
144
171
  def selected_child_path=(selected_path)
145
172
  return (project_dir.selected_child = nil) if selected_path.nil?
146
173
  # scratchpad scenario
147
- if selected_path.empty? #scratchpad
174
+ if selected_path.empty? # Scratchpad
148
175
  @selected_child&.write_dirty_content
149
176
  return (self.selected_child = File.new)
150
177
  end
@@ -154,7 +181,7 @@ module Glimmer
154
181
  selected_path = full_selected_path
155
182
  if ::File.file?(selected_path)
156
183
  @selected_child&.write_dirty_content
157
- new_child = Gladiator::File.new(selected_path, project_dir)
184
+ new_child = find_child_file(selected_path)
158
185
  begin
159
186
  unless new_child.dirty_content.nil?
160
187
  self.selected_child&.stop_filewatcher
@@ -175,6 +202,7 @@ module Glimmer
175
202
  end
176
203
 
177
204
  def selected_child=(new_child)
205
+ return if selected_child == new_child
178
206
  file_properties = @selected_child&.backup_properties if @selected_child == new_child
179
207
  @selected_child = new_child
180
208
  @selected_child.restore_properties(file_properties) if file_properties
@@ -191,7 +219,7 @@ module Glimmer
191
219
  def eql?(other)
192
220
  self.path.eql?(other&.path)
193
221
  end
194
-
222
+
195
223
  def hash
196
224
  self.path.hash
197
225
  end
@@ -3,7 +3,7 @@ module Glimmer
3
3
  class File
4
4
  include Glimmer
5
5
 
6
- attr_accessor :dirty_content, :line_numbers_content, :selection, :line_number, :find_text, :replace_text, :top_pixel, :display_path, :case_sensitive
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
7
  attr_reader :name, :path, :project_dir
8
8
 
9
9
  def initialize(path='', project_dir=nil)
@@ -13,33 +13,55 @@ module Glimmer
13
13
  @name = path.empty? ? 'Scratchpad' : ::File.basename(path)
14
14
  self.path = ::File.expand_path(path) unless path.empty?
15
15
  @top_pixel = 0
16
+ @caret_position = 0
16
17
  @selection_count = 0
17
- @selection = Point.new(0, 0 + @selection_count)
18
- read_dirty_content = path.empty? ? '' : ::File.read(path)
19
- begin
20
- # test read dirty content
21
- read_dirty_content.split("\n")
22
- observe(self, :dirty_content) do
23
- lines_text_size = lines.size.to_s.size
24
- self.line_numbers_content = lines.size.times.map {|n| (' ' * (lines_text_size - (n+1).to_s.size)) + (n+1).to_s }.join("\n")
25
- end
26
- @line_number = 1
27
- self.dirty_content = read_dirty_content
28
- observe(self, :selection) do
29
- self.line_number = line_index_for_caret_position(caret_position) + 1
30
- end
31
- observe(self, :line_number) do
32
- if line_number
18
+ @last_selection_count = 0
19
+ @line_number = 1
20
+ @init = nil
21
+ end
22
+
23
+ def init_content
24
+ unless @init
25
+ @init = true
26
+ begin
27
+ # test read dirty content
28
+ observe(self, :dirty_content) do
29
+ line_count = lines.empty? ? 1 : lines.size
30
+ lines_text_size = [line_count.to_s.size, 4].max
31
+ old_top_pixel = top_pixel
32
+ self.line_numbers_content = line_count.times.map {|n| (' ' * (lines_text_size - (n+1).to_s.size)) + (n+1).to_s }.join("\n")
33
+ self.top_pixel = old_top_pixel
34
+ end
35
+ the_dirty_content = read_dirty_content
36
+ the_dirty_content.split("\n") # test that it is not a binary file (crashes to rescue block otherwise)
37
+ self.dirty_content = the_dirty_content
38
+ observe(self, :caret_position) do |new_caret_position|
39
+ update_line_number_from_caret_position(new_caret_position)
40
+ end
41
+ observe(self, :line_number) do |new_line_number|
33
42
  line_index = line_number - 1
34
43
  new_caret_position = caret_position_for_line_index(line_index)
35
- 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)
44
+ current_caret_position = caret_position
45
+ line_index_for_new_caret_position = line_index_for_caret_position(new_caret_position)
46
+ line_index_for_current_caret_position = line_index_for_caret_position(current_caret_position)
47
+ self.caret_position = new_caret_position unless (current_caret_position && line_index_for_new_caret_position == line_index_for_current_caret_position)
36
48
  end
49
+ rescue # in case of a binary file
50
+ stop_filewatcher
37
51
  end
38
- rescue
39
- # no op in case of a binary file
40
52
  end
41
53
  end
42
-
54
+
55
+ def update_line_number_from_caret_position(new_caret_position)
56
+ new_line_number = line_index_for_caret_position(caret_position) + 1
57
+ current_line_number = line_number
58
+ unless (current_line_number && current_line_number == new_line_number)
59
+ self.line_number = new_line_number
60
+ # TODO check if the following line is needed
61
+ self.line_position = caret_position - caret_position_for_line_index(line_number - 1) + 1
62
+ end
63
+ end
64
+
43
65
  def path=(the_path)
44
66
  @path = the_path
45
67
  generate_display_path
@@ -49,6 +71,15 @@ module Glimmer
49
71
  return if @path.empty?
50
72
  @display_path = @path.sub(project_dir.path, '').sub(/^\//, '')
51
73
  end
74
+
75
+ def name=(the_name)
76
+ new_path = path.sub(/#{Regexp.escape(@name)}$/, the_name) unless scratchpad?
77
+ @name = the_name
78
+ if !scratchpad? && ::File.exist?(path)
79
+ FileUtils.mv(path, new_path)
80
+ self.path = new_path
81
+ end
82
+ end
52
83
 
53
84
  def scratchpad?
54
85
  path.to_s.empty?
@@ -66,50 +97,74 @@ module Glimmer
66
97
  send("#{property}=", value)
67
98
  end
68
99
  end
100
+
101
+ def caret_position=(value)
102
+ @last_caret_position = @caret_position
103
+ @caret_position = value
104
+ end
69
105
 
70
- # to use for widget data-binding
71
- def content=(value)
72
- value = value.gsub("\t", ' ')
73
- if dirty_content != value
74
- Command.do(self) # record a native (OS-widget) operation
75
- self.dirty_content = value
76
- end
106
+ def selection_count=(value)
107
+ #@last_selection_count = @selection_count
108
+ @selection_count = value
109
+ @last_selection_count = @selection_count
77
110
  end
78
111
 
112
+ def dirty_content
113
+ init_content
114
+ @dirty_content
115
+ end
116
+
117
+ def dirty_content=(the_content)
118
+ # TODO set partial dirty content by line(s) for enhanced performance
119
+ @dirty_content = the_content
120
+ old_caret_position = caret_position
121
+ old_top_pixel = top_pixel
122
+
123
+ notify_observers(:content)
124
+ if @formatting_dirty_content_for_writing
125
+ self.caret_position = old_caret_position
126
+ self.top_pixel = old_top_pixel
127
+ end
128
+ end
129
+
79
130
  def content
80
131
  dirty_content
81
132
  end
82
133
 
83
- def caret_position=(value)
84
- old_top_pixel = top_pixel
85
- self.selection = Point.new(value, value + selection_count.to_i)
86
- self.top_pixel = old_top_pixel
134
+ # to use for widget data-binding
135
+ def content=(value)
136
+ value = value.gsub("\t", ' ')
137
+ if dirty_content != value
138
+ Command.do(self, :change_content!, value)
139
+ end
87
140
  end
88
-
89
- def caret_position
90
- selection.x
141
+
142
+ def change_content!(value)
143
+ self.dirty_content = value
144
+ update_line_number_from_caret_position(caret_position)
91
145
  end
92
146
 
93
- def selection_count
94
- selection.y - selection.x
147
+ def start_command
148
+ @commmand_in_progress = true
95
149
  end
96
-
97
- def selection_count=(value)
98
- self.selection = Point.new(caret_position, caret_position + value.to_i)
150
+
151
+ def end_command
152
+ @commmand_in_progress = false
99
153
  end
100
-
101
- def name=(the_name)
102
- new_path = path.sub(/#{Regexp.escape(@name)}$/, the_name) unless scratchpad?
103
- @name = the_name
104
- if !scratchpad? && ::File.exist?(path)
105
- FileUtils.mv(path, new_path)
106
- self.path = new_path
107
- end
154
+
155
+ def command_in_progress?
156
+ @commmand_in_progress
108
157
  end
109
158
 
110
- def dirty_content=(the_content)
111
- @dirty_content = the_content
112
- notify_observers(:content)
159
+ def close
160
+ stop_filewatcher
161
+ remove_all_observers
162
+ initialize(path, project_dir)
163
+ Command.clear(self)
164
+ end
165
+
166
+ def read_dirty_content
167
+ path.empty? ? '' : ::File.read(path)
113
168
  end
114
169
 
115
170
  def start_filewatcher
@@ -117,15 +172,13 @@ module Glimmer
117
172
  @filewatcher = Filewatcher.new(@path)
118
173
  @thread = Thread.new(@filewatcher) do |fw|
119
174
  fw.watch do |filename, event|
120
- begin
121
- read_dirty_content = ::File.read(path)
122
- # test read dirty content
123
- read_dirty_content.split("\n")
124
- async_exec do
175
+ async_exec do
176
+ begin
125
177
  self.dirty_content = read_dirty_content if read_dirty_content != dirty_content
178
+ rescue StandardError, Errno::ENOENT
179
+ # in case of a binary file
180
+ stop_filewatcher
126
181
  end
127
- rescue
128
- # no op in case of a binary file
129
182
  end
130
183
  end
131
184
  end
@@ -135,21 +188,28 @@ module Glimmer
135
188
  @filewatcher&.stop
136
189
  end
137
190
 
138
- def format_dirty_content_for_writing!
139
- new_dirty_content = dirty_content.split("\n").map {|line| line.strip.empty? ? line : line.rstrip }.join("\n")
140
- new_dirty_content = "#{new_dirty_content.gsub("\r\n", "\n").gsub("\r", "\n").sub(/\n+\z/, '')}\n"
141
- self.dirty_content = new_dirty_content if new_dirty_content != self.dirty_content
142
- end
143
-
144
191
  def write_dirty_content
145
- return if scratchpad? || !::File.exist?(path)
192
+ # TODO write partial dirty content by line(s) for enhanced performance
193
+ return if scratchpad? || !::File.exist?(path) || !::File.exists?(path) || read_dirty_content == dirty_content
146
194
  format_dirty_content_for_writing!
147
- ::File.write(path, dirty_content) if ::File.exists?(path)
148
- rescue => e
195
+ ::File.write(path, dirty_content)
196
+ rescue StandardError, ArgumentError => e
149
197
  puts "Error in writing dirty content for #{path}"
150
198
  puts e.full_message
151
199
  end
152
200
 
201
+ def format_dirty_content_for_writing!
202
+ return if @commmand_in_progress
203
+ # TODO f ix c ar e t pos it ion after formatting dirty content (diff?)
204
+ new_dirty_content = dirty_content.to_s.split("\n").map {|line| line.strip.empty? ? line : line.rstrip }.join("\n")
205
+ new_dirty_content = "#{new_dirty_content.gsub("\r\n", "\n").gsub("\r", "\n").sub(/\n+\z/, '')}\n"
206
+ if new_dirty_content != self.dirty_content
207
+ @formatting_dirty_content_for_writing = true
208
+ self.dirty_content = new_dirty_content
209
+ @formatting_dirty_content_for_writing = false
210
+ end
211
+ end
212
+
153
213
  def write_raw_dirty_content
154
214
  return if scratchpad? || !::File.exist?(path)
155
215
  ::File.write(path, dirty_content) if ::File.exists?(path)
@@ -185,7 +245,7 @@ module Glimmer
185
245
  self.caret_position = caret_position_for_line_index(line_number) + current_line_indentation.size
186
246
  self.selection_count = 0
187
247
  end
188
-
248
+
189
249
  def comment_line!
190
250
  old_lines = lines
191
251
  return if old_lines.size < 1
@@ -219,6 +279,7 @@ module Glimmer
219
279
  new_caret_position = old_caret_position + delta
220
280
  new_caret_position = [new_caret_position, old_caret_position_line_caret_position].max
221
281
  self.caret_position = new_caret_position
282
+ self.selection_count = 0
222
283
  end
223
284
  end
224
285
 
@@ -243,6 +304,7 @@ module Glimmer
243
304
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
244
305
  else
245
306
  self.caret_position = old_caret_position + delta
307
+ self.selection_count = 0
246
308
  end
247
309
  end
248
310
 
@@ -274,6 +336,7 @@ module Glimmer
274
336
  new_caret_position = old_caret_position + delta
275
337
  new_caret_position = [new_caret_position, old_caret_position_line_caret_position].max
276
338
  self.caret_position = new_caret_position
339
+ self.selection_count = 0
277
340
  end
278
341
  end
279
342
 
@@ -311,6 +374,7 @@ module Glimmer
311
374
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
312
375
  else
313
376
  self.caret_position = old_caret_position + delta
377
+ self.selection_count = 0
314
378
  end
315
379
  end
316
380
 
@@ -455,13 +519,18 @@ module Glimmer
455
519
  write_dirty_content
456
520
  load path
457
521
  end
458
- rescue SyntaxError, StandardError => e
522
+ rescue LoadError, SyntaxError, StandardError => e
523
+ # TODO consider showing a message dialog or error message console in the future
459
524
  puts e.full_message
460
525
  end
461
526
  end
462
527
 
463
528
  def lines
464
- dirty_content.split("\n")
529
+ need_padding = dirty_content.to_s.end_with?("\n")
530
+ splittable_content = need_padding ? "#{dirty_content} " : dirty_content
531
+ the_lines = splittable_content.split("\n")
532
+ the_lines[-1] = the_lines[-1].strip if need_padding
533
+ the_lines
465
534
  end
466
535
 
467
536
  def line_for_caret_position(caret_position)
@@ -471,7 +540,7 @@ module Glimmer
471
540
  def line_index_for_caret_position(caret_position)
472
541
  dirty_content[0...caret_position.to_i].count("\n")
473
542
  end
474
-
543
+
475
544
  def caret_position_for_line_index(line_index)
476
545
  cp = lines[0...line_index].join("\n").size
477
546
  cp += 1 if line_index > 0