/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Miodrag Milanovic * Copyright (C) 2018 Alex Tsui * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include "line_editor.h" #include #include #include #include #include "ColumnFormatter.h" #include "Utils.h" #include "pyinterpreter.h" NEXTPNR_NAMESPACE_BEGIN LineEditor::LineEditor(ParseHelper *helper, QWidget *parent) : QLineEdit(parent), index(0), parseHelper(helper) { setContextMenuPolicy(Qt::CustomContextMenu); QAction *clearAction = new QAction("Clear &history", this); clearAction->setStatusTip("Clears line edit history"); connect(clearAction, &QAction::triggered, this, &LineEditor::clearHistory); contextMenu = createStandardContextMenu(); contextMenu->addSeparator(); contextMenu->addAction(clearAction); connect(this, &LineEditor::returnPressed, this, &LineEditor::textInserted); connect(this, &LineEditor::customContextMenuRequested, this, &LineEditor::showContextMenu); } void LineEditor::keyPressEvent(QKeyEvent *ev) { if (ev->matches(QKeySequence::Paste)) { QString clipboard = QApplication::clipboard()->text(); if (clipboard.isEmpty()) return; if (clipboard.contains('\n')) { QStringList clipboard_lines = clipboard.split('\n'); for (int i = 0; i < clipboard_lines.size(); i++) { addLineToHistory(clipboard_lines[i]); Q_EMIT textLineInserted(clipboard_lines[i]); } return; } } else if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) { QToolTip::hideText(); if (lines.empty()) return; if (ev->key() == Qt::Key_Up) index--; if (ev->key() == Qt::Key_Down) index++; if (index < 0) index = 0; if (index >= lines.size()) { index = lines.size(); clear(); return; } setText(lines[index]); } else if (ev->key() == Qt::Key_Escape) { QToolTip::hideText(); clear(); return; } else if (ev->key() == Qt::Key_Tab) { autocomplete(); return; } QToolTip::hideText(); QLineEdit::keyPressEvent(ev); } // This makes TAB work bool LineEditor::focusNextPrevChild(bool next) { return false; } void LineEditor::textInserted() { addLineToHistory(text()); clear(); Q_EMIT textLineInserted(lines.back()); } void LineEditor::addLineToHistory(QString line) { if (lines.empty() || lines.back() != line) lines += line; if (lines.size() > 100) lines.removeFirst(); index = lines.size(); } void LineEditor::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); } void LineEditor::clearHistory() { lines.clear(); index = 0; clear(); } void LineEditor::autocomplete() { QString line = text(); const std::list &suggestions = pyinterpreter_suggest(line.toStdString()); if (suggestions.size() == 1) { line = suggestions.back().c_str(); } else { // try to complete to longest common prefix std::string prefix = LongestCommonPrefix(suggestions.begin(), suggestions.end()); if (prefix.size() > (size_t)line.size()) { line = prefix.c_str(); } else { ColumnFormatter fmt; fmt.setItems(suggestions.begin(), suggestions.end()); fmt.format(width() / 5); QString out = ""; for (auto &it : fmt.formattedOutput()) { if (!out.isEmpty()) out += "\n"; out += it.c_str(); } QToolTip::setFont(font()); if (!out.trimmed().isEmpty()) QToolTip::showText(mapToGlobal(QPoint(0, 0)), out); } } // set up the next line on the console setText(line); } NEXTPNR_NAMESPACE_END