Compare commits

...

319 Commits
0.50 ... 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
Paul Kulchenko
486577524b Updated CHANGELOG for 0.70. 2014-06-18 18:55:20 -07:00
Paul Kulchenko
c4567e769c Added support for nginx debugging (Mobdebug 0.564). 2014-06-16 22:55:37 -07:00
Paul Kulchenko
7d956357c5 Updated CHANGELOG with recent changes. 2014-06-14 14:16:16 -07:00
Paul Kulchenko
600a379622 Updated contributor references. 2014-06-14 14:15:27 -07:00
crazybutcher
afbe8cb307 glslc: asm beautifier branch stats 2014-06-14 16:23:57 -04:00
Paul Kulchenko
9d1cba52ba Removed checks for specific errors in Local/Remote console.
This helps with handling error messages from unknown runtime
environments (e.g. moonscript).
2014-06-13 10:11:45 -07:00
Paul Kulchenko
3d29b5f188 Added support for custom debugger initializer (global or interpreter-based). 2014-06-12 17:41:47 -07:00
Paul Kulchenko
38948cd2b6 Added line mapping support for debugging Lua-based languages (e.g. moonscript). 2014-06-12 17:41:26 -07:00
Paul Kulchenko
d79035bf7a Updated auto-complete logic to use configured spec separators. 2014-06-12 08:37:21 -07:00
Paul Kulchenko
1fb919a6e6 Fixed setting control focus when the main frame is hidden. 2014-06-12 08:26:01 -07:00
Paul Kulchenko
227631f1df Added support to force local execution in console by prepending ! (#326). 2014-06-11 16:01:30 -07:00
Paul Kulchenko
1006f7b165 Fixed loading packages with dashes in filenames (fixes #330).
This still allows for `name-os-arch` format, but will load a package
if `os` or `arch` don't match any of the known values.
2014-06-09 12:49:54 -07:00
Paul Kulchenko
44dc63dbad Added setting proper arg[0] value during debugging (fixes #329). 2014-06-09 12:36:46 -07:00
Paul Kulchenko
adc2532802 Disabled message on failure to read symlinked folder content on Windows. 2014-06-09 12:34:32 -07:00
Paul Kulchenko
3937a134d5 Added double click navigation in the Output window for unnamed files. 2014-06-09 12:32:54 -07:00
Paul Kulchenko
f7329fb234 Added centering of line after double click in the Output window. 2014-06-08 21:09:43 -07:00
Paul Kulchenko
0775e1bcdc Upgraded metalua to v0.7.2. 2014-06-08 20:53:17 -07:00
Paul Kulchenko
b0eabd6382 Added editor.wrapindentmode and editor.wrapstartindent settings. 2014-06-07 11:05:02 -07:00
Paul Kulchenko
6955e1b3f4 Fixed toolbar to stay shown after failure to start debugging. 2014-06-06 16:23:05 -07:00
Paul Kulchenko
3c4fc71249 Added a workaround for focus switching between controls on OSX (#89, #327).
This is a more complete workaround for missing KILL_FOCUS event on OSX (an
incomplete workaround was removed by a315b1a8).
2014-06-05 22:19:39 -07:00
Paul Kulchenko
a2dc2e9934 Fixed focus on the editor after closing a dialog on OSX (fixes #328). 2014-06-05 22:17:02 -07:00
Paul Kulchenko
5b026e10a6 Fixed crash on OSX when changing focus while the app is being closed (#327). 2014-06-05 22:15:09 -07:00
Paul Kulchenko
131fc63deb Fixed some toolbar buttons being enabled with no editor tab open. 2014-06-04 20:40:57 -07:00
Paul Kulchenko
9ea833ce7d Fixed toolbar stealing focus after closing floating panels and dropdowns (#327). 2014-06-04 20:08:14 -07:00
Paul Kulchenko
070111b06e Fixed restoring control focus when the app gets focus on OSX (fixes #327). 2014-06-04 14:51:12 -07:00
Paul Kulchenko
a315b1a87b Removed focus handling workaround for editor tab changes (#89, #327).
The issue the workaround was for was already fixed in wxwidgets:
http://trac.wxwidgets.org/ticket/14142
2014-06-04 14:50:34 -07:00
Paul Kulchenko
8e2257f8ac Fixed activating editor when starting the app on OSX (#327). 2014-06-04 13:08:10 -07:00
Paul Kulchenko
0c227c0ad6 Added assertion to ensure inserted editor is not in the notebook already. 2014-06-01 18:56:27 -07:00
Paul Kulchenko
0555fed7ad Added format.apptitle option to format IDE title (thanks to @sclark39). 2014-05-31 15:30:32 -07:00
Paul Kulchenko
d3d6a7a485 Removed using internal structures. 2014-05-31 15:28:02 -07:00
Paul Kulchenko
58f86894e9 Renamed menuformatrecentprojects to format.menurecentprojects (#305). 2014-05-30 14:44:47 -07:00
Paul Kulchenko
51c0443c9a Updated logic for populating placeholders in dropdown menus. 2014-05-30 13:37:28 -07:00
Paul Kulchenko
ae6f7c5589 Disabled breakpoint toggling when editor is not in focus. 2014-05-30 13:30:51 -07:00
Paul Kulchenko
f5dc6f5018 Added restoring cursor position after sorting/re-indenting. 2014-05-29 12:26:41 -07:00
Paul Kulchenko
c3c141fedf Updated project switch to hide menu before switching. 2014-05-29 12:15:49 -07:00
Paul Kulchenko
fcb7d2a2ad Updated formatting. 2014-05-29 12:13:21 -07:00
Paul Kulchenko
83b69ad8e8 Fixed auto-complete to not offer the word the cursor is on. 2014-05-29 11:47:50 -07:00
Paul Kulchenko
a6db07d8e1 Fixed hiding auto-complete when the only option matches what's typed. 2014-05-29 10:35:13 -07:00
Paul Kulchenko
44f8d42156 Increase font size for code fragments in markup (#305). 2014-05-29 09:50:07 -07:00
Paul Kulchenko
b9a1bf27ac Fixed an error when all editor tabs are closed. 2014-05-29 09:49:19 -07:00
Christoph Kubisch
1a673ba7e5 glslc: change domain detection to be compatible with file.comp.glsl and file.tese 2014-05-29 12:15:55 +02:00
Paul Kulchenko
c21746f1a7 Added onEditorUserlistSelection event for userlist selection (#166). 2014-05-28 17:11:37 -07:00
Paul Kulchenko
41c38d0bcb Added onEditorAction event for cut/copy/paste actions (#166). 2014-05-28 17:10:58 -07:00
Paul Kulchenko
a5cd585bcc Added package GetEditorWithFocus method (#166). 2014-05-28 17:04:49 -07:00
Paul Kulchenko
c2c631c03e Merge branch 'master' of https://github.com/Yonaba/ZeroBraneStudio into french-translation-update 2014-05-28 11:51:43 -07:00
Yonaba
285a599dc9 Updated french translation 2014-05-28 12:03:12 +00:00
Paul Kulchenko
f9903f3470 Updated menu items to stay enabled only when appropriate object has focus. 2014-05-27 22:17:56 -07:00
Paul Kulchenko
843a22e6e9 Reorganized menu shortcut conflict handling (#233).
The earlier logic for shortcut conflict handling depended on the global
menu shortcut being always enabled, which prevented some shortcuts from
being disabled when needed. The new logic doesn't have that deficiency.
2014-05-27 22:09:53 -07:00
Paul Kulchenko
b90305efff Removed handling of project dropdown in menu as it's no longer needed (#305). 2014-05-27 22:03:16 -07:00
Paul Kulchenko
64a9f7c701 Fixed replace-in-files when saving backup copy is turned off. 2014-05-26 15:43:39 -07:00
Paul Kulchenko
64a68b443b Added editor.extradescent option for line spacing (#305). 2014-05-24 15:37:05 -07:00
Paul Kulchenko
8460abef86 Added centering of line on page after re-loading file with a known position. 2014-05-23 08:15:58 -07:00
Paul Kulchenko
63f76b6695 Fix re-indenting of anonymous functions in tables (#324). 2014-05-22 08:02:10 -07:00
Paul Kulchenko
7d2884ebfe Updated formatting. 2014-05-21 16:29:21 -07:00
Paul Kulchenko
4efc725211 Added re-indentation of selected fragment or entire file (closes #324). 2014-05-21 16:29:04 -07:00
Paul Kulchenko
4d44f28a06 Added sorting of the entire file if nothing is selected. 2014-05-21 16:29:03 -07:00
Paul Kulchenko
804567f6c3 Updated indentation logic for if/elseif/while/for split into 2+ lines (#324). 2014-05-21 16:28:20 -07:00
Paul Kulchenko
b811a8dd39 Updated indentation logic to ignore comments (#324). 2014-05-21 16:28:09 -07:00
Paul Kulchenko
638bc477d7 Updating sorting to keep original line endings. 2014-05-20 18:38:58 -07:00
Paul Kulchenko
aa0ecdb4f4 Added Edit | Source sub-menu. 2014-05-20 18:37:55 -07:00
Paul Kulchenko
dc1990c7b9 Updated README with supported engines and installation instructions. 2014-05-19 14:38:03 -07:00
Paul Kulchenko
2e497bf831 Added centering line on page after bookmark navigation. 2014-05-18 21:08:37 -07:00
Christoph Kubisch
e087186def treat unreal shaders as hlsl 2014-05-17 12:39:02 +02:00
Christoph Kubisch
715b7d06c4 simplified glslc usage (compile and link based on file extensions)
bugfix files with spaces
2014-05-17 11:53:28 +02:00
Paul Kulchenko
d68da68c4b Fixed F2 shortcut not working in file tree and watch panel (#233).
This is a workaround for a conflict between global shortcuts and local
shortcuts (like F2) used in the file tree or a watch panel.
Because of several issues on OSX (as described in details in this thread:
https://groups.google.com/d/msg/wx-dev/juJj_nxn-_Y/JErF1h24UFsJ),
the workaround installs a global event handler that manually re-routes
conflicting events when the current focus is on a proper object.
Non-conflicting shortcuts are handled through key-down events.
2014-05-16 15:09:49 -07:00
Paul Kulchenko
83088fe798 Added GetProjectTree, GetWatch, and GetStack package calls (#166). 2014-05-16 15:04:42 -07:00
Paul Kulchenko
1d9a043c25 Disabled changing toolbar color with auxwindow as it only works for the dropdown. 2014-05-15 14:41:54 -07:00
Paul Kulchenko
d67fcf931e Fixed debugger compatibility with Lua 5.2 (Mobdebug 0.561). 2014-05-14 09:22:02 -07:00
Paul Kulchenko
379db00a5c Added bookmark-toggle toolbar icon (#233). 2014-05-13 22:11:00 -07:00
Paul Kulchenko
d65acb2d40 Updated breakpoint-toggle toolbar icon to better match other icons (#305). 2014-05-13 22:09:56 -07:00
Paul Kulchenko
c7041940f3 Updated bookmark navigation to wrap around (#233). 2014-05-12 21:44:31 -07:00
Paul Kulchenko
f7df30614d Updated documentation for 0.60. 2014-05-11 19:37:27 -07:00
Paul Kulchenko
5db556849b Upgraded Mobdebug (0.56). 2014-05-11 17:00:15 -07:00
Leo Bartoloni
471eaa9087 Added new italian translations 2014-05-11 21:38:11 +02:00
Paul Kulchenko
e54ee64d79 Updated default fonts for Windows and Linux for better looking ones (#305). 2014-05-11 09:09:38 -07:00
Paul Kulchenko
45c83f73db Merge branch 'copyfullpath' of https://github.com/sclark39/ZeroBraneStudio into copyfullpath 2014-05-10 15:46:26 -07:00
riidom
0ffd472396 Update de.lua 2014-05-10 21:10:24 +02:00
Paul Kulchenko
adaca47bf6 Updated language files with new messages (#70). 2014-05-10 10:30:02 -07:00
Paul Kulchenko
d2d477b8f5 Added Russian translation for new messages (#70). 2014-05-10 10:26:05 -07:00
Paul Kulchenko
3cfc5f65c2 Updated copyright messages. 2014-05-10 09:53:00 -07:00
Paul Kulchenko
1ce791700c Updated Go To Line menu item and its translations. 2014-05-10 09:46:28 -07:00
sclark39
c28ef5000a Adding Copy Full Path to editor tabs, and a Clear Output Window option to the Output tab 2014-05-09 23:40:14 -07:00
Paul Kulchenko
61bb92950a Updated build scripts with a fix for a wxlua compilation issue (#260). 2014-05-09 21:43:11 -07:00
Paul Kulchenko
b14198161e Updated build prerequisites Linux install script. 2014-05-09 17:57:58 -07:00
Paul Kulchenko
ad2dd2e40e Enabled editor width auto-adjustment when wrapping is off. 2014-05-09 17:56:59 -07:00
Paul Kulchenko
d9fdbe0745 Added support for packages in config files (#166). 2014-05-08 14:50:38 -07:00
Paul Kulchenko
905d9cd75d Fixed Corona interpreter to clean debugger in plugin folder (Win). 2014-05-08 14:49:33 -07:00
Paul Kulchenko
d68b84f862 Added formatting for Recent Projects menu (#305). 2014-05-07 18:22:53 -07:00
Paul Kulchenko
1e7f5de4db Updated default indicator color to more neutral one (#305). 2014-05-07 10:15:31 -07:00
Paul Kulchenko
87d116a1c3 Fixed missing event parameter in onFiletreeActivate handler (#166). 2014-05-07 09:59:56 -07:00
Paul Kulchenko
33b4641b63 Fixed file tree activation of a deleted file. 2014-05-07 09:59:27 -07:00
Paul Kulchenko
9efb4454e4 Enable retina support (hidpi=true) by default on OSX (#305). 2014-05-07 09:58:57 -07:00
Paul Kulchenko
02f7e24d33 Updated OSX build script to use 10.7 SDK with 10.6 min-version (#260). 2014-05-07 09:58:44 -07:00
Paul Kulchenko
e001386383 Reordered markers to keep the curent line marker on top (#305). 2014-05-05 17:39:28 -07:00
Paul Kulchenko
9257c03b07 Updated Mobdebug (0.555) to add support for pause debugger call. 2014-05-05 14:31:50 -07:00
Paul Kulchenko
6216ac37a2 Updated lua interpreter to remove caching of executable path.
This allows updating `path.lua` from project config or changing
it without IDE restart.
2014-05-05 14:30:08 -07:00
Paul Kulchenko
476aa9d90a Added Detach process command to stop debugging and continue process. 2014-05-05 11:30:41 -07:00
Paul Kulchenko
c665a0cc5c Updated resetting pid only after completing non-debbugged process.
This changes the logic introduced by 78c44e60 as it interfered with
`done()` call from the debugger that prevented properly handling of
a process that stopped debugging, but continued execution.
2014-05-04 22:09:58 -07:00
Paul Kulchenko
96397e66c5 Adjusted code color in the comment markup for better visibility (#305). 2014-05-04 14:46:34 -07:00
Paul Kulchenko
758c5a8187 Added re/docking of Watch/Stack notebooks on tab background doubleclick (#305). 2014-05-02 14:45:37 -07:00
Paul Kulchenko
52e02f087c Updated formatting. 2014-05-02 14:36:34 -07:00
Paul Kulchenko
a02437dee1 Added bookmark handling (closes #233). 2014-05-02 14:24:31 -07:00
Paul Kulchenko
3cd754b6b9 Removed Clear Dynamic Words menu as it's rarely used. 2014-05-02 14:19:42 -07:00
Paul Kulchenko
9a8f870ae4 Fixed switching to full screen and restoring status bar on OSX (ref #305). 2014-05-02 10:31:08 -07:00
Paul Kulchenko
ea5b78707d Added Clear items to the Recent Files menu (ref #305). 2014-05-01 21:33:13 -07:00
Paul Kulchenko
1d8b02bb41 Added recent files dropdown to the toolbar (ref #305). 2014-05-01 17:52:36 -07:00
Paul Kulchenko
83f52d0a57 Merge branch 'filetree-simplified' 2014-05-01 17:14:03 -07:00
Paul Kulchenko
d807accec2 Added applying new UI settings after upgrade (ref #305). 2014-05-01 17:11:31 -07:00
Paul Kulchenko
430469ab3a Removed the gripper on the toolbar (ref #305). 2014-04-30 22:39:02 -07:00
Paul Kulchenko
e8978d8e89 Switched to plain background for the toolbar (ref #305). 2014-04-30 22:38:39 -07:00
Paul Kulchenko
aa0a0f8520 Updated shortcut for Recent File navigation (ref #305).
`Ctrl-<` and `Ctrl->` was a bad choice as it actually requires pressing
`Ctrl-Shift-,` and `Ctrl-Shift-.` on OSX (although it works without
`Shift` on Windows). Updated to `Ctrl-,` and `Ctrl-.` to use the same
shortcuts on all platforms.
2014-04-30 21:10:10 -07:00
Paul Kulchenko
c089037e26 Added toolbar button dropdown with recent projects (ref #305). 2014-04-30 20:22:16 -07:00
Paul Kulchenko
cf67ab3b6a Added Choose Project Directory to the toolbar (ref #305). 2014-04-30 17:10:22 -07:00
Paul Kulchenko
5beccc0498 Updated application icons (ref #305). 2014-04-30 16:30:31 -07:00
Paul Kulchenko
1e0e286682 Switched to AuiToolBar as it provides buttons with dropdowns (ref #305). 2014-04-30 14:50:13 -07:00
Paul Kulchenko
b895f51beb Updated stack/watch panel captions to be invisible (ref #305). 2014-04-30 14:48:37 -07:00
Paul Kulchenko
4e5356670b Updated interpreters to check ProgramFiles env variable on Windows.
This helps with locating executables for a localized OS.
2014-04-29 21:59:04 -07:00
Paul Kulchenko
7cbf2f99f6 Moved Project Directory menu item lower to not activate on OSX (ref #305). 2014-04-29 21:57:48 -07:00
Paul Kulchenko
84d7928b94 Disallowed closing Output/Console/Project tabs (fixes #310). 2014-04-29 16:09:04 -07:00
Paul Kulchenko
37b73d6c49 Added floating/docking of notebooks on tab background doubleclick (ref #305). 2014-04-28 18:01:51 -07:00
Paul Kulchenko
f1b74b9c2f Reduced sash (border between subsections) in all notebooks (ref #305). 2014-04-28 18:01:50 -07:00
Paul Kulchenko
9449650d47 Updated panel captions to be invisible (rev #305). 2014-04-28 18:01:49 -07:00
Paul Kulchenko
b3bc39a8d5 Removed project selection dropdown from the filetree (ref #305). 2014-04-28 18:01:47 -07:00
Paul Kulchenko
ab4ae3e281 Updated 'window unhide' logic (Windows only) to be called less frequently. 2014-04-28 17:27:25 -07:00
Paul Kulchenko
18dadbc001 Updated love2d interpreter to not hide the application window. 2014-04-28 17:25:30 -07:00
Paul Kulchenko
b4f0635ab7 Fixed right-click handling in filetree on OSX broken by 3709f61f (ref #166). 2014-04-28 12:04:16 -07:00
Paul Kulchenko
9a4ed47179 Updated file sorting to be case-insensitive on all platforms. 2014-04-27 23:02:09 -07:00
Paul Kulchenko
c314996902 Added Recent Project menu refresh after switching projects (ref #305). 2014-04-26 16:14:24 -07:00
Paul Kulchenko
3a49e4c37d Updated filetree menu to use 'Edit Project Directory' on root element (#305). 2014-04-25 23:28:35 -07:00
Paul Kulchenko
23a874b5ae Added setting project directory by renaming the filetree root element (#305). 2014-04-25 23:26:26 -07:00
Paul Kulchenko
92b88a8d26 Added filetree popup menu with the list of projects (ref #305). 2014-04-24 22:35:27 -07:00
Paul Kulchenko
3a8cb8aa42 Disabled current project on the recent projects list (ref #305). 2014-04-24 22:34:59 -07:00
Paul Kulchenko
08d1d9f1f8 Added 'Recent Projects' menu (ref #305). 2014-04-24 21:48:48 -07:00
Paul Kulchenko
95fcf4aa79 Updated love2d API to fix typos in descriptions (ref #247). 2014-04-24 21:20:09 -07:00
Paul Kulchenko
9c47414635 Updated love2d API for v0.9.1 (ref #247). 2014-04-24 19:46:41 -07:00
Paul Kulchenko
8969abb43f Removed paragraph conversion from love2d API conversion script (ref #247). 2014-04-24 19:45:59 -07:00
Paul Kulchenko
ce933f4f81 Updated love2d API conversion script to handle top-level functions (ref #247). 2014-04-24 19:45:09 -07:00
Paul Kulchenko
b8de606bed Removed border around Output/Console panels (ref #305). 2014-04-21 22:51:25 -07:00
Paul Kulchenko
9c60c1e37c Added package GetLaunchedProcess call (ref #166). 2014-04-19 10:38:53 -07:00
Paul Kulchenko
e9a8a8bd9c Fixed usage of self in one of package API calls (ref #166). 2014-04-19 10:38:28 -07:00
Paul Kulchenko
a91426eb5c Added IsRunning and IsConnected API calls for the debugger (ref #166). 2014-04-18 18:01:56 -07:00
Paul Kulchenko
ab5bec082d Updated PackageUnRegister call to return the package on success (ref #166). 2014-04-18 18:00:59 -07:00
Paul Kulchenko
e384146089 Added editor.wrapflags to configure line wrapping indicators (ref #305). 2014-04-17 21:37:13 -07:00
Paul Kulchenko
a5df5d63b6 Fixed find dialog to stay on top after search directory selection on OSX.
OSX moves the find dialog to the background after using the directory
dropdown in the open file dialog. This workaround keeps find dialog on top.
2014-04-16 21:17:01 -07:00
Paul Kulchenko
11729813a6 Added explicit sorting of files in the filetree.
Some systems (Linux and some instances of OSX) seem to return files in
arbitrary order.
2014-04-16 21:13:47 -07:00
Paul Kulchenko
e577d96a4e Removed deprecated startfile interpreter option.
Use `startwith` option instead.
2014-04-16 21:13:34 -07:00
Paul Kulchenko
d61be1cf77 Fixed search result navigation after clicking beyond the end of line. 2014-04-15 17:51:32 -07:00
Paul Kulchenko
6698f43f76 Disabled showing hidden toolbar while in full screen mode (ref #305).
The toolbar is drawn with artifacts on the screen in Windows, so it's
disabled for not until fixed in wxwidgets.
2014-04-15 14:28:34 -07:00
Paul Kulchenko
51cd41ceba Fixed status/toolbar visibility on exiting full screen mode on OSX (ref #305). 2014-04-15 14:27:26 -07:00
Paul Kulchenko
0480e9ca39 Removed a workaround on OSX that is no longer needed. 2014-04-15 14:10:37 -07:00
Paul Kulchenko
b67c509f30 Added a workaround for wxwdigets bug when status bar is shown (ref #305).
When the status bar is shown after being initially hidden, it draws the
content of the status bar in the wrong location. This workaround fixes that.
2014-04-15 13:30:47 -07:00
Paul Kulchenko
168d65b411 Added showing/hiding of the status bar (ref #305). 2014-04-15 13:23:49 -07:00
Paul Kulchenko
a667b75177 Added a workaround for wxwdigets bug when toolbar is shown (ref #305).
When the toolbar is shown after being initially hidden (with the main
window being maximized), it leaves a small artifact on the toolbar.
This workaround removes the artifact.
2014-04-15 11:11:32 -07:00
Paul Kulchenko
91a8518db9 Changed order of stopping debugger and saving settings (ref #305).
Need to stop the debugger first as it changes some of the UI elements
(toolbar visibility, tab names, stack/watch windows) so that the correst
state is saved.
2014-04-15 11:08:56 -07:00
Paul Kulchenko
eb68425a25 Added auto-showing toolbar when debugging starts (ref #305). 2014-04-15 11:08:00 -07:00
Paul Kulchenko
eaa9aba068 Added showing/hiding of the toolbar (ref #305). 2014-04-14 18:12:04 -07:00
Paul Kulchenko
95452706a0 Updated fold/wrap flag handling to work with wxwidgets 2.8 (ref #305). 2014-04-13 18:18:46 -07:00
Paul Kulchenko
a5eaa2fd8a Set def linenumber font size as one smaller than editor font size (ref #305). 2014-04-13 16:11:07 -07:00
Paul Kulchenko
445d90f50d Added outputshell.usewrap to set Output wrapping; on by default (ref #305). 2014-04-13 15:08:09 -07:00
Paul Kulchenko
36b8a414d5 Added wrapped line indicator to line number margin (ref #305). 2014-04-13 15:06:10 -07:00
Paul Kulchenko
21d0991b9b Added editor.foldflags; set default to draw one line when folded (ref #305). 2014-04-13 14:44:19 -07:00
Paul Kulchenko
483c70bef2 Updated breakpoint/currentline markers for less contrast colors (ref #305). 2014-04-13 14:30:07 -07:00
Paul Kulchenko
1613675b31 Improved compatibility with wxwidgets 2.8. 2014-04-13 14:26:50 -07:00
Paul Kulchenko
2cea675c10 Added editor.extraascent option to add line spacing (ref #305). 2014-04-13 14:25:18 -07:00
Paul Kulchenko
2723801604 Made fold and marker margins wider (ref #305). 2014-04-13 14:20:14 -07:00
Paul Kulchenko
fa6e8d1442 Added explicit conversion to number for numeric settings. 2014-04-13 14:19:30 -07:00
Paul Kulchenko
037476ac49 Increased default font size in the editor (ref #305). 2014-04-12 22:27:55 -07:00
Paul Kulchenko
a457ed62b9 Added editor.foldtype with box, cirle, arrow, and plus types (ref #305). 2014-04-12 22:26:46 -07:00
Paul Kulchenko
ae3ddb59f7 Refactored editor config references. 2014-04-12 22:25:38 -07:00
Paul Kulchenko
78c44e6060 Fixed an issue with running processes not terminated when closing IDE.
This was a side-effect of an earlier change that was done to run
end-callbacks before terminating external processes (as those callbacks
may be used, for example, to remove temporary files).

Many of interpreters had logic in those callbacks to reset debugger.pid,
which was useful (at a time) to reset UI after terminatin of a process
(introduced by 4554c67c). However, this logic was unnecesary in
interpreters and interfered with terminating running processes, so this
commit removes it from all the interpreters and does in the IDE itself.
2014-04-11 15:24:26 -07:00
Paul Kulchenko
9284a24750 Reduced the line number margin width and default font size (ref #305). 2014-04-10 21:57:35 -07:00
Paul Kulchenko
c8df92f8a0 Disable function call indicator by default to reduce clutter (ref #305). 2014-04-10 21:09:35 -07:00
Paul Kulchenko
1ae6ffe7f7 Updated default folding to use lighter colors (ref #305). 2014-04-10 21:08:11 -07:00
Paul Kulchenko
ae561ff880 Updated default colors to have less contrast (ref #305). 2014-04-10 21:08:01 -07:00
Paul Kulchenko
da584b9e0a Removed unused parameters. 2014-04-10 20:39:37 -07:00
Paul Kulchenko
809ba33187 Rearranged global functions in lua spec for simpler grouping (ref #79). 2014-04-09 23:10:24 -07:00
Paul Kulchenko
844626c6f4 Added opening a new tab on double click on tab background (ref #305). 2014-04-09 16:34:40 -07:00
Paul Kulchenko
b498140958 Upgraded debugger (mobdebug 0.553) to fix an issue with STEP command. 2014-04-06 15:41:33 -07:00
Paul Kulchenko
68ccda4ef5 Updated Open file dialog to use current file or project location (closes #303). 2014-04-05 19:20:21 -07:00
Paul Kulchenko
5632913e40 Disabled startng multiple find-in-files searches. 2014-04-03 17:44:50 -07:00
Paul Kulchenko
7496040f34 Fixed an error after manual canceling Find-in-files dialog. 2014-04-03 16:01:44 -07:00
Paul Kulchenko
2b7f0f4ad6 Increased wait time for Gideros player to start for more reliable launching. 2014-04-02 17:26:03 -07:00
Paul Kulchenko
f323cc967b Updated Moai API for v1.5 (thanks to @DanielSWolf). 2014-04-01 21:59:31 -07:00
Paul Kulchenko
9cd361989c Added ActivateItem method to the filetree API (ref #166). 2014-03-31 16:45:49 -07:00
Paul Kulchenko
c5df05f03e Fixed an issue with deleting column-based selection (fixes #300). 2014-03-31 16:10:03 -07:00
Paul Kulchenko
3709f61fc4 Added onFiletree* package events (ref #166). 2014-03-31 16:09:34 -07:00
Paul Kulchenko
59caf6cae5 Cleaned unused variables and functions based on static analysis. 2014-03-29 21:03:03 -07:00
Paul Kulchenko
44df223ac5 Added setting margin mask to allow for different margin order. 2014-03-29 20:28:42 -07:00
Paul Kulchenko
2dd3209682 Made jump-to-line in the Output window to work faster and more reliably. 2014-03-28 15:51:50 -07:00
Paul Kulchenko
0c52cde070 Removed explicit margin numbers to make configuraton simpler. 2014-03-28 15:08:17 -07:00
Paul Kulchenko
b734637eb5 Fixed an error in variable indicator processing. 2014-03-28 14:03:30 -07:00
Paul Kulchenko
4c01a530a0 Removed no longer used code. 2014-03-27 22:21:22 -07:00
Paul Kulchenko
8d965cca32 Updated autoanalyzer option to more common spelling (analizer -> analyzer). 2014-03-25 20:45:12 -07:00
Paul Kulchenko
01eab250eb Reorganized and updated configuration examples. 2014-03-23 20:56:21 -07:00
Paul Kulchenko
6b9adf1527 Fixed looping when valuetype creates self-reference in APIs (ref #297). 2014-03-22 16:56:28 -07:00
Paul Kulchenko
68a8fcec47 Added support for switching breakpoints at run-time (closes #288). 2014-03-20 22:27:51 -07:00
Paul Kulchenko
13f1174bb7 Fixed elseif auto-indentation (fixes #294). 2014-03-20 15:52:14 -07:00
Paul Kulchenko
d370279d24 Merge branch 'detach-debugger' 2014-03-20 10:02:29 -07:00
Paul Kulchenko
1bbefcbf7b Disabled editing/dragging of the project directory in the filetree. 2014-03-19 16:47:44 -07:00
Paul Kulchenko
dd3ce4d1e7 Added stopping debugging when debugger server is detached/stopped. 2014-03-19 16:08:39 -07:00
Paul Kulchenko
fdc17f1579 Added opening file on one-click in icon/padding area in the filetree. 2014-03-19 15:47:03 -07:00
madmaxoft
02bbdf17f8 Added AnalyzeString function.
This will be used by the MCServer package to analyze the entire plugin at once.
2014-03-19 19:14:25 +01:00
Paul Kulchenko
8492df40de Removed border around editor components. 2014-03-18 19:27:52 -07:00
Paul Kulchenko
8b9197552c Added zooming for Output/Console windows (ref #290). 2014-03-18 11:50:01 -07:00
Paul Kulchenko
377a4861ee Added IDs for Zoom menu items (ref #290). 2014-03-18 11:49:01 -07:00
Mika Attila
241a5d13c3 Add zoom actions with appropriate keyboard shortcuts to View menu 2014-03-18 19:16:41 +01:00
Paul Kulchenko
344ed6fd80 Added detaching debugger server. 2014-03-18 10:55:33 -07:00
Paul Kulchenko
bd03b4c4d1 Upgraded copas to the current version (v1.2.1). 2014-03-18 10:52:07 -07:00
Paul Kulchenko
4ba03afa8c Added skipping reporting for known globals in static analysis (closes #286).
Globals are checked against the API descriptions registered for the
current file (based on the interpreter configuration).
2014-03-17 14:14:29 -07:00
Paul Kulchenko
cb6971e37d Moved code to populate wx and wxstc descriptions to API files. 2014-03-17 14:09:59 -07:00
Paul Kulchenko
9c3b35d8fe Added support for running zbstudio script from any folder on OSX. 2014-03-16 21:04:08 -07:00
Paul Kulchenko
82f62d4d7d Fixed focus for Find field in the find dialog on some instances of OSX.
This is an additional fix for c4c3e7e5. It turned out that some instances
of OSX 10.9.2 require SetFocus to be called after form is shown.
2014-03-16 21:04:06 -07:00
Paul Kulchenko
38c56c4d31 Updated auto-complete to show in IDLE event for smoother processing. 2014-03-16 21:04:05 -07:00
SiENcE
683218a909 -minor color changes to notepad++ colorscheme 2014-03-16 09:14:08 +01:00
181 changed files with 17123 additions and 14440 deletions

View File

@@ -1,5 +1,372 @@
# 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
- Added support for OpenResty/Nginx, moonscript, and Lapis debugging.
- Added re-indentation of selected fragment or entire file.
- Added line mapping support for debugging Lua-based languages (e.g. moonscript).
- Added `editor.wrapindentmode` and `editor.wrapstartindent` settings.
- Fixed debugger compatibility with Lua 5.2.
- Fixed `F2` shortcut not working in file tree and watch panel.
- Fixed replace-in-files when saving backup copy is turned off.
### Special thanks
- To [sclark39](https://github.com/sclark39) for `format.apptitle` option to format IDE title.
- To [Christoph Kubisch](https://github.com/pixeljetstream) for glslc improvements.
- To [Yonaba](https://github.com/Yonaba/) for updated French translation.
### Improvements
- Added support for nginx debugging (Mobdebug 0.564).
- Added support for custom debugger initializer (global or interpreter-based).
- Added line mapping support for debugging Lua-based languages (e.g. moonscript).
- Added support to force local execution in console by prepending `!` (#326).
- Added setting proper `arg[0]` value during debugging (fixes #329).
- Added double click navigation in the Output window for unnamed files.
- Added centering of line after double click in the Output window.
- Added `editor.wrapindentmode` and `editor.wrapstartindent` settings.
- Added a workaround for focus switching between controls on OSX (#89, #327).
- Added assertion to ensure inserted editor is not in the notebook already.
- Added `format.apptitle` option to format IDE title (thanks to @sclark39).
- Added restoring cursor position after sorting/re-indenting.
- Added `onEditorUserlistSelection` event for userlist selection (#166).
- Added `onEditorAction` event for cut/copy/paste actions (#166).
- Added package `GetEditorWithFocus` method (#166).
- Added `editor.extradescent` option for line spacing (#305).
- Added centering of line on page after re-loading file with a known position.
- Added re-indentation of selected fragment or entire file (closes #324).
- Added sorting of the entire file if nothing is selected.
- Added `Edit | Source` sub-menu.
- Added centering line on page after bookmark navigation.
- Added `GetProjectTree`, `GetWatch`, and `GetStack` package calls (#166).
- Added bookmark-toggle toolbar icon (#233).
- Disabled message on failure to read symlinked folder content on Windows.
- Disabled breakpoint toggling when editor is not in focus.
- Disabled changing toolbar color with `auxwindow` as it only works for the dropdown.
- Increase font size for code fragments in markup (#305).
- glslc: change domain detection to be compatible with file.comp.glsl and file.tese
- Removed checks for specific errors in Local/Remote console.
- Removed focus handling workaround for editor tab changes (#89, #327).
- Renamed `menuformatrecentprojects` to `format.menurecentprojects` (#305).
- Removed handling of project dropdown in menu as it's no longer needed (#305).
- Reorganized menu shortcut conflict handling (#233).
- simplified glslc usage (compile and link based on file extensions)
- treat unreal shaders as hlsl
- Updated auto-complete logic to use configured spec separators.
- Updated logic for populating placeholders in dropdown menus.
- Updated french translation (thanks to @Yonaba)
- Updated menu items to stay enabled only when appropriate object has focus.
- Updated indentation logic for if/elseif/while/for split into 2+ lines (#324).
- Updated indentation logic to ignore comments (#324).
- Updated README with supported engines and installation instructions.
- Updated breakpoint-toggle toolbar icon to better match other icons (#305).
- Updated bookmark navigation to wrap around (#233).
- Updating sorting to keep original line endings.
- Upgraded metalua to v0.7.2.
### Fixes
- Fixed setting control focus when the main frame is hidden.
- Fixed loading packages with dashes in filenames (fixes #330).
- Fixed toolbar to stay shown after failure to start debugging.
- Fixed focus on the editor after closing a dialog on OSX (fixes #328).
- Fixed crash on OSX when changing focus while the app is being closed (#327).
- Fixed some toolbar buttons being enabled with no editor tab open.
- Fixed toolbar stealing focus after closing floating panels and dropdowns (#327).
- Fixed restoring control focus when the app gets focus on OSX (fixes #327).
- Fixed activating editor when starting the app on OSX (#327).
- Fixed auto-complete to not offer the word the cursor is on.
- Fixed hiding auto-complete when the only option matches what's typed.
- Fixed an error when all editor tabs are closed.
- Fixed replace-in-files when saving backup copy is turned off.
- Fixed re-indenting of anonymous functions in tables (#324).
- Fixed `F2` shortcut not working in file tree and watch panel (#233).
- Fixed debugger compatibility with Lua 5.2 (Mobdebug 0.561).
## v0.60 (May 11 2014)
### Highlights
- Added support for switching breakpoints at run-time.
- Added bookmark handling.
- Added `Detach process` command to stop debugging and continue process.
- Added detaching debugger server.
- Added showing/hiding toolbar and status bar.
- Simplified user interface and updated application icons.
- Updated love2d API for v0.9.1.
- Updated Moai API for v1.5.
- Added `outputshell.usewrap` to set Output wrapping; on by default.
- Added `editor.wrapflags` to configure line wrapping indicators.
- Added `editor.foldflags`; set default to draw one line when folded.
- Added `editor.foldtype` with box, cirle, arrow, and plus types.
- Added `editor.extraascent` option to add line spacing.
### Special thanks
- To [bartoleo](https://github.com/bartoleo) for italian translation update.
- To [riidom](https://github.com/riidom) for german translation update.
- To [sclark39](https://github.com/sclark39) for Copy Full Path implementation.
- To [DanielSWolf](https://github.com/DanielSWolf) for Moai API update for v1.5.
- To [madmaxoft](https://github.com/madmaxoft) for AnalyzeString patch.
- To [crumblingstatue](https://github.com/crumblingstatue) for Zoom update.
- To [SiENcE](https://github.com/SiENcE) for notepad++ colorscheme update.
### Improvements
- Added new italian translations (thanks to @bartoleo)
- Added Russian translation for new messages (#70).
- Adding Copy Full Path to editor tabs, and a Clear Output Window option to the Output tab (thanks to @sclark39)
- Added support for packages in config files (#166).
- Added formatting for Recent Projects menu (#305).
- Added `Detach process` command to stop debugging and continue process.
- Added re/docking of Watch/Stack notebooks on tab background doubleclick (#305).
- Added bookmark handling (closes #233).
- Added `Clear items` to the Recent Files menu (ref #305).
- Added recent files dropdown to the toolbar (ref #305).
- Added applying new UI settings after upgrade (ref #305).
- Added toolbar button dropdown with recent projects (ref #305).
- Added `Choose Project Directory` to the toolbar (ref #305).
- Added floating/docking of notebooks on tab background doubleclick (ref #305).
- Added Recent Project menu refresh after switching projects (ref #305).
- Added setting project directory by renaming the filetree root element (#305).
- Added filetree popup menu with the list of projects (ref #305).
- Added 'Recent Projects' menu (ref #305).
- Added package `GetLaunchedProcess` call (ref #166).
- Added `IsRunning` and `IsConnected` API calls for the debugger (ref #166).
- Added `editor.wrapflags` to configure line wrapping indicators (ref #305).
- Added explicit sorting of files in the filetree.
- Added showing/hiding of the status bar (ref #305).
- Added auto-showing toolbar when debugging starts (ref #305).
- Added showing/hiding of the toolbar (ref #305).
- Added `outputshell.usewrap` to set Output wrapping; on by default (ref #305).
- Added `editor.foldflags`; set default to draw one line when folded (ref #305).
- Added `editor.extraascent` option to add line spacing (ref #305).
- Added explicit conversion to number for numeric settings.
- Added `editor.foldtype` with box, cirle, arrow, and plus types (ref #305).
- Added opening a new tab on double click on tab background (ref #305).
- Added ActivateItem method to the filetree API (ref #166).
- Added onFiletree* package events (ref #166).
- Added setting margin mask to allow for different margin order.
- Added support for switching breakpoints at run-time (closes #288).
- Added stopping debugging when debugger server is detached/stopped.
- Added opening file on one-click in icon/padding area in the filetree.
- Added AnalyzeString function (thanks to @madmaxoft).
- Added zooming for Output/Console windows (ref #290).
- Added IDs for Zoom menu items (ref #290).
- Add zoom actions with appropriate keyboard shortcuts to View menu (thanks to @crumblingstatue)
- Added detaching debugger server.
- Added skipping reporting for known globals in static analysis (closes #286).
- Added support for running zbstudio script from any folder on OSX.
- Adjusted `code` color in the comment markup for better visibility (#305).
- Changed order of stopping debugger and saving settings (ref #305).
- Cleaned unused variables and functions based on static analysis.
- Disallowed closing Output/Console/Project tabs (fixes #310).
- Disabled current project on the recent projects list (ref #305).
- Disable function call indicator by default to reduce clutter (ref #305).
- Disabled startng multiple find-in-files searches.
- Disabled editing/dragging of the project directory in the filetree.
- Enabled editor width auto-adjustment when wrapping is off.
- Enable retina support (`hidpi=true`) by default on OSX (#305).
- Increased default font size in the editor (ref #305).
- Increased wait time for Gideros player to start for more reliable launching.
- Made fold and marker margins wider (ref #305).
- Made jump-to-line in the Output window to work faster and more reliably.
- Moved `Project Directory` menu item lower to not activate on OSX (ref #305).
- Moved code to populate `wx` and `wxstc` descriptions to API files.
- Rearranged global functions in lua spec for simpler grouping (ref #79).
- Reduced sash (border between subsections) in all notebooks (ref #305).
- Reduced the line number margin width and default font size (ref #305).
- Refactored editor config references.
- Removed `Clear Dynamic Words` menu as it's rarely used.
- Removed the gripper on the toolbar (ref #305).
- Removed project selection dropdown from the filetree (ref #305).
- Removed paragraph conversion from love2d API conversion script (ref #247).
- Removed border around Output/Console panels (ref #305).
- Removed deprecated `startfile` interpreter option.
- Removed explicit margin numbers to make configuraton simpler.
- Removed border around editor components.
- Reordered markers to keep the curent line marker on top (#305).
- Reorganized and updated configuration examples.
- Set def linenumber font size as one smaller than editor font size (ref #305).
- Switched to plain background for the toolbar (ref #305).
- Switched to AuiToolBar as it provides buttons with dropdowns (ref #305).
- Upgraded Mobdebug (0.56).
- Upgraded debugger (mobdebug 0.553) to fix an issue with STEP command.
- Upgraded copas to the current version (v1.2.1).
- Updated default fonts for Windows and Linux for better looking ones (#305).
- Update de.lua (thanks to @riidom)
- Updated language files with new messages (#70).
- Updated copyright messages.
- Updated `Go To Line` menu item and its translations.
- Updated build scripts with a fix for a wxlua compilation issue (#260).
- Updated build prerequisites Linux install script.
- Updated default indicator color to more neutral one (#305).
- Updated OSX build script to use 10.7 SDK with 10.6 min-version (#260).
- Updated Mobdebug (0.555) to add support for `pause` debugger call.
- Updated lua interpreter to remove caching of executable path.
- Updated resetting pid only after completing non-debbugged process.
- Updated shortcut for Recent File navigation (ref #305).
- Updated application icons (ref #305).
- Updated stack/watch panel captions to be invisible (ref #305).
- Updated interpreters to check `ProgramFiles` env variable on Windows.
- Updated panel captions to be invisible (rev #305).
- Updated 'window unhide' logic (Windows only) to be called less frequently.
- Updated love2d interpreter to not hide the application window.
- Updated file sorting to be case-insensitive on all platforms.
- Updated filetree menu to use 'Edit Project Directory' on root element (#305).
- Updated love2d API to fix typos in descriptions (ref #247).
- Updated love2d API for v0.9.1 (ref #247).
- Updated love2d API conversion script to handle top-level functions (ref #247).
- Updated `PackageUnRegister` call to return the package on success (ref #166).
- Updated fold/wrap flag handling to work with wxwidgets 2.8 (ref #305).
- Updated breakpoint/currentline markers for less contrast colors (ref #305).
- Updated default folding to use lighter colors (ref #305).
- Updated default colors to have less contrast (ref #305).
- Updated Open file dialog to use current file or project location (closes #303).
- Updated Moai API for v1.5 (thanks to @DanielSWolf).
- Updated `autoanalyzer` option to more common spelling (analizer -> analyzer).
- Updated auto-complete to show in IDLE event for smoother processing.
- -minor color changes to notepad++ colorscheme (thanks to @SiENcE).
### Incompatibilities
- Added opening a new tab on double click on tab background.
- Added re/docking of Watch/Stack notebooks on tab background doubleclick.
- Enabled retina support (`hidpi=true`) by default on OSX.
- Removed deprecated `startfile` interpreter option; use `startwith` option instead.
- Updated file sorting to be case-insensitive on all platforms.
- Updated `autoanalyzer` option to more common spelling (analizer -> analyzer).
- wxwidgets 2.8 is no longer supported (wxwidgets 2.9.5+ required).
### Fixes
- Fixed Corona interpreter to clean debugger in `plugin` folder (Win).
- Fixed file tree activation of a deleted file.
- Fixed switching to full screen and restoring status bar on OSX (ref #305).
- Fixed right-click handling in filetree on OSX broken by 3709f61f (ref #166).
- Fixed usage of `self` in one of package API calls (ref #166).
- Fixed find dialog to stay on top after search directory selection on OSX.
- Fixed search result navigation after clicking beyond the end of line.
- Fixed an issue with running processes not terminated when closing IDE.
- Fixed an error after manual canceling Find-in-files dialog.
- Fixed an issue with deleting column-based selection (fixes #300).
- Fixed an error in variable indicator processing.
- Fixed looping when `valuetype` creates self-reference in APIs (ref #297).
- Fixed `elseif` auto-indentation (fixes #294).
- Fixed focus for Find field in the find dialog on some instances of OSX.
## v0.50 (Mar 10 2014)
### Highlights

View File

@@ -1,8 +1,8 @@
# Project Description
[ZeroBrane Studio](http://studio.zerobrane.com/) is a lightweight Lua IDE with code completion, syntax
highlighting, remote debugger, code analyzer, live coding, and debugging
support for several Lua engines (LuaJIT,
[ZeroBrane Studio](http://studio.zerobrane.com/) is a lightweight cross-platform Lua IDE with code completion,
syntax highlighting, remote debugger, code analyzer, live coding,
and debugging support for several Lua engines (LuaJIT,
[Löve 2D](http://notebook.kulchenko.com/zerobrane/love2d-debugging),
[Moai](http://notebook.kulchenko.com/zerobrane/moai-debugging-with-zerobrane-studio),
[Gideros](http://notebook.kulchenko.com/zerobrane/gideros-debugging-with-zerobrane-studio-ide),
@@ -10,7 +10,11 @@ support for several Lua engines (LuaJIT,
[Marmalade Quick](http://notebook.kulchenko.com/zerobrane/marmalade-quick-debugging-with-zerobrane-studio),
[Cocos2d-x](http://notebook.kulchenko.com/zerobrane/cocos2d-x-simulator-and-on-device-debugging-with-zerobrane-studio),
[GSL-shell](http://notebook.kulchenko.com/zerobrane/gsl-shell-debugging-with-zerobrane-studio),
MobileLua, and others). It originated from the [Estrela Editor](http://www.luxinia.de/index.php/Estrela/).
[Adobe Lightroom](http://notebook.kulchenko.com/zerobrane/debugging-lightroom-plugins-zerobrane-studio-ide),
[OpenResty/Nginx](http://notebook.kulchenko.com/zerobrane/debugging-openresty-nginx-lua-scripts-with-zerobrane-studio)
and others). It originated from the [Estrela Editor](http://www.luxinia.de/index.php/Estrela/).
![ZeroBrane Studio debugger screenshot](http://studio.zerobrane.com/images/debugging.png)
## Features
@@ -45,16 +49,17 @@ GSL-shell, and other engines.
* [Tutorials and demos](http://studio.zerobrane.com/tutorials.html) that cover debugging and live coding for different environments.
* [Tips and tricks](http://studio.zerobrane.com/doc-tips-and-tricks.html).
## Screenshot
## Installation
![ZeroBrane Studio debugger screenshot](http://studio.zerobrane.com/images/debugging.png)
ZeroBrane Studio can be installed into and run from any folder.
No compilation is needed, although the scripts to compile required libraries for Windows, OSX, and Linux platforms are available in the `build/` folder.
## Usage
```
Open file(s):
zbstudio <filename> [<filename>...]
any non-option will be treated as filename
any non-option will be treated as a file to open or a directory to set as the project directory
Set project directory:
zbstudio <project directory> [<filename>...]
@@ -69,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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,24 @@
local api = {}
for key in pairs(wx) do
api[key] = {
type = (type(wx[key]) == "function" and "function" or "value"),
description = "",
returns = "",
}
local function populateAPI(t)
local api = {}
for k,v in pairs(t) do
api[k] = {
type = (type(v) == "function" and "function" or "value"),
description = "",
returns = "",
}
end
return api
end
return {
wx = {
type = "lib",
description = "WX lib",
childs = api
}
}
wx = {
type = "lib",
description = "wx lib",
childs = populateAPI(wx),
},
wxstc = {
type = "lib",
description = "wxSTC lib",
childs = populateAPI(wxstc),
},
}

View File

@@ -7,3 +7,5 @@ sudo yum install git
sudo yum install svn
sudo yum install cmake
sudo yum install gtk2-devel
sudo yum install wget
sudo yum install sqlite

View File

@@ -164,6 +164,10 @@ if [ $BUILD_WXLUA ]; then
# the following patches wxlua source to fix live coding support in wxlua apps
# http://www.mail-archive.com/wxlua-users@lists.sourceforge.net/msg03225.html
sed -i 's/\(m_wxlState = wxLuaState(wxlState.GetLuaState(), wxLUASTATE_GETSTATE|wxLUASTATE_ROOTSTATE);\)/\/\/ removed by ZBS build process \/\/ \1/' modules/wxlua/wxlcallb.cpp
# (temporary) fix for compilation issue in wxlua using wxwidgets 3.1+ (r238)
sed -i 's/{ "wxSTC_COFFEESCRIPT_HASHQUOTEDSTRING", wxSTC_COFFEESCRIPT_HASHQUOTEDSTRING },/\/\/ removed by ZBS build process/' modules/wxbind/src/wxstc_bind.cpp
cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX="$INSTALL_DIR" -DCMAKE_BUILD_TYPE=MinSizeRel -DBUILD_SHARED_LIBS=FALSE \
-DwxWidgets_CONFIG_EXECUTABLE="$INSTALL_DIR/bin/wx-config" \
-DwxWidgets_COMPONENTS="stc;html;aui;adv;core;net;base" \

View File

@@ -9,7 +9,7 @@ INSTALL_DIR="$PWD/deps"
# Mac OS X global settings
MACOSX_ARCH="i386"
MACOSX_VERSION="10.6"
MACOSX_SDK_PATH="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk"
MACOSX_SDK_PATH="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk"
# number of parallel jobs used for building
MAKEFLAGS="-j4"
@@ -191,6 +191,10 @@ if [ $BUILD_WXLUA ]; then
# the following patches wxlua source to fix live coding support in wxlua apps
# http://www.mail-archive.com/wxlua-users@lists.sourceforge.net/msg03225.html
sed -i "" 's/\(m_wxlState = wxLuaState(wxlState.GetLuaState(), wxLUASTATE_GETSTATE|wxLUASTATE_ROOTSTATE);\)/\/\/ removed by ZBS build process \/\/ \1/' modules/wxlua/wxlcallb.cpp
# (temporary) fix for compilation issue in wxlua using wxwidgets 3.1+ (r238)
sed -i 's/{ "wxSTC_COFFEESCRIPT_HASHQUOTEDSTRING", wxSTC_COFFEESCRIPT_HASHQUOTEDSTRING },/\/\/ removed by ZBS build process/' modules/wxbind/src/wxstc_bind.cpp
cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX="$INSTALL_DIR" -DCMAKE_BUILD_TYPE=$WXLUABUILD -DBUILD_SHARED_LIBS=FALSE \
-DCMAKE_OSX_ARCHITECTURES=$MACOSX_ARCH -DCMAKE_OSX_DEPLOYMENT_TARGET=$MACOSX_VERSION $MINSDK \
-DCMAKE_C_COMPILER=/usr/bin/gcc -DCMAKE_CXX_COMPILER=/usr/bin/g++ -DwxWidgets_CONFIG_EXECUTABLE="$INSTALL_DIR/bin/wx-config" \

View File

@@ -202,6 +202,9 @@ if [ $BUILD_WXLUA ]; then
# (temporary) fix for compilation issue in wxlua in Windows using mingw (r184)
sed -i 's/defined(__MINGW32__) || defined(__GNUWIN32__)/0/' modules/wxbind/src/wxcore_bind.cpp
# (temporary) fix for compilation issue in wxlua using wxwidgets 3.1+ (r238)
sed -i 's/{ "wxSTC_COFFEESCRIPT_HASHQUOTEDSTRING", wxSTC_COFFEESCRIPT_HASHQUOTEDSTRING },/\/\/ removed by ZBS build process/' modules/wxbind/src/wxstc_bind.cpp
[ -f "$INSTALL_DIR/lib/libwxscintilla-3.0.a" ] && cp "$INSTALL_DIR/lib/libwxscintilla-3.0.a" "$INSTALL_DIR/lib/libwx_mswu_scintilla-3.0.a"
[ -f "$INSTALL_DIR/lib/libwxscintilla-3.1.a" ] && cp "$INSTALL_DIR/lib/libwxscintilla-3.1.a" "$INSTALL_DIR/lib/libwx_mswu_scintilla-3.1.a"

View File

@@ -13,6 +13,7 @@ return {
["&Delete"] = nil, -- src\editor\filetree.lua
["&Documentation"] = nil, -- src\editor\menu_help.lua
["&Down"] = "往下", -- src\editor\findreplace.lua
["&Edit Project Directory"] = nil, -- src\editor\filetree.lua
["&Edit Watch"] = "编辑监视", -- src\editor\debugger.lua
["&Edit"] = "编辑", -- src\editor\menu_edit.lua
["&File"] = "文件", -- src\editor\menu_file.lua
@@ -22,7 +23,7 @@ return {
["&Fold/Unfold All"] = "全 折叠/展开", -- src\editor\menu_edit.lua
["&Frequently Asked Questions"] = nil, -- src\editor\menu_help.lua
["&Getting Started Guide"] = nil, -- src\editor\menu_help.lua
["&Goto Line"] = "到...行", -- src\editor\menu_search.lua
["&Go To Line..."] = "到...行", -- src\editor\menu_search.lua
["&Help"] = "帮助", -- src\editor\menu_help.lua
["&New Directory"] = nil, -- src\editor\filetree.lua
["&New"] = "新建", -- src\editor\menu_file.lua
@@ -41,7 +42,9 @@ return {
["&Sort"] = "分类", -- src\editor\menu_edit.lua
["&Stack Window"] = "叠视窗/堆栈视窗", -- src\editor\menu_view.lua
["&Start Debugger Server"] = "开启除错器伺服机", -- src\editor\menu_project.lua
["&Status Bar"] = nil, -- src\editor\menu_view.lua
["&Subdirectories"] = "子文件夹", -- src\editor\findreplace.lua
["&Tool Bar"] = nil, -- src\editor\menu_view.lua
["&Tutorials"] = nil, -- src\editor\menu_help.lua
["&Undo"] = "撤消", -- src\editor\menu_edit.lua, src\editor\editor.lua
["&Up"] = "往上", -- src\editor\findreplace.lua
@@ -57,6 +60,7 @@ return {
["Analyze"] = "分析", -- src\editor\inspect.lua
["Auto Complete Identifiers"] = "自动补全标识符", -- src\editor\menu_edit.lua
["Auto complete while typing"] = "当输入时自动补全", -- src\editor\menu_edit.lua
["Bookmark"] = nil, -- src\editor\menu_edit.lua
["Break execution at the next executed line of code"] = "执行下一语句之后中断执行", -- src\editor\gui.lua, src\editor\menu_project.lua
["C&lear Output Window"] = "清除输出视窗", -- src\editor\menu_project.lua
["C&omment/Uncomment"] = "注释/消除注释", -- src\editor\menu_edit.lua
@@ -67,11 +71,13 @@ return {
["Can't start debugger server at %s:%d: %s."] = nil, -- src\editor\debugger.lua
["Can't start debugging session due to internal error '%s'."] = "除错动作失败 '%s'.", -- src\editor\debugger.lua
["Can't start debugging without an opened file or with the current file not being saved ('%s')."] = "不能启动除错,没有文档被开启或当前更改过的文档还没保存('%s')", -- src\editor\debugger.lua
["Can't stop debugger server as it is not started."] = nil, -- src\editor\debugger.lua
["Cancel"] = "取消", -- src\editor\findreplace.lua
["Cancelled by the user."] = nil, -- src\editor\findreplace.lua
["Choose..."] = "请选...", -- src\editor\menu_project.lua
["Choose a project directory"] = "选择项目文件夹", -- src\editor\findreplace.lua, src\editor\menu_project.lua
["Clear &Dynamic Words"] = "清除动态词汇", -- src\editor\menu_edit.lua
["Choose a project directory"] = "选择项目文件夹", -- src\editor\findreplace.lua, src\editor\menu_project.lua, src\editor\gui.lua, src\editor\filetree.lua
["Choose..."] = "请选...", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Clear Items"] = nil, -- src\editor\menu_file.lua
["Clear items from this list"] = nil, -- src\editor\menu_file.lua
["Clear the output window before compiling or debugging"] = "编译或除错前清除输出视窗", -- src\editor\menu_project.lua
["Close &Other Pages"] = "关闭其他页面", -- src\editor\gui.lua
["Close A&ll Pages"] = "关闭全部页面", -- src\editor\gui.lua
@@ -94,9 +100,11 @@ return {
["Cu&t"] = "剪切", -- src\editor\menu_edit.lua, src\editor\editor.lua
["Cut selected text to clipboard"] = "剪切被选的text到clipboard", -- src\editor\menu_edit.lua
["Debugger server started at %s:%d."] = "除错伺服器起始于 %s:%d.", -- src\editor\debugger.lua
["Debugger server stopped at %s:%d."] = nil, -- src\editor\debugger.lua
["Debugging session completed (%s)."] = "除错会话完成 (%s)", -- src\editor\debugger.lua
["Debugging session started in '%s'."] = "除错会话于 '%s' 起始", -- src\editor\debugger.lua
["Debugging suspended at %s:%s (couldn't activate the file)."] = "除错挂起于 %s:%s (不能激活文档).", -- src\editor\debugger.lua
["Detach &Process"] = nil, -- src\editor\menu_project.lua
["Directory"] = "文件夹", -- src\editor\findreplace.lua
["Do you want to delete '%s'?"] = nil, -- src\editor\filetree.lua
["Do you want to overwrite it?"] = nil, -- src\editor\commands.lua
@@ -140,8 +148,10 @@ return {
["Found"] = "找到", -- src\editor\findreplace.lua
["Full &Screen"] = "全屏", -- src\editor\menu_view.lua
["Go To Definition"] = nil, -- src\editor\editor.lua
["Go To Line"] = "到...行", -- src\editor\menu_search.lua
["Go To Next Bookmark"] = nil, -- src\editor\menu_edit.lua
["Go To Previous Bookmark"] = nil, -- src\editor\menu_edit.lua
["Go to a selected line"] = "到所选的行", -- src\editor\menu_search.lua
["Goto Line"] = "到...行", -- src\editor\menu_search.lua
["INS"] = "INS", -- src\editor\editor.lua
["In Files"] = "在档案里", -- src\editor\findreplace.lua
["Jump to a function definition..."] = "跳到函数定义", -- src\editor\editor.lua
@@ -171,13 +181,15 @@ return {
["Program starting as '%s'."] = "程式以 '%s' 执行", -- src\editor\output.lua
["Program stopped (pid: %d)."] = "程式停止 (pid: %d).", -- src\editor\debugger.lua
["Program unable to run as '%s'."] = "程式不能以 '%s' 执行", -- src\editor\output.lua
["Project Directory"] = "项目文件夹", -- src\editor\menu_project.lua
["Project"] = "项目", -- src\editor\gui.lua, src\editor\settings.lua
["Project Directory"] = "项目文件夹", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Project history"] = nil, -- src\editor\menu_file.lua
["Project"] = "项目", -- src\editor\gui.lua
["Project/&FileTree Window"] = "项目/文档树 视窗", -- src\editor\menu_view.lua
["Provide command line parameters"] = nil, -- src\editor\menu_project.lua
["R/O"] = "唯读", -- src\editor\editor.lua
["R/W"] = "读写", -- src\editor\editor.lua
["Re&place In Files"] = "在文档中替换", -- src\editor\menu_search.lua
["Recent &Projects"] = nil, -- src\editor\menu_file.lua
["Recent Files"] = "最近的文档", -- src\editor\menu_file.lua
["Redo last edit undone"] = "重做最后被取消的编辑", -- src\editor\menu_edit.lua
["Refused a request to start a new debugging session as there is one in progress already."] = "因为有另一个除错在进行,拒绝开启新的除错对话", -- src\editor\debugger.lua
@@ -190,7 +202,6 @@ return {
["Replaced"] = "更换", -- src\editor\findreplace.lua
["Replacing"] = "更换中", -- src\editor\findreplace.lua
["Reset to default layout"] = "重置缺省布局", -- src\editor\menu_view.lua
["Resets the dynamic word list for autocompletion"] = "为自动补全重置动态word list", -- src\editor\menu_edit.lua
["Run as Scratchpad"] = "以Scratchpad执行", -- src\editor\menu_project.lua
["S&top Debugging"] = "停止除错", -- src\editor\menu_project.lua
["S&top Process"] = "停止进程", -- src\editor\menu_project.lua
@@ -206,6 +217,7 @@ return {
["Scope"] = "范围", -- src\editor\findreplace.lua
["Scratchpad error"] = "暂存器错误", -- src\editor\debugger.lua
["Searching for"] = "搜索", -- src\editor\findreplace.lua
["Sel: %d/%d"] = nil, -- src\editor\editor.lua
["Select &All"] = "选全部", -- src\editor\menu_edit.lua, src\editor\editor.lua
["Select all text in the editor"] = "选编辑器内的所有text", -- src\editor\menu_edit.lua
["Select and Find Next"] = nil, -- src\editor\menu_search.lua
@@ -215,12 +227,14 @@ return {
["Set From Current File"] = "从当前文档设置", -- src\editor\menu_project.lua
["Set project directory from current file"] = "从当前文档设置项目文件夹", -- src\editor\gui.lua, src\editor\menu_project.lua
["Set the interpreter to be used"] = "设置解释器", -- src\editor\menu_project.lua
["Set the project directory to be used"] = "设置项目文件夹", -- src\editor\menu_project.lua
["Set the project directory to be used"] = "设置项目文件夹", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Settings: System"] = "设置: 系统", -- src\editor\menu_edit.lua
["Settings: User"] = "设置: 用户", -- src\editor\menu_edit.lua
["Show &Tooltip"] = "展现tooltip", -- src\editor\menu_edit.lua
["Show Location"] = nil, -- src\editor\gui.lua, src\editor\filetree.lua
["Show tooltip for current position; place cursor after opening bracket of function"] = "在当前的位置展现tooltip; 把游标放置于函数的开括号之后", -- src\editor\menu_edit.lua
["Show/Hide the status bar"] = nil, -- src\editor\menu_view.lua
["Show/Hide the toolbar"] = nil, -- src\editor\menu_view.lua
["Sort selected lines"] = "对被选的行进行排列", -- src\editor\menu_edit.lua
["Stack"] = "堆栈", -- src\editor\debugger.lua, src\editor\gui.lua
["Start &Debugging"] = "开始除错", -- src\editor\menu_project.lua
@@ -232,10 +246,12 @@ return {
["Step into"] = "除错运行 进入子程序/函数", -- src\editor\gui.lua, src\editor\menu_project.lua
["Step out of the current function"] = "除错运行 离开当前的函数", -- src\editor\gui.lua, src\editor\menu_project.lua
["Step over"] = "除错运行 掠过子程序/函数", -- src\editor\gui.lua, src\editor\menu_project.lua
["Stop debugging and continue running the process"] = nil, -- src\editor\gui.lua, src\editor\menu_project.lua
["Stop the currently running process"] = "终止目前进行着的进程", -- src\editor\gui.lua, src\editor\menu_project.lua
["Switch to or from full screen mode"] = "切换全屏模式", -- src\editor\menu_view.lua
["Text not found."] = "寻找不到text", -- src\editor\findreplace.lua
["The API file must be located in a subdirectory of the API directory."] = "API file必须存放在API文件夹中的子文件夹", -- src\editor\autocomplete.lua
["Toggle Bookmark"] = nil, -- src\editor\menu_edit.lua
["Toggle Break&point"] = "切换中断点", -- src\editor\menu_project.lua
["Toggle breakpoint"] = "切换中断点", -- src\editor\gui.lua, src\editor\menu_project.lua
["Tr&ace"] = "追踪", -- src\editor\menu_project.lua
@@ -260,6 +276,10 @@ return {
["Welcome to the interactive Lua interpreter."] = "欢迎来到互动 Lua interpreter.", -- src\editor\shellbox.lua
["Wrap ar&ound"] = "卷绕", -- src\editor\findreplace.lua
["You must save the program first."] = "必须先保存程序", -- src\editor\commands.lua
["Zoom In"] = nil, -- src\editor\menu_view.lua
["Zoom Out"] = nil, -- src\editor\menu_view.lua
["Zoom to 100%"] = nil, -- src\editor\menu_view.lua
["Zoom"] = nil, -- src\editor\menu_view.lua
["on line %d"] = "在 %d 行", -- src\editor\debugger.lua, src\editor\editor.lua, src\editor\commands.lua
["traced %d instruction"] = "追踪 %d 指令", -- src\editor\debugger.lua
["unknown error"] = nil, -- src\editor\debugger.lua

View File

@@ -14,6 +14,7 @@ return {
["&Delete"] = "&Entfernen", -- src\editor\filetree.lua
["&Documentation"] = "&Dokumentation", -- src\editor\menu_help.lua
["&Down"] = "&Runter", -- src\editor\findreplace.lua
["&Edit Project Directory"] = "Projektverzeichnis ändern", -- src\editor\filetree.lua
["&Edit Watch"] = "&Beobachtungspunkt bearbeiten", -- src\editor\debugger.lua
["&Edit"] = "&Bearbeiten", -- src\editor\menu_edit.lua
["&File"] = "&Datei", -- src\editor\menu_file.lua
@@ -23,7 +24,7 @@ return {
["&Fold/Unfold All"] = "A&lles ein-/ausklappen", -- src\editor\menu_edit.lua
["&Frequently Asked Questions"] = "&FAQ", -- src\editor\menu_help.lua
["&Getting Started Guide"] = "&Anfängerleitfaden", -- src\editor\menu_help.lua
["&Goto Line"] = "&Gehe zu Zeile", -- src\editor\menu_search.lua
["&Go To Line..."] = "&Gehe zu Zeile...", -- src\editor\menu_search.lua
["&Help"] = "&Hilfe", -- src\editor\menu_help.lua
["&New Directory"] = "&Neuer Ordner", -- src\editor\filetree.lua
["&New"] = "&Neu", -- src\editor\menu_file.lua
@@ -42,7 +43,9 @@ return {
["&Sort"] = "&Sortieren", -- src\editor\menu_edit.lua
["&Stack Window"] = "&Stapel/Stack", -- src\editor\menu_view.lua
["&Start Debugger Server"] = "De&bugserver starten", -- src\editor\menu_project.lua
["&Status Bar"] = "S&tatuszeile", -- src\editor\menu_view.lua
["&Subdirectories"] = "&Unterverzeichnisse", -- src\editor\findreplace.lua
["&Tool Bar"] = "&Werkzeugleiste", -- src\editor\menu_view.lua
["&Tutorials"] = "&Tutorien", -- src\editor\menu_help.lua
["&Undo"] = "&Rückgängig", -- src\editor\menu_edit.lua, src\editor\editor.lua
["&Up"] = "&Hoch", -- src\editor\findreplace.lua
@@ -58,6 +61,7 @@ return {
["Analyze"] = "&Analyseroutine", -- src\editor\inspect.lua
["Auto Complete Identifiers"] = "Auto-Vervollständigen von Bezeichnern", -- src\editor\menu_edit.lua
["Auto complete while typing"] = "Auto-Vervollständigen beim Tippen", -- src\editor\menu_edit.lua
["Bookmark"] = "Lese&zeichen", -- src\editor\menu_edit.lua
["Break execution at the next executed line of code"] = "Programmausführung bei der nächsten ausgeführten Zeile stoppen", -- src\editor\gui.lua, src\editor\menu_project.lua
["C&lear Output Window"] = "Ausgabefenster l&öschen", -- src\editor\menu_project.lua
["C&omment/Uncomment"] = "(Aus-)/K&ommentieren", -- src\editor\menu_edit.lua
@@ -68,11 +72,13 @@ return {
["Can't start debugger server at %s:%d: %s."] = "Kann Debugserver nicht starten (%s:%d): %s.", -- src\editor\debugger.lua
["Can't start debugging session due to internal error '%s'."] = "Debugging kann nicht gestartet werden wegen internem Fehler '%s'.", -- src\editor\debugger.lua
["Can't start debugging without an opened file or with the current file not being saved ('%s')."] = "Debugging kann ohne geöffnete Datei nicht gestartet werden oder wenn die aktuelle Datei nicht gespeichert ist ('%s').", -- src\editor\debugger.lua
["Can't stop debugger server as it is not started."] = "Kann Debugserver nicht stoppen wenn er vorher nicht gestartet wurde.", -- src\editor\debugger.lua
["Cancel"] = "Abbrechen", -- src\editor\findreplace.lua
["Cancelled by the user."] = "Durch Benutzer abgebrochen.", -- src\editor\findreplace.lua
["Choose..."] = "Wählen...", -- src\editor\menu_project.lua
["Choose a project directory"] = "Projektverzeichnis auswählen", -- src\editor\findreplace.lua, src\editor\menu_project.lua
["Clear &Dynamic Words"] = "&Dynamic Words löschen", -- src\editor\menu_edit.lua
["Choose a project directory"] = "Projektverzeichnis auswählen", -- src\editor\findreplace.lua, src\editor\menu_project.lua, src\editor\gui.lua, src\editor\filetree.lua
["Choose..."] = "Wählen...", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Clear Items"] = "Liste &löschen", -- src\editor\menu_file.lua
["Clear items from this list"] = "Diese Liste löschen", -- src\editor\menu_file.lua
["Clear the output window before compiling or debugging"] = "Vor Kompilieren oder Debuggen das Ausgabefenster löschen", -- src\editor\menu_project.lua
["Close &Other Pages"] = "A&ndere Seiten schließen", -- src\editor\gui.lua
["Close A&ll Pages"] = "&Alle Seiten schließen", -- src\editor\gui.lua
@@ -95,9 +101,11 @@ return {
["Cu&t"] = "A&usschneiden", -- src\editor\menu_edit.lua, src\editor\editor.lua
["Cut selected text to clipboard"] = "Schneide ausgewählten Text in die Zwischenablage hinein", -- src\editor\menu_edit.lua
["Debugger server started at %s:%d."] = "Debugserver gestartet als %s:%d.", -- src\editor\debugger.lua
["Debugger server stopped at %s:%d."] = "Debugserver gestoppt als %s:%d.", -- src\editor\debugger.lua
["Debugging session completed (%s)."] = "Debugging Session beendet (%s).", -- src\editor\debugger.lua
["Debugging session started in '%s'."] = "Debugging Session gestartet '%s'.", -- src\editor\debugger.lua
["Debugging suspended at %s:%s (couldn't activate the file)."] = "Debugging angehalten bei %s:%s (konnte Datei nicht aktivieren).", -- src\editor\debugger.lua
["Detach &Process"] = "Prozeß abkoppeln", -- src\editor\menu_project.lua
["Directory"] = "Verzeichnis", -- src\editor\findreplace.lua
["Do you want to delete '%s'?"] = "Soll '%s' gelöscht werden?", -- src\editor\filetree.lua
["Do you want to overwrite it?"] = "Überschreiben?", -- src\editor\commands.lua
@@ -141,8 +149,10 @@ return {
["Found"] = "Gefunden", -- src\editor\findreplace.lua
["Full &Screen"] = "&Vollbild", -- src\editor\menu_view.lua
["Go To Definition"] = "Gehe zu Definition", -- src\editor\editor.lua
["Go To Line"] = "Gehe zu Zeile", -- src\editor\menu_search.lua
["Go To Next Bookmark"] = "Zu nächstem Lesezeichen", -- src\editor\menu_edit.lua
["Go To Previous Bookmark"] = "Zu vorherigem Lesezeichen", -- src\editor\menu_edit.lua
["Go to a selected line"] = "Gehe zu ausgewählter Zeile", -- src\editor\menu_search.lua
["Goto Line"] = "Gehe zu Zeile", -- src\editor\menu_search.lua
["INS"] = "INS", -- src\editor\editor.lua
["In Files"] = "In Dateien", -- src\editor\findreplace.lua
["Jump to a function definition..."] = "Springe zu Funktions-Definition...", -- src\editor\editor.lua
@@ -172,13 +182,15 @@ return {
["Program starting as '%s'."] = "Programm gestartet als '%s'.", -- src\editor\output.lua
["Program stopped (pid: %d)."] = "Programm gestoppt (pid: %d).", -- src\editor\debugger.lua
["Program unable to run as '%s'."] = "Programm kann nicht als '%s' laufen.", -- src\editor\output.lua
["Project Directory"] = "&Projektverzeichnis", -- src\editor\menu_project.lua
["Project"] = "Projekt", -- src\editor\gui.lua, src\editor\settings.lua
["Project Directory"] = "&Projektverzeichnis", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Project history"] = "Liste bisheriger Projekte", -- src\editor\menu_file.lua
["Project"] = "Projekt", -- src\editor\gui.lua
["Project/&FileTree Window"] = "&Projekt/Datei Fenster", -- src\editor\menu_view.lua
["Provide command line parameters"] = "Kommandozeilenparameter angeben", -- src\editor\menu_project.lua
["R/O"] = "R/O", -- src\editor\editor.lua
["R/W"] = "R/W", -- src\editor\editor.lua
["Re&place In Files"] = "Ersetze in &Dateien", -- src\editor\menu_search.lua
["Recent &Projects"] = "Letzte &Projekte", -- src\editor\menu_file.lua
["Recent Files"] = "Letzte Dateien", -- src\editor\menu_file.lua
["Redo last edit undone"] = "Stelle letzte rückgängig gemachte Bearbeitung wieder her", -- src\editor\menu_edit.lua
["Refused a request to start a new debugging session as there is one in progress already."] = "Starten einer neuen Debuggingsession abgelehnt, da bereits eine läuft.", -- src\editor\debugger.lua
@@ -191,7 +203,6 @@ return {
["Replaced"] = "Ersetzt:", -- src\editor\findreplace.lua
["Replacing"] = "Am Ersetzen", -- src\editor\findreplace.lua
["Reset to default layout"] = "Standard-Layout wiederherstellen", -- src\editor\menu_view.lua
["Resets the dynamic word list for autocompletion"] = "Zurücksetzen der Liste der dynamischen Wörter für Autovervollständigung", -- src\editor\menu_edit.lua
["Run as Scratchpad"] = "Als &Entwurf starten", -- src\editor\menu_project.lua
["S&top Debugging"] = "Debugging a&nhalten", -- src\editor\menu_project.lua
["S&top Process"] = "Prozeß &anhalten", -- src\editor\menu_project.lua
@@ -207,6 +218,7 @@ return {
["Scope"] = "Richtung", -- src\editor\findreplace.lua
["Scratchpad error"] = "Fehler im Entwurf", -- src\editor\debugger.lua
["Searching for"] = "Suchen nach", -- src\editor\findreplace.lua
["Sel: %d/%d"] = "Ausgew.: %d/%d", -- src\editor\editor.lua
["Select &All"] = "&Alles Auswählen", -- src\editor\menu_edit.lua, src\editor\editor.lua
["Select all text in the editor"] = "Kompletten Text im Editor auswählen", -- src\editor\menu_edit.lua
["Select and Find Next"] = "Auswählen und nächstes finden", -- src\editor\menu_search.lua
@@ -216,12 +228,14 @@ return {
["Set From Current File"] = "Anhand der aktuellen Datei festlegen", -- src\editor\menu_project.lua
["Set project directory from current file"] = "Lege Projektverzeichnis anhand der aktuellen Datei fest", -- src\editor\gui.lua, src\editor\menu_project.lua
["Set the interpreter to be used"] = "Wähle zu benutzenden Interpreter aus", -- src\editor\menu_project.lua
["Set the project directory to be used"] = "Lege zu benutzendes Projektverzeichnis fest", -- src\editor\menu_project.lua
["Set the project directory to be used"] = "Lege zu benutzendes Projektverzeichnis fest", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Settings: System"] = "Einstellungen: System", -- src\editor\menu_edit.lua
["Settings: User"] = "Einstellungen: Nutzer", -- src\editor\menu_edit.lua
["Show &Tooltip"] = "&Tooltip zeigen", -- src\editor\menu_edit.lua
["Show Location"] = "Ordner öffnen", -- src\editor\gui.lua, src\editor\filetree.lua
["Show tooltip for current position; place cursor after opening bracket of function"] = "Zeige Tooltip für aktuelle Position; setze Cursor hinter die öffnende Klammer der Funktion", -- src\editor\menu_edit.lua
["Show/Hide the status bar"] = "Statuszeile zeigen/verstecken", -- src\editor\menu_view.lua
["Show/Hide the toolbar"] = "Werkzeugleiste zeigen/verstecken", -- src\editor\menu_view.lua
["Sort selected lines"] = "Ausgewählte Zeilen sortieren", -- src\editor\menu_edit.lua
["Stack"] = "Stack", -- src\editor\debugger.lua, src\editor\gui.lua
["Start &Debugging"] = "&Debugging starten", -- src\editor\menu_project.lua
@@ -233,10 +247,12 @@ return {
["Step into"] = "Schritt hinein", -- src\editor\gui.lua, src\editor\menu_project.lua
["Step out of the current function"] = "Schritt aus der aktuellen Funktion heraus", -- src\editor\gui.lua, src\editor\menu_project.lua
["Step over"] = "Überspringen", -- src\editor\gui.lua, src\editor\menu_project.lua
["Stop debugging and continue running the process"] = "Beende debuggen und setze den Prozeß fort", -- src\editor\gui.lua, src\editor\menu_project.lua
["Stop the currently running process"] = "Aktuell laufenden Prozeß stoppen", -- src\editor\gui.lua, src\editor\menu_project.lua
["Switch to or from full screen mode"] = "Vollbild an/aus", -- src\editor\menu_view.lua
["Text not found."] = "Text nicht gefunden.", -- src\editor\findreplace.lua
["The API file must be located in a subdirectory of the API directory."] = "Die API-Datei muß sich in einem Unterverzeichnis des API-Vereichnisses befinden.", -- src\editor\autocomplete.lua
["Toggle Bookmark"] = "Lesezeichen setzen/löschen", -- src\editor\menu_edit.lua
["Toggle Break&point"] = "&Haltepunkt an/aus", -- src\editor\menu_project.lua
["Toggle breakpoint"] = "Haltepunkt an/aus", -- src\editor\gui.lua, src\editor\menu_project.lua
["Tr&ace"] = "Ablauf &verfolgen", -- src\editor\menu_project.lua
@@ -261,6 +277,10 @@ return {
["Welcome to the interactive Lua interpreter."] = "Willkommen zum interaktiven Lua-Interpretr!", -- src\editor\shellbox.lua
["Wrap ar&ound"] = "Am Anfang fortsetzen", -- src\editor\findreplace.lua
["You must save the program first."] = "Erst das Programm speichern.", -- src\editor\commands.lua
["Zoom In"] = "Hineinzoomen", -- src\editor\menu_view.lua
["Zoom Out"] = "Herauszoomen", -- src\editor\menu_view.lua
["Zoom to 100%"] = "Zoom zurücksetzen (100%)", -- src\editor\menu_view.lua
["Zoom"] = "Zoom", -- src\editor\menu_view.lua
["on line %d"] = "in Zeile %d", -- src\editor\debugger.lua, src\editor\editor.lua, src\editor\commands.lua
["traced %d instruction"] = {"%d Anweisung verfolgt", "%d Anweisungen verfolgt"}, -- src\editor\debugger.lua
["unknown error"] = "Unbekannter Fehler", -- src\editor\debugger.lua

View File

@@ -1,6 +1,4 @@
-- Traducción realiazada por Iñigo Sola
-- para la versión ZeroBrane Studio 17bdb48
-- 10 de Noviembre de 2012
return {
[0] = function(c) return c == 1 and 1 or 2 end, -- plural
["%d instance"] = nil, -- src\editor\findreplace.lua
@@ -17,6 +15,7 @@ return {
["&Delete"] = nil, -- src\editor\filetree.lua
["&Documentation"] = nil, -- src\editor\menu_help.lua
["&Down"] = nil, -- src\editor\findreplace.lua
["&Edit Project Directory"] = nil, -- src\editor\filetree.lua
["&Edit Watch"] = "Editar observación", -- src\editor\debugger.lua
["&Edit"] = "Editar", -- src\editor\menu_edit.lua
["&File"] = "Archivo", -- src\editor\menu_file.lua
@@ -26,7 +25,7 @@ return {
["&Fold/Unfold All"] = "Plegar/desplegar todo", -- src\editor\menu_edit.lua
["&Frequently Asked Questions"] = nil, -- src\editor\menu_help.lua
["&Getting Started Guide"] = nil, -- src\editor\menu_help.lua
["&Goto Line"] = "Ir a línea", -- src\editor\menu_search.lua
["&Go To Line..."] = "Ir a línea...", -- src\editor\menu_search.lua
["&Help"] = "Ayuda", -- src\editor\menu_help.lua
["&New Directory"] = nil, -- src\editor\filetree.lua
["&New"] = "&Nuevo", -- src\editor\menu_file.lua
@@ -45,7 +44,9 @@ return {
["&Sort"] = "Clasificar", -- src\editor\menu_edit.lua
["&Stack Window"] = "Ventana de la pila de ejecución", -- src\editor\menu_view.lua
["&Start Debugger Server"] = "Lanzar servidor de depuración", -- src\editor\menu_project.lua
["&Status Bar"] = nil, -- src\editor\menu_view.lua
["&Subdirectories"] = nil, -- src\editor\findreplace.lua
["&Tool Bar"] = nil, -- src\editor\menu_view.lua
["&Tutorials"] = nil, -- src\editor\menu_help.lua
["&Undo"] = "Deshacer", -- src\editor\menu_edit.lua, src\editor\editor.lua
["&Up"] = nil, -- src\editor\findreplace.lua
@@ -61,6 +62,7 @@ return {
["Analyze"] = "Analizar", -- src\editor\inspect.lua
["Auto Complete Identifiers"] = "Autocompletar identificadores", -- src\editor\menu_edit.lua
["Auto complete while typing"] = "Autocompletar mientras se escribe", -- src\editor\menu_edit.lua
["Bookmark"] = nil, -- src\editor\menu_edit.lua
["Break execution at the next executed line of code"] = "Parar ejecución en la siguiente línea de código", -- src\editor\gui.lua, src\editor\menu_project.lua
["C&lear Output Window"] = "Limpiar ventana de Salida", -- src\editor\menu_project.lua
["C&omment/Uncomment"] = "Comentar/descomentar", -- src\editor\menu_edit.lua
@@ -71,11 +73,13 @@ return {
["Can't start debugger server at %s:%d: %s."] = nil, -- src\editor\debugger.lua
["Can't start debugging session due to internal error '%s'."] = "No se puede iniciar la sesión de depuración debido a un error interno '%s'.'", -- src\editor\debugger.lua
["Can't start debugging without an opened file or with the current file not being saved ('%s')."] = "No se puede iniciar la depuración sin abrir un archivo o si no ha sido guardado ('%s').", -- src\editor\debugger.lua
["Can't stop debugger server as it is not started."] = nil, -- src\editor\debugger.lua
["Cancel"] = nil, -- src\editor\findreplace.lua
["Cancelled by the user."] = nil, -- src\editor\findreplace.lua
["Choose..."] = nil, -- src\editor\menu_project.lua
["Choose a project directory"] = "Elegir el directorio del proyecto", -- src\editor\findreplace.lua, src\editor\menu_project.lua
["Clear &Dynamic Words"] = "Limpiar las palabras dinámicas", -- src\editor\menu_edit.lua
["Choose a project directory"] = "Elegir el directorio del proyecto", -- src\editor\findreplace.lua, src\editor\menu_project.lua, src\editor\gui.lua, src\editor\filetree.lua
["Choose..."] = nil, -- src\editor\menu_project.lua, src\editor\filetree.lua
["Clear Items"] = nil, -- src\editor\menu_file.lua
["Clear items from this list"] = nil, -- src\editor\menu_file.lua
["Clear the output window before compiling or debugging"] = "Limpiar la ventana de salida antes de compilar o depurar", -- src\editor\menu_project.lua
["Close &Other Pages"] = nil, -- src\editor\gui.lua
["Close A&ll Pages"] = nil, -- src\editor\gui.lua
@@ -98,9 +102,11 @@ return {
["Cu&t"] = "Cortar", -- src\editor\menu_edit.lua, src\editor\editor.lua
["Cut selected text to clipboard"] = "Cortar el texto selecionado al portapapeles", -- src\editor\menu_edit.lua
["Debugger server started at %s:%d."] = "Servidor de depuración inciado en %s:%s", -- src\editor\debugger.lua
["Debugger server stopped at %s:%d."] = nil, -- src\editor\debugger.lua
["Debugging session completed (%s)."] = "Sesión de depuración completada (%s).", -- src\editor\debugger.lua
["Debugging session started in '%s'."] = "Sesión de depuración iniciada en '%s'.", -- src\editor\debugger.lua
["Debugging suspended at %s:%s (couldn't activate the file)."] = nil, -- src\editor\debugger.lua
["Detach &Process"] = nil, -- src\editor\menu_project.lua
["Directory"] = nil, -- src\editor\findreplace.lua
["Do you want to delete '%s'?"] = nil, -- src\editor\filetree.lua
["Do you want to overwrite it?"] = nil, -- src\editor\commands.lua
@@ -144,8 +150,10 @@ return {
["Found"] = nil, -- src\editor\findreplace.lua
["Full &Screen"] = "Pantalla completa", -- src\editor\menu_view.lua
["Go To Definition"] = nil, -- src\editor\editor.lua
["Go To Line"] = "Ir a línea", -- src\editor\menu_search.lua
["Go To Next Bookmark"] = nil, -- src\editor\menu_edit.lua
["Go To Previous Bookmark"] = nil, -- src\editor\menu_edit.lua
["Go to a selected line"] = "Ir a línea seleccionada", -- src\editor\menu_search.lua
["Goto Line"] = "Ir a línea", -- src\editor\menu_search.lua
["INS"] = "INS", -- src\editor\editor.lua
["In Files"] = nil, -- src\editor\findreplace.lua
["Jump to a function definition..."] = "Saltar a la definición de la función...", -- src\editor\editor.lua
@@ -175,13 +183,15 @@ return {
["Program starting as '%s'."] = "Programa iniciado como '%s'.", -- src\editor\output.lua
["Program stopped (pid: %d)."] = "Programa parado (pid: %d).", -- src\editor\debugger.lua
["Program unable to run as '%s'."] = "No se puede ejecutar el programa como '%s'.", -- src\editor\output.lua
["Project Directory"] = nil, -- src\editor\menu_project.lua
["Project"] = "Proyecto", -- src\editor\gui.lua, src\editor\settings.lua
["Project Directory"] = nil, -- src\editor\menu_project.lua, src\editor\filetree.lua
["Project history"] = nil, -- src\editor\menu_file.lua
["Project"] = "Proyecto", -- src\editor\gui.lua
["Project/&FileTree Window"] = "Ventana de proyecto/árbol de archivos", -- src\editor\menu_view.lua
["Provide command line parameters"] = nil, -- src\editor\menu_project.lua
["R/O"] = "R/O", -- src\editor\editor.lua
["R/W"] = "R/W", -- src\editor\editor.lua
["Re&place In Files"] = "Remplazar en archivos", -- src\editor\menu_search.lua
["Recent &Projects"] = nil, -- src\editor\menu_file.lua
["Recent Files"] = "Archivos recientes", -- src\editor\menu_file.lua
["Redo last edit undone"] = "Rehacer la última edición deshecha", -- src\editor\menu_edit.lua
["Refused a request to start a new debugging session as there is one in progress already."] = "No se pudo lanzar una nueva sesión de depuración porque ya hay una en curso.", -- src\editor\debugger.lua
@@ -194,7 +204,6 @@ return {
["Replaced"] = nil, -- src\editor\findreplace.lua
["Replacing"] = nil, -- src\editor\findreplace.lua
["Reset to default layout"] = "Restablecer el diseño por defecto", -- src\editor\menu_view.lua
["Resets the dynamic word list for autocompletion"] = "Restablecer la lista dinámica de palabras para autocompletado", -- src\editor\menu_edit.lua
["Run as Scratchpad"] = "Ejecutar como borrador", -- src\editor\menu_project.lua
["S&top Debugging"] = "Parar depuración", -- src\editor\menu_project.lua
["S&top Process"] = "Parar proceso", -- src\editor\menu_project.lua
@@ -210,6 +219,7 @@ return {
["Scope"] = nil, -- src\editor\findreplace.lua
["Scratchpad error"] = "Error en el borrador", -- src\editor\debugger.lua
["Searching for"] = nil, -- src\editor\findreplace.lua
["Sel: %d/%d"] = nil, -- src\editor\editor.lua
["Select &All"] = "Seleccionar todo", -- src\editor\menu_edit.lua, src\editor\editor.lua
["Select all text in the editor"] = "Seleccionar todo el texto en el editor", -- src\editor\menu_edit.lua
["Select and Find Next"] = nil, -- src\editor\menu_search.lua
@@ -219,12 +229,14 @@ return {
["Set From Current File"] = nil, -- src\editor\menu_project.lua
["Set project directory from current file"] = "Establecer el directorio del proyecto del archivo actual", -- src\editor\gui.lua, src\editor\menu_project.lua
["Set the interpreter to be used"] = "Establecer el intérprete a ser usado", -- src\editor\menu_project.lua
["Set the project directory to be used"] = nil, -- src\editor\menu_project.lua
["Set the project directory to be used"] = nil, -- src\editor\menu_project.lua, src\editor\filetree.lua
["Settings: System"] = nil, -- src\editor\menu_edit.lua
["Settings: User"] = nil, -- src\editor\menu_edit.lua
["Show &Tooltip"] = "Ver tooltip", -- src\editor\menu_edit.lua
["Show Location"] = nil, -- src\editor\gui.lua, src\editor\filetree.lua
["Show tooltip for current position; place cursor after opening bracket of function"] = "Ver tooltip para la posición actual; posicionar el cursor después de abrir el paréntisis de los argumentos de la función", -- src\editor\menu_edit.lua
["Show/Hide the status bar"] = nil, -- src\editor\menu_view.lua
["Show/Hide the toolbar"] = nil, -- src\editor\menu_view.lua
["Sort selected lines"] = "Clasificar las líneas seleccionadas", -- src\editor\menu_edit.lua
["Stack"] = nil, -- src\editor\debugger.lua, src\editor\gui.lua
["Start &Debugging"] = "Comenzar depuración", -- src\editor\menu_project.lua
@@ -236,10 +248,12 @@ return {
["Step into"] = "Paso dentro", -- src\editor\gui.lua, src\editor\menu_project.lua
["Step out of the current function"] = "Hasta salir de la función actual", -- src\editor\gui.lua, src\editor\menu_project.lua
["Step over"] = "Paso sin entrar", -- src\editor\gui.lua, src\editor\menu_project.lua
["Stop debugging and continue running the process"] = nil, -- src\editor\gui.lua, src\editor\menu_project.lua
["Stop the currently running process"] = "Parar el proceso en ejecución", -- src\editor\gui.lua, src\editor\menu_project.lua
["Switch to or from full screen mode"] = "Conmutar el modo de pantalla completa", -- src\editor\menu_view.lua
["Text not found."] = nil, -- src\editor\findreplace.lua
["The API file must be located in a subdirectory of the API directory."] = "El archivo de API debe ser almacenado en un subdirectorio del directorio de API.", -- src\editor\autocomplete.lua
["Toggle Bookmark"] = nil, -- src\editor\menu_edit.lua
["Toggle Break&point"] = "Conmutar punto de ruptura", -- src\editor\menu_project.lua
["Toggle breakpoint"] = "Conmutar punto de ruptura", -- src\editor\gui.lua, src\editor\menu_project.lua
["Tr&ace"] = "Traza", -- src\editor\menu_project.lua
@@ -264,6 +278,10 @@ return {
["Welcome to the interactive Lua interpreter."] = "Bienvenido al intérprete interactico de Lua.", -- src\editor\shellbox.lua
["Wrap ar&ound"] = nil, -- src\editor\findreplace.lua
["You must save the program first."] = "Debes guardar el programa primero", -- src\editor\commands.lua
["Zoom In"] = nil, -- src\editor\menu_view.lua
["Zoom Out"] = nil, -- src\editor\menu_view.lua
["Zoom to 100%"] = nil, -- src\editor\menu_view.lua
["Zoom"] = nil, -- src\editor\menu_view.lua
["on line %d"] = "en la línea %d", -- src\editor\debugger.lua, src\editor\editor.lua, src\editor\commands.lua
["traced %d instruction"] = {"%d instrucción trazada", "%d instrucciones trazadas"}, -- src\editor\debugger.lua
["unknown error"] = nil, -- src\editor\debugger.lua

View File

@@ -14,6 +14,7 @@ return {
["&Delete"] = "&Supprimer", -- src\editor\filetree.lua
["&Documentation"] = "&Documentation", -- src\editor\menu_help.lua
["&Down"] = "Vers le &bas", -- src\editor\findreplace.lua
["&Edit Project Directory"] = "&Modifier le répertoire de projet", -- src\editor\filetree.lua
["&Edit Watch"] = "&Modifier une expression", -- src\editor\debugger.lua
["&Edit"] = "É&dition", -- src\editor\menu_edit.lua
["&File"] = "&Fichier", -- src\editor\menu_file.lua
@@ -23,7 +24,7 @@ return {
["&Fold/Unfold All"] = "Re&plier/Déplier tout", -- src\editor\menu_edit.lua
["&Frequently Asked Questions"] = "&Questions Fréquemment Posées" , -- src\editor\menu_help.lua
["&Getting Started Guide"] = "&Guide de Prise en Main", -- src\editor\menu_help.lua
["&Goto Line"] = "&Aller à la ligne", -- src\editor\menu_search.lua
["&Go To Line..."] = "&Aller à la ligne...", -- src\editor\menu_search.lua
["&Help"] = "Aid&e", -- src\editor\menu_help.lua
["&New Directory"] = "&Nouveau Répertoire", -- src\editor\filetree.lua
["&New"] = "&Nouveau", -- src\editor\menu_file.lua
@@ -42,7 +43,9 @@ return {
["&Sort"] = "&Trier", -- src\editor\menu_edit.lua
["&Stack Window"] = "&Pile d'exécution", -- src\editor\menu_view.lua
["&Start Debugger Server"] = "Lancer le &serveur de débogage", -- src\editor\menu_project.lua
["&Status Bar"] = "&Barre d'état", -- src\editor\menu_view.lua
["&Subdirectories"] = "&Sous-répertoires", -- src\editor\findreplace.lua
["&Tool Bar"] = "&Barre d'outils", -- src\editor\menu_view.lua
["&Tutorials"] = "&Tutoriels", -- src\editor\menu_help.lua
["&Undo"] = "&Annuler", -- src\editor\menu_edit.lua, src\editor\editor.lua
["&Up"] = "Vers le &haut", -- src\editor\findreplace.lua
@@ -58,6 +61,7 @@ return {
["Analyze"] = "Analyser", -- src\editor\inspect.lua
["Auto Complete Identifiers"] = "Auto-compléter les identifiants", -- src\editor\menu_edit.lua
["Auto complete while typing"] = "Auto-compléter lors de la saisie", -- src\editor\menu_edit.lua
["Bookmark"] = "&Marque-pages", -- src\editor\menu_edit.lua
["Break execution at the next executed line of code"] = "Interrompre l'exécution à la ligne suivante", -- src\editor\gui.lua, src\editor\menu_project.lua
["C&lear Output Window"] = "E&ffacer la fenêtre de sortie", -- src\editor\menu_project.lua
["C&omment/Uncomment"] = "Co&mmenter/Décommenter", -- src\editor\menu_edit.lua
@@ -68,11 +72,13 @@ return {
["Can't start debugger server at %s:%d: %s."] = "Impossible de lancer le serveur de débogage à %s:%d: %s." , -- src\editor\debugger.lua
["Can't start debugging session due to internal error '%s'."] = "Impossible de lancer la session de débogage : erreur interne '%s'.", -- src\editor\debugger.lua
["Can't start debugging without an opened file or with the current file not being saved ('%s')."] = "Impossible de lancer le débogage si aucun fichier n'est ouvert ou si le fichier courant n'a pas été enregistré ('%s').", -- src\editor\debugger.lua
["Can't stop debugger server as it is not started."] = "Impossible d'arrêter le serveur de débogage car il n'a pas été démarré", -- src\editor\debugger.lua
["Cancel"] = "Annuler", -- src\editor\findreplace.lua
["Cancelled by the user."] = "Annulé par l'utilisateur.", -- src\editor\findreplace.lua
["Choose..."] = "Choisir...", -- src\editor\menu_project.lua
["Choose a project directory"] = "Choisissez un répertoire de projet", -- src\editor\findreplace.lua, src\editor\menu_project.lua
["Clear &Dynamic Words"] = "Effacer les mots &dynamiques", -- src\editor\menu_edit.lua
["Choose a project directory"] = "Choisissez un répertoire de projet", -- src\editor\findreplace.lua, src\editor\menu_project.lua, src\editor\gui.lua, src\editor\filetree.lua
["Choose..."] = "Choisir...", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Clear Items"] = "Effacer les éléments", -- src\editor\menu_file.lua
["Clear items from this list"] = "Effacer les éléments de cette liste", -- src\editor\menu_file.lua
["Clear the output window before compiling or debugging"] = "Effacer la fenêtre de sortie avant compilation ou débogage", -- src\editor\menu_project.lua
["Close &Other Pages"] = "Fermer les &autres pages", -- src\editor\gui.lua
["Close A&ll Pages"] = "Fermer &toutes les pages", -- src\editor\gui.lua
@@ -95,9 +101,11 @@ return {
["Cu&t"] = "&Couper", -- src\editor\menu_edit.lua, src\editor\editor.lua
["Cut selected text to clipboard"] = "Couper le texte sélectionné et copier dans le presse-papiers", -- src\editor\menu_edit.lua
["Debugger server started at %s:%d."] = "Serveur de débogage démarré à %s:%d.", -- src\editor\debugger.lua
["Debugger server stopped at %s:%d."] = "Serveur de débogage stoppé à %s:%d.", -- src\editor\debugger.lua
["Debugging session completed (%s)."] = "Session de débogage terminée (%s).", -- src\editor\debugger.lua
["Debugging session started in '%s'."] = "Session de débogage démarrée dans '%s'.", -- src\editor\debugger.lua
["Debugging suspended at %s:%s (couldn't activate the file)."] = "Débogage interrompu à %s:%s (impossible d'activer le fichier).", -- src\editor\debugger.lua
["Detach &Process"] = "Détacher le &processus", -- src\editor\menu_project.lua
["Directory"] = "Répertoire ", -- src\editor\findreplace.lua
["Do you want to delete '%s'?"] = "Voulez-vous effacer '%s'?", -- src\editor\filetree.lua
["Do you want to overwrite it?"] = "Voulez-vous l'écraser?", -- src\editor\commands.lua
@@ -141,8 +149,10 @@ return {
["Found"] = "Occurrences trouvées :", -- src\editor\findreplace.lua
["Full &Screen"] = "&Plein écran", -- src\editor\menu_view.lua
["Go To Definition"] = "Aller à la définition", -- src\editor\editor.lua
["Go To Line"] = "Aller à la ligne", -- src\editor\menu_search.lua
["Go To Next Bookmark"] = "Aller au marque-page suivant", -- src\editor\menu_edit.lua
["Go To Previous Bookmark"] = "Aller au marque-page précédent", -- src\editor\menu_edit.lua
["Go to a selected line"] = "Aller à la ligne sélectionnée", -- src\editor\menu_search.lua
["Goto Line"] = "Aller à la ligne", -- src\editor\menu_search.lua
["INS"] = "INS", -- src\editor\editor.lua
["In Files"] = "Dans les fichiers", -- src\editor\findreplace.lua
["Jump to a function definition..."] = "Aller à la définition de fonction...", -- src\editor\editor.lua
@@ -172,13 +182,15 @@ return {
["Program starting as '%s'."] = "Programme démarré en tant que '%s'.", -- src\editor\output.lua
["Program stopped (pid: %d)."] = "Programme stoppé (pid : %d).", -- src\editor\debugger.lua
["Program unable to run as '%s'."] = "Impossible d'exécuter le programme en tant que '%s'.", -- src\editor\output.lua
["Project Directory"] = "Répertoire de projet", -- src\editor\menu_project.lua
["Project"] = "Projet", -- src\editor\gui.lua, src\editor\settings.lua
["Project Directory"] = "Répertoire de projet", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Project history"] = "Historique de projet", -- src\editor\menu_file.lua
["Project"] = "Projet", -- src\editor\gui.lua
["Project/&FileTree Window"] = "&Explorateur de projet", -- src\editor\menu_view.lua
["Provide command line parameters"] = "Renseignez les paramètres de ligne de commande", -- src\editor\menu_project.lua
["R/O"] = "R/O", -- src\editor\editor.lua
["R/W"] = "R/W", -- src\editor\editor.lua
["Re&place In Files"] = "Remp&lacer dans les fichiers", -- src\editor\menu_search.lua
["Recent &Projects"] = "&Projets récents", -- src\editor\menu_file.lua
["Recent Files"] = "Fichiers récents", -- src\editor\menu_file.lua
["Redo last edit undone"] = "Rétablir la dernière modification", -- src\editor\menu_edit.lua
["Refused a request to start a new debugging session as there is one in progress already."] = "Une requête de lancement de débogage a été refusée car une session de débogage est déjà en cours.", -- src\editor\debugger.lua
@@ -191,7 +203,6 @@ return {
["Replaced"] = "Occurrences remplacées :", -- src\editor\findreplace.lua
["Replacing"] = "Remplacement de", -- src\editor\findreplace.lua
["Reset to default layout"] = "Restaurer l'affichage par défaut", -- src\editor\menu_view.lua
["Resets the dynamic word list for autocompletion"] = "Réinitialiser la liste des mots dynamiques pour l'auto-complétion", -- src\editor\menu_edit.lua
["Run as Scratchpad"] = "Exécuter comme brouillon", -- src\editor\menu_project.lua
["S&top Debugging"] = "&Arrêter le débogage", -- src\editor\menu_project.lua
["S&top Process"] = "&Arrêter le processus", -- src\editor\menu_project.lua
@@ -207,6 +218,7 @@ return {
["Scope"] = "Direction", -- src\editor\findreplace.lua
["Scratchpad error"] = "Erreur dans le brouillon", -- src\editor\debugger.lua
["Searching for"] = "Recherche de", -- src\editor\findreplace.lua
["Sel: %d/%d"] = "Sel: %d/%d", -- src\editor\editor.lua
["Select &All"] = "Sélectionner &tout", -- src\editor\menu_edit.lua, src\editor\editor.lua
["Select all text in the editor"] = "Sélectionner tout le texte dans l'éditeur", -- src\editor\menu_edit.lua
["Select and Find Next"] = "Sélectionner et chercher le suivant", -- src\editor\menu_search.lua
@@ -216,12 +228,14 @@ return {
["Set From Current File"] = "Définir depuis le fichier courant", -- src\editor\menu_project.lua
["Set project directory from current file"] = "Définir le répertoire de projet depuis le fichier courant", -- src\editor\gui.lua, src\editor\menu_project.lua
["Set the interpreter to be used"] = "Définir l'interpréteur à utiliser", -- src\editor\menu_project.lua
["Set the project directory to be used"] = "Définir le répertoire de projet à utiliser", -- src\editor\menu_project.lua
["Set the project directory to be used"] = "Définir le répertoire de projet à utiliser", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Settings: System"] = "Paramètres : Système", -- src\editor\menu_edit.lua
["Settings: User"] = "Paramètres : Utilisateur", -- src\editor\menu_edit.lua
["Show &Tooltip"] = "Afficher l'info-&bulle", -- src\editor\menu_edit.lua
["Show Location"] = "Afficher l'emplacement", -- src\editor\gui.lua, src\editor\filetree.lua
["Show tooltip for current position; place cursor after opening bracket of function"] = "Afficher l'info-bulle pour la position actuelle ; placez le curseur après la parenthèse ouvrante de la fonction", -- src\editor\menu_edit.lua
["Show/Hide the status bar"] = "Afficher/Masquer la barre de statut", -- src\editor\menu_view.lua
["Show/Hide the toolbar"] = "Afficher/Masquer la barre d'outils", -- src\editor\menu_view.lua
["Sort selected lines"] = "Trier les lignes sélectionnées", -- src\editor\menu_edit.lua
["Stack"] = "Pile d'exécution", -- src\editor\debugger.lua, src\editor\gui.lua
["Start &Debugging"] = "Lancer le &débogage", -- src\editor\menu_project.lua
@@ -233,10 +247,12 @@ return {
["Step into"] = "Rentrer dans l'instruction suivante", -- src\editor\gui.lua, src\editor\menu_project.lua
["Step out of the current function"] = "Sortir de la fonction courante", -- src\editor\gui.lua, src\editor\menu_project.lua
["Step over"] = "Enjamber l'instruction suivante", -- src\editor\gui.lua, src\editor\menu_project.lua
["Stop debugging and continue running the process"] = "Arrêter le débogage et continuer l'exécution du processus", -- src\editor\gui.lua, src\editor\menu_project.lua
["Stop the currently running process"] = "Arrêter le processus en cours d'exécution", -- src\editor\gui.lua, src\editor\menu_project.lua
["Switch to or from full screen mode"] = "Activer ou désactiver le mode plein écran", -- src\editor\menu_view.lua
["Text not found."] = "Texte non trouvé.", -- src\editor\findreplace.lua
["The API file must be located in a subdirectory of the API directory."] = "Le fichier d'API doit être placé dans un sous-répertoire du répertoire d'API.", -- src\editor\autocomplete.lua
["Toggle Bookmark"] = "Créer/Supprimer un marque-page", -- src\editor\menu_edit.lua
["Toggle Break&point"] = "Créer/Supprimer un &point d'arrêt", -- src\editor\menu_project.lua
["Toggle breakpoint"] = "Créer ou supprimer un point d'arrêt", -- src\editor\gui.lua, src\editor\menu_project.lua
["Tr&ace"] = "&Tracer", -- src\editor\menu_project.lua
@@ -261,6 +277,10 @@ return {
["Welcome to the interactive Lua interpreter."] = "Bienvenue dans l´interpréteur interactif Lua.", -- src\editor\shellbox.lua
["Wrap ar&ound"] = "B&oucler", -- src\editor\findreplace.lua
["You must save the program first."] = "Vous devez d'abord enregistrer le programme.", -- src\editor\commands.lua
["Zoom In"] = "Zoomer", -- src\editor\menu_view.lua
["Zoom Out"] = "Dézoomer", -- src\editor\menu_view.lua
["Zoom to 100%"] = "Zoomer à 100%", -- src\editor\menu_view.lua
["Zoom"] = "Zoom", -- src\editor\menu_view.lua
["on line %d"] = "à la ligne %d", -- src\editor\debugger.lua, src\editor\editor.lua, src\editor\commands.lua
["traced %d instruction"] = {"%d instruction tracée", "%d instructions tracées"}, -- src\editor\debugger.lua
["unknown error"] = "erreur inconnue", -- src\editor\debugger.lua

View File

@@ -14,6 +14,7 @@ return {
["&Delete"] = "Elimina", -- src\editor\filetree.lua
["&Documentation"] = "Documentazione", -- src\editor\menu_help.lua
["&Down"] = "Verso il basso", -- src\editor\findreplace.lua
["&Edit Project Directory"] = "Modifica directory di progetto", -- src\editor\filetree.lua
["&Edit Watch"] = "Modifica Espressione di Controllo", -- src\editor\debugger.lua
["&Edit"] = "Modifica", -- src\editor\menu_edit.lua
["&File"] = "File", -- src\editor\menu_file.lua
@@ -23,7 +24,7 @@ return {
["&Fold/Unfold All"] = "Apri/Chiudi tutto", -- src\editor\menu_edit.lua
["&Frequently Asked Questions"] = "Domande &Frequenti", -- src\editor\menu_help.lua
["&Getting Started Guide"] = "&Guida Introduttiva", -- src\editor\menu_help.lua
["&Goto Line"] = "Vai a riga", -- src\editor\menu_search.lua
["&Go To Line..."] = "Vai a riga...", -- src\editor\menu_search.lua
["&Help"] = "Aiuto", -- src\editor\menu_help.lua
["&New Directory"] = "&Nuova Directory", -- src\editor\filetree.lua
["&New"] = "&Nuovo", -- src\editor\menu_file.lua
@@ -42,7 +43,9 @@ return {
["&Sort"] = "Ordina", -- src\editor\menu_edit.lua
["&Stack Window"] = "Stack di chiamate", -- src\editor\menu_view.lua
["&Start Debugger Server"] = "Avvia Debugger Server", -- src\editor\menu_project.lua
["&Status Bar"] = "Barra di stato", -- src\editor\menu_view.lua
["&Subdirectories"] = "Sotto Directory", -- src\editor\findreplace.lua
["&Tool Bar"] = "Barra degli strumenti", -- src\editor\menu_view.lua
["&Tutorials"] = "Guide", -- src\editor\menu_help.lua
["&Undo"] = "Annulla", -- src\editor\menu_edit.lua, src\editor\editor.lua
["&Up"] = "Verso l'alto", -- src\editor\findreplace.lua
@@ -58,6 +61,7 @@ return {
["Analyze"] = "Analizza", -- src\editor\inspect.lua
["Auto Complete Identifiers"] = "Autocompletamento identificatori", -- src\editor\menu_edit.lua
["Auto complete while typing"] = "Autocompletamento in linea", -- src\editor\menu_edit.lua
["Bookmark"] = "Segnalibro", -- src\editor\menu_edit.lua
["Break execution at the next executed line of code"] = "Interrompi l'esecuzione alla successiva riga di codice ", -- src\editor\gui.lua, src\editor\menu_project.lua
["C&lear Output Window"] = "Pulisci finestra di output", -- src\editor\menu_project.lua
["C&omment/Uncomment"] = "Commenta/Scommenta", -- src\editor\menu_edit.lua
@@ -68,11 +72,13 @@ return {
["Can't start debugger server at %s:%d: %s."] = "Impossibile lanciare il server debugger a %s:%d: %s.", -- src\editor\debugger.lua
["Can't start debugging session due to internal error '%s'."] = "Impossibile lanciare la sessione di debug: errore interno '%s'.'", -- src\editor\debugger.lua
["Can't start debugging without an opened file or with the current file not being saved ('%s')."] = "Impossibile lanciare il debug senza aver aperto un file o se il file corrente non è stato salvato ('%s').", -- src\editor\debugger.lua
["Can't stop debugger server as it is not started."] = "Impossibile fermare il server debugger perchè non è stato avviato", -- src\editor\debugger.lua
["Cancel"] = "Annulla", -- src\editor\findreplace.lua
["Cancelled by the user."] = "Annullato dall'utente", -- src\editor\findreplace.lua
["Choose..."] = "Scegli...", -- src\editor\menu_project.lua
["Choose a project directory"] = "Scegli la directory di un progetto", -- src\editor\findreplace.lua, src\editor\menu_project.lua
["Clear &Dynamic Words"] = "Elimina le &Dynamic Words", -- src\editor\menu_edit.lua
["Choose a project directory"] = "Scegli la directory di un progetto", -- src\editor\findreplace.lua, src\editor\menu_project.lua, src\editor\gui.lua, src\editor\filetree.lua
["Choose..."] = "Scegli...", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Clear Items"] = "Pulisci elementi", -- src\editor\menu_file.lua
["Clear items from this list"] = "Pulisci elementi della lista", -- src\editor\menu_file.lua
["Clear the output window before compiling or debugging"] = "Pulisci la finestra di output prima di compilare o lanciare debug", -- src\editor\menu_project.lua
["Close &Other Pages"] = "Chidi le Altre Pagine", -- src\editor\gui.lua
["Close A&ll Pages"] = "Chiudi Tutte le Pagine", -- src\editor\gui.lua
@@ -94,10 +100,12 @@ return {
["Create an empty document"] = "Crea un documento vuoto", -- src\editor\gui.lua, src\editor\menu_file.lua
["Cu&t"] = "&Taglia", -- src\editor\menu_edit.lua, src\editor\editor.lua
["Cut selected text to clipboard"] = "Taglia il testo selezionato e mette negli appunti", -- src\editor\menu_edit.lua
["Debugger server started at %s:%d."] = "Server Debugger iniziato %s:%s", -- src\editor\debugger.lua
["Debugger server started at %s:%d."] = "Server Debugger iniziato %s:%d.", -- src\editor\debugger.lua
["Debugger server stopped at %s:%d."] = "Server Debugger fermato %s:%d.", -- src\editor\debugger.lua
["Debugging session completed (%s)."] = "Sessione di debug completata (%s).", -- src\editor\debugger.lua
["Debugging session started in '%s'."] = "Sessione di debug iniziata da '%s'.", -- src\editor\debugger.lua
["Debugging suspended at %s:%s (couldn't activate the file)."] = "Debug sospeso a %s:%s (impossibile attivare il file).", -- src\editor\debugger.lua
["Detach &Process"] = "Scollega Processo", -- src\editor\menu_project.lua
["Directory"] = "Directory", -- src\editor\findreplace.lua
["Do you want to delete '%s'?"] = "Vuoi eliminare '%s'?", -- src\editor\filetree.lua
["Do you want to overwrite it?"] = "Vuoi sovrascrivere '%s'?", -- src\editor\commands.lua
@@ -141,8 +149,10 @@ return {
["Found"] = "Occorrenze trovate:", -- src\editor\findreplace.lua
["Full &Screen"] = "Schermo intero", -- src\editor\menu_view.lua
["Go To Definition"] = "Vai a Definizione", -- src\editor\editor.lua
["Go To Line"] = "Vai alla riga", -- src\editor\menu_search.lua
["Go To Next Bookmark"] = "Vai al Prossimo Segnalibro", -- src\editor\menu_edit.lua
["Go To Previous Bookmark"] = "Vai al Precedente Segnalibro", -- src\editor\menu_edit.lua
["Go to a selected line"] = "Vai alla riga selezionata", -- src\editor\menu_search.lua
["Goto Line"] = "Vai alla riga", -- src\editor\menu_search.lua
["INS"] = "INS", -- src\editor\editor.lua
["In Files"] = "Nei Files", -- src\editor\findreplace.lua
["Jump to a function definition..."] = "Salta alla definizione della funzione...", -- src\editor\editor.lua
@@ -172,13 +182,15 @@ return {
["Program starting as '%s'."] = "Programma partito da '%s'.", -- src\editor\output.lua
["Program stopped (pid: %d)."] = "Programma fermato (pid: %d).", -- src\editor\debugger.lua
["Program unable to run as '%s'."] = "Il programma non puo' partire '%s'.", -- src\editor\output.lua
["Project Directory"] = "Directory del Progetto", -- src\editor\menu_project.lua
["Project"] = "Progetto", -- src\editor\gui.lua, src\editor\settings.lua
["Project Directory"] = "Directory del Progetto", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Project history"] = "Storia del Progetto", -- src\editor\menu_file.lua
["Project"] = "Progetto", -- src\editor\gui.lua
["Project/&FileTree Window"] = "Progetto/Explorer", -- src\editor\menu_view.lua
["Provide command line parameters"] = "Fornire parametri riga di comando", -- src\editor\menu_project.lua
["R/O"] = "R/O", -- src\editor\editor.lua
["R/W"] = "R/W", -- src\editor\editor.lua
["Re&place In Files"] = "Sostituisci nei files", -- src\editor\menu_search.lua
["Recent &Projects"] = "Progetti Recenti", -- src\editor\menu_file.lua
["Recent Files"] = "Files recenti", -- src\editor\menu_file.lua
["Redo last edit undone"] = "Ripeti l'ultima azione annullata", -- src\editor\menu_edit.lua
["Refused a request to start a new debugging session as there is one in progress already."] = "Impossibile aprire una nuova sessione di debug in quanto ne esiste una in corso", -- src\editor\debugger.lua
@@ -191,7 +203,6 @@ return {
["Replaced"] = "Sostituiti :", -- src\editor\findreplace.lua
["Replacing"] = "Sostituzione", -- src\editor\findreplace.lua
["Reset to default layout"] = "Ritorna al default layout", -- src\editor\menu_view.lua
["Resets the dynamic word list for autocompletion"] = "Azzera la lista di dynamic words per l'autocompletamento", -- src\editor\menu_edit.lua
["Run as Scratchpad"] = "Esegui in Scratchpad (Live coding)", -- src\editor\menu_project.lua
["S&top Debugging"] = "Ferma il debugger", -- src\editor\menu_project.lua
["S&top Process"] = "Ferma il processo", -- src\editor\menu_project.lua
@@ -207,6 +218,7 @@ return {
["Scope"] = "Direzione", -- src\editor\findreplace.lua
["Scratchpad error"] = "Errore durente Scratchpad", -- src\editor\debugger.lua
["Searching for"] = "Ricerca di", -- src\editor\findreplace.lua
["Sel: %d/%d"] = "Sel: %d/%d", -- src\editor\editor.lua
["Select &All"] = "Selezion&a Tutto", -- src\editor\menu_edit.lua, src\editor\editor.lua
["Select all text in the editor"] = "Seleziona tutto il testo nell'editor", -- src\editor\menu_edit.lua
["Select and Find Next"] = "Seleziona e trova successivo", -- src\editor\menu_search.lua
@@ -216,12 +228,14 @@ return {
["Set From Current File"] = "Impostato da file corrente", -- src\editor\menu_project.lua
["Set project directory from current file"] = "Definisci la directory del progeetto dal file corrente", -- src\editor\gui.lua, src\editor\menu_project.lua
["Set the interpreter to be used"] = "Definisci l'interprete da utilizzare", -- src\editor\menu_project.lua
["Set the project directory to be used"] = "Imposta la directory di progetto da usare", -- src\editor\menu_project.lua
["Set the project directory to be used"] = "Imposta la directory di progetto da usare", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Settings: System"] = "Impostazioni: Sistema", -- src\editor\menu_edit.lua
["Settings: User"] = "Impostazioni: Utente", -- src\editor\menu_edit.lua
["Show &Tooltip"] = "Mos&tra i consigli", -- src\editor\menu_edit.lua
["Show Location"] = "Mostra posizione", -- src\editor\gui.lua, src\editor\filetree.lua
["Show tooltip for current position; place cursor after opening bracket of function"] = "Mostra i consigli per la posizione corrente; muovi il cursore dopo la parentesi o la funzione", -- src\editor\menu_edit.lua
["Show/Hide the status bar"] = "Mostra/Nascondi la barra di stato", -- src\editor\menu_view.lua
["Show/Hide the toolbar"] = "Mostra/Nascondi la barra degli strumenti", -- src\editor\menu_view.lua
["Sort selected lines"] = "Ordina le righe selezionate", -- src\editor\menu_edit.lua
["Stack"] = "Stack", -- src\editor\debugger.lua, src\editor\gui.lua
["Start &Debugging"] = "Inizia il &Debug", -- src\editor\menu_project.lua
@@ -233,10 +247,12 @@ return {
["Step into"] = "Step into", -- src\editor\gui.lua, src\editor\menu_project.lua
["Step out of the current function"] = "Contina fino all'uscita della funzione", -- src\editor\gui.lua, src\editor\menu_project.lua
["Step over"] = "Continua senza entrare nella funzione", -- src\editor\gui.lua, src\editor\menu_project.lua
["Stop debugging and continue running the process"] = "Ferma il debug e continua l'esecuzione del processo", -- src\editor\gui.lua, src\editor\menu_project.lua
["Stop the currently running process"] = "Ferma il processo in esecuzione", -- src\editor\gui.lua, src\editor\menu_project.lua
["Switch to or from full screen mode"] = "Passa da tutto schermo a finestra", -- src\editor\menu_view.lua
["Text not found."] = "Testo non trovato.", -- src\editor\findreplace.lua
["The API file must be located in a subdirectory of the API directory."] = "Il file API deve essere presente in una sottodirectory o nella direcotory API.", -- src\editor\autocomplete.lua
["Toggle Bookmark"] = "Attiva/Disattiva Segnalibro", -- src\editor\menu_edit.lua
["Toggle Break&point"] = "Attiva/Disattiva Break&point", -- src\editor\menu_project.lua
["Toggle breakpoint"] = "Attiva/Disattiva Breakpoint", -- src\editor\gui.lua, src\editor\menu_project.lua
["Tr&ace"] = "Tr&ace", -- src\editor\menu_project.lua
@@ -261,6 +277,10 @@ return {
["Welcome to the interactive Lua interpreter."] = "Benvenuti nell`interprete interattivo Lua.", -- src\editor\shellbox.lua
["Wrap ar&ound"] = "Wrap ar&ound", -- src\editor\findreplace.lua
["You must save the program first."] = "Devi prima salvare il programma", -- src\editor\commands.lua
["Zoom In"] = "Zoom In", -- src\editor\menu_view.lua
["Zoom Out"] = "Zoom Out", -- src\editor\menu_view.lua
["Zoom to 100%"] = "Zoom a 100%", -- src\editor\menu_view.lua
["Zoom"] = "Zoom", -- src\editor\menu_view.lua
["on line %d"] = "alla linea %d", -- src\editor\debugger.lua, src\editor\editor.lua, src\editor\commands.lua
["traced %d instruction"] = {"tracciata %d istruzione", "%d istruzioni tracciate"}, -- src\editor\debugger.lua
["unknown error"] = "errore sconosciuto", -- src\editor\debugger.lua

View File

@@ -1,7 +1,7 @@
return {
[0] = function(c) c = (c-9)%100 < 9 and 9 or (c-1)%10 return c == 0 and 1 or c < 4 and 2 or 3 end, -- plural
["%d instance"] = {"%d совпадение", "%d совпадения", "%d совпадений"}, -- src\editor\findreplace.lua
["%s event failed: %s"] = nil, -- src\editor\package.lua
["%s event failed: %s"] = "%s обработчик события вернул ошибку: %s", -- src\editor\package.lua
["&About"] = "&О программе", -- src\editor\menu_help.lua
["&Add Watch"] = "&Добавить выражение", -- src\editor\debugger.lua
["&Break"] = "Пр&ервать", -- src\editor\menu_project.lua
@@ -11,9 +11,10 @@ return {
["&Copy"] = "&Копировать", -- src\editor\menu_edit.lua, src\editor\editor.lua
["&Default Layout"] = "Вид по &умолчанию", -- src\editor\menu_view.lua
["&Delete Watch"] = "&Удалить выражение", -- src\editor\debugger.lua
["&Delete"] = nil, -- src\editor\filetree.lua
["&Documentation"] = nil, -- src\editor\menu_help.lua
["&Delete"] = "&Удалить", -- src\editor\filetree.lua
["&Documentation"] = "Документация", -- src\editor\menu_help.lua
["&Down"] = "Вниз", -- src\editor\findreplace.lua
["&Edit Project Directory"] = "&Редактировать папку проекта", -- src\editor\filetree.lua
["&Edit Watch"] = "&Редактировать выражение", -- src\editor\debugger.lua
["&Edit"] = "&Правка", -- src\editor\menu_edit.lua
["&File"] = "&Файл", -- src\editor\menu_file.lua
@@ -21,19 +22,19 @@ return {
["&Find Next"] = "Найти далее", -- src\editor\findreplace.lua
["&Find"] = "&Найти", -- src\editor\menu_search.lua
["&Fold/Unfold All"] = "Св&ернуть/развернуть все", -- src\editor\menu_edit.lua
["&Frequently Asked Questions"] = nil, -- src\editor\menu_help.lua
["&Frequently Asked Questions"] = "&Часто задаваемые вопросы", -- src\editor\menu_help.lua
["&Getting Started Guide"] = nil, -- src\editor\menu_help.lua
["&Goto Line"] = "&Перейти к строке", -- src\editor\menu_search.lua
["&Go To Line..."] = "&Перейти к строке...", -- src\editor\menu_search.lua
["&Help"] = "&Справка", -- src\editor\menu_help.lua
["&New Directory"] = nil, -- src\editor\filetree.lua
["&New Directory"] = "&Новая папка", -- src\editor\filetree.lua
["&New"] = "Соз&дать", -- src\editor\menu_file.lua
["&Open..."] = "&Открыть...", -- src\editor\menu_file.lua
["&Output/Console Window"] = "Окно &вывода/консоли", -- src\editor\menu_view.lua
["&Paste"] = "В&ставить", -- src\editor\menu_edit.lua, src\editor\editor.lua
["&Project Page"] = nil, -- src\editor\menu_help.lua
["&Project Page"] = "Страница проекта", -- src\editor\menu_help.lua
["&Project"] = "Пр&оект", -- src\editor\inspect.lua, src\editor\menu_project.lua
["&Redo"] = "Верну&ть", -- src\editor\menu_edit.lua, src\editor\editor.lua
["&Rename"] = nil, -- src\editor\filetree.lua
["&Rename"] = "Переименовать", -- src\editor\filetree.lua
["&Replace All"] = "Заменить всe", -- src\editor\findreplace.lua
["&Replace"] = "За&менить", -- src\editor\findreplace.lua, src\editor\menu_search.lua
["&Run"] = "За&пустить", -- src\editor\menu_project.lua
@@ -42,8 +43,10 @@ return {
["&Sort"] = "&Cортировать", -- src\editor\menu_edit.lua
["&Stack Window"] = "Окно &стека", -- src\editor\menu_view.lua
["&Start Debugger Server"] = "Запустить сервер отла&дки", -- src\editor\menu_project.lua
["&Status Bar"] = "Панель состояния", -- src\editor\menu_view.lua
["&Subdirectories"] = "В папках", -- src\editor\findreplace.lua
["&Tutorials"] = nil, -- src\editor\menu_help.lua
["&Tool Bar"] = "Панель инструментов", -- src\editor\menu_view.lua
["&Tutorials"] = "&Обучающие материалы", -- src\editor\menu_help.lua
["&Undo"] = "&Отменить", -- src\editor\menu_edit.lua, src\editor\editor.lua
["&Up"] = "Вверх", -- src\editor\findreplace.lua
["&View"] = "&Вид", -- src\editor\menu_view.lua
@@ -58,6 +61,7 @@ return {
["Analyze"] = "Анализировать", -- src\editor\inspect.lua
["Auto Complete Identifiers"] = "Автодополнение идентификаторов", -- src\editor\menu_edit.lua
["Auto complete while typing"] = "Автоматически дополнять идентификаторы при наборе", -- src\editor\menu_edit.lua
["Bookmark"] = "Закладка", -- src\editor\menu_edit.lua
["Break execution at the next executed line of code"] = "Прервать выполнение на следующей строке", -- src\editor\gui.lua, src\editor\menu_project.lua
["C&lear Output Window"] = "Очистка ок&на вывода", -- src\editor\menu_project.lua
["C&omment/Uncomment"] = "Зако&мментировать/раскомментировать", -- src\editor\menu_edit.lua
@@ -65,14 +69,16 @@ return {
["Can't find file '%s' in the current project to activate for debugging. Update the project or open the file in the editor before debugging."] = "Файл '%s', необходимый для отладки, не найден в текущем проекте. Обновите проект или откройте файл в редакторе перед началом отладки.", -- src\editor\debugger.lua
["Can't process auto-recovery record; invalid format: %s."] = "Ошибка обработки записи автоматического восстановления; неверный формат: %s.", -- src\editor\commands.lua
["Can't run the entry point script ('%s')."] = "Ошибка выполнения стартового скрипта ('%s').", -- src\editor\debugger.lua
["Can't start debugger server at %s:%d: %s."] = nil, -- src\editor\debugger.lua
["Can't start debugger server at %s:%d: %s."] = "Невозможно запустить сервер отладки %s:%d: %s", -- src\editor\debugger.lua
["Can't start debugging session due to internal error '%s'."] = "Невозможно начать отладочную сессию из-за внутренней ошибки '%s'.", -- src\editor\debugger.lua
["Can't start debugging without an opened file or with the current file not being saved ('%s')."] = "Невозможно начать отладку без открытого файла или с несохраненным текущим файлом ('%s').", -- src\editor\debugger.lua
["Can't stop debugger server as it is not started."] = "Невозможно остановить сервер отладки пока он не запущен", -- src\editor\debugger.lua
["Cancel"] = "Отмена", -- src\editor\findreplace.lua
["Cancelled by the user."] = nil, -- src\editor\findreplace.lua
["Choose..."] = "Выбрать...", -- src\editor\menu_project.lua
["Choose a project directory"] = "Выберите папку проекта", -- src\editor\findreplace.lua, src\editor\menu_project.lua
["Clear &Dynamic Words"] = "Очистить &динамические слова", -- src\editor\menu_edit.lua
["Cancelled by the user."] = "Отменено пользователем.", -- src\editor\findreplace.lua
["Choose a project directory"] = "Выберите папку проекта", -- src\editor\findreplace.lua, src\editor\menu_project.lua, src\editor\gui.lua, src\editor\filetree.lua
["Choose..."] = "Выбрать...", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Clear Items"] = "Очистить список", -- src\editor\menu_file.lua
["Clear items from this list"] = "Удалить элементы из списка", -- src\editor\menu_file.lua
["Clear the output window before compiling or debugging"] = "Очистить окно вывода перед компиляцией или отладкой", -- src\editor\menu_project.lua
["Close &Other Pages"] = "Закрыть &остальные вкладки", -- src\editor\gui.lua
["Close A&ll Pages"] = "Закрыть &все вкладки", -- src\editor\gui.lua
@@ -87,25 +93,27 @@ return {
["Compile the current file"] = "Скомпилировать текущий файл", -- src\editor\menu_project.lua
["Complete &Identifier"] = "Дополнить &идентификатор", -- src\editor\menu_edit.lua
["Complete the current identifier"] = "Дополнить текущий идентификатор", -- src\editor\menu_edit.lua
["Consider removing backslash from escape sequence '%s'."] = nil, -- src\editor\commands.lua
["Copy Full Path"] = nil, -- src\editor\filetree.lua
["Consider removing backslash from escape sequence '%s'."] = "Рассмотрите вариант удаления backslash из строки '%s'.", -- src\editor\commands.lua
["Copy Full Path"] = "Скопировать полный путь", -- src\editor\filetree.lua
["Copy selected text to clipboard"] = "Скопировать выделенный текст в буфер обмена", -- src\editor\menu_edit.lua
["Couldn't activate file '%s' for debugging; continuing without it."] = "Невозможно открыть файл '%s' для отладки; выполнение будет продолжено без него.", -- src\editor\debugger.lua
["Create an empty document"] = "Создать новый документ", -- src\editor\gui.lua, src\editor\menu_file.lua
["Cu&t"] = "Вы&резать", -- src\editor\menu_edit.lua, src\editor\editor.lua
["Cut selected text to clipboard"] = "Вырезать выделенный текст в буфер обмена", -- src\editor\menu_edit.lua
["Debugger server started at %s:%d."] = "Сервер отладки запущен на %s:%d.", -- src\editor\debugger.lua
["Debugger server stopped at %s:%d."] = "Сервер отладки остановлен %s:%d.", -- src\editor\debugger.lua
["Debugging session completed (%s)."] = "Отладочная сессия завершена (%s).", -- src\editor\debugger.lua
["Debugging session started in '%s'."] = "Отладочная сессия запущена в '%s'.", -- src\editor\debugger.lua
["Debugging suspended at %s:%s (couldn't activate the file)."] = "Отладка остановлена на %s:%s (невозможно открыть файл).", -- src\editor\debugger.lua
["Detach &Process"] = "Отсоединить процесс", -- src\editor\menu_project.lua
["Directory"] = "Папка", -- src\editor\findreplace.lua
["Do you want to delete '%s'?"] = nil, -- src\editor\filetree.lua
["Do you want to overwrite it?"] = nil, -- src\editor\commands.lua
["Do you want to delete '%s'?"] = "Удалить '%s'?", -- src\editor\filetree.lua
["Do you want to overwrite it?"] = "Переписать его?", -- src\editor\commands.lua
["Do you want to reload it?"] = "Перезагрузить его?", -- src\editor\editor.lua
["Do you want to save the changes to '%s'?"] = "Сохранить изменения в '%s'?", -- src\editor\commands.lua
["E&xit"] = "Вы&ход", -- src\editor\menu_file.lua
["Enter Lua code and press Enter to run it."] = "Введите код на Lua и нажмите Enter для выполнения.", -- src\editor\shellbox.lua
["Enter command line parameters (use Cancel to clear)"] = nil, -- src\editor\menu_project.lua
["Enter command line parameters (use Cancel to clear)"] = "Введите параметры командной строки (Cancel чтобы очистить)", -- src\editor\menu_project.lua
["Enter line number"] = "Введите номер строки", -- src\editor\menu_search.lua
["Error while loading API file: %s"] = "Ошибка загрузки файла определений API: %s", -- src\editor\autocomplete.lua
["Error while loading configuration file: %s"] = "Ошибка загрузки файла конфигурации: %s", -- src\editor\style.lua
@@ -123,7 +131,7 @@ return {
["File '%s' has more recent timestamp than restored '%s'; please review before saving."] = "Файл '%s' имеет более позднее время модификации, чем восстановленный '%s'; пожалуйста просмотрите его перед сохранением.", -- src\editor\commands.lua
["File '%s' no longer exists."] = "Файл '%s' больше не существует.", -- src\editor\menu_file.lua, src\editor\editor.lua
["File Type"] = "Тип файла", -- src\editor\findreplace.lua
["File already exists."] = nil, -- src\editor\commands.lua
["File already exists."] = "Файл уже существует.", -- src\editor\commands.lua
["File history"] = "История файлов", -- src\editor\menu_file.lua
["Find &In Files"] = "Н&айти в файлах", -- src\editor\menu_search.lua
["Find &Next"] = "Найти &далее", -- src\editor\menu_search.lua
@@ -140,9 +148,11 @@ return {
["Found auto-recovery record and restored saved session."] = "Найдена запись авто-восстановления и восстановлена сохраненная сессия.", -- src\editor\commands.lua
["Found"] = "Найдено", -- src\editor\findreplace.lua
["Full &Screen"] = "Во весь экр&ан", -- src\editor\menu_view.lua
["Go To Definition"] = nil, -- src\editor\editor.lua
["Go To Definition"] = "Перейти к определению", -- src\editor\editor.lua
["Go To Line"] = "Перейти к строке", -- src\editor\menu_search.lua
["Go To Next Bookmark"] = "Перейти к следующей закладке", -- src\editor\menu_edit.lua
["Go To Previous Bookmark"] = "Перейти к предыдущей закладке", -- src\editor\menu_edit.lua
["Go to a selected line"] = "Перейти к заданной строке", -- src\editor\menu_search.lua
["Goto Line"] = "Перейти к строке", -- src\editor\menu_search.lua
["INS"] = "ВСТ", -- src\editor\editor.lua
["In Files"] = "Установки файлов", -- src\editor\findreplace.lua
["Jump to a function definition..."] = "Перейти к определению функции...", -- src\editor\editor.lua
@@ -154,9 +164,9 @@ return {
["Match &case"] = "Совпадение регистра", -- src\editor\findreplace.lua
["Match &whole word"] = "Совпадение целого слова", -- src\editor\findreplace.lua
["Mixed end-of-line encodings detected."] = "Обнаружены смешанные символы конца строки.", -- src\editor\commands.lua
["New &File"] = nil, -- src\editor\filetree.lua
["New &File"] = "Новый файл", -- src\editor\filetree.lua
["OVR"] = "ЗАМ", -- src\editor\editor.lua
["Open With Default Program"] = nil, -- src\editor\filetree.lua
["Open With Default Program"] = "Открыть используя программу по умолчанию", -- src\editor\filetree.lua
["Open an existing document"] = "Открыть существующий документ", -- src\editor\gui.lua, src\editor\menu_file.lua
["Open file"] = "Открыть файл", -- src\editor\commands.lua
["Options"] = "Установки", -- src\editor\findreplace.lua
@@ -172,26 +182,27 @@ return {
["Program starting as '%s'."] = "Программа запускается как '%s'.", -- src\editor\output.lua
["Program stopped (pid: %d)."] = "Программа завершена (pid: %d).", -- src\editor\debugger.lua
["Program unable to run as '%s'."] = "Программа не может быть запущена как '%s'.", -- src\editor\output.lua
["Project Directory"] = "Папка проекта", -- src\editor\menu_project.lua
["Project"] = "Проект", -- src\editor\gui.lua, src\editor\settings.lua
["Project Directory"] = "Папка проекта", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Project history"] = "История проектов", -- src\editor\menu_file.lua
["Project"] = "Проект", -- src\editor\gui.lua
["Project/&FileTree Window"] = "Окно &проекта/списка файлов", -- src\editor\menu_view.lua
["Provide command line parameters"] = nil, -- src\editor\menu_project.lua
["Provide command line parameters"] = "Установить параметры командной строки", -- src\editor\menu_project.lua
["R/O"] = "R/O", -- src\editor\editor.lua
["R/W"] = "R/W", -- src\editor\editor.lua
["Re&place In Files"] = "Замени&ть в файлах", -- src\editor\menu_search.lua
["Recent &Projects"] = "Недавние &проекты", -- src\editor\menu_file.lua
["Recent Files"] = "Недавние файлы", -- src\editor\menu_file.lua
["Redo last edit undone"] = "Вернуть последнее отмененное изменение", -- src\editor\menu_edit.lua
["Refused a request to start a new debugging session as there is one in progress already."] = "Отказано в запросе на запуск новой отладочной сессии, поскольку одна сессия уже выполняется.", -- src\editor\debugger.lua
["Regular &expression"] = "Регулярное выражение", -- src\editor\findreplace.lua
["Remote console"] = "Удаленная консоль", -- src\editor\shellbox.lua
["Rename All Instances"] = nil, -- src\editor\editor.lua
["Rename All Instances"] = "Переименовать все совпадения", -- src\editor\editor.lua
["Replace A&ll"] = "Заменить все", -- src\editor\findreplace.lua
["Replace"] = "Заменить", -- src\editor\findreplace.lua
["Replaced an invalid UTF8 character with %s."] = "Некорректный символ UTF8 заменен на %s.", -- src\editor\commands.lua
["Replaced"] = "Заменено", -- src\editor\findreplace.lua
["Replacing"] = "Замена", -- src\editor\findreplace.lua
["Reset to default layout"] = "Установить расположение окон по умолчанию", -- src\editor\menu_view.lua
["Resets the dynamic word list for autocompletion"] = "Очистить список динамических слов для автодополнения", -- src\editor\menu_edit.lua
["Run as Scratchpad"] = "Запустить как черновик", -- src\editor\menu_project.lua
["S&top Debugging"] = "&Завершить отладку", -- src\editor\menu_project.lua
["S&top Process"] = "&Завершить процесс", -- src\editor\menu_project.lua
@@ -207,44 +218,49 @@ return {
["Scope"] = "Направление", -- src\editor\findreplace.lua
["Scratchpad error"] = "Ошибка в черновике", -- src\editor\debugger.lua
["Searching for"] = "Поиск", -- src\editor\findreplace.lua
["Sel: %d/%d"] = "Выд: %d/%d", -- src\editor\editor.lua
["Select &All"] = "Выделить &все", -- src\editor\menu_edit.lua, src\editor\editor.lua
["Select all text in the editor"] = "Выделить весь текст в редакторе", -- src\editor\menu_edit.lua
["Select and Find Next"] = "Выделить и найти далее", -- src\editor\menu_search.lua
["Select and Find Previous"] = "Выделить и найти ранее", -- src\editor\menu_search.lua
["Select the word under cursor and find its next occurrence"] = nil, -- src\editor\menu_search.lua
["Select the word under cursor and find its previous occurrence"] = nil, -- src\editor\menu_search.lua
["Select the word under cursor and find its next occurrence"] = "Выделить слово под курсором и найти далее", -- src\editor\menu_search.lua
["Select the word under cursor and find its previous occurrence"] = "Выделить слово под курсором и найти ранее", -- src\editor\menu_search.lua
["Set From Current File"] = "Установить по текущему файлу", -- src\editor\menu_project.lua
["Set project directory from current file"] = "Установить папку проекта по текущему файлу", -- src\editor\gui.lua, src\editor\menu_project.lua
["Set the interpreter to be used"] = "Установить используемый интерпретатор", -- src\editor\menu_project.lua
["Set the project directory to be used"] = "Установить используемую папку проекта", -- src\editor\menu_project.lua
["Set the project directory to be used"] = "Установить используемую папку проекта", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Settings: System"] = "Установки: Системы", -- src\editor\menu_edit.lua
["Settings: User"] = "Установки: Пользователя", -- src\editor\menu_edit.lua
["Show &Tooltip"] = "Показать &подсказку", -- src\editor\menu_edit.lua
["Show Location"] = nil, -- src\editor\gui.lua, src\editor\filetree.lua
["Show Location"] = "Показать файл в папке", -- src\editor\gui.lua, src\editor\filetree.lua
["Show tooltip for current position; place cursor after opening bracket of function"] = "Показать подсказку в текущей позиции; переместите курсор в позицию после открывающей скобки функции", -- src\editor\menu_edit.lua
["Show/Hide the status bar"] = "Показать/спрятать панель состояния", -- src\editor\menu_view.lua
["Show/Hide the toolbar"] = "Показать/спрятать панель инструментов", -- src\editor\menu_view.lua
["Sort selected lines"] = "Отсортировать выделенные строки", -- src\editor\menu_edit.lua
["Stack"] = "Стек", -- src\editor\debugger.lua, src\editor\gui.lua
["Start &Debugging"] = "Начать &отладку", -- src\editor\menu_project.lua
["Start or Continue debugging"] = nil, -- src\editor\gui.lua
["Start or continue debugging"] = nil, -- src\editor\menu_project.lua
["Start or Continue debugging"] = "Начать или продолжить отладку", -- src\editor\gui.lua
["Start or continue debugging"] = "Начать или продолжить отладку", -- src\editor\menu_project.lua
["Step &Into"] = "&Войти", -- src\editor\menu_project.lua
["Step &Over"] = "&Следующая строка", -- src\editor\menu_project.lua
["Step O&ut"] = "В&ыйти", -- src\editor\menu_project.lua
["Step into"] = "Войти в функцию", -- src\editor\gui.lua, src\editor\menu_project.lua
["Step out of the current function"] = "Выйти из текущей функции", -- src\editor\gui.lua, src\editor\menu_project.lua
["Step over"] = "Перейти на следующую строку", -- src\editor\gui.lua, src\editor\menu_project.lua
["Stop debugging and continue running the process"] = "Завершить отладку и продолжить текущий процесс", -- src\editor\gui.lua, src\editor\menu_project.lua
["Stop the currently running process"] = "Завершить текущий процесс", -- src\editor\gui.lua, src\editor\menu_project.lua
["Switch to or from full screen mode"] = "Переключить полноэкранный режим", -- src\editor\menu_view.lua
["Text not found."] = "Текст не найден.", -- src\editor\findreplace.lua
["The API file must be located in a subdirectory of the API directory."] = "Файл определений API должен быть расположен внутри папки API.", -- src\editor\autocomplete.lua
["Toggle Bookmark"] = "Установить/Снять закладку", -- src\editor\menu_edit.lua
["Toggle Break&point"] = "&Точка останова", -- src\editor\menu_project.lua
["Toggle breakpoint"] = "Переключить точку останова", -- src\editor\gui.lua, src\editor\menu_project.lua
["Tr&ace"] = "Т&рассировка", -- src\editor\menu_project.lua
["Trace execution showing each executed line"] = "Отслеживать выполнение, показывая каждую выполненную строку", -- src\editor\menu_project.lua
["Unable to create directory '%s'."] = nil, -- src\editor\filetree.lua
["Unable to create file '%s'."] = nil, -- src\editor\filetree.lua
["Unable to create directory '%s'."] = "Ошибка создания папки '%s'.", -- src\editor\filetree.lua
["Unable to create file '%s'."] = "Ошибка создания файла '%s'.", -- src\editor\filetree.lua
["Unable to load file '%s'."] = "Ошибка загрузки файла '%s'.", -- src\editor\commands.lua
["Unable to rename file '%s'."] = nil, -- src\editor\filetree.lua
["Unable to rename file '%s'."] = "Ошибка переименования файла '%s'.", -- src\editor\filetree.lua
["Unable to save file '%s': %s"] = "Ошибка сохранения файла '%s': %s", -- src\editor\commands.lua
["Unable to stop program (pid: %d), code %d."] = "Невозможно завершить программу (pid: %d), код %d.", -- src\editor\debugger.lua
["Undo last edit"] = "Отменить последнее действие", -- src\editor\menu_edit.lua
@@ -261,7 +277,11 @@ return {
["Welcome to the interactive Lua interpreter."] = "Добро пожаловать в интерактивный интерпретатор Lua.", -- src\editor\shellbox.lua
["Wrap ar&ound"] = "Продолжить сначала", -- src\editor\findreplace.lua
["You must save the program first."] = "Вы должны сначала сохранить программу.", -- src\editor\commands.lua
["Zoom In"] = "Приблизить", -- src\editor\menu_view.lua
["Zoom Out"] = "Удалить", -- src\editor\menu_view.lua
["Zoom to 100%"] = "Установить 100%", -- src\editor\menu_view.lua
["Zoom"] = "Установить масштаб", -- src\editor\menu_view.lua
["on line %d"] = "в строке %d", -- src\editor\debugger.lua, src\editor\editor.lua, src\editor\commands.lua
["traced %d instruction"] = {"выполнена %d инструкция", "выполнено %d инструкции", "выполнено %d инструкций"}, -- src\editor\debugger.lua
["unknown error"] = nil, -- src\editor\debugger.lua
["unknown error"] = "неизвестная ошибка", -- src\editor\debugger.lua
}

View File

@@ -181,16 +181,16 @@ local colors = {
NotepadPlusPlus = { -- contributed by Florian (https://github.com/SiENcE)
Background = H'FFFFFF',
CurrentLine = H'E9E2FF',
Selection = H'ADADA1',
Selection = H'C0C0C0',
Foreground = H'000000',
Comment = H'008000',
Red = H'FF6900',
Orange = H'00FF00',
Orange = H'FF0000',
Yellow = H'FF4E00',
Green = H'808080',
Aqua = H'260099',
Aqua = H'000080',
Blue = H'2123FF',
Purple = H'FFFFFF',
Purple = H'8000FF',
},
SciTeLuaIDE = { -- contributed by Jayanth Acharya
Background = H'1B1D1E',
@@ -268,7 +268,7 @@ return {
edge = {},
indicator = {
fncall = {fg = C.Purple, st = wxstc.wxSTC_INDIC_ROUNDBOX},
fncall = {fg = C.Purple, st = wxstc.wxSTC_INDIC_HIDDEN},
--[[ other possible values are:
wxSTC_INDIC_PLAIN Single-line underline
wxSTC_INDIC_SQUIGGLE Squiggly underline

View File

@@ -9,7 +9,7 @@ See [configuration](http://studio.zerobrane.com/doc-configuration.html) page for
--]]--
-- an example of how loaded configuration can be modified from this file
-- to modify loaded configuration for recognized extensions for lua files
local G = ... -- this now points to the global environment in the script
local luaspec = G.ide.specs['lua']
luaspec.exts[#luaspec.exts+1] = "luaz"
@@ -140,3 +140,16 @@ editor.nomousezoom = true
-- you can also change it between runs from Local Console by executing
-- `ide.config.corona = {skin = 'iPad'}`
corona = { skin = "iPad" }
-- to style individual keywords; `return` and `break` are shown in red
local G = ... -- this now points to the global environment in the script
local luaspec = G.ide.specs.lua
local num = #luaspec.keywords
-- take a new slot in the list of keywords (starting from 1)
luaspec.keywords[num+1] = 'return break'
-- remove 'return' from the list of "regular" keywords
luaspec.keywords[1] = luaspec.keywords[1]:gsub(' return', ''):gsub(' break', '')
-- assign new style to the added slot (starting from 0)
styles["keywords"..num] = {fg = {240, 0, 0}, b = true}

View File

@@ -1,30 +0,0 @@
--[[-- Copy snippets from this file to `user.lua` --]]--
--[[ Add `Evaluate in Console` option to the Edit menu
local G = ... -- this now points to the global environment in the script
local ide, wx, TR, ID = G.ide, G.wx, G.TR, G.ID
local postinit = ide.app.postinit
ide.app.postinit = function()
if postinit then postinit() end
local menu = ide.frame.menuBar:GetMenu(ide.frame.menuBar:FindMenu(TR("&Edit")))
menu:Append(ID "eval", "Evaluate in Console\tCtrl-E")
ide.frame:Connect(ID "eval", wx.wxEVT_COMMAND_MENU_SELECTED,
function () ShellExecuteCode(GetEditor():GetSelectedText()) end)
ide.frame:Connect(ID "eval", wx.wxEVT_UPDATE_UI,
function (event) event:Enable(GetEditor() and #GetEditor():GetSelectedText() > 0) end)
end
--]]
--[[ An example of how individual keywords can be styled
local G = ... -- this now points to the global environment in the script
local luaspec = G.ide.specs['lua']
local num = #luaspec.keywords
-- take a new slot in the list of keywords (starting from 1)
luaspec.keywords[num+1] = 'return'
-- remove 'return' from the list of "regular" keywords
luaspec.keywords[1] = luaspec.keywords[1]:gsub(' return', '')
-- assign new style to the added slot (starting from 0)
styles["keywords"..num] = {fg = {240, 0, 0}, b = true}
--]]

View File

@@ -53,7 +53,7 @@ return {
local cmd = ('"%s" %s "%s"'):format(busted, options, file)
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
return CommandLineRun(cmd,self:fworkdir(wfilename),true,false,nil,nil,
function() ide.debugger.pid = nil if rundebug then wx.wxRemoveFile(file) end end)
function() if rundebug then wx.wxRemoveFile(file) end end)
end,
fprojdir = function(self,wfilename)
return wfilename:GetPath(wx.wxPATH_GET_VOLUME)

View File

@@ -13,10 +13,8 @@ return {
if not corona then
local sep = win and ';' or ':'
local default =
win and ([[C:\Program Files\Corona SDK]]..sep..[[D:\Program Files\Corona SDK]]..sep..
[[C:\Program Files\Corona Labs\Corona SDK]]..sep..[[D:\Program Files\Corona Labs\Corona SDK]]..sep..
[[C:\Program Files (x86)\Corona SDK]]..sep..[[D:\Program Files (x86)\Corona SDK]]..sep..
[[C:\Program Files (x86)\Corona Labs\Corona SDK]]..sep..[[D:\Program Files (x86)\Corona Labs\Corona SDK]]..sep)
win and (GenerateProgramFilesPath('Corona SDK', sep)..sep..
GenerateProgramFilesPath('Corona Labs\\Corona SDK', sep)..sep)
or mac and ('/Applications/CoronaSDK/Corona Simulator.app/Contents/MacOS'..sep)
or ''
local path = default
@@ -56,12 +54,13 @@ return {
or MergeFullPath(GetPathWithSep(corona), "Resources/mobdebug.lua")
local mdbl = MergeFullPath(GetPathWithSep(ide.editorFilename), "lualibs/mobdebug/mobdebug.lua")
local needed = needRefresh(mdbl, mdbc)
local mdbcplugin = win and MergeFullPath(wx.wxStandardPaths.Get():GetUserLocalDataDir(),
"../../Roaming/Corona Labs/Corona Simulator/Plugins/mobdebug.lua")
if needed then
local copied = FileCopy(mdbl, mdbc)
-- couldn't copy to the Resources/ folder; not have permissions?
if not copied and win then
mdbc = MergeFullPath(wx.wxStandardPaths.Get():GetUserLocalDataDir(),
"../../Roaming/Corona Labs/Corona Simulator/Plugins/mobdebug.lua")
mdbc = mdbcplugin
needed = needRefresh(mdbl, mdbc)
copied = needed and FileCopy(mdbl, mdbc)
end
@@ -74,6 +73,10 @@ return {
if not copied then return end
end
end
-- remove debugger if copied to plugin directory as it may be obsolete
if mdbcplugin and mdbcplugin ~= mdbc and wx.wxFileExists(mdbcplugin) then
wx.wxRemoveFile(mdbcplugin)
end
end
local debugopt = mac and "-debug 1 -project " or "-debug "
@@ -82,8 +85,7 @@ return {
local cmd = ('"%s" %s"%s"%s')
:format(corona, rundebug and debugopt or "", file, skin)
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
return CommandLineRun(cmd,self:fworkdir(wfilename),true,false,nil,nil,
function() ide.debugger.pid = nil end)
return CommandLineRun(cmd,self:fworkdir(wfilename),true,false)
end,
fprojdir = function(self,wfilename)
return wfilename:GetPath(wx.wxPATH_GET_VOLUME)

View File

@@ -29,8 +29,7 @@ return {
if not gideros then
local sep = win and ';' or ':'
local default =
win and ([[C:\Program Files\Gideros]]..sep..[[D:\Program Files\Gideros]]..sep..
[[C:\Program Files (x86)\Gideros]]..sep..[[D:\Program Files (x86)\Gideros]]..sep)
win and (GenerateProgramFilesPath('Gideros', sep)..sep)
or mac and ('/Applications/Gideros Studio/Gideros Player.app/Contents/MacOS'..sep)
or ''
local path = default
@@ -90,15 +89,14 @@ return {
else
local cmd = ('"%s"'):format(gideros)
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
pid = CommandLineRun(cmd,self:fworkdir(wfilename),not mac,true,nil,nil,
function() ide.debugger.pid = nil end)
pid = CommandLineRun(cmd,self:fworkdir(wfilename),not mac,true)
if not pid then return end
end
do
DisplayOutputLn("Starting the player and waiting for the bridge to connect at '"..gdrbridge.."'.")
local cmd = ('"%s" %s'):format(gdrbridge, 'isconnected')
local attempts, connected = 12
local attempts, connected = 15
for _ = 1, attempts do
local proc = wx.wxProcess()
proc:Redirect()
@@ -107,7 +105,7 @@ return {
if not isValidPid(bid, cmd) then return end
local streamin = proc:GetInputStream()
for _ = 1, 20 do
for _ = 1, 30 do
if streamin:CanRead() then
connected = tonumber(streamin:Read(4096)) == 1
break end

View File

@@ -11,10 +11,7 @@ return {
gslshell = gslshell or ide.config.path.gslshell -- check if the path is configured
if not gslshell then
local sep = win and ';' or ':'
local default =
win and ([[C:\Program Files\gsl-shell]]..sep..[[D:\Program Files\gsl-shell]]..sep..
[[C:\Program Files (x86)\gsl-shell]]..sep..[[D:\Program Files (x86)\gsl-shell]]..sep)
or ''
local default = win and GenerateProgramFilesPath('gsl-shell', sep)..sep or ''
local path = default
..(os.getenv('PATH') or '')..sep
..(GetPathWithSep(self:fworkdir(wfilename)))..sep
@@ -82,7 +79,7 @@ return {
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
return CommandLineRun(cmd,self:fworkdir(wfilename),true,false,nil,nil,
function() ide.debugger.pid = nil if rundebug then wx.wxRemoveFile(filepath) end end)
function() if rundebug then wx.wxRemoveFile(filepath) end end)
end,
fprojdir = function(self,wfilename)
return wfilename:GetPath(wx.wxPATH_GET_VOLUME)

View File

@@ -46,8 +46,7 @@ return {
end
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
local pid = CommandLineRun(cmd,self:fworkdir(wfilename),true,false,nil,nil,
function() ide.debugger.pid = nil end)
local pid = CommandLineRun(cmd,self:fworkdir(wfilename),true,false)
-- restore PATH
wx.wxSetEnv("PATH", path)

View File

@@ -13,8 +13,7 @@ return {
if not love2d then
local sep = win and ';' or ':'
local default =
win and ([[C:\Program Files\love]]..sep..[[D:\Program Files\love]]..sep..
[[C:\Program Files (x86)\love]]..sep..[[D:\Program Files (x86)\love]]..sep)
win and (GenerateProgramFilesPath('love', sep)..sep)
or mac and ('/Applications/love.app/Contents/MacOS'..sep)
or ''
local path = default
@@ -46,8 +45,7 @@ return {
local cmd = ('"%s" "%s"%s%s'):format(love2d, self:fworkdir(wfilename),
params and " "..params or "", rundebug and ' -debug' or '')
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
return CommandLineRun(cmd,self:fworkdir(wfilename),true,false,nil,nil,
function() ide.debugger.pid = nil end)
return CommandLineRun(cmd,self:fworkdir(wfilename),true,true)
end,
fprojdir = function(self,wfilename)
return wfilename:GetPath(wx.wxPATH_GET_VOLUME)

View File

@@ -1,7 +1,5 @@
function MakeLuaInterpreter(version, name)
local exe
local function exePath(self, version)
local version = tostring(version):gsub('%.','')
local mainpath = ide.editorFilename:gsub("[^/\\]+$","")
@@ -19,11 +17,14 @@ return {
luaversion = version or '5.1',
fexepath = exePath,
frun = function(self,wfilename,rundebug)
exe = exe or self:fexepath(version or "")
local exe = self:fexepath(version or "")
local filepath = wfilename:GetFullPath()
if rundebug then
DebuggerAttachDefault({runstart = ide.config.debugger.runonstart == true})
-- update arg to point to the proper file
rundebug = ('if arg then arg[0] = [[%s]] end '):format(filepath)..rundebug
local tmpfile = wx.wxFileName()
tmpfile:AssignTempFileName(".")
filepath = tmpfile:GetFullPath()
@@ -52,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() ide.debugger.pid = nil if rundebug then wx.wxRemoveFile(filepath) end end)
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

@@ -60,10 +60,7 @@ return {
local cmd = '"'..exe..'" '..args
local pid = CommandLineRun(cmd,wdir,true,true,nil,self:fuid(wfilename),
function() ide.debugger.pid = nil end)
return pid
return CommandLineRun(cmd,wdir,true,true,nil,self:fuid(wfilename))
end,
fuid = function(self,wfilename) return "luxinia2: luajit "..wfilename:GetFullName() end,
fprojdir = function(self,wfilename)

View File

@@ -16,8 +16,7 @@ return {
local sep = win and ';' or ':'
local path =
win and ([[C:\Marmalade]]..sep..[[D:\Marmalade]]..sep..
[[C:\Program Files\Marmalade]]..sep..[[D:\Program Files\Marmalade]]..sep..
[[C:\Program Files (x86)\Marmalade]]..sep..[[D:\Program Files (x86)\Marmalade]]..sep)
GenerateProgramFilesPath('Marmalade', sep)..sep)
or mac and ([[/Applications/Marmalade.app/Contents]]..sep..
[[/Developer/Marmalade]]..sep)
or ''
@@ -113,8 +112,7 @@ return {
local cmd = ('"%s" %s'):format(quick, options)
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
return CommandLineRun(cmd,GetPathWithSep(projdir),true,true,nil,nil,
function() ide.debugger.pid = nil end)
return CommandLineRun(cmd,GetPathWithSep(projdir),true,true)
end,
fprojdir = function(self,wfilename)
return wfilename:GetPath(wx.wxPATH_GET_VOLUME)

View File

@@ -11,10 +11,7 @@ return {
moai = moai or ide.config.path.moai -- check if the path is configured
if not moai then
local sep = win and ';' or ':'
local default =
win and ([[C:\Program Files\moai]]..sep..[[D:\Program Files\moai]]..sep..
[[C:\Program Files (x86)\moai]]..sep..[[D:\Program Files (x86)\moai]]..sep)
or ''
local default = win and GenerateProgramFilesPath('moai', sep)..sep or ''
local path = default
..(os.getenv('PATH') or '')..sep
..(os.getenv('MOAI_BIN') or '')..sep
@@ -87,7 +84,7 @@ return {
or ('"%s" "%s"'):format(moai, file)
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
return CommandLineRun(cmd,self:fworkdir(wfilename),true,false,nil,nil,
function() ide.debugger.pid = nil if rundebug then wx.wxRemoveFile(file) end end)
function() if rundebug then wx.wxRemoveFile(file) end end)
end,
fprojdir = function(self,wfilename)
return wfilename:GetPath(wx.wxPATH_GET_VOLUME)

View File

@@ -18,7 +18,7 @@ if package.loaded["socket.http"] then
end
local socket = require "socket"
local gettime = socket.gettime
local coxpcall = require "coxpcall"
local WATCH_DOG_TIMEOUT = 120
@@ -60,7 +60,7 @@ local copas = {}
-- Meta information is public even if beginning with an "_"
copas._COPYRIGHT = "Copyright (C) 2005-2010 Kepler Project"
copas._DESCRIPTION = "Coroutine Oriented Portable Asynchronous Services"
copas._VERSION = "Copas 1.1.7"
copas._VERSION = "Copas 1.2.1"
-- Close the socket associated with the current connection after the handler finishes
copas.autoclose = true
@@ -117,6 +117,65 @@ local function newset()
return set
end
local fnil = function()end
local _sleeping = {
times = {}, -- list with wake-up times
cos = {}, -- list with coroutines, index matches the 'times' list
lethargy = {}, -- list of coroutines sleeping without a wakeup time
insert = fnil,
remove = fnil,
push = function(self, sleeptime, co)
if not co then return end
if sleeptime<0 then
--sleep until explicit wakeup through copas.wakeup
self.lethargy[co] = true
return
else
sleeptime = gettime() + sleeptime
end
local t, c = self.times, self.cos
local i, cou = 1, #t
--TODO: do a binary search
while i<=cou and t[i]<=sleeptime do i=i+1 end
table.insert(t, i, sleeptime)
table.insert(c, i, co)
end,
getnext = function(self) -- returns delay until next sleep expires, or nil if there is none
local t = self.times
local delay = t[1] and t[1] - gettime() or nil
return delay and math.max(delay, 0) or nil
end,
-- find the thread that should wake up to the time
pop = function(self, time)
local t, c = self.times, self.cos
if #t==0 or time<t[1] then return end
local co = c[1]
table.remove(t, 1)
table.remove(c, 1)
return co
end,
wakeup = function(self, co)
local let = self.lethargy
if let[co] then
self:push(0, co)
let[co] = nil
else
let = self.cos
for i=1,#let do
if let[i]==co then
table.remove(let, i)
local tm = self.times[i]
table.remove(self.times, i)
self:push(0, co)
return
end
end
end
end
} --_sleeping
local _servers = newset() -- servers being handled
local _reading_log = {}
local _writing_log = {}
@@ -140,7 +199,7 @@ function copas.receive(client, pattern, part)
_reading_log[client] = nil
return s, err, part
end
_reading_log[client] = os.time()
_reading_log[client] = gettime()
coroutine.yield(client, _reading)
until false
end
@@ -156,26 +215,26 @@ function copas.receivefrom(client, size)
_reading_log[client] = nil
return s, err, port
end
_reading_log[client] = os.time()
_reading_log[client] = gettime()
coroutine.yield(client, _reading)
until false
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
return s, err, part
end
_reading_log[client] = os.time()
coroutine.yield(client, _reading)
until false
err ~= "timeout" then
_reading_log[client] = nil
return s, err, part
end
_reading_log[client] = gettime()
coroutine.yield(client, _reading)
until false
end
-- sends data to a client. The operation is buffered and
@@ -191,14 +250,14 @@ function copas.send(client, data, from, to)
-- adds extra corrotine swap
-- garantees that high throuput dont take other threads to starvation
if (math.random(100) > 90) then
_writing_log[client] = os.time()
_writing_log[client] = gettime()
coroutine.yield(client, _writing)
end
if s or err ~= "timeout" then
_writing_log[client] = nil
return s, err,lastIndex
end
_writing_log[client] = os.time()
_writing_log[client] = gettime()
coroutine.yield(client, _writing)
until false
end
@@ -213,14 +272,14 @@ function copas.sendto(client, data, ip, port)
-- adds extra corrotine swap
-- garantees that high throuput dont take other threads to starvation
if (math.random(100) > 90) then
_writing_log[client] = os.time()
_writing_log[client] = gettime()
coroutine.yield(client, _writing)
end
if s or err ~= "timeout" then
_writing_log[client] = nil
return s, err
end
_writing_log[client] = os.time()
_writing_log[client] = gettime()
coroutine.yield(client, _writing)
until false
end
@@ -235,7 +294,7 @@ function copas.connect(skt, host, port)
_writing_log[skt] = nil
return ret, err
end
_writing_log[skt] = os.time()
_writing_log[skt] = gettime()
coroutine.yield(skt, _writing)
until false
return ret, err
@@ -251,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)
@@ -264,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.
@@ -293,7 +356,7 @@ local _skt_mt_udp = {__index = {
settimeout = function (self,time)
self.timeout=time
return
return true
end,
}}
@@ -385,6 +448,13 @@ function copas.addserver(server, handler, timeout)
addTCPserver(server, handler, timeout)
end
end
function copas.removeserver(server)
_servers[server] = nil
_reading:remove(server)
return server:close()
end
-------------------------------------------------------------------------------
-- Adds an new courotine thread to Copas dispatcher
-------------------------------------------------------------------------------
@@ -464,6 +534,24 @@ local _writable_t = {
}
addtaskWrite (_writable_t)
--
--sleeping threads task
local _sleeping_t = {
tick = function (self, time, ...)
_doTick(_sleeping:pop(time), ...)
end
}
-- yields the current coroutine and wakes it after 'sleeptime' seconds.
-- If sleeptime<0 then it sleeps until explicitly woken up using 'wakeup'
function copas.sleep(sleeptime)
coroutine.yield((sleeptime or 0), _sleeping)
end
-- Wakes up a sleeping coroutine 'co'.
function copas.wakeup(co)
_sleeping:wakeup(co)
end
local last_cleansing = 0
@@ -472,8 +560,8 @@ local last_cleansing = 0
-------------------------------------------------------------------------------
local function _select (timeout)
local err
local now = os.time()
local duration = os.difftime
local now = gettime()
local duration = function(t2, t1) return t2-t1 end
_readable_t._evs, _writable_t._evs, err = socket.select(_reading, _writing, timeout)
local r_evs, w_evs = _readable_t._evs, _writable_t._evs
@@ -512,6 +600,14 @@ end
-- handled (or nil + error message)
-------------------------------------------------------------------------------
function copas.step(timeout)
_sleeping_t:tick(gettime())
-- Need to wake up the select call it time for the next sleeping event
local nextwait = _sleeping:getnext()
if nextwait then
timeout = timeout and math.min(nextwait, timeout) or nextwait
end
local err = _select (timeout)
if err == "timeout" then return false end
@@ -537,4 +633,4 @@ function copas.loop(timeout)
end
end
return copas
return copas

View File

@@ -23,20 +23,7 @@
--! require 'luainspect.typecheck' (context)
-- boilerplate/utility
-- LUA_PATH="?.lua;/path/to/metalua/src/compiler/?.lua;/path/to/metalua/src/lib/?.lua"
-- import modules -- order is important
require "lexer"
require "gg"
require "mlp_lexer"
require "mlp_misc"
require "mlp_table"
require "mlp_meta"
require "mlp_expr"
require "mlp_stat"
--require "mlp_ext"
_G.mlc = {} -- make gg happy
-- Metalua:IMPROVE: make above imports simpler
local mlc = require 'metalua.compiler'.new()
local M = {}
@@ -102,10 +89,7 @@ end
-- FIX? filename currently ignored in Metalua
-- CATEGORY: Lua parsing
local function ast_from_string_helper(src, filename)
filename = filename or '(string)'
local lx = mlp.lexer:newstream (src, filename)
local ast = mlp.chunk(lx)
return ast
return mlc:src_to_ast(src, filename)
end
@@ -370,7 +354,7 @@ function M.get_keywords(ast, src)
-- Some binary operations have arguments reversed from lexical order.
-- For example, `a > b` becomes `Op{'lt', `Id 'b', `Id 'a'}
local oast =
(ast.tag == 'Op' and #ast == 3 and ast[2].lineinfo.first[3] > ast[3].lineinfo.first[3])
(ast.tag == 'Op' and #ast == 3 and tostring(ast[2].lineinfo.first):match('|L(%d+)') > tostring(ast[3].lineinfo.first):match('|L(%d+)'))
and {ast[1], ast[3], ast[2]} or ast
local i = 0
@@ -381,18 +365,17 @@ function M.get_keywords(ast, src)
-- Get position range [fpos,lpos] between subsequent children.
local fpos
if i == 0 then -- before first child
fpos = ast.lineinfo.first[3]
fpos = tostring(ast.lineinfo.first):match('|L(%d+)')
else
local last = oast[i].lineinfo.last; local c = last.comments
fpos = (c and #c > 0 and c[#c][3] or last[3]) + 1
fpos = (c and #c > 0 and c[#c][3] or tostring(last):match('|L(%d+)')) + 1
end
local lpos
if j == #ast+1 then -- after last child
lpos = ast.lineinfo.last[3]
lpos = tostring(ast.lineinfo.last):match('|L(%d+)')
else
local first = oast[j].lineinfo.first; local c = first.comments
--DEBUG('first', ast.tag, first[3], src:sub(first[3], first[3]+3))
lpos = (c and #c > 0 and c[1][2] or first[3]) - 1
lpos = (c and #c > 0 and c[1][2] or tostring(first):match('|L(%d+)')) - 1
end
-- Find keyword in range.
@@ -441,7 +424,7 @@ function M.ast_to_tokenlist(top_ast, src)
if isterminal[ast.tag] then -- Extract terminal
local token = ast
if ast.lineinfo then
token.fpos, token.lpos, token.ast = ast.lineinfo.first[3], ast.lineinfo.last[3], ast
token.fpos, token.lpos, token.ast = tostring(ast.lineinfo.first):match('|L(%d+)'), tostring(ast.lineinfo.last):match('|L(%d+)'), ast
table.insert(tokens, token)
end
else -- Extract non-terminal

View File

@@ -770,7 +770,7 @@ function M.infer_values(top_ast, tokenlist, src, report)
end
-- Any call to require is handled specially (source analysis).
if func == require and type(argvalues[1]) == 'string' then
local spath = ast.lineinfo.first[4] -- a HACK? relies on AST lineinfo
local spath = tostring(ast.lineinfo.first):gsub('<C|','<'):match('<([^|]+)') -- a HACK? relies on AST lineinfo
local val = M.require_inspect(argvalues[1], report, spath:gsub('[^\\/]+$', ''))
if known(val) and val ~= nil then
ast.value = val
@@ -820,7 +820,7 @@ function M.infer_values(top_ast, tokenlist, src, report)
local x
local val = function() x=nil end
local fpos = LA.ast_pos_range(ast, tokenlist)
local source = ast.lineinfo.first[4] -- a HACK? relies on AST lineinfo
local source = tostring(ast.lineinfo.first):gsub('<C|','<'):match('<([^|]+)') -- a HACK? relies on AST lineinfo
local linenum = LA.pos_to_linecol(fpos, src)
local retvals
if ENABLE_RETURN_ANALYSIS then
@@ -1269,7 +1269,7 @@ function M.ast_to_definition_position(ast, tokenlist)
if local_ast then
local tidx = LA.ast_idx_range_in_tokenlist(tokenlist, local_ast)
if tidx then
local spath = ast.lineinfo.first[4] -- a HACK? using lineinfo
local spath = tostring(ast.lineinfo.first):gsub('<C|','<'):match('<([^|]+)') -- a HACK? using lineinfo
fpos = tokenlist[tidx].fpos; path = spath
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,162 @@
---------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Convert between various code representation formats. Atomic
-- converters are written in extenso, others are composed automatically
-- by chaining the atomic ones together in a closure.
--
-- Supported formats are:
--
-- * srcfile: the name of a file containing sources.
-- * src: these sources as a single string.
-- * lexstream: a stream of lexemes.
-- * ast: an abstract syntax tree.
-- * proto: a (Yueliang) struture containing a high level
-- representation of bytecode. Largely based on the
-- Proto structure in Lua's VM
-- * bytecode: a string dump of the function, as taken by
-- loadstring() and produced by string.dump().
-- * function: an executable lua function in RAM.
--
--------------------------------------------------------------------------------
require 'checks'
local M = { }
--------------------------------------------------------------------------------
-- Order of the transformations. if 'a' is on the left of 'b', then a 'a' can
-- be transformed into a 'b' (but not the other way around).
-- M.sequence goes for numbers to format names, M.order goes from format
-- names to numbers.
--------------------------------------------------------------------------------
M.sequence = {
'srcfile', 'src', 'lexstream', 'ast', 'proto', 'bytecode', 'function' }
local arg_types = {
srcfile = { 'string', '?string' },
src = { 'string', '?string' },
lexstream = { 'lexer.stream', '?string' },
ast = { 'table', '?string' },
proto = { 'table', '?string' },
bytecode = { 'string', '?string' },
}
M.order= { }; for a,b in pairs(M.sequence) do M.order[b]=a end
local CONV = { } -- conversion metatable __index
function CONV :srcfile_to_src(x, name)
checks('metalua.compiler', 'string', '?string')
name = name or '@'..x
local f, msg = io.open (x, 'rb')
if not f then error(msg) end
local r, msg = f :read '*a'
if not r then error("Cannot read file '"..x.."': "..msg) end
f :close()
return r, name
end
function CONV :src_to_lexstream(src, name)
checks('metalua.compiler', 'string', '?string')
local r = self.parser.lexer :newstream (src, name)
return r, name
end
function CONV :lexstream_to_ast(lx, name)
checks('metalua.compiler', 'lexer.stream', '?string')
local r = self.parser.chunk(lx)
r.source = name
return r, name
end
local bytecode_compiler = nil -- cache to avoid repeated `pcall(require(...))`
local function get_bytecode_compiler()
if bytecode_compiler then return bytecode_compiler else
local status, result = pcall(require, 'metalua.compiler.bytecode')
if status then
bytecode_compiler = result
return result
elseif string.match(result, "not found") then
error "Compilation only available with full Metalua"
else error (result) end
end
end
function CONV :ast_to_proto(ast, name)
checks('metalua.compiler', 'table', '?string')
return get_bytecode_compiler().ast_to_proto(ast, name), name
end
function CONV :proto_to_bytecode(proto, name)
return get_bytecode_compiler().proto_to_bytecode(proto), name
end
function CONV :bytecode_to_function(bc, name)
checks('metalua.compiler', 'string', '?string')
return loadstring(bc, name)
end
-- Create all sensible combinations
for i=1,#M.sequence do
local src = M.sequence[i]
for j=i+2, #M.sequence do
local dst = M.sequence[j]
local dst_name = src.."_to_"..dst
local my_arg_types = arg_types[src]
local functions = { }
for k=i, j-1 do
local name = M.sequence[k].."_to_"..M.sequence[k+1]
local f = assert(CONV[name], name)
table.insert (functions, f)
end
CONV[dst_name] = function(self, a, b)
checks('metalua.compiler', unpack(my_arg_types))
for _, f in ipairs(functions) do
a, b = f(self, a, b)
end
return a, b
end
--printf("Created M.%s out of %s", dst_name, table.concat(n, ', '))
end
end
--------------------------------------------------------------------------------
-- This one goes in the "wrong" direction, cannot be composed.
--------------------------------------------------------------------------------
function CONV :function_to_bytecode(...) return string.dump(...) end
function CONV :ast_to_src(...)
require 'metalua.loader' -- ast_to_string isn't written in plain lua
return require 'metalua.compiler.ast_to_src' (...)
end
local MT = { __index=CONV, __type='metalua.compiler' }
function M.new()
local parser = require 'metalua.compiler.parser' .new()
local self = { parser = parser }
setmetatable(self, MT)
return self
end
return M

View File

@@ -0,0 +1,42 @@
--------------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
--------------------------------------------------------------------------------
-- Export all public APIs from sub-modules, squashed into a flat spacename
local MT = { __type='metalua.compiler.parser' }
local MODULE_REL_NAMES = { "annot.grammar", "expr", "meta", "misc",
"stat", "table", "ext" }
local function new()
local M = {
lexer = require "metalua.compiler.parser.lexer" ();
extensions = { } }
for _, rel_name in ipairs(MODULE_REL_NAMES) do
local abs_name = "metalua.compiler.parser."..rel_name
local extender = require (abs_name)
if not M.extensions[abs_name] then
if type (extender) == 'function' then extender(M) end
M.extensions[abs_name] = extender
end
end
return setmetatable(M, MT)
end
return { new = new }

View File

@@ -0,0 +1,48 @@
--------------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
--------------------------------------------------------------------------------
require 'checks'
local gg = require 'metalua.grammar.generator'
local M = { }
function M.opt(mlc, primary, a_type)
checks('table', 'table|function', 'string')
return gg.sequence{
primary,
gg.onkeyword{ "#", function() return assert(mlc.annot[a_type]) end },
builder = function(x)
local t, annot = unpack(x)
return annot and { tag='Annot', t, annot } or t
end }
end
-- split a list of "foo" and "`Annot{foo, annot}" into a list of "foo"
-- and a list of "annot".
-- No annot list is returned if none of the elements were annotated.
function M.split(lst)
local x, a, some = { }, { }, false
for i, p in ipairs(lst) do
if p.tag=='Annot' then
some, x[i], a[i] = true, unpack(p)
else x[i] = p end
end
if some then return x, a else return lst end
end
return M

View File

@@ -0,0 +1,112 @@
--------------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
--------------------------------------------------------------------------------
local gg = require 'metalua.grammar.generator'
return function(M)
local _M = gg.future(M)
M.lexer :add '->'
local A = { }
local _A = gg.future(A)
M.annot = A
-- Type identifier: Lua keywords such as `"nil"` allowed.
function M.annot.tid(lx)
local w = lx :next()
local t = w.tag
if t=='Keyword' and w[1] :match '^[%a_][%w_]*$' or w.tag=='Id'
then return {tag='TId'; lineinfo=w.lineinfo; w[1]}
else return gg.parse_error (lx, 'tid expected') end
end
local field_types = { var='TVar'; const='TConst';
currently='TCurrently'; field='TField' }
-- TODO check lineinfo
function M.annot.tf(lx)
local tk = lx:next()
local w = tk[1]
local tag = field_types[w]
if not tag then error ('Invalid field type '..w)
elseif tag=='TField' then return {tag='TField'} else
local te = M.te(lx)
return {tag=tag; te}
end
end
M.annot.tebar_content = gg.list{
name = 'tebar content',
primary = _A.te,
separators = { ",", ";" },
terminators = ")" }
M.annot.tebar = gg.multisequence{
name = 'annot.tebar',
--{ '*', builder = 'TDynbar' }, -- maybe not user-available
{ '(', _A.tebar_content, ')',
builder = function(x) return x[1] end },
{ _A.te }
}
M.annot.te = gg.multisequence{
name = 'annot.te',
{ _A.tid, builder=function(x) return x[1] end },
{ '*', builder = 'TDyn' },
{ "[",
gg.list{
primary = gg.sequence{
_M.expr, "=", _A.tf,
builder = 'TPair'
},
separators = { ",", ";" },
terminators = { "]", "|" } },
gg.onkeyword{ "|", _A.tf },
"]",
builder = function(x)
local fields, other = unpack(x)
return { tag='TTable', other or {tag='TField'}, fields }
end }, -- "[ ... ]"
{ '(', _A.tebar_content, ')', '->', '(', _A.tebar_content, ')',
builder = function(x)
local p, r = unpack(x)
return {tag='TFunction', p, r }
end } }
M.annot.ts = gg.multisequence{
name = 'annot.ts',
{ 'return', _A.tebar_content, builder='TReturn' },
{ _A.tid, builder = function(x)
if x[1][1]=='pass' then return {tag='TPass'}
else error "Bad statement type" end
end } }
-- TODO: add parsers for statements:
-- #return tebar
-- #alias = te
-- #ell = tf
--[[
M.annot.stat_annot = gg.sequence{
gg.list{ primary=_A.tid, separators='.' },
'=',
XXX??,
builder = 'Annot' }
--]]
return M.annot
end

View File

@@ -0,0 +1,27 @@
--------------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
--------------------------------------------------------------------------------
-- Shared common parser table. It will be filled by parser.init(),
-- and every other module will be able to call its elements at runtime.
--
-- If the table was directly created in parser.init, a circular
-- dependency would be created: parser.init depends on other modules to fill the table,
-- so other modules can't simultaneously depend on it.
return { }

View File

@@ -0,0 +1,206 @@
-------------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
--
-- Exported API:
-- * [mlp.expr()]
-- * [mlp.expr_list()]
-- * [mlp.func_val()]
--
-------------------------------------------------------------------------------
local pp = require 'metalua.pprint'
local gg = require 'metalua.grammar.generator'
local annot = require 'metalua.compiler.parser.annot.generator'
return function(M)
local _M = gg.future(M)
local _table = gg.future(M, 'table')
local _meta = gg.future(M, 'meta') -- TODO move to ext?
local _annot = gg.future(M, 'annot') -- TODO move to annot
--------------------------------------------------------------------------------
-- Non-empty expression list. Actually, this isn't used here, but that's
-- handy to give to users.
--------------------------------------------------------------------------------
M.expr_list = gg.list{ primary=_M.expr, separators="," }
--------------------------------------------------------------------------------
-- Helpers for function applications / method applications
--------------------------------------------------------------------------------
M.func_args_content = gg.list{
name = "function arguments",
primary = _M.expr,
separators = ",",
terminators = ")" }
-- Used to parse methods
M.method_args = gg.multisequence{
name = "function argument(s)",
{ "{", _table.content, "}" },
{ "(", _M.func_args_content, ")", builder = unpack },
{ "+{", _meta.quote_content, "}" },
-- TODO lineinfo?
function(lx) local r = M.opt_string(lx); return r and {r} or { } end }
--------------------------------------------------------------------------------
-- [func_val] parses a function, from opening parameters parenthese to
-- "end" keyword included. Used for anonymous functions as well as
-- function declaration statements (both local and global).
--
-- It's wrapped in a [_func_val] eta expansion, so that when expr
-- parser uses the latter, they will notice updates of [func_val]
-- definitions.
--------------------------------------------------------------------------------
M.func_params_content = gg.list{
name="function parameters",
gg.multisequence{ { "...", builder = "Dots" }, annot.opt(M, _M.id, 'te') },
separators = ",", terminators = {")", "|"} }
-- TODO move to annot
M.func_val = gg.sequence{
name = "function body",
"(", _M.func_params_content, ")", _M.block, "end",
builder = function(x)
local params, body = unpack(x)
local annots, some = { }, false
for i, p in ipairs(params) do
if p.tag=='Annot' then
params[i], annots[i], some = p[1], p[2], true
else annots[i] = false end
end
if some then return { tag='Function', params, body, annots }
else return { tag='Function', params, body } end
end }
local func_val = function(lx) return M.func_val(lx) end
--------------------------------------------------------------------------------
-- Default parser for primary expressions
--------------------------------------------------------------------------------
function M.id_or_literal (lx)
local a = lx:next()
if a.tag~="Id" and a.tag~="String" and a.tag~="Number" then
local msg
if a.tag=='Eof' then
msg = "End of file reached when an expression was expected"
elseif a.tag=='Keyword' then
msg = "An expression was expected, and `"..a[1]..
"' can't start an expression"
else
msg = "Unexpected expr token " .. pp.tostring (a)
end
gg.parse_error (lx, msg)
end
return a
end
--------------------------------------------------------------------------------
-- Builder generator for operators. Wouldn't be worth it if "|x|" notation
-- were allowed, but then lua 5.1 wouldn't compile it
--------------------------------------------------------------------------------
-- opf1 = |op| |_,a| `Op{ op, a }
local function opf1 (op) return
function (_,a) return { tag="Op", op, a } end end
-- opf2 = |op| |a,_,b| `Op{ op, a, b }
local function opf2 (op) return
function (a,_,b) return { tag="Op", op, a, b } end end
-- opf2r = |op| |a,_,b| `Op{ op, b, a } -- (args reversed)
local function opf2r (op) return
function (a,_,b) return { tag="Op", op, b, a } end end
local function op_ne(a, _, b)
-- This version allows to remove the "ne" operator from the AST definition.
-- However, it doesn't always produce the exact same bytecode as Lua 5.1.
return { tag="Op", "not",
{ tag="Op", "eq", a, b, lineinfo= {
first = a.lineinfo.first, last = b.lineinfo.last } } }
end
--------------------------------------------------------------------------------
--
-- complete expression
--
--------------------------------------------------------------------------------
-- FIXME: set line number. In [expr] transformers probably
M.expr = gg.expr {
name = "expression",
primary = gg.multisequence{
name = "expr primary",
{ "(", _M.expr, ")", builder = "Paren" },
{ "function", _M.func_val, builder = unpack },
{ "-{", _meta.splice_content, "}", builder = unpack },
{ "+{", _meta.quote_content, "}", builder = unpack },
{ "nil", builder = "Nil" },
{ "true", builder = "True" },
{ "false", builder = "False" },
{ "...", builder = "Dots" },
{ "{", _table.content, "}", builder = unpack },
_M.id_or_literal },
infix = {
name = "expr infix op",
{ "+", prec = 60, builder = opf2 "add" },
{ "-", prec = 60, builder = opf2 "sub" },
{ "*", prec = 70, builder = opf2 "mul" },
{ "/", prec = 70, builder = opf2 "div" },
{ "%", prec = 70, builder = opf2 "mod" },
{ "^", prec = 90, builder = opf2 "pow", assoc = "right" },
{ "..", prec = 40, builder = opf2 "concat", assoc = "right" },
{ "==", prec = 30, builder = opf2 "eq" },
{ "~=", prec = 30, builder = op_ne },
{ "<", prec = 30, builder = opf2 "lt" },
{ "<=", prec = 30, builder = opf2 "le" },
{ ">", prec = 30, builder = opf2r "lt" },
{ ">=", prec = 30, builder = opf2r "le" },
{ "and",prec = 20, builder = opf2 "and" },
{ "or", prec = 10, builder = opf2 "or" } },
prefix = {
name = "expr prefix op",
{ "not", prec = 80, builder = opf1 "not" },
{ "#", prec = 80, builder = opf1 "len" },
{ "-", prec = 80, builder = opf1 "unm" } },
suffix = {
name = "expr suffix op",
{ "[", _M.expr, "]", builder = function (tab, idx)
return {tag="Index", tab, idx[1]} end},
{ ".", _M.id, builder = function (tab, field)
return {tag="Index", tab, _M.id2string(field[1])} end },
{ "(", _M.func_args_content, ")", builder = function(f, args)
return {tag="Call", f, unpack(args[1])} end },
{ "{", _table.content, "}", builder = function (f, arg)
return {tag="Call", f, arg[1]} end},
{ ":", _M.id, _M.method_args, builder = function (obj, post)
local m_name, args = unpack(post)
return {tag="Invoke", obj, _M.id2string(m_name), unpack(args)} end},
{ "+{", _meta.quote_content, "}", builder = function (f, arg)
return {tag="Call", f, arg[1] } end },
default = { name="opt_string_arg", parse = _M.opt_string, builder = function(f, arg)
return {tag="Call", f, arg } end } } }
return M
end

View File

@@ -0,0 +1,96 @@
-------------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
-------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Non-Lua syntax extensions
--
--------------------------------------------------------------------------------
local gg = require 'metalua.grammar.generator'
return function(M)
local _M = gg.future(M)
---------------------------------------------------------------------------
-- Algebraic Datatypes
----------------------------------------------------------------------------
local function adt (lx)
local node = _M.id (lx)
local tagval = node[1]
-- tagkey = `Pair{ `String "key", `String{ -{tagval} } }
local tagkey = { tag="Pair", {tag="String", "tag"}, {tag="String", tagval} }
if lx:peek().tag == "String" or lx:peek().tag == "Number" then
-- TODO support boolean litterals
return { tag="Table", tagkey, lx:next() }
elseif lx:is_keyword (lx:peek(), "{") then
local x = M.table.table (lx)
table.insert (x, 1, tagkey)
return x
else return { tag="Table", tagkey } end
end
M.adt = gg.sequence{ "`", adt, builder = unpack }
M.expr.primary :add(M.adt)
----------------------------------------------------------------------------
-- Anonymous lambda
----------------------------------------------------------------------------
M.lambda_expr = gg.sequence{
"|", _M.func_params_content, "|", _M.expr,
builder = function (x)
local li = x[2].lineinfo
return { tag="Function", x[1],
{ {tag="Return", x[2], lineinfo=li }, lineinfo=li } }
end }
M.expr.primary :add (M.lambda_expr)
--------------------------------------------------------------------------------
-- Allows to write "a `f` b" instead of "f(a, b)". Taken from Haskell.
--------------------------------------------------------------------------------
function M.expr_in_backquotes (lx) return M.expr(lx, 35) end -- 35=limited precedence
M.expr.infix :add{ name = "infix function",
"`", _M.expr_in_backquotes, "`", prec = 35, assoc="left",
builder = function(a, op, b) return {tag="Call", op[1], a, b} end }
--------------------------------------------------------------------------------
-- C-style op+assignments
-- TODO: no protection against side-effects in LHS vars.
--------------------------------------------------------------------------------
local function op_assign(kw, op)
local function rhs(a, b) return { tag="Op", op, a, b } end
local function f(a,b)
if #a ~= #b then gg.parse_error "assymetric operator+assignment" end
local right = { }
local r = { tag="Set", a, right }
for i=1, #a do right[i] = { tag="Op", op, a[i], b[i] } end
return r
end
M.lexer :add (kw)
M.assignments[kw] = f
end
local ops = { add='+='; sub='-='; mul='*='; div='/=' }
for ast_op_name, keyword in pairs(ops) do op_assign(keyword, ast_op_name) end
return M
end

View File

@@ -0,0 +1,43 @@
--------------------------------------------------------------------------------
-- Copyright (c) 2006-2014 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
--------------------------------------------------------------------------------
----------------------------------------------------------------------
-- Generate a new lua-specific lexer, derived from the generic lexer.
----------------------------------------------------------------------
local generic_lexer = require 'metalua.grammar.lexer'
return function()
local lexer = generic_lexer.lexer :clone()
local keywords = {
"and", "break", "do", "else", "elseif",
"end", "false", "for", "function",
"goto", -- Lua5.2
"if",
"in", "local", "nil", "not", "or", "repeat",
"return", "then", "true", "until", "while",
"...", "..", "==", ">=", "<=", "~=",
"::", -- Lua5,2
"+{", "-{" } -- Metalua
for _, w in ipairs(keywords) do lexer :add (w) end
return lexer
end

View File

@@ -0,0 +1,138 @@
-------------------------------------------------------------------------------
-- Copyright (c) 2006-2014 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
-------------------------------------------------------------------------------
-- Compile-time metaprogramming features: splicing ASTs generated during compilation,
-- AST quasi-quoting helpers.
local gg = require 'metalua.grammar.generator'
return function(M)
local _M = gg.future(M)
M.meta={ }
local _MM = gg.future(M.meta)
--------------------------------------------------------------------------------
-- External splicing: compile an AST into a chunk, load and evaluate
-- that chunk, and replace the chunk by its result (which must also be
-- an AST).
--------------------------------------------------------------------------------
-- TODO: that's not part of the parser
function M.meta.eval (ast)
-- TODO: should there be one mlc per splice, or per parser instance?
local mlc = require 'metalua.compiler'.new()
local f = mlc :ast_to_function (ast, '=splice')
local result=f(M) -- splices act on the current parser
return result
end
----------------------------------------------------------------------------
-- Going from an AST to an AST representing that AST
-- the only hash-part key being lifted is `"tag"`.
-- Doesn't lift subtrees protected inside a `Splice{ ... }.
-- e.g. change `Foo{ 123 } into
-- `Table{ `Pair{ `String "tag", `String "foo" }, `Number 123 }
----------------------------------------------------------------------------
local function lift (t)
--print("QUOTING:", table.tostring(t, 60,'nohash'))
local cases = { }
function cases.table (t)
local mt = { tag = "Table" }
--table.insert (mt, { tag = "Pair", quote "quote", { tag = "True" } })
if t.tag == "Splice" then
assert (#t==1, "Invalid splice")
local sp = t[1]
return sp
elseif t.tag then
table.insert (mt, { tag="Pair", lift "tag", lift(t.tag) })
end
for _, v in ipairs (t) do
table.insert (mt, lift(v))
end
return mt
end
function cases.number (t) return { tag = "Number", t, quote = true } end
function cases.string (t) return { tag = "String", t, quote = true } end
function cases.boolean (t) return { tag = t and "True" or "False", t, quote = true } end
local f = cases [type(t)]
if f then return f(t) else error ("Cannot quote an AST containing "..tostring(t)) end
end
M.meta.lift = lift
--------------------------------------------------------------------------------
-- when this variable is false, code inside [-{...}] is compiled and
-- avaluated immediately. When it's true (supposedly when we're
-- parsing data inside a quasiquote), [-{foo}] is replaced by
-- [`Splice{foo}], which will be unpacked by [quote()].
--------------------------------------------------------------------------------
local in_a_quote = false
--------------------------------------------------------------------------------
-- Parse the inside of a "-{ ... }"
--------------------------------------------------------------------------------
function M.meta.splice_content (lx)
local parser_name = "expr"
if lx:is_keyword (lx:peek(2), ":") then
local a = lx:next()
lx:next() -- skip ":"
assert (a.tag=="Id", "Invalid splice parser name")
parser_name = a[1]
end
-- TODO FIXME running a new parser with the old lexer?!
local parser = require 'metalua.compiler.parser'.new()
local ast = parser [parser_name](lx)
if in_a_quote then -- only prevent quotation in this subtree
--printf("SPLICE_IN_QUOTE:\n%s", _G.table.tostring(ast, "nohash", 60))
return { tag="Splice", ast }
else -- convert in a block, eval, replace with result
if parser_name == "expr" then ast = { { tag="Return", ast } }
elseif parser_name == "stat" then ast = { ast }
elseif parser_name ~= "block" then
error ("splice content must be an expr, stat or block") end
--printf("EXEC THIS SPLICE:\n%s", _G.table.tostring(ast, "nohash", 60))
return M.meta.eval (ast)
end
end
M.meta.splice = gg.sequence{ "-{", _MM.splice_content, "}", builder=unpack }
--------------------------------------------------------------------------------
-- Parse the inside of a "+{ ... }"
--------------------------------------------------------------------------------
function M.meta.quote_content (lx)
local parser
if lx:is_keyword (lx:peek(2), ":") then -- +{parser: content }
local parser_name = M.id(lx)[1]
parser = M[parser_name]
lx:next() -- skip ":"
else -- +{ content }
parser = M.expr
end
local prev_iq = in_a_quote
in_a_quote = true
--print("IN_A_QUOTE")
local content = parser (lx)
local q_content = M.meta.lift (content)
in_a_quote = prev_iq
return q_content
end
return M
end

View File

@@ -0,0 +1,147 @@
-------------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
--
-- Summary: metalua parser, miscellaneous utility functions.
--
-------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Exported API:
-- * [mlp.fget()]
-- * [mlp.id()]
-- * [mlp.opt_id()]
-- * [mlp.id_list()]
-- * [mlp.string()]
-- * [mlp.opt_string()]
-- * [mlp.id2string()]
--
--------------------------------------------------------------------------------
local gg = require 'metalua.grammar.generator'
-- TODO: replace splice-aware versions with naive ones, move etensions in ./meta
return function(M)
local _M = gg.future(M)
--------------------------------------------------------------------------------
-- Try to read an identifier (possibly as a splice), or return [false] if no
-- id is found.
--------------------------------------------------------------------------------
function M.opt_id (lx)
local a = lx:peek();
if lx:is_keyword (a, "-{") then
local v = M.meta.splice(lx)
if v.tag ~= "Id" and v.tag ~= "Splice" then
gg.parse_error(lx, "Bad id splice")
end
return v
elseif a.tag == "Id" then return lx:next()
else return false end
end
--------------------------------------------------------------------------------
-- Mandatory reading of an id: causes an error if it can't read one.
--------------------------------------------------------------------------------
function M.id (lx)
return M.opt_id (lx) or gg.parse_error(lx,"Identifier expected")
end
--------------------------------------------------------------------------------
-- Common helper function
--------------------------------------------------------------------------------
M.id_list = gg.list { primary = _M.id, separators = "," }
--------------------------------------------------------------------------------
-- Converts an identifier into a string. Hopefully one day it'll handle
-- splices gracefully, but that proves quite tricky.
--------------------------------------------------------------------------------
function M.id2string (id)
--print("id2string:", disp.ast(id))
if id.tag == "Id" then id.tag = "String"; return id
elseif id.tag == "Splice" then
error ("id2string on splice not implemented")
-- Evaluating id[1] will produce `Id{ xxx },
-- and we want it to produce `String{ xxx }.
-- The following is the plain notation of:
-- +{ `String{ `Index{ `Splice{ -{id[1]} }, `Number 1 } } }
return { tag="String", { tag="Index", { tag="Splice", id[1] },
{ tag="Number", 1 } } }
else error ("Identifier expected: "..table.tostring(id, 'nohash')) end
end
--------------------------------------------------------------------------------
-- Read a string, possibly spliced, or return an error if it can't
--------------------------------------------------------------------------------
function M.string (lx)
local a = lx:peek()
if lx:is_keyword (a, "-{") then
local v = M.meta.splice(lx)
if v.tag ~= "String" and v.tag ~= "Splice" then
gg.parse_error(lx,"Bad string splice")
end
return v
elseif a.tag == "String" then return lx:next()
else error "String expected" end
end
--------------------------------------------------------------------------------
-- Try to read a string, or return false if it can't. No splice allowed.
--------------------------------------------------------------------------------
function M.opt_string (lx)
return lx:peek().tag == "String" and lx:next()
end
--------------------------------------------------------------------------------
-- Chunk reader: block + Eof
--------------------------------------------------------------------------------
function M.skip_initial_sharp_comment (lx)
-- Dirty hack: I'm happily fondling lexer's private parts
-- FIXME: redundant with lexer:newstream()
lx :sync()
local i = lx.src:match ("^#.-\n()", lx.i)
if i then
lx.i = i
lx.column_offset = i
lx.line = lx.line and lx.line + 1 or 1
end
end
local function chunk (lx)
if lx:peek().tag == 'Eof' then
return { } -- handle empty files
else
M.skip_initial_sharp_comment (lx)
local chunk = M.block (lx)
if lx:peek().tag ~= "Eof" then
gg.parse_error(lx, "End-of-file expected")
end
return chunk
end
end
-- chunk is wrapped in a sequence so that it has a "transformer" field.
M.chunk = gg.sequence { chunk, builder = unpack }
return M
end

View File

@@ -0,0 +1,279 @@
------------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
--
-- Summary: metalua parser, statement/block parser. This is part of the
-- definition of module [mlp].
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
--
-- Exports API:
-- * [mlp.stat()]
-- * [mlp.block()]
-- * [mlp.for_header()]
--
-------------------------------------------------------------------------------
local lexer = require 'metalua.grammar.lexer'
local gg = require 'metalua.grammar.generator'
local annot = require 'metalua.compiler.parser.annot.generator'
--------------------------------------------------------------------------------
-- List of all keywords that indicate the end of a statement block. Users are
-- likely to extend this list when designing extensions.
--------------------------------------------------------------------------------
return function(M)
local _M = gg.future(M)
M.block_terminators = { "else", "elseif", "end", "until", ")", "}", "]" }
-- FIXME: this must be handled from within GG!!!
-- FIXME: there's no :add method in the list anyway. Added by gg.list?!
function M.block_terminators :add(x)
if type (x) == "table" then for _, y in ipairs(x) do self :add (y) end
else table.insert (self, x) end
end
----------------------------------------------------------------------------
-- list of statements, possibly followed by semicolons
----------------------------------------------------------------------------
M.block = gg.list {
name = "statements block",
terminators = M.block_terminators,
primary = function (lx)
-- FIXME use gg.optkeyword()
local x = M.stat (lx)
if lx:is_keyword (lx:peek(), ";") then lx:next() end
return x
end }
----------------------------------------------------------------------------
-- Helper function for "return <expr_list>" parsing.
-- Called when parsing return statements.
-- The specific test for initial ";" is because it's not a block terminator,
-- so without it gg.list would choke on "return ;" statements.
-- We don't make a modified copy of block_terminators because this list
-- is sometimes modified at runtime, and the return parser would get out of
-- sync if it was relying on a copy.
----------------------------------------------------------------------------
local return_expr_list_parser = gg.multisequence{
{ ";" , builder = function() return { } end },
default = gg.list {
_M.expr, separators = ",", terminators = M.block_terminators } }
local for_vars_list = gg.list{
name = "for variables list",
primary = _M.id,
separators = ",",
terminators = "in" }
----------------------------------------------------------------------------
-- for header, between [for] and [do] (exclusive).
-- Return the `Forxxx{...} AST, without the body element (the last one).
----------------------------------------------------------------------------
function M.for_header (lx)
local vars = M.id_list(lx)
if lx :is_keyword (lx:peek(), "=") then
if #vars ~= 1 then
gg.parse_error (lx, "numeric for only accepts one variable")
end
lx:next() -- skip "="
local exprs = M.expr_list (lx)
if #exprs < 2 or #exprs > 3 then
gg.parse_error (lx, "numeric for requires 2 or 3 boundaries")
end
return { tag="Fornum", vars[1], unpack (exprs) }
else
if not lx :is_keyword (lx :next(), "in") then
gg.parse_error (lx, '"=" or "in" expected in for loop')
end
local exprs = M.expr_list (lx)
return { tag="Forin", vars, exprs }
end
end
----------------------------------------------------------------------------
-- Function def parser helper: id ( . id ) *
----------------------------------------------------------------------------
local function fn_builder (list)
local acc = list[1]
local first = acc.lineinfo.first
for i = 2, #list do
local index = M.id2string(list[i])
local li = lexer.new_lineinfo(first, index.lineinfo.last)
acc = { tag="Index", acc, index, lineinfo=li }
end
return acc
end
local func_name = gg.list{ _M.id, separators = ".", builder = fn_builder }
----------------------------------------------------------------------------
-- Function def parser helper: ( : id )?
----------------------------------------------------------------------------
local method_name = gg.onkeyword{ name = "method invocation", ":", _M.id,
transformers = { function(x) return x and x.tag=='Id' and M.id2string(x) end } }
----------------------------------------------------------------------------
-- Function def builder
----------------------------------------------------------------------------
local function funcdef_builder(x)
local name, method, func = unpack(x)
if method then
name = { tag="Index", name, method,
lineinfo = {
first = name.lineinfo.first,
last = method.lineinfo.last } }
table.insert (func[1], 1, {tag="Id", "self"})
end
local r = { tag="Set", {name}, {func} }
r[1].lineinfo = name.lineinfo
r[2].lineinfo = func.lineinfo
return r
end
----------------------------------------------------------------------------
-- if statement builder
----------------------------------------------------------------------------
local function if_builder (x)
local cond_block_pairs, else_block, r = x[1], x[2], {tag="If"}
local n_pairs = #cond_block_pairs
for i = 1, n_pairs do
local cond, block = unpack(cond_block_pairs[i])
r[2*i-1], r[2*i] = cond, block
end
if else_block then table.insert(r, #r+1, else_block) end
return r
end
--------------------------------------------------------------------------------
-- produce a list of (expr,block) pairs
--------------------------------------------------------------------------------
local elseifs_parser = gg.list {
gg.sequence { _M.expr, "then", _M.block , name='elseif parser' },
separators = "elseif",
terminators = { "else", "end" }
}
local annot_expr = gg.sequence {
_M.expr,
gg.onkeyword{ "#", gg.future(M, 'annot').tf },
builder = function(x)
local e, a = unpack(x)
if a then return { tag='Annot', e, a }
else return e end
end }
local annot_expr_list = gg.list {
primary = annot.opt(M, _M.expr, 'tf'), separators = ',' }
------------------------------------------------------------------------
-- assignments and calls: statements that don't start with a keyword
------------------------------------------------------------------------
local function assign_or_call_stat_parser (lx)
local e = annot_expr_list (lx)
local a = lx:is_keyword(lx:peek())
local op = a and M.assignments[a]
-- TODO: refactor annotations
if op then
--FIXME: check that [e] is a LHS
lx :next()
local annots
e, annots = annot.split(e)
local v = M.expr_list (lx)
if type(op)=="string" then return { tag=op, e, v, annots }
else return op (e, v) end
else
assert (#e > 0)
if #e > 1 then
gg.parse_error (lx,
"comma is not a valid statement separator; statement can be "..
"separated by semicolons, or not separated at all")
elseif e[1].tag ~= "Call" and e[1].tag ~= "Invoke" then
local typename
if e[1].tag == 'Id' then
typename = '("'..e[1][1]..'") is an identifier'
elseif e[1].tag == 'Op' then
typename = "is an arithmetic operation"
else typename = "is of type '"..(e[1].tag or "<list>").."'" end
gg.parse_error (lx,
"This expression %s; "..
"a statement was expected, and only function and method call "..
"expressions can be used as statements", typename);
end
return e[1]
end
end
M.local_stat_parser = gg.multisequence{
-- local function <name> <func_val>
{ "function", _M.id, _M.func_val, builder =
function(x)
local vars = { x[1], lineinfo = x[1].lineinfo }
local vals = { x[2], lineinfo = x[2].lineinfo }
return { tag="Localrec", vars, vals }
end },
-- local <id_list> ( = <expr_list> )?
default = gg.sequence{
gg.list{
primary = annot.opt(M, _M.id, 'tf'),
separators = ',' },
gg.onkeyword{ "=", _M.expr_list },
builder = function(x)
local annotated_left, right = unpack(x)
local left, annotations = annot.split(annotated_left)
return {tag="Local", left, right or { }, annotations }
end } }
------------------------------------------------------------------------
-- statement
------------------------------------------------------------------------
M.stat = gg.multisequence {
name = "statement",
{ "do", _M.block, "end", builder =
function (x) return { tag="Do", unpack (x[1]) } end },
{ "for", _M.for_header, "do", _M.block, "end", builder =
function (x) x[1][#x[1]+1] = x[2]; return x[1] end },
{ "function", func_name, method_name, _M.func_val, builder=funcdef_builder },
{ "while", _M.expr, "do", _M.block, "end", builder = "While" },
{ "repeat", _M.block, "until", _M.expr, builder = "Repeat" },
{ "local", _M.local_stat_parser, builder = unpack },
{ "return", return_expr_list_parser, builder =
function(x) x[1].tag='Return'; return x[1] end },
{ "break", builder = function() return { tag="Break" } end },
{ "-{", gg.future(M, 'meta').splice_content, "}", builder = unpack },
{ "if", gg.nonempty(elseifs_parser), gg.onkeyword{ "else", M.block }, "end",
builder = if_builder },
default = assign_or_call_stat_parser }
M.assignments = {
["="] = "Set"
}
function M.assignments:add(k, v) self[k] = v end
return M
end

View File

@@ -0,0 +1,77 @@
--------------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Exported API:
-- * [M.table_bracket_field()]
-- * [M.table_field()]
-- * [M.table_content()]
-- * [M.table()]
--
-- KNOWN BUG: doesn't handle final ";" or "," before final "}"
--
--------------------------------------------------------------------------------
local gg = require 'metalua.grammar.generator'
return function(M)
M.table = { }
local _table = gg.future(M.table)
local _expr = gg.future(M).expr
--------------------------------------------------------------------------------
-- `[key] = value` table field definition
--------------------------------------------------------------------------------
M.table.bracket_pair = gg.sequence{ "[", _expr, "]", "=", _expr, builder = "Pair" }
--------------------------------------------------------------------------------
-- table element parser: list value, `id = value` pair or `[value] = value` pair.
--------------------------------------------------------------------------------
function M.table.element (lx)
if lx :is_keyword (lx :peek(), "[") then return M.table.bracket_pair(lx) end
local e = M.expr (lx)
if not lx :is_keyword (lx :peek(), "=") then return e end
lx :next(); -- skip the "="
local key = M.id2string(e) -- will fail on non-identifiers
local val = M.expr(lx)
local r = { tag="Pair", key, val }
r.lineinfo = { first = key.lineinfo.first, last = val.lineinfo.last }
return r
end
-----------------------------------------------------------------------------
-- table constructor, without enclosing braces; returns a full table object
-----------------------------------------------------------------------------
M.table.content = gg.list {
-- eta expansion to allow patching the element definition
primary = _table.element,
separators = { ",", ";" },
terminators = "}",
builder = "Table" }
--------------------------------------------------------------------------------
-- complete table constructor including [{...}]
--------------------------------------------------------------------------------
-- TODO beware, stat and expr use only table.content, this can't be patched.
M.table.table = gg.sequence{ "{", _table.content, "}", builder = unpack }
return M
end

View File

@@ -1,18 +1,29 @@
----------------------------------------------------------------------
-- Metalua.
--------------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Summary: parser generator. Collection of higher order functors,
-- which allow to build and combine parsers. Relies on a lexer
-- that supports the same API as the one exposed in mll.lua.
--
----------------------------------------------------------------------
--
-- Copyright (c) 2006-2008, Fabien Fleutot <metalua@gmail.com>.
--
-- This software is released under the MIT Licence, see licence.txt
-- for details.
--
----------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
@@ -26,48 +37,46 @@
-- * [gg.onkeyword()]
-- * [gg.optkeyword()]
--
-- Other functions:
-- Other functions:
-- * [gg.parse_error()]
-- * [gg.make_parser()]
-- * [gg.is_parser()]
--
--------------------------------------------------------------------------------
module("gg", package.seeall)
local M = { }
local lexer = require 'metalua.grammar.lexer'
--------------------------------------------------------------------------------
-- Symbol generator: [gensym()] returns a guaranteed-to-be-unique identifier.
-- The main purpose is to avoid variable capture in macros.
--
-- If a string is passed as an argument, theis string will be part of the
-- id name (helpful for macro debugging)
--------------------------------------------------------------------------------
local gensymidx = 0
function M.gensym (arg)
gensymidx = gensymidx + 1
return { tag="Id", string.format(".%i.%s", gensymidx, arg or "")}
end
-------------------------------------------------------------------------------
-- parser metatable, which maps __call to method parse, and adds some
-- error tracing boilerplate.
-------------------------------------------------------------------------------
local parser_metatable = { }
function parser_metatable.__call (parser, lx, ...)
--printf ("Call parser %q of type %q", parser.name or "?", parser.kind)
if mlc.metabugs then
return parser:parse (lx, ...)
--local x = parser:parse (lx, ...)
--printf ("Result of parser %q: %s",
-- parser.name or "?",
-- _G.table.tostring(x, "nohash", 80))
--return x
else
local li = lx:lineinfo_right() or { "?", "?", "?", "?" }
local status, ast = pcall (parser.parse, parser, lx, ...)
if status then return ast else
-- Try to replace the gg.lua location, in the error msg, with
-- the place where the current parser started handling the
-- lexstream.
-- Since the error is rethrown, these places are stacked.
error (string.format ("%s\n - (l.%s, c.%s, k.%s) in parser %s",
ast:match "gg.lua:%d+: (.*)" or ast,
li[1], li[2], li[3], parser.name or parser.kind))
end
end
function parser_metatable :__call (lx, ...)
return self :parse (lx, ...)
end
-------------------------------------------------------------------------------
-- Turn a table into a parser, mainly by setting the metatable.
-------------------------------------------------------------------------------
function make_parser(kind, p)
function M.make_parser(kind, p)
p.kind = kind
if not p.transformers then p.transformers = { } end
function p.transformers:add (x)
@@ -81,29 +90,32 @@ end
-- Return true iff [x] is a parser.
-- If it's a gg-generated parser, return the name of its kind.
-------------------------------------------------------------------------------
function is_parser (x)
function M.is_parser (x)
return type(x)=="function" or getmetatable(x)==parser_metatable and x.kind
end
-------------------------------------------------------------------------------
-- Parse a sequence, without applying builder nor transformers
-- Parse a sequence, without applying builder nor transformers.
-------------------------------------------------------------------------------
local function raw_parse_sequence (lx, p)
local r = { }
for i=1, #p do
e=p[i]
if type(e) == "string" then
if not lx:is_keyword (lx:next(), e) then
parse_error (lx, "A keyword was expected, probably `%s'.", e) end
elseif is_parser (e) then
table.insert (r, e (lx))
else
gg.parse_error (lx,"Sequence `%s': element #%i is neither a string "..
"nor a parser: %s",
p.name, i, table.tostring(e))
end
end
return r
local r = { }
for i=1, #p do
local e=p[i]
if type(e) == "string" then
local kw = lx :next()
if not lx :is_keyword (kw, e) then
M.parse_error(
lx, "A keyword was expected, probably `%s'.", e)
end
elseif M.is_parser (e) then
table.insert (r, e(lx))
else -- Invalid parser definition, this is *not* a parsing error
error(string.format(
"Sequence `%s': element #%i is neither a string nor a parser: %s",
p.name, i, table.tostring(e)))
end
end
return r
end
-------------------------------------------------------------------------------
@@ -124,10 +136,10 @@ local function transform (ast, parser, fli, lli)
if parser.transformers then
for _, t in ipairs (parser.transformers) do ast = t(ast) or ast end
end
if type(ast) == 'table'then
if type(ast) == 'table' then
local ali = ast.lineinfo
if not ali or ali.first~=fli or ali.last~=lli then
ast.lineinfo = { first = fli, last = lli }
ast.lineinfo = lexer.new_lineinfo(fli, lli)
end
end
return ast
@@ -136,21 +148,32 @@ end
-------------------------------------------------------------------------------
-- Generate a tracable parsing error (not implemented yet)
-------------------------------------------------------------------------------
function parse_error(lx, fmt, ...)
local li = lx:lineinfo_left() or {-1,-1,-1, "<unknown file>"}
local msg = string.format("line %i, char %i: "..fmt, li[1], li[2], ...)
function M.parse_error(lx, fmt, ...)
local li = lx:lineinfo_left()
local file, line, column, offset, positions
if li then
file, line, column, offset = li.source, li.line, li.column, li.offset
positions = { first = li, last = li }
else
line, column, offset = -1, -1, -1
end
local msg = string.format("line %i, char %i: "..fmt, line, column, ...)
if file and file~='?' then msg = "file "..file..", "..msg end
local src = lx.src
if li[3]>0 and src then
local i, j = li[3], li[3]
if offset>0 and src then
local i, j = offset, offset
while src:sub(i,i) ~= '\n' and i>=0 do i=i-1 end
while src:sub(j,j) ~= '\n' and j<=#src do j=j+1 end
while src:sub(j,j) ~= '\n' and j<=#src do j=j+1 end
local srcline = src:sub (i+1, j-1)
local idx = string.rep (" ", li[2]).."^"
local idx = string.rep (" ", column).."^"
msg = string.format("%s\n>>> %s\n>>> %s", msg, srcline, idx)
end
--lx :kill()
error(msg)
end
-------------------------------------------------------------------------------
--
-- Sequence parser generator
@@ -169,7 +192,7 @@ end
-- * [transformers]: a list of AST->AST functions, applied in order on ASTs
-- returned by the parser.
--
-- * Table-part entries corresponds to keywords (strings) and subparsers
-- * Table-part entries corresponds to keywords (strings) and subparsers
-- (function and callable objects).
--
-- After creation, the following fields are added:
@@ -178,13 +201,14 @@ end
-- * [name] is set, if it wasn't in the input.
--
-------------------------------------------------------------------------------
function sequence (p)
make_parser ("sequence", p)
function M.sequence (p)
M.make_parser ("sequence", p)
-------------------------------------------------------------------
-- Parsing method
-------------------------------------------------------------------
function p:parse (lx)
-- Raw parsing:
local fli = lx:lineinfo_right()
local seq = raw_parse_sequence (lx, self)
@@ -213,7 +237,7 @@ function sequence (p)
p.name = p[1] .. " ... " .. p[#p]
else p.name = p[1] .. " ..." end
else -- can't find a decent name
p.name = "<anonymous>"
p.name = "unnamed_sequence"
end
return p
@@ -258,52 +282,50 @@ end --</sequence>
-- * [kind] == "multisequence"
--
-------------------------------------------------------------------------------
function multisequence (p)
make_parser ("multisequence", p)
function M.multisequence (p)
M.make_parser ("multisequence", p)
-------------------------------------------------------------------
-- Add a sequence (might be just a config table for [gg.sequence])
-------------------------------------------------------------------
function p:add (s)
function p :add (s)
-- compile if necessary:
local keyword = type(s)=='table' and s[1]
if type(s)=='table' and not is_parser(s) then sequence(s) end
if is_parser(s)~='sequence' or type(keyword)~='string' then
if type(s)=='table' and not M.is_parser(s) then M.sequence(s) end
if M.is_parser(s)~='sequence' or type(keyword)~='string' then
if self.default then -- two defaults
error ("In a multisequence parser, all but one sequences "..
"must start with a keyword")
else self.default = s end -- first default
elseif self.sequences[keyword] then -- duplicate keyword
eprintf (" *** Warning: keyword %q overloaded in multisequence ***",
keyword)
else
if self.sequences[keyword] then -- duplicate keyword
-- TODO: warn that initial keyword `keyword` is overloaded in multiseq
end
self.sequences[keyword] = s
else -- newly caught keyword
self.sequences[keyword] = s
end
end
end -- </multisequence.add>
-------------------------------------------------------------------
-- Get the sequence starting with this keyword. [kw :: string]
-------------------------------------------------------------------
function p:get (kw) return self.sequences [kw] end
function p :get (kw) return self.sequences [kw] end
-------------------------------------------------------------------
-- Remove the sequence starting with keyword [kw :: string]
-------------------------------------------------------------------
function p:del (kw)
if not self.sequences[kw] then
eprintf("*** Warning: trying to delete sequence starting "..
"with %q from a multisequence having no such "..
"entry ***", kw) end
function p :del (kw)
if not self.sequences[kw] then
-- TODO: warn that we try to delete a non-existent entry
end
local removed = self.sequences[kw]
self.sequences[kw] = nil
self.sequences[kw] = nil
return removed
end
-------------------------------------------------------------------
-- Parsing method
-------------------------------------------------------------------
function p:parse (lx)
function p :parse (lx)
local fli = lx:lineinfo_right()
local x = raw_parse_multisequence (lx, self.sequences, self.default)
local lli = lx:lineinfo_left()
@@ -317,7 +339,7 @@ function multisequence (p)
-- from the array part of the parser to the hash part of field
-- [sequences]
p.sequences = { }
for i=1, #p do p:add (p[i]); p[i] = nil end
for i=1, #p do p :add (p[i]); p[i] = nil end
-- FIXME: why is this commented out?
--if p.default and not is_parser(p.default) then sequence(p.default) end
@@ -342,9 +364,9 @@ end --</multisequence>
-- * the builder takes specific parameters:
-- - for [prefix], it takes the result of the prefix sequence parser,
-- and the prefixed expression
-- - for [infix], it takes the left-hand-side expression, the results
-- - for [infix], it takes the left-hand-side expression, the results
-- of the infix sequence parser, and the right-hand-side expression.
-- - for [suffix], it takes the suffixed expression, and theresult
-- - for [suffix], it takes the suffixed expression, and the result
-- of the suffix sequence parser.
--
-- * the default field is a list, with parameters:
@@ -357,7 +379,7 @@ end --</multisequence>
-- In [p], useful fields are:
-- * [transformers]: as usual
-- * [name]: as usual
-- * [primary]: the atomic expression parser, or a multisequence config
-- * [primary]: the atomic expression parser, or a multisequence config
-- table (mandatory)
-- * [prefix]: prefix operators config table, see above.
-- * [infix]: infix operators config table, see above.
@@ -366,12 +388,12 @@ end --</multisequence>
-- After creation, these fields are added:
-- * [kind] == "expr"
-- * [parse] as usual
-- * each table is turned into a multisequence, and therefore has an
-- * each table is turned into a multisequence, and therefore has an
-- [add] method
--
-------------------------------------------------------------------------------
function expr (p)
make_parser ("expr", p)
function M.expr (p)
M.make_parser ("expr", p)
-------------------------------------------------------------------
-- parser method.
@@ -379,7 +401,7 @@ function expr (p)
-- it won't read expressions whose precedence is lower or equal
-- to [prec].
-------------------------------------------------------------------
function p:parse (lx, prec)
function p :parse (lx, prec)
prec = prec or 0
------------------------------------------------------
@@ -388,7 +410,7 @@ function expr (p)
-- Options include prec, assoc, transformers.
------------------------------------------------------
local function get_parser_info (tab)
local p2 = tab:get (lx:is_keyword (lx:peek()))
local p2 = tab :get (lx :is_keyword (lx :peek()))
if p2 then -- keyword-based sequence found
local function parser(lx) return raw_parse_sequence(lx, p2) end
return parser, p2
@@ -406,17 +428,17 @@ function expr (p)
-- expr, and one for the one with the prefix op.
------------------------------------------------------
local function handle_prefix ()
local fli = lx:lineinfo_right()
local fli = lx :lineinfo_right()
local p2_func, p2 = get_parser_info (self.prefix)
local op = p2_func and p2_func (lx)
if op then -- Keyword-based sequence found
local ili = lx:lineinfo_right() -- Intermediate LineInfo
local e = p2.builder (op, self:parse (lx, p2.prec))
local lli = lx:lineinfo_left()
local ili = lx :lineinfo_right() -- Intermediate LineInfo
local e = p2.builder (op, self :parse (lx, p2.prec))
local lli = lx :lineinfo_left()
return transform (transform (e, p2, ili, lli), self, fli, lli)
else -- No prefix found, get a primary expression
else -- No prefix found, get a primary expression
local e = self.primary(lx)
local lli = lx:lineinfo_left()
local lli = lx :lineinfo_left()
return transform (e, self, fli, lli)
end
end --</expr.parse.handle_prefix>
@@ -432,7 +454,7 @@ function expr (p)
-----------------------------------------
-- Handle flattening operators: gather all operands
-- of the series in [list]; when a different operator
-- of the series in [list]; when a different operator
-- is found, stop, build from [list], [transform] and
-- return.
-----------------------------------------
@@ -449,13 +471,13 @@ function expr (p)
local e2 = pflat.builder (list)
local lli = lx:lineinfo_left()
return transform (transform (e2, pflat, fli, lli), self, fli, lli)
-----------------------------------------
-- Handle regular infix operators: [e] the LHS is known,
-- just gather the operator and [e2] the RHS.
-- Result goes in [e3].
-----------------------------------------
elseif p2.prec and p2.prec>prec or
elseif p2.prec and p2.prec>prec or
p2.prec==prec and p2.assoc=="right" then
local fli = e.lineinfo.first -- lx:lineinfo_right()
local op = p2_func(lx)
@@ -466,10 +488,10 @@ function expr (p)
return transform (transform (e3, p2, fli, lli), self, fli, lli)
-----------------------------------------
-- Check for non-associative operators, and complain if applicable.
-- Check for non-associative operators, and complain if applicable.
-----------------------------------------
elseif p2.assoc=="none" and p2.prec==prec then
parse_error (lx, "non-associative operator!")
M.parse_error (lx, "non-associative operator!")
-----------------------------------------
-- No infix operator suitable at that precedence
@@ -501,7 +523,7 @@ function expr (p)
end --</expr.parse.handle_suffix>
------------------------------------------------------
-- Parser body: read suffix and (infix+operand)
-- Parser body: read suffix and (infix+operand)
-- extensions as long as we're able to fetch more at
-- this precedence level.
------------------------------------------------------
@@ -521,7 +543,7 @@ function expr (p)
if not p.primary then p.primary=p[1]; p[1]=nil end
for _, t in ipairs{ "primary", "prefix", "infix", "suffix" } do
if not p[t] then p[t] = { } end
if not is_parser(p[t]) then multisequence(p[t]) end
if not M.is_parser(p[t]) then M.multisequence(p[t]) end
end
function p:add(...) return self.primary:add(...) end
return p
@@ -558,40 +580,43 @@ end --</expr>
-- * [kind] == "list"
--
-------------------------------------------------------------------------------
function list (p)
make_parser ("list", p)
function M.list (p)
M.make_parser ("list", p)
-------------------------------------------------------------------
-- Parsing method
-------------------------------------------------------------------
function p:parse (lx)
function p :parse (lx)
------------------------------------------------------
-- Used to quickly check whether there's a terminator
-- Used to quickly check whether there's a terminator
-- or a separator immediately ahead
------------------------------------------------------
local function peek_is_in (keywords)
local function peek_is_in (keywords)
return keywords and lx:is_keyword(lx:peek(), unpack(keywords)) end
local x = { }
local fli = lx:lineinfo_right()
local fli = lx :lineinfo_right()
-- if there's a terminator to start with, don't bother trying
if not peek_is_in (self.terminators) then
repeat table.insert (x, self.primary (lx)) -- read one element
local is_empty_list = self.terminators and (peek_is_in (self.terminators) or lx:peek().tag=="Eof")
if not is_empty_list then
repeat
local item = self.primary(lx)
table.insert (x, item) -- read one element
until
-- First reason to stop: There's a separator list specified,
-- and next token isn't one. Otherwise, consume it with [lx:next()]
-- There's a separator list specified, and next token isn't in it.
-- Otherwise, consume it with [lx:next()]
self.separators and not(peek_is_in (self.separators) and lx:next()) or
-- Other reason to stop: terminator token ahead
-- Terminator token ahead
peek_is_in (self.terminators) or
-- Last reason: end of file reached
lx:peek().tag=="Eof"
end
local lli = lx:lineinfo_left()
-- Apply the builder. It can be a string, or a callable value,
-- Apply the builder. It can be a string, or a callable value,
-- or simply nothing.
local b = self.builder
if b then
@@ -620,10 +645,10 @@ end --</list>
-------------------------------------------------------------------------------
--
-- Keyword-conditionned parser generator
-- Keyword-conditioned parser generator
--
-------------------------------------------------------------------------------
--
--
-- Only apply a parser if a given keyword is found. The result of
-- [gg.onkeyword] parser is the result of the subparser (modulo
-- [transformers] applications).
@@ -639,10 +664,10 @@ end --</list>
--
-- * [transformers]: as usual
--
-- * [peek]: if non-nil, the conditionning keyword is left in the lexeme
-- * [peek]: if non-nil, the conditioning keyword is left in the lexeme
-- stream instead of being consumed.
--
-- * [primary]: the subparser.
-- * [primary]: the subparser.
--
-- * [keywords]: list of strings representing triggering keywords.
--
@@ -650,26 +675,27 @@ end --</list>
-- Strings are put in [keywords], and the parser is put in [primary].
--
-- After the call, the following fields will be set:
--
--
-- * [parse] the parsing method
-- * [kind] == "onkeyword"
-- * [primary]
-- * [keywords]
--
-------------------------------------------------------------------------------
function onkeyword (p)
make_parser ("onkeyword", p)
function M.onkeyword (p)
M.make_parser ("onkeyword", p)
-------------------------------------------------------------------
-- Parsing method
-------------------------------------------------------------------
function p:parse(lx)
if lx:is_keyword (lx:peek(), unpack(self.keywords)) then
--local fli = lx:lineinfo_right()
function p :parse (lx)
if lx :is_keyword (lx:peek(), unpack(self.keywords)) then
local fli = lx:lineinfo_right()
if not self.peek then lx:next() end
local content = self.primary (lx)
--local lli = lx:lineinfo_left()
local fli, lli = content.lineinfo.first, content.lineinfo.last
local lli = lx:lineinfo_left()
local li = content.lineinfo or { }
fli, lli = li.first or fli, li.last or lli
return transform (content, p, fli, lli)
else return false end
end
@@ -680,10 +706,9 @@ function onkeyword (p)
if not p.keywords then p.keywords = { } end
for _, x in ipairs(p) do
if type(x)=="string" then table.insert (p.keywords, x)
else assert (not p.primary and is_parser (x)); p.primary = x end
else assert (not p.primary and M.is_parser (x)); p.primary = x end
end
if not next (p.keywords) then
eprintf("Warning, no keyword to trigger gg.onkeyword") end
assert (next (p.keywords), "Missing trigger keyword in gg.onkeyword")
assert (p.primary, 'no primary parser in gg.onkeyword')
return p
end --</onkeyword>
@@ -696,15 +721,15 @@ end --</onkeyword>
-------------------------------------------------------------------------------
--
-- This doesn't return a real parser, just a function. That function parses
-- one of the keywords passed as parameters, and returns it. It returns
-- one of the keywords passed as parameters, and returns it. It returns
-- [false] if no matching keyword is found.
--
-- Notice that tokens returned by lexer already carry lineinfo, therefore
-- there's no need to add them, as done usually through transform() calls.
-------------------------------------------------------------------------------
function optkeyword (...)
function M.optkeyword (...)
local args = {...}
if type (args[1]) == "table" then
if type (args[1]) == "table" then
assert (#args == 1)
args = args[1]
end
@@ -729,15 +754,15 @@ end
-- The resulting parser returns whatever the argument parser does.
--
-------------------------------------------------------------------------------
function with_lexer(new_lexer, parser)
function M.with_lexer(new_lexer, parser)
-------------------------------------------------------------------
-- Most gg functions take their parameters in a table, so it's
-- Most gg functions take their parameters in a table, so it's
-- better to silently accept when with_lexer{ } is called with
-- its arguments in a list:
-------------------------------------------------------------------
if not parser and #new_lexer==2 and type(new_lexer[1])=='table' then
return with_lexer(unpack(new_lexer))
return M.with_lexer(unpack(new_lexer))
end
-------------------------------------------------------------------
@@ -754,3 +779,54 @@ function with_lexer(new_lexer, parser)
if status then return result else error(result) end
end
end
--------------------------------------------------------------------------------
--
-- Make sure a parser is used and returns successfully.
--
--------------------------------------------------------------------------------
function M.nonempty(primary)
local p = M.make_parser('non-empty list', { primary = primary, name=primary.name })
function p :parse (lx)
local fli = lx:lineinfo_right()
local content = self.primary (lx)
local lli = lx:lineinfo_left()
local li = content.lineinfo or { }
fli, lli = li.first or fli, li.last or lli
if #content == 0 then
M.parse_error (lx, "`%s' must not be empty.", self.name or "list")
else
return transform (content, self, fli, lli)
end
end
return p
end
local FUTURE_MT = { }
function FUTURE_MT:__tostring() return "<Proxy parser module>" end
function FUTURE_MT:__newindex(key, value) error "don't write in futures" end
function FUTURE_MT :__index (parser_name)
return function(...)
local p, m = rawget(self, '__path'), self.__module
if p then for _, name in ipairs(p) do
m=rawget(m, name)
if not m then error ("Submodule '"..name.."' undefined") end
end end
local f = rawget(m, parser_name)
if not f then error ("Parser '"..parser_name.."' undefined") end
return f(...)
end
end
function M.future(module, ...)
checks('table')
local path = ... and {...}
if path then for _, x in ipairs(path) do
assert(type(x)=='string', "Bad future arg")
end end
local self = { __module = module,
__path = path }
return setmetatable(self, FUTURE_MT)
end
return M

View File

@@ -0,0 +1,672 @@
-------------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
-------------------------------------------------------------------------------
require 'checks'
local M = { }
local lexer = { alpha={ }, sym={ } }
lexer.__index=lexer
lexer.__type='lexer.stream'
M.lexer = lexer
local debugf = function() end
-- local debugf=printf
----------------------------------------------------------------------
-- Some locale settings produce bad results, e.g. French locale
-- expect float numbers to use commas instead of periods.
-- TODO: change number parser into something loclae-independent,
-- locales are nasty.
----------------------------------------------------------------------
os.setlocale('C')
local MT = { }
M.metatables=MT
----------------------------------------------------------------------
-- Create a new metatable, for a new class of objects.
----------------------------------------------------------------------
local function new_metatable(name)
local mt = { __type = 'lexer.'..name };
mt.__index = mt
MT[name] = mt
end
----------------------------------------------------------------------
-- Position: represent a point in a source file.
----------------------------------------------------------------------
new_metatable 'position'
local position_idx=1
function M.new_position(line, column, offset, source)
checks('number', 'number', 'number', 'string')
local id = position_idx; position_idx = position_idx+1
return setmetatable({line=line, column=column, offset=offset,
source=source, id=id}, MT.position)
end
function MT.position :__tostring()
return string.format("<%s%s|L%d|C%d|K%d>",
self.comments and "C|" or "",
self.source, self.line, self.column, self.offset)
end
----------------------------------------------------------------------
-- Position factory: convert offsets into line/column/offset positions.
----------------------------------------------------------------------
new_metatable 'position_factory'
function M.new_position_factory(src, src_name)
-- assert(type(src)=='string')
-- assert(type(src_name)=='string')
local lines = { 1 }
for offset in src :gmatch '\n()' do table.insert(lines, offset) end
local max = #src+1
table.insert(lines, max+1) -- +1 includes Eof
return setmetatable({ src_name=src_name, line2offset=lines, max=max },
MT.position_factory)
end
function MT.position_factory :get_position (offset)
-- assert(type(offset)=='number')
assert(offset<=self.max)
local line2offset = self.line2offset
local left = self.last_left or 1
if offset<line2offset[left] then left=1 end
local right = left+1
if line2offset[right]<=offset then right = right+1 end
if line2offset[right]<=offset then right = #line2offset end
while true do
-- print (" trying lines "..left.."/"..right..", offsets "..line2offset[left]..
-- "/"..line2offset[right].." for offset "..offset)
-- assert(line2offset[left]<=offset)
-- assert(offset<line2offset[right])
-- assert(left<right)
if left+1==right then break end
local middle = math.floor((left+right)/2)
if line2offset[middle]<=offset then left=middle else right=middle end
end
-- assert(left+1==right)
-- printf("found that offset %d is between %d and %d, hence on line %d",
-- offset, line2offset[left], line2offset[right], left)
local line = left
local column = offset - line2offset[line] + 1
self.last_left = left
return M.new_position(line, column, offset, self.src_name)
end
----------------------------------------------------------------------
-- Lineinfo: represent a node's range in a source file;
-- embed information about prefix and suffix comments.
----------------------------------------------------------------------
new_metatable 'lineinfo'
function M.new_lineinfo(first, last)
checks('lexer.position', 'lexer.position')
return setmetatable({first=first, last=last}, MT.lineinfo)
end
function MT.lineinfo :__tostring()
local fli, lli = self.first, self.last
local line = fli.line; if line~=lli.line then line =line ..'-'..lli.line end
local column = fli.column; if column~=lli.column then column=column..'-'..lli.column end
local offset = fli.offset; if offset~=lli.offset then offset=offset..'-'..lli.offset end
return string.format("<%s%s|L%s|C%s|K%s%s>",
fli.comments and "C|" or "",
fli.source, line, column, offset,
lli.comments and "|C" or "")
end
----------------------------------------------------------------------
-- Token: atomic Lua language element, with a category, a content,
-- and some lineinfo relating it to its original source.
----------------------------------------------------------------------
new_metatable 'token'
function M.new_token(tag, content, lineinfo)
--printf("TOKEN `%s{ %q, lineinfo = %s} boundaries %d, %d",
-- tag, content, tostring(lineinfo), lineinfo.first.id, lineinfo.last.id)
return setmetatable({tag=tag, lineinfo=lineinfo, content}, MT.token)
end
function MT.token :__tostring()
--return string.format("`%s{ %q, %s }", self.tag, self[1], tostring(self.lineinfo))
return string.format("`%s %q", self.tag, self[1])
end
----------------------------------------------------------------------
-- Comment: series of comment blocks with associated lineinfo.
-- To be attached to the tokens just before and just after them.
----------------------------------------------------------------------
new_metatable 'comment'
function M.new_comment(lines)
local first = lines[1].lineinfo.first
local last = lines[#lines].lineinfo.last
local lineinfo = M.new_lineinfo(first, last)
return setmetatable({lineinfo=lineinfo, unpack(lines)}, MT.comment)
end
function MT.comment :text()
local last_line = self[1].lineinfo.last.line
local acc = { }
for i, line in ipairs(self) do
local nreturns = line.lineinfo.first.line - last_line
table.insert(acc, ("\n"):rep(nreturns))
table.insert(acc, line[1])
end
return table.concat(acc)
end
function M.new_comment_line(text, lineinfo, nequals)
checks('string', 'lexer.lineinfo', '?number')
return { lineinfo = lineinfo, text, nequals }
end
----------------------------------------------------------------------
-- Patterns used by [lexer :extract] to decompose the raw string into
-- correctly tagged tokens.
----------------------------------------------------------------------
lexer.patterns = {
spaces = "^[ \r\n\t]*()",
short_comment = "^%-%-([^\n]*)\n?()",
--final_short_comment = "^%-%-([^\n]*)()$",
long_comment = "^%-%-%[(=*)%[\n?(.-)%]%1%]()",
long_string = "^%[(=*)%[\n?(.-)%]%1%]()",
number_mantissa = { "^%d+%.?%d*()", "^%d*%.%d+()" },
number_mantissa_hex = { "^%x+%.?%x*()", "^%x*%.%x+()" }, --Lua5.1 and Lua5.2
number_exponant = "^[eE][%+%-]?%d+()",
number_exponant_hex = "^[pP][%+%-]?%d+()", --Lua5.2
number_hex = "^0[xX]()",
word = "^([%a_][%w_]*)()"
}
----------------------------------------------------------------------
-- unescape a whole string, applying [unesc_digits] and
-- [unesc_letter] as many times as required.
----------------------------------------------------------------------
local function unescape_string (s)
-- Turn the digits of an escape sequence into the corresponding
-- character, e.g. [unesc_digits("123") == string.char(123)].
local function unesc_digits (backslashes, digits)
if #backslashes%2==0 then
-- Even number of backslashes, they escape each other, not the digits.
-- Return them so that unesc_letter() can treat them
return backslashes..digits
else
-- Remove the odd backslash, which escapes the number sequence.
-- The rest will be returned and parsed by unesc_letter()
backslashes = backslashes :sub (1,-2)
end
local k, j, i = digits :reverse() :byte(1, 3)
local z = string.byte "0"
local code = (k or z) + 10*(j or z) + 100*(i or z) - 111*z
if code > 255 then
error ("Illegal escape sequence '\\"..digits..
"' in string: ASCII codes must be in [0..255]")
end
local c = string.char (code)
if c == '\\' then c = '\\\\' end -- parsed by unesc_letter (test: "\092b" --> "\\b")
return backslashes..c
end
-- Turn hex digits of escape sequence into char.
local function unesc_hex(backslashes, digits)
if #backslashes%2==0 then
return backslashes..'x'..digits
else
backslashes = backslashes :sub (1,-2)
end
local c = string.char(tonumber(digits,16))
if c == '\\' then c = '\\\\' end -- parsed by unesc_letter (test: "\x5cb" --> "\\b")
return backslashes..c
end
-- Handle Lua 5.2 \z sequences
local function unesc_z(backslashes, more)
if #backslashes%2==0 then
return backslashes..more
else
return backslashes :sub (1,-2)
end
end
-- Take a letter [x], and returns the character represented by the
-- sequence ['\\'..x], e.g. [unesc_letter "n" == "\n"].
local function unesc_letter(x)
local t = {
a = "\a", b = "\b", f = "\f",
n = "\n", r = "\r", t = "\t", v = "\v",
["\\"] = "\\", ["'"] = "'", ['"'] = '"', ["\n"] = "\n" }
return t[x] or x
end
s = s: gsub ("(\\+)(z%s*)", unesc_z) -- Lua 5.2
s = s: gsub ("(\\+)([0-9][0-9]?[0-9]?)", unesc_digits)
s = s: gsub ("(\\+)x([0-9a-fA-F][0-9a-fA-F])", unesc_hex) -- Lua 5.2
s = s: gsub ("\\(%D)",unesc_letter)
return s
end
lexer.extractors = {
"extract_long_comment", "extract_short_comment",
"extract_short_string", "extract_word", "extract_number",
"extract_long_string", "extract_symbol" }
----------------------------------------------------------------------
-- Really extract next token from the raw string
-- (and update the index).
-- loc: offset of the position just after spaces and comments
-- previous_i: offset in src before extraction began
----------------------------------------------------------------------
function lexer :extract ()
local attached_comments = { }
local function gen_token(...)
local token = M.new_token(...)
if #attached_comments>0 then -- attach previous comments to token
local comments = M.new_comment(attached_comments)
token.lineinfo.first.comments = comments
if self.lineinfo_last_extracted then
self.lineinfo_last_extracted.comments = comments
end
attached_comments = { }
end
token.lineinfo.first.facing = self.lineinfo_last_extracted
self.lineinfo_last_extracted.facing = assert(token.lineinfo.first)
self.lineinfo_last_extracted = assert(token.lineinfo.last)
return token
end
while true do -- loop until a non-comment token is found
-- skip whitespaces
self.i = self.src:match (self.patterns.spaces, self.i)
if self.i>#self.src then
local fli = self.posfact :get_position (#self.src+1)
local lli = self.posfact :get_position (#self.src+1) -- ok?
local tok = gen_token("Eof", "eof", M.new_lineinfo(fli, lli))
tok.lineinfo.last.facing = lli
return tok
end
local i_first = self.i -- loc = position after whitespaces
-- try every extractor until a token is found
for _, extractor in ipairs(self.extractors) do
local tag, content, xtra = self [extractor] (self)
if tag then
local fli = self.posfact :get_position (i_first)
local lli = self.posfact :get_position (self.i-1)
local lineinfo = M.new_lineinfo(fli, lli)
if tag=='Comment' then
local prev_comment = attached_comments[#attached_comments]
if not xtra -- new comment is short
and prev_comment and not prev_comment[2] -- prev comment is short
and prev_comment.lineinfo.last.line+1==fli.line then -- adjascent lines
-- concat with previous comment
prev_comment[1] = prev_comment[1].."\n"..content -- TODO quadratic, BAD!
prev_comment.lineinfo.last = lli
else -- accumulate comment
local comment = M.new_comment_line(content, lineinfo, xtra)
table.insert(attached_comments, comment)
end
break -- back to skipping spaces
else -- not a comment: real token, then
return gen_token(tag, content, lineinfo)
end -- if token is a comment
end -- if token found
end -- for each extractor
end -- while token is a comment
end -- :extract()
----------------------------------------------------------------------
-- Extract a short comment.
----------------------------------------------------------------------
function lexer :extract_short_comment()
-- TODO: handle final_short_comment
local content, j = self.src :match (self.patterns.short_comment, self.i)
if content then self.i=j; return 'Comment', content, nil end
end
----------------------------------------------------------------------
-- Extract a long comment.
----------------------------------------------------------------------
function lexer :extract_long_comment()
local equals, content, j = self.src:match (self.patterns.long_comment, self.i)
if j then self.i = j; return "Comment", content, #equals end
end
----------------------------------------------------------------------
-- Extract a '...' or "..." short string.
----------------------------------------------------------------------
function lexer :extract_short_string()
local k = self.src :sub (self.i,self.i) -- first char
if k~=[[']] and k~=[["]] then return end -- no match'
local i = self.i + 1
local j = i
while true do
local x,y; x, j, y = self.src :match ("([\\\r\n"..k.."])()(.?)", j) -- next interesting char
if x == '\\' then
if y == 'z' then -- Lua 5.2 \z
j = self.src :match ("^%s*()", j+1)
else
j=j+1 -- escaped char
end
elseif x == k then break -- end of string
else
assert (not x or x=='\r' or x=='\n')
return nil, 'Unterminated string'
end
end
self.i = j
return 'String', unescape_string (self.src :sub (i,j-2))
end
----------------------------------------------------------------------
-- Extract Id or Keyword.
----------------------------------------------------------------------
function lexer :extract_word()
local word, j = self.src:match (self.patterns.word, self.i)
if word then
self.i = j
return (self.alpha [word] and 'Keyword' or 'Id'), word
end
end
----------------------------------------------------------------------
-- Extract Number.
----------------------------------------------------------------------
function lexer :extract_number()
local j = self.src:match(self.patterns.number_hex, self.i)
if j then
j = self.src:match (self.patterns.number_mantissa_hex[1], j) or
self.src:match (self.patterns.number_mantissa_hex[2], j)
if j then
j = self.src:match (self.patterns.number_exponant_hex, j) or j
end
else
j = self.src:match (self.patterns.number_mantissa[1], self.i) or
self.src:match (self.patterns.number_mantissa[2], self.i)
if j then
j = self.src:match (self.patterns.number_exponant, j) or j
end
end
if not j then return end
-- Number found, interpret with tonumber() and return it
local str = self.src:sub (self.i, j-1)
-- :TODO: tonumber on Lua5.2 floating hex may or may not work on Lua5.1
local n = tonumber (str)
if not n then error(str.." is not a valid number according to tonumber()") end
self.i = j
return 'Number', n
end
----------------------------------------------------------------------
-- Extract long string.
----------------------------------------------------------------------
function lexer :extract_long_string()
local _, content, j = self.src :match (self.patterns.long_string, self.i)
if j then self.i = j; return 'String', content end
end
----------------------------------------------------------------------
-- Extract symbol.
----------------------------------------------------------------------
function lexer :extract_symbol()
local k = self.src:sub (self.i,self.i)
local symk = self.sym [k] -- symbols starting with `k`
if not symk then
self.i = self.i + 1
return 'Keyword', k
end
for _, sym in pairs (symk) do
if sym == self.src:sub (self.i, self.i + #sym - 1) then
self.i = self.i + #sym
return 'Keyword', sym
end
end
self.i = self.i+1
return 'Keyword', k
end
----------------------------------------------------------------------
-- Add a keyword to the list of keywords recognized by the lexer.
----------------------------------------------------------------------
function lexer :add (w, ...)
assert(not ..., "lexer :add() takes only one arg, although possibly a table")
if type (w) == "table" then
for _, x in ipairs (w) do self :add (x) end
else
if w:match (self.patterns.word .. "$") then self.alpha [w] = true
elseif w:match "^%p%p+$" then
local k = w:sub(1,1)
local list = self.sym [k]
if not list then list = { }; self.sym [k] = list end
table.insert (list, w)
elseif w:match "^%p$" then return
else error "Invalid keyword" end
end
end
----------------------------------------------------------------------
-- Return the [n]th next token, without consuming it.
-- [n] defaults to 1. If it goes pass the end of the stream, an EOF
-- token is returned.
----------------------------------------------------------------------
function lexer :peek (n)
if not n then n=1 end
if n > #self.peeked then
for i = #self.peeked+1, n do
self.peeked [i] = self :extract()
end
end
return self.peeked [n]
end
----------------------------------------------------------------------
-- Return the [n]th next token, removing it as well as the 0..n-1
-- previous tokens. [n] defaults to 1. If it goes pass the end of the
-- stream, an EOF token is returned.
----------------------------------------------------------------------
function lexer :next (n)
n = n or 1
self :peek (n)
local a
for i=1,n do
a = table.remove (self.peeked, 1)
-- TODO: is this used anywhere? I think not. a.lineinfo.last may be nil.
--self.lastline = a.lineinfo.last.line
end
self.lineinfo_last_consumed = a.lineinfo.last
return a
end
----------------------------------------------------------------------
-- Returns an object which saves the stream's current state.
----------------------------------------------------------------------
-- FIXME there are more fields than that to save
function lexer :save () return { self.i; {unpack(self.peeked) } } end
----------------------------------------------------------------------
-- Restore the stream's state, as saved by method [save].
----------------------------------------------------------------------
-- FIXME there are more fields than that to restore
function lexer :restore (s) self.i=s[1]; self.peeked=s[2] end
----------------------------------------------------------------------
-- Resynchronize: cancel any token in self.peeked, by emptying the
-- list and resetting the indexes
----------------------------------------------------------------------
function lexer :sync()
local p1 = self.peeked[1]
if p1 then
local li_first = p1.lineinfo.first
if li_first.comments then li_first=li_first.comments.lineinfo.first end
self.i = li_first.offset
self.column_offset = self.i - li_first.column
self.peeked = { }
self.attached_comments = p1.lineinfo.first.comments or { }
end
end
----------------------------------------------------------------------
-- Take the source and offset of an old lexer.
----------------------------------------------------------------------
function lexer :takeover(old)
self :sync(); old :sync()
for _, field in ipairs{ 'i', 'src', 'attached_comments', 'posfact' } do
self[field] = old[field]
end
return self
end
----------------------------------------------------------------------
-- Return the current position in the sources. This position is between
-- two tokens, and can be within a space / comment area, and therefore
-- have a non-null width. :lineinfo_left() returns the beginning of the
-- separation area, :lineinfo_right() returns the end of that area.
--
-- ____ last consummed token ____ first unconsummed token
-- / /
-- XXXXX <spaces and comments> YYYYY
-- \____ \____
-- :lineinfo_left() :lineinfo_right()
----------------------------------------------------------------------
function lexer :lineinfo_right()
return self :peek(1).lineinfo.first
end
function lexer :lineinfo_left()
return self.lineinfo_last_consumed
end
----------------------------------------------------------------------
-- Create a new lexstream.
----------------------------------------------------------------------
function lexer :newstream (src_or_stream, name)
name = name or "?"
if type(src_or_stream)=='table' then -- it's a stream
return setmetatable ({ }, self) :takeover (src_or_stream)
elseif type(src_or_stream)=='string' then -- it's a source string
local src = src_or_stream
local pos1 = M.new_position(1, 1, 1, name)
local stream = {
src_name = name; -- Name of the file
src = src; -- The source, as a single string
peeked = { }; -- Already peeked, but not discarded yet, tokens
i = 1; -- Character offset in src
attached_comments = { },-- comments accumulator
lineinfo_last_extracted = pos1,
lineinfo_last_consumed = pos1,
posfact = M.new_position_factory (src_or_stream, name)
}
setmetatable (stream, self)
-- Skip initial sharp-bang for Unix scripts
-- FIXME: redundant with mlp.chunk()
if src and src :match "^#!" then
local endofline = src :find "\n"
stream.i = endofline and (endofline + 1) or #src
end
return stream
else
assert(false, ":newstream() takes a source string or a stream, not a "..
type(src_or_stream))
end
end
----------------------------------------------------------------------
-- If there's no ... args, return the token a (whose truth value is
-- true) if it's a `Keyword{ }, or nil. If there are ... args, they
-- have to be strings. if the token a is a keyword, and it's content
-- is one of the ... args, then returns it (it's truth value is
-- true). If no a keyword or not in ..., return nil.
----------------------------------------------------------------------
function lexer :is_keyword (a, ...)
if not a or a.tag ~= "Keyword" then return false end
local words = {...}
if #words == 0 then return a[1] end
for _, w in ipairs (words) do
if w == a[1] then return w end
end
return false
end
----------------------------------------------------------------------
-- Cause an error if the next token isn't a keyword whose content
-- is listed among ... args (which have to be strings).
----------------------------------------------------------------------
function lexer :check (...)
local words = {...}
local a = self :next()
local function err ()
error ("Got " .. tostring (a) ..
", expected one of these keywords : '" ..
table.concat (words,"', '") .. "'") end
if not a or a.tag ~= "Keyword" then err () end
if #words == 0 then return a[1] end
for _, w in ipairs (words) do
if w == a[1] then return w end
end
err ()
end
----------------------------------------------------------------------
--
----------------------------------------------------------------------
function lexer :clone()
local alpha_clone, sym_clone = { }, { }
for word in pairs(self.alpha) do alpha_clone[word]=true end
for letter, list in pairs(self.sym) do sym_clone[letter] = { unpack(list) } end
local clone = { alpha=alpha_clone, sym=sym_clone }
setmetatable(clone, self)
clone.__index = clone
return clone
end
----------------------------------------------------------------------
-- Cancel everything left in a lexer, all subsequent attempts at
-- `:peek()` or `:next()` will return `Eof`.
----------------------------------------------------------------------
function lexer :kill()
self.i = #self.src+1
self.peeked = { }
self.attached_comments = { }
self.lineinfo_last = self.posfact :get_position (#self.src+1)
end
return M

File diff suppressed because it is too large Load Diff

View File

@@ -1,441 +0,0 @@
----------------------------------------------------------------------
--
-- WARNING! You're entering a hackish area, proceed at your own risks!
--
-- This code results from the borrowing, then ruthless abuse, of
-- Yueliang's implementation of Lua 5.0 compiler. I claim
-- responsibility for all of the ugly, dirty stuff that you might spot
-- in it.
--
-- Eventually, this code will be rewritten, either in Lua or more
-- probably in C. Meanwhile, if you're interested into digging
-- metalua's sources, this is not the best part to invest your time
-- on.
--
-- End of warning.
--
----------------------------------------------------------------------
--[[--------------------------------------------------------------------
$Id$
ldump.lua
Save bytecodes in Lua
This file is part of Yueliang.
Copyright (c) 2005 Kein-Hong Man <khman@users.sf.net>
The COPYRIGHT file describes the conditions
under which this software may be distributed.
------------------------------------------------------------------------
[FF] Slightly modified, mainly to produce Lua 5.1 bytecode.
----------------------------------------------------------------------]]
--[[--------------------------------------------------------------------
-- Notes:
-- * LUA_NUMBER (double), byte order (little endian) and some other
-- header values hard-coded; see other notes below...
-- * One significant difference is that instructions are still in table
-- form (with OP/A/B/C/Bx fields) and luaP:Instruction() is needed to
-- convert them into 4-char strings
-- * Deleted:
-- luaU:DumpVector: folded into DumpLines, DumpCode
-- * Added:
-- luaU:endianness() (from lundump.c)
-- luaU:make_setS: create a chunk writer that writes to a string
-- luaU:make_setF: create a chunk writer that writes to a file
-- (lua.h contains a typedef for a Chunkwriter pointer, and
-- a Lua-based implementation exists, writer() in lstrlib.c)
-- luaU:from_double(x): encode double value for writing
-- luaU:from_int(x): encode integer value for writing
-- (error checking is limited for these conversion functions)
-- (double conversion does not support denormals or NaNs)
-- luaU:ttype(o) (from lobject.h)
----------------------------------------------------------------------]]
module("bytecode", package.seeall)
format = { }
format.header = string.dump(function()end):sub(1, 12)
format.little_endian, format.int_size,
format.size_t_size, format.instr_size,
format.number_size, format.integral = format.header:byte(7, 12)
format.little_endian = format.little_endian~=0
format.integral = format.integral ~=0
assert(format.integral or format.number_size==8, "Number format not supported by dumper")
assert(format.little_endian, "Big endian architectures not supported by dumper")
--requires luaP
luaU = {}
-- constants used by dumper
luaU.LUA_TNIL = 0
luaU.LUA_TBOOLEAN = 1
luaU.LUA_TNUMBER = 3 -- (all in lua.h)
luaU.LUA_TSTRING = 4
luaU.LUA_TNONE = -1
-- definitions for headers of binary files
--luaU.LUA_SIGNATURE = "\27Lua" -- binary files start with "<esc>Lua"
--luaU.VERSION = 81 -- 0x50; last format change was in 5.0
--luaU.FORMAT_VERSION = 0 -- 0 is official version. yeah I know I'm a liar.
-- a multiple of PI for testing native format
-- multiplying by 1E7 gives non-trivial integer values
--luaU.TEST_NUMBER = 3.14159265358979323846E7
--[[--------------------------------------------------------------------
-- Additional functions to handle chunk writing
-- * to use make_setS and make_setF, see test_ldump.lua elsewhere
----------------------------------------------------------------------]]
------------------------------------------------------------------------
-- works like the lobject.h version except that TObject used in these
-- scripts only has a 'value' field, no 'tt' field (native types used)
------------------------------------------------------------------------
function luaU:ttype(o)
local tt = type(o.value)
if tt == "number" then return self.LUA_TNUMBER
elseif tt == "string" then return self.LUA_TSTRING
elseif tt == "nil" then return self.LUA_TNIL
elseif tt == "boolean" then return self.LUA_TBOOLEAN
else
return self.LUA_TNONE -- the rest should not appear
end
end
------------------------------------------------------------------------
-- create a chunk writer that writes to a string
-- * returns the writer function and a table containing the string
-- * to get the final result, look in buff.data
------------------------------------------------------------------------
function luaU:make_setS()
local buff = {}
buff.data = ""
local writer =
function(s, buff) -- chunk writer
if not s then return end
buff.data = buff.data..s
end
return writer, buff
end
------------------------------------------------------------------------
-- create a chunk writer that writes to a file
-- * returns the writer function and a table containing the file handle
-- * if a nil is passed, then writer should close the open file
------------------------------------------------------------------------
function luaU:make_setF(filename)
local buff = {}
buff.h = io.open(filename, "wb")
if not buff.h then return nil end
local writer =
function(s, buff) -- chunk writer
if not buff.h then return end
if not s then buff.h:close(); return end
buff.h:write(s)
end
return writer, buff
end
-----------------------------------------------------------------------
-- converts a IEEE754 double number to an 8-byte little-endian string
-- * luaU:from_double() and luaU:from_int() are from ChunkBake project
-- * supports +/- Infinity, but not denormals or NaNs
-----------------------------------------------------------------------
function luaU:from_double(x)
local function grab_byte(v)
return math.floor(v / 256),
string.char(math.mod(math.floor(v), 256))
end
local sign = 0
if x < 0 then sign = 1; x = -x end
local mantissa, exponent = math.frexp(x)
if x == 0 then -- zero
mantissa, exponent = 0, 0
elseif x == 1/0 then
mantissa, exponent = 0, 2047
else
mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53)
exponent = exponent + 1022
end
local v, byte = "" -- convert to bytes
x = mantissa
for i = 1,6 do
x, byte = grab_byte(x); v = v..byte -- 47:0
end
x, byte = grab_byte(exponent * 16 + x); v = v..byte -- 55:48
x, byte = grab_byte(sign * 128 + x); v = v..byte -- 63:56
return v
end
-----------------------------------------------------------------------
-- converts a number to a little-endian 32-bit integer string
-- * input value assumed to not overflow, can be signed/unsigned
-----------------------------------------------------------------------
function luaU:from_int(x, size)
local v = ""
x = math.floor(x)
if x >= 0 then
for i = 1, size do
v = v..string.char(math.mod(x, 256)); x = math.floor(x / 256)
end
else -- x < 0
x = -x
local carry = 1
for i = 1, size do
local c = 255 - math.mod(x, 256) + carry
if c == 256 then c = 0; carry = 1 else carry = 0 end
v = v..string.char(c); x = math.floor(x / 256)
end
end
return v
end
--[[--------------------------------------------------------------------
-- Functions to make a binary chunk
-- * many functions have the size parameter removed, since output is
-- in the form of a string and some sizes are implicit or hard-coded
-- * luaU:DumpVector has been deleted (used in DumpCode & DumpLines)
----------------------------------------------------------------------]]
------------------------------------------------------------------------
-- dump a block of literal bytes
------------------------------------------------------------------------
function luaU:DumpLiteral(s, D) self:DumpBlock(s, D) end
--[[--------------------------------------------------------------------
-- struct DumpState:
-- L -- lua_State (not used in this script)
-- write -- lua_Chunkwriter (chunk writer function)
-- data -- void* (chunk writer context or data already written)
----------------------------------------------------------------------]]
------------------------------------------------------------------------
-- dumps a block of bytes
-- * lua_unlock(D.L), lua_lock(D.L) deleted
------------------------------------------------------------------------
function luaU:DumpBlock(b, D) D.write(b, D.data) end
------------------------------------------------------------------------
-- dumps a single byte
------------------------------------------------------------------------
function luaU:DumpByte(y, D)
self:DumpBlock(string.char(y), D)
end
------------------------------------------------------------------------
-- dumps a 32-bit signed integer (for int)
------------------------------------------------------------------------
function luaU:DumpInt(x, D)
self:DumpBlock(self:from_int(x, format.int_size), D)
end
------------------------------------------------------------------------
-- dumps a 32-bit unsigned integer (for size_t)
------------------------------------------------------------------------
function luaU:DumpSize(x, D)
self:DumpBlock(self:from_int(x, format.size_t_size), D)
end
------------------------------------------------------------------------
-- dumps a LUA_NUMBER (hard-coded as a double)
------------------------------------------------------------------------
function luaU:DumpNumber(x, D)
if format.integral then
self:DumpBlock(self:from_int(x, format.number_size), D)
else
self:DumpBlock(self:from_double(x), D)
end
end
------------------------------------------------------------------------
-- dumps a Lua string
------------------------------------------------------------------------
function luaU:DumpString(s, D)
if s == nil then
self:DumpSize(0, D)
else
s = s.."\0" -- include trailing '\0'
self:DumpSize(string.len(s), D)
self:DumpBlock(s, D)
end
end
------------------------------------------------------------------------
-- dumps instruction block from function prototype
------------------------------------------------------------------------
function luaU:DumpCode(f, D)
local n = f.sizecode
self:DumpInt(n, D)
--was DumpVector
for i = 0, n - 1 do
self:DumpBlock(luaP:Instruction(f.code[i]), D)
end
end
------------------------------------------------------------------------
-- dumps local variable names from function prototype
------------------------------------------------------------------------
function luaU:DumpLocals(f, D)
local n = f.sizelocvars
self:DumpInt(n, D)
for i = 0, n - 1 do
-- Dirty temporary fix:
-- `Stat{ } keeps properly count of the number of local vars,
-- but fails to keep score of their debug info (names).
-- It therefore might happen that #f.localvars < f.sizelocvars, or
-- that a variable's startpc and endpc fields are left unset.
-- FIXME: This might not be needed anymore, check the bug report
-- by J. Belmonte.
local var = f.locvars[i]
if not var then break end
-- printf("[DUMPLOCALS] dumping local var #%i = %s", i, table.tostring(var))
self:DumpString(var.varname, D)
self:DumpInt(var.startpc or 0, D)
self:DumpInt(var.endpc or 0, D)
end
end
------------------------------------------------------------------------
-- dumps line information from function prototype
------------------------------------------------------------------------
function luaU:DumpLines(f, D)
local n = f.sizelineinfo
self:DumpInt(n, D)
--was DumpVector
for i = 0, n - 1 do
self:DumpInt(f.lineinfo[i], D) -- was DumpBlock
--print(i, f.lineinfo[i])
end
end
------------------------------------------------------------------------
-- dump upvalue names from function prototype
------------------------------------------------------------------------
function luaU:DumpUpvalues(f, D)
local n = f.sizeupvalues
self:DumpInt(n, D)
for i = 0, n - 1 do
self:DumpString(f.upvalues[i], D)
end
end
------------------------------------------------------------------------
-- dump constant pool from function prototype
-- * nvalue(o) and tsvalue(o) macros removed
------------------------------------------------------------------------
function luaU:DumpConstants(f, D)
local n = f.sizek
self:DumpInt(n, D)
for i = 0, n - 1 do
local o = f.k[i] -- TObject
local tt = self:ttype(o)
assert (tt >= 0)
self:DumpByte(tt, D)
if tt == self.LUA_TNUMBER then
self:DumpNumber(o.value, D)
elseif tt == self.LUA_TSTRING then
self:DumpString(o.value, D)
elseif tt == self.LUA_TBOOLEAN then
self:DumpByte (o.value and 1 or 0, D)
elseif tt == self.LUA_TNIL then
else
assert(false) -- cannot happen
end
end
end
function luaU:DumpProtos (f, D)
local n = f.sizep
assert (n)
self:DumpInt(n, D)
for i = 0, n - 1 do
self:DumpFunction(f.p[i], f.source, D)
end
end
function luaU:DumpDebug(f, D)
self:DumpLines(f, D)
self:DumpLocals(f, D)
self:DumpUpvalues(f, D)
end
------------------------------------------------------------------------
-- dump child function prototypes from function prototype
--FF completely reworked for 5.1 format
------------------------------------------------------------------------
function luaU:DumpFunction(f, p, D)
-- print "Dumping function:"
-- table.print(f, 60)
local source = f.source
if source == p then source = nil end
self:DumpString(source, D)
self:DumpInt(f.lineDefined, D)
self:DumpInt(f.lastLineDefined or 42, D)
self:DumpByte(f.nups, D)
self:DumpByte(f.numparams, D)
self:DumpByte(f.is_vararg, D)
self:DumpByte(f.maxstacksize, D)
self:DumpCode(f, D)
self:DumpConstants(f, D)
self:DumpProtos( f, D)
self:DumpDebug(f, D)
end
------------------------------------------------------------------------
-- dump Lua header section (some sizes hard-coded)
--FF: updated for version 5.1
------------------------------------------------------------------------
function luaU:DumpHeader(D)
self:DumpLiteral(format.header, D)
end
------------------------------------------------------------------------
-- dump function as precompiled chunk
-- * w, data are created from make_setS, make_setF
--FF: suppressed extraneous [L] param
------------------------------------------------------------------------
function luaU:dump (Main, w, data)
local D = {} -- DumpState
D.write = w
D.data = data
self:DumpHeader(D)
self:DumpFunction(Main, nil, D)
-- added: for a chunk writer writing to a file, this final call with
-- nil data is to indicate to the writer to close the file
D.write(nil, D.data)
end
------------------------------------------------------------------------
-- find byte order (from lundump.c)
-- * hard-coded to little-endian
------------------------------------------------------------------------
function luaU:endianness()
return 1
end
-- FIXME: ugly concat-base generation in [make_setS], bufferize properly!
function dump_string (proto)
local writer, buff = luaU:make_setS()
luaU:dump (proto, writer, buff)
return buff.data
end
-- FIXME: [make_setS] sucks, perform synchronous file writing
-- Now unused
function dump_file (proto, filename)
local writer, buff = luaU:make_setS()
luaU:dump (proto, writer, buff)
local file = io.open (filename, "wb")
file:write (buff.data)
io.close(file)
if UNIX_SHARPBANG then os.execute ("chmod a+x "..filename) end
end

View File

@@ -1,511 +0,0 @@
----------------------------------------------------------------------
-- Metalua: $Id: mll.lua,v 1.3 2006/11/15 09:07:50 fab13n Exp $
--
-- Summary: generic Lua-style lexer definition. You need this plus
-- some keyword additions to create the complete Lua lexer,
-- as is done in mlp_lexer.lua.
--
-- TODO:
--
-- * Make it easy to define new flavors of strings. Replacing the
-- lexer.patterns.long_string regexp by an extensible list, with
-- customizable token tag, would probably be enough. Maybe add:
-- + an index of capture for the regexp, that would specify
-- which capture holds the content of the string-like token
-- + a token tag
-- + or a string->string transformer function.
--
-- * There are some _G.table to prevent a namespace clash which has
-- now disappered. remove them.
----------------------------------------------------------------------
--
-- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
--
-- This software is released under the MIT Licence, see licence.txt
-- for details.
--
----------------------------------------------------------------------
module ("lexer", package.seeall)
-- don't load metalua.runtime as it loads metalua.base, which pollutes
-- global namespace and overwrites pairs/ipairs -- PK 6/4/2012
require 'metalua.table2'
lexer = { alpha={ }, sym={ } }
lexer.__index=lexer
local debugf = function() end
--local debugf=printf
----------------------------------------------------------------------
-- Patterns used by [lexer:extract] to decompose the raw string into
-- correctly tagged tokens.
----------------------------------------------------------------------
lexer.patterns = {
spaces = "^[ \r\n\t]*()",
short_comment = "^%-%-([^\n]*)()\n",
final_short_comment = "^%-%-([^\n]*)()$",
long_comment = "^%-%-%[(=*)%[\n?(.-)%]%1%]()",
long_string = "^%[(=*)%[\n?(.-)%]%1%]()",
number_mantissa = { "^%d+%.?%d*()", "^%d*%.%d+()" },
number_exponant = "^[eE][%+%-]?%d+()",
number_hex = "^0[xX]%x+()",
word = "^([%a_][%w_]*)()"
}
----------------------------------------------------------------------
-- unescape a whole string, applying [unesc_digits] and
-- [unesc_letter] as many times as required.
----------------------------------------------------------------------
local function unescape_string (s)
-- Turn the digits of an escape sequence into the corresponding
-- character, e.g. [unesc_digits("123") == string.char(123)].
local function unesc_digits (backslashes, digits)
if #backslashes%2==0 then
-- Even number of backslashes, they escape each other, not the digits.
-- Return them so that unesc_letter() can treaat them
return backslashes..digits
else
-- Remove the odd backslash, which escapes the number sequence.
-- The rest will be returned and parsed by unesc_letter()
backslashes = backslashes :sub (1,-2)
end
local k, j, i = digits:reverse():byte(1, 3)
local z = _G.string.byte "0"
local code = (k or z) + 10*(j or z) + 100*(i or z) - 111*z
if code > 255 then
error ("Illegal escape sequence '\\"..digits..
"' in string: ASCII codes must be in [0..255]")
end
return backslashes .. string.char (code)
end
-- Take a letter [x], and returns the character represented by the
-- sequence ['\\'..x], e.g. [unesc_letter "n" == "\n"].
local function unesc_letter(x)
local t = {
a = "\a", b = "\b", f = "\f",
n = "\n", r = "\r", t = "\t", v = "\v",
["\\"] = "\\", ["'"] = "'", ['"'] = '"', ["\n"] = "\n" }
return t[x] or error([[Unknown escape sequence '\]]..x..[[']])
end
return s
:gsub ("(\\+)([0-9][0-9]?[0-9]?)", unesc_digits)
:gsub ("\\(%D)",unesc_letter)
end
lexer.extractors = {
"skip_whitespaces_and_comments",
"extract_short_string", "extract_word", "extract_number",
"extract_long_string", "extract_symbol" }
lexer.token_metatable = {
-- __tostring = function(a)
-- return string.format ("`%s{'%s'}",a.tag, a[1])
-- end
}
lexer.lineinfo_metatable = { }
----------------------------------------------------------------------
-- Really extract next token fron the raw string
-- (and update the index).
-- loc: offset of the position just after spaces and comments
-- previous_i: offset in src before extraction began
----------------------------------------------------------------------
function lexer:extract ()
local previous_i = self.i
local loc = self.i
local eof, token
-- Put line info, comments and metatable around the tag and content
-- provided by extractors, thus returning a complete lexer token.
-- first_line: line # at the beginning of token
-- first_column_offset: char # of the last '\n' before beginning of token
-- i: scans from beginning of prefix spaces/comments to end of token.
local function build_token (tag, content)
assert (tag and content)
local i, first_line, first_column_offset, previous_line_length =
previous_i, self.line, self.column_offset, nil
-- update self.line and first_line. i := indexes of '\n' chars
while true do
i = self.src :find ("\n", i+1, true)
if not i or i>self.i then break end -- no more '\n' until end of token
previous_line_length = i - self.column_offset
if loc and i <= loc then -- '\n' before beginning of token
first_column_offset = i
first_line = first_line+1
end
self.line = self.line+1
self.column_offset = i
end
-- lineinfo entries: [1]=line, [2]=column, [3]=char, [4]=filename
local fli = { first_line, loc-first_column_offset, loc, self.src_name }
local lli = { self.line, self.i-self.column_offset-1, self.i-1, self.src_name }
--Pluto barfes when the metatable is set:(
setmetatable(fli, lexer.lineinfo_metatable)
setmetatable(lli, lexer.lineinfo_metatable)
local a = { tag = tag, lineinfo = { first=fli, last=lli }, content }
if lli[2]==-1 then lli[1], lli[2] = lli[1]-1, previous_line_length-1 end
if #self.attached_comments > 0 then
a.lineinfo.comments = self.attached_comments
fli.comments = self.attached_comments
if self.lineinfo_last then
self.lineinfo_last.comments = self.attached_comments
end
end
self.attached_comments = { }
return setmetatable (a, self.token_metatable)
end --</function build_token>
for ext_idx, extractor in ipairs(self.extractors) do
-- printf("method = %s", method)
local tag, content = self [extractor] (self)
-- [loc] is placed just after the leading whitespaces and comments;
-- for this to work, the whitespace extractor *must be* at index 1.
if ext_idx==1 then loc = self.i end
if tag then
--printf("`%s{ %q }\t%i", tag, content, loc);
return build_token (tag, content)
end
end
error "None of the lexer extractors returned anything!"
end
----------------------------------------------------------------------
-- skip whites and comments
-- FIXME: doesn't take into account:
-- - unterminated long comments
-- - short comments at last line without a final \n
----------------------------------------------------------------------
function lexer:skip_whitespaces_and_comments()
local table_insert = _G.table.insert
repeat -- loop as long as a space or comment chunk is found
local _, j
local again = false
local last_comment_content = nil
-- skip spaces
self.i = self.src:match (self.patterns.spaces, self.i)
-- skip a long comment if any
_, last_comment_content, j =
self.src :match (self.patterns.long_comment, self.i)
if j then
table_insert(self.attached_comments,
{last_comment_content, self.i, j, "long"})
self.i=j; again=true
end
-- skip a short comment if any
last_comment_content, j = self.src:match (self.patterns.short_comment, self.i)
if j then
table_insert(self.attached_comments,
{last_comment_content, self.i, j, "short"})
self.i=j; again=true
end
if self.i>#self.src then return "Eof", "eof" end
until not again
if self.src:match (self.patterns.final_short_comment, self.i) then
return "Eof", "eof" end
--assert (not self.src:match(self.patterns.short_comment, self.i))
--assert (not self.src:match(self.patterns.long_comment, self.i))
-- --assert (not self.src:match(self.patterns.spaces, self.i))
return
end
----------------------------------------------------------------------
-- extract a '...' or "..." short string
----------------------------------------------------------------------
function lexer:extract_short_string()
-- [k] is the first unread char, [self.i] points to [k] in [self.src]
local j, k = self.i, self.src :sub (self.i,self.i)
if k~="'" and k~='"' then return end
local i = self.i + 1
local j = i
while true do
-- k = opening char: either simple-quote or double-quote
-- i = index of beginning-of-string
-- x = next "interesting" character
-- j = position after interesting char
-- y = char just after x
local x, y
x, j, y = self.src :match ("([\\\r\n"..k.."])()(.?)", j)
if x == '\\' then j=j+1 -- don't parse escaped char
elseif x == k then break -- unescaped end of string
else -- eof or '\r' or '\n' reached before end of string
assert (not x or x=="\r" or x=="\n")
error "Unterminated string"
end
end
self.i = j
return "String", unescape_string (self.src:sub (i,j-2))
end
----------------------------------------------------------------------
--
----------------------------------------------------------------------
function lexer:extract_word()
-- Id / keyword
local word, j = self.src:match (self.patterns.word, self.i)
if word then
self.i = j
if self.alpha [word] then return "Keyword", word
else return "Id", word end
end
end
----------------------------------------------------------------------
--
----------------------------------------------------------------------
function lexer:extract_number()
-- Number
local j = self.src:match(self.patterns.number_hex, self.i)
if not j then
j = self.src:match (self.patterns.number_mantissa[1], self.i) or
self.src:match (self.patterns.number_mantissa[2], self.i)
if j then
j = self.src:match (self.patterns.number_exponant, j) or j;
end
end
if not j then return end
-- Number found, interpret with tonumber() and return it
local n = tonumber (self.src:sub (self.i, j-1))
self.i = j
return "Number", n
end
----------------------------------------------------------------------
--
----------------------------------------------------------------------
function lexer:extract_long_string()
-- Long string
local _, content, j = self.src:match (self.patterns.long_string, self.i)
if j then self.i = j; return "String", content end
end
----------------------------------------------------------------------
--
----------------------------------------------------------------------
function lexer:extract_symbol()
-- compound symbol
local k = self.src:sub (self.i,self.i)
local symk = self.sym [k]
if not symk then
self.i = self.i + 1
return "Keyword", k
end
for _, sym in pairs (symk) do
if sym == self.src:sub (self.i, self.i + #sym - 1) then
self.i = self.i + #sym;
return "Keyword", sym
end
end
-- single char symbol
self.i = self.i+1
return "Keyword", k
end
----------------------------------------------------------------------
-- Add a keyword to the list of keywords recognized by the lexer.
----------------------------------------------------------------------
function lexer:add (w, ...)
assert(not ..., "lexer:add() takes only one arg, although possibly a table")
if type (w) == "table" then
for _, x in ipairs (w) do self:add (x) end
else
if w:match (self.patterns.word .. "$") then self.alpha [w] = true
elseif w:match "^%p%p+$" then
local k = w:sub(1,1)
local list = self.sym [k]
if not list then list = { }; self.sym [k] = list end
_G.table.insert (list, w)
elseif w:match "^%p$" then return
else error "Invalid keyword" end
end
end
----------------------------------------------------------------------
-- Return the [n]th next token, without consumming it.
-- [n] defaults to 1. If it goes pass the end of the stream, an EOF
-- token is returned.
----------------------------------------------------------------------
function lexer:peek (n)
if not n then n=1 end
if n > #self.peeked then
for i = #self.peeked+1, n do
self.peeked [i] = self:extract()
end
end
return self.peeked [n]
end
----------------------------------------------------------------------
-- Return the [n]th next token, removing it as well as the 0..n-1
-- previous tokens. [n] defaults to 1. If it goes pass the end of the
-- stream, an EOF token is returned.
----------------------------------------------------------------------
function lexer:next (n)
n = n or 1
self:peek (n)
local a
for i=1,n do
a = _G.table.remove (self.peeked, 1)
if a then
--debugf ("lexer:next() ==> %s %s",
-- table.tostring(a), tostring(a))
end
self.lastline = a.lineinfo.last[1]
end
self.lineinfo_last = a.lineinfo.last
return a or eof_token
end
----------------------------------------------------------------------
-- Returns an object which saves the stream's current state.
----------------------------------------------------------------------
-- FIXME there are more fields than that to save
function lexer:save () return { self.i; _G.table.cat(self.peeked) } end
----------------------------------------------------------------------
-- Restore the stream's state, as saved by method [save].
----------------------------------------------------------------------
-- FIXME there are more fields than that to restore
function lexer:restore (s) self.i=s[1]; self.peeked=s[2] end
----------------------------------------------------------------------
-- Resynchronize: cancel any token in self.peeked, by emptying the
-- list and resetting the indexes
----------------------------------------------------------------------
function lexer:sync()
local p1 = self.peeked[1]
if p1 then
li = p1.lineinfo.first
self.line, self.i = li[1], li[3]
self.column_offset = self.i - li[2]
self.peeked = { }
self.attached_comments = p1.lineinfo.first.comments or { }
end
end
----------------------------------------------------------------------
-- Take the source and offset of an old lexer.
----------------------------------------------------------------------
function lexer:takeover(old)
self:sync()
self.line, self.column_offset, self.i, self.src, self.attached_comments =
old.line, old.column_offset, old.i, old.src, old.attached_comments
return self
end
-- function lexer:lineinfo()
-- if self.peeked[1] then return self.peeked[1].lineinfo.first
-- else return { self.line, self.i-self.column_offset, self.i } end
-- end
----------------------------------------------------------------------
-- Return the current position in the sources. This position is between
-- two tokens, and can be within a space / comment area, and therefore
-- have a non-null width. :lineinfo_left() returns the beginning of the
-- separation area, :lineinfo_right() returns the end of that area.
--
-- ____ last consummed token ____ first unconsummed token
-- / /
-- XXXXX <spaces and comments> YYYYY
-- \____ \____
-- :lineinfo_left() :lineinfo_right()
----------------------------------------------------------------------
function lexer:lineinfo_right()
return self:peek(1).lineinfo.first
end
function lexer:lineinfo_left()
return self.lineinfo_last
end
----------------------------------------------------------------------
-- Create a new lexstream.
----------------------------------------------------------------------
function lexer:newstream (src_or_stream, name)
name = name or "?"
if type(src_or_stream)=='table' then -- it's a stream
return setmetatable ({ }, self) :takeover (src_or_stream)
elseif type(src_or_stream)=='string' then -- it's a source string
local src = src_or_stream
local stream = {
src_name = name; -- Name of the file
src = src; -- The source, as a single string
peeked = { }; -- Already peeked, but not discarded yet, tokens
i = 1; -- Character offset in src
line = 1; -- Current line number
column_offset = 0; -- distance from beginning of file to last '\n'
attached_comments = { },-- comments accumulator
lineinfo_last = { 1, 1, 1, name }
}
setmetatable (stream, self)
-- skip initial sharp-bang for unix scripts
-- FIXME: redundant with mlp.chunk()
if src and src :match "^#" then stream.i = src :find "\n" + 1 end
return stream
else
assert(false, ":newstream() takes a source string or a stream, not a "..
type(src_or_stream))
end
end
----------------------------------------------------------------------
-- if there's no ... args, return the token a (whose truth value is
-- true) if it's a `Keyword{ }, or nil. If there are ... args, they
-- have to be strings. if the token a is a keyword, and it's content
-- is one of the ... args, then returns it (it's truth value is
-- true). If no a keyword or not in ..., return nil.
----------------------------------------------------------------------
function lexer:is_keyword (a, ...)
if not a or a.tag ~= "Keyword" then return false end
local words = {...}
if #words == 0 then return a[1] end
for _, w in ipairs (words) do
if w == a[1] then return w end
end
return false
end
----------------------------------------------------------------------
-- Cause an error if the next token isn't a keyword whose content
-- is listed among ... args (which have to be strings).
----------------------------------------------------------------------
function lexer:check (...)
local words = {...}
local a = self:next()
local function err ()
error ("Got " .. tostring (a) ..
", expected one of these keywords : '" ..
_G.table.concat (words,"', '") .. "'") end
if not a or a.tag ~= "Keyword" then err () end
if #words == 0 then return a[1] end
for _, w in ipairs (words) do
if w == a[1] then return w end
end
err ()
end
----------------------------------------------------------------------
--
----------------------------------------------------------------------
function lexer:clone()
local clone = {
alpha = table.deep_copy(self.alpha),
sym = table.deep_copy(self.sym) }
setmetatable(clone, self)
clone.__index = clone
return clone
end

View File

@@ -1,440 +0,0 @@
----------------------------------------------------------------------
--
-- WARNING! You're entering a hackish area, proceed at your own risks!
--
-- This code results from the borrowing, then ruthless abuse, of
-- Yueliang's implementation of Lua 5.0 compiler. I claim
-- responsibility for all of the ugly, dirty stuff that you might spot
-- in it.
--
-- Eventually, this code will be rewritten, either in Lua or more
-- probably in C. Meanwhile, if you're interested into digging
-- metalua's sources, this is not the best part to invest your time
-- on.
--
-- End of warning.
--
----------------------------------------------------------------------
--[[--------------------------------------------------------------------
$Id$
lopcodes.lua
Lua 5 virtual machine opcodes in Lua
This file is part of Yueliang.
Copyright (c) 2005 Kein-Hong Man <khman@users.sf.net>
The COPYRIGHT file describes the conditions
under which this software may be distributed.
See the ChangeLog for more information.
------------------------------------------------------------------------
[FF] Slightly modified, mainly to produce Lua 5.1 bytecode.
----------------------------------------------------------------------]]
--[[--------------------------------------------------------------------
-- Notes:
-- * an Instruction is a table with OP, A, B, C, Bx elements; this
-- should allow instruction handling to work with doubles and ints
-- * Added:
-- luaP:Instruction(i): convert field elements to a 4-char string
-- luaP:DecodeInst(x): convert 4-char string into field elements
-- * WARNING luaP:Instruction outputs instructions encoded in little-
-- endian form and field size and positions are hard-coded
----------------------------------------------------------------------]]
module("bytecode", package.seeall)
local function debugf() end
luaP = { }
--[[
===========================================================================
We assume that instructions are unsigned numbers.
All instructions have an opcode in the first 6 bits.
Instructions can have the following fields:
'A' : 8 bits
'B' : 9 bits
'C' : 9 bits
'Bx' : 18 bits ('B' and 'C' together)
'sBx' : signed Bx
A signed argument is represented in excess K; that is, the number
value is the unsigned value minus K. K is exactly the maximum value
for that argument (so that -max is represented by 0, and +max is
represented by 2*max), which is half the maximum for the corresponding
unsigned argument.
===========================================================================
--]]
luaP.OpMode = {"iABC", "iABx", "iAsBx"} -- basic instruction format
------------------------------------------------------------------------
-- size and position of opcode arguments.
-- * WARNING size and position is hard-coded elsewhere in this script
------------------------------------------------------------------------
luaP.SIZE_C = 9
luaP.SIZE_B = 9
luaP.SIZE_Bx = luaP.SIZE_C + luaP.SIZE_B
luaP.SIZE_A = 8
luaP.SIZE_OP = 6
luaP.POS_C = luaP.SIZE_OP
luaP.POS_B = luaP.POS_C + luaP.SIZE_C
luaP.POS_Bx = luaP.POS_C
luaP.POS_A = luaP.POS_B + luaP.SIZE_B
--FF from 5.1
luaP.BITRK = 2^(luaP.SIZE_B - 1)
function luaP:ISK(x) return x >= self.BITRK end
luaP.MAXINDEXRK = luaP.BITRK - 1
function luaP:RKASK(x)
if x < self.BITRK then return x+self.BITRK else return x end
end
------------------------------------------------------------------------
-- limits for opcode arguments.
-- we use (signed) int to manipulate most arguments,
-- so they must fit in BITS_INT-1 bits (-1 for sign)
------------------------------------------------------------------------
-- removed "#if SIZE_Bx < BITS_INT-1" test, assume this script is
-- running on a Lua VM with double or int as LUA_NUMBER
luaP.MAXARG_Bx = math.ldexp(1, luaP.SIZE_Bx) - 1
luaP.MAXARG_sBx = math.floor(luaP.MAXARG_Bx / 2) -- 'sBx' is signed
luaP.MAXARG_A = math.ldexp(1, luaP.SIZE_A) - 1
luaP.MAXARG_B = math.ldexp(1, luaP.SIZE_B) - 1
luaP.MAXARG_C = math.ldexp(1, luaP.SIZE_C) - 1
-- creates a mask with 'n' 1 bits at position 'p'
-- MASK1(n,p) deleted
-- creates a mask with 'n' 0 bits at position 'p'
-- MASK0(n,p) deleted
--[[--------------------------------------------------------------------
Visual representation for reference:
31 | | | 0 bit position
+-----+-----+-----+----------+
| B | C | A | Opcode | iABC format
+-----+-----+-----+----------+
- 9 - 9 - 8 - 6 - field sizes
+-----+-----+-----+----------+
| [s]Bx | A | Opcode | iABx | iAsBx format
+-----+-----+-----+----------+
----------------------------------------------------------------------]]
------------------------------------------------------------------------
-- the following macros help to manipulate instructions
-- * changed to a table object representation, very clean compared to
-- the [nightmare] alternatives of using a number or a string
------------------------------------------------------------------------
-- these accept or return opcodes in the form of string names
function luaP:GET_OPCODE(i) return self.ROpCode[i.OP] end
function luaP:SET_OPCODE(i, o) i.OP = self.OpCode[o] end
function luaP:GETARG_A(i) return i.A end
function luaP:SETARG_A(i, u) i.A = u end
function luaP:GETARG_B(i) return i.B end
function luaP:SETARG_B(i, b) i.B = b end
function luaP:GETARG_C(i) return i.C end
function luaP:SETARG_C(i, b) i.C = b end
function luaP:GETARG_Bx(i) return i.Bx end
function luaP:SETARG_Bx(i, b) i.Bx = b end
function luaP:GETARG_sBx(i) return i.Bx - self.MAXARG_sBx end
function luaP:SETARG_sBx(i, b) i.Bx = b + self.MAXARG_sBx end
function luaP:CREATE_ABC(o,a,b,c)
return {OP = self.OpCode[o], A = a, B = b, C = c}
end
function luaP:CREATE_ABx(o,a,bc)
return {OP = self.OpCode[o], A = a, Bx = bc}
end
------------------------------------------------------------------------
-- Bit shuffling stuffs
------------------------------------------------------------------------
if false and pcall (require, 'bit') then
------------------------------------------------------------------------
-- Return a 4-char string little-endian encoded form of an instruction
------------------------------------------------------------------------
function luaP:Instruction(i)
--FIXME
end
else
------------------------------------------------------------------------
-- Version without bit manipulation library.
------------------------------------------------------------------------
local p2 = {1,2,4,8,16,32,64,128,256, 512, 1024, 2048, 4096}
-- keeps [n] bits from [x]
local function keep (x, n) return x % p2[n+1] end
-- shifts bits of [x] [n] places to the right
local function srb (x,n) return math.floor (x / p2[n+1]) end
-- shifts bits of [x] [n] places to the left
local function slb (x,n) return x * p2[n+1] end
------------------------------------------------------------------------
-- Return a 4-char string little-endian encoded form of an instruction
------------------------------------------------------------------------
function luaP:Instruction(i)
-- printf("Instr->string: %s %s", self.opnames[i.OP], table.tostring(i))
local c0, c1, c2, c3
-- change to OP/A/B/C format if needed
if i.Bx then i.C = keep (i.Bx, 9); i.B = srb (i.Bx, 9) end
-- c0 = 6B from opcode + 2LSB from A (flushed to MSB)
c0 = i.OP + slb (keep (i.A, 2), 6)
-- c1 = 6MSB from A + 2LSB from C (flushed to MSB)
c1 = srb (i.A, 2) + slb (keep (i.C, 2), 6)
-- c2 = 7MSB from C + 1LSB from B (flushed to MSB)
c2 = srb (i.C, 2) + slb (keep (i.B, 1), 7)
-- c3 = 8MSB from B
c3 = srb (i.B, 1)
--printf ("Instruction: %s %s", self.opnames[i.OP], tostringv (i))
--printf ("Bin encoding: %x %x %x %x", c0, c1, c2, c3)
return string.char(c0, c1, c2, c3)
end
end
------------------------------------------------------------------------
-- decodes a 4-char little-endian string into an instruction struct
------------------------------------------------------------------------
function luaP:DecodeInst(x)
error "Not implemented"
end
------------------------------------------------------------------------
-- invalid register that fits in 8 bits
------------------------------------------------------------------------
luaP.NO_REG = luaP.MAXARG_A
------------------------------------------------------------------------
-- R(x) - register
-- Kst(x) - constant (in constant table)
-- RK(x) == if x < MAXSTACK then R(x) else Kst(x-MAXSTACK)
------------------------------------------------------------------------
------------------------------------------------------------------------
-- grep "ORDER OP" if you change these enums
------------------------------------------------------------------------
--[[--------------------------------------------------------------------
Lua virtual machine opcodes (enum OpCode):
------------------------------------------------------------------------
name args description
------------------------------------------------------------------------
OP_MOVE A B R(A) := R(B)
OP_LOADK A Bx R(A) := Kst(Bx)
OP_LOADBOOL A B C R(A) := (Bool)B; if (C) PC++
OP_LOADNIL A B R(A) := ... := R(B) := nil
OP_GETUPVAL A B R(A) := UpValue[B]
OP_GETGLOBAL A Bx R(A) := Gbl[Kst(Bx)]
OP_GETTABLE A B C R(A) := R(B)[RK(C)]
OP_SETGLOBAL A Bx Gbl[Kst(Bx)] := R(A)
OP_SETUPVAL A B UpValue[B] := R(A)
OP_SETTABLE A B C R(A)[RK(B)] := RK(C)
OP_NEWTABLE A B C R(A) := {} (size = B,C)
OP_SELF A B C R(A+1) := R(B); R(A) := R(B)[RK(C)]
OP_ADD A B C R(A) := RK(B) + RK(C)
OP_SUB A B C R(A) := RK(B) - RK(C)
OP_MUL A B C R(A) := RK(B) * RK(C)
OP_DIV A B C R(A) := RK(B) / RK(C)
OP_POW A B C R(A) := RK(B) ^ RK(C)
OP_UNM A B R(A) := -R(B)
OP_NOT A B R(A) := not R(B)
OP_CONCAT A B C R(A) := R(B).. ... ..R(C)
OP_JMP sBx PC += sBx
OP_EQ A B C if ((RK(B) == RK(C)) ~= A) then pc++
OP_LT A B C if ((RK(B) < RK(C)) ~= A) then pc++
OP_LE A B C if ((RK(B) <= RK(C)) ~= A) then pc++
OP_TEST A B C if (R(B) <=> C) then R(A) := R(B) else pc++
OP_CALL A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1))
OP_TAILCALL A B C return R(A)(R(A+1), ... ,R(A+B-1))
OP_RETURN A B return R(A), ... ,R(A+B-2) (see note)
OP_FORLOOP A sBx R(A)+=R(A+2); if R(A) <?= R(A+1) then PC+= sBx
OP_TFORLOOP A C R(A+2), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
if R(A+2) ~= nil then pc++
OP_TFORPREP A sBx if type(R(A)) == table then R(A+1):=R(A), R(A):=next;
PC += sBx
OP_SETLIST A Bx R(A)[Bx-Bx%FPF+i] := R(A+i), 1 <= i <= Bx%FPF+1
OP_SETLISTO A Bx (see note)
OP_CLOSE A close all variables in the stack up to (>=) R(A)
OP_CLOSURE A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n))
----------------------------------------------------------------------]]
luaP.opnames = {} -- opcode names
luaP.OpCode = {} -- lookup name -> number
luaP.ROpCode = {} -- lookup number -> name
local i = 0
for v in string.gmatch([[
MOVE -- 0
LOADK
LOADBOOL
LOADNIL
GETUPVAL
GETGLOBAL -- 5
GETTABLE
SETGLOBAL
SETUPVAL
SETTABLE
NEWTABLE -- 10
SELF
ADD
SUB
MUL
DIV -- 15
MOD
POW
UNM
NOT
LEN -- 20
CONCAT
JMP
EQ
LT
LE -- 25
TEST
TESTSET
CALL
TAILCALL
RETURN -- 30
FORLOOP
FORPREP
TFORLOOP
SETLIST
CLOSE -- 35
CLOSURE
VARARG
]], "[%a]+") do
local n = "OP_"..v
luaP.opnames[i] = v
luaP.OpCode[n] = i
luaP.ROpCode[i] = n
i = i + 1
end
luaP.NUM_OPCODES = i
--[[
===========================================================================
Notes:
(1) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
and can be 0: OP_CALL then sets 'top' to last_result+1, so
next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use 'top'.
(2) In OP_RETURN, if (B == 0) then return up to 'top'
(3) For comparisons, B specifies what conditions the test should accept.
(4) All 'skips' (pc++) assume that next instruction is a jump
(5) OP_SETLISTO is used when the last item in a table constructor is a
function, so the number of elements set is up to top of stack
===========================================================================
--]]
------------------------------------------------------------------------
-- masks for instruction properties
------------------------------------------------------------------------
-- was enum OpModeMask:
luaP.OpModeBreg = 2 -- B is a register
luaP.OpModeBrk = 3 -- B is a register/constant
luaP.OpModeCrk = 4 -- C is a register/constant
luaP.OpModesetA = 5 -- instruction set register A
luaP.OpModeK = 6 -- Bx is a constant
luaP.OpModeT = 1 -- operator is a test
------------------------------------------------------------------------
-- get opcode mode, e.g. "iABC"
------------------------------------------------------------------------
function luaP:getOpMode(m)
--printv(m)
--printv(self.OpCode[m])
--printv(self.opmodes [self.OpCode[m]+1])
return self.OpMode[tonumber(string.sub(self.opmodes[self.OpCode[m] + 1], 7, 7))]
end
------------------------------------------------------------------------
-- test an instruction property flag
-- * b is a string, e.g. "OpModeBreg"
------------------------------------------------------------------------
function luaP:testOpMode(m, b)
return (string.sub(self.opmodes[self.OpCode[m] + 1], self[b], self[b]) == "1")
end
-- number of list items to accumulate before a SETLIST instruction
-- (must be a power of 2)
-- * used in lparser, lvm, ldebug, ltests
luaP.LFIELDS_PER_FLUSH = 50 --FF updated to match 5.1
-- luaP_opnames[] is set above, as the luaP.opnames table
-- opmode(t,b,bk,ck,sa,k,m) deleted
--[[--------------------------------------------------------------------
Legend for luaP:opmodes:
1 T -> T (is a test?)
2 B -> B is a register
3 b -> B is an RK register/constant combination
4 C -> C is an RK register/constant combination
5 A -> register A is set by the opcode
6 K -> Bx is a constant
7 m -> 1 if iABC layout,
2 if iABx layout,
3 if iAsBx layout
----------------------------------------------------------------------]]
luaP.opmodes = {
-- TBbCAKm opcode
"0100101", -- OP_MOVE 0
"0000112", -- OP_LOADK
"0000101", -- OP_LOADBOOL
"0100101", -- OP_LOADNIL
"0000101", -- OP_GETUPVAL
"0000112", -- OP_GETGLOBAL 5
"0101101", -- OP_GETTABLE
"0000012", -- OP_SETGLOBAL
"0000001", -- OP_SETUPVAL
"0011001", -- OP_SETTABLE
"0000101", -- OP_NEWTABLE 10
"0101101", -- OP_SELF
"0011101", -- OP_ADD
"0011101", -- OP_SUB
"0011101", -- OP_MUL
"0011101", -- OP_DIV 15
"0011101", -- OP_MOD
"0011101", -- OP_POW
"0100101", -- OP_UNM
"0100101", -- OP_NOT
"0100101", -- OP_LEN 20
"0101101", -- OP_CONCAT
"0000003", -- OP_JMP
"1011001", -- OP_EQ
"1011001", -- OP_LT
"1011001", -- OP_LE 25
"1000101", -- OP_TEST
"1100101", -- OP_TESTSET
"0000001", -- OP_CALL
"0000001", -- OP_TAILCALL
"0000001", -- OP_RETURN 30
"0000003", -- OP_FORLOOP
"0000103", -- OP_FORPREP
"1000101", -- OP_TFORLOOP
"0000001", -- OP_SETLIST
"0000001", -- OP_CLOSE 35
"0000102", -- OP_CLOSURE
"0000101" -- OP_VARARG
}

View File

@@ -1,60 +0,0 @@
-- construct proper path to load metalua modules to build
-- an abstract syntax tree (AST)
local file = debug.getinfo(1, "S").source
if string.find(file, "@") == 1 then file = string.sub(file, 2) end
package.path = string.gsub(file, "metalua%.lua$", "?.lua") .. ';' .. package.path
-- these modules are sufficient to build an AST from a source file/string
require "lexer"
require "gg"
require "mlp_lexer"
require "mlp_misc"
require "mlp_table"
require "mlp_meta"
require "mlp_expr"
require "mlp_stat"
-- these modules are needed to convert an AST into bytecode to execute
require "lcode"
require "ldump"
require "lopcodes"
require "compile"
-- this is the compiler module that builds bytecode from an AST
local mlc = { }
function mlc.function_of_ast (ast)
local proto = bytecode.metalua_compile(ast)
local dump = bytecode.dump_string(proto)
local func = string.undump(dump)
return func
end
function mlc.ast_of_luastring (src, file)
local lx = mlp.lexer:newstream(src, file or "(string)")
local ast = mlp.chunk(lx)
return ast
end
function mlc.function_of_luastring (src, file)
local ast = mlc.ast_of_luastring(src, file)
local func = mlc.function_of_ast(ast)
return func
end
function mlc.function_of_luafile (name)
local f = io.open(name, 'r')
local src = f:read('*a')
f:close()
return mlc.function_of_luastring(src, "@"..name)
end
_G.mlc = mlc
--[[
-- Can be used with the following code:
require "metalua"
local ast = mlc.ast_of_luastring(src)
local f = mlc.function_of_ast(ast)
f()
]]

View File

@@ -1,380 +0,0 @@
---------------------------------------------------------------------
----------------------------------------------------------------------
--
-- Table module extension
--
----------------------------------------------------------------------
----------------------------------------------------------------------
-- todo: table.scan (scan1?) fold1? flip?
function table.transpose(t)
local tt = { }
for a, b in pairs(t) do tt[b] = a end
return tt
end
function table.iforeach(f, ...)
-- assert (type (f) == "function") [wouldn't allow metamethod __call]
local nargs = select("#", ...)
if nargs==1 then -- Quick iforeach (most common case), just one table arg
local t = ...
assert (type (t) == "table")
for i = 1, #t do
local result = f (t[i])
-- If the function returns non-false, stop iteration
if result then return result end
end
else -- advanced case: boundaries and/or multiple tables
-- fargs: arguments fot a single call to f
-- first, last: indexes of the first & last elements mapped in each table
-- arg1: index of the first table in args
-- 1 - find boundaries if any
local args, fargs, first, last, arg1 = {...}, { }
if type(args[1]) ~= "number" then first, arg1 = 1, 1 -- no boundary
elseif type(args[2]) ~= "number" then first, last, arg1 = 1, args[1], 2
else first, last, arg1 = args[1], args[2], 3 end
assert (nargs >= arg1) -- at least one table
-- 2 - determine upper boundary if not given
if not last then for i = arg1, nargs do
assert (type (args[i]) == "table")
last = max (#args[i], last)
end end
-- 3 - remove non-table arguments from args, adjust nargs
if arg1>1 then args = { select(arg1, unpack(args)) }; nargs = #args end
-- 4 - perform the iteration
for i = first, last do
for j = 1, nargs do fargs[j] = args[j][i] end -- build args list
local result = f (unpack (fargs)) -- here is the call
-- If the function returns non-false, stop iteration
if result then return result end
end
end
end
function table.imap (f, ...)
local result, idx = { }, 1
local function g(...) result[idx] = f(...); idx=idx+1 end
table.iforeach(g, ...)
return result
end
function table.ifold (f, acc, ...)
local function g(...) acc = f (acc,...) end
table.iforeach (g, ...)
return acc
end
-- function table.ifold1 (f, ...)
-- return table.ifold (f, acc, 2, false, ...)
-- end
function table.izip(...)
local function g(...) return {...} end
return table.imap(g, ...)
end
function table.ifilter(f, t)
local yes, no = { }, { }
for i=1,#t do table.insert (f(t[i]) and yes or no, t[i]) end
return yes, no
end
function table.icat(...)
local result = { }
for t in values {...} do
for x in values (t) do
table.insert (result, x)
end
end
return result
end
function table.iflatten (x) return table.icat (unpack (x)) end
function table.irev (t)
local result, nt = { }, #t
for i=0, nt-1 do result[nt-i] = t[i+1] end
return result
end
function table.isub (t, ...)
local ti, u = table.insert, { }
local args, nargs = {...}, select("#", ...)
for i=1, nargs/2 do
local a, b = args[2*i-1], args[2*i]
for i=a, b, a<=b and 1 or -1 do ti(u, t[i]) end
end
return u
end
function table.iall (f, ...)
local result = true
local function g(...) return not f(...) end
return not table.iforeach(g, ...)
--return result
end
function table.iany (f, ...)
local function g(...) return not f(...) end
return not table.iall(g, ...)
end
function table.shallow_copy(x)
local y={ }
for k, v in pairs(x) do y[k]=v end
return y
end
-- Warning, this is implementation dependent: it relies on
-- the fact the [next()] enumerates the array-part before the hash-part.
function table.cat(...)
local y={ }
for x in values{...} do
-- cat array-part
for _, v in ipairs(x) do table.insert(y,v) end
-- cat hash-part
local lx, k = #x
if lx>0 then k=next(x,lx) else k=next(x) end
while k do y[k]=x[k]; k=next(x,k) end
end
return y
end
function table.deep_copy(x)
local tracker = { }
local function aux (x)
if type(x) == "table" then
local y=tracker[x]
if y then return y end
y = { }; tracker[x] = y
setmetatable (y, getmetatable (x))
for k,v in pairs(x) do y[aux(k)] = aux(v) end
return y
else return x end
end
return aux(x)
end
function table.override(dst, src)
for k, v in pairs(src) do dst[k] = v end
for i = #src+1, #dst do dst[i] = nil end
return dst
end
function table.range(a,b,c)
if not b then assert(not(c)); b=a; a=1
elseif not c then c = (b>=a) and 1 or -1 end
local result = { }
for i=a, b, c do table.insert(result, i) end
return result
end
-- FIXME: new_indent seems to be always nil?!
-- FIXME: accumulator function should be configurable,
-- so that print() doesn't need to bufferize the whole string
-- before starting to print.
function table.tostring(t, ...)
local PRINT_HASH, HANDLE_TAG, FIX_INDENT, LINE_MAX, INITIAL_INDENT = true, true
for _, x in ipairs {...} do
if type(x) == "number" then
if not LINE_MAX then LINE_MAX = x
else INITIAL_INDENT = x end
elseif x=="nohash" then PRINT_HASH = false
elseif x=="notag" then HANDLE_TAG = false
else
local n = string['match'](x, "^indent%s*(%d*)$")
if n then FIX_INDENT = tonumber(n) or 3 end
end
end
LINE_MAX = LINE_MAX or math.huge
INITIAL_INDENT = INITIAL_INDENT or 1
local current_offset = 0 -- indentation level
local xlen_cache = { } -- cached results for xlen()
local acc_list = { } -- Generated bits of string
local function acc(...) -- Accumulate a bit of string
local x = table.concat{...}
current_offset = current_offset + #x
table.insert(acc_list, x)
end
local function valid_id(x)
-- FIXME: we should also reject keywords; but the list of
-- current keywords is not fixed in metalua...
return type(x) == "string"
and string['match'](x, "^[a-zA-Z_][a-zA-Z0-9_]*$")
end
-- Compute the number of chars it would require to display the table
-- on a single line. Helps to decide whether some carriage returns are
-- required. Since the size of each sub-table is required many times,
-- it's cached in [xlen_cache].
local xlen_type = { }
local function xlen(x, nested)
nested = nested or { }
if x==nil then return #"nil" end
--if nested[x] then return #tostring(x) end -- already done in table
local len = xlen_cache[x]
if len then return len end
local f = xlen_type[type(x)]
if not f then return #tostring(x) end
len = f (x, nested)
xlen_cache[x] = len
return len
end
-- optim: no need to compute lengths if I'm not going to use them
-- anyway.
if LINE_MAX == math.huge then xlen = function() return 0 end end
xlen_type["nil"] = function () return 3 end
function xlen_type.number (x) return #tostring(x) end
function xlen_type.boolean (x) return x and 4 or 5 end
function xlen_type.string (x) return #string.format("%q",x) end
function xlen_type.table (adt, nested)
-- Circular references detection
if nested [adt] then return #tostring(adt) end
nested [adt] = true
local has_tag = HANDLE_TAG and valid_id(adt.tag)
local alen = #adt
local has_arr = alen>0
local has_hash = false
local x = 0
if PRINT_HASH then
-- first pass: count hash-part
for k, v in pairs(adt) do
if k=="tag" and has_tag then
-- this is the tag -> do nothing!
elseif type(k)=="number" and k<=alen and math.fmod(k,1)==0 then
-- array-part pair -> do nothing!
else
has_hash = true
if valid_id(k) then x=x+#k
else x = x + xlen (k, nested) + 2 end -- count surrounding brackets
x = x + xlen (v, nested) + 5 -- count " = " and ", "
end
end
end
for i = 1, alen do x = x + xlen (adt[i], nested) + 2 end -- count ", "
nested[adt] = false -- No more nested calls
if not (has_tag or has_arr or has_hash) then return 3 end
if has_tag then x=x+#adt.tag+1 end
if not (has_arr or has_hash) then return x end
if not has_hash and alen==1 and type(adt[1])~="table" then
return x-2 -- substract extraneous ", "
end
return x+2 -- count "{ " and " }", substract extraneous ", "
end
-- Recursively print a (sub) table at given indentation level.
-- [newline] indicates whether newlines should be inserted.
local function rec (adt, nested, indent)
if not FIX_INDENT then indent = current_offset end
local function acc_newline()
acc ("\n"); acc (string.rep (" ", indent))
current_offset = indent
end
local x = { }
x["nil"] = function() acc "nil" end
function x.number() acc (tostring (adt)) end
--function x.string() acc (string.format ("%q", adt)) end
function x.string() acc ((string.format ("%q", adt):gsub("\\\n", "\\n"))) end
function x.boolean() acc (adt and "true" or "false") end
function x.table()
if nested[adt] then acc(tostring(adt)); return end
nested[adt] = true
local has_tag = HANDLE_TAG and valid_id(adt.tag)
local alen = #adt
local has_arr = alen>0
local has_hash = false
if has_tag then acc("`"); acc(adt.tag) end
-- First pass: handle hash-part
if PRINT_HASH then
for k, v in pairs(adt) do
-- pass if the key belongs to the array-part or is the "tag" field
if not (k=="tag" and HANDLE_TAG) and
not (type(k)=="number" and k<=alen and math.fmod(k,1)==0) then
-- Is it the first time we parse a hash pair?
if not has_hash then
acc "{ "
if not FIX_INDENT then indent = current_offset end
else acc ", " end
-- Determine whether a newline is required
local is_id, expected_len = valid_id(k)
if is_id then expected_len = #k + xlen (v, nested) + #" = , "
else expected_len = xlen (k, nested) +
xlen (v, nested) + #"[] = , " end
if has_hash and expected_len + current_offset > LINE_MAX
then acc_newline() end
-- Print the key
if is_id then acc(k); acc " = "
else acc "["; rec (k, nested, indent+(FIX_INDENT or 0)); acc "] = " end
-- Print the value
rec (v, nested, indent+(FIX_INDENT or 0))
has_hash = true
end
end
end
-- Now we know whether there's a hash-part, an array-part, and a tag.
-- Tag and hash-part are already printed if they're present.
if not has_tag and not has_hash and not has_arr then acc "{ }";
elseif has_tag and not has_hash and not has_arr then -- nothing, tag already in acc
else
assert (has_hash or has_arr)
local no_brace = false
if has_hash and has_arr then acc ", "
elseif has_tag and not has_hash and alen==1 and type(adt[1])~="table" then
-- No brace required; don't print "{", remember not to print "}"
acc (" "); rec (adt[1], nested, indent+(FIX_INDENT or 0))
no_brace = true
elseif not has_hash then
-- Braces required, but not opened by hash-part handler yet
acc "{ "
if not FIX_INDENT then indent = current_offset end
end
-- 2nd pass: array-part
if not no_brace and has_arr then
rec (adt[1], nested, indent+(FIX_INDENT or 0))
for i=2, alen do
acc ", ";
if current_offset + xlen (adt[i], { }) > LINE_MAX
then acc_newline() end
rec (adt[i], nested, indent+(FIX_INDENT or 0))
end
end
if not no_brace then acc " }" end
end
nested[adt] = false -- No more nested calls
end
local y = x[type(adt)]
if y then y() else acc(tostring(adt)) end
end
--printf("INITIAL_INDENT = %i", INITIAL_INDENT)
current_offset = INITIAL_INDENT or 0
rec(t, { }, 0)
return table.concat (acc_list)
end
function table.print(...) return print(table.tostring(...)) end
return table

View File

@@ -1,213 +0,0 @@
----------------------------------------------------------------------
-- Metalua: $Id: mlp_expr.lua,v 1.7 2006/11/15 09:07:50 fab13n Exp $
--
-- Summary: metalua parser, expression parser. This is part of the
-- definition of module [mlp].
--
----------------------------------------------------------------------
--
-- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
--
-- This software is released under the MIT Licence, see licence.txt
-- for details.
--
----------------------------------------------------------------------
-- History:
-- $Log: mlp_expr.lua,v $
-- Revision 1.7 2006/11/15 09:07:50 fab13n
-- debugged meta operators.
-- Added command line options handling.
--
-- Revision 1.6 2006/11/10 02:11:17 fab13n
-- compiler faithfulness to 5.1 improved
-- gg.expr extended
-- mlp.expr refactored
--
-- Revision 1.5 2006/11/09 09:39:57 fab13n
-- some cleanup
--
-- Revision 1.4 2006/11/07 21:29:02 fab13n
-- improved quasi-quoting
--
-- Revision 1.3 2006/11/07 04:38:00 fab13n
-- first bootstrapping version.
--
-- Revision 1.2 2006/11/05 15:08:34 fab13n
-- updated code generation, to be compliant with 5.1
--
----------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Exported API:
-- * [mlp.expr()]
-- * [mlp.expr_list()]
-- * [mlp.func_val()]
--
--------------------------------------------------------------------------------
--require "gg"
--require "mlp_misc"
--require "mlp_table"
--require "mlp_meta"
--------------------------------------------------------------------------------
-- These function wrappers (eta-expansions ctually) are just here to break
-- some circular dependencies between mlp_xxx.lua files.
--------------------------------------------------------------------------------
local function _expr (lx) return mlp.expr (lx) end
local function _table_content (lx) return mlp.table_content (lx) end
local function block (lx) return mlp.block (lx) end
local function stat (lx) return mlp.stat (lx) end
module ("mlp", package.seeall)
--------------------------------------------------------------------------------
-- Non-empty expression list. Actually, this isn't used here, but that's
-- handy to give to users.
--------------------------------------------------------------------------------
expr_list = gg.list{ _expr, separators = "," }
--------------------------------------------------------------------------------
-- Helpers for function applications / method applications
--------------------------------------------------------------------------------
func_args_content = gg.list {
name = "function arguments",
_expr, separators = ",", terminators = ")" }
-- Used to parse methods
method_args = gg.multisequence{
name = "function argument(s)",
{ "{", table_content, "}" },
{ "(", func_args_content, ")", builder = fget(1) },
{ "+{", quote_content, "}" },
function(lx) local r = opt_string(lx); return r and {r} or { } end }
--------------------------------------------------------------------------------
-- [func_val] parses a function, from opening parameters parenthese to
-- "end" keyword included. Used for anonymous functions as well as
-- function declaration statements (both local and global).
--
-- It's wrapped in a [_func_val] eta expansion, so that when expr
-- parser uses the latter, they will notice updates of [func_val]
-- definitions.
--------------------------------------------------------------------------------
func_params_content = gg.list{ name="function parameters",
gg.multisequence{ { "...", builder = "Dots" }, id },
separators = ",", terminators = {")", "|"} }
local _func_params_content = function (lx) return func_params_content(lx) end
func_val = gg.sequence { name="function body",
"(", func_params_content, ")", block, "end", builder = "Function" }
local _func_val = function (lx) return func_val(lx) end
--------------------------------------------------------------------------------
-- Default parser for primary expressions
--------------------------------------------------------------------------------
function id_or_literal (lx)
local a = lx:next()
if a.tag~="Id" and a.tag~="String" and a.tag~="Number" then
local msg
if a.tag=='Eof' then
msg = "End of file reached when an expression was expected"
elseif a.tag=='Keyword' then
msg = "An expression was expected, and `"..a[1]..
"' can't start an expression"
else
msg = "Unexpected expr token " .. _G.table.tostring (a, 'nohash')
end
gg.parse_error (lx, msg)
end
return a
end
--------------------------------------------------------------------------------
-- Builder generator for operators. Wouldn't be worth it if "|x|" notation
-- were allowed, but then lua 5.1 wouldn't compile it
--------------------------------------------------------------------------------
-- opf1 = |op| |_,a| `Op{ op, a }
local function opf1 (op) return
function (_,a) return { tag="Op", op, a } end end
-- opf2 = |op| |a,_,b| `Op{ op, a, b }
local function opf2 (op) return
function (a,_,b) return { tag="Op", op, a, b } end end
-- opf2r = |op| |a,_,b| `Op{ op, b, a } -- (args reversed)
local function opf2r (op) return
function (a,_,b) return { tag="Op", op, b, a } end end
local function op_ne(a, _, b)
-- The first version guarantees to return the same code as Lua,
-- but it relies on the non-standard 'ne' operator, which has been
-- suppressed from the official AST grammar (although still supported
-- in practice by the compiler).
-- return { tag="Op", "ne", a, b }
return { tag="Op", "not", { tag="Op", "eq", a, b, lineinfo= {
first = a.lineinfo.first, last = b.lineinfo.last } } }
end
--------------------------------------------------------------------------------
--
-- complete expression
--
--------------------------------------------------------------------------------
-- FIXME: set line number. In [expr] transformers probably
expr = gg.expr { name = "expression",
primary = gg.multisequence{ name="expr primary",
{ "(", _expr, ")", builder = "Paren" },
{ "function", _func_val, builder = fget(1) },
{ "-{", splice_content, "}", builder = fget(1) },
{ "+{", quote_content, "}", builder = fget(1) },
{ "nil", builder = "Nil" },
{ "true", builder = "True" },
{ "false", builder = "False" },
{ "...", builder = "Dots" },
table,
id_or_literal },
infix = { name="expr infix op",
{ "+", prec = 60, builder = opf2 "add" },
{ "-", prec = 60, builder = opf2 "sub" },
{ "*", prec = 70, builder = opf2 "mul" },
{ "/", prec = 70, builder = opf2 "div" },
{ "%", prec = 70, builder = opf2 "mod" },
{ "^", prec = 90, builder = opf2 "pow", assoc = "right" },
{ "..", prec = 40, builder = opf2 "concat", assoc = "right" },
{ "==", prec = 30, builder = opf2 "eq" },
{ "~=", prec = 30, builder = op_ne },
{ "<", prec = 30, builder = opf2 "lt" },
{ "<=", prec = 30, builder = opf2 "le" },
{ ">", prec = 30, builder = opf2r "lt" },
{ ">=", prec = 30, builder = opf2r "le" },
{ "and",prec = 20, builder = opf2 "and" },
{ "or", prec = 10, builder = opf2 "or" } },
prefix = { name="expr prefix op",
{ "not", prec = 80, builder = opf1 "not" },
{ "#", prec = 80, builder = opf1 "len" },
{ "-", prec = 80, builder = opf1 "unm" } },
suffix = { name="expr suffix op",
{ "[", _expr, "]", builder = function (tab, idx)
return {tag="Index", tab, idx[1]} end},
{ ".", id, builder = function (tab, field)
return {tag="Index", tab, id2string(field[1])} end },
{ "(", func_args_content, ")", builder = function(f, args)
return {tag="Call", f, unpack(args[1])} end },
{ "{", _table_content, "}", builder = function (f, arg)
return {tag="Call", f, arg[1]} end},
{ ":", id, method_args, builder = function (obj, post)
return {tag="Invoke", obj, id2string(post[1]), unpack(post[2])} end},
{ "+{", quote_content, "}", builder = function (f, arg)
return {tag="Call", f, arg[1] } end },
default = { name="opt_string_arg", parse = mlp.opt_string, builder = function(f, arg)
return {tag="Call", f, arg } end } } }

View File

@@ -1,89 +0,0 @@
--------------------------------------------------------------------------------
--
-- Non-Lua syntax extensions
--
--------------------------------------------------------------------------------
module ("mlp", package.seeall)
--------------------------------------------------------------------------------
-- Alebraic Datatypes
--------------------------------------------------------------------------------
local function adt (lx)
local tagval = id (lx) [1]
local tagkey = {tag="Pair", {tag="String", "tag"}, {tag="String", tagval} }
if lx:peek().tag == "String" or lx:peek().tag == "Number" then
return { tag="Table", tagkey, lx:next() }
elseif lx:is_keyword (lx:peek(), "{") then
local x = table (lx)
_G.table.insert (x, 1, tagkey)
return x
else return { tag="Table", tagkey } end
end
expr:add{ "`", adt, builder = fget(1) }
--------------------------------------------------------------------------------
-- Anonymous lambda
--------------------------------------------------------------------------------
local lambda_expr = gg.sequence{
"|", func_params_content, "|", expr,
builder= function (x)
local li = x[2].lineinfo
return { tag="Function", x[1],
{ {tag="Return", x[2], lineinfo=li }, lineinfo=li } }
end }
-- In an earlier version, lambda_expr took an expr_list rather than an expr
-- after the 2nd bar. However, it happened to be much more of a burden than an
-- help, So finally I disabled it. If you want to return several results,
-- use the long syntax.
--------------------------------------------------------------------------------
-- local lambda_expr = gg.sequence{
-- "|", func_params_content, "|", expr_list,
-- builder= function (x)
-- return {tag="Function", x[1], { {tag="Return", unpack(x[2]) } } } end }
expr:add (lambda_expr)
--------------------------------------------------------------------------------
-- Allows to write "a `f` b" instead of "f(a, b)". Taken from Haskell.
-- This is not part of Lua 5.1 syntax, so it's added to the expression
-- afterwards, so that it's easier to disable.
--------------------------------------------------------------------------------
local function expr_in_backquotes (lx) return expr(lx, 35) end
expr.infix:add{ name = "infix function",
"`", expr_in_backquotes, "`", prec = 35, assoc="left",
builder = function(a, op, b) return {tag="Call", op[1], a, b} end }
--------------------------------------------------------------------------------
-- table.override assignment
--------------------------------------------------------------------------------
mlp.lexer:add "<-"
stat.assignments["<-"] = function (a, b)
assert( #a==1 and #b==1, "No multi-args for '<-'")
return { tag="Call", { tag="Index", { tag="Id", "table" },
{ tag="String", "override" } },
a[1], b[1]}
end
--------------------------------------------------------------------------------
-- C-style op+assignments
--------------------------------------------------------------------------------
local function op_assign(kw, op)
local function rhs(a, b)
return { tag="Op", op, a, b }
end
local function f(a,b)
return { tag="Set", a, _G.table.imap(rhs, a, b) }
end
mlp.lexer:add (kw)
mlp.stat.assignments[kw] = f
end
_G.table.iforeach (op_assign,
{"+=", "-=", "*=", "/="},
{"add", "sub", "mul", "div"})

View File

@@ -1,32 +0,0 @@
----------------------------------------------------------------------
-- Metalua: $Id: mll.lua,v 1.3 2006/11/15 09:07:50 fab13n Exp $
--
-- Summary: Source file lexer. ~~Currently only works on strings.
-- Some API refactoring is needed.
--
----------------------------------------------------------------------
--
-- Copyright (c) 2006-2007, Fabien Fleutot <metalua@gmail.com>.
--
-- This software is released under the MIT Licence, see licence.txt
-- for details.
--
----------------------------------------------------------------------
module ("mlp", package.seeall)
require "lexer"
local mlp_lexer = lexer.lexer:clone()
local keywords = {
"and", "break", "do", "else", "elseif",
"end", "false", "for", "function", "if",
"in", "local", "nil", "not", "or", "repeat",
"return", "then", "true", "until", "while",
"...", "..", "==", ">=", "<=", "~=",
"+{", "-{" }
for _,w in pairs(keywords) do mlp_lexer:add(w) end -- PK 6/4/2012
_M.lexer = mlp_lexer

View File

@@ -1,118 +0,0 @@
----------------------------------------------------------------------
-- Metalua: $Id: mlp_meta.lua,v 1.4 2006/11/15 09:07:50 fab13n Exp $
--
-- Summary: Meta-operations: AST quasi-quoting and splicing
--
----------------------------------------------------------------------
--
-- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
--
-- This software is released under the MIT Licence, see licence.txt
-- for details.
--
----------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Exported API:
-- * [mlp.splice_content()]
-- * [mlp.quote_content()]
--
--------------------------------------------------------------------------------
module ("mlp", package.seeall)
--------------------------------------------------------------------------------
-- External splicing: compile an AST into a chunk, load and evaluate
-- that chunk, and replace the chunk by its result (which must also be
-- an AST).
--------------------------------------------------------------------------------
function splice (ast)
local f = mlc.function_of_ast(ast, '=splice')
local result=f()
return result
end
--------------------------------------------------------------------------------
-- Going from an AST to an AST representing that AST
-- the only key being lifted in this version is ["tag"]
--------------------------------------------------------------------------------
function quote (t)
--print("QUOTING:", _G.table.tostring(t, 60))
local cases = { }
function cases.table (t)
local mt = { tag = "Table" }
--_G.table.insert (mt, { tag = "Pair", quote "quote", { tag = "True" } })
if t.tag == "Splice" then
assert (#t==1, "Invalid splice")
local sp = t[1]
return sp
elseif t.tag then
_G.table.insert (mt, { tag = "Pair", quote "tag", quote (t.tag) })
end
for _, v in ipairs (t) do
_G.table.insert (mt, quote(v))
end
return mt
end
function cases.number (t) return { tag = "Number", t, quote = true } end
function cases.string (t) return { tag = "String", t, quote = true } end
return cases [ type (t) ] (t)
end
--------------------------------------------------------------------------------
-- when this variable is false, code inside [-{...}] is compiled and
-- avaluated immediately. When it's true (supposedly when we're
-- parsing data inside a quasiquote), [-{foo}] is replaced by
-- [`Splice{foo}], which will be unpacked by [quote()].
--------------------------------------------------------------------------------
in_a_quote = false
--------------------------------------------------------------------------------
-- Parse the inside of a "-{ ... }"
--------------------------------------------------------------------------------
function splice_content (lx)
local parser_name = "expr"
if lx:is_keyword (lx:peek(2), ":") then
local a = lx:next()
lx:next() -- skip ":"
assert (a.tag=="Id", "Invalid splice parser name")
parser_name = a[1]
end
local ast = mlp[parser_name](lx)
if in_a_quote then
--printf("SPLICE_IN_QUOTE:\n%s", _G.table.tostring(ast, "nohash", 60))
return { tag="Splice", ast }
else
if parser_name == "expr" then ast = { { tag="Return", ast } }
elseif parser_name == "stat" then ast = { ast }
elseif parser_name ~= "block" then
error ("splice content must be an expr, stat or block") end
--printf("EXEC THIS SPLICE:\n%s", _G.table.tostring(ast, "nohash", 60))
return splice (ast)
end
end
--------------------------------------------------------------------------------
-- Parse the inside of a "+{ ... }"
--------------------------------------------------------------------------------
function quote_content (lx)
local parser
if lx:is_keyword (lx:peek(2), ":") then -- +{parser: content }
parser = mlp[id(lx)[1]]
lx:next()
else -- +{ content }
parser = mlp.expr
end
local prev_iq = in_a_quote
in_a_quote = true
--print("IN_A_QUOTE")
local content = parser (lx)
local q_content = quote (content)
in_a_quote = prev_iq
return q_content
end

View File

@@ -1,185 +0,0 @@
----------------------------------------------------------------------
-- Metalua: $Id: mlp_misc.lua,v 1.6 2006/11/15 09:07:50 fab13n Exp $
--
-- Summary: metalua parser, miscellaneous utility functions.
--
----------------------------------------------------------------------
--
-- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
--
-- This software is released under the MIT Licence, see licence.txt
-- for details.
--
----------------------------------------------------------------------
-- History:
-- $Log: mlp_misc.lua,v $
-- Revision 1.6 2006/11/15 09:07:50 fab13n
-- debugged meta operators.
-- Added command line options handling.
--
-- Revision 1.5 2006/11/10 02:11:17 fab13n
-- compiler faithfulness to 5.1 improved
-- gg.expr extended
-- mlp.expr refactored
--
-- Revision 1.4 2006/11/09 09:39:57 fab13n
-- some cleanup
--
-- Revision 1.3 2006/11/07 04:38:00 fab13n
-- first bootstrapping version.
--
-- Revision 1.2 2006/11/05 15:08:34 fab13n
-- updated code generation, to be compliant with 5.1
--
----------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Exported API:
-- * [mlp.fget()]
-- * [mlp.id()]
-- * [mlp.opt_id()]
-- * [mlp.id_list()]
-- * [mlp.gensym()]
-- * [mlp.string()]
-- * [mlp.opt_string()]
-- * [mlp.id2string()]
--
--------------------------------------------------------------------------------
--require "gg"
--require "mll"
module ("mlp", package.seeall)
--------------------------------------------------------------------------------
-- returns a function that takes the [n]th element of a table.
-- if [tag] is provided, then this element is expected to be a
-- table, and this table receives a "tag" field whose value is
-- set to [tag].
--
-- The primary purpose of this is to generate builders for
-- grammar generators. It has little purpose in metalua, as lambda has
-- a lightweight syntax.
--------------------------------------------------------------------------------
function fget (n, tag)
assert (type (n) == "number")
if tag then
assert (type (tag) == "string")
return function (x)
assert (type (x[n]) == "table")
return {tag=tag, unpack(x[n])} end
else
return function (x) return x[n] end
end
end
--------------------------------------------------------------------------------
-- Try to read an identifier (possibly as a splice), or return [false] if no
-- id is found.
--------------------------------------------------------------------------------
function opt_id (lx)
local a = lx:peek();
if lx:is_keyword (a, "-{") then
local v = gg.sequence{ "-{", splice_content, "}" } (lx) [1]
if v.tag ~= "Id" and v.tag ~= "Splice" then
gg.parse_error(lx,"Bad id splice")
end
return v
elseif a.tag == "Id" then return lx:next()
else return false end
end
--------------------------------------------------------------------------------
-- Mandatory reading of an id: causes an error if it can't read one.
--------------------------------------------------------------------------------
function id (lx)
return opt_id (lx) or gg.parse_error(lx,"Identifier expected")
end
--------------------------------------------------------------------------------
-- Common helper function
--------------------------------------------------------------------------------
id_list = gg.list { primary = mlp.id, separators = "," }
--------------------------------------------------------------------------------
-- Symbol generator: [gensym()] returns a guaranteed-to-be-unique identifier.
-- The main purpose is to avoid variable capture in macros.
--
-- If a string is passed as an argument, theis string will be part of the
-- id name (helpful for macro debugging)
--------------------------------------------------------------------------------
local gensymidx = 0
function gensym (arg)
gensymidx = gensymidx + 1
return { tag="Id", _G.string.format(".%i.%s", gensymidx, arg or "")}
end
--------------------------------------------------------------------------------
-- Converts an identifier into a string. Hopefully one day it'll handle
-- splices gracefully, but that proves quite tricky.
--------------------------------------------------------------------------------
function id2string (id)
--print("id2string:", disp.ast(id))
if id.tag == "Id" then id.tag = "String"; return id
elseif id.tag == "Splice" then
assert (in_a_quote, "can't do id2string on an outermost splice")
error ("id2string on splice not implemented")
-- Evaluating id[1] will produce `Id{ xxx },
-- and we want it to produce `String{ xxx }
-- Morally, this is what I want:
-- return `String{ `Index{ `Splice{ id[1] }, `Number 1 } }
-- That is, without sugar:
return {tag="String", {tag="Index", {tag="Splice", id[1] },
{tag="Number", 1 } } }
else error ("Identifier expected: ".._G.table.tostring(id, 'nohash')) end
end
--------------------------------------------------------------------------------
-- Read a string, possibly spliced, or return an error if it can't
--------------------------------------------------------------------------------
function string (lx)
local a = lx:peek()
if lx:is_keyword (a, "-{") then
local v = gg.sequence{ "-{", splice_content, "}" } (lx) [1]
if v.tag ~= "" and v.tag ~= "Splice" then
gg.parse_error(lx,"Bad string splice")
end
return v
elseif a.tag == "String" then return lx:next()
else error "String expected" end
end
--------------------------------------------------------------------------------
-- Try to read a string, or return false if it can't. No splice allowed.
--------------------------------------------------------------------------------
function opt_string (lx)
return lx:peek().tag == "String" and lx:next()
end
--------------------------------------------------------------------------------
-- Chunk reader: block + Eof
--------------------------------------------------------------------------------
function skip_initial_sharp_comment (lx)
-- Dirty hack: I'm happily fondling lexer's private parts
-- FIXME: redundant with lexer:newstream()
lx :sync()
local i = lx.src:match ("^#.-\n()", lx.i)
if i then lx.i, lx.column_offset, lx.line = i, i, lx.line+1 end
end
local function _chunk (lx)
if lx:peek().tag == 'Eof' then return { } -- handle empty files
else
skip_initial_sharp_comment (lx)
local chunk = block (lx)
if lx:peek().tag ~= "Eof" then error "End-of-file expected" end
return chunk
end
end
-- chunk is wrapped in a sequence so that it has a "transformer" field.
chunk = gg.sequence { _chunk, builder = unpack }

View File

@@ -1,226 +0,0 @@
----------------------------------------------------------------------
-- Metalua: $Id: mlp_stat.lua,v 1.7 2006/11/15 09:07:50 fab13n Exp $
--
-- Summary: metalua parser, statement/block parser. This is part of
-- the definition of module [mlp].
--
----------------------------------------------------------------------
--
-- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
--
-- This software is released under the MIT Licence, see licence.txt
-- for details.
--
----------------------------------------------------------------------
--
----------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Exports API:
-- * [mlp.stat()]
-- * [mlp.block()]
-- * [mlp.for_header()]
--
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- eta-expansions to break circular dependency
--------------------------------------------------------------------------------
local expr = function (lx) return mlp.expr (lx) end
local func_val = function (lx) return mlp.func_val (lx) end
local expr_list = function (lx) return mlp.expr_list(lx) end
module ("mlp", package.seeall)
--------------------------------------------------------------------------------
-- List of all keywords that indicate the end of a statement block. Users are
-- likely to extend this list when designing extensions.
--------------------------------------------------------------------------------
local block_terminators = { "else", "elseif", "end", "until", ")", "}", "]" }
-- FIXME: this must be handled from within GG!!!
function block_terminators:add(x)
if type (x) == "table" then for _, y in ipairs(x) do self:add (y) end
else _G.table.insert (self, x) end
end
--------------------------------------------------------------------------------
-- list of statements, possibly followed by semicolons
--------------------------------------------------------------------------------
block = gg.list {
name = "statements block",
terminators = block_terminators,
primary = function (lx)
-- FIXME use gg.optkeyword()
local x = stat (lx)
if lx:is_keyword (lx:peek(), ";") then lx:next() end
return x
end }
--------------------------------------------------------------------------------
-- Helper function for "return <expr_list>" parsing.
-- Called when parsing return statements.
-- The specific test for initial ";" is because it's not a block terminator,
-- so without itgg.list would choke on "return ;" statements.
-- We don't make a modified copy of block_terminators because this list
-- is sometimes modified at runtime, and the return parser would get out of
-- sync if it was relying on a copy.
--------------------------------------------------------------------------------
local return_expr_list_parser = gg.multisequence{
{ ";" , builder = function() return { } end },
default = gg.list {
expr, separators = ",", terminators = block_terminators } }
--------------------------------------------------------------------------------
-- for header, between [for] and [do] (exclusive).
-- Return the `Forxxx{...} AST, without the body element (the last one).
--------------------------------------------------------------------------------
function for_header (lx)
local var = mlp.id (lx)
if lx:is_keyword (lx:peek(), "=") then
-- Fornum: only 1 variable
lx:next() -- skip "="
local e = expr_list (lx)
assert (2 <= #e and #e <= 3, "2 or 3 values in a fornum")
return { tag="Fornum", var, unpack (e) }
else
-- Forin: there might be several vars
local a = lx:is_keyword (lx:next(), ",", "in")
if a=="in" then var_list = { var, lineinfo = var.lineinfo } else
-- several vars; first "," skipped, read other vars
var_list = gg.list{
primary = id, separators = ",", terminators = "in" } (lx)
_G.table.insert (var_list, 1, var) -- put back the first variable
lx:next() -- skip "in"
end
local e = expr_list (lx)
return { tag="Forin", var_list, e }
end
end
--------------------------------------------------------------------------------
-- Function def parser helper: id ( . id ) *
--------------------------------------------------------------------------------
local function fn_builder (list)
local r = list[1]
for i = 2, #list do r = { tag="Index", r, id2string(list[i]) } end
return r
end
local func_name = gg.list{ id, separators = ".", builder = fn_builder }
--------------------------------------------------------------------------------
-- Function def parser helper: ( : id )?
--------------------------------------------------------------------------------
local method_name = gg.onkeyword{ name = "method invocation", ":", id,
transformers = { function(x) return x and id2string(x) end } }
--------------------------------------------------------------------------------
-- Function def builder
--------------------------------------------------------------------------------
local function funcdef_builder(x)
local name, method, func = x[1], x[2], x[3]
if method then
name = { tag="Index", name, method, lineinfo = {
first = name.lineinfo.first,
last = method.lineinfo.last } }
_G.table.insert (func[1], 1, {tag="Id", "self"})
end
local r = { tag="Set", {name}, {func} }
r[1].lineinfo = name.lineinfo
r[2].lineinfo = func.lineinfo
return r
end
--------------------------------------------------------------------------------
-- if statement builder
--------------------------------------------------------------------------------
local function if_builder (x)
local cb_pairs, else_block, r = x[1], x[2], {tag="If"}
for i=1,#cb_pairs do r[2*i-1]=cb_pairs[i][1]; r[2*i]=cb_pairs[i][2] end
if else_block then r[#r+1] = else_block end
return r
end
--------------------------------------------------------------------------------
-- produce a list of (expr,block) pairs
--------------------------------------------------------------------------------
local elseifs_parser = gg.list {
gg.sequence { expr, "then", block },
separators = "elseif",
terminators = { "else", "end" } }
--------------------------------------------------------------------------------
-- assignments and calls: statements that don't start with a keyword
--------------------------------------------------------------------------------
local function assign_or_call_stat_parser (lx)
local e = expr_list (lx)
local a = lx:is_keyword(lx:peek())
local op = a and stat.assignments[a]
if op then
--FIXME: check that [e] is a LHS
lx:next()
local v = expr_list (lx)
if type(op)=="string" then return { tag=op, e, v }
else return op (e, v) end
else
assert (#e > 0)
if #e > 1 then
gg.parse_error (lx,
"comma is not a valid statement separator; statement can be "..
"separated by semicolons, or not separated at all") end
if e[1].tag ~= "Call" and e[1].tag ~= "Invoke" then
local typename
if e[1].tag == 'Id' then
typename = '("'..e[1][1]..'") is an identifier'
elseif e[1].tag == 'Op' then
typename = "is an arithmetic operation"
else typename = "is of type '"..(e[1].tag or "<list>").."'" end
gg.parse_error (lx, "This expression " .. typename ..
"; a statement was expected, and only function and method call "..
"expressions can be used as statements");
end
return e[1]
end
end
local_stat_parser = gg.multisequence{
-- local function <name> <func_val>
{ "function", id, func_val, builder =
function(x)
local vars = { x[1], lineinfo = x[1].lineinfo }
local vals = { x[2], lineinfo = x[2].lineinfo }
return { tag="Localrec", vars, vals }
end },
-- local <id_list> ( = <expr_list> )?
default = gg.sequence{ id_list, gg.onkeyword{ "=", expr_list },
builder = function(x) return {tag="Local", x[1], x[2] or { } } end } }
--------------------------------------------------------------------------------
-- statement
--------------------------------------------------------------------------------
stat = gg.multisequence {
name="statement",
{ "do", block, "end", builder =
function (x) return { tag="Do", unpack (x[1]) } end },
{ "for", for_header, "do", block, "end", builder =
function (x) x[1][#x[1]+1] = x[2]; return x[1] end },
{ "function", func_name, method_name, func_val, builder=funcdef_builder },
{ "while", expr, "do", block, "end", builder = "While" },
{ "repeat", block, "until", expr, builder = "Repeat" },
{ "local", local_stat_parser, builder = fget (1) },
{ "return", return_expr_list_parser, builder = fget (1, "Return") },
{ "break", builder = function() return { tag="Break" } end },
{ "-{", splice_content, "}", builder = fget(1) },
{ "if", elseifs_parser, gg.onkeyword{ "else", block }, "end",
builder = if_builder },
default = assign_or_call_stat_parser }
stat.assignments = {
["="] = "Set" }
function stat.assignments:add(k, v) self[k] = v end

View File

@@ -1,92 +0,0 @@
----------------------------------------------------------------------
-- Metalua: $Id: mlp_table.lua,v 1.5 2006/11/10 02:11:17 fab13n Exp $
--
-- Summary: metalua parser, table constructor parser. This is part
-- of thedefinition of module [mlp].
--
----------------------------------------------------------------------
--
-- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
--
-- This software is released under the MIT Licence, see licence.txt
-- for details.
--
----------------------------------------------------------------------
-- History:
-- $Log: mlp_table.lua,v $
-- Revision 1.5 2006/11/10 02:11:17 fab13n
-- compiler faithfulness to 5.1 improved
-- gg.expr extended
-- mlp.expr refactored
--
-- Revision 1.4 2006/11/09 09:39:57 fab13n
-- some cleanup
--
-- Revision 1.3 2006/11/07 04:38:00 fab13n
-- first bootstrapping version.
--
-- Revision 1.2 2006/11/05 15:08:34 fab13n
-- updated code generation, to be compliant with 5.1
--
----------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Exported API:
-- * [mlp.table_field()]
-- * [mlp.table_content()]
-- * [mlp.table()]
--
-- KNOWN BUG: doesn't handle final ";" or "," before final "}"
--
--------------------------------------------------------------------------------
--require "gg"
--require "mll"
--require "mlp_misc"
module ("mlp", package.seeall)
--------------------------------------------------------------------------------
-- eta expansion to break circular dependencies:
--------------------------------------------------------------------------------
local function _expr (lx) return expr(lx) end
--------------------------------------------------------------------------------
-- [[key] = value] table field definition
--------------------------------------------------------------------------------
local bracket_field = gg.sequence{ "[", _expr, "]", "=", _expr, builder = "Pair" }
--------------------------------------------------------------------------------
-- [id = value] or [value] table field definition;
-- [[key]=val] are delegated to [bracket_field()]
--------------------------------------------------------------------------------
function table_field (lx)
if lx:is_keyword (lx:peek(), "[") then return bracket_field (lx) end
local e = _expr (lx)
if lx:is_keyword (lx:peek(), "=") then
lx:next(); -- skip the "="
local key = id2string(e)
local val = _expr(lx)
local r = { tag="Pair", key, val }
r.lineinfo = { first = key.lineinfo.first, last = val.lineinfo.last }
return r
else return e end
end
local function _table_field(lx) return table_field(lx) end
--------------------------------------------------------------------------------
-- table constructor, without enclosing braces; returns a full table object
--------------------------------------------------------------------------------
table_content = gg.list { _table_field,
separators = { ",", ";" }, terminators = "}", builder = "Table" }
local function _table_content(lx) return table_content(lx) end
--------------------------------------------------------------------------------
-- complete table constructor including [{...}]
--------------------------------------------------------------------------------
table = gg.sequence{ "{", _table_content, "}", builder = fget(1) }

295
lualibs/metalua/pprint.lua Normal file
View File

@@ -0,0 +1,295 @@
-------------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
----------------------------------------------------------------------
----------------------------------------------------------------------
----------------------------------------------------------------------
--
-- Lua objects pretty-printer
--
----------------------------------------------------------------------
----------------------------------------------------------------------
local M = { }
M.DEFAULT_CFG = {
hide_hash = false; -- Print the non-array part of tables?
metalua_tag = true; -- Use Metalua's backtick syntax sugar?
fix_indent = nil; -- If a number, number of indentation spaces;
-- If false, indent to the previous brace.
line_max = nil; -- If a number, tries to avoid making lines with
-- more than this number of chars.
initial_indent = 0; -- If a number, starts at this level of indentation
keywords = { }; -- Set of keywords which must not use Lua's field
-- shortcuts {["foo"]=...} -> {foo=...}
}
local function valid_id(cfg, x)
if type(x) ~= "string" then return false end
if not x:match "^[a-zA-Z_][a-zA-Z0-9_]*$" then return false end
if cfg.keywords and cfg.keywords[x] then return false end
return true
end
local __tostring_cache = setmetatable({ }, {__mode='k'})
-- Retrieve the string produced by `__tostring` metamethod if present,
-- return `false` otherwise. Cached in `__tostring_cache`.
local function __tostring(x)
local the_string = __tostring_cache[x]
if the_string~=nil then return the_string end
local mt = getmetatable(x)
if mt then
local __tostring = mt.__tostring
if __tostring then
the_string = __tostring(x)
__tostring_cache[x] = the_string
return the_string
end
end
if x~=nil then __tostring_cache[x] = false end -- nil is an illegal key
return false
end
local xlen -- mutually recursive with `xlen_type`
local xlen_cache = setmetatable({ }, {__mode='k'})
-- Helpers for the `xlen` function
local xlen_type = {
["nil"] = function ( ) return 3 end;
number = function (x) return #tostring(x) end;
boolean = function (x) return x and 4 or 5 end;
string = function (x) return #string.format("%q",x) end;
}
function xlen_type.table (adt, cfg, nested)
local custom_string = __tostring(adt)
if custom_string then return #custom_string end
-- Circular referenced objects are printed with the plain
-- `tostring` function in nested positions.
if nested [adt] then return #tostring(adt) end
nested [adt] = true
local has_tag = cfg.metalua_tag and valid_id(cfg, adt.tag)
local alen = #adt
local has_arr = alen>0
local has_hash = false
local x = 0
if not cfg.hide_hash then
-- first pass: count hash-part
for k, v in pairs(adt) do
if k=="tag" and has_tag then
-- this is the tag -> do nothing!
elseif type(k)=="number" and k<=alen and math.fmod(k,1)==0 and k>0 then
-- array-part pair -> do nothing!
else
has_hash = true
if valid_id(cfg, k) then x=x+#k
else x = x + xlen (k, cfg, nested) + 2 end -- count surrounding brackets
x = x + xlen (v, cfg, nested) + 5 -- count " = " and ", "
end
end
end
for i = 1, alen do x = x + xlen (adt[i], nested) + 2 end -- count ", "
nested[adt] = false -- No more nested calls
if not (has_tag or has_arr or has_hash) then return 3 end
if has_tag then x=x+#adt.tag+1 end
if not (has_arr or has_hash) then return x end
if not has_hash and alen==1 and type(adt[1])~="table" then
return x-2 -- substract extraneous ", "
end
return x+2 -- count "{ " and " }", substract extraneous ", "
end
-- Compute the number of chars it would require to display the table
-- on a single line. Helps to decide whether some carriage returns are
-- required. Since the size of each sub-table is required many times,
-- it's cached in [xlen_cache].
xlen = function (x, cfg, nested)
-- no need to compute length for 1-line prints
if not cfg.line_max then return 0 end
nested = nested or { }
if x==nil then return #"nil" end
local len = xlen_cache[x]
if len then return len end
local f = xlen_type[type(x)]
if not f then return #tostring(x) end
len = f (x, cfg, nested)
xlen_cache[x] = len
return len
end
local function consider_newline(p, len)
if not p.cfg.line_max then return end
if p.current_offset + len <= p.cfg.line_max then return end
if p.indent < p.current_offset then
p:acc "\n"; p:acc ((" "):rep(p.indent))
p.current_offset = p.indent
end
end
local acc_value
local acc_type = {
["nil"] = function(p) p:acc("nil") end;
number = function(p, adt) p:acc (tostring (adt)) end;
string = function(p, adt) p:acc ((string.format ("%q", adt):gsub("\\\n", "\\n"))) end;
boolean = function(p, adt) p:acc (adt and "true" or "false") end }
-- Indentation:
-- * if `cfg.fix_indent` is set to a number:
-- * add this number of space for each level of depth
-- * return to the line as soon as it flushes things further left
-- * if not, tabulate to one space after the opening brace.
-- * as a result, it never saves right-space to return before first element
function acc_type.table(p, adt)
if p.nested[adt] then p:acc(tostring(adt)); return end
p.nested[adt] = true
local has_tag = p.cfg.metalua_tag and valid_id(p.cfg, adt.tag)
local alen = #adt
local has_arr = alen>0
local has_hash = false
local previous_indent = p.indent
if has_tag then p:acc("`"); p:acc(adt.tag) end
local function indent(p)
if not p.cfg.fix_indent then p.indent = p.current_offset
else p.indent = p.indent + p.cfg.fix_indent end
end
-- First pass: handle hash-part
if not p.cfg.hide_hash then
for k, v in pairs(adt) do
if has_tag and k=='tag' then -- pass the 'tag' field
elseif type(k)=="number" and k<=alen and k>0 and math.fmod(k,1)==0 then
-- pass array-part keys (consecutive ints less than `#adt`)
else -- hash-part keys
if has_hash then p:acc ", " else -- 1st hash-part pair ever found
p:acc "{ "; indent(p)
end
-- Determine whether a newline is required
local is_id, expected_len=valid_id(p.cfg, k)
if is_id then expected_len=#k+xlen(v, p.cfg, p.nested)+#" = , "
else expected_len = xlen(k, p.cfg, p.nested)+xlen(v, p.cfg, p.nested)+#"[] = , " end
consider_newline(p, expected_len)
-- Print the key
if is_id then p:acc(k); p:acc " = " else
p:acc "["; acc_value (p, k); p:acc "] = "
end
acc_value (p, v) -- Print the value
has_hash = true
end
end
end
-- Now we know whether there's a hash-part, an array-part, and a tag.
-- Tag and hash-part are already printed if they're present.
if not has_tag and not has_hash and not has_arr then p:acc "{ }";
elseif has_tag and not has_hash and not has_arr then -- nothing, tag already in acc
else
assert (has_hash or has_arr) -- special case { } already handled
local no_brace = false
if has_hash and has_arr then p:acc ", "
elseif has_tag and not has_hash and alen==1 and type(adt[1])~="table" then
-- No brace required; don't print "{", remember not to print "}"
p:acc (" "); acc_value (p, adt[1]) -- indent= indent+(cfg.fix_indent or 0))
no_brace = true
elseif not has_hash then
-- Braces required, but not opened by hash-part handler yet
p:acc "{ "; indent(p)
end
-- 2nd pass: array-part
if not no_brace and has_arr then
local expected_len = xlen(adt[1], p.cfg, p.nested)
consider_newline(p, expected_len)
acc_value(p, adt[1]) -- indent+(cfg.fix_indent or 0)
for i=2, alen do
p:acc ", ";
consider_newline(p, xlen(adt[i], p.cfg, p.nested))
acc_value (p, adt[i]) --indent+(cfg.fix_indent or 0)
end
end
if not no_brace then p:acc " }" end
end
p.nested[adt] = false -- No more nested calls
p.indent = previous_indent
end
function acc_value(p, v)
local custom_string = __tostring(v)
if custom_string then p:acc(custom_string) else
local f = acc_type[type(v)]
if f then f(p, v) else p:acc(tostring(v)) end
end
end
-- FIXME: new_indent seems to be always nil?!s detection
-- FIXME: accumulator function should be configurable,
-- so that print() doesn't need to bufferize the whole string
-- before starting to print.
function M.tostring(t, cfg)
cfg = cfg or M.DEFAULT_CFG or { }
local p = {
cfg = cfg;
indent = 0;
current_offset = cfg.initial_indent or 0;
buffer = { };
nested = { };
acc = function(self, str)
table.insert(self.buffer, str)
self.current_offset = self.current_offset + #str
end;
}
acc_value(p, t)
return table.concat(p.buffer)
end
function M.print(...) return print(M.tostring(...)) end
function M.sprintf(fmt, ...)
local args={...}
for i, v in pairs(args) do
local t=type(v)
if t=='table' then args[i]=M.tostring(v)
elseif t=='nil' then args[i]='nil' end
end
return string.format(fmt, unpack(args))
end
function M.printf(...) print(M.sprintf(...)) end
return M

View File

@@ -1,5 +1,5 @@
--
-- MobDebug 0.5511
-- MobDebug 0.60
-- Copyright 2011-14 Paul Kulchenko
-- Based on RemDebug 1.0 Copyright Kepler Project 2005
--
@@ -18,10 +18,10 @@ end)("os")
local mobdebug = {
_NAME = "mobdebug",
_VERSION = 0.5511,
_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,
port = os and os.getenv and tonumber((os.getenv("MOBDEBUG_PORT"))) or 8172,
checkcount = 200,
yieldtimeout = 0.02,
}
@@ -47,6 +47,19 @@ local genv = _G or _ENV
local jit = rawget(genv, "jit")
local MOAICoroutine = rawget(genv, "MOAICoroutine")
-- ngx_lua debugging requires a special handling as its coroutine.*
-- methods use a different mechanism that doesn't allow resume calls
-- from debug hook handlers.
-- Instead, the "original" coroutine.* methods are used.
-- `rawget` needs to be used to protect against `strict` checks, but
-- ngx_lua hides those in a metatable, so need to use that.
local metagindex = getmetatable(genv) and getmetatable(genv).__index
local ngx = type(metagindex) == "table" and metagindex.rawget and metagindex:rawget("ngx") or nil
local corocreate = ngx and coroutine._create or coroutine.create
local cororesume = ngx and coroutine._resume or coroutine.resume
local coroyield = ngx and coroutine._yield or coroutine.yield
local corostatus = ngx and coroutine._status or coroutine.status
if not setfenv then -- Lua 5.2
-- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html
-- this assumes f is a function
@@ -245,6 +258,8 @@ end)() ---- end of Serpent module
mobdebug.line = serpent.line
mobdebug.dump = serpent.dump
mobdebug.linemap = nil
mobdebug.loadstring = loadstring
local function removebasedir(path, basedir)
if iscasepreserving then
@@ -280,6 +295,7 @@ local function stack(start)
end
local stack = {}
local linemap = mobdebug.linemap
for i = (start or 0), 100 do
local source = debug.getinfo(i, "Snl")
if not source then break end
@@ -291,8 +307,10 @@ local function stack(start)
end
table.insert(stack, { -- remove basedir from source
{source.name, removebasedir(src, basedir), source.linedefined,
source.currentline, source.what, source.namewhat, source.short_src},
{source.name, removebasedir(src, basedir),
linemap and linemap(source.linedefined, source.source) or source.linedefined,
linemap and linemap(source.currentline, source.source) or source.currentline,
source.what, source.namewhat, source.short_src},
vars(i+1)})
if source.what == 'main' then break end
end
@@ -429,6 +447,46 @@ local function is_pending(peer)
return buf
end
local function readnext(peer, num)
peer:settimeout(0) -- non-blocking
local res, err, partial = peer:receive(num)
peer:settimeout() -- back to blocking
return res or partial or '', err
end
local function handle_breakpoint(peer)
-- check if the buffer has the beginning of SETB/DELB command;
-- this is to avoid reading the entire line for commands that
-- don't need to be handled here.
if not buf or not (buf:sub(1,1) == 'S' or buf:sub(1,1) == 'D') then return end
-- check second character to avoid reading STEP or other S* and D* commands
if #buf == 1 then buf = buf .. readnext(peer, 1) end
if buf:sub(2,2) ~= 'E' then return end
-- need to read few more characters
buf = buf .. readnext(peer, 5-#buf)
if buf ~= 'SETB ' and buf ~= 'DELB ' then return end
local res, _, partial = peer:receive() -- get the rest of the line; blocking
if not res then
if partial then buf = buf .. partial end
return
end
local _, _, cmd, file, line = (buf..res):find("^([A-Z]+)%s+(.-)%s+(%d+)%s*$")
if cmd == 'SETB' then set_breakpoint(file, tonumber(line))
elseif cmd == 'DELB' then remove_breakpoint(file, tonumber(line))
else
-- this looks like a breakpoint command, but something went wrong;
-- return here to let the "normal" processing to handle,
-- although this is likely to not go well.
return
end
buf = nil
end
local function debug_hook(event, line)
-- (1) LuaJIT needs special treatment. Because debug_hook is set for
-- *all* coroutines, and not just the one being debugged as in regular Lua
@@ -464,6 +522,12 @@ local function debug_hook(event, line)
elseif event == "return" or event == "tail return" then
stack_level = stack_level - 1
elseif event == "line" then
if mobdebug.linemap then
local ok, mappedline = pcall(mobdebug.linemap, line, debug.getinfo(2, "S").source)
if ok then line = mappedline end
if not line then return end
end
-- may need to fall through because of the following:
-- (1) step_into
-- (2) step_over and stack_level <= step_level (need stack_level)
@@ -509,14 +573,19 @@ local function debug_hook(event, line)
-- set on foo.lua will not work if not converted to the same case.
if iscasepreserving then file = string.lower(file) end
if file:find("%./") == 1 then file = file:sub(3)
else file = file:gsub('^'..q(basedir), '') end
else file = file:gsub("^"..q(basedir), "") end
-- some file systems allow newlines in file names; remove these.
file = file:gsub("\n", ' ')
else
-- this is either a file name coming from loadstring("chunk", "file"),
-- or the actual source code that needs to be serialized (as it may
-- include newlines); assume it's a file name if it's all on one line.
file = file:find("[\r\n]") and mobdebug.line(file) or file
if file:find("[\r\n]") then
file = mobdebug.line(file)
else
if iscasepreserving then file = string.lower(file) end
file = file:gsub("\\", "/"):gsub(file:find("^%./") and "^%./" or "^"..q(basedir), "")
end
end
-- set to true if we got here; this only needs to be done once per
@@ -525,6 +594,8 @@ local function debug_hook(event, line)
lastfile = file
end
if is_pending(server) then handle_breakpoint(server) end
local vars, status, res
if (watchescnt > 0) then
vars = capture_vars()
@@ -532,7 +603,7 @@ local function debug_hook(event, line)
setfenv(value, vars)
local ok, fired = pcall(value)
if ok and fired then
status, res = coroutine.resume(coro_debugger, events.WATCH, vars, file, line, index)
status, res = cororesume(coro_debugger, events.WATCH, vars, file, line, index)
break -- any one watch is enough; don't check multiple times
end
end
@@ -552,7 +623,7 @@ local function debug_hook(event, line)
vars = vars or capture_vars()
step_into = false
step_over = false
status, res = coroutine.resume(coro_debugger, events.BREAK, vars, file, line)
status, res = cororesume(coro_debugger, events.BREAK, vars, file, line)
end
-- handle 'stack' command that provides stack() information to the debugger
@@ -561,8 +632,8 @@ local function debug_hook(event, line)
-- resume with the stack trace and variables
if vars then restore_vars(vars) end -- restore vars so they are reflected in stack values
-- this may fail if __tostring method fails at run-time
local ok, snapshot = pcall(stack, 4)
status, res = coroutine.resume(coro_debugger, ok and events.STACK or events.BREAK, snapshot, file, line)
local ok, snapshot = pcall(stack, ngx and 5 or 4)
status, res = cororesume(coro_debugger, ok and events.STACK or events.BREAK, snapshot, file, line)
end
end
@@ -599,6 +670,30 @@ local function stringify_results(status, ...)
return pcall(mobdebug.dump, t, {sparse = false})
end
local function isrunning()
return coro_debugger and corostatus(coro_debugger) == 'suspended'
end
-- this is a function that removes all hooks and closes the socket to
-- report back to the controller that the debugging is done.
-- the script that called `done` can still continue.
local function done()
if not (isrunning() and server) then return end
if not jit then
for co, debugged in pairs(coroutines) do
if debugged then debug.sethook(co) end
end
end
debug.sethook()
server:close()
coro_debugger = nil -- to make sure isrunning() returns `false`
seen_hook = nil -- to make sure that the next start() call works
abort = nil -- to make sure that callback calls use proper "abort" value
end
local function debugger_loop(sev, svars, sfile, sline)
local command
local app, osname
@@ -664,7 +759,7 @@ local function debugger_loop(sev, svars, sfile, sline)
elseif command == "EXEC" then
local _, _, chunk = string.find(line, "^[A-Z]+%s+(.+)$")
if chunk then
local func, res = loadstring(chunk)
local func, res = mobdebug.loadstring(chunk)
local status
if func then
setfenv(func, eval_env)
@@ -700,16 +795,16 @@ local function debugger_loop(sev, svars, sfile, sline)
if size == 0 and name == '-' then -- RELOAD the current script being debugged
server:send("200 OK 0\n")
coroutine.yield("load")
coroyield("load")
else
-- receiving 0 bytes blocks (at least in luasocket 2.0.2), so skip reading
local chunk = size == 0 and "" or server:receive(size)
if chunk then -- LOAD a new script for debugging
local func, res = loadstring(chunk, "@"..name)
local func, res = mobdebug.loadstring(chunk, "@"..name)
if func then
server:send("200 OK 0\n")
debugee = func
coroutine.yield("load")
coroyield("load")
else
server:send("401 Error in Expression " .. #res .. "\n")
server:send(res)
@@ -722,7 +817,7 @@ local function debugger_loop(sev, svars, sfile, sline)
elseif command == "SETW" then
local _, _, exp = string.find(line, "^[A-Z]+%s+(.+)%s*$")
if exp then
local func, res = loadstring("return(" .. exp .. ")")
local func, res = mobdebug.loadstring("return(" .. exp .. ")")
if func then
watchescnt = watchescnt + 1
local newidx = #watches + 1
@@ -748,7 +843,7 @@ local function debugger_loop(sev, svars, sfile, sline)
elseif command == "RUN" then
server:send("200 OK\n")
local ev, vars, file, line, idx_watch = coroutine.yield()
local ev, vars, file, line, idx_watch = coroyield()
eval_env = vars
if ev == events.BREAK then
server:send("202 Paused " .. file .. " " .. line .. "\n")
@@ -764,7 +859,7 @@ local function debugger_loop(sev, svars, sfile, sline)
server:send("200 OK\n")
step_into = true
local ev, vars, file, line, idx_watch = coroutine.yield()
local ev, vars, file, line, idx_watch = coroyield()
eval_env = vars
if ev == events.BREAK then
server:send("202 Paused " .. file .. " " .. line .. "\n")
@@ -785,7 +880,7 @@ local function debugger_loop(sev, svars, sfile, sline)
if command == "OUT" then step_level = stack_level - 1
else step_level = stack_level end
local ev, vars, file, line, idx_watch = coroutine.yield()
local ev, vars, file, line, idx_watch = coroyield()
eval_env = vars
if ev == events.BREAK then
server:send("202 Paused " .. file .. " " .. line .. "\n")
@@ -809,6 +904,10 @@ local function debugger_loop(sev, svars, sfile, sline)
end
elseif command == "SUSPEND" then
-- do nothing; it already fulfilled its role
elseif command == "DONE" then
server:send("200 OK\n")
done()
return -- done with all the debugging
elseif command == "STACK" then
-- first check if we can execute the stack command
-- as it requires yielding back to debug_hook it cannot be executed
@@ -816,7 +915,7 @@ local function debugger_loop(sev, svars, sfile, sline)
-- in this case we simply return an empty result
local vars, ev = {}
if seen_hook then
ev, vars = coroutine.yield("stack")
ev, vars = coroyield("stack")
end
if ev and ev ~= events.STACK then
server:send("401 Error in Execution " .. #vars .. "\n")
@@ -856,7 +955,7 @@ local function debugger_loop(sev, svars, sfile, sline)
end
elseif command == "EXIT" then
server:send("200 OK\n")
coroutine.yield("exit")
coroyield("exit")
else
server:send("400 Bad Request\n")
end
@@ -867,10 +966,6 @@ local function connect(controller_host, controller_port)
return (socket.connect4 or socket.connect)(controller_host, controller_port)
end
local function isrunning()
return coro_debugger and coroutine.status(coro_debugger) == 'suspended'
end
local lasthost, lastport
-- Starts a debug session by connecting to a controller
@@ -916,7 +1011,7 @@ local function start(controller_host, controller_port)
return (dtraceback(...):gsub("(stack traceback:\n)[^\n]*\n", "%1"))
end
end
coro_debugger = coroutine.create(debugger_loop)
coro_debugger = corocreate(debugger_loop)
debug.sethook(debug_hook, "lcr")
seen_hook = nil -- reset in case the last start() call was refused
step_into = true -- start with step command
@@ -949,16 +1044,16 @@ local function controller(controller_host, controller_port, scratchpad)
end
seen_hook = true -- allow to accept all commands
coro_debugger = coroutine.create(debugger_loop)
coro_debugger = corocreate(debugger_loop)
while true do
step_into = true -- start with step command
abort = false -- reset abort flag from the previous loop
if scratchpad then checkcount = mobdebug.checkcount end -- force suspend right away
coro_debugee = coroutine.create(debugee)
coro_debugee = corocreate(debugee)
debug.sethook(coro_debugee, debug_hook, "lcr")
local status, err = coroutine.resume(coro_debugee)
local status, err = cororesume(coro_debugee)
-- was there an error or is the script done?
-- 'abort' state is allowed here; ignore it
@@ -980,7 +1075,7 @@ local function controller(controller_host, controller_port, scratchpad)
-- variable from console, but they will be reset anyway.
-- This functionality is used when scratchpad is paused to
-- gain access to remote console to modify global variables.
local status, err = coroutine.resume(coro_debugger, events.RESTART, capture_vars(2))
local status, err = cororesume(coro_debugger, events.RESTART, capture_vars(2))
if not status or status and err == "exit" then break end
end
end
@@ -1038,7 +1133,7 @@ local function off()
-- if not, turn the debugging off
if jit then
local remove = true
for co, debugged in pairs(coroutines) do
for _, debugged in pairs(coroutines) do
if debugged then remove = false; break end
end
if remove then debug.sethook() end
@@ -1099,7 +1194,14 @@ local function handle(params, client, options)
end
if done then break end
end
elseif command == "setb" then
elseif command == "done" then
client:send(string.upper(command) .. "\n")
if client:receive() ~= "200 OK" then
print("Unknown error")
os.exit(1, true)
return nil, nil, "Debugger error: unexpected response after 'done'"
end
elseif command == "setb" or command == "asetb" then
_, _, _, file, line = string.find(params, "^([a-z]+)%s+(.-)%s+(%d+)%s*$")
if file and line then
-- if this is a file name, and not a file source
@@ -1108,7 +1210,7 @@ local function handle(params, client, options)
file = removebasedir(file, basedir)
end
client:send("SETB " .. file .. " " .. line .. "\n")
if client:receive() == "200 OK" then
if command == "asetb" or client:receive() == "200 OK" then
set_breakpoint(file, line)
else
print("Error: breakpoint not inserted")
@@ -1137,7 +1239,7 @@ local function handle(params, client, options)
else
print("Invalid command")
end
elseif command == "delb" then
elseif command == "delb" or command == "adelb" then
_, _, _, file, line = string.find(params, "^([a-z]+)%s+(.-)%s+(%d+)%s*$")
if file and line then
-- if this is a file name, and not a file source
@@ -1146,7 +1248,7 @@ local function handle(params, client, options)
file = removebasedir(file, basedir)
end
client:send("DELB " .. file .. " " .. line .. "\n")
if client:receive() == "200 OK" then
if command == "adelb" or client:receive() == "200 OK" then
remove_breakpoint(file, line)
else
print("Error: breakpoint not removed")
@@ -1380,7 +1482,8 @@ local function handle(params, client, options)
print("stack -- reports stack trace")
print("output stdout <d|c|r> -- capture and redirect io stream (default|copy|redirect)")
print("basedir [<path>] -- sets the base path of the remote application, or shows the current one")
print("exit -- exits debugger")
print("done -- stops the debugger and continues application execution")
print("exit -- exits debugger and the application")
else
local _, _, spaces = string.find(params, "^(%s*)$")
if not spaces then
@@ -1433,7 +1536,7 @@ local function coro()
cocreate = cocreate or coroutine.create
coroutine.create = function(f, ...)
return cocreate(function(...)
require("mobdebug").on()
mobdebug.on()
return f(...)
end, ...)
end
@@ -1452,7 +1555,7 @@ local function moai()
local patched = mt.run
mt.run = function(self, f, ...)
return patched(self, function(...)
require("mobdebug").on()
mobdebug.on()
return f(...)
end, ...)
end
@@ -1460,26 +1563,6 @@ local function moai()
end
end
-- this is a function that removes all hooks and closes the socket to
-- report back to the controller that the debugging is done.
-- the script that called `done` can still continue.
local function done()
if not (isrunning() and server) then return end
if not jit then
for co, debugged in pairs(coroutines) do
if debugged then debug.sethook(co) end
end
end
debug.sethook()
server:close()
coro_debugger = nil -- to make sure isrunning() returns `false`
seen_hook = nil -- to make sure that the next start() call works
abort = nil -- to make sure that callback calls use proper "abort" value
end
-- make public functions available
mobdebug.setbreakpoint = set_breakpoint
mobdebug.removebreakpoint = remove_breakpoint
@@ -1494,6 +1577,7 @@ mobdebug.off = off
mobdebug.moai = moai
mobdebug.coro = coro
mobdebug.done = done
mobdebug.pause = function() step_into = true end
mobdebug.yield = nil -- callback
-- this is needed to make "require 'modebug'" to work when mobdebug

View File

@@ -11,6 +11,8 @@ local P = {
-- abort further processing.
-- For `onEditorPreSave` event it means that file saving will be aborted.
-- For `onEditorKeyDown` event it means that the key will be "eaten".
-- For `onEditorAction` event it means that the action will not be executed.
-- For `onFiletreeActivate` event it means that no further processing is done.
-- For `onEditorCharAdded` event it means that no further processing is done
-- (but the character is still added to the editor).
@@ -24,8 +26,15 @@ local events = {
onEditorSave = function(self, editor) end,
onEditorFocusLost = function(self, editor) end,
onEditorFocusSet = function(self, editor) end,
onEditorAction = function(self, editor, event) end, -- return false
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,
onMenuEditor = function(self, menu, editor, event) end,
onMenuEditorTab = function(self, menu, notebook, event, index) end,
onMenuFiletree = function(self, menu, tree, event) end,
@@ -34,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

@@ -7,7 +7,7 @@ return {
exts = {"cg","cgh","cgfx","cgfxh",},
lexer = wxstc.wxSTC_LEX_CPP,
apitype = "cg",
sep = "%.",
sep = ".",
linecomment = "//",
isfncall = function(str)

View File

@@ -7,7 +7,7 @@ return {
exts = {"glsl","vert","frag","geom","cont","eval", "glslv", "glslf"},
lexer = wxstc.wxSTC_LEX_CPP,
apitype = "glsl",
sep = "%.",
sep = ".",
linecomment = "//",
isfncall = function(str)

View File

@@ -4,10 +4,10 @@
local funccall = "([A-Za-z_][A-Za-z0-9_]*)%s*"
return {
exts = {"hlsl","fx","fxh",},
exts = {"hlsl","fx","fxh","usf",},
lexer = wxstc.wxSTC_LEX_CPP,
apitype = "hlsl",
sep = "%.",
sep = ".",
linecomment = "//",
isfncall = function(str)

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 .. "(%(.-%))")
@@ -26,40 +26,65 @@ local function isfndef(str)
return s,e,cap,l
end
local q = EscapeMagic
return {
exts = {"lua", "rockspec", "wlua"},
lexer = wxstc.wxSTC_LEX_LUA,
apitype = "lua",
linecomment = "--",
sep = "%.:",
sep = ".:",
isfncall = function(str)
return string.find(str, funccall .. "[%({'\"]")
end,
isfndef = isfndef,
isdecindent = function(str)
str = str:gsub('%-%-%[=*%[.*%]=*%]',''):gsub('%-%-.*','')
-- this handles three different cases:
local term = str:match("^%s*(%w+)%s*$")
-- (1) 'end', 'elseif', 'else'
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', 'until'
local match = term and decindent[term]
-- (2) 'end)' and 'end}'
if not term then term, match = str:match("^%s*(end)%s*([%)%}]+)%s*[,;]?") end
-- (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("'.-\\'","'"):gsub("'.-'","")
:gsub('".-\\"','"'):gsub('".-"','')
:gsub('%-%-.*','') -- strip comments after strings are processed
:gsub("%b()","()") -- remove all function calls
)
local term = str:match("^%s*(%w+)%W*")
term = term and incindent[term] and 1 or 0
str = str:gsub("'.-'",""):gsub('".-"','')
local terminc = term and incindent[term] and 1 or 0
-- fix 'if' not terminated with 'then'
-- or 'then' not started with 'if'
if (term == 'if' or term == 'elseif') and not str:match("%f[%w]then%f[%W]")
or (term == 'for') and not str:match("%S%s+do%f[%W]")
or (term == 'while') and not str:match("%f[%w]do%f[%W]") then
terminc = 0
elseif not (term == 'if' or term == 'elseif') and str:match("%f[%w]then%f[%W]")
or not (term == 'for') and str:match("%S%s+do%f[%W]")
or not (term == 'while') and str:match("%f[%w]do%f[%W]") then
terminc = 1
end
local _, opened = str:gsub("([%{%(])", "%1")
local _, closed = str:gsub("([%}%)])", "%1")
local func = (isfndef(str) or str:match("%W+function%s*%(")) and 1 or 0
-- ended should only be used to negate term and func effects
local anon = str:match("%W+function%s*%(.+%Wend%W")
local ended = (term + func > 0) and (str:match("%W+end%s*$") or anon) and 1 or 0
local ended = (terminc + func > 0) and (str:match("%W+end%s*$") or anon) and 1 or 0
return opened - closed + func + term - ended
return opened - closed + func + terminc - ended
end,
markvars = function(code, pos, vars)
local PARSE = require 'lua_parser_loose'
@@ -92,7 +117,7 @@ return {
local line = editor:GetCurrentLine()-1
local maxlines = 48 -- scan up to this many lines back
local scopestart = {"if","do","while","function", "local%s+function", "for", "else", "elseif"}
local scopestart = {"if", "do", "while", "function", "local%s+function", "for", "else", "elseif"}
local scopeend = {"end"}
local iscomment = editor.spec.iscomment
@@ -107,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
@@ -129,8 +154,9 @@ return {
local tx = editor:GetLine(line) --= string
-- check for assignments
local varname = "([%w_][%w_%.]*)"
local identifier = "([%w_][%w_%.:%s]*)"
local sep = editor.spec.sep
local varname = "([%w_][%w_"..q(sep:sub(1,1)).."]*)"
local identifier = "([%w_][%w_"..q(sep).."%s]*)"
-- special hint
local typ,var = tx:match("%s*%-%-=%s*"..varname.."%s+"..identifier)
@@ -169,7 +195,7 @@ return {
end
if (var and typ) then
class,func = typ:match(varname.."[%.:]"..varname)
class,func = typ:match(varname.."["..q(sep).."]"..varname)
if (assigns[typ]) then
assigns[var] = assigns[typ]
elseif (func) then
@@ -227,8 +253,7 @@ return {
keywords = {
[[and break do else elseif end for function goto if in local not or repeat return then until while]],
[[_G _VERSION _ENV false io.stderr io.stdin io.stdout nil math.huge math.pi package.config
package.cpath package.loaded package.loaders package.path package.preload package.searchers self true]],
[[_G _VERSION _ENV false io.stderr io.stdin io.stdout nil math.huge math.pi self true]],
[[assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring
module next pairs pcall print rawequal rawget rawlen rawset require
@@ -247,7 +272,8 @@ return {
math.floor math.fmod math.frexp math.ldexp math.log math.log10 math.max math.min math.modf
math.pow math.rad math.random math.randomseed math.sin math.sinh math.sqrt math.tan math.tanh
os.clock os.date os.difftime os.execute os.exit os.getenv os.remove os.rename os.setlocale os.time os.tmpname
package.loadlib package.searchpath package.seeall
package.loadlib package.searchpath package.seeall package.config
package.cpath package.loaded package.loaders package.path package.preload package.searchers
string.byte string.char string.dump string.find string.format string.gmatch string.gsub string.len
string.lower string.match string.rep string.reverse string.sub string.upper
byte find format gmatch gsub len lower match rep reverse sub upper

View File

@@ -42,7 +42,7 @@ return {
exts = {"cl","ocl","clh",},
lexer = wxstc.wxSTC_LEX_CPP,
apitype = "opencl",
sep = "%.",
sep = ".",
linecomment = "//",
isfncall = function(str)

View File

@@ -5,7 +5,7 @@ return {
exts = {"ptx",},
lexer = wxstc.wxSTC_LEX_CPP,
apitype = "ptx",
sep = "%.",
sep = ".",
linecomment = "//",
isfndef = function(str)

View File

@@ -135,6 +135,10 @@ config = {
fontsize = nil, -- no default size as it is system dependent
},
format = { -- various formatting strings
menurecentprojects = nil,
},
keymap = {}, -- mapping of menu IDs to hot keys
messages = {}, -- list of messages in a particular language
language = "en", -- current UI language
@@ -145,7 +149,7 @@ config = {
interpreter = "luadeb", -- the default "project" lua interpreter
autocomplete = true, -- whether autocomplete is on by default
autoanalizer = true, -- whether auto syntax analizer is on by default
autoanalyzer = true, -- whether auto syntax analyzer is on by default
acandtip = {
shorttip = false, -- tooltips are compact during typing

View File

@@ -1,8 +1,12 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local ide = ide
local statusBar = ide.frame.statusBar
local q = EscapeMagic
-- api loading depends on Lua interpreter
-- and loaded specs
@@ -121,7 +125,7 @@ local function formatUpToX(s, x)
return table.concat(t, "\n")
end
local function fillTips(api,apibasename,apiname)
local function fillTips(api,apibasename)
local apiac = api.ac
local tclass = api.tip
local tipwidth = math.max(20, ide.config.acandtip.width)
@@ -152,11 +156,11 @@ local function fillTips(api,apibasename,apiname)
local description = formatUpToX(info.description or "", tipwidth)
-- build info
local inf = (info.type == "value" and "" or frontname.."\n")
..description
local inf = ((info.type == "value" and "" or frontname.."\n")
..description)
local sentence = description:match("^(.-)%. ?\n")
local infshort = (info.type == "value" and "" or frontname.."\n")
..(sentence and sentence.."..." or description)
local infshort = ((info.type == "value" and "" or frontname.."\n")
..(sentence and sentence.."..." or description))
local infshortbatch = (info.returns and info.args) and frontname or infshort
-- add to infoclass
@@ -213,15 +217,18 @@ end
-- assumes a tidied up string (no spaces, braces..)
local function resolveAssign(editor,tx)
local ac = editor.api.ac
local sep = editor.spec.sep
local anysep = "["..q(sep).."]"
local assigns = editor.assignscache and editor.assignscache.assigns
local function getclass(tab,a)
local key,rest = a:match("([%w_]+)[%.:](.*)")
local key,rest = a:match("([%w_]+)"..anysep.."(.*)")
key = tonumber(key) or key -- make this work for childs[0]
if (key and rest and tab.childs and tab.childs[key]) then
return getclass(tab.childs[key],rest)
end
if (tab.valuetype) then
return getclass(ac,tab.valuetype.."."..a)
-- process valuetype, but only if it doesn't reference the current tab
if (tab.valuetype and tab ~= ac.childs[tab.valuetype]) then
return getclass(ac,tab.valuetype..sep:sub(1,1)..a)
end
return tab,a
end
@@ -234,7 +241,7 @@ local function resolveAssign(editor,tx)
local classname = nil
c = ""
change = false
for w,s in tx:gmatch("([%w_]+)([%.:]?)") do
for w,s in tx:gmatch("([%w_]+)("..anysep.."?)") do
local old = classname
-- check if what we have so far can be matched with a class name
-- this can happen if it's a reference to a value with a known type
@@ -247,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("[.:]","[.:]"))) then break end
tx = c
end
else
@@ -269,11 +271,13 @@ function GetTipInfo(editor, content, short, fullmatch)
-- try to resolve the class
content = content:gsub("%b[]",".0")
local tab, rest = resolveAssign(editor, content)
local tab = resolveAssign(editor, content)
local sep = editor.spec.sep
local anysep = "["..q(sep).."]"
local caller = content:match("([%w_]+)%(?%s*$")
local class = (tab and tab.classname
or caller and content:match("([%w_]+)[%.:]"..caller.."%(?%s*$") or "")
or caller and content:match("([%w_]+)"..anysep..caller.."%(?%s*$") or "")
local tip = editor.api.tip
local classtab = short and tip.shortfinfoclass or tip.finfoclass
@@ -335,13 +339,13 @@ local function addDynamicWord (api,word)
if api.tip.keys[word] or api.tip.staticnames[word] then return end
local cnt = dywordentries[word]
if cnt then
dywordentries[word] = cnt +1
dywordentries[word] = cnt + 1
return
end
dywordentries[word] = 1
local wlow = word:lower()
for i=0,#word do
local k = wlow : sub (1,i)
local k = wlow:sub(1,i)
dynamicwords[k] = dynamicwords[k] or {}
table.insert(dynamicwords[k], word)
end
@@ -385,27 +389,28 @@ local function getEditorLines(editor,line,numlines)
editor:PositionFromLine(line),editor:PositionFromLine(line+numlines+1))
end
function DynamicWordsAdd(ev,editor,content,line,numlines)
function DynamicWordsAdd(editor,content,line,numlines)
if ide.config.acandtip.nodynwords then return end
local api = editor.api
local content = content or getEditorLines(editor,line,numlines)
for word in content:gmatch "[%.:]?%s*([a-zA-Z_]+[a-zA-Z_0-9]+)" do
local anysep = "["..q(editor.spec.sep).."]"
content = content or getEditorLines(editor,line,numlines)
for word in content:gmatch(anysep.."?%s*([a-zA-Z_]+[a-zA-Z_0-9]+)") do
addDynamicWord(api,word)
end
end
function DynamicWordsRem(ev,editor,content,line,numlines)
function DynamicWordsRem(editor,content,line,numlines)
if ide.config.acandtip.nodynwords then return end
local api = editor.api
local content = content or getEditorLines(editor,line,numlines)
for word in content:gmatch "[%.:]?%s*([a-zA-Z_]+[a-zA-Z_0-9]+)" do
local anysep = "["..q(editor.spec.sep).."]"
content = content or getEditorLines(editor,line,numlines)
for word in content:gmatch(anysep.."?%s*([a-zA-Z_]+[a-zA-Z_0-9]+)") do
removeDynamicWord(api,word)
end
end
function DynamicWordsRemoveAll (editor)
local tx = editor:GetText()
DynamicWordsRem("close",editor,tx)
function DynamicWordsRemoveAll(editor)
DynamicWordsRem(editor,editor:GetText())
end
------------
@@ -414,7 +419,6 @@ end
local cachemain = {}
local cachemethod = {}
local laststrategy
local lastmethod
local function getAutoCompApiList(childs,fragment,method)
fragment = fragment:lower()
local strategy = ide.config.acandtip.strategy
@@ -435,8 +439,8 @@ local function getAutoCompApiList(childs,fragment,method)
-- if a:b typed, then value (type == "value") not allowed
-- if a.b typed, then method (type == "method") not allowed
if type(v) ~= 'table' or (v.type and
((method and v.type ~= "value")
or (not method and v.type ~= "method"))) then
((method and v.type ~= "value")
or (not method and v.type ~= "method"))) then
wlist = wlist..i.." "
end
end
@@ -470,8 +474,8 @@ local function getAutoCompApiList(childs,fragment,method)
-- if a:b typed, then value (type == "value") not allowed
-- if a.b typed, then method (type == "method") not allowed
if type(v) ~= 'table' or (v.type and
((method and v.type ~= "value")
or (not method and v.type ~= "method"))) then
((method and v.type ~= "value")
or (not method and v.type ~= "method"))) then
local used = {}
--
local kl = key:lower()
@@ -503,10 +507,6 @@ local function getAutoCompApiList(childs,fragment,method)
return t
end
function ClearAutoCompCache()
cache = {}
end
-- make syntype dependent
function CreateAutoCompList(editor,key)
local api = editor.api
@@ -514,7 +514,7 @@ function CreateAutoCompList(editor,key)
local ac = api.ac
local sep = editor.spec.sep
local method = key:match(":[^"..sep.."]*$") ~= nil
local method = key:match(":[^"..q(sep).."]*$") ~= nil
-- ignore keywords
if tip.keys[key] then return end
@@ -527,7 +527,7 @@ function CreateAutoCompList(editor,key)
if not (progress) then return end
if (tab == ac) then
local _, krest = rest:match("([%w_]+)["..sep.."]([%w_]*)%s*$")
local _, krest = rest:match("([%w_]+)["..q(sep).."]([%w_]*)%s*$")
if (krest) then
tab = #krest >= (ide.config.acandtip.startat or 2) and tip.finfo or {}
rest = krest:gsub("[^%w_]","")
@@ -560,9 +560,8 @@ function CreateAutoCompList(editor,key)
break
end
end
local res = table.concat(list," ")
dw = res ~= "" and " "..res or ""
dw = table.concat(list," ")
end
end
@@ -571,7 +570,7 @@ function CreateAutoCompList(editor,key)
local function addInheritance(tab, apilist, seen)
if not tab.inherits then return end
for base in tab.inherits:gmatch("[%w_"..sep.."]+") do
for base in tab.inherits:gmatch("[%w_"..q(sep).."]+") do
local tab = ac
-- map "a.b.c" to class hierarchy (a.b.c)
for class in base:gmatch("[%w_]+") do tab = tab.childs[class] end
@@ -641,7 +640,7 @@ function CreateAutoCompList(editor,key)
end
-- concat final, list complete first
local li = (compstr .. dw)
local li = compstr .. (#compstr > 0 and #dw > 0 and " " or "") .. dw
return li ~= "" and (#li > 1024 and li:sub(1,1024).."..." or li) or nil
end

View File

@@ -1,6 +1,8 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Lomtik Software (J. Winwood & John Labenski)
-- Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local ide = ide
local frame = ide.frame
local notebook = frame.notebook
@@ -10,12 +12,11 @@ local unpack = table.unpack or unpack
local CURRENT_LINE_MARKER = StylesGetMarker("currentline")
local CURRENT_LINE_MARKER_VALUE = 2^CURRENT_LINE_MARKER
local BREAKPOINT_MARKER = StylesGetMarker("breakpoint")
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)
@@ -27,7 +28,7 @@ end
-- Find an editor page that hasn't been used at all, eg. an untouched NewFile()
local function findUnusedEditor()
local editor
for id, document in pairs(openDocuments) do
for _, document in pairs(openDocuments) do
if (document.editor:GetLength() == 0) and
(not document.isModified) and (not document.filePath) and
not (document.editor:GetReadOnly() == true) then
@@ -50,7 +51,8 @@ function LoadFile(filePath, editor, file_must_exist, skipselection)
if not skipselection and doc.index ~= notebook:GetSelection() then
-- selecting the same tab doesn't trigger PAGE_CHANGE event,
-- but moves the focus to the tab bar, which needs to be avoided.
notebook:SetSelection(doc.index) end
notebook:SetSelection(doc.index)
end
return doc.editor
end
end
@@ -69,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
@@ -107,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)
@@ -193,11 +198,26 @@ 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 i,spec in pairs(ide.specs) do
for _,spec in pairs(ide.specs) do
if (spec.exts) then
for n,ext in ipairs(spec.exts) do
for _,ext in ipairs(spec.exts) do
knownexts = knownexts.."*."..ext..";"
end
end
@@ -213,11 +233,12 @@ function ReportError(msg)
end
function OpenFile(event)
local exts = getExtsString()
local editor = GetEditor()
local path = editor and ide:GetDocument(editor):GetFilePath() or nil
local fileDialog = wx.wxFileDialog(ide.frame, TR("Open file"),
(path and GetPathWithSep(path) or FileTreeGetDir() or ""),
"",
"",
exts,
getExtsString(),
wx.wxFD_OPEN + wx.wxFD_FILE_MUST_EXIST)
if fileDialog:ShowModal() == wx.wxID_OK then
if not LoadFile(fileDialog:GetPath(), nil, true) then
@@ -320,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
@@ -343,7 +364,7 @@ function SaveFileAs(editor)
end
function SaveAll(quiet)
for id, document in pairs(openDocuments) do
for _, document in pairs(openDocuments) do
local editor = document.editor
local filePath = document.filePath
@@ -471,7 +492,7 @@ function SaveModifiedDialog(editor, allow_cancel)
end
function SaveOnExit(allow_cancel)
for id, document in pairs(openDocuments) do
for _, document in pairs(openDocuments) do
if (SaveModifiedDialog(document.editor, allow_cancel) == wx.wxID_CANCEL) then
return false
end
@@ -521,35 +542,24 @@ function FoldSome()
if foldall then
if foldHdr and editor:GetFoldExpanded(ln) then
editor:ToggleFold(ln) end
editor:ToggleFold(ln)
end
elseif hidebase then
if not foldHdr and (foldLvl == wxstc.wxSTC_FOLDLEVELBASE) then
editor:HideLines(ln, ln) end
editor:HideLines(ln, ln)
end
else -- unfold all
if foldHdr and not editor:GetFoldExpanded(ln) then
editor:ToggleFold(ln) end
editor:ToggleFold(ln)
end
end
end
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 id, document in pairs(openDocuments) do
local editor = document.editor
editor:SetReadOnly(enable)
for _, document in pairs(openDocuments) do
document.editor:SetReadOnly(enable)
end
end
@@ -557,9 +567,8 @@ end
-- Debug related
function ClearAllCurrentLineMarkers()
for id, document in pairs(openDocuments) do
local editor = document.editor
editor:MarkerDeleteAll(CURRENT_LINE_MARKER)
for _, document in pairs(openDocuments) do
document.editor:MarkerDeleteAll(CURRENT_LINE_MARKER)
end
end
@@ -570,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
@@ -604,7 +618,8 @@ function CompileProgram(editor, params)
end
end
if line and params.jumponerror and line-1 ~= editor:GetCurrentLine() then
editor:GotoLine(line-1) end
editor:GotoLine(line-1)
end
end
return func ~= nil -- return true if it compiled ok
@@ -640,7 +655,7 @@ end
function GetOpenFiles()
local opendocs = {}
for id, document in pairs(ide.openDocuments) do
for _, document in pairs(ide.openDocuments) do
if (document.filePath) then
local wxfname = wx.wxFileName(document.filePath)
wxfname:Normalize()
@@ -659,7 +674,7 @@ function GetOpenFiles()
end
function SetOpenFiles(nametab,params)
for i,doc in ipairs(nametab) do
for _, doc in ipairs(nametab) do
local editor = LoadFile(doc.filename,nil,true,true) -- skip selection
if editor then editor:GotoPosDelayed(doc.cursorpos or 0) end
end
@@ -668,6 +683,8 @@ function SetOpenFiles(nametab,params)
end
local beforeFullScreenPerspective
local statusbarShown
function ShowFullScreen(setFullScreen)
if setFullScreen then
beforeFullScreenPerspective = uimgr:SavePerspective()
@@ -684,21 +701,24 @@ function ShowFullScreen(setFullScreen)
beforeFullScreenPerspective = nil
end
-- On OSX, toolbar and status bar are not hidden when switched to
-- On OSX, status bar is not hidden when switched to
-- full screen: http://trac.wxwidgets.org/ticket/14259; do manually.
-- need to turn off before showing full screen and turn on after,
-- otherwise the window is restored incorrectly and is reduced in size.
if ide.osname == 'Macintosh' and setFullScreen then
statusbarShown = frame:GetStatusBar():IsShown()
frame:GetStatusBar():Hide()
frame:GetToolBar():Hide()
end
-- protect from systems that don't have ShowFullScreen (GTK on linux?)
pcall(function() frame:ShowFullScreen(setFullScreen) end)
if ide.osname == 'Macintosh' and not setFullScreen then
frame:GetStatusBar():Show()
frame:GetToolBar():Show()
if statusbarShown then
frame:GetStatusBar():Show()
-- refresh AuiManager as the statusbar may be shown below the border
uimgr:Update()
end
end
end
@@ -713,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()
@@ -733,9 +761,10 @@ end
local function getOpenTabs()
local opendocs = {}
for id, document in pairs(ide.openDocuments) do
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,
@@ -754,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()
@@ -860,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
@@ -870,7 +915,14 @@ local function closeWindow(event)
PackageEventHandle("onAppClose")
-- first need to detach all processes IDE has launched as the current
-- process is likely to terminate before child processes are terminated,
-- which may lead to a crash when EVT_END_PROCESS event is called.
DetachChildProcess()
DebuggerShutdown()
SettingsSaveAll()
if ide.config.hotexit then saveHotExit() end
ide.settings:Flush()
do -- hide all floating panes first
@@ -884,34 +936,80 @@ local function closeWindow(event)
frame.uimgr:UnInit()
frame:Hide() -- hide the main frame while the IDE exits
-- first need to detach all processes IDE has launched as the current
-- process is likely to terminate before child processes are terminated,
-- which may lead to a crash when EVT_END_PROCESS event is called.
DetachChildProcess()
DebuggerShutdown()
if ide.session.timer then ide.session.timer:Stop() end
event:Skip()
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,
-- the focus is always on the toolbar when the app gets focus,
-- so to restore the focus correctly, need to track where the control is
-- and to set the focus to the last element that had focus.
-- it would be easier to track KILL_FOCUS events, but controls on OSX
-- don't always generate KILL_FOCUS events (see relevant wxwidgets
-- tickets: http://trac.wxwidgets.org/ticket/14142
-- and http://trac.wxwidgets.org/ticket/14269)
local infocus
ide.editorApp:Connect(wx.wxEVT_SET_FOCUS, function(event)
if ide.exitingProgram then return end
local win = ide.frame:FindFocus()
if win then
local class = win:GetClassInfo():GetClassName()
-- don't set focus on the main frame or toolbar
if infocus and (class == 'wxAuiToolBar' or class == 'wxFrame') then
pcall(function() infocus:SetFocus() end)
return
end
-- keep track of the current control in focus, but only on the main frame
-- don't try to "remember" any of the focus changes on various dialog
-- windows as those will disappear along with their controls
local grandparent = win:GetGrandParent()
local frameid = ide.frame:GetId()
local mainwin = grandparent and grandparent:GetId() == frameid
local parent = win:GetParent()
while parent do
local class = parent:GetClassInfo():GetClassName()
if (class == 'wxFrame' or class:find('^wx.*Dialog$'))
and parent:GetId() ~= frameid then
mainwin = false; break
end
parent = parent:GetParent()
end
if mainwin then
if infocus and infocus ~= win and ide.osname == 'Macintosh' then
-- kill focus on the control that had the focus as wxwidgets on OSX
-- doesn't do it: http://trac.wxwidgets.org/ticket/14142;
-- wrap into pcall in case the window is already deleted
local ev = wx.wxFocusEvent(wx.wxEVT_KILL_FOCUS)
pcall(function() infocus:GetEventHandler():ProcessEvent(ev) end)
end
infocus = win
end
end
event:Skip()
end)
ide.editorApp:Connect(wx.wxEVT_ACTIVATE_APP,
function(event)
if not ide.exitingProgram then
-- wxSTC controls on OSX don't generate KILL_FOCUS events
-- when focus is switched between controls in the app;
-- manually kill focus when the app is deactivated
if ide.osname == 'Macintosh' and not event:GetActive() then
local ntbk = frame.bottomnotebook
for _,win in ipairs({ntbk.errorlog, ntbk.shellbox, GetEditor()}) do
local ev = wx.wxFocusEvent(wx.wxEVT_KILL_FOCUS)
win:GetEventHandler():ProcessEvent(ev)
end
if ide.osname == 'Macintosh' and infocus and event:GetActive() then
-- restore focus to the last element that received it;
-- wrap into pcall in case the element has disappeared
-- while the application was out of focus
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
@@ -923,3 +1021,15 @@ if ide.config.autorecoverinactivity then
-- check at least 5s to be never more than 5s off
ide.session.timer:Start(math.min(5, ide.config.autorecoverinactivity)*1000)
end
function PaneFloatToggle(window)
local pane = uimgr:GetPane(window)
if pane:IsFloating() then
pane:Dock()
else
pane:Float()
pane:FloatingPosition(pane.window:GetScreenPosition())
pane:FloatingSize(pane.window:GetSize())
end
uimgr:Update()
end

View File

@@ -1,7 +1,8 @@
-- Integration with MobDebug
-- Copyright 2011-13 Paul Kulchenko, ZeroBrane LLC
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- Original authors: Lomtik Software (J. Winwood & John Labenski)
-- Luxinia Dev (Eike Decker & Christoph Kubisch)
-- Integration with MobDebug
---------------------------------------------------------
local copas = require "copas"
local socket = require "socket"
@@ -9,19 +10,32 @@ local mobdebug = require "mobdebug"
local unpack = table.unpack or unpack
local ide = ide
local debugger = ide.debugger
local debugger = setmetatable(ide.debugger, ide.proto.Debugger)
debugger.server = nil -- DebuggerServer object when debugging, else nil
debugger.running = false -- true when the debuggee is running
debugger.listening = false -- true when the debugger is listening for a client
debugger.portnumber = ide.config.debugger.port or mobdebug.port -- the port # to use for debugging
debugger.watchCtrl = nil -- the watch ctrl that shows watch information
debugger.stackCtrl = nil -- the stack ctrl that shows stack information
debugger.toggleview = { stackpanel = false, watchpanel = false }
debugger.toggleview = {
stackpanel = false, watchpanel = false, toolbar = false }
debugger.hostname = ide.config.debugger.hostname or (function()
local hostname = socket.dns.gethostname()
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")
@@ -56,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())
@@ -65,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
@@ -111,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"
@@ -144,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] }
@@ -160,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
@@ -172,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)
@@ -196,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
@@ -209,10 +230,12 @@ local function debuggerToggleViews(show)
local mgr = ide.frame.uimgr
local refresh = false
for view, needed in pairs(debugger.toggleview) do
local bar = view == 'toolbar'
local pane = mgr:GetPane(view)
if show then -- starting debugging and pane is not shown
debugger.toggleview[view] = not pane:IsShown()
if debugger.toggleview[view] and needed then
if debugger.toggleview[view] and (needed or bar)
and (not bar or not ide.frame:IsFullScreen()) then
pane:Show()
refresh = true
end
@@ -393,10 +416,7 @@ debugger.shell = function(expression, isstatement)
local addedret, forceexpression = true, expression:match("^%s*=%s*")
expression = expression:gsub("^%s*=%s*","")
local _, values, err = debugger.evaluate(expression)
if not forceexpression and err and
(err:find("'?<eof>'? expected near '") or
err:find("'%(' expected near") or
err:find("unexpected symbol near '")) then
if not forceexpression and err then
_, values, err = debugger.execute(expression)
addedret = false
end
@@ -406,7 +426,6 @@ debugger.shell = function(expression, isstatement)
DisplayShellErr(err)
elseif addedret or #values > 0 then
if forceexpression then -- display elements as multi-line
local mobdebug = require "mobdebug"
for i,v in pairs(values) do -- stringify each of the returned values
local func = loadstring('return '..v) -- deserialize the value first
if func then -- if it's deserialized correctly
@@ -427,8 +446,11 @@ debugger.shell = function(expression, isstatement)
-- refresh Stack and Watch windows if executed a statement (and no err)
if isstatement and not err and not addedret and #values == 0 then
updateStackSync() updateWatchesSync() end
updateStackSync() updateWatchesSync()
end
end)
elseif debugger.server then
DisplayShellErr(TR("Can't evaluate the expression while the application is running."))
end
end
@@ -442,7 +464,60 @@ local function stoppedAtBreakpoint(file, line)
return breakpoint > -1 and breakpoint == current
end
debugger.listen = function()
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
debugger.terminate() -- terminate if running
copas.removeserver(debugger.listening)
DisplayOutputLn(TR("Debugger server stopped at %s:%d.")
:format(debugger.hostname, debugger.portnumber))
debugger.listening = false
else
DisplayOutputLn(TR("Can't stop debugger server as it is not started."))
end
return
end
local server, err = socket.bind("*", debugger.portnumber)
if not server then
DisplayOutputLn(TR("Can't start debugger server at %s:%d: %s.")
@@ -476,7 +551,8 @@ debugger.listen = function()
if not options.allowediting then options.allowediting = ide.config.debugger.allowediting end
if not debugger.scratchpad and not options.allowediting then
SetAllEditorsReadOnly(true) end
SetAllEditorsReadOnly(true)
end
debugger.server = copas.wrap(skt)
debugger.socket = skt
@@ -487,7 +563,7 @@ debugger.listen = function()
debugger.editormap = {}
local wxfilepath = GetEditorFileAndCurInfo()
local startfile = options.startfile or options.startwith
local startfile = options.startwith
or (wxfilepath and wxfilepath:GetFullPath())
if not startfile then
@@ -505,6 +581,12 @@ debugger.listen = function()
-- set basedir first, before loading to make sure that the path is correct
debugger.handle("basedir " .. debugger.basedir)
local init = options.init or ide.config.debugger.init
if init then
local _, _, err = debugger.execute(init)
if err then DisplayOutputLn(TR("Ignored error in debugger initialization code: %s."):format(err)) end
end
reSetBreakpoints()
local redirect = ide.config.debugger.redirect or options.redirect
@@ -562,8 +644,11 @@ debugger.listen = function()
..":\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
@@ -586,36 +671,8 @@ debugger.listen = function()
-- 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
@@ -657,20 +714,31 @@ debugger.listen = function()
end
end
end)
debugger.listening = true
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
osexit, os.exit = os.exit, function () end
gprint, _G.print = _G.print, function (...) if verbose then DisplayOutputLn(...) end end
gprint, _G.print = _G.print, function (...)
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
@@ -746,6 +814,18 @@ debugger.handleAsync = function(command)
copas.addthread(function () debugger.handle(command) end)
end
end
debugger.handleDirect = function(command)
local sock = debugger.socket
if debugger.server and sock then
local running = debugger.running
-- this needs to be short as it will block the UI
sock:settimeout(0.25)
debugger.handle(command, sock)
sock:settimeout(0)
-- restore running status
debugger.running = running
end
end
debugger.loadfile = function(file)
return debugger.handle("load " .. file)
@@ -813,6 +893,7 @@ end
debugger.over = function() debugger.exec("over") end
debugger.out = function() debugger.exec("out") end
debugger.run = function() debugger.exec("run") end
debugger.detach = function() debugger.exec("done") end
debugger.evaluate = function(expression) return debugger.handle('eval ' .. expression) end
debugger.execute = function(expression) return debugger.handle('exec ' .. expression) end
debugger.stack = function() return debugger.handle('stack') end
@@ -839,7 +920,10 @@ debugger.breaknow = function(command)
end
end
debugger.breakpoint = function(file, line, state)
debugger.handleAsync((state and "setb " or "delb ") .. file .. " " .. line)
if debugger.running then
return debugger.handleDirect((state and "asetb " or "adelb ") .. file .. " " .. line)
end
return debugger.handleAsync((state and "setb " or "delb ") .. file .. " " .. line)
end
debugger.quickeval = function(var, callback)
if debugger.server and not debugger.running
@@ -854,56 +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)
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():
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)
@@ -913,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
@@ -955,94 +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(wx.wxEVT_KEY_DOWN,
watchCtrl:Connect(wx.wxEVT_COMMAND_TREE_ITEM_EXPANDING,
function (event)
local keycode = event:GetKeyCode()
if (keycode == wx.WXK_DELETE) then return deleteWatch()
elseif (keycode == wx.WXK_INSERT) then return addWatch()
elseif (keycode == wx.WXK_F2) then return editWatch()
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
event:Skip()
return true
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_DELETE_ITEM,
function (event)
local row = event:GetIndex()
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)
@@ -1063,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
@@ -1095,10 +1254,9 @@ function DebuggerShutdown()
if debugger.pid then killClient() end
end
function DebuggerStop()
function DebuggerStop(resetpid)
if (debugger.server) then
debugger.server = nil
debugger.pid = nil
SetAllEditorsReadOnly(false)
ShellSupportRemote(nil)
ClearAllCurrentLineMarkers()
@@ -1112,19 +1270,20 @@ function DebuggerStop()
-- no debugger.server, but scratchpad may still be on. Turn it off.
DebuggerScratchpadOff()
end
-- reset pid for "running" (not debugged) processes
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)
-- ignore requests to toggle when the debugger is running
if debugger.server and debugger.running then return end
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
@@ -1157,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

@@ -1,8 +1,7 @@
-- Copyright 2011-13 Paul Kulchenko, ZeroBrane LLC
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Lomtik Software (J. Winwood & John Labenski)
-- Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local wxkeywords = nil -- a string of the keywords for scintilla of wxLua's wx.XXX items
local editorID = 100 -- window id to create editor pages with, incremented for new editors
@@ -14,8 +13,24 @@ local edcfg = ide.config.editor
local styles = ide.config.styles
local unpack = table.unpack or unpack
local DEFAULT_STYLE = 32
local margin = { LINENUMBER = 0, MARKER = 1, FOLD = 2 }
local linenummask = "99999"
local foldtypes = {
[0] = { wxstc.wxSTC_MARKNUM_FOLDEROPEN, wxstc.wxSTC_MARKNUM_FOLDER,
wxstc.wxSTC_MARKNUM_FOLDERSUB, wxstc.wxSTC_MARKNUM_FOLDERTAIL, wxstc.wxSTC_MARKNUM_FOLDEREND,
wxstc.wxSTC_MARKNUM_FOLDEROPENMID, wxstc.wxSTC_MARKNUM_FOLDERMIDTAIL,
},
box = { wxstc.wxSTC_MARK_BOXMINUS, wxstc.wxSTC_MARK_BOXPLUS,
wxstc.wxSTC_MARK_VLINE, wxstc.wxSTC_MARK_LCORNER, wxstc.wxSTC_MARK_BOXPLUSCONNECTED,
wxstc.wxSTC_MARK_BOXMINUSCONNECTED, wxstc.wxSTC_MARK_TCORNER,
},
circle = { wxstc.wxSTC_MARK_CIRCLEMINUS, wxstc.wxSTC_MARK_CIRCLEPLUS,
wxstc.wxSTC_MARK_VLINE, wxstc.wxSTC_MARK_LCORNERCURVE, wxstc.wxSTC_MARK_CIRCLEPLUSCONNECTED,
wxstc.wxSTC_MARK_CIRCLEMINUSCONNECTED, wxstc.wxSTC_MARK_TCORNERCURVE,
},
plus = { wxstc.wxSTC_MARK_MINUS, wxstc.wxSTC_MARK_PLUS },
arrow = { wxstc.wxSTC_MARK_ARROWDOWN, wxstc.wxSTC_MARK_ARROW },
}
-- ----------------------------------------------------------------------------
-- Update the statusbar text of the frame using the given editor.
@@ -73,8 +88,8 @@ local function updateBraceMatch(editor)
if (pos) then
-- don't match brackets in markup comments
local style = bit.band(editor:GetStyleAt(pos), 31)
if MarkupIsSpecial and MarkupIsSpecial(style)
or editor.spec.iscomment[style] then return end
if (MarkupIsSpecial and MarkupIsSpecial(style)
or editor.spec.iscomment[style]) then return end
local pos2 = editor:BraceMatch(pos)
if (pos2 == wxstc.wxSTC_INVALID_POSITION) then
@@ -90,15 +105,6 @@ local function updateBraceMatch(editor)
end
end
local function getFileTitle (editor)
if not editor or not openDocuments[editor:GetId()] then return GetIDEString("editor") end
local id = editor:GetId()
local filePath = openDocuments[id].filePath
local fileName = openDocuments[id].fileName
if not filePath or not fileName then return GetIDEString("editor") end
return GetIDEString("editor").." ["..filePath.."]"
end
-- Check if file is altered, show dialog to reload it
local function isFileAlteredOnDisk(editor)
if not editor then return end
@@ -135,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
@@ -144,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
@@ -167,7 +173,7 @@ function SetEditorSelection(selection)
local editor = GetEditor(selection)
updateStatusText(editor) -- update even if nil
statusBar:SetStatusText("",1)
ide.frame:SetTitle(getFileTitle(editor))
ide.frame:SetTitle(ExpandPlaceholders(ide.config.format.apptitle))
if editor then
if funclist:IsEmpty() then funclist:Append(TR("Jump to a function definition..."), 0) end
@@ -247,6 +253,14 @@ function EditorAutoComplete(editor)
-- know now which string is to be completed
local userList = CreateAutoCompList(editor,lt)
-- remove any suggestions that match the word the cursor is on
-- for example, if typing 'foo' in front of 'bar', 'foobar' is not offered
local right = linetx:sub(localpos+1,#linetx):match("^([%a_]+[%w_]*)")
if userList and right then
userList = userList:gsub("%f[%w_]"..lt..right.."%f[%W]",""):gsub(" +"," ")
end
-- don't show the list if it only suggests what's already typed
if userList and #userList > 0 and not lt:find(userList.."$") then
editor:UserListShow(1, userList)
@@ -453,7 +467,7 @@ function IndicateIfNeeded()
local editor = GetEditor()
-- do the current one first
if delayed[editor] then return IndicateAll(editor) end
for editor in pairs(delayed) do return IndicateAll(editor) end
for ed in pairs(delayed) do return IndicateAll(ed) end
end
-- find all instances of a symbol at pos
@@ -469,7 +483,8 @@ local function indicateFindInstances(editor, name, pos)
if this and token.fpos > pos and this == token.at+1 then break end
if #instances > 1 and instances[#instances][-1] == token.at+1 then
table.remove(instances) end
table.remove(instances)
end
elseif token.name == name then
if op == 'Id' then
table.insert(instances[#instances], token.fpos)
@@ -547,9 +562,12 @@ function IndicateAll(editor, lines, linee)
while vars do
for name, var in pairs(vars) do
-- remove all variables that are created later than the current pos
while type(var) == 'table' and var.fpos and (var.fpos > pos) do
var = var.masked -- restored a masked var
vars[name] = var
-- skip all non-variable elements from the vars table
if type(name) == 'string' then
while type(var) == 'table' and var.fpos and (var.fpos > pos) do
var = var.masked -- restored a masked var
vars[name] = var
end
end
end
vars = getmetatable(vars) and getmetatable(vars).__index
@@ -614,7 +632,7 @@ function IndicateAll(editor, lines, linee)
end
-- clear indicators till the end of processed fragment
local pos = delayed[editor] and delayed[editor][1] or editor:GetLength()+1
pos = delayed[editor] and delayed[editor][1] or editor:GetLength()+1
-- don't clear "masked" indicators as those can be set out of order (so
-- last updated fragment is not always the last in terms of its position);
@@ -624,15 +642,16 @@ function IndicateAll(editor, lines, linee)
return delayed[editor] ~= nil -- request more events if still need to work
end
if ide.wxver < "2.9.5" or not ide.config.autoanalizer then
IndicateAll = indicateFunctionsOnly end
if ide.wxver < "2.9.5" or not ide.config.autoanalyzer then
IndicateAll = indicateFunctionsOnly
end
-- ----------------------------------------------------------------------------
-- Create an editor
function CreateEditor()
local editor = wxstc.wxStyledTextCtrl(notebook, editorID,
wx.wxDefaultPosition, wx.wxSize(0, 0),
wx.wxBORDER_STATIC)
wx.wxBORDER_NONE)
editorID = editorID + 1 -- increment so they're always unique
@@ -645,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(ide.config.editor.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
@@ -667,68 +686,86 @@ function CreateEditor()
editor:SetFont(ide.font.eNormal)
editor:StyleSetFont(wxstc.wxSTC_STYLE_DEFAULT, ide.font.eNormal)
editor:SetTabWidth(ide.config.editor.tabwidth or 2)
editor:SetIndent(ide.config.editor.tabwidth or 2)
editor:SetUseTabs(ide.config.editor.usetabs and true or false)
editor:SetTabWidth(tonumber(edcfg.tabwidth) or 2)
editor:SetIndent(tonumber(edcfg.tabwidth) or 2)
editor:SetUseTabs(edcfg.usetabs and true or false)
editor:SetIndentationGuides(true)
editor:SetViewWhiteSpace(ide.config.editor.whitespace and true or false)
editor:SetViewWhiteSpace(edcfg.whitespace and true or false)
if (ide.config.editor.usewrap) then
if (edcfg.usewrap) then
editor:SetWrapMode(wxstc.wxSTC_WRAP_WORD)
editor:SetWrapStartIndent(0)
editor:SetWrapVisualFlagsLocation(wxstc.wxSTC_WRAPVISUALFLAGLOC_END_BY_TEXT)
if ide.wxver >= "2.9.5" then
if edcfg.wrapflags then
editor:SetWrapVisualFlags(tonumber(edcfg.wrapflags) or wxstc.wxSTC_WRAPVISUALFLAG_NONE)
end
if edcfg.wrapstartindent then
editor:SetWrapStartIndent(tonumber(edcfg.wrapstartindent) or 0)
end
if edcfg.wrapindentmode then
editor:SetWrapIndentMode(edcfg.wrapindentmode)
end
end
else
editor:SetScrollWidth(100) -- set default width
editor:SetScrollWidthTracking(1) -- enable width auto-adjustment
end
if (ide.config.editor.defaulteol == wxstc.wxSTC_EOL_CRLF
or ide.config.editor.defaulteol == wxstc.wxSTC_EOL_LF) then
editor:SetEOLMode(ide.config.editor.defaulteol)
if edcfg.defaulteol == wxstc.wxSTC_EOL_CRLF
or edcfg.defaulteol == wxstc.wxSTC_EOL_LF then
editor:SetEOLMode(edcfg.defaulteol)
-- else: keep wxStyledTextCtrl default behavior (CRLF on Windows, LF on Unix)
end
editor:SetCaretLineVisible(ide.config.editor.caretline and 1 or 0)
editor:SetCaretLineVisible(edcfg.caretline and true or false)
editor:SetVisiblePolicy(wxstc.wxSTC_VISIBLE_STRICT, 3)
editor:SetMarginWidth(margin.LINENUMBER, editor:TextWidth(DEFAULT_STYLE, "99999_"))
editor:SetMarginType(margin.LINENUMBER, wxstc.wxSTC_MARGIN_NUMBER)
editor:SetMarginMask(margin.LINENUMBER, 0)
editor:SetMarginWidth(margin.LINENUMBER,
editor:TextWidth(wxstc.wxSTC_STYLE_DEFAULT, linenummask))
editor:SetMarginWidth(margin.MARKER, 16)
editor:SetMarginWidth(margin.MARKER, 18)
editor:SetMarginType(margin.MARKER, wxstc.wxSTC_MARGIN_SYMBOL)
editor:SetMarginMask(margin.MARKER, bit.bnot(wxstc.wxSTC_MASK_FOLDERS))
editor:SetMarginSensitive(margin.MARKER, true)
editor:MarkerDefine(StylesGetMarker("currentline"))
editor:MarkerDefine(StylesGetMarker("breakpoint"))
editor:MarkerDefine(StylesGetMarker("bookmark"))
if ide.config.editor.fold then
editor:SetMarginWidth(margin.FOLD, 16)
if edcfg.fold then
editor:SetMarginWidth(margin.FOLD, 18)
editor:SetMarginType(margin.FOLD, wxstc.wxSTC_MARGIN_SYMBOL)
editor:SetMarginMask(margin.FOLD, wxstc.wxSTC_MASK_FOLDERS)
editor:SetMarginSensitive(margin.FOLD, true)
end
editor:SetFoldFlags(wxstc.wxSTC_FOLDFLAG_LINEBEFORE_CONTRACTED +
wxstc.wxSTC_FOLDFLAG_LINEAFTER_CONTRACTED)
editor:SetFoldFlags(tonumber(edcfg.foldflags) or wxstc.wxSTC_FOLDFLAG_LINEAFTER_CONTRACTED)
-- allow multiple selection and multi-cursor editing if supported
if ide.wxver >= "2.9.5" then
-- allow multiple selection and multi-cursor editing if supported
editor:SetMultipleSelection(1)
editor:SetAdditionalCaretsBlink(1)
editor:SetAdditionalSelectionTyping(1)
-- allow extra ascent/descent
editor:SetExtraAscent(tonumber(edcfg.extraascent) or 0)
editor:SetExtraDescent(tonumber(edcfg.extradescent) or 0)
end
do
local fg, bg = wx.wxWHITE, wx.wxColour(128, 128, 128)
editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDEROPEN, wxstc.wxSTC_MARK_BOXMINUS, fg, bg)
editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDER, wxstc.wxSTC_MARK_BOXPLUS, fg, bg)
editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDERSUB, wxstc.wxSTC_MARK_VLINE, fg, bg)
editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDERTAIL, wxstc.wxSTC_MARK_LCORNER, fg, bg)
editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDEREND, wxstc.wxSTC_MARK_BOXPLUSCONNECTED, fg, bg)
editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDEROPENMID, wxstc.wxSTC_MARK_BOXMINUSCONNECTED, fg, bg)
editor:MarkerDefine(wxstc.wxSTC_MARKNUM_FOLDERMIDTAIL, wxstc.wxSTC_MARK_TCORNER, fg, bg)
local foldtype = foldtypes[edcfg.foldtype] or foldtypes.box
local foldmarkers = foldtypes[0]
for m = 1, #foldmarkers do
editor:MarkerDefine(foldmarkers[m], foldtype[m] or wxstc.wxSTC_MARK_EMPTY, fg, bg)
end
bg:delete()
end
if ide.config.editor.calltipdelay and ide.config.editor.calltipdelay > 0 then
editor:SetMouseDwellTime(ide.config.editor.calltipdelay)
if edcfg.calltipdelay and edcfg.calltipdelay > 0 then
editor:SetMouseDwellTime(edcfg.calltipdelay)
end
editor:AutoCompSetIgnoreCase(ide.config.acandtip.ignorecase)
@@ -737,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.
@@ -753,26 +795,28 @@ function CreateEditor()
if ide.osname ~= 'Macintosh' then self:GotoPos(pos) end
else
redolater = nil
self:GotoPos(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:GotoPosEnforcePolicy(redolater)
redolater = nil
end
end
end
function editor:SetupKeywords(...) return SetupKeywords(self, ...) end
editor.ev = {}
editor:Connect(wxstc.wxEVT_STC_MARGINCLICK,
function (event)
local line = editor:LineFromPosition(event:GetPosition())
local margin = event:GetMargin()
if margin == 1 then
local marginno = event:GetMargin()
if marginno == margin.MARKER then
DebuggerToggleBreakpoint(editor, line)
elseif margin == 2 then
elseif marginno == margin.FOLD then
if wx.wxGetKeyState(wx.WXK_SHIFT) and wx.wxGetKeyState(wx.WXK_CONTROL) then
FoldSome()
else
@@ -790,31 +834,42 @@ 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("post",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("post",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("pre",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
DynamicWordsRem("pre",editor,nil,editor:LineFromPosition(event:GetPosition()), 0)
if beforeInserted then
DynamicWordsRem(editor,nil,editor:LineFromPosition(event:GetPosition()), 0)
end
end)
editor:Connect(wxstc.wxEVT_STC_CHARADDED,
function (event)
-- auto-indent
local LF = string.byte("\n")
local ch = event:GetKey()
local pos = editor:GetCurrentPos()
@@ -827,22 +882,27 @@ function CreateEditor()
if PackageEventHandle("onEditorCharAdded", editor, event) == false then
-- this event has already been handled
elseif (ch == LF) then
-- auto-indent
if (line > 0) then
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 ide.config.editor.smartindent
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)
@@ -873,9 +933,9 @@ function CreateEditor()
elseif ide.config.autocomplete then -- code completion prompt
local trigger = linetxtopos:match("["..editor.spec.sep.."%w_]+$")
if (trigger and (#trigger > 1 or trigger:match("["..editor.spec.sep.."]"))) then
editor.autocomplete = true
end
-- make sure .autocomplete is never `nil` or editor.autocomplete fails
editor.autocomplete = trigger and (#trigger > 1 or trigger:match("["..editor.spec.sep.."]"))
and true or false
end
end)
@@ -922,6 +982,10 @@ function CreateEditor()
editor:Connect(wxstc.wxEVT_STC_USERLISTSELECTION,
function (event)
if PackageEventHandle("onEditorUserlistSelection", editor, event) == false then
return
end
if ide.wxver >= "2.9.5" and editor:GetSelections() > 1 then
local text = event:GetText()
-- capture all positions as the selection may change
@@ -974,11 +1038,13 @@ 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)
if ide.config.editor.usewrap ~= true and editor:AutoCompActive() then
if edcfg.usewrap ~= true and editor:AutoCompActive() then
-- showing auto-complete list leaves artifacts on the screen,
-- which can only be fixed by a forced refresh.
-- shows with wxSTC 3.21 and both wxwidgets 2.9.5 and 3.1
@@ -989,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()
@@ -1009,7 +1077,10 @@ function CreateEditor()
firstvisible)
MarkupStyle(editor,minupdated or firstline,lastline)
editor.ev = {}
end)
editor:Connect(wx.wxEVT_IDLE,
function (event)
-- show auto-complete if needed
if editor.autocomplete then
EditorAutoComplete(editor)
@@ -1041,7 +1112,7 @@ function CreateEditor()
event:Skip()
end)
if ide.config.editor.nomousezoom then
if edcfg.nomousezoom then
-- disable zoom using mouse wheel as it triggers zooming when scrolling
-- on OSX with kinetic scroll and then pressing CMD.
editor:Connect(wx.wxEVT_MOUSEWHEEL,
@@ -1093,8 +1164,18 @@ function CreateEditor()
and (mod == wx.wxMOD_NONE) then
-- Delete and Backspace behave the same way for selected text
if #(editor:GetSelectedText()) > 0 then
editor:SetTargetStart(editor:GetSelectionStart())
editor:SetTargetEnd(editor:GetSelectionEnd())
local length = editor:GetLength()
local selections = ide.wxver >= "2.9.5" and editor:GetSelections() or 1
editor:Clear() -- remove selected fragments
-- check if the modification has failed, which may happen
-- if there is "invisible" text in the selected fragment.
-- if there is only one selection, then delete manually.
if length == editor:GetLength() and selections == 1 then
editor:SetTargetStart(editor:GetSelectionStart())
editor:SetTargetEnd(editor:GetSelectionEnd())
editor:ReplaceTarget("")
end
else
local pos = editor:GetCurrentPos()
if keycode == wx.WXK_BACK then
@@ -1114,12 +1195,16 @@ function CreateEditor()
editor:SetTargetStart(pos)
editor:SetTargetEnd(pos+1)
editor:ReplaceTarget("")
end
editor:ReplaceTarget("")
elseif mod == wx.wxMOD_ALT and keycode == wx.WXK_LEFT then
-- 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(
@@ -1135,7 +1220,7 @@ function CreateEditor()
local function selectAllInstances(instances, name, curpos)
local this
local idx = 0
for i, pos in pairs(instances) do
for _, pos in pairs(instances) do
pos = pos - 1 -- positions are 0-based in Scintilla
if idx == 0 then
-- clear selections first as there seems to be a bug (Scintilla 3.2.3)
@@ -1171,11 +1256,12 @@ function CreateEditor()
editor:Connect(wxstc.wxEVT_STC_ZOOM,
function(event)
editor:SetMarginWidth(margin.LINENUMBER, editor:TextWidth(DEFAULT_STYLE, "99999_"))
editor:SetMarginWidth(margin.LINENUMBER,
editor:TextWidth(wxstc.wxSTC_STYLE_DEFAULT, linenummask))
-- if Shift+Zoom is used, then zoom all editors, not just the current one
if wx.wxGetKeyState(wx.WXK_SHIFT) then
local zoom = editor:GetZoom()
for id, doc in pairs(openDocuments) do
for _, doc in pairs(openDocuments) do
-- check the editor zoom level to avoid recursion
if doc.editor:GetZoom() ~= zoom then doc.editor:SetZoom(zoom) end
end
@@ -1195,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") },
@@ -1207,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") },
@@ -1214,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)
@@ -1245,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)
@@ -1264,6 +1391,7 @@ end
-- ----------------------------------------------------------------------------
-- Add an editor to the notebook
function AddEditor(editor, name)
assert(notebook:GetPageIndex(editor) == -1, "Editor being added is not in the notebook: failed")
if notebook:AddPage(editor, name, true) then
local id = editor:GetId()
local document = setmetatable({}, ide.proto.Document)
@@ -1317,25 +1445,6 @@ function SetupKeywords(editor, ext, forcespec, styles, font, fontitalic)
end
end
if (spec.api == "lua") then
-- Get the items in the global "wx" table for autocompletion
if not wxkeywords then
local keyword_table = {}
for index in pairs(wx) do
table.insert(keyword_table, "wx."..index.." ")
end
for index in pairs(wxstc) do
table.insert(keyword_table, "wxstc."..index.." ")
end
table.sort(keyword_table)
wxkeywords = table.concat(keyword_table)
end
local offset = spec.keywords and #spec.keywords or 5
editor:SetKeyWords(offset, wxkeywords)
end
editor.api = GetApi(spec.apitype or "none")
editor.spec = spec
else
@@ -1348,10 +1457,10 @@ function SetupKeywords(editor, ext, forcespec, styles, font, fontitalic)
-- need to set folding property after lexer is set, otherwise
-- the folds are not shown (wxwidgets 2.9.5)
if ide.config.editor.fold then
if edcfg.fold then
editor:SetProperty("fold", "1")
editor:SetProperty("fold.html", "1")
editor:SetProperty("fold.compact", ide.config.editor.foldcompact and "1" or "0")
editor:SetProperty("fold.compact", edcfg.foldcompact and "1" or "0")
editor:SetProperty("fold.comment", "1")
end
@@ -1423,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

@@ -1,5 +1,7 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local ide = ide
--
-- filetree, treectrl for drive & project
@@ -8,8 +10,8 @@ local ide = ide
ide.filetree = {
projdir = "",
projdirlist = {},
projdirmap = {},
projdirpartmap = {},
projtree = nil,
}
local filetree = ide.filetree
@@ -20,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)
@@ -48,13 +47,14 @@ 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
while true do
local next = curr and tree:GetNextSibling(curr)
or tree:GetFirstChild(parent_id)
local next = (curr
and tree:GetNextSibling(curr)
or tree:GetFirstChild(parent_id))
if not next:IsOk() or name == tree:GetItemText(next) then
curr = next
break
@@ -62,25 +62,29 @@ local function treeAddDir(tree,parent_id,rootdir)
tree:Delete(next)
end
else -- new item
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
curr = (curr
and tree:InsertItem(parent_id, curr, name, icon)
or tree:PrependItem(parent_id, name, icon))
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
-- delete any leftovers (something that exists in the tree, but not on disk)
while true do
local next = curr and tree:GetNextSibling(curr)
or tree:GetFirstChild(parent_id)
local next = (curr
and tree:GetNextSibling(curr)
or tree:GetFirstChild(parent_id))
if not next:IsOk() then break end
tree:Delete(next)
end
-- cache the mapping from names to tree items
local data = wx.wxLuaTreeItemData()
data:SetData(cache)
tree:SetItemData(parent_id, data)
if ide.wxver >= "2.9.5" then
local data = wx.wxLuaTreeItemData()
data:SetData(cache)
tree:SetItemData(parent_id, data)
end
tree:SetItemHasChildren(parent_id,
tree:GetChildrenCount(parent_id, false) > 0)
@@ -90,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
@@ -134,7 +138,19 @@ 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, 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
@@ -166,9 +182,27 @@ local function treeSetConnectorsAndIcons(tree)
tree:SetEvtHandlerEnabled(true)
end
function tree:ActivateItem(item_id)
local name = tree:GetItemFullName(item_id)
local event = wx.wxTreeEvent(wx.wxEVT_COMMAND_TREE_ITEM_ACTIVATED, item_id:GetValue())
if PackageEventHandle("onFiletreeActivate", tree, event, item_id) == false then
return
end
-- refresh the folder
if (tree:IsDirectory(item_id)) then
if wx.wxDirExists(name) then treeAddDir(tree,item_id,name)
else refreshAncestors(tree:GetItemParent(item_id)) end -- stale content
else -- open file
if wx.wxFileExists(name) then LoadFile(name,nil,true)
else refreshAncestors(tree:GetItemParent(item_id)) end -- stale content
end
end
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)
@@ -177,9 +211,9 @@ local function treeSetConnectorsAndIcons(tree)
local docs = {}
if not isnew then -- find if source is already opened in the editor
docs = isdir
docs = (isdir
and ide:FindDocumentsByPartialPath(source)
or {ide:FindDocument(source)}
or {ide:FindDocument(source)})
for _, doc in ipairs(docs) do
if SaveModifiedDialog(doc.editor, true) == wx.wxID_CANCEL then return end
end
@@ -220,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)
@@ -231,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
@@ -261,54 +298,102 @@ local function treeSetConnectorsAndIcons(tree)
end)
tree:Connect(wx.wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
function (event)
local item_id = event:GetItem()
local name = tree:GetItemFullName(item_id)
-- refresh the folder
if (tree:GetItemImage(item_id) == IMG_DIRECTORY) then
if wx.wxDirExists(name) then treeAddDir(tree,item_id,name)
else refreshAncestors(tree:GetItemParent(item_id)) end -- stale content
else -- open file
if wx.wxFileExists(name) then LoadFile(name,nil,true)
else refreshAncestors(tree:GetItemParent(item_id)) end -- stale content
tree:ActivateItem(event:GetItem())
end)
-- handle context menu
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, img)
tree:SetItemHasChildren(parent, true)
-- temporarily disable expand as we don't need this node populated
tree:SetEvtHandlerEnabled(false)
tree:EnsureVisible(item)
tree:SetEvtHandlerEnabled(true)
return item
end
tree:Connect(ID_NEWFILE, wx.wxEVT_COMMAND_MENU_SELECTED,
function()
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, image.DIRECTORY))
end)
tree:Connect(ID_RENAMEFILE, wx.wxEVT_COMMAND_MENU_SELECTED,
function() tree:EditLabel(tree:GetSelection()) end)
tree:Connect(ID_DELETEFILE, wx.wxEVT_COMMAND_MENU_SELECTED,
function() deleteItem(tree:GetSelection()) end)
tree:Connect(ID_COPYFULLPATH, wx.wxEVT_COMMAND_MENU_SELECTED,
function()
local tdo = wx.wxTextDataObject(tree:GetItemFullName(tree:GetSelection()))
if wx.wxClipboard:Get():Open() then
wx.wxClipboard:Get():SetData(tdo)
wx.wxClipboard:Get():Close()
end
end)
-- handle context menu
tree:Connect(ID_OPENEXTENSION, wx.wxEVT_COMMAND_MENU_SELECTED,
function()
local fname = tree:GetItemFullName(tree:GetSelection())
local ext = '.'..wx.wxFileName(fname):GetExt()
local ft = wx.wxTheMimeTypesManager:GetFileTypeFromExtension(ext)
if ft then
local cmd = ft:GetOpenCommand(fname:gsub('"','\\"'))
local pid = wx.wxExecute(cmd, wx.wxEXEC_ASYNC)
if ide.osname == 'Windows' and pid and pid > 0 then
-- some programs on Windows (for example, PhotoViewer) accept
-- files with spaces in names ONLY if they are not in quotes.
-- wait for the process that failed to open file to finish
-- and retry without quotes.
wx.wxMilliSleep(250) -- 250ms seems enough; picked empirically.
if not wx.wxProcess.Exists(pid) then
local cmd = ft:GetOpenCommand(""):gsub('""%s*$', '')..fname
wx.wxExecute(cmd, wx.wxEXEC_ASYNC)
end
end
end
end)
tree:Connect(ID_SHOWLOCATION, wx.wxEVT_COMMAND_MENU_SELECTED,
function() ShowLocation(tree:GetItemFullName(tree:GetSelection())) end)
tree:Connect(wx.wxEVT_COMMAND_TREE_ITEM_MENU,
function (event)
local item_id = event:GetItem()
tree:SelectItem(item_id)
local renamelabel = (tree:IsRoot(item_id)
and TR("&Edit Project Directory")
or TR("&Rename"))
local menu = wx.wxMenu {
{ ID_NEWFILE, TR("New &File") },
{ ID_NEWDIRECTORY, TR("&New Directory") },
{ },
{ ID_RENAMEFILE, TR("&Rename")..KSC(ID_RENAMEFILE) },
{ ID_RENAMEFILE, renamelabel..KSC(ID_RENAMEFILE) },
{ ID_DELETEFILE, TR("&Delete")..KSC(ID_DELETEFILE) },
{ },
{ ID_OPENEXTENSION, TR("Open With Default Program") },
{ ID_COPYFULLPATH, TR("Copy Full Path") },
{ ID_SHOWLOCATION, TR("Show Location") },
}
local function addItem(item_id, name, image)
local isdir = tree:GetItemImage(item_id) == IMG_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)
tree:SetItemHasChildren(parent, true)
-- temporarily disable expand as we don't need this node populated
tree:SetEvtHandlerEnabled(false)
tree:EnsureVisible(item)
tree:SetEvtHandlerEnabled(true)
return item
end
local projectdirectorymenu = wx.wxMenu {
{ },
{ID_PROJECTDIRCHOOSE, TR("Choose...")..KSC(ID_PROJECTDIRCHOOSE), TR("Choose a project directory")},
}
local projectdirectory = wx.wxMenuItem(menu, ID_PROJECTDIR,
TR("Project Directory"), TR("Set the project directory to be used"),
wx.wxITEM_NORMAL, projectdirectorymenu)
menu:Insert(6, projectdirectory)
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)
@@ -317,71 +402,53 @@ local function treeSetConnectorsAndIcons(tree)
menu:Enable(ID_OPENEXTENSION, ft and #ft:GetOpenCommand("") > 0)
end
tree:Connect(ID_NEWFILE, wx.wxEVT_COMMAND_MENU_SELECTED,
function()
tree:EditLabel(addItem(item_id, empty, IMG_FILE_OTHER))
end)
tree:Connect(ID_NEWDIRECTORY, wx.wxEVT_COMMAND_MENU_SELECTED,
function()
tree:EditLabel(addItem(item_id, empty, IMG_DIRECTORY))
end)
tree:Connect(ID_RENAMEFILE, wx.wxEVT_COMMAND_MENU_SELECTED,
function() tree:EditLabel(item_id) end)
tree:Connect(ID_DELETEFILE, wx.wxEVT_COMMAND_MENU_SELECTED,
function() deleteItem(item_id) end)
tree:Connect(ID_COPYFULLPATH, wx.wxEVT_COMMAND_MENU_SELECTED,
function()
local tdo = wx.wxTextDataObject(tree:GetItemFullName(item_id))
if wx.wxClipboard:Get():Open() then
wx.wxClipboard:Get():SetData(tdo)
wx.wxClipboard:Get():Close()
end
end)
tree:Connect(ID_OPENEXTENSION, wx.wxEVT_COMMAND_MENU_SELECTED,
function()
local fname = tree:GetItemFullName(item_id)
local ext = '.'..wx.wxFileName(fname):GetExt()
local ft = wx.wxTheMimeTypesManager:GetFileTypeFromExtension(ext)
if ft then
local cmd = ft:GetOpenCommand(fname:gsub('"','\\"'))
local pid = wx.wxExecute(cmd, wx.wxEXEC_ASYNC)
if ide.osname == 'Windows' and pid and pid > 0 then
-- some programs on Windows (for example, PhotoViewer) accept
-- files with spaces in names ONLY if they are not in quotes.
-- wait for the process that failed to open file to finish
-- and retry without quotes.
wx.wxMilliSleep(250) -- 250ms seems enough; picked empirically.
if not wx.wxProcess.Exists(pid) then
local cmd = ft:GetOpenCommand(""):gsub('""%s*$', '')..fname
wx.wxExecute(cmd, wx.wxEXEC_ASYNC)
end
end
end
end)
tree:Connect(ID_SHOWLOCATION, wx.wxEVT_COMMAND_MENU_SELECTED,
function() ShowLocation(tree:GetItemFullName(item_id)) end)
PackageEventHandle("onMenuFiletree", menu, tree, event)
tree:PopupMenu(menu)
end)
tree:Connect(wx.wxEVT_RIGHT_DOWN,
function (event)
local item_id = tree:HitTest(event:GetPosition())
if PackageEventHandle("onFiletreeRDown", tree, event, item_id) == false then
return
end
event:Skip()
end)
-- toggle a folder on a single click
tree:Connect(wx.wxEVT_LEFT_DOWN,
function (event)
-- only toggle if this is a folder and the click is on the item line
-- (exclude the label as it's used for renaming and dragging)
local mask = wx.wxTREE_HITTEST_ONITEMINDENT
+ wx.wxTREE_HITTEST_ONITEMICON + wx.wxTREE_HITTEST_ONITEMRIGHT
local mask = (wx.wxTREE_HITTEST_ONITEMINDENT
+ wx.wxTREE_HITTEST_ONITEMICON + wx.wxTREE_HITTEST_ONITEMRIGHT)
local item_id, flags = tree:HitTest(event:GetPosition())
if item_id and tree:GetItemImage(item_id) == IMG_DIRECTORY
and bit.band(flags, mask) > 0 then
tree:Toggle(item_id)
tree:SelectItem(item_id)
if PackageEventHandle("onFiletreeLDown", tree, event, item_id) == false then
return
end
if item_id and bit.band(flags, mask) > 0 then
if tree:GetItemImage(item_id) == image.DIRECTORY then
tree:Toggle(item_id)
tree:SelectItem(item_id)
else
local name = tree:GetItemFullName(item_id)
if wx.wxFileExists(name) then LoadFile(name,nil,true) end
end
else
event:Skip()
end
return true
end)
local parent
tree:Connect(wx.wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT,
function (event)
local itemsrc = event:GetItem()
parent = tree:GetItemParent(itemsrc)
if not itemsrc:IsOk() then event:Veto() end
end)
tree:Connect(wx.wxEVT_COMMAND_TREE_END_LABEL_EDIT,
function (event)
-- veto the event to keep the original label intact as the tree
@@ -389,32 +456,30 @@ local function treeSetConnectorsAndIcons(tree)
event:Veto()
local itemsrc = event:GetItem()
if itemsrc == tree:GetRootItem() then return end -- don't edit root
if not itemsrc:IsOk() then return end
local sourcedir = tree:GetItemFullName(tree:GetItemParent(itemsrc))
local label = event:GetLabel():gsub("^%s+$","") -- clean all spaces
-- edited the root element; set the new project directory if needed
if tree:IsRoot(itemsrc) then
if not event:IsEditCancelled() and wx.wxDirExists(label) then
ProjectUpdateProjectDir(label)
end
return
end
if not parent or not parent:IsOk() then return end
local sourcedir = tree:GetItemFullName(parent)
local target = MergeFullPath(sourcedir, label)
if event:IsEditCancelled() or label == empty
or target and not renameItem(itemsrc, target)
then refreshAncestors(tree:GetItemParent(itemsrc)) end
end)
tree:Connect(wx.wxEVT_KEY_DOWN,
function (event)
local item = tree:GetSelection()
if item:IsOk() then
local keycode = event:GetKeyCode()
if keycode == wx.WXK_F2 then return tree:EditLabel(item)
elseif keycode == wx.WXK_DELETE then return deleteItem(item)
elseif keycode == wx.WXK_RETURN or keycode == wx.WXK_NUMPAD_ENTER then
tree:Toggle(item) end
end
event:Skip()
then refreshAncestors(parent) end
end)
local itemsrc
tree:Connect(wx.wxEVT_COMMAND_TREE_BEGIN_DRAG,
function (event)
if event:GetItem() ~= tree:GetRootItem() then
if ide.config.filetree.mousemove and tree:GetItemParent(event:GetItem()):IsOk() then
itemsrc = event:GetItem()
event:Allow()
end
@@ -422,7 +487,7 @@ local function treeSetConnectorsAndIcons(tree)
tree:Connect(wx.wxEVT_COMMAND_TREE_END_DRAG,
function (event)
local itemdst = event:GetItem()
if not itemdst:IsOk() then return end
if not itemdst:IsOk() or not itemsrc:IsOk() then return end
-- check if itemdst is a folder
local target = tree:GetItemFullName(itemdst)
@@ -436,83 +501,24 @@ local function treeSetConnectorsAndIcons(tree)
end
-- project
-- panel
-- (combobox, button)
-- (treectrl)
local projpanel = ide.frame.projpanel
local projcombobox = wx.wxComboBox(projpanel, ID "filetree.proj.drivecb",
filetree.projdir,
wx.wxDefaultPosition, wx.wxDefaultSize,
filetree.projdirlist, wx.wxTE_PROCESS_ENTER)
local projbutton = wx.wxButton(projpanel, ID_PROJECTDIRCHOOSE,
"...", wx.wxDefaultPosition, wx.wxSize(26,20))
local projtree = wx.wxTreeCtrl(projpanel, wx.wxID_ANY,
local projtree = wx.wxTreeCtrl(ide.frame, wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxDefaultSize,
wx.wxTR_HAS_BUTTONS + wx.wxTR_SINGLE + wx.wxTR_LINES_AT_ROOT
+ wx.wxTR_EDIT_LABELS)
-- use the same font in the combobox as is used in the filetree
projtree:SetFont(ide.font.fNormal)
projcombobox:SetFont(ide.font.fNormal)
filetree.projtree = projtree
local projTopSizer = wx.wxBoxSizer( wx.wxHORIZONTAL );
projTopSizer:Add(projcombobox, 1, wx.wxALL + wx.wxALIGN_LEFT + wx.wxGROW, 0)
projTopSizer:Add(projbutton, 0, wx.wxALL + wx.wxALIGN_RIGHT + wx.wxADJUST_MINSIZE + wx.wxALIGN_CENTER_VERTICAL, 0)
local projSizer = wx.wxBoxSizer( wx.wxVERTICAL );
projSizer:Add(projTopSizer, 0, wx.wxALL + wx.wxALIGN_CENTER_HORIZONTAL + wx.wxGROW, 0)
projSizer:Add(projtree, 1, wx.wxALL + wx.wxALIGN_LEFT + wx.wxGROW, 0)
projpanel:SetSizer(projSizer)
local projnotebook = ide.frame.projnotebook
projnotebook:AddPage(projtree, "Project", true)
-- proj connectors
-- ---------------
local inupdate = false
local function projcomboboxUpdate(event)
if inupdate then return end
local cur = projcombobox:GetValue()
local fn = wx.wxFileName(filetree.projdirmap[cur] or cur)
fn:Normalize()
-- on Windows, wxwidgets (2.9.5+) generates two COMMAND_COMBOBOX_SELECTED
-- events when the selection is done with ENTER, which causes recursive
-- call of updateProjectDir. To prevent this the second call is ignored.
inupdate = true
filetree:updateProjectDir(fn:GetFullPath())
inupdate = false
end
projpanel:Connect(ID "filetree.proj.drivecb", wx.wxEVT_COMMAND_COMBOBOX_SELECTED, projcomboboxUpdate)
projpanel:Connect(ID "filetree.proj.drivecb", wx.wxEVT_COMMAND_TEXT_ENTER, projcomboboxUpdate)
treeSetConnectorsAndIcons(projtree)
-- proj functions
-- ---------------
local function abbreviateProjList(projdirlist)
filetree.projdirmap = {}
local sep = "\t"
local dirs = table.concat(projdirlist, sep)..sep
local projlist = {}
for _, v in ipairs(projdirlist) do
-- using FileName because the path doesn't have trailing slash
local parts = wx.wxFileName(v..pathsep):GetDirs()
local name = table.remove(parts, #parts) or v
while #parts > 0
and select(2, dirs:gsub("%f[^".. pathsep .."]"..q(name)..sep, "")) > 1 do
name = table.remove(parts, #parts) .. pathsep .. name
end
local abbrev = ("%s (%s)"):format(name, v)
filetree.projdirmap[abbrev] = v
table.insert(projlist, abbrev)
end
return projlist
end
function filetree:updateProjectDir(newdir)
if (not newdir) or not wx.wxDirExists(newdir) then return end
local dirname = wx.wxFileName.DirName(newdir)
@@ -524,7 +530,8 @@ function filetree:updateProjectDir(newdir)
local newdir = dirname:GetPath(wx.wxPATH_GET_VOLUME)
if filetree.projdir and #filetree.projdir > 0 then
PackageEventHandle("onProjectClose", filetree.projdir) end
PackageEventHandle("onProjectClose", filetree.projdir)
end
PackageEventHandle("onProjectPreLoad", newdir)
@@ -540,32 +547,23 @@ function filetree:updateProjectDir(newdir)
newdir,
ide.config.projecthistorylength,
function(s1, s2) return dirname:SameAs(wx.wxFileName.DirName(s2)) end)
projcombobox:Clear()
projcombobox:Append(abbreviateProjList(filetree.projdirlist))
projcombobox:Select(0)
ProjectUpdateProjectDir(newdir,true)
treeSetRoot(projtree,newdir)
-- sync with the current editor window and activate selected file
local editor = GetEditor()
if (editor) then
local id = GetEditor():GetId()
if ide.openDocuments[id] then
FileTreeMarkSelected(ide.openDocuments[id].filePath)
end
end
if editor then FileTreeMarkSelected(ide:GetDocument(editor):GetFilePath()) end
-- refresh Recent Projects menu item
ide.frame:AddPendingEvent(wx.wxUpdateUIEvent(ID_RECENTPROJECTS))
PackageEventHandle("onProjectLoad", newdir)
end
projpanel.projbutton = projbutton
projpanel.projcombobox = projcombobox
projpanel.projtree = projtree
function FileTreeGetDir()
return filetree.projdir and #filetree.projdir > 0
and wx.wxFileName.DirName(filetree.projdir):GetFullPath()
return (filetree.projdir and #filetree.projdir > 0
and wx.wxFileName.DirName(filetree.projdir):GetFullPath() or nil)
end
function FileTreeSetProjects(tab)
@@ -579,11 +577,60 @@ function FileTreeGetProjects()
return filetree.projdirlist
end
local function getProjectLabels()
local labels = {}
local fmt = ide.config.format.menurecentprojects or '%f'
for _, proj in ipairs(FileTreeGetProjects()) do
local config = ide.session.projects[proj]
local intfname = config and config[2] and config[2].interpreter or ide.interpreter:GetFileName()
local interpreter = intfname and ide.interpreters[intfname]
local parts = wx.wxFileName(proj..pathsep):GetDirs()
table.insert(labels, ExpandPlaceholders(fmt, {
f = proj,
i = interpreter and interpreter:GetName() or '?',
s = parts[#parts] or '',
}))
end
return labels
end
function FileTreeProjectListClear()
-- remove all items from the list except the current one
filetree.projdirlist = {FileTreeGetDir()}
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)
local label = list[i]
if i <= items then -- this is an existing item; update the label
menu:FindItem(id):SetItemLabel(label)
else -- need to add an item
local item = wx.wxMenuItem(menu, id, label, "")
menu:Insert(items, item)
ide.frame:Connect(id, wx.wxEVT_COMMAND_MENU_SELECTED, function()
wx.wxSafeYield() -- let the menu on screen (if any) disappear
ProjectUpdateProjectDir(FileTreeGetProjects()[i])
end)
end
-- disable the currently selected project
if i == 1 then menu:Enable(id, false) end
end
for i=items, #list+1, -1 do -- delete the rest if the list got shorter
menu:Delete(menu:FindItemByPosition(i-1))
end
return #list
end
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
@@ -591,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
@@ -603,7 +650,8 @@ function FileTreeMarkSelected(file)
end
curr_file = file
if ide.wxver < "2.9.5" and ide.osname == 'Macintosh' then
projtree:Refresh() end
projtree:Refresh()
end
end
end

View File

@@ -1,6 +1,8 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Lomtik Software (J. Winwood & John Labenski)
-- Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local ide = ide
ide.findReplace = {
dialog = nil, -- the wxDialog for find/replace
@@ -16,6 +18,8 @@ ide.findReplace = {
fSubDirs = true, -- search in subdirectories
fMakeBak = true, -- make bak files for replace in files
buttons = {},
findTextArray = {}, -- array of last entered find text
findText = "", -- string to find
replaceTextArray = {}, -- array of last entered replace text
@@ -39,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
@@ -141,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
@@ -186,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)
@@ -280,7 +291,7 @@ local function ProcInFiles(startdir,mask,subdirs,replace)
if replace then
-- check if anything replaced, store changed content, make .bak
if findReplace:ReplaceString(true,onFileRegister)
and findReplace.fMakeBak and FileWrite(file..".bak",filetext) then
and (not findReplace.fMakeBak or FileWrite(file..".bak",filetext)) then
FileWrite(file,findReplace.oveditor:GetText())
end
else
@@ -288,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
@@ -303,7 +314,7 @@ function findReplace:RunInFiles(replace)
if not findReplace:HasText() then return end
findReplace.oveditor = wxstc.wxStyledTextCtrl(findReplace.dialog, wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxSize(1,1), wx.wxBORDER_STATIC)
wx.wxDefaultPosition, wx.wxSize(1,1), wx.wxBORDER_NONE)
findReplace.occurrences = 0
ActivateOutput()
@@ -544,6 +555,7 @@ function findReplace:createDialog(replace,infiles)
function()
TransferDataFromWindow()
if (findReplace.infiles) then
for _, b in pairs(findReplace.buttons) do b:Disable() end
findReplace:RunInFiles()
findReplace.dialog:Destroy()
findReplace.dialog = nil
@@ -558,6 +570,7 @@ function findReplace:createDialog(replace,infiles)
event:Skip()
if findReplace.replace then
if (findReplace.infiles) then
for _, b in pairs(findReplace.buttons) do b:Disable() end
findReplace:RunInFiles(true)
findReplace.dialog:Destroy()
findReplace.dialog = nil
@@ -588,24 +601,32 @@ function findReplace:createDialog(replace,infiles)
if res == wx.wxID_OK then
infilesDirCombo:SetValue(FixDir(filePicker:GetPath()))
end
-- when the dropdown is used to select the directory on OSX,
-- the find dialog moves to the background; this keeps it on top.
if ide.osname == 'Macintosh' then findDialog:Raise() end
end)
end
-- if on OSX then select the current value of the default dropdown
-- and don't set the default as it doesn't make Enter to work, but
-- prevents associated hotkey (Cmd-F) from working (wx2.9.5).
if mac then
findTextCombo:SetSelection(-1, -1)
findTextCombo:SetFocus() -- force focus on the Find
else
findButton:SetDefault()
end
-- reset search when re-creating dialog to avoid modifying selected
-- fragment after successful search and updated replacement
findReplace.foundString = false
findReplace.dialog = findDialog
findDialog:Show(true)
-- if on OSX then select the current value of the default dropdown
-- and don't set the default as it doesn't make Enter to work, but
-- prevents associated hotkey (Cmd-F) from working (wx2.9.5).
-- SetFocus has to be done after :Show on OSX as it doesn't put
-- the focus on the Find field on some instances of OSX 10.9.2.
if mac then
findTextCombo:SetSelection(-1, -1)
findTextCombo:SetFocus() -- force focus on the Find
else
findButton:SetDefault()
end
findReplace.buttons = {findButton, replaceButton}
return findDialog
end

View File

@@ -1,7 +1,10 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Luxinia Dev (Eike Decker & Christoph Kubisch)
-- Lomtik Software (J. Winwood & John Labenski)
---------------------------------------------------------
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)
@@ -51,11 +54,11 @@ local function createFrame()
local statusBar = frame:CreateStatusBar(6)
local section_width = statusBar:GetTextExtent("OVRW")
statusBar:SetStatusStyles({wx.wxSB_FLAT, wx.wxSB_FLAT, wx.wxSB_FLAT,
wx.wxSB_FLAT, wx.wxSB_FLAT, wx.wxSB_FLAT})
wx.wxSB_FLAT, wx.wxSB_FLAT, wx.wxSB_FLAT})
statusBar:SetStatusWidths(
{-1, section_width*6, section_width, section_width, section_width*5, section_width*4})
statusBar:SetStatusText(GetIDEString("statuswelcome"))
local mgr = wxaui.wxAuiManager()
mgr:SetManagedWindow(frame)
@@ -71,41 +74,76 @@ local function SCinB(id) -- shortcut in brackets
return shortcut and #shortcut > 0 and (" ("..shortcut..")") or ""
end
local function menuDropDownPosition(event)
local tb = event:GetEventObject():DynamicCast('wxAuiToolBar')
local rect = tb:GetToolRect(event:GetId())
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 = frame:CreateToolBar(wx.wxTB_FLAT + wx.wxTB_NODIVIDER, wx.wxID_ANY)
local toolBar = wxaui.wxAuiToolBar(frame, wx.wxID_ANY, wx.wxDefaultPosition, wx.wxDefaultSize,
wxaui.wxAUI_TB_PLAIN_BACKGROUND)
-- wxChoice is a bit too narrow on Linux, so make it a bit larger
local funclist = wx.wxChoice.new(toolBar, ID "toolBar.funclist",
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: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_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_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)
toolBar:Connect(ID_OPEN, wxaui.wxEVT_COMMAND_AUITOOLBAR_TOOL_DROPDOWN, function(event)
if event:IsDropDownClicked() then
local menu = wx.wxMenu()
FileRecentListUpdate(menu)
toolBar:PopupMenu(menu, menuDropDownPosition(event))
else
event:Skip()
end
end)
toolBar:SetToolDropDown(ID_PROJECTDIRCHOOSE, true)
toolBar:Connect(ID_PROJECTDIRCHOOSE, wxaui.wxEVT_COMMAND_AUITOOLBAR_TOOL_DROPDOWN, function(event)
if event:IsDropDownClicked() then
local menu = wx.wxMenu()
FileTreeProjectListUpdate(menu, 0)
toolBar:PopupMenu(menu, menuDropDownPosition(event))
else
event:Skip()
end
end)
toolBar:GetArtProvider():SetElementSize(wxaui.wxAUI_TBART_GRIPPER_SIZE, 0)
toolBar:Realize()
toolBar.funclist = funclist
frame.toolBar = toolBar
return toolBar
@@ -114,9 +152,9 @@ end
local function createNotebook(frame)
-- notebook for editors
local notebook = wxaui.wxAuiNotebook(frame, wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxDefaultSize,
wxaui.wxAUI_NB_DEFAULT_STYLE + wxaui.wxAUI_NB_TAB_EXTERNAL_MOVE
+ wxaui.wxAUI_NB_WINDOWLIST_BUTTON + wx.wxNO_BORDER)
wx.wxDefaultPosition, wx.wxDefaultSize,
wxaui.wxAUI_NB_DEFAULT_STYLE + wxaui.wxAUI_NB_TAB_EXTERNAL_MOVE
+ wxaui.wxAUI_NB_WINDOWLIST_BUTTON + wx.wxNO_BORDER)
-- wxEVT_SET_FOCUS could be used, but it only works on Windows with wx2.9.5+
notebook:Connect(wxaui.wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED,
@@ -137,13 +175,6 @@ local function createNotebook(frame)
and debug:traceback():find("'AddPage'"))
if doc and event:GetOldSelection() ~= -1 and not double then
-- switching between editor tabs doesn't trigger KILL_FOCUS events
-- on OSX (http://trac.wxwidgets.org/ticket/14142); trigger manually
if ide.osname == 'Macintosh' then
local win = notebook:GetPage(event:GetOldSelection())
local ev = wx.wxFocusEvent(wx.wxEVT_KILL_FOCUS)
win:GetEventHandler():ProcessEvent(ev)
end
SetEditorSelection(notebook:GetSelection())
end
end)
@@ -154,6 +185,24 @@ local function createNotebook(frame)
event:Veto() -- don't propagate the event as the page is already closed
end)
notebook:Connect(wxaui.wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK,
function (event)
-- as this event can be on different tab controls,
-- need to find the control to add the page to
local tabctrl = event:GetEventObject():DynamicCast("wxAuiTabCtrl")
-- check if the active page is in the current control
local active = tabctrl:GetActivePage()
if (active >= 0 and tabctrl:GetPage(active).window
~= notebook:GetPage(notebook:GetSelection())) then
-- if not, need to activate the control that was clicked on;
-- find the last window and switch to it (assuming there is always one)
assert(tabctrl:GetPageCount() >= 1, "Expected at least one page in a notebook tab control.")
local lastwin = tabctrl:GetPage(tabctrl:GetPageCount()-1).window
notebook:SetSelection(notebook:GetPageIndex(lastwin))
end
NewFile()
end)
-- tabs can be dragged around which may change their indexes;
-- when this happens stored indexes need to be updated to reflect the change.
-- there is DRAG_DONE event that I'd prefer to use, but it
@@ -191,6 +240,7 @@ local function createNotebook(frame)
{ ID_SAVE, TR("&Save") },
{ ID_SAVEAS, TR("Save &As...") },
{ },
{ ID_COPYFULLPATH, TR("Copy Full Path") },
{ ID_SHOWLOCATION, TR("Show Location") },
}
@@ -202,7 +252,7 @@ local function createNotebook(frame)
local function IfAtLeastOneTab(event)
event:Enable(notebook:GetPageCount() > 0)
if ide.osname == 'Macintosh' and (event:GetId() == ID_CLOSEALL
or event:GetId() == ID_CLOSE and notebook:GetPageCount() <= 1)
or event:GetId() == ID_CLOSE and notebook:GetPageCount() <= 1)
then event:Enable(false) end
end
local function IfModified(event) event:Enable(EditorIsModified(GetEditor(selection))) end
@@ -235,6 +285,14 @@ local function createNotebook(frame)
end)
notebook:Connect(ID_SHOWLOCATION, wx.wxEVT_UPDATE_UI, IfAtLeastOneTab)
notebook:Connect(ID_COPYFULLPATH, wx.wxEVT_COMMAND_MENU_SELECTED, function()
local tdo = wx.wxTextDataObject(ide:GetDocument(GetEditor(selection)):GetFilePath())
if wx.wxClipboard:Get():Open() then
wx.wxClipboard:Get():SetData(tdo)
wx.wxClipboard:Get():Close()
end
end)
frame.notebook = notebook
return notebook
end
@@ -267,7 +325,7 @@ local function createBottomNotebook(frame)
local mouse = wx.wxGetMouseState()
local mouseatpoint = wx.wxPoint(mouse:GetX(), mouse:GetY())
local ok, tabs = pcall(function() return wx.wxFindWindowAtPoint(mouseatpoint):DynamicCast("wxAuiTabCtrl") end)
tabs:SetNoneActive()
if ok then tabs:SetNoneActive() end
event:Allow()
end
@@ -329,8 +387,10 @@ local function createBottomNotebook(frame)
local label = bottomnotebook:GetPageText(selection)
-- names are translated on labels, so need to translate here as well
local dragout = ({[TR("Watch")] = DebuggerAddWatchWindow,
[TR("Stack")] = DebuggerAddStackWindow})[label]
local dragout = ({
[TR("Watch")] = DebuggerAddWatchWindow,
[TR("Stack")] = DebuggerAddStackWindow,
})[label]
if not dragout then return end
bottomnotebook:RemovePage(selection)
@@ -341,26 +401,60 @@ local function createBottomNotebook(frame)
mgr:Update()
end)
-- disallow tabs closing
bottomnotebook:Connect(wxaui.wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE,
function (event) event:Veto() end)
local errorlog = wxstc.wxStyledTextCtrl(bottomnotebook, wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxDefaultSize, wx.wxBORDER_STATIC)
wx.wxDefaultPosition, wx.wxDefaultSize, wx.wxBORDER_NONE)
errorlog:Connect(wx.wxEVT_CONTEXT_MENU,
function (event)
errorlog:PopupMenu(
wx.wxMenu {
{ ID_UNDO, TR("&Undo") },
{ ID_REDO, TR("&Redo") },
{ },
{ ID_CUT, TR("Cu&t") },
{ ID_COPY, TR("&Copy") },
{ ID_PASTE, TR("&Paste") },
{ ID_SELECTALL, TR("Select &All") },
{ },
{ ID_CLEAROUTPUT, TR("C&lear Output Window") },
}
)
end)
errorlog:Connect(ID_CLEAROUTPUT, wx.wxEVT_COMMAND_MENU_SELECTED,
function(event)
ClearOutput()
end)
local shellbox = wxstc.wxStyledTextCtrl(bottomnotebook, wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxDefaultSize, wx.wxBORDER_STATIC)
wx.wxDefaultPosition, wx.wxDefaultSize, wx.wxBORDER_NONE)
bottomnotebook:AddPage(errorlog, TR("Output"), true)
bottomnotebook:AddPage(shellbox, TR("Local console"), false)
frame.bottomnotebook = bottomnotebook
bottomnotebook.errorlog = errorlog
bottomnotebook.shellbox = shellbox
frame.bottomnotebook = bottomnotebook
return bottomnotebook
end
local function createProjpanel(frame)
local projpanel = wx.wxPanel(frame,wx.wxID_ANY)
frame.projpanel = projpanel
return projpanel
local function createProjNotebook(frame)
local projnotebook = wxaui.wxAuiNotebook(frame, wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxDefaultSize,
wxaui.wxAUI_NB_DEFAULT_STYLE
- wxaui.wxAUI_NB_CLOSE_ON_ACTIVE_TAB + wx.wxNO_BORDER)
-- disallow tabs closing
projnotebook:Connect(wxaui.wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE,
function (event) event:Veto() end)
frame.projnotebook = projnotebook
return projnotebook
end
-- ----------------------------------------------------------------------------
@@ -370,26 +464,40 @@ local frame = createFrame()
ide.frame = frame
createToolBar(frame)
createNotebook(frame)
createProjpanel(frame)
createProjNotebook(frame)
createBottomNotebook(frame)
do
local frame = ide.frame
local mgr = frame.uimgr
mgr:AddPane(frame.toolBar, wxaui.wxAuiPaneInfo():
Name("toolbar"):Caption("Toolbar"):
ToolbarPane():Top():CloseButton(false):PaneBorder(false):
LeftDockable(false):RightDockable(false))
mgr:AddPane(frame.notebook, wxaui.wxAuiPaneInfo():
Name("notebook"):
CenterPane():PaneBorder(false))
mgr:AddPane(frame.projpanel, wxaui.wxAuiPaneInfo():
Name("projpanel"):Caption(TR("Project")):
MinSize(200,200):FloatingSize(200,400):
Left():Layer(1):Position(1):
CloseButton(true):MaximizeButton(false):PinButton(true))
Name("notebook"):
CenterPane():PaneBorder(false))
mgr:AddPane(frame.projnotebook, wxaui.wxAuiPaneInfo():
Name("projpanel"):CaptionVisible(false):Caption(TR("Project")):
MinSize(200,200):FloatingSize(200,400):
Left():Layer(1):Position(1):PaneBorder(false):
CloseButton(true):MaximizeButton(false):PinButton(true))
mgr:AddPane(frame.bottomnotebook, wxaui.wxAuiPaneInfo():
Name("bottomnotebook"):
MinSize(100,100):BestSize(200,200):FloatingSize(400,200):
Bottom():Layer(1):Position(1):
CloseButton(true):MaximizeButton(false):PinButton(true))
Name("bottomnotebook"):CaptionVisible(false):
MinSize(100,100):BestSize(200,200):FloatingSize(400,200):
Bottom():Layer(1):Position(1):PaneBorder(false):
CloseButton(true):MaximizeButton(false):PinButton(true))
for _, uimgr in pairs {mgr, frame.notebook:GetAuiManager(),
frame.bottomnotebook:GetAuiManager(), frame.projnotebook:GetAuiManager()} do
uimgr:GetArtProvider():SetMetric(wxaui.wxAUI_DOCKART_SASH_SIZE, 2)
end
for _, nb in pairs {frame.bottomnotebook, frame.projnotebook} do
nb:Connect(wxaui.wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK,
function() PaneFloatToggle(nb) end)
end
mgr.defaultPerspective = mgr:SavePerspective()
end

View File

@@ -1,3 +1,4 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Lomtik Software (J. Winwood & John Labenski)
-- Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
@@ -13,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
@@ -30,8 +32,12 @@ ID_SAVE = linux and NewID() or wx.wxID_SAVE
ID_SAVEAS = linux and NewID() or wx.wxID_SAVEAS
ID_SAVEALL = NewID()
ID_RECENTFILES = NewID()
ID_RECENTFILESCLEAR = NewID()
ID_RECENTFILESPREV = NewID()
ID_RECENTFILESNEXT = NewID()
ID_RECENTPROJECTS = NewID()
ID_RECENTPROJECTSCLEAR = NewID()
ID_RECENTPROJECTSPREV = NewID()
ID_EXIT = linux and NewID() or wx.wxID_EXIT
-- Edit menu
ID_CUT = linux and NewID() or wx.wxID_CUT
@@ -46,6 +52,12 @@ ID_AUTOCOMPLETEENABLE = NewID()
ID_COMMENT = NewID()
ID_FOLD = NewID()
ID_CLEARDYNAMICWORDS = NewID()
ID_SOURCE = NewID()
ID_REINDENT = NewID()
ID_BOOKMARK = NewID()
ID_BOOKMARKTOGGLE = NewID()
ID_BOOKMARKNEXT = NewID()
ID_BOOKMARKPREV = NewID()
-- don't use wx.wxID_PREFERENCES to avoid merging with OSX app menu, because
-- Apple guidelines describe Preferences as a "normal" item without submenus.
ID_PREFERENCES = NewID()
@@ -67,9 +79,15 @@ ID_VIEWFILETREE = NewID()
ID_VIEWOUTPUT = NewID()
ID_VIEWCALLSTACK = NewID()
ID_VIEWWATCHWINDOW = NewID()
ID_VIEWTOOLBAR = NewID()
ID_VIEWSTATUSBAR = NewID()
ID_VIEWDEFAULTLAYOUT = NewID()
ID_VIEWFULLSCREEN = NewID()
ID_VIEWMINIMIZE = NewID()
ID_ZOOM = NewID()
ID_ZOOMRESET = NewID()
ID_ZOOMIN = NewID()
ID_ZOOMOUT = NewID()
-- Project menu
ID_TOGGLEBREAKPOINT = NewID()
ID_COMPILE = NewID()
@@ -79,6 +97,7 @@ ID_RUNNOW = NewID()
ID_ATTACHDEBUG = NewID()
ID_STARTDEBUG = NewID()
ID_STOPDEBUG = NewID()
ID_DETACHDEBUG = NewID()
ID_STEP = NewID()
ID_STEPOVER = NewID()
ID_STEPOUT = NewID()
@@ -105,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

@@ -1,5 +1,6 @@
-- Copyright 2012-14 Paul Kulchenko, ZeroBrane LLC
-- Integration with LuaInspect
-- (C) 2012 Paul Kulchenko
---------------------------------------------------------
local M, LA, LI, T = {}
local FAST = true
@@ -7,7 +8,11 @@ local FAST = true
local function init()
if LA then return end
require "metalua"
-- metalua is using 'checks', which noticeably slows the execution
-- stab it with out own
package.loaded.checks = {}
checks = function() end
LA = require "luainspect.ast"
LI = require "luainspect.init"
T = require "luainspect.types"
@@ -33,34 +38,45 @@ function M.warnings_from_string(src, file)
LI.mark_related_keywords(ast, tokenlist, src)
end
return M.show_warnings(ast)
local globinit = {}
local spec = GetSpec(wx.wxFileName(file):GetExt())
for k in pairs(spec and GetApi(spec.apitype or "none").ac.childs or {}) do
globinit[k] = true
end
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 M.show_warnings(top_ast)
function AnalyzeString(src)
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)
local warnings = {}
local function warn(msg, linenum, path)
warnings[#warnings+1] = (path or "?") .. "(" .. (linenum or 0) .. "): " .. msg
warnings[#warnings+1] = (path or "?") .. ":" .. (linenum or 0) .. ": " .. msg
end
local function known(o) return not T.istype[o] end
local function index(f) -- build abc.def.xyz name recursively
return (f[1].tag == 'Id' and f[1][1] or index(f[1])) .. '.' .. f[2][1] end
local isseen, globseen, fieldseen = {}, {}, {}
local globseen, isseen, fieldseen = globinit or {}, {}, {}
LA.walk(top_ast, function(ast)
local line = ast.lineinfo and ast.lineinfo.first[1] or 0
local path = ast.lineinfo and ast.lineinfo.first[4] or '?'
local path, line = tostring(ast.lineinfo):gsub('<C|','<'):match('<([^|]+)|L(%d+)')
local name = ast[1]
-- check if we're masking a variable in the same scope
if ast.localmasking and name ~= '_' and
ast.level == ast.localmasking.level then
local linenum = ast.localmasking.lineinfo.first[1]
local linenum = tostring(ast.localmasking.lineinfo.first):match('|L(%d+)')
local parent = ast.parent and ast.parent.parent
local func = parent and parent.tag == 'Localrec'
warn("local " .. (func and 'function' or 'variable') .. " '" ..
@@ -119,7 +135,7 @@ function M.show_warnings(top_ast)
and (" in '"..index(ast.parent):gsub("%."..name.."$","").."'")
or ""
warn("first use of unknown field '" .. name .."'"..parent,
ast.lineinfo.first[1], path)
tostring(ast.lineinfo.first):match('|L(%d+)'), path)
end
elseif ast.tag == 'Id' and not ast.localdefinition and not ast.definedglobal then
if not globseen[name] then
@@ -160,10 +176,9 @@ function M.show_warnings(top_ast)
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
@@ -172,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
@@ -197,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

@@ -1,5 +1,7 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local ide = ide
ide.iofilters["GermanUtf8Ascii"] = {

View File

@@ -32,9 +32,10 @@ ide.config.keymap = {
[ID_SAVEAS] = "Alt-Shift-S",
[ID_SAVEALL] = "",
[ID_RECENTFILES] = "",
[ID_RECENTFILESPREV] = "Ctrl-<",
[ID_RECENTFILESNEXT] = "Ctrl->",
[ID_RECENTFILESPREV] = "Ctrl-,",
[ID_RECENTFILESNEXT] = "Ctrl-.",
[ID_EXIT] = "Ctrl-Q",
[ID_RECENTPROJECTSPREV] = "Ctrl-Shift-<",
-- Edit menu
[ID_CUT] = "Ctrl-X",
[ID_COPY] = "Ctrl-C",
@@ -48,6 +49,10 @@ ide.config.keymap = {
[ID_COMMENT] = "Ctrl-U",
[ID_FOLD] = "F12",
[ID_CLEARDYNAMICWORDS] = "",
[ID_REINDENT] = "Ctrl-I",
[ID_BOOKMARKTOGGLE] = "Ctrl-F2",
[ID_BOOKMARKNEXT] = "F2",
[ID_BOOKMARKPREV] = "Shift-F2",
-- Search menu
[ID_FIND] = "Ctrl-F",
[ID_FINDNEXT] = "F3",
@@ -66,6 +71,9 @@ ide.config.keymap = {
[ID_VIEWCALLSTACK] = "Ctrl-Shift-S",
[ID_VIEWDEFAULTLAYOUT] = "",
[ID_VIEWFULLSCREEN] = "Ctrl-Shift-A",
[ID_ZOOMRESET] = "Ctrl-0",
[ID_ZOOMIN] = "Ctrl-+",
[ID_ZOOMOUT] = "Ctrl--",
-- Project menu
[ID_RUN] = "F6",
[ID_RUNNOW] = "Ctrl-F6",
@@ -118,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

@@ -1,5 +1,6 @@
-- Copyright (C) Paul Kulchenko 2011-2012
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- styles for comment markup
---------------------------------------------------------
local MD_MARK_ITAL = '_' -- italic
local MD_MARK_BOLD = '**' -- bold
@@ -14,7 +15,7 @@ local MD_MARK_MARK = ' ' -- separator
local MD_LINK_NEWWINDOW = '+' -- indicator to open a new window for links
local markup = {
[MD_MARK_BOXD] = {st=25, fg={127,0,127}, b=true},
[MD_MARK_CODE] = {st=26, fg={127,127,127}, fs=9},
[MD_MARK_CODE] = {st=26, fg={127,127,127}, fs=10},
[MD_MARK_HEAD] = {st=27, fn="Lucida Console", b=true},
[MD_MARK_LINK] = {st=28, u=true, hs={32,32,127}},
[MD_MARK_BOLD] = {st=29, b=true},
@@ -31,7 +32,8 @@ function MarkupAddStyles(styles)
local style = styles[key] or {}
-- copy all style features by value
for feature in pairs(value) do
style[feature] = style[feature] or value[feature] end
style[feature] = style[feature] or value[feature]
end
style.fg = style.fg or comment.fg
style.bg = style.bg or comment.bg
styles[key] = style

View File

@@ -1,6 +1,8 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Lomtik Software (J. Winwood & John Labenski)
-- Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local ide = ide
-- ---------------------------------------------------------------------------
-- Create the Edit menu and attach the callback functions
@@ -8,7 +10,7 @@ local ide = ide
local frame = ide.frame
local menuBar = frame.menuBar
local editMenu = wx.wxMenu{
local editMenu = wx.wxMenu {
{ ID_CUT, TR("Cu&t")..KSC(ID_CUT), TR("Cut selected text to clipboard") },
{ ID_COPY, TR("&Copy")..KSC(ID_COPY), TR("Copy selected text to clipboard") },
{ ID_PASTE, TR("&Paste")..KSC(ID_PASTE), TR("Paste text from the clipboard") },
@@ -21,72 +23,72 @@ local editMenu = wx.wxMenu{
{ ID_AUTOCOMPLETE, TR("Complete &Identifier")..KSC(ID_AUTOCOMPLETE), TR("Complete the current identifier") },
{ ID_AUTOCOMPLETEENABLE, TR("Auto Complete Identifiers")..KSC(ID_AUTOCOMPLETEENABLE), TR("Auto complete while typing"), wx.wxITEM_CHECK },
{ },
{ ID_COMMENT, TR("C&omment/Uncomment")..KSC(ID_COMMENT), TR("Comment or uncomment current or selected lines") },
{ },
{ ID_FOLD, TR("&Fold/Unfold All")..KSC(ID_FOLD), TR("Fold or unfold all code folds") },
{ ID_CLEARDYNAMICWORDS, TR("Clear &Dynamic Words")..KSC(ID_CLEARDYNAMICWORDS), TR("Resets the dynamic word list for autocompletion") },
{ ID_SORT, TR("&Sort")..KSC(ID_SORT), TR("Sort selected lines") },
{ },
}
local preferencesMenu = wx.wxMenu{
{ID_PREFERENCESSYSTEM, TR("Settings: System")..KSC(ID_PREFERENCESSYSTEM)},
{ID_PREFERENCESUSER, TR("Settings: User")..KSC(ID_PREFERENCESUSER)},
}
editMenu:Append(ID_PREFERENCES, TR("Preferences"), preferencesMenu)
editMenu:Append(ID_SOURCE, TR("Source"), wx.wxMenu {
{ ID_COMMENT, TR("&Comment/Uncomment")..KSC(ID_COMMENT), TR("Comment or uncomment current or selected lines") },
{ ID_REINDENT, TR("Correct &Indentation")..KSC(ID_REINDENT), TR("Re-indent selected lines") },
{ ID_FOLD, TR("&Fold/Unfold All")..KSC(ID_FOLD), TR("Fold or unfold all code folds") },
{ ID_SORT, TR("&Sort")..KSC(ID_SORT), TR("Sort selected lines") },
})
editMenu:Append(ID_BOOKMARK, TR("Bookmark"), wx.wxMenu {
{ ID_BOOKMARKTOGGLE, TR("Toggle Bookmark")..KSC(ID_BOOKMARKTOGGLE) },
{ ID_BOOKMARKNEXT, TR("Go To Next Bookmark")..KSC(ID_BOOKMARKNEXT) },
{ ID_BOOKMARKPREV, TR("Go To Previous Bookmark")..KSC(ID_BOOKMARKPREV) },
})
editMenu:AppendSeparator()
editMenu:Append(ID_PREFERENCES, TR("Preferences"), wx.wxMenu {
{ ID_PREFERENCESSYSTEM, TR("Settings: System")..KSC(ID_PREFERENCESSYSTEM) },
{ ID_PREFERENCESUSER, TR("Settings: User")..KSC(ID_PREFERENCESUSER) },
})
menuBar:Append(editMenu, TR("&Edit"))
editMenu:Check(ID_AUTOCOMPLETEENABLE, ide.config.autocomplete)
local function getControlWithFocus()
local editor = GetEditor()
for _,e in pairs({frame.bottomnotebook.shellbox, frame.bottomnotebook.errorlog}) do
local ctrl = e:FindFocus()
if ctrl and
(ctrl:GetId() == e:GetId()
or ide.osname == 'Macintosh' and
ctrl:GetParent():GetId() == e:GetId()) then editor = e end
end
return editor or nil
local function onUpdateUIEditorInFocus(event)
event:Enable(GetEditorWithFocus(GetEditor()) ~= nil)
end
local function onUpdateUIEditMenu(event)
local editor = getControlWithFocus()
local editor = GetEditorWithFocus()
if editor == nil then event:Enable(false); return end
local cancomment = pcall(function() return editor.spec end) and editor.spec
and editor.spec.linecomment and true or false
local alwaysOn = { [ID_SELECTALL] = true, [ID_FOLD] = ide.config.editor.fold,
local alwaysOn = {
[ID_SELECTALL] = true,
-- allow Cut and Copy commands as these work on a line if no selection
[ID_COPY] = true, [ID_CUT] = true,
[ID_COMMENT] = cancomment, [ID_AUTOCOMPLETE] = true, [ID_SORT] = true}
}
local menu_id = event:GetId()
local enable =
menu_id == ID_PASTE and editor:CanPaste() or
menu_id == ID_UNDO and editor:CanUndo() or
menu_id == ID_REDO and editor:CanRedo() or
alwaysOn[menu_id]
-- wxComboBox doesn't have SELECT ALL, so disable it
-- editor:GetClassInfo mysteriously fails on Ubuntu 13.10 (earlier versions
-- are okay), which indicates that the menu item is checked after editor
-- is already closed, so the first pcall() check should protect against that.
if pcall(function() editor:GetId() end)
and editor:GetClassInfo():GetClassName() == 'wxComboBox'
and menu_id == ID_SELECTALL then enable = false end
event:Enable(enable)
end
function OnEditMenu(event)
local editor = getControlWithFocus()
local function onEditMenu(event)
local editor = GetEditorWithFocus()
if editor == nil then event:Skip(); return end
-- if there is no editor, or if it's not the editor we care about,
-- then allow normal processing to take place
if editor == nil or
(editor:FindFocus() and editor:FindFocus():GetId() ~= editor:GetId()) or
editor:GetClassInfo():GetClassName() ~= 'wxStyledTextCtrl'
then event:Skip(); return end
if PackageEventHandle("onEditorAction", editor, event) == false then
return
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
@@ -98,13 +100,36 @@ 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
frame:Connect(event, wx.wxEVT_COMMAND_MENU_SELECTED, OnEditMenu)
frame:Connect(event, wx.wxEVT_COMMAND_MENU_SELECTED, onEditMenu)
frame:Connect(event, wx.wxEVT_UPDATE_UI, onUpdateUIEditMenu)
end
for _, event in pairs({
ID_BOOKMARKTOGGLE, ID_BOOKMARKNEXT, ID_BOOKMARKPREV,
ID_AUTOCOMPLETE, ID_SORT, ID_REINDENT, ID_SHOWTOOLTIP,
}) do
frame:Connect(event, wx.wxEVT_UPDATE_UI, onUpdateUIEditorInFocus)
end
frame:Connect(ID_FOLD, wx.wxEVT_UPDATE_UI,
function(event)
local editor = GetEditorWithFocus(GetEditor())
event:Enable(ide.config.editor.fold and editor ~= nil)
end)
frame:Connect(ID_COMMENT, wx.wxEVT_UPDATE_UI,
function(event)
local editor = GetEditorWithFocus(GetEditor())
event:Enable(editor ~= nil
and pcall(function() return editor.spec end) and editor.spec
and editor.spec.linecomment and true or false)
end)
local function generateConfigMessage(type)
return ([==[--[[--
Use this file to specify %s preferences.
@@ -145,19 +170,12 @@ frame:Connect(ID_SHOWTOOLTIP, wx.wxEVT_COMMAND_MENU_SELECTED,
EditorCallTip(editor, editor:GetCurrentPos())
end)
frame:Connect(ID_SHOWTOOLTIP, wx.wxEVT_UPDATE_UI,
function (event) event:Enable(GetEditor() ~= nil) end)
frame:Connect(ID_AUTOCOMPLETE, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
EditorAutoComplete(GetEditor())
end)
frame:Connect(ID_AUTOCOMPLETE, wx.wxEVT_UPDATE_UI, onUpdateUIEditMenu)
function (event) EditorAutoComplete(GetEditor()) end)
frame:Connect(ID_AUTOCOMPLETEENABLE, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
ide.config.autocomplete = event:IsChecked()
end)
function (event) ide.config.autocomplete = event:IsChecked() end)
frame:Connect(ID_COMMENT, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
@@ -219,28 +237,136 @@ frame:Connect(ID_COMMENT, wx.wxEVT_COMMAND_MENU_SELECTED,
+ math.max(0, curpos+#editor:GetLine(curline)-curlen))
end
end)
frame:Connect(ID_COMMENT, wx.wxEVT_UPDATE_UI, onUpdateUIEditMenu)
local function processSelection(editor, func)
local text = editor:GetSelectedText()
local line = editor:GetCurrentLine()
local posinline = editor:GetCurrentPos() - editor:PositionFromLine(line)
if #text == 0 then
editor:SelectAll()
text = editor:GetSelectedText()
end
local wholeline = text:find('\n$')
local buf = {}
for line in string.gmatch(text..(wholeline and '' or '\n'), "(.-\r?\n)") do
table.insert(buf, line)
end
if #buf > 0 then
if func then func(buf) end
-- add new line at the end if it was there
local newtext = table.concat(buf, ''):gsub('(\r?\n)$', wholeline and '%1' or '')
-- straightforward editor:ReplaceSelection() doesn't work reliably as
-- it sometimes doubles the context when the entire file is selected.
-- this seems like Scintilla issue, so use ReplaceTarget instead.
-- Since this doesn't work with rectangular selection, which
-- ReplaceSelection should handle (after wxwidgets 3.x upgrade), this
-- will need to be revisited when ReplaceSelection is updated.
if newtext ~= text then
editor:TargetFromSelection()
editor:ReplaceTarget(newtext)
end
end
editor:GotoPosEnforcePolicy(math.min(
editor:PositionFromLine(line)+posinline, editor:GetLineEndPosition(line)))
end
frame:Connect(ID_SORT, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event) processSelection(GetEditor(), table.sort) end)
local function reIndent(editor, buf)
local decindent, incindent = editor.spec.isdecindent, editor.spec.isincindent
if not (decindent and incindent) then return end
local line = editor:LineFromPosition(editor:GetSelectionStart())
local indent = 0
local text = ''
-- find the last non-empty line in the previous block (if any)
for n = line-1, 1, -1 do
indent = editor:GetLineIndentation(n)
text = editor:GetLine(n)
if text:match('[^\r\n]') then break end
end
local ut = editor:GetUseTabs()
local tw = ut and editor:GetTabWidth() or editor:GetIndent()
local indents = {}
local isstatic = {}
for line = 1, #buf+1 do
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
-- 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
end
indents[line] = indent
text = buf[line]
end
for line = 1, #buf do
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
frame:Connect(ID_REINDENT, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
local editor = GetEditor()
local buf = {}
for line in string.gmatch(editor:GetSelectedText()..'\n', "(.-)\r?\n") do
table.insert(buf, line)
end
if #buf > 0 then
local newline
if #(buf[#buf]) == 0 then newline = table.remove(buf) end
table.sort(buf)
-- add new line at the end if it was there
if newline then table.insert(buf, newline) end
editor:ReplaceSelection(table.concat(buf,"\n"))
end
processSelection(editor, function(buf) reIndent(editor, buf) end)
end)
frame:Connect(ID_SORT, wx.wxEVT_UPDATE_UI, onUpdateUIEditMenu)
frame:Connect(ID_FOLD, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
FoldSome()
end)
frame:Connect(ID_FOLD, wx.wxEVT_UPDATE_UI, onUpdateUIEditMenu)
function (event) FoldSome() end)
local BOOKMARK_MARKER = StylesGetMarker("bookmark")
local BOOKMARK_MARKER_VALUE = 2^BOOKMARK_MARKER
local function bookmarkToggle()
local editor = GetEditor()
local line = editor:GetCurrentLine()
local markers = editor:MarkerGet(line)
if bit.band(markers, BOOKMARK_MARKER_VALUE) > 0 then
editor:MarkerDelete(line, BOOKMARK_MARKER)
else
editor:MarkerAdd(line, BOOKMARK_MARKER)
end
end
local function bookmarkNext()
local editor = GetEditor()
local line = editor:MarkerNext(editor:GetCurrentLine()+1, BOOKMARK_MARKER_VALUE)
if line == -1 then line = editor:MarkerNext(0, BOOKMARK_MARKER_VALUE) end
if line ~= -1 then
editor:GotoLine(line)
editor:EnsureVisibleEnforcePolicy(line)
end
end
local function bookmarkPrev()
local editor = GetEditor()
local line = editor:MarkerPrevious(editor:GetCurrentLine()-1, BOOKMARK_MARKER_VALUE)
if line == -1 then line = editor:MarkerPrevious(editor:GetLineCount(), BOOKMARK_MARKER_VALUE) end
if line ~= -1 then
editor:GotoLine(line)
editor:EnsureVisibleEnforcePolicy(line)
end
end
frame:Connect(ID_BOOKMARKTOGGLE, wx.wxEVT_COMMAND_MENU_SELECTED, bookmarkToggle)
frame:Connect(ID_BOOKMARKNEXT, wx.wxEVT_COMMAND_MENU_SELECTED, bookmarkNext)
frame:Connect(ID_BOOKMARKPREV, wx.wxEVT_COMMAND_MENU_SELECTED, bookmarkPrev)

View File

@@ -1,6 +1,8 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Lomtik Software (J. Winwood & John Labenski)
-- Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local ide = ide
local frame = ide.frame
local menuBar = frame.menuBar
@@ -16,16 +18,27 @@ local fileMenu = wx.wxMenu({
{ ID_SAVEAS, TR("Save &As...")..KSC(ID_SAVEAS), TR("Save the current document to a file with a new name") },
{ ID_SAVEALL, TR("Save A&ll")..KSC(ID_SAVEALL), TR("Save all open documents") },
{ },
-- placeholder for ID_RECENTFILES
-- placeholder for ID_RECENTFILES and ID_RECENTPROJECTS
{ },
{ ID_EXIT, TR("E&xit")..KSC(ID_EXIT), TR("Exit program") }})
menuBar:Append(fileMenu, TR("&File"))
local filehistorymenu = wx.wxMenu({})
local filehistorymenu = wx.wxMenu({
{ },
{ ID_RECENTFILESCLEAR, TR("Clear Items")..KSC(ID_RECENTFILESCLEAR), TR("Clear items from this list") },
})
local filehistory = wx.wxMenuItem(fileMenu, ID_RECENTFILES,
TR("Recent Files")..KSC(ID_RECENTFILES), TR("File history"), wx.wxITEM_NORMAL, filehistorymenu)
fileMenu:Insert(8,filehistory)
local projecthistorymenu = wx.wxMenu({
{ },
{ ID_RECENTPROJECTSCLEAR, TR("Clear Items")..KSC(ID_RECENTPROJECTSCLEAR), TR("Clear items from this list") },
})
local projecthistory = wx.wxMenuItem(fileMenu, ID_RECENTPROJECTS,
TR("Recent &Projects")..KSC(ID_RECENTPROJECTS), TR("Project history"), wx.wxITEM_NORMAL, projecthistorymenu)
fileMenu:Insert(9,projecthistory)
do -- recent file history
local iscaseinsensitive = wx.wxFileName("A"):SameAs(wx.wxFileName("a"))
local function isSameAs(f1, f2)
@@ -116,8 +129,11 @@ do -- recent file history
end
end
local items = 0
updateRecentFiles = function (list)
local items = filehistorymenu:GetMenuItemCount()
-- 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)
@@ -129,13 +145,14 @@ do -- recent file history
filehistorymenu:FindItem(id):SetItemLabel(label)
else -- need to add an item
local item = wx.wxMenuItem(filehistorymenu, id, label, "")
filehistorymenu:Append(item)
filehistorymenu:Insert(i-1, item)
frame:Connect(id, wx.wxEVT_COMMAND_MENU_SELECTED, loadRecent)
end
end
for i=items, #list+1, -1 do -- delete the rest if the list got shorter
filehistorymenu:Delete(filehistorymenu:FindItemByPosition(i-1))
end
items = #list -- update the number of items for the next refresh
-- enable if there are any recent files
fileMenu:Enable(ID_RECENTFILES, #list > 0)
@@ -152,6 +169,17 @@ do -- recent file history
addFileHistory(filename)
updateRecentFiles(filehistory)
end
function FileRecentListUpdate(menu)
local list = filehistory
for i=#list, 1, -1 do
local id = ID("file.recentfiles."..i)
local label = list[i].filename
local item = wx.wxMenuItem(menu, id, label, "")
menu:Insert(0, item)
ide.frame:Connect(id, wx.wxEVT_COMMAND_MENU_SELECTED, loadRecent)
end
end
end
frame:Connect(ID_NEW, wx.wxEVT_COMMAND_MENU_SELECTED, function() return NewFile() end)
@@ -202,6 +230,28 @@ 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)
frame:Connect(ID_RECENTPROJECTSCLEAR, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event) FileTreeProjectListClear() end)
frame:Connect(ID_RECENTFILESCLEAR, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
SetFileHistory({})
local ed = ide:GetEditor()
if ed then AddToFileHistory(ide:GetDocument(ed):GetFilePath()) end
end)
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)
item:SetItemLabel(item:GetItemLabelText()..KSC(ID_RECENTPROJECTSPREV))
end
event:Enable(recentprojects > 0)
end)

View File

@@ -1,5 +1,6 @@
-- author: Paul Kulchenko
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
---------------------------------------------------------
local ide = ide
-- ---------------------------------------------------------------------------
-- Create the Help menu and attach the callback functions
@@ -45,7 +46,7 @@ local function DisplayAbout(event)
<tr>
<td>
<b>ZeroBrane Studio (%s; MobDebug %s)</b><br>
<b>Copyright &copy; 2011-2013 ZeroBrane LLC</b><br>
<b>Copyright &copy; 2011-2014 ZeroBrane LLC</b><br>
Paul Kulchenko<br>
Licensed under the MIT License.
</td>

View File

@@ -1,6 +1,8 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Lomtik Software (J. Winwood & John Labenski)
-- Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local ide = ide
local frame = ide.frame
local menuBar = frame.menuBar
@@ -17,9 +19,10 @@ local debugTab = {
{ ID_RUNNOW, TR("Run as Scratchpad")..KSC(ID_RUNNOW), TR("Execute the current project/file and keep updating the code to see immediate results"), wx.wxITEM_CHECK },
{ ID_COMPILE, TR("&Compile")..KSC(ID_COMPILE), TR("Compile the current file") },
{ ID_STARTDEBUG, TR("Start &Debugging")..KSC(ID_STARTDEBUG), TR("Start or continue debugging") },
{ ID_ATTACHDEBUG, TR("&Start Debugger Server")..KSC(ID_ATTACHDEBUG), TR("Allow external process to start debugging") },
{ ID_ATTACHDEBUG, TR("&Start Debugger Server")..KSC(ID_ATTACHDEBUG), TR("Allow external process to start debugging"), wx.wxITEM_CHECK },
{ },
{ ID_STOPDEBUG, TR("S&top Debugging")..KSC(ID_STOPDEBUG), TR("Stop the currently running process") },
{ ID_DETACHDEBUG, TR("Detach &Process")..KSC(ID_DETACHDEBUG), TR("Stop debugging and continue running the process") },
{ ID_STEP, TR("Step &Into")..KSC(ID_STEP), TR("Step into") },
{ ID_STEPOVER, TR("Step &Over")..KSC(ID_STEPOVER), TR("Step over") },
{ ID_STEPOUT, TR("Step O&ut")..KSC(ID_STEPOUT), TR("Step out of the current function") },
@@ -127,6 +130,7 @@ function ProjectUpdateProjectDir(projdir,skiptree)
ide.config.path.projectdir = projdir ~= "" and projdir or nil
frame:SetStatusText(projdir)
frame:SetTitle(ExpandPlaceholders(ide.config.format.apptitle))
if (not skiptree) then
ide.filetree:updateProjectDir(projdir)
end
@@ -255,9 +259,9 @@ frame:Connect(ID_TOGGLEBREAKPOINT, wx.wxEVT_COMMAND_MENU_SELECTED,
end)
frame:Connect(ID_TOGGLEBREAKPOINT, wx.wxEVT_UPDATE_UI,
function (event)
local editor = GetEditor()
local editor = GetEditorWithFocus(GetEditor())
event:Enable((ide.interpreter) and (ide.interpreter.hasdebugger) and (editor ~= nil)
and (not debugger.running) and (not debugger.scratchpad))
and (not debugger.scratchpad))
end)
frame:Connect(ID_COMPILE, wx.wxEVT_COMMAND_MENU_SELECTED,
@@ -281,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)
@@ -296,18 +300,28 @@ 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,
function ()
if (ide.interpreter.fattachdebug) then ide.interpreter:fattachdebug() end
function (event)
if event:IsChecked() then
if (ide.interpreter.fattachdebug) then ide.interpreter:fattachdebug() end
else
debugger.listen(false) -- stop listening
end
end)
frame:Connect(ID_ATTACHDEBUG, wx.wxEVT_UPDATE_UI,
function (event)
local editor = GetEditor()
event:Enable((ide.interpreter) and (ide.interpreter.fattachdebug)
and (not debugger.listening) and (debugger.server == nil)
and (editor ~= nil) and (not debugger.scratchpad))
event:Enable(ide.interpreter and ide.interpreter.fattachdebug and true or false)
ide.frame.menuBar:Check(event:GetId(), debugger.listening and true or false)
end)
frame:Connect(ID_STARTDEBUG, wx.wxEVT_COMMAND_MENU_SELECTED, function () ProjectDebug() end)
@@ -337,10 +351,16 @@ frame:Connect(ID_STOPDEBUG, wx.wxEVT_UPDATE_UI,
end
end)
frame:Connect(ID_STEP, wx.wxEVT_COMMAND_MENU_SELECTED,
function ()
debugger.step()
frame:Connect(ID_DETACHDEBUG, wx.wxEVT_COMMAND_MENU_SELECTED,
function () debugger.detach() end)
frame:Connect(ID_DETACHDEBUG, wx.wxEVT_UPDATE_UI,
function (event)
event:Enable((debugger.server ~= nil) and (not debugger.running)
and (not debugger.scratchpad))
end)
frame:Connect(ID_STEP, wx.wxEVT_COMMAND_MENU_SELECTED,
function () debugger.step() end)
frame:Connect(ID_STEP, wx.wxEVT_UPDATE_UI,
function (event)
local editor = GetEditor()
@@ -349,9 +369,7 @@ frame:Connect(ID_STEP, wx.wxEVT_UPDATE_UI,
end)
frame:Connect(ID_STEPOVER, wx.wxEVT_COMMAND_MENU_SELECTED,
function ()
debugger.over()
end)
function () debugger.over() end)
frame:Connect(ID_STEPOVER, wx.wxEVT_UPDATE_UI,
function (event)
local editor = GetEditor()
@@ -360,9 +378,7 @@ frame:Connect(ID_STEPOVER, wx.wxEVT_UPDATE_UI,
end)
frame:Connect(ID_STEPOUT, wx.wxEVT_COMMAND_MENU_SELECTED,
function ()
debugger.out()
end)
function () debugger.out() end)
frame:Connect(ID_STEPOUT, wx.wxEVT_UPDATE_UI,
function (event)
local editor = GetEditor()
@@ -371,9 +387,7 @@ frame:Connect(ID_STEPOUT, wx.wxEVT_UPDATE_UI,
end)
frame:Connect(ID_TRACE, wx.wxEVT_COMMAND_MENU_SELECTED,
function ()
debugger.trace()
end)
function () debugger.trace() end)
frame:Connect(ID_TRACE, wx.wxEVT_UPDATE_UI,
function (event)
local editor = GetEditor()
@@ -415,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

@@ -1,6 +1,8 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Lomtik Software (J. Winwood & John Labenski)
-- Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local ide = ide
-- Create the Search menu and attach the callback functions
@@ -20,7 +22,7 @@ local findMenu = wx.wxMenu{
{ ID_FINDINFILES, TR("Find &In Files")..KSC(ID_FINDINFILES), TR("Find text in files") },
{ ID_REPLACEINFILES, TR("Re&place In Files")..KSC(ID_REPLACEINFILES), TR("Find and replace text in files") },
{ },
{ ID_GOTOLINE, TR("&Goto Line")..KSC(ID_GOTOLINE), TR("Go to a selected line") },
{ ID_GOTOLINE, TR("&Go To Line...")..KSC(ID_GOTOLINE), TR("Go to a selected line") },
}
menuBar:Append(findMenu, TR("&Search"))
@@ -136,7 +138,7 @@ frame:Connect(ID_GOTOLINE, wx.wxEVT_COMMAND_MENU_SELECTED,
local linemax = editor:LineFromPosition(editor:GetLength()) + 1
local linenum = wx.wxGetNumberFromUser(TR("Enter line number"),
"1 .. "..tostring(linemax),
TR("Goto Line"),
TR("Go To Line"),
linecur, 1, linemax,
frame)
if linenum > 0 then

View File

@@ -1,5 +1,7 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local ide = ide
local frame = ide.frame
local menuBar = frame.menuBar
@@ -46,7 +48,7 @@ do
local exec = tool.exec
if (exec and cnt < maxcnt and exec.name and exec.fn and exec.description) then
local id = ID("tools.exec."..tool.fname)
table.insert(toolArgs,{id , exec.name, exec.description})
table.insert(toolArgs,{id, exec.name, exec.description})
-- flag it
tool._execid = id
cnt = cnt + 1

View File

@@ -1,6 +1,8 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Lomtik Software (J. Winwood & John Labenski)
-- Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local ide = ide
local frame = ide.frame
local menuBar = frame.menuBar
@@ -12,33 +14,62 @@ local viewMenu = wx.wxMenu {
{ ID_VIEWWATCHWINDOW, TR("&Watch Window")..KSC(ID_VIEWWATCHWINDOW), TR("View the watch window"), wx.wxITEM_CHECK },
{ ID_VIEWCALLSTACK, TR("&Stack Window")..KSC(ID_VIEWCALLSTACK), TR("View the stack window"), wx.wxITEM_CHECK },
{ },
{ ID_VIEWTOOLBAR, TR("&Tool Bar")..KSC(ID_VIEWTOOLBAR), TR("Show/Hide the toolbar"), wx.wxITEM_CHECK },
{ ID_VIEWSTATUSBAR, TR("&Status Bar")..KSC(ID_VIEWSTATUSBAR), TR("Show/Hide the status bar"), wx.wxITEM_CHECK },
{ },
{ ID_VIEWDEFAULTLAYOUT, TR("&Default Layout")..KSC(ID_VIEWDEFAULTLAYOUT), TR("Reset to default layout") },
{ ID_VIEWFULLSCREEN, TR("Full &Screen")..KSC(ID_VIEWFULLSCREEN), TR("Switch to or from full screen mode") },
}
do -- Add zoom submenu
local zoomMenu = wx.wxMenu{
{ID_ZOOMRESET, TR("Zoom to 100%")..KSC(ID_ZOOMRESET)},
{ID_ZOOMIN, TR("Zoom In")..KSC(ID_ZOOMIN)},
{ID_ZOOMOUT, TR("Zoom Out")..KSC(ID_ZOOMOUT)},
}
frame:Connect(ID_ZOOMRESET, wx.wxEVT_COMMAND_MENU_SELECTED,
function() local editor = GetEditorWithFocus()
if editor then editor:SetZoom(0) end end)
frame:Connect(ID_ZOOMIN, wx.wxEVT_COMMAND_MENU_SELECTED,
function() local editor = GetEditorWithFocus()
if editor then editor:SetZoom(editor:GetZoom()+1) end end)
frame:Connect(ID_ZOOMOUT, wx.wxEVT_COMMAND_MENU_SELECTED,
function() local editor = GetEditorWithFocus()
if editor then editor:SetZoom(editor:GetZoom()-1) end end)
-- only enable if there is an editor
local iseditor = function (event) event:Enable(GetEditorWithFocus() ~= nil) end
for _, id in ipairs({ID_ZOOMRESET, ID_ZOOMIN, ID_ZOOMOUT}) do
frame:Connect(id, wx.wxEVT_UPDATE_UI, iseditor)
end
viewMenu:Append(ID_ZOOM, TR("Zoom"), zoomMenu)
end
menuBar:Append(viewMenu, TR("&View"))
local panels = {
[ID_VIEWOUTPUT] = "bottomnotebook",
[ID_VIEWFILETREE] = "projpanel",
[ID_VIEWWATCHWINDOW] = "watchpanel",
[ID_VIEWCALLSTACK] = "stackpanel"
[ID_VIEWCALLSTACK] = "stackpanel",
[ID_VIEWTOOLBAR] = "toolbar",
}
local function togglePanel(event)
local panel = panels[event:GetId()]
local mgr = ide.frame.uimgr
local shown = not mgr:GetPane(panel):IsShown()
mgr:GetPane(panel):Show(shown)
mgr:Update()
local shown = not uimgr:GetPane(panel):IsShown()
uimgr:GetPane(panel):Show(shown)
uimgr:Update()
return shown
end
local function checkPanel(event)
local menubar = ide.frame.menuBar
local pane = ide.frame.uimgr:GetPane(panels[event:GetId()])
menubar:Enable(event:GetId(), pane:IsOk()) -- disable if doesn't exist
menubar:Check(event:GetId(), pane:IsOk() and pane:IsShown())
local pane = uimgr:GetPane(panels[event:GetId()])
menuBar:Enable(event:GetId(), pane:IsOk()) -- disable if doesn't exist
menuBar:Check(event:GetId(), pane:IsOk() and pane:IsShown())
end
frame:Connect(ID_VIEWDEFAULTLAYOUT, wx.wxEVT_COMMAND_MENU_SELECTED,
@@ -50,16 +81,25 @@ frame:Connect(ID_VIEWMINIMIZE, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event) ide.frame:Iconize(true) end)
frame:Connect(ID_VIEWFULLSCREEN, wx.wxEVT_COMMAND_MENU_SELECTED, function ()
pcall(function() ShowFullScreen(not frame:IsFullScreen()) end)
ShowFullScreen(not frame:IsFullScreen())
end)
frame:Connect(ID_VIEWFULLSCREEN, wx.wxEVT_UPDATE_UI,
function (event) event:Enable(GetEditor() ~= nil) end)
frame:Connect(ID_VIEWOUTPUT, wx.wxEVT_COMMAND_MENU_SELECTED, togglePanel)
frame:Connect(ID_VIEWFILETREE, wx.wxEVT_COMMAND_MENU_SELECTED, togglePanel)
frame:Connect(ID_VIEWTOOLBAR, wx.wxEVT_COMMAND_MENU_SELECTED, togglePanel)
frame:Connect(ID_VIEWWATCHWINDOW, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event) if togglePanel(event) then DebuggerRefreshPanels() end end)
frame:Connect(ID_VIEWCALLSTACK, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event) if togglePanel(event) then DebuggerRefreshPanels() end end)
frame:Connect(ID_VIEWSTATUSBAR, wx.wxEVT_COMMAND_MENU_SELECTED,
function (event)
frame:GetStatusBar():Show(menuBar:IsChecked(event:GetId()))
uimgr:Update()
end)
frame:Connect(ID_VIEWSTATUSBAR, wx.wxEVT_UPDATE_UI,
function (event) menuBar:Check(event:GetId(), frame:GetStatusBar():IsShown()) end)
for id in pairs(panels) do frame:Connect(id, wx.wxEVT_UPDATE_UI, checkPanel) end

View File

@@ -1,6 +1,8 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Lomtik Software (J. Winwood & John Labenski)
-- Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local ide = ide
local frame = ide.frame
local notebook = frame.notebook
@@ -23,6 +25,13 @@ errorlog:SetMarginType(1, wxstc.wxSTC_MARGIN_SYMBOL)
errorlog:MarkerDefine(StylesGetMarker("message"))
errorlog:MarkerDefine(StylesGetMarker("prompt"))
errorlog:SetReadOnly(true)
if (ide.config.outputshell.usewrap) then
errorlog:SetWrapMode(wxstc.wxSTC_WRAP_WORD)
errorlog:SetWrapStartIndent(0)
errorlog:SetWrapVisualFlags(wxstc.wxSTC_WRAPVISUALFLAG_END)
errorlog:SetWrapVisualFlagsLocation(wxstc.wxSTC_WRAPVISUALFLAGLOC_END_BY_TEXT)
end
StylesApplyToEditor(ide.config.stylesoutshell,errorlog,ide.font.oNormal,ide.font.oItalic)
function ClearOutput()
@@ -91,7 +100,8 @@ function CommandLineToShell(uid,state)
end
-- logic to "unhide" wxwidget window using winapi
pcall(function () return require 'winapi' end)
pcall(require, 'winapi')
local checkstart, checknext, checkperiod
local pid = nil
local function unHideWindow(pidAssign)
-- skip if not configured to do anything
@@ -100,6 +110,18 @@ local function unHideWindow(pidAssign)
pid = pidAssign > 0 and pidAssign or nil
end
if pid and winapi then
local now = TimeGet()
if pidAssign and pidAssign > 0 then
checkstart, checknext, checkperiod = now, now, 0.02
end
if now - checkstart > 1 and checkperiod < 0.5 then
checkperiod = checkperiod * 2
end
if now >= checknext then
checknext = now + checkperiod
else
return
end
local wins = winapi.find_all_windows(function(w)
return w:get_process():get_pid() == pid
end)
@@ -284,7 +306,7 @@ errorlog:Connect(wx.wxEVT_END_PROCESS, function(event)
end
customprocs[pid] = nil
unHideWindow(0)
DebuggerStop()
DebuggerStop(true)
DisplayOutputLn(TR("Program completed in %.2f seconds (pid: %d).")
:format(runtime, pid))
end
@@ -292,7 +314,7 @@ errorlog:Connect(wx.wxEVT_END_PROCESS, function(event)
errorlog:Connect(wx.wxEVT_IDLE, function()
if (#streamins or #streamerrs) then getStreams() end
unHideWindow()
if ide.osname == 'Windows' then unHideWindow() end
end)
local jumptopatterns = {
@@ -318,32 +340,44 @@ errorlog:Connect(wxstc.wxEVT_STC_DOUBLECLICK,
if (fname and jumpline) then break end
end
if (fname and jumpline) then
local name = GetFullPathIfExists(FileTreeGetDir(), fname)
or FileTreeFindByPartialName(fname)
if not (fname and jumpline) then return end
-- fname may include name of executable, as in "path/to/lua: file.lua";
-- strip it and try to find match again if needed
local fixedname = fname:match(": (.+)")
if not name and fixedname then
name = GetFullPathIfExists(FileTreeGetDir(), fixedname)
or FileTreeFindByPartialName(fixedname)
end
-- 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)
local editor = LoadFile(name or fname,nil,true)
if (editor) then
jumpline = tonumber(jumpline)
jumplinepos = tonumber(jumplinepos)
editor:GotoPos(editor:PositionFromLine(math.max(0,jumpline-1))
+ (jumplinepos and (math.max(0,jumplinepos-1)) or 0))
editor:SetFocus()
-- doubleclick can set selection, so reset it
errorlog:SetSelection(event:GetPosition(), event:GetPosition())
event:Skip()
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)
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)
end)
local function positionInLine(line)

View File

@@ -1,4 +1,5 @@
-- Copyright 2013 Paul Kulchenko, ZeroBrane LLC
-- Copyright 2013-14 Paul Kulchenko, ZeroBrane LLC
---------------------------------------------------------
local ide = ide
local iscaseinsensitive = wx.wxFileName("A"):SameAs(wx.wxFileName("a"))
@@ -46,7 +47,9 @@ end
function PackageUnRegister(file, ...)
PackageEventHandleOne(file, "onUnRegister", ...)
-- remove from the list of installed packages
local package = ide.packages[file]
ide.packages[file] = nil
return package
end
function PackageRegister(file, ...)
@@ -70,21 +73,29 @@ function ide:GetPackagePath(packname)
end
function ide:GetApp() return self.editorApp end
function ide:GetEditor(index) return GetEditor(index) end
function ide:GetEditorWithFocus(ed) return GetEditorWithFocus(ed) end
function ide:GetMenuBar() return self.frame.menuBar end
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
@@ -112,11 +123,16 @@ function ide:FindDocumentsByPartialPath(path)
return docs
end
function ide:GetInterpreter() return self.interpreter end
function ide:GetInterpreters() return ide.interpreters end
function ide:GetInterpreters() return self.interpreters end
function ide:GetConfig() return self.config end
function ide:GetOutput() return self.frame.bottomnotebook.errorlog end
function ide:GetEditorNotebook() return self.frame.notebook end
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.stackCtrl end
function ide:Yield() wx.wxYield() end
function ide:GetSetting(path, setting)
local settings = self.settings
@@ -127,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()
@@ -173,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

@@ -1,8 +1,10 @@
-- Copyright 2013 Paul Kulchenko, ZeroBrane LLC
-- Copyright 2013-14 Paul Kulchenko, ZeroBrane LLC
---------------------------------------------------------
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,
@@ -23,3 +25,10 @@ ide.proto.Interpreter = {__index = {
GetName = function(self) return self.name end,
GetFileName = function(self) return self.fname end,
}}
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

@@ -1,12 +1,15 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Lomtik Software (J. Winwood & John Labenski)
-- Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
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)
@@ -389,19 +392,40 @@ 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
local panes = frame.uimgr:GetAllPanes()
for _, name in pairs({"stackpanel", "watchpanel"}) do
local pane = frame.uimgr:GetPane(name)
if pane:IsOk() and not layout:find(name) then pane:Float() end
end
-- unfortunately need to explicitly (re-)assign the caption,
-- as it's going to be restored from the config regardless of how
-- it is set now (which affects its translation)
uimgr:GetPane("projpanel"):Caption(TR("Project"))
-- check if the toolbar is not mentioned in the layout and show it
for _, name in pairs({"toolbar"}) do
local pane = frame.uimgr:GetPane(name)
if pane:IsOk() and not layout:find(name) then pane:Show() end
end
-- remove captions from all panes
local panes = frame.uimgr:GetAllPanes()
for index = 0, panes:GetCount()-1 do
uimgr:GetPane(panes:Item(index).name):CaptionVisible(false)
end
end
frame:GetStatusBar():Show(settingsReadSafe(settings,"statusbar",true))
uimgr:Update()
local layoutcur = saveNotebook(frame.bottomnotebook)
@@ -430,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
@@ -445,9 +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("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

@@ -1,5 +1,7 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local ide = ide
local unpack = table.unpack or unpack
--
@@ -284,22 +286,23 @@ local function packResults(status, ...) return status, {...} end
local function executeShellCode(tx)
if tx == nil or tx == '' then return end
local forcelocalprefix = '^!'
local forcelocal = tx:find(forcelocalprefix)
tx = tx:gsub(forcelocalprefix, '')
DisplayShellPrompt('')
-- try to compile as statement
local _, err = loadstring(tx)
local isstatement = not err
if remotesend then remotesend(tx, isstatement); return end
if remotesend and not forcelocal then remotesend(tx, isstatement); return end
local addedret, forceexpression = true, tx:match("^%s*=%s*")
tx = tx:gsub("^%s*=%s*","")
local fn
fn, err = loadstring("return "..tx)
if not forceexpression and err and
(err:find("'?<eof>'? expected near '") or
err:find("'%(' expected near") or
err:find("unexpected symbol near '")) then
if not forceexpression and err then
fn, err = loadstring(tx)
addedret = false
end
@@ -374,11 +377,12 @@ function ShellExecuteCode(code)
end
local function displayShellIntro()
DisplayShellMsg(TR("Welcome to the interactive Lua interpreter.").."\n"
..TR("Enter Lua code and press Enter to run it.").." "
..TR("Use Shift-Enter for multiline code.").."\n"
..TR("Use 'clear' to clear the shell output and the history.").." "
..TR("Prepend '=' to show complex values on multiple lines."))
DisplayShellMsg(TR("Welcome to the interactive Lua interpreter.").." "
..TR("Enter Lua code and press Enter to run it.").."\n"
..TR("Use Shift-Enter for multiline code.").." "
..TR("Use 'clear' to clear the shell output and the history.").."\n"
..TR("Prepend '=' to show complex values on multiple lines.").." "
..TR("Prepend '!' to force local execution."))
DisplayShellPrompt('')
end

View File

@@ -1,5 +1,7 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local ide = ide
--[[ single instance
open an UDP port - if it fails it is either because
@@ -15,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."
@@ -35,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
@@ -51,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
@@ -72,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
@@ -83,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

@@ -1,4 +1,4 @@
-- Copyright 2011-13 Paul Kulchenko, ZeroBrane LLC
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
----------
@@ -24,37 +24,37 @@ local unpack = table.unpack or unpack
function StylesGetDefault()
return {
-- lexer specific (inherit fg/bg from text)
lexerdef = {fg = {128, 128, 128}},
comment = {fg = {32, 127, 32}, bg = {250, 250, 240}, fill= true},
stringtxt = {fg = {127, 0, 127}},
stringeol = {fg = {0, 0, 0}, bg = {224, 192, 224}, fill = true},
preprocessor = {fg = {127, 127, 0}},
operator = {fg = {0, 0, 0}},
number = {fg = {90, 0, 255}},
lexerdef = {fg = {160, 160, 160}},
comment = {fg = {128, 128, 128}},
stringtxt = {fg = {128, 32, 16}},
stringeol = {fg = {128, 32, 16}, bg = {224, 192, 224}, fill = true},
preprocessor = {fg = {128, 128, 0}},
operator = {fg = {64, 64, 64}},
number = {fg = {80, 112, 255}},
keywords0 = {fg = {0, 0, 127}, b = true},
keywords1 = {fg = {127, 0, 0}, b = true},
keywords2 = {fg = {0, 127, 0}, b = true},
keywords3 = {fg = {0, 0, 127}, b = true},
keywords4 = {fg = {127, 0, 95}, b = true},
keywords5 = {fg = {35, 95, 175}, b = true},
keywords6 = {fg = {0, 127, 127}, b = true},
keywords7 = {fg = {240, 255, 255}, b = true},
keywords0 = {fg = {32, 32, 192}},
keywords1 = {fg = {127, 32, 96}},
keywords2 = {fg = {32, 127, 96}},
keywords3 = {fg = {64, 32, 96}},
keywords4 = {fg = {127, 0, 95}},
keywords5 = {fg = {35, 95, 175}},
keywords6 = {fg = {0, 127, 127}},
keywords7 = {fg = {240, 255, 255}},
-- common (inherit fg/bg from text)
text = nil, -- let os pick
linenumber = {fg = {90, 90, 80}, bg = {240, 240, 240}},
bracematch = {fg = {0, 0, 255}, b = true},
bracemiss = {fg = {255, 0, 0 }, b = true},
text = {fg = {64, 64, 64}, bg = {250, 250, 250}},
linenumber = {fg = {128, 128, 128}, bg = {250, 250, 250}},
bracematch = {fg = {32, 128, 255}, b = true},
bracemiss = {fg = {255, 128, 32}, b = true},
ctrlchar = nil,
indent = {fg = {192, 192, 230}, bg = {255, 255, 255}},
calltip = nil,
-- common special (need custom fg & bg)
sel = {bg = {192, 192, 192}},
sel = {bg = {208, 208, 208}},
caret = {fg = {0, 0, 0}},
caretlinebg = {bg = {240, 240, 230}},
fold = {fg = {90, 90, 80}, bg = {250, 250, 250}, sel = {90+96, 90, 80}},
fold = {fg = {192, 192, 192}, bg = {250, 250, 250}, sel = {160, 128, 224}},
whitespace = nil,
edge = {},
@@ -64,7 +64,7 @@ function StylesGetDefault()
-- markup
['|'] = {fg = {127, 0, 127}},
['`'] = {fg = {127, 127, 127}},
['`'] = {fg = {64, 128, 64}},
['['] = {hs = {32, 32, 127}},
-- markers
@@ -79,7 +79,7 @@ function StylesGetDefault()
-- indicators
indicator = {
fncall = {},
fncall = {st = wxstc.wxSTC_INDIC_HIDDEN}, -- hide by default
varlocal = {},
varglobal = {},
varmasking = {},
@@ -89,8 +89,9 @@ function StylesGetDefault()
end
local markers = {
breakpoint = {1, wxstc.wxSTC_MARK_CIRCLE, wx.wxColour(220, 0, 0), wx.wxColour(220, 0, 0)},
currentline = {2, wxstc.wxSTC_MARK_ARROW, wx.wxBLACK, wx.wxColour(0, 220, 0)},
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)},
@@ -243,11 +244,16 @@ 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)
if window then
if window and panes:Item(index).name ~= 'toolbar' then
window:SetBackgroundColour(bg)
window:SetForegroundColour(fg)
window:Refresh()
@@ -309,6 +315,11 @@ function StylesApplyToEditor(styles,editor,font,fontitalic,lexerconvert)
end
editor:StyleClearAll()
-- set the default linenumber font size based on the editor font size
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
if (specialmapping[name]) then
specialmapping[name](editor,style)
@@ -336,7 +347,7 @@ function StylesApplyToEditor(styles,editor,font,fontitalic,lexerconvert)
if styles.calltip then editor:CallTipUseStyle(2) end
do
local defaultfg = styles.text and styles.text.fg or {127,127,127}
local defaultfg = {127,127,127}
local indic = styles.indicator or {}
-- use styles.fncall if not empty and if indic.fncall is empty
@@ -370,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
@@ -417,31 +428,3 @@ function ApplyStyleConfig(config, style)
ReApplySpecAndStyles()
end
end
function LoadStyleConfig()
local fileDialog = wx.wxFileDialog(ide.frame, "Open Config File",
"/cfg",
"",
"Lua file (*.lua)|*.lua|All files (*)|*",
wx.wxFD_OPEN + wx.wxFD_FILE_MUST_EXIST)
if fileDialog:ShowModal() == wx.wxID_OK then
ApplyStyleConfig(fileDialog:GetPath())
end
fileDialog:Destroy()
end
--[[
wxSTC_LUA_DEFAULT 0
wxSTC_LUA_COMMENT 1
wxSTC_LUA_COMMENTLINE 2
wxSTC_LUA_COMMENTDOC 3
wxSTC_LUA_NUMBER 4
wxSTC_LUA_WORD 5
wxSTC_LUA_STRING 6
wxSTC_LUA_CHARACTER 7
wxSTC_LUA_LITERALSTRING 8
wxSTC_LUA_PREPROCESSOR 9
wxSTC_LUA_OPERATOR 10
wxSTC_LUA_IDENTIFIER 11
wxSTC_LUA_STRINGEOL 12
--]]

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

@@ -1,3 +1,4 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
@@ -41,6 +42,7 @@ ide = {
editor = {
foldcompact = true,
checkeol = true,
saveallonrun = false,
},
debugger = {
verbose = false,
@@ -55,9 +57,16 @@ ide = {
interpreter = 'luadeb',
},
outputshell = {},
filetree = {},
filetree = {
mousemove = true,
},
funclist = {},
toolbar = {
icons = {},
iconmap = {},
},
keymap = {},
messages = {},
language = "en",
@@ -66,7 +75,7 @@ ide = {
stylesoutshell = nil,
autocomplete = true,
autoanalizer = true,
autoanalyzer = true,
acandtip = {
shorttip = false,
ignorecase = false,
@@ -75,17 +84,23 @@ ide = {
},
arg = {}, -- command line arguments
format = { -- various formatting strings
menurecentprojects = "%f | %i",
apptitle = "%T - %F",
},
activateoutput = false, -- activate output/console on Run/Debug/Compile
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 = {
@@ -152,6 +167,7 @@ end
if not wx.wxMOD_SHIFT then wx.wxMOD_SHIFT = 0x04 end
-- wxDIR_NO_FOLLOW is missing in wxlua 2.8.12 as well
if not wx.wxDIR_NO_FOLLOW then wx.wxDIR_NO_FOLLOW = 0x10 end
if not wxaui.wxAUI_TB_PLAIN_BACKGROUND then wxaui.wxAUI_TB_PLAIN_BACKGROUND = 2^8 end
if not setfenv then -- Lua 5.2
-- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html
@@ -173,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
@@ -248,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
@@ -288,19 +306,31 @@ local function loadPackages(filter)
loadToTab(filter, "packages", ide.packages, false, ide.proto.Plugin)
if ide.oshome then
local userpackages = MergeFullPath(ide.oshome, ".zbstudio/packages")
loadToTab(filter, userpackages, ide.packages, false, ide.proto.Plugin)
if wx.wxDirExists(userpackages) then
loadToTab(filter, userpackages, ide.packages, false, ide.proto.Plugin)
end
end
-- 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)
@@ -364,10 +394,19 @@ 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
local num = 0
ide.config.package = function(p)
if p then
num = num + 1
local name = 'config'..num..'package'
ide.packages[name] = setmetatable(p, ide.proto.Plugin)
end
end
end
LoadLuaConfig(ide.config.path.app.."/config.lua")
@@ -450,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
@@ -468,24 +507,116 @@ end
if app.postinit then app.postinit() end
if ide.osname == 'Macintosh' then
ide.frame:SetAcceleratorTable(wx.wxAcceleratorTable({
wx.wxAcceleratorEntry(wx.wxACCEL_CTRL, ('M'):byte(), ID_VIEWMINIMIZE)
}))
-- this is a workaround for a conflict between global shortcuts and local
-- shortcuts (like F2) used in the file tree or a watch panel.
-- because of several issues on OSX (as described in details in this thread:
-- https://groups.google.com/d/msg/wx-dev/juJj_nxn-_Y/JErF1h24UFsJ),
-- the workaround installs a global event handler that manually re-routes
-- conflicting events when the current focus is on a proper object.
-- non-conflicting shortcuts are handled through key-down events.
local remap = {
[ID_ADDWATCH] = ide:GetWatch(),
[ID_EDITWATCH] = ide:GetWatch(),
[ID_DELETEWATCH] = ide:GetWatch(),
[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()
for id, obj in pairs(remap) do
if obj:FindFocus():GetId() == obj:GetId() then
local ae = wx.wxAcceleratorEntry(); ae:FromString(KSC(id))
if ae:GetFlags() == mod and ae:GetKeyCode() == keycode then
rerouteMenuCommand(obj, id)
return
end
end
end
event:Skip()
end
ide:GetWatch():Connect(wx.wxEVT_KEY_DOWN, remapkey)
ide:GetProjectTree():Connect(wx.wxEVT_KEY_DOWN, remapkey)
local function resolveConflict(localid, globalid)
return function(event)
local shortcut = ide.config.keymap[localid]
for id, obj in pairs(remap) do
if ide.config.keymap[id]:lower() == shortcut:lower() then
local focus = obj:FindFocus()
if focus and focus:GetId() == obj:GetId() then
obj:AddPendingEvent(wx.wxCommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, id))
return
-- also need to check for children of objects
-- to avoid re-triggering events when labels are being edited
elseif focus and focus:GetParent():GetId() == obj:GetId() then
return
end
end
end
rerouteMenuCommand(ide.frame, globalid)
end
end
local at = {}
for lid in pairs(remap) do
local shortcut = ide.config.keymap[lid]
-- find a (potential) conflict for this shortcut (if any)
for gid, ksc in pairs(ide.config.keymap) do
-- if the same shortcut is used elsewhere (not one of IDs being checked)
if shortcut:lower() == ksc:lower() and not remap[gid] then
local fakeid = NewID()
ide.frame:Connect(fakeid, wx.wxEVT_COMMAND_MENU_SELECTED,
resolveConflict(lid, gid))
local ae = wx.wxAcceleratorEntry(); ae:FromString(ksc)
table.insert(at, wx.wxAcceleratorEntry(ae:GetFlags(), ae:GetKeyCode(), fakeid))
end
end
end
if ide.osname == 'Macintosh' then
table.insert(at, wx.wxAcceleratorEntry(wx.wxACCEL_CTRL, ('M'):byte(), ID_VIEWMINIMIZE))
end
ide.frame:SetAcceleratorTable(wx.wxAcceleratorTable(at))
-- only set menu bar *after* postinit handler as it may include adding
-- app-specific menus (Help/About), which are not recognized by MacOS
-- as special items unless SetMenuBar is done after menus are populated.
ide.frame:SetMenuBar(ide.frame.menuBar)
if ide.wxver < "2.9.5" and ide.osname == 'Macintosh' then -- force refresh to fix the filetree
pcall(function() ide.frame:ShowFullScreen(true) ide.frame:ShowFullScreen(false) end)
end
resumePrint()
PackageEventHandle("onAppLoad")
-- The status bar content is drawn incorrectly if it is shown
-- after being initially hidden.
-- Show the statusbar and hide it after showing the frame, which fixes the issue.
local statusbarfix = ide.osname == 'Windows' and not ide.frame:GetStatusBar():IsShown()
if statusbarfix then ide.frame:GetStatusBar():Show(true) end
ide.frame:Show(true)
if statusbarfix then ide.frame:GetStatusBar():Show(false) end
-- somehow having wxAuiToolbar "steals" the focus from the editor on OSX;
-- have to set the focus implicitly on the current editor (if any)
if ide.osname == 'Macintosh' then
local editor = GetEditor()
if editor then editor:SetFocus() end
end
wx.wxGetApp():MainLoop()
-- There are several reasons for this call:

View File

@@ -1,3 +1,4 @@
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- authors: Lomtik Software (J. Winwood & John Labenski)
-- Luxinia Dev (Eike Decker & Christoph Kubisch)
-- David Manura
@@ -154,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
@@ -172,6 +173,7 @@ function FileSysGetRecursive(path, recursive, spec, skip)
local dir = wx.wxDir(path)
if not dir:IsOpened() then return end
local log = wx.wxLogNull() -- disable error reporting; will report as needed
local found, file = dir:GetFirst("*", wx.wxDIR_DIRS)
while found do
if not skip or not file:find(skip) then
@@ -196,16 +198,12 @@ function FileSysGetRecursive(path, recursive, spec, skip)
end
getDir(path, spec)
-- explicitly sort files on Linux; directories first
if ide.osname == 'Unix' then
table.sort(content, function(a,b)
local ad, bd = a:sub(-1) == sep, b:sub(-1) == sep
-- both are folders or both are files
if ad and bd or not ad and not bd then return a < b
-- only one is folder; return true if it's the first one
else return ad end
end)
local prefix = '\001' -- prefix to sort directories first
local shadow = {}
for _, v in ipairs(content) do
shadow[v] = (v:sub(-1) == sep and prefix or '')..v:lower()
end
table.sort(content, function(a,b) return shadow[a] < shadow[b] end)
return content
end
@@ -271,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
@@ -389,11 +388,13 @@ function LoadLuaFileExt(tab, file, proto)
if not name then return end
-- check if os/arch matches to allow packages for different systems
local osvals = {windows = true, unix = true, macintosh = true}
local archvals = {x64 = true, x86 = true}
local os, arch = name:match("-(%w+)-?(%w*)")
if os and os:lower() ~= ide.osname:lower()
or arch and #arch > 0 and arch:lower() ~= ide.osarch:lower()
if os and os:lower() ~= ide.osname:lower() and osvals[os:lower()]
or arch and #arch > 0 and arch:lower() ~= ide.osarch:lower() and archvals[arch:lower()]
then return end
if os then name = name:gsub("-.*","") end
if os and osvals[os:lower()] then name = name:gsub("-.*","") end
local success, result = pcall(function()return cfgfn(assert(_G or _ENV))end)
if not success then
@@ -451,3 +452,72 @@ function LoadSafe(data)
end
function EscapeMagic(s) return s:gsub('([%(%)%.%%%+%-%*%?%[%^%$%]])','%%%1') end
local function isCtrlFocused(e)
local ctrl = e and e:FindFocus()
return ctrl and
(ctrl:GetId() == e:GetId()
or ide.osname == 'Macintosh' and
ctrl:GetParent():GetId() == e:GetId()) and ctrl or nil
end
function GetEditorWithFocus(...)
-- need to distinguish GetEditorWithFocus() and GetEditorWithFocus(nil)
-- as the latter may happen when GetEditor() is passed and returns `nil`
if select('#', ...) > 0 then
local ed = ...
return isCtrlFocused(ed) and ed or nil
end
local bnb = ide.frame.bottomnotebook
for _, e in pairs({bnb.shellbox, bnb.errorlog}) do
if isCtrlFocused(e) then return e end
end
local editor = GetEditor()
return isCtrlFocused(editor) and editor or nil
end
function GenerateProgramFilesPath(exec, sep)
local env = os.getenv('ProgramFiles')
return
(env and env..'\\'..exec..sep or '')..
[[C:\Program Files\]]..exec..sep..
[[D:\Program Files\]]..exec..sep..
[[C:\Program Files (x86)\]]..exec..sep..
[[D:\Program Files (x86)\]]..exec
end
--[[ format placeholders
- %f -- full project name (project path)
- %s -- short project name (directory name)
- %i -- interpreter name
- %S -- file name
- %F -- file path
- %n -- line number
- %c -- line content
- %T -- application title
- %v -- application version
- %t -- current tab name
--]]
function ExpandPlaceholders(msg, ph)
ph = ph or {}
if type(msg) == 'function' then return msg(ph) end
local editor = ide:GetEditor()
local proj = ide:GetProject() or ""
local dirs = wx.wxFileName(proj):GetDirs()
local doc = editor and ide:GetDocument(editor)
local nb = ide:GetEditorNotebook()
local def = {
f = proj,
s = dirs[#dirs] or "",
i = ide:GetInterpreter():GetName() or "",
S = doc and doc:GetFileName() or "",
F = doc and doc:GetFilePath() or "",
n = editor and editor:GetCurrentLine()+1 or 0,
c = editor and editor:GetLine(editor:GetCurrentLine()) or "",
T = GetIDEString("editor") or "",
v = ide.VERSION,
t = editor and nb:GetPageText(nb:GetPageIndex(editor)) or "",
}
return(msg:gsub('%%(%w)', function(p) return ph[p] or def[p] or '?' end))
end

View File

@@ -52,6 +52,23 @@ editor:AddText([[
ok(limit(10000, function() EditorAutoComplete(editor) end),
"Auto-complete doesn't loop for classes that reference '...'.")
-- create a valuetype self-reference
-- this is to test "s = Scan(); s:" fragment
ide.apis.lua.baselib.io.valuetype = "io"
ReloadLuaAPI()
editor:SetText('')
editor:AddText([[
s = io;
s:]])
ok(limitdepth(1000, function() EditorAutoComplete(editor) end),
"Auto-complete doesn't loop for classes that self-reference with 'valuetype'.")
-- restore valuetype
ide.apis.lua.baselib.io.valuetype = nil
ReloadLuaAPI()
local interpreter = ide:GetInterpreter():GetFileName()
ProjectSetInterpreter("gideros")

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)

Some files were not shown because too many files have changed in this diff Show More