Journal Amélioration de la coloration syntaxique C dans vim

Posté par  (site web personnel, Mastodon) . Licence CC By‑SA.
Étiquettes :
25
14
août
2021

Ça fait longtemps que je ne suis pas satisfait d'un truc avec la coloration syntaxique du C dans vim. La coloration des types est basée sur la reconnaissance de mots clés (int, unsigned, uint32_t, …) et par conséquent elle ne fonctionne pas pour les types définis par l'utilisateur qui ne sont pas dans cette liste.

Il y a quelques années, un collègue qui avait le même problème m'a donné une copie de son fichier de syntaxe modifié pour corriger ça. Je l'utilise depuis et j'en était assez content. Il y a quelques autres bugs, mais voir les types des variables avec une couleur différente, c'est quand même le truc le plus utile dans la coloration syntaxique.

Et ce mois-ci, a priori après une mise à jour de vim, j'ai eu une mauvaise surprise: ça ne fonctionne plus du tout! Argh! Impossible de travailler sans cette coloration que j'utilise maintenant depuis trop longtemps pour pouvoir changer mes habitudes.

Je me suis donc enfin décidé à regarder comment ce fichier fonctionnait. Il est basé sur une version de vim de 1998 et a vécu sa vie indépendament des évolutions dans le fichier officiel. Il avait donc pas mal de problèmes avec le C99 et avec le C++.

J'ai donc décidé d'extraire seulement la partie concernant la coloration des types, et d'en faire un fichier séparé. Maintenant, je pourrai mettre à jour mon vim et bénéficier de cette coloration ET des évolutions du fichier de syntaxe officiel.

C'est loin d'être partfait et j'ai probablement cassé d'autres trucs en faisant cette version du fichier. Donc je me suis dit que j'allais le publier et que peut être quelqu'un de plus motivé que moi va se décider à corriger certains des problèmes posés. À moins que je ne me décide à le faire moi-même après avoir essayé de l'utiliser et trouvé que ça ne fonctionne vraiment pas assez bien.

Le fichier, ses instructions d"installation et une capture d'écran avant/après se trouvent ici: https://github.com/pulkomandy/c.vim

  • # up

    Posté par  (site web personnel) . Évalué à 9.

    Depuis le temps que c'est ainsi, as-tu une idée de la raison pour laquelle ce n'est fait directement par le fichier upstream ?

    Pourquoi pas leur remonter tes modifs ? J'imagine que s'il y a des problèmes flagrants ils te seraient alors rapportés par les devs, non ?

    • [^] # Re: up

      Posté par  (site web personnel, Mastodon) . Évalué à 8.

      C'était dans ma TODO list depuis longtemps.

      Le problème était que j'avais un fichier forké à partir d'une version de vim de 1998 avec plein de modifications pas très bien documentées, et c'était compliqué de reporter les changements sur la version actuelle du fichier qui a beaucoup évolué. Maintenant que c'est découpé dans un fichier séparé, ce sera plus simple.

      Il y avait (et je pense qu'il y a toujours) des bugs dans certains cas, surtout sur le code C++ (je viens de modifier les patterns pour accepter les "::" dans les types et dans les noms de fonctions, par exemple). Et il n'y a pas seulement des ratages sur la surbrillance des types, mais aussi (surtout) ça cause des régressions sur d'autres choses. Même si pour moi, avoir la coloration des types est plus importante que les autres trucs qui sont cassés, il est probable que tout le monde ne sera pas d'accord.

      Je ne pense pas que ce sera accepté par vim en l'état, et il faudra que j'essaie de corriger au moins les plus gros problèmes. Je vais installer cette nouvelle version sur mes différentes machines perso et pro et la tester pendant quelques jours pour voir ou ça en est, avant de proposer l'intégration dans vim.

    • [^] # Re: up

      Posté par  . Évalué à 10.

      Je ne sais pas à quel point les devs de vim acceptent les heuristiques pour la coloration, mais C est tellement ambigü que ça semble impossible de ne pas faire d'erreur.

      typedef int A; 
      A * B;

      déclare un pointeur de A, alors que

      int A = 2;
      int B = 3;
      A * B;

      fait une multiplication (et n'en fait rien, mais ça n'est pas un problème de syntaxe).

      Si les déclarations de variables et les typedef sont dans un autre fichier, ça semble très difficile de faire une coloration syntaxique correcte sans se planter dans une partie des cas.

      • [^] # Re: up

        Posté par  (site web personnel, Mastodon) . Évalué à 4.

        Au fait, il me semble que l'analyse de la syntaxe du code, c'est typiquement une des tâches du Langage Server Protocol proposé par VSCode. Il devrait être capable de résoudre ce genre de cas que tu as remonté.

        Ça paraît un peu lourd de sortir LSP pour de la coloration syntaxique, mais finalement, quand on ouvre des codes sources (en C/C++ ou autre), on va avoir rapidement besoin de l'aide d'un mini-IDE, non ?

        • [^] # Re: up

          Posté par  . Évalué à 6.

          J'ai l'impression que ça ne dépend que de la philosophie du projet. Vim est avant tout un éditeur de texte, et pas un IDE. S'il faut parser 100k lignes de code pour mettre des couleurs sur une fonction de 3 lignes (et ça va être le cas, si tu as quelques include standard), alors tu vas le payer en terme de réactivité et de légèreté.

          En ce qui me concerne, j'ai toujours utilisé un éditeur de texte léger et des terminaux pour coder, parce que j'ai l'impression que les IDE me font perdre plus de temps qu'ils m'en font gagner (mais bien sûr, c'est complètement personnel, et ça ne marche que sur les petits projets). Si la coloration syntaxique n'est pas instantanée (par exemple, s'il faut 1/2 s pour vérifier si A est un type ou une variable), alors on aura commencé à taper la suite avant de s'apercevoir qu'on a fait une erreur. On perd toute la légèreté et la réactivité de l'interface.

          Du coup, si j'étais Monsieur Vim, je me contenterais d'une analyse locale au fichier, en utilisant une heuristique naïve. C'est le genre d'heuristiques sur lesquelles les compilateurs se basent pour émettre des warnings (par exemple sur if (A = B) ).

          Sur le fond, c'est quand même vraiment bizarre que C ou C++ sont assez ambigüs pour ne pas être capables de déterminer si une ligne est une déclaration ou une instruction à exécuter sans avoir besoin de la totalité du contexte. Je comprends bien que c'est pour des raisons historiques et pragmatiques, mais quand on en est là, on peut quand même se poser des questions sur la cohérence du langage. Je ne pense pas que ça soit le job des éditeurs de texte de pallier les problèmes profonds des langages; quand la coloration syntaxique n'est pas possible, bah elle n'est pas possible, et puis c'est tout. C'est un peu comme si un langage permettait de lire le caractère qui indique un commentaire dans une variables globale ou un truc chelou comme ça; au bout d'un moment, il y a des choses qu'un éditeur de texte ne peut pas vraiment faire;

          • [^] # Re: up

            Posté par  (site web personnel) . Évalué à 8.

            J'ai l'impression que ça ne dépend que de la philosophie du projet. Vim est avant tout un éditeur de texte, et pas un IDE. S'il faut parser 100k lignes de code pour mettre des couleurs sur une fonction de 3 lignes (et ça va être le cas, si tu as quelques include standard), alors tu vas le payer en terme de réactivité et de légèreté.

            C'est pour ça que tu délègues à LSP, coc, ou un autre outil qui marche de manière asynchrone. Vim ne gagne pas en complexité (vu que la solution existe déjà).

        • [^] # Re: up

          Posté par  (site web personnel, Mastodon) . Évalué à 8.

          Ça existe aussi: https://github.com/jeaye/color_coded

          Gros problème avec cette approche: impossible de colorer correctement un fichier qui ne compile pas et qui ne peut donc pas être analysé par clang. Et donc impossible d'avoir la coloration d'une ligne de code qu'on est en train d'écrire.

          Ce qui fait que ça risque de clignoter dans tous les sens selon que le code compile ou pas.

      • [^] # Re: up

        Posté par  (site web personnel, Mastodon) . Évalué à 4. Dernière modification le 16 août 2021 à 15:09.

        (et n'en fait rien, mais ça n'est pas un problème de syntaxe)

        Je n'ai jamais eu ce cas dans du code sur lequel je travaille.

        Mais, effectivement, la détection est basée sur des expressions régulières et très approximatives.

        Parmi les choses qui peuvent poser problème:
        - Déclarer une variable dans les parenthèses d'une boucle for demande de gérer un cas particulier
        - Les instanciations de templates en C++ qui peuvent contenir un mélange de types et de valeurs
        - Les déclarations de fonctions avec des paramètres non nommés (int fonction(int);) qui sont compliqués à distinguer d'un appel de fonction
        - Les conventions de formatage qui utilisent des retours à la ligne à des endroits un peu inhabituels:

            int
            fonction(int)
            {
                return 42;
            }
        • Les extensions au langage C, par exemple l'utilisation de __attribute__ ou aussi en C++ les mot clés const, override, final qui peuvent se trouver à la fin d'une déclaration de fonction
        • Les lambdas en C++ (j'ai pas encore essayé de voir si c'était possible).

        Et sûrement plein d'autres auxquels je n'ai pas encore pensé. Donc, oui, c'est clairement pas parfait. Mais pour mon usage, l'amélioration de la lisibilité est quand même globalement positive.

        • [^] # Re: up

          Posté par  . Évalué à 1.

          T'aurais la force de nous fournir une petite copie d'écran de ta coloration en fonctionnement ?

          • [^] # Re: up

            Posté par  (site web personnel, Mastodon) . Évalué à 3.

            Il y a deux exemples dans le README du dépôt github dont le lien est à la fin du journal (avec un avant/après pour voir la différence).

            Exemple en mode sombre sur du code C

            Exemple en mode clair sur du C++

            • [^] # Re: up

              Posté par  (site web personnel) . Évalué à 3.

              Fait gaffe, t'as pas de return TRUE à la fin de AddSysROMObjects() :P

              Et je soupçonne que Group devrait être écrit ’group’ :)

              • [^] # Re: up

                Posté par  (site web personnel, Mastodon) . Évalué à 5.

                Fait gaffe, t'as pas de return TRUE à la fin de AddSysROMObjects() :P

                Ah oui j'ai brutalement découpé un petit morceau de la fonction pour faire cet exemple , en vrai elle est un peu plus longue et il y a bien le return à la fin.

                Et je soupçonne que Group devrait être écrit ’group’ :)

                En effet.

                Ce code vient de http://ace.cpcscene.net (dont j'ai pas trop parlé ici parce que c'est pas libre, choix de l'auteur original). Je m'occupe du portage pour Haiku et le mélange des conventions de codage n'est pas simple. Le code original est en C pour MorphOS avec une UI implémentée en C orienté objet avec plein de macros. L'interface graphique pour Haiku est écrite en partant de ces fichiers et en remplaçant tout ça par du C++ et en adaptant en partie le style. D'où parfois des variables ou paramètres qui ont gardé les conventions de la version originale. J'essaie de rester proche de l'original car ça me facilite la comparaison avec les versions suivantes pour intégrer les changements.

                On ne voit pas sur la capture d'écran le mélange de tabulations et d'espaces pour l'indentation, aussi…

                D'où l'intérêt d'avoir une bonne coloration syntaxique pour s'y retrouver, au moins le temps que je fasse un peu de nettoyage ;)

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.