diff --git a/cmd/micro/action/actions.go b/cmd/micro/action/actions.go index 813831db..0422fed1 100644 --- a/cmd/micro/action/actions.go +++ b/cmd/micro/action/actions.go @@ -34,18 +34,18 @@ func (h *BufHandler) ScrollDown(n int) { // MousePress is the event that should happen when a normal click happens // This is almost always bound to left click func (h *BufHandler) MousePress(e *tcell.EventMouse) bool { - h.ScrollUp(h.Buf.Settings["scrollspeed"].(int)) return false } // ScrollUpAction scrolls the view up func (h *BufHandler) ScrollUpAction() bool { + h.ScrollUp(util.IntOpt(h.Buf.Settings["scrollspeed"])) return false } // ScrollDownAction scrolls the view up func (h *BufHandler) ScrollDownAction() bool { - h.ScrollDown(h.Buf.Settings["scrollspeed"].(int)) + h.ScrollDown(util.IntOpt(h.Buf.Settings["scrollspeed"])) return false } @@ -298,6 +298,38 @@ func (h *BufHandler) InsertSpace() bool { // InsertNewline inserts a newline plus possible some whitespace if autoindent is on func (h *BufHandler) InsertNewline() bool { + if h.Buf.Type == buffer.BTInfo { + info.MainBar.DonePrompt(false) + return false + } + + // Insert a newline + if h.Cursor.HasSelection() { + h.Cursor.DeleteSelection() + h.Cursor.ResetSelection() + } + + ws := util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y)) + cx := h.Cursor.X + h.Buf.Insert(h.Cursor.Loc, "\n") + // h.Cursor.Right() + + if h.Buf.Settings["autoindent"].(bool) { + if cx < len(ws) { + ws = ws[0:cx] + } + h.Buf.Insert(h.Cursor.Loc, string(ws)) + // for i := 0; i < len(ws); i++ { + // h.Cursor.Right() + // } + + // Remove the whitespaces if keepautoindent setting is off + if util.IsSpacesOrTabs(h.Buf.LineBytes(h.Cursor.Y-1)) && !h.Buf.Settings["keepautoindent"].(bool) { + line := h.Buf.LineBytes(h.Cursor.Y - 1) + h.Buf.Remove(buffer.Loc{X: 0, Y: h.Cursor.Y - 1}, buffer.Loc{X: utf8.RuneCount(line), Y: h.Cursor.Y - 1}) + } + } + h.Cursor.LastVisualX = h.Cursor.GetVisualX() return true } @@ -402,7 +434,7 @@ func (h *BufHandler) OutdentLine() bool { return false } - for x := 0; x < len(h.Buf.IndentString(h.Buf.Settings["tabsize"].(int))); x++ { + for x := 0; x < len(h.Buf.IndentString(util.IntOpt(h.Buf.Settings["tabsize"]))); x++ { if len(util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y))) == 0 { break } @@ -426,7 +458,7 @@ func (h *BufHandler) OutdentSelection() bool { startY := start.Y endY := end.Move(-1, h.Buf).Y for y := startY; y <= endY; y++ { - for x := 0; x < len(h.Buf.IndentString(h.Buf.Settings["tabsize"].(int))); x++ { + for x := 0; x < len(h.Buf.IndentString(util.IntOpt(h.Buf.Settings["tabsize"]))); x++ { if len(util.GetLeadingWhitespace(h.Buf.LineBytes(y))) == 0 { break } @@ -442,7 +474,7 @@ func (h *BufHandler) OutdentSelection() bool { // InsertTab inserts a tab or spaces func (h *BufHandler) InsertTab() bool { - indent := h.Buf.IndentString(h.Buf.Settings["tabsize"].(int)) + indent := h.Buf.IndentString(util.IntOpt(h.Buf.Settings["tabsize"])) tabBytes := len(indent) bytesUntilIndent := tabBytes - (h.Cursor.GetVisualX() % tabBytes) h.Buf.Insert(h.Cursor.Loc, indent[:bytesUntilIndent]) @@ -609,6 +641,14 @@ func (h *BufHandler) SelectAll() bool { // OpenFile opens a new file in the buffer func (h *BufHandler) OpenFile() bool { + cb := func(resp string, canceled bool) { + if !canceled { + info.MainBar.Message("Opening", resp) + } else { + info.MainBar.Error("Canceled") + } + } + info.MainBar.Prompt("Open file: ", cb) return false } diff --git a/cmd/micro/buffer/buffer.go b/cmd/micro/buffer/buffer.go index f6214429..5dbe637c 100644 --- a/cmd/micro/buffer/buffer.go +++ b/cmd/micro/buffer/buffer.go @@ -62,6 +62,7 @@ var ( BTLog = BufType{2, true, true, false} BTScratch = BufType{3, false, true, false} BTRaw = BufType{4, true, true, false} + BTInfo = BufType{5, false, true, false} ) // Buffer stores the main information about a currently open file including diff --git a/cmd/micro/display/infowindow.go b/cmd/micro/display/infowindow.go index 278c5b74..3fbd0ef4 100644 --- a/cmd/micro/display/infowindow.go +++ b/cmd/micro/display/infowindow.go @@ -1,12 +1,14 @@ package display import ( - "strings" + "unicode/utf8" runewidth "github.com/mattn/go-runewidth" + "github.com/zyedidia/micro/cmd/micro/buffer" "github.com/zyedidia/micro/cmd/micro/config" "github.com/zyedidia/micro/cmd/micro/info" "github.com/zyedidia/micro/cmd/micro/screen" + "github.com/zyedidia/micro/cmd/micro/util" "github.com/zyedidia/tcell" ) @@ -56,19 +58,99 @@ func (i *InfoWindow) Clear() { } } +func (i *InfoWindow) displayBuffer() { + b := i.Buffer + line := b.LineBytes(0) + activeC := b.GetActiveCursor() + + blocX := 0 + vlocX := utf8.RuneCountInString(i.Msg) + + tabsize := 4 + line, nColsBeforeStart := util.SliceVisualEnd(line, blocX, tabsize) + + draw := func(r rune, style tcell.Style) { + if nColsBeforeStart <= 0 { + bloc := buffer.Loc{X: blocX, Y: 0} + if activeC.HasSelection() && + (bloc.GreaterEqual(activeC.CurSelection[0]) && bloc.LessThan(activeC.CurSelection[1]) || + bloc.LessThan(activeC.CurSelection[0]) && bloc.GreaterEqual(activeC.CurSelection[1])) { + // The current character is selected + style = config.DefStyle.Reverse(true) + + if s, ok := config.Colorscheme["selection"]; ok { + style = s + } + + } + + screen.Screen.SetContent(vlocX, i.y, r, nil, style) + vlocX++ + } + nColsBeforeStart-- + } + + totalwidth := blocX - nColsBeforeStart + for len(line) > 0 { + if activeC.X == blocX { + screen.Screen.ShowCursor(vlocX, i.y) + } + + r, size := utf8.DecodeRune(line) + + draw(r, i.defStyle) + + width := 0 + + char := ' ' + switch r { + case '\t': + ts := tabsize - (totalwidth % tabsize) + width = ts + default: + width = runewidth.RuneWidth(r) + char = '@' + } + + blocX++ + line = line[size:] + + // Draw any extra characters either spaces for tabs or @ for incomplete wide runes + if width > 1 { + for j := 1; j < width; j++ { + draw(char, i.defStyle) + } + } + totalwidth += width + if vlocX >= i.width { + break + } + } + if activeC.X == blocX { + screen.Screen.ShowCursor(vlocX, i.y) + } +} + func (i *InfoWindow) Display() { x := 0 if i.HasPrompt || config.GlobalSettings["infobar"].(bool) { + if !i.HasPrompt && !i.HasMessage && !i.HasError { + return + } style := i.defStyle if i.HasError { style = i.errStyle } - display := i.Msg + strings.TrimSpace(string(i.Bytes())) + display := i.Msg for _, c := range display { screen.Screen.SetContent(x, i.y, c, nil, style) x += runewidth.RuneWidth(c) } + + if i.HasPrompt { + i.displayBuffer() + } } } diff --git a/cmd/micro/display/window.go b/cmd/micro/display/window.go index 14aff6ad..0cbd2e7d 100644 --- a/cmd/micro/display/window.go +++ b/cmd/micro/display/window.go @@ -1,7 +1,6 @@ package display import ( - "log" "strconv" "unicode/utf8" @@ -81,9 +80,8 @@ func (w *BufWindow) Bottomline() int { // } prev := 0 - for i, l := range w.lineHeight { + for _, l := range w.lineHeight { if l >= prev { - log.Println("lineHeight[", i, "] = ", l) prev = l } else { break @@ -98,7 +96,6 @@ func (w *BufWindow) Bottomline() int { func (w *BufWindow) Relocate() bool { b := w.Buf height := w.Bottomline() + 1 - w.StartLine - log.Println("Height: ", height) ret := false activeC := w.Buf.GetActiveCursor() cy := activeC.Y @@ -115,7 +112,6 @@ func (w *BufWindow) Relocate() bool { ret = true } else if cy >= b.LinesNum()-scrollmargin && cy >= height { w.StartLine = b.LinesNum() - height - log.Println(w.StartLine) ret = true } @@ -217,7 +213,7 @@ func (w *BufWindow) displayBuffer() { // this represents the current draw position in the buffer (char positions) bloc := buffer.Loc{X: w.StartCol, Y: w.StartLine} - activeC := w.Buf.GetActiveCursor() + activeC := b.GetActiveCursor() curStyle := config.DefStyle for vloc.Y = 0; vloc.Y < bufHeight; vloc.Y++ { diff --git a/cmd/micro/info/infobar.go b/cmd/micro/info/infobar.go index 099f4571..6a068efe 100644 --- a/cmd/micro/info/infobar.go +++ b/cmd/micro/info/infobar.go @@ -2,6 +2,7 @@ package info import ( "fmt" + "strings" "github.com/zyedidia/micro/cmd/micro/buffer" ) @@ -30,13 +31,15 @@ type Bar struct { // Is the current message a message from the gutter GutterMessage bool + + PromptCallback func(resp string, canceled bool) } func NewBar() *Bar { ib := new(Bar) ib.History = make(map[string][]string) - ib.Buffer = buffer.NewBufferFromString("", "infobar", buffer.BTScratch) + ib.Buffer = buffer.NewBufferFromString("", "infobar", buffer.BTInfo) return ib } @@ -60,7 +63,23 @@ func (i *Bar) Error(msg ...interface{}) { if i.HasPrompt == false { // if there is no active prompt then style and display the message as normal i.Msg = fmt.Sprint(msg...) - i.HasError = true + i.HasMessage, i.HasError = false, true } // TODO: add to log? } + +func (i *Bar) Prompt(msg string, callback func(string, bool)) { + i.Msg = msg + i.HasPrompt = true + i.HasMessage, i.HasError = false, false + i.PromptCallback = callback +} + +func (i *Bar) DonePrompt(canceled bool) { + i.HasPrompt = false + if canceled { + i.PromptCallback("", true) + } else { + i.PromptCallback(strings.TrimSpace(string(i.LineBytes(0))), false) + } +} diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index c56b7506..27d1ed29 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -212,6 +212,7 @@ func main() { for { // Display everything screen.Screen.Fill(' ', config.DefStyle) + screen.Screen.HideCursor() ep.Display() infowindow.Display() screen.Screen.Show() diff --git a/cmd/micro/util/util.go b/cmd/micro/util/util.go index 6d324434..02b5ffd9 100644 --- a/cmd/micro/util/util.go +++ b/cmd/micro/util/util.go @@ -155,6 +155,17 @@ func IsSpaces(str []byte) bool { return true } +// IsSpacesOrTabs checks if a given string contains only spaces and tabs +func IsSpacesOrTabs(str []byte) bool { + for _, c := range str { + if c != ' ' && c != '\t' { + return false + } + } + + return true +} + // IsWhitespace returns true if the given rune is a space, tab, or newline func IsWhitespace(c rune) bool { return c == ' ' || c == '\t' || c == '\n' @@ -247,3 +258,8 @@ func GetLeadingWhitespace(b []byte) []byte { } return ws } + +// IntOpt turns a float64 setting to an int +func IntOpt(opt interface{}) int { + return int(opt.(float64)) +}