1

Is it possible for an environment to detect which sectioning level it is at?

I'm in the process of creating a package which defines a numbered environment of question/answer pairs, and I want to provide the option to customise which section level the numbers should be reset at, as well as whether the question/answer pairs should be included in the table of contents. The idea is for these Q/A pairs to form a parallel set of sectioning-like environments that won't interfere with the normal sectioning commands.

EDIT Perhaps a bit more context would be useful. I've been using a macro not unlike the one reproduced below to create question/answer environments in my assignments that would function similarly to sections, but have their own counters. This is such useful code that I'm trying to split it out into a package I can reuse, but I need to figure out a way to make the question environment figure out whether it's inside a section in order to set its numbering correctly as well as optionally put it in the table of contents and PDF outline/index.

% vim: ft=tex
% arara: lualatex
% arara: lualatex

\documentclass{scrartcl}

\usepackage{etoolbox}

\usepackage{fontspec}
\defaultfontfeatures{Ligatures=TeX}

\newlength{\questionbeforeskip}
\newlength{\questionafterskip}
\setlength{\questionbeforeskip}{1.0ex plus -1ex minus -0.25ex}
\setlength{\questionafterskip}{1ex plus 0.25ex}
\newlength{\answerskip}
\setlength{\answerskip}{0.25ex plus -0.125ex minus -0.125ex}

\addtotoclist{loq}
\newcommand{\listofloqname}{List of Questions}
\newcommand{\listofquestions}{\listoftoc{loq}}
\setuptoc{loq}{leveldown}

\newcounter{question}[section]

\newcommand{\questionautorefname}{question}
\newenvironment{question}
{
  \refstepcounter{question}%
  \addxcontentsline{loq}{subsection}{Question~\thequestion}
  \pdfbookmark[2]{Question~\thequestion}{question:\thesection.\thequestion}
  {{\par\vspace\questionbeforeskip \large Question~\thequestion.}\par}
  \begin{itshape}
}
{\end{itshape}\par\vspace\questionafterskip}

\usepackage[inline]{enumitem}
\setlist[enumerate]{label=(\alph*)}

% Sub-questions are represented as an enumerate
\newlist{subquestions}{enumerate}{3}
\setlist[subquestions]{leftmargin=*}
\setlist[subquestions,1]{label=\textup{\textbf{(\alph*)}}}
% Inline subquestions
\newlist{subquestions*}{enumerate*}{3}
\setlist[subquestions*]{itemjoin*={{, and }}}
\setlist[subquestions*,1]{label=\textup{\textbf{(\alph*)}}}

\newcommand{\qitem}[1]{\item{\textit{#1}}}
\newcommand{\qitemp}[1]{\qitem{#1}\\[0.5\parskip]}
\newcommand{\qitemq}[1]{\qitem{#1}\quad}

%% Penalties
\hyphenpenalty=2500
\tolerance=500
\widowpenalty=1500
\clubpenalty=1500

\usepackage{hyperref}
\hypersetup{
  colorlinks=false,
  hidelinks}
\usepackage[all]{hypcap}
\usepackage[noabbrev,capitalise]{cleveref}
\labelcrefformat{subfigure}{\textbf{(#2#1#3)}}

\begin{document}


\section{Osmosis and kinetics}

\begin{question}
  What is reverse osmosis? Describe a process which involves the use of reverse
  osmosis (use a diagram in your answer).
\end{question}

Answer blah

\begin{question}
  What are colloidal particles? What are the main processes that are used to
  remove colloidal particles from aqueous solutions?
\end{question}

Some other answer

\section{Organic chemistry / Natural gas processing}

\begin{question}
What are four ways of representing organic molecules such as alkanes?
\end{question}

\begin{question}
  Draw structural formula for the following compounds:
  \begin{subquestions}
    \item 3-methylnonane
    \item 3-ethylheptane
    \item 2-methyloctane
  \end{subquestions}
\end{question}

\begin{subquestions}
  \item Answer 1
  \item Answer 2
  \item Answer 3
\end{subquestions}

\end{document}

So far what I've managed to extract into a package is the following. The PGFKeys switch to choose which section level to reset the question numbering is a bit clumsy, so I will probably replace it with something simpler (maybe xkeyval?). I'm using this exercise as an attempt to learn the ropes at creating a functional package that might be useful to someone other than myself. To use this package in the previous example, you'd have to load it with \usepackage{questions} and remove the duplicated code.

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{questions}
  [2017/11/17 v0.1 Assignment Questions]
\RequirePackage{etoolbox}
\RequirePackage{pgfopts}
\RequirePackage{chngcntr}
\RequirePackage[inline]{enumitem}
% Question counter
\newcounter{question}
% Define option keys with PGFKeys
\pgfkeys{/questions/.cd}
\pgfkeys{/questions/reset/.cd,
  .is choice,
  part/.code={%
    \counterwithin*{question}{part}
  },
  chapter/.code={%
    \counterwithin*{question}{chapter}
  },
  section/.code={%
    \counterwithin*{question}{section}
  },
  subsection/.code={%
    \counterwithin*{question}{subsection}
  },
  subsubsection/.code={%
    \counterwithin*{question}{subsubsection}
  },
  paragraph/.code={%
    \counterwithin*{question}{paragraph}
  },
  subparagraph/.code={%
    \counterwithin*{question}{subparagraph}
  },
  none/.code={%
    \counterwithout*{question}{section}
  },
}
% \pgfkeys{/questions/reset/.default = paragraph}

\newcommand{\questions@setdefaults}{
  \pgfkeys{/questions/.cd,
    reset=section,
  }
}

\newcommand{\questionset}[1]{\pgfkeys{/questions/.cd,#1}}
\questions@setdefaults
\ProcessPgfOptions{/questions}

\newlength{\questionbeforeskip}
\newlength{\questionafterskip}
\setlength{\questionbeforeskip}{1.0ex plus -1ex minus -0.25ex}
\setlength{\questionafterskip}{1ex plus 0.25ex}
\newlength{\answerskip}
\setlength{\answerskip}{0.25ex plus -0.125ex minus -0.125ex}

\addtotoclist{loq}
% \newcommand{\listofloqname}{List of Questions}
% \newcommand{\listofquestions}{\listoftoc{loq}}
% \setuptoc{loq}{sectionatlist}


\newenvironment{questionenv}
{
  \refstepcounter{question}%
  % \addxcontentsline{loq}{subsection}{Question~\thequestion}
  % \pdfbookmark{Question~\thequestion}{question:\thequestion}
  {{\par\vspace\questionbeforeskip Question~\thequestion.}\par}
  \begin{itshape}
}
{\end{itshape}\par\vspace\questionafterskip}

\newlist{subquestions}{enumerate}{3}
\setlist[subquestions]{leftmargin=*}
\setlist[subquestions,1]{label=\textup{\textbf{(\alph*)}}}
% Inline subquestions
\newlist{subquestions*}{enumerate*}{3}
\setlist[subquestions*]{itemjoin*={{, and }}}
\setlist[subquestions*,1]{label=\textup{\textbf{(\alph*)}}}

\endinput

The next MWE illustrates how the questions package is intended to be used. Setting the restart option changes where the questions are numbered from (it defaults to section).

% vim: ft=tex

% arara: lualatex
% arara: lualatex

\documentclass{scrartcl}

%% KOMA options
\KOMAoption{cleardoublepage}{empty}
\KOMAoption{DIV}{10}
\KOMAoption{draft}{false}
\KOMAoption{fontsize}{10pt}
\KOMAoption{headings}{small}
\KOMAoption{paper}{a4}
\KOMAoption{parskip}{half}
\KOMAoption{twoside}{false}
\KOMAoption{captions}{signature}
\KOMAoption{titlepage}{false}
\KOMAoption{abstract}{false}

\usepackage{etoolbox}

\usepackage{fontspec}
\defaultfontfeatures{Ligatures=TeX}

\usepackage[reset=none]{questions}

%% Penalties
\hyphenpenalty=2500
\tolerance=500
\widowpenalty=1500
\clubpenalty=1500

\usepackage{hyperref}
\hypersetup{
  colorlinks=false,
  hidelinks}
\usepackage[all]{hypcap}
\usepackage[noabbrev,capitalise]{cleveref}
\labelcrefformat{subfigure}{\textbf{(#2#1#3)}}

\begin{document}


\section{A section}

\begin{questionenv}
The question in a section
\end{questionenv}

\subsection{A subsection}

\begin{questionenv}
The question in a subsection
\end{questionenv}

\begin{questionenv}
Another question in a subsection
\end{questionenv}

\subsubsection{A subsubsection}

\begin{questionenv}
The question in a subsubsection
\end{questionenv}

\begin{questionenv}
Another question in a subsubsection
\end{questionenv}

\subsubsection{A subsubsection}

\begin{questionenv}
The question in a subsubsection
\end{questionenv}

\begin{questionenv}
Another question in a subsubsection
\end{questionenv}

\subsection{Another subsection}

\begin{questionenv}
Another question in a subsection
\end{questionenv}

\begin{questionenv}
What is this question?
\end{questionenv}

\section{Another section}

\begin{questionenv}
Another question in another section
\end{questionenv}

\begin{questionenv}
Another question in another section
\end{questionenv}

\subsection{A subsection}

\begin{questionenv}
The question in a subsection
\end{questionenv}

\begin{questionenv}
Another question in a subsection
\end{questionenv}

\begin{questionenv}
Another question in a subsection
\end{questionenv}

\subsubsection{A subsubsection}

\begin{questionenv}
The question in a subsubsection
\end{questionenv}

\begin{questionenv}
Another question in a subsubsection
\end{questionenv}

\subsubsection{A subsubsection}

\begin{questionenv}
The question in a subsubsection
\end{questionenv}

\begin{questionenv}
Another question in a subsubsection
\end{questionenv}

\end{document}
  • Isn't this solved by using a counter that has the corresponding reset level? – user31729 Nov 17 '17 at 8:25
  • I'm part-way there using chngcntr and \counterwithin in a PGF-keys .is choice. What I'm stuck on is trying to set up a means to (optionally) add the Q/A pairs to the index / table of contents, such that they appear one level below their reset level. – Robbie Nov 17 '17 at 9:26
  • Please make your example complete so we don't have to guess. – daleif Nov 17 '17 at 10:45
  • Can you please expand the code snippet that you have posted to a full minimal working example. A MWE should compile and be as small as possible to demonstrate your problem. it's much easier to help you if we have full working code to start from. For this question the document class that you are using is crucial. In the comments you mention using pgfkeys but this is nowhere to be seen in your MWE. – Andrew Nov 17 '17 at 10:55
  • 1
    ...of course, if what you really want to do is number your questions inside the sections (so 1.1, 1.2, 1.3, ... in section 1, then 2.1, 2.2, ... in section 2 etc) then you can just use \numberwithin{question}{section}, if you have loaded the amsmath package, or \counterwithin{question}{section}, with the chngcntr package. – Andrew Nov 17 '17 at 11:04
4

I don't think that LaTeX provides an automatic way of doing this, however, you can hack one easily enough. All sections, subsections, etc are created using \@startsection. This macro expects 6 arguments, the second of which is an "index" for the section depth (\@startsection compares this with secnumdepth to decide whether to add a toc entry etc). Using \pretocmd from the etoolbox you can make \@startsection "save" this index for future use:

\documentclass{article}
\usepackage{etoolbox}

\makeatletter
\def\currentsection{0}% initialise
\pretocmd\@startsection{\def\currentsection{Current section index: #2}}{}{}
\makeatother

\begin{document}

\section{A section}
\currentsection

\subsection{A subsection}
\currentsection

\subsubsection{A subsubsection}
\currentsection

\section*{A starred section}
\currentsection

\subsection*{A starred subsection}
\currentsection

\subsubsection*{A starred subsubsection}
\currentsection

\end{document}

This produces:

enter image description here

For other document classes you might have to change this a little but the idea should be the same. In practice, you'd probably just want to use

\pretocmd\@startsection{\def\currentsection{#2}}{}{}

after which you can do things like

\ifcase\currentsection% ignore 0, which should only happen with no sections
\or % stuff for sections
\or % stuff for subsections
....
\fi

Edit

As already mentioned, this might require tweaking with different document classes. For example, the KOMA-script scrartcl.cls redefines most of the LaTeX internals so unsurprisingly, and as noted in the comments, the code above does not work. KOMA-script does provide a \At@startsection hook but I can't see how to use this because, as far as I can see, there is no way for it to know which "section" it is in. This said, it turns out that scrartcl.cls replaces @\startsection with \scr@startsection so, much as above, one can use:

\pretocmd\scr@startsection{\def\currentsection{Current section index: #2}}{}{}

Be warned, however, that the KOMA documentation explicitly discourages changing macros like \scr@startsection as they don't guarantee not to changing them in future. Here is the full code - modulo the KOMA styling changes, the output is similar.

\documentclass{scrartcl}
\usepackage{etoolbox}

\makeatletter
\def\currentsection{0}% initialise
\pretocmd\scr@startsection{\def\currentsection{Current section index: #2}}{}{}
\makeatother

\begin{document}

\section{A section}
\currentsection

\subsection{A subsection}
\currentsection

\subsubsection{A subsubsection}
\currentsection

\section*{A starred section}
\currentsection

\subsection*{A starred subsection}
\currentsection

\subsubsection*{A starred subsubsection}
\currentsection

\end{document}
  • This doesn't work at all for me. The MWE compiles, but when I try to use this command in the package I'm writing it always prints out 0. – Robbie Nov 17 '17 at 8:58
  • 1
    @Robbie The code that I posted works for me. Have you changed it in some way? In particular, are you using a different \documentclass because, as I said in my answer, this could change things. Please add a minimal working example to your question to show what your set up is. It is really hard to trouble shoot when you give no information about what you are doing. – Andrew Nov 17 '17 at 10:54
  • I use KOMA scrartcl for most of my documents, and that does some quite extensive redefinitions of commands. – Robbie Nov 17 '17 at 11:19
  • @Robbie Yes, KOMA-script changes things quite a lot. I have edited the answer to cope with it as well. I have only answered the original question in the post and not looked at what you said about pgfkeys. – Andrew Nov 17 '17 at 12:39
  • @Robbie Did my edit fix your problem? – Andrew Nov 18 '17 at 8:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.