Update backend dependencies to latest

Update all Go modules in backend/ and backend/_example/memory_store/ to
their latest versions (chroma 2.27, go-redis 9.21, bbolt 1.5, slack 0.27,
golang.org/x/* and others); re-tidy and re-vendor, keep the example module
in sync.

Hold github.com/go-chi/chi/v5 at v5.2.5: v5.3.0 deprecates
middleware.RealIP (IP-spoofing advisories). Switching off RealIP changes
how the client IP is derived for rate limiting and votes, which is a
security decision better made on its own rather than inside a dependency
bump.

go test -race, go vet, golangci-lint and govulncheck all clean on both
modules.
This commit is contained in:
Dmitry Verkhoturov
2026-06-30 18:35:31 +01:00
parent 34ed97b7a6
commit 07c7926453
266 changed files with 26653 additions and 13801 deletions
+111 -78
View File
@@ -1,89 +1,122 @@
version: "2"
run:
tests: true
output:
print-issued-lines: false
show-stats: false
formats:
text:
print-issued-lines: false
colors: true
linters:
enable-all: true
default: all
disable:
- lll
- gocyclo
- dupl
- gochecknoglobals
- funlen
- godox
- wsl
- gocognit
- nolintlint
- testpackage
- godot
- nestif
- paralleltest
- nlreturn
- cyclop
- gci
- gofumpt
- errorlint
- exhaustive
- wrapcheck
- stylecheck
- thelper
- nonamedreturns
- revive
- dupword
- exhaustruct
- varnamelen
- forcetypeassert
- ireturn
- maintidx
- govet
- testableexamples
- musttag
- prealloc
- dupl
- godoclint
- cyclop
- depguard
- goconst
- perfsprint
- dupword
- err113
- errname
- errorlint
- exhaustruct
- forbidigo
- forcetypeassert
- funlen
- gochecknoglobals
- gocognit
- gocritic
- gocyclo
- godot
- godox
- gomoddirectives
- ireturn
- lll
- maintidx
- mnd
- nakedret
- nestif
- nilnil
- nlreturn
- nolintlint
- nonamedreturns
- paralleltest
- perfsprint
- predeclared
- recvcheck
- tenv
- err113
linters-settings:
gocyclo:
min-complexity: 10
dupl:
threshold: 100
goconst:
min-len: 8
min-occurrences: 3
forbidigo:
#forbid:
# - (Must)?NewLexer$
exclude_godoc_examples: false
- revive
- testpackage
- varnamelen
- wastedassign
- whitespace
- wsl
- wsl_v5
- funcorder
- noinlineerr
- tagalign
- goconst
- gochecknoinits
- durationcheck
- embeddedstructfieldcheck
- wrapcheck
- gomodguard
settings:
dupl:
threshold: 100
exhaustive:
default-signifies-exhaustive: true
goconst:
min-len: 8
min-occurrences: 3
gocyclo:
min-complexity: 10
wrapcheck:
report-internal-errors: false
ignore-package-globs:
- github.com/alecthomas/errors
exclusions:
generated: lax
rules:
- path: (.+)\.go$
text: "^(G104|G204|G307|G304):"
- path: (.+)\.go$
text: Error return value of .(.*\.Help|.*\.MarkFlagRequired|(os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked
- path: (.+)\.go$
text: exported method `(.*\.MarshalJSON|.*\.UnmarshalJSON|.*\.EntityURN|.*\.GoString|.*\.Pos)` should have comment or be unexported
- path: (.+)\.go$
text: uses unkeyed fields
- path: (.+)\.go$
text: declaration of "err" shadows declaration
- path: (.+)\.go$
text: bad syntax for struct tag key
- path: (.+)\.go$
text: bad syntax for struct tag pair
- path: (.+)\.go$
text: ^ST1012
- path: (.+)\.go$
text: log/slog.Logger.*must not be called
- path: (.+)_test\.go$
text: error returned from external package is unwrapped
- linters: [staticcheck]
text: QF1008
- text: "Error return value of `.*.Write` is not checked"
linters: [errcheck]
path: (.+)_test\.go$
paths:
- third_party$
- builtin$
- examples$
issues:
exclude-dirs:
- _examples
max-per-linter: 0
max-same: 0
exclude-use-default: false
exclude:
# Captured by errcheck.
- '^(G104|G204):'
# Very commonly not checked.
- 'Error return value of .(.*\.Help|.*\.MarkFlagRequired|(os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked'
- 'exported method (.*\.MarshalJSON|.*\.UnmarshalJSON|.*\.EntityURN|.*\.GoString|.*\.Pos) should have comment or be unexported'
- 'composite literal uses unkeyed fields'
- 'declaration of "err" shadows declaration'
- 'should not use dot imports'
- 'Potential file inclusion via variable'
- 'should have comment or be unexported'
- 'comment on exported var .* should be of the form'
- 'at least one file in a package should have a package comment'
- 'string literal contains the Unicode'
- 'methods on the same type should have the same receiver name'
- '_TokenType_name should be _TokenTypeName'
- '`_TokenType_map` should be `_TokenTypeMap`'
- 'rewrite if-else to switch statement'
max-issues-per-linter: 0
max-same-issues: 0
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
+1 -1
View File
@@ -1,6 +1,6 @@
Chroma is a syntax highlighting library, tool and web playground for Go. It is based on Pygments and includes importers for it, so most of the same concepts from Pygments apply to Chroma.
This project is written in Go, uses Hermit to manage tooling, and Just for helper commands. Helper scripts are in ./scripts.
This project is written in Go, uses Hermit to manage tooling, and Just for helper commands. Helper tooling is primarily in ./_tools.
Language definitions are XML files defined in ./lexers/embedded/*.xml.
+93
View File
@@ -0,0 +1,93 @@
let version = exec("git describe --tags --dirty --always") | trim
# TinyGo's installation root; used to source `wasm_exec.js`.
let tinygoroot = exec("tinygo env TINYGOROOT") | trim
# Generate tokentype_enumer.go from types.go via `//go:generate`.
tokentype = go.generate {
package = "."
inputs = ["types.go"]
outputs = ["tokentype_enumer.go"]
}
# Regenerate the lexer table in README.md by invoking the host `chroma` binary.
# GOOS/GOARCH are cleared so cross-compile env vars don't break the local run.
protected readme = exec {
command = "./table.py"
inputs = ["table.py", "lexers/**/*.go", "lexers/**/*.xml"]
output = "README.md"
}
# Format frontend JS sources in place. Runs as a sub-step of `index-min-js`,
# so bundling always sees formatted sources.
format-js = exec {
command = "biome format --write cmd/chromad/static/index.js cmd/chromad/static/chroma.js"
inputs = ["biome.js", "cmd/chromad/static/index.js", "cmd/chromad/static/chroma.js"]
}
# Copy TinyGo's wasm_exec.js into the chromad static assets.
wasm-exec = exec {
command = "install -m644 '#{tinygoroot}/targets/wasm_exec.js' cmd/chromad/static/wasm_exec.js"
resolve = "sha256 '#{tinygoroot}/targets/wasm_exec.js'"
output = "cmd/chromad/static/wasm_exec.js"
}
# Build the chroma WASM module via tinygo (installed via hermit) for the
# smaller output binary.
chroma-wasm = exec {
command = "tinygo build -no-debug -target wasm -o cmd/chromad/static/chroma.wasm cmd/libchromawasm/main.go"
inputs = ["cmd/libchromawasm/**/*.go", "*.go", "lexers/**/*.go", "lexers/**/*.xml", "formatters/**/*.go", "styles/**/*.go"]
output = "cmd/chromad/static/chroma.wasm"
}
# Bundle and minify the frontend JS. Depends on `format-js` so the bundle
# always reflects formatted sources.
index-min-js = exec {
command = "esbuild --platform=browser --format=esm --bundle cmd/chromad/static/index.js --minify --external:./wasm_exec.js --outfile=cmd/chromad/static/index.min.js"
inputs = ["cmd/chromad/static/index.js", "cmd/chromad/static/chroma.js"]
output = "cmd/chromad/static/index.min.js"
depends_on = [format-js]
}
# Bundle and minify the frontend CSS.
index-min-css = exec {
command = "esbuild --bundle cmd/chromad/static/index.css --minify --outfile=cmd/chromad/static/index.min.css"
inputs = ["cmd/chromad/static/index.css", "cmd/chromad/static/bulma.css"]
output = "cmd/chromad/static/index.min.css"
}
# Build the chromad server binary. cmd/chromad is a separate Go module, so
# `dir` puts the build in there and `package = "."` resolves against that
# module. `output` stays project-root-relative; bit absolutises it before
# passing to `go build -o`. Defaults to linux/amd64 to match the deploy
# target; override with GOOS/GOARCH env vars for local builds.
chromad = go.exe {
dir = "cmd/chromad"
package = "."
output = "build/chromad"
flags = ["-ldflags", "-X 'main.version=#{version}'"]
goos = env("GOOS", "linux")
goarch = env("GOARCH", "amd64")
cgo = false
depends_on = [wasm-exec, chroma-wasm, index-min-js, index-min-css, test]
}
pre format-go = go.fmt {
package = "./..."
}
# Run Go tests.
test = go.test {
package = "./..."
}
# Deploy chromad to swapoff.org. Must be explicitly selected.
explicit upload = exec {
command = <<-EOF
scp #{chromad.path} root@swapoff.org:
ssh root@swapoff.org 'install -m755 ./chromad /srv/http/swapoff.org/bin && service chromad restart'
EOF
depends_on = [chromad]
}
target default = [test, chromad, readme, tokentype]
-24
View File
@@ -1,24 +0,0 @@
VERSION = %(git describe --tags --dirty --always)%
export CGOENABLED = 0
tokentype_enumer.go: types.go
build: go generate
# Regenerate the list of lexers in the README
README.md: lexers/*.go lexers/*/*.xml table.py
build: ./table.py
-clean
implicit %{1}%{2}.min.%{3}: **/*.{css,js}
build: esbuild --bundle %{IN} --minify --outfile=%{OUT}
implicit build/%{1}: cmd/*
cd cmd/%{1}
inputs: cmd/%{1}/**/* **/*.go
build: go build -ldflags="-X 'main.version=%{VERSION}'" -o ../../build/%{1} .
#upload: chromad
# build:
# scp chromad root@swapoff.org:
# ssh root@swapoff.org 'install -m755 ./chromad /srv/http/swapoff.org/bin && service chromad restart'
# touch upload
+1 -1
View File
@@ -28,7 +28,7 @@ ENV GOARCH=amd64
RUN just chromad
# Runtime stage
FROM alpine:3.23 AS runtime
FROM alpine:3.24 AS runtime
# Install ca-certificates for HTTPS requests
RUN apk --no-cache add ca-certificates curl
+5 -1
View File
@@ -1,4 +1,4 @@
set positional-arguments := true
set positional-arguments
set shell := ["bash", "-c"]
version := `git describe --tags --dirty --always`
@@ -21,6 +21,10 @@ tokentype-string:
format-js:
biome format --write cmd/chromad/static/index.js cmd/chromad/static/chroma.js
# Tidy Go modules
tidy:
find . -name 'go.mod' -execdir go mod tidy \;
# Build chromad binary
chromad: wasm-exec chroma-wasm
#!/usr/bin/env bash
+9 -7
View File
@@ -36,25 +36,25 @@ translators for Pygments lexers and styles.
| Prefix | Language
| :----: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| A | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Agda, AL, Alloy, AMPL, Angular2, ANTLR, ApacheConf, APL, AppleScript, ArangoDB AQL, Arduino, ArmAsm, ATL, AutoHotkey, AutoIt, Awk
| A | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Agda, AL, Alloy, AMPL, Angular2, ANTLR, ApacheConf, APL, AppleScript, ArangoDB AQL, Arduino, ArmAsm, Arturo, ATL, AutoHotkey, AutoIt, Awk
| B | Ballerina, Bash, Bash Session, Batchfile, Beef, BibTeX, Bicep, BlitzBasic, BNF, BQN, Brainfuck
| C | C, C#, C++, C3, Caddyfile, Caddyfile Directives, Cap'n Proto, Cassandra CQL, Ceylon, CFEngine3, cfstatement, ChaiScript, Chapel, Cheetah, Clojure, CMake, COBOL, CoffeeScript, Common Lisp, Coq, Core, Crystal, CSS, CSV, CUE, Cython
| D | D, Dart, Dax, Desktop file, Diff, Django/Jinja, dns, Docker, DTD, Dylan
| E | EBNF, Elixir, Elm, EmacsLisp, Erlang
| D | D, Dart, Dax, Desktop file, Devicetree, Diff, Django/Jinja, dns, Docker, DTD, Dylan
| E | EBNF, Elixir, Elm, EmacsLisp, ERB, Erlang
| F | Factor, Fennel, Fish, Forth, Fortran, FortranFixed, FSharp
| G | GAS, GDScript, GDScript3, Gemtext, Genshi, Genshi HTML, Genshi Text, Gettext, Gherkin, Gleam, GLSL, Gnuplot, Go, Go HTML Template, Go Template, Go Text Template, GraphQL, Groff, Groovy
| H | Handlebars, Hare, Haskell, Haxe, HCL, Hexdump, HLB, HLSL, HolyC, HTML, HTTP, Hy
| I | Idris, Igor, INI, Io, ISCdhcpd
| J | J, Janet, Java, JavaScript, JSON, JSONata, Jsonnet, Julia, Jungle
| K | Kakoune, Kotlin
| L | Lean4, Lighttpd configuration file, LLVM, lox, Lua, Luau
| M | Makefile, Mako, markdown, Markless, Mason, Materialize SQL dialect, Mathematica, Matlab, MCFunction, Meson, Metal, MiniZinc, MLIR, Modelica, Modula-2, Mojo, MonkeyC, MoonScript, MorrowindScript, Myghty, MySQL
| K | Kakoune, KDL, Kotlin
| L | Lateralus, Lean4, Lighttpd configuration file, LilyPond, LLVM, lox, Lua, Luau
| M | Makefile, Mako, markdown, Markless, Mason, Materialize SQL dialect, Mathematica, Matlab, MCFunction, Meson, Metal, microcad, MiniZinc, MLIR, Modelica, Modula-2, Mojo, MonkeyC, MoonBit, MoonScript, MorrowindScript, Myghty, MySQL
| N | NASM, Natural, NDISASM, Newspeak, Nginx configuration file, Nim, Nix, NSIS, Nu
| O | Objective-C, ObjectPascal, OCaml, Octave, Odin, OnesEnterprise, OpenEdge ABL, OpenSCAD, Org Mode
| P | PacmanConf, Perl, PHP, PHTML, Pig, PkgConfig, PL/pgSQL, plaintext, Plutus Core, Pony, PostgreSQL SQL dialect, PostScript, POVRay, PowerQuery, PowerShell, Prolog, Promela, PromQL, properties, Protocol Buffer, Protocol Buffer Text Format, PRQL, PSL, Puppet, Python, Python 2
| Q | QBasic, QML
| R | R, Racket, Ragel, Raku, react, ReasonML, reg, Rego, reStructuredText, Rexx, RGBDS Assembly, Ring, RPGLE, RPMSpec, Ruby, Rust
| S | SAS, Sass, Scala, Scheme, Scilab, SCSS, Sed, Sieve, Smali, Smalltalk, Smarty, SNBT, Snobol, Solidity, SourcePawn, Spade, SPARQL, SQL, SquidConf, Standard ML, stas, Stylus, Svelte, Swift, SYSTEMD, systemverilog
| S | SAS, Sass, Scala, scdoc, Scheme, Scilab, SCSS, Sed, Sieve, Smali, Smalltalk, Smarty, SNBT, Snobol, Solidity, SourcePawn, Spade, SPARQL, SQL, SquidConf, Standard ML, stas, Stylus, Svelte, Swift, SYSTEMD, systemverilog
| T | TableGen, Tal, TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turing, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData, Typst
| U | ucode
| V | V, V shell, Vala, VB.net, verilog, VHDL, VHS, VimL, vue
@@ -276,6 +276,8 @@ for that setup the `chroma` executable can be just symlinked to `~/.lessfilter`.
its input using Chroma
* [Hugo](https://gohugo.io/) is a static site generator that [uses Chroma for syntax
highlighting code examples](https://gohugo.io/content-management/syntax-highlighting/)
* [f4](https://github.com/unxed/f4) is asynchronious cross platform Far Manager clone in Go
that uses Chroma for syntax highlighting in built-in editor
## Testing lexers
+1 -1
View File
@@ -52,7 +52,7 @@ type Colour int32
// NewColour creates a Colour directly from RGB values.
func NewColour(r, g, b uint8) Colour {
return ParseColour(fmt.Sprintf("%02x%02x%02x", r, g, b))
return Colour(int32(r)<<16|int32(g)<<8|int32(b)) + 1
}
// Distance between this colour and another.
+57 -13
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"html"
"io"
"slices"
"sort"
"strconv"
"strings"
@@ -83,6 +84,11 @@ func WithPreWrapper(wrapper PreWrapper) Option {
}
}
// WithModeClasses adds the style's mode (eg. "light" or "dark") as a CSS
// class on wrapper elements and scopes WriteCSS rules by mode. This enables
// combining light and dark stylesheets and switching themes at runtime.
func WithModeClasses(b bool) Option { return func(f *Formatter) { f.modeClasses = b } }
// WrapLongLines wraps long lines.
func WrapLongLines(b bool) Option {
return func(f *Formatter) {
@@ -206,6 +212,7 @@ type Formatter struct {
inlineCode bool
preventSurroundingPre bool
tabWidth int
modeClasses bool
wrapLongLines bool
lineNumbers bool
lineNumbersInTable bool
@@ -241,7 +248,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
fmt.Fprintf(w, "body { %s; }\n", css[chroma.Background])
fmt.Fprint(w, "</style>")
}
fmt.Fprintf(w, "<body%s>\n", f.styleAttr(css, chroma.Background))
fmt.Fprintf(w, "<body%s>\n", f.styleAttrWithMode(css, chroma.Background, style))
}
wrapInTable := f.lineNumbers && f.lineNumbersInTable
@@ -252,10 +259,10 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
if wrapInTable {
// List line numbers in its own <td>
fmt.Fprintf(w, "<div%s>\n", f.styleAttr(css, chroma.PreWrapper))
fmt.Fprintf(w, "<div%s>\n", f.styleAttrWithMode(css, chroma.PreWrapper, style))
fmt.Fprintf(w, "<table%s><tr>", f.styleAttr(css, chroma.LineTable))
fmt.Fprintf(w, "<td%s>\n", f.styleAttr(css, chroma.LineTableTD))
fmt.Fprintf(w, "%s", f.preWrapper.Start(false, f.styleAttr(css, chroma.PreWrapper)))
fmt.Fprintf(w, "%s", f.preWrapper.Start(false, f.styleAttrWithMode(css, chroma.PreWrapper, style)))
for index := range lines {
line := f.baseLineNumber + index
highlight, next := f.shouldHighlight(highlightIndex, line)
@@ -277,7 +284,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
fmt.Fprintf(w, "<td%s>\n", f.styleAttr(css, chroma.LineTableTD, "width:100%"))
}
fmt.Fprintf(w, "%s", f.preWrapper.Start(true, f.styleAttr(css, chroma.PreWrapper)))
fmt.Fprintf(w, "%s", f.preWrapper.Start(true, f.styleAttrWithMode(css, chroma.PreWrapper, style)))
highlightIndex = 0
for index, tokens := range lines {
@@ -288,7 +295,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
highlightIndex++
}
if !(f.preventSurroundingPre || f.inlineCode) {
if !f.preventSurroundingPre && !f.inlineCode {
// Start of Line
fmt.Fprint(w, `<span`)
@@ -321,7 +328,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
fmt.Fprint(w, html)
}
if !(f.preventSurroundingPre || f.inlineCode) {
if !f.preventSurroundingPre && !f.inlineCode {
fmt.Fprint(w, `</span>`) // End of CodeLine
fmt.Fprint(w, `</span>`) // End of Line
@@ -414,6 +421,26 @@ func (f *Formatter) styleAttr(styles map[chroma.TokenType]string, tt chroma.Toke
return fmt.Sprintf(` style="%s"`, strings.Join(css, ";"))
}
// modeClass returns the CSS class corresponding to the style's mode (eg.
// "light" or "dark"), with the formatter's class prefix applied.
func (f *Formatter) modeClass(style *chroma.Style) string {
return f.prefix + style.Mode().String()
}
// styleAttrWithMode is like styleAttr but, in classes mode, appends the
// style's mode class alongside the existing class. Used for the outer
// wrapper and standalone <body> so external CSS can target the mode.
func (f *Formatter) styleAttrWithMode(styles map[chroma.TokenType]string, tt chroma.TokenType, style *chroma.Style) string {
if !f.Classes || !f.modeClasses {
return f.styleAttr(styles, tt)
}
cls := f.class(tt)
if cls == "" {
return ""
}
return fmt.Sprintf(` class="%s %s"`, cls, f.modeClass(style))
}
func (f *Formatter) tabWidthStyle() string {
if f.tabWidth != 0 && f.tabWidth != 8 {
return fmt.Sprintf("-moz-tab-size: %[1]d; -o-tab-size: %[1]d; tab-size: %[1]d;", f.tabWidth)
@@ -437,20 +464,38 @@ func (f *Formatter) writeCSSRule(w io.Writer, comment string, selector string, s
}
// WriteCSS writes CSS style definitions (without any surrounding HTML).
//
// Rules are scoped by the style's mode (eg. ".chroma.dark") so that CSS
// generated from a light and dark style can be combined without conflict.
// To support dynamic theme switching, call WriteCSS with both styles,
// concatenate the output, and toggle the wrapper's mode class (added
// automatically by Format) at runtime. Tokens that one theme leaves
// unstyled fall back to that theme's ".chroma.<mode>" text/background
// via the CSS cascade; pass WithAllClasses(true) if you need every
// token's rule materialised explicitly for both themes.
func (f *Formatter) WriteCSS(w io.Writer, style *chroma.Style) error {
css := f.styleCache.get(style, false)
var chromaSel, bgSel string
if f.modeClasses {
modeCls := f.modeClass(style)
chromaSel = fmt.Sprintf(".%schroma.%s", f.prefix, modeCls)
bgSel = fmt.Sprintf(".%sbg.%s", f.prefix, modeCls)
} else {
chromaSel = fmt.Sprintf(".%schroma", f.prefix)
bgSel = fmt.Sprintf(".%sbg", f.prefix)
}
// Special-case background as it is mapped to the outer ".chroma" class.
if err := f.writeCSSRule(w, chroma.Background.String(), fmt.Sprintf(".%sbg", f.prefix), css[chroma.Background]); err != nil {
if err := f.writeCSSRule(w, chroma.Background.String(), bgSel, css[chroma.Background]); err != nil {
return err
}
// Special-case PreWrapper as it is the ".chroma" class.
if err := f.writeCSSRule(w, chroma.PreWrapper.String(), fmt.Sprintf(".%schroma", f.prefix), css[chroma.PreWrapper]); err != nil {
if err := f.writeCSSRule(w, chroma.PreWrapper.String(), chromaSel, css[chroma.PreWrapper]); err != nil {
return err
}
// Special-case code column of table to expand width.
if f.lineNumbers && f.lineNumbersInTable {
selector := fmt.Sprintf(".%schroma .%s:last-child", f.prefix, f.class(chroma.LineTableTD))
selector := fmt.Sprintf("%s .%s:last-child", chromaSel, f.class(chroma.LineTableTD))
if err := f.writeCSSRule(w, chroma.LineTableTD.String(), selector, "width: 100%;"); err != nil {
return err
}
@@ -460,7 +505,7 @@ func (f *Formatter) WriteCSS(w io.Writer, style *chroma.Style) error {
targetedLineCSS := StyleEntryToCSS(style.Get(chroma.LineHighlight))
for _, tt := range []chroma.TokenType{chroma.LineNumbers, chroma.LineNumbersTable} {
comment := fmt.Sprintf("%s targeted by URL anchor", tt)
selector := fmt.Sprintf(".%schroma .%s:target", f.prefix, f.class(tt))
selector := fmt.Sprintf("%s .%s:target", chromaSel, f.class(tt))
if err := f.writeCSSRule(w, comment, selector, targetedLineCSS); err != nil {
return err
}
@@ -481,7 +526,7 @@ func (f *Formatter) WriteCSS(w io.Writer, style *chroma.Style) error {
if class == "" {
continue
}
if err := f.writeCSSRule(w, tt.String(), fmt.Sprintf(".%schroma .%s", f.prefix, class), css[tt]); err != nil {
if err := f.writeCSSRule(w, tt.String(), fmt.Sprintf("%s .%s", chromaSel, class), css[tt]); err != nil {
return err
}
}
@@ -613,8 +658,7 @@ func (l *styleCache) get(style *chroma.Style, compress bool) map[chroma.TokenTyp
defer l.mu.Unlock()
// Look for an existing entry.
for i := len(l.cache) - 1; i >= 0; i-- {
entry := l.cache[i]
for i, entry := range slices.Backward(l.cache) {
if entry.style == style && entry.compressed == compress {
// Top of the cache, no need to adjust the order.
if i == len(l.cache)-1 {
@@ -0,0 +1,119 @@
<lexer>
<config>
<name>Arturo</name>
<alias>arturo</alias>
<alias>art</alias>
<filename>*.art</filename>
</config>
<rules>
<state name="root">
<rule pattern=";.*?$"><token type="CommentSingle"/></rule>
<rule pattern="^((\s#!)|(#!)).*?$"><token type="CommentHashbang"/></rule>
<rule pattern="(false|true|maybe)\b"><token type="NameConstant"/></rule>
<rule pattern="\b(this|init)\b\??:?"><token type="NameBuiltinPseudo"/></rule>
<rule pattern="`.`"><token type="LiteralStringChar"/></rule>
<rule pattern="\\\w+\b\??:?"><token type="NameProperty"/></rule>
<rule pattern="#\w+"><token type="NameConstant"/></rule>
<rule pattern="\b[0-9]+\.[0-9]+"><token type="LiteralNumberFloat"/></rule>
<rule pattern="\b[0-9]+"><token type="LiteralNumberInteger"/></rule>
<rule pattern="\w+\b\??:"><token type="NameLabel"/></rule>
<rule pattern="\&#x27;(?:\w+\b\??:?)"><token type="KeywordDeclaration"/></rule>
<rule pattern="\:\w+"><token type="KeywordType"/></rule>
<rule pattern="\.\w+\??:?"><token type="NameAttribute"/></rule>
<rule pattern="(\()(.*?)(\)\?)"><bygroups><token type="Punctuation"/><usingself state="root"/><token type="Punctuation"/></bygroups></rule>
<rule pattern="&quot;"><token type="LiteralStringDouble"/><push state="inside-simple-string"/></rule>
<rule pattern="»"><token type="LiteralStringSingle"/><push state="inside-smart-string"/></rule>
<rule pattern="«««"><token type="LiteralStringDouble"/><push state="inside-safe-string"/></rule>
<rule pattern="\{\/"><token type="LiteralStringSingle"/><push state="inside-regex-string"/></rule>
<rule pattern="\{\:"><token type="LiteralStringDouble"/><push state="inside-curly-verb-string"/></rule>
<rule pattern="(\{)(\!)(\w+)(\s|\n)([\w\W]*?)(^\})">
<usingbygroup>
<sublexer_name_group>3</sublexer_name_group>
<code_group>5</code_group>
<emitters>
<token type="LiteralStringDouble"/>
<token type="LiteralStringInterpol"/>
<token type="LiteralStringInterpol"/>
<token type="TextWhitespace"/>
<token type="LiteralString"/>
<token type="LiteralStringDouble"/>
</emitters>
</usingbygroup>
</rule>
<rule pattern="\{"><token type="LiteralStringSingle"/><push state="inside-curly-string"/></rule>
<rule pattern="\-{3,}"><token type="LiteralStringSingle"/><push state="inside-eof-string"/></rule>
<rule><include state="builtin-functions"/></rule>
<rule pattern="[()[\],]"><token type="Punctuation"/></rule>
<rule pattern="(\-&gt;|==&gt;|\||::|@|\#|\$|\&amp;|!|!!|\./)"><token type="NameDecorator"/></rule>
<rule pattern="(&lt;:|:&gt;|:&lt;|&gt;:|&lt;\\|&lt;&gt;|&lt;|&gt;|ø|∞|\+|\-|\*|\~|=|\^|%|/|//|==&gt;|&lt;=&gt;|&lt;==&gt;|=&gt;&gt;|&lt;&lt;=&gt;&gt;|&lt;&lt;==&gt;&gt;|\-\-&gt;|&lt;\-&gt;|&lt;\-\-&gt;|=\||\|=|\-:|:\-|_|\.|\.\.|\\)"><token type="Operator"/></rule>
<rule pattern="\b\w+"><token type="Name"/></rule>
<rule pattern="\s+"><token type="TextWhitespace"/></rule>
<rule pattern=".+$"><token type="Error"/></rule>
</state>
<state name="inside-interpol">
<rule pattern="\|"><token type="LiteralStringInterpol"/><pop depth="1"/></rule>
<rule pattern="[^|]+"><usingself state="root"/></rule>
</state>
<state name="inside-template">
<rule pattern="\|\|\&gt;"><token type="LiteralStringInterpol"/><pop depth="1"/></rule>
<rule pattern="[^|]+"><usingself state="root"/></rule>
</state>
<state name="string-escape">
<rule pattern="(\\\\|\\n|\\t|\\&quot;)"><token type="LiteralStringEscape"/></rule>
</state>
<state name="inside-simple-string">
<rule><include state="string-escape"/></rule>
<rule pattern="\|"><token type="LiteralStringInterpol"/><push state="inside-interpol"/></rule>
<rule pattern="\&lt;\|\|"><token type="LiteralStringInterpol"/><push state="inside-template"/></rule>
<rule pattern="&quot;"><token type="LiteralStringDouble"/><pop depth="1"/></rule>
<rule pattern="[^|&quot;]+"><token type="LiteralString"/></rule>
</state>
<state name="inside-smart-string">
<rule><include state="string-escape"/></rule>
<rule pattern="\|"><token type="LiteralStringInterpol"/><push state="inside-interpol"/></rule>
<rule pattern="\&lt;\|\|"><token type="LiteralStringInterpol"/><push state="inside-template"/></rule>
<rule pattern="\n"><token type="LiteralStringSingle"/><pop depth="1"/></rule>
<rule pattern="[^|\n]+"><token type="LiteralString"/></rule>
</state>
<state name="inside-safe-string">
<rule><include state="string-escape"/></rule>
<rule pattern="\|"><token type="LiteralStringInterpol"/><push state="inside-interpol"/></rule>
<rule pattern="\&lt;\|\|"><token type="LiteralStringInterpol"/><push state="inside-template"/></rule>
<rule pattern="»»»"><token type="LiteralStringDouble"/><pop depth="1"/></rule>
<rule pattern="[^|»]+"><token type="LiteralString"/></rule>
</state>
<state name="inside-regex-string">
<rule pattern="\\[sSwWdDbBZApPxucItnvfr0]+"><token type="LiteralStringEscape"/></rule>
<rule pattern="\|"><token type="LiteralStringInterpol"/><push state="inside-interpol"/></rule>
<rule pattern="\&lt;\|\|"><token type="LiteralStringInterpol"/><push state="inside-template"/></rule>
<rule pattern="\/\}"><token type="LiteralStringSingle"/><pop depth="1"/></rule>
<rule pattern="[^|\/]+"><token type="LiteralStringRegex"/></rule>
</state>
<state name="inside-curly-verb-string">
<rule><include state="string-escape"/></rule>
<rule pattern="\|"><token type="LiteralStringInterpol"/><push state="inside-interpol"/></rule>
<rule pattern="\&lt;\|\|"><token type="LiteralStringInterpol"/><push state="inside-template"/></rule>
<rule pattern="\:\}"><token type="LiteralStringDouble"/><pop depth="1"/></rule>
<rule pattern="[^|&lt;:]+"><token type="LiteralString"/></rule>
</state>
<state name="inside-curly-string">
<rule><include state="string-escape"/></rule>
<rule pattern="\|"><token type="LiteralStringInterpol"/><push state="inside-interpol"/></rule>
<rule pattern="\&lt;\|\|"><token type="LiteralStringInterpol"/><push state="inside-template"/></rule>
<rule pattern="\}"><token type="LiteralStringSingle"/><pop depth="1"/></rule>
<rule pattern="[^|&lt;}]+"><token type="LiteralString"/></rule>
</state>
<state name="inside-eof-string">
<rule><include state="string-escape"/></rule>
<rule pattern="\|"><token type="LiteralStringInterpol"/><push state="inside-interpol"/></rule>
<rule pattern="\&lt;\|\|"><token type="LiteralStringInterpol"/><push state="inside-template"/></rule>
<rule pattern="\Z"><token type="LiteralStringSingle"/><pop depth="1"/></rule>
<rule pattern="[^|&lt;]+"><token type="LiteralString"/></rule>
</state>
<state name="builtin-functions">
<rule pattern="\b(all|and|any|ascii|attr|attribute|attributeLabel|binary|blockchar|contains|database|date|dictionary|empty|equal|even|every|exists|false|floatin|function|greater|greaterOrEqual|if|in|inline|integer|is|key|label|leap|less|lessOrEqual|literal|logical|lower|nand|negative|nor|not|notEqual|null|numeric|odd|or|path|pathLabel|positive|prefix|prime|set|some|sorted|standalone|string|subset|suffix|superset|ymbol|true|try|type|unless|upper|when|whitespace|word|xnor|xor|zero)\b\?"><token type="NameBuiltin"/></rule>
<rule pattern="\b(abs|acos|acosh|acsec|acsech|actan|actanh|add|after|alphabet|and|angle|append|arg|args|arity|array|as|asec|asech|asin|asinh|atan|atan2|atanh|attr|attrs|average|before|benchmark|blend|break|builtins1|builtins2|call|capitalize|case|ceil|chop|chunk|clear|close|cluster|color|combine|conj|continue|copy|cos|cosh|couple|csec|csech|ctan|ctanh|cursor|darken|dec|decode|decouple|define|delete|desaturate|deviation|dictionary|difference|digest|digits|div|do|download|drop|dup|e|else|empty|encode|ensure|env|epsilon|escape|execute|exit|exp|extend|extract|factors|false|fdiv|filter|first|flatten|floor|fold|from|function|gamma|gcd|get|goto|hash|help|hypot|if|in|inc|indent|index|infinity|info|input|insert|inspect|intersection|invert|join|keys|kurtosis|last|let|levenshtein|lighten|list|ln|log|loop|lower|mail|map|match|max|maybe|median|min|mod|module|mul|nand|neg|new|nor|normalize|not|now|null|open|or|outdent|pad|panic|path|pause|permissions|permutate|pi|pop|pow|powerset|powmod|prefix|print|prints|process|product|query|random|range|read|relative|remove|rename|render|repeat|replace|request|return|reverse|round|sample|saturate|script|sec|sech|select|serve|set|shl|shr|shuffle|sin|sinh|size|skewness|slice|sort|split|sqrt|squeeze|stack|strip|sub|suffix|sum|switch|symbols|symlink|sys|take|tan|tanh|terminal|to|true|truncate|try|type|union|unique|unless|until|unzip|upper|values|var|variance|volume|webview|while|with|wordwrap|write|xnor|xor|zip)\b"><token type="NameBuiltin"/></rule>
</state>
</rules>
</lexer>
+1 -1
View File
@@ -81,7 +81,7 @@
<rule pattern="[^\S\n]+">
<token type="Text"/>
</rule>
<rule pattern="//.*?\n">
<rule pattern="//[^\n]*\n?">
<token type="CommentSingle"/>
</rule>
<rule pattern="/\*.*?\*/">
@@ -0,0 +1,81 @@
<lexer>
<config>
<name>Gemfile.lock</name>
<alias>gemfile-lock</alias>
<alias>gemfilelock</alias>
<filename>Gemfile.lock</filename>
<filename>*.gemfile.lock</filename>
</config>
<rules>
<state name="root">
<rule pattern="^(GIT|PATH|GEM|PLUGIN SOURCE|PLATFORMS|DEPENDENCIES|BUNDLED WITH|RUBY VERSION|CHECKSUMS)$">
<token type="Keyword"/>
</rule>
<rule pattern="^([ \t]+)(remote|revision|ref|branch|tag|submodules|specs|glob)(:)">
<bygroups>
<token type="Text"/>
<token type="NameAttribute"/>
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="\(">
<token type="Punctuation"/>
<push state="version"/>
</rule>
<rule pattern="!">
<token type="Operator"/>
</rule>
<rule pattern="https?://\S+">
<token type="LiteralStringSymbol"/>
</rule>
<rule pattern="git@\S+">
<token type="LiteralStringSymbol"/>
</rule>
<rule pattern="sha\d+=[A-Fa-f0-9]+">
<token type="LiteralNumberHex"/>
</rule>
<rule pattern="\b[a-f0-9]{7,40}\b">
<token type="LiteralNumberHex"/>
</rule>
<rule pattern="\b\d[\w.]*">
<token type="LiteralNumber"/>
</rule>
<rule pattern="[A-Za-z_][A-Za-z0-9_.-]*">
<token type="Name"/>
</rule>
<rule pattern="\n">
<token type="Text"/>
</rule>
<rule pattern="[ \t]+">
<token type="Text"/>
</rule>
<rule pattern=".">
<token type="Text"/>
</rule>
</state>
<state name="version">
<rule pattern="\)">
<token type="Punctuation"/>
<pop depth="1"/>
</rule>
<rule pattern="(~&gt;|&gt;=|&lt;=|!=|=|&lt;|&gt;)">
<token type="Operator"/>
</rule>
<rule pattern="[0-9][\w.]*">
<token type="LiteralNumber"/>
</rule>
<rule pattern="[A-Za-z][\w.-]*">
<token type="Name"/>
</rule>
<rule pattern=",">
<token type="Punctuation"/>
</rule>
<rule pattern="\s+">
<token type="Text"/>
</rule>
<rule pattern=".">
<token type="Text"/>
</rule>
</state>
</rules>
</lexer>
+16 -3
View File
@@ -32,7 +32,7 @@
pattern="(assert|break|case|catch|continue|default|do|else|finally|for|if|goto|instanceof|new|return|switch|this|throw|try|while)\b">
<token type="Keyword" />
</rule>
<rule pattern="((?:(?:[^\W\d]|\$)[\w.\[\]$&lt;&gt;]*\s+)+?)((?:[^\W\d]|\$)[\w$]*)(\s*)(\()">
<rule pattern="((?:(?:[^\W\d]|\$)[\w.\[\]$&lt;&gt;?]*\s+)+?)((?:[^\W\d]|\$)[\w$]*)(\s*)(\()">
<bygroups>
<usingself state="root" />
<token type="NameFunction" />
@@ -44,7 +44,7 @@
<token type="NameDecorator" />
</rule>
<rule
pattern="(abstract|const|enum|extends|final|implements|native|private|protected|public|sealed|static|strictfp|super|synchronized|throws|transient|volatile|yield)\b">
pattern="(abstract|const|enum|exports|extends|final|implements|native|non-sealed|open|opens|permits|private|protected|provides|public|requires|sealed|static|strictfp|super|synchronized|throws|to|transient|transitive|uses|volatile|with|yield)\b">
<token type="KeywordDeclaration" />
</rule>
<rule pattern="(boolean|byte|char|double|float|int|long|short|void)\b">
@@ -64,6 +64,10 @@
<token type="KeywordDeclaration" />
<push state="class" />
</rule>
<rule pattern="(module)\b">
<token type="KeywordDeclaration" />
<push state="module" />
</rule>
<rule pattern="(var)(\s+)">
<bygroups>
<token type="KeywordDeclaration" />
@@ -71,7 +75,7 @@
</bygroups>
<push state="var" />
</rule>
<rule pattern="(import(?:\s+static)?)(\s+)">
<rule pattern="(import(?:\s+(?:static|module))?)(\s+)">
<bygroups>
<token type="KeywordNamespace" />
<token type="TextWhitespace" />
@@ -147,6 +151,15 @@
<pop depth="1" />
</rule>
</state>
<state name="module">
<rule pattern="\s+">
<token type="Text" />
</rule>
<rule pattern="([^\W\d]|\$)[\w$]*">
<token type="NameClass" />
<pop depth="1" />
</rule>
</state>
<state name="var">
<rule pattern="([^\W\d]|\$)[\w$]*">
<token type="Name" />
@@ -2,12 +2,15 @@
<config>
<name>JSON</name>
<alias>json</alias>
<alias>jsonl</alias>
<filename>*.json</filename>
<filename>*.jsonl</filename>
<filename>*.jsonc</filename>
<filename>*.json5</filename>
<filename>*.avsc</filename>
<filename>.luaurc</filename>
<mime_type>application/json</mime_type>
<mime_type>application/jsonl</mime_type>
<dot_all>true</dot_all>
<not_multiline>true</not_multiline>
</config>
File diff suppressed because one or more lines are too long
+6 -1
View File
@@ -3,7 +3,12 @@
<name>Nu</name>
<alias>nu</alias>
<filename>*.nu</filename>
<mime_type>application/x-shellscript</mime_type>
<mime_type>text/plain</mime_type>
<mime_type>text/x-shellscript</mime_type>
<analyse first="true" >
<regex pattern="(?m)^#!.*/bin/(?:env(?: -[a-zA-Z0-9]+)*(?: --[a-zA-Z0-9-=]+)* |)nu" score="1.0" />
</analyse>
</config>
<rules>
<state name="root">
@@ -118,4 +123,4 @@
<rule><include state="root" /></rule>
</state>
</rules>
</lexer>
</lexer>
@@ -5,6 +5,10 @@
<alias>postscr</alias>
<filename>*.ps</filename>
<filename>*.eps</filename>
<filename>*.epsf</filename>
<filename>*.epsi</filename>
<filename>*.pfa</filename>
<filename>*.t42</filename>
<mime_type>application/postscript</mime_type>
</config>
<rules>
@@ -86,4 +90,4 @@
</rule>
</state>
</rules>
</lexer>
</lexer>
@@ -7,7 +7,7 @@
</config>
<rules>
<state name="package">
<rule pattern="[a-zA-Z_]\w*">
<rule pattern="[a-zA-Z_][\w.]*">
<token type="NameNamespace"/>
<pop depth="1"/>
</rule>
@@ -16,7 +16,7 @@
</rule>
</state>
<state name="message">
<rule pattern="[a-zA-Z_]\w*">
<rule pattern="[a-zA-Z_][\w.]*">
<token type="NameClass"/>
<pop depth="1"/>
</rule>
@@ -34,7 +34,7 @@
</rule>
</state>
<state name="root">
<rule pattern="[ \t]+">
<rule pattern="\s+">
<token type="Text"/>
</rule>
<rule pattern="[,;{}\[\]()&lt;&gt;]">
@@ -46,9 +46,12 @@
<rule pattern="/(\\\n)?\*(.|\n)*?\*(\\\n)?/">
<token type="CommentMultiline"/>
</rule>
<rule pattern="\b(extensions|required|repeated|optional|returns|default|option|packed|import|ctype|oneof|max|rpc|to)\b">
<rule pattern="\b(ctype|default|edition|export|local|max|option|optional|packed|public|repeated|required|reserved|returns|stream|syntax|to|weak)\b">
<token type="Keyword"/>
</rule>
<rule pattern="\b(extensions|map)\b">
<token type="KeywordDeclaration"/>
</rule>
<rule pattern="(sfixed32|sfixed64|fixed32|fixed64|sint32|sint64|double|string|uint32|uint64|int32|float|int64|bytes|bool)\b">
<token type="KeywordType"/>
</rule>
@@ -62,6 +65,9 @@
</bygroups>
<push state="package"/>
</rule>
<rule pattern="import\b">
<token type="KeywordNamespace"/>
</rule>
<rule pattern="(message|extend)(\s+)">
<bygroups>
<token type="KeywordDeclaration"/>
@@ -69,7 +75,7 @@
</bygroups>
<push state="message"/>
</rule>
<rule pattern="(enum|group|service)(\s+)">
<rule pattern="(enum|group|oneof|rpc|service)(\s+)">
<bygroups>
<token type="KeywordDeclaration"/>
<token type="Text"/>
@@ -115,4 +121,4 @@
</rule>
</state>
</rules>
</lexer>
</lexer>
@@ -12,7 +12,11 @@
<filename>*.rbx</filename>
<filename>*.duby</filename>
<filename>Gemfile</filename>
<filename>*.gemfile</filename>
<filename>Vagrantfile</filename>
<filename>Appraisals</filename>
<filename>.pryrc</filename>
<filename>*.json.jbuilder</filename>
<mime_type>text/x-ruby</mime_type>
<mime_type>application/x-ruby</mime_type>
<dot_all>true</dot_all>
@@ -0,0 +1,315 @@
<lexer>
<config>
<name>Templ</name>
<alias>templ</alias>
<filename>*.templ</filename>
<mime_type>text/x-templ</mime_type>
<dot_all>true</dot_all>
<analyse>
<regex pattern="(?m)^\s*templ\s+[A-Za-z_]\w*\s*\(" score="0.7"/>
<regex pattern="(?m)^\s*package\s+\w+[\s\S]*^\s*templ\s+" score="0.5"/>
</analyse>
</config>
<rules>
<state name="root">
<rule pattern="//[^\n\r]*">
<token type="CommentSingle"/>
</rule>
<rule pattern="/\*(?:.|\n)*?\*/">
<token type="CommentMultiline"/>
</rule>
<rule pattern="(?m)^(\s*)(package|import|const|type|func)([^\n]*)">
<bygroups>
<token type="TextWhitespace"/>
<using lexer="Go"/>
<using lexer="Go"/>
</bygroups>
</rule>
<rule pattern="\b(templ|css|script)(\s+)([A-Za-z_]\w*)(\s*)(\([^{}]*\))(\s*)({)">
<bygroups>
<token type="KeywordDeclaration"/>
<token type="TextWhitespace"/>
<token type="NameFunction"/>
<token type="TextWhitespace"/>
<using lexer="Go"/>
<token type="TextWhitespace"/>
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="@[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*(?:\([^{}()\r\n]*(?:\([^{}()\r\n]*\)[^{}()\r\n]*)*\))?(?:[ \t]*{)?">
<token type="NameFunction"/>
</rule>
<rule pattern="(?m)^(\s*)(if|for|switch|select)(\s+)([^{}\n]*)(\s*)({)">
<bygroups>
<token type="TextWhitespace"/>
<token type="Keyword"/>
<token type="TextWhitespace"/>
<using lexer="Go"/>
<token type="TextWhitespace"/>
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="(?m)^(\s*)(else)(\s*)(if)?(\s*)([^{}\n]*)(\s*)({)?">
<bygroups>
<token type="TextWhitespace"/>
<token type="Keyword"/>
<token type="TextWhitespace"/>
<token type="Keyword"/>
<token type="TextWhitespace"/>
<using lexer="Go"/>
<token type="TextWhitespace"/>
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="(})(\s*)(else)(\s*)(if)?(\s*)([^{}\n]*)(\s*)({)?">
<bygroups>
<token type="Punctuation"/>
<token type="TextWhitespace"/>
<token type="Keyword"/>
<token type="TextWhitespace"/>
<token type="Keyword"/>
<token type="TextWhitespace"/>
<using lexer="Go"/>
<token type="TextWhitespace"/>
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="({)([^{}\n]*)(})">
<bygroups>
<token type="Punctuation"/>
<using lexer="Go"/>
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="}">
<token type="Punctuation"/>
</rule>
<rule pattern="`(?:.|\n)*?`">
<token type="LiteralStringBacktick"/>
</rule>
<rule pattern="\s+">
<token type="TextWhitespace"/>
</rule>
<rule pattern="[^&lt;&amp;@{}`\s]+">
<token type="Text"/>
</rule>
<rule pattern="&amp;\S*?;">
<token type="NameEntity"/>
</rule>
<rule pattern="\&lt;\!\[CDATA\[.*?\]\]\&gt;">
<token type="CommentPreproc"/>
</rule>
<rule pattern="&lt;!--">
<token type="Comment"/>
<push state="comment"/>
</rule>
<rule pattern="&lt;\?.*?\?&gt;">
<token type="CommentPreproc"/>
</rule>
<rule pattern="&lt;![^&gt;]*&gt;">
<token type="CommentPreproc"/>
</rule>
<rule pattern="(&lt;)(script)(\s*)">
<bygroups>
<token type="Punctuation"/>
<token type="NameTag"/>
<token type="Text"/>
</bygroups>
<push state="script-content" state="tag"/>
</rule>
<rule pattern="(&lt;)(style)(\s*)">
<bygroups>
<token type="Punctuation"/>
<token type="NameTag"/>
<token type="Text"/>
</bygroups>
<push state="style-content" state="tag"/>
</rule>
<rule pattern="(&lt;)([A-Za-z][\w:.-]*)">
<bygroups>
<token type="Punctuation"/>
<token type="NameTag"/>
</bygroups>
<push state="tag"/>
</rule>
<rule pattern="(&lt;/)([A-Za-z][\w:.-]*)(\s*)(&gt;)">
<bygroups>
<token type="Punctuation"/>
<token type="NameTag"/>
<token type="Text"/>
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="[@{}&lt;&amp;]">
<token type="Punctuation"/>
</rule>
</state>
<state name="script-content">
<rule pattern="(&lt;)(\s*)(/)(\s*)(script)(\s*)(&gt;)">
<bygroups>
<token type="Punctuation"/>
<token type="Text"/>
<token type="Punctuation"/>
<token type="Text"/>
<token type="NameTag"/>
<token type="Text"/>
<token type="Punctuation"/>
</bygroups>
<pop depth="1"/>
</rule>
<rule pattern="\s*[^\r\n]*\{\{.*?\}\}[^\r\n]*">
<token type="Other"/>
</rule>
<rule pattern=".+?(?=&lt;\s*/\s*script\s*&gt;)">
<token type="Other"/>
</rule>
</state>
<state name="style-content">
<rule pattern="(&lt;)(\s*)(/)(\s*)(style)(\s*)(&gt;)">
<bygroups>
<token type="Punctuation"/>
<token type="Text"/>
<token type="Punctuation"/>
<token type="Text"/>
<token type="NameTag"/>
<token type="Text"/>
<token type="Punctuation"/>
</bygroups>
<pop depth="1"/>
</rule>
<rule pattern=".+?(?=&lt;\s*/\s*style\s*&gt;)">
<using lexer="CSS"/>
</rule>
</state>
<state name="comment">
<rule pattern="[^-]+">
<token type="Comment"/>
</rule>
<rule pattern="--&gt;">
<token type="Comment"/>
<pop depth="1"/>
</rule>
<rule pattern="-">
<token type="Comment"/>
</rule>
</state>
<state name="tag">
<rule pattern="\s+">
<token type="Text"/>
</rule>
<rule pattern="//[^\n\r]*">
<token type="CommentSingle"/>
</rule>
<rule pattern="/\*(?:.|\n)*?\*/">
<token type="CommentMultiline"/>
</rule>
<rule pattern="\b(if|for|switch|select)(\s+)([^{}]*)(\s*)({)">
<bygroups>
<token type="Keyword"/>
<token type="Text"/>
<using lexer="Go"/>
<token type="Text"/>
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="\b(else)(\s*)(if)?(\s*)([^{}]*)(\s*)({)?">
<bygroups>
<token type="Keyword"/>
<token type="Text"/>
<token type="Keyword"/>
<token type="Text"/>
<using lexer="Go"/>
<token type="Text"/>
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="}">
<token type="Punctuation"/>
</rule>
<rule pattern="({)([^{}]*)(})(\??)(\s*)(=)(\s*)({)([^{}]*)(})">
<bygroups>
<token type="Punctuation"/>
<using lexer="Go"/>
<token type="Punctuation"/>
<token type="Operator"/>
<token type="Text"/>
<token type="Operator"/>
<token type="Text"/>
<token type="Punctuation"/>
<using lexer="Go"/>
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="({)([^{}]*)(})(\??)(\s*)(=)(\s*)">
<bygroups>
<token type="Punctuation"/>
<using lexer="Go"/>
<token type="Punctuation"/>
<token type="Operator"/>
<token type="Text"/>
<token type="Operator"/>
<token type="Text"/>
</bygroups>
<push state="attr"/>
</rule>
<rule pattern="({)([^{}]*)(})(\??)">
<bygroups>
<token type="Punctuation"/>
<using lexer="Go"/>
<token type="Punctuation"/>
<token type="Operator"/>
</bygroups>
</rule>
<rule pattern="({)([^{}]*)(})">
<bygroups>
<token type="Punctuation"/>
<using lexer="Go"/>
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="((?:[@#.][\w:.*-]+|\[[^\]\s=]+\]|[\w:.*-]+\??)\s*)(=)(\s*)({)([^{}]*)(})">
<bygroups>
<token type="NameAttribute"/>
<token type="Operator"/>
<token type="Text"/>
<token type="Punctuation"/>
<using lexer="Go"/>
<token type="Punctuation"/>
</bygroups>
</rule>
<rule pattern="((?:[@#.][\w:.*-]+|\[[^\]\s=]+\]|[\w:.*-]+\??)\s*)(=)(\s*)">
<bygroups>
<token type="NameAttribute"/>
<token type="Operator"/>
<token type="Text"/>
</bygroups>
<push state="attr"/>
</rule>
<rule pattern="(?:[@#.][\w:.*-]+|\[[^\]\s=]+\]|[\w:.*-]+\??)">
<token type="NameAttribute"/>
</rule>
<rule pattern="(/?)(\s*)(&gt;)">
<bygroups>
<token type="Punctuation"/>
<token type="Text"/>
<token type="Punctuation"/>
</bygroups>
<pop depth="1"/>
</rule>
</state>
<state name="attr">
<rule pattern="&#34;.*?&#34;">
<token type="LiteralString"/>
<pop depth="1"/>
</rule>
<rule pattern="&#39;.*?&#39;">
<token type="LiteralString"/>
<pop depth="1"/>
</rule>
<rule pattern="[^\s&gt;]+">
<token type="LiteralString"/>
<pop depth="1"/>
</rule>
</state>
</rules>
</lexer>
+14 -4
View File
@@ -23,6 +23,9 @@
<rule pattern="#.*$">
<token type="Comment"/>
</rule>
<rule pattern="//.*$">
<token type="Comment"/>
</rule>
<rule pattern="!![^\s]+">
<token type="CommentPreproc"/>
</rule>
@@ -78,15 +81,22 @@
<token type="Comment"/>
</bygroups>
</rule>
<rule pattern="([^\{\}\[\]\?,\:\!\-\*&amp;\@].*)( )+(//.*)">
<bygroups>
<token type="Literal"/>
<token type="TextWhitespace"/>
<token type="Comment"/>
</bygroups>
</rule>
<rule pattern="[^\{\}\[\]\?,\:\!\-\*&amp;\@].*">
<token type="Literal"/>
</rule>
</state>
<state name="key">
<rule pattern="&#34;[^&#34;\n].*&#34;: ">
<rule pattern="&#34;[^&#34;\n#].*&#34;: ">
<token type="NameTag"/>
</rule>
<rule pattern="(-)( )([^&#34;\n{]*)(:)( )">
<rule pattern="(-)( )((?:(?!//)[^&#34;\n{#])*?)(:)( )">
<bygroups>
<token type="Punctuation"/>
<token type="TextWhitespace"/>
@@ -95,14 +105,14 @@
<token type="TextWhitespace"/>
</bygroups>
</rule>
<rule pattern="([^&#34;\n{]*)(:)( )">
<rule pattern="((?:(?!//)[^&#34;\n{#])*?)(:)( )">
<bygroups>
<token type="NameTag"/>
<token type="Punctuation"/>
<token type="TextWhitespace"/>
</bygroups>
</rule>
<rule pattern="([^&#34;\n{]*)(:)(\n)">
<rule pattern="((?:(?!//)[^&#34;\n{#])*?)(:)(\n)">
<bygroups>
<token type="NameTag"/>
<token type="Punctuation"/>
+2 -2
View File
@@ -30,8 +30,8 @@ func goRules() Rules {
"root": {
{`\n`, TextWhitespace, nil},
{`\s+`, TextWhitespace, nil},
{`//[^\s][^\n\r]*`, CommentPreproc, nil},
{`//\s+[^\n\r]*`, CommentSingle, nil},
{`//[^\s\n\r][^\n\r]*`, CommentPreproc, nil},
{`//[^\n\r]*`, CommentSingle, nil},
{`/(\\\n)?[*](.|\n)*?[*](\\\n)?/`, CommentMultiline, nil},
{`(import|package)\b`, KeywordNamespace, nil},
{`(var|func|struct|map|chan|type|interface|const)\b`, KeywordDeclaration, nil},
+1 -1
View File
@@ -122,7 +122,7 @@ func (d *httpBodyContentTyper) Tokenise(options *TokeniseOptions, text string) (
if err != nil {
panic(err)
}
return EOF
return subIterator()
}
}
}
+64 -3
View File
@@ -1,11 +1,13 @@
package lexers
import (
"strings"
. "github.com/alecthomas/chroma/v2" // nolint
)
// Markdown lexer.
var Markdown = Register(MustNewLexer(
// Markdown lexer with YAML frontmatter and HTML comment support.
var Markdown = Register(&markdownLexer{Lexer: MustNewLexer(
&Config{
Name: "markdown",
Aliases: []string{"md", "mkd"},
@@ -13,11 +15,69 @@ var Markdown = Register(MustNewLexer(
MimeTypes: []string{"text/x-markdown"},
},
markdownRules,
))
)})
// markdownLexer wraps the base Markdown lexer to highlight top-of-file YAML frontmatter.
type markdownLexer struct {
Lexer
}
// Lexes Markdown, highlighting a leading YAML frontmatter block before delegating to Markdown rules.
func (m *markdownLexer) Tokenise(options *TokeniseOptions, text string) (Iterator, error) {
frontmatter, rest, ok := splitFrontmatter(text)
if !ok {
return m.Lexer.Tokenise(options, text)
}
yamlLexer := Get("YAML")
if yamlLexer == nil {
return m.Lexer.Tokenise(options, text)
}
yamlTokens, err := yamlLexer.Tokenise(options, frontmatter)
if err != nil {
return nil, err
}
markdownTokens, err := m.Lexer.Tokenise(options, rest)
if err != nil {
return nil, err
}
return Concaterator(yamlTokens, markdownTokens), nil
}
// Extracts a leading YAML frontmatter block if the document starts with one.
func splitFrontmatter(text string) (frontmatter string, rest string, ok bool) {
if !strings.HasPrefix(text, "---\n") && !strings.HasPrefix(text, "---\r\n") {
return "", text, false
}
lineEnd := strings.IndexByte(text, '\n')
if lineEnd < 0 {
return "", text, false
}
if strings.TrimSuffix(text[:lineEnd], "\r") != "---" {
return "", text, false
}
for pos := lineEnd + 1; pos < len(text); {
next := strings.IndexByte(text[pos:], '\n')
if next < 0 {
break
}
lineEnd = pos + next
line := strings.TrimSuffix(text[pos:lineEnd], "\r")
if line == "---" {
return text[:lineEnd+1], text[lineEnd+1:], true
}
pos = lineEnd + 1
}
return "", text, false
}
func markdownRules() Rules {
return Rules{
"root": {
{`<!--[\w\W]*?-->`, CommentMultiline, nil},
{`^(#[^#].+\n)`, ByGroups(GenericHeading), nil},
{`^(#{2,6}.+\n)`, ByGroups(GenericSubheading), nil},
{`^(\s*)([*-] )(\[[ xX]\])( .+\n)`, ByGroups(Text, Keyword, Keyword, UsingSelf("inline")), nil},
@@ -33,6 +93,7 @@ func markdownRules() Rules {
Include("inline"),
},
"inline": {
{`<!--[\w\W]*?-->`, CommentMultiline, nil},
{`\\.`, Text, nil},
{`(\s)(\*|_)((?:(?!\2).)*)(\2)((?=\W|\n))`, ByGroups(Text, GenericEmph, GenericEmph, GenericEmph, Text), nil},
{`(\s)((\*\*|__).*?)\3((?=\W|\n))`, ByGroups(Text, GenericStrong, GenericStrong, Text), nil},
+2 -2
View File
@@ -70,14 +70,14 @@ func marklessRules() Rules {
{`(! )([^ ]+)(.+?)$`, ByGroups(Keyword, NameFunction, NameVariable), nil},
},
"embed": {
{`(\[ )([^ ]+)( )([^,]+)`, ByGroups(Keyword, NameFunction, TextWhitespace, String), Push("embed-options")},
{`(\[ )([^ ]+)( )([^,\]\n]+)`, ByGroups(Keyword, NameFunction, TextWhitespace, String), Push("embed-options")},
},
"embed-options": {
{`\\.`, Text, nil},
{`,`, Punctuation, nil},
{`\]?$`, Keyword, Pop(1)},
// Generic key or key/value pair
{`( *)([^, \]]+)([^,\]]+)?`, ByGroups(TextWhitespace, NameFunction, String), nil},
{`( *)([^, \]\n]+)([^,\]\n]+)?`, ByGroups(TextWhitespace, NameFunction, String), nil},
{`.`, Text, nil},
},
"footnote": {
+4 -6
View File
@@ -6,7 +6,7 @@ import (
"strings"
"unicode/utf8"
"github.com/dlclark/regexp2"
"github.com/dlclark/regexp2/v2"
. "github.com/alecthomas/chroma/v2" // nolint
)
@@ -458,8 +458,7 @@ func rakuRules() Rules {
var podRegex *regexp2.Regexp
if tokenClass == rakuPod {
podRegex = regexp2.MustCompile(
state.NamedGroups[`ws`]+`=end`+`\s+`+regexp2.Escape(state.NamedGroups[`name`]),
0,
state.NamedGroups[`ws`] + `=end` + `\s+` + regexp2.Escape(state.NamedGroups[`name`]),
)
} else {
closingChars = []rune(strings.Repeat(string(closingChar), nChars))
@@ -478,7 +477,7 @@ func rakuRules() Rules {
match, err := podRegex.FindRunesMatchStartingAt(text, searchPos+nChars)
if err == nil {
closingChars = match.Runes()
nextClosePos = match.Index
nextClosePos = match.RuneIndex
} else {
nextClosePos = -1
}
@@ -1599,8 +1598,7 @@ func quote(groups []string, state *LexerState) Iterator {
var tokenStates []string
// Set tokenStates based on adverbs
adverbs := strings.Split(adverbsStr, ":")
for _, adverb := range adverbs {
for adverb := range strings.SplitSeq(adverbsStr, ":") {
switch adverb {
case "c", "closure":
tokenStates = append(tokenStates, "Q-closure")
+20
View File
@@ -0,0 +1,20 @@
package lexers
import (
. "github.com/alecthomas/chroma/v2" // nolint
)
// YAML+Jinja is YAML with Jinja templating embedded. Used by Ansible playbooks
// and Salt SLS files.
var YAMLJinja = Register(DelegatingLexer(
MustNewXMLLexer(embedded, "embedded/yaml.xml"),
MustNewXMLLexer(embedded, "embedded/django_jinja.xml").SetConfig(
&Config{
Name: "YAML+Jinja",
Aliases: []string{"yaml+jinja", "salt", "sls", "ansible"},
Filenames: []string{"*.sls"},
MimeTypes: []string{"text/x-yaml+jinja", "text/x-sls"},
DotAll: true,
},
),
))
+9 -10
View File
@@ -3,6 +3,7 @@ package chroma
import (
"encoding/json"
"fmt"
"maps"
"os"
"path/filepath"
"regexp"
@@ -12,7 +13,7 @@ import (
"time"
"unicode/utf8"
"github.com/dlclark/regexp2"
"github.com/dlclark/regexp2/v2"
)
// A Rule is the fundamental matching unit of the Regex lexer state machine.
@@ -70,9 +71,7 @@ func (r Rules) Clone() Rules {
// Merge creates a clone of "r" then merges "rules" into the clone.
func (r Rules) Merge(rules Rules) Rules {
out := r.Clone()
for k, v := range rules.Clone() {
out[k] = v
}
maps.Copy(out, rules.Clone())
return out
}
@@ -177,19 +176,19 @@ type LexerState struct {
// Named Group matches.
NamedGroups map[string]string
// Custum context for mutators.
MutatorContext map[interface{}]interface{}
MutatorContext map[any]any
iteratorStack []Iterator
options *TokeniseOptions
newlineAdded bool
}
// Set mutator context.
func (l *LexerState) Set(key interface{}, value interface{}) {
func (l *LexerState) Set(key any, value any) {
l.MutatorContext[key] = value
}
// Get mutator context.
func (l *LexerState) Get(key interface{}) interface{} {
func (l *LexerState) Get(key any) any {
return l.MutatorContext[key]
}
@@ -369,7 +368,7 @@ func (r *RegexLexer) maybeCompile() (err error) {
pattern = "(?" + rule.flags + ")" + pattern
}
pattern = `\G` + pattern
rule.Regexp, err = regexp2.Compile(pattern, 0)
rule.Regexp, err = regexp2.Compile(pattern)
if err != nil {
return fmt.Errorf("failed to compile rule %s.%d: %s", state, i, err)
}
@@ -484,7 +483,7 @@ func (r *RegexLexer) Tokenise(options *TokeniseOptions, text string) (Iterator,
Text: []rune(text),
Stack: []string{options.State},
Rules: r.rules,
MutatorContext: map[interface{}]interface{}{},
MutatorContext: map[any]any{},
}
return state.Iterator, nil
}
@@ -501,7 +500,7 @@ func (r *RegexLexer) MustRules() Rules {
func matchRules(text []rune, pos int, rules []*CompiledRule) (int, *CompiledRule, []string, map[string]string) {
for i, rule := range rules {
match, err := rule.Regexp.FindRunesMatchStartingAt(text, pos)
if match != nil && err == nil && match.Index == pos {
if match != nil && err == nil && match.RuneIndex == pos {
groups := []string{}
namedGroups := make(map[string]string)
for _, g := range match.Groups() {
+1
View File
@@ -9,6 +9,7 @@
"schedule:earlyMondays", // Run once a week.
'helpers:pinGitHubActionDigests',
],
"postUpdateOptions": ["gomodTidy"],
"packageRules": [
{
"matchPackageNames": ["golangci-lint"],
+4 -4
View File
@@ -13,7 +13,7 @@ import (
"regexp"
"strings"
"github.com/dlclark/regexp2"
"github.com/dlclark/regexp2/v2"
)
// Serialisation of Chroma rules to XML. The format is:
@@ -440,14 +440,14 @@ func (t TokenType) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
}
// This hijinks is a bit unfortunate but without it we can't deserialise into TokenType.
func newFromTemplate(template interface{}) (value func() interface{}, target interface{}) {
func newFromTemplate(template any) (value func() any, target any) {
t := reflect.TypeOf(template)
if t.Kind() == reflect.Ptr {
if t.Kind() == reflect.Pointer {
v := reflect.New(t.Elem())
return v.Interface, v.Interface()
}
v := reflect.New(t)
return func() interface{} { return v.Elem().Interface() }, v.Interface()
return func() any { return v.Elem().Interface() }, v.Interface()
}
func (b *Emitters) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+72 -23
View File
@@ -4,7 +4,8 @@ import (
"encoding/xml"
"fmt"
"io"
"sort"
"maps"
"slices"
"strings"
)
@@ -18,6 +19,24 @@ const (
No
)
// Mode indicates whether a style is intended for a light or dark background.
type Mode uint8
// Mode values.
const (
Light Mode = iota
Dark
)
func (m Mode) String() string {
switch m {
case Dark:
return "dark"
default:
return "light"
}
}
func (t Trilean) String() string {
switch t {
case Yes:
@@ -31,12 +50,14 @@ func (t Trilean) String() string {
// Prefix returns s with "no" as a prefix if Trilean is no.
func (t Trilean) Prefix(s string) string {
if t == Yes {
switch t {
case Yes:
return s
} else if t == No {
case No:
return "no" + s
default:
return ""
}
return ""
}
// A StyleEntry in the Style map.
@@ -111,11 +132,10 @@ func (s StyleEntry) Sub(e StyleEntry) StyleEntry {
// Ancestors should be provided from oldest to newest.
func (s StyleEntry) Inherit(ancestors ...StyleEntry) StyleEntry {
out := s
for i := len(ancestors) - 1; i >= 0; i-- {
for _, ancestor := range slices.Backward(ancestors) {
if out.NoInherit {
return out
}
ancestor := ancestors[i]
if !out.Colour.IsSet() {
out.Colour = ancestor.Colour
}
@@ -147,19 +167,24 @@ func (s StyleEntry) IsZero() bool {
//
// Once built, a Style is immutable.
type StyleBuilder struct {
entries map[TokenType]string
name string
parent *Style
entries map[TokenType]string
name string
counterpart string
parent *Style
}
func NewStyleBuilder(name string) *StyleBuilder {
return &StyleBuilder{name: name, entries: map[TokenType]string{}}
}
// Counterpart sets the lowercase name of the opposite-mode style.
func (s *StyleBuilder) Counterpart(name string) *StyleBuilder {
s.counterpart = strings.ToLower(name)
return s
}
func (s *StyleBuilder) AddAll(entries StyleEntries) *StyleBuilder {
for ttype, entry := range entries {
s.entries[ttype] = entry
}
maps.Copy(s.entries, entries)
return s
}
@@ -205,10 +230,15 @@ func (s *StyleBuilder) Transform(transform func(StyleEntry) StyleEntry) *StyleBu
}
func (s *StyleBuilder) Build() (*Style, error) {
counterpart := s.counterpart
if counterpart == "" && s.parent != nil {
counterpart = s.parent.Counterpart
}
style := &Style{
Name: s.name,
entries: map[TokenType]StyleEntry{},
parent: s.parent,
Name: s.name,
Counterpart: counterpart,
entries: map[TokenType]StyleEntry{},
parent: s.parent,
}
for ttype, descriptor := range s.entries {
entry, err := ParseStyleEntry(descriptor)
@@ -257,9 +287,23 @@ func MustNewStyle(name string, entries StyleEntries) *Style {
//
// See http://pygments.org/docs/styles/ for details. Semantics are intended to be identical.
type Style struct {
Name string
entries map[TokenType]StyleEntry
parent *Style
Name string
// Counterpart is the lowercase name of the style intended as this style's
// opposite-mode pair (eg. "github-dark" for "github"). Resolved via
// styles.GetForMode. May be empty.
Counterpart string
entries map[TokenType]StyleEntry
parent *Style
}
// Mode returns Light or Dark based on the brightness of the Background entry's
// background colour. Styles with an unset Background default to Light.
func (s *Style) Mode() Mode {
bg := s.get(Background).Background
if bg.IsSet() && bg.Brightness() < 0.5 {
return Dark
}
return Light
}
func (s *Style) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
@@ -268,6 +312,9 @@ func (s *Style) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
}
start.Name = xml.Name{Local: "style"}
start.Attr = []xml.Attr{{Name: xml.Name{Local: "name"}, Value: s.Name}}
if s.Counterpart != "" {
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "counterpart"}, Value: s.Counterpart})
}
if err := e.EncodeToken(start); err != nil {
return err
}
@@ -275,7 +322,7 @@ func (s *Style) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
for ttype := range s.entries {
sorted = append(sorted, ttype)
}
sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] })
slices.Sort(sorted)
for _, ttype := range sorted {
entry := s.entries[ttype]
el := xml.StartElement{Name: xml.Name{Local: "entry"}}
@@ -295,9 +342,12 @@ func (s *Style) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
func (s *Style) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
for _, attr := range start.Attr {
if attr.Name.Local == "name" {
switch attr.Name.Local {
case "name":
s.Name = attr.Value
} else {
case "counterpart":
s.Counterpart = strings.ToLower(attr.Value)
default:
return fmt.Errorf("unexpected attribute %s", attr.Name.Local)
}
}
@@ -437,8 +487,7 @@ func MustParseStyleEntry(entry string) StyleEntry {
// ParseStyleEntry parses a Pygments style entry.
func ParseStyleEntry(entry string) (StyleEntry, error) { // nolint: gocyclo
out := StyleEntry{}
parts := strings.Fields(entry)
for _, part := range parts {
for part := range strings.FieldsSeq(entry) {
switch {
case part == "italic":
out.Italic = Yes
+29
View File
@@ -64,3 +64,32 @@ func Get(name string) *chroma.Style {
}
return Fallback
}
// GetForMode returns the named style if it already matches mode, otherwise its
// registered counterpart if one exists and matches mode. If neither matches,
// the originally-requested style is returned (or Fallback if the name is
// unknown), so callers always get something usable.
func GetForMode(name string, mode chroma.Mode) *chroma.Style {
style := Get(name)
if style.Mode() == mode {
return style
}
if style.Counterpart == "" {
return style
}
counterpart, ok := Registry[style.Counterpart]
if !ok || counterpart.Mode() != mode {
return style
}
return counterpart
}
// RegisterPair links two styles as light/dark counterparts of each other.
//
// Both styles are also registered if they are not already present.
func RegisterPair(a, b *chroma.Style) {
Register(a)
Register(b)
a.Counterpart = strings.ToLower(b.Name)
b.Counterpart = strings.ToLower(a.Name)
}
@@ -1,4 +1,4 @@
<style name="catppuccin-latte">
<style name="catppuccin-latte" counterpart="catppuccin-mocha">
<entry type="Background" style="bg:#eff1f5 #4c4f69"/>
<entry type="CodeLine" style="#4c4f69"/>
<entry type="Error" style="#d20f39"/>
@@ -1,4 +1,4 @@
<style name="catppuccin-mocha">
<style name="catppuccin-mocha" counterpart="catppuccin-latte">
<entry type="Background" style="bg:#1e1e2e #cdd6f4"/>
<entry type="CodeLine" style="#cdd6f4"/>
<entry type="Error" style="#f38ba8"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="github-dark">
<style name="github-dark" counterpart="github">
<entry type="Error" style="#f85149"/>
<entry type="LineHighlight" style="bg:#6e7681"/>
<entry type="LineNumbers" style="#6e7681"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="github">
<style name="github" counterpart="github-dark">
<entry type="Error" style="#f6f8fa bg:#82071e"/>
<entry type="Background" style="bg:#f7f7f7"/>
<entry type="Keyword" style="#cf222e"/>
+2 -1
View File
@@ -1,4 +1,4 @@
<style name="gruvbox-light">
<style name="gruvbox-light" counterpart="gruvbox">
<entry type="Background" style="noinherit #3c3836 bg:#fbf1c7"/>
<entry type="Keyword" style="noinherit #af3a03"/>
<entry type="KeywordType" style="noinherit #b57614"/>
@@ -10,6 +10,7 @@
<entry type="NameException" style="noinherit #fb4934"/>
<entry type="NameFunction" style="#b57614"/>
<entry type="NameLabel" style="noinherit #9d0006"/>
<entry type="NameNamespace" style="noinherit #79740e"/>
<entry type="NameTag" style="noinherit #9d0006"/>
<entry type="NameVariable" style="noinherit #3c3836"/>
<entry type="LiteralString" style="noinherit #79740e"/>
+2 -1
View File
@@ -1,4 +1,4 @@
<style name="gruvbox">
<style name="gruvbox" counterpart="gruvbox-light">
<entry type="Background" style="noinherit #ebdbb2 bg:#282828"/>
<entry type="Keyword" style="noinherit #fe8019"/>
<entry type="KeywordType" style="noinherit #fabd2f"/>
@@ -11,6 +11,7 @@
<entry type="NameFunction" style="#fabd2f"/>
<entry type="NameLabel" style="noinherit #fb4934"/>
<entry type="NameTag" style="noinherit #fb4934"/>
<entry type="NameNamespace" style="noinherit #b8bb26"/>
<entry type="NameVariable" style="noinherit #ebdbb2"/>
<entry type="LiteralString" style="noinherit #b8bb26"/>
<entry type="LiteralStringSymbol" style="#83a598"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="kanagawa-lotus">
<style name="kanagawa-lotus" counterpart="kanagawa-wave">
<entry type="Background" style="bg:#f2ecbc #545464" />
<entry type="CodeLine" style="#545464" />
<entry type="Error" style="#e82424" />
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="kanagawa-wave">
<style name="kanagawa-wave" counterpart="kanagawa-lotus">
<entry type="Background" style="bg:#1f1f28 #dcd7ba" />
<entry type="CodeLine" style="#dcd7ba" />
<entry type="Error" style="#e82424" />
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="modus-operandi">
<style name="modus-operandi" counterpart="modus-vivendi">
<entry type="Background" style="#000000 bg:#ffffff"/>
<entry type="Keyword" style="#5317ac"/>
<entry type="KeywordConstant" style="#0000c0"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="modus-vivendi">
<style name="modus-vivendi" counterpart="modus-operandi">
<entry type="Background" style="#ffffff bg:#000000"/>
<entry type="Keyword" style="#b6a0ff"/>
<entry type="KeywordConstant" style="#00bcff"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="monokai">
<style name="monokai" counterpart="monokailight">
<entry type="Error" style="#960050 bg:#1e0010"/>
<entry type="Background" style="bg:#272822"/>
<entry type="Keyword" style="#66d9ef"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="monokailight">
<style name="monokailight" counterpart="monokai">
<entry type="Error" style="#960050 bg:#1e0010"/>
<entry type="Background" style="bg:#fafafa"/>
<entry type="Keyword" style="#00a8c8"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="paraiso-dark">
<style name="paraiso-dark" counterpart="paraiso-light">
<entry type="Error" style="#ef6155"/>
<entry type="Background" style="bg:#2f1e2e"/>
<entry type="Keyword" style="#815ba4"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="paraiso-light">
<style name="paraiso-light" counterpart="paraiso-dark">
<entry type="Error" style="#ef6155"/>
<entry type="Background" style="bg:#e7e9db"/>
<entry type="Keyword" style="#815ba4"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="rose-pine-dawn">
<style name="rose-pine-dawn" counterpart="rose-pine">
<entry type="Error" style="#b4637a"/>
<entry type="Background" style="bg:#faf4ed"/>
<entry type="Keyword" style="#286983"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="rose-pine">
<style name="rose-pine" counterpart="rose-pine-dawn">
<entry type="Error" style="#eb6f92"/>
<entry type="Background" style="bg:#191724"/>
<entry type="Keyword" style="#31748f"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="solarized-dark">
<style name="solarized-dark" counterpart="solarized-light">
<entry type="Other" style="#cb4b16"/>
<entry type="Background" style="#93a1a1 bg:#002b36"/>
<entry type="Keyword" style="#719e07"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="solarized-light">
<style name="solarized-light" counterpart="solarized-dark">
<entry type="Background" style="bg:#fdf6e3"/>
<entry type="Keyword" style="#859900"/>
<entry type="KeywordConstant" style="bold"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="tokyonight-day">
<style name="tokyonight-day" counterpart="tokyonight-night">
<entry type="Background" style="bg:#e1e2e7 #3760bf"/>
<entry type="CodeLine" style="#3760bf"/>
<entry type="Error" style="#c64343"/>
@@ -1,4 +1,4 @@
<style name="tokyonight-night">
<style name="tokyonight-night" counterpart="tokyonight-day">
<entry type="Background" style="bg:#1a1b26 #c0caf5"/>
<entry type="CodeLine" style="#c0caf5"/>
<entry type="Error" style="#db4b4b"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="xcode-dark">
<style name="xcode-dark" counterpart="xcode">
<entry type="Error" style="#960050"/>
<entry type="Background" style="#ffffff bg:#1f1f24"/>
<entry type="Keyword" style="#fc5fa3"/>
+1 -1
View File
@@ -1,4 +1,4 @@
<style name="xcode">
<style name="xcode" counterpart="xcode-dark">
<entry type="Error" style="#000000"/>
<entry type="Background" style="bg:#ffffff"/>
<entry type="Keyword" style="#a90d91"/>
+2
View File
@@ -0,0 +1,2 @@
{"name": "Alice"}
{"name": "Bob"}