How to edit a Gist filename using `gist.el` with Emacs - emacs

Can anyone please steer me in the right direction regarding how to edit the filename of an existing Gist using gist.el. I've tried modifying gist-edit-current-description to handle filename modification, but my attempted variations haven't worked. Here is the gist-edit-current-description function -- I assume editing the filename would be something similar to the description:
(defun gist-edit-current-description ()
(interactive)
(let* ((id (tabulated-list-get-id))
(gist (gist-list-db-get-gist id))
(old-descr (oref gist :description))
(new-descr (read-from-minibuffer "Description: " old-descr)))
(let* ((g (clone gist
:files nil
:description new-descr))
(api (gist-get-api t))
(resp (gh-gist-edit api g)))
(gh-url-add-response-callback resp
(lambda (gist)
(gist-list-reload))))))
This may help give someone some ideas -- it is a method of setting the filename at the time of Gist creation (and it is based on a prior answer by #Jordon Biondo -- https://stackoverflow.com/a/22973794/2112489 ):
(defun gist-region-with-filename-description (begin end &optional filename description private callback)
"Post the current region as a new paste at gist.github.com
Copies the URL into the kill ring.
With a prefix argument, makes a private paste."
(interactive "r\nsGist Description: \nP") ;; we handle the prompt here!
(let* ((file (or (buffer-file-name) (buffer-name)))
(name (file-name-nondirectory file))
(ext (or (cdr (assoc major-mode gist-supported-modes-alist))
(file-name-extension file)
"txt"))
(fname (if filename filename (concat (file-name-sans-extension name) "." ext)))
(files (list
(gh-gist-gist-file "file"
:filename fname
:content (buffer-substring begin end)))))
;; finally we use our new arg to specify the description in the internal call
(gist-internal-new files private description callback)))
(defun gist-buffer-with-filename-description (&optional filename description private)
"Post the current buffer as a new paste at gist.github.com.
Copies the URL into the kill ring.
With a prefix argument, makes a private paste."
(interactive "P")
(let* (
(filename (if filename filename (read-string "Filename: " (buffer-name))))
(description (if description description (read-string "Description: " (buffer-name)))))
(gist-region-with-filename-description (point-min) (point-max) filename description private nil)))

The developer added this feature on December 29, 2014 -- the function is gist-mode-write-file, which is used when visiting the file. Issue number 55: https://github.com/defunkt/gist.el/issues/55

Related

Org-mode archive entire file if all TODOs DONE

While I've seen a lot of SO questions regarding archiving sub-trees, I use org-journal to create a daily file each day with a template (eg. 2018-09-14.org) which then I then record todos in a pre-templated structure for personal, work or what have you which go through various states till they are either finished DONE or cancelled KILL (I find this approach works for me since it also allows me visually to see in the agenda view how long a task has been hanging around since started).
I am trying to write an interactive function which:
processes a list of all my .org agenda files, and
if it detects all TODOs and DONE or KILL in the file (or there are none present),
prompts me y, n, skip to move the entire file to its whatever.org_archive
(starting to see slowdowns with agenda builds 5 months into using org-mode).
I'm assuming someone else already uses a similar approach ('cause emacs) but was wondering if anyone could point me at a similar function or approach that would be helpful for sussing this out. Googling and thrashing on the elisp has been unproductive so far.
=== One month later ===
Well, teaching myself some lisp has helped but am now at the point where I have the 3 independent functions working, but for some reason am getting an error on calling the final function.
However, I'm getting an error on line 28 with invalid function: on the call to rename-file-buffer-to-org-archive. If someone can see what the problem is, this solves my use case (and probably someone else's which is why I pasted it back here.).
(defun archive-done-org-journal-files ()
"Cycles all org files through checking function."
(interactive)
(save-excursion
(mapc 'check-org-file-finito (directory-files "~/Desktop/test_archives/" t ".org$"))
))
(defun check-org-file-finito (f)
"Checks TODO keyword items are DONE then archives."
(interactive)
(find-file f)
;; Shows open Todo items whether agenda or todo
(let (
(kwd-re
(cond (org-not-done-regexp)
(
(let ((kwd
(completing-read "Keyword (or KWD1|KWD2|...): "
(mapcar #'list org-todo-keywords-1))))
(concat "\\("
(mapconcat 'identity (org-split-string kwd "|") "\\|")
"\\)\\>")))
((<= (prefix-numeric-value) (length org-todo-keywords-1))
(regexp-quote (nth (1- (prefix-numeric-value))
org-todo-keywords-1)))
(t (user-error "Invalid prefix argument: %s")))))
(if (= (org-occur (concat "^" org-outline-regexp " *" kwd-re )) 0)
((rename-file-buffer-to-org-archive)
(kill-buffer (current-buffer)))
(kill-buffer (current-buffer))
)))
(defun rename-file-buffer-to-org-archive ()
"Renames current buffer and file it's visiting."
(interactive)
(let ((name (buffer-name))
(filename (buffer-file-name))
)
(if (not (and filename (file-exists-p filename)))
(error "Buffer '%s' is not visiting a file!" name)
(let ((new-name (concat (file-name-sans-extension filename) ".org_archive")))
(if (get-buffer new-name)
(error "A buffer named '%s' already exists!" new-name)
(rename-file filename new-name 1)
(rename-buffer new-name)
(set-visited-file-name new-name)
(set-buffer-modified-p nil)
(message "File '%s' successfully archived as '%s'."
name (file-name-nondirectory new-name)))))))
So, in the end, this is how I solved it. I'm sure there are optimizations and refactoring to be done here, but this definitely works and is reasonably modular if you need to figure it out. Just change the directory you use (mine is in Dropbox) for your org-files in the archive-done-org-journal-files and this should work for you. I highly recommend testing this on a test archive as per the ~/Desktop/test_archives/ directory as per the actual function just so you can make sure it works as advertised. YMMV. Hope it helps someone!
(defun archive-done-org-journal-files ()
"Cycles all org files through checking function."
(interactive)
(save-excursion
(mapc 'check-org-file-finito (directory-files "~/Desktop/test_archives/" t ".org$"))
))
(defun check-org-file-finito (f)
"Checks TODO keyword items are DONE then archives."
(interactive)
(find-file f)
;; Shows open Todo items whether agenda or todo
(let (
(kwd-re
(cond (org-not-done-regexp)
(
(let ((kwd
(completing-read "Keyword (or KWD1|KWD2|...): "
(mapcar #'list org-todo-keywords-1))))
(concat "\\("
(mapconcat 'identity (org-split-string kwd "|") "\\|")
"\\)\\>")))
((<= (prefix-numeric-value) (length org-todo-keywords-1))
(regexp-quote (nth (1- (prefix-numeric-value))
org-todo-keywords-1)))
(t (user-error "Invalid prefix argument: %s")))))
(if (= (org-occur (concat "^" org-outline-regexp " *" kwd-re )) 0)
(rename-file-buffer-to-org-archive)
(kill-buffer (current-buffer))
)))
(defun rename-file-buffer-to-org-archive ()
"Renames current buffer and file it's visiting."
(interactive)
(let ((name (buffer-name))
(filename (buffer-file-name))
)
(if (not (and filename (file-exists-p filename)))
(error "Buffer '%s' is not visiting a file!" name)
(let ((new-name (concat (file-name-sans-extension filename) ".org_archive")))
(if (get-buffer new-name)
(error "A buffer named '%s' already exists!" new-name)
(rename-file filename new-name 1)
(rename-buffer new-name)
(set-visited-file-name new-name)
(set-buffer-modified-p nil)
(kill-buffer (current-buffer))
(message "File '%s' successfully archived as '%s'."
name (file-name-nondirectory new-name)))))))

Distinguish between single (*.gz) and double (*.tar.gz) file type extensions

I'm looking for some assistance please, to distinguish between a single file extension in dired-mode (e.g., *.gz) and a double file extension (e.g., *.tar.gz).
The following is an excerpt of the function that I use when selecting one or more files in dired-mode to take specific actions -- e.g., open in Emacs, start a process and open externally, or compress / decompress. I originally wrote this function (borrowing excerpts from dired-do-create-files within dired-aux.el) with only single file type extensions in mind, and would now like to expand its functionality to include potential double file type extensions.
(defun test-for-tar-gz-extension ()
(interactive)
(let* (
(fn-list (dired-get-marked-files))
(rfn-list (mapcar (function dired-make-relative) fn-list))
(dired-one-file (and (consp fn-list) (null (cdr fn-list)) (car fn-list)))
(input-filename (if dired-one-file dired-one-file fn-list))
(ext
(cond
((stringp input-filename)
(file-name-extension input-filename))
((listp input-filename)
(file-name-extension (car input-filename)))))
(path (if (stringp input-filename) (file-name-directory input-filename)))
(dired-buffer-name (buffer-name))
(msword-regexp '("doc" "docx"))
(dired-tar '("tar.gz")))
(cond
;; http://www.emacswiki.org/emacs/DiredTar
((extension equals ".tar.gz")
(dired-tar-pack-unpack))
((extension equals ".gz" (but not .tar.gz))
(dired-do-compress))
((regexp-match-p msword-regexp ext)
(start-process "ms-word" nil "open" "-a" "Microsoft Word" input-filename))
(t
(message "Go fish.")))))
;; https://github.com/kentaro/auto-save-buffers-enhanced
;; `regexp-match-p` function modified by #sds on stackoverflow
;; http://stackoverflow.com/a/20343715/2112489
(defun regexp-match-p (regexps string)
(and string
(catch 'matched
(let ((inhibit-changing-match-data t)) ; small optimization
(dolist (regexp regexps)
(when (string-match regexp string)
(throw 'matched t)))))))
Not sure IIUC, here a draft how to do that part in question:
(defun gz-only ()
"List marked files in dired-buffer ending at `.gz', but not ending at `.tar.gz'"
(interactive)
(let ((flist (dired-get-marked-files))
erg)
(dolist (ele flist)
(and (string-match "\.gz$" ele)(not (string-match "\.tar\.gz$" ele))
(add-to-list 'erg ele)))
(when (interactive-p) (message "%s" erg))))

Gist (gist.el / Emacs) -- Set the `description` at the time of creation

The default behavior of gist-region is to leave the description blank. To set the description, it is necessary to switch to the gist-list buffer and then use the function gist-edit-current-description to set the description.
I would like to be able to set the description at the same time that the gist is created, without switching to the gist-list buffer. A mini-buffer prompt that defaults to the buffer-name would be the preferred method of handling this. How can this be accomplished programmatically?
Here are the two main functions in gist.el that are responsible for the behavior described above:
(defun gist-region (begin end &optional private callback)
"Post the current region as a new paste at gist.github.com
Copies the URL into the kill ring.
With a prefix argument, makes a private paste."
(interactive "r\nP")
(let* ((file (or (buffer-file-name) (buffer-name)))
(name (file-name-nondirectory file))
(ext (or (cdr (assoc major-mode gist-supported-modes-alist))
(file-name-extension file)
"txt"))
(fname (concat (file-name-sans-extension name) "." ext))
(files (list
(gh-gist-gist-file "file"
:filename fname
:content (buffer-substring begin end)))))
(gist-internal-new files private nil callback)))
(defun gist-edit-current-description ()
(interactive)
(let* ((id (tabulated-list-get-id))
(gist (gist-list-db-get-gist id))
(old-descr (oref gist :description))
(new-descr (read-from-minibuffer "Description: " old-descr)))
(let* ((g (clone gist
:files nil
:description new-descr))
(api (gist-get-api t))
(resp (gh-gist-edit api g)))
(gh-url-add-response-callback resp
(lambda (gist)
(gist-list-reload))))))
Take a look at the function gist-internal-new that gist-region is calling.
(gist-internal-new FILES &optional PRIVATE DESCRIPTION CALLBACK)
The third param is the description but gist-region is setting it to nil. You can modify that last expression in gist-region to read a string from the minibuffer to specify a description
(gist-internal-new files private (read-from-minibuffer "Gist Description: ") callback)
Or better yet, don't modify the function and just write your own! That way, you don't mess with other packages that use the function.
Here is just a slightly a modified version that adds an extra parameter and uses interactive to automatically prompt the user for a description while still allowing prefix args to make private gists.
Unlike the first example when we used `read-from-minibuffer' this one will allow you to use the function in code and specify the description directly without forcing a prompt to be used unless it's called interactively.
;; note that we added the DESCRIPTION argument
(defun gist-region-with-description (begin end &optional description private callback)
"Post the current region as a new paste at gist.github.com
Copies the URL into the kill ring.
With a prefix argument, makes a private paste."
(interactive "r\nsGist Description: \nP") ;; we handle the prompt here!
(let* ((file (or (buffer-file-name) (buffer-name)))
(name (file-name-nondirectory file))
(ext (or (cdr (assoc major-mode gist-supported-modes-alist))
(file-name-extension file)
"txt"))
(fname (concat (file-name-sans-extension name) "." ext))
(files (list
(gh-gist-gist-file "file"
:filename fname
:content (buffer-substring begin end)))))
;; finally we use our new arg to specify the description in the internal call
(gist-internal-new files private description callback)))

Asking emacs for default directory path "once"

I want to have a variable that keeps the default directory a user enters and keep using it throughout the run of emacs.
Basically, when the user executes a custom command, the prompt will ask for a default directory path to execute the command (only once) and whenever the user calls the same command emacs uses the same path onward.
How can I program that snippet of code in lisp?
I basically want this code in the igrep library to accept the input from user once and not ask again:
(defvar default-files-string-new "*.[sch]")
(defun igrep-read-files (&optional prompt-prefix)
"Read and return a file name pattern from the minibuffer.
If `current-prefix-arg' is '(16) or '(64), read multiple file name
patterns and return them in a list. Optional PROMPT-PREFIX is
prepended to the \"File(s): \" prompt."
(let* ((default-files (igrep-default-files))
(default-files-string (mapconcat 'identity default-files " "))
(insert-default-directory igrep-insert-default-directory)
(file (igrep-read-file-name
(igrep-prefix prompt-prefix
(if default-files
(format "File(s) [default: %s]: "
default-files-string)
"File(s): "))
nil (if default-files default-files-string "") nil nil
'igrep-files-history))
(files (list file)))
(if (or igrep-read-multiple-files
(and (consp current-prefix-arg)
(memq (prefix-numeric-value current-prefix-arg)
'(16 64))))
(let* ((key (igrep-default-key 'exit-minibuffer
minibuffer-local-completion-map
"\r"))
(prompt
(igrep-prefix prompt-prefix
(if igrep-verbose-prompts
(format "File(s): [Type `%s' when done] "
(key-description key))
"File(s): "))))
(while (and (setq file
(igrep-read-file-name prompt
nil "" nil nil
'igrep-files-history))
(not (equal file "")))
(setq files (cons file files)))))
(mapcar (lambda (file)
(if (file-directory-p file)
;; really should map expand-file-name over default-files:
(expand-file-name (if default-files default-files-string-new "*")
file)
file))
(nreverse files))))
You could use advices to do that:
(defvar wd-alist nil)
(mapc
(lambda (function)
(eval
`(defadvice ,function (around ,(intern (format "%s-wd" function)) activate)
(let ((wd (cdr (assoc ',function wd-alist))))
(unless wd
(setq wd (read-file-name "Default directory: "))
(push (cons ',function wd) wd-alist))
(let ((default-directory wd))
ad-do-it)))))
'(grep-find))
The variable wd-list stores the association (FUNCTION . PATH). The list mapc iterate over are the advised functions. Now, when calling find-grep, it asks for the working directory (after interactive arguments, so you first have to type the pattern and enter...) and stores it in wd-list for further use. Now your find-grep are always done in that directory.
You could have a custom variable for the sane default, and then have the user enter the path or accept the default on the first call.
(defcustom default-path "/tmp/foo" "Path")
(setq current-path nil)
(defun foo ()
(interactive)
(unless current-path
(setq current-path
(read-from-minibuffer
(format "Path [%s]" default-path) nil nil t nil default-path)))
(message "Path is: %s" current-path))
The first time you do M-x foo, it prompts for the path. A common idiom is to allow the user to specify a prefix argument when they want to change the value (after the first time.) This code will have the desired effect:
(defun foo (choose)
(interactive "P")
(when (or choose (not current-path))
(setq current-path
(read-from-minibuffer
(format "Path [%s]" default-path) nil nil t nil default-path)))
(message "Path is: %s" current-path))
Now doing M-x foo is the same as before, but C-0 M-x foo will prompt for a new value.
In your example, something like this will work.
(defun igrep-read-files (&optional prompt-prefix)
(interactive "P")
(when (or prompt-prefix (not current-path ))
(setq current-path
(read-file-name "Dir: " default-path nil t)))
(message (expand-file-name default-files-string-new current-path)))
Have a look at the code of sendmail-query-once.
Although it's not very fashionable to do this sort of thing.
Usually package writers pick a sane default and let the user
customize it as they want.

define your own tag in org-mode

There are Tags as in #+AUTHOR or #+LATEX in org-mode - are they called tags? I'd like to define my own tag which calls a function to preprocess the data and then outputs it - if the export target is LaTeX.
My solution was defining an own language, qtree, for SRC blocks.
#+BEGIN_SRC qtree
[.CP [.TP [.NP [] [.N' [.N Syntax] []]] [.VP [] [.V' [.V sucks] []]]]]
#+END_SRC
And process it accordingly. I even added a qtree-mode with paredit.
And a landscape parameter if the trees grow big. https://github.com/Tass/emacs-starter-kit/blob/master/vendor/assorted/org-babel-qtree.el
(require 'org)
(defun org-babel-execute:qtree (body params)
"Reformat a block of lisp-edited tree to one tikz-qtree likes."
(let (( tree
(concat "\\begin{tikzpicture}
\\tikzset{every tree node/.style={align=center, anchor=north}}
\\Tree "
(replace-regexp-in-string
" \\_<\\w+\\_>" (lambda (x) (concat "\\\\\\\\" (substring x 1)))
(replace-regexp-in-string
(regexp-quote "]") " ]" ; qtree needs a space
; before every closing
; bracket.
(replace-regexp-in-string
(regexp-quote "[]") "[.{}]" body)) ; empty leaf
; nodes, see
; http://tex.stackexchange.com/questions/75915
) ; For
; http://tex.stackexchange.com/questions/75217
"\n\\end{tikzpicture}"
)))
(if (assoc :landscape params)
(concat "\\begin{landscape}\n" tree "\n\\end{landscape}")
tree)))
(setq org-babel-default-header-args:qtree '((:results . "latex") (:exports . "results")))
(add-to-list 'org-src-lang-modes '("qtree" . qtree))
(define-generic-mode
'qtree-mode ;; name of the mode to create
'("%") ;; comments start with '%'
'() ;; no keywords
'(("[." . 'font-lock-operator) ;; some operators
("]" . 'font-lock-operator))
'() ;; files for which to activate this mode
'(paredit-mode) ;; other functions to call
"A mode for qtree edits" ;; doc string for this mode
)
They seem to be called keywords for in-buffer settings no more. Whatever they're called, they don't seem to be user-definable.
What you want to do is extremely related to a common way of handling whereas to export with xelatex or pdflatex as described on Worg.
The relevant part would be :
;; Originally taken from Bruno Tavernier: http://thread.gmane.org/gmane.emacs.orgmode/31150/focus=31432
(defun my-auto-tex-cmd ()
(if (string-match "YOUR_TAG: value1" (buffer-string))
(do something))
(if (string-match "YOUR_TAG: value2" (buffer-string))
(do something else))
(add-hook 'org-export-latex-after-initial-vars-hook 'my-auto-tex-cmd)