11import z from "zod"
2+ import { Effect } from "effect"
23import { Tool } from "./tool"
34import path from "path"
45import { LSP } from "../lsp"
@@ -20,78 +21,93 @@ const operations = [
2021 "outgoingCalls" ,
2122] as const
2223
23- export const LspTool = Tool . define ( "lsp" , {
24- description : DESCRIPTION ,
25- parameters : z . object ( {
26- operation : z . enum ( operations ) . describe ( "The LSP operation to perform" ) ,
27- filePath : z . string ( ) . describe ( "The absolute or relative path to the file" ) ,
28- line : z . number ( ) . int ( ) . min ( 1 ) . describe ( "The line number (1-based, as shown in editors)" ) ,
29- character : z . number ( ) . int ( ) . min ( 1 ) . describe ( "The character offset (1-based, as shown in editors)" ) ,
30- } ) ,
31- execute : async ( args , ctx ) => {
32- const file = path . isAbsolute ( args . filePath ) ? args . filePath : path . join ( Instance . directory , args . filePath )
33- await assertExternalDirectory ( ctx , file )
24+ const parameters = z . object ( {
25+ operation : z . enum ( operations ) . describe ( "The LSP operation to perform" ) ,
26+ filePath : z . string ( ) . describe ( "The absolute or relative path to the file" ) ,
27+ line : z . number ( ) . int ( ) . min ( 1 ) . describe ( "The line number (1-based, as shown in editors)" ) ,
28+ character : z . number ( ) . int ( ) . min ( 1 ) . describe ( "The character offset (1-based, as shown in editors)" ) ,
29+ } )
3430
35- await ctx . ask ( {
36- permission : "lsp" ,
37- patterns : [ "*" ] ,
38- always : [ "*" ] ,
39- metadata : { } ,
40- } )
41- const uri = pathToFileURL ( file ) . href
42- const position = {
43- file,
44- line : args . line - 1 ,
45- character : args . character - 1 ,
46- }
31+ export const LspTool = Tool . defineEffect (
32+ "lsp" ,
33+ Effect . gen ( function * ( ) {
34+ const lsp = yield * LSP . Service
4735
48- const relPath = path . relative ( Instance . worktree , file )
49- const title = `${ args . operation } ${ relPath } :${ args . line } :${ args . character } `
36+ const run = Effect . fn ( "LspTool.execute" ) ( function * ( args : z . infer < typeof parameters > , ctx : Tool . Context ) {
37+ const file = path . isAbsolute ( args . filePath ) ? args . filePath : path . join ( Instance . directory , args . filePath )
38+ yield * Effect . promise ( ( ) => assertExternalDirectory ( ctx , file ) )
5039
51- const exists = await Filesystem . exists ( file )
52- if ( ! exists ) {
53- throw new Error ( `File not found: ${ file } ` )
54- }
40+ yield * Effect . promise ( ( ) =>
41+ ctx . ask ( {
42+ permission : "lsp" ,
43+ patterns : [ "*" ] ,
44+ always : [ "*" ] ,
45+ metadata : { } ,
46+ } ) ,
47+ )
48+ const uri = pathToFileURL ( file ) . href
49+ const position = {
50+ file,
51+ line : args . line - 1 ,
52+ character : args . character - 1 ,
53+ }
5554
56- const available = await LSP . hasClients ( file )
57- if ( ! available ) {
58- throw new Error ( "No LSP server available for this file type." )
59- }
55+ const rel = path . relative ( Instance . worktree , file )
56+ const title = `${ args . operation } ${ rel } :${ args . line } :${ args . character } `
6057
61- await LSP . touchFile ( file , true )
58+ const exists = yield * Effect . promise ( ( ) => Filesystem . exists ( file ) )
59+ if ( ! exists ) {
60+ return yield * Effect . fail ( new Error ( `File not found: ${ file } ` ) )
61+ }
6262
63- const result : unknown [ ] = await ( async ( ) => {
64- switch ( args . operation ) {
65- case "goToDefinition" :
66- return LSP . definition ( position )
67- case "findReferences" :
68- return LSP . references ( position )
69- case "hover" :
70- return LSP . hover ( position )
71- case "documentSymbol" :
72- return LSP . documentSymbol ( uri )
73- case "workspaceSymbol" :
74- return LSP . workspaceSymbol ( "" )
75- case "goToImplementation" :
76- return LSP . implementation ( position )
77- case "prepareCallHierarchy" :
78- return LSP . prepareCallHierarchy ( position )
79- case "incomingCalls" :
80- return LSP . incomingCalls ( position )
81- case "outgoingCalls" :
82- return LSP . outgoingCalls ( position )
63+ const available = yield * lsp . hasClients ( file )
64+ if ( ! available ) {
65+ return yield * Effect . fail ( new Error ( "No LSP server available for this file type." ) )
8366 }
84- } ) ( )
8567
86- const output = ( ( ) => {
87- if ( result . length === 0 ) return `No results found for ${ args . operation } `
88- return JSON . stringify ( result , null , 2 )
89- } ) ( )
68+ yield * lsp . touchFile ( file , true )
69+
70+ const result : unknown [ ] = yield * ( ( ) => {
71+ switch ( args . operation ) {
72+ case "goToDefinition" :
73+ return lsp . definition ( position )
74+ case "findReferences" :
75+ return lsp . references ( position )
76+ case "hover" :
77+ return lsp . hover ( position )
78+ case "documentSymbol" :
79+ return lsp . documentSymbol ( uri )
80+ case "workspaceSymbol" :
81+ return lsp . workspaceSymbol ( "" )
82+ case "goToImplementation" :
83+ return lsp . implementation ( position )
84+ case "prepareCallHierarchy" :
85+ return lsp . prepareCallHierarchy ( position )
86+ case "incomingCalls" :
87+ return lsp . incomingCalls ( position )
88+ case "outgoingCalls" :
89+ return lsp . outgoingCalls ( position )
90+ }
91+ } ) ( )
92+
93+ const output = ( ( ) => {
94+ if ( result . length === 0 ) return `No results found for ${ args . operation } `
95+ return JSON . stringify ( result , null , 2 )
96+ } ) ( )
97+
98+ return {
99+ title,
100+ metadata : { result } ,
101+ output,
102+ }
103+ } )
90104
91105 return {
92- title,
93- metadata : { result } ,
94- output,
106+ description : DESCRIPTION ,
107+ parameters,
108+ async execute ( args : z . infer < typeof parameters > , ctx ) {
109+ return Effect . runPromise ( run ( args , ctx ) . pipe ( Effect . orDie ) )
110+ } ,
95111 }
96- } ,
97- } )
112+ } ) ,
113+ )
0 commit comments