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

feat(buffer): add Buffer::get_opt, get_mut_opt and index implementations #1084

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

joshka
Copy link
Member

@joshka joshka commented May 3, 2024

Code which previously called buf.get(x, y) or buf.get_mut(x, y) can
now use index operators, or be transitioned to buf_get_opt or
buf.get_mut_opt for safe access that avoids panics.

The new methods accept Into<Position> instead of x and y
coordinates, which makes them more ergonomic to use.

let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 10));

let cell = buf[(0, 0)];
let cell = buf[Position::new(0, 0)];

let symbol = buf.get_opt((0, 0)).map(|cell| cell.symbol());
let symbol = buf.get_opt(Position::new(0, 0)).map(|cell| cell.symbol());

buf[(0, 0)].set_symbol("🐀");
buf[Position::new(0, 0)].set_symbol("🐀");

buf.get_mut_opt((0, 0)).map(|cell| cell.set_symbol("🐀"));
buf.get_mut_opt(Position::new(0, 0)).map(|cell| cell.set_symbol("🐀"));

Partially addresses #1011

Code which previously called `buf.get(x, y)` or `buf.get_mut(x, y)` can
now use index operators, or be transitioned to `buf_get_opt` or
`buf.get_mut_opt` for safe access that avoids panics.

The new methods accept `Into<Position>` instead of `x` and `y`
coordinates, which makes them more ergonomic to use.

```rust
let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 10));

let cell = buf[(0, 0)];
let cell = buf[Position::new(0, 0)];

let symbol = buf.get_opt((0, 0)).map(|cell| cell.symbol());
let symbol = buf.get_opt(Position::new(0, 0)).map(|cell| cell.symbol());

buf[(0, 0)].set_symbol("🐀");
buf[Position::new(0, 0)].set_symbol("🐀");

buf.get_mut_opt((0, 0)).map(|cell| cell.set_symbol("🐀"));
buf.get_mut_opt(Position::new(0, 0)).map(|cell| cell.set_symbol("🐀"));
```
Copy link

codecov bot commented May 3, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 94.2%. Comparing base (bf20369) to head (a7dbc24).

Additional details and impacted files
@@           Coverage Diff           @@
##            main   #1084     +/-   ##
=======================================
- Coverage   94.3%   94.2%   -0.1%     
=======================================
  Files         61      61             
  Lines      14771   14826     +55     
=======================================
+ Hits       13930   13977     +47     
- Misses       841     849      +8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@EdJoPaTo EdJoPaTo left a comment

Choose a reason for hiding this comment

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

Is there a reason why this was not done with pos_of too?

#1049:

pub const fn pos_of_opt(&self, i: usize) -> Option<(u16, u16)> {
if i < self.area.area() as usize {
Some((
self.area.x + (i as u16) % self.area.width,
self.area.y + (i as u16) / self.area.width,
))
} else {
None
}
}

@@ -370,6 +449,52 @@ impl Buffer {
}
}

impl<P: Into<Position>> Index<P> for Buffer {
Copy link
Member

Choose a reason for hiding this comment

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

Increase readability by clarifying what exactly is implemented here inline. When first reading this I thought there is yet another ratatui trait. (same for IndexMut)

Suggested change
impl<P: Into<Position>> Index<P> for Buffer {
impl<P: Into<Position>> std::ops::Index<P> for Buffer {

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 changed this back to just Index as the trait has to be in scope for links to a trait defined method in a doc comment to resolve correctly.

src/buffer/buffer.rs Show resolved Hide resolved
src/buffer/buffer.rs Show resolved Hide resolved
src/buffer/buffer.rs Show resolved Hide resolved
@joshka
Copy link
Member Author

joshka commented May 3, 2024

Is there a reason why this was not done with pos_of too?

I don't think pos_of should be part of the API, and I'd prefer not to make it worse. (note the new index_of_opt method is private)

src/buffer/buffer.rs Outdated Show resolved Hide resolved
@kdheepak
Copy link
Collaborator

This looks good to me but maybe we can get more reviewers on this?

PR title + description should be updated.

Copy link
Member

@EdJoPaTo EdJoPaTo left a comment

Choose a reason for hiding this comment

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

I like the usage of the Index trait. It is still panicking like before but should have the same performance.

src/buffer/buffer.rs Outdated Show resolved Hide resolved
///
/// Returns `None` if the index is out of bounds.
///
/// Note that unlike `get`, this method accepts a `Position` instead of `x` and `y` coordinates.
Copy link
Member

Choose a reason for hiding this comment

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

Not sure whether I would keep this comment… either Index is used or cell. The latter has easy viewable documentation, so this comment doesn't provide much use. It would be more useful on using the index but documenting it is harder there 🤔

Copy link
Member Author

Choose a reason for hiding this comment

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

Replacing this with some better docs in the next commit.

Copy link
Member Author

Choose a reason for hiding this comment

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

/// Returns `None` if the given coordinates are outside of the Buffer's area.
///
/// Note that this is private because of <https://github.com/ratatui-org/ratatui/issues/1122>
const fn index_of_opt(&self, x: u16, y: u16) -> Option<usize> {
Copy link
Member

Choose a reason for hiding this comment

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

This and other methods here could get a #[must_use].

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

area and content also look like must_use candidates… Guess we should get that lint enabled for the full repo

Co-authored-by: EdJoPaTo <rfc-conform-git-commit-email@funny-long-domain-label-everyone-hates-as-it-is-too-long.edjopato.de>
@joshka
Copy link
Member Author

joshka commented May 22, 2024

I actually intended to put the deprecation of the existing methods in #1123, but accidentally pushed the commit to this PR. Happy to do it either way (though it would be easier at this point to just keep it in this PR and update the main PR comment.

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.

None yet

3 participants