gitlint-rules: Remove convoluted binary search for imperative forms.

This also fixes the suggestions for the following words: disabled,
disables, disabling, implemented, implementing, implements, kept,
made, took, using.

(Copied from zulip/zulip@91f048c056a66eb78a102c095e714eff5f28e36e.)

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2021-06-14 13:36:30 -07:00
parent 9ce7c52a10
commit d1b3ac8d94

View file

@ -10,276 +10,70 @@ from gitlint.rules import CommitMessageTitle, LineRule, RuleViolation
# Copyright (c) 2015 Mike Foley
# License: MIT
# Ref: fit_commit/validators/tense.rb
WORD_SET = {
"adds",
"adding",
"added",
"allows",
"allowing",
"allowed",
"amends",
"amending",
"amended",
"bumps",
"bumping",
"bumped",
"calculates",
"calculating",
"calculated",
"changes",
"changing",
"changed",
"cleans",
"cleaning",
"cleaned",
"commits",
"committing",
"committed",
"corrects",
"correcting",
"corrected",
"creates",
"creating",
"created",
"darkens",
"darkening",
"darkened",
"disables",
"disabling",
"disabled",
"displays",
"displaying",
"displayed",
"documents",
"documenting",
"documented",
"drys",
"drying",
"dryed",
"ends",
"ending",
"ended",
"enforces",
"enforcing",
"enforced",
"enqueues",
"enqueuing",
"enqueued",
"extracts",
"extracting",
"extracted",
"finishes",
"finishing",
"finished",
"fixes",
"fixing",
"fixed",
"formats",
"formatting",
"formatted",
"guards",
"guarding",
"guarded",
"handles",
"handling",
"handled",
"hides",
"hiding",
"hid",
"increases",
"increasing",
"increased",
"ignores",
"ignoring",
"ignored",
"implements",
"implementing",
"implemented",
"improves",
"improving",
"improved",
"keeps",
"keeping",
"kept",
"kills",
"killing",
"killed",
"makes",
"making",
"made",
"merges",
"merging",
"merged",
"moves",
"moving",
"moved",
"permits",
"permitting",
"permitted",
"prevents",
"preventing",
"prevented",
"pushes",
"pushing",
"pushed",
"rebases",
"rebasing",
"rebased",
"refactors",
"refactoring",
"refactored",
"removes",
"removing",
"removed",
"renames",
"renaming",
"renamed",
"reorders",
"reordering",
"reordered",
"replaces",
"replacing",
"replaced",
"requires",
"requiring",
"required",
"restores",
"restoring",
"restored",
"sends",
"sending",
"sent",
"sets",
"setting",
"separates",
"separating",
"separated",
"shows",
"showing",
"showed",
"simplifies",
"simplifying",
"simplified",
"skips",
"skipping",
"skipped",
"sorts",
"sorting",
"speeds",
"speeding",
"sped",
"starts",
"starting",
"started",
"supports",
"supporting",
"supported",
"takes",
"taking",
"took",
"testing",
"tested", # 'tests' excluded to reduce false negative
"truncates",
"truncating",
"truncated",
"updates",
"updating",
"updated",
"uses",
"using",
"used",
}
imperative_forms = [
"add",
"allow",
"amend",
"bump",
"calculate",
"change",
"clean",
"commit",
"correct",
"create",
"darken",
"disable",
"display",
"document",
"dry",
"end",
"enforce",
"enqueue",
"extract",
"finish",
"fix",
"format",
"guard",
"handle",
"hide",
"ignore",
"implement",
"improve",
"increase",
"keep",
"kill",
"make",
"merge",
"move",
"permit",
"prevent",
"push",
"rebase",
"refactor",
"remove",
"rename",
"reorder",
"replace",
"require",
"restore",
"send",
"separate",
"set",
"show",
"simplify",
"skip",
"sort",
"speed",
"start",
"support",
"take",
"test",
"truncate",
"update",
"use",
TENSE_DATA = [
(["adds", "adding", "added"], "add"),
(["allows", "allowing", "allowed"], "allow"),
(["amends", "amending", "amended"], "amend"),
(["bumps", "bumping", "bumped"], "bump"),
(["calculates", "calculating", "calculated"], "calculate"),
(["changes", "changing", "changed"], "change"),
(["cleans", "cleaning", "cleaned"], "clean"),
(["commits", "committing", "committed"], "commit"),
(["corrects", "correcting", "corrected"], "correct"),
(["creates", "creating", "created"], "create"),
(["darkens", "darkening", "darkened"], "darken"),
(["disables", "disabling", "disabled"], "disable"),
(["displays", "displaying", "displayed"], "display"),
(["documents", "documenting", "documented"], "document"),
(["drys", "drying", "dryed"], "dry"),
(["ends", "ending", "ended"], "end"),
(["enforces", "enforcing", "enforced"], "enforce"),
(["enqueues", "enqueuing", "enqueued"], "enqueue"),
(["extracts", "extracting", "extracted"], "extract"),
(["finishes", "finishing", "finished"], "finish"),
(["fixes", "fixing", "fixed"], "fix"),
(["formats", "formatting", "formatted"], "format"),
(["guards", "guarding", "guarded"], "guard"),
(["handles", "handling", "handled"], "handle"),
(["hides", "hiding", "hid"], "hide"),
(["increases", "increasing", "increased"], "increase"),
(["ignores", "ignoring", "ignored"], "ignore"),
(["implements", "implementing", "implemented"], "implement"),
(["improves", "improving", "improved"], "improve"),
(["keeps", "keeping", "kept"], "keep"),
(["kills", "killing", "killed"], "kill"),
(["makes", "making", "made"], "make"),
(["merges", "merging", "merged"], "merge"),
(["moves", "moving", "moved"], "move"),
(["permits", "permitting", "permitted"], "permit"),
(["prevents", "preventing", "prevented"], "prevent"),
(["pushes", "pushing", "pushed"], "push"),
(["rebases", "rebasing", "rebased"], "rebase"),
(["refactors", "refactoring", "refactored"], "refactor"),
(["removes", "removing", "removed"], "remove"),
(["renames", "renaming", "renamed"], "rename"),
(["reorders", "reordering", "reordered"], "reorder"),
(["replaces", "replacing", "replaced"], "replace"),
(["requires", "requiring", "required"], "require"),
(["restores", "restoring", "restored"], "restore"),
(["sends", "sending", "sent"], "send"),
(["sets", "setting"], "set"),
(["separates", "separating", "separated"], "separate"),
(["shows", "showing", "showed"], "show"),
(["simplifies", "simplifying", "simplified"], "simplify"),
(["skips", "skipping", "skipped"], "skip"),
(["sorts", "sorting"], "sort"),
(["speeds", "speeding", "sped"], "speed"),
(["starts", "starting", "started"], "start"),
(["supports", "supporting", "supported"], "support"),
(["takes", "taking", "took"], "take"),
(["testing", "tested"], "test"), # "tests" excluded to reduce false negatives
(["truncates", "truncating", "truncated"], "truncate"),
(["updates", "updating", "updated"], "update"),
(["uses", "using", "used"], "use"),
]
imperative_forms.sort()
def head_binary_search(key: str, words: List[str]) -> str:
"""Find the imperative mood version of `word` by looking at the first
3 characters."""
# Edge case: 'disable' and 'display' have the same 3 starting letters.
if key in ["displays", "displaying", "displayed"]:
return "display"
lower = 0
upper = len(words) - 1
while True:
if lower > upper:
# Should not happen
raise Exception(f"Cannot find imperative mood of {key}")
mid = (lower + upper) // 2
imperative_form = words[mid]
if key[:3] == imperative_form[:3]:
return imperative_form
elif key < imperative_form:
upper = mid - 1
elif key > imperative_form:
lower = mid + 1
TENSE_CORRECTIONS = {word: imperative for words, imperative in TENSE_DATA for word in words}
class ImperativeMood(LineRule):
@ -303,8 +97,8 @@ class ImperativeMood(LineRule):
words = line.split(": ", 1)[-1].split()
first_word = words[0].lower()
if first_word in WORD_SET:
imperative = head_binary_search(first_word, imperative_forms)
if first_word in TENSE_CORRECTIONS:
imperative = TENSE_CORRECTIONS[first_word]
violation = RuleViolation(
self.id,
self.error_msg.format(