How can I idiomatically transform some text in place using elisp? - emacs

I've got some json that I'd like to process in emacs. I've found and used the elisp library to extract the desired content from the json, and I'd like to replace the json with the elisp equiivalent that I've extracted.
This is what I've written:
(defun extract-foo (start end)
"Extract the foo field from a json object in the region"
(interactive "r")
(let ((my_json (cdr (assoc 'FOO (json-read-from-string (buffer-substring-no-properties start end))))))
(delete-region start end)
(SOMETHING)
))
I'm stuck at the something. I can't seem to find a way to write the contents of my_json to the buffer at the mark. The only way I can think of is to save the text instead to a temporary buffer, and then (insert-buffer) it. This seems excessive to me though.
How can I do this idiomatically in elisp?

to write back JSON partial
(insert (format "%s" (json-encode my-json)))
to write back elisp:
(insert (format "%s" my-json))
Note that your use of underscore in variable naming is contrary to convention.

Related

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

Dired: Duplicate Files (the operation, not how to get rid of 'em)

Sometimes I want to create a duplicate of a number of files (say, config files), which initially should have the same content as the initial files. Therefore I'd like to be able mark some files in dired and "duplicate" them, this duplication procedure could work similar like the duplication procedure utilised by most file managers, when pasting to the original directory: The file names of the duplicated get "(Copy)" appended (just before the file extension).
I can't seem to find a built-in dired function that does this, maybe someone can help/has already created a function like this?
Help is much appreciated!
There is one function that does what you want: dired-do-copy-regexp
Example of use:
mark the files
M-x dired-do-copy-regexp
\(.*\)\.\(.*\)
\1 (copy).\2
For a file named foo.txt you will be creating another named foo (copy).txt
Note that my first regexp has two groups, and the second regexp references them. You can do much more complicated things, if needed.
Maybe you will want to rename the functions (I didn't come up with better names), maybe some more elaborate formatting, if you wish...
(defcustom dired-keep-marker-version ?V
"Controls marking of versioned files.
If t, versioned files are marked if and as the corresponding original files were.
If a character, copied files are unconditionally marked with that character."
:type '(choice (const :tag "Keep" t)
(character :tag "Mark"))
:group 'dired-mark)
(defun dired-version-file (from to ok-flag)
(dired-handle-overwrite to)
(dired-copy-file-recursive from to ok-flag dired-copy-preserve-time t
dired-recursive-copies))
(defun dired-do-version (&optional arg)
"Search for numeric pattern in file name and create a version of that file
with that number incremented by one, or, in case such file already exists,
will search for a file with the similar name, incrementing the counter each
time by one.
Additionally, if called with prefix argument, will prompt for number format.
The formatting is the same as is used with `format' function."
(interactive "P")
(let ((fn-list (dired-get-marked-files nil nil)))
(dired-create-files
(function dired-version-file) "Version" fn-list
(function
(lambda (from)
(let (new-name (i 0) (fmt (if arg (read-string "Version format: " "%d") "%d")))
(while (or (null new-name) (file-exists-p new-name))
(setq new-name
(if (string-match "^\\([^0-9]*\\)\\([0-9]+\\)\\(.*\\)$" from)
(concat (match-string 1 from)
(format fmt
(+ (string-to-number (match-string 2 from)) (1+ i)))
(match-string 3 from))
(concat from (format (concat "." fmt) i)))
i (1+ i))) new-name)))
dired-keep-marker-version)))
(define-key dired-mode-map (kbd "c") 'dired-do-version)
Also, I've originally used v to bind this function because I don't use dired-view, but you would need to bind that inside direds hook. c just was the first undefined key, so I used it.
In the Dired mode, put cursor on the file you want to duplicate or mark that file, then press "C". You will be prompted for new name.
You can use this feature to copy files between Dired buffers as well. To make it possible put into your init file:
(setq dired-dwim-target t)

Replace file at point in emacs

I found the function here for replace filen at point but it doesn't seem to work properly: http://www.emacswiki.org/emacs/InsertFileName. It correctly finds the file-at-point but the replace appears to take the file you input and not the file detected originally from what I can see. I'm not sure how to fix this.
If I Run the function on /home/testfile
First it says file to replace so for example: /home/secondfile
Then it says replace '/home/secondfile' with: /home/secondfile
and then it says: No file at point
Any ideas??
Here is the function:
(autoload 'ffap-guesser "ffap")
(autoload 'ffap-read-file-or-url "ffap")
(defun my-replace-file-at-point (currfile newfile)
"Replace CURRFILE at point with NEWFILE.
When interactive, CURRFILE will need to be confirmed by user
and will need to exist on the file system to be recognized,
unless it is a URL.
NEWFILE does not need to exist. However, Emacs's minibuffer
completion can help if it needs to be.
"
(interactive
(let ((currfile (ffap-read-file-or-url "Replace filename: "
(ffap-guesser))))
(list currfile
(ffap-read-file-or-url (format "Replace `%s' with: "
currfile) currfile))))
(save-match-data
(if (or (looking-at (regexp-quote currfile))
(let ((filelen (length currfile))
(opoint (point))
(limit (+ (point) (length currfile))))
(save-excursion
(goto-char (1- filelen))
(and (search-forward currfile limit
'noerror)
(< (match-beginning 0) opoint))
(>= (match-end 0) opoint))))
(replace-match newfile)
(error "No file at point to replace"))))
There are probably a few things wrong/going on here. The first is, your point position when you are executing this. The second is, if you are using /home/user/something, that there is a strong possibility you will have mismatch between /home/user/something and ~/something (ffap returns the latter while at the point you may have written the former).
First:
The use of looking-at with the regexp quoted filename expects the point to be at the beginning: e.g. |/home/user/something.
Its partner, looking-back expects /home/user/something|. Being somewhere in the middle will throw this error.
One quick fix for this is changing looking-at to thing-at-point-looking-at.
Second:
If you have written /home/user/something, ffap functions (in my case) shorten this using ~.
There are probably some settings that govern this, but the easiest, quick fix I know of is using expand-file-name. This will take care of the first case, and if it is written as ~/something, the save-excursion body will replace it in the alternate case.
The only negative result I see is that, you might sometimes replace:
/home/user/something with ~/somethingelse
But, anyways, these two quick fixes just result in this complete change:
(thing-at-point-looking-at (regexp-quote (expand-file-name currfile)))
can't see where "ffap-guesser" is defined. Looks like a bug.
Maybe try instead
"find-file-at-point"

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

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

Help with an interactive Emacs Lisp function for replacing text

I've been using Emacs for a couple months now, and I want to get started in elisp programming. Specifically, I'd like to write my own interactive function. However, I'm more than a bit lost. (interactive ...) has tons of options and I'm not sure which one I want. Then, I don't really know the names of the functions I need. If someone could kindly help me turn my pseudocode into real code, I would be mighty appreciative! (And as always, any links to informative places would be good. Right now I've just been reading this.)
Here is pseudocode for what I'd like to do:
(defun my-func (buffer) ; I think I need the buffer as an arg?
"does some replacements"
(interactive ???) ; ?
(let (replacements (list
'("a-regexp-string" . "a-replacement-string-with-backreferences")
...)) ; more of the above
(while replacements
(let (current (car replacements)) ; get a regexp-replacement pair
(some-regexp-replace-func buffer (car current) (cdr current)) ; do the replacement
(setq replacements (cdr replacements))))))
First, from the looks of your function you would probably be doing it in the current buffer, so no, you don't need to have a 'buffer' argument. If that's a bad assumption, I can change the code. Next, in a 'let' if you are assigning to variables you need another set of parens around each pair of var/value. Finally, when looping through a list I prefer to use functional-programming-like functions (mapcar, mapc, etc.). I'll try to inline some comments here:
(defun my-func ()
"Do some replacements"
(interactive)
(let ((replacements (list '("foo" . "bar")
'("baz" . "quux"))))
(save-excursion ; So point isn't moved after this function
(mapc (lambda (x) ; Go through the list, with this 'inline' function
; being called with each element as the variable 'x'
(goto-char (point-min)) ; Start at the beginning of the buffer
(while (re-search-forward (car x) nil t) ; Search for the car of the replacement
(replace-match (cdr x)))) ; And replace it with the cdr
replacements)))) ; The list we're mapc'ing through
As for what to read, I'd suggest the Elisp manual that comes with Emacs.