mirror of
https://github.com/EnumeratedDev/typer.git
synced 2026-09-16 05:36:10 +00:00
586 lines
14 KiB
Go
586 lines
14 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
"typer/runestring"
|
|
)
|
|
|
|
type Command struct {
|
|
cmd string
|
|
run func(window *Window, args ...string)
|
|
autocomplete func(window *Window, args ...string) []string
|
|
}
|
|
|
|
var commands = make(map[string]*Command)
|
|
|
|
func initCommands() {
|
|
// Setup commands
|
|
selectAll := Command{
|
|
cmd: "select-all",
|
|
run: func(window *Window, args ...string) {
|
|
// Select entire buffer content
|
|
lastLine := window.CurrentBuffer.Contents[len(window.CurrentBuffer.Contents)-1]
|
|
window.CurrentBuffer.Selection = &Selection{
|
|
selectionStart: Position{0, 0},
|
|
selectionEnd: Position{len(window.CurrentBuffer.Contents) - 1, len(lastLine) - 1},
|
|
}
|
|
|
|
window.PrintMessage("Selected all text.")
|
|
},
|
|
}
|
|
|
|
cutCmd := Command{
|
|
cmd: "cut",
|
|
run: func(window *Window, args ...string) {
|
|
// Cut text from buffer
|
|
copiedText, copyingMethod := window.CurrentBuffer.CutText(window)
|
|
|
|
// Put cut text to clipboard
|
|
window.Clipboard = copiedText
|
|
|
|
// Send appropriate message and remove text depending on copying method
|
|
if copyingMethod == 0 {
|
|
window.PrintMessage("Copied line to clipboard.")
|
|
} else {
|
|
window.PrintMessage("Copied selection to clipboard.")
|
|
}
|
|
},
|
|
}
|
|
|
|
copyCmd := Command{
|
|
cmd: "copy",
|
|
run: func(window *Window, args ...string) {
|
|
// Copy text from buffer
|
|
copiedText, copyingMethod := window.CurrentBuffer.CopyText()
|
|
|
|
// Put copied text to clipboard
|
|
window.Clipboard = copiedText
|
|
|
|
// Send appropriate message depending on copying method
|
|
if copyingMethod == 0 {
|
|
window.PrintMessage("Copied line to clipboard.")
|
|
} else {
|
|
window.PrintMessage("Copied selection to clipboard. ")
|
|
}
|
|
},
|
|
}
|
|
|
|
pasteCmd := Command{
|
|
cmd: "paste",
|
|
run: func(window *Window, args ...string) {
|
|
if len(window.Clipboard) != 0 {
|
|
window.CurrentBuffer.PasteText(window, window.Clipboard)
|
|
window.PrintMessage("Pasted text to buffer. ")
|
|
}
|
|
},
|
|
}
|
|
|
|
saveCmd := Command{
|
|
cmd: "save",
|
|
run: func(window *Window, args ...string) {
|
|
if !window.CurrentBuffer.canSave {
|
|
window.PrintMessage("Cannot save buffer!")
|
|
return
|
|
}
|
|
|
|
inputChannel := RequestInput(window, "Save file [y\\N]:", "")
|
|
go func() {
|
|
input := <-inputChannel
|
|
|
|
if strings.ToLower(input) != "y" && strings.ToLower(input) != "yes" {
|
|
return
|
|
}
|
|
|
|
inputChannel = RequestInput(window, "Save buffer to:", window.CurrentBuffer.filename)
|
|
|
|
input = <-inputChannel
|
|
|
|
if strings.TrimSpace(input) == "" {
|
|
window.PrintMessage("No save location was given!")
|
|
return
|
|
}
|
|
|
|
window.CurrentBuffer.filename = strings.TrimSpace(input)
|
|
err := window.CurrentBuffer.Save()
|
|
if err != nil {
|
|
|
|
window.PrintMessage(fmt.Sprintf("Could not save file: %s", err))
|
|
window.CurrentBuffer.filename = ""
|
|
return
|
|
}
|
|
|
|
window.PrintMessage("File saved.")
|
|
}()
|
|
},
|
|
autocomplete: func(window *Window, args ...string) []string {
|
|
return nil
|
|
},
|
|
}
|
|
|
|
openCmd := Command{
|
|
cmd: "open",
|
|
run: func(window *Window, args ...string) {
|
|
inputChannel := RequestInput(window, "File to open:", "")
|
|
go func() {
|
|
input := <-inputChannel
|
|
|
|
if input == "" {
|
|
return
|
|
}
|
|
|
|
if openBuffer := GetOpenFileBuffer(input); openBuffer != nil {
|
|
window.PrintMessage(fmt.Sprintf("File already open! Switching to buffer: %s", openBuffer.Name))
|
|
window.CurrentBuffer = openBuffer
|
|
} else {
|
|
newBuffer, err := CreateFileBuffer(input, false)
|
|
if err != nil {
|
|
window.PrintMessage(fmt.Sprintf("Could not open file: %s", err.Error()))
|
|
return
|
|
}
|
|
|
|
window.PrintMessage(fmt.Sprintf("Opening file at: %s", newBuffer.filename))
|
|
window.CurrentBuffer = newBuffer
|
|
}
|
|
}()
|
|
},
|
|
}
|
|
|
|
reloadCmd := Command{
|
|
cmd: "reload",
|
|
run: func(window *Window, args ...string) {
|
|
err := window.CurrentBuffer.Load()
|
|
if err != nil {
|
|
log.Fatalf("Could not reload buffer: %s", err)
|
|
}
|
|
|
|
window.PrintMessage("Buffer reloaded.")
|
|
},
|
|
}
|
|
|
|
findCmd := Command{
|
|
cmd: "find",
|
|
run: func(window *Window, args ...string) {
|
|
if len(args) >= 1 {
|
|
input := runestring.RuneString(args[0])
|
|
|
|
if len(input) == 0 {
|
|
return
|
|
}
|
|
|
|
pos := window.CurrentBuffer.FindSubstring(input, window.CurrentBuffer.CursorPos)
|
|
if pos.X >= 0 && pos.Y >= 0 {
|
|
window.CurrentBuffer.CursorPos = pos
|
|
window.PrintMessage("Match found.")
|
|
} else {
|
|
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer!", string(input)))
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
inputChannel := RequestInput(window, "Substring to search for:", "")
|
|
go func() {
|
|
input := runestring.RuneString(<-inputChannel)
|
|
|
|
if len(input) == 0 {
|
|
return
|
|
}
|
|
|
|
pos := window.CurrentBuffer.FindSubstring(input, window.CurrentBuffer.CursorPos)
|
|
if pos.X >= 0 && pos.Y >= 0 {
|
|
window.CurrentBuffer.CursorPos = pos
|
|
window.PrintMessage("Match found.")
|
|
} else {
|
|
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer!", string(input)))
|
|
}
|
|
}()
|
|
},
|
|
}
|
|
|
|
replaceCmd := Command{
|
|
cmd: "replace",
|
|
run: func(window *Window, args ...string) {
|
|
if len(args) >= 2 {
|
|
findStr := runestring.RuneString(args[0])
|
|
replaceStr := runestring.RuneString(args[1])
|
|
|
|
if len(findStr) == 0 {
|
|
return
|
|
}
|
|
|
|
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.")
|
|
} else {
|
|
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer!", string(findStr)))
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
go func() {
|
|
inputChannel := RequestInput(window, "Substring to search for:", "")
|
|
findStr := runestring.RuneString(<-inputChannel)
|
|
if len(findStr) == 0 {
|
|
return
|
|
}
|
|
|
|
inputChannel = RequestInput(window, "String to replace with:", "")
|
|
replaceStr := runestring.RuneString(<-inputChannel)
|
|
|
|
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.")
|
|
} else {
|
|
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer!", string(findStr)))
|
|
}
|
|
}()
|
|
},
|
|
}
|
|
|
|
replaceAllCmd := Command{
|
|
cmd: "replace-all",
|
|
run: func(window *Window, args ...string) {
|
|
if len(args) >= 2 {
|
|
findStr := runestring.RuneString(args[0])
|
|
replaceStr := runestring.RuneString(args[1])
|
|
|
|
if len(findStr) == 0 {
|
|
return
|
|
}
|
|
|
|
replacements := window.CurrentBuffer.FindAndReplaceAll(findStr, replaceStr)
|
|
if replacements > 0 {
|
|
window.PrintMessage(fmt.Sprintf("Replaced all %d matches successfully.", replacements))
|
|
} else {
|
|
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer!", string(findStr)))
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
go func() {
|
|
inputChannel := RequestInput(window, "Substring to search for:", "")
|
|
findStr := runestring.RuneString(<-inputChannel)
|
|
if len(findStr) == 0 {
|
|
return
|
|
}
|
|
|
|
inputChannel = RequestInput(window, "String to replace with:", "")
|
|
replaceStr := runestring.RuneString(<-inputChannel)
|
|
|
|
replacements := window.CurrentBuffer.FindAndReplaceAll(findStr, replaceStr)
|
|
if replacements > 0 {
|
|
window.PrintMessage(fmt.Sprintf("Replaced all %d matches successfully.", replacements))
|
|
} else {
|
|
window.PrintMessage(fmt.Sprintf("'%s' not found in buffer!", string(findStr)))
|
|
}
|
|
}()
|
|
},
|
|
}
|
|
|
|
prevBufferCmd := Command{
|
|
cmd: "prev-buffer",
|
|
run: func(window *Window, args ...string) {
|
|
if window.CursorMode != CursorModeBuffer {
|
|
return
|
|
}
|
|
|
|
index := slices.Index(Buffers, window.CurrentBuffer)
|
|
|
|
index--
|
|
if index < 0 {
|
|
index = 0
|
|
}
|
|
|
|
window.CurrentBuffer = Buffers[index]
|
|
window.PrintMessage(fmt.Sprintf("Set current buffer to '%s'.", window.CurrentBuffer.Name))
|
|
},
|
|
}
|
|
|
|
nextBufferCmd := Command{
|
|
cmd: "next-buffer",
|
|
run: func(window *Window, args ...string) {
|
|
if window.CursorMode != CursorModeBuffer {
|
|
return
|
|
}
|
|
|
|
index := slices.Index(Buffers, window.CurrentBuffer)
|
|
|
|
index++
|
|
if index >= len(Buffers) {
|
|
index = len(Buffers) - 1
|
|
}
|
|
|
|
window.CurrentBuffer = Buffers[index]
|
|
window.PrintMessage(fmt.Sprintf("Set current buffer to '%s'.", window.CurrentBuffer.Name))
|
|
},
|
|
}
|
|
|
|
newBufferCmd := Command{
|
|
cmd: "new-buffer",
|
|
run: func(window *Window, args ...string) {
|
|
for i := 1; true; i++ {
|
|
buffer, err := CreateBuffer("New Buffer " + strconv.Itoa(i))
|
|
if err == nil {
|
|
window.CurrentBuffer = buffer
|
|
break
|
|
}
|
|
}
|
|
|
|
window.CursorMode = CursorModeBuffer
|
|
window.PrintMessage(fmt.Sprintf("New buffer created with the name '%s'.", window.CurrentBuffer.Name))
|
|
},
|
|
}
|
|
|
|
closeBufferCmd := Command{
|
|
cmd: "close-buffer",
|
|
run: func(window *Window, args ...string) {
|
|
bufferIndex := slices.Index(Buffers, window.CurrentBuffer)
|
|
Buffers = DeleteFromSlice(Buffers, bufferIndex)
|
|
if len(Buffers) == 0 {
|
|
window.Close()
|
|
return
|
|
}
|
|
if bufferIndex >= len(Buffers) {
|
|
window.CurrentBuffer = Buffers[bufferIndex-1]
|
|
} else {
|
|
window.CurrentBuffer = Buffers[bufferIndex]
|
|
}
|
|
window.CursorMode = CursorModeBuffer
|
|
window.PrintMessage("Buffer closed.")
|
|
},
|
|
}
|
|
|
|
toggleTopBar := Command{
|
|
cmd: "toggle-top-bar",
|
|
run: func(window *Window, args ...string) {
|
|
window.ShowTopMenu = !window.ShowTopMenu
|
|
},
|
|
}
|
|
|
|
toggleLineIndex := Command{
|
|
cmd: "toggle-line-index",
|
|
run: func(window *Window, args ...string) {
|
|
window.ShowLineIndex = !window.ShowLineIndex
|
|
},
|
|
}
|
|
|
|
setStyleCmd := Command{
|
|
cmd: "set-style",
|
|
run: func(window *Window, args ...string) {
|
|
if len(args) >= 1 {
|
|
input := args[0]
|
|
|
|
if input == "" {
|
|
return
|
|
}
|
|
|
|
if _, ok := AvailableStyles[input]; !ok {
|
|
window.PrintMessage(fmt.Sprintf("Could not set style to '%s'", input))
|
|
return
|
|
}
|
|
|
|
if ok := SetCurrentStyle(window.screen, input); ok {
|
|
window.PrintMessage(fmt.Sprintf("Setting style to '%s'", input))
|
|
} else {
|
|
window.PrintMessage(fmt.Sprintf("Could not set style to '%s'", input))
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
inputChannel := RequestInput(window, "Style to switch to:", "")
|
|
go func() {
|
|
input := <-inputChannel
|
|
|
|
if input == "" {
|
|
return
|
|
}
|
|
|
|
if _, ok := AvailableStyles[input]; !ok {
|
|
window.PrintMessage(fmt.Sprintf("Could not set style to '%s'", input))
|
|
return
|
|
}
|
|
|
|
if ok := SetCurrentStyle(window.screen, input); ok {
|
|
window.PrintMessage(fmt.Sprintf("Setting style to '%s'", input))
|
|
} else {
|
|
window.PrintMessage(fmt.Sprintf("Could not set style to '%s'", input))
|
|
}
|
|
}()
|
|
},
|
|
}
|
|
|
|
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'")
|
|
return
|
|
} else if _, ok := AvailableSyntaxes[input]; !ok {
|
|
window.PrintMessage(fmt.Sprintf("Could not set filetype to '%s'", input))
|
|
return
|
|
}
|
|
|
|
window.CurrentBuffer.filetype = input
|
|
window.PrintMessage(fmt.Sprintf("Setting filetype to '%s'", input))
|
|
|
|
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))
|
|
return
|
|
}
|
|
|
|
window.CurrentBuffer.filetype = input
|
|
window.PrintMessage(fmt.Sprintf("Setting filetype to '%s'", input))
|
|
|
|
}()
|
|
},
|
|
}
|
|
|
|
menuFileCmd := Command{
|
|
cmd: "menu-file",
|
|
run: func(window *Window, args ...string) {
|
|
for _, button := range TopMenuButtons {
|
|
if button.Name == "File" {
|
|
button.Action(window)
|
|
break
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
menuEditCmd := Command{
|
|
cmd: "menu-edit",
|
|
run: func(window *Window, args ...string) {
|
|
for _, button := range TopMenuButtons {
|
|
if button.Name == "Edit" {
|
|
button.Action(window)
|
|
break
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
menuBuffersCmd := Command{
|
|
cmd: "menu-buffers",
|
|
run: func(window *Window, args ...string) {
|
|
for _, button := range TopMenuButtons {
|
|
if button.Name == "Buffers" {
|
|
button.Action(window)
|
|
break
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
quitCmd := Command{
|
|
cmd: "quit",
|
|
run: func(window *Window, args ...string) {
|
|
window.Close()
|
|
window.CursorMode = CursorModeBuffer
|
|
},
|
|
}
|
|
|
|
executeCmd := Command{
|
|
cmd: "execute",
|
|
run: func(window *Window, args ...string) {
|
|
inputChannel := RequestInput(window, "Run:", "")
|
|
|
|
go func() {
|
|
input := strings.TrimSpace(<-inputChannel)
|
|
|
|
if input == "" {
|
|
return
|
|
}
|
|
|
|
var arguments []string
|
|
|
|
builder := &strings.Builder{}
|
|
quoted := false
|
|
for _, r := range input {
|
|
if r == '"' {
|
|
quoted = !quoted
|
|
} else if !quoted && r == ' ' {
|
|
arguments = append(arguments, builder.String())
|
|
builder.Reset()
|
|
} else {
|
|
builder.WriteRune(r)
|
|
}
|
|
}
|
|
if builder.Len() > 0 {
|
|
arguments = append(arguments, builder.String())
|
|
}
|
|
|
|
window.CursorMode = CursorModeBuffer
|
|
|
|
if len(arguments) == 1 {
|
|
RunCommand(window, arguments[0])
|
|
} else {
|
|
RunCommand(window, arguments[0], arguments[1:]...)
|
|
}
|
|
}()
|
|
},
|
|
}
|
|
|
|
// Register commands
|
|
commands["select-all"] = &selectAll
|
|
commands["cut"] = &cutCmd
|
|
commands["copy"] = ©Cmd
|
|
commands["paste"] = &pasteCmd
|
|
commands["save"] = &saveCmd
|
|
commands["open"] = &openCmd
|
|
commands["reload"] = &reloadCmd
|
|
commands["find"] = &findCmd
|
|
commands["replace"] = &replaceCmd
|
|
commands["replace-all"] = &replaceAllCmd
|
|
commands["prev-buffer"] = &prevBufferCmd
|
|
commands["next-buffer"] = &nextBufferCmd
|
|
commands["new-buffer"] = &newBufferCmd
|
|
commands["close-buffer"] = &closeBufferCmd
|
|
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
|
|
commands["quit"] = &quitCmd
|
|
commands["execute"] = &executeCmd
|
|
}
|
|
|
|
func RunCommand(window *Window, cmd string, args ...string) bool {
|
|
if command, ok := commands[cmd]; ok {
|
|
command.run(window, args...)
|
|
return true
|
|
} else {
|
|
window.PrintMessage(fmt.Sprintf("Could not find command '%s'!", cmd))
|
|
return false
|
|
}
|
|
}
|