boost::bind(&term::bindings::complete,
_1,
senf::membind(&LineEditorClientReader::completePath, this)));
+ editor_.defineKey(senf::term::KeyParser::Return, &senf::term::bindings::acceptWithRepeat);
}
prefix_ void senf::console::detail::LineEditorClientReader::v_setupFailed()
}
prefix_ void senf::console::detail::LineEditorClientReader::
-completePath(term::LineEditor & editor, unsigned b, unsigned e,
+completePath(term::LineEditor & editor, unsigned & b, unsigned & e, std::string & prefix,
std::vector<std::string> & completions)
{
- std::string base (editor.text().substr(b,e));
+ std::string const & t (editor.text());
+ // Search backward from e finding the longest valid path. This does *not* accept all valid
+ // path's, only those without empedded white-space. However, this is only for completion so
+ // it's ok.
+ if (b<e) {
+ unsigned bb (e-1);
+ for (;;) {
+ if (! CommandParser::isWordChar(t[bb]) && t[bb] != '/') {
+ ++bb;
+ break;
+ }
+ if (bb == b)
+ break;
+ --bb;
+ }
+ b = bb;
+ }
+ std::string base (t.substr(b,e));
CommandParser parser;
ParseCommandInfo cmd;
try {
}
DirectoryNode::ChildrenRange cs (dir->completions(i->value()));
+ prefix = basePath;
for (DirectoryNode::ChildrenRange::iterator j (cs.begin()); j != cs.end(); ++j)
- completions.push_back(basePath + j->first
- + (j->second->followLink().isDirectory() ? "/" : " "));
+ completions.push_back(j->first + (j->second->followLink().isDirectory() ? "/" : " "));
}
///////////////////////////////cc.e////////////////////////////////////////
// Editor callbacks
void executeLine(std::string const & text);
void deleteCharOrExit(term::LineEditor & editor);
- void completePath(term::LineEditor & editor, unsigned b, unsigned e,
- std::vector<std::string> & completions);
+ void completePath(term::LineEditor & editor, unsigned & b, unsigned & e,
+ std::string & prefix, std::vector<std::string> & completions);
term::LineEditor editor_;
LineEditorSwitcher * switcher_;
+ std::string default_;
};
}}}
prefix_ std::string::size_type senf::console::Client::handleInput(std::string data,
bool incremental)
{
- if (data.empty() && ! incremental) {
- data = lastCommand_;
- stream() << "repeat: " << data << std::endl;
- } else
- lastCommand_ = data;
-
std::string::size_type n (data.size());
try {
CommandParser parser_;
Executor executor_;
std::string name_;
- std::string lastCommand_;
boost::scoped_ptr<detail::ClientReader> reader_;
Server::Mode mode_;
std::string backtrace_;
editor.accept();
}
+prefix_ void senf::term::bindings::acceptWithRepeat(LineEditor & editor)
+{
+ if (editor.text().empty()) {
+ editor.prevHistory();
+ editor.forceRedisplay();
+ }
+ editor.accept();
+}
+
prefix_ void senf::term::bindings::backwardDeleteChar(LineEditor & editor)
{
unsigned p (editor.point());
{
typedef std::vector<std::string> Completions;
+ std::string text (editor.text());
Completions completions;
- completer(editor, 0, editor.point(), completions);
+ unsigned b (0);
+ unsigned e (editor.point());
+ std::string prefix;
+ completer(editor, b, e, prefix, completions);
if (completions.empty())
return;
+ if (e > text.size())
+ e = text.size();
+ if (b > e)
+ b = e;
// Find common start string of all completions
unsigned commonStart (completions[0].size());
}
// Replace to-be-completed string with the common start string shared by all completions
- std::string text (editor.text());
- std::string completion (completions[0].substr(0, commonStart));
+ std::string completion (prefix+completions[0].substr(0, commonStart));
bool didComplete (false);
- if (text.substr(0, editor.point()) != completion) {
- text.erase(0, editor.point());
- text.insert(0, completion);
+ if (text.substr(b, e) != completion) {
+ text.erase(b, e);
+ text.insert(b, completion);
didComplete = true;
}
// Otherwise place cursor directly after the (possibly partial) completion
- editor.set(text, commonStart);
+ editor.set(text, b+prefix.size()+commonStart);
if (didComplete || completions.size() == 1)
return;
Completions::iterator i (completions.begin());
for (unsigned row (0); row < nRows; ++row) {
std::string line;
- for (unsigned column (0); column < nColumns && i != completions.end(); ++column) {
+ for (unsigned column (0); column < nColumns && i != completions.end(); ++column, ++i) {
std::string entry (colWidth, ' ');
- if (i->size() > colWidth-2)
- std::copy(i->begin(), i->begin()+colWidth-2, entry.begin());
- else
- std::copy(i->begin(), i->end(), entry.begin());
+ std::copy(i->begin(),
+ i->size() > colWidth-2 ? i->begin()+colWidth-2 : i->end(),
+ entry.begin());
line += entry;
- ++i;
}
editor.auxDisplay(row, line);
}
bindings::complete():
\code
- void myCompleter(senf::term::LineEditor & editor, unsigned b, unsigned e,
- std::vector<std::string> & completions)
+ void myCompleter(senf::term::LineEditor & editor, unsigned & b, unsigned & e,
+ std::string & prefix, std::vector<std::string> & completions)
{
// Get text to complete
std::string text (editor.text().substr(b, e-b));
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.
+
+ The completion protocol is as follows: When completion is desired, the completer function is
+ called. \a b and \a e are set to 0 and <tt>editor.point()</tt> respectively. \a prefix and
+ \a completions are empty.
+
+ \li the completer may restrict the to-be-completed string to any subrange by changing \a b
+ and \a e accordingly.
+ \li If there is an initial substring which applies to \e all completions but should not be
+ listed in the list of completions, assign this value to \a prefix.
+ \li Add all possible completions to the \a completions vector not including the \a prefix.
+ \li The completion result is taken from the size of the \a completions vector \e only: If
+ this vector is empty, completion failed (even if \a prefix is set), a single entry in \a
+ completions (even if it is the empty string) signals a unique completion.
\section editor_auxarea The aux display area
void forwardChar (LineEditor & editor); ///< Move one char forward
void backwardChar (LineEditor & editor); ///< Move one char backwards
void accept (LineEditor & editor); ///< Accept input line
+ void acceptWithRepeat (LineEditor & editor); ///< Accept, possibly repeat last history entry
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 nextHistory (LineEditor & editor); ///< Move to next history entry
void clearScreen (LineEditor & editor); ///< Clear screen and redisplay editor
- typedef boost::function<void (LineEditor &, unsigned b, unsigned e, std::vector<std::string> &)> Completer;
+ typedef boost::function<void (LineEditor &, unsigned & b, unsigned & e,
+ std::string & prefix, std::vector<std::string> &)> 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.
+ all possible completions to the \a completions vector.
\see \ref editor_complete */