aboutsummaryrefslogtreecommitdiff
path: root/util/fbrun
diff options
context:
space:
mode:
authorThomas Lübking <thomas.luebking@gmail.com>2016-07-22 22:30:25 (GMT)
committerThomas Lübking <thomas.luebking@gmail.com>2016-07-23 14:58:04 (GMT)
commitd741b6fe6e805b570bb899e777ea7101f6395721 (patch)
tree97a1d8f2b8098da18263ee62cd5362701ee926ff /util/fbrun
parented75e883db8426e889a46ce61b9f4bd1a4d6bc66 (diff)
downloadfluxbox-d741b6fe6e805b570bb899e777ea7101f6395721.zip
fluxbox-d741b6fe6e805b570bb899e777ea7101f6395721.tar.bz2
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
Diffstat (limited to 'util/fbrun')
-rw-r--r--util/fbrun/FbRun.cc230
-rw-r--r--util/fbrun/FbRun.hh22
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):
65 m_gc(*this), 65 m_gc(*this),
66 m_end(false), 66 m_end(false),
67 m_current_history_item(0), 67 m_current_history_item(0),
68 m_last_completion_prefix(""), 68 m_current_files_item(-1),
69 m_current_apps_item(0), 69 m_last_completion_path(""),
70 m_current_apps_item(-1),
71 m_completion_pos(std::string::npos),
70 m_cursor(XCreateFontCursor(FbTk::App::instance()->display(), XC_xterm)) { 72 m_cursor(XCreateFontCursor(FbTk::App::instance()->display(), XC_xterm)) {
71 73
72 setGC(m_gc.gc()); 74 setGC(m_gc.gc());
@@ -255,7 +257,7 @@ void FbRun::keyPressEvent(XKeyEvent &ke) {
255 break; 257 break;
256 case XK_Tab: 258 case XK_Tab:
257 did_tab_complete = true; 259 did_tab_complete = true;
258 tabCompleteHistory(); 260 tabComplete(m_history, m_current_history_item, true); // reverse
259 break; 261 break;
260 } 262 }
261 } else if ((ke.state & (Mod1Mask|ShiftMask)) == (Mod1Mask | ShiftMask)) { 263 } else if ((ke.state & (Mod1Mask|ShiftMask)) == (Mod1Mask | ShiftMask)) {
@@ -293,9 +295,9 @@ void FbRun::keyPressEvent(XKeyEvent &ke) {
293 break; 295 break;
294 } 296 }
295 } 297 }
296 clear();
297 if (!did_tab_complete) 298 if (!did_tab_complete)
298 m_last_completion_prefix = ""; 299 m_completion_pos = std::string::npos;
300 clear();
299} 301}
300 302
301void FbRun::lockPosition(bool size_too) { 303void FbRun::lockPosition(bool size_too) {
@@ -357,134 +359,126 @@ void FbRun::lastHistoryItem() {
357 } 359 }
358} 360}
359 361
360void FbRun::tabCompleteHistory() { 362void FbRun::tabComplete(const std::vector<std::string> &list, int &currentItem, bool reverse) {
361 if (m_current_history_item == 0 || m_history.empty() ) { 363 if (list.empty()) {
362 XBell(m_display, 0); 364 XBell(m_display, 0);
363 } else { 365 return;
364 unsigned int nr= 0; 366 }
365 unsigned int history_item = m_current_history_item - 1; 367
366 if (m_last_completion_prefix.empty()) 368 if (m_completion_pos == std::string::npos)
367 m_last_completion_prefix = text().substr(0, textStartPos() + cursorPosition()); 369 m_completion_pos = textStartPos() + cursorPosition();
368 370 size_t split = text().find_last_of(' ', m_completion_pos);
369 while (history_item != m_current_history_item && nr++ < m_history.size()) { 371 if (split == std::string::npos)
370 if (m_history[history_item].find(m_last_completion_prefix) == 0) { 372 split = 0;
371 m_current_history_item = history_item; 373 else
372 setText(FbTk::BiDiString(m_history[m_current_history_item])); 374 ++split; // skip space
375 std::string prefix = text().substr(split, m_completion_pos - split);
376
377 if (currentItem < 0)
378 currentItem = 0;
379 else if (currentItem >= list.size())
380 currentItem = list.size() - 1;
381 int item = currentItem;
382
383 while (true) {
384 if (reverse) {
385 if (--item < 0)
386 item = list.size() - 1;
387 } else {
388 if (++item >= list.size())
389 item = 0;
390 }
391 if (list.at(item).find(prefix) == 0) {
392 setText(FbTk::BiDiString(text().substr(0, split) + list.at(item)));
393 if (item == currentItem) {
373 cursorEnd(); 394 cursorEnd();
374 break; 395 m_completion_pos = std::string::npos;
396 } else {
397 select(split + prefix.size(), text().size() - (prefix.size() + split));
375 } 398 }
376 if (history_item == 0) // loop 399 currentItem = item;
377 history_item = m_history.size(); 400 return;
378 history_item--; 401 }
402 if (item == currentItem) {
403 cursorEnd();
404 m_completion_pos = std::string::npos;
405 return;
379 } 406 }
380 if (history_item == m_current_history_item) XBell(m_display, 0);
381 } 407 }
408 // found nothing
409 XBell(m_display, 0);
382} 410}
383 411
384void FbRun::tabCompleteApps() {
385
386 static bool first_run= true;
387 if (m_last_completion_prefix.empty())
388 m_last_completion_prefix = text().substr(0, textStartPos() + cursorPosition());
389 string prefix = m_last_completion_prefix;
390 FbTk::Directory dir;
391
392 bool add_dirs= false;
393 bool changed_prefix= false;
394
395 // (re)build m_apps-container
396 if (first_run || m_last_completion_prefix != prefix) {
397 first_run= false;
398 412
399 string path; 413void FbRun::tabCompleteApps() {
400 414
401 if(!prefix.empty() && 415 if (m_completion_pos == std::string::npos)
402 string("/.~").find_first_of(prefix[0]) != string::npos) { 416 m_completion_pos = textStartPos() + cursorPosition();
403 size_t rseparator= prefix.find_last_of("/"); 417 size_t split = text().find_last_of(' ', m_completion_pos);
404 path= prefix.substr(0, rseparator + 1) + ":"; 418 if (split == std::string::npos)
405 add_dirs= true; 419 split = 0;
406 } else { 420 else
407 char* tmp_path = getenv("PATH"); 421 ++split; // skip the space
408 if (tmp_path) 422 std::string prefix = text().substr(split, m_completion_pos - split);
409 path = tmp_path; 423 if (prefix.empty()) {
424 XBell(m_display, 0);
425 return;
426 }
427 if (prefix.at(0) == '/' || prefix.at(0) == '.' || prefix.at(0) == '~') {
428 // we're completing a directory, find subdirs
429 split = prefix.find_last_of('/');
430 prefix = prefix.substr(0, split+1);
431 if (prefix != m_last_completion_path) {
432 m_files.clear();
433 m_current_files_item = -1;
434 m_last_completion_path = prefix;
435
436 FbTk::Directory dir;
437 std::string path = prefix;
438 if (path.at(0) == '~')
439 path.replace(0,1,getenv("HOME"));
440 dir.open(path.c_str());
441 int n = dir.entries();
442 while (--n > -1) {
443 std::string entry = dir.readFilename();
444 if (entry == "." || entry == "..")
445 continue;
446 if (FbTk::FileUtil::isDirectory(std::string(path + entry).c_str()))
447 m_files.push_back(prefix + entry + "/");
448 else
449 m_files.push_back(prefix + entry);
450 }
451 dir.close();
452 sort(m_files.begin(), m_files.end());
410 } 453 }
411 m_apps.clear(); 454 tabComplete(m_files, m_current_files_item);
412 455 } else {
413 unsigned int l; 456 static bool first_run = true;
414 unsigned int r; 457 if (first_run) {
415 458 first_run = false;
416 for(l= 0, r= 0; r < path.size(); r++) { 459 std::string path = getenv("PATH");
417 if ((path[r]==':' || r == path.size() - 1) && r - l > 0) { 460 FbTk::Directory dir;
418 string filename; 461 for (unsigned int l = 0, r = 0; r < path.size(); ++r) {
419 string fncomplete; 462 if ((path.at(r) == ':' || r == path.size() - 1) && r - l > 1) {
420 dir.open(path.substr(l, r - l).c_str()); 463 dir.open(path.substr(l, r - l).c_str());
421 int n= dir.entries(); 464 prefix = dir.name() + (*dir.name().rbegin() == '/' ? "" : "/");
422 if (n >= 0) { 465 int n = dir.entries();
423 while(n--) { 466 while (--n > -1) {
424 filename= dir.readFilename(); 467 std::string entry = dir.readFilename();
425 fncomplete= dir.name() + 468 std::string file = prefix + entry;
426 (*dir.name().rbegin() != '/' ? "/" : "") + 469 if (FbTk::FileUtil::isExecutable(file.c_str()) &&
427 filename; 470 !FbTk::FileUtil::isDirectory(file.c_str())) {
428 471 m_apps.push_back(entry);
429 // directories in dirmode ?
430 if (add_dirs && FbTk::FileUtil::isDirectory(fncomplete.c_str()) &&
431 filename != ".." && filename != ".") {
432 m_apps.push_back(fncomplete);
433 // executables in dirmode ?
434 } else if (add_dirs && FbTk::FileUtil::isRegularFile(fncomplete.c_str()) &&
435 FbTk::FileUtil::isExecutable(fncomplete.c_str()) &&
436 (prefix == "" ||
437 fncomplete.substr(0, prefix.size()) == prefix)) {
438 m_apps.push_back(fncomplete);
439 // executables in $PATH ?
440 } else if (FbTk::FileUtil::isRegularFile(fncomplete.c_str()) &&
441 FbTk::FileUtil::isExecutable(fncomplete.c_str()) &&
442 (prefix == "" ||
443 filename.substr(0, prefix.size()) == prefix)) {
444 m_apps.push_back(filename);
445 } 472 }
446 } 473 }
474 dir.close();
475 l = r + 1;
447 } 476 }
448 l= r + 1;
449 dir.close();
450 }
451 }
452 sort(m_apps.begin(), m_apps.end());
453 unique(m_apps.begin(), m_apps.end());
454
455 m_last_completion_prefix = prefix;
456 changed_prefix= true;
457 m_current_apps_item= 0;
458 }
459
460 if (m_apps.empty() ) {
461 XBell(m_display, 0);
462 } else {
463 size_t apps_item = m_current_apps_item + (changed_prefix ? 0 : 1);
464 bool loop= false;
465
466 while (true) {
467 if (apps_item >= m_apps.size() ) {
468 loop = true;
469 apps_item = 0;
470 }
471
472 if ((!changed_prefix || loop) && apps_item == m_current_apps_item) {
473 break;
474 }
475 if (m_apps[apps_item].find(prefix) == 0) {
476 m_current_apps_item = apps_item;
477 if (add_dirs && FbTk::FileUtil::isDirectory(m_apps[m_current_apps_item].c_str()))
478 setText(m_apps[m_current_apps_item] + "/");
479 else
480 setText(m_apps[m_current_apps_item]);
481 cursorEnd();
482 break;
483 } 477 }
484 apps_item++; 478 sort(m_apps.begin(), m_apps.end());
479 unique(m_apps.begin(), m_apps.end());
485 } 480 }
486 if (!changed_prefix && apps_item == m_current_apps_item) 481 tabComplete(m_apps, m_current_apps_item);
487 XBell(m_display, 0);
488 } 482 }
489} 483}
490 484
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:
80 void adjustEndPos(); 80 void adjustEndPos();
81 void firstHistoryItem(); 81 void firstHistoryItem();
82 void lastHistoryItem(); 82 void lastHistoryItem();
83 void tabCompleteHistory(); 83 void tabComplete(const std::vector<std::string> &list, int &current, bool reverse = false);
84 void tabCompleteApps(); 84 void tabCompleteApps();
85 85
86 bool m_print; ///< the input should be printed to stdout rather than run 86 bool m_print; ///< the input should be printed to stdout rather than run
@@ -89,16 +89,20 @@ private:
89 int m_bevel; 89 int m_bevel;
90 FbTk::GContext m_gc; ///< graphic context 90 FbTk::GContext m_gc; ///< graphic context
91 bool m_end; ///< marks when this object is done 91 bool m_end; ///< marks when this object is done
92
92 std::vector<std::string> m_history; ///< history list of commands 93 std::vector<std::string> m_history; ///< history list of commands
93 std::string m_history_file; ///< holds filename for command history file 94 std::string m_history_file; ///< holds filename for command history file
94 size_t m_current_history_item; ///< holds current position in command history 95 int m_current_history_item; ///< holds current position in command history
95 std::string m_last_completion_prefix; ///< last prefix we completed on 96
96 97 std::vector<std::string> m_files;
97 typedef std::vector<std::string> AppsContainer; 98 int m_current_files_item;
98 typedef AppsContainer::iterator AppsContainerIt; 99 std::string m_last_completion_path; ///< last prefix we completed on
99 AppsContainer m_apps; ///< holds all apps in $PATH 100
100 size_t m_current_apps_item; ///< holds current position in apps-history 101 std::vector<std::string> m_apps;
101 102 int m_current_apps_item; ///< holds current position in apps-history
103
104 size_t m_completion_pos;
105
102 Cursor m_cursor; 106 Cursor m_cursor;
103 107
104 FbTk::FbPixmap m_pixmap; 108 FbTk::FbPixmap m_pixmap;