diff --git a/internal/action/actions.go b/internal/action/actions.go index 345f47c6f..dd92a3640 100644 --- a/internal/action/actions.go +++ b/internal/action/actions.go @@ -879,11 +879,10 @@ func (h *BufPane) Search(str string, useRegex bool, searchDown bool) error { h.Cursor.SetSelectionEnd(match[1]) h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0] h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1] - h.Cursor.GotoLoc(h.Cursor.CurSelection[1]) + h.GotoLoc(h.Cursor.CurSelection[1]) h.Buf.LastSearch = str h.Buf.LastSearchRegex = useRegex h.Buf.HighlightSearch = h.Buf.Settings["hlsearch"].(bool) - h.Relocate() } else { h.Cursor.ResetSelection() } @@ -905,12 +904,11 @@ func (h *BufPane) find(useRegex bool) bool { h.Cursor.SetSelectionEnd(match[1]) h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0] h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1] - h.Cursor.GotoLoc(match[1]) + h.GotoLoc(match[1]) } else { - h.Cursor.GotoLoc(h.searchOrig) + h.GotoLoc(h.searchOrig) h.Cursor.ResetSelection() } - h.Relocate() } } findCallback := func(resp string, canceled bool) { @@ -925,7 +923,7 @@ func (h *BufPane) find(useRegex bool) bool { h.Cursor.SetSelectionEnd(match[1]) h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0] h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1] - h.Cursor.GotoLoc(h.Cursor.CurSelection[1]) + h.GotoLoc(h.Cursor.CurSelection[1]) h.Buf.LastSearch = resp h.Buf.LastSearchRegex = useRegex h.Buf.HighlightSearch = h.Buf.Settings["hlsearch"].(bool) @@ -936,7 +934,6 @@ func (h *BufPane) find(useRegex bool) bool { } else { h.Cursor.ResetSelection() } - h.Relocate() } pattern := string(h.Cursor.GetSelection()) if eventCallback != nil && pattern != "" { @@ -980,11 +977,10 @@ func (h *BufPane) FindNext() bool { h.Cursor.SetSelectionEnd(match[1]) h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0] h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1] - h.Cursor.Loc = h.Cursor.CurSelection[1] + h.GotoLoc(h.Cursor.CurSelection[1]) } else { h.Cursor.ResetSelection() } - h.Relocate() return true } @@ -1007,11 +1003,10 @@ func (h *BufPane) FindPrevious() bool { h.Cursor.SetSelectionEnd(match[1]) h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0] h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1] - h.Cursor.Loc = h.Cursor.CurSelection[1] + h.GotoLoc(h.Cursor.CurSelection[1]) } else { h.Cursor.ResetSelection() } - h.Relocate() return true } diff --git a/internal/action/bufpane.go b/internal/action/bufpane.go index 583e46ced..847f56416 100644 --- a/internal/action/bufpane.go +++ b/internal/action/bufpane.go @@ -13,6 +13,7 @@ import ( "github.com/zyedidia/micro/v2/internal/display" ulua "github.com/zyedidia/micro/v2/internal/lua" "github.com/zyedidia/micro/v2/internal/screen" + "github.com/zyedidia/micro/v2/internal/util" "github.com/zyedidia/tcell/v2" ) @@ -235,10 +236,14 @@ type BufPane struct { // remember original location of a search in case the search is canceled searchOrig buffer.Loc + + // The pane may not yet be fully initialized after its creation + // since we may not know the window geometry yet. In such case we finish + // its initialization a bit later, after the initial resize. + initialized bool } -// NewBufPane creates a new buffer pane with the given window. -func NewBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane { +func newBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane { h := new(BufPane) h.Buf = buf h.BWindow = win @@ -247,8 +252,13 @@ func NewBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane { h.Cursor = h.Buf.GetActiveCursor() h.mouseReleased = true - config.RunPluginFn("onBufPaneOpen", luar.New(ulua.L, h)) + return h +} +// NewBufPane creates a new buffer pane with the given window. +func NewBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane { + h := newBufPane(buf, win, tab) + h.finishInitialize() return h } @@ -256,7 +266,25 @@ func NewBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane { // creates a buf window. func NewBufPaneFromBuf(buf *buffer.Buffer, tab *Tab) *BufPane { w := display.NewBufWindow(0, 0, 0, 0, buf) - return NewBufPane(buf, w, tab) + h := newBufPane(buf, w, tab) + // Postpone finishing initializing the pane until we know the actual geometry + // of the buf window. + return h +} + +// TODO: make sure splitID and tab are set before finishInitialize is called +func (h *BufPane) finishInitialize() { + h.initialRelocate() + h.initialized = true + config.RunPluginFn("onBufPaneOpen", luar.New(ulua.L, h)) +} + +// Resize resizes the pane +func (h *BufPane) Resize(width, height int) { + h.BWindow.Resize(width, height) + if !h.initialized { + h.finishInitialize() + } } // SetTab sets this pane's tab. @@ -301,7 +329,7 @@ func (h *BufPane) OpenBuffer(b *buffer.Buffer) { h.BWindow.SetBuffer(b) h.Cursor = b.GetActiveCursor() h.Resize(h.GetView().Width, h.GetView().Height) - h.Relocate() + h.initialRelocate() // Set mouseReleased to true because we assume the mouse is not being // pressed when the editor is opened h.mouseReleased = true @@ -311,6 +339,43 @@ func (h *BufPane) OpenBuffer(b *buffer.Buffer) { h.lastClickTime = time.Time{} } +// GotoLoc moves the cursor to a new location and adjusts the view accordingly. +// Use GotoLoc when the new location may be far away from the current location. +func (h *BufPane) GotoLoc(loc buffer.Loc) { + sloc := h.SLocFromLoc(loc) + d := h.Diff(h.SLocFromLoc(h.Cursor.Loc), sloc) + + h.Cursor.GotoLoc(loc) + + // If the new location is far away from the previous one, + // ensure the cursor is at 25% of the window height + height := h.BufView().Height + if util.Abs(d) >= height { + v := h.GetView() + v.StartLine = h.Scroll(sloc, -height/4) + h.ScrollAdjust() + v.StartCol = 0 + } + h.Relocate() +} + +func (h *BufPane) initialRelocate() { + sloc := h.SLocFromLoc(h.Cursor.Loc) + height := h.BufView().Height + + // If the initial cursor location is far away from the beginning + // of the buffer, ensure the cursor is at 25% of the window height + v := h.GetView() + if h.Diff(display.SLoc{0, 0}, sloc) < height { + v.StartLine = display.SLoc{0, 0} + } else { + v.StartLine = h.Scroll(sloc, -height/4) + h.ScrollAdjust() + } + v.StartCol = 0 + h.Relocate() +} + // ID returns this pane's split id. func (h *BufPane) ID() uint64 { return h.splitID diff --git a/internal/action/command.go b/internal/action/command.go index 6e0c795cc..4818d3d22 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -722,7 +722,7 @@ func (h *BufPane) GotoCmd(args []string) { } line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1) col = util.Clamp(col-1, 0, util.CharacterCount(h.Buf.LineBytes(line))) - h.Cursor.GotoLoc(buffer.Loc{col, line}) + h.GotoLoc(buffer.Loc{col, line}) } else { line, err := strconv.Atoi(args[0]) if err != nil { @@ -733,9 +733,8 @@ func (h *BufPane) GotoCmd(args []string) { line = h.Buf.LinesNum() + 1 + line } line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1) - h.Cursor.GotoLoc(buffer.Loc{0, line}) + h.GotoLoc(buffer.Loc{0, line}) } - h.Relocate() } } @@ -834,13 +833,11 @@ func (h *BufPane) ReplaceCmd(args []string) { h.Cursor.SetSelectionStart(locs[0]) h.Cursor.SetSelectionEnd(locs[1]) - h.Cursor.GotoLoc(locs[0]) + h.GotoLoc(locs[0]) h.Buf.LastSearch = search h.Buf.LastSearchRegex = true h.Buf.HighlightSearch = h.Buf.Settings["hlsearch"].(bool) - h.Relocate() - InfoBar.YNPrompt("Perform replacement (y,n,esc)", func(yes, canceled bool) { if !canceled && yes { _, nrunes := h.Buf.ReplaceRegex(locs[0], locs[1], regex, replace)