mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-15 09:02:35 +00:00
refactor(lsp): move ty flag to runtime flags (#27610)
This commit is contained in:
@@ -56,7 +56,6 @@ export const Flag = {
|
||||
copy === undefined ? process.platform === "win32" : truthy("OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT"),
|
||||
OPENCODE_ENABLE_EXA: truthy("OPENCODE_ENABLE_EXA") || OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_EXA"),
|
||||
OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX: number("OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX"),
|
||||
OPENCODE_EXPERIMENTAL_LSP_TY: truthy("OPENCODE_EXPERIMENTAL_LSP_TY"),
|
||||
OPENCODE_EXPERIMENTAL_LSP_TOOL: OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_LSP_TOOL"),
|
||||
OPENCODE_EXPERIMENTAL_PLAN_MODE: OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_PLAN_MODE"),
|
||||
OPENCODE_EXPERIMENTAL_SCOUT: OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_SCOUT"),
|
||||
|
||||
@@ -31,6 +31,7 @@ export class Service extends ConfigService.Service<Service>()("@opencode/Runtime
|
||||
enableQuestionTool: bool("OPENCODE_ENABLE_QUESTION_TOOL"),
|
||||
experimentalScout: enabledByExperimental("OPENCODE_EXPERIMENTAL_SCOUT"),
|
||||
experimentalBackgroundSubagents: enabledByExperimental("OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS"),
|
||||
experimentalLspTy: bool("OPENCODE_EXPERIMENTAL_LSP_TY"),
|
||||
experimentalLspTool: enabledByExperimental("OPENCODE_EXPERIMENTAL_LSP_TOOL"),
|
||||
experimentalOxfmt: enabledByExperimental("OPENCODE_EXPERIMENTAL_OXFMT"),
|
||||
experimentalPlanMode: enabledByExperimental("OPENCODE_EXPERIMENTAL_PLAN_MODE"),
|
||||
|
||||
@@ -6,13 +6,13 @@ import path from "path"
|
||||
import { pathToFileURL, fileURLToPath } from "url"
|
||||
import * as LSPServer from "./server"
|
||||
import { Config } from "@/config/config"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { Process } from "@/util/process"
|
||||
import { spawn as lspspawn } from "./launch"
|
||||
import { Effect, Layer, Context, Schema } from "effect"
|
||||
import { InstanceState } from "@/effect/instance-state"
|
||||
import { containsPath } from "@/project/instance-context"
|
||||
import { NonNegativeInt } from "@opencode-ai/core/schema"
|
||||
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||
|
||||
const log = Log.create({ service: "lsp" })
|
||||
|
||||
@@ -98,8 +98,8 @@ const kinds = [
|
||||
SymbolKind.Enum,
|
||||
]
|
||||
|
||||
const filterExperimentalServers = (servers: Record<string, LSPServer.Info>) => {
|
||||
if (Flag.OPENCODE_EXPERIMENTAL_LSP_TY) {
|
||||
const filterExperimentalServers = (servers: Record<string, LSPServer.Info>, flags: RuntimeFlags.Info) => {
|
||||
if (flags.experimentalLspTy) {
|
||||
if (servers["pyright"]) {
|
||||
log.info("LSP server pyright is disabled because OPENCODE_EXPERIMENTAL_LSP_TY is enabled")
|
||||
delete servers["pyright"]
|
||||
@@ -143,6 +143,7 @@ export const layer = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
const config = yield* Config.Service
|
||||
const flags = yield* RuntimeFlags.Service
|
||||
|
||||
const state = yield* InstanceState.make<State>(
|
||||
Effect.fn("LSP.state")(function* (ctx) {
|
||||
@@ -157,7 +158,7 @@ export const layer = Layer.effect(
|
||||
servers[server.id] = server
|
||||
}
|
||||
|
||||
filterExperimentalServers(servers)
|
||||
filterExperimentalServers(servers, flags)
|
||||
|
||||
if (cfg.lsp !== true) {
|
||||
for (const [name, item] of Object.entries(cfg.lsp)) {
|
||||
@@ -217,7 +218,7 @@ export const layer = Layer.effect(
|
||||
|
||||
async function schedule(server: LSPServer.Info, root: string, key: string) {
|
||||
const handle = await server
|
||||
.spawn(root, ctx)
|
||||
.spawn(root, ctx, flags)
|
||||
.then((value) => {
|
||||
if (!value) s.broken.add(key)
|
||||
return value
|
||||
@@ -498,7 +499,7 @@ export const layer = Layer.effect(
|
||||
}),
|
||||
)
|
||||
|
||||
export const defaultLayer = layer.pipe(Layer.provide(Config.defaultLayer))
|
||||
export const defaultLayer = layer.pipe(Layer.provide(Config.defaultLayer), Layer.provide(RuntimeFlags.defaultLayer))
|
||||
|
||||
export * as Diagnostic from "./diagnostic"
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { which } from "../util/which"
|
||||
import { Module } from "@opencode-ai/core/util/module"
|
||||
import { spawn } from "./launch"
|
||||
import { Npm } from "@opencode-ai/core/npm"
|
||||
import type { RuntimeFlags } from "@/effect/runtime-flags"
|
||||
|
||||
const log = Log.create({ service: "lsp.server" })
|
||||
const pathExists = async (p: string) =>
|
||||
@@ -60,7 +61,7 @@ export interface Info {
|
||||
extensions: string[]
|
||||
global?: boolean
|
||||
root: RootFunction
|
||||
spawn(root: string, ctx: InstanceContext): Promise<Handle | undefined>
|
||||
spawn(root: string, ctx: InstanceContext, flags: RuntimeFlags.Info): Promise<Handle | undefined>
|
||||
}
|
||||
|
||||
export const Deno: Info = {
|
||||
@@ -431,8 +432,8 @@ export const Ty: Info = {
|
||||
"Pipfile",
|
||||
"pyrightconfig.json",
|
||||
]),
|
||||
async spawn(root) {
|
||||
if (!Flag.OPENCODE_EXPERIMENTAL_LSP_TY) {
|
||||
async spawn(root, _ctx, flags) {
|
||||
if (!flags.experimentalLspTy) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ describe("RuntimeFlags", () => {
|
||||
expect(flags.enableQuestionTool).toBe(true)
|
||||
expect(flags.experimentalScout).toBe(true)
|
||||
expect(flags.experimentalBackgroundSubagents).toBe(true)
|
||||
expect(flags.experimentalLspTy).toBe(false)
|
||||
expect(flags.experimentalLspTool).toBe(true)
|
||||
expect(flags.experimentalOxfmt).toBe(true)
|
||||
expect(flags.experimentalPlanMode).toBe(true)
|
||||
@@ -44,6 +45,20 @@ describe("RuntimeFlags", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("defaultLayer parses OPENCODE_EXPERIMENTAL_LSP_TY", () =>
|
||||
Effect.gen(function* () {
|
||||
const flags = yield* readFlags.pipe(
|
||||
Effect.provide(
|
||||
fromConfig({
|
||||
OPENCODE_EXPERIMENTAL_LSP_TY: "true",
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
expect(flags.experimentalLspTy).toBe(true)
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("layer accepts partial test overrides and fills defaults from Config definitions", () =>
|
||||
Effect.gen(function* () {
|
||||
const flags = yield* readFlags.pipe(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { describe, expect, spyOn } from "bun:test"
|
||||
import path from "path"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Config } from "@/config/config"
|
||||
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||
import { LSP } from "@/lsp/lsp"
|
||||
import * as LSPServer from "@/lsp/server"
|
||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||
@@ -8,6 +10,12 @@ import { provideTmpdirInstance } from "../fixture/fixture"
|
||||
import { testEffect } from "../lib/effect"
|
||||
|
||||
const it = testEffect(Layer.mergeAll(LSP.defaultLayer, CrossSpawnSpawner.defaultLayer))
|
||||
const experimentalTyIt = testEffect(
|
||||
Layer.mergeAll(
|
||||
LSP.layer.pipe(Layer.provide(Config.defaultLayer), Layer.provide(RuntimeFlags.layer({ experimentalLspTy: true }))),
|
||||
CrossSpawnSpawner.defaultLayer,
|
||||
),
|
||||
)
|
||||
|
||||
describe("lsp.spawn", () => {
|
||||
it.live("does not spawn builtin LSP for files outside instance", () =>
|
||||
@@ -106,4 +114,56 @@ describe("lsp.spawn", () => {
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
it.live("uses pyright instead of ty by default", () =>
|
||||
provideTmpdirInstance(
|
||||
(dir) =>
|
||||
LSP.Service.use((lsp) =>
|
||||
Effect.gen(function* () {
|
||||
const ty = spyOn(LSPServer.Ty, "spawn").mockResolvedValue(undefined)
|
||||
const pyright = spyOn(LSPServer.Pyright, "spawn").mockResolvedValue(undefined)
|
||||
|
||||
try {
|
||||
yield* lsp.hover({
|
||||
file: path.join(dir, "src", "inside.py"),
|
||||
line: 0,
|
||||
character: 0,
|
||||
})
|
||||
expect(ty).toHaveBeenCalledTimes(0)
|
||||
expect(pyright).toHaveBeenCalledTimes(1)
|
||||
} finally {
|
||||
ty.mockRestore()
|
||||
pyright.mockRestore()
|
||||
}
|
||||
}),
|
||||
),
|
||||
{ config: { lsp: true } },
|
||||
),
|
||||
)
|
||||
|
||||
experimentalTyIt.live("uses ty instead of pyright when experimentalLspTy is enabled", () =>
|
||||
provideTmpdirInstance(
|
||||
(dir) =>
|
||||
LSP.Service.use((lsp) =>
|
||||
Effect.gen(function* () {
|
||||
const ty = spyOn(LSPServer.Ty, "spawn").mockResolvedValue(undefined)
|
||||
const pyright = spyOn(LSPServer.Pyright, "spawn").mockResolvedValue(undefined)
|
||||
|
||||
try {
|
||||
yield* lsp.hover({
|
||||
file: path.join(dir, "src", "inside.py"),
|
||||
line: 0,
|
||||
character: 0,
|
||||
})
|
||||
expect(ty).toHaveBeenCalledTimes(1)
|
||||
expect(pyright).toHaveBeenCalledTimes(0)
|
||||
} finally {
|
||||
ty.mockRestore()
|
||||
pyright.mockRestore()
|
||||
}
|
||||
}),
|
||||
),
|
||||
{ config: { lsp: true } },
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user