9 KiB
- Enable lexical binding
- Require the needed libraries
- Clean-up the display of messages
- Automatically join some channels
- Clean-up the channel buffers further
- Show #mowedline in a frame without minibuffer
- Provide the right feature
I used to use ERC mostly because I didn't really use IRC at all and it was basically the first IRC client in Emacs that was presented to me, it being built-in and all. When I started to use IRC more and wanted to customize the way it looks more I was surprised to find that it wasn't all that easy. A friend of mine was using Circe and he helped me figure out how to get started with customizing Circe the way I wanted it. So now I use Circe.
Enable lexical binding
;; -*- lexical-binding: t -*-
Require the needed libraries
In order to keep compiler warnings to a minimum, require the libraries that are used in the configuration of Circe.
(require 'auth-source)
(require 'circe)
(require 's)
Clean-up the display of messages
I once saw a very clean and simple weechat configuration on /r/unixporn and really wanted to have something similar. This was the start of my disappointment in ERC, I couldn't figure out how to change the way messages were printed. With a little help I did find out how to do it in Circe.
First we create a variable to store the length of the longest known
nick, so we can properly align all messages. This variable should be
buffer-local because each IRC chat will have different users with
different length names. We start with a length of 0
because we don't
know what the shortest nick there is going to be.
(defvar oni:circe-longest-nick 0)
(make-variable-buffer-local 'oni:circe-longest-nick)
Then we write the function that will print the most important
messages, the ones people send, including me. Whenever we get a
message or send a message, we check the length of the nick with the
last recorded maximum length. If the new nick is longer that any
previous ones we set this new length as the longest known length and
adjust lui-fill-type
accordingly. This ensures that continuation
lines are indented to the correct column.
(defun oni:circe-say-formatter (&rest keywords)
(let* ((nick (plist-get keywords :nick))
(len (length nick)))
(when (> len oni:circe-longest-nick)
(setq oni:circe-longest-nick len)
(setq-local lui-fill-type (make-string (+ len 3) ?\ )))
(plist-put keywords :nick (s-pad-left oni:circe-longest-nick " " nick))
(lui-format "{nick} {body}" keywords)))
I use this formatter both for messages I send myself and incoming messages, because they should basically look the same.
(setq circe-format-self-say #'oni:circe-say-formatter
circe-format-say #'oni:circe-say-formatter)
The rest of the formatting functions are basically the same, except
they don't need to change the known size of nicks because they don't
print the nick in the same column, instead they usually print
something like ***
to indicate that it is a system message and not a
user message. We do pad whatever they print with the same number of
spaces to keep them right-justified with the nicks.
(defun oni:circe-action-formatter (&rest keywords)
(format "%s %s %s" (s-pad-left oni:circe-longest-nick " " "*")
(plist-get keywords :nick)
(plist-get keywords :body)))
(defun oni:circe-server-message-formatter (&rest keywords)
(format "%s %s" (s-pad-left oni:circe-longest-nick " " "***")
(plist-get keywords :body)))
(defun oni:circe-server-join-in-channel-formatter (&rest keywords)
(format "%s Join: %s (%s) joined %s"
(s-pad-left oni:circe-longest-nick " " "***")
(plist-get keywords :nick)
(plist-get keywords :userinfo)
(plist-get keywords :channel)))
(defun oni:circe-server-join-formatter (&rest keywords)
(format "%s %s joined the channel"
(s-pad-left oni:circe-longest-nick " " "***")
(plist-get keywords :nick)))
(defun oni:circe-server-quit-formatter (&rest keywords)
(format "%s %s quit IRC: %s"
(s-pad-left oni:circe-longest-nick " " "***")
(plist-get keywords :nick)
(plist-get keywords :reason)))
(defun oni:circe-server-quit-channel-formatter (&rest keywords)
(format "%s %s left %s: %s"
(s-pad-left oni:circe-longest-nick " " "***")
(plist-get keywords :nick)
(plist-get keywords :channel)
(plist-get keywords :reason)))
(defun oni:circe-server-part-formatter (&rest keywords)
(format "%s %s parted %s: %s"
(s-pad-left oni:circe-longest-nick " " "***")
(plist-get keywords :nick)
(plist-get keywords :channel)
(plist-get keywords :reason)))
(defun oni:circe-server-nick-change-formatter (&rest keywords)
(format "%s %s is now known as %s"
(s-pad-left oni:circe-longest-nick " " "***")
(plist-get keywords :old-nick)
(plist-get keywords :new-nick)))
(setq circe-format-self-action #'oni:circe-action-formatter)
(setq circe-format-action #'oni:circe-action-formatter)
(setq circe-format-server-message #'oni:circe-server-message-formatter)
(setq circe-format-server-join-in-channel
#'oni:circe-server-join-in-channel-formatter)
(setq circe-format-server-join #'oni:circe-server-join-formatter)
(setq circe-format-server-quit #'oni:circe-server-quit-formatter)
(setq circe-format-server-quit-channel
#'oni:circe-server-quit-channel-formatter)
(setq circe-format-server-part #'oni:circe-server-part-formatter)
(setq circe-format-server-nick-change
#'oni:circe-server-nick-change-formatter)
Automatically join some channels
I started using IRC because #mowedline was started and I felt obligated to join it as I was one of two known Mowedline users at the time. So now that's the one I'm usually active in. I do like to keep an eye on #emacs from time to time and #ninthfloor in case something happens there, though usually not.
(defun oni:circe-nickserv-password (_)
(let ((found (nth 0 (auth-source-search :max 1
:host "irc.freenode.net"
:require '(:secret)))))
(when found
(let ((secret (plist-get found :secret)))
(if (functionp secret)
(funcall secret)
secret)))))
(setq circe-network-options
`(("Freenode"
:nick "ryuslash"
:channels ("#emacs" "#mowedline" "#ninthfloor")
:nickserv-password oni:circe-nickserv-password)))
Clean-up the channel buffers further
When chatting with people in an IRC channel, there really isn't much
need for any information in the mode-line. This is mostly because
the channel I'm most active on always has its own window. Visual
line mode is very handy to have in chats, in case I type very long
lines. And the wrap-prefix
is set so that when I do type long lines,
they are filled nicely to the circe prompt.
(defun oni:remove-mode-line ()
(setq mode-line-format nil))
(defun oni:set-circe-prompt-wrap-prefix ()
(setq wrap-prefix " "))
(add-hook 'circe-channel-mode-hook #'oni:remove-mode-line)
(add-hook 'circe-channel-mode-hook #'oni:set-circe-prompt-wrap-prefix)
(add-hook 'circe-channel-mode-hook 'visual-line-mode)
Show #mowedline in a frame without minibuffer
When I'm chatting on #mowedline I do so in a separate small window.
This window needs no minibuffer as I do very little actualy Emacsy
things in it. Just typing a little and reading. So far I only do
this with #mowedline. In order to specifically show it in a frame
without a minibuffer I use display-buffer-alist
to specify how to
show it. The function called dynamically binds default-frame-alist
to add a minibuffer
element with the value nil
(meaning, no
minibuffer). I can't do this in the regular default-frame-alist
because I want all other frames to show up with a minibuffer. It
then creates a new frame and switches to the given buffer in it.
(defun oni:display-in-minibufferless-frame (buffer _)
(let ((default-frame-alist default-frame-alist))
(push '(minibuffer . nil) default-frame-alist)
(let ((frame (make-frame)))
(select-frame frame)
(switch-to-buffer buffer))))
(add-to-list 'display-buffer-alist
'("^\#mowedline$" oni:display-in-minibufferless-frame))
Provide the right feature
In order to be able to use (require 'circe-init)
we must first
provide
it.
(provide 'circe-init)