-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Local File Transformations #12042
Comments
This. |
This isn't related to that, what you are talking about is called inlay hints, they are entirely controlled by the language server and they already exist on Zed, there's a button to toggle them on the toolbar (you can also use the command palette, or their shortcut which is either |
Have you ever looked at our zed/crates/editor/src/display_map.rs Lines 1 to 18 in d21598e
Folds, wrapping, tabs, highlighting, inlay hints — they are all modeled as a series of optional transformations on the buffer that change how it's displayed. |
Check for existing issues
Describe the feature
I'll start by saying this is a very ambitious idea that I had for a long time that falls into "how would my hypothetical ideal text editor work" and adds quite a lot of complexity, but at the same time it's a really feature that seems useful and I haven't seem anywhere else, so I wanted to get it out there. This is also turning out to be the most detailed issue I've ever written.
Introduction
The core idea is to let the user install/configure Local File Transformations (LFTs), that modify the file that is displayed and edited compared to how it's stored on disk and seen by development tools.
Currently, the way files are saved and opened looks something like this (on the side of the machine where the files are in the case of remote):
With this feature, it might look like this:
LFT - Local File Transformation
ILFT - Inverse Local File Transformation
Implementation
LFTs are bijections that operate on the document.
To provide a good experience, LFTs should have the following properties:
LFTs increase latency in two ways:
Examples
JSDoc Type Hints
This was the motivating example that give me this idea about a year ago, when Svelte switched to JSDoc. The main drawback of JSDoc are it's verbose and inconvenient syntax, but for certain usecases like libraries, the reduced complexity of saving a build step and having the source be easier to access is very appealing.
LFTs solve this well:
<===>
this is a case where having access to the tree-sitter tree is very beneficial.
Indentation
If you think this example is silly and this is what tabs are for, I totally agree.
In an ideal world everyone would agree on indentation and noone would care about this example, and I think people mostly stopped caring, but for me the most annoying case is when a project uses spaces for indentation, which isn't resizable when rendering like tabs, and I don't agree with the amount of spaces (i.e. it's not 4). It's annoying to code with a indentation you're not used to, and if you change it you might accidentally commit indentation changes.
This is as simple as it can get:
<===>
This can be also be solved with git attributes as noted below.
Json Trailing Commas, Quoteless Keys & Comments
Trailing commas makes a lot sense for editing, they let you easily reorder lines and copy things around with ending up with missing commas and disallowed trailing commas, and might even slightly improve readability. There are formats like
jsonc
andjson5
which allows them, but unfortunately most things only allow strictjson
which forbids trailing commas. Similarly, the requirement of quoting all keys even when unnecessary also makes editing cumbersome and reduces readability.This can also apply to programming languages that don't support trailing commas in function arguments (looking at you lua).
Comments are problematic on a fundamental level - they need to be retained somewhere. Some schemas permit special keys that are ignored when parsing, these can map pretty well, here's an example that allows
_comment
on the root object:<===>
Indentation Based Scope
Let's have python like rust syntax, why not!
(This is inspired by a procedural macro that I saw years ago but I cannot find it for the life of me, I've gone through multiple pages of google results and Claude gaslighting me with a non-existent
piston-indentation
crate)<==>
This is an intentionally hard example, because it's inherently non-local: to know how many tabs to insert, you need to keep track of the current scope level. This turns out not to really be an issue because LFTs are always applied to the whole document, and the inverse LFTs doesn't have the same problem because they only need to see relative change in indentation. An inverse example of this, python with curly brackets:
<==>
does suffer from the issue of non-locally. This is a case where having access to the tree-stter tree can really be beneficial.
Hex Editor
Of course Zed will have a native hex editor at some point, but this is more customizable and might be useful for advanced usecases (e.g. support for niche vim operations), and it's a good example.
This could be done with
xxd
andxxd -r
.Alternative: Git Attributes
I recently read the git documentation on git attributes and they provide a similar feature to LFTs, with the main difference being that the transformations happen between the git repository and the filesystem, instead of between the filesystem and the editor. This makes it much easier to do because the files only to be transformed when interacting with git, which is much less frequently than on every edit. The fundamental limitation with this approach is that your development tools, tree-sitter and lsps still operate on the transformed files meaning you cannot make any syntax incompatible changes, limiting you to only formatting (you might be able to with very niche cases, e.g. load javascript files as typescript and alias
node
tots-node
).Alternative: FUSE Filesystems
This has the advantage of having many existing FUSE filesystems, but it has many downsides: it's linux only (maybe also macOS?), they are relatively hard to write, and while you might be able to get around syntax incompatible changes with development tools by placing the virtual filesystem in a different location and having the development tools use the original files, tree-sitter/lsp won't work.
Additional Idea: Virutal Filesystem
There is another feature that is very adjacent to this, which is virtual filesystems. These are commonly seen on linux with FUSE, and vscode has the FIleSystemProvider api that is cross platform and let's extensions register their own file systems, such as microsoft's remote development extension.
Virtual filesystems generally fall into 2 types:
Vscode's FileSystemProvider api is focused on the former one, with the filesystem provider having no good way to remember what file/folder it's backed by, and paths being provided as URIs that match the protocol you declared.
There is a lot of overlap between LFTs and file/folder backed filesystems, if you allow LFTs to optionally transform in addition to
file -> file
, alsofile/folder
->file/folder
, they work as transparent file/folder backed virtual filesystems.There are many examples of useful virtual filesystems, e.g. directly editing tarred and/or compressed files that would be really useful to add to Zed on their own regardless of this feature. A cool example I thought of was recreating oil.nvim, which is a vim plugin that lets you edit folders like text files to create/delete/rename files in the folder.
Open Questions
text -> text
transformations this doesn't give enough context to work with, so it likely necessitates the modifications to be done at the tree-sitter tree level for this to work. But then how do we map the valid untransformed to the potentially invalid transformed tree? Can we use the tree to approximately map cursor positions for the lsp? This is probably impossible to solve in the general case...The text was updated successfully, but these errors were encountered: