Back to the talks Previous by track: Weightlifting tracking with Emacs on Android Next by track: Zettelkasten for regular Emacs hackers Track: General

corfu+yasnippet: Easier than I thought

Pedro A. Aranda Gutiérrez (he, him) - paaguti@gmail.com

Format: 37-min talk ; Q&A: BigBlueButton conference room Etherpad: https://pad.emacsconf.org/2025-completion
Etherpad: https://pad.emacsconf.org/2025-completion
Status: TO_REVIEW_QA

00:00.000 Introduction 00:52.516 Motivation 03:29.120 My requirements 04:37.600 Basic setup: corfu + eglot 05:02.960 Looking at completion-at-point functions 06:44.880 Making my own (basic) c-a-p-f for yasnippet: the completion properties 08:10.900 Getting yas-kw-list right: What do I want? 09:18.880 Diving in yasnippet 11:33.840 Fine-tuning: adding cape 13:03.804 Automatic snippet expansion 14:05.360 Themes 14:58.320 My check-list 15:48.584 Takeaways 16:58.040 Requests (to whom it may concern) 18:55.000 Q: Did you try yasnippet-capf? If so, what did you miss from it that this approach has? Thanks! https://github.com/elken/yasnippet-capf 19:40.160 A small demo 24:51.880 Q: Do special characters in yasnippets work well too? example <FD ? 32:32.120 Emacs Lisp

Duration: 36:04 minutes

Description

I describe my experience in "getting rid" of company-mode and transitioning to corfu. I now have yasnippets integrated with a couple lines Emacs Lisp. The main advantage is that with I need less packages and configuration, specially on master. The talk will include some code snippets that show it was easier than expectedand a life demo.

20 minutes (with demo)

About the speaker:

After 30 years using Emacs, professionally and for my PhD, I'm currenly quite involved in org-mode, where I'm revamping the LaTeX backend. To do this, I needed a new approach to auto-completion because company-mode started requiring too much code and integration.

Transcript (unedited)

[00:00:00.000] Introduction
Nice to have you here on this talk. This is my second talk this year. First one was on things that I've done to Org Mode. Just as a side note, this presentation that I'm going to share with you about my work on Corfu and Yasnippet. I've prepared that on Org Mode and exported that with a work I've been doing to the latest exporter. Anyhow, what is my talk going to be about? It's going to be about Corfu and Yasnippet, which I thought would be difficult to integrate, and it was much easier than I thought. So just a short outline, my motivation, step-by-step of the things that I've been investigating, and some takeaways.
[00:00:52.516] Motivation
So my motivation, yasnippet is old. So I've tried another. I've tried Tempel and other template management packages, but it was really not my cup of tea. So mainly why? Mainly because I have a nice base of yasnippets I have generated for my needs. So it's not that I've been importing snippets from packages which are out there. No, they are my snippets. I'm used to them. And of course, migration counts as a cost. I've been using Company as my completion point function GUI for years, but with the time coming in features and so on, it was not as easy to set up as I wanted for my needs. So I had been working, I've been playing with Eglot already some time for a language server protocol. I had read about how easy it was to integrate with Corfu, which was really nice, and I had given it a try, but I'm using both graphical user interface mode in Emacs and text mode, and for text mode you need corfu-terminal, which was yet another package that I didn't download. Well, and I didn't really find a quick way to get rid of company to get yasnippet. So at the end, when, at the beginning, when I was using Corfu and Eglot, I also needed to load company to have yasnippet support, which was really like sort of weird because I wanted to get rid of yasnippet. Anyhow, while I try now, I'm following the mailing list, the development mailing list, and I got interested when I heard something about TTY child frames being announced for master. That would mean less packages to download because I thought I could get rid of Corfu, and I wanted to try if I could get rid of corfu-terminal and run Corfu without that. So, there were some hints there in that mailing list that made it interesting for me. And at the end, I'm also like a "why not try"ing man, so I said, let's give it a try.
[00:03:29.120] My requirements
My requirements, I'm working always on a new Emacs, a decently new Emacs, normally from master, vanilla, completely vanilla, and I don't have any extras like Doom or things like that. So I'm only vanilla. And one of the things that I don't want is that on this vanilla recent Emacs, I don't want a corfu-terminal. I need yasnippet. I'm basically an old man. Old dog doesn't do new tricks and snippets must, in my way of working, must be easy and quick to configure, and tempel or others that I've seen are not. I don't want any reminiscence of company in my setup. And of course, don't forget that I've embraced the language protocol implementations and basically Eglot. My main focus now is Python and LaTeX, and I have pylsp and texlab. I don't want to have to stop using them.
[00:04:37.600] Basic setup: corfu + eglot
So basic setup for Corfu and Eglot. You can find it everywhere you look for it. It's really easy. And actually, I also do something somehow naughty, which is to set this variable, the corfu-auto variable to true, although I know it's not recommended, blah, blah, blah. But I use that because I'm a bit lazy in that.
[00:05:02.960] Looking at completion-at-point functions
So next step was looking at completion at point functions. So the information there, if you go through, is scattered and sometimes a bit cryptic. At the end I came up with something like the thing that you see there on the screen. It's a function for yas-completion-at-point. I need my list of keywords, so that I'm going to be talking later, and I have my bounds, which is normally a word, and from that, I get the start and the end of the thing that I want to be my seed for looking and bringing up Corfu, and of course, I need some completion properties here. This looked like this is what you need to do, but I had to dig quite deep to create a yasnippet keyword test and to understand the completion props. And as an update of what I've been doing in the last weeks, I've created a bound of things at point for me with a different thing that doesn't skip over non-blank characters, that only skips over non-blank characters. Why? Because 'word was confusing LaTeX, because the backslash, like in the example for the teletype text, was not taken into account by 'word. So I had to create my own one, which was a bounds-of-thing-at-point, and then my thing is non-blanks.
[00:06:44.880] Making my own (basic) c-a-p-f for yasnippet: the completion properties
Completion at point properties, what are they? They allow Emacs to know how to handle the information for a specific completion time. So you normally will have an annotation, which then can disappear if you use nerd-icons-corfu, which is what I'm doing currently, but I keep it commented just in case I get tired of Corfu and I want to have my completion function. Then the :company-kind is actually not something that comes from the company package, but does not require. And that's going to allow nerd-icons-corfu to identify and put the right icon there in the completion list, as you will see in a couple of minutes. So it's a snippet key. So basically what this is telling you is that this is a snippet keyword. These two lines, either line, tell you that this is a snippet keyword, and that it should be added to the other completions that you already have in your list.
[00:08:10.900] Getting yas-kw-list right: What do I want?
Problems. Looking... Now, next step, once I had the completion props which was relatively easy was to go and get the keyword list right. I've been looking at pre-existing solutions like, for example, how the menu is built in by yasnippet and it looked a bit like Mission Impossible, because the approach by all the things that I have seen and I have examined is to get the keys and the names and then further process them. My take was, do I really need both? At the end, if I use my own snippets, I'm going to be using something I would call meaningful keys for them, or at least meaningful for... These keys are meaningful for me, and I try not to repeat them, because it makes little sense to repeat a keyword. So why not center everything around the keys only, and can that help simplify my code?
[00:09:18.880] Diving in yasnippet
So I started to dive into yasnippet and I found a lot of useful semi-hidden functions there. I discovered that getting the list, the list of keys for a given mode was not that difficult. And at the end, what I started doing is get all the snippet tables used by a major mode and get the lists of the keys that you have in each table. Sometimes the list is empty so it's going to return a nil and that you have to discard. When you're using structured snippets like snippets and submenus and so on to get a structured menu. You also get some non-strings that you need to filter out in order to get a workable keyword list. At the end of the day, what I had was something like this. I have, for a mode, I went through all modes. through all modes associated to that, and then I went, I got my results from all the tables that I had for a given time, for a given table. So what you do is you get the tables that are associated to a mode, because, surprise, surprise, some modes have more than one table. And then what you do is you filter out all non strings from each of the keys list that you have for each table. So as you see, it's a 1, 2, 3, 4, 5, 6, 7 liner, which was not too much. By the way, if someone from Yasnippet is around, I sent a pull request to include this as a public function in Yasnipit because it might be nice to have it in the package in order to do this kind of things.
[00:11:33.840] Fine-tuning: adding cape
So fine-tuning. Just adding a yas completion to the completion point functions was not enough. I don't really know, but :exclusive no didn't seem to work how I wanted so I needed to escape... Sorry. Yeah. I was saying I was getting rid of packages, and I had to add one package in order to get a function, which is very, very, nice, and which is part of the cape function of the cape package, and that's cape-capf-super. So at the end, using that, you define an alias, which, for that, where you use cape-capf-super to have a list of what you want... So in this case, for example, for the demo I'm going to make, I'm using yas completion and then the elisp-completion-at-point function provided by Emacs. I combine them using cape-capf-super, and with that, I create a completion point, a new completion point function which I call cape-lisp-mode, and then I add this alias to the completion functions list, and with that, it is enough.
[00:13:03.804] Automatic snippet expansion
Snippet expansion. If you want to have your snippets expanded automatically, you have to add an exit function to the, I'm sorry, to the completion properties. Yet another functionality you have to add. And to avoid this automatic selection to be too eager, you need to add this setq corfu-on-exact-match to nil because otherwise, you will always get the snippet expanded, even if you don't want it. Basically, why? Basically, because this would be suboptimal because the key can appear as part of a variable name. Another nice thing, I'm also creating my own themes. I'm trying to have very sleek themes that only cover the modes that I use and for that I have my own theme creator fork from the original theme creator. In my personal fork that I'm running at home, I only have the faces for the modes I use. I don't want to overload the thing with too much different things. Looking at this, I really didn't need, as you will see now, I don't need to add anything to my themes, because the default faces for Corfu adapt quite well to most of the themes.
[00:14:58.320] My check-list
So if I go back to my checklist, decently new Emacs, yes, compiled. The one you'll see in the demo I'm doing is a master compiled the day before yesterday and I don't need corfu-terminal there. I need yasnippet, and you're going to see that in a second with a couple of snippets that I can expand here. I don't want any reminiscence of a company in my setup, and there's none. Well, actually, :company-kind is there you see the company there, but it isn't defined by company strictly speaking, and for... I don't want... I need Eglot integration which I will also be showing you.
[00:15:48.584] Takeaways
Takeaways from all this, if you accept the extra burden of corfu-terminal for Emacs 30 or earlier Emacs 30s, it's not too difficult to get this set up running. Corfu was easier to integrate and configure than Company, and it's much lighter in terms of number of lines, et cetera. I learned a lot. Well, actually, yes, with the help of Cape, but it is much lighter and much easier to integrate and configure. I've learned a lot about computational functions in the process, which is, something that is always nice to learn new things and the nerd-icons-corfu makes the... at least, at this point in time... I might get tired of it, but at this point in time, it makes a very nice overall look and feel for Emacs.
[00:16:58.040] Requests (to whom it may concern)
Requests (to whom it may concern): cape has nice features that maybe could make their way into emacs. I'm thinking basically about this super cape functionality which is very nice, and overcomes the problem of linking, and this :exclusive and all this kind of things that we have currently in Corfu with the completion-at-point functions. Corfu is also really nice to have, and it's not too big. So is there any possibility that it makes its way into Emacs? Please keep yasnippet alive. I'm not saying here that my pull request should be there, but it would be nice if someone took a look and made it part of Yasnippet. And P.S., currently on master, there's a lot of semantic highlighting going on, which is very, very nice. No criticism on that. But you may need to add to your snippet hook this simple local value for elisp-fontify-semantically, because at least in my case, I felt that the faces were a bit too pushy, so I had to make the snippet mode use the old Emacs Lisp fontification. This would be my talk. Any initial reactions to this? There's a question here.
[00:18:55.000] Q: Did you try yasnippet-capf? If so, what did you miss from it that this approach has? Thanks! https://github.com/elken/yasnippet-capf
Someone asked, did you try yasnippet-capf? If so, what did you miss from this approach? I tried that. And it's not that I missed anything. It was more or less that I wanted to do it myself. So I wanted to see what was behind it. That's my answer. There are lots of packages there, but I try to keep learning. So, this was a nice objective to learn a bit more about Emacs. And now, just a second.
[00:19:40.160] A small demo
Now, a small demo. This is the interaction. And as you see, I have the snippet there. and I have a couple of snippets. So, for example, if I would like to say I want to define a function, I can go like this. And what you see here is that I have two snippets appearing and then some variables. So, I could go for defun or if I want a key map, for def-keymap, which would be something like this. And then when I press enter, I get directly into the map and I could say like, show off map. Then it sets out directly a :prefix t, which is something that I asked for in Emacs master. So with :prefix t, for those who prefix it true, for those who don't know it, it makes integrating this into keymaps in use-package much easier. So the next thing would be, I would like to write a description, like, for example, a cool show-off keymap, and then my keys are my functions, and that would be it. Of course, you also have like this define function. And of course you can say, that's nice, but you're not showing the integration with Eglot, and you're right. So I'm going just to open up a small program that I'm currently developing in Python. This is a tool to do things in MP3. And here I would have, like, all these things. As you see here in the bottom, server is running, file is local, eglot is active. So I have my eglot stop and then I go down. And I want to add a new argument here. I would go like for it. I would go like add flag. For example, I would add a flag and I would get a new flag to add here. Oops. Of course, this is integrated into Eglot, so I'm getting your information about what I have. I could... I don't have os, so I would need to import here, but I can go up just to see... If I would like to, for example, create a new regular expression, I'm getting this information that you see right now on call. I'm getting that from Eglot. So you see there's the integration with Eglot too in Python. I have Eglot, and as you've seen I also have the... and all these are snippets. Fine. More reactions and questions? Because that would be my show off here. Any questions? Any more questions on the pad? but anyhow I'm going to try, I'm going to try yasnippet too and I'm going to be answering this question more yeah time is good okay fine so I would be done if there's no more reactions... Thank you so much. You're welcome. If you have any other questions, folks, you can always follow up on the pad. That was a great demonstration, and I'm sure lots of people are looking forward to trying it out. Oh, I see some questions coming in now. You may go ahead if you like. Okay, fine. There's someone asking:
[00:24:51.880] Q: Do special characters in yasnippets work well too? example <FD ?
Do special characters in your snippets work well too? what kind of... I don't use special characters in the key name, so in this case everything works quite nicely, and then I'm passing, I'm passing the control to yasnippet, so if there's any problem in yasnippet with special characters, that, I don't know. I don't use that as a key. I'm just using for key names. I normally use a... I only use letters, but that should work. I mean, let's, uh, let's give it a trial. Let's kill here. Yes. I don't want, I don't want to touch this. Ugh. Let's go into this one. Let's say I'm going to define this, for example, like this, and I'm going to create a new snippet. I'm going to create a new snippet and use this. For example, when you look at this, if you have the new way of the new Emacs semantical highlighting working, this would be quite cramped. This is why I'm using, this is why I said the snippet, the... So is this more or less what you're talking about? This is what you're talking about. Snippet. Save the snippet. Snippet, load and put window. Enable interaction mode. Yes, I'm going to save. And I'm going to save that as fd test in the file. No, I'm going to save this. Load. Load input window. I'm going to put this in Emacs Lisp mode. I want to save it. No. I'm going to write that directly into Emacs Lisp mode. Going to go back into scratch buffer, and here I have it. We have it here. But anyhow... And I'm just going to try to see, if I feel like... Empty? Of course, there's only one. It will not show in Corfu. but I mean, I don't have any problems with that, as you see. Was that what you were meaning? Yeah, I guess that works. Fine. All right, shall we wrap up here so that you can have supper and have lunch and other things? Okay, fine for me. I was hoping to see the drop down. Just a second. I think we can do that too. Two seconds. How can we do that with a drop down? Yeah, if I say something like this. And then I go and save it. And I'm going to go and write this into, with a second, fine. And now I need to quit here, sorry. And I'm going to come back in a second with another remark. Well, I'm bringing back and now let's see. Let's see what we have in yasnippet. It's not there. Why not? Just a second. Let's see if I go. I don't know if it matters that the name was that didn't have the characters in the beginning. Just a second. I think I know what is happening here. Do I have them? I'm going to clean. cd - to get back in. Yeah. That's right. So you see how the name also has... it doesn't have the same as the keys. I don't know if that affects what shows up. That's a quick one. This is my... Fine, now that I have this, which is going to be quicker, we check again. They both seem to be the same now and I don't know if that affects, but anyhow, let's try it. I go and then I look at the yasnippet, if it's there. Yes, it is. It doesn't seem to be affecting.
[00:32:32.120] Emacs Lisp
getting more templates set up with yasnippet. I really love the fact that you can evaluate Emacs Lisp in it too. try that because if I go into my, for example, into my org mode stuff and in my org mode, I go to the article, which is one of the big ones. I have things like, for example, I defined a couple of functions here to do if it's empty, if that is empty, just add a white space. If one is empty, add a white space. add a white space here so it becomes a comment. I have functions to do more things on that, and I also have menus to see what language I want to choose for my spell checking and so on. And that's all... As you see, this is Lisp being evaluated. So yes, do. I really encourage you like, especially if they're working in different programming languages, so they can just have the syntax for the different languages be condensed into a consistent abbreviation. This is when I'm writing articles. I have another one. I have another one for writing letters in org mode and so on. So, it's like letter, block, and you have the complete infrastructure and you don't have to type it by hand. So, it's really, really nice. a future Emacs carnival, you know, shared blogging theme thing be around having people share their snippets. this is something stupid. I'm switching my themes. All right. And of course, there you see, I have also, this is also with, this is my way of switching buffers, which is with the shift control and tab, I can switch different families and then when I'm in a family, I can go and switch with control tab between the different, I'm using tab line by the way. I'm not using the other one. I'm using the old plain tab line with my themes. So that's more or less everything. I will work on getting the recordings for the live talks sorted out at some point very soon. I might even be able to get them out next week. So thanks again. All right. Have a nice supper.

Questions or comments? Please e-mail paaguti@gmail.com

Back to the talks Previous by track: Weightlifting tracking with Emacs on Android Next by track: Zettelkasten for regular Emacs hackers Track: General