[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [akonadi-next/feature/new_cli] /: initial import of the new cli app
From: Aaron Seigo <aseigo () kde ! org>
Date: 2015-12-23 11:09:11
Message-ID: E1aBhI7-0008IN-Sv () scm ! kde ! org
[Download RAW message or body]
Git commit 255d73af197faf8437343abc10bd98cca2057a1e by Aaron Seigo.
Committed on 23/12/2015 at 09:47.
Pushed by aseigo into branch 'feature/new_cli'.
initial import of the new cli app
M +3 -0 CMakeLists.txt
A +16 -0 akonadi2_cli/CMakeLists.txt
A +71 -0 akonadi2_cli/main.cpp [License: GPL (v2+)]
A +195 -0 akonadi2_cli/module.cpp [License: GPL (v2+)]
A +61 -0 akonadi2_cli/module.h [License: GPL (v2+)]
A +39 -0 akonadi2_cli/modules/exit/exit.cpp [License: GPL (v2+)]
A +37 -0 akonadi2_cli/modules/exit/exit.h [License: GPL (v2+)]
A +80 -0 akonadi2_cli/modules/help/help.cpp [License: GPL (v2+)]
A +37 -0 akonadi2_cli/modules/help/help.h [License: GPL (v2+)]
A +78 -0 akonadi2_cli/repl/repl.cpp [License: GPL (v2+)]
A +34 -0 akonadi2_cli/repl/repl.h [License: GPL (v2+)]
A +155 -0 akonadi2_cli/repl/replStates.cpp [License: GPL (v2+)]
A +85 -0 akonadi2_cli/repl/replStates.h [License: GPL (v2+)]
A +28 -0 akonadi2_cli/state.cpp [License: GPL (v2+)]
A +29 -0 akonadi2_cli/state.h [License: GPL (v2+)]
A +47 -0 cmake/modules/FindReadline.cmake
http://commits.kde.org/akonadi-next/255d73af197faf8437343abc10bd98cca2057a1e
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c480ddd..d92f830 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -57,4 +57,7 @@ add_subdirectory(examples)
# some tests
add_subdirectory(tests)
+# cli
+add_subdirectory(akonadi2_cli)
+
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/akonadi2_cli/CMakeLists.txt b/akonadi2_cli/CMakeLists.txt
new file mode 100644
index 0000000..b5bdb46
--- /dev/null
+++ b/akonadi2_cli/CMakeLists.txt
@@ -0,0 +1,16 @@
+project(akonadi2_cli)
+
+find_package(Readline REQUIRED)
+
+
+set(akonadi2_SRCS
+ main.cpp
+ module.cpp
+ modules/exit/exit.cpp
+ modules/help/help.cpp
+ repl/repl.cpp
+ repl/replStates.cpp
+ state.cpp)
+
+add_executable(${PROJECT_NAME} ${akonadi2_SRCS})
+
diff --git a/akonadi2_cli/main.cpp b/akonadi2_cli/main.cpp
new file mode 100644
index 0000000..1a036d0
--- /dev/null
+++ b/akonadi2_cli/main.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
+ *
+ * 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 <unistd.h>
+
+#include <QCoreApplication>
+#include <QDebug>
+
+#include "module.h"
+// #include "jsonlistener.h"
+#include "repl/repl.h"
+
+/*
+ * modes of operation:
+ *
+ * 1. called with no commands: start the REPL and listen for JSON on stin
+ * 2. called with -: listen for JSON on stdin
+ * 3. called with commands: try to match to syntx
+ */
+
+int main(int argc, char *argv[])
+{
+ // load all modules
+ Module::loadModules();
+
+ const bool interactive = isatty(fileno(stdin));
+ const bool startRepl = (argc == 1) && interactive;
+ //TODO: make a json command parse cause that would be awesomesauce
+ const bool startJsonListener = !startRepl &&
+ (argc == 2 && qstrcmp(argv[1], "-") == 0);
+ qDebug() << "state at startup is" << interactive << startRepl << \
startJsonListener; +
+ QCoreApplication app(argc, argv);
+ app.setApplicationName("funq");
+
+ if (startRepl || startJsonListener) {
+ if (startRepl) {
+ Repl *repl = new Repl;
+ QObject::connect(repl, &QStateMachine::finished,
+ repl, &QObject::deleteLater);
+ QObject::connect(repl, &QStateMachine::finished,
+ &app, &QCoreApplication::quit);
+ }
+
+ if (startJsonListener) {
+// JsonListener listener(syntax);
+ }
+
+ return app.exec();
+ }
+
+ QStringList commands = app.arguments();
+ commands.removeFirst();
+ return Module::run(commands);
+}
diff --git a/akonadi2_cli/module.cpp b/akonadi2_cli/module.cpp
new file mode 100644
index 0000000..0b83d98
--- /dev/null
+++ b/akonadi2_cli/module.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
+ *
+ * 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 "module.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+
+// TODO: needs a proper registry; making "core" modules plugins is
+// almost certainly overkill, but this is not the way either
+#include "modules/exit/exit.h"
+#include "modules/help/help.h"
+
+QList<Module> Module::s_modules;
+State Module::s_state;
+
+Module::Syntax::Syntax()
+{
+}
+
+Module::Syntax::Syntax(const QString &k, std::function<bool(const QStringList &, \
State &)> l, const QString &helpText, bool e) + : keyword(k),
+ lambda(l),
+ help(helpText),
+ eventDriven(e)
+{
+}
+
+Module::Module()
+{
+}
+
+void Module::loadModules()
+{
+ addModule(CLI::Exit());
+ addModule(CLI::Help());
+}
+
+void Module::addModule(const Module &module)
+{
+ s_modules.append(module);
+}
+
+QList<Module> Module::modules()
+{
+ return s_modules;
+}
+
+Module::Command Module::match(const QStringList &commands)
+{
+ Command command;
+ for (const Module &module: s_modules) {
+ command = module.matches(commands);
+ if (command.first) {
+ break;
+ }
+ }
+
+ return command;
+}
+
+Module::Syntax Module::syntax() const
+{
+ return m_syntax;
+}
+
+void Module::setSyntax(const Syntax &syntax)
+{
+ m_syntax = syntax;
+}
+
+bool Module::run(const QStringList &commands)
+{
+ Command command = match(commands);
+ if (command.first && command.first->lambda) {
+ bool rv = command.first->lambda(command.second, s_state);
+ if (rv && command.first->eventDriven) {
+ return QCoreApplication::instance()->exec();
+ }
+
+ return rv;
+ }
+
+ return false;
+}
+
+Module::Command Module::matches(const QStringList &commandLine) const
+{
+ if (commandLine.isEmpty()) {
+ return Command();
+ }
+
+ QStringListIterator commandLineIt(commandLine);
+
+ if (commandLineIt.next() != m_syntax.keyword) {
+ return Command();
+ }
+
+ QListIterator<Syntax> syntaxIt(m_syntax.children);
+ const Syntax *syntax = &m_syntax;
+ QStringList tailCommands;
+ while (commandLineIt.hasNext() && syntaxIt.hasNext()) {
+ const QString word = commandLineIt.next();
+ while (syntaxIt.hasNext()) {
+ const Syntax &child = syntaxIt.next();
+ if (word == child.keyword) {
+ syntax = &child;
+ syntaxIt = child.children;
+ }
+ }
+
+ if (!syntaxIt.hasNext()) {
+ tailCommands << word;
+ break;
+ }
+ }
+
+ if (syntax && syntax->lambda) {
+ while (commandLineIt.hasNext()) {
+ tailCommands << commandLineIt.next();
+ }
+
+ return std::make_pair(syntax, tailCommands);
+ }
+
+ return Command();
+}
+
+QVector<Module::Syntax> Module::nearestSyntax(const QStringList &words, const \
QString &fragment) +{
+ QVector<Module::Syntax> matches;
+
+ //qDebug() << "words are" << words;
+ if (words.isEmpty()) {
+ for (const Module &module: s_modules) {
+ if (module.syntax().keyword.startsWith(fragment)) {
+ matches.push_back(module.syntax());
+ }
+ }
+ } else {
+ QStringListIterator wordIt(words);
+ QString word = wordIt.next();
+ Syntax lastFullSyntax;
+
+ for (const Module &module: s_modules) {
+ if (module.syntax().keyword == word) {
+ lastFullSyntax = module.syntax();
+ QListIterator<Syntax> syntaxIt(module.syntax().children);
+ while (wordIt.hasNext()) {
+ word = wordIt.next();
+ while (syntaxIt.hasNext()) {
+ const Syntax &child = syntaxIt.next();
+ if (word == child.keyword) {
+ lastFullSyntax = child;
+ syntaxIt = child.children;
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ //qDebug() << "exiting with" << lastFullSyntax.keyword << words.last();
+ if (lastFullSyntax.keyword == words.last()) {
+ QListIterator<Syntax> syntaxIt(lastFullSyntax.children);
+ while (syntaxIt.hasNext()) {
+ Syntax syntax = syntaxIt.next();
+ if (fragment.isEmpty() || syntax.keyword.startsWith(fragment)) {
+ matches.push_back(syntax);
+ }
+ }
+ }
+ }
+
+ return matches;
+}
+
+
diff --git a/akonadi2_cli/module.h b/akonadi2_cli/module.h
new file mode 100644
index 0000000..4f426b8
--- /dev/null
+++ b/akonadi2_cli/module.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
+ *
+ * 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 "state.h"
+
+#include <QStringList>
+#include <QVector>
+
+class Module
+{
+public:
+ struct Syntax
+ {
+ Syntax();
+ Syntax(const QString &keyword, std::function<bool(const QStringList &, State \
&)> lambda = std::function<bool(const QStringList &, State &)>(), const QString \
&helpText = QString(), bool eventDriven = false); + QString keyword;
+ std::function<bool(const QStringList &, State &)> lambda;
+ QList<Syntax> children;
+ QString help;
+ bool eventDriven;
+ };
+
+ typedef std::pair<const Syntax *, QStringList> Command;
+
+ static void addModule(const Module &module);
+ static QList<Module> modules();
+ static Command match(const QStringList &commands);
+ static bool run(const QStringList &commands);
+ static void loadModules();
+ static QVector<Syntax>nearestSyntax(const QStringList &words, const QString \
&fragment); +
+ Module();
+ Module::Syntax syntax() const;
+ void setSyntax(const Syntax &syntax);
+
+private:
+ Command matches(const QStringList &commands) const;
+
+ Syntax m_syntax;
+ static QList<Module> s_modules;
+ static State s_state;
+};
+
diff --git a/akonadi2_cli/modules/exit/exit.cpp b/akonadi2_cli/modules/exit/exit.cpp
new file mode 100644
index 0000000..64828be
--- /dev/null
+++ b/akonadi2_cli/modules/exit/exit.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
+ *
+ * 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 "exit.h"
+
+#include <QObject>
+
+namespace CLI
+{
+
+Exit::Exit()
+{
+ setSyntax(Syntax("exit", &Exit::exit, QObject::tr("Exits the application. Ctrl-d \
also works!"))); +}
+
+bool Exit::exit(const QStringList &, State &)
+{
+ ::exit(0);
+ return true;
+}
+
+} // namespace CLI
+
diff --git a/akonadi2_cli/modules/exit/exit.h b/akonadi2_cli/modules/exit/exit.h
new file mode 100644
index 0000000..5ed4174
--- /dev/null
+++ b/akonadi2_cli/modules/exit/exit.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
+ *
+ * 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 "module.h"
+
+namespace CLI
+{
+
+class Exit : public Module
+{
+public:
+ Exit();
+
+private:
+ static bool exit(const QStringList &commands, State &state);
+};
+
+} // namespace CLI
+
diff --git a/akonadi2_cli/modules/help/help.cpp b/akonadi2_cli/modules/help/help.cpp
new file mode 100644
index 0000000..aaff6fb
--- /dev/null
+++ b/akonadi2_cli/modules/help/help.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
+ *
+ * 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 "help.h"
+
+#include <QObject>
+#include <QSet>
+#include <QTextStream>
+
+#include "module.h"
+
+namespace CLI
+{
+
+Help::Help()
+{
+ Syntax topLevel = Syntax(QObject::tr("help"), &Help::showHelp, \
QObject::tr("Print command information: help [command]")); + setSyntax(topLevel);
+}
+
+bool Help::showHelp(const QStringList &commands, State &)
+{
+ Module::Command command = Module::match(commands);
+ QTextStream stream(stdout);
+ if (commands.isEmpty()) {
+ stream << QObject::tr("Welcome to the Akonadi2 command line tool!") << "\n";
+ stream << QObject::tr("Top-level commands:") << "\n";
+ QSet<QString> sorted;
+ for (auto module: Module::modules()) {
+ sorted.insert(module.syntax().keyword);
+ }
+
+ for (auto keyword: sorted) {
+ stream << "\t" << keyword << "\n";
+ }
+ } else if (const Syntax *syntax = command.first) {
+ //TODO: get parent!
+ stream << QObject::tr("Command `%1`").arg(syntax->keyword);
+
+ if (!syntax->help.isEmpty()) {
+ stream << ": " << syntax->help;
+ }
+ stream << "\n";
+
+ if (!syntax->children.isEmpty()) {
+ stream << "\tSub-commands:\n";
+ QSet<QString> sorted;
+ for (auto childSyntax: syntax->children) {
+ sorted.insert(childSyntax.keyword);
+ }
+
+ for (auto keyword: sorted) {
+ stream << "\t" << keyword << "\n";
+ }
+ }
+ } else {
+ stream << "Unknown command: " << commands.join(" ") << "\n";
+ }
+
+ return true;
+}
+
+} // namespace CLI
+
diff --git a/akonadi2_cli/modules/help/help.h b/akonadi2_cli/modules/help/help.h
new file mode 100644
index 0000000..df1cfc2
--- /dev/null
+++ b/akonadi2_cli/modules/help/help.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
+ *
+ * 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 "module.h"
+
+namespace CLI
+{
+
+class Help : public Module
+{
+public:
+ Help();
+
+private:
+ static bool showHelp(const QStringList &commands, State &state);
+};
+
+} // namespace CLI
+
diff --git a/akonadi2_cli/repl/repl.cpp b/akonadi2_cli/repl/repl.cpp
new file mode 100644
index 0000000..395661e
--- /dev/null
+++ b/akonadi2_cli/repl/repl.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
+ *
+ * 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 <readline/history.h>
+
+#include <QDir>
+#include <QFile>
+#include <QFinalState>
+#include <QStandardPaths>
+
+#include "replStates.h"
+
+Repl::Repl(QObject *parent)
+ : QStateMachine(parent)
+{
+ // readline history setup
+ using_history();
+ read_history(commandHistoryPath().toLocal8Bit());
+
+ // 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);
+ start();
+}
+
+Repl::~Repl()
+{
+ // readline history writing
+ write_history(commandHistoryPath().toLocal8Bit());
+}
+
+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/akonadi2_cli/repl/repl.h b/akonadi2_cli/repl/repl.h
new file mode 100644
index 0000000..b76c66b
--- /dev/null
+++ b/akonadi2_cli/repl/repl.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
+ *
+ * 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 <QStateMachine>
+
+class Repl : public QStateMachine
+{
+ Q_OBJECT
+
+public:
+ Repl(QObject *parent = 0);
+ ~Repl();
+
+private:
+ static QString commandHistoryPath();
+};
diff --git a/akonadi2_cli/repl/replStates.cpp b/akonadi2_cli/repl/replStates.cpp
new file mode 100644
index 0000000..e87dd5f
--- /dev/null
+++ b/akonadi2_cli/repl/replStates.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
+ *
+ * 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 <unistd.h>
+#include <iostream>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <QDebug>
+#include <QEvent>
+#include <QStateMachine>
+
+#include "module.h"
+
+static char *akonadi2_cli_next_tab_complete_match(const char *text, int state);
+static char ** akonadi2_cli_tab_completion(const char *text, int start, int end);
+
+ReadState::ReadState(QState *parent)
+ : QState(parent)
+{
+ rl_completion_entry_function = akonadi2_cli_next_tab_complete_match;
+ rl_attempted_completion_function = akonadi2_cli_tab_completion;
+}
+
+void ReadState::onEntry(QEvent *event)
+{
+ Q_UNUSED(event)
+ char *line = readline(prompt());
+
+ if (!line) {
+ std::cout << std::endl;
+ emit exitRequested();
+ return;
+ }
+
+ // we have actual data, so let's wait for a full line of text
+ QByteArray input(line);
+ const QString text = QString(line).simplified();
+ //qDebug() << "Line is ... " << text;
+
+ if (text.length() > 0) {
+ add_history(line);
+ }
+
+ free(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),
+ m_complete(false)
+{
+}
+
+void EvalState::onEntry(QEvent *event)
+{
+ QStateMachine::SignalEvent *e = \
dynamic_cast<QStateMachine::SignalEvent*>(event); +
+ if (!e || e->arguments().isEmpty()) {
+ if (m_complete) {
+ emit completed();
+ } else {
+ emit continueInput();
+ }
+ return;
+ }
+
+ //TODO: properly tokenize (e.g. "foo bar" should not become ['"foo', 'bar"']
+ const QString command = e->arguments()[0].toString();
+
+ if (!command.isEmpty()) {
+ m_complete = command.right(1) != "\\";
+ if (m_complete) {
+ //emit output("Processing ... " + command);
+ const QStringList commands = command.split(" ");
+ Module::run(commands);
+ emit completed();
+ }
+ }
+}
+
+PrintState::PrintState(QState *parent)
+ : QState(parent)
+{
+}
+
+void PrintState::onEntry(QEvent *event)
+{
+ QStateMachine::SignalEvent *e = \
dynamic_cast<QStateMachine::SignalEvent*>(event); +
+ if (e && !e->arguments().isEmpty()) {
+ const QString command = e->arguments()[0].toString();
+ QTextStream stream(stdout);
+ stream << command << "\n";
+ }
+
+ emit completed();
+}
+
+static QStringList tab_completion_full_state;
+static bool tab_completion_at_root = false;
+
+static char **akonadi2_cli_tab_completion(const char *text, int start, int end)
+{
+ tab_completion_at_root = start == 0;
+ tab_completion_full_state = QString(rl_line_buffer).remove(start, end - \
start).split(" ", QString::SkipEmptyParts); + return NULL;
+}
+
+static char *akonadi2_cli_next_tab_complete_match(const char *text, int state)
+{
+ QVector<Module::Syntax> nearest = \
Module::nearestSyntax(tab_completion_full_state, QString(text)); +
+ if (nearest.size() > state) {
+ return qstrdup(nearest[state].keyword.toUtf8());
+ }
+
+ return rl_filename_completion_function(text, state);
+}
+
+#include "moc_replStates.cpp"
diff --git a/akonadi2_cli/repl/replStates.h b/akonadi2_cli/repl/replStates.h
new file mode 100644
index 0000000..a019a37
--- /dev/null
+++ b/akonadi2_cli/repl/replStates.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
+ *
+ * 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 <QState>
+
+class QSocketNotifier;
+
+class ReadState : public QState
+{
+ Q_OBJECT
+
+public:
+ ReadState(QState *parent = 0);
+
+Q_SIGNALS:
+ void command(const QString &command);
+ void exitRequested();
+
+protected:
+ void onEntry(QEvent *event);
+ virtual const char *prompt() const;
+};
+
+class UnfinishedReadState : public ReadState
+{
+ Q_OBJECT
+
+public:
+ UnfinishedReadState(QState *parent = 0);
+
+protected:
+ const char *prompt() const;
+};
+
+class EvalState : public QState
+{
+ Q_OBJECT
+
+public:
+ EvalState(QState *parent = 0);
+
+Q_SIGNALS:
+ void completed();
+ void continueInput();
+ void output(const QString &output);
+
+protected:
+ void onEntry(QEvent *event);
+
+private:
+ bool m_complete;
+};
+
+class PrintState : public QState
+{
+ Q_OBJECT
+
+public:
+ PrintState(QState *parent = 0);
+
+Q_SIGNALS:
+ void completed();
+
+protected:
+ void onEntry(QEvent *event);
+};
+
diff --git a/akonadi2_cli/state.cpp b/akonadi2_cli/state.cpp
new file mode 100644
index 0000000..bb21e0e
--- /dev/null
+++ b/akonadi2_cli/state.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
+ *
+ * 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 "state.h"
+
+#include <QDebug>
+#include <QDir>
+
+State::State()
+{
+}
+
diff --git a/akonadi2_cli/state.h b/akonadi2_cli/state.h
new file mode 100644
index 0000000..10b2318
--- /dev/null
+++ b/akonadi2_cli/state.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
+ *
+ * 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 <QStringList>
+
+class State
+{
+public:
+ State();
+};
+
diff --git a/cmake/modules/FindReadline.cmake b/cmake/modules/FindReadline.cmake
new file mode 100644
index 0000000..883ad3f
--- /dev/null
+++ b/cmake/modules/FindReadline.cmake
@@ -0,0 +1,47 @@
+# - Try to find readline include dirs and libraries
+#
+# Usage of this module as follows:
+#
+# find_package(Readline)
+#
+# Variables used by this module, they can change the default behaviour and need
+# to be set before calling find_package:
+#
+# Readline_ROOT_DIR Set this variable to the root installation of
+# readline if the module has problems finding the
+# proper installation path.
+#
+# Variables defined by this module:
+#
+# READLINE_FOUND System has readline, include and lib dirs found
+# Readline_INCLUDE_DIR The readline include directories.
+# Readline_LIBRARY The readline library.
+
+find_path(Readline_ROOT_DIR
+ NAMES include/readline/readline.h
+ )
+
+find_path(Readline_INCLUDE_DIR
+ NAMES readline/readline.h
+ HINTS ${Readline_ROOT_DIR}/include
+ )
+
+find_library(Readline_LIBRARY
+ NAMES readline
+ HINTS ${Readline_ROOT_DIR}/lib
+ )
+
+if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+ set(READLINE_FOUND TRUE)
+else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+ FIND_LIBRARY(Readline_LIBRARY NAMES readline)
+ include(FindPackageHandleStandardArgs)
+ FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR \
Readline_LIBRARY ) + MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY)
+endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+
+mark_as_advanced(
+ Readline_ROOT_DIR
+ Readline_INCLUDE_DIR
+ Readline_LIBRARY
+ )
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic