How to reference a code block points in org mode? - emacs

I want to use libxml-parse-html-region to test some HTML extraction functions. But it only accepts region points as the input parameters. Does Org mode provide a way to reference another code block as a region?
E.g.
#+Name: html-test
#+BEGIN_EXAMPLE
<html>
<body>test</body>
</html>
#+END_EXAMPLE
#+BEGIN_SRC elisp
(libxml-parse-html-region html-test-start-point html-test-end-point)
#+END_SRC
So, how can I set the html-test-start-point and html-test-end-point?

The org-element library (the official parser for Org mode) provides the capability to get the body of the block as a string. You can then insert that string into a temporary buffer, where the region specified by (point-min) and (point-max) contains exactly the body of the block. You can find developer documentation for the library on Worg.
Since you don't specify the interface exactly, all I can do here is give some guidelines. The basic idea is to set point to the beginning of the example block somehow (maybe by searching for the name html-test and then moving to the beginning of the line). Once there, you call org-element-at-point to parse the element (and maybe sanity-check that you are in an example block). From the parse tree, extract the value of the :value property which is the body of the block as a string. Create a temporary buffer, insert the string and then execute the libxml function in the context of the temporary buffer.
Here is some illustrative code, commented fairly liberally and I hope in an enlightening manner (for the mysterious (goto-char 130), just keep reading - it's explained in the complete Org mode file below):
(save-excursion
;; this should probably be replaced by a search for the correct place
(goto-char 130)
;; parse the element at point
(setq parsed (org-element-at-point))
;; check that it is an example block
(unless (eq (org-element-type parsed) 'example-block)
(error "Not an example block"))
;; retrieve the :value property, i.e, the body of the block
(setq value (org-element-property :value parsed))
;; create a temp buffer
(with-temp-buffer
;; in the context of the temp buffer, insert the body of the block
(insert value)
;; do something with the region containing the body by using (point-min)
;; as the beginning of the region and (point-max) as the end
(message (buffer-substring (point-min) (point-max)))
(libxml-parse-html-region (point-min) (point-max))))
and here's the same code as a code block in an Org mode file that contains an example block that the code block can operate on. Cut-and-paste the whole thing into a foo.org file, open it in emacs and C-c C-c on the code block to see it in action:
* foo
We have a named block and we want to get the beginning and the end of the region
that consists of the body of the block.
#+Name: html-test
#+BEGIN_EXAMPLE
<html>
<body>test</body>
</html>
#+END_EXAMPLE
The position of the beginning of the block is 130 - you can check by evaluating
this expression with C-x C-e after the closing paren:
(goto-char 130)
You should end up at the beginning of the `#+name:` line.
If we imagine a code block like this:
#+BEGIN_SRC elisp
(libxml-parse-html-region html-test-start-point html-test-end-point)
#+END_SRC
the question is how to calculate the html-test-{start,end}-point values.
The suggested solution is to copy the body of the block to a temporary
buffer and do the evaluation of the XML function in that buffer. The region
is then delimited by (point-min) and (point-max).
* Code :noexport:
#+begin_src elisp :results drawer
(save-excursion
;; this should probably be replaced by a search for the correct place
(goto-char 130)
;; parse the element at point
(setq parsed (org-element-at-point))
;; check that it is an example block
(unless (eq (org-element-type parsed) 'example-block)
(error "Not an example block"))
;; retrieve the :value property, i.e, the body of the block
(setq value (org-element-property :value parsed))
;; create a temp buffer
(with-temp-buffer
;; in the context of the temp buffer, insert the body of the block
(insert value)
;; do something with the region containing the body by using (point-min)
;; as the beginning of the region and (point-max) as the end
(message (buffer-substring (point-min) (point-max)))
(libxml-parse-html-region (point-min) (point-max))))
#+end_src
As the OP points out in a comment and #TianshuWang describes in his answer, you can add a variable to the source code block and get the body of the example block directly:
#+begin_src elisp :results drawer :var value=html-test
;; create a temp buffer
(with-temp-buffer
;; in the context of the temp buffer, insert the body of the block
(insert value)
;; do something with the region containing the body by using (point-min)
;; as the beginning of the region and (point-max) as the end
(message (buffer-substring (point-min) (point-max)))
(libxml-parse-html-region (point-min) (point-max))))
#+end_src
But that bypasses the org-element library: what fun is that? :-)

After consulting the manual, I suspect that there is no simple way to achieve this.
You can use other block as argument like https://orgmode.org/worg/org-contrib/babel/intro.html#arguments-to-source-code-blocks, however there is not a function of libxml which accept string.
#+name: html-test
#+begin_example
<html>
<body>test</body>
</html>
#+end_example
#+begin_src elisp :var html=html-test
(message html)
#+end_src

Related

How to extract titles as links from a list of Org files in a directory

I have a list of org files under a directory:
> org-file1.org
> org-file2.org
> org-file3.org
> ...
> org-fileN.org
I want to extract their titles (using #+title tag) as links as follows:
[[file:org-file1.org][title for org file 1]]
[[file:org-file2.org][title for org file 2]]
[[file:org-file3.org][title for org file 3]]
...
[[file:org-fileN.org][title for org file N]]
How can I extract these as a list using Emacs Lisp?
You could use the org-element-api for this, but in this case it is probably easier/faster to simply search by regexp using looking-at-p. To get all files in a directory, there is directory-files(-recursively). Then finally you could achieve it using the following function:
(defun extract-org-directory-titles-as-list (&optional dir)
(interactive "D")
(print
(delete nil
(let ((case-fold-search t))
(mapcar (lambda (f)
(when (string-match "org$" f)
(with-temp-buffer
(insert-file-contents-literally
(concat (file-name-as-directory dir) f))
(while (and (not (looking-at-p "#\\+TITLE:"))
(not (eobp)))
(forward-line))
(when (not (eobp))
(cons f (substring (thing-at-point 'line) 9 -1))))))
(directory-files dir))))))
(defun insert-directory-org-file-titles (&optional dir)
(interactive "D")
(let ((files-titles (extract-org-directory-titles-as-list dir)))
(dolist (ft files-titles)
(insert (concat "[[file:" (car ft)"][" (cdr ft) "]]\n")))))
If you prefer to just have it as a (a)list, like you asked, then just use extract-org-directory-titles-as-list.
Here's some code that can be used on a single file to get the title. It's part of an org mode file that can be used for testing. Save it as an Org mode file, visit it in Emacs and type C-c C-c on the code block. Change the title and evaluate the code block again.
#+OPTIONS: toc:nil
#+TITLE: This is a very nice title, don't you think?
#+AUTHOR: A.U. Thor
* foo
bar
* Code
#+begin_src elisp :results drawer
(save-excursion
(goto-char (point-min))
(let ((case-fold-search t))
(search-forward-regexp "^#\\+TITLE:")
(org-element-property :value (org-element-context (org-element-at-point)))))
#+end_src
#+RESULTS:
:results:
This is a very nice title, don't you think?
:end:
The code block goes to the beginning of the buffer, searches forward for the string #+TITLE: at the beginning of a line (the search is case-insensitive) and then uses some functions from the org-element library to parse the buffer at point, get the context and get the :value property out of it.
In order to make it into a complete solution, you have to:
make it more robust: check that this is a title keyword not just some random junk that happened to satisfy the match, although that's unlikely; but better safe than sorry.
wrap it in a loop that does a find-file on every file in the directory and gets the title for each file and returns a list of tuples: (filename title) that you can easily use to create your links.

How to count characters of a subtree in emacs org mode?

I would like to count characters in a subtree (heading) in org mode. Right now I have figured out how to count characters in a single paragraph, but not over multiple paragraphs. I first define a source block:
#+NAME: countChars
#+BEGIN_SRC sh :var X="" :results output
echo "$X" | wc --chars
#+END_SRC
And then I use it on named paragraphs:
#+NAME: paragraph
This is the paragraph
#+CALL: countChars(paragraph)
This works well but the #+NAME: only covers one paragraph. I have tried to use a heading as argument but I could not make it work.
EDIT: Based on comments, I came up with:
#+NAME: countChars
#+BEGIN_SRC emacs-lisp :results output :eval no-export :exports results
(interactive)
(save-excursion
(org-mark-subtree)
(setq a (- (mark) (point)))
(deactivate-mark)
(prin1 'Count= )
(prin1 a))
#+END_SRC
which does almost what I want when invoked as
#+CALL: countChars()
but has the problem of counting source code blocks (including itself) as well as text. I would like to count text only (excluding the headline).
You can only use #+NAME in front of a source block, not a subtree.
It's easier to write this in emacs lisp.
This code block will count the number of characters in the current subtree, not including the header or the last line of the content:
If you want to count the number of characters in the current subtree using emacs lisp, try this:
(save-excursion
(org-mark-subtree) ;mark the whole subtre
(forward-line 1) ;move past header
(exchange-point-and-mark) ;swap point and mark (ends of region)
(forward-line -1) ;move backwards past the last line
(let ((nchars (- (point) (mark))))
(deactivate-mark) ;clear the region
(message "%d" nchars))))

Create autoloaded function from macro

How can I get create an autoloaded function from a macro function factory? For example, say I have a macro to create alignment functions as follows, but I want to be able to specify an option so the expanded macro has an autoload cookie.
(defmacro align-by (name doc regex)
"Alignment function factory."
(declare (indent 1))
(let ((fn (intern name)))
`(defun ,fn (start end)
,doc
(interactive "r")
(align-regexp start end ,regex))))
(align-by "align-backslash"
"Align backslashes at end of line in region."
"\\(\\s-*\\)\\\\$")
I know I can write this, but can I avoid needing to write this for every function?
;;;###autoload (autoload 'align-backslash "this-file")
It's not clear where the macro would pick up the name of the file to be autoloaded - you do not pass a file name to the macro, currently.
Assuming that the file name comes from a file that is being visited when the macro is expanded, this will do it:
(defmacro align-by (name doc regex)
"Alignment function factory."
(declare (indent 1))
(let ((fn (intern name)))
`(progn
,(and (buffer-file-name)
`(autoload ',name ,(buffer-file-name)))
(defun ,fn (start end)
,doc
(interactive "r")
(align-regexp start end ,regex)))))
Testing:
(macroexpand '(align-by "align-backslash"
"Align backslashes at end of line in region."
"\\(\\s-*\\)\\\\$"))
C-u C-x C-e shows that that gives this when the current buffer is not visiting a file:
(progn
(autoload '"align-backslash" nil)
(defun align-backslash
(start end)
"Align backslashes at end of line in region."
(interactive "r")
(align-regexp start end "\\(\\s-*\\)\\\\$")))
And it gives this when the buffer is visiting file foo.el, where ".../foo.el" is really the absolute file name for foo.el:
(progn
(autoload '"align-backslash" ".../foo.el")
(defun align-backslash
(start end)
"Align backslashes at end of line in region."
(interactive "r")
(align-regexp start end "\\(\\s-*\\)\\\\$")))
The code that picks up the ;;;###autoload cookies does expand the macros before looking at the code, so you should be able to just place a ;;;###autoload cookie right before a (align-by ...) expression and get the right autoload placed in the <foo>-autoloads.el file.
The problem, tho is that your macro is probably not going to be defined at the time the autoloads are created, so the macro expansion will not actually happen. Maybe a M-x report-emacs-bug is in order.
As emacs manual mentioned
If you write a function definition with an unusual macro that is not one of the known and recognized function definition methods, use of an ordinary magic autoload comment would copy the whole definition into loaddefs.el. That is not desirable. You can put the desired autoload call into loaddefs.el instead by writing this:
;;;###autoload (autoload 'foo "myfile")
(mydefunmacro foo
...)
Your align-by is like mydefunmacro in the manual example. It is not known function definition method and it is not supported by autoload mechanism.
So there are three alternatives:
Extend the list of supported types (defun, defmacro, cl-defun, defclass,...) by your special macros. Then you can use simple ;;;###autoload "decorator".
Invent your own mechanism of "myfile" parsing (without executing) and "loaddefs" populating by necessary autoload definitions.
Use more complicated construction (with (autoload 'align-backslash "myfile")) as function defintion method.
If you rewrite align-by like this (without intern):
(defmacro align-by-defun (name doc regex)
"Alignment function factory."
(declare (indent 1))
`(defun ,name (start end)
,doc
(interactive "r")
(align-regexp start end ,regex)))
;;;###autoload (autoload 'align-backslash "myfile")
(align-by-defun align-backslash
"Align backslashes at end of line in region."
"\\(\\s-*\\)\\\\$")
you can see that align-by is just a function definition method (like cl-defun).

Inline code in org-mode

Markdown allows for embedded code. How can this be done in org-mode?
I know about source-code blocks:
#+begin_example
blah-blah
#+end_example
But what I want is something like this (obviously, with the right syntax, which I do not know):
This is `embeded code`.
Can this be done in org-mode? Not possible to find that in the documentation ...
While monospaced is good enough for most cases, inline code blocks have the form src_LANG[headers]{your code}. For example, src_xml[:exports code]{<tag>text</tag>}.
Edit: Code highlighting of inline code is certainly possible, albeit with patching org.el itself: The answer given here https://stackoverflow.com/a/20652913/594138 works as advertised, turning
- Inline code src_sh[:exports code]{echo -e "test"}
Into
in html-export. And the winning answer in this post, https://stackoverflow.com/a/28059832/594138, achieves the same without the need to patch org.el, but you will have to adapt it if you don't like the optics during editing.
You can enclose the text within = or ~ signs to have it typeset in monospaced font and export it verbatim (which means it is not processed for org-specific syntax):
This is =verbatim text= or ~code~.
You'll find all information about org-mode markup elements in the relevant section of the manual.
I wrote a function which I hope will be useful to help manage the code inline.
You put this code in your init file
(defun org-insert-inline-code()
"This function insert inline code `src_lang{inline code}' \nYour buffer must contain '#+PROPERTY: header-args:lang :exports code' where `lang` can be python or an other programming language."
(interactive (if (use-region-p)
(progn
(setq start (region-beginning))
(setq end (region-end))
(goto-char start)
(if (re-search-backward "^#\\+PROPERTY: header-args:[^[:blank:]]*" 1 t 1)
(progn
(forward-char 24)
(setq org-inline-lang (word-at-point))
(goto-char start)
(insert (concat "src_" org-inline-lang "{"))
(goto-char (+ 11 end))
(insert "}")
)))
(progn
(setq start (point))
(if (re-search-backward "^#\\+PROPERTY: header-args:[^[:blank:]]*" 1 t 1)
(progn
(forward-char 24)
(setq org-inline-lang (word-at-point))
(goto-char start)
(insert (concat "src_" org-inline-lang "{} "))
(backward-char 2)
))))))
(define-key org-mode-map (kbd "C-M-,") 'org-insert-inline-code)
You put this kind of PROPERTY in the org-file
#+PROPERTY: header-args:python :exports code
The required [:exports code] is given that way and the programming language can be identify by the function too.
Insert the code in line with C-M-, (the function then search back to read the language in the PROPERTY line and insert the correct command).

Refactoring in Emacs

I am working on splitting code into smaller files and refactoring it a bit. Consider the following code below as the section I want to extract:
(require 'package)
(add-to-list 'package-archives
'("marmalade" . "http://marmalade-repo.org/packages/") t)
(package-initialize)
(when (not package-archive-contents)
(package-refresh-contents))
(defvar my-packages '(org magit)
"A list of packages to ensure are installed at launch.")
(dolist (p my-packages)
(when (not (package-installed-p p))
(package-install p)))
I want to take the section above and replace it with something like (require `file-name)
Then take the text replaced and place that in a new file in the current directory named file-name.el
And then add a line to the top of the file (provides `file-name)
It would be great if I could hit a keychord and then type a name and have this happen. If there is an easy way to do this then I would love to hear possible solutions.
Edit:
I'm starting a bounty because I think this applies to more types of code than Lisp and I would like to have something a little more general that I can expand upon.
I have considered yasnippet but I don't think it's powerful enough to perform the task at hand. Basically the ideal workflow would be marking the lines to be extracted, replacing that with an appropriate require or include directive and sending the text off to it's own file. Ideally one command and something that is aware of the type of file being edited or at least the major mode so the behavior can be customized, again yasnippet is good at performing different tasks when editing in different major modes however I would have no idea how to make that work or evaluate the possibility of making it work.
Let me know if you need any more information.
A general solution to this type of problem are keyboard macros (not to be confused with (Emacs) LISP macros). Basically Emacs allows you to record a sequence of keystrokes and "play them back" afterwards. This can be a very handy tool in situations where writing custom LISP code seems overkill.
For instance you could create the following keyboard macro (type the key combinations on the left hand side, the right hand side shows explanations for each key stroke):
C-x ( ; start recording a keyboard macro
C-x h ; mark whole buffer
C-w ; kill region
(require 'file-name) ; insert a require statement into the buffer
C-x C-s ; save buffer
C-x C-f ; find file
file-name.el <RET> ; specify the name of the file
M-< ; move to the beginning of the buffer
C-u C-y ; insert the previously killed text, leaving point where it is
(provide 'file-name) <RET> <RET> ; insert a provide statement into the buffer
C-x ) ; stop recording the keyboard macro
Now you can re-play that macro in some other buffer by typing C-x e, or save it for later use. You can also bind a macro to a shortcut just like a function.
However, there is one weakness with this approach: you want to be able to actually specify the file-name, and not just use the string "file-name" every time. That is a bit difficult - by default, keyboard macros provide no general facility for querying the user (except the very minimal C-x q, as documented here).
The Emacs Wiki has some work-arounds for that, however, instead of prompting the user in the minibuffer, it can sometimes be sufficient to start the macro by killing the current line and saving its text to a register.
C-x (
C-e C-<SPC> C-a ; mark current line
C-x r s T ; copy line to register T
C-k C-k ; kill current line
... ; actual macro
C-x )
Now when you want to use your macro, you would first write the desired file-name in an otherwise empty line, and then do C-x e in that line. Whenever the value of the file-name is needed in the macro you can retrieve it from the register T:
C-x r i T ; insert file-name into buffer
For instance, for the provide statement in the above macro, you could write: (provide ' C-x r i T ). Note that this technique (inserting) also works in the minibuffer, and of course you could save multiple lines to different registers.
May sound complicated, but is actually quite easy in practice.
Slightly tested:
(defun extract-to-package (name start end)
(interactive (list (read-string "Package name to create: ")
(region-beginning) (region-end)))
(let ((snip (buffer-substring start end)))
(delete-region start end)
(insert (format "(require '%s)\n" name))
(with-current-buffer (find-file-noselect (concat name ".el"))
(insert snip)
(insert (format "(provide '%s)\n" name))
(save-buffer))))
For a such thing I use the following snippet (with yasnippet):
;; `(buffer-name)`
;; Copyright (C) `(format-time-string "%Y")` name
;; Author: name <email>
;; 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 <http://www.gnu.org/licenses/>.
$0
(provide '`(subseq (buffer-name) 0 (- (length (buffer-name)) 3))`)
1st create the file C-xC-ffile-name.elRET
then insert the snippet with C-c&C-s
and add any piece of code you wish.
I've also the following hook:
(add-hook 'after-save-hook 'autocompile)
(defun autocompile ()
"Byte compile an elisp."
(interactive)
(require 'bytecomp)
(let ((filename (buffer-file-name)))
(if (string-match "\\.el$" filename)
(byte-compile-file filename))))
to produce an .elc whenever I save a .el.
(defun region-to-file+require (beg end file append)
"Move region text to FILE, and replace it with `(require 'FEATURE)'.
You are prompted for FILE, the name of an Emacs-Lisp file. If FILE
does not yet exist then it is created.
With a prefix argument, the region text is appended to existing FILE.
FEATURE is the relative name of FILE, minus the extension `.el'."
(interactive "#*r\nG\nP")
(when (string= (expand-file-name (buffer-file-name)) (expand-file-name file))
(error "Same file as current"))
(unless (string-match-p ".+[.]el$" file)
(error "File extension must be `.el' (Emacs-Lisp file)"))
(unless (or (region-active-p)
(y-or-n-p "Region is not active. Use it anyway? "))
(error "OK, canceled"))
(unless (> (region-end) (region-beginning)) (error "Region is empty"))
(unless (or (not append)
(and (file-exists-p file) (file-readable-p file))
(y-or-n-p (format "File `%s' does not exist. Create it? " file)))
(error "OK, canceled"))
(write-region beg end file append nil nil (not append))
(delete-region beg end)
(let ((feature (and (string-match "\\(.+\\)[.]el$" file)
(match-string 1 file))))
(when feature
(insert (format "(require '%s)\n" feature)))))