Index: CHANGELOG.txt ================================================================== --- CHANGELOG.txt +++ CHANGELOG.txt @@ -22,16 +22,16 @@ Lexicographer French Conjugueur ## 0.4 Suggestion mechanisms - Simplier user options writing + Simpler user options writing Application Launcher Author field edition ## 0.5 Grammalecte is an autonomous package, free from Hunspell and LibreOffice Indexable binary dictionary (DAWG-FSA) generator Disambiguator Multi-actions rules (bi-passes engine again) - Simplier options for word boundaries + Simpler options for word boundaries Unit tests Index: LICENSE.fr.txt ================================================================== --- LICENSE.fr.txt +++ LICENSE.fr.txt @@ -113,11 +113,11 @@ vous assurer qu’eux aussi reçoivent ou peuvent recevoir son code source. Et vous devez leur montrer les termes de cette licence afin qu’ils connaissent leurs droits. Les développeurs qui utilisent la GPL GNU protègent vos droits en deux -étapes : (1) ils affirment leur droits d’auteur (“copyright”) sur le +étapes : (1) ils affirment leurs droits d’auteur (“copyright”) sur le logiciel, et (2) vous accordent cette Licence qui vous donne la permission légale de le copier, le distribuer et/ou le modifier. Pour la protection des développeurs et auteurs, la GPL stipule clairement qu’il n’y a pas de garantie pour ce logiciel libre. Aux fins @@ -130,11 +130,11 @@ à l’installation ou l’exécution de versions modifiées du logiciel à l’intérieur de ces dispositifs, alors que les fabricants le peuvent. Ceci est fondamentalement incompatible avec le but de protéger la liberté des utilisateurs de modifier le logiciel. L’aspect systématique de tels abus se produit dans le secteur des produits destinés aux -utilisateurs individuels, ce qui est précidément ce qui est le plus +utilisateurs individuels, ce qui est précisément ce qui est le plus inacceptable. Aussi, nous avons conçu cette version de la GPL pour prohiber cette pratique pour ces produits. Si de tels problèmes surviennent dans d’autres domaines, nous nous tenons prêt à étendre cette restriction à ces domaines dans de futures versions de la GPL, autant qu’il sera nécessaire pour protéger la liberté des utilisateurs. @@ -357,16 +357,16 @@ Une compilation d’un Travail Couvert avec d’autres travaux séparés et indépendants, qui ne sont pas par leur nature des extensions du Travail Couvert, et qui ne sont pas combinés avec lui de façon à former un programme plus large, dans ou sur un volume de stockage ou un support -de distribution, est appelé un « aggrégat » si la compilation et son +de distribution, est appelé un « agrégat » si la compilation et son Droit d’Auteur résultant ne sont pas utilisés pour limiter l’accès ou -les droits légaux des utilisateurs de la compilation en deça de ce que +les droits légaux des utilisateurs de la compilation en deçà de ce que permettent les travaux individuels. L’inclusion d’un Travail Couvert -dans un aggrégat ne cause pas l’application de cette Licence aux -autres parties de l’aggrégat. +dans un agrégat ne cause pas l’application de cette Licence aux +autres parties de l’agrégat. Article 6. Acheminement des formes non sources. Vous pouvez acheminer sous forme de code objet un Travail Couvert @@ -392,11 +392,11 @@ de la source, ou soit (2) un accès permettant de copier le Source Correspondant depuis un serveur réseau sans frais. c) Acheminer des copies individuelles du code objet avec une copie de l’offre écrite de fournir le Source Correspondant. Cette - alternative est permise seulement occasionellement et non + alternative est permise seulement occasionnellement et non commercialement, et seulement si vous avez reçu le code objet avec une telle offre, en accord avec l’article 6 alinéa b. d) Acheminer le code objet en offrant un accès depuis un emplacement désigné (gratuit ou contre facturation) et offrir un accès @@ -418,11 +418,11 @@ d’égal-à-égal, pourvu que vous informiez les autres participants sur où le code objet et le Source Correspondant du travail sont offerts sans frais au public général suivant l’article 6 alinéa d. Une portion séparable du code objet, dont le code source est exclu du Source Correspondant en tant que Bibliothèque Système, n’a pas - besoin d’être inclu dans l’acheminement du travail sous forme de + besoin d’être inclus dans l’acheminement du travail sous forme de code objet. Un « Produit Utilisateur » est soit (1) un « Produit de Consommation, » ce qui signifie toute propriété personnelle tangible normalement utilisée à des fins personnelles, familiales ou relatives au foyer, @@ -457,11 +457,11 @@ Utilisateur est transféré au Destinataire définitivement ou pour un terme fixé (indépendamment de la façon dont la transaction est caractérisée), le Source Correspondant acheminé selon cet article-ci doit être accompagné des Informations d’Installation. Mais cette obligation ne s’applique pas si ni vous ni aucune tierce partie ne -détient la possibilité d’intaller un code objet modifié sur le Produit +détient la possibilité d’installer un code objet modifié sur le Produit Utilisateur (par exemple, le travail a été installé en mémoire morte). L’obligation de fournir les Informations d’Installation n’inclue pas celle de continuer à fournir un service de support, une garantie ou des mises à jour pour un travail qui a été modifié ou installé par le @@ -479,23 +479,23 @@ la copie. Article 7. Termes additionnels. -Les « permissions additionelles » désignent les termes qui +Les « permissions additionnelles » désignent les termes qui supplémentent ceux de cette Licence en émettant des exceptions à l’une ou plusieurs de ses conditions. Les permissions additionnelles qui sont applicables au Programme entier doivent être traitées comme si -elles étaient incluent dans cette Licence, dans les limites de leur +elles étaient incluses dans cette Licence, dans les limites de leur validité suivant la loi applicable. Si des permissions additionnelles s’appliquent seulement à une partie du Programme, cette partie peut être utilisée séparément suivant ces permissions, mais le Programme tout entier reste gouverné par cette Licence sans regard aux -permissions additionelles. +permissions additionnelles. Quand vous acheminez une copie d’un Travail Couvert, vous pouvez à -votre convenance ôter toute permission additionelle de cette copie, ou +votre convenance ôter toute permission additionnelle de cette copie, ou de n’importe quelle partie de celui-ci. (Des permissions additionnelles peuvent être rédigées de façon à requérir leur propre suppression dans certains cas où vous modifiez le travail.) Vous pouvez placer les permissions additionnelles sur le matériel acheminé, ajoutées par vous à un Travail Couvert pour lequel vous avez ou pouvez @@ -664,11 +664,11 @@ à cette partie. Si vous acheminez un Travail Couvert, dépendant en connaissance d’une licence de brevet, et si le Source Correspondant du travail n’est pas disponible à quiconque copie, sans frais et suivant les termes de cette -Licence, à travers un serveur réseau publiquement acessible ou tout +Licence, à travers un serveur réseau publiquement accessible ou tout autre moyen immédiatement accessible, alors vous devez soit (1) rendre la Source Correspondante ainsi disponible, soit (2) vous engager à vous priver pour vous-même du bénéfice de la licence de brevet pour ce travail particulier, soit (3) vous engager, d’une façon consistante avec les obligations de cette Licence, à étendre la licence de brevet @@ -714,11 +714,11 @@ Si des conditions vous sont imposées (que ce soit par décision judiciaire, par un accord ou autrement) qui contredisent les conditions de cette Licence, elles ne vous excusent pas des conditions de cette Licence. Si vous ne pouvez pas acheminer un Travail Couvert de façon à -satisfaire simulténément vos obligations suivant cette Licence et +satisfaire simultanément vos obligations suivant cette Licence et toutes autres obligations pertinentes, alors en conséquence vous ne pouvez pas du tout l’acheminer. Par exemple, si vous avez un accord sur des termes qui vous obligent à collecter pour le réacheminement des royalties depuis ceux à qui vous acheminez le Programme, la seule façon qui puisse vous permettre de satisfaire à la fois à ces termes et ceux @@ -762,12 +762,12 @@ versions futures de la Licence Générale Publique GNU peut être utilisée, la déclaration publique d’acceptation d’une version par cet intermédiaire vous autorise à choisir cette version pour le Programme. Des versions ultérieures de la licence peuvent vous donner des -permissions additionelles ou différentes. Cependant aucune obligation -additionelle n’est imposée à l’un des auteurs ou titulaires de Droit +permissions additionnelles ou différentes. Cependant aucune obligation +additionnelle n’est imposée à l’un des auteurs ou titulaires de Droit d’Auteur du fait de votre choix de suivre une version ultérieure. Article 15. Déclaration d’absence de garantie. Index: THANKS.txt ================================================================== --- THANKS.txt +++ THANKS.txt @@ -1,11 +1,11 @@ # THANKS ## Thanks to all who contributed to the project László Németh (creator of Lightproof, from which Grammalecte forked), -Dominique Pelé (dominiko), +Dominique Pellé (dominiko), Jean-Luc T. (Tbj), Pierre Choffardet (pitpit), ## Thanks to all those who supported us @@ -179,6 +179,7 @@ Yann Asset, Yann Brelière, Yannick Geynet, Yelin -et aux centaines de contributeurs qui ont préféré garder l’anonymat, ainsi qu’à la GPL3 de Richard Matthew Stallman. +and to hundred of supporters who prefered to keep their anonymity, +and to the GPL3 by Richard Matthew Stallman. Index: compile_rules.py ================================================================== --- compile_rules.py +++ compile_rules.py @@ -449,11 +449,11 @@ del lRule[-1] # tGroups positioning codes are useless for Python # error messages for aAction in lRuleJS[6]: if aAction[1] == "-": aAction[2] = aAction[2].replace(" ", " ") # nbsp --> nnbsp - aAction[4] = aAction[4].replace("« ", "« ").replace(" »", " »") + aAction[4] = aAction[4].replace("« ", "« ").replace(" »", " »") # js regexes lRuleJS[1], lNegLookBehindRegex = regex2js( dJSREGEXES.get(lRuleJS[3], lRuleJS[1]) ) lRuleJS.append(lNegLookBehindRegex) return lRuleJS Index: gc_core/js/helpers.js ================================================================== --- gc_core/js/helpers.js +++ gc_core/js/helpers.js @@ -30,10 +30,19 @@ } else { console.error(sMsg); } } +function inspect (o) { + let sMsg = "__inspect__: " + typeof o; + for (let sParam in o) { + sMsg += "\n" + sParam + ": " + o.sParam; + } + sMsg += "\n" + JSON.stringify(o) + "\n__end__"; + echo(sMsg); +} + // load ressources in workers (suggested by Mozilla extensions reviewers) // for more options have a look here: https://gist.github.com/Noitidart/ec1e6b9a593ec7e3efed // if not in workers, use sdk/data.load() instead function loadFile (spf) { @@ -78,11 +87,12 @@ obj[k] = v; } return obj; } +exports.setLogOutput = setLogOutput; exports.echo = echo; exports.logerror = logerror; +exports.inspect = inspect; exports.objectToMap = objectToMap; exports.mapToObject = mapToObject; -exports.setLogOutput = setLogOutput; exports.loadFile = loadFile; Index: gc_core/js/text.js ================================================================== --- gc_core/js/text.js +++ gc_core/js/text.js @@ -37,19 +37,20 @@ } function getReadableError (oErr) { // Returns an error oErr as a readable error try { - let s = "\n* " + oErr['nStart'] + ":" + oErr['nEnd'] + " # " + oErr['sRuleId']+":\n"; - s += " " + oErr["sMessage"]; + let sResult = "\n* " + oErr['nStart'] + ":" + oErr['nEnd'] + + " # " + oErr['sLineId'] + " # " + oErr['sRuleId'] + ":\n"; + sResult += " " + oErr["sMessage"]; if (oErr["aSuggestions"].length > 0) { - s += "\n > Suggestions : " + oErr["aSuggestions"].join(" | "); + sResult += "\n > Suggestions : " + oErr["aSuggestions"].join(" | "); } if (oErr["URL"] !== "") { - s += "\n > URL: " + oErr["URL"]; + sResult += "\n > URL: " + oErr["URL"]; } - return s; + return sResult; } catch (e) { helpers.logerror(e); return "\n# Error. Data: " + oErr.toString(); } Index: gc_core/js/tokenizer.js ================================================================== --- gc_core/js/tokenizer.js +++ gc_core/js/tokenizer.js @@ -3,17 +3,16 @@ "use strict"; const helpers = require("resource://grammalecte/helpers.js"); - const aPatterns = { // All regexps must start with ^. "default": [ [/^[ \t]+/, 'SPACE'], - [/^[,.;:!?…«»“”"()/·]+/, 'SEPARATOR'], + [/^[,.;:!?…«»“”‘’"(){}\[\]/·–—]+/, 'SEPARATOR'], [/^(?:https?:\/\/|www[.]|[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_]+[@.][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_]+[@.])[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_.\/?&!%=+*"'@$#-]+/, 'LINK'], [/^[#@][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_-]+/, 'TAG'], [/^<[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+.*?>|<\/[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+ *>/, 'HTML'], [/^\[\/?[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+\]/, 'PSEUDOHTML'], [/^&\w+;(?:\w+;|)/, 'HTMLENTITY'], @@ -22,19 +21,19 @@ [/^[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+(?:[’'`-][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+)*/, 'WORD'] ], "fr": [ [/^[ \t]+/, 'SPACE'], - [/^[,.;:!?…«»“”"()/·]+/, 'SEPARATOR'], + [/^[,.;:!?…«»“”‘’"(){}\[\]/·–—]+/, 'SEPARATOR'], [/^(?:https?:\/\/|www[.]|[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_]+[@.][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_]+[@.])[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_.\/?&!%=+*"'@$#-]+/, 'LINK'], [/^[#@][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_-]+/, 'TAG'], [/^<[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+.*?>|<\/[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+ *>/, 'HTML'], [/^\[\/?[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+\]/, 'PSEUDOHTML'], [/^&\w+;(?:\w+;|)/, 'HTMLENTITY'], [/^(?:l|d|n|m|t|s|j|c|ç|lorsqu|puisqu|jusqu|quoiqu|qu)['’`]/i, 'ELPFX'], [/^\d\d?[hm]\d\d\b/, 'HOUR'], - [/^\d+(?:er|nd|e|de|ième|ème|eme)\b/, 'ORDINAL'], + [/^\d+(?:er|nd|e|de|ième|ème|eme)s?\b/, 'ORDINAL'], [/^-?\d+(?:[.,]\d+|)/, 'NUM'], [/^[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+(?:[’'`-][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+)*/, 'WORD'] ] } @@ -44,11 +43,11 @@ constructor (sLang) { this.sLang = sLang; if (!aPatterns.hasOwnProperty(sLang)) { this.sLang = "default"; } - this.aRules = aPatterns[sLang]; + this.aRules = aPatterns[this.sLang]; }; * genTokens (sText) { let m; let i = 0; @@ -55,11 +54,17 @@ while (sText) { let nCut = 1; for (let [zRegex, sType] of this.aRules) { try { if ((m = zRegex.exec(sText)) !== null) { - yield { "sType": sType, "sValue": m[0], "nStart": i, "nEnd": i + m[0].length } + if (sType == 'SEPARATOR') { + for (let c of m[0]) { + yield { "sType": sType, "sValue": c, "nStart": i, "nEnd": i + m[0].length } + } + } else { + yield { "sType": sType, "sValue": m[0], "nStart": i, "nEnd": i + m[0].length } + } nCut = m[0].length; break; } } catch (e) { @@ -67,9 +72,19 @@ } } i += nCut; sText = sText.slice(nCut); } + }; + + getSpellingErrors (sText, oDict) { + let aSpellErr = []; + for (let oToken of this.genTokens(sText)) { + if (oToken.sType === 'WORD' && !oDict.isValidToken(oToken.sValue)) { + aSpellErr.push(oToken); + } + } + return aSpellErr; } } exports.Tokenizer = Tokenizer; Index: gc_lang/fr/config.ini ================================================================== --- gc_lang/fr/config.ini +++ gc_lang/fr/config.ini @@ -3,11 +3,11 @@ lang_name = French locales = fr_FR fr_BE fr_CA fr_CH fr_LU fr_MC fr_BF fr_CI fr_SN fr_ML fr_NE fr_TG fr_BJ country_default = FR name = Grammalecte implname = grammalecte -version = 0.5.17.2 +version = 0.5.18 author = Olivier R. provider = Dicollecte link = http://grammalecte.net description = Correcteur grammatical pour le français. extras = README_fr.txt Index: gc_lang/fr/modules-js/lexicographe.js ================================================================== --- gc_lang/fr/modules-js/lexicographe.js +++ gc_lang/fr/modules-js/lexicographe.js @@ -2,13 +2,15 @@ // License: MPL 2 "use strict"; ${string} +${map} const helpers = require("resource://grammalecte/helpers.js"); +const tkz = require("resource://grammalecte/tokenizer.js"); const _dTAGS = new Map ([ [':G', "[mot grammatical]"], [':N', " nom,"], @@ -155,10 +157,42 @@ ["m'en", " (me) pronom personnel objet + (en) pronom adverbial"], ["t'en", " (te) pronom personnel objet + (en) pronom adverbial"], ["s'en", " (se) pronom personnel objet + (en) pronom adverbial"] ]); +const _dSeparator = new Map ([ + ['.', "point"], + ['·', "point médian"], + ['…', "points de suspension"], + [':', "deux-points"], + [';', "point-virgule"], + [',', "virgule"], + ['?', "point d’interrogation"], + ['!', "point d’exclamation"], + ['(', "parenthèse ouvrante"], + [')', "parenthèse fermante"], + ['[', "crochet ouvrante"], + [']', "crochet fermante"], + ['{', "accolade ouvrante"], + ['}', "accolade fermante"], + ['-', "tiret"], + ['—', "tiret cadratin"], + ['–', "tiret demi-cadratin"], + ['«', "guillemet ouvrant (chevrons)"], + ['»', "guillemet fermant (chevrons)"], + ['“', "guillemet ouvrant double"], + ['”', "guillemet fermant double"], + ['‘', "guillemet ouvrant"], + ['’', "guillemet fermant"], + ['/', "signe de la division"], + ['+', "signe de l’addition"], + ['*', "signe de la multiplication"], + ['=', "signe de l’égalité"], + ['<', "inférieur à"], + ['>', "supérieur à"], +]); + class Lexicographe { constructor (oDict) { this.oDict = oDict; @@ -165,71 +199,58 @@ this._zElidedPrefix = new RegExp ("^([dljmtsncç]|quoiqu|lorsqu|jusqu|puisqu|qu)['’](.+)", "i"); this._zCompoundWord = new RegExp ("([a-zA-Zà-ö0-9À-Öø-ÿØ-ßĀ-ʯ]+)-((?:les?|la)-(?:moi|toi|lui|[nv]ous|leur)|t-(?:il|elle|on)|y|en|[mts][’'](?:y|en)|les?|l[aà]|[mt]oi|leur|lui|je|tu|ils?|elles?|on|[nv]ous)$", "i"); this._zTag = new RegExp ("[:;/][a-zA-Zà-ö0-9À-Öø-ÿØ-ßĀ-ʯ*][^:;/]*", "g"); }; - analyzeText (sText) { - sText = sText.replace(/[.,.?!:;…\/()\[\]“”«»"„{}–—#+*<>%=\n]/g, " ").replace(/\s+/g, " "); - let iStart = 0; - let iEnd = 0; - let sHtml = '
' + sWord + " : élément complexe indéterminé
' + sWord + " : nombre
" + m[1] + "’ : " + _dPFX.get(m[1].toLowerCase()) + "
" + sWord + " : " + this.formatTags(lMorph[0]) + "
" + sWord + "
' + sWord + " : absent du dictionnaire
\n"; - } - // suffixe d’un mot composé - if (m2) { - sHtml += "
-" + m2[2] + " : " + this._formatSuffix(m2[2].toLowerCase()) + "
' + e.message + '
'); + xGCPanel.port.emit("addMessage", 'bug', e.message); } xGCPanel.port.emit("end"); } @@ -569,19 +570,19 @@ let nParagraph = 0; // non empty paragraphs let sRes = ""; try { for (let sParagraph of text.getParagraph(sText)) { if (sParagraph.trim() !== "") { - sRes = await xGCEWorker.post('analyzeWords', [sParagraph]) - xLxgPanel.port.emit("addElem", sRes); + sRes = await xGCEWorker.post('getListOfElements', [sParagraph]); + xLxgPanel.port.emit("addParagraphElems", sRes); nParagraph += 1; } } - xLxgPanel.port.emit("addElem", ' '); + xLxgPanel.port.emit("addMessage", 'message', _("numberOfParagraphs") + " " + nParagraph); } catch (e) { - xLxgPanel.port.emit("addElem", ''+e.message+"
"); + xLxgPanel.port.emit("addMessage", 'bug', e.message); } xLxgPanel.port.emit("stopWaitIcon"); }