loop.el-1.3/0000755000175000017500000000000012752731473012572 5ustar dogslegdogslegloop.el-1.3/Makefile0000644000175000017500000000015612752731473014234 0ustar dogslegdogslegCASK ?= cask EMACS ?= emacs all: test test: unit unit: ${CASK} exec ert-runner install: ${CASK} install loop.el-1.3/Cask0000644000175000017500000000034112752731473013374 0ustar dogslegdogsleg(source gnu) (source melpa) (package-file "loop.el") (development (depends-on "f") (depends-on "ecukes") (depends-on "ert-runner") (depends-on "el-mock") (depends-on "cask-package-toolset") (depends-on "undercover")) loop.el-1.3/README.md0000644000175000017500000001027212752731473014053 0ustar dogslegdogsleg# loop.el --- friendly imperative loop structures for Emacs lisp [![Build Status](https://travis-ci.org/Wilfred/loop.el.svg)](https://travis-ci.org/Wilfred/loop.el) [![Coverage Status](https://coveralls.io/repos/Wilfred/loop.el/badge.svg)](https://coveralls.io/r/Wilfred/loop.el) [![MELPA](http://melpa.org/packages/loop-badge.svg)](http://melpa.org/#/loop) [![MELPA Stable](http://stable.melpa.org/packages/loop-badge.svg)](http://stable.melpa.org/#/loop) [![License](http://img.shields.io/:license-gpl3-blue.svg)](http://www.gnu.org/licenses/gpl-3.0.html) Emacs lisp is missing loop structures familiar to users of newer languages. This library adds a selection of popular loop structures as well as break and continue. loop.el also has full unit tests. **Table of Contents** - [loop.el --- friendly imperative loop structures for Emacs lisp](#loopel-----friendly-imperative-loop-structures-for-emacs-lisp) - [Contents](#contents) - [loop-while](#loop-while) - [loop-do-while](#loop-do-while) - [loop-until](#loop-until) - [loop-for-each](#loop-for-each) - [loop-for-each-line](#loop-for-each-line) - [loop-break](#loop-break) - [loop-continue](#loop-continue) - [Alternatives](#alternatives) - [Changelog](#changelog) - [Running the tests](#running-the-tests) ## Contents ### loop-while Repeatedly evaluate BODY while CONDITION is non-nil. `loop-while` `(condition body...)` Example: ``` lisp (let ((x 0) (sum 0)) ;; sum the numbers 0 to 9 (loop-while (< x 10) (setq sum (+ sum x)) (setq x (1+ x)))) ``` ### loop-do-while Evaluate BODY, then repeatedly BODY while CONDITION is non-nil. `loop-do-while` `(condition body...)` Example: ``` lisp (let ((x 0) (sum 0)) ;; our condition is false on the first iteration (loop-do-while (and (> x 0) (< x 10)) (setq sum (+ sum x)) (setq x (1+ x)))) ``` ### loop-until Repeatedly evaluate BODY until CONDITION is non-nil. `loop-until` `(condition body...)` Example: ``` lisp (let ((x 0) (sum 0)) ;; sum the numbers 0 to 9 (loop-until (= x 10) (setq sum (+ sum x)) (setq x (1+ x)))) ``` ### loop-for-each For every item in LIST, evaluate BODY with VAR bound to that item. * `loop-for-each` `(var list body...)` Example: ``` lisp (let ((sum 0)) (loop-for-each x (list 1 2 3 4 5 6 7 8 9) (setq sum (+ sum x)))) ``` ### loop-for-each-line For every line in the buffer, put point at the start of the line and execute BODY. * `loop-for-each-loop` `(body...)` Example: ``` lisp ;; Count headings in a markdown buffer. (let ((heading-count 0)) (loop-for-each-line (when (looking-at "#") (setq heading-count (+ heading 1)))) ``` ### loop-break Terminate evaluation of a `loop-while`, `loop-do-while`, `loop-for-each`, or `loop-for-each-line` block. If there are nested loops, breaks out of the innermost loop. `loop-break` `()` Example: ``` lisp (let ((sum 0)) ;; sum the numbers 1 to 5 (loop-for-each x (list 1 2 3 4 5 6 7 8 9) (setq sum (+ sum x)) (when (= x 5) (loop-break)))) ``` ### loop-continue Skip the rest of the current `loop-while`, `loop-do-while`, or `loop-for-each` block and continue to the next iteration. If there are nested loops, applies to the innermost loop. `loop-continue` `()` Example: ``` lisp (let ((sum 0)) ;; sum the numbers 1, 3, 4, 5 (loop-for-each x (list 1 2 3 4 5) (when (= x 2) (loop-continue)) (setq sum (+ sum x)))) ``` ## Alternatives * `while` and `dolist` are built-in loop structures * The `cl-loop` macro in `cl-lib` * `-each` in [dash.el](https://github.com/magnars/dash.el) ## Changelog * v1.3 `loop-for-each-line` now works even if point moves around. Inside `loop-for-each-line`, `it` is now set to the current line. Added `loop-return`. * v1.2 Added `loop-for-each-line`. Also added edebug support, so you can step through loops in loop.el. * v1.1 Added `loop-continue` * v1.0 `loop-for-each` now takes three arguments: `(VAR LIST BODY...)` * v0.3 Added `loop-until` * v0.2 Basic working implementation ## Running the tests M-x loop-run-tests loop.el-1.3/.gitignore0000644000175000017500000000017212752731473014562 0ustar dogslegdogsleg# Compiled and temporary files *.elc *~ # Cask /.cask dist # Ecukes /features/project/.cask /features/project/test/*.el loop.el-1.3/loop.el0000644000175000017500000001022412752731473014064 0ustar dogslegdogsleg;;; loop.el --- friendly imperative loop structures ;; Copyright (C) 2013 Wilfred Hughes ;; Author: Wilfred Hughes ;; Version: 1.3 ;; Keywords: loop, while, for each, break, continue ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; Emacs lisp is missing loop structures familiar to users of newer ;; languages. This library adds a selection of popular loop structures ;; as well as break and continue. ;; Future ideas: ;; * Named loops so you can break/continue outer loops (defmacro loop-while (condition &rest body) "Repeatedly evaluate BODY while CONDITION is non-nil." (declare (indent defun) (debug (form &rest form))) `(catch 'loop-break (while ,condition (catch 'loop-continue ,@body)))) (defmacro loop-do-while (condition &rest body) "Evaluate BODY, then repeatedly BODY while CONDITION is non-nil." (declare (indent defun) (debug (form &rest form))) (let ((is-first-iteration-var (make-symbol "first-iteration-p"))) `(catch 'loop-break (progn (catch 'loop-continue ,@body) (while ,condition (catch 'loop-continue ,@body)))))) (defmacro loop-until (condition &rest body) "Repeatedly evaluate BODY until CONDITION is non-nil." (declare (indent defun) (debug (form &rest form))) `(loop-while (not ,condition) ,@body)) ;; todo: support vectors and strings (defmacro loop-for-each (var list &rest body) "For every item in LIST, evaluate BODY with VAR bound to that item." (declare (indent defun) (debug (symbolp form &rest form))) (let ((list-var (make-symbol "list"))) `(catch 'loop-break (let ((,list-var ,list) (,var)) (while ,list-var (catch 'loop-continue (setq ,var (car ,list-var)) (setq ,list-var (cdr ,list-var)) ,@body)))))) (defun loop--last-line-p () "Return non-nil if point is on the last line in the buffer." (looking-at (rx (0+ not-newline) buffer-end))) (defun loop--current-line () "Return the current line that contains point." (save-excursion (let ((line-start (progn (beginning-of-line) (point))) (line-end (progn (end-of-line) (point)))) (buffer-substring line-start line-end)))) (defmacro loop-for-each-line (&rest body) "Execute BODY for every line in the buffer. Point is placed at the start of the line on each iteration. Inside BODY, `it' is bound to the contents of the current line." (declare (indent 0) (debug (&rest form))) `(save-excursion (catch 'loop-break (goto-char (point-min)) ;; Execute body on all but the last line. (while (not (loop--last-line-p)) (catch 'loop-continue (save-excursion (let ((it (loop--current-line))) ,@body))) (forward-line)) ;; Execute body on the last line. (catch 'loop-continue (let ((it (loop--current-line))) ,@body))))) (defsubst loop-break () "Terminate evaluation of a `loop-while', `loop-do-while', or `loop-for-each' block. If there are nested loops, breaks out of the innermost loop." (throw 'loop-break nil)) (defun loop-continue () "Skip the rest of the current `loop-while', `loop-do-while', or `loop-for-each' block and continue to the next iteration. If there are nested loops, applies to the innermost loop." (throw 'loop-continue nil)) (defun loop-return (value) "Terminate evaluation of a `loop-while', `loop-do-while', or `loop-for-each' block. The return value from the loop is VALUE." (throw 'loop-break value)) (provide 'loop) ;;; loop.el ends here loop.el-1.3/test/0000755000175000017500000000000012752731473013551 5ustar dogslegdogslegloop.el-1.3/test/test-helper.el0000644000175000017500000000053112752731473016326 0ustar dogslegdogsleg;;; test-helper --- Test helper for loop ;;; Commentary: ;; test helper inspired from https://github.com/tonini/overseer.el/blob/master/test/test-helper.el ;;; Code: (require 'undercover) (undercover "loop.el" (:exclude "*-test.el") (:report-file "/tmp/undercover-report.json")) (provide 'test-helper) ;;; test-helper.el ends here loop.el-1.3/test/loop-test.el0000644000175000017500000001306112752731473016022 0ustar dogslegdogsleg(require 'ert) (require 'loop) (ert-deftest loop-test-while () "Test basic `loop-while' usage." (let ((x 0) (sum 0)) ;; sum the numbers 0 to 9 (loop-while (< x 10) (setq sum (+ sum x)) (setq x (1+ x))) (should (equal sum 45)))) (ert-deftest loop-test-while-break () "Test `loop-break' inside a `loop-while' block." (let ((x 0) (sum 0)) ;; sum the numbers 0 to 5 (loop-while (< x 10) (setq sum (+ sum x)) (setq x (1+ x)) (when (= x 6) (loop-break))) (should (equal sum 15)))) (ert-deftest loop-test-while-continue () "Test `loop-continue' inside a `loop-while' block." (let ((x 0) (sum 0)) ;; sum the numbers 1, 3, 4, 5 (loop-while (< x 5) (setq x (1+ x)) (when (= x 2) (loop-continue)) (setq sum (+ sum x))) (should (equal sum 13)))) (ert-deftest loop-test-do-while () "Test basic `loop-do-while' usage." (let ((x 0) (sum 0)) ;; our condition is false on the first iteration (loop-do-while (and (> x 0) (< x 10)) (setq sum (+ sum x)) (setq x (1+ x))) (should (equal sum 45)))) (ert-deftest loop-test-do-while-continue () "Test `loop-continue' inside a `loop-do-while' block." (let ((x 0) (sum 0)) ;; sum the numbers 1, 3, 4, 5 (loop-while (< x 5) (setq x (1+ x)) (when (= x 2) (loop-continue)) (setq sum (+ sum x))) (should (equal sum 13)))) (ert-deftest loop-test-until () "Test basic `loop-until' usage." (let ((x 0) (sum 0)) ;; sum the numbers 0 to 9 (loop-until (= x 10) (setq sum (+ sum x)) (setq x (1+ x))) (should (equal sum 45)))) (ert-deftest loop-test-until-break () "Test `loop-break' inside a `loop-until' block." (let ((x 0) (sum 0)) ;; sum the numbers 0 to 5 (loop-until (= x 10) (setq sum (+ sum x)) (setq x (1+ x)) (when (= x 6) (loop-break))) (should (equal sum 15)))) (ert-deftest loop-test-until-continue () "Test `loop-continue' inside a `loop-until' block." (let ((x 0) (sum 0)) ;; sum the numbers 1, 3, 4, 5 (loop-until (= x 5) (setq x (1+ x)) (when (= x 2) (loop-continue)) (setq sum (+ sum x))) (should (equal sum 13)))) (ert-deftest loop-test-for-each () "Test basic `loop-for-each' usage." (let ((sum 0)) (loop-for-each x (list 1 2 3 4 5 6 7 8 9) (setq sum (+ sum x))) (should (equal sum 45)))) (ert-deftest loop-test-for-each-break () "Test `loop-break' inside a `loop-for-each' block." (let ((sum 0)) ;; sum the numbers 1 to 5 (loop-for-each x (list 1 2 3 4 5 6 7 8 9) (setq sum (+ sum x)) (when (= x 5) (loop-break))) (should (equal sum 15)))) (ert-deftest loop-test-for-each-continue () "Test `loop-continue' inside a `loop-for-each' block." (let ((sum 0)) ;; sum the numbers 1, 3, 4, 5 (loop-for-each x (list 1 2 3 4 5) (when (= x 2) (loop-continue)) (setq sum (+ sum x))) (should (equal sum 13)))) (ert-deftest loop-test-for-each-return () "`loop-return' should let us return values from a loop." (should (equal 3 (loop-for-each x (list 1 2 3 4) (when (equal x 3) (loop-return x)))))) (ert-deftest loop-test-for-each-line () (with-temp-buffer (insert "foo\nbar\nbaz") (let ((lines nil)) (loop-for-each-line (let ((line-start (progn (beginning-of-line) (point))) (line-end (progn (end-of-line) (point)))) (push (buffer-substring line-start line-end) lines))) (should (equal (nreverse lines) '("foo" "bar" "baz")))))) (ert-deftest loop-test-for-each-line-ignores-movement () "We should execute BODY once for each line, even if point moves around." (with-temp-buffer (insert "foo\nbar\nbaz") (let ((lines nil)) (loop-for-each-line (let ((line-start (progn (beginning-of-line) (point))) (line-end (progn (end-of-line) (point)))) (push (buffer-substring line-start line-end) lines) ;; This line should have no effect. (forward-line 1))) (should (equal (nreverse lines) '("foo" "bar" "baz")))))) (ert-deftest loop-test-for-each-line-it-line () "We should have `it' set inside `loop-for-each-line'." (with-temp-buffer (insert "foo\nbar\nbaz") (let ((lines nil)) (loop-for-each-line (push it lines)) (should (equal (nreverse lines) '("foo" "bar" "baz")))))) (ert-deftest loop-test-for-each-line-break () "Test breaking out of `loop-for-each-line'." (with-temp-buffer (insert "foo\nbar\nbaz") (let ((lines nil)) (loop-for-each-line (let ((line-start (save-excursion (beginning-of-line) (point))) (line-end (save-excursion (end-of-line) (point)))) (when (looking-at "bar") (loop-break)) (push (buffer-substring line-start line-end) lines))) (should (equal (nreverse lines) '("foo")))))) (ert-deftest loop-test-for-each-line-continue () "Test continuing in `loop-for-each-line'." (with-temp-buffer (insert "foo\nbar\nbaz") (let ((lines nil)) (loop-for-each-line (let ((line-start (save-excursion (beginning-of-line) (point))) (line-end (save-excursion (end-of-line) (point)))) (when (looking-at "bar") (loop-continue)) (push (buffer-substring line-start line-end) lines))) (should (equal (nreverse lines) '("foo" "baz")))))) (defun loop-run-tests () "Run all unit tests for loop.el" (interactive) (ert-run-tests-interactively "loop-test-")) loop.el-1.3/.ert-runner0000644000175000017500000000000512752731473014667 0ustar dogslegdogsleg-L . loop.el-1.3/.travis.yml0000644000175000017500000000055512752731473014710 0ustar dogslegdogsleglanguage: generic sudo: false before_install: - curl -fsSkL https://gist.github.com/rejeep/ebcd57c3af83b049833b/raw > x.sh && source ./x.sh - evm install $EVM_EMACS --use --skip - cask env: - EVM_EMACS=emacs-24.3-travis - EVM_EMACS=emacs-24.4-travis - EVM_EMACS=emacs-24.5-travis script: - emacs --version - make test notifications: email: false