ERC - modify incoming messages - emacs

I am using the below elisp code to modify incoming messages from ERC
(defun my-modify (msg)
(replace-regexp-in-string "abc" "xyz" msg))
(add-hook 'erc-insert-modify-hook 'my-modify)
I am getting the following error
error in process filter: Wrong number of arguments: (lambda (msg)
(replace-regexp-in-string "abc" "xyz" msg)), 0

I am not sure what's wrong in my previous code, but I was able to modify incoming messages using this code (from http://mwolson.org/projects/emacs-config/erc-init.el)
(defun my-modify (proc parsed)
(let ((msg (erc-response.contents parsed)))
(when (stringp msg)
(setf (erc-response.contents parsed)
(erc-replace-regexp-in-string "abc" "xyz" msg))
nil)))
(add-hook 'erc-server-PRIVMSG-functions 'my-modify)
This works as expected, but what I really wanted was to strip away a particular nick and display only the message. However I don't think that's possible at ERC level. Rather I will have to try it at the buffer level.

Apparently functions invoked from erc-insert-modify-hook do not get the incoming message as an argument, like your code expects.
Quoting http://www.emacswiki.org/emacs/ErcHooks we see:
This hook is run after the actual text is inserted. While it runs, the buffer is narrowed to the inserted text using narrow-to-region. Thus, you can use (point-min) and (point-max) to determine start and end of the inserted text on the modify hooks.
So you should replace in the current buffer instead.

Related

emacs - after-change-functions are not executed after buffer modification

I have added a function to after-change-functions list using this code
(defun test-func ()
(message "foo"))
(add-hook 'after-change-functions 'test-func nil t)
Now whenever i change buffer manually, test-func is getting called. But when i programatically modify the buffer using insert, the contents of buffer are getting updated, but test-func is not getting called.
Any pointers on how to activate test-func everytime buffer is updated?
Update:
I am trying to convert markdown to html and serve that on browser, so that whenever user types some markdown, html will be updated automatically.
Here is original implementation of test-func
(defun impatient-markup-update (&rest args)
"Update html buffer if markup buffer updates."
(save-buffer impatient-markup-buffer)
(with-current-buffer (get-buffer impatient-markup-html-buffer)
(erase-buffer)
(insert (shell-command-to-string
(format "%s %s" impatient-markup-pandoc impatient-markup-buffer)))))
Use sleep-for after the call to message, as a test, to see whether you see the message then.
after-change-functions does not necessarily run your hook in the buffer you expect. And as the doc says:
*Buffer changes made while executing the after-change-functions don't call any before-change or after-change functions. That's because inhibit-modification-hooks is temporarily set non-nil.
Check what else is on that hook, etc. IOW, do a little debugging.

Emacs completion-at-point-functions

I'm writing a derived mode, based on comint-mode. The mode is an interface to a command line program (GRASS gis), and the comint mode completion works for the programs. I'm trying to add on support for completing the arguments to the program, via completion-at-point-functions. A toy example is:
(setq my-commands
'(("ls"
("my-completion-1")
("my-completion-2"))
("mv"
("my-completion-3")
("my-completion-4"))))
(defun my-completion-at-point ()
(interactive)
(let ((pt (point)) ;; collect point
start end)
(save-excursion ;; collect the program name
(comint-bol)
(re-search-forward "\\(\\S +\\)\\s ?"))
(if (and (>= pt (match-beginning 1))
(<= pt (match-end 1)))
() ;; if we're still entering the command, pass completion on to
;; comint-completion-at-point by returning nil
(let ((command (match-string-no-properties 1)))
(when (member* command my-commands :test 'string= :key 'car)
;; If the command is one of my-commands, use the associated completions
(goto-char pt)
(re-search-backward "\\S *")
(setq start (point))
(re-search-forward "\\S *")
(setq end (point))
(list start end (cdr (assoc command my-commands)) :exclusive 'no))))))
(push 'my-completion-at-point completion-at-point-functions)
This almost works. I get normal completion of program names. However, if I have entered ls at the command line, hitting tab inserts my-completion- and doesn't offer the two options. Hitting tab again inserts my-completion- a second time, so that I now have ls my-completion-mycompletion-.
My actual code includes a few lines to check for multi-line commands, but makes no changes to the completion code. With this version of the code, I hitting tab on a line that starts with one of the program names in my-commands I am presented with a list of the possible arguments to complete the command with, but nothing is inserted in the buffer, and the list does not get narrowed by typing the first few letters of an argument.
I've been over the manual, but I can't figure out the correct way to write a completion-at-point function. Any ideas what I'm missing?
I have looked briefly at pcomplete, but the didn't really understand the 'documentation', and didn't make any progress.
The problem seems to be with the way you're finding start and end to return the boundaries of the argument at point. I didn't spend long enough debugging it to be sure of the details, but I think if you call the function interactively you'll see that it returns the same value for start and end, and this means that the completion UI doesn't know to use the argument at point to select from the completion table you've passed it.
Changing the last part of your function to the following seems to be one fix:
(when (member* command my-commands :test 'string= :key 'car)
;; If the command is one of my-commands, use the associated completions
(goto-char pt)
(let ((start
(save-excursion
(skip-syntax-backward "^ ")
(point))))
(list start pt (cdr (assoc command my-commands)) :exclusive 'no)))))))
This gives the expected results when added as an element of completion-at-point-functions.
Here I've used skip-syntax-backward instead of regexp search, which I think is slightly more idiomatic Elisp for this kind of thing. It just says to move point backwards across anything that is not in syntax class "whitespace". The skip-syntax functions return the distance moved rather than the value of point, so we have to add a call to point at the end of the save-excursion.
If you do use regexp searches in a function like this, it's usually a good idea to pass t for the fourth argument, noerror, so that it doesn't pass on errors to the user if it fails to match. This does mean that you have to check for yourself whether the return value is nil, though.
Finally, instead of push to add the completion function you might want to use add-hook as follows:
(add-hook 'completion-at-point-functions 'my-completion-at-point nil t)
This does two useful things: it checks whether your function is already in the hook before adding it, and (by passing t for the fourth argument, local) it only adds the function to the buffer-local value of the completion-at-point hook. This is almost certainly what you want, since you don't want to use these completions in every other Emacs buffer when you press the TAB key.

elisp message-box: can I include newlines in the text, and if so, how?

Using the message-box fn, I can display a modal dialog.
I know this is annoying and not always a good user experience. Flymake's use of the message-box to warn when a flymake check has failed, seems a good example of that.
But put the user experience issue aside for the purposes of this question. Assume that I am sensible enough to use message-box responsibly.
How can I format the text displayed by the message box? The simplest case is, how can I tell message box to display multiple lines of text. If I have a longish message, it results in a very wide message box. (Another UI problem exhibited in the Flymake usage).
See here for an example. this code:
(message-box (concat "You need to get an \"api key\".<NL>"
"Then, set it in your .emacs with the appropriate statement."))
results in this UI:
I'd like a newline in place of the <NL>. I tried using \n and \r and \r\n, none of those worked. I also tried \x000D and \x000A.
Even better than simple line breaks, I'd like to be able to format the text. Italic, bold, or whatever. Are there options? Nothing is mentioned in the doc on this.
I looked in the source to try to figure this out but could not find message2, which is called by message-box, and I'm not sure I'd learn anything anyway by just looking at source.
Use \n. That does the trick:
(message-box (concat "You need to get an \"api key\".\n"
"Then, set it in your .emacs with the appropriate statement."))
hack workaround on Windows for bug #11138.
(defun multiline-message-box (msg)
"display a multiline message box on Windows.
According to bug #11138, when passing a message with newlines to
`message-box' on Windows, the rendered message-box appears all on
one line.
This function can work around that problem.
"
(flet ((ok (&optional p1 &rest args) t))
(let ((parts (split-string msg "\n"))
(menu-1 (make-sparse-keymap "Attention"))
c)
(define-key menu-1 [menu-1-ok-event]
`(menu-item ,(purecopy "OK")
ok
:keys ""))
(define-key menu-1 [separator-1] menu-bar-separator)
;; add lines in reverse order
(setq c (length parts))
(while (> c 0)
(setq c (1- c))
(define-key menu-1 (vector (intern (format "menu-1-fake-event-%d" c)))
`(menu-item ,(purecopy (nth c parts))
nil
:keys ""
:enable t)))
(x-popup-menu t menu-1))))
(multiline-message-box "Hello!\nI must be going!\nThis is line 3.")

How to automatically evaluate certain lisp code after starting an emacsclient?

When starting Emacs, init.el (or .emacs.el) is evaluated. However, when starting emacsclient, no similar lisp code is evaluated.
How can I get a lisp file to be evaluated every time I open a new emacsclient?
(This would be handy for frame specific customizations.)
I assume the answer is to use some hook, but I can't seem to find the correct hook to use.
I look forward to your answers.
You can add a function to the hook 'server-visit-hook, which is run every time the server is called (every time you call emacsclient).
I use the following code to automatically change the behavior of server buffers. I use it especially with the Firefox extension It's All Text. In that extension, buffers are named according to the domain name, so you can figure out which rule to apply by using string-match to match the name of the file.
(defun server-edit-presets ()
(cond
;; When editing mail, set the goal-column to 72.
((string-match "mail\\.google\\.com\\.[0-9a-z]+\\.txt" (buffer-name))
(longlines-mode-off)
(auto-fill-mode 1)
(set-fill-column 72)
(save-excursion
;; Don't know if this is necessary, but it seems to help.
(set-buffer (buffer-name))
(goto-char (point-min))
;; Replace non-breaking strange space characters
(while (search-forward (char-to-string 160) nil t)
(replace-match " "))))))
(add-hook 'server-visit-hook 'server-edit-presets)
(add-hook 'server-visit-hook '(lambda () (longlines-mode 1)))
If you really want new frame customizations, there's create-frame-hook which takes one arg (the new frame)...
If you mean gnuclient, you can use the command-line option "-eval" to evaluate something (and then just make an alias to always eval your customizations).
#LSW:
Try 'window-setup-hook. This addresses the annoyance since it is called even if emacsclient is not passed a file.
It seems that those hooks are no more, so here's the new version.
(add-hook 'server-after-make-frame-hook 'consult-recent-file)

Emacs Elisp dynamic interactive prompt

I am trying to have a dynamic prompt from my elisp function. I want something like replace-regexp where it will show you the last regexp entered. I tried
(interactive
(concat "sab" "bab")))
that doesnt work!
I also tried message like format
(interactive "s %s" last-used-regexp)
and that doesn't work!
Anyone know how to do this?
Thank you!
M-x find-function is your friend. It will tell you how anything in emacs works by showing you the source code. Using it, I find that query-regexp-replace calls query-replace-read-args which calls query-replace-read-from which calls read-from-minibuffer using a prompt created from the last used regexp, which is saved in the dotted pair query-replace-defaults.
So:
(defun my-func ()
"Do stuff..."
(interactive)
(read-from-minibuffer "Regexp? " (first query-replace-defaults)))
is a command that throws up a prompt with the last entered regexp as the default.
Use a variable for input history, and interactive with a list:
(defvar my-func-history nil)
(defun my-func (str)
(interactive (list (read-from-minibuffer "Input string: " (car my-func-history) nil nil 'my-func-history)))
(insert str))
If you don't want the last value entered in there initially, change the (car my-func-history) to nil. You can of course up/down arrow to go through the history at the prompt.