Language Server Protocol
Daytona provides Language Server Protocol (LSP) support through sandbox instances. This enables code completion, document symbols, and workspace symbol search inside sandboxes.
Workflow
Section titled “Workflow”Follow this order when using LSP in a sandbox:
- Create an LSP server instance with
create_lsp_serverorcreateLspServer - Call
start()to initialize the language server; LSP methods fail until the server is started - Call
didOpen()on a file before requesting completions or document symbols for that file - Use completions, document symbols, or sandbox symbols
- Call
didClose()when you finish with a file - Call
stop()when the LSP server is no longer needed
Create LSP servers
Section titled “Create LSP servers”Create an LSP server instance by providing the language ID and the path to the project.
- Python:
LspLanguageId.PYTHON - TypeScript and JavaScript:
LspLanguageId.TYPESCRIPT - Custom: Install the language server for your target language
from daytona import Daytona, LspLanguageId
# Create Sandboxdaytona = Daytona()sandbox = daytona.create()
# Create LSP server for Pythonlsp_server = sandbox.create_lsp_server( language_id=LspLanguageId.PYTHON, path_to_project="workspace/project")import { Daytona, LspLanguageId } from '@daytona/sdk'
// Create sandboxconst daytona = new Daytona()const sandbox = await daytona.create()
// Create LSP server for TypeScriptconst lspServer = await sandbox.createLspServer( LspLanguageId.TYPESCRIPT, 'workspace/project')require 'daytona'
# Create Sandboxdaytona = Daytona::Daytona.newsandbox = daytona.create
# Create LSP server for Pythonlsp_server = sandbox.create_lsp_server( language_id: Daytona::LspServer::Language::PYTHON, path_to_project: 'workspace/project')// Create sandboxclient, err := daytona.NewClient()if err != nil { log.Fatal(err)}
ctx := context.Background()sandbox, err := client.Create(ctx, nil)if err != nil { log.Fatal(err)}
// Create LSP server for Pythonlsp := sandbox.CreateLspServer(types.LspLanguagePython, "workspace/project")import io.daytona.sdk.Daytona;import io.daytona.sdk.LspServer;import io.daytona.sdk.Sandbox;
public class App { public static void main(String[] args) { try (Daytona daytona = new Daytona()) { Sandbox sandbox = daytona.create(); LspServer lspServer = sandbox.createLspServer( "python", "workspace/project"); } }}Start LSP servers
Section titled “Start LSP servers”Start an LSP server by calling start() before any other LSP operation.
lsp = sandbox.create_lsp_server( language_id=LspLanguageId.PYTHON, path_to_project="workspace/project")lsp.start() # Initialize the server# Now ready for LSP operationsconst lsp = await sandbox.createLspServer( LspLanguageId.TYPESCRIPT, 'workspace/project')await lsp.start() // Initialize the server// Now ready for LSP operationslsp = sandbox.create_lsp_server( language_id: Daytona::LspServer::Language::PYTHON, path_to_project: 'workspace/project')lsp.start # Initialize the server# Now ready for LSP operationslsp := sandbox.CreateLspServer(types.LspLanguagePython, "workspace/project")err := lsp.Start(ctx) // Initialize the serverif err != nil { log.Fatal(err)}// Now ready for LSP operationsLspServer lsp = sandbox.createLspServer("typescript", "workspace/project");lsp.start("typescript", "workspace/project");// Now ready for LSP operationscurl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/lsp/start' \ --request POST \ --header 'Content-Type: application/json' \ --data '{ "languageId": "python", "pathToProject": "workspace/project"}'Stop LSP servers
Section titled “Stop LSP servers”Stop an LSP server by calling stop() when the LSP server is no longer needed.
# When done with LSP featureslsp.stop() # Clean up resources// When done with LSP featuresawait lsp.stop() // Clean up resources# When done with LSP featureslsp.stop # Clean up resources// When done with LSP featureserr := lsp.Stop(ctx) // Clean up resourcesif err != nil { log.Fatal(err)}// When done with LSP featureslsp.stop("typescript", "workspace/project"); // Clean up resourcescurl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/lsp/stop' \ --request POST \ --header 'Content-Type: application/json' \ --data '{ "languageId": "python", "pathToProject": "workspace/project"}'Code completions
Section titled “Code completions”Get code completions for a specific position in a file by providing the file path and position.
- Position values are zero-based (
line: 0is the first line) - Call
didOpen()on the file before requesting completions
completions = lsp_server.completions( path="workspace/project/main.py", position={"line": 10, "character": 15})print(f"Completions: {completions}")const completions = await lspServer.completions('workspace/project/main.ts', { line: 10, character: 15,})console.log('Completions:', completions)completions = lsp_server.completions( path: 'workspace/project/main.py', position: { line: 10, character: 15 })puts "Completions: #{completions}"completions, err := lsp.Completions(ctx, "workspace/project/main.py", types.Position{Line: 10, Character: 15},)if err != nil { log.Fatal(err)}fmt.Printf("Completions: %v\n", completions)var completions = lsp.completions( "typescript", "workspace/project", "workspace/project/Main.java", 10, 15);System.out.println("Completions: " + completions);curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/lsp/completions' \ --request POST \ --header 'Content-Type: application/json' \ --data '{ "context": { "triggerCharacter": "", "triggerKind": 1 }, "languageId": "python", "pathToProject": "workspace/project", "position": { "character": 15, "line": 10 }, "uri": "workspace/project/main.py"}'File notifications
Section titled “File notifications”Daytona provides methods to notify the LSP server when files are opened or closed. This enables completion and symbol tracking for the specified files.
Open file
Section titled “Open file”Notify the language server that a file has been opened for editing by providing the path to the file. The server reads the file contents from disk at open time.
# Notify server that a file is openlsp_server.did_open("workspace/project/main.py")// Notify server that a file is openawait lspServer.didOpen('workspace/project/main.ts')# Notify server that a file is openlsp_server.did_open('workspace/project/main.py')// Notify server that a file is openerr := lsp.DidOpen(ctx, "workspace/project/main.py")if err != nil { log.Fatal(err)}// Notify server that a file is openlsp.didOpen("typescript", "workspace/project", "workspace/project/Main.java");curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/lsp/did-open' \ --request POST \ --header 'Content-Type: application/json' \ --data '{ "languageId": "python", "pathToProject": "workspace/project", "uri": "workspace/project/main.py"}'Close file
Section titled “Close file”Notify the language server that a file has been closed by providing the path to the file. This allows the server to clean up resources associated with that file.
# Notify server that a file is closedlsp_server.did_close("workspace/project/main.py")// Notify server that a file is closedawait lspServer.didClose('workspace/project/main.ts')# Notify server that a file is closedlsp_server.did_close('workspace/project/main.py')// Notify server that a file is closederr := lsp.DidClose(ctx, "workspace/project/main.py")if err != nil { log.Fatal(err)}// Notify server that a file is closedlsp.didClose("typescript", "workspace/project", "workspace/project/Main.java");curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/lsp/did-close' \ --request POST \ --header 'Content-Type: application/json' \ --data '{ "languageId": "python", "pathToProject": "workspace/project", "uri": "workspace/project/main.py"}'Document symbols
Section titled “Document symbols”Retrieve symbols (functions, classes, variables, etc.) from a document by providing the path to the file.
symbols = lsp_server.document_symbols("workspace/project/main.py")for symbol in symbols: print(f"Symbol: {symbol.name}, Kind: {symbol.kind}")const symbols = await lspServer.documentSymbols('workspace/project/main.ts')symbols.forEach((symbol) => { console.log(`Symbol: ${symbol.name}, Kind: ${symbol.kind}`)})symbols = lsp_server.document_symbols('workspace/project/main.py')symbols.each do |symbol| puts "Symbol: #{symbol.name}, Kind: #{symbol.kind}"endsymbols, err := lsp.DocumentSymbols(ctx, "workspace/project/main.py")if err != nil { log.Fatal(err)}for _, symbol := range symbols { fmt.Printf("Symbol: %v\n", symbol)}var symbols = lsp.documentSymbols( "typescript", "workspace/project", "workspace/project/Main.java");for (var symbol : symbols) { System.out.println("Symbol: " + symbol.getName() + ", Kind: " + symbol.getKind());}curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/lsp/document-symbols?languageId=python&pathToProject=workspace/project&uri=workspace/project/main.py'Sandbox symbols
Section titled “Sandbox symbols”Search for symbols across all files in the sandbox by providing the query and the language ID. The Java SDK uses workspaceSymbols() instead of sandboxSymbols().
symbols = lsp_server.sandbox_symbols("MyClass")for symbol in symbols: print(f"Found: {symbol.name} at {symbol.location}")const symbols = await lspServer.sandboxSymbols('MyClass')symbols.forEach((symbol) => { console.log(`Found: ${symbol.name} at ${symbol.location}`)})symbols = lsp_server.sandbox_symbols('MyClass')symbols.each do |symbol| puts "Found: #{symbol.name} at #{symbol.location}"endsymbols, err := lsp.SandboxSymbols(ctx, "MyClass")if err != nil { log.Fatal(err)}for _, symbol := range symbols { fmt.Printf("Found: %v\n", symbol)}var symbols = lsp.workspaceSymbols("MyClass", "typescript", "workspace/project");for (var symbol : symbols) { System.out.println("Found: " + symbol.getName() + " at " + symbol.getLocation());}curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/lsp/workspacesymbols?query=MyClass&languageId=python&pathToProject=workspace/project'