Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions internal/action/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -916,16 +916,6 @@ func (h *BufPane) Autocomplete() bool {
return true
}

if h.Cursor.X == 0 {
return false
}
r := h.Cursor.RuneUnder(h.Cursor.X)
prev := h.Cursor.RuneUnder(h.Cursor.X - 1)
if !util.IsAutocomplete(prev) || util.IsWordChar(r) {
// don't autocomplete if cursor is within a word
return false
}

return b.Autocomplete(buffer.BufferComplete)
}

Expand Down
55 changes: 48 additions & 7 deletions internal/buffer/autocomplete.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ func (b *Buffer) GetSuggestions() {

// Autocomplete starts the autocomplete process
func (b *Buffer) Autocomplete(c Completer) bool {
if !b.GetActiveCursor().CanAutocomplete() {
return false
}
b.Completions, b.Suggestions = c(b)
if len(b.Completions) != len(b.Suggestions) || len(b.Completions) == 0 {
return false
Expand All @@ -49,23 +52,57 @@ func (b *Buffer) CycleAutocomplete(forward bool) {
b.CurSuggestion = len(b.Suggestions) - 1
}

c := b.GetActiveCursor()
// cycle autocomplete for all except active cursors
for i, c := range b.cursors {
if i == b.curCursor || !c.CanAutocomplete() {
continue
}

activeWord, _ := b.GetWordCursor(b.GetActiveCursor())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can get activeWord once before the loop, instead of recalculating it on each iteration?

BTW then we don't need to postpone autocompleting the active cursor until after the loop, we can do it before the loop... but only with an extra change like this:

--- a/internal/buffer/autocomplete.go
+++ b/internal/buffer/autocomplete.go
@@ -115,7 +116,7 @@ func (b *Buffer) GetWordCursor(c *Cursor) ([]byte, int) {
 	}
 
 	args := bytes.FieldsFunc(l, util.IsNonWordChar)
-	input := args[len(args)-1]
+	input := bytes.Clone(args[len(args)-1])
 	return input, c.X - util.CharacterCount(input)
 }

Hmm, maybe an extra change like this would be good to apply anyway?

word, _ := b.GetWordCursor(c);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unneeded semicolon (see gofmt)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Damn, this must be automatically found and not every time by you within the review! 😓
-> #4175

if !bytes.Equal(word, activeWord) {
continue
}

b.AutocompleteSingle(c, prevSuggestion)
}

// cycle autocomplete for active cursor
b.AutocompleteSingle(b.GetActiveCursor(), prevSuggestion)

if len(b.Suggestions) > 1 {
b.HasSuggestions = true
}
}

func (b *Buffer) AutocompleteSingle(c *Cursor, prevSuggestion int) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this function to be public? I'm not sure we want to expose this low-level function to plugins...

So maybe name it e.g. just autocomplete() (lowercase)?

start := c.Loc
end := c.Loc

if prevSuggestion < len(b.Suggestions) && prevSuggestion >= 0 {
start = end.Move(-util.CharacterCountInString(b.Completions[prevSuggestion]), b)
}

b.Replace(start, end, b.Completions[b.CurSuggestion])
if len(b.Suggestions) > 1 {
b.HasSuggestions = true
}

func (c *Cursor) CanAutocomplete() bool {
if c.X == 0 {
return false
}

r := c.RuneUnder(c.X)
prev := c.RuneUnder(c.X - 1)
if !util.IsAutocomplete(prev) || util.IsWordChar(r) {
// don't autocomplete if cursor is within a word
return false
}
return true
}

// GetWord gets the most recent word separated by any separator
// GetWordCursor gets the most recent word separated by any separator
// (whitespace, punctuation, any non alphanumeric character)
func (b *Buffer) GetWord() ([]byte, int) {
c := b.GetActiveCursor()
func (b *Buffer) GetWordCursor(c *Cursor) ([]byte, int) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make it a method of Cursor?

l := b.LineBytes(c.Y)
l = util.SliceStart(l, c.X)

Expand All @@ -82,6 +119,10 @@ func (b *Buffer) GetWord() ([]byte, int) {
return input, c.X - util.CharacterCount(input)
}

func (b *Buffer) GetWord() ([]byte, int) {
return b.GetWordCursor(b.GetActiveCursor())
}

// GetArg gets the most recent word (separated by ' ' only)
func (b *Buffer) GetArg() (string, int) {
c := b.GetActiveCursor()
Expand Down Expand Up @@ -153,7 +194,7 @@ func FileComplete(b *Buffer) ([]string, []string) {
// BufferComplete autocompletes based on previous words in the buffer
func BufferComplete(b *Buffer) ([]string, []string) {
c := b.GetActiveCursor()
input, argstart := b.GetWord()
input, argstart := b.GetWordCursor(c)

if argstart == -1 {
return []string{}, []string{}
Expand Down