Skip to content

Add spellcheck for the message input - #2277

Open
rtmongold wants to merge 3 commits into
squidowl:mainfrom
rtmongold:feat/input-spellcheck
Open

rtmongold wants to merge 3 commits into
squidowl:mainfrom
rtmongold:feat/input-spellcheck

Conversation

@rtmongold

@rtmongold rtmongold commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Opt-in spellcheck for the message input (buffer.text_input.spellcheck).

Adds a dependency on spellkit (0.4), a small cross-platform crate (rtmongold/spellkit) that I updated from an orphaned repo (euclio/spellbound).

Platform Backend
macOS NSSpellChecker
Windows ISpellChecker
Linux / other Unix Hunspell (system dictionaries, e.g. hunspell-en_us)

If no dictionary is available, spellcheck is a no-op (a warning is logged). Optional locale (e.g. "en-US") selects the language; unset uses the system/default.

How it works

While typing a word
The word under/just before the caret is not marked misspelled. You are not flagged mid-word.
After the word is finished
Once you leave the word (space, punctuation, or move the caret off it), misspellings are highlighted with text.spellcheck_misspelled (fallback: text.error in italic). With underline = true (default), a solid underline is drawn; color from optional theme underline_color, otherwise the misspelled text color.
Tab / Shift+Tab
Nick and command autocomplete still run first. If Tab would complete a nick, command, etc., that wins. Spell suggestions only run when autocomplete does not produce a candidate: with suggestions = true, put the caret in a finished misspelled word and Tab / Shift+Tab cycles native dictionary replacements, then back to the original spelling.
Skipped as misspellings
Channel names (#…), your nick, nicks in the current channel, and the query peer.
Disabled by default:

[buffer.text_input.spellcheck]
enabled = true
# suggestions = true
# underline = true
# locale = "en-US"

Pins iced/cosmic-text to @luca020400’s underline Style work (squidowl/iced#4, 6317712) until that lands on main’s iced pin.

@rtmongold
rtmongold force-pushed the feat/input-spellcheck branch from c72b296 to 40d914e Compare August 12, 2026 01:30
@rtmongold
rtmongold force-pushed the feat/input-spellcheck branch 3 times, most recently from 316c861 to 3bd03b6 Compare August 30, 2026 06:16
@andymandias andymandias added this to the 2026.10 milestone Sep 21, 2026
@rtmongold
rtmongold force-pushed the feat/input-spellcheck branch 2 times, most recently from 04987c2 to f897f3a Compare October 6, 2026 01:48
@rtmongold

Copy link
Copy Markdown
Contributor Author

Pins iced/cosmic-text to @luca020400’s underline Style work
(squidowl/iced#4, 6317712)
until that lands on main’s iced pin.

@rtmongold
rtmongold force-pushed the feat/input-spellcheck branch from f897f3a to 7d351eb Compare October 21, 2026 15:48
@rtmongold

Copy link
Copy Markdown
Contributor Author

brought inline with #2388 work.

@englut englut left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Works pretty well from my tests! @rtmongold could you add the following patch?

halloy-flatpak-hunspell.patch

You can apply via: patch assets/flatpak/org.squidowl.halloy.json < halloy-flatpak-hunspell.patch

This addresses hunspell build errors for flatpak.

We'll likely need squidowl/iced#4 to be rebased and merged as well. @luca020400 when you have time, could you rebase and fix the conflicts in that PR? Can give it a proper review once that's done 🙏

After that iced PR is merged, will need to update this Cargo.toml to point to the new commit hash for our iced repo.

@rtmongold

rtmongold commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

@englut I applied the SDK extension. The LLVM 22 README also lists these two build paths, so I added them: /usr/lib/sdk/llvm22/bin on append-path, and prepend-ld-library-path set to /usr/lib/sdk/llvm22/lib. If you don't think they are needed I can drop them.

@englut

englut commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

@englut I applied the SDK extension. The LLVM 22 README also lists these two build paths, so I added them: /usr/lib/sdk/llvm22/bin on append-path, and prepend-ld-library-path set to /usr/lib/sdk/llvm22/lib. If you don't think they are needed I can drop them.

My test worked without, but probably good to follow the readme! Good call 🫡

@luca020400

Copy link
Copy Markdown
Collaborator

https://github.com/squidowl/cosmic-text/tree/emoji now has the text decors fix, update the sha to af189a4cb4056f23215f90a35e93b9e1ce5d26de when you can

squidowl/iced#4 has been rebased, once merged (that'll likely happen when this pr is close to getting merged as well) mind updating the sha as well?

ty :)

@rtmongold
rtmongold force-pushed the feat/input-spellcheck branch from f4b0819 to 3ad936d Compare October 28, 2026 01:38
@luca020400

Copy link
Copy Markdown
Collaborator

fyi I just updated cosmic text as a whole, the current tree already contains the fix so you can drop the sha change

@englut

englut commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

I just tried building on my asahi linux machine. It's an aarch64 chip rather than x86_64.

I am seeing build errors relating to spellkit:

    Checking ipc v0.1.0 (/home/quaff/develop/squidowl/halloy/ipc)
error[E0308]: mismatched types
   --> /home/quaff/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/spellkit-0.4.0/src/unix.rs:189:51
    |
189 |                 unsafe { Hunspell_spell(hunspell, cstr.as_bytes_with_nul().as_ptr() as *const i8) }
    |                          --------------           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `*const u8`, found `*const i8`
    |                          |
    |                          arguments to this function are incorrect
    |
    = note: expected raw pointer `*const u8`
               found raw pointer `*const i8`
note: function defined here
   --> /home/quaff/develop/squidowl/halloy/target/debug/build/hunspell-sys-2aafe668120392ac/out/bindings.rs:36:12
    |
 36 |     pub fn Hunspell_spell(
    |            ^^^^^^^^^^^^^^

error[E0308]: arguments to this function are incorrect
   --> /home/quaff/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/spellkit-0.4.0/src/unix.rs:158:21
    |
158 |             let n = Hunspell_suggest(
    |                     ^^^^^^^^^^^^^^^^
    |
note: expected `*mut *mut *mut u8`, found `&mut *mut *mut i8`
   --> /home/quaff/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/spellkit-0.4.0/src/unix.rs:160:17
    |
160 |                 &mut list,
    |                 ^^^^^^^^^
    = note:    expected raw pointer `*mut *mut *mut u8`
            found mutable reference `&mut *mut *mut i8`
note: expected `*const u8`, found `*const i8`
   --> /home/quaff/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/spellkit-0.4.0/src/unix.rs:161:17
    |
161 |                 cstr.as_bytes_with_nul().as_ptr() as *const i8,
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: expected raw pointer `*const u8`
               found raw pointer `*const i8`
note: function defined here
   --> /home/quaff/develop/squidowl/halloy/target/debug/build/hunspell-sys-2aafe668120392ac/out/bindings.rs:48:12
    |
 48 |     pub fn Hunspell_suggest(
    |            ^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
   --> /home/quaff/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/spellkit-0.4.0/src/unix.rs:174:47
    |
174 |                 if let Ok(s) = CStr::from_ptr(p).to_str() {
    |                                -------------- ^ expected `*const u8`, found `*mut i8`
    |                                |
    |                                arguments to this function are incorrect
    |
    = note: expected raw pointer `*const u8`
               found raw pointer `*mut i8`
note: associated function defined here
   --> /home/quaff/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ffi/c_str.rs:254:25
    |
254 |     pub const unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr {
    |                         ^^^^^^^^

error[E0308]: mismatched types
   --> /home/quaff/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/spellkit-0.4.0/src/unix.rs:178:47
    |
178 |             Hunspell_free_list(self.hunspell, &mut list, n);
    |             ------------------                ^^^^^^^^^ expected `*mut *mut *mut u8`, found `&mut *mut *mut i8`
    |             |
    |             arguments to this function are incorrect
    |
    = note:    expected raw pointer `*mut *mut *mut u8`
            found mutable reference `&mut *mut *mut i8`
note: function defined here
   --> /home/quaff/develop/squidowl/halloy/target/debug/build/hunspell-sys-2aafe668120392ac/out/bindings.rs:128:12
    |
128 |     pub fn Hunspell_free_list(
    |            ^^^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
   --> /home/quaff/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/spellkit-0.4.0/src/unix.rs:211:17
    |
209 |             Hunspell_add(
    |             ------------ arguments to this function are incorrect
210 |                 self.hunspell,
211 |                 cstr.as_bytes_with_nul().as_ptr() as *const i8,
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `*const u8`, found `*const i8`
    |
    = note: expected raw pointer `*const u8`
               found raw pointer `*const i8`
note: function defined here
   --> /home/quaff/develop/squidowl/halloy/target/debug/build/hunspell-sys-2aafe668120392ac/out/bindings.rs:101:12
    |
101 |     pub fn Hunspell_add(
    |            ^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `spellkit` (lib) due to 5 previous errors
warning: build failed, waiting for other jobs to finish...

If I remove all the as *const i8 casting in https://github.com/rtmongold/spellkit/blob/master/src/unix.rs, I can build fine. When you get the chance, could you confirm this and release a new version of spellkit with this fixed? If not, I can submit a PR to your spellkit repo for this 🙏 I have tested on my x86_64 machine and builds fine here too.

@rtmongold

Copy link
Copy Markdown
Contributor Author

@englut Thanks for the report and for checking both arches. Happy to take a PR if you have the time; once it’s in I’ll cut a new spellkit release and update this PR to pull it in.

@rtmongold
rtmongold force-pushed the feat/input-spellcheck branch 2 times, most recently from 7629298 to 02c85ac Compare October 28, 2026 16:07
@rtmongold

Copy link
Copy Markdown
Contributor Author

@luca020400 thanks for the updated I have made the change.

@englut

englut commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator

@rtmongold

Cleaned up my approach a little and submitted a PR 🙏
Builds on both x86_64 and aarch64 linux.

rtmongold/spellkit#26

Comment thread .github/linux-deps.txt Outdated
Comment on lines +9 to +10
libhunspell-dev
libclang-dev

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should add libhunspell-dev (and what appears to be the equivalent hunspell-devel) to the packages comment in scripts/install-linux.sh. And potentially libclang-dev, but that was not necessary on my system for some reason? (It's not already installed, but looks like maybe it's getting pulled in by libc6? Probably better too many packages than too few.)

@englut

englut commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Good news, the iced PR has been merged 🙌

@rtmongold can you please update the squidowl/iced sha to 41673dbb9399789134755379202142d23d41f705? And also, the spellkit version as well I see that you've cut a new relese 🫡

Building successfully with:

  • linux x86_64 (flatpak)
  • linux aarch64 (flatpak)

spell check works on x86_64, but not on aarch64 (flatpak or baremetal). something with the latest spellkit broke it. I think maybe hunspell-sys was doing something necessary.

@andymandias

Copy link
Copy Markdown
Collaborator

Have been testing a bit, and I think it works really nicely and is a very solid first implementation. A couple minor things that might make sense for the first implementation:

  • I believe the typical misspelled indicator is a (squiggly) underline. Since we now have the capability for underline an error colored underline might be a reasonable default, but if we don't go with that then I think it would be good to put a tip in the documentation for users to configure it that way. For me, at least, it took a little bit to put together spellcheck_misspelled = { color = "<primary_color>", font_style = "normal", underline_color = "<error_color>" }.
  • I haven't looked through all of the code yet, but would it be simple to provide a setting for words to exempt from spellcheck? Whether that's via spellkit::Checker::ignore or another mechanism? If it'll be complicated to add then I don't think we need it in this PR, but I'm seeing e.g. http(s) and irc(s) marked as misspelled (and halloy 😢 ).

@rtmongold

Copy link
Copy Markdown
Contributor Author

@englut Yeah I saw that merged that is great, I was working on it and got tired should have it updated soon, as well as the new spellkit version.

On aarch64, is there anything in the Logs pane / halloy log files that mentions spellcheck?

@englut

englut commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

On aarch64, is there anything in the Logs pane / halloy log files that mentions spellcheck?

My bad. I was testing with the wrong branch last night 😅. Can confirm that the recent version of spellkit and the updated iced sha works on aarch64 linux 🫡

@rtmongold
rtmongold force-pushed the feat/input-spellcheck branch from 02c85ac to 3591420 Compare October 29, 2026 18:06
Opt-in platform spellchecking via spellkit
(buffer.text_input.spellcheck):
highlighting, optional locale, Tab suggestion cycling after
autocomplete, and skips for channel names / known nicks.
Misspellings use text.spellcheck_misspelled (default text.error +
italic) with a solid underline (underline = true by default; optional
underline_color).
Pins iced to squidowl/iced @ 6317712 (underline Style / squidowl#4) and
cosmic-text to af189a4 (decoration fix) until those land on main's pins.
@rtmongold
rtmongold force-pushed the feat/input-spellcheck branch from 3591420 to def497a Compare October 29, 2026 18:14
@rtmongold

Copy link
Copy Markdown
Contributor Author

@andymandias Added buffer.text_input.spellcheck.ignore for exempt words (case-insensitive). Default misspell styling is now normal text with an error-colored underline (underline = true by default).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

4 participants