glimmer-cs-gladiator 0.7.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|