From 2377a8c522e07ad0e31d7f29137cf0cea6c3f64e Mon Sep 17 00:00:00 2001 From: Eugene Pankov Date: Thu, 11 May 2017 22:56:48 +0200 Subject: [PATCH] clink --- clink/CHANGES | 215 ++ clink/LICENSE | 675 ++++ clink/clink.bat | 60 + clink/clink.html | 915 +++++ clink/clink.lua | 3420 +++++++++++++++++ clink/clink_dll_x64.dll | Bin 0 -> 552960 bytes clink/clink_dll_x86.dll | Bin 0 -> 538112 bytes clink/clink_inputrc_base | 72 + clink/clink_x64.exe | Bin 0 -> 136704 bytes clink/clink_x86.exe | Bin 0 -> 122880 bytes clink/profile/.history | 1 + clink/profile/settings | 116 + package.json | 3 +- terminus-terminal/src/buttonProvider.ts | 21 +- .../terminalSettingsTab.component.ts | 3 +- terminus-terminal/src/config.ts | 2 +- terminus-terminal/src/persistenceProviders.ts | 2 + 17 files changed, 5500 insertions(+), 5 deletions(-) create mode 100644 clink/CHANGES create mode 100644 clink/LICENSE create mode 100644 clink/clink.bat create mode 100644 clink/clink.html create mode 100644 clink/clink.lua create mode 100644 clink/clink_dll_x64.dll create mode 100644 clink/clink_dll_x86.dll create mode 100644 clink/clink_inputrc_base create mode 100644 clink/clink_x64.exe create mode 100644 clink/clink_x86.exe create mode 100644 clink/profile/.history create mode 100644 clink/profile/settings diff --git a/clink/CHANGES b/clink/CHANGES new file mode 100644 index 00000000..b3d3164a --- /dev/null +++ b/clink/CHANGES @@ -0,0 +1,215 @@ + +### Changes + +##### v0.4.8 + +- Environment variable 'clink_profile' overrides Clink's profile path (#390). +- Load a clink_inputrc file from Clink's profile directory (fixes #406). +- Bug fixes; + - Redraw issues when prompts end in OSC ANSI codes (#387, #384). + - Fixed 'clink autorun --help' crash. + +##### v0.4.7 + +- Bug fixes; + - Sometimes autorun tries to run clink.exe (#374). + - Autorun would cause cmd.exe to return an error if it wasn't interactive (#373). + +##### v0.4.6 + +- HOME is only set if it is currently unset. +- Readline can be initialised with .inputrc and _inputrc files too (#258). +- Bug fixes; + - Executable completion; + - Paths from PATH were checked twice. + - Incorrect results were returned for words ending in '.' or '-'. + - Directories . and .. were incorrectly displayed. + - Fixed a crash if .bat script's stdout is redirected (#366). + - Occasional crash when injecting Clink (#351). + - Display errors; + - When editing near the window's right-hand edge (#347). + - Double display of multi-line prompts when resizing the terminal (#352). + - Very rare wrap artefacts when making the terminal window larger. + - Doskey emulation (#344). + - Improved 'clink autorun' help (#348). + - Fixed launching Clink when clink.bat is renamed (#357). + +##### v0.4.5 + +- Improved 'clink autorun'. It now defaults to the Current User registry hive. +- 'clink set' gives more details for enumeration-type settings. +- Tab completion for p4vc. +- New settings 'history_expand_mode' to control history expansion in quotes (#317). +- Bug fixes; + - Use full width of the terminal (#337). + - Fixed MinGW compile error (#335). + - Autorun now defaults to the current user's hive (#332). + - Creating clink.html no longer needs Pandoc, plus it looks a bit better (#331). + - Added settings to control history expansion (#326). + - Correct fallback when 'use_altgr_substitute' is off (#325). + - Load history prior to saving it on shutdown (#318). + - Added Shift-Tab documentation and menu completion example (#190). + - Added shim for backwards menu completion (#190). + - Input handling now outputs '\e`Z' for Shift-Tab (#190). + - Updated Readme with current Premake info (#310). + - Guard against there being no buffer to read from (#304). + - Fixed artefacts when resizing conhost's buffer (#139). + - Clear remaining characters if scroll window was too small (#301) + - Escape % characters when expanding aliases (#280). + - Fixed leaking exception filters. + - Clearing the screen doesn't leave artefacts behind. + +##### v0.4.4 + +- Completing .. behaves more like Bash (#277). +- Escape from yes/no question when Ctrl+C is pressed. +- Valid XP executables (#278, #289). +- Fixed n-th argument yank not working as expected (#254). +- Fixed prompt colours sometimes not working (#279, #286). +- Fixed '!0' causing Clink to crash. +- Save/restore cursor position in case Readline moves it. + +##### v0.4.3 + +- Localised Y/N when auto-answering 'terminate?' prompt. +- $* would early out if there was no arguments. +- Disable ANSI code support if third party provides it. +- Installer can now set %CLINK_DIR% to install location. +- Improved output from 'clink set'. +- Support for Windows 10 Technical Preview. +- Ctrl-L now scrolls last line to the top of the window rather than clearing. +- New option to control how newline characters are pasted to the line. +- New options to control history; + - 'history_file_lines' - maximum lines saved to disk. + - 'history_ignore_space' - ignore lines prefixed with whitespace. + - 'history_dupe_mode' - how duplicate entries are handled. + - 'history_io' - load/save history from disk every line. +- Fixed nonfunctional numpad keys. +- Fixed missing WINCH signals if other processes resize the buffer. +- Support Alt codes sent from Conhost. + +##### v0.4.2 + +- Prompt colouring no longer requires third party ANSI code utility. +- Override settings with environment variables prefixed with 'clink'. +- Ctrl-PgUp goes up a directory. +- Updated Go completions (by matrixik). +- Arguments to clink.arg.new_parser() now initialise parser's flags/args (from vladimir-kotikov). +- New clink.arg.add_flags() and clink.arg.add_arguments() functions. +- Removed footer and Alt-H tip for more succinct stdout output. +- Bug fixes; + - Windows XP works again. + - Fixed race condition in lua_execute(). + +##### v0.4.1 + +- Bug fixes; + - Various Unicode fixes causing corrupt environment variables. + - Fixed thread resume/suspend causing rare system-wide deadlock. + - Fixed incorrect translation of suffixed slash when completing flags. +- Add --nolog argument to disable file logging. Fix #187 Fix #154 +- Added missing escape sequences from doskey emulation. +- Reinstated unix-kill-line key binding. +- Mapped PgUp/Down to search history using line typed so far. +- Added documentation covering escape codes for special keys. +- Added support for Windows' AltGr substitute Ctrl-Alt. +- Support for Readline's 'menu' style completion (see docs). + +##### v0.4 + +- New features; + - Better 'clink.arg' API. Easier, more intuitive, and more powerful. + - Whitespace prefix skips exec matching. + - Added a 'set' verb to easily change settings from the command line. + - Basic support for a shells other than cmd.exe. + - Completion for Go (contributed by Dobroslaw Zybort). + - Setting 'exec_match_style' to -1 disables it entirely. + - Make history persistence optional. + - Alias/doskey completion. + - Very basic support for Powershell. + - View cmd.exe's autorun entry without needing admin rights. +- New key bindings; + - Ctrl-Alt-C : Copy command line to the clipboard. + - Ctrl-Alt-E : Expand environment variable under cursor. + - Ctrl-Alt-U : 'up directory' (formerly Shift-Up). + - Ctrl-U : Adds '..\' to the command line. + - Alt-H : Shows active keymap's key bindings. +- New Lua functions; + - clink.execute(). + - clink.get_host_process(). + - clink.match_files(). + - clink.match_words(). + - clink.get_console_aliases(). +- Lots of bug fixes, including; + - Better command extraction. + - Fixed cmd.exe command paging and Ctrl-C/Ctrl-Break handling. + - Multiple locale fixes. + - Use localised text for 'Terminate batch job?' prompt. + +##### v0.3 + +- Automatic answering of cmd.exe's 'Terminate batch script?' prompt. +- Coloured prompts (requires ANSICON or ConEmu). +- Added Shift-Up keyboard shortcut to automatically execute 'cd ..' +- Mapped Ctrl-Z to undo, Microsoft style. +- Improved integration of Readline; + - New input handling code (Ctrl-Alt combos now work). + - An implementation of the Termcap library. + - Fully functional Vi-mode support. + - Support for resizable consoles. + - Line wrapping now works correctly (issue 50). +- Adjustable executable match style (issue 65). +- Improved environment variable completion. +- Added settings file to customise Clink. +- New Lua features and functions; + - Matches can now be filtered in Lua before they are display. + - clink.quote_split(). + - clink.arg.node_merge(). + - clink.get_screen_info() (issue 71). + - clink.split() (for splitting strings). + - clink.chdir(). + - clink.get_cwd(). + - Functions to query Clink's settings. +- New command line options; + - '--profile ' to override default profile directory. + - '--nohostcheck' disables verification that host is cmd.exe. + - '--pid' specifies the process to inject into. +- Update Mercurial completion (issue 73). +- Start menu shortcut starts in USERPROFILE, like cmd.exe +- Zip distribution is now portable. + +##### v0.2.1 + +- The .history file now merges multiple sessions together. +- Fixed missing y/n, pause, and other prompts. +- Fixed segfault in loader executable. +- Better ConEmu compatibility. + +##### v0.2 + +- Basic argument completion for 'git', 'hg', 'svn', and 'p4'. +- Traditional Bash clear screen ('Ctrl-L') and exit shortcuts ('Ctrl-D'). +- Scrollable command window using 'PgUp'/'PgDown' keys. +- Doskey support. +- Automatic quoting of file names with spaces. +- Scriptable custom prompts. +- New argument framework to ease writing context-sensitive match generators. +- History and log file is now saved per-user rather than globally. +- Improved Clink's command line interface ('clink --help'). +- More reliable handling of cmd.exe's autorun entry. +- General improvements to executable and directory-command completion. +- Symbolic link support. +- Documentation. +- Windows 8 support. +- Improved hooking so Clink can be shared with other thirdparty utilities that + also hook cmd.exe (ConEmu, ANSICon, etc.). + +##### v0.1.1 + +- Fixed AltGr+ on international keyboards. +- Fixed broken completion when directories have a '-' in their name (Mark Hammond) +- The check for single match scenarios now correctly handles case-insensitivity. + +##### v0.1 + +- Initial release. diff --git a/clink/LICENSE b/clink/LICENSE new file mode 100644 index 00000000..10926e87 --- /dev/null +++ b/clink/LICENSE @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/clink/clink.bat b/clink/clink.bat new file mode 100644 index 00000000..0c73ec28 --- /dev/null +++ b/clink/clink.bat @@ -0,0 +1,60 @@ +:: Copyright (c) 2012 Martin Ridgers +:: +:: Permission is hereby granted, free of charge, to any person obtaining a copy +:: of this software and associated documentation files (the "Software"), to deal +:: in the Software without restriction, including without limitation the rights +:: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +:: copies of the Software, and to permit persons to whom the Software is +:: furnished to do so, subject to the following conditions: +:: +:: The above copyright notice and this permission notice shall be included in +:: all copies or substantial portions of the Software. +:: +:: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +:: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +:: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +:: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +:: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +:: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +:: SOFTWARE. + +@echo off + +:: Mimic cmd.exe's behaviour when starting from the start menu. +if /i "%1"=="startmenu" ( + cd /d "%userprofile%" + shift /1 +) + +:: Check for the --profile option. +if /i "%1"=="--profile" ( + set clink_profile_arg=--profile "%~2" + shift /1 + shift /1 +) + +:: If the .bat is run without any arguments, then start a cmd.exe instance. +if "%1"=="" ( + call :launch + goto :end +) + +:: Pass through to appropriate loader. +if /i "%processor_architecture%"=="x86" ( + "%~dp0\clink_x86.exe" %* +) else if /i "%processor_architecture%"=="amd64" ( + if defined processor_architew6432 ( + "%~dp0\clink_x86.exe" %* + ) else ( + "%~dp0\clink_x64.exe" %* + ) +) + +:end +set clink_profile_arg= +goto :eof + +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:launch +start "Clink" cmd.exe /s /k ""%~dpnx0" inject %clink_profile_arg%" +exit /b 0 diff --git a/clink/clink.html b/clink/clink.html new file mode 100644 index 00000000..ac2ed598 --- /dev/null +++ b/clink/clink.html @@ -0,0 +1,915 @@ + + + + + + + + + + + + +
+
+ +### What is Clink? + +Clink combines the native Windows shell cmd.exe with the powerful command line editing features of the GNU Readline library, which provides rich completion, history, and line-editing capabilities. Readline is best known for its use in the famous Unix shell Bash, the standard shell for Mac OS X and many Linux distributions. + +### Features + +- The same line editing as Bash (from GNU's Readline library). +- History persistence between sessions. +- Context sensitive completion; + - Executables (and aliases). + - Directory commands. + - Environment variables + - Thirdparty tools; Git, Mercurial, SVN, Go, and P4. +- New keyboard shortcuts; + - Paste from clipboard (**Ctrl-V**). + - Incremental history search (**Ctrl-R/Ctrl-S**). + - Powerful completion (**TAB**). + - Undo (**Ctrl-Z**). + - Automatic "cd .." (**Ctrl-Alt-U**). + - Environment variable expansion (**Ctrl-Alt-E**). + - (press **Alt-H** for many more...) +- Scriptable completion with Lua. +- Coloured and scriptable prompt. +- Auto-answering of the "Terminate batch job?" prompt. + +By default Clink binds **Alt-H** to display the current key bindings. More features can also be found in GNU's [Readline](http://tinyurl.com/oum26rp) and [History](http://tinyurl.com/p92oq5d) libraries' manuals. + +### Usage + +There are three ways to use Clink the first of which is to add Clink to cmd.exe's autorun registry entry. This can be selected when installing Clink using the installer and Clink also provides the ability to manage this autorun entry from the command line. Running **clink autorun --help** has more information. + +The second alternative is to manually run Clink using the command **clink inject** from within a command prompt session to run Clink in that session. + +The last option is to use the Clink shortcut that the installer adds to Windows' start menu. This is in essence a shortcut to the command **cmd.exe /k clink inject**. + +### How Clink Works + +When running Clink via the methods above, Clink checks the parent process is supported and injects a DLL into it. The DLL then hooks the WriteConsole() and ReadConsole() Windows functions. The former is so that Clink can capture the current prompt, and the latter hook allows Clink to provide it's own Readline-powered command line editing. + +### Configuring Clink + +The easiest way to configure Clink is to use Clink's **set** command line option. This can list, query, and set Clink's settings. Run **clink set --help** from a Clink-installed cmd.exe process to learn more both about how to use it and to get descriptions for Clink's various options. + +Settings that are loaded when Clink starts can be overridden by setting environment variables matching the setting name and prefixed with "clink.". For example, the command **set clink.prompt_colour=10** will turn the prompt green regardless of what is in the settings file. Overrides are not saved to disk. + +The following table describes the available settings; + +Name | Description +:--: | ----------- +**ansi_code_support** | When printing the prompt, Clink has basic built-in support for SGR ANSI escape codes to control the text colours. This is automatically disabled if a third party tool is detected that also provides this facility. It can also be disabled by setting this to 0. +**ctrld_exits** | Ctrl-D exits the process when it is pressed on an empty line. +**esc_clears_line** | Clink clears the current line when Esc is pressed (unless Readline's Vi mode is enabled). +**exec_match_style** | Changes how Clink will match executables when there is no path separator on the line. 0 = PATH only, 1 = PATH and CWD, 2 = PATH, CWD, and directories. In all cases both executables and directories are matched when there is a path separator present. +**history_dupe_mode** | If a line is a duplicate of an existing history entry Clink will erase the duplicate when this is set 2. A value of 1 will not add duplicates to the history and a value of 0 will always add lines. +**history_expand_mode** | The '!' character in an entered line can be interpreted to introduce words from the history. This can be enabled and disable by setting this value to 1 or 0. Values or 2, 3 or 4 will skip any ! character quoted in single, double, or both quotes respectively. +**history_file_lines** | When set to a positive integer this is the number of lines of history that will persist when Clink saves the command history to disk. Use 0 for infinite lines and <0 to disable history persistence. +**history_ignore_space** | Ignore lines that begin with whitespace when adding lines in to the history. +**history_io** | Use this setting to control when the history is written to disk and when it is read back. A value of 1 will read the history before editing of a new line commences, 2 will write the history, and 3 will do both. The default (0) is to write the history when the process exits.", +**match_colour** | Colour to use when displaying matches. A value less than 0 will be the opposite brightness of the default colour. +**prompt_colour** | Surrounds the prompt in ANSI escape codes to set the prompt's colour (0..15). Disabled when the value is less than 0. +**space_prefix_match_files** | If the line begins with whitespace then Clink bypasses executable matching and will match all files and directories instead. +**terminate_autoanswer** | Automatically answers cmd.exe's **Terminate batch job (Y/N)?** prompts. 0 = disabled, 1 = answer Y, 2 = answer N. +**use_altgr_substitute** | Windows provides Ctrl-Alt as a substitute for AltGr, historically to support keyboards with no AltGr key. This may collide with some of Readline's bindings. + +#### File Locations + +Settings and history are persisted to disk from session to session. The location of these files depends on which distribution of Clink was used. If you installed Clink using the .exe installer then Clink uses the current user's non-roaming application data directory. This user directory is usually found in one of the following locations; + +- c:\Documents and Settings\\<username>\Local Settings\Application Data *(XP)* +- c:\Users\\<username>\AppData\Local *(Vista onwards)* + +The .zip distribution of Clink creates and uses a directory called **profile** which is located in the same directory where Clink's core files are found. + +All of the above locations can be overridden using the **--profile <path>** command line option which is specified when injecting Clink into cmd.exe using **clink inject**. + +### Configuring Readline + +Readline itself can also be configured to add custom keybindings and macros by creating a Readline init file. There is excellent documentation for all the options available to configure Readline in Readline's [manual](http://tinyurl.com/oum26rp). + +Clink will search in the directory as specified by the HOME environment variable for one or all of the following files; `clink_inputrc`, `_inputrc`, and `.inputrc`. If HOME is unset then Clink will use either of the standard Windows environment variables `%homedrive%\%homepath%` or `%userprofile%`. + +Other software that also uses Readline will also look for the `.inputrc` file (and possibly the `_inputrc` file too). To set macros and keybindings intended only for Clink one can use the Readline init file conditional construct like this; `$if cmd.exe [...] $endif`. + +Editing the `clink_inputrc_base` is discouraged as this will change from version to version and may not be present in the future. + +### Extending Clink + +The Readline library allows clients to offer an alternative path for creating completion matches. Clink uses this to hook Lua into the completion process making it possible to script the generation of matches with Lua scripts. The following sections describe this in more detail and shows some examples. + +#### The Location of Lua Scripts + +Clink looks for Lua scripts in the folders as described in the **Configuring Clink** section. By default **Ctrl-Q** is mapped to reload all Lua scripts which can be useful when developing and iterating on your own scripts. + +#### Match Generators + +These are Lua functions that are registered with Clink and are called as part of Readline's completion process. Match generator functions take the following form; + +``` +function my_match_generator(text, first, last) + -- Use text/rl_state.line_buffer to create matches, + -- Submit matches to Clink using clink.add_match() + -- Return true/false. +end +``` + +**Text** is the word that is being completed, **first** and **last** and the indices into the complete line buffer for **text** (the full line buffer can be accessed using the variable **rl_state.line_buffer**). If no further match generators need to be called then the function should return true. + +Registering the match generation function is done as follows; + +``` +clink.register_match_generator(my_match_generator, sort_id) +``` + +The **sort_id** argument is used to sort the match generators such that generators with a lower sort ids are called first. + +Here is an simple example script that checks if **text** begins with a **%** character and then uses the remained of **text** to match the names of environment variables. + +``` +function env_vars_match_generator(text, first, last) + if not text:find("^%%") then + return false + end + + text = clink.lower(text:sub(2)) + local text_len = #text + for _, name in ipairs(clink.get_env_var_names()) do + if clink.lower(name:sub(1, text_len)) == text then + clink.add_match('%'..name..'%') + end + end + + return true +end + +clink.register_match_generator(env_vars_match_generator, 10) +``` + +#### Argument Completion + +Clink provides a framework for writing complex argument match generators in Lua. It works by creating a parser object that describes a command's arguments and flags and then registering the parser with Clink. When Clink detects the command is being entered on the current command line being edited, it uses the parser to generate matches. + +Here is an example of a simple parser for the command **foobar**; + +``` +my_parser = clink.arg.new_parser() +my_parser:set_flags("-foo", "-bar") +my_parser:set_arguments( + { "hello", "hi" }, + { "world", "wombles" } +) + +clink.arg.register_parser("foobar", my_parser) +``` + +This parser describes a command that has two positional arguments each with two potential options. It also has two flags which the parser considers to be position independent meaning that provided the word being completed starts with a certain prefix the parser with attempt to match the from the set of flags. + +On the command line completion would look something like this; + +``` +C:\>foobar hello -foo wo +world wombles +C:\>foobar hello -foo wo_ +``` + +As an alternative to calling **clink.arg.set_arguments()** and **clink.arg.set_flags()** you can instead provide the parser's flags and positional arguments as arguments to **clink.arg.new_parser()** as follows; + +``` +some_parser = clink.arg.new_parser( + { "arg1-1", "arg1-2" }, + { "arg2-1", "arg2-2" }, + "-flag1", "-flag2" +) +``` + +##### More Advanced Stuff + +###### Linking Parsers + +There are often situations where the parsing of a command's arguments is dependent on the previous words (**git merge ...** compared to **git log ...** for example). For these scenarios Clink allows you to link parsers to arguments' words using Lua's concatenation operator. Parsers can also be concatenated with flags too. + +``` +a_parser = clink.arg.new_parser():set_arguments({"foo", "bar" }) +b_parser = clink.arg.new_parser():set_arguments({ "abc", "123" }) +c_parser = clink.arg.new_parser() +c_parser:set_arguments( + { "foobar" .. b_parser }, + { c_parser } +) +``` + +With syntax from preceding section this converts into: + +``` +parser = clink.arg.new_parser +a_parser = parser({"foo", "bar" }) +c_parser = parser( + { "foobar" .. parser({ "abc", "123" }) }, + { c_parser } +) +``` + +As the example above shows, it is also possible to use a parser without concatenating it to a word. When Clink follows a link to a parser it is permanent and it will not return to the previous parser. + +###### Functions As Argument Options + +Argument options are not limited solely to strings. Clink also accepts functions too so more context aware argument options can be used. + +``` +function rainbow_function(word) + return { "red", "white", "blue" } +end + +the_parser = clink.arg.new_parser() +the_parser:set_arguments( + { "zippy", "bungle", "george" }, + { rainbow_function, "yellow", "green" } +) +``` + +The functions take a single argument which is a word from the command line being edited (or partial word if it is the one under the cursor). Functions should return a table of potential matches (or an empty table if it calls clink.add_match() directly itself). + +#### Filtering The Match Display + +In some instances it may be preferable to display potential matches in an alternative form than the generated matches passed to and used internally by Readline. This happens for example with Readline's standard file name matches, where the matches are the whole word being completed but only the last part of the path is shown (e.g. the match **foo/bar** is displayed as **bar**). + +To facilitate custom match generators that may wish to do this there is the **clink.match_display_filter** variable. This can be set to a function that will then be called before matches are to be displayed. + +``` +function my_display_filter(matches) + new_matches = {} + + for _, m in ipairs(matches) do + local _, _, n = m:find("\\([^\\]+)$") + table.insert(new_matches, n) + end + + return new_matches +end + +function my_match_generator(text, first, last) + ... + + clink.match_display_filter = my_display_filter + return true +end +``` + +The function's single argument **matches** is a table containing what Clink is going to display. The return value is a table with the input matches filtered as required by the match generator. The value of **clink.match_display_filter** is reset every time match generation is invoked. + +#### Customising The Prompt + +Before Clink displays the prompt it filters the prompt through Lua so that the prompt can be customised. This happens each and every time that the prompt is shown which allows for context sensitive customisations (such as showing the current branch of a git repository for example). + +Writing a prompt filter is straight forward and best illustrated with an example that displays the current git branch when the current directory is a git repository. + +``` +function git_prompt_filter() + for line in io.popen("git branch 2>nul"):lines() do + local m = line:match("%* (.+)$") + if m then + clink.prompt.value = "["..m.."] "..clink.prompt.value + break + end + end + + return false +end + +clink.prompt.register_filter(git_prompt_filter, 50) +``` + +The filter function takes no arguments instead receiving and modifying the prompt through the **clink.prompt.value** variable. It returns true if the prompt filtering is finished, and false if it should continue on to the next registered filter. + +A filter function is registered into the filter chain by passing the function to **clink.prompt.register_filter()** along with a sort id which dictates the order in which filters are called. Lower sort ids are called first. + +### Miscellaneous + +#### Binding special keys + +Due to differences between Windows and Linux, escape codes for keys like PageUp/Down and the arrow keys are different in Clink. Escape codes take the format **\\e`?** where '?' is one of the characters from the following table; + +Key | Normal | Shift | Ctrl | Ctrl-Shift +:-: | :-: | :-: | :-: | :-: +Home | G | a | w | ! +Up | H | b | T | " +PageUp | I | c | U | # +Left | K | d | s | $ +Right | M | e | t | % +End | O | f | u | & +Down | P | g | V | ' +PageDown | Q | h | v | ( +Insert | R | i | W | ) +Delete | S | j | X | \* +Tab | (n/a) | Z | (n/a) | (n/a) + +Here is an example line from a clink_inputrc file that binds Shift-End to the Readline function **transpose-word** function; + +``` +"\e`f": transpose-word +``` + +#### Readline's menu-complete + +Clink supports Readline's menu-complete command (which is similar to vanilla cmd.exe completion that cycles through matches rather than displaying available ones). To use this menu-style completion Clink provides the alternative command **clink-menu-completion-shim**. Using this ensures that appropriate path separator translation takes place. + +#### Powershell + +Clink has basic support for Powershell. In order to show completion correctly Clink needs to parse Powershell's prompt to extract the current directory. If the prompt has been customized Clink is unlikely to work as expected. + + + + +### The Clink Lua API + +#### Matches + +##### clink.add_match(text) + +Outputs **text** as a match for the active completion. + +##### clink.compute_lcd(text, matches) + +Returns the least-common-denominator of **matches**. It is assumed that **text** was the input to generate **matches**. As such it is expected that each match starts with **text**. + +##### clink.get_match(index) + +Returns a match by **index** from the matches output by clink.add_match(). + +##### clink.is_match(needle, candidate) + +Given a **needle** (such as the section of the current line buffer being completed), this function returns true or false if **candidate** begins with **needle**. Readline's -/_ case-mapping is respected if it is enabled. + +##### clink.is_single_match(matches) + +Checks each match in the table **matches** and checks to see if they are all duplicates of each other. + +##### clink.match_count() + +Returns the number of matches output by calls to clink.add_match(). + +##### clink.match_display_filter + +This variable can be set to a function so that matches can be filtered before they are displayed. See **Display Filtering** section for more info. + +##### clink.matches_are_files() + +Tells Readline that the matches we are passing back to it are files. This will cause Readline to append the path separator character to the line if there's only one match, and mark directories when displaying multiple matches. + +##### clink.register_match_generator(generator, sort_id) + +Registers a match **generator** function that is called to generate matches when the complete keys is press (TAB by default). + +The generator function takes the form **generator_function(text, first, last)** where **text** is the portion of the line buffer that is to be completed, **first** and **last** are the start and end indices into the line buffer for **text**. + +##### clink.set_match(index, value) + +Explicitly sets match at **index** to **value**. + +#### Argument Framework + +##### parser:add_arguments(table1, table2, ...) + +Adds more positional arguments to the parser. See **parser:set_arguments()**. + +##### parser:add_flags(flag1, flag2, ...) + +Adds more flags to the parser. See **parser:set_flags()**. + +##### parser:be_precise() + +Ordinarily Clink only loosely matches word as it traverses a parser. Calling this will make Clink only accept an exact matching word to consider moving onto the next one. + +##### parser:disable_file_matching() + +If this is called then Clink will not default to matching the file system if parsing comes to an end or can not be completed. + +##### parser:dump() + +Prints the parser to stdout. + +##### parser:go(parts) + +This runs the parser for the table of words **parts**. It returns a table of argument options. It is this method that Clink uses internally. + +##### parser:is_flag(word) + +Returns true of **word** is a valid flag. + +##### parser:loop(index) + +By default parsers do not loop and parsing comes to an end when there are no more arguments to traverse through. If loop() is called Clink will loop back to argument at **index** rather than terminating the parse. + +##### parser:set_arguments(table1, table2, ...) + +This method sets the parser's positional arguments. Each of the variable number of arguments to the method is a table of potential options for the argument at that position. Note that calling this method replaces any existing positional arguments the parser may already have. Use **parser:add_arguments()** to append more positional arguments. + +##### parser:set_flags(flag1, flag2, ...) + +Sets the parser's flags (which can be thought of as position independent arguments). Each argument is a string and must start with the expected flag prefix ("-" by default). Be aware that calling **set_flags()** will replace the parser's existing flags. To add more use **parser:add_flags()**. + +#### Prompt Filtering + +##### clink.prompt.register_filter(filter, sort_id) + +Used to register a **filter** function to pre-process the prompt before use by Readline. Filters are called by **sort_id** where lower sort ids get called first. Filter functions will receive no arguments and return true if filtering is finished. Getting and setting the prompt value is done through the **clink.prompt.value** variable. + +##### clink.prompt.value + +User-provided prompt filter functions can get and set the prompt value using this variable. + +#### Miscellaneous + +##### clink.chdir(path) + +Changes the current working directory to **path**. Clink caches and restores the working directory between calls to the match generation so that it does not interfere with the processes normal operation. + +##### clink.find_dirs(mask, case_map) + +Returns a table (array) of directories that match the supplied **mask**. If **case_map** is **true** then Clink will adjust the last part of the mask's path so that returned matches respect Readline's case-mapping feature (if it is enabled). For example; **.\foo_foo\bar_bar*** becomes **.\foo_foo\bar?bar***. + +There is no support for recursively traversing the path in **mask**. + +##### clink.find_files(mask, case_map) + +Returns a table (array) of files that match the supplied **mask**. See **find_dirs** for details on the **case_map** argument. + +There is no support for recursively traversing the path in **mask**. + +##### clink.get_cwd() + +Returns the current working directory. + +##### clink.get_console_aliases() + +Returns a table of all the registered console aliases. Windows' console alias API is exposed via **doskey** or progromatically via the AddConsoleAlias() function. + +##### clink.get_env(env_var_name) + +Returns the value of the environment variable **env_var_name**. This is preferable to the built-in Lua function os.getenv() as the latter uses a cached version of the current process' environment which can result in incorrect results. + +##### clink.get_env_var_names() + +Returns a table of the names of the current process' environment variables. + +##### clink.get_host_process() + +Returns the name of the host process (the rl_readline_name variable). + +##### clink.get_screen_info() + +Returns a table describing the current console buffer's state with the following +contents; + +``` +{ + -- Dimensions of the console's buffer. + buffer_width + buffer_height + + -- Dimensions of the visible area of the console buffer. + window_width + window_height +} +``` + +##### clink.get_setting_str(name) + +Retrieves the Clink setting **name**, returning it as a string. See **Settings** for more information on the available settings. + +##### clink.get_setting_int(name) + +As **clink.get_setting_str** but returning a number instead. + +##### clink.is_dir(path) + +Returns true if **path** resolves to a directory. + +##### clink.is_rl_variable_true(readline_var_name) + +Returns the boolean value of a Readline variable. These can be set with the clink_inputrc file, more details of which can be found in the [Readline manual](http://tinyurl.com/oum26rp). + +##### clink.lower(text) + +Same as os.lower() but respects Readline's case-mapping feature which will consider - and _ as case insensitive. + +Care should be taken when using this to generate masks for file/dir find operations due to the -/_ giving different results (unless of course Readline's extended case-mapping is disabled). + +##### clink.match_files(pattern, full_path, find_func) + +Globs files using **pattern** and adds results as matches. If **full_path** is **true** then the path from **pattern** is prefixed to the results (otherwise only the file names are included). The last argument **find_func** is the function to use to do the globbing. If it's unspecified (or nil) Clink falls back to **clink.find_files**. + +##### clink.match_words(text, words) + +Calls clink.is_match() on each word in the table **words** and adds matches to Clink that match the needle **text**. + +##### clink.quote_split(str, ql, qr) + +This function takes the string **str** which is quoted by **ql** (the opening quote character) and **qr** (the closing character) and splits it into parts as per the quotes. A table of these parts is returned. + +``` +clink.quote_split("pre(middle)post", "(", ")") = { + "pre", "middle", "post" +} +``` + +##### clink.slash_translation(type) + +Controls how Clink will translate the path separating slashes for the current path being completed. Values for **type** are; + +- -1 - no translation +- 0 - to backslashes +- 1 - to forward slashes. + + +##### clink.split(str, sep) + +Splits the string **str** into pieces separated by **sep**, returning a table of the pieces. + +##### clink.suppress_char_append() + +This stops Readline from adding a trailing character when completion is finished (usually when a single match is returned). The suffixing of a character is enabled before completion functions are called so a call to this will only apply for the current completion. + +By default Readline appends a space character (' ') when the is only a single match unless it is completing files where it will use the path separator instead. + +##### clink.suppress_quoting() + +Suppress the prefixing and suffixing of quotes even if there is a character in the current word being completed that would ordinarily need surrounding in quotes. + +#### Readline Constants + +Clink exposes a small amount of state from Readline in the global **rl_state** table. Readline's nomenclature is maintained (minus the *rl* prefix) so Readline's manual can also be used as reference. This table should be considered read-only - changes to the table's members are not fed back to Readline. + +##### rl_state.line_buffer + +This variable contains the current state of the whole line being edited. + +##### rl_state.point + +The current cursor position within the line buffer. + + + + + +### Changes + +##### v0.4.8 + +- Environment variable 'clink_profile' overrides Clink's profile path (#390). +- Load a clink_inputrc file from Clink's profile directory (fixes #406). +- Bug fixes; + - Redraw issues when prompts end in OSC ANSI codes (#387, #384). + - Fixed 'clink autorun --help' crash. + +##### v0.4.7 + +- Bug fixes; + - Sometimes autorun tries to run clink.exe (#374). + - Autorun would cause cmd.exe to return an error if it wasn't interactive (#373). + +##### v0.4.6 + +- HOME is only set if it is currently unset. +- Readline can be initialised with .inputrc and _inputrc files too (#258). +- Bug fixes; + - Executable completion; + - Paths from PATH were checked twice. + - Incorrect results were returned for words ending in '.' or '-'. + - Directories . and .. were incorrectly displayed. + - Fixed a crash if .bat script's stdout is redirected (#366). + - Occasional crash when injecting Clink (#351). + - Display errors; + - When editing near the window's right-hand edge (#347). + - Double display of multi-line prompts when resizing the terminal (#352). + - Very rare wrap artefacts when making the terminal window larger. + - Doskey emulation (#344). + - Improved 'clink autorun' help (#348). + - Fixed launching Clink when clink.bat is renamed (#357). + +##### v0.4.5 + +- Improved 'clink autorun'. It now defaults to the Current User registry hive. +- 'clink set' gives more details for enumeration-type settings. +- Tab completion for p4vc. +- New settings 'history_expand_mode' to control history expansion in quotes (#317). +- Bug fixes; + - Use full width of the terminal (#337). + - Fixed MinGW compile error (#335). + - Autorun now defaults to the current user's hive (#332). + - Creating clink.html no longer needs Pandoc, plus it looks a bit better (#331). + - Added settings to control history expansion (#326). + - Correct fallback when 'use_altgr_substitute' is off (#325). + - Load history prior to saving it on shutdown (#318). + - Added Shift-Tab documentation and menu completion example (#190). + - Added shim for backwards menu completion (#190). + - Input handling now outputs '\e`Z' for Shift-Tab (#190). + - Updated Readme with current Premake info (#310). + - Guard against there being no buffer to read from (#304). + - Fixed artefacts when resizing conhost's buffer (#139). + - Clear remaining characters if scroll window was too small (#301) + - Escape % characters when expanding aliases (#280). + - Fixed leaking exception filters. + - Clearing the screen doesn't leave artefacts behind. + +##### v0.4.4 + +- Completing .. behaves more like Bash (#277). +- Escape from yes/no question when Ctrl+C is pressed. +- Valid XP executables (#278, #289). +- Fixed n-th argument yank not working as expected (#254). +- Fixed prompt colours sometimes not working (#279, #286). +- Fixed '!0' causing Clink to crash. +- Save/restore cursor position in case Readline moves it. + +##### v0.4.3 + +- Localised Y/N when auto-answering 'terminate?' prompt. +- $* would early out if there was no arguments. +- Disable ANSI code support if third party provides it. +- Installer can now set %CLINK_DIR% to install location. +- Improved output from 'clink set'. +- Support for Windows 10 Technical Preview. +- Ctrl-L now scrolls last line to the top of the window rather than clearing. +- New option to control how newline characters are pasted to the line. +- New options to control history; + - 'history_file_lines' - maximum lines saved to disk. + - 'history_ignore_space' - ignore lines prefixed with whitespace. + - 'history_dupe_mode' - how duplicate entries are handled. + - 'history_io' - load/save history from disk every line. +- Fixed nonfunctional numpad keys. +- Fixed missing WINCH signals if other processes resize the buffer. +- Support Alt codes sent from Conhost. + +##### v0.4.2 + +- Prompt colouring no longer requires third party ANSI code utility. +- Override settings with environment variables prefixed with 'clink'. +- Ctrl-PgUp goes up a directory. +- Updated Go completions (by matrixik). +- Arguments to clink.arg.new_parser() now initialise parser's flags/args (from vladimir-kotikov). +- New clink.arg.add_flags() and clink.arg.add_arguments() functions. +- Removed footer and Alt-H tip for more succinct stdout output. +- Bug fixes; + - Windows XP works again. + - Fixed race condition in lua_execute(). + +##### v0.4.1 + +- Bug fixes; + - Various Unicode fixes causing corrupt environment variables. + - Fixed thread resume/suspend causing rare system-wide deadlock. + - Fixed incorrect translation of suffixed slash when completing flags. +- Add --nolog argument to disable file logging. Fix #187 Fix #154 +- Added missing escape sequences from doskey emulation. +- Reinstated unix-kill-line key binding. +- Mapped PgUp/Down to search history using line typed so far. +- Added documentation covering escape codes for special keys. +- Added support for Windows' AltGr substitute Ctrl-Alt. +- Support for Readline's 'menu' style completion (see docs). + +##### v0.4 + +- New features; + - Better 'clink.arg' API. Easier, more intuitive, and more powerful. + - Whitespace prefix skips exec matching. + - Added a 'set' verb to easily change settings from the command line. + - Basic support for a shells other than cmd.exe. + - Completion for Go (contributed by Dobroslaw Zybort). + - Setting 'exec_match_style' to -1 disables it entirely. + - Make history persistence optional. + - Alias/doskey completion. + - Very basic support for Powershell. + - View cmd.exe's autorun entry without needing admin rights. +- New key bindings; + - Ctrl-Alt-C : Copy command line to the clipboard. + - Ctrl-Alt-E : Expand environment variable under cursor. + - Ctrl-Alt-U : 'up directory' (formerly Shift-Up). + - Ctrl-U : Adds '..\' to the command line. + - Alt-H : Shows active keymap's key bindings. +- New Lua functions; + - clink.execute(). + - clink.get_host_process(). + - clink.match_files(). + - clink.match_words(). + - clink.get_console_aliases(). +- Lots of bug fixes, including; + - Better command extraction. + - Fixed cmd.exe command paging and Ctrl-C/Ctrl-Break handling. + - Multiple locale fixes. + - Use localised text for 'Terminate batch job?' prompt. + +##### v0.3 + +- Automatic answering of cmd.exe's 'Terminate batch script?' prompt. +- Coloured prompts (requires ANSICON or ConEmu). +- Added Shift-Up keyboard shortcut to automatically execute 'cd ..' +- Mapped Ctrl-Z to undo, Microsoft style. +- Improved integration of Readline; + - New input handling code (Ctrl-Alt combos now work). + - An implementation of the Termcap library. + - Fully functional Vi-mode support. + - Support for resizable consoles. + - Line wrapping now works correctly (issue 50). +- Adjustable executable match style (issue 65). +- Improved environment variable completion. +- Added settings file to customise Clink. +- New Lua features and functions; + - Matches can now be filtered in Lua before they are display. + - clink.quote_split(). + - clink.arg.node_merge(). + - clink.get_screen_info() (issue 71). + - clink.split() (for splitting strings). + - clink.chdir(). + - clink.get_cwd(). + - Functions to query Clink's settings. +- New command line options; + - '--profile ' to override default profile directory. + - '--nohostcheck' disables verification that host is cmd.exe. + - '--pid' specifies the process to inject into. +- Update Mercurial completion (issue 73). +- Start menu shortcut starts in USERPROFILE, like cmd.exe +- Zip distribution is now portable. + +##### v0.2.1 + +- The .history file now merges multiple sessions together. +- Fixed missing y/n, pause, and other prompts. +- Fixed segfault in loader executable. +- Better ConEmu compatibility. + +##### v0.2 + +- Basic argument completion for 'git', 'hg', 'svn', and 'p4'. +- Traditional Bash clear screen ('Ctrl-L') and exit shortcuts ('Ctrl-D'). +- Scrollable command window using 'PgUp'/'PgDown' keys. +- Doskey support. +- Automatic quoting of file names with spaces. +- Scriptable custom prompts. +- New argument framework to ease writing context-sensitive match generators. +- History and log file is now saved per-user rather than globally. +- Improved Clink's command line interface ('clink --help'). +- More reliable handling of cmd.exe's autorun entry. +- General improvements to executable and directory-command completion. +- Symbolic link support. +- Documentation. +- Windows 8 support. +- Improved hooking so Clink can be shared with other thirdparty utilities that + also hook cmd.exe (ConEmu, ANSICon, etc.). + +##### v0.1.1 + +- Fixed AltGr+ on international keyboards. +- Fixed broken completion when directories have a '-' in their name (Mark Hammond) +- The check for single match scenarios now correctly handles case-insensitivity. + +##### v0.1 + +- Initial release. + + + + diff --git a/clink/clink.lua b/clink/clink.lua new file mode 100644 index 00000000..c59f06c0 --- /dev/null +++ b/clink/clink.lua @@ -0,0 +1,3420 @@ +-- +-- Copyright (c) 2012 Martin Ridgers +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +-------------------------------------------------------------------------------- +clink.matches = {} +clink.generators = {} + +clink.prompt = {} +clink.prompt.filters = {} + +-------------------------------------------------------------------------------- +function clink.compute_lcd(text, list) + local list_n = #list + if list_n < 2 then + return + end + + -- Find min and max limits + local max = 100000 + for i = 1, #list, 1 do + local j = #(list[i]) + if max > j then + max = j + end + end + + -- For each character in the search range... + local mid = #text + local lcd = "" + for i = 1, max, 1 do + local same = true + local l = list[1]:sub(i, i) + local m = l:lower() + + -- Compare character at the index with each other character in the + -- other matches. + for j = 2, list_n, 1 do + local n = list[j]:sub(i, i):lower() + if m ~= n then + same = false + break + end + end + + -- If all characters match then use first match's character. + if same then + lcd = lcd..l + else + -- Otherwise use what the user's typed or if we're past that then + -- bail out. + if i <= mid then + lcd = lcd..text:sub(i, i) + else + break + end + end + end + + return lcd +end + +-------------------------------------------------------------------------------- +function clink.is_single_match(matches) + if #matches <= 1 then + return true + end + + local first = matches[1]:lower() + for i = 2, #matches, 1 do + if first ~= matches[i]:lower() then + return false + end + end + + return true +end + +-------------------------------------------------------------------------------- +function clink.is_point_in_quote(str, i) + if i > #str then + i = #str + end + + local c = 1 + local q = string.byte("\"") + for j = 1, i do + if string.byte(str, j) == q then + c = c * -1 + end + end + + if c < 0 then + return true + end + + return false +end + +-------------------------------------------------------------------------------- +function clink.adjust_for_separator(buffer, point, first, last) + local seps = nil + if clink.get_host_process() == "cmd.exe" then + seps = "|&" + end + + if seps then + -- Find any valid command separators and if found, manipulate the + -- completion state a little bit. + local leading = buffer:sub(1, first - 1) + + -- regex is: + local regex = "["..seps.."]([^"..seps.."]*)$" + local sep_found, _, post_sep = leading:find(regex) + + if sep_found and not clink.is_point_in_quote(leading, sep_found) then + local delta = #leading - #post_sep + buffer = buffer:sub(delta + 1) + first = first - delta + last = last - delta + point = point - delta + + if first < 1 then + first = 1 + end + end + end + + return buffer, point, first, last +end + +-------------------------------------------------------------------------------- +function clink.generate_matches(text, first, last) + local line_buffer + local point + + line_buffer, point, first, last = clink.adjust_for_separator( + rl_state.line_buffer, + rl_state.point, + first, + last + ) + + rl_state.line_buffer = line_buffer + rl_state.point = point + + clink.matches = {} + clink.match_display_filter = nil + + for _, generator in ipairs(clink.generators) do + if generator.f(text, first, last) == true then + if #clink.matches > 1 then + -- Catch instances where there's many entries of a single match + if clink.is_single_match(clink.matches) then + clink.matches = { clink.matches[1] } + return true; + end + + -- First entry in the match list should be the user's input, + -- modified here to be the lowest common denominator. + local lcd = clink.compute_lcd(text, clink.matches) + table.insert(clink.matches, 1, lcd) + end + + return true + end + end + + return false +end + +-------------------------------------------------------------------------------- +function clink.add_match(match) + if type(match) == "table" then + for _, i in ipairs(match) do + table.insert(clink.matches, i) + end + + return + end + + table.insert(clink.matches, match) +end + +-------------------------------------------------------------------------------- +function clink.register_match_generator(func, priority) + if priority == nil then + priority = 999 + end + + table.insert(clink.generators, {f=func, p=priority}) + table.sort(clink.generators, function(a, b) return a["p"] < b["p"] end) +end + +-------------------------------------------------------------------------------- +function clink.is_match(needle, candidate) + if needle == nil then + error("Nil needle value when calling clink.is_match()", 2) + end + + if clink.lower(candidate:sub(1, #needle)) == clink.lower(needle) then + return true + end + return false +end + +-------------------------------------------------------------------------------- +function clink.match_count() + return #clink.matches +end + +-------------------------------------------------------------------------------- +function clink.set_match(i, value) + clink.matches[i] = value +end + +-------------------------------------------------------------------------------- +function clink.get_match(i) + return clink.matches[i] +end + +-------------------------------------------------------------------------------- +function clink.match_words(text, words) + local count = clink.match_count() + + for _, i in ipairs(words) do + if clink.is_match(text, i) then + clink.add_match(i) + end + end + + return clink.match_count() - count +end + +-------------------------------------------------------------------------------- +function clink.match_files(pattern, full_path, find_func) + -- Fill out default values + if type(find_func) ~= "function" then + find_func = clink.find_files + end + + if full_path == nil then + full_path = true + end + + if pattern == nil then + pattern = "*" + end + + -- Glob files. + pattern = pattern:gsub("/", "\\") + local glob = find_func(pattern, true) + + -- Get glob's base. + local base = "" + local i = pattern:find("[\\:][^\\:]*$") + if i and full_path then + base = pattern:sub(1, i) + end + + -- Match them. + local count = clink.match_count() + + for _, i in ipairs(glob) do + local full = base..i + clink.add_match(full) + end + + return clink.match_count() - count +end + +-------------------------------------------------------------------------------- +function clink.split(str, sep) + local i = 1 + local ret = {} + for _, j in function() return str:find(sep, i, true) end do + table.insert(ret, str:sub(i, j - 1)) + i = j + 1 + end + table.insert(ret, str:sub(i, j)) + + return ret +end + +-------------------------------------------------------------------------------- +function clink.quote_split(str, ql, qr) + if not qr then + qr = ql + end + + -- First parse in "pre[ql]quote_string[qr]" chunks + local insert = table.insert + local i = 1 + local needle = "%b"..ql..qr + local parts = {} + for l, r, quote in function() return str:find(needle, i) end do + -- "pre" + if l > 1 then + insert(parts, str:sub(i, l - 1)) + end + + -- "quote_string" + insert(parts, str:sub(l, r)) + i = r + 1 + end + + -- Second parse what remains as "pre[ql]being_quoted" + local l = str:find(ql, i, true) + if l then + -- "pre" + if l > 1 then + insert(parts, str:sub(i, l - 1)) + end + + -- "being_quoted" + insert(parts, str:sub(l)) + elseif i <= #str then + -- Finally add whatever remains... + insert(parts, str:sub(i)) + end + + return parts +end + +-------------------------------------------------------------------------------- +function clink.prompt.register_filter(filter, priority) + if priority == nil then + priority = 999 + end + + table.insert(clink.prompt.filters, {f=filter, p=priority}) + table.sort(clink.prompt.filters, function(a, b) return a["p"] < b["p"] end) +end + +-------------------------------------------------------------------------------- +function clink.filter_prompt(prompt) + local function add_ansi_codes(p) + local c = tonumber(clink.get_setting_int("prompt_colour")) + if c < 0 then + return p + end + + c = c % 16 + + --[[ + <4 >=4 %2 + 0 0 0 Black 4 1 -3 Blue 0 + 1 4 3 Red 5 5 0 Magenta 1 + 2 2 0 Green 6 3 -3 Cyan 0 + 3 6 3 Yellow 7 7 0 Gray 1 + --]] + + -- Convert from cmd.exe colour indices to ANSI ones. + local colour_id = c % 8 + if (colour_id % 2) == 1 then + if colour_id < 4 then + c = c + 3 + end + elseif colour_id >= 4 then + c = c - 3 + end + + -- Clamp + if c > 15 then + c = 15 + end + + -- Build ANSI code + local code = "\x1b[0;" + if c > 7 then + c = c - 8 + code = code.."1;" + end + code = code..(c + 30).."m" + + return code..p.."\x1b[0m" + end + + clink.prompt.value = prompt + + for _, filter in ipairs(clink.prompt.filters) do + if filter.f() == true then + return add_ansi_codes(clink.prompt.value) + end + end + + return add_ansi_codes(clink.prompt.value) +end + +-- vim: expandtab +-- +-- Copyright (c) 2012 Martin Ridgers +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +-------------------------------------------------------------------------------- +clink.arg = {} + +-------------------------------------------------------------------------------- +local parsers = {} +local is_parser +local is_sub_parser +local new_sub_parser +local parser_go_impl +local merge_parsers + +local parser_meta_table = {} +local sub_parser_meta_table = {} + +-------------------------------------------------------------------------------- +function parser_meta_table.__concat(lhs, rhs) + if not is_parser(rhs) then + error("Right-handside must be parser.", 2) + end + + local t = type(lhs) + if t == "table" then + local ret = {} + for _, i in ipairs(lhs) do + table.insert(ret, i .. rhs) + end + + return ret + elseif t ~= "string" then + error("Left-handside must be a string or a table.", 2) + end + + return new_sub_parser(lhs, rhs) +end + +-------------------------------------------------------------------------------- +local function unfold_table(source, target) + for _, i in ipairs(source) do + if type(i) == "table" and getmetatable(i) == nil then + unfold_table(i, target) + else + table.insert(target, i) + end + end +end + +-------------------------------------------------------------------------------- +local function parser_is_flag(parser, part) + if part == nil then + return false + end + + local prefix = part:sub(1, 1) + return prefix == "-" or prefix == "/" +end + +-------------------------------------------------------------------------------- +local function parser_add_arguments(parser, ...) + for _, i in ipairs({...}) do + -- Check all arguments are tables. + if type(i) ~= "table" then + error("All arguments to add_arguments() must be tables.", 2) + end + + -- Only parsers are allowed to be specified without being wrapped in a + -- containing table. + if getmetatable(i) ~= nil then + if is_parser(i) then + table.insert(parser.arguments, i) + else + error("Tables can't have meta-tables.", 2) + end + else + -- Expand out nested tables and insert into object's arguments table. + local arguments = {} + unfold_table(i, arguments) + table.insert(parser.arguments, arguments) + end + end + + return parser +end + +-------------------------------------------------------------------------------- +local function parser_set_arguments(parser, ...) + parser.arguments = {} + return parser:add_arguments(...) +end + +-------------------------------------------------------------------------------- +local function parser_add_flags(parser, ...) + local flags = {} + unfold_table({...}, flags) + + -- Validate the specified flags. + for _, i in ipairs(flags) do + if is_sub_parser(i) then + i = i.key + end + + -- Check all flags are strings. + if type(i) ~= "string" then + error("All parser flags must be strings. Found "..type(i), 2) + end + + -- Check all flags start with a - or a / + if not parser:is_flag(i) then + error("Flags must begin with a '-' or a '/'", 2) + end + end + + -- Append flags to parser's existing table of flags. + for _, i in ipairs(flags) do + table.insert(parser.flags, i) + end + + return parser +end + +-------------------------------------------------------------------------------- +local function parser_set_flags(parser, ...) + parser.flags = {} + return parser:add_flags(...) +end + +-------------------------------------------------------------------------------- +local function parser_flatten_argument(parser, index, func_thunk) + -- Sanity check the 'index' param to make sure it's valid. + if type(index) == "number" then + if index <= 0 or index > #parser.arguments then + return parser.use_file_matching + end + end + + -- index == nil is a special case that returns the parser's flags + local opts = {} + local arg_opts + if index == nil then + arg_opts = parser.flags + else + arg_opts = parser.arguments[index] + end + + -- Convert each argument option into a string and collect them in a table. + for _, i in ipairs(arg_opts) do + if is_sub_parser(i) then + table.insert(opts, i.key) + else + local t = type(i) + if t == "function" then + local results = func_thunk(i) + local t = type(results) + if not results then + return parser.use_file_matching + elseif t == "boolean" then + return (results and parser.use_file_matching) + elseif t == "table" then + for _, j in ipairs(results) do + table.insert(opts, j) + end + end + elseif t == "string" or t == "number" then + table.insert(opts, tostring(i)) + end + end + end + + return opts +end + +-------------------------------------------------------------------------------- +local function parser_go_args(parser, state) + local exhausted_args = false + local exhausted_parts = false + + local part = state.parts[state.part_index] + local arg_index = state.arg_index + local arg_opts = parser.arguments[arg_index] + local arg_count = #parser.arguments + + -- Is the next argument a parser? Parse control directly on to it. + if is_parser(arg_opts) then + state.arg_index = 1 + return parser_go_impl(arg_opts, state) + end + + -- Advance parts state. + state.part_index = state.part_index + 1 + if state.part_index > #state.parts then + exhausted_parts = true + end + + -- Advance argument state. + state.arg_index = arg_index + 1 + if arg_index > arg_count then + exhausted_args = true + end + + -- We've exhausted all available arguments. We either loop or we're done. + if parser.loop_point > 0 and state.arg_index > arg_count then + state.arg_index = parser.loop_point + if state.arg_index > arg_count then + state.arg_index = arg_count + end + end + + -- Is there some state to process? + if not exhausted_parts and not exhausted_args then + local exact = false + for _, arg_opt in ipairs(arg_opts) do + -- Is the argument a key to a sub-parser? If so then hand control + -- off to it. + if is_sub_parser(arg_opt) then + if arg_opt.key == part then + state.arg_index = 1 + return parser_go_impl(arg_opt.parser, state) + end + end + + -- Check so see if the part has an exact match in the argument. Note + -- that only string-type options are considered. + if type(arg_opt) == "string" then + exact = exact or arg_opt == part + else + exact = true + end + end + + -- If the parser's required to be precise then check here. + if parser.precise and not exact then + exhausted_args = true + else + return nil + end + end + + -- If we've no more arguments to traverse but there's still parts remaining + -- then we start skipping arguments but keep going so that flags still get + -- parsed (as flags have no position). + if exhausted_args then + state.part_index = state.part_index - 1 + + if not exhausted_parts then + if state.depth <= 1 then + state.skip_args = true + return + end + + return parser.use_file_matching + end + end + + -- Now we've an index into the parser's arguments that matches the line + -- state. Flatten it. + local func_thunk = function(func) + return func(part) + end + + return parser:flatten_argument(arg_index, func_thunk) +end + +-------------------------------------------------------------------------------- +local function parser_go_flags(parser, state) + local part = state.parts[state.part_index] + + -- Advance parts state. + state.part_index = state.part_index + 1 + if state.part_index > #state.parts then + return parser:flatten_argument() + end + + for _, arg_opt in ipairs(parser.flags) do + if is_sub_parser(arg_opt) then + if arg_opt.key == part then + local arg_index_cache = state.arg_index + local skip_args_cache = state.skip_args + + state.arg_index = 1 + state.skip_args = false + state.depth = state.depth + 1 + + local ret = parser_go_impl(arg_opt.parser, state) + if type(ret) == "table" then + return ret + end + + state.depth = state.depth - 1 + state.skip_args = skip_args_cache + state.arg_index = arg_index_cache + end + end + end +end + +-------------------------------------------------------------------------------- +function parser_go_impl(parser, state) + local has_flags = #parser.flags > 0 + + while state.part_index <= #state.parts do + local part = state.parts[state.part_index] + local dispatch_func + + if has_flags and parser:is_flag(part) then + dispatch_func = parser_go_flags + elseif not state.skip_args then + dispatch_func = parser_go_args + end + + if dispatch_func ~= nil then + local ret = dispatch_func(parser, state) + if ret ~= nil then + return ret + end + else + state.part_index = state.part_index + 1 + end + end + + return parser.use_file_matching +end + +-------------------------------------------------------------------------------- +local function parser_go(parser, parts) + -- Validate 'parts'. + if type(parts) ~= "table" then + error("'Parts' param must be a table of strings ("..type(parts)..").", 2) + else + if #parts == 0 then + part = { "" } + end + + for i, j in ipairs(parts) do + local t = type(parts[i]) + if t ~= "string" then + error("'Parts' table can only contain strings; "..j.."="..t, 2) + end + end + end + + local state = { + arg_index = 1, + part_index = 1, + parts = parts, + skip_args = false, + depth = 1, + } + + return parser_go_impl(parser, state) +end + +-------------------------------------------------------------------------------- +local function parser_dump(parser, depth) + if depth == nil then + depth = 0 + end + + function prt(depth, index, text) + local indent = string.sub(" ", 1, depth) + text = tostring(text) + print(indent..depth.."."..index.." - "..text) + end + + -- Print arguments + local i = 0 + for _, arg_opts in ipairs(parser.arguments) do + for _, arg_opt in ipairs(arg_opts) do + if is_sub_parser(arg_opt) then + prt(depth, i, arg_opt.key) + arg_opt.parser:dump(depth + 1) + else + prt(depth, i, arg_opt) + end + end + + i = i + 1 + end + + -- Print flags + for _, flag in ipairs(parser.flags) do + prt(depth, "F", flag) + end +end + +-------------------------------------------------------------------------------- +function parser_be_precise(parser) + parser.precise = true + return parser +end + +-------------------------------------------------------------------------------- +function is_parser(p) + return type(p) == "table" and getmetatable(p) == parser_meta_table +end + +-------------------------------------------------------------------------------- +function is_sub_parser(sp) + return type(sp) == "table" and getmetatable(sp) == sub_parser_meta_table +end + +-------------------------------------------------------------------------------- +local function get_sub_parser(argument, str) + for _, arg in ipairs(argument) do + if is_sub_parser(arg) then + if arg.key == str then + return arg.parser + end + end + end +end + +-------------------------------------------------------------------------------- +function new_sub_parser(key, parser) + local sub_parser = {} + sub_parser.key = key + sub_parser.parser = parser + + setmetatable(sub_parser, sub_parser_meta_table) + return sub_parser +end + +-------------------------------------------------------------------------------- +local function parser_disable_file_matching(parser) + parser.use_file_matching = false + return parser +end + +-------------------------------------------------------------------------------- +local function parser_loop(parser, loop_point) + if loop_point == nil or type(loop_point) ~= "number" or loop_point < 1 then + loop_point = 1 + end + + parser.loop_point = loop_point + return parser +end + +-------------------------------------------------------------------------------- +local function parser_initialise(parser, ...) + for _, word in ipairs({...}) do + local t = type(word) + if t == "string" then + parser:add_flags(word) + elseif t == "table" then + if is_sub_parser(word) and parser_is_flag(nil, word.key) then + parser:add_flags(word) + else + parser:add_arguments(word) + end + else + error("Additional arguments to new_parser() must be tables or strings", 2) + end + end +end + +-------------------------------------------------------------------------------- +function clink.arg.new_parser(...) + local parser = {} + + -- Methods + parser.set_flags = parser_set_flags + parser.add_flags = parser_add_flags + parser.set_arguments = parser_set_arguments + parser.add_arguments = parser_add_arguments + parser.dump = parser_dump + parser.go = parser_go + parser.flatten_argument = parser_flatten_argument + parser.be_precise = parser_be_precise + parser.disable_file_matching = parser_disable_file_matching + parser.loop = parser_loop + parser.is_flag = parser_is_flag + + -- Members. + parser.flags = {} + parser.arguments = {} + parser.precise = false + parser.use_file_matching = true + parser.loop_point = 0 + + setmetatable(parser, parser_meta_table) + + -- If any arguments are provided treat them as parser's arguments or flags + if ... then + success, msg = pcall(parser_initialise, parser, ...) + if not success then + error(msg, 2) + end + end + + return parser +end + +-------------------------------------------------------------------------------- +function merge_parsers(lhs, rhs) + -- Merging parsers is not a trivial matter and this implementation is far + -- from correct. It is however sufficient for the majority of cases. + + -- Merge flags. + for _, rflag in ipairs(rhs.flags) do + table.insert(lhs.flags, rflag) + end + + -- Remove (and save value of) the first argument in RHS. + local rhs_arg_1 = table.remove(rhs.arguments, 1) + if rhs_arg_1 == nil then + return + end + + -- Get reference to the LHS's first argument table (creating it if needed). + local lhs_arg_1 = lhs.arguments[1] + if lhs_arg_1 == nil then + lhs_arg_1 = {} + table.insert(lhs.arguments, lhs_arg_1) + end + + -- Link RHS to LHS through sub-parsers. + for _, rarg in ipairs(rhs_arg_1) do + local child + + -- Split sub parser + if is_sub_parser(rarg) then + child = rarg.parser + rarg = rarg.key + else + child = rhs + end + + -- If LHS's first argument has rarg in it which links to a sub-parser + -- then we need to recursively merge them. + local lhs_sub_parser = get_sub_parser(lhs_arg_1, rarg) + if lhs_sub_parser then + merge_parsers(lhs_sub_parser, child) + else + local to_add = rarg + if type(rarg) ~= "function" then + to_add = rarg .. child + end + + table.insert(lhs_arg_1, to_add) + end + end +end + +-------------------------------------------------------------------------------- +function clink.arg.register_parser(cmd, parser) + if not is_parser(parser) then + local p = clink.arg.new_parser() + p:set_arguments({ parser }) + parser = p + end + + cmd = cmd:lower() + local prev = parsers[cmd] + if prev ~= nil then + merge_parsers(prev, parser) + else + parsers[cmd] = parser + end +end + +-------------------------------------------------------------------------------- +local function argument_match_generator(text, first, last) + local leading = rl_state.line_buffer:sub(1, first - 1):lower() + + -- Extract the command. + local cmd_l, cmd_r + if leading:find("^%s*\"") then + -- Command appears to be surround by quotes. + cmd_l, cmd_r = leading:find("%b\"\"") + if cmd_l and cmd_r then + cmd_l = cmd_l + 1 + cmd_r = cmd_r - 1 + end + else + -- No quotes so the first, longest, non-whitespace word is extracted. + cmd_l, cmd_r = leading:find("[^%s]+") + end + + if not cmd_l or not cmd_r then + return false + end + + local regex = "[\\/:]*([^\\/:.]+)(%.*[%l]*)%s*$" + local _, _, cmd, ext = leading:sub(cmd_l, cmd_r):lower():find(regex) + + -- Check to make sure the extension extracted is in pathext. + if ext and ext ~= "" then + if not clink.get_env("pathext"):lower():match(ext.."[;$]", 1, true) then + return false + end + end + + -- Find a registered parser. + local parser = parsers[cmd] + if parser == nil then + return false + end + + -- Split the command line into parts. + local str = rl_state.line_buffer:sub(cmd_r + 2, last) + local parts = {} + for _, sub_str in ipairs(clink.quote_split(str, "\"")) do + -- Quoted strings still have their quotes. Look for those type of + -- strings, strip the quotes and add it completely. + if sub_str:sub(1, 1) == "\"" then + local l, r = sub_str:find("\"[^\"]+") + if l then + local part = sub_str:sub(l + 1, r) + table.insert(parts, part) + end + else + -- Extract non-whitespace parts. + for _, r, part in function () return sub_str:find("^%s*([^%s]+)") end do + table.insert(parts, part) + sub_str = sub_str:sub(r + 1) + end + end + end + + -- If 'text' is empty then add it as a part as it would have been skipped + -- by the split loop above. + if text == "" then + table.insert(parts, text) + end + + -- Extend rl_state with match generation state; text, first, and last. + rl_state.text = text + rl_state.first = first + rl_state.last = last + + -- Call the parser. + local needle = parts[#parts] + local ret = parser:go(parts) + if type(ret) ~= "table" then + return not ret + end + + -- Iterate through the matches the parser returned and collect matches. + for _, match in ipairs(ret) do + if clink.is_match(needle, match) then + clink.add_match(match) + end + end + + return true +end + +-------------------------------------------------------------------------------- +clink.register_match_generator(argument_match_generator, 25) + +-- vim: expandtab + +--{{{ history + +--15/03/06 DCN Created based on RemDebug +--28/04/06 DCN Update for Lua 5.1 +--01/06/06 DCN Fix command argument parsing +-- Add step/over N facility +-- Add trace lines facility +--05/06/06 DCN Add trace call/return facility +--06/06/06 DCN Make it behave when stepping through the creation of a coroutine +--06/06/06 DCN Integrate the simple debugger into the main one +--07/06/06 DCN Provide facility to step into coroutines +--13/06/06 DCN Fix bug that caused the function environment to get corrupted with the global one +--14/06/06 DCN Allow 'sloppy' file names when setting breakpoints +--04/08/06 DCN Allow for no space after command name +--11/08/06 DCN Use io.write not print +--30/08/06 DCN Allow access to array elements in 'dump' +--10/10/06 DCN Default to breakfile for all commands that require a filename and give '-' +--06/12/06 DCN Allow for punctuation characters in DUMP variable names +--03/01/07 DCN Add pause on/off facility +--19/06/07 DCN Allow for duff commands being typed in the debugger (thanks to Michael.Bringmann@lsi.com) +-- Allow for case sensitive file systems (thanks to Michael.Bringmann@lsi.com) +--04/08/09 DCN Add optional line count param to pause +--05/08/09 DCN Reset the debug hook in Pause() even if we think we're started +--30/09/09 DCN Re-jig to not use co-routines (makes debugging co-routines awkward) +--01/10/09 DCN Add ability to break on reaching any line in a file +--24/07/13 TWW Added code for emulating setfenv/getfenv in Lua 5.2 as per +-- http://lua-users.org/lists/lua-l/2010-06/msg00313.html +--25/07/13 TWW Copied Alex Parrill's fix for errors when tracing back across a C frame +-- (https://github.com/ColonelThirtyTwo/clidebugger, 26/01/12) +--25/07/13 DCN Allow for windows and unix file name conventions in has_breakpoint +--26/07/13 DCN Allow for \ being interpreted as an escape inside a [] pattern in 5.2 + +--}}} +--{{{ description + +--A simple command line debug system for Lua written by Dave Nichols of +--Match-IT Limited. Its public domain software. Do with it as you wish. + +--This debugger was inspired by: +-- RemDebug 1.0 Beta +-- Copyright Kepler Project 2005 (http://www.keplerproject.org/remdebug) + +--Usage: +-- require('debugger') --load the debug library +-- pause(message) --start/resume a debug session + +--An assert() failure will also invoke the debugger. + +--}}} + +local IsWindows = string.find(string.lower(os.getenv('OS') or ''),'^windows') + +local coro_debugger +local events = { BREAK = 1, WATCH = 2, STEP = 3, SET = 4 } +local breakpoints = {} +local watches = {} +local step_into = false +local step_over = false +local step_lines = 0 +local step_level = {main=0} +local stack_level = {main=0} +local trace_level = {main=0} +local trace_calls = false +local trace_returns = false +local trace_lines = false +local ret_file, ret_line, ret_name +local current_thread = 'main' +local started = false +local pause_off = false +local _g = _G +local cocreate, cowrap = coroutine.create, coroutine.wrap +local pausemsg = 'pause' + +local aliases = { + p = "over", + t = "step", + q = "exit", + g = "run", + dv = "dump", + dt = "locs", + k = "trace", + bp = "setb", + bc = "delb", + bl = "listb", + pt = "out", +} + +--{{{ make Lua 5.2 compatible + +if not setfenv then -- Lua 5.2 + --[[ + As far as I can see, the only missing detail of these functions (except + for occasional bugs) to achieve 100% compatibility is the case of + 'getfenv' over a function that does not have an _ENV variable (that is, + it uses no globals). + + We could use a weak table to keep the environments of these functions + when set by setfenv, but that still misses the case of a function + without _ENV that was not subjected to setfenv. + + -- Roberto + ]]-- + + setfenv = setfenv or function(f, t) + f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func) + local name + local up = 0 + repeat + up = up + 1 + name = debug.getupvalue(f, up) + until name == '_ENV' or name == nil + if name then + debug.upvaluejoin(f, up, function() return name end, 1) -- use unique upvalue + debug.setupvalue(f, up, t) + end + end + + getfenv = getfenv or function(f) + f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func) + local name, val + local up = 0 + repeat + up = up + 1 + name, val = debug.getupvalue(f, up) + until name == '_ENV' or name == nil + return val + end + + unpack = table.unpack + +end + +--}}} + +--{{{ local hints -- command help +--The format in here is name=summary|description +local hints = { + +pause = [[ +pause(msg[,lines][,force]) -- start/resume a debugger session| + +This can only be used in your code or from the console as a means to +start/resume a debug session. +If msg is given that is shown when the session starts/resumes. Useful to +give a context if you've instrumented your code with pause() statements. + +If lines is given, the script pauses after that many lines, else it pauses +immediately. + +If force is true, the pause function is honoured even if poff has been used. +This is useful when in an interactive console session to regain debugger +control. +]], + +poff = [[ +poff -- turn off pause() command| + +This causes all pause() commands to be ignored. This is useful if you have +instrumented your code in a busy loop and want to continue normal execution +with no further interruption. +]], + +pon = [[ +pon -- turn on pause() command| + +This re-instates honouring the pause() commands you may have instrumented +your code with. +]], + +setb = [[ +setb [line file] -- set a breakpoint to line/file|, line 0 means 'any' + +If file is omitted or is "-" the breakpoint is set at the file for the +currently set level (see "set"). Execution pauses when this line is about +to be executed and the debugger session is re-activated. + +The file can be given as the fully qualified name, partially qualified or +just the file name. E.g. if file is set as "myfile.lua", then whenever +execution reaches any file that ends with "myfile.lua" it will pause. If +no extension is given, any extension will do. + +If the line is given as 0, then reaching any line in the file will do. +]], + +delb = [[ +delb [line file] -- removes a breakpoint| + +If file is omitted or is "-" the breakpoint is removed for the file of the +currently set level (see "set"). +]], + +delallb = [[ +delallb -- removes all breakpoints| +]], + +setw = [[ +setw -- adds a new watch expression| + +The expression is evaluated before each line is executed. If the expression +yields true then execution is paused and the debugger session re-activated. +The expression is executed in the context of the line about to be executed. +]], + +delw = [[ +delw -- removes the watch expression at index| + +The index is that returned when the watch expression was set by setw. +]], + +delallw = [[ +delallw -- removes all watch expressions| +]], + +run = [[ +run -- run until next breakpoint or watch expression| +]], + +step = [[ +step [N] -- run next N lines, stepping into function calls| + +If N is omitted, use 1. +]], + +over = [[ +over [N] -- run next N lines, stepping over function calls| + +If N is omitted, use 1. +]], + +out = [[ +out [N] -- run lines until stepped out of N functions| + +If N is omitted, use 1. +If you are inside a function, using "out 1" will run until you return +from that function to the caller. +]], + +gotoo = [[ +gotoo [line file] -- step to line in file| + +This is equivalent to 'setb line file', followed by 'run', followed +by 'delb line file'. +]], + +listb = [[ +listb -- lists breakpoints| +]], + +listw = [[ +listw -- lists watch expressions| +]], + +set = [[ +set [level] -- set context to stack level, omitted=show| + +If level is omitted it just prints the current level set. +This sets the current context to the level given. This affects the +context used for several other functions (e.g. vars). The possible +levels are those shown by trace. +]], + +vars = [[ +vars [depth] -- list context locals to depth, omitted=1| + +If depth is omitted then uses 1. +Use a depth of 0 for the maximum. +Lists all non-nil local variables and all non-nil upvalues in the +currently set context. For variables that are tables, lists all fields +to the given depth. +]], + +fenv = [[ +fenv [depth] -- list context function env to depth, omitted=1| + +If depth is omitted then uses 1. +Use a depth of 0 for the maximum. +Lists all function environment variables in the currently set context. +For variables that are tables, lists all fields to the given depth. +]], + +glob = [[ +glob [depth] -- list globals to depth, omitted=1| + +If depth is omitted then uses 1. +Use a depth of 0 for the maximum. +Lists all global variables. +For variables that are tables, lists all fields to the given depth. +]], + +ups = [[ +ups -- list all the upvalue names| + +These names will also be in the "vars" list unless their value is nil. +This provides a means to identify which vars are upvalues and which are +locals. If a name is both an upvalue and a local, the local value takes +precedance. +]], + +locs = [[ +locs -- list all the locals names| + +These names will also be in the "vars" list unless their value is nil. +This provides a means to identify which vars are upvalues and which are +locals. If a name is both an upvalue and a local, the local value takes +precedance. +]], + +dump = [[ +dump [depth] -- dump all fields of variable to depth| + +If depth is omitted then uses 1. +Use a depth of 0 for the maximum. +Prints the value of in the currently set context level. If +is a table, lists all fields to the given depth. can be just a +name, or name.field or name.# to any depth, e.g. t.1.f accesses field +'f' in array element 1 in table 't'. + +Can also be called from a script as dump(var,depth). +]], + +tron = [[ +tron [crl] -- turn trace on for (c)alls, (r)etuns, (l)lines| + +If no parameter is given then tracing is turned off. +When tracing is turned on a line is printed to the console for each +debug 'event' selected. c=function calls, r=function returns, l=lines. +]], + +trace = [[ +trace -- dumps a stack trace| + +Format is [level] = file,line,name +The level is a candidate for use by the 'set' command. +]], + +info = [[ +info -- dumps the complete debug info captured| + +Only useful as a diagnostic aid for the debugger itself. This information +can be HUGE as it dumps all variables to the maximum depth, so be careful. +]], + +show = [[ +show line file X Y -- show X lines before and Y after line in file| + +If line is omitted or is '-' then the current set context line is used. +If file is omitted or is '-' then the current set context file is used. +If file is not fully qualified and cannot be opened as specified, then +a search for the file in the package[path] is performed using the usual +"require" searching rules. If no file extension is given, .lua is used. +Prints the lines from the source file around the given line. +]], + +exit = [[ +exit -- exits debugger, re-start it using pause()| +]], + +help = [[ +help [command] -- show this list or help for command| +]], + +[""] = [[ + -- execute a statement in the current context| + +The statement can be anything that is legal in the context, including +assignments. Such assignments affect the context and will be in force +immediately. Any results returned are printed. Use '=' as a short-hand +for 'return', e.g. "=func(arg)" will call 'func' with 'arg' and print +the results, and "=var" will just print the value of 'var'. +]], + +what = [[ +what -- show where is defined (if known)| +]], + +} +--}}} + +--{{{ local function getinfo(level,field) + +--like debug.getinfo but copes with no activation record at the given level +--and knows how to get 'field'. 'field' can be the name of any of the +--activation record fields or any of the 'what' names or nil for everything. +--only valid when using the stack level to get info, not a function name. + +local function getinfo(level,field) + level = level + 1 --to get to the same relative level as the caller + if not field then return debug.getinfo(level) end + local what + if field == 'name' or field == 'namewhat' then + what = 'n' + elseif field == 'what' or field == 'source' or field == 'linedefined' or field == 'lastlinedefined' or field == 'short_src' then + what = 'S' + elseif field == 'currentline' then + what = 'l' + elseif field == 'nups' then + what = 'u' + elseif field == 'func' then + what = 'f' + else + return debug.getinfo(level,field) + end + local ar = debug.getinfo(level,what) + if ar then return ar[field] else return nil end +end + +--}}} +--{{{ local function indented( level, ... ) + +local function indented( level, ... ) + io.write( string.rep(' ',level), table.concat({...}), '\n' ) +end + +--}}} +--{{{ local function dumpval( level, name, value, limit ) + +local dumpvisited + +local function dumpval( level, name, value, limit ) + local index + if type(name) == 'number' then + index = string.format('[%d] = ',name) + elseif type(name) == 'string' + and (name == '__VARSLEVEL__' or name == '__ENVIRONMENT__' or name == '__GLOBALS__' or name == '__UPVALUES__' or name == '__LOCALS__') then + --ignore these, they are debugger generated + return + elseif type(name) == 'string' and string.find(name,'^[_%a][_.%w]*$') then + index = name ..' = ' + else + index = string.format('[%q] = ',tostring(name)) + end + if type(value) == 'table' then + if dumpvisited[value] then + indented( level, index, string.format('ref%q;',dumpvisited[value]) ) + else + dumpvisited[value] = tostring(value) + if (limit or 0) > 0 and level+1 >= limit then + indented( level, index, dumpvisited[value] ) + else + indented( level, index, '{ -- ', dumpvisited[value] ) + for n,v in pairs(value) do + dumpval( level+1, n, v, limit ) + end + indented( level, '};' ) + end + end + else + if type(value) == 'string' then + if string.len(value) > 40 then + indented( level, index, '[[', value, ']];' ) + else + indented( level, index, string.format('%q',value), ';' ) + end + else + indented( level, index, tostring(value), ';' ) + end + end +end + +--}}} +--{{{ local function dumpvar( value, limit, name ) + +local function dumpvar( value, limit, name ) + dumpvisited = {} + dumpval( 0, name or tostring(value), value, limit ) +end + +--}}} +--{{{ local function show(file,line,before,after) + +--show +/-N lines of a file around line M + +local function show(file,line,before,after) + + line = tonumber(line or 1) + before = tonumber(before or 10) + after = tonumber(after or before) + + if not string.find(file,'%.') then file = file..'.lua' end + + local f = io.open(file,'r') + if not f then + --{{{ try to find the file in the path + + -- + -- looks for a file in the package path + -- + local path = package.path or LUA_PATH or '' + for c in string.gmatch (path, "[^;]+") do + local c = string.gsub (c, "%?%.lua", file) + f = io.open (c,'r') + if f then + break + end + end + + --}}} + if not f then + io.write('Cannot find '..file..'\n') + return + end + end + + local i = 0 + for l in f:lines() do + i = i + 1 + if i >= (line-before) then + if i > (line+after) then break end + if i == line then + io.write(i..'***\t'..l..'\n') + else + io.write(i..'\t'..l..'\n') + end + end + end + + f:close() + +end + +--}}} +--{{{ local function tracestack(l) + +local function gi( i ) + return function() i=i+1 return debug.getinfo(i),i end +end + +local function gl( level, j ) + return function() j=j+1 return debug.getlocal( level, j ) end +end + +local function gu( func, k ) + return function() k=k+1 return debug.getupvalue( func, k ) end +end + +local traceinfo + +local function tracestack(l) + local l = l + 1 --NB: +1 to get level relative to caller + traceinfo = {} + traceinfo.pausemsg = pausemsg + for ar,i in gi(l) do + table.insert( traceinfo, ar ) + if ar.what ~= 'C' then + local names = {} + local values = {} + for n,v in gl(i,0) do + if string.sub(n,1,1) ~= '(' then --ignore internal control variables + table.insert( names, n ) + table.insert( values, v ) + end + end + if #names > 0 then + ar.lnames = names + ar.lvalues = values + end + end + if ar.func then + local names = {} + local values = {} + for n,v in gu(ar.func,0) do + if string.sub(n,1,1) ~= '(' then --ignore internal control variables + table.insert( names, n ) + table.insert( values, v ) + end + end + if #names > 0 then + ar.unames = names + ar.uvalues = values + end + end + end +end + +--}}} +--{{{ local function trace() + +local function trace(set) + local mark + for level,ar in ipairs(traceinfo) do + if level == set then + mark = '***' + else + mark = '' + end + io.write('['..level..']'..mark..'\t'..(ar.name or ar.what)..' in '..ar.short_src..':'..ar.currentline..'\n') + end +end + +--}}} +--{{{ local function info() + +local function info() dumpvar( traceinfo, 0, 'traceinfo' ) end + +--}}} + +--{{{ local function set_breakpoint(file, line, once) + +local function set_breakpoint(file, line, once) + if not breakpoints[line] then + breakpoints[line] = {} + end + if once then + breakpoints[line][file] = 1 + else + breakpoints[line][file] = true + end +end + +--}}} +--{{{ local function remove_breakpoint(file, line) + +local function remove_breakpoint(file, line) + if breakpoints[line] then + breakpoints[line][file] = nil + end +end + +--}}} +--{{{ local function has_breakpoint(file, line) + +--allow for 'sloppy' file names +--search for file and all variations walking up its directory hierachy +--ditto for the file with no extension +--a breakpoint can be permenant or once only, if once only its removed +--after detection here, these are used for temporary breakpoints in the +--debugger loop when executing the 'gotoo' command +--a breakpoint on line 0 of a file means any line in that file + +local function has_breakpoint(file, line) + local isLine = breakpoints[line] + local isZero = breakpoints[0] + if not isLine and not isZero then return false end + local noext = string.gsub(file,"(%..-)$",'',1) + if noext == file then noext = nil end + while file do + if isLine and isLine[file] then + if isLine[file] == 1 then isLine[file] = nil end + return true + end + if isZero and isZero[file] then + if isZero[file] == 1 then isZero[file] = nil end + return true + end + if IsWindows then + file = string.match(file,"[:/\\](.+)$") + else + file = string.match(file,"[:/](.+)$") + end + end + while noext do + if isLine and isLine[noext] then + if isLine[noext] == 1 then isLine[noext] = nil end + return true + end + if isZero and isZero[noext] then + if isZero[noext] == 1 then isZero[noext] = nil end + return true + end + if IsWindows then + noext = string.match(noext,"[:/\\](.+)$") + else + noext = string.match(noext,"[:/](.+)$") + end + end + return false +end + +--}}} +--{{{ local function capture_vars(ref,level,line) + +local function capture_vars(ref,level,line) + --get vars, file and line for the given level relative to debug_hook offset by ref + + local lvl = ref + level --NB: This includes an offset of +1 for the call to here + + --{{{ capture variables + + local ar = debug.getinfo(lvl, "f") + if not ar then return {},'?',0 end + + local vars = {__UPVALUES__={}, __LOCALS__={}} + local i + + local func = ar.func + if func then + i = 1 + while true do + local name, value = debug.getupvalue(func, i) + if not name then break end + if string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables + vars[name] = value + vars.__UPVALUES__[i] = name + end + i = i + 1 + end + vars.__ENVIRONMENT__ = getfenv(func) + end + + vars.__GLOBALS__ = getfenv(0) + + i = 1 + while true do + local name, value = debug.getlocal(lvl, i) + if not name then break end + if string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables + vars[name] = value + vars.__LOCALS__[i] = name + end + i = i + 1 + end + + vars.__VARSLEVEL__ = level + + if func then + --NB: Do not do this until finished filling the vars table + setmetatable(vars, { __index = getfenv(func), __newindex = getfenv(func) }) + end + + --NB: Do not read or write the vars table anymore else the metatable functions will get invoked! + + --}}} + + local file = getinfo(lvl, "source") + if string.find(file, "@") == 1 then + file = string.sub(file, 2) + end + if IsWindows then file = string.lower(file) end + + if not line then + line = getinfo(lvl, "currentline") + end + + return vars,file,line + +end + +--}}} +--{{{ local function restore_vars(ref,vars) + +local function restore_vars(ref,vars) + + if type(vars) ~= 'table' then return end + + local level = vars.__VARSLEVEL__ --NB: This level is relative to debug_hook offset by ref + if not level then return end + + level = level + ref --NB: This includes an offset of +1 for the call to here + + local i + local written_vars = {} + + i = 1 + while true do + local name, value = debug.getlocal(level, i) + if not name then break end + if vars[name] and string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables + debug.setlocal(level, i, vars[name]) + written_vars[name] = true + end + i = i + 1 + end + + local ar = debug.getinfo(level, "f") + if not ar then return end + + local func = ar.func + if func then + + i = 1 + while true do + local name, value = debug.getupvalue(func, i) + if not name then break end + if vars[name] and string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables + if not written_vars[name] then + debug.setupvalue(func, i, vars[name]) + end + written_vars[name] = true + end + i = i + 1 + end + + end + +end + +--}}} +--{{{ local function trace_event(event, line, level) + +local function print_trace(level,depth,event,file,line,name) + + --NB: level here is relative to the caller of trace_event, so offset by 2 to get to there + level = level + 2 + + local file = file or getinfo(level,'short_src') + local line = line or getinfo(level,'currentline') + local name = name or getinfo(level,'name') + + local prefix = '' + if current_thread ~= 'main' then prefix = '['..tostring(current_thread)..'] ' end + + io.write(prefix.. + string.format('%08.2f:%02i.',os.clock(),depth).. + string.rep('.',depth%32).. + (file or '')..' ('..(line or '')..') '.. + (name or '').. + ' ('..event..')\n') + +end + +local function trace_event(event, line, level) + + if event == 'return' and trace_returns then + --note the line info for later + ret_file = getinfo(level+1,'short_src') + ret_line = getinfo(level+1,'currentline') + ret_name = getinfo(level+1,'name') + end + + if event ~= 'line' then return end + + local slevel = stack_level[current_thread] + local tlevel = trace_level[current_thread] + + if trace_calls and slevel > tlevel then + --we are now in the function called, so look back 1 level further to find the calling file and line + print_trace(level+1,slevel-1,'c',nil,nil,getinfo(level+1,'name')) + end + + if trace_returns and slevel < tlevel then + print_trace(level,slevel,'r',ret_file,ret_line,ret_name) + end + + if trace_lines then + print_trace(level,slevel,'l') + end + + trace_level[current_thread] = stack_level[current_thread] + +end + +--}}} +--{{{ local function report(ev, vars, file, line, idx_watch) + +local function report(ev, vars, file, line, idx_watch) + function show_source() + show(traceinfo[1].short_src, traceinfo[1].currentline, 2, 2) + end + + local vars = vars or {} + local file = file or '?' + local line = line or 0 + local prefix = '' + if current_thread ~= 'main' then prefix = '['..tostring(current_thread)..'] ' end + if ev == events.STEP then + io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..')\n') + show_source() + elseif ev == events.BREAK then + io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..') (breakpoint)\n') + show_source() + elseif ev == events.WATCH then + io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..')'.." (watch expression "..idx_watch.. ": ["..watches[idx_watch].exp.."])\n") + show_source() + elseif ev == events.SET then + --do nothing + else + io.write(prefix.."Error in application: "..file.." line "..line.."\n") + end + if ev ~= events.SET then + if pausemsg and pausemsg ~= '' then io.write('Message: '..pausemsg..'\n') end + pausemsg = '' + end + return vars, file, line +end + +--}}} + +--{{{ local function debugger_loop(ev, vars, file, line, idx_watch) + +local last_line = "" + +local function debugger_loop(ev, vars, file, line, idx_watch) + + local eval_env = vars or {} + local breakfile = file or '?' + local breakline = line or 0 + + local command, args + + --{{{ local function getargs(spec) + + --get command arguments according to the given spec from the args string + --the spec has a single character for each argument, arguments are separated + --by white space, the spec characters can be one of: + -- F for a filename (defaults to breakfile if - given in args) + -- L for a line number (defaults to breakline if - given in args) + -- N for a number + -- V for a variable name + -- S for a string + + local function getargs(spec) + local res={} + local char,arg + local ptr=1 + for i=1,string.len(spec) do + char = string.sub(spec,i,i) + if char == 'F' then + _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr) + if not arg or arg == '' then arg = '-' end + if arg == '-' then arg = breakfile end + elseif char == 'L' then + _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr) + if not arg or arg == '' then arg = '-' end + if arg == '-' then arg = breakline end + arg = tonumber(arg) or 0 + elseif char == 'N' then + _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr) + if not arg or arg == '' then arg = '0' end + arg = tonumber(arg) or 0 + elseif char == 'V' then + _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr) + if not arg or arg == '' then arg = '' end + elseif char == 'S' then + _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr) + if not arg or arg == '' then arg = '' end + else + arg = '' + end + table.insert(res,arg or '') + end + return unpack(res) + end + + --}}} + + while true do + io.write("[DEBUG]> ") + local line = io.read("*line") + if line == nil then io.write('\n'); line = 'exit' end + + if line == "" then + line = last_line + else + last_line = line + end + io.write("'" .. last_line .. "'\n") + + if string.find(line, "^[a-z]+") then + command = string.sub(line, string.find(line, "^[a-z]+")) + args = string.gsub(line,"^[a-z]+%s*",'',1) --strip command off line + else + command = '' + end + + command = aliases[command] or command + + if command == "setb" then + --{{{ set breakpoint + + local line, filename = getargs('LF') + if filename ~= '' and line ~= '' then + set_breakpoint(filename,line) + io.write("Breakpoint set in file "..filename..' line '..line..'\n') + else + io.write("Bad request\n") + end + + --}}} + + elseif command == "delb" then + --{{{ delete breakpoint + + local line, filename = getargs('LF') + if filename ~= '' and line ~= '' then + remove_breakpoint(filename, line) + io.write("Breakpoint deleted from file "..filename..' line '..line.."\n") + else + io.write("Bad request\n") + end + + --}}} + + elseif command == "delallb" then + --{{{ delete all breakpoints + breakpoints = {} + io.write('All breakpoints deleted\n') + --}}} + + elseif command == "listb" then + --{{{ list breakpoints + for i, v in pairs(breakpoints) do + for ii, vv in pairs(v) do + io.write("Break at: "..i..' in '..ii..'\n') + end + end + --}}} + + elseif command == "setw" then + --{{{ set watch expression + + if args and args ~= '' then + local func = loadstring("return(" .. args .. ")") + local newidx = #watches + 1 + watches[newidx] = {func = func, exp = args} + io.write("Set watch exp no. " .. newidx..'\n') + else + io.write("Bad request\n") + end + + --}}} + + elseif command == "delw" then + --{{{ delete watch expression + + local index = tonumber(args) + if index then + watches[index] = nil + io.write("Watch expression deleted\n") + else + io.write("Bad request\n") + end + + --}}} + + elseif command == "delallw" then + --{{{ delete all watch expressions + watches = {} + io.write('All watch expressions deleted\n') + --}}} + + elseif command == "listw" then + --{{{ list watch expressions + for i, v in pairs(watches) do + io.write("Watch exp. " .. i .. ": " .. v.exp..'\n') + end + --}}} + + elseif command == "run" then + --{{{ run until breakpoint + step_into = false + step_over = false + return 'cont' + --}}} + + elseif command == "step" then + --{{{ step N lines (into functions) + local N = tonumber(args) or 1 + step_over = false + step_into = true + step_lines = tonumber(N or 1) + return 'cont' + --}}} + + elseif command == "over" then + --{{{ step N lines (over functions) + local N = tonumber(args) or 1 + step_into = false + step_over = true + step_lines = tonumber(N or 1) + step_level[current_thread] = stack_level[current_thread] + return 'cont' + --}}} + + elseif command == "out" then + --{{{ step N lines (out of functions) + local N = tonumber(args) or 1 + step_into = false + step_over = true + step_lines = 1 + step_level[current_thread] = stack_level[current_thread] - tonumber(N or 1) + return 'cont' + --}}} + + elseif command == "gotoo" then + --{{{ step until reach line + local line, filename = getargs('LF') + if line ~= '' then + step_over = false + step_into = false + if has_breakpoint(filename,line) then + return 'cont' + else + set_breakpoint(filename,line,true) + return 'cont' + end + else + io.write("Bad request\n") + end + --}}} + + elseif command == "set" then + --{{{ set/show context level + local level = args + if level and level == '' then level = nil end + if level then return level end + --}}} + + elseif command == "vars" then + --{{{ list context variables + local depth = args + if depth and depth == '' then depth = nil end + depth = tonumber(depth) or 1 + dumpvar(eval_env, depth+1, 'variables') + --}}} + + elseif command == "glob" then + --{{{ list global variables + local depth = args + if depth and depth == '' then depth = nil end + depth = tonumber(depth) or 1 + dumpvar(eval_env.__GLOBALS__,depth+1,'globals') + --}}} + + elseif command == "fenv" then + --{{{ list function environment variables + local depth = args + if depth and depth == '' then depth = nil end + depth = tonumber(depth) or 1 + dumpvar(eval_env.__ENVIRONMENT__,depth+1,'environment') + --}}} + + elseif command == "ups" then + --{{{ list upvalue names + dumpvar(eval_env.__UPVALUES__,2,'upvalues') + --}}} + + elseif command == "locs" then + --{{{ list locals names + dumpvar(eval_env.__LOCALS__,2,'upvalues') + --}}} + + elseif command == "what" then + --{{{ show where a function is defined + if args and args ~= '' then + local v = eval_env + local n = nil + for w in string.gmatch(args,"[%w_]+") do + v = v[w] + if n then n = n..'.'..w else n = w end + if not v then break end + end + if type(v) == 'function' then + local def = debug.getinfo(v,'S') + if def then + io.write(def.what..' in '..def.short_src..' '..def.linedefined..'..'..def.lastlinedefined..'\n') + else + io.write('Cannot get info for '..v..'\n') + end + else + io.write(v..' is not a function\n') + end + else + io.write("Bad request\n") + end + --}}} + + elseif command == "dump" then + --{{{ dump a variable + local name, depth = getargs('VN') + if name ~= '' then + if depth == '' or depth == 0 then depth = nil end + depth = tonumber(depth or 1) + local v = eval_env + local n = nil + for w in string.gmatch(name,"[^%.]+") do --get everything between dots + if tonumber(w) then + v = v[tonumber(w)] + else + v = v[w] + end + if n then n = n..'.'..w else n = w end + if not v then break end + end + dumpvar(v,depth+1,n) + else + io.write("Bad request\n") + end + --}}} + + elseif command == "show" then + --{{{ show file around a line or the current breakpoint + + local line, file, before, after = getargs('LFNN') + if before == 0 then before = 10 end + if after == 0 then after = before end + + if file ~= '' and file ~= "=stdin" then + show(file,line,before,after) + else + io.write('Nothing to show\n') + end + + --}}} + + elseif command == "poff" then + --{{{ turn pause command off + pause_off = true + --}}} + + elseif command == "pon" then + --{{{ turn pause command on + pause_off = false + --}}} + + elseif command == "tron" then + --{{{ turn tracing on/off + local option = getargs('S') + trace_calls = false + trace_returns = false + trace_lines = false + if string.find(option,'c') then trace_calls = true end + if string.find(option,'r') then trace_returns = true end + if string.find(option,'l') then trace_lines = true end + --}}} + + elseif command == "trace" then + --{{{ dump a stack trace + trace(eval_env.__VARSLEVEL__) + --}}} + + elseif command == "info" then + --{{{ dump all debug info captured + info() + --}}} + + elseif command == "pause" then + --{{{ not allowed in here + io.write('pause() should only be used in the script you are debugging\n') + --}}} + + elseif command == "help" then + --{{{ help + local command = getargs('S') + if command ~= '' and hints[command] then + io.write(hints[command]..'\n') + else + for _,v in pairs(hints) do + local _,_,h = string.find(v,"(.+)|") + io.write(h..'\n') + end + end + --}}} + + elseif command == "exit" then + --{{{ exit debugger + return 'stop' + --}}} + + elseif line ~= '' then + --{{{ just execute whatever it is in the current context + + --map line starting with "=..." to "return ..." + if string.sub(line,1,1) == '=' then line = string.gsub(line,'=','return ',1) end + + local ok, func = pcall(loadstring,line) + if func == nil then --Michael.Bringmann@lsi.com + io.write("Compile error: "..line..'\n') + elseif not ok then + io.write("Compile error: "..func..'\n') + else + setfenv(func, eval_env) + local res = {pcall(func)} + if res[1] then + if res[2] then + table.remove(res,1) + for _,v in ipairs(res) do + io.write(tostring(v)) + io.write('\t') + end + io.write('\n') + end + --update in the context + return 0 + else + io.write("Run error: "..res[2]..'\n') + end + end + + --}}} + end + end + +end + +--}}} +--{{{ local function debug_hook(event, line, level, thread) + +local function debug_hook(event, line, level, thread) + if not started then debug.sethook(); coro_debugger = nil; return end + current_thread = thread or 'main' + local level = level or 2 + trace_event(event,line,level) + if event == "call" then + stack_level[current_thread] = stack_level[current_thread] + 1 + elseif event == "return" then + stack_level[current_thread] = stack_level[current_thread] - 1 + if stack_level[current_thread] < 0 then stack_level[current_thread] = 0 end + else + local vars,file,line = capture_vars(level,1,line) + local stop, ev, idx = false, events.STEP, 0 + while true do + for index, value in pairs(watches) do + setfenv(value.func, vars) + local status, res = pcall(value.func) + if status and res then + ev, idx = events.WATCH, index + stop = true + break + end + end + if stop then break end + if (step_into) + or (step_over and (stack_level[current_thread] <= step_level[current_thread] or stack_level[current_thread] == 0)) then + step_lines = step_lines - 1 + if step_lines < 1 then + ev, idx = events.STEP, 0 + break + end + end + if has_breakpoint(file, line) then + ev, idx = events.BREAK, 0 + break + end + return + end + tracestack(level) + if not coro_debugger then + io.write("\nLua Debugger\n") + vars, file, line = report(ev, vars, file, line, idx) + io.write("Type 'help' for commands\n") + coro_debugger = true + else + vars, file, line = report(ev, vars, file, line, idx) + end + local last_next = 1 + local next = 'ask' + local silent = false + while true do + if next == 'ask' then + next = debugger_loop(ev, vars, file, line, idx) + elseif next == 'cont' then + return + elseif next == 'stop' then + started = false + debug.sethook() + coro_debugger = nil + return + elseif tonumber(next) then --get vars for given level or last level + next = tonumber(next) + if next == 0 then silent = true; next = last_next else silent = false end + last_next = next + restore_vars(level,vars) + vars, file, line = capture_vars(level,next) + if not silent then + if vars and vars.__VARSLEVEL__ then + io.write('Level: '..vars.__VARSLEVEL__..'\n') + else + io.write('No level set\n') + end + end + ev = events.SET + next = 'ask' + else + io.write('Unknown command from debugger_loop: '..tostring(next)..'\n') + io.write('Stopping debugger\n') + next = 'stop' + end + end + end +end + +--}}} + +--{{{ coroutine.create + +--This function overrides the built-in for the purposes of propagating +--the debug hook settings from the creator into the created coroutine. + +_G.coroutine.create = function(f) + local thread + local hook, mask, count = debug.gethook() + if hook then + local function thread_hook(event,line) + hook(event,line,3,thread) + end + thread = cocreate(function(...) + stack_level[thread] = 0 + trace_level[thread] = 0 + step_level [thread] = 0 + debug.sethook(thread_hook,mask,count) + return f(...) + end) + return thread + else + return cocreate(f) + end +end + +--}}} +--{{{ coroutine.wrap + +--This function overrides the built-in for the purposes of propagating +--the debug hook settings from the creator into the created coroutine. + +_G.coroutine.wrap = function(f) + local thread + local hook, mask, count = debug.gethook() + if hook then + local function thread_hook(event,line) + hook(event,line,3,thread) + end + thread = cowrap(function(...) + stack_level[thread] = 0 + trace_level[thread] = 0 + step_level [thread] = 0 + debug.sethook(thread_hook,mask,count) + return f(...) + end) + return thread + else + return cowrap(f) + end +end + +--}}} + +--{{{ function pause(x,l,f) + +-- +-- Starts/resumes a debug session +-- + +function pause(x,l,f) + if not f and pause_off then return end --being told to ignore pauses + pausemsg = x or 'pause' + local lines + local src = getinfo(2,'short_src') + if l then + lines = l --being told when to stop + elseif src == "stdin" then + lines = 1 --if in a console session, stop now + else + lines = 2 --if in a script, stop when get out of pause() + end + if started then + --we'll stop now 'cos the existing debug hook will grab us + step_lines = lines + step_into = true + debug.sethook(debug_hook, "crl") --reset it in case some external agent fiddled with it + else + --set to stop when get out of pause() + trace_level[current_thread] = 0 + step_level [current_thread] = 0 + stack_level[current_thread] = 1 + step_lines = lines + step_into = true + started = true + debug.sethook(debug_hook, "crl") --NB: this will cause an immediate entry to the debugger_loop + end +end + +--}}} +--{{{ function dump(v,depth) + +--shows the value of the given variable, only really useful +--when the variable is a table +--see dump debug command hints for full semantics + +function dump(v,depth) + dumpvar(v,(depth or 1)+1,tostring(v)) +end + +--}}} +--{{{ function debug.traceback(x) + +local _traceback = debug.traceback --note original function + +--override standard function +debug.traceback = function(x) + local assertmsg = _traceback(x) --do original function + pause(x) --let user have a look at stuff + return assertmsg --carry on +end + +_TRACEBACK = debug.traceback --Lua 5.0 function + +--}}} + +-------------------------------------------------------------------------------- +-- dir.lua +-- + +-- +-- Copyright (c) 2012 Martin Ridgers +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +-------------------------------------------------------------------------------- +function dir_match_generator_impl(text) + -- Strip off any path components that may be on text. + local prefix = "" + local i = text:find("[\\/:][^\\/:]*$") + if i then + prefix = text:sub(1, i) + end + + local include_dots = text:find("%.+$") ~= nil + + local matches = {} + local mask = text.."*" + + -- Find matches. + for _, dir in ipairs(clink.find_dirs(mask, true)) do + local file = prefix..dir + + if include_dots or (dir ~= "." and dir ~= "..") then + if clink.is_match(text, file) then + table.insert(matches, prefix..dir) + end + end + end + + return matches +end + +-------------------------------------------------------------------------------- +local function dir_match_generator(word) + local matches = dir_match_generator_impl(word) + + -- If there was no matches but text is a dir then use it as the single match. + -- Otherwise tell readline that matches are files and it will do magic. + if #matches == 0 then + if clink.is_dir(rl_state.text) then + table.insert(matches, rl_state.text) + end + else + clink.matches_are_files() + end + + return matches +end + +-------------------------------------------------------------------------------- +clink.arg.register_parser("cd", dir_match_generator) +clink.arg.register_parser("chdir", dir_match_generator) +clink.arg.register_parser("pushd", dir_match_generator) +clink.arg.register_parser("rd", dir_match_generator) +clink.arg.register_parser("rmdir", dir_match_generator) +clink.arg.register_parser("md", dir_match_generator) +clink.arg.register_parser("mkdir", dir_match_generator) + +-- vim: expandtab + +-------------------------------------------------------------------------------- +-- env.lua +-- + +-- +-- Copyright (c) 2012 Martin Ridgers +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +-------------------------------------------------------------------------------- +local special_env_vars = { + "cd", "date", "time", "random", "errorlevel", + "cmdextversion", "cmdcmdline", "highestnumanodenumber" +} + +-------------------------------------------------------------------------------- +local function env_vars_display_filter(matches) + local to_display = {} + for _, m in ipairs(matches) do + local _, _, out = m:find("(%%[^%%]+%%)$") + table.insert(to_display, out) + end + + return to_display +end + +-------------------------------------------------------------------------------- +local function env_vars_find_matches(candidates, prefix, part) + local part_len = #part + for _, name in ipairs(candidates) do + if clink.lower(name:sub(1, part_len)) == part then + clink.add_match(prefix..'%'..name:lower()..'%') + end + end +end + +-------------------------------------------------------------------------------- +local function env_vars_match_generator(text, first, last) + local all = rl_state.line_buffer:sub(1, last) + + -- Skip pairs of %s + local i = 1 + for _, r in function () return all:find("%b%%", i) end do + i = r + 2 + end + + -- Find a solitary % + local i = all:find("%%", i) + if not i then + return false + end + + if i < first then + return false + end + + local part = clink.lower(all:sub(i + 1)) + local part_len = #part + + i = i - first + local prefix = text:sub(1, i) + + env_vars_find_matches(clink.get_env_var_names(), prefix, part) + env_vars_find_matches(special_env_vars, prefix, part) + + if clink.match_count() >= 1 then + clink.match_display_filter = env_vars_display_filter + + clink.suppress_char_append() + clink.suppress_quoting() + + return true + end + + return false +end + +-------------------------------------------------------------------------------- +if clink.get_host_process() == "cmd.exe" then + clink.register_match_generator(env_vars_match_generator, 10) +end + +-- vim: expandtab + +-------------------------------------------------------------------------------- +-- exec.lua +-- + +-- +-- Copyright (c) 2012 Martin Ridgers +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +-------------------------------------------------------------------------------- +local dos_commands = { + "assoc", "break", "call", "cd", "chcp", "chdir", "cls", "color", "copy", + "date", "del", "dir", "diskcomp", "diskcopy", "echo", "endlocal", "erase", + "exit", "for", "format", "ftype", "goto", "graftabl", "if", "md", "mkdir", + "mklink", "more", "move", "path", "pause", "popd", "prompt", "pushd", "rd", + "rem", "ren", "rename", "rmdir", "set", "setlocal", "shift", "start", + "time", "title", "tree", "type", "ver", "verify", "vol" +} + +-------------------------------------------------------------------------------- +local function get_environment_paths() + local paths = clink.split(clink.get_env("PATH"), ";") + + -- We're expecting absolute paths and as ';' is a valid path character + -- there maybe unneccessary splits. Here we resolve them. + local paths_merged = { paths[1] } + for i = 2, #paths, 1 do + if not paths[i]:find("^[a-zA-Z]:") then + local t = paths_merged[#paths_merged]; + paths_merged[#paths_merged] = t..paths[i] + else + table.insert(paths_merged, paths[i]) + end + end + + -- Append slashes. + for i = 1, #paths_merged, 1 do + paths_merged[i] = paths_merged[i].."/" + end + + return paths_merged +end + +-------------------------------------------------------------------------------- +local function exec_find_dirs(pattern, case_map) + local ret = {} + + for _, dir in ipairs(clink.find_dirs(pattern, case_map)) do + if dir ~= "." and dir ~= ".." then + table.insert(ret, dir) + end + end + + return ret +end + +-------------------------------------------------------------------------------- +local function exec_match_generator(text, first, last) + -- If match style setting is < 0 then consider executable matching disabled. + local match_style = clink.get_setting_int("exec_match_style") + if match_style < 0 then + return false + end + + -- We're only interested in exec completion if this is the first word of the + -- line, or the first word after a command separator. + if clink.get_setting_int("space_prefix_match_files") > 0 then + if first > 1 then + return false + end + else + local leading = rl_state.line_buffer:sub(1, first - 1) + local is_first = leading:find("^%s*\"*$") + if not is_first then + return false + end + end + + -- Split text into directory and name + local text_dir = "" + local text_name = text + local i = text:find("[\\/:][^\\/:]*$") + if i then + text_dir = text:sub(1, i) + text_name = text:sub(i + 1) + end + + local paths + if not text:find("[\\/:]") then + -- If the terminal is cmd.exe check it's commands for matches. + if clink.get_host_process() == "cmd.exe" then + clink.match_words(text, dos_commands) + end + + -- Add console aliases as matches. + local aliases = clink.get_console_aliases() + clink.match_words(text, aliases) + + paths = get_environment_paths(); + else + paths = {} + + -- 'text' is an absolute or relative path. If we're doing Bash-style + -- matching should now consider directories. + if match_style < 1 then + match_style = 2 + else + match_style = 1 + end + end + + -- Should we also consider the path referenced by 'text'? + if match_style >= 1 then + table.insert(paths, text_dir) + end + + -- Search 'paths' for files ending in 'suffices' and look for matches + local suffices = clink.split(clink.get_env("pathext"), ";") + for _, suffix in ipairs(suffices) do + for _, path in ipairs(paths) do + local files = clink.find_files(path.."*"..suffix, false) + for _, file in ipairs(files) do + if clink.is_match(text_name, file) then + clink.add_match(text_dir..file) + end + end + end + end + + -- Lastly we may wish to consider directories too. + if clink.match_count() == 0 or match_style >= 2 then + clink.match_files(text.."*", true, exec_find_dirs) + end + + clink.matches_are_files() + return true +end + +-------------------------------------------------------------------------------- +clink.register_match_generator(exec_match_generator, 50) + +-- vim: expandtab + +-------------------------------------------------------------------------------- +-- git.lua +-- + +-- +-- Copyright (c) 2012 Martin Ridgers +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +-------------------------------------------------------------------------------- +local git_argument_tree = { + -- Porcelain and ancillary commands from git's man page. + "add", "am", "archive", "bisect", "branch", "bundle", "checkout", + "cherry-pick", "citool", "clean", "clone", "commit", "describe", "diff", + "fetch", "format-patch", "gc", "grep", "gui", "init", "log", "merge", "mv", + "notes", "pull", "push", "rebase", "reset", "revert", "rm", "shortlog", + "show", "stash", "status", "submodule", "tag", "config", "fast-export", + "fast-import", "filter-branch", "lost-found", "mergetool", "pack-refs", + "prune", "reflog", "relink", "remote", "repack", "replace", "repo-config", + "annotate", "blame", "cherry", "count-objects", "difftool", "fsck", + "get-tar-commit-id", "help", "instaweb", "merge-tree", "rerere", + "rev-parse", "show-branch", "verify-tag", "whatchanged" +} + +clink.arg.register_parser("git", git_argument_tree) + +-- vim: expandtab + +-------------------------------------------------------------------------------- +-- go.lua +-- + +-- +-- Copyright (c) 2013 Dobroslaw Zybort +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +-------------------------------------------------------------------------------- +local function flags(...) + local p = clink.arg.new_parser() + p:set_flags(...) + return p +end + +-------------------------------------------------------------------------------- +local go_tool_parser = clink.arg.new_parser() +go_tool_parser:set_flags("-n") +go_tool_parser:set_arguments({ + "8a", "8c", "8g", "8l", "addr2line", "cgo", "dist", "nm", "objdump", + "pack", + "cover" .. flags("-func", "-html", "-mode", "-o", "-var"), + "fix" .. flags("-diff", "-force", "-r"), + "prof" .. flags("-p", "-t", "-d", "-P", "-h", "-f", "-l", "-r", "-s", + "-hs"), + "pprof" .. flags(-- Options: + "--cum", "--base", "--interactive", "--seconds", + "--add_lib", "--lib_prefix", + -- Reporting Granularity: + "--addresses", "--lines", "--functions", "--files", + -- Output type: + "--text", "--callgrind", "--gv", "--web", "--list", + "--disasm", "--symbols", "--dot", "--ps", "--pdf", + "--svg", "--gif", "--raw", + -- Heap-Profile Options: + "--inuse_space", "--inuse_objects", "--alloc_space", + "--alloc_objects", "--show_bytes", "--drop_negative", + -- Contention-profile options: + "--total_delay", "--contentions", "--mean_delay", + -- Call-graph Options: + "--nodecount", "--nodefraction", "--edgefraction", + "--focus", "--ignore", "--scale", "--heapcheck", + -- Miscellaneous: + "--tools", "--test", "--help", "--version"), + "vet" .. flags("-all", "-asmdecl", "-assign", "-atomic", "-buildtags", + "-composites", "-compositewhitelist", "-copylocks", + "-methods", "-nilfunc", "-printf", "-printfuncs", + "-rangeloops", "-shadow", "-shadowstrict", "-structtags", + "-test", "-unreachable", "-v"), + "yacc" .. flags("-l", "-o", "-p", "-v"), +}) + +-------------------------------------------------------------------------------- +local go_parser = clink.arg.new_parser() +go_parser:set_arguments({ + "env", + "fix", + "version", + "build" .. flags("-o", "-a", "-n", "-p", "-installsuffix", "-v", "-x", + "-work", "-gcflags", "-ccflags", "-ldflags", + "-gccgoflags", "-tags", "-compiler", "-race"), + "clean" .. flags("-i", "-n", "-r", "-x"), + "fmt" .. flags("-n", "-x"), + "get" .. flags("-d", "-fix", "-t", "-u", + -- Build flags + "-a", "-n", "-p", "-installsuffix", "-v", "-x", + "-work", "-gcflags", "-ccflags", "-ldflags", + "-gccgoflags", "-tags", "-compiler", "-race"), + "install" .. flags(-- All `go build` flags + "-o", "-a", "-n", "-p", "-installsuffix", "-v", "-x", + "-work", "-gcflags", "-ccflags", "-ldflags", + "-gccgoflags", "-tags", "-compiler", "-race"), + "list" .. flags("-e", "-race", "-f", "-json", "-tags"), + "run" .. flags("-exec", + -- Build flags + "-a", "-n", "-p", "-installsuffix", "-v", "-x", + "-work", "-gcflags", "-ccflags", "-ldflags", + "-gccgoflags", "-tags", "-compiler", "-race"), + "test" .. flags(-- Local. + "-c", "-file", "-i", "-cover", "-coverpkg", + -- Build flags + "-a", "-n", "-p", "-x", "-work", "-ccflags", + "-gcflags", "-exec", "-ldflags", "-gccgoflags", + "-tags", "-compiler", "-race", "-installsuffix", + -- Passed to 6.out + "-bench", "-benchmem", "-benchtime", "-covermode", + "-coverprofile", "-cpu", "-cpuprofile", "-memprofile", + "-memprofilerate", "-blockprofile", + "-blockprofilerate", "-outputdir", "-parallel", "-run", + "-short", "-timeout", "-v"), + "tool" .. go_tool_parser, + "vet" .. flags("-n", "-x"), +}) + +-------------------------------------------------------------------------------- +local go_help_parser = clink.arg.new_parser() +go_help_parser:set_arguments({ + "help" .. clink.arg.new_parser():set_arguments({ + go_parser:flatten_argument(1) + }) +}) + +-------------------------------------------------------------------------------- +local godoc_parser = clink.arg.new_parser() +godoc_parser:set_flags( + "-zip", "-write_index", "-analysis", "-http", "-server", "-html","-src", + "-url", "-q", "-v", "-goroot", "-tabwidth", "-timestamps", "-templates", + "-play", "-ex", "-links", "-index", "-index_files", "-maxresults", + "-index_throttle", "-notes", "-httptest.serve" +) + +-------------------------------------------------------------------------------- +local gofmt_parser = clink.arg.new_parser() +gofmt_parser:set_flags( + "-cpuprofile", "-d", "-e", "-l", "-r", "-s", "-w" +) + +-------------------------------------------------------------------------------- +clink.arg.register_parser("go", go_parser) +clink.arg.register_parser("go", go_help_parser) +clink.arg.register_parser("godoc", godoc_parser) +clink.arg.register_parser("gofmt", gofmt_parser) + +-------------------------------------------------------------------------------- +-- hg.lua +-- + +-- +-- Copyright (c) 2012 Martin Ridgers +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +-------------------------------------------------------------------------------- +local hg_tree = { + "add", "addremove", "annotate", "archive", "backout", "bisect", "bookmarks", + "branch", "branches", "bundle", "cat", "clone", "commit", "copy", "diff", + "export", "forget", "grep", "heads", "help", "identify", "import", + "incoming", "init", "locate", "log", "manifest", "merge", "outgoing", + "parents", "paths", "pull", "push", "recover", "remove", "rename", "resolve", + "revert", "rollback", "root", "serve", "showconfig", "status", "summary", + "tag", "tags", "tip", "unbundle", "update", "verify", "version", "graft", + "phases" +} + +clink.arg.register_parser("hg", hg_tree) + +-- vim: expandtab + +-------------------------------------------------------------------------------- +-- p4.lua +-- + +-- +-- Copyright (c) 2012 Martin Ridgers +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +-------------------------------------------------------------------------------- +local p4_tree = { + "add", "annotate", "attribute", "branch", "branches", "browse", "change", + "changes", "changelist", "changelists", "client", "clients", "copy", + "counter", "counters", "cstat", "delete", "depot", "depots", "describe", + "diff", "diff2", "dirs", "edit", "filelog", "files", "fix", "fixes", + "flush", "fstat", "grep", "group", "groups", "have", "help", "info", + "integrate", "integrated", "interchanges", "istat", "job", "jobs", "label", + "labels", "labelsync", "legal", "list", "lock", "logger", "login", + "logout", "merge", "move", "opened", "passwd", "populate", "print", + "protect", "protects", "reconcile", "rename", "reopen", "resolve", + "resolved", "revert", "review", "reviews", "set", "shelve", "status", + "sizes", "stream", "streams", "submit", "sync", "tag", "tickets", "unlock", + "unshelve", "update", "user", "users", "where", "workspace", "workspaces" +} + +clink.arg.register_parser("p4", p4_tree) + +-------------------------------------------------------------------------------- +local p4vc_tree = { + "help", "branchmappings", "branches", "diff", "groups", "branch", "change", + "client", "workspace", "depot", "group", "job", "label", "user", "jobs", + "labels", "pendingchanges", "resolve", "revisiongraph", "revgraph", + "streamgraph", "streams", "submit", "submittedchanges", "timelapse", + "timelapseview", "tlv", "users", "workspaces", "clients", "shutdown" +} + +clink.arg.register_parser("p4vc", p4vc_tree) + +-- vim: expandtab + +-------------------------------------------------------------------------------- +-- powershell.lua +-- + +-- +-- Copyright (c) 2013 Martin Ridgers +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +-------------------------------------------------------------------------------- +local function powershell_prompt_filter() + local l, r, path = clink.prompt.value:find("([a-zA-Z]:\\.*)> $") + if path ~= nil then + clink.chdir(path) + end +end + +-------------------------------------------------------------------------------- +if clink.get_host_process() == "powershell.exe" then + clink.prompt.register_filter(powershell_prompt_filter, -493) +end + +-------------------------------------------------------------------------------- +-- self.lua +-- + +-- +-- Copyright (c) 2012 Martin Ridgers +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +-------------------------------------------------------------------------------- +local null_parser = clink.arg.new_parser() +null_parser:disable_file_matching() + +local inject_parser = clink.arg.new_parser() +inject_parser:set_flags( + "--help", + "--nohostcheck", + "--pid", + "--profile", + "--quiet", + "--scripts" +) + +local autorun_dashdash_parser = clink.arg.new_parser() +autorun_dashdash_parser:set_arguments({ "--" .. inject_parser }) + +local autorun_parser = clink.arg.new_parser() +autorun_parser:set_flags("--allusers", "--help") +autorun_parser:set_arguments( + { + "install" .. autorun_dashdash_parser, + "uninstall" .. null_parser, + "show" .. null_parser, + "set" + } +) + +local set_parser = clink.arg.new_parser() +set_parser:disable_file_matching() +set_parser:set_flags("--help") +set_parser:set_arguments( + { + "ansi_code_support", + "ctrld_exits", + "esc_clears_line", + "exec_match_style", + "history_dupe_mode", + "history_expand_mode", + "history_file_lines", + "history_ignore_space", + "history_io", + "match_colour", + "prompt_colour", + "space_prefix_match_files", + "strip_crlf_on_paste", + "terminate_autoanswer", + "use_altgr_substitute", + } +) + +local self_parser = clink.arg.new_parser() +self_parser:set_arguments( + { + "inject" .. inject_parser, + "autorun" .. autorun_parser, + "set" .. set_parser, + } +) + +clink.arg.register_parser("clink", self_parser) +clink.arg.register_parser("clink_x86", self_parser) +clink.arg.register_parser("clink_x64", self_parser) + +-- vim: expandtab + +-------------------------------------------------------------------------------- +-- set.lua +-- + +-- +-- Copyright (c) 2012 Martin Ridgers +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +-------------------------------------------------------------------------------- +local function set_match_generator(word) + -- Skip this generator if first is in the rvalue. + local leading = rl_state.line_buffer:sub(1, rl_state.first - 1) + if leading:find("=") then + return false + end + + -- Enumerate environment variables and check for potential matches. + local matches = {} + for _, name in ipairs(clink.get_env_var_names()) do + if clink.is_match(word, name) then + table.insert(matches, name:lower()) + end + end + + clink.suppress_char_append() + return matches +end + +-------------------------------------------------------------------------------- +clink.arg.register_parser("set", set_match_generator) + +-- vim: expandtab + +-------------------------------------------------------------------------------- +-- svn.lua +-- + +-- +-- Copyright (c) 2012 Martin Ridgers +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +-------------------------------------------------------------------------------- +local svn_tree = { + "add", "blame", "praise", "annotate", "ann", "cat", "changelist", "cl", + "checkout", "co", "cleanup", "commit", "ci", "copy", "cp", "delete", "del", + "remove", "rm", "diff", "di", "export", "help", "h", "import", "info", + "list", "ls", "lock", "log", "merge", "mergeinfo", "mkdir", "move", "mv", + "rename", "ren", "propdel", "pdel", "pd", "propedit", "pedit", "pe", + "propget", "pget", "pg", "proplist", "plist", "pl", "propset", "pset", "ps", + "resolve", "resolved", "revert", "status", "stat", "st", "switch", "sw", + "unlock", "update", "up" +} + +clink.arg.register_parser("svn", svn_tree) + +-- vim: expandtab diff --git a/clink/clink_dll_x64.dll b/clink/clink_dll_x64.dll new file mode 100644 index 0000000000000000000000000000000000000000..34c8dd534d0d29b44e325d0da0c049b912281a52 GIT binary patch literal 552960 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

gjbto*e)@F*|^|(dWp-tnBcW20I_)*0F8TlsIer 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