Skip to content
Snippets Groups Projects
  1. Nov 21, 2024
  2. Oct 18, 2024
    • Simon Tatham's avatar
      Merge branch 'screen-layout' into 'main' · 6a2f93f1
      Simon Tatham authored
      Another rewrite of the File screen layout code.
      
      See merge request !33
      6a2f93f1
    • Simon Tatham's avatar
      Update to html2text 0.13.2. · 543e76ed
      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 :-)
      543e76ed
  3. Oct 14, 2024
    • Simon Tatham's avatar
      Make 'blank = last' actually work in file search. · 63ad334d
      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.
      63ad334d
    • Simon Tatham's avatar
      FilePosition: add a placement option to centre an item. · 77fdbdbf
      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.
      77fdbdbf
    • Simon Tatham's avatar
      Another rewrite of the File screen layout code. · de600cd6
      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.
      de600cd6
  4. Sep 27, 2024
    • Simon Tatham's avatar
      Merge branch 'file-position-fraction' into 'main' · edbabb9c
      Simon Tatham authored
      Make FilePosition store a fraction, not a line number.
      
      See merge request !32
      edbabb9c
    • Simon Tatham's avatar
      Make FilePosition store a fraction, not a line number. · 8cbc9e12
      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].
      8cbc9e12
  5. Sep 22, 2024
    • Simon Tatham's avatar
      Merge branch 'docs' into 'main' · 479d2b36
      Simon Tatham authored
      Made a start on documenting the code.
      
      See merge request !31
      479d2b36
    • Simon Tatham's avatar
      Make searching in files case-insensitive. · e5d0a595
      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.
      e5d0a595
    • Simon Tatham's avatar
      Document some pending code cleanups. · 3f78061b
      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.
      3f78061b
    • Simon Tatham's avatar
      Make Tui::run_inner into a closure. · 976865b9
      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.
      976865b9
    • Simon Tatham's avatar
      Made a start on documenting the code. · 2daceb4d
      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).
      2daceb4d
  6. Sep 21, 2024
    • Simon Tatham's avatar
      Merge branch 'updates' into 'main' · d4369f4a
      Simon Tatham authored
      cargo update, and update html2text
      
      See merge request !30
      d4369f4a
    • Simon Tatham's avatar
      Make Clippy happy. · 20c102ed
      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.
      20c102ed
    • Simon Tatham's avatar
      Fix syntax warning from cargo doc. · 34ef0bb8
      Simon Tatham authored
      34ef0bb8
    • Simon Tatham's avatar
      Update to html2text 0.12.5. · 42c22038
      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.
      42c22038
    • Simon Tatham's avatar
      Update dependencies. · d09b0172
      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.
      d09b0172
  7. Sep 20, 2024
    • Simon Tatham's avatar
      Merge branch 'editing' into 'main' · dde61ab3
      Simon Tatham authored
      Support for editing existing posts
      
      See merge request !29
      dde61ab3
    • Simon Tatham's avatar
      Scroll to keep the selection in view. · 30892fc4
      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.
      30892fc4
    • Simon Tatham's avatar
      Formatting churn. · 3ff44140
      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.
      3ff44140
    • Simon Tatham's avatar
      Support for editing existing posts. · e6e0840d
      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.
      e6e0840d
    • Simon Tatham's avatar
      Make a language tag optional when posting. · 91534b36
      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.)
      91534b36
  8. Sep 02, 2024
    • Simon Tatham's avatar
      Merge branch 'fq' into 'main' · d52241cf
      Simon Tatham authored
      Stop calling client.fq() on URLs!
      
      See merge request !28
      d52241cf
    • Simon Tatham's avatar
      Check feeds for extension in FileDataSource::init(). · f27dfb2a
      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.
      f27dfb2a
    • Simon Tatham's avatar
      Stop calling client.fq() on URLs! · b53f1b5f
      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.
      b53f1b5f
  9. May 22, 2024
    • Simon Tatham's avatar
      Merge branch 'cleanups' into 'main' · 0d488765
      Simon Tatham authored
      Minor cleanups.
      
      See merge request !27
      0d488765
    • Simon Tatham's avatar
      Impl Error for all our error types. · 6ca56237
      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.
      6ca56237
    • Simon Tatham's avatar
      Remove FileDataSource::want_to_jump_to_new_content. · d6c06b58
      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.
      d6c06b58
    • Simon Tatham's avatar
      More TODO updates. · 51c1452c
      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.)
      51c1452c
  10. Apr 03, 2024
  11. Mar 31, 2024
    • Simon Tatham's avatar
      Merge branch 'deletion' into 'main' · 40d452db
      Simon Tatham authored
      Major refactoring to handle posts being deleted.
      
      See merge request !25
      40d452db
    • Simon Tatham's avatar
      Handle posts being deleted from the home feed. · a98b9ae5
      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.
      a98b9ae5
    • Simon Tatham's avatar
      Replace StreamEvent::Other with None. · 07a5b423
      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.
      07a5b423
    • Simon Tatham's avatar
      Remove FileContents::first_index() and FilePosition::clip(). · 24192373
      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.
      24192373
    • Simon Tatham's avatar
      Switch all the isize to usize. · c1fdbca0
      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!
      c1fdbca0
    • Simon Tatham's avatar
      Remove FileContents::origin and Feed::origin. · 7e6e566c
      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.
      7e6e566c
    • Simon Tatham's avatar
      Store ids rather than indices in SavedFilePos::file_pos. · fcf5ee04
      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.
      fcf5ee04
Loading