glimmer-cs-gladiator 0.5.0 → 0.6.0

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: bb031342309fc665039e7a7a64e4a3996f3e659a17767527d47da00e5d7ede50
4
- data.tar.gz: 434912cc7be89f07d1e14cdf94ff1511ea7c6ca8e5b09d19654850c3680cb443
3
+ metadata.gz: 686df61b4e15ac85a1cc5d08cb3e83f088bce3a40b2a6e155101f657189075f3
4
+ data.tar.gz: 12874887ef0d8a846ef43d5eb89ad2d1ec184fe3d0171a4cf692a1f9348efe61
5
5
  SHA512:
6
- metadata.gz: 6c594f90f5643023e7424747b9abd1959778c9d8d878a99e6b577f7f94e796f5c930c68eccfe267b84860f330cc0ca3004db784fbd6e59f73d62abe41988a6d9
7
- data.tar.gz: db5c00808053ba03f441fc3fdf9d00afd627cc688cb8baa036fe749a679eba34628dd5dd629d7d20ef56614f2429bdce0918c7f294cf10fec611e74b20251923
6
+ metadata.gz: 8816804e0a23b860d98b6f583dba265cafac70ce3960b297f5c99dfa6de27d55441c5f959378d4851901e7cf059ec565bdbfc5911c114e8f41e250191f4b27fb
7
+ data.tar.gz: 288eccb3f7edbd055d54c0f05d9878a1630c057ca1f7464f7ecb586e5a632d33466197511f0e564900816dbb0b76b3a86ddc743d0fb0d60763a776ef592eb9d2
data/README.md CHANGED
@@ -1,28 +1,24 @@
1
- # <img src='https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-gladiator/master/images/glimmer-cs-gladiator-logo.svg' height=85 /> Gladiator 0.5.0 - [Ugliest Text Editor Ever](https://www.reddit.com/r/ruby/comments/hgve8k/gladiator_glimmer_editor_ugliest_text_editor_ever/)
1
+ # <img src='https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-gladiator/master/images/glimmer-cs-gladiator-logo.svg' height=85 /> Gladiator 0.6.0 - [Ugliest Text Editor Ever](https://www.reddit.com/r/ruby/comments/hgve8k/gladiator_glimmer_editor_ugliest_text_editor_ever/)
2
2
  ## [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=40 /> Glimmer Custom Shell](https://github.com/AndyObtiva/glimmer-dsl-swt#custom-shell-gem)
3
3
  [![Gem Version](https://badge.fury.io/rb/glimmer-cs-gladiator.svg)](http://badge.fury.io/rb/glimmer-cs-gladiator)
4
4
 
5
5
  ![Gladiator](images/glimmer-gladiator.png)
6
6
 
7
- Gladiator (short for Glimmer Editor) is a [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) sample project under on-going development that demonstrates how to build a text editor in Ruby using [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) (JRuby Desktop Development GUI Library).
7
+ Gladiator (short for Glimmer Editor) is a [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) sample beta project under on-going development that demonstrates how to build a text editor in Ruby using [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) (JRuby Desktop Development GUI Library).
8
8
  It is not intended to be a full-fledged editor by any means, yet mostly a fun educational exercise in using [Glimmer](https://github.com/AndyObtiva/glimmer).
9
9
  Gladiator is also a personal tool for shaping an editor exactly the way I like, with all the keyboard shortcuts I prefer.
10
10
  I leave building truly professional text editors to software tooling experts who would hopefully use [Glimmer](https://github.com/AndyObtiva/glimmer) one day. Otherwise, I have been happily using Gladiator to develop all my [open-source projects](https://github.com/AndyObtiva) since May of 2020.
11
11
 
12
12
  ## Features
13
13
 
14
- Gladiator currently supports the following text editing features (including keyboard shortcuts with Mac CMD=CTRL on Windows/Linux):
15
- - Ruby Syntax Highlighting with colors
16
- - File explorer navigation with context menu to open file, rename, delete, add new file, add new directory, or refresh tree (CMD+T)
17
- - File lookup by name ignoring slashes, underscores, and dots to ease lookup (CMD+R)
18
- - Watch open file for external changes to automatically refresh in editor
19
- - Watch project subdirectories for changes to automatically refresh in file explorer/file lookup
20
- - Find & Replace (CMD+F)
14
+ Gladiator currently supports the following text editing features (including keyboard shortcuts with Mac CMD=CTRL on Windows/Linux)
15
+
16
+ ### Text Editor
17
+
18
+ - Text Editor with Colored Ruby Syntax Highlighting
21
19
  - Show Line Numbers
22
- - Jump to Line (CMD+L)
23
- - Multiple tab support (CMD+SHIFT+[ & CMD+SHIFT+] for tab navigation. CMD+1-9 to jump to a specific tab)
24
- - Remember opened tabs, caret position, top line, window size, and window location
25
- - Autosave on focus out/quit/open new file
20
+ - Multi-tab support (CMD+SHIFT+[ & CMD+SHIFT+] for tab navigation. CMD+1-9 to jump to a specific tab)
21
+ - Drag and drop split pane (drag a file from File Tree or File Lookup List, and it splits the pane)
26
22
  - Duplicate Line(s)/selection (CMD+D)
27
23
  - Kill Line(s)/selection (CMD+K)
28
24
  - Move line/selection up (CMD+UP)
@@ -30,10 +26,41 @@ Gladiator currently supports the following text editing features (including keyb
30
26
  - Comment/Uncomment line/selection (CMD+/)
31
27
  - Indent/Unindent line/selection (CMD+] & CMD+[)
32
28
  - Insert/Prefix New Line (CMD+ENTER & CMD+SHIFT+ENTER)
33
- - Drag and Drop Text Editor Split Screen (drag a file from File Tree or File Lookup List, and it splits the screen)
34
- - Run Ruby code (CMD+SHIFT+R)
35
- - Scratchpad for running arbitrary Ruby/Glimmer code without saving to disk (CMD+SHIFT+S)
36
- - Change Split Orientation to Horizontal/Vertical via View Menu (CMD+SHIFT+O)
29
+
30
+ ### File Explorer Tree
31
+
32
+ - Collapsable file explorer tree listing files and directories for open project
33
+ - Context menu to open file, rename, delete, add new file, add new directory, and refresh tree
34
+ - Jump to open file in tree (CMD+T)
35
+
36
+ ### File Lookup List Filter
37
+
38
+ - Collapsable file lookup list filter (CMD+R)
39
+ - Semi-fuzzy filtering by ignoring slashes, underscores, and dots to ease lookup
40
+
41
+ ### Navigation Area
42
+
43
+ - Show current text editor file name
44
+ - Show file navigation stats (Caret Position / Line Position / Selection Count / Top Pixel)
45
+ - Jump to Line (CMD+L)
46
+ - Find & Replace (CMD+F)
47
+
48
+ ### Menus
49
+
50
+ - File Menu to Open Project (CMD+SHIFT+P) and open Scratchpad for running arbitrary Ruby/Glimmer code without saving to disk (CMD+SHIFT+S)
51
+ - View Menu to Split Pane & Change Split Orientation to Horizontal/Vertical (CMD+SHIFT+O)
52
+ - Run Menu to run Ruby code (CMD+SHIFT+R)
53
+
54
+ ### Watch External Changes
55
+
56
+ - Watch open file for external changes to automatically refresh in editor
57
+ - Watch project subdirectories for changes to automatically refresh in file explorer/file lookup
58
+
59
+ ### Automatic Data Management
60
+
61
+ - Autosave on focus out/quit/open new file
62
+ - Remember opened tabs, caret position, top pixel, window size, and window location
63
+ - [Default](#configuration) "ignore paths" to avoid bogging down editor with irrelevant directory files
37
64
 
38
65
  ## Platforms
39
66
 
@@ -77,6 +104,12 @@ You may run the `gladiator` command to bring up the text editor in the project d
77
104
  gladiator
78
105
  ```
79
106
 
107
+ On Linux, you may need to run with extra memory via this command instead:
108
+
109
+ ```
110
+ gladiator -J-Xmx1200M
111
+ ```
112
+
80
113
  On Windows, you may need to run with extra memory via this command instead:
81
114
 
82
115
  ```
@@ -103,7 +136,7 @@ To reuse Gladiator as a Glimmer Custom Shell inside another Glimmer application,
103
136
  following to the application's `Gemfile`:
104
137
 
105
138
  ```
106
- gem 'glimmer-cs-gladiator', '~> 0.5.0'
139
+ gem 'glimmer-cs-gladiator', '~> 0.6.0'
107
140
  ```
108
141
 
109
142
  Run:
@@ -129,16 +162,12 @@ Opens Gladiator with "/Users/User/code" as the root directory.
129
162
 
130
163
  ## Configuration
131
164
 
132
- Gladiator automatically saves configuration data in a `.gladiator` file at the directory it is run from.
165
+ Gladiator automatically saves configuration data in a `.gladiator` file at the directory it is run from. It may be edited to add extra ignore paths.
133
166
 
134
167
  It currently remembers:
135
- - Last opened file
136
- - Caret position
137
- - Top line position
138
- - Window size
139
- - Opened tabs
140
- - Split tabs
141
- - Ignore Paths
168
+ - Last opened files (in both split panes if split)
169
+ - Window size and position
170
+ - Ignore Paths (default: '.gladiator', '.git', 'coverage', 'packages', 'node_modules', 'tmp', 'vendor')
142
171
 
143
172
  ## Gotcha
144
173
 
@@ -173,4 +202,4 @@ Copyright (c) 2020 Andy Maleh. See [LICENSE.txt](LICENSE.txt) for further detail
173
202
 
174
203
  [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=40 />](https://github.com/AndyObtiva/glimmer) Built with [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) (JRuby Desktop Development GUI Library)
175
204
 
176
- Gladiator icon made by <a href="https://www.flaticon.com/authors/freepik" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a>
205
+ Gladiator icon made by <a href="https://www.flaticon.com/authors/freepik" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a>
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.0
1
+ 0.6.0
@@ -23,7 +23,7 @@ module Glimmer
23
23
  args = SWTProxy.constantify_args(args)
24
24
  @swt_layout = self.class.swt_layout_class_for(underscored_layout_name).new(*args)
25
25
  @widget_proxy.swt_widget.setLayout(@swt_layout)
26
- end
26
+ end
27
27
  end
28
28
  end
29
29
  end
@@ -1,6 +1,8 @@
1
1
  module Glimmer
2
2
  class Gladiator
3
- class Command
3
+ class Command
4
+ include Glimmer
5
+
4
6
  class << self
5
7
  include Glimmer
6
8
 
@@ -10,18 +12,25 @@ module Glimmer
10
12
 
11
13
  def command_history_for(file)
12
14
  # keeping a first command to make redo support work by remembering next command after undoing all
13
- command_history[file] ||= [Command.new(file)]
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)
24
- return if command_history_for(file).size <= 1
33
+ return if command_history_for(file).size <= 1
25
34
  command = command_history_for(file).pop
26
35
  command&.undo
27
36
  end
@@ -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
 
@@ -48,6 +57,14 @@ module Glimmer
48
57
  is_local_dir ? path : @display_path = @path.sub(project_dir.path, '').sub(/^\//, '')
49
58
  end
50
59
 
60
+ def name=(the_name)
61
+ self.display_path = display_path.sub(/#{Regexp.escape(@name)}$/, the_name)
62
+ @name = the_name
63
+ new_path = ::File.expand_path(display_path)
64
+ FileUtils.mv(path, new_path)
65
+ self.path = display_path
66
+ end
67
+
51
68
  def children
52
69
  @children ||= retrieve_children
53
70
  end
@@ -59,7 +76,7 @@ module Glimmer
59
76
  result || p.include?(ignore_path)
60
77
  end
61
78
  end.map do |p|
62
- ::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)
63
80
  end.sort_by do |c|
64
81
  c.path.to_s.downcase
65
82
  end.sort_by do |c|
@@ -68,6 +85,22 @@ module Glimmer
68
85
  child.retrieve_children if child.is_a?(Dir)
69
86
  end
70
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
71
104
 
72
105
  def selected_child_path_history
73
106
  @selected_child_path_history ||= []
@@ -83,6 +116,7 @@ module Glimmer
83
116
 
84
117
  def refresh(async: true, force: false)
85
118
  return if @refresh_paused && !force
119
+ @refresh_in_progress = true
86
120
  retrieve_children
87
121
  collect_all_children
88
122
  refresh_operation = lambda do
@@ -94,6 +128,7 @@ module Glimmer
94
128
  else
95
129
  sync_exec(&refresh_operation)
96
130
  end
131
+ @refresh_in_progress = false
97
132
  end
98
133
 
99
134
  def filter=(value)
@@ -134,19 +169,19 @@ module Glimmer
134
169
  end
135
170
 
136
171
  def selected_child_path=(selected_path)
172
+ return (project_dir.selected_child = nil) if selected_path.nil?
137
173
  # scratchpad scenario
138
- if selected_path&.empty? #scratchpad
174
+ if selected_path.empty? # Scratchpad
139
175
  @selected_child&.write_dirty_content
140
176
  return (self.selected_child = File.new)
141
177
  end
142
178
  full_selected_path = selected_path.include?(project_dir.path) ? selected_path : ::File.join(project_dir.path, selected_path)
143
- return if selected_path.nil? ||
144
- ::Dir.exist?(full_selected_path) ||
179
+ return if ::Dir.exist?(full_selected_path) ||
145
180
  (selected_child && selected_child.path == full_selected_path)
146
181
  selected_path = full_selected_path
147
182
  if ::File.file?(selected_path)
148
183
  @selected_child&.write_dirty_content
149
- new_child = Gladiator::File.new(selected_path, project_dir)
184
+ new_child = find_child_file(selected_path)
150
185
  begin
151
186
  unless new_child.dirty_content.nil?
152
187
  self.selected_child&.stop_filewatcher
@@ -167,6 +202,7 @@ module Glimmer
167
202
  end
168
203
 
169
204
  def selected_child=(new_child)
205
+ return if selected_child == new_child
170
206
  file_properties = @selected_child&.backup_properties if @selected_child == new_child
171
207
  @selected_child = new_child
172
208
  @selected_child.restore_properties(file_properties) if file_properties
@@ -183,7 +219,7 @@ module Glimmer
183
219
  def eql?(other)
184
220
  self.path.eql?(other&.path)
185
221
  end
186
-
222
+
187
223
  def hash
188
224
  self.path.hash
189
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,48 @@ 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
+ new_line_number = line_index_for_caret_position(caret_position) + 1
40
+ current_line_number = line_number
41
+ self.line_number = new_line_number unless current_line_number && current_line_number == new_line_number
42
+ self.line_position = caret_position - caret_position_for_line_index(line_number - 1) + 1
43
+ end
44
+ observe(self, :line_number) do |new_line_number|
33
45
  line_index = line_number - 1
34
46
  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)
47
+ current_caret_position = caret_position
48
+ line_index_for_new_caret_position = line_index_for_caret_position(new_caret_position)
49
+ line_index_for_current_caret_position = line_index_for_caret_position(current_caret_position)
50
+ self.caret_position = new_caret_position unless (current_caret_position && line_index_for_new_caret_position == line_index_for_current_caret_position)
36
51
  end
52
+ rescue # in case of a binary file
53
+ stop_filewatcher
37
54
  end
38
- rescue
39
- # no op in case of a binary file
40
55
  end
41
56
  end
42
-
57
+
43
58
  def path=(the_path)
44
59
  @path = the_path
45
60
  generate_display_path
@@ -49,9 +64,18 @@ module Glimmer
49
64
  return if @path.empty?
50
65
  @display_path = @path.sub(project_dir.path, '').sub(/^\//, '')
51
66
  end
67
+
68
+ def name=(the_name)
69
+ new_path = path.sub(/#{Regexp.escape(@name)}$/, the_name) unless scratchpad?
70
+ @name = the_name
71
+ if !scratchpad? && ::File.exist?(path)
72
+ FileUtils.mv(path, new_path)
73
+ self.path = new_path
74
+ end
75
+ end
52
76
 
53
77
  def scratchpad?
54
- path.empty?
78
+ path.to_s.empty?
55
79
  end
56
80
 
57
81
  def backup_properties
@@ -66,41 +90,73 @@ module Glimmer
66
90
  send("#{property}=", value)
67
91
  end
68
92
  end
93
+
94
+ def caret_position=(value)
95
+ @last_caret_position = @caret_position
96
+ @caret_position = value
97
+ end
98
+
99
+ def selection_count=(value)
100
+ #@last_selection_count = @selection_count
101
+ @selection_count = value
102
+ @last_selection_count = @selection_count
103
+ end
104
+
105
+ def dirty_content
106
+ init_content
107
+ @dirty_content
108
+ end
109
+
110
+ def dirty_content=(the_content)
111
+ # TODO set partial dirty content by line(s) for enhanced performance
112
+ @dirty_content = the_content
113
+ old_caret_position = caret_position
114
+ old_top_pixel = top_pixel
115
+
116
+ notify_observers(:content)
117
+ if @formatting_dirty_content_for_writing
118
+ self.caret_position = old_caret_position
119
+ self.top_pixel = old_top_pixel
120
+ end
121
+ end
122
+
123
+ def content
124
+ dirty_content
125
+ end
69
126
 
70
127
  # to use for widget data-binding
71
128
  def content=(value)
72
129
  value = value.gsub("\t", ' ')
73
130
  if dirty_content != value
74
- Command.do(self) # record a native (OS-widget) operation
75
- self.dirty_content = value
131
+ Command.do(self, :change_content!, value)
76
132
  end
77
133
  end
78
-
79
- def content
80
- dirty_content
134
+
135
+ def change_content!(value)
136
+ self.dirty_content = value
81
137
  end
82
138
 
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
139
+ def start_command
140
+ @commmand_in_progress = true
87
141
  end
88
-
89
- def caret_position
90
- selection.x
142
+
143
+ def end_command
144
+ @commmand_in_progress = false
91
145
  end
92
-
93
- def selection_count
94
- selection.y - selection.x
146
+
147
+ def command_in_progress?
148
+ @commmand_in_progress
95
149
  end
96
-
97
- def selection_count=(value)
98
- self.selection = Point.new(caret_position, caret_position + value.to_i)
150
+
151
+ def close
152
+ stop_filewatcher
153
+ remove_all_observers
154
+ initialize(path, project_dir)
155
+ Command.clear(self)
99
156
  end
100
-
101
- def dirty_content=(the_content)
102
- @dirty_content = the_content
103
- notify_observers(:content)
157
+
158
+ def read_dirty_content
159
+ path.empty? ? '' : ::File.read(path)
104
160
  end
105
161
 
106
162
  def start_filewatcher
@@ -108,15 +164,13 @@ module Glimmer
108
164
  @filewatcher = Filewatcher.new(@path)
109
165
  @thread = Thread.new(@filewatcher) do |fw|
110
166
  fw.watch do |filename, event|
111
- begin
112
- read_dirty_content = ::File.read(path)
113
- # test read dirty content
114
- read_dirty_content.split("\n")
115
- async_exec do
167
+ async_exec do
168
+ begin
116
169
  self.dirty_content = read_dirty_content if read_dirty_content != dirty_content
170
+ rescue StandardError, Errno::ENOENT
171
+ # in case of a binary file
172
+ stop_filewatcher
117
173
  end
118
- rescue
119
- # no op in case of a binary file
120
174
  end
121
175
  end
122
176
  end
@@ -126,21 +180,28 @@ module Glimmer
126
180
  @filewatcher&.stop
127
181
  end
128
182
 
129
- def format_dirty_content_for_writing!
130
- new_dirty_content = dirty_content.split("\n").map {|line| line.strip.empty? ? line : line.rstrip }.join("\n")
131
- new_dirty_content = "#{new_dirty_content.gsub("\r\n", "\n").gsub("\r", "\n").sub(/\n+\z/, '')}\n"
132
- self.dirty_content = new_dirty_content if new_dirty_content != self.dirty_content
133
- end
134
-
135
183
  def write_dirty_content
136
- return if scratchpad? || !::File.exist?(path)
184
+ # TODO write partial dirty content by line(s) for enhanced performance
185
+ return if scratchpad? || !::File.exist?(path) || !::File.exists?(path) || read_dirty_content == dirty_content
137
186
  format_dirty_content_for_writing!
138
- ::File.write(path, dirty_content) if ::File.exists?(path)
139
- rescue => e
187
+ ::File.write(path, dirty_content)
188
+ rescue StandardError, ArgumentError => e
140
189
  puts "Error in writing dirty content for #{path}"
141
190
  puts e.full_message
142
191
  end
143
192
 
193
+ def format_dirty_content_for_writing!
194
+ return if @commmand_in_progress
195
+ # TODO f ix c ar e t pos it ion after formatting dirty content (diff?)
196
+ new_dirty_content = dirty_content.to_s.split("\n").map {|line| line.strip.empty? ? line : line.rstrip }.join("\n")
197
+ new_dirty_content = "#{new_dirty_content.gsub("\r\n", "\n").gsub("\r", "\n").sub(/\n+\z/, '')}\n"
198
+ if new_dirty_content != self.dirty_content
199
+ @formatting_dirty_content_for_writing = true
200
+ self.dirty_content = new_dirty_content
201
+ @formatting_dirty_content_for_writing = false
202
+ end
203
+ end
204
+
144
205
  def write_raw_dirty_content
145
206
  return if scratchpad? || !::File.exist?(path)
146
207
  ::File.write(path, dirty_content) if ::File.exists?(path)
@@ -176,7 +237,7 @@ module Glimmer
176
237
  self.caret_position = caret_position_for_line_index(line_number) + current_line_indentation.size
177
238
  self.selection_count = 0
178
239
  end
179
-
240
+
180
241
  def comment_line!
181
242
  old_lines = lines
182
243
  return if old_lines.size < 1
@@ -210,6 +271,7 @@ module Glimmer
210
271
  new_caret_position = old_caret_position + delta
211
272
  new_caret_position = [new_caret_position, old_caret_position_line_caret_position].max
212
273
  self.caret_position = new_caret_position
274
+ self.selection_count = 0
213
275
  end
214
276
  end
215
277
 
@@ -234,6 +296,7 @@ module Glimmer
234
296
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
235
297
  else
236
298
  self.caret_position = old_caret_position + delta
299
+ self.selection_count = 0
237
300
  end
238
301
  end
239
302
 
@@ -265,6 +328,7 @@ module Glimmer
265
328
  new_caret_position = old_caret_position + delta
266
329
  new_caret_position = [new_caret_position, old_caret_position_line_caret_position].max
267
330
  self.caret_position = new_caret_position
331
+ self.selection_count = 0
268
332
  end
269
333
  end
270
334
 
@@ -302,6 +366,7 @@ module Glimmer
302
366
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
303
367
  else
304
368
  self.caret_position = old_caret_position + delta
369
+ self.selection_count = 0
305
370
  end
306
371
  end
307
372