31

This is a question one might answer with "Why the hell would you want to do it?!", sort of an experiment.

What I am looking for is a way to write lists with many levels of nestings (notes for school) in a "natural" way. Previously, I used an elaborate system of Pandoc, LaTeX and Makefiles to generate notes with occasional LaTeX snippets, however, I would like a more integrated workflow (for example, embedding pseudocode in a codebox environment in a Pandoc/Markdown file is quite challenging, as Markdown was designed with HTML in mind).

If I make + an active character, like

\makeatletter
\mathchardef\@my@mathplus=\mathcode`+
\catcode`+=\active
\def{+}{\ifmmode\@my@mathplus\else\item\fi}
\mateatother

I can write itemizes like

\begin{itemize}
+ one
+ two
+ three
\end{itemize}

however, I would like to get rid of \begin{itemize} and \end{itemize} and write notes like

+ one
    + subitem one
    + subitem two
+ two
+ three

resulting in the nested list, neatly rendered.

Is there a way to achieve this, or I should consider some kind of pre-procession instead? (Creative and mildly insane answers are appreciated.)

lockstep
  • 250,273

5 Answers5

25

Insane answer to an insane question ;)

\documentclass{article}

\def\+{+}

\makeatletter
\catcode`\ =12\let\@nl@space= \catcode`\ =10
\newcount\@nl@rlevel
\newcount\@nl@llevel
\@nl@llevel=-1

\def\@nl{%
  \catcode`\ =12
  \global\@nl@rlevel=0
  \futurelet\@nl@store\@nl@%
}
\def\@nl@gobble#1{\futurelet\@nl@store\@nl@}
\def\@nl@enditemize{
  \ifnum\the\@nl@rlevel<\the\@nl@llevel%
    \end{itemize}%
    \egroup%
    \expandafter\@nl@enditemize%
  \else%
    \ifnum\the\@nl@rlevel=\the\@nl@llevel\else%
       \errmessage{Error: inconsistent identation}
    \fi%
  \fi%
}
\def\@nl@{%
  \ifx\@nl@store\@nl@space%
    \global\advance\@nl@rlevel by 1
    \expandafter\@nl@gobble%
  \else%
    \catcode`\ =10
    \ifx\@nl@store+%
      \ifnum\the\@nl@rlevel>\the\@nl@llevel%
        \bgroup%
        \@nl@llevel=\the\@nl@rlevel
        \begin{itemize}%
      \fi%
      \@nl@enditemize%
      \item \expandafter\expandafter\expandafter\@gobble%
    \else%
      \ifx\@nl@store\@nl%
        \global\@nl@rlevel=-1\relax\@nl@enditemize\par
      \else\space\fi%
    \fi%
  \fi%
}

\catcode`\^^M=\active%
\AtBeginDocument{%
  \catcode`\^^M=\active%
  \let^^M=\@nl%
}%
\catcode`\^^M=5
\makeatother

\begin{document}
Some sample text.

 + foo
 + bar
   + a
   + b
     + c. A very long line
       split into multiple lines.
 + hehe

Just like nothing happened. Ha!

\+ escaped starting plus

\[\lim_{x\to\infty}\frac1x = 0\]
\end{document}
DirtY iCE
  • 366
16

You may need to redefine ^^M to get this feature. That will be somewhat complicated.

As a suggestion, you may use ++ for a subitem, and +++ for a subsubitem. That's much easier:

\documentclass{article}

\makeatletter
\catcode`\+\active
\def\itemX{\@ifnextchar+{\subitemX}{\item}}
\def\subitemX#1{\@ifnextchar+{\subsubitemX}{\subitem}}
\def\subsubitemX#1{\subsubitem}
\def+{\ifmmode\string+\else\expandafter\itemX\fi}
\@makeother\+
\makeatother

\newenvironment{easylist}{\trivlist\item
  \def\item{\par\noindent\textbullet\enspace}%
  \def\subitem{\par\noindent\quad\textasteriskcentered\enspace\ignorespaces}%
  \def\subsubitem{\par\noindent\qquad-\enspace\ignorespaces}%
  \catcode`\+\active
}{\endtrivlist}

\begin{document}

\begin{easylist}
+ foo $a+b$
+ foo 
++ bar
++ bar
+++ baz
+++ baz
+ foo
\end{easylist}

\end{document}

See also: nicetext package; pandoc tool.

Leo Liu
  • 77,365
  • 1
    Note: After reassigning the category codes of ^^M and space char, there is no essential difficulty to distinguish different level of items through indent. However, it is a bit dangerous to redefine newlines and spaces for normal text. – Leo Liu Oct 20 '11 at 18:43
  • 1
    Oh, finally I realize that there is a package easylist, which implement what I said. I just reinvent the wheel. – Leo Liu Oct 21 '11 at 10:29
6

This is a bit different rather use two commands, one to narrow the paragraph and another to widen it. I have used \i, \w. The first one to make the paragraph narrower (i.e indent but lose the dotless i and the second the \w for wider.

\documentclass{article}
\usepackage{lipsum}
\parskip12pt
\def\narrower{\advance\leftskip by\parindent
\advance\rightskip by\parindent}
\def\wider{\advance\leftskip by-\parindent
\advance\rightskip by-\parindent}


\def\w{\wider}
\def\i{\narrower}

\begin{document}
   \lipsum[1]
   \i \lipsum[2] 
   \i \lipsum[2]
   \i \lipsum[3]
   \w \lipsum[4]
   \w \lipsum[5]
   \w \lipsum[6]
\end{document}

Advantages, never type more than is necessary! Style to suit!

yannisl
  • 117,160
  • The command \i is already defined. \expandafter\detokenize\expandafter{\i} – Marco Daniel Oct 20 '11 at 19:10
  • @MarcoDaniel ... I know that is why I mentioned you lose the dotless i, you don't need a dotless i do you? – yannisl Oct 20 '11 at 19:14
  • I didn't read this ;-(. Sorry. Nobody need this ;-) but I thought it is important to mention this. – Marco Daniel Oct 20 '11 at 19:15
  • @MarcoDaniel No problem, you only need it if you type turkish texts, I picked it for its mnemonics i.e, indent, then the indentation level stays until you change it. – yannisl Oct 20 '11 at 19:24
4

All of the solutions suggested here rely on catcode trickery. LuaTeX provides a sane way to do such input translation. The solution below translates the + at the beginning of line to \firstlevel and ++ at the beginning of line to \secondlevel. So, first lets define the \firstlevel and \secondlevel macros (in ConTeXt)

\define\firstlevel
    {\endgraf
     \blank
     \noindentation
     \hangindent=1em
     \hangafter\plusone
     \dontleavehmode\hbox to 1em {\symbol[1]}}

\define\secondlevel
    {\endgraf
     \blank[none]
     \noindentation
     \hangindent=2em
     \hangafter\plusone
     \null \quad \hbox to 1em {\symbol[2]}}

ConTeXt already have a module m-translate that allows you to translate the input while the file is being read and before the text is passed on to TeX. So, you can do:

\usemodule[translate]
\translateinput[++][\string\secondlevel]
\translateinput[+][\string\firstlevel]

\starttext

+ One
+ Two
+ Three

\enableinputtranslation

+ One, a really long line \input ward
++ Two 
+ Three

\stoptext

which gives

enter image description here

The only trouble is that this translates all the + to \firstlevel. The m-translate module does not provide an interface to only match the character at the beginning of the line, but it is a short module, so we can override how the match is done.

In the following code, I have simply copied the m-translate module and changed the translators.translate() function.

\startluacode
    local translators = { }

    moduledata.translators = translators

    local compiled, list = nil, nil

    function translators.register(from,to)
        local l = lpeg.P(from)/to
        if not list then
            list = l
        else
            list = list + l
        end
        compiled = nil
    end

    function translators.translate(s)
        if list then
            if not compiled then
                compiled = lpeg.Cs((list)^0*(lpeg.P(1))^0)
            end
            return compiled:match(s)
        else
            return s
        end
    end

    local textlineactions = resolvers.openers.helpers.textlineactions

    utilities.sequencers.appendaction(textlineactions,"after","moduledata.translators.translate")

    function translators.enable()
        utilities.sequencers.enableaction(textlineactions,"moduledata.translators.translate")
    end

    function translators.disable()
        utilities.sequencers.disableaction(textlineactions,"moduledata.translators.translate")
    end

    function translators.reset(s)
        translators.enable()
        list, compiled = nil, nil
    end

    translators.disable()
\stopluacode

\unprotect

\unexpanded\def\translateinput
  {\dodoubleargument\module_translate_input}

\def\module_translate_input[#1][#2]%
  {\ctxlua{moduledata.translators.register(\!!bs#1\!!es,\!!bs#2\!!es)}}

\unexpanded\def\resetinputtranslation
  {\ctxlua{moduledata.translators.reset()}}

\unexpanded\def\enableinputtranslation
  {\ctxlua{moduledata.translators.enable()}}

\unexpanded\def\disableinputtranslation
  {\ctxlua{moduledata.translators.disable()}}

\unexpanded\def\readtranslatedfile#1%
  {\enableinputtranslation
   \readfile{#1}\donothing\donothing
   \disableinputtranslation}

\protect

\define\firstlevel
    {\endgraf
     \blank
     \noindentation
     \hangindent=1em
     \hangafter\plusone
     \dontleavehmode\hbox to 1em {\symbol[1]}}

\define\secondlevel
    {\endgraf
     \blank[none]
     \noindentation
     \hangindent=2em
     \hangafter\plusone
     \null \quad \hbox to 1em {\symbol[2]}}

\translateinput[++][\string\secondlevel]
\translateinput[+][\string\firstlevel]

\starttext

+ One
+ Two
+ Three

\enableinputtranslation

+ One, a really long line \input ward
++ Two and math $a + b$ works
+ Three

\stoptext

which gives (notice that the + in the math mode has not changed).

enter image description here

Aditya
  • 62,301
0

I had trouble using the previous solutions with beamerposter, so I came up with another. It's a bit of a kludge, but it works for me, and preserves the bullet styles of the beamerposter:

\newcommand{\point}[1]{
\begin{itemize}
\item{#1}
\end{itemize}
}
\newcommand{\subpoint}[1]{
\begin{itemize}
\item[]
\begin{itemize}
\item{#1}
\end{itemize}
\end{itemize}
}

Perhaps someone with better code-fu can make this work with active characters instead of my clumsy environment-based way of doing this.

  • 4
    Welcome to TeX.SX! I think you misread the question, it is about indention in the source. – mafp Aug 26 '13 at 22:13
  • Thanks! As I said, I'm not familiar enough with LaTeX to use the catcode to achieve the desired input syntax. I was just offering part of a solution (how to make a bullet point of arbitrary depth with a single command instead of a nested list). – user2719544 Aug 27 '13 at 15:13