emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: "Göktuğ Kayaalp" <self@gkayaalp.com>
To: Nicolas Goaziou <mail@nicolasgoaziou.fr>
Cc: Aaron Ecay <aaronecay@gmail.com>, emacs-orgmode@gnu.org
Subject: Re: Inheriting some local variables from source code block editing buffers
Date: Sat, 19 May 2018 00:48:53 +0300	[thread overview]
Message-ID: <ygmwow0ocsa.fsf@gkayaalp.com> (raw)
In-Reply-To: <ygm4lj8ix4k.fsf@gkayaalp.com> ("Göktuğ Kayaalp"'s message of "Tue, 15 May 2018 21:36:59 +0300")

[-- Attachment #1: Type: text/plain, Size: 1277 bytes --]

On 2018-05-15 21:36 +03, Göktuğ Kayaalp <self@gkayaalp.com> wrote:
> So, today I've started implementing a version of this that works like
> this:
>
> #+begin_example
>   #+property: edit-bindings /varlist/
>   * heading
>   :properties:
>   :edit_bindings: /varlist/
>   :end:
>   #+header: edit-bindings /varlist/
> #+end_example
>
>
> where scoping is like (x > y meaning x overrides y):
>
>   header line bindings > subtree bindings > #+property bindings before
>   the element
>
> I'll send a complete patch soon.

And here it is.  Please find the patch attached.  I have ran the entire
test suite against this, which completed w/ 6 failures, seemingly
unrelated (they fail on revision 58c9bedfd too); find the list below.  I
have tried to follow apparent conventions in each file w.r.t. the
copyright/authors lines, sorry if I modified them unnecessarily.

6 unexpected results:
   FAILED  test-ob/org-babel-remove-result--results-list
   FAILED  test-org-clock/clocktable/step
   FAILED  test-org/add-planning-info
   FAILED  test-org/clone-with-time-shift
   FAILED  test-org/deadline
   FAILED  test-org/schedule

-- 
İ. Göktuğ Kayaalp	<https://www.gkayaalp.com/>
			 024C 30DD 597D 142B 49AC
			 40EB 465C D949 B101 2427


[-- Attachment #2: The patch. --]
[-- Type: text/x-diff, Size: 22351 bytes --]

From b6d2b60730ceed68f46ef839c486e03764defdc7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C4=B0=2E=20G=C3=B6ktu=C4=9F=20Kayaalp?= <self@gkayaalp.com>
Date: Tue, 15 May 2018 20:34:28 +0300
Subject: [PATCH] Implement edit bindings feature

Enable defining local variable bindings to be applied when editing
source code.

* lisp/org-src.el (org-src--apply-edit-bindings)
(org-src--simplify-edit-bindings)
(org-src--parse-edit-bindings)
(org-src--edit-bindings-string)
(org-src--get-edit-bindings-for-subtree)
(org-src--get-edit-bindings-from-header)
(org-src--collect-global-edit-bindings)
(org-src--collect-edit-bindings-for-element): New functions.
(org-src-apply-risky-edit-bindings): New defcustom.
(org-src--edit-element):
* doc/org.texi (Editing source code): Add edit bindings.

* testing/lisp/test-org-src.el (test-org-src/edit-bindings-parser)
(test-org-src/collect-edit-bindings-for-element)
(test-org-src/edit-bindings-precedence-and-application)
(test-org-src/edit-bindings-use-cases): Add relevant tests.
---
 doc/org.texi                 |  43 +++++++++
 etc/ORG-NEWS                 |  15 +++
 lisp/org-src.el              | 223 +++++++++++++++++++++++++++++++++++++++----
 testing/lisp/test-org-src.el | 172 ++++++++++++++++++++++++++++++++-
 4 files changed, 436 insertions(+), 17 deletions(-)

diff --git a/doc/org.texi b/doc/org.texi
index 6aab1ba4e..c588152fd 100644
--- a/doc/org.texi
+++ b/doc/org.texi
@@ -15364,6 +15364,7 @@ Source code in the dialect of the specified language identifier.
 
 @vindex org-edit-src-auto-save-idle-delay
 @vindex org-edit-src-turn-on-auto-save
+@vindex org-src-apply-risky-edit-bindings
 @kindex C-c '
 @kbd{C-c '} for editing the current code block.  It opens a new major-mode
 edit buffer containing the body of the @samp{src} code block, ready for any
@@ -15421,6 +15422,48 @@ Emacs-Lisp languages.
                             ("python" (:background "#E5FFB8"))))
 @end lisp
 
+It is possible to define local variable bindings for these buffers using the
+@samp{edit-bindings} element header, the @samp{edit-bindings} buffer
+property, or the @samp{EDIT_BINDINGS} entry property.  All three can be used
+together, the bindings from the header override those of the subtree, and
+they both override the bindings from buffer properties.  The syntax is
+similar to that of @code{let} varlists, but a sole symbol means the
+variable's value is copied from the Org mode buffer.  Multiple uses of
+@samp{edit-bindings} headers and buffer properties are supported, and works
+like @code{let*}.  Entry property @samp{EDIT_BINDINGS} can not be repeated.
+Below is an example:
+
+@example
+# -*- fill-column: 65 -*-
+#+PROPERTY: edit-bindings '(fill-column (lexical-binding t))
+
+* Example section
+:PROPERTIES:
+:EDIT_BINDINGS: '((emacs-lisp-docstring-fill-column 60))
+:END:
+
+#+HEADER: edit-bindings '((lexical-binding nil))
+#+BEGIN_SRC elisp
+(defun hello ()
+  (message "Hello world!"))
+#+END_SRC
+
+* Another section
+#+BEGIN_SRC elisp
+(defun hello ()
+  (message "Byes world!"))
+#+END_SRC
+@end example
+
+In this example, when editing the first block, @code{lexical-binding} will be
+@code{nil}, and @code{emacs-lisp-docstring-fill-column} 60.  With the second
+one, they will be @code{t} and the variable's default value, respectively.
+@code{fill-column} will be 65 for both.
+
+Set @code{org-src-apply-risky-edit-bindings} for how risky local variables in
+these bindings are handled.  The default behaviour is to ask to the user
+whether or not to apply them.
+
 @node Exporting code blocks
 @section Exporting code blocks
 @cindex code block, exporting
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 21eaaece6..879240f31 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -95,6 +95,21 @@ document, use =shrink= value instead, or in addition to align:
 #+END_EXAMPLE
 
 ** New features
+*** Set local variables for editing blocks
+Bindings from =edit-bindings= header and buffer property, and
+=EDIT_BINDINGS= entry property are applied in Org Src buffers.  For
+example, when editing the following code block with
+~org-edit-special~:
+
+#+BEGIN_EXAMPLE
+#+header: edit-bindings '((lexical-binding t))
+#+BEGIN_SRC elisp
+;; some code
+#+END_SRC
+#+END_EXAMPLE
+
+in the source code editing buffer, ~lexical-binding~ is set to ~t~.
+
 *** Org Tempo may used for snippet expansion of structure template.
 See manual and the commentary section in ~org-tempo.el~ for details.
 *** Exclude unnumbered headlines from table of contents
diff --git a/lisp/org-src.el b/lisp/org-src.el
index b27e96cbc..a1b766813 100644
--- a/lisp/org-src.el
+++ b/lisp/org-src.el
@@ -1,10 +1,11 @@
 ;;; org-src.el --- Source code examples in Org       -*- lexical-binding: t; -*-
 ;;
-;; Copyright (C) 2004-2017 Free Software Foundation, Inc.
+;; Copyright (C) 2004-2018 Free Software Foundation, Inc.
 ;;
 ;; Author: Carsten Dominik <carsten at orgmode dot org>
 ;;	   Bastien Guerry <bzg@gnu.org>
 ;;         Dan Davison <davison at stats dot ox dot ac dot uk>
+;;         Göktuğ Kayaalp <self at gkayaalp.com>
 ;; Keywords: outlines, hypermedia, calendar, wp
 ;; Homepage: http://orgmode.org
 ;;
@@ -36,6 +37,7 @@
 (require 'org-compat)
 (require 'ob-keys)
 (require 'ob-comint)
+(require 'subr-x)
 
 (declare-function org-base-buffer "org" (buffer))
 (declare-function org-do-remove-indentation "org" (&optional n))
@@ -226,6 +228,26 @@ issued in the language major mode buffer."
   :version "24.1"
   :group 'org-babel)
 
+(defcustom org-src-apply-risky-edit-bindings 'ask
+  "What to do if an edit binding is a risky local variable.
+If this is nil, bindings that satisfy ‘risky-local-variable-p’
+are skipped, with a warning message.  Otherwise, its value should
+be a symbol telling how to thread them.  Possible values of this
+setting are:
+
+nil		Skip, warning the user via a message.
+skip-silent	Skip risky local varibles silently.
+ask		Prompt user for each variable.
+t		Apply the variable but show a warning.
+apply-silent	Apply risky local variables silently."
+  :group 'org-edit-structure
+  :risky t
+  :type '(radio
+	  (const :tag "Skip, warning the user via a message" nil)
+	  (const :tag "Skip risky local varibles silently" 'skip-silent)
+	  (const :tag "Prompt user for each variable" 'ask)
+	  (const :tag "Apply the variable but show a warning" t)
+	  (const :tag "Apply risky local variables silently" 'apply-silent)))
 
 \f
 ;;; Internal functions and variables
@@ -424,6 +446,168 @@ Assume point is in the corresponding edit buffer."
 	  (forward-line)))
       (buffer-string))))
 
+(defun org-src--apply-edit-bindings (simplified-bindings)
+  (pcase-dolist (`(,name . ,value) simplified-bindings)
+    (let ((prompt-apply
+	   (concat "Setting risky local variable ‘%S’"
+		   " in edit-special buffer, its value is: %S; Continue?"))
+	  (risky-message "%s risky local variable ‘%S’ in edit-special buffer.")
+	  (apply-binding (lambda () (set (make-local-variable name)
+					 (eval value)))))
+      (unless
+	  (and
+	   (risky-local-variable-p name)
+	   (cond ((or (and (eq org-src-apply-risky-edit-bindings 'ask)
+			   (y-or-n-p (format prompt-apply name value)))
+		      (eq org-src-apply-risky-edit-bindings 'apply-silent))
+		  (funcall apply-binding))
+		 (org-src-apply-risky-edit-bindings
+		  (prog1
+		      (funcall apply-binding)
+		    (message risky-message "Applied" name)))
+		 ((not org-src-apply-risky-edit-bindings)
+		  (message risky-message "Skipped" name))
+		 ((eq org-src-apply-risky-edit-bindings 'skip-silent))
+		 ('else
+		  (user-error
+		   "Unexpected value for ‘%S’, will not apply this or any more bindings."
+		   'org-src-apply-risky-edit-bindings))))
+	(funcall apply-binding)))))
+
+(defun org-src--simplify-edit-bindings (raw-bindings)
+  ;; The many uses of ‘nreverse’ is aimed at keeping the order the
+  ;; bindings are written, so that the effect of the previous binding
+  ;; can reliably be used by the following one.
+  ;;
+  ;; The deep copy of raw bindings should not be necessary for general
+  ;; use cases, but it's useful for the tests, and might come in handy
+  ;; if values are cached in the future.
+  (let* ((b (copy-tree raw-bindings))
+	 (elem (plist-get (plist-get b :element-bindings) :varlist))
+	 (subtree (plist-get (plist-get b :subtree-bindings) :varlist))
+	 (global (plist-get b :global-bindings))
+	 (resulting-bindings
+	  (nreverse
+	   (append
+	    (nreverse (apply #'append elem))
+	    (apply #'append
+		   (nreverse subtree)
+		   (mapcar (lambda (s)
+			     (nreverse (plist-get s :varlist)))
+			   (nreverse global))))))
+	 simplified-bindings)
+    (pcase-dolist (`(,name . ,value) resulting-bindings)
+      (setq simplified-bindings
+	    (append (cl-remove-if
+		     (lambda (x) (equal (car x) name))
+		     simplified-bindings)
+		    (list (cons name value)))))
+    simplified-bindings))
+
+(defun org-src--collect-edit-bindings-for-element ()
+  (let* ((element-bindings (org-src--get-edit-bindings-from-header))
+	 (subtree-bindings (org-src--get-edit-bindings-for-subtree))
+	 (global-bindings
+	  (save-excursion
+	    (save-restriction
+	      (narrow-to-region (point-min) (plist-get element-bindings :end))
+	      (org-src--collect-global-edit-bindings)))))
+    (list :element-bindings element-bindings
+	  :subtree-bindings subtree-bindings
+	  :global-bindings global-bindings)))
+
+(defun org-src--collect-global-edit-bindings ()
+  ;; XXX: is setting GRANULARITY to 'element a performance
+  ;; improvement, and does it cause any problems over just using the
+  ;; default 'object?
+  ;;
+  ;; Also, is it possible to not have to parse the entire buffer every
+  ;; time?
+  (org-element-map
+      (org-element-parse-buffer 'element)
+      'keyword
+    (lambda (keyword)
+      (cl-destructuring-bind
+	  (_1 (_2 type _3 value _4 pos-beg _5 pos-end &rest _6))
+	  keyword
+	(ignore _1 _2 _3 _4 _5 _6)
+	(when-let*
+	    ((sexp-str
+	      (and (string= type "PROPERTY")
+		   (org-src--edit-bindings-string value))))
+	  (list
+	   :varlist
+	   (org-src--parse-edit-bindings sexp-str pos-beg pos-end)
+	   :begin pos-beg :end pos-end))))))
+
+(defun org-src--get-edit-bindings-from-header ()
+  (let* ((element (org-element-at-point))
+	 (props (cadr element))
+	 (beg (plist-get props :begin))
+	 (end (plist-get props :end))
+	 (headers (org-element-property :header element))
+	 (bindings
+	  (nreverse
+	   (cl-mapcar
+	    (lambda (header)
+	      (when-let* ((sexp-str (org-src--edit-bindings-string header)))
+		(org-src--parse-edit-bindings sexp-str beg end)))
+	    headers))))
+    (list :varlist bindings :begin beg :end end)))
+
+(defun org-src--get-edit-bindings-for-subtree ()
+  (save-excursion
+    (when-let*
+	((entry-bindings
+	  (and (ignore-errors (org-back-to-heading t))
+	       (org-entry-get (point) "EDIT_BINDINGS" 'selective))))
+      (let ((beg (org-entry-beginning-position))
+	    (end (org-entry-end-position)))
+	(list
+	 :varlist
+	 ;; Use the string removing the initial quote character which
+	 ;; is required for consistency with #+headers, as without
+	 ;; them Babel causes errors.
+	 (org-src--parse-edit-bindings (substring entry-bindings 1) beg end)
+	 :begin beg :end end)))))
+
+(defun org-src--edit-bindings-string (property-value)
+  (let ((str
+	 (save-match-data
+	   ;; We include a quote in order to fool Babel, which parses
+	   ;; headers too.
+	   (and (string-match "^edit-bindings '(" property-value)
+		(string-trim (substring property-value (1- (match-end 0))))))))
+    (unless (and str (string-empty-p str))
+      str)))
+
+(defun org-src--parse-edit-bindings (sexp-str pos-beg pos-end)
+  ;; XXX: require cadr of the varlist items to be atoms, for security?
+  ;; Or prompt users?  Because otherwise there can be complete
+  ;; programs embedded in there.
+  (cl-destructuring-bind
+      (sexp . final-string-index)
+      (read-from-string sexp-str)
+    (when sexp
+      ;; Do not allow trailing stuff.
+      '(unless (= (1+ (length sexp-str)) final-string-index)
+	 (user-error "Junk after edit-bindings varlist at line %d"
+		     (line-number-at-pos pos-beg t))))
+    ;; XXX: Only allow static data, no function calls?
+    (cl-loop for varexp in sexp
+	     collect
+	     (pcase varexp
+	       ((pred null)
+		(ignore varexp))
+	       (`(,name ,value)
+		`(,name . ,value))
+	       ((pred symbolp)
+		`(,varexp
+		  . ',(buffer-local-value
+		       varexp (current-buffer))))
+	       (_ (user-error "Erroneous expression in varlist: %S"
+			      varexp))))))
+
 (defun org-src--edit-element
     (datum name &optional initialize write-back contents remote)
   "Edit DATUM contents in a dedicated buffer NAME.
@@ -513,21 +697,28 @@ Leave point in edit buffer."
 	(org-src-mode)
 	;; Move mark and point in edit buffer to the corresponding
 	;; location.
-	(if remote
-	    (progn
-	      ;; Put point at first non read-only character after
-	      ;; leading blank.
-	      (goto-char
-	       (or (text-property-any (point-min) (point-max) 'read-only nil)
-		   (point-max)))
-	      (skip-chars-forward " \r\t\n"))
-	  ;; Set mark and point.
-	  (when mark-coordinates
-	    (org-src--goto-coordinates mark-coordinates (point-min) (point-max))
-	    (push-mark (point) 'no-message t)
-	    (setq deactivate-mark nil))
-	  (org-src--goto-coordinates
-	   point-coordinates (point-min) (point-max)))))))
+	(prog1
+	    (if remote
+		(progn
+		  ;; Put point at first non read-only character after
+		  ;; leading blank.
+		  (goto-char
+		   (or (text-property-any (point-min) (point-max) 'read-only nil)
+		       (point-max)))
+		  (skip-chars-forward " \r\t\n"))
+	      ;; Set mark and point.
+	      (when mark-coordinates
+		(org-src--goto-coordinates mark-coordinates (point-min) (point-max))
+		(push-mark (point) 'no-message t)
+		(setq deactivate-mark nil))
+	      (org-src--goto-coordinates
+	       point-coordinates (point-min) (point-max)))
+	  ;; Apply edit-bindings.
+	  (when-let* ((edit-bindings
+		       (with-current-buffer (org-src--source-buffer)
+			    (org-src--collect-edit-bindings-for-element))))
+	    (org-src--apply-edit-bindings
+	     (org-src--simplify-edit-bindings edit-bindings))))))))
 
 
 \f
diff --git a/testing/lisp/test-org-src.el b/testing/lisp/test-org-src.el
index 86f08eccb..4406e5f02 100644
--- a/testing/lisp/test-org-src.el
+++ b/testing/lisp/test-org-src.el
@@ -1,8 +1,10 @@
 ;;; test-org-src.el --- tests for org-src.el
 
+;; Copyright (C) 2018  Göktuğ Kayaalp
 ;; Copyright (C) 2012-2015  Le Wang
 
-;; Author: Le Wang <l26wang at gmail dot com>
+;; Authors: Le Wang <l26wang at gmail dot com>
+;;          Göktuğ Kayaalp <self at gkayaalp.com>
 
 ;; This file is not part of GNU Emacs.
 
@@ -480,6 +482,174 @@ This is a tab:\t.
   (should (equal "#" (org-unescape-code-in-string "#")))
   (should (equal "," (org-unescape-code-in-string ","))))
 
+(ert-deftest test-org-src/edit-bindings-parser ()
+  "Test edit-bindings parser."
+  ;; ‘org-src--edit-bindings-string’
+  (should (null (org-src--edit-bindings-string "not-edit-bindings '(whatever)")))
+  (should (string= (org-src--edit-bindings-string "edit-bindings '()")
+		   "()"))
+  (should (string= (org-src--edit-bindings-string "edit-bindings '(fill-column)")
+		   "(fill-column)"))
+  (should (string=
+	   (org-src--edit-bindings-string "edit-bindings '((fill-column 80))")
+	   "((fill-column 80))"))
+  ;; ‘org-src--parse-edit-bindings’
+  (should (equal (org-src--parse-edit-bindings "((lexical-binding t))" 0 0)
+		 '((lexical-binding . t))))
+  (should (equal (org-src--parse-edit-bindings "(lexical-binding)" 0 0)
+		 '((lexical-binding . 'nil))))
+  ;; ‘org-src--collect-global-edit-bindings’
+  (org-test-with-temp-text
+      "<point>#+property: edit-bindings '()"
+    (should (null (plist-get (org-src--collect-global-edit-bindings) :varlist))))
+  (org-test-with-temp-text
+      "<point>#+property: edit-bindings '(())"
+    (should (null (plist-get (org-src--collect-global-edit-bindings) :varlist))))
+  (org-test-with-temp-text
+      "<point>#+property: edit-bindings '(major-mode)"
+    (let ((ret (car (org-src--collect-global-edit-bindings))))
+      (should (equal '((major-mode . 'org-mode)) (plist-get ret :varlist)))))
+  ;; ‘org-src--get-edit-bindings-from-header’
+  (org-test-with-temp-text
+      "<point>#+header: edit-bindings '(major-mode)
+#+BEGIN_EXPORT latex
+#+END_EXPORT"
+    (let ((ret (org-src--get-edit-bindings-from-header)))
+      (should (equal (plist-get ret :varlist)
+		     '(((major-mode . 'org-mode)))))))
+  (org-test-with-temp-text
+      "<point>#+header: edit-bindings '(major-mode)
+#+header: edit-bindings '((xxx 12))
+#+BEGIN_EXPORT latex
+#+END_EXPORT"
+    (let ((ret (org-src--get-edit-bindings-from-header)))
+      (should (equal (plist-get ret :varlist)
+		     '(((major-mode . 'org-mode))
+		       ((xxx . 12))))))))
+
+(ert-deftest test-org-src/collect-edit-bindings-for-element ()
+  "Collecting edit-bindings settings scoped to an element."
+  (org-test-with-temp-text
+      "<point>#+header: edit-bindings '(major-mode)
+#+BEGIN_SRC elisp
+#+END_SRC"
+    (cl-destructuring-bind
+	(_1 elem _2 subtree _3 global)
+	(org-src--collect-edit-bindings-for-element)
+      (should (equal (car (plist-get elem :varlist)) '((major-mode . 'org-mode))))
+      (should (null (plist-get subtree :varlist)))
+      (should (null (plist-get global :varlist)))))
+  (org-test-with-temp-text
+      "#+property: edit-bindings '((fill-column 38))
+<point>#+header: edit-bindings '(major-mode)
+#+BEGIN_SRC elisp
+#+END_SRC"
+    (cl-destructuring-bind
+	(_1 elem _2 subtree _3 global)
+	(org-src--collect-edit-bindings-for-element)
+      (should (equal (plist-get elem :varlist) '(((major-mode . 'org-mode)))))
+      (should (equal (plist-get (car global) :varlist) '((fill-column . 38))))
+      (should (null (plist-get subtree :varlist)))))
+  (org-test-with-temp-text
+      "#+property: edit-bindings '((fill-column 38))
+<point>#+header: edit-bindings '(major-mode)
+#+BEGIN_SRC elisp
+#+END_SRC
+#+property: edit-bindings '((lexical-let 'orly))"
+    (cl-destructuring-bind
+	(_1 elem _2 subtree _3 global)
+	(org-src--collect-edit-bindings-for-element)
+      (should (equal (plist-get elem :varlist) '(((major-mode . 'org-mode)))))
+      (should (equal (plist-get (car global) :varlist) '((fill-column . 38))))
+      (should (null subtree))))
+  (org-test-with-temp-text
+      "#+property: edit-bindings '((fill-column 38))
+* A subtree!
+:PROPERTIES:
+:EDIT_BINDINGS: '((fill-column 47))
+:END:
+<point>#+header: edit-bindings '(major-mode)
+#+BEGIN_SRC elisp
+#+END_SRC
+#+property: edit-bindings '((lexical-binding 'orly))"
+    (cl-destructuring-bind
+	(_1 elem _2 subtree _3 global)
+	(org-src--collect-edit-bindings-for-element)
+      (should (equal (plist-get elem :varlist) '(((major-mode . 'org-mode)))))
+      (should (equal (plist-get subtree :varlist) '((fill-column . 47))))
+      (should (equal (plist-get (car global) :varlist) '((fill-column . 38))))
+      (should
+       (not (equal (plist-get (car global) :varlist)
+		   '((lexical-binding . 'orly))))))))
+
+(ert-deftest test-org-src/edit-bindings-precedence-and-application ()
+  "Test handling of scope precedence, and application of bindings."
+  (let ((case1 (list :element-bindings '(:varlist (((a . 'b) (c . 'd))))
+		     :subtree-bindings '(:varlist ((c . 'e) (p . 'q)))
+		     :global-bindings  '((:varlist ((z . 'o) (k . 'f)))
+					 (:varlist ((q . 'u) (x . 'y))))))
+	(case1-expected
+	 '((z . 'o) (k . 'f) (q . 'u) (x . 'y) ;global
+	   (p . 'q)			   ;subtree
+	   (a . 'b) (c . 'd)		   ;element
+	   ))
+	(case2 (list :element-bindings '(:varlist (((a . 'b))))
+		     :global-bindings  '((:varlist ((a . 'z)))))))
+    ;; Precedence:
+    (should (equal (org-src--simplify-edit-bindings nil) nil))
+    (should
+     (equal (org-src--simplify-edit-bindings case1)
+	    case1-expected))
+    (should
+     (equal (org-src--simplify-edit-bindings case2)
+	    '((a . 'b))))
+    ;; Application:
+    (with-temp-buffer
+      (org-src--apply-edit-bindings
+       (org-src--simplify-edit-bindings case2))
+      (should (equal (buffer-local-value 'a (current-buffer)) 'b)))
+    (with-temp-buffer
+      (org-src--apply-edit-bindings
+       (org-src--simplify-edit-bindings case1))
+      (pcase-dolist (`(,var . ,val) case1-expected)
+	(should (equal (buffer-local-value var (current-buffer))
+		       (eval val)))))))
+
+(ert-deftest test-org-src/edit-bindings-use-cases ()
+  "Test possible uses of edit bindings feature."
+  (org-test-with-temp-text
+      "<point>#+header: edit-bindings '((xxx t))
+#+BEGIN_SRC elisp
+#+END_SRC"
+    (org-edit-special)
+    (should
+     (equal (buffer-local-value 'xxx (current-buffer)) t))
+    (org-edit-src-exit))
+  (org-test-with-temp-text
+      "#+property: edit-bindings '((xxx 12) major-mode)
+<point>#+header: edit-bindings '((xxx t))
+#+BEGIN_SRC elisp
+#+END_SRC
+#+property: edit-bindings '((xxx 13))"
+    (org-edit-special)
+    (should (equal (buffer-local-value 'xxx (current-buffer)) t))
+    (should (equal (buffer-local-value 'major-mode (current-buffer)) 'org-mode))
+    (org-edit-src-exit))
+  (org-test-with-temp-text
+      "#+property: edit-bindings '((xxx 12))
+* header
+:properties:
+:edit_bindings: '((zzz 34))
+:end:
+#+property: edit-bindings '((zzz 413))
+<point>#+header: edit-bindings '((xxx t))
+#+BEGIN_SRC elisp
+#+END_SRC
+#+property: edit-bindings '((xxx 13))"
+    (org-edit-special)
+    (should (equal (buffer-local-value 'xxx (current-buffer)) t))
+    (should (equal (buffer-local-value 'zzz (current-buffer)) 34))
+    (org-edit-src-exit)))
 
 (provide 'test-org-src)
 ;;; test-org-src.el ends here
-- 
2.11.0


  reply	other threads:[~2018-05-18 21:49 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-04-29 17:19 Inheriting some local variables from source code block editing buffers Göktuğ Kayaalp
2018-04-29 22:09 ` Bastien
2018-05-01  3:30   ` Göktuğ Kayaalp
2018-05-01  8:43     ` Nicolas Goaziou
2018-05-01  8:45       ` Nicolas Goaziou
2018-05-01 11:41       ` Göktuğ Kayaalp
2018-05-01 11:55         ` Nicolas Goaziou
     [not found]           ` <ygmvac7lcl1.fsf@gkayaalp.com>
2018-05-01 14:00             ` Nicolas Goaziou
2018-05-01 16:37           ` Aaron Ecay
     [not found]             ` <ygmin87kwua.fsf@gkayaalp.com>
2018-05-01 19:35               ` Aaron Ecay
2018-05-01 20:04                 ` Göktuğ Kayaalp
2018-05-01 20:53                   ` Aaron Ecay
2018-05-01 22:12                     ` Göktuğ Kayaalp
2018-05-01 22:19                       ` Aaron Ecay
2018-05-01 22:26                       ` Göktuğ Kayaalp
2018-05-02 10:16                         ` Nicolas Goaziou
2018-05-02 19:52                           ` Göktuğ Kayaalp
2018-05-01 11:45       ` Göktuğ Kayaalp
2018-05-14  5:44 ` Göktuğ Kayaalp
2018-05-14 12:13   ` Nicolas Goaziou
2018-05-14 16:34     ` Göktuğ Kayaalp
2018-05-14 16:47       ` Nicolas Goaziou
2018-05-15 18:36         ` Göktuğ Kayaalp
2018-05-18 21:48           ` Göktuğ Kayaalp [this message]
2018-05-19 12:26             ` Nicolas Goaziou
2018-05-21 14:20             ` Aaron Ecay
2018-05-14 13:33   ` Aaron Ecay
2018-05-14 16:46     ` Göktuğ Kayaalp

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ygmwow0ocsa.fsf@gkayaalp.com \
    --to=self@gkayaalp.com \
    --cc=aaronecay@gmail.com \
    --cc=emacs-orgmode@gnu.org \
    --cc=mail@nicolasgoaziou.fr \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).