Packages

Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.

Current section

Files

Jump to
raxol lib raxol extensions vscode_backend.ex
Raw

lib/raxol/extensions/vscode_backend.ex

defmodule Raxol.Extensions.VSCodeBackend do
@moduledoc """
Backend handler for the VS Code extension.
This module provides the Elixir-side implementation for communicating
with the VS Code extension, handling requests for AI features, performance
analysis, and component management.
"""
use Raxol.Core.Behaviours.BaseManager
alias Raxol.Core.Runtime.Log
# AI modules removed - AI features disabled
@json_start_marker "RAXOL-JSON-BEGIN"
@json_end_marker "RAXOL-JSON-END"
defmodule State do
@moduledoc false
defstruct [:mode, :extension_pid, :active_requests, :capabilities]
def new do
%__MODULE__{
mode: System.get_env("RAXOL_MODE") || "standalone",
extension_pid: nil,
active_requests: %{},
capabilities:
MapSet.new([
# AI features disabled - removed:
# :ai_content_generation,
# :code_completion,
# :optimization_suggestions
:performance_analysis,
:component_analysis
])
}
end
end
# def start_link(opts \\ []) do
# GenServer.start_link(__MODULE__, opts, name: __MODULE__)
# end
@impl true
def init_manager(_opts) do
state = State.new()
initialize_mode(state.mode, state.capabilities)
{:ok, state}
end
defp initialize_mode("vscode_ext", capabilities) do
Log.info("Starting in VS Code extension mode")
# Start listening for stdin messages from the extension
_ = Task.start_link(&listen_for_messages/0)
_ = send_capabilities(capabilities)
end
defp initialize_mode(_mode, _capabilities), do: :ok
@impl true
def handle_manager_cast({:handle_message, message}, state) do
# handle_extension_message always returns {:ok, state}
{:ok, new_state} = handle_extension_message(message, state)
{:noreply, new_state}
end
@impl true
def handle_manager_cast({:send_response, request_id, response}, state) do
send_json_message(%{
type: "response",
request_id: request_id,
payload: response
})
# Remove from active requests
active_requests = Map.delete(state.active_requests, request_id)
{:noreply, %{state | active_requests: active_requests}}
end
# Public API
@doc """
Handles a code completion request from the VS Code extension.
"""
def handle_completion_request(_input, context) do
# AI features disabled - ContentGeneration module removed
{:ok,
%{suggestions: [], context: context, message: "AI completion disabled"}}
end
@doc """
Handles a performance analysis request from the VS Code extension.
"""
def handle_performance_analysis(_code, component_name, _metrics \\ %{}) do
# AI features disabled - PerformanceOptimization module removed
{:ok,
%{
component: component_name,
analysis: %{message: "AI performance analysis disabled"},
suggestions: []
}}
end
@doc """
Handles a component analysis request from the VS Code extension.
"""
def handle_component_analysis(file_path) do
with {:ok, content} <- File.read(file_path),
{:ok, ast} <- Code.string_to_quoted(content) do
analysis = %{
module_name: extract_module_name(ast),
functions: extract_functions(ast),
dependencies: extract_dependencies(ast),
complexity_score: calculate_complexity(ast),
suggestions: generate_component_suggestions(ast)
}
{:ok, analysis}
else
{:error, reason} ->
{:error, "Failed to analyze component: #{inspect(reason)}"}
end
end
@doc """
Lists available components in the project.
"""
def list_components do
case Raxol.Core.ErrorHandling.safe_call(fn ->
# Find all .ex files that look like components
Path.wildcard("lib/**/*.ex")
|> Enum.filter(&component_file?/1)
|> Enum.map(&analyze_component_file/1)
|> Enum.filter(& &1)
end) do
{:ok, components} ->
{:ok, components}
{:error, error} ->
{:error, "Failed to list components: #{inspect(error)}"}
end
end
# Private functions
defp listen_for_messages do
case IO.read(:stdio, :line) do
:eof ->
Log.info("EOF received, stopping message listener")
:ok
{:error, reason} ->
Log.error(
"[VSCodeBackend] Error reading from stdin: #{inspect(reason)}"
)
:error
data ->
case Jason.decode(String.trim(data)) do
{:ok, message} ->
GenServer.cast(__MODULE__, {:handle_message, message})
{:error, _} ->
Log.warning(
"[VSCodeBackend] Invalid JSON received: #{inspect(data)}"
)
end
listen_for_messages()
end
end
defp handle_extension_message(
%{"type" => "request", "id" => request_id} = message,
state
) do
# Store the request
active_requests = Map.put(state.active_requests, request_id, message)
new_state = %{state | active_requests: active_requests}
# Process the request asynchronously
_ =
Task.start(fn ->
response = process_request(message)
GenServer.cast(__MODULE__, {:send_response, request_id, response})
end)
{:ok, new_state}
end
defp handle_extension_message(%{"type" => "shutdown"}, state) do
Log.info("Shutdown requested by extension")
System.stop()
{:ok, state}
end
defp handle_extension_message(message, state) do
Log.info("Received message: #{inspect(message)}")
{:ok, state}
end
defp process_request(%{"action" => "complete", "payload" => payload}) do
input = Map.get(payload, "input", "")
context = Map.get(payload, "context", %{})
{:ok, result} = handle_completion_request(input, context)
%{status: "success", data: result}
end
defp process_request(%{
"action" => "analyze_performance",
"payload" => payload
}) do
code = Map.get(payload, "code", "")
component_name = Map.get(payload, "component_name", "unknown")
metrics = Map.get(payload, "metrics", %{})
{:ok, result} = handle_performance_analysis(code, component_name, metrics)
%{status: "success", data: result}
end
defp process_request(%{"action" => "analyze_component", "payload" => payload}) do
file_path = Map.get(payload, "file_path", "")
case handle_component_analysis(file_path) do
{:ok, result} -> %{status: "success", data: result}
{:error, reason} -> %{status: "error", error: inspect(reason)}
end
end
defp process_request(%{"action" => "list_components"}) do
case list_components() do
{:ok, result} -> %{status: "success", data: result}
{:error, reason} -> %{status: "error", error: inspect(reason)}
end
end
defp process_request(%{"action" => action}) do
%{status: "error", error: "Unknown action: #{action}"}
end
defp send_capabilities(capabilities) do
send_json_message(%{
type: "capabilities",
payload: %{
capabilities: MapSet.to_list(capabilities),
ai_provider: Application.get_env(:raxol, :ai_provider, :mock),
version: Application.spec(:raxol, :vsn)
}
})
end
defp send_json_message(message) do
json = Jason.encode!(message)
Log.info("#{@json_start_marker}#{json}#{@json_end_marker}")
end
defp component_file?(file_path) do
content = File.read!(file_path)
String.contains?(content, [
"defmodule",
"@behaviour",
"use Raxol.Core.Behaviours.BaseManager"
]) and
not String.contains?(file_path, ["test/", "_test.exs"])
end
defp analyze_component_file(file_path) do
case Raxol.Core.ErrorHandling.safe_call(fn ->
content = File.read!(file_path)
{:ok, ast} = Code.string_to_quoted(content)
%{
file_path: file_path,
module_name: extract_module_name(ast),
type: determine_component_type(ast),
functions: length(extract_functions(ast)),
complexity: calculate_complexity(ast)
}
end) do
{:ok, result} -> result
{:error, _} -> nil
end
end
defp extract_module_name(ast) do
case ast do
{:defmodule, _, [{:__aliases__, _, module_parts}, _]} ->
Enum.join(module_parts, ".")
_ ->
"Unknown"
end
end
defp extract_functions(ast) do
{functions, _} =
Macro.prewalk(ast, [], fn
{:def, _, [{name, _, _} | _]}, acc -> {ast, [name | acc]}
{:defp, _, [{name, _, _} | _]}, acc -> {ast, [name | acc]}
node, acc -> {node, acc}
end)
Enum.uniq(functions)
end
defp extract_dependencies(ast) do
{deps, _} =
Macro.prewalk(ast, [], fn
{:alias, _, [{:__aliases__, _, module_parts}]}, acc ->
{ast, [Enum.join(module_parts, ".") | acc]}
{:import, _, [{:__aliases__, _, module_parts}]}, acc ->
{ast, [Enum.join(module_parts, ".") | acc]}
node, acc ->
{node, acc}
end)
Enum.uniq(deps)
end
defp calculate_complexity(ast) do
{_, complexity} =
Macro.prewalk(ast, 0, fn
{:if, _, _}, acc -> {ast, acc + 1}
{:case, _, _}, acc -> {ast, acc + 2}
{:cond, _, _}, acc -> {ast, acc + 2}
{:with, _, _}, acc -> {ast, acc + 1}
{:try, _, _}, acc -> {ast, acc + 2}
node, acc -> {node, acc}
end)
complexity
end
# Helper functions for pattern matching refactoring
defp determine_component_type(ast) do
content = Macro.to_string(ast)
case {String.contains?(content, "GenServer"),
String.contains?(content, "@behaviour"),
String.contains?(content, "Supervisor"),
String.contains?(content, "Application")} do
{true, _, _, _} -> :genserver
{false, true, _, _} -> :behaviour
{false, false, true, _} -> :supervisor
{false, false, false, true} -> :application
{false, false, false, false} -> :module
end
end
defp generate_component_suggestions(ast) do
complexity = calculate_complexity(ast)
functions = extract_functions(ast)
suggestions =
[]
|> add_complexity_suggestion(complexity)
|> add_size_suggestion(functions)
|> finalize_suggestions()
suggestions
end
defp add_complexity_suggestion(suggestions, complexity)
when complexity > 10 do
["Consider breaking down complex functions" | suggestions]
end
defp add_complexity_suggestion(suggestions, _complexity), do: suggestions
defp add_size_suggestion(suggestions, functions)
when length(functions) > 20 do
["Large module - consider splitting into smaller modules" | suggestions]
end
defp add_size_suggestion(suggestions, _functions), do: suggestions
defp finalize_suggestions([]), do: ["Component looks well-structured"]
defp finalize_suggestions(suggestions), do: suggestions
end