package debugger
import (
"encoding/gob"
"errors"
"os/exec"
"runtime"
"strings"
"sync"
"time"
"github.com/charmbracelet/ssh"
"github.com/pancsta/cview"
"github.com/pancsta/tcell-v2"
"github.com/pancsta/tcell-v2/terminfo"
"github.com/pancsta/asyncmachine-go/tools/debugger/server"
"github.com/pancsta/asyncmachine-go/tools/debugger/types"
am "github.com/pancsta/asyncmachine-go/pkg/machine"
)
const (
colorActive = tcell .ColorOlive
colorActive2 = tcell .ColorGreenYellow
colorInactive = tcell .ColorLimeGreen
colorHighlight = tcell .ColorDarkSlateGray
colorHighlight2 = tcell .ColorDimGray
colorHighlight3 = tcell .Color233
colorErr = tcell .ColorRed
playInterval = 500 * time .Millisecond
maxClients = 5000
timeFormat = "15:04:05.000000000"
fastJumpAmount = 50
arrowThrottleMs = 200
logUpdateDebounce = 300 * time .Millisecond
sidebarUpdateDebounce = time .Second
searchAsTypeWindow = 1500 * time .Millisecond
heartbeatInterval = 1 * time .Minute
maxMemMb = 50
msgMaxThreshold = 300
scrollTxThrottle = 3
)
var colorDefault = cview .Styles .PrimaryTextColor
const (
toolFilterCanceledTx ToolName = "skip-canceled"
toolFilterQueuedTx ToolName = "skip-queued"
toolFilterAutoTx ToolName = "skip-auto"
toolFilterEmptyTx ToolName = "skip-empty"
toolFilterHealth ToolName = "skip-health"
toolFilterOutGroup ToolName = "skip-outgroup"
toolFilterChecks ToolName = "skip-checks"
toolFilterDisconn ToolName = "skip-disconn"
ToolLogTimestamps ToolName = "hide-timestamps"
ToolFilterTraces ToolName = "hide-traces"
toolLog ToolName = "log"
toolDiagrams ToolName = "diagrams"
toolTimelines ToolName = "timelines"
toolReader ToolName = "reader"
toolRain ToolName = "rain"
toolWeb ToolName = "web"
toolHelp ToolName = "help"
toolPlay ToolName = "play"
toolTail ToolName = "tail"
toolPrev ToolName = "prev"
toolNext ToolName = "next"
toolJumpNext ToolName = "jump-next"
toolJumpPrev ToolName = "jump-prev"
toolFirst ToolName = "first"
toolLast ToolName = "last"
toolExpand ToolName = "expand"
toolMatrix ToolName = "matrix"
toolExport ToolName = "export"
toolNextStep ToolName = "next-step"
toolPrevStep ToolName = "prev-step"
)
type ToolName string
type Client struct {
*server .Client
CursorTx1 int
ReaderCollapsed bool
CursorStep1 int
SelectedState string
LogReader map [string ][]*types .LogReaderEntry
logRenderedCursor1 int
logRenderedLevel am .LogLevel
logRenderedFilters *types .Filters
logRenderedTimestamps bool
logReaderMx sync .Mutex
}
func init() {
gob .Register (server .Exportable {})
gob .Register (am .Relation (0 ))
}
func newClient(id , connId , schemaHash string , data *server .Exportable ) *Client {
c := &Client {
Client : &server .Client {
Id : id ,
ConnId : connId ,
SchemaHash : schemaHash ,
Exportable : data ,
},
LogReader : make (map [string ][]*types .LogReaderEntry ),
}
c .ParseSchema ()
return c
}
func (c *Client ) AddReaderEntry (txId string , entry *types .LogReaderEntry ) int {
c .logReaderMx .Lock ()
defer c .logReaderMx .Unlock ()
c .LogReader [txId ] = append (c .LogReader [txId ], entry )
return len (c .LogReader [txId ]) - 1
}
func (c *Client ) GetReaderEntry (txId string , idx int ) *types .LogReaderEntry {
c .logReaderMx .Lock ()
defer c .logReaderMx .Unlock ()
ptrTx , ok := c .LogReader [txId ]
if !ok || idx >= len (ptrTx ) {
return nil
}
return ptrTx [idx ]
}
func NewSessionScreen (s ssh .Session ) (tcell .Screen , error ) {
pi , ch , ok := s .Pty ()
if !ok {
return nil , errors .New ("no pty requested" )
}
ti , err := terminfo .LookupTerminfo (pi .Term )
if err != nil {
return nil , err
}
screen , err := tcell .NewTerminfoScreenFromTtyTerminfo (&tty {
Session : s ,
size : pi .Window ,
ch : ch ,
}, ti )
if err != nil {
return nil , err
}
return screen , nil
}
type tty struct {
ssh .Session
size ssh .Window
ch <-chan ssh .Window
resizecb func ()
mu sync .Mutex
}
func (t *tty ) Start () error {
go func () {
for win := range t .ch {
t .mu .Lock ()
t .size = win
t .notifyResize ()
t .mu .Unlock ()
}
}()
return nil
}
func (t *tty ) Stop () error {
return nil
}
func (t *tty ) Drain () error {
return nil
}
func (t *tty ) WindowSize () (window tcell .WindowSize , err error ) {
t .mu .Lock ()
defer t .mu .Unlock ()
return tcell .WindowSize {
Width : t .size .Width ,
Height : t .size .Height ,
}, nil
}
func (t *tty ) NotifyResize (cb func ()) {
t .mu .Lock ()
defer t .mu .Unlock ()
t .resizecb = cb
}
func (t *tty ) notifyResize () {
if t .resizecb != nil {
t .resizecb ()
}
}
func openURL(url string ) error {
var cmd string
var args []string
switch runtime .GOOS {
case "windows" :
cmd = "cmd.exe"
args = []string {
"/c" , "rundll32" , "url.dll,FileProtocolHandler" ,
strings .ReplaceAll (url , "&" , "^&" ),
}
case "darwin" :
cmd = "open"
args = []string {url }
default :
if isWSL () {
cmd = "cmd.exe"
args = []string {"start" , url }
} else {
cmd = "xdg-open"
args = []string {url }
}
}
e := exec .Command (cmd , args ...)
err := e .Start ()
if err != nil {
return err
}
err = e .Wait ()
if err != nil {
return err
}
return nil
}
func isWSL() bool {
releaseData , err := exec .Command ("uname" , "-r" ).Output ()
if err != nil {
return false
}
return strings .Contains (strings .ToLower (string (releaseData )), "microsoft" )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .