emacs-elpa-diffs
[Top][All Lists]
Advanced

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

[elpa] master 3f035ad 46/46: Add 'packages/tiny/' from commit '159c3f74e


From: Oleh Krehel
Subject: [elpa] master 3f035ad 46/46: Add 'packages/tiny/' from commit '159c3f74e75970808b83fe4b732f180cb76872a3'
Date: Sun, 22 Mar 2015 17:42:41 +0000

branch: master
commit 3f035ad4f8ef0bf6b8e60a819ec97d6f7f6ea5b5
Merge: 302a16a 159c3f7
Author: Oleh Krehel <address@hidden>
Commit: Oleh Krehel <address@hidden>

    Add 'packages/tiny/' from commit '159c3f74e75970808b83fe4b732f180cb76872a3'
    
    git-subtree-dir: packages/tiny
    git-subtree-mainline: 302a16a15bbaf3842246293a27c77ba2fd9a56e1
    git-subtree-split: 159c3f74e75970808b83fe4b732f180cb76872a3
---
 packages/tiny/.travis.yml  |   20 ++
 packages/tiny/Cask         |    5 +
 packages/tiny/Makefile     |   18 ++
 packages/tiny/README.md    |   98 +++++++++++
 packages/tiny/tiny-test.el |  214 +++++++++++++++++++++++
 packages/tiny/tiny.el      |  412 ++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 767 insertions(+), 0 deletions(-)

diff --git a/packages/tiny/.travis.yml b/packages/tiny/.travis.yml
new file mode 100644
index 0000000..fe3ab71
--- /dev/null
+++ b/packages/tiny/.travis.yml
@@ -0,0 +1,20 @@
+language: emacs-lisp
+before_install:
+  # PPA for stable Emacs packages
+  - sudo add-apt-repository -y ppa:cassou/emacs
+  # PPA for Emacs nightlies
+  - sudo add-apt-repository -y ppa:ubuntu-elisp/ppa
+  # Update and install the Emacs for our environment
+  - sudo apt-get update -qq
+  - sudo apt-get install -qq -yy ${EMACS}-nox ${EMACS}-el
+  # Install cask dependencies
+  - curl -fsSLo /tmp/cask-master.zip 
https://github.com/cask/cask/archive/master.zip
+  - sudo unzip -qq -d /opt /tmp/cask-master.zip
+  - sudo ln -sf /opt/cask-master/bin/cask /usr/local/bin/cask
+  - cask
+env:
+  - EMACS=emacs24
+  - EMACS=emacs-snapshot
+script:
+  - emacs --version
+  - make test
diff --git a/packages/tiny/Cask b/packages/tiny/Cask
new file mode 100644
index 0000000..5336cec
--- /dev/null
+++ b/packages/tiny/Cask
@@ -0,0 +1,5 @@
+(source gnu)
+(source melpa)
+
+(development
+ (depends-on "undercover"))
diff --git a/packages/tiny/Makefile b/packages/tiny/Makefile
new file mode 100644
index 0000000..6332238
--- /dev/null
+++ b/packages/tiny/Makefile
@@ -0,0 +1,18 @@
+EMACS ?= emacs
+CASK_EXEC ?= cask exec
+
+all: test
+
+test: clean-elc
+       ${MAKE} unit
+
+unit:
+       ${CASK_EXEC} ${EMACS} -Q -batch -l tiny-test.el -l tiny.el --eval "(ert 
t)"
+
+compile:
+       ${CASK_EXEC} ${EMACS} -Q -batch -f batch-byte-compile tiny.el
+
+clean-elc:
+       rm -f f.elc
+
+.PHONY:        all test
diff --git a/packages/tiny/README.md b/packages/tiny/README.md
new file mode 100644
index 0000000..ac9e690
--- /dev/null
+++ b/packages/tiny/README.md
@@ -0,0 +1,98 @@
+[![Build 
Status](https://travis-ci.org/abo-abo/tiny.svg?branch=master)](https://travis-ci.org/abo-abo/tiny)
+[![Coverage 
Status](https://coveralls.io/repos/abo-abo/tiny/badge.svg?branch=master)](https://coveralls.io/r/abo-abo/tiny?branch=master)
+
+### Main idea:
+
+This is an alternative to inserting numeric ranges with macros (i.e. `F3 F3`).
+The advantages are:
+
+1. Brevity: consider `F3 F3 SPC M-1 M-0 F4` vs. `m10 C-;`.
+2. Much better undo context: a single `C-_` will undo the whole thing
+   and allow you to edit the code. With macros you'd have to undo multiple
+   times and restart from scratch, instead of tweaking what you just invoked.
+3. The ability to insert the same number several times in a single iteration,
+   and transform it with `format`-style expressions
+   e.g. `m6\n15%s=0%o=0x%x` will expand to
+
+        6=06=0x6
+        7=07=0x7
+        8=010=0x8
+        9=011=0x9
+        10=012=0xa
+        11=013=0xb
+        12=014=0xc
+        13=015=0xd
+        14=016=0xe
+        15=017=0xf
+4. Last but not least, the ability to transform the number with lisp 
expressions.
+   For instance:
+    1. `m5 10*xx` -> `25 36 49 64 81 100`
+    2. `m5 10*xx|0x%x` -> `0x19 0x24 0x31 0x40 0x51 0x64`
+    3. `m10+x?a%c` -> `a b c d e f g h i j k`
+    4. `m10+x?A%c` -> `A B C D E F G H I J K`
+    5. `m97,105stringx` -> `a,b,c,d,e,f,g,h,i`
+    6. `m97,102stringxupcasex` -> `aA,bB,cC,dD,eE,fF`
+    7. `m,3|%(+ x x) and %(* x x) and %s` -> `0 and 0 and 0,2 and 1 and 1,4 
and 4 and 2,6 and 9 and 3,8 and 16 and 4,10 and 25 and 5`
+
+### Use in conjunction with `org-mode`:
+
+    m1\n14|*** TODO http://emacsrocks.com/e%02d.html
+
+    *** TODO http://emacsrocks.com/e01.html
+    *** TODO http://emacsrocks.com/e02.html
+    *** TODO http://emacsrocks.com/e03.html
+    *** TODO http://emacsrocks.com/e04.html
+    *** TODO http://emacsrocks.com/e05.html
+    *** TODO http://emacsrocks.com/e06.html
+    *** TODO http://emacsrocks.com/e07.html
+    *** TODO http://emacsrocks.com/e08.html
+    *** TODO http://emacsrocks.com/e09.html
+    *** TODO http://emacsrocks.com/e10.html
+    *** TODO http://emacsrocks.com/e11.html
+    *** TODO http://emacsrocks.com/e12.html
+    *** TODO http://emacsrocks.com/e13.html
+    *** TODO http://emacsrocks.com/e14.html
+
+You can even schedule and deadline:
+
+    m\n8|**** TODO Learning from Data Week %(+ x 2) \nSCHEDULED: <%(date "Oct 
7" (* x 7))> DEADLINE: <%(date "Oct 14" (* x 7))>
+
+    **** TODO Learning from Data Week 2
+    SCHEDULED: <2013-10-07 Mon> DEADLINE: <2013-10-14 Mon>
+    **** TODO Learning from Data Week 3
+    SCHEDULED: <2013-10-14 Mon> DEADLINE: <2013-10-21 Mon>
+    **** TODO Learning from Data Week 4
+    SCHEDULED: <2013-10-21 Mon> DEADLINE: <2013-10-28 Mon>
+    **** TODO Learning from Data Week 5
+    SCHEDULED: <2013-10-28 Mon> DEADLINE: <2013-11-04 Mon>
+    **** TODO Learning from Data Week 6
+    SCHEDULED: <2013-11-04 Mon> DEADLINE: <2013-11-11 Mon>
+    **** TODO Learning from Data Week 7
+    SCHEDULED: <2013-11-11 Mon> DEADLINE: <2013-11-18 Mon>
+    **** TODO Learning from Data Week 8
+    SCHEDULED: <2013-11-18 Mon> DEADLINE: <2013-11-25 Mon>
+    **** TODO Learning from Data Week 9
+    SCHEDULED: <2013-11-25 Mon> DEADLINE: <2013-12-02 Mon>
+    **** TODO Learning from Data Week 10
+    SCHEDULED: <2013-12-02 Mon> DEADLINE: <2013-12-09 Mon>
+
+Here's how to schedule a task that repeats Monday through Friday at 10:00, 
every week:
+
+    m0\n4|** TODO Something work-related\nSCHEDULED: <%(date "mon" x) 10:00 
+1w>
+
+    ** TODO Something work-related
+    SCHEDULED: <2013-11-04 Mon 10:00 +1w>
+    ** TODO Something work-related
+    SCHEDULED: <2013-11-05 Tue 10:00 +1w>
+    ** TODO Something work-related
+    SCHEDULED: <2013-11-06 Wed 10:00 +1w>
+    ** TODO Something work-related
+    SCHEDULED: <2013-11-07 Thu 10:00 +1w>
+    ** TODO Something work-related
+    SCHEDULED: <2013-11-08 Fri 10:00 +1w>
+
+### Setup
+In `~/.emacs`:
+
+    (require 'tiny)
+    (tiny-setup-default)
diff --git a/packages/tiny/tiny-test.el b/packages/tiny/tiny-test.el
new file mode 100644
index 0000000..dc9c3b9
--- /dev/null
+++ b/packages/tiny/tiny-test.el
@@ -0,0 +1,214 @@
+;;; tiny-test.el --- Tests for Tiny
+
+;; Copyright (C) 2015  Free Software Foundation, Inc.
+
+;; Author: Oleh Krehel
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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.
+
+;; GNU Emacs 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 GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+(when (require 'undercover nil t)
+  (undercover "tiny.el"))
+
+(require 'tiny nil t)
+
+(defun with-text-value (txt fn &rest args)
+  "Return the result of (apply 'FN ARGS), in a temp buffer with TXT,
+with point at the end of TXT."
+  (with-temp-buffer
+    (insert txt)
+    (apply fn args)))
+
+(ert-deftest tiny-mapconcat-parse ()
+  (should (equal (with-text-value "m10" #'tiny-mapconcat-parse)
+                 '(nil nil "10" nil nil)))
+  (should (equal (with-text-value "m5%x" #'tiny-mapconcat-parse)
+                 '(nil nil "5" nil "%x")))
+  (should (equal (with-text-value "m5 10" #'tiny-mapconcat-parse)
+                 '("5" " " "10" nil nil)))
+  (should (equal (with-text-value "m5,10" #'tiny-mapconcat-parse)
+                 '("5" "," "10" nil nil)))
+  (should (equal (with-text-value "m5 10*xx" #'tiny-mapconcat-parse)
+                 '("5" " " "10" "(* x x)" nil)))
+  (should (equal (with-text-value "m5 10*xx%x" #'tiny-mapconcat-parse)
+                 '("5" " " "10" "(* x x)" "%x")))
+  (should (equal (with-text-value "m5 10*xx|0x%x" #'tiny-mapconcat-parse)
+                 '("5" " " "10" "(* x x)" "0x%x")))
+  (should (equal (with-text-value "m25+x?a%c" #'tiny-mapconcat-parse)
+                 '(nil nil "25" "(+ x 97)" "%c")))
+  (should (equal (with-text-value "m25+x?A%c" #'tiny-mapconcat-parse)
+                 '(nil nil "25" "(+ x 65)" "%c")))
+  (should (equal (with-text-value "m97,122stringx" #'tiny-mapconcat-parse)
+                 '("97" "," "122" "(string x)" nil)))
+  (should (equal (with-text-value "m97,122stringxx" #'tiny-mapconcat-parse)
+                 '("97" "," "122" "(string x x)" nil)))
+  (should (equal (with-text-value "m97,120stringxupcasex" 
#'tiny-mapconcat-parse)
+                 '("97" "," "120" "(string x (upcase x))" nil)))
+  (should (equal (with-text-value "m97,120stringxupcasex)x" 
#'tiny-mapconcat-parse)
+                 '("97" "," "120" "(string x (upcase x) x)" nil)))
+  (should (equal (with-text-value "m\\n;; 10|%(+ x x) and %(* x x) and %s" 
#'tiny-mapconcat-parse)
+                 '(nil "\\n;; " "10" nil "%(+ x x) and %(* x x) and %s")))
+  (should (equal (with-text-value "m10|%0.2f" #'tiny-mapconcat-parse)
+                 '(nil nil "10" nil "%0.2f"))))
+
+(ert-deftest tiny-extract-sexps ()
+  (should (equal (tiny-extract-sexps "expr1 %(+ x x), nothing %%  char %c, hex 
%x, and expr2 %(* x x), float %0.2f and sym %s")
+                 '("expr1 %s, nothing %%  char %c, hex %x, and expr2 %s, float 
%0.2f and sym %s"
+                   "(+ x x)" nil nil "(* x x)" nil nil)))
+  (should (equal (tiny-extract-sexps "m1\n5| (%c(+ x ?a -1)) %0.1f(* x 0.2)")
+                 '("m1
+5| (%c) %0.1f" "(+ x ?a -1)" "(* x 0.2)"))))
+
+(ert-deftest tiny-mapconcat ()
+  (should (equal (with-text-value "m10" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 "0 1 2 3 4 5 6 7 8 9 10"))
+  (should (equal (with-text-value "m5 10" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 "5 6 7 8 9 10"))
+  (should (equal (with-text-value "m5 10*xx" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 "25 36 49 64 81 100"))
+  (should (equal (with-text-value "m5 10*xx%x" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 "19 24 31 40 51 64"))
+  (should (equal (with-text-value "m5 10*xx|0x%x" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 "0x19 0x24 0x31 0x40 0x51 0x64"))
+  (should (equal (with-text-value "m25+x?a%c" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 "a b c d e f g h i j k l m n o p q r s t u v w x y z"))
+  (should (equal (with-text-value "m25+x?A%c" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"))
+  (should (equal (with-text-value "m97,122(string x)" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z"))
+  (should (equal (with-text-value "m97,122stringxx" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 
"aa,bb,cc,dd,ee,ff,gg,hh,ii,jj,kk,ll,mm,nn,oo,pp,qq,rr,ss,tt,uu,vv,ww,xx,yy,zz"))
+  (should (equal (with-text-value "m97,120stringxupcasex" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 
"aA,bB,cC,dD,eE,fF,gG,hH,iI,jJ,kK,lL,mM,nN,oO,pP,qQ,rR,sS,tT,uU,vV,wW,xX"))
+  (should (equal (with-text-value "m97,120stringxupcasex)x" (lambda()(eval 
(read (tiny-mapconcat)))))
+                 
"aAa,bBb,cCc,dDd,eEe,fFf,gGg,hHh,iIi,jJj,kKk,lLl,mMm,nNn,oOo,pPp,qQq,rRr,sSs,tTt,uUu,vVv,wWw,xXx"))
+  (should (equal (with-text-value "m10|%(+ x x) and %(* x x) and %s" 
(lambda()(eval (read (tiny-mapconcat)))))
+                 "0 and 0 and 0 2 and 1 and 1 4 and 4 and 2 6 and 9 and 3 8 
and 16 and 4 10 and 25 and 5 12 and 36 and 6 14 and 49 and 7 16 and 64 and 8 18 
and 81 and 9 20 and 100 and 10"))
+  (should (equal (with-text-value "m10*2+3x" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 "6 8 10 12 14 16 18 20 22 24 26"))
+  (should (equal (with-text-value "m10expx" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 "1.0 2.718281828459045 7.38905609893065 20.085536923187668 
54.598150033144236 148.4131591025766 403.4287934927351 1096.6331584284585 
2980.9579870417283 8103.083927575384 22026.465794806718"))
+  (should (equal (with-text-value "m5 20expx%014.2f" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 "00000000148.41 00000000403.43 00000001096.63 00000002980.96 
00000008103.08 00000022026.47 00000059874.14 00000162754.79 00000442413.39 
00001202604.28 00003269017.37 00008886110.52 00024154952.75 00065659969.14 
00178482300.96 00485165195.41"))
+  (should (equal (with-text-value "m, 7|0x%02x" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 "0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07"))
+  (should (equal (with-text-value "m1\\n14|*** TODO 
http://emacsrocks.com/e%02d.html"; (lambda()(eval (read (tiny-mapconcat)))))
+                 "*** TODO http://emacsrocks.com/e01.html
+*** TODO http://emacsrocks.com/e02.html
+*** TODO http://emacsrocks.com/e03.html
+*** TODO http://emacsrocks.com/e04.html
+*** TODO http://emacsrocks.com/e05.html
+*** TODO http://emacsrocks.com/e06.html
+*** TODO http://emacsrocks.com/e07.html
+*** TODO http://emacsrocks.com/e08.html
+*** TODO http://emacsrocks.com/e09.html
+*** TODO http://emacsrocks.com/e10.html
+*** TODO http://emacsrocks.com/e11.html
+*** TODO http://emacsrocks.com/e12.html
+*** TODO http://emacsrocks.com/e13.html
+*** TODO http://emacsrocks.com/e14.html";))
+  (should (equal (with-text-value "m1\\n10|convert img%s.jpg -monochrome 
-resize 50%% -rotate 180 img%s_mono.pdf" (lambda()(eval (read 
(tiny-mapconcat)))))
+                 "convert img1.jpg -monochrome -resize 50% -rotate 180 
img1_mono.pdf
+convert img2.jpg -monochrome -resize 50% -rotate 180 img2_mono.pdf
+convert img3.jpg -monochrome -resize 50% -rotate 180 img3_mono.pdf
+convert img4.jpg -monochrome -resize 50% -rotate 180 img4_mono.pdf
+convert img5.jpg -monochrome -resize 50% -rotate 180 img5_mono.pdf
+convert img6.jpg -monochrome -resize 50% -rotate 180 img6_mono.pdf
+convert img7.jpg -monochrome -resize 50% -rotate 180 img7_mono.pdf
+convert img8.jpg -monochrome -resize 50% -rotate 180 img8_mono.pdf
+convert img9.jpg -monochrome -resize 50% -rotate 180 img9_mono.pdf
+convert img10.jpg -monochrome -resize 50% -rotate 180 img10_mono.pdf"))
+  (should (equal (with-text-value "m\\n;; 16list*xxx)*xx%s:%s:%s" 
(lambda()(eval (read (tiny-mapconcat)))))
+                 "0:0:0
+;; 1:1:1
+;; 8:4:2
+;; 27:9:3
+;; 64:16:4
+;; 125:25:5
+;; 216:36:6
+;; 343:49:7
+;; 512:64:8
+;; 729:81:9
+;; 1000:100:10
+;; 1331:121:11
+;; 1728:144:12
+;; 2197:169:13
+;; 2744:196:14
+;; 3375:225:15
+;; 4096:256:16"))
+  (should (equal (with-text-value "m\\n8|**** TODO Learning from Data Week %(+ 
x 2)\\nSCHEDULED: <%(date \"Oct 7\" (* x 7))> DEADLINE: <%(date \"Oct 14\" (* x 
7))>"
+                   (lambda()(eval (read (tiny-mapconcat)))))
+                 "**** TODO Learning from Data Week 2
+SCHEDULED: <2015-10-07 Wed> DEADLINE: <2015-10-14 Wed>
+**** TODO Learning from Data Week 3
+SCHEDULED: <2015-10-14 Wed> DEADLINE: <2015-10-21 Wed>
+**** TODO Learning from Data Week 4
+SCHEDULED: <2015-10-21 Wed> DEADLINE: <2015-10-28 Wed>
+**** TODO Learning from Data Week 5
+SCHEDULED: <2015-10-28 Wed> DEADLINE: <2015-11-04 Wed>
+**** TODO Learning from Data Week 6
+SCHEDULED: <2015-11-04 Wed> DEADLINE: <2015-11-11 Wed>
+**** TODO Learning from Data Week 7
+SCHEDULED: <2015-11-11 Wed> DEADLINE: <2015-11-18 Wed>
+**** TODO Learning from Data Week 8
+SCHEDULED: <2015-11-18 Wed> DEADLINE: <2015-11-25 Wed>
+**** TODO Learning from Data Week 9
+SCHEDULED: <2015-11-25 Wed> DEADLINE: <2015-12-02 Wed>
+**** TODO Learning from Data Week 10
+SCHEDULED: <2015-12-02 Wed> DEADLINE: <2015-12-09 Wed>"))
+  (should (string= (with-text-value "m\\n4|**** TODO Classical Mechanics Week 
%(+ x 5)\\nSCHEDULED: <%(date \"Oct 15\" (* x 7))> DEADLINE: <%(date \"Oct 23\" 
(* x 7))>"
+                     (lambda()(eval (read (tiny-mapconcat)))))
+                   "**** TODO Classical Mechanics Week 5
+SCHEDULED: <2015-10-15 Thu> DEADLINE: <2015-10-23 Fri>
+**** TODO Classical Mechanics Week 6
+SCHEDULED: <2015-10-22 Thu> DEADLINE: <2015-10-30 Fri>
+**** TODO Classical Mechanics Week 7
+SCHEDULED: <2015-10-29 Thu> DEADLINE: <2015-11-06 Fri>
+**** TODO Classical Mechanics Week 8
+SCHEDULED: <2015-11-05 Thu> DEADLINE: <2015-11-13 Fri>
+**** TODO Classical Mechanics Week 9
+SCHEDULED: <2015-11-12 Thu> DEADLINE: <2015-11-20 Fri>"))
+  (should (string= (with-text-value "m7|%(expt 2 x)"
+                     (lambda()(eval (read (tiny-mapconcat)))))
+                   "1 2 4 8 16 32 64 128"))
+  (should (string= (with-text-value "m\\n25+?ax|(\"%c\")"
+                     (lambda()(eval (read (tiny-mapconcat)))))
+                   
"(\"a\")\n(\"b\")\n(\"c\")\n(\"d\")\n(\"e\")\n(\"f\")\n(\"g\")\n(\"h\")\n(\"i\")\n(\"j\")\n(\"k\")\n(\"l\")\n(\"m\")\n(\"n\")\n(\"o\")\n(\"p\")\n(\"q\")\n(\"r\")\n(\"s\")\n(\"t\")\n(\"u\")\n(\"v\")\n(\"w\")\n(\"x\")\n(\"y\")\n(\"z\")")))
+
+(ert-deftest tiny-replace-this-sexp ()
+  (should (equal (with-text-value "(mapcar (lambda (x) (* x x)) '(1 2 3))"
+                   (lambda()(goto-char 
16)(tiny-replace-this-sexp)(buffer-string)))
+                 "(1 4 9)"))
+  (should (equal (with-text-value "(mapcar (lambda (x) (* x x)) '(1 2 3))"
+                   (lambda()(goto-char 
2)(tiny-replace-this-sexp)(buffer-string)))
+                 "(1 4 9)")))
+
+(ert-deftest tiny-tokenize ()
+    (should (equal (tiny-tokenize "stringxx") "(string x x)"))
+    (should (equal (tiny-tokenize "*2+xxx") "(* 2 (+ x x x))"))
+    (should (equal (tiny-tokenize "*2+xxx") "(* 2 (+ x x x))"))
+    (should (equal (tiny-tokenize "*2+xx)x") "(* 2 (+ x x) x)"))
+    (should (equal (tiny-tokenize "string x") "(string x)"))
+    (should (equal (tiny-tokenize "(string x)") "(string x)"))
+    (should (equal (tiny-tokenize "string x)") "(string x)"))
+    (should (equal (tiny-tokenize "stringxupcasex)x") "(string x (upcase x) 
x)"))
+    (should (equal (tiny-tokenize "(stringxupcasex)x") "(string x (upcase x) 
x)"))
+    (should (equal (tiny-tokenize "(string xupcasex)x") "(string x (upcase x) 
x)"))
+    (should (equal (tiny-tokenize "(string x upcasex)x") "(string x (upcase x) 
x)"))
+    (should (equal (tiny-tokenize "(string x upcase x) x") "(string x (upcase 
x) x)"))
+    (should (equal (tiny-tokenize "(string x (upcase x) x") "(string x (upcase 
x) x)"))
+    (should (equal (tiny-tokenize "(string x (upcase x) x)") "(string x 
(upcase x) x)")))
+
+(provide 'tiny-test)
diff --git a/packages/tiny/tiny.el b/packages/tiny/tiny.el
new file mode 100644
index 0000000..78d44bb
--- /dev/null
+++ b/packages/tiny/tiny.el
@@ -0,0 +1,412 @@
+;;; tiny.el --- Quickly generate linear ranges in Emacs
+
+;; Copyright (C) 2013-2015  Free Software Foundation, Inc.
+
+;; Author: Oleh Krehel <address@hidden>
+;; URL: https://github.com/abo-abo/tiny
+;; Version: 0.1
+;; Keywords: convenience
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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.
+
+;; GNU Emacs 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 GNU Emacs.  If not, see <http://www.gnu.org/licenses/>
+
+;;; Commentary:
+;;
+;; To set it up, just bind e.g.:
+;;
+;;     (global-set-key (kbd "C-;") 'tiny-expand)
+;;
+;; Usage:
+;; This extension's main command is `tiny-expand'.
+;; It's meant to quickly generate linear ranges, e.g. 5, 6, 7, 8.
+;; Some elisp proficiency is an advantage, since you can transform
+;; your numeric range with an elisp expression.
+;;
+;; There's also some emphasis on the brevity of the expression to be
+;; expanded: e.g. instead of typing (+ x 2), you can do +x2.
+;; You can still do the full thing, but +x2 would save you some
+;; key strokes.
+;;
+;; You can test out the following snippets
+;; by positioning the point at the end of the expression
+;; and calling `tiny-expand' (default shortcut is C-;):
+;;
+;; m10
+;; m5 10
+;; m5,10
+;; m5 10*xx
+;; m5 10*xx%x
+;; m5 10*xx|0x%x
+;; m25+x?a%c
+;; m25+x?A%c
+;; m97,122(string x)
+;; m97,122stringxx
+;; m97,120stringxupcasex
+;; m97,120stringxupcasex)x
+;; m\n;; 10|%(+ x x) and %(* x x) and %s
+;; m10*2+3x
+;; m\n;; 10expx
+;; m5\n;; 20expx%014.2f
+;; m7|%(expt 2 x)
+;; m, 7|0x%02x
+;; m10|%0.2f
+;; m1\n14|*** TODO http://emacsrocks.com/e%02d.html
+;; m1\n10|convert img%s.jpg -monochrome -resize 50%% -rotate 180 img%s_mono.pdf
+;; (setq foo-list '(m1 11+x96|?%c))
+;; m1\n10listx+x96|convert img%s.jpg -monochrome -resize 50%% -rotate 180 
img%c_mono.pdf
+;; m1\n10listxnthxfoo-list|convert img%s.jpg -monochrome -resize 50%% -rotate 
180 img%c_mono.pdf
+;; m\n;; 16list*xxx)*xx%s:%s:%s
+;; m\n8|**** TODO Learning from Data Week %(+ x 2) \nSCHEDULED: <%(date "Oct 
7" (* x 7))> DEADLINE: <%(date "Oct 14" (* x 7))>
+;;
+;; As you might have guessed, the syntax is as follows:
+;; m[<range start:=0>][<separator:= >]<range end>[Lisp expr]|[format expr]
+;;
+;; x is the default var in the elisp expression.  It will take one by one
+;; the value of all numbers in the range.
+;;
+;; | means that elisp expr has ended and format expr has begun.
+;; It can be omitted if the format expr starts with %.
+;; The keys are the same as for format.
+;; In addition %(sexp) forms are allowed.  The sexp can depend on x.
+;;
+;; Note that multiple % can be used in the format expression.
+;; In that case:
+;; * if the Lisp expression returns a list, the members of this list
+;;   are used in the appropriate place.
+;; * otherwise, it's just the result of the expression repeated as
+;;   many times as necessary.
+
+;;; Code:
+
+(eval-when-compile
+  (require 'cl))
+(require 'help-fns)
+(require 'org)
+
+(defvar tiny-beg nil
+  "Last matched snippet start position.")
+
+(defvar tiny-end nil
+  "Last matched snippet end position.")
+
+;;;###autoload
+(defun tiny-expand ()
+  "Expand current snippet.
+It polls the expander functions one by one
+if they can expand the thing at point.
+First one to return a string succeeds.
+These functions are expected to set `tiny-beg' and `tiny-end'
+to the bounds of the snippet that they matched.
+At the moment, only `tiny-mapconcat' is supported.
+`tiny-mapconcat2' should be added to expand rectangles."
+  (interactive)
+  (let ((str (tiny-mapconcat)))
+    (when str
+      (delete-region tiny-beg tiny-end)
+      (insert str)
+      (tiny-replace-this-sexp))))
+
+(defun tiny-setup-default ()
+  "Setup shortcuts."
+  (global-set-key (kbd "C-;") 'tiny-expand))
+
+;;;###autoload
+(defun tiny-replace-this-sexp ()
+  "Eval and replace the current sexp.
+On error go up list and try again."
+  (interactive)
+  (if (region-active-p)
+      (let ((s (buffer-substring-no-properties
+                (region-beginning)
+                (region-end))))
+        (delete-region (region-beginning)
+                       (region-end))
+        (insert (format "%s" (eval (read s)))))
+    (catch 'success
+      (while t
+        (ignore-errors
+          (unless (looking-back ")")
+            (error "Bad location"))
+          (let ((sexp (preceding-sexp)))
+            (if (eq (car sexp) 'lambda)
+                (error "Lambda evaluates to itself")
+              (let ((value (eval sexp)))
+                (kill-sexp -1)
+                (insert (format "%s" value))
+                (throw 'success t)))))
+        ;; if can't replace, go up list
+        (condition-case nil
+            (tiny-up-list)
+          (error
+           (message "reached the highest point, couldn't eval.")
+           (throw 'success nil)))))))
+
+(defun tiny-up-list ()
+  "An `up-list' that can exit from string.
+Must throw an error when can't go up further."
+  (interactive)
+  ;; check if inside string
+  (let ((p (nth 8 (syntax-ppss))))
+    (when (eq (char-after p) ?\")
+      ;; go to beginning for string
+      (goto-char p)))
+  (up-list))
+
+(defun tiny-mapconcat ()
+  "Format output of `tiny-mapconcat-parse'.
+Defaults are used in place of null values."
+  (let ((parsed (tiny-mapconcat-parse)))
+    (when parsed
+      (let* ((n1 (or (nth 0 parsed) "0"))
+             (s1 (or (nth 1 parsed) " "))
+             (n2 (nth 2 parsed))
+             (expr (or (nth 3 parsed) "x"))
+             (lexpr (read expr))
+             (n-have (if (and (listp lexpr) (eq (car lexpr) 'list))
+                         (1- (length lexpr))
+                       0))
+             (expr (if (zerop n-have) `(list ,lexpr) lexpr))
+             (n-have (if (zerop n-have) 1 n-have))
+             (tes (tiny-extract-sexps (or (nth 4 parsed) "%s")))
+             (fmt (car tes))
+             (n-need (cl-count nil (cdr tes)))
+             (idx -1)
+             (format-expression
+              (concat "(mapconcat (lambda(x) (let ((lst %s)) (format %S "
+                      (mapconcat (lambda (x)
+                                   (or x
+                                       (if (>= (1+ idx) n-have)
+                                           "x"
+                                         (format "(nth %d lst)" (incf idx)))))
+                                 (cdr tes)
+                                 " ")
+                      ")))(number-sequence %s %s) \"%s\")")))
+        (unless (>= (read n1) (read n2))
+          (format
+           format-expression
+           expr
+           (replace-regexp-in-string "\\\\n" "\n" fmt)
+           n1
+           n2
+           s1))))))
+
+(defconst tiny-format-str
+  (let ((flags "[+ #-0]\\{0,1\\}")
+        (width "[0-9]*")
+        (precision "\\(?:\\.[0-9]+\\)?")
+        (character "[sdoxXefgcS]?"))
+    (format "\\(%s%s%s%s\\)("
+            flags width precision character)))
+
+(defun tiny-extract-sexps (str)
+  "Return (STR & FORMS).
+Each element of FORMS corresponds to a `format'-style % form in STR.
+
+  * %% forms are skipped
+  * %(sexp) is replaced with %s in STR, and put in FORMS
+  * the rest of forms are untouched in STR, and put as nil in FORMS"
+  (let ((start 0)
+        forms beg fexp)
+    (condition-case nil
+        (while (setq beg (string-match "%" str start))
+          (setq start (1+ beg))
+
+          (cond ((= ?% (aref str (1+ beg)))
+                 (incf start))
+
+                ((and (eq beg (string-match tiny-format-str str beg))
+                      (setq fexp (match-string-no-properties 1 str)))
+                 (incf beg (length fexp))
+                 (destructuring-bind (sexp . end)
+                     (read-from-string str beg)
+                   (push
+                    (replace-regexp-in-string "(date" "(tiny-date"
+                                              (substring str beg end))
+                    forms)
+                   (setq str (concat (substring str 0 beg)
+                                     (if (string= fexp "%") "s" "")
+                                     (substring str end)))))
+                (t (push nil forms))))
+      (error (message "Malformed sexp: %s" (substring str beg))))
+    (cons str (nreverse forms))))
+
+(defun tiny-mapconcat-parse ()
+  "Try to match a snippet of this form:
+m[START][SEPARATOR]END[EXPR]|[FORMAT]
+
+* START     - integer (defaults to 0)
+* SEPARATOR - string  (defaults to \" \")
+* END       - integer (required)
+* EXPR      - Lisp expression: function body with argument x (defaults to x)
+  Parens are optional if it's unambiguous:
+  - `(* 2 (+ x 3))'  <-> *2+x3
+  - `(exp x)'        <-> expx
+  A closing paren may be added to resolve ambiguity:
+  - `(* 2 (+ x 3) x) <-> *2+x3)
+* FORMAT    - string, `format'-style (defaults to \"%s\")
+  | separator can be omitted if FORMAT starts with %.
+
+Return nil if nothing was matched, otherwise
+ (START SEPARATOR END EXPR FORMAT)"
+  (let ((case-fold-search nil)
+        n1 s1 n2 expr fmt str
+        n-uses)
+    (when (catch 'done
+            (cond
+              ;; either start with a number
+              ((looking-back "\\bm\\(-?[0-9]+\\)\\([^\n]*?\\)")
+               (setq n1 (match-string-no-properties 1)
+                     str (match-string-no-properties 2)
+                     tiny-beg (match-beginning 0)
+                     tiny-end (match-end 0))
+               (when (zerop (length str))
+                 (setq n2 n1
+                       n1 nil)
+                 (throw 'done t)))
+              ;; else capture the whole thing
+              ((looking-back "\\bm\\([^%|\n]*[0-9][^\n]*\\)")
+               (setq str (match-string-no-properties 1)
+                     tiny-beg (match-beginning 0)
+                     tiny-end (match-end 0))
+               (when (zerop (length str))
+                 (throw 'done nil)))
+              (t (throw 'done nil)))
+            ;; at this point, `str' should be either [sep]<num>[expr][fmt]
+            ;; or [expr][fmt]
+            ;;
+            ;; First, try to match [expr][fmt]
+            (string-match "^\\(.*?\\)|?\\(%.*\\)?$" str)
+            (setq expr (match-string-no-properties 1 str))
+            (setq fmt (match-string-no-properties 2 str))
+            ;; If it's a valid expression, we're done
+            (when (setq expr (tiny-tokenize expr))
+              (setq n2 n1
+                    n1 nil)
+              (throw 'done t))
+            ;; at this point, `str' is [sep]<num>[expr][fmt]
+            (if (string-match "^\\([^\n0-9]*?\\)\\(-?[0-9]+\\)\\(.*\\)?$" str)
+                (setq s1 (match-string-no-properties 1 str)
+                      n2 (match-string-no-properties 2 str)
+                      str (match-string-no-properties 3 str))
+              ;; here there's only n2 that was matched as n1
+              (setq n2 n1
+                    n1 nil))
+            ;; match expr_fmt
+            (unless (zerop (length str))
+              (if (or (string-match "^\\([^\n%|]*?\\)|\\([^\n]*\\)?$" str)
+                      (string-match "^\\([^\n%|]*?\\)\\(%[^\n]*\\)?$" str))
+                  (progn
+                    (setq expr (tiny-tokenize (match-string-no-properties 1 
str)))
+                    (setq fmt (match-string-no-properties 2 str)))
+                (error "Couldn't match %s" str)))
+            t)
+      (when (equal expr "")
+        (setq expr nil))
+      (list n1 s1 n2 expr fmt))))
+
+;; TODO: check for arity: this doesn't work: exptxy
+(defun tiny-tokenize (str)
+  "Transform shorthand Lisp expression STR to proper Lisp."
+  (if (equal str "")
+      ""
+    (ignore-errors
+      (let ((i 0) (j 1)
+            (len (length str))
+            sym s out allow-spc
+            (n-paren 0)
+            (expect-fun t))
+        (while (< i len)
+          (setq s (substring str i j))
+          (when (cond
+                  ((string= s "x")
+                   (push s out)
+                   (push " " out))
+                  ((string= s " ")
+                   (if allow-spc
+                       t
+                     (error "Unexpected \" \"")))
+                  ;; special syntax to read chars
+                  ((string= s "?")
+                   (setq s (format "%s" (read (substring str i (incf j)))))
+                   (push s out)
+                   (push " " out))
+                  ((string= s ")")
+                   ;; expect a close paren only if it's necessary
+                   (if (>= n-paren 0)
+                       (decf n-paren)
+                     (error "Unexpected \")\""))
+                   (when (string= (car out) " ")
+                     (pop out))
+                   (push ")" out)
+                   (push " " out))
+                  ((string= s "(")
+                   ;; open paren is used sometimes
+                   ;; when there are numbers in the expression
+                   (setq expect-fun t)
+                   (incf n-paren)
+                   (push "(" out))
+                  ((progn (setq sym (intern-soft s))
+                          (cond
+                            ;; general functionp
+                            ((not (eq t (help-function-arglist sym)))
+                             (setq expect-fun)
+                             (setq allow-spc t)
+                             ;; (when (zerop n-paren) (push "(" out))
+                             (unless (equal (car out) "(")
+                               (push "(" out)
+                               (incf n-paren))
+                             t)
+                            ((and sym (boundp sym) (not expect-fun))
+                             t)))
+                   (push s out)
+                   (push " " out))
+                  ((numberp (read s))
+                   (let* ((num (string-to-number (substring str i)))
+                          (num-s (format "%s" num)))
+                     (push num-s out)
+                     (push " " out)
+                     (setq j (+ i (length num-s)))))
+                  (t
+                   (incf j)
+                   nil))
+            (setq i j)
+            (setq j (1+ i))))
+        ;; last space
+        (when (string= (car out) " ")
+          (pop out))
+        (concat
+         (apply #'concat (nreverse out))
+         (make-string n-paren ?\)))))))
+
+(defun tiny-date (s &optional shift)
+  "Return date representation of S.
+`org-mode' format is used.
+Optional SHIFT argument is the integer amount of days to shift."
+  (let* ((ct (decode-time (current-time)))
+         (time (apply 'encode-time
+                      (org-read-date-analyze
+                       s nil
+                       ct)))
+         (formatter
+          (if (equal (cl-subseq ct 1 3)
+                     (cl-subseq (decode-time time) 1 3))
+              "%Y-%m-%d %a"
+            "%Y-%m-%d %a %H:%M")))
+    (when shift
+      (setq time (time-add time (days-to-time shift))))
+    (format-time-string formatter time)))
+
+(provide 'tiny)
+;;; tiny.el ends here



reply via email to

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