Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ A dead-simple Markdown writing app built with Qt Quick and C++ that automaticall

Install via the Omarchy Package Repository via the `omawrite` package. It's installed by default in new installations of Omarchy (from Quattro forward).

## Command line

`omawrite` opens an empty document, and `omawrite FILE` opens a Markdown file.
`omawrite --help` prints that usage without opening a window.

That last one is mostly for coding agents. Ask one to open a file in Omawrite
and it runs `omawrite --help` first to work out how, so the help has to answer
in the terminal, and it has to say that the process stays up until the window
is closed.

## Shortcuts

- `Ctrl+S` saves. Unsaved documents use the XDG desktop portal file picker.
Expand Down
2 changes: 2 additions & 0 deletions omawrite.pro
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ TEMPLATE = app

HEADERS += \
src/backend.h \
src/cli.h \
src/markdownhighlighter.h \
src/systemtheme.h

SOURCES += \
src/main.cpp \
src/backend.cpp \
src/cli.cpp \
src/markdownhighlighter.cpp \
src/systemtheme.cpp

Expand Down
56 changes: 56 additions & 0 deletions src/cli.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include "cli.h"

#include <QTextStream>

QString Cli::usage() {
return QStringLiteral(
"Omawrite is a dead-simple Markdown writing app.\n"
"\n"
"Usage:\n"
" omawrite [FILE]\n"
"\n"
"Opens the Omawrite window. FILE is the Markdown file to open; without\n"
"one, Omawrite starts on an empty document, or on the draft recovered\n"
"from the last session.\n"
"\n"
"Options:\n"
" -h, --help Show this message and exit\n"
"\n"
"Omawrite is a graphical app: it needs a desktop session, and it keeps\n"
"running until the window is closed. Scripts and agents that only want\n"
"a file on screen should background it:\n"
"\n"
" omawrite draft.md &\n"
"\n"
"Everything else is a keyboard shortcut. Ctrl+? lists them in the app,\n"
"and https://github.com/omacom-io/omawrite#shortcuts has the same list.\n");
}

std::optional<int> Cli::handleArguments(const QStringList &arguments) {
for (const QString &argument : arguments.mid(1)) {
if (argument == QLatin1String("-h") || argument == QLatin1String("--help")) {
QTextStream(stdout) << usage();
return 0;
}

// A leading dash means an option was meant, not a file. Answering with
// the usage beats opening a window for a file that cannot exist.
if (argument.startsWith(QLatin1Char('-')) && argument != QLatin1String("-")) {
QTextStream(stderr) << QStringLiteral("omawrite: unrecognized option '%1'\n\n")
.arg(argument)
<< usage();
return 1;
}
}

return std::nullopt;
}

std::optional<int> Cli::handleArguments(int argc, char *argv[]) {
QStringList arguments;
arguments.reserve(argc);
for (int index = 0; index < argc; ++index)
arguments.append(QString::fromLocal8Bit(argv[index]));

return handleArguments(arguments);
}
18 changes: 18 additions & 0 deletions src/cli.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <QString>
#include <QStringList>
#include <optional>

// Command line handling that runs before the GUI starts, so `omawrite --help`
// answers in the terminal instead of opening a window.
namespace Cli {

QString usage();

// The exit code main should return when the arguments are answered without
// starting the app, or nothing when Omawrite should open as usual.
std::optional<int> handleArguments(const QStringList &arguments);
std::optional<int> handleArguments(int argc, char *argv[]);

}
6 changes: 6 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@
#include <QFile>

#include "backend.h"
#include "cli.h"
#include "systemtheme.h"

int main(int argc, char *argv[]) {
// Answer --help before Qt claims the terminal, so the usage prints even
// without a desktop session to open a window in.
if (const std::optional<int> exitCode = Cli::handleArguments(argc, argv))
return *exitCode;

QApplication app(argc, argv);
app.setApplicationName(QStringLiteral("omawrite"));
app.setDesktopFileName(QStringLiteral("omawrite"));
Expand Down
2 changes: 2 additions & 0 deletions tests/tests.pro
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ INCLUDEPATH += ../src
SOURCES += \
tst_omawrite.cpp \
../src/backend.cpp \
../src/cli.cpp \
../src/markdownhighlighter.cpp
HEADERS += \
../src/backend.h \
../src/cli.h \
../src/markdownhighlighter.h

QT += widgets printsupport quickcontrols2 quickdialogs2 dbus
17 changes: 17 additions & 0 deletions tests/tst_omawrite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <QQuickStyle>

#include "backend.h"
#include "cli.h"
#include "markdownhighlighter.h"

class OmawriteTest : public QObject {
Expand Down Expand Up @@ -44,6 +45,22 @@ private slots:
QStringLiteral("Already.md"));
}

void answersHelpBeforeOpeningAWindow() {
const QString usage = Cli::usage();
QVERIFY(usage.contains(QStringLiteral("omawrite [FILE]")));
QVERIFY(usage.contains(QStringLiteral("-h, --help")));

QCOMPARE(Cli::handleArguments({QStringLiteral("omawrite"), QStringLiteral("--help")}),
std::optional<int>(0));
QCOMPARE(Cli::handleArguments({QStringLiteral("omawrite"), QStringLiteral("-h")}),
std::optional<int>(0));
QCOMPARE(Cli::handleArguments({QStringLiteral("omawrite"), QStringLiteral("--nope")}),
std::optional<int>(1));
QCOMPARE(Cli::handleArguments({QStringLiteral("omawrite")}), std::nullopt);
QCOMPARE(Cli::handleArguments({QStringLiteral("omawrite"), QStringLiteral("draft.md")}),
std::nullopt);
}

void findsInlineMarkdownRanges() {
const auto markup = MarkdownHighlighter::inlineMarkup(
QStringLiteral("**bold** and *italic* and [site](https://example.com)"));
Expand Down