Skip to content
Closed
Prev Previous commit
Next Next commit
fix(lsp): merge dev refactoring with correct workspaceSymbol paramete…
…r validation
  • Loading branch information
VladD2 committed May 9, 2026
commit cadc0dd07c548067dc25ab070aff956b2699ed62
13 changes: 7 additions & 6 deletions packages/opencode/src/tool/lsp.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Effect, Schema } from "effect"
import * as Tool from "./tool"
import path from "path"
import { LSP } from "../lsp"
import { LSP } from "@/lsp/lsp"
import DESCRIPTION from "./lsp.txt"
import { Instance } from "../project/instance"
import { InstanceState } from "@/effect/instance-state"
import { pathToFileURL } from "url"
import { assertExternalDirectoryEffect } from "./external-directory"
import { AppFileSystem } from "@opencode-ai/core/filesystem"
Expand All @@ -23,8 +23,8 @@ const operations = [
export const Parameters = Schema.Struct({
operation: Schema.Literals(operations).annotate({ description: "The LSP operation to perform" }),
filePath: Schema.optional(Schema.String).annotate({ description: "The absolute or relative path to the file. Required for all operations except workspaceSymbol." }),
line: Schema.optional(Schema.Number).annotate({ description: "The line number (1-based, as shown in editors). Required for: goToDefinition, findReferences, hover, goToImplementation, prepareCallHierarchy, incomingCalls, outgoingCalls." }),
character: Schema.optional(Schema.Number).annotate({ description: "The character offset (1-based, as shown in editors). Required for: goToDefinition, findReferences, hover, goToImplementation, prepareCallHierarchy, incomingCalls, outgoingCalls." }),
line: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(1))).annotate({ description: "The line number (1-based, as shown in editors). Required for: goToDefinition, findReferences, hover, goToImplementation, prepareCallHierarchy, incomingCalls, outgoingCalls." }),
character: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(1))).annotate({ description: "The character offset (1-based, as shown in editors). Required for: goToDefinition, findReferences, hover, goToImplementation, prepareCallHierarchy, incomingCalls, outgoingCalls." }),
query: Schema.optional(Schema.String).annotate({ description: "Search query. Required for workspaceSymbol operation." }),
})

Expand Down Expand Up @@ -57,7 +57,8 @@ export const LspTool = Tool.define(
throw new Error(`filePath is required for operation '${args.operation}'`)
}

const file = path.isAbsolute(args.filePath) ? args.filePath : path.join(Instance.directory, args.filePath)
const instance = yield* InstanceState.context
const file = path.isAbsolute(args.filePath) ? args.filePath : path.join(instance.directory, args.filePath)
yield* assertExternalDirectoryEffect(ctx, file)
const meta =
args.operation === "documentSymbol"
Expand All @@ -79,7 +80,7 @@ export const LspTool = Tool.define(
yield* lsp.touchFile(file, "document")

const uri = pathToFileURL(file).href
const relPath = path.relative(Instance.worktree, file)
const relPath = path.relative(instance.worktree, file)

if (args.operation === "documentSymbol") {
const result: unknown[] = yield* lsp.documentSymbol(uri)
Expand Down
13 changes: 7 additions & 6 deletions packages/opencode/test/tool/lsp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ import path from "path"
import { Agent } from "../../src/agent/agent"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { AppFileSystem } from "@opencode-ai/core/filesystem"
import { LSP } from "../../src/lsp"
import { LSP } from "@/lsp/lsp"
import { Permission } from "../../src/permission"
import { Instance } from "../../src/project/instance"
import { MessageID, SessionID } from "../../src/session/schema"
import { Tool, Truncate } from "../../src/tool"
import { Tool } from "@/tool/tool"
import { Truncate } from "@/tool/truncate"
import { LspTool } from "../../src/tool/lsp"
import { provideTmpdirInstance } from "../fixture/fixture"
import { disposeAllInstances, provideTmpdirInstance } from "../fixture/fixture"
import { testEffect } from "../lib/effect"

afterEach(async () => {
await Instance.disposeAll()
await disposeAllInstances()
})

const ctx = {
Expand Down Expand Up @@ -208,10 +209,10 @@ describe("tool.lsp", () => {
const file = path.join(dir, "test.ts")
yield* put(file)

const err1 = yield* fail(dir, { operation: op, line: 1, character: 0 }, ctx)
const err1 = yield* fail(dir, { operation: op, line: 1, character: 1 }, ctx)
expect(err1.message).toContain("filePath is required")

const err2 = yield* fail(dir, { operation: op, filePath: file, character: 0 }, ctx)
const err2 = yield* fail(dir, { operation: op, filePath: file, character: 1 }, ctx)
expect(err2.message).toContain("line and character are required")

const err3 = yield* fail(dir, { operation: op, filePath: file, line: 1 }, ctx)
Expand Down
Loading