From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp0.migadu.com ([2001:41d0:403:4876::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms8.migadu.com with LMTPS id mEI/GAgY1mXJXAAAqHPOHw:P1 (envelope-from ) for ; Wed, 21 Feb 2024 16:34:32 +0100 Received: from aspmx1.migadu.com ([2001:41d0:403:4876::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp0.migadu.com with LMTPS id mEI/GAgY1mXJXAAAqHPOHw (envelope-from ) for ; Wed, 21 Feb 2024 16:34:32 +0100 X-Envelope-To: larch@yhetil.org Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=posteo.net header.s=2017 header.b=fRx2613a; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=posteo.net ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1708529406; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=xmLCO3VjV/3vBBdrTOSQBo5WKrk1D8QOCf3jyfX0h4o=; b=htdMiJTo2W1TxKI2StgC3hEWAgbf19X3/tnFjbIaSOzXVd319oSXuJFmxY4CuW2janY7n8 EwNB2qsqXucttkdcEamah9fVCSF9kA7Ew098ICQoNgAjrZ+G7RoLSgRdulHb8xfIIS3yKD 3lhh9a4/XVhp+yX2QfnMhV9kLXzk9IXrkhhUbYZW57DzdJA8SMvPI4Jr4GHUBCrBXUsf/6 e45jGdZRThO7tjhiaem1IXpCBoPyBhQ4zReR4+6FH3K2dRFYOmjW/YXzi+Dnb5NtL0Pt5P R1fQzF3DxZ5qhpsXOdTHJzGPQnizr5ddIs0mNUysITrAi5YOGoVNF8shIXqkkA== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=pass header.d=posteo.net header.s=2017 header.b=fRx2613a; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=posteo.net ARC-Seal: i=1; s=key1; d=yhetil.org; t=1708529406; a=rsa-sha256; cv=none; b=d2rVjwWetvZJdM6WTdo9WD6l8XYgozq+IVJrnzs4ZeXFixr818qqH60NCtJziXsKO+qwSW KF11WHQRUTxKfEjL1hWbsYb4tDm8YLr6muHOotFonSluo9frqWeE+FQXNVCRJ3rJzNmRhV jye5dPiyi9/nR+nmyyNkXXEEl4uQcX1S1r6PqOQAI9vVcpziulUKUM5YeYog2TnN3hvMoF KqEMj9VmxXkuxuA13Oq4yyg/xNgHRilcxAtwVrDHrbZhLAA4y2rdz+ioVx3s6F4dqvTk3a FU+BybN1BrZoyskDbEx+5vv6eCPQIuICY//uy+m3uv4gBKmc98sF8/v+MTEtIw== Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 93CFE7A799 for ; Wed, 21 Feb 2024 16:30:05 +0100 (CET) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rcoTC-0006GZ-3G; Wed, 21 Feb 2024 10:25:14 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rcoMq-0000sX-LC for emacs-orgmode@gnu.org; Wed, 21 Feb 2024 10:18:40 -0500 Received: from mout02.posteo.de ([185.67.36.66]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rcj2C-0004tH-QJ for emacs-orgmode@gnu.org; Wed, 21 Feb 2024 04:37:04 -0500 Received: from submission (posteo.de [185.67.36.169]) by mout02.posteo.de (Postfix) with ESMTPS id 9C1A5240103 for ; Wed, 21 Feb 2024 10:36:58 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.net; s=2017; t=1708508218; bh=KsMssTJAoy+PMAgfkQSxSH/CCQAsLCTCz8syjs1aCSo=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version:Content-Type: From; b=fRx2613asXZDyxZ7Iixc2GDUIlR5E+peB2X5s/zeQka09Jsw/vwcVqXkFTBYztqxp Oo1145GV7Z7wex6sUC9SUsQ46e5sEFgJa9fXyq48lKh1UHor+Cr7iBIMFOVWqYStVx CaSwmBGRLZUVOvHbpsS5zKRhl09tuMR8w0PqaOa3hAetSiZhDZMTo5nBPfsjlHc2t1 hOYA5qb1IHBKXnpRFp8vmuS818kA4cNBHkw4sNMkDEVXO/6n5gRc98de5HXTZqm19j n0ubIitevie4cb90td8ngm/HXOCvQ80Gy3gEDdDDuEQe2FRzq2Uvx/eyweaDC5h2A9 tOz++xFZTkMUw== Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4TfrlF2vbtz9rxK; Wed, 21 Feb 2024 10:36:57 +0100 (CET) From: Ihor Radchenko To: gerard.vermeulen@posteo.net Cc: Emacs orgmode , emacs-orgmode-bounces+gerard.vermeulen=posteo.net@gnu.org Subject: Re: [PATCH] org-babel-demarcate-block: split using element API In-Reply-To: References: <7e41f9b6e9026a404e256f33371e974c@posteo.net> <87v88a78qd.fsf@localhost> <7f23ea966f0f0aad3fad29b0b96864f3@posteo.net> <87y1d55fc6.fsf@localhost> <37fdcc4bfcf734c2e5ec439d40b4f7d8@posteo.net> <878r50yqmo.fsf@localhost> <442006329cfba666710c989aece5191a@posteo.net> <261356c53e857e0bc4b04f884366edfa@posteo.net> <87bk9uy31w.fsf@localhost> <680e4a4fccf4f3c5c8f51d8215257eff@posteo.net> <87a5p9uute.fsf@localhost> <87wmscrty4.fsf@localhost> <2c82fa1399ddaa52ebe5bb21e241fb5f@posteo.net> <87o7dll7vn.fsf@localhost> <874je4hjmr.fsf@localhost> Date: Wed, 21 Feb 2024 09:40:36 +0000 Message-ID: <87frxmtasr.fsf@localhost> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Received-SPF: pass client-ip=185.67.36.66; envelope-from=yantar92@posteo.net; helo=mout02.posteo.de X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: emacs-orgmode-bounces+larch=yhetil.org@gnu.org X-Migadu-Flow: FLOW_IN X-Migadu-Country: US X-Migadu-Scanner: mx10.migadu.com X-Migadu-Spam-Score: -7.62 X-Spam-Score: -7.62 X-Migadu-Queue-Id: 93CFE7A799 X-TUID: BEskf2LWBkyf --=-=-= Content-Type: text/plain gerard.vermeulen@posteo.net writes: >>> Still failing on my side (when running tests non-interactively from >>> command line). To fix the problem, please use the approach from >>> `test-org-list/indent-item': >>> >>> (transient-mark-mode 1) >>> (push-mark (point) t t) >>> >>> Instead of (set-mark-command nil) >> >> Gerard, may I know if you had a chance to look into my comments? > > I think that I have addressed this particular comment. Not really. In any case, see the attached updated patch with my suggestion incorporated. > However, I am confused by your comments concerning this example > #+begin_src emacs-lisp > '(1 2 3) > '(1 2 3) > #+end_src > since it breaks my patch as well as main in the sense that such examples > can generate three blocks with invalid code. > I think there is in general no way to protect a user against bad > selections > for splitting by demarcation. Fair. > Secondly, I see (saw) sometimes Org emitting warnings with backtraces > starting from my patch. I think the warning may be due either to a > mistake > on my side or a bug in Org, but I am not sure. May you please provide more details? --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=0001-org-babel-demarcate-block-split-using-element-API.patch >From dc71a916c829e979c0728f95bfefe54b1cfa3887 Mon Sep 17 00:00:00 2001 Message-ID: From: Gerard Vermeulen Date: Thu, 11 Jan 2024 20:20:01 +0100 Subject: [PATCH] org-babel-demarcate-block: split using element API * lisp/ob-babel.el (org-babel-demarcate-block): Modify a copy of (org-element-at-point) to replace the old source block with 2 or 3 new modified copies by means of `org-element-interpret-data'. The 1st source block contains the text from the body of the old block before point or region, the 2nd block contains the body text after point or body text within region, and in case of region, the 3rd block contains the text after region. The caption and the name are deleted from the 1 or 2 blocks below the upper source block. Indent all blocks immediately after insertion (pitfall, see link). Use :post-blank to control white lines between inserted blocks. Leave point at the last inserted block. Take care to preserve (current-column) of text after point (and mark) in the 2nd (and 3rd) block. Trying to split when point or region is not within the body of the old source block raises an user-error. * lisp/ob-babel (org-get-src-block-info): add the "within blank lines after a source block" condition to the doc-string to match it with the doc-string of and a comment in `org-babel-demarcate-block'. * testing/lisp/test-ob.el (test-ob/demarcate-block-split-duplication) (test-ob/demarcate-block-split-prefix-point) (test-ob/demarcate-block-split-prefix-region) (test-ob/demarcate-block-split-user-errors) (test-ob/demarcate-block-wrap-point) (test-ob/demarcate-block-wrap-region): New tests to check test cases that broke earlier versions of this patch. Link: https://list.orgmode.org/87ply6nyue.fsf@localhost/ --- lisp/ob-core.el | 94 +++++++++++----- testing/lisp/test-ob.el | 241 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 307 insertions(+), 28 deletions(-) diff --git a/lisp/ob-core.el b/lisp/ob-core.el index bfeac257b..e3110a3f1 100644 --- a/lisp/ob-core.el +++ b/lisp/ob-core.el @@ -73,10 +73,12 @@ (declare-function org-element-contents-end "org-element" (node)) (declare-function org-element-parent "org-element-ast" (node)) (declare-function org-element-type "org-element-ast" (node &optional anonymous)) (declare-function org-element-type-p "org-element-ast" (node &optional types)) +(declare-function org-element-interpret-data "org-element" (data)) (declare-function org-entry-get "org" (pom property &optional inherit literal-nil)) (declare-function org-escape-code-in-region "org-src" (beg end)) (declare-function org-forward-heading-same-level "org" (arg &optional invisible-ok)) (declare-function org-in-commented-heading-p "org" (&optional no-inheritance)) +(declare-function org-indent-block "org" ()) (declare-function org-indent-line "org" ()) (declare-function org-list-get-list-end "org-list" (item struct prevs)) (declare-function org-list-prevs-alist "org-list" (struct)) @@ -700,8 +702,9 @@ (defun org-babel-get-src-block-info (&optional no-eval datum) argument DATUM is provided, extract information from that parsed object instead. -Return nil if point is not on a source block. Otherwise, return -a list with the following pattern: +Return nil if point is not on a source block (blank lines after a +source block are considered a part of that source block). +Otherwise, return a list with the following pattern: (language body arguments switches name start coderef)" (let* ((datum (or datum (org-element-context))) @@ -2075,7 +2078,7 @@ (defun org-babel-mark-block () (goto-char (match-beginning 5))))) (defun org-babel-demarcate-block (&optional arg) - "Wrap or split the code in the region or on the point. + "Wrap or split the code in an active region or at point. With prefix argument ARG, also create a new heading at point. @@ -2085,41 +2088,76 @@ (defun org-babel-demarcate-block (&optional arg) region is not active then the point is demarcated. When called within blank lines after a code block, create a new code -block of the same language with the previous." +block of the same language as the previous." (interactive "P") (let* ((info (org-babel-get-src-block-info 'no-eval)) (start (org-babel-where-is-src-block-head)) ;; `start' will be nil when within space lines after src block. (block (and start (match-string 0))) - (headers (and start (match-string 4))) + (body-beg (and start (match-beginning 5))) + (body-end (and start (match-end 5))) (stars (concat (make-string (or (org-current-level) 1) ?*) " ")) (upper-case-p (and block (let (case-fold-search) (string-match-p "#\\+BEGIN_SRC" block))))) (if (and info start) ;; At src block, but not within blank lines after it. - (mapc - (lambda (place) - (save-excursion - (goto-char place) - (let ((lang (nth 0 info)) - (indent (make-string (org-current-text-indentation) ?\s))) - (when (string-match "^[[:space:]]*$" - (buffer-substring (line-beginning-position) - (line-end-position))) - (delete-region (line-beginning-position) (line-end-position))) - (insert (concat - (if (looking-at "^") "" "\n") - indent (if upper-case-p "#+END_SRC\n" "#+end_src\n") - (if arg stars indent) "\n" - indent (if upper-case-p "#+BEGIN_SRC " "#+begin_src ") - lang - (if (> (length headers) 1) - (concat " " headers) headers) - (if (looking-at "[\n\r]") - "" - (concat "\n" (make-string (current-column) ? ))))))) - (move-end-of-line 2)) - (sort (if (org-region-active-p) (list (mark) (point)) (list (point))) #'>)) + (let* ((copy (org-element-copy (org-element-at-point))) + (before (org-element-begin copy)) + (beyond (org-element-end copy)) + (parts + (if (org-region-active-p) + (list body-beg (region-beginning) (region-end) body-end) + (list body-beg (point) body-end))) + (pads ;; To calculate left-side white-space padding. + (if (org-region-active-p) + (list (region-beginning) (region-end)) + (list (point)))) + (n (- (length parts) 2)) ;; 1 or 2 parts in `dolist' below. + ;; `post-blank' caches the property before setting it to 0. + (post-blank (org-element-property :post-blank copy))) + ;; Point or region are within body when parts is in increasing order. + (unless (apply #'<= parts) + (user-error "Select within the source block body to split it")) + (setq parts (mapcar (lambda (p) (buffer-substring (car p) (cdr p))) + (seq-mapn #'cons parts (cdr parts)))) + ;; Map positions to columns for white-space padding. + (setq pads (mapcar (lambda (p) (save-excursion + (goto-char p) + (current-column))) + pads)) + (push 0 pads) ;; The 1st part never requires white-space padding. + (setq parts (mapcar (lambda (p) (string-join + (list (make-string (car p) ?\s) + (cdr p)))) + (seq-mapn #'cons pads parts))) + (delete-region before beyond) + ;; Set `:post-blank' to 0. We take care of spacing between blocks. + (org-element-put-property copy :post-blank 0) + (org-element-put-property copy :value (car parts)) + (insert (org-element-interpret-data copy)) + ;; `org-indent-block' may see another `org-element' (e.g. paragraph) + ;; immediately after the block. Ensure to indent the inserted block + ;; and move point to its end. + (org-babel-previous-src-block 1) + (org-indent-block) + (goto-char (org-element-end (org-element-at-point))) + (org-element-put-property copy :caption nil) + (org-element-put-property copy :name nil) + ;; Insert the 2nd block, and the 3rd block when region is active. + (dolist (part (cdr parts)) + (org-element-put-property copy :value part) + (insert (if arg (concat stars "\n") "\n")) + (cl-decf n) + (when (= n 0) + ;; Use `post-blank' to reset the property of the last block. + (org-element-put-property copy :post-blank post-blank)) + (insert (org-element-interpret-data copy)) + ;; Ensure to indent the inserted block and move point to its end. + (org-babel-previous-src-block 1) + (org-indent-block) + (goto-char (org-element-end (org-element-at-point)))) + ;; Leave point at the last inserted block. + (goto-char (org-babel-previous-src-block 1))) (let ((start (point)) (lang (or (car info) ; Reuse language from previous block. (completing-read diff --git a/testing/lisp/test-ob.el b/testing/lisp/test-ob.el index 42c77ca56..0ecc8810a 100644 --- a/testing/lisp/test-ob.el +++ b/testing/lisp/test-ob.el @@ -2545,6 +2545,247 @@ (ert-deftest test-ob/import-elisp-from-file () (lambda (&rest _) (error "No warnings should occur")))) (org-babel-import-elisp-from-file (buffer-file-name)))))) +(ert-deftest test-ob/demarcate-block-split-duplicatigon () + "Test duplication of language, body, switches, and headers in splitting." + (let ((caption "#+caption: caption.") + (regexp (rx "#+caption: caption.")) + (org-adapt-indentation nil)) + (org-test-with-temp-text (format " +%s +#+header: :var edge=\"also duplicated\" +#+header: :wrap \"src any-spanish -n\" +#+name: Nobody +#+begin_src any-english -i -n :var here=\"duplicated\" :wrap \"src any-english -n\" + +above split + +below split + +#+end_src +do not org-indent-block text here +" caption) + (let ((wrap-val "src any-spanish -n") above below avars bvars) + (org-babel-demarcate-block) + (goto-char (point-min)) + (org-babel-next-src-block) ;; upper source block + (setq above (org-babel-get-src-block-info)) + (setq avars (org-babel--get-vars (nth 2 above))) + (org-babel-next-src-block) ;; lower source block + (setq below (org-babel-get-src-block-info)) + (setq bvars (org-babel--get-vars (nth 2 below))) + ;; duplicated multi-line header arguments: + (should (string= "also duplicated" (cdr (assq 'edge avars)))) + (should (string= "also duplicated" (cdr (assq 'edge bvars)))) + (should (string= wrap-val (cdr (assq :wrap (nth 2 above))))) + (should (string= wrap-val (cdr (assq :wrap (nth 2 below))))) + ;; duplicated language, other header arguments, and switches: + (should (string= "any-english" (nth 0 above))) + (should (string= "any-english" (nth 0 below))) + (should (string= "above split" (org-trim (nth 1 above)))) + (should (string= "below split" (org-trim (nth 1 below)))) + (should (string= "duplicated" (cdr (assq 'here avars)))) + (should (string= "duplicated" (cdr (assq 'here bvars)))) + (should (string= "-i -n" (nth 3 above))) + (should (string= "-i -n" (nth 3 below))) + ;; non-duplication of name and caption, which is not in above/below. + (should (string= "Nobody" (nth 4 above))) + (should-not (string= "" (nth 4 below))) + (goto-char (point-min)) + (should (re-search-forward regexp)) + (should-not (re-search-forward regexp nil 'noerror)))))) + +(ert-deftest test-ob/demarcate-block-split-prefix-point () + "Test prefix argument point splitting." + (let ((org-adapt-indentation t) + (org-edit-src-content-indentation 2) + (org-src-preserve-indentation nil) + (ok-col 11) + (stars "^\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*")) + (org-test-with-temp-text " +********** 10 stars with point between two lines + #+begin_src emacs-lisp + ;; to upper block + + ;; to lower block + #+end_src +" + (org-babel-demarcate-block 'a-prefix-arg) + (goto-char (point-min)) + (dolist (regexp `(,stars + "#\\+beg" ";; to upper block" "#\\+end" + ,stars + "#\\+beg" ";; to lower block" "#\\+end")) + (should (re-search-forward regexp)) + (goto-char (match-beginning 0)) + (cond ((string= regexp stars) + (should (= 0 (current-column)))) + ((string-prefix-p ";;" regexp) + (should (= (+ ok-col org-edit-src-content-indentation) + (current-column)))) + (t (should (= ok-col (current-column))))))))) + +(ert-deftest test-ob/demarcate-block-split-prefix-region () + "Test prefix argument region splitting." + (let ((org-adapt-indentation t) + (org-edit-src-content-indentation 2) + (org-src-preserve-indentation nil) + (ok-col 11) + (stars "^\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*") + (parts '("to upper block" "mark those words as region" "to lower block"))) + (org-test-with-temp-text (format " +********** 10 stars with region between two lines + #+header: :var b=\"also seen\" + #+begin_src any-language -i -n :var a=\"seen\" + %s + %s + %s + #+end_src +" (nth 0 parts) (nth 1 parts) (nth 2 parts)) + (let ((n 0) info vars) + (transient-mark-mode 1) + (push-mark (point) t t) + (search-forward (nth 1 parts)) + (exchange-point-and-mark) + ;; Check (mark). + (should (string= (nth 1 parts) + (buffer-substring + (- (mark) (length (nth 1 parts))) (mark)))) + (org-babel-demarcate-block 'a-prefix-argument) + (goto-char (point-min)) + (while (< n (length parts)) + (org-babel-next-src-block) + (setq info (org-babel-get-src-block-info)) + (setq vars (org-babel--get-vars (nth 2 info))) + (should (string= "any-language" (nth 0 info))) + (should (string= (nth n parts) (org-trim (nth 1 info)))) + (should (string= "seen" (cdr (assq 'a vars)))) + (should (string= "also seen" (cdr (assq 'b vars)))) + (should (string= "-i -n" (nth 3 info))) + (cl-incf n))) + (goto-char (point-min)) + (dolist (regexp `(,stars + "#\\+beg" ,(nth 0 parts) "#\\+end" + ,stars + "#\\+beg" ,(nth 1 parts) "#\\+end" + ,stars + "#\\+beg" ,(nth 2 parts) "#\\+end")) + (should (re-search-forward regexp)) + (goto-char (match-beginning 0)) + (cond ((string= regexp stars) + (should (= 0 (current-column)))) + ((memq regexp parts) + (should (= (+ ok-col org-edit-src-content-indentation) + (current-column)))) + (t (should (= ok-col (current-column))))))) + )) + +(ert-deftest test-ob/demarcate-block-split-user-errors () + "Test for `user-error's in splitting" + (let ((org-adapt-indentation t) + (org-edit-src-content-indentation 2) + (org-src-preserve-indentation)) + (let* ((caption "#+caption: caption.") + (within-body ";; within-body") + (below-block "# below block") + (template " +%s%s +#+begin_src emacs-lisp + + %s + +#+end_src + +%s%s +")) + ;; Test point at caption. + (org-test-with-temp-text + (format template "" caption within-body below-block "") + ;; Check (point). + (should (string= caption + (buffer-substring + (point) (+ (point) (length caption))))) + (should-error (org-babel-demarcate-block) :type 'user-error)) + ;; Test region from below the block (mark) to within the body (point). + (org-test-with-temp-text + (format template "" caption within-body below-block "") + ;; Set mark. + (transient-mark-mode 1) + (push-mark (point) t t) + ;; Check (mark). + (should (string= below-block + (buffer-substring + (- (mark) (length below-block)) (mark)))) + ;; Set point. + (should (search-backward within-body nil 'noerror)) + (goto-char (match-beginning 0)) + ;; Check (point). + (should (string= within-body + (buffer-substring + (point) (+ (point) (length within-body))))) + (should-error (org-babel-demarcate-block) :type 'user-error))))) + +(ert-deftest test-ob/demarcate-block-wrap-point () + "Test wrapping point in blank lines below a source block." + (org-test-with-temp-text " +#+begin_src any-language -i -n :var here=\"not duplicated\" +to upper block +#+end_src + +" + (let (info vars) + (org-babel-demarcate-block) + (goto-char (point-min)) + (org-babel-next-src-block) + (setq info (org-babel-get-src-block-info)) ;; upper source block info + (setq vars (org-babel--get-vars (nth 2 info))) + (should (string= "any-language" (nth 0 info))) + (should (string= "to upper block" (org-trim (nth 1 info)))) + (should (string= "not duplicated" (cdr (assq 'here vars)))) + (should (string= "-i -n" (nth 3 info))) + (org-babel-next-src-block) + (setq info (org-babel-get-src-block-info)) ;; lower source block info + (setq vars (org-babel--get-vars (nth 2 info))) + (should (string= "any-language" (nth 0 info))) + (should (string= "" (org-trim (nth 1 info)))) + (should-not vars) + (should (string= "" (nth 3 info)))))) + +(ert-deftest test-ob/demarcate-block-wrap-region () + "Test wrapping region in blank lines below a source block." + (let ((region-text "mark this line as region leaving point in blank lines")) + (org-test-with-temp-text (format " +#+begin_src any-language -i -n :var here=\"not duplicated\" +to upper block +#+end_src + +%s +" region-text) + (let (info vars) + (transient-mark-mode 1) + (push-mark (point) t t) + (search-forward region-text) + (exchange-point-and-mark) + ;; Check (mark). + (should (string= region-text + (buffer-substring + (- (mark) (length region-text)) (mark)))) + (org-babel-demarcate-block) + (goto-char (point-min)) + (org-babel-next-src-block) + (setq info (org-babel-get-src-block-info)) ;; upper source block info + (setq vars (org-babel--get-vars (nth 2 info))) + (should (string= "any-language" (nth 0 info))) + (should (string= "to upper block" (org-trim (nth 1 info)))) + (should (string= "not duplicated" (cdr (assq 'here vars)))) + (should (string= "-i -n" (nth 3 info))) + (org-babel-next-src-block) + (setq info (org-babel-get-src-block-info)) ;; lower source block info + (setq vars (org-babel--get-vars (nth 2 info))) + (should (string= "any-language" (nth 0 info))) + (should (string= region-text (org-trim (nth 1 info)))) + (should-not vars) + (should (string= "" (nth 3 info))))))) + (provide 'test-ob) ;;; test-ob ends here -- 2.43.0 --=-=-= Content-Type: text/plain -- Ihor Radchenko // yantar92, Org mode contributor, Learn more about Org mode at . Support Org development at , or support my work at --=-=-=--