Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0ce69da6a | ||
|
|
da7edc4580 | ||
|
|
31b7e4d788 | ||
|
|
c940b56459 | ||
|
|
dcc28d9ce2 | ||
|
|
334a071219 | ||
|
|
48ba4e26b8 | ||
|
|
8da70c6e75 | ||
|
|
61198caae5 | ||
|
|
4e8b9d41ba | ||
|
|
86af637781 | ||
|
|
08825ba79a | ||
|
|
f2b3161474 | ||
|
|
22f2f68a3a | ||
|
|
e3f666570a | ||
|
|
dc757d48e5 | ||
|
|
da6b7db0e6 | ||
|
|
ec218e1424 | ||
|
|
c178ec9ab4 | ||
|
|
9f87a780a7 | ||
|
|
40809b6396 | ||
|
|
6f04ef8921 | ||
|
|
a3235b23bb | ||
|
|
b3fdde036e | ||
|
|
1e86c3c2d6 | ||
|
|
57a89f0c45 | ||
|
|
710c49850c | ||
|
|
94662bbd4c | ||
|
|
fc1f9375ed | ||
|
|
7db6b1ad07 | ||
|
|
d70d6a0bd6 | ||
|
|
b130e68b51 | ||
|
|
fdbb835199 | ||
|
|
3cc2d861db | ||
|
|
e5ca96879a | ||
|
|
80248d2a77 | ||
|
|
400de47586 | ||
|
|
cfdbbff3c7 | ||
|
|
5235cc001b | ||
|
|
12fd9611f7 | ||
|
|
be323d555d | ||
|
|
11facf0acb | ||
|
|
4b13017620 | ||
|
|
7ddaaa20e6 | ||
|
|
b659dfaf79 | ||
|
|
20c73a9e92 | ||
|
|
dfca13b96d | ||
|
|
3c25189fdb | ||
|
|
a1c67447b5 | ||
|
|
6710758962 | ||
|
|
e761a5f7ef | ||
|
|
c8f84e4694 | ||
|
|
e40215a4d1 | ||
|
|
46d6ab8f9e | ||
|
|
377fbfab39 | ||
|
|
4c4259f5ca | ||
|
|
0e8b29936e | ||
|
|
54f16def09 | ||
|
|
e8d7235cfb | ||
|
|
bac1cbc028 | ||
|
|
94ceb8d9df | ||
|
|
6f1a0c0316 | ||
|
|
a225d7e7c7 | ||
|
|
e3f7719ca7 | ||
|
|
809e46eaf7 | ||
|
|
5450ad8311 | ||
|
|
68866eb2cb | ||
|
|
9ff569e8ce | ||
|
|
9375235fc6 | ||
|
|
a2cd63afa6 | ||
|
|
6052a86f0a | ||
|
|
75357d7f41 | ||
|
|
c493f62466 | ||
|
|
89ef72aab3 | ||
|
|
343423898e | ||
|
|
cf02a3ea55 | ||
|
|
811f2a7021 | ||
|
|
fe92bf89e5 | ||
|
|
c9ac9ca23f | ||
|
|
300c6b61c6 | ||
|
|
8678404b84 | ||
|
|
54b29472cc | ||
|
|
f0a3305753 | ||
|
|
d9ce3d0538 | ||
|
|
b457ccbccd | ||
|
|
1fb61028b1 | ||
|
|
98fc8e05bc | ||
|
|
03989f3fd8 | ||
|
|
b41eb364bb | ||
|
|
27708b4dd2 | ||
|
|
0d2ab45c6b | ||
|
|
0cfede0e7a | ||
|
|
2a404541e5 | ||
|
|
657526eab4 | ||
|
|
eb16a80515 | ||
|
|
e283bcb65d | ||
|
|
a1459ba494 | ||
|
|
042998dd71 | ||
|
|
717d46a332 | ||
|
|
b6fd404788 | ||
|
|
02a43a63a6 | ||
|
|
719b76ea80 | ||
|
|
eae8540708 | ||
|
|
539a74aa7a | ||
|
|
4b0bcaa20e |
118
CHANGELOG.md
@@ -1,5 +1,123 @@
|
||||
# ZeroBrane Studio Changelog
|
||||
|
||||
## v0.80 (Aug 31 2014)
|
||||
|
||||
### Highlights
|
||||
- Added support for expanding table elements in Watch window.
|
||||
- Added editing of values in Watch window.
|
||||
- Added highlighting all instances of selected text.
|
||||
- Added replacing all selected instances using a dialog.
|
||||
- Added saving (one-line) layout for editor tabs.
|
||||
- Added support for `filename:<line>` and `filename:p<pos>` on the command line.
|
||||
- Added search in Console and Output windows.
|
||||
- Improved compatibility with Lua 5.2 to run the IDE.
|
||||
|
||||
### Special thanks
|
||||
- To [Li Jia](https://github.com/tiwb) for fixing remote path map when 'runonstart' option is set.
|
||||
|
||||
### Improvements
|
||||
- Added default values for `hotexit` and `saveallonrun` settings.
|
||||
- Added debugger `GetHostName` and `GetPortNumber` methods (#166).
|
||||
- Added a check for a local shortcut (F2/Del) being enabled before triggering.
|
||||
- Added refresh of expanded Watch values.
|
||||
- Added support for expanding table elements in Watch window.
|
||||
- Added package `AddWatch` method (#166).
|
||||
- Added `toolbar.iconsize` to configure toolbar icon size.
|
||||
- Added `run-as-scratchpad` toolbar icon (hidden by default).
|
||||
- Added `run` toolbar icon (hidden by default).
|
||||
- Added `find-in-files` toolbar icon (hidden by default).
|
||||
- Added support for disabling individual icons in the toolbar.
|
||||
- Added replacing all selected instances using a dialog (closes #342).
|
||||
- Added highlighting all instances of selected text (closes #344).
|
||||
- Added `filetree.mousemove` option to disable drag-n-drop (closes #351).
|
||||
- Added `suspended` to Output panel title when debugger is stopped (closes #350).
|
||||
- Added a warning when remote console can't evaluate an expression (#350).
|
||||
- Added handling of `osname` to package dependencies (#166).
|
||||
- Added `onIdle` event (#166).
|
||||
- Added `tree:FindItem` method (#166).
|
||||
- Added package `Yield` method (#166).
|
||||
- Added ability to set location of `ini` file from config.
|
||||
- Added ability to load bitmap as toolbar icon.
|
||||
- Added package `RemoveMenuItem` method (#166).
|
||||
- Added ability to customize toolbar.
|
||||
- Added saving (one-line) layout for editor tabs.
|
||||
- Added centering of the screen after re-indenting and sorting (#337).
|
||||
- Added local to variable 'activated' in function mapRemotePath
|
||||
- Added centering of the screen after 'go to definition' and back (#337).
|
||||
- Added centering of the screen after selection from the function list (#337).
|
||||
- Added package `onEditorUpdateUI` event (#166).
|
||||
- Added package `AddPanel` method (#166).
|
||||
- Added package `GetUIManager` method (#166).
|
||||
- Added editor `SetupKeywords` method (#166).
|
||||
- Added document `GetFileExit` method (#166).
|
||||
- Added `onEditorPainted` event (#166).
|
||||
- Added support for `name:<line>` and `name:p<pos>` on the command line.
|
||||
- Added error reporting on failure to load file from the command line.
|
||||
- Added metalua components to MANIFEST (missing in packaging on OSX).
|
||||
- Added saving auto-recovery record on switching from the application.
|
||||
- Added `hotexit` option to exit without forcing to save files.
|
||||
- Added setting of margin properties to support their reordering.
|
||||
- Added error reporting on failure to delete directory from project tree.
|
||||
- Added check for shortcut in conflict being enabled before activating (#233).
|
||||
- Added workaround for missing `GetChildren` call in some wxlua configurations.
|
||||
- Added unfolding modified lines to avoid leaving hidden lines in the editor.
|
||||
- Added search in Console and Output windows (closes #313).
|
||||
- Allowed double-click selection in the Output window (#313).
|
||||
- Avoided system lib conflict when debugging by using bundled libs (fixes #355).
|
||||
- Disabled editing on non-root watch elements.
|
||||
- Disabled smart indentation for multi-line comments and strings (#324).
|
||||
- Disabled re-indentation of multi-line comments/strings (#324).
|
||||
- Disabled `Opt+Shift+Left/Right` shortcut as it conflicts with block selection.
|
||||
- Enabled editing of values in Watch window.
|
||||
- Enabled `editor.autoreload` by default.
|
||||
- Improved config handling when `editor` configuration is removed/empty.
|
||||
- Improved `autotabs` logic when the file starts with indentation.
|
||||
- Improved auto-complete logic that tracks variable assignments (fixes #343).
|
||||
- Improved cursor positioning after re-indenting or sorting.
|
||||
- Improved compatibility with Lua5.2 to run the IDE.
|
||||
- Increased default project history length to 20.
|
||||
- Removed check for multiple references in stack values.
|
||||
- Refactored stack processing to use methods to handle expandable table values.
|
||||
- Refactored file name generation for compilation and static analysis.
|
||||
- Removed erroneous message about failure to open '-psn...' file on OSX.
|
||||
- Renamed all image files to remove cruft from their names.
|
||||
- Simplified logic for watch processing.
|
||||
- Switched from using TreeItemData to Lua tables for watch expressions.
|
||||
- Switched to using tree control for watches.
|
||||
- Updated copas library to support non-blocking requests using socket.http.
|
||||
- Updated Stack and Watch views to better stringify keys.
|
||||
- Updated watch menu to handle item under mouse cursor.
|
||||
- Updated constants for image lists.
|
||||
- Updated `FindMenuItem` method to search in the main and specified menus (#166).
|
||||
- Updated `ide.config` to access wx, wxstc, and os through metatable.
|
||||
- Updated recent projects/files handling to allow menus to be removed.
|
||||
- Updated package `FindMenuItem` method (#166).
|
||||
- Updated `autotabs` to respect `usetabs` when no indentation is present.
|
||||
- Updated copy/cut to capture one instance when all are the same (closes #345).
|
||||
- Updated default marker colors for lighter border (#305).
|
||||
- Updated auto-recovery logic to skip missing files (fixes #323).
|
||||
|
||||
### Fixes
|
||||
- Fixed disabling auto-recovery on app switching.
|
||||
- Fixed find-in-files error when used with editor not in focus (fixes #354).
|
||||
- Fixed package `GetStack` method to return proper control (#166).
|
||||
- Fixed Watch window background color on some Mint Linux systems.
|
||||
- Fixed debugging error when `debugger.runonstart` is specified (fixes #348, #341).
|
||||
- Fixed keybinding for `Ctrl-<punctuation>` working on Linux (fixes #346).
|
||||
- Fixed localization based on static analysis.
|
||||
- Fixed remote path map when 'runonstart' option is set.
|
||||
- Fixed error reporting during Analyze (fixes #340).
|
||||
- Fixed using image lists for stack/filetree to keep them in memory.
|
||||
- Fixed indentation when Enter is hit at the middle of a line.
|
||||
- Fixed formatting of `until` statements (fixes #335).
|
||||
- Fixed formatting of strings including comments '--' (#335).
|
||||
- Fixed restoring proper file names for unsaved tabs during auto-recovery.
|
||||
- Fixed deleting 'dynamic words' when multiple lines are removed.
|
||||
- Fixed `love.update` description (#247).
|
||||
- Fixed indentation of strings starting from `endSomething` (#324).
|
||||
- Fixed use of '%' in replacement for Lua5.2 compatibility (#153, #156, #143).
|
||||
- Fixed warnings from static analysis.
|
||||
|
||||
## v0.70 (Jun 18 2014)
|
||||
|
||||
### Highlights
|
||||
|
||||
@@ -74,6 +74,8 @@ Loading custom configuration:
|
||||
e.g.: zbstudio -cfg cfg/estrela.lua
|
||||
```
|
||||
|
||||
If you are loading a file, you can also request the cursor to be set on a particular line or at a particular position by using `filename:<line>` and `filename:p<pos>` syntax (0.71+).
|
||||
|
||||
## Author
|
||||
|
||||
### ZeroBrane Studio and MobDebug
|
||||
|
||||
@@ -5370,7 +5370,7 @@ local love = {
|
||||
},
|
||||
update = {
|
||||
args = "(dt: number)",
|
||||
description = "Callback function triggered when a key is pressed.",
|
||||
description = "Callback function used to update the state of the game every frame.",
|
||||
returns = "()",
|
||||
type = "function"
|
||||
},
|
||||
|
||||
@@ -53,14 +53,19 @@ return {
|
||||
-- modify CPATH to work with other Lua versions
|
||||
local clibs = ('/clibs%s/'):format(version and tostring(version):gsub('%.','') or '')
|
||||
local _, cpath = wx.wxGetEnv("LUA_CPATH")
|
||||
if rundebug and cpath then
|
||||
wx.wxSetEnv("LUA_CPATH", ide.osclibs..';'..cpath)
|
||||
end
|
||||
if version and cpath and not cpath:find(clibs, 1, true) then
|
||||
wx.wxSetEnv("LUA_CPATH", cpath:gsub('/clibs/', clibs)) end
|
||||
local _, cpath = wx.wxGetEnv("LUA_CPATH")
|
||||
wx.wxSetEnv("LUA_CPATH", cpath:gsub('/clibs/', clibs))
|
||||
end
|
||||
|
||||
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
|
||||
local pid = CommandLineRun(cmd,self:fworkdir(wfilename),true,false,nil,nil,
|
||||
function() if rundebug then wx.wxRemoveFile(filepath) end end)
|
||||
|
||||
if version and cpath then wx.wxSetEnv("LUA_CPATH", cpath) end
|
||||
if (rundebug or version) and cpath then wx.wxSetEnv("LUA_CPATH", cpath) end
|
||||
return pid
|
||||
end,
|
||||
fprojdir = function(self,wfilename)
|
||||
|
||||
@@ -222,11 +222,11 @@ end
|
||||
|
||||
-- same as above but with special treatment when reading chunks,
|
||||
-- unblocks on any data received.
|
||||
function copas.receivePartial(client, pattern)
|
||||
local s, err, part
|
||||
function copas.receivePartial(client, pattern, part)
|
||||
local s, err
|
||||
pattern = pattern or "*l"
|
||||
repeat
|
||||
s, err, part = client:receive(pattern)
|
||||
s, err, part = client:receive(pattern, part)
|
||||
if s or ( (type(pattern)=="number") and part~="" and part ~=nil ) or
|
||||
err ~= "timeout" then
|
||||
_reading_log[client] = nil
|
||||
@@ -310,11 +310,11 @@ local _skt_mt = {__index = {
|
||||
return copas.send (self.socket, data, from, to)
|
||||
end,
|
||||
|
||||
receive = function (self, pattern)
|
||||
receive = function (self, pattern, prefix)
|
||||
if (self.timeout==0) then
|
||||
return copas.receivePartial(self.socket, pattern)
|
||||
return copas.receivePartial(self.socket, pattern, prefix)
|
||||
end
|
||||
return copas.receive(self.socket, pattern)
|
||||
return copas.receive(self.socket, pattern, prefix)
|
||||
end,
|
||||
|
||||
flush = function (self)
|
||||
@@ -323,8 +323,12 @@ local _skt_mt = {__index = {
|
||||
|
||||
settimeout = function (self,time)
|
||||
self.timeout=time
|
||||
return
|
||||
return true
|
||||
end,
|
||||
|
||||
skip = function(self, ...) return self.socket:skip(...) end,
|
||||
|
||||
close = function(self, ...) return self.socket:close(...) end,
|
||||
}}
|
||||
|
||||
-- wraps a UDP socket, copy of TCP one adapted for UDP.
|
||||
@@ -352,7 +356,7 @@ local _skt_mt_udp = {__index = {
|
||||
|
||||
settimeout = function (self,time)
|
||||
self.timeout=time
|
||||
return
|
||||
return true
|
||||
end,
|
||||
}}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
--
|
||||
-- MobDebug 0.564
|
||||
-- MobDebug 0.60
|
||||
-- Copyright 2011-14 Paul Kulchenko
|
||||
-- Based on RemDebug 1.0 Copyright Kepler Project 2005
|
||||
--
|
||||
@@ -18,7 +18,7 @@ end)("os")
|
||||
|
||||
local mobdebug = {
|
||||
_NAME = "mobdebug",
|
||||
_VERSION = 0.564,
|
||||
_VERSION = 0.60,
|
||||
_COPYRIGHT = "Paul Kulchenko",
|
||||
_DESCRIPTION = "Mobile Remote Debugger for the Lua programming language",
|
||||
port = os and os.getenv and tonumber((os.getenv("MOBDEBUG_PORT"))) or 8172,
|
||||
|
||||
@@ -30,6 +30,8 @@ local events = {
|
||||
onEditorKeyDown = function(self, editor, event) end, -- return false
|
||||
onEditorCharAdded = function(self, editor, event) end, -- return false
|
||||
onEditorUserlistSelection = function(self, editor, event) end, -- return false
|
||||
onEditorUpdateUI = function(self, editor, event) end, -- return false
|
||||
onEditorPainted = function(self, editor, event) end, -- return false
|
||||
onFiletreeActivate = function(self, tree, event, item) end, -- return false
|
||||
onFiletreeLDown = function(self, tree, event, item) end,
|
||||
onFiletreeRDown = function(self, tree, event, item) end,
|
||||
@@ -41,6 +43,7 @@ local events = {
|
||||
onProjectClose = function(self, project) end,
|
||||
onInterpreterLoad = function(self, interpreter) end,
|
||||
onInterpreterClose = function(self, interpreter) end,
|
||||
onIdle = function(self, event) end,
|
||||
onIdleOnce = function(self, event) end,
|
||||
onAppFocusLost = function(self, app) end,
|
||||
onAppFocusSet = function(self, app) end,
|
||||
|
||||
26
spec/lua.lua
@@ -4,10 +4,10 @@
|
||||
local funcdef = "([A-Za-z_][A-Za-z0-9_%.%:]*)%s*"
|
||||
local funccall = "([A-Za-z_][A-Za-z0-9_]*)%s*"
|
||||
local decindent = {
|
||||
['else'] = true, ['elseif'] = true, ['end'] = true}
|
||||
['else'] = true, ['elseif'] = true, ['until'] = true, ['end'] = true}
|
||||
local incindent = {
|
||||
['else'] = true, ['elseif'] = true, ['for'] = true, ['do'] = true,
|
||||
['if'] = true, ['repeat'] = true, ['until'] = true, ['while'] = true}
|
||||
['if'] = true, ['repeat'] = true, ['while'] = true}
|
||||
local function isfndef(str)
|
||||
local l
|
||||
local s,e,cap,par = string.find(str, "function%s+" .. funcdef .. "(%(.-%))")
|
||||
@@ -43,21 +43,25 @@ return {
|
||||
-- this handles three different cases:
|
||||
local term = (str:match("^%s*(%w+)%s*$")
|
||||
or str:match("^%s*(elseif)[%s%(]")
|
||||
or str:match("^%s*(until)[%s%(]")
|
||||
or str:match("^%s*(else)%f[%W]")
|
||||
)
|
||||
-- (1) 'end', 'elseif', 'else'
|
||||
-- (1) 'end', 'elseif', 'else', 'until'
|
||||
local match = term and decindent[term]
|
||||
-- (2) 'end)', 'end}', 'end,', and 'end;'
|
||||
if not term then term, match = str:match("^%s*(end)%s*([%)%}]*)%s*[,;]?") end
|
||||
-- endFoo could be captured as well; filter it out
|
||||
if term and str:match("^%s*(end)%w") then term = nil end
|
||||
-- (3) '},', '};', '),' and ');'
|
||||
if not term then match = str:match("^%s*[%)%}]+%s*[,;]?%s*$") end
|
||||
|
||||
return match and 1 or 0, match and term and 1 or 0
|
||||
end,
|
||||
isincindent = function(str)
|
||||
str = (str:gsub('%-%-%[=*%[.*%]=*%]',''):gsub('%-%-.*','')
|
||||
str = (str:gsub('%-%-%[=*%[.*%]=*%]','')
|
||||
:gsub("'.-\\'","'"):gsub("'.-'","")
|
||||
:gsub('".-\\"','"'):gsub('".-"','')
|
||||
:gsub('%-%-.*','') -- strip comments after strings are processed
|
||||
:gsub("%b()","()") -- remove all function calls
|
||||
)
|
||||
local term = str:match("^%s*(%w+)%W*")
|
||||
@@ -128,16 +132,16 @@ return {
|
||||
|
||||
if (not iscomment[s]) then
|
||||
local tx = editor:GetLine(line)
|
||||
local leftscope
|
||||
local sstart, send
|
||||
|
||||
for i,v in ipairs(scopestart) do
|
||||
if (tx:match("^%s*"..v)) then
|
||||
leftscope = true
|
||||
end
|
||||
for _, v in ipairs(scopestart) do
|
||||
if (tx:match("^%s*"..v.."%f[^%w]")) then sstart = true end
|
||||
end
|
||||
if (leftscope) then
|
||||
break
|
||||
for _, v in ipairs(scopeend) do
|
||||
if (tx:match("%f[%w]"..v.."%s*$")) then send = true end
|
||||
end
|
||||
-- if the scope starts, but doesn't end on one line, stop searching
|
||||
if sstart and not send then break end
|
||||
end
|
||||
line = line -1
|
||||
end
|
||||
|
||||
@@ -254,11 +254,6 @@ local function resolveAssign(editor,tx)
|
||||
c = c..w..s
|
||||
end
|
||||
end
|
||||
-- abort if the same or recursive value is returned; no need to continue.
|
||||
-- this can happen after typing "smth = smth:new(); smth:" or
|
||||
-- "line = line:gsub(...); line:" as the current algorithm attempts to
|
||||
-- replace "line" with the value that also includes "line"
|
||||
if change and c:find("^"..(tx:gsub(anysep,anysep))) then break end
|
||||
tx = c
|
||||
end
|
||||
else
|
||||
|
||||
@@ -16,7 +16,7 @@ local CURRENT_LINE_MARKER_VALUE = 2^CURRENT_LINE_MARKER
|
||||
function NewFile(filename)
|
||||
filename = filename or ide.config.default.fullname
|
||||
local editor = CreateEditor()
|
||||
SetupKeywords(editor, GetFileExt(filename))
|
||||
editor:SetupKeywords(GetFileExt(filename))
|
||||
local doc = AddEditor(editor, filename)
|
||||
if doc then
|
||||
PackageEventHandle("onEditorNew", editor)
|
||||
@@ -71,7 +71,7 @@ function LoadFile(filePath, editor, file_must_exist, skipselection)
|
||||
editor = editor or findUnusedEditor() or CreateEditor()
|
||||
|
||||
editor:Freeze()
|
||||
SetupKeywords(editor, GetFileExt(filePath))
|
||||
editor:SetupKeywords(GetFileExt(filePath))
|
||||
editor:MarkerDeleteAll(-1)
|
||||
|
||||
-- remove BOM from UTF-8 encoded files; store BOM to add back when saving
|
||||
@@ -109,13 +109,16 @@ function LoadFile(filePath, editor, file_must_exist, skipselection)
|
||||
editor:Colourise(0, -1)
|
||||
editor:Thaw()
|
||||
|
||||
local edcfg = ide.config.editor
|
||||
if current then editor:GotoPos(current) end
|
||||
if (file_text and ide.config.editor.autotabs) then
|
||||
local found = string.find(file_text,"\t") ~= nil
|
||||
editor:SetUseTabs(found)
|
||||
if (file_text and edcfg.autotabs) then
|
||||
-- use tabs if they are already used
|
||||
-- or if "usetabs" is set and no space indentation is used in a file
|
||||
editor:SetUseTabs(string.find(file_text, "\t") ~= nil
|
||||
or edcfg.usetabs and (file_text:find("%f[^\r\n] ") or file_text:find("^ ")) == nil)
|
||||
end
|
||||
|
||||
if (file_text and ide.config.editor.checkeol) then
|
||||
if (file_text and edcfg.checkeol) then
|
||||
-- Auto-detect CRLF/LF line-endings
|
||||
local foundcrlf = string.find(file_text,"\r\n") ~= nil
|
||||
local foundlf = (string.find(file_text,"[^\r]\n") ~= nil)
|
||||
@@ -195,6 +198,21 @@ function ReLoadFile(filePath, editor, ...)
|
||||
return editor
|
||||
end
|
||||
|
||||
function ActivateFile(filename)
|
||||
local name, suffix, value = filename:match('(.+):([lLpP]?)(%d+)$')
|
||||
if name and not wx.wxFileExists(filename) and wx.wxFileExists(name) then
|
||||
filename = name
|
||||
end
|
||||
|
||||
local opened = LoadFile(filename, nil, true)
|
||||
if opened and value then
|
||||
if suffix:upper() == 'P' then opened:GotoPosDelayed(tonumber(value))
|
||||
else opened:GotoPosDelayed(opened:PositionFromLine(value-1))
|
||||
end
|
||||
end
|
||||
return opened
|
||||
end
|
||||
|
||||
local function getExtsString()
|
||||
local knownexts = ""
|
||||
for _,spec in pairs(ide.specs) do
|
||||
@@ -323,7 +341,7 @@ function SaveFileAs(editor)
|
||||
if ext ~= GetFileExt(filePath) then
|
||||
-- new extension, so setup new keywords and re-apply indicators
|
||||
editor:ClearDocumentStyle() -- remove styles from the document
|
||||
SetupKeywords(editor, GetFileExt(filePath))
|
||||
editor:SetupKeywords(GetFileExt(filePath))
|
||||
IndicateAll(editor)
|
||||
MarkupStyle(editor)
|
||||
end
|
||||
@@ -539,19 +557,6 @@ function FoldSome()
|
||||
editor:EnsureCaretVisible()
|
||||
end
|
||||
|
||||
function EnsureRangeVisible(posStart, posEnd)
|
||||
local editor = GetEditor()
|
||||
if posStart > posEnd then
|
||||
posStart, posEnd = posEnd, posStart
|
||||
end
|
||||
|
||||
local lineStart = editor:LineFromPosition(posStart)
|
||||
local lineEnd = editor:LineFromPosition(posEnd)
|
||||
for line = lineStart, lineEnd do
|
||||
editor:EnsureVisibleEnforcePolicy(line)
|
||||
end
|
||||
end
|
||||
|
||||
function SetAllEditorsReadOnly(enable)
|
||||
for _, document in pairs(openDocuments) do
|
||||
document.editor:SetReadOnly(enable)
|
||||
@@ -574,14 +579,19 @@ function StripShebang(code) return (code:gsub("^#!.-\n", "\n")) end
|
||||
|
||||
local compileOk, compileTotal = 0, 0
|
||||
function CompileProgram(editor, params)
|
||||
local params = { jumponerror = (params or {}).jumponerror ~= false,
|
||||
reportstats = (params or {}).reportstats ~= false }
|
||||
local id = editor:GetId()
|
||||
local filePath = DebuggerMakeFileName(editor, openDocuments[id].filePath)
|
||||
local params = {
|
||||
jumponerror = (params or {}).jumponerror ~= false,
|
||||
reportstats = (params or {}).reportstats ~= false,
|
||||
keepoutput = (params or {}).keepoutput,
|
||||
}
|
||||
local doc = ide:GetDocument(editor)
|
||||
local filePath = doc:GetFilePath() or doc:GetFileName()
|
||||
local func, err = loadstring(StripShebang(editor:GetText()), '@'..filePath)
|
||||
local line = not func and tonumber(err:match(":(%d+)%s*:")) or nil
|
||||
|
||||
if ide.frame.menuBar:IsChecked(ID_CLEAROUTPUT) then ClearOutput() end
|
||||
if not params.keepoutput and ide.frame.menuBar:IsChecked(ID_CLEAROUTPUT) then
|
||||
ClearOutput()
|
||||
end
|
||||
|
||||
compileTotal = compileTotal + 1
|
||||
if func then
|
||||
@@ -723,19 +733,27 @@ function SetOpenTabs(params)
|
||||
DisplayOutputLn(TR("Can't process auto-recovery record; invalid format: %s."):format(nametab))
|
||||
return
|
||||
end
|
||||
DisplayOutputLn(TR("Found auto-recovery record and restored saved session."))
|
||||
if not params.quiet then
|
||||
DisplayOutputLn(TR("Found auto-recovery record and restored saved session."))
|
||||
end
|
||||
for _,doc in ipairs(nametab) do
|
||||
local editor = doc.filename and LoadFile(doc.filename,nil,true,true) or NewFile()
|
||||
local opendoc = openDocuments[editor:GetId()]
|
||||
if doc.content then
|
||||
notebook:SetPageText(opendoc.index, doc.tabname)
|
||||
editor:SetText(doc.content)
|
||||
if doc.filename and opendoc.modTime and doc.modified < opendoc.modTime:GetTicks() then
|
||||
DisplayOutputLn(TR("File '%s' has more recent timestamp than restored '%s'; please review before saving.")
|
||||
:format(doc.filename, doc.tabname))
|
||||
-- check for missing file is no content is stored
|
||||
if doc.filepath and not doc.content and not wx.wxFileExists(doc.filepath) then
|
||||
DisplayOutputLn(TR("File '%s' is missing and can't be recovered.")
|
||||
:format(doc.filepath))
|
||||
else
|
||||
local editor = doc.filepath and LoadFile(doc.filepath,nil,true,true) or NewFile(doc.filename)
|
||||
local opendoc = openDocuments[editor:GetId()]
|
||||
if doc.content then
|
||||
editor:SetText(doc.content)
|
||||
if doc.filepath and opendoc.modTime and doc.modified < opendoc.modTime:GetTicks() then
|
||||
DisplayOutputLn(TR("File '%s' has more recent timestamp than restored '%s'; please review before saving.")
|
||||
:format(doc.filepath, doc.tabname))
|
||||
end
|
||||
SetDocumentModified(editor:GetId(), true)
|
||||
end
|
||||
editor:GotoPosDelayed(doc.cursorpos or 0)
|
||||
end
|
||||
editor:GotoPosDelayed(doc.cursorpos or 0)
|
||||
end
|
||||
notebook:SetSelection(params and params.index or 0)
|
||||
SetEditorSelection()
|
||||
@@ -745,7 +763,8 @@ local function getOpenTabs()
|
||||
local opendocs = {}
|
||||
for _, document in pairs(ide.openDocuments) do
|
||||
table.insert(opendocs, {
|
||||
filename = document.filePath,
|
||||
filename = document.fileName,
|
||||
filepath = document.filePath,
|
||||
tabname = notebook:GetPageText(document.index),
|
||||
modified = document.modTime and document.modTime:GetTicks(), -- get number of seconds
|
||||
content = document.isModified and document.editor:GetText() or nil,
|
||||
@@ -764,18 +783,34 @@ function SetAutoRecoveryMark()
|
||||
ide.session.lastupdated = os.time()
|
||||
end
|
||||
|
||||
local function saveAutoRecovery(event)
|
||||
local function generateRecoveryRecord(opentabs)
|
||||
return require('mobdebug').line(opentabs, {comment = false})
|
||||
end
|
||||
|
||||
local function saveHotExit()
|
||||
local opentabs, params = getOpenTabs()
|
||||
if #opentabs > 0 then
|
||||
params.recovery = generateRecoveryRecord(opentabs)
|
||||
params.quiet = true
|
||||
SettingsSaveFileSession({}, params)
|
||||
end
|
||||
end
|
||||
|
||||
local function saveAutoRecovery(force)
|
||||
if not ide.config.autorecoverinactivity then return end
|
||||
|
||||
local lastupdated = ide.session.lastupdated
|
||||
if not ide.config.autorecoverinactivity or not lastupdated then return end
|
||||
if lastupdated < (ide.session.lastsaved or 0) then return end
|
||||
if not force then
|
||||
if not lastupdated or lastupdated < (ide.session.lastsaved or 0) then return end
|
||||
end
|
||||
|
||||
local now = os.time()
|
||||
if lastupdated + ide.config.autorecoverinactivity > now then return end
|
||||
if not force and lastupdated + ide.config.autorecoverinactivity > now then return end
|
||||
|
||||
-- find all open modified files and save them
|
||||
local opentabs, params = getOpenTabs()
|
||||
if #opentabs > 0 then
|
||||
params.recovery = require('mobdebug').line(opentabs, {comment = false})
|
||||
params.recovery = generateRecoveryRecord(opentabs)
|
||||
SettingsSaveAll()
|
||||
SettingsSaveFileSession({}, params)
|
||||
ide.settings:Flush()
|
||||
@@ -870,7 +905,7 @@ local function closeWindow(event)
|
||||
|
||||
ide.exitingProgram = true -- don't handle focus events
|
||||
|
||||
if not SaveOnExit(event:CanVeto()) then
|
||||
if not ide.config.hotexit and not SaveOnExit(event:CanVeto()) then
|
||||
event:Veto()
|
||||
ide.exitingProgram = false
|
||||
return
|
||||
@@ -887,6 +922,7 @@ local function closeWindow(event)
|
||||
DebuggerShutdown()
|
||||
|
||||
SettingsSaveAll()
|
||||
if ide.config.hotexit then saveHotExit() end
|
||||
ide.settings:Flush()
|
||||
|
||||
do -- hide all floating panes first
|
||||
@@ -906,7 +942,7 @@ local function closeWindow(event)
|
||||
end
|
||||
frame:Connect(wx.wxEVT_CLOSE_WINDOW, closeWindow)
|
||||
|
||||
frame:Connect(wx.wxEVT_TIMER, saveAutoRecovery)
|
||||
frame:Connect(wx.wxEVT_TIMER, function() saveAutoRecovery() end)
|
||||
|
||||
-- in the presence of wxAuiToolbar, when (1) the app gets focus,
|
||||
-- (2) a floating panel is closed or (3) a toolbar dropdown is closed,
|
||||
@@ -971,6 +1007,9 @@ ide.editorApp:Connect(wx.wxEVT_ACTIVATE_APP,
|
||||
pcall(function() infocus:SetFocus() end)
|
||||
end
|
||||
|
||||
-- save auto-recovery record when making the app inactive
|
||||
if not event:GetActive() then saveAutoRecovery(true) end
|
||||
|
||||
local event = event:GetActive() and "onAppFocusSet" or "onAppFocusLost"
|
||||
PackageEventHandle(event, ide.editorApp)
|
||||
end
|
||||
|
||||
@@ -24,6 +24,18 @@ debugger.hostname = ide.config.debugger.hostname or (function()
|
||||
return hostname and socket.dns.toip(hostname) and hostname or "localhost"
|
||||
end)()
|
||||
|
||||
local image = { STACK = 0, LOCAL = 1, UPVALUE = 2 }
|
||||
|
||||
do
|
||||
local getBitmap = (ide.app.createbitmap or wx.wxArtProvider.GetBitmap)
|
||||
local size = wx.wxSize(16,16)
|
||||
local imglist = wx.wxImageList(16,16)
|
||||
imglist:Add(getBitmap("GO-FORWARD", "OTHER", size)) -- 0 = stack call
|
||||
imglist:Add(getBitmap("LIST-VIEW", "OTHER", size)) -- 1 = local variables
|
||||
imglist:Add(getBitmap("REPORT-VIEW", "OTHER", size)) -- 2 = upvalues
|
||||
debugger.imglist = imglist
|
||||
end
|
||||
|
||||
local notebook = ide.frame.notebook
|
||||
|
||||
local CURRENT_LINE_MARKER = StylesGetMarker("currentline")
|
||||
@@ -58,7 +70,7 @@ end
|
||||
|
||||
local q = EscapeMagic
|
||||
|
||||
local function updateWatchesSync(num)
|
||||
local function updateWatchesSync(onlyitem)
|
||||
local watchCtrl = debugger.watchCtrl
|
||||
local pane = ide.frame.uimgr:GetPane("watchpanel")
|
||||
local shown = watchCtrl and (pane:IsOk() and pane:IsShown() or not pane:IsOk() and watchCtrl:IsShown())
|
||||
@@ -67,43 +79,55 @@ local function updateWatchesSync(num)
|
||||
local bgcl = watchCtrl:GetBackgroundColour()
|
||||
local hicl = wx.wxColour(math.floor(bgcl:Red()*.9),
|
||||
math.floor(bgcl:Green()*.9), math.floor(bgcl:Blue()*.9))
|
||||
for idx = 0, watchCtrl:GetItemCount() - 1 do
|
||||
if not num or idx == num then
|
||||
local expression = watchCtrl:GetItemText(idx)
|
||||
local _, values, error = debugger.evaluate(expression)
|
||||
if error then error = error:gsub("%[.-%]:%d+:%s+","")
|
||||
elseif #values == 0 then values = {'nil'} end
|
||||
|
||||
local newval = error and ('error: '..error) or values[1]
|
||||
-- get the current value from a list item
|
||||
do local litem = wx.wxListItem()
|
||||
litem:SetMask(wx.wxLIST_MASK_TEXT)
|
||||
litem:SetId(idx)
|
||||
litem:SetColumn(1)
|
||||
watchCtrl:GetItem(litem)
|
||||
watchCtrl:SetItemBackgroundColour(idx,
|
||||
watchCtrl:GetItem(litem) and newval ~= litem:GetText()
|
||||
and hicl or bgcl)
|
||||
local root = watchCtrl:GetRootItem()
|
||||
if not root or not root:IsOk() then return end
|
||||
|
||||
local item = onlyitem or watchCtrl:GetFirstChild(root)
|
||||
while true do
|
||||
if not item:IsOk() then break end
|
||||
|
||||
local expression = watchCtrl:GetItemExpression(item)
|
||||
if expression then
|
||||
local _, values, error = debugger.evaluate(expression)
|
||||
local curchildren = watchCtrl:GetItemChildren(item)
|
||||
if error then
|
||||
error = error:gsub("%[.-%]:%d+:%s+","")
|
||||
watchCtrl:SetItemValueIfExpandable(item, nil)
|
||||
else
|
||||
if #values == 0 then values = {'nil'} end
|
||||
local ok, res = LoadSafe("return "..values[1])
|
||||
watchCtrl:SetItemValueIfExpandable(item, res)
|
||||
end
|
||||
|
||||
watchCtrl:SetItem(idx, 1, newval)
|
||||
local newval = (expression .. ' = '
|
||||
.. (error and ('error: '..error) or table.concat(values, ", ")))
|
||||
local val = watchCtrl:GetItemText(item)
|
||||
|
||||
watchCtrl:SetItemBackgroundColour(item, val ~= newval and hicl or bgcl)
|
||||
watchCtrl:SetItemText(item, newval)
|
||||
|
||||
if onlyitem or val ~= newval then
|
||||
local newchildren = watchCtrl:GetItemChildren(item)
|
||||
if next(curchildren) ~= nil and next(newchildren) == nil then
|
||||
watchCtrl:SetItemHasChildren(item, true)
|
||||
watchCtrl:CollapseAndReset(item)
|
||||
watchCtrl:SetItemHasChildren(item, false)
|
||||
elseif next(curchildren) ~= nil and next(newchildren) ~= nil then
|
||||
watchCtrl:CollapseAndReset(item)
|
||||
watchCtrl:Expand(item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if onlyitem then break end
|
||||
item = watchCtrl:GetNextSibling(item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local simpleType = {['nil'] = true, ['string'] = true, ['number'] = true, ['boolean'] = true}
|
||||
local stackItemValue = {}
|
||||
local callData = {}
|
||||
local function checkIfExpandable(value, item)
|
||||
local expandable = type(value) == 'table' and next(value) ~= nil
|
||||
and not stackItemValue[value] -- only expand first time
|
||||
if expandable then -- cache table value to expand when requested
|
||||
stackItemValue[item:GetValue()] = value
|
||||
stackItemValue[value] = item:GetValue() -- to avoid circular refs
|
||||
end
|
||||
return expandable
|
||||
end
|
||||
|
||||
local function updateStackSync()
|
||||
local stackCtrl = debugger.stackCtrl
|
||||
@@ -113,17 +137,16 @@ local function updateStackSync()
|
||||
and not debugger.scratchpad then
|
||||
local stack, _, err = debugger.stack()
|
||||
if not stack or #stack == 0 then
|
||||
stackCtrl:DeleteAllItems()
|
||||
stackCtrl:DeleteAll()
|
||||
if err then -- report an error if any
|
||||
stackCtrl:AppendItem(stackCtrl:AddRoot("Stack"), "Error: " .. err, 0)
|
||||
stackCtrl:AppendItem(stackCtrl:AddRoot("Stack"), "Error: " .. err, image.STACK)
|
||||
end
|
||||
return
|
||||
end
|
||||
stackCtrl:Freeze()
|
||||
stackCtrl:DeleteAllItems()
|
||||
stackCtrl:DeleteAll()
|
||||
|
||||
local root = stackCtrl:AddRoot("Stack")
|
||||
stackItemValue = {} -- reset cache of items in the stack
|
||||
callData = {} -- reset call cache
|
||||
for _,frame in ipairs(stack) do
|
||||
-- "main chunk at line 24"
|
||||
@@ -146,7 +169,7 @@ local function updateStackSync()
|
||||
or " (defined in "..call[7]..")"))
|
||||
|
||||
-- create the new tree item for this level of the call stack
|
||||
local callitem = stackCtrl:AppendItem(root, text, 0)
|
||||
local callitem = stackCtrl:AppendItem(root, text, image.STACK)
|
||||
|
||||
-- register call data to provide stack navigation
|
||||
callData[callitem:GetValue()] = { call[2], call[4] }
|
||||
@@ -162,10 +185,8 @@ local function updateStackSync()
|
||||
local text = ("%s = %s%s"):
|
||||
format(name, fixUTF8(trimToMaxLength(serialize(value, params))),
|
||||
simpleType[type(value)] and "" or (" --[["..comment.."]]"))
|
||||
local item = stackCtrl:AppendItem(callitem, text, 1)
|
||||
if checkIfExpandable(value, item) then
|
||||
stackCtrl:SetItemHasChildren(item, true)
|
||||
end
|
||||
local item = stackCtrl:AppendItem(callitem, text, image.LOCAL)
|
||||
stackCtrl:SetItemValueIfExpandable(item, value)
|
||||
end
|
||||
|
||||
-- add the upvalues for this call stack level to the tree item
|
||||
@@ -174,10 +195,8 @@ local function updateStackSync()
|
||||
local text = ("%s = %s%s"):
|
||||
format(name, fixUTF8(trimToMaxLength(serialize(value, params))),
|
||||
simpleType[type(value)] and "" or (" --[["..comment.."]]"))
|
||||
local item = stackCtrl:AppendItem(callitem, text, 2)
|
||||
if checkIfExpandable(value, item) then
|
||||
stackCtrl:SetItemHasChildren(item, true)
|
||||
end
|
||||
local item = stackCtrl:AppendItem(callitem, text, image.UPVALUE)
|
||||
stackCtrl:SetItemValueIfExpandable(item, value)
|
||||
end
|
||||
|
||||
stackCtrl:SortChildren(callitem)
|
||||
@@ -198,12 +217,12 @@ local function updateStackAndWatches()
|
||||
end
|
||||
end
|
||||
|
||||
local function updateWatches(num)
|
||||
local function updateWatches(item)
|
||||
-- check if the debugger is running and may be waiting for a response.
|
||||
-- allow that request to finish, otherwise updateWatchesSync() does nothing.
|
||||
if debugger.running then debugger.update() end
|
||||
if debugger.server and not debugger.running then
|
||||
copas.addthread(function() updateWatchesSync(num) end)
|
||||
copas.addthread(function() updateWatchesSync(item) end)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -430,6 +449,8 @@ debugger.shell = function(expression, isstatement)
|
||||
updateStackSync() updateWatchesSync()
|
||||
end
|
||||
end)
|
||||
elseif debugger.server then
|
||||
DisplayShellErr(TR("Can't evaluate the expression while the application is running."))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -443,6 +464,46 @@ local function stoppedAtBreakpoint(file, line)
|
||||
return breakpoint > -1 and breakpoint == current
|
||||
end
|
||||
|
||||
local function mapRemotePath(basedir, file, line, method)
|
||||
if not file then return end
|
||||
|
||||
-- file is /foo/bar/my.lua; basedir is d:\local\path\
|
||||
-- check for d:\local\path\my.lua, d:\local\path\bar\my.lua, ...
|
||||
-- wxwidgets on Windows handles \\ and / as separators, but on OSX
|
||||
-- and Linux it only handles 'native' separator;
|
||||
-- need to translate for GetDirs to work.
|
||||
local file = file:gsub("\\", "/")
|
||||
local parts = wx.wxFileName(file):GetDirs()
|
||||
local name = wx.wxFileName(file):GetFullName()
|
||||
|
||||
-- find the longest remote path that can be mapped locally
|
||||
local longestpath, remotedir
|
||||
while true do
|
||||
local mapped = GetFullPathIfExists(basedir, name)
|
||||
if mapped then
|
||||
longestpath = mapped
|
||||
remotedir = file:gsub(q(name):gsub("/", ".").."$", "")
|
||||
end
|
||||
if #parts == 0 then break end
|
||||
name = table.remove(parts, #parts) .. "/" .. name
|
||||
end
|
||||
|
||||
-- if found a local mapping under basedir
|
||||
local activated = longestpath and activateDocument(longestpath, line, method or activate.NOREPORT)
|
||||
if activated then
|
||||
-- find remote basedir by removing the tail from remote file
|
||||
debugger.handle("basedir " .. debugger.basedir .. "\t" .. remotedir)
|
||||
-- reset breakpoints again as remote basedir has changed
|
||||
reSetBreakpoints()
|
||||
DisplayOutputLn(TR("Mapped remote request for '%s' to '%s'.")
|
||||
:format(remotedir, debugger.basedir))
|
||||
|
||||
return longestpath
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
debugger.listen = function(start)
|
||||
if start == false then
|
||||
if debugger.listening then
|
||||
@@ -583,8 +644,11 @@ debugger.listen = function(start)
|
||||
..":\n"..err)
|
||||
return debugger.terminate()
|
||||
elseif options.runstart then
|
||||
if stoppedAtBreakpoint(file or startfile, line or 0) then
|
||||
activateDocument(file or startfile, line or 0)
|
||||
local file = (mapRemotePath(basedir, file, line or 0, activate.CHECKONLY)
|
||||
or file or startfile)
|
||||
|
||||
if stoppedAtBreakpoint(file, line or 0) then
|
||||
activateDocument(file, line or 0)
|
||||
options.runstart = false
|
||||
end
|
||||
elseif file and line then
|
||||
@@ -607,36 +671,8 @@ debugger.listen = function(start)
|
||||
-- when autoactivation is disabled.
|
||||
if not activated and (not wx.wxFileName(file):FileExists()
|
||||
or wx.wxIsAbsolutePath(file)) then
|
||||
-- file is /foo/bar/my.lua; basedir is d:\local\path\
|
||||
-- check for d:\local\path\my.lua, d:\local\path\bar\my.lua, ...
|
||||
-- wxwidgets on Windows handles \\ and / as separators, but on OSX
|
||||
-- and Linux it only handles 'native' separator;
|
||||
-- need to translate for GetDirs to work.
|
||||
local file = file:gsub("\\", "/")
|
||||
local parts = wx.wxFileName(file):GetDirs()
|
||||
local name = wx.wxFileName(file):GetFullName()
|
||||
|
||||
-- find the longest remote path that can be mapped locally
|
||||
local longestpath, remotedir
|
||||
while true do
|
||||
local mapped = GetFullPathIfExists(basedir, name)
|
||||
if mapped then
|
||||
longestpath = mapped
|
||||
remotedir = file:gsub(q(name):gsub("/", ".").."$", "")
|
||||
end
|
||||
if #parts == 0 then break end
|
||||
name = table.remove(parts, #parts) .. "/" .. name
|
||||
end
|
||||
|
||||
-- if found a local mapping under basedir
|
||||
activated = longestpath and activateDocument(longestpath, line, activate.NOREPORT)
|
||||
if activated then
|
||||
-- find remote basedir by removing the tail from remote file
|
||||
debugger.handle("basedir " .. debugger.basedir .. "\t" .. remotedir)
|
||||
-- reset breakpoints again as remote basedir has changed
|
||||
reSetBreakpoints()
|
||||
DisplayOutputLn(TR("Mapped remote request for '%s' to '%s'.")
|
||||
:format(remotedir, debugger.basedir))
|
||||
if mapRemotePath(basedir, file, line, activate.NOREPORT) then
|
||||
activated = true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -681,6 +717,12 @@ debugger.listen = function(start)
|
||||
debugger.listening = server
|
||||
end
|
||||
|
||||
local function nameOutputTab(name)
|
||||
local nbk = ide.frame.bottomnotebook
|
||||
local index = nbk:GetPageIndex(ide:GetOutput())
|
||||
if index then nbk:SetPageText(index, name) end
|
||||
end
|
||||
|
||||
debugger.handle = function(command, server, options)
|
||||
local verbose = ide.config.debugger.verbose
|
||||
local osexit, gprint
|
||||
@@ -689,11 +731,14 @@ debugger.handle = function(command, server, options)
|
||||
if verbose then DisplayOutputLn(...) end
|
||||
end
|
||||
|
||||
nameOutputTab(TR("Output (running)"))
|
||||
debugger.running = true
|
||||
if verbose then DisplayOutputLn("Debugger sent (command):", command) end
|
||||
local file, line, err = mobdebug.handle(command, server or debugger.server, options)
|
||||
if verbose then DisplayOutputLn("Debugger received (file, line, err):", file, line, err) end
|
||||
debugger.running = false
|
||||
-- only set suspended if the debugging hasn't been terminated
|
||||
if debugger.server then nameOutputTab(TR("Output (suspended)")) end
|
||||
|
||||
os.exit = osexit
|
||||
_G.print = gprint
|
||||
@@ -893,58 +938,56 @@ debugger.quickeval = function(var, callback)
|
||||
end
|
||||
end
|
||||
|
||||
-- need imglist to be a file local variable as SetImageList takes ownership
|
||||
-- of it and if done inside a function, icons do not work as expected
|
||||
local imglist = wx.wxImageList(16,16)
|
||||
do
|
||||
local getBitmap = (ide.app.createbitmap or wx.wxArtProvider.GetBitmap)
|
||||
local size = wx.wxSize(16,16)
|
||||
-- 0 = stack call
|
||||
imglist:Add(getBitmap(wx.wxART_GO_FORWARD, wx.wxART_OTHER, size))
|
||||
-- 1 = local variables
|
||||
imglist:Add(getBitmap(wx.wxART_LIST_VIEW, wx.wxART_OTHER, size))
|
||||
-- 2 = upvalues
|
||||
imglist:Add(getBitmap(wx.wxART_REPORT_VIEW, wx.wxART_OTHER, size))
|
||||
function DebuggerAddStackWindow()
|
||||
return ide:AddPanel(debugger.stackCtrl, "stackpanel", TR("Stack"))
|
||||
end
|
||||
|
||||
function DebuggerAddWatchWindow()
|
||||
return ide:AddPanel(debugger.watchCtrl, "watchpanel", TR("Watch"))
|
||||
end
|
||||
|
||||
local width, height = 360, 200
|
||||
|
||||
function debuggerAddWindow(ctrl, panel, name)
|
||||
local notebook = wxaui.wxAuiNotebook(ide.frame, wx.wxID_ANY,
|
||||
wx.wxDefaultPosition, wx.wxDefaultSize,
|
||||
wxaui.wxAUI_NB_DEFAULT_STYLE + wxaui.wxAUI_NB_TAB_EXTERNAL_MOVE
|
||||
- wxaui.wxAUI_NB_CLOSE_ON_ACTIVE_TAB + wx.wxNO_BORDER)
|
||||
notebook:AddPage(ctrl, name, true)
|
||||
notebook:Connect(wxaui.wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK,
|
||||
function() PaneFloatToggle(notebook) end)
|
||||
local keyword = {}
|
||||
for _,k in ipairs({'and', 'break', 'do', 'else', 'elseif', 'end', 'false',
|
||||
'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat',
|
||||
'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end
|
||||
|
||||
local mgr = ide.frame.uimgr
|
||||
mgr:AddPane(notebook, wxaui.wxAuiPaneInfo():
|
||||
Name(panel):Float():CaptionVisible(false):PaneBorder(false):
|
||||
MinSize(width/2,height/2):
|
||||
BestSize(width,height):FloatingSize(width,height):
|
||||
PinButton(true):Hide())
|
||||
mgr.defaultPerspective = mgr:SavePerspective() -- resave default perspective
|
||||
|
||||
return notebook
|
||||
local function stringifyKeyIntoPrefix(name, num)
|
||||
return (type(name) == "number"
|
||||
and (num and num == name and '' or ("[%s] = "):format(name))
|
||||
or type(name) == "string" and (name:match("^[%l%u_][%w_]*$") and not keyword[name]
|
||||
and ("%s = "):format(name)
|
||||
or ("[%q] = "):format(name))
|
||||
or ("[%s] = "):format(tostring(name)))
|
||||
end
|
||||
|
||||
function DebuggerAddStackWindow()
|
||||
return debuggerAddWindow(debugger.stackCtrl, "stackpanel", TR("Stack"))
|
||||
end
|
||||
|
||||
function DebuggerAddWatchWindow()
|
||||
return debuggerAddWindow(debugger.watchCtrl, "watchpanel", TR("Watch"))
|
||||
end
|
||||
|
||||
function debuggerCreateStackWindow()
|
||||
local function debuggerCreateStackWindow()
|
||||
local stackCtrl = wx.wxTreeCtrl(ide.frame, wx.wxID_ANY,
|
||||
wx.wxDefaultPosition, wx.wxSize(width, height),
|
||||
wx.wxTR_LINES_AT_ROOT + wx.wxTR_HAS_BUTTONS + wx.wxTR_SINGLE + wx.wxTR_HIDE_ROOT)
|
||||
|
||||
debugger.stackCtrl = stackCtrl
|
||||
|
||||
stackCtrl:SetImageList(imglist)
|
||||
stackCtrl:SetImageList(debugger.imglist)
|
||||
|
||||
local valuecache = {}
|
||||
function stackCtrl:SetItemValueIfExpandable(item, value)
|
||||
local expandable = type(value) == 'table' and next(value) ~= nil
|
||||
if expandable then -- cache table value to expand when requested
|
||||
valuecache[item:GetValue()] = value
|
||||
end
|
||||
self:SetItemHasChildren(item, expandable)
|
||||
end
|
||||
|
||||
function stackCtrl:DeleteAll()
|
||||
self:DeleteAllItems()
|
||||
valuecache = {}
|
||||
end
|
||||
|
||||
function stackCtrl:GetItemChildren(item)
|
||||
return valuecache[item:GetValue()] or {}
|
||||
end
|
||||
|
||||
stackCtrl:Connect(wx.wxEVT_COMMAND_TREE_ITEM_EXPANDING,
|
||||
function (event)
|
||||
@@ -954,15 +997,12 @@ function debuggerCreateStackWindow()
|
||||
|
||||
local image = stackCtrl:GetItemImage(item_id)
|
||||
local num = 1
|
||||
for name,value in pairs(stackItemValue[item_id:GetValue()]) do
|
||||
for name,value in pairs(stackCtrl:GetItemChildren(item_id)) do
|
||||
local strval = fixUTF8(trimToMaxLength(serialize(value, params)))
|
||||
local text = type(name) == "number"
|
||||
and (num == name and strval or ("[%s] = %s"):format(name, strval))
|
||||
or ("%s = %s"):format(tostring(name), strval)
|
||||
local text = stringifyKeyIntoPrefix(name, num)..strval
|
||||
local item = stackCtrl:AppendItem(item_id, text, image)
|
||||
if checkIfExpandable(value, item) then
|
||||
stackCtrl:SetItemHasChildren(item, true)
|
||||
end
|
||||
stackCtrl:SetItemValueIfExpandable(item, value)
|
||||
|
||||
num = num + 1
|
||||
if num > stackmaxnum then break end
|
||||
end
|
||||
@@ -996,84 +1036,193 @@ function debuggerCreateStackWindow()
|
||||
end
|
||||
|
||||
local function debuggerCreateWatchWindow()
|
||||
local watchCtrl = wx.wxListCtrl(ide.frame, wx.wxID_ANY,
|
||||
wx.wxDefaultPosition, wx.wxDefaultSize,
|
||||
wx.wxLC_REPORT + wx.wxLC_EDIT_LABELS)
|
||||
local watchCtrl = wx.wxTreeCtrl(ide.frame, wx.wxID_ANY,
|
||||
wx.wxDefaultPosition, wx.wxSize(width, height),
|
||||
wx.wxTR_LINES_AT_ROOT + wx.wxTR_HAS_BUTTONS + wx.wxTR_SINGLE
|
||||
+ wx.wxTR_HIDE_ROOT + wx.wxTR_EDIT_LABELS)
|
||||
|
||||
debugger.watchCtrl = watchCtrl
|
||||
|
||||
local info = wx.wxListItem()
|
||||
info:SetMask(wx.wxLIST_MASK_TEXT + wx.wxLIST_MASK_WIDTH)
|
||||
info:SetText(TR("Expression"))
|
||||
info:SetWidth(width * 0.32)
|
||||
watchCtrl:InsertColumn(0, info)
|
||||
local root = watchCtrl:AddRoot("Watch")
|
||||
watchCtrl:SetImageList(debugger.imglist)
|
||||
|
||||
info:SetText(TR("Value"))
|
||||
info:SetWidth(width * 0.56)
|
||||
watchCtrl:InsertColumn(1, info)
|
||||
local defaultExpr = "watch expression"
|
||||
local expressions = {} -- table to keep track of expressions
|
||||
|
||||
local watchMenu = wx.wxMenu {
|
||||
{ ID_ADDWATCH, TR("&Add Watch")..KSC(ID_ADDWATCH) },
|
||||
{ ID_EDITWATCH, TR("&Edit Watch")..KSC(ID_EDITWATCH) },
|
||||
{ ID_DELETEWATCH, TR("&Delete Watch")..KSC(ID_DELETEWATCH) },
|
||||
}
|
||||
function watchCtrl:SetItemExpression(item, expr, value)
|
||||
expressions[item:GetValue()] = expr
|
||||
self:SetItemText(item, expr .. ' = ' .. (value or '?'))
|
||||
self:SelectItem(item, true)
|
||||
if not value then updateWatches(item) end
|
||||
end
|
||||
|
||||
local function findSelectedWatchItem()
|
||||
local count = watchCtrl:GetSelectedItemCount()
|
||||
if count > 0 then
|
||||
for idx = 0, watchCtrl:GetItemCount() - 1 do
|
||||
if watchCtrl:GetItemState(idx, wx.wxLIST_STATE_FOCUSED) ~= 0 then
|
||||
return idx
|
||||
end
|
||||
end
|
||||
function watchCtrl:GetItemExpression(item)
|
||||
return expressions[item:GetValue()]
|
||||
end
|
||||
|
||||
local names = {}
|
||||
function watchCtrl:SetItemName(item, name)
|
||||
local nametype = type(name)
|
||||
names[item:GetValue()] = (
|
||||
(nametype == 'string' or nametype == 'number' or nametype == 'boolean')
|
||||
and name or nil
|
||||
)
|
||||
end
|
||||
|
||||
function watchCtrl:GetItemName(item)
|
||||
return names[item:GetValue()]
|
||||
end
|
||||
|
||||
local valuecache = {}
|
||||
function watchCtrl:SetItemValueIfExpandable(item, value)
|
||||
local expandable = type(value) == 'table' and next(value) ~= nil
|
||||
valuecache[item:GetValue()] = expandable and value or nil
|
||||
self:SetItemHasChildren(item, expandable)
|
||||
end
|
||||
|
||||
function watchCtrl:GetItemChildren(item)
|
||||
return valuecache[item:GetValue()] or {}
|
||||
end
|
||||
|
||||
function watchCtrl:IsWatch(item)
|
||||
return item:IsOk() and watchCtrl:GetItemParent(item):GetValue() == root:GetValue()
|
||||
end
|
||||
|
||||
function watchCtrl:IsEditable(item)
|
||||
return (item and item:IsOk()
|
||||
and (watchCtrl:IsWatch(item) or watchCtrl:GetItemName(item) ~= nil))
|
||||
end
|
||||
|
||||
function watchCtrl:UpdateItemValue(item, value)
|
||||
local expr = ''
|
||||
local origitem = item
|
||||
while true do
|
||||
local name = watchCtrl:GetItemName(item)
|
||||
expr = (watchCtrl:IsWatch(item)
|
||||
and ('({%s})[1]'):format(watchCtrl:GetItemExpression(item))
|
||||
or (type(name) == 'string' and '[%q]' or '[%s]'):format(tostring(name))
|
||||
)..expr
|
||||
if watchCtrl:IsWatch(item) then break end
|
||||
item = watchCtrl:GetItemParent(item)
|
||||
if not item:IsOk() then break end
|
||||
end
|
||||
|
||||
if debugger.running then debugger.update() end
|
||||
if debugger.server and not debugger.running
|
||||
and (not debugger.scratchpad or debugger.scratchpad.paused) then
|
||||
copas.addthread(function ()
|
||||
local _, _, err = debugger.execute(expr..'='..value)
|
||||
if err then
|
||||
watchCtrl:SetItemText(origitem, 'error: '..err:gsub("%[.-%]:%d+:%s+",""))
|
||||
else
|
||||
updateWatchesSync(item)
|
||||
end
|
||||
updateStackSync()
|
||||
end)
|
||||
end
|
||||
return -1
|
||||
end
|
||||
|
||||
local defaultExpr = ""
|
||||
local function addWatch()
|
||||
local row = watchCtrl:InsertItem(watchCtrl:GetItemCount(), TR("Expr"))
|
||||
watchCtrl:SetItem(row, 0, defaultExpr)
|
||||
watchCtrl:SetItem(row, 1, TR("Value"))
|
||||
watchCtrl:EditLabel(row)
|
||||
end
|
||||
|
||||
local function editWatch()
|
||||
local row = findSelectedWatchItem()
|
||||
if row >= 0 then watchCtrl:EditLabel(row) end
|
||||
end
|
||||
|
||||
local function deleteWatch()
|
||||
local row = findSelectedWatchItem()
|
||||
if row >= 0 then watchCtrl:DeleteItem(row) end
|
||||
end
|
||||
|
||||
watchCtrl:Connect(wx.wxEVT_CONTEXT_MENU,
|
||||
function (event) watchCtrl:PopupMenu(watchMenu) end)
|
||||
|
||||
watchCtrl:Connect(ID_ADDWATCH, wx.wxEVT_COMMAND_MENU_SELECTED, addWatch)
|
||||
|
||||
watchCtrl:Connect(ID_EDITWATCH, wx.wxEVT_COMMAND_MENU_SELECTED, editWatch)
|
||||
watchCtrl:Connect(ID_EDITWATCH, wx.wxEVT_UPDATE_UI,
|
||||
function (event) event:Enable(watchCtrl:GetSelectedItemCount() > 0) end)
|
||||
|
||||
watchCtrl:Connect(ID_DELETEWATCH, wx.wxEVT_COMMAND_MENU_SELECTED, deleteWatch)
|
||||
watchCtrl:Connect(ID_DELETEWATCH, wx.wxEVT_UPDATE_UI,
|
||||
function (event) event:Enable(watchCtrl:GetSelectedItemCount() > 0) end)
|
||||
|
||||
watchCtrl:Connect(wx.wxEVT_COMMAND_LIST_ITEM_ACTIVATED,
|
||||
function (event) watchCtrl:EditLabel(event:GetIndex()) end)
|
||||
|
||||
watchCtrl:Connect(wx.wxEVT_COMMAND_LIST_END_LABEL_EDIT,
|
||||
watchCtrl:Connect(wx.wxEVT_COMMAND_TREE_ITEM_EXPANDING,
|
||||
function (event)
|
||||
local row = event:GetIndex()
|
||||
local item_id = event:GetItem()
|
||||
local count = watchCtrl:GetChildrenCount(item_id, false)
|
||||
if count > 0 then return true end
|
||||
|
||||
local image = watchCtrl:GetItemImage(item_id)
|
||||
local num = 1
|
||||
for name,value in pairs(watchCtrl:GetItemChildren(item_id)) do
|
||||
local strval = fixUTF8(trimToMaxLength(serialize(value, params)))
|
||||
local text = stringifyKeyIntoPrefix(name, num)..strval
|
||||
local item = watchCtrl:AppendItem(item_id, text, image)
|
||||
watchCtrl:SetItemValueIfExpandable(item, value)
|
||||
watchCtrl:SetItemName(item, name)
|
||||
|
||||
num = num + 1
|
||||
if num > stackmaxnum then break end
|
||||
end
|
||||
return true
|
||||
end)
|
||||
|
||||
watchCtrl:Connect(wx.wxEVT_COMMAND_TREE_DELETE_ITEM,
|
||||
function (event)
|
||||
local value = event:GetItem():GetValue()
|
||||
expressions[value] = nil
|
||||
valuecache[value] = nil
|
||||
names[value] = nil
|
||||
end)
|
||||
|
||||
local item
|
||||
-- wx.wxEVT_CONTEXT_MENU is only triggered over tree items on OSX,
|
||||
-- but it needs to be also triggered below any item to add a watch,
|
||||
-- so use RIGHT_DOWN instead
|
||||
watchCtrl:Connect(wx.wxEVT_RIGHT_DOWN,
|
||||
function (event)
|
||||
-- store the item to be used in edit/delete actions
|
||||
item = watchCtrl:HitTest(watchCtrl:ScreenToClient(wx.wxGetMousePosition()))
|
||||
local editlabel = watchCtrl:IsWatch(item) and TR("&Edit Watch") or TR("&Edit Value")
|
||||
watchCtrl:PopupMenu(wx.wxMenu {
|
||||
{ ID_ADDWATCH, TR("&Add Watch")..KSC(ID_ADDWATCH) },
|
||||
{ ID_EDITWATCH, editlabel..KSC(ID_EDITWATCH) },
|
||||
{ ID_DELETEWATCH, TR("&Delete Watch")..KSC(ID_DELETEWATCH) },
|
||||
})
|
||||
item = nil
|
||||
end)
|
||||
|
||||
watchCtrl:Connect(ID_ADDWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function (event) watchCtrl:EditLabel(watchCtrl:AppendItem(root, defaultExpr, image.LOCAL)) end)
|
||||
|
||||
watchCtrl:Connect(ID_EDITWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function (event) watchCtrl:EditLabel(item or watchCtrl:GetSelection()) end)
|
||||
watchCtrl:Connect(ID_EDITWATCH, wx.wxEVT_UPDATE_UI,
|
||||
function (event) event:Enable(watchCtrl:IsEditable(item or watchCtrl:GetSelection())) end)
|
||||
|
||||
watchCtrl:Connect(ID_DELETEWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function (event) watchCtrl:Delete(item or watchCtrl:GetSelection()) end)
|
||||
watchCtrl:Connect(ID_DELETEWATCH, wx.wxEVT_UPDATE_UI,
|
||||
function (event) event:Enable(watchCtrl:IsWatch(item or watchCtrl:GetSelection())) end)
|
||||
|
||||
local label
|
||||
watchCtrl:Connect(wx.wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT,
|
||||
function (event)
|
||||
local item = event:GetItem()
|
||||
if not (item:IsOk() and watchCtrl:IsEditable(item)) then
|
||||
event:Veto()
|
||||
return
|
||||
end
|
||||
|
||||
label = watchCtrl:GetItemText(item)
|
||||
|
||||
if watchCtrl:IsWatch(item) then
|
||||
local expr = watchCtrl:GetItemExpression(item)
|
||||
if expr then watchCtrl:SetItemText(item, expr) end
|
||||
else
|
||||
local prefix = stringifyKeyIntoPrefix(watchCtrl:GetItemName(item))
|
||||
local val = watchCtrl:GetItemText(item):gsub(q(prefix),'')
|
||||
watchCtrl:SetItemText(item, val)
|
||||
end
|
||||
end)
|
||||
watchCtrl:Connect(wx.wxEVT_COMMAND_TREE_END_LABEL_EDIT,
|
||||
function (event)
|
||||
event:Veto()
|
||||
|
||||
local item = event:GetItem()
|
||||
if event:IsEditCancelled() then
|
||||
if watchCtrl:GetItemText(row) == defaultExpr then
|
||||
watchCtrl:DeleteItem(row)
|
||||
if watchCtrl:GetItemText(item) == defaultExpr then
|
||||
-- when Delete is called from END_EDIT, it causes infinite loop
|
||||
-- on OSX (wxwidgets 2.9.5) as Delete calls END_EDIT again.
|
||||
-- disable handlers during Delete and then enable back.
|
||||
watchCtrl:SetEvtHandlerEnabled(false)
|
||||
watchCtrl:Delete(item)
|
||||
watchCtrl:SetEvtHandlerEnabled(true)
|
||||
else
|
||||
watchCtrl:SetItemText(item, label)
|
||||
end
|
||||
else
|
||||
watchCtrl:SetItem(row, 0, event:GetText())
|
||||
updateWatches(row)
|
||||
if watchCtrl:IsWatch(item) then
|
||||
watchCtrl:SetItemExpression(item, event:GetLabel())
|
||||
else
|
||||
watchCtrl:UpdateItemValue(item, event:GetLabel())
|
||||
end
|
||||
end
|
||||
event:Skip()
|
||||
end)
|
||||
@@ -1094,27 +1243,6 @@ debuggerCreateWatchWindow()
|
||||
|
||||
DebuggerRefreshPanels = updateStackAndWatches
|
||||
|
||||
function DebuggerAddWatch(watch)
|
||||
local mgr = ide.frame.uimgr
|
||||
local pane = mgr:GetPane("watchpanel")
|
||||
if (pane:IsOk() and not pane:IsShown()) then
|
||||
pane:Show()
|
||||
mgr:Update()
|
||||
end
|
||||
|
||||
local watchCtrl = debugger.watchCtrl
|
||||
-- check if this expression is already on the list
|
||||
for idx = 0, watchCtrl:GetItemCount() - 1 do
|
||||
if watchCtrl:GetItemText(idx) == watch then return end
|
||||
end
|
||||
|
||||
local row = watchCtrl:InsertItem(watchCtrl:GetItemCount(), TR("Expr"))
|
||||
watchCtrl:SetItem(row, 0, watch)
|
||||
watchCtrl:SetItem(row, 1, TR("Value"))
|
||||
|
||||
updateWatches(row)
|
||||
end
|
||||
|
||||
function DebuggerAttachDefault(options)
|
||||
debugger.options = options
|
||||
if (debugger.listening) then return end
|
||||
@@ -1146,15 +1274,16 @@ function DebuggerStop(resetpid)
|
||||
if resetpid then debugger.pid = nil end
|
||||
end
|
||||
|
||||
function DebuggerMakeFileName(editor, filePath)
|
||||
return filePath or ide.config.default.fullname
|
||||
local function debuggerMakeFileName(editor)
|
||||
return ide:GetDocument(editor):GetFilePath()
|
||||
or ide:GetDocument(editor):GetFileName()
|
||||
or ide.config.default.fullname
|
||||
end
|
||||
|
||||
function DebuggerToggleBreakpoint(editor, line)
|
||||
local markers = editor:MarkerGet(line)
|
||||
local id = editor:GetId()
|
||||
local filePath = debugger.editormap and debugger.editormap[editor]
|
||||
or DebuggerMakeFileName(editor, ide.openDocuments[id].filePath)
|
||||
or debuggerMakeFileName(editor)
|
||||
if bit.band(markers, BREAKPOINT_MARKER_VALUE) > 0 then
|
||||
editor:MarkerDelete(line, BREAKPOINT_MARKER)
|
||||
if debugger.server then debugger.breakpoint(filePath, line+1, false) end
|
||||
@@ -1187,8 +1316,7 @@ function DebuggerRefreshScratchpad()
|
||||
end
|
||||
else
|
||||
local clear = ide.frame.menuBar:IsChecked(ID_CLEAROUTPUT)
|
||||
local filePath = DebuggerMakeFileName(scratchpadEditor,
|
||||
ide.openDocuments[scratchpadEditor:GetId()].filePath)
|
||||
local filePath = debuggerMakeFileName(scratchpadEditor)
|
||||
|
||||
-- wrap into a function call to make "return" to work with scratchpad
|
||||
code = "(function()"..code.."\nend)()"
|
||||
|
||||
@@ -141,7 +141,7 @@ end
|
||||
|
||||
local function navigateToPosition(editor, fromPosition, toPosition, length)
|
||||
table.insert(editor.jumpstack, fromPosition)
|
||||
editor:GotoPos(toPosition)
|
||||
editor:GotoPosEnforcePolicy(toPosition)
|
||||
if length then
|
||||
editor:SetAnchor(toPosition + length)
|
||||
end
|
||||
@@ -150,7 +150,7 @@ end
|
||||
local function navigateBack(editor)
|
||||
if #editor.jumpstack == 0 then return end
|
||||
local pos = table.remove(editor.jumpstack)
|
||||
editor:GotoPos(pos)
|
||||
editor:GotoPosEnforcePolicy(pos)
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -664,12 +664,12 @@ function CreateEditor()
|
||||
-- populate cache with Ctrl-<letter> combinations for workaround on Linux
|
||||
-- http://wxwidgets.10942.n7.nabble.com/Menu-shortcuts-inconsistentcy-issue-td85065.html
|
||||
for id, shortcut in pairs(ide.config.keymap) do
|
||||
local key = shortcut:match('^Ctrl[-+](%w)$')
|
||||
local key = shortcut:match('^Ctrl[-+](.)$')
|
||||
if key then editor.ctrlcache[key:byte()] = id end
|
||||
end
|
||||
|
||||
-- populate editor keymap with configured combinations
|
||||
for _, map in ipairs(edcfg.keymap) do
|
||||
for _, map in ipairs(edcfg.keymap or {}) do
|
||||
local key, mod, cmd, os = unpack(map)
|
||||
if not os or os == ide.osname then
|
||||
if cmd then
|
||||
@@ -721,6 +721,8 @@ function CreateEditor()
|
||||
|
||||
editor:SetVisiblePolicy(wxstc.wxSTC_VISIBLE_STRICT, 3)
|
||||
|
||||
editor:SetMarginType(margin.LINENUMBER, wxstc.wxSTC_MARGIN_NUMBER)
|
||||
editor:SetMarginMask(margin.LINENUMBER, 0)
|
||||
editor:SetMarginWidth(margin.LINENUMBER,
|
||||
editor:TextWidth(wxstc.wxSTC_STYLE_DEFAULT, linenummask))
|
||||
|
||||
@@ -772,6 +774,11 @@ function CreateEditor()
|
||||
editor:AutoCompStops([[ \n\t=-+():.,;*/!"'$%&~'#°^@?´`<>][|}{]])
|
||||
end
|
||||
|
||||
function editor:GotoPosEnforcePolicy(pos)
|
||||
self:GotoPos(pos)
|
||||
self:EnsureVisibleEnforcePolicy(self:LineFromPosition(pos))
|
||||
end
|
||||
|
||||
-- GotoPos should work by itself, but it doesn't (wx 2.9.5).
|
||||
-- This is likely because the editor window hasn't been refreshed yet,
|
||||
-- so its LinesOnScreen method returns 0/-1, which skews the calculations.
|
||||
@@ -788,20 +795,20 @@ function CreateEditor()
|
||||
if ide.osname ~= 'Macintosh' then self:GotoPos(pos) end
|
||||
else
|
||||
redolater = nil
|
||||
self:GotoPos(pos)
|
||||
self:EnsureVisibleEnforcePolicy(self:LineFromPosition(pos))
|
||||
self:GotoPosEnforcePolicy(pos)
|
||||
end
|
||||
elseif not badtime and redolater then
|
||||
-- reset the left margin first to make sure that the position
|
||||
-- is set "from the left" to get the best content displayed.
|
||||
self:SetXOffset(0)
|
||||
self:GotoPos(redolater)
|
||||
self:EnsureVisibleEnforcePolicy(self:LineFromPosition(redolater))
|
||||
self:GotoPosEnforcePolicy(redolater)
|
||||
redolater = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function editor:SetupKeywords(...) return SetupKeywords(self, ...) end
|
||||
|
||||
editor.ev = {}
|
||||
editor:Connect(wxstc.wxEVT_STC_MARGINCLICK,
|
||||
function (event)
|
||||
@@ -827,24 +834,36 @@ function CreateEditor()
|
||||
editor.assignscache = false
|
||||
end
|
||||
local evtype = event:GetModificationType()
|
||||
if (bit.band(evtype,wxstc.wxSTC_MOD_INSERTTEXT) ~= 0) then
|
||||
local inserted = bit.band(evtype, wxstc.wxSTC_MOD_INSERTTEXT) ~= 0
|
||||
local deleted = bit.band(evtype, wxstc.wxSTC_MOD_DELETETEXT) ~= 0
|
||||
if (inserted or deleted) then
|
||||
SetAutoRecoveryMark()
|
||||
table.insert(editor.ev,{event:GetPosition(),event:GetLinesAdded()})
|
||||
DynamicWordsAdd(editor,nil,editor:LineFromPosition(event:GetPosition()),event:GetLinesAdded())
|
||||
|
||||
local firstLine = editor:LineFromPosition(event:GetPosition())
|
||||
local linesChanged = inserted and event:GetLinesAdded() or 0
|
||||
table.insert(editor.ev, {event:GetPosition(), linesChanged})
|
||||
DynamicWordsAdd(editor, nil, firstLine, linesChanged)
|
||||
end
|
||||
if (bit.band(evtype,wxstc.wxSTC_MOD_DELETETEXT) ~= 0) then
|
||||
SetAutoRecoveryMark()
|
||||
table.insert(editor.ev,{event:GetPosition(),0})
|
||||
DynamicWordsAdd(editor,nil,editor:LineFromPosition(event:GetPosition()),0)
|
||||
|
||||
local beforeInserted = bit.band(evtype,wxstc.wxSTC_MOD_BEFOREINSERT) ~= 0
|
||||
local beforeDeleted = bit.band(evtype,wxstc.wxSTC_MOD_BEFOREDELETE) ~= 0
|
||||
|
||||
if (beforeInserted or beforeDeleted) then
|
||||
-- unfold the current line being changed if folded
|
||||
local firstLine = editor:LineFromPosition(event:GetPosition())
|
||||
if not editor:GetFoldExpanded(firstLine) then editor:ToggleFold(firstLine) end
|
||||
end
|
||||
|
||||
if ide.config.acandtip.nodynwords then return end
|
||||
-- only required to track changes
|
||||
if (bit.band(evtype,wxstc.wxSTC_MOD_BEFOREDELETE) ~= 0) then
|
||||
local _, numlines = event:GetText():gsub("\r?\n","%1")
|
||||
DynamicWordsRem(editor,nil,editor:LineFromPosition(event:GetPosition()), numlines)
|
||||
|
||||
if beforeDeleted then
|
||||
local pos = event:GetPosition()
|
||||
local text = editor:GetTextRange(pos, pos+event:GetLength())
|
||||
local _, numlines = text:gsub("\r?\n","%1")
|
||||
DynamicWordsRem(editor,nil,editor:LineFromPosition(pos), numlines)
|
||||
end
|
||||
if (bit.band(evtype,wxstc.wxSTC_MOD_BEFOREINSERT) ~= 0) then
|
||||
if beforeInserted then
|
||||
DynamicWordsRem(editor,nil,editor:LineFromPosition(event:GetPosition()), 0)
|
||||
end
|
||||
end)
|
||||
@@ -868,18 +887,22 @@ function CreateEditor()
|
||||
local indent = editor:GetLineIndentation(line - 1)
|
||||
local linedone = editor:GetLine(line - 1)
|
||||
|
||||
-- if the indentation is 0 and the current line is not empty
|
||||
-- then take indentation from the current line (instead of the
|
||||
-- previous one). This may happen when CR is hit at the beginning
|
||||
-- of a line (rather than at the end).
|
||||
if indent == 0 and not linetx:match("^[\010\013]*$") then
|
||||
-- if the indentation is 0 and the current line is not empty,
|
||||
-- but the previous line is empty, then take indentation from the
|
||||
-- current line (instead of the previous one). This may happen when
|
||||
-- CR is hit at the beginning of a line (rather than at the end).
|
||||
if indent == 0 and not linetx:match("^[\010\013]*$")
|
||||
and linedone:match("^[\010\013]*$") then
|
||||
indent = editor:GetLineIndentation(line)
|
||||
end
|
||||
|
||||
local ut = editor:GetUseTabs()
|
||||
local tw = ut and editor:GetTabWidth() or editor:GetIndent()
|
||||
local style = bit.band(editor:GetStyleAt(editor:PositionFromLine(line-1)), 31)
|
||||
|
||||
if edcfg.smartindent
|
||||
-- don't apply smartindent to multi-line comments or strings
|
||||
and not (editor.spec.iscomment[style] or editor.spec.isstring[style])
|
||||
and editor.spec.isdecindent and editor.spec.isincindent then
|
||||
local closed, blockend = editor.spec.isdecindent(linedone)
|
||||
local opened = editor.spec.isincindent(linedone)
|
||||
@@ -1015,7 +1038,9 @@ function CreateEditor()
|
||||
-- where refresh of R/W and R/O status in the status bar is delayed.
|
||||
|
||||
editor:Connect(wxstc.wxEVT_STC_PAINTED,
|
||||
function ()
|
||||
function (event)
|
||||
PackageEventHandle("onEditorPainted", editor, event)
|
||||
|
||||
if ide.osname == 'Windows' then
|
||||
updateStatusText(editor)
|
||||
|
||||
@@ -1030,7 +1055,9 @@ function CreateEditor()
|
||||
end)
|
||||
|
||||
editor:Connect(wxstc.wxEVT_STC_UPDATEUI,
|
||||
function ()
|
||||
function (event)
|
||||
PackageEventHandle("onEditorUpdateUI", editor, event)
|
||||
|
||||
if ide.osname ~= 'Windows' then updateStatusText(editor) end
|
||||
|
||||
editor:GotoPosDelayed()
|
||||
@@ -1174,6 +1201,10 @@ function CreateEditor()
|
||||
-- if no "jump back" is needed, then do normal processing as this
|
||||
-- combination can be mapped to some action
|
||||
if not navigateBack(editor) then event:Skip() end
|
||||
elseif (keycode == wx.WXK_DELETE and mod == wx.wxMOD_SHIFT)
|
||||
or (keycode == wx.WXK_INSERT and mod == wx.wxMOD_CONTROL) then
|
||||
ide.frame:AddPendingEvent(wx.wxCommandEvent(
|
||||
wx.wxEVT_COMMAND_MENU_SELECTED, keycode == wx.WXK_INSERT and ID_COPY or ID_CUT))
|
||||
elseif ide.osname == "Unix" and ide.wxver >= "2.9.5"
|
||||
and mod == wx.wxMOD_CONTROL and editor.ctrlcache[keycode] then
|
||||
ide.frame:AddPendingEvent(wx.wxCommandEvent(
|
||||
@@ -1250,6 +1281,7 @@ function CreateEditor()
|
||||
or (" (%d)"):format(#instances+(instances[0] and 1 or 0))
|
||||
local line = instances and instances[0] and editor:LineFromPosition(instances[0]-1)+1
|
||||
local def = line and " ("..TR("on line %d"):format(line)..")" or ""
|
||||
local selections = ide.wxver >= "2.9.5" and editor:GetSelections() or 1
|
||||
|
||||
local menu = wx.wxMenu {
|
||||
{ ID_UNDO, TR("&Undo") },
|
||||
@@ -1262,6 +1294,7 @@ function CreateEditor()
|
||||
{ },
|
||||
{ ID_GOTODEFINITION, TR("Go To Definition")..def },
|
||||
{ ID_RENAMEALLINSTANCES, TR("Rename All Instances")..occurrences },
|
||||
{ ID_REPLACEALLSELECTIONS, TR("Replace All Selections") },
|
||||
{ },
|
||||
{ ID_QUICKADDWATCH, TR("Add Watch Expression") },
|
||||
{ ID_QUICKEVAL, TR("Evaluate In Console") },
|
||||
@@ -1269,7 +1302,9 @@ function CreateEditor()
|
||||
}
|
||||
|
||||
menu:Enable(ID_GOTODEFINITION, instances and instances[0])
|
||||
menu:Enable(ID_RENAMEALLINSTANCES, instances and (instances[0] or #instances > 0))
|
||||
menu:Enable(ID_RENAMEALLINSTANCES, instances and (instances[0] or #instances > 0)
|
||||
or editor:GetSelectionStart() ~= editor:GetSelectionEnd())
|
||||
menu:Enable(ID_REPLACEALLSELECTIONS, selections > 1)
|
||||
menu:Enable(ID_QUICKADDWATCH, value ~= nil)
|
||||
menu:Enable(ID_QUICKEVAL, value ~= nil)
|
||||
|
||||
@@ -1300,12 +1335,49 @@ function CreateEditor()
|
||||
editor:Connect(ID_RENAMEALLINSTANCES, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function(event)
|
||||
if value and pos then
|
||||
if not (instances and (instances[0] or #instances > 0)) then
|
||||
-- if multiple instances (of a variable) are not detected,
|
||||
-- then simply find all instances of (selected) `value`
|
||||
instances = {}
|
||||
local length, pos = editor:GetLength(), 0
|
||||
while true do
|
||||
editor:SetTargetStart(pos)
|
||||
editor:SetTargetEnd(length)
|
||||
pos = editor:SearchInTarget(value)
|
||||
if pos == -1 then break end
|
||||
table.insert(instances, pos+1)
|
||||
pos = pos + #value
|
||||
end
|
||||
end
|
||||
selectAllInstances(instances, value, pos)
|
||||
end
|
||||
end)
|
||||
|
||||
editor:Connect(ID_REPLACEALLSELECTIONS, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function(event)
|
||||
local main = editor:GetMainSelection()
|
||||
local text = wx.wxGetTextFromUser(
|
||||
TR("Enter replacement text"),
|
||||
TR("Replace All Selections"),
|
||||
editor:GetTextRange(editor:GetSelectionNStart(main), editor:GetSelectionNEnd(main))
|
||||
)
|
||||
if not text or text == "" then return end
|
||||
|
||||
editor:BeginUndoAction()
|
||||
for s = 0, editor:GetSelections()-1 do
|
||||
local selst, selend = editor:GetSelectionNStart(s), editor:GetSelectionNEnd(s)
|
||||
editor:SetTargetStart(selst)
|
||||
editor:SetTargetEnd(selend)
|
||||
editor:ReplaceTarget(text)
|
||||
editor:SetSelectionNStart(s, selst)
|
||||
editor:SetSelectionNEnd(s, selst+#text)
|
||||
end
|
||||
editor:EndUndoAction()
|
||||
editor:SetMainSelection(main)
|
||||
end)
|
||||
|
||||
editor:Connect(ID_QUICKADDWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function(event) DebuggerAddWatch(value) end)
|
||||
function(event) ide:AddWatch(value) end)
|
||||
|
||||
editor:Connect(ID_QUICKEVAL, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function(event) ShellExecuteCode(value) end)
|
||||
@@ -1460,5 +1532,6 @@ funclist:Connect(wx.wxEVT_COMMAND_CHOICE_SELECTED,
|
||||
editor:GotoLine(l)
|
||||
editor:SetFocus()
|
||||
editor:SetSTCFocus(true)
|
||||
editor:EnsureVisibleEnforcePolicy(l)
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -22,18 +22,15 @@ local q = EscapeMagic
|
||||
-- generic tree
|
||||
-- ------------
|
||||
|
||||
local IMG_DIRECTORY, IMG_FILE_KNOWN, IMG_FILE_OTHER = 0, 1, 2
|
||||
local image = { DIRECTORY = 0, FILEKNOWN = 1, FILEOTHER = 2 }
|
||||
|
||||
do
|
||||
local getBitmap = (ide.app.createbitmap or wx.wxArtProvider.GetBitmap)
|
||||
local size = wx.wxSize(16, 16)
|
||||
filetree.imglist = wx.wxImageList(16,16)
|
||||
-- 0 = directory
|
||||
filetree.imglist:Add(getBitmap(wx.wxART_FOLDER, wx.wxART_OTHER, size))
|
||||
-- 1 = file known spec
|
||||
filetree.imglist:Add(getBitmap(wx.wxART_HELP_PAGE, wx.wxART_OTHER, size))
|
||||
-- 2 = file other
|
||||
filetree.imglist:Add(getBitmap(wx.wxART_NORMAL_FILE, wx.wxART_OTHER, size))
|
||||
filetree.imglist:Add(getBitmap("FOLDER", "OTHER", size)) -- 0 = directory
|
||||
filetree.imglist:Add(getBitmap("HELP-PAGE", "OTHER", size)) -- 1 = file known spec
|
||||
filetree.imglist:Add(getBitmap("NORMAL-FILE", "OTHER", size)) -- 2 = file other
|
||||
end
|
||||
|
||||
local function treeAddDir(tree,parent_id,rootdir)
|
||||
@@ -50,7 +47,7 @@ local function treeAddDir(tree,parent_id,rootdir)
|
||||
for _, file in ipairs(FileSysGetRecursive(rootdir)) do
|
||||
local name, dir = file:match("([^"..pathsep.."]+)("..pathsep.."?)$")
|
||||
local known = GetSpec(GetFileExt(name))
|
||||
local icon = #dir>0 and IMG_DIRECTORY or known and IMG_FILE_KNOWN or IMG_FILE_OTHER
|
||||
local icon = #dir>0 and image.DIRECTORY or known and image.FILEKNOWN or image.FILEOTHER
|
||||
local item = items[name .. icon]
|
||||
if item then -- existing item
|
||||
-- keep deleting items until we find item
|
||||
@@ -68,7 +65,7 @@ local function treeAddDir(tree,parent_id,rootdir)
|
||||
curr = (curr
|
||||
and tree:InsertItem(parent_id, curr, name, icon)
|
||||
or tree:PrependItem(parent_id, name, icon))
|
||||
if #dir>0 then tree:SetItemHasChildren(curr, FileSysHasContent(file)) end
|
||||
if #dir>0 then tree:SetItemHasChildren(curr, FileDirHasContent(file)) end
|
||||
end
|
||||
if curr:IsOk() then cache[iscaseinsensitive and name:lower() or name] = curr end
|
||||
end
|
||||
@@ -97,7 +94,7 @@ local function treeSetRoot(tree,rootdir)
|
||||
tree:DeleteAllItems()
|
||||
if (not wx.wxDirExists(rootdir)) then return end
|
||||
|
||||
local root_id = tree:AddRoot(rootdir, IMG_DIRECTORY)
|
||||
local root_id = tree:AddRoot(rootdir, image.DIRECTORY)
|
||||
tree:SetItemHasChildren(root_id, true) -- make sure that the item can expand
|
||||
tree:Expand(root_id) -- this will also populate the tree
|
||||
end
|
||||
@@ -141,15 +138,20 @@ local function findItem(tree, match)
|
||||
end
|
||||
|
||||
local function treeSetConnectorsAndIcons(tree)
|
||||
tree:SetImageList(filetree.imglist)
|
||||
tree:AssignImageList(filetree.imglist)
|
||||
|
||||
local function isIt(item, imgtype) return tree:GetItemImage(item) == imgtype end
|
||||
|
||||
function tree:IsDirectory(item_id) return isIt(item_id, IMG_DIRECTORY) end
|
||||
function tree:IsFileKnown(item_id) return isIt(item_id, IMG_FILE_KNOWN) end
|
||||
function tree:IsFileOther(item_id) return isIt(item_id, IMG_FILE_OTHER) end
|
||||
function tree:IsDirectory(item_id) return isIt(item_id, image.DIRECTORY) end
|
||||
function tree:IsFileKnown(item_id) return isIt(item_id, image.FILEKNOWN) end
|
||||
function tree:IsFileOther(item_id) return isIt(item_id, image.FILEOTHER) end
|
||||
function tree:IsRoot(item_id) return not tree:GetItemParent(item_id):IsOk() end
|
||||
|
||||
function tree:FindItem(match)
|
||||
return findItem(self, wx.wxIsAbsolutePath(match) and match
|
||||
or MergeFullPath(ide:GetProject(), match))
|
||||
end
|
||||
|
||||
function tree:GetItemFullName(item_id)
|
||||
local tree = self
|
||||
local str = tree:GetItemText(item_id)
|
||||
@@ -200,7 +202,7 @@ local function treeSetConnectorsAndIcons(tree)
|
||||
|
||||
local empty = ""
|
||||
local function renameItem(itemsrc, target)
|
||||
local isdir = tree:GetItemImage(itemsrc) == IMG_DIRECTORY
|
||||
local isdir = tree:GetItemImage(itemsrc) == image.DIRECTORY
|
||||
local isnew = tree:GetItemText(itemsrc) == empty
|
||||
local source = tree:GetItemFullName(itemsrc)
|
||||
local fn = wx.wxFileName(target)
|
||||
@@ -252,7 +254,7 @@ local function treeSetConnectorsAndIcons(tree)
|
||||
LoadFile(fullpath:gsub(q(source), target), doc.editor)
|
||||
end
|
||||
else -- refresh the tree and select the new item
|
||||
local itemdst = findItem(tree, target)
|
||||
local itemdst = tree:FindItem(target)
|
||||
if itemdst then
|
||||
refreshAncestors(tree:GetItemParent(itemdst))
|
||||
tree:SelectItem(itemdst)
|
||||
@@ -263,17 +265,20 @@ local function treeSetConnectorsAndIcons(tree)
|
||||
return true
|
||||
end
|
||||
local function deleteItem(item_id)
|
||||
local isdir = tree:GetItemImage(item_id) == IMG_DIRECTORY
|
||||
local isdir = tree:GetItemImage(item_id) == image.DIRECTORY
|
||||
local source = tree:GetItemFullName(item_id)
|
||||
|
||||
if isdir and FileSysHasContent(source..pathsep) then return false end
|
||||
if isdir and FileDirHasContent(source..pathsep) then return false end
|
||||
if wx.wxMessageBox(
|
||||
TR("Do you want to delete '%s'?"):format(source),
|
||||
GetIDEString("editormessage"),
|
||||
wx.wxYES_NO + wx.wxCENTRE, ide.frame) ~= wx.wxYES then return false end
|
||||
|
||||
if isdir then
|
||||
wx.wxRmdir(source)
|
||||
if not wx.wxRmdir(source) then
|
||||
ReportError(TR("Unable to delete directory '%s': %s")
|
||||
:format(source, wx.wxSysErrorMsg()))
|
||||
end
|
||||
else
|
||||
local doc = ide:FindDocument(source)
|
||||
if doc then ClosePage(doc.index) end
|
||||
@@ -297,12 +302,12 @@ local function treeSetConnectorsAndIcons(tree)
|
||||
end)
|
||||
|
||||
-- handle context menu
|
||||
local function addItem(item_id, name, image)
|
||||
local isdir = tree:GetItemImage(item_id) == IMG_DIRECTORY
|
||||
local function addItem(item_id, name, img)
|
||||
local isdir = tree:GetItemImage(item_id) == image.DIRECTORY
|
||||
local parent = isdir and item_id or tree:GetItemParent(item_id)
|
||||
if isdir then tree:Expand(item_id) end -- expand to populate if needed
|
||||
|
||||
local item = tree:PrependItem(parent, name, image)
|
||||
local item = tree:PrependItem(parent, name, img)
|
||||
tree:SetItemHasChildren(parent, true)
|
||||
-- temporarily disable expand as we don't need this node populated
|
||||
tree:SetEvtHandlerEnabled(false)
|
||||
@@ -313,11 +318,11 @@ local function treeSetConnectorsAndIcons(tree)
|
||||
|
||||
tree:Connect(ID_NEWFILE, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function()
|
||||
tree:EditLabel(addItem(tree:GetSelection(), empty, IMG_FILE_OTHER))
|
||||
tree:EditLabel(addItem(tree:GetSelection(), empty, image.FILEOTHER))
|
||||
end)
|
||||
tree:Connect(ID_NEWDIRECTORY, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function()
|
||||
tree:EditLabel(addItem(tree:GetSelection(), empty, IMG_DIRECTORY))
|
||||
tree:EditLabel(addItem(tree:GetSelection(), empty, image.DIRECTORY))
|
||||
end)
|
||||
tree:Connect(ID_RENAMEFILE, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function() tree:EditLabel(tree:GetSelection()) end)
|
||||
@@ -385,10 +390,10 @@ local function treeSetConnectorsAndIcons(tree)
|
||||
FileTreeProjectListUpdate(projectdirectorymenu, 0)
|
||||
|
||||
-- disable Delete on non-empty directories
|
||||
local isdir = tree:GetItemImage(item_id) == IMG_DIRECTORY
|
||||
local isdir = tree:GetItemImage(item_id) == image.DIRECTORY
|
||||
if isdir then
|
||||
local source = tree:GetItemFullName(item_id)
|
||||
menu:Enable(ID_DELETEFILE, not FileSysHasContent(source..pathsep))
|
||||
menu:Enable(ID_DELETEFILE, not FileDirHasContent(source..pathsep))
|
||||
menu:Enable(ID_OPENEXTENSION, false)
|
||||
else
|
||||
local fname = tree:GetItemText(item_id)
|
||||
@@ -425,7 +430,7 @@ local function treeSetConnectorsAndIcons(tree)
|
||||
end
|
||||
|
||||
if item_id and bit.band(flags, mask) > 0 then
|
||||
if tree:GetItemImage(item_id) == IMG_DIRECTORY then
|
||||
if tree:GetItemImage(item_id) == image.DIRECTORY then
|
||||
tree:Toggle(item_id)
|
||||
tree:SelectItem(item_id)
|
||||
else
|
||||
@@ -474,7 +479,7 @@ local function treeSetConnectorsAndIcons(tree)
|
||||
local itemsrc
|
||||
tree:Connect(wx.wxEVT_COMMAND_TREE_BEGIN_DRAG,
|
||||
function (event)
|
||||
if tree:GetItemParent(event:GetItem()):IsOk() then
|
||||
if ide.config.filetree.mousemove and tree:GetItemParent(event:GetItem()):IsOk() then
|
||||
itemsrc = event:GetItem()
|
||||
event:Allow()
|
||||
end
|
||||
@@ -595,6 +600,9 @@ function FileTreeProjectListClear()
|
||||
end
|
||||
|
||||
function FileTreeProjectListUpdate(menu, items)
|
||||
-- protect against recent project menu not being present
|
||||
if not ide:FindMenuItem(ID_RECENTPROJECTS) then return end
|
||||
|
||||
local list = getProjectLabels()
|
||||
for i=#list, 1, -1 do
|
||||
local id = ID("file.recentprojects."..i)
|
||||
@@ -622,7 +630,7 @@ local curr_file
|
||||
function FileTreeMarkSelected(file)
|
||||
if not file or not filetree.projdir or #filetree.projdir == 0 then return end
|
||||
|
||||
local item_id = findItem(projtree, file)
|
||||
local item_id = projtree:FindItem(file)
|
||||
|
||||
-- if the select item is different from the current one
|
||||
-- or the current one is the same, but not bold (which may happen when
|
||||
@@ -630,7 +638,7 @@ function FileTreeMarkSelected(file)
|
||||
if curr_file ~= file
|
||||
or item_id and not projtree:IsBold(item_id) then
|
||||
if curr_file then
|
||||
local curr_id = findItem(projtree, curr_file)
|
||||
local curr_id = projtree:FindItem(curr_file)
|
||||
if curr_id and projtree:IsBold(curr_id) then
|
||||
projtree:SetItemBold(curr_id, false)
|
||||
end
|
||||
|
||||
@@ -43,8 +43,12 @@ ide.findReplace = {
|
||||
}
|
||||
local findReplace = ide.findReplace
|
||||
|
||||
local lastEditor
|
||||
function findReplace:GetEditor()
|
||||
return findReplace.oveditor or GetEditor()
|
||||
lastEditor = findReplace.oveditor or GetEditorWithFocus() or lastEditor
|
||||
-- last editor may already be "userdata" instead of a Scintilla object,
|
||||
-- so check if this is still a valid wxSTC object
|
||||
return pcall(function() lastEditor:GetId() end) and lastEditor or GetEditor()
|
||||
end
|
||||
|
||||
-------------------- Find replace dialog
|
||||
@@ -145,7 +149,7 @@ function findReplace:FindString(reverse)
|
||||
findReplace.foundString = true
|
||||
local start = editor:GetTargetStart()
|
||||
local finish = editor:GetTargetEnd()
|
||||
EnsureRangeVisible(start, finish)
|
||||
editor:EnsureVisibleEnforcePolicy(editor:LineFromPosition(start))
|
||||
editor:SetSelection(start, finish)
|
||||
ide.frame:SetStatusText("")
|
||||
end
|
||||
@@ -190,6 +194,9 @@ function findReplace:ReplaceString(fReplaceAll, inFileRegister)
|
||||
|
||||
if findReplace:HasText() then
|
||||
local editor = findReplace:GetEditor()
|
||||
-- don't replace in read-only editors
|
||||
if editor:GetReadOnly() then return false end
|
||||
|
||||
local endTarget = inFileRegister and setTargetAll(editor) or
|
||||
setTarget(editor, findReplace.fDown, fReplaceAll, findReplace.fWrap)
|
||||
|
||||
@@ -292,7 +299,7 @@ local function ProcInFiles(startdir,mask,subdirs,replace)
|
||||
end
|
||||
|
||||
-- give time to the UI to refresh
|
||||
if TimeGet() - start > 0.25 then wx.wxYield() end
|
||||
if TimeGet() - start > 0.25 then ide:Yield() end
|
||||
if not findReplace.dialog:IsShown() then
|
||||
DisplayOutputLn(TR("Cancelled by the user."))
|
||||
break
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
---------------------------------------------------------
|
||||
|
||||
local ide = ide
|
||||
local unpack = table.unpack or unpack
|
||||
|
||||
-- Pick some reasonable fixed width fonts to use for the editor
|
||||
local function setFont(style, config)
|
||||
@@ -79,6 +80,13 @@ local function menuDropDownPosition(event)
|
||||
return ide.frame:ScreenToClient(tb:ClientToScreen(rect:GetBottomLeft()))
|
||||
end
|
||||
|
||||
local function tbIconSize()
|
||||
local iconsize = (tonumber(ide.config.toolbar and ide.config.toolbar.iconsize)
|
||||
or (ide.osname == 'Macintosh' and 24 or 16))
|
||||
if iconsize ~= 24 then iconsize = 16 end
|
||||
return iconsize
|
||||
end
|
||||
|
||||
local function createToolBar(frame)
|
||||
local toolBar = wxaui.wxAuiToolBar(frame, wx.wxID_ANY, wx.wxDefaultPosition, wx.wxDefaultSize,
|
||||
wxaui.wxAUI_TB_PLAIN_BACKGROUND)
|
||||
@@ -87,34 +95,28 @@ local function createToolBar(frame)
|
||||
wx.wxDefaultPosition, wx.wxSize.new(240, ide.osname == 'Unix' and 28 or 24))
|
||||
|
||||
-- there are two sets of icons: use 24 on OSX and 16 on others.
|
||||
local toolBmpSize = (
|
||||
ide.osname == 'Macintosh' and wx.wxSize(24, 24) or wx.wxSize(16, 16))
|
||||
local iconsize = tbIconSize()
|
||||
local toolBmpSize = wx.wxSize(iconsize, iconsize)
|
||||
local getBitmap = (ide.app.createbitmap or wx.wxArtProvider.GetBitmap)
|
||||
toolBar:AddTool(ID_NEW, "New", getBitmap(wx.wxART_NORMAL_FILE, wx.wxART_TOOLBAR, toolBmpSize), TR("Create an empty document")..SCinB(ID_NEW))
|
||||
toolBar:AddTool(ID_OPEN, "Open", getBitmap(wx.wxART_FILE_OPEN, wx.wxART_TOOLBAR, toolBmpSize), TR("Open an existing document")..SCinB(ID_OPEN))
|
||||
toolBar:AddTool(ID_SAVE, "Save", getBitmap(wx.wxART_FILE_SAVE, wx.wxART_TOOLBAR, toolBmpSize), TR("Save the current document")..SCinB(ID_SAVE))
|
||||
toolBar:AddTool(ID_SAVEALL, "Save All", getBitmap(wx.wxART_NEW_DIR, wx.wxART_TOOLBAR, toolBmpSize), TR("Save all open documents")..SCinB(ID_SAVEALL))
|
||||
toolBar:AddTool(ID_PROJECTDIRFROMFILE, "Update", getBitmap(wx.wxART_GO_DIR_UP, wx.wxART_TOOLBAR, toolBmpSize), TR("Set project directory from current file")..SCinB(ID_PROJECTDIRFROMFILE))
|
||||
toolBar:AddTool(ID_PROJECTDIRCHOOSE, "Choose", getBitmap("wxART_DIR_SETUP", wx.wxART_TOOLBAR, toolBmpSize), TR("Choose a project directory")..SCinB(ID_PROJECTDIRCHOOSE))
|
||||
toolBar:AddSeparator()
|
||||
toolBar:AddTool(ID_FIND, "Find", getBitmap(wx.wxART_FIND, wx.wxART_TOOLBAR, toolBmpSize), TR("Find text")..SCinB(ID_FIND))
|
||||
toolBar:AddTool(ID_REPLACE, "Replace", getBitmap(wx.wxART_FIND_AND_REPLACE, wx.wxART_TOOLBAR, toolBmpSize), TR("Find and replace text")..SCinB(ID_REPLACE))
|
||||
if ide.app.createbitmap then -- custom handler should handle all bitmaps
|
||||
toolBar:AddSeparator()
|
||||
toolBar:AddTool(ID_STARTDEBUG, "Start Debugging", getBitmap("wxART_DEBUG_START", wx.wxART_TOOLBAR, toolBmpSize), TR("Start or Continue debugging")..SCinB(ID_STARTDEBUG))
|
||||
toolBar:AddTool(ID_STOPDEBUG, "Stop Debugging", getBitmap("wxART_DEBUG_STOP", wx.wxART_TOOLBAR, toolBmpSize), TR("Stop the currently running process")..SCinB(ID_STOPDEBUG))
|
||||
toolBar:AddTool(ID_DETACHDEBUG, "Detach Process", getBitmap("wxART_DEBUG_DETACH", wx.wxART_TOOLBAR, toolBmpSize), TR("Stop debugging and continue running the process")..SCinB(ID_DETACHDEBUG))
|
||||
toolBar:AddTool(ID_BREAK, "Break", getBitmap("wxART_DEBUG_BREAK", wx.wxART_TOOLBAR, toolBmpSize), TR("Break execution at the next executed line of code")..SCinB(ID_BREAK))
|
||||
toolBar:AddTool(ID_STEP, "Step into", getBitmap("wxART_DEBUG_STEP_INTO", wx.wxART_TOOLBAR, toolBmpSize), TR("Step into")..SCinB(ID_STEP))
|
||||
toolBar:AddTool(ID_STEPOVER, "Step over", getBitmap("wxART_DEBUG_STEP_OVER", wx.wxART_TOOLBAR, toolBmpSize), TR("Step over")..SCinB(ID_STEPOVER))
|
||||
toolBar:AddTool(ID_STEPOUT, "Step out", getBitmap("wxART_DEBUG_STEP_OUT", wx.wxART_TOOLBAR, toolBmpSize), TR("Step out of the current function")..SCinB(ID_STEPOUT))
|
||||
toolBar:AddSeparator()
|
||||
toolBar:AddTool(ID_TOGGLEBREAKPOINT, "Toggle breakpoint", getBitmap("wxART_DEBUG_BREAKPOINT_TOGGLE", wx.wxART_TOOLBAR, toolBmpSize), TR("Toggle breakpoint")..SCinB(ID_TOGGLEBREAKPOINT))
|
||||
toolBar:AddTool(ID_BOOKMARKTOGGLE, "Toggle bookmark", getBitmap("wxART_BOOKMARK_TOGGLE", wx.wxART_TOOLBAR, toolBmpSize), TR("Toggle bookmark")..SCinB(ID_BOOKMARKTOGGLE))
|
||||
toolBar:AddTool(ID_VIEWCALLSTACK, "Stack window", getBitmap("wxART_DEBUG_CALLSTACK", wx.wxART_TOOLBAR, toolBmpSize), TR("View the stack window")..SCinB(ID_VIEWCALLSTACK))
|
||||
toolBar:AddTool(ID_VIEWWATCHWINDOW, "Watch window", getBitmap("wxART_DEBUG_WATCH", wx.wxART_TOOLBAR, toolBmpSize), TR("View the watch window")..SCinB(ID_VIEWWATCHWINDOW))
|
||||
local icons, prev = ide.config.toolbar.icons
|
||||
for _, id in ipairs(icons) do
|
||||
if icons[id] ~= false then -- skip explicitly disabled icons
|
||||
if id == ID_SEPARATOR then
|
||||
-- make sure that there are no two separators next to each other;
|
||||
-- this may happen when some of the icons are disabled.
|
||||
if prev ~= ID_SEPARATOR then toolBar:AddSeparator() end
|
||||
else
|
||||
local iconmap = ide.config.toolbar.iconmap[id]
|
||||
if iconmap then
|
||||
local icon, description = unpack(iconmap)
|
||||
local isbitmap = type(icon) == "userdata" and icon:GetClassInfo():GetClassName() == "wxBitmap"
|
||||
local bitmap = isbitmap and icon or getBitmap(icon, "TOOLBAR", toolBmpSize)
|
||||
toolBar:AddTool(id, "", bitmap, TR(description)..SCinB(id))
|
||||
end
|
||||
end
|
||||
prev = id
|
||||
end
|
||||
end
|
||||
toolBar:AddSeparator()
|
||||
toolBar:AddControl(funclist)
|
||||
|
||||
toolBar:SetToolDropDown(ID_OPEN, true)
|
||||
@@ -471,7 +473,6 @@ do
|
||||
|
||||
mgr:AddPane(frame.toolBar, wxaui.wxAuiPaneInfo():
|
||||
Name("toolbar"):Caption("Toolbar"):
|
||||
MinSize(300,16):FloatingSize(800,48):
|
||||
ToolbarPane():Top():CloseButton(false):PaneBorder(false):
|
||||
LeftDockable(false):RightDockable(false))
|
||||
mgr:AddPane(frame.notebook, wxaui.wxAuiPaneInfo():
|
||||
|
||||
@@ -14,6 +14,7 @@ end
|
||||
-- so don't use stock IDs on Linux
|
||||
local linux = ide.osname == 'Unix'
|
||||
|
||||
ID_SEPARATOR = NewID()
|
||||
-- File menu
|
||||
ID_NEW = linux and NewID() or wx.wxID_NEW
|
||||
ID_OPEN = linux and NewID() or wx.wxID_OPEN
|
||||
@@ -123,6 +124,7 @@ ID_DELETEWATCH = NewID()
|
||||
-- Editor popup menu items
|
||||
ID_GOTODEFINITION = NewID()
|
||||
ID_RENAMEALLINSTANCES = NewID()
|
||||
ID_REPLACEALLSELECTIONS = NewID()
|
||||
ID_QUICKADDWATCH = NewID()
|
||||
ID_QUICKEVAL = NewID()
|
||||
ID_ADDTOSCRATCHPAD = NewID()
|
||||
|
||||
@@ -47,20 +47,18 @@ function M.warnings_from_string(src, file)
|
||||
return M.show_warnings(ast, globinit)
|
||||
end
|
||||
|
||||
local function cleanError(err)
|
||||
return err and err:gsub(".-:%d+: file%s+",""):gsub(", line (%d+), char %d+", ":%1")
|
||||
end
|
||||
|
||||
function AnalyzeFile(file)
|
||||
local warn, err, line, pos = M.warnings_from_string(FileRead(file), file)
|
||||
if err then
|
||||
err = err:gsub("line %d+, char %d+", "syntax error")
|
||||
end
|
||||
return warn, err, line, pos
|
||||
return warn, cleanError(err), line, pos
|
||||
end
|
||||
|
||||
function AnalyzeString(src)
|
||||
local warn, err, line, pos = M.warnings_from_string(src, "src")
|
||||
if err then
|
||||
err = err:gsub("line %d+, char %d+", "syntax error")
|
||||
end
|
||||
return warn, err, line, pos
|
||||
local warn, err, line, pos = M.warnings_from_string(src, "<string>")
|
||||
return warn, cleanError(err), line, pos
|
||||
end
|
||||
|
||||
function M.show_warnings(top_ast, globinit)
|
||||
@@ -178,10 +176,9 @@ function M.show_warnings(top_ast, globinit)
|
||||
end
|
||||
|
||||
local frame = ide.frame
|
||||
local menu = frame.menuBar:GetMenu(frame.menuBar:FindMenu(TR("&Project")))
|
||||
|
||||
-- insert after "Compile" item
|
||||
local _, compilepos = ide:FindMenuItem(menu, ID_COMPILE)
|
||||
local _, menu, compilepos = ide:FindMenuItem(ID_COMPILE)
|
||||
if compilepos then
|
||||
menu:Insert(compilepos+1, ID_ANALYZE, TR("Analyze")..KSC(ID_ANALYZE), TR("Analyze the source code"))
|
||||
end
|
||||
@@ -190,17 +187,16 @@ local debugger = ide.debugger
|
||||
local openDocuments = ide.openDocuments
|
||||
|
||||
local function analyzeProgram(editor)
|
||||
local editorText = editor:GetText()
|
||||
local id = editor:GetId()
|
||||
local filePath = DebuggerMakeFileName(editor, openDocuments[id].filePath)
|
||||
|
||||
if frame.menuBar:IsChecked(ID_CLEAROUTPUT) then ClearOutput() end
|
||||
if ide:GetMenuBar():IsChecked(ID_CLEAROUTPUT) then ClearOutput() end
|
||||
DisplayOutput("Analyzing the source code")
|
||||
frame:Update()
|
||||
|
||||
local editorText = editor:GetText()
|
||||
local doc = ide:GetDocument(editor)
|
||||
local filePath = doc:GetFilePath() or doc:GetFileName()
|
||||
local warn, err = M.warnings_from_string(editorText, filePath)
|
||||
if err then -- report compilation error
|
||||
DisplayOutput(": not completed\n")
|
||||
DisplayOutput((": not completed.\n%s\n"):format(cleanError(err)))
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -215,7 +211,9 @@ frame:Connect(ID_ANALYZE, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function ()
|
||||
ActivateOutput()
|
||||
local editor = GetEditor()
|
||||
if not analyzeProgram(editor) then CompileProgram(editor, { reportstats = false }) end
|
||||
if not analyzeProgram(editor) then
|
||||
CompileProgram(editor, { reportstats = false, keepoutput = true })
|
||||
end
|
||||
end)
|
||||
frame:Connect(ID_ANALYZE, wx.wxEVT_UPDATE_UI,
|
||||
function (event)
|
||||
|
||||
@@ -126,7 +126,4 @@ ide.config.editor.keymap = {
|
||||
-- Opt+Left/Right moves one word left (to the beginning)/right (to the end)
|
||||
{wxstc.wxSTC_KEY_LEFT, wxstc.wxSTC_SCMOD_ALT, wxstc.wxSTC_CMD_WORDLEFT, "Macintosh"},
|
||||
{wxstc.wxSTC_KEY_RIGHT, wxstc.wxSTC_SCMOD_ALT, wxstc.wxSTC_CMD_WORDRIGHTEND, "Macintosh"},
|
||||
-- Opt+Shift+Left/Right selects one word left (to the beginning)/right (to the end)
|
||||
{wxstc.wxSTC_KEY_LEFT, wxstc.wxSTC_SCMOD_ALT+wxstc.wxSTC_SCMOD_SHIFT, wxstc.wxSTC_CMD_WORDLEFTEXTEND, "Macintosh"},
|
||||
{wxstc.wxSTC_KEY_RIGHT, wxstc.wxSTC_SCMOD_ALT+wxstc.wxSTC_SCMOD_SHIFT, wxstc.wxSTC_CMD_WORDRIGHTENDEXTEND, "Macintosh"},
|
||||
}
|
||||
|
||||
@@ -76,6 +76,19 @@ local function onEditMenu(event)
|
||||
end
|
||||
|
||||
local menu_id = event:GetId()
|
||||
local copytext
|
||||
if (menu_id == ID_CUT or menu_id == ID_COPY)
|
||||
and ide.wxver >= "2.9.5" and editor:GetSelections() > 1 then
|
||||
local main = editor:GetMainSelection()
|
||||
copytext = editor:GetTextRange(editor:GetSelectionNStart(main), editor:GetSelectionNEnd(main))
|
||||
for s = 0, editor:GetSelections()-1 do
|
||||
if copytext ~= editor:GetTextRange(editor:GetSelectionNStart(s), editor:GetSelectionNEnd(s)) then
|
||||
copytext = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if menu_id == ID_CUT then
|
||||
if editor:GetSelectionStart() == editor:GetSelectionEnd()
|
||||
then editor:LineCut() else editor:Cut() end
|
||||
@@ -87,6 +100,8 @@ local function onEditMenu(event)
|
||||
elseif menu_id == ID_UNDO then editor:Undo()
|
||||
elseif menu_id == ID_REDO then editor:Redo()
|
||||
end
|
||||
|
||||
if copytext then editor:CopyText(#copytext, copytext) end
|
||||
end
|
||||
|
||||
for _, event in pairs({ID_CUT, ID_COPY, ID_PASTE, ID_SELECTALL, ID_UNDO, ID_REDO}) do
|
||||
@@ -225,7 +240,8 @@ frame:Connect(ID_COMMENT, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
|
||||
local function processSelection(editor, func)
|
||||
local text = editor:GetSelectedText()
|
||||
local pos = editor:GetCurrentPos()
|
||||
local line = editor:GetCurrentLine()
|
||||
local posinline = editor:GetCurrentPos() - editor:PositionFromLine(line)
|
||||
if #text == 0 then
|
||||
editor:SelectAll()
|
||||
text = editor:GetSelectedText()
|
||||
@@ -250,7 +266,8 @@ local function processSelection(editor, func)
|
||||
editor:ReplaceTarget(newtext)
|
||||
end
|
||||
end
|
||||
editor:GotoPos(pos)
|
||||
editor:GotoPosEnforcePolicy(math.min(
|
||||
editor:PositionFromLine(line)+posinline, editor:GetLineEndPosition(line)))
|
||||
end
|
||||
|
||||
frame:Connect(ID_SORT, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
@@ -274,28 +291,36 @@ local function reIndent(editor, buf)
|
||||
local tw = ut and editor:GetTabWidth() or editor:GetIndent()
|
||||
|
||||
local indents = {}
|
||||
local isstatic = {}
|
||||
for line = 1, #buf+1 do
|
||||
local closed, blockend = decindent(text)
|
||||
local opened = incindent(text)
|
||||
local style = bit.band(editor:GetStyleAt(editor:PositionFromLine(line-1)), 31)
|
||||
-- don't reformat multi-line comments or strings
|
||||
isstatic[line] = editor.spec.iscomment[style] or editor.spec.isstring[style]
|
||||
if not isstatic[line] or line == 1 or not isstatic[line-1] then
|
||||
local closed, blockend = decindent(text)
|
||||
local opened = incindent(text)
|
||||
|
||||
-- ignore impact from initial block endings as they are already indented
|
||||
if line == 1 then blockend = 0 end
|
||||
-- ignore impact from initial block endings as they are already indented
|
||||
if line == 1 then blockend = 0 end
|
||||
|
||||
-- this only needs to be done for 2, #buf+1; do it and get out when done
|
||||
if line > 1 then indents[line-1] = indents[line-1] - tw * closed end
|
||||
if line > #buf then break end
|
||||
-- this only needs to be done for 2, #buf+1; do it and get out when done
|
||||
if line > 1 then indents[line-1] = indents[line-1] - tw * closed end
|
||||
if line > #buf then break end
|
||||
|
||||
indent = indent + tw * (opened - blockend)
|
||||
if indent < 0 then indent = 0 end
|
||||
indent = indent + tw * (opened - blockend)
|
||||
if indent < 0 then indent = 0 end
|
||||
end
|
||||
|
||||
indents[line] = indent
|
||||
text = buf[line]
|
||||
end
|
||||
|
||||
for line = 1, #buf do
|
||||
buf[line] = buf[line]:gsub("^[ \t]*",
|
||||
not buf[line]:match('%S') and ''
|
||||
or ut and ("\t"):rep(indents[line] / tw) or (" "):rep(indents[line]))
|
||||
if not isstatic[line] then
|
||||
buf[line] = buf[line]:gsub("^[ \t]*",
|
||||
not buf[line]:match('%S') and ''
|
||||
or ut and ("\t"):rep(indents[line] / tw) or (" "):rep(indents[line]))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -131,6 +131,9 @@ do -- recent file history
|
||||
|
||||
local items = 0
|
||||
updateRecentFiles = function (list)
|
||||
-- protect against recent files menu not being present
|
||||
if not ide:FindMenuItem(ID_RECENTFILES) then return end
|
||||
|
||||
for i=1, #list do
|
||||
local file = list[i].filename
|
||||
local id = ID("file.recentfiles."..i)
|
||||
@@ -227,7 +230,6 @@ frame:Connect(ID_CLOSE, wx.wxEVT_UPDATE_UI,
|
||||
|
||||
frame:Connect(ID_EXIT, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function (event)
|
||||
if not SaveOnExit(true) then return end
|
||||
frame:Close() -- this will trigger wxEVT_CLOSE_WINDOW
|
||||
end)
|
||||
|
||||
@@ -245,6 +247,7 @@ local recentprojects = 0
|
||||
frame:Connect(ID_RECENTPROJECTS, wx.wxEVT_UPDATE_UI,
|
||||
function (event)
|
||||
recentprojects = FileTreeProjectListUpdate(projecthistorymenu, recentprojects)
|
||||
if not recentprojects then return end
|
||||
local pos = 1 -- add shortcut for the previous project (if any)
|
||||
if recentprojects > pos then
|
||||
local item = projecthistorymenu:FindItemByPosition(pos)
|
||||
|
||||
@@ -285,11 +285,11 @@ frame:Connect(ID_RUN, wx.wxEVT_UPDATE_UI,
|
||||
|
||||
frame:Connect(ID_RUNNOW, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function (event)
|
||||
if event:IsChecked() then
|
||||
if not DebuggerScratchpadOn(GetEditor()) then
|
||||
menuBar:Check(ID_RUNNOW, false) -- disable if couldn't start scratchpad
|
||||
end
|
||||
else DebuggerScratchpadOff() end
|
||||
if debugger.scratchpad then
|
||||
DebuggerScratchpadOff()
|
||||
else
|
||||
DebuggerScratchpadOn(GetEditor())
|
||||
end
|
||||
end)
|
||||
frame:Connect(ID_RUNNOW, wx.wxEVT_UPDATE_UI,
|
||||
function (event)
|
||||
@@ -300,6 +300,13 @@ frame:Connect(ID_RUNNOW, wx.wxEVT_UPDATE_UI,
|
||||
(ide.interpreter.scratchextloop ~= nil) and -- nil == no scratchpad support
|
||||
(editor ~= nil) and ((debugger.server == nil or debugger.scratchable)
|
||||
and debugger.pid == nil or debugger.scratchpad ~= nil))
|
||||
local isscratchpad = debugger.scratchpad ~= nil
|
||||
menuBar:Check(ID_RUNNOW, isscratchpad)
|
||||
local tool = ide:GetToolBar():FindTool(ID_RUNNOW)
|
||||
if tool and tool:IsSticky() ~= isscratchpad then
|
||||
tool:SetSticky(isscratchpad)
|
||||
ide:GetToolBar():Refresh()
|
||||
end
|
||||
end)
|
||||
|
||||
frame:Connect(ID_ATTACHDEBUG, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
@@ -422,5 +429,6 @@ frame:Connect(wx.wxEVT_IDLE,
|
||||
if (debugger.scratchpad) then DebuggerRefreshScratchpad() end
|
||||
if IndicateIfNeeded() then event:RequestMore(true) end
|
||||
PackageEventHandleOnce("onIdleOnce", event)
|
||||
PackageEventHandle("onIdle", event)
|
||||
event:Skip() -- let other EVT_IDLE handlers to work on the event
|
||||
end)
|
||||
|
||||
@@ -340,42 +340,41 @@ errorlog:Connect(wxstc.wxEVT_STC_DOUBLECLICK,
|
||||
if (fname and jumpline) then break end
|
||||
end
|
||||
|
||||
if (fname and jumpline) then
|
||||
-- fname may include name of executable, as in "path/to/lua: file.lua";
|
||||
-- strip it and try to find match again if needed.
|
||||
-- try the stripped name first as if it doesn't match, the longer
|
||||
-- name may have parts that may be interpreter as network path and
|
||||
-- may take few seconds to check.
|
||||
local name
|
||||
local fixedname = fname:match(":%s+(.+)")
|
||||
if fixedname then
|
||||
name = GetFullPathIfExists(FileTreeGetDir(), fixedname)
|
||||
or FileTreeFindByPartialName(fixedname)
|
||||
end
|
||||
name = name
|
||||
or GetFullPathIfExists(FileTreeGetDir(), fname)
|
||||
or FileTreeFindByPartialName(fname)
|
||||
if not (fname and jumpline) then return end
|
||||
|
||||
local editor = LoadFile(name or fname,nil,true)
|
||||
if not editor then
|
||||
local ed = GetEditor()
|
||||
if ed and ide:GetDocument(ed):GetFileName() == (name or fname) then
|
||||
editor = ed
|
||||
end
|
||||
end
|
||||
if editor then
|
||||
jumpline = tonumber(jumpline)
|
||||
jumplinepos = tonumber(jumplinepos)
|
||||
-- fname may include name of executable, as in "path/to/lua: file.lua";
|
||||
-- strip it and try to find match again if needed.
|
||||
-- try the stripped name first as if it doesn't match, the longer
|
||||
-- name may have parts that may be interpreter as network path and
|
||||
-- may take few seconds to check.
|
||||
local name
|
||||
local fixedname = fname:match(":%s+(.+)")
|
||||
if fixedname then
|
||||
name = GetFullPathIfExists(FileTreeGetDir(), fixedname)
|
||||
or FileTreeFindByPartialName(fixedname)
|
||||
end
|
||||
name = name
|
||||
or GetFullPathIfExists(FileTreeGetDir(), fname)
|
||||
or FileTreeFindByPartialName(fname)
|
||||
|
||||
editor:GotoPos(editor:PositionFromLine(math.max(0,jumpline-1))
|
||||
+ (jumplinepos and (math.max(0,jumplinepos-1)) or 0))
|
||||
editor:EnsureVisibleEnforcePolicy(jumpline)
|
||||
editor:SetFocus()
|
||||
local editor = LoadFile(name or fname,nil,true)
|
||||
if not editor then
|
||||
local ed = GetEditor()
|
||||
if ed and ide:GetDocument(ed):GetFileName() == (name or fname) then
|
||||
editor = ed
|
||||
end
|
||||
end
|
||||
if editor then
|
||||
jumpline = tonumber(jumpline)
|
||||
jumplinepos = tonumber(jumplinepos)
|
||||
|
||||
-- doubleclick can set selection, so reset it;
|
||||
-- for consistency, do it even when no pattern is detected.
|
||||
editor:GotoPos(editor:PositionFromLine(math.max(0,jumpline-1))
|
||||
+ (jumplinepos and (math.max(0,jumplinepos-1)) or 0))
|
||||
editor:EnsureVisibleEnforcePolicy(jumpline)
|
||||
editor:SetFocus()
|
||||
end
|
||||
|
||||
-- doubleclick can set selection, so reset it
|
||||
local pos = event:GetPosition()
|
||||
if pos == -1 then pos = errorlog:GetLineEndPosition(event:GetLine()) end
|
||||
errorlog:SetSelection(pos, pos)
|
||||
|
||||
@@ -79,16 +79,23 @@ function ide:GetStatusBar() return self.frame.statusBar end
|
||||
function ide:GetToolBar() return self.frame.toolBar end
|
||||
function ide:GetDebugger() return self.debugger end
|
||||
function ide:GetMainFrame() return self.frame end
|
||||
function ide:GetUIManager() return self.frame.uimgr end
|
||||
function ide:GetDocument(ed) return self.openDocuments[ed:GetId()] end
|
||||
function ide:GetDocuments() return self.openDocuments end
|
||||
function ide:FindMenuItem(menu, itemid)
|
||||
function ide:FindMenuItem(itemid, menu)
|
||||
local item, imenu = ide:GetMenuBar():FindItem(itemid, menu)
|
||||
if menu and not item then item = menu:FindItem(itemid) end
|
||||
if not item then return end
|
||||
menu = menu or imenu
|
||||
|
||||
for pos = 0, menu:GetMenuItemCount()-1 do
|
||||
if menu:FindItemByPosition(pos):GetId() == itemid then
|
||||
return menu:FindItemByPosition(pos), pos
|
||||
return item, menu, pos
|
||||
end
|
||||
end
|
||||
return nil
|
||||
return
|
||||
end
|
||||
|
||||
function ide:FindDocument(path)
|
||||
local fileName = wx.wxFileName(path)
|
||||
for _, doc in pairs(ide.openDocuments) do
|
||||
@@ -124,7 +131,8 @@ function ide:GetProject() return FileTreeGetDir() end
|
||||
function ide:GetLaunchedProcess() return self.debugger and self.debugger.pid end
|
||||
function ide:GetProjectTree() return ide.filetree.projtree end
|
||||
function ide:GetWatch() return self.debugger and self.debugger.watchCtrl end
|
||||
function ide:GetStack() return self.debugger and self.debugger.watchCtrl end
|
||||
function ide:GetStack() return self.debugger and self.debugger.stackCtrl end
|
||||
function ide:Yield() wx.wxYield() end
|
||||
|
||||
function ide:GetSetting(path, setting)
|
||||
local settings = self.settings
|
||||
@@ -135,6 +143,52 @@ function ide:GetSetting(path, setting)
|
||||
return ok and value or nil
|
||||
end
|
||||
|
||||
function ide:RemoveMenuItem(id, menu)
|
||||
local _, menu, pos = ide:FindMenuItem(id, menu)
|
||||
if menu then
|
||||
menu:Disconnect(id, wx.wxID_ANY, wx.wxEVT_COMMAND_MENU_SELECTED)
|
||||
menu:Disconnect(id, wx.wxID_ANY, wx.wxEVT_UPDATE_UI)
|
||||
menu:Remove(id)
|
||||
|
||||
local positem = menu:FindItemByPosition(pos)
|
||||
if (not positem or positem:GetKind() == wx.wxITEM_SEPARATOR)
|
||||
and (menu:FindItemByPosition(pos-1):GetKind() == wx.wxITEM_SEPARATOR) then
|
||||
menu:Destroy(menu:FindItemByPosition(pos-1))
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ide:AddWatch(watch, value)
|
||||
local mgr = ide.frame.uimgr
|
||||
local pane = mgr:GetPane("watchpanel")
|
||||
if (pane:IsOk() and not pane:IsShown()) then
|
||||
pane:Show()
|
||||
mgr:Update()
|
||||
end
|
||||
|
||||
local watchCtrl = ide.debugger.watchCtrl
|
||||
if not watchCtrl then return end
|
||||
|
||||
local root = watchCtrl:GetRootItem()
|
||||
if not root or not root:IsOk() then return end
|
||||
|
||||
local item = watchCtrl:GetFirstChild(root)
|
||||
while true do
|
||||
if not item:IsOk() then break end
|
||||
if watchCtrl:GetItemExpression(item) == watch then
|
||||
if value then watchCtrl:SetItemText(item, watch .. ' = ' .. tostring(value)) end
|
||||
return item
|
||||
end
|
||||
item = watchCtrl:GetNextSibling(item)
|
||||
end
|
||||
|
||||
local item = watchCtrl:AppendItem(root, watch, 1)
|
||||
watchCtrl:SetItemExpression(item, watch, value)
|
||||
return item
|
||||
end
|
||||
|
||||
function ide:AddInterpreter(name, interpreter)
|
||||
self.interpreters[name] = setmetatable(interpreter, ide.proto.Interpreter)
|
||||
UpdateInterpreters()
|
||||
@@ -181,3 +235,25 @@ function ide:RemoveConfig(name)
|
||||
configcache[name] = nil -- clear the slot after use
|
||||
ReApplySpecAndStyles() -- apply current config to the UI
|
||||
end
|
||||
|
||||
function ide:AddPanel(ctrl, panel, name, conf)
|
||||
local width, height = 360, 200
|
||||
local notebook = wxaui.wxAuiNotebook(ide.frame, wx.wxID_ANY,
|
||||
wx.wxDefaultPosition, wx.wxDefaultSize,
|
||||
wxaui.wxAUI_NB_DEFAULT_STYLE + wxaui.wxAUI_NB_TAB_EXTERNAL_MOVE
|
||||
- wxaui.wxAUI_NB_CLOSE_ON_ACTIVE_TAB + wx.wxNO_BORDER)
|
||||
notebook:AddPage(ctrl, name, true)
|
||||
notebook:Connect(wxaui.wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK,
|
||||
function() PaneFloatToggle(notebook) end)
|
||||
|
||||
local mgr = ide.frame.uimgr
|
||||
mgr:AddPane(notebook, wxaui.wxAuiPaneInfo():
|
||||
Name(panel):Float():CaptionVisible(false):PaneBorder(false):
|
||||
MinSize(width/2,height/2):
|
||||
BestSize(width,height):FloatingSize(width,height):
|
||||
PinButton(true):Hide())
|
||||
if type(conf) == "function" then conf(mgr:GetPane(panel)) end
|
||||
mgr.defaultPerspective = mgr:SavePerspective() -- resave default perspective
|
||||
|
||||
return notebook
|
||||
end
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
ide.proto.Document = {__index = {
|
||||
GetFileName = function(self) return self.fileName end,
|
||||
GetFilePath = function(self) return self.filePath end,
|
||||
GetFileExt = function(self) return GetFileExt(self.fileName) end,
|
||||
GetModTime = function(self) return self.modTime end,
|
||||
GetEditor = function(self) return self.editor end,
|
||||
GetTabIndex = function(self) return self.index end,
|
||||
@@ -28,4 +29,6 @@ ide.proto.Interpreter = {__index = {
|
||||
ide.proto.Debugger = {__index = {
|
||||
IsRunning = function(self) return self.running end,
|
||||
IsConnected = function(self) return self.server end,
|
||||
GetHostName = function(self) return self.hostname end,
|
||||
GetPortNumber = function(self) return self.portnumber end,
|
||||
}}
|
||||
|
||||
@@ -8,7 +8,8 @@ local ide = ide
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- Initialize the wxConfig for loading/saving the preferences
|
||||
|
||||
local settings = wx.wxFileConfig(GetIDEString("settingsapp"),GetIDEString("settingsvendor"))
|
||||
local settings = wx.wxFileConfig(GetIDEString("settingsapp"),
|
||||
GetIDEString("settingsvendor"), ide.config.ini or "")
|
||||
ide.settings = settings
|
||||
|
||||
local function settingsReadSafe(settings,what,default)
|
||||
@@ -391,7 +392,18 @@ function SettingsRestoreView()
|
||||
local layoutcur = uimgr:SavePerspective()
|
||||
local layout = settingsReadSafe(settings,"uimgrlayout",layoutcur)
|
||||
if (layout ~= layoutcur) then
|
||||
-- save the current toolbar configuration and restore re-apply it
|
||||
-- as it's always correct (to avoid storing minh and minw values)
|
||||
local toolbar = frame.uimgr:GetPane("toolbar")
|
||||
local toolbarlayout = (toolbar:IsOk()
|
||||
-- layout includes bestw that is only as wide as the toolbar size,
|
||||
-- this leaves default background on the right side of the toolbar;
|
||||
-- fix it by explicitly replacing with the screen width
|
||||
and uimgr:SavePaneInfo(toolbar):gsub("(bestw=)[^;]+",
|
||||
function(s) return s..wx.wxSystemSettings.GetMetric(wx.wxSYS_SCREEN_X) end)
|
||||
or nil)
|
||||
uimgr:LoadPerspective(layout, false)
|
||||
if toolbarlayout then uimgr:LoadPaneInfo(toolbarlayout, toolbar) end
|
||||
|
||||
-- check if debugging panes are not mentioned and float them
|
||||
for _, name in pairs({"stackpanel", "watchpanel"}) do
|
||||
@@ -442,6 +454,24 @@ function SettingsRestoreView()
|
||||
end
|
||||
end
|
||||
|
||||
-- restore configuration for notebook pages that have been split;
|
||||
-- load saved dock_size values and update current values with saved ones
|
||||
-- where dock_size configuration matches
|
||||
local docksizemask = '(dock_size[^=]+=)(%d+)'
|
||||
for l, m in pairs({
|
||||
nbdocklayout = frame.notebook:GetAuiManager(),
|
||||
nbbtmdocklayout = frame.bottomnotebook:GetAuiManager(),
|
||||
}) do
|
||||
-- ...|dock_size(5,0,0)=20|dock_size(2,1,0)=200|...
|
||||
local prevlayout = settingsReadSafe(settings, l, "")
|
||||
local curlayout = m:SavePerspective()
|
||||
local newlayout = curlayout:gsub('(dock_size[^=]+=)(%d+)', function(t,v)
|
||||
local val = prevlayout:match(EscapeMagic(t)..'(%d+)')
|
||||
return t..(val or v)
|
||||
end)
|
||||
if newlayout ~= curlayour then m:LoadPerspective(newlayout) end
|
||||
end
|
||||
|
||||
local editor = GetEditor()
|
||||
if editor then editor:SetFocus() end
|
||||
|
||||
@@ -457,10 +487,12 @@ function SettingsSaveView()
|
||||
local frame = ide.frame
|
||||
local uimgr = frame.uimgr
|
||||
|
||||
settings:Write("uimgrlayout",uimgr:SavePerspective())
|
||||
settings:Write("nblayout", saveNotebook(frame.notebook))
|
||||
settings:Write("nbbtmlayout",saveNotebook(frame.bottomnotebook))
|
||||
settings:Write("statusbar",frame:GetStatusBar():IsShown())
|
||||
settings:Write("uimgrlayout", uimgr:SavePerspective())
|
||||
settings:Write("nblayout", saveNotebook(frame.notebook))
|
||||
settings:Write("nbbtmlayout", saveNotebook(frame.bottomnotebook))
|
||||
settings:Write("statusbar", frame:GetStatusBar():IsShown())
|
||||
settings:Write("nbdocklayout", frame.notebook:GetAuiManager():SavePerspective())
|
||||
settings:Write("nbbtmdocklayout", frame.bottomnotebook:GetAuiManager():SavePerspective())
|
||||
|
||||
settings:SetPath(path)
|
||||
end
|
||||
|
||||
@@ -17,14 +17,11 @@ probably a pitfal: an instance is running but is not visible
|
||||
|
||||
if not ide.config.singleinstance then return end
|
||||
|
||||
require "socket"
|
||||
|
||||
local socket = require "socket"
|
||||
local port = ide.config.singleinstanceport
|
||||
local delay = tonumber(ide.config.singleinstance) or 1000 -- in ms
|
||||
local svr = socket.udp()
|
||||
|
||||
local success, errmsg = svr:setsockname("127.0.0.1",port) -- bind on local host
|
||||
|
||||
local success = svr:setsockname("127.0.0.1",port) -- bind on local host
|
||||
local protocol = {client = {}, server = {}}
|
||||
|
||||
protocol.client.greeting = "Is this you, my IDE? It's me, a new instance."
|
||||
@@ -37,15 +34,14 @@ if success then -- ok, server was started, we are solo
|
||||
ide.idletimer = wx.wxTimer(wx.wxGetApp())
|
||||
ide.idletimer:Start(delay,false)
|
||||
svr:settimeout(0) -- don't block
|
||||
wx.wxGetApp():Connect(wx.wxEVT_TIMER,function (evt)
|
||||
wx.wxGetApp():Connect(wx.wxEVT_TIMER, function()
|
||||
if ide.exitingProgram then -- if exiting, terminate the timer loop
|
||||
wx.wxGetApp():Disconnect(wx.wxEVT_TIMER)
|
||||
return
|
||||
end
|
||||
|
||||
local msg, err, port = svr:receivefrom() -- receive a msg
|
||||
local msg, ip, port = svr:receivefrom() -- receive a msg
|
||||
if msg then
|
||||
local ip = err -- the errmsg is actually the IP
|
||||
if msg == protocol.client.greeting then -- just send back hi
|
||||
svr:sendto(protocol.server.greeting,ip,port)
|
||||
elseif msg:match(protocol.client.requestloading:gsub("%%s",".+$")) then -- ok we need to open something
|
||||
@@ -53,16 +49,12 @@ if success then -- ok, server was started, we are solo
|
||||
local filename = msg:match(protocol.client.requestloading:gsub("%%s","(.+)$"))
|
||||
if filename then
|
||||
RequestAttention()
|
||||
local done = true
|
||||
if wx.wxDirExists(filename) then
|
||||
local dir = wx.wxFileName.DirName(filename)
|
||||
dir:Normalize() -- turn into absolute path if needed
|
||||
ProjectUpdateProjectDir(dir:GetFullPath())
|
||||
else
|
||||
done = LoadFile(filename, nil, true)
|
||||
end
|
||||
if not done then
|
||||
DisplayOutputLn("Can't open requested file '"..filename.."'.")
|
||||
elseif not ActivateFile(filename) then
|
||||
DisplayOutputLn(TR("Can't open file '%s': %s"):format(filename, wx.wxSysErrorMsg()))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -74,7 +66,7 @@ else -- something different is running on our port
|
||||
cln:settimeout(2)
|
||||
cln:send(protocol.client.greeting)
|
||||
|
||||
local msg,err = cln:receive()
|
||||
local msg = cln:receive()
|
||||
local arg = ide.arg
|
||||
if msg and msg == protocol.server.greeting then
|
||||
local failed = false
|
||||
@@ -85,7 +77,7 @@ else -- something different is running on our port
|
||||
and (ide.osname ~= 'Macintosh' or not fileName:find("^-psn")) then
|
||||
cln:send(protocol.client.requestloading:format(fileName))
|
||||
|
||||
local msg,err = cln:receive()
|
||||
local msg, err = cln:receive()
|
||||
if msg ~= protocol.server.answerok then
|
||||
failed = true
|
||||
print(err,msg)
|
||||
|
||||
@@ -89,9 +89,9 @@ function StylesGetDefault()
|
||||
end
|
||||
|
||||
local markers = {
|
||||
breakpoint = {0, wxstc.wxSTC_MARK_CIRCLE, wx.wxColour(220, 64, 64), wx.wxColour(220, 64, 64)},
|
||||
bookmark = {1, wxstc.wxSTC_MARK_SHORTARROW, wx.wxBLACK, wx.wxColour(96, 160, 220)},
|
||||
currentline = {2, wxstc.wxSTC_MARK_ARROW, wx.wxBLACK, wx.wxColour(64, 220, 64)},
|
||||
breakpoint = {0, wxstc.wxSTC_MARK_CIRCLE, wx.wxColour(196, 64, 64), wx.wxColour(220, 64, 64)},
|
||||
bookmark = {1, wxstc.wxSTC_MARK_SHORTARROW, wx.wxColour(16, 96, 128), wx.wxColour(96, 160, 220)},
|
||||
currentline = {2, wxstc.wxSTC_MARK_ARROW, wx.wxColour(16, 128, 16), wx.wxColour(64, 220, 64)},
|
||||
message = {3, wxstc.wxSTC_MARK_CHARACTER+(' '):byte(), wx.wxBLACK, wx.wxColour(220, 220, 220)},
|
||||
output = {4, wxstc.wxSTC_MARK_BACKGROUND, wx.wxBLACK, wx.wxColour(240, 240, 240)},
|
||||
prompt = {5, wxstc.wxSTC_MARK_ARROWS, wx.wxBLACK, wx.wxColour(220, 220, 220)},
|
||||
@@ -244,7 +244,12 @@ local specialmapping = {
|
||||
local panes = uimgr:GetAllPanes()
|
||||
for index = 0, panes:GetCount()-1 do
|
||||
local wind = uimgr:GetPane(panes:Item(index).name).window
|
||||
local children = wind:GetChildren()
|
||||
|
||||
-- wxlua compiled with STL doesn't provide GetChildren() method
|
||||
-- as per http://sourceforge.net/p/wxlua/mailman/message/32500995/
|
||||
local ok, children = pcall(function() return wind:GetChildren() end)
|
||||
if not ok then break end
|
||||
|
||||
for child = 0, children:GetCount()-1 do
|
||||
local data = children:Item(child):GetData()
|
||||
local _, window = pcall(function() return data:DynamicCast("wxWindow") end)
|
||||
@@ -311,8 +316,8 @@ function StylesApplyToEditor(styles,editor,font,fontitalic,lexerconvert)
|
||||
editor:StyleClearAll()
|
||||
|
||||
-- set the default linenumber font size based on the editor font size
|
||||
if styles.linenumber then
|
||||
styles.linenumber.fs = styles.linenumber.fs or ide.config.editor.fontsize - 1
|
||||
if styles.linenumber and not styles.linenumber.fs then
|
||||
styles.linenumber.fs = ide.config.editor.fontsize and (ide.config.editor.fontsize - 1) or nil
|
||||
end
|
||||
|
||||
for name,style in pairs(styles) do
|
||||
@@ -376,7 +381,7 @@ function ReApplySpecAndStyles()
|
||||
local openDocuments = ide.openDocuments
|
||||
for i,doc in pairs(openDocuments) do
|
||||
if (doc.editor.spec) then
|
||||
SetupKeywords(doc.editor,nil,doc.editor.spec)
|
||||
doc.editor:SetupKeywords(nil,doc.editor.spec)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
40
src/editor/toolbar.lua
Normal file
@@ -0,0 +1,40 @@
|
||||
-- Copyright 2014 Paul Kulchenko, ZeroBrane LLC
|
||||
|
||||
ide.config.toolbar.icons = {
|
||||
ID_NEW, ID_OPEN, ID_SAVE, ID_SAVEALL, ID_PROJECTDIRFROMFILE, ID_PROJECTDIRCHOOSE,
|
||||
ID_SEPARATOR,
|
||||
ID_FIND, ID_REPLACE, ID_FINDINFILES,
|
||||
ID_SEPARATOR,
|
||||
ID_RUN, ID_STARTDEBUG, ID_RUNNOW, ID_STOPDEBUG, ID_DETACHDEBUG, ID_BREAK, ID_STEP, ID_STEPOVER, ID_STEPOUT,
|
||||
ID_SEPARATOR,
|
||||
ID_TOGGLEBREAKPOINT, ID_BOOKMARKTOGGLE, ID_VIEWCALLSTACK, ID_VIEWWATCHWINDOW,
|
||||
ID_SEPARATOR,
|
||||
[ID_FINDINFILES] = false,
|
||||
[ID_RUN] = false,
|
||||
[ID_RUNNOW] = false,
|
||||
}
|
||||
|
||||
ide.config.toolbar.iconmap = {
|
||||
[ID_NEW] = {"NORMAL-FILE", "Create an empty document"},
|
||||
[ID_OPEN] = {"FILE-OPEN", "Open an existing document"},
|
||||
[ID_SAVE] = {"FILE-SAVE", "Save the current document"},
|
||||
[ID_SAVEALL] = {"NEW-DIR", "Save all open documents"},
|
||||
[ID_PROJECTDIRFROMFILE]= {"GO-DIR-UP", "Set project directory from current file"},
|
||||
[ID_PROJECTDIRCHOOSE] = {"DIR-SETUP", "Choose a project directory"},
|
||||
[ID_FIND] = {"FIND", "Find text"},
|
||||
[ID_REPLACE] = {"FIND-AND-REPLACE", "Find and replace text"},
|
||||
[ID_FINDINFILES] = {"FIND-IN-FILES", "Find in files"},
|
||||
[ID_RUN] = {"RUN", "Run"},
|
||||
[ID_RUNNOW] = {"RUN-NOW", "Run as Scratchpad"},
|
||||
[ID_STARTDEBUG] = {"DEBUG-START", "Start or Continue debugging"},
|
||||
[ID_STOPDEBUG] = {"DEBUG-STOP", "Stop the currently running process"},
|
||||
[ID_DETACHDEBUG]= {"DEBUG-DETACH", "Stop debugging and continue running the process"},
|
||||
[ID_BREAK] = {"DEBUG-BREAK", "Break execution at the next executed line of code"},
|
||||
[ID_STEP] = {"DEBUG-STEP-INTO", "Step into"},
|
||||
[ID_STEPOVER] = {"DEBUG-STEP-OVER", "Step over"},
|
||||
[ID_STEPOUT] = {"DEBUG-STEP-OUT", "Step out of the current function"},
|
||||
[ID_TOGGLEBREAKPOINT] = {"DEBUG-BREAKPOINT-TOGGLE", "Toggle breakpoint"},
|
||||
[ID_BOOKMARKTOGGLE] = {"BOOKMARK-TOGGLE", "Toggle bookmark"},
|
||||
[ID_VIEWCALLSTACK] = {"DEBUG-CALLSTACK", "View the stack window"},
|
||||
[ID_VIEWWATCHWINDOW] = {"DEBUG-WATCH", "View the watch window"},
|
||||
}
|
||||
61
src/main.lua
@@ -42,6 +42,7 @@ ide = {
|
||||
editor = {
|
||||
foldcompact = true,
|
||||
checkeol = true,
|
||||
saveallonrun = false,
|
||||
},
|
||||
debugger = {
|
||||
verbose = false,
|
||||
@@ -56,9 +57,16 @@ ide = {
|
||||
interpreter = 'luadeb',
|
||||
},
|
||||
outputshell = {},
|
||||
filetree = {},
|
||||
filetree = {
|
||||
mousemove = true,
|
||||
},
|
||||
funclist = {},
|
||||
|
||||
toolbar = {
|
||||
icons = {},
|
||||
iconmap = {},
|
||||
},
|
||||
|
||||
keymap = {},
|
||||
messages = {},
|
||||
language = "en",
|
||||
@@ -85,13 +93,14 @@ ide = {
|
||||
unhidewindow = false, -- to unhide a gui window
|
||||
allowinteractivescript = false, -- allow interaction in the output window
|
||||
filehistorylength = 20,
|
||||
projecthistorylength = 15,
|
||||
projecthistorylength = 20,
|
||||
savebak = false,
|
||||
singleinstance = false,
|
||||
singleinstanceport = 0xe493,
|
||||
-- HiDPI/Retina display support;
|
||||
-- `false` by default because of issues with indicators with alpha setting
|
||||
hidpi = false,
|
||||
hotexit = false,
|
||||
},
|
||||
specs = {
|
||||
none = {
|
||||
@@ -180,7 +189,7 @@ end
|
||||
|
||||
dofile "src/version.lua"
|
||||
|
||||
for _, file in ipairs({"ids", "style", "keymap", "proto"}) do
|
||||
for _, file in ipairs({"ids", "style", "keymap", "proto", "toolbar"}) do
|
||||
dofile("src/editor/"..file..".lua")
|
||||
end
|
||||
|
||||
@@ -255,7 +264,9 @@ do
|
||||
for index = 2, #arg do
|
||||
if (arg[index] == "-cfg" and index+1 <= #arg) then
|
||||
table.insert(configs,arg[index+1])
|
||||
elseif arg[index-1] ~= "-cfg" then
|
||||
elseif arg[index-1] ~= "-cfg"
|
||||
-- on OSX command line includes -psn... parameter, don't include these
|
||||
and (ide.osname ~= 'Macintosh' or not arg[index]:find("^-psn")) then
|
||||
table.insert(filenames,arg[index])
|
||||
end
|
||||
end
|
||||
@@ -303,13 +314,23 @@ local function loadPackages(filter)
|
||||
-- check dependencies and assign file names to each package
|
||||
local unload = {}
|
||||
for fname, package in pairs(ide.packages) do
|
||||
if type(package.dependencies) == 'table'
|
||||
and package.dependencies.osname
|
||||
and not package.dependencies.osname:find(ide.osname, 1, true) then
|
||||
(DisplayOutputLn or print)(
|
||||
("Package '%s' not loaded: requires %s platform, but you are running %s.")
|
||||
:format(fname, package.dependencies.osname, ide.osname)
|
||||
)
|
||||
table.insert(unload, fname)
|
||||
end
|
||||
|
||||
local needsversion = tonumber(package.dependencies)
|
||||
or type(package.dependencies) == 'table' and tonumber(package.dependencies[1])
|
||||
or -1
|
||||
local isversion = tonumber(ide.VERSION)
|
||||
if isversion and needsversion > isversion then
|
||||
(DisplayOutputLn or print)(
|
||||
("Package '%s' not loaded: requires version %s, but you are running version %s")
|
||||
("Package '%s' not loaded: requires version %s, but you are running version %s.")
|
||||
:format(fname, needsversion, ide.VERSION)
|
||||
)
|
||||
table.insert(unload, fname)
|
||||
@@ -373,8 +394,7 @@ end
|
||||
-- process config
|
||||
|
||||
-- set ide.config environment
|
||||
ide.config.os = os
|
||||
ide.config.wxstc = wxstc
|
||||
setmetatable(ide.config, {__index = {os = os, wxstc = wxstc, wx = wx}})
|
||||
ide.config.load = { interpreters = loadInterpreters, specs = loadSpecs,
|
||||
tools = loadTools }
|
||||
do
|
||||
@@ -469,14 +489,14 @@ SettingsRestoreView()
|
||||
-- Load the filenames
|
||||
|
||||
do
|
||||
for _, fileName in ipairs(filenames) do
|
||||
if fileName ~= "--" then
|
||||
if wx.wxDirExists(fileName) then
|
||||
local dir = wx.wxFileName.DirName(fileName)
|
||||
for _, filename in ipairs(filenames) do
|
||||
if filename ~= "--" then
|
||||
if wx.wxDirExists(filename) then
|
||||
local dir = wx.wxFileName.DirName(filename)
|
||||
dir:Normalize() -- turn into absolute path if needed
|
||||
ProjectUpdateProjectDir(dir:GetFullPath())
|
||||
else
|
||||
LoadFile(fileName, nil, true)
|
||||
elseif not ActivateFile(filename) then
|
||||
DisplayOutputLn(("Can't open file '%s': %s"):format(filename, wx.wxSysErrorMsg()))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -501,6 +521,17 @@ local remap = {
|
||||
[ID_RENAMEFILE] = ide:GetProjectTree(),
|
||||
[ID_DELETEFILE] = ide:GetProjectTree(),
|
||||
}
|
||||
|
||||
local function rerouteMenuCommand(obj, id)
|
||||
-- check if the conflicting shortcut is enabled:
|
||||
-- (1) SetEnabled wasn't called or (2) Enabled was set to `true`.
|
||||
local uievent = wx.wxUpdateUIEvent(id)
|
||||
obj:ProcessEvent(uievent)
|
||||
if not uievent:GetSetEnabled() or uievent:GetEnabled() then
|
||||
obj:AddPendingEvent(wx.wxCommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, id))
|
||||
end
|
||||
end
|
||||
|
||||
local function remapkey(event)
|
||||
local keycode = event:GetKeyCode()
|
||||
local mod = event:GetModifiers()
|
||||
@@ -508,7 +539,7 @@ local function remapkey(event)
|
||||
if obj:FindFocus():GetId() == obj:GetId() then
|
||||
local ae = wx.wxAcceleratorEntry(); ae:FromString(KSC(id))
|
||||
if ae:GetFlags() == mod and ae:GetKeyCode() == keycode then
|
||||
obj:AddPendingEvent(wx.wxCommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, id))
|
||||
rerouteMenuCommand(obj, id)
|
||||
return
|
||||
end
|
||||
end
|
||||
@@ -534,7 +565,7 @@ local function resolveConflict(localid, globalid)
|
||||
end
|
||||
end
|
||||
end
|
||||
ide.frame:AddPendingEvent(wx.wxCommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, globalid))
|
||||
rerouteMenuCommand(ide.frame, globalid)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -155,8 +155,8 @@ function GetPathWithSep(wxfn)
|
||||
return wxfn:GetPath(bit.bor(wx.wxPATH_GET_VOLUME, wx.wxPATH_GET_SEPARATOR))
|
||||
end
|
||||
|
||||
function FileSysHasContent(dir)
|
||||
local f = wx.wxFindFirstFile(dir,wx.wxFILE + wx.wxDIR)
|
||||
function FileDirHasContent(dir)
|
||||
local f = wx.wxFindFirstFile(dir, wx.wxFILE + wx.wxDIR)
|
||||
return #f>0
|
||||
end
|
||||
|
||||
@@ -269,7 +269,8 @@ function FileCopy(file1, file2)
|
||||
return wx.wxCopyFile(file1, file2), wx.wxSysErrorMsg()
|
||||
end
|
||||
|
||||
TimeGet = pcall(require, "socket") and socket.gettime or os.clock
|
||||
local ok, socket = pcall(require, "socket")
|
||||
TimeGet = ok and socket.gettime or os.clock
|
||||
|
||||
function isBinary(text) return text:find("[^\7\8\9\10\12\13\27\32-\255]") end
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ editor:SetText('print("select")')
|
||||
-- this is to set proper styles, which are needed for EditorCallTip
|
||||
editor:Colourise(0, -1)
|
||||
|
||||
local value
|
||||
local value = ''
|
||||
local CTS = editor.CallTipShow
|
||||
editor.CallTipShow = function(editor, pos, tip) value = tip end
|
||||
EditorCallTip(editor, 10)
|
||||
|
||||
@@ -18,6 +18,10 @@ lualibs/lua_lexer_loose.lua
|
||||
lualibs/lua_parser_loose.lua
|
||||
lualibs/luainspect/*.lua
|
||||
lualibs/metalua/*.lua
|
||||
lualibs/metalua/compiler/*.lua
|
||||
lualibs/metalua/compiler/parser/*.lua
|
||||
lualibs/metalua/compiler/parser/annot/*.lua
|
||||
lualibs/metalua/grammar/*.lua
|
||||
lualibs/mobdebug/mobdebug.lua
|
||||
lualibs/ltn12.lua
|
||||
lualibs/luadist.lua
|
||||
|
||||
@@ -7,6 +7,7 @@ editor.usewrap = true
|
||||
editor.calltipdelay = 500
|
||||
editor.smartindent = true
|
||||
editor.fold = true
|
||||
editor.autoreload = true
|
||||
|
||||
local G = ... -- this now points to the global environment
|
||||
local mac = G.ide.osname == 'Macintosh'
|
||||
@@ -34,7 +35,6 @@ else
|
||||
end
|
||||
|
||||
outputshell.usewrap = true
|
||||
filehistorylength = 20
|
||||
|
||||
hidpi = mac -- support Retina displays by default (OSX)
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 884 B After Width: | Height: | Size: 884 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 374 B After Width: | Height: | Size: 374 B |
|
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 702 B |
|
Before Width: | Height: | Size: 500 B After Width: | Height: | Size: 500 B |
|
Before Width: | Height: | Size: 583 B After Width: | Height: | Size: 583 B |
|
Before Width: | Height: | Size: 259 B After Width: | Height: | Size: 259 B |
|
Before Width: | Height: | Size: 331 B After Width: | Height: | Size: 331 B |
|
Before Width: | Height: | Size: 357 B After Width: | Height: | Size: 357 B |
|
Before Width: | Height: | Size: 353 B After Width: | Height: | Size: 353 B |
|
Before Width: | Height: | Size: 594 B After Width: | Height: | Size: 594 B |
|
Before Width: | Height: | Size: 786 B After Width: | Height: | Size: 786 B |
|
Before Width: | Height: | Size: 738 B After Width: | Height: | Size: 738 B |
|
Before Width: | Height: | Size: 602 B After Width: | Height: | Size: 602 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 451 B After Width: | Height: | Size: 451 B |
|
Before Width: | Height: | Size: 671 B After Width: | Height: | Size: 671 B |
|
Before Width: | Height: | Size: 662 B After Width: | Height: | Size: 662 B |
|
Before Width: | Height: | Size: 615 B After Width: | Height: | Size: 615 B |
|
Before Width: | Height: | Size: 778 B After Width: | Height: | Size: 778 B |
|
Before Width: | Height: | Size: 717 B After Width: | Height: | Size: 717 B |
|
Before Width: | Height: | Size: 623 B After Width: | Height: | Size: 623 B |
|
Before Width: | Height: | Size: 698 B After Width: | Height: | Size: 698 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 652 B After Width: | Height: | Size: 652 B |
|
Before Width: | Height: | Size: 730 B After Width: | Height: | Size: 730 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 753 B After Width: | Height: | Size: 753 B |
BIN
zbstudio/res/16/RUN-NOW.png
Normal file
|
After Width: | Height: | Size: 294 B |
BIN
zbstudio/res/16/RUN.png
Normal file
|
After Width: | Height: | Size: 271 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 473 B After Width: | Height: | Size: 473 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 566 B After Width: | Height: | Size: 566 B |
|
Before Width: | Height: | Size: 755 B After Width: | Height: | Size: 755 B |
|
Before Width: | Height: | Size: 369 B After Width: | Height: | Size: 369 B |
|
Before Width: | Height: | Size: 425 B After Width: | Height: | Size: 425 B |
|
Before Width: | Height: | Size: 428 B After Width: | Height: | Size: 428 B |
|
Before Width: | Height: | Size: 447 B After Width: | Height: | Size: 447 B |
|
Before Width: | Height: | Size: 799 B After Width: | Height: | Size: 799 B |
|
Before Width: | Height: | Size: 1008 B After Width: | Height: | Size: 1008 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 780 B After Width: | Height: | Size: 780 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 495 B After Width: | Height: | Size: 495 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 775 B After Width: | Height: | Size: 775 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 622 B After Width: | Height: | Size: 622 B |
|
Before Width: | Height: | Size: 821 B After Width: | Height: | Size: 821 B |
BIN
zbstudio/res/24/RUN-NOW.png
Normal file
|
After Width: | Height: | Size: 400 B |
BIN
zbstudio/res/24/RUN.png
Normal file
|
After Width: | Height: | Size: 358 B |