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:
parent
b0c03248d1
commit
155942854a
4 changed files with 97 additions and 131 deletions
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 == '-');
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Add table
Reference in a new issue