Console: Implement autocomplete
g0dil [Tue, 29 Apr 2008 10:33:17 +0000 (10:33 +0000)]
Console: Add autocomplete and autocd options to executor
Console: Add Server::stop member

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

Console/Executor.cc
Console/Executor.cci
Console/Executor.hh
Console/Node.cci
Console/Node.ct
Console/Node.hh
Console/Node.test.cc
Console/Server.cc
Console/Server.cci
Console/Server.hh

index da8aa49..a7db590 100644 (file)
@@ -60,8 +60,11 @@ prefix_ void senf::console::Executor::execute(std::ostream & output,
             GenericNode & node ( traverseCommand(command.commandPath()) );
             DirectoryNode * dir ( dynamic_cast<DirectoryNode*>(&node) );
             if ( dir ) {
-                oldCwd_ = cwd_;
-                cwd_ = dir->thisptr();
+                if (autocd_) {
+                    oldCwd_ = cwd_;
+                    cwd_ = dir->thisptr();
+                } else
+                    throw InvalidCommandException();
             } else {
                 dynamic_cast<CommandNode &>(node)(output, command);
             }
@@ -144,7 +147,8 @@ senf::console::Executor::traverseNode(ParseCommandInfo::argument_value_type cons
         return cwd().traverse(
             boost::make_iterator_range(
                 boost::make_transform_iterator(path.begin(), TraverseTokens()),
-                boost::make_transform_iterator(path.end(), TraverseTokens())));
+                boost::make_transform_iterator(path.end(), TraverseTokens())),
+            autocomplete_);
     }
     catch (std::bad_cast &) {
         throw InvalidPathException();
@@ -158,7 +162,7 @@ prefix_ senf::console::GenericNode &
 senf::console::Executor::traverseCommand(ParseCommandInfo::CommandPathRange const & path)
 {
     try {
-        return cwd().traverse(path);
+        return cwd().traverse(path, autocomplete_);
     }
     catch (std::bad_cast &) {
         throw InvalidPathException();
index 81ef6e0..89e2501 100644 (file)
@@ -34,6 +34,7 @@
 // senf::console::Executor
 
 prefix_ senf::console::Executor::Executor()
+    : autocd_ (false), autocomplete_ (false)
 {
     oldCwd_ = cwd_ = boost::static_pointer_cast<DirectoryNode>(
         root().shared_from_this());
@@ -51,6 +52,30 @@ prefix_ senf::console::DirectoryNode & senf::console::Executor::cwd()
     return cwd_.expired() ? root() : *cwd_.lock();
 }
 
+prefix_ bool senf::console::Executor::autocd()
+    const
+{
+    return autocd_;
+}
+
+prefix_ senf::console::Executor & senf::console::Executor::autocd(bool v)
+{
+    autocd_ = v;
+    return *this;
+}
+
+prefix_ bool senf::console::Executor::autocomplete()
+    const
+{
+    return autocomplete_;
+}
+
+prefix_ senf::console::Executor & senf::console::Executor::autocomplete(bool v)
+{
+    autocomplete_ = true;
+    return *this;
+}
+
 ///////////////////////////////cci.e///////////////////////////////////////
 #undef prefix_
 
index 441c3d7..92a2dfe 100644 (file)
@@ -86,6 +86,24 @@ namespace console {
                                              Same as execute(). */
         DirectoryNode & cwd() const;    ///< Current working directory
 
+        bool autocd() const;            ///< Get current autocd status
+                                        /**< if autocd is enabled, specifying a directory name as
+                                             command will cd to that directory. Disabled by
+                                             default (but enabled automatically by the interactive
+                                             console). */
+        Executor & autocd(bool v);      ///< Set autocd status
+                                        /**< \see autocd() */
+
+        bool autocomplete() const;      ///< Get current autocomplete status
+                                        /**< if autocomplete is enabled, path components which can
+                                             be uniquely completed will be completed
+                                             automatically. Disabled by default (but enabled
+                                             automatically by the interactive console) void
+                                             autocomplete(bool v). */
+
+        Executor & autocomplete(bool v); ///< Set autocomplete status
+                                        /**< \see autocomplete() */
+
     protected:
 
     private:
@@ -101,6 +119,9 @@ namespace console {
         DirectoryNode::weak_ptr oldCwd_;
         typedef std::vector<DirectoryNode::weak_ptr> DirStack;
         DirStack dirstack_;
+
+        bool autocd_;
+        bool autocomplete_;
     };
 
 
index c0d76bc..8fe2c2b 100644 (file)
@@ -145,6 +145,14 @@ prefix_ senf::console::DirectoryNode::ChildrenRange senf::console::DirectoryNode
     return boost::make_iterator_range(children_.begin(), children_.end());
 }
 
+prefix_ senf::console::DirectoryNode::ChildrenRange
+senf::console::DirectoryNode::completions(std::string const & s)
+    const
+{
+    return boost::make_iterator_range(children_.lower_bound(s),
+                                      children_.lower_bound(s + "\xff"));
+}
+
 prefix_ senf::console::DirectoryNode::DirectoryNode()
 {}
 
index 4244e69..ddf17ac 100644 (file)
@@ -36,7 +36,7 @@
 
 template <class ForwardRange>
 prefix_ senf::console::GenericNode &
-senf::console::DirectoryNode::traverse(ForwardRange const & range)
+senf::console::DirectoryNode::traverse(ForwardRange const & range, bool autocomplete)
 {
     typedef typename boost::range_const_iterator<ForwardRange>::type const_iterator;
     DirectoryNode::ptr dir (thisptr());
@@ -55,12 +55,18 @@ senf::console::DirectoryNode::traverse(ForwardRange const & range)
                 dir = root().thisptr();
         }
         else if (*i != std::string("") && *i != std::string(".")) {
+            std::string name (*i);
+            if (! dir->hasChild(name) && autocomplete) {
+                ChildrenRange completions (dir->completions(name));
+                if (completions.size() == 1)
+                    name = completions.begin()->first;
+            }
             if (next_i == i_end)
-                return dir->get(*i);
+                return dir->get(name);
             else {
                 // Why does g++ give an error on this line ???? :
-                // dir = dynamic_cast<DirectoryNode&>( dir->get(*i) ).thisptr();
-                DirectoryNode & d (dynamic_cast<DirectoryNode&>( dir->get(*i) ));
+                // dir = dynamic_cast<DirectoryNode&>( dir->get(name) ).thisptr();
+                DirectoryNode & d (dynamic_cast<DirectoryNode&>( dir->get(name) ));
                 dir = d.thisptr();
             }
         }
index a93c23c..e443205 100644 (file)
@@ -465,15 +465,18 @@ namespace console {
         DirectoryNode & mkdir(std::string const & name);
                                         ///< Create sub-directory node
         
-        ChildrenRange children() const;
-                                        ///< Return iterator range over all children.
+        ChildrenRange children() const; ///< Return iterator range over all children.
+                                        /**< The returned range is sorted by child name. */
+
+        ChildrenRange completions(std::string const & s) const;
+                                        ///< Return iterator range of completions for \a s
                                         /**< The returned range is sorted by child name. */
 
         ///\}
         ///////////////////////////////////////////////////////////////////////////
 
         template <class ForwardRange>
-        GenericNode & traverse(ForwardRange const & range);
+        GenericNode & traverse(ForwardRange const & range, bool autocomplete=false);
                                         ///< Traverse node path starting at this node
                                         /**< The <tt>ForwardRange::value_type</tt> must be
                                              (convertible to) std::string. Each range element
@@ -482,7 +485,11 @@ namespace console {
                                              If the range starts with an empty element, the
                                              traversal is started at the root() node, otherwise it
                                              is started at \a this node. The traversal supports '.',
-                                             '..' and ignores further empty elements. */
+                                             '..' and ignores further empty elements. 
+
+                                             If \a autocomplete is set to \c true, invalid path
+                                             components which can be uniquely completed will be
+                                             completed automatically while traversing the tree. */
 
         DirectoryNode & doc(std::string const & doc);
                                         ///< Set node documentation
index d2f9142..0e0668d 100644 (file)
@@ -98,12 +98,28 @@ BOOST_AUTO_UNIT_TEST(directoryNode)
         children, 
         children+sizeof(children)/sizeof(children[0]) );
 
+    char const * const completions[] = { "dir1", "dir2" };
+    BOOST_CHECK_EQUAL_COLLECTIONS(
+        boost::make_transform_iterator(senf::console::root().completions("dir").begin(), 
+                                       select1st<std::string const &>()),
+        boost::make_transform_iterator(senf::console::root().completions("dir").end(),
+                                       select1st<std::string const &>()),
+        completions, 
+        completions+sizeof(completions)/sizeof(completions[0]) );
+
     char const * const path[] = { "..", "dir2", "dir3" };
     BOOST_CHECK( &senf::console::root()["dir1"].traverse( boost::make_iterator_range(
                                                               path, 
                                                               path+sizeof(path)/sizeof(path[0])) )
                  == &senf::console::root()["dir2"]["dir3"] );
 
+    char const * const incompletePath[] = { "d" };
+    BOOST_CHECK( &senf::console::root()["dir2"].traverse( boost::make_iterator_range(
+                                                              incompletePath, 
+                                                              incompletePath+sizeof(incompletePath)/sizeof(incompletePath[0])),
+                                                          true )
+                 == &senf::console::root()["dir2"]["dir3"] );
+
     p->doc("test doc");
     std::stringstream ss;
     p->help(ss);
index ffe7246..9ab2c8d 100644 (file)
@@ -198,6 +198,7 @@ prefix_ senf::console::Client::Client(Server & server, ClientHandle handle,
     : out_t(boost::ref(*this)), senf::log::IOStreamTarget(out_t::member), server_ (server),
       handle_ (handle), name_ (name), reader_ (new detail::SafeReadlineClientReader (*this))
 {
+    executor_.autocd(true).autocomplete(true);
     handle_.facet<senf::TCPSocketProtocol>().nodelay();
     // route< senf::SenfLog, senf::log::NOTICE >();
 }
index 573773d..b684439 100644 (file)
@@ -46,9 +46,16 @@ prefix_ senf::console::Client & senf::console::detail::NonblockingSocketSink::cl
 ///////////////////////////////////////////////////////////////////////////
 // senf::console::Server
 
-prefix_ void senf::console::Server::name(std::string const & name)
+prefix_ senf::console::Server & senf::console::Server::name(std::string const & name)
 {
     name_ = name;
+    return *this;
+}
+
+prefix_ void senf::console::Server::stop()
+{
+    // commit suicide
+    instancePtr().reset(0);
 }
 
 ///////////////////////////////////////////////////////////////////////////
index 8d9b30a..e33be2c 100644 (file)
@@ -85,9 +85,12 @@ namespace console {
                                         ///< Start server on given IPv4 address/port
         static Server & start(senf::INet6SocketAddress const & address);
                                         ///< Start server on given IPv6 address/port
-        void name(std::string const & name); ///< Set server name
+        Server & name(std::string const & name); ///< Set server name
                                         /**< This information is used in the prompt string. */
-
+        
+        void stop();                    ///< Stop the server
+                                        /**< All clients will be closed */
+        
     protected:
 
     private: