Append org-agenda-files with files of another directory - emacs

So i try to create a list of string in the form
"a" "b" "c"
This is a list of files which i want to pass to org-agenda-files
As for now this looks like this
(setq org-agenda-files "~/.tasks.org")
Now i need to add some files coming from a directory. i can get those with directory-files-recursively "~/orgroam/daily" "\\[^.].*\.org\'". this will return lets say ("~/b" "~/c")`.
The problem comes when i try to concatenate the "a". What i have tied so far is
(list "~/.tasks.org" (mapconcat #'identity (directory-files-recursively "~/orgroam/daily" "\\`[^.].*\\.org\\'") " "))
and
(concat "~/.tasks.org " (mapconcat #'identity (directory-files-recursively "~/orgroam/daily" "\\`[^.].*\\.org\\'") " "))
but this returns "a" "b c" and "a b c"
Another thing i tried out is to add the result of directory-files-recursively after the (setq org-agenda-files '("~/.tasks.org")) using add-to-list. in this case i get Wrong type argument: stringp, ("~/orgroam/daily/2022-01-19.org" "~/orgroam/daily/2022-01-28.org"....)
Does anyone have a suggestion to achieve the desired output for org-agenda-files?

If you want to include all org files from another directory, you can:
(setq org-agenda-files '("~/.tasks.org"
"~/orgroam/daily/"))
If you still want to append files, you can:
(setq org-agenda-files (append
'("~/.tasks.org")
(directory-files-recursively "~/Documents/Org/notes/" "\\`[^.].*\\.org\\'")))
As a friendly reminder, numerous Org files may cause Agenda to be very slow, you can refer to this blog.

Related

Emacs dired - using predefined variable

In emacs dired I want to do something I do quite often in Microsoft PowerShell.
In PowerShell, I have a set of folders that I always use, and I assign their full path to global variables in my profile script (similar to init.el in the emacs world) e.g:
$standardTemp = "C:\Long\Path\To\Folder"
If I am in another folder and I want to copy something to the above folder, I do:
copy myFile $standardTemp
Even more useful as a feature, is if I put a backslash after $standardTemp, it will expand it out, so I can go into subfolders if I need to. This is a very awesome feature and saves me a lot of time.
With the dired copy command can I do something similar, if I define variables with e.g. setq in my init.el file?
How about something like this?
;; Use ido
(require 'ido)
(ido-mode t)
;; Make a hash table to hold the paths
(setq my-target-dirs (make-hash-table :test 'equal))
;; Put some paths in the hash (sorry for Unix pathnames)
(puthash "home" "/home/jhrr/" my-target-dirs)
(puthash "target" "/home/jhrr/target/" my-target-dirs)
;; A function to return all the keys from a hash.
(defun get-keys-from-hash (hash)
(let ((keys ()))
(maphash (lambda (k v) (push k keys)) hash)
keys))
;; And the function to prompt for a directory by keyword that is looked
;; up in the hash-table and used to build the target path from the
;; value of the lookup.
(defun my-dired-expand-copy ()
(interactive)
(let* ((my-hash my-target-dirs)
(files (dired-get-marked-files))
(keys (get-keys-from-hash my-hash)))
(mapc (lambda (file)
(copy-file file
(concat
(gethash
(ido-completing-read
(concat "copy " file " to: ") keys) my-hash)
(file-name-nondirectory file))))
files)))
It's not exhaustively tested as I just whipped it up in 10 minutes, but it does the job and it can handle multiple files.
You will need to open the dired buffer in the directory the files are in and mark each file you want to copy with 'm', then invoke my-dired-expand-copy and it will prompt you for a target destination (in the form of a keyword from the hash-table we set-up) for the file before, finally, copying the file over to the directory that maps to the target keyword.
It doesn't quite cover the sub-directories use-case you mention, but it shouldn't be too hard to get there given a bit more hacking.
UPDATE:
This should now prompt you to be able to descend into subdirectories from an original target; maybe not the most mind-shatteringly wonderful UX on the whole, but, it works:
(defun my-dired-expand-copy-2 ()
(interactive)
(let* ((my-hash my-target-dirs)
(files (dired-get-marked-files))
(keys (get-keys-from-hash my-hash)))
(mapc (lambda (file)
(let ((target (gethash
(ido-completing-read
(concat "copy " file " to: ") keys) my-hash)))
(if (y-or-n-p "Descend?")
;; Descend into subdirectories relative to target dir
(let ((new-target (ido-read-directory-name "new dir: " target)))
(copy-file file (concat new-target
(file-name-nondirectory file)))
(message (concat "File: " file " was copied to " new-target)))
;; Else copy to root of originally selected directory
(copy-file file (concat target (file-name-nondirectory file)))
(message (concat "File: " file " was copied to " target)))))
files)))
When I need to use dired to get to frequently-used directories, I use the standard emacs bookmarking capabilities.
I manually navigate to the directory, and press
C-x r m
to execute the command
bookmark-set
You'll be prompted for a name for the bookmark. Enter a shortcut that you can remember.
At this point, anytime you want to open that directory within dired, simply execute the command
bookmark-jump
with the keys
C-x r b
Enter your shortcut to the directory, and dired will open to that location.
To copy from one directory to another, ensure you have the following set in your init file
(setq dired-dwim-target t)
Then you can open a dired window for the source directory, and another window for the target directory within in the same frame, and dired will automatically assign the source and target location to the appropriate directories.
Note this is just a subset of what emacs bookmarks can do for you!
Chris
In addition to using bookmarks, consider using directory-name aliases (e.g. symlinks) or directory-abbrev-alist. See the Emacs manual, node File Aliases.
If you want to insert the value of an environment variable into the minibuffer, you can do it this way:
C-u M-: (getenv "THE-VARIABLE")
where THE-VARIABLE is the variable name. Using C-u inserts the value of evaluating the sexp into the current buffer (in this case the minibuffer).
So you would, say, use C to copy the marked files in Dired, and then use C-u with a getenv sexp for the existing variable you have, to insert its value into the minibuffer when prompted for the directory to copy to.
(Depending on your Emacs setup, you might need to set enable-recursive-minibuffers to non-nil, to be able to use M-: from the minibuffer.)

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)

How to concat a pattern in Emacs Lisp?

I think the title can't describe the question clearly. But I will try to describe in more detail words.
Here is my Emacs lisp code to set the backup folder:
(setq backup-directory-alist '(("" . "/hom/test/.backups")))
But if I want to do this job by generating the backup path in a dynamic way, which generates the path based on the user's path.
Here is what I am trying to do:
(setq temp-file-folder "/home/test")
(setq backups-save-folder (concat temp-file-folder "/.backups"))
(setq backup-directory-alist '(("" . backups-save-folder)))
But it doesn't work. The final output of above code is:
(("" . backups-save-folder))
I think what I am trying to get should be:
(("" . "/home/test/.backups"))
So, what's the right code to generate the path dynamic? What's the meaning of . in the code?
Thanks
First, a point. You can use the format function instead of concat to do this (it's similar to printf). Here is an example
(let ((home_dir "/home/noufal")
(posts_dir "posts"))
(format "%s/%s" home_dir posts_dir))
evaluates to "/home/noufal/posts"
It's not idiomatic lisp to setq temporary variables. You should work within the scope of a let construct which can bind values to a few temporary symbols and then get your work done.
In your last case, you've used a ' before your second parameter. This will prevent it from being evaluated (it's synonymous to "quote literally") and so, the backups-save-folder will not be replaced with the value. If you want do that, do something like this.
(setq backup-directory-alist (list (cons "" backups-save-folder)))
The . is a way of representing lists whose cdr is not a list. Refer the elisp documentation for more details.
seems you don't need an associated list, designed to store paired values, but a simple list:
get the current user directory (getenv "HOME")
maybe combined with another arbitrary string "-BACKSTAGE-AREA"
(setq my-dir (list (concat (getenv "HOME")"-BACKSTAGE-AREA")))
get the directory name stored that way with
(car my-dir)
According to Noufal Ibrahim's answer, here is my final code:
(setq backup-directory-alist (list (cons ""
(let ((backups_dir "/.backups")
(temp_folder desktop-temp-file-folder))
(format "%s/%s" temp_folder backups_dir)))))
It works.
Thanks

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

What is the canonical way to list numbered backup files emacs has created?

I know how to configure emacs to keep numbered backups. I don't know the most canonical way to find those numbered backups.
The emacs function "find-backup-file-name" seems like it is the closest. Its documentation states:
This function computes the file name for a new backup file for filename. It may also propose certain existing backup files for deletion. find-backup-file-name returns a list whose CAR is the name for the new backup file and whose CDR is a list of backup files whose deletion is proposed.
However, this is not what I am looking for. I'm looking for a list of ALL previously created backup files. Here's the code (paraphrased) I have written to accomplish this:
(defvar backup-directory "~/emacs.d/backups/")
(defun get-backup-pattern (file-name)
(concat "*" (replace-regexp-in-string "\/" "\\!" file-name t t) ".~*"))
(butlast
(split-string
(shell-command-to-string
(concat "find "
backup-directory
" -name \""
(get-backup-pattern (buffer-file-name))
"\""))
"\n"))
This method works fine. However, shelling out to "find" seems a like a hack to me; Especially since this method is platform specific.
Is there a built-in method I should use or at least something more idiomatic?
Personally, I don't save backup files in a central folder so I can't provide working code, but if you want to search the contents of a directory, use directory-files.
So here is the solution I've decided on. I went away from using the *nix find command and am using directory-files as suggested.
(defun get-filter-pattern (file-name)
(concat (replace-regexp-in-string "\/" "!" file-name t t)
".~[0-9]*~*$"))
(defun filter (condp lst)
(delq nil
(mapcar (lambda (x) (and (funcall condp x) x)) lst)))
(defun filter-files (backup-directory buffer-file-name)
(mapcar (lambda (backup-name) (concat backup-directory backup-name))
(filter (lambda (backup-name)
(string-match (get-filter-pattern buffer-file-name) backup-name))
(directory-files backup-directory))))
Perhaps this isn't quite as optimized as using find. However, it should be platform independent (ie can use on Windows).