Skip to content

Correctifs pour le filtre `|couper`

Glop a demandé de fusionner gh-3abd0771/5422/unknown/refs/pull/5422/head vers master

Bonjour,

Après avoir rencontré des problèmes avec le plugin Mastodon et des pouets qui se retrouvaient incorrectement tronqués, je me suis rendu compte qu'il y avait quelques soucis avec le filtre |couper, et que celui-ci pouvait parfois renvoyer des chaînes de caractères plus longues que la longueur qui lui était spécifiée.

Voici donc un ensemble de correctifs pour tenter de remédier aux problèmes détectés. Dans l'ordre des commits :

  • Ne pas décompter les caractères UTF-8 : les fonctions spip_substr() et spip_strlen() gèrent déjà correctement les chaînes UTF-8. Il n'y a donc pas besoin de corriger la longueur de coupe en lui rajoutant des caractères supplémentaires pour les caractères UTF-8 (cf. code ici).

    Ainsi, par exemple :

    [(#VAL{Tést àvéc plêïn d’àççènts bla bli blu}|couper{31})]

    renvoie

    • jusqu'à eebacfbe inclus : Tést àvéc plêïn d’àççènts bla (...) (35 caractères → trop long) ;
    • après ce correctif : Tést àvéc plêïn d’àççènts (...) (31 caractères → OK).

    Cela rejoint le bug rapporté par @Fil dans #255 (closed) (dans les commentaires).

  • Gérer le cas où l'on coupe dès le premier mot : le filtre tente de ne pas tronquer le dernier mot si la coupe tombe au milieu de celui-ci. Pour cela, il essaie de couper au mot précédent. Mais lorsque le premier mot est long (ou que la longueur de coupe est courte), il se peut que l'on coupe au milieu du premier mot, auquel cas il ne faut pas couper au mot précédent mais carrément en début de chaîne. Les mécanismes suivants pour s'assurer que la chaîne coupée n'est pas trop courte feront ensuite en sorte que la coupe se fasse au caractère, si nécessaire.

    Ainsi, par exemple :

    [(#VAL{Supercalifragilisticexpialidocious}|couper{20})]

    renvoie

    • jusqu'à eebacfbe inclus : Supercalifragili (...) (22 caractères → trop long, et le filtre n'est pas censé ajouter les points de suite (...) lors d'une coupe au caractère) ;
    • après ce correctif : Supercalifragilistic (20 caractères → OK).
  • Prendre en compte la longueur du texte de suite et mieux détecter le dernier mot : jusqu'à présent, le filtre supposait que le texte de suite faisait exactement 5 caractères, indépendamment de sa véritable taille. Qui plus est, le texte de suite par défaut  (...) fait 6 caractères et non pas 5 (car il faut aussi compter l'espace insécable), ce qui fait que la longueur de la chaîne retournée était parfois incorrecte.

    Ainsi, par exemple :

    [(#VAL{Un nouveau test du filtre |couper bla bli blu}|couper{42})]

    renvoie

    • jusqu'à eebacfbe inclus : Un nouveau test du filtre |couper bla (...) (43 caractères → trop long) ;
    • après ce correctif : Un nouveau test du filtre |couper (...) (39 caractères → OK).

    Aussi, lorsque la coupe tombait juste après un mot, l'expression régulière employée dans le preg_replace() suivant considérait ce mot comme tronqué car non suivi d'une espace. Afin de pouvoir détecter les coupes en fin de mot, il faut en fait couper avec un caractère de plus et modifier l'expression régulière en conséquence.

  • Ne pas couper si le texte est déjà à la bonne taille : plutôt que de reposer sur un marqueur de fin de texte (\n), autant détecter dès le début si le texte est déjà à la longueur demandée. Cela évite en particulier des cas où le texte est inutilement coupé juste parce que la coupe retire le marqueur de fin pour laisser de la place pour les points de suite.

    Ainsi, par exemple :

    [(#VAL{Encore un test du filtre |couper bla bli blu}|couper{44})]

    renvoie

    • jusqu'à eebacfbe inclus : Encore un test du filtre |couper bla (...) (42 caractères → OK, mais coupe inutile) ;
    • après ce correctif : Encore un test du filtre |couper bla bli blu (44 caractères → OK, sans coupe inutile).
  • Toujours compter deux caractères pour les sauts de paragraphes : pendant le calcul de la coupe du texte, les sauts de paragraphes sont représentés par des \r (un caractère) mais, à l'issue du filtre, ils sont remplacés par \n\n (deux caractères). Par conséquent, cela fausse le calcul de la coupe et peut renvoyer un texte plus long que demandé.

    Ainsi, par exemple :

    [(#VAL{<p>Un test du filtre |couper</p><p>avec deux paragraphes</p>}|couper{41})]

    renvoie

    • jusqu'à eebacfbe inclus : Un test du filtre |couper\n\navec deux (...) (42 caractères → trop long) ;
    • après ce correctif : Un test du filtre |couper\n\navec (...) (37 caractères → OK).

Ces correctifs ont été testés sur plusieurs exemples à l'aide du squelette suivant (grandement inspiré de celui de @Fil dans #255 (closed)), qui itère le filtre sur un texte donné sur toutes les longueurs de coupe (jusqu'à 10 de plus que la taille du texte) et qui indique la longueur du texte coupé (la longueur du texte coupé est affichée en vert si elle est exactement égale à la longueur de coupe, en noir si elle est strictement inférieure, et en rouge si elle est strictement supérieure) :

[(#SET{str,La chaîne de caractères à tester})]
<pre>
<BOUCLE_iter(DATA){enum 1,#GET{str}|filtrer_entites|spip_strlen|plus{10}}>[(#REM)
][(#SET{sub,#GET{str}|couper{#VALEUR}})
][(#SET{len,#GET{sub}|filtrer_entites|spip_strlen})
][(#SET{col,#GET{len}|<{#VALEUR}|sinon{#GET{len}|=={#VALEUR}|?{'#0a0','#f00'}}})
]|couper{#VALEUR}  '#GET{sub}' [(#REM)
](<span[ style="color: (#GET{col});"]>#GET{len}</span>)
</BOUCLE_iter>
</pre>

Dans ces correctifs, le seul objectif était de faire en sorte que le filtre renvoie un texte de longueur toujours inférieure ou égale à la longueur de coupe demandée. Pour éviter de trop changer le comportement du filtre, je n'ai donc pas touché au reste, et en particulier à certains comportements surprenants et non documentés :

  • J'ai laissé le comportement qui fait que le filtre remplace les sauts de ligne simples (<br> ou \n) par des espaces (alors qu'il préserve les sauts de paragraphes).
  • J'ai aussi laissé le comportement du choix de quand mettre ou non les points de suite (...) après la coupe : contrairement à ce qui est documenté, ce choix ne dépend pas tant de la longueur de l'extrait que du ratio entre la longueur de coupe demandée (avec les points de suite) et la longueur de l'extrait (sans les points de suite) (cf. code ici). Ainsi, avec le texte Un test avec bla bli blu, le squelette ci-dessus va afficher :
    […]
    |couper{20} → 'Un test avec bla bli' (20)
    |couper{21} → 'Un test avec bla bli' (20)
    |couper{22} → 'Un test avec bla (...)' (22)
    |couper{23} → 'Un test avec bla bli' (20)
    […]
    Dans l'exemple ci-dessus, le choix de mettre les points de suite pour une longueur de coupe de 22 (mais pas pour 23) est non seulement lié au fait que l'extrait Un test avec bla  fait 17 caractères, mais aussi que 0,75 × 22 = 16,5 < 17 (alors que 0,75 × 23 = 17,25 > 17).

Mais si vous pensez que cette PR peut aussi être l'occasion de corriger ces comportements, je serai ravi d'y ajouter quelques commits de plus ! :) Et je reste bien entendu disponible pour modifier cette PR si elle ne convient pas ou si j'y ai introduit des erreurs.

Pardon pour ce long message, en tout cas. J'espère que vous aurez la patience de lire jusque-là ! Merci d'avance ! :)

++
Glop

Rapports de requête de fusion