Commit 855cddf8 authored by Benoit Perrot's avatar Benoit Perrot
Browse files

2005-03-17 Benoît Perrot <benoit@lrde.epita.fr>

	Introduce command identifier completion in shell.
	
	* src/shell/shell.hh, src/shell-tasks.cc:
	Make Shell a singleton.
	* src/shell/shell.cc: Use readline's custom completers system 
	to complete command identifiers.
	
parent 5298fb04
2005-03-17 Benoît Perrot <benoit@lrde.epita.fr>
Introduce command identifier completion in shell.
* src/shell/shell.hh, src/shell-tasks.cc:
Make Shell a singleton.
* src/shell/shell.cc: Use readline's custom completers system
to complete command identifiers.
2005-03-08 Benoît Perrot <benoit@lrde.epita.fr>
* src/vm/table.hh: Move to...
......
......@@ -32,8 +32,7 @@ namespace shell
void
shell_exec()
{
shell::Shell sh;
sh.run();
shell::Shell::instance().run();
}
} // namespace tasks
......
......@@ -34,6 +34,71 @@
namespace shell
{
#ifdef HAVE_READLINE_READLINE_H
// --------------------------------------------------------------------------
// Completion engine
static std::string
find_common_prefix(const std::vector<std::string> &l)
{
if (0 < l.size())
{
unsigned nb_common_chars = l[0].size();
for (std::vector<std::string>::const_iterator
e = l.begin(); e != l.end() && 0 < nb_common_chars; ++e)
{
unsigned c = 0;
while (c < nb_common_chars && l.front()[c] == (*e)[c])
++c;
nb_common_chars = c;
}
return std::string(l.front(), 0, nb_common_chars);
}
return std::string();
}
static char **
shell_completer(const char *text, int start, int end)
{
// if (rl_line_buffer[rl_point] && !isblank(rl_line_buffer[rl_point]))
// return 0;
// Generate possible completions
std::string line_to_complete(rl_line_buffer, end);
std::vector<std::string> completions;
delete Shell::instance().parse(line_to_complete,
&completions); // wonderfull
char **matches = 0;
if (0 < completions.size())
{
matches = (char**) malloc(sizeof(char*) * (completions.size() + 2));
if (matches)
{
// Extract max common characters
std::string substitution = find_common_prefix(completions);
// Fill matches array
if (!substitution.empty())
matches[0] = strdup(substitution.c_str());
else
matches[0] = strdup(text);
++matches;
for (unsigned c = 0; c < completions.size(); ++c)
matches[c] = strdup(completions[c].c_str());
matches[completions.size()] = 0;
--matches;
}
}
return matches;
}
#endif // !HAVE_READLINE_READLINE_H
// --------------------------------------------------------------------------
// Shell
// --------------------------------------------------------------------------
......@@ -41,6 +106,10 @@ namespace shell
Shell::Shell():
program_(0)
{
#ifdef HAVE_READLINE_READLINE_H
rl_attempted_completion_function = shell_completer;
#endif // !HAVE_READLINE_READLINE_H
vm_.get_cpu().set_check_callee_save(true);
vm_.get_cpu().set_trace(false);
......@@ -113,7 +182,7 @@ namespace shell
if (!line.empty())
{
delete command;
command = build_cmd(line);
command = parse(line);
}
}
......@@ -164,33 +233,54 @@ namespace shell
}
Cmd::identifier_type
Shell::eat_command_id(std::istringstream &iss)
Shell::eat_command_id(std::istringstream &iss,
std::vector<std::string> *completions)
{
std::string id = eat_word(iss);
if (id.empty())
return Cmd::id_null;
Cmd::identifier_type id = Cmd::id_null;
map_token_type::const_iterator it = map_token_.lower_bound(id);
if (it == map_token_.end() || it->first.find(id) != 0)
return Cmd::id_unknown;
else
std::string word = eat_word(iss);
bool complete_p = iss.eof() && completions;
if (!word.empty())
{
map_token_type::const_iterator next(it);
++next;
if (next != map_token_.end() && next->first.find(id) == 0)
return Cmd::id_ambiguous;
map_token_type::const_iterator it = map_token_.lower_bound(word);
if (it == map_token_.end() || it->first.find(word) != 0)
id = Cmd::id_unknown;
else
{
id = it->second;
map_token_type::const_iterator next(it);
while (next != map_token_.end() && next->first.find(word) == 0)
{
if (complete_p)
completions->push_back(next->first);
if (it->first != next->first)
id = Cmd::id_ambiguous;
++next;
}
return id;
}
}
return it->second;
if (complete_p)
for (map_token_type::const_iterator
it = map_token_.begin(); it != map_token_.end(); ++it)
completions->push_back(it->first);
return id;
}
Cmd*
Shell::build_cmd(const std::string &str)
Shell::parse(const std::string &str,
std::vector<std::string> *completions)
{
std::istringstream iss(str);
Cmd* command = 0;
Cmd::identifier_type id = eat_command_id(iss);
Cmd::identifier_type id = eat_command_id(iss, completions);
switch (id)
{
case Cmd::id_run:
......
......@@ -52,10 +52,17 @@ namespace shell
/** \name Constructor and destructor
\{ */
public:
protected:
/// Construct a Shell.
Shell();
public:
static Shell &instance()
{
static Shell sh;
return sh;
}
/// Destroy a Shell.
~Shell();
/** \} */
......@@ -65,7 +72,8 @@ namespace shell
void run();
/// Build a command.
Cmd* build_cmd(const std::string &str);
Cmd* parse(const std::string &str,
std::vector<std::string> *completions = 0);
public:
/// Access to a register.
......@@ -122,7 +130,8 @@ namespace shell
/// Eat a word from input stringstream and return the
/// corresponding keyword.
Cmd::identifier_type eat_command_id(std::istringstream &iss);
Cmd::identifier_type eat_command_id(std::istringstream &iss,
std::vector<std::string> *completions);
/** \name Execution engine
\{ */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment