shithub: hell

Download patch

ref: 78ac6a671de03d1101955f2f3b390bfae7fc4673
parent: 58a0909ef0e68e01874fad0e4fb01fbe6c47a2d6
author: penny <penny@limitedideas.org>
date: Mon Sep 8 20:19:44 EDT 2025

Modal multi line mode

--- a/hellclient.go
+++ b/hellclient.go
@@ -7,6 +7,7 @@
 	"strings"
 	"sync"
 	"time"
+	"os"
 
 	"github.com/ergochat/readline"
 	"github.com/mattn/go-mastodon"
@@ -25,19 +26,20 @@
 
 type Hellclient struct {
 	//if you're gonna touch or read anything here lock the mutex with hc.lock()
-	isPaused    bool
-	rl          *readline.Instance
-	client      *mastodon.Client
-	currentuser *mastodon.Account
-	dispatch    chan *mastodon.Toot
-	block       sync.Mutex
-	recentpost  *mastodon.Status
-	preferences *Hellprefs
+	isPaused      bool
+	rl            *readline.Instance
+	client        *mastodon.Client
+	currentuser   *mastodon.Account
+	dispatch      chan *mastodon.Toot
+	block         sync.Mutex
+	recentpost    *mastodon.Status
+	preferences   *Hellprefs
+	multiLineMode bool
 
 	//Global status map for status indexes
 	//Needs to be converted to a postref struct
 	homeMap map[string]*mastodon.Status
-	homeref      *postref
+	homeref *postref
 	//Contextual indexes for commands
 	ctxref       *postref
 	urlMap       map[string][]string
@@ -49,12 +51,59 @@
 	apidelay time.Duration
 }
 
+//Use this to make private versions of runes to stop default behavior
+func toPUA(r rune) rune {
+	const (
+		puaStart = 0xE000
+		puaEnd   = 0xF8FF
+		puaSize  = puaEnd - puaStart + 1
+	)
+	return rune(int32(puaStart) + (int32(r) % int32(puaSize)))
+}
+
 func NewHellclient() (*Hellclient, error) {
-	rl, err := readline.New("Hell> ")
-	rl.CaptureExitSignal()
+	var hc Hellclient
+	config := &readline.Config{
+		Prompt: "Hell> ",
+		FuncFilterInputRune: func(r rune) (rune, bool) {
+			if r == readline.CharCtrlJ { // ctrl-j
+				hc.multiLineMode = !hc.multiLineMode
+				hc.updatePrompt()
+				return r, false
+			}
+			if r == readline.CharKill {
+				return toPUA(r), true
+			}
+			if r == readline.CharEnter && hc.multiLineMode {
+				return toPUA(r), true
+			}
+			return r, true
+		},
+		Listener: func(line []rune, pos int, key rune) ([]rune, int, bool) {
+			if key == toPUA(readline.CharEnter) && hc.multiLineMode {
+				// handle multi-line input
+				line = line[:len(line) - 1]
+				line = append(line, '\n')
+				return line, pos, true
+			}
+			
+			//If we get an interupt just clear the line if it's not empty
+			if key == toPUA(readline.CharKill) {
+				if len(line) > 1 {
+					return nil, 0, true
+				}
+				os.Exit(0)
+				return []rune("Goodbye!\n"), 0, true
+			}
+			
+			return nil, 0, false
+		},
+	}
+	rl, err := readline.NewEx(config)
 	if err != nil {
 		return nil, err
 	}
+
 	account, err := loadConfig()
 	if err != nil {
 		return nil, err
@@ -67,8 +116,6 @@
 
 	dispatch := make(chan *mastodon.Toot, 15)
 
-	var hc Hellclient
-
 	defer func() {
 		//Got some stuff to do when we're done
 		hc.updatePrompt()
@@ -83,12 +130,12 @@
 		ref:     "a",
 		postmap: homeMap,
 	}
-	
+
 	homeref := &postref{
 		ref:     "a",
 		postmap: homeMap,
 	}
-	
+
 	debugMap := make(map[string]interface{})
 	urlMap := make(map[string][]string)
 	prefs := &Hellprefs{apidelay: time.Second * 3}
@@ -113,6 +160,9 @@
 	unread, err := hc.client.GetUnreadNotifications(context.Background(), nil, nil, 0)
 	if err == nil {
 		sb.WriteString(fmt.Sprintf("ur:%v ", unread.Count))
+	}
+	if hc.multiLineMode {
+		sb.WriteString("MULTI-LINE ")
 	}
 	if hc.isPaused {
 		sb.WriteString("STREAMING PAUSED ")
--- a/main.go
+++ b/main.go
@@ -9,8 +9,8 @@
 	"strconv"
 	"strings"
 
-	"github.com/mattn/go-mastodon"
 	"github.com/k3a/html2text"
+	"github.com/mattn/go-mastodon"
 )
 
 func main() {
@@ -35,7 +35,6 @@
 	homeMap := hc.homeMap
 	debugMap := hc.debugMap
 	lastindex := ""
-	interupted := false //use this to check if we were interupted last turn
 	recentpost := &hc.recentpost
 
 	if err != nil {
@@ -50,23 +49,10 @@
 			//If we get an interupt error, we'll return to read the next line
 			//If the next line is empty, exit the program
 			//If it isn't empty, we were just clearing the line
-			if interupted {
-				if len(line) == 0 {
-					os.Exit(0)
-				} else {
-					interupted = false
-					return
-				}
-			}
+
 			if err != nil {
-				if err == EOF || err == ErrInterrupt {
-					interupted = true
-					return
-				}
-				if err != nil {
-					fmt.Println("Error:", err)
-					return
-				}
+				fmt.Println("Error:", err)
+				return
 			}
 			command, arguments := processInput(line)
 
--- a/mastodon.go
+++ b/mastodon.go
@@ -84,7 +84,7 @@
 	preformats := make(map[string]string)
 
 	for node := range doc.Descendants() {
-		if node.Data == "pre" && node.FirstChild != nil {
+		if (node.Data == "pre" || node.Data == "") && node.FirstChild != nil {
 			preformats[fmt.Sprintf("%p%p", hc, node.FirstChild)] = node.FirstChild.Data
 			node.FirstChild.Data = fmt.Sprintf("%p%p", hc, node.FirstChild)
 		}
@@ -128,9 +128,9 @@
 	if err != nil {
 		return "", nil
 	}
-	
+
 	renderedPlainText := rendered.String()
-	
+
 	return renderedPlainText, preformats
 }
 
--- a/notifications.go
+++ b/notifications.go
@@ -25,11 +25,11 @@
 	if err != nil {
 		return
 	}
-	
+
 	marker := markers["notifications"]
 	marker.ID = ID
 	marker.Timeline = "notifications"
-	
+
 	err = hc.client.SetTimelineMarkers(context.Background(), &[]mastodon.Marker{*marker})
 	return
 }
--