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

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))))

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 reference a code block points in org mode?

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

Creating org-tables from the results of a code block

I am trying to create an org-table based on the content of a CSV file that uses ";" as a delimiter.
I thought it would be easy to have a source code block to "cat" the content of the file and then pass the result to a function that creates the table, but I got stuck: I can't find a way to use the "results" of the first source code block. The function I know (org-table-convert-region) expects a region to work on and I don't know how to pass the "cat"-ed text as a region.
#+NAME: csvraw
#+BEGIN_SRC sh :results raw
cat afile.csv
#+END_SRC
I'd appreciate your help to generate a code block that produces an org-table out of my csv file, which contains lines like the following:
ID;Region;SubRegion;Area;No
1234;Asia;India;45;2
24251;Europe;Romania;456;67
There is org-table-convert-region (bound to C-c |) which can do the transformation fairly simply. The only trick is to specify ; as the separator. You can do that by invoking it with the proper prefix argument - the doc string says:
(org-table-convert-region BEG0 END0 &optional SEPARATOR)
Convert region to a table.
The region goes from BEG0 to END0, but these borders will be moved
slightly, to make sure a beginning of line in the first line is included.
SEPARATOR specifies the field separator in the lines. It can have the
following values:
(4) Use the comma as a field separator
(16) Use a TAB as field separator
(64) Prompt for a regular expression as field separator
integer When a number, use that many spaces, or a TAB, as field separator
regexp When a regular expression, use it to match the separator
nil When nil, the command tries to be smart and figure out the
separator in the following way:
- when each line contains a TAB, assume TAB-separated material
- when each line contains a comma, assume CSV material
- else, assume one or more SPACE characters as separator.
The (64) value is just three C-u in a row, so the process is as follows:
insert the CSV file with C-x i.
C-x C-x to mark the inserted contents as the active region.
C-u C-u C-u C-c | ; RET
What's even cooler, leaving an empty line in the CSV file between the first line and the rest of the lines, will make the first line a header in the table automatically.
And you can wrap it up in a code block as well:
#+begin_src elisp :var file="/tmp/foo.csv" :results raw
(defun csv-to-table (file)
(with-temp-buffer
(erase-buffer)
(insert-file file)
(org-table-convert-region (point-min) (point-max) ";")
(buffer-string)))
(csv-to-table file)
#+end_src
#+RESULTS:
| a | b | c |
|---+---+---|
| d | e | f |
| g | h | i |
(defun jea-convert-csv-to-org-table (fname)
(interactive "fCSV to convert: ")
(let ((result '("|-\n")))
(with-temp-buffer
(save-excursion (insert-file-contents-literally fname))
(while (and (not (eobp)) (re-search-forward "^\\(.+\\)$" nil t nil))
(push (concat "|" (replace-regexp-in-string ";" "|" (match-string 1)) "|\n")
result))
(push '"|-\n" result))
(concat (seq-mapcat #'identity (reverse result)))))
install the elisp code in your ~/.emacs file and restart emacs. Or, better yet, eval it into existence (CTRL+x, CTRL+e or ALT+x eval-last-sexp).
#+NAME: csvraw
#+BEGIN_SRC elisp :results raw
(jea-convert-csv-to-org-table "/Users/jamesanderson/Downloads/test1.csv")
#+END_SRC
note the change to elisp from sh in the above. here is a gif of it in action:
emacs converting csv to org table
The code is not optimized for large files, and, to be frank, quite quickly thrown together. But, after a tiny bit of testing, seems to work.

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).

Indent code in org-babel src blocks

In an org-mode file, with code like the following:
#+begin_src emacs-lisp
(add-to-list 'org-tab-before-tab-emulation-hook
(lambda ()
(when (within-the-body-of-a-begin-src-block)
(indent-for-tab-command--as-if-in-lisp-mode))))
#+end_src
I would like the TAB key to indent the code as it would if it were in a buffer in lisp mode.
What I need is:
A way to figure out whether the cursor is within a src block. It needs to not trigger when on the header line itself, as in that case the default org folding should take place.
A way to indent the code according to the mode (emacs-lisp in this case) specified in the header.
Org can already syntax highlight src blocks according to mode, and the TAB hooks are there. This looks do-able.
Since Emacs 24.1 you can now set the following option:
(setq org-src-tab-acts-natively t)
...and that should handle all src blocks.
Just move point into the code block and press C-c '
This will pop up a buffer in elisp-mode, syntax higlighting ad all...
Here's a rough solution:
(defun indent-org-src-block-line ()
"Indent the current line of emacs lisp code."
(interactive)
(let ((info (org-babel-get-src-block-info 'light)))
(when info
(let ((lang (nth 0 info)))
(when (string= lang "emacs-lisp")
(let ((indent-line-function 'lisp-indent-line))
(indent-for-tab-command)))))))
(add-to-list 'org-tab-before-tab-emulation-hook
'indent-org-src-block-line)
It only handles emacs-lisp blocks. I've only tested with the src block un-indented (not the org default).
It is tough in general to make one mode work inside another - many keyboard commands will conflict. But some of the more basic strokes, like tab for indent, newline, commenting (org will comment the lisp code with #, which is wrong) seem like they could be made to work and would have the largest impact.
(defun my/org-cleanup ()
(interactive)
(org-edit-special)
(indent-buffer)
(org-edit-src-exit))
should do it, where `indent-buffer' is defined as:
(defun indent-buffer ()
(interactive)
(indent-region (point-min) (point-max)))