forked from danlucraft/redcar
/
redcar.rb
607 lines (527 loc) · 17.3 KB
/
redcar.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
146
147
148
149
150
151
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
228
229
230
231
232
233
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
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
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
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
530
531
532
533
534
535
536
537
538
539
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
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
module Redcar
module Top
class NewCommand < Command
key :osx => "Cmd+N",
:linux => "Ctrl+N",
:windows => "Ctrl+N"
def execute
tab = win.new_tab(Redcar::EditTab)
tab.title = "untitled"
tab.focus
tab
end
end
class NewNotebookCommand < Command
sensitize :single_notebook
key :osx => "Cmd+Alt+N",
:linux => "Ctrl+Alt+N",
:windows => "Ctrl+Alt+N"
def execute
unless win.notebooks.length > 1
win.create_notebook
end
end
end
class NewWindowCommand < Command
def initialize(title=nil)
@title = title
end
def execute
window = Redcar.app.new_window
window.title = @title if @title
end
end
class CloseWindowCommand < Command
def initialize(window=nil)
@window = window
end
def execute
(@window||win).close
end
end
class RotateNotebooksCommand < Command
sensitize :multiple_notebooks
def execute
win.rotate_notebooks
end
end
class CloseNotebookCommand < Command
sensitize :multiple_notebooks
def execute
unless win.notebooks.length == 1
win.close_notebook
end
end
end
class SwitchNotebookCommand < Command
sensitize :multiple_notebooks, :other_notebook_has_tab
key :osx => "Cmd+Alt+O",
:linux => "Ctrl+Alt+O",
:windows => "Ctrl+Alt+O"
def execute
new_notebook = win.nonfocussed_notebook
if new_notebook.focussed_tab
new_notebook.focussed_tab.focus
end
end
end
class MoveTabToOtherNotebookCommand < Command
sensitize :multiple_notebooks, :open_tab
key :osx => "Cmd+Shift+Alt+O",
:linux => "Ctrl+Shift+Alt+O",
:windows => "Ctrl+Shift+Alt+O"
def execute
current_notebook = tab.notebook
target_notebook = win.notebooks.detect {|nb| nb != current_notebook}
target_notebook.grab_tab_from(current_notebook, tab)
tab.focus
end
end
class PrintContents < EditTabCommand
key "Cmd+P"
def execute
puts "printing contents"
p tab.edit_view.document.to_s
end
end
class PrintHistoryCommand < Command
def execute
Redcar.history.each do |c|
puts c
end
end
end
class PrintScopeTreeCommand < Command
def execute
puts tab.edit_view.controller.mate_text.parser.root.pretty(0)
end
end
class CloseTabCommand < TabCommand
key :osx => "Cmd+W",
:linux => "Ctrl+W",
:windows => "Ctrl+W"
def execute
# TODO: should be win.focussed_notebook
if tab = win.focussed_notebook.focussed_tab
tab.close
end
end
end
class ListTabsCommand < Command
def execute
p win.focussed_notebook.tabs.map {|tab| tab.class}
end
end
class SwitchTabDownCommand < Command
key :osx => "Cmd+Shift+[",
:linux => "Ctrl+Shift+[",
:windows => "Ctrl+Shift+["
def execute
win.focussed_notebook.switch_down
end
end
class SwitchTabUpCommand < Command
key :osx => "Cmd+Shift+]",
:linux => "Ctrl+Shift+]",
:windows => "Ctrl+Shift+]"
def execute
win.focussed_notebook.switch_up
end
end
class UndoCommand < EditTabCommand
sensitize :undoable
key :osx => "Cmd+Z",
:linux => "Ctrl+Z",
:windows => "Ctrl+Z"
def execute
tab.edit_view.undo
end
end
class RedoCommand < EditTabCommand
sensitize :redoable
key :osx => "Cmd+Shift+Z",
:linux => "Ctrl+Shift+Z",
:windows => "Ctrl+Shift+Z"
def execute
tab.edit_view.redo
end
end
class MoveHomeCommand < EditTabCommand
key "Ctrl+A"
def execute
doc = tab.edit_view.document
line_ix = doc.line_at_offset(doc.cursor_offset)
doc.cursor_offset = doc.offset_at_line(line_ix)
end
end
class MoveEndCommand < EditTabCommand
key "Ctrl+E"
def execute
doc = tab.edit_view.document
line_ix = doc.line_at_offset(doc.cursor_offset)
if line_ix == doc.line_count - 1
doc.cursor_offset = doc.length
else
doc.cursor_offset = doc.offset_at_line(line_ix + 1) - 1
end
end
end
class ChangeIndentCommand < EditTabCommand
def execute
doc = tab.edit_view.document
if doc.selection?
first_line_ix = doc.line_at_offset(doc.selection_range.begin)
last_line_ix = doc.line_at_offset(doc.selection_range.end)
if doc.selection_range.end == doc.offset_at_line(last_line_ix)
last_line_ix -= 1
end
first_line_ix.upto(last_line_ix) do |line_ix|
indent_line(doc, line_ix)
end
start_selection = doc.offset_at_line(first_line_ix)
if last_line_ix == doc.line_count - 1
end_selection = doc.length
else
end_selection = doc.offset_at_line(last_line_ix + 1)
end
doc.set_selection_range(start_selection..end_selection)
else
indent_line(doc, doc.cursor_line)
end
end
end
class DecreaseIndentCommand < ChangeIndentCommand
key :osx => "Cmd+[",
:linux => "Ctrl+[",
:windows => "Ctrl+["
def indent_line(doc, line_ix)
use_spaces = true
num_spaces = 2
line = doc.get_line(line_ix)
if line[0..0] == "\t"
line_start = doc.offset_at_line(line_ix)
to = line_start + 1
elsif line[0...num_spaces] == " "*num_spaces
line_start = doc.offset_at_line(line_ix)
to = line_start + num_spaces
end
doc.delete(line_start, to - line_start) unless line_start == to
end
end
class IncreaseIndentCommand < ChangeIndentCommand
key :osx => "Cmd+]",
:linux => "Ctrl+]",
:windows => "Ctrl+]"
def indent_line(doc, line_ix)
line = doc.get_line(line_ix)
whitespace_type = line[/^( |\t)/, 1] || " "
doc.insert(doc.offset_at_line(line_ix), whitespace_type)
end
end
class StripWhitespaceCommand < Redcar::EditTabCommand
def execute
doc.text = doc.to_s.gsub(/\s+$/, "\n")
end
end
class SelectAllCommand < Redcar::EditTabCommand
key :osx => "Cmd+A",
:linux => "Ctrl+Shift+A",
:windows => "Ctrl+Shift+A"
def execute
doc.set_selection_range(0..(doc.length))
end
end
class SelectLineCommand < Redcar::EditTabCommand
key :osx => "Cmd+Shift+L",
:linux => "Ctrl+Shift+L",
:windows => "Ctrl+Shift+L"
def execute
doc.set_selection_range(
doc.cursor_line_start_offset..doc.cursor_line_end_offset)
end
end
class CutCommand < Redcar::EditTabCommand
key :osx => "Cmd+X",
:linux => "Ctrl+X",
:windows => "Ctrl+X"
def execute
if doc.selection?
text = doc.selection_ranges.map do |range|
doc.get_range(range.begin, range.count)
end
p text
Redcar.app.clipboard << text
diff = 0
doc.selection_ranges.each do |range|
doc.delete(range.begin - diff, range.count)
diff += range.count
end
else
Redcar.app.clipboard << doc.get_line(doc.cursor_line)
doc.delete(doc.cursor_line_start_offset,
doc.cursor_line_end_offset - doc.cursor_line_start_offset)
end
end
end
class CopyCommand < Redcar::EditTabCommand
key :osx => "Cmd+C",
:linux => "Ctrl+C",
:windows => "Ctrl+C"
def execute
if doc.selection?
text = doc.selection_ranges.map do |range|
doc.get_range(range.begin, range.count)
end
Redcar.app.clipboard << text
else
Redcar.app.clipboard << doc.get_line(doc.cursor_line)
end
end
end
class PasteCommand < Redcar::EditTabCommand
sensitize :clipboard_not_empty
key :osx => "Cmd+V",
:linux => "Ctrl+V",
:windows => "Ctrl+V"
def execute
start_offset = doc.selection_ranges.first.begin
start_line = doc.line_at_offset(start_offset)
line_offset = start_offset - doc.offset_at_line(start_line)
cursor_line = doc.cursor_line
cursor_line_offset = doc.cursor_line_offset
diff = 0
doc.selection_ranges.each do |range|
doc.delete(range.begin - diff, range.count)
diff += range.count
end
texts = Redcar.app.clipboard.last.dup
texts.each_with_index do |text, i|
line_ix = start_line + i
if line_ix == doc.line_count
doc.insert(doc.length, "\n" + " "*line_offset)
else
line = doc.get_line(line_ix)
if line.length - 1 < line_offset
doc.insert(
doc.offset_at_inner_end_of_line(line_ix),
" "*(line_offset - line.length + 1)
)
end
end
doc.insert(
doc.offset_at_line(line_ix) + line_offset,
text
)
end
new_offset = doc.offset_at_line(cursor_line) + cursor_line_offset + Redcar.app.clipboard.last.first.length
doc.cursor_offset = new_offset
end
end
class DialogExample < Redcar::Command
def execute
builder = Menu::Builder.new do
item("Foo") { p :foo }
item("Bar") { p :bar }
separator
sub_menu "Baz" do
item("Qux") { p :qx}
item("Quux") { p :quux }
item("Corge") { p :corge }
end
end
win.popup_menu(builder.menu)
end
end
class GotoLineCommand < Redcar::EditTabCommand
key :osx => "Cmd+L",
:linux => "Ctrl+L",
:windows => "Ctrl+L"
class Speedbar < Redcar::Speedbar
label "Goto line:"
textbox :line
button :go, "Return" do
new_line_ix = @speedbar.line.to_i - 1
if new_line_ix < doc.line_count and new_line_ix >= 0
doc.cursor_offset = doc.offset_at_line(new_line_ix)
doc.scroll_to_line(new_line_ix)
win.close_speedbar
end
end
end
def execute
@speedbar = GotoLineCommand::Speedbar.new(self)
win.open_speedbar(@speedbar)
end
end
class SearchForwardCommand < Redcar::EditTabCommand
key :osx => "Cmd+F", :linux => "Ctrl+F", :windows => "Ctrl+F"
class Speedbar < Redcar::Speedbar
class << self
attr_accessor :previous_query
end
def initialize(controller)
super(controller)
self.query = Speedbar.previous_query || ""
end
label "Regex"
textbox :query
button :search, "Return" do
current_query = @speedbar.query
SearchForwardCommand::Speedbar.previous_query = current_query
FindNextRegex.new(Regexp.new(current_query), true).run
end
end
def execute
@speedbar = SearchForwardCommand::Speedbar.new(self)
win.open_speedbar(@speedbar)
end
end
class RepeatPreviousSearchForwardCommand < Redcar::EditTabCommand
key :osx => "Cmd+G", :linux => "Ctrl+G", :windows => "Ctrl+G" # TODO F3 on doze
def execute
open_bar = SearchForwardCommand.new
# open_bar.execute # Question: is there a way to programmatically
# pull up a bar (and execute it)? (the above line doesn't work)
# open_bar.speedbar.find
FindNextRegex.new(Regexp.new(SearchForwardCommand::Speedbar.previous_query), true).run
end
end
class FindNextRegex < Redcar::EditTabCommand
def initialize(re, wrap=nil)
@re = re
@wrap = wrap
end
def to_s
"<#{self.class}: @re:#{@re.inspect} wrap:#{!!@wrap}>"
end
def execute
# first search the remainder of the current line
curr_line = doc.get_line(doc.cursor_line)
cursor_line_offset = doc.cursor_offset - doc.offset_at_line(doc.cursor_line)
curr_line = curr_line[cursor_line_offset..-1]
if curr_line =~ @re
line_start = doc.offset_at_line(doc.cursor_line)
startoff = line_start + $`.length + cursor_line_offset
endoff = startoff + $&.length
doc.set_selection_range(startoff..endoff)
elsif doc.cursor_line < doc.line_count - 1
# next search the rest of the lines
line_num = doc.cursor_line + 1
curr_line = doc.get_line(line_num)
until line_num == (doc.line_count - 1) or
found = (curr_line.to_s =~ @re)
line_num += 1
curr_line = doc.get_line(line_num)
end
if found
line_start = doc.offset_at_line(line_num)
startoff = line_start + $`.length
endoff = startoff + $&.length
doc.set_selection_range(startoff..endoff)
doc.scroll_to_line(line_num)
end
if line_num == (doc.line_count - 1) and @wrap
@wrap = false
doc.cursor_offset = 0
execute
end
end
end
end
class ToggleBlockSelectionCommand < Redcar::EditTabCommand
key :osx => "Cmd+B",
:windows => "Ctrl+B",
:linux => "Ctrl+B"
def execute
doc.block_selection_mode = !doc.block_selection_mode?
end
end
def self.menus
Menu::Builder.build do
sub_menu "File" do
item "New", NewCommand
item "New Notebook", NewNotebookCommand
item "New Window", NewWindowCommand
item "Open", Project::FileOpenCommand
item "Open Directory", Project::DirectoryOpenCommand
separator
item "Save", Project::FileSaveCommand
item "Save As", Project::FileSaveAsCommand
separator
item "Close Tab", CloseTabCommand
item "Close Notebook", CloseNotebookCommand
item "Close Window", CloseWindowCommand
item "Close Directory", Project::DirectoryCloseCommand
end
sub_menu "Edit" do
item "Undo", UndoCommand
item "Redo", RedoCommand
separator
item "Cut", CutCommand
item "Copy", CopyCommand
item "Paste", PasteCommand
separator
item "Home", MoveHomeCommand
item "End", MoveEndCommand
separator
item "Increase Indent", IncreaseIndentCommand
item "Decrease Indent", DecreaseIndentCommand
separator
item "Strip Whitespace", StripWhitespaceCommand
separator
item "Goto Line", GotoLineCommand
item "Regex Search", SearchForwardCommand
item "Repeat Last Search", RepeatPreviousSearchForwardCommand
separator
sub_menu "Select" do
item "All", SelectAllCommand
item "Line", SelectLineCommand
end
item "Toggle Block Selection", ToggleBlockSelectionCommand
item "Auto Complete", AutoCompleter::AutoCompleteCommand
item "Menu Auto Complete", AutoCompleter::MenuAutoCompleterCommand
end
sub_menu "Project" do
item "Find File", Project::FindFileCommand
end
sub_menu "Debug" do
item "Print Command History", PrintHistoryCommand
item "Print Contents", PrintContents
item "List Tabs", ListTabsCommand
item "Print Scope Tree", PrintScopeTreeCommand
item "Refresh Directory", Project::RefreshDirectoryCommand
item "Dialog Tester", DialogExample
end
sub_menu "View" do
item "Rotate Notebooks", RotateNotebooksCommand
item "Move Tab To Other Notebook", MoveTabToOtherNotebookCommand
item "Switch Notebooks", SwitchNotebookCommand
separator
item "Previous Tab", SwitchTabDownCommand
item "Next Tab", SwitchTabUpCommand
end
sub_menu "Plugins" do
item "Plugin Manager", PluginManagerUi::OpenCommand
item "Reload Again", PluginManagerUi::ReloadLastReloadedCommand
separator
end
sub_menu "Help" do
item "Website", PrintHistoryCommand
end
end
end
def self.start
Application.start
ApplicationSWT.start
AutoIndenter.start
EditViewSWT.start
AutoCompleter.start
Redcar.gui = ApplicationSWT.gui
Redcar.app.controller = ApplicationSWT.new(Redcar.app)
Redcar.app.load_menus
Redcar.app.load_sensitivities
Redcar.app.new_window
Redcar::Project.start
end
end
end