summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvitya-smirnov <[email protected]>2025-07-31 14:54:51 +0300
committervitya-smirnov <[email protected]>2025-07-31 15:11:42 +0300
commit9987028f33c53b335e33a2a52f58cfad1cf558f3 (patch)
tree051eec252c5439dce098d3f38b750bc93fbabdda
parentbe28619189eb153f4454ec29cfb94e0359a1838c (diff)
YQL-19616: Generate YQL TextMate Bundle
- Introduced the Generator interface. - Made tool to generate content to stdout and file. - Supported TextMate Bundle, tested on IDEA and TextMate. - Fixed UDF highlighting priority. commit_hash:bcbc446a2fe58da3400f0e981a03d821b8f77dae
-rw-r--r--yql/essentials/sql/v1/highlight/sql_highlight.cpp2
-rw-r--r--yql/essentials/sql/v1/highlight/ut/suite.json4
-rw-r--r--yql/essentials/tools/yql_highlight/artifact/ya.make14
-rw-r--r--yql/essentials/tools/yql_highlight/generate_textmate.cpp124
-rw-r--r--yql/essentials/tools/yql_highlight/generate_textmate.h11
-rw-r--r--yql/essentials/tools/yql_highlight/generate_vim.h11
-rw-r--r--yql/essentials/tools/yql_highlight/generator.cpp31
-rw-r--r--yql/essentials/tools/yql_highlight/generator.h20
-rw-r--r--yql/essentials/tools/yql_highlight/generator_json.cpp15
-rw-r--r--yql/essentials/tools/yql_highlight/generator_json.h9
-rw-r--r--yql/essentials/tools/yql_highlight/generator_textmate.cpp301
-rw-r--r--yql/essentials/tools/yql_highlight/generator_textmate.h11
-rw-r--r--yql/essentials/tools/yql_highlight/generator_vim.cpp (renamed from yql/essentials/tools/yql_highlight/generate_vim.cpp)6
-rw-r--r--yql/essentials/tools/yql_highlight/generator_vim.h9
-rw-r--r--yql/essentials/tools/yql_highlight/ya.make40
-rw-r--r--yql/essentials/tools/yql_highlight/yql_highlight.cpp64
16 files changed, 471 insertions, 201 deletions
diff --git a/yql/essentials/sql/v1/highlight/sql_highlight.cpp b/yql/essentials/sql/v1/highlight/sql_highlight.cpp
index 97cd0e2f8ce..ea82a43e0d0 100644
--- a/yql/essentials/sql/v1/highlight/sql_highlight.cpp
+++ b/yql/essentials/sql/v1/highlight/sql_highlight.cpp
@@ -245,11 +245,11 @@ namespace NSQLHighlight {
THighlighting h;
h.Units.emplace_back(MakeUnit<EUnitKind::Comment>(s));
h.Units.emplace_back(MakeUnit<EUnitKind::Punctuation>(s));
+ h.Units.emplace_back(MakeUnit<EUnitKind::FunctionIdentifier>(s));
h.Units.emplace_back(MakeUnit<EUnitKind::TypeIdentifier>(s));
h.Units.emplace_back(MakeUnit<EUnitKind::Keyword>(s));
h.Units.emplace_back(MakeUnit<EUnitKind::QuotedIdentifier>(s));
h.Units.emplace_back(MakeUnit<EUnitKind::BindParameterIdentifier>(s));
- h.Units.emplace_back(MakeUnit<EUnitKind::FunctionIdentifier>(s));
h.Units.emplace_back(MakeUnit<EUnitKind::Identifier>(s));
h.Units.emplace_back(MakeUnit<EUnitKind::Literal>(s));
h.Units.emplace_back(MakeUnit<EUnitKind::StringLiteral>(s));
diff --git a/yql/essentials/sql/v1/highlight/ut/suite.json b/yql/essentials/sql/v1/highlight/ut/suite.json
index 04e961a6f91..26fb2f3f4f4 100644
--- a/yql/essentials/sql/v1/highlight/ut/suite.json
+++ b/yql/essentials/sql/v1/highlight/ut/suite.json
@@ -47,11 +47,11 @@
["SELECT id, alias from users", "KKKKKK#_#II#P#_#IIIII#_#KKKK#_#IIIII"],
["INSERT INTO users (id, alias) VALUES (12, \"tester\")", "KKKKKK#_#KKKK#_#IIIII#_#P#II#P#_#IIIII#P#_#KKKKKK#_#P#LL#P#_#SSSSSSSS#P"],
["SELECT 123467, \"HeLLo, {name}!\", (1 + (5 * 1 / 0)), MIN(identifier) FROM `local/test/space/table` JOIN test;", "KKKKKK#_#LLLLLL#P#_#SSSSSSSSSSSSSSSS#P#_#P#L#_#P#_#P#L#_#P#_#L#_#P#_#L#P#P#P#_#FFF#P#IIIIIIIIII#P#_#KKKK#_#QQQQQQQQQQQQQQQQQQQQQQQQ#_#KKKK#_#IIII#P"],
- ["SELECT Bool(phone) FROM customer", "KKKKKK#_#TTTT#P#IIIII#P#_#KKKK#_#IIIIIIII"]
+ ["SELECT Bool(phone) FROM customer", "KKKKKK#_#FFFF#P#IIIII#P#_#KKKK#_#IIIIIIII"]
],
"TypeIdentifier": [
["Bool", "TTTT"],
- ["Bool(value)", "TTTT#P#IIIII#P"]
+ ["Bool(value)", "FFFF#P#IIIII#P"]
],
"Identifier": [
["test", "IIII"]
diff --git a/yql/essentials/tools/yql_highlight/artifact/ya.make b/yql/essentials/tools/yql_highlight/artifact/ya.make
index cb3d1c0d464..b9d54aefba3 100644
--- a/yql/essentials/tools/yql_highlight/artifact/ya.make
+++ b/yql/essentials/tools/yql_highlight/artifact/ya.make
@@ -6,8 +6,8 @@ RUN_PROGRAM(
)
RUN_PROGRAM(
- yql/essentials/tools/yql_highlight --generate="textmate"
- STDOUT yql.tmLanguage.json
+ yql/essentials/tools/yql_highlight --generate="tmlanguage"
+ STDOUT YQL.tmLanguage.json
)
RUN_PROGRAM(
@@ -15,4 +15,14 @@ RUN_PROGRAM(
STDOUT yql_new.vim
)
+RUN_PROGRAM(
+ yql/essentials/tools/yql_highlight --generate="tmbundle" --output="${BINDIR}/YQL.tmbundle.zip"
+ OUT ${BINDIR}/YQL.tmbundle.zip
+)
+
+RUN_PROGRAM(
+ yql/essentials/tools/yql_highlight --generate="tmbundle" --output="${BINDIR}/YQL.tmbundle.tar"
+ OUT ${BINDIR}/YQL.tmbundle.tar
+)
+
END()
diff --git a/yql/essentials/tools/yql_highlight/generate_textmate.cpp b/yql/essentials/tools/yql_highlight/generate_textmate.cpp
deleted file mode 100644
index 28244c323d4..00000000000
--- a/yql/essentials/tools/yql_highlight/generate_textmate.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-#include "generate_textmate.h"
-
-#include "json.h"
-
-#include <library/cpp/json/json_value.h>
-
-#include <util/string/builder.h>
-#include <util/string/cast.h>
-
-namespace NSQLHighlight {
-
- namespace {
-
- TString ToTextMateRegex(const TUnit& unit, const NSQLTranslationV1::TRegexPattern& pattern) {
- TStringBuilder regex;
-
- if (pattern.IsCaseInsensitive) {
- regex << "(?i)";
- }
-
- if (unit.IsPlain) {
- regex << R"re(\b)re";
- }
-
- regex << "(" << pattern.Body << ")";
-
- if (!pattern.After.empty()) {
- regex << "(?=" << pattern.After << ")";
- }
-
- if (unit.IsPlain) {
- regex << R"re(\b)re";
- }
-
- return regex;
- }
-
- TStringBuf ToTextMateGroup(EUnitKind kind) {
- switch (kind) {
- case EUnitKind::Keyword:
- return "keyword.control";
- case EUnitKind::Punctuation:
- return "keyword.operator";
- case EUnitKind::QuotedIdentifier:
- return "string.interpolated";
- case EUnitKind::BindParameterIdentifier:
- return "variable.parameter";
- case EUnitKind::TypeIdentifier:
- return "entity.name.type";
- case EUnitKind::FunctionIdentifier:
- return "entity.name.function";
- case EUnitKind::Identifier:
- return "variable.other";
- case EUnitKind::Literal:
- return "constant.numeric";
- case EUnitKind::StringLiteral:
- return "string.quoted.double";
- case EUnitKind::Comment:
- return "comment.block";
- case EUnitKind::Whitespace:
- return "";
- case EUnitKind::Error:
- return "";
- }
- }
-
- TMaybe<NJson::TJsonMap> TextMateMultilinePattern(const TUnit& unit) {
- auto range = unit.RangePattern;
- if (!range) {
- return Nothing();
- }
-
- return NJson::TJsonMap({
- {"begin", range->Begin},
- {"end", range->End},
- {"name", ToTextMateGroup(unit.Kind)},
- });
- }
-
- NJson::TJsonMap ToTextMatePattern(const TUnit& unit, const NSQLTranslationV1::TRegexPattern& pattern) {
- return NJson::TJsonMap({
- {"match", ToTextMateRegex(unit, pattern)},
- {"name", ToTextMateGroup(unit.Kind)},
- });
- }
-
- TString ToTextMateName(EUnitKind kind) {
- return ToString(kind);
- }
-
- } // namespace
-
- void GenerateTextMate(IOutputStream& out, const THighlighting& highlighting) {
- NJson::TJsonMap root;
- root["$schema"] = "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json";
- root["name"] = "yql";
- root["scopeName"] = "source.yql";
- root["fileTypes"] = NJson::TJsonArray({"yql"});
-
- for (const TUnit& unit : highlighting.Units) {
- if (unit.IsCodeGenExcluded) {
- continue;
- }
-
- TString name = ToTextMateName(unit.Kind);
-
- root["patterns"].AppendValue(NJson::TJsonMap({
- {"include", "#" + name},
- }));
-
- for (const NSQLTranslationV1::TRegexPattern& pattern : unit.Patterns) {
- auto textmate = ToTextMatePattern(unit, pattern);
- root["repository"][name]["patterns"].AppendValue(std::move(textmate));
- }
-
- if (auto textmate = TextMateMultilinePattern(unit)) {
- root["repository"][name]["patterns"].AppendValue(*textmate);
- }
- }
-
- Print(out, root);
- }
-
-} // namespace NSQLHighlight
diff --git a/yql/essentials/tools/yql_highlight/generate_textmate.h b/yql/essentials/tools/yql_highlight/generate_textmate.h
deleted file mode 100644
index ac2c32f50c4..00000000000
--- a/yql/essentials/tools/yql_highlight/generate_textmate.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include <yql/essentials/sql/v1/highlight/sql_highlight.h>
-
-#include <util/stream/output.h>
-
-namespace NSQLHighlight {
-
- void GenerateTextMate(IOutputStream& out, const THighlighting& highlighting);
-
-} // namespace NSQLHighlight
diff --git a/yql/essentials/tools/yql_highlight/generate_vim.h b/yql/essentials/tools/yql_highlight/generate_vim.h
deleted file mode 100644
index 54d8e8b41eb..00000000000
--- a/yql/essentials/tools/yql_highlight/generate_vim.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include <yql/essentials/sql/v1/highlight/sql_highlight.h>
-
-#include <util/stream/output.h>
-
-namespace NSQLHighlight {
-
- void GenerateVim(IOutputStream& out, const THighlighting& highlighting);
-
-} // namespace NSQLHighlight
diff --git a/yql/essentials/tools/yql_highlight/generator.cpp b/yql/essentials/tools/yql_highlight/generator.cpp
new file mode 100644
index 00000000000..16a95a60beb
--- /dev/null
+++ b/yql/essentials/tools/yql_highlight/generator.cpp
@@ -0,0 +1,31 @@
+#include "generator.h"
+
+#include <util/stream/file.h>
+
+namespace NSQLHighlight {
+
+ class TOnlyFunctionGenerator: public IGenerator {
+ public:
+ explicit TOnlyFunctionGenerator(std::function<void(IOutputStream&, const THighlighting&)> function)
+ : Function_(std::move(function))
+ {
+ }
+
+ void Write(IOutputStream& out, const THighlighting& highlighting) override {
+ Function_(out, highlighting);
+ }
+
+ void Write(const TFsPath& path, const THighlighting& highlighting) override {
+ TFileOutput out(path);
+ Write(out, highlighting);
+ }
+
+ private:
+ std::function<void(IOutputStream&, const THighlighting&)> Function_;
+ };
+
+ IGenerator::TPtr MakeOnlyFileGenerator(std::function<void(IOutputStream&, const THighlighting&)> function) {
+ return new TOnlyFunctionGenerator(std::move(function));
+ }
+
+} // namespace NSQLHighlight
diff --git a/yql/essentials/tools/yql_highlight/generator.h b/yql/essentials/tools/yql_highlight/generator.h
new file mode 100644
index 00000000000..2d90e9e2c88
--- /dev/null
+++ b/yql/essentials/tools/yql_highlight/generator.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <yql/essentials/sql/v1/highlight/sql_highlight.h>
+
+#include <util/generic/ptr.h>
+#include <util/folder/path.h>
+
+namespace NSQLHighlight {
+
+ class IGenerator: public TThrRefBase {
+ public:
+ using TPtr = TIntrusivePtr<IGenerator>;
+
+ virtual void Write(IOutputStream& out, const THighlighting& highlighting) = 0;
+ virtual void Write(const TFsPath& path, const THighlighting& highlighting) = 0;
+ };
+
+ IGenerator::TPtr MakeOnlyFileGenerator(std::function<void(IOutputStream&, const THighlighting&)> function);
+
+} // namespace NSQLHighlight
diff --git a/yql/essentials/tools/yql_highlight/generator_json.cpp b/yql/essentials/tools/yql_highlight/generator_json.cpp
new file mode 100644
index 00000000000..1d503b54d42
--- /dev/null
+++ b/yql/essentials/tools/yql_highlight/generator_json.cpp
@@ -0,0 +1,15 @@
+#include "generator_json.h"
+
+#include "json.h"
+
+#include <yql/essentials/sql/v1/highlight/sql_highlight_json.h>
+
+namespace NSQLHighlight {
+
+ IGenerator::TPtr MakeJsonGenerator() {
+ return MakeOnlyFileGenerator([](IOutputStream& out, const THighlighting& highlighting) {
+ Print(out, ToJson(highlighting));
+ });
+ }
+
+} // namespace NSQLHighlight
diff --git a/yql/essentials/tools/yql_highlight/generator_json.h b/yql/essentials/tools/yql_highlight/generator_json.h
new file mode 100644
index 00000000000..24f09041ff5
--- /dev/null
+++ b/yql/essentials/tools/yql_highlight/generator_json.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "generator.h"
+
+namespace NSQLHighlight {
+
+ IGenerator::TPtr MakeJsonGenerator();
+
+} // namespace NSQLHighlight
diff --git a/yql/essentials/tools/yql_highlight/generator_textmate.cpp b/yql/essentials/tools/yql_highlight/generator_textmate.cpp
new file mode 100644
index 00000000000..7a0a66c0e3d
--- /dev/null
+++ b/yql/essentials/tools/yql_highlight/generator_textmate.cpp
@@ -0,0 +1,301 @@
+#include "generator_textmate.h"
+
+#include "json.h"
+
+#include <library/cpp/json/json_value.h>
+#include <library/cpp/json/json_writer.h>
+#include <library/cpp/on_disk/tar_archive/archive_writer.h>
+
+#include <util/string/builder.h>
+#include <util/string/cast.h>
+#include <util/memory/blob.h>
+
+namespace NSQLHighlight {
+
+ namespace NTextMate {
+
+ using TRegex = TString;
+
+ struct TRange {
+ TRegex Begin;
+ TRegex End;
+ };
+
+ struct TMatcher {
+ TString Name;
+ TString Group;
+ std::variant<TRegex, TRange> Pattern;
+ };
+
+ struct TLanguage {
+ TString Name;
+ TString ScopeName;
+ TVector<TString> FileTypes;
+ TVector<TMatcher> Matchers;
+ };
+
+ } // namespace NTextMate
+
+ namespace {
+
+ NTextMate::TRegex ToTextMateRegex(const TUnit& unit, const NSQLTranslationV1::TRegexPattern& pattern) {
+ TStringBuilder regex;
+
+ if (pattern.IsCaseInsensitive) {
+ regex << "(?i)";
+ }
+
+ if (unit.IsPlain) {
+ regex << R"re(\b)re";
+ }
+
+ regex << "(" << pattern.Body << ")";
+
+ if (!pattern.After.empty()) {
+ regex << "(?=" << pattern.After << ")";
+ }
+
+ if (unit.IsPlain) {
+ regex << R"re(\b)re";
+ }
+
+ return regex;
+ }
+
+ TString ToTextMateGroup(EUnitKind kind) {
+ switch (kind) {
+ case EUnitKind::Keyword:
+ return "keyword.control";
+ case EUnitKind::Punctuation:
+ return "keyword.operator";
+ case EUnitKind::QuotedIdentifier:
+ return "string.interpolated";
+ case EUnitKind::BindParameterIdentifier:
+ return "variable.parameter";
+ case EUnitKind::TypeIdentifier:
+ return "entity.name.type";
+ case EUnitKind::FunctionIdentifier:
+ return "entity.name.function";
+ case EUnitKind::Identifier:
+ return "variable.other";
+ case EUnitKind::Literal:
+ return "constant.numeric";
+ case EUnitKind::StringLiteral:
+ return "string.quoted.double";
+ case EUnitKind::Comment:
+ return "comment.block";
+ case EUnitKind::Whitespace:
+ return "";
+ case EUnitKind::Error:
+ return "";
+ }
+ }
+
+ TString ToTextMateName(EUnitKind kind) {
+ return ToString(kind);
+ }
+
+ TMaybe<NTextMate::TMatcher> TextMateMultilinePattern(const TUnit& unit) {
+ auto range = unit.RangePattern;
+ if (!range) {
+ return Nothing();
+ }
+
+ return NTextMate::TMatcher{
+ .Name = ToTextMateName(unit.Kind),
+ .Group = ToTextMateGroup(unit.Kind),
+ .Pattern = NTextMate::TRange{
+ .Begin = range->Begin,
+ .End = range->End,
+ },
+ };
+ }
+
+ NTextMate::TMatcher ToTextMatePattern(const TUnit& unit, const NSQLTranslationV1::TRegexPattern& pattern) {
+ return NTextMate::TMatcher{
+ .Name = ToTextMateName(unit.Kind),
+ .Group = ToTextMateGroup(unit.Kind),
+ .Pattern = ToTextMateRegex(unit, pattern),
+ };
+ }
+
+ } // namespace
+
+ NTextMate::TLanguage ToTextMateLanguage(const THighlighting& highlighting) {
+ NTextMate::TLanguage language = {
+ .Name = "YQL",
+ .ScopeName = "source.yql",
+ .FileTypes = {"yql"},
+ };
+
+ for (const TUnit& unit : highlighting.Units) {
+ if (unit.IsCodeGenExcluded) {
+ continue;
+ }
+
+ for (const NSQLTranslationV1::TRegexPattern& pattern : unit.Patterns) {
+ language.Matchers.emplace_back(ToTextMatePattern(unit, pattern));
+ }
+ if (auto textmate = TextMateMultilinePattern(unit)) {
+ language.Matchers.emplace_back(*textmate);
+ }
+ }
+
+ return language;
+ }
+
+ NJson::TJsonValue ToJson(const NTextMate::TMatcher& matcher) {
+ NJson::TJsonMap json = {{"name", matcher.Group}};
+ std::visit([&](const auto& pattern) {
+ using T = std::decay_t<decltype(pattern)>;
+
+ if constexpr (std::is_same_v<T, NTextMate::TRegex>) {
+ json["match"] = pattern;
+ } else if constexpr (std::is_same_v<T, NTextMate::TRange>) {
+ json["begin"] = pattern.Begin;
+ json["end"] = pattern.End;
+ } else {
+ static_assert(false);
+ }
+ }, matcher.Pattern);
+ return json;
+ }
+
+ NJson::TJsonValue ToJson(const NTextMate::TLanguage& language) {
+ NJson::TJsonMap root;
+ root["$schema"] = "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json";
+ root["name"] = language.Name;
+ root["scopeName"] = language.ScopeName;
+
+ for (const TString& type : language.FileTypes) {
+ root["fileTypes"].AppendValue(type);
+ }
+
+ THashSet<TString> visited;
+ for (const NTextMate::TMatcher& matcher : language.Matchers) {
+ root["repository"][matcher.Name]["patterns"].AppendValue(ToJson(matcher));
+
+ if (!visited.contains(matcher.Name)) {
+ root["patterns"].AppendValue(NJson::TJsonMap({{"include", "#" + matcher.Name}}));
+ visited.emplace(matcher.Name);
+ }
+ }
+
+ return root;
+ }
+
+ TString EscapeXML(TString string) {
+ SubstGlobal(string, "<", "&lt;");
+ SubstGlobal(string, ">", "&gt;");
+ return string;
+ }
+
+ void WriteXML(IOutputStream& out, const NJson::TJsonValue& json, TString indent = "") {
+ static constexpr TStringBuf extra = " ";
+
+ if (TString string; json.GetString(&string)) {
+ out << indent << "<string>" << EscapeXML(string) << "</string>" << "\n";
+ } else if (NJson::TJsonValue::TMapType dict; json.GetMap(&dict)) {
+ out << indent << "<dict>" << '\n';
+ for (const auto& [key, value] : dict) {
+ out << indent << extra << "<key>" << EscapeXML(key) << "</key>" << '\n';
+ WriteXML(out, value, indent + extra);
+ }
+ out << indent << "</dict>" << '\n';
+ } else if (NJson::TJsonValue::TArray array; json.GetArray(&array)) {
+ out << indent << "<array>" << '\n';
+ for (const auto& value : array) {
+ WriteXML(out, value, indent + extra);
+ }
+ out << indent << "</array>" << '\n';
+ } else {
+ TStringStream str;
+ Print(str, json);
+ ythrow yexception() << "Unexpected JSON '" + str.Str() + "'";
+ }
+ }
+
+ void GenerateTextMateJson(IOutputStream& out, const THighlighting& highlighting) {
+ Print(out, ToJson(ToTextMateLanguage(highlighting)));
+ }
+
+ class TTextMateBundleGenerator: public IGenerator {
+ private:
+ static constexpr TStringBuf InfoUUID = "9BB0DBAF-E65C-4E14-A6A7-467D4AA535E0";
+ static constexpr TStringBuf SyntaxUUID = "1C3868E4-F96B-4E55-B204-1DCB5A20748B";
+ static constexpr TStringBuf BundleDir = "YQL.tmbundle";
+ static constexpr TStringBuf InfoFile = "info.plist";
+ static constexpr TStringBuf SyntaxFile = "Syntaxes/YQL.tmLanguage";
+
+ template <class TWriter>
+ void Write(
+ NTar::TArchiveWriter& acrhive,
+ TStringBuf path,
+ TWriter writer,
+ const NTextMate::TLanguage& langugage)
+ {
+ TStringStream stream;
+ writer(stream, langugage);
+ TBlob blob = TBlob::FromString(stream.Str());
+ acrhive.WriteFile(TString(path), blob);
+ }
+
+ public:
+ void Write(IOutputStream& out, const THighlighting& highlighting) final {
+ out << "File " << BundleDir << "/" << InfoFile << ":" << '\n';
+ WriteInfo(out, ToTextMateLanguage(highlighting));
+ out << "File " << BundleDir << "/" << SyntaxFile << ":" << '\n';
+ WriteSyntax(out, ToTextMateLanguage(highlighting));
+ }
+
+ void Write(const TFsPath& path, const THighlighting& highlighting) final {
+ if (TString name = path.GetName(); !name.StartsWith(BundleDir)) {
+ ythrow yexception()
+ << "Invalid path '" << name
+ << "', expected '" << BundleDir << "' "
+ << "as an archive name";
+ }
+
+ NTextMate::TLanguage language = ToTextMateLanguage(highlighting);
+
+ NTar::TArchiveWriter archive(path);
+ Write(archive, InfoFile, WriteInfo, language);
+ Write(archive, SyntaxFile, WriteSyntax, language);
+ }
+
+ private:
+ static void WriteInfo(IOutputStream& out, const NTextMate::TLanguage& language) {
+ out << R"(<?xml version="1.0" encoding="UTF-8"?>)" << '\n';
+ out << R"(<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">)" << '\n';
+ out << R"(<plist version="1.0">)" << '\n';
+ out << R"(<dict>)" << '\n';
+ out << R"( <key>name</key>)" << '\n';
+ out << R"( <string>)" << language.Name << R"(</string>)" << '\n';
+ out << R"( <key>uuid</key>)" << '\n';
+ out << R"( <string>)" << InfoUUID << R"(</string>)" << '\n';
+ out << R"(</dict>)" << '\n';
+ out << R"(</plist>)" << '\n';
+ }
+
+ static void WriteSyntax(IOutputStream& out, const NTextMate::TLanguage& language) {
+ NJson::TJsonValue json = ToJson(language);
+ json.EraseValue("$schema");
+ json["uuid"] = SyntaxUUID;
+
+ out << R"(<?xml version="1.0" encoding="UTF-8"?>)" << '\n';
+ out << R"(<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">)" << '\n';
+ out << R"(<plist version="1.0">)" << '\n';
+ WriteXML(out, json);
+ out << R"(</plist>)" << '\n';
+ }
+ };
+
+ IGenerator::TPtr MakeTextMateJsonGenerator() {
+ return MakeOnlyFileGenerator(GenerateTextMateJson);
+ }
+
+ IGenerator::TPtr MakeTextMateBundleGenerator() {
+ return new TTextMateBundleGenerator();
+ }
+
+} // namespace NSQLHighlight
diff --git a/yql/essentials/tools/yql_highlight/generator_textmate.h b/yql/essentials/tools/yql_highlight/generator_textmate.h
new file mode 100644
index 00000000000..d1ffbac87a2
--- /dev/null
+++ b/yql/essentials/tools/yql_highlight/generator_textmate.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "generator.h"
+
+namespace NSQLHighlight {
+
+ IGenerator::TPtr MakeTextMateJsonGenerator();
+
+ IGenerator::TPtr MakeTextMateBundleGenerator();
+
+} // namespace NSQLHighlight
diff --git a/yql/essentials/tools/yql_highlight/generate_vim.cpp b/yql/essentials/tools/yql_highlight/generator_vim.cpp
index e914b5c6e32..bb33157ce2e 100644
--- a/yql/essentials/tools/yql_highlight/generate_vim.cpp
+++ b/yql/essentials/tools/yql_highlight/generator_vim.cpp
@@ -1,4 +1,4 @@
-#include "generate_vim.h"
+#include "generator_vim.h"
#include <yql/essentials/utils/yql_panic.h>
@@ -165,4 +165,8 @@ namespace NSQLHighlight {
out.Flush();
}
+ IGenerator::TPtr MakeVimGenerator() {
+ return MakeOnlyFileGenerator(GenerateVim);
+ }
+
} // namespace NSQLHighlight
diff --git a/yql/essentials/tools/yql_highlight/generator_vim.h b/yql/essentials/tools/yql_highlight/generator_vim.h
new file mode 100644
index 00000000000..1e3927f8417
--- /dev/null
+++ b/yql/essentials/tools/yql_highlight/generator_vim.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "generator.h"
+
+namespace NSQLHighlight {
+
+ IGenerator::TPtr MakeVimGenerator();
+
+} // namespace NSQLHighlight
diff --git a/yql/essentials/tools/yql_highlight/ya.make b/yql/essentials/tools/yql_highlight/ya.make
index c255bcef95d..710d7ff40e6 100644
--- a/yql/essentials/tools/yql_highlight/ya.make
+++ b/yql/essentials/tools/yql_highlight/ya.make
@@ -1,25 +1,25 @@
IF (NOT EXPORT_CMAKE OR NOT OPENSOURCE OR OPENSOURCE_PROJECT != "yt")
+ PROGRAM()
-PROGRAM()
+ PEERDIR(
+ library/cpp/getopt
+ library/cpp/json
+ library/cpp/on_disk/tar_archive
+ yql/essentials/sql/v1/highlight
+ yql/essentials/utils
+ )
-PEERDIR(
- library/cpp/getopt
- library/cpp/json
- yql/essentials/sql/v1/highlight
- yql/essentials/utils
-)
-
-SRCS(
- generate_textmate.cpp
- generate_vim.cpp
- json.cpp
- yql_highlight.cpp
-)
-
-END()
-
-RECURSE(
- artifact
-)
+ SRCS(
+ generator_json.cpp
+ generator_textmate.cpp
+ generator_vim.cpp
+ generator.cpp
+ json.cpp
+ yql_highlight.cpp
+ )
+ END()
+ RECURSE(
+ artifact
+ )
ENDIF()
diff --git a/yql/essentials/tools/yql_highlight/yql_highlight.cpp b/yql/essentials/tools/yql_highlight/yql_highlight.cpp
index ed651881325..4361ee6e106 100644
--- a/yql/essentials/tools/yql_highlight/yql_highlight.cpp
+++ b/yql/essentials/tools/yql_highlight/yql_highlight.cpp
@@ -1,8 +1,7 @@
-#include "generate_textmate.h"
-#include "generate_vim.h"
-#include "json.h"
+#include "generator_json.h"
+#include "generator_textmate.h"
+#include "generator_vim.h"
-#include <yql/essentials/sql/v1/highlight/sql_highlight_json.h>
#include <yql/essentials/sql/v1/highlight/sql_highlight.h>
#include <yql/essentials/sql/v1/highlight/sql_highlighter.h>
@@ -14,23 +13,24 @@
using namespace NSQLHighlight;
-int RunGenerateJSON() {
- THighlighting highlighting = MakeHighlighting();
- Print(Cout, ToJson(highlighting));
- return 0;
-}
+using TGeneratorFactory = std::function<IGenerator::TPtr()>;
-int RunGenerateTextMate() {
- THighlighting highlighting = MakeHighlighting();
- GenerateTextMate(Cout, highlighting);
- return 0;
-}
+using TGeneratorMap = THashMap<TString, TGeneratorFactory>;
-int RunGenerateVim() {
- THighlighting highlighting = MakeHighlighting();
- GenerateVim(Cout, highlighting);
- return 0;
-}
+const TGeneratorMap generators = {
+ {"json", MakeJsonGenerator},
+ {"tmlanguage", MakeTextMateJsonGenerator},
+ {"tmbundle", MakeTextMateBundleGenerator},
+ {"vim", MakeVimGenerator},
+};
+
+const TVector<TString> targets = []() {
+ TVector<TString> result;
+ for (const auto& [name, _] : generators) {
+ result.push_back(name);
+ }
+ return result;
+}();
int RunHighlighter() {
THashMap<EUnitKind, NColorizer::EAnsiCode> ColorByKind = {
@@ -64,28 +64,34 @@ int RunHighlighter() {
int Run(int argc, char* argv[]) {
TString target;
+ TString path;
NLastGetopt::TOpts opts = NLastGetopt::TOpts::Default();
opts.AddLongOption('g', "generate", "generate a highlighting configuration")
.RequiredArgument("target")
- .Choices({"json", "textmate", "vim"})
+ .Choices(targets)
.StoreResult(&target);
+ opts.AddLongOption('o', "output", "path to output file")
+ .OptionalArgument("path")
+ .StoreResult(&path);
opts.SetFreeArgsNum(0);
opts.AddHelpOption();
NLastGetopt::TOptsParseResult res(&opts, argc, argv);
if (res.Has("generate")) {
- if (target == "json") {
- return RunGenerateJSON();
- }
- if (target == "textmate") {
- return RunGenerateTextMate();
+ const TGeneratorFactory* generator = generators.FindPtr(target);
+ Y_ENSURE(generator, "No generator for target '" << target << "'");
+
+ if (res.Has("output")) {
+ TFsPath stdpath(path.c_str());
+ (*generator)()->Write(stdpath, MakeHighlighting());
+ } else {
+ (*generator)()->Write(Cout, MakeHighlighting());
}
- if (target == "vim") {
- return RunGenerateVim();
- }
- Y_ABORT();
+
+ return 0;
}
+
return RunHighlighter();
}