Code review of a medium-sized project

I've just finished getting my stenography engine to a usable state (I'm using it to write this :grin:) and now I'd like to get some more eyes on the code. Any and all suggestions are welcome, including to refactor entire components (though I would appreciate a viable alternative). Thanks in advance!

1 Like

Are there any parts of which you particularly wonder if they could be written better?

Well, the biggest central focus is the steno engine itself in the steno module. Some logic is a bit complicated and I wasn't sure if someone else might be able to find a simpler way to do some of it. Or at least just to point out that they're confusing.

I skimmed over the code under the steno folder. Generally it's looking alright to me, you seem to be using a minimal number of libraries and the ones you use have a distinct purpose and seem fine. The code is pretty DRY as far as I can tell, you used macros to declare the Keys enum. There are tests. My gut feeling says that maybe something could be done about the organisation of the code and it might be possible to reduce the amount of code. Perhaps some of the flexibility that you've added through Traits like Dict and WordList is not necessary and they can simply be replaced by concrete types. Maybe the flexibility is necessary and then the current solution is fine. I do wish you would format the code with the default configuration just so it's easier for anyone else who is used to that (most people) to collaborate with you.

A small thing, what is the point of this AsRef<Self> implementation?

If there is any particular design problem or piece of code of which you suspect it may be possible to write it more succintly, I'm happy to take a look. I can't go over all the code you referred in detail though.

I have not tried to simplify the logic. I would have to learn about stenography first but that is currently not one of my interests.

1 Like

This is how I felt as well, hence my post here.

The main goal of this was to allow things like Arc<Dict> or &'static Lazy<Dict>. In fact I use this feature in my tests.

Also, it allows for programmatic dictionaries and word lists rather than ones backed by data structures stored in files. I haven't used this functionality yet.


Otherwise Dict itself can't be used there because there is no impl<T> AsRef<T> for T (see AsRef in std::convert - Rust "A trivial implementation of AsRef<T> for T must be added explicitly for a particular type T where needed or desired.")

My main struggle when developing this project has been how to structure the backlog. The backlog serves a few distinct purposes:

  • undoing strokes using the * key (basically backspace)
  • creating multi-stroke chords (many dictionary entries require multiple strokes to be pressed in sequence to create a longer word)
  • applying orthography rules for suffixes (that's the main reason each backlog entry stores the actual text that was inserted)

My struggle here has been the distinction between the pre-existing backlog entries and the current backlog entry that is being built. I still haven't found a satisfactory solution that doesn't require handling lots of edge cases.

I really like the API of being able to flush at any time rather than tying it to a single run_keys invocation so I would like to preserve that if possible.

Orthography rules create an annoying case where a backlog entry may actually be a "replacement" for a previous entry, because for example if I type "story" and then "^s" (the pluralization suffix), I can't just append "s" ("storys") and I actually need to replace "story" with "stories". But this means that when this "replacement" entry is deleted, I need to re-append the text of the previous entry that was deleted when this entry was applied. But also also, for backlog entries where I had to type multiple strokes to get the unpluralized word to begin with, the "replaced previous" flag actually applies to a previous iteration of the same backlog entry, so the text should not be re-appended. As you can see, edge, corner, 4d corner (??) cases. This is why I feel that there's some possibility to simplify but I just haven't found it yet.

I've tried different approaches such as

  • two backlogs, one for keys and one for text. didn't work because it was too hard to associate them together
  • one Keys per backlog entry. worked in some ways but made it much harder to implement find_action.
  • modifying backlog entries destructively, such as for suffixes, so that I wouldn't need the "replaced previous" flag. unfortunately this also interferes with find_action because it will never split up existing entries, only combine them (this is a basic stenography guarantee which applies in all other implementations I've looked at. eg for plover: Appendix: Word Boundaries - Learn Plover! "1. Plover will never split a word it has already output")

I realize that stenography knowledge is probably required to fully address this design problem and of course I have no expectation that you will go out of your way to figure this out. Thanks for your help so far too.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.