7 Commits
12 changed files with 375 additions and 133 deletions
+20
View File
@@ -0,0 +1,20 @@
# Typer Text Editor
### A simple and easy to use text editor written in Go
| Default Style | Classic Style |
|:----------------------------------------------------------------:|:----------------------------------------------------------------:|
| ![Example of the Typer's default style](media/default-style.png) | ![Example of the Typer's classic style](media/classic-style.png) |
### Installation
#### From source:
- Download `go` from your package manager or from the go website
- Downlaod `which` from your package manager
- Download `make` from your package manager
- Run the following command to compile Typer
```shell
make
```
- Run the following command **with superuser privileges** to install Typer to your system
```shell
make install SYSCONFDIR=/etc
```
+1
View File
@@ -5,4 +5,5 @@ selected_style_fallback: "default-fallback" # Style for 8-color capable terminal
# Other
show_top_menu: true
show_line_index: true
buffer_info_message: "File: %f Cursor: (%x, %y, %p) Chars: %c"
tab_indentation: 4 # Length of tab characters
+1 -1
View File
@@ -22,7 +22,7 @@ keybindings:
command: "prev-buffer"
- keybinding: "PgDn"
cursor_modes: ["buffer"]
command: "prev-buffer"
command: "next-buffer"
- keybinding: "Ctrl-N"
cursor_modes: ["buffer"]
command: "new-buffer"
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

+82 -11
View File
@@ -2,13 +2,13 @@ package main
import (
"fmt"
"github.com/gdamore/tcell/v2"
"os"
"path/filepath"
"strings"
)
type Buffer struct {
Id int
Name string
Contents string
@@ -26,8 +26,71 @@ type Selection struct {
selectionEnd int
}
var Buffers = make(map[int]*Buffer)
var LastBufferId int
var Buffers = make([]*Buffer, 0)
func GetBufferByName(name string) *Buffer {
for _, buffer := range Buffers {
if buffer.Name == name {
return buffer
}
}
return nil
}
func GetBufferByFilename(filename string) *Buffer {
for _, buffer := range Buffers {
if buffer.filename == filename {
return buffer
}
}
return nil
}
func drawBuffer(window *Window) {
buffer := window.CurrentBuffer
x, y, _, _ := window.GetTextAreaDimensions()
bufferX, bufferY, _, _ := window.GetTextAreaDimensions()
for i, r := range buffer.Contents + " " {
if x-buffer.OffsetX >= bufferX && y-buffer.OffsetY >= bufferY {
// Default style
style := tcell.StyleDefault.Background(CurrentStyle.BufferAreaBg).Foreground(CurrentStyle.BufferAreaFg)
// Change background if under cursor
if i == buffer.CursorPos {
style = style.Background(CurrentStyle.BufferAreaSel)
}
// Change background if selected
if buffer.Selection != nil {
if edge1, edge2 := buffer.GetSelectionEdges(); i >= edge1 && i <= edge2 {
style = style.Background(CurrentStyle.BufferAreaSel)
// Show selection on entire tab space
if r == '\t' {
for j := 0; j < int(Config.TabIndentation); j++ {
window.screen.SetContent(x+j-buffer.OffsetX, y-buffer.OffsetY, r, nil, style)
}
}
}
}
window.screen.SetContent(x-buffer.OffsetX, y-buffer.OffsetY, r, nil, style)
}
// Change position for next character
if r == '\n' {
x = bufferX
y++
} else if r == '\t' {
x += int(Config.TabIndentation)
} else {
x++
}
}
}
func (buffer *Buffer) Load() error {
// Do not load if canSave is false or filename is not set
@@ -157,8 +220,15 @@ func CreateFileBuffer(filename string, openNonExistentFile bool) (*Buffer, error
}
}
if GetBufferByName(filename) != nil {
return nil, fmt.Errorf("a buffer with the name (%s) is already open", filename)
}
if GetBufferByFilename(abs) != nil {
return nil, fmt.Errorf("%s is already open in another buffer", filename)
}
buffer := Buffer{
Id: LastBufferId + 1,
Name: filename,
Contents: "",
CursorPos: 0,
@@ -175,15 +245,13 @@ func CreateFileBuffer(filename string, openNonExistentFile bool) (*Buffer, error
}
}
Buffers[buffer.Id] = &buffer
LastBufferId++
Buffers = append(Buffers, &buffer)
return &buffer, nil
}
func CreateBuffer(bufferName string) *Buffer {
func CreateBuffer(bufferName string) (*Buffer, error) {
buffer := Buffer{
Id: LastBufferId + 1,
Name: bufferName,
Contents: "",
CursorPos: 0,
@@ -191,8 +259,11 @@ func CreateBuffer(bufferName string) *Buffer {
filename: "",
}
Buffers[buffer.Id] = &buffer
LastBufferId++
if GetBufferByName(bufferName) != nil {
return nil, fmt.Errorf("a buffer with the name (%s) is already open", bufferName)
}
return &buffer
Buffers = append(Buffers, &buffer)
return &buffer, nil
}
+25 -19
View File
@@ -3,8 +3,8 @@ package main
import (
"fmt"
"log"
"maps"
"slices"
"strconv"
"strings"
)
@@ -140,15 +140,15 @@ func initCommands() {
return
}
buffers := slices.Collect(maps.Values(Buffers))
index := slices.Index(buffers, window.CurrentBuffer)
index := slices.Index(Buffers, window.CurrentBuffer)
index--
if index < 0 {
index = 0
}
window.CurrentBuffer = buffers[index]
window.CurrentBuffer = Buffers[index]
PrintMessage(window, fmt.Sprintf("Set current buffer to '%s'.", window.CurrentBuffer.Name))
},
}
@@ -159,44 +159,50 @@ func initCommands() {
return
}
buffers := slices.Collect(maps.Values(Buffers))
index := slices.Index(buffers, window.CurrentBuffer)
index := slices.Index(Buffers, window.CurrentBuffer)
index++
if index >= len(buffers) {
index = len(buffers) - 1
if index >= len(Buffers) {
index = len(Buffers) - 1
}
window.CurrentBuffer = buffers[index]
window.CurrentBuffer = Buffers[index]
PrintMessage(window, fmt.Sprintf("Set current buffer to '%s'.", window.CurrentBuffer.Name))
},
}
newBufferCmd := Command{
cmd: "new-buffer",
run: func(window *Window, args ...string) {
number := 1
for _, buffer := range Buffers {
if strings.HasPrefix(buffer.Name, "New File ") {
number++
for i := 1; true; i++ {
buffer, err := CreateBuffer("New Buffer " + strconv.Itoa(i))
if err == nil {
window.CurrentBuffer = buffer
break
}
}
buffer := CreateBuffer(fmt.Sprintf("New File %d", number))
window.CurrentBuffer = buffer
window.CursorMode = CursorModeBuffer
PrintMessage(window, fmt.Sprintf("New buffer created with the name '%s'.", window.CurrentBuffer.Name))
},
}
closeBufferCmd := Command{
cmd: "close-buffer",
run: func(window *Window, args ...string) {
delete(Buffers, window.CurrentBuffer.Id)
buffersSlice := slices.Collect(maps.Values(Buffers))
if len(buffersSlice) == 0 {
bufferIndex := slices.Index(Buffers, window.CurrentBuffer)
Buffers = DeleteFromSlice(Buffers, bufferIndex)
if len(Buffers) == 0 {
window.Close()
return
}
window.CurrentBuffer = buffersSlice[0]
if bufferIndex >= len(Buffers) {
window.CurrentBuffer = Buffers[bufferIndex-1]
} else {
window.CurrentBuffer = Buffers[bufferIndex]
}
window.CursorMode = CursorModeBuffer
PrintMessage(window, "Buffer closed.")
},
}
+12 -10
View File
@@ -8,22 +8,24 @@ import (
)
type TyperConfig struct {
SelectedStyle string `yaml:"selected_style,omitempty"`
FallbackStyle string `yaml:"fallback_style,omitempty"`
ShowTopMenu bool `yaml:"show_top_menu,omitempty"`
ShowLineIndex bool `yaml:"show_line_index,omitempty"`
TabIndentation int `yaml:"tab_indentation,omitempty"`
SelectedStyle string `yaml:"selected_style,omitempty"`
FallbackStyle string `yaml:"fallback_style,omitempty"`
ShowTopMenu bool `yaml:"show_top_menu,omitempty"`
ShowLineIndex bool `yaml:"show_line_index,omitempty"`
BufferInfoMessage string `yaml:"buffer_info_message,omitempty"`
TabIndentation int `yaml:"tab_indentation,omitempty"`
}
var Config TyperConfig
func readConfig() {
Config = TyperConfig{
SelectedStyle: "default",
FallbackStyle: "default-fallback",
ShowTopMenu: true,
ShowLineIndex: true,
TabIndentation: 4,
SelectedStyle: "default",
FallbackStyle: "default-fallback",
ShowTopMenu: true,
ShowLineIndex: true,
BufferInfoMessage: "File: %f Cursor: (%x, %y, %p) Chars: %c",
TabIndentation: 4,
}
homeDir, err := os.UserHomeDir()
+3 -3
View File
@@ -24,16 +24,16 @@ func main() {
}
if len(os.Args) > 1 {
for _, file := range os.Args[1:] {
for i, file := range os.Args[1:] {
b, err := CreateFileBuffer(file, true)
if err != nil {
PrintMessage(window, "Could not open file: "+file)
continue
}
if window.CurrentBuffer.Name == "New File 1" {
delete(Buffers, window.CurrentBuffer.Id)
if i == 0 {
window.CurrentBuffer = b
Buffers = Buffers[1:]
}
}
}
+34 -18
View File
@@ -4,7 +4,6 @@ import (
"fmt"
"github.com/gdamore/tcell/v2"
"path/filepath"
"slices"
"strconv"
"strings"
)
@@ -82,27 +81,17 @@ func initTopMenu() {
}
buffersSlice := make([]string, 0)
for _, buffer := range Buffers {
for i, buffer := range Buffers {
if window.CurrentBuffer == buffer {
buffersSlice = append(buffersSlice, fmt.Sprintf("[%d] * %s", buffer.Id, buffer.Name))
buffersSlice = append(buffersSlice, fmt.Sprintf("[%d] * %s", i+1, buffer.Name))
} else {
buffersSlice = append(buffersSlice, fmt.Sprintf("[%d] %s", buffer.Id, buffer.Name))
buffersSlice = append(buffersSlice, fmt.Sprintf("[%d] %s", i+1, buffer.Name))
}
}
slices.Sort(buffersSlice)
d := CreateDropdownMenu(buffersSlice, 0, y, 0, func(i int) {
start := strings.Index(buffersSlice[i], "[")
end := strings.Index(buffersSlice[i], "]")
id, err := strconv.Atoi(buffersSlice[i][start+1 : end])
if err != nil {
PrintMessage(window, fmt.Sprintf("Cannot convert buffer id '%s' to int", buffersSlice[i][start:end]))
return
}
window.CurrentBuffer = Buffers[id]
window.CurrentBuffer = Buffers[i]
PrintMessage(window, fmt.Sprintf("Set current buffer to '%s'.", window.CurrentBuffer.Name))
ClearDropdowns()
window.CursorMode = CursorModeBuffer
})
@@ -133,11 +122,38 @@ func drawTopMenu(window *Window) {
}
// Draw buffer info
bufferInfoMsg := getBufferInfoMsg(window)
drawText(screen, sizeX-len(bufferInfoMsg)-1, 0, sizeX-1, 0, topMenuStyle, bufferInfoMsg)
}
func getBufferInfoMsg(window *Window) string {
pathToFile := "Not set"
filename := "Not set"
if window.CurrentBuffer.filename != "" {
pathToFile = window.CurrentBuffer.filename
}
if filepath.Base(window.CurrentBuffer.filename) != "." {
filename = filepath.Base(window.CurrentBuffer.filename)
}
cursorPos := window.CurrentBuffer.CursorPos
cursorX, cursorY := window.GetCursorPos2D()
cursorInfo := fmt.Sprintf("File: %s Cursor: (%d,%d,%d) Words: %d", filename, cursorX+1, cursorY+1, window.CurrentBuffer.CursorPos+1, len(strings.Fields(window.CurrentBuffer.Contents)))
drawText(screen, sizeX-len(cursorInfo)-1, 0, sizeX-1, 0, topMenuStyle, cursorInfo)
cursorX++
cursorY++
chars := len(window.CurrentBuffer.Contents)
words := len(strings.Fields(window.CurrentBuffer.Contents))
ret := Config.BufferInfoMessage
ret = strings.ReplaceAll(ret, "\n", " ")
ret = strings.ReplaceAll(ret, "%F", pathToFile)
ret = strings.ReplaceAll(ret, "%f", filename)
ret = strings.ReplaceAll(ret, "%x", strconv.Itoa(cursorX))
ret = strings.ReplaceAll(ret, "%y", strconv.Itoa(cursorY))
ret = strings.ReplaceAll(ret, "%p", strconv.Itoa(cursorPos))
ret = strings.ReplaceAll(ret, "%c", strconv.Itoa(chars))
ret = strings.ReplaceAll(ret, "%w", strconv.Itoa(words))
return ret
}
+12
View File
@@ -53,3 +53,15 @@ func drawBox(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style) {
drawText(s, x1+1, y1+1, x2-1, y2-1, style, " ")
}
func DeleteFromSlice[T any](slice []T, i int) []T {
if i >= len(slice) {
return slice
} else if i < 0 {
return slice
} else if i == len(slice)-1 {
return slice[:len(slice)-1]
} else {
return append(slice[:i], slice[i+1:]...)
}
}
+185 -71
View File
@@ -4,7 +4,10 @@ import (
"github.com/gdamore/tcell/v2"
"log"
"slices"
"strconv"
"strings"
"time"
"unicode"
)
type CursorMode uint8
@@ -36,6 +39,7 @@ type Window struct {
}
var mouseHeld = false
var lastClick int64 = 0
func CreateWindow() (*Window, error) {
window := Window{
@@ -49,8 +53,11 @@ func CreateWindow() (*Window, error) {
}
// Create empty buffer if nil
if window.CurrentBuffer == nil {
window.CurrentBuffer = CreateBuffer("New File 1")
for i := 1; window.CurrentBuffer == nil; i++ {
buffer, err := CreateBuffer("New Buffer " + strconv.Itoa(i))
if err == nil {
window.CurrentBuffer = buffer
}
}
// Create tcell screen
@@ -79,52 +86,6 @@ func CreateWindow() (*Window, error) {
return &window, nil
}
func (window *Window) drawCurrentBuffer() {
buffer := window.CurrentBuffer
x, y, _, _ := window.GetTextAreaDimensions()
bufferX, bufferY, _, _ := window.GetTextAreaDimensions()
for i, r := range buffer.Contents + " " {
if x-buffer.OffsetX >= bufferX && y-buffer.OffsetY >= bufferY {
// Default style
style := tcell.StyleDefault.Background(CurrentStyle.BufferAreaBg).Foreground(CurrentStyle.BufferAreaFg)
// Change background if under cursor
if i == buffer.CursorPos {
style = style.Background(CurrentStyle.BufferAreaSel)
}
// Change background if selected
if buffer.Selection != nil {
if edge1, edge2 := buffer.GetSelectionEdges(); i >= edge1 && i <= edge2 {
style = style.Background(CurrentStyle.BufferAreaSel)
// Show selection on entire tab space
if r == '\t' {
for j := 0; j < int(Config.TabIndentation); j++ {
window.screen.SetContent(x+j-buffer.OffsetX, y-buffer.OffsetY, r, nil, style)
}
}
}
}
window.screen.SetContent(x-buffer.OffsetX, y-buffer.OffsetY, r, nil, style)
}
// Change position for next character
if r == '\n' {
x = bufferX
y++
} else if r == '\t' {
x += int(Config.TabIndentation)
} else {
x++
}
}
}
func (window *Window) Draw() {
// Clear screen
window.screen.Clear()
@@ -141,7 +102,7 @@ func (window *Window) Draw() {
// Draw current buffer
if window.CurrentBuffer != nil {
window.drawCurrentBuffer()
drawBuffer(window)
}
// Draw input bar
@@ -184,16 +145,47 @@ func (window *Window) Draw() {
func (window *Window) input(ev *tcell.EventKey) {
if ev.Key() == tcell.KeyRight { // Navigation Keys
if window.CursorMode == CursorModeBuffer {
// Get original cursor position
pos := window.CurrentBuffer.CursorPos
if ev.Modifiers()&tcell.ModCtrl != 0 {
// Move cursor to start of word
// Set variable to one character right of current position
endOfWord := pos + 1
if endOfWord >= len(window.CurrentBuffer.Contents) {
endOfWord = len(window.CurrentBuffer.Contents)
}
// Skip all spaces
for endOfWord < len(window.CurrentBuffer.Contents) && unicode.IsSpace(rune(window.CurrentBuffer.Contents[endOfWord])) {
endOfWord++
}
// Find end of word
for endOfWord < len(window.CurrentBuffer.Contents) && !unicode.IsSpace(rune(window.CurrentBuffer.Contents[endOfWord])) {
endOfWord++
}
window.SetCursorPos(endOfWord)
} else {
// Move cursor one character backwards
window.SetCursorPos(window.CurrentBuffer.CursorPos + 1)
}
// Add to selection
if ev.Modifiers() == tcell.ModShift {
if ev.Modifiers()&tcell.ModShift != 0 {
if window.CurrentBuffer.Selection == nil {
// Cancel cursor movement when creating selection without holding ctrl
if ev.Modifiers()&tcell.ModCtrl == 0 {
window.SetCursorPos(pos)
}
window.CurrentBuffer.Selection = &Selection{
selectionStart: window.CurrentBuffer.CursorPos,
selectionStart: pos,
selectionEnd: window.CurrentBuffer.CursorPos,
}
return
} else {
window.CurrentBuffer.Selection.selectionEnd = window.CurrentBuffer.CursorPos + 1
window.CurrentBuffer.Selection.selectionEnd = window.CurrentBuffer.CursorPos
}
// Prevent selecting dummy character at the end of the buffer
if window.CurrentBuffer.Selection.selectionEnd >= len(window.CurrentBuffer.Contents) {
@@ -202,46 +194,83 @@ func (window *Window) input(ev *tcell.EventKey) {
} else if window.CurrentBuffer.Selection != nil {
// Unset selection
window.CurrentBuffer.Selection = nil
return
}
// Move cursor
window.SetCursorPos(window.CurrentBuffer.CursorPos + 1)
}
} else if ev.Key() == tcell.KeyLeft {
if window.CursorMode == CursorModeBuffer {
// Get original cursor position
pos := window.CurrentBuffer.CursorPos
if ev.Modifiers()&tcell.ModCtrl != 0 {
// Move cursor to start of word
// Set variable to one character left of current position
startOfWord := pos - 1
if startOfWord < 0 {
startOfWord = 0
}
// Skip all spaces
for startOfWord >= 0 && len(window.CurrentBuffer.Contents) != 0 && unicode.IsSpace(rune(window.CurrentBuffer.Contents[startOfWord])) {
startOfWord--
}
// Find start of word
for startOfWord >= 0 && len(window.CurrentBuffer.Contents) != 0 && !unicode.IsSpace(rune(window.CurrentBuffer.Contents[startOfWord])) {
startOfWord--
}
// Move one character to the right
startOfWord++
window.SetCursorPos(startOfWord)
} else {
// Move cursor one character backwards
window.SetCursorPos(window.CurrentBuffer.CursorPos - 1)
}
// Add to selection
if ev.Modifiers() == tcell.ModShift {
if ev.Modifiers()&tcell.ModShift != 0 {
if window.CurrentBuffer.Selection == nil {
// Cancel cursor movement when creating selection without holding ctrl
if ev.Modifiers()&tcell.ModCtrl == 0 {
window.SetCursorPos(pos)
}
window.CurrentBuffer.Selection = &Selection{
selectionStart: window.CurrentBuffer.CursorPos,
selectionStart: pos,
selectionEnd: window.CurrentBuffer.CursorPos,
}
return
} else {
window.CurrentBuffer.Selection.selectionEnd = window.CurrentBuffer.CursorPos - 1
window.CurrentBuffer.Selection.selectionEnd = window.CurrentBuffer.CursorPos
}
} else if window.CurrentBuffer.Selection != nil {
// Unset selection
window.CurrentBuffer.Selection = nil
return
}
// Move cursor
window.SetCursorPos(window.CurrentBuffer.CursorPos - 1)
}
} else if ev.Key() == tcell.KeyUp {
if window.CursorMode == CursorModeBuffer {
// Get original cursor position
pos := window.CurrentBuffer.CursorPos
// Move cursor
x, y := window.GetCursorPos2D()
window.SetCursorPos2D(x, y-1)
if ev.Modifiers()&tcell.ModCtrl != 0 {
// Move cursor to top of buffer
window.SetCursorPos(0)
} else {
// Move cursor one line up
x, y := window.GetCursorPos2D()
window.SetCursorPos2D(x, y-1)
}
// Add to selection
if ev.Modifiers() == tcell.ModShift {
if ev.Modifiers()&tcell.ModShift != 0 {
// Add to selection
if window.CurrentBuffer.Selection == nil {
window.CurrentBuffer.Selection = &Selection{
selectionStart: window.CurrentBuffer.CursorPos,
selectionEnd: pos,
selectionStart: pos,
selectionEnd: window.CurrentBuffer.CursorPos,
}
} else {
window.CurrentBuffer.Selection.selectionEnd = window.CurrentBuffer.CursorPos
@@ -276,11 +305,18 @@ func (window *Window) input(ev *tcell.EventKey) {
if window.CursorMode == CursorModeBuffer {
// Get original cursor position
pos := window.CurrentBuffer.CursorPos
// Move cursor
x, y := window.GetCursorPos2D()
window.SetCursorPos2D(x, y+1)
if ev.Modifiers()&tcell.ModCtrl != 0 {
// Move cursor to bottom of buffer
window.SetCursorPos(len(window.CurrentBuffer.Contents))
} else {
// Move cursor one line down
x, y := window.GetCursorPos2D()
window.SetCursorPos2D(x, y+1)
}
// Add to selection
if ev.Modifiers() == tcell.ModShift {
if ev.Modifiers()&tcell.ModShift != 0 {
// Add to selection
if window.CurrentBuffer.Selection == nil {
window.CurrentBuffer.Selection = &Selection{
@@ -484,9 +520,12 @@ func (window *Window) mouseInput(ev *tcell.EventMouse) {
// Left click was pressed
if ev.Buttons() == tcell.Button1 {
// Get last click time
lastClickTime := time.UnixMilli(lastClick)
// Ensure click was in buffer area
x1, y1, x2, y2 := window.GetTextAreaDimensions()
if mouseX >= x1 && mouseY >= y1 && mouseX <= x2 && mouseY <= y2 {
currentX, currentY := window.GetCursorPos2D()
bufferMouseX, bufferMouseY := window.AbsolutePosToCursorPos2D(mouseX, mouseY)
if mouseHeld {
// Add to selection
@@ -495,6 +534,10 @@ func (window *Window) mouseInput(ev *tcell.EventMouse) {
selectionStart: window.CurrentBuffer.CursorPos,
selectionEnd: window.CursorPos2DToCursorPos(bufferMouseX, bufferMouseY),
}
// Set last click time
lastClick = time.Now().UnixMilli()
return
} else {
window.CurrentBuffer.Selection.selectionEnd = window.CursorPos2DToCursorPos(bufferMouseX, bufferMouseY)
@@ -503,6 +546,74 @@ func (window *Window) mouseInput(ev *tcell.EventMouse) {
if window.CurrentBuffer.Selection.selectionEnd >= len(window.CurrentBuffer.Contents) {
window.CurrentBuffer.Selection.selectionEnd = len(window.CurrentBuffer.Contents) - 1
}
} else if currentX == bufferMouseX && currentY == bufferMouseY && window.CurrentBuffer.CursorPos < len(window.CurrentBuffer.Contents) && time.Since(lastClickTime).Milliseconds() < 300 {
selectedText := window.CurrentBuffer.GetSelectedText()
if window.CurrentBuffer.Selection == nil || strings.HasSuffix(selectedText, "\n") {
// Select word
startOfWord := window.CurrentBuffer.CursorPos
endOfWord := window.CurrentBuffer.CursorPos
// Find end of word
for i := window.CurrentBuffer.CursorPos + 1; i < len(window.CurrentBuffer.Contents); i++ {
currentRune := rune(window.CurrentBuffer.Contents[i])
if unicode.IsLetter(currentRune) || unicode.IsDigit(currentRune) || currentRune == '_' {
endOfWord++
} else {
break
}
}
// Find start of word
for i := window.CurrentBuffer.CursorPos - 1; i >= 0; i-- {
currentRune := rune(window.CurrentBuffer.Contents[i])
if unicode.IsLetter(currentRune) || unicode.IsDigit(currentRune) || currentRune == '_' {
startOfWord--
} else {
break
}
}
// Add to selection
window.CurrentBuffer.Selection = &Selection{
selectionStart: startOfWord,
selectionEnd: endOfWord,
}
} else {
// Select line
startOfLine := window.CurrentBuffer.CursorPos
endOfLine := window.CurrentBuffer.CursorPos
// Find end of line
for i := window.CurrentBuffer.CursorPos + 1; i < len(window.CurrentBuffer.Contents); i++ {
currentLetter := window.CurrentBuffer.Contents[i]
endOfLine++
if currentLetter == '\n' {
break
}
}
// Find start of line
for i := window.CurrentBuffer.CursorPos - 1; i >= 0; i-- {
currentLetter := window.CurrentBuffer.Contents[i]
if currentLetter != '\n' {
startOfLine--
} else {
break
}
}
// Add to selection
window.CurrentBuffer.Selection = &Selection{
selectionStart: startOfLine,
selectionEnd: endOfLine,
}
}
// Set last click time
lastClick = time.Now().UnixMilli()
return
} else {
// Clear selection
if window.CurrentBuffer.Selection != nil {
@@ -511,6 +622,9 @@ func (window *Window) mouseInput(ev *tcell.EventMouse) {
}
// Move cursor
window.SetCursorPos2D(bufferMouseX, bufferMouseY)
// Set last click time
lastClick = time.Now().UnixMilli()
}
mouseHeld = true
} else if ev.Buttons() == tcell.ButtonNone {