Skip to content
Snippets Groups Projects
Commit bd12dd39 authored by g0dil's avatar g0dil
Browse files

Console: Implement autocomplete

Console: Add autocomplete and autocd options to executor
Console: Add Server::stop member
parent 72106003
No related branches found
No related tags found
No related merge requests found
......@@ -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();
......
......@@ -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_
......
......@@ -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_;
};
......
......@@ -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()
{}
......
......@@ -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();
}
}
......
......@@ -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
......
......@@ -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);
......
......@@ -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 >();
}
......
......@@ -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);
}
///////////////////////////////////////////////////////////////////////////
......
......@@ -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:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment