diff --git a/pkg/cmdrunner/cmdrunner.go b/pkg/cmdrunner/cmdrunner.go index 7fba1f59..5712b74a 100644 --- a/pkg/cmdrunner/cmdrunner.go +++ b/pkg/cmdrunner/cmdrunner.go @@ -219,6 +219,10 @@ func init() { // CodeEditCommand is overloaded to do codeedit and codeview registerCmdFn("codeedit", CodeEditCommand) registerCmdFn("codeview", CodeEditCommand) + + registerCmdFn("imageview", ImageViewCommand) + registerCmdFn("mdview", MarkdownViewCommand) + registerCmdFn("markdownview", MarkdownViewCommand) } func getValidCommands() []string { @@ -3152,6 +3156,80 @@ func CodeEditCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sst return update, nil } +func ImageViewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { + if len(pk.Args) == 0 { + return nil, fmt.Errorf("%s requires an argument (file name)", GetCmdStr(pk)) + } + // TODO more error checking on filename format? + if pk.Args[0] == "" { + return nil, fmt.Errorf("%s argument cannot be empty", GetCmdStr(pk)) + } + ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_RemoteConnected) + if err != nil { + return nil, err + } + outputStr := fmt.Sprintf("%s %q", GetCmdStr(pk), pk.Args[0]) + cmd, err := makeStaticCmd(ctx, GetCmdStr(pk), ids, pk.GetRawStr(), []byte(outputStr)) + if err != nil { + // TODO tricky error since the command was a success, but we can't show the output + return nil, err + } + update, err := addLineForCmd(ctx, "/"+GetCmdStr(pk), false, ids, cmd, "image") + if err != nil { + // TODO tricky error since the command was a success, but we can't show the output + return nil, err + } + // set the line state + // TODO turn these strings into constants + lineState := make(map[string]any) + lineState["prompt:source"] = "file" + lineState["prompt:file"] = pk.Args[0] + err = sstore.UpdateLineState(ctx, ids.ScreenId, update.Line.LineId, lineState) + if err != nil { + return nil, fmt.Errorf("%s error updating line state: %v", GetCmdStr(pk), err) + } + update.Line.LineState = lineState + update.Interactive = pk.Interactive + return update, nil +} + +func MarkdownViewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { + if len(pk.Args) == 0 { + return nil, fmt.Errorf("%s requires an argument (file name)", GetCmdStr(pk)) + } + // TODO more error checking on filename format? + if pk.Args[0] == "" { + return nil, fmt.Errorf("%s argument cannot be empty", GetCmdStr(pk)) + } + ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_RemoteConnected) + if err != nil { + return nil, err + } + outputStr := fmt.Sprintf("%s %q", GetCmdStr(pk), pk.Args[0]) + cmd, err := makeStaticCmd(ctx, GetCmdStr(pk), ids, pk.GetRawStr(), []byte(outputStr)) + if err != nil { + // TODO tricky error since the command was a success, but we can't show the output + return nil, err + } + update, err := addLineForCmd(ctx, "/"+GetCmdStr(pk), false, ids, cmd, "markdown") + if err != nil { + // TODO tricky error since the command was a success, but we can't show the output + return nil, err + } + // set the line state + // TODO turn these strings into constants + lineState := make(map[string]any) + lineState["prompt:source"] = "file" + lineState["prompt:file"] = pk.Args[0] + err = sstore.UpdateLineState(ctx, ids.ScreenId, update.Line.LineId, lineState) + if err != nil { + return nil, fmt.Errorf("%s error updating line state: %v", GetCmdStr(pk), err) + } + update.Line.LineState = lineState + update.Interactive = pk.Interactive + return update, nil +} + func EditTestCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { if len(pk.Args) == 0 { return nil, fmt.Errorf("/edit:test requires an argument (file name)") diff --git a/pkg/cmdrunner/shparse.go b/pkg/cmdrunner/shparse.go index d21e4e56..3fa80c7a 100644 --- a/pkg/cmdrunner/shparse.go +++ b/pkg/cmdrunner/shparse.go @@ -28,6 +28,9 @@ var BareMetaCmds = []BareMetaCmdDecl{ BareMetaCmdDecl{"reset", "reset"}, BareMetaCmdDecl{"codeedit", "codeedit"}, BareMetaCmdDecl{"codeview", "codeview"}, + BareMetaCmdDecl{"imageview", "imageview"}, + BareMetaCmdDecl{"markdownview", "markdownview"}, + BareMetaCmdDecl{"mdview", "markdownview"}, } const ( @@ -241,6 +244,27 @@ func EvalBracketArgs(origCmdStr string) (map[string]string, string, error) { return rtn, restStr, nil } +func unescapeBackSlashes(s string) string { + if strings.Index(s, "\\") == -1 { + return s + } + var newStr []rune + var lastSlash bool + for _, r := range s { + if lastSlash { + lastSlash = false + newStr = append(newStr, r) + continue + } + if r == '\\' { + lastSlash = true + continue + } + newStr = append(newStr, r) + } + return string(newStr) +} + func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType) (*scpacket.FeCommandPacketType, error) { if len(origPk.Args) == 0 { return nil, fmt.Errorf("empty command (no fields)") @@ -299,7 +323,7 @@ func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType) rtnPk.Kwargs[varName] = varVal continue } - rtnPk.Args = append(rtnPk.Args, literalVal) + rtnPk.Args = append(rtnPk.Args, unescapeBackSlashes(literalVal)) } if resolveBool(rtnPk.Kwargs["dump"], false) { DumpPacket(rtnPk)