Index: gc_core/py/grammar_checker.py
==================================================================
--- gc_core/py/grammar_checker.py
+++ gc_core/py/grammar_checker.py
@@ -53,24 +53,21 @@
         "returns a tuple: (grammar errors, spelling errors)"
         aGrammErrs = self.gce.parse(sText, "FR", bDebug=bDebug, dOptions=dOptions, bContext=bContext)
         aSpellErrs = self.oSpellChecker.parseParagraph(sText, bSpellSugg)
         return aGrammErrs, aSpellErrs
 
-    def generateText (self, sText, bEmptyIfNoErrors=False, bSpellSugg=False, nWidth=100, bDebug=False):
-        "[todo]"
-
-    def generateTextAsJSON (self, sText, bContext=False, bEmptyIfNoErrors=False, bSpellSugg=False, bReturnText=False, bDebug=False):
-        "[todo]"
-
-    def generateParagraph (self, sText, dOptions=None, bEmptyIfNoErrors=False, bSpellSugg=False, nWidth=100, bDebug=False):
+    def getParagraphWithErrors (self, sText, dOptions=None, bEmptyIfNoErrors=False, bSpellSugg=False, nWidth=100, bDebug=False):
         "parse text and return a readable text with underline errors"
         aGrammErrs, aSpellErrs = self.getParagraphErrors(sText, dOptions, False, bSpellSugg, bDebug)
         if bEmptyIfNoErrors and not aGrammErrs and not aSpellErrs:
             return ""
         return text.generateParagraph(sText, aGrammErrs, aSpellErrs, nWidth)
 
-    def generateParagraphAsJSON (self, iIndex, sText, dOptions=None, bContext=False, bEmptyIfNoErrors=False, bSpellSugg=False, bReturnText=False, lLineSet=None, bDebug=False):
+    def getTextWithErrors (self, sText, bEmptyIfNoErrors=False, bSpellSugg=False, nWidth=100, bDebug=False):
+        "[todo]"
+
+    def getParagraphErrorsAsJSON (self, iIndex, sText, dOptions=None, bContext=False, bEmptyIfNoErrors=False, bSpellSugg=False, bReturnText=False, lLineSet=None, bDebug=False):
         "parse text and return errors as a JSON string"
         aGrammErrs, aSpellErrs = self.getParagraphErrors(sText, dOptions, bContext, bSpellSugg, bDebug)
         aGrammErrs = list(aGrammErrs)
         if bEmptyIfNoErrors and not aGrammErrs and not aSpellErrs:
             return ""
@@ -78,5 +75,8 @@
             aGrammErrs, aSpellErrs = text.convertToXY(aGrammErrs, aSpellErrs, lLineSet)
             return json.dumps({ "lGrammarErrors": aGrammErrs, "lSpellingErrors": aSpellErrs }, ensure_ascii=False)
         if bReturnText:
             return json.dumps({ "iParagraph": iIndex, "sText": sText, "lGrammarErrors": aGrammErrs, "lSpellingErrors": aSpellErrs }, ensure_ascii=False)
         return json.dumps({ "iParagraph": iIndex, "lGrammarErrors": aGrammErrs, "lSpellingErrors": aSpellErrs }, ensure_ascii=False)
+
+    def getTextErrorsAsJSON (self, sText, bContext=False, bEmptyIfNoErrors=False, bSpellSugg=False, bReturnText=False, bDebug=False):
+        "[todo]"

Index: gc_core/py/text.py
==================================================================
--- gc_core/py/text.py
+++ gc_core/py/text.py
@@ -59,10 +59,13 @@
     "Returns a text with readable errors"
     if not sParagraph:
         return ""
     lGrammErrs = sorted(aGrammErrs, key=lambda d: d["nStart"])
     lSpellErrs = sorted(aSpellErrs, key=lambda d: d['nStart'])
+    lErrors = sorted(lGrammErrs + lSpellErrs, key=lambda d: d["nStart"])
+    for n, dErr in enumerate(lErrors, 1):
+        dErr["iError"] = n
     sText = ""
     nOffset = 0
     for sLine in wrap(sParagraph, nWidth): # textwrap.wrap(sParagraph, nWidth, drop_whitespace=False)
         sText += sLine + "\n"
         nLineLen = len(sLine)
@@ -96,11 +99,11 @@
             del lGrammErrs[0:nGrammErr]
         if nSpellErr:
             sText += getReadableErrors(lSpellErrs[:nSpellErr], nWidth, True)
             del lSpellErrs[0:nSpellErr]
         nOffset += nLineLen
-    return sText
+    return (sText, lErrors)
 
 
 def getReadableErrors (lErrs, nWidth, bSpellingError=False):
     "Returns lErrs errors as readable errors"
     sErrors = ""
@@ -117,16 +120,16 @@
 
 def getReadableError (dErr, bSpellingError=False):
     "Returns an error dErr as a readable error"
     try:
         if bSpellingError:
-            sText = u"* {nStart}:{nEnd}  # {sValue}:".format(**dErr)
+            sText = u"* {iError} [{nStart}:{nEnd}]  # {sValue}:".format(**dErr)
         else:
-            sText = u"* {nStart}:{nEnd}  # {sLineId} / {sRuleId}:\n".format(**dErr)
+            sText = u"* {iError} [{nStart}:{nEnd}]  # {sLineId} / {sRuleId}:\n".format(**dErr)
             sText += "  " + dErr.get("sMessage", "# error : message not found")
         if dErr.get("aSuggestions", None):
-            sText += "\n  > Suggestions : " + " | ".join(dErr.get("aSuggestions", "# error : suggestions not found"))
+            sText += "\n  > Suggestions : " + " | ".join(dErr["aSuggestions"])
         if dErr.get("URL", None):
             sText += "\n  > URL: " + dErr["URL"]
         return sText
     except KeyError:
         return u"* Non-compliant error: {}".format(dErr)

Index: grammalecte-cli.py
==================================================================
--- grammalecte-cli.py
+++ grammalecte-cli.py
@@ -6,10 +6,12 @@
 
 import sys
 import os.path
 import argparse
 import json
+import re
+import traceback
 
 import grammalecte
 import grammalecte.text as txt
 from grammalecte.graphspell.echo import echo
 
@@ -102,16 +104,37 @@
             return None
         return oJSON
     print("# Error: file <" + spf + "> not found.")
     return None
 
+
+def getCommand ():
+    while True:
+        print("COMMANDS: [N]ext paragraph  [Q]uit.")
+        print("          [Error number]>[suggestion number]")
+        print("          [Error number] (apply first suggestion)")
+        print("          [Error number]=[replacement]")
+        sCommand = input("        ? ")
+        if sCommand == "q" or sCommand == "Q" or sCommand == "n" or sCommand == "N":
+            return sCommand
+        elif re.match("^([0-9]+)(>[0-9]*|=.*|)$", sCommand):
+            m = re.match("^([0-9]+)(>[0-9]*|=.*|)$", sCommand)
+            nError = int(m.group(1)) - 1
+            cAction = m.group(2)[0:1] or ">"
+            if cAction == ">":
+                vSugg = int(m.group(2)[1:]) - 1  if m.group(2)  else 0
+            else:
+                vSugg = m.group(2)[1:]
+            return (nError, cAction, vSugg)
+
 
 def main ():
     "launch the CLI (command line interface)"
     xParser = argparse.ArgumentParser()
     xParser.add_argument("-f", "--file", help="parse file (UTF-8 required!) [on Windows, -f is similar to -ff]", type=str)
     xParser.add_argument("-ff", "--file_to_file", help="parse file (UTF-8 required!) and create a result file (*.res.txt)", type=str)
+    xParser.add_argument("-iff", "--interactive_file_to_file", help="parse file (UTF-8 required!) and create a result file (*.res.txt)", type=str)
     xParser.add_argument("-owe", "--only_when_errors", help="display results only when there are errors", action="store_true")
     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 (not 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")
@@ -175,13 +198,14 @@
     # disable grammar rules
     if xArgs.rule_off:
         for sRule in xArgs.rule_off:
             oGrammarChecker.gce.ignoreRule(sRule)
 
-    sFile = xArgs.file or xArgs.file_to_file
-    if sFile:
+
+    if xArgs.file or xArgs.file_to_file:
         # file processing
+        sFile = xArgs.file or xArgs.file_to_file
         hDst = open(sFile[:sFile.rfind(".")]+".res.txt", "w", encoding="utf-8", newline="\n")  if xArgs.file_to_file or sys.platform == "win32"  else None
         bComma = False
         if xArgs.json:
             output('{ "grammalecte": "'+oGrammarChecker.gce.version+'", "lang": "'+oGrammarChecker.gce.lang+'", "data" : [\n', hDst)
         for i, sText, lLineSet in generateParagraphFromFile(sFile, xArgs.concat_lines):
@@ -189,23 +213,56 @@
                 sText = oTextFormatter.formatText(sText)
             if xArgs.textformatteronly:
                 output(sText, hDst)
                 continue
             if xArgs.json:
-                sText = oGrammarChecker.generateParagraphAsJSON(i, sText, bContext=xArgs.context, bEmptyIfNoErrors=xArgs.only_when_errors, \
+                sText = oGrammarChecker.getParagraphErrorsAsJSON(i, sText, bContext=xArgs.context, bEmptyIfNoErrors=xArgs.only_when_errors, \
                                                                 bSpellSugg=xArgs.with_spell_sugg, bReturnText=xArgs.textformatter, lLineSet=lLineSet)
             else:
-                sText = oGrammarChecker.generateParagraph(sText, bEmptyIfNoErrors=xArgs.only_when_errors, bSpellSugg=xArgs.with_spell_sugg, nWidth=xArgs.width)
+                sText, _ = oGrammarChecker.getParagraphWithErrors(sText, bEmptyIfNoErrors=xArgs.only_when_errors, bSpellSugg=xArgs.with_spell_sugg, nWidth=xArgs.width)
             if sText:
                 if xArgs.json and bComma:
                     output(",\n", hDst)
                 output(sText, hDst)
                 bComma = True
             if hDst:
                 echo("§ %d\r" % i, end="", flush=True)
         if xArgs.json:
             output("\n]}\n", hDst)
+    elif xArgs.interactive_file_to_file:
+        sFile = xArgs.interactive_file_to_file
+        hDst = open(sFile[:sFile.rfind(".")]+".res.txt", "w", encoding="utf-8", newline="\n")
+        for i, sText, lLineSet in generateParagraphFromFile(sFile, xArgs.concat_lines):
+            if xArgs.textformatter:
+                sText = oTextFormatter.formatText(sText)
+            while True:
+                sResult, lErrors = oGrammarChecker.getParagraphWithErrors(sText, bEmptyIfNoErrors=False, bSpellSugg=True, nWidth=xArgs.width)
+                print("\n\n============================== Paragraph " + str(i) + " ==============================\n")
+                echo(sResult)
+                print("\n")
+                vCommand = getCommand()
+                if vCommand == "q":
+                    # quit
+                    hDst.close()
+                    exit()
+                elif vCommand == "n":
+                    # next paragraph
+                    hDst.write(sText)
+                    break
+                else:
+                    nError, cAction, vSugg = vCommand
+                    if 0 <= nError <= len(lErrors) - 1:
+                        dErr = lErrors[nError]
+                        if cAction == ">"  and  0 <= vSugg <= len(dErr["aSuggestions"]) - 1:
+                            sSugg = dErr["aSuggestions"][vSugg]
+                            sText = sText[0:dErr["nStart"]] + sSugg + sText[dErr["nEnd"]:]
+                        elif cAction == "=":
+                            sText = sText[0:dErr["nStart"]] + vSugg + sText[dErr["nEnd"]:]
+                        else:
+                            print("Error. Action not possible.")
+                    else:
+                        print("Error. This error doesn’t exist.")
     else:
         # pseudo-console
         sInputText = "\n~==========~ Enter your text [/h /q] ~==========~\n"
         sText = _getText(sInputText)
         while True:
@@ -285,15 +342,15 @@
                         echo(txt.getReadableErrors(dSentence["lGrammarErrors"], xArgs.width))
             else:
                 for sParagraph in txt.getParagraph(sText):
                     if xArgs.textformatter:
                         sText = oTextFormatter.formatText(sParagraph)
-                    sRes = oGrammarChecker.generateParagraph(sParagraph, bEmptyIfNoErrors=xArgs.only_when_errors, nWidth=xArgs.width, bDebug=xArgs.debug)
+                    sRes, _ = oGrammarChecker.getParagraphWithErrors(sParagraph, bEmptyIfNoErrors=xArgs.only_when_errors, nWidth=xArgs.width, bDebug=xArgs.debug)
                     if sRes:
                         echo("\n" + sRes)
                     else:
                         echo("\nNo error found.")
             sText = _getText(sInputText)
 
 
 if __name__ == '__main__':
     main()

Index: grammalecte-server.py
==================================================================
--- grammalecte-server.py
+++ grammalecte-server.py
@@ -32,11 +32,11 @@
     sJSON = '{ "program": "grammalecte-fr", "version": "'+oGCE.version+'", "lang": "'+oGCE.lang+'", "error": "'+sError+'", "data" : [\n'
     sDataJSON = ""
     for i, sParagraph in enumerate(txt.getParagraph(sText), 1):
         if bFormatText:
             sParagraph = oTextFormatter.formatText(sParagraph)
-        sResult = oGrammarChecker.generateParagraphAsJSON(i, sParagraph, dOptions=dOptions, bEmptyIfNoErrors=True, bReturnText=bFormatText)
+        sResult = oGrammarChecker.getParagraphErrorsAsJSON(i, sParagraph, dOptions=dOptions, bEmptyIfNoErrors=True, bReturnText=bFormatText)
         if sResult:
             if sDataJSON:
                 sDataJSON += ",\n"
             sDataJSON += sResult
     sJSON += sDataJSON + "\n]}\n"