Zed Weekly: #17

August 22nd, 2023

New week, new blogpost. We've decided to rename the blog series to "Zed Weekly" to better reflect the content of the posts.

This week, we mainly worked on AI parts of the editor, channels, improving language server support and performance.


This week has been pretty exciting, as we started implementing a new inline assistant feature that lets you generate code or transform your selections. We're very close on a first version of this feature, which could ship to preview as soon as next week.

An interesting technical challenge was taking the text that GPT streams to us on a token by token basis and incrementally apply it to the selection. In a typical diff, you compare entire strings and produce a "shortest edit script". Here, the right hand-side of the diff is produced incrementally, so we had to roll out our own version of a diff algorithm that is capable of incrementally producing hunks as text from GPT arrives.


Another week focused almost entirely on our Semantic Index. Firstly, we released an internal version of our Search UI this week, which allows the team the opportunity to test out our Semantic engine ourselves. On the engine side, we implemented an Eager/Lazy operation queue, allowing us to offload quick tasks to the background, simplifying our indexing process, and improving throughput. Next week, we are looking to extend this with an embeddings cache, and tokenization optimizations. Ultimately, we have very high expectations for performance, which in the AI space can be particularly challenging. I'm really happy with the progress Piotr and I made this week, and we've got a few more high priority optimization pieces, which'll push us over the line to the point in which we are excited about shipping.

Beyond that, I spent a bit of time with Antonio pairing on our inline assistant feature. I continue to be impressed with how far we've come in the AI space over the last 5 years, and it's an exciting time to be working in this space.


Work on vim emulation is coming along well. The primary change this week is support for "relative_line_numbers": true in your settings file. To make this make sense, we also had to fix a long-standing bug where j, k and related actions worked in screen co-ordinates instead of file co-ordinates. The next major feature we've started on is probably my favorite: . to replay the last action. Emulating it is a fun learning experience in exactly how vim works under the hood: there's a lot of detail to get right, but it will be worth it! Stay tuned for more...


This week I continued working on Semantic Search with Kyle. We are making quite a progress here; other than that, I augmented our language configuration to allow custom word characters to be specified. This was something that we needed for languages such as PHP and Tailwind as - in PHP's case - we did not treat leading '$' as a part of variable name, thus leading us to discarding the suggestions from LSP server. Last but not least, I finally wrapped up project search changes we've kicked off with Julia few weeks prior; the results are now sorted (as they were before) and pop up straight away. End to end latency is still the same, but I have few ideas on how to tackle that. Stay tuned!


Continued focus on a new approach to styling and layout. Taffy is working out well so far. Looking to iterate on the demo below to start building up more complex components and interfaces. You can style with helper methods based on Tailwind CSS classes or set properties directly.

This layout is produced by the code below
This layout is produced by the code below

fn example<V: 'static>() -> impl Element<V> {
    use div::div;
    let palette = RosePinePalette::dawn();


One of our last tasks for Tailwind support has been to make sure the autocomplete behaves as expected with the kebab-case class names. In order to know where an autocomplete should start from, Zed looks around the cursor to recognize word boundaries. Previously this was a hard coded mechanism which was the same for all languages, which Kirill realized causes issues when we think a minus sign is a boundary but the symbol is kebab-case. That would cause us to erroneously cut off the autocomplete query after every dash the user types, so "bg-yellow" would become "yellow" leading to incorrect autocomplete sorting and filtering.

Piotr had discovered the same issue with the dollar sign in our PHP support so he added a new system for configuring this mechanism on a per-language bases. This came in at the perfect time to help us out with Tailwind and all was good, at least until we hit JSX that is.

The problem with JSX is that we need to be able to complete kebab-case Tailwind class names but the outer language syntax needs to continue having the minus sign as a word boundary. Thankfully at the beginning of the year Max and I had built a mechanism for overriding exactly this kind of thing at a per-syntactic-scope basis. This mechanism allows us to have normal word boundaries in normal JS/TS, but when typing a class name we can recognize that as an attribute within a JSX element and allow the dashes to be part of the word and making the autocomplete work as expected!


Last week, we built out a new feature for channels: Channel Notes! Now each channel has a markdown document associated with it that you can use to help manage whatever it is you're using the channels for. For us, we've found them useful for organizing our projects and tracking what people are working on. It's exciting to be building new mechanisms for collaboration from the ground up :D


I spent a good portion of this week in our analytics dashboards, correcting a variety of issues that have come up all at the same time. It is pretty common knowledge that comparing strings containing numerical characters doesn't really work out, but it totally slipped past me as things were working correctly until our versions rolled over from 0.99.0 to 0.100.0 (๐Ÿ˜‚); every chart that was grouping or ordering by versions fell entirely apart. The task of updating this was simple in theory, but it has actualy been a bit of a pain - I'm near the finish line though!

Outside of dashboards, I've been considering how Zed Industries can improve the quality of our support for the core languages we plan to ship. I thought that team members becoming "language advocates" for the languages they use extensively in their daily coding might be good. These advocates would be responsible for flagging issues and pressuring the team to fix them or solving them themselves. I also thought it could be useful to bring community members in to cover languages no one on the team specializes in. I'm not sure if this idea will be realized, since the team is very busy with their scheduled tasks, but theoretically, it makes sense. While I intend to continue treating all issues and community feedback with the same level of importance, I plan to advocate for Python - I have some ideas for PRs I can open to hopefully improve the dev experience.


We have been exploring some further features for channels, including channel notes and chat. I've been working on designs for these things this week, as well as doing some design updates to make channels easier to scan and consume.

Exploring a more distinct Current Call section in channels.
Exploring a more distinct Current Call section in channels.

Beyond that I've been exploring some longer range design & theme ideas that I'll talk about in later weeks!


This week, Mikayla and I continued working on Zed's upcoming channels feature. We've been using it internally for a week now, and we've found our team gravitating towards a pattern of creating a channel per project.

This week, we added a feature that we call "channel notes", where every channel has a markdown buffer that is stored on the server, and can be collaboratively edited just like files in shared projects. We finished an initial implementation of this on Thursday, and we plan to start using these channel notes to track our progress on projects.


I have worked a bit on Tailwind support with Julia and Piotr covering their work, intersecting with this task. We have managed to orchestrate the changes very well, so that I had to add a few lines only on top of others' work to enable good Tailwind completions in CSS and HTML. JSX completions are not good enough still, so there's more work to do there.

Otherwise, I've got drowned into inlay hints entirely: I've managed to implement hint resolve logic, dynamic hints (you can now hover and click them!) and did some optimizations on top of that to query even less hints when possible. It took me a long while to root out the bugs around the hovering and I think current result looks good, but only a prolonged usage can tell now. One of the most rewarding parts of the task was to contribute again into rust-analyzer, hopefully improving even more developers' workflows. With a better client's perspective on inlay hints resolve, it appears that we can shrink rust-analyzer's base inlayHint response quite significantly, sometimes removing over 50% of the previous size.