Using the :general with use-package sometimes doesn't work - emacs

I started converting my evil-define-key calls to use the :general extension to use-package, and sometimes they work, and sometimes not. Re-evaluating the (use-package ...) s-expression doesn't change the behavior, so I'm assuming I have a configuration issue.
This code works, as typing g l in the normal mode state, calls the link-hint:
(use-package link-hint
:bind
("s-o" . link-hint-open-link)
:general
(:states 'normal :keymaps 'global-map
"gl" 'link-hint-open-link
"gL" 'link-hint-copy-link))
However, the following code, which looks very similar, does not add a g p binding in normal mode:
(use-package consult
;; Enable automatic preview at point in the *Completions* buffer. This is
;; relevant when you use the default completion UI.
:hook (completion-list-mode . consult-preview-at-point-mode)
:init
;; Use Consult to select xref locations with preview
(setq xref-show-xrefs-function #'consult-xref
xref-show-definitions-function #'consult-xref)
:general
(:states 'normal :keymaps 'global-mode-map
"gp" 'consult-yank-pop
"gs" 'consult-line))
I don't believe the issue is with the :general extension, but really with general.el adding to the g sequence, as the following doesn't work either:
(general-define-key
:states 'normal :keymaps 'global-mode-map
"gp" '(consult-yank-pop :which-key "yank pop")
"gs" '(consult-line :which-key "visual search"))
And perhaps the issue isn't with general at all, as the following doesn't work either:
(evil-define-key 'normal 'global-mode-map (kbd "gp") 'consult-yank-pop)
So I guess my real question is why gl works, but gs and gp do not.

While I don't know why my initial code sometimes works, a reliable way to change the global map with the :general extension to use-package is to drop the global-mode-map. In other words:
(use-package consult
...
:general
(:states 'normal
"gp" 'consult-yank-pop
"gs" 'consult-line)

Related

emacs + clojure-mode: add multiple hooks

This is a part of my init.el file, which enables one hook to the clojure-mode:
(use-package clojure-mode
:ensure t
:config (add-hook 'clojure-mode-hook #'aggressive-indent-mode))
What is the syntax for enabling multiple hooks (e.g. #'paredit-mode and maybe more)?
As #ArthurUlfeldt points out, you can add multiple add-hook statements, one for each of the hook functions you want to add. However, this can become a little tiresome when you have a common set of things you want to add to multiple modes.
For example, if you use a number of lisp like languages, then you might want to add paredit, aggressive-indent, eldoc, rainbow-delimiters etc to each of these modes. Rather than having to sprinkle lots of add-hook commands all through your init file, you can define your own function i.e. my-lisp-hook which calls all the init functions you were adding with individual add-hook commands. Then you just need to do an add-hook which calls your function.
The other advantage this can have is it makes adding/removing something like a new minor mode much easier. For example, if you had
(defun my-lisp-hook ()
(paredit-mode 1)
(aggressive-indent-mode 1)
(raindbow-delimiter-mode 1))
and then had
(add-hook 'emacs-lisp-mode-hook 'my-lisp-hook)
(add-hook 'lisp-mode-hook 'my-lisp-hook)
(add-hook 'clojure-mode-hook 'my-lisp-hook)
(add-hook 'cider-mode-hook 'my-lisp-hook)
and then after an ELPA package update found the new version of aggressive-indent-mode was causing problems, you could just comment out 1 line in your my-lisp-hook function to stop the call to aggressive-indent-mode and get back to work. On the other hand, if you had added it separately in each mode hook, you would hve to comment out 4 lines. It also keep things consistent. I have run into subtle issues when I've loaded minor modes in one order for one mode and a different order for a different mode. With the use of your own hook function to load common setup requirements, it all happens consistently.
Note that the above code is pseudo code and is not meant to actually run. I don't know if the command to turn on rainbow delimiters is rainbow-delimiters-mode 1 or if the right hook to use for lisp mode is lisp-mode-hook. My examples are merely done to demonstrate the concept.
the way you write your emacs init file is a personal choice, but I think you should apply the same sort of rules we all know should be applied to writing code. Two of which are 'do not repreat yourself' and 'write your code to be clear rather than clever'. I think we shold apply the same logic to our emacs init file.
I also think your off to a great start with use-package. I've just switched to using it and think it is a great way to help structure your init file. However, to get the most out of it, especially with respect to delayed loading of packages so that my startup time is reduced, I did find I had to restructure my init file a fair bit. This could simply be a sign my original structure wasn't that good or it could be a sign of my current interest in trying and tweaking my emacs config as a bit of a hobby/distraction from cutting real code, whih tends to result in a less structure init due to the constant change.
you can add multiple statements after the :config keyword:
(use-package clojure-mode
:ensure t
:config (add-hook 'clojure-mode-hook #'aggressive-indent-mode)
(add-hook 'clojure-mode-hook #'other-thing-here)
(yas-global-mode 1))
Here's a chunk from my config:
(use-package cider
:ensure t
:config
(define-key cider-mode-map (kbd "C-c SPC") 'avy-goto-word-1)
(define-key cider-mode-map (kbd "C-x SPC") 'avy-pop-mark))

Emacs evil-mode and ensime

I am a VIM guy - but evil-mode allowed me to productively use Emacs for my Scala development, and it did it so well that I am sort of hooked... I want to customize the environment to my liking, so I e.g. added these to my .emacs:
(define-key evil-normal-state-map (kbd "<f7>") 'ensime-typecheck-all)
(define-key evil-normal-state-map (kbd "C-]") 'ensime-edit-definition)
While in evil-normal-mode, the first line allows me to do a typecheck of my Scala code by just hitting F7, while the second one navigates me to a type/val definition via the muscle-memoried Ctrl-] (used for tags-based navigation in VIM).
My only problem is that what I've done so far is a "global" assignment - if I open a Python file, I'd want F7 to do a different thing (pyflakes or pylint). How can I assign different actions to the same key based on what kind of file is currently open in the buffer I am viewing?
In case it helps, in my .vimrc, I've done this via sections like these:
au BufNewFile,BufRead *.ml call SetupOCamlEnviron()
function! SetupOCamlEnviron()
se shiftwidth=2
"
" Remap F7 to make if the file is an .ml one
"
noremap <buffer> <special> <F7> :make<CR>
noremap! <buffer> <special> <F7> <ESC>:make<CR>
"
" Thanks to Merlin
"
noremap <buffer> <silent> <F6> :SyntasticCheck<CR>
noremap! <buffer> <silent> <F6> <ESC>:SyntasticCheck<CR>
inoremap <buffer> <C-Space> <C-x><C-o>
noremap <buffer> <C-]> :Locate<CR>
inoremap <buffer> <C-]> <ESC>:Locate<CR>
endfunction
How do I do this kind of thing in Emacs/evil?
EDIT: In search of a way to do this, I realized I can dispatch on different code when F7 is pressed in normal mode - so I wrote this Emacs lisp:
(defun current-buffer-extension ()
(if (stringp (buffer-file-name))
(file-name-extension buffer-file-name)
"Unknown"))
(defun handle-f7 ()
(interactive)
(if (string= (current-buffer-extension) "scala")
(ensime-typecheck-all)))
(define-key evil-normal-state-map (kbd "<f7>") 'handle-f7)
...which can be extended with other tool invocations as I learn more about Emacs, by adding actions based on the file extension.
Being completely new to Emacs, I am open to suggestions...
Am I on the right track here? Is there a better way to do what I want? Have I violated some principle by hooking evil-normal-mode keystrokes?
The thing you are looking for is called hook. There are plenty of hooks that emacs runs is specifics moments, and are fully customizable by user.
For instance, whenever entering a mode, a "mode-hook" is run. So you could do:
(add-hook 'python-mode-hook (lambda ()
(setq fill-column 79)
(local-set-key [(f3)] 'run-flake8)))
Hooks are the canonical way of customizing a lot of things in emacs, and there is plenty of examples around about those. In the example above, I define the key F3 to do something whenever I am in python mode.
Note that the example does not cope with evil keymap, but uses a local key that should have preference. If you want to assign a key to do different things depending on the evil state, you should either add some additional logic or make the evil-X-state-map buffer local and then change it the way you want (you could do that in the hook)
Edit: Regarding your question about if you have "violated some principle"... lets say that because of the hooks and the ability to set local variables, checking file extensions to customize behaviour is never (afaik) done other than for setting the mode (and that is done internally). Even checking the major-mode (that would be preferred to checking extension) is not done, as it is less scalable than just hooking preferences and functionality into modes.
Evil has a useful facility for mode-specific keybindings: evil-define-key. As arguments, it takes:
the state (as a quoted symbol, in your case, 'normal),
an unquoted keymap (I don't use ensime, but it's probably something like ensime-mode-map),
the key sequence, and
the command to bind.
So, to make your bindings ensime-specific (again, fiddle with the specific map name to make sure it's correct):
(evil-define-key 'normal ensime-mode-map (kbd "<f7>") #'ensime-typecheck-all)
(evil-define-key 'normal ensime-mode-map (kbd "C-]") #'ensime-edit-definition)
You can also bind f7 to something else for use with python (again, tinker with the map names and commands accordingly):
(evil-define-key 'normal python-mode-map (kbd "<f7>") #'run-flake8)
(Aside: I'm using the sharp-quote (#', which is lisp-shorthand for "function") in front of command names rather than the plain quote ('). For your purposes the two are basically equivalent, but it's good to get in the habit of the former in case you start byte-compiling your setup files, as the byte-compiler will warn you if the function in question is undefined when you sharp-quote but not if you simple-quote. See a related discussion on the Emacs Stack Exchange site.)

What happened to the ido-imenu in ruby-mode function in Emacs24?

Emacs 23.2 in emacs-starter-kit v1 has C-x C-i (or ido-imenu) (similar to Sublime Text's Cmd+R). Emacs24 in emacs-starter-kit v2 lacks this function. I found this github issue and a fix, which try to recreate the functionality. While this ido-imenu works in elisp-mode, it stopped working in ruby-mode. I get:
imenu--make-index-alist: No items suitable for an index found in this buffer
Has anyone figured out how to get this to work?
Why was this taken out of Emacs24?
Is there a new replacement for this function?
Since the function is part of ESK (as opposed to something budled with Emacs) you'd probably do best to report the bug upstream. On a related note ESK main competitor Emacs Prelude offers the same functionality (bound to C-c i by default) and it seems to be working fine with ruby-mode in Emacs 24. Here you can find more on ido-imenu.
So I finally figured it out, after reading the Defining an Imenu Menu for a Mode section on emacs-wiki again.
Short answer: you need to add this bit to your customization. Feel free to add more types to the list (I am happy with just methods).
(add-hook 'ruby-mode-hook
(lambda ()
(set (make-local-variable imenu-generic-expression)
'(("Methods" "^\\( *\\(def\\) +.+\\)" 1)
))))
Longer answer: I first tried to define a ruby-imenu-generic-expression function and set that to imenu-generic-expression by using the ruby-mode-hook:
(defvar ruby-imenu-generic-expression
'(("Methods" "^\\( *\\(def\\) +.+\\)" 1))
"The imenu regex to parse an outline of the ruby file")
(defun ruby-set-imenu-generic-expression ()
(make-local-variable 'imenu-generic-expression)
(make-local-variable 'imenu-create-index-function)
(setq imenu-create-index-function 'imenu-default-create-index-function)
(setq imenu-generic-expression ruby-imenu-generic-expression))
(add-hook 'ruby-mode-hook 'ruby-set-imenu-generic-expression)
This however did not work (I would get the same error as before). More reading of the Defining an Imenu Menu for a Mode section showed me the way. Now, I'm not an elisp expert, so here's my hypothesis: basically, the above method works for modes where the
major mode supports a buffer local copy of the “real” variable, ‘imenu-generic-expression’. If your mode doesn’t do it, you will have to rely on a hook.
The example for foo-mode made it clear how to do it for ruby-mode. So it appears that ruby-mode does not have a buffer-local copy of the real imenu-generic-expression variable. I still can't explain why it worked in Emacs 23.2 (with ESK v1) but does not on Emacs24, but hey at least I found a working solution.

How to get whitespace-mode enabled only for certain modes

I'm trying to get emacs whitespace-mode enabled automatically only in certain modes. According to the documentation, enabling global-whitespace-mode and setting the whitespace-global-modes variable should do exactly that. But I can't get it to work correctly.
In my .emacs.el I have:
(require 'whitespace)
(global-whitespace-mode t)
(setq whitespace-global-modes '(c-mode c++-mode))
but the definition of whitespace-global-modes seems to be ignored; global-whitespace-mode is enabled in every buffer. I know that I've got the variable name correctly, because C-h v whitespace-global-modes tells me:
whitespace-global-modes's value is (c-mode c++mode)
Documentation:
Modes for which global `whitespace-mode' is automagically turned on.
...
So what am I doing wrong? Have I misunderstood the purpose of whitespace-global-modes?
I'm running emacs 23.2.1.
Apparently the meaning of whitespace-global-modes is very different from what you (and I) understand.
How about trying
(require 'whitespace)
(add-hook 'c-mode-hook
(function (lambda ()
(whitespace-mode t))))
and repeating the same thing for c++-mode?
It turns out that the commands in my .emacs.el were (almost) working after all. What confused me was that "WS" appears in the modeline of all buffers, even though only C and C++ buffers were getting the effect of whitespace-mode, as desired.
The other problem was that I had a typo: c++mode rather than c++-mode.

Auto-complete mode doesn't turn on automatically in ObjC buffers

I load auto-complete mode like this:
(let ((ac-path "path/to/auto-complete"))
(add-to-list 'load-path ac-path)
(require 'auto-complete-config)
(add-to-list 'ac-dictionary-directories (concat ac-path "ac-dict"))
(ac-config-default))
It works fine with C major mode, but doesn't turn on automatically when I open ObjC files. I can still turn it on manually and it will work fine along with the ObjC major mode.
Here's a snippet from the docs regarding ObjC major mode:
The hook `c-mode-common-hook' is run with no args
at mode initialization, then `objc-mode-hook'.
If I understand correctly, auto-complete adds a hook to the c-mode-common-hook, but objc-mode-hook somehow overrides it. Is there a way to fix this?
Thanks.
While looking through the source code of auto-complete.el, I've stumbled upon this definition
(defcustom ac-modes
'(emacs-lisp-mode
lisp-interaction-mode
c-mode cc-mode c++-mode
java-mode clojure-mode scala-mode
scheme-mode
ocaml-mode tuareg-mode
perl-mode cperl-mode python-mode ruby-mode
ecmascript-mode javascript-mode js-mode js2-mode php-mode css-mode
makefile-mode sh-mode fortran-mode f90-mode ada-mode
xml-mode sgml-mode)
"Major modes `auto-complete-mode' can run on."
:type '(repeat symbol)
:group 'auto-complete)
It turns out that auto-complete doesn't have a true global mode. It is enabled only with those major modes that are included in the ac-modes variable.
So, adding the following line to the .emacs file has solved the issue for me.
; add this line after the auto-complete mode has been loaded
(add-to-list 'ac-modes 'objc-mode)
Use the following:
(defun my-objc-mode-hook ()
(auto-complete-mode 1))
(add-hook 'objc-mode-hook 'my-objc-mode-hook)
Note 1: The function auto-complete-mode is a toggle function, when called with no arguments.
Note 2: It's possible to add an anonymous function using lambda, but this have several drawbacks. The most important ones are: modifying the function and reevaluating the expression will add the modified function in addition to the earlier version and C-h v xxx will print the full unformatted lambda function, which typically is hopeless to read and understand.
(add-hook 'objc-mode-hook 'auto-complete-mode)
That should do it if you're using auto-complete-mode. You can add more complex things to mode hooks by doing:
(add-hook 'objc-mode-hook '(lambda ()
(something-with arguments)))
Note that both arguments to add-hook are quoted, this is necessary and if you add unquoted functions they will probably not work.