Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor completion #647

Merged
merged 2 commits into from
Apr 1, 2024
Merged

Conversation

tompng
Copy link
Member

@tompng tompng commented Mar 19, 2024

Fixes #611 and #621

Split logic and state of autocompletion and tab completion

Before

Reline has tab complete and autocomplete.
Tab complete uses

@completion_state = NORMAL | COMPLETION | MENU | MENU_WITH_PERFECT_MATCH | PERFECT_MATCH

Autocomplete uses

@completion_state = NORMAL | JOURNEY
@completion_journey_data
retrieve_completion_block(not stored to ivar, calling every time, causing dialog disappearing bug)

It's a separate feature but uses same instance variable @completion_state that making hard to fix some bugs.

After

Tab complete uses

@completion_state = NORMAL | COMPLETION | MENU | MENU_WITH_PERFECT_MATCH | PERFECT_MATCH

Autocomplete uses

@completion_journey_state = CompletionJourneyState.new(line_index, pre, target, post, list, pointer) | nil

State is separated. Logic is also separated. Now we can remove redundant proc calls and fix #611 #621

completion_journey_data

completion_journey_data is an API for dialog proc, defined in DialogProcScope. We need to keep compatibility.
It was an instance variable but changed to a method that calculates from @completion_journey_state.

DEFAULT_DIALOG_PROC_AUTOCOMPLETE

Move completion candidate list up logic to LineEditor
All completion logic is in LineEditor. DEFAULT_DIALOG_PROC_AUTOCOMPLETE only needs to create dialog content from completion_journey_data

else
complete(result)
if !@config.disable_completion
process_insert(force: true)
Copy link
Member

Choose a reason for hiding this comment

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

For my learning: Why is force: true needed?

Copy link
Member Author

Choose a reason for hiding this comment

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

If you type "1." and then paste "0.\t\t" to the terminal, "0." is inserted to @continuous_insertion_buffer (because in_pasting? is true).
Without this process_insert(force: true), 0. part will be ignored.

irb(main):001> 1.floor
               1.abs
               1.floor[selected]
               1.ceil

We need to force insert "0." into the buffer before calculating completion list.

irb(main):001> 1.0.ceil
               1.0.floor
               1.0.ceil[selected]
               1.0.round

Copy link
Member

Choose a reason for hiding this comment

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

Thank you for your information!
But I could not paste "0./t/t" in my environment. Can you paste it?

I got this:

irb(main):001> 1.__id__
               1.__send__[selected]
               1.equal?

Copy link
Member Author

Choose a reason for hiding this comment

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

It was broken in the last commit...

Fixed and I added test for it

list = call_completion_proc
return false unless list.is_a?(Array)

candidates = list.select{ |item| item.start_with?(target) }
Copy link
Member

Choose a reason for hiding this comment

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

For my learning: I'm not sure why select is needed. Is it possible to create a list that does not start with target?

Copy link
Member Author

@tompng tompng Mar 25, 2024

Choose a reason for hiding this comment

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

Is it possible to create a list that does not start with target?

Yes, it's possible by this code

require 'reline'
Reline.autocompletion = true
Reline.completion_proc = ->*{['abc','def','ghi','jkl']}
p Reline.readmultiline('>'){true}

I'll remove this select because older reline does not reject candidates that does not start with target.

> x
  abc
  def
  ghi
  jkl

Copy link
Member Author

Choose a reason for hiding this comment

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

The old code was doing this select in L886

private def move_completed_list(list, direction)
  ...
  @completion_journey_data = CompletionJourneyData.new(
    preposing, postposing,
    [target] + list.select{ |item| item.start_with?(target) }, 0)

The old behavior (renders candidates that does not start from target, but disappears after pressing tab) seems buggy.

@ima1zumi ima1zumi added the bug Something isn't working label Mar 25, 2024
@tompng tompng force-pushed the autocompletion_jouney_refactor branch from 0666479 to ebeb56e Compare March 30, 2024 17:15
@@ -537,6 +536,8 @@ def rerender
end

class DialogProcScope
CompletionJourneyData = Struct.new(:preposing, :postposing, :list, :pointer)
Copy link
Member

Choose a reason for hiding this comment

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

How about separating list[0] from list?
CompletionJourneyData only uses by completion_journey_data. I don't think it constitutes a breaking change. By separating this, there will be no need to drop it at the caller side.
Moving a constant may also be considered a breaking change, but it doesn't seem to be used by anyone.

https://github.com/search?q=CompletionJourneyData+language%3ARuby+path%3A**%2F*.rb+NOT+is%3Afork&type=code

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it's reasonable. It will make DEFAULT_DIALOG_PROC_AUTOCOMPLETE simpler.
However, I think removing list[0] will make completion_journey_data not a journey_data thing.
completion-journey includes target as pointer=0. Renaming it to something like completion_list_for_completion_dialog will break IRB so this is not possible now.

Copy link
Member

Choose a reason for hiding this comment

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

Thank you for the explanation. I understand.

@tompng tompng force-pushed the autocompletion_jouney_refactor branch from ebeb56e to 4c645a4 Compare April 1, 2024 16:53
@tompng tompng force-pushed the autocompletion_jouney_refactor branch from 4c645a4 to 0ea822a Compare April 1, 2024 17:15
Copy link
Member

@ima1zumi ima1zumi left a comment

Choose a reason for hiding this comment

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

LGTM!

@ima1zumi ima1zumi merged commit c3c09ac into ruby:master Apr 1, 2024
40 checks passed
matzbot pushed a commit to ruby/ruby that referenced this pull request Apr 1, 2024
(ruby/reline#647)

* Refactor completion: split autocompletion and tabcompletion logic and state

* Move completion candidate listup logic from dialog proc to LineEditor

ruby/reline@c3c09ac9c2
@tompng tompng deleted the autocompletion_jouney_refactor branch April 2, 2024 03:17
artur-intech pushed a commit to artur-intech/ruby that referenced this pull request Apr 26, 2024
(ruby/reline#647)

* Refactor completion: split autocompletion and tabcompletion logic and state

* Move completion candidate listup logic from dialog proc to LineEditor

ruby/reline@c3c09ac9c2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Development

Successfully merging this pull request may close these issues.

Reline calls completion_proc too many times
2 participants