glimmer-cs-gladiator 0.3.1 → 0.5.2

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: 650c712355cabdc815732a44b445b14ba7940daf39905019f10b7cdf1c62a935
4
- data.tar.gz: 02377cc0cc8891d0ff3128877ee521c0ae6674c86304b0663ce3956e603e3cd1
3
+ metadata.gz: 5de57f9122b239134e9567e99976ed7c5e859edf89a91f72f15f257597ee0815
4
+ data.tar.gz: 99414159618bea5a0a99fd37772424b452f26471ca6e5dd791ff0ce26135646c
5
5
  SHA512:
6
- metadata.gz: fcefebd0897944ec7e6634027c9e44cce3f705001a452d21ad68647b6e5aec70652c0d6cc7d8dfdd4870ddda6995902625eebc91bf085e7b5fbfe5db35c1b479
7
- data.tar.gz: 4604954cce35346ddb5203f76e45efc492b37421b3f4748be5bbd7b7c34208efb4bb5de92445391f7a537b25aaa74747ffca9fa698139eb11f0a3549e37ede6b
6
+ metadata.gz: 6e7d7c9cca67e351dbf169d785c6ef927bcd911f03a5b4505fdd2973c9b9291b1a72f8475dceeae592bb3d56de85a65f96b9f1cb96575dc91768d13b46f52749
7
+ data.tar.gz: 35c3dc09a4f5d0e29d8e52dd0cbae1ffc879f6d7cd3d5ec0cc2cbabde3475f3898a959c34d7adf60789c9a8c8fa6e2e5ebcfefe9e5e1db8ba0e7281b268ef572
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.3.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.2 - [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 [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
- 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,12 +31,15 @@ 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
 
@@ -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.3.1'
106
+ gem 'glimmer-cs-gladiator', '~> 0.5.2'
103
107
  ```
104
108
 
105
109
  Run:
@@ -112,7 +116,7 @@ And, then instantiate the Gladiator [custom shell](https://github.com/AndyObtiva
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:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.1
1
+ 0.5.2
@@ -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
@@ -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
@@ -2,13 +2,15 @@ module Glimmer
2
2
  class Gladiator
3
3
  class Command
4
4
  class << self
5
+ include Glimmer
6
+
5
7
  def command_history
6
8
  @command_history ||= {}
7
9
  end
8
10
 
9
11
  def command_history_for(file)
10
12
  # keeping a first command to make redo support work by remembering next command after undoing all
11
- command_history[file] ||= [Command.new(file)]
13
+ command_history[file] ||= [Command.new(file)]
12
14
  end
13
15
 
14
16
  def do(file, method = nil, command: nil)
@@ -19,12 +21,14 @@ module Glimmer
19
21
  end
20
22
 
21
23
  def undo(file)
22
- return if command_history_for(file).size <= 1
23
- command_history_for(file).pop.undo
24
+ return if command_history_for(file).size <= 1
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,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,7 +45,7 @@ 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
51
  def name=(the_name)
@@ -61,15 +62,15 @@ module Glimmer
61
62
 
62
63
  def retrieve_children
63
64
  @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|
65
+ # TODO make sure to configure ignore_paths in a preferences dialog
66
+ project_dir.ignore_paths.reduce(false) do |result, ignore_path|
66
67
  result || p.include?(ignore_path)
67
68
  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|
69
+ end.map do |p|
70
+ ::File.file?(p) ? Gladiator::File.new(p, project_dir) : Gladiator::Dir.new(p, project_dir)
71
+ end.sort_by do |c|
72
+ c.path.to_s.downcase
73
+ end.sort_by do |c|
73
74
  c.class.name
74
75
  end.each do |child|
75
76
  child.retrieve_children if child.is_a?(Dir)
@@ -105,7 +106,7 @@ module Glimmer
105
106
 
106
107
  def filter=(value)
107
108
  if value.to_s.empty?
108
- @filter = nil
109
+ @filter = nil
109
110
  else
110
111
  @filter = value
111
112
  end
@@ -117,8 +118,8 @@ module Glimmer
117
118
  def filtered
118
119
  return if filter.nil?
119
120
  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, '')
121
+ children_files.select do |child|
122
+ child_path = child.path.to_s.sub(project_dir.path, '')
122
123
  child_path.downcase.include?(filter.downcase) ||
123
124
  child_path.downcase.gsub(/[_\/.-]/, '').include?(filter.downcase.gsub(/[_\/.-]/, ''))
124
125
  end.sort_by {|c| c.path.to_s.downcase}
@@ -141,14 +142,19 @@ module Glimmer
141
142
  end
142
143
 
143
144
  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) ||
145
+ return (project_dir.selected_child = nil) if selected_path.nil?
146
+ # scratchpad scenario
147
+ if selected_path.empty? #scratchpad
148
+ @selected_child&.write_dirty_content
149
+ return (self.selected_child = File.new)
150
+ end
151
+ full_selected_path = selected_path.include?(project_dir.path) ? selected_path : ::File.join(project_dir.path, selected_path)
152
+ return if ::Dir.exist?(full_selected_path) ||
147
153
  (selected_child && selected_child.path == full_selected_path)
148
154
  selected_path = full_selected_path
149
155
  if ::File.file?(selected_path)
150
156
  @selected_child&.write_dirty_content
151
- new_child = Gladiator::File.new(selected_path)
157
+ new_child = Gladiator::File.new(selected_path, project_dir)
152
158
  begin
153
159
  unless new_child.dirty_content.nil?
154
160
  self.selected_child&.stop_filewatcher
@@ -168,6 +174,12 @@ module Glimmer
168
174
  @selected_child&.path
169
175
  end
170
176
 
177
+ def selected_child=(new_child)
178
+ file_properties = @selected_child&.backup_properties if @selected_child == new_child
179
+ @selected_child = new_child
180
+ @selected_child.restore_properties(file_properties) if file_properties
181
+ end
182
+
171
183
  def delete!
172
184
  FileUtils.rm_rf(path)
173
185
  end
@@ -183,11 +195,7 @@ module Glimmer
183
195
  def hash
184
196
  self.path.hash
185
197
  end
186
- end
198
+ end
187
199
  end
188
200
  end
189
-
190
- at_exit do
191
- Glimmer::Gladiator::Dir.local_dir.selected_child&.write_raw_dirty_content
192
- end
193
201
 
@@ -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.to_s.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,41 @@ 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
-
92
+
93
+ def selection_count
94
+ selection.y - selection.x
95
+ end
96
+
97
+ def selection_count=(value)
98
+ self.selection = Point.new(caret_position, caret_position + value.to_i)
99
+ end
100
+
76
101
  def name=(the_name)
77
- new_path = path.sub(/#{Regexp.escape(@name)}$/, the_name)
102
+ new_path = path.sub(/#{Regexp.escape(@name)}$/, the_name) unless scratchpad?
78
103
  @name = the_name
79
- if ::File.exists?(path)
104
+ if !scratchpad? && ::File.exist?(path)
80
105
  FileUtils.mv(path, new_path)
81
106
  self.path = new_path
82
107
  end
83
108
  end
84
109
 
85
110
  def dirty_content=(the_content)
86
- @dirty_content = the_content if ::File.exist?(path)
111
+ @dirty_content = the_content
87
112
  notify_observers(:content)
88
113
  end
89
-
114
+
90
115
  def start_filewatcher
116
+ return if scratchpad?
91
117
  @filewatcher = Filewatcher.new(@path)
92
- @thread = Thread.new(@filewatcher) do |fw|
118
+ @thread = Thread.new(@filewatcher) do |fw|
93
119
  fw.watch do |filename, event|
94
120
  begin
95
121
  read_dirty_content = ::File.read(path)
@@ -104,27 +130,28 @@ module Glimmer
104
130
  end
105
131
  end
106
132
  end
107
-
133
+
108
134
  def stop_filewatcher
109
135
  @filewatcher&.stop
110
136
  end
111
137
 
112
138
  def format_dirty_content_for_writing!
113
- new_dirty_content = "#{dirty_content.gsub("\r\n", "\n").gsub("\r", "\n").sub(/\n+\z/, '')}\n"
139
+ new_dirty_content = dirty_content.split("\n").map {|line| line.strip.empty? ? line : line.rstrip }.join("\n")
140
+ new_dirty_content = "#{new_dirty_content.gsub("\r\n", "\n").gsub("\r", "\n").sub(/\n+\z/, '')}\n"
114
141
  self.dirty_content = new_dirty_content if new_dirty_content != self.dirty_content
115
142
  end
116
-
143
+
117
144
  def write_dirty_content
118
- return unless ::File.exist?(path)
145
+ return if scratchpad? || !::File.exist?(path)
119
146
  format_dirty_content_for_writing!
120
147
  ::File.write(path, dirty_content) if ::File.exists?(path)
121
148
  rescue => e
122
149
  puts "Error in writing dirty content for #{path}"
123
150
  puts e.full_message
124
151
  end
125
-
152
+
126
153
  def write_raw_dirty_content
127
- return unless ::File.exist?(path)
154
+ return if scratchpad? || !::File.exist?(path)
128
155
  ::File.write(path, dirty_content) if ::File.exists?(path)
129
156
  rescue => e
130
157
  puts "Error in writing raw dirty content for #{path}"
@@ -138,16 +165,17 @@ module Glimmer
138
165
  def current_line
139
166
  lines[line_number - 1]
140
167
  end
141
-
168
+
142
169
  def delete!
143
- FileUtils.rm(path)
170
+ FileUtils.rm(path) unless scratchpad?
144
171
  end
145
-
172
+
146
173
  def prefix_new_line!
147
174
  the_lines = lines
148
175
  the_lines[line_number-1...line_number-1] = [current_line_indentation]
149
176
  self.dirty_content = the_lines.join("\n")
150
177
  self.caret_position = caret_position_for_line_index(line_number-1) + current_line_indentation.size
178
+ self.selection_count = 0
151
179
  end
152
180
 
153
181
  def insert_new_line!
@@ -155,6 +183,7 @@ module Glimmer
155
183
  the_lines[line_number...line_number] = [current_line_indentation]
156
184
  self.dirty_content = the_lines.join("\n")
157
185
  self.caret_position = caret_position_for_line_index(line_number) + current_line_indentation.size
186
+ self.selection_count = 0
158
187
  end
159
188
 
160
189
  def comment_line!
@@ -182,7 +211,7 @@ module Glimmer
182
211
  delta += 2
183
212
  end
184
213
  end
185
- self.dirty_content = new_lines.join("\n")
214
+ self.dirty_content = new_lines.join("\n")
186
215
  if old_selection_count.to_i > 0
187
216
  self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
188
217
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
@@ -192,7 +221,7 @@ module Glimmer
192
221
  self.caret_position = new_caret_position
193
222
  end
194
223
  end
195
-
224
+
196
225
  def indent!
197
226
  new_lines = lines
198
227
  old_lines = lines
@@ -208,7 +237,7 @@ module Glimmer
208
237
  new_lines[the_line_index] = " #{the_line}"
209
238
  end
210
239
  old_caret_position = self.caret_position
211
- self.dirty_content = new_lines.join("\n")
240
+ self.dirty_content = new_lines.join("\n")
212
241
  if old_selection_count.to_i > 0
213
242
  self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
214
243
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
@@ -216,7 +245,7 @@ module Glimmer
216
245
  self.caret_position = old_caret_position + delta
217
246
  end
218
247
  end
219
-
248
+
220
249
  def outdent!
221
250
  new_lines = lines
222
251
  old_lines = lines
@@ -237,7 +266,7 @@ module Glimmer
237
266
  delta = -1
238
267
  end
239
268
  end
240
- self.dirty_content = new_lines.join("\n")
269
+ self.dirty_content = new_lines.join("\n")
241
270
  if old_selection_count.to_i > 0
242
271
  self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
243
272
  self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
@@ -247,7 +276,7 @@ module Glimmer
247
276
  self.caret_position = new_caret_position
248
277
  end
249
278
  end
250
-
279
+
251
280
  def kill_line!
252
281
  new_lines = lines
253
282
  return if new_lines.size < 1
@@ -260,7 +289,7 @@ module Glimmer
260
289
  self.caret_position = caret_position_for_line_index(old_line_index) + [line_position, lines[old_line_index].to_s.size].min
261
290
  self.selection_count = 0
262
291
  end
263
-
292
+
264
293
  def duplicate_line!
265
294
  new_lines = lines
266
295
  old_lines = lines
@@ -284,13 +313,13 @@ module Glimmer
284
313
  self.caret_position = old_caret_position + delta
285
314
  end
286
315
  end
287
-
316
+
288
317
  def find_next
289
- return if find_text.to_s.empty?
318
+ return if find_text.to_s.empty?
290
319
  all_lines = lines
291
320
  the_line_index = line_index_for_caret_position(caret_position)
292
321
  line_position = line_position_for_caret_position(caret_position)
293
- found =
322
+ found = found_text?(caret_position)
294
323
  2.times do |i|
295
324
  rotation = the_line_index
296
325
  all_lines.rotate(rotation).each_with_index do |the_line, the_index|
@@ -307,7 +336,7 @@ module Glimmer
307
336
  end
308
337
  end
309
338
  end
310
-
339
+
311
340
  def find_previous
312
341
  return if find_text.to_s.empty?
313
342
  all_lines = lines
@@ -332,7 +361,7 @@ module Glimmer
332
361
  end
333
362
  end
334
363
  end
335
-
364
+
336
365
  def ensure_find_next
337
366
  return if find_text.to_s.empty? || dirty_content.to_s.strip.size < 1
338
367
  find_next unless found_text?(self.caret_position)
@@ -341,37 +370,45 @@ module Glimmer
341
370
  def found_text?(caret_position)
342
371
  dirty_content[caret_position.to_i, find_text.to_s.size].to_s.downcase == find_text.to_s.downcase
343
372
  end
344
-
373
+
345
374
  def replace_next!
346
375
  return if find_text.to_s.empty? || dirty_content.to_s.strip.size < 1
347
376
  ensure_find_next
348
377
  new_dirty_content = dirty_content
349
378
  new_dirty_content[caret_position, find_text.size] = replace_text.to_s
350
379
  self.dirty_content = new_dirty_content
351
- find_next
380
+ find_next
352
381
  find_next if replace_text.to_s.include?(find_text) && !replace_text.to_s.start_with?(find_text)
353
382
  end
354
-
383
+
355
384
  def page_up
356
385
  self.selection_count = 0
357
386
  self.line_number = [(self.line_number - 15), 1].max
358
387
  end
359
-
388
+
360
389
  def page_down
361
390
  self.selection_count = 0
362
391
  self.line_number = [(self.line_number + 15), lines.size].min
363
392
  end
364
-
393
+
365
394
  def home
366
395
  self.selection_count = 0
367
396
  self.line_number = 1
368
397
  end
369
-
398
+
370
399
  def end
371
400
  self.selection_count = 0
372
401
  self.line_number = lines.size
373
402
  end
374
-
403
+
404
+ def start_of_line
405
+ self.caret_position = caret_position_for_line_index(self.line_number - 1)
406
+ end
407
+
408
+ def end_of_line
409
+ self.caret_position = caret_position_for_line_index(self.line_number) - 1
410
+ end
411
+
375
412
  def move_up!
376
413
  old_lines = lines
377
414
  return if old_lines.size < 2
@@ -390,7 +427,7 @@ module Glimmer
390
427
  self.caret_position = caret_position_for_line_index(new_line_index) + [old_caret_position_line_position, new_lines[new_line_index].size].min
391
428
  self.selection_count = old_selection_count.to_i if old_selection_count.to_i > 0
392
429
  end
393
-
430
+
394
431
  def move_down!
395
432
  old_lines = lines
396
433
  return if old_lines.size < 2
@@ -409,25 +446,38 @@ module Glimmer
409
446
  self.caret_position = caret_position_for_line_index(new_line_index) + [old_caret_position_line_position, new_lines[new_line_index].size].min
410
447
  self.selection_count = old_selection_count.to_i if old_selection_count.to_i > 0
411
448
  end
412
-
449
+
450
+ def run
451
+ begin
452
+ if scratchpad?
453
+ eval content
454
+ else
455
+ write_dirty_content
456
+ load path
457
+ end
458
+ rescue SyntaxError, StandardError => e
459
+ puts e.full_message
460
+ end
461
+ end
462
+
413
463
  def lines
414
464
  dirty_content.split("\n")
415
465
  end
416
-
466
+
417
467
  def line_for_caret_position(caret_position)
418
468
  lines[line_index_for_caret_position(caret_position.to_i)]
419
469
  end
420
-
470
+
421
471
  def line_index_for_caret_position(caret_position)
422
472
  dirty_content[0...caret_position.to_i].count("\n")
423
473
  end
424
-
474
+
425
475
  def caret_position_for_line_index(line_index)
426
476
  cp = lines[0...line_index].join("\n").size
427
477
  cp += 1 if line_index > 0
428
478
  cp
429
479
  end
430
-
480
+
431
481
  def caret_position_for_caret_position_start_of_line(caret_position)
432
482
  caret_position_for_line_index(line_index_for_caret_position(caret_position))
433
483
  end
@@ -438,23 +488,23 @@ module Glimmer
438
488
  caret_position = caret_position.to_i
439
489
  caret_position - caret_position_for_caret_position_start_of_line(caret_position)
440
490
  end
441
-
491
+
442
492
  def line_caret_positions_for_selection(caret_position, selection_count)
443
493
  line_indices = line_indices_for_selection(caret_position, selection_count)
444
494
  line_caret_positions = line_indices.map { |line_index| caret_position_for_line_index(line_index) }.to_a
445
495
  end
446
-
496
+
447
497
  def end_caret_position_line_index(caret_position, selection_count)
448
498
  end_caret_position = caret_position + selection_count.to_i
449
499
  end_caret_position -= 1 if dirty_content[end_caret_position - 1] == "\n"
450
500
  end_line_index = line_index_for_caret_position(end_caret_position)
451
501
  end
452
-
502
+
453
503
  def lines_for_selection(caret_position, selection_count)
454
504
  line_indices = line_indices_for_selection(caret_position, selection_count)
455
505
  lines[line_indices.first..line_indices.last]
456
506
  end
457
-
507
+
458
508
  def line_indices_for_selection(caret_position, selection_count)
459
509
  start_line_index = line_index_for_caret_position(caret_position)
460
510
  if selection_count.to_i > 0
@@ -464,22 +514,22 @@ module Glimmer
464
514
  end
465
515
  (start_line_index..end_line_index).to_a
466
516
  end
467
-
517
+
468
518
  def children
469
519
  []
470
520
  end
471
-
521
+
472
522
  def to_s
473
523
  path
474
524
  end
475
-
525
+
476
526
  def eql?(other)
477
527
  self.path.eql?(other&.path)
478
528
  end
479
-
529
+
480
530
  def hash
481
531
  self.path.hash
482
532
  end
483
- end
533
+ end
484
534
  end
485
535
  end