implement /cd and /compgen

This commit is contained in:
sawka 2022-08-09 14:24:57 -07:00
parent 66f547a695
commit a175d236c1
3 changed files with 152 additions and 15 deletions

View File

@ -3,6 +3,8 @@ package cmdrunner
import ( import (
"context" "context"
"fmt" "fmt"
"path"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
@ -32,6 +34,7 @@ type resolvedIds struct {
ScreenId string ScreenId string
WindowId string WindowId string
RemoteId string RemoteId string
RemoteName string
RemoteState *sstore.RemoteState RemoteState *sstore.RemoteState
} }
@ -59,6 +62,13 @@ func firstArg(pk *scpacket.FeCommandPacketType) string {
return pk.Args[0] return pk.Args[0]
} }
func argN(pk *scpacket.FeCommandPacketType, n int) string {
if len(pk.Args) <= n {
return ""
}
return pk.Args[n]
}
func resolveBool(arg string, def bool) bool { func resolveBool(arg string, def bool) bool {
if arg == "" { if arg == "" {
return def return def
@ -138,22 +148,22 @@ func resolveScreenId(ctx context.Context, pk *scpacket.FeCommandPacketType, sess
return resolveSessionScreen(ctx, sessionId, screenArg) return resolveSessionScreen(ctx, sessionId, screenArg)
} }
func resolveRemote(ctx context.Context, pk *scpacket.FeCommandPacketType, sessionId string, windowId string) (string, *sstore.RemoteState, error) { func resolveRemote(ctx context.Context, pk *scpacket.FeCommandPacketType, sessionId string, windowId string) (string, string, *sstore.RemoteState, error) {
remoteArg := pk.Kwargs["remote"] remoteName := pk.Kwargs["remote"]
if remoteArg == "" { if remoteName == "" {
return "", nil, nil return "", "", nil, nil
} }
remoteId, state, err := sstore.GetRemoteState(ctx, remoteArg, sessionId, windowId) remoteId, state, err := sstore.GetRemoteState(ctx, remoteName, sessionId, windowId)
if err != nil { if err != nil {
return "", nil, fmt.Errorf("cannot resolve remote '%s': %w", remoteArg, err) return "", "", nil, fmt.Errorf("cannot resolve remote '%s': %w", remoteName, err)
} }
if state == nil { if state == nil {
state, err = remote.GetDefaultRemoteStateById(remoteId) state, err = remote.GetDefaultRemoteStateById(remoteId)
if err != nil { if err != nil {
return "", nil, fmt.Errorf("cannot resolve remote '%s': %w", remoteArg, err) return "", "", nil, fmt.Errorf("cannot resolve remote '%s': %w", remoteName, err)
} }
} }
return remoteId, state, nil return remoteName, remoteId, state, nil
} }
func resolveIds(ctx context.Context, pk *scpacket.FeCommandPacketType, rtype int) (resolvedIds, error) { func resolveIds(ctx context.Context, pk *scpacket.FeCommandPacketType, rtype int) (resolvedIds, error) {
@ -191,7 +201,7 @@ func resolveIds(ctx context.Context, pk *scpacket.FeCommandPacketType, rtype int
} }
} }
if (rtype&R_Remote)+(rtype&R_RemoteOpt) > 0 { if (rtype&R_Remote)+(rtype&R_RemoteOpt) > 0 {
rtn.RemoteId, rtn.RemoteState, err = resolveRemote(ctx, pk, rtn.SessionId, rtn.WindowId) rtn.RemoteName, rtn.RemoteId, rtn.RemoteState, err = resolveRemote(ctx, pk, rtn.SessionId, rtn.WindowId)
if err != nil { if err != nil {
return rtn, err return rtn, err
} }
@ -222,6 +232,9 @@ func HandleCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstor
case "cd": case "cd":
return CdCommand(ctx, pk) return CdCommand(ctx, pk)
case "compgen":
return CompGenCommand(ctx, pk)
default: default:
return nil, fmt.Errorf("invalid command '/%s', no handler", pk.MetaCmd) return nil, fmt.Errorf("invalid command '/%s', no handler", pk.MetaCmd)
} }
@ -354,19 +367,102 @@ func CdCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.Up
return nil, fmt.Errorf("/cd error: %w", err) return nil, fmt.Errorf("/cd error: %w", err)
} }
newDir := firstArg(pk) newDir := firstArg(pk)
if newDir == "" {
return nil, nil
}
if !strings.HasPrefix(newDir, "/") {
if ids.RemoteState == nil {
return nil, fmt.Errorf("/cd error: cannot get current remote directory (can only cd with absolute path)")
}
newDir = path.Join(ids.RemoteState.Cwd, newDir)
newDir, err = filepath.Abs(newDir)
if err != nil {
return nil, fmt.Errorf("/cd error: error canonicalizing new directory: %w", err)
}
}
fmt.Printf("cd [%s] => [%s]\n", firstArg(pk), newDir)
cdPacket := packet.MakeCdPacket() cdPacket := packet.MakeCdPacket()
cdPacket.ReqId = uuid.New().String() cdPacket.ReqId = uuid.New().String()
cdPacket.Dir = newDir cdPacket.Dir = newDir
localRemote := remote.GetRemoteById(ids.RemoteId) curRemote := remote.GetRemoteById(ids.RemoteId)
if localRemote == nil { if curRemote == nil {
return nil, fmt.Errorf("invalid remote, cannot execute command") return nil, fmt.Errorf("invalid remote, cannot execute command")
} }
resp, err := localRemote.PacketRpc(ctx, cdPacket) _, err = curRemote.PacketRpc(ctx, cdPacket)
if err != nil { if err != nil {
return nil, err return nil, err
} }
fmt.Printf("GOT cd RESP: %v\n", resp) remote, err := sstore.UpdateRemoteCwd(ctx, ids.RemoteName, ids.SessionId, ids.WindowId, ids.RemoteId, newDir)
return nil, nil if err != nil {
return nil, err
}
update := sstore.WindowUpdate{
Window: sstore.WindowType{
SessionId: ids.SessionId,
WindowId: ids.WindowId,
Remotes: []*sstore.RemoteInstance{remote},
},
}
return update, nil
}
func getStrArr(v interface{}, field string) []string {
if v == nil {
return nil
}
m, ok := v.(map[string]interface{})
if !ok {
return nil
}
fieldVal := m[field]
if fieldVal == nil {
return nil
}
iarr, ok := fieldVal.([]interface{})
if !ok {
return nil
}
var sarr []string
for _, iv := range iarr {
if sv, ok := iv.(string); ok {
sarr = append(sarr, sv)
}
}
return sarr
}
func CompGenCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveIds(ctx, pk, R_Session|R_Window|R_Remote)
if err != nil {
return nil, fmt.Errorf("/compgen error: %w", err)
}
compType := argN(pk, 0)
prefix := argN(pk, 1)
if !packet.IsValidCompGenType(compType) {
return nil, fmt.Errorf("/compgen invalid type '%s'", compType)
}
cgPacket := packet.MakeCompGenPacket()
cgPacket.ReqId = uuid.New().String()
cgPacket.CompType = compType
cgPacket.Prefix = prefix
if ids.RemoteState == nil {
return nil, fmt.Errorf("/compgen invalid remote state")
}
cgPacket.Cwd = ids.RemoteState.Cwd
curRemote := remote.GetRemoteById(ids.RemoteId)
if curRemote == nil {
return nil, fmt.Errorf("invalid remote, cannot execute command")
}
resp, err := curRemote.PacketRpc(ctx, cgPacket)
if err != nil {
return nil, err
}
comps := getStrArr(resp.Data, "comps")
update := sstore.InfoUpdate{
InfoTitle: fmt.Sprintf("%s completions", compType),
InfoStrings: comps,
}
return update, nil
} }
func CommentCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { func CommentCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {

View File

@ -503,3 +503,34 @@ func GetRemoteState(ctx context.Context, rname string, sessionId string, windowI
}) })
return remoteId, remoteState, txErr return remoteId, remoteState, txErr
} }
func UpdateRemoteCwd(ctx context.Context, rname string, sessionId string, windowId string, remoteId string, cwd string) (*RemoteInstance, error) {
var ri RemoteInstance
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?`
if !tx.Exists(query, sessionId, windowId) {
return fmt.Errorf("cannot update remote instance cwd, no window found")
}
query = `SELECT * FROM remote_instance WHERE sessionid = ? AND windowid = ? AND name = ?`
found := tx.GetWrap(&ri, query, sessionId, windowId, rname)
if !found {
ri = RemoteInstance{
RIId: uuid.New().String(),
Name: rname,
SessionId: sessionId,
WindowId: windowId,
RemoteId: remoteId,
SessionScope: (windowId == ""),
State: RemoteState{Cwd: cwd},
}
query = `INSERT INTO remote_instance (riid, name, sessionid, windowid, remoteid, sessionscope, state) VALUES (:riid, :name, :sessionid, :windowid, :remoteid, :sessionscope, :state)`
tx.NamedExecWrap(query, ri)
return nil
}
ri.State.Cwd = cwd
query = `UPDATE remote_instance SET state = ? WHERE sessionid = ? AND windowid = ? AND name = ?`
tx.ExecWrap(query, ri.State, ri.SessionId, ri.WindowId, ri.Name)
return nil
})
return &ri, txErr
}

View File

@ -9,6 +9,7 @@ const SessionUpdateStr = "session"
const WindowUpdateStr = "window" const WindowUpdateStr = "window"
const CmdUpdateStr = "cmd" const CmdUpdateStr = "cmd"
const LineCmdUpdateStr = "line+cmd" const LineCmdUpdateStr = "line+cmd"
const InfoUpdateStr = "info"
type UpdatePacket interface { type UpdatePacket interface {
UpdateType() string UpdateType() string
@ -36,7 +37,7 @@ type WindowUpdate struct {
Remove bool `json:"remove,omitempty"` Remove bool `json:"remove,omitempty"`
} }
func (WindowUpdate) WindowUpdate() string { func (WindowUpdate) UpdateType() string {
return WindowUpdateStr return WindowUpdateStr
} }
@ -70,6 +71,15 @@ func (LineUpdate) UpdateType() string {
return LineCmdUpdateStr return LineCmdUpdateStr
} }
type InfoUpdate struct {
InfoTitle string `json:"infotitle"`
InfoStrings []string `json:"infostrings"`
}
func (InfoUpdate) UpdateType() string {
return InfoUpdateStr
}
type UpdateChannel struct { type UpdateChannel struct {
SessionId string SessionId string
ClientId string ClientId string