glimmer-cs-gladiator 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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