-
Notifications
You must be signed in to change notification settings - Fork 62
Expand file tree
/
Copy patheditor.wx.lua
More file actions
2611 lines (2268 loc) · 98.2 KB
/
editor.wx.lua
File metadata and controls
2611 lines (2268 loc) · 98.2 KB
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
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
-------------------------------------------------------------------------=---
-- Name: Editor.wx.lua
-- Purpose: wxLua IDE
-- Author: J Winwood
-- Created: March 2002
-- Copyright: (c) 2002-5 Lomtick Software. All rights reserved.
-- Licence: wxWidgets licence
-------------------------------------------------------------------------=---
-- Load the wxLua module, does nothing if running from wxLua, wxLuaFreeze, or wxLuaEdit
package.cpath = package.cpath..";./?.dll;./?.so;../lib/?.so;../lib/vc_dll/?.dll;../lib/bcc_dll/?.dll;../lib/mingw_dll/?.dll;"
require("wx")
-- Equivalent to C's "cond ? a : b", all terms will be evaluated
function iff(cond, a, b) if cond then return a else return b end end
-- Does the num have all the bits in value
function HasBit(value, num)
for n = 32, 0, -1 do
local b = 2^n
local num_b = num - b
local value_b = value - b
if num_b >= 0 then
num = num_b
else
return true -- already tested bits in num
end
if value_b >= 0 then
value = value_b
end
if (num_b >= 0) and (value_b < 0) then
return false
end
end
return true
end
-- Generate a unique new wxWindowID
local ID_IDCOUNTER = wx.wxID_HIGHEST + 1
function NewID()
ID_IDCOUNTER = ID_IDCOUNTER + 1
return ID_IDCOUNTER
end
-- File menu
local ID_NEW = wx.wxID_NEW
local ID_OPEN = wx.wxID_OPEN
local ID_CLOSE = NewID()
local ID_SAVE = wx.wxID_SAVE
local ID_SAVEAS = wx.wxID_SAVEAS
local ID_SAVEALL = NewID()
local ID_EXIT = wx.wxID_EXIT
local ID_PRINT = wx.wxID_PRINT
local ID_PRINT_PREVIEW = NewID()
local ID_PAGE_SETUP = wx.wxID_PAGE_SETUP
-- Edit menu
local ID_CUT = wx.wxID_CUT
local ID_COPY = wx.wxID_COPY
local ID_PASTE = wx.wxID_PASTE
local ID_SELECTALL = wx.wxID_SELECTALL
local ID_UNDO = wx.wxID_UNDO
local ID_REDO = wx.wxID_REDO
local ID_AUTOCOMPLETE = NewID()
local ID_AUTOCOMPLETE_ENABLE = NewID()
local ID_COMMENT = NewID()
local ID_FOLD = NewID()
-- Find menu
local ID_FIND = wx.wxID_FIND
local ID_FINDNEXT = NewID()
local ID_FINDPREV = NewID()
local ID_REPLACE = NewID()
local ID_GOTOLINE = NewID()
local ID_SORT = NewID()
-- Debug menu
local ID_TOGGLEBREAKPOINT = NewID()
local ID_COMPILE = NewID()
local ID_RUN = NewID()
local ID_ATTACH_DEBUG = NewID()
local ID_START_DEBUG = NewID()
local ID_USECONSOLE = NewID()
local ID_STOP_DEBUG = NewID()
local ID_STEP = NewID()
local ID_STEP_OVER = NewID()
local ID_STEP_OUT = NewID()
local ID_CONTINUE = NewID()
local ID_BREAK = NewID()
local ID_VIEWCALLSTACK = NewID()
local ID_VIEWWATCHWINDOW = NewID()
local ID_SHOWHIDEWINDOW = NewID()
local ID_CLEAROUTPUT = NewID()
local ID_DEBUGGER_PORT = NewID()
-- Help menu
local ID_ABOUT = wx.wxID_ABOUT
-- Watch window menu items
local ID_WATCH_LISTCTRL = NewID()
local ID_ADDWATCH = NewID()
local ID_EDITWATCH = NewID()
local ID_REMOVEWATCH = NewID()
local ID_EVALUATEWATCH = NewID()
-- Markers for editor marker margin
local BREAKPOINT_MARKER = 1
local BREAKPOINT_MARKER_VALUE = 2 -- = 2^BREAKPOINT_MARKER
local CURRENT_LINE_MARKER = 2
local CURRENT_LINE_MARKER_VALUE = 4 -- = 2^CURRENT_LINE_MARKER
-- ASCII values for common chars
local char_CR = string.byte("\r")
local char_LF = string.byte("\n")
local char_Tab = string.byte("\t")
local char_Sp = string.byte(" ")
-- Global variables
programName = nil -- the name of the wxLua program to be used when starting debugger
editorApp = wx.wxGetApp()
debuggerServer = nil -- wxLuaDebuggerServer object when debugging, else nil
debuggerServer_ = nil -- temp wxLuaDebuggerServer object for deletion
debuggee_running = false -- true when the debuggee is running
debugger_destroy = 0 -- > 0 if the debugger is to be destroyed in wxEVT_IDLE
debuggee_pid = 0 -- pid of the debuggee process
debuggerPortNumber = 1551 -- the port # to use for debugging
-- wxWindow variables
frame = nil -- wxFrame the main top level window
splitter = nil -- wxSplitterWindow for the notebook and errorLog
notebook = nil -- wxNotebook of editors
errorLog = nil -- wxStyledTextCtrl log window for messages
watchWindow = nil -- the watchWindow, nil when not created
watchListCtrl = nil -- the child listctrl in the watchWindow
in_evt_focus = false -- true when in editor focus event to avoid recursion
openDocuments = {} -- open notebook editor documents[winId] = {
-- editor = wxStyledTextCtrl,
-- index = wxNotebook page index,
-- filePath = full filepath, nil if not saved,
-- fileName = just the filename,
-- modTime = wxDateTime of disk file or nil,
-- isModified = bool is the document modified? }
ignoredFilesList = {}
editorID = 100 -- window id to create editor pages with, incremented for new editors
exitingProgram = false -- are we currently exiting, ID_EXIT
autoCompleteEnable = true -- value of ID_AUTOCOMPLETE_ENABLE menu item
wxkeywords = nil -- a string of the keywords for scintilla of wxLua's wx.XXX items
font = nil -- fonts to use for the editor
fontItalic = nil
findReplace = {
dialog = nil, -- the wxDialog for find/replace
replace = false, -- is it a find or replace dialog
fWholeWord = false, -- match whole words
fMatchCase = false, -- case sensitive
fDown = true, -- search downwards in doc
fRegularExpr = false, -- use regex
fWrap = false, -- search wraps around
findTextArray = {}, -- array of last entered find text
findText = "", -- string to find
replaceTextArray = {}, -- array of last entered replace text
replaceText = "", -- string to replace find string with
foundString = false, -- was the string found for the last search
-- HasText() is there a string to search for
-- GetSelectedString() get currently selected string if it's on one line
-- FindString(reverse) find the findText string
-- Show(replace) create the dialog
}
-- ----------------------------------------------------------------------------
-- Pick some reasonable fixed width fonts to use for the editor
if wx.__WXMSW__ then
font = wx.wxFont(10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_NORMAL, wx.wxFONTWEIGHT_NORMAL, false, "Andale Mono")
fontItalic = wx.wxFont(10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_ITALIC, wx.wxFONTWEIGHT_NORMAL, false, "Andale Mono")
else
font = wx.wxFont(10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_NORMAL, wx.wxFONTWEIGHT_NORMAL, false, "")
fontItalic = wx.wxFont(10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_ITALIC, wx.wxFONTWEIGHT_NORMAL, false, "")
end
-- ----------------------------------------------------------------------------
-- Initialize the wxConfig for loading/saving the preferences
config = wx.wxFileConfig("wxLuaIDE", "WXLUA")
if config then
config:SetRecordDefaults()
end
-- ----------------------------------------------------------------------------
-- Create the wxFrame
-- ----------------------------------------------------------------------------
frame = wx.wxFrame(wx.NULL, wx.wxID_ANY, "wxLua")
statusBar = frame:CreateStatusBar( 4 )
local status_txt_width = statusBar:GetTextExtent("OVRW")
frame:SetStatusWidths({-1, status_txt_width, status_txt_width, status_txt_width*5})
frame:SetStatusText("Welcome to wxLua")
toolBar = frame:CreateToolBar(wx.wxNO_BORDER + wx.wxTB_FLAT + wx.wxTB_DOCKABLE)
-- note: Ususally the bmp size isn't necessary, but the HELP icon is not the right size in MSW
local toolBmpSize = toolBar:GetToolBitmapSize()
toolBar:AddTool(ID_NEW, "New", wx.wxArtProvider.GetBitmap(wx.wxART_NORMAL_FILE, wx.wxART_MENU, toolBmpSize), "Create an empty document")
toolBar:AddTool(ID_OPEN, "Open", wx.wxArtProvider.GetBitmap(wx.wxART_FILE_OPEN, wx.wxART_MENU, toolBmpSize), "Open an existing document")
toolBar:AddTool(ID_SAVE, "Save", wx.wxArtProvider.GetBitmap(wx.wxART_FILE_SAVE, wx.wxART_MENU, toolBmpSize), "Save the current document")
toolBar:AddTool(ID_SAVEALL, "Save All", wx.wxArtProvider.GetBitmap(wx.wxART_NEW_DIR, wx.wxART_MENU, toolBmpSize), "Save all documents")
toolBar:AddSeparator()
toolBar:AddTool(ID_CUT, "Cut", wx.wxArtProvider.GetBitmap(wx.wxART_CUT, wx.wxART_MENU, toolBmpSize), "Cut the selection")
toolBar:AddTool(ID_COPY, "Copy", wx.wxArtProvider.GetBitmap(wx.wxART_COPY, wx.wxART_MENU, toolBmpSize), "Copy the selection")
toolBar:AddTool(ID_PASTE, "Paste", wx.wxArtProvider.GetBitmap(wx.wxART_PASTE, wx.wxART_MENU, toolBmpSize), "Paste text from the clipboard")
toolBar:AddSeparator()
toolBar:AddTool(ID_UNDO, "Undo", wx.wxArtProvider.GetBitmap(wx.wxART_UNDO, wx.wxART_MENU, toolBmpSize), "Undo last edit")
toolBar:AddTool(ID_REDO, "Redo", wx.wxArtProvider.GetBitmap(wx.wxART_REDO, wx.wxART_MENU, toolBmpSize), "Redo last undo")
toolBar:AddSeparator()
toolBar:AddTool(ID_FIND, "Find", wx.wxArtProvider.GetBitmap(wx.wxART_FIND, wx.wxART_MENU, toolBmpSize), "Find text")
toolBar:AddTool(ID_REPLACE, "Replace", wx.wxArtProvider.GetBitmap(wx.wxART_FIND_AND_REPLACE, wx.wxART_MENU, toolBmpSize), "Find and replace text")
toolBar:Realize()
-- ----------------------------------------------------------------------------
-- Add the child windows to the frame
splitter = wx.wxSplitterWindow(frame, wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxDefaultSize,
wx.wxSP_3DSASH)
notebook = wx.wxNotebook(splitter, wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxDefaultSize,
wx.wxCLIP_CHILDREN)
notebookFileDropTarget = wx.wxLuaFileDropTarget();
notebookFileDropTarget.OnDropFiles = function(self, x, y, filenames)
for i = 1, #filenames do
LoadFile(filenames[i], nil, true)
end
return true
end
notebook:SetDropTarget(notebookFileDropTarget)
notebook:Connect(wx.wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED,
function (event)
if not exitingProgram then
SetEditorSelection(event:GetSelection())
end
event:Skip() -- skip to let page change
end)
errorLog = wxstc.wxStyledTextCtrl(splitter, wx.wxID_ANY)
errorLog:Show(false)
errorLog:SetFont(font)
errorLog:StyleSetFont(wxstc.wxSTC_STYLE_DEFAULT, font)
errorLog:StyleClearAll()
errorLog:SetMarginWidth(1, 16) -- marker margin
errorLog:SetMarginType(1, wxstc.wxSTC_MARGIN_SYMBOL);
errorLog:MarkerDefine(CURRENT_LINE_MARKER, wxstc.wxSTC_MARK_ARROWS, wx.wxBLACK, wx.wxWHITE)
errorLog:SetReadOnly(true)
splitter:Initialize(notebook) -- split later to show errorLog
-- ----------------------------------------------------------------------------
-- wxConfig load/save preferences functions
function ConfigRestoreFramePosition(window, windowName)
local path = config:GetPath()
config:SetPath("/"..windowName)
local _, s = config:Read("s", -1)
local _, x = config:Read("x", 0)
local _, y = config:Read("y", 0)
local _, w = config:Read("w", 0)
local _, h = config:Read("h", 0)
if (s ~= -1) and (s ~= 2) then
local clientX, clientY, clientWidth, clientHeight
clientX, clientY, clientWidth, clientHeight = wx.wxClientDisplayRect()
if x < clientX then x = clientX end
if y < clientY then y = clientY end
if w > clientWidth then w = clientWidth end
if h > clientHeight then h = clientHeight end
window:SetSize(x, y, w, h)
elseif s == 1 then
window:Maximize(true)
end
config:SetPath(path)
end
function ConfigSaveFramePosition(window, windowName)
local path = config:GetPath()
config:SetPath("/"..windowName)
local s = 0
local w, h = window:GetSizeWH()
local x, y = window:GetPositionXY()
if window:IsMaximized() then
s = 1
elseif window:IsIconized() then
s = 2
end
config:Write("s", s)
if s == 0 then
config:Write("x", x)
config:Write("y", y)
config:Write("w", w)
config:Write("h", h)
end
config:SetPath(path)
end
-- ----------------------------------------------------------------------------
-- Get/Set notebook editor page, use nil for current page, returns nil if none
function GetEditor(selection)
local editor = nil
if selection == nil then
selection = notebook:GetSelection()
end
if (selection >= 0) and (selection < notebook:GetPageCount()) then
editor = notebook:GetPage(selection):DynamicCast("wxStyledTextCtrl")
end
return editor
end
-- init new notebook page selection, use nil for current page
function SetEditorSelection(selection)
local editor = GetEditor(selection)
if editor then
editor:SetFocus()
editor:SetSTCFocus(true)
IsFileAlteredOnDisk(editor)
end
UpdateStatusText(editor) -- update even if nil
end
-- ----------------------------------------------------------------------------
-- Update the statusbar text of the frame using the given editor.
-- Only update if the text has changed.
statusTextTable = { "OVR?", "R/O?", "Cursor Pos" }
function UpdateStatusText(editor)
local texts = { "", "", "" }
if frame and editor then
local pos = editor:GetCurrentPos()
local line = editor:LineFromPosition(pos)
local col = 1 + pos - editor:PositionFromLine(line)
texts = { iff(editor:GetOvertype(), "OVR", "INS"),
iff(editor:GetReadOnly(), "R/O", "R/W"),
"Ln "..tostring(line + 1).." Col "..tostring(col) }
end
if frame then
for n = 1, 3 do
if (texts[n] ~= statusTextTable[n]) then
frame:SetStatusText(texts[n], n)
statusTextTable[n] = texts[n]
end
end
end
end
-- ----------------------------------------------------------------------------
-- Get file modification time, returns a wxDateTime (check IsValid) or nil if
-- the file doesn't exist
function GetFileModTime(filePath)
if filePath and (string.len(filePath) > 0) then
local fn = wx.wxFileName(filePath)
if fn:FileExists() then
return fn:GetModificationTime()
end
end
return nil
end
-- Check if file is altered, show dialog to reload it
function IsFileAlteredOnDisk(editor)
if not editor then return end
local id = editor:GetId()
if openDocuments[id] then
local filePath = openDocuments[id].filePath
local fileName = openDocuments[id].fileName
local oldModTime = openDocuments[id].modTime
if filePath and (string.len(filePath) > 0) and oldModTime and oldModTime:IsValid() then
local modTime = GetFileModTime(filePath)
if modTime == nil then
openDocuments[id].modTime = nil
wx.wxMessageBox(fileName.." is no longer on the disk.",
"wxLua Message",
wx.wxOK + wx.wxCENTRE, frame)
elseif modTime:IsValid() and oldModTime:IsEarlierThan(modTime) then
local ret = wx.wxMessageBox(fileName.." has been modified on disk.\nDo you want to reload it?",
"wxLua Message",
wx.wxYES_NO + wx.wxCENTRE, frame)
if ret ~= wx.wxYES or LoadFile(filePath, editor, true) then
openDocuments[id].modTime = nil
end
end
end
end
end
-- Set if the document is modified and update the notebook page text
function SetDocumentModified(id, modified)
local pageText = openDocuments[id].fileName or "untitled.lua"
if modified then
pageText = "* "..pageText
end
openDocuments[id].isModified = modified
notebook:SetPageText(openDocuments[id].index, pageText)
end
-- ----------------------------------------------------------------------------
-- Create an editor and add it to the notebook
function CreateEditor(name)
local editor = wxstc.wxStyledTextCtrl(notebook, editorID,
wx.wxDefaultPosition, wx.wxDefaultSize,
wx.wxSUNKEN_BORDER)
editorID = editorID + 1 -- increment so they're always unique
editor:SetBufferedDraw(true)
editor:StyleClearAll()
editor:SetFont(font)
editor:StyleSetFont(wxstc.wxSTC_STYLE_DEFAULT, font)
for i = 0, 32 do
editor:StyleSetFont(i, font)
end
editor:StyleSetForeground(0, wx.wxColour(128, 128, 128)) -- White space
editor:StyleSetForeground(1, wx.wxColour(0, 127, 0)) -- Block Comment
editor:StyleSetFont(1, fontItalic)
--editor:StyleSetUnderline(1, false)
editor:StyleSetForeground(2, wx.wxColour(0, 127, 0)) -- Line Comment
editor:StyleSetFont(2, fontItalic) -- Doc. Comment
--editor:StyleSetUnderline(2, false)
editor:StyleSetForeground(3, wx.wxColour(127, 127, 127)) -- Number
editor:StyleSetForeground(4, wx.wxColour(0, 127, 127)) -- Keyword
editor:StyleSetForeground(5, wx.wxColour(0, 0, 127)) -- Double quoted string
editor:StyleSetBold(5, true)
--editor:StyleSetUnderline(5, false)
editor:StyleSetForeground(6, wx.wxColour(127, 0, 127)) -- Single quoted string
editor:StyleSetForeground(7, wx.wxColour(127, 0, 127)) -- not used
editor:StyleSetForeground(8, wx.wxColour(0, 127, 127)) -- Literal strings
editor:StyleSetForeground(9, wx.wxColour(127, 127, 0)) -- Preprocessor
editor:StyleSetForeground(10, wx.wxColour(0, 0, 0)) -- Operators
--editor:StyleSetBold(10, true)
editor:StyleSetForeground(11, wx.wxColour(0, 0, 0)) -- Identifiers
editor:StyleSetForeground(12, wx.wxColour(0, 0, 0)) -- Unterminated strings
editor:StyleSetBackground(12, wx.wxColour(224, 192, 224))
editor:StyleSetBold(12, true)
editor:StyleSetEOLFilled(12, true)
editor:StyleSetForeground(13, wx.wxColour(0, 0, 95)) -- Keyword 2 highlighting styles
editor:StyleSetForeground(14, wx.wxColour(0, 95, 0)) -- Keyword 3
editor:StyleSetForeground(15, wx.wxColour(127, 0, 0)) -- Keyword 4
editor:StyleSetForeground(16, wx.wxColour(127, 0, 95)) -- Keyword 5
editor:StyleSetForeground(17, wx.wxColour(35, 95, 175)) -- Keyword 6
editor:StyleSetForeground(18, wx.wxColour(0, 127, 127)) -- Keyword 7
editor:StyleSetBackground(18, wx.wxColour(240, 255, 255)) -- Keyword 8
editor:StyleSetForeground(19, wx.wxColour(0, 127, 127))
editor:StyleSetBackground(19, wx.wxColour(224, 255, 255))
editor:StyleSetForeground(20, wx.wxColour(0, 127, 127))
editor:StyleSetBackground(20, wx.wxColour(192, 255, 255))
editor:StyleSetForeground(21, wx.wxColour(0, 127, 127))
editor:StyleSetBackground(21, wx.wxColour(176, 255, 255))
editor:StyleSetForeground(22, wx.wxColour(0, 127, 127))
editor:StyleSetBackground(22, wx.wxColour(160, 255, 255))
editor:StyleSetForeground(23, wx.wxColour(0, 127, 127))
editor:StyleSetBackground(23, wx.wxColour(144, 255, 255))
editor:StyleSetForeground(24, wx.wxColour(0, 127, 127))
editor:StyleSetBackground(24, wx.wxColour(128, 155, 255))
editor:StyleSetForeground(32, wx.wxColour(224, 192, 224)) -- Line number
editor:StyleSetBackground(33, wx.wxColour(192, 192, 192)) -- Brace highlight
editor:StyleSetForeground(34, wx.wxColour(0, 0, 255))
editor:StyleSetBold(34, true) -- Brace incomplete highlight
editor:StyleSetForeground(35, wx.wxColour(255, 0, 0))
editor:StyleSetBold(35, true) -- Indentation guides
editor:StyleSetForeground(37, wx.wxColour(192, 192, 192))
editor:StyleSetBackground(37, wx.wxColour(255, 255, 255))
editor:SetUseTabs(false)
editor:SetTabWidth(4)
editor:SetIndent(4)
editor:SetIndentationGuides(true)
editor:SetVisiblePolicy(wxstc.wxSTC_VISIBLE_SLOP, 3)
--editor:SetXCaretPolicy(wxstc.wxSTC_CARET_SLOP, 10)
--editor:SetYCaretPolicy(wxstc.wxSTC_CARET_SLOP, 3)
editor:SetMarginWidth(0, editor:TextWidth(32, "99999_")) -- line # margin
editor:SetMarginWidth(1, 16) -- marker margin
editor:SetMarginType(1, wxstc.wxSTC_MARGIN_SYMBOL)
editor:SetMarginSensitive(1, true)
editor:MarkerDefine(BREAKPOINT_MARKER, wxstc.wxSTC_MARK_ROUNDRECT, wx.wxWHITE, wx.wxRED)
editor:MarkerDefine(CURRENT_LINE_MARKER, wxstc.wxSTC_MARK_ARROW, wx.wxBLACK, wx.wxGREEN)
editor:SetMarginWidth(2, 16) -- fold margin
editor:SetMarginType(2, wxstc.wxSTC_MARGIN_SYMBOL)
editor:SetMarginMask(2, wxstc.wxSTC_MASK_FOLDERS)
editor:SetMarginSensitive(2, true)
editor:SetFoldFlags(wxstc.wxSTC_FOLDFLAG_LINEBEFORE_CONTRACTED +
wxstc.wxSTC_FOLDFLAG_LINEAFTER_CONTRACTED)
editor:SetProperty("fold", "1")
editor:SetProperty("fold.compact", "1")
editor:SetProperty("fold.comment", "1")
local grey = wx.wxColour(128, 128, 128)
editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDEROPEN, wxstc.wxSTC_MARK_BOXMINUS, wx.wxWHITE, grey)
editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDER, wxstc.wxSTC_MARK_BOXPLUS, wx.wxWHITE, grey)
editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDERSUB, wxstc.wxSTC_MARK_VLINE, wx.wxWHITE, grey)
editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDERTAIL, wxstc.wxSTC_MARK_LCORNER, wx.wxWHITE, grey)
editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDEREND, wxstc.wxSTC_MARK_BOXPLUSCONNECTED, wx.wxWHITE, grey)
editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDEROPENMID, wxstc.wxSTC_MARK_BOXMINUSCONNECTED, wx.wxWHITE, grey)
editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDERMIDTAIL, wxstc.wxSTC_MARK_TCORNER, wx.wxWHITE, grey)
grey:delete()
editor:Connect(wxstc.wxEVT_STC_MARGINCLICK,
function (event)
local line = editor:LineFromPosition(event:GetPosition())
local margin = event:GetMargin()
if margin == 1 then
ToggleDebugMarker(editor, line)
elseif margin == 2 then
if wx.wxGetKeyState(wx.WXK_SHIFT) and wx.wxGetKeyState(wx.WXK_CONTROL) then
FoldSome()
else
local level = editor:GetFoldLevel(line)
if HasBit(level, wxstc.wxSTC_FOLDLEVELHEADERFLAG) then
editor:ToggleFold(line)
end
end
end
end)
editor:Connect(wxstc.wxEVT_STC_CHARADDED,
function (event)
-- auto-indent
local ch = event:GetKey()
if (ch == char_CR) or (ch == char_LF) then
local pos = editor:GetCurrentPos()
local line = editor:LineFromPosition(pos)
if (line > 0) and (editor:LineLength(line) == 0) then
local indent = editor:GetLineIndentation(line - 1)
if indent > 0 then
editor:SetLineIndentation(line, indent)
editor:GotoPos(pos + indent)
end
end
elseif autoCompleteEnable then -- code completion prompt
local pos = editor:GetCurrentPos()
local start_pos = editor:WordStartPosition(pos, true)
-- must have "wx.X" otherwise too many items
if (pos - start_pos > 0) and (start_pos > 2) then
local range = editor:GetTextRange(start_pos-3, start_pos)
if range == "wx." then
local commandEvent = wx.wxCommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED,
ID_AUTOCOMPLETE)
wx.wxPostEvent(frame, commandEvent)
end
end
end
end)
editor:Connect(wxstc.wxEVT_STC_USERLISTSELECTION,
function (event)
local pos = editor:GetCurrentPos()
local start_pos = editor:WordStartPosition(pos, true)
editor:SetSelection(start_pos, pos)
editor:ReplaceSelection(event:GetText())
end)
editor:Connect(wxstc.wxEVT_STC_SAVEPOINTREACHED,
function (event)
SetDocumentModified(editor:GetId(), false)
end)
editor:Connect(wxstc.wxEVT_STC_SAVEPOINTLEFT,
function (event)
SetDocumentModified(editor:GetId(), true)
end)
editor:Connect(wxstc.wxEVT_STC_UPDATEUI,
function (event)
UpdateStatusText(editor)
end)
editor:Connect(wx.wxEVT_SET_FOCUS,
function (event)
event:Skip()
if in_evt_focus or exitingProgram then return end
in_evt_focus = true
IsFileAlteredOnDisk(editor)
in_evt_focus = false
end)
if notebook:AddPage(editor, name, true) then
local id = editor:GetId()
local document = {}
document.editor = editor
document.index = notebook:GetSelection()
document.fileName = nil
document.filePath = nil
document.modTime = nil
document.isModified = false
openDocuments[id] = document
end
return editor
end
function IsLuaFile(filePath)
return filePath and (string.len(filePath) > 4) and
(string.lower(string.sub(filePath, -4)) == ".lua")
end
function SetupKeywords(editor, useLuaParser)
if useLuaParser then
editor:SetLexer(wxstc.wxSTC_LEX_LUA)
-- Note: these keywords are shamelessly ripped from scite 1.68
editor:SetKeyWords(0,
[[and break do else elseif end false for function if
in local nil not or repeat return then true until while]])
editor:SetKeyWords(1,
[[_VERSION assert collectgarbage dofile error gcinfo loadfile loadstring
print rawget rawset require tonumber tostring type unpack]])
editor:SetKeyWords(2,
[[_G getfenv getmetatable ipairs loadlib next pairs pcall
rawequal setfenv setmetatable xpcall
string table math coroutine io os debug
load module select]])
editor:SetKeyWords(3,
[[string.byte string.char string.dump string.find string.len
string.lower string.rep string.sub string.upper string.format string.gfind string.gsub
table.concat table.foreach table.foreachi table.getn table.sort table.insert table.remove table.setn
math.abs math.acos math.asin math.atan math.atan2 math.ceil math.cos math.deg math.exp
math.floor math.frexp math.ldexp math.log math.log10 math.max math.min math.mod
math.pi math.pow math.rad math.random math.randomseed math.sin math.sqrt math.tan
string.gmatch string.match string.reverse table.maxn
math.cosh math.fmod math.modf math.sinh math.tanh math.huge]])
editor:SetKeyWords(4,
[[coroutine.create coroutine.resume coroutine.status
coroutine.wrap coroutine.yield
io.close io.flush io.input io.lines io.open io.output io.read io.tmpfile io.type io.write
io.stdin io.stdout io.stderr
os.clock os.date os.difftime os.execute os.exit os.getenv os.remove os.rename
os.setlocale os.time os.tmpname
coroutine.running package.cpath package.loaded package.loadlib package.path
package.preload package.seeall io.popen
debug.debug debug.getfenv debug.gethook debug.getinfo debug.getlocal
debug.getmetatable debug.getregistry debug.getupvalue debug.setfenv
debug.sethook debug.setlocal debug.setmetatable debug.setupvalue debug.traceback]])
-- Get the items in the global "wx" table for autocompletion
if not wxkeywords then
local keyword_table = {}
for index, value in pairs(wx) do
table.insert(keyword_table, "wx."..index.." ")
end
table.sort(keyword_table)
wxkeywords = table.concat(keyword_table)
end
editor:SetKeyWords(5, wxkeywords)
else
editor:SetLexer(wxstc.wxSTC_LEX_NULL)
editor:SetKeyWords(0, "")
end
editor:Colourise(0, -1)
end
function CreateAutoCompList(key_) -- much faster than iterating the wx. table
local key = "wx."..key_;
local a, b = string.find(wxkeywords, key, 1, 1)
local key_list = ""
while a do
local c, d = string.find(wxkeywords, " ", b, 1)
key_list = key_list..string.sub(wxkeywords, a+3, c or -1)
a, b = string.find(wxkeywords, key, d, 1)
end
return key_list
end
-- ---------------------------------------------------------------------------
-- Create the watch window
function ProcessWatches()
if watchListCtrl and debuggerServer then
for idx = 0, watchListCtrl:GetItemCount() - 1 do
local expression = watchListCtrl:GetItemText(idx)
debuggerServer:EvaluateExpr(idx, expression)
end
end
end
function CloseWatchWindow()
if watchWindow then
watchListCtrl = nil
watchWindow:Destroy()
watchWindow = nil
end
end
function CreateWatchWindow()
local width = 180
watchWindow = wx.wxFrame(frame, wx.wxID_ANY, "wxLua Watch Window",
wx.wxDefaultPosition, wx.wxSize(width, 160))
local watchMenu = wx.wxMenu{
{ ID_ADDWATCH, "&Add Watch" },
{ ID_EDITWATCH, "&Edit Watch\tF2" },
{ ID_REMOVEWATCH, "&Remove Watch" },
{ ID_EVALUATEWATCH, "Evaluate &Watches" }}
local watchMenuBar = wx.wxMenuBar()
watchMenuBar:Append(watchMenu, "&Watches")
watchWindow:SetMenuBar(watchMenuBar)
watchListCtrl = wx.wxListCtrl(watchWindow, ID_WATCH_LISTCTRL,
wx.wxDefaultPosition, wx.wxDefaultSize,
wx.wxLC_REPORT + wx.wxLC_EDIT_LABELS)
local info = wx.wxListItem()
info:SetMask(wx.wxLIST_MASK_TEXT + wx.wxLIST_MASK_WIDTH)
info:SetText("Expression")
info:SetWidth(width / 2)
watchListCtrl:InsertColumn(0, info)
info:SetText("Value")
info:SetWidth(width / 2)
watchListCtrl:InsertColumn(1, info)
watchWindow:CentreOnParent()
ConfigRestoreFramePosition(watchWindow, "WatchWindow")
watchWindow:Show(true)
local function FindSelectedWatchItem()
local count = watchListCtrl:GetSelectedItemCount()
if count > 0 then
for idx = 0, watchListCtrl:GetItemCount() - 1 do
if watchListCtrl:GetItemState(idx, wx.wxLIST_STATE_FOCUSED) ~= 0 then
return idx
end
end
end
return -1
end
watchWindow:Connect( wx.wxEVT_CLOSE_WINDOW,
function (event)
ConfigSaveFramePosition(watchWindow, "WatchWindow")
watchWindow = nil
watchListCtrl = nil
event:Skip()
end)
watchWindow:Connect(ID_ADDWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
local row = watchListCtrl:InsertItem(watchListCtrl:GetItemCount(), "Expr")
watchListCtrl:SetItem(row, 0, "Expr")
watchListCtrl:SetItem(row, 1, "Value")
watchListCtrl:EditLabel(row)
end)
watchWindow:Connect(ID_EDITWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
local row = FindSelectedWatchItem()
if row >= 0 then
watchListCtrl:EditLabel(row)
end
end)
watchWindow:Connect(ID_EDITWATCH, wx.wxEVT_UPDATE_UI,
function (event)
event:Enable(watchListCtrl:GetSelectedItemCount() > 0)
end)
watchWindow:Connect(ID_REMOVEWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
local row = FindSelectedWatchItem()
if row >= 0 then
watchListCtrl:DeleteItem(row)
end
end)
watchWindow:Connect(ID_REMOVEWATCH, wx.wxEVT_UPDATE_UI,
function (event)
event:Enable(watchListCtrl:GetSelectedItemCount() > 0)
end)
watchWindow:Connect(ID_EVALUATEWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
ProcessWatches()
end)
watchWindow:Connect(ID_EVALUATEWATCH, wx.wxEVT_UPDATE_UI,
function (event)
event:Enable(watchListCtrl:GetItemCount() > 0)
end)
watchListCtrl:Connect(wx.wxEVT_COMMAND_LIST_END_LABEL_EDIT,
function (event)
watchListCtrl:SetItem(event:GetIndex(), 0, event:GetText())
ProcessWatches()
event:Skip()
end)
end
-- ---------------------------------------------------------------------------
-- Create the File menu and attach the callback functions
-- force all the wxEVT_UPDATE_UI handlers to be called
function UpdateUIMenuItems()
if frame and frame:GetMenuBar() then
for n = 0, frame:GetMenuBar():GetMenuCount()-1 do
frame:GetMenuBar():GetMenu(n):UpdateUI()
end
end
end
menuBar = wx.wxMenuBar()
fileMenu = wx.wxMenu({
{ ID_NEW, "&New\tCtrl-N", "Create an empty document" },
{ ID_OPEN, "&Open...\tCtrl-O", "Open an existing document" },
{ ID_CLOSE, "&Close page\tCtrl+W", "Close the current editor window" },
{ },
{ ID_SAVE, "&Save\tCtrl-S", "Save the current document" },
{ ID_SAVEAS, "Save &As...\tAlt-S", "Save the current document to a file with a new name" },
{ ID_SAVEALL, "Save A&ll...\tCtrl-Shift-S", "Save all open documents" },
{ },
{ ID_PRINT, "&Print... \tCtrl-p", "Print document"},
{ ID_PRINT_PREVIEW, "&Print Preview... \tShift-Ctrl-p", "Print preview"},
{ ID_PAGE_SETUP, "Page S&etup...", "Set up printing"},
{ },
{ ID_EXIT, "E&xit\tAlt-X", "Exit Program" }})
menuBar:Append(fileMenu, "&File")
function NewFile(event)
local editor = CreateEditor("untitled.lua")
SetupKeywords(editor, true)
end
frame:Connect(ID_NEW, wx.wxEVT_COMMAND_MENU_SELECTED, NewFile)
-- Find an editor page that hasn't been used at all, eg. an untouched NewFile()
function FindDocumentToReuse()
local editor = nil
for id, document in pairs(openDocuments) do
if (document.editor:GetLength() == 0) and
(not document.isModified) and (not document.filePath) and
not (document.editor:GetReadOnly() == true) then
editor = document.editor
break
end
end
return editor
end
function LoadFile(filePath, editor, file_must_exist)
local file_text = ""
local handle = io.open(filePath, "rb")
if handle then
file_text = handle:read("*a")
handle:close()
elseif file_must_exist then
return nil
end
if not editor then
editor = FindDocumentToReuse()
end
if not editor then
editor = CreateEditor(wx.wxFileName(filePath):GetFullName() or "untitled.lua")
end
editor:Clear()
editor:ClearAll()
SetupKeywords(editor, IsLuaFile(filePath))
editor:MarkerDeleteAll(BREAKPOINT_MARKER)
editor:MarkerDeleteAll(CURRENT_LINE_MARKER)
editor:AppendText(file_text)
editor:EmptyUndoBuffer()
local id = editor:GetId()
openDocuments[id].filePath = filePath
openDocuments[id].fileName = wx.wxFileName(filePath):GetFullName()
openDocuments[id].modTime = GetFileModTime(filePath)
SetDocumentModified(id, false)
editor:Colourise(0, -1)
return editor
end
function OpenFile(event)
local fileDialog = wx.wxFileDialog(frame, "Open file",
"",
"",
"Lua files (*.lua)|*.lua|Text files (*.txt)|*.txt|All files (*)|*",
wx.wxFD_OPEN + wx.wxFD_FILE_MUST_EXIST)
if fileDialog:ShowModal() == wx.wxID_OK then
if not LoadFile(fileDialog:GetPath(), nil, true) then
wx.wxMessageBox("Unable to load file '"..fileDialog:GetPath().."'.",
"wxLua Error",
wx.wxOK + wx.wxCENTRE, frame)
end
end
fileDialog:Destroy()
end
frame:Connect(ID_OPEN, wx.wxEVT_COMMAND_MENU_SELECTED, OpenFile)
-- save the file to filePath or if filePath is nil then call SaveFileAs
function SaveFile(editor, filePath)
if not filePath then
return SaveFileAs(editor)
else
local backPath = filePath..".bak"
os.remove(backPath)
os.rename(filePath, backPath)
local handle = io.open(filePath, "wb")
if handle then
local st = editor:GetText()
handle:write(st)
handle:close()
editor:EmptyUndoBuffer()
local id = editor:GetId()
openDocuments[id].filePath = filePath
openDocuments[id].fileName = wx.wxFileName(filePath):GetFullName()
openDocuments[id].modTime = GetFileModTime(filePath)
SetDocumentModified(id, false)
return true
else
wx.wxMessageBox("Unable to save file '"..filePath.."'.",
"wxLua Error Saving",
wx.wxOK + wx.wxCENTRE, frame)
end
end
return false
end
frame:Connect(ID_SAVE, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
local editor = GetEditor()
local id = editor:GetId()
local filePath = openDocuments[id].filePath
SaveFile(editor, filePath)
end)
frame:Connect(ID_SAVE, wx.wxEVT_UPDATE_UI,
function (event)
local editor = GetEditor()
if editor then
local id = editor:GetId()
if openDocuments[id] then
event:Enable(openDocuments[id].isModified)
end
end
end)
function SaveFileAs(editor)
local id = editor:GetId()
local saved = false
local fn = wx.wxFileName(openDocuments[id].filePath or "")
fn:Normalize() -- want absolute path for dialog
local fileDialog = wx.wxFileDialog(frame, "Save file as",
fn:GetPath(),
fn:GetFullName(),
"Lua files (*.lua)|*.lua|Text files (*.txt)|*.txt|All files (*)|*",
wx.wxFD_SAVE + wx.wxFD_OVERWRITE_PROMPT)
if fileDialog:ShowModal() == wx.wxID_OK then
local filePath = fileDialog:GetPath()
local save_file = true
if wx.wxFileExists(filePath) then
save_file = (wx.wxYES == wx.wxMessageBox(string.format("Replace file:\n%s", filePath), "wxLua Overwrite File",
wx.wxYES_NO + wx.wxICON_QUESTION, frame))
end