From d55dabaa3b20717d59aa2c40b9673c24326c2fc7 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Fri, 18 Mar 2016 20:40:00 -0400 Subject: [PATCH] Add comment fix go style --- buffer.go | 36 +++++++++---- cursor.go | 137 +++++++++++++++++++++++++++++++------------------- micro.go | 14 +++--- statusline.go | 9 ++-- util.go | 9 ++-- view.go | 88 +++++++++++++++++--------------- 6 files changed, 176 insertions(+), 117 deletions(-) diff --git a/buffer.go b/buffer.go index 9fdc33fc..770b2660 100644 --- a/buffer.go +++ b/buffer.go @@ -5,7 +5,11 @@ import ( "strings" ) +// Buffer stores the text for files that are loaded into the text editor +// It uses a rope to efficiently store the string and contains some +// simple functions for saving and wrapper functions for modifying the rope type Buffer struct { + // Stores the text of the buffer r *Rope // Path to the file on disk @@ -16,47 +20,57 @@ type Buffer struct { // This is the text stored everytime the buffer is saved to check if the buffer is modified savedText string + // Provide efficient and easy access to text and lines so the rope toString does not + // need to be constantly recalculated + // These variables are updated in the update() function text string lines []string } -func newBuffer(txt, path string) *Buffer { +// NewBuffer creates a new buffer from `txt` with path and name `path` +func NewBuffer(txt, path string) *Buffer { b := new(Buffer) b.r = newRope(txt) b.path = path b.name = path b.savedText = txt - b.update() + b.Update() return b } -func (b *Buffer) update() { +// Update fetches the string from the rope and updates the `text` and `lines` in the buffer +func (b *Buffer) Update() { b.text = b.r.toString() b.lines = strings.Split(b.text, "\n") } -func (b *Buffer) save() error { - return b.saveAs(b.path) +// Save saves the buffer to its default path +func (b *Buffer) Save() error { + return b.SaveAs(b.path) } -func (b *Buffer) saveAs(filename string) error { +// SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist +func (b *Buffer) SaveAs(filename string) error { b.savedText = b.text err := ioutil.WriteFile(filename, []byte(b.text), 0644) return err } -func (b *Buffer) insert(idx int, value string) { +// Insert a string into the rope +func (b *Buffer) Insert(idx int, value string) { b.r.insert(idx, value) - b.update() + b.Update() } -func (b *Buffer) remove(start, end int) { +// Remove a slice of the rope from start to end (exclusive) +func (b *Buffer) Remove(start, end int) { b.r.remove(start, end) - b.update() + b.Update() } -func (b *Buffer) length() int { +// Len gives the length of the buffer +func (b *Buffer) Len() int { return b.r.len } diff --git a/cursor.go b/cursor.go index 48c8bbe5..93785a5c 100644 --- a/cursor.go +++ b/cursor.go @@ -1,128 +1,158 @@ package main import ( - // "github.com/gdamore/tcell" "strings" ) -// Cursor stores the location of the cursor in the view +// The Cursor struct stores the location of the cursor in the view type Cursor struct { v *View + // We need three variables here because we insert text at loc but + // display the cursor at x, y x int y int loc int - selectionStart int + // Start of the selection in charNum + selectionStart int + + // We store the x, y of the start because when the user deletes the selection + // the cursor needs to go back to the start, and this is the simplest way selectionStartX int selectionStartY int - selectionEnd int + + // End of the selection in charNum + // We don't need to store the x, y here because when if the user is selecting backwards + // and they delete the selection, the cursor is already in the right place + selectionEnd int } -func (c *Cursor) resetSelection() { +// ResetSelection resets the user's selection +func (c *Cursor) ResetSelection() { c.selectionStart = 0 c.selectionEnd = 0 } -func (c *Cursor) hasSelection() bool { +// HasSelection returns whether or not the user has selected anything +func (c *Cursor) HasSelection() bool { return c.selectionEnd != c.selectionStart } -func (c *Cursor) deleteSelected() { +// DeleteSelected deletes the currently selected text +func (c *Cursor) DeleteSelected() { if c.selectionStart > c.selectionEnd { - c.v.buf.remove(c.selectionEnd, c.selectionStart+1) + c.v.buf.Remove(c.selectionEnd, c.selectionStart+1) // Since the cursor is already at the selection start we don't need to move } else { - c.v.buf.remove(c.selectionStart, c.selectionEnd+1) + c.v.buf.Remove(c.selectionStart, c.selectionEnd+1) c.loc -= c.selectionEnd - c.selectionStart c.x = c.selectionStartX c.y = c.selectionStartY } } -func (c *Cursor) runeUnder() rune { +// RuneUnder returns the rune under the cursor +func (c *Cursor) RuneUnder() rune { line := c.v.buf.lines[c.y] - if c.x >= count(line) { + if c.x >= Count(line) { return ' ' } return []rune(line)[c.x] } -func (c *Cursor) up() { +// Up moves the cursor up one line (if possible) +func (c *Cursor) Up() { if c.y > 0 { - c.loc -= count(c.v.buf.lines[c.y][:c.x]) + c.loc -= Count(c.v.buf.lines[c.y][:c.x]) // Count the newline c.loc-- c.y-- - if c.x > count(c.v.buf.lines[c.y]) { - c.x = count(c.v.buf.lines[c.y]) + if c.x > Count(c.v.buf.lines[c.y]) { + c.x = Count(c.v.buf.lines[c.y]) } - c.loc -= count(c.v.buf.lines[c.y][c.x:]) + c.loc -= Count(c.v.buf.lines[c.y][c.x:]) } } -func (c *Cursor) down() { + +// Down moves the cursor down one line (if possible) +func (c *Cursor) Down() { if c.y < len(c.v.buf.lines)-1 { - c.loc += count(c.v.buf.lines[c.y][c.x:]) + c.loc += Count(c.v.buf.lines[c.y][c.x:]) // Count the newline c.loc++ c.y++ - if c.x > count(c.v.buf.lines[c.y]) { - c.x = count(c.v.buf.lines[c.y]) + if c.x > Count(c.v.buf.lines[c.y]) { + c.x = Count(c.v.buf.lines[c.y]) } - c.loc += count(c.v.buf.lines[c.y][:c.x]) + c.loc += Count(c.v.buf.lines[c.y][:c.x]) } } -func (c *Cursor) left() { + +// Left moves the cursor left one cell (if possible) or to the last line if it is at the beginning +func (c *Cursor) Left() { + if c.loc == 0 { + return + } if c.x > 0 { c.loc-- c.x-- } else { - c.up() - c.end() + c.Up() + c.End() } } -func (c *Cursor) right() { - if c.x < count(c.v.buf.lines[c.y]) { + +// Right moves the cursor right one cell (if possible) or to the next line if it is at the end +func (c *Cursor) Right() { + if c.loc == c.v.buf.Len() { + return + } + if c.x < Count(c.v.buf.lines[c.y]) { c.loc++ c.x++ } else { - c.down() - c.start() + c.Down() + c.Start() } } -func (c *Cursor) end() { - c.loc += count(c.v.buf.lines[c.y][c.x:]) - c.x = count(c.v.buf.lines[c.y]) +// End moves the cursor to the end of the line it is on +func (c *Cursor) End() { + c.loc += Count(c.v.buf.lines[c.y][c.x:]) + c.x = Count(c.v.buf.lines[c.y]) } -func (c *Cursor) start() { - c.loc -= count(c.v.buf.lines[c.y][:c.x]) +// Start moves the cursor to the start of the line it is on +func (c *Cursor) Start() { + c.loc -= Count(c.v.buf.lines[c.y][:c.x]) c.x = 0 } -func (c *Cursor) getCharPosInLine(lineNum, visualPos int) int { - visualLine := strings.Replace(c.v.buf.lines[lineNum], "\t", "\t"+emptyString(tabSize-1), -1) - if visualPos > count(visualLine) { - visualPos = count(visualLine) +// GetCharPosInLine gets the char position of a visual x y coordinate (this is necessary because tabs are 1 char but 4 visual spaces) +func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int { + visualLine := strings.Replace(c.v.buf.lines[lineNum], "\t", "\t"+EmptyString(tabSize-1), -1) + if visualPos > Count(visualLine) { + visualPos = Count(visualLine) } - numTabs := numOccurences(visualLine[:visualPos], '\t') + numTabs := NumOccurences(visualLine[:visualPos], '\t') if visualPos >= (tabSize-1)*numTabs { return visualPos - (tabSize-1)*numTabs - } else { - return visualPos / tabSize } + return visualPos / tabSize } -func (c *Cursor) getVisualX() int { - return c.x + numOccurences(c.v.buf.lines[c.y][:c.x], '\t')*(tabSize-1) +// GetVisualX returns the x value of the cursor in visual spaces +func (c *Cursor) GetVisualX() int { + return c.x + NumOccurences(c.v.buf.lines[c.y][:c.x], '\t')*(tabSize-1) } -func (c *Cursor) distance(x, y int) int { +// Distance returns the distance between the cursor and x, y in runes +func (c *Cursor) Distance(x, y int) int { // Same line if y == c.y { return x - c.x @@ -130,45 +160,46 @@ func (c *Cursor) distance(x, y int) int { var distance int if y > c.y { - distance += count(c.v.buf.lines[c.y][c.x:]) + distance += Count(c.v.buf.lines[c.y][c.x:]) // Newline distance++ i := 1 for y != c.y+i { - distance += count(c.v.buf.lines[c.y+i]) + distance += Count(c.v.buf.lines[c.y+i]) // Newline distance++ i++ } - if x < count(c.v.buf.lines[y]) { - distance += count(c.v.buf.lines[y][:x]) + if x < Count(c.v.buf.lines[y]) { + distance += Count(c.v.buf.lines[y][:x]) } else { - distance += count(c.v.buf.lines[y]) + distance += Count(c.v.buf.lines[y]) } return distance } - distance -= count(c.v.buf.lines[c.y][:c.x]) + distance -= Count(c.v.buf.lines[c.y][:c.x]) // Newline distance-- i := 1 for y != c.y-i { - distance -= count(c.v.buf.lines[c.y-i]) + distance -= Count(c.v.buf.lines[c.y-i]) // Newline distance-- i++ } if x >= 0 { - distance -= count(c.v.buf.lines[y][x:]) + distance -= Count(c.v.buf.lines[y][x:]) } return distance } -func (c *Cursor) display() { +// Display draws the cursor to the screen at the correct position +func (c *Cursor) Display() { if c.y-c.v.topline < 0 || c.y-c.v.topline > c.v.height-1 { c.v.s.HideCursor() } else { - voffset := numOccurences(c.v.buf.lines[c.y][:c.x], '\t') * (tabSize - 1) + voffset := NumOccurences(c.v.buf.lines[c.y][:c.x], '\t') * (tabSize - 1) c.v.s.ShowCursor(c.x+voffset, c.y-c.v.topline) // cursorStyle := tcell.StyleDefault.Reverse(true) // c.v.s.SetContent(c.x+voffset, c.y-c.v.topline, c.runeUnder(), nil, cursorStyle) diff --git a/micro.go b/micro.go index b5a55217..f8958f76 100644 --- a/micro.go +++ b/micro.go @@ -54,20 +54,20 @@ func main() { s.SetStyle(defStyle) s.EnableMouse() - v := newView(newBuffer(string(input), filename), s) + v := NewView(NewBuffer(string(input), filename), s) // Initially everything needs to be drawn redraw := 2 for { if redraw == 2 { s.Clear() - v.display() - v.cursor.display() - v.sl.display() + v.Display() + v.cursor.Display() + v.sl.Display() s.Show() } else if redraw == 1 { - v.cursor.display() - v.sl.display() + v.cursor.Display() + v.sl.Display() s.Show() } @@ -81,6 +81,6 @@ func main() { } } - redraw = v.handleEvent(event) + redraw = v.HandleEvent(event) } } diff --git a/statusline.go b/statusline.go index a7529d6d..8ca1e1d3 100644 --- a/statusline.go +++ b/statusline.go @@ -5,11 +5,14 @@ import ( "strconv" ) +// Statusline represents the blue line at the bottom of the +// editor that gives information about the buffer type Statusline struct { v *View } -func (sl *Statusline) display() { +// Display draws the statusline to the screen +func (sl *Statusline) Display() { y := sl.v.height file := sl.v.buf.name @@ -19,12 +22,12 @@ func (sl *Statusline) display() { if sl.v.buf.text != sl.v.buf.savedText { file += " +" } - file += " (" + strconv.Itoa(sl.v.cursor.y+1) + "," + strconv.Itoa(sl.v.cursor.getVisualX()+1) + ")" + file += " (" + strconv.Itoa(sl.v.cursor.y+1) + "," + strconv.Itoa(sl.v.cursor.GetVisualX()+1) + ")" statusLineStyle := tcell.StyleDefault.Background(tcell.ColorNavy).Foreground(tcell.ColorBlack) for x := 0; x < sl.v.width; x++ { - if x < count(file) { + if x < Count(file) { sl.v.s.SetContent(x, y, []rune(file)[x], nil, statusLineStyle) } else { sl.v.s.SetContent(x, y, ' ', nil, statusLineStyle) diff --git a/util.go b/util.go index e78561a3..aaea4f25 100644 --- a/util.go +++ b/util.go @@ -4,11 +4,13 @@ import ( "unicode/utf8" ) -func count(s string) int { +// Count returns the length of a string in runes +func Count(s string) int { return utf8.RuneCountInString(s) } -func numOccurences(s string, c byte) int { +// NumOccurences counts the number of occurences of a byte in a string +func NumOccurences(s string, c byte) int { var n int for i := 0; i < len(s); i++ { if s[i] == c { @@ -18,7 +20,8 @@ func numOccurences(s string, c byte) int { return n } -func emptyString(n int) string { +// EmptyString returns an empty string n spaces long +func EmptyString(n int) string { var str string for i := 0; i < n; i++ { str += " " diff --git a/view.go b/view.go index 68b306af..21c1d215 100644 --- a/view.go +++ b/view.go @@ -4,6 +4,9 @@ import ( "github.com/gdamore/tcell" ) +// The View struct stores information about a view into a buffer. +// It has a value for the cursor, and the window that the user sees +// the buffer from. type View struct { cursor Cursor topline int @@ -18,19 +21,21 @@ type View struct { s tcell.Screen } -func newView(buf *Buffer, s tcell.Screen) *View { +// NewView returns a new view with fullscreen width and height +func NewView(buf *Buffer, s tcell.Screen) *View { w, h := s.Size() - return newViewWidthHeight(buf, s, w, h) + return NewViewWidthHeight(buf, s, w, h-1) } -func newViewWidthHeight(buf *Buffer, s tcell.Screen, w, h int) *View { +// NewViewWidthHeight returns a new view with the specified width and height +func NewViewWidthHeight(buf *Buffer, s tcell.Screen, w, h int) *View { v := new(View) v.buf = buf v.s = s v.topline = 0 - v.height = h - 2 + v.height = h - 1 v.width = w v.cursor = Cursor{ x: 0, @@ -46,7 +51,8 @@ func newViewWidthHeight(buf *Buffer, s tcell.Screen, w, h int) *View { return v } -func (v *View) scrollUp(n int) { +// ScrollUp scrolls the view up n lines (if possible) +func (v *View) ScrollUp(n int) { // Try to scroll by n but if it would overflow, scroll by 1 if v.topline-n >= 0 { v.topline -= n @@ -55,7 +61,8 @@ func (v *View) scrollUp(n int) { } } -func (v *View) scrollDown(n int) { +// ScrollDown scrolls the view down n lines (if possible) +func (v *View) ScrollDown(n int) { // Try to scroll by n but if it would overflow, scroll by 1 if v.topline+n <= len(v.buf.lines)-v.height { v.topline += n @@ -64,63 +71,64 @@ func (v *View) scrollDown(n int) { } } -// Returns an int describing how the screen needs to be redrawn +// HandleEvent handles an event passed by the main loop +// It returns an int describing how the screen needs to be redrawn // 0: Screen does not need to be redrawn // 1: Only the cursor/statusline needs to be redrawn // 2: Everything needs to be redrawn -func (v *View) handleEvent(event tcell.Event) int { +func (v *View) HandleEvent(event tcell.Event) int { var ret int switch e := event.(type) { case *tcell.EventKey: switch e.Key() { case tcell.KeyUp: - v.cursor.up() + v.cursor.Up() ret = 1 case tcell.KeyDown: - v.cursor.down() + v.cursor.Down() ret = 1 case tcell.KeyLeft: - v.cursor.left() + v.cursor.Left() ret = 1 case tcell.KeyRight: - v.cursor.right() + v.cursor.Right() ret = 1 case tcell.KeyEnter: - v.buf.insert(v.cursor.loc, "\n") - v.cursor.right() + v.buf.Insert(v.cursor.loc, "\n") + v.cursor.Right() ret = 2 case tcell.KeySpace: - v.buf.insert(v.cursor.loc, " ") - v.cursor.right() + v.buf.Insert(v.cursor.loc, " ") + v.cursor.Right() ret = 2 case tcell.KeyBackspace2: - if v.cursor.hasSelection() { - v.cursor.deleteSelected() - v.cursor.resetSelection() + if v.cursor.HasSelection() { + v.cursor.DeleteSelected() + v.cursor.ResetSelection() ret = 2 } else if v.cursor.loc > 0 { - v.cursor.left() - v.buf.remove(v.cursor.loc, v.cursor.loc+1) + v.cursor.Left() + v.buf.Remove(v.cursor.loc, v.cursor.loc+1) ret = 2 } case tcell.KeyTab: - v.buf.insert(v.cursor.loc, "\t") - v.cursor.right() + v.buf.Insert(v.cursor.loc, "\t") + v.cursor.Right() ret = 2 case tcell.KeyCtrlS: - err := v.buf.save() + err := v.buf.Save() if err != nil { // Error! } // Need to redraw the status line ret = 1 case tcell.KeyRune: - if v.cursor.hasSelection() { - v.cursor.deleteSelected() - v.cursor.resetSelection() + if v.cursor.HasSelection() { + v.cursor.DeleteSelected() + v.cursor.ResetSelection() } - v.buf.insert(v.cursor.loc, string(e.Rune())) - v.cursor.right() + v.buf.Insert(v.cursor.loc, string(e.Rune())) + v.cursor.Right() ret = 2 } case *tcell.EventMouse: @@ -135,18 +143,18 @@ func (v *View) handleEvent(event tcell.Event) int { switch button { case tcell.Button1: if y-v.topline > v.height-1 { - v.scrollDown(1) + v.ScrollDown(1) y = v.height + v.topline - 1 } if y > len(v.buf.lines) { y = len(v.buf.lines) - 2 } - x = v.cursor.getCharPosInLine(y, x) - if x > count(v.buf.lines[y]) { - x = count(v.buf.lines[y]) + x = v.cursor.GetCharPosInLine(y, x) + if x > Count(v.buf.lines[y]) { + x = Count(v.buf.lines[y]) } - d := v.cursor.distance(x, y) + d := v.cursor.Distance(x, y) v.cursor.loc += d v.cursor.x = x v.cursor.y = y @@ -163,10 +171,10 @@ func (v *View) handleEvent(event tcell.Event) int { v.mouseReleased = true return 0 case tcell.WheelUp: - v.scrollUp(2) + v.ScrollUp(2) return 2 case tcell.WheelDown: - v.scrollDown(2) + v.ScrollDown(2) return 2 } } @@ -184,18 +192,18 @@ func (v *View) handleEvent(event tcell.Event) int { return ret } -func (v *View) display() { - charNum := v.cursor.loc + v.cursor.distance(0, v.topline) +// Display renders the view to the screen +func (v *View) Display() { + charNum := v.cursor.loc + v.cursor.Distance(0, v.topline) for lineN := 0; lineN < v.height; lineN++ { if lineN+v.topline >= len(v.buf.lines) { break } - // line := strings.Replace(v.buf.lines[lineN+v.topline], "\t", emptyString(tabSize), -1) line := v.buf.lines[lineN+v.topline] tabchars := 0 for colN, ch := range line { st := tcell.StyleDefault - if v.cursor.hasSelection() && + if v.cursor.HasSelection() && (charNum >= v.cursor.selectionStart && charNum <= v.cursor.selectionEnd || charNum <= v.cursor.selectionStart && charNum >= v.cursor.selectionEnd) { st = st.Reverse(true)