mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-17 14:27:12 +09:00
Improve buffer view relocation after jumping to a far-away location (#2628)
* Improve buffer view relocation after jumping to a far-away location
When the cursor is moved to a location which is far away from the
current location (e.g. after a search or a goto line), the buffer view
is always relocated in such a way that the cursor is at the bottom or
at the top (minus scrollmargin), i.e. as if we just scrolled to this
location. It's not like in other editors, and IMHO it's annoying. When
we jump to a new location far away, we usually want to see more of its
context, so the cursor should be placed closer to the center of the
view, not near its edges.
This change implements the behavior similar to other editors:
- If the distance between the new and the old location is less than one
frame (i.e. the view either doesn't change or just slightly "shifts")
then the current behavior remains unchanged.
- Otherwise the current line is placed at 25% of the window height.
* Postpone calling onBufPaneOpen until the initial resize
It is currently not possible to find out the geometry of a newly created
bufpane in onBufPaneOpen lua callback: bp:GetView() returns {0,0,0,0}
instead of the actual window. The reason is that the bufpane view is not
properly initialized yet when the bufpane is created and the callback is
triggered. It is initialized a bit later, at the initial resize.
So postpone calling onBufPaneOpen until after the initial resize.
* Improve buffer view relocation when opening a file at a far-away location
When a file is opened with the initial cursor location at a given line
which is far away from the beginning of the file, the buffer view is
relocated so that the cursor is at the bottom (minus scrollmargin)
as if we just scrolled to this line, which is annoying since we'd rather
like to see more of the context of this initial location.
So implement the behavior similar to the earlier commit (which addresses
a similar issue about jumping far away after a search or goto):
- If the initial cursor location is less than one frame away from the
beginning of the buffer, keep the existing behavior i.e. just display
the beginning of the buffer.
- Otherwise place the cursor location at 25% of the window height.
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user