2021-04-02 15:36:43 -04:00
|
|
|
package document
|
2021-04-01 12:02:17 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"log"
|
|
|
|
"os"
|
2021-04-02 14:52:44 -04:00
|
|
|
"timshome.page/gilo/internal/gilo"
|
2021-04-01 12:02:17 -04:00
|
|
|
)
|
|
|
|
|
2021-04-02 14:52:44 -04:00
|
|
|
type Document struct {
|
2021-04-01 20:26:40 -04:00
|
|
|
dirty bool
|
2021-04-02 15:36:43 -04:00
|
|
|
Filename string
|
2021-04-02 14:52:44 -04:00
|
|
|
rows []*Row
|
2021-04-01 12:02:17 -04:00
|
|
|
}
|
|
|
|
|
2021-04-02 14:52:44 -04:00
|
|
|
func NewDocument() *Document {
|
|
|
|
var rows []*Row
|
2021-04-01 12:02:17 -04:00
|
|
|
|
2021-04-02 14:52:44 -04:00
|
|
|
return &Document{
|
2021-04-01 20:26:40 -04:00
|
|
|
false,
|
2021-04-01 12:02:17 -04:00
|
|
|
"",
|
|
|
|
rows,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 14:52:44 -04:00
|
|
|
func (d *Document) Open(filename string) {
|
2021-04-01 12:02:17 -04:00
|
|
|
file, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to open file")
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
2021-04-02 15:36:43 -04:00
|
|
|
d.Filename = filename
|
2021-04-01 12:02:17 -04:00
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
scanner.Split(bufio.ScanLines)
|
|
|
|
|
|
|
|
for scanner.Scan() {
|
2021-04-02 14:52:44 -04:00
|
|
|
d.AppendRow(scanner.Text())
|
2021-04-01 12:02:17 -04:00
|
|
|
}
|
2021-04-02 10:07:37 -04:00
|
|
|
|
|
|
|
d.dirty = false
|
2021-04-01 12:02:17 -04:00
|
|
|
}
|
|
|
|
|
2021-04-02 14:52:44 -04:00
|
|
|
func (d *Document) Save() int {
|
2021-04-02 15:36:43 -04:00
|
|
|
if d.Filename == "" {
|
2021-04-01 20:23:51 -04:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2021-04-02 15:36:43 -04:00
|
|
|
file, err := os.OpenFile(d.Filename, os.O_RDWR|os.O_CREATE, 0644)
|
2021-04-01 20:23:51 -04:00
|
|
|
if err != nil {
|
|
|
|
panic("failed to open/create file for saving")
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
2021-04-02 14:52:44 -04:00
|
|
|
fileStr := d.ToString()
|
2021-04-01 20:23:51 -04:00
|
|
|
fileLen := len(fileStr)
|
|
|
|
|
|
|
|
err = file.Truncate(int64(fileLen))
|
|
|
|
if err != nil {
|
|
|
|
panic("failed to truncate file")
|
|
|
|
}
|
|
|
|
|
|
|
|
size, err := file.WriteString(fileStr)
|
|
|
|
|
|
|
|
if err != nil {
|
2021-04-02 14:52:44 -04:00
|
|
|
panic("failed to save Document to file")
|
2021-04-01 20:23:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if fileLen == size {
|
2021-04-06 09:58:45 -04:00
|
|
|
d.dirty = false
|
|
|
|
|
2021-04-01 20:23:51 -04:00
|
|
|
return size
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2021-04-02 19:24:20 -04:00
|
|
|
func (d *Document) GetRow(at int) *Row {
|
2021-04-02 14:52:44 -04:00
|
|
|
return d.rows[at]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *Document) AppendRow(s string) {
|
2021-04-01 12:02:17 -04:00
|
|
|
newRow := newRow(s)
|
|
|
|
newRow.update()
|
|
|
|
d.rows = append(d.rows, newRow)
|
2021-04-02 10:07:37 -04:00
|
|
|
|
|
|
|
d.dirty = true
|
2021-04-01 12:02:17 -04:00
|
|
|
}
|
|
|
|
|
2021-04-02 18:43:49 -04:00
|
|
|
func (d *Document) InsertRow(at int, s string) {
|
|
|
|
if at < 0 || at > d.RowCount() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var newRows []*Row
|
|
|
|
|
|
|
|
// Split the array of rows at the specified index
|
|
|
|
start := d.rows[0:at]
|
2021-04-02 19:24:58 -04:00
|
|
|
end := d.rows[at:d.RowCount()]
|
2021-04-02 18:43:49 -04:00
|
|
|
|
|
|
|
// Splice it back together
|
|
|
|
newRows = append(newRows, start...)
|
|
|
|
newRows = append(newRows, newRow(s))
|
|
|
|
newRows = append(newRows, end...)
|
|
|
|
|
|
|
|
d.rows = newRows
|
|
|
|
d.dirty = true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *Document) InsertNewline(at *gilo.Point) {
|
|
|
|
if at.X == 0 {
|
|
|
|
d.InsertRow(at.Y, "")
|
|
|
|
} else {
|
|
|
|
row := d.rows[at.Y]
|
|
|
|
|
|
|
|
// Insert the characters right of the cursor to the next line
|
2021-04-02 19:24:58 -04:00
|
|
|
d.InsertRow(at.Y+1, string(row.chars[at.X:row.Size()]))
|
2021-04-02 18:43:49 -04:00
|
|
|
|
|
|
|
// Remove the characters copied to the next line
|
|
|
|
row.chars = row.chars[0:at.X]
|
|
|
|
row.update()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 16:14:19 -04:00
|
|
|
func (d *Document) MergeRows(to int, from int) {
|
|
|
|
d.rows[to].appendString(d.rows[from].toString())
|
|
|
|
d.delRow(from)
|
|
|
|
}
|
|
|
|
|
2021-04-02 14:52:44 -04:00
|
|
|
func (d *Document) ToString() string {
|
2021-04-02 15:36:43 -04:00
|
|
|
buf := gilo.NewBuffer()
|
2021-04-01 20:23:51 -04:00
|
|
|
|
|
|
|
for _, row := range d.rows {
|
2021-04-02 13:11:54 -04:00
|
|
|
buf.Append(row.toString())
|
|
|
|
buf.AppendRune('\n')
|
2021-04-01 20:23:51 -04:00
|
|
|
}
|
|
|
|
|
2021-04-02 13:11:54 -04:00
|
|
|
return buf.ToString()
|
2021-04-01 20:23:51 -04:00
|
|
|
}
|
|
|
|
|
2021-04-02 14:52:44 -04:00
|
|
|
func (d *Document) RowCount() int {
|
2021-04-01 12:02:17 -04:00
|
|
|
return len(d.rows)
|
2021-04-01 16:17:13 -04:00
|
|
|
}
|
2021-04-02 14:52:44 -04:00
|
|
|
|
|
|
|
func (d *Document) InsertChar(at *gilo.Point, ch rune) {
|
|
|
|
d.rows[at.Y].insertRune(ch, at.X)
|
2021-04-02 15:36:43 -04:00
|
|
|
|
|
|
|
d.dirty = true
|
2021-04-02 14:52:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *Document) DelChar(at *gilo.Point) {
|
|
|
|
d.rows[at.Y].deleteRune(at.X)
|
2021-04-02 15:36:43 -04:00
|
|
|
|
|
|
|
d.dirty = true
|
2021-04-02 14:52:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *Document) IsDirty() bool {
|
|
|
|
return d.dirty
|
|
|
|
}
|
2021-04-02 16:14:19 -04:00
|
|
|
|
|
|
|
func (d *Document) delRow(at int) {
|
|
|
|
var newSlice []*Row
|
|
|
|
|
|
|
|
// Split the array of rows at the specified index
|
|
|
|
start := d.rows[0:at]
|
|
|
|
end := d.rows[at+1 : d.RowCount()] // Skip the index in question
|
|
|
|
|
|
|
|
// Splice it back together
|
|
|
|
newSlice = append(newSlice, start...)
|
|
|
|
newSlice = append(newSlice, end...)
|
|
|
|
|
|
|
|
d.rows = newSlice
|
|
|
|
d.dirty = true
|
|
|
|
}
|