bug-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bug#69344: ELPA process collapses Markdown fenced code block into 1 line


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.

NonGNU ELPA - loopy

loopy

Description
A looping macro
Latest
loopy-0.12.0.tar, 2024-Feb-23, 1.36 MiB
Maintainer
Website
https://github.com/okamsn/loopy
Browse ELPA's repository
CGit or Gitweb
Badge
Manual
loopy

To install this package, run in Emacs:

M-x package-install RET loopy RET

Full description

loopy: loopy-badge.svg
loopy-dash: loopy-dash-badge.svg


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:

  • Version 0.12.0:
    • The boolean commands always, never, and thereis now behave more like accumulation commands and use loopy-result by default.
    • Using multiple conditions in 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.
    • Re-using iteration variables in multiple iteration commands now signals an error. It never produced correct code.
    • By default, the 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.
    • The :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.
    • The deprecated flags lax-naming and split were removed.
    • The deprecated command sub-loop was removed.
    • The non-keyword arguments of 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.
    • Fixed a problem with macro expansion in some cases for sub-macros that created a new macro environment (e.g., cl-flet).
    • Loop commands now evaluate arguments like :test, :key, and :by only once.
    • Change the argument order of 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.
  • See the change log for less recent changes.

1. Introduction

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.

2. Similar Libraries

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.

3. How to Install

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)

4. Multiple Kinds of Destructuring

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_.

5. Loop Commands in Arbitrary Code

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.

6. Adding Custom Commands

It is easy to create custom commands for Loopy. To see how, see the section Custom Commands in the documentation.

7. Comparing to cl-loop

See the documentation page Comparing to cl-loop. See also the wiki page Speed Comparisons.

8. Real-World Examples

See the wiki page Examples.

News

CHANGELOG

This document describes the user-facing changes to Loopy.

0.12.0

Bugs Fixed
  • 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).

Breaking Changes
  • 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.

Removals
  • The deprecated flag split was removed ([#165], [#131], [#124]). Instead, use named accumulation variables with the special macro argument accum-opt ... ...

Attachment: CHANGELOG.md
Description: Text Data


reply via email to

[Prev in Thread] Current Thread [Next in Thread]