gpoore / minted

minted is a LaTeX package that provides syntax highlighting using the Pygments library. Highlighted source code can be customized using fancyvrb.
1.73k stars 125 forks source link

Only allow page break on a blank line #352

Open GitMew opened 1 year ago

GitMew commented 1 year ago

minted handles long code files given to \inputminted by adding a page break when the page is full. However, for languages like Scheme, it can be hard for the reader to know the relative indentation of the next statement w.r.t. the previous line (take this file as example), and hence a page break makes the code hard to follow.

Is there a way of only inserting a page break on blank lines -- or, by lack of them, at least prefer breaking on blank lines? There is only one post about this on the internet as far as I know, and all it does for me is add the word BLANK to blank lines in the code listing, without causing breaks. I tried changing that to \newpage, but to no avail.

My preamble is fairly plain:

\usepackage{minted}
\usemintedstyle{autumn}

\usepackage{xhfill}
\usepackage{titlesec}
\usepackage{fancyhdr}

\setminted{breaklines,linenos}
\renewcommand{\theFancyVerbLine}{\textcolor[rgb]{0.85,0.85,0.85}{\footnotesize\arabic{FancyVerbLine}}}
muzimuzhi commented 1 year ago

[...], and all it does for me is add the word BLANK to blank lines in the code listing, without causing breaks. I tried changing that to \newpage, but to no avail.

Could you provide a reproducible example?

Update: Ah, I guess that's because a longly BLANK is not considered an error in Scheme but was an error in JSON, which was the case in mentioned tex-sx answer. A more general solution is required.

GitMew commented 1 year ago

Could you provide a reproducible example?

@muzimuzhi Here's the full example using the code cited above. It reproduces the behaviour with pdfLaTeX 2022. Beware: embedding the code within TeX using \begin{minted} instead of \inputminted causes the inserted occurrences of BLANK to get a red colouring instead of being black. That shows that proposed solutions may behave differently depending on how the code is included.

\documentclass{article}

\usepackage[margin=0.75in]{geometry}
\usepackage[utf8]{inputenc}

% Section titles
\usepackage{xhfill}
\usepackage{titlesec}
\titleformat{name=\section,numberless}
    {\filcenter\Large\bfseries}
    {}
    {.5em}
    {\hrule}
    [\hrule]

% Minted
\usepackage{minted}
\usemintedstyle{autumn}

% - Change the colour of the line numbers to gray.
\renewcommand{\theFancyVerbLine}{\textcolor[rgb]{0.85,0.85,0.85}{\footnotesize\arabic{FancyVerbLine}}}

% Page numbers
\usepackage{fancyhdr}
\pagestyle{fancyplain}
\fancyhf{}
\fancyhead[R]{\thepage}
\renewcommand{\headrulewidth}{0pt}
\setlength{\headheight}{2em}

% --- Failed attempt at causing \newpage on blank lines ---
\makeatletter
%\showoutput

\def\@wasblank{\PYG {err}{BLANK}}
\def\foo{\aftergroup\ffoo}
\def\ffoo{\goodbreak}% or \clearpage or whatever
\let\zzzz\FV@PreProcessLine
\def\FV@PreProcessLine{%
    \ifx\FV@Line\@empty
        \def\FV@Line{BLANK}%
    \fi
    \ifx\FV@Line\@wasblank
        \def\FV@Line{\aftergroup\aftergroup\aftergroup\foo}%
    \fi
\zzzz}
% --- ---

\begin{document}

\section*{EXAMPLE}
\begin{minted}[breaklines,linenos]{scheme}
(module interp (lib "eopl.ss" "eopl")

  (require "drscheme-init.scm")

  (require "lang.scm")
  (require "data-structures.scm")
  (require "environments.scm")
  (require "store.scm")
  (require "classes.scm")

  (provide value-of-program value-of instrument-let instrument-newref)

;;;;;;;;;;;;;;;; switches for instrument-let ;;;;;;;;;;;;;;;;

  (define instrument-let (make-parameter #f))

  ;; say (instrument-let #t) to turn instrumentation on.
  ;;     (instrument-let #f) to turn it off again.

;;;;;;;;;;;;;;;; the interpreter ;;;;;;;;;;;;;;;;

  ;; value-of-program : Program -> ExpVal
  ;; Page: 336
  (define value-of-program 
    (lambda (pgm)
      (initialize-store!)             
      (cases program pgm
        (a-program (class-decls body)
          (initialize-class-env! class-decls)
          (value-of body (init-env))))))

  ;; value-of : Exp * Env -> ExpVal
  ;; Page: 336 and 337
  (define value-of
    (lambda (exp env)
      (cases expression exp

        (const-exp (num) (num-val num))

        (var-exp (var) (deref (apply-env env var)))

        (diff-exp (exp1 exp2)
          (let ((val1
          (expval->num
            (value-of exp1 env)))
                (val2
          (expval->num
            (value-of exp2 env))))
            (num-val
          (- val1 val2))))

        (sum-exp (exp1 exp2)
          (let ((val1
          (expval->num
            (value-of exp1 env)))
                (val2
          (expval->num
            (value-of exp2 env))))
            (num-val
          (+ val1 val2))))

        (zero?-exp (exp1)
      (let ((val1 (expval->num (value-of exp1 env))))
        (if (zero? val1)
          (bool-val #t)
          (bool-val #f))))

        (if-exp (exp0 exp1 exp2) 
          (if (expval->bool (value-of exp0 env))
            (value-of exp1 env)
            (value-of exp2 env)))

        (let-exp (vars exps body)       
      (when (instrument-let)
        (eopl:printf "entering let ~s~%" vars))
          (let ((new-env 
                  (extend-env 
                    vars
                    (map newref (values-of-exps exps env))
                    env)))
          (when (instrument-let)
        (begin
          (eopl:printf "entering body of let ~s with env =~%" vars)
          (pretty-print (env->list new-env))
          (eopl:printf "store =~%")
          (pretty-print (store->readable (get-store-as-list)))
          (eopl:printf "~%")
          ))
          (value-of body new-env)))

        (proc-exp (bvars body)
      (proc-val
        (procedure bvars body env)))

        (call-exp (rator rands)          
          (let ((proc (expval->proc (value-of rator env)))
                (args (values-of-exps rands env)))
        (apply-procedure proc args)))

        (letrec-exp (p-names b-varss p-bodies letrec-body)
          (value-of letrec-body
            (extend-env-rec** p-names b-varss p-bodies env)))

        (begin-exp (exp1 exps)
          (letrec 
            ((value-of-begins
               (lambda (e1 es)
                 (let ((v1 (value-of e1 env)))
                   (if (null? es)
                     v1
                     (value-of-begins (car es) (cdr es)))))))
            (value-of-begins exp1 exps)))

        (assign-exp (x e)
          (begin
            (setref!
              (apply-env env x)
              (value-of e env))
            (num-val 27)))

        (list-exp (exps)
          (list-val
            (values-of-exps exps env)))

        ;; new cases for CLASSES language

        (new-object-exp (class-name rands)
          (let ((args (values-of-exps rands env))
                (obj (new-object class-name)))
            (apply-method
              (find-method class-name 'initialize)
              obj
              args)
            obj))

        (self-exp ()
          (apply-env env '%self))

        (method-call-exp (obj-exp method-name rands)
          (let ((args (values-of-exps rands env))
                (obj (value-of obj-exp env)))
            (apply-method
              (find-method (object->class-name obj) method-name)
              obj
              args)))

        (super-call-exp (method-name rands)
          (let ((args (values-of-exps rands env))
                (obj (apply-env env '%self)))
            (apply-method
              (find-method (apply-env env '%super) method-name)
              obj
              args)))        
        )))

  ;; apply-procedure : Proc * Listof(ExpVal) -> ExpVal
  (define apply-procedure
    (lambda (proc1 args)
      (cases proc proc1
        (procedure (vars body saved-env)
          (let ((new-env
                  (extend-env
                    vars
                    (map newref args)
                    saved-env)))
            (when (instrument-let)
              (begin
                (eopl:printf
                  "entering body of proc ~s with env =~%"
                  vars)
                (pretty-print (env->list new-env)) 
                (eopl:printf "store =~%")
                (pretty-print (store->readable (get-store-as-list)))
                (eopl:printf "~%")))
            (value-of body new-env))))))

  ;; apply-method : Method * Obj * Listof(ExpVal) -> ExpVal
  (define apply-method                    
    (lambda (m self args)
      (cases method m
        (a-method (vars body super-name field-names)
          (value-of body
            (extend-env vars (map newref args)
              (extend-env-with-self-and-super
                self super-name
                (extend-env field-names (object->fields self)
                  (empty-env)))))))))

  (define values-of-exps
    (lambda (exps env)
      (map
        (lambda (exp) (value-of exp env))
        exps)))

  ;; store->readable : Listof(List(Ref,Expval)) 
  ;;                    -> Listof(List(Ref,Something-Readable))
  (define store->readable
    (lambda (l)
      (map
        (lambda (p)
          (cons
            (car p)
            (expval->printable (cadr p))))
        l)))

  )
\end{minted}

% \inputminted[breaklines,linenos]{scheme}{./rkt/test.rkt}

\end{document}