%% setbuilder.sty
%% Smart set-builder notation for LaTeX.
%%
%%   \set{x \in \mathbb{R} \given x^2 < 2}
%%
%% produces { x ∈ ℝ | x² < 2 } with correctly spaced and, on demand,
%% correctly scaled braces and separator bar. No more
%% \left\{ ... \,\middle|\, ... \right\}.
%%
%% Commands:
%%   \set{...}          canonical command: braces and bar at normal size
%%   \set*{...}         braces and bar auto-scale (\left/\middle/\right)
%%   \set[\Big]{...}    braces and bar at a fixed size
%%   \given             the separator, usable only inside \set
%%   \suchthat          alias for \given
%%
%% Package options (key-value):
%%   bar     (default)  separator is a vertical bar  { x | ... }
%%   colon              separator is a colon         { x : ... }
%%   synonym=<name>     additionally define \<name> as a full copy of
%%                      \set (with all its * and [\Big] variants).
%%                      If \set is already taken by another package,
%%                      setbuilder leaves it untouched, and the synonym
%%                      becomes the way to access the functionality:
%%                        \usepackage[synonym=mkset]{setbuilder}
%%
%% Requires LaTeX kernel 2022-06-01 or newer (for key-value options).
%%
%% Copyright (c) 2026 Vseslav Sekorin
%% License: MIT (see LICENSE file or https://opensource.org/licenses/MIT)
%%
\NeedsTeXFormat{LaTeX2e}[2022/06/01]
\ProvidesPackage{setbuilder}[2026/07/19 v0.1 Smart set-builder notation]

\RequirePackage{mathtools}

%% ---------------------------------------------------------------
%% Key-value options
%% ---------------------------------------------------------------
\newif\ifsetbuilder@colon
\newcommand*\setbuilder@synonym{}

\DeclareKeys[setbuilder]{
  bar.code     = {\setbuilder@colonfalse},
  colon.code   = {\setbuilder@colontrue},
  synonym.store = \setbuilder@synonym,
  synonym.usage = load,
}
\ProcessKeyOptions[setbuilder]

%% ---------------------------------------------------------------
%% The separator
%% ---------------------------------------------------------------
%% \setbuilder@sep[<size>] typesets the separator with correct spacing:
%%   - \nonscript\: adds a medium-thin space that vanishes in sub/superscripts;
%%   - \allowbreak permits a line break after the bar in inline formulas;
%%   - \mathopen{} makes what follows behave as if freshly opened, so
%%     unary minus etc. after the bar are spaced correctly.
%% The bar scales with the braces (it receives \delimsize / \middle);
%% a colon never needs to scale, so the size argument is ignored there.
\ifsetbuilder@colon
  \newcommand\setbuilder@sep[1][]{%
    \nonscript\:{\mathord{:}}\allowbreak\nonscript\:\mathopen{}}
\else
  \newcommand\setbuilder@sep[1][]{%
    \nonscript\:#1\vert\allowbreak\nonscript\:\mathopen{}}
\fi

%% Outside \set the separator commands raise a friendly error.
\newcommand\given{%
  \PackageError{setbuilder}%
    {\string\given\space may only be used inside \string\set}%
    {Write e.g. \string\set{x \string\given\space x>0}.}}
\newcommand\suchthat{%
  \PackageError{setbuilder}%
    {\string\suchthat\space may only be used inside \string\set}%
    {Write e.g. \string\set{x \string\suchthat\space x>0}.}}

%% ---------------------------------------------------------------
%% The command itself (internal master copy)
%% ---------------------------------------------------------------
%% \DeclarePairedDelimiterX gives us the *, [\Big] and plain variants
%% for free; inside the body we locally redefine \given so that it
%% knows the current delimiter size (\delimsize is \middle in the
%% starred version and the chosen \big/\Big/... otherwise).
\DeclarePairedDelimiterX\setbuilder@cmd[1]\{\}{%
  \renewcommand\given{\setbuilder@sep[\delimsize]}%
  \let\suchthat\given
  #1}

%% ---------------------------------------------------------------
%% Canonical name \set + optional synonym
%% ---------------------------------------------------------------
%% \NewCommandCopy makes a faithful copy of the robust paired-delimiter
%% command, so the copy supports \set, \set* and \set[\Big] alike.
\ifdefined\set
  \ifx\setbuilder@synonym\@empty
    \PackageWarning{setbuilder}{%
      \string\set\space is already defined by another package and is\MessageBreak
      left untouched. Load setbuilder with a synonym to access\MessageBreak
      its functionality, e.g.\MessageBreak
      \space\space\string\usepackage[synonym=mkset]{setbuilder}}%
  \else
    \PackageInfo{setbuilder}{%
      \string\set\space is already defined elsewhere and is left\MessageBreak
      untouched; using the requested synonym instead}%
  \fi
\else
  \NewCommandCopy\set\setbuilder@cmd
\fi

\ifx\setbuilder@synonym\@empty\else
  \ifcsname \setbuilder@synonym\endcsname
    \PackageError{setbuilder}%
      {Cannot create synonym \expandafter\string
       \csname\setbuilder@synonym\endcsname: this name is already
       defined}%
      {Choose another name in the synonym=... option.}%
  \else
    \expandafter\NewCommandCopy
      \csname\setbuilder@synonym\endcsname \setbuilder@cmd
  \fi
\fi

\endinput
