Compare commits

...

80 Commits
0.29 ... 0.31

Author SHA1 Message Date
Paul Kulchenko
7b2241085d Added refresh of filetree on MacOS to get it displayed correctly when the app is started 2012-07-14 11:45:57 -07:00
Paul Kulchenko
c9cfd42d34 Corrected resetting of project directory when it's already set and doesn't need to be changed 2012-07-13 23:30:13 -07:00
Paul Kulchenko
c25c56069d Added handling of balanced brackets in markup links 2012-07-13 22:43:27 -07:00
Paul Kulchenko
a68e8f7bd3 Added unit test module 2012-07-13 22:42:35 -07:00
Paul Kulchenko
fc6de036e0 Reorganized handling of font configuration and added font config for filetree (with a different size default on MacOS) 2012-07-13 09:54:52 -07:00
Paul Kulchenko
08d42a2501 Removed setting the editor font in the config as the default font is different on different platforms 2012-07-12 22:07:10 -07:00
Paul Kulchenko
da32878984 Added reporting the number of traced lines during debugging 2012-07-12 14:21:56 -07:00
Paul Kulchenko
3fd5d88656 Reset project directory if the current one doesn't exist 2012-07-11 23:16:33 -07:00
Paul Kulchenko
7d9ad5100c Fixed markup styling and file tree drawing on MacOS 2012-07-11 16:11:11 -07:00
Paul Kulchenko
6bff634446 Added explicit calls to CreateBitmap as wxArtProvider.GetBitmap doesn't alway call a custom art provider on MacOS (2.8.12) and in some cases on msWin (2.8.7) 2012-07-11 13:24:45 -07:00
Paul Kulchenko
b0de487bf0 Fixed detecting executable name in commands with spaces 2012-07-11 13:21:22 -07:00
Paul Kulchenko
560d56835a Fixed typo 2012-07-11 13:20:35 -07:00
Paul Kulchenko
3c6a06f537 Added setting of PATH and CPATH to find proper libs on windows and mac os platforms 2012-07-11 11:24:33 -07:00
Paul Kulchenko
b1f3bf0bd9 Fixed incorrect folders reported in the file tree when no project directory is set and a file is open 2012-07-10 23:32:38 -07:00
Paul Kulchenko
6ea3a4708d Fixed incorrect filename reported in compile errors when the file is not saved 2012-07-10 22:31:06 -07:00
Paul Kulchenko
4de0eb1dc0 Made 'View Stack Window' and 'View Watch Window' refresh window content if it's already shown 2012-07-07 15:08:58 -07:00
Paul Kulchenko
c2f6ed6338 Removed extension from the template to match folders to make it more portable 2012-07-07 11:49:30 -07:00
Paul Kulchenko
fad7ff0cc3 Updated matching of links to make them less greedy (to avoid capturing link terminators) 2012-07-07 11:46:41 -07:00
Paul Kulchenko
9afa35f9b2 Upgraded deprecated constants and logic for compatibility with wxwidgets 2.9.x 2012-07-06 20:54:11 -07:00
Paul Kulchenko
3a5b46f65e Fixed an error thrown when a window with debugging is closed before the application being debugged is terminated 2012-07-06 20:51:01 -07:00
Paul Kulchenko
26d0908c89 Added scratchpad support for love2d 2012-07-03 13:00:18 -07:00
Paul Kulchenko
770db579d9 Added reset of 'modified' status to keep tab names and their config settings correct upon exit 2012-07-03 12:47:18 -07:00
Paul Kulchenko
8e09ed1658 Fixed incorrect storing of settings for editor tabs with the same text (filename). This was causing only one tab displayed for multiple StyledText controls with interesting effects 2012-07-02 11:53:24 -07:00
Paul Kulchenko
25ac96d39a Removed styling of function calls and capturing definitions in strings and comments (fixed #18) 2012-07-01 22:11:03 -07:00
Paul Kulchenko
22fda661ec Updated matching logic for function definitions to allow for a.b.c() definitions (fixes #17) 2012-07-01 21:59:13 -07:00
Paul Kulchenko
529a0e8c9e Added window title update and filetree refresh after SaveAs command 2012-07-01 15:13:12 -07:00
Paul Kulchenko
7cc061e18d Fixed an issue with launching a process when its output is not redirected to the IDE (fixes #16). Added callback for all started processes to make them call OnTerminate event upon completion 2012-07-01 09:44:00 -07:00
Paul Kulchenko
5f2226bac2 Fixed console to evaluate 'function a() ... end' without errors 2012-06-30 11:12:30 -07:00
Paul Kulchenko
37795c2480 Added tooltip to display variable/expression values during debugging 2012-06-30 11:04:48 -07:00
Paul Kulchenko
5e934dfd69 Added checks around ShowFullScreen() calls to avoid failures on those systems that don't provide it (linux/GTK) 2012-06-29 23:39:53 -07:00
Paul Kulchenko
f970ba38a0 Removed setting focus to the Output window when output is processed as it interfered with Run as Scratchpad 2012-06-29 23:35:35 -07:00
Paul Kulchenko
8af6c1b5b6 Fixed a compilation error caused by shebang in scripts 2012-06-29 16:58:25 -07:00
Paul Kulchenko
0777a5fbd8 Added check for debugger calls to avoid errors when debugger is not loaded 2012-06-29 15:31:09 -07:00
Paul Kulchenko
942681e246 Fixed an issue with love2d path with spaces 2012-06-28 20:05:35 -07:00
Paul Kulchenko
ce8f120e48 Improved logic in love2d integration to distinguish Debug and Run commands
(closes #13)
2012-06-28 10:24:13 -07:00
Paul Kulchenko
048fd2349a Updated menus to avoid conflicts with MacOS shortcuts 2012-06-26 22:13:52 -07:00
Paul Kulchenko
2627957e5a Updated logic creating menubar to make it work correctly on MacOS with special Help/About items 2012-06-26 21:32:06 -07:00
Paul Kulchenko
16b0ca0fc7 Updated path handling to better detect how the app is started and to avoid loading dlls on non-windows platforms 2012-06-26 19:29:00 -07:00
Paul Kulchenko
e3189ba38a Updated logic for detecting hostname (used in the debugger) to make sure it is resolvable 2012-06-26 19:11:39 -07:00
Paul Kulchenko
ad48622f68 Fixed aborting running/debugged programs on MacOS by adding MAKE_GROUP_LEADER option to wxExecute 2012-06-26 19:06:14 -07:00
Paul Kulchenko
0a9439ad22 Added display of hierarchical data in Stack window 2012-06-25 18:23:26 -07:00
Paul Kulchenko
a7577d5054 Updated Analyze output 2012-06-25 18:22:20 -07:00
Paul Kulchenko
668cdcb4db Added auto complete for love2d API 2012-06-25 14:47:21 -07:00
Paul Kulchenko
4ec3e87473 Added support for debugging processes running under LuaJIT (requires MobDebug v0.467+) 2012-06-24 22:01:02 -07:00
Paul Kulchenko
90b4f38223 Added love2d support 2012-06-24 17:52:59 -07:00
Paul Kulchenko
c1918e5c38 Removed extensions from launch commands and updated display logic in the Output window 2012-06-24 16:40:10 -07:00
Paul Kulchenko
34baaa9447 Fixed an issue in the logic for setting breakpoints, which ignored breakpoints in luxinia2 debug sessions 2012-06-24 16:00:25 -07:00
Paul Kulchenko
817938593a Fixed logic in the local/remote console that returned incorrect error message on executing code like '%s':format(1); expressions are evaluated first now and the order of evaluation is consistent between local and remote consoles 2012-06-23 23:55:59 -07:00
Paul Kulchenko
b944f9d01d Fixed IDs for Project menu items to allow them to be removed from the menu if needed 2012-06-23 23:53:34 -07:00
Paul Kulchenko
5d4b2395b5 Added execution time and updated messages in the Output window to be more consistent 2012-06-23 13:33:29 -07:00
Paul Kulchenko
f9ab1546ff Added displaying 'nil' values in local console when no result is returned by an expression 2012-06-20 21:36:04 -07:00
Paul Kulchenko
971e7c8316 Disabled Stack and Watch updates when scratchpad is active as they interfere with application execution 2012-06-20 21:30:42 -07:00
Paul Kulchenko
60a89b8f32 Fixed an app running without debugger when 'stack' command is sent immediately after an external application connects to the debugger 2012-06-20 19:21:25 -07:00
Paul Kulchenko
7275f0d11a Fixed an issue with remote application not terminating when IDE is closed while debugging is in progress 2012-06-20 18:19:40 -07:00
Paul Kulchenko
35213d7d3b Added a check to refuse starting a new debugging session if there is one in progress already 2012-06-20 17:02:05 -07:00
Paul Kulchenko
cde26253e3 Fixed sending breakpoints set before establishing a debugging session started from another process that connects to the IDE debugger 2012-06-20 16:54:50 -07:00
Paul Kulchenko
b45c0600c7 Added handling of tail calls in the Stack window 2012-06-20 16:33:24 -07:00
Paul Kulchenko
5784dea99a Fixed refreshing a modified file when the editor is set to read-only mode 2012-06-19 12:24:06 -07:00
Paul Kulchenko
e880fdde60 Added pretty printing in Watch and Console (local and remote) windows and handling of multiple results in Console 2012-06-19 09:34:32 -07:00
Paul Kulchenko
b7d8512b7b Added Stack window to display stack information and local/upvalue values for each stack frame 2012-06-18 14:53:10 -07:00
Paul Kulchenko
a7a8f32c91 Fixed saving/restoring configuration of 'Output'/'Console' tabs when IDE is closed while debugging is in progress 2012-06-16 17:31:21 -07:00
Paul Kulchenko
9fc4ab5e9b Fixed removing variable name in Watch window after escaping editing 2012-06-15 16:39:51 -07:00
Paul Kulchenko
37434534cc Fixed #9 as it had incorrect logic in one of UTF filters 2012-06-11 08:24:27 -07:00
Paul Kulchenko
fc03c2843b Fixed ability to set font encoding for 'Output' and 'Console' windows in the config 2012-06-10 16:01:24 -07:00
Paul Kulchenko
2abbad04ac Added ability to set font encoding in the config 2012-06-10 15:40:14 -07:00
Paul Kulchenko
dbb392e009 Moved processing of user.lua to a later phase after tools and specs are
already loaded to allow modification of IDE configuration from `user.lua`.
Closes #5.

Added example to user-sample.lua on how the configuration can be modified.
2012-06-09 18:57:11 -07:00
Paul Kulchenko
65947ff924 Added checks to prevent text modification in 'Output' and 'Console'
windows. Fixes #8.

Text modifications are checked against (1) direct input, (2) actions using
context menu, and (3) move/copy using selection and mouse drag and drop.
Added input marker to the 'Output' window to indicate where input is
expected.
2012-06-09 17:17:24 -07:00
Paul Kulchenko
d8324b269d Fixed edit menu shortcuts to work in the 'Output' window (when allowed) 2012-06-08 16:44:17 -07:00
Paul Kulchenko
ac9c2e9a84 Added ability to interact with scripts by allowing text to be entered in the 'Output' window.
This is controlled by ide.config.allowinteractivescript variable".
 The window is only activated and switched into read-write mode after
 there is some output to it.
2012-06-08 13:07:48 -07:00
Paul Kulchenko
3b2fcfd1f9 Added restoring cursor position when a modified file is reloaded in the editor; removed unnecessary isFileAlteredOnDisk check 2012-06-08 13:01:44 -07:00
Paul Kulchenko
14fe7af771 Fixed reporting of processes that failed to start after 'Run' or 'Debug' commands 2012-06-07 14:13:17 -07:00
Paul Kulchenko
7b0405bc79 Fixed executable path matching to work on systems that don't have file extensions 2012-06-07 13:52:50 -07:00
Paul Kulchenko
fc6b0c176d Cleaned up ShellSupportRemote interface to remove a parameter not being used 2012-06-07 13:51:20 -07:00
Paul Kulchenko
5c50a2645b Improved reporting in static analysis for functions and global variables.
Added "unknown global function...".
Added "unused local function...".
Added "local function ... masks...".
Updated reporting of unused global to "first use of unknown global" to
minimise the number of instances reported until globals assigned through
"required" modules are recognized.
2012-06-05 16:15:02 -07:00
Paul Kulchenko
11cfbfa68d Fixed localization of variables in several places 2012-06-05 15:37:30 -07:00
Paul Kulchenko
e6b318c643 Fixed #3 'unused parameter...' check not to fail on anonymous functions that are part of an expression 2012-06-05 14:06:40 -07:00
Paul Kulchenko
1351db6114 Several fixes to the static code analyzer.
"Unknown global" is only reported once per line.
"First assignment to global..." is fixed to only report when the variable
is on the left side of the assignment.
"Value discarded" warning is added when multiple assignment has more values than
variables.
2012-06-05 11:06:46 -07:00
Paul Kulchenko
b58e931409 Changed static analyzer to only load metalua when Analyze function is
used, rather than on startup.
2012-06-04 12:21:47 -07:00
Paul Kulchenko
e0eb05b122 Disabled 'Run as Scratchpad' if there is no debugger registered capable of running it 2012-06-04 12:15:14 -07:00
Paul Kulchenko
8ecd8dfa6b Changed order of lualibs/ and bin/ directories in package.path and
package.cpath to load included modules first.

This should minimize conflicts with other versions of same modules that
may exist somewhere else in the path.
2012-06-04 12:01:10 -07:00
43 changed files with 5977 additions and 560 deletions

4301
api/lua/love2d.lua Normal file

File diff suppressed because it is too large Load Diff

27
cfg/user-sample.lua Normal file
View File

@@ -0,0 +1,27 @@
--[[-- Rename this file to `user.lua` to get loaded
Configuration files are loaded in the following order
1. <application>\config.lua
2. cfg\user.lua
3. -cfg commandline strings
-- an example of how loaded configuration can be modified from this file
local G = ... -- this now points to the global environment in the script
local luaspec = G.ide.specs['lua']
luaspec.exts[2] = "luaz"
luaspec.keywords[1] = luaspec.keywords[1] .. ' foo'
-- these changes are going to be mapped to ide.config.editor...
-- change encoding to Cyrillic
editor.fontencoding = G.wx.wxFONTENCODING_ISO8859_5
-- or WinCyrillic
editor.fontencoding = G.wx.wxFONTENCODING_CP1251
outputshell.fontencoding = G.wx.wxFONTENCODING_CP1251
-- specify full path to love2d executable; this is only needed
-- if the game folder and the executable are NOT in the same folder.
path.love2d = 'd:/lua/love/love' -- set the path of love executable
--]]--

View File

@@ -1,9 +0,0 @@
--[[--
estrela loads configs in the following order
1. <application>\config.lua
2. cfg\user.lua
3. -cfg commandline strings
--]]--

28
interpreters/love2d.lua Normal file
View File

@@ -0,0 +1,28 @@
return {
name = "Love2d",
description = "Love2d game engine",
api = {"baselib", "love2d"},
frun = function(self,wfilename,rundebug)
if rundebug then DebuggerAttachDefault() end
local love2d = ide.config.path.love2d
or wx.wxFileName(self:fprojdir(wfilename)):GetPath(wx.wxPATH_GET_VOLUME)
.. '/love'
local cmd = ('"%s" "%s"%s'):format(string.gsub(love2d, "\\","/"),
self:fprojdir(wfilename), rundebug and ' -debug' or '')
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
return CommandLineRun(cmd,self:fworkdir(wfilename),true,false,nil,nil,
function() ide.debugger.pid = nil end)
end,
fprojdir = function(self,wfilename)
return wfilename:GetPath(wx.wxPATH_GET_VOLUME)
end,
fworkdir = function (self,wfilename)
return ide.config.path.projectdir
or wfilename:GetPath(wx.wxPATH_GET_VOLUME)
end,
hasdebugger = true,
fattachdebug = function(self)
DebuggerAttachDefault()
end,
scratchextloop = true,
}

View File

@@ -1,22 +0,0 @@
return {
name = "Lua",
description = "Commandline Lua interpreter",
api = {"wxwidgets","baselib"},
frun = function(self,wfilename)
local mainpath = ide.editorFilename:gsub("[^/\\]+$","")
local filepath = wfilename:GetFullPath()
local code = ([[
xpcall(function() dofile '%s' end,
function(err) print(debug.traceback(err)) end)
]]):format(filepath:gsub("\\","/"))
local cmd = '"'..mainpath..'/bin/lua.exe" -e "'..code..'"'
CommandLineRun(cmd,self:fworkdir(wfilename),true,false)
end,
fprojdir = function(self,wfilename)
return wfilename:GetPath(wx.wxPATH_GET_VOLUME)
end,
fworkdir = function (self,wfilename)
return ide.config.path.projectdir and ide.config.path.projectdir:len()>0 and
ide.config.path.projectdir
end,
}

View File

@@ -1,23 +1,33 @@
local mainpath = string.gsub(ide.editorFilename:gsub("[^/\\]+$",""),"\\","/")
local os = ide.osname
local clibs =
os == "Windows" and mainpath.."bin/?.dll;"..mainpath.."bin/clibs/?.dll" or
os == "Macintosh" and mainpath.."bin/lib?.dylib;"..mainpath.."bin/clibs/?.dylib" or
os == "Unix" and mainpath.."bin/?.so;"..mainpath.."bin/clibs/?.so" or nil
wx.wxSetEnv("LUA_PATH", package.path .. ';'
.. mainpath.."lualibs/?/?.lua;"..mainpath.."lualibs/?.lua")
if clibs then wx.wxSetEnv("LUA_CPATH", package.cpath .. ';' .. clibs) end
local macExe = mainpath..'bin/lua.app/Contents/MacOS/lua'
local exe = (os == "Macintosh" and wx.wxFileExists(macExe)
and macExe or mainpath..'bin/lua')
return {
name = "Lua Debug",
description = "Commandline Lua interpreter",
name = "Lua with Debugger",
description = "Lua interpreter with debugger",
api = {"wxwidgets","baselib"},
frun = function(self,wfilename,rundebug)
local mainpath = string.gsub(ide.editorFilename:gsub("[^/\\]+$",""),"\\","/")
local filepath = string.gsub(wfilename:GetFullPath(), "\\","/")
local script
if rundebug then
DebuggerAttachDefault()
script = (
"package.path=package.path..';"..mainpath.."lualibs/?/?.lua;"..mainpath.."lualibs/?.lua';"..
"package.cpath=package.cpath..';"..mainpath.."bin/clibs/?.dll';"..
rundebug
)
script = rundebug
else
script = ([[dofile '%s']]):format(filepath)
end
local code = ([[xpcall(function() io.stdout:setvbuf('no'); %s end,function(err) print(debug.traceback(err)) end)]]):format(script)
local cmd = '"'..mainpath..'bin/lua.exe" -e "'..code..'"'
local cmd = '"'..exe..'" -e "'..code..'"'
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
return CommandLineRun(cmd,self:fworkdir(wfilename),true,false,nil,nil,
function() ide.debugger.pid = nil end)

View File

@@ -52,7 +52,7 @@ return {
local editorDir = string.gsub(ide.editorFilename:gsub("[^/\\]+$",""),"\\","/")
script = ""..
"package.path=package.path..';"..editorDir.."lualibs/?/?.lua';"..
"io.stdout:setvbuf('no'); require('mobdebug').start('" .. wx.wxGetHostName().."',"..ide.debugger.portnumber..")"
"io.stdout:setvbuf('no'); require('mobdebug').start('" .. ide.debugger.hostname.."',"..ide.debugger.portnumber..")"
args = args..' -es "'..script..'"'..startargs
else

View File

@@ -28,8 +28,9 @@
module ("lexer", package.seeall)
require 'metalua.runtime'
-- don't load metalua.runtime as it loads metalua.base, which pollutes
-- global namespace and overwrites pairs/ipairs -- PK 6/4/2012
require 'metalua.table2'
lexer = { alpha={ }, sym={ } }
lexer.__index=lexer

View File

@@ -27,6 +27,6 @@ local keywords = {
"...", "..", "==", ">=", "<=", "~=",
"+{", "-{" }
for w in values(keywords) do mlp_lexer:add(w) end
for _,w in pairs(keywords) do mlp_lexer:add(w) end -- PK 6/4/2012
_M.lexer = mlp_lexer

View File

@@ -1,15 +1,15 @@
--
-- MobDebug 0.449
-- Copyright Paul Kulchenko 2011-2012
-- MobDebug 0.473
-- Copyright 2011-12 Paul Kulchenko
-- Based on RemDebug 1.0 Copyright Kepler Project 2005
-- (http://www.keplerproject.org/remdebug)
--
local mobdebug = {
_NAME = "mobdebug",
_VERSION = 0.473,
_COPYRIGHT = "Paul Kulchenko",
_DESCRIPTION = "Mobile Remote Debugger for the Lua programming language",
_VERSION = "0.449"
port = 8171
}
local coroutine = coroutine
@@ -24,6 +24,7 @@ local setmetatable = setmetatable
local string = string
local tonumber = tonumber
local mosync = mosync
local jit = jit
-- this is a socket class that implements maConnect interface
local function socketMobileLua()
@@ -45,13 +46,14 @@ local function socketMobileLua()
mosync.maWait(0)
mosync.maGetEvent(event)
local eventType = mosync.SysEventGetType(event)
if (mosync.EVENT_TYPE_CLOSE == eventType) then mosync.maExit(0) end
if (mosync.EVENT_TYPE_CONN == eventType and
mosync.SysEventGetConnHandle(event) == connection and
mosync.SysEventGetConnOpType(event) == mosync.CONNOP_CONNECT) then
-- result > 0 ? success : error
if not (mosync.SysEventGetConnResult(event) > 0) then connection = nil end
break
elseif mosync.EventMonitor and mosync.EventMonitor.HandleEvent then
mosync.EventMonitor:HandleEvent(event)
end
end
mosync.SysFree(event)
@@ -84,16 +86,16 @@ local function socketMobileLua()
while true do
local numberOfBytes = stringToBuffer(msg, outBuffer)
mosync.maConnWrite(connection, outBuffer, numberOfBytes)
local result = 0
while true do
mosync.maWait(0)
mosync.maGetEvent(event)
local eventType = mosync.SysEventGetType(event)
if (mosync.EVENT_TYPE_CLOSE == eventType) then mosync.maExit(0) end
if (mosync.EVENT_TYPE_CONN == eventType and
mosync.SysEventGetConnHandle(event) == connection and
mosync.SysEventGetConnOpType(event) == mosync.CONNOP_WRITE) then
break
elseif mosync.EventMonitor and mosync.EventMonitor.HandleEvent then
mosync.EventMonitor:HandleEvent(event)
end
end
self, msg = coroutine.yield()
@@ -110,7 +112,6 @@ local function socketMobileLua()
if len ~= 0 then mosync.maWait(0) end
mosync.maGetEvent(event)
local eventType = mosync.SysEventGetType(event)
if (mosync.EVENT_TYPE_CLOSE == eventType) then mosync.maExit(0) end
if (mosync.EVENT_TYPE_CONN == eventType and
mosync.SysEventGetConnHandle(event) == connection and
mosync.SysEventGetConnOpType(event) == mosync.CONNOP_READ) then
@@ -120,6 +121,8 @@ local function socketMobileLua()
break -- got the event we wanted; now check if we have all we need
elseif len == 0 then
self, len = coroutine.yield(nil)
elseif mosync.EventMonitor and mosync.EventMonitor.HandleEvent then
mosync.EventMonitor:HandleEvent(event)
end
end
end
@@ -151,18 +154,26 @@ local function socketMobileLua()
return self
end
-- overwrite RunEventLoop in MobileLua as it conflicts with the event
-- loop that needs to run to process debugger events (socket read/write).
-- event loop functionality is implemented by calling HandleEvent
-- while waiting for debugger events.
if mosync and mosync.EventMonitor then
mosync.EventMonitor.RunEventLoop = function(self) end
end
local socket = mosync and socketMobileLua() or (require "socket")
local debug = require "debug"
local coro_debugger
local events = { BREAK = 1, WATCH = 2, RESTART = 3 }
local events = { BREAK = 1, WATCH = 2, RESTART = 3, STACK = 4 }
local breakpoints = {}
local watches = {}
local lastsource
local lastfile
local watchescnt = 0
local abort -- default value is nil; this is used in start/loop distinction
local check_break = false
local seen_hook = false
local skip
local skipcount = 0
local step_into = false
@@ -177,6 +188,137 @@ local debugee = function ()
error(deferror)
end
local serpent = (function() ---- include Serpent module for serialization
local n, v = "serpent", 0.15 -- (C) 2012 Paul Kulchenko; MIT License
local c, d = "Paul Kulchenko", "Serializer and pretty printer of Lua data types"
local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'}
local badtype = {thread = true, userdata = true}
local keyword, globals, G = {}, {}, (_G or _ENV)
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
for k,v in pairs(G) do globals[v] = k end -- build func to name mapping
for _,g in ipairs({'coroutine', 'debug', 'io', 'math', 'string', 'table', 'os'}) do
for k,v in pairs(G[g]) do globals[v] = g..'.'..k end end
local function s(t, opts)
local name, indent, fatal = opts.name, opts.indent, opts.fatal
local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge
local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge)
local comm = opts.comment and (tonumber(opts.comment) or math.huge)
local seen, sref, syms, symn = {}, {}, {}, 0
local function gensym(val) return tostring(val):gsub("[^%w]",""):gsub("(%d%w+)",
function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return syms[s] end) end
local function safestr(s) return type(s) == "number" and (huge and snum[tostring(s)] or s)
or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026
or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end
local function comment(s,l) return comm and (l or 0) < comm and ' --[['..tostring(s)..']]' or '' end
local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal
and safestr(tostring(s))..comment('err',l) or error("Can't serialize "..tostring(s)) end
local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r']
local n = name == nil and '' or name
local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n]
local safe = plain and n or '['..safestr(n)..']'
return (path or '')..(plain and path and '.' or '')..safe, safe end
local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or function(o, n)
local maxn, to = tonumber(n) or 12, {number = 'a', string = 'b'}
local function padnum(d) return ("%0"..maxn.."d"):format(d) end
table.sort(o, function(a,b)
return (o[a] and 0 or to[type(a)] or 'z')..(tostring(a):gsub("%d+",padnum))
< (o[b] and 0 or to[type(b)] or 'z')..(tostring(b):gsub("%d+",padnum)) end) end
local function val2str(t, name, indent, path, plainindex, level)
local ttype, level = type(t), (level or 0)
local spath, sname = safename(path, name)
local tag = plainindex and
((type(name) == "number") and '' or name..space..'='..space) or
(name ~= nil and sname..space..'='..space or '')
if seen[t] then
table.insert(sref, spath..space..'='..space..seen[t])
return tag..'nil'..comment('ref', level)
elseif badtype[ttype] then return tag..globerr(t, level)
elseif ttype == 'function' then
seen[t] = spath
local ok, res = pcall(string.dump, t)
local func = ok and ((opts.nocode and "function() end" or
"loadstring("..safestr(res)..",'@serialized')")..comment(t, level))
return tag..(func or globerr(t, level))
elseif ttype == "table" then
if level >= maxl then return tag..'{}'..comment('max', level) end
seen[t] = spath
if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty
local maxn, o, out = #t, {}, {}
for key = 1, maxn do table.insert(o, key) end
for key in pairs(t) do if not o[key] then table.insert(o, key) end end
if opts.sortkeys then alphanumsort(o, opts.sortkeys) end
for n, key in ipairs(o) do
local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse
if opts.ignore and opts.ignore[value] -- skip ignored values; do nothing
or sparse and value == nil then -- skipping nils; do nothing
elseif ktype == 'table' or ktype == 'function' then
if not seen[key] and not globals[key] then
table.insert(sref, 'local '..val2str(key,gensym(key),indent)) end
table.insert(sref, seen[t]..'['..(seen[key] or globals[key] or gensym(key))
..']'..space..'='..space..(seen[value] or val2str(value,nil,indent)))
else
if badtype[ktype] then plainindex, key = true, '['..globerr(key, level+1)..']' end
table.insert(out,val2str(value,key,indent,spath,plainindex,level+1))
end
end
local prefix = string.rep(indent or '', level)
local head = indent and '{\n'..prefix..indent or '{'
local body = table.concat(out, ','..(indent and '\n'..prefix..indent or space))
local tail = indent and "\n"..prefix..'}' or '}'
return (custom and custom(tag,head,body,tail) or tag..head..body..tail)..comment(t, level)
else return tag..safestr(t) end -- handle all other types
end
local sepr = indent and "\n" or ";"..space
local body = val2str(t, name, indent) -- this call also populates sref
local tail = #sref>0 and table.concat(sref, sepr)..sepr or ''
return not name and body or "do local "..body..sepr..tail.."return "..name..sepr.."end"
end
local function merge(a, b) if b then for k,v in pairs(b) do a[k] = v end end; return a; end
return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s,
dump = function(a, opts) return s(a, merge({name = '_', compact = true, sparse = true}, opts)) end,
line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end,
block = function(a, opts) return s(a, merge({indent = ' ', sortkeys = true, comment = true}, opts)) end }
end)() ---- end of Serpent module
local function stack(start)
local function vars(f)
local func = debug.getinfo(f, "f").func
local i = 1
local locals = {}
while true do
local name, value = debug.getlocal(f, i)
if not name then break end
if string.sub(name, 1, 1) ~= '(' then locals[name] = {value, tostring(value)} end
i = i + 1
end
i = 1
local ups = {}
while func and true do -- check for func as it may be nil for tail calls
local name, value = debug.getupvalue(func, i)
if not name then break end
ups[name] = {value, tostring(value)}
i = i + 1
end
return locals, ups
end
local stack = {}
for i = (start or 0), 100 do
local source = debug.getinfo(i, "Snl")
if not source then break end
table.insert(stack, {
{source.name, source.source, source.linedefined,
source.currentline, source.what, source.namewhat, source.short_src},
vars(i+1)})
if source.what == 'main' then break end
end
return stack
end
local function set_breakpoint(file, line)
if file == '-' and lastfile then file = lastfile end
if not breakpoints[file] then
@@ -262,7 +404,32 @@ local function is_safe(stack_level, conservative)
return true
end
local function in_debugger()
local this = debug.getinfo(1, "S").source
-- only need to check few frames as mobdebug frames should be close
for i = 3, 7 do
local info = debug.getinfo(i, "S")
if not info then return false end
if info.source == this then return true end
end
return false
end
local function debug_hook(event, line)
if abort and is_safe(stack_level) then error(abort) end
-- LuaJIT needs special treatment. Because debug_hook is set for
-- *all* coroutines, and not just the one being debugged as in regular Lua
-- (http://lua-users.org/lists/lua-l/2011-06/msg00513.html),
-- need to avoid debugging mobdebug's own code as LuaJIT doesn't
-- always correctly generate call/return hook events (there are more
-- calls than returns, which breaks stack depth calculation and
-- 'step' and 'step over' commands stop working; possibly because
-- 'tail return' events are not generated by LuaJIT).
-- the next line checks if the debugger is run under LuaJIT and if
-- one of debugger methods is present in the stack, it simply returns.
if jit and in_debugger() then return end
if event == "call" then
stack_level = stack_level + 1
elseif event == "return" or event == "tail return" then
@@ -313,30 +480,52 @@ local function debug_hook(event, line)
end
end
local suspend = (check_break
-- stack check is at least two times faster than select
-- 1.2s vs 2.5s for 100,000 iterations on 1.6Ghz CPU
and is_safe(stack_level)
and (socket.select({server}, {}, 0))[server]
)
if suspend
or step_into
if step_into
or (step_over and stack_level <= step_level)
or has_breakpoint(file, line) then
or has_breakpoint(file, line)
or (socket.select({server}, {}, 0))[server] then
vars = vars or capture_vars()
check_break = true -- this is only needed to avoid breaking too early when debugging is starting
seen_hook = true
step_into = false
step_over = false
local status, res = coroutine.resume(coro_debugger, events.BREAK, vars, file, line)
if status and res then
-- handle 'stack' command that provides stack() information to the debugger
if status and res == 'stack' then
while status and res == 'stack' do
-- resume with the stack trace and variables
if vars then restore_vars(vars) end -- restore vars so they are reflected in stack values
status, res = coroutine.resume(coro_debugger, events.STACK, stack(3), file, line)
end
end
-- need to recheck once more as resume after 'stack' command may
-- return something else (for example, 'exit'), which needs to be handled
if status and res and res ~= 'stack' then
if abort == nil and res == "exit" then os.exit(1) end
abort = res
error(abort)
end -- abort execution if requested
-- only abort if safe; if not, there is another (earlier) check inside
-- debug_hook, which will abort execution at the first safe opportunity
if is_safe(stack_level) then error(abort) end
end
end
if vars then restore_vars(vars) end
end
end
local function stringify_results(status, ...)
if not status then return status, ... end -- on error report as it
local t = {...}
for i,v in pairs(t) do -- stringify each of the returned values
t[i] = serpent.line(v, {nocode = true, comment = 1})
end
-- stringify table with all returned values
-- this is done to allow each returned value to be used (serialized or not)
-- intependently and to preserve "original" comments
return status, serpent.dump(t, {sparse = false})
end
local function debugger_loop(sfile, sline)
local command
local app
@@ -358,7 +547,7 @@ local function debugger_loop(sfile, sline)
if win then
-- process messages in a regular way
-- and exit as soon as the event loop is idle
win:Connect(wx.wxEVT_IDLE, function(event)
win:Connect(wx.wxEVT_IDLE, function()
win:Disconnect(wx.wxID_ANY, wx.wxID_ANY, wx.wxEVT_IDLE)
app:ExitMainLoop()
end)
@@ -380,7 +569,7 @@ local function debugger_loop(sfile, sline)
server:send("400 Bad Request\n")
end
elseif command == "DELB" then
_, _, _, file, line = string.find(line, "^([A-Z]+)%s+([%w%p%s]+)%s+(%d+)%s*$")
local _, _, _, file, line = string.find(line, "^([A-Z]+)%s+([%w%p%s]+)%s+(%d+)%s*$")
if file and line then
remove_breakpoint(file, tonumber(line))
server:send("200 OK\n")
@@ -394,9 +583,8 @@ local function debugger_loop(sfile, sline)
local status
if func then
setfenv(func, eval_env)
status, res = pcall(func)
status, res = stringify_results(pcall(func))
end
res = tostring(res)
if status then
server:send("200 OK " .. string.len(res) .. "\n")
server:send(res)
@@ -524,6 +712,23 @@ local function debugger_loop(sfile, sline)
end
elseif command == "SUSPEND" then
-- do nothing; it already fulfilled its role
elseif command == "STACK" then
-- first check if we can execute the stack command
-- as it requires yielding back to debug_hook it cannot be executed
-- if we have not seen the hook yet as happens after start().
-- in this case we simply return an empty result
if not seen_hook then
server:send("200 OK " .. serpent.dump({}) .. "\n")
else
-- yield back to debug hook to get stack information
local ev, vars = coroutine.yield("stack")
if ev == events.STACK then
server:send("200 OK " ..
serpent.dump(vars, {nocode = true, sparse = false}) .. "\n")
else
server:send("401 Error in Expression 0\n")
end
end
elseif command == "EXIT" then
server:send("200 OK\n")
coroutine.yield("exit")
@@ -537,8 +742,18 @@ local function connect(controller_host, controller_port)
return socket.connect(controller_host, controller_port)
end
local function isrunning()
return coro_debugger and coroutine.status(coro_debugger) == 'suspended'
end
-- Starts a debug session by connecting to a controller
local function start(controller_host, controller_port)
-- only one debugging session can be run (as there is only one debug hook)
if isrunning() then return end
controller_host = controller_host or "localhost"
controller_port = controller_port or mobdebug.port
server = socket.connect(controller_host, controller_port)
if server then
local info = debug.getinfo(2, "Sl")
@@ -546,6 +761,12 @@ local function start(controller_host, controller_port)
if string.find(file, "@") == 1 then file = string.sub(file, 2) end
if string.find(file, "%.[/\\]") == 1 then file = string.sub(file, 3) end
-- correct stack depth which already has some calls on it
-- so it doesn't go into negative when those calls return
-- as this breaks subsequence checks in stack_depth().
-- start from 16th frame, which is sufficiently large for this check.
stack_level = stack_depth(16)
debug.sethook(debug_hook, "lcr")
coro_debugger = coroutine.create(debugger_loop)
return coroutine.resume(coro_debugger, file, info.currentline)
@@ -555,6 +776,12 @@ local function start(controller_host, controller_port)
end
local function controller(controller_host, controller_port)
-- only one debugging session can be run (as there is only one debug hook)
if isrunning() then return end
controller_host = controller_host or "localhost"
controller_port = controller_port or mobdebug.port
local exitonerror = not skip -- exit if not running a scratchpad
server = socket.connect(controller_host, controller_port)
if server then
@@ -583,8 +810,10 @@ local function controller(controller_host, controller_port)
else
if status then -- normal execution is done
break
elseif err and not err:find(deferror) then -- report the error
report(debug.traceback(coro_debugee), err)
elseif err and not tostring(err):find(deferror) then
-- report the error back
-- err is not necessarily a string, so convert to string to report
report(debug.traceback(coro_debugee), tostring(err))
if exitonerror then break end
-- resume once more to clear the response the debugger wants to send
local status, err = coroutine.resume(coro_debugger, events.RESTART)
@@ -644,12 +873,12 @@ local function handle(params, client)
if size then
local msg = client:receive(tonumber(size))
print("Error in remote application: " .. msg)
os.exit()
os.exit(1)
return nil, nil, msg -- use return here for those cases where os.exit() is not wanted
end
else
print("Unknown error")
os.exit()
os.exit(1)
-- use return here for those cases where os.exit() is not wanted
return nil, nil, "Debugger error: unexpected response '" .. breakpoint .. "'"
end
@@ -753,7 +982,8 @@ local function handle(params, client)
else
local file = io.open(exp, "r")
if not file then print("Cannot open file " .. exp); return end
local lines = file:read("*all")
-- read the file and remove the shebang line as it causes a compilation error
local lines = file:read("*all"):gsub("^#!.-\n", "\n")
file:close()
local file = string.gsub(exp, "\\", "/") -- convert slash
@@ -769,9 +999,23 @@ local function handle(params, client)
if status == "200" then
len = tonumber(len)
if len > 0 then
local res = client:receive(len)
print(res)
return res
local status, res
local str = client:receive(len)
-- handle serialized table with results
local func, err = loadstring(str)
if func then
status, res = pcall(func)
if not status then err = res
elseif type(res) ~= "table" then
err = "received "..type(res).." instead of expected 'table'"
end
end
if err then
print("Error in processing results: " .. err)
return nil, nil, "Error in processing results: " .. err
end
print((table.unpack or unpack)(res))
return res[1], res
end
elseif status == "201" then
_, _, file, line = string.find(params, "^201 Started%s+([%w%p%s]+)%s+(%d+)%s*$")
@@ -793,7 +1037,7 @@ local function handle(params, client)
elseif command == "listb" then
for k, v in pairs(breakpoints) do
local b = k .. ": " -- get filename
for k, v in pairs(v) do
for k in pairs(v) do
b = b .. k .. " " -- get line numbers
end
print(b)
@@ -804,6 +1048,34 @@ local function handle(params, client)
end
elseif command == "suspend" then
client:send("SUSPEND\n")
elseif command == "stack" then
client:send("STACK\n")
local resp = client:receive()
local _, _, status, res = string.find(resp, "^(%d+)%s+%w+%s+(.+)%s*$")
if status == "200" then
local func, err = loadstring(res)
if func == nil then
print("Error in stack information: " .. err)
return nil, nil, err
end
local stack = func()
for _,frame in ipairs(stack) do
-- remove basedir from short_src or source
local src = string.gsub(frame[1][2], "\\", "/") -- convert slash
if string.find(src, "@") == 1 then src = string.sub(src, 2) end
if string.find(src, "%./") == 1 then src = string.sub(src, 3) end
frame[1][2] = string.gsub(src, basedir, '') -- remove basedir
print(serpent.line(frame[1], {comment = false}))
end
return stack
elseif status == "401" then
local res = "Error in stack result"
print(res)
return nil, nil, res
else
print("Unknown error")
return nil, nil, "Debugger error: unexpected response after STACK"
end
elseif command == "basedir" then
local _, _, dir = string.find(params, "^[a-z]+%s+(.+)$")
if dir then
@@ -821,16 +1093,17 @@ local function handle(params, client)
print("setw <exp> -- adds a new watch expression")
print("delw <index> -- removes the watch expression at index")
print("delallw -- removes all watch expressions")
print("run -- run until next breakpoint")
print("step -- run until next line, stepping into function calls")
print("over -- run until next line, stepping over function calls")
print("out -- run until line after returning from current function")
print("run -- runs until next breakpoint")
print("step -- runs until next line, stepping into function calls")
print("over -- runs until next line, stepping over function calls")
print("out -- runs until line after returning from current function")
print("listb -- lists breakpoints")
print("listw -- lists watch expressions")
print("eval <exp> -- evaluates expression on the current context and returns its value")
print("exec <stmt> -- executes statement on the current context")
print("load <file> -- loads a local file for debugging")
print("reload -- restarts the current debugging session")
print("stack -- reports stack trace")
print("basedir [<path>] -- sets the base path of the remote application, or shows the current one")
print("exit -- exits debugger")
else
@@ -845,6 +1118,8 @@ end
-- Starts debugging server
local function listen(host, port)
host = host or "*"
port = port or mobdebug.port
local socket = require "socket"
@@ -884,6 +1159,8 @@ mobdebug.scratchpad = scratchpad
mobdebug.handle = handle
mobdebug.connect = connect
mobdebug.start = start
mobdebug.line = serpent.line
mobdebug.dump = serpent.dump
-- this is needed to make "require 'modebug'" to work when mobdebug
-- module is loaded manually

315
lualibs/testwell.lua Normal file
View File

@@ -0,0 +1,315 @@
--
-- Copyright (C) 2012 Paul Kulchenko
-- A simple testing library
-- Based on lua-TestMore : <http://fperrad.github.com/lua-TestMore/>
-- Copyright (c) 2009-2011 Francois Perrad
-- This library is licensed under the terms of the MIT/X11 license,
-- like Lua itself.
--
local pairs = pairs
local tostring = tostring
local type = type
local _G = _G or _ENV
-----------------------------------------------------------
local tb = {
curr_test = 0,
good_test = 0,
skip_test = 0,
}
function tb:print(...)
print(...)
end
function tb:note(...)
self:print(...)
end
function tb:diag(...)
local arg = {...}
for k, v in pairs(arg) do
arg[k] = tostring(v)
end
local msg = table.concat(arg)
msg = msg:gsub("\n", "\n# ")
msg = msg:gsub("\n# \n", "\n#\n")
msg = msg:gsub("\n# $", '')
self:print("# " .. msg)
end
function tb:ok(test, name, more)
self.curr_test = self.curr_test + 1
self.good_test = self.good_test + (test and 1 or 0)
self.skip_test = self.skip_test + (test == nil and 1 or 0)
name = tostring(name or '')
local out = ''
if not test then
out = "not "
end
out = out .. "ok " .. self.curr_test
if name ~= '' then
out = out .. " - " .. name
end
self:print(out)
if test == false then
self:diag(" Failed test " .. (name and ("'" .. name .. "'") or ''))
if debug then
local info = debug.getinfo(3)
local file = info.short_src
local line = info.currentline
self:diag(" in " .. file .. " at line " .. line .. ".")
end
self:diag(more)
end
end
function tb:done_testing(reset)
local c, g, s = self.curr_test, self.good_test, self.skip_test
if reset then
self.curr_test = 0
self.good_test = 0
self.skip_test = 0
end
return c, g, s
end
-----------------------------------------------------------
local serpent = (function() ---- include Serpent module for serialization
local n, v = "serpent", 0.15 -- (C) 2012 Paul Kulchenko; MIT License
local c, d = "Paul Kulchenko", "Serializer and pretty printer of Lua data types"
local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'}
local badtype = {thread = true, userdata = true}
local keyword, globals, G = {}, {}, (_G or _ENV)
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
for k,v in pairs(G) do globals[v] = k end -- build func to name mapping
for _,g in ipairs({'coroutine', 'debug', 'io', 'math', 'string', 'table', 'os'}) do
for k,v in pairs(G[g]) do globals[v] = g..'.'..k end end
local function s(t, opts)
local name, indent, fatal = opts.name, opts.indent, opts.fatal
local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge
local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge)
local comm = opts.comment and (tonumber(opts.comment) or math.huge)
local seen, sref, syms, symn = {}, {}, {}, 0
local function gensym(val) return tostring(val):gsub("[^%w]",""):gsub("(%d%w+)",
function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return syms[s] end) end
local function safestr(s) return type(s) == "number" and (huge and snum[tostring(s)] or s)
or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026
or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end
local function comment(s,l) return comm and (l or 0) < comm and ' --[['..tostring(s)..']]' or '' end
local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal
and safestr(tostring(s))..comment('err',l) or error("Can't serialize "..tostring(s)) end
local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r']
local n = name == nil and '' or name
local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n]
local safe = plain and n or '['..safestr(n)..']'
return (path or '')..(plain and path and '.' or '')..safe, safe end
local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or function(o, n)
local maxn, to = tonumber(n) or 12, {number = 'a', string = 'b'}
local function padnum(d) return ("%0"..maxn.."d"):format(d) end
table.sort(o, function(a,b)
return (o[a] and 0 or to[type(a)] or 'z')..(tostring(a):gsub("%d+",padnum))
< (o[b] and 0 or to[type(b)] or 'z')..(tostring(b):gsub("%d+",padnum)) end) end
local function val2str(t, name, indent, path, plainindex, level)
local ttype, level = type(t), (level or 0)
local spath, sname = safename(path, name)
local tag = plainindex and
((type(name) == "number") and '' or name..space..'='..space) or
(name ~= nil and sname..space..'='..space or '')
if seen[t] then
table.insert(sref, spath..space..'='..space..seen[t])
return tag..'nil'..comment('ref', level)
elseif badtype[ttype] then return tag..globerr(t, level)
elseif ttype == 'function' then
seen[t] = spath
local ok, res = pcall(string.dump, t)
local func = ok and ((opts.nocode and "function() end" or
"loadstring("..safestr(res)..",'@serialized')")..comment(t, level))
return tag..(func or globerr(t, level))
elseif ttype == "table" then
if level >= maxl then return tag..'{}'..comment('max', level) end
seen[t] = spath
if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty
local maxn, o, out = #t, {}, {}
for key = 1, maxn do table.insert(o, key) end
for key in pairs(t) do if not o[key] then table.insert(o, key) end end
if opts.sortkeys then alphanumsort(o, opts.sortkeys) end
for n, key in ipairs(o) do
local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse
if opts.ignore and opts.ignore[value] -- skip ignored values; do nothing
or sparse and value == nil then -- skipping nils; do nothing
elseif ktype == 'table' or ktype == 'function' then
if not seen[key] and not globals[key] then
table.insert(sref, 'local '..val2str(key,gensym(key),indent)) end
table.insert(sref, seen[t]..'['..(seen[key] or globals[key] or gensym(key))
..']'..space..'='..space..(seen[value] or val2str(value,nil,indent)))
else
if badtype[ktype] then plainindex, key = true, '['..globerr(key, level+1)..']' end
table.insert(out,val2str(value,key,indent,spath,plainindex,level+1))
end
end
local prefix = string.rep(indent or '', level)
local head = indent and '{\n'..prefix..indent or '{'
local body = table.concat(out, ','..(indent and '\n'..prefix..indent or space))
local tail = indent and "\n"..prefix..'}' or '}'
return (custom and custom(tag,head,body,tail) or tag..head..body..tail)..comment(t, level)
else return tag..safestr(t) end -- handle all other types
end
local sepr = indent and "\n" or ";"..space
local body = val2str(t, name, indent) -- this call also populates sref
local tail = #sref>0 and table.concat(sref, sepr)..sepr or ''
return not name and body or "do local "..body..sepr..tail.."return "..name..sepr.."end"
end
local function merge(a, b) if b then for k,v in pairs(b) do a[k] = v end end; return a; end
return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s,
dump = function(a, opts) return s(a, merge({name = '_', compact = true, sparse = true}, opts)) end,
line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end,
block = function(a, opts) return s(a, merge({indent = ' ', sortkeys = true, comment = true}, opts)) end }
end)() ---- end of Serpent module
-----------------------------------------------------------
local m = {}
function m.ok(test, name)
tb:ok(test, name)
end
local parms = {comment = false}
function m.is(got, expected, name)
if type(got) == 'table' then got = serpent.line(got, parms) end
if type(expected) == 'table' then expected = serpent.line(expected, parms) end
local pass = got == expected
if got == nil then pass = nil end
tb:ok(pass, name, not pass and
" got: " .. tostring(got) ..
"\n expected: " .. tostring(expected))
end
function m.isnt(got, expected, name)
if type(got) == 'table' then got = serpent.line(got, parms) end
if type(expected) == 'table' then expected = serpent.line(expected, parms) end
local pass = got ~= expected
if got == nil then pass = nil end
tb:ok(pass, name, not pass and
" got: " .. tostring(got) ..
"\n expected: anything else")
end
function m.like(got, pattern, name)
if type(pattern) ~= 'string' then
return tb:ok(false, name, "pattern isn't a string : " .. tostring(pattern))
end
local pass = tostring(got):match(pattern)
if got == nil then pass = nil end
tb:ok(pass, name, not pass and
" '" .. tostring(got) .. "'" ..
"\n doesn't match '" .. pattern .. "'")
end
function m.unlike(got, pattern, name)
if type(pattern) ~= 'string' then
return tb:ok(false, name, "pattern isn't a string : " .. tostring(pattern))
end
local pass = not tostring(got):match(pattern)
if got == nil then pass = nil end
tb:ok(pass, name, not pass and
" '" .. tostring(got) .. "'" ..
"\n matches '" .. pattern .. "'")
end
local cmp = {
['<'] = function (a, b) return a < b end,
['<='] = function (a, b) return a <= b end,
['>'] = function (a, b) return a > b end,
['>='] = function (a, b) return a >= b end,
['=='] = function (a, b) return a == b end,
['~='] = function (a, b) return a ~= b end,
}
function m.cmp_ok(this, op, that, name)
local f = cmp[op]
if not f then
return tb:ok(false, name, "unknown operator : " .. tostring(op))
end
local pass = f(this, that)
if this == nil then pass = nil end
tb:ok(pass, name, not pass and
" " .. tostring(this) ..
"\n " .. op ..
"\n " .. tostring(that))
end
function m.type_ok(val, t, name)
if type(t) ~= 'string' then
return tb:ok(false, name, "type isn't a string : " .. tostring(t))
end
if type(val) == t then
tb:ok(true, name)
else
tb:ok(false, name,
" " .. tostring(val) .. " isn't a '" .. t .."', it's a '" .. type(val) .. "'")
end
end
function m.diag(...)
tb:diag(...)
end
function m.report()
local total, good, skipped = tb:done_testing(true)
if total == 0 then return end
local failed = total - good - skipped
local sum = ("(%d/%d/%d)."):format(good, skipped, total)
local num, msg = 0, ""
if good > 0 then
num, msg = good, msg .. "passed " .. good
end
if failed > 0 then
num, msg = failed, msg .. (#msg > 0 and (skipped > 0 and ", " or " and ") or "")
.. "failed " .. failed
end
if skipped > 0 then
num, msg = skipped, msg .. (#msg > 0 and ((good > 0 and failed > 0 and ',' or '') .." and ") or "")
.. "skipped " .. skipped
end
msg = ("Looks like you %s test%s of %d %s"):format(msg, (num > 1 and 's' or ''), total, sum)
if skipped == total then msg = "Looks like you skipped all tests " .. sum end
if good == total then msg = "All tests passed " .. sum end
tb:note(("1..%d # %s"):format(total, msg))
end
function m.ismain()
for l = 3, 64 do -- only check up to 64 level; no more needed
local info = debug.getinfo(l)
if not info then return true end
if info.func == require then return false end
end
return true
end
-- this is needed to call report() when the test object is destroyed
if _VERSION >= "Lua 5.2" then
setmetatable(m, {__gc = m.report})
else
-- keep sentinel alive until 'm' is garbage collected
m.sentinel = newproxy(true)
getmetatable(m.sentinel).__gc = m.report
end
for k, v in pairs(m) do -- injection
_G[k] = v
end
return m

View File

@@ -1,6 +1,8 @@
-- authors: Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local funcdef = "([A-Za-z_][A-Za-z0-9_%.%:]*)%s*"
local funccall = "([A-Za-z_][A-Za-z0-9_]*)%s*"
return {
exts = {"lua"},
lexer = wxstc.wxSTC_LEX_LUA,
@@ -8,23 +10,18 @@ return {
linecomment = "--",
sep = "%.:",
isfncall = function(str)
return string.find(str,"([A-Za-z0-9_]+)%s*%(")
return string.find(str, funccall .. "%(")
end,
isfndef = function(str)
local l
local s,e,cap,par = string.find(str,"function%s+([A-Za-z0-9_]+%s-[%.%:]?%s-[A-Za-z0-9_]*)%s*(%(.-%))")
local s,e,cap,par = string.find(str, "function%s+" .. funcdef .. "(%(.-%))")
-- try to match without brackets now, but only at the beginning of the line
if (not s) then
s,e,cap = string.find(str,"^%s*function%s+([A-Za-z0-9_]+%s-[%.%:]?%s-[A-Za-z0-9_]*)%s*")
s,e,cap = string.find(str, "^%s*function%s+" .. funcdef)
end
-- try to match "foo = function()"
if (not s) then
s,e,cap,cap1,cap2,par = string.find(str,"(([A-Za-z0-9_]+%s-[%.%:]?%s-)([A-Za-z0-9_]*))%s*=%s*function%s*(%(.-%))")
-- check if we captured 'local foo =' instead of 'foo.bar ='
if cap1 and cap2 and string.len(cap2) > 0 and not string.find(cap1,'[%.%:]') then
cap = cap2
s = s + string.len(cap1)
end
s,e,cap,par = string.find(str, funcdef .. "=%s*function%s*(%(.-%))")
end
if (s) then
l = string.find(string.sub(str,1,s-1),"local%s+$")

View File

@@ -166,6 +166,10 @@ config = {
singleinstanceport = 0xe493,
-- UDP port for single instance communication
activateoutput = false, -- activate output/console on Run/Debug/Compile
unhidewxwindow = false, -- try to unhide a wx window
allowinteractivescript = false, -- allow interaction in the output window
}
-- application engine
@@ -306,12 +310,10 @@ debuginterface = {
interpreter = {
name = "",
description = "",
api = {"apifile_without_extension"} -- optional to limit loaded lua apis
frun = function(self,wfilename,withdebugger)
end,
fprojdir = function(self,wfilename)
return "projpath_from_filename" -- optional
end,
fattachdebug = function(self) end, -- optional
api = {"apifile_without_extension"} -- (opt) to limit loaded lua apis
frun = function(self,wfilename,withdebugger) end,
fprojdir = function(self,wfilename) return "projpath_from_filename" end, -- (opt)
fattachdebug = function(self) end, -- (opt)
hasdebugger = false, -- if debugging is available
scratchextloop = false, -- (opt) if scratchpad requires handling for external loop
}

View File

@@ -89,8 +89,8 @@ local function addAPI(apifile,only,subapis,known) -- relative to API directory
end
local function loadallAPIs (only,subapis,known)
for i,dir in ipairs(FileSysGet(".\\api\\*.*",wx.wxDIR)) do
local files = FileSysGet(dir.."\\*.*",wx.wxFILE)
for i,dir in ipairs(FileSysGet("./api/*",wx.wxDIR)) do
local files = FileSysGet(dir.."/*.*",wx.wxFILE)
for i,file in ipairs(files) do
if file:match "%.lua$" then
addAPI(file,only,subapis,known)
@@ -239,7 +239,7 @@ end
function GetTipInfo(editor, content, short)
local caller = content:match("([%w_]+)%(%s*$")
local class = caller and content:match("([%w_%.]+)[%.:]"..caller.."%(%s*$")
local class = caller and content:match("([%w_]+)[%.:]"..caller.."%(%s*$")
local tip = editor.api.tip
local classtab = short and tip.shortfinfoclass or tip.finfoclass

View File

@@ -29,7 +29,7 @@ end
function LoadFile(filePath, editor, file_must_exist)
filePath = wx.wxFileName(filePath):GetFullPath()
local cmpName = string.lower(string.gsub(filePath, "\\", "/"))
-- prevent files from being reopened again
if (not editor) then
for id, doc in pairs(openDocuments) do
@@ -54,6 +54,7 @@ function LoadFile(filePath, editor, file_must_exist)
return nil
end
local current = editor and editor:GetCurrentPos()
editor = editor
or findDocumentToReuse()
or CreateEditor(wx.wxFileName(filePath):GetFullName() or "untitled.lua")
@@ -64,6 +65,7 @@ function LoadFile(filePath, editor, file_must_exist)
editor:MarkerDeleteAll(BREAKPOINT_MARKER)
editor:MarkerDeleteAll(CURRENT_LINE_MARKER)
editor:AppendText(file_text)
if current then editor:GotoPos(current) end
if (ide.config.editor.autotabs) then
local found = string.find(file_text,"\t") ~= nil
editor:SetUseTabs(found)
@@ -80,8 +82,10 @@ function LoadFile(filePath, editor, file_must_exist)
IndicateFunctions(editor)
SettingsAppendFileToHistory(filePath)
SetEditorSelection(nil)
-- activate the editor; this is needed for those cases when the editor is
-- created from some other element, for example, from a project tree.
SetEditorSelection()
return editor
end
@@ -111,7 +115,7 @@ function OpenFile(event)
"",
"",
exts,
wx.wxOPEN + wx.wxFILE_MUST_EXIST)
wx.wxFD_OPEN + wx.wxFD_FILE_MUST_EXIST)
if fileDialog:ShowModal() == wx.wxID_OK then
if not LoadFile(fileDialog:GetPath(), nil, true) then
wx.wxMessageBox("Unable to load file '"..fileDialog:GetPath().."'.",
@@ -178,12 +182,15 @@ function SaveFileAs(editor)
fn:GetPath(wx.wxPATH_GET_VOLUME),
fn:GetFullName(),
exts,
wx.wxSAVE)
wx.wxFD_SAVE)
if fileDialog:ShowModal() == wx.wxID_OK then
local filePath = fileDialog:GetPath()
if SaveFile(editor, filePath) then
SetEditorSelection() -- update title of the editor
FileTreeRefresh() -- refresh the tree to reflect the new file
FileTreeMarkSelected(filePath)
SetupKeywords(editor, GetFileExt(filePath))
IndicateFunctions(editor)
if MarkupStyle then MarkupStyle(editor) end
@@ -250,7 +257,7 @@ local function removePage(index)
notebook:SetSelection(prevIndex)
end
SetEditorSelection(nil) -- will use notebook GetSelection to update
SetEditorSelection() -- will use notebook GetSelection to update
end
function ClosePage(selection)
@@ -307,8 +314,13 @@ function SaveOnExit(allow_cancel)
if (SaveModifiedDialog(document.editor, allow_cancel) == wx.wxID_CANCEL) then
return false
end
end
document.isModified = false
-- if all documents have been saved or refused to save, then mark those that
-- are still modified as not modified (they don't need to be saved)
-- to keep their tab names correct
for id, document in pairs(openDocuments) do
if document.isModified then SetDocumentModified(id, false) end
end
return true
@@ -394,7 +406,10 @@ function ClearAllCurrentLineMarkers()
end
function CompileProgram(editor, quiet)
local editorText = editor:GetText()
-- remove shebang line (#!) as it throws a compilation error as
-- loadstring() doesn't allow it even though lua/loadfile accepts it.
-- replace with a new line to keep the number of lines the same.
local editorText = editor:GetText():gsub("^#!.-\n", "\n")
local id = editor:GetId()
local filePath = DebuggerMakeFileName(editor, openDocuments[id].filePath)
local _, errMsg, line_num = wxlua.CompileLuaScript(editorText, filePath)
@@ -492,7 +507,8 @@ function ShowFullScreen(setFullScreen)
uimgr:GetPane("toolBar"):Show(not setFullScreen)
uimgr:Update()
frame:ShowFullScreen(setFullScreen)
-- protect from systems that don't have ShowFullScreen (GTK on linux?)
pcall(function() frame:ShowFullScreen(setFullScreen) end)
end
function CloseWindow(event)
@@ -510,9 +526,14 @@ function CloseWindow(event)
SettingsSaveView()
SettingsSaveFramePosition(ide.frame, "MainFrame")
SettingsSaveEditorSettings()
DebuggerCloseWatchWindow()
DebuggerKillClient()
if DebuggerCloseWatchWindow then DebuggerCloseWatchWindow() end
if DebuggerCloseStackWindow then DebuggerCloseStackWindow() end
if DebuggerShutdown then DebuggerShutdown() end
ide.settings:delete() -- always delete the config
event:Skip()
-- without explicit exit() the IDE crashes with SIGILL exception when closed
-- on MacOS compiled under 64bit with wxwidgets 2.9.3
if ide.osname == "Macintosh" then os.exit() end
end
frame:Connect(wx.wxEVT_CLOSE_WINDOW, CloseWindow)

View File

@@ -1,5 +1,5 @@
-- Integration with MobDebug
-- Copyright Paul Kulchenko 2011
-- Copyright 2011-12 Paul Kulchenko
-- Original authors: Lomtik Software (J. Winwood & John Labenski)
-- Luxinia Dev (Eike Decker & Christoph Kubisch)
@@ -12,26 +12,127 @@ local debugger = ide.debugger
debugger.server = nil -- DebuggerServer object when debugging, else nil
debugger.running = false -- true when the debuggee is running
debugger.listening = false -- true when the debugger is listening for a client
debugger.portnumber = 8171 -- the port # to use for debugging
debugger.portnumber = mobdebug.port or 8171 -- the port # to use for debugging
debugger.watchWindow = nil -- the watchWindow, nil when not created
debugger.watchListCtrl = nil -- the child listctrl in the watchWindow
debugger.watchCtrl = nil -- the child ctrl in the watchWindow
debugger.stackWindow = nil -- the stackWindow, nil when not created
debugger.stackCtrl = nil -- the child ctrl in the stackWindow
debugger.hostname = (function() -- check what address is resolvable
local addr = wx.wxIPV4address()
for _, host in ipairs({wx.wxGetHostName(), wx.wxGetFullHostName()}) do
if addr:Hostname(host) then return host end
end
return "localhost" -- last resort; no known good hostname
end)()
local notebook = ide.frame.notebook
local function updateWatchesSync()
local watchListCtrl = debugger.watchListCtrl
if watchListCtrl and debugger.server and not debugger.running then
for idx = 0, watchListCtrl:GetItemCount() - 1 do
local expression = watchListCtrl:GetItemText(idx)
local value, _, error = debugger.evaluate(expression)
watchListCtrl:SetItem(idx, 1, value or ('error: ' .. error))
local watchCtrl = debugger.watchCtrl
if watchCtrl and debugger.server
and not debugger.running and not debugger.scratchpad then
for idx = 0, watchCtrl:GetItemCount() - 1 do
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
watchCtrl:SetItem(idx, 1, error and ('error: '..error) or values[1])
end
end
end
local simpleType = {['nil'] = true, ['string'] = true, ['number'] = true, ['boolean'] = true}
local stackItemValue = {}
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
if stackCtrl and debugger.server
and not debugger.running and not debugger.scratchpad then
local stack = debugger.stack()
if not stack or #stack == 0 then stackCtrl:DeleteAllItems(); return end
stackCtrl:Freeze()
stackCtrl:DeleteAllItems()
local params = {comment = false, nocode = true}
local root = stackCtrl:AddRoot("Stack")
stackItemValue = {} -- reset cache of items in the stack
for _,frame in ipairs(stack) do
-- "main chunk at line 24"
-- "foo() at line 13 (defined at foobar.lua:11)"
-- call = { source.name, source.source, source.linedefined,
-- source.currentline, source.what, source.namewhat, source.short_src }
local call = frame[1]
local func = call[5] == "main" and "main chunk"
or call[5] == "C" and (call[1] or "C function")
or call[5] == "tail" and "tail call"
or (call[1] or "anonymous function")
local text = func ..
(call[4] == -1 and '' or " at line "..call[4]) ..
(call[5] ~= "main" and call[5] ~= "Lua" and ''
or (call[3] > 0 and " (defined at "..call[2]..":"..call[3]..")"
or " (defined in "..call[2]..")"))
local callitem = stackCtrl:AppendItem(root, text, 0)
for name,val in pairs(frame[2]) do
local value, comment = val[1], val[2]
local text = ("%s = %s%s"):
format(name, mobdebug.line(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
end
for name,val in pairs(frame[3]) do
local value, comment = val[1], val[2]
local text = ("%s = %s%s"):
format(name, mobdebug.line(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
end
stackCtrl:SortChildren(callitem)
stackCtrl:Expand(callitem)
end
stackCtrl:EnsureVisible(stackCtrl:GetFirstChild(root))
stackCtrl:Thaw()
end
end
local function updateStackAndWatches()
if debugger.server and not debugger.running then
copas.addthread(function() updateStackSync() updateWatchesSync() end)
end
end
local function updateWatches()
if debugger.watchListCtrl and debugger.server and not debugger.running then
copas.addthread(updateWatchesSync)
if debugger.server and not debugger.running then
copas.addthread(function() updateWatchesSync() end)
end
end
local function killClient()
if (debugger.pid) then
-- using SIGTERM for some reason kills not only the debugee process,
-- but also some system processes, which leads to a blue screen crash
-- (at least on Windows Vista SP2)
local ret = wx.wxProcess.Kill(debugger.pid, wx.wxSIGKILL, wx.wxKILL_CHILDREN)
if ret == wx.wxKILL_OK then
DisplayOutput(("Program stopped (pid: %d).\n"):format(debugger.pid))
elseif ret ~= wx.wxKILL_NO_PROCESS then
DisplayOutput(("Unable to stop program (pid: %d), code %d.\n")
:format(debugger.pid, ret))
end
debugger.pid = nil
end
end
@@ -50,7 +151,7 @@ local function activateDocument(fileName, line)
for _, document in pairs(ide.openDocuments) do
local editor = document.editor
-- for running in cygwin, use same type of separators
filePath = string.gsub(document.filePath, "\\", "/")
local filePath = string.gsub(document.filePath, "\\", "/")
local fileName = string.gsub(fileName, "\\", "/")
if string.upper(filePath) == string.upper(fileName) then
local selection = document.index
@@ -69,23 +170,56 @@ local function activateDocument(fileName, line)
return activated ~= nil, activated
end
debugger.shell = function(expression)
local function reSetBreakpoints()
-- remove all breakpoints that may still be present from the last session
-- this only matters for those remote clients that reload scripts
-- without resetting their breakpoints
debugger.handle("delallb")
-- go over all windows and find all breakpoints
if (not debugger.scratchpad) then
for _, document in pairs(ide.openDocuments) do
local editor = document.editor
local filePath = document.filePath
local line = editor:MarkerNext(0, BREAKPOINT_MARKER_VALUE)
while line ~= -1 do
debugger.handle("setb " .. filePath .. " " .. (line+1))
line = editor:MarkerNext(line + 1, BREAKPOINT_MARKER_VALUE)
end
end
end
end
debugger.shell = function(expression, isstatement)
if debugger.server and not debugger.running then
copas.addthread(function ()
local addedret = false
local value, _, err = debugger.handle('exec ' .. expression)
if err and (err:find("'=' expected near '<eof>'") or
err:find("syntax error near '") or
err:find("unexpected symbol near '")) then
value, _, err = debugger.handle('eval ' .. expression:gsub("^%s*=%s*",""))
addedret = true
-- exec command is not expected to return anything.
-- eval command returns 0 or more results.
-- 'values' has a list of serialized results returned.
-- as it is not possible to distinguish between 0 and nil returned,
-- 'nil' is always returned in this case.
-- the first value returned by eval command is not used;
-- this may need to be taken into account by other debuggers.
local addedret, forceexpression = true, expression:match("^%s*=%s*")
expression = expression:gsub("^%s*=%s*","")
local _, values, err = debugger.evaluate(expression)
if not forceexpression and err and
(err:find("'<eof>' expected near '") or
err:find("'%(' expected near") or
err:find("unexpected symbol near '")) then
_, values, err = debugger.execute(expression)
addedret = false
end
if err then
if addedret then err = err:gsub('^%[string "return ', '[string "') end
DisplayShellErr(err)
elseif addedret or (value ~= nil and value ~= 'nil') then
DisplayShell(value)
elseif addedret or #values > 0 then
-- if empty table is returned, then show nil if this was an expression
if #values == 0 and (forceexpression or not isstatement) then
values = {'nil'}
end
DisplayShell((table.unpack or unpack)(values))
end
end)
end
@@ -93,9 +227,15 @@ end
debugger.listen = function()
local server = socket.bind("*", debugger.portnumber)
DisplayOutput("Started debugger server; clients can connect to "..wx.wxGetHostName()..":"..debugger.portnumber..".\n")
DisplayOutput(("Debugger server started at %s:%d.\n")
:format(debugger.hostname, debugger.portnumber))
copas.autoclose = false
copas.addserver(server, function (skt)
if debugger.server then
DisplayOutput("Refused a request to start a new debugging session as there is one in progress already.\n")
return
end
local options = debugger.options or {}
if not debugger.scratchpad then SetAllEditorsReadOnly(true) end
local wxfilepath = GetEditorFileAndCurInfo()
@@ -109,28 +249,13 @@ debugger.listen = function()
debugger.socket = skt
debugger.loop = false
debugger.scratchable = false
debugger.stats = {line = 0}
-- load the remote file into the debugger
-- set basedir first, before loading to make sure that the path is correct
debugger.handle("basedir " .. debugger.basedir)
-- remove all breakpoints that may still be present from the last session
-- this only matters for those remote clients that reload scripts
-- without resetting their breakpoints
debugger.handle("delallb")
-- go over all windows and find all breakpoints
if (not debugger.scratchpad) then
for _, document in pairs(ide.openDocuments) do
local editor = document.editor
local filePath = document.filePath
line = editor:MarkerNext(0, BREAKPOINT_MARKER_VALUE)
while line ~= -1 do
debugger.handle("setb " .. filePath .. " " .. (line+1))
line = editor:MarkerNext(line + 1, BREAKPOINT_MARKER_VALUE)
end
end
end
reSetBreakpoints()
if (options.run) then
local file, line = debugger.handle("run")
@@ -162,16 +287,20 @@ debugger.listen = function()
if activated then
debugger.basedir = path
debugger.handle("basedir " .. debugger.basedir)
-- reset breakpoints again as basedir has changed
reSetBreakpoints()
end
end
end
if not activated then
DisplayOutput("Can't find file '" .. file .. "' to activate for debugging; open the file before debugging.\n")
DisplayOutput(("Can't find file '%s' to activate for debugging; open the file in the editor before debugging.\n")
:format(file))
return debugger.terminate()
end
elseif err then
DisplayOutput("Can't debug the script in the active editor window. Compilation error:\n" .. err .. "\n")
DisplayOutput(("Can't debug the script in the active editor window. Compilation error:\n%s\n")
:format(err))
return debugger.terminate()
else
debugger.scratchable = true
@@ -180,11 +309,14 @@ debugger.listen = function()
end
if (not options.noshell and not debugger.scratchpad) then
ShellSupportRemote(debugger.shell, debugger.pid)
ShellSupportRemote(debugger.shell)
end
DisplayOutput("Started remote debugging session (base directory: '" .. debugger.basedir .. "').\n")
updateStackSync()
updateWatchesSync()
DisplayOutput(("Debugging session started in '%s'.\n")
:format(debugger.basedir))
end)
debugger.listening = true
end
@@ -201,7 +333,7 @@ debugger.handle = function(command, server)
end
debugger.running = true
local file, line, err = mobdebug.handle(command, server and server or debugger.server)
local file, line, err = mobdebug.handle(command, server or debugger.server)
debugger.running = false
return file, line, err
@@ -223,10 +355,12 @@ debugger.exec = function(command)
file = debugger.basedir .. file
end
if activateDocument(file, line) then
debugger.stats.line = debugger.stats.line + 1
if debugger.loop then
updateStackSync()
updateWatchesSync()
else
updateWatches()
updateStackAndWatches()
return
end
else
@@ -254,7 +388,7 @@ debugger.update = function() copas.step(0) end
debugger.terminate = function()
if debugger.server then
if debugger.pid then -- if there is PID, try local kill
DebuggerKillClient()
killClient()
else -- otherwise, try graceful exit for the remote process
debugger.breaknow("exit")
end
@@ -271,6 +405,7 @@ debugger.out = function() debugger.exec("out") end
debugger.run = function() debugger.exec("run") end
debugger.evaluate = function(expression) return debugger.handle('eval ' .. expression) end
debugger.execute = function(expression) return debugger.handle('exec ' .. expression) end
debugger.stack = function() return debugger.handle('stack') end
debugger.breaknow = function(command)
-- stop if we're running a "trace" command
debugger.loop = false
@@ -295,6 +430,17 @@ end
debugger.breakpoint = function(file, line, state)
debugger.handleAsync((state and "setb " or "delb ") .. file .. " " .. line)
end
debugger.quickeval = function(var, callback)
if debugger.server and not debugger.running then
copas.addthread(function ()
local _, values, err = debugger.evaluate(var)
local val = err
and err:gsub("%[.-%]:%d+:%s*","error: ")
or (var .. " = " .. (#values > 0 and values[1] or 'nil'))
if callback then callback(val) end
end)
end
end
----------------------------------------------
-- public api
@@ -305,19 +451,9 @@ function DebuggerAttachDefault(options)
debugger.listen()
end
function DebuggerKillClient()
if (debugger.pid) then
-- using SIGTERM for some reason kills not only the debugee process,
-- but also some system processes, which leads to a blue screen crash
-- (at least on Windows Vista SP2)
local ret = wx.wxProcess.Kill(debugger.pid, wx.wxSIGKILL, wx.wxKILL_CHILDREN)
if ret == wx.wxKILL_OK then
DisplayOutput("Stopped process (pid: "..debugger.pid..").\n")
elseif ret ~= wx.wxKILL_NO_PROCESS then
DisplayOutput("Unable to stop process (pid: "..debugger.pid.."), code "..tostring(ret)..".\n")
end
debugger.pid = nil
end
function DebuggerShutdown()
if debugger.server then debugger.terminate() end
if debugger.pid then killClient() end
end
function DebuggerStop()
@@ -325,34 +461,108 @@ function DebuggerStop()
debugger.server = nil
debugger.pid = nil
SetAllEditorsReadOnly(false)
ShellSupportRemote(nil, 0)
ShellSupportRemote(nil)
ClearAllCurrentLineMarkers()
DebuggerScratchpadOff()
DisplayOutput("Completed debugging session.\n")
DisplayOutput(("Debugging session completed (traced %d instruction%s).\n")
:format(debugger.stats.line, debugger.stats.line == 1 and '' or 's'))
end
end
function DebuggerCreateStackWindow()
DisplayOutput("Not Yet Implemented\n")
end
function DebuggerCloseStackWindow()
if (debugger.stackWindow) then
SettingsSaveFramePosition(debugger.stackWindow, "StackWindow")
debugger.stackCtrl = nil
debugger.stackWindow = nil
end
end
function DebuggerCloseWatchWindow()
if (debugger.watchWindow) then
SettingsSaveFramePosition(debugger.watchWindow, "WatchWindow")
debugger.watchListCtrl = nil
debugger.watchCtrl = nil
debugger.watchWindow = nil
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))
end
function DebuggerCreateStackWindow()
if (debugger.stackWindow) then return updateStackAndWatches() end
local width = 360
local stackWindow = wx.wxFrame(ide.frame, wx.wxID_ANY,
"Stack Window",
wx.wxDefaultPosition, wx.wxSize(width, 200),
wx.wxDEFAULT_FRAME_STYLE + wx.wxFRAME_FLOAT_ON_PARENT)
debugger.stackWindow = stackWindow
local stackCtrl = wx.wxTreeCtrl(stackWindow, ID "debug.stack",
wx.wxDefaultPosition, wx.wxDefaultSize,
wx.wxTR_LINES_AT_ROOT + wx.wxTR_HAS_BUTTONS + wx.wxTR_SINGLE + wx.wxTR_HIDE_ROOT)
debugger.stackCtrl = stackCtrl
stackCtrl:SetImageList(imglist)
stackWindow:CentreOnParent()
SettingsRestoreFramePosition(stackWindow, "StackWindow")
stackWindow:Show(true)
stackWindow:Connect(wx.wxEVT_CLOSE_WINDOW,
function (event)
DebuggerCloseStackWindow()
stackWindow = nil
stackCtrl = nil
event:Skip()
end)
stackCtrl:Connect( wx.wxEVT_COMMAND_TREE_ITEM_EXPANDING,
function (event)
local item_id = event:GetItem()
local count = stackCtrl:GetChildrenCount(item_id, false)
if count > 0 then return true end
local image = stackCtrl:GetItemImage(item_id)
local num = 1
for name,value in pairs(stackItemValue[item_id:GetValue()]) do
local strval = mobdebug.line(value, {comment = false, nocode = true})
local text = type(name) == "number"
and (num == name and strval or ("[%s] = %s"):format(name, strval))
or ("%s = %s"):format(name, strval)
local item = stackCtrl:AppendItem(item_id, text, image)
if checkIfExpandable(value, item) then
stackCtrl:SetItemHasChildren(item, true)
end
num = num + 1
end
stackCtrl:SortChildren(item_id)
return true
end)
stackCtrl:Connect( wx.wxEVT_COMMAND_TREE_ITEM_COLLAPSED,
function() return true end)
updateStackAndWatches()
end
function DebuggerCreateWatchWindow()
local width = 200
if (debugger.watchWindow) then return updateWatches() end
local width = 360
local watchWindow = wx.wxFrame(ide.frame, wx.wxID_ANY,
"Watch Window",
wx.wxDefaultPosition, wx.wxSize(width, 160),
wx.wxDefaultPosition, wx.wxSize(width, 200),
wx.wxDEFAULT_FRAME_STYLE + wx.wxFRAME_FLOAT_ON_PARENT)
debugger.watchWindow = watchWindow
@@ -367,31 +577,31 @@ function DebuggerCreateWatchWindow()
watchMenuBar:Append(watchMenu, "&Watches")
watchWindow:SetMenuBar(watchMenuBar)
local watchListCtrl = wx.wxListCtrl(watchWindow, ID_WATCH_LISTCTRL,
local watchCtrl = wx.wxListCtrl(watchWindow, ID_WATCH_LISTCTRL,
wx.wxDefaultPosition, wx.wxDefaultSize,
wx.wxLC_REPORT + wx.wxLC_EDIT_LABELS)
debugger.watchListCtrl = watchListCtrl
debugger.watchCtrl = watchCtrl
local info = wx.wxListItem()
info:SetMask(wx.wxLIST_MASK_TEXT + wx.wxLIST_MASK_WIDTH)
info:SetText("Expression")
info:SetWidth(width * 0.45)
watchListCtrl:InsertColumn(0, info)
info:SetWidth(width * 0.32)
watchCtrl:InsertColumn(0, info)
info:SetText("Value")
info:SetWidth(width * 0.45)
watchListCtrl:InsertColumn(1, info)
info:SetWidth(width * 0.56)
watchCtrl:InsertColumn(1, info)
watchWindow:CentreOnParent()
SettingsRestoreFramePosition(watchWindow, "WatchWindow")
watchWindow:Show(true)
local function findSelectedWatchItem()
local count = watchListCtrl:GetSelectedItemCount()
local count = watchCtrl:GetSelectedItemCount()
if count > 0 then
for idx = 0, watchListCtrl:GetItemCount() - 1 do
if watchListCtrl:GetItemState(idx, wx.wxLIST_STATE_FOCUSED) ~= 0 then
for idx = 0, watchCtrl:GetItemCount() - 1 do
if watchCtrl:GetItemState(idx, wx.wxLIST_STATE_FOCUSED) ~= 0 then
return idx
end
end
@@ -403,62 +613,61 @@ function DebuggerCreateWatchWindow()
function (event)
DebuggerCloseWatchWindow()
watchWindow = nil
watchListCtrl = nil
watchCtrl = nil
event:Skip()
end)
watchWindow:Connect(ID_ADDWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
function ()
local row = watchListCtrl:InsertItem(watchListCtrl:GetItemCount(), "Expr")
watchListCtrl:SetItem(row, 0, "Expr")
watchListCtrl:SetItem(row, 1, "Value")
watchListCtrl:EditLabel(row)
local row = watchCtrl:InsertItem(watchCtrl:GetItemCount(), "Expr")
watchCtrl:SetItem(row, 0, "Expr")
watchCtrl:SetItem(row, 1, "Value")
watchCtrl:EditLabel(row)
end)
watchWindow:Connect(ID_EDITWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
function ()
local row = findSelectedWatchItem()
if row >= 0 then
watchListCtrl:EditLabel(row)
watchCtrl:EditLabel(row)
end
end)
watchWindow:Connect(ID_EDITWATCH, wx.wxEVT_UPDATE_UI,
function (event)
event:Enable(watchListCtrl:GetSelectedItemCount() > 0)
event:Enable(watchCtrl:GetSelectedItemCount() > 0)
end)
watchWindow:Connect(ID_REMOVEWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
function ()
local row = findSelectedWatchItem()
if row >= 0 then
watchListCtrl:DeleteItem(row)
watchCtrl:DeleteItem(row)
end
end)
watchWindow:Connect(ID_REMOVEWATCH, wx.wxEVT_UPDATE_UI,
function (event)
event:Enable(watchListCtrl:GetSelectedItemCount() > 0)
event:Enable(watchCtrl:GetSelectedItemCount() > 0)
end)
watchWindow:Connect(ID_EVALUATEWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
function () updateWatches() end)
watchWindow:Connect(ID_EVALUATEWATCH, wx.wxEVT_UPDATE_UI,
function (event)
event:Enable(watchListCtrl:GetItemCount() > 0)
event:Enable(watchCtrl:GetItemCount() > 0)
end)
watchListCtrl:Connect(wx.wxEVT_COMMAND_LIST_END_LABEL_EDIT,
watchCtrl:Connect(wx.wxEVT_COMMAND_LIST_END_LABEL_EDIT,
function (event)
watchListCtrl:SetItem(event:GetIndex(), 0, event:GetText())
updateWatches()
if #(event:GetText()) > 0 then
watchCtrl:SetItem(event:GetIndex(), 0, event:GetText())
updateWatches()
end
event:Skip()
end)
end
function DebuggerMakeFileName(editor, filePath)
if not filePath then
filePath = "file"..tostring(editor)
end
return filePath
return filePath or editor:GetText()
end
function DebuggerToggleBreakpoint(editor, line)
@@ -496,7 +705,8 @@ function DebuggerRefreshScratchpad()
else
local clear = ide.frame.menuBar:IsChecked(ID_CLEAROUTPUT)
local scratchpadEditor = debugger.scratchpad.editor
local code = scratchpadEditor:GetText()
-- take editor text and remove shebang line
local code = scratchpadEditor:GetText():gsub("^#!.-\n", "\n")
local filePath = DebuggerMakeFileName(scratchpadEditor,
ide.openDocuments[scratchpadEditor:GetId()].filePath)
@@ -505,12 +715,27 @@ function DebuggerRefreshScratchpad()
-- these errors are handled and not reported to the user
local errormsg = 'execution suspended at ' .. os.clock()
local stopper = "\ndo error('" .. errormsg .. "') end"
-- store if interpreter requires a special handling for external loop
local extloop = ide.interpreter.scratchextloop
local function reloadScratchpadCode()
debugger.scratchpad.running = os.clock()
debugger.scratchpad.updated = false
debugger.scratchpad.runs = (debugger.scratchpad.runs or 0) + 1
-- the code can be running in two ways under scratchpad:
-- 1. controlled by the application, requires stopper (most apps)
-- 2. controlled by some external loop (for example, love2d).
-- in the first case we need to reload the app after each change
-- in the second case, we need to load the app once and then
-- "execute" new code to reflect the changes (with some limitations).
local _, _, err
if extloop then -- if the execution is controlled by an external loop
if debugger.scratchpad.runs == 1
then _, _, err = debugger.loadstring(filePath, code)
else _, _, err = debugger.execute(code) end
else _, _, err = debugger.loadstring(filePath, code .. stopper) end
local _, _, err = debugger.loadstring(filePath, code .. stopper)
local prefix = "Compilation error"
if clear then ClearOutput() end
@@ -551,7 +776,7 @@ function DebuggerScratchpadOn(editor)
debugger.scratchpad.updated = true
ClearAllCurrentLineMarkers()
SetAllEditorsReadOnly(false)
ShellSupportRemote(nil, 0) -- disable remote shell
ShellSupportRemote(nil) -- disable remote shell
DebuggerRefreshScratchpad()
elseif not ProjectDebug(true, "scratchpad") then
debugger.scratchpad = nil

View File

@@ -18,7 +18,7 @@ local projcombobox = ide.frame.projpanel.projcombobox
local statusTextTable = { "OVR?", "R/O?", "Cursor Pos" }
-- set funclist font to be the same as the combobox in the project dropdown
funclist:SetFont(projcombobox:GetFont())
funclist:SetFont(ide.font.fNormal)
local function updateStatusText(editor)
local texts = { "", "", "" }
@@ -105,7 +105,7 @@ local function isFileAlteredOnDisk(editor)
wx.wxMessageBox(fileName.." is no longer on the disk.",
GetIDEString("editormessage"),
wx.wxOK + wx.wxCENTRE, ide.frame)
elseif modTime:IsValid() and oldModTime:IsEarlierThan(modTime) then
elseif not editor:GetReadOnly() and modTime:IsValid() and oldModTime:IsEarlierThan(modTime) then
local ret = wx.wxMessageBox(fileName.." has been modified on disk.\nDo you want to reload it?",
GetIDEString("editormessage"),
wx.wxYES_NO + wx.wxCENTRE, ide.frame)
@@ -125,7 +125,8 @@ function GetEditor(selection)
selection = notebook:GetSelection()
end
local editor
if (selection >= 0) and (selection < notebook:GetPageCount()) and (notebook:GetPage(selection):GetClassInfo():GetClassName()=="wxStyledTextCtrl") then
if (selection >= 0) and (selection < notebook:GetPageCount())
and (notebook:GetPage(selection):GetClassInfo():GetClassName()=="wxStyledTextCtrl") then
editor = notebook:GetPage(selection):DynamicCast("wxStyledTextCtrl")
end
return editor
@@ -146,7 +147,6 @@ function SetEditorSelection(selection)
editor:SetFocus()
editor:SetSTCFocus(true)
isFileAlteredOnDisk(editor)
local id = editor:GetId()
if openDocuments[id] and openDocuments[id].filePath then
FileTreeMarkSelected(openDocuments[id].filePath)
@@ -206,8 +206,8 @@ function CreateEditor(name)
editor:SetBufferedDraw(true)
editor:StyleClearAll()
editor:SetFont(ide.font)
editor:StyleSetFont(wxstc.wxSTC_STYLE_DEFAULT, ide.font)
editor:SetFont(ide.font.eNormal)
editor:StyleSetFont(wxstc.wxSTC_STYLE_DEFAULT, ide.font.eNormal)
editor:SetTabWidth(ide.config.editor.tabwidth or 4)
editor:SetIndent(ide.config.editor.tabwidth or 4)
@@ -363,17 +363,17 @@ function CreateEditor(name)
end)
editor:Connect(wxstc.wxEVT_STC_SAVEPOINTREACHED,
function (event)
function ()
SetDocumentModified(editor:GetId(), false)
end)
editor:Connect(wxstc.wxEVT_STC_SAVEPOINTLEFT,
function (event)
function ()
SetDocumentModified(editor:GetId(), true)
end)
editor:Connect(wxstc.wxEVT_STC_UPDATEUI,
function (event)
function ()
updateStatusText(editor)
updateBraceMatch(editor)
for _,iv in ipairs(editor.ev) do
@@ -396,13 +396,14 @@ function CreateEditor(name)
event:Skip()
end)
local inhandler = false
editor:Connect(wx.wxEVT_SET_FOCUS,
function (event)
event:Skip()
if ide.in_evt_focus or ide.exitingProgram then return end
ide.in_evt_focus = true -- true when in editor focus event to avoid recursion
if inhandler or ide.exitingProgram then return end
inhandler = true
isFileAlteredOnDisk(editor)
ide.in_evt_focus = false
inhandler = false
end)
editor:Connect(wx.wxEVT_KEY_DOWN,
@@ -447,10 +448,10 @@ function GetSpec(ext,forcespec)
-- search proper spec
-- allow forcespec for "override"
if ext and not spec then
for i,curspec in pairs(ide.specs) do
for _,curspec in pairs(ide.specs) do
local exts = curspec.exts
if (exts) then
for n,curext in ipairs(exts) do
for _,curext in ipairs(exts) do
if (curext == ext) then
spec = curspec
break
@@ -474,16 +475,11 @@ function IndicateFunctions(editor, lines, linee)
if (lines < 0) then return end
local isfunc = editor.spec.isfncall
local iscomment = editor.spec.iscomment
local iskeyword0 = editor.spec.iskeyword0
local isfncall = editor.spec.isfncall
local isinvalid = {}
for i,v in pairs(iscomment) do
isinvalid[i] = v
end
for i,v in pairs(iskeyword0) do
isinvalid[i] = v
end
for i,v in pairs(editor.spec.iscomment) do isinvalid[i] = v end
for i,v in pairs(editor.spec.iskeyword0) do isinvalid[i] = v end
for i,v in pairs(editor.spec.isstring) do isinvalid[i] = v end
local INDICS_MASK = wxstc.wxSTC_INDICS_MASK
local INDIC0_MASK = wxstc.wxSTC_INDIC0_MASK
@@ -500,13 +496,13 @@ function IndicateFunctions(editor, lines, linee)
while from do
tx = from==1 and tx or string.sub(tx,from)
local f,t,w = isfunc(tx)
local f,t,w = isfncall(tx)
if (f) then
local p = ls+f+off
local s = bit.band(editor:GetStyleAt(p),31)
editor:StartStyling(p,INDICS_MASK)
editor:SetStyling(t-f,isinvalid[s] and 0 or (INDIC0_MASK + 1))
editor:SetStyling(#w,isinvalid[s] and 0 or (INDIC0_MASK + 1))
off = off + t
end
from = t and (t+1)
@@ -533,11 +529,11 @@ function SetupKeywords(editor, ext, forcespec, styles, font, fontitalic)
-- Get the items in the global "wx" table for autocompletion
if not wxkeywords then
local keyword_table = {}
for index, value in pairs(wx) do
for index in pairs(wx) do
table.insert(keyword_table, "wx."..index.." ")
end
for index, value in pairs(wxstc) do
for index in pairs(wxstc) do
table.insert(keyword_table, "wxstc."..index.." ")
end
@@ -559,7 +555,7 @@ function SetupKeywords(editor, ext, forcespec, styles, font, fontitalic)
end
StylesApplyToEditor(styles or ide.config.styles, editor,
font or ide.font,fontitalic or ide.fontItalic,lexerstyleconvert)
font or ide.font.eNormal,fontitalic or ide.font.eItalic,lexerstyleconvert)
end
----------------------------------------------------
@@ -587,9 +583,13 @@ funclist:Connect(wx.wxEVT_SET_FOCUS,
local linee = editor:GetLineCount()-1
for line=lines,linee do
local tx = editor:GetLine(line)
local s,e,cap,l = editor.spec.isfndef(tx)
local s,_,cap,l = editor.spec.isfndef(tx)
if (s) then
funclist:Append((l and " " or "")..cap,line)
local ls = editor:PositionFromLine(line)
local style = bit.band(editor:GetStyleAt(ls+s),31)
if not (editor.spec.iscomment[style] or editor.spec.isstring[style]) then
funclist:Append((l and " " or "")..cap,line)
end
end
end

View File

@@ -30,14 +30,15 @@ local filetree = ide.filetree
-- ------------
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(wx.wxArtProvider.GetBitmap(wx.wxART_FOLDER, wx.wxART_OTHER, size))
filetree.imglist:Add(getBitmap(wx.wxART_FOLDER, wx.wxART_OTHER, size))
-- 1 = file known spec
filetree.imglist:Add(wx.wxArtProvider.GetBitmap(wx.wxART_HELP_PAGE, wx.wxART_OTHER, size))
filetree.imglist:Add(getBitmap(wx.wxART_HELP_PAGE, wx.wxART_OTHER, size))
-- 2 = file rest
filetree.imglist:Add(wx.wxArtProvider.GetBitmap(wx.wxART_NORMAL_FILE, wx.wxART_OTHER, size))
filetree.imglist:Add(getBitmap(wx.wxART_NORMAL_FILE, wx.wxART_OTHER, size))
end
local function treeAddDir(tree,parent_id,rootdir)
@@ -50,11 +51,10 @@ local function treeAddDir(tree,parent_id,rootdir)
end
local curr
local search = rootdir..string_Pathsep.."*.*"
local dirs = FileSysGet(search,wx.wxDIR)
local search = rootdir..string_Pathsep.."*"
-- append directories
for _,dir in ipairs(dirs) do
for _,dir in ipairs(FileSysGet(search,wx.wxDIR)) do
local name = dir:match("%"..string_Pathsep.."("..stringset_File.."+)$")
local icon = 0
local item = items[name .. icon]
@@ -78,8 +78,7 @@ local function treeAddDir(tree,parent_id,rootdir)
end
-- then append files
local files = FileSysGet(search,wx.wxFILE)
for _,file in ipairs(files) do
for _,file in ipairs(FileSysGet(search,wx.wxFILE)) do
local name = file:match("%"..string_Pathsep.."("..stringset_File.."+)$")
local known = GetSpec(GetFileExt(name))
local icon = known and 1 or 2
@@ -127,10 +126,8 @@ end
local function treeSetRoot(tree,treedata,rootdir)
tree:DeleteAllItems()
if (not wx.wxDirExists(rootdir)) then
treedata.root_id = nil
tree:AddRoot("Invalid")
return
end
@@ -199,7 +196,8 @@ local projtree = wx.wxTreeCtrl(projpanel, ID "filetree.projtree",
or (wx.wxTR_HAS_BUTTONS + wx.wxTR_SINGLE + wx.wxTR_HIDE_ROOT))
-- use the same font in the combobox as is used in the filetree
projcombobox:SetFont(projtree:GetFont())
projtree:SetFont(ide.font.fNormal)
projcombobox:SetFont(ide.font.fNormal)
local projTopSizer = wx.wxBoxSizer( wx.wxHORIZONTAL );
projTopSizer:Add(projcombobox, 1, wx.wxALL + wx.wxALIGN_LEFT + wx.wxGROW, 0)
@@ -308,7 +306,7 @@ end
local curr_id
function FileTreeMarkSelected(file)
if not file then return end
if not file or not filetree.projdirText or #filetree.projdirText == 0 then return end
local item_id = findItem(projtree, file)
if curr_id ~= item_id then
if curr_id and projtree:IsBold(curr_id) then
@@ -321,3 +319,7 @@ function FileTreeMarkSelected(file)
end
end
end
function FileTreeRefresh()
treeSetRoot(projtree,filetree.projdata,filetree.projdirText)
end

View File

@@ -219,9 +219,8 @@ end
local function ProcInFiles(startdir,mask,subdirs,replace)
if (subdirs) then
local dirs = FileSysGet(startdir..string_Pathsep.."*.*",wx.wxDIR)
local dirs = FileSysGet(startdir..string_Pathsep.."*",wx.wxDIR)
for i,dir in ipairs(dirs) do
--DisplayOutput(dir.."\n")
ProcInFiles(dir,mask,true,replace)
end
end
@@ -230,8 +229,6 @@ local function ProcInFiles(startdir,mask,subdirs,replace)
for i,file in ipairs(files) do
findReplace.curfilename = file
--DisplayOutput(file.."\n")
-- load file
local handle = io.open(file, "rb")
if handle then

View File

@@ -10,33 +10,23 @@ BREAKPOINT_MARKER_VALUE = 2 -- = 2^BREAKPOINT_MARKER
CURRENT_LINE_MARKER = 2
CURRENT_LINE_MARKER_VALUE = 4 -- = 2^CURRENT_LINE_MARKER
-- Globals
local font = nil -- fonts to use for the editor
local fontItalic = nil
local ofont = nil -- fonts to use for the outputshell
local ofontItalic = nil
-- ----------------------------------------------------------------------------
-- Pick some reasonable fixed width fonts to use for the editor
if wx.__WXMSW__ then
font = wx.wxFont(ide.config.editor.fontsize or 10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_NORMAL, wx.wxFONTWEIGHT_NORMAL, false, ide.config.editor.fontname or "Courier New")
fontItalic = wx.wxFont(ide.config.editor.fontsize or 10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_ITALIC, wx.wxFONTWEIGHT_NORMAL, false, ide.config.editor.fontname or "Courier New")
else
font = wx.wxFont(ide.config.editor.fontsize or 10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_NORMAL, wx.wxFONTWEIGHT_NORMAL, false, ide.config.editor.fontname or "")
fontItalic = wx.wxFont(ide.config.editor.fontsize or 10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_ITALIC, wx.wxFONTWEIGHT_NORMAL, false, ide.config.editor.fontname or "")
local function setFont(style, config, name, size)
return wx.wxFont(config.fontsize or size or 10, wx.wxFONTFAMILY_MODERN, style,
wx.wxFONTWEIGHT_NORMAL, false, config.fontname or name,
config.fontencoding or wx.wxFONTENCODING_DEFAULT)
end
ide.font.eNormal = setFont(wx.wxFONTSTYLE_NORMAL, ide.config.editor, wx.__WXMSW__ and "Courier New" or "")
ide.font.eItalic = setFont(wx.wxFONTSTYLE_ITALIC, ide.config.editor, wx.__WXMSW__ and "Courier New" or "")
if wx.__WXMSW__ then
ofont = wx.wxFont(ide.config.outputshell.fontsize or 10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_NORMAL, wx.wxFONTWEIGHT_NORMAL, false, ide.config.outputshell.fontname or "Courier New")
ofontItalic = wx.wxFont(ide.config.outputshell.fontsize or 10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_ITALIC, wx.wxFONTWEIGHT_NORMAL, false, ide.config.outputshell.fontname or "Courier New")
else
ofont = wx.wxFont(ide.config.outputshell.fontsize or 10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_NORMAL, wx.wxFONTWEIGHT_NORMAL, false, ide.config.outputshell.fontname or "")
ofontItalic = wx.wxFont(ide.config.outputshell.fontsize or 10, wx.wxFONTFAMILY_MODERN, wx.wxFONTSTYLE_ITALIC, wx.wxFONTWEIGHT_NORMAL, false, ide.config.outputshell.fontname or "")
end
ide.font.oNormal = setFont(wx.wxFONTSTYLE_NORMAL, ide.config.outputshell, wx.__WXMSW__ and "Courier New" or "")
ide.font.oItalic = setFont(wx.wxFONTSTYLE_ITALIC, ide.config.outputshell, wx.__WXMSW__ and "Courier New" or "")
ide.font = font
ide.fontItalic = fontItalic
ide.ofont = ofont
ide.ofontItalic = ofontItalic
-- treeCtrl font requires slightly different handling
local gui, config = wx.wxTreeCtrl():GetFont(), ide.config.filetree
if config.fontsize then gui:SetPointSize(config.fontsize) end
if config.fontname then gui:SetFaceName(config.fontname) end
ide.font.fNormal = gui
-- ----------------------------------------------------------------------------
-- Create the wxFrame
@@ -57,7 +47,6 @@ local function createFrame()
end)
local menuBar = wx.wxMenuBar()
frame:SetMenuBar(menuBar)
frame.menuBar = menuBar
local statusBar = frame:CreateStatusBar( 5 )
@@ -79,23 +68,24 @@ local function createToolBar(frame)
local funclist = wx.wxChoice.new(toolBar,ID "toolBar.funclist",wx.wxDefaultPosition, wx.wxSize.new(240,16))
-- note: Ususally the bmp size isn't necessary, but the HELP icon is not the right size in MSW
local getBitmap = (ide.app.createbitmap or wx.wxArtProvider.GetBitmap)
local toolBmpSize = toolBar:GetToolBitmapSize()
toolBar:AddTool(ID_NEW, "New", wx.wxArtProvider.GetBitmap(wx.wxART_NORMAL_FILE, wx.wxART_TOOLBAR, toolBmpSize), "Create an empty document")
toolBar:AddTool(ID_OPEN, "Open", wx.wxArtProvider.GetBitmap(wx.wxART_FILE_OPEN, wx.wxART_TOOLBAR, toolBmpSize), "Open an existing document")
toolBar:AddTool(ID_SAVE, "Save", wx.wxArtProvider.GetBitmap(wx.wxART_FILE_SAVE, wx.wxART_TOOLBAR, toolBmpSize), "Save the current document")
toolBar:AddTool(ID_SAVEALL, "Save All", wx.wxArtProvider.GetBitmap(wx.wxART_NEW_DIR, wx.wxART_TOOLBAR, toolBmpSize), "Save all documents")
toolBar:AddTool(ID_NEW, "New", getBitmap(wx.wxART_NORMAL_FILE, wx.wxART_TOOLBAR, toolBmpSize), "Create an empty document")
toolBar:AddTool(ID_OPEN, "Open", getBitmap(wx.wxART_FILE_OPEN, wx.wxART_TOOLBAR, toolBmpSize), "Open an existing document")
toolBar:AddTool(ID_SAVE, "Save", getBitmap(wx.wxART_FILE_SAVE, wx.wxART_TOOLBAR, toolBmpSize), "Save the current document")
toolBar:AddTool(ID_SAVEALL, "Save All", getBitmap(wx.wxART_NEW_DIR, wx.wxART_TOOLBAR, toolBmpSize), "Save all documents")
toolBar:AddSeparator()
toolBar:AddTool(ID_CUT, "Cut", wx.wxArtProvider.GetBitmap(wx.wxART_CUT, wx.wxART_TOOLBAR, toolBmpSize), "Cut the selection")
toolBar:AddTool(ID_COPY, "Copy", wx.wxArtProvider.GetBitmap(wx.wxART_COPY, wx.wxART_TOOLBAR, toolBmpSize), "Copy the selection")
toolBar:AddTool(ID_PASTE, "Paste", wx.wxArtProvider.GetBitmap(wx.wxART_PASTE, wx.wxART_TOOLBAR, toolBmpSize), "Paste text from the clipboard")
toolBar:AddTool(ID_CUT, "Cut", getBitmap(wx.wxART_CUT, wx.wxART_TOOLBAR, toolBmpSize), "Cut the selection")
toolBar:AddTool(ID_COPY, "Copy", getBitmap(wx.wxART_COPY, wx.wxART_TOOLBAR, toolBmpSize), "Copy the selection")
toolBar:AddTool(ID_PASTE, "Paste", getBitmap(wx.wxART_PASTE, wx.wxART_TOOLBAR, toolBmpSize), "Paste text from the clipboard")
toolBar:AddSeparator()
toolBar:AddTool(ID_UNDO, "Undo", wx.wxArtProvider.GetBitmap(wx.wxART_UNDO, wx.wxART_TOOLBAR, toolBmpSize), "Undo last edit")
toolBar:AddTool(ID_REDO, "Redo", wx.wxArtProvider.GetBitmap(wx.wxART_REDO, wx.wxART_TOOLBAR, toolBmpSize), "Redo last undo")
toolBar:AddTool(ID_UNDO, "Undo", getBitmap(wx.wxART_UNDO, wx.wxART_TOOLBAR, toolBmpSize), "Undo last edit")
toolBar:AddTool(ID_REDO, "Redo", getBitmap(wx.wxART_REDO, wx.wxART_TOOLBAR, toolBmpSize), "Redo last undo")
toolBar:AddSeparator()
toolBar:AddTool(ID_FIND, "Find", wx.wxArtProvider.GetBitmap(wx.wxART_FIND, wx.wxART_TOOLBAR, toolBmpSize), "Find text")
toolBar:AddTool(ID_REPLACE, "Replace", wx.wxArtProvider.GetBitmap(wx.wxART_FIND_AND_REPLACE, wx.wxART_TOOLBAR, toolBmpSize), "Find and replace text")
toolBar:AddTool(ID_FIND, "Find", getBitmap(wx.wxART_FIND, wx.wxART_TOOLBAR, toolBmpSize), "Find text")
toolBar:AddTool(ID_REPLACE, "Replace", getBitmap(wx.wxART_FIND_AND_REPLACE, wx.wxART_TOOLBAR, toolBmpSize), "Find and replace text")
toolBar:AddSeparator()
toolBar:AddTool(ID "debug.projectdir.fromfile", "Update", wx.wxArtProvider.GetBitmap(wx.wxART_GO_DIR_UP , wx.wxART_TOOLBAR, toolBmpSize), "Sets projectdir from file")
toolBar:AddTool(ID "debug.projectdir.fromfile", "Update", getBitmap(wx.wxART_GO_DIR_UP , wx.wxART_TOOLBAR, toolBmpSize), "Sets projectdir from file")
toolBar:AddSeparator()
toolBar:AddControl(funclist)
toolBar:Realize()

View File

@@ -37,15 +37,13 @@ ID_FIND_IN_FILES = NewID()
ID_REPLACE_IN_FILES = NewID()
ID_GOTOLINE = NewID()
ID_SORT = NewID()
-- Debug menu
-- Project/Debug menu
ID_TOGGLEBREAKPOINT = NewID()
ID_COMPILE = NewID()
ID_RUN = NewID()
ID_RUNNOW = NewID()
ID_ATTACH_DEBUG = NewID()
ID_START_DEBUG = NewID()
--ID_USECONSOLE = NewID()
ID_STOP_DEBUG = NewID()
ID_STEP = NewID()
ID_STEP_OVER = NewID()
@@ -53,10 +51,12 @@ ID_STEP_OUT = NewID()
ID_CONTINUE = NewID()
ID_BREAK = NewID()
ID_TRACE = NewID()
--ID_VIEWCALLSTACK = NewID()
--ID_VIEWWATCHWINDOW = NewID()
ID_VIEWCALLSTACK = NewID()
ID_VIEWWATCHWINDOW = NewID()
ID_FULLSCREEN = NewID()
ID_CLEAROUTPUT = NewID()
ID_DEBUGGER_PORT = NewID()
ID_PROJECTDIR = NewID()
ID_INTERPRETER = NewID()
-- Help menu
ID_ABOUT = wx.wxID_ABOUT
-- Watch window menu items
@@ -66,8 +66,6 @@ ID_EDITWATCH = NewID()
ID_REMOVEWATCH = NewID()
ID_EVALUATEWATCH = NewID()
ID_WORKDIR_CHOSE = NewID()
local ids = {}
function ID (name)
ids[name] = ids[name] or NewID()

View File

@@ -1,21 +1,26 @@
-- Integration with LuaInspect
-- (C) 2012 Paul Kulchenko
require "metalua"
local M = {}
local LA = require "luainspect.ast"
local LI = require "luainspect.init"
local T = require "luainspect.types"
local M, LA, LI, T = {}
local FAST = true
if FAST then
LI.eval_comments = function () end
LI.infer_values = function () end
local function init()
if LA then return end
require "metalua"
LA = require "luainspect.ast"
LI = require "luainspect.init"
T = require "luainspect.types"
if FAST then
LI.eval_comments = function () end
LI.infer_values = function () end
end
end
function M.warnings_from_string(src, file)
init()
local ast, err, linenum, colnum = LA.ast_from_string(src, file)
if err then return nil, err, linenum, colnum end
@@ -41,11 +46,15 @@ function M.show_warnings(top_ast)
LA.walk(top_ast, function(ast)
local line = ast.lineinfo and ast.lineinfo.first[1] or 0
local path = ast.lineinfo and ast.lineinfo.first[4] or '?'
local name = ast[1]
-- check if we're masking a variable in the same scope
if ast.localmasking and
if ast.localmasking and name ~= '_' and
ast.level == ast.localmasking.level then
local linenum = ast.localmasking.lineinfo.first[1]
warn("local variable '" .. ast[1] .. "' masks earlier declaration " ..
local parent = ast.parent and ast.parent.parent
local func = parent and parent.tag == 'Localrec'
warn("local " .. (func and 'function' or 'variable') .. " '" ..
name .. "' masks earlier declaration " ..
(linenum and "on line " .. linenum or "in the same scope"),
line, path)
end
@@ -54,40 +63,60 @@ function M.show_warnings(top_ast)
local parent = ast.parent and ast.parent.parent
local isparam = parent and parent.tag == 'Function'
if isparam then
if ast[1] ~= 'self' then
if name ~= 'self' then
local func = parent.parent and parent.parent.parent
local name = type(func[1][1][1]) == 'string' and func[1][1][1]
local assignment = not func.tag or func.tag == 'Set' or func.tag == 'Localrec'
local fname = assignment and type(func[1][1][1]) == 'string' and func[1][1][1]
-- "function foo(bar)" => func.tag == 'Set'
-- "local function foo(bar)" => func.tag == 'Localrec'
-- "local _, foo = 1, function(bar)" => func.tag == 'Local'
-- "print(function(bar) end)" => func.tag == nil
warn("unused parameter '" .. ast[1] .. "'" ..
(func and (not func.tag or func.tag == 'Set' or func.tag == 'Localrec')
and (name and func.tag
and (" in function '" .. name .. "'")
warn("unused parameter '" .. name .. "'" ..
(func and assignment
and (fname and func.tag
and (" in function '" .. fname .. "'")
or " in anonymous function")
or ""),
line, path)
end
else
warn("unused local variable '" .. ast[1] ..
"'; consider removing or replacing with '_'",
line, path)
if parent.tag == 'Localrec' then -- local function foo...
warn("unused local function '" .. name .. "'", line, path)
else
warn("unused local variable '" .. name .. "'; "..
"consider removing or replacing with '_'", line, path)
end
end
end
-- added check for FAST as ast.seevalue relies on value evaluation,
-- which is very slow even on simple and short scripts
if not FAST and ast.isfield and not(known(ast.seevalue.value) and ast.seevalue.value ~= nil) then
warn("unknown field " .. ast[1], ast.lineinfo.first[1], path)
warn("unknown field " .. name, ast.lineinfo.first[1], path)
elseif ast.tag == 'Id' and not ast.localdefinition and not ast.definedglobal then
warn("unknown global variable '" .. ast[1] .. "'", line, path)
if not globseen[name] then
globseen[name] = true
local parent = ast.parent
-- if being called and not one of the parameters
if parent and parent.tag == 'Call' and parent[1] == ast then
warn("first use of unknown global function '" .. name .. "'", line, path)
else
warn("first use of unknown global variable '" .. name .. "'", line, path)
end
end
elseif ast.tag == 'Id' and not ast.localdefinition and ast.definedglobal then
local parent = ast.parent and ast.parent.parent
if parent and parent.tag == 'Set' and not globseen[ast[1]] -- report assignments to global
if parent and parent.tag == 'Set' and not globseen[name] -- report assignments to global
-- only report if it is on the left side of the assignment
-- this is a bit tricky as it can be assigned as part of a, b = c, d
-- `Set{ {lhs+} {expr+} } -- lhs1, lhs2... = e1, e2...
and parent[1] == ast.parent
and parent[2][1].tag ~= "Function" then -- but ignore global functions
warn("first assignment to global variable '" .. ast[1] .. "'", line, path)
globseen[ast[1]] = true
warn("first assignment to global variable '" .. name .. "'", line, path)
globseen[name] = true
end
elseif (ast.tag == 'Set' or ast.tag == 'Local') and #(ast[2]) > #(ast[1]) then
warn(("value discarded in multiple assignment: %d values assigned to %d variable%s")
:format(#(ast[2]), #(ast[1]), #(ast[1]) > 1 and 's' or ''), line, path)
end
local vast = ast.seevalue or ast
local note = vast.parent
@@ -95,7 +124,7 @@ function M.show_warnings(top_ast)
and vast.parent.note
if note and not isseen[vast.parent] then
isseen[vast.parent] = true
warn("function '" .. ast[1] .. "': " .. note, line, path)
warn("function '" .. name .. "': " .. note, line, path)
end
end)
return warnings
@@ -131,7 +160,8 @@ local function analyzeProgram(editor)
return false
end
DisplayOutput(": " .. (warn and #warn > 0 and (#warn .. " warnings") or "no warnings.") .. "\n")
DisplayOutput((": %s warning%s.\n")
:format(#warn > 0 and #warn or 'no', #warn == 1 and '' or 's'))
DisplayOutputNoMarker(table.concat(warn, "\n") .. "\n")
return true -- analyzed ok

View File

@@ -39,7 +39,7 @@ ide.iofilters["GermanUtf8Ascii"] = {
}
local lst = "["
for k in pairs(charconv) do lst = lst .. k end
lst = "]"
lst = lst.."]"
return content:gsub(lst,charconv)
end,

View File

@@ -5,12 +5,13 @@ local styles = ide.config.styles
local comment = styles.comment
local MD_MARK_ITAL = '_' -- italic
local MD_MARK_BOLD = '**' -- bold
local MD_MARK_LINK = '[' -- link
local MD_MARK_LINT = ')' -- link terminator
local MD_MARK_LINK = '[' -- link description start
local MD_MARK_LINZ = ']' -- link description end
local MD_MARK_LINA = '(' -- link URL start
local MD_MARK_LINT = ')' -- link URL end
local MD_MARK_HEAD = '#' -- header
local MD_MARK_CODE = '`' -- code
local MD_MARK_BOXD = '|' -- highlight
local MD_MARK_LSEP = '](' -- link separator (between text and link)
local MD_MARK_MARK = ' ' -- separator
local MD_LINK_NEWWINDOW = '+' -- indicator to open a new window for links
local markup = {
@@ -45,13 +46,13 @@ function MarkupHotspotClick(pos, editor)
pos = pos + #MD_MARK_LINK - editor:PositionFromLine(line) -- turn into relative position
-- extract the URL/command on the right side of the separator
local _,_,text = string.find(tx, q(MD_MARK_LSEP).."([^%s]+)"..q(MD_MARK_LINT), pos)
local _,_,text = string.find(tx, q(MD_MARK_LINZ).."(%b"..MD_MARK_LINA..MD_MARK_LINT..")", pos)
if text then
text = text:gsub("^"..q(MD_MARK_LINA), ""):gsub(q(MD_MARK_LINT).."$", "")
local filepath = ide.openDocuments[editor:GetId()].filePath
local _,_,shell = string.find(text, [[^macro:shell%((.*%S)%)$]])
local _,_,http = string.find(text, [[^(http:%S+)$]])
local _,_,http = string.find(text, [[^(https?:%S+)$]])
local _,_,command = string.find(text, [[^macro:(%w+)$]])
local bottomnotebook = ide.frame.bottomnotebook
if shell then
ShellExecuteCode(shell)
elseif command == 'run' then -- run the current file
@@ -60,7 +61,7 @@ function MarkupHotspotClick(pos, editor)
ProjectDebug()
elseif http then -- open the URL in a new browser window
wx.wxLaunchDefaultBrowser(http, 0)
else
elseif filepath then -- only check for saved files
-- check if requested to open in a new window
local newwindow = string.find(text, MD_LINK_NEWWINDOW, 1, true) -- plain search
if newwindow then text = string.gsub(text, "^%" .. MD_LINK_NEWWINDOW, "") end
@@ -70,7 +71,8 @@ function MarkupHotspotClick(pos, editor)
local filename = wx.wxFileName(name)
filename:Normalize() -- remove .., ., and other similar elements
if filename:FileExists() and
(newindow or SaveModifiedDialog(editor, true) ~= wx.wxID_CANCEL) then
(newwindow or SaveModifiedDialog(editor, true) ~= wx.wxID_CANCEL) then
if not newwindow and ide.osname == 'Macintosh' then editor:GotoPos(0) end
LoadFile(filename,not newwindow and editor or nil,true)
end
end
@@ -97,10 +99,10 @@ local function ismarkup (tx)
-- [%w%p] set is needed to avoid continuing this markup to the next line
s,e,cap = string.find(tx,"^("..q(MD_MARK_HEAD)..".+[%w%p])")
elseif sep == MD_MARK_LINK then
-- allow everything except spaces in the second part
s,e,cap = string.find(tx,"^("..q(MD_MARK_LINK)..nonspace..".-"..nonspace
..q(MD_MARK_LSEP).."[^%s]+"
..q(MD_MARK_LINT)..")", st)
-- allow everything based on balanced link separators
s,e,cap = string.find(tx,
"^(%b"..MD_MARK_LINK..MD_MARK_LINZ
.."%b"..MD_MARK_LINA..MD_MARK_LINT..")", st)
elseif markup[sep] then
-- try 2+ characters between separators first
-- if not found, try a single character
@@ -145,7 +147,9 @@ function MarkupStyle(editor, lines, linee)
if (f) then
local p = ls+f+off
local s = bit.band(editor:GetStyleAt(p), 31)
if iscomment[s] then
-- only style comments and only those that are not at the beginning
-- of the file to avoid styling shebang (#!) lines
if iscomment[s] and p > 0 then
local smark = #mark
local emark = #mark -- assumes end mark is the same length as start mark
if mark == MD_MARK_HEAD then
@@ -153,7 +157,7 @@ function MarkupStyle(editor, lines, linee)
local _,_,full = string.find(w,"^("..q(MD_MARK_HEAD).."+)")
smark,emark = #full,0
elseif mark == MD_MARK_LINK then
local lsep = w:find(q(MD_MARK_LSEP))
local lsep = w:find(q(MD_MARK_LINZ)..q(MD_MARK_LINA))
if lsep then emark = #w-lsep+#MD_MARK_LINT end
end
editor:StartStyling(p, 31)

View File

@@ -8,5 +8,3 @@ dofile "src/editor/menu_search.lua"
dofile "src/editor/menu_view.lua"
dofile "src/editor/menu_project.lua"
dofile "src/editor/menu_tools.lua"
ide.frame:SetMenuBar(ide.frame.menuBar )

View File

@@ -17,11 +17,11 @@ local editMenu = wx.wxMenu{
{ ID_UNDO, "&Undo\tCtrl-Z", "Undo the last action" },
{ ID_REDO, "&Redo\tCtrl-Y", "Redo the last action undone" },
{ },
{ ID "edit.showtooltip", "Show &Tooltip\tCtrl+T", "Show tooltip for current position. Place cursor after opening bracket of function."},
{ ID_AUTOCOMPLETE, "Complete &Identifier\tCtrl+K", "Complete the current identifier" },
{ ID "edit.showtooltip", "Show &Tooltip\tCtrl-T", "Show tooltip for current position. Place cursor after opening bracket of function."},
{ ID_AUTOCOMPLETE, "Complete &Identifier\tCtrl-K", "Complete the current identifier" },
{ ID_AUTOCOMPLETE_ENABLE, "Auto complete Identifiers", "Auto complete while typing", wx.wxITEM_CHECK },
{ },
{ ID_COMMENT, "C&omment/Uncomment\tCtrl-Q", "Comment or uncomment current or selected lines"},
{ ID_COMMENT, "C&omment/Uncomment\tCtrl-U", "Comment or uncomment current or selected lines"},
{ },
{ ID_FOLD, "&Fold/Unfold all\tF12", "Fold or unfold all code folds"},
{ ID "edit.cleardynamics", "Clear &Dynamic Words", "Resets the dynamic word list for autcompletion."},
@@ -35,13 +35,13 @@ function OnUpdateUIEditMenu(event) -- enable if there is a valid focused editor
event:Enable(editor ~= nil)
end
local shellboxeditor = ide.frame.bottomnotebook.shellbox
local othereditors = { frame.bottomnotebook.shellbox, frame.bottomnotebook.errorlog }
function OnEditMenu(event)
local menu_id = event:GetId()
local editor = GetEditor()
if shellboxeditor:FindFocus():GetId() == shellboxeditor:GetId() then
editor = shellboxeditor
for _,e in pairs(othereditors) do
if e:FindFocus():GetId() == e:GetId() then editor = e end
end
if editor == nil then return end
@@ -93,22 +93,38 @@ frame:Connect(ID "edit.cleardynamics", wx.wxEVT_COMMAND_MENU_SELECTED,
frame:Connect(ID "edit.showtooltip", wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
local editor = GetEditor()
if (editor:CallTipActive()) then
editor:CallTipCancel()
return
end
local pos = editor:GetCurrentPos()
local line = editor:GetCurrentLine()
local linetx = editor:GetLine(line)
local linestart = editor:PositionFromLine(line)
local localpos = pos-linestart
local ident = "([a-zA-Z_0-9][a-zA-Z_0-9%.%:]*)"
local linetxtopos = linetx:sub(1,localpos)
linetxtopos = linetxtopos..")"
linetxtopos = linetxtopos:match("([a-zA-Z_0-9%.%:]+)%b()$")
linetxtopos = linetxtopos:match(ident .. "%b()$")
local tip = linetxtopos and GetTipInfo(editor,linetxtopos.."(",false)
if tip then
if(editor:CallTipActive()) then
editor:CallTipCancel()
editor:CallTipShow(pos, tip)
else
-- check if we have a selected text or an identifier
-- for an identifier, check fragments on the left and on the right.
-- this is to match 'io' in 'i^o.print' and 'io.print' in 'io.pr^int'
local left = linetx:sub(1,localpos):match(ident.."$")
local right = linetx:sub(localpos+1,#linetx):match("^[a-zA-Z_0-9]*")
local var = editor:GetSelectionStart() ~= editor:GetSelectionEnd()
and editor:GetSelectedText()
or left and left..right or nil
if var and ide.debugger then
ide.debugger.quickeval(var, function(val) editor:CallTipShow(pos, val) end)
end
editor:CallTipShow(pos,tip)
end
end)

View File

@@ -7,20 +7,19 @@ local ide = ide
local frame = ide.frame
local menuBar = frame.menuBar
local openDocuments = ide.openDocuments
local debugger = ide.debugger
local fileMenu = wx.wxMenu({
{ ID_NEW, "&New\tCtrl-N", "Create an empty document" },
{ ID_OPEN, "&Open...\tCtrl-O", "Open an existing document" },
{ ID_CLOSE, "&Close page\tCtrl+W", "Close the current editor window" },
{ ID_CLOSE, "&Close page\tCtrl-W", "Close the current editor window" },
{ },
{ ID_SAVE, "&Save\tCtrl-S", "Save the current document" },
{ ID_SAVEAS, "Save &As...\tAlt-Shift-S", "Save the current document to a file with a new name" },
{ ID_SAVEALL, "Save A&ll...\tCtrl-Shift-S", "Save all open documents" },
{ ID_SAVEALL, "Save A&ll...", "Save all open documents" },
{ },
--{ ID "file.recentfiles", "Recent files",},
{ },
{ ID_EXIT, "E&xit\tAlt-X", "Exit Program" }})
{ ID_EXIT, "E&xit", "Exit Program" }})
menuBar:Append(fileMenu, "&File")
local filehistorymenu = wx.wxMenu({})

View File

@@ -46,17 +46,16 @@ local debugTab = {
{ ID_RUNNOW, "Run as Scratchpad\tCtrl-F6", "Execute the current project/file and keep updating the code to see immediate results", wx.wxITEM_CHECK },
{ ID_COMPILE, "&Compile\tF7", "Test compile the Lua file" },
{ ID_START_DEBUG, "Start &Debugging\tF5", "Start a debugging session" },
{ ID_ATTACH_DEBUG, "&Start Debugger Server\tShift-F6", "Allow a client to start a debugging session" },
{ ID_ATTACH_DEBUG, "&Start Debugger Server", "Allow a client to start a debugging session" },
{ },
{ ID_STOP_DEBUG, "S&top Debugging\tShift-F12", "Stop the currently running process" },
{ ID_STEP, "St&ep\tF11", "Step into the next line" },
{ ID_STEP_OVER, "Step &Over\tF10", "Step over the next line" },
{ ID_STEP_OUT, "Step O&ut\tShift-F10", "Step out of the current function" },
{ ID_TRACE, "Tr&ace", "Trace execution showing each executed line" },
{ ID_BREAK, "&Break", "Stop execution of the program at the next executed line of code" },
{ ID_BREAK, "&Break\tShift-F9", "Stop execution of the program at the next executed line of code" },
{ },
{ ID_TOGGLEBREAKPOINT, "Toggle &Breakpoint\tF9", "Toggle Breakpoint" },
--{ ID "view.debug.callstack", "V&iew Call Stack", "View the call stack" },
{ ID_TOGGLEBREAKPOINT, "Toggle Break&point\tF9", "Toggle Breakpoint" },
{ },
{ ID_CLEAROUTPUT, "C&lear Output Window", "Clear the output window before compiling or debugging", wx.wxITEM_CHECK },
}
@@ -72,8 +71,8 @@ local targetDirMenu = wx.wxMenu{
{ID "debug.projectdir.currentdir",""}
}
debugMenu:Append(0,"Lua &interpreter",targetMenu,"Set the interpreter to be used")
debugMenu:Append(0,"Project directory",targetDirMenu,"Set the project directory to be used")
debugMenu:Append(ID_INTERPRETER,"Lua &Interpreter",targetMenu,"Set the interpreter to be used")
debugMenu:Append(ID_PROJECTDIR,"Project Directory",targetDirMenu,"Set the project directory to be used")
menuBar:Append(debugMenu, "&Project")
-----------------------------
@@ -221,7 +220,7 @@ function ProjectDebug(skipcheck, debtype)
end
else
local debcall = (debuggers[debtype or "debug"]):
format(wx.wxGetHostName(), ide.debugger.portnumber)
format(ide.debugger.hostname, ide.debugger.portnumber)
local fname = getNameToRun(skipcheck)
if not fname then return end
runInterpreter(fname, debcall)
@@ -274,9 +273,10 @@ frame:Connect(ID_RUNNOW, wx.wxEVT_COMMAND_MENU_SELECTED,
frame:Connect(ID_RUNNOW, wx.wxEVT_UPDATE_UI,
function (event)
local editor = GetEditor()
-- allow scratchpad if there is no server or there is a server, but it is
-- allowed to turn it into a scratchpad and we are not debugging anything
event:Enable((editor ~= nil) and ((debugger.server == nil or debugger.scratchable)
-- allow scratchpad if there is no server or (there is a server and it is
-- allowed to turn it into a scratchpad) and we are not debugging anything
event:Enable((ide.interpreter) and (ide.interpreter.hasdebugger) and
(editor ~= nil) and ((debugger.server == nil or debugger.scratchable)
and debugger.pid == nil or debugger.scratchpad ~= nil))
end)
@@ -311,8 +311,7 @@ frame:Connect(ID_START_DEBUG, wx.wxEVT_UPDATE_UI,
frame:Connect(ID_STOP_DEBUG, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
ClearAllCurrentLineMarkers()
if debugger.server then debugger.terminate() end
if debugger.pid then DebuggerKillClient() end
DebuggerShutdown()
end)
frame:Connect(ID_STOP_DEBUG, wx.wxEVT_UPDATE_UI,
function (event)
@@ -386,19 +385,6 @@ frame:Connect(ID_BREAK, wx.wxEVT_UPDATE_UI,
and (editor ~= nil) and (not debugger.scratchpad))
end)
--[[
frame:Connect(ID "view.debug.callstack", wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
if debugger.server then
DebuggerCreateStackWindow()
end
end)
frame:Connect(ID "view.debug.callstack", wx.wxEVT_UPDATE_UI,
function (event)
event:Enable((debugger.server ~= nil) and (not debugger.running))
end)
]]
frame:Connect(wx.wxEVT_IDLE,
function(event)
if (debugger.update) then debugger.update() end

View File

@@ -13,10 +13,10 @@ local findMenu = wx.wxMenu{
{ ID_FIND, "&Find\tCtrl-F", "Find the specified text" },
{ ID_FINDNEXT, "Find &Next\tF3", "Find the next occurrence of the specified text" },
{ ID_FINDPREV, "Find &Previous\tShift-F3", "Repeat the search backwards in the file" },
{ ID_REPLACE, "&Replace\tCtrl-H", "Replaces the specified text with different text" },
{ ID_REPLACE, "&Replace\tCtrl-R", "Replaces the specified text with different text" },
{ },
{ ID_FIND_IN_FILES, "Find &In Files\tCtrl-Shift-F", " Find specified text in files"},
{ ID_REPLACE_IN_FILES, "Re&place In Files\tCtrl-Shift-H", " Replace specified text in files"},
{ ID_REPLACE_IN_FILES, "Re&place In Files\tCtrl-Shift-R", " Replace specified text in files"},
{ },
{ ID_GOTOLINE, "&Goto line\tCtrl-G", "Go to a selected line" },
{ },

View File

@@ -6,17 +6,16 @@ local frame = ide.frame
local menuBar = frame.menuBar
local uimgr = frame.uimgr
local debugger = ide.debugger
local viewMenu = wx.wxMenu{
local viewMenu = wx.wxMenu {
-- NYI { ID "view.preferences", "&Preferences...", "Brings up dialog for settings (TODO)" },
-- NYI { },
{ ID "view.filetree.show", "Project/&FileTree Window\tCtrl-Alt-P", "View the project/filetree window" },
{ ID "view.output.show", "&Output/Shell Window\tCtrl-Alt-O", "View the output/shell window" },
{ ID "view.debug.watches", "&Watch Window", "View the Watch window" },
{ ID "view.filetree.show", "Project/&FileTree Window\tCtrl-Shift-P", "View the project/filetree window" },
{ ID "view.output.show", "&Output/Console Window\tCtrl-Shift-O", "View the output/console window" },
{ ID_VIEWWATCHWINDOW, "&Watch Window\tCtrl-Shift-W", "View the Watch window" },
{ ID_VIEWCALLSTACK, "&Stack Window\tCtrl-Shift-S", "View the Stack window" },
{ },
{ ID "view.defaultlayout", "&Default Layout", "Reset to default layout"},
{ ID "view.fullscreen", "Full &Screen\tCtrl-Alt-F", "Switch to or from full screen mode"},
{ ID_FULLSCREEN, "Full &Screen\tCtrl-Shift-A", "Switch to or from full screen mode"},
{ ID "view.style.loadconfig", "&Load Config Style...", "Load and apply style from config file (must contain .styles)"},
}
menuBar:Append(viewMenu, "&View")
@@ -46,12 +45,12 @@ frame:Connect(ID "view.filetree.show", wx.wxEVT_COMMAND_MENU_SELECTED,
uimgr:Update()
end)
frame:Connect(ID "view.fullscreen", wx.wxEVT_COMMAND_MENU_SELECTED,
function (event) ShowFullScreen(not frame:IsFullScreen()) end)
frame:Connect(ID "view.debug.watches", wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
if not debugger.watchWindow then
DebuggerCreateWatchWindow()
end
frame:Connect(ID_FULLSCREEN, wx.wxEVT_COMMAND_MENU_SELECTED, function ()
pcall(function() ShowFullScreen(not frame:IsFullScreen()) end)
end)
frame:Connect(ID_VIEWWATCHWINDOW, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event) DebuggerCreateWatchWindow() end)
frame:Connect(ID_VIEWCALLSTACK, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event) DebuggerCreateStackWindow() end)

View File

@@ -9,15 +9,20 @@ local errorlog = bottomnotebook.errorlog
-------
-- setup errorlog
local INPUT_MARKER = 3
local INPUT_MARKER_VALUE = 2^INPUT_MARKER
errorlog:Show(true)
errorlog:SetFont(ide.ofont)
errorlog:StyleSetFont(wxstc.wxSTC_STYLE_DEFAULT, ide.ofont)
errorlog:SetFont(ide.font.oNormal)
errorlog:StyleSetFont(wxstc.wxSTC_STYLE_DEFAULT, ide.font.oNormal)
errorlog:StyleClearAll()
errorlog:SetMarginWidth(1, 16) -- marker margin
errorlog:SetMarginType(1, wxstc.wxSTC_MARGIN_SYMBOL);
errorlog:MarkerDefine(CURRENT_LINE_MARKER, wxstc.wxSTC_MARK_ARROWS, wx.wxBLACK, wx.wxWHITE)
errorlog:MarkerDefine(INPUT_MARKER, wxstc.wxSTC_MARK_CHARACTER+string.byte('>'),
wx.wxColour(127, 127, 127), wx.wxColour(240, 240, 240))
errorlog:SetReadOnly(true)
StylesApplyToEditor(ide.config.stylesoutshell,errorlog,ide.ofont,ide.ofontItalic)
StylesApplyToEditor(ide.config.stylesoutshell,errorlog,ide.font.oNormal,ide.font.oItalic)
function ClearOutput()
errorlog:SetReadOnly(false)
@@ -33,9 +38,11 @@ function DisplayOutputNoMarker(...)
message = message..tostring(v)..(i<cnt and "\t" or "")
end
local current = errorlog:GetReadOnly()
errorlog:SetReadOnly(false)
errorlog:AppendText(message)
errorlog:SetReadOnly(true)
errorlog:EmptyUndoBuffer()
errorlog:SetReadOnly(current)
errorlog:GotoPos(errorlog:GetLength())
end
function DisplayOutput(...)
@@ -45,7 +52,9 @@ end
local streamins = {}
local streamerrs = {}
local streamouts = {}
local customprocs = {}
local textout = '' -- this is a buffer for any text sent to external scripts
function CommandLineRunning(uid)
for pid,custom in pairs(customprocs) do
@@ -89,32 +98,33 @@ local function unHideWxWindow(pidAssign)
end
end
local function nameTab(tab, name)
local index = bottomnotebook:GetPageIndex(tab)
if index then bottomnotebook:SetPageText(index, name) end
end
function CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
if (not cmd) then return end
-- try to extract the name of the executable from the command
-- the executable may not have the extension and may be in quotes
local exename = string.gsub(cmd, "\\", "/")
exename = string.match(exename,'%/*([^%/]+%.%w+)') or exename
exename = string.match(exename,'%/*([^%/]+%.%w+)[%s%"]') or exename
local _,_,fullname = string.find(exename,'^[\'"]([^\'"]+)[\'"]')
exename = fullname and string.match(fullname,'/?([^/]+)$')
or string.match(exename,'/?([^/]-)%s') or exename
uid = uid or exename
if (CommandLineRunning(uid)) then
DisplayOutput("Conflicting Process still running: "..cmd.."\n")
DisplayOutput(("Program can't start because conflicting process is running as '%s'.\n")
:format(cmd))
return
end
DisplayOutput("Running program: "..cmd.."\n")
DisplayOutput(("Program starting as '%s'.\n"):format(cmd))
local pid = -1
local proc = nil
local customproc
if (tooutput) then
customproc = wx.wxProcess(errorlog)
customproc:Redirect()
proc = customproc
end
local proc = wx.wxProcess(errorlog)
if (tooutput) then proc:Redirect() end -- redirect the output if requested
-- manipulate working directory
local oldcwd
@@ -124,42 +134,64 @@ function CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
end
-- launch process
local pid = (proc and wx.wxExecute(cmd, wx.wxEXEC_ASYNC + (nohide and wx.wxEXEC_NOHIDE or 0),proc) or
wx.wxExecute(cmd, wx.wxEXEC_ASYNC + (nohide and wx.wxEXEC_NOHIDE or 0)))
local params = wx.wxEXEC_ASYNC + wx.wxEXEC_MAKE_GROUP_LEADER + (nohide and wx.wxEXEC_NOHIDE or 0)
local pid = wx.wxExecute(cmd, params, proc)
if (oldcwd) then
wx.wxFileName.SetCwd(oldcwd)
end
-- check process
if not pid or pid == -1 then
DisplayOutputNoMarker("Unknown ERROR Running program!\n")
customproc = nil
-- For asynchronous execution, the return value is the process id and
-- zero value indicates that the command could not be executed.
-- The return value of -1 in this case indicates that we didn't launch
-- a new process, but connected to the running one (e.g. DDE under Windows).
if not pid or pid == -1 or pid == 0 then
DisplayOutput(("Program unable to run as '%s'\n"):format(cmd))
return
else
DisplayOutput(
"Process: "..uid..", pid:"..tostring(pid)..
", started in '"..(wdir and wdir or wx.wxFileName.GetCwd()).."'\n")
customprocs[pid] = {proc=customproc, uid=uid, endcallback=endcallback}
end
DisplayOutput(("Program '%s' started in '%s' (pid: %d).\n")
:format(uid, (wdir and wdir or wx.wxFileName.GetCwd()), pid))
customprocs[pid] = {proc=proc, uid=uid, endcallback=endcallback, started = os.clock()}
local streamin = proc and proc:GetInputStream()
local streamerr = proc and proc:GetErrorStream()
local streamout = proc and proc:GetOutputStream()
if (streamin) then
streamins[pid] = {stream=streamin, callback=stringcallback}
end
if (streamerr) then
streamerrs[pid] = {stream=streamerr, callback=stringcallback}
end
if (streamout) then
streamouts[pid] = {stream=streamout, callback=stringcallback, out=true}
end
unHideWxWindow(pid)
nameTab(errorlog, "Output (running)")
return pid
end
local inputBound -- to track where partial output ends for input editing purposes
local function getInputLine()
local totalLines = errorlog:GetLineCount()
return errorlog:MarkerPrevious(totalLines+1, INPUT_MARKER_VALUE)
end
local function getInputText(bound)
return errorlog:GetTextRange(
errorlog:PositionFromLine(getInputLine())+(bound or 0), errorlog:GetLength())
end
local function updateInputMarker()
local lastline = errorlog:GetLineCount()-1
errorlog:MarkerDeleteAll(INPUT_MARKER)
errorlog:MarkerAdd(lastline, INPUT_MARKER)
inputBound = #getInputText()
end
local function getStreams()
local function displayStream(tab)
for i,v in pairs(tab) do
local function readStream(tab)
for _,v in pairs(tab) do
while(v.stream:CanRead()) do
local str = v.stream:Read(4096)
local pfn
@@ -171,35 +203,66 @@ local function getStreams()
else
DisplayOutputNoMarker(str)
end
if str and ide.config.allowinteractivescript and
(getInputLine() > -1 or errorlog:GetReadOnly()) then
ActivateOutput()
updateInputMarker()
end
pfn = pfn and pfn()
end
end
end
local function sendStream(tab)
local str = textout
if not str then return end
textout = nil
str = str .. "\n"
for _,v in pairs(tab) do
local pfn
if (v.callback) then
str,pfn = v.callback(str)
end
v.stream:Write(str, #str)
updateInputMarker()
pfn = pfn and pfn()
end
end
displayStream(streamins)
displayStream(streamerrs)
readStream(streamins)
readStream(streamerrs)
sendStream(streamouts)
end
errorlog:Connect(wx.wxEVT_END_PROCESS, function(event)
local pid = event:GetPid()
if (pid ~= -1) then
getStreams()
-- delete markers and set focus to the editor if there is an input marker
if errorlog:MarkerPrevious(errorlog:GetLineCount(), INPUT_MARKER_VALUE) > -1 then
errorlog:MarkerDeleteAll(INPUT_MARKER)
local editor = GetEditor()
-- check if editor still exists; it may not if the window is closed
if editor then editor:SetFocus() end
end
nameTab(errorlog, "Output")
local runtime = os.clock() - customprocs[pid].started
streamins[pid] = nil
streamerrs[pid] = nil
streamouts[pid] = nil
if (customprocs[pid].endcallback) then
customprocs[pid].endcallback()
end
customprocs[pid] = nil
unHideWxWindow(0)
DebuggerStop()
DisplayOutput("Program finished (pid: "..pid..").\n")
DisplayOutput(("Program completed in %.2f seconds (pid: %d).\n")
:format(runtime, pid))
end
end)
errorlog:Connect(wx.wxEVT_IDLE, function(event)
if (#streamins or #streamerrs) then
getStreams()
end
errorlog:Connect(wx.wxEVT_IDLE, function()
if (#streamins or #streamerrs) then getStreams() end
unHideWxWindow()
end)
@@ -215,7 +278,7 @@ local jumptopatterns = {
}
errorlog:Connect(wxstc.wxEVT_STC_DOUBLECLICK,
function(event)
function()
local line = errorlog:GetCurrentLine()
local linetx = errorlog:GetLine(line)
-- try to detect a filename + line
@@ -225,7 +288,7 @@ errorlog:Connect(wxstc.wxEVT_STC_DOUBLECLICK,
local jumpline
local jumplinepos
for i,pattern in ipairs(jumptopatterns) do
for _,pattern in ipairs(jumptopatterns) do
fname,jumpline,jumplinepos = linetx:match(pattern)
if (fname and jumpline) then
break
@@ -244,5 +307,92 @@ errorlog:Connect(wxstc.wxEVT_STC_DOUBLECLICK,
editor:SetFocus()
end
end
end)
local function positionInLine(line)
return errorlog:GetCurrentPos() - errorlog:PositionFromLine(line)
end
local function caretOnInputLine(disallowLeftmost)
local inputLine = getInputLine()
local boundary = inputBound + (disallowLeftmost and 0 or -1)
return (errorlog:GetCurrentLine() > inputLine
or errorlog:GetCurrentLine() == inputLine
and positionInLine(inputLine) > boundary)
end
errorlog:Connect(wx.wxEVT_KEY_DOWN,
function (event)
-- this loop is only needed to allow to get to the end of function easily
-- "return" aborts the processing and ignores the key
-- "break" aborts the processing and processes the key normally
while true do
-- no special processing if it's readonly
if errorlog:GetReadOnly() then break end
local key = event:GetKeyCode()
if key == wx.WXK_UP or key == wx.WXK_NUMPAD_UP then
if errorlog:GetCurrentLine() > getInputLine() then break
else return end
elseif key == wx.WXK_DOWN or key == wx.WXK_NUMPAD_DOWN then
break -- can go down
elseif key == wx.WXK_LEFT or key == wx.WXK_NUMPAD_LEFT then
if not caretOnInputLine(true) then return end
elseif key == wx.WXK_BACK then
if not caretOnInputLine(true) then return end
elseif key == wx.WXK_DELETE or key == wx.WXK_NUMPAD_DELETE then
if not caretOnInputLine()
or errorlog:LineFromPosition(errorlog:GetSelectionStart()) < getInputLine() then
return
end
elseif key == wx.WXK_PAGEUP or key == wx.WXK_NUMPAD_PAGEUP
or key == wx.WXK_PAGEDOWN or key == wx.WXK_NUMPAD_PAGEDOWN
or key == wx.WXK_END or key == wx.WXK_NUMPAD_END
or key == wx.WXK_HOME or key == wx.WXK_NUMPAD_HOME
or key == wx.WXK_RIGHT or key == wx.WXK_NUMPAD_RIGHT
or key == wx.WXK_SHIFT or key == wx.WXK_CONTROL
or key == wx.WXK_ALT then
break
elseif key == wx.WXK_RETURN or key == wx.WXK_NUMPAD_ENTER then
if not caretOnInputLine()
or errorlog:LineFromPosition(errorlog:GetSelectionStart()) < getInputLine() then
return
end
errorlog:GotoPos(errorlog:GetLength()) -- move to the end
textout = (textout or '') .. getInputText(inputBound)
-- remove selection if any, otherwise the text gets replaced
errorlog:SetSelection(errorlog:GetSelectionEnd()+1,errorlog:GetSelectionEnd())
break -- don't need to do anything else with return
else
-- move cursor to end if not already there
if not caretOnInputLine() then
errorlog:GotoPos(errorlog:GetLength())
-- check if the selection starts before the input line and reset it
elseif errorlog:LineFromPosition(errorlog:GetSelectionStart()) < getInputLine(-1) then
errorlog:GotoPos(errorlog:GetLength())
errorlog:SetSelection(errorlog:GetSelectionEnd()+1,errorlog:GetSelectionEnd())
end
end
break
end
event:Skip()
end)
local function inputEditable(line)
local inputLine = getInputLine()
local currentLine = line or errorlog:GetCurrentLine()
return inputLine > -1 and
(currentLine > inputLine or
currentLine == inputLine and positionInLine(inputLine) >= inputBound) and
not (errorlog:LineFromPosition(errorlog:GetSelectionStart()) < getInputLine())
end
errorlog:Connect(wxstc.wxEVT_STC_UPDATEUI,
function () errorlog:SetReadOnly(not inputEditable()) end)
-- only allow copy/move text by dropping to the input line
errorlog:Connect(wxstc.wxEVT_STC_DO_DROP,
function (event)
if not inputEditable(errorlog:LineFromPosition(event:GetPosition())) then
event:SetDragResult(wx.wxDragNone)
end
end)

View File

@@ -233,7 +233,6 @@ end
-----------------------------------
local function saveNotebook(nb)
local cnt = nb:GetPageCount()
@@ -250,7 +249,6 @@ local function saveNotebook(nb)
for i=1,cnt do
local id = nb:GetPageText(i-1)
local pg = nb:GetPage(i-1)
local x,y = pg:GetPosition():GetXY()
addTo(pagesX,x,id)
@@ -303,14 +301,14 @@ local function loadNotebook(nb,str,fnIdConvert)
if (not str) then return end
local cnt = nb:GetPageCount()
local sel = nb:GetSelection()
-- store old pages
local currentpages = {}
for i=1,cnt do
local id = nb:GetPageText(i-1)
local newid = fnIdConvert and fnIdConvert(id) or id
currentpages[newid] = {page = nb:GetPage(i-1), text = id, index = i-1}
currentpages[newid] = currentpages[newid] or {}
table.insert(currentpages[newid], {page = nb:GetPage(i-1), text = id, index = i-1})
end
-- remove them
@@ -318,7 +316,7 @@ local function loadNotebook(nb,str,fnIdConvert)
nb:RemovePage(i-1)
end
-- readd them and perform splits
-- read them and perform splits
local direction
local splits = {
X = wx.wxRIGHT,
@@ -337,13 +335,13 @@ local function loadNotebook(nb,str,fnIdConvert)
local instr = cmd:match("<(%w)>")
if (not instr) then
local id = fnIdConvert and fnIdConvert(cmd) or cmd
local page = currentpages[id]
if (page) then
local pageind = next(currentpages[id] or {})
if (pageind) then
local page = currentpages[id][pageind]
currentpages[id][pageind] = nil
nb:AddPage(page.page, page.text)
currentpages[id] = nil
if (direction) then
nb:Split(t, direction)
end
if (direction) then nb:Split(t, direction) end
finishPage(page)
end
end
@@ -351,18 +349,18 @@ local function loadNotebook(nb,str,fnIdConvert)
end
-- add anything we forgot
for i,page in pairs(currentpages) do
nb:AddPage(page.page, page.text)
finishPage(page)
for _,pagelist in pairs(currentpages) do
for _,page in pairs(pagelist) do
nb:AddPage(page.page, page.text)
finishPage(page)
end
end
if (newsel) then
nb:SetSelection(newsel)
end
end
function SettingsRestoreView()
local listname = "/view"
local path = settings:GetPath()
@@ -394,7 +392,7 @@ function SettingsRestoreView()
local layout = settingsReadSafe(settings,"nbbtmlayout",layoutcur)
if (layout ~= layoutcur) then
loadNotebook(ide.frame.bottomnotebook,layout,
function(name) return name:match("console") or name end)
function(name) return name:match("Output") or name end)
end
settings:SetPath(path)
@@ -424,6 +422,7 @@ function SettingsRestoreEditorSettings()
ide.config.interpreter = settingsReadSafe(settings,"interpreter",ide.config.interpreter)
ProjectSetInterpreter(ide.config.interpreter)
end
function SettingsSaveEditorSettings()
local listname = "/editor"
local path = settings:GetPath()

View File

@@ -8,14 +8,11 @@ local ide = ide
local bottomnotebook = ide.frame.bottomnotebook
local out = bottomnotebook.shellbox
local OUTPUT_MARKER = 3
local remotesend
local OUTPUT_MARKER = 3
local OUTPUT_MARKER_VALUE = 8 -- = 2^OUTPUT_MARKER
local frame = ide.frame
out:SetFont(ide.ofont)
out:StyleSetFont(wxstc.wxSTC_STYLE_DEFAULT, ide.ofont)
out:SetFont(ide.font.oNormal)
out:StyleSetFont(wxstc.wxSTC_STYLE_DEFAULT, ide.font.oNormal)
out:StyleClearAll()
out:SetBufferedDraw(true)
@@ -35,7 +32,7 @@ out:MarkerDefine(BREAKPOINT_MARKER, wxstc.wxSTC_MARK_BACKGROUND, wx.wxBLACK, wx.
out:MarkerDefine(OUTPUT_MARKER, wxstc.wxSTC_MARK_BACKGROUND, wx.wxBLACK, wx.wxColour(240, 240, 240))
out:SetReadOnly(false)
SetupKeywords(out,"lua",nil,ide.config.stylesoutshell,ide.ofont,ide.ofontItalic)
SetupKeywords(out,"lua",nil,ide.config.stylesoutshell,ide.font.oNormal,ide.font.oItalic)
local function getPromptLine()
local totalLines = out:GetLineCount()
@@ -59,11 +56,12 @@ local function positionInLine(line)
return out:GetCurrentPos() - out:PositionFromLine(line)
end
local function caretOnPromptLine(disallowLeftmost)
local function caretOnPromptLine(disallowLeftmost, line)
local promptLine = getPromptLine()
local currentLine = line or out:GetCurrentLine()
local boundary = disallowLeftmost and 0 or -1
return (out:GetCurrentLine() > promptLine
or out:GetCurrentLine() == promptLine and positionInLine(promptLine) > boundary)
return (currentLine > promptLine
or currentLine == promptLine and positionInLine(promptLine) > boundary)
end
local function chomp(line)
@@ -109,6 +107,8 @@ end
local function shellPrint(marker, ...)
local cnt = select('#',...)
if cnt == 0 then return end -- return if nothing to print
local isPrompt = marker and (getPromptLine() > -1)
local text = ''
@@ -155,6 +155,7 @@ end
local function filterTraceError(err, addedret)
local err = err:match("(.-:%d+:.-)\n[^\n]*\n[^\n]*\n[^\n]*src/editor/shellbox.lua:.*in function 'executeShellCode'")
or err
err = err:gsub("stack traceback:.-\n[^\n]+\n?","")
if addedret then err = err:gsub('^%[string "return ', '[string "') end
err = err:match("(.*)\n[^\n]*%(tail call%): %?$") or err
@@ -200,7 +201,7 @@ local function createenv ()
end
local function relativeFilepath(file)
local name,level = luafilepath(3)
local name = luafilepath(3)
return (file and name) and name.."/"..file or file or name
end
@@ -235,29 +236,32 @@ end
local env = createenv()
local function packResults(status, ...) return status, {...} end
local function executeShellCode(tx)
if tx == nil or tx == '' then return end
DisplayShellPrompt('')
local addedret = false
local fn,err
if remotesend then
remotesend(tx)
else
fn,err = loadstring(tx)
-- for statement queries create the return
if err and (err:find("'=' expected near '<eof>'") or
err:find("syntax error near '") or
err:find("unexpected symbol near '")) then
local errmore
fn,errmore = loadstring("return "..tx:gsub("^%s*=%s*",""))
addedret = not errmore
end
-- try to compile as statement
local _, err = loadstring(tx)
local isstatement = not err
if remotesend then remotesend(tx, isstatement); return end
local addedret, forceexpression = true, tx:match("^%s*=%s*")
tx = tx:gsub("^%s*=%s*","")
fn, err = loadstring("return "..tx)
if not forceexpression and err and
(err:find("'<eof>' expected near '") or
err:find("'%(' expected near") or
err:find("unexpected symbol near '")) then
fn, err = loadstring(tx)
addedret = false
end
if fn == nil and err then
DisplayShellErr(err)
DisplayShellErr(filterTraceError(err, addedret))
elseif fn then
setfenv(fn,env)
@@ -269,19 +273,33 @@ local function executeShellCode(tx)
wx.wxFileName.SetCwd(projectDir)
end
local ok, res = xpcall(fn,
local ok, res = packResults(xpcall(fn,
function(err)
DisplayShellErr(filterTraceError(debug.traceback(err), addedret))
end)
end))
-- restore the current dir
if projectDir then wx.wxFileName.SetCwd(cwd) end
if ok and (addedret or res ~= nil) then DisplayShell(res) end
if ok and (addedret or #res > 0) then
if addedret then
local mobdebug = require "mobdebug"
for i,v in pairs(res) do -- stringify each of the returned values
res[i] = mobdebug.line(v, {nocode = true, comment = 1})
end
-- add nil only if we are forced (using =) or if this is not a statement
-- this is needed to print 'nil' when asked for 'foo',
-- and don't print it when asked for 'print(1)'
if #res == 0 and (forceexpression or not isstatement) then
res = {'nil'}
end
end
DisplayShell((table.unpack or unpack)(res))
end
end
end
function ShellSupportRemote(client,uid)
function ShellSupportRemote(client)
remotesend = client
local index = bottomnotebook:GetPageIndex(out)
@@ -359,7 +377,7 @@ out:Connect(wx.wxEVT_KEY_DOWN,
or key == wx.WXK_SHIFT or key == wx.WXK_CONTROL
or key == wx.WXK_ALT then
break
elseif key == wx.WXK_RETURN or key == WXK_NUMPAD_ENTER then
elseif key == wx.WXK_RETURN or key == wx.WXK_NUMPAD_ENTER then
if not caretOnPromptLine()
or out:LineFromPosition(out:GetSelectionStart()) < getPromptLine() then
return
@@ -369,6 +387,7 @@ out:Connect(wx.wxEVT_KEY_DOWN,
if caretOnPromptLine(true) and event:ShiftDown() then break end
local promptText = getPromptText()
if #promptText == 0 then return end -- nothing to execute, exit
if promptText == 'clear' then
out:ClearAll()
displayShellIntro()
@@ -393,4 +412,20 @@ out:Connect(wx.wxEVT_KEY_DOWN,
event:Skip()
end)
local function inputEditable(line)
return caretOnPromptLine(fale, line) and
not (out:LineFromPosition(out:GetSelectionStart()) < getPromptLine())
end
out:Connect(wxstc.wxEVT_STC_UPDATEUI,
function (event) out:SetReadOnly(not inputEditable()) end)
-- only allow copy/move text by dropping to the input line
out:Connect(wxstc.wxEVT_STC_DO_DROP,
function (event)
if not inputEditable(out:LineFromPosition(event:GetPosition())) then
event:SetDragResult(wx.wxDragNone)
end
end)
displayShellIntro()

View File

@@ -224,9 +224,9 @@ function ReApplySpecAndStyles()
local errorlog = ide.frame.bottomnotebook.errorlog
local shellbox = ide.frame.bottomnotebook.shellbox
SetupKeywords(shellbox,"lua",nil,ide.config.stylesoutshell,ide.ofont,ide.ofontItalic)
SetupKeywords(shellbox,"lua",nil,ide.config.stylesoutshell,ide.font.oNormal,ide.font.oItalic)
StylesApplyToEditor(ide.config.stylesoutshell,errorlog,ide.ofont,ide.ofontItalic)
StylesApplyToEditor(ide.config.stylesoutshell,errorlog,ide.font.oNormal,ide.font.oItalic)
end
function LoadConfigStyle()
@@ -234,7 +234,7 @@ function LoadConfigStyle()
"/cfg",
"",
"Lua file (*.lua)|*.lua|All files (*)|*",
wx.wxOPEN + wx.wxFILE_MUST_EXIST)
wx.wxFD_OPEN + wx.wxFD_FILE_MUST_EXIST)
if fileDialog:ShowModal() == wx.wxID_OK then
local cfg = {wxstc = wxstc, path = {}, editor = {}, view ={}, acandtip = {}, outputshell = {}, debugger={},}
local cfgfn,err = loadfile(fileDialog:GetPath())

View File

@@ -1,9 +1,15 @@
-- authors: Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
package.cpath = package.cpath..';bin/?.dll;bin/clibs/?.dll;bin/clibs/?/?.dll;bin/clibs/?/?/?.dll'
package.cpath = package.cpath..';bin/?.so;bin/clibs/?.so;bin/clibs/?/?.so;bin/clibs/?/?/?.so'
package.path = package.path..';lualibs/?.lua;lualibs/?/?.lua;lualibs/?/init.lua;lualibs/?/?/?.lua;lualibs/?/?/init.lua'
-- put bin/ and lualibs/ first to avoid conflicts with included modules
-- that may have other versions present somewhere else in path/cpath
local iswindows = os.getenv('WINDIR') or (os.getenv('OS') or ''):match('[Ww]indows')
package.cpath = (iswindows
and 'bin/?.dll;bin/clibs/?.dll;'
or 'bin/clibs/?.dylib;bin/lib?.dylib;bin/?.so;bin/clibs/?.so;')
.. package.cpath
package.path = 'lualibs/?.lua;lualibs/?/?.lua;lualibs/?/init.lua;lualibs/?/?/?.lua;lualibs/?/?/init.lua;'
.. package.path
require("wx")
require("bit")
@@ -31,6 +37,7 @@ ide = {
verbose = false,
},
outputshell = {},
filetree = {},
styles = StylesGetDefault(),
stylesoutshell = StylesGetDefault(),
@@ -45,6 +52,7 @@ ide = {
activateoutput = false, -- activate output/console on Run/Debug/Compile
unhidewxwindow = false, -- try to unhide a wx window
allowinteractivescript = false, -- allow interaction in the output window
filehistorylength = 20,
projecthistorylength = 15,
savebak = false,
@@ -84,10 +92,13 @@ ide = {
-- modTime = wxDateTime of disk file or nil,
-- isModified = bool is the document modified? }
ignoredFilesList = {},
font = nil,
fontItalic = nil,
ofont = nil,
ofontItalic = nil,
font = {
eNormal = nil,
eItalic = nil,
oNormal = nil,
oItalic = nil,
fNormal = nil,
}
}
---------------
@@ -96,11 +107,18 @@ local filenames = {}
local configs = {}
do
local arg = {...}
local fullPath = arg[1] -- first argument must be the application name
assert(type(fullPath) == "string", "first argument must be application name")
if not wx.wxIsAbsolutePath(fullPath) then
fullPath = wx.wxGetCwd().."/"..fullPath
if wx.__WXMSW__ then fullPath = wx.wxUnix2DosFilename(fullPath) end
end
ide.arg = arg
-- first argument must be the application name
assert(type(arg[1]) == "string","first argument must be application name")
ide.editorFilename = arg[1]
ide.config.path.app = arg[1]:match("([%w_-]+)%.?[^%.]*$")
ide.editorFilename = fullPath
ide.osname = wx.wxPlatformInfo.Get():GetOperatingSystemFamilyName()
ide.config.path.app = fullPath:match("([%w_-%.]+)$"):gsub("%.[^%.]*$","")
assert(ide.config.path.app, "no application path defined")
for index = 2, #arg do
if (arg[index] == "-cfg" and index+1 <= #arg) then
@@ -129,7 +147,7 @@ local function addConfig(filename,showerror,isstring)
ide.config.os = os
ide.config.wxstc = wxstc
setfenv(cfgfn,ide.config)
xpcall(function()cfgfn(assert(_G))end,
xpcall(function()cfgfn(assert(_G or _ENV))end,
function(err)
print("Error while executing configuration file: \n",
debug.traceback(err))
@@ -139,11 +157,6 @@ end
do
addConfig(ide.config.path.app.."/config.lua",true)
addConfig("cfg/user.lua",false)
for i,v in ipairs(configs) do
addConfig(v,true,true)
end
configs = nil
end
----------------------
@@ -172,7 +185,7 @@ local function addToTab(tab,file)
local success,result
success, result = xpcall(
function()return cfgfn(_G)end,
function()return cfgfn(_G or _ENV)end,
function(err)
print(("Error while executing configuration file (%s): \n%s"):
format(file,debug.traceback(err)))
@@ -192,8 +205,7 @@ end
-- load interpreters
local function loadInterpreters()
local files = FileSysGet(".\\interpreters\\*.*",wx.wxFILE)
local files = FileSysGet("./interpreters/*.*",wx.wxFILE)
for i,file in ipairs(files) do
if file:match "%.lua$" and app.loadfilters.interpreters(file) then
addToTab(ide.interpreters,file)
@@ -204,8 +216,7 @@ loadInterpreters()
-- load specs
local function loadSpecs()
local files = FileSysGet(".\\spec\\*.*",wx.wxFILE)
local files = FileSysGet("./spec/*.*",wx.wxFILE)
for i,file in ipairs(files) do
if file:match "%.lua$" and app.loadfilters.specs(file) then
addToTab(ide.specs,file)
@@ -216,6 +227,7 @@ local function loadSpecs()
spec.sep = spec.sep or ""
spec.iscomment = {}
spec.iskeyword0 = {}
spec.isstring = {}
if (spec.lexerstyleconvert) then
if (spec.lexerstyleconvert.comment) then
for i,s in pairs(spec.lexerstyleconvert.comment) do
@@ -227,6 +239,11 @@ local function loadSpecs()
spec.iskeyword0[s] = true
end
end
if (spec.lexerstyleconvert.stringtxt) then
for i,s in pairs(spec.lexerstyleconvert.stringtxt) do
spec.isstring[s] = true
end
end
end
end
end
@@ -234,8 +251,7 @@ loadSpecs()
-- load tools
local function loadTools()
local files = FileSysGet(".\\tools\\*.*",wx.wxFILE)
local files = FileSysGet("./tools/*.*",wx.wxFILE)
for i,file in ipairs(files) do
if file:match "%.lua$" and app.loadfilters.tools(file) then
addToTab(ide.tools,file)
@@ -246,6 +262,14 @@ loadTools()
if app.preinit then app.preinit() end
do
addConfig("cfg/user.lua",false)
for i,v in ipairs(configs) do
addConfig(v,true,true)
end
configs = nil
end
---------------
-- Load App
@@ -303,10 +327,16 @@ end
if app.postinit then app.postinit() end
-- only set menu bar *after* postinit handler as it may include adding
-- app-specific menus (Help/About), which are not recognized by MacOS
-- as special items unless SetMenuBar is done after menus are populated.
ide.frame:SetMenuBar(ide.frame.menuBar)
if ide.osname == 'Macintosh' then -- force refresh to fix the filetree
pcall(function() ide.frame:ShowFullScreen(true) ide.frame:ShowFullScreen(false) end)
end
ide.frame:Show(true)
-- Call wx.wxGetApp():MainLoop() last to start the wxWidgets event loop,
-- otherwise the wxLua program will exit immediately.
-- Does nothing if running from wxLua, wxLuaFreeze, or wxLuaEdit since the
-- MainLoop is already running or will be started by the C++ program.
-- call wx.wxGetApp():MainLoop() last to start the wxWidgets event loop,
-- otherwise the program will exit immediately.
-- Does nothing if the MainLoop is already running.
wx.wxGetApp():MainLoop()

View File

@@ -168,7 +168,7 @@ function FileSysGet(dir,spec)
end
local f = browse:FindFirst(dir,spec)
while #f>0 do
table.insert(content,f)
table.insert(content,(f:gsub("^file:",""))) -- remove file: protocol (wx2.9+)
f = browse:FindNext()
end
return content

View File

@@ -1,6 +1,7 @@
LICENSE
README.md
api/lua/baselib.lua
api/lua/love2d.lua
api/readme.txt
bin/clibs/lfs.dll
bin/clibs/mojoshader.dll
@@ -40,7 +41,8 @@ bin/wxmsw28_qa_vc_custom.dll
bin/wxmsw28_richtext_vc_custom.dll
bin/wxmsw28_stc_vc_custom.dll
bin/wxmsw28_xrc_vc_custom.dll
cfg/user.lua_for_custom_settings.txt
cfg/user-sample.lua
interpreters/love2d.lua
interpreters/luadeb.lua
lualibs/copas/copas.lua
lualibs/coxpcall/coxpcall.lua
@@ -126,12 +128,15 @@ zbstudio/res/16/wxART_FIND.png
zbstudio/res/16/wxART_FIND_AND_REPLACE.png
zbstudio/res/16/wxART_FOLDER.png
zbstudio/res/16/wxART_GO_DIR_UP.png
zbstudio/res/16/wxART_GO_FORWARD-wxART_OTHER_C.png
zbstudio/res/16/wxART_HELP_PAGE.png
zbstudio/res/16/wxART_LIST_VIEW-wxART_OTHER_C.png
zbstudio/res/16/wxART_NEW_DIR.png
zbstudio/res/16/wxART_NORMAL_FILE-wxART_OTHER_C.png
zbstudio/res/16/wxART_NORMAL_FILE.png
zbstudio/res/16/wxART_PASTE.png
zbstudio/res/16/wxART_REDO.png
zbstudio/res/16/wxART_REPORT_VIEW-wxART_OTHER_C.png
zbstudio/res/16/wxART_UNDO.png
zbstudio/res/32.ico
zbstudio/res/zerobrane.png

View File

@@ -1,6 +1,20 @@
local icons = {}
local CreateBitmap = function(id, client, size)
local width = size:GetWidth()
local key = width .. "/" .. id
local fileClient = "zbstudio/res/" .. key .. "-" .. client .. ".png"
local fileKey = "zbstudio/res/" .. key .. ".png"
local file
if wx.wxFileName(fileClient):FileExists() then file = fileClient
elseif wx.wxFileName(fileKey):FileExists() then file = fileKey
else return wx.wxNullBitmap end
local icon = icons[file] or wx.wxBitmap(file)
icons[file] = icon
return icon
end
local ide = ide
local app = {
createbitmap = CreateBitmap,
loadfilters = {
tools = function(file) return false end,
specs = function(file) return true end,
@@ -9,24 +23,12 @@ local app = {
preinit = function ()
local artProvider = wx.wxLuaArtProvider()
local icons = {}
artProvider.CreateBitmap = function(self, id, client, size)
local width = size:GetWidth()
local key = width .. "/" .. id
local fileClient = "zbstudio/res/" .. key .. "-" .. client .. ".png"
local fileKey = "zbstudio/res/" .. key .. ".png"
local file
if wx.wxFileName(fileClient):FileExists() then file = fileClient
elseif wx.wxFileName(fileKey):FileExists() then file = fileKey
else return wx.wxNullBitmap end
local icon = icons[file] or wx.wxBitmap(file)
icons[file] = icon
return icon
end
artProvider.CreateBitmap = function(self, ...) return CreateBitmap(...) end
wx.wxArtProvider.Push(artProvider)
ide.config.interpreter = "luadeb"
ide.config.unhidewxwindow = true
ide.config.unhidewxwindow = true -- allow unhiding of wx windows
ide.config.allowinteractivescript = true -- allow interaction in the output window
-- this needs to be in pre-init to load the styles
dofile("src/editor/markup.lua")
@@ -49,9 +51,7 @@ local app = {
local menuBar = ide.frame.menuBar
local menu = menuBar:GetMenu(menuBar:FindMenu("&Project"))
local itemid = menu:FindItem("Lua &interpreter")
if itemid ~= wx.wxNOT_FOUND then menu:Destroy(itemid) end
itemid = menu:FindItem("Project directory")
local itemid = menu:FindItem("Project Directory")
if itemid ~= wx.wxNOT_FOUND then menu:Destroy(itemid) end
menu = menuBar:GetMenu(menuBar:FindMenu("&View"))
@@ -60,14 +60,20 @@ local app = {
menuBar:Check(ID_CLEAROUTPUT, true)
-- load welcome.lua from myprograms/ if exists
local fn = wx.wxFileName("myprograms/welcome.lua")
if fn:FileExists() and
(not ide.config.path.projectdir
or string.len(ide.config.path.projectdir) == 0) then
fn:Normalize() -- make absolute path
LoadFile(fn:GetFullPath(),nil,true)
ProjectUpdateProjectDir(fn:GetPath(wx.wxPATH_GET_VOLUME))
-- load myprograms/welcome.lua if exists and no projectdir
local projectdir = ide.config.path.projectdir
if (not projectdir or string.len(projectdir) == 0
or not wx.wxFileName(projectdir):DirExists()) then
local home = wx.wxGetHomeDir():gsub("[\\/]$","")
for _,dir in pairs({home, home.."/Desktop", ""}) do
local fn = wx.wxFileName("myprograms/welcome.lua")
-- normalize to absolute path
if fn:Normalize(wx.wxPATH_NORM_ALL, dir) and fn:FileExists() then
LoadFile(fn:GetFullPath(),nil,true)
ProjectUpdateProjectDir(fn:GetPath(wx.wxPATH_GET_VOLUME))
break
end
end
end
end,
@@ -79,7 +85,6 @@ local app = {
settingsapp = "ZeroBraneStudio",
settingsvendor = "ZeroBraneLLC",
},
}
return app

View File

@@ -1,4 +1,3 @@
editor.fontname = "Courier New"
editor.caretline = true
editor.showfncall = true
editor.autotabs = false
@@ -6,12 +5,15 @@ editor.usetabs = false
editor.tabwidth = 2
editor.usewrap = true
local G = ... -- this now points to the global environment
if G.ide.osname == 'Macintosh' then filetree.fontsize = 11 end
filehistorylength = 20
singleinstance = true
singleinstanceport = 0xe493
acandtip.shorttip = true
acandtip.shorttip = false
acandtip.nodynwords = true
activateoutput = true

View File

@@ -55,7 +55,7 @@ local function DisplayAbout(event)
</body>
</html>]]
local dlg = wx.wxDialog(frame, wx.wxID_ANY, "About ZeroBane Studio")
local dlg = wx.wxDialog(frame, wx.wxID_ANY, "About ZeroBrane Studio")
local html = wx.wxLuaHtmlWindow(dlg, wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxSize(440, 270),
wx.wxHW_SCROLLBAR_NEVER)
@@ -73,11 +73,10 @@ local function DisplayAbout(event)
topsizer:Add(html, 1, wx.wxALL, 10)
topsizer:Add(line, 0, wx.wxEXPAND + wx.wxLEFT + wx.wxRIGHT, 10)
topsizer:Add(button, 0, wx.wxALL + wx.wxALIGN_RIGHT, 10)
topsizer:Fit(dlg)
dlg:SetAutoLayout(true)
dlg:SetSizer(topsizer)
topsizer:Fit(dlg)
dlg:ShowModal()
dlg:Destroy()
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 783 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 B