z_mirror/pesticide
git clone https://git.raiguard.me/z_mirror/pesticide
388f8ba
trunk
1package adapter 2 3import ( 4"bufio" 5"encoding/json" 6"errors" 7"io" 8"log" 9"net" 10"os/exec" 11"time" 12 13 tea"github.com/charmbracelet/bubbletea" 14"github.com/google/go-dap" 15"github.com/google/shlex" 16"codeberg.org/raiguard/pesticide/config" 17) 18 19type AdapterState int 20 21const ( 22Initializing AdapterState = iota 23Running 24Stopped 25) 26 27type Adapter struct { 28// Internally managed 29Capabilities dap.Capabilities 30ID string 31Seq int 32PendingRequests map [int ]dap.Message 33// Managed by router 34State AdapterState 35Threads []dap.Thread 36FocusedThread int 37StackFrames map [int ][]dap.StackFrame 38FocusedStackFrame * dap.StackFrame 39Breakpoints map [string ][]dap.SourceBreakpoint 40// Internal I/O 41rw bufio.ReadWriter 42cmd * exec.Cmd 43launchArgs json.RawMessage 44conn * net.Conn 45} 46 47type Msg struct { 48ID string 49Msg tea.Msg 50} 51 52func New (config config.AdapterConfig ) (* Adapter ,error ) { 53var cmd * exec.Cmd 54var conn * net.Conn 55var rw * bufio.ReadWriter 56if config .Cmd != nil { 57args ,err := shlex .Split (* config .Cmd ) 58if err != nil { 59return nil ,err 60 } 61child := exec .Command (args [0 ],args [1 :]... ) 62stdin ,err := child .StdinPipe () 63if err != nil { 64return nil ,err 65 } 66stdout ,err := child .StdoutPipe () 67if err != nil { 68return nil ,err 69 } 70// TODO: Hook stderr and show in the UI 71err = child .Start () 72if err != nil { 73return nil ,err 74 } 75cmd = child 76 77reader := bufio .NewReader (stdout ) 78writer := bufio .NewWriter (stdin ) 79rw = & bufio.ReadWriter {Reader :reader ,Writer :writer } 80 } 81if config .Addr != nil { 82if cmd != nil { 83time .Sleep (time .Millisecond * 500 )// Give time for the cmd to init 84 } 85conn ,err := net .Dial ("tcp" ,* config .Addr ) 86if err != nil { 87return nil ,err 88 } 89 90reader := bufio .NewReader (conn ) 91writer := bufio .NewWriter (conn ) 92 93// TODO: Combine with STDIO input/output 94rw = & bufio.ReadWriter {Reader :reader ,Writer :writer } 95 } 96 97if rw == nil { 98return nil ,errors .New ("Adapter must either have a connection or a subprocess" ) 99 } 100 101a := & Adapter { 102Capabilities : dap.Capabilities {}, 103ID :config .ID , 104Seq :0 , 105PendingRequests :map [int ]dap.Message {}, 106Threads : []dap.Thread {}, 107FocusedThread :0 , 108StackFrames :map [int ][]dap.StackFrame {}, 109FocusedStackFrame :& dap.StackFrame {}, 110Breakpoints :map [string ][]dap.SourceBreakpoint {}, 111rw :* rw , 112cmd :cmd , 113launchArgs :config .Args , 114conn :conn , 115 } 116 117return a ,nil 118} 119 120func (a * Adapter )Shutdown () { 121conn := a .conn 122if conn != nil { 123 (* conn ).Close () 124 } 125cmd := a .cmd 126if cmd != nil { 127cmd .Process .Kill () 128 } 129log .Printf ("[%s] EXITED\n" ,a .ID ) 130} 131 132func (a * Adapter )Send (msg dap.Message ) { 133a .PendingRequests [msg .GetSeq ()]= msg 134err := dap .WriteProtocolMessage (a .rw .Writer ,msg ) 135if err != nil { 136log .Println ("Unable to send message to adapter: " ,err ) 137return 138 } 139val ,err := json .Marshal (msg ) 140if err != nil { 141panic (err ) 142 } 143log .Printf ("[%s] <- %s" ,a .ID ,string (val )) 144a .rw .Writer .Flush () 145} 146 147func (a * Adapter )Receive ()Msg { 148if a == nil { 149return Msg {} 150 } 151res := Msg {ID :a .ID } 152msg ,err := dap .ReadProtocolMessage (a .rw .Reader ) 153if err != nil { 154if ! errors .Is (err ,io .EOF ) { 155res .Msg = tea .Println (err ) 156 } 157return res 158 } 159val ,err := json .Marshal (msg ) 160if err != nil { 161res .Msg = tea .Println (err ) 162return res 163 } 164log .Printf ("[%s] -> %s" ,a .ID ,string (val )) 165// Increment seq 166seq := msg .GetSeq () 167if seq > a .Seq { 168a .Seq = seq 169 } 170res .Msg = msg 171return res 172} 173 174func (a * Adapter )NewRequest (command string ) dap.Request { 175a .Seq ++ 176return dap.Request { 177ProtocolMessage : dap.ProtocolMessage {Seq :a .Seq ,Type :"request" }, 178Command :command , 179 } 180} 181 182func (a * Adapter )Launch () { 183a .Send (& dap.LaunchRequest { 184Request :a .NewRequest ("launch" ), 185Arguments :a .launchArgs , 186 }) 187}