Utils/Termlib: Extend the completion API
[senf.git] / Utils / Termlib / Editor.cc
index f268efb..cceb4d8 100644 (file)
@@ -253,11 +253,13 @@ prefix_ void senf::term::BaseEditor::processKeys()
 }
 
 prefix_ unsigned senf::term::BaseEditor::width()
+    const
 {
     return terminal_->width();
 }
 
 prefix_ unsigned senf::term::BaseEditor::height()
+    const
 {
     return terminal_->height();
 }
@@ -276,7 +278,7 @@ prefix_ void senf::term::BaseEditor::write(std::string const & s)
 ///////////////////////////////////////////////////////////////////////////
 
 prefix_ senf::term::LineEditor::LineEditor(AbstractTerminal & terminal, AcceptCallback cb)
-    : BaseEditor(terminal), enabled_ (true), prompt_ ("$"), promptWidth_ (1u), editWidth_ (0u), 
+    : BaseEditor(terminal), enabled_ (false), prompt_ ("$"), promptWidth_ (1u), editWidth_ (0u), 
       text_ (""), point_ (0u), displayPos_ (0u), lastKey_ (0u), callback_ (cb), historyPoint_ (0u)
 {
     defineKey(KeyParser::Return,    &bindings::accept);
@@ -324,6 +326,11 @@ prefix_ void senf::term::LineEditor::show()
     if (enabled_)
         return;
     enabled_ = true;
+    for (unsigned n (0); n < auxDisplay_.size(); ++n) {
+        toLine(n+1);
+        put(auxDisplay_[n]);
+    }
+    toLine(0);
     forceRedisplay();
 }
 
@@ -331,6 +338,7 @@ prefix_ void senf::term::LineEditor::hide()
 {
     if (! enabled_)
         return;
+    reset();
     clearLine();
     enabled_ = false;
 }
@@ -340,7 +348,7 @@ prefix_ void senf::term::LineEditor::accept()
     if (enabled_)
         newline();
     hide();
-    pushHistory(text_);
+    pushHistory(text_, true);
     callback_(text_);
     clear();
 }
@@ -424,15 +432,16 @@ prefix_ void senf::term::LineEditor::insert(std::string const & text)
     redisplay();
 }
 
-prefix_ void senf::term::LineEditor::pushHistory(std::string const & text)
+prefix_ void senf::term::LineEditor::pushHistory(std::string const & text, bool accept)
 {
     if (! text.empty()
-        && (historyPoint_ == history_.size() || history_[historyPoint_] != text)
+        && (accept || historyPoint_ == history_.size() || history_[historyPoint_] != text)
         && (history_.empty() || history_.back() != text)) {
         history_.push_back(text);
         while (history_.size() > MAX_HISTORY_SIZE)
             history_.erase(history_.begin());
-        historyPoint_ = history_.size() - 1;
+        if (accept)
+            historyPoint_ = history_.size() - 1;
     }
 }
 
@@ -459,11 +468,14 @@ prefix_ void senf::term::LineEditor::nextHistory()
     }
 }
 
-prefix_ void senf::term::LineEditor::auxDisplay(int line, std::string const & text)
+prefix_ void senf::term::LineEditor::auxDisplay(unsigned line, std::string const & text)
 {
     toLine(line+1);
     clearLine();
     put(text);
+    while (auxDisplay_.size() < line+1)
+        auxDisplay_.push_back("");
+    auxDisplay_[line] = text;
 }
 
 prefix_ unsigned senf::term::LineEditor::maxAuxDisplayHeight()
@@ -474,6 +486,7 @@ prefix_ unsigned senf::term::LineEditor::maxAuxDisplayHeight()
 prefix_ void senf::term::LineEditor::clearAuxDisplay()
 {
     reset();
+    auxDisplay_.clear();
 }
 
 prefix_ std::string const & senf::term::LineEditor::text()
@@ -511,7 +524,7 @@ prefix_ bool senf::term::LineEditor::cb_init()
     if (!BaseEditor::cb_init())
         return false;
     prompt(prompt_);
-    forceRedisplay();
+    show();
     return true;
 }
 
@@ -569,6 +582,15 @@ prefix_ void senf::term::bindings::accept(LineEditor & editor)
     editor.accept();
 }
 
+prefix_ void senf::term::bindings::acceptWithRepeat(LineEditor & editor)
+{
+    if (editor.text().empty()) {
+        editor.prevHistory();
+        editor.forceRedisplay();
+    }
+    editor.accept();
+}
+
 prefix_ void senf::term::bindings::backwardDeleteChar(LineEditor & editor)
 {
     unsigned p (editor.point());
@@ -626,10 +648,18 @@ prefix_ void senf::term::bindings::complete(LineEditor & editor, Completer compl
 {
     typedef std::vector<std::string> Completions;
 
+    std::string text (editor.text());
     Completions completions;
-    completer(editor, 0, editor.point(), completions);
+    unsigned b (0);
+    unsigned e (editor.point());
+    std::string prefix;
+    completer(editor, b, e, prefix, completions);
     if (completions.empty())
         return;
+    if (e > text.size()) 
+        e = text.size();
+    if (b > e)
+        b = e;
     
     // Find common start string of all completions
     unsigned commonStart (completions[0].size());
@@ -644,27 +674,17 @@ prefix_ void senf::term::bindings::complete(LineEditor & editor, Completer compl
     }
 
     // Replace to-be-completed string with the common start string shared by all completions
-    std::string text (editor.text());
-    std::string completion (completions[0].substr(0, commonStart));
+    std::string completion (prefix+completions[0].substr(0, commonStart));
     bool didComplete (false);
-    if (text.substr(0, editor.point()) != completion) {
-        text.erase(0, editor.point());
-        text.insert(0, completion);
+    if (text.substr(b, e) != completion) {
+        text.erase(b, e);
+        text.insert(b, completion);
         didComplete = true;
     }
 
-    // If completion is already unique, make sure completion is followed by a space and place cursor
-    // after that space
-    if (completions.size() == 1u) {
-        if (text.size() <= commonStart || text[commonStart] != ' ')
-            text.insert(commonStart, " ");
-        editor.set(text, commonStart + 1);
-        return;
-    }
-
-    // Otherwise place cursor directly after the partial completion
-    editor.set(text, commonStart);
-    if (didComplete)
+    // Otherwise place cursor directly after the (possibly partial) completion
+    editor.set(text, b+prefix.size()+commonStart);
+    if (didComplete || completions.size() == 1)
         return;
 
     // Text was not changed, show list of possible completions
@@ -679,14 +699,12 @@ prefix_ void senf::term::bindings::complete(LineEditor & editor, Completer compl
     Completions::iterator i (completions.begin());
     for (unsigned row (0); row < nRows; ++row) {
         std::string line;
-        for (unsigned column (0); column < nColumns && i != completions.end(); ++column) {
+        for (unsigned column (0); column < nColumns && i != completions.end(); ++column, ++i) {
             std::string entry (colWidth, ' ');
-            if (i->size() > colWidth-2)
-                std::copy(i->begin(), i->begin()+colWidth-2, entry.begin());
-            else
-                std::copy(i->begin(), i->end(), entry.begin());
+            std::copy(i->begin(), 
+                      i->size() > colWidth-2 ? i->begin()+colWidth-2 : i->end(), 
+                      entry.begin());
             line += entry;
-            ++i;
         }
         editor.auxDisplay(row, line);
     }