-
Notifications
You must be signed in to change notification settings - Fork 83
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
In ed_search_[prev|next]_history, make the cursor come to the end of the line when there is no search substr #714
Changes from 3 commits
baf24bd
e5b0979
7ddd20d
19dae5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,6 +45,8 @@ module CompletionState | |
RenderedScreen = Struct.new(:base_y, :lines, :cursor_y, keyword_init: true) | ||
|
||
CompletionJourneyState = Struct.new(:line_index, :pre, :target, :post, :list, :pointer) | ||
ActionState = Struct.new(:state_type, :state_value) | ||
NullActionState = ActionState.new(nil, nil) | ||
|
||
class MenuInfo | ||
attr_reader :list | ||
|
@@ -1133,6 +1135,9 @@ def input_key(key) | |
else | ||
normal_char(key) | ||
end | ||
|
||
@prev_action_state, @next_action_state = @next_action_state, NullActionState | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add an initialization of these instance variables in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the advice. I have also added initialization of variables at 19dae5b |
||
|
||
unless @completion_occurs | ||
@completion_state = CompletionState::NORMAL | ||
@completion_journey_state = nil | ||
|
@@ -1763,29 +1768,31 @@ def finish | |
end | ||
|
||
private def ed_search_prev_history(key, arg: 1) | ||
substr = current_line.byteslice(0, @byte_pointer) | ||
substr = @prev_action_state == ActionState.new(:search_history, :empty) ? '' : current_line.byteslice(0, @byte_pointer) | ||
return if @history_pointer == 0 | ||
return if @history_pointer.nil? && substr.empty? && !current_line.empty? | ||
|
||
history_range = 0...(@history_pointer || Reline::HISTORY.size) | ||
h_pointer, line_index = search_history(substr, history_range.reverse_each) | ||
return unless h_pointer | ||
move_history(h_pointer, line: line_index || :start, cursor: @byte_pointer) | ||
move_history(h_pointer, line: line_index || :start, cursor: substr.empty? ? :end : @byte_pointer) | ||
arg -= 1 | ||
@next_action_state = substr.empty? ? ActionState.new(:search_history, :empty) : NullActionState | ||
ed_search_prev_history(key, arg: arg) if arg > 0 | ||
end | ||
alias_method :history_search_backward, :ed_search_prev_history | ||
|
||
private def ed_search_next_history(key, arg: 1) | ||
substr = current_line.byteslice(0, @byte_pointer) | ||
substr = @prev_action_state == ActionState.new(:search_history, :empty) ? '' : current_line.byteslice(0, @byte_pointer) | ||
return if @history_pointer.nil? | ||
|
||
history_range = @history_pointer + 1...Reline::HISTORY.size | ||
h_pointer, line_index = search_history(substr, history_range) | ||
return if h_pointer.nil? and not substr.empty? | ||
|
||
move_history(h_pointer, line: line_index || :start, cursor: @byte_pointer) | ||
move_history(h_pointer, line: line_index || :start, cursor: substr.empty? ? :end : @byte_pointer) | ||
arg -= 1 | ||
@next_action_state = substr.empty? ? ActionState.new(:search_history, :empty) : NullActionState | ||
ed_search_next_history(key, arg: arg) if arg > 0 | ||
end | ||
alias_method :history_search_forward, :ed_search_next_history | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1242,14 +1242,22 @@ def test_ed_search_prev_history_with_empty | |
'12345' # new | ||
]) | ||
# The ed_search_prev_history doesn't have default binding | ||
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord) | ||
assert_line_around_cursor('', '12345') | ||
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord) | ||
assert_line_around_cursor('', '12aaa') | ||
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord) | ||
assert_line_around_cursor('', '12356') | ||
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord) | ||
assert_line_around_cursor('', '12356') | ||
input_key_by_symbol(:ed_search_prev_history) | ||
assert_line_around_cursor('12345', '') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part should be I checked GNU Readline.
There is a difference between (A) and (B). Same input, same cursor position, but there is a hidden state. We need to introduce a new state (instance variable) to achieve this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding new instance variable for many One idea is: def input_key
...
# process_key() or normal_char() is called here
...
@prev_action_state, @next_action_state = @next_action_state, nil
end Key action method can read There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for your clear comment. To replicate the behavior of Readline, it is best to include information about the before and after states of the current operation. I will give it a try There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed in 7ddd20d |
||
input_key_by_symbol(:ed_search_prev_history) | ||
assert_line_around_cursor('12aaa', '') | ||
input_key_by_symbol(:ed_search_prev_history) | ||
assert_line_around_cursor('12356', '') | ||
input_key_by_symbol(:ed_search_next_history) | ||
assert_line_around_cursor('12aaa', '') | ||
input_key_by_symbol(:ed_prev_char) | ||
input_key_by_symbol(:ed_next_char) | ||
assert_line_around_cursor('12aaa', '') | ||
input_key_by_symbol(:ed_search_prev_history) | ||
assert_line_around_cursor('12aaa', '') | ||
3.times { input_key_by_symbol(:ed_prev_char) } | ||
input_key_by_symbol(:ed_search_prev_history) | ||
assert_line_around_cursor('12', '356') | ||
end | ||
|
||
def test_ed_search_prev_history_without_match | ||
|
@@ -1291,18 +1299,25 @@ def test_ed_search_next_history_with_empty | |
'12345' # new | ||
]) | ||
# The ed_search_prev_history and ed_search_next_history doesn't have default binding | ||
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord) | ||
assert_line_around_cursor('', '12345') | ||
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord) | ||
assert_line_around_cursor('', '12aaa') | ||
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord) | ||
assert_line_around_cursor('', '12356') | ||
@line_editor.__send__(:ed_search_next_history, "\C-n".ord) | ||
assert_line_around_cursor('', '12aaa') | ||
@line_editor.__send__(:ed_search_next_history, "\C-n".ord) | ||
assert_line_around_cursor('', '12345') | ||
@line_editor.__send__(:ed_search_next_history, "\C-n".ord) | ||
assert_line_around_cursor('', '') | ||
input_key_by_symbol(:ed_search_prev_history) | ||
assert_line_around_cursor('12345', '') | ||
input_key_by_symbol(:ed_search_prev_history) | ||
assert_line_around_cursor('12aaa', '') | ||
input_key_by_symbol(:ed_search_prev_history) | ||
assert_line_around_cursor('12356', '') | ||
input_key_by_symbol(:ed_search_next_history) | ||
assert_line_around_cursor('12aaa', '') | ||
input_key_by_symbol(:ed_search_next_history) | ||
assert_line_around_cursor('12345', '') | ||
input_key_by_symbol(:ed_search_prev_history) | ||
assert_line_around_cursor('12aaa', '') | ||
input_key_by_symbol(:ed_prev_char) | ||
input_key_by_symbol(:ed_next_char) | ||
input_key_by_symbol(:ed_search_next_history) | ||
assert_line_around_cursor('12aaa', '') | ||
3.times { input_key_by_symbol(:ed_prev_char) } | ||
input_key_by_symbol(:ed_search_next_history) | ||
assert_line_around_cursor('12', '345') | ||
end | ||
|
||
def test_incremental_search_history_cancel_by_symbol_key | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have used a Struct to make the contents of action_state more understandable, but using a simple Array would also be good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In
ed_search_prev_history
anded_search_next_history
,@prev_action_state == ActionState.new(:search_history, :empty)
is working great 👍In general, I think this kind of ideom will appear frequently.
To make it easy to use, can you add a helper method like this?
Using it, we can just use a simple array internally. It will reduce total struct/class count (complexity).
And we don't need to think of NullActionState wrongly modified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! I tried to represent the state in a 2-element array!
19dae5b