glimmer-cs-gladiator 0.4.1 → 0.5.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: 22778fafc962d8b91e8901b2a70ca645b5cfd5a3dbcf089efa688aecaf643924
4
- data.tar.gz: '0190bf73da9810409ef4cd6f392847c9186761b8e3e71d8094e0e82becfe4600'
3
+ metadata.gz: bb031342309fc665039e7a7a64e4a3996f3e659a17767527d47da00e5d7ede50
4
+ data.tar.gz: 434912cc7be89f07d1e14cdf94ff1511ea7c6ca8e5b09d19654850c3680cb443
5
5
  SHA512:
6
- metadata.gz: 513bedaec5b4d2ec4285b8ab32663099207dc7638588b31d3007b6acf87dcb5c1b03d276d9900f485ea1da1fcd2a3307e0463331caa5e3a87efe00c63632f17d
7
- data.tar.gz: f0fec7f0f3183602e7c287f7edb777b367df756ac76e6e095091821328cc7891623b03f8b6428c24502f8fc90e6aefe5a8b591029c42b2a569a3e115dc037ee8
6
+ metadata.gz: 6c594f90f5643023e7424747b9abd1959778c9d8d878a99e6b577f7f94e796f5c930c68eccfe267b84860f330cc0ca3004db784fbd6e59f73d62abe41988a6d9
7
+ data.tar.gz: db5c00808053ba03f441fc3fdf9d00afd627cc688cb8baa036fe749a679eba34628dd5dd629d7d20ef56614f2429bdce0918c7f294cf10fec611e74b20251923
data/README.md CHANGED
@@ -1,14 +1,12 @@
1
- # <img src='https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-gladiator/master/images/glimmer-cs-gladiator-logo.svg' height=85 /> Gladiator 0.4.1 - [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.5.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
- (Now, slightly less ugly with Ruby syntax highlighting colors)
8
-
9
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).
10
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).
11
- 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.
12
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.
13
11
 
14
12
  ## Features
@@ -24,7 +22,7 @@ Gladiator currently supports the following text editing features (including keyb
24
22
  - Jump to Line (CMD+L)
25
23
  - Multiple tab support (CMD+SHIFT+[ & CMD+SHIFT+] for tab navigation. CMD+1-9 to jump to a specific tab)
26
24
  - Remember opened tabs, caret position, top line, window size, and window location
27
- - Autosave on focus out/quit/open new file
25
+ - Autosave on focus out/quit/open new file
28
26
  - Duplicate Line(s)/selection (CMD+D)
29
27
  - Kill Line(s)/selection (CMD+K)
30
28
  - Move line/selection up (CMD+UP)
@@ -33,14 +31,15 @@ Gladiator currently supports the following text editing features (including keyb
33
31
  - Indent/Unindent line/selection (CMD+] & CMD+[)
34
32
  - Insert/Prefix New Line (CMD+ENTER & CMD+SHIFT+ENTER)
35
33
  - Drag and Drop Text Editor Split Screen (drag a file from File Tree or File Lookup List, and it splits the screen)
36
- - Run current Ruby file via Run Menu (CMD+SHIFT+R)
34
+ - Run Ruby code (CMD+SHIFT+R)
35
+ - Scratchpad for running arbitrary Ruby/Glimmer code without saving to disk (CMD+SHIFT+S)
37
36
  - Change Split Orientation to Horizontal/Vertical via View Menu (CMD+SHIFT+O)
38
37
 
39
38
  ## Platforms
40
39
 
41
40
  - Mac: Gladiator works best on the Mac. This is the platform it is most used on and receives the most maintenance for.
42
- - Windows: Gladiator works OK on Windows, but has some annoying bugs.
43
- - 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.
44
43
 
45
44
  ## Pre-requisites
46
45
 
@@ -100,11 +99,11 @@ bin/gladiator relative-or-absolute-path/to/project
100
99
 
101
100
  ### Glimmer Custom Shell Reuse
102
101
 
103
- 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
104
103
  following to the application's `Gemfile`:
105
104
 
106
105
  ```
107
- gem 'glimmer-cs-gladiator', '~> 0.4.1'
106
+ gem 'glimmer-cs-gladiator', '~> 0.5.0'
108
107
  ```
109
108
 
110
109
  Run:
@@ -117,7 +116,7 @@ And, then instantiate the Gladiator [custom shell](https://github.com/AndyObtiva
117
116
 
118
117
  ## Env Var Options
119
118
 
120
- Gladiator opens with the current directory as the root by default.
119
+ Gladiator opens with the current directory as the root by default.
121
120
  If you would like to open another directory, set `LOCAL_DIR` environment variable.
122
121
 
123
122
  Example:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.1
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,39 +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
11
  attr_accessor :selected_child, :filter, :children, :filtered_path_options, :filtered_path, :display_path, :ignore_paths
30
- attr_reader :name, :parent, :path, :is_local_dir
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))
37
- @ignore_paths = ['packages', 'tmp', 'vendor']
30
+ @ignore_paths = ['.gladiator', '.git', 'coverage', 'packages', 'tmp', 'vendor']
38
31
  self.filtered_path_options = []
39
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
40
41
 
41
42
  def path=(the_path)
42
43
  @path = the_path
@@ -44,32 +45,24 @@ module Glimmer
44
45
  end
45
46
 
46
47
  def generate_display_path
47
- 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(/^\//, '')
48
49
  end
49
50
 
50
- def name=(the_name)
51
- self.display_path = display_path.sub(/#{Regexp.escape(@name)}$/, the_name)
52
- @name = the_name
53
- new_path = ::File.expand_path(display_path)
54
- FileUtils.mv(path, new_path)
55
- self.path = display_path
56
- end
57
-
58
51
  def children
59
52
  @children ||= retrieve_children
60
53
  end
61
54
 
62
55
  def retrieve_children
63
56
  @children = ::Dir.glob(::File.join(@path, '*')).reject do |p|
64
- # TODO make sure to configure ignore_paths in a preferences dialog
65
- Dir.local_dir.ignore_paths.reduce(false) do |result, ignore_path|
57
+ # TODO make sure to configure ignore_paths in a preferences dialog
58
+ project_dir.ignore_paths.reduce(false) do |result, ignore_path|
66
59
  result || p.include?(ignore_path)
67
60
  end
68
- end.map do |p|
69
- ::File.file?(p) ? Gladiator::File.new(p) : Gladiator::Dir.new(p)
70
- end.sort_by do |c|
71
- c.path.to_s.downcase
72
- end.sort_by do |c|
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|
73
66
  c.class.name
74
67
  end.each do |child|
75
68
  child.retrieve_children if child.is_a?(Dir)
@@ -105,7 +98,7 @@ module Glimmer
105
98
 
106
99
  def filter=(value)
107
100
  if value.to_s.empty?
108
- @filter = nil
101
+ @filter = nil
109
102
  else
110
103
  @filter = value
111
104
  end
@@ -117,8 +110,8 @@ module Glimmer
117
110
  def filtered
118
111
  return if filter.nil?
119
112
  children_files = !@last_filter.to_s.empty? && filter.downcase.start_with?(@last_filter.downcase) ? @last_filtered : all_children_files
120
- children_files.select do |child|
121
- 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, '')
122
115
  child_path.downcase.include?(filter.downcase) ||
123
116
  child_path.downcase.gsub(/[_\/.-]/, '').include?(filter.downcase.gsub(/[_\/.-]/, ''))
124
117
  end.sort_by {|c| c.path.to_s.downcase}
@@ -141,14 +134,19 @@ module Glimmer
141
134
  end
142
135
 
143
136
  def selected_child_path=(selected_path)
144
- full_selected_path = selected_path.include?(Dir.local_dir.path) ? selected_path : ::File.join(Dir.local_dir.path, selected_path)
145
- return if selected_path.nil? ||
146
- ::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) ||
147
145
  (selected_child && selected_child.path == full_selected_path)
148
146
  selected_path = full_selected_path
149
147
  if ::File.file?(selected_path)
150
148
  @selected_child&.write_dirty_content
151
- new_child = Gladiator::File.new(selected_path)
149
+ new_child = Gladiator::File.new(selected_path, project_dir)
152
150
  begin
153
151
  unless new_child.dirty_content.nil?
154
152
  self.selected_child&.stop_filewatcher
@@ -168,6 +166,12 @@ module Glimmer
168
166
  @selected_child&.path
169
167
  end
170
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
+
171
175
  def delete!
172
176
  FileUtils.rm_rf(path)
173
177
  end
@@ -183,11 +187,7 @@ module Glimmer
183
187
  def hash
184
188
  self.path.hash
185
189
  end
186
- end
190
+ end
187
191
  end
188
192
  end
189
-
190
- at_exit do
191
- Glimmer::Gladiator::Dir.local_dir.selected_child&.write_raw_dirty_content
192
- end
193
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
-
5
+
8
6
  attr_accessor :dirty_content, :line_numbers_content, :selection, :line_number, :find_text, :replace_text, :top_pixel, :display_path, :case_sensitive
9
- attr_reader :name, :path
7
+ attr_reader :name, :path, :project_dir
10
8
 
11
- def initialize(path)
12
- raise "Not a file path: #{path}" unless ::File.file?(path)
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)
13
+ @name = path.empty? ? 'Scratchpad' : ::File.basename(path)
14
+ self.path = ::File.expand_path(path) unless path.empty?
16
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,16 +39,34 @@ 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
72
  value = value.gsub("\t", ' ')
@@ -68,7 +85,7 @@ module Glimmer
68
85
  self.selection = Point.new(value, value + selection_count.to_i)
69
86
  self.top_pixel = old_top_pixel
70
87
  end
71
-
88
+
72
89
  def caret_position
73
90
  selection.x
74
91
  end
@@ -76,28 +93,20 @@ module Glimmer
76
93
  def selection_count
77
94
  selection.y - selection.x
78
95
  end
79
-
96
+
80
97
  def selection_count=(value)
81
98
  self.selection = Point.new(caret_position, caret_position + value.to_i)
82
- end
83
-
84
- def name=(the_name)
85
- new_path = path.sub(/#{Regexp.escape(@name)}$/, the_name)
86
- @name = the_name
87
- if ::File.exists?(path)
88
- FileUtils.mv(path, new_path)
89
- self.path = new_path
90
- end
91
99
  end
92
-
100
+
93
101
  def dirty_content=(the_content)
94
- @dirty_content = the_content if ::File.exist?(path)
102
+ @dirty_content = the_content
95
103
  notify_observers(:content)
96
104
  end
97
-
105
+
98
106
  def start_filewatcher
107
+ return if scratchpad?
99
108
  @filewatcher = Filewatcher.new(@path)
100
- @thread = Thread.new(@filewatcher) do |fw|
109
+ @thread = Thread.new(@filewatcher) do |fw|
101
110
  fw.watch do |filename, event|
102
111
  begin
103
112
  read_dirty_content = ::File.read(path)
@@ -112,27 +121,28 @@ module Glimmer
112
121
  end
113
122
  end
114
123
  end
115
-
124
+
116
125
  def stop_filewatcher
117
126
  @filewatcher&.stop
118
127
  end
119
128
 
120
129
  def format_dirty_content_for_writing!
121
- 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"
122
132
  self.dirty_content = new_dirty_content if new_dirty_content != self.dirty_content
123
133
  end
124
-
134
+
125
135
  def write_dirty_content
126
- return unless ::File.exist?(path)
136
+ return if scratchpad? || !::File.exist?(path)
127
137
  format_dirty_content_for_writing!
128
138
  ::File.write(path, dirty_content) if ::File.exists?(path)
129
139
  rescue => e
130
140
  puts "Error in writing dirty content for #{path}"
131
141
  puts e.full_message
132
142
  end
133
-
143
+
134
144
  def write_raw_dirty_content
135
- return unless ::File.exist?(path)
145
+ return if scratchpad? || !::File.exist?(path)
136
146
  ::File.write(path, dirty_content) if ::File.exists?(path)
137
147
  rescue => e
138
148
  puts "Error in writing raw dirty content for #{path}"
@@ -143,19 +153,20 @@ module Glimmer
143
153
  current_line.to_s.match(/^(\s+)/).to_a[1].to_s
144
154
  end
145
155
 
146
- def current_line
156
+ def current_line
147
157
  lines[line_number - 1]
148
158
  end
149
159
 
150
160
  def delete!
151
- FileUtils.rm(path)
161
+ FileUtils.rm(path) unless scratchpad?
152
162
  end
153
-
163
+
154
164
  def prefix_new_line!
155
165
  the_lines = lines
156
166
  the_lines[line_number-1...line_number-1] = [current_line_indentation]
157
167
  self.dirty_content = the_lines.join("\n")
158
168
  self.caret_position = caret_position_for_line_index(line_number-1) + current_line_indentation.size
169
+ self.selection_count = 0
159
170
  end
160
171
 
161
172
  def insert_new_line!
@@ -163,6 +174,7 @@ module Glimmer
163
174
  the_lines[line_number...line_number] = [current_line_indentation]
164
175
  self.dirty_content = the_lines.join("\n")
165
176
  self.caret_position = caret_position_for_line_index(line_number) + current_line_indentation.size
177
+ self.selection_count = 0
166
178
  end
167
179
 
168
180
  def comment_line!
@@ -190,7 +202,7 @@ module Glimmer
190
202
  delta += 2
191
203
  end
192
204
  end
193
- self.dirty_content = new_lines.join("\n")
205
+ self.dirty_content = new_lines.join("\n")
194
206
  if old_selection_count.to_i > 0
195
207
  self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
196
208
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
@@ -200,7 +212,7 @@ module Glimmer
200
212
  self.caret_position = new_caret_position
201
213
  end
202
214
  end
203
-
215
+
204
216
  def indent!
205
217
  new_lines = lines
206
218
  old_lines = lines
@@ -216,16 +228,15 @@ module Glimmer
216
228
  new_lines[the_line_index] = " #{the_line}"
217
229
  end
218
230
  old_caret_position = self.caret_position
219
- self.dirty_content = new_lines.join("\n")
231
+ self.dirty_content = new_lines.join("\n")
220
232
  if old_selection_count.to_i > 0
221
- caret_position_value = caret_position_for_line_index(old_caret_position_line_index)
222
- selection_count_value = (caret_position_for_line_index(old_end_caret_line_index + 1) - caret_position_value)
223
- self.selection = Point.new(caret_position_value, caret_position_value + selection_count_value)
233
+ self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
234
+ self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
224
235
  else
225
236
  self.caret_position = old_caret_position + delta
226
237
  end
227
238
  end
228
-
239
+
229
240
  def outdent!
230
241
  new_lines = lines
231
242
  old_lines = lines
@@ -246,18 +257,17 @@ module Glimmer
246
257
  delta = -1
247
258
  end
248
259
  end
249
- self.dirty_content = new_lines.join("\n")
260
+ self.dirty_content = new_lines.join("\n")
250
261
  if old_selection_count.to_i > 0
251
- caret_position_value = caret_position_for_line_index(old_caret_position_line_index)
252
- selection_count_value = (caret_position_for_line_index(old_end_caret_line_index + 1) - caret_position_value)
253
- self.selection = Point.new(caret_position_value, caret_position_value + selection_count_value)
262
+ self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
263
+ self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
254
264
  else
255
265
  new_caret_position = old_caret_position + delta
256
266
  new_caret_position = [new_caret_position, old_caret_position_line_caret_position].max
257
267
  self.caret_position = new_caret_position
258
268
  end
259
269
  end
260
-
270
+
261
271
  def kill_line!
262
272
  new_lines = lines
263
273
  return if new_lines.size < 1
@@ -270,7 +280,7 @@ module Glimmer
270
280
  self.caret_position = caret_position_for_line_index(old_line_index) + [line_position, lines[old_line_index].to_s.size].min
271
281
  self.selection_count = 0
272
282
  end
273
-
283
+
274
284
  def duplicate_line!
275
285
  new_lines = lines
276
286
  old_lines = lines
@@ -294,9 +304,9 @@ module Glimmer
294
304
  self.caret_position = old_caret_position + delta
295
305
  end
296
306
  end
297
-
307
+
298
308
  def find_next
299
- return if find_text.to_s.empty?
309
+ return if find_text.to_s.empty?
300
310
  all_lines = lines
301
311
  the_line_index = line_index_for_caret_position(caret_position)
302
312
  line_position = line_position_for_caret_position(caret_position)
@@ -312,13 +322,12 @@ module Glimmer
312
322
  if occurrence_index
313
323
  self.caret_position = caret_position_for_line_index(the_index) + start_position + occurrence_index
314
324
  self.selection_count = find_text.to_s.size
315
- self.selection = Point.new(self.caret_position, self.caret_position + self.selection_count)
316
325
  return
317
326
  end
318
327
  end
319
328
  end
320
329
  end
321
-
330
+
322
331
  def find_previous
323
332
  return if find_text.to_s.empty?
324
333
  all_lines = lines
@@ -343,7 +352,7 @@ module Glimmer
343
352
  end
344
353
  end
345
354
  end
346
-
355
+
347
356
  def ensure_find_next
348
357
  return if find_text.to_s.empty? || dirty_content.to_s.strip.size < 1
349
358
  find_next unless found_text?(self.caret_position)
@@ -352,37 +361,45 @@ module Glimmer
352
361
  def found_text?(caret_position)
353
362
  dirty_content[caret_position.to_i, find_text.to_s.size].to_s.downcase == find_text.to_s.downcase
354
363
  end
355
-
364
+
356
365
  def replace_next!
357
366
  return if find_text.to_s.empty? || dirty_content.to_s.strip.size < 1
358
367
  ensure_find_next
359
368
  new_dirty_content = dirty_content
360
369
  new_dirty_content[caret_position, find_text.size] = replace_text.to_s
361
370
  self.dirty_content = new_dirty_content
362
- find_next
371
+ find_next
363
372
  find_next if replace_text.to_s.include?(find_text) && !replace_text.to_s.start_with?(find_text)
364
373
  end
365
-
374
+
366
375
  def page_up
367
376
  self.selection_count = 0
368
377
  self.line_number = [(self.line_number - 15), 1].max
369
378
  end
370
-
379
+
371
380
  def page_down
372
381
  self.selection_count = 0
373
382
  self.line_number = [(self.line_number + 15), lines.size].min
374
383
  end
375
-
384
+
376
385
  def home
377
386
  self.selection_count = 0
378
387
  self.line_number = 1
379
388
  end
380
-
389
+
381
390
  def end
382
391
  self.selection_count = 0
383
392
  self.line_number = lines.size
384
393
  end
385
-
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
+
386
403
  def move_up!
387
404
  old_lines = lines
388
405
  return if old_lines.size < 2
@@ -401,7 +418,7 @@ module Glimmer
401
418
  self.caret_position = caret_position_for_line_index(new_line_index) + [old_caret_position_line_position, new_lines[new_line_index].size].min
402
419
  self.selection_count = old_selection_count.to_i if old_selection_count.to_i > 0
403
420
  end
404
-
421
+
405
422
  def move_down!
406
423
  old_lines = lines
407
424
  return if old_lines.size < 2
@@ -420,25 +437,38 @@ module Glimmer
420
437
  self.caret_position = caret_position_for_line_index(new_line_index) + [old_caret_position_line_position, new_lines[new_line_index].size].min
421
438
  self.selection_count = old_selection_count.to_i if old_selection_count.to_i > 0
422
439
  end
423
-
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
+
424
454
  def lines
425
455
  dirty_content.split("\n")
426
456
  end
427
-
457
+
428
458
  def line_for_caret_position(caret_position)
429
459
  lines[line_index_for_caret_position(caret_position.to_i)]
430
460
  end
431
-
461
+
432
462
  def line_index_for_caret_position(caret_position)
433
463
  dirty_content[0...caret_position.to_i].count("\n")
434
464
  end
435
-
465
+
436
466
  def caret_position_for_line_index(line_index)
437
467
  cp = lines[0...line_index].join("\n").size
438
468
  cp += 1 if line_index > 0
439
469
  cp
440
470
  end
441
-
471
+
442
472
  def caret_position_for_caret_position_start_of_line(caret_position)
443
473
  caret_position_for_line_index(line_index_for_caret_position(caret_position))
444
474
  end
@@ -449,23 +479,23 @@ module Glimmer
449
479
  caret_position = caret_position.to_i
450
480
  caret_position - caret_position_for_caret_position_start_of_line(caret_position)
451
481
  end
452
-
482
+
453
483
  def line_caret_positions_for_selection(caret_position, selection_count)
454
484
  line_indices = line_indices_for_selection(caret_position, selection_count)
455
485
  line_caret_positions = line_indices.map { |line_index| caret_position_for_line_index(line_index) }.to_a
456
486
  end
457
-
487
+
458
488
  def end_caret_position_line_index(caret_position, selection_count)
459
489
  end_caret_position = caret_position + selection_count.to_i
460
490
  end_caret_position -= 1 if dirty_content[end_caret_position - 1] == "\n"
461
491
  end_line_index = line_index_for_caret_position(end_caret_position)
462
492
  end
463
-
493
+
464
494
  def lines_for_selection(caret_position, selection_count)
465
495
  line_indices = line_indices_for_selection(caret_position, selection_count)
466
496
  lines[line_indices.first..line_indices.last]
467
497
  end
468
-
498
+
469
499
  def line_indices_for_selection(caret_position, selection_count)
470
500
  start_line_index = line_index_for_caret_position(caret_position)
471
501
  if selection_count.to_i > 0
@@ -475,22 +505,22 @@ module Glimmer
475
505
  end
476
506
  (start_line_index..end_line_index).to_a
477
507
  end
478
-
508
+
479
509
  def children
480
510
  []
481
511
  end
482
-
512
+
483
513
  def to_s
484
514
  path
485
515
  end
486
-
516
+
487
517
  def eql?(other)
488
518
  self.path.eql?(other&.path)
489
519
  end
490
-
520
+
491
521
  def hash
492
522
  self.path.hash
493
523
  end
494
- end
524
+ end
495
525
  end
496
526
  end