updates for /screen:close

This commit is contained in:
sawka 2022-12-23 15:56:29 -08:00
parent d2530339e4
commit 962261ec35
5 changed files with 200 additions and 29 deletions

View File

@ -123,9 +123,11 @@ func init() {
registerCmdFn("screen", ScreenCommand)
registerCmdFn("screen:close", ScreenCloseCommand)
registerCmdFn("screen:purge", ScreenPurgeCommand)
registerCmdFn("screen:open", ScreenOpenCommand)
registerCmdAlias("screen:new", ScreenOpenCommand)
registerCmdFn("screen:set", ScreenSetCommand)
registerCmdFn("screen:showall", ScreenShowAllCommand)
registerCmdAlias("remote", RemoteCommand)
registerCmdFn("remote:show", RemoteShowCommand)
@ -378,10 +380,53 @@ func EvalCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.
}
func ScreenCloseCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen)
ids, err := resolveUiIds(ctx, pk, R_Session) // don't force R_Screen
if err != nil {
return nil, fmt.Errorf("/screen:close cannot close screen: %w", err)
}
screenId := ids.ScreenId
if len(pk.Args) > 0 {
ri, err := resolveSessionScreen(ctx, ids.SessionId, pk.Args[0], ids.ScreenId)
if err != nil {
return nil, fmt.Errorf("/screen:close cannot resolve screen arg: %v", err)
}
screenId = ri.Id
}
if screenId == "" {
return nil, fmt.Errorf("/screen:close no active screen or screen arg passed")
}
closeVal := true
if len(pk.Args) > 1 {
closeVal = resolveBool(pk.Args[1], true)
}
var update sstore.UpdatePacket
if closeVal {
update, err = sstore.CloseScreen(ctx, ids.SessionId, screenId)
if err != nil {
return nil, err
}
return update, nil
} else {
fmt.Printf("unclose screen %s\n", screenId)
err = sstore.UnCloseScreen(ctx, ids.SessionId, screenId)
if err != nil {
return nil, fmt.Errorf("/screen:close cannot re-open screen: %v", err)
}
screen, err := sstore.GetScreenById(ctx, ids.SessionId, screenId)
if err != nil {
return nil, fmt.Errorf("/screen:close cannot get updated screen obj: %v", err)
}
update, session := sstore.MakeSingleSessionUpdate(ids.SessionId)
session.Screens = append(session.Screens, screen)
return update, nil
}
}
func ScreenPurgeCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen)
if err != nil {
return nil, fmt.Errorf("/screen:purge cannot close screen: %w", err)
}
update, err := sstore.DeleteScreen(ctx, ids.SessionId, ids.ScreenId)
if err != nil {
return nil, err
@ -900,6 +945,33 @@ func RemoteShowAllCommand(ctx context.Context, pk *scpacket.FeCommandPacketType)
}, nil
}
func ScreenShowAllCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session)
screenArr, err := sstore.GetAllSessionScreens(ctx, ids.SessionId)
if err != nil {
return nil, fmt.Errorf("/screen:showall error getting screen list: %v", err)
}
var buf bytes.Buffer
for _, screen := range screenArr {
var closedStr string
if screen.Closed {
closedStr = " (closed)"
}
screenIdxStr := "-"
if screen.ScreenIdx != 0 {
screenIdxStr = strconv.Itoa(int(screen.ScreenIdx))
}
outStr := fmt.Sprintf("%s %-30s %s\n", screen.ScreenId, screen.Name+closedStr, screenIdxStr)
buf.WriteString(outStr)
}
return sstore.ModelUpdate{
Info: &sstore.InfoMsgType{
InfoTitle: fmt.Sprintf("all screens for session"),
InfoLines: splitLinesForInfo(buf.String()),
},
}, nil
}
func RemoteArchiveCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_Remote)
if err != nil {
@ -1309,13 +1381,19 @@ func SessionDeleteCommand(ctx context.Context, pk *scpacket.FeCommandPacketType)
if err != nil {
return nil, fmt.Errorf("cannot delete session: %v", err)
}
sessionIds, _ := sstore.GetAllSessionIds(ctx) // ignore error, session is already deleted so that's the main return value
delSession := &sstore.SessionType{SessionId: ids.SessionId, Remove: true}
update := sstore.ModelUpdate{
Sessions: []*sstore.SessionType{delSession},
}
if len(sessionIds) > 0 {
update.ActiveSessionId = sessionIds[0]
activeSessionId, _ := sstore.GetActiveSessionId(ctx) // ignore error
if activeSessionId == "" {
sessionIds, _ := sstore.GetAllSessionIds(ctx) // ignore error, session is already deleted so that's the main return value
if len(sessionIds) > 0 {
err = sstore.SetActiveSessionId(ctx, sessionIds[0])
if err != nil {
update.ActiveSessionId = sessionIds[0]
}
}
}
return update, nil
}

View File

@ -58,7 +58,7 @@ func sessionsToResolveItems(sessions []*sstore.SessionType) []ResolveItem {
}
rtn := make([]ResolveItem, len(sessions))
for idx, session := range sessions {
rtn[idx] = ResolveItem{Name: session.Name, Id: session.SessionId}
rtn[idx] = ResolveItem{Name: session.Name, Id: session.SessionId, Hidden: session.Closed}
}
return rtn
}
@ -69,7 +69,7 @@ func screensToResolveItems(screens []*sstore.ScreenType) []ResolveItem {
}
rtn := make([]ResolveItem, len(screens))
for idx, screen := range screens {
rtn[idx] = ResolveItem{Name: screen.Name, Id: screen.ScreenId}
rtn[idx] = ResolveItem{Name: screen.Name, Id: screen.ScreenId, Hidden: screen.Closed}
}
return rtn
}
@ -133,7 +133,13 @@ func parsePosArg(posStr string) *posArgType {
return &posArgType{Pos: pos}
}
func resolveByPosition(isNumeric bool, items []ResolveItem, curId string, posStr string) *ResolveItem {
func resolveByPosition(isNumeric bool, allItems []ResolveItem, curId string, posStr string) *ResolveItem {
items := make([]ResolveItem, 0, len(allItems))
for _, item := range allItems {
if !item.Hidden {
items = append(items, item)
}
}
if len(items) == 0 {
return nil
}
@ -160,7 +166,8 @@ func resolveByPosition(isNumeric bool, items []ResolveItem, curId string, posStr
finalPos = curIdx + posArg.Pos
} else if isNumeric {
// these resolve items have a "Num" set that should be used to look up non-relative positions
for _, item := range items {
// use allItems for numeric resolve
for _, item := range allItems {
if item.Num == posArg.Pos {
return &item
}
@ -342,7 +349,7 @@ func genericResolve(arg string, curArg string, items []ResolveItem, isNumeric bo
if (isUuid && item.Id == arg) || (tryPuid && strings.HasPrefix(item.Id, arg)) {
return &item, nil
}
if item.Name != "" {
if !item.Hidden && item.Name != "" {
if item.Name == arg {
return &item, nil
}
@ -396,7 +403,7 @@ func resolveScreenArg(sessionId string, screenArg string) (string, error) {
return "", nil
}
if _, err := uuid.Parse(screenArg); err != nil {
return "", fmt.Errorf("invalid screen arg specified (must be sessionid) '%s'", screenArg)
return "", fmt.Errorf("invalid screen arg specified (must be screenid) '%s'", screenArg)
}
return screenArg, nil
}

View File

@ -233,6 +233,7 @@ func GetHistoryItems(ctx context.Context, sessionId string, windowId string, opt
return rtn, nil
}
// includes closed sessions
func GetBareSessions(ctx context.Context) ([]*SessionType, error) {
var rtn []*SessionType
err := WithTx(ctx, func(tx *TxWrap) error {
@ -249,7 +250,7 @@ func GetBareSessions(ctx context.Context) ([]*SessionType, error) {
func GetAllSessionIds(ctx context.Context) ([]string, error) {
var rtn []string
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT sessionid from session ORDER by sessionidx`
query := `SELECT sessionid from session WHERE NOT closed ORDER by sessionidx`
rtn = tx.SelectStrings(query)
return nil
})
@ -287,7 +288,7 @@ func GetAllSessions(ctx context.Context) (*ModelUpdate, error) {
session.Full = true
}
var screens []*ScreenType
query = `SELECT * FROM screen ORDER BY screenidx`
query = `SELECT * FROM screen WHERE NOT closed ORDER BY screenidx`
tx.SelectWrap(&screens, query)
screenMap := make(map[string][]*ScreenType)
for _, screen := range screens {
@ -352,6 +353,7 @@ func GetWindowById(ctx context.Context, sessionId string, windowId string) (*Win
return rtnWindow, err
}
// includes closed screens
func GetSessionScreens(ctx context.Context, sessionId string) ([]*ScreenType, error) {
var rtn []*ScreenType
txErr := WithTx(ctx, func(tx *TxWrap) error {
@ -362,6 +364,16 @@ func GetSessionScreens(ctx context.Context, sessionId string) ([]*ScreenType, er
return rtn, txErr
}
func GetAllSessionScreens(ctx context.Context, sessionId string) ([]*ScreenType, error) {
var rtn []*ScreenType
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * FROM screen WHERE sessionid = ? ORDER BY closed, screenidx`
tx.SelectWrap(&rtn, query, sessionId)
return nil
})
return rtn, txErr
}
func GetSessionById(ctx context.Context, id string) (*SessionType, error) {
allSessionsUpdate, err := GetAllSessions(ctx)
if err != nil {
@ -436,7 +448,7 @@ func InsertSessionWithName(ctx context.Context, sessionName string, activate boo
func SetActiveSessionId(ctx context.Context, sessionId string) error {
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT sessionid FROM session WHERE sessionid = ?`
query := `SELECT sessionid FROM session WHERE sessionid = ? AND NOT closed`
if !tx.Exists(query, sessionId) {
return fmt.Errorf("cannot switch to session, not found")
}
@ -447,6 +459,16 @@ func SetActiveSessionId(ctx context.Context, sessionId string) error {
return txErr
}
func GetActiveSessionId(ctx context.Context) (string, error) {
var rtnId string
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT activesessionid FROM client`
rtnId = tx.GetString(query)
return nil
})
return rtnId, txErr
}
func SetWinSize(ctx context.Context, winSize ClientWinSizeType) error {
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `UPDATE client SET winsize = ?`
@ -492,7 +514,7 @@ func fmtUniqueName(name string, defaultFmtStr string, startIdx int, strs []strin
func InsertScreen(ctx context.Context, sessionId string, origScreenName string, activate bool) (UpdatePacket, error) {
var newScreenId string
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT sessionid FROM session WHERE sessionid = ?`
query := `SELECT sessionid FROM session WHERE sessionid = ? AND NOT closed`
if !tx.Exists(query, sessionId) {
return fmt.Errorf("cannot create screen, no session found")
}
@ -501,8 +523,8 @@ func InsertScreen(ctx context.Context, sessionId string, origScreenName string,
return fmt.Errorf("cannot create screen, no local remote found")
}
newWindowId := txCreateWindow(tx, sessionId, RemotePtrType{RemoteId: remoteId})
maxScreenIdx := tx.GetInt(`SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ?`, sessionId)
screenNames := tx.SelectStrings(`SELECT name FROM screen WHERE sessionid = ?`, sessionId)
maxScreenIdx := tx.GetInt(`SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ? AND NOT closed`, sessionId)
screenNames := tx.SelectStrings(`SELECT name FROM screen WHERE sessionid = ? AND NOT closed`, sessionId)
screenName := fmtUniqueName(origScreenName, "s%d", maxScreenIdx+1, screenNames)
newScreenId = scbase.GenPromptUUID()
query = `INSERT INTO screen (sessionid, screenid, name, activewindowid, screenidx, screenopts, ownerid, sharemode, incognito, closed) VALUES (?, ?, ?, ?, ?, ?, '', 'local', 0, 0)`
@ -810,7 +832,7 @@ func getNextId(ids []string, delId string) string {
func SwitchScreenById(ctx context.Context, sessionId string, screenId string) (UpdatePacket, error) {
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ?`
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? AND NOT closed`
if !tx.Exists(query, sessionId, screenId) {
return fmt.Errorf("cannot switch to screen, screen=%s does not exist in session=%s", screenId, sessionId)
}
@ -826,12 +848,66 @@ func SwitchScreenById(ctx context.Context, sessionId string, screenId string) (U
func CleanWindows() {
}
func CloseScreen(ctx context.Context, sessionId string, screenId string) (UpdatePacket, error) {
var newActiveScreenId string
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ?`
if !tx.Exists(query, sessionId, screenId) {
return fmt.Errorf("cannot close screen (not found)")
}
query = `SELECT closed FROM screen WHERE sessionid = ? AND screenid = ?`
closeVal := tx.GetBool(query, sessionId, screenId)
if closeVal {
return nil
}
query = `SELECT count(*) FROM screen WHERE sessionid = ? AND NOT closed`
numScreens := tx.GetInt(query, sessionId)
if numScreens <= 1 {
return fmt.Errorf("cannot close the last screen in a session")
}
query = `UPDATE screen SET closed = 1, screenidx = 0 WHERE sessionid = ? AND screenid = ?`
tx.ExecWrap(query, sessionId, screenId)
isActive := tx.Exists(`SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ?`, sessionId, screenId)
if isActive {
screenIds := tx.SelectStrings(`SELECT screenid FROM screen WHERE sessionid = ? AND NOT closed ORDER BY screenidx`, sessionId)
nextId := getNextId(screenIds, screenId)
tx.ExecWrap(`UPDATE session SET activescreenid = ? WHERE sessionid = ?`, nextId, sessionId)
newActiveScreenId = nextId
}
return nil
})
if txErr != nil {
return nil, txErr
}
update, session := MakeSingleSessionUpdate(sessionId)
session.ActiveScreenId = newActiveScreenId
session.Screens = append(session.Screens, &ScreenType{SessionId: sessionId, ScreenId: screenId, Remove: true})
return update, nil
}
func UnCloseScreen(ctx context.Context, sessionId string, screenId string) error {
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? AND closed`
if !tx.Exists(query, sessionId, screenId) {
return fmt.Errorf("cannot re-open screen (not found or not closed)")
}
origScreenName := tx.GetString(`SELECT name FROM screen WHERE sessionid = ? AND screenid = ?`, sessionId, screenId)
maxScreenIdx := tx.GetInt(`SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ? AND NOT closed`, sessionId)
screenNames := tx.SelectStrings(`SELECT name FROM screen WHERE sessionid = ? AND NOT closed`, sessionId)
newScreenName := fmtUniqueName(origScreenName, "s-%d", 2, screenNames)
query = `UPDATE screen SET closed = 0, screenidx = ?, name = ? WHERE sessionid = ? AND screenid = ?`
tx.ExecWrap(query, maxScreenIdx+1, newScreenName, sessionId, screenId)
return nil
})
return txErr
}
func DeleteScreen(ctx context.Context, sessionId string, screenId string) (UpdatePacket, error) {
var newActiveScreenId string
txErr := WithTx(ctx, func(tx *TxWrap) error {
isActive := tx.Exists(`SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ?`, sessionId, screenId)
if isActive {
screenIds := tx.SelectStrings(`SELECT screenid FROM screen WHERE sessionid = ? ORDER BY screenidx`, sessionId)
screenIds := tx.SelectStrings(`SELECT screenid FROM screen WHERE sessionid = ? AND NOT closed ORDER BY screenidx`, sessionId)
nextId := getNextId(screenIds, screenId)
tx.ExecWrap(`UPDATE session SET activescreenid = ? WHERE sessionid = ?`, nextId, sessionId)
newActiveScreenId = nextId
@ -1068,7 +1144,7 @@ func SetScreenName(ctx context.Context, sessionId string, screenId string, name
if !tx.Exists(query, sessionId, screenId) {
return fmt.Errorf("screen does not exist")
}
query = `SELECT screenid FROM screen WHERE sessionid = ? AND name = ?`
query = `SELECT screenid FROM screen WHERE sessionid = ? AND name = ? AND NOT closed`
dupScreenId := tx.GetString(query, sessionId, name)
if dupScreenId == screenId {
return nil
@ -1169,8 +1245,10 @@ func GetSessionStats(ctx context.Context, sessionId string) (*SessionStatsType,
if !tx.Exists(query, sessionId) {
return fmt.Errorf("not found")
}
query = `SELECT count(*) FROM screen WHERE sessionid = ?`
query = `SELECT count(*) FROM screen WHERE sessionid = ? AND NOT closed`
rtn.NumScreens = tx.GetInt(query, sessionId)
query = `SELECT count(*) FROM screen WHERE sessionid = ? AND closed`
rtn.NumClosedScreens = tx.GetInt(query, sessionId)
query = `SELECT count(*) FROM window WHERE sessionid = ?`
rtn.NumWindows = tx.GetInt(query, sessionId)
query = `SELECT count(*) FROM line WHERE sessionid = ?`

View File

@ -162,12 +162,13 @@ type SessionType struct {
}
type SessionStatsType struct {
SessionId string `json:"sessionid"`
NumScreens int `json:"numscreens"`
NumWindows int `json:"numwindows"`
NumLines int `json:"numlines"`
NumCmds int `json:"numcmds"`
DiskStats SessionDiskSizeType `json:"diskstats"`
SessionId string `json:"sessionid"`
NumScreens int `json:"numscreens"`
NumClosedScreens int `json:"numclosedscreens"`
NumWindows int `json:"numwindows"`
NumLines int `json:"numlines"`
NumCmds int `json:"numcmds"`
DiskStats SessionDiskSizeType `json:"diskstats"`
}
type WindowOptsType struct {
@ -592,9 +593,10 @@ type LineType struct {
}
type ResolveItem struct {
Name string
Num int
Id string
Name string
Num int
Id string
Hidden bool
}
type SSHOpts struct {

View File

@ -105,6 +105,12 @@ func (tx *TxWrap) GetString(query string, args ...interface{}) string {
return rtnStr
}
func (tx *TxWrap) GetBool(query string, args ...interface{}) bool {
var rtnBool bool
tx.GetWrap(&rtnBool, query, args...)
return rtnBool
}
func (tx *TxWrap) SelectStrings(query string, args ...interface{}) []string {
var rtnArr []string
tx.SelectWrap(&rtnArr, query, args...)