|
From: | Okamsn |
Subject: | bug#69344: ELPA process collapses Markdown fenced code block into 1 line for HTML page |
Date: | Sat, 24 Feb 2024 02:16:29 +0000 |
Hello, While working on adding my package to Non-GNU ELPA, I noticed that the `make build/loopy` process collapsed the fenced code blocks in the file "CHANGELOG.md" into 1 line in the HTML output. I have attached the Markdown file "CHANGELOG.md" and the outputted file "loopy.html". The squashed code blocks can be seen towards the end of the HTML file when viewed in EWW and Chromium. Thank you.
To install this package, run in Emacs:
M-x package-install RET loopy RET
loopy
is a macro meant for iterating and looping. It is similar in usage to
cl-loop
but uses symbolic expressions rather than keywords.
For most use cases, loopy
should be a nice substitute for cl-loop
and
complementary to the features provided by the Seq and CL libraries and Emacs's
regular looping and mapping features.
For detailed information, see the documentation file. This README is just an overview.
NOTE: Loopy is still in its latter middle stages.
Constructive criticism is welcome. If you see a place for improvement,
please let me know.
Recent breaking changes:
always
, never
, and thereis
now behave more like
accumulation commands and use loopy-result
by default.always
, never
, and thereis
is
deprecated. These commands will be changed to have call argument lists
more like accumulation commands, such as (always [VAR] VAL &key into)
.
This will simplify the code and remove an inconsistency between them and
the other commands.cons
, iter
, nums
, and seq-index
can update named iteration variables outside of the main loop body
and initialize variables to non-nil values, producing faster code.
This can be overridden via the special macro argument with
.:result-type
is deprecated. Instead, use coercion functions in
special macro arguments, possibly with accum-opt
.:init
keyword argument is deprecated. Use the special macro argument
with
instead.reduce
has been fixed. It now works like cl-reduce
when the variable
starting value isn't explicitly given, storing the first value instead of
storing the result of passing the first value and nil
to the function.lax-naming
and split
were removed.sub-loop
was removed.numbers
are deprecated. Use the keyword
arguments instead. A :test
keyword argument was added, which is more
flexible and explicit than the non-keyword arguments.cl-flet
).:test
, :key
, and :by
only
once.test
to be (1) the sequence element then (2)
the tested value, like in seq-contains-p
and unlike in cl-member
, and
explicitly state this order in the documentation.&key
now signals an error when there are unmatched keys in the plist, as
in cl-lib
. &allow-other-keys
has been added.
The loopy
macro is used to generate code for a loop, similar to cl-loop
.
Unlike cl-loop
, loopy
uses parenthetical expressions instead of "clauses".
;; A simple usage of `cl-loop': (cl-loop for i from 1 to 10 if (cl-evenp i) collect i into evens else collect i into odds end ; This `end' keyword is optional here. finally return (list odds evens)) ;; How it could be done using `loopy': (loopy (numbers i :from 1 :to 10) (if (cl-evenp i) (collect evens i) (collect odds i)) (finally-return odds evens)) (loopy (numbers i :from 1 :to 10) (if (cl-evenp i) (collect i :into evens) (collect i :into odds)) (finally-return odds evens))
loopy
supports destructuring for iteration commands like list
and
accumulation commands like sum
or collect
.
;; Summing the nth elements of arrays: ;; => (8 10 12 14 16 18) (loopy (list (list-elem1 list-elem2) '(([1 2 3] [4 5 6]) ([7 8 9] [10 11 12]))) (sum [sum1 sum2 sum3] list-elem1) (sum [sum4 sum5 sum6] list-elem2) (finally-return sum1 sum2 sum3 sum4 sum5 sum6)) ;; Or, more simply: ;; => (8 10 12 14 16 18) (loopy (list list-elem '(([1 2 3] [4 5 6]) ([7 8 9] [10 11 12]))) (sum ([sum1 sum2 sum3] [sum4 sum5 sum6]) list-elem) (finally-return sum1 sum2 sum3 sum4 sum5 sum6)) ;; Separate the elements of sub-list: ;; => ((1 3) (2 4)) (loopy (list i '((1 2) (3 4))) (collect (elem1 elem2) i) (finally-return elem1 elem2))
The loopy
macro is configurable and extensible. In addition to writing one's
own "loop commands" (such as list
in the example above), by using "flags", one
can choose whether to instead use pcase-let
, seq-let
, or even the Dash
library for destructuring.
;; Use `pcase' to destructure array elements: ;; => ((1 2 3 4) (10 12 14) (11 13 15)) (loopy (flag pcase) (array (or `(,car . ,cdr) digit) [1 (10 . 11) 2 (12 . 13) 3 4 (14 . 15)]) (if digit (collect digits digit) (collect cars car) (collect cdrs cdr)) (finally-return digits cars cdrs)) ;; Using the default destructuring: ;; => ((1 2 3 4) (10 12 14) (11 13 15)) (loopy (array elem [1 (10 . 11) 2 (12 . 13) 3 4 (14 . 15)]) (if (numberp elem) (collect digits elem) (collect (cars . cdrs) elem)) (finally-return digits cars cdrs))
Variables like cars
, cdrs
, and digits
in the example above are
automatically let
-bound so as to not affect code outside of the loop.
loopy
has arguments for binding (or not binding) variables, executing code
before or after the loop, executing code only if the loop completes, and for
setting the macro's return value (default nil
). This is in addition to the
looping features themselves.
All of this makes loopy
a useful and convenient choice for looping and
iteration.
Loopy is not the only Lisp library that uses parenthetical expressions instead of
keyword clauses (as in cl-loop
). Iterate and For are two examples from
Common Lisp.
;; Collecting 10 random numbers: ;; cl-loop (Emacs Lisp) (cl-loop repeat 10 collect (random 10)) ;; loopy (Loopy) (loopy (repeat 10) (collect (random 10))) ;; iterate (Common Lisp) (iterate (repeat 10) (collect (random 10))) ;; for (Common Lisp) (for:for ((i repeat 10) (randoms collecting (random 10))))
Generally, all of the packages handle basic use cases in similar ways. One
large difference is that iterate
can embed its looping constructs in arbitrary
code. Loopy is currently provides this feature as a separate macro,
loopy-iter
, which expands looping constructs using macroexpand
.
(require 'loopy-iter) ;; Things to node: ;; - `accum-opt' produces more efficient accumulations for names variables ;; - `cycling' is another name for `repeat' ;; => ((-9 -8 -7 -6 -5 -4 -3 -2 -1) ;; (0) ;; (1 2 3 4 5 6 7 8 9 10 11)) (loopy-iter (accum-opt positives negatives zeroes) (numbering i :from -10 :to 10) ;; Normal `let' and `pcase', not Loopy constructs: (let ((var (1+ i))) (pcase var ((pred cl-plusp) (collecting positives var)) ((pred cl-minusp) (collecting negatives var)) ((pred zerop) (collecting zeroes var)))) (finally-return negatives zeroes positives))
Loopy is not yet feature complete. Please request features or report problems
in this project’s issues tracker. While basic uses are covered, some of the
more niche features of cl-loop
and iterate
are still being added.
Loopy can be installed from MELPA as the package loopy
. The optional package
loopy-dash
can be installed to enable using the Dash library for destructuring
(instead of other methods).
(use-package loopy) ;; Optional support for destructuring with Dash. (use-package loopy-dash :after (loopy) :demand t)
To load all of the alternative destructuring libraries (see section Multiple Kinds of Destructuring) and the alternative macro form (see section Loop Commands in Arbitrary Code), use
(use-package loopy :config (require 'loopy-iter) (require 'loopy-pcase) (require 'loopy-seq)) (use-package loopy-dash :after (loopy) :demand t)
The default destructuring system is a super-set of what cl-lib
provides
and is described in the section Basic Destructuring in the documentation.
In addition to the built-in destructuring style, loopy
can optionally use
destructuring provided by pcase-let
, seq-let
, the dash
library. This
provides greater flexibility and allows you to use destructuring patterns that
you're already familiar with.
These features can be enabled with "flags", described in the section Using Flags in the documentation.
Here are a few examples that demonstrate how loopy
can use destructuring with
accumulation commands.
(require 'loopy-dash) ;; => (((1 (2 3)) (4 (5 6))) ; whole ;; (1 4) ; i ;; (3 6)) ; k (loopy (flag dash) (list elem '((1 (2 3)) (4 (5 6)))) (collect (whole &as i (_ k)) elem) (finally-return whole i k)) ;; = > ((3 5) (4 6)) (loopy (flag dash) (list (&plist :a a :b b) '((:a 3 :b 4 :c 7) (:g 8 :a 5 :b 6))) (collect a-vals a) (collect b-vals b) (finally-return a-vals b-vals)) (require 'loopy-pcase) ;; => ((1 4) (3 6)) (loopy (flag pcase) (list elem '((1 (2 3)) (4 (5 6)))) (collect `(,a (,_ ,b)) elem) (finally-return a b)) ;; => ((1 6) (3 8) ([4 5] [9 10])) (require 'loopy-seq) (loopy (flag seq) (list elem '([1 2 3 4 5] [6 7 8 9 10])) (collect [a _ b &rest c] elem) (finally-return a b c))
For more on how dash
does destructuring, see their documentation on the -let
_expression_.
The macro loopy-iter
can be used to embed loop commands in arbitrary code. It
is similar in use to Common Lisp's Iterate macro, but it is not a port of
Iterate to Emacs Lisp.
(require 'loopy-iter) ;; => ((1 2 3) (-3 -2 -1) (0)) (loopy-iter (accum-opt positives negatives other) (numbering i :from -3 :to 3) (pcase i ((pred cl-plusp) (collecting positives i)) ((pred cl-minusp) (collecting negatives i)) (_ (collecting other i))) (finally-return positives negatives other)) ;; => 6 (loopy-iter (listing elem '(1 2 3)) (funcall #'(lambda (x) (summing x)) elem))
For more on this, see the documentation.
It is easy to create custom commands for Loopy. To see how, see the section Custom Commands in the documentation.
cl-loop
See the documentation page Comparing to cl-loop
. See also the wiki page Speed
Comparisons.
See the wiki page Examples.
This document describes the user-facing changes to Loopy.
Rewrite prepend
in terms of append
. Rewrite push-into
in terms of
collect
([#160]). This change makes these commands work with the new system for
optimized accumulation variables.
Without an explicit starting value for the accumulation variable, reduce
now
uses the first accumulated value without passing it to the function. This is
how reducing actually works, and was the intended behavior. Previously, the
function was always called. This is a breaking change. See [#164].
emacs-lisp
;; Now correct behavior (would previously error):
;; => 6
(loopy (list i '(1 2 3))
(reduce i #'*))
Fix find
when :on-failure
is nil ([#171]). Previously, nil
was
interpreted as not passing :on-failure
.
emacs-lisp
;; Previously erroneously returned 27:
;; => nil
(loopy (with (val 27))
(list i '(1 2 3))
(find val nil (> i 10) :on-failure nil)
(finally-return val))
Fix find
when EXPR
is nil and :on-failure
is given ([#171]).
Previously, after the test passed and VAR
was set to nil
, that nil
was
interpreted as not passing the test, so that VAR
then bound to the value
passed for :on-failure
.
emacs-lisp
;; Previously erroneously returned 27:
;; => nil
(loopy (list i '(1 2 3))
(find nil (> i 1) :on-failure 27))
Better signal an error with conflicting arguments in numbers
. See [#172].
Fix macro expansion in some cases by not resetting the macro environment
([#173]). For example, we failed to pass the current/modified environment to
macroexpand-all
in some cases.
emacs-lisp
;; Previously failed to use `cl-flet''s internally defined function
;; for `10+':
;; => (11 12 13 14 15)
(loopy (named outer)
(list i '((1 2) (3 4) (5)))
(loopy-iter (listing j i)
(cl-flet ((10+ (y) (+ 10 y)))
(at outer
(collecting (10+ j))))))
The documentation describes Loopy's default destructuring style as a super-set
of that of cl-lib
. &key
now behaves more like it does in cl-lib
,
signaling an error when appropriate ([#182]) and supporting the full form
((KEY VAR) DEFAULT SUPPLIED)
.
Fix how the first accumulated value is used in reduce
. See [#164] and the
item above.
Make it an error to re-use iteration variables with multiple iteration commands ([#142], [#144]). The resulting code shouldn't have worked anyway, but we now report it as an error during macro expansion.
elisp
;; Will now signal an error during expansion:
(loopy (list i '(1 2 3))
(list i '(4 5 6)))
The keyword arguments of commands are now evaluated only once. This is now
consistent with passing function values of other loop commands. If constant
according to macroexp-const-p
, then they are used directly. Otherwise, the
value is first stored in a variable. See [#170], [#177], [#176], and [#180].
In accumulation commands using the test
keyword argument, the argument order
of the two-argument test function is now document as (SEQUENCE-ITEM,
TESTED-ITEM)
, similar to seq-contains-p
. The argument order was previously
undocumented and not guaranteed. See [#170] and [#177].
Like in cl-lib
, destructuring with &key
will now signal an error if there
are unmatched keys and &allow-other-keys
was not given or
:allow-other-keys
is not present in the property list with a non-nil value
([#182]).
The default destructuring style now uses pcase
underneath ([#182]). To
accomodate this, some of the defined errors and error detections have changed.
split
was removed ([#165], [#131], [#124]). Instead,
use named accumulation variables with the special macro argument accum-opt
...
...
CHANGELOG.md
Description: Text Data
[Prev in Thread] | Current Thread | [Next in Thread] |