Utils/Console: Add short help to 'ls' output
[senf.git] / Utils / Console / LineEditor.cc
index ab2f548..31df1f7 100644 (file)
@@ -28,6 +28,7 @@
 
 // Custom includes
 #include "../Logger/SenfLog.hh"
+#include "../../Utils/range.hh"
 
 //#include "LineEditor.mpp"
 #define prefix_
@@ -76,6 +77,10 @@ LineEditorClientReader(Client & client, LineEditorSwitcher & switcher)
     editor_.prompt(promptString());
     editor_.defineKey(senf::term::KeyParser::Ctrl('D'),
                       senf::membind(&LineEditorClientReader::deleteCharOrExit, this));
+    editor_.defineKey(senf::term::KeyParser::Tab,
+                      boost::bind(&term::bindings::complete,
+                                  _1, 
+                                  senf::membind(&LineEditorClientReader::completePath, this)));
 }
 
 prefix_ void senf::console::detail::LineEditorClientReader::v_setupFailed()
@@ -122,6 +127,76 @@ senf::console::detail::LineEditorClientReader::deleteCharOrExit(term::LineEditor
         term::bindings::deleteChar(editor);
 }
 
+prefix_ void senf::console::detail::LineEditorClientReader::
+completePath(term::LineEditor & editor, unsigned b, unsigned e,
+             std::vector<std::string> & completions)
+{
+    std::string base (editor.text().substr(b,e));
+    CommandParser parser;
+    ParseCommandInfo cmd;
+    try {
+        parser.parsePath(base, cmd);
+    }
+    catch (CommandParser::ParserErrorException & ex) {
+        return;
+    }
+    ParseCommandInfo::TokensRange path (cmd.commandPath());
+    if (path.empty()) {
+        DirectoryNode::ChildrenRange cs (client().cwd().children());
+        for (DirectoryNode::ChildrenRange::iterator i (cs.begin()); i != cs.end(); ++i)
+            completions.push_back(i->first + (i->second->followLink().isDirectory() ? "/" : " "));
+        return;
+    }
+    
+    ParseCommandInfo::TokensRange::const_iterator i (path.begin());
+    ParseCommandInfo::TokensRange::const_iterator const i_end (boost::prior(path.end()));
+    DirectoryNode * dir (& client().cwd());
+    std::string basePath;
+    for (; i != i_end; ++i)
+        if (*i == NoneToken()) {
+            if (i == path.begin()) {
+                dir = & client().root();
+                basePath = "/";
+            }
+        }
+        else if (*i == WordToken("..")) {
+            DirectoryNode * parent (dir->parent().get());
+            if (parent) dir = parent;
+            basePath += "../";
+        }
+        else if (*i == WordToken(".")) 
+            basePath += "./";
+        else {
+            if (dir->hasChild(i->value())) {
+                try {
+                    dir = & dir->getDirectory(i->value());
+                }
+                catch (std::bad_cast &) {
+                    return;
+                }
+                basePath += i->value() + "/";
+            } 
+            else {
+                DirectoryNode::ChildrenRange cs (dir->completions(i->value()));
+                if (has_one_elt(cs)) {
+                    GenericNode & node (cs.begin()->second->followLink());
+                    if (!node.isDirectory())
+                        return;
+                    dir = static_cast<DirectoryNode*>(&node);
+                    basePath += cs.begin()->first + "/";
+                }
+                else
+                    return;
+            }
+        }
+
+    DirectoryNode::ChildrenRange cs (dir->completions(i->value()));
+    for (DirectoryNode::ChildrenRange::iterator j (cs.begin()); j != cs.end(); ++j)
+        completions.push_back(basePath + j->first 
+                              + (j->second->followLink().isDirectory() ? "/" : " "));
+}
+
 ///////////////////////////////cc.e////////////////////////////////////////
 #undef prefix_
 //#include "LineEditor.mpp"