Add logs buffer

This commit is contained in:
2026-05-04 22:46:21 +03:00
parent e4462f9674
commit 999f1953f9
11 changed files with 180 additions and 53 deletions
+16 -1
View File
@@ -22,6 +22,7 @@ type Buffer struct {
Selection *Selection
canSave bool
canEdit bool
filetype string
filename string
}
@@ -59,7 +60,7 @@ func drawBuffer(window *Window) {
parsedSyntaxes, err := HighlightString(string(buffer.GetContentsAsString()), buffer.filetype)
if err != nil {
window.PrintMessage(fmt.Sprintf("Could not parse regular expression in '%s' syntax: %s", buffer.filetype, err))
window.PrintMessage(fmt.Sprintf("Could not parse regular expression in '%s' syntax: %s", buffer.filetype, err), TYPER_MESSAGE_ERROR)
}
i := -1
@@ -88,6 +89,14 @@ func drawBuffer(window *Window) {
style = style.Foreground(CurrentStyle.SyntaxVariable)
case "string":
style = style.Foreground(CurrentStyle.SyntaxString)
// Special types for typer logs
case "info":
style = style.Foreground(CurrentStyle.SyntaxInfo)
case "warning":
style = style.Foreground(CurrentStyle.SyntaxWarning)
case "error":
style = style.Foreground(CurrentStyle.SyntaxError)
}
break
@@ -168,6 +177,10 @@ func (buffer *Buffer) Load() error {
// Set buffer filetype
for _, syntax := range AvailableSyntaxes {
if syntax.Filenames == "" {
continue
}
if ok, _ := regexp.MatchString(syntax.Filenames, buffer.filename); ok {
buffer.filetype = syntax.Filetype
}
@@ -641,6 +654,7 @@ func CreateFileBuffer(filename string, openNonExistentFile bool) (*Buffer, error
Contents: make([]runestring.RuneString, 1),
CursorPos: Position{0, 0},
canSave: true,
canEdit: true,
filename: abs,
}
@@ -664,6 +678,7 @@ func CreateBuffer(bufferName string) (*Buffer, error) {
Contents: make([]runestring.RuneString, 1),
CursorPos: Position{0, 0},
canSave: true,
canEdit: true,
filename: "",
}
+47 -42
View File
@@ -29,7 +29,7 @@ func initCommands() {
selectionEnd: Position{len(window.CurrentBuffer.Contents) - 1, len(lastLine) - 1},
}
window.PrintMessage("Selected all text.")
window.PrintMessage("Selected all text", TYPER_MESSAGE_INFO)
},
}
@@ -44,9 +44,9 @@ func initCommands() {
// Send appropriate message and remove text depending on copying method
if copyingMethod == 0 {
window.PrintMessage("Copied line to clipboard.")
window.PrintMessage("Copied line to clipboard", TYPER_MESSAGE_INFO)
} else {
window.PrintMessage("Copied selection to clipboard.")
window.PrintMessage("Copied selection to clipboard", TYPER_MESSAGE_INFO)
}
},
}
@@ -62,9 +62,9 @@ func initCommands() {
// Send appropriate message depending on copying method
if copyingMethod == 0 {
window.PrintMessage("Copied line to clipboard.")
window.PrintMessage("Copied line to clipboard", TYPER_MESSAGE_INFO)
} else {
window.PrintMessage("Copied selection to clipboard. ")
window.PrintMessage("Copied selection to clipboard", TYPER_MESSAGE_INFO)
}
},
}
@@ -72,9 +72,14 @@ func initCommands() {
pasteCmd := Command{
cmd: "paste",
run: func(window *Window, args ...string) {
if !window.CurrentBuffer.canEdit {
window.PrintMessage(fmt.Sprintf("Buffer '%s' is read-only", window.CurrentBuffer.Name), TYPER_MESSAGE_WARNING)
return
}
if len(window.Clipboard) != 0 {
window.CurrentBuffer.PasteText(window, window.Clipboard)
window.PrintMessage("Pasted text to buffer. ")
window.PrintMessage("Pasted text to buffer", TYPER_MESSAGE_INFO)
}
},
}
@@ -83,7 +88,7 @@ func initCommands() {
cmd: "save",
run: func(window *Window, args ...string) {
if !window.CurrentBuffer.canSave {
window.PrintMessage("Cannot save buffer!")
window.PrintMessage("Cannot save buffer", TYPER_MESSAGE_ERROR)
return
}
@@ -100,7 +105,7 @@ func initCommands() {
input = <-inputChannel
if strings.TrimSpace(input) == "" {
window.PrintMessage("No save location was given!")
window.PrintMessage("No save location was given", TYPER_MESSAGE_ERROR)
return
}
@@ -108,12 +113,12 @@ func initCommands() {
err := window.CurrentBuffer.Save()
if err != nil {
window.PrintMessage(fmt.Sprintf("Could not save file: %s", err))
window.PrintMessage(fmt.Sprintf("Could not save file: %s", err), TYPER_MESSAGE_ERROR)
window.CurrentBuffer.filename = ""
return
}
window.PrintMessage("File saved.")
window.PrintMessage("File saved", TYPER_MESSAGE_INFO)
}()
},
autocomplete: func(window *Window, args ...string) []string {
@@ -133,16 +138,16 @@ func initCommands() {
}
if openBuffer := GetOpenFileBuffer(input); openBuffer != nil {
window.PrintMessage(fmt.Sprintf("File already open! Switching to buffer: %s", openBuffer.Name))
window.PrintMessage(fmt.Sprintf("File already open! Switching to buffer: %s", openBuffer.Name), TYPER_MESSAGE_INFO)
window.CurrentBuffer = openBuffer
} else {
newBuffer, err := CreateFileBuffer(input, false)
if err != nil {
window.PrintMessage(fmt.Sprintf("Could not open file: %s", err.Error()))
window.PrintMessage(fmt.Sprintf("Could not open file: %s", err.Error()), TYPER_MESSAGE_WARNING)
return
}
window.PrintMessage(fmt.Sprintf("Opening file at: %s", newBuffer.filename))
window.PrintMessage(fmt.Sprintf("Opening file at: %s", newBuffer.filename), TYPER_MESSAGE_INFO)
window.CurrentBuffer = newBuffer
}
}()
@@ -157,7 +162,7 @@ func initCommands() {
log.Fatalf("Could not reload buffer: %s", err)
}
window.PrintMessage("Buffer reloaded.")
window.PrintMessage("Buffer reloaded", TYPER_MESSAGE_INFO)
},
}
@@ -174,9 +179,9 @@ func initCommands() {
pos := window.CurrentBuffer.FindSubstring(input, window.CurrentBuffer.CursorPos)
if pos.X >= 0 && pos.Y >= 0 {
window.CurrentBuffer.CursorPos = pos
window.PrintMessage("Match found.")
window.PrintMessage("Match found", TYPER_MESSAGE_INFO)
} else {
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer!", string(input)))
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer", string(input)), TYPER_MESSAGE_WARNING)
}
return
@@ -193,9 +198,9 @@ func initCommands() {
pos := window.CurrentBuffer.FindSubstring(input, window.CurrentBuffer.CursorPos)
if pos.X >= 0 && pos.Y >= 0 {
window.CurrentBuffer.CursorPos = pos
window.PrintMessage("Match found.")
window.PrintMessage("Match found", TYPER_MESSAGE_INFO)
} else {
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer!", string(input)))
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer", string(input)), TYPER_MESSAGE_WARNING)
}
}()
},
@@ -215,9 +220,9 @@ func initCommands() {
pos := window.CurrentBuffer.FindAndReplaceSubstring(findStr, replaceStr, window.CurrentBuffer.CursorPos)
if pos.X >= 0 && pos.Y >= 0 {
window.CurrentBuffer.CursorPos = pos
window.PrintMessage("Match replaced successfully.")
window.PrintMessage("Match replaced successfully", TYPER_MESSAGE_INFO)
} else {
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer!", string(findStr)))
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer", string(findStr)), TYPER_MESSAGE_WARNING)
}
return
@@ -236,9 +241,9 @@ func initCommands() {
pos := window.CurrentBuffer.FindAndReplaceSubstring(findStr, replaceStr, window.CurrentBuffer.CursorPos)
if pos.X >= 0 && pos.Y >= 0 {
window.CurrentBuffer.CursorPos = pos
window.PrintMessage("Match replaced successfully.")
window.PrintMessage("Match replaced successfully", TYPER_MESSAGE_INFO)
} else {
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer!", string(findStr)))
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer", string(findStr)), TYPER_MESSAGE_WARNING)
}
}()
},
@@ -257,9 +262,9 @@ func initCommands() {
replacements := window.CurrentBuffer.FindAndReplaceAll(findStr, replaceStr)
if replacements > 0 {
window.PrintMessage(fmt.Sprintf("Replaced all %d matches successfully.", replacements))
window.PrintMessage(fmt.Sprintf("Replaced all %d matches successfully", replacements), TYPER_MESSAGE_INFO)
} else {
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer!", string(findStr)))
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer", string(findStr)), TYPER_MESSAGE_WARNING)
}
return
@@ -277,9 +282,9 @@ func initCommands() {
replacements := window.CurrentBuffer.FindAndReplaceAll(findStr, replaceStr)
if replacements > 0 {
window.PrintMessage(fmt.Sprintf("Replaced all %d matches successfully.", replacements))
window.PrintMessage(fmt.Sprintf("Replaced all %d matches successfully", replacements), TYPER_MESSAGE_INFO)
} else {
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer!", string(findStr)))
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer", string(findStr)), TYPER_MESSAGE_WARNING)
}
}()
},
@@ -300,7 +305,7 @@ func initCommands() {
}
window.CurrentBuffer = Buffers[index]
window.PrintMessage(fmt.Sprintf("Set current buffer to '%s'.", window.CurrentBuffer.Name))
window.PrintMessage(fmt.Sprintf("Set current buffer to '%s'", window.CurrentBuffer.Name), TYPER_MESSAGE_INFO)
},
}
@@ -319,7 +324,7 @@ func initCommands() {
}
window.CurrentBuffer = Buffers[index]
window.PrintMessage(fmt.Sprintf("Set current buffer to '%s'.", window.CurrentBuffer.Name))
window.PrintMessage(fmt.Sprintf("Set current buffer to '%s'", window.CurrentBuffer.Name), TYPER_MESSAGE_INFO)
},
}
@@ -335,7 +340,7 @@ func initCommands() {
}
window.CursorMode = CursorModeBuffer
window.PrintMessage(fmt.Sprintf("New buffer created with the name '%s'.", window.CurrentBuffer.Name))
window.PrintMessage(fmt.Sprintf("New buffer created with the name '%s'", window.CurrentBuffer.Name), TYPER_MESSAGE_INFO)
},
}
@@ -354,7 +359,7 @@ func initCommands() {
window.CurrentBuffer = Buffers[bufferIndex]
}
window.CursorMode = CursorModeBuffer
window.PrintMessage("Buffer closed.")
window.PrintMessage("Buffer closed", TYPER_MESSAGE_INFO)
},
}
@@ -383,14 +388,14 @@ func initCommands() {
}
if _, ok := AvailableStyles[input]; !ok {
window.PrintMessage(fmt.Sprintf("Could not set style to '%s'", input))
window.PrintMessage(fmt.Sprintf("Could not set style to '%s'", input), TYPER_MESSAGE_ERROR)
return
}
if ok := SetCurrentStyle(window.screen, input); ok {
window.PrintMessage(fmt.Sprintf("Setting style to '%s'", input))
window.PrintMessage(fmt.Sprintf("Setting style to '%s'", input), TYPER_MESSAGE_INFO)
} else {
window.PrintMessage(fmt.Sprintf("Could not set style to '%s'", input))
window.PrintMessage(fmt.Sprintf("Could not set style to '%s'", input), TYPER_MESSAGE_ERROR)
}
return
@@ -405,14 +410,14 @@ func initCommands() {
}
if _, ok := AvailableStyles[input]; !ok {
window.PrintMessage(fmt.Sprintf("Could not set style to '%s'", input))
window.PrintMessage(fmt.Sprintf("Could not set style to '%s'", input), TYPER_MESSAGE_ERROR)
return
}
if ok := SetCurrentStyle(window.screen, input); ok {
window.PrintMessage(fmt.Sprintf("Setting style to '%s'", input))
window.PrintMessage(fmt.Sprintf("Setting style to '%s'", input), TYPER_MESSAGE_INFO)
} else {
window.PrintMessage(fmt.Sprintf("Could not set style to '%s'", input))
window.PrintMessage(fmt.Sprintf("Could not set style to '%s'", input), TYPER_MESSAGE_ERROR)
}
}()
},
@@ -430,15 +435,15 @@ func initCommands() {
if strings.ToLower(input) == "none" {
window.CurrentBuffer.filetype = ""
window.PrintMessage("Setting filetype to 'none'")
window.PrintMessage("Setting filetype to 'none'", TYPER_MESSAGE_INFO)
return
} else if _, ok := AvailableSyntaxes[input]; !ok {
window.PrintMessage(fmt.Sprintf("Could not set filetype to '%s'", input))
window.PrintMessage(fmt.Sprintf("Could not set filetype to '%s'", input), TYPER_MESSAGE_ERROR)
return
}
window.CurrentBuffer.filetype = input
window.PrintMessage(fmt.Sprintf("Setting filetype to '%s'", input))
window.PrintMessage(fmt.Sprintf("Setting filetype to '%s'", input), TYPER_MESSAGE_INFO)
return
}
@@ -452,12 +457,12 @@ func initCommands() {
}
if _, ok := AvailableSyntaxes[input]; !ok {
window.PrintMessage(fmt.Sprintf("Could not set filetype to '%s'", input))
window.PrintMessage(fmt.Sprintf("Could not set filetype to '%s'", input), TYPER_MESSAGE_ERROR)
return
}
window.CurrentBuffer.filetype = input
window.PrintMessage(fmt.Sprintf("Setting filetype to '%s'", input))
window.PrintMessage(fmt.Sprintf("Setting filetype to '%s'", input), TYPER_MESSAGE_INFO)
}()
},
@@ -579,7 +584,7 @@ func RunCommand(window *Window, cmd string, args ...string) bool {
command.run(window, args...)
return true
} else {
window.PrintMessage(fmt.Sprintf("Could not find command '%s'!", cmd))
window.PrintMessage(fmt.Sprintf("Could not find command '%s'", cmd), TYPER_MESSAGE_ERROR)
return false
}
}
+9 -1
View File
@@ -38,7 +38,7 @@ func main() {
for i, file := range flag.Args() {
b, err := CreateFileBuffer(file, true)
if err != nil {
window.PrintMessage("Could not open file: " + file)
window.PrintMessage("Could not open file: "+file, TYPER_MESSAGE_ERROR)
continue
}
@@ -49,6 +49,14 @@ func main() {
}
}
// Create logs buffer
logsBuffer, err := CreateBuffer("Logs")
if err != nil {
log.Fatalf("Could not create logs buffer")
}
logsBuffer.filetype = "typer_logs"
logsBuffer.canEdit = false
for !window.closed {
window.Draw()
window.ProcessEvents()
+57 -7
View File
@@ -1,20 +1,60 @@
package main
import (
"slices"
"time"
"typer/runestring"
"github.com/gdamore/tcell/v2"
)
type TyperMessageUrgency uint
const (
TYPER_MESSAGE_INFO TyperMessageUrgency = iota
TYPER_MESSAGE_WARNING
TYPER_MESSAGE_ERROR
)
type TyperMessage struct {
timestamp int64
message string
Timestamp int64
Urgency TyperMessageUrgency
Message string
}
var messageLog = make([]TyperMessage, 0)
var lastMessage *TyperMessage
func (window *Window) PrintMessage(message string) {
messageLog = append(messageLog, TyperMessage{timestamp: time.Now().UnixMilli(), message: message})
func (window *Window) PrintMessage(message string, urgency TyperMessageUrgency) {
lastMessage = &TyperMessage{Timestamp: time.Now().UnixMilli(), Message: message, Urgency: urgency}
logsBuffer := GetBufferByName("Logs")
if logsBuffer != nil {
messageToPrint := ""
switch lastMessage.Urgency {
case TYPER_MESSAGE_INFO:
messageToPrint = "[INFO] "
case TYPER_MESSAGE_WARNING:
messageToPrint = "[WARNING] "
case TYPER_MESSAGE_ERROR:
messageToPrint = "[ERROR] "
default:
messageToPrint = "[???] "
}
messageToPrint += "[" + time.UnixMilli(lastMessage.Timestamp).Format("15:04:05") + "] "
messageToPrint += lastMessage.Message + "\n"
if len(logsBuffer.Contents) >= 1000 {
logsBuffer.Contents = slices.Delete(logsBuffer.Contents, 0, len(logsBuffer.Contents)-999)
}
logsBuffer.CursorPos = Position{
X: len(logsBuffer.Contents[len(logsBuffer.Contents)-1]),
Y: len(logsBuffer.Contents) - 1,
}
logsBuffer.WriteString(runestring.RuneString(messageToPrint))
}
err := window.screen.PostEvent(tcell.NewEventInterrupt(nil))
if err != nil {
@@ -39,8 +79,18 @@ func drawMessageBar(window *Window) {
sizeX, sizeY := screen.Size()
messageToPrint := ""
if len(messageLog) > 0 && time.Since(time.UnixMilli(messageLog[len(messageLog)-1].timestamp)).Seconds() < 5 {
messageToPrint = messageLog[len(messageLog)-1].message
if lastMessage != nil && time.Since(time.UnixMilli(lastMessage.Timestamp)).Seconds() < 5 {
switch lastMessage.Urgency {
case TYPER_MESSAGE_INFO:
messageToPrint = "[INFO] "
case TYPER_MESSAGE_WARNING:
messageToPrint = "[WARNING] "
case TYPER_MESSAGE_ERROR:
messageToPrint = "[ERROR] "
default:
messageToPrint = "[???] "
}
messageToPrint += lastMessage.Message
}
for x := 0; x < sizeX; x++ {
+4
View File
@@ -43,6 +43,10 @@ type TyperStyle struct {
SyntaxConstant tcell.Color `name:"syntax_constant"`
SyntaxVariable tcell.Color `name:"syntax_variable"`
SyntaxString tcell.Color `name:"syntax_string"`
// Typer logs highlighting
SyntaxInfo tcell.Color `name:"syntax_info"`
SyntaxWarning tcell.Color `name:"syntax_warning"`
SyntaxError tcell.Color `name:"syntax_error"`
}
type typerStyleYaml struct {
+1 -1
View File
@@ -94,7 +94,7 @@ func initTopMenu() {
d := CreateDropdownMenu(buffersSlice, 0, y, 0, func(i int) {
window.CurrentBuffer = Buffers[i]
window.PrintMessage(fmt.Sprintf("Set current buffer to '%s'.", window.CurrentBuffer.Name))
window.PrintMessage(fmt.Sprintf("Set current buffer to '%s'", window.CurrentBuffer.Name), TYPER_MESSAGE_INFO)
ClearDropdowns()
window.CursorMode = CursorModeBuffer
})
+22 -1
View File
@@ -1,6 +1,7 @@
package main
import (
"fmt"
"log"
"slices"
"strconv"
@@ -86,7 +87,7 @@ func CreateWindow() (*Window, error) {
if ok := SetCurrentStyle(screen, Config.FallbackStyle); !ok {
// Use hard-coded fallback style
screen.SetStyle(tcell.StyleDefault.Foreground(CurrentStyle.BufferAreaFg).Background(CurrentStyle.BufferAreaBg))
window.PrintMessage("Could not set style either to selected one nor to fallback one!")
window.PrintMessage("Could not set style either to selected one nor to fallback one", TYPER_MESSAGE_ERROR)
}
}
@@ -396,6 +397,11 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
// Typing
if ev.Key() == tcell.KeyBackspace || ev.Key() == tcell.KeyBackspace2 {
if window.CursorMode == CursorModeBuffer {
if !window.CurrentBuffer.canEdit {
window.PrintMessage(fmt.Sprintf("Buffer '%s' is read-only", window.CurrentBuffer.Name), TYPER_MESSAGE_WARNING)
return
}
if window.CurrentBuffer.Selection != nil {
window.CurrentBuffer.CutText(window)
} else {
@@ -413,6 +419,11 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
}
} else if ev.Key() == tcell.KeyTab {
if window.CursorMode == CursorModeBuffer {
if !window.CurrentBuffer.canEdit {
window.PrintMessage(fmt.Sprintf("Buffer '%s' is read-only", window.CurrentBuffer.Name), TYPER_MESSAGE_WARNING)
return
}
// Remove selected text
if window.CurrentBuffer.Selection != nil {
window.CurrentBuffer.CutText(window)
@@ -422,6 +433,11 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
}
} else if ev.Key() == tcell.KeyEnter {
if window.CursorMode == CursorModeBuffer {
if !window.CurrentBuffer.canEdit {
window.PrintMessage(fmt.Sprintf("Buffer '%s' is read-only", window.CurrentBuffer.Name), TYPER_MESSAGE_WARNING)
return
}
// Remove selected text
if window.CurrentBuffer.Selection != nil {
window.CurrentBuffer.CutText(window)
@@ -441,6 +457,11 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
}
} else if ev.Key() == tcell.KeyRune {
if window.CursorMode == CursorModeBuffer {
if !window.CurrentBuffer.canEdit {
window.PrintMessage(fmt.Sprintf("Buffer '%s' is read-only", window.CurrentBuffer.Name), TYPER_MESSAGE_WARNING)
return
}
// Remove selected text
if window.CurrentBuffer.Selection != nil {
window.CurrentBuffer.CutText(window)