Compare commits

..

105 Commits
0.70 ... 0.80

Author SHA1 Message Date
Paul Kulchenko
b0ce69da6a Updated CHANGELOG for 0.80. 2014-08-31 21:43:23 -07:00
Paul Kulchenko
da7edc4580 Upgraded MobDebug (0.60). 2014-08-30 21:17:26 -07:00
Paul Kulchenko
31b7e4d788 Fixed watch refresh of tables without array part. 2014-08-30 20:39:35 -07:00
Paul Kulchenko
c940b56459 Updated README with filename:<line> and filename:p<pos> syntax. 2014-08-30 14:33:16 -07:00
Paul Kulchenko
dcc28d9ce2 Added default values for hotexit and saveallonrun settings. 2014-08-29 22:40:11 -07:00
Paul Kulchenko
334a071219 Added debugger GetHostName and GetPortNumber methods (#166). 2014-08-25 12:25:23 -07:00
Paul Kulchenko
48ba4e26b8 Fixed disabling auto-recovery on app switching. 2014-08-25 12:24:41 -07:00
Paul Kulchenko
8da70c6e75 Avoided system lib conflict when debugging by using bundled libs (fixes #355). 2014-08-24 22:28:34 -07:00
Paul Kulchenko
61198caae5 Updated copas library to support non-blocking requests using socket.http.
See for details: http://lua-users.org/lists/lua-l/2014-07/msg00516.html
2014-08-22 23:27:40 -07:00
Paul Kulchenko
4e8b9d41ba Merge branch 'watch-as-tree-with-edit' 2014-08-21 14:44:25 -07:00
Paul Kulchenko
86af637781 Enabled editing of values in Watch window. 2014-08-21 14:34:11 -07:00
Paul Kulchenko
08825ba79a Added a check for a local shortcut (F2/Del) being enabled before triggering. 2014-08-21 14:15:02 -07:00
Paul Kulchenko
f2b3161474 Updated Stack and Watch views to better stringify keys. 2014-08-21 14:15:01 -07:00
Paul Kulchenko
22f2f68a3a Disabled editing on non-root watch elements. 2014-08-21 14:15:00 -07:00
Paul Kulchenko
e3f666570a Added refresh of expanded Watch values. 2014-08-21 14:14:59 -07:00
Paul Kulchenko
dc757d48e5 Added support for expanding table elements in Watch window. 2014-08-21 14:14:58 -07:00
Paul Kulchenko
da6b7db0e6 Updated watch menu to handle item under mouse cursor. 2014-08-21 14:14:57 -07:00
Paul Kulchenko
ec218e1424 Removed check for multiple references in stack values.
All references will be shown; circular references can be expanded as well.
2014-08-21 14:14:55 -07:00
Paul Kulchenko
c178ec9ab4 Refactored stack processing to use methods to handle expandable table values. 2014-08-21 14:14:54 -07:00
Paul Kulchenko
9f87a780a7 Simplified logic for watch processing. 2014-08-21 14:14:53 -07:00
Paul Kulchenko
40809b6396 Switched from using TreeItemData to Lua tables for watch expressions.
Storing changes to wxLuaTreeItemData doesn't work from coroutines for some
reason, so using Lua tables instead.
2014-08-21 14:14:52 -07:00
Paul Kulchenko
6f04ef8921 Added package AddWatch method (#166). 2014-08-21 14:14:51 -07:00
Paul Kulchenko
a3235b23bb Updated constants for image lists. 2014-08-21 14:13:57 -07:00
Paul Kulchenko
b3fdde036e Fixed find-in-files error when used with editor not in focus (fixes #354). 2014-08-20 00:45:17 -07:00
Paul Kulchenko
1e86c3c2d6 Switched to using tree control for watches.
Replaced AssignImageList with SetImageList as there are multiple controls
using the same Image list (and it can't be shared if Assign is used).
2014-08-14 21:34:14 -07:00
Paul Kulchenko
57a89f0c45 Updated FindMenuItem method to search in the main and specified menus (#166). 2014-08-14 12:30:21 -07:00
Paul Kulchenko
710c49850c Added toolbar.iconsize to configure toolbar icon size. 2014-08-12 14:36:52 -07:00
Paul Kulchenko
94662bbd4c Added run-as-scratchpad toolbar icon (hidden by default). 2014-08-12 12:05:17 -07:00
Paul Kulchenko
fc1f9375ed Added run toolbar icon (hidden by default). 2014-08-12 12:05:07 -07:00
Paul Kulchenko
7db6b1ad07 Added find-in-files toolbar icon (hidden by default). 2014-08-12 12:04:54 -07:00
Paul Kulchenko
d70d6a0bd6 Added support for disabling individual icons in the toolbar. 2014-08-11 17:30:56 -07:00
Paul Kulchenko
b130e68b51 Added replacing all selected instances using a dialog (closes #342). 2014-08-10 22:24:15 -07:00
Paul Kulchenko
fdbb835199 Added highlighting all instances of selected text (closes #344).
If the currently selected fragment doesn't look like a variable,
then all instances of the fragment are selected in the text when
`Rename All Instances` is used.
2014-08-10 21:41:34 -07:00
Paul Kulchenko
3cc2d861db Improved config handling when editor configuration is removed/empty. 2014-08-09 16:16:48 -07:00
Paul Kulchenko
e5ca96879a Updated ide.config to access wx, wxstc, and os through metatable. 2014-08-09 16:13:22 -07:00
Paul Kulchenko
80248d2a77 Added filetree.mousemove option to disable drag-n-drop (closes #351). 2014-08-09 16:12:10 -07:00
Paul Kulchenko
400de47586 Added suspended to Output panel title when debugger is stopped (closes #350). 2014-08-08 11:14:30 -07:00
Paul Kulchenko
cfdbbff3c7 Added a warning when remote console can't evaluate an expression (#350). 2014-08-08 11:13:00 -07:00
Paul Kulchenko
5235cc001b Added handling of osname to package dependencies (#166). 2014-08-07 18:08:18 -07:00
Paul Kulchenko
12fd9611f7 Added onIdle event (#166). 2014-08-07 15:22:33 -07:00
Paul Kulchenko
be323d555d Added tree:FindItem method (#166). 2014-08-07 14:47:25 -07:00
Paul Kulchenko
11facf0acb Added package Yield method (#166). 2014-08-06 22:01:59 -07:00
Paul Kulchenko
4b13017620 Fixed package GetStack method to return proper control (#166). 2014-08-06 22:01:28 -07:00
Paul Kulchenko
7ddaaa20e6 Updated recent projects/files handling to allow menus to be removed. 2014-08-06 22:00:11 -07:00
Paul Kulchenko
b659dfaf79 Added package RemoveMenuItem method (#166). 2014-08-06 17:24:04 -07:00
Paul Kulchenko
20c73a9e92 Updated package FindMenuItem method (#166). 2014-08-06 17:23:38 -07:00
Paul Kulchenko
dfca13b96d Fixed Watch window background color on some Mint Linux systems. 2014-08-06 14:53:53 -07:00
Paul Kulchenko
3c25189fdb Fixed debugging error when debugger.runonstart is specified (fixes #348, #341). 2014-08-04 16:25:42 -07:00
Paul Kulchenko
a1c67447b5 Added ability to set location of ini file from config. 2014-08-04 14:32:10 -07:00
Paul Kulchenko
6710758962 Added ability to load bitmap as toolbar icon. 2014-08-03 22:24:26 -07:00
Paul Kulchenko
e761a5f7ef Renamed all image files to remove cruft from their names. 2014-08-03 14:17:04 -07:00
Paul Kulchenko
c8f84e4694 Added ability to customize toolbar. 2014-08-03 13:47:19 -07:00
Paul Kulchenko
e40215a4d1 Added saving (one-line) layout for editor tabs. 2014-08-02 16:57:31 -07:00
Paul Kulchenko
46d6ab8f9e Improved autotabs logic when the file starts with indentation. 2014-08-02 10:23:38 -07:00
Paul Kulchenko
377fbfab39 Updated autotabs to respect usetabs when no indentation is present. 2014-08-01 21:58:20 -07:00
Paul Kulchenko
4c4259f5ca Added centering of the screen after re-indenting and sorting (#337). 2014-08-01 14:55:23 -07:00
Paul Kulchenko
0e8b29936e Updated copy/cut to capture one instance when all are the same (closes #345).
This logic is also applied to `Ctrl-Ins` and `Shift-Del` combinations.
2014-07-31 15:35:21 -07:00
Paul Kulchenko
54f16def09 Fixed keybinding for Ctrl-<punctuation> working on Linux (fixes #346). 2014-07-31 13:51:40 -07:00
Paul Kulchenko
e8d7235cfb Improved auto-complete logic that tracks variable assignments (fixes #343). 2014-07-31 11:54:00 -07:00
Paul Kulchenko
bac1cbc028 Refactored file name generation for compilation and static analysis. 2014-07-30 17:54:55 -07:00
Paul Kulchenko
94ceb8d9df Fixed localization based on static analysis. 2014-07-30 17:54:26 -07:00
Paul Kulchenko
6f1a0c0316 Merge branch 'map-remote-runonstart' 2014-07-30 17:14:48 -07:00
Li Jia
a225d7e7c7 Add local to variable 'activated' in function mapRemotePath 2014-07-30 13:10:41 +08:00
Li Jia
e3f7719ca7 Fix remote path map when 'runonstart' option is set. 2014-07-30 12:53:58 +08:00
Paul Kulchenko
809e46eaf7 Increased default project history length to 20. 2014-07-29 12:59:31 -07:00
Paul Kulchenko
5450ad8311 Fixed error reporting during Analyze (fixes #340). 2014-07-29 12:58:04 -07:00
Paul Kulchenko
68866eb2cb Updated default marker colors for lighter border (#305). 2014-07-26 16:27:09 -07:00
Paul Kulchenko
9ff569e8ce Added centering of the screen after 'go to definition' and back (#337). 2014-07-25 20:19:26 -07:00
Paul Kulchenko
9375235fc6 Fixed using image lists for stack/filetree to keep them in memory. 2014-07-23 14:36:04 -07:00
Paul Kulchenko
a2cd63afa6 Added centering of the screen after selection from the function list (#337). 2014-07-23 09:39:48 -07:00
Paul Kulchenko
6052a86f0a Improved cursor positioning after re-indenting or sorting. 2014-07-22 12:06:19 -07:00
Paul Kulchenko
75357d7f41 Fixed indentation when Enter is hit at the middle of a line. 2014-07-21 21:46:13 -07:00
Paul Kulchenko
c493f62466 Added package onEditorUpdateUI event (#166). 2014-07-20 10:05:58 -07:00
Paul Kulchenko
89ef72aab3 Removed erroneous message about failure to open '-psn...' file on OSX.
This fixes a side effect of an earlier commit (8678404b).
2014-07-17 16:48:41 -07:00
Paul Kulchenko
343423898e Added package AddPanel method (#166). 2014-07-17 15:13:40 -07:00
Paul Kulchenko
cf02a3ea55 Added package GetUIManager method (#166). 2014-07-17 13:52:01 -07:00
Paul Kulchenko
811f2a7021 Added editor SetupKeywords method (#166). 2014-07-17 11:07:46 -07:00
Paul Kulchenko
fe92bf89e5 Added document GetFileExit method (#166). 2014-07-17 09:22:26 -07:00
Paul Kulchenko
c9ac9ca23f Added onEditorPainted event (#166). 2014-07-17 09:21:45 -07:00
Paul Kulchenko
300c6b61c6 Added support for name:<line> and name:p<pos> on the command line. 2014-07-16 10:17:38 -07:00
Paul Kulchenko
8678404b84 Added error reporting on failure to load file from the command line. 2014-07-15 15:45:03 -07:00
Paul Kulchenko
54b29472cc Disabled smart indentation for multi-line comments and strings (#324). 2014-07-13 23:16:55 -07:00
Paul Kulchenko
f0a3305753 Disabled re-indentation of multi-line comments/strings (#324). 2014-07-11 15:51:57 -07:00
Paul Kulchenko
d9ce3d0538 Fixed formatting of until statements (fixes #335). 2014-07-09 09:50:22 -07:00
Paul Kulchenko
b457ccbccd Fixed formatting of strings including comments '--' (#335). 2014-07-09 09:49:38 -07:00
Paul Kulchenko
1fb61028b1 Added metalua components to MANIFEST (missing in packaging on OSX). 2014-07-08 19:22:39 -07:00
Paul Kulchenko
98fc8e05bc Disabled Opt+Shift+Left/Right shortcut as it conflicts with block selection. 2014-07-05 22:55:34 -07:00
Paul Kulchenko
03989f3fd8 Added saving auto-recovery record on switching from the application. 2014-07-03 16:50:50 -07:00
Paul Kulchenko
b41eb364bb Added hotexit option to exit without forcing to save files. 2014-07-02 22:12:13 -07:00
Paul Kulchenko
27708b4dd2 Enabled editor.autoreload by default. 2014-07-01 21:25:30 -07:00
Paul Kulchenko
0d2ab45c6b Added setting of margin properties to support their reordering. 2014-06-30 22:25:38 -07:00
Paul Kulchenko
0cfede0e7a Added error reporting on failure to delete directory from project tree. 2014-06-30 14:55:32 -07:00
Paul Kulchenko
2a404541e5 Allowed double-click selection in the Output window (#313).
Double-click selection has been disabled in the Output window, because
double-click is reserved for `file:line` navigation. It's now enabled
on those lines that don't match `file:line` navigation pattern.
2014-06-28 15:43:30 -07:00
Paul Kulchenko
657526eab4 Added search in Console and Output windows (closes #313). 2014-06-28 15:28:33 -07:00
Paul Kulchenko
eb16a80515 Fixed restoring proper file names for unsaved tabs during auto-recovery. 2014-06-27 22:21:00 -07:00
Paul Kulchenko
e283bcb65d Updated auto-recovery logic to skip missing files (fixes #323). 2014-06-27 17:06:59 -07:00
Paul Kulchenko
a1459ba494 Added check for shortcut in conflict being enabled before activating (#233). 2014-06-27 15:52:15 -07:00
Paul Kulchenko
042998dd71 Added workaround for missing GetChildren call in some wxlua configurations. 2014-06-24 20:22:58 -07:00
Paul Kulchenko
717d46a332 Added unfolding modified lines to avoid leaving hidden lines in the editor. 2014-06-23 20:15:03 -07:00
Paul Kulchenko
b6fd404788 Fixed deleting 'dynamic words' when multiple lines are removed. 2014-06-23 19:59:29 -07:00
Paul Kulchenko
02a43a63a6 Fixed love.update description (#247). 2014-06-22 12:41:54 -07:00
Paul Kulchenko
719b76ea80 Fixed indentation of strings starting from endSomething (#324). 2014-06-21 23:48:47 -07:00
Paul Kulchenko
eae8540708 Fixed use of '%' in replacement for Lua5.2 compatibility (#153, #156, #143).
Removed a fragment of auto-complete processing that is no longer needed
as its logic is covered by another part of the code.
2014-06-20 10:06:24 -07:00
Paul Kulchenko
539a74aa7a Fixed warnings from static analysis. 2014-06-19 16:25:10 -07:00
Paul Kulchenko
4b0bcaa20e Improved compatibility with Lua5.2 to run the IDE. 2014-06-19 16:16:18 -07:00
88 changed files with 1100 additions and 497 deletions

View File

@@ -1,5 +1,123 @@
# ZeroBrane Studio Changelog
## v0.80 (Aug 31 2014)
### Highlights
- Added support for expanding table elements in Watch window.
- Added editing of values in Watch window.
- Added highlighting all instances of selected text.
- Added replacing all selected instances using a dialog.
- Added saving (one-line) layout for editor tabs.
- Added support for `filename:<line>` and `filename:p<pos>` on the command line.
- Added search in Console and Output windows.
- Improved compatibility with Lua 5.2 to run the IDE.
### Special thanks
- To [Li Jia](https://github.com/tiwb) for fixing remote path map when 'runonstart' option is set.
### Improvements
- Added default values for `hotexit` and `saveallonrun` settings.
- Added debugger `GetHostName` and `GetPortNumber` methods (#166).
- Added a check for a local shortcut (F2/Del) being enabled before triggering.
- Added refresh of expanded Watch values.
- Added support for expanding table elements in Watch window.
- Added package `AddWatch` method (#166).
- Added `toolbar.iconsize` to configure toolbar icon size.
- Added `run-as-scratchpad` toolbar icon (hidden by default).
- Added `run` toolbar icon (hidden by default).
- Added `find-in-files` toolbar icon (hidden by default).
- Added support for disabling individual icons in the toolbar.
- Added replacing all selected instances using a dialog (closes #342).
- Added highlighting all instances of selected text (closes #344).
- Added `filetree.mousemove` option to disable drag-n-drop (closes #351).
- Added `suspended` to Output panel title when debugger is stopped (closes #350).
- Added a warning when remote console can't evaluate an expression (#350).
- Added handling of `osname` to package dependencies (#166).
- Added `onIdle` event (#166).
- Added `tree:FindItem` method (#166).
- Added package `Yield` method (#166).
- Added ability to set location of `ini` file from config.
- Added ability to load bitmap as toolbar icon.
- Added package `RemoveMenuItem` method (#166).
- Added ability to customize toolbar.
- Added saving (one-line) layout for editor tabs.
- Added centering of the screen after re-indenting and sorting (#337).
- Added local to variable 'activated' in function mapRemotePath
- Added centering of the screen after 'go to definition' and back (#337).
- Added centering of the screen after selection from the function list (#337).
- Added package `onEditorUpdateUI` event (#166).
- Added package `AddPanel` method (#166).
- Added package `GetUIManager` method (#166).
- Added editor `SetupKeywords` method (#166).
- Added document `GetFileExit` method (#166).
- Added `onEditorPainted` event (#166).
- Added support for `name:<line>` and `name:p<pos>` on the command line.
- Added error reporting on failure to load file from the command line.
- Added metalua components to MANIFEST (missing in packaging on OSX).
- Added saving auto-recovery record on switching from the application.
- Added `hotexit` option to exit without forcing to save files.
- Added setting of margin properties to support their reordering.
- Added error reporting on failure to delete directory from project tree.
- Added check for shortcut in conflict being enabled before activating (#233).
- Added workaround for missing `GetChildren` call in some wxlua configurations.
- Added unfolding modified lines to avoid leaving hidden lines in the editor.
- Added search in Console and Output windows (closes #313).
- Allowed double-click selection in the Output window (#313).
- Avoided system lib conflict when debugging by using bundled libs (fixes #355).
- Disabled editing on non-root watch elements.
- Disabled smart indentation for multi-line comments and strings (#324).
- Disabled re-indentation of multi-line comments/strings (#324).
- Disabled `Opt+Shift+Left/Right` shortcut as it conflicts with block selection.
- Enabled editing of values in Watch window.
- Enabled `editor.autoreload` by default.
- Improved config handling when `editor` configuration is removed/empty.
- Improved `autotabs` logic when the file starts with indentation.
- Improved auto-complete logic that tracks variable assignments (fixes #343).
- Improved cursor positioning after re-indenting or sorting.
- Improved compatibility with Lua5.2 to run the IDE.
- Increased default project history length to 20.
- Removed check for multiple references in stack values.
- Refactored stack processing to use methods to handle expandable table values.
- Refactored file name generation for compilation and static analysis.
- Removed erroneous message about failure to open '-psn...' file on OSX.
- Renamed all image files to remove cruft from their names.
- Simplified logic for watch processing.
- Switched from using TreeItemData to Lua tables for watch expressions.
- Switched to using tree control for watches.
- Updated copas library to support non-blocking requests using socket.http.
- Updated Stack and Watch views to better stringify keys.
- Updated watch menu to handle item under mouse cursor.
- Updated constants for image lists.
- Updated `FindMenuItem` method to search in the main and specified menus (#166).
- Updated `ide.config` to access wx, wxstc, and os through metatable.
- Updated recent projects/files handling to allow menus to be removed.
- Updated package `FindMenuItem` method (#166).
- Updated `autotabs` to respect `usetabs` when no indentation is present.
- Updated copy/cut to capture one instance when all are the same (closes #345).
- Updated default marker colors for lighter border (#305).
- Updated auto-recovery logic to skip missing files (fixes #323).
### Fixes
- Fixed disabling auto-recovery on app switching.
- Fixed find-in-files error when used with editor not in focus (fixes #354).
- Fixed package `GetStack` method to return proper control (#166).
- Fixed Watch window background color on some Mint Linux systems.
- Fixed debugging error when `debugger.runonstart` is specified (fixes #348, #341).
- Fixed keybinding for `Ctrl-<punctuation>` working on Linux (fixes #346).
- Fixed localization based on static analysis.
- Fixed remote path map when 'runonstart' option is set.
- Fixed error reporting during Analyze (fixes #340).
- Fixed using image lists for stack/filetree to keep them in memory.
- Fixed indentation when Enter is hit at the middle of a line.
- Fixed formatting of `until` statements (fixes #335).
- Fixed formatting of strings including comments '--' (#335).
- Fixed restoring proper file names for unsaved tabs during auto-recovery.
- Fixed deleting 'dynamic words' when multiple lines are removed.
- Fixed `love.update` description (#247).
- Fixed indentation of strings starting from `endSomething` (#324).
- Fixed use of '%' in replacement for Lua5.2 compatibility (#153, #156, #143).
- Fixed warnings from static analysis.
## v0.70 (Jun 18 2014)
### Highlights

View File

@@ -74,6 +74,8 @@ Loading custom configuration:
e.g.: zbstudio -cfg cfg/estrela.lua
```
If you are loading a file, you can also request the cursor to be set on a particular line or at a particular position by using `filename:<line>` and `filename:p<pos>` syntax (0.71+).
## Author
### ZeroBrane Studio and MobDebug

View File

@@ -5370,7 +5370,7 @@ local love = {
},
update = {
args = "(dt: number)",
description = "Callback function triggered when a key is pressed.",
description = "Callback function used to update the state of the game every frame.",
returns = "()",
type = "function"
},

View File

@@ -53,14 +53,19 @@ return {
-- modify CPATH to work with other Lua versions
local clibs = ('/clibs%s/'):format(version and tostring(version):gsub('%.','') or '')
local _, cpath = wx.wxGetEnv("LUA_CPATH")
if rundebug and cpath then
wx.wxSetEnv("LUA_CPATH", ide.osclibs..';'..cpath)
end
if version and cpath and not cpath:find(clibs, 1, true) then
wx.wxSetEnv("LUA_CPATH", cpath:gsub('/clibs/', clibs)) end
local _, cpath = wx.wxGetEnv("LUA_CPATH")
wx.wxSetEnv("LUA_CPATH", cpath:gsub('/clibs/', clibs))
end
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
local pid = CommandLineRun(cmd,self:fworkdir(wfilename),true,false,nil,nil,
function() if rundebug then wx.wxRemoveFile(filepath) end end)
if version and cpath then wx.wxSetEnv("LUA_CPATH", cpath) end
if (rundebug or version) and cpath then wx.wxSetEnv("LUA_CPATH", cpath) end
return pid
end,
fprojdir = function(self,wfilename)

View File

@@ -222,11 +222,11 @@ end
-- same as above but with special treatment when reading chunks,
-- unblocks on any data received.
function copas.receivePartial(client, pattern)
local s, err, part
function copas.receivePartial(client, pattern, part)
local s, err
pattern = pattern or "*l"
repeat
s, err, part = client:receive(pattern)
s, err, part = client:receive(pattern, part)
if s or ( (type(pattern)=="number") and part~="" and part ~=nil ) or
err ~= "timeout" then
_reading_log[client] = nil
@@ -310,11 +310,11 @@ local _skt_mt = {__index = {
return copas.send (self.socket, data, from, to)
end,
receive = function (self, pattern)
receive = function (self, pattern, prefix)
if (self.timeout==0) then
return copas.receivePartial(self.socket, pattern)
return copas.receivePartial(self.socket, pattern, prefix)
end
return copas.receive(self.socket, pattern)
return copas.receive(self.socket, pattern, prefix)
end,
flush = function (self)
@@ -323,8 +323,12 @@ local _skt_mt = {__index = {
settimeout = function (self,time)
self.timeout=time
return
return true
end,
skip = function(self, ...) return self.socket:skip(...) end,
close = function(self, ...) return self.socket:close(...) end,
}}
-- wraps a UDP socket, copy of TCP one adapted for UDP.
@@ -352,7 +356,7 @@ local _skt_mt_udp = {__index = {
settimeout = function (self,time)
self.timeout=time
return
return true
end,
}}

View File

@@ -1,5 +1,5 @@
--
-- MobDebug 0.564
-- MobDebug 0.60
-- Copyright 2011-14 Paul Kulchenko
-- Based on RemDebug 1.0 Copyright Kepler Project 2005
--
@@ -18,7 +18,7 @@ end)("os")
local mobdebug = {
_NAME = "mobdebug",
_VERSION = 0.564,
_VERSION = 0.60,
_COPYRIGHT = "Paul Kulchenko",
_DESCRIPTION = "Mobile Remote Debugger for the Lua programming language",
port = os and os.getenv and tonumber((os.getenv("MOBDEBUG_PORT"))) or 8172,

View File

@@ -30,6 +30,8 @@ local events = {
onEditorKeyDown = function(self, editor, event) end, -- return false
onEditorCharAdded = function(self, editor, event) end, -- return false
onEditorUserlistSelection = function(self, editor, event) end, -- return false
onEditorUpdateUI = function(self, editor, event) end, -- return false
onEditorPainted = function(self, editor, event) end, -- return false
onFiletreeActivate = function(self, tree, event, item) end, -- return false
onFiletreeLDown = function(self, tree, event, item) end,
onFiletreeRDown = function(self, tree, event, item) end,
@@ -41,6 +43,7 @@ local events = {
onProjectClose = function(self, project) end,
onInterpreterLoad = function(self, interpreter) end,
onInterpreterClose = function(self, interpreter) end,
onIdle = function(self, event) end,
onIdleOnce = function(self, event) end,
onAppFocusLost = function(self, app) end,
onAppFocusSet = function(self, app) end,

View File

@@ -4,10 +4,10 @@
local funcdef = "([A-Za-z_][A-Za-z0-9_%.%:]*)%s*"
local funccall = "([A-Za-z_][A-Za-z0-9_]*)%s*"
local decindent = {
['else'] = true, ['elseif'] = true, ['end'] = true}
['else'] = true, ['elseif'] = true, ['until'] = true, ['end'] = true}
local incindent = {
['else'] = true, ['elseif'] = true, ['for'] = true, ['do'] = true,
['if'] = true, ['repeat'] = true, ['until'] = true, ['while'] = true}
['if'] = true, ['repeat'] = true, ['while'] = true}
local function isfndef(str)
local l
local s,e,cap,par = string.find(str, "function%s+" .. funcdef .. "(%(.-%))")
@@ -43,21 +43,25 @@ return {
-- this handles three different cases:
local term = (str:match("^%s*(%w+)%s*$")
or str:match("^%s*(elseif)[%s%(]")
or str:match("^%s*(until)[%s%(]")
or str:match("^%s*(else)%f[%W]")
)
-- (1) 'end', 'elseif', 'else'
-- (1) 'end', 'elseif', 'else', 'until'
local match = term and decindent[term]
-- (2) 'end)', 'end}', 'end,', and 'end;'
if not term then term, match = str:match("^%s*(end)%s*([%)%}]*)%s*[,;]?") end
-- endFoo could be captured as well; filter it out
if term and str:match("^%s*(end)%w") then term = nil end
-- (3) '},', '};', '),' and ');'
if not term then match = str:match("^%s*[%)%}]+%s*[,;]?%s*$") end
return match and 1 or 0, match and term and 1 or 0
end,
isincindent = function(str)
str = (str:gsub('%-%-%[=*%[.*%]=*%]',''):gsub('%-%-.*','')
str = (str:gsub('%-%-%[=*%[.*%]=*%]','')
:gsub("'.-\\'","'"):gsub("'.-'","")
:gsub('".-\\"','"'):gsub('".-"','')
:gsub('%-%-.*','') -- strip comments after strings are processed
:gsub("%b()","()") -- remove all function calls
)
local term = str:match("^%s*(%w+)%W*")
@@ -128,16 +132,16 @@ return {
if (not iscomment[s]) then
local tx = editor:GetLine(line)
local leftscope
local sstart, send
for i,v in ipairs(scopestart) do
if (tx:match("^%s*"..v)) then
leftscope = true
end
for _, v in ipairs(scopestart) do
if (tx:match("^%s*"..v.."%f[^%w]")) then sstart = true end
end
if (leftscope) then
break
for _, v in ipairs(scopeend) do
if (tx:match("%f[%w]"..v.."%s*$")) then send = true end
end
-- if the scope starts, but doesn't end on one line, stop searching
if sstart and not send then break end
end
line = line -1
end

View File

@@ -254,11 +254,6 @@ local function resolveAssign(editor,tx)
c = c..w..s
end
end
-- abort if the same or recursive value is returned; no need to continue.
-- this can happen after typing "smth = smth:new(); smth:" or
-- "line = line:gsub(...); line:" as the current algorithm attempts to
-- replace "line" with the value that also includes "line"
if change and c:find("^"..(tx:gsub(anysep,anysep))) then break end
tx = c
end
else

View File

@@ -16,7 +16,7 @@ local CURRENT_LINE_MARKER_VALUE = 2^CURRENT_LINE_MARKER
function NewFile(filename)
filename = filename or ide.config.default.fullname
local editor = CreateEditor()
SetupKeywords(editor, GetFileExt(filename))
editor:SetupKeywords(GetFileExt(filename))
local doc = AddEditor(editor, filename)
if doc then
PackageEventHandle("onEditorNew", editor)
@@ -71,7 +71,7 @@ function LoadFile(filePath, editor, file_must_exist, skipselection)
editor = editor or findUnusedEditor() or CreateEditor()
editor:Freeze()
SetupKeywords(editor, GetFileExt(filePath))
editor:SetupKeywords(GetFileExt(filePath))
editor:MarkerDeleteAll(-1)
-- remove BOM from UTF-8 encoded files; store BOM to add back when saving
@@ -109,13 +109,16 @@ function LoadFile(filePath, editor, file_must_exist, skipselection)
editor:Colourise(0, -1)
editor:Thaw()
local edcfg = ide.config.editor
if current then editor:GotoPos(current) end
if (file_text and ide.config.editor.autotabs) then
local found = string.find(file_text,"\t") ~= nil
editor:SetUseTabs(found)
if (file_text and edcfg.autotabs) then
-- use tabs if they are already used
-- or if "usetabs" is set and no space indentation is used in a file
editor:SetUseTabs(string.find(file_text, "\t") ~= nil
or edcfg.usetabs and (file_text:find("%f[^\r\n] ") or file_text:find("^ ")) == nil)
end
if (file_text and ide.config.editor.checkeol) then
if (file_text and edcfg.checkeol) then
-- Auto-detect CRLF/LF line-endings
local foundcrlf = string.find(file_text,"\r\n") ~= nil
local foundlf = (string.find(file_text,"[^\r]\n") ~= nil)
@@ -195,6 +198,21 @@ function ReLoadFile(filePath, editor, ...)
return editor
end
function ActivateFile(filename)
local name, suffix, value = filename:match('(.+):([lLpP]?)(%d+)$')
if name and not wx.wxFileExists(filename) and wx.wxFileExists(name) then
filename = name
end
local opened = LoadFile(filename, nil, true)
if opened and value then
if suffix:upper() == 'P' then opened:GotoPosDelayed(tonumber(value))
else opened:GotoPosDelayed(opened:PositionFromLine(value-1))
end
end
return opened
end
local function getExtsString()
local knownexts = ""
for _,spec in pairs(ide.specs) do
@@ -323,7 +341,7 @@ function SaveFileAs(editor)
if ext ~= GetFileExt(filePath) then
-- new extension, so setup new keywords and re-apply indicators
editor:ClearDocumentStyle() -- remove styles from the document
SetupKeywords(editor, GetFileExt(filePath))
editor:SetupKeywords(GetFileExt(filePath))
IndicateAll(editor)
MarkupStyle(editor)
end
@@ -539,19 +557,6 @@ function FoldSome()
editor:EnsureCaretVisible()
end
function EnsureRangeVisible(posStart, posEnd)
local editor = GetEditor()
if posStart > posEnd then
posStart, posEnd = posEnd, posStart
end
local lineStart = editor:LineFromPosition(posStart)
local lineEnd = editor:LineFromPosition(posEnd)
for line = lineStart, lineEnd do
editor:EnsureVisibleEnforcePolicy(line)
end
end
function SetAllEditorsReadOnly(enable)
for _, document in pairs(openDocuments) do
document.editor:SetReadOnly(enable)
@@ -574,14 +579,19 @@ function StripShebang(code) return (code:gsub("^#!.-\n", "\n")) end
local compileOk, compileTotal = 0, 0
function CompileProgram(editor, params)
local params = { jumponerror = (params or {}).jumponerror ~= false,
reportstats = (params or {}).reportstats ~= false }
local id = editor:GetId()
local filePath = DebuggerMakeFileName(editor, openDocuments[id].filePath)
local params = {
jumponerror = (params or {}).jumponerror ~= false,
reportstats = (params or {}).reportstats ~= false,
keepoutput = (params or {}).keepoutput,
}
local doc = ide:GetDocument(editor)
local filePath = doc:GetFilePath() or doc:GetFileName()
local func, err = loadstring(StripShebang(editor:GetText()), '@'..filePath)
local line = not func and tonumber(err:match(":(%d+)%s*:")) or nil
if ide.frame.menuBar:IsChecked(ID_CLEAROUTPUT) then ClearOutput() end
if not params.keepoutput and ide.frame.menuBar:IsChecked(ID_CLEAROUTPUT) then
ClearOutput()
end
compileTotal = compileTotal + 1
if func then
@@ -723,19 +733,27 @@ function SetOpenTabs(params)
DisplayOutputLn(TR("Can't process auto-recovery record; invalid format: %s."):format(nametab))
return
end
DisplayOutputLn(TR("Found auto-recovery record and restored saved session."))
if not params.quiet then
DisplayOutputLn(TR("Found auto-recovery record and restored saved session."))
end
for _,doc in ipairs(nametab) do
local editor = doc.filename and LoadFile(doc.filename,nil,true,true) or NewFile()
local opendoc = openDocuments[editor:GetId()]
if doc.content then
notebook:SetPageText(opendoc.index, doc.tabname)
editor:SetText(doc.content)
if doc.filename and opendoc.modTime and doc.modified < opendoc.modTime:GetTicks() then
DisplayOutputLn(TR("File '%s' has more recent timestamp than restored '%s'; please review before saving.")
:format(doc.filename, doc.tabname))
-- check for missing file is no content is stored
if doc.filepath and not doc.content and not wx.wxFileExists(doc.filepath) then
DisplayOutputLn(TR("File '%s' is missing and can't be recovered.")
:format(doc.filepath))
else
local editor = doc.filepath and LoadFile(doc.filepath,nil,true,true) or NewFile(doc.filename)
local opendoc = openDocuments[editor:GetId()]
if doc.content then
editor:SetText(doc.content)
if doc.filepath and opendoc.modTime and doc.modified < opendoc.modTime:GetTicks() then
DisplayOutputLn(TR("File '%s' has more recent timestamp than restored '%s'; please review before saving.")
:format(doc.filepath, doc.tabname))
end
SetDocumentModified(editor:GetId(), true)
end
editor:GotoPosDelayed(doc.cursorpos or 0)
end
editor:GotoPosDelayed(doc.cursorpos or 0)
end
notebook:SetSelection(params and params.index or 0)
SetEditorSelection()
@@ -745,7 +763,8 @@ local function getOpenTabs()
local opendocs = {}
for _, document in pairs(ide.openDocuments) do
table.insert(opendocs, {
filename = document.filePath,
filename = document.fileName,
filepath = document.filePath,
tabname = notebook:GetPageText(document.index),
modified = document.modTime and document.modTime:GetTicks(), -- get number of seconds
content = document.isModified and document.editor:GetText() or nil,
@@ -764,18 +783,34 @@ function SetAutoRecoveryMark()
ide.session.lastupdated = os.time()
end
local function saveAutoRecovery(event)
local function generateRecoveryRecord(opentabs)
return require('mobdebug').line(opentabs, {comment = false})
end
local function saveHotExit()
local opentabs, params = getOpenTabs()
if #opentabs > 0 then
params.recovery = generateRecoveryRecord(opentabs)
params.quiet = true
SettingsSaveFileSession({}, params)
end
end
local function saveAutoRecovery(force)
if not ide.config.autorecoverinactivity then return end
local lastupdated = ide.session.lastupdated
if not ide.config.autorecoverinactivity or not lastupdated then return end
if lastupdated < (ide.session.lastsaved or 0) then return end
if not force then
if not lastupdated or lastupdated < (ide.session.lastsaved or 0) then return end
end
local now = os.time()
if lastupdated + ide.config.autorecoverinactivity > now then return end
if not force and lastupdated + ide.config.autorecoverinactivity > now then return end
-- find all open modified files and save them
local opentabs, params = getOpenTabs()
if #opentabs > 0 then
params.recovery = require('mobdebug').line(opentabs, {comment = false})
params.recovery = generateRecoveryRecord(opentabs)
SettingsSaveAll()
SettingsSaveFileSession({}, params)
ide.settings:Flush()
@@ -870,7 +905,7 @@ local function closeWindow(event)
ide.exitingProgram = true -- don't handle focus events
if not SaveOnExit(event:CanVeto()) then
if not ide.config.hotexit and not SaveOnExit(event:CanVeto()) then
event:Veto()
ide.exitingProgram = false
return
@@ -887,6 +922,7 @@ local function closeWindow(event)
DebuggerShutdown()
SettingsSaveAll()
if ide.config.hotexit then saveHotExit() end
ide.settings:Flush()
do -- hide all floating panes first
@@ -906,7 +942,7 @@ local function closeWindow(event)
end
frame:Connect(wx.wxEVT_CLOSE_WINDOW, closeWindow)
frame:Connect(wx.wxEVT_TIMER, saveAutoRecovery)
frame:Connect(wx.wxEVT_TIMER, function() saveAutoRecovery() end)
-- in the presence of wxAuiToolbar, when (1) the app gets focus,
-- (2) a floating panel is closed or (3) a toolbar dropdown is closed,
@@ -971,6 +1007,9 @@ ide.editorApp:Connect(wx.wxEVT_ACTIVATE_APP,
pcall(function() infocus:SetFocus() end)
end
-- save auto-recovery record when making the app inactive
if not event:GetActive() then saveAutoRecovery(true) end
local event = event:GetActive() and "onAppFocusSet" or "onAppFocusLost"
PackageEventHandle(event, ide.editorApp)
end

View File

@@ -24,6 +24,18 @@ debugger.hostname = ide.config.debugger.hostname or (function()
return hostname and socket.dns.toip(hostname) and hostname or "localhost"
end)()
local image = { STACK = 0, LOCAL = 1, UPVALUE = 2 }
do
local getBitmap = (ide.app.createbitmap or wx.wxArtProvider.GetBitmap)
local size = wx.wxSize(16,16)
local imglist = wx.wxImageList(16,16)
imglist:Add(getBitmap("GO-FORWARD", "OTHER", size)) -- 0 = stack call
imglist:Add(getBitmap("LIST-VIEW", "OTHER", size)) -- 1 = local variables
imglist:Add(getBitmap("REPORT-VIEW", "OTHER", size)) -- 2 = upvalues
debugger.imglist = imglist
end
local notebook = ide.frame.notebook
local CURRENT_LINE_MARKER = StylesGetMarker("currentline")
@@ -58,7 +70,7 @@ end
local q = EscapeMagic
local function updateWatchesSync(num)
local function updateWatchesSync(onlyitem)
local watchCtrl = debugger.watchCtrl
local pane = ide.frame.uimgr:GetPane("watchpanel")
local shown = watchCtrl and (pane:IsOk() and pane:IsShown() or not pane:IsOk() and watchCtrl:IsShown())
@@ -67,43 +79,55 @@ local function updateWatchesSync(num)
local bgcl = watchCtrl:GetBackgroundColour()
local hicl = wx.wxColour(math.floor(bgcl:Red()*.9),
math.floor(bgcl:Green()*.9), math.floor(bgcl:Blue()*.9))
for idx = 0, watchCtrl:GetItemCount() - 1 do
if not num or idx == num then
local expression = watchCtrl:GetItemText(idx)
local _, values, error = debugger.evaluate(expression)
if error then error = error:gsub("%[.-%]:%d+:%s+","")
elseif #values == 0 then values = {'nil'} end
local newval = error and ('error: '..error) or values[1]
-- get the current value from a list item
do local litem = wx.wxListItem()
litem:SetMask(wx.wxLIST_MASK_TEXT)
litem:SetId(idx)
litem:SetColumn(1)
watchCtrl:GetItem(litem)
watchCtrl:SetItemBackgroundColour(idx,
watchCtrl:GetItem(litem) and newval ~= litem:GetText()
and hicl or bgcl)
local root = watchCtrl:GetRootItem()
if not root or not root:IsOk() then return end
local item = onlyitem or watchCtrl:GetFirstChild(root)
while true do
if not item:IsOk() then break end
local expression = watchCtrl:GetItemExpression(item)
if expression then
local _, values, error = debugger.evaluate(expression)
local curchildren = watchCtrl:GetItemChildren(item)
if error then
error = error:gsub("%[.-%]:%d+:%s+","")
watchCtrl:SetItemValueIfExpandable(item, nil)
else
if #values == 0 then values = {'nil'} end
local ok, res = LoadSafe("return "..values[1])
watchCtrl:SetItemValueIfExpandable(item, res)
end
watchCtrl:SetItem(idx, 1, newval)
local newval = (expression .. ' = '
.. (error and ('error: '..error) or table.concat(values, ", ")))
local val = watchCtrl:GetItemText(item)
watchCtrl:SetItemBackgroundColour(item, val ~= newval and hicl or bgcl)
watchCtrl:SetItemText(item, newval)
if onlyitem or val ~= newval then
local newchildren = watchCtrl:GetItemChildren(item)
if next(curchildren) ~= nil and next(newchildren) == nil then
watchCtrl:SetItemHasChildren(item, true)
watchCtrl:CollapseAndReset(item)
watchCtrl:SetItemHasChildren(item, false)
elseif next(curchildren) ~= nil and next(newchildren) ~= nil then
watchCtrl:CollapseAndReset(item)
watchCtrl:Expand(item)
end
end
end
if onlyitem then break end
item = watchCtrl:GetNextSibling(item)
end
end
end
local simpleType = {['nil'] = true, ['string'] = true, ['number'] = true, ['boolean'] = true}
local stackItemValue = {}
local callData = {}
local function checkIfExpandable(value, item)
local expandable = type(value) == 'table' and next(value) ~= nil
and not stackItemValue[value] -- only expand first time
if expandable then -- cache table value to expand when requested
stackItemValue[item:GetValue()] = value
stackItemValue[value] = item:GetValue() -- to avoid circular refs
end
return expandable
end
local function updateStackSync()
local stackCtrl = debugger.stackCtrl
@@ -113,17 +137,16 @@ local function updateStackSync()
and not debugger.scratchpad then
local stack, _, err = debugger.stack()
if not stack or #stack == 0 then
stackCtrl:DeleteAllItems()
stackCtrl:DeleteAll()
if err then -- report an error if any
stackCtrl:AppendItem(stackCtrl:AddRoot("Stack"), "Error: " .. err, 0)
stackCtrl:AppendItem(stackCtrl:AddRoot("Stack"), "Error: " .. err, image.STACK)
end
return
end
stackCtrl:Freeze()
stackCtrl:DeleteAllItems()
stackCtrl:DeleteAll()
local root = stackCtrl:AddRoot("Stack")
stackItemValue = {} -- reset cache of items in the stack
callData = {} -- reset call cache
for _,frame in ipairs(stack) do
-- "main chunk at line 24"
@@ -146,7 +169,7 @@ local function updateStackSync()
or " (defined in "..call[7]..")"))
-- create the new tree item for this level of the call stack
local callitem = stackCtrl:AppendItem(root, text, 0)
local callitem = stackCtrl:AppendItem(root, text, image.STACK)
-- register call data to provide stack navigation
callData[callitem:GetValue()] = { call[2], call[4] }
@@ -162,10 +185,8 @@ local function updateStackSync()
local text = ("%s = %s%s"):
format(name, fixUTF8(trimToMaxLength(serialize(value, params))),
simpleType[type(value)] and "" or (" --[["..comment.."]]"))
local item = stackCtrl:AppendItem(callitem, text, 1)
if checkIfExpandable(value, item) then
stackCtrl:SetItemHasChildren(item, true)
end
local item = stackCtrl:AppendItem(callitem, text, image.LOCAL)
stackCtrl:SetItemValueIfExpandable(item, value)
end
-- add the upvalues for this call stack level to the tree item
@@ -174,10 +195,8 @@ local function updateStackSync()
local text = ("%s = %s%s"):
format(name, fixUTF8(trimToMaxLength(serialize(value, params))),
simpleType[type(value)] and "" or (" --[["..comment.."]]"))
local item = stackCtrl:AppendItem(callitem, text, 2)
if checkIfExpandable(value, item) then
stackCtrl:SetItemHasChildren(item, true)
end
local item = stackCtrl:AppendItem(callitem, text, image.UPVALUE)
stackCtrl:SetItemValueIfExpandable(item, value)
end
stackCtrl:SortChildren(callitem)
@@ -198,12 +217,12 @@ local function updateStackAndWatches()
end
end
local function updateWatches(num)
local function updateWatches(item)
-- check if the debugger is running and may be waiting for a response.
-- allow that request to finish, otherwise updateWatchesSync() does nothing.
if debugger.running then debugger.update() end
if debugger.server and not debugger.running then
copas.addthread(function() updateWatchesSync(num) end)
copas.addthread(function() updateWatchesSync(item) end)
end
end
@@ -430,6 +449,8 @@ debugger.shell = function(expression, isstatement)
updateStackSync() updateWatchesSync()
end
end)
elseif debugger.server then
DisplayShellErr(TR("Can't evaluate the expression while the application is running."))
end
end
@@ -443,6 +464,46 @@ local function stoppedAtBreakpoint(file, line)
return breakpoint > -1 and breakpoint == current
end
local function mapRemotePath(basedir, file, line, method)
if not file then return end
-- file is /foo/bar/my.lua; basedir is d:\local\path\
-- check for d:\local\path\my.lua, d:\local\path\bar\my.lua, ...
-- wxwidgets on Windows handles \\ and / as separators, but on OSX
-- and Linux it only handles 'native' separator;
-- need to translate for GetDirs to work.
local file = file:gsub("\\", "/")
local parts = wx.wxFileName(file):GetDirs()
local name = wx.wxFileName(file):GetFullName()
-- find the longest remote path that can be mapped locally
local longestpath, remotedir
while true do
local mapped = GetFullPathIfExists(basedir, name)
if mapped then
longestpath = mapped
remotedir = file:gsub(q(name):gsub("/", ".").."$", "")
end
if #parts == 0 then break end
name = table.remove(parts, #parts) .. "/" .. name
end
-- if found a local mapping under basedir
local activated = longestpath and activateDocument(longestpath, line, method or activate.NOREPORT)
if activated then
-- find remote basedir by removing the tail from remote file
debugger.handle("basedir " .. debugger.basedir .. "\t" .. remotedir)
-- reset breakpoints again as remote basedir has changed
reSetBreakpoints()
DisplayOutputLn(TR("Mapped remote request for '%s' to '%s'.")
:format(remotedir, debugger.basedir))
return longestpath
end
return nil
end
debugger.listen = function(start)
if start == false then
if debugger.listening then
@@ -583,8 +644,11 @@ debugger.listen = function(start)
..":\n"..err)
return debugger.terminate()
elseif options.runstart then
if stoppedAtBreakpoint(file or startfile, line or 0) then
activateDocument(file or startfile, line or 0)
local file = (mapRemotePath(basedir, file, line or 0, activate.CHECKONLY)
or file or startfile)
if stoppedAtBreakpoint(file, line or 0) then
activateDocument(file, line or 0)
options.runstart = false
end
elseif file and line then
@@ -607,36 +671,8 @@ debugger.listen = function(start)
-- when autoactivation is disabled.
if not activated and (not wx.wxFileName(file):FileExists()
or wx.wxIsAbsolutePath(file)) then
-- file is /foo/bar/my.lua; basedir is d:\local\path\
-- check for d:\local\path\my.lua, d:\local\path\bar\my.lua, ...
-- wxwidgets on Windows handles \\ and / as separators, but on OSX
-- and Linux it only handles 'native' separator;
-- need to translate for GetDirs to work.
local file = file:gsub("\\", "/")
local parts = wx.wxFileName(file):GetDirs()
local name = wx.wxFileName(file):GetFullName()
-- find the longest remote path that can be mapped locally
local longestpath, remotedir
while true do
local mapped = GetFullPathIfExists(basedir, name)
if mapped then
longestpath = mapped
remotedir = file:gsub(q(name):gsub("/", ".").."$", "")
end
if #parts == 0 then break end
name = table.remove(parts, #parts) .. "/" .. name
end
-- if found a local mapping under basedir
activated = longestpath and activateDocument(longestpath, line, activate.NOREPORT)
if activated then
-- find remote basedir by removing the tail from remote file
debugger.handle("basedir " .. debugger.basedir .. "\t" .. remotedir)
-- reset breakpoints again as remote basedir has changed
reSetBreakpoints()
DisplayOutputLn(TR("Mapped remote request for '%s' to '%s'.")
:format(remotedir, debugger.basedir))
if mapRemotePath(basedir, file, line, activate.NOREPORT) then
activated = true
end
end
@@ -681,6 +717,12 @@ debugger.listen = function(start)
debugger.listening = server
end
local function nameOutputTab(name)
local nbk = ide.frame.bottomnotebook
local index = nbk:GetPageIndex(ide:GetOutput())
if index then nbk:SetPageText(index, name) end
end
debugger.handle = function(command, server, options)
local verbose = ide.config.debugger.verbose
local osexit, gprint
@@ -689,11 +731,14 @@ debugger.handle = function(command, server, options)
if verbose then DisplayOutputLn(...) end
end
nameOutputTab(TR("Output (running)"))
debugger.running = true
if verbose then DisplayOutputLn("Debugger sent (command):", command) end
local file, line, err = mobdebug.handle(command, server or debugger.server, options)
if verbose then DisplayOutputLn("Debugger received (file, line, err):", file, line, err) end
debugger.running = false
-- only set suspended if the debugging hasn't been terminated
if debugger.server then nameOutputTab(TR("Output (suspended)")) end
os.exit = osexit
_G.print = gprint
@@ -893,58 +938,56 @@ debugger.quickeval = function(var, callback)
end
end
-- need imglist to be a file local variable as SetImageList takes ownership
-- of it and if done inside a function, icons do not work as expected
local imglist = wx.wxImageList(16,16)
do
local getBitmap = (ide.app.createbitmap or wx.wxArtProvider.GetBitmap)
local size = wx.wxSize(16,16)
-- 0 = stack call
imglist:Add(getBitmap(wx.wxART_GO_FORWARD, wx.wxART_OTHER, size))
-- 1 = local variables
imglist:Add(getBitmap(wx.wxART_LIST_VIEW, wx.wxART_OTHER, size))
-- 2 = upvalues
imglist:Add(getBitmap(wx.wxART_REPORT_VIEW, wx.wxART_OTHER, size))
function DebuggerAddStackWindow()
return ide:AddPanel(debugger.stackCtrl, "stackpanel", TR("Stack"))
end
function DebuggerAddWatchWindow()
return ide:AddPanel(debugger.watchCtrl, "watchpanel", TR("Watch"))
end
local width, height = 360, 200
function debuggerAddWindow(ctrl, panel, name)
local notebook = wxaui.wxAuiNotebook(ide.frame, wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxDefaultSize,
wxaui.wxAUI_NB_DEFAULT_STYLE + wxaui.wxAUI_NB_TAB_EXTERNAL_MOVE
- wxaui.wxAUI_NB_CLOSE_ON_ACTIVE_TAB + wx.wxNO_BORDER)
notebook:AddPage(ctrl, name, true)
notebook:Connect(wxaui.wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK,
function() PaneFloatToggle(notebook) end)
local keyword = {}
for _,k in ipairs({'and', 'break', 'do', 'else', 'elseif', 'end', 'false',
'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat',
'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end
local mgr = ide.frame.uimgr
mgr:AddPane(notebook, wxaui.wxAuiPaneInfo():
Name(panel):Float():CaptionVisible(false):PaneBorder(false):
MinSize(width/2,height/2):
BestSize(width,height):FloatingSize(width,height):
PinButton(true):Hide())
mgr.defaultPerspective = mgr:SavePerspective() -- resave default perspective
return notebook
local function stringifyKeyIntoPrefix(name, num)
return (type(name) == "number"
and (num and num == name and '' or ("[%s] = "):format(name))
or type(name) == "string" and (name:match("^[%l%u_][%w_]*$") and not keyword[name]
and ("%s = "):format(name)
or ("[%q] = "):format(name))
or ("[%s] = "):format(tostring(name)))
end
function DebuggerAddStackWindow()
return debuggerAddWindow(debugger.stackCtrl, "stackpanel", TR("Stack"))
end
function DebuggerAddWatchWindow()
return debuggerAddWindow(debugger.watchCtrl, "watchpanel", TR("Watch"))
end
function debuggerCreateStackWindow()
local function debuggerCreateStackWindow()
local stackCtrl = wx.wxTreeCtrl(ide.frame, wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxSize(width, height),
wx.wxTR_LINES_AT_ROOT + wx.wxTR_HAS_BUTTONS + wx.wxTR_SINGLE + wx.wxTR_HIDE_ROOT)
debugger.stackCtrl = stackCtrl
stackCtrl:SetImageList(imglist)
stackCtrl:SetImageList(debugger.imglist)
local valuecache = {}
function stackCtrl:SetItemValueIfExpandable(item, value)
local expandable = type(value) == 'table' and next(value) ~= nil
if expandable then -- cache table value to expand when requested
valuecache[item:GetValue()] = value
end
self:SetItemHasChildren(item, expandable)
end
function stackCtrl:DeleteAll()
self:DeleteAllItems()
valuecache = {}
end
function stackCtrl:GetItemChildren(item)
return valuecache[item:GetValue()] or {}
end
stackCtrl:Connect(wx.wxEVT_COMMAND_TREE_ITEM_EXPANDING,
function (event)
@@ -954,15 +997,12 @@ function debuggerCreateStackWindow()
local image = stackCtrl:GetItemImage(item_id)
local num = 1
for name,value in pairs(stackItemValue[item_id:GetValue()]) do
for name,value in pairs(stackCtrl:GetItemChildren(item_id)) do
local strval = fixUTF8(trimToMaxLength(serialize(value, params)))
local text = type(name) == "number"
and (num == name and strval or ("[%s] = %s"):format(name, strval))
or ("%s = %s"):format(tostring(name), strval)
local text = stringifyKeyIntoPrefix(name, num)..strval
local item = stackCtrl:AppendItem(item_id, text, image)
if checkIfExpandable(value, item) then
stackCtrl:SetItemHasChildren(item, true)
end
stackCtrl:SetItemValueIfExpandable(item, value)
num = num + 1
if num > stackmaxnum then break end
end
@@ -996,84 +1036,193 @@ function debuggerCreateStackWindow()
end
local function debuggerCreateWatchWindow()
local watchCtrl = wx.wxListCtrl(ide.frame, wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxDefaultSize,
wx.wxLC_REPORT + wx.wxLC_EDIT_LABELS)
local watchCtrl = wx.wxTreeCtrl(ide.frame, wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxSize(width, height),
wx.wxTR_LINES_AT_ROOT + wx.wxTR_HAS_BUTTONS + wx.wxTR_SINGLE
+ wx.wxTR_HIDE_ROOT + wx.wxTR_EDIT_LABELS)
debugger.watchCtrl = watchCtrl
local info = wx.wxListItem()
info:SetMask(wx.wxLIST_MASK_TEXT + wx.wxLIST_MASK_WIDTH)
info:SetText(TR("Expression"))
info:SetWidth(width * 0.32)
watchCtrl:InsertColumn(0, info)
local root = watchCtrl:AddRoot("Watch")
watchCtrl:SetImageList(debugger.imglist)
info:SetText(TR("Value"))
info:SetWidth(width * 0.56)
watchCtrl:InsertColumn(1, info)
local defaultExpr = "watch expression"
local expressions = {} -- table to keep track of expressions
local watchMenu = wx.wxMenu {
{ ID_ADDWATCH, TR("&Add Watch")..KSC(ID_ADDWATCH) },
{ ID_EDITWATCH, TR("&Edit Watch")..KSC(ID_EDITWATCH) },
{ ID_DELETEWATCH, TR("&Delete Watch")..KSC(ID_DELETEWATCH) },
}
function watchCtrl:SetItemExpression(item, expr, value)
expressions[item:GetValue()] = expr
self:SetItemText(item, expr .. ' = ' .. (value or '?'))
self:SelectItem(item, true)
if not value then updateWatches(item) end
end
local function findSelectedWatchItem()
local count = watchCtrl:GetSelectedItemCount()
if count > 0 then
for idx = 0, watchCtrl:GetItemCount() - 1 do
if watchCtrl:GetItemState(idx, wx.wxLIST_STATE_FOCUSED) ~= 0 then
return idx
end
end
function watchCtrl:GetItemExpression(item)
return expressions[item:GetValue()]
end
local names = {}
function watchCtrl:SetItemName(item, name)
local nametype = type(name)
names[item:GetValue()] = (
(nametype == 'string' or nametype == 'number' or nametype == 'boolean')
and name or nil
)
end
function watchCtrl:GetItemName(item)
return names[item:GetValue()]
end
local valuecache = {}
function watchCtrl:SetItemValueIfExpandable(item, value)
local expandable = type(value) == 'table' and next(value) ~= nil
valuecache[item:GetValue()] = expandable and value or nil
self:SetItemHasChildren(item, expandable)
end
function watchCtrl:GetItemChildren(item)
return valuecache[item:GetValue()] or {}
end
function watchCtrl:IsWatch(item)
return item:IsOk() and watchCtrl:GetItemParent(item):GetValue() == root:GetValue()
end
function watchCtrl:IsEditable(item)
return (item and item:IsOk()
and (watchCtrl:IsWatch(item) or watchCtrl:GetItemName(item) ~= nil))
end
function watchCtrl:UpdateItemValue(item, value)
local expr = ''
local origitem = item
while true do
local name = watchCtrl:GetItemName(item)
expr = (watchCtrl:IsWatch(item)
and ('({%s})[1]'):format(watchCtrl:GetItemExpression(item))
or (type(name) == 'string' and '[%q]' or '[%s]'):format(tostring(name))
)..expr
if watchCtrl:IsWatch(item) then break end
item = watchCtrl:GetItemParent(item)
if not item:IsOk() then break end
end
if debugger.running then debugger.update() end
if debugger.server and not debugger.running
and (not debugger.scratchpad or debugger.scratchpad.paused) then
copas.addthread(function ()
local _, _, err = debugger.execute(expr..'='..value)
if err then
watchCtrl:SetItemText(origitem, 'error: '..err:gsub("%[.-%]:%d+:%s+",""))
else
updateWatchesSync(item)
end
updateStackSync()
end)
end
return -1
end
local defaultExpr = ""
local function addWatch()
local row = watchCtrl:InsertItem(watchCtrl:GetItemCount(), TR("Expr"))
watchCtrl:SetItem(row, 0, defaultExpr)
watchCtrl:SetItem(row, 1, TR("Value"))
watchCtrl:EditLabel(row)
end
local function editWatch()
local row = findSelectedWatchItem()
if row >= 0 then watchCtrl:EditLabel(row) end
end
local function deleteWatch()
local row = findSelectedWatchItem()
if row >= 0 then watchCtrl:DeleteItem(row) end
end
watchCtrl:Connect(wx.wxEVT_CONTEXT_MENU,
function (event) watchCtrl:PopupMenu(watchMenu) end)
watchCtrl:Connect(ID_ADDWATCH, wx.wxEVT_COMMAND_MENU_SELECTED, addWatch)
watchCtrl:Connect(ID_EDITWATCH, wx.wxEVT_COMMAND_MENU_SELECTED, editWatch)
watchCtrl:Connect(ID_EDITWATCH, wx.wxEVT_UPDATE_UI,
function (event) event:Enable(watchCtrl:GetSelectedItemCount() > 0) end)
watchCtrl:Connect(ID_DELETEWATCH, wx.wxEVT_COMMAND_MENU_SELECTED, deleteWatch)
watchCtrl:Connect(ID_DELETEWATCH, wx.wxEVT_UPDATE_UI,
function (event) event:Enable(watchCtrl:GetSelectedItemCount() > 0) end)
watchCtrl:Connect(wx.wxEVT_COMMAND_LIST_ITEM_ACTIVATED,
function (event) watchCtrl:EditLabel(event:GetIndex()) end)
watchCtrl:Connect(wx.wxEVT_COMMAND_LIST_END_LABEL_EDIT,
watchCtrl:Connect(wx.wxEVT_COMMAND_TREE_ITEM_EXPANDING,
function (event)
local row = event:GetIndex()
local item_id = event:GetItem()
local count = watchCtrl:GetChildrenCount(item_id, false)
if count > 0 then return true end
local image = watchCtrl:GetItemImage(item_id)
local num = 1
for name,value in pairs(watchCtrl:GetItemChildren(item_id)) do
local strval = fixUTF8(trimToMaxLength(serialize(value, params)))
local text = stringifyKeyIntoPrefix(name, num)..strval
local item = watchCtrl:AppendItem(item_id, text, image)
watchCtrl:SetItemValueIfExpandable(item, value)
watchCtrl:SetItemName(item, name)
num = num + 1
if num > stackmaxnum then break end
end
return true
end)
watchCtrl:Connect(wx.wxEVT_COMMAND_TREE_DELETE_ITEM,
function (event)
local value = event:GetItem():GetValue()
expressions[value] = nil
valuecache[value] = nil
names[value] = nil
end)
local item
-- wx.wxEVT_CONTEXT_MENU is only triggered over tree items on OSX,
-- but it needs to be also triggered below any item to add a watch,
-- so use RIGHT_DOWN instead
watchCtrl:Connect(wx.wxEVT_RIGHT_DOWN,
function (event)
-- store the item to be used in edit/delete actions
item = watchCtrl:HitTest(watchCtrl:ScreenToClient(wx.wxGetMousePosition()))
local editlabel = watchCtrl:IsWatch(item) and TR("&Edit Watch") or TR("&Edit Value")
watchCtrl:PopupMenu(wx.wxMenu {
{ ID_ADDWATCH, TR("&Add Watch")..KSC(ID_ADDWATCH) },
{ ID_EDITWATCH, editlabel..KSC(ID_EDITWATCH) },
{ ID_DELETEWATCH, TR("&Delete Watch")..KSC(ID_DELETEWATCH) },
})
item = nil
end)
watchCtrl:Connect(ID_ADDWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event) watchCtrl:EditLabel(watchCtrl:AppendItem(root, defaultExpr, image.LOCAL)) end)
watchCtrl:Connect(ID_EDITWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event) watchCtrl:EditLabel(item or watchCtrl:GetSelection()) end)
watchCtrl:Connect(ID_EDITWATCH, wx.wxEVT_UPDATE_UI,
function (event) event:Enable(watchCtrl:IsEditable(item or watchCtrl:GetSelection())) end)
watchCtrl:Connect(ID_DELETEWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event) watchCtrl:Delete(item or watchCtrl:GetSelection()) end)
watchCtrl:Connect(ID_DELETEWATCH, wx.wxEVT_UPDATE_UI,
function (event) event:Enable(watchCtrl:IsWatch(item or watchCtrl:GetSelection())) end)
local label
watchCtrl:Connect(wx.wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT,
function (event)
local item = event:GetItem()
if not (item:IsOk() and watchCtrl:IsEditable(item)) then
event:Veto()
return
end
label = watchCtrl:GetItemText(item)
if watchCtrl:IsWatch(item) then
local expr = watchCtrl:GetItemExpression(item)
if expr then watchCtrl:SetItemText(item, expr) end
else
local prefix = stringifyKeyIntoPrefix(watchCtrl:GetItemName(item))
local val = watchCtrl:GetItemText(item):gsub(q(prefix),'')
watchCtrl:SetItemText(item, val)
end
end)
watchCtrl:Connect(wx.wxEVT_COMMAND_TREE_END_LABEL_EDIT,
function (event)
event:Veto()
local item = event:GetItem()
if event:IsEditCancelled() then
if watchCtrl:GetItemText(row) == defaultExpr then
watchCtrl:DeleteItem(row)
if watchCtrl:GetItemText(item) == defaultExpr then
-- when Delete is called from END_EDIT, it causes infinite loop
-- on OSX (wxwidgets 2.9.5) as Delete calls END_EDIT again.
-- disable handlers during Delete and then enable back.
watchCtrl:SetEvtHandlerEnabled(false)
watchCtrl:Delete(item)
watchCtrl:SetEvtHandlerEnabled(true)
else
watchCtrl:SetItemText(item, label)
end
else
watchCtrl:SetItem(row, 0, event:GetText())
updateWatches(row)
if watchCtrl:IsWatch(item) then
watchCtrl:SetItemExpression(item, event:GetLabel())
else
watchCtrl:UpdateItemValue(item, event:GetLabel())
end
end
event:Skip()
end)
@@ -1094,27 +1243,6 @@ debuggerCreateWatchWindow()
DebuggerRefreshPanels = updateStackAndWatches
function DebuggerAddWatch(watch)
local mgr = ide.frame.uimgr
local pane = mgr:GetPane("watchpanel")
if (pane:IsOk() and not pane:IsShown()) then
pane:Show()
mgr:Update()
end
local watchCtrl = debugger.watchCtrl
-- check if this expression is already on the list
for idx = 0, watchCtrl:GetItemCount() - 1 do
if watchCtrl:GetItemText(idx) == watch then return end
end
local row = watchCtrl:InsertItem(watchCtrl:GetItemCount(), TR("Expr"))
watchCtrl:SetItem(row, 0, watch)
watchCtrl:SetItem(row, 1, TR("Value"))
updateWatches(row)
end
function DebuggerAttachDefault(options)
debugger.options = options
if (debugger.listening) then return end
@@ -1146,15 +1274,16 @@ function DebuggerStop(resetpid)
if resetpid then debugger.pid = nil end
end
function DebuggerMakeFileName(editor, filePath)
return filePath or ide.config.default.fullname
local function debuggerMakeFileName(editor)
return ide:GetDocument(editor):GetFilePath()
or ide:GetDocument(editor):GetFileName()
or ide.config.default.fullname
end
function DebuggerToggleBreakpoint(editor, line)
local markers = editor:MarkerGet(line)
local id = editor:GetId()
local filePath = debugger.editormap and debugger.editormap[editor]
or DebuggerMakeFileName(editor, ide.openDocuments[id].filePath)
or debuggerMakeFileName(editor)
if bit.band(markers, BREAKPOINT_MARKER_VALUE) > 0 then
editor:MarkerDelete(line, BREAKPOINT_MARKER)
if debugger.server then debugger.breakpoint(filePath, line+1, false) end
@@ -1187,8 +1316,7 @@ function DebuggerRefreshScratchpad()
end
else
local clear = ide.frame.menuBar:IsChecked(ID_CLEAROUTPUT)
local filePath = DebuggerMakeFileName(scratchpadEditor,
ide.openDocuments[scratchpadEditor:GetId()].filePath)
local filePath = debuggerMakeFileName(scratchpadEditor)
-- wrap into a function call to make "return" to work with scratchpad
code = "(function()"..code.."\nend)()"

View File

@@ -141,7 +141,7 @@ end
local function navigateToPosition(editor, fromPosition, toPosition, length)
table.insert(editor.jumpstack, fromPosition)
editor:GotoPos(toPosition)
editor:GotoPosEnforcePolicy(toPosition)
if length then
editor:SetAnchor(toPosition + length)
end
@@ -150,7 +150,7 @@ end
local function navigateBack(editor)
if #editor.jumpstack == 0 then return end
local pos = table.remove(editor.jumpstack)
editor:GotoPos(pos)
editor:GotoPosEnforcePolicy(pos)
return true
end
@@ -664,12 +664,12 @@ function CreateEditor()
-- populate cache with Ctrl-<letter> combinations for workaround on Linux
-- http://wxwidgets.10942.n7.nabble.com/Menu-shortcuts-inconsistentcy-issue-td85065.html
for id, shortcut in pairs(ide.config.keymap) do
local key = shortcut:match('^Ctrl[-+](%w)$')
local key = shortcut:match('^Ctrl[-+](.)$')
if key then editor.ctrlcache[key:byte()] = id end
end
-- populate editor keymap with configured combinations
for _, map in ipairs(edcfg.keymap) do
for _, map in ipairs(edcfg.keymap or {}) do
local key, mod, cmd, os = unpack(map)
if not os or os == ide.osname then
if cmd then
@@ -721,6 +721,8 @@ function CreateEditor()
editor:SetVisiblePolicy(wxstc.wxSTC_VISIBLE_STRICT, 3)
editor:SetMarginType(margin.LINENUMBER, wxstc.wxSTC_MARGIN_NUMBER)
editor:SetMarginMask(margin.LINENUMBER, 0)
editor:SetMarginWidth(margin.LINENUMBER,
editor:TextWidth(wxstc.wxSTC_STYLE_DEFAULT, linenummask))
@@ -772,6 +774,11 @@ function CreateEditor()
editor:AutoCompStops([[ \n\t=-+():.,;*/!"'$%&~'#°^@?´`<>][|}{]])
end
function editor:GotoPosEnforcePolicy(pos)
self:GotoPos(pos)
self:EnsureVisibleEnforcePolicy(self:LineFromPosition(pos))
end
-- GotoPos should work by itself, but it doesn't (wx 2.9.5).
-- This is likely because the editor window hasn't been refreshed yet,
-- so its LinesOnScreen method returns 0/-1, which skews the calculations.
@@ -788,20 +795,20 @@ function CreateEditor()
if ide.osname ~= 'Macintosh' then self:GotoPos(pos) end
else
redolater = nil
self:GotoPos(pos)
self:EnsureVisibleEnforcePolicy(self:LineFromPosition(pos))
self:GotoPosEnforcePolicy(pos)
end
elseif not badtime and redolater then
-- reset the left margin first to make sure that the position
-- is set "from the left" to get the best content displayed.
self:SetXOffset(0)
self:GotoPos(redolater)
self:EnsureVisibleEnforcePolicy(self:LineFromPosition(redolater))
self:GotoPosEnforcePolicy(redolater)
redolater = nil
end
end
end
function editor:SetupKeywords(...) return SetupKeywords(self, ...) end
editor.ev = {}
editor:Connect(wxstc.wxEVT_STC_MARGINCLICK,
function (event)
@@ -827,24 +834,36 @@ function CreateEditor()
editor.assignscache = false
end
local evtype = event:GetModificationType()
if (bit.band(evtype,wxstc.wxSTC_MOD_INSERTTEXT) ~= 0) then
local inserted = bit.band(evtype, wxstc.wxSTC_MOD_INSERTTEXT) ~= 0
local deleted = bit.band(evtype, wxstc.wxSTC_MOD_DELETETEXT) ~= 0
if (inserted or deleted) then
SetAutoRecoveryMark()
table.insert(editor.ev,{event:GetPosition(),event:GetLinesAdded()})
DynamicWordsAdd(editor,nil,editor:LineFromPosition(event:GetPosition()),event:GetLinesAdded())
local firstLine = editor:LineFromPosition(event:GetPosition())
local linesChanged = inserted and event:GetLinesAdded() or 0
table.insert(editor.ev, {event:GetPosition(), linesChanged})
DynamicWordsAdd(editor, nil, firstLine, linesChanged)
end
if (bit.band(evtype,wxstc.wxSTC_MOD_DELETETEXT) ~= 0) then
SetAutoRecoveryMark()
table.insert(editor.ev,{event:GetPosition(),0})
DynamicWordsAdd(editor,nil,editor:LineFromPosition(event:GetPosition()),0)
local beforeInserted = bit.band(evtype,wxstc.wxSTC_MOD_BEFOREINSERT) ~= 0
local beforeDeleted = bit.band(evtype,wxstc.wxSTC_MOD_BEFOREDELETE) ~= 0
if (beforeInserted or beforeDeleted) then
-- unfold the current line being changed if folded
local firstLine = editor:LineFromPosition(event:GetPosition())
if not editor:GetFoldExpanded(firstLine) then editor:ToggleFold(firstLine) end
end
if ide.config.acandtip.nodynwords then return end
-- only required to track changes
if (bit.band(evtype,wxstc.wxSTC_MOD_BEFOREDELETE) ~= 0) then
local _, numlines = event:GetText():gsub("\r?\n","%1")
DynamicWordsRem(editor,nil,editor:LineFromPosition(event:GetPosition()), numlines)
if beforeDeleted then
local pos = event:GetPosition()
local text = editor:GetTextRange(pos, pos+event:GetLength())
local _, numlines = text:gsub("\r?\n","%1")
DynamicWordsRem(editor,nil,editor:LineFromPosition(pos), numlines)
end
if (bit.band(evtype,wxstc.wxSTC_MOD_BEFOREINSERT) ~= 0) then
if beforeInserted then
DynamicWordsRem(editor,nil,editor:LineFromPosition(event:GetPosition()), 0)
end
end)
@@ -868,18 +887,22 @@ function CreateEditor()
local indent = editor:GetLineIndentation(line - 1)
local linedone = editor:GetLine(line - 1)
-- if the indentation is 0 and the current line is not empty
-- then take indentation from the current line (instead of the
-- previous one). This may happen when CR is hit at the beginning
-- of a line (rather than at the end).
if indent == 0 and not linetx:match("^[\010\013]*$") then
-- if the indentation is 0 and the current line is not empty,
-- but the previous line is empty, then take indentation from the
-- current line (instead of the previous one). This may happen when
-- CR is hit at the beginning of a line (rather than at the end).
if indent == 0 and not linetx:match("^[\010\013]*$")
and linedone:match("^[\010\013]*$") then
indent = editor:GetLineIndentation(line)
end
local ut = editor:GetUseTabs()
local tw = ut and editor:GetTabWidth() or editor:GetIndent()
local style = bit.band(editor:GetStyleAt(editor:PositionFromLine(line-1)), 31)
if edcfg.smartindent
-- don't apply smartindent to multi-line comments or strings
and not (editor.spec.iscomment[style] or editor.spec.isstring[style])
and editor.spec.isdecindent and editor.spec.isincindent then
local closed, blockend = editor.spec.isdecindent(linedone)
local opened = editor.spec.isincindent(linedone)
@@ -1015,7 +1038,9 @@ function CreateEditor()
-- where refresh of R/W and R/O status in the status bar is delayed.
editor:Connect(wxstc.wxEVT_STC_PAINTED,
function ()
function (event)
PackageEventHandle("onEditorPainted", editor, event)
if ide.osname == 'Windows' then
updateStatusText(editor)
@@ -1030,7 +1055,9 @@ function CreateEditor()
end)
editor:Connect(wxstc.wxEVT_STC_UPDATEUI,
function ()
function (event)
PackageEventHandle("onEditorUpdateUI", editor, event)
if ide.osname ~= 'Windows' then updateStatusText(editor) end
editor:GotoPosDelayed()
@@ -1174,6 +1201,10 @@ function CreateEditor()
-- if no "jump back" is needed, then do normal processing as this
-- combination can be mapped to some action
if not navigateBack(editor) then event:Skip() end
elseif (keycode == wx.WXK_DELETE and mod == wx.wxMOD_SHIFT)
or (keycode == wx.WXK_INSERT and mod == wx.wxMOD_CONTROL) then
ide.frame:AddPendingEvent(wx.wxCommandEvent(
wx.wxEVT_COMMAND_MENU_SELECTED, keycode == wx.WXK_INSERT and ID_COPY or ID_CUT))
elseif ide.osname == "Unix" and ide.wxver >= "2.9.5"
and mod == wx.wxMOD_CONTROL and editor.ctrlcache[keycode] then
ide.frame:AddPendingEvent(wx.wxCommandEvent(
@@ -1250,6 +1281,7 @@ function CreateEditor()
or (" (%d)"):format(#instances+(instances[0] and 1 or 0))
local line = instances and instances[0] and editor:LineFromPosition(instances[0]-1)+1
local def = line and " ("..TR("on line %d"):format(line)..")" or ""
local selections = ide.wxver >= "2.9.5" and editor:GetSelections() or 1
local menu = wx.wxMenu {
{ ID_UNDO, TR("&Undo") },
@@ -1262,6 +1294,7 @@ function CreateEditor()
{ },
{ ID_GOTODEFINITION, TR("Go To Definition")..def },
{ ID_RENAMEALLINSTANCES, TR("Rename All Instances")..occurrences },
{ ID_REPLACEALLSELECTIONS, TR("Replace All Selections") },
{ },
{ ID_QUICKADDWATCH, TR("Add Watch Expression") },
{ ID_QUICKEVAL, TR("Evaluate In Console") },
@@ -1269,7 +1302,9 @@ function CreateEditor()
}
menu:Enable(ID_GOTODEFINITION, instances and instances[0])
menu:Enable(ID_RENAMEALLINSTANCES, instances and (instances[0] or #instances > 0))
menu:Enable(ID_RENAMEALLINSTANCES, instances and (instances[0] or #instances > 0)
or editor:GetSelectionStart() ~= editor:GetSelectionEnd())
menu:Enable(ID_REPLACEALLSELECTIONS, selections > 1)
menu:Enable(ID_QUICKADDWATCH, value ~= nil)
menu:Enable(ID_QUICKEVAL, value ~= nil)
@@ -1300,12 +1335,49 @@ function CreateEditor()
editor:Connect(ID_RENAMEALLINSTANCES, wx.wxEVT_COMMAND_MENU_SELECTED,
function(event)
if value and pos then
if not (instances and (instances[0] or #instances > 0)) then
-- if multiple instances (of a variable) are not detected,
-- then simply find all instances of (selected) `value`
instances = {}
local length, pos = editor:GetLength(), 0
while true do
editor:SetTargetStart(pos)
editor:SetTargetEnd(length)
pos = editor:SearchInTarget(value)
if pos == -1 then break end
table.insert(instances, pos+1)
pos = pos + #value
end
end
selectAllInstances(instances, value, pos)
end
end)
editor:Connect(ID_REPLACEALLSELECTIONS, wx.wxEVT_COMMAND_MENU_SELECTED,
function(event)
local main = editor:GetMainSelection()
local text = wx.wxGetTextFromUser(
TR("Enter replacement text"),
TR("Replace All Selections"),
editor:GetTextRange(editor:GetSelectionNStart(main), editor:GetSelectionNEnd(main))
)
if not text or text == "" then return end
editor:BeginUndoAction()
for s = 0, editor:GetSelections()-1 do
local selst, selend = editor:GetSelectionNStart(s), editor:GetSelectionNEnd(s)
editor:SetTargetStart(selst)
editor:SetTargetEnd(selend)
editor:ReplaceTarget(text)
editor:SetSelectionNStart(s, selst)
editor:SetSelectionNEnd(s, selst+#text)
end
editor:EndUndoAction()
editor:SetMainSelection(main)
end)
editor:Connect(ID_QUICKADDWATCH, wx.wxEVT_COMMAND_MENU_SELECTED,
function(event) DebuggerAddWatch(value) end)
function(event) ide:AddWatch(value) end)
editor:Connect(ID_QUICKEVAL, wx.wxEVT_COMMAND_MENU_SELECTED,
function(event) ShellExecuteCode(value) end)
@@ -1460,5 +1532,6 @@ funclist:Connect(wx.wxEVT_COMMAND_CHOICE_SELECTED,
editor:GotoLine(l)
editor:SetFocus()
editor:SetSTCFocus(true)
editor:EnsureVisibleEnforcePolicy(l)
end
end)

View File

@@ -22,18 +22,15 @@ local q = EscapeMagic
-- generic tree
-- ------------
local IMG_DIRECTORY, IMG_FILE_KNOWN, IMG_FILE_OTHER = 0, 1, 2
local image = { DIRECTORY = 0, FILEKNOWN = 1, FILEOTHER = 2 }
do
local getBitmap = (ide.app.createbitmap or wx.wxArtProvider.GetBitmap)
local size = wx.wxSize(16, 16)
filetree.imglist = wx.wxImageList(16,16)
-- 0 = directory
filetree.imglist:Add(getBitmap(wx.wxART_FOLDER, wx.wxART_OTHER, size))
-- 1 = file known spec
filetree.imglist:Add(getBitmap(wx.wxART_HELP_PAGE, wx.wxART_OTHER, size))
-- 2 = file other
filetree.imglist:Add(getBitmap(wx.wxART_NORMAL_FILE, wx.wxART_OTHER, size))
filetree.imglist:Add(getBitmap("FOLDER", "OTHER", size)) -- 0 = directory
filetree.imglist:Add(getBitmap("HELP-PAGE", "OTHER", size)) -- 1 = file known spec
filetree.imglist:Add(getBitmap("NORMAL-FILE", "OTHER", size)) -- 2 = file other
end
local function treeAddDir(tree,parent_id,rootdir)
@@ -50,7 +47,7 @@ local function treeAddDir(tree,parent_id,rootdir)
for _, file in ipairs(FileSysGetRecursive(rootdir)) do
local name, dir = file:match("([^"..pathsep.."]+)("..pathsep.."?)$")
local known = GetSpec(GetFileExt(name))
local icon = #dir>0 and IMG_DIRECTORY or known and IMG_FILE_KNOWN or IMG_FILE_OTHER
local icon = #dir>0 and image.DIRECTORY or known and image.FILEKNOWN or image.FILEOTHER
local item = items[name .. icon]
if item then -- existing item
-- keep deleting items until we find item
@@ -68,7 +65,7 @@ local function treeAddDir(tree,parent_id,rootdir)
curr = (curr
and tree:InsertItem(parent_id, curr, name, icon)
or tree:PrependItem(parent_id, name, icon))
if #dir>0 then tree:SetItemHasChildren(curr, FileSysHasContent(file)) end
if #dir>0 then tree:SetItemHasChildren(curr, FileDirHasContent(file)) end
end
if curr:IsOk() then cache[iscaseinsensitive and name:lower() or name] = curr end
end
@@ -97,7 +94,7 @@ local function treeSetRoot(tree,rootdir)
tree:DeleteAllItems()
if (not wx.wxDirExists(rootdir)) then return end
local root_id = tree:AddRoot(rootdir, IMG_DIRECTORY)
local root_id = tree:AddRoot(rootdir, image.DIRECTORY)
tree:SetItemHasChildren(root_id, true) -- make sure that the item can expand
tree:Expand(root_id) -- this will also populate the tree
end
@@ -141,15 +138,20 @@ local function findItem(tree, match)
end
local function treeSetConnectorsAndIcons(tree)
tree:SetImageList(filetree.imglist)
tree:AssignImageList(filetree.imglist)
local function isIt(item, imgtype) return tree:GetItemImage(item) == imgtype end
function tree:IsDirectory(item_id) return isIt(item_id, IMG_DIRECTORY) end
function tree:IsFileKnown(item_id) return isIt(item_id, IMG_FILE_KNOWN) end
function tree:IsFileOther(item_id) return isIt(item_id, IMG_FILE_OTHER) end
function tree:IsDirectory(item_id) return isIt(item_id, image.DIRECTORY) end
function tree:IsFileKnown(item_id) return isIt(item_id, image.FILEKNOWN) end
function tree:IsFileOther(item_id) return isIt(item_id, image.FILEOTHER) end
function tree:IsRoot(item_id) return not tree:GetItemParent(item_id):IsOk() end
function tree:FindItem(match)
return findItem(self, wx.wxIsAbsolutePath(match) and match
or MergeFullPath(ide:GetProject(), match))
end
function tree:GetItemFullName(item_id)
local tree = self
local str = tree:GetItemText(item_id)
@@ -200,7 +202,7 @@ local function treeSetConnectorsAndIcons(tree)
local empty = ""
local function renameItem(itemsrc, target)
local isdir = tree:GetItemImage(itemsrc) == IMG_DIRECTORY
local isdir = tree:GetItemImage(itemsrc) == image.DIRECTORY
local isnew = tree:GetItemText(itemsrc) == empty
local source = tree:GetItemFullName(itemsrc)
local fn = wx.wxFileName(target)
@@ -252,7 +254,7 @@ local function treeSetConnectorsAndIcons(tree)
LoadFile(fullpath:gsub(q(source), target), doc.editor)
end
else -- refresh the tree and select the new item
local itemdst = findItem(tree, target)
local itemdst = tree:FindItem(target)
if itemdst then
refreshAncestors(tree:GetItemParent(itemdst))
tree:SelectItem(itemdst)
@@ -263,17 +265,20 @@ local function treeSetConnectorsAndIcons(tree)
return true
end
local function deleteItem(item_id)
local isdir = tree:GetItemImage(item_id) == IMG_DIRECTORY
local isdir = tree:GetItemImage(item_id) == image.DIRECTORY
local source = tree:GetItemFullName(item_id)
if isdir and FileSysHasContent(source..pathsep) then return false end
if isdir and FileDirHasContent(source..pathsep) then return false end
if wx.wxMessageBox(
TR("Do you want to delete '%s'?"):format(source),
GetIDEString("editormessage"),
wx.wxYES_NO + wx.wxCENTRE, ide.frame) ~= wx.wxYES then return false end
if isdir then
wx.wxRmdir(source)
if not wx.wxRmdir(source) then
ReportError(TR("Unable to delete directory '%s': %s")
:format(source, wx.wxSysErrorMsg()))
end
else
local doc = ide:FindDocument(source)
if doc then ClosePage(doc.index) end
@@ -297,12 +302,12 @@ local function treeSetConnectorsAndIcons(tree)
end)
-- handle context menu
local function addItem(item_id, name, image)
local isdir = tree:GetItemImage(item_id) == IMG_DIRECTORY
local function addItem(item_id, name, img)
local isdir = tree:GetItemImage(item_id) == image.DIRECTORY
local parent = isdir and item_id or tree:GetItemParent(item_id)
if isdir then tree:Expand(item_id) end -- expand to populate if needed
local item = tree:PrependItem(parent, name, image)
local item = tree:PrependItem(parent, name, img)
tree:SetItemHasChildren(parent, true)
-- temporarily disable expand as we don't need this node populated
tree:SetEvtHandlerEnabled(false)
@@ -313,11 +318,11 @@ local function treeSetConnectorsAndIcons(tree)
tree:Connect(ID_NEWFILE, wx.wxEVT_COMMAND_MENU_SELECTED,
function()
tree:EditLabel(addItem(tree:GetSelection(), empty, IMG_FILE_OTHER))
tree:EditLabel(addItem(tree:GetSelection(), empty, image.FILEOTHER))
end)
tree:Connect(ID_NEWDIRECTORY, wx.wxEVT_COMMAND_MENU_SELECTED,
function()
tree:EditLabel(addItem(tree:GetSelection(), empty, IMG_DIRECTORY))
tree:EditLabel(addItem(tree:GetSelection(), empty, image.DIRECTORY))
end)
tree:Connect(ID_RENAMEFILE, wx.wxEVT_COMMAND_MENU_SELECTED,
function() tree:EditLabel(tree:GetSelection()) end)
@@ -385,10 +390,10 @@ local function treeSetConnectorsAndIcons(tree)
FileTreeProjectListUpdate(projectdirectorymenu, 0)
-- disable Delete on non-empty directories
local isdir = tree:GetItemImage(item_id) == IMG_DIRECTORY
local isdir = tree:GetItemImage(item_id) == image.DIRECTORY
if isdir then
local source = tree:GetItemFullName(item_id)
menu:Enable(ID_DELETEFILE, not FileSysHasContent(source..pathsep))
menu:Enable(ID_DELETEFILE, not FileDirHasContent(source..pathsep))
menu:Enable(ID_OPENEXTENSION, false)
else
local fname = tree:GetItemText(item_id)
@@ -425,7 +430,7 @@ local function treeSetConnectorsAndIcons(tree)
end
if item_id and bit.band(flags, mask) > 0 then
if tree:GetItemImage(item_id) == IMG_DIRECTORY then
if tree:GetItemImage(item_id) == image.DIRECTORY then
tree:Toggle(item_id)
tree:SelectItem(item_id)
else
@@ -474,7 +479,7 @@ local function treeSetConnectorsAndIcons(tree)
local itemsrc
tree:Connect(wx.wxEVT_COMMAND_TREE_BEGIN_DRAG,
function (event)
if tree:GetItemParent(event:GetItem()):IsOk() then
if ide.config.filetree.mousemove and tree:GetItemParent(event:GetItem()):IsOk() then
itemsrc = event:GetItem()
event:Allow()
end
@@ -595,6 +600,9 @@ function FileTreeProjectListClear()
end
function FileTreeProjectListUpdate(menu, items)
-- protect against recent project menu not being present
if not ide:FindMenuItem(ID_RECENTPROJECTS) then return end
local list = getProjectLabels()
for i=#list, 1, -1 do
local id = ID("file.recentprojects."..i)
@@ -622,7 +630,7 @@ local curr_file
function FileTreeMarkSelected(file)
if not file or not filetree.projdir or #filetree.projdir == 0 then return end
local item_id = findItem(projtree, file)
local item_id = projtree:FindItem(file)
-- if the select item is different from the current one
-- or the current one is the same, but not bold (which may happen when
@@ -630,7 +638,7 @@ function FileTreeMarkSelected(file)
if curr_file ~= file
or item_id and not projtree:IsBold(item_id) then
if curr_file then
local curr_id = findItem(projtree, curr_file)
local curr_id = projtree:FindItem(curr_file)
if curr_id and projtree:IsBold(curr_id) then
projtree:SetItemBold(curr_id, false)
end

View File

@@ -43,8 +43,12 @@ ide.findReplace = {
}
local findReplace = ide.findReplace
local lastEditor
function findReplace:GetEditor()
return findReplace.oveditor or GetEditor()
lastEditor = findReplace.oveditor or GetEditorWithFocus() or lastEditor
-- last editor may already be "userdata" instead of a Scintilla object,
-- so check if this is still a valid wxSTC object
return pcall(function() lastEditor:GetId() end) and lastEditor or GetEditor()
end
-------------------- Find replace dialog
@@ -145,7 +149,7 @@ function findReplace:FindString(reverse)
findReplace.foundString = true
local start = editor:GetTargetStart()
local finish = editor:GetTargetEnd()
EnsureRangeVisible(start, finish)
editor:EnsureVisibleEnforcePolicy(editor:LineFromPosition(start))
editor:SetSelection(start, finish)
ide.frame:SetStatusText("")
end
@@ -190,6 +194,9 @@ function findReplace:ReplaceString(fReplaceAll, inFileRegister)
if findReplace:HasText() then
local editor = findReplace:GetEditor()
-- don't replace in read-only editors
if editor:GetReadOnly() then return false end
local endTarget = inFileRegister and setTargetAll(editor) or
setTarget(editor, findReplace.fDown, fReplaceAll, findReplace.fWrap)
@@ -292,7 +299,7 @@ local function ProcInFiles(startdir,mask,subdirs,replace)
end
-- give time to the UI to refresh
if TimeGet() - start > 0.25 then wx.wxYield() end
if TimeGet() - start > 0.25 then ide:Yield() end
if not findReplace.dialog:IsShown() then
DisplayOutputLn(TR("Cancelled by the user."))
break

View File

@@ -4,6 +4,7 @@
---------------------------------------------------------
local ide = ide
local unpack = table.unpack or unpack
-- Pick some reasonable fixed width fonts to use for the editor
local function setFont(style, config)
@@ -79,6 +80,13 @@ local function menuDropDownPosition(event)
return ide.frame:ScreenToClient(tb:ClientToScreen(rect:GetBottomLeft()))
end
local function tbIconSize()
local iconsize = (tonumber(ide.config.toolbar and ide.config.toolbar.iconsize)
or (ide.osname == 'Macintosh' and 24 or 16))
if iconsize ~= 24 then iconsize = 16 end
return iconsize
end
local function createToolBar(frame)
local toolBar = wxaui.wxAuiToolBar(frame, wx.wxID_ANY, wx.wxDefaultPosition, wx.wxDefaultSize,
wxaui.wxAUI_TB_PLAIN_BACKGROUND)
@@ -87,34 +95,28 @@ local function createToolBar(frame)
wx.wxDefaultPosition, wx.wxSize.new(240, ide.osname == 'Unix' and 28 or 24))
-- there are two sets of icons: use 24 on OSX and 16 on others.
local toolBmpSize = (
ide.osname == 'Macintosh' and wx.wxSize(24, 24) or wx.wxSize(16, 16))
local iconsize = tbIconSize()
local toolBmpSize = wx.wxSize(iconsize, iconsize)
local getBitmap = (ide.app.createbitmap or wx.wxArtProvider.GetBitmap)
toolBar:AddTool(ID_NEW, "New", getBitmap(wx.wxART_NORMAL_FILE, wx.wxART_TOOLBAR, toolBmpSize), TR("Create an empty document")..SCinB(ID_NEW))
toolBar:AddTool(ID_OPEN, "Open", getBitmap(wx.wxART_FILE_OPEN, wx.wxART_TOOLBAR, toolBmpSize), TR("Open an existing document")..SCinB(ID_OPEN))
toolBar:AddTool(ID_SAVE, "Save", getBitmap(wx.wxART_FILE_SAVE, wx.wxART_TOOLBAR, toolBmpSize), TR("Save the current document")..SCinB(ID_SAVE))
toolBar:AddTool(ID_SAVEALL, "Save All", getBitmap(wx.wxART_NEW_DIR, wx.wxART_TOOLBAR, toolBmpSize), TR("Save all open documents")..SCinB(ID_SAVEALL))
toolBar:AddTool(ID_PROJECTDIRFROMFILE, "Update", getBitmap(wx.wxART_GO_DIR_UP, wx.wxART_TOOLBAR, toolBmpSize), TR("Set project directory from current file")..SCinB(ID_PROJECTDIRFROMFILE))
toolBar:AddTool(ID_PROJECTDIRCHOOSE, "Choose", getBitmap("wxART_DIR_SETUP", wx.wxART_TOOLBAR, toolBmpSize), TR("Choose a project directory")..SCinB(ID_PROJECTDIRCHOOSE))
toolBar:AddSeparator()
toolBar:AddTool(ID_FIND, "Find", getBitmap(wx.wxART_FIND, wx.wxART_TOOLBAR, toolBmpSize), TR("Find text")..SCinB(ID_FIND))
toolBar:AddTool(ID_REPLACE, "Replace", getBitmap(wx.wxART_FIND_AND_REPLACE, wx.wxART_TOOLBAR, toolBmpSize), TR("Find and replace text")..SCinB(ID_REPLACE))
if ide.app.createbitmap then -- custom handler should handle all bitmaps
toolBar:AddSeparator()
toolBar:AddTool(ID_STARTDEBUG, "Start Debugging", getBitmap("wxART_DEBUG_START", wx.wxART_TOOLBAR, toolBmpSize), TR("Start or Continue debugging")..SCinB(ID_STARTDEBUG))
toolBar:AddTool(ID_STOPDEBUG, "Stop Debugging", getBitmap("wxART_DEBUG_STOP", wx.wxART_TOOLBAR, toolBmpSize), TR("Stop the currently running process")..SCinB(ID_STOPDEBUG))
toolBar:AddTool(ID_DETACHDEBUG, "Detach Process", getBitmap("wxART_DEBUG_DETACH", wx.wxART_TOOLBAR, toolBmpSize), TR("Stop debugging and continue running the process")..SCinB(ID_DETACHDEBUG))
toolBar:AddTool(ID_BREAK, "Break", getBitmap("wxART_DEBUG_BREAK", wx.wxART_TOOLBAR, toolBmpSize), TR("Break execution at the next executed line of code")..SCinB(ID_BREAK))
toolBar:AddTool(ID_STEP, "Step into", getBitmap("wxART_DEBUG_STEP_INTO", wx.wxART_TOOLBAR, toolBmpSize), TR("Step into")..SCinB(ID_STEP))
toolBar:AddTool(ID_STEPOVER, "Step over", getBitmap("wxART_DEBUG_STEP_OVER", wx.wxART_TOOLBAR, toolBmpSize), TR("Step over")..SCinB(ID_STEPOVER))
toolBar:AddTool(ID_STEPOUT, "Step out", getBitmap("wxART_DEBUG_STEP_OUT", wx.wxART_TOOLBAR, toolBmpSize), TR("Step out of the current function")..SCinB(ID_STEPOUT))
toolBar:AddSeparator()
toolBar:AddTool(ID_TOGGLEBREAKPOINT, "Toggle breakpoint", getBitmap("wxART_DEBUG_BREAKPOINT_TOGGLE", wx.wxART_TOOLBAR, toolBmpSize), TR("Toggle breakpoint")..SCinB(ID_TOGGLEBREAKPOINT))
toolBar:AddTool(ID_BOOKMARKTOGGLE, "Toggle bookmark", getBitmap("wxART_BOOKMARK_TOGGLE", wx.wxART_TOOLBAR, toolBmpSize), TR("Toggle bookmark")..SCinB(ID_BOOKMARKTOGGLE))
toolBar:AddTool(ID_VIEWCALLSTACK, "Stack window", getBitmap("wxART_DEBUG_CALLSTACK", wx.wxART_TOOLBAR, toolBmpSize), TR("View the stack window")..SCinB(ID_VIEWCALLSTACK))
toolBar:AddTool(ID_VIEWWATCHWINDOW, "Watch window", getBitmap("wxART_DEBUG_WATCH", wx.wxART_TOOLBAR, toolBmpSize), TR("View the watch window")..SCinB(ID_VIEWWATCHWINDOW))
local icons, prev = ide.config.toolbar.icons
for _, id in ipairs(icons) do
if icons[id] ~= false then -- skip explicitly disabled icons
if id == ID_SEPARATOR then
-- make sure that there are no two separators next to each other;
-- this may happen when some of the icons are disabled.
if prev ~= ID_SEPARATOR then toolBar:AddSeparator() end
else
local iconmap = ide.config.toolbar.iconmap[id]
if iconmap then
local icon, description = unpack(iconmap)
local isbitmap = type(icon) == "userdata" and icon:GetClassInfo():GetClassName() == "wxBitmap"
local bitmap = isbitmap and icon or getBitmap(icon, "TOOLBAR", toolBmpSize)
toolBar:AddTool(id, "", bitmap, TR(description)..SCinB(id))
end
end
prev = id
end
end
toolBar:AddSeparator()
toolBar:AddControl(funclist)
toolBar:SetToolDropDown(ID_OPEN, true)
@@ -471,7 +473,6 @@ do
mgr:AddPane(frame.toolBar, wxaui.wxAuiPaneInfo():
Name("toolbar"):Caption("Toolbar"):
MinSize(300,16):FloatingSize(800,48):
ToolbarPane():Top():CloseButton(false):PaneBorder(false):
LeftDockable(false):RightDockable(false))
mgr:AddPane(frame.notebook, wxaui.wxAuiPaneInfo():

View File

@@ -14,6 +14,7 @@ end
-- so don't use stock IDs on Linux
local linux = ide.osname == 'Unix'
ID_SEPARATOR = NewID()
-- File menu
ID_NEW = linux and NewID() or wx.wxID_NEW
ID_OPEN = linux and NewID() or wx.wxID_OPEN
@@ -123,6 +124,7 @@ ID_DELETEWATCH = NewID()
-- Editor popup menu items
ID_GOTODEFINITION = NewID()
ID_RENAMEALLINSTANCES = NewID()
ID_REPLACEALLSELECTIONS = NewID()
ID_QUICKADDWATCH = NewID()
ID_QUICKEVAL = NewID()
ID_ADDTOSCRATCHPAD = NewID()

View File

@@ -47,20 +47,18 @@ function M.warnings_from_string(src, file)
return M.show_warnings(ast, globinit)
end
local function cleanError(err)
return err and err:gsub(".-:%d+: file%s+",""):gsub(", line (%d+), char %d+", ":%1")
end
function AnalyzeFile(file)
local warn, err, line, pos = M.warnings_from_string(FileRead(file), file)
if err then
err = err:gsub("line %d+, char %d+", "syntax error")
end
return warn, err, line, pos
return warn, cleanError(err), line, pos
end
function AnalyzeString(src)
local warn, err, line, pos = M.warnings_from_string(src, "src")
if err then
err = err:gsub("line %d+, char %d+", "syntax error")
end
return warn, err, line, pos
local warn, err, line, pos = M.warnings_from_string(src, "<string>")
return warn, cleanError(err), line, pos
end
function M.show_warnings(top_ast, globinit)
@@ -178,10 +176,9 @@ function M.show_warnings(top_ast, globinit)
end
local frame = ide.frame
local menu = frame.menuBar:GetMenu(frame.menuBar:FindMenu(TR("&Project")))
-- insert after "Compile" item
local _, compilepos = ide:FindMenuItem(menu, ID_COMPILE)
local _, menu, compilepos = ide:FindMenuItem(ID_COMPILE)
if compilepos then
menu:Insert(compilepos+1, ID_ANALYZE, TR("Analyze")..KSC(ID_ANALYZE), TR("Analyze the source code"))
end
@@ -190,17 +187,16 @@ local debugger = ide.debugger
local openDocuments = ide.openDocuments
local function analyzeProgram(editor)
local editorText = editor:GetText()
local id = editor:GetId()
local filePath = DebuggerMakeFileName(editor, openDocuments[id].filePath)
if frame.menuBar:IsChecked(ID_CLEAROUTPUT) then ClearOutput() end
if ide:GetMenuBar():IsChecked(ID_CLEAROUTPUT) then ClearOutput() end
DisplayOutput("Analyzing the source code")
frame:Update()
local editorText = editor:GetText()
local doc = ide:GetDocument(editor)
local filePath = doc:GetFilePath() or doc:GetFileName()
local warn, err = M.warnings_from_string(editorText, filePath)
if err then -- report compilation error
DisplayOutput(": not completed\n")
DisplayOutput((": not completed.\n%s\n"):format(cleanError(err)))
return false
end
@@ -215,7 +211,9 @@ frame:Connect(ID_ANALYZE, wx.wxEVT_COMMAND_MENU_SELECTED,
function ()
ActivateOutput()
local editor = GetEditor()
if not analyzeProgram(editor) then CompileProgram(editor, { reportstats = false }) end
if not analyzeProgram(editor) then
CompileProgram(editor, { reportstats = false, keepoutput = true })
end
end)
frame:Connect(ID_ANALYZE, wx.wxEVT_UPDATE_UI,
function (event)

View File

@@ -126,7 +126,4 @@ ide.config.editor.keymap = {
-- Opt+Left/Right moves one word left (to the beginning)/right (to the end)
{wxstc.wxSTC_KEY_LEFT, wxstc.wxSTC_SCMOD_ALT, wxstc.wxSTC_CMD_WORDLEFT, "Macintosh"},
{wxstc.wxSTC_KEY_RIGHT, wxstc.wxSTC_SCMOD_ALT, wxstc.wxSTC_CMD_WORDRIGHTEND, "Macintosh"},
-- Opt+Shift+Left/Right selects one word left (to the beginning)/right (to the end)
{wxstc.wxSTC_KEY_LEFT, wxstc.wxSTC_SCMOD_ALT+wxstc.wxSTC_SCMOD_SHIFT, wxstc.wxSTC_CMD_WORDLEFTEXTEND, "Macintosh"},
{wxstc.wxSTC_KEY_RIGHT, wxstc.wxSTC_SCMOD_ALT+wxstc.wxSTC_SCMOD_SHIFT, wxstc.wxSTC_CMD_WORDRIGHTENDEXTEND, "Macintosh"},
}

View File

@@ -76,6 +76,19 @@ local function onEditMenu(event)
end
local menu_id = event:GetId()
local copytext
if (menu_id == ID_CUT or menu_id == ID_COPY)
and ide.wxver >= "2.9.5" and editor:GetSelections() > 1 then
local main = editor:GetMainSelection()
copytext = editor:GetTextRange(editor:GetSelectionNStart(main), editor:GetSelectionNEnd(main))
for s = 0, editor:GetSelections()-1 do
if copytext ~= editor:GetTextRange(editor:GetSelectionNStart(s), editor:GetSelectionNEnd(s)) then
copytext = nil
break
end
end
end
if menu_id == ID_CUT then
if editor:GetSelectionStart() == editor:GetSelectionEnd()
then editor:LineCut() else editor:Cut() end
@@ -87,6 +100,8 @@ local function onEditMenu(event)
elseif menu_id == ID_UNDO then editor:Undo()
elseif menu_id == ID_REDO then editor:Redo()
end
if copytext then editor:CopyText(#copytext, copytext) end
end
for _, event in pairs({ID_CUT, ID_COPY, ID_PASTE, ID_SELECTALL, ID_UNDO, ID_REDO}) do
@@ -225,7 +240,8 @@ frame:Connect(ID_COMMENT, wx.wxEVT_COMMAND_MENU_SELECTED,
local function processSelection(editor, func)
local text = editor:GetSelectedText()
local pos = editor:GetCurrentPos()
local line = editor:GetCurrentLine()
local posinline = editor:GetCurrentPos() - editor:PositionFromLine(line)
if #text == 0 then
editor:SelectAll()
text = editor:GetSelectedText()
@@ -250,7 +266,8 @@ local function processSelection(editor, func)
editor:ReplaceTarget(newtext)
end
end
editor:GotoPos(pos)
editor:GotoPosEnforcePolicy(math.min(
editor:PositionFromLine(line)+posinline, editor:GetLineEndPosition(line)))
end
frame:Connect(ID_SORT, wx.wxEVT_COMMAND_MENU_SELECTED,
@@ -274,28 +291,36 @@ local function reIndent(editor, buf)
local tw = ut and editor:GetTabWidth() or editor:GetIndent()
local indents = {}
local isstatic = {}
for line = 1, #buf+1 do
local closed, blockend = decindent(text)
local opened = incindent(text)
local style = bit.band(editor:GetStyleAt(editor:PositionFromLine(line-1)), 31)
-- don't reformat multi-line comments or strings
isstatic[line] = editor.spec.iscomment[style] or editor.spec.isstring[style]
if not isstatic[line] or line == 1 or not isstatic[line-1] then
local closed, blockend = decindent(text)
local opened = incindent(text)
-- ignore impact from initial block endings as they are already indented
if line == 1 then blockend = 0 end
-- ignore impact from initial block endings as they are already indented
if line == 1 then blockend = 0 end
-- this only needs to be done for 2, #buf+1; do it and get out when done
if line > 1 then indents[line-1] = indents[line-1] - tw * closed end
if line > #buf then break end
-- this only needs to be done for 2, #buf+1; do it and get out when done
if line > 1 then indents[line-1] = indents[line-1] - tw * closed end
if line > #buf then break end
indent = indent + tw * (opened - blockend)
if indent < 0 then indent = 0 end
indent = indent + tw * (opened - blockend)
if indent < 0 then indent = 0 end
end
indents[line] = indent
text = buf[line]
end
for line = 1, #buf do
buf[line] = buf[line]:gsub("^[ \t]*",
not buf[line]:match('%S') and ''
or ut and ("\t"):rep(indents[line] / tw) or (" "):rep(indents[line]))
if not isstatic[line] then
buf[line] = buf[line]:gsub("^[ \t]*",
not buf[line]:match('%S') and ''
or ut and ("\t"):rep(indents[line] / tw) or (" "):rep(indents[line]))
end
end
end

View File

@@ -131,6 +131,9 @@ do -- recent file history
local items = 0
updateRecentFiles = function (list)
-- protect against recent files menu not being present
if not ide:FindMenuItem(ID_RECENTFILES) then return end
for i=1, #list do
local file = list[i].filename
local id = ID("file.recentfiles."..i)
@@ -227,7 +230,6 @@ frame:Connect(ID_CLOSE, wx.wxEVT_UPDATE_UI,
frame:Connect(ID_EXIT, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
if not SaveOnExit(true) then return end
frame:Close() -- this will trigger wxEVT_CLOSE_WINDOW
end)
@@ -245,6 +247,7 @@ local recentprojects = 0
frame:Connect(ID_RECENTPROJECTS, wx.wxEVT_UPDATE_UI,
function (event)
recentprojects = FileTreeProjectListUpdate(projecthistorymenu, recentprojects)
if not recentprojects then return end
local pos = 1 -- add shortcut for the previous project (if any)
if recentprojects > pos then
local item = projecthistorymenu:FindItemByPosition(pos)

View File

@@ -285,11 +285,11 @@ frame:Connect(ID_RUN, wx.wxEVT_UPDATE_UI,
frame:Connect(ID_RUNNOW, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
if event:IsChecked() then
if not DebuggerScratchpadOn(GetEditor()) then
menuBar:Check(ID_RUNNOW, false) -- disable if couldn't start scratchpad
end
else DebuggerScratchpadOff() end
if debugger.scratchpad then
DebuggerScratchpadOff()
else
DebuggerScratchpadOn(GetEditor())
end
end)
frame:Connect(ID_RUNNOW, wx.wxEVT_UPDATE_UI,
function (event)
@@ -300,6 +300,13 @@ frame:Connect(ID_RUNNOW, wx.wxEVT_UPDATE_UI,
(ide.interpreter.scratchextloop ~= nil) and -- nil == no scratchpad support
(editor ~= nil) and ((debugger.server == nil or debugger.scratchable)
and debugger.pid == nil or debugger.scratchpad ~= nil))
local isscratchpad = debugger.scratchpad ~= nil
menuBar:Check(ID_RUNNOW, isscratchpad)
local tool = ide:GetToolBar():FindTool(ID_RUNNOW)
if tool and tool:IsSticky() ~= isscratchpad then
tool:SetSticky(isscratchpad)
ide:GetToolBar():Refresh()
end
end)
frame:Connect(ID_ATTACHDEBUG, wx.wxEVT_COMMAND_MENU_SELECTED,
@@ -422,5 +429,6 @@ frame:Connect(wx.wxEVT_IDLE,
if (debugger.scratchpad) then DebuggerRefreshScratchpad() end
if IndicateIfNeeded() then event:RequestMore(true) end
PackageEventHandleOnce("onIdleOnce", event)
PackageEventHandle("onIdle", event)
event:Skip() -- let other EVT_IDLE handlers to work on the event
end)

View File

@@ -340,42 +340,41 @@ errorlog:Connect(wxstc.wxEVT_STC_DOUBLECLICK,
if (fname and jumpline) then break end
end
if (fname and jumpline) then
-- fname may include name of executable, as in "path/to/lua: file.lua";
-- strip it and try to find match again if needed.
-- try the stripped name first as if it doesn't match, the longer
-- name may have parts that may be interpreter as network path and
-- may take few seconds to check.
local name
local fixedname = fname:match(":%s+(.+)")
if fixedname then
name = GetFullPathIfExists(FileTreeGetDir(), fixedname)
or FileTreeFindByPartialName(fixedname)
end
name = name
or GetFullPathIfExists(FileTreeGetDir(), fname)
or FileTreeFindByPartialName(fname)
if not (fname and jumpline) then return end
local editor = LoadFile(name or fname,nil,true)
if not editor then
local ed = GetEditor()
if ed and ide:GetDocument(ed):GetFileName() == (name or fname) then
editor = ed
end
end
if editor then
jumpline = tonumber(jumpline)
jumplinepos = tonumber(jumplinepos)
-- fname may include name of executable, as in "path/to/lua: file.lua";
-- strip it and try to find match again if needed.
-- try the stripped name first as if it doesn't match, the longer
-- name may have parts that may be interpreter as network path and
-- may take few seconds to check.
local name
local fixedname = fname:match(":%s+(.+)")
if fixedname then
name = GetFullPathIfExists(FileTreeGetDir(), fixedname)
or FileTreeFindByPartialName(fixedname)
end
name = name
or GetFullPathIfExists(FileTreeGetDir(), fname)
or FileTreeFindByPartialName(fname)
editor:GotoPos(editor:PositionFromLine(math.max(0,jumpline-1))
+ (jumplinepos and (math.max(0,jumplinepos-1)) or 0))
editor:EnsureVisibleEnforcePolicy(jumpline)
editor:SetFocus()
local editor = LoadFile(name or fname,nil,true)
if not editor then
local ed = GetEditor()
if ed and ide:GetDocument(ed):GetFileName() == (name or fname) then
editor = ed
end
end
if editor then
jumpline = tonumber(jumpline)
jumplinepos = tonumber(jumplinepos)
-- doubleclick can set selection, so reset it;
-- for consistency, do it even when no pattern is detected.
editor:GotoPos(editor:PositionFromLine(math.max(0,jumpline-1))
+ (jumplinepos and (math.max(0,jumplinepos-1)) or 0))
editor:EnsureVisibleEnforcePolicy(jumpline)
editor:SetFocus()
end
-- doubleclick can set selection, so reset it
local pos = event:GetPosition()
if pos == -1 then pos = errorlog:GetLineEndPosition(event:GetLine()) end
errorlog:SetSelection(pos, pos)

View File

@@ -79,16 +79,23 @@ function ide:GetStatusBar() return self.frame.statusBar end
function ide:GetToolBar() return self.frame.toolBar end
function ide:GetDebugger() return self.debugger end
function ide:GetMainFrame() return self.frame end
function ide:GetUIManager() return self.frame.uimgr end
function ide:GetDocument(ed) return self.openDocuments[ed:GetId()] end
function ide:GetDocuments() return self.openDocuments end
function ide:FindMenuItem(menu, itemid)
function ide:FindMenuItem(itemid, menu)
local item, imenu = ide:GetMenuBar():FindItem(itemid, menu)
if menu and not item then item = menu:FindItem(itemid) end
if not item then return end
menu = menu or imenu
for pos = 0, menu:GetMenuItemCount()-1 do
if menu:FindItemByPosition(pos):GetId() == itemid then
return menu:FindItemByPosition(pos), pos
return item, menu, pos
end
end
return nil
return
end
function ide:FindDocument(path)
local fileName = wx.wxFileName(path)
for _, doc in pairs(ide.openDocuments) do
@@ -124,7 +131,8 @@ function ide:GetProject() return FileTreeGetDir() end
function ide:GetLaunchedProcess() return self.debugger and self.debugger.pid end
function ide:GetProjectTree() return ide.filetree.projtree end
function ide:GetWatch() return self.debugger and self.debugger.watchCtrl end
function ide:GetStack() return self.debugger and self.debugger.watchCtrl end
function ide:GetStack() return self.debugger and self.debugger.stackCtrl end
function ide:Yield() wx.wxYield() end
function ide:GetSetting(path, setting)
local settings = self.settings
@@ -135,6 +143,52 @@ function ide:GetSetting(path, setting)
return ok and value or nil
end
function ide:RemoveMenuItem(id, menu)
local _, menu, pos = ide:FindMenuItem(id, menu)
if menu then
menu:Disconnect(id, wx.wxID_ANY, wx.wxEVT_COMMAND_MENU_SELECTED)
menu:Disconnect(id, wx.wxID_ANY, wx.wxEVT_UPDATE_UI)
menu:Remove(id)
local positem = menu:FindItemByPosition(pos)
if (not positem or positem:GetKind() == wx.wxITEM_SEPARATOR)
and (menu:FindItemByPosition(pos-1):GetKind() == wx.wxITEM_SEPARATOR) then
menu:Destroy(menu:FindItemByPosition(pos-1))
end
return true
end
return false
end
function ide:AddWatch(watch, value)
local mgr = ide.frame.uimgr
local pane = mgr:GetPane("watchpanel")
if (pane:IsOk() and not pane:IsShown()) then
pane:Show()
mgr:Update()
end
local watchCtrl = ide.debugger.watchCtrl
if not watchCtrl then return end
local root = watchCtrl:GetRootItem()
if not root or not root:IsOk() then return end
local item = watchCtrl:GetFirstChild(root)
while true do
if not item:IsOk() then break end
if watchCtrl:GetItemExpression(item) == watch then
if value then watchCtrl:SetItemText(item, watch .. ' = ' .. tostring(value)) end
return item
end
item = watchCtrl:GetNextSibling(item)
end
local item = watchCtrl:AppendItem(root, watch, 1)
watchCtrl:SetItemExpression(item, watch, value)
return item
end
function ide:AddInterpreter(name, interpreter)
self.interpreters[name] = setmetatable(interpreter, ide.proto.Interpreter)
UpdateInterpreters()
@@ -181,3 +235,25 @@ function ide:RemoveConfig(name)
configcache[name] = nil -- clear the slot after use
ReApplySpecAndStyles() -- apply current config to the UI
end
function ide:AddPanel(ctrl, panel, name, conf)
local width, height = 360, 200
local notebook = wxaui.wxAuiNotebook(ide.frame, wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxDefaultSize,
wxaui.wxAUI_NB_DEFAULT_STYLE + wxaui.wxAUI_NB_TAB_EXTERNAL_MOVE
- wxaui.wxAUI_NB_CLOSE_ON_ACTIVE_TAB + wx.wxNO_BORDER)
notebook:AddPage(ctrl, name, true)
notebook:Connect(wxaui.wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK,
function() PaneFloatToggle(notebook) end)
local mgr = ide.frame.uimgr
mgr:AddPane(notebook, wxaui.wxAuiPaneInfo():
Name(panel):Float():CaptionVisible(false):PaneBorder(false):
MinSize(width/2,height/2):
BestSize(width,height):FloatingSize(width,height):
PinButton(true):Hide())
if type(conf) == "function" then conf(mgr:GetPane(panel)) end
mgr.defaultPerspective = mgr:SavePerspective() -- resave default perspective
return notebook
end

View File

@@ -4,6 +4,7 @@
ide.proto.Document = {__index = {
GetFileName = function(self) return self.fileName end,
GetFilePath = function(self) return self.filePath end,
GetFileExt = function(self) return GetFileExt(self.fileName) end,
GetModTime = function(self) return self.modTime end,
GetEditor = function(self) return self.editor end,
GetTabIndex = function(self) return self.index end,
@@ -28,4 +29,6 @@ ide.proto.Interpreter = {__index = {
ide.proto.Debugger = {__index = {
IsRunning = function(self) return self.running end,
IsConnected = function(self) return self.server end,
GetHostName = function(self) return self.hostname end,
GetPortNumber = function(self) return self.portnumber end,
}}

View File

@@ -8,7 +8,8 @@ local ide = ide
-- ----------------------------------------------------------------------------
-- Initialize the wxConfig for loading/saving the preferences
local settings = wx.wxFileConfig(GetIDEString("settingsapp"),GetIDEString("settingsvendor"))
local settings = wx.wxFileConfig(GetIDEString("settingsapp"),
GetIDEString("settingsvendor"), ide.config.ini or "")
ide.settings = settings
local function settingsReadSafe(settings,what,default)
@@ -391,7 +392,18 @@ function SettingsRestoreView()
local layoutcur = uimgr:SavePerspective()
local layout = settingsReadSafe(settings,"uimgrlayout",layoutcur)
if (layout ~= layoutcur) then
-- save the current toolbar configuration and restore re-apply it
-- as it's always correct (to avoid storing minh and minw values)
local toolbar = frame.uimgr:GetPane("toolbar")
local toolbarlayout = (toolbar:IsOk()
-- layout includes bestw that is only as wide as the toolbar size,
-- this leaves default background on the right side of the toolbar;
-- fix it by explicitly replacing with the screen width
and uimgr:SavePaneInfo(toolbar):gsub("(bestw=)[^;]+",
function(s) return s..wx.wxSystemSettings.GetMetric(wx.wxSYS_SCREEN_X) end)
or nil)
uimgr:LoadPerspective(layout, false)
if toolbarlayout then uimgr:LoadPaneInfo(toolbarlayout, toolbar) end
-- check if debugging panes are not mentioned and float them
for _, name in pairs({"stackpanel", "watchpanel"}) do
@@ -442,6 +454,24 @@ function SettingsRestoreView()
end
end
-- restore configuration for notebook pages that have been split;
-- load saved dock_size values and update current values with saved ones
-- where dock_size configuration matches
local docksizemask = '(dock_size[^=]+=)(%d+)'
for l, m in pairs({
nbdocklayout = frame.notebook:GetAuiManager(),
nbbtmdocklayout = frame.bottomnotebook:GetAuiManager(),
}) do
-- ...|dock_size(5,0,0)=20|dock_size(2,1,0)=200|...
local prevlayout = settingsReadSafe(settings, l, "")
local curlayout = m:SavePerspective()
local newlayout = curlayout:gsub('(dock_size[^=]+=)(%d+)', function(t,v)
local val = prevlayout:match(EscapeMagic(t)..'(%d+)')
return t..(val or v)
end)
if newlayout ~= curlayour then m:LoadPerspective(newlayout) end
end
local editor = GetEditor()
if editor then editor:SetFocus() end
@@ -457,10 +487,12 @@ function SettingsSaveView()
local frame = ide.frame
local uimgr = frame.uimgr
settings:Write("uimgrlayout",uimgr:SavePerspective())
settings:Write("nblayout", saveNotebook(frame.notebook))
settings:Write("nbbtmlayout",saveNotebook(frame.bottomnotebook))
settings:Write("statusbar",frame:GetStatusBar():IsShown())
settings:Write("uimgrlayout", uimgr:SavePerspective())
settings:Write("nblayout", saveNotebook(frame.notebook))
settings:Write("nbbtmlayout", saveNotebook(frame.bottomnotebook))
settings:Write("statusbar", frame:GetStatusBar():IsShown())
settings:Write("nbdocklayout", frame.notebook:GetAuiManager():SavePerspective())
settings:Write("nbbtmdocklayout", frame.bottomnotebook:GetAuiManager():SavePerspective())
settings:SetPath(path)
end

View File

@@ -17,14 +17,11 @@ probably a pitfal: an instance is running but is not visible
if not ide.config.singleinstance then return end
require "socket"
local socket = require "socket"
local port = ide.config.singleinstanceport
local delay = tonumber(ide.config.singleinstance) or 1000 -- in ms
local svr = socket.udp()
local success, errmsg = svr:setsockname("127.0.0.1",port) -- bind on local host
local success = svr:setsockname("127.0.0.1",port) -- bind on local host
local protocol = {client = {}, server = {}}
protocol.client.greeting = "Is this you, my IDE? It's me, a new instance."
@@ -37,15 +34,14 @@ if success then -- ok, server was started, we are solo
ide.idletimer = wx.wxTimer(wx.wxGetApp())
ide.idletimer:Start(delay,false)
svr:settimeout(0) -- don't block
wx.wxGetApp():Connect(wx.wxEVT_TIMER,function (evt)
wx.wxGetApp():Connect(wx.wxEVT_TIMER, function()
if ide.exitingProgram then -- if exiting, terminate the timer loop
wx.wxGetApp():Disconnect(wx.wxEVT_TIMER)
return
end
local msg, err, port = svr:receivefrom() -- receive a msg
local msg, ip, port = svr:receivefrom() -- receive a msg
if msg then
local ip = err -- the errmsg is actually the IP
if msg == protocol.client.greeting then -- just send back hi
svr:sendto(protocol.server.greeting,ip,port)
elseif msg:match(protocol.client.requestloading:gsub("%%s",".+$")) then -- ok we need to open something
@@ -53,16 +49,12 @@ if success then -- ok, server was started, we are solo
local filename = msg:match(protocol.client.requestloading:gsub("%%s","(.+)$"))
if filename then
RequestAttention()
local done = true
if wx.wxDirExists(filename) then
local dir = wx.wxFileName.DirName(filename)
dir:Normalize() -- turn into absolute path if needed
ProjectUpdateProjectDir(dir:GetFullPath())
else
done = LoadFile(filename, nil, true)
end
if not done then
DisplayOutputLn("Can't open requested file '"..filename.."'.")
elseif not ActivateFile(filename) then
DisplayOutputLn(TR("Can't open file '%s': %s"):format(filename, wx.wxSysErrorMsg()))
end
end
end
@@ -74,7 +66,7 @@ else -- something different is running on our port
cln:settimeout(2)
cln:send(protocol.client.greeting)
local msg,err = cln:receive()
local msg = cln:receive()
local arg = ide.arg
if msg and msg == protocol.server.greeting then
local failed = false
@@ -85,7 +77,7 @@ else -- something different is running on our port
and (ide.osname ~= 'Macintosh' or not fileName:find("^-psn")) then
cln:send(protocol.client.requestloading:format(fileName))
local msg,err = cln:receive()
local msg, err = cln:receive()
if msg ~= protocol.server.answerok then
failed = true
print(err,msg)

View File

@@ -89,9 +89,9 @@ function StylesGetDefault()
end
local markers = {
breakpoint = {0, wxstc.wxSTC_MARK_CIRCLE, wx.wxColour(220, 64, 64), wx.wxColour(220, 64, 64)},
bookmark = {1, wxstc.wxSTC_MARK_SHORTARROW, wx.wxBLACK, wx.wxColour(96, 160, 220)},
currentline = {2, wxstc.wxSTC_MARK_ARROW, wx.wxBLACK, wx.wxColour(64, 220, 64)},
breakpoint = {0, wxstc.wxSTC_MARK_CIRCLE, wx.wxColour(196, 64, 64), wx.wxColour(220, 64, 64)},
bookmark = {1, wxstc.wxSTC_MARK_SHORTARROW, wx.wxColour(16, 96, 128), wx.wxColour(96, 160, 220)},
currentline = {2, wxstc.wxSTC_MARK_ARROW, wx.wxColour(16, 128, 16), wx.wxColour(64, 220, 64)},
message = {3, wxstc.wxSTC_MARK_CHARACTER+(' '):byte(), wx.wxBLACK, wx.wxColour(220, 220, 220)},
output = {4, wxstc.wxSTC_MARK_BACKGROUND, wx.wxBLACK, wx.wxColour(240, 240, 240)},
prompt = {5, wxstc.wxSTC_MARK_ARROWS, wx.wxBLACK, wx.wxColour(220, 220, 220)},
@@ -244,7 +244,12 @@ local specialmapping = {
local panes = uimgr:GetAllPanes()
for index = 0, panes:GetCount()-1 do
local wind = uimgr:GetPane(panes:Item(index).name).window
local children = wind:GetChildren()
-- wxlua compiled with STL doesn't provide GetChildren() method
-- as per http://sourceforge.net/p/wxlua/mailman/message/32500995/
local ok, children = pcall(function() return wind:GetChildren() end)
if not ok then break end
for child = 0, children:GetCount()-1 do
local data = children:Item(child):GetData()
local _, window = pcall(function() return data:DynamicCast("wxWindow") end)
@@ -311,8 +316,8 @@ function StylesApplyToEditor(styles,editor,font,fontitalic,lexerconvert)
editor:StyleClearAll()
-- set the default linenumber font size based on the editor font size
if styles.linenumber then
styles.linenumber.fs = styles.linenumber.fs or ide.config.editor.fontsize - 1
if styles.linenumber and not styles.linenumber.fs then
styles.linenumber.fs = ide.config.editor.fontsize and (ide.config.editor.fontsize - 1) or nil
end
for name,style in pairs(styles) do
@@ -376,7 +381,7 @@ function ReApplySpecAndStyles()
local openDocuments = ide.openDocuments
for i,doc in pairs(openDocuments) do
if (doc.editor.spec) then
SetupKeywords(doc.editor,nil,doc.editor.spec)
doc.editor:SetupKeywords(nil,doc.editor.spec)
end
end
end

40
src/editor/toolbar.lua Normal file
View File

@@ -0,0 +1,40 @@
-- Copyright 2014 Paul Kulchenko, ZeroBrane LLC
ide.config.toolbar.icons = {
ID_NEW, ID_OPEN, ID_SAVE, ID_SAVEALL, ID_PROJECTDIRFROMFILE, ID_PROJECTDIRCHOOSE,
ID_SEPARATOR,
ID_FIND, ID_REPLACE, ID_FINDINFILES,
ID_SEPARATOR,
ID_RUN, ID_STARTDEBUG, ID_RUNNOW, ID_STOPDEBUG, ID_DETACHDEBUG, ID_BREAK, ID_STEP, ID_STEPOVER, ID_STEPOUT,
ID_SEPARATOR,
ID_TOGGLEBREAKPOINT, ID_BOOKMARKTOGGLE, ID_VIEWCALLSTACK, ID_VIEWWATCHWINDOW,
ID_SEPARATOR,
[ID_FINDINFILES] = false,
[ID_RUN] = false,
[ID_RUNNOW] = false,
}
ide.config.toolbar.iconmap = {
[ID_NEW] = {"NORMAL-FILE", "Create an empty document"},
[ID_OPEN] = {"FILE-OPEN", "Open an existing document"},
[ID_SAVE] = {"FILE-SAVE", "Save the current document"},
[ID_SAVEALL] = {"NEW-DIR", "Save all open documents"},
[ID_PROJECTDIRFROMFILE]= {"GO-DIR-UP", "Set project directory from current file"},
[ID_PROJECTDIRCHOOSE] = {"DIR-SETUP", "Choose a project directory"},
[ID_FIND] = {"FIND", "Find text"},
[ID_REPLACE] = {"FIND-AND-REPLACE", "Find and replace text"},
[ID_FINDINFILES] = {"FIND-IN-FILES", "Find in files"},
[ID_RUN] = {"RUN", "Run"},
[ID_RUNNOW] = {"RUN-NOW", "Run as Scratchpad"},
[ID_STARTDEBUG] = {"DEBUG-START", "Start or Continue debugging"},
[ID_STOPDEBUG] = {"DEBUG-STOP", "Stop the currently running process"},
[ID_DETACHDEBUG]= {"DEBUG-DETACH", "Stop debugging and continue running the process"},
[ID_BREAK] = {"DEBUG-BREAK", "Break execution at the next executed line of code"},
[ID_STEP] = {"DEBUG-STEP-INTO", "Step into"},
[ID_STEPOVER] = {"DEBUG-STEP-OVER", "Step over"},
[ID_STEPOUT] = {"DEBUG-STEP-OUT", "Step out of the current function"},
[ID_TOGGLEBREAKPOINT] = {"DEBUG-BREAKPOINT-TOGGLE", "Toggle breakpoint"},
[ID_BOOKMARKTOGGLE] = {"BOOKMARK-TOGGLE", "Toggle bookmark"},
[ID_VIEWCALLSTACK] = {"DEBUG-CALLSTACK", "View the stack window"},
[ID_VIEWWATCHWINDOW] = {"DEBUG-WATCH", "View the watch window"},
}

View File

@@ -42,6 +42,7 @@ ide = {
editor = {
foldcompact = true,
checkeol = true,
saveallonrun = false,
},
debugger = {
verbose = false,
@@ -56,9 +57,16 @@ ide = {
interpreter = 'luadeb',
},
outputshell = {},
filetree = {},
filetree = {
mousemove = true,
},
funclist = {},
toolbar = {
icons = {},
iconmap = {},
},
keymap = {},
messages = {},
language = "en",
@@ -85,13 +93,14 @@ ide = {
unhidewindow = false, -- to unhide a gui window
allowinteractivescript = false, -- allow interaction in the output window
filehistorylength = 20,
projecthistorylength = 15,
projecthistorylength = 20,
savebak = false,
singleinstance = false,
singleinstanceport = 0xe493,
-- HiDPI/Retina display support;
-- `false` by default because of issues with indicators with alpha setting
hidpi = false,
hotexit = false,
},
specs = {
none = {
@@ -180,7 +189,7 @@ end
dofile "src/version.lua"
for _, file in ipairs({"ids", "style", "keymap", "proto"}) do
for _, file in ipairs({"ids", "style", "keymap", "proto", "toolbar"}) do
dofile("src/editor/"..file..".lua")
end
@@ -255,7 +264,9 @@ do
for index = 2, #arg do
if (arg[index] == "-cfg" and index+1 <= #arg) then
table.insert(configs,arg[index+1])
elseif arg[index-1] ~= "-cfg" then
elseif arg[index-1] ~= "-cfg"
-- on OSX command line includes -psn... parameter, don't include these
and (ide.osname ~= 'Macintosh' or not arg[index]:find("^-psn")) then
table.insert(filenames,arg[index])
end
end
@@ -303,13 +314,23 @@ local function loadPackages(filter)
-- check dependencies and assign file names to each package
local unload = {}
for fname, package in pairs(ide.packages) do
if type(package.dependencies) == 'table'
and package.dependencies.osname
and not package.dependencies.osname:find(ide.osname, 1, true) then
(DisplayOutputLn or print)(
("Package '%s' not loaded: requires %s platform, but you are running %s.")
:format(fname, package.dependencies.osname, ide.osname)
)
table.insert(unload, fname)
end
local needsversion = tonumber(package.dependencies)
or type(package.dependencies) == 'table' and tonumber(package.dependencies[1])
or -1
local isversion = tonumber(ide.VERSION)
if isversion and needsversion > isversion then
(DisplayOutputLn or print)(
("Package '%s' not loaded: requires version %s, but you are running version %s")
("Package '%s' not loaded: requires version %s, but you are running version %s.")
:format(fname, needsversion, ide.VERSION)
)
table.insert(unload, fname)
@@ -373,8 +394,7 @@ end
-- process config
-- set ide.config environment
ide.config.os = os
ide.config.wxstc = wxstc
setmetatable(ide.config, {__index = {os = os, wxstc = wxstc, wx = wx}})
ide.config.load = { interpreters = loadInterpreters, specs = loadSpecs,
tools = loadTools }
do
@@ -469,14 +489,14 @@ SettingsRestoreView()
-- Load the filenames
do
for _, fileName in ipairs(filenames) do
if fileName ~= "--" then
if wx.wxDirExists(fileName) then
local dir = wx.wxFileName.DirName(fileName)
for _, filename in ipairs(filenames) do
if filename ~= "--" then
if wx.wxDirExists(filename) then
local dir = wx.wxFileName.DirName(filename)
dir:Normalize() -- turn into absolute path if needed
ProjectUpdateProjectDir(dir:GetFullPath())
else
LoadFile(fileName, nil, true)
elseif not ActivateFile(filename) then
DisplayOutputLn(("Can't open file '%s': %s"):format(filename, wx.wxSysErrorMsg()))
end
end
end
@@ -501,6 +521,17 @@ local remap = {
[ID_RENAMEFILE] = ide:GetProjectTree(),
[ID_DELETEFILE] = ide:GetProjectTree(),
}
local function rerouteMenuCommand(obj, id)
-- check if the conflicting shortcut is enabled:
-- (1) SetEnabled wasn't called or (2) Enabled was set to `true`.
local uievent = wx.wxUpdateUIEvent(id)
obj:ProcessEvent(uievent)
if not uievent:GetSetEnabled() or uievent:GetEnabled() then
obj:AddPendingEvent(wx.wxCommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, id))
end
end
local function remapkey(event)
local keycode = event:GetKeyCode()
local mod = event:GetModifiers()
@@ -508,7 +539,7 @@ local function remapkey(event)
if obj:FindFocus():GetId() == obj:GetId() then
local ae = wx.wxAcceleratorEntry(); ae:FromString(KSC(id))
if ae:GetFlags() == mod and ae:GetKeyCode() == keycode then
obj:AddPendingEvent(wx.wxCommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, id))
rerouteMenuCommand(obj, id)
return
end
end
@@ -534,7 +565,7 @@ local function resolveConflict(localid, globalid)
end
end
end
ide.frame:AddPendingEvent(wx.wxCommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, globalid))
rerouteMenuCommand(ide.frame, globalid)
end
end

View File

@@ -155,8 +155,8 @@ function GetPathWithSep(wxfn)
return wxfn:GetPath(bit.bor(wx.wxPATH_GET_VOLUME, wx.wxPATH_GET_SEPARATOR))
end
function FileSysHasContent(dir)
local f = wx.wxFindFirstFile(dir,wx.wxFILE + wx.wxDIR)
function FileDirHasContent(dir)
local f = wx.wxFindFirstFile(dir, wx.wxFILE + wx.wxDIR)
return #f>0
end
@@ -269,7 +269,8 @@ function FileCopy(file1, file2)
return wx.wxCopyFile(file1, file2), wx.wxSysErrorMsg()
end
TimeGet = pcall(require, "socket") and socket.gettime or os.clock
local ok, socket = pcall(require, "socket")
TimeGet = ok and socket.gettime or os.clock
function isBinary(text) return text:find("[^\7\8\9\10\12\13\27\32-\255]") end

View File

@@ -4,7 +4,7 @@ editor:SetText('print("select")')
-- this is to set proper styles, which are needed for EditorCallTip
editor:Colourise(0, -1)
local value
local value = ''
local CTS = editor.CallTipShow
editor.CallTipShow = function(editor, pos, tip) value = tip end
EditorCallTip(editor, 10)

View File

@@ -18,6 +18,10 @@ lualibs/lua_lexer_loose.lua
lualibs/lua_parser_loose.lua
lualibs/luainspect/*.lua
lualibs/metalua/*.lua
lualibs/metalua/compiler/*.lua
lualibs/metalua/compiler/parser/*.lua
lualibs/metalua/compiler/parser/annot/*.lua
lualibs/metalua/grammar/*.lua
lualibs/mobdebug/mobdebug.lua
lualibs/ltn12.lua
lualibs/luadist.lua

View File

@@ -7,6 +7,7 @@ editor.usewrap = true
editor.calltipdelay = 500
editor.smartindent = true
editor.fold = true
editor.autoreload = true
local G = ... -- this now points to the global environment
local mac = G.ide.osname == 'Macintosh'
@@ -34,7 +35,6 @@ else
end
outputshell.usewrap = true
filehistorylength = 20
hidpi = mac -- support Retina displays by default (OSX)

View File

Before

Width:  |  Height:  |  Size: 884 B

After

Width:  |  Height:  |  Size: 884 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 374 B

After

Width:  |  Height:  |  Size: 374 B

View File

Before

Width:  |  Height:  |  Size: 702 B

After

Width:  |  Height:  |  Size: 702 B

View File

Before

Width:  |  Height:  |  Size: 500 B

After

Width:  |  Height:  |  Size: 500 B

View File

Before

Width:  |  Height:  |  Size: 583 B

After

Width:  |  Height:  |  Size: 583 B

View File

Before

Width:  |  Height:  |  Size: 259 B

After

Width:  |  Height:  |  Size: 259 B

View File

Before

Width:  |  Height:  |  Size: 331 B

After

Width:  |  Height:  |  Size: 331 B

View File

Before

Width:  |  Height:  |  Size: 357 B

After

Width:  |  Height:  |  Size: 357 B

View File

Before

Width:  |  Height:  |  Size: 353 B

After

Width:  |  Height:  |  Size: 353 B

View File

Before

Width:  |  Height:  |  Size: 594 B

After

Width:  |  Height:  |  Size: 594 B

View File

Before

Width:  |  Height:  |  Size: 786 B

After

Width:  |  Height:  |  Size: 786 B

View File

Before

Width:  |  Height:  |  Size: 738 B

After

Width:  |  Height:  |  Size: 738 B

View File

Before

Width:  |  Height:  |  Size: 602 B

After

Width:  |  Height:  |  Size: 602 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 451 B

After

Width:  |  Height:  |  Size: 451 B

View File

Before

Width:  |  Height:  |  Size: 671 B

After

Width:  |  Height:  |  Size: 671 B

View File

Before

Width:  |  Height:  |  Size: 662 B

After

Width:  |  Height:  |  Size: 662 B

View File

Before

Width:  |  Height:  |  Size: 615 B

After

Width:  |  Height:  |  Size: 615 B

View File

Before

Width:  |  Height:  |  Size: 778 B

After

Width:  |  Height:  |  Size: 778 B

View File

Before

Width:  |  Height:  |  Size: 717 B

After

Width:  |  Height:  |  Size: 717 B

View File

Before

Width:  |  Height:  |  Size: 623 B

After

Width:  |  Height:  |  Size: 623 B

View File

Before

Width:  |  Height:  |  Size: 698 B

After

Width:  |  Height:  |  Size: 698 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 652 B

After

Width:  |  Height:  |  Size: 652 B

View File

Before

Width:  |  Height:  |  Size: 730 B

After

Width:  |  Height:  |  Size: 730 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 753 B

After

Width:  |  Height:  |  Size: 753 B

BIN
zbstudio/res/16/RUN-NOW.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

BIN
zbstudio/res/16/RUN.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 473 B

After

Width:  |  Height:  |  Size: 473 B

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 566 B

After

Width:  |  Height:  |  Size: 566 B

View File

Before

Width:  |  Height:  |  Size: 755 B

After

Width:  |  Height:  |  Size: 755 B

View File

Before

Width:  |  Height:  |  Size: 369 B

After

Width:  |  Height:  |  Size: 369 B

View File

Before

Width:  |  Height:  |  Size: 425 B

After

Width:  |  Height:  |  Size: 425 B

View File

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 428 B

View File

Before

Width:  |  Height:  |  Size: 447 B

After

Width:  |  Height:  |  Size: 447 B

View File

Before

Width:  |  Height:  |  Size: 799 B

After

Width:  |  Height:  |  Size: 799 B

View File

Before

Width:  |  Height:  |  Size: 1008 B

After

Width:  |  Height:  |  Size: 1008 B

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 780 B

After

Width:  |  Height:  |  Size: 780 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 495 B

After

Width:  |  Height:  |  Size: 495 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 775 B

After

Width:  |  Height:  |  Size: 775 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 622 B

After

Width:  |  Height:  |  Size: 622 B

View File

Before

Width:  |  Height:  |  Size: 821 B

After

Width:  |  Height:  |  Size: 821 B

BIN
zbstudio/res/24/RUN-NOW.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

BIN
zbstudio/res/24/RUN.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B