Chroma — Выделитель синтаксиса общего назначения в чистом Go
NOTE: Поскольку Chroma только что выпущен, его API все еще находится в процессе разработки. Тем не менее, интерфейс высокого уровня не должен существенно измениться.
Chroma принимает исходный код и другой структурированный текст и преобразует его в HTML с подсветкой синтаксиса, цветной текст ANSI и т.д.
Chroma в значительной степени основана на Pygments, и включает трансляторы для лексеров и стилей Pygments.
Поддерживаемые языки
Префикс | Язык |
---|---|
A | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Angular2, ANTLR, ApacheConf, APL, AppleScript, Arduino, Awk |
B | Ballerina, Bash, Batchfile, BibTeX, Bicep, BlitzBasic, BNF, Brainfuck, BQN |
C | C, C#, C++, Caddyfile, Caddyfile Directives, Cap'n Proto, Cassandra CQL, Ceylon, CFEngine3, cfstatement, ChaiScript, Chapel, Cheetah, Clojure, CMake, COBOL, CoffeeScript, Common Lisp, Coq, Crystal, CSS, Cython |
D | D, Dart, Diff, Django/Jinja, Docker, DTD, Dylan |
E | EBNF, Elixir, Elm, EmacsLisp, Erlang |
F | Factor, Fish, Forth, Fortran, FSharp |
G | GAS, GDScript, Genshi, Genshi HTML, Genshi Text, Gherkin, GLSL, Gnuplot, Go, Go HTML Template, Go Text Template, GraphQL, Groff, Groovy |
H | Handlebars, Haskell, Haxe, HCL, Hexdump, HLB, HLSL, HTML, HTTP, Hy |
I | Idris, Igor, INI, Io |
J | J, Java, JavaScript, JSON, Julia, Jungle |
K | Kotlin |
L | Конфигурационный файл Lighttpd, LLVM, Lua |
M | Makefile, Mako, markdown, Mason, Mathematica, Matlab, MiniZinc, MLIR, Modula-2, MonkeyC, MorrowindScript, Myghty, MySQL |
N | NASM, Newspeak, конфигурационный файл Nginx, Nim, Nix |
O | Objective-C, OCaml, Octave, OnesEnterprise, OpenEdge ABL, OpenSCAD, Org Mode |
P | PacmanConf, Perl, PHP, PHTML, Pig, PkgConfig, PL/pgSQL, plaintext, Pony, диалект SQL PostgreSQL, PostScript, POVRay, PowerShell, Prolog, PromQL, Properties, Protocol Buffer, PSL, Puppet, Python 2, Python |
Q | QBasic |
R | R, Racket, Ragel, Raku, react, ReasonML, reg, reStructuredText, Rexx, Ruby, Rust |
S | SAS, Sass, Scala, Scheme, Scilab, SCSS, Sed, Smalltalk, Smarty, Snobol, Solidity, SPARQL, SQL, SquidConf, Standard ML, stas, Stylus, Svelte, Swift, SYSTEMD, systemverilog |
T | TableGen, TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turing, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData |
V | VB.net, verilog, VHDL, VHS, VimL, vue |
W | WDTE |
X | XML, Xorg |
Y | YAML, YANG |
Z | Zig |
Я постараюсь поддерживать этот раздел в актуальном состоянии, но авторитетный список можно посмотреть по chroma --list
.
Попробуйте
Попробуйте различные языки и стили на Chroma Playground.
Использование библиотеки
В Chroma, как и в Pygments, есть понятия лексеры, форматеры и стили.
Лексеры преобразуют исходный текст в поток лексем, стили определяют, как типы лексем отображаются на цвета, а форматоры преобразуют лексемы и стили в форматированный вывод.
Для каждого из них существует пакет, содержащий глобальную переменную Registry
со всеми зарегистрированными реализациями. В каждом пакете также есть вспомогательные функции для использования реестра, такие как поиск лексеров по имени или сопоставление имен файлов и т.д.
Во всех случаях, если лексер, форматтер или стиль не могут быть определены, будет возвращено nil
. В этой ситуации вы можете использовать значение по умолчанию Fallback
в каждом соответствующем пакете, которое обеспечивает нормальные значения по умолчанию.
Быстрый запуск
Существует удобная функция, которую можно использовать для простого форматирования исходного текста без каких-либо усилий:
err := quick.Highlight(os.Stdout, someSourceCode, "go", "html", "monokai")
Определение языка
Чтобы выделить код, сначала нужно определить, на каком языке он написан. Есть три основных способа сделать это:
Определить язык по имени файла.
lexer := lexers.Match("foo.go")
Явно указать язык по его синтаксическому идентификатору Chroma (полный список доступен по ссылке
lexers.Names()
).lexer := lexers.Get("go")
Определить язык по содержимому файла.
lexer := lexers.Analyse("package main\n\nfunc main()\n{\n}\n")
Во всех случаях будет возвращено nil
, если язык не может быть определен.
if lexer == nil { lexer = lexers.Fallback }
На этом этапе следует отметить, что некоторые лексеры могут быть чрезвычайно болтливыми. Чтобы смягчить это, вы можете использовать лексер коалесцирующий, чтобы объединить прогоны одинаковых типов лексем в одну лексему:
lexer = chroma.Coalesce(lexer)
Форматирование вывода
После определения языка вам нужно выбрать форматтер и стиль (тему).
style := styles.Get("swapoff") if style == nil { style = styles.Fallback } formatter := formatters.Get("html") if formatter == nil { formatter = formatters.Fallback }
Затем получить итератор по лексемам:
contents, err := ioutil.ReadAll(r) iterator, err := lexer.Tokenise(nil, string(contents))
И, наконец, отформатировать лексемы из итератора:
err := formatter.Format(w, style, iterator)
HTML-форматер
По умолчанию html
зарегистрированный форматтер генерирует отдельный HTML со встроенным CSS. Более гибкие возможности доступны с помощью пакета formatters/html
.
Во-первых, вывод, генерируемый форматером, можно настроить с помощью следующих опций конструктора:
Standalone()
- генерировать отдельный HTML со встроенным CSS.WithClasses()
- использовать классы, а не встроенные атрибуты стиля.ClassPrefix(prefix)
- префикс каждого сгенерированного класса CSS.TabWidth(width)
- установить ширину отображаемой вкладки в символах.WithLineNumbers()
- Вернуть номера строк (стиль сLineNumbers
).WithLinkableLineNumbers()
- Сделать номера строк соединяемыми и ссылкой на самих себя.HighlightLines(ranges)
- Выделите строки в этих диапазонах (стиль сLineHighlight
).LineNumbersInTable()
- Используйте таблицу для форматирования номеров строк и кода, а не диапазонов.
Если используется WithClasses()
, соответствующий CSS может быть получен из форматера с:
formatter := html.New(html.WithClasses(true)) err := formatter.WriteCSS(w, style)
Подробнее
Лексеры
Подробности о реализации лексеров смотрите в документации Pygments. Большинство концепций применимо непосредственно к Chroma, но реальные примеры смотрите в существующих реализациях лексеров.
Во многих случаях лексеры могут быть автоматически преобразованы непосредственно из Pygments с помощью прилагаемого скрипта Python 3 pygments2chroma_xml.py
. Я использую что-то вроде следующего:
python3 _tools/pygments2chroma_xml.py \
pygments.lexers.jvm.KotlinLexer \
> lexers/embedded/kotlin.xml
Список лексеров и заметки о некоторых проблемах их импорта см. в pygments-lexers.txt.
Форматировщики
Chroma поддерживает вывод HTML, а также терминальный вывод в 8, 256 и true-colour цветах.
Включен форматтер noop
, выводящий только текст лексемы, и форматтер tokens
, выводящий необработанные лексемы. Последний полезен для отладки лексеров.
Стили
Стили Chroma определяются в XML. Записи стилей используют тот же синтаксис, что и Pygments.
Все стили Pygments были преобразованы в Chroma с помощью скрипта _tools/style.py
.
Когда вы работаете с одним из стилей Chroma, знайте, что тип лексемы Background
обеспечивает стиль по умолчанию для лексем. Для этого он определяет цвет переднего плана и цвет фона.
Например, здесь для каждого имени токена, не определенного в стиле, по умолчанию используется цвет #f8f8f8
, а для фона выделенного блока кода - #000000
:
<entry type="Background" style="#f8f8f2 bg:#000000"/>
Кроме того, типы лексем в файле стилей иерархичны. Например, когда CommentSpecial
не определен, Chroma использует стиль лексемы из Comment
. Таким образом, если несколько маркеров комментариев используют один и тот же цвет, вам нужно будет только определить Comment
и переопределить тот, который имеет другой цвет.
Для краткого обзора доступных стилей и их внешнего вида посмотрите Chroma Style Gallery.
Интерфейс командной строки
В комплект поставки Chroma входит интерфейс командной строки.
Бинарные файлы доступны для установки на странице релизов.
CLI можно использовать в качестве препроцессора для раскраски вывода less(1)
, см. документацию для переменной окружения LESSOPEN
.
Флаг --fail
можно использовать для подавления вывода и возврата со статусом выхода 1, чтобы облегчить возврат к другому препроцессору в случае, если chroma не определит конкретный лексер для данного файла. Например:
export LESSOPEN='| p() { chroma --fail "$1" || cat "$1"; }; p "%s"'
Замените cat
на ваш любимый резервный препроцессор.
При вызове .lessfilter
автоматически включается флаг --fail
для простой интеграции с lesspipe shipping with Debian and derivatives; для этой установки исполняемый файл chroma
может быть просто симлинкован на ~/.lessfilter
.
Чего не хватает по сравнению с Pygments?
- Довольно много лексеров, по разным причинам (pull-requests welcome):
- Лексеры Pygments для сложных языков часто включают пользовательский код для обработки определенных аспектов, таких как возможность Raku вложить код в регулярные выражения. Это требует времени и усилий для преобразования.
- Я в основном преобразовывал только те языки, о которых слышал, чтобы снизить стоимость переноса.
- Некоторые более эзотерические особенности Pygments опущены для простоты.
- Хотя Chroma API поддерживает обнаружение содержимого, очень немногие языки поддерживают их. У меня есть планы реализовать статистический анализатор в какой-то момент, но не хватает времени.