glimmer-cs-gladiator 0.5.3 → 0.6.3

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