6

Like, say I have a shell script source code block.

#+BEGIN_SRC shell
  echo 'Hello World!'
#+END_SRC

On export, I want the code text to pick up a dollar sign, so $ echo 'Hello World!'. But I only want this to happen to shell script blocks, not any other language blocks.

I know there are switches like -n that cause a code block to pick up line numbers on export, but it seems to me there’s no easy way to add a new switch or modify an old one to do what I want.

I hoped I could advise an appropriate function, but I can’t find the right function to hook into either.

5
  • You might be able to do it with an export filter (probably added to org-export-filter-src-block-functions), although I don't know how to select just shell src-blocks. See Advanced configuration in the org manual and the (admittedly opaque-unless-you-know-the-code) reference docs on Worg.
    – NickD
    Sep 25, 2018 at 18:37
  • Oh snap, that's right. I had forgotten about that. Unfortunately the filter-src-block functions act too late to recognize the source block language (they only receive the post-processed string value of the source block). But I'm pretty sure I can work something into the filter-parse-tree functions that walks the tree and modifies shell source blocks specifically. Sep 25, 2018 at 22:03
  • If you want that sweet sweet stack exchange karma, and wanna write an answer based on your comment, I'll accept it. Sep 25, 2018 at 22:03
  • Oh wait! Just found org-babel-map-src-blocks and the two hooks org-export-before-{processing,parsing}-hook. That's probably what I'm going to do. Sep 25, 2018 at 22:14
  • If you write a good answer (either method), I'll upvote it :-)
    – NickD
    Sep 25, 2018 at 22:37

3 Answers 3

6

As NickD points out, there is a hook that allows this. It falls under Advanced Configuration in the Org manual. Specifically,

The export process executes two hooks before the actual exporting begins. The first hook, org-export-before-processing-hook, runs before any expansions of macros, Babel code, and include keywords in the buffer. The second hook, org-export-before-parsing-hook, runs before the buffer is parsed.

I think the before-parsing hook is the better fit for this problem, but I’m not sure.

The manual doesn’t mention this, but the hook is actually an abnormal hook, so its functions must take an argument (the exporting backend).

It's also worth mentioning that these functions don't execute in the context of the org buffer you're exporting from. Like all export transformations, they run in a temporary buffer that starts with a copy of the org buffer's text.


To do the prefix insertion, use org-babel-map-src-blocks and take advantage of how it dynamically binds useful context variables like the source block language and the beginning / end of the code body.

(defun my-insert-shell-prompt (_backend)
  (org-babel-map-src-blocks nil         ; nil implies current buffer
    (let (;; capture macro-defined variables
         (lang lang)
         (beg-body beg-body)
         (end-body end-body)
         ;; other variables
         (shell-langs '("sh" "shell"))
         (prefix "$ "))
      (when (member lang shell-langs)
        (goto-char beg-body)
        (skip-chars-forward "\n\s-" end-body)
        (while (< (point) end-body)
          (insert prefix)
          (end-of-line)
          (skip-chars-forward "\n\s-" end-body)))))))

(add-hook 'org-export-before-parsing-hook #'my-insert-shell-prompt)
0

In case anyone else finds it useful, here's a slight modification to the great answer by @purple_arrows to skip the "$ " prefix for continuation lines (i.e., following lines that end in "\").

(defun org-export-insert-shell-prompt (_backend)
  (org-babel-map-src-blocks nil         ; nil implies current buffer
    (let (;; capture macro-defined variables
          (lang lang)
          (beg-body beg-body)
          (end-body end-body)
          ;; other variables
          (shell-langs '("sh" "shell"))
          (prefix "$ ")
          (is-contd-from-prev-line nil)) ; t if prev line ends in '\'; nil otherwise
      (when (member lang shell-langs)
        (goto-char beg-body)
        (skip-chars-forward "\n\s" end-body) ; not sure why OP included '-'
        (while (< (point) end-body)
          (if (not is-contd-from-prev-line) ; skip prefix if continuing previous line
                (insert prefix))
          (end-of-line)
          (if (eq ?\\ (char-after (- (point) 1))) ; check if statement continues in next line
              (setq is-contd-from-prev-line t)
            (setq is-contd-from-prev-line nil))
          (skip-chars-forward "\n\s" end-body))))))
0

Just to add complementary information, there is an org-babel-shell-names variable defined in ob-shell.el that you can use instead of defining your own list

(defun org-export-insert-shell-prompt (_backend)
  (org-babel-map-src-blocks nil         ; nil implies current buffer
    (let (...
          ...
          ...
          (shell-langs org-babel-shell-names)
          ...
          ...)
      (when ...
        ...))

It's value is: ("sh" "bash" "zsh" "fish" "csh" "ash" "dash" "ksh" "mksh" "posh") on doom-emacs

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.