Switch buffer contents to string slice

This commit is contained in:
2026-03-19 11:53:01 +02:00
parent 1b5ce6b7f8
commit 2bf960b085
6 changed files with 514 additions and 541 deletions
+339 -165
View File
@@ -2,18 +2,20 @@ package main
import (
"fmt"
"github.com/gdamore/tcell/v2"
"os"
"path/filepath"
"slices"
"strings"
"github.com/gdamore/tcell/v2"
)
type Buffer struct {
Name string
Contents string
Contents []string
CursorPos int
OffsetX, OffsetY int
CursorPos Position
Offset Position
Selection *Selection
@@ -22,8 +24,7 @@ type Buffer struct {
}
type Selection struct {
selectionStart int
selectionEnd int
selectionStart, selectionEnd Position
}
var Buffers = make([]*Buffer, 0)
@@ -53,43 +54,50 @@ func drawBuffer(window *Window) {
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)
for lineIndex, line := range buffer.Contents {
for runeIndex, r := range line + " " {
drawPosition := Position{runeIndex, lineIndex}
// Change background if under cursor
if i == buffer.CursorPos {
style = style.Background(CurrentStyle.BufferAreaSel)
}
if x-buffer.Offset.X >= bufferX && y-buffer.Offset.Y >= bufferY {
// Default style
style := tcell.StyleDefault.Background(CurrentStyle.BufferAreaBg).Foreground(CurrentStyle.BufferAreaFg)
// Change background if selected
if buffer.Selection != nil {
if edge1, edge2 := buffer.GetSelectionEdges(); i >= edge1 && i <= edge2 {
// Change background if under cursor
if buffer.CursorPos.Equals(runeIndex, lineIndex) {
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)
// Change background if selected
if buffer.Selection != nil {
edge1, edge2 := buffer.GetSelectionEdges()
if ComparePositions(drawPosition, edge1) >= 0 && ComparePositions(drawPosition, edge2) <= 0 {
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.Offset.X, y-buffer.Offset.Y, r, nil, style)
}
}
}
}
window.screen.SetContent(x-buffer.Offset.X, y-buffer.Offset.Y, 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++
// Change position for next character
if r == '\t' {
x += int(Config.TabIndentation)
} else {
x++
}
}
// Draw new line
x = bufferX
y++
}
}
func (buffer *Buffer) Load() error {
@@ -113,7 +121,7 @@ func (buffer *Buffer) Load() error {
return err
}
buffer.Contents = string(content)
buffer.Contents = strings.Split(string(content), "\n")
return nil
}
@@ -133,12 +141,13 @@ func (buffer *Buffer) Save() error {
buffer.filename = filepath.Join(homedir, buffer.filename[2:])
}
// Append new line character at end of buffer contents if not present
if buffer.Contents == "" || buffer.Contents[len(buffer.Contents)-1] != '\n' {
buffer.Contents += "\n"
// Add newline at the end of buffer Contents
line := buffer.Contents[len(buffer.Contents)-1]
if len(line) != 0 {
buffer.Contents = append(buffer.Contents, "")
}
err := os.WriteFile(buffer.filename, []byte(buffer.Contents), 0644)
err := os.WriteFile(buffer.filename, []byte(buffer.GetContentsAsString()), 0644)
if err != nil {
return err
}
@@ -146,12 +155,60 @@ func (buffer *Buffer) Save() error {
return nil
}
func (buffer *Buffer) GetSelectionEdges() (int, int) {
if buffer.Selection == nil {
return -1, -1
func (buffer *Buffer) GetContentsAsString() string {
finalText := strings.Builder{}
for i, line := range buffer.Contents {
for _, rune := range line {
finalText.WriteRune(rune)
}
if i != len(buffer.Contents)-1 {
finalText.WriteRune('\n')
}
}
if buffer.Selection.selectionStart < buffer.Selection.selectionEnd {
return finalText.String()
}
func (buffer *Buffer) PositionToAbsolutePosition(position Position) int {
i := 0
for lineIndex, line := range buffer.Contents {
if len(line) == 0 {
line += " "
}
for runeIndex, _ := range line + " " {
if position.Equals(runeIndex, lineIndex) {
return i
}
i++
}
}
return i
}
func (buffer *Buffer) AbsolutePositionToPosition(absolutePosition int) Position {
i := 0
for lineIndex, line := range buffer.Contents {
for runeIndex, _ := range line + " " {
if i == absolutePosition {
return Position{runeIndex, lineIndex}
}
i++
}
}
lastLine := buffer.Contents[len(buffer.Contents)-1]
return Position{len(buffer.Contents) - 1, len(lastLine) - 1}
}
func (buffer *Buffer) GetSelectionEdges() (Position, Position) {
if buffer.Selection == nil {
return Position{-1, -1}, Position{-1, -1}
}
if ComparePositions(buffer.Selection.selectionStart, buffer.Selection.selectionEnd) == -1 {
return buffer.Selection.selectionStart, buffer.Selection.selectionEnd
} else {
return buffer.Selection.selectionEnd, buffer.Selection.selectionStart
@@ -162,113 +219,75 @@ func (buffer *Buffer) GetSelectedText() string {
if buffer.Selection == nil {
return ""
}
if len(buffer.Contents) == 0 {
return ""
}
start := buffer.Selection.selectionStart
end := buffer.Selection.selectionEnd
edge1, edge2 := buffer.GetSelectionEdges()
if start >= len(buffer.Contents) {
start = len(buffer.Contents) - 1
}
if end >= len(buffer.Contents) {
end = len(buffer.Contents) - 1
selectedText := strings.Builder{}
if r := buffer.GetCharAtPosition(edge1); r != 0 {
selectedText.WriteRune(r)
}
if start <= end {
return buffer.Contents[start : end+1]
} else {
return buffer.Contents[end : start+1]
for ComparePositions(edge1, edge2) < 0 {
edge1.X++
if edge1.X > len(buffer.Contents[edge1.Y]) {
if edge1.Y < len(buffer.Contents) {
edge1.Y++
edge1.X = 0
} else {
edge1.X = len(buffer.Contents[edge1.Y])
break
}
}
if r := buffer.GetCharAtPosition(edge1); r != 0 {
selectedText.WriteRune(buffer.GetCharAtPosition(edge1))
}
}
return selectedText.String()
}
func (buffer *Buffer) CutText(window *Window) (string, int) {
if buffer.Selection == nil {
// Copy line
copiedText := ""
startOfLine := window.CurrentBuffer.CursorPos
endOfLine := window.CurrentBuffer.CursorPos
// Add current letter to copied text
if buffer.CursorPos < len(buffer.Contents) {
copiedText = string(buffer.Contents[buffer.CursorPos])
}
// Find end of line
for i := buffer.CursorPos + 1; i < len(buffer.Contents); i++ {
currentLetter := buffer.Contents[i]
endOfLine++
copiedText += string(currentLetter)
if currentLetter == '\n' {
break
}
}
// Find start of line
for i := buffer.CursorPos - 1; i >= 0; i-- {
currentLetter := buffer.Contents[i]
if currentLetter != '\n' {
startOfLine--
copiedText = string(currentLetter) + copiedText
} else {
break
}
}
// Cut current line
cutText := buffer.Contents[buffer.CursorPos.Y] + "\n"
// Remove line from buffer contents
buffer.Contents = buffer.Contents[:startOfLine] + buffer.Contents[endOfLine+1:]
return copiedText, 0
} else {
// Copy selection
copiedText := buffer.GetSelectedText()
// Remove selected text
edge1, edge2 := buffer.GetSelectionEdges()
if edge2 == len(buffer.Contents) {
edge2 = len(buffer.Contents) - 1
if len(buffer.Contents) == 1 {
buffer.Contents[0] = ""
} else {
buffer.Contents = slices.Delete(buffer.Contents, buffer.CursorPos.Y, buffer.CursorPos.Y+1)
}
buffer.Contents = buffer.Contents[:edge1] + buffer.Contents[edge2+1:]
window.SetCursorPos(edge1)
buffer.CursorPos.Y -= 1
if buffer.CursorPos.Y < 0 {
buffer.CursorPos = Position{0, 0}
}
return cutText, 0
} else {
// Cut selection
cutText := buffer.GetSelectedText()
// Remove selected text
_, edge2 := buffer.GetSelectionEdges()
buffer.CursorPos = edge2
buffer.Delete(len(cutText))
// Remove selection
buffer.Selection = nil
return copiedText, 1
return cutText, 1
}
}
func (buffer *Buffer) CopyText() (string, int) {
if buffer.Selection == nil {
// Copy line
copiedText := ""
// Add current letter to copied text
if buffer.CursorPos < len(buffer.Contents) {
copiedText = string(buffer.Contents[buffer.CursorPos])
}
// Find end of line
for i := buffer.CursorPos + 1; i < len(buffer.Contents); i++ {
currentLetter := buffer.Contents[i]
copiedText += string(currentLetter)
if currentLetter == '\n' {
break
}
}
// Find start of line
for i := buffer.CursorPos - 1; i >= 0; i-- {
currentLetter := buffer.Contents[i]
if currentLetter != '\n' {
copiedText = string(currentLetter) + copiedText
} else {
break
}
}
// Cut current line
copiedText := buffer.Contents[buffer.CursorPos.Y] + "\n"
return copiedText, 0
} else {
@@ -278,77 +297,232 @@ func (buffer *Buffer) CopyText() (string, int) {
}
func (buffer *Buffer) PasteText(window *Window, text string) {
str := buffer.Contents
contents := buffer.GetContentsAsString()
// Remove selected text
if buffer.Selection != nil {
edge1, edge2 := buffer.GetSelectionEdges()
if edge2 == len(buffer.Contents) {
edge2 = len(buffer.Contents) - 1
absEdge1 := buffer.PositionToAbsolutePosition(edge1)
absEdge2 := buffer.PositionToAbsolutePosition(edge2)
if absEdge2 == len(buffer.Contents) {
absEdge2 = len(buffer.Contents) - 1
}
str = str[:edge1] + str[edge2+1:]
buffer.Contents = str
window.SetCursorPos(edge1)
contents = contents[:absEdge1] + contents[absEdge2+1:]
buffer.Contents = strings.Split(contents, "\n")
buffer.CursorPos = buffer.AbsolutePositionToPosition(absEdge1)
buffer.Selection = nil
}
index := buffer.CursorPos
if index == len(str) {
str += text
} else {
str = str[:index] + text + str[index:]
}
buffer.Contents = str
window.SetCursorPos(buffer.CursorPos + len(text))
buffer.WriteString(text)
}
func (buffer *Buffer) FindSubstring(substring string, afterPos int) int {
func (buffer *Buffer) FindSubstring(substring string, afterPos Position) Position {
// Return no match if afterPos is larger than the buffer contents size
if afterPos >= len(buffer.Contents) {
return -1
contents := buffer.GetContentsAsString()
absAfterPos := buffer.PositionToAbsolutePosition(afterPos)
if absAfterPos >= len(contents) {
return Position{-1, -1}
}
index := strings.Index(buffer.Contents[afterPos+1:], substring)
index := strings.Index(contents[absAfterPos+1:], substring)
if index != -1 {
index += afterPos + 1
index += absAfterPos + 1
}
return index
return buffer.AbsolutePositionToPosition(index)
}
func (buffer *Buffer) FindAndReplaceSubstring(substring, replacement string, afterPos int) int {
index := buffer.FindSubstring(substring, afterPos)
func (buffer *Buffer) FindAndReplaceSubstring(substring, replacement string, afterPos Position) Position {
// Return no match if afterPos is larger than the buffer contents size
contents := buffer.GetContentsAsString()
absAfterPos := buffer.PositionToAbsolutePosition(afterPos)
// Return if substring isn't found
if index == -1 {
return -1
if absAfterPos >= len(contents) {
return Position{-1, -1}
}
index := strings.Index(contents[absAfterPos+1:], substring)
if index != -1 {
index += absAfterPos + 1
}
// Replace substring with replacement string
buffer.Contents = buffer.Contents[:index] + replacement + buffer.Contents[index+len(substring):]
contents = contents[:index] + replacement + contents[index+len(substring):]
return index
buffer.Contents = strings.Split(contents, "\n")
return buffer.AbsolutePositionToPosition(index)
}
func (buffer *Buffer) FindAndReplaceAll(substring, replacement string) int {
replacements := 0
index := 0
for index != -1 {
index = buffer.FindAndReplaceSubstring(substring, replacement, index)
if index != -1 {
position := Position{}
for position.X != -1 && position.Y != -1 {
position = buffer.FindAndReplaceSubstring(substring, replacement, position)
if position.X != -1 && position.Y != -1 {
replacements++
}
if index == 0 {
index++
}
}
return replacements
}
func (buffer *Buffer) MoveUp(i int) bool {
buffer.CursorPos.Y -= i
if buffer.CursorPos.Y < 0 {
buffer.CursorPos.Y = 0
return false
}
if buffer.CursorPos.X >= len(buffer.Contents[buffer.CursorPos.Y]) {
buffer.CursorPos.X = len(buffer.Contents[buffer.CursorPos.Y])
}
return true
}
func (buffer *Buffer) MoveDown(i int) bool {
buffer.CursorPos.Y += i
if buffer.CursorPos.Y >= len(buffer.Contents) {
buffer.CursorPos.Y = len(buffer.Contents) - 1
return false
}
if buffer.CursorPos.X >= len(buffer.Contents[buffer.CursorPos.Y]) {
buffer.CursorPos.X = len(buffer.Contents[buffer.CursorPos.Y])
}
return true
}
func (buffer *Buffer) MoveLeft(i int) bool {
remainingSteps := i
for remainingSteps > 0 {
buffer.CursorPos.X--
if buffer.CursorPos.X < 0 {
if buffer.CursorPos.Y > 0 {
buffer.CursorPos.Y--
buffer.CursorPos.X = len(buffer.Contents[buffer.CursorPos.Y])
} else {
buffer.CursorPos.X = 0
return false
}
}
remainingSteps--
}
return true
}
func (buffer *Buffer) MoveRight(i int) bool {
remainingSteps := i
for remainingSteps > 0 {
buffer.CursorPos.X++
if buffer.CursorPos.X > len(buffer.Contents[buffer.CursorPos.Y]) {
if buffer.CursorPos.Y < len(buffer.Contents)-1 {
buffer.CursorPos.Y++
buffer.CursorPos.X = 0
} else {
buffer.CursorPos.X = len(buffer.Contents[buffer.CursorPos.Y])
return false
}
}
remainingSteps--
}
return true
}
func (buffer *Buffer) WriteRune(r rune) {
if r == '\n' {
if buffer.CursorPos.Y == len(buffer.Contents) {
buffer.Contents = append(buffer.Contents, "")
} else {
buffer.Contents = slices.Insert(buffer.Contents, buffer.CursorPos.Y+1, "")
}
// Move line content after cursor X to the new line
line := buffer.Contents[buffer.CursorPos.Y]
buffer.Contents[buffer.CursorPos.Y+1] = line[buffer.CursorPos.X:] + buffer.Contents[buffer.CursorPos.Y+1]
buffer.Contents[buffer.CursorPos.Y] = line[:buffer.CursorPos.X]
buffer.MoveDown(1)
buffer.CursorPos.X = 0
} else {
line := buffer.Contents[buffer.CursorPos.Y]
buffer.Contents[buffer.CursorPos.Y] = line[:buffer.CursorPos.X] + string(r) + line[buffer.CursorPos.X:]
buffer.MoveRight(1)
}
}
func (buffer *Buffer) WriteString(str string) {
for _, r := range str {
buffer.WriteRune(r)
}
}
func (buffer *Buffer) Delete(i int) bool {
remainingSteps := i
for remainingSteps > 0 {
buffer.CursorPos.X--
if buffer.CursorPos.X < 0 {
if buffer.CursorPos.Y > 0 {
// Save deleted line text
deletedLine := buffer.Contents[buffer.CursorPos.Y]
buffer.CursorPos.Y--
// Delete line
buffer.Contents = slices.Delete(buffer.Contents, buffer.CursorPos.Y+1, buffer.CursorPos.Y+2)
// Append deleted line text to end of current line
buffer.Contents[buffer.CursorPos.Y] += deletedLine
buffer.CursorPos.X = len(buffer.Contents[buffer.CursorPos.Y]) - len(deletedLine)
} else {
buffer.CursorPos.X = 0
return false
}
} else {
line := buffer.Contents[buffer.CursorPos.Y]
buffer.Contents[buffer.CursorPos.Y] = line[:buffer.CursorPos.X] + line[buffer.CursorPos.X+1:]
}
remainingSteps--
}
return true
}
func (buffer *Buffer) GetCharAtPosition(position Position) rune {
if position.Y < 0 || position.Y >= len(buffer.Contents) {
return 0
}
line := buffer.Contents[position.Y]
if position.X == len(line) {
// Do not return newline for last line if it's empty
if position.Y == len(buffer.Contents)-1 && len(line) == 0 {
return 0
}
return '\n'
} else if position.X < 0 || position.X > len(line) {
return 0
}
return rune(line[position.X])
}
func GetOpenFileBuffer(filename string) *Buffer {
// Replace tilde with home directory
if filename != "~" && strings.HasPrefix(filename, "~/") {
@@ -415,8 +589,8 @@ func CreateFileBuffer(filename string, openNonExistentFile bool) (*Buffer, error
buffer := Buffer{
Name: filename,
Contents: "",
CursorPos: 0,
Contents: make([]string, 1),
CursorPos: Position{0, 0},
canSave: true,
filename: abs,
}
@@ -438,8 +612,8 @@ func CreateFileBuffer(filename string, openNonExistentFile bool) (*Buffer, error
func CreateBuffer(bufferName string) (*Buffer, error) {
buffer := Buffer{
Name: bufferName,
Contents: "",
CursorPos: 0,
Contents: make([]string, 1),
CursorPos: Position{0, 0},
canSave: true,
filename: "",
}
+25 -13
View File
@@ -18,6 +18,20 @@ 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},
}
PrintMessage(window, "Selected all text.")
},
}
cutCmd := Command{
cmd: "cut",
run: func(window *Window, args ...string) {
@@ -49,7 +63,7 @@ func initCommands() {
if copyingMethod == 0 {
PrintMessage(window, "Copied line to clipboard.")
} else {
PrintMessage(window, "Copied selection to clipboard.")
PrintMessage(window, "Copied selection to clipboard. ")
}
},
}
@@ -59,7 +73,7 @@ func initCommands() {
run: func(window *Window, args ...string) {
if window.Clipboard != "" {
window.CurrentBuffer.PasteText(window, window.Clipboard)
PrintMessage(window, "Pasted text to buffer.")
PrintMessage(window, "Pasted text to buffer. ")
}
},
}
@@ -142,7 +156,6 @@ func initCommands() {
log.Fatalf("Could not reload buffer: %s", err)
}
window.SetCursorPos(window.CurrentBuffer.CursorPos)
PrintMessage(window, "Buffer reloaded.")
},
}
@@ -158,8 +171,8 @@ func initCommands() {
}
pos := window.CurrentBuffer.FindSubstring(input, window.CurrentBuffer.CursorPos)
if pos >= 0 {
window.SetCursorPos(pos)
if pos.X >= 0 && pos.Y >= 0 {
window.CurrentBuffer.CursorPos = pos
PrintMessage(window, "Match found.")
} else {
PrintMessage(window, fmt.Sprintf("'%s' not found in buffer!", input))
@@ -177,8 +190,8 @@ func initCommands() {
}
pos := window.CurrentBuffer.FindSubstring(input, window.CurrentBuffer.CursorPos)
if pos >= 0 {
window.SetCursorPos(pos)
if pos.X >= 0 && pos.Y >= 0 {
window.CurrentBuffer.CursorPos = pos
PrintMessage(window, "Match found.")
} else {
PrintMessage(window, fmt.Sprintf("'%s' not found in buffer!", input))
@@ -199,8 +212,8 @@ func initCommands() {
}
pos := window.CurrentBuffer.FindAndReplaceSubstring(findStr, replaceStr, window.CurrentBuffer.CursorPos)
if pos >= 0 {
window.SetCursorPos(pos)
if pos.X >= 0 && pos.Y >= 0 {
window.CurrentBuffer.CursorPos = pos
PrintMessage(window, "Match replaced successfully.")
} else {
PrintMessage(window, fmt.Sprintf("'%s' not found in buffer!", findStr))
@@ -220,8 +233,8 @@ func initCommands() {
replaceStr := <-inputChannel
pos := window.CurrentBuffer.FindAndReplaceSubstring(findStr, replaceStr, window.CurrentBuffer.CursorPos)
if pos >= 0 {
window.SetCursorPos(pos)
if pos.X >= 0 && pos.Y >= 0 {
window.CurrentBuffer.CursorPos = pos
PrintMessage(window, "Match replaced successfully.")
} else {
PrintMessage(window, fmt.Sprintf("'%s' not found in buffer!", findStr))
@@ -243,7 +256,6 @@ func initCommands() {
replacements := window.CurrentBuffer.FindAndReplaceAll(findStr, replaceStr)
if replacements > 0 {
window.SetCursorPos(window.CurrentBuffer.CursorPos)
PrintMessage(window, fmt.Sprintf("Replaced all %d matches successfully.", replacements))
} else {
PrintMessage(window, fmt.Sprintf("'%s' not found in buffer!", findStr))
@@ -264,7 +276,6 @@ func initCommands() {
replacements := window.CurrentBuffer.FindAndReplaceAll(findStr, replaceStr)
if replacements > 0 {
window.SetCursorPos(window.CurrentBuffer.CursorPos)
PrintMessage(window, fmt.Sprintf("Replaced all %d matches successfully.", replacements))
} else {
PrintMessage(window, fmt.Sprintf("'%s' not found in buffer!", findStr))
@@ -492,6 +503,7 @@ func initCommands() {
}
// Register commands
commands["select-all"] = &selectAll
commands["cut"] = &cutCmd
commands["copy"] = &copyCmd
commands["paste"] = &pasteCmd
+5 -5
View File
@@ -1,9 +1,9 @@
package main
import (
"github.com/gdamore/tcell/v2"
"strconv"
"strings"
"github.com/gdamore/tcell/v2"
)
func drawLineIndex(window *Window) {
@@ -16,9 +16,9 @@ func drawLineIndex(window *Window) {
_, bufferY1, _, bufferY2 := window.GetTextAreaDimensions()
lineIndex := 1 + buffer.OffsetY
lineIndex := 1 + buffer.Offset.Y
for y := bufferY1; y <= bufferY2; y++ {
if lineIndex > strings.Count(buffer.Contents, "\n")+1 {
if lineIndex > len(buffer.Contents) {
if Config.ExtendLineIndex {
for x := 0; x < lineIndexSize; x++ {
screen.SetContent(x, y, ' ', nil, lineIndexStyle)
@@ -42,7 +42,7 @@ func drawLineIndex(window *Window) {
}
func getLineIndexSize(window *Window) int {
i := strings.Count(window.CurrentBuffer.Contents, "\n") + 1
i := len(window.CurrentBuffer.Contents)
if i == 0 {
return 4
}
+24
View File
@@ -0,0 +1,24 @@
package main
type Position struct {
X int
Y int
}
func (position *Position) Equals(x, y int) bool {
return position.X == x && position.Y == y
}
func ComparePositions(pos1, pos2 Position) int {
if pos1.Y < pos2.Y {
return -1
} else if pos1.Y > pos2.Y {
return 1
} else if pos1.X < pos2.X {
return -1
} else if pos1.X > pos2.X {
return 1
} else {
return 0
}
}
+8 -11
View File
@@ -2,10 +2,11 @@ package main
import (
"fmt"
"github.com/gdamore/tcell/v2"
"path/filepath"
"strconv"
"strings"
"github.com/gdamore/tcell/v2"
)
type TopMenuButton struct {
@@ -140,22 +141,18 @@ func getBufferInfoMsg(window *Window) string {
filename = filepath.Base(window.CurrentBuffer.filename)
}
cursorPos := window.CurrentBuffer.CursorPos
cursorX, cursorY := window.GetCursorPos2D()
cursorX++
cursorY++
chars := len(window.CurrentBuffer.Contents)
words := len(strings.Fields(window.CurrentBuffer.Contents))
contents := window.CurrentBuffer.GetContentsAsString()
chars := len(contents)
words := len(strings.Fields(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, "%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))
ret = strings.ReplaceAll(ret, "%c", strconv.Itoa(chars))
ret = strings.ReplaceAll(ret, "%w", strconv.Itoa(words))
+113 -347
View File
@@ -1,13 +1,14 @@
package main
import (
"github.com/gdamore/tcell/v2"
"log"
"slices"
"strconv"
"strings"
"time"
"unicode"
"github.com/gdamore/tcell/v2"
)
type CursorMode uint8
@@ -98,6 +99,9 @@ func (window *Window) Draw() {
// Clear screen
window.screen.Clear()
// Sync buffer offset
window.SyncBufferOffset()
// Draw top menu
if window.ShowTopMenu {
drawTopMenu(window)
@@ -161,25 +165,24 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
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)
}
window.CurrentBuffer.MoveRight(1)
// Skip all spaces
for endOfWord < len(window.CurrentBuffer.Contents) && unicode.IsSpace(rune(window.CurrentBuffer.Contents[endOfWord])) {
endOfWord++
for unicode.IsSpace(window.CurrentBuffer.GetCharAtPosition(window.CurrentBuffer.CursorPos)) {
if !window.CurrentBuffer.MoveRight(1) {
break
}
}
// Find end of word
for endOfWord < len(window.CurrentBuffer.Contents) && !unicode.IsSpace(rune(window.CurrentBuffer.Contents[endOfWord])) {
endOfWord++
for !unicode.IsSpace(window.CurrentBuffer.GetCharAtPosition(window.CurrentBuffer.CursorPos)) {
if !window.CurrentBuffer.MoveRight(1) {
break
}
}
window.SetCursorPos(endOfWord)
} else {
// Move cursor one character backwards
window.SetCursorPos(window.CurrentBuffer.CursorPos + 1)
// Move cursor one character forwards
window.CurrentBuffer.MoveRight(1)
}
// Add to selection
@@ -187,7 +190,7 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
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.CursorPos = pos
}
window.CurrentBuffer.Selection = &Selection{
@@ -197,10 +200,6 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
} else {
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) {
window.CurrentBuffer.Selection.selectionEnd = len(window.CurrentBuffer.Contents) - 1
}
} else if window.CurrentBuffer.Selection != nil {
// Unset selection
window.CurrentBuffer.Selection = nil
@@ -214,28 +213,33 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
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
}
window.CurrentBuffer.MoveLeft(1)
// Skip all spaces
for startOfWord >= 0 && len(window.CurrentBuffer.Contents) != 0 && unicode.IsSpace(rune(window.CurrentBuffer.Contents[startOfWord])) {
startOfWord--
for unicode.IsSpace(window.CurrentBuffer.GetCharAtPosition(window.CurrentBuffer.CursorPos)) {
if !window.CurrentBuffer.MoveLeft(1) {
break
}
}
// Find start of word
for startOfWord >= 0 && len(window.CurrentBuffer.Contents) != 0 && !unicode.IsSpace(rune(window.CurrentBuffer.Contents[startOfWord])) {
startOfWord--
// Find end of word
for {
char := window.CurrentBuffer.GetCharAtPosition(window.CurrentBuffer.CursorPos)
if char == 0 || unicode.IsSpace(char) {
break
}
if !window.CurrentBuffer.MoveLeft(1) {
break
}
}
// Move one character to the right
startOfWord++
window.SetCursorPos(startOfWord)
// Move one character to the right if not selecting
if window.CurrentBuffer.CursorPos.X != 0 {
window.CurrentBuffer.MoveRight(1)
}
} else {
// Move cursor one character backwards
window.SetCursorPos(window.CurrentBuffer.CursorPos - 1)
window.CurrentBuffer.MoveLeft(1)
}
// Add to selection
@@ -243,7 +247,7 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
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.CursorPos = pos
}
window.CurrentBuffer.Selection = &Selection{
@@ -267,11 +271,11 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
if ev.Modifiers()&tcell.ModCtrl != 0 {
// Move cursor to top of buffer
window.SetCursorPos(0)
window.CurrentBuffer.CursorPos.X = 0
window.CurrentBuffer.CursorPos.Y = 0
} else {
// Move cursor one line up
x, y := window.GetCursorPos2D()
window.SetCursorPos2D(x, y-1)
window.CurrentBuffer.MoveUp(1)
}
// Add to selection
@@ -318,11 +322,11 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
if ev.Modifiers()&tcell.ModCtrl != 0 {
// Move cursor to bottom of buffer
window.SetCursorPos(len(window.CurrentBuffer.Contents))
window.CurrentBuffer.CursorPos.Y = len(window.CurrentBuffer.Contents) - 1
window.CurrentBuffer.CursorPos.X = len(window.CurrentBuffer.Contents[window.CurrentBuffer.CursorPos.Y])
} else {
// Move cursor one line down
x, y := window.GetCursorPos2D()
window.SetCursorPos2D(x, y+1)
window.CurrentBuffer.MoveDown(1)
}
// Add to selection
@@ -337,9 +341,9 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
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) {
window.CurrentBuffer.Selection.selectionEnd = len(window.CurrentBuffer.Contents) - 1
}
//if window.CurrentBuffer.Selection.selectionEnd >= len(window.CurrentBuffer.Contents) {
// window.CurrentBuffer.Selection.selectionEnd = len(window.CurrentBuffer.Contents) - 1
//}
} else if window.CurrentBuffer.Selection != nil {
// Unset selection
window.CurrentBuffer.Selection = nil
@@ -389,25 +393,12 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
}
// Typing
if ev.Key() == tcell.KeyBackspace2 {
if ev.Key() == tcell.KeyBackspace || ev.Key() == tcell.KeyBackspace2 {
if window.CursorMode == CursorModeBuffer {
str := window.CurrentBuffer.Contents
index := window.CurrentBuffer.CursorPos
if window.CurrentBuffer.Selection != nil {
edge1, edge2 := window.CurrentBuffer.GetSelectionEdges()
if edge2 == len(window.CurrentBuffer.Contents) {
edge2 = len(window.CurrentBuffer.Contents) - 1
}
str = str[:edge1] + str[edge2+1:]
window.CurrentBuffer.Contents = str
window.SetCursorPos(edge1)
window.CurrentBuffer.Selection = nil
} else if index != 0 {
str = str[:index-1] + str[index:]
window.CurrentBuffer.Contents = str
window.SetCursorPos(window.CurrentBuffer.CursorPos - 1)
window.CurrentBuffer.CutText(window)
} else {
window.CurrentBuffer.Delete(1)
}
} else if window.CursorMode == CursorModeInputBar {
str := currentInputRequest.input
@@ -421,57 +412,21 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
}
} else if ev.Key() == tcell.KeyTab {
if window.CursorMode == CursorModeBuffer {
str := window.CurrentBuffer.Contents
// Remove selected text
if window.CurrentBuffer.Selection != nil {
edge1, edge2 := window.CurrentBuffer.GetSelectionEdges()
if edge2 == len(window.CurrentBuffer.Contents) {
edge2 = len(window.CurrentBuffer.Contents) - 1
}
str = str[:edge1] + str[edge2+1:]
window.CurrentBuffer.Contents = str
window.SetCursorPos(edge1)
window.CurrentBuffer.Selection = nil
window.CurrentBuffer.CutText(window)
}
index := window.CurrentBuffer.CursorPos
if index == len(str) {
str += "\t"
} else {
str = str[:index] + "\t" + str[index:]
}
window.CurrentBuffer.Contents = str
window.SetCursorPos(window.CurrentBuffer.CursorPos + 1)
window.CurrentBuffer.WriteRune('\t')
}
} else if ev.Key() == tcell.KeyEnter {
if window.CursorMode == CursorModeBuffer {
str := window.CurrentBuffer.Contents
// Remove selected text
if window.CurrentBuffer.Selection != nil {
edge1, edge2 := window.CurrentBuffer.GetSelectionEdges()
if edge2 == len(window.CurrentBuffer.Contents) {
edge2 = len(window.CurrentBuffer.Contents) - 1
}
str = str[:edge1] + str[edge2+1:]
window.CurrentBuffer.Contents = str
window.SetCursorPos(edge1)
window.CurrentBuffer.Selection = nil
window.CurrentBuffer.CutText(window)
}
index := window.CurrentBuffer.CursorPos
if index == len(str) {
str += "\n"
} else {
str = str[:index] + "\n" + str[index:]
}
window.CurrentBuffer.Contents = str
window.SetCursorPos(window.CurrentBuffer.CursorPos + 1)
window.CurrentBuffer.WriteRune('\n')
} else if window.CursorMode == CursorModeInputBar {
if currentInputRequest.input == "" && slices.Index(inputHistory, currentInputRequest.input) == -1 {
inputHistory = append(inputHistory, currentInputRequest.input)
@@ -485,30 +440,12 @@ func (window *Window) handleKeyInput(ev *tcell.EventKey) {
}
} else if ev.Key() == tcell.KeyRune {
if window.CursorMode == CursorModeBuffer {
str := window.CurrentBuffer.Contents
// Remove selected text
if window.CurrentBuffer.Selection != nil {
edge1, edge2 := window.CurrentBuffer.GetSelectionEdges()
if edge2 == len(window.CurrentBuffer.Contents) {
edge2 = len(window.CurrentBuffer.Contents) - 1
}
str = str[:edge1] + str[edge2+1:]
window.CurrentBuffer.Contents = str
window.SetCursorPos(edge1)
window.CurrentBuffer.Selection = nil
window.CurrentBuffer.CutText(window)
}
index := window.CurrentBuffer.CursorPos
if index == len(str) {
str += string(ev.Rune())
} else {
str = str[:index] + string(ev.Rune()) + str[index:]
}
window.CurrentBuffer.Contents = str
window.SetCursorPos(window.CurrentBuffer.CursorPos + 1)
window.CurrentBuffer.WriteRune(ev.Rune())
} else if window.CursorMode == CursorModeInputBar {
str := currentInputRequest.input
index := currentInputRequest.cursorPos
@@ -535,14 +472,44 @@ func (window *Window) handleMouseInput(ev *tcell.EventMouse) {
// 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)
currentPos := window.CurrentBuffer.CursorPos
mouseBufferPos := Position{mouseX + window.CurrentBuffer.Offset.X - x1, mouseY + window.CurrentBuffer.Offset.Y - y1}
// Keep mouse Y in bounds
if mouseBufferPos.Y >= len(window.CurrentBuffer.Contents) {
mouseBufferPos.Y = len(window.CurrentBuffer.Contents) - 1
}
// Offset mouse X for each tab character in line
posInLine := make([]int, 0)
for i, r := range window.CurrentBuffer.Contents[mouseBufferPos.Y] + " " {
if r == '\t' {
for j := 0; j < Config.TabIndentation; j++ {
posInLine = append(posInLine, i)
}
} else {
posInLine = append(posInLine, i)
}
}
if len(posInLine) == 0 {
mouseBufferPos.X = 0
} else if mouseBufferPos.X >= len(posInLine) {
mouseBufferPos.X = posInLine[len(posInLine)-1]
} else {
mouseBufferPos.X = posInLine[mouseBufferPos.X]
}
// Keep mouse X in bounds
if mouseBufferPos.X > len(window.CurrentBuffer.Contents[mouseBufferPos.Y]) {
mouseBufferPos.X = len(window.CurrentBuffer.Contents[mouseBufferPos.Y])
}
if mouseHeld {
// Add to selection
if window.CurrentBuffer.Selection == nil {
window.CurrentBuffer.Selection = &Selection{
selectionStart: window.CurrentBuffer.CursorPos,
selectionEnd: window.CursorPos2DToCursorPos(bufferMouseX, bufferMouseY),
selectionEnd: mouseBufferPos,
}
// Set last click time
@@ -550,22 +517,19 @@ func (window *Window) handleMouseInput(ev *tcell.EventMouse) {
return
} else {
window.CurrentBuffer.Selection.selectionEnd = window.CursorPos2DToCursorPos(bufferMouseX, bufferMouseY)
window.CurrentBuffer.Selection.selectionEnd = mouseBufferPos
}
// Prevent selecting dummy character at the end of the buffer
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 {
} else if currentPos == mouseBufferPos && 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
cursorPos := window.CurrentBuffer.CursorPos
startOfWord := window.CurrentBuffer.CursorPos.X
endOfWord := window.CurrentBuffer.CursorPos.X
// Find end of word
for i := window.CurrentBuffer.CursorPos + 1; i < len(window.CurrentBuffer.Contents); i++ {
currentRune := rune(window.CurrentBuffer.Contents[i])
for i := cursorPos.X + 1; i < len(window.CurrentBuffer.Contents[cursorPos.Y]); i++ {
currentRune := rune(window.CurrentBuffer.Contents[cursorPos.Y][i])
if unicode.IsLetter(currentRune) || unicode.IsDigit(currentRune) || currentRune == '_' {
endOfWord++
} else {
@@ -574,8 +538,8 @@ func (window *Window) handleMouseInput(ev *tcell.EventMouse) {
}
// Find start of word
for i := window.CurrentBuffer.CursorPos - 1; i >= 0; i-- {
currentRune := rune(window.CurrentBuffer.Contents[i])
for i := cursorPos.X - 1; i >= 0; i-- {
currentRune := rune(window.CurrentBuffer.Contents[cursorPos.Y][i])
if unicode.IsLetter(currentRune) || unicode.IsDigit(currentRune) || currentRune == '_' {
startOfWord--
} else {
@@ -585,38 +549,17 @@ func (window *Window) handleMouseInput(ev *tcell.EventMouse) {
// Add to selection
window.CurrentBuffer.Selection = &Selection{
selectionStart: startOfWord,
selectionEnd: endOfWord,
selectionStart: Position{startOfWord, cursorPos.Y},
selectionEnd: Position{endOfWord, cursorPos.Y},
}
} 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
}
}
cursorPos := window.CurrentBuffer.CursorPos
// Add to selection
window.CurrentBuffer.Selection = &Selection{
selectionStart: startOfLine,
selectionEnd: endOfLine,
selectionStart: Position{0, cursorPos.Y},
selectionEnd: Position{len(window.CurrentBuffer.Contents[cursorPos.Y]), cursorPos.Y},
}
}
@@ -631,7 +574,7 @@ func (window *Window) handleMouseInput(ev *tcell.EventMouse) {
}
}
// Move cursor
window.SetCursorPos2D(bufferMouseX, bufferMouseY)
window.CurrentBuffer.CursorPos = mouseBufferPos
// Set last click time
lastClick = time.Now().UnixMilli()
@@ -667,196 +610,19 @@ func (window *Window) GetTextAreaDimensions() (int, int, int, int) {
return x1, y1, x2 - 1, y2 - 2
}
func (window *Window) CursorPos2DToCursorPos(x, y int) int {
// Ensure x and y are positive
x = max(x, 0)
y = max(y, 0)
// Set cursor position to 0 buffer is empty
if len(window.CurrentBuffer.Contents) == 0 {
return 0
}
// Create line slice from buffer contents
lines := make([]struct {
charIndex int
str string
}, 0)
var str string
for i, char := range window.CurrentBuffer.Contents {
str += string(char)
if char == '\n' || i == len(window.CurrentBuffer.Contents)-1 {
lines = append(lines, struct {
charIndex int
str string
}{charIndex: i - len(str) + 1, str: str})
str = ""
}
}
// Append extra character or line
if window.CurrentBuffer.Contents[len(window.CurrentBuffer.Contents)-1] == '\n' {
lines = append(lines, struct {
charIndex int
str string
}{charIndex: len(window.CurrentBuffer.Contents), str: " "})
} else {
lines[len(lines)-1].str += " "
}
// Limit x and y
y = min(y, len(lines)-1)
x = min(x, len(lines[y].str)-1)
return lines[y].charIndex + x
}
func (window *Window) AbsolutePosToCursorPos2D(x, y int) (int, int) {
x1, y1, _, _ := window.GetTextAreaDimensions()
x -= x1
y -= y1
x += window.CurrentBuffer.OffsetX
y += window.CurrentBuffer.OffsetY
if x < 0 {
x = 0
}
if y < 0 {
y = 0
}
split := strings.SplitAfter(window.CurrentBuffer.Contents+" ", "\n")
if y >= len(split) {
y = len(split) - 1
}
line := split[y]
posInLine := make([]int, 0)
for i, char := range []rune(line) {
if char == '\t' {
for j := 0; j < Config.TabIndentation; j++ {
posInLine = append(posInLine, i)
}
} else {
posInLine = append(posInLine, i)
}
}
if len(posInLine) == 0 {
x = 0
} else if x >= len(posInLine) {
x = posInLine[len(posInLine)-1]
} else {
x = posInLine[x]
}
return x, y
}
func (window *Window) GetAbsoluteCursorPos() (int, int) {
cursorX, cursorY := window.GetCursorPos2D()
x1, y1, _, _ := window.GetTextAreaDimensions()
cursorX += x1
cursorY += y1
return cursorX, cursorY
}
func (window *Window) GetCursorPos2D() (int, int) {
cursorX := 0
cursorY := 0
for i := 0; i < window.CurrentBuffer.CursorPos; i++ {
char := window.CurrentBuffer.Contents[i]
if char == '\n' {
cursorY++
cursorX = 0
} else {
cursorX++
}
}
return cursorX, cursorY
}
func (window *Window) SetCursorPos(position int) {
window.CurrentBuffer.CursorPos = position
if window.CurrentBuffer.CursorPos < 0 {
window.CurrentBuffer.CursorPos = 0
}
if window.CurrentBuffer.CursorPos > len(window.CurrentBuffer.Contents) {
window.CurrentBuffer.CursorPos = len(window.CurrentBuffer.Contents)
}
window.SyncBufferOffset()
}
func (window *Window) SetCursorPos2D(x, y int) {
// Ensure x and y are positive
x = max(x, 0)
y = max(y, 0)
// Set cursor position to 0 buffer is empty
if len(window.CurrentBuffer.Contents) == 0 {
window.SetCursorPos(0)
return
}
// Create line slice from buffer contents
lines := make([]struct {
charIndex int
str string
}, 0)
var str string
for i, char := range window.CurrentBuffer.Contents {
str += string(char)
if char == '\n' || i == len(window.CurrentBuffer.Contents)-1 {
lines = append(lines, struct {
charIndex int
str string
}{charIndex: i - len(str) + 1, str: str})
str = ""
}
}
// Append extra character or line
if window.CurrentBuffer.Contents[len(window.CurrentBuffer.Contents)-1] == '\n' {
lines = append(lines, struct {
charIndex int
str string
}{charIndex: len(window.CurrentBuffer.Contents), str: " "})
} else {
lines[len(lines)-1].str += " "
}
// Limit x and y
y = min(y, len(lines)-1)
x = min(x, len(lines[y].str)-1)
window.SetCursorPos(lines[y].charIndex + x)
}
func (window *Window) SyncBufferOffset() {
x, y := window.GetCursorPos2D()
cursorPos := window.CurrentBuffer.CursorPos
bufferX1, bufferY1, bufferX2, bufferY2 := window.GetTextAreaDimensions()
if y < window.CurrentBuffer.OffsetY {
window.CurrentBuffer.OffsetY = y
} else if y > window.CurrentBuffer.OffsetY+(bufferY2-bufferY1) {
window.CurrentBuffer.OffsetY = y - (bufferY2 - bufferY1)
if cursorPos.Y < window.CurrentBuffer.Offset.Y {
window.CurrentBuffer.Offset.Y = cursorPos.Y
} else if cursorPos.Y > window.CurrentBuffer.Offset.Y+(bufferY2-bufferY1) {
window.CurrentBuffer.Offset.Y = cursorPos.Y - (bufferY2 - bufferY1)
}
if x < window.CurrentBuffer.OffsetX {
window.CurrentBuffer.OffsetX = x
} else if x > window.CurrentBuffer.OffsetX+(bufferX2-bufferX1) {
window.CurrentBuffer.OffsetX = x - (bufferX2 - bufferX1)
if cursorPos.X < window.CurrentBuffer.Offset.X {
window.CurrentBuffer.Offset.X = cursorPos.X
} else if cursorPos.X > window.CurrentBuffer.Offset.X+(bufferX2-bufferX1) {
window.CurrentBuffer.Offset.X = cursorPos.X - (bufferX2 - bufferX1)
}
}