How do I get the path from which init.el was loaded? - emacs

I am looking to create a custom config for emacs to use for Erlang work and I want to refer to my custom EDTS repo as being under the directory from which init.el was loaded. Right now I have this:
(add-to-list 'load-path "~/.emacs-edts/edts/")
But I would rather not hardcode it and refer to it by variable.
Suggestions?

Strictly speaking the answer is (file-name-directory user-init-file), but instead see C-hv user-emacs-directory

I have the following snippet in my init.el:
(setq my-init-dir
(file-name-directory
(or load-file-name (buffer-file-name))))
This has the advantage of working whether init.el is in your emacs.d directory or not.

I have the following in my init file:
(defun my-file-name-basename (s)
"The directory name, without the final part.
For example:
(my-file-name-basename \"alpha/beta/gamma\") => \"alpha/beta\""
(substring (file-name-directory s) 0 -1))
;; Note: Normally, it's not possible to find out the file a specific
;; function is defined in. However, it's possible to save the file
;; name at the time this file was loaded.
(defvar my-load-file-name load-file-name
"The file name of this file.")
(defun my-start-directory (&optional path)
"The root directory that contains this module.
When PATH is specified, return the start directory concatenated with PATH.
Otherwise return the directory with a trailing slash."
;; Note: Try to figure out where we are, so that we can add the
;; subdirectories. `load-file-name' only works when the file is
;; loaded. Picking up the file from the symbol works when this is
;; evaluated later.
(let ((file-name (or my-load-file-name
(symbol-file 'my-start-directory)
;; Default value. (This is used, for example,
;; when using `eval-buffer' or `eval-region'.)
"~/emacs")))
(let ((start (concat (my-file-name-basename
(my-file-name-basename file-name))
"/")))
(if path
(concat start path)
start))))
In addition to finding out where the file containing the above above code is located (which does not have to be the init file), it provides a convenient way to create paths based on it. For example:
(setq custom-file (my-start-directory "init/custom.el"))

Related

Emacs and company autocompletion. Add a header directory globally

I'm trying to add a header files path to company-mode but I can't do it. I did it with flycheck as follow:
(defun include-paths ()
(setq flycheck-clang-include-path (list (expand-file-name "../include"))))
(add-hook 'c++-mode-hook 'include-paths)
Is there a similar way to do it with company-mode?
I'm assuming you're using company-c-headers.
Almost verbatim from their README file, you can use M-x customize-groups, company-c-headers to modify the search directories.
Alternatively, you can bind the company-c-headers-path-system and company-c-headers-path-user variables to functions which return the corresponding paths. For example, if you are using EDE, you can use the following:
(defun ede-object-system-include-path ()
"Return the system include path for the current buffer."
(when ede-object
(ede-system-include-path ede-object)))
(setq company-c-headers-path-system 'ede-object-system-include-path)
Source

Configuring Emacs extension per-buffer, watching for changes

I have written an Emacs extension that runs arbitrary functions when a buffer is saved. Configuration is stored per Git repo, in a .graffitist file with the following format:
(setq graffitist-rules
'((".*" . (lambda (file-name project-dir-name) ... ))))
That is, if the saved buffer filename matches the regex ".*", the provided function is executed. The Emacs Lisp code responsible for this is as follows:
(defun graffitist--run-actions-for-file ()
"Runs the action specified in the project .graffitist file for the filename of the current buffer, if any."
(let* ((filename (buffer-file-name (current-buffer)))
(project-directory (graffitist--find-project-dir filename))
(config-filename (graffitist--config-filename project-directory))
(action (graffitist--find-action config-filename filename)))
(if action
(funcall action filename project-directory))))
(defun graffitist--find-project-dir (filename)
"Finds the project directory for the specified filename. Returns nil if there is no project directory.
The project directory is defined as the first directory upwards in the hierarchy containing .git."
(let ((directory-name (locate-dominating-file filename ".git")))
(if directory-name
(file-name-as-directory directory-name)
nil)))
(defun graffitist--config-filename (project-directory)
"Returns the filename to the .graffitist configuration file for the specified project directory."
(if project-directory
(concat project-directory ".graffitist")
nil))
(defun graffitist--find-action (config-filename filename)
"Finds the first action associated with a regex that matches filename."
(if (and config-filename (file-exists-p config-filename))
(progn
(load config-filename)
(assoc-default filename graffitist-rules #'string-match))))
(add-hook 'after-save-hook #'graffitist--run-actions-for-file)
This works, but seems a bit odd. It's loading the .graffitist file every time a buffer is saved, which is expensive. Also, there's just the one graffitist-rules global that's updated each time a buffer is saved.
Is there a more idiomatic way of doing this in Emacs Lisp? That is, loading per-buffer configuration and keeping it current should the configuration file change?
Perhaps once you load a given config file, you could cache it and then make use of notifications on file changes to watch for changes on that file. If a watched file changes, clear it from the cache so it gets reloaded next time it's needed.

emacs: load-path and require (cannot open load file)

I've got "Cannot open load file" error at (require 'org-mime) while load-path variable seems to be all right:
load-path is a variable defined in `C source code'.
Its value is
("/home/alexey/.emacs.d/elpa/bbdb-20130526.1945" "/home/alexey/.emacs.d/elpa/org-mime-20120112" "/home/alexey/.emacs.d/elpa/smex-20130421.2153" "/usr/share/emacs/24.3/site-lisp" "/usr/share/emacs/site-lisp" "/usr/share/emacs/24.3/lisp
...
Curiously, the remedy looks like this (.emacs):
(add-to-list 'load-path "~/.emacs.d/elpa/org-mime-20120112")
It isn't merely ugly: it's dysfunctional, because the versioned path is subject to change. But why the error?
There is an interesting issue that happens when you load a file that requires another file -- the file that is required must be loaded in chronological order before the next file. For example, if B requires A then A must be placed higher up in chronological order so that when B loads, A is already loaded.
I've had really good luck with this type of setup. Most files end with el or elc, so I'm not sure why you want to load a file with a different or no extension, but it is certainly possible to do that if you want.
(let* ((root.d "~/") (sub-dir (concat root.d ".emacs.d/")))
(load-file (concat sub-dir "init.el"))
(setq load-path
(append `(,root.d ,sub-dir
,(concat sub-dir "elpa/yasnippet")
) load-path)))

How to create an empty file by elisp?

I set an explicit file to customization created via the UI. It's named custom.el. Currently, I use the followed snippets to create this file if not exist.
(defconst custom-file (expand-file-name "custom.el" user-emacs-directory))
(unless (file-exists-p custom-file)
(shell-command (concat "touch " custom-file)))
There is an ugly shell-command touch in, any other elisp functions can do this?
You can use (write-region "" nil custom-file) not sure that is the ideal solution.
Perhaps a simpler solution to the underlying problem would be:
(defconst custom-file (expand-file-name "custom.el" user-emacs-directory))
;; NOERROR to ignore nonexistent file - Emacs will create it
(load custom-file t)
In other words, instead of manually creating custom.el, simply don't error when you try to load it (optional arg NOERROR is only for file existence error). Emacs will create the file the first time it writes out custom variables.
This is what I'm currently using in my init.el
I write this function below based on Joao Tavara anwser in this post: How do I create an empty file in emacs?
(defun fzl-create-empty-file-if-no-exists(filePath)
"Create a file with FILEPATH parameter."
(if (file-exists-p filePath)
(message (concat "File " (concat filePath " already exists")))
(with-temp-buffer (write-file filePath))))
(make-empty-file custom-file)
Check help to know more about it: C-h f make-empty-file

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