Skip to content

Instantly share code, notes, and snippets.

@RomneyDa
Created December 7, 2024 11:34
Show Gist options
  • Save RomneyDa/2ba350c32073d30cc85d05dba9ad21f3 to your computer and use it in GitHub Desktop.
Save RomneyDa/2ba350c32073d30cc85d05dba9ad21f3 to your computer and use it in GitHub Desktop.
Continue Messaging Crash Course

Continue Messaging

The Continue Extension can be seen as 3 main entities:

  • GUI - a React App embedded in the IDE, which provides a chat interface, etc.
  • Core - code shared between extensions (and the GUI)
  • IDE - code specifically required to build the IDE extension and and access IDE functionality i.e. VS Code or Jetbrains

Protocols

The GUI, IDE, and Core communicate through messaging protocols, whose message names and data types are defined in Typescript in core/protocol

These protocols are defined specific to each origin/destination combination, i.e., specific message types are defined for e.g. GUI -> Core, Core -> IDE, IDE -> GUI, etc.

Note that Jetbrains (written in Kotlin) does not currently have access to these types, so must be careful with Jetbrains messaging.

Since the GUI is embedded in the IDE and can only directly communicate with the IDE, the idea of "pass through" messaging is implemented to pass certain messages directly from the GUI to the Core and vice versa

Channels

Continue utilizes a variety of messaging channels depending on platform, environment, extension capabilities, etc.

  • TCP - websockets port listeners
  • IPC (Inter-Process Communication) channels
  • In-process communication
  • Browser window message events

All of these messenger implementations utilize some combination of these paradigms for sending and receiving messages:

  • post - sends a message with no expectation for a response
  • send - sends a message with an ID
  • request - sends a message with an ID and then listens (async) for a response with the same ID to resolve
  • respond - responds to a request with same message ID
  • invoke - sends a message to itself to invoke its own listeners
  • on - adds a listener to a specific message type

GUI <-> IDE

  • The GUI and IDE communicate through browser window events/methods
  • Sending Messages
    • Messages are sent to the IDE using an IdeMessenger, which provides request, post, respond, and send functionality
    • This is implemented with an ideMessenger context available to the entire React app, which contains an instance of IdeMessenger
    • It also exposes ideMessenger.ide as syntactic sugar - an instance of MessageIde which provides explicit methods for each method, but simply calls ideMessenger.request("message"),
    • In VS Code's Electron environment, the GUI can post messages to the globally available vscode window - vscode.postMessage
    • In Jetbrains, the GUI posts messages to the Tool Window browser using the injected window.postJetbrainsMessage (see below)
  • Receiving Messages
    • a useWebviewListener hook can be used modularly by any component to listen to and respond to messages received from the IDE. This works by registering a listener to the window that processes a message and then uses ideMessenger.respond to send a response back to the IDE

VS Code <-> GUI & Core

  • Since VSCode uses Typescript and does not need a compiled core binary, it can instantiate Core directly and message it in-process using an InProcessMessenger. Messages to and from core use this, although, occasionally confusingly, the extension can also directly access core methods since it's just an instance of Core
  • VS Code sends and receives messages with the GUI using a VsCodeWebviewProtocol, which implements request, invoke, etc. methods for the vscode.Webview type
  • VsCodeMessenger is what connects everything - it registers listeners and implements pass-through functionality from the InProcessMessenger to the VsCodeWebviewProtocol, and hooks up IDE responses using an instance of VsCodeIde
  • VsCodeExtension houses everything, including the webview provider (ContinueGUIWebviewViewProvider, a vscode.WebviewViewProvider) that houses the webviewProtocol

Jetbrains <-> GUI & Core

  • The Typescript core is compiled to a binary for use in the Kotlin/Java Jetbrains environment
  • This Binary runs an Ipc (or Tcp for testing) server for communication with the extension (see binary/src/index.ts) Jetbrains communication can be understood from the vantage point of the continuePluginService, which is initialized during continue plugin startup activity and registered to the global ServiceManager. The plugin services gives three main communication channels access to eachother
  • CoreMessenger: uses Tcp or Ipc to send and recieve messages with the core. IDE-specific messages are forwarded to the ideProtocol and webview-specific messages are forwarded to the GUI
  • ContinueBrowser: a wrapper around a JCEFBrowser which hosts the GUI and is used to communicate with the GUI.
    • Captures messages from the GUI by injecting a window.postJetbrainsMessage method on initialization, which the GUI specifically calls if on Jetbrains
    • Sends messages to the GUI by executing a javascript script within the JCEFBrowser which invokes window.postMessage with the message contents
    • Forwards "pass through" events directly to the coreMessenger and sends the others to the ideProtocol
    • Initialized in the ContinuePluginToolWindowFactory
  • IdeProtocol: provides responses to IDE-specific messages for the coreMessenger and continueBrowser to use.
  • Since these 3 are set up in parallel there is no need for actual passing through the IdeProtocol
  • The Jetbrains IdeProtocol confusingly has an unused send method that sends to webview 😂
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment