refactor nick completion into a general word_completion() function

use this function in all places we have nick completion (completely
fixes bug #10944: Using tab to fill in name under certain conditions
causes problems)

enable nick completion for the commandline and search
This commit is contained in:
Gunter Labes 2008-02-01 10:00:47 +00:00
parent b0c03248d1
commit 155942854a
4 changed files with 97 additions and 131 deletions

View file

@ -119,86 +119,44 @@ namespace gui{
}
switch(mode_) {
case gui::TEXTBOX_SEARCH:
case gui::TEXTBOX_COMMAND:
case gui::TEXTBOX_MESSAGE:
{
std::string text = box_->text();
std::string semiword;
bool beginning;
const size_t last_space = text.rfind(" ");
//if last character is a space return
if(last_space == text.size() -1) {
return;
}
if(last_space == std::string::npos) {
beginning = true;
semiword = text;
}else{
beginning = false;
semiword.assign(text,last_space+1,text.size());
}
std::set<std::string> matches;
std::string best_match = semiword;
std::vector<std::string> matches;
// Add players
for(size_t n = 0; n != teams.size(); ++n) {
if(teams[n].is_empty()) {
continue;
}
const std::string& name = teams[n].current_player();
if( name.size() >= semiword.size() &&
std::equal(semiword.begin(),semiword.end(),name.begin(),chars_equal_insensitive)) {
if(matches.empty()) {
best_match = name;
} else {
// Testing if this name already isn't in set
// Because some players can control more sides
if (matches.count(name) >= 1){
continue;
}
int j= 0;;
while(best_match[j] == name[j]) j++;
best_match.erase(best_match.begin()+j,best_match.end());
}
matches.insert(name);
}
if(teams[n].is_empty()) continue;
matches.push_back(teams[n].current_player());
}
// Searching in observers list
// Add observers
const std::set<std::string>& observers = gui.observers();
for(std::set<std::string>::const_iterator i = observers.begin(); i != observers.end(); ++i) {
if( i->size() >= semiword.size() &&
std::equal(semiword.begin(),semiword.end(),i->begin(),chars_equal_insensitive)) {
if(matches.empty()) {
best_match = *i;
} else {
int j = 0;
while(toupper(best_match[j]) == toupper((*i)[j])) j++;
best_match.erase(best_match.begin()+j,best_match.end());
}
matches.insert(*i);
}
for(std::set<std::string>::const_iterator i = observers.begin();
i != observers.end(); ++i)
{
matches.push_back(*i);
}
// Remove duplicates.
std::sort<std::vector<std::string>::iterator>
(matches.begin(), matches.end());
matches.erase(std::unique(matches.begin(), matches.end()), matches.end());
// Exclude own nick from tab-completion.
if (mode_ == gui::TEXTBOX_MESSAGE) {
matches.erase(std::remove(matches.begin(), matches.end(),
preferences::login()), matches.end());
}
const bool line_start = utils::word_completion(text, matches);
if(!matches.empty()) {
std::string add = beginning ? ": " : " ";
text.replace(last_space+1, semiword.size(), best_match);
if(matches.size() == 1) {
text.append(add);
} else {
std::string completion_list;
std::set<std::string>::iterator it;
for(it =matches.begin();it!=matches.end();it++) {
completion_list += " ";
completion_list += *it;
}
gui.add_chat_message(time(NULL), "", 0, completion_list,
game_display::MESSAGE_PRIVATE, false);
}
box_->set_text(text);
if (matches.empty()) return;
if (matches.size() == 1 && mode_ == gui::TEXTBOX_MESSAGE) {
text.append(line_start ? ": " : " ");
} else {
std::string completion_list = utils::join(matches, ' ');
gui.add_chat_message(time(NULL), "", 0, completion_list,
game_display::MESSAGE_PRIVATE, false);
}
box_->set_text(text);
break;
}
default:

View file

@ -423,69 +423,22 @@ void ui::handle_key_event(const SDL_KeyboardEvent& event)
// nick tab-completion
} else if(event.keysym.sym == SDLK_TAB ) {
std::string text = entry_textbox_.text();
std::string semiword;
bool beginning;
const size_t last_space = text.rfind(" ");
//if last character is a space return
if(last_space == text.size() -1) {
return;
}
if(last_space == std::string::npos) {
beginning = true;
semiword = text;
}else{
beginning = false;
semiword.assign(text,last_space+1,text.size());
}
std::vector<std::string> matches;
std::string best_match = semiword;
std::vector<std::string> users = user_list_;
std::vector<std::string> matches = user_list_;
// Exclude own nick from tab-completion.
users.erase(std::remove(users.begin(), users.end(),
preferences::login()), users.end());
std::sort<std::vector<std::string>::iterator>(users.begin(), users.end());
for(std::vector<std::string>::const_iterator i = users.begin();
i != users.end(); ++i)
{
if (i->size() < semiword.size()
|| !std::equal(semiword.begin(), semiword.end(), i->begin(),
chars_equal_insensitive))
{
continue;
}
if (matches.empty()) {
best_match = *i;
} else {
int j = 0;
while (toupper(best_match[j]) == toupper((*i)[j])) j++;
if (best_match.begin() + j < best_match.end()) {
best_match.erase(best_match.begin() + j, best_match.end());
}
}
matches.push_back(*i);
}
matches.erase(std::remove(matches.begin(), matches.end(),
preferences::login()), matches.end());
const bool line_start = utils::word_completion(text, matches);
if(!matches.empty()) {
text.replace(last_space + 1, best_match.size(), best_match);
if(matches.size() == 1) {
text.append(beginning ? ": " : " ");
} else {
std::string completion_list;
std::vector<std::string>::iterator it;
for(it =matches.begin();it!=matches.end();it++) {
completion_list += " ";
completion_list += *it;
}
chat_.add_message(time(NULL), "", completion_list);
chat_.update_textbox(chat_textbox_);
}
entry_textbox_.set_text(text);
}
if (matches.empty()) return;
if (matches.size() == 1) {
text.append(line_start ? ": " : " ");
} else {
std::string completion_list = utils::join(matches, ' ');
chat_.add_message(time(NULL), "", completion_list);
chat_.update_textbox(chat_textbox_);
}
entry_textbox_.set_text(text);
}
#endif
}

View file

@ -424,6 +424,59 @@ bool isvalid_username(const std::string& username) {
return true;
}
//! Try to complete the last word of 'text' with the 'wordlist'.
//! @param[in] 'text' Text where we try to complete the last word of.
//! @param[out] 'text' Text with completed last word.
//! @param[in] 'wordlist' A vector of strings to complete against.
//! @param[out] 'wordlist' A vector of strings that matched 'text'.
//! @return 'true' iff text is just one word (no spaces)
bool word_completion(std::string& text, std::vector<std::string>& wordlist) {
std::vector<std::string> matches;
const size_t last_space = text.rfind(" ");
// If last character is a space return.
if (last_space == text.size() -1) {
wordlist = matches;
return false;
}
bool text_start;
std::string semiword;
if (last_space == std::string::npos) {
text_start = true;
semiword = text;
} else {
text_start = false;
semiword.assign(text, last_space + 1, text.size());
}
std::string best_match = semiword;
for (std::vector<std::string>::const_iterator word = wordlist.begin();
word != wordlist.end(); ++word)
{
if (word->size() < semiword.size()
|| !std::equal(semiword.begin(), semiword.end(), word->begin(),
chars_equal_insensitive))
{
continue;
}
if (matches.empty()) {
best_match = *word;
} else {
int j = 0;
while (toupper(best_match[j]) == toupper((*word)[j])) j++;
if (best_match.begin() + j < best_match.end()) {
best_match.erase(best_match.begin() + j, best_match.end());
}
}
matches.push_back(*word);
}
if(!matches.empty()) {
text.replace(last_space + 1, best_match.size(), best_match);
}
wordlist = matches;
return text_start;
}
bool is_word_boundary(char c) {
return (c == ' ' || c == ',' || c == ':' || c == '\'' || c == '"' || c == '-');
}

View file

@ -69,6 +69,8 @@ std::string &strip(std::string &str);
std::string& strip_char(std::string &str, const char c);
bool string_bool(const std::string& str,bool def=false);
//! Try to complete the last word of 'text' with the 'wordlist'.
bool word_completion(std::string& text, std::vector<std::string>& wordlist);
//! Check if a message contains a word.
bool word_match(const std::string& message, const std::string& word);
//! Match using '*' as any number of characters (including none),