KSyntaxHighlighting

ksyntaxhighlighter.cpp
1/*
2 SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: MIT
5*/
6
7#include "ksyntaxhighlighting_version.h"
8
9#include <KSyntaxHighlighting/Definition>
10#include <KSyntaxHighlighting/DefinitionDownloader>
11#include <KSyntaxHighlighting/Repository>
12#include <KSyntaxHighlighting/Theme>
13#include <ansihighlighter.h>
14#include <htmlhighlighter.h>
15
16#include <QCommandLineParser>
17#include <QCoreApplication>
18#include <QFile>
19
20#include <cstdio>
21
22using namespace KSyntaxHighlighting;
23
24template<class Highlighter, class... Ts>
25static void applyHighlighter(Highlighter &highlighter,
26 QCommandLineParser &parser,
27 bool fromFileName,
28 const QString &inFileName,
29 const QCommandLineOption &outputName,
30 const Ts &...highlightParams)
31{
32 if (parser.isSet(outputName)) {
33 highlighter.setOutputFile(parser.value(outputName));
34 } else {
35 highlighter.setOutputFile(stdout);
36 }
37
38 if (fromFileName) {
39 highlighter.highlightFile(inFileName, highlightParams...);
40 } else {
41 QFile inFile;
42 inFile.open(stdin, QIODevice::ReadOnly);
43 highlighter.highlightData(&inFile, highlightParams...);
44 }
45}
46
47static Theme theme(const Repository &repo, const QString &themeName, Repository::DefaultTheme t)
48{
49 if (themeName.isEmpty()) {
50 return repo.defaultTheme(t);
51 }
52 return repo.theme(themeName);
53}
54
55int main(int argc, char **argv)
56{
57 QCoreApplication app(argc, argv);
58 QCoreApplication::setApplicationName(QStringLiteral("ksyntaxhighlighter"));
59 QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
60 QCoreApplication::setOrganizationName(QStringLiteral("KDE"));
61 QCoreApplication::setApplicationVersion(QStringLiteral(KSYNTAXHIGHLIGHTING_VERSION_STRING));
62
63 QCommandLineParser parser;
64 parser.setApplicationDescription(app.translate("SyntaxHighlightingCLI", "Command line syntax highlighter using KSyntaxHighlighting syntax definitions."));
65 parser.addHelpOption();
66 parser.addVersionOption();
68 app.translate("SyntaxHighlightingCLI", "source"),
69 app.translate("SyntaxHighlightingCLI", "The source file to highlight. If absent, read the file from stdin and the --syntax option must be used."));
70
71 QCommandLineOption listDefs(QStringList() << QStringLiteral("l") << QStringLiteral("list"),
72 app.translate("SyntaxHighlightingCLI", "List all available syntax definitions."));
73 parser.addOption(listDefs);
74 QCommandLineOption listThemes(QStringList() << QStringLiteral("list-themes"), app.translate("SyntaxHighlightingCLI", "List all available themes."));
75 parser.addOption(listThemes);
76
77 QCommandLineOption updateDefs(QStringList() << QStringLiteral("u") << QStringLiteral("update"),
78 app.translate("SyntaxHighlightingCLI", "Download new/updated syntax definitions."));
79 parser.addOption(updateDefs);
80
81 QCommandLineOption outputName(QStringList() << QStringLiteral("o") << QStringLiteral("output"),
82 app.translate("SyntaxHighlightingCLI", "File to write HTML output to (default: stdout)."),
83 app.translate("SyntaxHighlightingCLI", "output"));
84 parser.addOption(outputName);
85
86 QCommandLineOption syntaxName(QStringList() << QStringLiteral("s") << QStringLiteral("syntax"),
87 app.translate("SyntaxHighlightingCLI", "Highlight using this syntax definition (default: auto-detect based on input file)."),
88 app.translate("SyntaxHighlightingCLI", "syntax"));
89 parser.addOption(syntaxName);
90
91 QCommandLineOption themeName(QStringList() << QStringLiteral("t") << QStringLiteral("theme"),
92 app.translate("SyntaxHighlightingCLI", "Color theme to use for highlighting."),
93 app.translate("SyntaxHighlightingCLI", "theme"));
94 parser.addOption(themeName);
95
96 QCommandLineOption outputFormatOption(QStringList() << QStringLiteral("f") << QStringLiteral("output-format"),
97 app.translate("SyntaxHighlightingCLI", "Use the specified format instead of html. Must be html, ansi or ansi256."),
98 app.translate("SyntaxHighlightingCLI", "format"),
99 QStringLiteral("html"));
100 parser.addOption(outputFormatOption);
101
102 QCommandLineOption traceOption(QStringList() << QStringLiteral("syntax-trace"),
103 app.translate("SyntaxHighlightingCLI",
104 "Add information to debug a syntax file. Only works with --output-format=ansi or ansi256. Possible "
105 "values are format, region, context, stackSize and all."),
106 app.translate("SyntaxHighlightingCLI", "type"));
107 parser.addOption(traceOption);
108
109 QCommandLineOption noAnsiEditorBg(QStringList() << QStringLiteral("b") << QStringLiteral("no-ansi-background"),
110 app.translate("SyntaxHighlightingCLI", "Disable ANSI background for the default color."));
111 parser.addOption(noAnsiEditorBg);
112
113 QCommandLineOption bgRole(QStringList() << QStringLiteral("B") << QStringLiteral("background-role"),
114 app.translate("SyntaxHighlightingCLI", "Select background color role from theme."),
115 app.translate("SyntaxHighlightingCLI", "role"));
116 parser.addOption(bgRole);
117
118 QCommandLineOption unbufferedAnsi(QStringList() << QStringLiteral("U") << QStringLiteral("unbuffered"),
119 app.translate("SyntaxHighlightingCLI", "For ansi and ansi256 formats, flush the output buffer on each line."));
120 parser.addOption(unbufferedAnsi);
121
122 QCommandLineOption titleOption(
123 QStringList() << QStringLiteral("T") << QStringLiteral("title"),
124 app.translate("SyntaxHighlightingCLI", "Set HTML page's title\n(default: the filename or \"KSyntaxHighlighter\" if reading from stdin)."),
125 app.translate("SyntaxHighlightingCLI", "title"));
126 parser.addOption(titleOption);
127
128 parser.process(app);
129
130 Repository repo;
131
132 if (parser.isSet(listDefs)) {
133 for (const auto &def : repo.definitions()) {
134 fprintf(stdout, "%s\n", qPrintable(def.name()));
135 }
136 return 0;
137 }
138
139 if (parser.isSet(listThemes)) {
140 for (const auto &theme : repo.themes()) {
141 fprintf(stdout, "%s\n", qPrintable(theme.name()));
142 }
143 return 0;
144 }
145
147
148 if (parser.isSet(bgRole)) {
149 /*
150 * Theme::EditorColorRole contains border, foreground and background colors.
151 * To ensure that only the background colors used in text editing are used,
152 * QMetaEnum is avoided and values are listed in hard.
153 */
154
155 struct BgRole {
158 // name for display
159 const char *asciiName;
160 };
161
162#define BG_ROLE(role) \
163 BgRole \
164 { \
165 QStringView(u"" #role, sizeof(#role) - 1), Theme::role, #role \
166 }
167 constexpr BgRole bgRoles[] = {
168 BG_ROLE(BackgroundColor),
169 BG_ROLE(TextSelection),
170 BG_ROLE(CurrentLine),
171 BG_ROLE(SearchHighlight),
172 BG_ROLE(ReplaceHighlight),
173 BG_ROLE(BracketMatching),
174 BG_ROLE(CodeFolding),
175 BG_ROLE(MarkBookmark),
176 BG_ROLE(MarkBreakpointActive),
177 BG_ROLE(MarkBreakpointReached),
178 BG_ROLE(MarkBreakpointDisabled),
179 BG_ROLE(MarkExecution),
180 BG_ROLE(MarkWarning),
181 BG_ROLE(MarkError),
182 BG_ROLE(TemplateBackground),
183 BG_ROLE(TemplatePlaceholder),
184 BG_ROLE(TemplateFocusedPlaceholder),
185 BG_ROLE(TemplateReadOnlyPlaceholder),
186 };
187#undef BG_ROLE
188
189 const auto role = parser.value(bgRole);
190 bool ok = false;
191 for (const auto &def : bgRoles) {
192 if (def.name == role) {
193 bgColorRole = def.role;
194 ok = true;
195 break;
196 }
197 }
198
199 if (!ok) {
200 fprintf(stderr, "Unknown background role. Expected:\n");
201 for (const auto &def : bgRoles) {
202 fprintf(stderr, " - %s\n", def.asciiName);
203 }
204 return 1;
205 }
206 }
207
208 if (parser.isSet(updateDefs)) {
209 DefinitionDownloader downloader(&repo);
210 QObject::connect(&downloader, &DefinitionDownloader::informationMessage, &app, [](const QString &msg) {
211 fprintf(stdout, "%s\n", qPrintable(msg));
212 });
214 downloader.start();
215 return app.exec();
216 }
217
218 bool fromFileName = false;
219 QString inFileName;
220 if (parser.positionalArguments().size() == 1) {
221 fromFileName = true;
222 inFileName = parser.positionalArguments().at(0);
223 }
224
225 Definition def;
226 if (parser.isSet(syntaxName)) {
227 const QString syntax = parser.value(syntaxName);
228 def = repo.definitionForName(syntax);
229 if (!def.isValid()) {
230 /* see if it's a mimetype instead */
231 def = repo.definitionForMimeType(syntax);
232 if (!def.isValid()) {
233 /* see if it's a extension instead */
234 def = repo.definitionForFileName(QLatin1String("f.") + syntax);
235 if (!def.isValid()) {
236 /* see if it's a filename instead */
237 def = repo.definitionForFileName(syntax);
238 }
239 }
240 }
241 } else if (fromFileName) {
242 def = repo.definitionForFileName(inFileName);
243 } else {
244 parser.showHelp(1);
245 }
246
247 if (!def.isValid()) {
248 fprintf(stderr, "Unknown syntax.\n");
249 return 1;
250 }
251
252 const QString outputFormat = parser.value(outputFormatOption);
253 if (0 == outputFormat.compare(QLatin1String("html"), Qt::CaseInsensitive)) {
254 QString title;
255 if (parser.isSet(titleOption)) {
256 title = parser.value(titleOption);
257 }
258
259 HtmlHighlighter highlighter;
260 highlighter.setDefinition(def);
261 highlighter.setBackgroundRole(bgColorRole);
262 highlighter.setTheme(theme(repo, parser.value(themeName), Repository::LightTheme));
263 applyHighlighter(highlighter, parser, fromFileName, inFileName, outputName, title);
264 } else {
265 auto AnsiFormat = AnsiHighlighter::AnsiFormat::TrueColor;
266 // compatible with the old ansi256Colors value
267 if (outputFormat.startsWith(QLatin1String("ansi256"), Qt::CaseInsensitive)) {
268 AnsiFormat = AnsiHighlighter::AnsiFormat::XTerm256Color;
269 } else if (0 != outputFormat.compare(QLatin1String("ansi"), Qt::CaseInsensitive)) {
270 fprintf(stderr, "Unknown output format.\n");
271 return 2;
272 }
273
274 AnsiHighlighter::Options options{};
275 options |= parser.isSet(noAnsiEditorBg) ? AnsiHighlighter::Option::NoOptions : AnsiHighlighter::Option::UseEditorBackground;
276 options |= parser.isSet(unbufferedAnsi) ? AnsiHighlighter::Option::Unbuffered : AnsiHighlighter::Option::NoOptions;
277 if (parser.isSet(traceOption)) {
278 const auto traceOptions = parser.values(traceOption);
279 for (auto const &option : traceOptions) {
280 if (option == QStringLiteral("format")) {
281 options |= AnsiHighlighter::Option::TraceFormat;
282 } else if (option == QStringLiteral("region")) {
283 options |= AnsiHighlighter::Option::TraceRegion;
284 } else if (option == QStringLiteral("context")) {
285 options |= AnsiHighlighter::Option::TraceContext;
286 } else if (option == QStringLiteral("stackSize")) {
287 options |= AnsiHighlighter::Option::TraceStackSize;
288 } else if (option == QStringLiteral("all")) {
289 options |= AnsiHighlighter::Option::TraceAll;
290 } else {
291 fprintf(stderr, "Unknown trace name.\n");
292 return 2;
293 }
294 }
295 }
296
297 AnsiHighlighter highlighter;
298 highlighter.setDefinition(def);
299 highlighter.setBackgroundRole(bgColorRole);
300 highlighter.setTheme(theme(repo, parser.value(themeName), Repository::DarkTheme));
301 applyHighlighter(highlighter, parser, fromFileName, inFileName, outputName, AnsiFormat, options);
302 }
303
304 return 0;
305}
virtual void setDefinition(const Definition &def)
Sets the syntax definition used for highlighting.
virtual void setTheme(const Theme &theme)
Sets the theme used for highlighting.
Helper class to download definition file updates.
void done()
This signal is emitted when there are no pending downloads anymore.
void informationMessage(const QString &msg)
Prints the information about the current state of the definition files.
Represents a syntax definition.
Definition definition.h:83
bool isValid() const
Checks whether this object refers to a valid syntax definition.
DefaultTheme
Built-in default theme types.
Definition repository.h:213
Color theme definition used for highlighting.
Definition theme.h:65
EditorColorRole
Editor color roles, used to paint line numbers, editor background etc.
Definition theme.h:158
@ BackgroundColor
Background color for the editing area.
Definition theme.h:160
QString name(StandardAction id)
Syntax highlighting engine for Kate syntax definitions.
QCommandLineOption addHelpOption()
bool addOption(const QCommandLineOption &option)
void addPositionalArgument(const QString &name, const QString &description, const QString &syntax)
QCommandLineOption addVersionOption()
bool isSet(const QCommandLineOption &option) const const
QStringList positionalArguments() const const
void process(const QCoreApplication &app)
void setApplicationDescription(const QString &description)
void showHelp(int exitCode)
QString value(const QCommandLineOption &option) const const
QStringList values(const QCommandLineOption &option) const const
void setApplicationName(const QString &application)
void setApplicationVersion(const QString &version)
void setOrganizationDomain(const QString &orgDomain)
void setOrganizationName(const QString &orgName)
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
const_reference at(qsizetype i) const const
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
bool isEmpty() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
CaseInsensitive
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:09:06 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.