Class ui.Editbox

An editbox is a multi-line input widget.

It is the widget you interact with when you use MC’s editor, but you may also embed it in your own dialogs.

Modifying

ui.Editbox:delete(count[, backwards]) Deletes text at the cursor location.
ui.Editbox:insert(s) Inserts a string into the buffer.
ui.Editbox:load(filepath, [line_number]) Loads a file into the buffer.

Reading

ui.Editbox.current_char r The character on which the cursor stands.
ui.Editbox.current_word r The word on which the cursor stands.
ui.Editbox:get_line([num[, keep_eol]]) Fetches a line.
ui.Editbox:lines() Lines iterator.
ui.Editbox:sub(i [, j]) Extracts a substring.

Meta

ui.Editbox.filename r The filename associated with the buffer.
ui.Editbox.fullscreen rw Whether the editbox is shown fullscreen.
ui.Editbox:get_markers() Returns the extents of the selected text.
ui.Editbox:len() Returns the size of the buffer.
ui.Editbox.max_line r The number of lines in the buffer.
ui.Editbox.modified rw Whether the buffer has been modified.
ui.Editbox.top_line rw The number of the first line displayed.

Bookmarks

ui.Editbox:bookmark_clear(line, style) Clears a bookmark.
ui.Editbox:bookmark_exists(line, style) Queries for a bookmark.
ui.Editbox:bookmark_flush([style]) Clears all bookmarks.
ui.Editbox:bookmark_set(line, style) Sets a bookmark.

Cursor

ui.Editbox.cursor_col rw The cursor’s column.
ui.Editbox.cursor_line rw The cursor’s line number.
ui.Editbox.cursor_offs rw The cursor’s offset within the buffer.
ui.Editbox.cursor_xoffs rw The cursor’s offset within the line.

Syntax

ui.Editbox:add_keyword(s, style[, opts]) Syntax-highlights a string.
ui.Editbox:get_style_at(pos) Returns the style at a certain position.
ui.Editbox.syntax rw The buffer’s syntax type.

i18n

ui.Editbox:is_utf8() Whether the buffer is UTF-8 encoded.
ui.Editbox:to_tty(s) Converts a string, extracted from the buffer, to the terminal’s encoding.

Static functions

ui.Editbox.options Editor options.

Static functions (syntax)

ui.Editbox.get_syntax_list() Returns a list of all the recognized syntaxes.
ui.Editbox.search_syntax(keyword) Searches within the syntax list.

Events

<<load>> Triggered when an editbox is opened.
<<unload>> Triggered when an editbox is closed.


Modifying

ui.Editbox:delete(count[, backwards])

Deletes text at the cursor location.

As an example, here’s how to delete the current word (with the help of current_word):

-- Various ways to delete a word.

ui.Editbox.bind('f16', function(edt)
  local whole, part = edt:get_current_word()
  whole = whole or abort "stand on a word, will ya?"
  edt:delete(part:len(), true)
  edt:delete(whole:len() - part:len())
end)

ui.Editbox.bind('f16', function(edt)
  local whole, part = edt:get_current_word()
  whole = whole or abort "stand on a word, will ya?"
  edt.cursor_offs = edt.cursor_offs - part:len()
  edt:delete(whole:len())
end)

-- For completeness sake, here's how to do it using commands. But this
-- doesn't behave exactly as the above solutions when standing on
-- the beginning/end of the word.

ui.Editbox.bind('f16', function(edt)
  edt:command "DeleteToWordBegin"
  edt:command "DeleteToWordEnd"
end)

Parameters:

  • count How many bytes to delete.
  • backwards Boolean. Whether to “backspace” instead of delete. (optional)
ui.Editbox:insert(s)

Inserts a string into the buffer.

Inserts text at the cursor location (the cursor then moves forward).

-- Insert the current date and time.
ui.Editbox.bind("C-y", function(edt)
  edt:insert(os.date("%Y-%m-%d %H:%M:%S"))
end)

Parameters:

  • s text to insert (may contain null bytes).
ui.Editbox:load(filepath, [line_number])
Loads a file into the buffer.

(The file does not have to exist.)

Returns:

    true unless an error occurred (anything that causes an error message to be shown).

Reading

ui.Editbox.current_char r
The character on which the cursor stands.

When called as a method, returns two values.

Returns:

  1. The character, as a string.
  2. The character’s numeric code (Unicode, in case of a UTF-8 encoded buffer).
ui.Editbox.current_word r
The word on which the cursor stands.

This is simply a convenience wrapper around utils.text.extract_word (see there for explanation), implemented thus:

function ui.Editbox.meta:get_current_word()
  return utils.text.extract_word(
    self.line, self.cursor_xoffs
  )
end

You may invoke it as a property:

ui.Editbox.bind('C-y', function(edt)
  alert(edt.current_word)
end)

Or you may use the full method syntax (“:get_current_word()”) to also get the portion of the word preceding the cursor:

ui.Editbox.bind('C-y', function(edt)
  devel.view{edt:get_current_word()}
end)

See usage examples at delete.

As a more elaborate example, here’s a very simple implementation of “word completion” for the editor:

--[[

Word-completion for the editor.

Stand on a word and hit C-y. You'll be shown a list of
all the words in the buffer sharing that prefix.

]]

ui.Editbox.bind('C-y', function(edt)
  local whole, part = edt:get_current_word()

  if not whole      -- cursor is not on a word.
      or part == "" -- cursor is on start of a word.
  then
    abort(T"Please stand on a word (past its first letter).")
  end

  local words = utils.table.List {}
  for word in edt:sub(1):p_gmatch('\\b' .. part .. '[\\w_]+') do
    words[word] = true
  end

  words = words:keys():sort()

  if #words ~= 0 then

    local lbox = ui.Listbox{items=words}

    if ui.Dialog{compact=true}:add(lbox):popup(lbox) then
      local word = lbox.value
      edt:insert(word:sub(part:len()+1)) -- We need just the tail of the word.
    end

  else
    tty.beep()   -- no completions found.
  end

end)

To keep this “Word Completion” snippet short, no proper i18n handling is done here. See complete_word.lua for a version with proper i18n handling.

ui.Editbox:get_line([num[, keep_eol]])
Fetches a line.

There are three ways to fetch the line on which the cursor stands:

edt:get_line(edt.cursor_line)
edt:get_line()
edt.line

(The third way is syntactic sugar for the second way.)

Parameters:

  • num The line number. Defaults to the cursor’s line. (optional)
  • keep_eol Boolean. Whether to keep the EOL at the end. (optional)
ui.Editbox:lines()

Lines iterator.

Iterates over the lines. Returns the line and its number.

-- Highlight all the lines containing "Linux".
ui.Editbox.bind('C-y', function(edt)
  for line, i in edt:lines() do
    if line:find('Linux') then
      edt:bookmark_set(i, tty.style('editor.bookmarkfound'))
    end
  end
end)
ui.Editbox:sub(i [, j])
Extracts a substring.

Extracts a substring from the buffer. The arguments are the same as string.sub’s (negative indices have the same semantics). The indexing is byte-oriented (not character-oriented).

see len.

Meta

ui.Editbox.filename r
The filename associated with the buffer.

Returns nil if no filename is associated with the buffer (this could happen for example, when you call up the editor with shift-F4).

ui.Editbox.fullscreen rw

Whether the editbox is shown fullscreen.

An editbox normally fills the whole client area of the editor (“fullscreen”), but it may alternatively be shown inside a resizeable framed box within.

The editor —though not many users are aware of this— is an MDI application. It may contain more than one editbox. You can see this clearly if you use the Window / Toggle fullscreen command from the menu.

Example:

-- Make the current editbox occupy the western half of the screen.
ui.Editbox.bind("C-l w", function(edt)

  -- (1) Show it in its own box:
  edt.fullscreen = false

  -- (2) Set the dimensions to be half of the editor's ("the editor"
  -- simply being the dialog; but we can instead just use
  -- tty.get_cols() and tty.get_rows()).
  --
  -- Note that doing 'edt.fullscreen = false' restores the editbox'
  -- dimensions to previously recorded ones, so we have to set the
  -- dimensions _afterwards_ or they'll get overwritten.
  --
  edt.x, edt.y = 0, 1
  edt.cols = math.ceil(edt.dialog.cols / 2)
  edt.rows = edt.dialog.rows - 2

  -- (3) We need to redraw the entire dialog because some areas in
  -- it now contain junk (where the editbox has been).
  tty.redraw()  -- We can do 'edt.dialog:redraw()' too.

end)
ui.Editbox:get_markers()

Returns the extents of the selected text.

When text is selected (aka “marked”) within the buffer, it is identified by its starting marker and ending marker. “Marker” being an offset, in bytes, within the buffer.

This function returns the two markers. If no text is selected, nothing is returned.

It so happens that you can pass the two returned values directly to sub, which is why an Editbox:get_selection() method isn’t necessary:

-- Show the marked text
if edt:get_markers() then
  alert(edt:sub(edt:get_markers()))
  -- To be proper, we should use :to_tty before sending the text to alert().
end
ui.Editbox:len()
Returns the size of the buffer.

That is, returns the number of bytes that compose the text.

To make the API similar to that of Lua’s strings, this is a method, not a property. That is, you do edt:len(), not edt.len.

ui.Editbox.max_line r
The number of lines in the buffer.

This is also the number of the last line, because line numbers are 1-based. Hence the name.

ui.Editbox.modified rw
Whether the buffer has been modified.

This property is writable. You can use this when you want some modification to not “dirty” the buffer. Example:

local mod = edt.modified
-- ...
-- code to remove all Windows/DOS line ends (CRs) from the buffer
-- ...
edt.modified = mod
ui.Editbox.top_line rw
The number of the first line displayed.

Bookmarks

Bookmarks are markers that are set on lines.

A bookmark has a UI style (color, underline, etc.) that tells MC how to display it.

A single line may hold several bookmarks.

ui.Editbox:bookmark_clear(line, style)
Clears a bookmark.

Parameters:

  • line Line number.
  • style The style whose bookmark is to be cleared, or -1 to clear all bookmarks on this line.
ui.Editbox:bookmark_exists(line, style)
Queries for a bookmark.

Parameters:

  • line Line number.
  • style The style whose bookmark is to be looked for, or -1 to look for any bookmark.
ui.Editbox:bookmark_flush([style])
Clears all bookmarks.

Parameters:

  • style The style whose bookmarks are to be flushed. If omitted, or if -1, all styles are flushed. (optional)
ui.Editbox:bookmark_set(line, style)
Sets a bookmark.

-- Highlight lines 2 and 5 to show that we've found some
-- string there.
local found = tty.style("yellow, green")
edt:bookmark_set(2, found)
edt:bookmark_set(5, found)

-- Highlight line 3 to show that it has a typo.
local typo = tty.style("yellow, green")
edt:bookmark_set(3, typo)

(See another example at lines.)

The above code reveals a subtle issue: bookmarks have only a UI style, not an ID. We can’t later tell the editor to clear all the “typo” bookmarks but leave the others, because they're indistinguishable from the “found” bookmarks: the found and typo variables happen to hold exactly the same value in our case (they are one and the same UI style). Hopefully bookmarks will have an ID in future versions of MC.

MC’s skin defines two styles for bookmarks, which you can use if you wish. tty.style("editor.bookmark") is for bookmarks set explicitly by the user. tty.style("editor.bookmarkfound") is used for lines matching a search.

Parameters:

  • line Line number.
  • style The style to use for this bookmark.

Cursor

ui.Editbox.cursor_col rw
The cursor’s column.

This is where on the screen the cursor ends up. That is, the widths of TAB characters, wide Asian characters and non-spacing characters are taken in account.

It might be tempting to use this property, but often it’s the cursor_xoffs property that you should be using instead.

For example, the correct way to jump to the string “Linux” on the current line is:

ui.Editbox.bind("C-z", function(edt)
  local pos = edt.line:find "Linux"
  if pos then
    edt.cursor_xoffs = pos
  end
end)

the following, however, is a mistake:

ui.Editbox.bind("C-z", function(edt)
  local pos = edt.line:find "Linux"
  if pos then
    -- MISTAKE! Use cursor_xoffs instead.
    edt.cursor_col = pos
    -- To understand why it's a mistake, insert some TABs
    -- or UTF-8 characters before the string "Linux".
  end
end)

See also cursor_xoffs.

ui.Editbox.cursor_line rw
The cursor’s line number.

When you set the cursor’s line (by writing to this property), the view will be scrolled so the line is centered on screen. If you don’t want to center the line, call this property as a method and pass a “nocenter” argument. E.g.: edt:set_cursor_line(50, "nocenter").

ui.Editbox.cursor_offs rw
The cursor’s offset within the buffer.

(Byte-based, not character-based.)

Example:

-- Jump to the next place where "Linux" appears in the text.
ui.Editbox.bind("C-c", function(edt)
  local pos = edt:sub(1):find("Linux", edt.cursor_offs + 1)
  if pos then
    edt.cursor_offs = pos
  else
    tty.beep()
  end
end)

(For a useful variation of this code snippet, see search_by_regex.lua.)

ui.Editbox.cursor_xoffs rw
The cursor’s offset within the line.

(Byte-based, not character-based.)

See also cursor_col.

Syntax

ui.Editbox:add_keyword(s, style[, opts])
Syntax-highlights a string.

This adds a string (typically a keyword) to the syntax definition. This makes the string shown in a different style than normal text.

Example:

-- When you're editing source code you sometimes wish to see
-- all the places on the screen where a variable is used.
--
-- In Vim this is done with the * (asterisk) key. Here we use
-- alt-* instead.
--
ui.Editbox.bind('M-*', function(edt)
  abortive(edt.current_word, T'Stand on a word, will you?')
  edt:add_keyword(edt.current_word, tty.style('white, red'), {range='all'})
  edt:redraw()
end)

Another example:

-- Spellcheck the file.
-- Misspelled words will be highlighted.
ui.Editbox.bind('C-c', function(edt)
  local f = io.popen('spell < ' .. edt.filename .. ' | sort | uniq')
  for word in f:lines() do
    edt:add_keyword(word, tty.style('white, red'), {range='spellcheck'})
  end
  edt:redraw()
  f:close()
end)

(For better spellchecking, see editbox/speller.lua.)

Another example:

--[[

When you read novels you sometimes want people's names
highlighted.

Look no further :-)

With this script you can add "Actors:" lines to the first
lines of your novel's text to make that happen. Example:

   Actors: Benedict Brand Corwin Eric Julian Oberon (male)
   Actors: Deirdre Fiona Flora Llewella (female)

It's probably convenient to use this on 256 color terminals
only, where we can pick non-intrusive colors.

]]

ui.Editbox.bind('<<load>>', function(edt)

  local styles = {
    -- By specifying only the foreground color we get the default
    -- background color, which is usually (not always) the editor's
    -- background as well. You may, of course, explicitly specify
    -- the background here.
    male   = tty.style {color='yellow', hicolor='color159'}, -- Bluish
    female = tty.style {color='brown',  hicolor='color219'}, -- Pinkish
    object = tty.style {color='white',  hicolor='color186'}, -- Yellowish
    place  = tty.style {color='green',  hicolor='color120'}, -- Greenish
  }

  for line, i in edt:lines() do
    -- The following "[o]" is a trick to prevent this line from
    -- being recognized as an Actors line.
    local names, gender = line:match "Act[o]rs:(.*)%((.*)%)"
    if names then
      for name in names:gmatch "[^%s,]+" do
        edt:add_keyword(name, abortive(styles[gender], 'missing style ' .. gender), {range='all'})
      end
    end
    if i > 50 then  -- look in 50 first lines only.
      break
    end
  end

end)

You must redraw the editbox, by calling :redraw(), to see the effect on the text. For performance reasons this is not done automatically after each :add_keyword().

Of course, if you're doing your stuff in <<load>>, as in the example above, you don’t need to call :redraw() because the text is drawn afterwards in any case.

There’s currently no remove_keyword() method to cancel the effect of add_keyword().

To remove any keywords you added you can reset the syntax definition by doing:

edt.style = edt.style

Or, as a user, press C-s twice (the first time disables syntax highlighting; the second enables it again).

Parameters:

  • s The string to highlight.
  • style The style to highlight it in.
  • opts An optional table with additional options:

    whole (true by default): Whether the string must be whole (bounded by non-word characters) or not.

    range (“default” by default): One of “default”, “all”, “spellcheck”, “!spellcheck”. Syntaxes are composed of contexts; E.g., in a programming language the default context holds normal code, another context is for comments, another for strings, etc. The range option determines which context(s) the string will be added to. By default the string will be added to the default context only (which means that it won’t be recognized in comments and strings). You can specify “all” to add it to all contexts; “spellcheck” to add it to all contexts marked as being appropriate for spell checking; and “!spellcheck” for those not marked so.

    linestart (false by default): Whether the string must start at beginnings of lines.

ui.Editbox:get_style_at(pos)
Returns the style at a certain position.

See usage example at tty.destruct_style.

Parameters:

  • pos Position in buffer (1-based; byte-oriented).
ui.Editbox.syntax rw
The buffer’s syntax type.

E.g., “C Program”, “HTML File”, “Shell Script”. If no syntax is associated with the buffer, this property is nil.

Examples:

-- Treat "README" files as HTML files.
ui.Editbox.bind('<<load>>', function(edt)
  if edt.filename and edt.filename:find 'README' then
    edt.syntax = "HTML File"
  end
end)

-- Auto-detect HTML files.
-- It looks for a closing HTML tag in the first 1024 bytes.
ui.Editbox.bind('<<load>>', function(edt)
  if not edt.syntax then
    if edt:sub(1,1024):find '</' then
      edt.syntax = "HTML File"
    end
  end
end)

Unfortunately, this property is the descriptive name of the syntax (e.g., “Shell Script”), which may change among MC releases, rather than some fixed identifier (e.g., “shell”). This makes it problematic to hard-code such strings in your code. Hopefully this will be remedied in MC sometime.

i18n

ui.Editbox:is_utf8()
Whether the buffer is UTF-8 encoded.

For stylistic uniformity with tty.is_utf8() only, this was made a method, not a property.

ui.Editbox:to_tty(s)
Converts a string, extracted from the buffer, to the terminal’s encoding.

See example and discussion at Encodings.

Static functions

ui.Editbox.options

Editor options.

A table containing some editbox options, which you can get and set.

-- Show line numbers when editing C files.
ui.Editbox.bind("<<load>>", function(edt)
  if edt.syntax == "C Program" then
    ui.Editbox.options.show_numbers = true
  else
    ui.Editbox.options.show_numbers = false
  end
end)

Note that these are global options. Unfortunately, MC doesn’t store these values on each Editbox but in shared global variables. This means that you can’t have two Editboxes opened at once each having a different tab_size value.

Here are the available fields (options). In parentheses is how the feature is named in MC’s Editor Options dialog.

  • tab_size – The tab character width
  • fake_half_tab – (boolean) Simulate tabs at half the size.
  • expand_tab – (boolean) Emit spaces, instead of a tab, when the TAB key is pressed.
  • show_tabs – (boolean) Show tabs (“Visible tabs”).
  • show_tws – (boolean) Show trailing whitespace (“Visible trailing spaces”).
  • show_numbers – (boolean) Show line numbers.
  • wrap_column – The column for word-wrapping.
  • show_right_margin – (boolean) Show where wrap_column is (works even if wrapping is off). A useful feature found in many other editors.
  • save_position – (boolean) Save the file position when the editbox is closed.

Static functions (syntax)

ui.Editbox.get_syntax_list()

Returns a list of all the recognized syntaxes.

keymap.bind('C-y', function()
  devel.view {
    "Supported syntaxes:", ui.Editbox.syntax_list
  }
end)
ui.Editbox.search_syntax(keyword)
Searches within the syntax list.

As mentioned earlier, the syntax property is a human-readable pretty string instead of being a keyword. This utility function tries its best to find a syntax string that matches a keyword.

assert(ui.Editbox.search_syntax("bison") == "Yacc/Bison Parser")
assert(ui.Editbox.search_syntax("PERL") == "Perl Program")
assert(ui.Editbox.search_syntax("C#") == "C# Program")

It returns nil if it finds none.

Events

<<load>>
Triggered when an editbox is opened.

Example:

ui.Editbox.bind("<<load>>", function(edt)
  alert(edt.syntax)
end)

Another example:

-- When a user opens a *.log file, automatically jump to its
-- end and insert a date header.
ui.Editbox.bind('<<load>>', function(edt)
  if edt.filename and edt.filename:find '%.log$' then
    edt.cursor_offs = edt:len() + 1
    edt:insert("\n--------" .. os.date() .. "--------\n")
  end
end)

See more examples at ui.Editbox:add_keyword, ui.Editbox.syntax, ui.Editbox.options, modeline.lua.

The name of this event is borrowed from the JavaScript world (body.onload).

<<unload>>
Triggered when an editbox is closed.
generated by LDoc 1.4.3 Last updated 2016-08-23 17:29:40