-
Notifications
You must be signed in to change notification settings - Fork 314
Description
Justification
Rubberduck is currently monolithic - everything runs in-process in the host. This raises performance, stability and testability concerns.
Details
Currently, like every other VBE add-in, Rubberduck runs entirely within the process space of the host VBE. Whilst this is convenient for accessing host resources through the VBIDE interface, it does have several drawbacks:
-
We are constrained by the resources available to the host process, primarily RAM. Large projects necessarily mean large parse trees to be held in memory, which can lead to OOM exceptions even on 64-bit systems with plenty of physical memory available. See Holy RAM, batman! (Out of memory errors and excessively high memory usage) #3347.
-
If we crash, we can take down the host or other add-ins. Even if we avoid this, the host can mark us as unreliable and disable us from loading without explicit user intervention at restart. This is not a good look.
-
There is no natural 'seam' between the COM-focused aspects of host interaction at the front-end and the language-focused aspects of lexing, parsing, resolving and anaylsing at the back-end. Facilitating testing of these concerns relies on careful design of abstractions to ensure proper decoupling.
Proposal
The front-end aspects of Rubberduck (interacting with the host through COM interop) should be strictly isolated from the back-end workloads. This is best acheived by running the front-end as a COM add-in (as currently), but commicating with a back-end running in a separate process on the same machine.
This requires inter process communication over a high-performance, low latency mechanism.
Background
Microsoft, during the design of VSCode recognised the need to separate front-end editors from back-end language analysis. Their driver was primarily to avoid the n:n relationship between editors and supported languages, however the solution they devised can be leveraged for our purposes too.
Language Server Protocol (CCA 3.0) is an open standard which describes an astraction between IDEs ("Tools" in their documentation) and language servers. It allows IDEs to support multiple languages without having to include support directly in their native codebase. Instead, a JSON-RPC request is made to an external process requesting information for source code at a given location.
For example, if a user wishes to find implementations of a virtual function:
- The (LSP-aware) IDE makes a request to the language server stating "Give me implementations for source file
Class1.cls
atline 20
character 36
. - The language server uses its knowledge of the source file, coupled with its analysis, to deduce the correct answer and reply to the IDE
- The IDE displays the result to the user in an approriate manner
Typically this occurs over stdio, but the protocol itself is transport-agnostic.
Language servers can be authored in any language, regardless of the client architecture. Many IDEs and editors, including Visual Studio, VSCode, Eclipse, Sublime, Atom and even emacs now offer LSP clients either by default or as options or extensions.
Implementation
Rubberduck could split itself into a client/server system, with both running on the user's machine. The front-end would remain as a VBE COM Add-in, but would limit itself to collecting user events and displaying a UI. The back-end would be a separate process, launched by the add-in on demand, which would load source files (either transmitted from the front-end or exported by it), parse, resolve and anylase them, then cache them to respond to front-end requests.
Fortunately, we would not need to implement the LSP protocol from spec. Middleware packages exist for many languages, including a highly active project for C#: OmniSharp (MIT), which is the mainline C# provider for VSCode and part of the .Net foundation. This is made available as a Nuget package for construction of C# LSP clients and servers.