I have made a number of upgrades to functions and customizations I’ve previously blogged about, so this post is going to be an aggregation of these improvements. Think of it as a ‘corrections’ post.
In a previous post, I talked about updating imenu-generic-expression for java to work with functions with generic types or annotated arguments. The regular expression there is somewhat inadequate: if you used horrible spacing conventions, it could thing an if statement was a function. This new hook for java-mode to set imenu-generic-expression fixes this case by actually breaking out the annotations, types, and variable names, without just mixing them together as in the previous version.
(add-hook 'java-mode-hook '(lambda () (setq imenu-generic-expression `((nil ,(concat "[" c-alpha "_][\]\[." c-alnum "_<> ]+[ \t\n\r]+" ; type spec "\\([" c-alpha "_][" c-alnum "_]+\\)" ; method name "[ \t\n\r]*" ;; An argument list htat is either empty or contains any number ;; of arguments. An argument is any number of annotations ;; followed by a type spec followed by a word. A word is an ;; identifier. A type spec is an identifier, possibly followed ;; by < typespec > possibly followed by []. (concat "(" "\\(" "[ \t\n\r]*" "\\(" "@" "[" c-alpha "_]" "[" c-alnum "._]""*" "[ \t\n\r]+" "\\)*" "\\(" "[" c-alpha "_]" "[\]\[" c-alnum "_.]*" "\\(" "<" "[ \t\n\r]*" "[\]\[.," c-alnum "_<> \t\n\r]*" ">" "\\)?" "\\(\\[\\]\\)?" "[ \t\n\r]+" "\\)" "[" c-alpha "_]" "[" c-alnum "_]*" "[ \t\n\r,]*" "\\)*" ")" "[ \t\n\r]*" "{" )) 1))) ))
The following are a few utilities I wrote in order to write functions such as my-grep. my-fn will take a function with one argument and a prompt, and execute that function with a default of the current word. Defun-my is a macro that defines both the original function and an interactive ‘my’ version of the function. The new my-grep has the same effect as the old my-grep, but uses my-fn.
(defun my-fn (fn prompt) "When given a function taking one argument and applying a function to it, will use that function and default to the word at point, with a prompt including that word." (let ((default (current-word))) (let ((needle (read-string (concat prompt " <" default ">: ")))) (if (equal needle "") (funcall fn default) (funcall fn needle))))) (defmacro defun-my (name prompt &rest body) "Will define both a function and a my- version of the function, which defaults to the word at point." `(progn (defun ,name (arg) ,@body) (defun ,(intern (concat "my-" (symbol-name name))) () (interactive) (my-fn (quote ,name) ,prompt)))) (defun my-grep nil "Grep the whole directory for something defaults to term at cursor position" (interactive) (my-fn (lambda (arg) (grep (concat "egrep -s -i -n -r " arg " * " ))) "grep for"))
I also used defun-my in order to upgrade java-describe. The new ‘my-java-descibe-class’ does the same thing, except it defaults to the word at point. I also renamed it to java-describe-class, since I’ve been working on some other Java utilities and wanted to make this name more specific.
(defun-my java-describe-class "Open Javadoc for Class" "Loads javadoc for specified class in your browser." (interactive "MClass Name: ") (let ((my-string (replace-regexp-in-string "^.*class-use/.*\n" "" (shell-command-to-string (concat "find ~/.emacs.d/documentation/java/ -name " arg ".html")) ))) (string-match "^\\(.*\\)$" my-string) (browse-url (match-string 0 my-string)))) (global-set-key (kbd "C-x j c") 'my-java-describe-class)
I also upgraded my-help, a function that gives documentation on whatever the point is on. It now checks to see if the buffer is a java buffer; if so, we open the javadoc for the current word instead of calling man on it. I was going to switch the order of checking whether a function or symbol was described first, in the case where a function and variable have the same name, but the issue then is that since function-called-at-point will return the calling function if you are anywhere in a function call, it would frequently mask variable lookup.
(defun my-help () "If function given tries to `describe-function' if variable uses 'describe-variable', otherwise uses `manual-entry' to display manpage of a `current-word'." (interactive) (let ((var (variable-at-point)) (fn (function-called-at-point))) (if (symbolp var) (describe-variable var) (if fn (describe-function fn) (if (eq major-mode 'java-mode) (java-describe (current-word)) (man (current-word)))))))
I previously just added visual-line-mode to text-mode-hook, which actually toggled visual-line-mode in the buffer and could lead to strange behaviour. This correction turns visual-line-mode on when entering text modes.
(add-hook 'text-mode-hook '(lambda nil (visual-line-mode 1)))
A reader suggested this improvement in the comments of this post. This will open the current file as sudo at the current position; the old function would lose the position you were at.
(defun sudo-edit-current-file () (interactive) (let ((pos (point))) (find-alternate-file (concat "/sudo:root@localhost:" (buffer-file-name (current-buffer)))) (goto-char pos)))
Yank-pop is an advice I describe here to properly indent code fragments. I implemented a suggestion I received in the comments of the linked post in order to make it work better when transient-mark-mode is enabled.
(defadvice yank-pop (after indent-region activate) (if (member major-mode '(emacs-lisp-mode scheme-mode lisp-mode c-mode c++-mode objc-mode latex-mode plain-tex-mode)) (let ((mark-even-if-inactive transient-mark-mode)) (indent-region (region-beginning) (region-end) nil))))
I created a few new functions to keep track of how long each of my customization files take to load. While load time isn’t a problem for me right now, and I could easily fix it if it was by byte-compiling my customizations(right now, the only compiled elisp file I have that is not part of GNU Emacs core is js2.el), I do want to be able to account for what is taking the most time to load. While emacs-init-time tells me how long emacs took to initialize in total, I want a more fine-grained solution. I implemented this by defining time-load-file, a function that would load a file and return a pair consisting of the filename and the time taken to load it in seconds, and changing load-directory to use time-load-file instead of load-file. By saving the results of load-directory, I have a permanent record of the time taken to load each file.
(defun time-load-file (file) "Loads a file and returns a pair consisting of the filename and the time in seconds it took to load." (let ((prev-time (float-time))) (load-file file) (cons file (- (float-time) prev-time)))) (defun load-directory (dir) "Loads every .el file in a directory." (mapcar 'time-load-file (directory-files dir t "\\.el\\'"))) (setq customizations-directory-load-times (load-directory "~/.emacs.d/customizations/")) (setq util-directory-load-times (load-directory "~/.emacs.d/utilities/"))
That’s all I have for now, but I will no doubt improve my customizations further, so once I amass a suitable number I’ll do another one of these posts. If you see anything that should be corrected or could be improved in any of my posts, please leave a comment.