Skip to content

Conversation

@trevarj
Copy link
Contributor

@trevarj trevarj commented May 20, 2020

Closes #101

See #183 for conversation history.

@osa1
Copy link
Owner

osa1 commented May 20, 2020

Thanks for rebasing your branch! Let me know why you've fixed the bugs and this is ready for review.

@trevarj
Copy link
Contributor Author

trevarj commented May 21, 2020

Let me know why you've fixed the bugs

I think I got confused somewhere in the rebase and picked the wrong change. It was a little confusing because I did a few merges from master earlier (mistake).

Just want to mention that this change does include a little tweak to indent the message lines and align them with the nickname. It's easily removable. I'm not quite sure I personally like it yet, but maybe it's ok.

Also there was a bug you mentioned on the last PR, where someone had posted a long link and it moved it to a new line. I tried this out on master and it happens there. The cause is because we split on the space after the ": " so it moves the whole link to the next line. It doesn't like when the user sends text with no whitespace either. It gets cut off. I solved this in InputLine by adding a fallback when there are no whitespaces, so it just wraps anyway.

@trevarj trevarj marked this pull request as ready for review May 21, 2020 06:59
@osa1
Copy link
Owner

osa1 commented May 21, 2020

Great, thanks. Started reading an testing. One bug is in some cases it crashes with this backtrace (you need a debug build):

Backtrace
thread 'main' panicked at 'index out of bounds: the len is 507 but the index is 507', termbox/src/lib.rs:344:18
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.46/src/backtrace/libunwind.rs:86
   1: backtrace::backtrace::trace_unsynchronized
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.46/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print_fmt
             at src/libstd/sys_common/backtrace.rs:78
   3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
             at src/libstd/sys_common/backtrace.rs:59
   4: core::fmt::write
             at src/libcore/fmt/mod.rs:1069
   5: std::io::Write::write_fmt
             at src/libstd/io/mod.rs:1537
   6: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:62
   7: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:49
   8: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:198
   9: std::panicking::default_hook
             at src/libstd/panicking.rs:218
  10: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:477
  11: rust_begin_unwind
             at src/libstd/panicking.rs:385
  12: core::panicking::panic_fmt
             at src/libcore/panicking.rs:89
  13: core::panicking::panic_bounds_check
             at src/libcore/panicking.rs:65
  14: termbox_simple::Termbox::change_cell
             at termbox/src/lib.rs:344
  15: libtiny_tui::input_area::input_line::draw_line_wrapped
             at libtiny_tui/src/input_area/input_line.rs:252
  16: libtiny_tui::input_area::input_line::draw_line
             at libtiny_tui/src/input_area/input_line.rs:419
  17: libtiny_tui::input_area::TextField::draw
             at libtiny_tui/src/input_area/mod.rs:170
  18: libtiny_tui::messaging::MessagingUI::draw
             at libtiny_tui/src/messaging.rs:120
  19: libtiny_tui::tui::TUI::draw
             at libtiny_tui/src/tui.rs:793
  20: libtiny_tui::input_handler::{{closure}}
             at libtiny_tui/src/lib.rs:149
  21: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
             at /home/omer/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/future/mod.rs:69
  22: tokio::runtime::task::core::Core<T,S>::poll::{{closure}}
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/task/core.rs:173
  23: tokio::loom::std::unsafe_cell::UnsafeCell<T>::with_mut
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/loom/std/unsafe_cell.rs:14
  24: tokio::runtime::task::core::Core<T,S>::poll
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/task/core.rs:158
  25: tokio::runtime::task::harness::Harness<T,S>::poll::{{closure}}
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/task/harness.rs:107
  26: core::ops::function::FnOnce::call_once
             at /home/omer/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/ops/function.rs:232
  27: <std::panic::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
             at /home/omer/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panic.rs:318
  28: std::panicking::try::do_call
             at /home/omer/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:297
  29: __rust_try
  30: std::panicking::try
             at /home/omer/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:274
  31: std::panic::catch_unwind
             at /home/omer/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panic.rs:394
  32: tokio::runtime::task::harness::Harness<T,S>::poll
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/task/harness.rs:89
  33: tokio::runtime::task::raw::poll
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/task/raw.rs:104
  34: tokio::runtime::task::raw::RawTask::poll
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/task/raw.rs:66
  35: tokio::runtime::task::Notified<S>::run
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/task/mod.rs:169
  36: tokio::task::local::LocalSet::tick::{{closure}}
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/task/local.rs:406
  37: tokio::coop::with_budget::{{closure}}
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/coop.rs:127
  38: std::thread::local::LocalKey<T>::try_with
             at /home/omer/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/thread/local.rs:263
  39: std::thread::local::LocalKey<T>::with
             at /home/omer/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/thread/local.rs:239
  40: tokio::coop::with_budget
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/coop.rs:120
  41: tokio::coop::budget
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/coop.rs:96
  42: tokio::task::local::LocalSet::tick
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/task/local.rs:406
  43: <tokio::task::local::RunUntil<T> as core::future::future::Future>::poll::{{closure}}
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/task/local.rs:530
  44: tokio::macros::scoped_tls::ScopedKey<T>::set
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/macros/scoped_tls.rs:63
  45: tokio::task::local::LocalSet::with
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/task/local.rs:440
  46: <tokio::task::local::RunUntil<T> as core::future::future::Future>::poll
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/task/local.rs:516
  47: tokio::task::local::LocalSet::run_until::{{closure}}
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/task/local.rs:390
  48: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
             at /home/omer/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/future/mod.rs:69
  49: tokio::runtime::basic_scheduler::BasicScheduler<P>::block_on::{{closure}}::{{closure}}
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/basic_scheduler.rs:131
  50: tokio::coop::with_budget::{{closure}}
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/coop.rs:127
  51: std::thread::local::LocalKey<T>::try_with
             at /home/omer/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/thread/local.rs:263
  52: std::thread::local::LocalKey<T>::with
             at /home/omer/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/thread/local.rs:239
  53: tokio::coop::with_budget
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/coop.rs:120
  54: tokio::coop::budget
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/coop.rs:96
  55: tokio::runtime::basic_scheduler::BasicScheduler<P>::block_on::{{closure}}
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/basic_scheduler.rs:131
  56: tokio::runtime::basic_scheduler::enter::{{closure}}
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/basic_scheduler.rs:213
  57: tokio::macros::scoped_tls::ScopedKey<T>::set
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/macros/scoped_tls.rs:63
  58: tokio::runtime::basic_scheduler::enter
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/basic_scheduler.rs:213
  59: tokio::runtime::basic_scheduler::BasicScheduler<P>::block_on
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/basic_scheduler.rs:123
  60: tokio::runtime::Runtime::block_on::{{closure}}
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/mod.rs:444
  61: tokio::runtime::context::enter
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/context.rs:72
  62: tokio::runtime::handle::Handle::enter
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/handle.rs:76
  63: tokio::runtime::Runtime::block_on
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/mod.rs:441
  64: tokio::task::local::LocalSet::block_on
             at /home/omer/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/task/local.rs:351
  65: tiny::run
             at tiny/src/main.rs:89
  66: tiny::main
             at tiny/src/main.rs:64
  67: std::rt::lang_start::{{closure}}
             at /home/omer/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67
  68: std::rt::lang_start_internal::{{closure}}
             at src/libstd/rt.rs:52
  69: std::panicking::try::do_call
             at src/libstd/panicking.rs:297
  70: std::panicking::try
             at src/libstd/panicking.rs:274
  71: std::panic::catch_unwind
             at src/libstd/panic.rs:394
  72: std::rt::lang_start_internal
             at src/libstd/rt.rs:51
  73: std::rt::lang_start
             at /home/omer/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67
  74: main
  75: __libc_start_main
  76: _start
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Not sure how to best reproduce this, I just made the screen small and edited randomly until it crashed like this.

@trevarj
Copy link
Contributor Author

trevarj commented May 21, 2020

Ok, thanks, I will take a look. I probably missed a place where we need to invalidate the TexTArea's height on edit/history/autoocomplete

Copy link
Owner

@osa1 osa1 left a comment

Choose a reason for hiding this comment

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

Thanks @trevarj , this looks great now! Added a few inline comments. Two high-level comments:

  • Could you hide aligning messages in the message area behind a flag? It's fine to keep it but it shouldn't be the default. (Note to self: review that code)
  • Could you document when we fall back to wrapping exactly?

I also think we should make this the default and remove text_field_wrap setting. What do you think?

/// Get the scroll value based on width of TextArea
/// If the width is less than the width defined on SCROLL_FALLBACK_WIDTH
/// we will transfer to scroll mode, otherwise we will continue to wrap text.
fn get_scroll_for_resize(&mut self) -> Option<i32> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would be nice to have a way to fall back to scrolling when the TextField height is greater than, say, half of the entire window height, but I don't think it would be a clean implementation. On resize, we invalidate the height and then calculate it later while drawing.

I think this is a decent way for now, unless someone uses a really wide and short window 😆

@trevarj
Copy link
Contributor Author

trevarj commented May 21, 2020

Could you hide aligning messages in the message area behind a flag? It's fine to keep it but it shouldn't be the default. (Note to self: review that code)

I removed this for now. I think it's too hacky and not a really nice solution right now. I think #169 can address this.

I also think we should make this the default and remove text_field_wrap setting. What do you think?

I agree. Done.

@osa1
Copy link
Owner

osa1 commented May 23, 2020

I've been testing this -- so far if I only edit when the cursor is at the end everything works fine, I couldn't find any bugs. But when I edit in the middle things get weird. Here's an example:

Screenshot_2020-05-23_23-40-11

Note that some part of the text field is written to the tab line. I can even move my cursor there and edit it:

Screenshot_2020-05-23_23-40-28

@trevarj
Copy link
Contributor Author

trevarj commented May 24, 2020

@osa1 thanks for testing. I can reproduce this, but only when there's a full line with no whitespace at all. I will have to add some extra checks on calculate_lines

@trevarj
Copy link
Contributor Author

trevarj commented May 24, 2020

Not really happy with the fix that I had to do to get this working 👀, but it seems ok. Maybe you will have a better idea than I could come up with.

We will have to address a similar issue with MsgArea as well - like in that screenshot where someone sent a multi-line link and it didn't render correctly.

@osa1
Copy link
Owner

osa1 commented May 25, 2020

Thanks @trevarj , it seems much better. I still find bugs though. One example:

Screenshot_2020-05-26_00-01-42

Now if I resize the screen

Screenshot_2020-05-26_00-04-24

Note that there's more than enough space in the first line for the next word, which is just one characters, but the character is still on the next line.

I wonder if we could simplify the implementation, maybe at the cost of some performance, to make this easier to implement and maintain. I haven't tried to implement this myself, but it seems quite tricky.

@trevarj
Copy link
Contributor Author

trevarj commented May 26, 2020

Yeah, this is demoralizing. I would imagine that other programs that do this are probably taking a bigger performance hit in order to make things more robust.

If you can show me in more detail how to reproduce this one bug, maybe I can fix it. If there are a lot more than just this one than it's probably better to think of another solution...

@osa1
Copy link
Owner

osa1 commented May 26, 2020

I would imagine that other programs that do this are probably taking a bigger performance hit in order to make things more robust.

Let's take this approach. Thinking about this more, I think it's safe to assume that text field will be edited very rarely (most of the time tiny will be rendering incoming messages and waiting idle). Resizes are also rare, and resizing while text field is not empty should be even more rare. So assuming the common case (when the text field is empty) is fast when resizing and rendering (which should be the case, it's trivial to render and resize an empty text field) I think performance hits here will be acceptable.

@osa1
Copy link
Owner

osa1 commented May 26, 2020

If you can show me in more detail how to reproduce this one bug

Basically I'm adding some long text, then removing stuff in the middle, and resize every once in a while, and at each step (after every key press basically) I look and check if it looks as expected.

@trevarj
Copy link
Contributor Author

trevarj commented May 26, 2020

Ok 😅. I think that the refactoring into the separate module is good and should stay. I will start thinking of how to make this simpler. If you come up with any sleek ideas, let me know!

@trevarj
Copy link
Contributor Author

trevarj commented May 28, 2020

Tried to keep things simple and comment everything so it all makes sense. I'm glad to have removed the BTree stuff, which was nasty looking.
This way is the most inefficient, but I think it's probably the easiest to read/understand. I also think my brain is tired of thinking about ways to word-wrap text😆

@trevarj
Copy link
Contributor Author

trevarj commented May 29, 2020

Working on adding a limit of lines. I'd hate to add the magic number "5" maximum lines again. Would rather it be half of the window height.

@osa1
Copy link
Owner

osa1 commented May 29, 2020

Yeah I think half of the window height is a good heuristic. If it'll make things simpler we could use a magic number, but even if we did that at some point we'd have to check window height anyway, for example if the magic number is "5" but window height is "6", even though we have enough space we should fall back to scrolling to give space to other widgets. So I think hard-coded magic number will not make this any more simpler. "No more than half of the window height" handles that nicely. If the ratio (1/2) is not great we could tweak it later easily if you could make it a parameter (just define it as a constant in the module).

) {
let slice: &[char] = &line[scroll as usize..min(line.len(), (scroll + width + pos_x) as usize)];
let slice: &[char] =
&line[scroll as usize..min(line.len(), (scroll + (width - pos_x)) as usize)];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed a bug that I introduced

height: Option<i32>,

/// Maximum lines the widget can grow to
max_lines: i32,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Gets calculated on resize(), sent from MessagingUI

/// Gets the height of the widget
/// If the height is larger than the allowed max lines turn on scroll
/// else turn off scroll
pub(crate) fn get_height(&mut self, width: i32) -> i32 {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it makes the most sense to add the fallback code here. It abstracts it away from the draw() and resize() of MessagingUI.

pub(crate) fn resize(&mut self, width: i32, max_lines: i32) {
self.width = width;
self.height = None;
self.scroll = self.get_scroll_for_resize();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Setting scroll is moved to get_height().

@osa1
Copy link
Owner

osa1 commented Jun 1, 2020

Hey @trevarj -- great work, this works really well now! I've been using and testing recently, and haven't found any problems so far. I'll review the code soon. Thanks!

@osa1
Copy link
Owner

osa1 commented Jun 6, 2020

Hey @trevarj -- I took another look at the code, and I've been also using it lately, it looks and works great! If you could squash your commits and rebase I'm going to merge this now.

Closes osa1#101

Update tests:

- Improve error reporting on test failure
- Use smaller screen in text_field_wrap tests, document screen size
- Combine multiple text_field_wrap tests in single test
  (not complete)

Fix a few clippy warnings

tui: Implement methods for testing resizing

Allow servers to have aliases. Fixes osa1#186.

Remove outdated contributors list from README

We don't update it, and Github maintains an up-to-date list for us
anyway (in 'contributors' page).

Test resizing

Fixed test after rebase with master

Added word wrapping on text field input

Fix for line calculation.

Added indentation for message lines

Fixed bug with Line::rendered_height not calculating based on timestamp and nick length

Implement wrapping in text field

Closes osa1#101

Update tests:

- Improve error reporting on test failure
- Use smaller screen in text_field_wrap tests, document screen size
- Combine multiple text_field_wrap tests in single test
  (not complete)

Test resizing

Formatting

Fixed test after rebase with master

Added word wrapping on text field input

Fix for line calculation.

Added indentation for message lines

Fixed bug with Line::rendered_height not calculating based on timestamp and nick length

- moved text_field to own module - input_area
- started making TextArea work like MsgArea, new InputLine like Line

Working more on wrapping...autocomplete still buggy

buffer remove fix.

Progress on wrapping, added some tests - need more

Autocompletion - hacky solution

Not resizing msg_area every draw - only on height change

Caching textarea height so that we only need to calculate lines on modify() or resize()

Fixes after my bad rebase

Fixed nickname not showing on tiny window.

Fixed PR feedback:
- Nickname suffix is now static
- removed pub from some functions

Fixes for PR:
- Removes text_field_wrap config. Defaulting to text wrapping, falling
back to scroll when width is too small.
- Reverted indenting for message area.

Fixed issue with calculate_lines() where it would not correctly calculate when there were multi-line words.

Another rework. Exhaustive wrapping algorithm. Removed keeping track of white spaces. Added keeping track of line starts for easy wrapped drawing. No more crazy BTree insert/remove code.

Added fallback to scroll after input area is greater than half of the
screen height.
@trevarj trevarj force-pushed the text-wrap-refactor branch from 69867ac to dfd080d Compare June 6, 2020 20:19
@trevarj
Copy link
Contributor Author

trevarj commented Jun 6, 2020

@osa1 Hey, sorry for not seeing your message sooner. I rebased from master, squashed, and forced a push. Not sure why the commit message is so long...it should have only been one line.

Thanks!

@osa1 osa1 merged commit 85e6ce5 into osa1:master Jun 7, 2020
@osa1
Copy link
Owner

osa1 commented Jun 7, 2020

Thanks!

osa1 added a commit that referenced this pull request Jun 7, 2020
@trevarj trevarj deleted the text-wrap-refactor branch June 7, 2020 09:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TUI feature request: grow text field vertically instead of scrolling

2 participants