Emacs org-mode: textual reference to a file:line - emacs

I am using org-mode in Emacs to document my development activities. One of the tasks which I must continuously do by hand is to describe areas of code. Emacs has a very nice Bookmark List: create a bookmark with CTRL-x r m, list them with CTRL-x r l. This is very useful, but is not quite what I need.
Org-mode has the concept of link, and the command org-store-link will record a link to the current position in any file, which can be pasted to the org-file. The problem with this is two-fold:
It is stored as an org-link, and the linked position is not directly visible (just the description).
It is stored in the format file/search, which is not what I want.
I need to have the bookmark in textual form, so that I can copy paste it into org-mode, end edit it if needed, with a simple format like this:
absolute-file-path:line
And this must be obtained from the current point position. The workflow would be as simple as:
Go to the position which I want to record
Call a function: position-to-kill-ring (I would bind this to a keyboard shortcut)
Go to the org-mode buffer.
Yank the position.
Edit if needed (sometimes I need to change absolute paths by relative paths, since my code is in a different location in different machines)
Unfortunately my lisp is non-existent, so I do not know how to do this. Is there a simple solution to my problem?

(defun position-to-kill-ring ()
"Copy to the kill ring a string in the format \"file-name:line-number\"
for the current buffer's file name, and the line number at point."
(interactive)
(kill-new
(format "%s:%d" (buffer-file-name) (save-restriction
(widen) (line-number-at-pos)))))

You want to use the org-create-file-search-functions and org-execute-file-search-functions hooks.
For example, if you need the search you describe for text-mode files, use this:
(add-hook 'org-create-file-search-functions
'(lambda ()
(when (eq major-mode 'text-mode)
(number-to-string (line-number-at-pos)))))
(add-hook 'org-execute-file-search-functions
'(lambda (search-string)
(when (eq major-mode 'text-mode)
(goto-line (string-to-number search-string)))))
Then M-x org-store-link RET will do the right thing (store a line number as the search string) and C-c C-o (i.e. M-x org-open-at-point RET) will open the file and go to this line number.
You can of course check for other modes and/or conditions.

An elisp beginner myself I though of it as a good exercise et voila:
Edit: Rewrote it using the format methode, but I still think not storing it to the kill-ring is less intrusive in my workflow (don't know about you). Also I have added the capability to add column position.
(defvar current-file-reference "" "Global variable to store the current file reference")
(defun store-file-line-and-col ()
"Stores the current file, line and column point is at in a string in format \"file-name:line-number-column-number\". Insert the string using \"insert-file-reference\"."
(interactive)
(setq current-file-reference (format "%s:%d:%d" (buffer-file-name) (line-number-at-pos) (current-column))))
(defun store-file-and-line ()
"Stores the current file and line oint is at in a string in format \"file-name:line-number\". Insert the string using \"insert-file-reference\"."
(interactive)
(setq current-file-reference (format "%s:%d" (buffer-file-name) (line-number-at-pos))))
(defun insert-file-reference ()
"Inserts the value stored for current-file-reference at point."
(interactive)
(if (string= "" current-file-reference)
(message "No current file/line/column set!")
(insert current-file-reference)))
Not tested extensively but working for me. Just hit store-file-and-line or store-file-line-and-col to store current location and insert-file-reference to insert the stored value at point.

BTW, if you want something better than FILE:LINE, you can try to use add-log-current-defun (in add-log.el) which should return the name of the current function.

;; Insert a org link to the function in the next window
(defun insert-org-link-to-func ()
(interactive)
(insert (with-current-buffer (window-buffer (next-window))
(org-make-link-string
(concat "file:" (buffer-file-name)
"::" (number-to-string (line-number-at-pos)))
(which-function)
))))
This func generates link with the function name as the description.
Open two windows, one is the org file and the other is src code.
Then M-x insert-org-link-to-func RET

Related

How to use the function option of org-capture correctly?

I want to dynamically open the correct file in an orgmode capture template using the function option:
("a" "foo" plain
(function my-visit-timestamped-file)
"<some content>")
Function my-visit-timestamped-file is defined as
(defun my-visit-timestamped-file ()
(interactive)
(let
((theDate (format-time-string "%Y%m%d-%H%M.org")))
(find-file (concat "<some_path>" theDate))))
If I run the capture template a, emacs opens the file <some_path>theDate in a buffer and opens the capture buffer with the file.
Thus my window is split into 2 buffers showing the same content.
Can function my-visit-timestamped-file be changed somehow such that the buffer is not opened but org capture still gets the correct file pointer/file handle?
The answer by #jpkotta pointed me in the right direction.
The bug was gone, but instead the content of the capture buffer was always pasted into the buffer I was currently editing.
In an old thread on the org-mode mailing list I found the answer to my question. The function should be:
(defun my-visit-timestamped-file ()
"Visit a new file named by the current timestamp"
(interactive)
(let* (
(curr-date-stamp (format-time-string "%Y%m%d-%H%M.org"))
(file-name (expand-file-name curr-date-stamp "/some/path/")))
(set-buffer (org-capture-target-buffer file-name))
(goto-char (point-max))))
Link to mailing list thread: https://lists.gnu.org/archive/html/emacs-orgmode/2013-11/msg00676.html
You probably want find-file-noselect instead of find-file. Note that the docs of org-capture-templates say this:
(function function-finding-location)
Most general way: write your own function which both visits
the file and moves point to the right location
so you might want to add some code to go to the correct location (I'm guessing either (point-min) or (point-max)) in the file. That might look like this:
(defun my-visit-timestamped-file ()
(interactive)
(let* ((the-date (format-time-string "%Y%m%d-%H%M.org"))
(the-buffer (find-file-noselect (expand-file-name the-date "/some/path/"))))
(with-current-buffer the-buffer
(goto-char (point-min)))
the-buffer))

Emacs: fix 'changed on disk'/'Reread from disk' when file has not changed [duplicate]

How to disable Emacs from checking the buffer file was changed outside the editor?
Emacs is really trying to help you here. Read the info page on Protection against Simultaneous Editing.
But, if you still want to avoid that message/prompt, you can redefine the function that is doing the prompting:
(defun ask-user-about-supersession-threat (fn)
"blatantly ignore files that changed on disk"
)
(defun ask-user-about-lock (file opponent)
"always grab lock"
t)
The second function there is for when two people are using Emacs to edit the same file, and would provide a similar prompt (but not the one you seemed to refer to in the question).
I'd advise against overriding the two routines, but it's there if you want.
On the off chance global-auto-revert-mode is on, you could disable that. Add this to your .emacs:
(global-auto-revert-mode -1)
You can tell if the mode is on by looking at the variable of the same name:
C-h v global-auto-revert-mode RET
If the value is t, then the mode is on, otherwise it is off.
I have the following in my .emacs. It makes Emacs only ask about really changed files. If a file remains the same bytewise, just its timestamp is updated, as often happens when you switch branches in VCS, this "change" is ignored by Emacs.
;; Ignore modification-time-only changes in files, i.e. ones that
;; don't really change the contents. This happens often with
;; switching between different VC buffers.
(defun update-buffer-modtime-if-byte-identical ()
(let* ((size (buffer-size))
(byte-size (position-bytes size))
(filename buffer-file-name))
(when (and byte-size (<= size 1000000))
(let* ((attributes (file-attributes filename))
(file-size (nth 7 attributes)))
(when (and file-size
(= file-size byte-size)
(string= (buffer-substring-no-properties 1 (1+ size))
(with-temp-buffer
(insert-file-contents filename)
(buffer-string))))
(set-visited-file-modtime (nth 5 attributes))
t)))))
(defun verify-visited-file-modtime--ignore-byte-identical (original &optional buffer)
(or (funcall original buffer)
(with-current-buffer buffer
(update-buffer-modtime-if-byte-identical))))
(advice-add 'verify-visited-file-modtime :around #'verify-visited-file-modtime--ignore-byte-identical)
(defun ask-user-about-supersession-threat--ignore-byte-identical (original &rest arguments)
(unless (update-buffer-modtime-if-byte-identical)
(apply original arguments)))
(advice-add 'ask-user-about-supersession-threat :around #'ask-user-about-supersession-threat--ignore-byte-identical)
In my case I wanted:
(setq revert-without-query '(".*"))
Documentation for revert-without-query:
Specify which files should be reverted without query.
The value is a list of regular expressions.
If the file name matches one of these regular expressions,
then ‘revert-buffer’ reverts the file without querying
if the file has changed on disk and you have not edited the buffer.
I had annoyance with this because every time I switched branches in git, emacs thought all my files had changed.
Revbuffs helps you cope with the symptoms of this. It allows you to cause all your buffers to be reloaded.
You can also try (global-auto-revert-mode) which will automatically revert your files to what's on disk.

Adding hook on elisp function

I'm using Emacs.
Is there any way to add hook on a function?
Assume that there is a markdown-export function.
It is designed to export HTML file into current directory where current working 'markdown file' exsits.
But, I want to export HTML file into another directory. How can I do that without modification on Emacs markdown plugin (markdown-mode.el)?
This is markdown-mode.el's export function:
(defun markdown-export (&optional output-file)
"Run Markdown on the current buffer, save to file, and return the filename.
If OUTPUT-FILE is given, use that as the filename. Otherwise, use the filename
generated by `markdown-export-file-name', which will be constructed using the
current filename, but with the extension removed and replaced with .html."
(interactive)
(unless output-file
(setq output-file (markdown-export-file-name ".html")))
(when output-file
(let* ((init-buf (current-buffer))
(init-point (point))
(init-buf-string (buffer-string))
(output-buffer (find-file-noselect output-file))
(output-buffer-name (buffer-name output-buffer)))
(run-hooks 'markdown-before-export-hook)
(markdown-standalone output-buffer-name)
(with-current-buffer output-buffer
(run-hooks 'markdown-after-export-hook)
(save-buffer))
;; if modified, restore initial buffer
(when (buffer-modified-p init-buf)
(erase-buffer)
(insert init-buf-string)
(save-buffer)
(goto-char init-point))
output-file)))
=====================================================================
I have made an advice to save exported HTML at temp directory
Here is the code.
(defadvice markdown-export (around set-temp-path-for-exported-file activate)
(ad-set-arg 0 (format "%s/%s" "~/.emacs.d/temp-dir" (file-name-nondirectory buffer-file-name)))
ad-do-it)
Thanks!!!!!!!!!!!!!!
In this case you do not need to hook on this function since it already accepts the filename as an argument, unfortunately it does not accept the filename when called interactively. As a workaround you can define a simple wrapper around the function like follows
(defun my-markdown-export (&optional file)
(interactive (list (ido-read-file-name "Export as: ")))
(markdown-export file))
The advice mechanism is a bit like having hooks for any arbitrary function, but here you have actual hooks you can use, as well as a function argument which addresses your requirement directly.
So you can:
(a) Pass the function any arbitrary output filename.
(b) Use the provided markdown-before-export-hook to setq whichever variables you need to (which at a glance looks like output-file, output-buffer, and output-buffer-name).

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

Emacs in place filename / path insertion

I am looking for a way to insert a filename/path inline while editing. Something like an inline ido-style file selection would be perfect. Is there anything like that out there?
I always use comint-dynamic-complete-filename for this. This does not seem to be loaded by default, but provided by comint-mode. Thus you could put something like
(autoload 'comint-dynamic-complete-filename "comint" nil t)
(global-set-key "\M-]" 'comint-dynamic-complete-filename)
in your ~/.emacs or the like. Use your own prefered keybinding of course.
Okay, if you want to just insert the current file name at point, then
(insert (expand-file-name (buffer-file-name)))
should do it.
If you want to be able to find a file at any path, then you'll want to replicate some of the code in find-file-noselect in files.el at about line 1714 .
In either case, if you want to bind this a a function, you'll probably want
(defun insert-file-name-at-point ()
(interactive) .... )
(defun insert-file-name (file &optional relativep)
"Read file name and insert it at point.
With a prefix argument, insert only the non-directory part."
(interactive "fFile: \nP")
(when relativep (setq file (file-name-nondirectory file)))
(insert file))