From 97a49b6aeb3c085d4ba6a1bc935d3204cd83613a Mon Sep 17 00:00:00 2001 From: "Lara A. Ross" Date: Wed, 25 Mar 2015 01:16:22 -0700 Subject: [PATCH] Upgrade output, allow JSON, improve naming scheme Closes #1, #21. --- app.py | 13 ++-- .../SublimeLinter-contrib-proselint/linter.py | 2 +- proselint/.proselintrc | 18 +++--- proselint/checks/butterick/symbols.py | 10 +-- proselint/checks/consistency/spacing.py | 2 +- proselint/checks/consistency/spelling.py | 2 +- proselint/checks/garner/a_vs_an.py | 2 +- proselint/checks/garner/airlinese.py | 2 +- proselint/checks/garner/animal_labels.py | 2 +- proselint/checks/garner/archaism.py | 2 +- proselint/checks/garner/back_formations.py | 2 +- proselint/checks/garner/capitalization.py | 2 +- proselint/checks/garner/cliches.py | 2 +- proselint/checks/garner/commercialese.py | 2 +- proselint/checks/garner/dates.py | 10 +-- proselint/checks/garner/denizen_labels.py | 2 +- proselint/checks/garner/errata.md | 1 + proselint/checks/garner/illogic.py | 6 +- proselint/checks/garner/jargon.py | 2 +- proselint/checks/garner/malaproprisms.py | 25 +------- proselint/checks/garner/misspelling.py | 2 +- proselint/checks/garner/mixed_metaphors.py | 2 +- proselint/checks/garner/needless_variants.py | 2 +- proselint/checks/garner/oxymorons.py | 2 +- proselint/checks/garner/preferred_forms.py | 8 +-- proselint/checks/garner/punctuation.py | 2 +- proselint/checks/garner/redundancy.py | 4 +- proselint/checks/garner/sexism.py | 2 +- proselint/checks/junge/__init__.py | 1 + proselint/checks/junge/but.py | 55 +++++++++++++++++ proselint/checks/misc/annotations.py | 2 +- proselint/checks/misc/chatspeak.py | 2 +- .../misc/{creditcard.py => credit_card.py} | 2 +- proselint/checks/misc/currency.py | 2 +- proselint/checks/misc/hyperbolic.py | 2 +- .../misc/{linkchecker.py => link_checker.py} | 2 +- proselint/checks/misc/password.py | 3 +- proselint/checks/misc/yelling.py | 2 +- proselint/checks/pinker/apologizing.py | 2 +- proselint/checks/pinker/hedging.py | 2 +- proselint/checks/pinker/latin.py | 2 +- proselint/checks/pinker/metaconcepts.py | 2 +- proselint/checks/pinker/metadiscourse.py | 2 +- proselint/checks/pinker/narcisissm.py | 2 +- proselint/checks/pinker/scare_quotes.py | 2 +- .../{strunkwhite => strunk_white}/__init__.py | 0 .../composition.py | 2 +- .../{strunkwhite => strunk_white}/greylist.py | 2 +- .../{strunkwhite => strunk_white}/usage.py | 2 +- proselint/checks/wallace/tense_present.py | 2 +- proselint/checks/wallace/uncomparables.py | 2 +- .../{writegood => write_good}/__init__.py | 0 .../{writegood => write_good}/cliches.py | 3 +- .../lexical_illusions.py | 2 +- .../{writegood => write_good}/weasel_words.py | 2 +- .../{wallstreetjournal => wsj}/__init__.py | 0 .../misspelling.py => wsj/athletes.py} | 4 +- proselint/command_line.py | 61 ++++++++++++++----- 58 files changed, 186 insertions(+), 116 deletions(-) create mode 100644 proselint/checks/garner/errata.md create mode 100644 proselint/checks/junge/__init__.py create mode 100644 proselint/checks/junge/but.py rename proselint/checks/misc/{creditcard.py => credit_card.py} (95%) rename proselint/checks/misc/{linkchecker.py => link_checker.py} (98%) rename proselint/checks/{strunkwhite => strunk_white}/__init__.py (100%) rename proselint/checks/{strunkwhite => strunk_white}/composition.py (98%) rename proselint/checks/{strunkwhite => strunk_white}/greylist.py (96%) rename proselint/checks/{strunkwhite => strunk_white}/usage.py (98%) rename proselint/checks/{writegood => write_good}/__init__.py (100%) rename proselint/checks/{writegood => write_good}/cliches.py (99%) rename proselint/checks/{writegood => write_good}/lexical_illusions.py (93%) rename proselint/checks/{writegood => write_good}/weasel_words.py (89%) rename proselint/checks/{wallstreetjournal => wsj}/__init__.py (100%) rename proselint/checks/{wallstreetjournal/misspelling.py => wsj/athletes.py} (93%) diff --git a/app.py b/app.py index e19751d62..9252e53ba 100644 --- a/app.py +++ b/app.py @@ -55,10 +55,15 @@ def lint(): for i, e in enumerate(job.result): app.logger.debug(e) errors.append({ - "line": e[0], - "column": e[1], - "err": e[2], - "msg": e[3], + "check": e[0], + "message": e[1], + "line": e[2], + "column": e[3], + "start": e[4], + "end": e[5], + "extent": e[6], + "severity": e[7], + "replacements": e[8], }) return jsonify( status="success", diff --git a/plugins/sublime/SublimeLinter-contrib-proselint/linter.py b/plugins/sublime/SublimeLinter-contrib-proselint/linter.py index a833e6304..79c7715bb 100644 --- a/plugins/sublime/SublimeLinter-contrib-proselint/linter.py +++ b/plugins/sublime/SublimeLinter-contrib-proselint/linter.py @@ -23,7 +23,7 @@ class Proselint(Linter): version_re = r'(?P\d+\.\d+\.\d+)' version_requirement = '>= 0.0.0' regex = ( - r'^.+?:(?P\d+):(?P\d+): (?P.+)' + r'^.+?:(?P\d+):(?P\d+): \S* (?P.+)' ) multiline = True line_col_base = (1, 1) diff --git a/proselint/.proselintrc b/proselint/.proselintrc index 4fd9d7906..5baca7e78 100644 --- a/proselint/.proselintrc +++ b/proselint/.proselintrc @@ -27,10 +27,10 @@ "butterick.symbols" : true, "misc.annotations" : true, "misc.chatspeak" : true, - "misc.creditcard" : true, + "misc.credit_card" : true, "misc.currency" : true, "misc.hyperbolic" : true, - "misc.linkchecker" : true, + "misc.link_checker" : true, "misc.password" : true, "misc.yelling" : true, "pinker.apologizing" : true, @@ -39,14 +39,14 @@ "pinker.metaconcepts" : true, "pinker.narcisissm" : true, "pinker.scare_quotes" : true, - "strunkwhite.composition" : true, - "strunkwhite.greylist" : true, - "strunkwhite.usage" : true, + "strunk_white.composition" : true, + "strunk_white.greylist" : true, + "strunk_white.usage" : true, "wallace.tense_present" : true, "wallace.uncomparables" : true, - "wallstreetjournal.misspelling" : true, - "writegood.cliches" : true, - "writegood.lexical_illusions" : true, - "writegood.weasel_words" : true + "wsj.athletes" : true, + "write_good.cliches" : true, + "write_good.lexical_illusions" : true, + "write_good.weasel_words" : true } } diff --git a/proselint/checks/butterick/symbols.py b/proselint/checks/butterick/symbols.py index 0064e270c..20e420255 100644 --- a/proselint/checks/butterick/symbols.py +++ b/proselint/checks/butterick/symbols.py @@ -20,7 +20,7 @@ @memoize def check_ellipsis(blob): """Use an ellipsis instead of three dots.""" - err = "BTR101" + err = "butterick.symbols.ellipsis" msg = u"'...' is an approximation, use the ellipsis symbol '…'." regex = "\.\.\." @@ -31,7 +31,7 @@ def check_ellipsis(blob): @memoize def check_copyright_symbol(blob): """Use the copyright symbol instead of (c).""" - err = "BTR102" + err = "butterick.symbols.copyright" msg = u"(c) is a goofy alphabetic approximation, use the symbol ©." regex = "\([cC]\)" @@ -42,7 +42,7 @@ def check_copyright_symbol(blob): @memoize def check_trademark_symbol(blob): """Use the trademark symbol instead of (c).""" - err = "BTR103" + err = "butterick.symbols.trademark" msg = u"(TM) is a goofy alphabetic approximation, use the symbol ™." regex = "\(TM\)" @@ -53,7 +53,7 @@ def check_trademark_symbol(blob): @memoize def check_registered_trademark_symbol(blob): """Use the registered trademark symbol instead of (R).""" - err = "BTR103" + err = "butterick.symbols.trademark" msg = u"(R) is a goofy alphabetic approximation, use the symbol ®." regex = "\([rR]\)" @@ -64,7 +64,7 @@ def check_registered_trademark_symbol(blob): @memoize def check_sentence_spacing(blob): """Use the registered trademark symbol instead of (R).""" - err = "BTR104" + err = "butterick.symbols.sentence_spacing" msg = u"More than two spaces after the period; use 1 or 2." regex = "\. {3}" diff --git a/proselint/checks/consistency/spacing.py b/proselint/checks/consistency/spacing.py index 0f439d053..733da7dec 100644 --- a/proselint/checks/consistency/spacing.py +++ b/proselint/checks/consistency/spacing.py @@ -21,7 +21,7 @@ @memoize def check(blob): """Check the text.""" - err = "CST200" + err = "consistency.spacing" msg = "Inconsistent spacing after period (1 vs. 2 spaces)." regex = ["[\.\?!] [A-Z]", "[\.\?!] [A-Z]"] diff --git a/proselint/checks/consistency/spelling.py b/proselint/checks/consistency/spelling.py index 495e3d9e8..91bb932cd 100644 --- a/proselint/checks/consistency/spelling.py +++ b/proselint/checks/consistency/spelling.py @@ -27,7 +27,7 @@ @memoize def check(blob): """Check the text.""" - err = "IEL100" + err = "consistency.spelling" msg = "Inconsistent spelling of '{}' (vs. '{}')." word_pairs = [ diff --git a/proselint/checks/garner/a_vs_an.py b/proselint/checks/garner/a_vs_an.py index d8f1def2b..2f69471ea 100644 --- a/proselint/checks/garner/a_vs_an.py +++ b/proselint/checks/garner/a_vs_an.py @@ -23,7 +23,7 @@ @memoize def check(blob): """Define the check.""" - err = "MAU101" + err = "garner.a_vs_an" msg_a = "'a' should be 'an'." msg_an = "'an' should be 'a'." diff --git a/proselint/checks/garner/airlinese.py b/proselint/checks/garner/airlinese.py index 2892742b5..b799190d3 100644 --- a/proselint/checks/garner/airlinese.py +++ b/proselint/checks/garner/airlinese.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "MAU108" + err = "garner.airlinese" msg = u"'{}' is airlinese." airlinese = [ diff --git a/proselint/checks/garner/animal_labels.py b/proselint/checks/garner/animal_labels.py index bf7eb016e..8737f3181 100644 --- a/proselint/checks/garner/animal_labels.py +++ b/proselint/checks/garner/animal_labels.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "MAU118" + err = "garner.animal_labels" msg = "There's a word for this: '{}'." preferences = [ diff --git a/proselint/checks/garner/archaism.py b/proselint/checks/garner/archaism.py index 0ed1a3cdd..1d79b450b 100644 --- a/proselint/checks/garner/archaism.py +++ b/proselint/checks/garner/archaism.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "MAU103" + err = "garner.archaism" msg = u"'{}' is archaic." archaisms = [ diff --git a/proselint/checks/garner/back_formations.py b/proselint/checks/garner/back_formations.py index b44447429..e092c2a7e 100644 --- a/proselint/checks/garner/back_formations.py +++ b/proselint/checks/garner/back_formations.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "MAU102" + err = "garner.back_formations" msg = "Back-formation. '{}' is the preferred form." list = [ diff --git a/proselint/checks/garner/capitalization.py b/proselint/checks/garner/capitalization.py index 02f50edf7..274d9ccc6 100644 --- a/proselint/checks/garner/capitalization.py +++ b/proselint/checks/garner/capitalization.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "MAU102" + err = "garner.captalization" msg = "Incorrect capitalization. '{}' is the preferred form." list = [ diff --git a/proselint/checks/garner/cliches.py b/proselint/checks/garner/cliches.py index a36e6bece..6965abc7d 100644 --- a/proselint/checks/garner/cliches.py +++ b/proselint/checks/garner/cliches.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "MAU101" + err = "garner.cliches" msg = u"'{}' is cliché." cliches = [ diff --git a/proselint/checks/garner/commercialese.py b/proselint/checks/garner/commercialese.py index 45a21407d..89990135b 100644 --- a/proselint/checks/garner/commercialese.py +++ b/proselint/checks/garner/commercialese.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "MAU104" + err = "garner.commercialese" msg = u"'{}' is commercialese." commercialese = [ diff --git a/proselint/checks/garner/dates.py b/proselint/checks/garner/dates.py index e4c2849e2..eef4af0a2 100644 --- a/proselint/checks/garner/dates.py +++ b/proselint/checks/garner/dates.py @@ -21,7 +21,7 @@ @memoize def check_decade_apostrophes_short(blob): """Check the text for dates of the form X0's.""" - err = "MAU103" + err = "garner.dates" msg = u"Apostrophes aren't needed for decades." regex = "\d0\'s" @@ -33,7 +33,7 @@ def check_decade_apostrophes_short(blob): @memoize def check_decade_apostrophes_long(blob): """Check the text for dates of the form XXX0's.""" - err = "MAU103" + err = "garner.dates" msg = u"Apostrophes aren't needed for decades." regex = "\d\d\d0\'s" @@ -43,7 +43,7 @@ def check_decade_apostrophes_long(blob): @memoize def check_dash_and_from(blob): """Check the text.""" - err = "MAU103" + err = "garner.dates" msg = u"When specifying a date range, write 'from X to Y'." regex = "from \d+[^ \t\n\r\f\va-zA-Z0-9_]\d+" @@ -52,7 +52,7 @@ def check_dash_and_from(blob): def check_month_year_comma(blob): """Check the text.""" - err = "MAU103" + err = "garner.dates" msg = u"When specifying a month and year, no comma is needed." regex = "(?:" + "|".join(calendar.month_name[1:]) + "), \d{3,}" @@ -62,7 +62,7 @@ def check_month_year_comma(blob): @memoize def check_month_of_year(blob): """Check the text.""" - err = "MAU103" + err = "garner.dates" msg = u"When specifying a month and year, 'of' is unnecessary." regex = "(?:" + "|".join(calendar.month_name[1:]) + ") of \d{3,}" diff --git a/proselint/checks/garner/denizen_labels.py b/proselint/checks/garner/denizen_labels.py index 42ff8b0ec..44b958591 100644 --- a/proselint/checks/garner/denizen_labels.py +++ b/proselint/checks/garner/denizen_labels.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "MAU109" + err = "garner.denizen_labels" msg = "'{}' is the preferred denizen label." preferences = [ diff --git a/proselint/checks/garner/errata.md b/proselint/checks/garner/errata.md new file mode 100644 index 000000000..34dcc3eb3 --- /dev/null +++ b/proselint/checks/garner/errata.md @@ -0,0 +1 @@ +? Page 82. Inconsistent spelling of "back-formations" as "backformation". diff --git a/proselint/checks/garner/illogic.py b/proselint/checks/garner/illogic.py index 477da8ccd..17fa86f32 100644 --- a/proselint/checks/garner/illogic.py +++ b/proselint/checks/garner/illogic.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "MAU105" + err = "garner.illogic" msg = u"'{}' is illogical." illogics = [ @@ -41,7 +41,7 @@ def check(blob): @memoize def check_coin_a_phrase_from(blob): """Check the text.""" - err = "MAU104" + err = "garner.illogic.coin" msg = "You can't coin an existing phrase. Did you mean 'borrow'?" regex = "to coin a phrase from" @@ -52,7 +52,7 @@ def check_coin_a_phrase_from(blob): @memoize def check_without_your_collusion(blob): """Check the textself.""" - err = "MAU838" + err = "garner.illogic.collusion" msg = "It's impossible to defraud yourself. Try 'aquiescence'." regex = "without your collusion" diff --git a/proselint/checks/garner/jargon.py b/proselint/checks/garner/jargon.py index 308635784..faeb9fc20 100644 --- a/proselint/checks/garner/jargon.py +++ b/proselint/checks/garner/jargon.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "MAU101" + err = "garner.jargon" msg = u"'{}' is jargon. Can you replace it with something more standard?" jargon = [ diff --git a/proselint/checks/garner/malaproprisms.py b/proselint/checks/garner/malaproprisms.py index 85f9687f1..516246467 100644 --- a/proselint/checks/garner/malaproprisms.py +++ b/proselint/checks/garner/malaproprisms.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "MAU105" + err = "garner.malaproprisms" msg = u"'{}' is a malaproprism." illogics = [ @@ -30,26 +30,3 @@ def check(blob): ] return existence_check(blob, illogics, err, msg, offset=1) - - -@memoize -def check_coin_a_phrase_from(blob): - """Check the text.""" - err = "MAU104" - msg = "You can't coin an existing phrase. Did you mean 'borrow'?" - - regex = "to coin a phrase from" - - return existence_check(blob, [regex], err, msg, offset=1) - - -@memoize -def check_without_your_collusion(blob): - """Check the textself.""" - err = "MAU838" - msg = "It's impossible to defraud yourself. Try 'aquiescence'." - - regex = "without your collusion" - - return existence_check( - blob, [regex], err, msg, require_padding=False, offset=-1) diff --git a/proselint/checks/garner/misspelling.py b/proselint/checks/garner/misspelling.py index d35ef5701..3abf1696d 100644 --- a/proselint/checks/garner/misspelling.py +++ b/proselint/checks/garner/misspelling.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "MAU102" + err = "garner.misspellings" msg = "Misspelling. '{}' is the preferred form." misspellings = [ diff --git a/proselint/checks/garner/mixed_metaphors.py b/proselint/checks/garner/mixed_metaphors.py index 530303e3b..288e039e0 100644 --- a/proselint/checks/garner/mixed_metaphors.py +++ b/proselint/checks/garner/mixed_metaphors.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "MAU104" + err = "garner.mixed_metaphors" msg = u"Mixed metaphor. Try '{}'." preferences = [ diff --git a/proselint/checks/garner/needless_variants.py b/proselint/checks/garner/needless_variants.py index 0cad144f9..62f433870 100644 --- a/proselint/checks/garner/needless_variants.py +++ b/proselint/checks/garner/needless_variants.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "MAU102" + err = "garner.needless_variants" msg = "Needless variant. '{}' is the preferred form." preferences = [ diff --git a/proselint/checks/garner/oxymorons.py b/proselint/checks/garner/oxymorons.py index dd632d8d0..9b329ae92 100644 --- a/proselint/checks/garner/oxymorons.py +++ b/proselint/checks/garner/oxymorons.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "MAU120" + err = "garner.oxymorons" msg = u"'{}' is an oxymoron." oxymorons = [ diff --git a/proselint/checks/garner/preferred_forms.py b/proselint/checks/garner/preferred_forms.py index 4fe35389a..9fccafef3 100644 --- a/proselint/checks/garner/preferred_forms.py +++ b/proselint/checks/garner/preferred_forms.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "MAU102" + err = "ganer.preferred_forms" msg = "'{}' is the preferred form." preferences = [ @@ -154,7 +154,7 @@ def check(blob): @memoize def check_able_ible(blob): """-able vs. -ible.""" - err = "MAU102" + err = "garner.preferred_forms.ible" msg = "-able vs. -ible. '{}' is the preferred form." preferences = [ @@ -324,7 +324,7 @@ def check_able_ible(blob): @memoize def check_able_atable(blob): """-able vs. -ible.""" - err = "MAU102" + err = "garner.preferred_forms.atable" msg = "-able vs. -atable. '{}' is the preferred form." preferences = [ @@ -405,7 +405,7 @@ def check_able_atable(blob): @memoize def check_em_vs_em_and_en_vs_in(blob): """em- vs. en-, im- vs. in-.""" - err = "MAU102" + err = "garner.preferred_forms.em" msg = "em-, im-, en-, and in-. '{}' is the preferred form." preferences = [ diff --git a/proselint/checks/garner/punctuation.py b/proselint/checks/garner/punctuation.py index 8529f5849..8256c2d4e 100644 --- a/proselint/checks/garner/punctuation.py +++ b/proselint/checks/garner/punctuation.py @@ -20,7 +20,7 @@ @memoize def check_et_al(blob): """Check the text.""" - err = "MAU103" + err = "garner.punctuation" msg = u"Misplaced punctuation. It's 'et al.'" list = [ diff --git a/proselint/checks/garner/redundancy.py b/proselint/checks/garner/redundancy.py index 90a615d8e..dfdc1d0a6 100644 --- a/proselint/checks/garner/redundancy.py +++ b/proselint/checks/garner/redundancy.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "MAU103" + err = "garner.redundancy" msg = "Redundancy. Use '{}' instead of '{}'." redundancies = [ @@ -110,7 +110,7 @@ def check(blob): @memoize def check_redundant_acronym_syndrome(blob): """Suggest the preferred forms.""" - err = "MAU104" + err = "garner.redundancy.ras" msg = "RAS syndrome. Use '{}' instead of '{}'." redundancies = [ diff --git a/proselint/checks/garner/sexism.py b/proselint/checks/garner/sexism.py index 7aa4d26d1..1d1fb9144 100644 --- a/proselint/checks/garner/sexism.py +++ b/proselint/checks/garner/sexism.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "MAU103" + err = "garner.sexism" msg = "Gender bias. Use '{}' instead of '{}'." sexism = [ diff --git a/proselint/checks/junge/__init__.py b/proselint/checks/junge/__init__.py new file mode 100644 index 000000000..98f96055f --- /dev/null +++ b/proselint/checks/junge/__init__.py @@ -0,0 +1 @@ +u"""Advice from Justin Jungé.""" diff --git a/proselint/checks/junge/but.py b/proselint/checks/junge/but.py new file mode 100644 index 000000000..ddbf4d348 --- /dev/null +++ b/proselint/checks/junge/but.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +"""MAU103: Sexism. + +--- +layout: post +error_code: MAU103 +source: Garner's Modern American Usage +source_url: http://amzn.to/15wF76r +title: sexism +date: 2014-06-10 12:31:19 +categories: writing +--- + +Points out sexist language. + +""" +from tools import memoize, preferred_forms_check + + +@memoize +def check(blob): + """Suggest the preferred forms.""" + err = "junge.but" + msg = "Gender bias. Use '{}' instead of '{}'." + + sexism = [ + ["anchor", ["anchorman", "anchorwoman", "anchorperson"]], + ["chair", ["chairman", "chairwoman", "chairperson"]], + ["drafter", ["draftman", "draftwoman", "draftperson"]], + ["ombuds", ["ombudsman", "ombudswoman", "ombudsperson"]], + ["tribe member", ["tribesman", "tribeswoman", "tribesperson"]], + ["police officer", ["policeman", "policewoman", "policeperson"]], + ["firefighter", ["fireman", "firewoman", "fireperson"]], + ["mail carrier", ["mailman", "mailwoman", "mailperson"]], + ["history", ["herstory"]], + ["women", ["womyn"]], + ["poet", ["poetess"]], + ["author", ["authoress"]], + ["waiter", ["waitress"]], + ["lawyer", ["lady lawyer"]], + ["doctor", ["woman doctor"]], + ["bookseller", ["female booksalesman"]], + ["air pilot", ["femaile airman"]], + ["executor", ["executrix"]], + ["prosecutor", ["prosecutrix"]], + ["testator", ["testatrix"]], + ["husband and wife", ["man and wife"]], + ["chairs", ["chairmen and chairs"]], + ["men and women", ["men and girls"]], + ["comedian", ["comedienne"]], + ["confidant", ["confidante"]], + # ["hero", ["heroine"]] + ] + + return preferred_forms_check(blob, sexism, err, msg, ignore_case=False) diff --git a/proselint/checks/misc/annotations.py b/proselint/checks/misc/annotations.py index 6ef8baec6..0a1d3ee14 100644 --- a/proselint/checks/misc/annotations.py +++ b/proselint/checks/misc/annotations.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "ANN100" + err = "misc.annotations" msg = u"Annotation left in text." annotations = [ diff --git a/proselint/checks/misc/chatspeak.py b/proselint/checks/misc/chatspeak.py index 67112cd33..f8c78058d 100644 --- a/proselint/checks/misc/chatspeak.py +++ b/proselint/checks/misc/chatspeak.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "MSC104" + err = "misc.chatspeak" msg = u"'{}' is chatspeak. Write it out." words = [ diff --git a/proselint/checks/misc/creditcard.py b/proselint/checks/misc/credit_card.py similarity index 95% rename from proselint/checks/misc/creditcard.py rename to proselint/checks/misc/credit_card.py index ad1366c9d..ea82faa36 100644 --- a/proselint/checks/misc/creditcard.py +++ b/proselint/checks/misc/credit_card.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "MSC102" + err = "misc.credit_card" msg = u"Don't put credit card numbers in plain text." credit_card_numbers = [ diff --git a/proselint/checks/misc/currency.py b/proselint/checks/misc/currency.py index 17462d686..369a86d5d 100644 --- a/proselint/checks/misc/currency.py +++ b/proselint/checks/misc/currency.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "MSC110" + err = "misc.currency" msg = u"Incorrent use of symbols in {}." symbols = [ diff --git a/proselint/checks/misc/hyperbolic.py b/proselint/checks/misc/hyperbolic.py index 38830ab0a..3ddfea22e 100644 --- a/proselint/checks/misc/hyperbolic.py +++ b/proselint/checks/misc/hyperbolic.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "MSC100" + err = "misc.hyperbolic" msg = u"'{}' is hyperbolic." words = [ diff --git a/proselint/checks/misc/linkchecker.py b/proselint/checks/misc/link_checker.py similarity index 98% rename from proselint/checks/misc/linkchecker.py rename to proselint/checks/misc/link_checker.py index 64a066498..1c115176b 100644 --- a/proselint/checks/misc/linkchecker.py +++ b/proselint/checks/misc/link_checker.py @@ -26,7 +26,7 @@ @memoize def check(blob): """Check the text.""" - err = "ANN100" + err = "misc.link_checker" msg = u"Broken link: {}" regex = re.compile( diff --git a/proselint/checks/misc/password.py b/proselint/checks/misc/password.py index b2158fcbe..f8fb06afd 100644 --- a/proselint/checks/misc/password.py +++ b/proselint/checks/misc/password.py @@ -19,7 +19,7 @@ @memoize def check(blob): """Check the text.""" - err = "MSC103" + err = "misc.password" msg = u"Don't put passwords in plain text." pwd_regex = "[:]? [\S]{6,30}" @@ -29,6 +29,7 @@ def check(blob): "my password is{}".format(pwd_regex), "the password's{}".format(pwd_regex), "my password's{}".format(pwd_regex), + "^[pP]assword{}".format(pwd_regex), ] return existence_check(blob, password, err, msg) diff --git a/proselint/checks/misc/yelling.py b/proselint/checks/misc/yelling.py index f511e53c0..7c3903a66 100644 --- a/proselint/checks/misc/yelling.py +++ b/proselint/checks/misc/yelling.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "MAU103" + err = "misc.yelling" msg = u"Too much yelling." regex = r"[^A-Z]\b((\s[A-Z]+){3,})" diff --git a/proselint/checks/pinker/apologizing.py b/proselint/checks/pinker/apologizing.py index 10478ccb8..9db03438a 100644 --- a/proselint/checks/pinker/apologizing.py +++ b/proselint/checks/pinker/apologizing.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "PKR102" + err = "pinker.apologizing" msg = "Excessive apologizing." narcisissm = [ diff --git a/proselint/checks/pinker/hedging.py b/proselint/checks/pinker/hedging.py index 6be8a8b98..fa7d3ebb6 100644 --- a/proselint/checks/pinker/hedging.py +++ b/proselint/checks/pinker/hedging.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "PKR102" + err = "pinker.hedging" msg = "Hedging. Just say it." narcisissm = [ diff --git a/proselint/checks/pinker/latin.py b/proselint/checks/pinker/latin.py index e4358f034..e2c6e0f1d 100644 --- a/proselint/checks/pinker/latin.py +++ b/proselint/checks/pinker/latin.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "MAU102" + err = "pinker.latin" msg = "Use English. '{}' is the preferred form." list = [ diff --git a/proselint/checks/pinker/metaconcepts.py b/proselint/checks/pinker/metaconcepts.py index 6409da459..cee276dd7 100644 --- a/proselint/checks/pinker/metaconcepts.py +++ b/proselint/checks/pinker/metaconcepts.py @@ -20,7 +20,7 @@ # @memoize # def check(blob): # """Suggest the preferred forms.""" -# err = "PKR102" +# err = "pinker.metaconcepts" # msg = "Misuse of 'scare quotes'. Delete them." # narcisissm = [ diff --git a/proselint/checks/pinker/metadiscourse.py b/proselint/checks/pinker/metadiscourse.py index d8e158d1d..73298a865 100644 --- a/proselint/checks/pinker/metadiscourse.py +++ b/proselint/checks/pinker/metadiscourse.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "PKR100" + err = "pinker.metadiscourse" msg = "Excessive metadiscourse." metadiscourse = [ diff --git a/proselint/checks/pinker/narcisissm.py b/proselint/checks/pinker/narcisissm.py index f3f278f8f..4068c43ae 100644 --- a/proselint/checks/pinker/narcisissm.py +++ b/proselint/checks/pinker/narcisissm.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "PKR101" + err = "pinker.narcisissm" msg = "Professional narcisissm. Talk about the subject, not its study." narcisissm = [ diff --git a/proselint/checks/pinker/scare_quotes.py b/proselint/checks/pinker/scare_quotes.py index 4a45165e8..6be5a3246 100644 --- a/proselint/checks/pinker/scare_quotes.py +++ b/proselint/checks/pinker/scare_quotes.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "PKR102" + err = "pinker.scare_quotes" msg = "Misuse of 'scare quotes'. Delete them." narcisissm = [ diff --git a/proselint/checks/strunkwhite/__init__.py b/proselint/checks/strunk_white/__init__.py similarity index 100% rename from proselint/checks/strunkwhite/__init__.py rename to proselint/checks/strunk_white/__init__.py diff --git a/proselint/checks/strunkwhite/composition.py b/proselint/checks/strunk_white/composition.py similarity index 98% rename from proselint/checks/strunkwhite/composition.py rename to proselint/checks/strunk_white/composition.py index da597b9ad..62af4b4da 100644 --- a/proselint/checks/strunkwhite/composition.py +++ b/proselint/checks/strunk_white/composition.py @@ -51,7 +51,7 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "STW102" + err = "strunk_white.composition" msg = "Try '{}' instead of '{}'." bad_forms = [ diff --git a/proselint/checks/strunkwhite/greylist.py b/proselint/checks/strunk_white/greylist.py similarity index 96% rename from proselint/checks/strunkwhite/greylist.py rename to proselint/checks/strunk_white/greylist.py index d6d3d8076..b0e9b7331 100644 --- a/proselint/checks/strunkwhite/greylist.py +++ b/proselint/checks/strunk_white/greylist.py @@ -21,7 +21,7 @@ @memoize def check(blob): """Check the text.""" - err = "STW100" + err = "strunk_white.greylist" msg = "Use of '{}'. {}" bad_words = [ diff --git a/proselint/checks/strunkwhite/usage.py b/proselint/checks/strunk_white/usage.py similarity index 98% rename from proselint/checks/strunkwhite/usage.py rename to proselint/checks/strunk_white/usage.py index 8baa9531b..f3d9c5c8b 100644 --- a/proselint/checks/strunkwhite/usage.py +++ b/proselint/checks/strunk_white/usage.py @@ -38,7 +38,7 @@ # @memoize # def check(text): -# err = "STW100" +# err = "strunk_white.usage" # msg = "Use of '{}'. {}" # bad_words = [ diff --git a/proselint/checks/wallace/tense_present.py b/proselint/checks/wallace/tense_present.py index 0cbf49e87..4707be795 100644 --- a/proselint/checks/wallace/tense_present.py +++ b/proselint/checks/wallace/tense_present.py @@ -21,7 +21,7 @@ @memoize def check(blob): """Check the text.""" - err = "DFW201" + err = "wallace.tense_present" msg = u"'{}'." illogics = [ diff --git a/proselint/checks/wallace/uncomparables.py b/proselint/checks/wallace/uncomparables.py index c86722ceb..b03b6b13d 100644 --- a/proselint/checks/wallace/uncomparables.py +++ b/proselint/checks/wallace/uncomparables.py @@ -53,7 +53,7 @@ @memoize def check(blob): """Check the text.""" - err = "DFW200" + err = "wallace.uncomparables" msg = "Comparison of an uncomparable: '{}' is not comparable." comparators = [ diff --git a/proselint/checks/writegood/__init__.py b/proselint/checks/write_good/__init__.py similarity index 100% rename from proselint/checks/writegood/__init__.py rename to proselint/checks/write_good/__init__.py diff --git a/proselint/checks/writegood/cliches.py b/proselint/checks/write_good/cliches.py similarity index 99% rename from proselint/checks/writegood/cliches.py rename to proselint/checks/write_good/cliches.py index 7bccf1863..e4474b36b 100644 --- a/proselint/checks/writegood/cliches.py +++ b/proselint/checks/write_good/cliches.py @@ -15,13 +15,12 @@ """ from tools import existence_check, memoize -import re @memoize def check(blob): """Check the text.""" - err = "WGD101" + err = "write_good.cliches" msg = u"'{}' is a cliché." cliches = [ diff --git a/proselint/checks/writegood/lexical_illusions.py b/proselint/checks/write_good/lexical_illusions.py similarity index 93% rename from proselint/checks/writegood/lexical_illusions.py rename to proselint/checks/write_good/lexical_illusions.py index 007a91486..da61c7a94 100644 --- a/proselint/checks/writegood/lexical_illusions.py +++ b/proselint/checks/write_good/lexical_illusions.py @@ -20,7 +20,7 @@ @memoize def check(blob): """Check the text.""" - err = "WGD105" + err = "write_good.lexical_illusions" msg = u"There's a lexical illusion here: a word is repeated." commercialese = [ diff --git a/proselint/checks/writegood/weasel_words.py b/proselint/checks/write_good/weasel_words.py similarity index 89% rename from proselint/checks/writegood/weasel_words.py rename to proselint/checks/write_good/weasel_words.py index 27891830d..715e04708 100644 --- a/proselint/checks/writegood/weasel_words.py +++ b/proselint/checks/write_good/weasel_words.py @@ -17,7 +17,7 @@ # def check(text): -# error_code = "WGD200" +# error_code = "write_good.weasel_words" # msg = "Weasel words present." # return [(1, 1, error_code, msg)] diff --git a/proselint/checks/wallstreetjournal/__init__.py b/proselint/checks/wsj/__init__.py similarity index 100% rename from proselint/checks/wallstreetjournal/__init__.py rename to proselint/checks/wsj/__init__.py diff --git a/proselint/checks/wallstreetjournal/misspelling.py b/proselint/checks/wsj/athletes.py similarity index 93% rename from proselint/checks/wallstreetjournal/misspelling.py rename to proselint/checks/wsj/athletes.py index 209dbb0aa..5eb05ca35 100644 --- a/proselint/checks/wallstreetjournal/misspelling.py +++ b/proselint/checks/wsj/athletes.py @@ -20,8 +20,8 @@ @memoize def check(blob): """Suggest the preferred forms.""" - err = "WSJ100" - msg = "Misspelling. '{}' is the preferred form." + err = "wsj.athletes" + msg = "Misspelling of athlete's name. '{}' is the preferred form." misspellings = [ ["Dwyane Wade", ["Dwayne Wade"]], diff --git a/proselint/command_line.py b/proselint/command_line.py index c013697c6..c651b9620 100644 --- a/proselint/command_line.py +++ b/proselint/command_line.py @@ -13,7 +13,7 @@ import ntpath import re import textblob -import json +import json as js import time import importlib import sys @@ -49,7 +49,7 @@ def lint(path, debug=False): """Run the linter on the file with the given path.""" # Load the options. proselint_path = os.path.dirname(os.path.realpath(__file__)) - options = json.load(open(os.path.join(proselint_path, '.proselintrc'))) + options = js.load(open(os.path.join(proselint_path, '.proselintrc'))) # Extract the checks. sys.path.append(proselint_path) @@ -69,12 +69,19 @@ def lint(path, debug=False): for check in checks: if debug: print(check.__module__ + "." + check.__name__) - start = time.time() + start_time = time.time() - errors += check(blob) + result = check(blob) + + for error in result: + (start, end, check, message) = error + (line, column) = line_and_column(blob.raw, start) + if not is_quoted(start, blob): + errors += [(check, message, line, column, start, end, + None, "warning", None)] if debug: - print(time.time() - start) + print(time.time() - start_time) if len(errors) > options["max_errors"]: break @@ -82,13 +89,6 @@ def lint(path, debug=False): # Sort the errors by line and column number. errors = sorted(errors[:options["max_errors"]]) - # Display the errors. - for error in errors: - (start, end, error_code, msg) = error - (line, column) = line_and_column(blob.raw, start) - if not is_quoted(start, blob): - log_error(path, line, column, error_code, msg) - return errors @@ -155,9 +155,10 @@ def lintscore(): @click.option('--initialize/--i', default=None) @click.option('--debug/--d', default=False) @click.option('--score/--s', default=False) +@click.option('--json/-j', default=False) @click.argument('file', default=False) -def proselint( - file=None, version=None, initialize=None, debug=None, score=None): +def proselint(file=None, version=None, initialize=None, + debug=None, score=None, json=False): """Define the linter command line API.""" # Return the version number. if version: @@ -187,8 +188,38 @@ def proselint( if not file: file = os.path.join(proselint_path, "demo.md") - return lint(file, debug=debug) + errors = lint(file, debug=debug) + + # Display the errors. + + if json: + print(errors) + out = [] + for e in errors: + out.append({ + "check": e[0], + "message": e[1], + "line": e[2], + "column": e[3], + "start": e[4], + "end": e[5], + "extent": e[6], + "severity": e[7], + "replacements": e[8], + }) + result = dict( + status="success", + data={"errors": out}) + + print(js.dumps(result)) + + else: + for error in errors: + + (check, message, line, column, start, end, + extent, severity, replacements) = error + log_error(file, line, column, check, message) if __name__ == '__main__': proselint()