- Nov 21, 2024
-
-
Simon Tatham authored
Bump version number! See merge request !34
-
Simon Tatham authored
This version of the code has been pretty stable for a while, so I think it's time to publish it to crates.io.
-
- Oct 18, 2024
-
-
Simon Tatham authored
Another rewrite of the File screen layout code. See merge request !33
-
Simon Tatham authored
This brings a new feature: we can set 'white-space: pre-wrap' as a CSS attribute, preserving details of whitespace (multiple spaces, spaces at start of paragraph) while still wrapping words. The web Mastodon client sets this by default on <p> elements, and I think we should too, because it means indented displays such as code snippets are displayed properly. (Particularly important in Python, where the whitespace is semantic! E.g. https://mastodon.social/@glyph/112628092787725236 rendered illegibly before this change, and now you can see what the code is supposed to be doing.) 0.13.x also includes an API change making the text_renderer module private. But that's OK, we can still import all the things we needed from the parent module. The net effect is to save us 15 characters :-)
-
- Oct 14, 2024
-
-
Simon Tatham authored
When you press [/] or [\] to search in a file, the bottom-line editor prompt says "(blank = last)", meaning that if you enter the empty string at the prompt, you're supposed to search for the same pattern as last time (but perhaps in the opposite direction). I copied the idea and the prompt text from Monochrome, but forgot to make it actually work! Only noticed while I was re-testing everything in File after the screen layout rewrite.
-
Simon Tatham authored
When you request the thread of a toot, you want to see its ancestors and its descendants. So I think better than putting it at the top of the screen (as we were previously doing) is to put it in the middle, so you can see that there are indeed things further up and down.
-
Simon Tatham authored
The problem with the previous rewrite in commit 8cbc9e12 was that now FilePosition _only_ had the ability to specify a particular position within an item and say 'align this position to the bottom of the screen'. Unfortunately, sometimes you want to align a toot to the _top_ of the screen (or further down if the end of the file doesn't permit): before the rewrite we were doing that to your next unread mention, and also to the toot you just asked to see the thread of. But after the rewrite, those toots end up just off the bottom of the screen, which isn't helpful! To fix that, I've rewritten FilePosition _again_, so that it's now a combination of an item number and a sub-enum 'Placement', and there's a match statement that allows each branch of Placement to cause a completely different calculation about where things appear on the screen. So 'put this toot at the top' and 'put the n/d point in this toot at the bottom' use different Placement branches, and are alternatives to each other. The calculation in question lives in another new type ScreenLayout, which gives the _concrete_ answer to 'what things are at the top/bottom of the screen?', as opposed to this new very abstract FilePosition. Translating one to the other is done by File::make_screen_layout, which is now sophisticated enough to replace _several_ old functions like ensure_enough_rendered and fix_overshoot_at_top. So although the code overall has become larger (ScreenLayout's constructor is complicated and also has a pile of unit tests), File is simpler, and I think that's a win. Another win is that you can add further branches of FilePosition, and in fact I plan to do one of those in the next commit.
-
- Sep 27, 2024
-
-
Simon Tatham authored
Make FilePosition store a fraction, not a line number. See merge request !32
-
Simon Tatham authored
This fixes two bugs at once: instability under repeated window resizing, and a panic due to an out-of-bounds array access attempt. The panic: if a post on my home timeline is edited to be slightly shorter, and the file position was right at the bottom of its original size, then Mastodonochrome panics in File::draw(), with a backtrace suggesting that a bounds check failed because of the line number in FilePosition being out of range. I'm sure I could fix that by itself, by inserting a missing call to clip_pos_within_item() somewhere or other. But I had a better idea, which fixes another bug at the same time. The resize instability: the old FilePosition had the semantics "As long as the post we're referring to was last rendered at width W, we're at line L within it." If the width changes, we throw up our hands and say "in that case I don't know where we were within the post, better reset to the bottom". So if you resize the window back and forth, your previous file position isn't restored. This is only rarely inconvenient for me, but some other desktop environments will be much harder hit: in particular, tiling window managers often resize windows a great deal without much deliberate user action. To fix both problems at once, I've reworked FilePosition so that instead of a width and a line number, it stores a fraction, as a usize numerator and denominator, with the semantics "We're currently N/D of the way through this item". This is easy to interpret for the current line count of the item, and it doesn't need to be mutated on a window resize at all. Every time you use a movement key, the fraction is reset to be one based on the current line count of the item, but as long as you don't logically move within the file, the previous line count is retained. So we should have the required stability property: if you resize the window and then resize it back again, your file position will be exactly where it was before. And now clip_pos_within_item() doesn't even exist, so there's no need to worry about whether _any_ piece of code should be calling it or not. File positions now automatically avoid going beyond the bounds of the containing item, because they always represent a rational in the interval [0,1].
-
- Sep 22, 2024
-
-
Simon Tatham authored
Made a start on documenting the code. See merge request !31
-
Simon Tatham authored
It was always meant to be: that's how Monochrome's search works. I just called `Regex::new` without thinking, and haven't got round to fixing it until now.
-
Simon Tatham authored
One of the advantages of writing all those doc comments is that I noticed which parts of the internal APIs were embarrassing to write down, so now I have some thoughts about what needs fixing.
-
Simon Tatham authored
There wasn't enough interesting to say about it in its doc comment to make me think it really _needed_ to be a separate function. Now it appears in the context where the stuff actually happens, I think that's clearer.
-
Simon Tatham authored
This seems less important for an application than a library - it's _only_ useful to developers of Mastodonochrome - but still, it's practice using rustdoc, and also, I've been neglecting this project for a while and reminding myself of how it all works enough to document it is a good way to refresh my memory. I've documented three modules in full: one self-contained library (coloured_string.rs), one moderately important subpart of the UI design (activity_stack.rs, including explaining what all the activity enum values mean), and one absolutely central part of the architecture (tui.rs).
-
- Sep 21, 2024
-
-
Simon Tatham authored
cargo update, and update html2text See merge request !30
-
Simon Tatham authored
It's started complaining about assignments 'foo = bar.clone()' or 'foo = bar.to_owned()', preferring 'foo.clone_from(bar)' and 'bar.clone_into(foo)' respectively. Makes sense in general, because you can imagine the target object e.g. already having an allocated subthing which it can reuse rather than freeing it and making a new one. Not sure about the aesthetics in the case of "some string literal".clone_into(&mut some_String), but I suppose the 'reuse existing buffer' argument still has some validity. Clippy wanted to modify scroll_to_selection() less intrusively, so that it just turned the None clause of the match from 'return' into (). But having done that it seemed better to make the whole thing an if let, saving an indent level.
-
Simon Tatham authored
-
Simon Tatham authored
That involves an API change: some '&mut self' become '&self' in the TextDecorator trait. Happily none of my implementations of those methods needed the mut in any case.
-
Simon Tatham authored
'cargo audit' reported several known vulnerabilities in dependencies, so we should update to fix them. Running 'cargo update' by itself pulled in clap 4.5.18 which has MSRV 1.74, later than our current 1.72. I don't think I'd mind too much bumping our MSRV to match, but there's also no need, because clap 4.4.18 is good enough to make cargo audit not complain.
-
- Sep 20, 2024
-
-
Simon Tatham authored
Support for editing existing posts See merge request !29
-
Simon Tatham authored
This has been on the todo list for a while, but it becomes particularly useful in conjunction with the new edit mode, because if you press [.] in your timeline, candidate posts for editing are only your own posts, few and far between. So it's useful to scroll to the selected one.
-
Simon Tatham authored
'cargo fmt' wanted to apply this change as part of the previous commit, but I deliberately left it for a separate one so that the previous diff would show more clearly what _change_ I'd made to the complex expression, and this NFC reformatting could be shown separately.
-
Simon Tatham authored
Now you can [.][SPACE] on a post of your own to edit it. This retrieves the source format of the post from the server, puts it back into your editor, and when you've edited it, submits it as an edit on the existing post. Currently this won't work if the post uses features that Mastodonochrome can't support when writing new posts: media attachments or polls. In that situation you get an Error Log message, which is not very friendly, but (a) it's intended to be temporary (I want to support those features anyway!), (b) it's at least less bad than having an unexplained 'computer says no' beep.
-
Simon Tatham authored
Language tags are optional in the Mastodon API, which means that if we want to be able to edit an existing post without changing things, we have to preserve an absence-of-language if it's there in the post being edited. Also, I can think of at least one perfectly sensible _reason_ to have a post with no language: if it's a post with no text, and only an image! (It would have to be an image with no alt text, but no matter how much those are socially disapproved of, they do _exist_, and in some cases you could imagine them being perfectly fine in practice, such as when posted in reply to a thread which gave all the necessary context.)
-
- Sep 02, 2024
-
-
Simon Tatham authored
Stop calling client.fq() on URLs! See merge request !28
-
Simon Tatham authored
I've had this lying around uncommitted for a while. This is useful if you want to occasionally read a feed that's being updated but there's no background streaming connection notifying us about updates, like a hashtag feed. When you come back to it after a while, you don't want the client to believe it already knows everything in there: you want it to check for new stuff.
-
Simon Tatham authored
I think this must have been a copy-paste error from another field. When we're printing the URL of a toot or a user in the Mastodon web UI, we don't want to call client.fq() on it, which is the method that appends @domain to bare usernames. I'm impressed at how long it's taken me to notice this bug! I think what must have happened is that _nearly_ all Mastodon web UI URLs contain an @ sign (usually [site]/@user or [site]/@user/[more details]), which caused client.fq() to leave the string unchanged. But today I encountered an instance which formats its URLs differently, and finally noticed this glaring error.
-
- May 22, 2024
-
-
Simon Tatham authored
Minor cleanups. See merge request !27
-
Simon Tatham authored
I was trying to debug something in File just now and found that I couldn't casually dbg! a value of type FileDataSource::Error, because FileDataSource didn't have a trait bound saying its Error had to be Debug. Instead of just adding the Debug trait, let's do it properly, and make all the error types implement Error, which implies Debug. This involved adding #[derive(Debug)] on LoginError, and also an unused implementation of Display, but those both seem like useful things to have around anyway.
-
Simon Tatham authored
It wasn't used at all, and never has been! When I introduced it in commit 1e897d4c I wrote the method but forgot to add a call to it, so files would jump to new content regardless of whether the method returned true. Later I noticed that bug, and fixed it in commit dcbcb9ae via the completely different method of checking the is_interrupt parameter in TuiLogicalState::ensure_activity_state, so we still didn't need this query method. I think the technique of checking is_interrupt is perfectly sensible, so we can just get rid of the unused want_to_jump_to_new_content.
-
Simon Tatham authored
Muting users isn't just an on/off switch: you can also set a timer on it, so the user becomes automatically unmuted when the timeout elapses. Also, you can follow whole hashtags, as well as following users. (I also heard a rumour that it might be possible to _mute_ a hashtag? On Eurovision night recently I saw someone post a warning that any of their followers who weren't interested might want to temporarily mute the #Eurovision tag. But I can't find anything in the protocol spec, so perhaps that was a misunderstanding.)
-
- Apr 03, 2024
-
-
Simon Tatham authored
TODO updates. See merge request !26
-
Simon Tatham authored
-
- Mar 31, 2024
-
-
Simon Tatham authored
Major refactoring to handle posts being deleted. See merge request !25
-
Simon Tatham authored
This is the payoff for a lot of the refactorings in this series. Now that File handles updating to a new list of ids by doing its own correlation between indices in the old and new lists, it doesn't depend on a fixed overlap between the two lists any more, which makes it possible to modify the list so that there _isn't_ a simple overlap. As a result, when the author of a post on your home timeline deletes it, and we get a streaming update saying they've done so, we can simply remove that id from the list in the Feed, and trust File to just sort itself out as best it can.
-
Simon Tatham authored
Now StreamResponse stores an Option<StreamEvent> which can be None, instead of a StreamEvent which can be a sensible thing or Other. The idea is that, this way, I'm not so tempted to put a catch-all `_ =>` arm at the end of every match on a StreamEvent, which means in turn that when I add a new supported event type the compiler will be able to point out the places I need to handle it.
-
Simon Tatham authored
Neither is necessary any more. FileContents contains an ordinary Vec now, with no movable origin, so its first index is always 0. That makes the lower-bound checking half of FilePosition::clip() meaningless (the usize index is never going to be less than 0), and in fact _all_ of it is unnecessary, because it's only ever called in the constructor of File, which has only just _invented_ the FilePosition's array index field, and it did it based on looking up a string id, which won't have returned an out-of-range value in any case.
-
Simon Tatham authored
Now that we don't have to maintain stable array indices in File, we can use usize to store the indices, the way nature intended, and save a lot of really tedious integer casts!
-
Simon Tatham authored
FileContents no longer depends on keeping its list of TextFragments in sync with the corresponding array of items in the Client's feed. Instead, whenever FileContents is updated, we compare the list of ids we previously had with the new one, and use that to update all our fields that refer to indices in our list. This refactoring is groundwork for supporting changes to the list that aren't rigid, in the sense that the distance between existing items changes. In particular, this will enable us to handle an item being deleted from a feed we're watching. The previously tiny wrapper method File::update_items now contains all the code to do these updates, including the complicated handling for what happens if an item we were interested in has actually vanished.
-
Simon Tatham authored
Now array indices into FileContents's vector never persist beyond the lifetime of a FileContents. When we save the file position prior to destroying a File and its contents, we save it in the form of a string item id; when we restore later, we look up that string using the index_before_id method I just factored out.
-