add simple history expansion

This commit is contained in:
sawka
2023-03-20 12:19:48 -07:00
parent 1742a3dbbc
commit cec75c0d5b
3 changed files with 110 additions and 11 deletions
+11 -11
View File
@@ -95,7 +95,7 @@ CREATE TABLE cmd (
runout json NOT NULL,
rtnstate boolean NOT NULL,
rtnbasehash varchar(36) NOT NULL,
rtndiffhasharr json NOT NULL,
rtndiffhasharr json NOT NULL, screenid varchar(36) NOT NULL DEFAULT '',
PRIMARY KEY (sessionid, cmdid)
);
CREATE TABLE history (
@@ -113,7 +113,16 @@ CREATE TABLE history (
cmdstr text NOT NULL,
ismetacmd boolean,
incognito boolean
);
, linenum int NOT NULL DEFAULT 0);
CREATE TABLE activity (
day varchar(20) PRIMARY KEY,
uploaded boolean NOT NULL,
tdata json NOT NULL,
tzname varchar(50) NOT NULL,
tzoffset int NOT NULL,
clientversion varchar(20) NOT NULL,
clientarch varchar(20) NOT NULL
, buildtime varchar(20) NOT NULL DEFAULT '-', osrelease varchar(20) NOT NULL DEFAULT '-');
CREATE TABLE bookmark (
bookmarkid varchar(36) PRIMARY KEY,
createdts bigint NOT NULL,
@@ -134,15 +143,6 @@ CREATE TABLE bookmark_cmd (
cmdid varchar(36) NOT NULL,
PRIMARY KEY (bookmarkid, sessionid, cmdid)
);
CREATE TABLE activity (
day varchar(20) PRIMARY KEY,
uploaded boolean NOT NULL,
tdata json NOT NULL,
tzname varchar(50) NOT NULL,
tzoffset int NOT NULL,
clientversion varchar(50) NOT NULL,
clientarch varchar(50) NOT NULL
, buildtime varchar(20) NOT NULL DEFAULT '-', osrelease varchar(20) NOT NULL DEFAULT '-');
CREATE TABLE playbook (
playbookid varchar(36) PRIMARY KEY,
playbookname varchar(100) NOT NULL,
+83
View File
@@ -47,6 +47,7 @@ const DefaultPTERM = "MxM"
const MaxCommandLen = 4096
const MaxSignalLen = 12
const MaxSignalNum = 64
const MaxEvalDepth = 5
var ColorNames = []string{"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", "orange"}
var RemoteColorNames = []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white", "orange"}
@@ -87,6 +88,7 @@ var sigNameRe = regexp.MustCompile("^((SIG[A-Z0-9]+)|(\\d+))$")
type contextType string
var historyContextKey = contextType("history")
var depthContextKey = contextType("depth")
type SetVarScope struct {
ScopeName string
@@ -323,6 +325,68 @@ func resolveNonNegInt(arg string, def int) (int, error) {
return ival, nil
}
var histExpansionRe = regexp.MustCompile(`^!(\d+)$`)
func doCmdHistoryExpansion(ctx context.Context, ids resolvedIds, cmdStr string) (string, error) {
if !strings.HasPrefix(cmdStr, "!") {
return "", nil
}
if strings.HasPrefix(cmdStr, "! ") {
return "", nil
}
if cmdStr == "!!" {
return doHistoryExpansion(ctx, ids, -1)
}
if strings.HasPrefix(cmdStr, "!-") {
return "", fmt.Errorf("prompt does not support negative history offsets, use a stable positive history offset instead: '![linenum]'")
}
m := histExpansionRe.FindStringSubmatch(cmdStr)
if m == nil {
return "", fmt.Errorf("unsupported history substitution, can use '!!' or '![linenum]'")
}
ival, err := strconv.Atoi(m[1])
if err != nil {
return "", fmt.Errorf("invalid history expansion")
}
return doHistoryExpansion(ctx, ids, ival)
}
func doHistoryExpansion(ctx context.Context, ids resolvedIds, hnum int) (string, error) {
if hnum == 0 {
return "", fmt.Errorf("invalid history expansion, cannot expand line number '0'")
}
if hnum < -1 {
return "", fmt.Errorf("invalid history expansion, cannot expand negative history offsets")
}
foundHistoryNum := hnum
if hnum == -1 {
var err error
foundHistoryNum, err = sstore.GetLastHistoryLineNum(ctx, ids.ScreenId)
if err != nil {
return "", fmt.Errorf("cannot expand history, error finding last history item: %v", err)
}
if foundHistoryNum == 0 {
return "", fmt.Errorf("cannot expand history, no last history item")
}
}
hitem, err := sstore.GetHistoryItemByLineNum(ctx, ids.ScreenId, foundHistoryNum)
if err != nil {
return "", fmt.Errorf("cannot get history item '%d': %v", foundHistoryNum, err)
}
if hitem == nil {
return "", fmt.Errorf("cannot expand history, history item '%d' not found", foundHistoryNum)
}
return hitem.CmdStr, nil
}
func getEvalDepth(ctx context.Context) int {
depthVal := ctx.Value(depthContextKey)
if depthVal == nil {
return 0
}
return depthVal.(int)
}
func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_RemoteConnected)
if err != nil {
@@ -333,6 +397,22 @@ func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.U
}
renderer := pk.Kwargs["renderer"]
cmdStr := firstArg(pk)
expandedCmdStr, err := doCmdHistoryExpansion(ctx, ids, cmdStr)
if err != nil {
return nil, err
}
if expandedCmdStr != "" {
newPk := scpacket.MakeFeCommandPacket()
newPk.MetaCmd = "eval"
newPk.Args = []string{expandedCmdStr}
newPk.Kwargs = pk.Kwargs
newPk.RawStr = pk.RawStr
newPk.UIContext = pk.UIContext
newPk.Interactive = pk.Interactive
evalDepth := getEvalDepth(ctx)
ctxWithDepth := context.WithValue(ctx, depthContextKey, evalDepth+1)
return EvalCommand(ctxWithDepth, newPk)
}
isRtnStateCmd := IsReturnStateCommand(cmdStr)
// runPacket.State is set in remote.RunCommand()
runPacket := packet.MakeRunPacket()
@@ -410,6 +490,9 @@ func EvalCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.
// fall through (non-fatal error)
}
}
if getEvalDepth(ctx) > MaxEvalDepth {
return nil, fmt.Errorf("alias/history expansion max-depth exceeded")
}
var historyContext historyContextType
ctxWithHistory := context.WithValue(ctx, historyContextKey, &historyContext)
var update sstore.UpdatePacket
+16
View File
@@ -341,6 +341,22 @@ func GetHistoryItems(ctx context.Context, opts HistoryQueryOpts) (*HistoryQueryR
return rtn, nil
}
func GetHistoryItemByLineNum(ctx context.Context, screenId string, lineNum int) (*HistoryItemType, error) {
return WithTxRtn(ctx, func(tx *TxWrap) (*HistoryItemType, error) {
query := `SELECT * FROM history WHERE screenid = ? AND linenum = ?`
hitem := GetMapGen[*HistoryItemType](tx, query, screenId, lineNum)
return hitem, nil
})
}
func GetLastHistoryLineNum(ctx context.Context, screenId string) (int, error) {
return WithTxRtn(ctx, func(tx *TxWrap) (int, error) {
query := `SELECT COALESCE(max(linenum), 0) FROM history WHERE screenid = ?`
maxLineNum := tx.GetInt(query, screenId)
return maxLineNum, nil
})
}
// includes archived sessions
func GetBareSessions(ctx context.Context) ([]*SessionType, error) {
var rtn []*SessionType