Files
typer/src/command.go
T

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"] = &copyCmd
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
}
}