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

Reline 0.5.0.pre #614

Merged
merged 15 commits into from
Mar 19, 2024
Merged

Reline 0.5.0.pre #614

merged 15 commits into from
Mar 19, 2024

Conversation

tompng
Copy link
Member

@tompng tompng commented Dec 1, 2023

This is a branch to release #595 as 0.5.0.pre.x
See description of #595

Instance variables of cursor and screens

before this pull request
line_editor_before

after this pull request
line_editor_after_screen
line_editor_after_rendered_cache

lib/reline/line_editor.rb Outdated Show resolved Hide resolved
@tompng tompng changed the title [DO NOT MERGE] Reline 0.5.0.pre release Reline 0.5.0.pre Jan 21, 2024
@tompng tompng marked this pull request as ready for review January 21, 2024 05:40
lib/reline.rb Outdated Show resolved Hide resolved
lib/reline/line_editor.rb Show resolved Hide resolved
end

if new_lines != rendered_lines
Reline::IOGate.hide_cursor
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 own learning: when do we need to hide cursor and why? I see there are other operations involving moving cursor around, but this seems to be the only place to hide it?

Copy link
Member Author

Choose a reason for hiding this comment

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

When lines to be rendered changed, reline needs to move cursor and write texts many times. We need to hide cursor because we don't want to show the cursor moving while rendering.
Example of not hiding cursor (Repeating Insert ) and delete)
no_hide

On the other hand, if we always hide cursor in render_differential, cursor sometimes blink when just moving cursor.
Example of pressing arrow keys with hiding cursor
always_hide

We need this for comfortable editing, so we don't have to hide cursor in exceptional case like clear screen with C-l.

Copy link
Member

Choose a reason for hiding this comment

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

Can we have a comment for this?

end

def rerender
handle_cleared
Copy link
Member

Choose a reason for hiding this comment

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

Since this is the only caller of handle_cleared, how about we just move its content here and remove the method?

It also looks like we could be calling render_differential twice under the current implementation?

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 also looks like we could be calling render_differential twice under the current implementation?

Nice catch. I remove render_differential from handle_cleared. Finally when pasting is complete, rerender will be called with @in_pasting == false from reline.rb.

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 want to leave handle_cleared because I want to remove handle_cleared from rerender in the future.
But changing it now needs test_key_actor_emacs.rb that depends on instance variable @cleared to be changed.

Plan and reason

private def ed_clear_screen(key)
  @cleared = true
end

def rerender
  handle_cleared
  render_differential unless @in_pasting
end

private def ed_clear_screen(key)
  @rendered_screen_cache = nil
  Reline::IOGate.clear_screen
  @screen_size = Reline::IOGate.get_screen_size
  @cursor_base_y = 0
  @cursor_y = 0
end
# We need handle_resize(`def resize` is implemented) and handle_interrupt(not implemented, causing deadlock and thread problem) to handle signals correctly
# We don't need handle_cleared. clear is not a signal
def rerender
  render_differential unless @in_pasting
end

lib/reline/line_editor.rb Show resolved Hide resolved
lib/reline/line_editor.rb Outdated Show resolved Hide resolved
lib/reline/line_editor.rb Show resolved Hide resolved
Comment on lines +343 to +345
line_editor.print_nomultiline_prompt(prompt)
line_editor.update_dialogs
Copy link
Member

Choose a reason for hiding this comment

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

Please let me know for study purposes. Why do I need to update dialogs before read_io?

Copy link
Member Author

Choose a reason for hiding this comment

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

Reline has an ability to always show dialog. Reline needs to setup and render dialog on initial render, before first read_io.

irb(main):001> Reline.add_dialog_proc(:FOO,->{Reline::DialogRenderInfo.new contents:['DIALOG'], pos: cursor_pos})
irb(main):002> 
               DIALOG

This is tested in test_dialog_narrower_than_screen and test_dialog_with_fullwidth_scrollbar

lib/reline.rb Show resolved Hide resolved
else
@line = Reline::HISTORY[@history_pointer]
@buffer_of_lines = [Reline::HISTORY[@history_pointer]]
calculate_nearest_cursor(cursor)
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 own learning: Why is it necessary to calculate the cursor position here?

Copy link
Member Author

Choose a reason for hiding this comment

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

When @buffer_of_lines[@line_index] or @buffer_of_lines itself is directly modified, we need set byte_pointer to 0 or line.bytesize, or re-calculate byte_position based on previous cursor position to avoid byte_position being in a middle of grapheme cluster.

For this case, I just found a bug like this which is fixed by this calculate_nearest_cursor.

# prepare history
irb(main):001> "1️⃣"
=> "1️⃣"
irb(main):002> "1
# press PGUP and 2
irb(main):002> "12️⃣"
# press DELETE DELETE
irb(main):002> "(garbled text)"

prompt> 5
prompt> 6 Ruby is...
prompt> 5 Ruby is...
prompt> 6 A dynamic, open source programming
Copy link
Member

Choose a reason for hiding this comment

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

Why is this change 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.

It's because it was testing wrong behavior.

dialog_pos_bug
This bug is fixed in this pull request, so the expected state needs to be updated.


# Calculate cursor position in word wrapped content.
def wrapped_cursor_position
line = ' ' * calculate_width(prompt_list[@line_index], true) + whole_lines[@line_index].byteslice(0, @byte_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 a local variable name of line_before_cursor? I thought line here is not the whole line.

test/reline/test_line_editor.rb Outdated Show resolved Hide resolved
end

if new_lines != rendered_lines
Reline::IOGate.hide_cursor
Copy link
Member

Choose a reason for hiding this comment

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

Can we have a comment for this?

@ima1zumi
Copy link
Member

PROMPT_LIST_CACHE_TIMEOUT in line_editor.rb can also be removed.

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.

Thank you for the excellent refactoring! 🌟
This PR has made the rendering much easier to understand and use.

@ima1zumi ima1zumi merged commit 3fa3762 into master Mar 19, 2024
79 checks passed
@ima1zumi ima1zumi deleted the reline_0.5.0.pre branch March 19, 2024 14:17
matzbot pushed a commit to ruby/ruby that referenced this pull request Mar 19, 2024
(ruby/reline#614)

* Re-architecture LineEditor's internal state and rendering

* Fix test related to LineEditor re-architecture

* Bump to 0.5.0.pre.1

* Hide cursor only when updating screen. Frequent hide&show makes cursor flickering.

* Simplify rerender call from reline.rb

* Simplify handle_cleared

It only need to clear screen. line_editor.rerender will be called later.

* Add description of updating pasting_state inserts continuous_insertion_buffer

* Use meaningful block parameter

Co-authored-by: Stan Lo <[email protected]>

* Fix use of `@cursor_y`

Fix bug updating `@cursor_y`. Do not use `@cursor_y` while updating dialog because it is not current cursor position but cursor position at last rendered time.

* Remove useless instance_variable_set in test

These instance variables are already removed from LineEditor

* Always initialize instance variables to avoid ruby 2.7 warning, remove unused instance variable

* Call update_dialogs from reline.rb before first render

* Combine state representing rendered screen information into `@rendered_screen`

* Rename editor_cursor_ to wrapped_cursor

It represents cursor position of word wrapped whole content

* Remove unused code, tweak, add comment

---------

ruby/reline@3fa376217d

Co-authored-by: Stan Lo <[email protected]>
artur-intech pushed a commit to artur-intech/ruby that referenced this pull request Apr 26, 2024
(ruby/reline#614)

* Re-architecture LineEditor's internal state and rendering

* Fix test related to LineEditor re-architecture

* Bump to 0.5.0.pre.1

* Hide cursor only when updating screen. Frequent hide&show makes cursor flickering.

* Simplify rerender call from reline.rb

* Simplify handle_cleared

It only need to clear screen. line_editor.rerender will be called later.

* Add description of updating pasting_state inserts continuous_insertion_buffer

* Use meaningful block parameter

Co-authored-by: Stan Lo <[email protected]>

* Fix use of `@cursor_y`

Fix bug updating `@cursor_y`. Do not use `@cursor_y` while updating dialog because it is not current cursor position but cursor position at last rendered time.

* Remove useless instance_variable_set in test

These instance variables are already removed from LineEditor

* Always initialize instance variables to avoid ruby 2.7 warning, remove unused instance variable

* Call update_dialogs from reline.rb before first render

* Combine state representing rendered screen information into `@rendered_screen`

* Rename editor_cursor_ to wrapped_cursor

It represents cursor position of word wrapped whole content

* Remove unused code, tweak, add comment

---------

ruby/reline@3fa376217d

Co-authored-by: Stan Lo <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants