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.