glimmer-cs-gladiator 0.2.4 → 0.5.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: 1d7db15419bc0913d01e3e15634e12833a1f36a7fc3160e2a7835ab6560832af
4
- data.tar.gz: 6e55a2c18985603a0b6438198fad5959bcea27b65a8e43b8a810fd42ec5fda76
3
+ metadata.gz: bb031342309fc665039e7a7a64e4a3996f3e659a17767527d47da00e5d7ede50
4
+ data.tar.gz: 434912cc7be89f07d1e14cdf94ff1511ea7c6ca8e5b09d19654850c3680cb443
5
5
  SHA512:
6
- metadata.gz: bae9a2b2eb76cf9ca64ec0d76216fa8aa7c12e97805dbfee42dace54deceedba6eeb1579b65cbdb7c1ad595a44e5768ac372619db804f5fbc240ee52afbecb4d
7
- data.tar.gz: 56186b8a8d31dc8974ff4f35afd4f7a37d4932cea6b1c4517832ae760ec1f6c5d0092c6048a5f333bf1e438bd514ccd2db3a6ca0ac45d35a519d35194861c251
6
+ metadata.gz: 6c594f90f5643023e7424747b9abd1959778c9d8d878a99e6b577f7f94e796f5c930c68eccfe267b84860f330cc0ca3004db784fbd6e59f73d62abe41988a6d9
7
+ data.tar.gz: db5c00808053ba03f441fc3fdf9d00afd627cc688cb8baa036fe749a679eba34628dd5dd629d7d20ef56614f2429bdce0918c7f294cf10fec611e74b20251923
data/README.md CHANGED
@@ -1,17 +1,18 @@
1
- # <img src='https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-gladiator/master/images/glimmer-cs-gladiator-logo.svg' height=85 /> Gladiator 0.2.4 - [Ugliest Text Editor Ever](https://www.reddit.com/r/ruby/comments/hgve8k/gladiator_glimmer_editor_ugliest_text_editor_ever/)
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#custom-shell-gem)
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/)
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](https://github.com/AndyObtiva/glimmer) sample project under on-going development that demonstrates how to build a text editor in [Glimmer](https://github.com/AndyObtiva/glimmer) (Ruby Desktop Development GUI Library).
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).
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
- Gladiator is also a personal tool for shaping an editor exactly the way I like, with all the keyboard shortcuts I prefer.
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
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
15
16
  - File explorer navigation with context menu to open file, rename, delete, add new file, add new directory, or refresh tree (CMD+T)
16
17
  - File lookup by name ignoring slashes, underscores, and dots to ease lookup (CMD+R)
17
18
  - Watch open file for external changes to automatically refresh in editor
@@ -21,7 +22,7 @@ Gladiator currently supports the following text editing features (including keyb
21
22
  - Jump to Line (CMD+L)
22
23
  - Multiple tab support (CMD+SHIFT+[ & CMD+SHIFT+] for tab navigation. CMD+1-9 to jump to a specific tab)
23
24
  - Remember opened tabs, caret position, top line, window size, and window location
24
- - Autosave on focus out/quit/open new file
25
+ - Autosave on focus out/quit/open new file
25
26
  - Duplicate Line(s)/selection (CMD+D)
26
27
  - Kill Line(s)/selection (CMD+K)
27
28
  - Move line/selection up (CMD+UP)
@@ -30,18 +31,21 @@ Gladiator currently supports the following text editing features (including keyb
30
31
  - Indent/Unindent line/selection (CMD+] & CMD+[)
31
32
  - Insert/Prefix New Line (CMD+ENTER & CMD+SHIFT+ENTER)
32
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)
33
37
 
34
38
  ## Platforms
35
39
 
36
40
  - Mac: Gladiator works best on the Mac. This is the platform it is most used on and receives the most maintenance for.
37
- - Windows: Gladiator works OK on Windows, but has some annoying bugs.
38
- - Linux: Gladiator works with very bad handicaps on Linux (performing text editing operations causes scroll jitter)
41
+ - Windows: Gladiator works OK on Windows, but has some annoying bugs. Contributers could help fix.
42
+ - Linux: Gladiator works with handicaps on Linux (performing some text editing operations causes scroll jitter). Contributers could help fix.
39
43
 
40
44
  ## Pre-requisites
41
45
 
42
- - [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer) (Ruby Desktop Development GUI Library): '>= 0.1.0', '< 2.0.0' (dependency included in Ruby gem).
43
- - [JRuby](https://www.jruby.org/download): Same version required by [Glimmer](https://github.com/AndyObtiva/glimmer)
44
- - [JDK](https://www.oracle.com/java/technologies/javase-downloads.html): Same version required by [Glimmer](https://github.com/AndyObtiva/glimmer)
46
+ - [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) (JRuby Desktop Development GUI Library): '>= 4.17.2.0', '< 5.0.0.0' (dependency included in Ruby gem).
47
+ - [JRuby](https://www.jruby.org/download): Same version required by [Glimmer](https://github.com/AndyObtiva/glimmer-dsl-swt)
48
+ - [JDK](https://www.oracle.com/java/technologies/javase-downloads.html): Same version required by [Glimmer](https://github.com/AndyObtiva/glimmer-dsl-swt)
45
49
 
46
50
  ## Setup Instructions
47
51
 
@@ -95,11 +99,11 @@ bin/gladiator relative-or-absolute-path/to/project
95
99
 
96
100
  ### Glimmer Custom Shell Reuse
97
101
 
98
- To reuse Gladiator as a Glimmer Custom Shell inside another Glimmer application, add the
102
+ To reuse Gladiator as a Glimmer Custom Shell inside another Glimmer application, add the
99
103
  following to the application's `Gemfile`:
100
104
 
101
105
  ```
102
- gem 'glimmer-cs-gladiator', '~> 0.2.4'
106
+ gem 'glimmer-cs-gladiator', '~> 0.5.0'
103
107
  ```
104
108
 
105
109
  Run:
@@ -108,11 +112,11 @@ Run:
108
112
  jruby -S bundle
109
113
  ```
110
114
 
111
- And, then instantiate the Gladiator [custom shell](https://github.com/AndyObtiva/glimmer#custom-shells) in your [Glimmer](https://github.com/AndyObtiva/glimmer) application via the `gladiator` keyword assuming you already have `include Glimmer` in your class, module, or main object.
115
+ And, then instantiate the Gladiator [custom shell](https://github.com/AndyObtiva/glimmer-dsl-swt#custom-shells) in your [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) application via the `gladiator` keyword assuming you already have `include Glimmer` in your class, module, or main object.
112
116
 
113
117
  ## Env Var Options
114
118
 
115
- Gladiator opens with the current directory as the root by default.
119
+ Gladiator opens with the current directory as the root by default.
116
120
  If you would like to open another directory, set `LOCAL_DIR` environment variable.
117
121
 
118
122
  Example:
@@ -131,7 +135,10 @@ It currently remembers:
131
135
  - Last opened file
132
136
  - Caret position
133
137
  - Top line position
138
+ - Window size
134
139
  - Opened tabs
140
+ - Split tabs
141
+ - Ignore Paths
135
142
 
136
143
  ## Gotcha
137
144
 
@@ -164,6 +171,6 @@ Copyright (c) 2020 Andy Maleh. See [LICENSE.txt](LICENSE.txt) for further detail
164
171
 
165
172
  --
166
173
 
167
- [<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](https://github.com/AndyObtiva/glimmer) (Ruby Desktop Development GUI Library)
174
+ [<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)
168
175
 
169
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>
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.4
1
+ 0.5.0
@@ -2,4 +2,5 @@ require_relative '../lib/glimmer-cs-gladiator'
2
2
 
3
3
  include Glimmer
4
4
 
5
- gladiator.open
5
+ local_dir = ENV['LOCAL_DIR'] || '.'
6
+ gladiator(project_dir_path: local_dir).open
@@ -4,3 +4,26 @@ require 'filewatcher'
4
4
  require 'clipboard'
5
5
  require 'puts_debuggerer'
6
6
  require 'views/glimmer/gladiator'
7
+
8
+ # Custom Composite Initializer (avoid default margins)
9
+ Glimmer::SWT::WidgetProxy::DEFAULT_INITIALIZERS['composite'] = lambda do |composite|
10
+ if composite.get_layout.nil?
11
+ layout = GridLayout.new
12
+ composite.layout = layout
13
+ end
14
+ end
15
+
16
+ # Custom LayoutProxy initialize method (avoid default margins)
17
+ module Glimmer
18
+ module SWT
19
+ class LayoutProxy
20
+ def initialize(underscored_layout_name, widget_proxy, args)
21
+ @underscored_layout_name = underscored_layout_name
22
+ @widget_proxy = widget_proxy
23
+ args = SWTProxy.constantify_args(args)
24
+ @swt_layout = self.class.swt_layout_class_for(underscored_layout_name).new(*args)
25
+ @widget_proxy.swt_widget.setLayout(@swt_layout)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,7 +1,9 @@
1
1
  module Glimmer
2
2
  class Gladiator
3
- class Command
3
+ class Command
4
4
  class << self
5
+ include Glimmer
6
+
5
7
  def command_history
6
8
  @command_history ||= {}
7
9
  end
@@ -11,7 +13,7 @@ module Glimmer
11
13
  command_history[file] ||= [Command.new(file)]
12
14
  end
13
15
 
14
- def do(file, method = nil, command: nil)
16
+ def do(file, method = nil, command: nil)
15
17
  command ||= Command.new(file, method)
16
18
  command_history_for(file)&.last&.next_command = command
17
19
  command.do
@@ -20,11 +22,13 @@ module Glimmer
20
22
 
21
23
  def undo(file)
22
24
  return if command_history_for(file).size <= 1
23
- command_history_for(file).pop.undo
25
+ command = command_history_for(file).pop
26
+ command&.undo
24
27
  end
25
28
 
26
29
  def redo(file)
27
- command_history_for(file).last&.redo
30
+ command = command_history_for(file).last
31
+ command&.redo
28
32
  end
29
33
  end
30
34
 
@@ -4,38 +4,40 @@ module Glimmer
4
4
  class Gladiator
5
5
  class Dir
6
6
  include Glimmer
7
+ include Glimmer::DataBinding::ObservableModel
7
8
 
8
9
  REFRESH_DELAY = 7
9
10
 
10
- class << self
11
- def local_dir
12
- unless @local_dir
13
- @local_dir = new(ENV['LOCAL_DIR'] || '.', true)
14
- @local_dir.refresh
15
- @filewatcher = Filewatcher.new(@local_dir.path)
16
- @thread = Thread.new(@filewatcher) do |fw|
17
- fw.watch do |filename, event|
18
- if @last_update.nil? || (Time.now.to_f - @last_update) > REFRESH_DELAY
19
- @local_dir.refresh if !filename.include?('new_file') && !@local_dir.selected_child_path_history.include?(filename) && filename != @local_dir.selected_child_path
20
- end
21
- @last_update = Time.now.to_f
22
- end
23
- end
24
- end
25
- @local_dir
26
- end
27
- end
28
-
29
- attr_accessor :selected_child, :filter, :children, :filtered_path_options, :filtered_path, :display_path
30
- attr_reader :name, :parent, :path, :is_local_dir
11
+ attr_accessor :selected_child, :filter, :children, :filtered_path_options, :filtered_path, :display_path, :ignore_paths
12
+ attr_reader :name, :parent, :path
31
13
  attr_writer :all_children
32
14
 
33
- def initialize(path, is_local_dir = false)
34
- @is_local_dir = is_local_dir
15
+ def initialize(path, project_dir = nil)
16
+ @project_dir = project_dir
17
+ 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
27
+ end
35
28
  self.path = ::File.expand_path(path)
36
29
  @name = ::File.basename(::File.expand_path(path))
30
+ @ignore_paths = ['.gladiator', '.git', 'coverage', 'packages', 'tmp', 'vendor']
37
31
  self.filtered_path_options = []
38
32
  end
33
+
34
+ def is_local_dir
35
+ @project_dir.nil?
36
+ end
37
+
38
+ def project_dir
39
+ @project_dir || self
40
+ end
39
41
 
40
42
  def path=(the_path)
41
43
  @path = the_path
@@ -43,27 +45,24 @@ module Glimmer
43
45
  end
44
46
 
45
47
  def generate_display_path
46
- is_local_dir ? path : @display_path = @path.sub(Dir.local_dir.path, '').sub(/^\//, '')
48
+ is_local_dir ? path : @display_path = @path.sub(project_dir.path, '').sub(/^\//, '')
47
49
  end
48
50
 
49
- def name=(the_name)
50
- self.display_path = display_path.sub(/#{Regexp.escape(@name)}$/, the_name)
51
- @name = the_name
52
- new_path = ::File.expand_path(display_path)
53
- FileUtils.mv(path, new_path)
54
- self.path = display_path
55
- end
56
-
57
51
  def children
58
52
  @children ||= retrieve_children
59
53
  end
60
54
 
61
55
  def retrieve_children
62
- @children = ::Dir.glob(::File.join(@path, '*')).map do |p|
63
- ::File.file?(p) ? Gladiator::File.new(p) : Gladiator::Dir.new(p)
64
- end.sort_by do |c|
65
- c.path.to_s.downcase
66
- end.sort_by do |c|
56
+ @children = ::Dir.glob(::File.join(@path, '*')).reject do |p|
57
+ # TODO make sure to configure ignore_paths in a preferences dialog
58
+ project_dir.ignore_paths.reduce(false) do |result, ignore_path|
59
+ result || p.include?(ignore_path)
60
+ end
61
+ end.map do |p|
62
+ ::File.file?(p) ? Gladiator::File.new(p, project_dir) : Gladiator::Dir.new(p, project_dir)
63
+ end.sort_by do |c|
64
+ c.path.to_s.downcase
65
+ end.sort_by do |c|
67
66
  c.class.name
68
67
  end.each do |child|
69
68
  child.retrieve_children if child.is_a?(Dir)
@@ -99,7 +98,7 @@ module Glimmer
99
98
 
100
99
  def filter=(value)
101
100
  if value.to_s.empty?
102
- @filter = nil
101
+ @filter = nil
103
102
  else
104
103
  @filter = value
105
104
  end
@@ -111,8 +110,8 @@ module Glimmer
111
110
  def filtered
112
111
  return if filter.nil?
113
112
  children_files = !@last_filter.to_s.empty? && filter.downcase.start_with?(@last_filter.downcase) ? @last_filtered : all_children_files
114
- children_files.select do |child|
115
- child_path = child.path.to_s.sub(Dir.local_dir.path, '')
113
+ children_files.select do |child|
114
+ child_path = child.path.to_s.sub(project_dir.path, '')
116
115
  child_path.downcase.include?(filter.downcase) ||
117
116
  child_path.downcase.gsub(/[_\/.-]/, '').include?(filter.downcase.gsub(/[_\/.-]/, ''))
118
117
  end.sort_by {|c| c.path.to_s.downcase}
@@ -135,14 +134,19 @@ module Glimmer
135
134
  end
136
135
 
137
136
  def selected_child_path=(selected_path)
138
- full_selected_path = selected_path.include?(Dir.local_dir.path) ? selected_path : ::File.join(Dir.local_dir.path, selected_path)
139
- return if selected_path.nil? ||
140
- ::Dir.exist?(full_selected_path) ||
137
+ # scratchpad scenario
138
+ if selected_path&.empty? #scratchpad
139
+ @selected_child&.write_dirty_content
140
+ return (self.selected_child = File.new)
141
+ end
142
+ 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) ||
141
145
  (selected_child && selected_child.path == full_selected_path)
142
146
  selected_path = full_selected_path
143
147
  if ::File.file?(selected_path)
144
148
  @selected_child&.write_dirty_content
145
- new_child = Gladiator::File.new(selected_path)
149
+ new_child = Gladiator::File.new(selected_path, project_dir)
146
150
  begin
147
151
  unless new_child.dirty_content.nil?
148
152
  self.selected_child&.stop_filewatcher
@@ -162,6 +166,12 @@ module Glimmer
162
166
  @selected_child&.path
163
167
  end
164
168
 
169
+ def selected_child=(new_child)
170
+ file_properties = @selected_child&.backup_properties if @selected_child == new_child
171
+ @selected_child = new_child
172
+ @selected_child.restore_properties(file_properties) if file_properties
173
+ end
174
+
165
175
  def delete!
166
176
  FileUtils.rm_rf(path)
167
177
  end
@@ -177,11 +187,7 @@ module Glimmer
177
187
  def hash
178
188
  self.path.hash
179
189
  end
180
- end
190
+ end
181
191
  end
182
192
  end
183
-
184
- at_exit do
185
- Glimmer::Gladiator::Dir.local_dir.selected_child&.write_raw_dirty_content
186
- end
187
193
 
@@ -1,22 +1,21 @@
1
- require 'models/glimmer/gladiator/dir'
2
-
3
1
  module Glimmer
4
2
  class Gladiator
5
3
  class File
6
4
  include Glimmer
7
-
8
- attr_accessor :dirty_content, :line_numbers_content, :selection, :selection_count, :line_number, :find_text, :replace_text, :top_index, :display_path, :case_sensitive
9
- attr_reader :name, :path
10
-
11
- def initialize(path)
12
- raise "Not a file path: #{path}" unless ::File.file?(path)
5
+
6
+ attr_accessor :dirty_content, :line_numbers_content, :selection, :line_number, :find_text, :replace_text, :top_pixel, :display_path, :case_sensitive
7
+ attr_reader :name, :path, :project_dir
8
+
9
+ def initialize(path='', project_dir=nil)
10
+ raise "Not a file path: #{path}" if path.nil? || (!path.empty? && !::File.file?(path))
11
+ @project_dir = project_dir
13
12
  @command_history = []
14
- @name = ::File.basename(path)
15
- self.path = ::File.expand_path(path)
16
- @top_index = 0
13
+ @name = path.empty? ? 'Scratchpad' : ::File.basename(path)
14
+ self.path = ::File.expand_path(path) unless path.empty?
15
+ @top_pixel = 0
17
16
  @selection_count = 0
18
17
  @selection = Point.new(0, 0 + @selection_count)
19
- read_dirty_content = ::File.read(path)
18
+ read_dirty_content = path.empty? ? '' : ::File.read(path)
20
19
  begin
21
20
  # test read dirty content
22
21
  read_dirty_content.split("\n")
@@ -40,20 +39,41 @@ module Glimmer
40
39
  # no op in case of a binary file
41
40
  end
42
41
  end
43
-
42
+
44
43
  def path=(the_path)
45
44
  @path = the_path
46
45
  generate_display_path
47
46
  end
48
-
47
+
49
48
  def generate_display_path
50
- @display_path = @path.sub(Dir.local_dir.path, '').sub(/^\//, '')
49
+ return if @path.empty?
50
+ @display_path = @path.sub(project_dir.path, '').sub(/^\//, '')
51
51
  end
52
52
 
53
+ def scratchpad?
54
+ path.empty?
55
+ end
56
+
57
+ def backup_properties
58
+ [:find_text, :replace_text, :case_sensitive, :top_pixel, :caret_position, :selection_count].reduce({}) do |hash, property|
59
+ hash.merge(property => send(property))
60
+ end
61
+ end
62
+
63
+ def restore_properties(properties_hash)
64
+ return if properties_hash[:caret_position] == 0 && properties_hash[:selection_count] == 0 && properties_hash[:find_text].nil? && properties_hash[:replace_text].nil? && properties_hash[:top_pixel] == 0 && properties_hash[:case_sensitive].nil?
65
+ properties_hash.each do |property, value|
66
+ send("#{property}=", value)
67
+ end
68
+ end
69
+
53
70
  # to use for widget data-binding
54
71
  def content=(value)
55
- Command.do(self) # record a native (OS-widget) operation
56
- self.dirty_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
57
77
  end
58
78
 
59
79
  def content
@@ -61,35 +81,32 @@ module Glimmer
61
81
  end
62
82
 
63
83
  def caret_position=(value)
84
+ old_top_pixel = top_pixel
64
85
  self.selection = Point.new(value, value + selection_count.to_i)
65
- if OS.linux?
66
- async_exec do
67
- self.top_index = line_index_for_caret_position(value)
68
- end
69
- end
86
+ self.top_pixel = old_top_pixel
70
87
  end
71
-
88
+
72
89
  def caret_position
73
90
  selection.x
74
91
  end
75
-
76
- def name=(the_name)
77
- new_path = path.sub(/#{Regexp.escape(@name)}$/, the_name)
78
- @name = the_name
79
- if ::File.exists?(path)
80
- FileUtils.mv(path, new_path)
81
- self.path = new_path
82
- end
92
+
93
+ def selection_count
94
+ selection.y - selection.x
83
95
  end
84
-
96
+
97
+ def selection_count=(value)
98
+ self.selection = Point.new(caret_position, caret_position + value.to_i)
99
+ end
100
+
85
101
  def dirty_content=(the_content)
86
- @dirty_content = the_content if ::File.exist?(path)
102
+ @dirty_content = the_content
87
103
  notify_observers(:content)
88
104
  end
89
-
105
+
90
106
  def start_filewatcher
107
+ return if scratchpad?
91
108
  @filewatcher = Filewatcher.new(@path)
92
- @thread = Thread.new(@filewatcher) do |fw|
109
+ @thread = Thread.new(@filewatcher) do |fw|
93
110
  fw.watch do |filename, event|
94
111
  begin
95
112
  read_dirty_content = ::File.read(path)
@@ -104,27 +121,28 @@ module Glimmer
104
121
  end
105
122
  end
106
123
  end
107
-
124
+
108
125
  def stop_filewatcher
109
126
  @filewatcher&.stop
110
127
  end
111
128
 
112
129
  def format_dirty_content_for_writing!
113
- new_dirty_content = "#{dirty_content.gsub("\r\n", "\n").gsub("\r", "\n").sub(/\n+\z/, '')}\n"
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"
114
132
  self.dirty_content = new_dirty_content if new_dirty_content != self.dirty_content
115
133
  end
116
-
134
+
117
135
  def write_dirty_content
118
- return unless ::File.exist?(path)
136
+ return if scratchpad? || !::File.exist?(path)
119
137
  format_dirty_content_for_writing!
120
138
  ::File.write(path, dirty_content) if ::File.exists?(path)
121
139
  rescue => e
122
140
  puts "Error in writing dirty content for #{path}"
123
141
  puts e.full_message
124
142
  end
125
-
143
+
126
144
  def write_raw_dirty_content
127
- return unless ::File.exist?(path)
145
+ return if scratchpad? || !::File.exist?(path)
128
146
  ::File.write(path, dirty_content) if ::File.exists?(path)
129
147
  rescue => e
130
148
  puts "Error in writing raw dirty content for #{path}"
@@ -138,16 +156,17 @@ module Glimmer
138
156
  def current_line
139
157
  lines[line_number - 1]
140
158
  end
141
-
159
+
142
160
  def delete!
143
- FileUtils.rm(path)
161
+ FileUtils.rm(path) unless scratchpad?
144
162
  end
145
-
163
+
146
164
  def prefix_new_line!
147
165
  the_lines = lines
148
166
  the_lines[line_number-1...line_number-1] = [current_line_indentation]
149
167
  self.dirty_content = the_lines.join("\n")
150
168
  self.caret_position = caret_position_for_line_index(line_number-1) + current_line_indentation.size
169
+ self.selection_count = 0
151
170
  end
152
171
 
153
172
  def insert_new_line!
@@ -155,6 +174,7 @@ module Glimmer
155
174
  the_lines[line_number...line_number] = [current_line_indentation]
156
175
  self.dirty_content = the_lines.join("\n")
157
176
  self.caret_position = caret_position_for_line_index(line_number) + current_line_indentation.size
177
+ self.selection_count = 0
158
178
  end
159
179
 
160
180
  def comment_line!
@@ -182,7 +202,7 @@ module Glimmer
182
202
  delta += 2
183
203
  end
184
204
  end
185
- self.dirty_content = new_lines.join("\n")
205
+ self.dirty_content = new_lines.join("\n")
186
206
  if old_selection_count.to_i > 0
187
207
  self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
188
208
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
@@ -192,7 +212,7 @@ module Glimmer
192
212
  self.caret_position = new_caret_position
193
213
  end
194
214
  end
195
-
215
+
196
216
  def indent!
197
217
  new_lines = lines
198
218
  old_lines = lines
@@ -208,7 +228,7 @@ module Glimmer
208
228
  new_lines[the_line_index] = " #{the_line}"
209
229
  end
210
230
  old_caret_position = self.caret_position
211
- self.dirty_content = new_lines.join("\n")
231
+ self.dirty_content = new_lines.join("\n")
212
232
  if old_selection_count.to_i > 0
213
233
  self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
214
234
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
@@ -216,7 +236,7 @@ module Glimmer
216
236
  self.caret_position = old_caret_position + delta
217
237
  end
218
238
  end
219
-
239
+
220
240
  def outdent!
221
241
  new_lines = lines
222
242
  old_lines = lines
@@ -237,7 +257,7 @@ module Glimmer
237
257
  delta = -1
238
258
  end
239
259
  end
240
- self.dirty_content = new_lines.join("\n")
260
+ self.dirty_content = new_lines.join("\n")
241
261
  if old_selection_count.to_i > 0
242
262
  self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
243
263
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
@@ -247,7 +267,7 @@ module Glimmer
247
267
  self.caret_position = new_caret_position
248
268
  end
249
269
  end
250
-
270
+
251
271
  def kill_line!
252
272
  new_lines = lines
253
273
  return if new_lines.size < 1
@@ -260,7 +280,7 @@ module Glimmer
260
280
  self.caret_position = caret_position_for_line_index(old_line_index) + [line_position, lines[old_line_index].to_s.size].min
261
281
  self.selection_count = 0
262
282
  end
263
-
283
+
264
284
  def duplicate_line!
265
285
  new_lines = lines
266
286
  old_lines = lines
@@ -284,13 +304,13 @@ module Glimmer
284
304
  self.caret_position = old_caret_position + delta
285
305
  end
286
306
  end
287
-
307
+
288
308
  def find_next
289
- return if find_text.to_s.empty?
309
+ return if find_text.to_s.empty?
290
310
  all_lines = lines
291
311
  the_line_index = line_index_for_caret_position(caret_position)
292
312
  line_position = line_position_for_caret_position(caret_position)
293
- found =
313
+ found = found_text?(caret_position)
294
314
  2.times do |i|
295
315
  rotation = the_line_index
296
316
  all_lines.rotate(rotation).each_with_index do |the_line, the_index|
@@ -307,7 +327,7 @@ module Glimmer
307
327
  end
308
328
  end
309
329
  end
310
-
330
+
311
331
  def find_previous
312
332
  return if find_text.to_s.empty?
313
333
  all_lines = lines
@@ -332,7 +352,7 @@ module Glimmer
332
352
  end
333
353
  end
334
354
  end
335
-
355
+
336
356
  def ensure_find_next
337
357
  return if find_text.to_s.empty? || dirty_content.to_s.strip.size < 1
338
358
  find_next unless found_text?(self.caret_position)
@@ -341,7 +361,7 @@ module Glimmer
341
361
  def found_text?(caret_position)
342
362
  dirty_content[caret_position.to_i, find_text.to_s.size].to_s.downcase == find_text.to_s.downcase
343
363
  end
344
-
364
+
345
365
  def replace_next!
346
366
  return if find_text.to_s.empty? || dirty_content.to_s.strip.size < 1
347
367
  ensure_find_next
@@ -349,28 +369,37 @@ module Glimmer
349
369
  new_dirty_content[caret_position, find_text.size] = replace_text.to_s
350
370
  self.dirty_content = new_dirty_content
351
371
  find_next
372
+ find_next if replace_text.to_s.include?(find_text) && !replace_text.to_s.start_with?(find_text)
352
373
  end
353
-
374
+
354
375
  def page_up
355
376
  self.selection_count = 0
356
377
  self.line_number = [(self.line_number - 15), 1].max
357
378
  end
358
-
379
+
359
380
  def page_down
360
381
  self.selection_count = 0
361
382
  self.line_number = [(self.line_number + 15), lines.size].min
362
383
  end
363
-
384
+
364
385
  def home
365
386
  self.selection_count = 0
366
387
  self.line_number = 1
367
388
  end
368
-
389
+
369
390
  def end
370
391
  self.selection_count = 0
371
392
  self.line_number = lines.size
372
393
  end
373
-
394
+
395
+ def start_of_line
396
+ self.caret_position = caret_position_for_line_index(self.line_number - 1)
397
+ end
398
+
399
+ def end_of_line
400
+ self.caret_position = caret_position_for_line_index(self.line_number) - 1
401
+ end
402
+
374
403
  def move_up!
375
404
  old_lines = lines
376
405
  return if old_lines.size < 2
@@ -389,7 +418,7 @@ module Glimmer
389
418
  self.caret_position = caret_position_for_line_index(new_line_index) + [old_caret_position_line_position, new_lines[new_line_index].size].min
390
419
  self.selection_count = old_selection_count.to_i if old_selection_count.to_i > 0
391
420
  end
392
-
421
+
393
422
  def move_down!
394
423
  old_lines = lines
395
424
  return if old_lines.size < 2
@@ -408,25 +437,38 @@ module Glimmer
408
437
  self.caret_position = caret_position_for_line_index(new_line_index) + [old_caret_position_line_position, new_lines[new_line_index].size].min
409
438
  self.selection_count = old_selection_count.to_i if old_selection_count.to_i > 0
410
439
  end
411
-
440
+
441
+ def run
442
+ begin
443
+ if scratchpad?
444
+ eval content
445
+ else
446
+ write_dirty_content
447
+ load path
448
+ end
449
+ rescue SyntaxError, StandardError => e
450
+ puts e.full_message
451
+ end
452
+ end
453
+
412
454
  def lines
413
455
  dirty_content.split("\n")
414
456
  end
415
-
457
+
416
458
  def line_for_caret_position(caret_position)
417
459
  lines[line_index_for_caret_position(caret_position.to_i)]
418
460
  end
419
-
461
+
420
462
  def line_index_for_caret_position(caret_position)
421
463
  dirty_content[0...caret_position.to_i].count("\n")
422
464
  end
423
-
465
+
424
466
  def caret_position_for_line_index(line_index)
425
467
  cp = lines[0...line_index].join("\n").size
426
468
  cp += 1 if line_index > 0
427
469
  cp
428
470
  end
429
-
471
+
430
472
  def caret_position_for_caret_position_start_of_line(caret_position)
431
473
  caret_position_for_line_index(line_index_for_caret_position(caret_position))
432
474
  end
@@ -437,23 +479,23 @@ module Glimmer
437
479
  caret_position = caret_position.to_i
438
480
  caret_position - caret_position_for_caret_position_start_of_line(caret_position)
439
481
  end
440
-
482
+
441
483
  def line_caret_positions_for_selection(caret_position, selection_count)
442
484
  line_indices = line_indices_for_selection(caret_position, selection_count)
443
485
  line_caret_positions = line_indices.map { |line_index| caret_position_for_line_index(line_index) }.to_a
444
486
  end
445
-
487
+
446
488
  def end_caret_position_line_index(caret_position, selection_count)
447
489
  end_caret_position = caret_position + selection_count.to_i
448
490
  end_caret_position -= 1 if dirty_content[end_caret_position - 1] == "\n"
449
491
  end_line_index = line_index_for_caret_position(end_caret_position)
450
492
  end
451
-
493
+
452
494
  def lines_for_selection(caret_position, selection_count)
453
495
  line_indices = line_indices_for_selection(caret_position, selection_count)
454
496
  lines[line_indices.first..line_indices.last]
455
497
  end
456
-
498
+
457
499
  def line_indices_for_selection(caret_position, selection_count)
458
500
  start_line_index = line_index_for_caret_position(caret_position)
459
501
  if selection_count.to_i > 0
@@ -463,22 +505,22 @@ module Glimmer
463
505
  end
464
506
  (start_line_index..end_line_index).to_a
465
507
  end
466
-
508
+
467
509
  def children
468
510
  []
469
511
  end
470
-
512
+
471
513
  def to_s
472
514
  path
473
515
  end
474
-
516
+
475
517
  def eql?(other)
476
518
  self.path.eql?(other&.path)
477
519
  end
478
-
520
+
479
521
  def hash
480
522
  self.path.hash
481
523
  end
482
- end
524
+ end
483
525
  end
484
526
  end