WEBVTT captioned by sachac, checked by sachac NOTE Introduction 00:00:00.000 --> 00:00:01.839 Hi, I'm Austin Theriault, 00:00:01.840 --> 00:00:04.159 and this is writing a language server in OCaml 00:00:04.160 --> 00:00:07.639 for Emacs, fun, and profit. 00:00:07.640 --> 00:00:08.919 Real quick, who am I? 00:00:08.920 --> 00:00:10.919 Well, I'm a software engineer at Semgrep. 00:00:10.920 --> 00:00:13.239 I work on our editor integrations, 00:00:13.240 --> 00:00:15.359 and I love working on programming languages, editors, 00:00:15.360 --> 00:00:16.539 and cryptography. NOTE What is Semgrep? 00:00:16.540 --> 00:00:17.799 What is Semgrep? 00:00:17.800 --> 00:00:20.039 We're a small cybersecurity startup 00:00:20.040 --> 00:00:21.919 whose core product is a SaaS tool, 00:00:21.920 --> 00:00:24.759 which is static application security testing. 00:00:24.760 --> 00:00:27.799 You can think of it as like a security linter. 00:00:27.800 --> 00:00:30.119 Normal linters will say, hey, 00:00:30.120 --> 00:00:31.919 you wrote ugly code, fix it. 00:00:31.920 --> 00:00:35.079 We'll say, hey, you wrote a SQL injection, fix that. 00:00:35.080 --> 00:00:36.959 We support 30+ languages, 00:00:36.960 --> 00:00:39.319 and we have lots of customers all using different IDEs. 00:00:39.320 --> 00:00:40.719 Why does that matter? NOTE How do we show security bugs early? 00:00:40.720 --> 00:00:42.779 Well, our goal is to show security bugs 00:00:42.780 --> 00:00:45.239 as early as possible in the development cycle. 00:00:45.240 --> 00:00:48.479 In the industry, we call this shifting left. 00:00:48.480 --> 00:00:52.959 And so how far left can we shift? The editor. 00:00:52.960 --> 00:00:53.619 So that's why it matters 00:00:53.620 --> 00:00:56.079 that our customers have different editors. 00:00:56.080 --> 00:00:58.919 Our goal is to have Semgrep and the editor 00:00:58.920 --> 00:01:01.319 show up like other language tooling. 00:01:01.320 --> 00:01:05.199 And what I mean by that is I wrote some bad OCaml up here, 00:01:05.200 --> 00:01:07.599 and the editor gave me that red squiggly and said, 00:01:07.600 --> 00:01:12.199 fix your OCaml, and we want Semgrep to do something similar. 00:01:12.200 --> 00:01:15.519 And so our goal then is to provide a similar experience 00:01:15.520 --> 00:01:16.919 to normal language checking. 00:01:16.920 --> 00:01:18.999 And then since we're a small startup, 00:01:19.000 --> 00:01:22.079 and there's a ton of different IDEs that our customers use, 00:01:22.080 --> 00:01:24.919 ideally, we don't want to have to rewrite a plugin 00:01:24.920 --> 00:01:27.559 for every single type of editor out there. 00:01:27.560 --> 00:01:29.159 Our other goal is abstract away 00:01:29.160 --> 00:01:32.119 editing and language features for editors to one code base. 00:01:32.120 --> 00:01:33.879 Ideally, we write it once 00:01:33.880 --> 00:01:35.799 and then plug it into all of them. 00:01:35.800 --> 00:01:37.879 So how can we do that, though? NOTE What is the Language Server Protocol? 00:01:37.880 --> 00:01:40.679 Well, in the process of working on this stuff, 00:01:40.680 --> 00:01:42.999 I found out about 00:01:43.000 --> 00:01:44.879 the Language Server Protocol. 00:01:44.880 --> 00:01:47.279 And what's great about the Language Server Protocol is 00:01:47.280 --> 00:01:50.319 it's a specification that defines all the ways 00:01:50.320 --> 00:01:52.679 that these language tools might interact 00:01:52.680 --> 00:01:56.879 with a development tool. And by development tool, 00:01:56.880 --> 00:02:01.599 I mean like VS Code, Sublime, Emacs, any of those. 00:02:01.600 --> 00:02:07.279 And by language tool, I mean something like PyRight, MyPy. 00:02:07.280 --> 00:02:09.319 So what's cool about LSP is that 00:02:09.320 --> 00:02:12.999 you can separate out those tools into language servers 00:02:13.000 --> 00:02:15.519 and the development tools into language clients. 00:02:15.520 --> 00:02:18.079 And because they share this common specification, 00:02:18.080 --> 00:02:20.359 they can now interact without knowing each other. 00:02:20.360 --> 00:02:22.799 So it's this great abstraction that means 00:02:22.800 --> 00:02:25.439 all you have to do is go write one language server 00:02:25.440 --> 00:02:27.439 and you can hook it up to a bunch of language clients 00:02:27.440 --> 00:02:29.039 and it'll just work. NOTE Case study: Rust Analyzer 00:02:29.040 --> 00:02:34.039 So let's do a quick case study on language servers in LSP, 00:02:34.040 --> 00:02:37.239 just so you get an idea of why this is super cool. 00:02:37.240 --> 00:02:40.439 So there's this language server called Rust Analyzer. 00:02:40.440 --> 00:02:42.879 It's a language server for the Rust language. 00:02:42.880 --> 00:02:44.119 If you've ever developed in Rust, 00:02:44.120 --> 00:02:46.959 you'll know that takes a really long time to compile, 00:02:46.960 --> 00:02:50.359 but the compiler gives you fantastic feedback. 00:02:50.360 --> 00:02:52.359 Rust has a lot of advanced language features, 00:02:52.360 --> 00:02:55.439 so that feedback is super important for developing. 00:02:55.440 --> 00:02:58.919 And so Rust Analyzer will give you that feedback instantly. 00:02:58.920 --> 00:03:01.119 Here's a ton of things that it gives you. 00:03:01.120 --> 00:03:05.079 Code completion, fixes, compiler errors, warnings, 00:03:05.080 --> 00:03:08.679 type signatures. Rust has a pretty strong type system. 00:03:08.680 --> 00:03:12.199 It also has this thing called lifetimes. 00:03:12.200 --> 00:03:15.079 A bunch of advanced language features in Rust Analyzer 00:03:15.080 --> 00:03:16.199 helps you manage all that 00:03:16.200 --> 00:03:17.439 and gives you all that info 00:03:17.440 --> 00:03:19.219 without having to wait for it to compile. 00:03:19.220 --> 00:03:21.519 Developing with the Rust Analyzer 00:03:21.520 --> 00:03:24.319 is just orders of magnitude easier 00:03:24.320 --> 00:03:26.519 than just trying to write Rust straight. 00:03:26.520 --> 00:03:30.919 Rust Analyzer, fantastic. They went and they developed it, 00:03:30.920 --> 00:03:33.639 and now you can go use that in Emacs, NeoVim, 00:03:33.640 --> 00:03:35.239 VS Code, wherever. 00:03:35.240 --> 00:03:39.079 So you can develop Rust in a way that's relatively efficient 00:03:39.080 --> 00:03:42.759 without having to give up your favorite editor. NOTE Rust Analyzer in action 00:03:42.760 --> 00:03:44.399 So here's a quick little demo 00:03:44.400 --> 00:03:46.319 of all the cool things it can do. 00:03:46.320 --> 00:03:48.119 So you can see I typed an error. 00:03:48.120 --> 00:03:50.719 It tells me that I wrote an error. 00:03:50.720 --> 00:03:52.519 I used the incorrect lifetime, 00:03:52.520 --> 00:03:54.159 which is some advanced language feature, 00:03:54.160 --> 00:03:55.159 and it'll let me know that. 00:03:55.160 --> 00:03:57.519 I expanded a Rust macro just there, 00:03:57.520 --> 00:03:59.239 which is similar to Lisp macros, 00:03:59.240 --> 00:04:01.359 and then I ran a single unit test, 00:04:01.360 --> 00:04:04.639 and that's really cool because I ran a single unit test 00:04:04.640 --> 00:04:05.439 from my editor. 00:04:05.440 --> 00:04:07.839 I didn't have to go and type any commands or anything. 00:04:07.840 --> 00:04:09.959 It just worked. NOTE Why is this useful? 00:04:09.960 --> 00:04:13.399 So why is this just useful in general for a user? 00:04:13.400 --> 00:04:15.799 Well, you get the same experience across editors. 00:04:15.800 --> 00:04:17.119 Like I was saying, you don't have to give up 00:04:17.120 --> 00:04:18.359 one editor for another 00:04:18.360 --> 00:04:21.719 so you get some sort of cool language feature. 00:04:21.720 --> 00:04:23.559 You can easily set up and use language servers 00:04:23.560 --> 00:04:24.599 made for other editors 00:04:24.600 --> 00:04:27.859 if developers don't support your editor of choice. 00:04:27.860 --> 00:04:31.239 Performance is not dependent on the editor. 00:04:31.240 --> 00:04:35.439 That's fantastic because to do all that Rust stuff, 00:04:35.440 --> 00:04:37.439 it takes a lot of CPU power, 00:04:37.440 --> 00:04:40.499 and so that's going to be slow 00:04:40.500 --> 00:04:43.679 if your editor language is not great, not fast. 00:04:43.680 --> 00:04:47.799 And then bug fixes, updates, all that, 00:04:47.800 --> 00:04:50.119 it all comes out at the same time. 00:04:50.120 --> 00:04:53.399 And then from the developer perspective, well, 00:04:53.400 --> 00:04:55.359 adding new editors is quick and easy. 00:04:55.360 --> 00:04:58.699 For reference, when I wrote the Semgrep language server, 00:04:58.700 --> 00:05:00.519 it took me maybe two or three weeks, 00:05:00.520 --> 00:05:03.999 but then actually going and setting it up for VS Code, 00:05:04.000 --> 00:05:06.439 that took an hour. For Emacs, 30 minutes. 00:05:06.440 --> 00:05:08.359 IntelliJ, maybe another hour. 00:05:08.360 --> 00:05:10.399 So it took me a day to add support 00:05:10.400 --> 00:05:11.879 for three different editors, 00:05:11.880 --> 00:05:14.799 which was I think something like 75% of the market share 00:05:14.800 --> 00:05:16.319 or something crazy like that. 00:05:16.320 --> 00:05:20.179 So very quick. You only need one mental model. 00:05:20.180 --> 00:05:21.079 You don't have to figure out 00:05:21.080 --> 00:05:23.959 all these different extension mental models, 00:05:23.960 --> 00:05:26.519 how those editors work, anything like that. 00:05:26.520 --> 00:05:28.639 And another thing that's cool is 00:05:28.640 --> 00:05:30.399 you only have to write tests for the language server, 00:05:30.400 --> 00:05:31.959 not necessarily for the editor. 00:05:31.960 --> 00:05:33.839 It's great to have just one set of tests 00:05:33.840 --> 00:05:36.219 that you have to pass. NOTE So what about Emacs? 00:05:36.220 --> 00:05:40.159 So why does a language server protocol matter with Emacs? 00:05:40.160 --> 00:05:42.379 Well, like I was saying before, 00:05:42.380 --> 00:05:45.479 Emacs gets the benefit from work put into other editors. 00:05:45.480 --> 00:05:47.759 So we get all this language support, 00:05:47.760 --> 00:05:51.119 and no one actually has to go and write the list for it 00:05:51.120 --> 00:05:53.199 or write those tools specific to Emacs. 00:05:53.200 --> 00:05:54.919 You get the language tooling, 00:05:54.920 --> 00:05:56.759 the CPU-intensive part of the editors. 00:05:56.760 --> 00:05:58.559 It can be written in something else. 00:05:58.560 --> 00:06:01.319 Lisp is fast. It's not that fast. 00:06:01.320 --> 00:06:04.719 Having that speed is fantastic. It's all asynchronous. 00:06:04.720 --> 00:06:06.439 It won't slow down Emacs. 00:06:06.440 --> 00:06:08.919 And then there's this package called `lsp-mode`, 00:06:08.920 --> 00:06:11.359 which is an LSP client commonly included 00:06:11.360 --> 00:06:13.319 in popular Emacs distributions. 00:06:13.320 --> 00:06:15.159 So a lot of people already have that. 00:06:15.160 --> 00:06:18.679 If you're using Emacs 29 or greater, you have `eglot-mode`, 00:06:18.680 --> 00:06:21.679 which is a lighter weight version of `lsp-mode`. 00:06:21.680 --> 00:06:24.239 It's just another LSP client. 00:06:24.240 --> 00:06:26.359 When I wrote the Semgrep language server, 00:06:26.360 --> 00:06:28.319 Emacs 29 hadn't come out yet. 00:06:28.320 --> 00:06:31.479 I'm not going to talk too much about `eglot-mode` 00:06:31.480 --> 00:06:33.299 because I did everything in `lsp-mode`, 00:06:33.300 --> 00:06:37.779 but I would imagine a lot of this stuff is very similar. 00:06:37.780 --> 00:06:40.699 Here's a list of some supported languages. NOTE Technical part - Brief communication overview 00:06:40.700 --> 00:06:42.639 Now let's get into the technical part. 00:06:42.640 --> 00:06:45.039 How does LSP actually work? 00:06:45.040 --> 00:06:47.159 So let's go over how it communicates first. 00:06:47.160 --> 00:06:49.759 It uses JSONRPC, 00:06:49.760 --> 00:06:51.959 which is just kind of like HTTP, 00:06:51.960 --> 00:06:54.619 but instead of sending plain text, you're sending JSON. 00:06:54.620 --> 00:06:56.439 So it's just sending JSON back and forth. 00:06:56.440 --> 00:06:58.539 It's great because it's a way 00:06:58.540 --> 00:06:59.959 for two programs to communicate 00:06:59.960 --> 00:07:02.839 without sharing a common programming language. 00:07:02.840 --> 00:07:04.959 Transport platform agnostic, 00:07:04.960 --> 00:07:07.079 so it could be stdin, stdout, 00:07:07.080 --> 00:07:09.399 sockets, whatever. It's just JSON. 00:07:09.400 --> 00:07:11.139 You can send it over whatever. 00:07:11.140 --> 00:07:12.719 There's two different types of messages, 00:07:12.720 --> 00:07:15.839 a request, which requires a response from the other party, 00:07:15.840 --> 00:07:19.259 and a notification, which does not expect a response. 00:07:19.260 --> 00:07:21.759 So just a quick little example, 00:07:21.760 --> 00:07:23.759 a user might open a document, 00:07:23.760 --> 00:07:28.079 and then it'll send like a text document did open 00:07:28.080 --> 00:07:30.199 and what document it was to the language server, 00:07:30.200 --> 00:07:31.079 and then they'll change it. 00:07:31.080 --> 00:07:35.079 Maybe they edit some code and introduce a syntax error. 00:07:35.080 --> 00:07:37.159 The changes will be sent to the language server, 00:07:37.160 --> 00:07:39.219 and then the language server will publish diagnostics, 00:07:39.220 --> 00:07:41.199 which is those red squigglies 00:07:41.200 --> 00:07:42.559 I was talking about earlier, 00:07:42.560 --> 00:07:45.459 and say, hey, syntax error or whatever here, 00:07:45.460 --> 00:07:46.919 or maybe the user says, 00:07:46.920 --> 00:07:49.159 I want to go to the definition of this function, 00:07:49.160 --> 00:07:51.239 and then the language server will spit back, 00:07:51.240 --> 00:07:53.799 hey, this is where that function lives. 00:07:53.800 --> 00:07:55.399 All very useful, 00:07:55.400 --> 00:07:57.719 and the communication is relatively simple, 00:07:57.720 --> 00:07:58.759 which is great. NOTE Example request 00:07:58.760 --> 00:08:01.239 This is what it looks like, what a request looks like. 00:08:01.240 --> 00:08:03.379 Notifications look somewhat similar. NOTE LSP capabilities 00:08:03.380 --> 00:08:05.879 So now we know how LSP communication works, 00:08:05.880 --> 00:08:09.859 but how does the actual protocol work? 00:08:09.860 --> 00:08:12.399 Well, almost all of the protocol is opt-in, 00:08:12.400 --> 00:08:15.839 meaning you don't have to support the entire specification, 00:08:15.840 --> 00:08:17.399 you can just pick and choose. 00:08:17.400 --> 00:08:19.839 Servers and clients will then communicate 00:08:19.840 --> 00:08:21.679 what part of the protocol they both support, 00:08:21.680 --> 00:08:22.679 so they'll both say, hey, 00:08:22.680 --> 00:08:26.359 we support being notified when a user opens a document, 00:08:26.360 --> 00:08:28.879 or if they're looking for documentation. 00:08:28.880 --> 00:08:33.799 And so then once they agree upon what they'll both support, 00:08:33.800 --> 00:08:35.199 then they'll send that stuff, 00:08:35.200 --> 00:08:38.579 those notifications and requests back and forth. 00:08:38.580 --> 00:08:41.319 Things like opening and closing files, diagnostics, 00:08:41.320 --> 00:08:46.039 code completion, hovering over stuff, type signatures, 00:08:46.040 --> 00:08:48.559 all of that. And what's cool is 00:08:48.560 --> 00:08:50.239 even though the specification is huge 00:08:50.240 --> 00:08:52.039 and probably has everything you need, 00:08:52.040 --> 00:08:54.479 you can go ahead and add custom capabilities 00:08:54.480 --> 00:08:55.519 if you really want to. 00:08:55.520 --> 00:08:57.979 So you can just define a custom method, 00:08:57.980 --> 00:09:01.359 and then now that works for you, 00:09:01.360 --> 00:09:03.519 and now you can have that in all your editors. 00:09:03.520 --> 00:09:04.559 For example, Rust Analyzer 00:09:04.560 --> 00:09:06.199 has structural search and replace, 00:09:06.200 --> 00:09:08.159 which is like find and replace, 00:09:08.160 --> 00:09:11.599 but with respect to the structure of the code. 00:09:11.600 --> 00:09:13.639 And if you choose to go down this route 00:09:13.640 --> 00:09:15.159 with the custom capabilities, 00:09:15.160 --> 00:09:16.659 you do have to remember you're going to have to 00:09:16.660 --> 00:09:18.699 implement it in every client. 00:09:18.700 --> 00:09:20.399 And that's a little bit more work, 00:09:20.400 --> 00:09:23.379 but it's better than where we were without LSP. NOTE Tips on writing a LS 00:09:23.380 --> 00:09:25.439 So some quick tips on writing a language server. 00:09:25.440 --> 00:09:27.479 I'm not going to get too into this 00:09:27.480 --> 00:09:30.799 because it's very application-specific. 00:09:30.800 --> 00:09:32.759 I wrote Semgrep's in OCaml 00:09:32.760 --> 00:09:35.119 since our code base was almost all OCaml already, 00:09:35.120 --> 00:09:36.599 and I wanted to leverage that. 00:09:36.600 --> 00:09:38.039 Would not recommend 00:09:38.040 --> 00:09:41.559 unless you also have a code base all in OCaml. 00:09:41.560 --> 00:09:43.639 Structure is similar to a Rust server, 00:09:43.640 --> 00:09:45.739 so a bunch of independent endpoints. 00:09:45.740 --> 00:09:48.639 I would do everything functionally if I were you. 00:09:48.640 --> 00:09:49.919 This is EmacsConf. 00:09:49.920 --> 00:09:53.399 We're all hopefully used to writing functional Lisp. 00:09:53.400 --> 00:09:56.239 I would recommend TypeScript or Rust, though, 00:09:56.240 --> 00:09:58.319 depending on your level of performance 00:09:58.320 --> 00:10:00.839 that you really need or whatever language 00:10:00.840 --> 00:10:02.254 you're trying to support ideally. 00:10:02.255 --> 00:10:03.399 Most languages have 00:10:03.400 --> 00:10:06.499 some sort of language server protocol already. 00:10:06.500 --> 00:10:09.199 But if they don't, then it might be easier 00:10:09.200 --> 00:10:10.159 to do it in that language. 00:10:10.160 --> 00:10:12.799 TypeScript has a lot of support, a lot of documentation, 00:10:12.800 --> 00:10:14.159 a lot of examples out there 00:10:14.160 --> 00:10:17.679 because it was what Microsoft originally intended 00:10:17.680 --> 00:10:20.919 the language server protocol to be for, for VS Code, 00:10:20.920 --> 00:10:22.079 which is written in TypeScript. 00:10:22.080 --> 00:10:24.439 Rust is fast, it's going to take more effort, 00:10:24.440 --> 00:10:28.519 but it's very fast, and Rust Analyzer has a great library 00:10:28.520 --> 00:10:30.279 that they use and that they support. 00:10:30.280 --> 00:10:32.799 So support there, examples there are great. 00:10:32.800 --> 00:10:35.839 The hard part is not really the language server protocol, 00:10:35.840 --> 00:10:38.999 but the actual logic. So, like, if you're doing, like, 00:10:39.000 --> 00:10:40.199 language tooling, you're going to have to do 00:10:40.200 --> 00:10:42.679 analysis on the code, so you need to do parsing, 00:10:42.680 --> 00:10:46.999 possibly compiling, all these different advanced features, 00:10:47.000 --> 00:10:48.959 all these advanced different things. 00:10:48.960 --> 00:10:52.519 For example, Rust Analyzer will do incremental compilation, 00:10:52.520 --> 00:10:54.319 which is really, really cool, 00:10:54.320 --> 00:10:58.119 but that's, like, a whole separate talk. 00:10:58.120 --> 00:11:00.319 If you're adapting an existing language tool, 00:11:00.320 --> 00:11:01.679 this stuff is really easy. 00:11:01.680 --> 00:11:03.479 You're basically just wiring stuff up. NOTE Supporting a LS through LSP mode in Emacs 00:11:03.480 --> 00:11:08.359 But, yeah. So, now we know all about 00:11:08.360 --> 00:11:10.799 LSP and language servers. 00:11:10.800 --> 00:11:11.879 Say you want to actually 00:11:11.880 --> 00:11:14.079 add support for a language server in Emacs. 00:11:14.080 --> 00:11:19.159 How do you do that? Well, let's look at LSP mode, 00:11:19.160 --> 00:11:21.519 because, like I said, this is what I'm most familiar with. 00:11:21.520 --> 00:11:24.259 I'm sure `eglot-mode` is pretty similar. 00:11:24.260 --> 00:11:27.479 So, `lsp-mode`'s repository is on GitHub, 00:11:27.480 --> 00:11:31.499 like everything, and it has a ton of different clients 00:11:31.500 --> 00:11:34.439 for a ton of different languages and frameworks and tools, 00:11:34.440 --> 00:11:37.039 like Semgrep, and these are available 00:11:37.040 --> 00:11:39.739 to anyone who installs LSP mode. 00:11:39.740 --> 00:11:42.239 Alternatively, you can make a separate package 00:11:42.240 --> 00:11:43.679 and just use LSP mode as a library, 00:11:43.680 --> 00:11:45.479 but I'm not going to focus on this, 00:11:45.480 --> 00:11:47.879 because there's already a ton of resources out there 00:11:47.880 --> 00:11:50.799 on packaging and Emacs. 00:11:50.800 --> 00:11:54.559 So, our steps, very quickly, are going to look like 00:11:54.560 --> 00:11:58.299 adding an Emacs Lisp file that contains some logic, 00:11:58.300 --> 00:12:01.319 add an entry somewhere, so we added a new client 00:12:01.320 --> 00:12:03.719 to the list of clients, and then do some documentation, 00:12:03.720 --> 00:12:05.999 because documentation's great. NOTE Create a client 00:12:06.000 --> 00:12:07.639 First, creating a client. 00:12:07.640 --> 00:12:09.639 In the `clients/` folder in `lsp-mode/`, 00:12:09.640 --> 00:12:12.919 literally just add, like, `lsp-` whatever it is, 00:12:12.920 --> 00:12:15.759 `require` the library, and register a client. 00:12:15.760 --> 00:12:18.039 Registering a client just means, like, 00:12:18.040 --> 00:12:19.559 saying what kind of connection it is. 00:12:19.560 --> 00:12:21.479 It's most likely going to be standard I/O, 00:12:21.480 --> 00:12:24.359 because that's pretty easy to implement, 00:12:24.360 --> 00:12:26.839 and then you just pass it the executable 00:12:26.840 --> 00:12:29.559 that you actually want to run. 00:12:29.560 --> 00:12:31.719 Say what the activation function is, 00:12:31.720 --> 00:12:33.319 so this is when the client should start, 00:12:33.320 --> 00:12:36.239 so you can specify the language 00:12:36.240 --> 00:12:38.279 or the major mode or whatever, 00:12:38.760 --> 00:12:43.099 and now your client will start whenever that's triggered, 00:12:43.100 --> 00:12:45.639 and then finally provide just a server ID, 00:12:45.640 --> 00:12:48.579 so that way it's easy to keep track of, 00:12:48.580 --> 00:12:52.759 and then run this LSP consistency check function. 00:12:52.760 --> 00:12:56.579 This just makes sure everything up there is good. 00:12:56.580 --> 00:12:59.519 You can do more advanced stuff with making an LSP client 00:12:59.520 --> 00:13:01.199 that I'm not going to get into, 00:13:01.200 --> 00:13:03.799 but just know that these aren't your only options, 00:13:03.800 --> 00:13:07.299 and then finally provide your client. NOTE Add to list of client packages 00:13:07.300 --> 00:13:09.799 Next, you just have to add your client 00:13:09.800 --> 00:13:12.159 to the list of clients that `lsp-mode` supports, 00:13:12.160 --> 00:13:15.639 and now you've added support for a whole new language, 00:13:15.640 --> 00:13:17.719 whole new framework, whole new tool to Emacs, 00:13:17.720 --> 00:13:20.219 and it's taking you, what, like, what is that, 00:13:20.220 --> 00:13:23.639 20 lines of Lisp? No, not even, like, 15. 00:13:23.640 --> 00:13:26.639 15 lines of Lisp, whole new language for Emacs. 00:13:26.640 --> 00:13:31.599 It's really exciting. Now that you have your client, 00:13:31.600 --> 00:13:35.119 let's do some documentation. Go fill out this, like, name, 00:13:35.120 --> 00:13:37.919 where the repository, the source code is, 00:13:37.920 --> 00:13:39.599 because free software is great, 00:13:39.600 --> 00:13:42.179 and you should open source your stuff. 00:13:42.180 --> 00:13:44.199 Specify the installation command. 00:13:44.200 --> 00:13:45.399 What's cool about this is 00:13:45.400 --> 00:13:48.059 this can be run automatically from Emacs, 00:13:48.060 --> 00:13:50.319 so if it's, like, `pip install pyright`, right, 00:13:50.320 --> 00:13:53.399 you can put that there, and Emacs will ask you, 00:13:53.400 --> 00:13:55.279 do you want to install the language server, 00:13:55.280 --> 00:13:56.199 and you can hit yes 00:13:56.200 --> 00:13:59.539 and users will just have it installed for them, 00:13:59.540 --> 00:14:01.879 and then you can say whether or not it's a debugger. 00:14:01.880 --> 00:14:03.159 This is completely separate, 00:14:03.160 --> 00:14:05.119 so there's this thing called DAP, 00:14:05.120 --> 00:14:07.319 which is the debugger adapter protocol, 00:14:07.320 --> 00:14:09.679 and it's similar to LSP but for debuggers, 00:14:09.680 --> 00:14:11.679 which is very cool, NOTE Add documentation! 00:14:11.680 --> 00:14:14.599 and then finally link to your documentation. 00:14:14.600 --> 00:14:17.879 Please, please document your stuff. NOTE Adding commands and custom capabilities 00:14:17.880 --> 00:14:20.479 If you want to add, like, a custom Emacs function 00:14:20.480 --> 00:14:22.679 or custom capabilities, it's super easy. 00:14:22.680 --> 00:14:27.639 It's literally just, like, calling a normal Emacs function. 00:14:27.640 --> 00:14:30.559 For example, Semgrep normally only scans files 00:14:30.560 --> 00:14:34.199 when you open them, but we added a Emacs function 00:14:34.200 --> 00:14:36.719 that will scan your entire project, right, 00:14:36.720 --> 00:14:40.959 and so that was just a client notification. 00:14:40.960 --> 00:14:44.119 It was just `lsp-notify` and then a custom method, 00:14:44.120 --> 00:14:46.719 and it's great because now you can just scan your project 00:14:46.720 --> 00:14:48.719 from a simple Emacs function. 00:14:48.720 --> 00:14:52.119 Requests, very similar to notifications. 00:14:52.120 --> 00:14:56.079 You send it and then pass it a lambda 00:14:56.080 --> 00:14:58.459 and do something with the result, 00:14:58.460 --> 00:15:01.359 and so that's adding custom capabilities. NOTE Thanks for listening 00:15:01.360 --> 00:15:04.319 That's pretty much it. Thank you for listening. 00:15:04.320 --> 00:15:05.639 Some resources here. 00:15:05.640 --> 00:15:08.239 These links are clickable if you get the PDF, 00:15:08.240 --> 00:15:10.919 if you get the slides. Semgrep: we're hiring! 00:15:10.920 --> 00:15:12.119 If you want to work on, like, 00:15:12.120 --> 00:15:13.719 programming language theory stuff, 00:15:13.720 --> 00:15:18.119 compilers, parsers, editors, 00:15:18.120 --> 00:15:22.119 email me or go look at our jobs. 00:15:22.120 --> 00:15:25.119 The LSP specification, this is, like, the holy Bible. 00:15:25.120 --> 00:15:28.339 It has all the specs, all the types, everything. 00:15:28.340 --> 00:15:30.419 `lsp-mode` and the docs. 00:15:30.420 --> 00:15:33.279 `lsp-mode`, right, that's where you want to add your client. 00:15:33.280 --> 00:15:36.099 The docs are great, super useful. 00:15:36.100 --> 00:15:38.079 Rust Analyzer is just a great reference 00:15:38.080 --> 00:15:39.919 for language servers in general 00:15:39.920 --> 00:15:42.119 if you want to write one or if you just want to, like, 00:15:42.120 --> 00:15:45.399 see how they work. It's all just really well done. 00:15:45.400 --> 00:15:47.039 It's great code, very readable. 00:15:47.040 --> 00:15:50.479 And then down here is just a long video tutorial, 00:15:50.480 --> 00:15:54.699 a longer video tutorial, not by me, 00:15:54.700 --> 00:15:58.439 by someone else, on how to add a language client to Emacs, 00:15:58.440 --> 00:16:00.679 but hopefully this is sufficient for y'all, 00:16:01.480 --> 00:16:03.920 and now it's time for some Q&A.