Utils/Termlib: Extend the completion API
g0dil [Tue, 16 Jun 2009 14:35:33 +0000 (14:35 +0000)]
Utils/Console: Update completion to use new prefix return value
Utils/Console: Update completion to allow completing command arguments (i.e. help, ls, cd)
Utils/Console: Move auto-repeat-on-empty-return functionality into LineEditor keybinding
Utils/Termlib: Update completer documentation

git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@1234 270642c3-0616-0410-b53a-bc976706d245

Utils/Console/LineEditor.cc
Utils/Console/LineEditor.hh
Utils/Console/Server.cc
Utils/Console/Server.hh
Utils/Termlib/Editor.cc
Utils/Termlib/Editor.hh

index df9d065..512ab6f 100644 (file)
@@ -87,6 +87,7 @@ LineEditorClientReader(Client & client, LineEditorSwitcher & switcher)
                       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()
@@ -140,10 +141,27 @@ senf::console::detail::LineEditorClientReader::deleteCharOrExit(term::LineEditor
 }
 
 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 {
@@ -204,9 +222,9 @@ completePath(term::LineEditor & editor, unsigned b, unsigned e,
         }
 
     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////////////////////////////////////////
index 6fe3ea3..0360cd9 100644 (file)
@@ -89,11 +89,12 @@ namespace detail {
         // 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_;
     };
 
 }}}
index 913a271..66564bb 100644 (file)
@@ -284,12 +284,6 @@ prefix_ void senf::console::Client::setNoninteractive()
 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 {
index 9e9471a..53aa969 100644 (file)
@@ -234,7 +234,6 @@ namespace console {
         CommandParser parser_;
         Executor executor_;
         std::string name_;
-        std::string lastCommand_;
         boost::scoped_ptr<detail::ClientReader> reader_;
         Server::Mode mode_;
         std::string backtrace_;
index 0122904..cceb4d8 100644 (file)
@@ -582,6 +582,15 @@ prefix_ void senf::term::bindings::accept(LineEditor & editor)
     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());
@@ -639,10 +648,18 @@ prefix_ void senf::term::bindings::complete(LineEditor & editor, Completer compl
 {
     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());
@@ -657,17 +674,16 @@ prefix_ void senf::term::bindings::complete(LineEditor & editor, Completer compl
     }
 
     // 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;
 
@@ -683,14 +699,12 @@ prefix_ void senf::term::bindings::complete(LineEditor & editor, Completer compl
     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);
     }
index 952291f..d7cadde 100644 (file)
@@ -163,8 +163,8 @@ namespace term {
         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));
@@ -180,6 +180,19 @@ namespace term {
 
         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
@@ -325,6 +338,7 @@ namespace bindings {
     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
@@ -335,13 +349,14 @@ namespace bindings {
     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 */