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 well-known Unix shell Bash, the standard shell for Mac OS X and many Linux distributions.

Features

By default Clink binds Alt+H to display the current key bindings. More features can also be found in GNU's Readline and History libraries' manuals.

Want some quick but powerful tips to get started?

Ctrl+OThis is operate-and-get-next, which accepts the current input line and gets the next history line. You can search history for a command, then press Ctrl+O to run that command and queue up the next command after it. Repeat it to conveniently rerun a series of commands from the history.
Alt+.This is yank-last-arg, which inserts the last argument from the previous line. You can use it repeatedly to cycle backwards through the history, inserting the last argument from each line. Learn more by reading up on the "yank" features in the Readline manual.
Ctrl+RThis is reverse-search-history, which incrementally searches the history. Press it, then type, and it does a reverse incremental search while you type. Press Ctrl+R again (and again, etc) to search for other matches of the search text. Learn more by reading up on the "search" and "history" features in the Readline manual.
Ctrl+Alt+DThis is remove-history, which deletes the currently selected history line after using any of the history search or navigation commands.
Ctrl+Alt+KThis is add-history, which adds the current line to the history without executing it, and then clears the input line.
Ctrl+Alt+NThis is clink-menu-complete-numbers, which grabs numbers with 3 or more digits from the current console screen and cycles through inserting them as completions (binary, octal, decimal, hexadecimal). Super handy for quickly inserting a commit hash that was printed as output from a preceding command.
Alt+0 to Alt+9These are digit-argument, which let you enter a numeric value used by many commands. For example Ctrl+Alt+W copies the current word to the clipboard, but if you first type Alt+2 followed by Ctrl+Alt+W then it copies the 3rd word to the clipboard (the first word is 0, the second is 1, etc). Learn more by reading up on "Readline Arguments" in the Readline manual.
Alt+HThis is clink-show-help, which lists the key bindings and commands. Learn more by visiting Key Bindings.

Usage

There are several ways to start Clink.

  1. If you installed the auto-run, just start cmd.exe. Run clink autorun --help for more info.
  2. To manually start, run the Clink shortcut from the Start menu (or the clink.bat located in the install directory).
  3. To establish Clink to an existing cmd.exe process, use <install_dir>\clink.exe inject.

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 its own Readline-powered command line editing.

Privacy

Clink does not collect user data. Clink writes diagnostic information to its local log file, and does not transmit the log file off the local computer. For the location of the log file, refer to File Locations or run clink info.

Configuring Clink

Startup Cmd Script

When Clink is injected, it looks for a clink_start.cmd script in the binaries directory and profile directory. Clink automatically runs the scripts, if present, when the first CMD prompt is shown after Clink is injected.

You can set the clink.autostart setting to run a different command, or set it to "nul" to run no command at all.

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.

The following table describes the available Clink settings:

Name Default Description
clink.autostart This command is automatically run when the first CMD prompt is shown after Clink is injected. If this is blank (the default), then Clink instead looks for clink_start.cmd in the binaries directory and profile directory and runs them. Set it to "nul" to not run any autostart command.
clink.colorize_input True Enables context sensitive coloring for the input text (see Coloring The Input Text).
clink.paste_crlf crlf What to do with CR and LF characters on paste. Setting this to delete deletes them, space replaces them with spaces, ampersand replaces them with ampersands, and crlf pastes them as-is (executing commands that end with a newline).
clink.path A list of paths to load Lua scripts. Multiple paths can be delimited semicolons.
clink.promptfilter True Enable prompt filtering by Lua scripts.
cmd.auto_answer off Automatically answers cmd.exe's "Terminate batch job (Y/N)?" prompts. off = disabled, answer_yes = answer Y, answer_no = answer N.
cmd.ctrld_exits True Ctrl+D exits the process when it is pressed on an empty line.
cmd.get_errorlevel False When this is enabled, Clink runs a hidden echo %errorlevel% command before each interactive input prompt to retrieve the last exit code for use by Lua scripts. If you experience problems, try turning this off. This is off by default.
color.arg The color for arguments in the input line when clink.colorize_input is enabled.
color.argmatcher The color for the command name in the input line when clink.colorize_input is enabled, if the command name has an argmatcher available.
color.cmd bold Used when Clink displays shell (CMD.EXE) command completions, and in the input line when clink.colorize_input is enabled.
color.doskey bright cyan Used when Clink displays doskey alias completions, and in the input line when clink.colorize_input is enabled.
color.filtered bold The default color for filtered completions (see Filtering the Match Display).
color.flag default The color for flags in the input line when clink.colorize_input is enabled.
color.hidden Used when Clink displays file completions with the "hidden" attribute.
color.horizscroll Used when Clink displays the < or > horizontal scroll indicators when Readline's horizontal-scroll-mode variable is set.
color.input Used when Clink displays the input line text. Note that when clink.colorize_input is disabled, the entire input line is displayed using color.input.
color.interact bold Used when Clink displays text or prompts such as a pager's --More?-- prompt.
color.message default The color for the message area (e.g. the search prompt message, digit argument prompt message, etc).
color.modmark Used when Clink displays the * mark on modified history lines when Readline's mark-modified-lines variable and Clink's color.input setting are both set. Falls back to color.input if not set.
color.prompt When set, this is used as the default color for the prompt. But it's overridden by any colors set by Customising The Prompt.
color.readonly Used when Clink displays file completions with the "readonly" attribute.
color.selected_completion The color for the selected completion with the clink-select-complete command. If no color is set, then bright reverse video is used.
color.selection The color for selected text in the input line. If no color is set, then reverse video is used.
color.unexpected default The color for unexpected arguments in the input line when clink.colorize_input is enabled.
debug.log_terminal False Logs all terminal input and output to the clink.log file. This is intended for diagnostic purposes only, and can make the log file grow significantly.
doskey.enhanced True Enhanced Doskey adds the expansion of macros that follow | and & command separators and respects quotes around words when parsing $1..$9 tags. Note that these features do not apply to Doskey use in Batch files.
exec.cwd True When matching executables as the first word (exec.enable), include executables in the current directory. (This is implicit if the word being completed is a relative path).
exec.dirs True When matching executables as the first word (exec.enable), also include directories relative to the current working directory as matches.
exec.enable True Match executables when completing the first word of a line.
exec.path True When matching executables as the first word (exec.enable), include executables found in the directories specified in the %PATH% environment variable.
exec.space_prefix True If the line begins with whitespace then Clink bypasses executable matching (exec.path) and will do normal files matching instead.
files.hidden True Includes or excludes files with the "hidden" attribute set when generating file lists.
files.system False Includes or excludes files with the "system" attribute set when generating file lists.
history.dont_add_to_history_cmds exit history List of commands that aren't automatically added to the history. Commands are separated by spaces, commas, or semicolons. Default is exit history, to exclude both of those commands.
history.dupe_mode erase_prev If a line is a duplicate of an existing history entry Clink will erase the duplicate when this is set to 'erase_prev'. Setting it to 'ignore' will not add duplicates to the history, and setting it to 'add' will always add lines (except when overridden by history.sticky_search).
history.expand_mode not_quoted 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 on or off. Values of not_squoted, not_dquoted, or not_quoted will skip any ! character quoted in single, double, or both quotes respectively.
history.ignore_space True Ignore lines that begin with whitespace when adding lines in to the history.
history.max_lines 2500 The number of history lines to save if history.save is enabled (1 to 50000).
history.save True Saves history between sessions. When disabled, history is neither read from nor written to a master history list; history for each session exists only in memory until the session ends.
history.shared False When history is shared, all instances of Clink update the master history list after each command and reload the master history list on each prompt. When history is not shared, each instance updates the master history list on exit.
history.sticky_search False When enabled, reusing a history line does not add the reused line to the end of the history, and it leaves the history search position on the reused line so next/prev history can continue from there (e.g. replaying commands via Up several times then Enter, Down, Enter, etc).
lua.break_on_error False Breaks into Lua debugger on Lua errors.
lua.break_on_traceback False Breaks into Lua debugger on traceback().
lua.debug False Loads a simple embedded command line debugger when enabled. Breakpoints can be added by calling pause().
lua.path Value to append to package.path. Used to search for Lua scripts specified in require() statements.
lua.reload_scripts False When false, Lua scripts are loaded once and are only reloaded if forced (see The Location of Lua Scripts for details). When true, Lua scripts are loaded each time the edit prompt is activated.
lua.strict True When enabled, argument errors cause Lua scripts to fail. This may expose bugs in some older scripts, causing them to fail where they used to succeed. In that case you can try turning this off, but please alert the script owner about the issue so they can fix the script.
lua.traceback_on_error False Prints stack trace on Lua errors.
match.ignore_accent True Controls accent sensitivity when completing matches. For example, ä and a are considered equivalent with this enabled.
match.ignore_case relaxed Controls case sensitivity when completing matches. off = case sensitive, on = case insensitive, relaxed = case insensitive plus - and _ are considered equal.
match.sort_dirs with How to sort matching directory names. before = before files, with = with files, after = after files.
match.translate_slashes system File and directory completions can be translated to use consistent slashes. The default is system to use the appropriate path separator for the OS host (backslashes on Windows). Use slash to use forward slashes, or backslash to use backslashes. Use off to turn off translating slashes from custom match generators.
match.wild True Matches ? and * wildcards when using any of the menu-complete commands or the clink-popup-complete command. Turn this off to behave how bash does, and not match wildcards.
prompt.async True Enables asynchronous prompt refresh. Turn this off if prompt filter refreshes are annoying or cause problems.
readline.hide_stderr False Suppresses stderr from the Readline library. Enable this if Readline error messages are getting in the way.
terminal.adjust_cursor_style True When enabled, Clink adjusts the cursor shape and visibility to show Insert Mode, produce the visible bell effect, avoid disorienting cursor flicker, and to support ANSI escape codes that adjust the cursor shape and visibility. But it interferes with the Windows 10 Cursor Shape console setting. You can make the Cursor Shape setting work by disabling this Clink setting (and the features this provides).
terminal.differentiate_keys False When enabled, pressing Ctrl + H or I or M or [ generate special key sequences to enable binding them separately from Backspace or Tab or Enter or Esc.
terminal.emulation auto Clink can either emulate a virtual terminal and handle ANSI escape codes itself, or let the console host natively handle ANSI escape codes. native = pass output directly to the console host process, emulate = clink handles ANSI escape codes itself, auto = emulate except when running in ConEmu, Windows Terminal, or Windows 10 new console.
terminal.raw_esc False When enabled, pressing Esc sends a literal escape character like in Unix/etc terminals. This setting is disabled by default to provide a more predictable, reliable, and configurable input experience on Windows. Changing this only affects future Clink sessions, not the current session.
terminal.use_altgr_substitute False Support Windows' Ctrl-Alt substitute for AltGr. Turning this off may resolve collisions with Readline's key bindings.

Compatibility Notes:

  • The esc_clears_line setting has been replaced by a clink-reset-line command that is by default bound to the Escape key. See Key Bindings and Readline for more information.
  • The match_colour setting has been removed, and Clink now supports Readline's completion coloring. See Completion Colors for more information.

Color Settings

Friendly Color Names

The Clink color settings use the following syntax:

[attributes] [foreground_color] [on [background_color]]

Optional attributes (can be abbreviated to 3 letters):

Optional colors for foreground_color and background_color (can be abbreviated to 3 letters):

Examples (specific results may depend on the console host program):

Alternative SGR Syntax

It's also possible to set any ANSI SGR escape code using sgr SGR_parameters (for example sgr 7 is the code for reverse video, which swaps the foreground and background colors).

Be careful, since some escape code sequences might behave strangely.

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;

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. Or with the %CLINK_PROFILE% environment variable if it is already present when Clink is injected (this envvar takes precedence over any other mechanism of specifying a profile directory, if more than one was used).

You can use clink info to find the directories and configuration files for the current Clink session.

Command Line Options

clink
Shows command line usage help.

clink inject
Injects Clink into a CMD.EXE process.
See clink inject --help for more information.

clink autorun
Manages Clink's entry in CMD.EXE's autorun section, which can automatically inject Clink when starting CMD.EXE.
When Clink is installed for autorun, the automatic inject can be overridden by setting the CLINK_NOAUTORUN environment variable (to any value).
See clink autorun --help for more information.

clink set
clink set by itself lists all settings and their values.
clink set setting_name describes the setting shows its current value.
clink set setting_name clear resets the setting to its default value.
clink set setting_name value sets the setting to the specified value.

clink installscripts
Adds a path to search for Lua scripts.
The path is stored in the registry and applies to all installations of Clink, regardless where their config paths are, etc. This is intended to make it easy for package managers like Scoop to be able to install (and uninstall) scripts for use with Clink.
See clink installscripts --help for more information.

clink uninstallscripts
Removes a path added by `clink installscripts`.
See clink uninstallscripts --help for more information.

clink history
Lists the command history.
See clink history --help for more information.
Also, Clink automatically defines history as an alias for clink history.

clink info
Prints information about Clink, including the version and various configuration directories and files.
Or clink --version shows just the version number.

clink echo
Echos key sequences to use in the .inputrc files for binding keys to Clink commands. Each key pressed prints the associated key sequence on a separate line, until Ctrl+C is pressed.

Portable Configuration

Sometimes it's useful to run Clink from a flash drive or from a network share, especially if you want to use Clink on someone else's computer.

Here's how you can set up a portable configuration for Clink:

  1. Put your Lua scripts and other tools in the same directory as the Clink executable files. For example fzf.exe, z.cmd, oh-my-posh.exe, or etc can all go in the same directory on a flash drive or network share.
  2. Make a batch file such as portable.bat that injects Clink using a specific profile directory.
    • On a flash drive, you can have a portable profile in a subdirectory under the Clink directory.
    • On a network share, you'll want to copy some initial settings into a local profile directory (a profile directory on a network share will be slow).
  3. In any cmd.exe window on any computer, you can then run the portable.bat script to inject Clink and have all your favorite settings and key bindings work.

Here are some sample scripts:

portable.bat (on a flash drive)

This sample script assumes the portable.bat script is in the Clink directory, and it uses a clink_portable profile directory under the Clink directory.

@echo off
rem -- Do any other desired configuration here, such as loading a doskey macro file.
call "%~dp0clink.bat" inject --profile "%~dp0clink_portable" %1 %2 %3 %4 %5 %6 %7 %8 %9

portable.bat (on a network share)

This sample script assumes the portable.bat script is in the Clink directory, and that there is a file portable_clink_settings with the settings you want to copy to the local profile directory.

@echo off
if not exist "%TEMP%\clink_portable" md "%TEMP%\clink_portable" >nul
if not exist "%TEMP%\clink_portable\clink_settings" copy "%~dp0portable_clink_settings" "%TEMP%\clink_portable\clink_settings" >nul
rem -- Do any other desired configuration here, such as loading a doskey macro file.
call "%~dp0clink.bat" inject --profile "%TEMP%\clink_portable" %1 %2 %3 %4 %5 %6 %7 %8 %9

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 and commands available to configure Readline in Readline's manual.

Clink searches in the directories referenced by the following environment variables and loads any .inputrc or _inputrc files present, in the order listed here:

Configuration in files loaded earlier can be overridden by files loaded later.

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 clink [...] $endif.

You can use clink info to find the directories and configuration files for the current Clink session.

Compatibility Notes:

  • The clink_inputrc_base file from v0.4.8 is no longer used.
  • For backward compatibility, clink_inputrc is also loaded from the above locations, but it has been deprecated and may be removed in the future.

Basic Format

The quick version is that mostly you'll use these kinds of lines:

If a Readline macro begins with "luafunc:" then Clink interprets it as a Lua key binding and will invoke the named Lua function. Function names can include periods (such as foo.bar) but cannot include any other punctuation. See Lua Key Bindings for more information.

Refer to the Readline manual for a more thorough explanation of the .inputrc file format, list of available commands, and list of configuration variables and their values.

See Key Bindings for more information about binding keys in Clink.

New Configuration Variables

Clink adds some new configuration variables to Readline, beyond what's described in the Readline manual:

Name Default Description
completion-auto-query-items on Automatically prompts before displaying completions if they need more than half a screen page.
history-point-at-end-of-anchored-search off Puts the cursor at the end of the line when using history-search-forward or history-search-backward.
menu-complete-wraparound on The menu-complete family of commands wraps around when reaching the end of the possible completions.
search-ignore-case on Controls whether the history search commands ignore case.

New Commands

Clink adds some new commands to Readline, beyond what's described in the Readline manual:

Name Description
add-history Adds the current line to the history without executing it, and clears the editing line.
clink-complete-numbers Like complete, but for numbers from the console screen (3 digits or more, up to hexadecimal).
clink-copy-cwd Copy the current working directory to the clipboard.
clink-copy-line Copy the current line to the clipboard.
clink-copy-word Copy the word at the cursor to the clipboard, or copies the nth word if a numeric argument is provided via the digit-argument keys.
clink-ctrl-c Discards the current line and starts a new one (like Ctrl+C in CMD.EXE).
clink-diagnostics Show internal diagnostic information.
clink-exit Replaces the current line with exit and executes it (exits the shell instance).
clink-expand-doskey-alias Expand the doskey alias (if any) at the beginning of the line.
clink-expand-env-vars Expand the environment variable (e.g. %FOOBAR%) at the cursor.
clink-find-conhost Activates the "Find" dialog when running in a standard console window (hosted by the OS conhost). This is equivalent to picking "Find..." from the console window's system menu.
clink-insert-dot-dot Inserts ..\ at the cursor.
clink-mark-conhost Activates the "Mark" mode when running in a standard console window (hosted by the OS conhost). This is equivalent to picking "Mark" from the console window's system menu.
clink-menu-complete-numbers Like menu-complete, but for numbers from the console screen (3 digits or more, up to hexadecimal).
clink-menu-complete-numbers-backward Like menu-complete-backward, but for numbers from the console screen (3 digits or more, up to hexadecimal).
clink-old-menu-complete-numbers Like old-menu-complete, but for numbers from the console screen (3 digits or more, up to hexadecimal).
clink-old-menu-complete-numbers-backward Like old-menu-complete-backward, but for numbers from the console screen (3 digits or more, up to hexadecimal).
clink-paste Paste the clipboard at the cursor.
clink-popup-complete Show a popup window that lists the available completions.
clink-popup-complete-numbers Like clink-popup-complete, but for numbers from the console screen (3 digits or more, up to hexadecimal).
clink-popup-directories Show a popup window of recent current working directories. In the popup, use Enter to cd /d to the highlighted directory.
clink-popup-history Show a popup window that lists the command history (if any text precedes the cursor then it uses an anchored search to filter the list). In the popup, use Enter to execute the highlighted command.
clink-popup-show-help Show a popup window that lists the currently active key bindings, and can invoke a selected key binding. The default key binding for this is Ctrl+Alt+H.
clink-reload Reloads the .inputrc file and the Lua scripts.
clink-reset-line Clears the current line.
clink-scroll-bottom Scroll the console window to the bottom (the current input line).
clink-scroll-line-down Scroll the console window down one line.
clink-scroll-line-up Scroll the console window up one line.
clink-scroll-page-down Scroll the console window down one page.
clink-scroll-page-up Scroll the console window up one page.
clink-scroll-top Scroll the console window to the top.
clink-select-complete Like complete, but shows interactive menu of matches and responds to arrow keys and typing to filter the matches.
clink-show-help Lists the currently active key bindings using friendly key names. A numeric argument affects showing categories and descriptions: 0 for neither, 1 for categories, 2 for descriptions, 3 for categories and descriptions (the default).
clink-show-help-raw Lists the currently active key bindings using raw key sequences. A numeric argument affects showing categories and descriptions: 0 for neither, 1 for categories, 2 for descriptions, 3 for categories and descriptions (the default).
clink-up-directory Changes to the parent directory.
clink-what-is Show the key binding for the next key sequence that is input.
cua-backward-char Extends the selection and moves back a character.
cua-backward-word Extends the selection and moves back a word.
cua-beg-of-line Extends the selection and moves to the start of the current line.
cua-copy Copies the selection to the clipboard.
cua-cut Cuts the selection to the clipboard.
cua-end-of-line Extends the selection and moves to the end of the line.
cua-forward-char Extends the selection and moves forward a character.
cua-forward-word Extends the selection and moves forward a word.
old-menu-complete-backward Like old-menu-complete, but in reverse.
remove-history While searching history, removes the current line from the history.

Completion Colors

The %LS_COLORS% environment variable provides color definitions as a series of color definitions separated by colons (:). Each definition is a either a two character type id or a file extension, followed by an equals sign and then the SGR parameters for an ANSI escape code. The two character type ids are listed below.

When the colored-completion-prefix Readline setting is configured to on, then the "so" color from %LS_COLORS% is used to color the common prefix when displaying possible completions. The default for "so" is magenta, but for example set LS_COLORS=so=90 sets the color to bright black (which shows up as a dark gray).

When colored-stats is configured to on, then the color definitions from %LS_COLORS% are used to color file completions according to their file type or extension. Multiple definitions are separated by colons. Also, since %LS_COLORS% doesn't cover readonly files, hidden files, doskey aliases, or shell commands the color.readonly, color.hidden, color.doskey, and color.cmd Clink settings exist to cover those.

Type Description Default
di Directories. 01;34 (bright blue)
ex Executable files. 01;32 (bright green)
fi Normal files.
ln Symlinks. When ln=target then symlinks are colored according to the target of the symlink. target
mi Missing file or directory.
no Normal color. This is used for anything not covered by one of the other types.
It may be overridden by various other Clink color settings as appropriate depending on the completion type.
or Orphaned symlink (the target of the symlink is missing).
so Common prefix for possible completions. 01;35 (bright magenta)

Here is an example where %LS_COLORS% defines colors for various types.

set LS_COLORS=so=90:fi=97:di=93:ex=92:*.pdf=30;105:*.md=4

The clink-popup-complete, clink-popup-directories, and clink-popup-history Readline commands show a searchable popup window that lists the available completions, directory history, or command history. Here's how the popup windows work:

Key Description
Escape Cancels the popup.
Enter Inserts the highlighted completion, changes to the highlighted directory, or executes the highlighted command.
Shift+Enter Inserts the highlighted completion, inserts the highlighted directory, or jumps to the highlighted command history entry without executing it.
Ctrl+Enter Same as Shift+Enter.
Typing Typing does an incremental search.
F3 Go to the next match.
Ctrl+L Go to the next match.
Shift+F3 Go to the previous match.
Ctrl+Shift+L Go to the previous match.

Extending Clink With Lua

Clink can be extended with Lua scripts to customize startup actions, create completion matches, customize the prompt, and more. The following sections describe these in more detail and show some examples.

The Location of Lua Scripts

Clink loads all Lua scripts it finds in these directories:

  1. All directories listed in the clink.path setting, separated by semicolons.
  2. If clink.path is not set, then the DLL directory and the profile directory are used (see File Locations for info about the profile directory).
  3. All directories listed in the %CLINK_PATH% environment variable, separated by semicolons.
  4. All directories registered by the clink installscripts command.

Lua scripts are loaded once and are only reloaded if forced because the scripts locations change, the clink-reload command is invoked (Ctrl+X,Ctrl+R), or the lua.reload_scripts setting changes (or is True).

Run clink info to see the script paths for the current session.

Tips for starting to write Lua scripts

The following sections describe these in more detail and show some examples.

Match Generators

These are Lua functions that are called as part of Readline's completion process (for example when pressing Tab).

First create a match generator object:

local my_generator = clink.generator(priority)

The priority argument is a number that influences when the generator gets called, with lower numbers going before higher numbers.

The :generate() Function

Next define a match generator function on the object, taking the following form:

function my_generator:generate(line_state, match_builder)
    -- Use the line_state object to examine the current line and create matches.
    -- Submit matches to Clink using the match_builder object.
    -- Return true or false.
end

line_state is a line object that has information about the current line.

match_builder is a builder object to which matches can be added.

If no further match generators need to be called then the function should return true. Returning false or nil continues letting other match generators get called.

Here is an example script that supplies git branch names as matches for git checkout. This example doesn't handle git aliases, but that could be added with additional script code.

local git_branch_autocomplete = clink.generator(1)

local function starts_with(str, start)
    return string.sub(str, 1, string.len(start)) == start
end

local function is_checkout_ac(text)
    if starts_with(text, "git checkout") then
        return true
    end
    return false
end

local function get_branches()
    -- Run git command to get branches.
    local handle = io.popen("git branch -a 2>&1")
    local result = handle:read("*a")
    handle:close()
    -- Parse the branches from the output.
    local branches = {}
    if starts_with(result, "fatal") == false then
        for branch in string.gmatch(result, "  %S+") do
            branch = string.gsub(branch, "  ", "")
            if branch ~= "HEAD" then
                table.insert(branches, branch)
            end
        end
    end
    return branches
end

function git_branch_autocomplete:generate(line_state, match_builder)
    -- Check if it's a checkout command.
    if not is_checkout_ac(line_state:getline()) then
        return false
    end
    -- Get branches and add them (does nothing if not in a git repo).
    local matchCount = 0
    for _, branch in ipairs(get_branches()) do
        match_builder:addmatch(branch)
        matchCount = matchCount + 1
    end
    -- If we found branches, then stop other match generators.
    return matchCount > 0
end

The :getwordbreakinfo() Function

If needed, a generator can optionally influence word breaking for the end word by defining a :getwordbreakinfo() function.

The function takes a line_state line object that has information about the current line. If it returns nil or 0, the end word is truncated to 0 length. This is the normal behavior, which allows Clink to collect and cache all matches and then filter them based on typing. Or it can return two numbers: word break length and an optional end word length. The end word is split at the word break length: one word contains the first word break length characters from the end word (if 0 length then it's discarded), and the next word contains the rest of the end word truncated to the optional word length (0 if omitted).

A good example to look at is Clink's own built-in environment variable match generator. It has a :getwordbreakinfo() function that understands the % syntax of environment variables and produces word break info accordingly.

When the environment variable match generator's :getwordbreakinfo() function sees the end word is abc%USER it returns 3,1 so that the last two words become "abc" and "%" so that its generator knows it can do environment variable matching. But when it sees abc%FOO%def it returns 8,0 so that the last two words become "abc%FOO%" and "" so that its generator won't do environment variable matching, and also so other generators can produce matches for what follows, since "%FOO%" is an already-completed environment variable and therefore should behave like a word break. In other words, it breaks the end word differently depending on whether the number of percent signs is odd or even, to account for environent variable syntax rules.

And when an argmatcher sees the end word begins with a flag character it returns 0,1 so the end word contains only the flag character in order to switch from argument matching to flag matching.

Note: The :getwordbreakinfo() function is called very often, so it needs to be very fast or it can cause responsiveness problems while typing.

local envvar_generator = clink.generator(10)

function envvar_generator:generate(line_state, match_builder)
    -- Does the word end with a percent sign?
    local word = line_state:getendword()
    if word:sub(-1) ~= "%" then
        return false
    end

    -- Add env vars as matches.
    for _, i in ipairs(os.getenvnames()) do
        match_builder:addmatch("%"..i.."%", "word")
    end

    match_builder:setsuppressappend()   -- Don't append a space character.
    match_builder:setsuppressquoting()  -- Don't quote envvars.
    return true
end

function envvar_generator:getwordbreakinfo(line_state)
    local word = line_state:getendword()
    local in_out = false
    local index = nil

    -- Paired percent signs denote already-completed environment variables.
    -- So use envvar completion for abc%foo%def%USER but not for abc%foo%USER.
    for i = 1, #word do
        if word:sub(i, i) == "%" then
            in_out = not in_out
            if in_out then
                index = i - 1
            else
                index = i
            end
        end
    end

    -- If there were any percent signs, return word break info to influence the
    -- match generators.
    if index then
        return index, (in_out and 1) or 0
    end
end

More Advanced Stuff

Filtering Match Completions

A match generator or luafunc: key binding can use clink.onfiltermatches() to register a function that will be called after matches are generated but before they are displayed or inserted.

The function receives a table argument containing the matches to be displayed, a string argument indicating the completion type, and a boolean argument indicating whether filename completion is desired. The table argument has a match string field and a type string field; these are the same as in builder:addmatch().

The possible completion types are:

Type Description Example
"?" List the possible completions. possible-completions or popup-complete
"*" Insert all of the possible completions. insert-completions
"\t" Do standard completion. complete
"!" Do standard completion, and list all possible completions if there is more than one. complete (when the show-all-if-ambiguous config variable is set)
"@" Do standard completion, and list all possible completions if there is more than one and partial completion is not possible. complete (when the show-all-if-unmodified config variable is set)
"%" Do menu completion (cycle through possible completions). menu-complete or old-menu-complete

The return value is a table with the input matches filtered as desired. The match filter function can remove matches, but cannot add matches (use a match generator instead). If only one match remains after filtering, then many commands will insert the match without displaying it. This makes it possible to spawn a process (such as fzf) to perform enhanced completion by interactively filtering the matches and keeping only one selected match.

settings.add("fzf.height", "40%", "Height to use for the --height flag")
settings.add("fzf.exe_location", "", "Location of fzf.exe if not on the PATH")

-- Build a command line to launch fzf.
local function get_fzf()
    local height = settings.get("fzf.height")
    local command = settings.get("fzf.exe_location")
    if not command or command == "" then
        command = "fzf.exe"
    else
        command = os.getshortname(command)
    end
    if height and height ~= "" then
        command = command..' --height '..height
    end
    return command
end

local fzf_complete_intercept = false

-- Sample key binding in .inputrc:
--      M-C-x: "luafunc:fzf_complete"
function fzf_complete(rl_buffer)
    fzf_complete_intercept = true
    rl.invokecommand("complete")
    if fzf_complete_intercept then
        rl_buffer:ding()
    end
    fzf_complete_intercept = false
    rl_buffer:refreshline()
end

local function filter_matches(matches, completion_type, filename_completion_desired)
    if not fzf_complete_intercept then
        return
    end
    -- Start fzf.
    local r,w = io.popenrw(get_fzf()..' --layout=reverse-list')
    if not r or not w then
        return
    end
    -- Write matches to the write pipe.
    for _,m in ipairs(matches) do
        w:write(m.match.."\n")
    end
    w:close()
    -- Read filtered matches.
    local ret = {}
    while (true) do
        local line = r:read('*line')
        if not line then
            break
        end
        for _,m in ipairs(matches) do
            if m.match == line then
                table.insert(ret, m)
            end
        end
    end
    r:close()
    -- Yay, successful; clear it to not ding.
    fzf_complete_intercept = false
    return ret
end

local interceptor = clink.generator(0)
function interceptor:generate(line_state, match_builder)
    -- Only intercept when the specific command was used.
    if fzf_complete_intercept then
        clink.onfiltermatches(filter_matches)
    end
    return false
end

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. For example, it might be desirable to display a * next to some matches, or to show additional information about each match.

A match generator can use clink.ondisplaymatches() to register a function that will be called before matches are displayed (this is reset every time match generation is invoked).

The function receives a table argument containing the matches to be displayed, and a boolean argument indicating whether they'll be displayed in a popup window. The table argument has a match string field and a type string field; these are the same as in builder:addmatch(). The return value is a table with the input matches filtered as required by the match generator.

The returned table can also optionally include a display string field and a description string field. When present, display will be displayed instead of the match field, and description will be displayed next to the match. Putting the description in a separate field enables Clink to align the descriptions in a column.

Filtering the match display can affect completing matches: the match field is what gets inserted. It can also affect displaying matches: the display field is displayed if present, otherwise the match field is displayed.

If a match's type is "none" or its match field is different from its display field then the match is displayed using the color specified by the color.filtered Clink setting, otherwise normal completion coloring is applied. The display and description fields can include ANSI escape codes to apply other colors if desired.

local function my_filter(matches, popup)
    local new_matches = {}
    local magenta = "\x1b[35m"
    local filtered = settings.get("color.filtered")
    for _,m in ipairs(matches) do
        if m.match:find("[0-9]") then
            -- Ignore matches with one or more digits.
        else
            -- Keep the match, and also add a magenta * prefix to directory matches.
            if m.type:find("^dir") then
                m.display = magenta.."*"..filtered..m.match
            end
            table.insert(new_matches, m)
        end
    end
    return new_matches
end

function my_match_generator:generate(line_state, match_builder)
    ...
    clink.ondisplaymatches(my_filter)
end

Compatibility Note: When a match display filter has been set, it changes how match generation behaves.

  • When a match display filter is set, then match generation is also re-run whenever matches are displayed.
  • Normally match generation only happens at the start of a new word. The full set of potential matches is remembered and dynamically filtered based on further typing.
  • So if a match generator made contextual decisions during match generation (other than filtering) then it could potentially behave differently in Clink v1.x than it did in v0.x.

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;

clink.argmatcher("foobar")
:addflags("-foo", "-bar")
:addarg(
    { "hello", "hi" },      -- Completions for arg #1
    { "world", "wombles" }  -- Completions for arg #2
)

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
wombles  wonder   world
C:>foobar hello -foo wo_

When displaying possible completions, flag matches are only shown if the flag character has been input (so command and Alt+= would list only non-flag matches, or command - and Alt+= would list only flag matches).

If a command doesn't have an argmatcher but is a doskey macro, Clink automatically expands the doskey macro and looks for an argmatcher for the expanded command. A macro like gco=git checkout $* automatically reuses a git argmatcher and produces completions for its checkout argument. However, it only expands the doskey macro up to the first $, so complex aliases like foo=app 2$gnul text $* or foo=$2 $1 might behave strangely.

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.

a_parser = clink.argmatcher():addarg({ "foo", "bar" })
b_parser = clink.argmatcher():addarg({ "abc", "123" })
c_parser = clink.argmatcher()
c_parser:addarg({ "foobar" .. a_parser })   -- Arg #1 is "foobar", which has args "foo" or "bar".
c_parser:addarg({ b_parser })               -- Arg #2 is "abc" or "123".

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 will only return to the previous parser when the linked parser runs out of arguments.

Flags With Arguments

Parsers can be concatenated with flags, too.

Here's an example of a flag that takes an argument:

clink.argmatcher("git")
:addarg({
    "merge"..clink.argmatcher():addflags({
        "--strategy"..clink.argmatcher():addarg({
            "resolve",
            "recursive",
            "ours",
            "octopus",
            "subtree",
        })
    })
})

A : or = at the end of a flag indicates the flag takes an argument but requires no space between the flag and its argument. If such a flag is not linked to a parser, then it automatically gets linked to a parser to match files. Here's an example with a few flags that take arguments without a space in between:

clink.argmatcher("findstr")
:addflags({
    "/b", "/e", "/l", "/r", "/s", "/i", "/x", "/v", "/n", "/m", "/o", "/p", "/offline",
    "/a:"..clink.argmatcher():addarg( "attr" ),
    "/f:"..clink.argmatcher():addarg( clink.filematches ),
    "/c:"..clink.argmatcher():addarg( "search_string" ),
    "/g:", -- This is the same as linking with clink.argmatcher():addarg(clink.filematches).
    "/d:"..clink.argmatcher():addarg( clink.dirmatches )
})

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.argmatcher()
the_parser:addarg({ "zippy", "bungle", "george" })
the_parser:addarg({ 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.

Some built-in matcher functions are available:

Function Description
clink.dirmatches Generates directory matches.
clink.filematches Generates file matches.

Shorthand

It is also possible to omit the addarg and addflags function calls and use a more declarative shorthand form:

-- Shorthand form; requires tables.
clink.argmatcher()
    { "one", "won" }                -- Arg #1
    { "two", "too" }                -- Arg #2
    { "-a", "-b", "/?", "/h" }      -- Flags

-- Normal form:
clink.argmatcher()
:addarg(
    { "one", "won" }                -- Arg #1
    { "two", "too" }                -- Arg #2
)
:addflags("-a", "-b", "/?", "/h")   -- Flags

With the shorthand form flags are implied rather than declared. When a shorthand table's first value is a string starting with - or / then the table is interpreted as flags. Note that it's still possible with shorthand form to mix flag prefixes, and even add additional flag prefixes, such as { '-a', '/b', '=c' }.

Coloring The Input Text

When the clink.colorize_input setting is enabled, argmatcher automatically apply colors to the input text by parsing it.

It's possible for an argmatcher to provide a function to override how its arguments are colored. This function is called once for each of the argmatcher's arguments.

It's also possible to register a classifier function for the whole input line. This function is very similar to a match generator; classifier functions are called in priority order, and a classifier can choose to stop the classification process.

More Advanced Stuff

Setting a classifier function in an argmatcher

In cases where an argmatcher isn't able to color the input text in the desired manner, it's possible to supply a classifier function that overrides how the argmatcher colors the input text. An argmatcher's classifier function is called once for each word the argmatcher parses, but it can classify any words (not just the word it was called for). Each argmatcher can have its own classifier function, so when there are linked argmatchers more than one function may be invoked.

Words are colored by classifying the words, and each classification has an associated color. See word_classifications:classifyword() for the available classification codes.

The clink set command has different syntax depending on the setting type, so the argmatcher for clink needs help in order to get everything right. A custom generator function parses the input text to provide appropriate matches, and a custom classifier function applies appropriate coloring.

-- In this example, the argmatcher matches a directory as the first argument and
-- a file as the second argument.  It uses a word classifier function to classify
-- directories (words that end with a path separator) as "unexpected" in the
-- second argument position.

local function classify_handler(arg_index, word, word_index, line_state, classifications)
    -- `arg_index` is the argument position in the argmatcher.
    -- In this example only position 2 needs special treatent.
    if arg_index ~= 2 then
        return
    end

    -- `arg_index` is the argument position in the argmatcher.
    -- `word_index` is the word position in the `line_state`.
    -- Ex1: in `samp dir file` for the word `dir` the argument index is 1 and
    -- the word index is 2.
    -- Ex2: in `samp --help dir file` for the word `dir` the argument index is
    -- still 1, but the word index is 3.

    -- `word` is the word the classifier function was called for and `word_index`
    -- is its position in the line.  Because `line_state` is also provided, the
    -- function can examine any words in the input line.
    if word:sub(-1) == "\\" then
        -- The word appears to be a directory, but this example expects only
        -- files in argument position 2.  Here the word gets classified as "n"
        -- (unexpected) so it gets colored differently.
        classifications:classifyword(word_index, "n")
    end
end

local matcher = clink.argmatcher("samp")
:addflags("--help")
:addarg({ clink.dirmatches })
:addarg({ clink.filematches })
:setclassifier(classify_handler)

Setting a classifier function for the whole input line

In some cases it may be desireable to use a custom classifier to apply coloring in an input line.

First create a classifier object:

local my_classifier = clink.classifier(priority)

The priority argument is a number that influences when the classifier gets called, with lower numbers going before higher numbers.

Next define a classify function on the object, taking the following form:

function my_classifier:classify(commands)
    -- commands is a table of { line_state, classifications } objects, one per
    -- command in the input line.  For example, "echo hello & echo world" is two
    -- commands:  "echo hello" and "echo world".
end

commands is a table of tables, with the following scheme: { {line_state:line, classifications:word_classifications}, ... }.

If no further classifiers need to be called then the function should return true. Returning false or nil continues letting other classifiers get called.

-- In this example, a custom classifier applies colors to command separators and
-- redirection symbols in the input line.
local cmdsep_classifier = clink.classifier(50)
function cmdsep_classifier:classify(commands)
    -- The `classifications:classifyword()` method can only affect the words for
    -- the corresponding command.  However, this example doesn't need to parse
    -- words within commands, it just wants to parse the whole line.  And since
    -- each command's `classifications:applycolor()` method can apply color
    -- anywhere in the entire input line, this example can simply use the first
    -- command's `classifications` object.
    if commands[1] then
        local line_state = commands[1].line_state
        local classifications = commands[1].classifications
        local line = line_state:getline()
        local quote = false
        local i = 1
        while (i <= #line) do
            local c = line:sub(i,i)
            if c == '^' then
                i = i + 1
            elseif c == '"' then
                quote = not quote
            elseif quote then
            elseif c == '&' or c == '|' then
                classifications:applycolor(i, 1, "95")
            elseif c == '>' or c == '<' then
                classifications:applycolor(i, 1, "35")
                if line:sub(i,i+1) == '>&' then
                    i = i + 1
                    classifications:applycolor(i, 1, "35")
                end
            end
            i = i + 1
        end
    end
end

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).

Writing a prompt filter is straightforward:

  1. Create a new prompt filter by calling clink.promptfilter() along with a priority id which dictates the order in which filters are called. Lower priority ids are called first.
  2. Define a :filter() function on the returned prompt filter.

The filter function takes a string argument that contains the filtered prompt so far. If the filter function returns nil, it has no effect. If the filter function returns a string, that string is used as the new filtered prompt (and may be further modified by other prompt filters with higher priority ids). If the filter function returns a string and a boolean, then if the boolean is false the prompt filtering is done and no further filter functions are called.

local p = clink.promptfilter(30)
function p:filter(prompt)
    return "new prefix "..prompt.." new suffix" -- Add ,false to stop filtering.
end

The following example illustrates setting the prompt, modifying the prompt, using ANSI escape code for colors, running a git command to find the current branch, and stopping any further processing.

local green  = "\x1b[92m"
local yellow = "\x1b[93m"
local cyan   = "\x1b[36m"
local normal = "\x1b[m"

-- A prompt filter that discards any prompt so far and sets the
-- prompt to the current working directory.  An ANSI escape code
-- colors it yellow.
local cwd_prompt = clink.promptfilter(30)
function cwd_prompt:filter(prompt)
    return yellow..os.getcwd()..normal
end

-- A prompt filter that inserts the date at the beginning of the
-- the prompt.  An ANSI escape code colors the date green.
local date_prompt = clink.promptfilter(40)
function date_prompt:filter(prompt)
    return green..os.date("%a %H:%M")..normal.." "..prompt
end

-- A prompt filter that may stop further prompt filtering.
-- This is a silly example, but on Wednesdays, it stops the
-- filtering, which in this example prevents git branch
-- detection and the line feed and angle bracket.
local wednesday_silliness = clink.promptfilter(60)
function wednesday_silliness:filter(prompt)
    if os.date("%a") == "Wed" then
        -- The ,false stops any further filtering.
        return prompt.." HAPPY HUMP DAY! ", false
    end
end

-- A prompt filter that appends the current git branch.
local git_branch_prompt = clink.promptfilter(65)
function git_branch_prompt:filter(prompt)
    local line = io.popen("git branch --show-current 2>nul"):read("*a")
    local branch = line:match("(.+)\n")
    if branch then
        return prompt.." "..cyan.."["..branch.."]"..normal
    end
end

-- A prompt filter that adds a line feed and angle bracket.
local bracket_prompt = clink.promptfilter(150)
function bracket_prompt:filter(prompt)
    return prompt.."\n> "
end

The resulting prompt will look like this:

Wed 12:54 c:\dir [master]
> _

...except on Wednesdays, when it will look like this:

Wed 12:54 c:\dir HAPPY HUMP DAY! _

ANSI escape codes in the prompt string

Readline needs to be told which characters in the prompt are unprintable or invisible. To help with that, Clink automatically detects most standard ANSI escape codes (and most of ConEmu's non-standard escape codes) and the BEL character (^G, audible bell) and surrounds them with \001 (^A) and \002 (^B) characters. For any other unprintable characters, the \001 and \002 characters need to be added manually. Otherwise Readline misinterprets the length of the prompt and can display the prompt and input line incorrectly in some cases (especially if the input line wraps onto a second line).

More Advanced Stuff

Right Side Prompt

In addition to the normal prompt filtering, Clink can also show a prompt on the right side of the first line of input. The right side prompt defaults to the value of the %CLINK_RPROMPT% environment variable, if set, otherwise it is blank. This right side prompt is automatically hidden if the input line text reaches it.

The right side prompt can be filtered through Lua just like the normal prompt can be. Simply define a :rightfilter() function on the prompt filter returned by a call to clink.promptfilter(). A prompt filter can define both :filter() and :rightfilter(), or can define only :filter().

The :rightfilter() function works the same as the :filter() function, except that it operates on the right side prompt. It takes a string argument that contains the filtered right side prompt so far. If the rightfilter function returns nil, it has no effect. If the rightfilter function returns a string, that string is used as the new filtered right side prompt (and may be further modified by other prompt filters with higher priority ids). If either the rightfilter function or the normal filter function returns a string and a boolean, then if the boolean is false the prompt filtering is done and no further filter functions are called.

This example modifies the right side prompt by prepending the current date:

local p = clink.promptfilter(30)
function p:filter(prompt)
    -- The :filter() function must be defined.  But if the prompt filter is
    -- only interested in modifying the right side prompt, then the :filter()
    -- function may do nothing.
end
function p:rightfilter(prompt)
    local sep = #prompt > 0 and "  " or ""
    return os.date()..sep..prompt
end

Note: If the console font and encoding are mismatched, or if some kinds of emoji are present, then the right side prompt might show up positioned incorrectly. If that happens, try adjusting the font or encoding (e.g. sometimes running chcp utf-8 can resolve positioning issues).

Asynchronous Prompt Filtering

Prompt filtering needs to be fast, or it can interfere with using the shell (e.g. git status can be slow in a large repo).

Clink provides a way for prompt filters to do some initial work and set the prompt, continue doing work in the background, and then refresh the prompt again when the background work is finished. This is accomplished by using Lua coroutines, but Clink simplifies and streamlines the process.

A prompt filter can call clink.promptcoroutine(my_func) to run my_func() inside a coroutine. Clink will automatically resume the coroutine repeatedly while input line editing is idle. When my_func() completes, Clink will automatically refresh the prompt by triggering prompt filtering again.

Typically the motivation to use asynchronous prompt filtering is that one or more io.popen("some slow command") calls take too long. They can be replaced with io.popenyield() calls inside the prompt coroutine to let them run in the background.

Global data: If my_func() needs to use any global data, then it's important to use clink.onbeginedit() to register an event handler that can reset the global data for each new input line session. Otherwise the data may accidentally "bleed" across different input line sessions.

Backward compatibility: A prompt filter must handle backward compatibility itself if it needs to run on versions of Clink that don't support asynchronous prompt filtering (v1.2.9 and lower). E.g. you can use if clink.promptcoroutine then to test whether the API exists.

The following example illustrates running git status in the background. It also remembers the status from the previous input line, so that it can reduce flicker by using the color from last time until the background status operation completes.

local prev_dir      -- Most recent git repo visited.
local prev_info     -- Most recent info retrieved by the coroutine.

local function get_git_dir(dir)
    -- Check if the current directory is in a git repo.
    local child
    repeat
        if os.isdir(path.join(dir, ".git")) then
            return dir
        end
        -- Walk up one level to the parent directory.
        dir,child = path.toparent(dir)
        -- If child is empty, we've reached the top.
    until (not child or child == "")
    return nil
end

local function get_git_branch()
    -- Get the current git branch name.
    local file = io.popen("git branch --show-current 2>nul")
    local branch = file:read("*a"):match("(.+)\n")
    file:close()
    return branch
end

local function get_git_status()
    -- The io.popenyield API is like io.popen, but it yields until the output is
    -- ready to be read.
    local file = io.popenyield("git --no-optional-locks status --porcelain 2>nul")
    local status = false
    for line in file:lines() do
        -- If there's any output, the status is not clean.  Since this example
        -- doesn't analyze the details, it can stop once it knows there's any
        -- output at all.
        status = true
        break
    end
    file:close()
    return status
end

local function get_git_conflict()
    -- The io.popenyield API is like io.popen, but it yields until the output is
    -- ready to be read.
    local file = io.popenyield("git diff --name-only --diff-filter=U 2>nul")
    local conflict = false
    for line in file:lines() do
        -- If there's any output, there's a conflict.
        conflict = true
        break
    end
    file:close()
    return conflict
end

local function collect_git_info()
    -- This is run inside the coroutine, which happens while idle while waiting
    -- for keyboard input.
    local info = {}
    info.status = get_git_status()
    info.conflict = get_git_conflict()
    -- Until this returns, the call to clink.promptcoroutine() will keep
    -- returning nil.  After this returns, subsequent calls to
    -- clink.promptcoroutine() will keep returning this return value, until a
    -- new input line begins.
    return info
end

local git_prompt = clink.promptfilter(55)
function git_prompt:filter(prompt)
    -- Do nothing if not a git repo.
    local dir = get_git_dir(os.getcwd())
    if not dir then
        return
    end
    -- Reset the cached status if in a different repo.
    if prev_dir ~= dir then
        prev_info = nil
        prev_dir = dir
    end
    -- Do nothing if git branch not available.  Getting the branch name is fast,
    -- so it can run outside the coroutine.  That way the branch name is visible
    -- even while the coroutine is running.
    local branch = get_git_branch()
    if not branch or branch == "" then
        return
    end
    -- Start a coroutine to collect various git info in the background.  The
    -- clink.promptcoroutine() call returns nil immediately, and the
    -- coroutine runs in the background.  After the coroutine finishes, prompt
    -- filtering is triggered again, and subsequent clink.promptcoroutine()
    -- calls from this prompt filter immediately return whatever the
    -- collect_git_info() function returned when it completed.  When a new input
    -- line begins, the coroutine results are reset to nil to allow new results.
    local info = clink.promptcoroutine(collect_git_info)
    -- If no status yet, use the status from the previous prompt.
    if info == nil then
        info = prev_info or {}
    else
        prev_info = info
    end
    -- Choose color for the git branch name:  green if status is clean, yellow
    -- if status is not clean, red if conflict is present, or default color if
    -- status isn't known yet.
    local sgr = "37;1"
    if info.conflict then
        sgr = "31;1"
    elseif info.status ~= nil then
        sgr = info.status and "33;1" or "32;1"
    end
    -- Prefix the prompt with "[branch]" using the status color.
    return "\x1b["..sgr.."m["..branch.."]\x1b[m  "..prompt
end

Miscellaneous

Key bindings

Key bindings are defined in .inputrc files. See the Configuring Readline section for more information.

Here is the quick version:

You can use clink info to find the directories and configuration files for the current Clink session.

Here is an example .inputrc file with the key bindings that I use myself:

$if clink           # begin clink-only section

# The following key bindings are for emacs mode.
set keymap emacs

# Completion key bindings.
"\t":               old-menu-complete               # Tab
"\e[Z":             old-menu-complete-backward      # Shift+Tab
"\e[27;5;9~":       clink-popup-complete            # Ctrl+Tab (ConEmu needs additional configuration to allow Ctrl+Tab)
"\x1b[27;5;32~":    complete                        # Ctrl+Space

# Some key bindings I got used to from 4Dos/4NT/Take Command.
C-b:                                                # Ctrl+B (cleared because I redefined Ctrl+F)
C-d:                remove-history                  # Ctrl+D (replaces `delete-char`)
C-f:                clink-expand-doskey-alias       # Ctrl+F (replaces `forward-char`)
C-k:                add-history                     # Ctrl+K (replaces `kill-line`)
"\e[A":             history-search-backward         # Up (replaces `previous-history`)
"\e[B":             history-search-forward          # Down (replaces `next-history`)
"\e[5~":            clink-popup-history             # PgUp (replaces `history-search-backward`)
"\e[6~":                                            # PgDn (cleared because I redefined PgUp)
"\e[1;5F":          end-of-line                     # Ctrl+End (replaces `kill-line`)
"\e[1;5H":          beginning-of-line               # Ctrl+Home (replaces `backward-kill-line`)

# Some key bindings handy in default (conhost) console windows.
M-b:                                                # Alt+B (cleared because I redefined Alt+F)
M-f:                clink-find-conhost              # Alt+F for "Find..." from the console's system menu
M-m:                clink-mark-conhost              # Alt+M for "Mark" from the console's system menu

# Some key bindings for interrogating the Readline configuration.
"\C-x\C-f":         dump-functions                  # Ctrl+X, Ctrl+F
"\C-x\C-m":         dump-macros                     # Ctrl+X, Ctrl+M
"\C-x\C-v":         dump-variables                  # Ctrl+X, Ctrl+V

# Misc other key bindings.
"\e[5;6~":          clink-popup-directories         # Ctrl+Shift+PgUp
C-_:                kill-line                       # Ctrl+- (replaces `undo`)

$endif              # end clink-only section

The clink-show-help command is bound to Alt+H and lists all currently active key bindings. The list displays "friendly" key names, and these names are generally not suitable for use in .inputrc files. For example "Up" is the friendly name for "\e[A", and "A-C-F2" is the friendly name for "\e\e[1;5Q". To see key sequence strings suitable for use in .inputrc files use clink echo or Alt+Shift+H.

Note: Third party console hosts such as ConEmu may have their own key bindings that supersede Clink. They usually have documentation for how to change or disable their key bindings to allow console programs to handle the keys instead.

Clink provides an easy way to find the key sequence for any key combination that Clink supports. Run clink echo and then press key combinations; the associated key binding sequence is printed to the console output and can be used for a key binding in the inputrc file.

A chord can be formed by concatenating multiple key binding sequences. For example, "\C-X" and "\e[H" can be concatenated to form "\C-X\e[H" representing the chord Ctrl+X,Home.

Binding special keys

Here is a table of the key binding sequences for the special keys. Clink primarily uses VT220 emulation for keyboard input, but also uses some Xterm extended key sequences.

Normal Shift Ctrl Ctrl+Shift Alt Alt+Shift Alt+Ctrl Alt+Ctrl+Shift
Up \e[A \e[1;2A \e[1;5A \e[1;6A \e[1;3A \e[1;4A \e[1;7A \e[1;8A
Down \e[B \e[1;2B \e[1;5B \e[1;6B \e[1;3B \e[1;4B \e[1;7B \e[1;8B
Left \e[D \e[1;2D \e[1;5D \e[1;6D \e[1;3D \e[1;4D \e[1;7D \e[1;8D
Right \e[C \e[1;2C \e[1;5C \e[1;6C \e[1;3C \e[1;4C \e[1;7C \e[1;8C
Insert \e[2~ \e[2;2~ \e[2;5~ \e[2;6~ \e[2;3~ \e[2;4~ \e[2;7~ \e[2;8~
Delete \e[3~ \e[3;2~ \e[3;5~ \e[3;6~ \e[3;3~ \e[3;4~ \e[3;7~ \e[3;8~
Home \e[H \e[1;2H \e[1;5H \e[1;6H \e[1;3H \e[1;4H \e[1;7H \e[1;8H
End \e[F \e[1;2F \e[1;5F \e[1;6F \e[1;3F \e[1;4F \e[1;7F \e[1;8F
PgUp \e[5~ \e[5;2~ \e[5;5~ \e[5;6~ \e[5;3~ \e[5;4~ \e[5;7~ \e[5;8~
PgDn \e[6~ \e[6;2~ \e[6;5~ \e[6;6~ \e[6;3~ \e[6;4~ \e[6;7~ \e[6;8~
Tab \t \e[Z \e[27;5;9~ \e[27;6;9~ - - - -
Space Space - \e[27;5;32~ \e[27;6;32~ - - \e[27;7;32~ \e[27;8;32~
Backspace ^h \e[27;2;8~ Rubout \e[27;6;8~ \e^h \e[27;4;8~ \eRubout \e[27;8;8~
F1 \eOP \e[1;2P \e[1;5P \e[1;6P \e\eOP \e\e[1;2P \e\e[1;5P \e\e[1;6P
F2 \eOQ \e[1;2Q \e[1;5Q \e[1;6Q \e\eOQ \e\e[1;2Q \e\e[1;5Q \e\e[1;6Q
F3 \eOR \e[1;2R \e[1;5R \e[1;6R \e\eOR \e\e[1;2R \e\e[1;5R \e\e[1;6R
F4 \eOS \e[1;2S \e[1;5S \e[1;6S \e\eOS \e\e[1;2S \e\e[1;5S \e\e[1;6S
F5 \e[15~ \e[15;2~ \e[15;5~ \e[15;6~ \e\e[15~ \e\e[15;2~ \e\e[15;5~ \e\e[15;6~
F6 \e[17~ \e[17;2~ \e[17;5~ \e[17;6~ \e\e[17~ \e\e[17;2~ \e\e[17;5~ \e\e[17;6~
F7 \e[18~ \e[18;2~ \e[18;5~ \e[18;6~ \e\e[18~ \e\e[18;2~ \e\e[18;5~ \e\e[18;6~
F8 \e[19~ \e[19;2~ \e[19;5~ \e[19;6~ \e\e[19~ \e\e[19;2~ \e\e[19;5~ \e\e[19;6~
F9 \e[20~ \e[20;2~ \e[20;5~ \e[20;6~ \e\e[20~ \e\e[20;2~ \e\e[20;5~ \e\e[20;6~
F10 \e[21~ \e[21;2~ \e[21;5~ \e[21;6~ \e\e[21~ \e\e[21;2~ \e\e[21;5~ \e\e[21;6~
F11 \e[23~ \e[23;2~ \e[23;5~ \e[23;6~ \e\e[23~ \e\e[23;2~ \e\e[23;5~ \e\e[23;6~
F12 \e[24~ \e[24;2~ \e[24;5~ \e[24;6~ \e\e[24~ \e\e[24;2~ \e\e[24;5~ \e\e[24;6~

When the terminal.differentiate_keys setting is enabled then the following key bindings are also available:

Ctrl Ctrl+Shift Alt Alt+Shift Alt+Ctrl Alt+Ctrl+Shift
H \e[27;5;72~ \e[27;6;72~ \eh \eH \e[27;7;72~ \e[27;8;72~
I \e[27;5;73~ \e[27;6;73~ \ei \eI \e[27;7;73~ \e[27;8;73~
M \e[27;5;77~ \e[27;6;77~ \em \eM \e[27;7;77~ \e[27;8;77~
[ \e[27;5;219~ \e[27;6;219~ \e[27;3;219~ \e[27;4;219~ \e[27;7;219~ \e[27;8;219~

The terminal.raw_esc setting controls the binding sequence for the Esc key:

terminal.raw_esc Setting Value Key Binding Sequence
False (the default) \e[27;27~
True (replicate Unix terminal input quirks and issues) \e

Lua Key Bindings

You can bind a key to a Lua function by binding it to a macro that begins with "luafunc:". Clink will invoke the named Lua function when the key binding is input. Function names can include periods (such as foo.bar) but cannot include any other punctuation.

The Lua function receives a rl_buffer argument that gives it access to the input buffer.

Lua functions can print output, but should first call rl_buffer:beginoutput so that the output doesn't overwrite the displayed input line.

Example of a Lua function key binding in a .inputrc file:

M-C-y:      "luafunc:insert_date"
M-C-z:      "luafunc:print_date"

Example functions to go with that:

function insert_date(rl_buffer)
    rl_buffer:insert(os.date("%x %X"))
end

function print_date(rl_buffer)
    rl_buffer:beginoutput()
    print(os.date("%A %B %d, %Y   %I:%M %p"))
end

Saved Command History

Clink has a list of commands from the current session, and it can be saved and loaded across sessions.

A line won't be added to history if either of the following are true:

To prevent doskey alias expansion while still adding the line to history, you can start the line with a semicolon.

Line Description
somecmd Expands doskey alias and adds to history.
 somecmd Doesn't expand doskey alias and doesn't add to history.
;somecmd Doesn't expand doskey alias but does add to history.

The master history file

When the history.saved setting is enabled, then the command history is loaded and saved as follows (or when the setting is disabled, then it isn't saved between sessions).

Every time a new input line starts, Clink reloads the master history list and prunes it not to exceed the history.max_lines setting.

For performance reasons, deleting a history line marks the line as deleted without rewriting the history file. When the number of deleted lines gets too large (exceeding the max lines or 200, whichever is larger) then the history file is compacted: the file is rewritten with the deleted lines removed.

You can force the history file to be compacted regardless of the number of deleted lines by running history compact.

Shared command history

When the history.shared setting is enabled, then all instances of Clink update the master history file and reload it every time a new input line starts. This gives the effect that all instances of Clink share the same history -- a command entered in one instance will appear in other instances' history the next time they start an input line.

When the setting is disabled, then each instance of Clink loads the master file but doesn't append its own history back to the master file until after it exits, giving the effect that once an instance starts its history is isolated from other instances' history.

Multiple master history files

Normally Clink saves a single saved master history list. All instances of Clink load and save the same master history list.

It's also possible to make one or more instances of Clink use a different saved master history list by setting the %CLINK_HISTORY_LABEL% environment variable. This can be up to 32 alphanumeric characters, and is appended to the master history file name. Changing the %CLINK_HISTORY_LABEL% environment variable takes effect at the next input line.

Sample Scripts

Here are a few samples of what can be done with Clink.

The clink-completions collection of scripts has a bunch of argument matchers and completion generators for things like git, mercurial, npm, and more.

cmder-powerline-prompt

The cmder-powerline-prompt collection of scripts provides a Powerline-like prompt for Clink. It's extensible so you can add your own segments, and some configuration of built-in segments is also available.

oh-my-posh

The oh-my-posh program can generate fancy prompts. Refer to its documentation for how to configure it.

Integrating oh-my-posh with Clink is easy: just save the following text to an oh-my-posh.lua file in your Clink scripts directory (run clink info to find that), and make sure the oh-my-posh.exe program is in a directory listed in the %PATH% environment variable. (Or edit the script below to provide a fully qualified path to the oh-my-posh.exe program.)

-- oh-my-posh.lua
local ohmyposh_prompt = clink.promptfilter(1)
function ohmyposh_prompt:filter(prompt)
    prompt = io.popen("oh-my-posh.exe"):read("*a")
    return prompt, false
end

z.lua

The z.lua tool is a faster way to navigate directories, and it integrates with Clink.

The new Clink tries to be as backward compatible with Clink v0.4.9 as possible. However, in some cases upgrading may require a little bit of configuration work.

Troubleshooting Tips

If something seems to malfunction, here are some things to try that often help track down what's going wrong:

When reporting an issue, please include the following which answer a bunch of commonly relevant questions and saves time:

Lua API Reference

This section describes the Clink Lua API extensions. Also see Lua Documentation and the Lua 5.2 Manual for more information about the Lua programming language.

API groups

_argmatcher
( choices...:string|table ) : self

This adds argument matches. Arguments can be a string, a string linked to another parser by the concatenation operator, a table of arguments, or a function that returns a table of arguments. See Argument Completion for more information.

local my_parser = clink.argmatcher("git")
:addarg("add", "status", "commit", "checkout")

( flags...:string ) : self

This adds flag matches. Flags are separate from arguments: When listing possible completions for an empty word, only arguments are listed. But when the word being completed starts with the first character of any of the flags, then only flags are listed. See Argument Completion for more information.

local my_parser = clink.argmatcher("git")
:addarg({ "add", "status", "commit", "checkout" })
:addflags("-a", "-g", "-p", "--help")

( [index:integer] ) : self

This makes the parser loop back to argument position index when it runs out of positional sets of arguments (if index is omitted it loops back to argument position 1).

clink.argmatcher("xyzzy")
:addarg("zero", "cero")     -- first arg can be zero or cero
:addarg("one", "uno")       -- second arg can be one or uno
:addarg("two", "dos")       -- third arg can be two or dos
:loop(2)    -- fourth arg loops back to position 2, for one or uno, and so on

This makes the parser prevent invoking match generators. You can use it to "dead end" a parser and suggest no completions.


( func:function ) : self

This registers a function that gets called for each word the argmatcher handles, to classify the word as part of coloring the input text. See Coloring The Input Text for more information.


builder
( match:string|table, [type:string] ) : boolean

Adds a match. If match is a string, it's added as a match and type is the optional match type.

Or match can be a table with the following scheme: { match:string, [type:string] }. If a table element is missing the type field, then the type argument is used for that element.

If the type argument is omitted, "none" is assumed.

The match type can affect how the match is inserted, displayed, and colored:

TypeDescription
"word"Shows the whole word even if it contains slashes.
"arg"Avoids appending a space if the match ends with a colon or equal sign.
"command"Displays the match using color.cmd.
"alias"Displays the match using color.doskey.
"file"Shows only the last path component, with appropriate file coloring.
"dir"Shows only the last path component and adds a trailing path separator, with appropriate directory coloring.
"none"For backward compatibility the match is treated like "file", unless it ends with a path separator in which case it's treated like "dir".

ModifierDescription
"hidden"This can be combined with "file" or "dir" to use color.hidden (e.g. "file,hidden").
"readonly"This can be combined with "file" or "dir" to use color.readonly (e.g. "file,readonly").
"link"This can be combined with "file" or "dir" to appropriate symlink coloring (e.g. "file,link").
"orphaned"This can be combined with "link" to use appropriate orphaned symlink coloring (e.g. "file,link,orphaned").

See Completion Colors and Color Settings for more information about colors.

builder:addmatch("hello") -- type is "none"
builder:addmatch("some_word", "word")
builder:addmatch("/flag", "arg")
builder:addmatch("abbrev", "alias")
builder:addmatch({ match="foo.cpp", type="file" })
builder:addmatch({ match="bar", type="dir" })
builder:addmatch({ match=".git", type="dir hidden" })

( matches:table, [type:string] ) : integer, boolean

This is the equivalent of calling builder:addmatch() in a for-loop. Returns the number of matches added and a boolean indicating if all matches were added successfully.

matches can be a table of match strings, or a table of tables describing the matches.
type is used as the type when a match doesn't explicitly include a type, and is "none" if omitted.

builder:addmatches({"abc", "def"}) -- Adds two matches of type "none"
builder:addmatches({"abc", "def"}, "file") -- Adds two matches of type "file"
builder:addmatches({
  -- Same table scheme per entry here as in builder:addmatch()
  { match="remote/origin/master", type="word" },
  { match="remote/origin/topic", type="word" }
})

( [append:string] ) : nil

Sets character to append after matches. For example the set match generator uses this to append "=" when completing matches, so that completing set USER becomes set USERDOMAIN= (rather than set USERDOMAIN ).


( [state:boolean] ) : nil

Sets whether to suppress appending anything after the match except a possible closing quote. For example the env var match generator uses this.


( [state:integer] ) : nil

Sets whether to suppress quoting for the matches. Set to 0 for normal quoting, or 1 to suppress quoting, or 2 to suppress end quotes. For example the env var match generator sets this to 1 to overcome the quoting that would normally happen for "%" characters in filenames.


clink
( [priority:integer], commands...:string ) : _argmatcher

Creates and returns a new argument matcher parser object. Use :addarg() and etc to add arguments, flags, other parsers, and more. See Argument Completion for more information.

If one command is provided and an argument matcher parser object is already associated with the command, this returns the existing parser rather than creating a new parser. Using :addarg() starts at arg position 1, making it possible to merge new args and etc into the existing parser.


( [priority:integer] ) : table

Creates and returns a new word classifier object. Define on the object a :classify() function which gets called in increasing priority order (low values to high values) when classifying words for coloring the input. See Coloring The Input Text for more information.


( word:string ) : table

You can use this function in an argmatcher to supply directory matches. This automatically handles Readline tilde completion.

-- Make "cd" generate directory matches (no files).
clink.argmatcher("cd")
:addflags("/d")
:addarg({ clink.dirmatches })

( word:string ) : table

You can use this function in an argmatcher to supply file matches. This automatically handles Readline tilde completion.

Argmatchers default to matching files, so it's unusual to need this function. However, some exceptions are when a flag needs to accept file matches but other flags and arguments don't, or when matches need to include more than files.

-- Make "foo --file" generate file matches, but other flags and args don't.
-- And the third argument can be a file or $stdin or $stdout.
clink.argmatcher("foo")
:addflags(
  "--help",
  "--file"..clink.argmatcher():addarg({ clink.filematches })
)
:addarg({ "one", "won" })
:addarg({ "two", "too" })
:addarg({ clink.filematches, "$stdin", "$stdout" })

( [priority:integer] ) : table

Creates and returns a new match generator object. Define on the object a :generate() function which gets called in increasing priority order (low values to high values) when generating matches for completion. See Match Generators for more information.


() : string

Returns a string indicating who Clink thinks will currently handle ANSI escape codes. This can change based on the terminal.emulation setting. This always returns "unknown" until the first edit prompt (see clink.onbeginedit()).

This can be useful in choosing what kind of ANSI escape codes to use, but it is a best guess and is not necessarily 100% reliable.

ReturnDescription
"unknown"Clink doesn't know.
"clink"Clink is emulating ANSI support. 256 color and 24 bit color escape codes are mapped to the nearest of the 16 basic colors.
"conemu"Clink thinks ANSI escape codes will be handled by ConEmu.
"ansicon"Clink thinks ANSI escape codes will be handled by ANSICON.
"winterminal"Clink thinks ANSI escape codes will be handled by Windows Terminal.
"winconsole"Clink thinks ANSI escape codes will be handled by the default console support in Windows, but Clink detected a terminal replacement that won't support 256 color or 24 bit color.
"winconsolev2"Clink thinks ANSI escape codes will be handled by the default console support in Windows, or it might be handled by a terminal replacement that Clink wasn't able to detect.


() : string

Returns the current Clink session id.

This is needed when using io.popen() (or similar functions) to invoke clink history or clink info while Clink is installed for autorun. The popen API spawns a new CMD.exe, which gets a new Clink instance injected, so the history or info command will use the new session unless explicitly directed to use the calling session.

local c = os.getalias("clink")
local r = io.popen(c.." --session "..clink.getsession().." history")

( text:string ) : string

This API correctly converts UTF8 strings to lowercase, with international linguistic awareness.

clink.lower("Hello World") -- returns "hello world"

( func:function ) : nil

Registers func to be called when Clink's edit prompt is activated. The function receives no arguments and has no return values.


( func:function ) : nil

Registers func to be called when Clink is about to display matches. See Filtering the Match Display for more information.

local function my_filter(matches, popup)
  local new_matches = {}
  for _,m in ipairs(matches) do
    if m.match:find("[0-9]") then
      -- Ignore matches with one or more digits.
    else
      -- Keep the match, and also add * prefix to directory matches.
      if m.type:find("^dir") then
        m.display = "*"..m.match
      end
      table.insert(new_matches, m)
    end
  end
  return new_matches
end

function my_match_generator:generate(line_state, match_builder)
  ...
  clink.ondisplaymatches(my_filter)
end

( func:function ) : nil

Registers func to be called when Clink's edit prompt ends. The function receives a string argument containing the input text from the edit prompt.

Breaking Change in v1.2.16: The ability to replace the user's input has been moved to a separate onfilterinput event.


( func:function ) : nil

Registers func to be called after Clink's edit prompt ends (it is called after the onendedit event). The function receives a string argument containing the input text from the edit prompt. The function returns up to two values. If the first is not nil then it's a string that replaces the edit prompt text. If the second is not nil and is false then it stops further onfilterinput handlers from running.

Note: Be very careful if you replace the text; this has the potential to interfere with or even ruin the user's ability to enter command lines for CMD to process.


( func:function ) : nil

Registers func to be called after Clink generates matches for completion. See Filtering Match Completions for more information.


( func:function ) : nil

Registers func to be called when Clink is injected into a CMD process. The function is called only once per session.


( title:string, items:table, [index:integer] ) : string, boolean, integer

Displays a popup list and returns the selected item. May only be used within a luafunc: key binding.

title is required and captions the popup list.

items is a table of strings to display, or a table of items with the following scheme: { {display:string, value:string, description:string}, ... }. The display field is displayed in the popup list (or if not present then value is displayed). The value field is returned if the item is chosen. The description is optional, and is displayed in a dimmed color in a second column. If it contains tab characters ("\t") the description string is split into multiple columns (up to 3).

index optionally specifies the default item (or 1 if omitted).

If the popup is canceled or an error occurs, the function returns nil.

Otherwise the 3 return values are:

  • string indicating the value field from the selected item (or the display field if no value field is present).
  • boolean which is true if the item was selected with Shift or Ctrl pressed.
  • integer indicating the index of the selected item in the original items table.


( ... ) : nil

This works like print(), but this supports ANSI escape codes.

If the special value NONL is included anywhere in the argument list then the usual trailing newline is omitted. This can sometimes be useful particularly when printing certain ANSI escape codes.

Note: In Clink versions before v1.2.11 the clink.print() API exists (undocumented) but accepts exactly one string argument and is therefore not fully compatible with normal print() syntax. If you use fewer or more than 1 argument or if the argument is not a string, then first checking the Clink version (e.g. clink.version_encoded) can avoid runtime errors.

clink.print("\x1b[32mgreen\x1b[m \x1b[35mmagenta\x1b[m")
-- Outputs green in green, a space, and magenta in magenta.

local a = "hello"
local world = 73
clink.print("a", a, "world", world)
-- Outputs a       hello   world   73.

clink.print("hello", NONL)
clink.print("world")
-- Outputs helloworld.

( func:function ) : [return value from func]

Creates a coroutine to run the func function in the background. Clink will automatically resume the coroutine repeatedly while input line editing is idle. When the func function completes, Clink will automatically refresh the prompt by triggering prompt filtering again.

A coroutine is only created the first time each prompt filter calls this API during a given input line session. Subsequent calls reuse the already-created coroutine. (E.g. pressing Enter ends an input line session.)

The API returns nil until the func function has finished. After that, the API returns whatever the func function returned. The API returns one value; if multiple return values are needed, return them in a table.

If the prompt.async setting is disabled, then the coroutine runs to completion immediately before returning. Otherwise, the coroutine runs during idle while editing the input line. The func function receives one argument: true if it's running in the background, or false if it's running immediately.

See Asynchronous Prompt Filtering for more information.

Note: each prompt filter can have at most one prompt coroutine.


( [priority:integer] ) : table

Creates and returns a new promptfilter object that is applied in increasing priority order (low values to high values). Define on the object a :filter() function that takes a string argument which contains the filtered prompt so far. The function can return nil to have no effect, or can return a new prompt string. It can optionally stop further prompt filtering by also returning false. See Customising The Prompt for more information.

local foo_prompt = clink.promptfilter(80)
function foo_prompt:filter(prompt)
  -- Insert the date at the beginning of the prompt.
  return os.date("%a %H:%M").." "..prompt
end

( [mode:integer] ) : integer

This overrides how Clink translates slashes in completion matches, which is normally determined by the match.translate_slashes setting.

This is reset every time match generation is invoked, so use a generator to set this.

The mode specifies how to translate slashes when generators add matches:
ModeDescription
0No translation.
1Translate using the system path separator (backslash on Windows).
2Translate to slashes (/).
3Translate to backslashes (\).

If mode is omitted, then the function returns the current slash translation mode without changing it.

Note: Clink always generates file matches using the system path separator (backslash on Windows), regardless what path separator may have been typed as input. Setting this to 0 does not disable normalizing typed input paths when invoking completion; it only disables translating slashes in custom generators.

-- This example affects all match generators, by using priority -1 to
-- run first and returning false to let generators continue.
-- To instead affect only one generator, call clink.translateslashes()
-- in its :generate() function and return true.
local force_slashes = clink.generator(-1)
function force_slashes:generate()
  clink.translateslashes(2)    -- Convert to slashes.
  return false                 -- Allow generators to continue.
end

( text:string ) : string

This API correctly converts UTF8 strings to uppercase, with international linguistic awareness.

clink.upper("Hello World") -- returns "HELLO WORLD"

string variable

The commit part of the Clink version number. For v1.2.3.a0f14d the commit part is a0f14d.


integer variable

The Clink version number encoded as a single integer following the format Mmmmpppp where M is the major part, m is the minor part, and p is the patch part of the version number.

For example, Clink v95.6.723 would be 950060723.

This format makes it easy to test for feature availability by encoding version numbers from the release notes.


integer variable

The major part of the Clink version number. For v1.2.3.a0f14d the major version is 1.


integer variable

The minor part of the Clink version number. For v1.2.3.a0f14d the minor version is 2.


integer variable

The patch part of the Clink version number. For v1.2.3.a0f14d the patch version is 3.


console
( text:string ) : integer

Returns the count of visible character cells that would be consumed if the text string were output to the console, accounting for any ANSI escape codes that may be present in the text.

Note: backspace characters and line endings are counted as visible character cells and will skew the resulting count.


( starting_line:integer, [text:string], [mode:string], [attr:integer], [attrs:table of integers], [mask:string] ) : integer

Searches downwards (forwards) for a line containing the specified text and/or attributes, starting at line starting_line. The matching line number is returned, or 0 if no matching line is found.

This behaves the same as console.findprevline() except that it searches in the opposite direction.


( starting_line:integer, [text:string], [mode:string], [attr:integer], [attrs:table of integers], [mask:string] ) : integer

Searches upwards (backwards) for a line containing the specified text and/or attributes, starting at line starting_line. The matching line number is returned, or 0 if no matching line is found, or -1 if an invalid regular expression is provided.

You can search for text, attributes, or both. Include the text argument to search for text, and include either the attr or attrs argument to search for attributes. If both text and attribute(s) are passed, then the attribute(s) must be found within the found text. If only attribute(s) are passed, then they must be found anywhere in the line. See console.linehascolor() for more information about the color codes.

The mode argument selects how the search behaves. To use a regular expression, pass "regex". To use a case insensitive search, pass "icase". These can be combined by separating them with a comma. The regular expression syntax is the ECMAScript syntax described here.

Any trailing whitespace is ignored when searching. This especially affects the $ (end of line) regex operator.

mask is optional and can be "fore" or "back" to only match foreground or background colors, respectively.

Note: Although most of the arguments are optional, the order of provided arguments is important.

The following example provides a pair of find_prev_colored_line and find_next_colored_line functions. The functions can be bound to keys via the luafunc: macro syntax in a .inputrc file. They scroll the screen buffer to the previous or next line that contains "warn" or "error" colored red or yellow.

local was_top
local found_index

local function reset_found()
  was_top = nil
  found_index = nil
end

-- Register for the onbeginedit event, to reset the found
-- line number each time a new editing prompt begins.
clink.onbeginedit(reset_found)

-- Searches upwards for a line containing "warn" or "error"
-- colored red or yellow.
function find_prev_colored_line(rl_buffer)
  local height = console.getheight()
  local cur_top = console.gettop()
  local offset = math.modf((height - 1) / 2) -- For vertically centering the found line.

  local start
  if found_index == nil or cur_top ~= was_top then
    start = cur_top
    was_top = start
  else
    start = found_index
  end

  -- Only search if there's still room to scroll up.
  if start - offset > 1 then
    local match = console.findprevline(start - 1, "warn|error", "regex", {4,12,14}, "fore")
    if match ~= nil and match > 0 then
      found_index = match
    end
  end

  if found_index ~= nil then
    console.scroll("absolute", found_index - offset)
    was_top = console.gettop()
  else
    rl_buffer:ding()
  end
end

-- Searches downwards for a line containing "warn" or "error"
-- colored red or yellow.
function find_next_colored_line(rl_buffer)
  if found_index == nil then
    rl_buffer:ding()
    return
  end

  local height = console.getheight()
  local cur_top = console.gettop()
  local offset = math.modf((height - 1) / 2)

  local start
  if cur_top ~= was_top then
      start = cur_top + height - 1
      was_top = cur_top
  else
      start = found_index
  end

  -- Only search if there's still room to scroll down.
  local bottom = console.getnumlines()
  if start - offset + height - 1 < bottom then
    local match = console.findnextline(start + 1, "warn|error", "regex", {4,12,14}, "fore")
    if match ~= nil and match > 0 then
      found_index = match
    end
  end

  if found_index ~= nil then
    console.scroll("absolute", found_index - offset)
    was_top = console.gettop()
  else
    rl_buffer:ding()
  end
end

() : integer

Returns the number of visible lines of the console screen buffer.


( line:integer ) : string

Returns the text from line number line, from 1 to console.getnumlines().

Any trailing whitespace is stripped before returning the text.


() : integer

Returns the total number of lines in the console screen buffer.


() : string

Returns the console title text.


() : integer

Returns the current top line (scroll position) in the console screen buffer.


() : integer

Returns the width of the console screen buffer in characters.


( line:integer ) : boolean

Returns whether line number line uses only the default text color.


( line:integer, [attr:integer], [attrs:table of integers], [mask:string] ) : boolean

Returns whether line number line contains the DOS color code attr, or any of the DOS color codes in attrs (either an integer or a table of integers must be provided, but not both). mask is optional and can be "fore" or "back" to only match foreground or background colors, respectively.

The low 4 bits of the color code are the foreground color, and the high 4 bits of the color code are the background color. This refers to the default 16 color palette used by console windows. When 256 color or 24-bit color ANSI escape codes have been used, the closest of the 16 colors is used.

To build a color code, add the corresponding Foreground color and the Background color values from this table:

ForegroundBackgroundColor
00
 
Black
116
 
Dark Blue
232
 
Dark Green
348
 
Dark Cyan
464
 
Dark Red
580
 
Dark Magenta
696
 
Dark Yellow
7112
 
Gray
8128
 
Dark Gray
9144
 
Bright Blue
10160
 
Bright Green
11176
 
Bright Cyan
12192
 
Bright Red
13208
 
Bright Magenta
14224
 
Bright Yellow
15240
 
White


( text:string ) : string, integer

Returns the input text with ANSI escape codes removed, and the count of visible character cells that would be consumed if the text were output to the console.

Note: backspace characters and line endings are counted as visible character cells and will skew the resulting count.


( candidate_pattern:string, accept_pattern:string ) : table

Uses the provided Lua string patterns to collect text from the current console screen and returns a table of matching text snippets. The snippets are ordered by distance from the input line.

For example candidate_pattern could specify a pattern that identifies words, and accept_pattern could specify a pattern that matches words composed of hexadecimal digits.

local matches = console.screengrab(
        "[^%w]*(%w%w[%w]+)",   -- Words with 3 or more letters are candidates.
        "^%x+$")               -- A candidate containing only hexadecimal digits is a match.

( mode:string, amount:integer ) : integer

Scrolls the console screen buffer and returns the number of lines scrolled up (negative) or down (positive).

The mode specifies how to scroll:
ModeDescription
"line"Scrolls by amount lines; negative is up and positive is down.
"page"Scrolls by amount pages; negative is up and positive is down.
"end"Scrolls to the top if amount is negative, or to the bottom if positive.
"absolute"Scrolls to line amount, from 1 to console.getnumlines().


( title:string ) : nil

Sets the console title text.


io
( command:string, [mode:string] ) : file, file

Runs command and returns two file handles: a file handle for reading output from the command, and a file handle for writing input to the command.

mode can be "t" for text mode (the default if omitted) or "b" for binary mode.

If the function fails it returns nil, an error message, and an error number.

Warning This can result in deadlocks unless the command fully reads all of its input before writing any output. This is because Lua uses blocking IO to read and write file handles. If the write buffer fills (or the read buffer is empty) then the write (or read) will block and can only become unblocked if the command correspondingly reads (or writes). But the other command can easily experience the same blocking IO problem on its end, resulting in a deadlock: process 1 is blocked from writing more until process 2 reads, but process 2 can't read because it is blocked from writing until process 1 reads.

local r,w = io.popenrw("fzf.exe --height 40%")

w:write("hello\n")
w:write("world\n")
w:close()

while (true) do
  local line = r:read("*line")
  if not line then
    break
  end
  print(line)
end
r:close()

( command:string, [mode:string] ) : file

This is the same as io.popen(command, mode) except that it only supports read mode and it yields until the command has finished:

Runs command and returns a read file handle for reading output from the command. It yields until the command has finished and the complete output is ready to be read without blocking.

The mode can contain "r" (read mode) and/or either "t" for text mode (the default if omitted) or "b" for binary mode. Write mode is not supported, so it cannot contain "w".

Note: if the prompt.async setting is disabled then this turns into a call to io.popen(command, mode) instead.

local file = io.popenyield("git status")

while (true) do
  local line = file:read("*line")
  if not line then
    break
  end
  do_things_with(line)
end
file:close()

line
() : integer

Returns the offset to the start of the delimited command in the line that's being effectively edited. Note that this may not be the offset of the first command of the line unquoted as whitespace isn't considered for words.

-- Given the following line; abc& 123
-- where commands are separated by & symbols.
line:getcommandoffset() == 4

() : integer

Returns the position of the cursor.


() : string

Returns the last word of the line. This is the word that matches are being generated for.

Note: The returned word omits any quotes. This helps generators naturally complete "foo\"ba to "foo\bar". The raw word including quotes can be obtained using the offset and length fields from line:getwordinfo() to extract a substring from the line returned by line:getline().

line:getword(line:getwordcount()) == line:getendword()

() : string

Returns the current line in its entirety.


( index:integer ) : string

Returns the word of the line at index.

Note: The returned word omits any quotes. This helps generators naturally complete "foo\"ba to "foo\bar". The raw word including quotes can be obtained using the offset and length fields from line:getwordinfo() to extract a substring from the line returned by line:getline().


() : integer

Returns the number of words in the current line.


( index:integer ) : table

Returns a table of information about the Nth word in the line.

Note: The length refers to the substring in the line; it omits leading and trailing quotes, but includes embedded quotes. line:getword() conveniently strips embedded quotes to help generators naturally complete "foo\"ba to "foo\bar".

The table returned has the following scheme:

{
  offset,  -- [integer] offset where the word starts in the line:getline() string.
  length,  -- [integer] length of the word (includes embedded quotes).
  quoted,  -- [boolean] indicates whether the word is quoted.
  delim,   -- [string] the delimiter character, or an empty string.
  alias,   -- [boolean | nil] true if the word is a doskey alias, otherwise nil.
}

log
( message:string ) : nil

Writes info message to the Clink log file. Use this sparingly, or it could cause performance problems or disk space problems.


os
( path:string ) : boolean

Changes the current directory to path and returns whether it was successful.


( src:string, dest:string ) : boolean

Copies the src file to the dest file.


( [prefix:string], [ext:string], [path:string], [mode:string] ) : file, string

Creates a uniquely named file, intended for use as a temporary file. The name pattern is "location \ prefix _ processId _ uniqueNum extension".

prefix optionally specifies a prefix for the file name and defaults to "tmp".

ext optionally specifies a suffix for the file name and defaults to "" (if ext starts with a period "." then it is a filename extension).

path optionally specifies a path location in which to create the file. The default is the system TEMP directory.

mode optionally specifies "t" for text mode (line endings are translated) or "b" for binary mode (untranslated IO). The default is "t".

When successful, the function returns a file handle and the file name.

Note: Be sure to delete the file when finished, or it will be leaked.

If the function is unable to create a file it returns nil, an error message, and an error number. For example if the directory is inaccessible, or if there are already too many files, or invalid file name characters are used, or etc.


( ... ) : nil

This works like print() but writes the output via the OS `OutputDebugString()` API.

This function has no effect if the `lua.debug` Clink setting is off.

clink.debugprint("my variable = "..myvar)

( value:string ) : string

Returns value with any %name% environment variables expanded. Names are case insensitive. Special CMD syntax is not supported (e.g. %name:str1=str2% or %name:~offset,length%).

Note: See os.getenv() for a list of special variable names.


( name:string ) : string | nil

Returns command string for doskey alias name, or nil if the named alias does not exist.


() : table

Returns doskey alias names in a table of strings.


Returns a table containing the battery status for the device, or nil if an error occurs. The returned table has the following scheme:

{
  level,         -- [integer] the battery life from 0 to 100, or -1 if an
                 --            error occurred or there is no battery.
  acpower,       -- [boolean] whether the device is connected to AC power.
  charging,      -- [boolean] whether the battery is charging.
  batterysaver,  -- [boolean] whether Battery Saver mode is active.
}

() : string

Returns the current directory.


( name:string ) : string | nil

Returns the value of the named environment variable, or nil if it doesn't exist.

Note: Certain environment variable names receive special treatment:

NameSpecial Behavior
"HOME"If %HOME% is not set then a return value is synthesized from %HOMEDRIVE% and %HOMEPATH%, or from %USERPROFILE%.
"ERRORLEVEL"When the cmd.get_errorlevel setting is enabled (it is off by default) this returns the most recent exit code, just like the echo %ERRORLEVEL% command displays. Otherwise this returns 0.


() : table

Returns all environment variables in a table with the following scheme: { {name:string, value:string}, ... }.


() : integer

Returns the last command's exit code, if the cmd.get_errorlevel setting is enabled (it is disabled by default). Otherwise it returns 0.


( path:string ) : string

Returns the full path name for path.


() : string

Returns the fully qualified file name of the host process. Currently only CMD.EXE can host Clink.


( path:string ) : string

Returns the long path name for path.


() : integer

Returns the CMD.EXE process ID. This is mainly intended to help with salting unique resource names (for example named pipes).


() : table

Returns dimensions of the terminal's buffer and visible window. The returned table has the following scheme:

{
  bufwidth,     -- [integer] width of the screen buffer
  bufheight,    -- [integer] height of the screen buffer
  winwidth,     -- [integer] width of the visible window
  winheight,    -- [integer] height of the visible window
}

( path:string ) : string

Returns the 8.3 short path name for path. This may return the input path if an 8.3 short path name is not available.


( globpattern:string, [extrainfo:boolean] ) : table

Collects directories matching globpattern and returns them in a table of strings.

When extrainfo is true, then the returned table has the following scheme: { {name:string, type:string}, ... }.

The type string can be "file" or "dir", and may also contain ",hidden", ",readonly", ",link", and ",orphaned" depending on the attributes (making it usable as a match type for builder:addmatch()).

Note: any quotation marks (") in globpattern are stripped.


( globpattern:string, [extrainfo:boolean] ) : table

Collects files and/or directories matching globpattern and returns them in a table of strings.

When extrainfo is true, then the returned table has the following scheme: { {name:string, type:string}, ... }.

The type string can be "file" or "dir", and may also contain ",hidden", ",readonly", ",link", and ",orphaned" depending on the attributes (making it usable as a match type for builder:addmatch()).

Note: any quotation marks (") in globpattern are stripped.


( path:string ) : boolean

Returns whether path is a directory.


( path:string ) : boolean

Returns whether path is a file.


( path:string ) : boolean

Creates the directory path and returns whether it was successful.


( src:string, dest:string ) : boolean

Moves the src file to the dest file.


( path:string ) : boolean

Removes the directory path and returns whether it was successful.


( name:string, value:string ) : boolean

Sets the name environment variable to value and returns whether it was successful.


( path:string ) : boolean

Deletes the file path and returns whether it was successful.


path
( path:string ) : string

path.getbasename("/foo/bar.ext")    -- returns "bar"
path.getbasename("")                -- returns ""
path.getbasename(nil)               -- returns nil

( path:string ) : nil or string

This is similar to path.toparent() but can behave differently when the input path ends with a path separator. This is the recommended API for parsing a path into its component pieces, but is not recommended for walking up through parent directories.

path.getdirectory("foo")                -- returns nil
path.getdirectory("\foo")               -- returns "\"
path.getdirectory("c:foo")              -- returns "c:"
path.getdirectory([[c:\]])              -- returns "c:\"
path.getdirectory("c:\foo")             -- returns "c:\"
path.getdirectory("c:\foo\bar")         -- returns "c:\foo"
path.getdirectory("\\foo\bar")          -- returns "\\foo\bar"
path.getdirectory("\\foo\bar\dir")      -- returns "\\foo\bar"
path.getdirectory("")                   -- returns nil
path.getdirectory(nil)                  -- returns nil

-- These split the path components differently than path.toparent().
path.getdirectory([[c:\foo\bar\]])      -- returns "c:\foo\bar"
path.getdirectory([[\\foo\bar\dir\]])   -- returns "\\foo\bar\dir"

( path:string ) : nil or string

path.getdrive("e:/foo/bar")     -- returns "e:"
path.getdrive("foo/bar")        -- returns nil
path.getdrive("")               -- returns nil
path.getdrive(nil)              -- returns nil

( path:string ) : string

path.getextension("bar.ext")    -- returns ".ext"
path.getextension("bar")        -- returns ""
path.getextension("")           -- returns ""
path.getextension(nil)          -- returns nil

( path:string ) : string

path.getname("/foo/bar.ext")    -- returns "bar.ext"
path.getname("")                -- returns ""
path.getname(nil)               -- returns nil

( path:string ) : boolean

Examines the extension of the path name. Returns true if the extension is listed in %PATHEXT%. This caches the extensions in a map so that it's more efficient than getting and parsing %PATHEXT% each time.

path.isexecext("program.exe")   -- returns true
path.isexecext("file.doc")      -- returns false
path.isexecext("")              -- returns false
path.isexecext(nil)             -- returns nil

( left:string, right:string ) : string

path.join("/foo", "bar")    -- returns "/foo\bar"
path.join("", "bar")        -- returns "bar"
path.join("/foo", "")       -- returns "/foo"
path.join(nil, "bar")       -- returns nil
path.join("/foo", nil)      -- returns nil

( path:string, [separator:string] ) : string

Cleans path by normalising separators and removing "." and ".." elements. If separator is provided it is used to delimit path elements, otherwise a system-specific delimiter is used.

path.normalise("a////b/\\/c/")  -- returns "a\b\c\"
path.normalise("")              -- returns ""
path.normalise(nil)             -- returns nil

( path:string ) : parent:string, child:string

Splits the last path component from path, if possible. Returns the result and the component that was split, if any.

This is similar to path.getdirectory() but can behave differently when the input path ends with a path separator. This is the recommended API for walking up through parent directories.

local parent,child
parent,child = path.toparent("foo")             -- returns "", "foo"
parent,child = path.toparent("\foo")            -- returns "\", "foo"
parent,child = path.toparent("c:foo")           -- returns "c:", "foo"
parent,child = path.toparent([[c:\]])           -- returns "c:\", ""
parent,child = path.toparent("c:\foo")          -- returns "c:\", "foo"
parent,child = path.toparent("c:\foo\bar")      -- returns "c:\foo", "bar"
parent,child = path.toparent("\\foo\bar")       -- returns "\\foo\bar", ""
parent,child = path.toparent("\\foo\bar\dir")   -- returns "\\foo\bar", "dir"
parent,child = path.toparent("")                -- returns "", ""
parent,child = path.toparent(nil)               -- returns nil

-- These split the path components differently than path.getdirectory().
parent,child = path.toparent([[c:\foo\bar\]])   -- returns "c:\foo", "bar"
parent,child = path.toparent([[\\foo\bar\dir\]])-- returns "\\foo\bar", "dir"

rl
( path:string, [force:boolean] ) : string

Undoes Readline tilde expansion. See rl.expandtilde for more information.

rl.collapsetilde("C:\Users\yourusername\Documents")
 
-- The return value depends on the expand-tilde configuration variable:
-- When "on", the function returns "C:\Users\yourusername\Documents".
-- When "off", the function returns "~\Documents".
 
-- Or when force is true, the function returns "~\Documents".

( path:string ) : string, boolean

Performs Readline tilde expansion.

When generating filename matches for a word, use the rl.expandtilde and rl.collapsetilde helper functions to perform tilde completion expansion according to Readline's configuration.

Use rl.expandtilde to do tilde expansion before collecting file matches (e.g. via os.globfiles). If it indicates that it expanded the string, then use rl.collapsetilde to put back the tilde before returning a match.

local result, expanded = rl.expandtilde("~\Documents")
-- result is "C:\Users\yourusername\Documents"
-- expanded is true
 
-- This dir_matches function demonstrates efficient use of rl.expandtilde()
-- and rl.collapsetilde() to generate directory matches from the file system.
function dir_matches(match_word, word_index, line_state)
  -- Expand tilde before scanning file system.
  local word = line_state:getword(word_index)
  local expanded
  word, expanded = rl.expandtilde(word)
 
  -- Get the directory from 'word', and collapse tilde before generating
  -- matches.  Notice that collapsetilde() only needs to be called once!
  local root = path.getdirectory(word) or ""
  if expanded then
    root = rl.collapsetilde(root)
  end
 
  local matches = {}
  for _, d in ipairs(os.globdirs(word.."*", true)) do
    -- Join the filename with the input directory (might have a tilde).
    local dir = path.join(root, d.name)
    table.insert(matches, { match = dir, type = d.type })
  end
  return matches
end

( raw:boolean ) : table

Returns key bindings in a table with the following scheme: { {key:string, binding:string, desc:string, category:string}, ... }.

The following example demonstrates using this function in a luafunc: key binding to invoke clink.popuplist() to show a searchable list of key bindings, and then invoke whichever key binding is selected.

function luafunc_showkeybindings(rl_buffer)
local bindings = rl.getkeybindings()
if #bindings <= 0 then
rl_buffer:refreshline()
return
end

local items = {}
for _,kb in ipairs(bindings) do
table.insert(items, { value=kb.binding, display=kb.key, description=kb.binding.."\t"..kb.desc })
end

local binding, _, index = clink.popuplist("Key Bindings", items)
rl_buffer:refreshline()
if binding then
rl.invokecommand(binding)
end
end

() : string, function

Returns two values:

  • The name of the last Readline command invoked by a key binding.
  • The name of the last Lua function invoked by a key binding.

If the last key binding invoked a Lua function, then the first return value is an empty string unless the Lua function used rl.invokecommand() to also internally invoke a Readline command. If the last key binding did not invoke a Lua function, then the second return value is an empty string.

local last_rl_func, last_lua_func = rl.getlastcommand()

( name:string ) : string | nil

Returns the value of the named Readline configuration variable as a string, or nil if the variable name is not recognized.


( command:string, [count:integer] ) : boolean | nil

Invokes a Readline command named command. May only be used within a luafunc: key binding.

count is optional and defaults to 1 if omitted.

Returns true if the named command succeeds, false if the named command fails, or nil if the named command doesn't exist.

Warning: Invoking more than one Readline command in a luafunc: key binding could have unexpected results, depending on which commands are invoked.


( name:string ) : boolean | nil

Returns a boolean value indicating whether the named Readline configuration variable is set to true (on), or nil if the variable name is not recognized.


( matches:table, [type:string] ) : integer, boolean

Provides an alternative set of matches for the current word. This discards any matches that may have already been collected and uses matches for subsequent Readline completion commands until any action that normally resets the matches (such as moving the cursor or editing the input line).

May only be used within a luafunc: key binding.

The syntax is the same as for builder:addmatches() with one addition: You can add a "nosort" key to the matches table to disable sorting the matches.

local matches = {}
matches["nosort"] = true
rl.setmatches(matches)

This function can be used by a luafunc: key binding to provide matches based on some special criteria. For example, a key binding could collect numbers from the current screen buffer (such as issue numbers, commit hashes, line numbers, etc) and provide them to Readline as matches, making it convenient to grab a number from the screen and insert it as a command line argument.

Match display filtering is also possible by using clink.ondisplaymatches() after setting the matches.

Example .inputrc key binding:

M-n:            "luafunc:completenumbers"       # Alt+N

Example Lua function:

function completenumbers()
  local _,last_luafunc = rl.getlastcommand()
  if last_luafunc ~= "completenumbers" then
    -- Collect numbers from the screen (minimum of three digits).
    -- The numbers can be any base up to hexadecimal (decimal, octal, etc).
    local matches = console.screengrab("[^%w]*(%w%w[%w]+)", "^%x+$")
    -- They're already sorted by distance from the input line.
    matches["nosort"] = true
    rl.setmatches(matches)
  end

  rl.invokecommand("old-menu-complete")
end

( name:string, value:string ) : boolean

Temporarily overrides the named Readline configuration variable to the specified value. The return value reports whether it was successful, or is nil if the variable name is not recognized.

Note: This does not write the value into a config file. Instead it updates the variable in memory, temporarily overriding whatever is present in any config files. When config files are reloaded, they may replace the value again.


rl_buffer

Advances the output cursor to the next line after the Readline input buffer so that subsequent output doesn't overwrite the input buffer display.


Starts a new undo group. This is useful for grouping together multiple editing actions into a single undo operation.


() : nil

Dings the bell. If the bell-style Readline variable is visible then it flashes the cursor instead.


Ends an undo group. This is useful for grouping together multiple editing actions into a single undo operation.

Note: all undo groups are automatically ended when a key binding finishes execution, so this function is only needed if a key binding needs to create more than one undo group.


() : integer | nil

Returns any accumulated numeric argument (Alt+Digits, etc), or nil if no numeric argument has been entered.


() : string

Returns the current input line.


() : integer

Returns the cursor position in the input line.


() : integer

Returns the length of the input line.


( text:string ) : nil

Inserts text at the cursor position in the input line.


Redraws the input line.


( from:integer, to:integer ) : nil

Removes text from the input line starting at cursor position from through to.

Note: the input line is UTF8, and removing only part of a multi-byte Unicode character may have undesirable results.


( cursor:integer ) : integer

Sets the cursor position in the input line and returns the previous cursor position. cursor can be from 1 to rl_buffer:getlength().

Note: the input line is UTF8, and setting the cursor position inside a multi-byte Unicode character may have undesirable results.


settings
( name:string, default:..., [short_desc:string], [long_desc:string] ) : boolean

Adds a setting to the list of Clink settings and includes it in clink set. The new setting is named name and has a default value default when the setting isn't explicitly set.

The type of default determines what kind of setting is added: boolean, integer, and string values add the corresponding setting type. Or if the type is table then an enum setting is added: the table defines the accepted values, and the first value is the default value. Or if it's a string type and the name starts with "color." then a color setting is added.

name can't be more than 32 characters.
short_desc is an optional quick summary description and can't be more than 48 characters.
long_desc is an optional long description.

settings.add("myscript.myabc", true, "Boolean setting")
settings.add("myscript.mydef", 100, "Number setting")
settings.add("myscript.myghi", "abc", "String setting")
settings.add("myscript.myjkl", {"x","y","z"}, "Enum setting")
settings.add("color.mymno", "bright magenta", "Color setting")

( name:string, [descriptive:boolean] ) : boolean or string or integer

Returns the current value of the name Clink setting.

If it's a color setting and the optional descriptive parameter is true then the user friendly color name is returned.


( name:string, value:string ) : boolean

Sets the name Clink setting to value and returns whether it was successful.


string
( a:string, b:string ) : boolean

Performs a case insensitive comparison of the strings with international linguistic awareness. This is more efficient than converting both strings to lowercase and comparing the results.


( text:string, [delims:string], [quote_pair:string] ) : table

Splits text delimited by delims (or by spaces if not provided) and returns a table containing the substrings.

The optional quote_pair can provide a beginning quote character and an ending quote character. If only one character is provided it is used as both a beginning and ending quote character.


( text:string ) : integer

Returns a hash of the input text.


( a:string, b:string ) : integer

Returns how many characters match at the beginning of the strings, or -1 if the entire strings match. This respects the match.ignore_case and match.ignore_accents Clink settings.

string.matchlen("abx", "a")         -- returns 1
string.matchlen("abx", "aby")       -- returns 2
string.matchlen("abx", "abx")       -- returns -1

word_classifications
( start:integer, length:integer, color:string, [overwrite:boolean] ) : nil

Applies an ANSI SGR escape code to some characters in the input line.

start is where to begin applying the SGR code.

length is the number of characters to affect.

color is the SGR parameters sequence to apply (for example "7" is the code for reverse video, which swaps the foreground and background colors).

By default the color is applied to the characters even if some of them are already colored. But if overwrite is false each character is only colored if it hasn't been yet.

See Coloring The Input Text for more information.


( word_index:integer, word_class:string, [overwrite:boolean] ) : nil

This classifies the indicated word so that it can be colored appropriately.

The word_class is one of the following codes:

CodeClassificationClink Color Setting
"a"Argument; used for words that match a list of preset argument matches.color.arg or color.input
"c"Shell command; used for CMD command names.color.cmd
"d"Doskey alias.color.doskey
"f"Flag; used for flags that match a list of preset flag matches.color.flag
"o"Other; used for file names and words that don't fit any of the other classifications.color.input
"n"None; used for words that aren't recognized as part of the expected input syntax.color.unexpected
"m"Prefix that can be combined with another code (for the first word) to indicate the command has an argmatcher (e.g. "mc" or "md").color.argmatcher or the other code's color

By default the classification is applied to the word even if the word has already been classified. But if overwrite is false the word is only classified if it hasn't been yet.

See Coloring The Input Text for more information.


Deprecated
( [prefixes...:string] ) : self

Deprecated; don't use this. See _argmatcher:addflags for more information.

This overrides the default flag prefix (-). The flag prefixes are used to switch between matching arguments versus matching flags. When listing possible completions for an empty word (e.g. command _ where the cursor is at the _), only arguments are listed. And only flags are listed when the word starts with one of the flag prefixes. Each flag prefix must be a single character, but there can be multiple prefixes.

This is no longer needed because :addflags() does it automatically.

local my_parser = clink.argmatcher()
:setflagprefix("-", "/", "+")
:addflags("--help", "/?", "+mode")

( match:string ) : nil

Deprecated; don't use this. See builder:addmatch for more information.

This is a shim that lets clink.register_match_generator continue to work for now, despite being obsolete.


( ... ) : table

Deprecated; don't use this. See clink.argmatcher for more information.

Creates a new parser and adds ... to it.

-- Deprecated form:
local parser = clink.arg.new_parser(
  { "abc", "def" },       -- arg position 1
  { "ghi", "jkl" },       -- arg position 2
  "--flag1", "--flag2"    -- flags
)

-- Replace with form:
local parser = clink.argmatcher()
:addarg("abc", "def")               -- arg position 1
:addarg("ghi", "jkl")               -- arg position 2
:addflags("--flag1", "--flag2")     -- flags

( cmd:string, parser:table ) : table

Deprecated; don't use this. See clink.argmatcher for more information.

Adds parser to the first argmatcher for cmd. This behaves similarly to v0.4.8, but not identically. The Clink schema has changed significantly enough that there is no direct 1:1 translation. Calling clink.arg.register_parser repeatedly with the same command to merge parsers is not supported anymore.

-- Deprecated form:
local parser1 = clink.arg.new_parser("abc", "def")
local parser2 = clink.arg.new_parser("ghi", "jkl")
clink.arg.register_parser("foo", parser1)
clink.arg.register_parser("foo", parser2)

-- Replace with new form:
clink.argmatcher("foo"):addarg(parser1, parser2)

-- Warning:  Note that the following are NOT the same as above!
-- This replaces parser1 with parser2:
clink.argmatcher("foo"):addarg(parser1)
clink.argmatcher("foo"):addarg(parser2)
-- This uses only parser2 if/when parser1 finishes parsing args:
clink.argmatcher("foo"):addarg(parser1):addarg(parser2)

( needle:string, candidate:string ) : boolean

Deprecated; don't use this. See clink.generator for more information.

This returns true if needle is a prefix of candidate with a case insensitive comparison.

Normally in Clink v1.x and higher the needle will be an empty string because the generators are no longer responsible for filtering matches. The match pipeline itself handles that internally now.


function variable

Deprecated; don't use this. See builder:addmatch for more information.

This is no longer used.

clink.match_display_filter = function(matches)
  -- Transform matches.
  return matches
end

function variable

Deprecated; don't use this. See clink.ondisplaymatches for more information.

A match generator can set this varible to a filter function that is called before displaying matches. It is reset every time match generation is invoked. The filter function receives table argument containing the matches that are going to be displayed, and it returns a table filtered as required by the match generator.

See Filtering the Match Display for more information.


( [files:boolean] ) : nil

Deprecated; don't use this. See builder:addmatch for more information.

This is no longer needed, because now it's inferred from the match type when adding matches.


( filter_func:function, [priority:integer] ) : table

Deprecated; don't use this. See clink.promptfilter for more information.

Registers a prompt filter function. The capabilities are the same as before but the syntax is changed.

-- Deprecated form:
function foo_prompt()
  clink.prompt.value = "FOO "..clink.prompt.value.." >>"
  --return true  -- Returning true stops further filtering.
end
clink.prompt.register_filter(foo_prompt, 10)

-- Replace with new form:
local foo_prompt = clink.promptfilter(10)
function foo_prompt:filter(prompt)
  return "FOO "..prompt.." >>" --,false  -- Adding ,false stops further filtering.
end

( func:function, priority:integer ) : nil

Deprecated; don't use this. See clink.generator for more information.

Registers a generator function for producing matches. This behaves similarly to v0.4.8, but not identically. The Clink schema has changed significantly enough that there is no direct 1:1 translation; generators are called at a different time than before and have access to more information than before.

-- Deprecated form:
local function match_generator_func(text, first, last)
  -- `text` is the word text.
  -- `first` is the index of the beginning of the end word.
  -- `last` is the index of the end of the end word.
  -- `clink.add_match()` is used to add matches.
  -- return true if handled, or false to let another generator try.
end
clink.register_match_generator(match_generator_func, 10)

-- Replace with new form:
local g = clink.generator(10)
function g:generate(line_state, match_builder)
  -- `line_state` is a line object.
  -- `match_builder:addmatch()` is used to add matches.
  -- return true if handled, or false to let another generator try.
end

( type:integer ) : nil

Deprecated; don't use this. See clink.translateslashes for more information.

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


( path:string ) : boolean

Deprecated; don't use this. See os.globfiles for more information.

Returns whether path has the hidden attribute set.


table variable

Deprecated; don't use this. See line for more information.

This is an obsolete global variable that was set while running match generators. It has been superseded by the line type parameter passed into match generator functions when using the new clink.generator API.


Changes

v1.2.24

v1.2.23

v1.2.22

v1.2.21

v1.2.20

v1.2.19

v1.2.18

v1.2.17

v1.2.16

v1.2.15

v1.2.14

v1.2.13

v1.2.12

v1.2.11

v1.2.10

v1.2.9

v1.2.8

v1.2.7

v1.2.6

v1.2.5

v1.2.4

v1.2.3

v1.2.2

v1.2.1

v1.2

v1.1.49

v1.1.48

v1.1.47

v1.1.46

v1.1.45

v1.1.44

v1.1.43

v1.1.42

v1.1.41

v1.1.40

v1.1.39

v1.1.38

v1.1.37

v1.1.36

v1.1.35

v1.1.34

v1.1.33

v1.1.32

v1.1.31

v1.1.30

v1.1.29

v1.1.28

v1.1.27

v1.1.26

v1.1.25

v1.1.24

v1.1.23

v1.1.22

v1.1.21

v1.1.20

v1.1.19

v1.1.18

v1.1.17

v1.1.16

v1.1.15

v1.1.14

v1.1.13

v1.1.12

v1.1.11

v1.1.10

v1.1.9

v1.1.8

v1.1.7

v1.1.6

v1.1.5

v1.1.4

v1.1.3-alpha

v1.1.2-alpha

v1.1.1-alpha

v1.0.0a1 (alpha test release)

v1.0.0a0 (alpha test release)

v0.4.9

v0.4.8

v0.4.7

v0.4.6

v0.4.5

v0.4.4

v0.4.3

v0.4.2

v0.4.1

v0.4

v0.3

v0.2.1

v0.2

v0.1.1

v0.1

License

Clink is distributed under the terms of the GNU General Public License, version 3.

Credits

Clink

Clink was originally built by Martin Ridgers (https://github.com/mridgers/clink).
Copyright (c) 2012-2018 by Martin Ridgers.

Clink has been forked and renovated by Christopher Antos (https://github.com/chrisant996/clink).
Portions Copyright (c) 2020-2021 by Christopher Antos.

Libraries

GNU Readline library version 8.1 (https://tiswww.case.edu/php/chet/readline/rltop.html).
GNU Readline is distributed under the terms of the GNU General Public License, version 3.

Lua 5.2 (https://www.lua.org).

getopt library.
Copyright (c) 1997 Gregory Pietsch, placed in the public domain.

Detours library version 4.0.1 (https://github.com/microsoft/detours).
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT license.

Clink documentation embeds the highlight.js library (https://highlightjs.org).
Highlight.js is released under the BSD License.