From d741b6fe6e805b570bb899e777ea7101f6395721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20L=C3=BCbking?= Date: Sat, 23 Jul 2016 00:30:25 +0200 Subject: improve fbrun completion - streamline code - indicate completion by making use of selection - fix buggy behavior (notably subsequent completions and FS path following) - support "~" in paths - support chunk completion (ie. "mp[layer] ~/vid[eos/favporn.mp4]" can be completed in both tokens; buggy with paths including spaces in non-leafs) REQUEST: 223 --- util/fbrun/FbRun.cc | 230 +++++++++++++++++++++++++--------------------------- util/fbrun/FbRun.hh | 22 +++-- 2 files changed, 125 insertions(+), 127 deletions(-) diff --git a/util/fbrun/FbRun.cc b/util/fbrun/FbRun.cc index 8cc6b16..7fde936 100644 --- a/util/fbrun/FbRun.cc +++ b/util/fbrun/FbRun.cc @@ -65,8 +65,10 @@ FbRun::FbRun(int x, int y, size_t width): m_gc(*this), m_end(false), m_current_history_item(0), - m_last_completion_prefix(""), - m_current_apps_item(0), + m_current_files_item(-1), + m_last_completion_path(""), + m_current_apps_item(-1), + m_completion_pos(std::string::npos), m_cursor(XCreateFontCursor(FbTk::App::instance()->display(), XC_xterm)) { setGC(m_gc.gc()); @@ -255,7 +257,7 @@ void FbRun::keyPressEvent(XKeyEvent &ke) { break; case XK_Tab: did_tab_complete = true; - tabCompleteHistory(); + tabComplete(m_history, m_current_history_item, true); // reverse break; } } else if ((ke.state & (Mod1Mask|ShiftMask)) == (Mod1Mask | ShiftMask)) { @@ -293,9 +295,9 @@ void FbRun::keyPressEvent(XKeyEvent &ke) { break; } } - clear(); if (!did_tab_complete) - m_last_completion_prefix = ""; + m_completion_pos = std::string::npos; + clear(); } void FbRun::lockPosition(bool size_too) { @@ -357,134 +359,126 @@ void FbRun::lastHistoryItem() { } } -void FbRun::tabCompleteHistory() { - if (m_current_history_item == 0 || m_history.empty() ) { +void FbRun::tabComplete(const std::vector &list, int ¤tItem, bool reverse) { + if (list.empty()) { XBell(m_display, 0); - } else { - unsigned int nr= 0; - unsigned int history_item = m_current_history_item - 1; - if (m_last_completion_prefix.empty()) - m_last_completion_prefix = text().substr(0, textStartPos() + cursorPosition()); - - while (history_item != m_current_history_item && nr++ < m_history.size()) { - if (m_history[history_item].find(m_last_completion_prefix) == 0) { - m_current_history_item = history_item; - setText(FbTk::BiDiString(m_history[m_current_history_item])); + return; + } + + if (m_completion_pos == std::string::npos) + m_completion_pos = textStartPos() + cursorPosition(); + size_t split = text().find_last_of(' ', m_completion_pos); + if (split == std::string::npos) + split = 0; + else + ++split; // skip space + std::string prefix = text().substr(split, m_completion_pos - split); + + if (currentItem < 0) + currentItem = 0; + else if (currentItem >= list.size()) + currentItem = list.size() - 1; + int item = currentItem; + + while (true) { + if (reverse) { + if (--item < 0) + item = list.size() - 1; + } else { + if (++item >= list.size()) + item = 0; + } + if (list.at(item).find(prefix) == 0) { + setText(FbTk::BiDiString(text().substr(0, split) + list.at(item))); + if (item == currentItem) { cursorEnd(); - break; + m_completion_pos = std::string::npos; + } else { + select(split + prefix.size(), text().size() - (prefix.size() + split)); } - if (history_item == 0) // loop - history_item = m_history.size(); - history_item--; + currentItem = item; + return; + } + if (item == currentItem) { + cursorEnd(); + m_completion_pos = std::string::npos; + return; } - if (history_item == m_current_history_item) XBell(m_display, 0); } + // found nothing + XBell(m_display, 0); } -void FbRun::tabCompleteApps() { - - static bool first_run= true; - if (m_last_completion_prefix.empty()) - m_last_completion_prefix = text().substr(0, textStartPos() + cursorPosition()); - string prefix = m_last_completion_prefix; - FbTk::Directory dir; - - bool add_dirs= false; - bool changed_prefix= false; - - // (re)build m_apps-container - if (first_run || m_last_completion_prefix != prefix) { - first_run= false; - string path; +void FbRun::tabCompleteApps() { - if(!prefix.empty() && - string("/.~").find_first_of(prefix[0]) != string::npos) { - size_t rseparator= prefix.find_last_of("/"); - path= prefix.substr(0, rseparator + 1) + ":"; - add_dirs= true; - } else { - char* tmp_path = getenv("PATH"); - if (tmp_path) - path = tmp_path; + if (m_completion_pos == std::string::npos) + m_completion_pos = textStartPos() + cursorPosition(); + size_t split = text().find_last_of(' ', m_completion_pos); + if (split == std::string::npos) + split = 0; + else + ++split; // skip the space + std::string prefix = text().substr(split, m_completion_pos - split); + if (prefix.empty()) { + XBell(m_display, 0); + return; + } + if (prefix.at(0) == '/' || prefix.at(0) == '.' || prefix.at(0) == '~') { + // we're completing a directory, find subdirs + split = prefix.find_last_of('/'); + prefix = prefix.substr(0, split+1); + if (prefix != m_last_completion_path) { + m_files.clear(); + m_current_files_item = -1; + m_last_completion_path = prefix; + + FbTk::Directory dir; + std::string path = prefix; + if (path.at(0) == '~') + path.replace(0,1,getenv("HOME")); + dir.open(path.c_str()); + int n = dir.entries(); + while (--n > -1) { + std::string entry = dir.readFilename(); + if (entry == "." || entry == "..") + continue; + if (FbTk::FileUtil::isDirectory(std::string(path + entry).c_str())) + m_files.push_back(prefix + entry + "/"); + else + m_files.push_back(prefix + entry); + } + dir.close(); + sort(m_files.begin(), m_files.end()); } - m_apps.clear(); - - unsigned int l; - unsigned int r; - - for(l= 0, r= 0; r < path.size(); r++) { - if ((path[r]==':' || r == path.size() - 1) && r - l > 0) { - string filename; - string fncomplete; - dir.open(path.substr(l, r - l).c_str()); - int n= dir.entries(); - if (n >= 0) { - while(n--) { - filename= dir.readFilename(); - fncomplete= dir.name() + - (*dir.name().rbegin() != '/' ? "/" : "") + - filename; - - // directories in dirmode ? - if (add_dirs && FbTk::FileUtil::isDirectory(fncomplete.c_str()) && - filename != ".." && filename != ".") { - m_apps.push_back(fncomplete); - // executables in dirmode ? - } else if (add_dirs && FbTk::FileUtil::isRegularFile(fncomplete.c_str()) && - FbTk::FileUtil::isExecutable(fncomplete.c_str()) && - (prefix == "" || - fncomplete.substr(0, prefix.size()) == prefix)) { - m_apps.push_back(fncomplete); - // executables in $PATH ? - } else if (FbTk::FileUtil::isRegularFile(fncomplete.c_str()) && - FbTk::FileUtil::isExecutable(fncomplete.c_str()) && - (prefix == "" || - filename.substr(0, prefix.size()) == prefix)) { - m_apps.push_back(filename); + tabComplete(m_files, m_current_files_item); + } else { + static bool first_run = true; + if (first_run) { + first_run = false; + std::string path = getenv("PATH"); + FbTk::Directory dir; + for (unsigned int l = 0, r = 0; r < path.size(); ++r) { + if ((path.at(r) == ':' || r == path.size() - 1) && r - l > 1) { + dir.open(path.substr(l, r - l).c_str()); + prefix = dir.name() + (*dir.name().rbegin() == '/' ? "" : "/"); + int n = dir.entries(); + while (--n > -1) { + std::string entry = dir.readFilename(); + std::string file = prefix + entry; + if (FbTk::FileUtil::isExecutable(file.c_str()) && + !FbTk::FileUtil::isDirectory(file.c_str())) { + m_apps.push_back(entry); } } + dir.close(); + l = r + 1; } - l= r + 1; - dir.close(); - } - } - sort(m_apps.begin(), m_apps.end()); - unique(m_apps.begin(), m_apps.end()); - - m_last_completion_prefix = prefix; - changed_prefix= true; - m_current_apps_item= 0; - } - - if (m_apps.empty() ) { - XBell(m_display, 0); - } else { - size_t apps_item = m_current_apps_item + (changed_prefix ? 0 : 1); - bool loop= false; - - while (true) { - if (apps_item >= m_apps.size() ) { - loop = true; - apps_item = 0; - } - - if ((!changed_prefix || loop) && apps_item == m_current_apps_item) { - break; - } - if (m_apps[apps_item].find(prefix) == 0) { - m_current_apps_item = apps_item; - if (add_dirs && FbTk::FileUtil::isDirectory(m_apps[m_current_apps_item].c_str())) - setText(m_apps[m_current_apps_item] + "/"); - else - setText(m_apps[m_current_apps_item]); - cursorEnd(); - break; } - apps_item++; + sort(m_apps.begin(), m_apps.end()); + unique(m_apps.begin(), m_apps.end()); } - if (!changed_prefix && apps_item == m_current_apps_item) - XBell(m_display, 0); + tabComplete(m_apps, m_current_apps_item); } } diff --git a/util/fbrun/FbRun.hh b/util/fbrun/FbRun.hh index 5d678b0..a74d347 100644 --- a/util/fbrun/FbRun.hh +++ b/util/fbrun/FbRun.hh @@ -80,7 +80,7 @@ private: void adjustEndPos(); void firstHistoryItem(); void lastHistoryItem(); - void tabCompleteHistory(); + void tabComplete(const std::vector &list, int ¤t, bool reverse = false); void tabCompleteApps(); bool m_print; ///< the input should be printed to stdout rather than run @@ -89,16 +89,20 @@ private: int m_bevel; FbTk::GContext m_gc; ///< graphic context bool m_end; ///< marks when this object is done + std::vector m_history; ///< history list of commands std::string m_history_file; ///< holds filename for command history file - size_t m_current_history_item; ///< holds current position in command history - std::string m_last_completion_prefix; ///< last prefix we completed on - - typedef std::vector AppsContainer; - typedef AppsContainer::iterator AppsContainerIt; - AppsContainer m_apps; ///< holds all apps in $PATH - size_t m_current_apps_item; ///< holds current position in apps-history - + int m_current_history_item; ///< holds current position in command history + + std::vector m_files; + int m_current_files_item; + std::string m_last_completion_path; ///< last prefix we completed on + + std::vector m_apps; + int m_current_apps_item; ///< holds current position in apps-history + + size_t m_completion_pos; + Cursor m_cursor; FbTk::FbPixmap m_pixmap; -- cgit v0.11.2