Repository navigation
💬 Determine Extension Architecture #180
Description
Activity
- addedenhancementNew feature or requestNew feature or requestextensionsIssues related to the extension architecture in CodeEditIssues related to the extension architecture in CodeEdit
on Mar 22, 2022 First swift, then js. Swift is the easiest for us to implement.
Reacted by Jeff L., Richard Zak and AhmedElshaerI would prefer using Swift for extensions, but having JavaScript API would be nice to have also, considering that the app may be ported to iPadOS later.
So, I think we need to provide Swift Extension API and prioritize it right now, but also export it to JavaScript.
Reacted by Austin Condiff, Henry Chu and Jeff L.I'd imagine more Javascript devs would be using this than Swift devs because they would use Xcode for Swift development for the most part correct? I think both are important and I agree with our current path.
Reacted by Pavel Kasila, Jason Platts and Federico ZivoloMay I propose Lua as another option? Lua is small, portable, and fast. Actually neovim has a quite mature plugin ecosystem in Lua (see nvim-lua-guide). We can attract those plugin developers if we provide a Lua plugin interface.
That's an interesting option @henryhchchc. I think at this point we are probably best sticking with Swift and/or JavaScript as our primary focus.
In my opinion, considering the large VS Code user base, most extensions developers are going to be looking to develop using JavaScript/TypeScript. Initially it will be important to encourage as many of those developers as possible to contribute and add features that aren't in the core application. I believe extensions are key to making CodeEdit usable for the majority of developers.
Since this is a Mac-centric application, it would be really cool to have Swift as an option, but as @austincondiff mentioned, I don't think that is what most extension developers be looking to use. However, if that what is practical to begin, I'm all for it!
Ultimately, I wish we could design this in a way that lets extension developers use whatever language they feel most comfortable. I don't want to discourage anyone from contributing. But, it probably isn't realistic without providing some form of language conversion. Additional extension language support is something we could maybe revisit further down the road?
Reacted by Henry Chu and B .To the Swift developers, @pkasila, @MarcoCarnevali, @underthestars-zhy, and any others, how could we go about writing this API in Swift and then packaging/compiling/submitting the extensions in an approachable way?
Please correct me if I am wrong, VS Code uses the command line I think. Nova just takes care of everything via the GUI. There is a menu option to "Submit extension" and everything simply happens in the background. Not all developers like working in the command line, so it be nice to provide the ability for developers to build/submit the extensions from the GUI. Maybe we could require advanced builds to be performed from the command line?
So, in my opinion, Swift Extension Architecture can be implemented the way described below.
Swift API
So, let's say we have a
CodeEditExtensionAPIpackage that declares protocols to work with API (the main one isCodeEditAPIProtocol). Both the CodeEdit app and extensions link to this package. Then, the API class, conforming toCodeEditAPIProtocol, is implemented in the CodeEdit app. Then, an object of this class should be passed to the extension, so it can interact with API.Loading extensions
All extensions are loaded using
dlopenat the runtime from dynamic libraries.Each dynamic library should have
createExtensionfunction as follows:@_cdecl("createExtension") public func createExtension() -> UnsafeMutableRawPointer { return Unmanaged.passRetained(HelloWorldExtensionBuilder()).toOpaque() }
The API object is passed to the
ExtensionBuilder.buildfunction.More information about plugin system using dynamic libraries in Swift can be found here
Compiling and packaging extension
Extensions should be compiled as dynamic libraries (
dylibs).The minimal extension's package structure should be as follows:
HelloWorld.ceext | - manifest.json | - plugin.dylibmanifest.jsonstores the manifest of the extensionplugin.dylibis the dynamic library with the extension
Submitting extensions
I think there are 2 ways:
- We simply create a Git repository for developers to submit their extensions using PRs (by providing a manifest and a link to the GitHub Release page or somewhere else as a JSON file): it will be pulled by the CodeEdit app and indexed on the device
- We create a custom catalog service (marketplace) where (and there are 2 options)
- both extensions' manifests and packages are submitted by the developer and stored by us
- only extensions' manifests are stored by us and packages are stored by the developer somewhere else (e.g. GitHub Releases)
Other languages
- If we are speaking about using JavaScriptCore then we can conform this API to be exported to the JavaScriptCore
- If we are speaking about any other language then there should be more or less similar way of exporting API
Reacted by Jason Platts, Henry Chu, Austin Condiff, B . and AhmedElshaerAwesome @pkasila!
Compiling and packaging extension
Extensions should be compiled as dynamic libraries (
dylibs).The minimal extension's package structure should be as follows:
HelloWorld.ceext | - manifest.json | - plugin.dylibmanifest.jsonstores the manifest of the extensionplugin.dylibis the dynamic library with the extension
Are we able to automate and simplify this process from the extension developers perspective through CodeEdit? Maybe a "Package and Submit Extension…" menu item?
Submitting extensions
- both extensions' manifests and packages are submitted by the developer and stored by us
I am in favor of this option if we can make it work. I understand there will be costs involved and it probably depends on community support. I think it's the most reliable choice.
If extension developers provide their own storage, whether it be GitHub or something else, there is the possibility that these services might be down, causing some extensions to work and some other not, which could cause confusion to users. Our storage might go down too, but I would think it would likely be the entire extension library and we would at least have the ability to troubleshoot and/or explain the outage.
We also run the risk of extensions being deleted, but still appearing in the extension library.
- addedclarification neededFurther information is requestedFurther information is requested
on Mar 29, 2022 Are we able to automate and simplify this process from the extension developers perspective through CodeEdit? Maybe a "Package and Submit Extension…" menu item?
I think we can determine whether an extension project is open. Any extension's project can be a Swift Package (with a dynamic library) with
manifest.jsonfile, so if CodeEdit (actually, a specific extension for extension developers) detects that this is an extension's project, then it adds a target for the extension to be published and this target handles packaging and submitting the extension to the store.I am in favor of this option if we can make it work. I understand there will be costs involved and it probably depends on community support. I think it's the most reliable choice.
If extension developers provide their own storage, whether it be GitHub or something else, there is the possibility that these services might be down, causing some extensions to work and some other not, which could cause confusion to users. Our storage might go down too, but I would think it would likely be the entire extension library and we would at least have the ability to troubleshoot and/or explain the outage.
We also run the risk of extensions being deleted, but still appearing in the extension library.I think that I'll try to play around with how API for the extensions store and the store itself can be implemented a little bit. And I'll write about what I think suits our needs better later.
Reacted by Jason PlattsThanks @pkasila. Sounds to me like a solid starting point. I know there will be details to figure out and a lot of trial and error as we go.
29 remaining items
The main issue of Electron apps is not node, it's the rendering, and the huge amount of resources needed by each app. Having a node process run in background of a native app would be totally fine in my opinion.
Reacted by Austin CondiffI think something like that should be possible. I am not sure whether this was mentioned here already, but VSCode also has something called "activation events", where extensions are only activated once they are actually used, e.g. the C++ extension would only become active once a C++ file is opened or a C++ command is used.
The same mechanisms could be used for hot-reloading where an application is deactivated, the source code is replaced and then activated again.
It's the same in Nova too. I mentioned this briefly in issue #76 in the "Extension Entry Point" section. Both VS Code and Nova also include activation information in the extension manifest.
Based on a prior conversation on Discord with @josephschmitt @mattmassicotte @jasonplatts @pkasila @avdept and a few others, with the announcement of ExtensionKit in the latest WWDC which has been written about here, we have arrived at the following conclusion.
We will use ExtensionKit and expose a Swift API with the goal of later exposing a Javascript API later on. This will allow us to move fast and remain true to our overall mission statement which is to create something as native as possible to create a more performant editor. I invite anyone who cares to add to this or to explore specifics around this chosen path to do so. I feel like we can close this issue once all the kinks have been ironed out as long as nobody objects to it as I feel like we have arrived at a conclusion here.
Reacted by Jason Platts, Thiago Holanda, Ryo "Genbuchan" Shigematsu and Alex ViscreanuReacted by Ryo "Genbuchan" ShigematsuI just wanted to leave a quick update in our progress. We've been taking this real slow because we really need to get this right to begin with because it will affect everything we do going forward.
Our idea to execute on this is relatively simple. We will end up exposing a Swift API and JS API in parallel, Swift to start because we will obviously need Swift functions to do anything. Then either as we go or shortly after we will expose a JS API
To get JS and Swift to play nicely we'd have a server/client model. We'd use Bun instead of Node for faster execution especially because it is based on JSCore rather than Chromium's JavaScript v8 engine. JavaScript execution using Bun can reach near-native speeds which is what we are looking for. Bun also supports modern JS and TypeScript out-of-the box.
We would spin up a JavaScript service on Bun on application start that houses all of the users installed extensions. The app and each installed extension would communicate via XPC and/or JSON-RPC. I also found this project that can reconcile React JSX into JSON so that we can offer React components to render out our Swift views.
We need to prove out this theory before we bring any of this to CodeEdit by creating a simple POC. That might look like a simple "hello world" Swift application. We might change the text or enter a name with an input and a button that a Javascript extension tells our Swift application to render, then when clicked, our JS extension will tell our Swift application to execute a specific function with a certain set of arguments in will provide.
Once we get this POC done, we can start putting it into CodeEdit. We can tell it to add a new navigator, inspector, or debugger. We can tell it to add autosuggestions under certain conditions, add snippets, and add commands. Then we build from there.
Before we begin putting a POC together, does anyone have any feedback on this approach?
We also need to figure out the communication protocol. We have mentioned gRPC, JSON-RPC, and XPC. Any preference? Pros and cons?
Reacted by Federico Zivolo and Thiago Holandamatthijseikelenboom commented
on Sep 11, 2022 ContributorMore actionsThis is what I know of the topic, so people, please correct me if I'm wrong.
JSON-RPC (Personally never used it)
Pros:- Human readable
- Relative easy to learn
- Language agnostic
Cons:
- Slower due to text encoding and decoding over the network
- Not type save
gRPC (Tinkered with this)
Pros:- Fast, low latency
- Widely used in micro-service architectures (So it's "battle-tested")
- Type save
- Auto generated code based on .proto files
- Language agnostic
Cons:
- Not human readable, uses binary
- Steep learning curve (In my opinion)
XPC (Personally never used it, actually never heard of it prior)
So, I don't really no the pros and cons of this. But it does seems like very few developers use this. The issue I see with that is that it could take more time develop the system if it's decided that this protocol is used. Also I have no idea how this would interact with the JavaScript API. The thing that it has going for it, is that it is Apple native, but I don't think that matters. So, my conclusion would be that I wouldn't recommend using XPC.gRPC has my personal preference, but on whether to use it would kind of depend on how many people in our community know how it works and/or want to learn how it works. As far as I know, it's mainly used in backend development and I assume most of the CodeEdit community are frontend developers.
I've worked with grpc for last few years and its really good tool to communicate between 2 servers. We don't really need human-readability, since there's nobody in between to read request/response data. Also I can't say it has steep curve. It has some issues if you compile it from sources for specific platform, but if you use prebuilt binaries then IDE should provide code completion for generated code.
I have app that makes you to use UI to compile your proto files into specific language. https://github.com/avdept/Protobuf-GUI-Compiler
matthijseikelenboom commented
on Sep 15, 2022 ContributorMore actionsPerhaps we should discuss this topic in the next meeting because I think we should make a decision on this, because of the high priority.
- locked and limited conversation to collaborators
on Oct 1, 2022
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fields🏁 Complete
Before moving too far forward with the Extension API and marketplace, we need to decide what type of environment will be used to run extensions.
JavaScript Core, https://developer.apple.com/documentation/javascriptcore, is an option. It does not support ESM, but as some have mentioned, there are ways to work around these limitations using build tools.Initially, a Swift extension API is being developed.
However, we ultimately plan to take an approach similar to Raycast that was described by mathieudutour here, #180 (comment). The architecture involves using node and communicating with the CodeEdit application using JSON-RPC. A react-reconciler will allow extensions to create a custom UI with React components.
Thank you to everyone who contributed to the conversation! Please feel free to continue to add feedback and/or suggestions.