mirror of
https://github.com/EnumeratedDev/typer.git
synced 2026-09-21 07:36:11 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b784bc0208
|
||
|
|
999f1953f9
|
||
|
|
e4462f9674
|
+1
-1
@@ -6,5 +6,5 @@ selected_style_fallback: "default-fallback" # Style for 8-color capable terminal
|
||||
show_top_menu: true
|
||||
show_line_index: true
|
||||
extend_line_index: false # Extend line index to the bottom of the screen
|
||||
buffer_info_message: "File: %f Cursor: (%x, %y, %p) Chars: %c"
|
||||
buffer_info_message: "File: %f, Filetype: %t, Cursor: (%x, %y, %p), Chars: %c"
|
||||
tab_indentation: 4 # Length of tab characters
|
||||
|
||||
@@ -19,3 +19,15 @@ colors:
|
||||
message_bar_fg: "black" # Message bar text color
|
||||
input_bar_bg: "245" # Input bar background color
|
||||
input_bar_fg: "black" # Input bar text color
|
||||
|
||||
# Syntax highlighting
|
||||
syntax_comment: "darkgray"
|
||||
syntax_keyword: "lightgreen"
|
||||
syntax_identifier: "yellow"
|
||||
syntax_constant: "purple"
|
||||
syntax_variable: "yellow"
|
||||
syntax_string: "purple"
|
||||
# Typer logs highlighting
|
||||
syntax_info: "lightgreen"
|
||||
syntax_warning: "gold"
|
||||
syntax_error: "red"
|
||||
|
||||
@@ -19,3 +19,7 @@ colors:
|
||||
message_bar_fg: "black" # Message bar text color
|
||||
input_bar_bg: "white" # Input bar background color
|
||||
input_bar_fg: "black" # Input bar text color
|
||||
# Typer logs highlighting
|
||||
syntax_info: "green"
|
||||
syntax_warning: "yellow"
|
||||
syntax_error: "red"
|
||||
|
||||
@@ -5,6 +5,7 @@ style_type: "256-color"
|
||||
|
||||
# Colors
|
||||
colors:
|
||||
# Main colors
|
||||
buffer_area_bg: "234" # Buffer area background color
|
||||
buffer_area_fg: "white" # Buffer area text color
|
||||
buffer_area_sel: "243" # Buffer area selected text and cursor background color
|
||||
@@ -18,4 +19,16 @@ colors:
|
||||
message_bar_bg: "236" # Message bar background color
|
||||
message_bar_fg: "white" # Message bar text color
|
||||
input_bar_bg: "236" # Input bar background color
|
||||
input_bar_fg: "white" # Input bar text color
|
||||
input_bar_fg: "white" # Input bar text color
|
||||
|
||||
# Syntax highlighting
|
||||
syntax_comment: "gray"
|
||||
syntax_keyword: "gold"
|
||||
syntax_identifier: "purple"
|
||||
syntax_constant: "teal"
|
||||
syntax_variable: "purple"
|
||||
syntax_string: "blue"
|
||||
# Typer logs highlighting
|
||||
syntax_info: "green"
|
||||
syntax_warning: "yellow"
|
||||
syntax_error: "red"
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
filetype: typer_logs
|
||||
|
||||
rules:
|
||||
- type: info
|
||||
regex: '^\[INFO\].*$'
|
||||
multiline: true
|
||||
- type: warning
|
||||
regex: '^\[WARNING\].*$'
|
||||
multiline: true
|
||||
- type: error
|
||||
regex: '^\[ERROR\].*$'
|
||||
multiline: true
|
||||
@@ -0,0 +1,15 @@
|
||||
filetype: yaml
|
||||
filenames: ".y[a]?ml$"
|
||||
|
||||
rules:
|
||||
- type: string # Strings
|
||||
regex: '(["''])(.*?)(["''])'
|
||||
- type: comment
|
||||
regex: "#.*"
|
||||
- type: identifier # Keys
|
||||
regex: '^[[:blank:]]*(-.)?([a-z0-9\._\-])+:'
|
||||
multiline: true
|
||||
- type: constant # Number values
|
||||
regex: '\d+(\.\d+)?'
|
||||
- type: constant # True/false values
|
||||
regex: '\b(YES|yes|Y|y|ON|on|TRUE|True|true|NO|no|N|n|OFF|off|FALSE|False|false)\b'
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"typer/runestring"
|
||||
@@ -21,6 +22,8 @@ type Buffer struct {
|
||||
Selection *Selection
|
||||
|
||||
canSave bool
|
||||
canEdit bool
|
||||
filetype string
|
||||
filename string
|
||||
}
|
||||
|
||||
@@ -55,14 +58,51 @@ func drawBuffer(window *Window) {
|
||||
|
||||
bufferX, bufferY, _, _ := window.GetTextAreaDimensions()
|
||||
|
||||
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), TYPER_MESSAGE_ERROR)
|
||||
}
|
||||
|
||||
i := -1
|
||||
for lineIndex, line := range buffer.Contents {
|
||||
for runeIndex, r := range append(line, ' ') {
|
||||
i++
|
||||
drawPosition := Position{runeIndex, lineIndex}
|
||||
|
||||
if x-buffer.Offset.X >= bufferX && y-buffer.Offset.Y >= bufferY {
|
||||
// Default style
|
||||
style := tcell.StyleDefault.Background(CurrentStyle.BufferAreaBg).Foreground(CurrentStyle.BufferAreaFg)
|
||||
|
||||
// Check for syntax highlighting
|
||||
for _, parsedSyntax := range parsedSyntaxes {
|
||||
if i >= parsedSyntax.StartIndex && i < parsedSyntax.EndIndex {
|
||||
switch parsedSyntax.Type {
|
||||
case "comment":
|
||||
style = style.Foreground(CurrentStyle.SyntaxComment)
|
||||
case "keyword":
|
||||
style = style.Foreground(CurrentStyle.SyntaxKeyword)
|
||||
case "identifier":
|
||||
style = style.Foreground(CurrentStyle.SyntaxIdentifier)
|
||||
case "constant":
|
||||
style = style.Foreground(CurrentStyle.SyntaxConstant)
|
||||
case "variable":
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Change background if under cursor
|
||||
if buffer.CursorPos.Equals(runeIndex, lineIndex) {
|
||||
style = style.Background(CurrentStyle.BufferAreaSel)
|
||||
@@ -135,6 +175,17 @@ func (buffer *Buffer) Load() error {
|
||||
buffer.CursorPos.X = len(buffer.Contents[buffer.CursorPos.Y])
|
||||
}
|
||||
|
||||
// Set buffer filetype
|
||||
for _, syntax := range AvailableSyntaxes {
|
||||
if syntax.Filenames == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if ok, _ := regexp.MatchString(syntax.Filenames, buffer.filename); ok {
|
||||
buffer.filetype = syntax.Filetype
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -603,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,
|
||||
}
|
||||
|
||||
@@ -626,6 +678,7 @@ func CreateBuffer(bufferName string) (*Buffer, error) {
|
||||
Contents: make([]runestring.RuneString, 1),
|
||||
CursorPos: Position{0, 0},
|
||||
canSave: true,
|
||||
canEdit: true,
|
||||
filename: "",
|
||||
}
|
||||
|
||||
|
||||
+91
-40
@@ -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,25 +410,70 @@ 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)
|
||||
}
|
||||
}()
|
||||
},
|
||||
}
|
||||
|
||||
setFiletypeCmd := Command{
|
||||
cmd: "set-filetype",
|
||||
run: func(window *Window, args ...string) {
|
||||
if len(args) >= 1 {
|
||||
input := args[0]
|
||||
|
||||
if input == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.ToLower(input) == "none" {
|
||||
window.CurrentBuffer.filetype = ""
|
||||
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), TYPER_MESSAGE_ERROR)
|
||||
return
|
||||
}
|
||||
|
||||
window.CurrentBuffer.filetype = input
|
||||
window.PrintMessage(fmt.Sprintf("Setting filetype to '%s'", input), TYPER_MESSAGE_INFO)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
inputChannel := RequestInput(window, "Filetype to switch to:", "")
|
||||
go func() {
|
||||
input := <-inputChannel
|
||||
|
||||
if input == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := AvailableSyntaxes[input]; !ok {
|
||||
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), TYPER_MESSAGE_INFO)
|
||||
|
||||
}()
|
||||
},
|
||||
}
|
||||
|
||||
menuFileCmd := Command{
|
||||
cmd: "menu-file",
|
||||
run: func(window *Window, args ...string) {
|
||||
for _, button := range TopMenuButtons {
|
||||
if button.Name == "File" {
|
||||
button.Action(window)
|
||||
button.Action(window, &button)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -435,7 +485,7 @@ func initCommands() {
|
||||
run: func(window *Window, args ...string) {
|
||||
for _, button := range TopMenuButtons {
|
||||
if button.Name == "Edit" {
|
||||
button.Action(window)
|
||||
button.Action(window, &button)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -447,7 +497,7 @@ func initCommands() {
|
||||
run: func(window *Window, args ...string) {
|
||||
for _, button := range TopMenuButtons {
|
||||
if button.Name == "Buffers" {
|
||||
button.Action(window)
|
||||
button.Action(window, &button)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -521,6 +571,7 @@ func initCommands() {
|
||||
commands["toggle-top-bar"] = &toggleTopBar
|
||||
commands["toggle-line-index"] = &toggleLineIndex
|
||||
commands["set-style"] = &setStyleCmd
|
||||
commands["set-filetype"] = &setFiletypeCmd
|
||||
commands["menu-file"] = &menuFileCmd
|
||||
commands["menu-edit"] = &menuEditCmd
|
||||
commands["menu-buffers"] = &menuBuffersCmd
|
||||
@@ -533,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type SyntaxRule struct {
|
||||
Type string `yaml:"type"`
|
||||
Regex string `yaml:"regex"`
|
||||
Multiline bool `yaml:"multiline"`
|
||||
}
|
||||
|
||||
type Syntax struct {
|
||||
Filetype string `yaml:"filetype"`
|
||||
Filenames string `yaml:"filenames"`
|
||||
Rules []SyntaxRule `yaml:"rules"`
|
||||
}
|
||||
|
||||
type ParsedSyntax struct {
|
||||
StartIndex int
|
||||
EndIndex int
|
||||
Type string
|
||||
}
|
||||
|
||||
var AvailableSyntaxes map[string]Syntax = make(map[string]Syntax)
|
||||
|
||||
func ReadSyntaxHighlighters() {
|
||||
// Get syntax directory path
|
||||
syntaxDirPath := GetConfigPath("syntax")
|
||||
|
||||
// Ensure directory exists at path
|
||||
if stat, err := os.Stat(syntaxDirPath); syntaxDirPath == "" || err != nil || !stat.IsDir() {
|
||||
return
|
||||
}
|
||||
|
||||
// Get directory entries
|
||||
entries, err := os.ReadDir(syntaxDirPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not read syntax directory: %s", err)
|
||||
}
|
||||
|
||||
// Read entries in directory
|
||||
for _, entry := range entries {
|
||||
entryPath := filepath.Join(syntaxDirPath, entry.Name())
|
||||
|
||||
data, err := os.ReadFile(entryPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not read syntax file (%s): %s", entryPath, err)
|
||||
}
|
||||
|
||||
syntax := Syntax{}
|
||||
err = yaml.Unmarshal(data, &syntax)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not read syntax file (%s): %s", entryPath, err)
|
||||
}
|
||||
|
||||
if _, ok := AvailableSyntaxes[syntax.Filetype]; !ok {
|
||||
AvailableSyntaxes[syntax.Filetype] = syntax
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func HighlightString(s string, filetype string) (parsedSyntaxes []ParsedSyntax, err error) {
|
||||
// Get syntax for filetype
|
||||
syntax, ok := AvailableSyntaxes[filetype]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for _, rule := range syntax.Rules {
|
||||
|
||||
regex := rule.Regex
|
||||
if rule.Multiline {
|
||||
regex = "(?m)" + rule.Regex
|
||||
}
|
||||
r, err := regexp.Compile(regex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matches := r.FindAllStringIndex(s, -1)
|
||||
for _, match := range matches {
|
||||
skip := false
|
||||
for _, parsedSyntax := range parsedSyntaxes {
|
||||
if (match[0] >= parsedSyntax.StartIndex && match[0] < parsedSyntax.EndIndex) || (match[1] >= parsedSyntax.StartIndex && match[1] < parsedSyntax.EndIndex) {
|
||||
skip = true
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
continue
|
||||
}
|
||||
|
||||
parsedSyntax := ParsedSyntax{
|
||||
StartIndex: match[0],
|
||||
EndIndex: match[1],
|
||||
Type: rule.Type,
|
||||
}
|
||||
|
||||
parsedSyntaxes = append(parsedSyntaxes, parsedSyntax)
|
||||
}
|
||||
}
|
||||
|
||||
return parsedSyntaxes, nil
|
||||
}
|
||||
+12
-1
@@ -23,6 +23,9 @@ func main() {
|
||||
// Read styles directory
|
||||
readStyles()
|
||||
|
||||
// Read syntax directory
|
||||
ReadSyntaxHighlighters()
|
||||
|
||||
// Initialize commands
|
||||
initCommands()
|
||||
|
||||
@@ -35,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
|
||||
}
|
||||
|
||||
@@ -46,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
@@ -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++ {
|
||||
|
||||
+13
-1
@@ -20,7 +20,7 @@ type TyperStyle struct {
|
||||
Description string
|
||||
StyleType string
|
||||
|
||||
// Colors
|
||||
// Main Colors
|
||||
BufferAreaBg tcell.Color `name:"buffer_area_bg"`
|
||||
BufferAreaFg tcell.Color `name:"buffer_area_fg"`
|
||||
BufferAreaSel tcell.Color `name:"buffer_area_sel"`
|
||||
@@ -35,6 +35,18 @@ type TyperStyle struct {
|
||||
MessageBarFg tcell.Color `name:"message_bar_fg"`
|
||||
InputBarBg tcell.Color `name:"input_bar_bg"`
|
||||
InputBarFg tcell.Color `name:"input_bar_fg"`
|
||||
|
||||
// Syntax highlighting
|
||||
SyntaxComment tcell.Color `name:"syntax_comment"`
|
||||
SyntaxKeyword tcell.Color `name:"syntax_keyword"`
|
||||
SyntaxIdentifier tcell.Color `name:"syntax_identifier"`
|
||||
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 {
|
||||
|
||||
+17
-9
@@ -11,7 +11,8 @@ import (
|
||||
|
||||
type TopMenuButton struct {
|
||||
Name string
|
||||
Action func(w *Window)
|
||||
PosX int
|
||||
Action func(w *Window, b *TopMenuButton)
|
||||
}
|
||||
|
||||
var TopMenuButtons = make([]TopMenuButton, 0)
|
||||
@@ -20,7 +21,7 @@ func initTopMenu() {
|
||||
// Buttons
|
||||
fileButton := TopMenuButton{
|
||||
Name: "File",
|
||||
Action: func(window *Window) {
|
||||
Action: func(window *Window, button *TopMenuButton) {
|
||||
ClearDropdowns()
|
||||
|
||||
y := 0
|
||||
@@ -28,7 +29,7 @@ func initTopMenu() {
|
||||
y++
|
||||
}
|
||||
|
||||
d := CreateDropdownMenu([]string{"New", "Save", "Open", "Close", "Quit"}, 0, y, 0, func(i int) {
|
||||
d := CreateDropdownMenu([]string{"New", "Save", "Open", "Close", "Quit"}, button.PosX, y, 0, func(i int) {
|
||||
switch i {
|
||||
case 0:
|
||||
RunCommand(window, "new-buffer")
|
||||
@@ -49,7 +50,7 @@ func initTopMenu() {
|
||||
}
|
||||
EditButton := TopMenuButton{
|
||||
Name: "Edit",
|
||||
Action: func(window *Window) {
|
||||
Action: func(window *Window, button *TopMenuButton) {
|
||||
ClearDropdowns()
|
||||
|
||||
y := 0
|
||||
@@ -57,7 +58,7 @@ func initTopMenu() {
|
||||
y++
|
||||
}
|
||||
|
||||
d := CreateDropdownMenu([]string{"Cut", "Copy", "Paste"}, 0, y, 0, func(i int) {
|
||||
d := CreateDropdownMenu([]string{"Cut", "Copy", "Paste"}, button.PosX, y, 0, func(i int) {
|
||||
switch i {
|
||||
case 0:
|
||||
RunCommand(window, "cut")
|
||||
@@ -75,7 +76,7 @@ func initTopMenu() {
|
||||
}
|
||||
Buffers := TopMenuButton{
|
||||
Name: "Buffers",
|
||||
Action: func(window *Window) {
|
||||
Action: func(window *Window, button *TopMenuButton) {
|
||||
ClearDropdowns()
|
||||
|
||||
y := 0
|
||||
@@ -92,9 +93,9 @@ func initTopMenu() {
|
||||
}
|
||||
}
|
||||
|
||||
d := CreateDropdownMenu(buffersSlice, 0, y, 0, func(i int) {
|
||||
d := CreateDropdownMenu(buffersSlice, button.PosX, 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
|
||||
})
|
||||
@@ -119,8 +120,9 @@ func drawTopMenu(window *Window) {
|
||||
}
|
||||
|
||||
currentX := 1
|
||||
for _, button := range TopMenuButtons {
|
||||
for i, button := range TopMenuButtons {
|
||||
drawText(screen, currentX, 0, currentX+len(button.Name), 0, topMenuStyle, button.Name)
|
||||
TopMenuButtons[i].PosX = currentX
|
||||
currentX += len(button.Name) + 1
|
||||
}
|
||||
|
||||
@@ -134,6 +136,7 @@ func drawTopMenu(window *Window) {
|
||||
func getBufferInfoMsg(window *Window) string {
|
||||
pathToFile := "Not set"
|
||||
filename := "Not set"
|
||||
filetype := "Not set"
|
||||
if window.CurrentBuffer.filename != "" {
|
||||
pathToFile = window.CurrentBuffer.filename
|
||||
}
|
||||
@@ -141,6 +144,10 @@ func getBufferInfoMsg(window *Window) string {
|
||||
filename = filepath.Base(window.CurrentBuffer.filename)
|
||||
}
|
||||
|
||||
if window.CurrentBuffer.filetype != "" {
|
||||
filetype = window.CurrentBuffer.filetype
|
||||
}
|
||||
|
||||
contents := window.CurrentBuffer.GetContentsAsString()
|
||||
chars := len(contents)
|
||||
words := len(strings.Fields(string(contents)))
|
||||
@@ -150,6 +157,7 @@ func getBufferInfoMsg(window *Window) string {
|
||||
ret = strings.ReplaceAll(ret, "\n", " ")
|
||||
ret = strings.ReplaceAll(ret, "%F", pathToFile)
|
||||
ret = strings.ReplaceAll(ret, "%f", filename)
|
||||
ret = strings.ReplaceAll(ret, "%t", filetype)
|
||||
ret = strings.ReplaceAll(ret, "%x", strconv.Itoa(window.CurrentBuffer.CursorPos.X+1))
|
||||
ret = strings.ReplaceAll(ret, "%y", strconv.Itoa(window.CurrentBuffer.CursorPos.Y+1))
|
||||
ret = strings.ReplaceAll(ret, "%p", strconv.Itoa(window.CurrentBuffer.PositionToAbsolutePosition(window.CurrentBuffer.CursorPos)+1))
|
||||
|
||||
+22
-1
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user