aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editor/buffer.go (renamed from editor/text.go)365
-rw-r--r--editor/commands.go100
-rw-r--r--editor/editor.go79
-rw-r--r--editor/file.go4
-rw-r--r--todo27
-rw-r--r--ui/cli.go2
-rw-r--r--ui/tcell/layout.go32
-rw-r--r--ui/tcell/style.go23
-rw-r--r--ui/tcell/tcell.go101
-rw-r--r--ui/tcell/view.go72
-rw-r--r--ui/tcell/window.go46
-rw-r--r--ui/ui.go7
12 files changed, 429 insertions, 429 deletions
diff --git a/editor/text.go b/editor/buffer.go
index c9a62ce..189b57b 100644
--- a/editor/text.go
+++ b/editor/buffer.go
@@ -15,8 +15,7 @@ import (
)
const (
- BufferScratch uint8 = iota
- BufferFile
+ BufferFile uint8 = iota
BufferDir
)
@@ -25,7 +24,7 @@ const (
// Although the underlying buffer is a pure byte slice, Buffer only works with runes and UTF-8.
type Buffer struct {
buf *gapbuffer.Buffer
- file *File
+ file *file
what uint8
dirty bool
q0, q1 int // dot/cursor
@@ -35,24 +34,27 @@ type Buffer struct {
history History // undo/redo stack
}
-func (t *Buffer) initBuffer() {
- if t.buf == nil {
- t.buf = &gapbuffer.Buffer{}
+// initBuffer initialized a nil buffer into the zero value of buffer.
+func (b *Buffer) initBuffer() {
+ if b.buf == nil {
+ b.buf = &gapbuffer.Buffer{}
}
}
-func (t *Buffer) NewFile(fn string) {
- t.file = &File{name: fn}
+// NewFile sets a filename for the buffer.
+func (b *Buffer) NewFile(fn string) {
+ b.file = &file{name: fn}
}
-func (t *Buffer) ReadFile() error {
- t.initBuffer()
+// ReadFile reads content of the buffer's filename into the buffer.
+func (b *Buffer) ReadFile() error {
+ b.initBuffer()
- if t.file == nil || t.file.read {
+ if b.file == nil || b.file.read {
return nil // silent
}
- info, err := os.Stat(t.file.name)
+ info, err := os.Stat(b.file.name)
if err != nil {
// if the file exists, print why we could not open it
// otherwise just close silently
@@ -64,12 +66,12 @@ func (t *Buffer) ReadFile() error {
// name is a directory; list it's content into the buffer
if info.IsDir() {
- files, err := ioutil.ReadDir(t.file.name)
+ files, err := ioutil.ReadDir(b.file.name)
if err != nil {
return fmt.Errorf("%s", err)
}
- t.what = BufferDir
+ b.what = BufferDir
// list files in dir
for _, f := range files {
@@ -77,19 +79,19 @@ func (t *Buffer) ReadFile() error {
if f.IsDir() {
dirchar = string(filepath.Separator)
}
- fmt.Fprintf(t.buf, "%s%s\n", f.Name(), dirchar)
+ fmt.Fprintf(b.buf, "%s%s\n", f.Name(), dirchar)
}
return nil
}
// name is a file
- fh, err := os.OpenFile(t.file.name, os.O_RDWR|os.O_CREATE, 0644)
+ fh, err := os.OpenFile(b.file.name, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return fmt.Errorf("%s", err)
}
defer fh.Close()
- if _, err := io.Copy(t.buf, fh); err != nil {
+ if _, err := io.Copy(b.buf, fh); err != nil {
return fmt.Errorf("%s", err)
}
fh.Seek(0, 0)
@@ -98,24 +100,25 @@ func (t *Buffer) ReadFile() error {
if _, err := io.Copy(h, fh); err != nil {
return fmt.Errorf("%s", err)
}
- t.file.sha256 = fmt.Sprintf("%x", h.Sum(nil))
+ b.file.sha256 = fmt.Sprintf("%x", h.Sum(nil))
- t.file.mtime = info.ModTime()
- t.file.read = true
+ b.file.mtime = info.ModTime()
+ b.file.read = true
- t.what = BufferFile
+ b.what = BufferFile
return nil
}
-func (t *Buffer) SaveFile() (int, error) {
- t.initBuffer()
+// SaveFile writes content of buffer to its filename.
+func (b *Buffer) SaveFile() (int, error) {
+ b.initBuffer()
- if t.file == nil || t.file.name == "" {
+ if b.file == nil || b.file.name == "" {
return 0, errors.New("no filename")
}
- if t.what != BufferFile { // can only save file buffers
+ if b.what != BufferFile { // can only save file buffers
return 0, nil
}
@@ -127,10 +130,10 @@ func (t *Buffer) SaveFile() (int, error) {
// namechange = true // to skip sha256 checksum
// }
- f, err := os.OpenFile(t.file.name, os.O_RDWR|os.O_CREATE, 0644)
+ f, err := os.OpenFile(b.file.name, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
if os.IsExist(err) {
- return 0, fmt.Errorf("%s already exists", t.file.name)
+ return 0, fmt.Errorf("%s already exists", b.file.name)
}
return 0, err
}
@@ -147,41 +150,42 @@ func (t *Buffer) SaveFile() (int, error) {
// return 0, errors.Errorf("file has been modified outside of poe")
// }
- n, err := f.WriteAt(t.buf.Bytes(), 0)
+ n, err := f.WriteAt(b.buf.Bytes(), 0)
if err != nil {
return 0, err
}
f.Truncate(int64(n))
f.Sync()
- t.file.sha256 = fmt.Sprintf("%x", sha256.Sum256(t.buf.Bytes()))
+ b.file.sha256 = fmt.Sprintf("%x", sha256.Sum256(b.buf.Bytes()))
info, err := f.Stat()
if err != nil {
return n, err
}
- t.file.mtime = info.ModTime()
+ b.file.mtime = info.ModTime()
- t.dirty = false
+ b.dirty = false
return n, nil
}
// Name returns either the file from disk name or empty string if the buffer has no disk counterpart.
-func (t *Buffer) Name() string {
- if t.file == nil || t.file.name == "" {
+func (b *Buffer) Name() string {
+ if b.file == nil || b.file.name == "" {
return ""
}
- s, _ := filepath.Abs(t.file.name)
+ s, _ := filepath.Abs(b.file.name)
return s
}
-func (t *Buffer) WorkDir() string {
- switch t.what {
- case BufferFile, BufferScratch:
- return filepath.Dir(t.Name())
+// WorkDir returns the working directory of the underlying file, ie the absolute path to the file with the last part stripped. If file is a directory, its name is returned as is.
+func (b *Buffer) WorkDir() string {
+ switch b.what {
+ case BufferFile:
+ return filepath.Dir(b.Name())
case BufferDir:
- return t.Name()
+ return b.Name()
default:
return ""
}
@@ -190,97 +194,108 @@ func (t *Buffer) WorkDir() string {
// Write implements io.Writer, with the side effect of storing written data into a history stack for undo/redo.
//
// If dot has content, it will be replaced by an initial deletion before inserting the bytes.
-func (t *Buffer) Write(p []byte) (int, error) {
- t.initBuffer()
+func (b *Buffer) Write(p []byte) (int, error) {
+ b.initBuffer()
// handle replace
- if len(t.ReadDot()) > 0 {
- t.Delete()
+ if len(b.ReadDot()) > 0 {
+ b.Delete()
}
// do the actual insertion
- c := Change{t.q0, HInsert, p}
- n, err := t.commit(c)
+ c := Change{b.q0, HInsert, p}
+ n, err := b.commit(c)
if err != nil {
return n, err
}
- t.history.Do(c)
- t.SeekDot(n, 1) // move dot
+ b.history.Do(c)
+ b.SeekDot(n, 1) // move dot
+ if b.what == BufferFile {
+ b.dirty = true
+ }
return n, nil
}
// Delete removes current selection in dot. If dot is empty, it selects the previous rune and deletes that.
-func (t *Buffer) Delete() (int, error) {
- t.initBuffer()
+func (b *Buffer) Delete() (int, error) {
+ b.initBuffer()
- if len(t.ReadDot()) == 0 {
- t.q0--
- c, _ := t.buf.ByteAt(t.q0)
+ if len(b.ReadDot()) == 0 {
+ b.q0--
+ c, _ := b.buf.ByteAt(b.q0)
for !utf8.RuneStart(c) {
- t.q0--
- c, _ = t.buf.ByteAt(t.q0)
+ b.q0--
+ c, _ = b.buf.ByteAt(b.q0)
}
- if t.q0 < 0 {
+ if b.q0 < 0 {
return 0, nil
}
}
- c := Change{t.q0, HDelete, []byte(t.ReadDot())}
- n, err := t.commit(c)
+ c := Change{b.q0, HDelete, []byte(b.ReadDot())}
+ n, err := b.commit(c)
if err != nil {
return n, err
}
- t.history.Do(c)
+ b.history.Do(c)
+ if b.what == BufferFile {
+ b.dirty = true
+ }
return n, nil
}
// Len returns the number of bytes in buffer.
-func (t *Buffer) Len() int {
- t.initBuffer()
+func (b *Buffer) Len() int {
+ b.initBuffer()
- return t.buf.Len()
+ return b.buf.Len()
}
// String returns the entire text buffer as a string.
-func (t *Buffer) String() string {
- t.initBuffer()
+func (b *Buffer) String() string {
+ b.initBuffer()
+
+ return string(b.buf.Bytes())
+}
- return string(t.buf.Bytes())
+// Dirty returns true if the buffer has changed since last save.
+func (b *Buffer) Dirty() bool {
+ return b.dirty
}
// ReadRune reads a rune from buffer and advances the internal offset. This could be called in sequence to get all runes from buffer. This populates LastRune().
-func (t *Buffer) ReadRune() (r rune, size int, err error) {
- r, size, err = t.ReadRuneAt(t.off)
- t.off += size
- t.lastRune = r
+func (b *Buffer) ReadRune() (r rune, size int, err error) {
+ r, size, err = b.ReadRuneAt(b.off)
+ b.off += size
+ b.lastRune = r
return
}
// UnreadRune returns the rune before the current Seek offset and moves the offset to point to that. This could be called in sequence to scan backwards.
-func (t *Buffer) UnreadRune() (r rune, size int, err error) {
- t.off--
- r, size, err = t.ReadRuneAt(t.off)
- t.off++
+func (b *Buffer) UnreadRune() (r rune, size int, err error) {
+ b.off--
+ r, size, err = b.ReadRuneAt(b.off)
+ b.off++
if err != nil {
return
}
- t.off -= size
+ b.off -= size
return
}
// ReadRuneAt returns the rune and its size at offset. If the given offset (in byte count) is not a valid rune, it will try to back up until it finds a valid starting point for a rune and return that one.
//
// This is basically a Seek(offset) followed by a ReadRune(), but does not affect the internal offset for future reads.
-func (t *Buffer) ReadRuneAt(offset int) (r rune, size int, err error) {
- t.initBuffer()
+func (b *Buffer) ReadRuneAt(offset int) (r rune, size int, err error) {
+ b.initBuffer()
var c byte
- c, err = t.buf.ByteAt(offset)
+ c, err = b.buf.ByteAt(offset)
if err != nil {
return 0, 0, err
}
for !utf8.RuneStart(c) {
offset--
- c, err = t.buf.ByteAt(offset)
+ c, err = b.buf.ByteAt(offset)
if err != nil {
return 0, 0, err
}
@@ -290,32 +305,32 @@ func (t *Buffer) ReadRuneAt(offset int) (r rune, size int, err error) {
return rune(c), 1, nil
}
- if cap(t.runeBuf) < 4 {
- t.runeBuf = make([]byte, 4) // max length of a rune
+ if cap(b.runeBuf) < 4 {
+ b.runeBuf = make([]byte, 4) // max length of a rune
}
- _, err = t.buf.ReadAt(t.runeBuf, offset)
+ _, err = b.buf.ReadAt(b.runeBuf, offset)
if err != nil {
return 0, 0, err
}
- r, n := utf8.DecodeRune(t.runeBuf)
+ r, n := utf8.DecodeRune(b.runeBuf)
return r, n, nil
}
// LastRune returns the last rune read by ReadRune().
-func (t *Buffer) LastRune() rune {
- return t.lastRune
+func (b *Buffer) LastRune() rune {
+ return b.lastRune
}
// ReadDot returns content of current dot.
-func (t *Buffer) ReadDot() string {
- t.initBuffer()
+func (b *Buffer) ReadDot() string {
+ b.initBuffer()
- if t.q0 == t.q1 {
+ if b.q0 == b.q1 {
return ""
}
- buf := make([]byte, t.q1-t.q0)
- _, err := t.buf.ReadAt(buf, t.q0)
+ buf := make([]byte, b.q1-b.q0)
+ _, err := b.buf.ReadAt(buf, b.q0)
if err != nil {
return ""
}
@@ -323,47 +338,47 @@ func (t *Buffer) ReadDot() string {
}
// Dot returns current offsets for dot.
-func (t *Buffer) Dot() (int, int) {
- return t.q0, t.q1
+func (b *Buffer) Dot() (int, int) {
+ return b.q0, b.q1
}
// Seek implements io.Seeker and sets the internal offset for next ReadRune() or UnreadRune(). If the offset is not a valid rune start, it will backup until it finds one.
-func (t *Buffer) Seek(offset, whence int) (int, error) {
- t.initBuffer()
+func (b *Buffer) Seek(offset, whence int) (int, error) {
+ b.initBuffer()
- t.off = offset
+ b.off = offset
switch whence {
case io.SeekStart:
- t.off = offset
+ b.off = offset
case io.SeekCurrent:
- t.off += offset
+ b.off += offset
case io.SeekEnd:
- t.off = t.Len() + offset
+ b.off = b.Len() + offset
default:
return 0, errors.New("invalid whence")
}
- c, _ := t.buf.ByteAt(t.off)
+ c, _ := b.buf.ByteAt(b.off)
for !utf8.RuneStart(c) {
- t.off--
- c, _ = t.buf.ByteAt(t.off)
+ b.off--
+ c, _ = b.buf.ByteAt(b.off)
}
- return t.off, nil
+ return b.off, nil
}
// SeekDot sets the dot to a single offset in the text buffer.
-func (t *Buffer) SeekDot(offset, whence int) (int, error) {
+func (b *Buffer) SeekDot(offset, whence int) (int, error) {
switch whence {
case io.SeekStart:
- q0, _, err := t.SetDot(offset, offset)
+ q0, _, err := b.SetDot(offset, offset)
return q0, err
case io.SeekCurrent:
- q0, _, err := t.SetDot(t.q0+offset, t.q0+offset)
+ q0, _, err := b.SetDot(b.q0+offset, b.q0+offset)
return q0, err
case io.SeekEnd:
- q0, _, err := t.SetDot(t.Len()+offset, t.Len()+offset)
+ q0, _, err := b.SetDot(b.Len()+offset, b.Len()+offset)
return q0, err
default:
return 0, errors.New("invalid whence")
@@ -371,59 +386,59 @@ func (t *Buffer) SeekDot(offset, whence int) (int, error) {
}
// SetDot sets both ends of the dot into an absolute position. It will check the given offsets and adjust them accordingly, so they are not out of bounds or on an invalid rune start. It returns the final offsets. Error is always nil.
-func (t *Buffer) SetDot(q0, q1 int) (int, int, error) {
- t.initBuffer()
+func (b *Buffer) SetDot(q0, q1 int) (int, int, error) {
+ b.initBuffer()
- t.q0, t.q1 = q0, q1
+ b.q0, b.q1 = q0, q1
// check out of bounds
- if t.q0 < 0 {
- t.q0 = 0
+ if b.q0 < 0 {
+ b.q0 = 0
}
- if t.q1 < 0 {
- t.q1 = 0
+ if b.q1 < 0 {
+ b.q1 = 0
}
- if t.q0 >= t.buf.Len() {
- t.q0 = t.buf.Len()
+ if b.q0 >= b.buf.Len() {
+ b.q0 = b.buf.Len()
}
- if t.q1 >= t.buf.Len() {
- t.q1 = t.buf.Len()
+ if b.q1 >= b.buf.Len() {
+ b.q1 = b.buf.Len()
}
// q0 must never be greater than q1
- if t.q0 > t.q1 {
- t.q0 = t.q1
+ if b.q0 > b.q1 {
+ b.q0 = b.q1
}
- if t.q1 < t.q0 {
- t.q1 = t.q0
+ if b.q1 < b.q0 {
+ b.q1 = b.q0
}
// set only to valid rune start
var c byte
- c, _ = t.buf.ByteAt(t.q0)
+ c, _ = b.buf.ByteAt(b.q0)
for !utf8.RuneStart(c) {
- t.q0--
- c, _ = t.buf.ByteAt(t.q0)
+ b.q0--
+ c, _ = b.buf.ByteAt(b.q0)
}
- c, _ = t.buf.ByteAt(t.q1)
+ c, _ = b.buf.ByteAt(b.q1)
for !utf8.RuneStart(c) {
- t.q1--
- c, _ = t.buf.ByteAt(t.q1)
+ b.q1--
+ c, _ = b.buf.ByteAt(b.q1)
}
- return t.q0, t.q1, nil
+ return b.q0, b.q1, nil
}
// ExpandDot expands the current selection in positive or negative offset. A positive offset expands forwards and a negative expands backwards. Q is 0 or 1, either the left or the right end of the dot.
-func (t *Buffer) ExpandDot(q, offset int) {
+func (b *Buffer) ExpandDot(q, offset int) {
if q < 0 || q > 1 {
return
}
if q == 0 {
- t.SetDot(t.q0+offset, t.q1)
+ b.SetDot(b.q0+offset, b.q1)
} else {
- t.SetDot(t.q0, t.q1+offset)
+ b.SetDot(b.q0, b.q1+offset)
}
}
@@ -434,8 +449,8 @@ func (t *Buffer) ExpandDot(q, offset int) {
// If on newline, select the whole line.
//
// Otherwise, select word (longest alphanumeric sequence).
-func (t *Buffer) Select(offset int) {
- offset, _ = t.Seek(offset, io.SeekStart)
+func (b *Buffer) Select(offset int) {
+ offset, _ = b.Seek(offset, io.SeekStart)
start, end := offset, offset
// space
@@ -443,30 +458,30 @@ func (t *Buffer) Select(offset int) {
//end += t.NextSpace(end)
// word
- start -= t.PrevWord(start)
- end += t.NextWord(end)
+ start -= b.PrevWord(start)
+ end += b.NextWord(end)
// return a single char selection if no word was found
if start == end {
- t.Seek(offset, io.SeekStart)
- _, size, _ := t.ReadRune()
+ b.Seek(offset, io.SeekStart)
+ _, size, _ := b.ReadRune()
end += size
}
// Set dot
- t.SetDot(start, end)
+ b.SetDot(start, end)
}
-func (t *Buffer) NextSpace(offset int) (n int) {
- offset, _ = t.Seek(offset, io.SeekStart)
+func (b *Buffer) NextSpace(offset int) (n int) {
+ offset, _ = b.Seek(offset, io.SeekStart)
- r, size, err := t.ReadRune()
+ r, size, err := b.ReadRune()
if err != nil {
return 0
}
for !unicode.IsSpace(r) {
n += size
- r, size, err = t.ReadRune()
+ r, size, err = b.ReadRune()
if err != nil {
if err == io.EOF {
return n
@@ -478,15 +493,15 @@ func (t *Buffer) NextSpace(offset int) (n int) {
return n
}
-func (t *Buffer) PrevSpace(offset int) (n int) {
- offset, _ = t.Seek(offset, io.SeekStart)
+func (b *Buffer) PrevSpace(offset int) (n int) {
+ offset, _ = b.Seek(offset, io.SeekStart)
- r, size, err := t.ReadRuneAt(offset)
+ r, size, err := b.ReadRuneAt(offset)
if err != nil {
return 0
}
for !unicode.IsSpace(r) {
- r, size, err = t.UnreadRune()
+ r, size, err = b.UnreadRune()
if err != nil {
if err == gapbuffer.ErrOutOfRange {
return n
@@ -502,16 +517,16 @@ func (t *Buffer) PrevSpace(offset int) (n int) {
return n
}
-func (t *Buffer) NextWord(offset int) (n int) {
- offset, _ = t.Seek(offset, io.SeekStart)
+func (b *Buffer) NextWord(offset int) (n int) {
+ offset, _ = b.Seek(offset, io.SeekStart)
- r, size, err := t.ReadRune()
+ r, size, err := b.ReadRune()
if err != nil {
return 0
}
for unicode.IsLetter(r) || unicode.IsDigit(r) {
n += size
- r, size, err = t.ReadRune()
+ r, size, err = b.ReadRune()
if err != nil {
if err == io.EOF {
return n
@@ -523,12 +538,12 @@ func (t *Buffer) NextWord(offset int) (n int) {
return n
}
-func (t *Buffer) PrevWord(offset int) (n int) {
- offset, _ = t.Seek(offset, io.SeekStart)
+func (b *Buffer) PrevWord(offset int) (n int) {
+ offset, _ = b.Seek(offset, io.SeekStart)
- r, size, _ := t.ReadRuneAt(offset)
+ r, size, _ := b.ReadRuneAt(offset)
for unicode.IsLetter(r) || unicode.IsDigit(r) {
- r, size, _ = t.UnreadRune()
+ r, size, _ = b.UnreadRune()
n += size
}
@@ -540,17 +555,17 @@ func (t *Buffer) PrevWord(offset int) (n int) {
}
// NextDelim returns number of bytes from given offset up until next delimiter.
-func (t *Buffer) NextDelim(delim rune, offset int) (n int) {
- t.Seek(offset, io.SeekStart)
+func (b *Buffer) NextDelim(delim rune, offset int) (n int) {
+ b.Seek(offset, io.SeekStart)
- r, size, err := t.ReadRune()
+ r, size, err := b.ReadRune()
if err != nil {
return 0
}
for r != delim {
n += size
- r, size, err = t.ReadRune()
+ r, size, err = b.ReadRune()
if err != nil {
if err == io.EOF {
return n
@@ -563,16 +578,16 @@ func (t *Buffer) NextDelim(delim rune, offset int) (n int) {
}
// PrevDelim returns number of bytes from given offset up until next delimiter.
-func (t *Buffer) PrevDelim(delim rune, offset int) (n int) {
- t.Seek(offset, io.SeekStart)
- r, size, err := t.UnreadRune()
+func (b *Buffer) PrevDelim(delim rune, offset int) (n int) {
+ b.Seek(offset, io.SeekStart)
+ r, size, err := b.UnreadRune()
if err != nil {
return 0
}
n += size
for r != delim {
- r, size, err = t.UnreadRune()
+ r, size, err = b.UnreadRune()
n += size
if err != nil {
if err == gapbuffer.ErrOutOfRange {
@@ -585,50 +600,50 @@ func (t *Buffer) PrevDelim(delim rune, offset int) (n int) {
return n
}
-func (t *Buffer) Undo() error {
- c, err := t.history.Undo()
+func (b *Buffer) Undo() error {
+ c, err := b.history.Undo()
if err != nil {
return errors.Wrap(err, "undo")
}
- t.commit(c)
+ b.commit(c)
// highlight text
- t.SetDot(c.offset, c.offset+len(c.content))
+ b.SetDot(c.offset, c.offset+len(c.content))
return nil
}
-func (t *Buffer) Redo() error {
- c, err := t.history.Redo()
+func (b *Buffer) Redo() error {
+ c, err := b.history.Redo()
if err != nil {
return errors.Wrap(err, "redo")
}
- t.commit(c)
+ b.commit(c)
if c.action == HDelete {
- t.SetDot(c.offset, c.offset)
+ b.SetDot(c.offset, c.offset)
} else {
- t.SetDot(c.offset+len(c.content), c.offset+len(c.content))
+ b.SetDot(c.offset+len(c.content), c.offset+len(c.content))
}
return nil
}
-func (t *Buffer) commit(c Change) (int, error) {
- t.initBuffer()
+func (b *Buffer) commit(c Change) (int, error) {
+ b.initBuffer()
switch c.action {
case HInsert:
- t.buf.Seek(c.offset) // sync gap buffer
- n, err := t.buf.Write([]byte(c.content))
+ b.buf.Seek(c.offset) // sync gap buffer
+ n, err := b.buf.Write([]byte(c.content))
if err != nil {
return 0, err
}
return n, err
case HDelete:
n := len(c.content)
- t.buf.Seek(c.offset + n) // sync gap buffer
+ b.buf.Seek(c.offset + n) // sync gap buffer
for i := n; i > 0; i-- {
- t.buf.Delete() // gap buffer deletes one byte at a time
+ b.buf.Delete() // gap buffer deletes one byte at a time
}
return n, nil
default:
diff --git a/editor/commands.go b/editor/commands.go
deleted file mode 100644
index e3a77b9..0000000
--- a/editor/commands.go
+++ /dev/null
@@ -1,100 +0,0 @@
-package editor
-
-import (
- "fmt"
- "strings"
-)
-
-type CommandFunc func(args string) string
-
-var poecmds map[string]CommandFunc
-
-func (e *editor) initCommands() {
- poecmds = map[string]CommandFunc{
- // "Exit": CmdExit,
- // "New": CmdNew,
- // "Del": CmdDel,
- "Edit": e.CmdEdit,
- // "Newcol": CmdNewcol,
- }
-}
-
-func (e *editor) Run(input string) {
- if input == "" {
- return
- }
-
- input = strings.Trim(input, "\t\n ")
-
- // check poe default commands
- cmd := strings.Split(string(input), " ")
- if fn, ok := poecmds[cmd[0]]; ok {
- fn(strings.TrimPrefix(input, cmd[0]))
- return
- }
-
- // Edit shortcuts for external commands and piping
- switch input[0] {
- case '!', '<', '>', '|':
- e.CmdEdit(input)
- }
-
- e.CmdEdit("!" + input)
-}
-
-//func CmdExit(args string) {
-// ok := true
-// for _, win := range AllWindows() {
-// if !win.CanClose() {
-// ok = false
-// }
-// }
-// if ok {
-// quit <- true
-// }
-//}
-//
-//func CmdNewcol(args string) {
-// screen.Clear()
-// screen.Sync()
-// workspace.AddCol()
-// CmdNew("")
-//}
-//
-//
-//
-
-func (e *editor) CmdEdit(args string) string {
- if len(args) < 2 {
- return ""
- }
-
- switch args[0] {
- case 'f':
- var names []string
- for _, buf := range e.buffers {
- names = append(names, fmt.Sprintf("%s", buf.Name()))
- }
- return fmt.Sprintf("buffers:\n%s\n", strings.Join(names, "\n"))
- // case '!':
- // os.Chdir(CurWin.Dir())
- // cmd := strings.Split(string(args[1:]), " ")
- // path, err := exec.LookPath(cmd[0])
- // if err != nil { // path not found, break with silence
- // //printMsg("path not found: %s\n", cmd[0])
- // break
- // }
- // out, err := exec.Command(path, cmd[1:]...).Output()
- // if err != nil {
- // printMsg("error: %s\n", err)
- // break
- // }
- // // if command produced output, print it
- // outstr := string(out)
- // if outstr != "" {
- // printMsg("%s", outstr)
- // }
- default:
- return "?"
- }
-}
diff --git a/editor/editor.go b/editor/editor.go
index 67f0ab3..87b3eff 100644
--- a/editor/editor.go
+++ b/editor/editor.go
@@ -1,7 +1,11 @@
package editor
import (
+ "fmt"
+ "os"
+ "os/exec"
"path/filepath"
+ "strings"
"time"
"github.com/prodhe/poe/gapbuffer"
@@ -12,49 +16,45 @@ type Editor interface {
NewBuffer() (id int64, buf *Buffer)
Buffer(id int64) *Buffer
Buffers() ([]int64, []*Buffer)
- Current() *Buffer
LoadBuffers(filenames []string)
CloseBuffer(id int64)
WorkDir() string
Len() int
- Run(cmd string)
+ Edit(bufid int64, cmd string) string
}
// New returns an empty editor with no buffers loaded.
func New() Editor {
- e := &editor{}
+ e := &ed{}
e.buffers = map[int64]*Buffer{}
e.workdir, _ = filepath.Abs(".")
- e.initCommands()
return e
}
-// editor implements Editor.
-type editor struct {
+// ed implements Editor.
+type ed struct {
buffers map[int64]*Buffer
- current int64 // id ref to current buffer
workdir string
}
// NewBuffer creates an empty buffer and appends it to the editor. Returns the new id and the new buffer.
-func (e *editor) NewBuffer() (id int64, buf *Buffer) {
+func (e *ed) NewBuffer() (id int64, buf *Buffer) {
buf = &Buffer{buf: &gapbuffer.Buffer{}}
id = e.genBufferID()
e.buffers[id] = buf
- e.current = id
return id, buf
}
// Buffer returns the buffer with given index. Nil if id not found.
-func (e *editor) Buffer(id int64) *Buffer {
+func (e *ed) Buffer(id int64) *Buffer {
if _, ok := e.buffers[id]; ok {
- e.current = id
+ return e.buffers[id]
}
- return e.buffers[id]
+ return nil
}
// Buffers returns a slice of IDs and a slice of buffers.
-func (e *editor) Buffers() ([]int64, []*Buffer) {
+func (e *ed) Buffers() ([]int64, []*Buffer) {
ids := make([]int64, 0, len(e.buffers))
bs := make([]*Buffer, 0, len(e.buffers))
for i, b := range e.buffers {
@@ -64,23 +64,18 @@ func (e *editor) Buffers() ([]int64, []*Buffer) {
return ids, bs
}
-// Current returns the current buffer.
-func (e *editor) Current() *Buffer {
- return e.buffers[e.current]
-}
-
// CloseBuffer deletes the given buffer from memory. No warnings. Here be dragons.
-func (e *editor) CloseBuffer(id int64) {
+func (e *ed) CloseBuffer(id int64) {
delete(e.buffers, id)
}
// Len returns number of buffers currently in the editor.
-func (e *editor) Len() int {
+func (e *ed) Len() int {
return len(e.buffers)
}
// WorkDir returns the base working directory of the editor.
-func (e *editor) WorkDir() string {
+func (e *ed) WorkDir() string {
if e.workdir == "" {
d, _ := filepath.Abs(".")
return d
@@ -89,7 +84,7 @@ func (e *editor) WorkDir() string {
}
// LoadBuffers reads files from disk and loads them into windows. Screen need to be initialized.
-func (e *editor) LoadBuffers(fns []string) {
+func (e *ed) LoadBuffers(fns []string) {
// load given filenames and append to buffer list
for _, fn := range fns {
_, buf := e.NewBuffer()
@@ -102,6 +97,44 @@ func (e *editor) LoadBuffers(fns []string) {
}
}
-func (e *editor) genBufferID() int64 {
+func (e *ed) genBufferID() int64 {
return time.Now().UnixNano()
}
+
+func (e *ed) Edit(bufid int64, args string) string {
+ if len(args) < 1 {
+ return ""
+ }
+
+ if _, ok := e.buffers[bufid]; !ok {
+ // no such bufid
+ return ""
+ }
+
+ switch args[0] {
+ case 'f':
+ var names []string
+ for _, buf := range e.buffers {
+ names = append(names, fmt.Sprintf("%s", buf.Name()))
+ }
+ return fmt.Sprintf("buffers:\n%s", strings.Join(names, "\n"))
+ case '!':
+ os.Chdir(e.buffers[bufid].WorkDir())
+ cmd := strings.Split(string(args[1:]), " ")
+ path, err := exec.LookPath(cmd[0])
+ if err != nil { // path not found or not executable
+ //return fmt.Sprintf("cannot execute: %s", cmd[0])
+ return ""
+ }
+ out, err := exec.Command(path, cmd[1:]...).Output()
+ if err != nil {
+ return fmt.Sprintf("error: %s", err)
+ break
+ }
+ outstr := string(out)
+ return outstr
+ }
+
+ // no match
+ return "?"
+}
diff --git a/editor/file.go b/editor/file.go
index e22c279..41657e1 100644
--- a/editor/file.go
+++ b/editor/file.go
@@ -4,8 +4,8 @@ import (
"time"
)
-// File holds information about a file on disk.
-type File struct {
+// file holds information about a file on disk.
+type file struct {
name string
read bool // true if file has been read
mtime time.Time // of file when last read/written
diff --git a/todo b/todo
index d0aef98..bd8faa8 100644
--- a/todo
+++ b/todo
@@ -24,7 +24,32 @@ visual
file stats with ^G
keep tagline updated with flags
show running processes in menu
- another style for top menu
do not change CurWin on mousepressed
scroll on mpressed at bottom line
unprintableChar styling
+
+
+
+---
+
+
+slice insertion
+
+func insert(original []int, position int, value int) []int {
+ l := len(original)
+ target := original
+ if cap(original) == l {
+ target = make([]int, l+1, l+10)
+ copy(target, original[:position])
+ } else {
+ target = append(target, -1)
+ }
+ copy(target[position+1:], original[position:])
+ target[position] = value
+ return target
+}
+
+
+s = append(s, 0)
+copy(s[i+1:], s[i:])
+s[i] = x \ No newline at end of file
diff --git a/ui/cli.go b/ui/cli.go
index a7cf9e4..a28bd03 100644
--- a/ui/cli.go
+++ b/ui/cli.go
@@ -47,7 +47,7 @@ outer:
if len(input) < 2 {
break
}
- c.ed.Current().Write([]byte(strings.Join(input[1:], " ")))
+ //c.ed.Current().Write([]byte(strings.Join(input[1:], " ")))
default:
fmt.Println("?")
}
diff --git a/ui/tcell/layout.go b/ui/tcell/layout.go
index 599fb6a..bc30f8c 100644
--- a/ui/tcell/layout.go
+++ b/ui/tcell/layout.go
@@ -80,7 +80,8 @@ func (wrk *Workspace) Draw() {
// draw vertical lines between cols
for x, y := col.x+col.w+1, wrk.y; y < wrk.y+wrk.h; y++ {
- screen.SetContent(x, y, '|', nil, vertlineStyle)
+ // 2502
+ screen.SetContent(x, y, '\u007c', nil, vertlineStyle)
}
}
}
@@ -101,11 +102,8 @@ func (c *Column) CloseWindow(w *Window) {
}
c.windows = c.windows[:j]
- // If we deleted the current window (probably), select another
if CurWin == w {
- all := AllWindows()
-
- // If we are out of windows in our own column, pick another or exit
+ // If we are not out of windows in our own column, pick another or exit
if len(c.windows) > 0 {
CurWin = c.windows[j-1]
} else {
@@ -115,18 +113,7 @@ func (c *Column) CloseWindow(w *Window) {
// clear clutter
screen.Clear()
- // find another window to focus or exit
- if len(all) > 0 {
- CurWin = AllWindows()[0]
-
- } else {
- ed.Run("Exit")
- }
- }
-
- // if the only win left is the message win, close all
- if len(all) == 1 && CurWin.Name() == FnMessageWin {
- ed.Run("Exit")
+ return
}
}
@@ -141,12 +128,7 @@ func (c *Column) Resize(x, y, w, h int) {
}
func (c *Column) ResizeWindows() {
- var n int
- for _, win := range c.windows {
- if !win.hidden {
- n++
- }
- }
+ n := len(c.windows)
var remainder int
if n > 0 {
@@ -163,8 +145,6 @@ func (c *Column) ResizeWindows() {
func (c *Column) Draw() {
for _, win := range c.windows {
- if !win.hidden {
- win.Draw()
- }
+ win.Draw()
}
}
diff --git a/ui/tcell/style.go b/ui/tcell/style.go
index 0dcb565..a179c1f 100644
--- a/ui/tcell/style.go
+++ b/ui/tcell/style.go
@@ -24,19 +24,24 @@ var (
// initStyles initializes the different styles (colors for background/foreground).
func initStyles() error {
- bodyStyle = tcell.StyleDefault.
- Background(tcell.NewHexColor(0xffffea)).
- Foreground(tcell.ColorBlack)
+ bodyStyle = tcell.StyleDefault
+
+ // bodyStyle = tcell.StyleDefault.
+ // Background(tcell.NewHexColor(0xffffea)).
+ // Foreground(tcell.ColorBlack)
+
bodyCursorStyle = bodyStyle.
Background(tcell.NewHexColor(0xeaea9e))
- bodyHilightStyle = bodyStyle.
- Background(tcell.NewHexColor(0xa6a65a))
+
+ bodyHilightStyle = bodyStyle.Reverse(true)
+ // bodyHilightStyle = bodyStyle.
+ // Background(tcell.NewHexColor(0xa6a65a))
unprintableStyle = bodyStyle.
Foreground(tcell.ColorRed)
- tagStyle = tcell.StyleDefault.
- Background(tcell.NewHexColor(0xeaffff)).
- Foreground(tcell.ColorBlack)
+ tagStyle = tcell.StyleDefault.Reverse(true)
+ //Background(tcell.NewHexColor(0xeaffff)).
+ //Foreground(tcell.ColorBlack)
tagCursorStyle = tagStyle.
Background(tcell.NewHexColor(0x8888cc)).
Foreground(tcell.ColorBlack)
@@ -47,7 +52,7 @@ func initStyles() error {
tagSquareModifiedStyle = tagStyle.
Background(tcell.NewHexColor(0x2222cc))
- vertlineStyle = bodyStyle
+ vertlineStyle = bodyStyle.Reverse(false)
return nil
}
diff --git a/ui/tcell/tcell.go b/ui/tcell/tcell.go
index ff20628..1ebc0cc 100644
--- a/ui/tcell/tcell.go
+++ b/ui/tcell/tcell.go
@@ -3,6 +3,7 @@ package uitcell
import (
"fmt"
"path/filepath"
+ "strings"
"github.com/gdamore/tcell"
"github.com/prodhe/poe/editor"
@@ -17,14 +18,17 @@ const (
var (
screen tcell.Screen
ed editor.Editor
- menu *View
workspace *Workspace
CurWin *Window
+ poecmds map[string]commandFunc
+
quit chan bool
events chan tcell.Event
)
+type commandFunc func()
+
type Tcell struct{}
func (t *Tcell) Init(e editor.Editor) error {
@@ -37,10 +41,6 @@ func (t *Tcell) Init(e editor.Editor) error {
return err
}
- if err := initMenu(); err != nil {
- return err
- }
-
if err := initWorkspace(); err != nil {
return err
}
@@ -49,6 +49,8 @@ func (t *Tcell) Init(e editor.Editor) error {
return err
}
+ initCommands()
+
quit = make(chan bool, 1)
events = make(chan tcell.Event, 100)
@@ -65,14 +67,10 @@ func (t *Tcell) Close() {
func printMsg(format string, a ...interface{}) {
// get output window
- var poewin *Window
- for _, win := range AllWindows() {
- poename := win.Dir() + string(filepath.Separator) + FnMessageWin
- poename = filepath.Clean(poename)
- if win.Name() == poename && CurWin.Dir() == win.Dir() {
- poewin = win
- }
- }
+ poename := CurWin.Dir() + string(filepath.Separator) + FnMessageWin
+ poename = filepath.Clean(poename)
+
+ poewin := FindWindow(poename)
if poewin == nil {
id, buf := ed.NewBuffer()
@@ -110,19 +108,6 @@ func initScreen() error {
return nil
}
-func initMenu() error {
- menu = &View{
- text: &editor.Buffer{},
- what: ViewMenu,
- style: bodyStyle,
- cursorStyle: bodyCursorStyle,
- hilightStyle: bodyHilightStyle,
- tabstop: 4,
- }
- fmt.Fprintf(menu, "Exit New Newcol")
- return nil
-}
-
func initWorkspace() error {
workspace = &Workspace{} // first resize event will set proper dimensions
workspace.AddCol()
@@ -134,12 +119,21 @@ func initWindows() error {
for _, id := range ids {
win := NewWindow(id)
workspace.LastCol().AddWindow(win)
+ CurWin = win
}
return nil
}
+func initCommands() {
+ poecmds = map[string]commandFunc{
+ "New": CmdNew,
+ "Newcol": CmdNewcol,
+ "Del": CmdDel,
+ "Exit": CmdExit,
+ }
+}
+
func (t *Tcell) redraw() {
- menu.Draw()
workspace.Draw()
screen.Show()
}
@@ -168,9 +162,7 @@ outer:
switch e := event.(type) {
case *tcell.EventResize:
w, h := screen.Size()
- menu.Resize(0, 0, w, 1)
- workspace.Resize(0, 1, w, h-1)
- //screen.Clear()
+ workspace.Resize(0, 0, w, h)
screen.Sync()
case *tcell.EventKey: // system wide shortcuts
switch e.Key() {
@@ -178,10 +170,6 @@ outer:
screen.Clear()
screen.Sync()
default: // let the focused view handle event
- if menu.focused {
- menu.HandleEvent(e)
- break
- }
if CurWin != nil {
CurWin.HandleEvent(e)
}
@@ -198,15 +186,8 @@ outer:
}
}
- // check if we are in the menu
- menu.focused = false
- if my < 1 {
- menu.focused = true
- menu.HandleEvent(e)
- break
- }
-
if CurWin != nil {
+ CurWin.Focus()
CurWin.HandleEvent(e)
}
}
@@ -220,6 +201,29 @@ outer:
}
}
+func Cmd(input string) string {
+ if input == "" {
+ return ""
+ }
+
+ input = strings.Trim(input, "\t\n ")
+
+ // check poe default commands
+ cmd := strings.Split(string(input), " ")
+ if fn, ok := poecmds[cmd[0]]; ok {
+ fn()
+ return ""
+ }
+
+ // Edit shortcuts for external commands and piping
+ switch input[0] {
+ case '!', '<', '>', '|':
+ return ed.Edit(CurWin.bufid, input)
+ }
+
+ return ed.Edit(CurWin.bufid, "!"+input)
+}
+
func CmdOpen(fn string) {
screen.Clear()
var win *Window
@@ -233,7 +237,7 @@ func CmdOpen(fn string) {
}
}
-func CmdNew(args string) {
+func CmdNew() {
screen.Clear()
id, _ := ed.NewBuffer()
win := NewWindow(id)
@@ -241,9 +245,18 @@ func CmdNew(args string) {
}
func CmdDel() {
+ if len(AllWindows()) == 1 {
+ CmdExit()
+ return
+ }
CurWin.Close()
}
+func CmdNewcol() {
+ workspace.AddCol()
+ CmdNew()
+}
+
func CmdExit() {
exit := true
wins := AllWindows()
@@ -252,7 +265,7 @@ func CmdExit() {
exit = false
}
}
- if exit || len(wins) == 0 {
+ if exit {
quit <- true
}
}
diff --git a/ui/tcell/view.go b/ui/tcell/view.go
index ef42207..7c0eb64 100644
--- a/ui/tcell/view.go
+++ b/ui/tcell/view.go
@@ -33,7 +33,6 @@ type View struct {
tabstop int
focused bool
what int
- dirty bool // modified since last read/write
mclicktime time.Time // last mouse click in time
mclickpos int // byte offset accounting for runes
mpressed bool
@@ -44,9 +43,6 @@ func (v *View) Write(p []byte) (int, error) {
if err != nil {
return 0, err
}
- if v.what == ViewBody {
- v.dirty = true
- }
v.SetCursor(0, 1) // force scroll if needed
return n, err
}
@@ -62,9 +58,6 @@ func (v *View) Delete() (int, error) {
if err != nil {
return n, err
}
- if v.what == ViewBody {
- v.dirty = true
- }
v.SetCursor(0, 1) // force scroll
return n, nil
}
@@ -79,11 +72,17 @@ func (v *View) Rune() rune {
return r
}
+// Cursor returns start of dot.
func (v *View) Cursor() int {
q0, _ := v.text.Dot()
return q0
}
+// Dirty returns true if the buffer has been written to.
+func (v *View) Dirty() bool {
+ return v.text.Dirty()
+}
+
func (v *View) SetCursor(pos, whence int) {
v.text.SeekDot(pos, whence)
@@ -235,11 +234,15 @@ func (v *View) ScrollTo(offset int) {
}
func (b *View) Draw() {
+ //screen.HideCursor()
+
x, y := b.x, b.y
if b.text.Len() > 0 {
b.opos = b.scrollpos // keep track of last visible char/overflow
b.text.Seek(b.scrollpos, io.SeekStart)
+ q0, q1 := b.text.Dot()
+
for i := b.scrollpos; i < b.text.Len(); { // i gets incremented after reading of the rune, to know how many bytes we need to skip
// line wrap
if x > b.x+b.w {
@@ -255,15 +258,18 @@ func (b *View) Draw() {
// default style
style := b.style
- // highlight cursor
- q0, q1 := b.text.Dot()
+ // highlight cursor if on screen
if (q0 == q1 && i == q0) && b.focused {
- style = b.cursorStyle
+ //style = b.cursorStyle
+ screen.ShowCursor(x, y)
}
- // highlight selection
- if (i >= q0 && i < q1) && b.focused {
+ // highlight selection, even if not focused
+ if i >= q0 && i < q1 {
style = b.hilightStyle
+ if b.focused {
+ screen.HideCursor()
+ }
}
// draw rune from buffer
@@ -334,10 +340,16 @@ func (b *View) Draw() {
}
if y < b.y+b.h {
screen.SetContent(x, y, ' ', nil, b.cursorStyle)
+ screen.ShowCursor(x, y)
x++
}
}
+ // if we are in focus, we are allowed to hide the central cursor if dot is currently off screen
+ if b.focused && (q0 < b.scrollpos || q0 > b.opos) {
+ screen.HideCursor()
+ }
+
// clear the rest and optionally show a special char as empty line
for w := b.x + b.w; w >= x; w-- {
screen.SetContent(w, y, ' ', nil, b.style)
@@ -385,7 +397,10 @@ func (v *View) HandleEvent(ev tcell.Event) {
// if we clicked inside a current selection, run that one
q0, q1 := v.text.Dot()
if pos >= q0 && pos <= q1 && q0 != q1 {
- ed.Run(v.text.ReadDot())
+ output := Cmd(v.text.ReadDot())
+ if output != "" {
+ printMsg(output)
+ }
return
}
@@ -393,9 +408,12 @@ func (v *View) HandleEvent(ev tcell.Event) {
p := pos - v.text.PrevSpace(pos)
n := pos + v.text.NextSpace(pos)
v.text.SetDot(p, n)
- fn := strings.Trim(v.text.ReadDot(), "\n\t ")
+ str := strings.Trim(v.text.ReadDot(), "\n\t ")
v.text.SetDot(q0, q1)
- ed.Run(fn)
+ output := Cmd(str)
+ if output != "" {
+ printMsg(output)
+ }
return
}
@@ -445,7 +463,7 @@ func (v *View) HandleEvent(ev tcell.Event) {
// if we clicked inside a current selection, run that one
q0, q1 := v.text.Dot()
if pos >= q0 && pos <= q1 && q0 != q1 {
- ed.Run(v.text.ReadDot())
+ Cmd(v.text.ReadDot())
return
}
@@ -455,7 +473,7 @@ func (v *View) HandleEvent(ev tcell.Event) {
v.text.SetDot(p, n)
fn := strings.Trim(v.text.ReadDot(), "\n\t ")
v.text.SetDot(q0, q1)
- ed.Run(fn)
+ Cmd(fn)
return
case tcell.Button3: // right click
pos := v.XYToOffset(mx, my)
@@ -562,10 +580,12 @@ func (v *View) HandleEvent(ev tcell.Event) {
v.Delete()
return
case tcell.KeyCtrlG: // file info/statistics
- printMsg("0x%.4x %q len %d\nbasedir: %s\nwindir: %s\nname: %s\n",
+ sw, sh := screen.Size()
+ printMsg("0x%.4x %q len %d\nbasedir: %s\nwindir: %s\nname: %s\nw: %d h: %d sw: %d sh: %d\n",
v.Rune(), v.Rune(),
v.text.Len(),
- ed.WorkDir(), CurWin.Dir(), CurWin.Name())
+ ed.WorkDir(), CurWin.Dir(), CurWin.Name(),
+ CurWin.w, CurWin.h, sh, sw)
return
case tcell.KeyCtrlO: // open file/dir
fn := v.text.ReadDot()
@@ -586,6 +606,9 @@ func (v *View) HandleEvent(ev tcell.Event) {
}
CmdOpen(fn)
return
+ case tcell.KeyCtrlN: // new column
+ CmdNewcol()
+ return
case tcell.KeyCtrlR: // run command in dot
cmd := v.text.ReadDot()
if cmd == "" { // select all non-space characters
@@ -599,7 +622,8 @@ func (v *View) HandleEvent(ev tcell.Event) {
return
}
}
- ed.Run(cmd)
+ res := Cmd(cmd)
+ printMsg("%s\n", res)
return
case tcell.KeyCtrlC: // copy to clipboard
str := v.text.ReadDot()
@@ -618,13 +642,7 @@ func (v *View) HandleEvent(ev tcell.Event) {
}
v.text.Write([]byte(s))
return
- case tcell.KeyCtrlQ:
- // close entire application if we are in the top menu
- if v.what == ViewMenu {
- CmdExit()
- return
- }
- // otherwise, just close this window (CurWin)
+ case tcell.KeyCtrlQ: // close window
CmdDel()
return
default:
diff --git a/ui/tcell/window.go b/ui/tcell/window.go
index fcb146c..b3774d2 100644
--- a/ui/tcell/window.go
+++ b/ui/tcell/window.go
@@ -14,9 +14,8 @@ type Window struct {
body *View
tagline *View
col *Column // reference to the column where in
- hidden bool
- collapsed bool // not implemented
- qcnt int // quit count
+ collapsed bool // not implemented
+ qcnt int // quit count
}
// NewWindow returns a fresh window associated with the given filename. For special case filenames, look at the package constants.
@@ -75,8 +74,13 @@ func (win *Window) Resize(x, y, w, h int) {
win.body.Resize(win.x, win.y+1, win.w, win.h-1)
}
-func (win *Window) UnFocus() {
+func (win *Window) Focus() {
win.body.focused = true
+ //win.tagline.focused = true
+}
+
+func (win *Window) UnFocus() {
+ win.body.focused = false
win.tagline.focused = false
}
@@ -85,7 +89,22 @@ func (win *Window) Name() string {
}
func (win *Window) Dir() string {
- return win.body.text.WorkDir()
+ d := win.body.text.WorkDir()
+ if d == "." {
+ return ed.WorkDir() // base path for program
+ }
+ return d
+}
+
+func (win *Window) Flags() [2]rune {
+ flags := [2]rune{' ', '-'}
+ if win.body.Dirty() {
+ flags[0] = '\''
+ }
+ if win.collapsed {
+ flags[1] = '+'
+ }
+ return flags
}
func (win *Window) HandleEvent(ev tcell.Event) {
@@ -119,13 +138,9 @@ func (win *Window) HandleEvent(ev tcell.Event) {
}
func (win *Window) Draw() {
- // Draw tag square
- boxstyle := tagSquareStyle
- if win.body.dirty {
- boxstyle = tagSquareModifiedStyle
- }
- screen.SetContent(win.x, win.y, ' ', nil, boxstyle)
- screen.SetContent(win.x+1, win.y, ' ', nil, boxstyle)
+ flags := win.Flags()
+ screen.SetContent(win.x, win.y, flags[0], nil, win.tagline.style)
+ screen.SetContent(win.x+1, win.y, flags[1], nil, win.tagline.style)
screen.SetContent(win.x+2, win.y, ' ', nil, win.tagline.style)
// Tagline
@@ -136,11 +151,14 @@ func (win *Window) Draw() {
}
func (win *Window) CanClose() bool {
- ok := !win.body.dirty || win.qcnt > 0
+ if win.body.what == ViewScratch {
+ return true
+ }
+ ok := (!win.body.Dirty() || win.qcnt > 0)
if !ok {
name := win.Name()
if name == FnEmptyWin {
- name = "unnamed file"
+ name = "unnamed buffer"
}
printMsg("%s modified\n", name)
win.qcnt++
diff --git a/ui/ui.go b/ui/ui.go
index 6dc843a..9f742bf 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -9,14 +9,7 @@ const (
SignalQuit int = iota
)
-type Messager interface {
- // Message prints output from the editor, using the Printf signature.
- Message(format string, a ...interface{})
-}
-
type Interface interface {
- //Messager
-
// Init initializes the user interface.
Init(ed editor.Editor) error