%<*driver>
\def\nameofplainTeX{plain}
\ifx\fmtname\nameofplainTeX
\input l3docstrip.tex
\keepsilent
\askforoverwritefalse
\preamble

========================================================================
 osglistings - Code-listing front end for osglecture, built on minted.

 (C) 2026 Matthias Werner
 E-mail: matthias.werner@informatik.tu-chemnitz.de

 Released under the LaTeX Project Public License v1.3c or later
 See http://www.latex-project.org/lppl.txt
========================================================================

\endpreamble
\postamble

 This work is "maintained" (as per LPPL maintenance status) by
 Matthias Werner.
\endpostamble
\generate{\file{osglistings.sty}{\from{osglistings.dtx}{package}}}
\expandafter\endbatchfile
\fi

\iffalse
%</driver>
%<*package>
\NeedsTeXFormat{LaTeX2e}[2022-06-01]
\ProvidesExplPackage{osglistings}{2026-08-28}{v0.1.0}
  {Code-listing front end for osglecture, built on minted}
\ExplSyntaxOff
%</package>
%<*driver>
\fi

\documentclass[add-bib=false]{osgdoc}

\OnlyDescription

\usepackage{enumitem}
\usepackage{microtype}
\usepackage[
languages={de,en},
map={de=german,en=english},
targetlang={job=2},
load babel
]{langselect}
\newpackagename{\osglistings}{osglistings}
\makeindex
\setcnltx{
  package  =  osglistings,
  version  = \UseVersionOf{osglistings.sty},
  date     = \UseDateOf{osglistings.sty},
  title    = {\ldeen{Das \osglistings\ Paket}{The \osglistings\ Package}},
  info     = {\ldeen{Ein auf \pkg*{minted} aufbauendes Frontend für Code-Listings}{
      A \pkg*{minted}-based front end for code listings}},
  authors  = {Matthias Werner[matthias.werner@informatik.tu-chemnitz.de]},
  abstract={
    \ldeen*{
      Das Paket \osglistings\ stellt ein Frontend für \pkg*{minted} 
      bereit, welches Farben und Rahmenstile leicht konfigurierbar macht und
      Markierungen, externe Editoren und Codequellen (gists) unterstützt.
      
      Außerdem wird der Quelltext als @2 getaggt, falls @1 gesetzt ist.

      Das Paket ist Teil des \bnd*{osglecture}-Bundles, aber von den anderen
      Paketen weitgehend unabhängig.
    }{
            The \osglistings\ package provides a frontend for \pkg*{minted}
      that makes colors and border styles easily configurable and
      supports highlights, external editors, and code sources (gists).

      In addition, the source code is tagged as @2 if @1 is set.

      The package is part of the \bnd*{osglecture} bundle, but is largely
      independent of the other packages.
    }{\cs{DocumentMetadata}\Marg{tagging=on}}{\code{Code}}
  },
  url      =https://github.com/werner-matthias/osglecture,
  build-title
}
\begin{document}
% --------------------------------------------------------------------------
% Documentation
% --------------------------------------------------------------------------
\section{\ldeen{Nutzung}{Usage}}
\ldeen*{Das Paket wird auf dem üblichen Weg mit @1 geladen und lädt seinerseits (u.a.) @2.}{
The package is loaded in the usual way using @1 and, in turn, loads (among other things) @2.
}{\cs{usepackage}[\oarg{\ldeen{Optionen}{options}}\Marg{osglistings}]}{\pkg*{minted}}

\ldeen*{@1 kennt vier Paketoptionen: @2, @3, @4 und @5, Diese werden
  unverändert an @6s eigene, gleichnamige Ladezeit-Optionen weitergereicht
  und steuern dessen Highlighting-Cache. Ohne Angabe verhält sich
  @6 wie mit seinen eigenen Voreinstellungen. Andere @6-Paketoptionen
  lassen sich derzeit nicht über @1 setzen.}{@1 knows four package
  options: @2, @3, @4, and @5. They are forwarded unchanged to @6's own,
  identically named load-time options, controlling its highlighting
  cache. Without them, @6 behaves as with its own defaults. Other @6
  package options currently cannot be set through @1.}
  {\pkg*{osglistings}}{\code{cache}}{\code{cachedir}}{\code{frozencache}}
  {\code{cacheignoresfilecontents}}{\pkg*{minted}}

\begin{example}[code-only]
  \usepackage[cachedir=build/minted-cache]{osglistings}
\end{example}

\ldeen*{Ein Quelltext, der direkt im Dokument steht, wird mit der Umgebung
  @1 gesetzt; ein Quelltext aus einer externen Datei mit @2. Beide
  akzeptieren dieselben Schlüssel, dazu jede von \pkg*{minted} selbst
  verstandene Option (@3, @4, \ldots), die unverändert durchgereicht
  wird.}{Source that lives directly in the document is typeset with the
  @1 environment; source from an external file with @2. Both accept the
  same keys, plus any option \pkg*{minted} itself understands (@3, @4,
  \ldots), forwarded unchanged.}
  {\code{osglisting}}{\cs{osglistinginput}}{\code{firstline}}{\code{breaklines}}

\begin{example}[compile,program=lualatex,exe-with=--shell-escape,
  graphics={trim=30mm 200mm 30mm 30mm,clip,scale=1.2}]
  \documentclass{article}
  \usepackage{osglistings}
  \begin{document}
    \begin{osglisting}[style=editor, title=main.rs]{rust}
    fn main() {
      println!("hi");
    }
    \end{osglisting}
  \end{document}
\end{example}

\subsection{\ldeen{Rahmenstile}{Box Styles}}
\ldeen*{Der Schlüssel @1 wählt zwischen drei Darstellungen: @2 (ein
  schlichter Rahmen), @3 (\glqq Druckerpapier\grqq{} mit gerissener Kante,
  ohne Kopfzeile) und @4 (ein \glqq Editorfenster\grqq{} mit Titelzeile,
  Sprachsymbol und, falls @5 gesetzt ist, einem Bearbeiten-Symbol). Der
  Stil @3 unterstützt keinen Seitenumbruch innerhalb der Box.}{The @1 key
  chooses between three looks: @2 (a plain frame), @3 (\glqq printer
  paper\grqq{} with a torn edge, no title bar), and @4 (an \glqq editor
  window\grqq{} with a title bar, a language badge, and, when @5 is set,
  an edit-link icon). The @3 style does not support breaking across a page.}
  {\code{style}}{\code{plain}}{\code{paper}}{\code{editor}}{\code{link}}

\subsection{\ldeen{Farbpalette}{Color Palette}}
\ldeen*{Die Syntaxfarben kommen aus einer benannten Palette, aktiviert mit
  @1 bzw.\ für einen einzelnen Aufruf mit dem Schlüssel @2. Eine eigene
  Palette wird mit @3 deklariert; nicht angegebene Slots übernehmen den Wert
  der eingebauten Standardpalette @4.}{Syntax colors come from a named
  palette, activated with @1, or for a single call via the @2 key. A custom
  palette is declared with @3; slots left unspecified inherit their value
  from the built-in default palette @4.}
  {\cs{UseOsgListingsPalette}}{\code{palette}}{\cs{DeclareOsgListingsPalette}}
  {\code{osglistings-default}}

\begin{example}[code-only]
  \DeclareOsgListingsPalette{my-theme}{
    keyword = {model=HTML, value=8A2BE2},
    string  = {alias=keyword},
  }
  \UseOsgListingsPalette{my-theme}
\end{example}

\ldeen*{Jeder Slot ist entweder eine \emph{Ressource} (@1+@2, in jedem von
  \pkg*{xcolor} unterstützten Modell) oder ein \emph{Alias} (@3, höchstens
  zwei Ebenen tief aufgelöst).}{Each slot is either a \emph{resource} (@1+@2,
  in any model \pkg*{xcolor} supports) or an \emph{alias} (@3, resolved at
  most two levels deep).}
  {\code{model}}{\code{value}}{\code{alias=}\meta{other slot}}

\subsection{\ldeen{Links in Online-Editoren}{Links to Online Editors}}
\ldeen*{Der Schlüssel @1 hinterlegt eine URL; @2 legt fest, wo sie
  erscheint: @3 (ein Symbol in der Titelzeile, nur für @4), @5 (ein
  klickbarer Text unterhalb der Box), @6 (ein QR-Code unterhalb der Box)
  oder @7 (kein sichtbarer Hinweis).}
  {The @1 key attaches a URL; @2 controls where it shows up: @3 (an icon in
  the title bar, @4 style only), @5 (clickable text below the box), @6
  (a QR code below the box), or @7 (no visible indicator).}
  {\code{link}}{\code{link-display}}{\code{titlebar}}{\code{editor}}
  {\code{label}}{\code{qrcode}}{\code{none}}

\subsection{\ldeen{Remote-Quellen}{Remote Sources}}
\ldeen*{Das dritte Argument von @1 akzeptiert statt eines lokalen Pfads
  auch einen @2- oder @3-Lokator, z.\,B.\ @4 bzw.\ @5. Der Quelltext wird
  beim ersten Kompilieren geladen und unter @6 gecacht; @7 steuert die
  Wiederverwendung: @8 (Voreinstellung) nutzt einen vorhandenen Cache-
  Eintrag, @9 lädt immer neu und aktualisiert ihn.}
  {The third argument of @1 accepts, besides a local path, a @2 or @3
  locator as well, e.g.\ @4 or @5. The source is fetched on first
  compile and cached under @6; @7 controls reuse: @8 (the default) uses
  an existing cache entry, @9 always re-fetches and updates it.}
  {\cs{osglistinginput}}{\code{gist:}}{\code{github:}}
  {\code{gist:}\meta{owner}\code{/}\meta{gist-id}\code{/}\meta{filename}}
  {\code{github:}\meta{owner}\code{/}\meta{repo}\code{@}\meta{ref}\code{/}\meta{path}}
  {\File{\cs{jobname}.osglistings-cache/}}{\code{cache}}{\code{cache=true}}
  {\code{cache=refresh}}

\begin{example}[code-only]
  \osglistinginput[style=editor, title={fizzbuzz.rs}]
    {rust}{gist:werner-matthias/3af1e0.../fizzbuzz.rs}
\end{example}

\ldeen*{Fehlt der Schlüssel @1, leitet das Paket automatisch einen
  Editor-Link aus dem Sprachregister ab: aus @2, falls für die Sprache
  hinterlegt und die Quelle ein Gist ist (Vorlage typischerweise mit
  @3 statt @4), sonst aus @5. Der Platzhalter @4 wird durch den
  URL-codierten Quelltext ersetzt, @3 durch die Gist-ID.}
  {When @1 is not set, the package automatically derives an editor link
  from the language registry: from @2, if declared for the language and
  the source is a gist (template typically using @3 instead of @4), else
  from @5. The @4 placeholder is replaced with the URL-encoded source,
  @3 with the gist id.}
  {\code{link}}{\code{editor-url-gist}}{\code{\{GIST\_ID\}}}
  {\code{\{CODE\}}}{\code{editor-url}}

\ldeen*{Ein einmal gecachter Eintrag wird beim Kompilieren nicht
  automatisch gegen die Quelle validiert -- @1 (Voreinstellung) prüft
  gar nicht; @2 löst am Dokumentende genau eine Anfrage pro
  \emph{unterschiedlichem} Repository/Gist aus (nicht pro Datei) und
  warnt bei veralteten Einträgen; @3 aktualisiert veraltete Einträge
  dabei zusätzlich sofort. Außerhalb von \LaTeX{} übernimmt das
  Kommandozeilenskript @4 dieselbe Prüfung für einen oder mehrere
  Cache-Ordner.}{An entry, once cached, is not automatically validated
  against its source on compile -- @1 (the default) checks nothing;
  @2 fires exactly one request per \emph{distinct} repository/gist at
  the end of the document (not per file) and warns about stale entries;
  @3 additionally refreshes stale entries immediately. Outside \LaTeX,
  the command-line script @4 runs the same check for one or more cache
  directories.}
  {\code{remote-check=manual}}{\code{remote-check=compile}}
  {\code{remote-refresh=true}}{\File{osglistings-check-updates.lua}}

\begin{example}[code-only]
  texlua osglistings-check-updates.lua --refresh myfile.osglistings-cache
\end{example}

\begin{warnbox}{\ldeen{Achtung, Sicherheitsrisiko!}{Attention, Security Risk!}}
\ldeen*{Ein @1- oder @2-Lokator benötigt zur Übersetzung @3 und ruft
  dabei intern @4 mit den Rechten des \TeX-Prozesses auf. Verwenden Sie
  diese Lokatoren nur in Dokumenten, deren Inhalt (und deren Lokatoren)
  Sie vertrauen -- ein böswillig gewählter Lokator könnte sonst beliebige
  Netzwerkanfragen unter Ihrer Identität auslösen.}
  {A @1 or @2 locator requires compiling with @3 and internally invokes
  @4 with the \TeX{} process's own permissions. Only use these locators
  with documents (and locators) you trust -- a maliciously chosen
  locator could otherwise trigger arbitrary network requests under your
  identity.}
  {\code{gist:}}{\code{github:}}{\texttt{--shell-escape}}{\texttt{curl}}
\end{warnbox}

\subsection{\ldeen{Zeichengenaue Markierungen}{Character-level Marks}}
\ldeen*{@1 legt an der Stelle, an der es im Quelltext steht, einen
  unsichtbaren, benannten \pkg*{tikz}-Knoten ab; ein späteres @2 kann
  von oder zu ihm zeichnen. Für Inline-Quelltext (Umgebung @3) muss dazu
  @4 selbst gesetzt werden (z.\,B.\ @5), damit @1 überhaupt als
  \LaTeX-Code statt als Verbatim-Text erkannt wird.}
  {@1 drops an invisible, named \pkg*{tikz} node exactly where it appears
  in the source; a later @2 can then draw to or from it. For inline
  source (the @3 environment) @4 itself must be set (e.g.\ @5) for @1 to
  be recognized as \LaTeX{} code at all, rather than verbatim text.}
  {\cs{osglistingsmark}\Marg{name}}{\code{tikzpicture}}
  {\code{osglisting}}{\code{escapeinside}}{\code{escapeinside=§§}}

\begin{example}[compile,program=lualatex,exe-with=--shell-escape,
  graphics={trim=30mm 200mm 30mm 30mm,clip,scale=1.2}]
  \documentclass{article}
  \usepackage{tikz}
  \usepackage{osglistings}
  \begin{document}
    \begin{osglisting}[style=plain, escapeinside=§§]{rust}
    let code = ExitCode::from(42§\osglistingsmark{here}§);
    \end{osglisting}
    \begin{tikzpicture}[remember picture, overlay]
      \draw[->, red, thick] (osgm-here) ++(0,1) node[above]{Exit-Code}
        -- (osgm-here);
    \end{tikzpicture}
  \end{document}
\end{example}

\ldeen*{Für externe Quellen übernimmt der Schlüssel @1 von @2 dieselbe
  Aufgabe, ohne dass @3 manuell gesetzt werden muss -- ist @1 nicht
  leer, schaltet das Paket @3 selbst ein (Trennzeichen @4, änderbar über
  @5). Jeder durch Komma getrennte Eintrag adressiert entweder
  zeichengenau über @6 (@7 zählt in \emph{Zeichen}, nicht Bytes) oder
  über eine wörtliche Textsuche mit @8 bzw.\ @9, optional gefolgt von
  \code{occurrence=}\meta{n} (Voreinstellung~1).}
  {For external sources, the @1 key of @2 does the same job without
  needing @3 set manually -- when @1 is non-empty, the package switches
  on @3 itself (delimiter @4, overridable via @5). Each comma-separated
  entry addresses a position either exactly by @6 (@7 counts in
  \emph{characters}, not bytes) or via a literal text search using @8 or
  @9, optionally followed by \code{occurrence=}\meta{n} (default~1).}
  {\code{marks}}{\cs{osglistinginput}}{\code{escapeinside}}{\code{§}}
  {\code{marks-escape}}{\meta{name}\code{ at }\meta{line}\code{:}\meta{col}}
  {\meta{col}}{\meta{name}\code{ after "}\meta{text}\code{"}}
  {\meta{name}\code{ before "}\meta{text}\code{"}}

\begin{example}[code-only]
  \osglistinginput[
    marks = {
      exitc after "ExitCode",
      semi before ";" occurrence=2,
    }
  ]{rust}{src/example.rs}
\end{example}

\ldeen*{@1 verhält sich wie @2/@3, markiert aber keine Einzelstelle,
  sondern grenzt den gefundenen Text mit einem Knotenpaar @4/@5 ein.
  @6 verbindet ein solches Paar anschließend zu einer Markierungsbox --
  dank der in @7 mitgemessenen Zeichenhöhe/-tiefe ganz ohne manuelle
  \code{xshift}/\code{yshift}-Korrekturen. Für Inline-Quelltext lässt
  sich derselbe Effekt auch ohne @8 erzielen, indem @7 einfach zweimal
  (mit den Namen @4/@5) direkt im Quelltext gesetzt wird.}
  {@1 behaves like @2/@3, but instead of marking a single spot it
  brackets the found text with a node pair @4/@5. @6 then connects
  such a pair into a highlight box -- thanks to the character
  height/depth captured by @7, with no manual
  \code{xshift}/\code{yshift} corrections needed. For inline source
  the same effect is available without @8 too, by simply placing @7
  twice (named @4/@5) directly in the source.}
  {\code{around}}{\code{after}}{\code{before}}
  {\meta{name}\code{-begin}}{\meta{name}\code{-end}}
  {\cs{osglistingshighlight}}{\cs{osglistingsmark}}{\code{marks}}

\begin{example}[compile,program=lualatex,exe-with=--shell-escape,
  graphics={trim=30mm 200mm 30mm 30mm,clip,scale=1.2}]
  \documentclass{article}
  \usepackage{tikz}
  \usepackage{osglistings}
  \begin{document}
    \begin{osglisting}[style=plain, escapeinside=§§]{rust}
    let code = §\osglistingsmark{kw-begin}§ExitCode§\osglistingsmark{kw-end}§::from(42);
    \end{osglisting}
    \osglistingshighlight{kw}
  \end{document}
\end{example}

\ldeen*{Für externe Quellen genügt dagegen @1 im @2-Eintrag, gefolgt
  von @3.}
  {For external sources, @1 in the @2 entry is enough instead,
  followed by @3.}
  {\code{around}}{\code{marks}}{\cs{osglistingshighlight}}

\begin{example}[code-only]
  \osglistinginput[
    marks = { exitc around "ExitCode" }
  ]{rust}{src/example.rs}
  \osglistingshighlight{exitc}
\end{example}

\ldeen*{@1 (wie jede Nutzung von @2) benötigt Lua\LaTeX.}
  {@1 (like any use of @2) requires Lua\LaTeX.}
  {\code{marks}}{\cs{osglistingsmark}}

\subsection{\ldeen{Barrierefreiheit}{Accessibility}}
\ldeen*{Trägt ein @1-Eintrag zusätzlich @2, öffnet die Ersetzung statt
  eines bloßen Punktpaars eine getaggte @3-Struktur mit diesem Text als
  \code{alt}-Attribut (Voraussetzung: @4, sonst bleibt es beim
  Punktpaar) -- assistive Technologien lesen dann @2 statt des
  markierten Quelltexts selbst. Für Inline-Quelltext leisten @5/@6
  dasselbe von Hand. @7 selbst kapselt sein Overlay dabei automatisch
  als \code{Artifact}; assistive Technologien überspringen es, weil die
  eigentliche Information ja schon über @2 am Quelltext hängt, nicht am
  Pfeil oder der Box drumherum.}
  {When an @1 entry also carries @2, the substitution opens a tagged
  @3 structure instead of a bare point pair, with that text as its
  \code{alt} attribute (requires @4; without it, a plain point pair as
  usual) -- assistive technology then reads @2 instead of the marked-up
  source itself. For inline source, @5/@6 do the same by hand. @7
  itself wraps its overlay as an \code{Artifact} automatically;
  assistive technology skips it, because the real information already
  hangs off the source via @2, not off the arrow or box around it.}
  {\code{around}}{\code{desc="}\meta{text}\code{"}}{\code{Span}}
  {\cs{DocumentMetadata}\Marg{tagging=on}}
  {\cs{osglistingsmarkspanbegin}}{\cs{osglistingsmarkspanend}}
  {\cs{osglistingshighlight}}

\begin{example}[code-only]
  \osglistinginput[
    marks = {
      exitc around "ExitCode" desc="the process exit status",
    }
  ]{rust}{src/example.rs}
  \osglistingshighlight{exitc}
\end{example}

\begin{warnbox}{\ldeen{Nur getaggte Spannen tragen Text}{Only Tagged Spans Carry Text}}
\ldeen*{Der übrige Quelltext landet zwar in einer getaggten @1-Struktur,
  aber (Stand dieser Implementierung) ohne für Screenreader auslesbaren
  Text -- @2 ist für eine markierte Spanne also meist nicht Zusatz,
  sondern die einzige tatsächlich vorgelesene Information.}
  {The rest of the source does land inside a tagged @1 structure, but
  (as currently implemented) without screen-reader-readable text -- for
  a marked span, @2 is therefore usually not an addition but the only
  information actually read aloud.}{\code{Code}}{\code{desc}}
\end{warnbox}

\begin{warnbox}{\ldeen{Zweiter Kompilierlauf nötig}{Second Compile Run Needed}}
\ldeen*{@1 löst sich, wie jedes seitenübergreifende \pkg*{tikz}-Overlay,
  erst nach einem zweiten Kompilierlauf korrekt auf -- vor dem ersten
  Lauf (oder nach Änderungen an den markierten Positionen) zeigen
  Pfeile/Markierungen noch auf ihre vorherige Position oder gar nicht.}
  {@1 only resolves correctly, like any cross-page \pkg*{tikz} overlay,
  after a second compile run -- before the first run (or after changing
  the marked positions) arrows/markers still point at their previous
  position or not at all.}{\code{remember~picture}}
\end{warnbox}

\subsection{\ldeen{Tagging}{Tagging}}
\ldeen*{Ist \LaTeX{} mit @1 gestartet, wird der Quelltext als
  Struktur @2 getaggt (Slotname @3, umsetzbar mit @4/@5). Tagging wird pro
  Aufruf mit @6 abgeschaltet.}{When \LaTeX{} is started with @1, the source
  is tagged as structure @2 (structure name @3, remappable with @4/@5).
  Tagging is switched off per call with @6.}
  {\cs{DocumentMetadata}\Marg{tagging=on}}{\code{Code}}
  {\code{osglistings/code}}{\cs{NewStructureName}}{\cs{AssignStructureRole}}
  {\code{tag=false}}

\section{Implementation}
\MaybeStop{\ldeen*{Um die Implementationsbeschreibung zu erhalten, kommentieren Sie bitte \cs{OnlyDescription} in @1 aus.}{To view the implementation description, please uncomment @1 in \code{osgdoc.dtx}.}{\File{osglistings.dtx}}}

\subsection{\emph{osglistings.sty}}
\AutoDocInput{osglistings.dtx}{package}
\end{document}

%</driver>

%<*package>
\RequirePackage{xcolor}
\RequirePackage{tikz}
\usetikzlibrary{positioning,calc,shapes.geometric,decorations.pathreplacing,decorations.pathmorphing,fit}
\RequirePackage{tcolorbox}
\tcbuselibrary{skins,breakable}
\RequirePackage{fontawesome5}
\RequirePackage{hyperref}
\RequirePackage{qrcode}
% \ldeen{@1/@2/\ldots{} sind Ladezeit-Paketoptionen von @3, keine
%   \cs{setminted}-Schlüssel -- sie steuern, ob/wohin @3 seinen
%   Highlighting-Cache schreibt, nicht das Aussehen eines einzelnen
%   Listings. Nur diese vier werden durchgereicht: @4/@5 (der Nummerierungs-
%   Parent von @3s eigenem, hier ungenutzten Listing-Float) blieben
%   bewusst außen vor, weil ein falsch gesetzter Wert dort das
%   Kapitel-/Abschnitts-Zählsystem des ganzen Dokuments durcheinanderbringen
%   kann, nicht nur die Darstellung eines Listings.}{@1/@2/\ldots{} are
%   load-time package options of @3, not \cs{setminted} keys -- they
%   control whether/where @3 writes its highlighting cache, not how a
%   single listing looks. Only these four are forwarded: @4/@5 (the
%   numbering parent of @3's own, here-unused listing float) were
%   deliberately left out, because a wrong value there can derail the
%   whole document's chapter/section counters, not just one listing's
%   appearance.}{\code{cache}}{\code{cachedir}}{\pkg*{minted}}
%   {\code{chapter}}{\code{section}}
\RequirePackage{l3keys2e}
\ExplSyntaxOn
\tl_new:N \l__osglistings_mintedopts_tl
\tl_set:Nn \l__osglistings_mintedopts_tl { newfloat, highlightmode=immediate }
\bool_new:N \l__osglistings_cacheignoresfilecontents_given_bool
\tl_new:N \l__osglistings_cacheignoresfilecontents_tl
\keys_define:nn { osglistings/options }
  {
    cache .choices:nn = { true, false }
      { \tl_put_right:Nx \l__osglistings_mintedopts_tl { , cache = \l_keys_choice_tl } },
    cache .default:n = true,
    cachedir .code:n =
      { \tl_put_right:Nx \l__osglistings_mintedopts_tl { , cachedir = {#1} } },
    frozencache .choices:nn = { true, false }
      { \tl_put_right:Nx \l__osglistings_mintedopts_tl { , frozencache = \l_keys_choice_tl } },
    frozencache .default:n = true,
% \ldeen{@1 wird nicht mehr an @2s Ladezeit-Optionen angehängt, sondern
%   erst danach über dessen eigenen etoolbox-Bool gesetzt (siehe unten) --
%   @1 gibt es erst seit einer neueren @2-Version, und eine unbekannte
%   Ladezeit-Option würde auf älteren Installationen sonst kommentarlos
%   verworfen, ohne dass @3 dies dem Nutzer mitteilen könnte.}{@1 is no
%   longer appended to @2's load-time options but set afterwards through
%   its own etoolbox bool instead (see below) -- @1 only exists from a
%   certain @2 version onward, and an unknown load-time option would
%   otherwise be silently dropped on older installations, leaving @3 with
%   no way to tell the user.}{\code{cacheignoresfilecontents}}{\pkg*{minted}}
%   {\pkg*{osglistings}}
    cacheignoresfilecontents .choices:nn = { true, false }
      {
        \bool_set_true:N \l__osglistings_cacheignoresfilecontents_given_bool
        \tl_set:Nx \l__osglistings_cacheignoresfilecontents_tl { \l_keys_choice_tl }
      },
    cacheignoresfilecontents .default:n = true,
  }
\ProcessKeysPackageOptions { osglistings/options }
% \ldeen{@1 kann @2 nicht direkt als eckige Klammer füttern -- anders als
%   bei einem regulären \pkg*{expl3}-Argument nimmt @3 seine optionale
%   Klammer über eigene, klassische Scan-Logik entgegen, nicht über ein
%   \pkg*{expl3}-Argumentschema, das \cs{exp\_args:No} umleiten könnte.
%   Ein \cs{tl\_set:Nx} baut den kompletten Aufruf inklusive Klammer
%   stattdessen als expandierten Text zusammen.}{@1 cannot feed @2
%   directly into a square-bracket argument -- unlike a regular
%   \pkg*{expl3} argument, @3 takes its optional bracket via its own,
%   classic scanning logic, not an \pkg*{expl3} argument scheme that
%   \cs{exp\_args:No} could redirect. A \cs{tl\_set:Nx} instead builds
%   the whole call, brackets included, as expanded text.}
%   {\cs{exp\_args:No}}{\cs{l\_\_osglistings\_mintedopts\_tl}}{\cs{RequirePackage}}
\tl_new:N \l__osglistings_mintedload_tl
\tl_set:Nx \l__osglistings_mintedload_tl
  { \exp_not:N \RequirePackage [ \l__osglistings_mintedopts_tl ] { minted } }
\l__osglistings_mintedload_tl
% \ldeen{@1 prüft mit @2 die Existenz von @3s eigenem Bool, statt dessen
%   Version zu vergleichen -- der Bool entsteht nur, wenn @3 die Option
%   selbst kennt, ist also unabhängig von der genauen Versionsnummer, ab
%   der sie eingeführt wurde.}{@1 checks whether @3's own bool exists via
%   @2 rather than comparing version numbers -- the bool only comes into
%   being if @3 knows the option itself, so this is independent of the
%   exact version number at which it was introduced.}
%   {\pkg*{osglistings}}{\cs{ifcsname}}{\pkg*{minted}}
\bool_if:NT \l__osglistings_cacheignoresfilecontents_given_bool
  {
    \ifcsname ifminted@cacheignoresfilecontents\endcsname
      \str_if_eq:eeTF { \l__osglistings_cacheignoresfilecontents_tl } { true }
        { \booltrue{minted@cacheignoresfilecontents} }
        { \boolfalse{minted@cacheignoresfilecontents} }
    \else
      \PackageWarningNoLine{osglistings}
        { The~option~'cacheignoresfilecontents'~requires~a~newer~version~
          of~minted~than~the~one~currently~loaded;~ignoring~it }
    \fi
  }
\ExplSyntaxOff
\setminted{style=default}
% \ldeen{Latin Modern Mono (die Standardschrift von \pkg*{minted}/\pkg*{fvextra})
%   deckt weder Kyrillisch noch die meisten Symbolblöcke ab -- fehlende
%   Zeichen werden von \LuaTeX{} stillschweigend ausgelassen, nicht durch
%   Kästchen ersetzt. @1 hat spürbar breitere Abdeckung und ist Teil jeder
%   vollständigen \TeX~Live-Installation; ohne sie fällt die Schrift auf
%   \cs{ttfamily} zurück.}{Latin Modern Mono (the default font of
%   \pkg*{minted}/\pkg*{fvextra}) covers neither Cyrillic nor most symbol
%   blocks -- missing glyphs are silently dropped by \LuaTeX, not replaced
%   with a box. @1 has noticeably wider coverage and ships with any full
%   \TeX~Live install; without it, the font falls back to \cs{ttfamily}.}
%   {DejaVu~Sans~Mono}
\RequirePackage{fontspec}
\IfFontExistsTF{DejaVu Sans Mono}
  { \newfontfamily\osglistingsmonofont{DejaVu Sans Mono} }
  {
    \PackageWarning{osglistings}
      {Font~"DejaVu~Sans~Mono"~not~found;~falling~back~to~ttfamily.\MessageBreak
       Multi-byte~UTF-8~source~(Cyrillic,~symbols,~...)~may~lose~glyphs}
    \let\osglistingsmonofont\ttfamily
  }
% \ldeen{\pkg*{fvextra} wählt seine Verbatim-Schrift beim Öffnen jeder
%   Umgebung selbst neu (Default @1); ein umgebendes \cs{fontupper} wird
%   dabei überschrieben. @2 ist der von \pkg*{fvextra} vorgesehene Haken,
%   um stattdessen ein beliebiges Font-Makro zu benutzen.}{\pkg*{fvextra}
%   reselects its own verbatim font every time an environment opens
%   (default @1); a surrounding \cs{fontupper} gets overwritten in the
%   process. @2 is \pkg*{fvextra}'s own hook for using an arbitrary font
%   macro instead.}{\cs{ttfamily}}{\code{fontfamily=myFont}}
\let\myFont\osglistingsmonofont
\setminted{fontfamily=myFont}
\makeatletter
\ExplSyntaxOn

% \subsection{\ldeen{Farbpalette}{Color palette}}
% \ldeen*{Eine Palette bildet portable Slotnamen auf konkrete
%   \pkg*{xcolor}-Farben ab; die Rohangaben (Ressource oder Alias) landen in
%   dieser Prop unter dem Schlüssel "\meta{Palette}/\meta{Slot}". Erst
%   @1 löst sie zu tatsächlichen \cs{definecolor}-Aufrufen auf.}{A palette
%   maps portable slot names onto concrete \pkg*{xcolor} colors; the raw
%   specification (resource or alias) lands in this prop keyed by
%   "\meta{palette}/\meta{slot}". Only @1 resolves it to actual
%   \cs{definecolor} calls.}{\cs{UseOsgListingsPalette}}
\prop_new:N \g__osglistings_palette_data_prop
\clist_const:Nn \c__osglistings_palette_slots_clist
  {
    background, frame, titlebar-bg, titlebar-fg,
    keyword, string, comment, number, operator, directive, emph,
    linenumber, fail-icon
  }
\tl_new:N \g_osglistings_active_palette_tl
\tl_new:N \l__osglistings_slot_model_tl
\tl_new:N \l__osglistings_slot_value_tl
\tl_new:N \l__osglistings_slot_alias_tl
\tl_new:N \l__osglistings_tmp_tl
\tl_new:N \l__osglistings_tmp_two_tl
\tl_new:N \l__osglistings_active_lookup_tl

\keys_define:nn { osglistings/paletteslot }
  {
    model .tl_set:N = \l__osglistings_slot_model_tl,
    model .initial:n = { },
    value .tl_set:N = \l__osglistings_slot_value_tl,
    value .initial:n = { },
    alias .tl_set:N = \l__osglistings_slot_alias_tl,
    alias .initial:n = { },
  }
\tl_new:N \l__osglistings_palette_name_tl

% \ldeen{Ein Key pro erlaubtem Slotnamen speichert die Rohangabe unter
%   "\meta{Palette}/\meta{Slot}", ohne sie schon aufzulösen -- Aliase
%   dürfen auf Slots verweisen, die erst später in derselben Deklaration
%   folgen.}{One key per allowed slot name stores the raw spec under
%   "\meta{palette}/\meta{slot}", without resolving it yet -- aliases may
%   point at slots that only follow later in the same declaration.}
% \ldeen{Schlüssel und Wert werden mit \code{.tl\_set:Nx} eingefroren, bevor
%   sie in die Prop wandern -- sonst landete dort nur ein Verweis auf die
%   (wiederverwendeten) Slot-Variablen, und jeder Eintrag zeigte am Ende
%   den zuletzt verarbeiteten Slot.}{Key and value are frozen with
%   \code{.tl\_set:Nx} before going into the prop -- otherwise it would only
%   store a reference to the (reused) slot variables, and every entry
%   would end up showing whichever slot was processed last.}
\cs_new_protected:Npn \__osglistings_palette_slot:nn #1 #2
  {
    \__osglistings_slot_spec_set:n {#2}
    \tl_set:Nx \l__osglistings_tmp_tl
      { \l__osglistings_palette_name_tl / #1 }
    \tl_set:Nx \l__osglistings_tmp_two_tl
      {
        model = \l__osglistings_slot_model_tl,
        value = \l__osglistings_slot_value_tl,
        alias = \l__osglistings_slot_alias_tl,
      }
    \exp_args:NNVV \prop_gput:Nnn \g__osglistings_palette_data_prop
      \l__osglistings_tmp_tl \l__osglistings_tmp_two_tl
  }
\clist_map_inline:Nn \c__osglistings_palette_slots_clist
  {
    \keys_define:nn { osglistings/palette }
      { #1 .code:n = \__osglistings_palette_slot:nn {#1} {##1} }
  }

\msg_new:nnn { osglistings } { unknown-palette }
  { Palette~"#1"~is~not~declared.~Use~\token_to_str:N\DeclareOsgListingsPalette~before~using~it. }
\msg_new:nnn { osglistings } { alias-too-deep }
  { Slot~"#2"~of~palette~"#1"~is~an~alias~chain~longer~than~two~levels;~falling~back~to~black. }

% \ldeen{Neue Paletten übernehmen zunächst jeden Slot der eingebauten
%   Standardpalette; die übergebenen Keys überschreiben danach nur die
%   ausdrücklich genannten Slots.}{New palettes start by inheriting every
%   slot from the built-in default palette; the keys passed in then
%   override only the slots named explicitly.}
\cs_new_protected:Npn \DeclareOsgListingsPalette #1 #2
  {
    \tl_set:Nn \l__osglistings_palette_name_tl {#1}
    \str_if_eq:eeF {#1} { osglistings-default }
      {
        \clist_map_inline:Nn \c__osglistings_palette_slots_clist
          {
            \prop_get:NnNTF \g__osglistings_palette_data_prop
              { osglistings-default / ##1 } \l__osglistings_tmp_tl
              {
                \prop_gput:NnV \g__osglistings_palette_data_prop
                  { #1 / ##1 } \l__osglistings_tmp_tl
              }
              { }
          }
      }
    \keys_set:nn { osglistings/palette } {#2}
  }

% \ldeen{Setzt die drei Slot-Spec-Variablen aus einem gespeicherten
%   Eintrag; sie werden vorher geleert, weil @1 nicht erwähnte Keys nicht
%   auf ihren @2 zurücksetzt.}{Sets the three slot-spec variables from a
%   stored entry; they are cleared first because @1 does not reset keys
%   it isn't given back to their @2.}{\cs{keys\_set:nn}}{\code{.initial:n}}
\cs_new_protected:Npn \__osglistings_slot_spec_set:n #1
  {
    \tl_clear:N \l__osglistings_slot_model_tl
    \tl_clear:N \l__osglistings_slot_value_tl
    \tl_clear:N \l__osglistings_slot_alias_tl
    \exp_args:Nnx \keys_set:nn { osglistings/paletteslot } {#1}
  }
% \ldeen{Löst einen Slot höchstens zweistufig zu einer Ressource auf und
%   definiert die zugehörige Farbe \code{osglistings@color@}\meta{Slot}.}
%   {Resolves a slot at most two levels deep to a resource and defines the
%   corresponding color \code{osglistings@color@}\meta{slot}.}
\cs_new_protected:Npn \__osglistings_palette_resolve:nn #1 #2
  {
    \tl_set:Nx \l__osglistings_tmp_two_tl { #1 / #2 }
    \exp_args:NNV \prop_get:NnNTF \g__osglistings_palette_data_prop
      \l__osglistings_tmp_two_tl
      \l__osglistings_tmp_tl
      {
        \__osglistings_slot_spec_set:n { \l__osglistings_tmp_tl }
        \tl_if_empty:NTF \l__osglistings_slot_alias_tl
          {
            \definecolor { osglistings@color@#2 }
              { \l__osglistings_slot_model_tl } { \l__osglistings_slot_value_tl }
          }
          {
            \tl_set:Nx \l__osglistings_tmp_two_tl
              { #1 / \l__osglistings_slot_alias_tl }
            \exp_args:NNV \prop_get:NnNTF \g__osglistings_palette_data_prop
              \l__osglistings_tmp_two_tl
              \l__osglistings_tmp_tl
              {
                \__osglistings_slot_spec_set:n { \l__osglistings_tmp_tl }
                \tl_if_empty:NTF \l__osglistings_slot_alias_tl
                  {
                    \definecolor { osglistings@color@#2 }
                      { \l__osglistings_slot_model_tl } { \l__osglistings_slot_value_tl }
                  }
                  { \msg_warning:nnnn { osglistings } { alias-too-deep } {#1} {#2}
                    \definecolor { osglistings@color@#2 } { rgb } { 0,0,0 } }
              }
              { \definecolor { osglistings@color@#2 } { rgb } { 0,0,0 } }
          }
      }
      { \definecolor { osglistings@color@#2 } { rgb } { 0,0,0 } }
  }

% \ldeen{@1 kann eine wörtliche Palette oder eine Variable sein, die einen
%   Palettennamen enthält (z.\,B.\ der @2-Schlüssel); deshalb wird zuerst
%   einmal expandiert und das Ergebnis danach überall wiederverwendet.}
%   {@1 may be a literal palette name or a variable holding one (e.g.\ the
%   @2 key); it is therefore expanded once up front and the result reused
%   throughout.}{\cs{UseOsgListingsPalette}}{\code{palette=}}
\cs_new_protected:Npn \UseOsgListingsPalette #1
  {
    \tl_set:Nx \l__osglistings_active_lookup_tl {#1}
    \tl_set:Nx \l__osglistings_tmp_tl
      { \l__osglistings_active_lookup_tl / background }
    \exp_args:NNV \prop_get:NnNTF \g__osglistings_palette_data_prop
      \l__osglistings_tmp_tl
      \l__osglistings_tmp_two_tl
      {
        \clist_map_inline:Nn \c__osglistings_palette_slots_clist
          {
            \exp_args:NV \__osglistings_palette_resolve:nn
              \l__osglistings_active_lookup_tl {##1}
          }
        \tl_gset:Nx \g_osglistings_active_palette_tl { \l__osglistings_active_lookup_tl }
      }
      { \msg_error:nnx { osglistings } { unknown-palette } { \l__osglistings_active_lookup_tl } }
  }

% \ldeen{Rein expandierbar: liefert den Farbnamen eines Slots der
%   aktuell aufgelösten Palette, verwendbar überall dort, wo \pkg*{xcolor}
%   einen Farbnamen erwartet.}{Purely expandable: returns the color name
%   of a slot in the currently resolved palette, usable anywhere
%   \pkg*{xcolor} expects a color name.}
\cs_new:Npn \OsgListingsColor #1 { osglistings@color@#1 }

% \ldeen{Hausfarben, portiert aus den historischen \cs{clst...}-Makros.}
%   {House colors, ported from the historical \cs{clst...} macros.}
\DeclareOsgListingsPalette { osglistings-default }
  {
    background  = { model=gray,  value=1 },
    frame       = { model=gray,  value=0.6 },
    titlebar-bg = { model=gray,  value=0.9 },
    titlebar-fg = { model=gray,  value=0 },
    keyword     = { model=HTML,  value=1B5E20 },
    string      = { model=HTML,  value=0000CD },
    comment     = { model=HTML,  value=00CDCD },
    number      = { model=gray,  value=0.5 },
    operator    = { model=gray,  value=0.3 },
    directive   = { model=HTML,  value=8B008B },
    emph        = { model=HTML,  value=CC6600 },
    linenumber  = { model=gray,  value=0.5 },
    fail-icon   = { model=HTML,  value=CC0000 },
  }
\UseOsgListingsPalette { osglistings-default }

% \subsection{\ldeen{Pygments-Farbkopplung}{Pygments color hook}}
% \ldeen*{\pkg*{minted} legt seine Token-Farben als
%   \cs{PYG@tok@}\meta{Kürzel}-Makros neu an, jedes Mal wenn ein Stil
%   (wieder-)geladen wird -- lokal zum jeweiligen Aufruf, siehe
%   \cs{minted@patch@PygmentsStyledef} in \File{minted.sty}. Das ist der
%   einzige Punkt, an dem sich diese Makros zuverlässig umbiegen lassen,
%   bevor \pkg*{minted} sie zum Setzen des Quelltexts benutzt.}{@1
%   redefines its token colors as \cs{PYG@tok@}\meta{code} macros every
%   time a style is (re)loaded -- locally to that call, see
%   \cs{minted@patch@PygmentsStyledef} in \File{minted.sty}. That is the
%   only place these macros can reliably be redirected before @1 uses them
%   to typeset the source.}{\pkg*{minted}}
% \ldeen{\cs{cs\_gset:cn}/\cs{cs\_gset:Nn} lehnen Zielnamen ohne \code{:} als
%   Kodierfehler ab; die Pygments-Tokenmakros folgen aber \pkg*{minted}s
%   \LaTeXe-Namensschema, nicht dem von \pkg*{expl3}, deshalb der Umweg
%   über die nackten Primitive.}{\cs{cs\_gset:cn}/\cs{cs\_gset:Nn} reject
%   target names without a \code{:} as a coding error; the Pygments token
%   macros follow \pkg*{minted}'s \LaTeXe{} naming scheme, not \pkg*{expl3}'s,
%   hence the detour through the bare primitives.}
% \ldeen{Die eigentliche Parametermarke \code{#1} steckt allein in @1s
%   eigener, einstufiger Definition; \cs{PYG@tok@}\meta{Kürzel}s Rumpf
%   enthält dadurch selbst kein \code{\#}~mehr und muss nicht durch
%   mehrere ineinander verschachtelte \cs{tex\_def:D}-Aufrufe hindurch mit
%   der richtigen Anzahl Doppelraute überleben.}{The real parameter marker
%   \code{\#1} lives entirely inside @1's own, single-level definition;
%   \cs{PYG@tok@}\meta{code}'s body therefore contains no \code{\#} of its
%   own and doesn't have to survive the right number of doubled hashes
%   through several nested \cs{tex\_def:D} calls.}
%   {\cs{\_\_osglistings\_pyg\_settc:n}}
\cs_new_protected:Npn \__osglistings_pyg_settc:n #1
  { \tex_def:D \PYG@tc ##1 { \textcolor { \OsgListingsColor{#1} } {##1} } }
\cs_new_protected:Npn \__osglistings_pygments_tok:nn #1 #2
  {
    \tex_global:D \exp_args:Nc \tex_def:D { PYG@tok@#1 }
      { \__osglistings_pyg_settc:n {#2} }
  }
\cs_new_protected:Npn \__osglistings_pyg_seterr:
  {
    \tex_def:D \PYG@bc ##1
      { { \fboxsep=-\fboxrule
          \fcolorbox { \OsgListingsColor{fail-icon} } { osglistings@color@background }
            { \strut ##1 } } }
  }
\cs_new_protected:Npn \__osglistings_patch_pygments_colors:
  {
    \clist_map_inline:nn { k,kd,kn,kt,kp,kc,kr,bp }
      { \__osglistings_pygments_tok:nn {##1} { keyword } }
    \clist_map_inline:nn { s,sd,sh,s1,s2,sx,ss,si,se,sr }
      { \__osglistings_pygments_tok:nn {##1} { string } }
    \clist_map_inline:nn { c,c1,cm,cs,cp }
      { \__osglistings_pygments_tok:nn {##1} { comment } }
    \clist_map_inline:nn { m,mi,mf,mh,mo,il }
      { \__osglistings_pygments_tok:nn {##1} { number } }
    \clist_map_inline:nn { o,ow }
      { \__osglistings_pygments_tok:nn {##1} { operator } }
    \clist_map_inline:nn { nd,na }
      { \__osglistings_pygments_tok:nn {##1} { directive } }
    \tex_global:D \tex_def:D \PYG@tok@err { \__osglistings_pyg_seterr: }
  }
\cs_if_exist:NT \minted@patch@PygmentsStyledef
  { \g@addto@macro \minted@patch@PygmentsStyledef \__osglistings_patch_pygments_colors: }

% \subsection{\ldeen{Sprachregister}{Language registry}}
\prop_new:N \g__osglistings_lang_icon_prop
\prop_new:N \g__osglistings_lang_url_prop
\prop_new:N \g__osglistings_lang_gisturl_prop
\keys_define:nn { osglistings/language }
  {
    % \ldeen{\code{.code:n} liefert den Slotnamen unexpandiert -- ohne
    %   \cs{exp\_args:NNx} wäre der gespeicherte Schlüssel das
    %   \emph{Makro} \cs{l\_\_osglistings\_lang\_name\_tl} selbst, nicht
    %   sein Wert, sodass alle Sprachen denselben (falschen) Eintrag
    %   überschreiben würden.}{\code{.code:n} leaves the slot name
    %   unexpanded -- without \cs{exp\_args:NNx} the stored key would be
    %   the \emph{macro} \cs{l\_\_osglistings\_lang\_name\_tl} itself, not
    %   its value, so every language would overwrite the same (wrong)
    %   entry.}
    icon .code:n = \exp_args:NNx \prop_gput:Nnn \g__osglistings_lang_icon_prop { \l__osglistings_lang_name_tl } {#1},
    editor-url .code:n = \exp_args:NNx \prop_gput:Nnn \g__osglistings_lang_url_prop { \l__osglistings_lang_name_tl } {#1},
    % \ldeen{Eigene, bevorzugte Vorlage für Gist-Quellen (typischerweise
    %   mit \code{\{GIST\_ID\}} statt \code{\{CODE\}}, z.\,B.\ Rust
    %   Playgrounds \code{gist=}-Parameter) -- \code{editor-url} bleibt
    %   der Fallback für alle anderen Quellen.}{Own, preferred template for
    %   gist sources (typically using \code{\{GIST\_ID\}} instead of
    %   \code{\{CODE\}}, e.g.\ Rust Playground's \code{gist=} parameter) --
    %   \code{editor-url} remains the fallback for every other source.}
    editor-url-gist .code:n = \exp_args:NNx \prop_gput:Nnn \g__osglistings_lang_gisturl_prop { \l__osglistings_lang_name_tl } {#1},
  }
\tl_new:N \l__osglistings_lang_name_tl
\cs_new_protected:Npn \DeclareOsgListingsLanguage #1 #2
  {
    \tl_set:Nn \l__osglistings_lang_name_tl {#1}
    \keys_set:nn { osglistings/language } {#2}
  }
\NewDocumentCommand \osglistingshextile { m }
  {
    \tikz[baseline=-0.5ex]{
      \node[regular~polygon, draw, regular~polygon~sides=6,
        shape~border~rotate=30, inner~sep=.5pt, fill=black,
        text=white, font=\tiny\bfseries\sffamily] {#1};
    }
  }
% \ldeen{@1 in den URL-Vorlagen ist ein Platzhalter für den (in Phase~2
%   hinzukommenden) codierten Quelltext -- in Phase~1 unbenutzt, aber schon
%   hinterlegt, damit sich das Sprachregister später nicht ändert.}{@1 in
%   the URL templates is a placeholder for the (Phase~2) encoded source --
%   unused in Phase~1, but already stored so the language registry doesn't
%   need to change later.}{\{CODE\}}
\DeclareOsgListingsLanguage { rust }   { icon = \faRust,   editor-url = { https://play.rust-lang.org/?version=stable\&mode=debug\&edition=2021\&code={CODE} }, editor-url-gist = { https://play.rust-lang.org/?version=stable\&mode=debug\&edition=2021\&gist={GIST_ID} } }
\DeclareOsgListingsLanguage { python } { icon = \faPython, editor-url = { https://trinket.io/python3?code={CODE} } }
\DeclareOsgListingsLanguage { c }      { icon = \osglistingshextile{C}, editor-url = { https://code.livegap.com/?l=c\&code={CODE} } }
\DeclareOsgListingsLanguage { cpp }    { icon = \osglistingshextile{\smash{\raisebox{1pt}{\scalebox{.7}{C++}}}}, editor-url = { https://code.livegap.com/?l=cpp\&code={CODE} } }

\cs_new:Npn \__osglistings_lang_icon:n #1
  {
    \prop_item:Nn \g__osglistings_lang_icon_prop {#1}
  }

% \subsection{\ldeen{Tagging}{Tagging}}
% \ldeen{Tagging darf nicht erfolgen, wenn das Dokument nicht mit @1
%   geladen wurde.}{Tagging must not happen when the document was not
%   loaded with @1.}{\cs{DocumentMetadata}\Marg{tagging=on}}
\cs_new_protected:Npn \__osglistings_tag_struct_begin:n #1
  { \cs_if_exist:NT \tagstructbegin { \tagstructbegin{ tag=#1 } } }
\cs_new_protected:Npn \__osglistings_tag_struct_end:
  { \cs_if_exist:NT \tagstructend { \tagstructend } }
\cs_if_exist:NT \NewStructureName
  {
    \NewStructureName{ osglistings/code }
    \AssignStructureRole{ osglistings/code }{ Code }
  }

% \subsection{\ldeen{Schlüssel}{Keys}}
\tl_new:N \l__osglistings_style_tl
\tl_new:N \l__osglistings_title_tl
\tl_new:N \l__osglistings_langicon_tl
\tl_new:N \l__osglistings_link_tl
\tl_new:N \l__osglistings_linkdisplay_tl
\tl_new:N \l__osglistings_palette_tl
\bool_new:N \l__osglistings_tag_bool
\tl_new:N \l__osglistings_tagname_tl
\tl_new:N \l__osglistings_extra_tl
\tl_new:N \l__osglistings_cache_tl
\tl_new:N \l__osglistings_remotecheck_tl
\bool_new:N \l__osglistings_remoterefresh_bool
\tl_new:N \l__osglistings_marks_tl
\tl_new:N \l__osglistings_marksescape_tl

\keys_define:nn { osglistings }
  {
    style .choices:nn = { plain, paper, editor }
      { \tl_set:Nx \l__osglistings_style_tl { \l_keys_choice_tl } },
    title .tl_set:N = \l__osglistings_title_tl,
    lang-icon .tl_set:N = \l__osglistings_langicon_tl,
    link .tl_set:N = \l__osglistings_link_tl,
    link-display .choices:nn = { titlebar, label, none, qrcode }
      { \tl_set:Nx \l__osglistings_linkdisplay_tl { \l_keys_choice_tl } },
    palette .tl_set:N = \l__osglistings_palette_tl,
    % \ldeen{Nur für \cs{osglistinginput} relevant; siehe Abschnitt
    %   \glqq Zeichengenaue Markierungen\grqq.}{Only relevant for
    %   \cs{osglistinginput}; see the \glqq Character-level marks\grqq{}
    %   section.}
    marks .tl_set:N = \l__osglistings_marks_tl,
    marks-escape .tl_set:N = \l__osglistings_marksescape_tl,
    tag .bool_set:N = \l__osglistings_tag_bool,
    tag .default:n = true,
    tag-name .tl_set:N = \l__osglistings_tagname_tl,
    % \ldeen{Nur für \code{gist:}/\code{github:}-Quellen relevant; steuert,
    %   ob eine vorhandene, gecachte Fassung wiederverwendet wird.}{Only
    %   relevant for \code{gist:}/\code{github:} sources; controls whether
    %   an existing cached copy is reused.}
    cache .choices:nn = { true, false, refresh }
      { \tl_set:Nx \l__osglistings_cache_tl { \l_keys_choice_tl } },
    % \ldeen{Paketweite Option (nicht pro Aufruf): automatischer
    %   Aktualisierungscheck beim Kompilieren -- bewusst standardmäßig aus,
    %   siehe Sicherheitshinweis im Abschnitt \glqq Remote-Quellen\grqq.}
    %   {Package-wide option (not per call): automatic update check on
    %   compile -- deliberately off by default, see the security note in
    %   the \glqq Remote sources\grqq{} section.}
    remote-check .choices:nn = { manual, compile }
      { \tl_set:Nx \l__osglistings_remotecheck_tl { \l_keys_choice_tl } },
    remote-refresh .bool_set:N = \l__osglistings_remoterefresh_bool,
    % \ldeen{@1 statt @2: \cs{l_keys_key_str} ist eine
    %   gemeinsam genutzte l3keys-Variable, die \pkg*{minted}s eigener
    %   \cs{keys_set:nn}-Aufruf (beim späteren Verarbeiten von @3)
    %   selbst wieder überschreibt -- ohne sofortige Expansion würde
    %   hier nur eine \emph{Referenz} gespeichert, die zum
    %   Verwendungszeitpunkt längst einen anderen Schlüsselnamen
    %   trägt. @4 schützt dagegen den Wert @5 selbst vor dieser Expansion:
    %   Ein Wert wie \cs{tiny} expandiert unter \cs{edef}/@1 zu
    %   \cs{let}\cs{@currsize}\cs{tiny} -- das erneut lesbare \cs{tiny}
    %   am Ende expandiert die \cs{edef}-Maschinerie sofort weiter, und
    %   das wiederholt sich ohne Ende, bis der Eingabestack überläuft.}
    %   {@1 instead of @2: \cs{l_keys_key_str} is a shared l3keys
    %   variable that \pkg*{minted}'s own \cs{keys\_set:nn} call (when
    %   later processing @3) overwrites again itself -- without
    %   immediate expansion this would only store a \emph{reference},
    %   which by the time it's used already holds some other key's
    %   name. @4 in turn protects the value @5 itself from that same
    %   expansion: a value such as \cs{tiny} expands under \cs{edef}/@1
    %   to \cs{let}\cs{@currsize}\cs{tiny} -- the freshly re-exposed
    %   \cs{tiny} at the end gets expanded again immediately by the same
    %   \cs{edef} machinery, and that repeats without end until the input
    %   stack overflows.}
    %   {\cs{tl\_set:Nx}}{\cs{tl\_set:Nn}}{\cs{l\_\_osglistings\_extra\_tl}}
    %   {\cs{exp\_not:n}}{\code{#1}}
    unknown .code:n =
      {
        \tl_if_empty:NTF \l__osglistings_extra_tl
          { \tl_set:Nx \l__osglistings_extra_tl { \l_keys_key_str = { \exp_not:n {#1} } } }
          { \tl_put_right:Nx \l__osglistings_extra_tl { , \l_keys_key_str = { \exp_not:n {#1} } } }
      },
  }
\keys_set:nn { osglistings }
  {
    style = plain, title = {}, lang-icon = auto, link = {},
    link-display = titlebar, palette = {}, tag = true,
    tag-name = osglistings/code, cache = true,
    remote-check = manual, remote-refresh = false,
    marks = {}, marks-escape = §,
  }

% \subsection{\ldeen{Rahmenstile}{Box styles}}
% \ldeen{Sprachsymbol oben links, in allen drei Stilen gleich
%   positioniert.}{Language badge top-left, positioned the same way in
%   all three styles.}
\cs_new_protected:Npn \__osglistings_overlay_langicon:n #1
  {
    \tl_if_eq:NnF \l__osglistings_langicon_tl { none }
      {
        \node [anchor=north~west, inner~sep=2pt, font=\scriptsize]
          at (frame.north~west)
          {
            \tl_if_eq:NnTF \l__osglistings_langicon_tl { auto }
              { \__osglistings_lang_icon:n {#1} }
              { \l__osglistings_langicon_tl }
          };
      }
  }
% \ldeen{Bearbeiten-Symbol oben rechts, nur wenn ein Link gesetzt und
%   @1 gewählt ist.}{Edit-link icon top-right, only when a link is set
%   and @1 is selected.}{\code{link-display=titlebar}}
\cs_new_protected:Npn \__osglistings_overlay_link:
  {
    \tl_if_empty:NF \l__osglistings_link_tl
      {
        \tl_if_eq:NnT \l__osglistings_linkdisplay_tl { titlebar }
          {
            \node [anchor=north~east, inner~sep=2pt, font=\scriptsize]
              at (frame.north~east)
              { \href { \l__osglistings_link_tl } { \faEdit[regular] } };
          }
      }
  }
\cs_new_protected:Npn \__osglistings_below_link:
  {
    \tl_if_empty:NF \l__osglistings_link_tl
      {
        \tl_if_eq:NnT \l__osglistings_linkdisplay_tl { label }
          {
            \par\noindent\hfill
            {\scriptsize\href{\l__osglistings_link_tl}{\faLink~\ttfamily\l__osglistings_link_tl}}\par
          }
        % \ldeen{Anders als \code{link-display=label} steht der Link hier
        % nirgends als sichtbarer Text -- der QR-Code ist der einzige Weg
        % dorthin. Kein lokales \cs{qrset}\Marg{tag=...} nötig: der
        % zentrale \pkg{qrcode}-Tagging-Hook aus \File{osglecture.dtx}
        % taggt ihn per Default als \code{Figure} mit der URL als
        % Alt-Text.}{Unlike \code{link-display=label}, the link appears
        % nowhere as visible text here -- the QR code is the only way to
        % it. No local \cs{qrset}\Marg{tag=...} needed: the central
        % \pkg{qrcode} tagging hook in \File{osglecture.dtx} tags it as a
        % \code{Figure} by default, with the URL as alt text.}
        \tl_if_eq:NnT \l__osglistings_linkdisplay_tl { qrcode }
          {
            \par\noindent\hfill
            \qrcode*[height=1.5cm]{\l__osglistings_link_tl}\par
          }
      }
  }

\tcbset
  {
    osglistings/plain/.style =
      {
        enhanced, breakable,
        colframe = osglistings@color@frame, colback = osglistings@color@background,
        boxrule = 0.4pt, arc = 0pt, left=2mm, right=2mm, top=1mm, bottom=1mm,
        boxsep = .5ex, fontupper = \osglistingsmonofont,
      },
    osglistings/paper/.style =
      {
        enhanced, breakable = false,
        colframe = osglistings@color@background, colback = osglistings@color@background,
        boxrule = 0pt, left=4mm, right=3mm, top=3mm, bottom=3mm,
        boxsep = .5ex, fontupper = \osglistingsmonofont,
        frame~code =
          {
            \pgfmathsetseed{13}
            \fill[very~thin, osglistings@color@background, draw=black!30!white,
              decoration={random~steps,segment~length=3pt,amplitude=1pt,raise=2pt,
                pre=lineto,post=lineto,pre~length=2pt,post~length=2pt},decorate]
              (interior.north~west) -- ++(0,2pt) -| (interior.north~east);
            \fill[very~thin, osglistings@color@background, draw=black!30!white,
              decoration={random~steps,segment~length=3pt,amplitude=1pt,raise=-2pt,
                pre=lineto,post=lineto,pre~length=2pt,post~length=2pt},decorate]
              (interior.south~west) -- ++(0,-2pt) -| (interior.south~east);
          },
      },
    osglistings/editor/.style =
      {
        enhanced, breakable,
        colframe = osglistings@color@frame, colback = osglistings@color@background,
        coltitle = osglistings@color@titlebar-fg, colbacktitle = osglistings@color@titlebar-bg,
        title = \l__osglistings_title_tl, center~title, fonttitle = \scriptsize\ttfamily,
        boxrule = 0.4pt, left=2mm, right=2mm, top=1mm, bottom=1mm,
        boxsep = .5ex, fontupper = \osglistingsmonofont,
      },
  }

\cs_new_protected:Npn \__osglistings_box_begin:n #1
  {
    \tl_if_eq:NnTF \l__osglistings_style_tl { paper }
      {
        \begin{tcolorbox}[ osglistings/paper,
          overlay = { \__osglistings_overlay_langicon:n {#1} \__osglistings_overlay_link: } ]
      }
      {
        \tl_if_eq:NnTF \l__osglistings_style_tl { editor }
          {
            \begin{tcolorbox}[ osglistings/editor,
              overlay~first = { \__osglistings_overlay_langicon:n {#1} \__osglistings_overlay_link: },
              overlay~unbroken = { \__osglistings_overlay_langicon:n {#1} \__osglistings_overlay_link: } ]
          }
          {
            \begin{tcolorbox}[ osglistings/plain,
              overlay~first = { \__osglistings_overlay_langicon:n {#1} \__osglistings_overlay_link: },
              overlay~unbroken = { \__osglistings_overlay_langicon:n {#1} \__osglistings_overlay_link: } ]
          }
      }
  }
\cs_new_protected:Npn \__osglistings_box_end:
  { \end{tcolorbox} \__osglistings_below_link: }

% \subsection{\ldeen{Remote-Quellen und Lua-Anbindung}{Remote sources and Lua bridge}}
\ExplSyntaxOff
\newcommand\osglistingsluainit{%
  % A missing osglistings.lua (e.g.\ an unfinished local install) must
  % not turn ordinary package loading into a fatal error -- only actually
  % using a gist:/github: locator should fail, and only then.
  \ifdefined\directlua
    \directlua{local found; found, osglistings = pcall(require, "osglistings"); if not found then osglistings = nil end}%
  \fi
}
\newcommand\osglistingsluafetch[2]{%
  % \#1 = locator, \#2 = cache mode (true/false/refresh)
  \directlua{
    local bs = string.char(92)
    local locator = "\luaescapestring{#1}"
    if not osglistings then
      tex.error("osglistings: the Lua module osglistings.lua was not found; remote sources are unavailable")
    else
      local ok, result = pcall(osglistings.fetch, {
        locator = locator,
        cache_dir = "\luaescapestring{\jobname}.osglistings-cache",
        cache = "\luaescapestring{#2}",
      })
      if ok then
        local loc = osglistings.parse_locator(locator)
        tex.print(bs .. "gdef" .. bs .. "gosglistingsresolvedpath{" .. result .. "}")
        tex.print(bs .. "gdef" .. bs .. "gosglistingsresolvedkind{" .. loc.kind .. "}")
        tex.print(bs .. "gdef" .. bs .. "gosglistingsresolvedgistid{" .. (loc.id or "") .. "}")
      else
        tex.error("osglistings: could not fetch '" .. locator .. "': " .. tostring(result))
      end
    end
  }%
}
\newcommand\osglistingsluabuildlink[2]{%
  % \#1 = editor-url template (may contain \{CODE\}/\{GIST\_ID\}), \#2 = resolved content path
  \directlua{
    local bs = string.char(92)
    local url = "\luaescapestring{#1}"
    url = osglistings.replace_once(url, "{GIST_ID}", "\luaescapestring{\gosglistingsresolvedgistid}")
    if url:find("{CODE}", 1, true) then
      local f = io.open("\luaescapestring{#2}", "rb")
      local content = f and f:read("*a") or ""
      if f then f:close() end
      url = osglistings.replace_once(url, "{CODE}", osglistings.percent_encode(content))
    end
    tex.print(bs .. "gdef" .. bs .. "gosglistingsbuiltlink{" .. url .. "}")
  }%
}
\newcommand\osglistingsluacheckupdates[1]{%
  % \#1 = "true"/"false" -- refresh stale entries immediately?
  \directlua{
    local bs = string.char(92)
    if osglistings then
      local ok, report = pcall(osglistings.check_updates,
        "\luaescapestring{\jobname}.osglistings-cache", { refresh = ("\luaescapestring{#1}" == "true") })
      if ok then
        for _, e in ipairs(report) do
          local refreshed = {}
          for _, r in ipairs(e.refreshed) do refreshed[r] = true end
          for _, loc in ipairs(e.stale_files) do
            if not refreshed[loc] then
              tex.print(bs .. "osglistingsremotestalewarning{"
                .. loc .. "}{" .. e.type .. " " .. e.key .. "}")
            end
          end
        end
      end
    end
  }%
}
\newcommand\osglistingsluaapplymarks[3]{%
  % \#1 = source path, \#2 = marks spec, \#3 = escape char (used for both
  % the escapeinside open and close delimiter)
  \directlua{
    local bs = string.char(92)
    if not osglistings then
      tex.error("osglistings: the Lua module osglistings.lua was not found; marks are unavailable")
    else
      local ok, result = pcall(osglistings.inject_marks, {
        path = "\luaescapestring{#1}",
        marks = "\luaescapestring{#2}",
        escape_open = "\luaescapestring{#3}",
        escape_close = "\luaescapestring{#3}",
        cache_dir = "\luaescapestring{\jobname}.osglistings-cache",
      })
      if ok then
        tex.print(bs .. "gdef" .. bs .. "gosglistingsmarkedpath{" .. result .. "}")
      else
        tex.error("osglistings: could not apply marks to '" .. "\luaescapestring{#1}" .. "': " .. tostring(result))
      end
    end
  }%
}
\ExplSyntaxOn

\osglistingsluainit
\msg_new:nnn { osglistings } { luatex-required-remote }
  { Remote~sources~(gist:/github:)~require~LuaLaTeX. }
\msg_new:nnn { osglistings } { luatex-required-marks }
  { Character-level~marks~(the~marks~key)~require~LuaLaTeX. }
\msg_new:nnn { osglistings } { remote-stale }
  { Cached~source~'#1'~may~be~out~of~date~(#2).~Run~texlua~osglistings-check-updates.lua,~or~set~cache=refresh. }
\cs_new_protected:Npn \osglistingsremotestalewarning #1 #2
  { \msg_warning:nnnn { osglistings } { remote-stale } {#1} {#2} }
\bool_new:N \g__osglistings_was_remote_bool
\tl_new:N \g__osglistings_remote_kind_tl
\tl_new:N \g__osglistings_remote_gistid_tl
\cs_new_protected:Npn \__osglistings_resolve_remote:nN #1 #2
  {
    \sys_if_engine_luatex:TF
      {
        \osglistingsluafetch{#1}{\l__osglistings_cache_tl}
        \tl_set:Nx #2 { \gosglistingsresolvedpath }
        \bool_gset_true:N \g__osglistings_was_remote_bool
        \tl_gset:Nx \g__osglistings_remote_kind_tl { \gosglistingsresolvedkind }
        \tl_gset:Nx \g__osglistings_remote_gistid_tl { \gosglistingsresolvedgistid }
      }
      { \msg_fatal:nn { osglistings } { luatex-required-remote } }
  }
\cs_new_protected:Npn \__osglistings_input_path:nN #1 #2
  {
    \bool_gset_false:N \g__osglistings_was_remote_bool
    \regex_match:nnTF { \A (?: gist | github ) : } {#1}
      { \__osglistings_resolve_remote:nN {#1} #2 }
      { \tl_set:Nn #2 {#1} }
  }

% \subsection{\ldeen{Zeichengenaue Markierungen}{Character-level marks}}
% \ldeen{@1 legt an dieser Stelle einen namentlich adressierbaren,
%   unsichtbaren \pkg*{tikz}-Knoten ab (@2); ein späteres @3 kann von
%   oder zu ihm zeichnen. Die Auflösung passiert erst im zweiten
%   Kompilierlauf, wie bei jedem seitenübergreifenden \pkg*{tikz}-Overlay.}
%   {@1 drops an addressable, invisible \pkg*{tikz} node at this exact
%   spot (@2); a later @3 can then draw to or from it. Resolution only
%   happens on the second compile pass, like any cross-element
%   \pkg*{tikz} overlay.}{\cs{osglistingsmark}}{\code{remember~picture}}
%   {\code{tikzpicture}}
% \ldeen{@1 im Knoteninhalt gibt dem sonst punktförmigen Knoten die
%   Höhe/Tiefe eines Zeichens der aktuell aktiven (Mono-)Schrift, ohne
%   seine Breite zu verändern -- @2 verankert den Knoten trotzdem exakt
%   an der Einfügestelle (der Textbasislinie), so dass bestehende
%   Verweise auf \code{osgm-}\meta{name} als Punkt unverändert bleiben;
%   erst @3 nutzt die neu gewonnene Ausdehnung, um zwei solcher Knoten
%   zu einer Box zu verbinden.}{@1 in the node content gives the
%   otherwise point-sized node the height/depth of a character in the
%   currently active (mono) font, without changing its width -- @2
%   still pins the node exactly at the insertion point (the text
%   baseline), so that existing references to \code{osgm-}\meta{name}
%   as a point are unaffected; only @3 makes use of the newly gained
%   extent to connect two such nodes into a box.}
%   {\cs{vphantom\{Xg\}}}{\code{anchor=base}}{\cs{osglistingshighlight}}
\NewDocumentCommand \osglistingsmark { m }
  {
    \tikz[remember~picture,~overlay,~baseline=-0.5ex]
      \node[inner~sep=0pt,~anchor=base](osgm-#1){\vphantom{Xg}};
  }

% \ldeen{Verbindet zwei per @1 (oder per \code{marks}-Schlüssel mit
%   \code{around}) gesetzte Knoten \code{osgm-}\meta{name}\code{-begin}
%   und \code{osgm-}\meta{name}\code{-end} zu einer Markierungsbox --
%   dank der in @1 gemessenen Höhe/Tiefe ohne manuelle
%   \code{xshift}/\code{yshift}-Korrekturen. @2 reicht beliebige
%   weitere \pkg*{tikz}-Optionen durch, z.\,B. um Farbe oder
%   Eckenradius zu ändern; die Voreinstellung zeichnet eine
%   halbtransparente, gelbe, abgerundete Box hinter (dank Deckkraft
%   durchscheinendem) Text.}{Connects two nodes
%   \code{osgm-}\meta{name}\code{-begin} and
%   \code{osgm-}\meta{name}\code{-end} placed via @1 (or via the
%   \code{marks} key's \code{around}) into a highlight box -- thanks to
%   the height/depth measured in @1, with no manual
%   \code{xshift}/\code{yshift} corrections needed. @2 passes through
%   arbitrary further \pkg*{tikz} options, e.g.\ to change color or
%   corner radius; the default draws a semi-transparent, yellow,
%   rounded box behind (translucent, so the) text (shows through).}
%   {\cs{osglistingsmark}}{\Oarg{tikz~options}}
\NewDocumentCommand \osglistingshighlight { O{} m }
  {
    \osglistingsartifact
      {
        \tikz[remember~picture,~overlay]
          \node[fill=yellow,~fill~opacity=0.4,~draw=none,~rounded~corners=1pt,
                inner~sep=1pt,~fit=(osgm-#2-begin)(osgm-#2-end),~#1]{};
      }
  }

% \ldeen{Markiert #1 als \code{Artifact} (ist Tagging aktiv) -- für rein
%   dekoratives Beiwerk wie Pfeile oder Beschriftungen eines Overlays,
%   das assistive Technologien überspringen sollen, weil die eigentliche
%   Information bereits über einen \code{alt}-Text am Quelltext selbst
%   hängt (siehe \code{desc=} bei \code{around}). Ohne Tagging (oder
%   ohne @1) ein reines Durchreichen.}{Marks #1 as an \code{Artifact}
%   (when tagging is active) -- for purely decorative overlay chrome
%   like arrows or labels that assistive technology should skip,
%   because the real information already hangs off the source itself
%   via an \code{alt} text (see \code{desc=} on \code{around}). Without
%   tagging (or without @1) a plain passthrough.}
%   {\cs{DocumentMetadata}\Marg{tagging=on}}
\NewDocumentCommand \osglistingsartifact { m }
  {
    \cs_if_exist:NT \tagstructbegin { \tagstructbegin{ tag=Artifact } }
    #1
    \cs_if_exist:NT \tagstructend { \tagstructend }
  }

% \ldeen{Das Gegenstück zu @1 für den \code{desc=}-Fall von
%   \code{around}: statt eines bloßen Punktpaars @2/@3 öffnet @4 (bzw.
%   schließt @5) zusätzlich eine getaggte \code{Span}-Struktur mit dem
%   angegebenen Text als \code{alt}-Attribut -- assistive Technologien
%   lesen dann diesen Text statt (oder zusätzlich zu) dem markierten
%   Quelltext selbst. Ohne Tagging ein reines @2/@3-Punktpaar wie
%   sonst auch.}{The counterpart to @1 for \code{around}'s \code{desc=}
%   case: instead of a bare point pair @2/@3, @4 (resp.\ @5) also opens
%   (resp.\ closes) a tagged \code{Span} structure carrying the given
%   text as its \code{alt} attribute -- assistive technology then reads
%   that text instead of (or alongside) the marked-up source itself.
%   Without tagging, a plain @2/@3 point pair like any other.}
%   {\cs{osglistingsmark}}{\code{osgm-}\meta{name}\code{-begin}}
%   {\code{osgm-}\meta{name}\code{-end}}
%   {\cs{osglistingsmarkspanbegin}}{\cs{osglistingsmarkspanend}}
\NewDocumentCommand \osglistingsmarkspanbegin { m m }
  {
    \osglistingsmark{#1-begin}
    \cs_if_exist:NT \tagstructbegin
      {
        % \tagstructbegin alone only pushes an (empty) structure element;
        % \tagmcbegin is what actually attaches the following glyphs to
        % it -- the \alt attribute has to sit on the former, since that's
        % the one \tagmcbegin's own `alt` key would only stamp onto the
        % raw MC dictionary, not onto a real child structure element.
        \tagstructbegin{ tag=Span, alt={#2} }
        \tagmcbegin{ tag=Span }
      }
  }
\NewDocumentCommand \osglistingsmarkspanend { m }
  {
    \cs_if_exist:NT \tagstructbegin
      {
        \tagmcend
        \tagstructend
      }
    \osglistingsmark{#1-end}
  }

\cs_new_protected:Npn \__osglistings_apply_marks:N #1
  {
    \tl_if_empty:NF \l__osglistings_marks_tl
      {
        \sys_if_engine_luatex:TF
          {
            \osglistingsluaapplymarks { #1 } { \l__osglistings_marks_tl } { \l__osglistings_marksescape_tl }
            \tl_set:Nx #1 { \gosglistingsmarkedpath }
          }
          { \msg_fatal:nn { osglistings } { luatex-required-marks } }
      }
  }

\cs_new_protected:Npn \__osglistings_maybe_derive_link:n #1 % #1 = language
  {
    \bool_if:NT \g__osglistings_was_remote_bool
      {
        \tl_if_empty:NT \l__osglistings_link_tl
          {
            \tl_clear:N \l__osglistings_tmp_tl
            \tl_if_eq:NnT \g__osglistings_remote_kind_tl { gist }
              { \prop_get:NnNTF \g__osglistings_lang_gisturl_prop {#1} \l__osglistings_tmp_tl { } { } }
            \tl_if_empty:NT \l__osglistings_tmp_tl
              { \prop_get:NnNTF \g__osglistings_lang_url_prop {#1} \l__osglistings_tmp_tl { } { } }
            \tl_if_empty:NF \l__osglistings_tmp_tl
              {
                \sys_if_engine_luatex:T
                  {
                    \osglistingsluabuildlink { \l__osglistings_tmp_tl } { \l__osglistings_input_path_tl }
                    \tl_set:Nx \l__osglistings_link_tl { \gosglistingsbuiltlink }
                  }
              }
          }
      }
  }
\cs_new_protected:Npn \__osglistings_check_updates_now:
  {
    \sys_if_engine_luatex:T
      { \bool_if:NTF \l__osglistings_remoterefresh_bool { \osglistingsluacheckupdates{true} } { \osglistingsluacheckupdates{false} } }
  }
\AtEndDocument{
  \tl_if_eq:NnT \l__osglistings_remotecheck_tl { compile }
    { \__osglistings_check_updates_now: }
}

% \subsection{\ldeen{Öffentliche Schnittstelle}{Public interface}}
\cs_new_protected:Npn \__osglistings_setup:n #1
  {
    \tl_clear:N \l__osglistings_extra_tl
    \keys_set:nn { osglistings } {#1}
    \tl_if_empty:NF \l__osglistings_palette_tl
      { \UseOsgListingsPalette { \l__osglistings_palette_tl } }
    \tl_if_empty:NF \l__osglistings_marks_tl
      {
        \tl_if_empty:NTF \l__osglistings_extra_tl
          {
            \tl_set:Nx \l__osglistings_extra_tl
              { escapeinside = \l__osglistings_marksescape_tl \l__osglistings_marksescape_tl }
          }
          {
            \tl_put_right:Nx \l__osglistings_extra_tl
              { , escapeinside = \l__osglistings_marksescape_tl \l__osglistings_marksescape_tl }
          }
      }
  }

% \ldeen{@1 muss ausgeführt werden, \emph{bevor} die umschließende
%   @2-Box geöffnet wird -- @1 merkt sich die gerade aktive Umgebung
%   (\code{osglisting}) als Endemarke für die spätere Verbatim-Suche;
%   würde @2 zuerst geöffnet, würde stattdessen \emph{diese} zur (im
%   Quelltext nie erscheinenden) Endemarke.}{@1 has to run \emph{before}
%   the enclosing @2 box opens -- @1 records the currently active
%   environment (\code{osglisting}) as the end marker for the later
%   verbatim search; opening @2 first would make \emph{that} the end
%   marker instead, which never appears in the source.}
%   {\cs{VerbatimEnvironment}}{\pkg*{tcolorbox}}
\NewDocumentEnvironment { osglisting } { O{} m }
  {
    \group_begin:
    \VerbatimEnvironment
    \__osglistings_setup:n {#1}
    \__osglistings_box_begin:n {#2}
    \bool_if:NT \l__osglistings_tag_bool
      { \__osglistings_tag_struct_begin:n { \UseStructureName{ \l__osglistings_tagname_tl } } }
    % \ldeen{@1 statt @2 -- @3 nimmt sein Argument als Kontrollsequenz
    %   entgegen und würde diese sonst unexpandiert (also den
    %   Variablennamen statt ihres Inhalts) an \pkg*{pgfkeys}
    %   weiterreichen.}{@1 instead of @2 -- @3 takes its argument as a
    %   bare control sequence and would otherwise forward it
    %   unexpanded (i.e.\ the variable's name rather than its content)
    %   to \pkg*{pgfkeys}.}{\cs{exp\_args:No}}{a~plain~brace~group}
    %   {\cs{setminted}}
    \exp_args:No \setminted { \l__osglistings_extra_tl }
    \begin{minted}{#2}
  }
  {
    \end{minted}
    \bool_if:NT \l__osglistings_tag_bool { \__osglistings_tag_struct_end: }
    \__osglistings_box_end:
    \group_end:
  }

\tl_new:N \l__osglistings_input_path_tl
\NewDocumentCommand \osglistinginput { O{} m m }
  {
    \group_begin:
    \__osglistings_setup:n {#1}
    \__osglistings_input_path:nN {#3} \l__osglistings_input_path_tl
    \__osglistings_maybe_derive_link:n {#2}
    \__osglistings_apply_marks:N \l__osglistings_input_path_tl
    \__osglistings_box_begin:n {#2}
    \bool_if:NT \l__osglistings_tag_bool
      { \__osglistings_tag_struct_begin:n { \UseStructureName{ \l__osglistings_tagname_tl } } }
    % \ldeen{@1 statt einer eckigen Klammer mit @2 darin -- @2 wäre
    %   dort nur ein unexpandiertes Token; @3 setzt dieselben
    %   Optionen global (innerhalb der \cs{group\_begin:}/\cs{group\_end:}
    %   dieses Aufrufs also effektiv lokal) und wirkt genauso auf das
    %   folgende, klammerlose @4.}{@1 instead of a bracket option
    %   holding @2 -- @2 there would be just an unexpanded token; @3
    %   sets the same options globally (effectively local within this
    %   call's \cs{group\_begin:}/\cs{group\_end:}) and applies just as
    %   well to the following, bracket-less @4.}
    %   {\cs{exp\_args:No}\cs{setminted}}{\cs{l\_\_osglistings\_extra\_tl}}
    %   {\cs{setminted}}{\cs{inputminted}}
    \exp_args:No \setminted { \l__osglistings_extra_tl }
    \inputminted {#2} { \l__osglistings_input_path_tl }
    \bool_if:NT \l__osglistings_tag_bool { \__osglistings_tag_struct_end: }
    \__osglistings_box_end:
    \group_end:
  }

\NewDocumentCommand \OsgListingsSetup { m } { \keys_set:nn { osglistings } {#1} }

\ExplSyntaxOff
\makeatother
%</package>
