Bowmansarrow

Emacs 30.1 released… and stuff broke.

Well, it's been over a year since I posted an update about Crafted Emacs! How time flies, right! I continue to support the project and have been thinking about different things to do with Crafted Emacs, but nothing really sticks as something that needs to be done.

Lost package-selected-packages value!!

However, with the release of Emacs 30.1, there are a few things I need to review. One thing kinda broke configurations depending on how you write your configuration. The new “feature” from Emacs has to do with the function that saves the values of defcustom variables. We make a call to the package--save-selected-packages by adding it to the after-init-hook. Notice there are two dash characters in the name. That generally means it is not a “public” API, and using it can be risky. Basically, the form of that function is this:

(defun package--save-selected-packages (&optional value) ... )

Notice the &optional value parameter? In Emacs version prior to 30.1 if that value was not present (ie its value is nil) it would use whatever the value of the package-selected-packages variable, which is a list of the packages to be installed. We update that value by calling add-to-list to append the packages to install, which is important essentially the first time you startup Emacs from your configuration. After that, they are already installed, so the value is checked and if there is nothing to do, no additional packages are installed. In Emacs prior to version 30.1, this was safe since we were already updating the value and the &optional value parameter would not be used if it were nil.

All that changes with Emacs 30.1 which resulted in something that looked like this:

(custom-set-variables
 ...
 '(package-selected-packages nil)
 ...)

That's a bit concerning as we need to check if all the packages are installed already, but the value is nil when we start Emacs (assuming you follow the pattern in the example configurations and load the custom.el file at the top of your init.el file). It is updated during initialization, but then written back out as nil after initialization.

That happens because the new code for that function checks to see if either the optional value is not nil or the value of after-init-time is set. When after-init-time is set, then the assumption is the provided (or possibly not provided) value parameter contains the list of installed packages and it gets sorted and set as the list package-selected-packages. Since we weren't providing that optional value, the result was the package-selected-packages was always nil. I had noticed this behavior and just thought it was something wrong with my configuration, but then Jabbo (one of the Crafted Emacs maintainers) raised a bug about it and provided a PR to solve the problem. The solution is to pass the package-selected-packages along as the (not so) &optional value.

Oh, and by the way…

We also added code for using the new preview completion function globally. David demonstrated it in a recent live stream. This configuration lives in the crafted-defaults-config module and gets turned off if you use the crafted-completion-config module with Corfu installed.

In other news…

There was an issue raised about tree sitter, upstream broke a few things like latex, janet, and markdown for different reasons. Jvdydev provided some guidance on how to work around the problem. We use the treesit-auto package to help automate the installation of grammars, and usage is by opt-in for the specific grammars you would actually use. If you ran into the problem from the issue, you could remove those from the list of grammars, and possibly try to install them manually or use the Emacs built-in functions to install them. My comment on that issue has an example installing the grammars for tsx and typescript. As the problem is outside the scope of Crafted Emacs (ie we depend on the author of the treesit-auto package to fix issues), there isn't much we can do, but, as you can see in the comments, we do try to be as helpful as possible.

A similar kind of problem was raised regarding org-appear and org version 9.7.2. The built-in version of org in Emacs prior to version 30.1 is 9.6.15 and the problem mentioned does not appear with that version. This is another situation where the problem is outside the scope of Crafted Emacs. I'll test again with Emacs 30.1 which includes org 9.7.11 and see if the problem persists and make an update to this blog with what I find.

[UPDATE] I could not duplicate the problem above as mentioned in this issue.

Finally, there was an issue that raised a question about setting the value of the package-archives based on the running Emacs version. The question was very well answered by Jvdydev but it turns out the question was actually about the inability to install emark-consult. Jvdydev provided an answer to that as well, at least as best as she could without an error message.

Conclusion

So, it's been slow on the Crafted Emacs front so far, but if you are using Crafted Emacs and notice anything a bit odd after the update to Emacs 30.1, please post an issue and we'll be happy to take look and either fix something in our code or possibly help you with your configuration. Of course, you can always submit a PR with your issue as well, we'd be happy to review it and possibly pull it in.

Happy Crafting!

Tags: #emacs

It's been a while since I posted a blog. After reviewing a couple of the more recent SystemCrafters live streams1 it seemed like a good time to add some thoughts on how Crafted Emacs might be used to solve similar problems. I wasn't able to participate live, I still have a day job and have meetings and stuff, so I had to watch them after-the-fact. Something I usually try to do anyway, but I prefer to participate in the streams as they happen.

Crafted Emacs for minimal configuration

In the live stream on 16 November 2024, David talks about his approach to rewriting his configuration. After not using a literate approach for a while, he has returned to using a literate approach to his config. Not my cup of tea, but I totally understand why he would go that route, and he gives reasons why he likes that approach during the stream. He also mentions only using GNU and Non-GNU ELPAs to help minimize his configuration. Crafted Emacs assumes you want every ELPA, but it does prioritize the GNU and Non-GNU ELPAs above MELPA-stable and MELPA. With Emacs 29, you get both GNU and Non-GNU ELPAs by default, so if you choose to limit yourself to just those, you don't need to use the crafted-early-config.el in your config, but you probably still need to add (require 'package) to your config if you intend to continue to use any of the crafted-*-packages.el modules as they need the package-selected-packages variable to be defined. For the completions David talked about, if you use the crafted-defaults-config.el module, those are setup by default for you. This includes the following snippet to control the size of the *Completions* buffer:

(add-to-list 'display-buffer-alist
             '("\\*Completions\\*"
               (display-buffer-reuse-window display-buffer-pop-up-window)
               (inhibit-same-window . t)
               (window-height . 10)))

If you are not using that module, but prefer to use the built-in completions (or something like icomplete, ido, or fido) the above snippet will help keep *Completions* window from taking over the screen. David discusses the completion styles and gives examples, so watch the live stream2 for a good overview of how that variable works.

With all that said, a minimal Crafted Emacs configuration might be something like this:

(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(when (and custom-file
           (file-exists-p custom-file))
  (load custom-file nil :nomessage))

(load (expand-file-name "~/crafted-emacs/modules/crafted-init-config"))
;; Adjust the path (e.g. to an absolute one)
;; depending where you cloned Crafted Emacs.
;; (load "/path/to/crafted-emacs/modules/crafted-init-config")

;; needed if you want to use any of the crafted-*-packages modules
(require 'package)

(require 'crafted-defaults-config)

(add-to-list 'display-buffer-alist
             '("\\*Completions\\*"
               (display-buffer-reuse-window display-buffer-pop-up-window)
               (inhibit-same-window . t)
               (window-height . 10)))

;; Set default coding system (especially for Windows)
(set-default-coding-systems 'utf-8)

(provide 'init)
;;; init.el ends here

You can also take a look at my post where I develop a minimal configuration.

Final thoughts…

Developing a minimal configuration is an interesting challenge. My configuration is not quite as minimal as it used to be, I have added packages over time as needed to be productive with tasks at work. I also have a “default” set of “can't live without” packages I choose to install. David chose to implement the portions of some packages he actually uses rather than install the full package. The pros of just installing the package is reduced cognitive load (someone else does the work of writing and updating features you need) and an ostensibly shorter base configuration. The pros of writing the packages by pulling just the functionality you use and molding it to your configuration as David does is less reliance on external packages. This is quicker to get up and running with just your configuration, which can be handy when on a remote host, or a system with corporate controls that prevent downloading code not vetted by the corporate IT infrastructure.

Regardless of your approach, Crafted Emacs can help you get started with the building blocks you need to be productive quickly. After that, it's up to you to whittle things down or write custom code or whatever to fit your own personal needs.

Happy Crafting!

Tags: #emacs #systemcrafters

Footnotes

1 See this one and this one for context.

2 Start here for completion styles

Easy Customization interface?

There is the impression, at least to me, the built-in Emacs configuration utility known as the Easy Customization gets a bit overlooked by a lot of users. I see posts in different places about people just coming to Emacs and wanting to get started. What they tend to find is random snippets of Emacs Lisp to paste into a file. Rarely does anyone actually mention you can configure Emacs without writing any lisp code and get quite a well configured Emacs as a result.

The problem.

I have been nagged by a behavior in Emacs, one I'm guilty of authoring in which the *Completions* window unhelpfully pops up when I'm trying to complete something in the minibuffer with fido-vertical-mode. This seems like a duplication of effort and is largely due to the way Crafted Emacs sets up completion in the crafted-defaults-config module. As I was considering how I might resolve this I started Emacs in a blank directory (ala the --init-directory command line parameter), and then I opened the Emacs manual (C-h i) and started reading chapter 9 on completions.

Researching.

As I was reading, I thought to set some customizations for completions, so I ran M-x customize-option RET <option> RET and then applied the settings. This generated a bunch of variable settings in a block of lisp. But I didn't write any of it. I just set values for my preferences and Emacs saved those decisions in a way it would be able to consume later – in the custom-set-variables form.

Going bigger.

After tinkering with completions for a bit, this thing grew another head and I thought about trying to see how close I could get to my current configuration without installing any packages and not writing any elisp, or at least as little as possible. The result can be found here. I think I was a bit surprised at how close I got to my normal configuration. Really, its about 80%-90% of the way there. I need a few extra packages to really flush things out, but in general, I have most of everything I need on a day-to-day basis. Full disclosure, I did write some lisp, I didn't find a way to change key bindings through the Easy Customization interface, and I modified the display-buffer-alist by copying code from Crafted Emacs, but I could have used the Easy Customization interface instead.

And bigger still.

Inspired by how easy that was, I rewrote my entire configuration. Starting with the base from above, I just added more customization through the Easy Customization interface, wrote some elisp where it seemed to make sense and restarted Emacs. I goofed in a couple of places, but those were easy to fix. I looked through all the packages I was installing decided I didn't really need some of them, and left them out of this configuration. I listed all the packages directly in the custom-set-variables form as values for the package-selected-packages variable. Crafted Emacs does this by calling add-to-list in the *-package modules. So, by putting them directly there I didn't need any of the *-package modules, and since the custom.el file gets loaded early, I could still call package-install-selected-packages per the Crafted Emacs manual. I do have some elisp to conditionally add some packages to the list based on which system-type or emacs-version or if certain executables are on the system. I still use the *-config modules from Crafted Emacs and some of my own personal modules get loaded as well. The rest of the configuration is largely keybindings and lazily applied settings when certain packages are loaded.

The result.

Overall, I'm happy with the result. The “minimal” version of my init.el file is about 66% of the size of the “craftedv2” version, which is a bit arbitrary as I have my configuration spread out over numerous other files (custom.el, custom modules, Crafted Emacs modules). Startup time is less than a second, not that it matters since I run Emacs as a server and then use emacsclient to start editing sessions as needed.

Conclusion

What started out as just tinkering with completions and the Easy Customization interface turned into a rewrite of my configuration. I do like to recommend the usage of the Easy Customization interface, especially for those coming to Emacs for the first time.

Tags: emacs

Literate Emacs Configurations

I'd like to present some thoughts on literate configurations. This is one of those interesting bits in the Emacs configuration space that shows up from time to time. I'm in the “I don't do it this way” camp and I'll present some thoughts on why that is. For those of you who choose to be in the “I do it!” camp, well, that's fantastic! The great thing about Emacs is doing it “your” way, whatever “way” that is.

What is it anyway?

Donald Knuth wrote, “Literate programming” is a methodology that combines a programming language with a documentation language, thereby making programs more robust, more portable, more easily maintained, and arguably more fun to write than programs that are written only in a high-level language. The main idea is to treat a program as a piece of literature, addressed to human beings rather than to a computer. The program is also viewed as a hypertext document, rather like the World Wide Web.”1

In the sense of your Emacs configuration, it is basically writing everything in some markup language with executable code snippets woven through the prose documenting your decisions. The most common markup language in this context is the venerable Org mode. It is well supported, has a huge list of language integrations, including Emacs Lisp (of course), and is arguably one of the “killer features” available to Emacs users. It's also a very simple markup language with very little in the way of actual syntax and it exports to many different presentation formats. Forges like GitHub and GitLab will render Org files natively in the same way they render Markdown files. This make it very useful for influencers like Protesilaos Stavrou2 (also known as “Prot”) among others to host their Emacs configuration where people can read them, get some context on why or how the configuration works, and still see the code. David Wilson in a recent livestream3 converted his configuration back to a literate style and mentioned some of the advantages being:

  • Having things nicely organized in a single large Org file.
  • Easy to generate a website from the configuration.
  • Easy to describe why you do certain things in your configuration.

Why its a challenge

In the same video, David goes on to talk about why he stopped using it for a while:

  • He got tired of having to re-tangle his configuration files every time he synced his dot files between machines.
  • If you tangle (ie. generate) all the code files in your “dot files” folder and then symlink those to your home folder, you either have to ignore all the generated files or check them in with your Org file, which means lots of diffs when you re-tangle your Org file after each change.
  • There is the possibility you might change one of the generated files and forget to update the Org file, which would then overwrite your changes when you re-tangle your configuration.

Another challenge, it does take time to tangle your configuration, especially if yours is on the longish side. Do that every time you save your configuration and Emacs will seem to lag a few seconds as it tangles things.

Why I'm a “don't” person

Keeping in mind, these are my opinions, here are some reasons why I prefer not to use a literate configuration:

  • It's a hassle. Keeping things synced, both on the filesystem as well as in version control is just more than I prefer to manage.
  • It's easier to not use a literate configuration. No extra configuration, no slow down tangling things, no extra files to either ignore or version frequently, etc.
  • Documenting things can be done just as easily with code comments.
  • Organizing your configuration is a matter of discipline not markup. Using Org mode does not improve how well organized your code is if you don't discipline yourself to keep things organized. It's just extra syntax. The same is true of keeping your configuration in “module” files. Its only more organized if you maintain the discipline of keeping it organized.
  • I don't need to generate a website from my configuration. Anyone who wants to read it can do so on GitLab, which will render the Emacs Lisp code and comments just fine.

Outline modes – outline-minor-mode and allout-mode

There was a suggestion on David's stream regarding using allout-mode, one of the outline modes built-in to Emacs. I've used this mode in the past, but I usually end up back in Org mode because of all the features that project brings to the table.

allout-mode is a fairly simple outliner though and pretty easy to get into if you need a nice outlining tool. And by simple I mean, really simple. No frills. It outlines and that's about it. The default keyboard prefix is C-c SPACE which can be a bit to type, but once you get used to it, its not bad. That said, changing it to something like s-SPACE might make it more approachable.

Using the built-in outline modes (outline-minor-mode or allout-mode) seem to be a nice alternative. In fact, reading through the documentation on Comment Tips4, using three ;;; should start on the left column and are already considered headers in Outline mode, thus using them and turning on outline-minor-mode and you instantly get navigation keyboarding, the ability to collapse sections, etc. and you can document your code in the same way you would with Org mode using only the Emacs Lisp syntax, and no tangling required.

allout-mode adds additional flavor to those ;;; comments, so you only write ;;; plus the header level needed and you also get code navigation keyboarding, the ability to collapse sections, etc and you still only document your code with Emacs Lisp syntax and no tangling required. allout-mode also should allow generating your configuration as LaTeX, but there is a bug in my case which I'll try tracking down at some point. I don't need it as I don't need to generate my configuration in any other language than the default Emacs Lisp.

Getting started with allout-mode

I'll show an example here of how to get started using allout-mode. Using outline-minor-mode doesn't require anything, per se, but you might like to use something like this to turn it on in your configuration:

M-x add-file-local-variable-prop-line RET eval RET (outline-minor-mode 1) RET

This would assume you are just using multiple semi-colon's as described in the Emacs Lisp Comment Tips4. To use allout-mode requires just a little more setup and then some updates to those lines beginning with ;;;.

So, lets start with your init.el file.

  • M-x add-file-local-variable-prop-line RET mode RET emacs-lisp RET
  • M-x add-file-local-variable-prop-line RET eval RET (allout-mode 1) RET
  • M-x add-file-local-variable RET eval RET (allout-expose-topic '(0 :)) RET
  • M-x revert-buffer RET

The list item in that list sets the variables and executes the code, you'll get the standard Emacs warning about this, so you can choose to say 'y' to run it once. Other options exist to persist that decision as well. That's all you need to get things started. You should also customize the allout-auto-activation variable and set it to t, at least you should according to the various docstrings in allout.el.

To proceed from there, you add ;;;_* to major headings. A sub-heading can be added with ;;;_ * (Note the extra space). See the allout.el source to see a more detailed example. In my case, I generally use just these, with a couple of exceptions.

allout-mode allows you to set your bullets to something that means something semantically. For example ;;;_ ? might indicate a question (like, “do I need this section?”), ;;;_ ^ might indicate this section refers to the section above, for example if you are setting up IDE rules, you might have generic IDE configuration first, then sub-topics for each programming language:

;;;_* IDE
;;; This section configures eglot as my language server
(require 'crafted-ide-config)           ; Crafted Emacs module
(with-eval-after-load 'crafted-ide-config
  (crafted-ide-configure-tree-sitter '(latex python)))

;;;_ ^ Prog mode hooks
(with-eval-after-load "prog-mode"
  (keymap-set prog-mode-map "C-c e n" #'flymake-goto-next-error)
  (keymap-set prog-mode-map "C-c e p" #'flymake-goto-prev-error))

;;;_ ^ Python
(require 'my-ide-python)                ; My custom Python module

Example init.el file

To complete this blog post, here is the Crafted Emacs example init.el5 using allout-mode. To complete the requirement as mentioned by Mr. Knuth1, you should be able to export this to LaTeX, although, for me there seems to be a bug.

;;; init.el --- Crafted Emacs Base Example -*- mode: emacs-lisp; lexical-binding: t; eval: (allout-mode 1); -*-

;; Copyright (C) 2023
;; SPDX-License-Identifier: MIT

;; Author: System Crafters Community

;;; Commentary:

;; Base example init.el (extended from the info file).
;; Basic example of loading a module.

;;; Code:

;;;_* Initial phase

;;;_ ^ Custom File
;;; Load the custom file if it exists.  Among other settings, this will
;;; have the list `package-selected-packages', so we need to load that
;;; before adding more packages.  The value of the `custom-file'
;;; variable must be set appropriately, by default the value is nil.
;;; This can be done here, or in the early-init.el file.
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(when (and custom-file
           (file-exists-p custom-file))
  (load custom-file nil :nomessage))

;;;_ ^ Bootstrap crafted-emacs
;;; Bootstrap crafted-emacs in init.el
;;; Adds crafted-emacs modules to the `load-path', sets up a module
;;; writing template, sets the `crafted-emacs-home' variable.
(load (expand-file-name "../../modules/crafted-init-config"
                        user-emacs-directory))
;;; Adjust the path (e.g. to an absolute one)
;;; depending where you cloned Crafted Emacs.
;;; (load "/path/to/crafted-emacs/modules/crafted-init-config")

;;;_* Packages phase
;;; Collect list of packages to install. Do not just blindly copy this
;;; list, instead think about what you need and see if there is a
;;; module which provides the list of packages needed. This phase is
;;; not needed if manage the installed packages with Guix or Nix. It
;;; is also not needed if you do not need Crafted Emacs to install
;;; packages for a module, for example,
;;; `crafted-speedbar-config' does not require any packages to
;;; be installed.

;;; Add package definitions for completion packages
;;; to `package-selected-packages'.
(require 'crafted-completion-packages)

;;;_ ^ Manually select "ef-themes" package
(add-to-list 'package-selected-packages 'ef-themes)

;;;_ ^ Install the packages listed in the `package-selected-packages' list.
(package-install-selected-packages :noconfirm)

;;;_* Configuration phase
;;; Some example modules to configure Emacs. Don't blindly copy these,
;;; they are here for example purposes. Find the modules which work
;;; for you.

;;;_ ^ Completions
;;; Load configuration for the completion module
(require 'crafted-completion-config)

;;;_ ^ Crafted Emacs configuration
;;; Some more configurations that don't require packages to be installed
(require 'crafted-defaults-config)
(require 'crafted-startup-config)

;;;_* Optional configuration

;;;_ ^ Profile emacs startup
(defun ce-base-example/display-startup-time ()
  "Display the startup time after Emacs is fully initialized."
  (message "Crafted Emacs loaded in %s."
           (emacs-init-time)))
(add-hook 'emacs-startup-hook #'ce-base-example/display-startup-time)

;;;_ ^ Set default coding system (especially for Windows)
(set-default-coding-systems 'utf-8)


(provide 'init)

;;;_* Local emacs vars.
;;Local variables:
;;allout-layout: (0 :)
;;eval: (allout-expose-topic allout-layout)
;;End:

;;; init.el ends here

Final thoughts

I'm generally not a fan of literate configuration. The idea isn't a bad one, it's just not for me. I don't see the point in writing in two different languages for this particular use-case, and there are simpler ways to get the same effect. I do think literate programming has it's place, for example, see this video from a recent meetup on the subject. I encourage you to try it both ways though, and to find the one which makes sense to you in your configuration.

Happy Crafting!

Footnotes

1 https://www-cs-faculty.stanford.edu/~knuth/lp.html

2 https://protesilaos.com

3 https://www.youtube.com/live/Ex9zI4Fcirs?si=Izqdqu88BSmsI-c6

4 https://www.gnu.org/software/emacs/manual/html_node/elisp/Comment-Tips.html

5 Yep, shameless plug. https://github.com/SystemCrafters/crafted-emacs/blob/master/examples/base/init.el

Happy New Year

Hello Crafters, welcome to 2024! Hope everyone had a wonderful and safe holiday season, and now that is behind us time to get back to work, right!?!!

Crafted Emacs Update

Since the release of Crafted Emacs v2 last year, things have been a bit quite on the development front. Since the last time I gave an update there have been a few improvements:

  • Usage of eglot-ensure was changed to an “opt-in” configuration. This change included checking to see if the mode was actually available on the system before adding eglot-ensure to a hook. This makes a lot more sense now, not everyone will have all the programming modes installed, much less have the desire to turn on eglot for all of them.
  • There was a bug in the docs after the release, I missed removing a reference to the development branch. Thanks to @bird-dancer for noticing and providing the PR for the fix!
  • Another bug related to loading the custom-file was found by @jvalleroy, also thanks for the PR to fix it!
  • @mgmarlow has been fairly active, providing discussion, the patch for eglot-ensure mentioned above, and an example configuration for the rust programming language. Thank you!!
  • I changed our tree-sitter configuration, originally we tried to install every ts grammar by default, but a change to the tree-sit-auto broke it. Reviewing how we were using that package, it seemed we had been approaching the problem backwards all along, so it is also an “opt-in” rather than an “opt-out” configuration.

Example: Adding the Go language

There have been frequent questions around providing one language mode or another, or all of them, over the course of the project. With Crafted Emacs v2, we moved to more general modules, which a user could use as building blocks for extending Crafted Emacs as they see fit. You can read more of the language module discussion here if you wish.

Currently, we have the example of adding rust as a language module, I'll walk through the code I used recently to extend my own configuration for the Go language.

The initial process

I took a little time to search the internet for others who had configured Emacs for Go development. I also took some time to review the existing go-mode.el, which originally shipped with Go, but later was rewritten by Dominik Honnef, see the GitHub page for more information.

Updating my init.el configuration

I need to make several changes in my base configuration file:

  • I add to the list of packages which need to be installed
  • Turn on the tree-sitter mode for Go

Adding the needed packages

I need a couple of packages installed as Emacs doesn't know about Go out of the box. I add these lines early in my init.el file:

(add-to-list 'package-selected-packages 'go-eldoc)
(add-to-list 'package-selected-packages 'go-mode)

I evaluate these lines, then evaluate this (package-install-selected-packages :noconfirm) to get the packages installed.

Update treesit-auto to install go-ts-mode

I like to use tree-sitter modes, so I add go to the list I already have. Mine now looks like this (yours may be different):

(with-eval-after-load 'crafted-ide-config (crafted-ide-configure-tree-sitter '(go java javascript latex markdown python typescript)))

Putting together a custom module

I start by building on the work in crafted-ide-config:

(require 'crafted-ide-config nil :noerror)

This way, I get to take advantage of the work already done to setup eglot, tree-sitter modes, editorconfig, etc.

Setup project.el

We need to tell project.el about Go projects. project.el does not know about GOPATH or go modules, so we need to tell it how to find the go.mod file. This also enables eglot to work as it uses project.el to find project assets.

(require 'project)

(defun project-find-go-module (dir)
  (when-let ((root (locate-dominating-file dir "go.mod")))
    (cons 'go-module root)))

(cl-defmethod project-root ((project (head go-module)))
  (cdr project))

(add-hook 'project-find-functions #'project-find-go-module)

Setup hooks

Now, we need to setup some hooks to turn things on. I setup both go-mode and go-ts-mode here, but you may choose whichever. That said, go-mode and go-ts-mode are not equal. There are features provided in go-mode that are not available in the ts version. This lack of parity is a bit unfortunate, and exists for other language modes as well. Usually, the ts modes is much less fully featured.

You can reasonably pick one of the modes in the example below and leave the other one out, or just include both if you aren't sure, no harm done.

On the hooks, you'll see I order them using numbers. This is because I want to make sure flymake is loaded before the call to flymake-show-buffer-diagnostics.

For the before-save-hook I make sure it is first in the list (a negative number makes it earlier in the list), but I also make it buffer local, rather than a global setting. So the call to eglot-format-buffer only gets called on go buffers.

(require 'go-mode)
(require 'eglot)


;; these lines are only needed if you choose to use go-mode
(add-hook 'go-mode-hook 'flymake-mode 8)
(add-hook 'go-mode-hook 'flymake-show-buffer-diagnostics 9)
(add-hook 'go-mode-hook 'eglot-ensure 10)

;; these lines are only needed if you choose to use go-ts-mode.
(add-hook 'go-ts-mode-hook 'flymake-mode 8)
(add-hook 'go-ts-mode-hook 'flymake-show-buffer-diagnostics 9)
(add-hook 'go-ts-mode-hook 'eglot-ensure 10)

;; Optional: install eglot-format-buffer as a save hook.
;; The depth of -10 places this before eglot's willSave notification,
;; so that that notification reports the actual contents that will be saved.
(defun eglot-format-buffer-on-save ()
  (add-hook 'before-save-hook #'eglot-format-buffer -10 t))
(add-hook 'go-mode-hook #'eglot-format-buffer-on-save)
(add-hook 'go-ts-mode-hook #'eglot-format-buffer-on-save)

Setup gopls

Configuring gopls when using eglot happens on the eglot-workspace-configuration which can be set globally in your Emacs configuration or in a .dir-locals.el file in your project. I choose to set it in my Emacs configuration. For more configuration options for gopls see here

(setq-default eglot-workspace-configuration
              '((:gopls .
                        ((staticcheck . t)
                         (matcher . "CaseSensitive")))))

Setup flymake window height

And, finally, as I want the diagnostics window to popup when I open a Go file, I configure the window height to my preference. This sets the window height to 10 lines at the bottom of the frame so I can read the diagnostics as they popup. Recall, I add the function to open the flymake diagnostics window to the hook go-mode and go-ts-mode hooks.

(add-to-list 'display-buffer-alist
             '("^\\*Flymake diagnostics"
               (display-buffer-reuse-window display-buffer-pop-up-window)
               (window-height . 10)))

Updating my configuration

I put all that code in a file located in the custom-modules folder in my user-emacs-directory, which in my case is: $HOME/.emacs.d/custom-modules/my-ide-go.el. Crafted Emacs automatically puts the $HOME/.emacs.d/custom-modules onto the Emacs load-path list, so all that remains is to add the appropriate require in my init.el file:

(require 'my-ide-go)

Now, I can restart Emacs or just evaluate that one line and my Go configuration is installed!

Tags: #emacs

It's been slow… which is a good thing… I think

It's been a surprisingly slow month in Crafted Em