Checks all your documentation for spelling and grammar mistakes with hunspell and a nlprule based checker for grammar

Overview

cargo-spellcheck

crates.io CI commits-since rust 1.51.0+ badge

Check your spelling with hunspell and/or nlprule.

Use Cases

Run cargo spellcheck --fix or cargo spellcheck fix to fix all your documentation comments in order to avoid nasty typos all over your source tree. Meant as a helper simplifying review as well as improving CI checks after a learning phase for custom/topic specific lingo.

cargo-cpellcheck is also a valuable tool to run from git commit hooks or CI/CD systems.

Check For Spelling and/or Grammar Mistakes

cargo spellcheck check
error: spellcheck
   --> src/main.rs:44
    |
 44 | Fun facets shalld cause some erroris.
    |            ^^^^^^
    | - shall or shall d
    |

Apply Suggestions Interactively

cargo spellcheck fix
error: spellcheck(Hunspell)
    --> /media/supersonic1t/projects/cargo-spellcheck/src/literalset.rs:291
     |
 291 |  Returns literl within the Err variant if not adjacent
     |          ^^^^^^

(13/14) Apply this suggestion [y,n,q,a,d,j,e,?]?

   lite
   litter
   litterer
   liter l
   liters
   literal
   liter
 Β» a custom replacement literal

Installation

cargo install --locked cargo-spellcheck

The --locked flag is the preferred way of installing to get the tested set of dependencies.

🎈 Contribute!

Contributions are very welcome!

Generally the preferred way of doing so, is to comment in an issue that you would like to tackle the implementation/fix.

This is usually followed by an initial PR where the implementation is then discussed and iteratively refined. No need to get it all correct the first time!

Documentation

Comments
  • reflow

    reflow

    What does this PR accomplish?

    Pushing #72 over the :checkered_flag:

    • :peacock: Feature

    Closes #39

    Changes proposed by this PR:

    Remaining Challenges

    • [x] a span is not a 1:1 mapping of a line
    • [ ] user selection must work with multiline suggestions (related to / covered by #95 )
    • [ ] provide the same logic of --code exit code flags like we do for check
    • [ ] assure test cases cover all variant types with all possible single/multiline variations

    Notes to reviewer:

    πŸ“œ Checklist

    • [ ] Works on the ./demo sub directory
    • [ ] Test coverage is excellent and passes
    • [ ] Documentation is thorough
    enhancement 🦚 heavy-duty :tractor: checker / reflow 
    opened by drahnr 18
  • Write files atomically: don't act on signals.

    Write files atomically: don't act on signals.

    What does this PR accomplish?

    • 🩹 Bug Fix/Feature

    Closes #214 .

    Changes proposed by this PR:

    Use an AtomicBool to check if writing to disk is currently in progress. Wait for write to be finished and then act on signal as before.

    Notes to reviewer:

    I thought of surrounding write_changes_to_disk() (action/mod.rs:333). This would be more robust in the future if the match in mod.rs:263 called something else then correct_files(). However, I went for the smaller scope to not ignore signals for too long.

    The signal handler is just busy-waiting until the write has been finished.

    πŸ“œ Checklist

    • [x] Works on the ./demo sub directory
    • [x] Test coverage is excellent and passes
    • [x] Documentation is thorough
    opened by KuabeM 12
  • markdown: detect valid url

    markdown: detect valid url

    Steps:

    • [X] [https://ahoi.io](https://ahoi.io)
    • [ ] [fun](../fun.html)
    • [ ] [struct Foo](super::Foo)

    Signed-off-by: Laysa Uchoa [email protected]

    What does this PR accomplish?

    • 🩹 Bug Fix

    Closes # https://github.com/drahnr/cargo-spellcheck/issues/44

    Changes proposed by this PR:

    detect valid urls in [] so spell checkings can be ignored in that span https://ahoi.io

    πŸ“œ Checklist

    • [ ] Works on the ./demo sub directory
    • [ ] Test coverage is excellent and passes
    • [x] Documentation is thorough
    enhancement 🦚 stale 😐 
    opened by laysauchoa 11
  • inconsistent handling of graphemes πŸ‘©πŸ½β€πŸŒΎπŸ§ŸπŸ§žβ€β™€οΈπŸ¦Ήβ€β™‚οΈπŸ§”πŸ§›πŸΎβ€β™‚οΈ

    inconsistent handling of graphemes πŸ‘©πŸ½β€πŸŒΎπŸ§ŸπŸ§žβ€β™€οΈπŸ¦Ήβ€β™‚οΈπŸ§”πŸ§›πŸΎβ€β™‚οΈ

    Describe the bug In various places, the length used for offset calculation is used in bytes, which breaks Dow when multicharacter graphemes are used in literals.

    To Reproduce Currently there is a lack of unit tests to cover this issue sufficiently ( = none)

    Expected behavior Spans must be calculated based on graphemes (superset of chars) OR bytes consistently.

    bug internal :gear: 
    opened by drahnr 11
  • Enable pre-commit to use cargo-spellcheck

    Enable pre-commit to use cargo-spellcheck

    pre-commit is a rather convenient way to manage git hooks.

    This PR enables to use cargo-spellcheck from pre-commit. I added some information on how to do so into README.md.

    • 🦚 Feature

    Notes to reviewer:

    pre-commit will unfortunately fail building cargo-spellcheck at this time:

           Compiling nlprule-build v0.6.4
           Compiling cargo-spellcheck v0.8.14-alpha.0 (/home/dev/.cache/pre-commit/reponz1unczh)
        error: field is never read: `spans`
          --> src/reflow/iter.rs:22:5
           |
        22 |     spans: IndexMap<Range, Span>,
           |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           |
        note: the lint level is defined here
          --> src/main.rs:1:9
           |
        1  | #![deny(dead_code)]
           |         ^^^^^^^^^
        
        error: failed to compile `cargo-spellcheck v0.8.14-alpha.0 (/home/dev/.cache/pre-commit/reponz1unczh)`, intermediate artifacts can be found at `/home/dev/.cache/pre-commit/reponz1unczh/target`
        
        Caused by:
          could not compile `cargo-spellcheck` due to previous error
        
    Check the log at /home/dev/.cache/pre-commit/pre-commit.log
    

    But this should be unrelated to this PR.

    πŸ“œ Checklist

    • [-] Works on the ./demo sub directory
    • [-] Test coverage is excellent and passes
    • [-] Documentation is thorough

    None of these applies I think.

    opened by hunger 10
  • Unable to install cargo-spellcheck due to yanked crate

    Unable to install cargo-spellcheck due to yanked crate

    Describe the bug

    bindgen = 0.54.1 this crate has been yanked: https://crates.io/crates/bindgen/0.54.1

    To Reproduce

    ✦ ➜ cargo install cargo-spellcheck --force
        Updating crates.io index
      Installing cargo-spellcheck v0.4.3
    error: failed to compile `cargo-spellcheck v0.4.3`, intermediate artifacts can be found at `/tmp/cargo-installFh8W2v`
    
    Caused by:
      failed to select a version for the requirement `bindgen = "^0.54.1"`
      candidate versions found which didn't match: 0.54.0, 0.53.3, 0.53.2, ...
      location searched: crates.io index
    required by package `hunspell-sys v0.2.1`
        ... which is depended on by `hunspell-rs v0.3.0`
        ... which is depended on by `cargo-spellcheck v0.4.3`
    

    Expected behavior Expect that the installation was successfully completed.

    ✦ ➜ cargo install cargo-spellcheck
        Updating crates.io index
         Ignored package `cargo-spellcheck v0.4.3` is already installed, use --force to override
    
    

    Screenshots

    yanked-crate

    Please complete the following information:

    • System: Ubuntu 18.04.5 LTS
    • Obtained: cargo
    • Version: v0.4.3
    bug dependency 
    opened by laysauchoa 10
  • handle markdown files

    handle markdown files

    Summary

    Enable handling of *.md files, the infrastructure is already there and the handling that is necessary is mostly in traverse.rs and in documentation.rs

    enhancement 🦚 good first issue :beginner: 
    opened by drahnr 10
  • fix/emojis-and-vulgar-fractions: hunspell should not check it

    fix/emojis-and-vulgar-fractions: hunspell should not check it

    What does this PR accomplish?

    • 🩹 Bug Fix
    • 🦚 Feature

    Closes #141 .

    Changes proposed by this PR:

    Notes to reviewer:

    πŸ“œ Checklist

    • [x] Works on the ./demo sub directory
    • [X] Test coverage is excellent and passes
    • [X] Documentation is thorough

    Test for emojis

    error: spellcheck(Hunspell)

      --> /home/tmhdev/Documents/rust_samples/recursion_rust/src/fibonnacci.rs:2
       |
     2 |  πŸ˜˜πŸ€—πŸ¦€
       |  ^^^
       |   Possible spelling mistake found.
    
    error: spellcheck(Hunspell)
      --> /home/tmhdev/Documents/rust_samples/recursion_rust/src/fibonnacci.rs:2
       |
     2 |  πŸ˜˜πŸ€—πŸ¦€β…”β…”
       |  ^^^^^
       |   Possible spelling mistake found.
    
    error: spellcheck(Hunspell)
      --> /home/tmhdev/Documents/rust_samples/recursion_rust/src/fibonnacci.rs:3
       |
     3 |  β…”β…”
       |  ^^
       |   Possible spelling mistake found.
    

    I ran again and the possible mistake is not seen.

    This PR refers to https://github.com/drahnr/cargo-spellcheck/issues/141

    opened by laysauchoa 9
  • Segmentation fault when running cargo spellcheck

    Segmentation fault when running cargo spellcheck

    Describe the bug

    Segmentation fault, without further message.

    To Reproduce

    Run cargo spellcheck.

    Version is 0.8.14. Cargo version is 1.55.0.

    Expected behavior

    It does something nice :)

    Screenshots

    Backtracke obtained by gdb:

    surban@dino:~/dev/remoc/remoc$ gdb --args cargo spellcheck 
    GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
    Copyright (C) 2020 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    Type "show copying" and "show warranty" for details.
    This GDB was configured as "x86_64-linux-gnu".
    Type "show configuration" for configuration details.
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>.
    Find the GDB manual and other documentation resources online at:
        <http://www.gnu.org/software/gdb/documentation/>.
    
    For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from cargo...
    (gdb) go
    Command requires an argument.
    (gdb) run
    Starting program: /home/surban/.cargo/bin/cargo spellcheck
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
    process 15661 is executing new program: /home/surban/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/cargo
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
    process 15661 is executing new program: /home/surban/.cargo/bin/cargo-spellcheck
    warning: Missing auto-load script at offset 0 in section .debug_gdb_scripts
    of file /home/surban/.cargo/bin/cargo-spellcheck.
    Use `info auto-load python-scripts [REGEXP]' to list them.
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
    [New Thread 0x7ffff7a45700 (LWP 15665)]
    [New Thread 0x7ffff7844700 (LWP 15666)]
    [New Thread 0x7ffff7643700 (LWP 15667)]
    [New Thread 0x7ffff7442700 (LWP 15668)]
    [New Thread 0x7ffff7241700 (LWP 15669)]
    [New Thread 0x7ffff7040700 (LWP 15670)]
    [New Thread 0x7ffff6e3f700 (LWP 15671)]
    [New Thread 0x7ffff6c3e700 (LWP 15672)]
    [New Thread 0x7ffff6a37700 (LWP 15673)]
    [New Thread 0x7ffff6833700 (LWP 15674)]
    [New Thread 0x7ffff662f700 (LWP 15675)]
    [New Thread 0x7ffff642b700 (LWP 15676)]
    [New Thread 0x7ffff622a700 (LWP 15677)]
    [New Thread 0x7ffff6026700 (LWP 15678)]
    [New Thread 0x7ffff5e1f700 (LWP 15679)]
    [New Thread 0x7ffff5c1e700 (LWP 15680)]
    [New Thread 0x7ffff5a17700 (LWP 15681)]
    
    Thread 11 "cargo-spellchec" received signal SIGSEGV, Segmentation fault.
    [Switching to Thread 0x7ffff6833700 (LWP 15674)]
    0x000055555594f76b in mkallsmall (s="bioreactor", csconv=0x0) at vendor/src/hunspell/csutil.cxx:536
    536         *aI = clower(csconv, static_cast<unsigned char>(*aI));
    (gdb) bt
    #0  0x000055555594f76b in mkallsmall (s="bioreactor", csconv=0x0) at vendor/src/hunspell/csutil.cxx:536
    #1  0x0000555555931d3d in SuggestMgr::ngsuggest (this=0x5555591e76c0, 
        wlst=std::vector of length 0, capacity 0, w=<optimized out>, 
        rHMgr=std::vector of length 2, capacity 2 = {...}, captype=captype@entry=0)
        at vendor/src/hunspell/suggestmgr.cxx:1206
    #2  0x00005555559267a5 in HunspellImpl::suggest_internal (this=0x5555591e82c0, word=..., 
        capwords=<optimized out>, abbv=<optimized out>, captype=@0x7ffff68311b4: 0)
        at /usr/include/c++/9/bits/basic_string.h:2300
    #3  0x0000555555927e41 in HunspellImpl::suggest (this=0x5555591e82c0, word="serializable")
        at vendor/src/hunspell/hunspell.cxx:899
    #4  0x0000555555928674 in HunspellImpl::suggest (this=0x5555591e82c0, slst=0x7ffff68312f8, 
        word=0x7fffc8406b80 "serializable") at /usr/include/c++/9/bits/char_traits.h:300
    #5  0x000055555591aedb in hunspell_rs::Hunspell::suggest (self=0x55555b5a9ea0, word=...)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/hunspell-rs-0.3.0/src/lib.rs:91
    #6  0x000055555574ac1e in cargo_spellcheck::checker::hunspell::obtain_suggestions (
        plain=0x7ffff68318e8, chunk=0x555557181b00, hunspell=0x55555b5a9ea0, origin=0x55555722dcf8, 
        word=..., range=..., allow_concatenated=<optimized out>, allow_dashed=<optimized out>, 
        allow_emojis=<optimized out>, acc=0x7ffff6831950)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/cargo-spellcheck-0.8.14/src/checker/hunspell.rs:362
    #7  0x00005555556d2129 in <cargo_spellcheck::checker::hunspell::HunspellChecker as cargo_spellcheck::checker::Checker>::check::{{closure}} (acc=...)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/cargo-spellcheck-0.8.14/src/checker/hunspell.rs:281
    #8  <rayon::iter::try_fold::TryFoldFolder<C,U,F> as rayon::iter::plumbing::Folder<T>>::consume (
        self=..., item=...)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.1/src/iter/try_fold.rs:147
    #9  rayon::iter::plumbing::Folder::consume_iter (self=..., iter=...)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.1/src/iter/plumbing/mod.rs:179
    #10 0x000055555578a52e in <rayon::iter::map::MapFolder<C,F> as rayon::iter::plumbing::Folder<T>>::consume_iter (self=..., iter=...)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.1/src/iter/map.rs:248
    #11 rayon::iter::plumbing::Producer::fold_with (self=..., folder=...)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.1/src/iter/plumbing/mod.rs:110
    #12 rayon::iter::plumbing::bridge_producer_consumer::helper (len=<optimized out>, 
        migrated=<optimized out>, splitter=..., producer=..., consumer=...)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.1/src/iter/plumbing/mod.rs:438
    #13 0x0000555555700712 in rayon::iter::plumbing::bridge_producer_consumer::helper::{{closure}} (
        context=...)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.1/src/iter/plumbing/mod.rs:427
    #14 rayon_core::join::join_context::call_b::{{closure}} (migrated=<optimized out>)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/join/mod.rs:129
    #15 rayon_core::job::StackJob<L,F,R>::run_inline (self=..., stolen=<optimized out>)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/job.rs:97
    #16 0x000055555575e4d0 in rayon_core::join::join_context::{{closure}} (worker_thread=0x7ffff6832800, 
        injected=<optimized out>)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/join/mod.rs:158
    #17 0x000055555578a6af in rayon_core::registry::in_worker (op=...)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/registry.rs:875
    #18 rayon_core::join::join_context (oper_a=..., oper_b=...)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/join/mod.rs:132
    #19 rayon::iter::plumbing::bridge_producer_consumer::helper (len=<optimized out>, 
        migrated=<optimized out>, splitter=..., producer=..., consumer=...)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.1/src/iter/plumbing/mod.rs:416
    #20 0x000055555570b9c1 in rayon::iter::plumbing::bridge_producer_consumer::helper::{{closure}} (
        context=...)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.1/src/iter/plumbing/mod.rs:427
    #21 rayon_core::join::join_context::call_b::{{closure}} (migrated=true)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/join/mod.rs:129
    #22 <rayon_core::job::StackJob<L,F,R> as rayon_core::job::Job>::execute::call::{{closure}} ()
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/job.rs:113
    #23 <std::panic::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once (self=..., 
        _args=<optimized out>)
        at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panic.rs:347
    #24 std::panicking::try::do_call (data=<optimized out>)
        at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panicking.rs:401
    #25 std::panicking::try (f=...)
        at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panicking.rs:365
    #26 std::panic::catch_unwind (f=...)
        at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panic.rs:434
    #27 rayon_core::unwind::halt_unwinding (func=...)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/unwind.rs:17
    #28 <rayon_core::job::StackJob<L,F,R> as rayon_core::job::Job>::execute (this=0x7ffff723ffa0)
        at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/job.rs:119
    --Type <RET> for more, q to quit, c to continue without paging--c
    #29 0x0000555555614a21 in rayon_core::job::JobRef::execute (self=<optimized out>) at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/job.rs:59
    #30 rayon_core::registry::WorkerThread::execute (self=<optimized out>, job=...) at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/registry.rs:749
    #31 rayon_core::registry::WorkerThread::wait_until_cold (self=<optimized out>, latch=<optimized out>) at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/registry.rs:726
    #32 0x0000555555a3529d in rayon_core::registry::WorkerThread::wait_until (self=0x7ffff6832800, latch=<optimized out>) at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/registry.rs:700
    #33 rayon_core::registry::main_loop (registry=..., index=9, worker=...) at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/registry.rs:833
    #34 rayon_core::registry::ThreadBuilder::run (self=...) at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/registry.rs:55
    #35 0x0000555555a36b65 in <rayon_core::registry::DefaultSpawn as rayon_core::registry::ThreadSpawn>::spawn::{{closure}} () at /home/surban/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.1/src/registry.rs:100
    #36 std::sys_common::backtrace::__rust_begin_short_backtrace (f=...) at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/sys_common/backtrace.rs:125
    #37 0x0000555555a32d5b in std::thread::Builder::spawn_unchecked::{{closure}}::{{closure}} () at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/thread/mod.rs:476
    #38 <std::panic::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once (self=..., _args=<optimized out>) at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panic.rs:347
    #39 std::panicking::try::do_call (data=<optimized out>) at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panicking.rs:401
    #40 std::panicking::try (f=...) at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panicking.rs:365
    #41 std::panic::catch_unwind (f=...) at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panic.rs:434
    #42 std::thread::Builder::spawn_unchecked::{{closure}} () at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/thread/mod.rs:475
    #43 core::ops::function::FnOnce::call_once{{vtable-shim}} () at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/core/src/ops/function.rs:227
    #44 0x0000555555ab6357 in <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once () at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/alloc/src/boxed.rs:1572
    #45 <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once () at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/alloc/src/boxed.rs:1572
    #46 std::sys::unix::thread::Thread::new::thread_start () at library/std/src/sys/unix/thread.rs:74
    #47 0x00007ffff7d9b609 in start_thread (arg=<optimized out>) at pthread_create.c:477
    #48 0x00007ffff7b6d293 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
    
    bug checker / hunspell 
    opened by surban 8
  • nlp rules backend

    nlp rules backend

    What does this PR accomplish?

    Start of adding a nlp based backend without the need of a service.

    Currently statically requires the tokens and rules set (en builtin, can be overriden via config).

    • 🦚 Feature

    Closes #139 .

    Changes proposed by this PR:

    Adds another checker backend, obsoletes the LanguageTool backend.

    πŸ“œ Checklist

    • [x] Works on the ./demo sub directory
    • [x] Test coverage is excellent and passes
    • [x] Documentation is thorough
    enhancement 🦚 checker 
    opened by drahnr 8
  • Support dev comments

    Support dev comments

    Is your feature request related to a particular use-case?

    Dev comments could contain spelling mistakes too.

    Describe the solution you'd like to implement/see implemented

    A file containing // or /*! .. */ based comments should also be spell checked.

    Describe alternatives you've considered

    Status quo. Do not check them.

    enhancement 🦚 help wanted :handshake: 
    opened by drahnr 8
  • fix: remove suggestions' duplication

    fix: remove suggestions' duplication

    What does this PR accomplish?

    • 🩹 Bug Fix

    Closes #10 .

    Changes proposed by this PR:

    To remove the duplicated suggestions, the Vector of Suggestion is converted once to a HashSet and then again to a Vector.

    Notes to reviewer:

    This PR tries to resolve the πŸ”° part of the issue, but I'm willing to work on further parts of it.

    πŸ“œ Checklist

    • [x] Works on the ./demo sub directory
    • [ ] Test coverage is excellent and passes
    • [x] Documentation is thorough
    opened by granddaifuku 7
  • prepare for musl releases

    prepare for musl releases

    What does this PR accomplish?

    Uses the exhibited flags to statically link libclang which is used by bindgen.

    Needs https://github.com/drahnr/hunspell-rs/pull/4 https://github.com/euclio/hunspell-sys/pull/6

    • 🩹 Bug Fix
    • 🦚 Feature

    Closes #283

    Changes proposed by this PR:

    Notes to reviewer:

    πŸ“œ Checklist

    • [ ] Works on the ./demo sub directory
    • [ ] Test coverage is excellent and passes
    • [ ] Documentation is thorough
    opened by drahnr 0
  • Broader CI/Docker usage support

    Broader CI/Docker usage support

    Is your feature request related to a particular use-case?

    The usecase is running cargo-spellcheck in my CI system, which is based on Drone. I'm using the official rust:latest Docker image to run the step in which I'm trying to get cargo-spellcheck to work.

    A clean build attempt of cargo-spellcheck can take over 15 minutes (on hertzner cx21 = 2vCore 4GB RAM) and fail due to dynamic library linking errors which weren't as trivial as just apt-get installing hunspell.

    Trying to just download the precompiled release binary also fails due to being linked against a newer GLIBC version I think. Additionally means that using the slimmer rust:alpine docker image is also out of question because of the GLIBC requirement.

    Describe the solution you'd like to implement/see implemented

    1. Make prebuilt releases against x86_64-unknown-linux-musl.

    Seemed to work on my system after doing a hacky workaround sudo ln -s /usr/bin/g++ /usr/bin/musl-g++, which was from rust-musl-builder which could be used too most likely for the building

    1. Provide a Docker image for cargo-spellcheck.

    While it'd be better for my use case, I think that just providing a musl binary would potentially be more applicable for other possible similar use cases. Though both would be even more awesome than just picking one :)

    Describe alternatives you've considered

    • Build against an older GLIBC version
    • Look into if making cargo-spellcheck properly supported by cargo-quickinstall/cargo-binstall would solve the glibc issues
    • Document the solution to the compilation/linking issues in the official rust docker image, and accept waiting for the CI runs to compile cargo-spellcheck

    Additional context

    Relates to #241

    opened by ljoonal 6
  • hunspell stumbles over copyright symbol

    hunspell stumbles over copyright symbol

    Describe the bug

    Encountered error Utf8Error { valid_up_to: 2, error_len: Some(1) } returned from Hunspell_suggest(handle, ["\"\\xc2\\xa9\""]): 0: "\xc2\x80\x93"
    [2022-09-16T10:03:50Z DEBUG hunspell] Β© --{suggest}--> []
    

    To Reproduce

    Steps to reproduce the behaviour:

    1. A file containing Β©
    2. Run cargo spellcheck file.rs
    3. ...

    Expected behavior

    Handle or ignore, currently hunspell-rs is hacked to print an error.

    Screenshots

    Please complete the following information:

    • System: Fedora
    • Obtained: cargo + git
    • Version: <!-- run $(cargo spellcheck --version) --> 0.12.2 / git
    bug 
    opened by drahnr 5
  • reflow sub command transposes `//` and leading space

    reflow sub command transposes `//` and leading space

    Describe the bug

    cargo spellcheck reflow produces bad comments.

    To Reproduce

    Steps to reproduce the behaviour:

    1. A file containing:
    use std::any::Any;
    use std::borrow::Cow;
    use std::collections::HashSet;
    use std::ffi::CStr;
    use std::hash::{Hash, Hasher};
    use std::ptr::NonNull;
    
    use crate::def::{ConstantNameError, EnclosingRubyScope, Free, Method, NotDefinedError};
    use crate::error::Error;
    use crate::ffi::InterpreterExtractError;
    use crate::method;
    use crate::sys;
    use crate::Artichoke;
    
    mod registry;
    
    pub use registry::Registry;
    
    #[derive(Debug)]
    pub struct Builder<'a> {
        interp: &'a mut Artichoke,
        spec: &'a Spec,
        is_mrb_tt_data: bool,
        super_class: Option<NonNull<sys::RClass>>,
        methods: HashSet<method::Spec>,
    }
    
    impl<'a> Builder<'a> {
        #[must_use]
        pub fn for_spec(interp: &'a mut Artichoke, spec: &'a Spec) -> Self {
            Self {
                interp,
                spec,
                is_mrb_tt_data: false,
                super_class: None,
                methods: HashSet::default(),
            }
        }
    
        #[must_use]
        pub fn value_is_rust_object(mut self) -> Self {
            self.is_mrb_tt_data = true;
            self
        }
    
        pub fn with_super_class<T, U>(mut self, classname: U) -> Result<Self, Error>
        where
            T: Any,
            U: Into<Cow<'static, str>>,
        {
            let state = self.interp.state.as_deref().ok_or_else(InterpreterExtractError::new)?;
            let rclass = if let Some(spec) = state.classes.get::<T>() {
                spec.rclass()
            } else {
                return Err(NotDefinedError::super_class(classname.into()).into());
            };
            let rclass = unsafe { self.interp.with_ffi_boundary(|mrb| rclass.resolve(mrb))? };
            if let Some(rclass) = rclass {
                self.super_class = Some(rclass);
                Ok(self)
            } else {
                Err(NotDefinedError::super_class(classname.into()).into())
            }
        }
    
        pub fn add_method<T>(mut self, name: T, method: Method, args: sys::mrb_aspec) -> Result<Self, ConstantNameError>
        where
            T: Into<Cow<'static, str>>,
        {
            let spec = method::Spec::new(method::Type::Instance, name.into(), method, args)?;
            self.methods.insert(spec);
            Ok(self)
        }
    
        pub fn add_self_method<T>(
            mut self,
            name: T,
            method: Method,
            args: sys::mrb_aspec,
        ) -> Result<Self, ConstantNameError>
        where
            T: Into<Cow<'static, str>>,
        {
            let spec = method::Spec::new(method::Type::Class, name.into(), method, args)?;
            self.methods.insert(spec);
            Ok(self)
        }
    
        pub fn define(self) -> Result<(), NotDefinedError> {
            use sys::mrb_vtype::MRB_TT_DATA;
    
            let name = self.spec.name_c_str().as_ptr();
    
            let mut super_class = if let Some(super_class) = self.super_class {
                super_class
            } else {
                // SAFETY: Although this direct access of the `mrb` property on the
                // interp does not go through `Artichoke::with_ffi_boundary`, no
                // `MRB_API` functions are called, which means it is not required to
                // re-box the Artichoke `State` into the `mrb_state->ud` pointer.
                //
                // This code only performs a memory access to read a field from the
                // `mrb_state`.
                let rclass = unsafe { self.interp.mrb.as_ref().object_class };
                NonNull::new(rclass).ok_or_else(|| NotDefinedError::super_class("Object"))?
            };
    
            let rclass = self.spec.rclass();
            let rclass = unsafe { self.interp.with_ffi_boundary(|mrb| rclass.resolve(mrb)) };
    
            let mut rclass = if let Ok(Some(rclass)) = rclass {
                rclass
            } else if let Some(enclosing_scope) = self.spec.enclosing_scope() {
                let scope = unsafe { self.interp.with_ffi_boundary(|mrb| enclosing_scope.rclass(mrb)) };
                if let Ok(Some(mut scope)) = scope {
                    let rclass = unsafe {
                        self.interp.with_ffi_boundary(|mrb| {
                            sys::mrb_define_class_under(mrb, scope.as_mut(), name, super_class.as_mut())
                        })
                    };
                    let rclass = rclass.map_err(|_| NotDefinedError::class(self.spec.name()))?;
                    NonNull::new(rclass).ok_or_else(|| NotDefinedError::class(self.spec.name()))?
                } else {
                    return Err(NotDefinedError::enclosing_scope(enclosing_scope.fqname().into_owned()));
                }
            } else {
                let rclass = unsafe {
                    self.interp
                        .with_ffi_boundary(|mrb| sys::mrb_define_class(mrb, name, super_class.as_mut()))
                };
                let rclass = rclass.map_err(|_| NotDefinedError::class(self.spec.name()))?;
                NonNull::new(rclass).ok_or_else(|| NotDefinedError::class(self.spec.name()))?
            };
    
            for method in &self.methods {
                unsafe {
                    method.define(self.interp, rclass.as_mut())?;
                }
            }
    
            // If a `Spec` defines a `Class` whose instances own a pointer to a
            // Rust object, mark them as `MRB_TT_DATA`.
            if self.is_mrb_tt_data {
                unsafe {
                    sys::mrb_sys_set_instance_tt(rclass.as_mut(), MRB_TT_DATA);
                }
            }
            Ok(())
        }
    }
    
    #[derive(Debug, Clone, PartialEq, Eq)]
    pub struct Rclass {
        name: &'static CStr,
        enclosing_scope: Option<EnclosingRubyScope>,
    }
    
    impl Rclass {
        #[must_use]
        pub const fn new(name: &'static CStr, enclosing_scope: Option<EnclosingRubyScope>) -> Self {
            Self { name, enclosing_scope }
        }
    
        /// Resolve a type's [`sys::RClass`] using its enclosing scope and name.
        ///
        /// # Safety
        ///
        /// This function must be called within an [`Artichoke::with_ffi_boundary`]
        /// closure because the FFI APIs called in this function may require access
        /// to the Artichoke [`State`](crate::state::State).
        pub unsafe fn resolve(&self, mrb: *mut sys::mrb_state) -> Option<NonNull<sys::RClass>> {
            let class_name = self.name.as_ptr();
            if let Some(ref scope) = self.enclosing_scope {
                // short circuit if enclosing scope does not exist.
                let mut scope = scope.rclass(mrb)?;
                let is_defined_under = sys::mrb_class_defined_under(mrb, scope.as_mut(), class_name);
                if is_defined_under {
                    // Enclosing scope exists.
                    // Class is defined under the enclosing scope.
                    let class = sys::mrb_class_get_under(mrb, scope.as_mut(), class_name);
                    NonNull::new(class)
                } else {
                    // Enclosing scope exists.
                    // Class is not defined under the enclosing scope.
                    None
                }
            } else {
                let is_defined = sys::mrb_class_defined(mrb, class_name);
                if is_defined {
                    // Class exists in root scope.
                    let class = sys::mrb_class_get(mrb, class_name);
                    NonNull::new(class)
                } else {
                    // Class does not exist in root scope.
                    None
                }
            }
        }
    }
    
    #[derive(Debug)]
    pub struct Spec {
        name: Cow<'static, str>,
        name_cstr: &'static CStr,
        data_type: Box<sys::mrb_data_type>,
        enclosing_scope: Option<EnclosingRubyScope>,
    }
    
    impl Spec {
        pub fn new<T>(
            name: T,
            name_cstr: &'static CStr,
            enclosing_scope: Option<EnclosingRubyScope>,
            free: Option<Free>,
        ) -> Result<Self, ConstantNameError>
        where
            T: Into<Cow<'static, str>>,
        {
            let name = name.into();
            // SAFETY: The constructed `mrb_data_type` has `'static` lifetime:
            //
            // - `name_cstr` is `&'static` so it will outlive the `data_type`.
            // - `Spec` does not offer mutable access to these fields.
            let data_type = sys::mrb_data_type {
                struct_name: name_cstr.as_ptr(),
                dfree: free,
            };
            let data_type = Box::new(data_type);
            Ok(Self {
                name,
                name_cstr,
                data_type,
                enclosing_scope,
            })
        }
    
        #[must_use]
        pub fn data_type(&self) -> *const sys::mrb_data_type {
            self.data_type.as_ref()
        }
    
        #[must_use]
        pub fn name(&self) -> Cow<'static, str> {
            match &self.name {
                Cow::Borrowed(name) => Cow::Borrowed(name),
                Cow::Owned(name) => name.clone().into(),
            }
        }
    
        #[must_use]
        pub fn name_c_str(&self) -> &'static CStr {
            self.name_cstr
        }
    
        #[must_use]
        pub fn enclosing_scope(&self) -> Option<&EnclosingRubyScope> {
            self.enclosing_scope.as_ref()
        }
    
        #[must_use]
        pub fn fqname(&self) -> Cow<'_, str> {
            if let Some(scope) = self.enclosing_scope() {
                let mut fqname = String::from(scope.fqname());
                fqname.push_str("::");
                fqname.push_str(self.name.as_ref());
                fqname.into()
            } else {
                self.name.as_ref().into()
            }
        }
    
        #[must_use]
        pub fn rclass(&self) -> Rclass {
            Rclass::new(self.name_cstr, self.enclosing_scope.clone())
        }
    }
    
    impl Hash for Spec {
        fn hash<H: Hasher>(&self, state: &mut H) {
            self.name().hash(state);
            self.enclosing_scope().hash(state);
        }
    }
    
    impl Eq for Spec {}
    
    impl PartialEq for Spec {
        fn eq(&self, other: &Self) -> bool {
            self.fqname() == other.fqname()
        }
    }
    
    #[cfg(test)]
    mod tests {
        use spinoso_exception::StandardError;
    
        use crate::extn::core::kernel::Kernel;
        use crate::test::prelude::*;
    
        struct RustError;
    
        #[test]
        fn super_class() {
            let mut interp = interpreter();
            let spec = class::Spec::new("RustError", qed::const_cstr_from_str!("RustError\0"), None, None).unwrap();
            class::Builder::for_spec(&mut interp, &spec)
                .with_super_class::<StandardError, _>("StandardError")
                .unwrap()
                .define()
                .unwrap();
            interp.def_class::<RustError>(spec).unwrap();
    
            let result = interp.eval(b"RustError.new.is_a?(StandardError)").unwrap();
            let result = result.try_convert_into::<bool>(&interp).unwrap();
            assert!(result, "RustError instances are instance of StandardError");
    
            let result = interp.eval(b"RustError < StandardError").unwrap();
            let result = result.try_convert_into::<bool>(&interp).unwrap();
            assert!(result, "RustError inherits from StandardError");
        }
    
        #[test]
        fn rclass_for_undef_root_class() {
            let mut interp = interpreter();
            let spec = class::Spec::new("Foo", qed::const_cstr_from_str!("Foo\0"), None, None).unwrap();
            let rclass = unsafe { interp.with_ffi_boundary(|mrb| spec.rclass().resolve(mrb)) }.unwrap();
            assert!(rclass.is_none());
        }
    
        #[test]
        fn rclass_for_undef_nested_class() {
            let mut interp = interpreter();
            let scope = interp.module_spec::<Kernel>().unwrap().unwrap();
            let spec = class::Spec::new(
                "Foo",
                qed::const_cstr_from_str!("Foo\0"),
                Some(EnclosingRubyScope::module(scope)),
                None,
            )
            .unwrap();
            let rclass = unsafe { interp.with_ffi_boundary(|mrb| spec.rclass().resolve(mrb)) }.unwrap();
            assert!(rclass.is_none());
        }
    
        #[test]
        fn rclass_for_nested_class() {
            let mut interp = interpreter();
            interp.eval(b"module Foo; class Bar; end; end").unwrap();
            let spec = module::Spec::new(&mut interp, "Foo", qed::const_cstr_from_str!("Foo\0"), None).unwrap();
            let spec = class::Spec::new(
                "Bar",
                qed::const_cstr_from_str!("Bar\0"),
                Some(EnclosingRubyScope::module(&spec)),
                None,
            )
            .unwrap();
            let rclass = unsafe { interp.with_ffi_boundary(|mrb| spec.rclass().resolve(mrb)) }.unwrap();
            assert!(rclass.is_some());
        }
    
        #[test]
        fn rclass_for_nested_class_under_class() {
            let mut interp = interpreter();
            interp.eval(b"class Foo; class Bar; end; end").unwrap();
            let spec = class::Spec::new("Foo", qed::const_cstr_from_str!("Foo\0"), None, None).unwrap();
            let spec = class::Spec::new(
                "Bar",
                qed::const_cstr_from_str!("Bar\0"),
                Some(EnclosingRubyScope::class(&spec)),
                None,
            )
            .unwrap();
            let rclass = unsafe { interp.with_ffi_boundary(|mrb| spec.rclass().resolve(mrb)) }.unwrap();
            assert!(rclass.is_some());
        }
    }
    
    1. Run cargo spellcheck reflow
    2. Observe this malformed diff:
    diff --git i/artichoke-backend/src/class.rs w/artichoke-backend/src/class.rs
    index 941b22a09c..42f4a3a881 100644
    --- i/artichoke-backend/src/class.rs
    +++ w/artichoke-backend/src/class.rs
    @@ -138,8 +138,8 @@ impl<'a> Builder<'a> {
                 }
             }
    
    -        // If a `Spec` defines a `Class` whose instances own a pointer to a
    -        // Rust object, mark them as `MRB_TT_DATA`.
    +        // If a `Spec` defines a `Class` whose instances own a pointer to a Rust
    +         //object, mark them as `MRB_TT_DATA`.
             if self.is_mrb_tt_data {
                 unsafe {
                     sys::mrb_sys_set_instance_tt(rclass.as_mut(), MRB_TT_DATA);
    @@ -175,13 +175,13 @@ impl Rclass {
                 let mut scope = scope.rclass(mrb)?;
                 let is_defined_under = sys::mrb_class_defined_under(mrb, scope.as_mut(), class_name);
                 if is_defined_under {
    -                // Enclosing scope exists.
    -                // Class is defined under the enclosing scope.
    +                // Enclosing scope exists. Class is defined under the enclosing
    +                 //scope.
                     let class = sys::mrb_class_get_under(mrb, scope.as_mut(), class_name);
                     NonNull::new(class)
                 } else {
    -                // Enclosing scope exists.
    -                // Class is not defined under the enclosing scope.
    +                // Enclosing scope exists. Class is not defined under the
    +                 //enclosing scope.
                     None
                 }
             } else {
    

    Expected behavior

    No extra space before comment on second line, a space after the //.

    Screenshots

    Screen Shot 2022-09-13 at 5 07 41 PM

    Please complete the following information:

    • System: macOS
    • Obtained: cargo
    • Version: cargo-spellcheck 0.11.3

    Additional context

    bug 
    opened by lopopolo 7
  • Handle mdbook `book.toml`

    Handle mdbook `book.toml`

    Is your feature request related to a particular use-case?

    Currently mdbook manifests are skipped.

    Describe the solution you'd like to implement/see implemented

    It'd be nice to handle this gracefully, maybe skipping some subdirs.

    Describe alternatives you've considered

    Ignore and let the files be picked up recursively.

    Additional context

    opened by drahnr 0
Releases(v0.12.2)
Owner
Bernhard Schuster
Ever curious software engineer - running the @rust-meetup-munich with @flxo and @sassman .
Bernhard Schuster
WriteForAll is a text file style checker, that compares text documents with editorial tips to make text better.

WriteForAll: tips to make text better WriteForAll is a text file style checker, that compares text documents with editorial tips to make text better.

Joel Parker Henderson 2 Dec 27, 2022
Find all your TODO notes with one command!

Todo_r Find all your notes with one command! Todo_r is a simple rust command line utility that keeps track of your todo items in code. It is pronounce

Lavi Blumberg 34 Apr 22, 2022
An implementation of regular expressions for Rust. This implementation uses finite automata and guarantees linear time matching on all inputs.

regex A Rust library for parsing, compiling, and executing regular expressions. Its syntax is similar to Perl-style regular expressions, but lacks a f

The Rust Programming Language 2.6k Jan 8, 2023
A command line tool for renaming your ipa files quickly and easily.

ipa_renamer A command line tool for renaming your ipa files quickly and easily. Usage ipa_renamer 0.0.1 A command line tool for renaming your ipa file

Noah Hsu 31 Dec 31, 2022
Rust native ready-to-use NLP pipelines and transformer-based models (BERT, DistilBERT, GPT2,...)

rust-bert Rust native Transformer-based models implementation. Port of Hugging Face's Transformers library, using the tch-rs crate and pre-processing

null 1.3k Jan 8, 2023
Vaporetto: a fast and lightweight pointwise prediction based tokenizer

?? VAporetto: POintwise pREdicTion based TOkenizer Vaporetto is a fast and lightweight pointwise prediction based tokenizer. Overview This repository

null 184 Dec 22, 2022
A backend for mdBook written in Rust for generating PDF based on headless chrome and Chrome DevTools Protocol.

A backend for mdBook written in Rust for generating PDF based on headless chrome and Chrome DevTools Protocol.

Hollow Man 52 Jan 7, 2023
πŸ›₯ Vaporetto is a fast and lightweight pointwise prediction based tokenizer. This is a Python wrapper for Vaporetto.

?? python-vaporetto ?? Vaporetto is a fast and lightweight pointwise prediction based tokenizer. This is a Python wrapper for Vaporetto. Installation

null 17 Dec 22, 2022
Neural network transition-based dependency parser (in Rust)

dpar Introduction dpar is a neural network transition-based dependency parser. The original Go version can be found in the oldgo branch. Dependencies

DaniΓ«l de Kok 41 Jan 25, 2022
Simple STM32F103 based glitcher FW

Airtag glitcher (Bluepill firmware) Simple glitcher firmware running on an STM32F103 on a bluepill board. See https://github.com/pd0wm/airtag-dump for

Willem Melching 27 Dec 22, 2022
Difftastic is an experimental structured diff tool that compares files based on their syntax.

Difftastic is an experimental structured diff tool that compares files based on their syntax.

Wilfred Hughes 13.9k Jan 2, 2023
Probabilistically split concatenated words using NLP based on English Wikipedia unigram frequencies.

Untanglr Untanglr takes in a some mangled words and makes sense out of them so you dont have to. It goes through the input and splits it probabilistic

Andrei Butnaru 15 Nov 23, 2022
A small rust library for creating regex-based lexers

A small rust library for creating regex-based lexers

nph 1 Feb 5, 2022
A rule based sentence segmentation library.

cutters A rule based sentence segmentation library. ?? This library is experimental. ?? Features Full UTF-8 support. Robust parsing. Language specific

null 11 Jul 29, 2022
Viterbi-based accelerated tokenizer (Python wrapper)

?? python-vibrato ?? Vibrato is a fast implementation of tokenization (or morphological analysis) based on the Viterbi algorithm. This is a Python wra

null 20 Dec 29, 2022
A simple and fast linear algebra library for games and graphics

glam A simple and fast 3D math library for games and graphics. Development status glam is in beta stage. Base functionality has been implemented and t

Cameron Hart 953 Jan 3, 2023
A Markdown to HTML compiler and Syntax Highlighter, built using Rust's pulldown-cmark and tree-sitter-highlight crates.

A blazingly fast( possibly the fastest) markdown to html parser and syntax highlighter built using Rust's pulldown-cmark and tree-sitter-highlight crate natively for Node's Foreign Function Interface.

Ben Wishovich 48 Nov 11, 2022
Text calculator with support for units and conversion

cpc calculation + conversion cpc parses and evaluates strings of math, with support for units and conversion. 128-bit decimal floating points are used

Kasper 82 Jan 4, 2023
A command-line tool and library for generating regular expressions from user-provided test cases

Table of Contents What does this tool do? Do I still need to learn to write regexes then? Current features How to install? 4.1 The command-line tool 4

Peter M. Stahl 5.8k Dec 30, 2022