X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=Utils%2FTermlib%2FEditor.hh;h=bacfeff1bdd54aeb3e8609ac4b1ab028abf029a5;hb=81f84badf27b66dbadec9890646ca1193e998505;hp=e5cb475cdc75745ea4358adc7144b74bc265fa25;hpb=27d5a4aaebd8abb6c6bb842af3c170063b206f0f;p=senf.git diff --git a/Utils/Termlib/Editor.hh b/Utils/Termlib/Editor.hh index e5cb475..bacfeff 100644 --- a/Utils/Termlib/Editor.hh +++ b/Utils/Termlib/Editor.hh @@ -28,6 +28,8 @@ // Custom includes #include +#include +#include #include #include #include "AbstractTerminal.hh" @@ -39,6 +41,22 @@ namespace senf { namespace term { + /** \brief Provide editor support terminal functionality + + This base class utilizes an arbitrary AbstractTerminal and provides terminfo based + terminal navigation and key parsing. + + All navigation is relative to the current line. The display area can then be extended + downwards up to a maximum of height() lines. Resetting this extended display area will + return to a one line area containing only the top line. + + All navigation is restricted to a width() x height() area. The last column may not be + written to since auto-margin terminals will move the cursor to the next line when writing to + that column. + + This base class calls v_keyReceived() which must be defined in a derived class whenever a + key is fully parsed. + */ class BaseEditor : public AbstractTerminal::Callbacks { @@ -52,21 +70,31 @@ namespace term { void newline(); ///< Move to beginning of a new, empty line void toColumn(unsigned c); ///< Move cursor to column \p c void put(char ch); ///< Write \p ch at current column - void put(std::string const & text); + void put(std::string const & text); ///< Write \a text starting at current column void clearLine(); ///< Clear current line and move cursor to first column void setBold(); ///< Set bold char display void setNormal(); ///< Set normal char display + void maybeClrScr(); ///< Clear screen if possible + + void toLine(unsigned l); ///< Move to relative display line \a l + void reset(); ///< Reset display area to single line unsigned currentColumn() const; ///< Return number of current column - unsigned width(); + unsigned currentLine() const; ///< Return number of current relative line + unsigned width(); ///< Return current screen width + unsigned height(); ///< Return current screen height protected: - virtual bool cb_init(); + virtual bool cb_init(); ///< Called when terminal is initialized virtual void cb_windowSizeChanged(); + ///< Called whenever the terminal window size changes +#ifndef DOXYGEN private: - virtual void v_keyReceived(keycode_t key) = 0; +#endif + virtual void v_keyReceived(keycode_t key) = 0; ///< Called whenever a key is received + private: virtual void cb_charReceived(char c); void keySequenceTimeout(); @@ -82,8 +110,91 @@ namespace term { ClockService::clock_type keyTimeout_; scheduler::TimerEvent timer_; unsigned column_; + unsigned displayHeight_; + unsigned line_; }; + + /** \brief Single line interactive text editor + + LineEditor implements a single-line input widget on an arbitrary AbstractTerminal. + + \li It supports all the customary editing functions + \li It is possible to arbitrarily assign functions to keys via a key map + \li LineEditor has builtin TAB completion support + \li The LineEditor has a built-in history + \li The LineEditor supports an arbitrary auxiliary display area below the input line + \li The LineEditor has hide() / show() support to allow editing to be temporarily + interrupted. + + The LineEditor will query the user for an input line. When the user accepts a line, + LineEditor will call a user callback function. After the callback has been called, the + editor is disabled. To accept a new input line, call show(). + + \section editor_keymap The editor key map + + Keys are defined in the keymap using defineKey(). The default bindings are: + + + + + + + + + + + + + + + + + +
\c Return bindings::accept
\c Right bindings::forwardChar
\c Left bindings::backwardChar
\c Up bindings::prevHistory
\c Down bindings::nextHistory
\c Backspace bindings::backwardDeleteChar
\c Delete bindings::deleteChar
\c Home bindings::beginningOfLine
\c End bindings::endOfLine
\c Ctrl-K bindings::deleteToEndOfLine
\c Ctrl-A bindings::beginningOfLine
\c Ctrl-E bindings::endOfLine
\c Ctrl-D bindings::deleteChar
\c Ctrl-C bindings::restartEdit
\c Ctrl-L bindings::clearScreen
+ + See the senf::term::bindings namespace for a list of all default provided key binding + functions. + + + \section editor_complete Completion suppoprt + + Completion support is provided by senf::term::bindings::complete(). To use the completer, + you need to implement a completion function and pass it as second argument to + bindings::complete(): + + \code + void myCompleter(senf::term::LineEditor & editor, unsigned b, unsigned e, + std::vector & completions) + { + // Get text to complete + std::string text (editor.text().substr(b, e-b)); + + // Return possible completions in 'completions' array + completions.push_back( ... ); + } + + senf::term::LineEditor editor (...); + editor.defineKey(senf::term::KeyParser::TAB, + boost::bind(&senf::term::bindings::complete, _1, &myCompleter)); + \endcode + + When \c myCompleter is a class member, use senf::membind() and pass this instead of \c + &myCompleter to \c boost::bind() and thus to senf::term::bindings::complete. + + + \section editor_auxarea The aux display area + + The auxiliary display area is accessed using auxDisplay() and clearAuxDisplay(). The aux + display area is \e cleared \e before each new key is processed. Therefore it is only + temporary. The aux display area however will survive hide() / show(). + + + \section editor_hideshow Temporarily disabling the editor + Calling hide() will temporarily disable the editor. All editor display will be + removed. Calling show() will redisplay the editor in it's current state including the aux + display area. + */ class LineEditor : public BaseEditor { @@ -91,44 +202,95 @@ namespace term { /////////////////////////////////////////////////////////////////////////// // Types - typedef boost::function KeyBinding; + typedef boost::function KeyBinding; ///< Type of a key binding function typedef boost::function AcceptCallback; + ///< Callback function type + + static unsigned const MAX_HISTORY_SIZE = 1024u; /////////////////////////////////////////////////////////////////////////// LineEditor(AbstractTerminal & terminal, AcceptCallback cb); + ///< Create a LineEditor + /**< \parm[in] terminal abstract terminal interface + \parm[in] cb callback to call for complete input + line */ /////////////////////////////////////////////////////////////////////////// - void prompt(std::string const & text); + ///\name Overall edit control + ///\{ + + void show(); ///< Enable editor widget + void hide(); ///< Disable editor widget + void accept(); ///< Accept current user input and call the accept callback + void clear(); ///< Clear editor buffer + void redisplay(); ///< Mark the editor buffer for redisplay + void forceRedisplay(); ///< Redisplay the editor buffer \e now + void prompt(std::string const & text); ///< Set prompt string + + ///\} + + ///\name Cursor and display movement + ///\{ + + void gotoChar(unsigned n); ///< Move cursor to position \a n + void scrollTo(unsigned n); ///< Move positon \n to beginning of display line + + ///\} + + ///\name Text manipulation + ///\{ + + void deleteChar(unsigned n=1); ///< Delete \a n characters at point + void insert(char ch); ///< Insert \a ch at point + void insert(std::string const & text); ///< Insert \a text at point void set(std::string const & text, unsigned pos = 0u); + ///< Set edit buffer contents + /**< The edit buffer contents will be replaced by \a + text. The cursor will be placed at position \a pos + within this text. */ + + ///\} + + ///\name History + ///\{ + + void pushHistory(std::string const & text, bool accept = false); + ///< Add string \a text to history + void prevHistory(); ///< Switch to previous history entry + void nextHistory(); ///< Switch to next history entry + + ///\} + + ///\name Aux Display + ///\{ + + void auxDisplay(unsigned line, std::string const & text); + ///< Display \a text on aux display line \a lilne + unsigned maxAuxDisplayHeight(); ///< Get maximum height of the aux display area + void clearAuxDisplay(); ///< Clear the aux display area + + ///\} + + ///\name Get information + ///\{ + + std::string const & text(); ///< Get current editor buffer contents + unsigned point(); ///< Get current cursor position + unsigned displayPos(); ///< Get current display position + keycode_t lastKey(); ///< Get last key code received + + ///\} + + ///\name Key bindings + ///\{ - // Overall edit control - void show(); - void hide(); - void accept(); - void clear(); - void redisplay(); - void forceRedisplay(); - - // Cursor and display movement - void gotoChar(unsigned n); - void scrollTo(unsigned n); - - // Text manipulation - void deleteChar(unsigned n=1); - void insert(char ch); - void insert(std::string const & text); - - // Get information - std::string const & text(); - unsigned point(); - unsigned displayPos(); - keycode_t lastKey(); - - // Key bindings void defineKey(keycode_t key, KeyBinding binding); - void unsetKey(keycode_t key); + ///< Bind key \a key to \a binding + void unsetKey(keycode_t key); ///< Remove all bindings for \a key + + ///\} private: virtual bool cb_init(); @@ -136,6 +298,8 @@ namespace term { virtual void v_keyReceived(keycode_t key); typedef std::map KeyMap; + typedef std::vector History; + typedef std::vector AuxDisplay; bool enabled_; bool redisplayNeeded_; @@ -148,20 +312,38 @@ namespace term { keycode_t lastKey_; AcceptCallback callback_; KeyMap bindings_; + History history_; + unsigned historyPoint_; + AuxDisplay auxDisplay_; }; +/** \brief LineEditor key bindings + */ namespace bindings { - void selfInsertCommand (LineEditor & editor); - void forwardChar (LineEditor & editor); - void backwardChar (LineEditor & editor); - void accept (LineEditor & editor); - void backwardDeleteChar (LineEditor & editor); - void deleteChar (LineEditor & editor); - void beginningOfLine (LineEditor & editor); - void endOfLine (LineEditor & editor); - void deleteToEndOfLine (LineEditor & editor); - void restartEdit (LineEditor & editor); + void selfInsertCommand (LineEditor & editor); ///< Insert key as literal character + void forwardChar (LineEditor & editor); ///< Move one char forward + void backwardChar (LineEditor & editor); ///< Move one char backwards + void accept (LineEditor & editor); ///< Accept input line + void backwardDeleteChar (LineEditor & editor); ///< Delete char before cursor + void deleteChar (LineEditor & editor); ///< Delete char at cursor + void beginningOfLine (LineEditor & editor); ///< Move to beginning of line + void endOfLine (LineEditor & editor); ///< Move to end of line + void deleteToEndOfLine (LineEditor & editor); ///< Delete from cursor to end of line + void restartEdit (LineEditor & editor); ///< Clear edit buffer and restart edit + void prevHistory (LineEditor & editor); ///< Move to previous history entry + void nextHistory (LineEditor & editor); ///< Move to next history entry + void clearScreen (LineEditor & editor); ///< Clear screen and redisplay editor + + typedef boost::function &)> Completer; + void complete (LineEditor & editor, Completer completer); + ///< Complete text at cursor + /**< This function calls \a completer to find the list of + possible completions for the text between \a b and \a e + (as passed to the completer). The completer must add + all possible completions to the completions vector. + + \see \ref editor_complete */ }