Open Pelican relative path in emacs org-mode - emacs

I'm using Pelican, a static site generator, to provide an html personal wiki or knowledge base using .org files as its base. Links to internal content in Pelican use the syntax {filename}/path/to/file. However, since I'm using emacs org-mode, I would also like to be able to follow such links to their relevant files in org-mode. I'm hoping that there is a simple function I could write which would allow org-mode to follow such a link and open the relevant file, without compromising Pelican's use of the internal link syntax. I suspect that this might be done by means of org-add-link-type and a function which parses the proper absolute path to the file. But my elisp-fu is weak, and I'm unsure of how to proceed. Help appreciated!
Edit: just to give an example, I might have a link like [[file:{filename}/path/to/file.org]] in an org file. Is there a way to get org to open the file by substituting (maybe via regexp?) an absolute file path for {filename}, but also wouldn't alter the original link so that Pelican can still process it correctly?

I think you want something like:
#+BEGIN_SRC emacs-lisp
(org-add-link-type
"pelican"
(lambda (path) (org-open-file path))
;; ; export
(lambda (path desc backend)
(cond
((eq backend 'html)
(format "{filename}/%s" (file-relative-name path))))))
#+END_SRC
Link to internal content pelican:/Users/jkitchin/blogofile-jkitchin.github.com/_blog/blog.org
#+BEGIN_SRC sh
pwd
#+END_SRC
#+RESULTS:
: /Users/jkitchin/blogofile-jkitchin.github.com/_blog
exports to:
<p>
Link to internal content {filename}/blog.org
</p>

Using the original answer by John Kitchen as a basis to build upon (along with this answer), the best function I came up with is the following:
(org-add-link-type
"pelican"
(lambda (path) (org-open-file path))
;; ; export
(lambda (path desc backend)
(cond
((eq backend 'org)
(format "[[file:{filename}/%s][%s]]" path (or desc "")))
((eq backend 'html)
(format "%s" path (or desc "")))
((eq backend 'md)
(format "[{filename}/%s](%s)" path (or desc ""))))))
Depending on the backend used for org-publish the function will provide relative links for org, html, or markdown.

Related

Jump to zotero from emacs failed

I'd like to jump to zotero when I click zotero links like zotero://select/items/1_2S5A64QI in my org file, but at the first time it doesn't work (no response). After opened the .emacs file and M-x RET eval-buffer RET, I backed to the org file, this time I can jump to the zotero successfully. What cause it?
(defun zotero-org (path)
(browse-url (format "zotero:%s" path)))
(org-add-link-type "zotero" 'zotero-org)
This is what I use to add a new link type.
You need to add-link after org is loaded.
Besides, org-add-link-type is obsolete since 9.0; use org-link-set-parameters instead.
(defun org-zotero-open (path)
(browse-url (format "zotero:%s" path)))
(with-eval-after-load 'org
(org-link-set-parameters "zotero" :follow #'org-zotero-open))

Org-mode Babels :file with link abbreviations

I have some link-abbreviations in org-mode like this:
(setq org-link-abbrev-alist
'(("dropboxpath" . "~/Dropbox")
("cloudpath" . "~/")
("imgpath" . "~/images")
("gitpath" . "~/git")
))
It works fine and as i am working on different systems and syncing my org-files the paths are different on every system. My problem is the following:
#+BEGIN_SRC plantuml :file gitpath:/test.png
<some plantumlstuff here>
#+END_SRC
This is not working, org-babel does not recognize the link abbreviation.
I also tried the following (where temp is a variable containing the path to the git-directory):
#+BEGIN_SRC plantuml :file (concat temp "/test.png")
This works in principle but gives me the following result:
#+RESULTS:
[[file:~/git/test.png]]
This does not meet my requirements though because i need gitpath in order to make it work over all my machines...
#+RESULTS:
[[gitpath:/test.png]]
Does anybody have a suitable solution to this problem?
You can simply refer to org-link-abbrev-alist. Since that variable is structured as an alist, you can use assoc. It returns the first element of the alist that matches the supplied key.
(concat
(car (assoc "gitpath" org-link-abbrev-alist))
"/test.png")

How to make all org-files under a folder added in agenda-list automatically?

I am using org-mode to write notes and org-agenda to organize all notes, especially to search some info. by keyword or tag.
C-c a m can search some files by tag inputed, C-c a s by keyword ,those functions from org-agenda are well to utilize, however, I need to add org-file into the agenda-list by hand.
I added some codes into .emacs, such as
(setq org-agenda-files (list "path/folder/*.org"))
or
(setq org-agenda-files (file-expand-wildcards "path/folder/*.org"))
but, both failed to add files under the folder specified into agenda-list automatically, so I can't search keyword or tag among those org-files, unless that I open a org-file and type C-c [ to add it into agenda-list.
How can I make all org-files under a folder automatically added in agenda?
Just naming the directory should be enough. For example this works for me very well:
(setq org-agenda-files '("~/org"))
Also take a look at org-agenda-text-search-extra-files; it lets you
add extra files included only in text searches. A typical value might
be,
(setq org-agenda-text-search-extra-files
'(agenda-archives
"~/org/subdir/textfile1.txt"
"~/org/subdir/textfile1.txt"))
Caveat: If you add a file to the directory after you have started
Emacs, it will not be included.
Edit: (2018) To include all files with a certain extension in the extra files list you can try the following function I wrote sometime back (a more recent version might be available here).
;; recursively find .org files in provided directory
;; modified from an Emacs Lisp Intro example
(defun sa-find-org-file-recursively (&optional directory filext)
"Return .org and .org_archive files recursively from DIRECTORY.
If FILEXT is provided, return files with extension FILEXT instead."
(interactive "DDirectory: ")
(let* (org-file-list
(case-fold-search t) ; filesystems are case sensitive
(file-name-regex "^[^.#].*") ; exclude dot, autosave, and backupfiles
(filext (or filext "org$\\\|org_archive"))
(fileregex (format "%s\\.\\(%s$\\)" file-name-regex filext))
(cur-dir-list (directory-files directory t file-name-regex)))
;; loop over directory listing
(dolist (file-or-dir cur-dir-list org-file-list) ; returns org-file-list
(cond
((file-regular-p file-or-dir) ; regular files
(if (string-match fileregex file-or-dir) ; org files
(add-to-list 'org-file-list file-or-dir)))
((file-directory-p file-or-dir)
(dolist (org-file (sa-find-org-file-recursively file-or-dir filext)
org-file-list) ; add files found to result
(add-to-list 'org-file-list org-file)))))))
You can use it like this:
(setq org-agenda-text-search-extra-files
(append (sa-find-org-file-recursively "~/org/dir1/" "txt")
(sa-find-org-file-recursively "~/org/dir2/" "tex")))
Edit: (2019) As mentioned in the answer by #mingwei-zhang and the comment by #xiaobing, find-lisp-find-files from find-lisp and directory-files-recursively also provides this functionality. However, please note in these cases the file name argument is a (greedy) regex. So something like (directory-files-recursively "~/my-dir" "org") will give you all Org files including backup files (*.org~). To include only *.org files, you may use (directory-files-recursively "~/my-dir" "org$").
There is a simpler way of doing recursive search of org files (courtesy #xiaobing):
(setq org-agenda-files (directory-files-recursively "~/org/" "\\.org$"))
EDIT: You can also filter out certain directory from lookup by adding a array filter. Example, filtering out all org files in xxxx/xxx/daily/ directory:
(setq org-agenda-files
(seq-filter (lambda(x) (not (string-match "/daily/"(file-name-directory x))))
(directory-files-recursively "~/Notes/roam" "\\.org$")
))
For Emacs <25, you can use find-lisp-find-files:
(load-library "find-lisp")
(setq org-agenda-files
(find-lisp-find-files "FOLDERNAME" "\.org$"))

Insert yasnippet by name

I want to insert a specific yasnippet as part of a function in emacs-lisp. Is there a way to do that?
The only command that seems related is yas/insert-snippet, but it simply opens a popup with all the options and the documentation doesn't say anything about bypassing the popup by specifing the snippet name.
yas/insert-snippet is indeed just a thin wrapper around yas/expand-snippet for interactive use. However, the internal structures are... interesting. Judging from the source code the following does work for me when I want to expand the "defun" snippet in elisp-mode:
(yas/expand-snippet
(yas/template-content (cdar (mapcan #'(lambda (table)
(yas/fetch table "defun"))
(yas/get-snippet-tables)))))
As the author of yasnippet, I think you'd rather not rely on internal details of yasnippet's interesting data structures, which may change in the future. I would do this based on the documentation of yas/insert-snippet and yas/prompt-functions:
(defun yas/insert-by-name (name)
(flet ((dummy-prompt
(prompt choices &optional display-fn)
(declare (ignore prompt))
(or (find name choices :key display-fn :test #'string=)
(throw 'notfound nil))))
(let ((yas/prompt-functions '(dummy-prompt)))
(catch 'notfound
(yas/insert-snippet t)))))
(yas/insert-by-name "defun")
I'm just getting into yasnippet and I wanted to automatically insert one of my snippets upon opening a new file for certain modes. That led me to here but I've generated a slightly different solution. Providing yet another alternative: ("new-shell" is the name of my personal snippet for providing a new shell script template)
(defun jsm/new-file-snippet (key)
"Call particular yasnippet template for newly created
files. Use by adding a lambda function to the particular mode
hook passing the correct yasnippet key"
(interactive)
(if (= (buffer-size) 0)
(progn
(insert key)
(call-interactively 'yas-expand))))
(add-hook 'sh-mode-hook '(lambda () (jsm/new-file-snippet "new-shell")))
IMO, my solution is a tad less susceptible to breaking should yasnippet change dramatically.
This is 2022 now, so we can simply do the following :
(yas-expand-snippet (yas-lookup-snippet "name-of-your-snippet"))
See the documentation

How can I check if a file exists using Emacs Lisp?

I would like emacs to mark files that are generated as read-only when they're opened. The part of the puzzle that I'm missing is how to check if a file "exists". I currently have the following:
;;
;; get file extension
;;
(defun get-ext (file-name)
(car (cdr (split-string file-name "\\."))))
;;
;; get the base name of the file
;;
(defun base-name (file-name)
(car (split-string file-name "\\.")))
;;
;; if an 'lzz' file exists for this header, mark it as read only
;;
(defun mark-read-only ()
(if (string= (get-ext (cur-file)) "h")
(if ( ??file-exists??? (concat (base-name (cur-file)) ".lzz") )
(toggle-read-only))))
What can I use for "???file-exists???"?
Once I find this, I'll add "mark-read-only" to the appropriate hook (which I think is the find-file-hook).
BACKGROUND
We use lzz as a code generator to simplify our C/C++ development process. Briefly, lzz takes a single input file (which looks very like C/C++) and generates header and source files as appropriate.
By default, lzz includes #line directives so that the debugger points to the original source and not the generated source, however, to reduce compilation dependencies we normally disable these directives in header files. The result is that when debugging templates or inline functions, the debugger normally points to the generated header file and not the original source file.
This is not a big deal, however, recently I've found that when debugging I'll make a quick modification to the displayed file and then I'll rebuild. Of course this normally means that the change I made disappears because the file I edited is generated and so the changes are "blown away" during the library rebuild.
SOLUTION
Thanks to everyone for their help and comments. A special thanks to cobbal for pointing out the correct function to use.
Here's the resulting code (with updates based on the other comments here too):
(defun cur-file ()
"Return the filename (without directory) of the current buffer"
(file-name-nondirectory (buffer-file-name (current-buffer)))
)
(defun mark-generated-as-read-only ()
"Mark generated source files as read only.
Mark generated files (lzz or gz) read only to avoid accidental updates."
(if
(or (string= (file-name-extension (cur-file)) "h")
(string= (file-name-extension (cur-file)) "cpp"))
(cond
(
(file-exists-p (concat (file-name-sans-extension (cur-file)) ".lzz"))
(toggle-read-only))
(
(file-exists-p (concat (file-name-sans-extension (cur-file)) ".gz") )
(toggle-read-only))
)
)
)
try file-exists-p
"Return t if file filename exists (whether or not you can read it.)".
Note that it's not spesific to files and works for directories too.
Depending on what you need, you might want file-readable-p instead of file-exists-p.
Apropos will only get you so far. Icicles provides apropos completion and progressive completion which let you find help easily for command, function, variable, etc. names that match subparts in an arbitrary order (is it file-exists-p or exists-file-p?).
Use f.el, modern library for file and directory manipulation. You can use f-exists?, f-file?, f-directory? and many other predicates. The library is better than standard functions, because it's every file related function you'll ever need under one namespace.