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 if (inFile.open(stdin, QIODevice::ReadOnly)) {
43 highlighter.highlightData(&inFile, highlightParams...);
44 }
45 }
46}
47
48static Theme theme(const Repository &repo, const QString &themeName, Repository::DefaultTheme t)
49{
50 if (themeName.isEmpty()) {
51 return repo.defaultTheme(t);
52 }
53 return repo.theme(themeName);
54}
55
56int main(int argc, char **argv)
57{
58 QCoreApplication app(argc, argv);
59 QCoreApplication::setApplicationName(QStringLiteral("ksyntaxhighlighter"));
60 QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
61 QCoreApplication::setOrganizationName(QStringLiteral("KDE"));
62 QCoreApplication::setApplicationVersion(QStringLiteral(KSYNTAXHIGHLIGHTING_VERSION_STRING));
63
64 QCommandLineParser parser;
65 parser.setApplicationDescription(app.translate("SyntaxHighlightingCLI", "Command line syntax highlighter using KSyntaxHighlighting syntax definitions."));
66 parser.addHelpOption();
67 parser.addVersionOption();
69 app.translate("SyntaxHighlightingCLI", "source"),
70 app.translate("SyntaxHighlightingCLI", "The source file to highlight. If absent, read the file from stdin and the --syntax option must be used."));
71
72 QCommandLineOption listDefs(QStringList() << QStringLiteral("l") << QStringLiteral("list"),
73 app.translate("SyntaxHighlightingCLI", "List all available syntax definitions."));
74 parser.addOption(listDefs);
75 QCommandLineOption listThemes(QStringList() << QStringLiteral("list-themes"), app.translate("SyntaxHighlightingCLI", "List all available themes."));
76 parser.addOption(listThemes);
77
78 QCommandLineOption updateDefs(QStringList() << QStringLiteral("u") << QStringLiteral("update"),
79 app.translate("SyntaxHighlightingCLI", "Download new/updated syntax definitions."));
80 parser.addOption(updateDefs);
81
82 QCommandLineOption outputName(QStringList() << QStringLiteral("o") << QStringLiteral("output"),
83 app.translate("SyntaxHighlightingCLI", "File to write HTML output to (default: stdout)."),
84 app.translate("SyntaxHighlightingCLI", "output"));
85 parser.addOption(outputName);
86
87 QCommandLineOption syntaxName(QStringList() << QStringLiteral("s") << QStringLiteral("syntax"),
88 app.translate("SyntaxHighlightingCLI", "Highlight using this syntax definition (default: auto-detect based on input file)."),
89 app.translate("SyntaxHighlightingCLI", "syntax"));
90 parser.addOption(syntaxName);
91
92 QCommandLineOption themeName(QStringList() << QStringLiteral("t") << QStringLiteral("theme"),
93 app.translate("SyntaxHighlightingCLI", "Color theme to use for highlighting."),
94 app.translate("SyntaxHighlightingCLI", "theme"));
95 parser.addOption(themeName);
96
97 QCommandLineOption outputFormatOption(QStringList() << QStringLiteral("f") << QStringLiteral("output-format"),
98 app.translate("SyntaxHighlightingCLI", "Use the specified format instead of html. Must be html, ansi or ansi256."),
99 app.translate("SyntaxHighlightingCLI", "format"),
100 QStringLiteral("html"));
101 parser.addOption(outputFormatOption);
102
103 QCommandLineOption traceOption(QStringList() << QStringLiteral("syntax-trace"),
104 app.translate("SyntaxHighlightingCLI",
105 "Add information to debug a syntax file. Only works with --output-format=ansi or ansi256. Possible "
106 "values are format, region, context, stackSize and all."),
107 app.translate("SyntaxHighlightingCLI", "type"));
108 parser.addOption(traceOption);
109
110 QCommandLineOption noAnsiEditorBg(QStringList() << QStringLiteral("b") << QStringLiteral("no-ansi-background"),
111 app.translate("SyntaxHighlightingCLI", "Disable ANSI background for the default color."));
112 parser.addOption(noAnsiEditorBg);
113
114 QCommandLineOption bgRole(QStringList() << QStringLiteral("B") << QStringLiteral("background-role"),
115 app.translate("SyntaxHighlightingCLI", "Select background color role from theme."),
116 app.translate("SyntaxHighlightingCLI", "role"));
117 parser.addOption(bgRole);
118
119 QCommandLineOption unbufferedAnsi(QStringList() << QStringLiteral("U") << QStringLiteral("unbuffered"),
120 app.translate("SyntaxHighlightingCLI", "For ansi and ansi256 formats, flush the output buffer on each line."));
121 parser.addOption(unbufferedAnsi);
122
123 QCommandLineOption titleOption(
124 QStringList() << QStringLiteral("T") << QStringLiteral("title"),
125 app.translate("SyntaxHighlightingCLI", "Set HTML page's title\n(default: the filename or \"KSyntaxHighlighter\" if reading from stdin)."),
126 app.translate("SyntaxHighlightingCLI", "title"));
127 parser.addOption(titleOption);
128
129 parser.process(app);
130
131 Repository repo;
132
133 if (parser.isSet(listDefs)) {
134 for (const auto &def : repo.definitions()) {
135 fprintf(stdout, "%s\n", qPrintable(def.name()));
136 }
137 return 0;
138 }
139
140 if (parser.isSet(listThemes)) {
141 for (const auto &theme : repo.themes()) {
142 fprintf(stdout, "%s\n", qPrintable(theme.name()));
143 }
144 return 0;
145 }
146
148
149 if (parser.isSet(bgRole)) {
150 /*
151 * Theme::EditorColorRole contains border, foreground and background colors.
152 * To ensure that only the background colors used in text editing are used,
153 * QMetaEnum is avoided and values are listed in hard.
154 */
155
156 struct BgRole {
157 QStringView name;
159 // name for display
160 const char *asciiName;
161 };
162
163#define BG_ROLE(role) \
164 BgRole \
165 { \
166 QStringView(u"" #role, sizeof(#role) - 1), Theme::role, #role \
167 }
168 constexpr BgRole bgRoles[] = {
169 BG_ROLE(BackgroundColor),
170 BG_ROLE(TextSelection),
171 BG_ROLE(CurrentLine),
172 BG_ROLE(SearchHighlight),
173 BG_ROLE(ReplaceHighlight),
174 BG_ROLE(BracketMatching),
175 BG_ROLE(CodeFolding),
176 BG_ROLE(MarkBookmark),
177 BG_ROLE(MarkBreakpointActive),
178 BG_ROLE(MarkBreakpointReached),
179 BG_ROLE(MarkBreakpointDisabled),
180 BG_ROLE(MarkExecution),
181 BG_ROLE(MarkWarning),
182 BG_ROLE(MarkError),
183 BG_ROLE(TemplateBackground),
184 BG_ROLE(TemplatePlaceholder),
185 BG_ROLE(TemplateFocusedPlaceholder),
186 BG_ROLE(TemplateReadOnlyPlaceholder),
187 };
188#undef BG_ROLE
189
190 const auto role = parser.value(bgRole);
191 bool ok = false;
192 for (const auto &def : bgRoles) {
193 if (def.name == role) {
194 bgColorRole = def.role;
195 ok = true;
196 break;
197 }
198 }
199
200 if (!ok) {
201 fprintf(stderr, "Unknown background role. Expected:\n");
202 for (const auto &def : bgRoles) {
203 fprintf(stderr, " - %s\n", def.asciiName);
204 }
205 return 1;
206 }
207 }
208
209 if (parser.isSet(updateDefs)) {
210 DefinitionDownloader downloader(&repo);
211 QObject::connect(&downloader, &DefinitionDownloader::informationMessage, &app, [](const QString &msg) {
212 fprintf(stdout, "%s\n", qPrintable(msg));
213 });
215 downloader.start();
216 return app.exec();
217 }
218
219 bool fromFileName = false;
220 QString inFileName;
221 if (parser.positionalArguments().size() == 1) {
222 fromFileName = true;
223 inFileName = parser.positionalArguments().at(0);
224 }
225
226 Definition def;
227 if (parser.isSet(syntaxName)) {
228 const QString syntax = parser.value(syntaxName);
229 def = repo.definitionForName(syntax);
230 if (!def.isValid()) {
231 /* see if it's a mimetype instead */
232 def = repo.definitionForMimeType(syntax);
233 if (!def.isValid()) {
234 /* see if it's a extension instead */
235 def = repo.definitionForFileName(QLatin1String("f.") + syntax);
236 if (!def.isValid()) {
237 /* see if it's a filename instead */
238 def = repo.definitionForFileName(syntax);
239 }
240 }
241 }
242 } else if (fromFileName) {
243 def = repo.definitionForFileName(inFileName);
244 } else {
245 parser.showHelp(1);
246 }
247
248 if (!def.isValid()) {
249 fprintf(stderr, "Unknown syntax.\n");
250 return 1;
251 }
252
253 const QString outputFormat = parser.value(outputFormatOption);
254 if (0 == outputFormat.compare(QLatin1String("html"), Qt::CaseInsensitive)) {
255 QString title;
256 if (parser.isSet(titleOption)) {
257 title = parser.value(titleOption);
258 }
259
260 HtmlHighlighter highlighter;
261 highlighter.setDefinition(def);
262 highlighter.setBackgroundRole(bgColorRole);
263 highlighter.setTheme(theme(repo, parser.value(themeName), Repository::LightTheme));
264 applyHighlighter(highlighter, parser, fromFileName, inFileName, outputName, title);
265 } else {
266 auto AnsiFormat = AnsiHighlighter::AnsiFormat::TrueColor;
267 // compatible with the old ansi256Colors value
268 if (outputFormat.startsWith(QLatin1String("ansi256"), Qt::CaseInsensitive)) {
269 AnsiFormat = AnsiHighlighter::AnsiFormat::XTerm256Color;
270 } else if (0 != outputFormat.compare(QLatin1String("ansi"), Qt::CaseInsensitive)) {
271 fprintf(stderr, "Unknown output format.\n");
272 return 2;
273 }
274
275 AnsiHighlighter::Options options{};
276 options |= parser.isSet(noAnsiEditorBg) ? AnsiHighlighter::Option::NoOptions : AnsiHighlighter::Option::UseEditorBackground;
277 options |= parser.isSet(unbufferedAnsi) ? AnsiHighlighter::Option::Unbuffered : AnsiHighlighter::Option::NoOptions;
278 if (parser.isSet(traceOption)) {
279 const auto traceOptions = parser.values(traceOption);
280 for (auto const &option : traceOptions) {
281 if (option == QStringLiteral("format")) {
282 options |= AnsiHighlighter::Option::TraceFormat;
283 } else if (option == QStringLiteral("region")) {
284 options |= AnsiHighlighter::Option::TraceRegion;
285 } else if (option == QStringLiteral("context")) {
286 options |= AnsiHighlighter::Option::TraceContext;
287 } else if (option == QStringLiteral("stackSize")) {
288 options |= AnsiHighlighter::Option::TraceStackSize;
289 } else if (option == QStringLiteral("all")) {
290 options |= AnsiHighlighter::Option::TraceAll;
291 } else {
292 fprintf(stderr, "Unknown trace name.\n");
293 return 2;
294 }
295 }
296 }
297
298 AnsiHighlighter highlighter;
299 highlighter.setDefinition(def);
300 highlighter.setBackgroundRole(bgColorRole);
301 highlighter.setTheme(theme(repo, parser.value(themeName), Repository::DarkTheme));
302 applyHighlighter(highlighter, parser, fromFileName, inFileName, outputName, AnsiFormat, options);
303 }
304
305 return 0;
306}
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
@ DarkTheme
Theme with a dark background color.
Definition repository.h:217
@ LightTheme
Theme with a light background color.
Definition repository.h:215
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-2025 The KDE developers.
Generated on Fri Feb 28 2025 11:50:30 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.