X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=cmd%2Fmicro%2Fview.go;h=44fa6f8accee1cde6d0cdbc47716b7a16a250429;hb=7e0286b6213a59692c1ac59bb45f6728a1c95473;hp=1a73728a5bda9f126b6b6d5ac04a212aa8b710c4;hpb=646cdd6a9fd6ef688a5be8a5ef57404ba10a6d24;p=micro.git diff --git a/cmd/micro/view.go b/cmd/micro/view.go index 1a73728a..44fa6f8a 100644 --- a/cmd/micro/view.go +++ b/cmd/micro/view.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/mattn/go-runewidth" "github.com/zyedidia/tcell" ) @@ -30,6 +31,9 @@ type View struct { width int height int + // Where this view is located + x, y int + // How much to offset because of line numbers lineNumOffset int @@ -39,6 +43,11 @@ type View struct { // Is the help text opened in this view helpOpen bool + // This is the index of this view in the views array + Num int + // What tab is this view stored in + TabNum int + // Is this view modifiable? Modifiable bool @@ -90,6 +99,8 @@ func NewView(buf *Buffer) *View { func NewViewWidthHeight(buf *Buffer, w, h int) *View { v := new(View) + v.x, v.y = 0, 0 + v.widthPercent = w v.heightPercent = h v.Resize(screen.Size()) @@ -112,6 +123,13 @@ func NewViewWidthHeight(buf *Buffer, w, h int) *View { func (v *View) Resize(w, h int) { // Always include 1 line for the command line at the bottom h-- + if len(tabs) > 1 { + // Include one line for the tab bar at the top + h-- + v.y = 1 + } else { + v.y = 0 + } v.width = int(float32(w) * float32(v.widthPercent) / 100) // We subtract 1 for the statusline v.height = int(float32(h) * float32(v.heightPercent) / 100) @@ -147,7 +165,7 @@ func (v *View) ScrollDown(n int) { // The message is what to print after saying "You have unsaved changes. " func (v *View) CanClose(msg string) bool { if v.Buf.IsModified { - quit, canceled := messenger.Prompt("You have unsaved changes. "+msg, "Unsaved") + quit, canceled := messenger.Prompt("You have unsaved changes. "+msg, "Unsaved", NoCompletion) if !canceled { if strings.ToLower(quit) == "yes" || strings.ToLower(quit) == "y" { return true @@ -172,6 +190,7 @@ func (v *View) OpenBuffer(buf *Buffer) { v.Topline = 0 v.leftCol = 0 v.Cursor.ResetSelection() + v.Relocate() v.messages = make(map[string][]GutterMessage) v.matches = Match(v) @@ -199,6 +218,20 @@ func (v *View) ReOpen() { } } +func (v *View) VSplit() bool { + v.widthPercent /= 2 + v.Resize(screen.Size()) + + newView := NewViewWidthHeight(NewBuffer([]byte{}, ""), v.widthPercent, v.heightPercent) + newView.TabNum = v.TabNum + newView.x = v.x + v.width + tab := tabs[v.TabNum] + tab.curView++ + newView.Num = len(tab.views) + tab.views = append(tab.views, newView) + return false +} + // Relocate moves the view window so that the cursor is in view // This is useful if the user has scrolled far away, and then starts typing func (v *View) Relocate() bool { @@ -250,8 +283,8 @@ func (v *View) MoveToMouseClick(x, y int) { } x = v.Cursor.GetCharPosInLine(y, x) - if x > Count(v.Buf.Lines[y]) { - x = Count(v.Buf.Lines[y]) + if x > Count(v.Buf.Line(y)) { + x = Count(v.Buf.Line(y)) } v.Cursor.X = x v.Cursor.Y = y @@ -271,14 +304,21 @@ func (v *View) HandleEvent(event tcell.Event) { // Window resized v.Resize(e.Size()) case *tcell.EventKey: - if e.Key() == tcell.KeyRune && e.Modifiers() == 0 { + if e.Key() == tcell.KeyRune && (e.Modifiers() == 0 || e.Modifiers() == tcell.ModShift) { // Insert a character if v.Cursor.HasSelection() { v.Cursor.DeleteSelection() v.Cursor.ResetSelection() } - v.Buf.Insert(v.Cursor.Loc(), string(e.Rune())) + v.Buf.Insert(v.Cursor.Loc, string(e.Rune())) v.Cursor.Right() + + for _, pl := range loadedPlugins { + err := Call(pl+".onRune", []string{string(e.Rune())}) + if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") { + TermMessage(err) + } + } } else { for key, actions := range bindings { if e.Key() == key.keyCode { @@ -293,7 +333,7 @@ func (v *View) HandleEvent(event tcell.Event) { relocate = action(v) || relocate for _, pl := range loadedPlugins { funcName := strings.Split(runtime.FuncForPC(reflect.ValueOf(action).Pointer()).Name(), ".") - err := Call(pl+"_on"+funcName[len(funcName)-1], nil) + err := Call(pl+".on"+funcName[len(funcName)-1], nil) if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") { TermMessage(err) } @@ -309,13 +349,13 @@ func (v *View) HandleEvent(event tcell.Event) { v.Cursor.ResetSelection() } clip := e.Text() - v.Buf.Insert(v.Cursor.Loc(), clip) - v.Cursor.SetLoc(v.Cursor.Loc() + Count(clip)) + v.Buf.Insert(v.Cursor.Loc, clip) + v.Cursor.Loc = v.Cursor.Loc.Move(Count(clip), v.Buf) v.freshClip = false case *tcell.EventMouse: x, y := e.Position() - x -= v.lineNumOffset - v.leftCol - y += v.Topline + x -= v.lineNumOffset - v.leftCol + v.x + y += v.Topline - v.y // Don't relocate for mouse events relocate = false @@ -349,10 +389,9 @@ func (v *View) HandleEvent(event tcell.Event) { v.tripleClick = false v.lastClickTime = time.Now() - loc := v.Cursor.Loc() - v.Cursor.OrigSelection[0] = loc - v.Cursor.CurSelection[0] = loc - v.Cursor.CurSelection[1] = loc + v.Cursor.OrigSelection[0] = v.Cursor.Loc + v.Cursor.CurSelection[0] = v.Cursor.Loc + v.Cursor.CurSelection[1] = v.Cursor.Loc } v.mouseReleased = false } else if !v.mouseReleased { @@ -362,7 +401,7 @@ func (v *View) HandleEvent(event tcell.Event) { } else if v.doubleClick { v.Cursor.AddWordToSelection() } else { - v.Cursor.CurSelection[1] = v.Cursor.Loc() + v.Cursor.CurSelection[1] = v.Cursor.Loc } } case tcell.ButtonNone: @@ -378,7 +417,7 @@ func (v *View) HandleEvent(event tcell.Event) { if !v.doubleClick && !v.tripleClick { v.MoveToMouseClick(x, y) - v.Cursor.CurSelection[1] = v.Cursor.Loc() + v.Cursor.CurSelection[1] = v.Cursor.Loc } v.mouseReleased = true } @@ -435,7 +474,7 @@ func (v *View) ClearAllGutterMessages() { // DisplayView renders the view to the screen func (v *View) DisplayView() { // The character number of the character in the top left of the screen - charNum := ToCharPos(0, v.Topline, v.Buf) + charNum := Loc{0, v.Topline} // Convert the length of buffer to a string, and get the length of the string // We are going to have to offset by that amount @@ -446,7 +485,7 @@ func (v *View) DisplayView() { } else { v.lineNumOffset = 0 } - var highlightStyle tcell.Style + highlightStyle := defStyle var hasGutterMessages bool for _, v := range v.messages { @@ -459,17 +498,17 @@ func (v *View) DisplayView() { } for lineN := 0; lineN < v.height; lineN++ { - var x int + x := v.x // If the buffer is smaller than the view height if lineN+v.Topline >= v.Buf.NumLines { // We have to clear all this space - for i := 0; i < v.width; i++ { - screen.SetContent(i, lineN, ' ', nil, defStyle) + for i := v.x; i < v.width; i++ { + screen.SetContent(i, lineN+v.y, ' ', nil, defStyle) } continue } - line := v.Buf.Lines[lineN+v.Topline] + line := v.Buf.Line(lineN + v.Topline) if hasGutterMessages { msgOnLine := false @@ -477,7 +516,7 @@ func (v *View) DisplayView() { for _, msg := range v.messages[k] { if msg.lineNum == lineN+v.Topline { msgOnLine = true - gutterStyle := tcell.StyleDefault + gutterStyle := defStyle switch msg.kind { case GutterInfo: if style, ok := colorscheme["gutter-info"]; ok { @@ -492,9 +531,9 @@ func (v *View) DisplayView() { gutterStyle = style } } - screen.SetContent(x, lineN, '>', nil, gutterStyle) + screen.SetContent(x, lineN+v.y, '>', nil, gutterStyle) x++ - screen.SetContent(x, lineN, '>', nil, gutterStyle) + screen.SetContent(x, lineN+v.y, '>', nil, gutterStyle) x++ if v.Cursor.Y == lineN+v.Topline { messenger.Message(msg.msg) @@ -504,9 +543,9 @@ func (v *View) DisplayView() { } } if !msgOnLine { - screen.SetContent(x, lineN, ' ', nil, tcell.StyleDefault) + screen.SetContent(x, lineN+v.y, ' ', nil, defStyle) x++ - screen.SetContent(x, lineN, ' ', nil, tcell.StyleDefault) + screen.SetContent(x, lineN+v.y, ' ', nil, defStyle) x++ if v.Cursor.Y == lineN+v.Topline && messenger.gutterMessage { messenger.Reset() @@ -525,24 +564,24 @@ func (v *View) DisplayView() { if settings["ruler"] == true { lineNum = strconv.Itoa(lineN + v.Topline + 1) for i := 0; i < maxLineLength-len(lineNum); i++ { - screen.SetContent(x, lineN, ' ', nil, lineNumStyle) + screen.SetContent(x, lineN+v.y, ' ', nil, lineNumStyle) x++ } // Write the actual line number for _, ch := range lineNum { - screen.SetContent(x, lineN, ch, nil, lineNumStyle) + screen.SetContent(x, lineN+v.y, ch, nil, lineNumStyle) x++ } if settings["ruler"] == true { // Write the extra space - screen.SetContent(x, lineN, ' ', nil, lineNumStyle) + screen.SetContent(x, lineN+v.y, ' ', nil, lineNumStyle) x++ } } // Write the line for colN, ch := range line { - var lineStyle tcell.Style + lineStyle := defStyle if settings["syntax"].(bool) { // Syntax highlighting is enabled @@ -550,10 +589,10 @@ func (v *View) DisplayView() { } if v.Cursor.HasSelection() && - (charNum >= v.Cursor.CurSelection[0] && charNum < v.Cursor.CurSelection[1] || - charNum < v.Cursor.CurSelection[0] && charNum >= v.Cursor.CurSelection[1]) { + (charNum.GreaterEqual(v.Cursor.CurSelection[0]) && charNum.LessThan(v.Cursor.CurSelection[1]) || + charNum.LessThan(v.Cursor.CurSelection[0]) && charNum.GreaterEqual(v.Cursor.CurSelection[1])) { - lineStyle = tcell.StyleDefault.Reverse(true) + lineStyle = defStyle.Reverse(true) if style, ok := colorscheme["selection"]; ok { lineStyle = style @@ -562,7 +601,7 @@ func (v *View) DisplayView() { lineStyle = highlightStyle } - if settings["cursorline"].(bool) && v.Cursor.Y == lineN+v.Topline { + if settings["cursorline"].(bool) && !v.Cursor.HasSelection() && v.Cursor.Y == lineN+v.Topline { if style, ok := colorscheme["cursor-line"]; ok { fg, _, _ := style.Decompose() lineStyle = lineStyle.Background(fg) @@ -575,25 +614,37 @@ func (v *View) DisplayView() { lineIndentStyle = style } if v.Cursor.HasSelection() && - (charNum >= v.Cursor.CurSelection[0] && charNum < v.Cursor.CurSelection[1] || - charNum < v.Cursor.CurSelection[0] && charNum >= v.Cursor.CurSelection[1]) { + (charNum.GreaterEqual(v.Cursor.CurSelection[0]) && charNum.LessThan(v.Cursor.CurSelection[1]) || + charNum.LessThan(v.Cursor.CurSelection[0]) && charNum.GreaterEqual(v.Cursor.CurSelection[1])) { - lineIndentStyle = tcell.StyleDefault.Reverse(true) + lineIndentStyle = defStyle.Reverse(true) if style, ok := colorscheme["selection"]; ok { lineIndentStyle = style } } - if settings["cursorline"].(bool) && v.Cursor.Y == lineN+v.Topline { + if settings["cursorline"].(bool) && !v.Cursor.HasSelection() && v.Cursor.Y == lineN+v.Topline { if style, ok := colorscheme["cursor-line"]; ok { fg, _, _ := style.Decompose() lineIndentStyle = lineIndentStyle.Background(fg) } } indentChar := []rune(settings["indentchar"].(string)) - screen.SetContent(x-v.leftCol, lineN, indentChar[0], nil, lineIndentStyle) + if x-v.leftCol >= v.lineNumOffset { + screen.SetContent(x-v.leftCol, lineN+v.y, indentChar[0], nil, lineIndentStyle) + } tabSize := int(settings["tabsize"].(float64)) for i := 0; i < tabSize-1; i++ { + x++ + if x-v.leftCol >= v.lineNumOffset { + screen.SetContent(x-v.leftCol, lineN+v.y, ' ', nil, lineStyle) + } + } + } else if runewidth.RuneWidth(ch) > 1 { + if x-v.leftCol >= v.lineNumOffset { + screen.SetContent(x-v.leftCol, lineN, ch, nil, lineStyle) + } + for i := 0; i < runewidth.RuneWidth(ch)-1; i++ { x++ if x-v.leftCol >= v.lineNumOffset { screen.SetContent(x-v.leftCol, lineN, ' ', nil, lineStyle) @@ -601,10 +652,10 @@ func (v *View) DisplayView() { } } else { if x-v.leftCol >= v.lineNumOffset { - screen.SetContent(x-v.leftCol, lineN, ch, nil, lineStyle) + screen.SetContent(x-v.leftCol, lineN+v.y, ch, nil, lineStyle) } } - charNum++ + charNum = charNum.Move(1, v.Buf) x++ } // Here we are at a newline @@ -612,29 +663,31 @@ func (v *View) DisplayView() { // The newline may be selected, in which case we should draw the selection style // with a space to represent it if v.Cursor.HasSelection() && - (charNum >= v.Cursor.CurSelection[0] && charNum < v.Cursor.CurSelection[1] || - charNum < v.Cursor.CurSelection[0] && charNum >= v.Cursor.CurSelection[1]) { + (charNum.GreaterEqual(v.Cursor.CurSelection[0]) && charNum.LessThan(v.Cursor.CurSelection[1]) || + charNum.LessThan(v.Cursor.CurSelection[0]) && charNum.GreaterEqual(v.Cursor.CurSelection[1])) { selectStyle := defStyle.Reverse(true) if style, ok := colorscheme["selection"]; ok { selectStyle = style } - screen.SetContent(x-v.leftCol, lineN, ' ', nil, selectStyle) + screen.SetContent(x-v.leftCol, lineN+v.y, ' ', nil, selectStyle) x++ } - charNum++ + charNum = charNum.Move(1, v.Buf) - for i := 0; i < v.width-x; i++ { - lineStyle := tcell.StyleDefault - if settings["cursorline"].(bool) && v.Cursor.Y == lineN+v.Topline { + for i := 0; i < v.width-((x-v.x)-v.leftCol); i++ { + lineStyle := defStyle + if settings["cursorline"].(bool) && !v.Cursor.HasSelection() && v.Cursor.Y == lineN+v.Topline { if style, ok := colorscheme["cursor-line"]; ok { fg, _, _ := style.Decompose() lineStyle = lineStyle.Background(fg) } } - screen.SetContent(x+i, lineN, ' ', nil, lineStyle) + if !(x-v.leftCol < v.lineNumOffset) { + screen.SetContent(x+i, lineN+v.y, ' ', nil, lineStyle) + } } } } @@ -645,7 +698,7 @@ func (v *View) DisplayCursor() { if (v.Cursor.Y-v.Topline < 0 || v.Cursor.Y-v.Topline > v.height-1) || v.Cursor.HasSelection() { screen.HideCursor() } else { - screen.ShowCursor(v.Cursor.GetVisualX()+v.lineNumOffset-v.leftCol, v.Cursor.Y-v.Topline) + screen.ShowCursor(v.x+v.Cursor.GetVisualX()+v.lineNumOffset-v.leftCol, v.Cursor.Y-v.Topline+v.y) } }