diff --git a/sinksh/CMakeLists.txt b/sinksh/CMakeLists.txt index cfff36ce..3154b0cc 100644 --- a/sinksh/CMakeLists.txt +++ b/sinksh/CMakeLists.txt @@ -1,33 +1,34 @@ project(sinksh) set(sink_cli_SRCS main.cpp syntaxtree.cpp syntax_modules/core_syntax.cpp syntax_modules/sink_list.cpp syntax_modules/sink_clear.cpp syntax_modules/sink_count.cpp syntax_modules/sink_create.cpp syntax_modules/sink_modify.cpp syntax_modules/sink_remove.cpp syntax_modules/sink_stat.cpp syntax_modules/sink_sync.cpp syntax_modules/sink_trace.cpp syntax_modules/sink_inspect.cpp syntax_modules/sink_drop.cpp syntax_modules/sink_upgrade.cpp syntax_modules/sink_info.cpp syntax_modules/sink_livequery.cpp syntax_modules/sink_selftest.cpp sinksh_utils.cpp repl/repl.cpp repl/replStates.cpp + repl/commandline.cpp state.cpp utils.cpp) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) add_executable(${PROJECT_NAME} ${sink_cli_SRCS}) target_link_libraries(${PROJECT_NAME} Qt5::Core sink ${XAPIAN_LIBRARIES}) install(TARGETS ${PROJECT_NAME} ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/sinksh/repl/commandline.cpp b/sinksh/repl/commandline.cpp new file mode 100644 index 00000000..443cf3bc --- /dev/null +++ b/sinksh/repl/commandline.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 Christian Mollekopf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "commandline.h" + +#include "linenoise.hpp" + +void Commandline::loadHistory(const QString &path) +{ + linenoise::LoadHistory(path.toLocal8Bit()); +} + +void Commandline::saveHistory(const QString &path) +{ + linenoise::SaveHistory(path.toLocal8Bit()); +} + +void Commandline::addHistory(const std::string &line) +{ + linenoise::AddHistory(line.c_str()); +} + +void Commandline::setCompletionCallback(std::function&)> callback) +{ + linenoise::SetCompletionCallback(callback); +} + +bool Commandline::readline(const char *prompt, std::string &line) +{ + return linenoise::Readline(prompt, line); +} diff --git a/sinksh/repl/commandline.h b/sinksh/repl/commandline.h new file mode 100644 index 00000000..dbd6bd40 --- /dev/null +++ b/sinksh/repl/commandline.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 Christian Mollekopf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#pragma once + +#include +#include +#include + +/* + * Wrapper for linenoise. + * + * Because global variables in header files don't work when included from multiple places. + */ +namespace Commandline { + void loadHistory(const QString &); + void saveHistory(const QString &); + void addHistory(const std::string &); + void setCompletionCallback(std::function&)>); + bool readline(const char *prompt, std::string &line); +}; diff --git a/sinksh/repl/repl.cpp b/sinksh/repl/repl.cpp index 32932cb2..c8f9b454 100644 --- a/sinksh/repl/repl.cpp +++ b/sinksh/repl/repl.cpp @@ -1,87 +1,87 @@ /* * Copyright (C) 2014 Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "repl.h" #include #include #include #include #include #include "replStates.h" #include "syntaxtree.h" -#include "linenoise.hpp" +#include "commandline.h" Repl::Repl(QObject *parent) : QStateMachine(parent) { - linenoise::LoadHistory(commandHistoryPath().toLocal8Bit()); + Commandline::loadHistory(commandHistoryPath()); // create all states ReadState *read = new ReadState(this); UnfinishedReadState *unfinishedRead = new UnfinishedReadState(this); EvalState *eval = new EvalState(this); PrintState *print = new PrintState(this); QFinalState *final = new QFinalState(this); // connect the transitions read->addTransition(read, SIGNAL(command(QString)), eval); read->addTransition(read, SIGNAL(exitRequested()), final); unfinishedRead->addTransition(unfinishedRead, SIGNAL(command(QString)), eval); unfinishedRead->addTransition(unfinishedRead, SIGNAL(exitRequested()), final); eval->addTransition(eval, SIGNAL(completed()), read); eval->addTransition(eval, SIGNAL(continueInput()), unfinishedRead); eval->addTransition(eval, SIGNAL(output(QString)), print); print->addTransition(print, SIGNAL(completed()), eval); setInitialState(read); printWelcomeBanner(); start(); } Repl::~Repl() { - linenoise::SaveHistory(commandHistoryPath().toLocal8Bit()); + Commandline::saveHistory(commandHistoryPath()); } void Repl::printWelcomeBanner() { QTextStream out(stdout); out << QObject::tr("Welcome to the Sink interative shell!\n"); out << QObject::tr("Type `help` for information on the available commands.\n"); out.flush(); } QString Repl::commandHistoryPath() { const QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation); if (!QFile::exists(path)) { QDir dir; dir.mkpath(path); } return path + "/repl_history"; } #include "moc_repl.cpp" diff --git a/sinksh/repl/replStates.cpp b/sinksh/repl/replStates.cpp index c4b08b7b..38ebdf20 100644 --- a/sinksh/repl/replStates.cpp +++ b/sinksh/repl/replStates.cpp @@ -1,152 +1,152 @@ /* * Copyright (C) 2014 Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "replStates.h" #include #include #include #include -#include "linenoise.hpp" +#include "commandline.h" #include "syntaxtree.h" ReadState::ReadState(QState *parent) : QState(parent) { - linenoise::SetCompletionCallback([](const char* editBuffer, std::vector& completions) { + Commandline::setCompletionCallback([](const char* editBuffer, std::vector& completions) { QStringList words = QString(editBuffer).split(" ", QString::SkipEmptyParts); const QString fragment = words.takeLast(); Syntax::List nearest = SyntaxTree::self()->nearestSyntax(words, fragment); if (nearest.isEmpty()) { SyntaxTree::Command command = SyntaxTree::self()->match(words); if (command.first && command.first->completer) { QStringList commandCompletions = command.first->completer(words, fragment, SyntaxTree::self()->state()); for (const auto &c : commandCompletions) { completions.push_back(c.toStdString()); } } } else { for (const auto &n : nearest) { completions.push_back(n.keyword.toStdString()); } } }); } void ReadState::onEntry(QEvent *event) { Q_UNUSED(event) std::string line; - if (linenoise::Readline(prompt(), line)) { + if (Commandline::readline(prompt(), line)) { std::cout << std::endl; emit exitRequested(); return; } // we have actual data, so let's wait for a full line of text const QString text = QString::fromStdString(line).simplified(); if (text.length() > 0) { - linenoise::AddHistory(line.c_str()); + Commandline::addHistory(line); } emit command(text); } const char *ReadState::prompt() const { return "> "; } UnfinishedReadState::UnfinishedReadState(QState *parent) : ReadState(parent) { } const char *UnfinishedReadState::prompt() const { return " "; } EvalState::EvalState(QState *parent) : QState(parent) { } void EvalState::onEntry(QEvent *event) { QStateMachine::SignalEvent *e = dynamic_cast(event); const QString command = e ? e->arguments()[0].toString() : QString(); if (command.isEmpty()) { complete(); return; } if (command.right(1) == "\\") { m_partial += " " + command.left(command.size() - 1); continueInput(); } else { m_partial += " " + command; complete(); } } void EvalState::complete() { m_partial = m_partial.simplified(); if (!m_partial.isEmpty()) { //emit output("Processing ... " + command); const QStringList commands = SyntaxTree::tokenize(m_partial); SyntaxTree::self()->run(commands); m_partial.clear(); } emit completed(); } PrintState::PrintState(QState *parent) : QState(parent) { } void PrintState::onEntry(QEvent *event) { QStateMachine::SignalEvent *e = dynamic_cast(event); if (e && !e->arguments().isEmpty()) { const QString command = e->arguments()[0].toString(); QTextStream stream(stdout); stream << command << "\n"; } emit completed(); } //Ignore warning I don't know how to fix in a moc file #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundefined-reinterpret-cast" #include "moc_replStates.cpp" #pragma clang diagnostic pop