Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed Compound Matcher Conversions #608

Merged
merged 25 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bf9e631
feat(core): create linter for "special attention"
elijah-potter Feb 5, 2025
10f8830
feat(core): extend `MergeWords` to include contractions
elijah-potter Feb 5, 2025
73dc677
feat(core): added `ThanOthers` phrase linter
elijah-potter Feb 5, 2025
8836400
feat(core): added `Whereas` rule
elijah-potter Feb 6, 2025
f2ec21a
feat(core): created rule for `it self`
elijah-potter Feb 6, 2025
7ab9fc1
refactor(core): created macro for closed compounds
elijah-potter Feb 6, 2025
c948b36
feat(core): add a bunch more closed compounds
elijah-potter Feb 6, 2025
faebc9a
feat(core): add more closed compounds from #440
elijah-potter Feb 6, 2025
09fcc79
feat(core): add more closed compounds
elijah-potter Feb 6, 2025
d8c3dc6
chore(core): fix imports
elijah-potter Feb 6, 2025
18408ba
feat(core): even more closed compounds
elijah-potter Feb 6, 2025
8faa551
feat(core): create specific linter for closed compound nouns
elijah-potter Feb 6, 2025
50d7fdf
feat(core): remove incorrect rules
elijah-potter Feb 7, 2025
1d26436
refactor(core): remove bad lints and make `Nobody` more specific.
elijah-potter Feb 7, 2025
4ff1245
refactor(core): delete macro calls and make `Likewise` stricter
elijah-potter Feb 7, 2025
f2640e4
feat(core): create special rule for `hereby`
elijah-potter Feb 7, 2025
dc7aeff
test(core): add exceptions for compound words
elijah-potter Feb 10, 2025
e4420ce
feat(core): make `overnight` a special rule
elijah-potter Feb 10, 2025
01c0fbd
refactor(core): use patterns for `CompoundNouns`
elijah-potter Feb 10, 2025
eb26cc5
fix(core): remove faulty test
elijah-potter Feb 10, 2025
207b6fd
feat(core): make possessive nouns imply compound nouns
elijah-potter Feb 10, 2025
c39a61e
Merge branch 'master' into matcher-conversions
elijah-potter Feb 10, 2025
2739f0f
docs(core): add line to new affix
elijah-potter Feb 10, 2025
fec6f47
deps(harper.js): update Vitest
elijah-potter Feb 10, 2025
3ad79a4
fix(core): ignore "you" followed by an adverb
elijah-potter Feb 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
|--------------------------------------------------------------------------
|
| When your application is in debug mode, detailed error messages with
| stack traces will be shown on every error that occurs within your
| stacktraces will be shown on every error that occurs within your
| application. If disabled, a simple generic error page is shown.
|
*/
Expand Down
10 changes: 10 additions & 0 deletions harper-core/affixes.json
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,16 @@
"gifts_metadata": {
"article": true
}
},
"j": {
"#": "adverb property",
"suffix": true,
"cross_product": true,
"replacements": [],
"adds_metadata": {},
"gifts_metadata": {
"adverb": {}
elijah-potter marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
16 changes: 12 additions & 4 deletions harper-core/dictionary.dict
Original file line number Diff line number Diff line change
Expand Up @@ -11410,6 +11410,7 @@ afoul/
afraid/5U
afresh/
aft/15RZ
after/+
afterbirth/1M
afterbirths/1
afterburner/1MS
Expand Down Expand Up @@ -19204,6 +19205,7 @@ cyanobacteria/1
cyberbully/14SM
cybercafe/1S
cybernetic/5S
cyber/5
cybernetics/1M
cyberpunk/1SM
cybersex/14
Expand Down Expand Up @@ -26668,7 +26670,7 @@ herd/14MDRZGS
herder/1M
herdsman/1M
herdsmen/1
here/15M
here/15Mj
hereabout/S
hereafter/15SM
hereby/
Expand Down Expand Up @@ -34754,7 +34756,7 @@ our/4S~
ourselves/8
oust/4ZGDRS
ouster/14M
out/+145SJGMDR~
out/+145SJGMDR~j
outage/1SM
outargue/4GDS
outback/154MS
Expand Down Expand Up @@ -34833,7 +34835,8 @@ outlying/51
outmaneuver/4GDS
outmatch/4GDS
outmoded/54
outnumber/4DSG
outnumber/4DG
outnumbers/5
outpace/4GDS
outpatient/15MS
outperform/4GSD
Expand Down Expand Up @@ -48522,7 +48525,7 @@ waxiness/1M
waxwing/1SM
waxwork/1SM
waxy/51RPT
way/145SM~
way/15SM~
waybill/14SM
wayfarer/1MS
wayfaring/514SM
Expand Down Expand Up @@ -49846,5 +49849,10 @@ KSQL/1
PRQL/1
MapReduce/1
SQLAlchemy/1
inclusivity/SM
touchpad/SM
bitstream/SM
backplane/SM
cyberattack/SM
RTX/1SM
PDP/1SM
258 changes: 258 additions & 0 deletions harper-core/src/linting/closed_compounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
use crate::{
patterns::{ExactPhrase, Pattern},
Token, TokenStringExt,
};

use super::{Lint, LintKind, PatternLinter, Suggestion};

macro_rules! create_closed_compound_linter {
($name:ident, $phrase:literal, $correct:expr) => {
pub struct $name {
pattern: Box<dyn Pattern>,
}

impl Default for $name {
fn default() -> Self {
let pattern = ExactPhrase::from_phrase($phrase);

Self {
pattern: Box::new(pattern),
}
}
}

impl PatternLinter for $name {
fn pattern(&self) -> &dyn Pattern {
self.pattern.as_ref()
}

fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Lint {
let span = matched_tokens.span().unwrap();
let orig_chars = span.get_content(source);

Lint {
span,
lint_kind: LintKind::WordChoice,
suggestions: vec![Suggestion::replace_with_match_case(
$correct.chars().collect(),
orig_chars,
)],
message: format!("Did you mean the closed compound `{}`?", $correct),
..Default::default()
}
}

fn description(&self) -> &'static str {
concat!(
"Looks for incorrect spacing inside the closed compound `",
$correct,
"`."
)
}
}
};
}

create_closed_compound_linter!(Itself, "it self", "itself");
create_closed_compound_linter!(Myself, "my self", "myself");
create_closed_compound_linter!(Therefore, "there fore", "therefore");
create_closed_compound_linter!(Misunderstand, "miss understand", "misunderstand");
create_closed_compound_linter!(Misunderstood, "miss understood", "misunderstood");
create_closed_compound_linter!(Misuse, "miss use", "misuse");
create_closed_compound_linter!(Misused, "miss used", "misused");
create_closed_compound_linter!(Postpone, "post pone", "postpone");
create_closed_compound_linter!(Worldwide, "world wide", "worldwide");
create_closed_compound_linter!(Overall, "over all", "overall");
create_closed_compound_linter!(However, "how ever", "however");
create_closed_compound_linter!(Upset, "up set", "upset");
create_closed_compound_linter!(Intact, "in tact", "intact");
elijah-potter marked this conversation as resolved.
Show resolved Hide resolved
create_closed_compound_linter!(Somehow, "some how", "somehow");
create_closed_compound_linter!(Proofread, "proof read", "proofread");
create_closed_compound_linter!(Somebody, "some body", "somebody");
create_closed_compound_linter!(Anybody, "any body", "anybody");
elijah-potter marked this conversation as resolved.
Show resolved Hide resolved
create_closed_compound_linter!(Nothing, "no thing", "nothing");
create_closed_compound_linter!(Anywhere, "any where", "anywhere");
create_closed_compound_linter!(Instead, "in stead", "instead");
create_closed_compound_linter!(Somewhere, "some where", "somewhere");
create_closed_compound_linter!(Middleware, "middle ware", "middleware");
create_closed_compound_linter!(Into, "in to", "into");
Copy link
Contributor

Choose a reason for hiding this comment

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

Both are legit. As Grammarly apparently says: “Into” is a preposition that shows what something is within or inside. As separate words, in and to sometimes simply wind up next to each other.

create_closed_compound_linter!(Overclocking, "over clocking", "overclocking");
create_closed_compound_linter!(Backplane, "back plane", "backplane");
create_closed_compound_linter!(Overload, "over load", "overload");
create_closed_compound_linter!(Underclock, "under clock", "underclock");
create_closed_compound_linter!(Devops, "dev ops", "devops");
create_closed_compound_linter!(Multithreading, "multi threading", "multithreading");
create_closed_compound_linter!(Everywhere, "every where", "everywhere");
create_closed_compound_linter!(Multicore, "multi core", "multicore");
create_closed_compound_linter!(Multimedia, "multi media", "multimedia");
Copy link
Contributor

Choose a reason for hiding this comment

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

"multi-core" is also correct. Note that the hyphenated forms are not always correct. I'd consult Wiktionary when in doubt even though it's not perfect.

create_closed_compound_linter!(Widespread, "wide spread", "widespread");
create_closed_compound_linter!(Notwithstanding, "not with standing", "notwithstanding");
create_closed_compound_linter!(Anyhow, "any how", "anyhow");
create_closed_compound_linter!(Nonetheless, "none the less", "nonetheless");
create_closed_compound_linter!(Thereupon, "there upon", "thereupon");
create_closed_compound_linter!(Forthwith, "forth with", "forthwith");
Copy link
Contributor

Choose a reason for hiding this comment

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

Can occur in coding context: In my day we had to code in Forth with a line editor!

create_closed_compound_linter!(Insofar, "in so far", "insofar");
create_closed_compound_linter!(Whereupon, "where upon", "whereupon");
create_closed_compound_linter!(Upward, "up ward", "upward");
create_closed_compound_linter!(Henceforth, "hence forth", "henceforth");
create_closed_compound_linter!(Regardless, "regard less", "regardless");
create_closed_compound_linter!(Overnight, "over night", "overnight");

#[cfg(test)]
mod tests {
use super::{
Anyhow, Forthwith, Henceforth, Insofar, Nonetheless, Notwithstanding, Overnight,
Regardless, Thereupon, Upward, Whereupon, Widespread,
};
use super::{
However, Itself, Misunderstood, Misuse, Misused, Myself, Overall, Therefore, Worldwide,
};
use crate::linting::tests::assert_suggestion_result;

#[test]
fn it_self() {
let test_sentence = "The project, it self, was quite challenging.";
let expected = "The project, itself, was quite challenging.";
assert_suggestion_result(test_sentence, Itself::default(), expected);
}

#[test]
fn my_self() {
let test_sentence = "He treated my self with respect.";
let expected = "He treated myself with respect.";
assert_suggestion_result(test_sentence, Myself::default(), expected);
}

#[test]
fn there_fore() {
let test_sentence = "This is the reason; there fore, this is true.";
let expected = "This is the reason; therefore, this is true.";
assert_suggestion_result(test_sentence, Therefore::default(), expected);
}

#[test]
fn mis_understood() {
let test_sentence = "She miss understood the instructions.";
let expected = "She misunderstood the instructions.";
assert_suggestion_result(test_sentence, Misunderstood::default(), expected);
}

#[test]
fn mis_use() {
let test_sentence = "He tends to miss use the tool.";
let expected = "He tends to misuse the tool.";
assert_suggestion_result(test_sentence, Misuse::default(), expected);
}

#[test]
fn mis_used() {
let test_sentence = "The software was miss used.";
let expected = "The software was misused.";
assert_suggestion_result(test_sentence, Misused::default(), expected);
}

#[test]
fn world_wide() {
let test_sentence = "The world wide impact was significant.";
let expected = "The worldwide impact was significant.";
assert_suggestion_result(test_sentence, Worldwide::default(), expected);
}

#[test]
fn over_all() {
let test_sentence = "The over all performance was good.";
let expected = "The overall performance was good.";
assert_suggestion_result(test_sentence, Overall::default(), expected);
}

#[test]
fn how_ever() {
let test_sentence = "This is true, how ever, details matter.";
let expected = "This is true, however, details matter.";
assert_suggestion_result(test_sentence, However::default(), expected);
}

#[test]
fn wide_spread() {
let test_sentence = "The news was wide spread throughout the region.";
let expected = "The news was widespread throughout the region.";
assert_suggestion_result(test_sentence, Widespread::default(), expected);
}

#[test]
fn not_with_standing() {
let test_sentence = "They decided to proceed not with standing any further delay.";
let expected = "They decided to proceed notwithstanding any further delay.";
assert_suggestion_result(test_sentence, Notwithstanding::default(), expected);
}

#[test]
fn any_how() {
let test_sentence = "She solved the problem any how, even under pressure.";
let expected = "She solved the problem anyhow, even under pressure.";
assert_suggestion_result(test_sentence, Anyhow::default(), expected);
}

#[test]
fn none_the_less() {
let test_sentence = "The results were disappointing, none the less, they continued.";
let expected = "The results were disappointing, nonetheless, they continued.";
assert_suggestion_result(test_sentence, Nonetheless::default(), expected);
}

#[test]
fn there_upon() {
let test_sentence = "A decision was made there upon reviewing the data.";
let expected = "A decision was made thereupon reviewing the data.";
assert_suggestion_result(test_sentence, Thereupon::default(), expected);
}

#[test]
fn forth_with() {
let test_sentence = "Please reply forth with to our previous inquiry.";
let expected = "Please reply forthwith to our previous inquiry.";
assert_suggestion_result(test_sentence, Forthwith::default(), expected);
}

#[test]
fn in_so_far() {
let test_sentence = "This rule applies in so far as it covers all cases.";
let expected = "This rule applies insofar as it covers all cases.";
assert_suggestion_result(test_sentence, Insofar::default(), expected);
}

#[test]
fn where_upon() {
let test_sentence = "They acted where upon the circumstances allowed.";
let expected = "They acted whereupon the circumstances allowed.";
assert_suggestion_result(test_sentence, Whereupon::default(), expected);
}

#[test]
fn up_ward() {
let test_sentence = "The temperature moved up ward during the afternoon.";
let expected = "The temperature moved upward during the afternoon.";
assert_suggestion_result(test_sentence, Upward::default(), expected);
}

#[test]
fn hence_forth() {
let test_sentence = "All new policies apply hence forth immediately.";
let expected = "All new policies apply henceforth immediately.";
assert_suggestion_result(test_sentence, Henceforth::default(), expected);
}

#[test]
fn regard_less() {
let test_sentence = "The decision was made, regard less of the opposition.";
let expected = "The decision was made, regardless of the opposition.";
assert_suggestion_result(test_sentence, Regardless::default(), expected);
}

#[test]
fn over_night() {
let test_sentence = "They set off on their journey over night.";
let expected = "They set off on their journey overnight.";
assert_suggestion_result(test_sentence, Overnight::default(), expected);
}
}
Loading