Skip to content

Instantly share code, notes, and snippets.

@zr-tex8r
Last active August 29, 2015 14:06
Show Gist options
  • Save zr-tex8r/6c1eabe4f8cf1a012d23 to your computer and use it in GitHub Desktop.
Save zr-tex8r/6c1eabe4f8cf1a012d23 to your computer and use it in GitHub Desktop.
LaTeX: An extension to easylist, where an arbitrary symbol can be used as list starters
%
% tceasylist.sty
%
%% package declaratoin
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{tceasylist}[2014/09/15 v0.2]
%% preparations
\def\tcel@pkgname{tceasylist}
\def\tcel@error{\PackageError\tcel@pkgname}
\def\tcel@warn{\PackageWarningNoLine\tcel@pkgname}
\newif\iftcel@ok
\newif\iftcel@unicode
\providecommand*\bxDebug[1]{}
%--------------------------------------- helpers
%% unique toens
\def\tcel@mt{\tcel@mt@}
\def\tcel@mark{\tcel@mark@}
\def\tcel@end{\tcel@end@}
%% Set switch tcel@unicode, which means whether Unicode codespace
% is available.
\begingroup
\def\tcel@check#1#2{%
\edef\tcel@tmpa{\string#1}%
\edef\tcel@tmpb{\meaning#1}%
\ifx\tcel@tmpa\tcel@tmpb #2\fi
}
\tcel@check\XeTeXversion{\global\tcel@unicodetrue}
\tcel@check\luatexversion{\global\tcel@unicodetrue}
\endgroup
%% \tcel@bs : a backslash
\edef\tcel@bs{\expandafter\@gobble\string\\}
%% \tcel@detokenize\CS
% Update a token-list macro \CS with the detokenized list.
\def\tcel@detokenize#1{%
\expandafter\tcel@detokenize@a\meaning#1\tcel@end#1%
}
\def\tcel@detokenize@a#1->#2\tcel@end#3{\def#3{#2}}
%% \tcel@check@single{<arg>}\CSa\CSb
% If <arg> is a single char then call \CSa with that char;
% if <arg> is a single cs then call \CSb with that cs;
% otherwise do nothing.
\def\tcel@check@single#1#2#3{%
\def\tcel@tmpa{#1}%
\let\tcel@next\tcel@tmpa \tcel@detokenize\tcel@next
\def\tcel@tmpb{#2}%
\def\tcel@tmpc{#3}%
\expandafter\tcel@check@single@a\tcel@next\relax\relax\tcel@end
}
\def\tcel@check@single@a#1#2#3\tcel@end{%
\if#1\tcel@bs % starting with a cs
\expandafter\tcel@check@single@b\tcel@tmpa\tcel@end
\else\ifx#1\relax % no-op
\else\ifx#2\relax % single char
\tcel@tmpb#1%
\fi\fi\fi
}
\def\tcel@check@single@b#1#2\tcel@end{%
\ifx\tcel@mt#2\tcel@mt % single cs
\tcel@tmpc#1%
\fi
}
%% \tcel@check@hexnum{<arg>}
% Parse <arg> as a hex number and set the result to \@tempcnta;
% if failed the result is negative.
\def\tcel@check@hexnum#1{%
\def\tcel@tmpa{#1}\tcel@detokenize\tcel@tmpa
\ifx\tcel@tmpa\@empty\else
\edef\tcel@next{%
\uppercase{\noexpand\tcel@check@hexnum@a{\tcel@tmpa}}}%
\tcel@next
\fi
}
\def\tcel@check@hexnum@a#1{%
\afterassignment\tcel@check@hexnum@b
\@tempcnta\string"0#1\relax
}
\def\tcel@check@hexnum@b#1\relax{%
\ifx\tcel@mt#1\tcel@mt\else
\@tempcnta\m@ne
\fi
}
%% \tcel@get@char@tokens{<number>}
% Make char token with the given code value and assign them
% to token-list macros; \tcel@char@A holds an active char
% and \tcel@char@N holds a non-active char.
\def\tcel@get@char@tokens#1{%
\lccode`\~=#1\relax \lccode`\:=#1\relax
\lowercase{\def\tcel@char@A{~}\def\tcel@char@N{:}}%
}
%--------------------------------------- option handling
%% \tcel@promise
% Process that will be done at the end of the package.
\let\tcel@promise\relax
%% \tcel@check@istart{<string>}
% Check an option string, and if it is of the form 'item=X' then
% update \tcel@promise with an invocation of \SetItemStarter.
\def\tcel@check@istart#1{%
\tcel@okfalse
\tcel@check@istart@a#1\tcel@mark item=\tcel@mark\tcel@end
}
\def\tcel@check@istart@a#1item=#2\tcel@mark#3\tcel@end{%
\ifx\tcel@mt#1\tcel@mt
\tcel@check@single{#2}\tcel@check@istart@b\tcel@check@istart@b
\iftcel@ok\else
\tcel@error
{Option value of 'item' must be a single character}%
\@ehc
\tcel@oktrue % not pass the option anyway
\fi
\fi
}
\def\tcel@check@istart@b#1{%
\tcel@oktrue
\def\tcel@promise{\SetItemStarter{#1}}%
}
%% option handler
\DeclareOption{snowman}{%
\def\tcel@promise{\SetItemStarter{2603}}%
}
\DeclareOption*{%
\expandafter\tcel@decl@opt@a\expandafter{\CurrentOption}%
}
\def\tcel@decl@opt@a#1{%
\tcel@check@istart{#1}%
\iftcel@ok\else
\PassOptionsToPackage{#1}{easylist}%
\fi
}
%% process it
\ProcessOptions\relax
%% load the package
\RequirePackage{easylist}
%--------------------------------------- patch to easylist
%% Check consistency
\def\tcel@next{%
\endgroup\par}
\ifx\tcel@next\endeasylist\else
\tcel@error
{Cannot patch to easylist code}
{Package loading is aborted.}
\expandafter\endinput\fi\relax
%% Redefine '\easylist'
\tcel@okfalse
\begingroup
\def\tcel@patch{%
\expandafter\tcel@patch@a\easylist\tcel@mark
\begingroup\tcel@mark\tcel@end
}
\def\tcel@patch@a#1\begingroup#2\tcel@mark#3\tcel@end{%
\ifx\tcel@mt#3\tcel@mt\else
\aftergroup\tcel@oktrue
\gdef\tcel@reset@item@starter{#1}%
\gdef\easylist{%
\let\SetItemStarter\tcel@invalid@SetItemStarter
\tcel@reset@item@starter
\begingroup #2}%
\fi
}
\tcel@patch
\endgroup
\iftcel@ok\else
\tcel@error
{Cannot patch to easylist code}
{Package loading is aborted.}
\expandafter\endinput\fi\relax
%% Patch to '\elCreateItem' (not mandatory)
\ifx\detokenize\@undefined\else
\begingroup
% messages after replacement
\def\tcel@msga{%
Too many item-start tokens.}
\def\tcel@msgb{%
You can't use more than \the\el@CounterTotal\space
item-start tokens}
% patch process
\edef\tcel@patch{%
\noexpand\tcel@patch@a
\detokenize{Too many:.:You can't:'s:}%
{\detokenize\expandafter{\elCreateItem}}}
\def\tcel@patch@a#1:#2:#3:#4:#5{%
\def\tcel@next##1#1##2#2##3#3##4#4##5\tcel@mark{%
\tcel@patch@c{##1\tcel@msga##3\tcel@msgb##5}}%
\tcel@next#5\tcel@mark#1#2#3#4\tcel@mark\tcel@end}
\def\tcel@patch@c#1#2\tcel@end{%
\ifx\tcel@mt#2\tcel@mt
\tcel@warn{Cannot apply patch}%
\else
\edef\tcel@next{\noexpand\tcel@patch@d{#1}}%
\tcel@next
\fi}
\def\tcel@patch@d#1{%
\scantokens{\toks@{#1}}%
\edef\elCreateItem{\the\toks@}}
\tcel@patch
\endgroup
\fi
%%<+>\ItemStarter
% The item-starter function, which is normally assigned to
% 'the active char' in easylist.
\newcommand*\ItemStarter{%
\futurelet\elNextToken\elCreateItem
}
%%<+>\ItemStarterUpdated\CS
% Must be called when the specification of item-starter token
% is updated, where the argument \CS is a macro that activates
% the token specified.
\newcommand*\ItemStarterUpdated[1]{%
\global\let\tcel@reset@item@starter#1%
}
%% \tcel@invalid@SetItemStarter
% In easylist environments, \SetItemStarter is overwritten by
% this macro.
\def\tcel@invalid@SetItemStarter#1{%
\tcel@error
{Cannot change item starter here}
\@eha
}
%--------------------------------------- all done
%% \tcel@g@char : codepoint of item-starter char
\let\tcel@g@char\relax
%% \tcel@char : for local use
\let\tcel@char\relax
%% \tcel@cmd : item-starter command
\let\tcel@g@cmd\relax
%%<*>\SetItemStarter{<some>}
\newcommand*\SetItemStarter[1]{%
\tcel@okfalse \let\tcel@char\relax
\tcel@check@single{#1}\tcel@set@is@char\tcel@set@is@cmd
\iftcel@ok\else
\tcel@check@hexnum{#1}%
\ifnum\@tempcnta<\z@
\tcel@error
{Invalid argument given to \string\SetItemStarter}%
\@ehc
\else\ifnum
\iftcel@unicode "10FFFF \else "FF \fi
<\@tempcnta
\tcel@error
{The code value given is too big}%
\@ehc
\else
\chardef\tcel@char\@tempcnta
\fi\fi
\fi
\ifx\tcel@char\relax\else
\tcel@okfalse
\ifnum`\#=\tcel@char \tcel@oktrue \fi
\ifnum`\@=\tcel@char \tcel@oktrue \fi
\ifnum`\&=\tcel@char \tcel@oktrue \fi
\ifnum\tcel@char>"7F
\ifnum\catcode\tcel@char=11 \tcel@oktrue \fi
\ifnum\catcode\tcel@char=12 \tcel@oktrue \fi
\fi
\tcel@get@char@tokens\tcel@char
\iftcel@ok
\expandafter\tcel@set@is@char@a\tcel@char@A
\else
\tcel@error
{Cannot use '\tcel@char@N' as item starter}%
\@eha
\fi
\fi
}
\def\tcel@set@is@char#1{%
\chardef\tcel@char`#1\relax
\tcel@oktrue
}
\def\tcel@set@is@char@a#1{%
\global\let\tcel@g@char\tcel@char
\global\let\Activate\tcel@Activate@char
\global\let\Deactivate\tcel@Deactivate@char
\def\tcel@next{%
\catcode\tcel@g@char\active
\let#1\ItemStarter}%
\ItemStarterUpdated\tcel@next
}
\def\tcel@set@is@cmd#1{%
\ifx#1\tcel@ltx@star
\gdef\tcel@g@cmd{#1}%
\global\let\Activate\relax
\global\let\Deactivate\relax
\def\tcel@next{\let#1\tcel@switch@star}%
\ItemStarterUpdated\tcel@next
\else
\@ifdefinable{#1}{%
\gdef\tcel@g@cmd{#1}%
\global\let\Activate\relax
\global\let\Deactivate\relax
\def\tcel@next{\let#1\ItemStarter}%
\ItemStarterUpdated\tcel@next
}%
\fi
\tcel@oktrue
}
\def\tcel@ltx@star{%
\discretionary{\thinspace\the\textfont2\char2}{}{}%
}
\def\tcel@switch@star{%
\ifmmode \tcel@ltx@star \expandafter\@gobble \fi
\ItemStarter
}
%% \tcel@Activate@char
\let\tcel@deactivate\relax
\def\tcel@Activate@char{%
\ifx\tcel@deactivate\relax
\edef\tcel@deactivate{%
\catcode\tcel@g@char=\the\catcode\tcel@g@char\relax}%
\fi
\catcode\tcel@g@char\active
}
\def\tcel@Deactivate@char{%
\tcel@deactivate
\let\tcel@deactivate\relax
}
%--------------------------------------- all done
%% do the promise
\tcel@promise
%% done
\endinput
%% EOF
% XeLaTeX document, encoded in UTF-8
\documentclass[a4paper]{article}
\usepackage[snowman]{tceasylist}% '☃' starts a list item
\begin{document}
\begin{easylist}
☃ Blah.
☃ Blahblah.
☃☃ Blah, blahblah blah blah.
☃☃☃ Blah.
☃☃ Blahblah, `blah' blah?
☃☃☃ Blahblahblah\ldots.
☃ Blah, \emph{blahblah blah} blah blahblah.
\end{easylist}
\end{document}
% XeLaTeX document, encoded in UTF-8
\documentclass[a4paper]{article}
\usepackage[item=☺]{tceasylist}% '☺' starts a list item
\begin{document}
\begin{easylist}
☺ Blah.
☺ Blahblah.
☺☺ Blah, blahblah blah blah.
☺☺☺ Blah.
☺☺ Blahblah, `blah' blah?
☺☺☺ Blahblahblah\ldots.
☺ Blah, \emph{blahblah blah} blah blahblah.
\end{easylist}
\end{document}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment