From: g0dil Date: Tue, 29 Apr 2008 10:33:17 +0000 (+0000) Subject: Console: Implement autocomplete X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=ca70e274a556bf217f3f4c7b12e0fad2a7cd4853;p=senf.git Console: Implement autocomplete 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 --- diff --git a/Console/Executor.cc b/Console/Executor.cc index da8aa49..a7db590 100644 --- a/Console/Executor.cc +++ b/Console/Executor.cc @@ -60,8 +60,11 @@ prefix_ void senf::console::Executor::execute(std::ostream & output, GenericNode & node ( traverseCommand(command.commandPath()) ); DirectoryNode * dir ( dynamic_cast(&node) ); if ( dir ) { - oldCwd_ = cwd_; - cwd_ = dir->thisptr(); + if (autocd_) { + oldCwd_ = cwd_; + cwd_ = dir->thisptr(); + } else + throw InvalidCommandException(); } else { dynamic_cast(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(); diff --git a/Console/Executor.cci b/Console/Executor.cci index 81ef6e0..89e2501 100644 --- a/Console/Executor.cci +++ b/Console/Executor.cci @@ -34,6 +34,7 @@ // senf::console::Executor prefix_ senf::console::Executor::Executor() + : autocd_ (false), autocomplete_ (false) { oldCwd_ = cwd_ = boost::static_pointer_cast( 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_ diff --git a/Console/Executor.hh b/Console/Executor.hh index 441c3d7..92a2dfe 100644 --- a/Console/Executor.hh +++ b/Console/Executor.hh @@ -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 DirStack; DirStack dirstack_; + + bool autocd_; + bool autocomplete_; }; diff --git a/Console/Node.cci b/Console/Node.cci index c0d76bc..8fe2c2b 100644 --- a/Console/Node.cci +++ b/Console/Node.cci @@ -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() {} diff --git a/Console/Node.ct b/Console/Node.ct index 4244e69..ddf17ac 100644 --- a/Console/Node.ct +++ b/Console/Node.ct @@ -36,7 +36,7 @@ template 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::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( dir->get(*i) ).thisptr(); - DirectoryNode & d (dynamic_cast( dir->get(*i) )); + // dir = dynamic_cast( dir->get(name) ).thisptr(); + DirectoryNode & d (dynamic_cast( dir->get(name) )); dir = d.thisptr(); } } diff --git a/Console/Node.hh b/Console/Node.hh index a93c23c..e443205 100644 --- a/Console/Node.hh +++ b/Console/Node.hh @@ -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 - GenericNode & traverse(ForwardRange const & range); + GenericNode & traverse(ForwardRange const & range, bool autocomplete=false); ///< Traverse node path starting at this node /**< The ForwardRange::value_type 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 diff --git a/Console/Node.test.cc b/Console/Node.test.cc index d2f9142..0e0668d 100644 --- a/Console/Node.test.cc +++ b/Console/Node.test.cc @@ -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()), + boost::make_transform_iterator(senf::console::root().completions("dir").end(), + select1st()), + 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); diff --git a/Console/Server.cc b/Console/Server.cc index ffe7246..9ab2c8d 100644 --- a/Console/Server.cc +++ b/Console/Server.cc @@ -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().nodelay(); // route< senf::SenfLog, senf::log::NOTICE >(); } diff --git a/Console/Server.cci b/Console/Server.cci index 573773d..b684439 100644 --- a/Console/Server.cci +++ b/Console/Server.cci @@ -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); } /////////////////////////////////////////////////////////////////////////// diff --git a/Console/Server.hh b/Console/Server.hh index 8d9b30a..e33be2c 100644 --- a/Console/Server.hh +++ b/Console/Server.hh @@ -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: