File indexing completed on 2024-05-12 04:02:14

0001 /*
0002     SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #include "ksyntaxhighlighting_version.h"
0008 
0009 #include <KSyntaxHighlighting/Definition>
0010 #include <KSyntaxHighlighting/DefinitionDownloader>
0011 #include <KSyntaxHighlighting/Repository>
0012 #include <KSyntaxHighlighting/Theme>
0013 #include <ansihighlighter.h>
0014 #include <htmlhighlighter.h>
0015 
0016 #include <QCommandLineParser>
0017 #include <QCoreApplication>
0018 #include <QFile>
0019 
0020 #include <iostream>
0021 
0022 using namespace KSyntaxHighlighting;
0023 
0024 template<class Highlighter, class... Ts>
0025 static void applyHighlighter(Highlighter &highlighter,
0026                              QCommandLineParser &parser,
0027                              bool fromFileName,
0028                              const QString &inFileName,
0029                              const QCommandLineOption &outputName,
0030                              const Ts &...highlightParams)
0031 {
0032     if (parser.isSet(outputName)) {
0033         highlighter.setOutputFile(parser.value(outputName));
0034     } else {
0035         highlighter.setOutputFile(stdout);
0036     }
0037 
0038     if (fromFileName) {
0039         highlighter.highlightFile(inFileName, highlightParams...);
0040     } else {
0041         QFile inFile;
0042         inFile.open(stdin, QIODevice::ReadOnly);
0043         highlighter.highlightData(&inFile, highlightParams...);
0044     }
0045 }
0046 
0047 static Theme theme(const Repository &repo, const QString &themeName, Repository::DefaultTheme t)
0048 {
0049     if (themeName.isEmpty()) {
0050         return repo.defaultTheme(t);
0051     }
0052     return repo.theme(themeName);
0053 }
0054 
0055 int main(int argc, char **argv)
0056 {
0057     QCoreApplication app(argc, argv);
0058     QCoreApplication::setApplicationName(QStringLiteral("ksyntaxhighlighter"));
0059     QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
0060     QCoreApplication::setOrganizationName(QStringLiteral("KDE"));
0061     QCoreApplication::setApplicationVersion(QStringLiteral(KSYNTAXHIGHLIGHTING_VERSION_STRING));
0062 
0063     QCommandLineParser parser;
0064     parser.setApplicationDescription(app.translate("SyntaxHighlightingCLI", "Command line syntax highlighter using KSyntaxHighlighting syntax definitions."));
0065     parser.addHelpOption();
0066     parser.addVersionOption();
0067     parser.addPositionalArgument(
0068         app.translate("SyntaxHighlightingCLI", "source"),
0069         app.translate("SyntaxHighlightingCLI", "The source file to highlight. If absent, read the file from stdin and the --syntax option must be used."));
0070 
0071     QCommandLineOption listDefs(QStringList() << QStringLiteral("l") << QStringLiteral("list"),
0072                                 app.translate("SyntaxHighlightingCLI", "List all available syntax definitions."));
0073     parser.addOption(listDefs);
0074     QCommandLineOption listThemes(QStringList() << QStringLiteral("list-themes"), app.translate("SyntaxHighlightingCLI", "List all available themes."));
0075     parser.addOption(listThemes);
0076 
0077     QCommandLineOption updateDefs(QStringList() << QStringLiteral("u") << QStringLiteral("update"),
0078                                   app.translate("SyntaxHighlightingCLI", "Download new/updated syntax definitions."));
0079     parser.addOption(updateDefs);
0080 
0081     QCommandLineOption outputName(QStringList() << QStringLiteral("o") << QStringLiteral("output"),
0082                                   app.translate("SyntaxHighlightingCLI", "File to write HTML output to (default: stdout)."),
0083                                   app.translate("SyntaxHighlightingCLI", "output"));
0084     parser.addOption(outputName);
0085 
0086     QCommandLineOption syntaxName(QStringList() << QStringLiteral("s") << QStringLiteral("syntax"),
0087                                   app.translate("SyntaxHighlightingCLI", "Highlight using this syntax definition (default: auto-detect based on input file)."),
0088                                   app.translate("SyntaxHighlightingCLI", "syntax"));
0089     parser.addOption(syntaxName);
0090 
0091     QCommandLineOption themeName(QStringList() << QStringLiteral("t") << QStringLiteral("theme"),
0092                                  app.translate("SyntaxHighlightingCLI", "Color theme to use for highlighting."),
0093                                  app.translate("SyntaxHighlightingCLI", "theme"));
0094     parser.addOption(themeName);
0095 
0096     QCommandLineOption outputFormatOption(
0097         QStringList() << QStringLiteral("f") << QStringLiteral("output-format"),
0098         app.translate("SyntaxHighlightingCLI", "Use the specified format instead of html. Must be html, ansi or ansi256Colors."),
0099         app.translate("SyntaxHighlightingCLI", "format"),
0100         QStringLiteral("html"));
0101     parser.addOption(outputFormatOption);
0102 
0103     QCommandLineOption traceOption(QStringList() << QStringLiteral("syntax-trace"),
0104                                    app.translate("SyntaxHighlightingCLI",
0105                                                  "Add information to debug a syntax file. Only works with --output-format=ansi or ansi256Colors. Possible "
0106                                                  "values are format, region, context, stackSize and all."),
0107                                    app.translate("SyntaxHighlightingCLI", "type"));
0108     parser.addOption(traceOption);
0109 
0110     QCommandLineOption noAnsiEditorBg(QStringList() << QStringLiteral("b") << QStringLiteral("no-ansi-background"),
0111                                       app.translate("SyntaxHighlightingCLI", "Disable ANSI background for the default color."));
0112     parser.addOption(noAnsiEditorBg);
0113 
0114     QCommandLineOption unbufferedAnsi(QStringList() << QStringLiteral("U") << QStringLiteral("unbuffered"),
0115                                       app.translate("SyntaxHighlightingCLI", "For ansi and ansi256Colors formats, flush the output buffer on each line."));
0116     parser.addOption(unbufferedAnsi);
0117 
0118     QCommandLineOption titleOption(
0119         QStringList() << QStringLiteral("T") << QStringLiteral("title"),
0120         app.translate("SyntaxHighlightingCLI", "Set HTML page's title\n(default: the filename or \"KSyntaxHighlighter\" if reading from stdin)."),
0121         app.translate("SyntaxHighlightingCLI", "title"));
0122     parser.addOption(titleOption);
0123 
0124     parser.process(app);
0125 
0126     Repository repo;
0127 
0128     if (parser.isSet(listDefs)) {
0129         for (const auto &def : repo.definitions()) {
0130             std::cout << qPrintable(def.name()) << std::endl;
0131         }
0132         return 0;
0133     }
0134 
0135     if (parser.isSet(listThemes)) {
0136         for (const auto &theme : repo.themes()) {
0137             std::cout << qPrintable(theme.name()) << std::endl;
0138         }
0139         return 0;
0140     }
0141 
0142     if (parser.isSet(updateDefs)) {
0143         DefinitionDownloader downloader(&repo);
0144         QObject::connect(&downloader, &DefinitionDownloader::informationMessage, [](const QString &msg) {
0145             std::cout << qPrintable(msg) << std::endl;
0146         });
0147         QObject::connect(&downloader, &DefinitionDownloader::done, &app, &QCoreApplication::quit);
0148         downloader.start();
0149         return app.exec();
0150     }
0151 
0152     bool fromFileName = false;
0153     QString inFileName;
0154     if (parser.positionalArguments().size() == 1) {
0155         fromFileName = true;
0156         inFileName = parser.positionalArguments().at(0);
0157     }
0158 
0159     Definition def;
0160     if (parser.isSet(syntaxName)) {
0161         const QString syntax = parser.value(syntaxName);
0162         def = repo.definitionForName(syntax);
0163         if (!def.isValid()) {
0164             /* see if it's a mimetype instead */
0165             def = repo.definitionForMimeType(syntax);
0166             if (!def.isValid()) {
0167                 /* see if it's a extension instead */
0168                 def = repo.definitionForFileName(QLatin1String("f.") + syntax);
0169                 if (!def.isValid()) {
0170                     /* see if it's a filename instead */
0171                     def = repo.definitionForFileName(syntax);
0172                 }
0173             }
0174         }
0175     } else if (fromFileName) {
0176         def = repo.definitionForFileName(inFileName);
0177     } else {
0178         parser.showHelp(1);
0179     }
0180 
0181     if (!def.isValid()) {
0182         std::cerr << "Unknown syntax." << std::endl;
0183         return 1;
0184     }
0185 
0186     const QString outputFormat = parser.value(outputFormatOption);
0187     if (0 == outputFormat.compare(QLatin1String("html"), Qt::CaseInsensitive)) {
0188         QString title;
0189         if (parser.isSet(titleOption)) {
0190             title = parser.value(titleOption);
0191         }
0192 
0193         HtmlHighlighter highlighter;
0194         highlighter.setDefinition(def);
0195         highlighter.setTheme(theme(repo, parser.value(themeName), Repository::LightTheme));
0196         applyHighlighter(highlighter, parser, fromFileName, inFileName, outputName, title);
0197     } else {
0198         auto AnsiFormat = AnsiHighlighter::AnsiFormat::TrueColor;
0199         if (0 == outputFormat.compare(QLatin1String("ansi256Colors"), Qt::CaseInsensitive)) {
0200             AnsiFormat = AnsiHighlighter::AnsiFormat::XTerm256Color;
0201         } else if (0 != outputFormat.compare(QLatin1String("ansi"), Qt::CaseInsensitive)) {
0202             std::cerr << "Unknown output format." << std::endl;
0203             return 2;
0204         }
0205 
0206         AnsiHighlighter::Options options{};
0207         options |= parser.isSet(noAnsiEditorBg) ? AnsiHighlighter::Option::NoOptions : AnsiHighlighter::Option::UseEditorBackground;
0208         options |= parser.isSet(unbufferedAnsi) ? AnsiHighlighter::Option::Unbuffered : AnsiHighlighter::Option::NoOptions;
0209         if (parser.isSet(traceOption)) {
0210             const auto traceOptions = parser.values(traceOption);
0211             for (auto const &option : traceOptions) {
0212                 if (option == QStringLiteral("format")) {
0213                     options |= AnsiHighlighter::Option::TraceFormat;
0214                 } else if (option == QStringLiteral("region")) {
0215                     options |= AnsiHighlighter::Option::TraceRegion;
0216                 } else if (option == QStringLiteral("context")) {
0217                     options |= AnsiHighlighter::Option::TraceContext;
0218                 } else if (option == QStringLiteral("stackSize")) {
0219                     options |= AnsiHighlighter::Option::TraceStackSize;
0220                 } else if (option == QStringLiteral("all")) {
0221                     options |= AnsiHighlighter::Option::TraceAll;
0222                 } else {
0223                     std::cerr << "Unknown trace name." << std::endl;
0224                     return 2;
0225                 }
0226             }
0227         }
0228 
0229         AnsiHighlighter highlighter;
0230         highlighter.setDefinition(def);
0231         highlighter.setTheme(theme(repo, parser.value(themeName), Repository::DarkTheme));
0232         applyHighlighter(highlighter, parser, fromFileName, inFileName, outputName, AnsiFormat, options);
0233     }
0234 
0235     return 0;
0236 }