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 (
"context"
"fmt"
"path"
"path/filepath"
"strconv"
"strings"
@ -32,6 +34,7 @@ type resolvedIds struct {
ScreenId string
WindowId string
RemoteId string
RemoteName string
RemoteState *sstore.RemoteState
}
@ -59,6 +62,13 @@ func firstArg(pk *scpacket.FeCommandPacketType) string {
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 {
if arg == "" {
return def
@ -138,22 +148,22 @@ func resolveScreenId(ctx context.Context, pk *scpacket.FeCommandPacketType, sess
return resolveSessionScreen(ctx, sessionId, screenArg)
}
func resolveRemote(ctx context.Context, pk *scpacket.FeCommandPacketType, sessionId string, windowId string) (string, *sstore.RemoteState, error) {
remoteArg := pk.Kwargs["remote"]
if remoteArg == "" {
return "", nil, nil
func resolveRemote(ctx context.Context, pk *scpacket.FeCommandPacketType, sessionId string, windowId string) (string, string, *sstore.RemoteState, error) {
remoteName := pk.Kwargs["remote"]
if remoteName == "" {
return "", "", nil, nil
}
remoteId, state, err := sstore.GetRemoteState(ctx, remoteArg, sessionId, windowId)
remoteId, state, err := sstore.GetRemoteState(ctx, remoteName, sessionId, windowId)
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 {
state, err = remote.GetDefaultRemoteStateById(remoteId)
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) {
@ -191,7 +201,7 @@ func resolveIds(ctx context.Context, pk *scpacket.FeCommandPacketType, rtype int
}
}
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 {
return rtn, err
}
@ -222,6 +232,9 @@ func HandleCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstor
case "cd":
return CdCommand(ctx, pk)
case "compgen":
return CompGenCommand(ctx, pk)
default:
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)
}
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.ReqId = uuid.New().String()
cdPacket.Dir = newDir
localRemote := remote.GetRemoteById(ids.RemoteId)
if localRemote == nil {
curRemote := remote.GetRemoteById(ids.RemoteId)
if curRemote == nil {
return nil, fmt.Errorf("invalid remote, cannot execute command")
}
resp, err := localRemote.PacketRpc(ctx, cdPacket)
_, err = curRemote.PacketRpc(ctx, cdPacket)
if err != nil {
return nil, err
}
fmt.Printf("GOT cd RESP: %v\n", resp)
return nil, nil
remote, err := sstore.UpdateRemoteCwd(ctx, ids.RemoteName, ids.SessionId, ids.WindowId, ids.RemoteId, newDir)
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) {

View File

@ -503,3 +503,34 @@ func GetRemoteState(ctx context.Context, rname string, sessionId string, windowI
})
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 CmdUpdateStr = "cmd"
const LineCmdUpdateStr = "line+cmd"
const InfoUpdateStr = "info"
type UpdatePacket interface {
UpdateType() string
@ -36,7 +37,7 @@ type WindowUpdate struct {
Remove bool `json:"remove,omitempty"`
}
func (WindowUpdate) WindowUpdate() string {
func (WindowUpdate) UpdateType() string {
return WindowUpdateStr
}
@ -70,6 +71,15 @@ func (LineUpdate) UpdateType() string {
return LineCmdUpdateStr
}
type InfoUpdate struct {
InfoTitle string `json:"infotitle"`
InfoStrings []string `json:"infostrings"`
}
func (InfoUpdate) UpdateType() string {
return InfoUpdateStr
}
type UpdateChannel struct {
SessionId string
ClientId string