mylesmmurphy / prettify-ts

Prettify TypeScript: Better Type Previews
https://marketplace.visualstudio.com/items?itemName=MylesMurphy.prettify-ts
MIT License
181 stars 6 forks source link

Consider Becoming a TSServer Plugin #9

Open LukeAbby opened 5 months ago

LukeAbby commented 5 months ago

Please correct me if I'm wrong about the performance impact of ts-morph but my understanding is that the TypeScript project is forced to be essentially recomputed. Based upon how slow it can be it's probably not just the AST either, at least not the AST of just this file.

I looked into the source code and I think you could create a TSServer plugin that decorates the implementation of LanguageService.getQuickInfoAtPosition such that getPrettifyType is called.

I suspect you may have to do some additional work like making sure Prettify_[ulid] and PrettifiedType_[ulid] doesn't show up in completions and maybe you've already considered work like this.

LukeAbby commented 5 months ago

Pros:

Cons:

The cons would both be eliminated if the VSCode extension could transparently load a TSServer plugin without a modification to the tsconfig.json. I don't know if this is at all possible and it's probably not supported. This could maybe be an upstream feature request. Alternatively it could probably be possible to have an extension and a TSServer plugin at the same time.

This has already been useful and honestly I should totally have pursued trying to make something like this. I'm shocked it didn't exist before. I always used Expand<T> by hand when debugging types and even use Expand<T> a lot inside of types to make them display cleaner for end users of library.

That tangent aside, I'd be willing to implement this as a PR and get more concrete information about the pros and cons but I wanted to know if this will even considered as a strategy before just immediately trying it out.

mylesmmurphy commented 5 months ago

Hey @LukeAbby! Thanks for your feedback. I especially appreciate the pros and cons list. :)

You are absolutely correct, the project is essentially being completely re-computed within the extension. In order to speed up type previews, I also had to implement cacheing of the ts-morph projects. This is definitely a memory-hog, but is the only way to provide this functionality as a standalone vs-code extension. Unfortunately, extensions can't interface with the existing TS server.

It is actually on my roadmap to create a Language Service Plugin which does exactly as you describe! Big thanks to @mattiamanzati who has helped guide me greatly on that side of things. That being said, I have yet to get started on it. In the near future, I want to convert this repo into a monorepo that houses both the extension and the plugin (which I plan to name ts-prettify-plugin, thoughts?), and publish to npm.

Once the plugin is created, I want to explore the option of having the existing extension actually use that plugin in the ts-morph project to get the modified quick info, and then just append that to the user's hover preview. I think this could be faster than the existing implementation?

The biggest issue I've ran into with this idea is precisely what you mentioned -- how do we handle configurability? One of the most important parts of this project IMO is the ability to quickly and easily adjust nested type previews. While many types I'd want to view deeply nested properties, some types are monsters spanning across thousands of lines. This definitely could be resolved, like you mentioned, as properties of the tsconfig. I've also considered having a setting that checks for numbers of lines in the preview, and shows the nested types if it's under a specific length. I'd love to hear your thoughts!

If you're wanting to give a shot at a PR for the plugin, I'd love to see what you do and work with you on that.

LukeAbby commented 5 months ago

Unfortunately, extensions can't interface with the TS server.

I don't think that's true!

The hackiest approach would be to find the TS Server process and hijack its stdin or stdout to send and receive JSON. The slightly more advisable way would be to have it open its port (it's a command line argument) and send requests and receive responses in JSON. I was looking into this approach because if you could get it to send enough type information it'd work! Or get it to register a plugin at runtime or so on.

However! While doing this happened to open up the TS Server logs and noticed an interesting detail about the TS Server startup:

[home]/.vscode-server/bin/[...]/node [path]/node_modules/typescript/lib/tsserver.js --serverMode partialSemantic --useInferredProjectPerProjectRoot --disableAutomaticTypingAcquisition [...] --globalPlugins typescript-svelte-plugin,@vsintellicode/typescript-intellicode-plugin,ms-vsintellicode-typescript [...] --locale en --noGetErrOnBackgroundUpdate --validateDefaultNpmLocation --useNodeIpc

There's a LOT of flags, and I cut out a lot of them but what I noticed was the --globalPlugins flag. These all seem to be from VSCode extensions I've enabled over the years. I've already figured out how to add my own and I'm pretty sure they get treated just like a regular plugin but I'll save that for a PR when I make it.

mylesmmurphy commented 5 months ago

I had no idea you could do that -- I'm really excited to see what you come up with. Working with the TS server and compiler is all new to me, so I really appreciate your help with exploring this option. Looking forward to your PR!

mattiamanzati commented 5 months ago

Unfortunately, extensions can't interface with the existing TS server.

Sorry maybe I mis-explained myself, with extensions you CAN interface with the existing TS server, what you can't do is add capabilities to the TS server from the extension side.

For example you can trigger from the extension a request that emulates the "hover" event (https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover), and that would return the same data that appears in the standard typescript hover.

The problem is that the scope of this plugin is to improve the output of the visualized type trough AST and TypeChecker information, so you need to access AST and TypeChecker, and there is no API that gives access to that through the data that runs over the LSP protocol.

So the suggestion was to do the heavy work of getting the type and prettify it on the LSP side, and then you can either override/wrap the existing "hover" call to append your desidered MarkedString with the prettified type.

You can also implement new requests that can be handled on the LSP side (for example getting the type tree for the left side paned of vscode) and trigger them from your extension.

mylesmmurphy commented 5 months ago

Hey @mattiamanzati -- thanks for hopping in and clarifying for me, sorry for my misunderstanding. Again, this is all new to me and I really appreciate both of your help in improving this extension. 😄

mylesmmurphy commented 1 month ago

Prettify TS is currently being completely re-engineered to utilize a custom TSServer plugin, which should include massive performance increases. The pre-release tag includes a release candidate for the extension. Here is the PR with these changes: https://github.com/mylesmmurphy/prettify-ts/pull/25

In order to promote this to production, I need to do further testing.

Pre-release: https://github.com/mylesmmurphy/prettify-ts/releases/tag/v0.1.0-alpha