Index: cli.py ================================================================== --- cli.py +++ cli.py @@ -1,6 +1,6 @@ -#!python3 +#!/usr/bin/env python3 import sys import os.path import argparse import json @@ -42,29 +42,31 @@ # So we reverse it to avoid many useless warnings. sText = sText.replace("'", "’") return sText -def _getErrors (sText, oTokenizer, oDict, bContext=False, bDebug=False): +def _getErrors (sText, oTokenizer, oDict, bContext=False, bSpellSuggestions=False, bDebug=False): "returns a tuple: (grammar errors, spelling errors)" aGrammErrs = gce.parse(sText, "FR", bDebug=bDebug, bContext=bContext) aSpellErrs = [] for dToken in oTokenizer.genTokens(sText): if dToken['sType'] == "WORD" and not oDict.isValidToken(dToken['sValue']): + if bSpellSuggestions: + dToken['aSuggestions'] = oDict.suggest(dToken['sValue']) aSpellErrs.append(dToken) return aGrammErrs, aSpellErrs -def generateText (sText, oTokenizer, oDict, bDebug=False, bEmptyIfNoErrors=False, nWidth=100): - aGrammErrs, aSpellErrs = _getErrors(sText, oTokenizer, oDict, False, bDebug) +def generateText (sText, oTokenizer, oDict, bDebug=False, bEmptyIfNoErrors=False, bSpellSuggestions=False, nWidth=100): + aGrammErrs, aSpellErrs = _getErrors(sText, oTokenizer, oDict, False, bSpellSuggestions, bDebug) if bEmptyIfNoErrors and not aGrammErrs and not aSpellErrs: return "" return txt.generateParagraph(sText, aGrammErrs, aSpellErrs, nWidth) -def generateJSON (iIndex, sText, oTokenizer, oDict, bContext=False, bDebug=False, bEmptyIfNoErrors=False, lLineSet=None, bReturnText=False): - aGrammErrs, aSpellErrs = _getErrors(sText, oTokenizer, oDict, bContext, bDebug) +def generateJSON (iIndex, sText, oTokenizer, oDict, bContext=False, bDebug=False, bEmptyIfNoErrors=False, bSpellSuggestions=False, lLineSet=None, bReturnText=False): + aGrammErrs, aSpellErrs = _getErrors(sText, oTokenizer, oDict, bContext, bSpellSuggestions, bDebug) aGrammErrs = list(aGrammErrs) if bEmptyIfNoErrors and not aGrammErrs and not aSpellErrs: return "" if lLineSet: aGrammErrs, aSpellErrs = txt.convertToXY(aGrammErrs, aSpellErrs, lLineSet) @@ -112,13 +114,15 @@ xParser.add_argument("-j", "--json", help="generate list of errors in JSON (only with option --file or --file_to_file)", action="store_true") xParser.add_argument("-cl", "--concat_lines", help="concatenate lines not separated by an empty paragraph (only with option --file or --file_to_file)", action="store_true") xParser.add_argument("-tf", "--textformatter", help="auto-format text according to typographical rules (unavailable with option --concat_lines)", action="store_true") xParser.add_argument("-tfo", "--textformatteronly", help="auto-format text and disable grammar checking (only with option --file or --file_to_file)", action="store_true") xParser.add_argument("-ctx", "--context", help="return errors with context (only with option --json)", action="store_true") + xParser.add_argument("-as", "--add_suggestions", help="add suggestions for spelling errors (only with option --file or --file_to_file)", action="store_true") xParser.add_argument("-w", "--width", help="width in characters (40 < width < 200; default: 100)", type=int, choices=range(40,201,10), default=100) xParser.add_argument("-lo", "--list_options", help="list options", action="store_true") xParser.add_argument("-lr", "--list_rules", nargs="?", help="list rules [regex pattern as filter]", const="*") + xParser.add_argument("-ls", "--list_suggestions", help="list suggestions", type=str) xParser.add_argument("-on", "--opt_on", nargs="+", help="activate options") xParser.add_argument("-off", "--opt_off", nargs="+", help="deactivate options") xParser.add_argument("-roff", "--rule_off", nargs="+", help="deactivate rules") xParser.add_argument("-d", "--debug", help="debugging mode (only in interactive mode)", action="store_true") xArgs = xParser.parse_args() @@ -136,10 +140,19 @@ if xArgs.list_options: gce.displayOptions("fr") if xArgs.list_rules: gce.displayRules(None if xArgs.list_rules == "*" else xArgs.list_rules) exit() + + if xArgs.list_suggestions: + lSugg = oDict.suggest(xArgs.list_suggestions) + if xArgs.json: + sText = json.dumps({ "aSuggestions": lSugg }, ensure_ascii=False) + else: + sText = "Suggestions : " + " | ".join(lSugg) + echo(sText) + exit() if not xArgs.json: xArgs.context = False gce.setOptions({"html": True, "latex": True}) @@ -166,13 +179,13 @@ sText = oTF.formatText(sText) if xArgs.textformatteronly: output(sText, hDst) else: if xArgs.json: - sText = generateJSON(i, sText, oTokenizer, oDict, bContext=xArgs.context, bDebug=False, bEmptyIfNoErrors=xArgs.only_when_errors, bReturnText=xArgs.textformatter) + sText = generateJSON(i, sText, oTokenizer, oDict, bContext=xArgs.context, bDebug=False, bEmptyIfNoErrors=xArgs.only_when_errors, bSpellSuggestions=xArgs.add_suggestions, bReturnText=xArgs.textformatter) else: - sText = generateText(sText, oTokenizer, oDict, bDebug=False, bEmptyIfNoErrors=xArgs.only_when_errors, nWidth=xArgs.width) + sText = generateText(sText, oTokenizer, oDict, bDebug=False, bEmptyIfNoErrors=xArgs.only_when_errors, bSpellSuggestions=xArgs.add_suggestions, nWidth=xArgs.width) if sText: if xArgs.json and bComma: output(",\n", hDst) output(sText, hDst) bComma = True @@ -181,13 +194,13 @@ else: # concaténation des lignes non séparées par une ligne vide for i, lLine in enumerate(readfileAndConcatLines(sFile), 1): sText, lLineSet = txt.createParagraphWithLines(lLine) if xArgs.json: - sText = generateJSON(i, sText, oTokenizer, oDict, bContext=xArgs.context, bDebug=False, bEmptyIfNoErrors=xArgs.only_when_errors, lLineSet=lLineSet) + sText = generateJSON(i, sText, oTokenizer, oDict, bContext=xArgs.context, bDebug=False, bEmptyIfNoErrors=xArgs.only_when_errors, bSpellSuggestions=xArgs.add_suggestions, lLineSet=lLineSet) else: - sText = generateText(sText, oTokenizer, oDict, bDebug=False, bEmptyIfNoErrors=xArgs.only_when_errors, nWidth=xArgs.width) + sText = generateText(sText, oTokenizer, oDict, bDebug=False, bEmptyIfNoErrors=xArgs.only_when_errors, bSpellSuggestions=xArgs.add_suggestions, nWidth=xArgs.width) if sText: if xArgs.json and bComma: output(",\n", hDst) output(sText, hDst) bComma = True Index: gc_core/py/text.py ================================================================== --- gc_core/py/text.py +++ gc_core/py/text.py @@ -68,28 +68,41 @@ else: break if sErrLine: sText += sErrLine + "\n" if nGrammErr: - for dErr in lGrammErrs[:nGrammErr]: - sMsg, *others = getReadableError(dErr).split("\n") - sText += "\n".join(textwrap.wrap(sMsg, nWidth, subsequent_indent=" ")) + "\n" - for arg in others: - sText += "\n".join(textwrap.wrap(arg, nWidth, subsequent_indent=" ")) + "\n" - sText += "\n" + sText += getReadableErrors(lGrammErrs[:nGrammErr], nWidth) del lGrammErrs[0:nGrammErr] if nSpellErr: + sText += getReadableErrors(lSpellErrs[:nSpellErr], nWidth, True) del lSpellErrs[0:nSpellErr] nOffset += ln return sText -def getReadableError (dErr): +def getReadableErrors (lErrs, nWidth, bSpell=False): + "Returns lErrs errors as readable errors" + sErrors = "" + for dErr in lErrs: + if not bSpell or "aSuggestions" in dErr: + sMsg, *others = getReadableError(dErr, bSpell).split("\n") + sErrors += "\n".join(textwrap.wrap(sMsg, nWidth, subsequent_indent=" ")) + "\n" + for arg in others: + sErrors += "\n".join(textwrap.wrap(arg, nWidth, subsequent_indent=" ")) + "\n" + if sErrors != "": + sErrors += "\n" + return sErrors + + +def getReadableError (dErr, bSpell=False): "Returns an error dErr as a readable error" try: - s = u"* {nStart}:{nEnd} # {sLineId} / {sRuleId}:\n".format(**dErr) - s += " " + dErr.get("sMessage", "# error : message not found") + if bSpell: + s = u"* {nStart}:{nEnd} # {sValue}:".format(**dErr) + else: + s = u"* {nStart}:{nEnd} # {sLineId} / {sRuleId}:\n".format(**dErr) + s += " " + dErr.get("sMessage", "# error : message not found") if dErr.get("aSuggestions", None): s += "\n > Suggestions : " + " | ".join(dErr.get("aSuggestions", "# error : suggestions not found")) if dErr.get("URL", None): s += "\n > URL: " + dErr["URL"] return s