glimmer-cs-gladiator 0.7.0 → 0.8.1
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 +4 -4
- data/CHANGELOG.md +45 -0
- data/LICENSE.txt +20 -20
- data/README.md +60 -14
- data/VERSION +1 -1
- data/bin/gladiator +34 -13
- data/bin/gladiator-setup +80 -0
- data/bin/glimmer-cs-gladiator +22 -0
- data/glimmer-cs-gladiator.gemspec +20 -9
- data/lib/glimmer-cs-gladiator.rb +30 -29
- data/lib/glimmer-cs-gladiator/launch.rb +6 -0
- data/lib/models/glimmer/gladiator/command.rb +112 -112
- data/lib/models/glimmer/gladiator/dir.rb +8 -4
- data/lib/models/glimmer/gladiator/file.rb +662 -599
- data/lib/views/glimmer/gladiator.rb +510 -801
- data/lib/views/glimmer/gladiator/file_explorer_tree.rb +244 -0
- data/lib/views/glimmer/gladiator/file_lookup_list.rb +64 -0
- data/lib/views/glimmer/gladiator/gladiator_menu_bar.rb +189 -0
- data/lib/views/glimmer/gladiator/progress_shell.rb +31 -0
- data/lib/views/glimmer/gladiator/text_editor.rb +40 -19
- metadata +32 -6
- data/bin/gladiator_runner.rb +0 -6
data/lib/glimmer-cs-gladiator.rb
CHANGED
@@ -1,29 +1,30 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.expand_path('..', __FILE__))
|
2
|
-
|
3
|
-
require '
|
4
|
-
require '
|
5
|
-
require '
|
6
|
-
require '
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
@
|
23
|
-
|
24
|
-
|
25
|
-
@
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
1
|
+
$LOAD_PATH.unshift(File.expand_path('..', __FILE__))
|
2
|
+
|
3
|
+
require 'glimmer-dsl-swt'
|
4
|
+
require 'filewatcher'
|
5
|
+
require 'clipboard'
|
6
|
+
require 'puts_debuggerer'
|
7
|
+
require 'views/glimmer/gladiator'
|
8
|
+
|
9
|
+
# Custom Composite Initializer (avoid default margins)
|
10
|
+
Glimmer::SWT::WidgetProxy::DEFAULT_INITIALIZERS['composite'] = lambda do |composite|
|
11
|
+
if composite.get_layout.nil?
|
12
|
+
layout = GridLayout.new
|
13
|
+
composite.layout = layout
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Custom LayoutProxy initialize method (avoid default margins)
|
18
|
+
module Glimmer
|
19
|
+
module SWT
|
20
|
+
class LayoutProxy
|
21
|
+
def initialize(underscored_layout_name, widget_proxy, args)
|
22
|
+
@underscored_layout_name = underscored_layout_name
|
23
|
+
@widget_proxy = widget_proxy
|
24
|
+
args = SWTProxy.constantify_args(args)
|
25
|
+
@swt_layout = self.class.swt_layout_class_for(underscored_layout_name).new(*args)
|
26
|
+
@widget_proxy.swt_widget.setLayout(@swt_layout)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,112 +1,112 @@
|
|
1
|
-
module Glimmer
|
2
|
-
class Gladiator
|
3
|
-
class Command
|
4
|
-
include Glimmer
|
5
|
-
|
6
|
-
class << self
|
7
|
-
include Glimmer
|
8
|
-
|
9
|
-
def command_history
|
10
|
-
@command_history ||= {}
|
11
|
-
end
|
12
|
-
|
13
|
-
def command_history_for(file)
|
14
|
-
# keeping a first command to make redo support work by remembering next command after undoing all
|
15
|
-
command_history[file] ||= [Command.new(file)]
|
16
|
-
end
|
17
|
-
|
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
|
30
|
-
end
|
31
|
-
|
32
|
-
def undo(file)
|
33
|
-
return if command_history_for(file).size <= 1
|
34
|
-
command = command_history_for(file).pop
|
35
|
-
command&.undo
|
36
|
-
end
|
37
|
-
|
38
|
-
def redo(file)
|
39
|
-
command = command_history_for(file).last
|
40
|
-
command&.redo
|
41
|
-
end
|
42
|
-
|
43
|
-
def clear(file)
|
44
|
-
command_history[file] = [Command.new(file)]
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
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
|
50
|
-
|
51
|
-
def initialize(file, method = nil, *args)
|
52
|
-
@file = file
|
53
|
-
@method = method
|
54
|
-
@args = args
|
55
|
-
end
|
56
|
-
|
57
|
-
|
58
|
-
def native?
|
59
|
-
@method.nil?
|
60
|
-
end
|
61
|
-
|
62
|
-
def do
|
63
|
-
return if native?
|
64
|
-
backup
|
65
|
-
execute
|
66
|
-
end
|
67
|
-
|
68
|
-
def undo
|
69
|
-
return if native?
|
70
|
-
restore
|
71
|
-
end
|
72
|
-
|
73
|
-
def redo
|
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
|
78
|
-
Command.do(next_command.file, command: next_command)
|
79
|
-
end
|
80
|
-
|
81
|
-
def backup
|
82
|
-
@previous_file_dirty_content = @file.dirty_content.clone
|
83
|
-
@previous_file_caret_position = @file.caret_position
|
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
|
89
|
-
end
|
90
|
-
|
91
|
-
def restore
|
92
|
-
@file.dirty_content = @previous_file_dirty_content.clone
|
93
|
-
@file.caret_position = @previous_file_caret_position
|
94
|
-
@file.selection_count = @previous_file_selection_count
|
95
|
-
end
|
96
|
-
|
97
|
-
def execute
|
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
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
1
|
+
module Glimmer
|
2
|
+
class Gladiator
|
3
|
+
class Command
|
4
|
+
include Glimmer
|
5
|
+
|
6
|
+
class << self
|
7
|
+
include Glimmer
|
8
|
+
|
9
|
+
def command_history
|
10
|
+
@command_history ||= {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def command_history_for(file)
|
14
|
+
# keeping a first command to make redo support work by remembering next command after undoing all
|
15
|
+
command_history[file] ||= [Command.new(file)]
|
16
|
+
end
|
17
|
+
|
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
|
30
|
+
end
|
31
|
+
|
32
|
+
def undo(file)
|
33
|
+
return if command_history_for(file).size <= 1
|
34
|
+
command = command_history_for(file).pop
|
35
|
+
command&.undo
|
36
|
+
end
|
37
|
+
|
38
|
+
def redo(file)
|
39
|
+
command = command_history_for(file).last
|
40
|
+
command&.redo
|
41
|
+
end
|
42
|
+
|
43
|
+
def clear(file)
|
44
|
+
command_history[file] = [Command.new(file)]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
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
|
50
|
+
|
51
|
+
def initialize(file, method = nil, *args)
|
52
|
+
@file = file
|
53
|
+
@method = method
|
54
|
+
@args = args
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def native?
|
59
|
+
@method.nil?
|
60
|
+
end
|
61
|
+
|
62
|
+
def do
|
63
|
+
return if native?
|
64
|
+
backup
|
65
|
+
execute
|
66
|
+
end
|
67
|
+
|
68
|
+
def undo
|
69
|
+
return if native?
|
70
|
+
restore
|
71
|
+
end
|
72
|
+
|
73
|
+
def redo
|
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
|
78
|
+
Command.do(next_command.file, command: next_command)
|
79
|
+
end
|
80
|
+
|
81
|
+
def backup
|
82
|
+
@previous_file_dirty_content = @file.dirty_content.clone
|
83
|
+
@previous_file_caret_position = @file.caret_position
|
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
|
89
|
+
end
|
90
|
+
|
91
|
+
def restore
|
92
|
+
@file.dirty_content = @previous_file_dirty_content.clone
|
93
|
+
@file.caret_position = @previous_file_caret_position
|
94
|
+
@file.selection_count = @previous_file_selection_count
|
95
|
+
end
|
96
|
+
|
97
|
+
def execute
|
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
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -5,6 +5,8 @@ module Glimmer
|
|
5
5
|
class Dir
|
6
6
|
include Glimmer
|
7
7
|
include Glimmer::DataBinding::ObservableModel
|
8
|
+
|
9
|
+
IGNORE_PATHS = ['.gladiator', '.git', 'coverage', 'packages', 'node_modules', 'tmp', 'vendor', 'pkg', 'dist']
|
8
10
|
|
9
11
|
attr_accessor :selected_child, :filter, :children, :filtered_path_options, :filtered_path, :display_path, :ignore_paths
|
10
12
|
attr_reader :name, :parent, :path
|
@@ -36,7 +38,7 @@ module Glimmer
|
|
36
38
|
end
|
37
39
|
self.path = ::File.expand_path(path)
|
38
40
|
@name = ::File.basename(::File.expand_path(path))
|
39
|
-
@ignore_paths =
|
41
|
+
@ignore_paths = IGNORE_PATHS
|
40
42
|
self.filtered_path_options = []
|
41
43
|
end
|
42
44
|
|
@@ -72,8 +74,10 @@ module Glimmer
|
|
72
74
|
def retrieve_children
|
73
75
|
@children = ::Dir.glob(::File.join(@path, '*')).reject do |p|
|
74
76
|
# TODO make sure to configure ignore_paths in a preferences dialog
|
75
|
-
project_dir
|
76
|
-
|
77
|
+
if project_dir == self
|
78
|
+
project_dir.ignore_paths.any? do |ignore_path|
|
79
|
+
p.include?(ignore_path)
|
80
|
+
end
|
77
81
|
end
|
78
82
|
end.map do |p|
|
79
83
|
::File.file?(p) ? File.new(p, project_dir) : Dir.new(p, project_dir)
|
@@ -93,7 +97,7 @@ module Glimmer
|
|
93
97
|
def depth_first_search_file(dir, file_path)
|
94
98
|
dir.children.each do |child|
|
95
99
|
if child.is_a?(File)
|
96
|
-
return child if child.path.include?(file_path)
|
100
|
+
return child if child.path.include?(file_path.to_s)
|
97
101
|
else
|
98
102
|
result = depth_first_search_file(child, file_path)
|
99
103
|
return result unless result.nil?
|
@@ -1,599 +1,662 @@
|
|
1
|
-
module Glimmer
|
2
|
-
class Gladiator
|
3
|
-
class File
|
4
|
-
include Glimmer
|
5
|
-
|
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
|
-
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
|
12
|
-
@command_history = []
|
13
|
-
@name = path.empty? ? 'Scratchpad' : ::File.basename(path)
|
14
|
-
self.path = ::File.expand_path(path) unless path.empty?
|
15
|
-
@top_pixel = 0
|
16
|
-
@caret_position = 0
|
17
|
-
@selection_count = 0
|
18
|
-
@last_selection_count = 0
|
19
|
-
@line_number = 1
|
20
|
-
@init = nil
|
21
|
-
end
|
22
|
-
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
end
|
146
|
-
|
147
|
-
def
|
148
|
-
|
149
|
-
end
|
150
|
-
|
151
|
-
def
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
@
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
end
|
228
|
-
|
229
|
-
def
|
230
|
-
|
231
|
-
end
|
232
|
-
|
233
|
-
def
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
end
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
self.
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
old_lines = lines
|
314
|
-
return if old_lines.size < 1
|
315
|
-
old_selection_count = self.selection_count
|
316
|
-
old_caret_position = self.caret_position
|
317
|
-
old_caret_position_line_index = line_index_for_caret_position(old_caret_position)
|
318
|
-
old_caret_position_line_caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
319
|
-
old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
new_lines[the_line_index] = the_line.sub(
|
328
|
-
delta
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
self.
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
self.selection_count
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
end
|
530
|
-
|
531
|
-
def
|
532
|
-
|
533
|
-
end
|
534
|
-
|
535
|
-
def
|
536
|
-
|
537
|
-
end
|
538
|
-
|
539
|
-
def
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
if
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
end
|
585
|
-
|
586
|
-
def
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
end
|
593
|
-
|
594
|
-
def
|
595
|
-
|
596
|
-
end
|
597
|
-
|
598
|
-
|
599
|
-
|
1
|
+
module Glimmer
|
2
|
+
class Gladiator
|
3
|
+
class File
|
4
|
+
include Glimmer
|
5
|
+
|
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
|
+
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
|
12
|
+
@command_history = []
|
13
|
+
@name = path.empty? ? 'Scratchpad' : ::File.basename(path)
|
14
|
+
self.path = ::File.expand_path(path) unless path.empty?
|
15
|
+
@top_pixel = 0
|
16
|
+
@caret_position = 0
|
17
|
+
@selection_count = 0
|
18
|
+
@last_selection_count = 0
|
19
|
+
@line_number = 1
|
20
|
+
@init = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def language
|
24
|
+
# TODO consider using Rouge::Lexer.guess_by_filename instead and perhaps guess_by_source when it fails
|
25
|
+
extension = path.split('.').last if path.to_s.include?('.')
|
26
|
+
return 'ruby' if scratchpad?
|
27
|
+
return 'ruby' if path.to_s.end_with?('Gemfile') || path.to_s.end_with?('Rakefile')
|
28
|
+
return 'ruby' if dirty_content.start_with?('#!/usr/bin/env ruby') || dirty_content.start_with?('#!/usr/bin/env jruby')
|
29
|
+
return 'yaml' if path.to_s.end_with?('Gemfile.lock')
|
30
|
+
return 'shell' if extension.nil? && path.to_s.include?('/bin/')
|
31
|
+
case extension
|
32
|
+
# TODO extract case statement to an external config file
|
33
|
+
when 'rb'
|
34
|
+
'ruby'
|
35
|
+
when 'md', 'markdown'
|
36
|
+
'markdown'
|
37
|
+
when 'js', 'es6'
|
38
|
+
'javascript'
|
39
|
+
when 'json'
|
40
|
+
'json'
|
41
|
+
when 'yaml'
|
42
|
+
'yaml'
|
43
|
+
when 'html'
|
44
|
+
'html'
|
45
|
+
when 'h', 'c'
|
46
|
+
'c'
|
47
|
+
when 'hs'
|
48
|
+
'haskell'
|
49
|
+
when 'gradle'
|
50
|
+
'gradle'
|
51
|
+
when 'cpp'
|
52
|
+
'cpp'
|
53
|
+
when 'css'
|
54
|
+
'css'
|
55
|
+
when 'java'
|
56
|
+
'java'
|
57
|
+
when 'jsp'
|
58
|
+
'jsp'
|
59
|
+
when 'plist'
|
60
|
+
'plist'
|
61
|
+
when 'haml'
|
62
|
+
'haml'
|
63
|
+
when 'xml'
|
64
|
+
'xml'
|
65
|
+
when 'ini'
|
66
|
+
'ini'
|
67
|
+
when 'pl'
|
68
|
+
'perl'
|
69
|
+
when 'tcl'
|
70
|
+
'tcl'
|
71
|
+
when 'sass'
|
72
|
+
'sass'
|
73
|
+
when 'scss'
|
74
|
+
'scss'
|
75
|
+
when 'sql'
|
76
|
+
'sql'
|
77
|
+
when 'sh'
|
78
|
+
'shell'
|
79
|
+
when 'vue'
|
80
|
+
'vue'
|
81
|
+
when 'txt', nil
|
82
|
+
'plain_text'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def init_content
|
87
|
+
unless @init
|
88
|
+
@init = true
|
89
|
+
begin
|
90
|
+
# test read dirty content
|
91
|
+
observe(self, :dirty_content) do
|
92
|
+
line_count = lines.empty? ? 1 : lines.size
|
93
|
+
lines_text_size = [line_count.to_s.size, 4].max
|
94
|
+
old_top_pixel = top_pixel
|
95
|
+
self.line_numbers_content = line_count.times.map {|n| (' ' * (lines_text_size - (n+1).to_s.size)) + (n+1).to_s }.join("\n")
|
96
|
+
self.top_pixel = old_top_pixel
|
97
|
+
end
|
98
|
+
the_dirty_content = read_dirty_content
|
99
|
+
the_dirty_content.split("\n") # test that it is not a binary file (crashes to rescue block otherwise)
|
100
|
+
self.dirty_content = the_dirty_content
|
101
|
+
observe(self, :caret_position) do |new_caret_position|
|
102
|
+
update_line_number_from_caret_position(new_caret_position)
|
103
|
+
end
|
104
|
+
observe(self, :line_number) do |new_line_number|
|
105
|
+
line_index = line_number - 1
|
106
|
+
new_caret_position = caret_position_for_line_index(line_index)
|
107
|
+
current_caret_position = caret_position
|
108
|
+
line_index_for_new_caret_position = line_index_for_caret_position(new_caret_position)
|
109
|
+
line_index_for_current_caret_position = line_index_for_caret_position(current_caret_position)
|
110
|
+
self.caret_position = new_caret_position unless (current_caret_position && line_index_for_new_caret_position == line_index_for_current_caret_position)
|
111
|
+
end
|
112
|
+
rescue # in case of a binary file
|
113
|
+
stop_filewatcher
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def update_line_number_from_caret_position(new_caret_position)
|
119
|
+
new_line_number = line_index_for_caret_position(caret_position) + 1
|
120
|
+
current_line_number = line_number
|
121
|
+
unless (current_line_number && current_line_number == new_line_number)
|
122
|
+
self.line_number = new_line_number
|
123
|
+
# TODO check if the following line is needed
|
124
|
+
self.line_position = caret_position - caret_position_for_line_index(line_number - 1) + 1
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def path=(the_path)
|
129
|
+
@path = the_path
|
130
|
+
generate_display_path
|
131
|
+
end
|
132
|
+
|
133
|
+
def generate_display_path
|
134
|
+
return if @path.empty?
|
135
|
+
@display_path = @path.sub(project_dir.path, '').sub(/^\//, '')
|
136
|
+
end
|
137
|
+
|
138
|
+
def name=(the_name)
|
139
|
+
new_path = path.sub(/#{Regexp.escape(@name)}$/, the_name) unless scratchpad?
|
140
|
+
@name = the_name
|
141
|
+
if !scratchpad? && ::File.exist?(path)
|
142
|
+
FileUtils.mv(path, new_path)
|
143
|
+
self.path = new_path
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def scratchpad?
|
148
|
+
path.to_s.empty?
|
149
|
+
end
|
150
|
+
|
151
|
+
def backup_properties
|
152
|
+
[:find_text, :replace_text, :case_sensitive, :top_pixel, :caret_position, :selection_count].reduce({}) do |hash, property|
|
153
|
+
hash.merge(property => send(property))
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def restore_properties(properties_hash)
|
158
|
+
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?
|
159
|
+
properties_hash.each do |property, value|
|
160
|
+
send("#{property}=", value)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def caret_position=(value)
|
165
|
+
@last_caret_position = @caret_position
|
166
|
+
@caret_position = value
|
167
|
+
end
|
168
|
+
|
169
|
+
def selection_count=(value)
|
170
|
+
#@last_selection_count = @selection_count
|
171
|
+
@selection_count = value
|
172
|
+
@last_selection_count = @selection_count
|
173
|
+
end
|
174
|
+
|
175
|
+
def dirty_content
|
176
|
+
init_content
|
177
|
+
@dirty_content
|
178
|
+
end
|
179
|
+
|
180
|
+
def dirty_content=(the_content)
|
181
|
+
# TODO set partial dirty content by line(s) for enhanced performance
|
182
|
+
@dirty_content = the_content
|
183
|
+
old_caret_position = caret_position
|
184
|
+
old_top_pixel = top_pixel
|
185
|
+
|
186
|
+
notify_observers(:content)
|
187
|
+
if @formatting_dirty_content_for_writing
|
188
|
+
self.caret_position = old_caret_position
|
189
|
+
self.top_pixel = old_top_pixel
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def content
|
194
|
+
dirty_content
|
195
|
+
end
|
196
|
+
|
197
|
+
# to use for widget data-binding
|
198
|
+
def content=(value)
|
199
|
+
value = value.gsub("\t", ' ')
|
200
|
+
if dirty_content != value
|
201
|
+
Command.do(self, :change_content!, value)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def change_content!(value)
|
206
|
+
self.dirty_content = value
|
207
|
+
update_line_number_from_caret_position(caret_position)
|
208
|
+
end
|
209
|
+
|
210
|
+
def start_command
|
211
|
+
@commmand_in_progress = true
|
212
|
+
end
|
213
|
+
|
214
|
+
def end_command
|
215
|
+
@commmand_in_progress = false
|
216
|
+
end
|
217
|
+
|
218
|
+
def command_in_progress?
|
219
|
+
@commmand_in_progress
|
220
|
+
end
|
221
|
+
|
222
|
+
def close
|
223
|
+
stop_filewatcher
|
224
|
+
remove_all_observers
|
225
|
+
initialize(path, project_dir)
|
226
|
+
Command.clear(self)
|
227
|
+
end
|
228
|
+
|
229
|
+
def read_dirty_content
|
230
|
+
path.empty? ? '' : ::File.read(path)
|
231
|
+
end
|
232
|
+
|
233
|
+
def start_filewatcher
|
234
|
+
return if scratchpad?
|
235
|
+
@filewatcher = Filewatcher.new(@path)
|
236
|
+
@thread = Thread.new(@filewatcher) do |fw|
|
237
|
+
fw.watch do |filename, event|
|
238
|
+
async_exec do
|
239
|
+
begin
|
240
|
+
self.dirty_content = read_dirty_content if read_dirty_content != dirty_content
|
241
|
+
rescue StandardError, Errno::ENOENT
|
242
|
+
# in case of a binary file
|
243
|
+
stop_filewatcher
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def stop_filewatcher
|
251
|
+
@filewatcher&.stop
|
252
|
+
end
|
253
|
+
|
254
|
+
def write_dirty_content
|
255
|
+
# TODO write partial dirty content by line(s) for enhanced performance
|
256
|
+
return if scratchpad? || !::File.exist?(path) || !::File.exists?(path) || read_dirty_content == dirty_content
|
257
|
+
format_dirty_content_for_writing!
|
258
|
+
::File.write(path, dirty_content)
|
259
|
+
rescue StandardError, ArgumentError => e
|
260
|
+
puts "Error in writing dirty content for #{path}"
|
261
|
+
puts e.full_message
|
262
|
+
end
|
263
|
+
|
264
|
+
def format_dirty_content_for_writing!
|
265
|
+
return if @commmand_in_progress
|
266
|
+
# TODO f ix c ar e t pos it ion after formatting dirty content (diff?)
|
267
|
+
new_dirty_content = dirty_content.to_s.split("\n").map {|line| line.strip.empty? ? line : line.rstrip }.join("\n")
|
268
|
+
new_dirty_content = "#{new_dirty_content.gsub("\r\n", "\n").gsub("\r", "\n").sub(/\n+\z/, '')}\n"
|
269
|
+
if new_dirty_content != self.dirty_content
|
270
|
+
@formatting_dirty_content_for_writing = true
|
271
|
+
self.dirty_content = new_dirty_content
|
272
|
+
@formatting_dirty_content_for_writing = false
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def write_raw_dirty_content
|
277
|
+
return if scratchpad? || !::File.exist?(path)
|
278
|
+
::File.write(path, dirty_content) if ::File.exists?(path)
|
279
|
+
rescue => e
|
280
|
+
puts "Error in writing raw dirty content for #{path}"
|
281
|
+
puts e.full_message
|
282
|
+
end
|
283
|
+
|
284
|
+
def current_line_indentation
|
285
|
+
current_line.to_s.match(/^(\s+)/).to_a[1].to_s
|
286
|
+
end
|
287
|
+
|
288
|
+
def current_line
|
289
|
+
lines[line_number - 1]
|
290
|
+
end
|
291
|
+
|
292
|
+
def delete!
|
293
|
+
FileUtils.rm(path) unless scratchpad?
|
294
|
+
end
|
295
|
+
|
296
|
+
def prefix_new_line!
|
297
|
+
the_lines = lines
|
298
|
+
the_lines[line_number-1...line_number-1] = [current_line_indentation]
|
299
|
+
self.dirty_content = the_lines.join("\n")
|
300
|
+
self.caret_position = caret_position_for_line_index(line_number-1) + current_line_indentation.size
|
301
|
+
self.selection_count = 0
|
302
|
+
end
|
303
|
+
|
304
|
+
def insert_new_line!
|
305
|
+
the_lines = lines
|
306
|
+
the_lines[line_number...line_number] = [current_line_indentation]
|
307
|
+
self.dirty_content = the_lines.join("\n")
|
308
|
+
self.caret_position = caret_position_for_line_index(line_number) + current_line_indentation.size
|
309
|
+
self.selection_count = 0
|
310
|
+
end
|
311
|
+
|
312
|
+
def comment_line!
|
313
|
+
old_lines = lines
|
314
|
+
return if old_lines.size < 1
|
315
|
+
old_selection_count = self.selection_count
|
316
|
+
old_caret_position = self.caret_position
|
317
|
+
old_caret_position_line_index = line_index_for_caret_position(old_caret_position)
|
318
|
+
old_caret_position_line_caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
319
|
+
old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
|
320
|
+
new_lines = lines
|
321
|
+
delta = 0
|
322
|
+
line_indices_for_selection(caret_position, selection_count).reverse.each do | the_line_index |
|
323
|
+
delta = 0
|
324
|
+
the_line = old_lines[the_line_index]
|
325
|
+
return if the_line.nil?
|
326
|
+
if the_line.strip.start_with?('# ')
|
327
|
+
new_lines[the_line_index] = the_line.sub(/# /, '')
|
328
|
+
delta -= 2
|
329
|
+
elsif the_line.strip.start_with?('#')
|
330
|
+
new_lines[the_line_index] = the_line.sub(/#/, '')
|
331
|
+
delta -= 1
|
332
|
+
else
|
333
|
+
new_lines[the_line_index] = "# #{the_line}"
|
334
|
+
delta += 2
|
335
|
+
end
|
336
|
+
end
|
337
|
+
self.dirty_content = new_lines.join("\n")
|
338
|
+
if old_selection_count.to_i > 0
|
339
|
+
self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
340
|
+
self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
|
341
|
+
else
|
342
|
+
new_caret_position = old_caret_position + delta
|
343
|
+
new_caret_position = [new_caret_position, old_caret_position_line_caret_position].max
|
344
|
+
self.caret_position = new_caret_position
|
345
|
+
self.selection_count = 0
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def indent!
|
350
|
+
new_lines = lines
|
351
|
+
old_lines = lines
|
352
|
+
return if old_lines.size < 1
|
353
|
+
old_selection_count = self.selection_count
|
354
|
+
old_caret_position = self.caret_position
|
355
|
+
old_caret_position_line_index = line_index_for_caret_position(old_caret_position)
|
356
|
+
old_caret_position_line_caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
357
|
+
old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
|
358
|
+
delta = 2
|
359
|
+
line_indices_for_selection(caret_position, selection_count).each do |the_line_index|
|
360
|
+
the_line = old_lines[the_line_index]
|
361
|
+
new_lines[the_line_index] = " #{the_line}"
|
362
|
+
end
|
363
|
+
old_caret_position = self.caret_position
|
364
|
+
self.dirty_content = new_lines.join("\n")
|
365
|
+
if old_selection_count.to_i > 0
|
366
|
+
self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
367
|
+
self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
|
368
|
+
else
|
369
|
+
self.caret_position = old_caret_position + delta
|
370
|
+
self.selection_count = 0
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
def outdent!
|
375
|
+
new_lines = lines
|
376
|
+
old_lines = lines
|
377
|
+
return if old_lines.size < 1
|
378
|
+
old_selection_count = self.selection_count
|
379
|
+
old_caret_position = self.caret_position
|
380
|
+
old_caret_position_line_index = line_index_for_caret_position(old_caret_position)
|
381
|
+
old_caret_position_line_caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
382
|
+
old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
|
383
|
+
delta = 0
|
384
|
+
line_indices_for_selection(caret_position, selection_count).each do |the_line_index|
|
385
|
+
the_line = old_lines[the_line_index]
|
386
|
+
if the_line.to_s.start_with?(' ')
|
387
|
+
new_lines[the_line_index] = the_line.sub(/ /, '')
|
388
|
+
delta = -2
|
389
|
+
elsif the_line&.start_with?(' ')
|
390
|
+
new_lines[the_line_index] = the_line.sub(/ /, '')
|
391
|
+
delta = -1
|
392
|
+
end
|
393
|
+
end
|
394
|
+
self.dirty_content = new_lines.join("\n")
|
395
|
+
if old_selection_count.to_i > 0
|
396
|
+
self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
397
|
+
self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
|
398
|
+
else
|
399
|
+
new_caret_position = old_caret_position + delta
|
400
|
+
new_caret_position = [new_caret_position, old_caret_position_line_caret_position].max
|
401
|
+
self.caret_position = new_caret_position
|
402
|
+
self.selection_count = 0
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def kill_line!
|
407
|
+
new_lines = lines
|
408
|
+
return if new_lines.size < 1
|
409
|
+
line_indices = line_indices_for_selection(caret_position, selection_count)
|
410
|
+
new_lines = new_lines[0...line_indices.first] + new_lines[(line_indices.last+1)...new_lines.size]
|
411
|
+
old_caret_position = self.caret_position
|
412
|
+
old_line_index = self.line_number - 1
|
413
|
+
line_position = line_position_for_caret_position(old_caret_position)
|
414
|
+
self.dirty_content = "#{new_lines.join("\n")}\n"
|
415
|
+
self.caret_position = caret_position_for_line_index(old_line_index) + [line_position, lines[old_line_index].to_s.size].min
|
416
|
+
self.selection_count = 0
|
417
|
+
end
|
418
|
+
|
419
|
+
def duplicate_line!
|
420
|
+
new_lines = lines
|
421
|
+
old_lines = lines
|
422
|
+
return if old_lines.size < 1
|
423
|
+
old_selection_count = self.selection_count
|
424
|
+
old_caret_position = self.caret_position
|
425
|
+
old_caret_position_line_index = line_index_for_caret_position(old_caret_position)
|
426
|
+
old_caret_position_line_caret_position = caret_position_for_caret_position_start_of_line(old_caret_position_line_index)
|
427
|
+
old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
|
428
|
+
the_line_indices = line_indices_for_selection(caret_position, selection_count)
|
429
|
+
the_lines = lines_for_selection(caret_position, selection_count)
|
430
|
+
delta = the_lines.join("\n").size + 1
|
431
|
+
the_lines.each_with_index do |the_line, i|
|
432
|
+
new_lines.insert(the_line_indices.first + i, the_line)
|
433
|
+
end
|
434
|
+
self.dirty_content = new_lines.join("\n")
|
435
|
+
if old_selection_count.to_i > 0
|
436
|
+
self.caret_position = caret_position_for_line_index(old_caret_position_line_index)
|
437
|
+
self.selection_count = (caret_position_for_line_index(old_end_caret_line_index + 1) - self.caret_position)
|
438
|
+
else
|
439
|
+
self.caret_position = old_caret_position + delta
|
440
|
+
self.selection_count = 0
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
def find_next
|
445
|
+
return if find_text.to_s.empty?
|
446
|
+
all_lines = lines
|
447
|
+
the_line_index = line_index_for_caret_position(caret_position)
|
448
|
+
line_position = line_position_for_caret_position(caret_position)
|
449
|
+
found = found_text?(caret_position)
|
450
|
+
2.times do |i|
|
451
|
+
rotation = the_line_index
|
452
|
+
all_lines.rotate(rotation).each_with_index do |the_line, the_index|
|
453
|
+
the_index = (the_index + rotation)%all_lines.size
|
454
|
+
start_position = 0
|
455
|
+
start_position = line_position + find_text.to_s.size if i == 0 && the_index == the_line_index && found_text?(caret_position)
|
456
|
+
text_to_find_in = the_line[start_position..-1]
|
457
|
+
occurrence_index = case_sensitive ? text_to_find_in&.index(find_text.to_s) : text_to_find_in&.downcase&.index(find_text.to_s.downcase)
|
458
|
+
if occurrence_index
|
459
|
+
self.caret_position = caret_position_for_line_index(the_index) + start_position + occurrence_index
|
460
|
+
self.selection_count = find_text.to_s.size
|
461
|
+
return
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
def find_previous
|
468
|
+
return if find_text.to_s.empty?
|
469
|
+
all_lines = lines
|
470
|
+
the_line_index = line_index_for_caret_position(caret_position)
|
471
|
+
line_position = line_position_for_caret_position(caret_position)
|
472
|
+
2.times do |i|
|
473
|
+
rotation = - the_line_index - 1 + all_lines.size
|
474
|
+
all_lines.reverse.rotate(rotation).each_with_index do |the_line, the_index|
|
475
|
+
the_index = all_lines.size - 1 - (the_index + rotation)%all_lines.size
|
476
|
+
if the_index == the_line_index
|
477
|
+
start_position = i > 0 ? 0 : (the_line.size - line_position)
|
478
|
+
else
|
479
|
+
start_position = 0
|
480
|
+
end
|
481
|
+
text_to_find_in = the_line.downcase.reverse[start_position...the_line.size].to_s
|
482
|
+
occurrence_index = text_to_find_in.index(find_text.to_s.downcase.reverse)
|
483
|
+
if occurrence_index
|
484
|
+
self.caret_position = caret_position_for_line_index(the_index) + (the_line.size - (start_position + occurrence_index + find_text.to_s.size))
|
485
|
+
self.selection_count = find_text.to_s.size
|
486
|
+
return
|
487
|
+
end
|
488
|
+
end
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
def ensure_find_next
|
493
|
+
return if find_text.to_s.empty? || dirty_content.to_s.strip.size < 1
|
494
|
+
find_next unless found_text?(self.caret_position)
|
495
|
+
end
|
496
|
+
|
497
|
+
def found_text?(caret_position)
|
498
|
+
dirty_content[caret_position.to_i, find_text.to_s.size].to_s.downcase == find_text.to_s.downcase
|
499
|
+
end
|
500
|
+
|
501
|
+
def replace_next!
|
502
|
+
return if find_text.to_s.empty? || dirty_content.to_s.strip.size < 1
|
503
|
+
ensure_find_next
|
504
|
+
new_dirty_content = dirty_content
|
505
|
+
new_dirty_content[caret_position, find_text.size] = replace_text.to_s
|
506
|
+
self.dirty_content = new_dirty_content
|
507
|
+
find_next
|
508
|
+
find_next if replace_text.to_s.include?(find_text) && !replace_text.to_s.start_with?(find_text)
|
509
|
+
end
|
510
|
+
|
511
|
+
def page_up
|
512
|
+
self.selection_count = 0
|
513
|
+
self.line_number = [(self.line_number - 15), 1].max
|
514
|
+
end
|
515
|
+
|
516
|
+
def page_down
|
517
|
+
self.selection_count = 0
|
518
|
+
self.line_number = [(self.line_number + 15), lines.size].min
|
519
|
+
end
|
520
|
+
|
521
|
+
def home
|
522
|
+
self.selection_count = 0
|
523
|
+
self.line_number = 1
|
524
|
+
end
|
525
|
+
|
526
|
+
def end
|
527
|
+
self.selection_count = 0
|
528
|
+
self.line_number = lines.size
|
529
|
+
end
|
530
|
+
|
531
|
+
def start_of_line
|
532
|
+
self.caret_position = caret_position_for_line_index(self.line_number - 1)
|
533
|
+
end
|
534
|
+
|
535
|
+
def end_of_line
|
536
|
+
self.caret_position = caret_position_for_line_index(self.line_number) - 1
|
537
|
+
end
|
538
|
+
|
539
|
+
def move_up!
|
540
|
+
old_lines = lines
|
541
|
+
return if old_lines.size < 2
|
542
|
+
old_selection_count = self.selection_count
|
543
|
+
old_caret_position = self.caret_position
|
544
|
+
old_caret_position_line_caret_position = caret_position_for_caret_position_start_of_line(old_caret_position)
|
545
|
+
old_caret_position_line_position = old_caret_position - old_caret_position_line_caret_position
|
546
|
+
old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
|
547
|
+
new_lines = lines
|
548
|
+
the_line_indices = line_indices_for_selection(caret_position, selection_count)
|
549
|
+
the_lines = lines_for_selection(caret_position, selection_count)
|
550
|
+
new_line_index = [the_line_indices.first - 1, 0].max
|
551
|
+
new_lines[the_line_indices.first..the_line_indices.last] = []
|
552
|
+
new_lines[new_line_index...new_line_index] = the_lines
|
553
|
+
self.dirty_content = new_lines.join("\n")
|
554
|
+
self.caret_position = caret_position_for_line_index(new_line_index) + [old_caret_position_line_position, new_lines[new_line_index].size].min
|
555
|
+
self.selection_count = old_selection_count.to_i if old_selection_count.to_i > 0
|
556
|
+
end
|
557
|
+
|
558
|
+
def move_down!
|
559
|
+
old_lines = lines
|
560
|
+
return if old_lines.size < 2
|
561
|
+
old_selection_count = self.selection_count
|
562
|
+
old_caret_position = self.caret_position
|
563
|
+
old_caret_position_line_caret_position = caret_position_for_caret_position_start_of_line(old_caret_position)
|
564
|
+
old_caret_position_line_position = old_caret_position - old_caret_position_line_caret_position
|
565
|
+
old_end_caret_line_index = end_caret_position_line_index(caret_position, selection_count)
|
566
|
+
new_lines = lines
|
567
|
+
the_line_indices = line_indices_for_selection(caret_position, selection_count)
|
568
|
+
the_lines = lines_for_selection(caret_position, selection_count)
|
569
|
+
new_line_index = [the_line_indices.first + 1, new_lines.size - 1].min
|
570
|
+
new_lines[the_line_indices.first..the_line_indices.last] = []
|
571
|
+
new_lines[new_line_index...new_line_index] = the_lines
|
572
|
+
self.dirty_content = new_lines.join("\n")
|
573
|
+
self.caret_position = caret_position_for_line_index(new_line_index) + [old_caret_position_line_position, new_lines[new_line_index].size].min
|
574
|
+
self.selection_count = old_selection_count.to_i if old_selection_count.to_i > 0
|
575
|
+
end
|
576
|
+
|
577
|
+
def run
|
578
|
+
if scratchpad?
|
579
|
+
eval content
|
580
|
+
else
|
581
|
+
write_dirty_content
|
582
|
+
load path
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
def lines
|
587
|
+
need_padding = dirty_content.to_s.end_with?("\n")
|
588
|
+
splittable_content = need_padding ? "#{dirty_content} " : dirty_content
|
589
|
+
the_lines = splittable_content.split("\n")
|
590
|
+
the_lines[-1] = the_lines[-1].strip if need_padding
|
591
|
+
the_lines
|
592
|
+
end
|
593
|
+
|
594
|
+
def line_for_caret_position(caret_position)
|
595
|
+
lines[line_index_for_caret_position(caret_position.to_i)]
|
596
|
+
end
|
597
|
+
|
598
|
+
def line_index_for_caret_position(caret_position)
|
599
|
+
dirty_content[0...caret_position.to_i].count("\n")
|
600
|
+
end
|
601
|
+
|
602
|
+
def caret_position_for_line_index(line_index)
|
603
|
+
cp = lines[0...line_index].join("\n").size
|
604
|
+
cp += 1 if line_index > 0
|
605
|
+
cp
|
606
|
+
end
|
607
|
+
|
608
|
+
def caret_position_for_caret_position_start_of_line(caret_position)
|
609
|
+
caret_position_for_line_index(line_index_for_caret_position(caret_position))
|
610
|
+
end
|
611
|
+
|
612
|
+
# position within line containing "caret position" (e.g. for caret position 5 in 1st line, they match as 5, for 15 in line 2 with line 1 having 10 characters, line position is 4)
|
613
|
+
# TODO consider renaming to line_character_position_for_caret_position
|
614
|
+
def line_position_for_caret_position(caret_position)
|
615
|
+
caret_position = caret_position.to_i
|
616
|
+
caret_position - caret_position_for_caret_position_start_of_line(caret_position)
|
617
|
+
end
|
618
|
+
|
619
|
+
def line_caret_positions_for_selection(caret_position, selection_count)
|
620
|
+
line_indices = line_indices_for_selection(caret_position, selection_count)
|
621
|
+
line_caret_positions = line_indices.map { |line_index| caret_position_for_line_index(line_index) }.to_a
|
622
|
+
end
|
623
|
+
|
624
|
+
def end_caret_position_line_index(caret_position, selection_count)
|
625
|
+
end_caret_position = caret_position + selection_count.to_i
|
626
|
+
end_caret_position -= 1 if dirty_content[end_caret_position - 1] == "\n"
|
627
|
+
end_line_index = line_index_for_caret_position(end_caret_position)
|
628
|
+
end
|
629
|
+
|
630
|
+
def lines_for_selection(caret_position, selection_count)
|
631
|
+
line_indices = line_indices_for_selection(caret_position, selection_count)
|
632
|
+
lines[line_indices.first..line_indices.last]
|
633
|
+
end
|
634
|
+
|
635
|
+
def line_indices_for_selection(caret_position, selection_count)
|
636
|
+
start_line_index = line_index_for_caret_position(caret_position)
|
637
|
+
if selection_count.to_i > 0
|
638
|
+
end_line_index = end_caret_position_line_index(caret_position, selection_count)
|
639
|
+
else
|
640
|
+
end_line_index = start_line_index
|
641
|
+
end
|
642
|
+
(start_line_index..end_line_index).to_a
|
643
|
+
end
|
644
|
+
|
645
|
+
def children
|
646
|
+
[]
|
647
|
+
end
|
648
|
+
|
649
|
+
def to_s
|
650
|
+
path
|
651
|
+
end
|
652
|
+
|
653
|
+
def eql?(other)
|
654
|
+
self.path.eql?(other&.path)
|
655
|
+
end
|
656
|
+
|
657
|
+
def hash
|
658
|
+
self.path.hash
|
659
|
+
end
|
660
|
+
end
|
661
|
+
end
|
662
|
+
end
|