How do I add auto-completion to my custom comint mode in Emacs? - emacs

I'm writing a comint mode for a legacy command-line tool.
I'd like to add basic auto-completion to it.
Suppose I have the following list of keywords used by the tool:
(defconst my-keywords '("export" "extract" "display"))
How would I add auto-completion to my mode, based on this list?
What I found so far:
I know there are examples of this in shell.el or in comint.el, but I did not manage to understand the code well enough to answer to this basic question. I did understand though that I could build a regexp list out of my-keywords, like this:
(regexp-opt my-keywords)
;; output:
"\\(?:display\\|ex\\(?:\\(?:por\\|trac\\)t\\)\\)"
Other than that, I gathered that I could use pcomplete, or company, or both – I'm fine with any solution actually, but how do I do it?

Comint also calls customizable comint-dynamic-complete-functions from its comint-completion-at-point function. Derived modes will often add functions to this hook (see shell-dynamic-complete-functions), eg.
(defconst my-keywords '("export" "extract" "display"))
(defun my-comint-dynamic-completion-function ()
(when-let* ((bds (bounds-of-thing-at-point 'symbol))
(beg (car bds))
(end (cdr bds)))
(when (> end beg)
(list beg end my-keywords :annotation-function (lambda (_) "my-keywords")))))
(define-derived-mode my-comint-mode comint-mode "my mode"
(add-hook 'comint-dynamic-complete-functions
#'my-comint-dynamic-completion-function nil 'local)
(make-local-variable 'company-backends)
(cl-pushnew 'company-capf company-backends))
By adding company-capf to your company-backends you will additionally get company support from your completion at point function (see elisp-completion-at-point for example of additional company-specific symbols for showing help/location/etc. of completion candidates).

Thanks to ergoemacs I found a first solution :
Define a completion function which returns a list of the form (start end my-keywords . nil), with start and end delimiting the entity to complete at point. I added a tweak to take into account the program prompt, for the first keyword.
Add this function to completion-at-point-functions, in the definition of the mode;
Add a tab shortcut to completion-at-point, in the mode-map.
(defconst my-keywords '("export" "extract" "display"))
;; 1 - custom completion function
(defun my-completion-at-point ()
"This is the function to be used for the hook `completion-at-point-functions'."
(interactive)
(let* (
(bds (bounds-of-thing-at-point 'symbol))
(start (max (car bds) (comint-line-beginning-position)))
(end (cdr bds)))
(list start end xyz-keywords . nil )))
;; 2 - adding it to my-completion-at-point
(define-derived-mode my-comint-mode comint-mode "My comint mode"
;; your code....
(add-hook 'completion-at-point-functions 'my-completion-at-point nil 'local))
;; 3 - add a tab shortcut in the map of the mode
(defvar my-mode-map
(let ((map (nconc (make-sparse-keymap) comint-mode-map)))
;; your code...
(define-key map "\t" 'completion-at-point)
map))

Related

Emacs -- test to determine what type of hook is running

Is there a test to determine what type of hook is running -- e.g., window-configuration-change-hook?
For example, I have a function that contains conditions used determine whether it should be run -- it is used in conjunction with post-command-hook. I would like to use the same function for the window-configuration-change-hook, without triggering the first set of conditions:
(when
(or
(and
(not window-configuration-change-hook) ;; illustrative example only
(memq this-command this-last-command-inclusions)
(not multiple-cursors-mode))
window-configuration-change-hook) ;; illustrative example only
. . .
AFAIK there is no standardized method to determine which hook is running.
If you want to use advice or something like that to store this information you have to be aware that hooks can run within hooks. See the following example.
That means you need a stack-like structure to store that information.
EDIT: The example includes now a hook-stack storing the currently running hooks.
Note, I do not recommend to use this method since it is quite critical and does not work in all cases. Better, advice the mode/function or whatever what you want to identify.
(defvar hook-stack nil)
(defadvice run-hooks (around register-hooks activate)
"Store current hook into `hook-stack'."
(let ((hooks (ad-get-args 0)))
(loop for h in hooks do
(unwind-protect
(progn
(push h hook-stack)
(ad-set-args 0 (list h))
ad-do-it))
(pop hook-stack)
)))
(ad-remove-advice 'run-hooks 'around 'register-hooks)
(setq hooks1 nil)
(setq hooks2 nil)
(add-hook 'hooks1 (lambda () (message "Running hooks1, hook-stack: %S" hook-stack)))
(add-hook 'hooks2 (lambda () (message "Running hooks2") (run-hooks 'hooks1)
(message "Finishing hooks2, hook-stack: %S" hook-stack)))
(run-hooks 'hooks2)
Note: This does not work if run-hooks is called from C instaead of lisp. Furthermore, there are other functions like run-hook-with-args-until-success.

Using 2 consecutive control key in Emacs [duplicate]

Let's say I bind the key to a certain function as follows:
(global-set-key (kbd "C-c =") 'function-foo)
Now, I want the key binding to work as:
After I press C-c = for the first time, if I want to repeat the function-foo, I don't need to press C-c again, but simply repeat pressing =. Then, after I call the function-foo for enough times, I can just press keys other than = (or explicitly press C-g) to quit.
How to do this?
This may be the thing you are looking for:
(defun function-foo ()
(interactive)
(do-your-thing)
(set-temporary-overlay-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "=") 'function-foo)
map)))
There's a smartrep.el package that does exactly what you need. The documentation is a bit scarce but you can get a grip of how it's supposed to be used by looking into numerous emacs configs found on github. For example (taken from here):
(require 'smartrep)
(smartrep-define-key
global-map "C-q" '(("n" . (scroll-other-window 1))
("p" . (scroll-other-window -1))
("N" . 'scroll-other-window)
("P" . (scroll-other-window '-))
("a" . (beginning-of-buffer-other-window 0))
("e" . (end-of-buffer-other-window 0))))
This is what I use. I like it because you don't have to specify the repeating key.
(require 'repeat)
(defun make-repeatable-command (cmd)
"Returns a new command that is a repeatable version of CMD.
The new command is named CMD-repeat. CMD should be a quoted
command.
This allows you to bind the command to a compound keystroke and
repeat it with just the final key. For example:
(global-set-key (kbd \"C-c a\") (make-repeatable-command 'foo))
will create a new command called foo-repeat. Typing C-c a will
just invoke foo. Typing C-c a a a will invoke foo three times,
and so on."
(fset (intern (concat (symbol-name cmd) "-repeat"))
`(lambda ,(help-function-arglist cmd) ;; arg list
,(format "A repeatable version of `%s'." (symbol-name cmd)) ;; doc string
,(interactive-form cmd) ;; interactive form
;; see also repeat-message-function
(setq last-repeatable-command ',cmd)
(repeat nil)))
(intern (concat (symbol-name cmd) "-repeat")))
You want your function-foo to use set-temporary-overlay-map.
In addition to what #juanleon suggested, which uses set-temporary-overlay-map, here is an alternative that I use quite a bit. It uses standard library repeat.el.
;; This function builds a repeatable version of its argument COMMAND.
(defun repeat-command (command)
"Repeat COMMAND."
(interactive)
(let ((repeat-previous-repeated-command command)
(last-repeatable-command 'repeat))
(repeat nil)))
Use that to define different repeatable commands. E.g.,
(defun backward-char-repeat ()
"Like `backward-char', but repeatable even on a prefix key."
(interactive)
(repeat-command 'backward-char))
Then bind such a command to a key with a repeatable suffix, e.g., C-c = (for C-c = = = =...)
See this SO post for more information.

Emacs quicker bookmark-jump?

I have most of my bookmarks prefixed by a letter in a way that
the first letter almost always uniquely determines the bookmark.
This way I can, for instance,
jump to my source folder (bookmarked as "s: source") with M-x bookmark-jump RET s RET.
I have it on a shortcut, so it's actually ~ s RET.
I'd like to get rid of RET in the end,
i.e. get M-x bookmark-quick-jump RET s or ~ s
to do the aforementioned job.
I'd also like it to fall back to the default behavior: to show me all bookmarks
that start with the given letter, in case there's not just one variant.
So far, I've got:
(defun bookmark-do-quick-jump (str)
(let ((completions (all-completions str bookmark-alist)))
(bookmark-jump
(if (eq 1 (length completions))
(car completions)
(completing-read "Jump to bookmark: " bookmark-alist nil t str)))))
There's still two hiccups:
Firstly, I need to jump into minibuffer somehow and stick in there this map (don't know how to do this):
(setq bookmark-quick-jump-map
(let ((map (make-sparse-keymap)))
(mapcar (lambda (key)
(define-key map key
(lambda()
(interactive)
(bookmark-do-quick-jump key))))
(loop for c from ?a to ?z
collect (string c)))
map))
Secondly, when I do a call
(bookmark-do-quick-jump "o")
It comes back with 3 variants (org-capture-last-stored, org-capture-last-stored-marker...).
I'm in minibuffer now, but I still need to press RET RET
to see these 3 variants. I'd like this to be done automatically.
I'd appreciate any responses that either directly answer my two sub-problems,
or an altogether different approach, as long as I can get the behavior and usability
that I described.
UPD:
I've solved the second thing by switching from completing-read to ido-completing-read:
(defun bookmark-do-quick-jump (str)
(let ((completions (all-completions str bookmark-alist)))
(bookmark-jump
(if (eq 1 (length completions))
(car completions)
(ido-completing-read "Jump to bookmark: " completions nil t str)))))
Btw, I forgot to mention that I use bookmark+. I'm not sure if jumping to dired
is supported by the default bookmark-jump.
We can remap self-insert-command during the completing-read to trigger the auto-completion and auto-acceptance behaviour.
I originally used (or (minibuffer-complete-and-exit) (minibuffer-completion-help)) which at first glance worked very nicely but, as noted in the comments, is less than ideal when one bookmark's name is the prefix of another, as it will immediately accept the shorter name, hence making the longer one inaccessible.
Calling minibuffer-complete and minibuffer-completion-help together breaks the completion functionality, however, so instead I've copied the relevant part of minibuffer-complete-and-exit to a new function. Using this resolves all of the earlier problems.
(require 'bookmark)
(defvar bookmark-do-quick-jump-map (copy-keymap minibuffer-local-must-match-map)
"Keymap for `bookmark-do-quick-jump'.
`minibuffer-local-must-match-map' is used by `completing-read' when its
REQUIRE-MATCH argument is t.
In `bookmark-do-quick-jump' we bind this modified copy to use in its place.")
(define-key bookmark-do-quick-jump-map
[remap self-insert-command] 'my-self-insert-complete-and-exit)
(defun bookmark-do-quick-jump ()
"Jump to specified bookmark with auto-completion and auto-acceptance."
(interactive)
(bookmark-maybe-load-default-file)
(let ((minibuffer-local-must-match-map bookmark-do-quick-jump-map))
(bookmark-jump
(completing-read "Jump to bookmark: " bookmark-alist nil t))))
(defun my-self-insert-complete-and-exit (n)
"Insert the character, then attempt to complete the current string,
automatically exiting when only one option remains, and displaying the
completion options otherwise."
(interactive "p")
(self-insert-command n)
(my-minibuffer-complete)
(let ((my-completions (completion-all-sorted-completions)))
(if (and my-completions (eq 0 (cdr my-completions)))
(exit-minibuffer)
(minibuffer-completion-help))))
(defun my-minibuffer-complete ()
"Copied from `minibuffer-complete-and-exit'."
(interactive)
(condition-case nil
(completion--do-completion nil 'expect-exact)
(error 1)))
Edit:
I took another stab at this using ido. It's a little unfortunate that you don't get the next 'important character' highlighted the way that you do with the regular minibuffer completion (as that was a nice indicator of what to type next), but this seems to work nicely in other respects.
(require 'bookmark)
(require 'ido)
(defvar bookmark-ido-quick-jump-map (copy-keymap minibuffer-local-map)
"Keymap for `bookmark-ido-quick-jump'.
Every time `ido-completing-read' is called it re-initializes
`ido-common-completion-map' and sets its parent to be `minibuffer-local-map'.
In `bookmark-ido-quick-jump' we provide this modified copy as a replacement
parent.")
(define-key bookmark-ido-quick-jump-map
[remap self-insert-command] 'my-self-insert-and-ido-complete)
(defun bookmark-ido-quick-jump ()
"Jump to selected bookmark, using auto-completion and auto-acceptance."
(interactive)
(bookmark-maybe-load-default-file)
(let ((minibuffer-local-map bookmark-ido-quick-jump-map)
(ido-enable-prefix t))
(bookmark-jump
(ido-completing-read "Jump to bookmark: "
(loop for b in bookmark-alist collect (car b))))))
(defun my-self-insert-and-ido-complete (n)
"Insert the character, then attempt to complete the current string,
automatically exiting when only one option remains."
(interactive "p")
(self-insert-command n)
;; ido uses buffer-local pre- and post-command hooks, so we need to
;; co-operate with those. We append our post-command function so that
;; it executes after ido has finished processing our self-insert.
(add-hook 'post-command-hook
'my-self-insert-and-ido-complete-post-command t t))
(defun my-self-insert-and-ido-complete-post-command ()
(remove-hook 'post-command-hook
'my-self-insert-and-ido-complete-post-command t)
;; Now that ido has finished its normal processing for the current
;; command, we simulate a subsequent `ido-complete' command.
(ido-tidy) ;; pre-command-hook
(ido-complete)
(ido-exhibit)) ;; post-command-hook
Here's another take:
(defun bookmark-do-quick-jump (str)
(let ((completions (all-completions str bookmark-alist)))
(if (null (cdr completions))
(bookmark-jump (car completions))
(minibuffer-with-setup-hook
(lambda () (insert str)
(minibuffer-completion-help))
(call-interactively 'bookmark-jump)))))
Or yet another (even more guaranteed untested):
(defadvice bookmark-jump (around quick-bookmarks activate)
(minibuffer-with-setup-hook
(lambda ()
(add-hook 'post-self-insert-hook
(lambda ()
(let ((completions
(all-completions (minibuffer-contents)
bookmark-alist)))
(if (cdr completions)
(minibuffer-completion-help)
(minibuffer-complete-and-exit))))
nil t))
ad-do-it))
Sounds like you're doing a lot of extra work. Just use Icicles.
User option icicle-incremental-completion non-nil and non-t means show all matches as soon as you type input.
Option icicle-top-level-when-sole-completion-flag non-nil means accept a solitary match without your needing to hit a key (e.g. RET).
Instead of customizing the options to have these values in general, you can just bind them to the values in your own command.

Is it possible to alternate two input methods in Emacs?

I know that there is "C-\" to switch input methods, but that allows only one additional input method - for example, by default I have english qwerty layout, and I can alternate between it and dvorak.
But is there some way to do alternation between two other languages with the same ease? For example, if I want to alternate english-dvorak and my native language layout?
To switch between 2 or more alternative input methods quickly, I've added the following code to my init.el:
;; Input method and key binding configuration.
(setq alternative-input-methods
'(("russian-computer" . [?\C-\\])
("chinese-py-punct" . [?\C-|])
("german-postfix" . [?\C-\M-|])))
(setq default-input-method
(caar alternative-input-methods))
(defun toggle-alternative-input-method (method &optional arg interactive)
(if arg
(toggle-input-method arg interactive)
(let ((previous-input-method current-input-method))
(when current-input-method
(deactivate-input-method))
(unless (and previous-input-method
(string= previous-input-method method))
(activate-input-method method)))))
(defun reload-alternative-input-methods ()
(dolist (config alternative-input-methods)
(let ((method (car config)))
(global-set-key (cdr config)
`(lambda (&optional arg interactive)
,(concat "Behaves similar to `toggle-input-method', but uses \""
method "\" instead of `default-input-method'")
(interactive "P\np")
(toggle-alternative-input-method ,method arg interactive))))))
(reload-alternative-input-methods)
So, to switch to Russian, Chinese or German IME I use C-\, C-| and C-M-| accordingly. And to switch back to English I use the same key as for the current IME (i.e. if I have Chinese IME active, I'll switch back using C-|).
To configure use alternative-input-methods variable. It's a list of conses of an input method name and a key binding.
Note! If you activate an IME either by calling M-x toggle-input-method or by C-u C-\, pressing C-\ would switch to an input method according to alternative-input-methods variable (in the default configuration Emacs remembers new IME and uses it for C-\).
If I type C-u C-\ to select one input method, and once again to select another, I find that every subsequent invocation uses the previous input method as the default input, so switching to the other input method boils down to C-u C-\ RET.
(setq my/input-methods '(nil "some-input-method" "japanese" ))
(defvar my/input-method-switch-counts 0 "a count; more than index")
(make-variable-buffer-local 'my/input-method-switch-counts)
(put 'my/input-method-switch-counts 'permanent-local t)
(defun my/switch-input-method ()
(interactive)
(setq my/input-method-switch-counts
(1+ my/input-method-switch-counts))
(let ((i (nth (% my/input-method-switch-counts
(length my/input-methods)
)
my/input-methods
)))
(set-input-method i) ;;interactive use may set any needed
)
;;(message "IME %s on" current-input-method)
)
(global-set-key (kbd "C-<SPC>") 'my/switch-input-method)
;;also make sure mode-line-mule-info in mode-line-format to show current input-method in mode-line

Suppress emacs auto-fill in a selected region

I use emacs to edit everything. On some of my LateX documents I would like to automatically disable auto-fill mode when I am editing tables and code. Basically, I'd like to have two tags, like:
%%% BEGIN NO FILL
%%% END NO FILL
and nothing between them will be autofilled.
Can anybody think of a way to do this? I would need to figure out whether or not the cursor is inside the region and then have to toggle the mode, and would need to do that every time the cursor moved. Or is there a better way to do it?
If you are using AUCTeX (you should be) then you may want to check out LaTeX-indent-environment-list. Adding an environment to this variable will make it so that (among other things) M-q doesn't refill the paragraph. Unfortunately it doesn't seem work for auto-fill-mode. The following largely untested code added to LaTeX-mode-hook might do what you want.
(setq auto-fill-function
(lambda ()
(unless (> (save-excursion (or (search-backward "%%% BEGIN NO FILL" (point-min) t) 0))
(save-excursion (or (search-backward "%%% END NO FILL" (point-min) t) 0)))
(do-auto-fill))))
It's very stupid and inefficient, but seems to be fast enough on my machine. It doesn't allow nesting, and requires that you manually mark up all sections that you don't want filled. What I am thinking of adding to my .emacs (until I read your question I didn't realize how much this bugged me) is below which keys off of the current environment so there is no need for special markup (though it only looks at the innermost environment (I'm not sure how much of a problem that will cause in practice)). Combining the two is left as an exercise to the interested reader.
;; You can use the following to unset the variables and play around with them
;; (makunbound 'auto-fill-ignore-environments)
;; (makunbound 'auto-fill-ignore-environments-regexp)
(defcustom auto-fill-ignore-environments
(mapcar 'car LaTeX-indent-environment-list)
"List of environments for which `auto-fill-mode' should be
disabled. Used to generate `auto-fill-ignore-environments-regexp'."
:type '(sexp)
)
(defcustom auto-fill-ignore-environments-regexp
(regexp-opt auto-fill-ignore-environments)
"Regexp matching LaTeX environments for which `auto-fill-mode'
should be disabled. If not set, automatically generated from
`auto-fill-ignore-environments'"
:type '(string)
:set-after '(auto-fill-ignore-environments)
)
(add-hook 'LaTeX-mode-hook
(lambda ()
(setq auto-fill-function
(lambda ()
(unless (string-match auto-fill-ignore-environments-regexp
(LaTeX-current-environment))
(do-auto-fill))))))
I have never used defcustom before so I'm sure that part could be improved quite a bit.
Got it. Check this out:
(defun in-no-auto-fill-region ()
(> (save-excursion (or (search-backward "%%% BEGIN NO FILL" (point-min) t) 0))
(save-excursion (or (search-backward "%%% END NO FILL" (point-min) t) 0))
))
(defun previous-line-checking-auto-fill (arg)
(interactive "P")
(previous-line arg)
(if (in-no-auto-fill-region)
(turn-off-auto-fill)
(turn-on-auto-fill)))
(defun next-line-checking-auto-fill (arg)
(interactive "P")
(next-line arg)
(if (in-no-auto-fill-region)
(turn-off-auto-fill)
(turn-on-auto-fill)))
(add-hook 'LaTeX-mode-hook
'(lambda nil
(local-set-key "C-p" 'previous-line-checking-auto-fill)
(local-set-key "C-n" 'next-line-checking-auto-fill)
(auto-fill-mode 1)
))
Alternately, you can turn off auto-fill-mode and use M-q to format paragraphs. I don't love auto-fill's jumpiness so I use this in every mode.
If you want to go the route of advising/redefining all the movement functions, this should help:
(defmacro movement-advice (func)
`(defadvice ,func (after ; run this after the original function is done (and point has moved)
;; Give it a unique name
,(intern (concat (symbol-name func) "-auto-fill-auto-off"))
;; Hopefully this satisfies the arguments of any function we can throw at it
(&rest args)
;; turn it on
activate
)
"Turn auto-fill-mode on or off automatically."
(auto-fill-mode (not (in-no-auto-fill-region)))))
(dolist (func '(next-line
previous-line
forward-paragraph
backward-paragraph
mouse-drag-region
;; Whatever you use
))
(eval `(movement-advice ,func)))