Under Linux, eshell-autojump will do case sensitive matching which I just find a nuisance. I've tried to circumvent this by advising eshell/j with a eshell-under-windows-p that always returns t but to my chagrin eshell-under-windows-p invoked in eshell/j is unaffected by cl-letf. I've modified my eshell/j a bit to give me some debug info:
;; Modified eshell/j inside eshell-autojump.el to this
(defun eshell/j (&rest args) ; all but first ignored
"Jump to a directory you often cd to.
This compares the argument with the list of directories you usually jump to.
Without an argument, list the ten most common directories.
With a positive integer argument, list the n most common directories.
Otherwise, call `eshell/cd' with the result."
(setq args (eshell-flatten-list args))
(let ((path (car args))
(candidates (eshell-autojump-candidates))
(case-fold-search (eshell-under-windows-p))
result)
(when (not path)
(setq path 10))
(message "case-fold-search = %S" case-fold-search)
(message "eshell-under-windows-p returns %s from inside eshell/j" (eshell-under-windows-p))
(if (and (integerp path) (> path 0))
(progn
(let ((n (nthcdr (1- path) candidates)))
(when n
(setcdr n nil)))
(eshell-lisp-command (mapconcat 'identity candidates "\n")))
(while (and candidates (not result))
(if (string-match path (car candidates))
(setq result (car candidates))
(setq candidates (cdr candidates))))
(eshell/cd result))))
My init.el adds the advice to attempt to make eshell/j caseless by trying to trick it to think we are on Windows:
;; Added to init.el
(require 'eshell-autojump)
(advice-add 'eshell/j :around
(lambda (orig-fun &rest xs)
(cl-letf (((symbol-function 'eshell-under-windows-p) (lambda () t)))
(progn (message "eshell-under-windows-p returns %s from lambda" (eshell-under-windows-p)) (apply orig-fun xs)))))
But all I get in Messages buffer when I try to jump in eshell is:
;; I get in *Messages*
eshell-under-windows-p returns t from lambda
case-fold-search = nil
eshell-under-windows-p returns nil from inside eshell/j
My rookie knowledge of elisp is not enough to wrestle with probable scoping issues here. Can anyone decode why eshell-under-window-p is unaffected when called from eshell/j here?
I've found the answer. cl-letf does not work for byte compiled functions. As eshell-autojump is a package it gets byte compiled upon installation and cl-letf cannot be used to modify it's internal behavior. I had to resort to redefining the eshell/j which is a suboptimal solution.
Related
I dont like default ggtags interface around ggtags-find-definition/reference/file. I managed to get ggtags-find-file work with IDO, but ggtags-find-definition is a lot harder problem.
Is there some package which can do it? If not, how to make one?
NOTE: I want to be able to browse found definitions/references in fuzzy minibuffer, not whole new buffer (and window).
Normally, I would suggest ido-completing-read-plus (formerly ido-ubiquitous). It makes almost every command that uses completing-read use ido-completing-read instead. However, there is one major case where it doesn't work well: when the completion candidates are generated by a function. This is the case for ggtags-completion-table, which is how ggtags generates its completion candidates.
The solution is to define your own ggtags-completing-read-function that expands the candidates before passing to ido-completing-read. Note: the reason why ido-completing-read (and in turn ido-completing-read-plus) doesn't allow this is because it might use a ton of memory and CPU, and may freeze the UI. You will probably need to tune this to make it acceptable if you have a large number of completion candidates. Also note that most ggtags commands work with the symbol at point; it seems like you generally just give a prefix arg (C-u) to make it prompt for the symbol.
(defun jpk/ggtags-completing-read (&rest args)
(apply #'ido-completing-read
(car args)
(all-completions "" ggtags-completion-table)
(cddr args)))
(setq ggtags-completing-read-function #'jpk/ggtags-completing-read)
Assuming that you ran ggtags-find-definition and it found results and put them in the buffer *ggtags-global*, this function will extract the filenames and line numbers and let you use IDO to pick the one you want.
(defun ido-goto-grep-results (grep-buffer prompt)
(interactive)
(let (candidates result filename dirname line)
(with-current-buffer grep-buffer
(setq dirname default-directory)
(save-excursion
(save-match-data
(goto-char (point-min))
(forward-line 4)
(while (re-search-forward "^\\(.+\\):\\([0-9]+\\):" nil 'noerror)
(push (concat (match-string 1) ":" (match-string 2))
candidates)))))
(setq result (split-string (ido-completing-read prompt candidates nil t) ":"))
(setq filename (car result))
(setq line (string-to-number (cadr result)))
(find-file-other-window (expand-file-name filename dirname))
(goto-char (point-min))
(forward-line (1- line))))
(ido-goto-grep-results "*ggtags-global*" "Jump to definition: ")
This is pretty rough. You probably want to make your own find-definition command that runs ggtags-find-definition and ido-goto-grep-results at once. Somehow you need to only get the actual results and not the "Global found N definitions..." line (better regexp, narrow the ggtags buffer, or some other way).
(defun magit-max-args-internal (function)
"Return the maximum number of arguments accepted by FUNCTION."
(if (symbolp function)
(setq function (symbol-function function)))
(if (subrp function)
(let ((max (cdr (subr-arity function))))
(if (eq 'many max)
most-positive-fixnum
max))
(if (eq 'macro (car-safe function))
(setq function (cdr function)))
(let ((arglist (if (byte-code-function-p function)
(aref function 0) ; <--------- format changed
(cadr function))))
(if (memq '&rest arglist)
most-positive-fixnum
(length (remq '&optional arglist))))))
I had to recompile magit.el and discovered this problem in their code. If I follow the code correctly, then what they were after here is the function's arity, but instead they are getting some "strange" number. Any ideas what happened?
In addition, this post: Elisp get function arity? offers a better solution (which does the job just fine, the answer by Andreas Röhler. So I will probably try to suggest it to magit maintainers.
Indeed this "number in (aref bytecode 0)" was introduced for lexical-binding. The better fix is to throw away magit-max-args-internal and use (condition-case nil (delete-directory <args>) (wrong-number-of-arguments (delete-directory <fewerargs>)) instead.
The Dilemma: readability or maintainability?
Let's look at the following function.
It doesn't really matter what it does, the important part is that
it's using twice the string "(let\\*?[ \t]*":
(defun setq-expression-or-sexp ()
"Return the smallest list that contains point.
If inside VARLIST part of `let' form,
return the corresponding `setq' expression."
(interactive)
(ignore-errors
(save-excursion
(up-list)
(let ((sexp (preceding-sexp)))
(backward-list 1)
(cond
((looking-back "(let\\*?[ \t]*")
(cons 'setq
(if (= (length sexp) 1)
(car sexp)
(cl-mapcan
(lambda (x) (unless (listp x) (list x nil)))
sexp))))
((progn
(up-list)
(backward-list 1)
(looking-back "(let\\*?[ \t]*"))
(cons 'setq sexp))
(t
sexp))))))
Since it's a headache having to update the string in two (or more) locations,
I'd have to defconst it like so:
(defconst regex-let-form "(let\\*?[ \t]*")
Although the code became more maintainable, it became less readable as well,
because it's hard to see at a glance what regex-let-form really is:
(defun setq-expression-or-sexp ()
"Return the smallest list that contains point.
If inside VARLIST part of `let' form,
return the corresponding `setq' expression."
(interactive)
(ignore-errors
(save-excursion
(up-list)
(let ((sexp (preceding-sexp)))
(backward-list 1)
(cond
((looking-back regex-let-form)
(cons 'setq
(if (= (length sexp) 1)
(car sexp)
(cl-mapcan
(lambda (x) (unless (listp x) (list x nil)))
sexp))))
((progn
(up-list)
(backward-list 1)
(looking-back regex-let-form))
(cons 'setq sexp))
(t
sexp))))))
The idea: why not both?
Since it's a constant anyway, why not font-lock it
and make regex-let-form appear as if it's "(let\\*?[ \t]*"?
It's a feasable job, since:
It's possible to font-lock identifiers like so: http://www.emacswiki.org/emacs/PrettyLambda,
or even so: rainbow-mode.
And it's possible to font-lock constants. It's already done for c++-mode,
but not yet for emacs-lisp-mode, as far as I know.
Then it remains only to connect the two. Unfortunately, I don't know
enough of font-lock innards to do it, but maybe someone else does?
Or is there already a package that does this?
Tweaking the code from this answer,
I've solved the problem:
(font-lock-add-keywords
'emacs-lisp-mode
'((fl-string-constant . 'font-lock-constant-face)) 'append)
(defun fl-string-constant (_limit)
(while (not
(ignore-errors
(save-excursion
(skip-chars-forward "'")
(let ((opoint (point))
(obj (read (current-buffer)))
obj-val)
(and (symbolp obj)
(risky-local-variable-p obj)
(special-variable-p obj)
(stringp (setq obj-val (eval obj)))
(progn
(put-text-property
(1- (point)) (point) 'display
(format "%c\"%s\"" (char-before) obj-val))
(set-match-data (list opoint (point)))
t))))))
(if (looking-at "\\(\\sw\\|\\s_\\)")
(forward-sexp 1)
(forward-char 1)))
t)
This displays the value of a string constant right after the constant name.
It works quite nicely with fontified string constants as well.
Speed is a bit of an issue - suggestions to improve are welcome.
Also, I couldn't find anything better than risky-local-variable-p to determine
that it's a constant. The doc says that defconst marks the variable
as special and risky, but nothing else.
hl-defined.el (updated today, 2013-10-20) can highlight constant Emacs-Lisp symbols as such, that is, variables whose current value is the symbol itself. If your defconst has been evaluated then this will do what you are requesting.
This seems to work (source: http://www.emacswiki.org/emacs/PrettyLambda):
(font-lock-add-keywords 'emacs-lisp-mode
`(("\\<\\(regex-let-form\\)\\>" (0 (prog1 nil
(compose-region (match-beginning 1)
(match-end 1)
"\"(let\\\\*?[ \\t]*\""))))))
Although I think adding regex-let-form into the existing let block would be a cleaner solution:
(let ((sexp (preceding-sexp))
(regex-let-form "(let\\*?[ \t]*"))
...
Perhaps your example is not indicative of the real problem, and you really do want to do some display replacement or font-locking, as you say.
But I will answer wrt your example and the problem as posed, regarding maintainability vs readability: Just let-bind your regexp. The binding, unlike a defconst will be nearby and clearly related to the occurrences of the bound variable.
This is typically what people do. Again, you might have had another use case in mind --- I am responding only to the problem as posed narrowly.
Is there a list of commands to choose Emacs modes? How can I know which modes are available on my platform? I mean the list of modes names you type after M-x.
type M-x *-mode <Tab> and emacs will list all interactive commands ending in -mode that are currently loaded.
I'm not sure you can easily see what modes are available after a require without first having loaded all the elisp files in your load path.
A function for listing major modes with some guess-work to avoid the listing of minor-modes and other functions that end in -mode:
(defun list-major-modes ()
"Returns list of potential major mode names (without the final -mode).
Note, that this is guess work."
(interactive)
(let (l)
(mapatoms #'(lambda (f) (and
(commandp f)
(string-match "-mode$" (symbol-name f))
;; auto-loaded
(or (and (autoloadp (symbol-function f))
(let ((doc (documentation f)))
(when doc
(and
(let ((docSplit (help-split-fundoc doc f)))
(and docSplit ;; car is argument list
(null (cdr (read (car docSplit)))))) ;; major mode starters have no arguments
(if (string-match "[mM]inor" doc) ;; If the doc contains "minor"...
(string-match "[mM]ajor" doc) ;; it should also contain "major".
t) ;; else we cannot decide therefrom
))))
(null (help-function-arglist f)))
(setq l (cons (substring (symbol-name f) 0 -5) l)))))
(when (called-interactively-p 'any)
(with-current-buffer (get-buffer-create "*Major Modes*")
(clear-buffer-delete)
(let ((standard-output (current-buffer)))
(display-completion-list l)
(display-buffer (current-buffer)))))
l))
C-h a mode
displays a summary of all modes
Here is the list : http://www.emacswiki.org/CategoryModes
I'm using folding-mode in emacs and was trying to make a function to insert the appropriate folding marker (start or end) depending on mode. So far I have
(defun insert-folding-mode-mark ()
(interactive)
(let ((st "##{{{")
(en "##}}}")
string-to-insert)
(save-excursion
(setq string-to-insert
(let ((here (point))
sp ep)
(setq sp (search-backward st))
(goto-char here)
(setq ep (search-backward en))
(if (< sp ep) st en))))
(insert string-to-insert)))
This inserts "##{{{" at (point) unless "##{{{" precedes it, in which case it inserts "##}}}".
I'd like to replace the first (let) assignment with something that determines the start and end markers with something like
(let* ((match (assoc (intern mode-name) folding-mode-marks-alist))
(st (nth 1 match))
(en (nth 2 match)))
[is (intern) meant to be called in this way?] A truncated version of my folding-mode-marks-alist looks something like
((ess-mode "##{{{" "##}}}")
(tex-mode "%{{{" "%}}}")
(python-mode "# {{{" "# }}}")
(emacs-lisp-mode ";;{{{" ";;}}}")
(TeX-mode "%{{{" "%}}}")
(LaTeX-mode "%{{{" "%}}}"))
while the mode-name returned from various modes are {"Emacs-Lisp", "ESS[S]", "PDFLaTeX", "Python", ...}. Seems like I might want to do some partial matching with strings using (downcase), (concat x "-mode"), and so on, but was wondering if there was an idiomatic way in emacs lisp to do this sort of matching with keys of an alist, or do I just have to have a separate block of code by which I extract the keys with (mapcar 'car folding-mode-marks-alist) and convert each symbol to string (how?) to do the matching?
Thanks much!
Emacs Lisp has a destructuring-bind facility which may be helpful here. Also taking advantage of the fact that the symbol naming the current major mode is available via the variable major-mode, you can write something like this:
(destructuring-bind (st en) (cdr (assoc major-mode folding-mode-marks-alist))
; do stuff
)
Note that this won't work if (assoc major-mode folding-mode-marks-alist) returns nil, so better replace that with a call to some custom function capable of returning a sensible default.
In addition to major-mode being more appropriate than mode-name here, the function insert-folding-mode-mark as listed above would throw an error if there were no folding marker between cursor and beginning of buffer. Here is a revision without that quirk:
(require 'cl)
(defun insert-folding-mode-mark ()
(interactive)
(flet ((fn (s) (save-excursion (or (search-backward s () t) 0))))
(destructuring-bind (mode st en)
(or (assoc major-mode folding-mode-marks-alist) '(nil "" ""))
(insert (if (<= (fn st) (fn en)) st en)))))
Edit: fix problem pointed out in comment.
You may be interested to know there are comment-start and comment-end variables which should already contain the information you need based on major-mode. Something like
(search-backward (concat comment-start "{{{"))
...
(insert comment-start "{{{" comment-end)
should be sufficient. Of course, for lisp modes comment-start is ";" so you may want to do what you are doing to get ";;" but fall back on comment-start for other modes. You can also (setq comment-start ";;") though I'm not entirely sure what difference that makes for the lisp modes.