X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=Utils%2FTermlib%2FEditor.cc;h=f268efbe7003bb04195edc3a2285d30a13b0d5fa;hb=0461eef93912cb9d454d726b4a7b4ccf50ed31bd;hp=57e1e78adc427fb91a88f042ac35c2037282ab4f;hpb=27d5a4aaebd8abb6c6bb842af3c170063b206f0f;p=senf.git diff --git a/Utils/Termlib/Editor.cc b/Utils/Termlib/Editor.cc index 57e1e78..f268efb 100644 --- a/Utils/Termlib/Editor.cc +++ b/Utils/Termlib/Editor.cc @@ -39,14 +39,15 @@ prefix_ senf::term::BaseEditor::BaseEditor(AbstractTerminal & terminal) keyTimeout_ (senf::ClockService::milliseconds(DEFAULT_KEY_TIMEOUT_MS)), timer_ ("senf::term::BaseEditor::keySequenceTimeout", senf::membind(&BaseEditor::keySequenceTimeout, this)), - column_ (0u) + column_ (0u), displayHeight_ (1u), line_ (0u) { terminal_->setCallbacks(*this); } prefix_ void senf::term::BaseEditor::newline() { - write("\r\n"); + reset(); + write("\n"); write(tifo_.getString(Terminfo::properties::ClrEol)); column_ = 0; } @@ -124,12 +125,77 @@ prefix_ void senf::term::BaseEditor::setNormal() write(tifo_.getString(Terminfo::properties::ExitAttributeMode)); } +prefix_ void senf::term::BaseEditor::maybeClrScr() +{ + if (tifo_.hasProperty(Terminfo::properties::ClearScreen)) + write(tifo_.getString(Terminfo::properties::ClearScreen)); +} + +prefix_ void senf::term::BaseEditor::toLine(unsigned l) +{ + if (l >= height()) + l = height() - 1; + unsigned ll (l); + if (ll >= displayHeight_) + ll = displayHeight_-1; + if (ll > line_) { + if (tifo_.hasProperty(Terminfo::properties::ParmDownCursor)) { + write(tifo_.formatString(Terminfo::properties::ParmDownCursor, ll - line_)); + line_ = ll; + } + else { + char const * cud1 (tifo_.getString(Terminfo::properties::CursorDown)); + while (ll > line_) { + write(cud1); + ++line_; + } + } + } + else if (ll < line_) { + if (tifo_.hasProperty(Terminfo::properties::ParmUpCursor)) { + write(tifo_.formatString(Terminfo::properties::ParmUpCursor, line_ - ll)); + line_ = ll; + } + else { + char const * cuu1 (tifo_.getString(Terminfo::properties::CursorUp)); + while (ll < line_) { + write(cuu1); + --line_; + } + } + } + while (line_ < l) { + write("\n"); + write(tifo_.getString(Terminfo::properties::ClrEol)); + ++displayHeight_; + ++line_; + } + write('\r'); + column_ = 0; +} + +prefix_ void senf::term::BaseEditor::reset() +{ + for (unsigned i (1); i < displayHeight_; ++i) { + toLine(i); + clearLine(); + } + toLine(0); + displayHeight_ = 1; +} + prefix_ unsigned senf::term::BaseEditor::currentColumn() const { return column_; } +prefix_ unsigned senf::term::BaseEditor::currentLine() + const +{ + return line_; +} + prefix_ bool senf::term::BaseEditor::cb_init() { try { @@ -148,6 +214,7 @@ prefix_ bool senf::term::BaseEditor::cb_init() if (tifo_.hasProperty(Terminfo::properties::KeypadXmit)) write(tifo_.getString(Terminfo::properties::KeypadXmit)); + return true; } prefix_ void senf::term::BaseEditor::cb_charReceived(char c) @@ -190,6 +257,11 @@ prefix_ unsigned senf::term::BaseEditor::width() return terminal_->width(); } +prefix_ unsigned senf::term::BaseEditor::height() +{ + return terminal_->height(); +} + prefix_ void senf::term::BaseEditor::write(char ch) { terminal_->write(ch); @@ -205,11 +277,13 @@ 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), - text_ (""), point_ (0u), displayPos_ (0u), lastKey_ (0u), callback_ (cb) + text_ (""), point_ (0u), displayPos_ (0u), lastKey_ (0u), callback_ (cb), historyPoint_ (0u) { defineKey(KeyParser::Return, &bindings::accept); defineKey(KeyParser::Right, &bindings::forwardChar); defineKey(KeyParser::Left, &bindings::backwardChar); + defineKey(KeyParser::Up, &bindings::prevHistory); + defineKey(KeyParser::Down, &bindings::nextHistory); defineKey(KeyParser::Backspace, &bindings::backwardDeleteChar); defineKey(KeyParser::Delete, &bindings::deleteChar); defineKey(KeyParser::Home, &bindings::beginningOfLine); @@ -219,6 +293,7 @@ prefix_ senf::term::LineEditor::LineEditor(AbstractTerminal & terminal, AcceptCa defineKey(KeyParser::Ctrl('E'), &bindings::endOfLine); defineKey(KeyParser::Ctrl('D'), &bindings::deleteChar); defineKey(KeyParser::Ctrl('C'), &bindings::restartEdit); + defineKey(KeyParser::Ctrl('L'), &bindings::clearScreen); } prefix_ void senf::term::LineEditor::prompt(std::string const & text) @@ -249,7 +324,7 @@ prefix_ void senf::term::LineEditor::show() if (enabled_) return; enabled_ = true; - redisplay(); + forceRedisplay(); } prefix_ void senf::term::LineEditor::hide() @@ -265,6 +340,7 @@ prefix_ void senf::term::LineEditor::accept() if (enabled_) newline(); hide(); + pushHistory(text_); callback_(text_); clear(); } @@ -272,6 +348,7 @@ prefix_ void senf::term::LineEditor::accept() prefix_ void senf::term::LineEditor::clear() { set(""); + historyPoint_ = history_.size(); } prefix_ void senf::term::LineEditor::redisplay() @@ -347,6 +424,58 @@ prefix_ void senf::term::LineEditor::insert(std::string const & text) redisplay(); } +prefix_ void senf::term::LineEditor::pushHistory(std::string const & text) +{ + if (! text.empty() + && (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; + } +} + +prefix_ void senf::term::LineEditor::prevHistory() +{ + if (historyPoint_ <= 0) + return; + pushHistory(text_); + std::string entry (history_[--historyPoint_]); + set(entry, entry.size()); +} + +prefix_ void senf::term::LineEditor::nextHistory() +{ + if (historyPoint_ >= history_.size()) + return; + pushHistory(text_); + ++ historyPoint_; + if (historyPoint_ >= history_.size()) + set(""); + else { + std::string entry (history_[historyPoint_]); + set(entry, entry.size()); + } +} + +prefix_ void senf::term::LineEditor::auxDisplay(int line, std::string const & text) +{ + toLine(line+1); + clearLine(); + put(text); +} + +prefix_ unsigned senf::term::LineEditor::maxAuxDisplayHeight() +{ + return height()-1; +} + +prefix_ void senf::term::LineEditor::clearAuxDisplay() +{ + reset(); +} + prefix_ std::string const & senf::term::LineEditor::text() { return text_; @@ -389,6 +518,7 @@ prefix_ bool senf::term::LineEditor::cb_init() prefix_ void senf::term::LineEditor::cb_windowSizeChanged() { BaseEditor::cb_windowSizeChanged(); + clearAuxDisplay(); prompt(prompt_); gotoChar(point_); forceRedisplay(); @@ -398,14 +528,19 @@ prefix_ void senf::term::LineEditor::v_keyReceived(keycode_t key) { if (! enabled_) return; + clearAuxDisplay(); lastKey_ = key; KeyMap::iterator i (bindings_.find(key)); if (i != bindings_.end()) i->second(*this); else if (key >= ' ' && key < 256) insert(char(key)); + if (currentLine() != 0) + toLine(0); if (redisplayNeeded_) forceRedisplay(); + else + toColumn(point_ - displayPos_ + promptWidth_ + 1); } /////////////////////////////////////////////////////////////////////////// @@ -470,6 +605,93 @@ prefix_ void senf::term::bindings::restartEdit(LineEditor & editor) editor.redisplay(); } +prefix_ void senf::term::bindings::prevHistory(LineEditor & editor) +{ + editor.prevHistory(); +} + +prefix_ void senf::term::bindings::nextHistory(LineEditor & editor) +{ + editor.nextHistory(); +} + +prefix_ void senf::term::bindings::clearScreen(LineEditor & editor) +{ + editor.maybeClrScr(); + editor.clearLine(); + editor.forceRedisplay(); +} + +prefix_ void senf::term::bindings::complete(LineEditor & editor, Completer completer) +{ + typedef std::vector Completions; + + Completions completions; + completer(editor, 0, editor.point(), completions); + if (completions.empty()) + return; + + // Find common start string of all completions + unsigned commonStart (completions[0].size()); + unsigned maxLen (commonStart); + for (Completions::const_iterator i (boost::next(completions.begin())); + i != completions.end(); ++i) { + if (i->size() > maxLen) + maxLen = i->size(); + unsigned n (0u); + for (; n < commonStart && n < i->size() && completions[0][n] == (*i)[n]; ++n) ; + commonStart = n; + } + + // 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)); + bool didComplete (false); + if (text.substr(0, editor.point()) != completion) { + text.erase(0, editor.point()); + text.insert(0, 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) + return; + + // Text was not changed, show list of possible completions + unsigned colWidth (maxLen+2); + unsigned nColumns ((editor.width()-1) / colWidth); + if (nColumns < 1) nColumns = 1; + unsigned nRows ((completions.size()+nColumns-1) / nColumns); + if (nRows > editor.maxAuxDisplayHeight()) { + editor.auxDisplay(0, "(too many completions)"); + return; + } + 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) { + 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()); + line += entry; + ++i; + } + editor.auxDisplay(row, line); + } +} + ///////////////////////////////cc.e//////////////////////////////////////// #undef prefix_ //#include "Editor.mpp"