From ba555e4f1ea894b0bacc5a89234a2c78188ac363 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Mon, 14 Jan 2019 16:52:25 -0500 Subject: [PATCH] Add help --- cmd/micro/action/actions.go | 1 + cmd/micro/action/command.go | 31 ++++++++++++++++++ cmd/micro/buffer/buffer.go | 55 ++++++++++++++++++++------------ cmd/micro/buffer/eventhandler.go | 22 +++++++------ cmd/micro/buffer/line_array.go | 10 ++---- cmd/micro/buffer/save.go | 8 ++--- cmd/micro/buffer/serialize.go | 6 ++-- cmd/micro/buffer/settings.go | 2 +- 8 files changed, 90 insertions(+), 45 deletions(-) diff --git a/cmd/micro/action/actions.go b/cmd/micro/action/actions.go index 7d0814ec..e7278f8b 100644 --- a/cmd/micro/action/actions.go +++ b/cmd/micro/action/actions.go @@ -967,6 +967,7 @@ func (h *BufHandler) ClearStatus() bool { // ToggleHelp toggles the help screen func (h *BufHandler) ToggleHelp() bool { + h.openHelp("help") return true } diff --git a/cmd/micro/action/command.go b/cmd/micro/action/command.go index 19405fb5..58170f72 100644 --- a/cmd/micro/action/command.go +++ b/cmd/micro/action/command.go @@ -1,6 +1,8 @@ package action import ( + "errors" + "fmt" "os" "path/filepath" "strconv" @@ -274,8 +276,37 @@ func (h *BufHandler) ToggleLogCmd(args []string) { func (h *BufHandler) ReloadCmd(args []string) { } +func (h *BufHandler) openHelp(page string) error { + if data, err := config.FindRuntimeFile(config.RTHelp, page).Data(); err != nil { + return errors.New(fmt.Sprint("Unable to load help text", page, "\n", err)) + } else { + helpBuffer := buffer.NewBufferFromString(string(data), page+".md", buffer.BTHelp) + helpBuffer.SetName("Help " + page) + + if h.Buf.Type == buffer.BTHelp { + h.OpenBuffer(helpBuffer) + } else { + h.HSplitBuf(helpBuffer) + } + } + return nil +} + // HelpCmd tries to open the given help page in a horizontal split func (h *BufHandler) HelpCmd(args []string) { + if len(args) < 1 { + // Open the default help if the user just typed "> help" + h.openHelp("help") + } else { + if config.FindRuntimeFile(config.RTHelp, args[0]) != nil { + err := h.openHelp(args[0]) + if err != nil { + InfoBar.Error(err) + } + } else { + InfoBar.Error("Sorry, no help for ", args[0]) + } + } } // VSplitCmd opens a vertical split with file given in the first argument diff --git a/cmd/micro/buffer/buffer.go b/cmd/micro/buffer/buffer.go index f5c83ea0..7189b298 100644 --- a/cmd/micro/buffer/buffer.go +++ b/cmd/micro/buffer/buffer.go @@ -5,6 +5,7 @@ import ( "errors" "io" "io/ioutil" + "log" "os" "path/filepath" "strings" @@ -38,6 +39,25 @@ var ( ErrFileTooLarge = errors.New("File is too large to hash") ) +type SharedBuffer struct { + *LineArray + // Stores the last modification time of the file the buffer is pointing to + ModTime time.Time + // Type of the buffer (e.g. help, raw, scratch etc..) + Type BufType + + isModified bool +} + +func (b *SharedBuffer) insert(pos Loc, value []byte) { + b.isModified = true + b.LineArray.insert(pos, value) +} +func (b *SharedBuffer) remove(start, end Loc) []byte { + b.isModified = true + return b.LineArray.remove(start, end) +} + // Buffer stores the main information about a currently open file including // the actual text (in a LineArray), the undo/redo stack (in an EventHandler) // all the cursors, the syntax highlighting info, the settings for the buffer @@ -46,8 +66,8 @@ var ( // highlighter attaches information to each line of the buffer for optimization // purposes so it doesn't have to rehighlight everything on every update. type Buffer struct { - *LineArray *EventHandler + *SharedBuffer cursors []*Cursor curCursor int @@ -60,9 +80,6 @@ type Buffer struct { // Name of the buffer on the status line name string - // Stores the last modification time of the file the buffer is pointing to - ModTime *time.Time - SyntaxDef *highlight.Def Highlighter *highlight.Highlighter @@ -72,9 +89,6 @@ type Buffer struct { // Settings customized by the user Settings map[string]interface{} - // Type of the buffer (e.g. help, raw, scratch etc..) - Type BufType - Messages []*Message } @@ -122,7 +136,6 @@ func NewBuffer(reader io.Reader, size int64, path string, cursorPosition []strin absPath, _ := filepath.Abs(path) b := new(Buffer) - b.Type = btype b.Settings = config.DefaultLocalSettings() for k, v := range config.GlobalSettings { @@ -137,28 +150,24 @@ func NewBuffer(reader io.Reader, size int64, path string, cursorPosition []strin for _, buf := range OpenBuffers { if buf.AbsPath == absPath { found = true - b.LineArray = buf.LineArray + b.SharedBuffer = buf.SharedBuffer b.EventHandler = buf.EventHandler - b.ModTime = buf.ModTime - b.isModified = buf.isModified } } } if !found { + b.SharedBuffer = new(SharedBuffer) + b.Type = btype b.LineArray = NewLineArray(uint64(size), FFAuto, reader) - b.EventHandler = NewEventHandler(b.LineArray, b.cursors) - b.ModTime = new(time.Time) - b.isModified = new(bool) - *b.isModified = false - *b.ModTime = time.Time{} + b.EventHandler = NewEventHandler(b.SharedBuffer, b.cursors) } b.Path = path b.AbsPath = absPath // The last time this file was modified - *b.ModTime, _ = GetModTime(b.Path) + b.ModTime, _ = GetModTime(b.Path) b.UpdateRules() @@ -219,6 +228,11 @@ func (b *Buffer) GetName() string { return b.name } +//SetName changes the name for this buffer +func (b *Buffer) SetName(s string) { + b.name = s +} + // FileType returns the buffer's filetype func (b *Buffer) FileType() string { return b.Settings["filetype"].(string) @@ -234,8 +248,8 @@ func (b *Buffer) ReOpen() error { } b.EventHandler.ApplyDiff(txt) - *b.ModTime, err = GetModTime(b.Path) - *b.isModified = false + b.ModTime, err = GetModTime(b.Path) + b.isModified = false for _, c := range b.cursors { c.Relocate() } @@ -264,7 +278,7 @@ func (b *Buffer) RuneAt(loc Loc) rune { // being opened func (b *Buffer) Modified() bool { if b.Settings["fastdirty"].(bool) { - return *b.isModified + return b.isModified } var buff [md5.Size]byte @@ -402,6 +416,7 @@ func (b *Buffer) SetCursors(c []*Cursor) { func (b *Buffer) AddCursor(c *Cursor) { b.cursors = append(b.cursors, c) b.EventHandler.cursors = b.cursors + log.Println(b.cursors) b.UpdateCursors() } diff --git a/cmd/micro/buffer/eventhandler.go b/cmd/micro/buffer/eventhandler.go index 8d88f182..ac6294a1 100644 --- a/cmd/micro/buffer/eventhandler.go +++ b/cmd/micro/buffer/eventhandler.go @@ -1,6 +1,7 @@ package buffer import ( + "log" "time" "unicode/utf8" @@ -37,7 +38,7 @@ type Delta struct { } // ExecuteTextEvent runs a text event -func ExecuteTextEvent(t *TextEvent, buf *LineArray) { +func ExecuteTextEvent(t *TextEvent, buf *SharedBuffer) { if t.EventType == TextEventInsert { for _, d := range t.Deltas { buf.insert(d.Start, d.Text) @@ -60,25 +61,25 @@ func ExecuteTextEvent(t *TextEvent, buf *LineArray) { } // UndoTextEvent undoes a text event -func UndoTextEvent(t *TextEvent, buf *LineArray) { +func UndoTextEvent(t *TextEvent, buf *SharedBuffer) { t.EventType = -t.EventType ExecuteTextEvent(t, buf) } // EventHandler executes text manipulations and allows undoing and redoing type EventHandler struct { - buf *LineArray + buf *SharedBuffer cursors []*Cursor UndoStack *TEStack RedoStack *TEStack } // NewEventHandler returns a new EventHandler -func NewEventHandler(la *LineArray, cursors []*Cursor) *EventHandler { +func NewEventHandler(buf *SharedBuffer, cursors []*Cursor) *EventHandler { eh := new(EventHandler) eh.UndoStack = new(TEStack) eh.RedoStack = new(TEStack) - eh.buf = la + eh.buf = buf eh.cursors = cursors return eh } @@ -93,12 +94,12 @@ func (eh *EventHandler) ApplyDiff(new string) { loc := eh.buf.Start() for _, d := range diff { if d.Type == dmp.DiffDelete { - eh.Remove(loc, loc.MoveLA(utf8.RuneCountInString(d.Text), eh.buf)) + eh.Remove(loc, loc.MoveLA(utf8.RuneCountInString(d.Text), eh.buf.LineArray)) } else { if d.Type == dmp.DiffInsert { eh.Insert(loc, d.Text) } - loc = loc.MoveLA(utf8.RuneCountInString(d.Text), eh.buf) + loc = loc.MoveLA(utf8.RuneCountInString(d.Text), eh.buf.LineArray) } } } @@ -113,15 +114,16 @@ func (eh *EventHandler) Insert(start Loc, textStr string) { Time: time.Now(), } eh.Execute(e) - e.Deltas[0].End = start.MoveLA(utf8.RuneCount(text), eh.buf) + e.Deltas[0].End = start.MoveLA(utf8.RuneCount(text), eh.buf.LineArray) end := e.Deltas[0].End + log.Println(eh.cursors) for _, c := range eh.cursors { move := func(loc Loc) Loc { if start.Y != end.Y && loc.GreaterThan(start) { loc.Y += end.Y - start.Y } else if loc.Y == start.Y && loc.GreaterEqual(start) { - loc = loc.MoveLA(utf8.RuneCount(text), eh.buf) + loc = loc.MoveLA(utf8.RuneCount(text), eh.buf.LineArray) } return loc } @@ -149,7 +151,7 @@ func (eh *EventHandler) Remove(start, end Loc) { if start.Y != end.Y && loc.GreaterThan(end) { loc.Y -= end.Y - start.Y } else if loc.Y == end.Y && loc.GreaterEqual(end) { - loc = loc.MoveLA(-DiffLA(start, end, eh.buf), eh.buf) + loc = loc.MoveLA(-DiffLA(start, end, eh.buf.LineArray), eh.buf.LineArray) } return loc } diff --git a/cmd/micro/buffer/line_array.go b/cmd/micro/buffer/line_array.go index e7e86896..43d86100 100644 --- a/cmd/micro/buffer/line_array.go +++ b/cmd/micro/buffer/line_array.go @@ -52,10 +52,9 @@ type FileFormat byte // A LineArray simply stores and array of lines and makes it easy to insert // and delete in it type LineArray struct { - lines []Line - endings FileFormat - initsize uint64 - isModified *bool + lines []Line + endings FileFormat + initsize uint64 } // Append efficiently appends lines together @@ -162,7 +161,6 @@ func (la *LineArray) newlineBelow(y int) { // Inserts a byte array at a given location func (la *LineArray) insert(pos Loc, value []byte) { - *la.isModified = true x, y := runeToByteIndex(pos.X, la.lines[pos.Y].data), pos.Y for i := 0; i < len(value); i++ { if value[i] == '\n' { @@ -203,7 +201,6 @@ func (la *LineArray) split(pos Loc) { // removes from start to end func (la *LineArray) remove(start, end Loc) []byte { - *la.isModified = true sub := la.Substr(start, end) startX := runeToByteIndex(start.X, la.lines[start.Y].data) endX := runeToByteIndex(end.X, la.lines[end.Y].data) @@ -222,7 +219,6 @@ func (la *LineArray) remove(start, end Loc) []byte { // deleteToEnd deletes from the end of a line to the position func (la *LineArray) deleteToEnd(pos Loc) { - *la.isModified = true la.lines[pos.Y].data = la.lines[pos.Y].data[:pos.X] } diff --git a/cmd/micro/buffer/save.go b/cmd/micro/buffer/save.go index 44872151..fe82a8e0 100644 --- a/cmd/micro/buffer/save.go +++ b/cmd/micro/buffer/save.go @@ -73,7 +73,7 @@ func (b *Buffer) SaveAs(filename string) error { // Update the last time this file was updated after saving defer func() { - *b.ModTime, _ = GetModTime(filename) + b.ModTime, _ = GetModTime(filename) }() // Removes any tilde and replaces with the absolute path to home @@ -146,7 +146,7 @@ func (b *Buffer) SaveAs(filename string) error { b.Path = filename absPath, _ := filepath.Abs(filename) b.AbsPath = absPath - *b.isModified = false + b.isModified = false return b.Serialize() } @@ -182,8 +182,8 @@ func (b *Buffer) SaveAsWithSudo(filename string) error { err := cmd.Wait() if err == nil { - *b.isModified = false - *b.ModTime, _ = GetModTime(filename) + b.isModified = false + b.ModTime, _ = GetModTime(filename) return b.Serialize() } return err diff --git a/cmd/micro/buffer/serialize.go b/cmd/micro/buffer/serialize.go index 6edd24b5..ccafd1a6 100644 --- a/cmd/micro/buffer/serialize.go +++ b/cmd/micro/buffer/serialize.go @@ -36,7 +36,7 @@ func (b *Buffer) Serialize() error { err := gob.NewEncoder(file).Encode(SerializedBuffer{ b.EventHandler, b.GetActiveCursor().Loc, - *b.ModTime, + b.ModTime, }) return err }) @@ -61,10 +61,10 @@ func (b *Buffer) Unserialize() error { if b.Settings["saveundo"].(bool) { // We should only use last time's eventhandler if the file wasn't modified by someone else in the meantime - if *b.ModTime == buffer.ModTime { + if b.ModTime == buffer.ModTime { b.EventHandler = buffer.EventHandler b.EventHandler.cursors = b.cursors - b.EventHandler.buf = b.LineArray + b.EventHandler.buf = b.SharedBuffer } } } diff --git a/cmd/micro/buffer/settings.go b/cmd/micro/buffer/settings.go index 2894a5b5..fb3b5486 100644 --- a/cmd/micro/buffer/settings.go +++ b/cmd/micro/buffer/settings.go @@ -30,7 +30,7 @@ func (b *Buffer) SetOption(option, value string) error { } else if option == "filetype" { b.UpdateRules() } else if option == "fileformat" { - *b.isModified = true + b.isModified = true } else if option == "syntax" { if !nativeValue.(bool) { b.ClearMatches()