.clang-format IncludeCategories



  • Hi, man findet erstaunlich wenig Beispiele zur Verwendung des Regex Mechanismus um mittels .clang-format Include Dateien zu sortieren.

    z.B.:

    IncludeCategories:
     - Regex: '.*/.*h'
       Priority: 2
     - Regex: '.*h'
       Priority: 1
    

    Leider bekomme ich das gewünschte Ergebnis auch nicht hin.
    Evtl. kann mir jemand helfen:

    Ziel:

    #include <a.h>
    #include <f.h>
    #include <b/a.h>
    #include <b/b.h>
    #include <c/x.h>
    #include <c/y.h>
    #include <c/a/a.h>
    #include <c/a/b.h>
    

    Jemand eine Idee ob das mittels Regex Möglich ist?


  • Mod

    Der Eingangsbeitrag war so undeutlich und die Frage im C++-Forum gestellt. Die Antowrt unten ist für C++ im Allgemeinen und nicht für irgendwelche Quellcodeformatierungswerkzeuge.

    Regex klingt nach dem falschen Mittel. Du willst etwas sortieren. Also wähle ein Vergleichskriterium. Das Vergleichskriterium, das ich aus deinem Beispiel ableite, ist:

    • Ein Includepfad ist eine Folge von "Pfadbestandteilen" getrennt durch /
    • Diese Pfadbestandteilen sind wiederum eine Folge von chars.
    • Kürzere Folgen von Pfadbestandteilen werden vor längere Folgen sortiert
    • Bei gleich langen Folgen wird das für Folgen übliche Vergleichkriterium angewandt (d.h. Vergleich der Folgenglieder von vorne nach hinten)
    • Das Untervergleichskriterium der Pfadbestandteile ist ein alphabetischer Vergleich von char-Folgen

    Das setzt du in Code um. Der Code ist wahrscheinlich sogar deutlich kürzer als meine umständliche formale Beschreibung 🙂



  • @SeppJ sagte in .clang-format IncludeCategories:

    Regex klingt nach dem falschen Mittel. Du willst etwas sortieren.

    Ich glaube die Idee hinter diesem clang-format-Feature ist eher Gruppierung als Sortierung. Da will man vielleicht zuerst projektinterne Includes, dann Header der Standardbibliothek und dann irgendwelche anderen Bibliotheken. Innerhalb der Gruppen dann einfach dumm nach Include-Pfad sortiert.

    @Enumerator Ist das nicht einfach nur eine einfache lexikographische Sortierung der Include-Pfade, die du da haben möchtest? Ich habe den Verdacht, dass IncludeCategories wohl das falsche Werkzeug ist. Wie wäre es einfach keine Kategorien zu definieren und SortIncludes zu aktivieren?

    SortIncludes (bool)
    
        If true, clang-format will sort #includes.
    
        false:                                 true:
        #include "b.h"                 vs.     #include "a.h"
        #include "a.h"                         #include "b.h"
    

  • Mod

    Hat diese Frage überhaupt mit C++ zu tun oder ist das eine Frage zur Benutzung eines Werkzeugs?

    PS: Wohl eher letzteres. Verschoben.



  • @Finnegan
    Weil die Standard Sortierung bei Dateipfaden eine Katastrophe ist. So wie ich das verstehe sind die "IncludeCategories" die Sortierregel für "SortIncludes". Leider bin ich kein Regex-Experte und online findet man auch erstaunlich wenig Beispiele um sich inspirieren zu lassen.



  • @Enumerator sagte in .clang-format IncludeCategories:

    @Finnegan
    Weil die Standard Sortierung bei Dateipfaden eine Katastrophe ist. So wie ich das verstehe sind die "IncludeCategories" die Sortierregel für "SortIncludes". Leider bin ich kein Regex-Experte und online findet man auch erstaunlich wenig Beispiele um sich inspirieren zu lassen.

    Jetzt, wo ich mir dein Beispiel nochmal anschaue, sehe ich auch, dass es eben keine lexikographische Sortierung ist. Du willst zuerst nach Anzahl der Pfad-Komponenten sortieren und erst dann lexikographisch, korrekt?

    Nun verstehe ich auch den Sinn hinter deinen Kategorien. Leider bin ich auch jemand, der bei Regulären Ausdrücken immer wieder jede Menge Try-And-Error macht, vor allem weil da oft sehr unterschiedliche Regex-Engines verwendet werden, die sich alle etwas unterscheiden.

    Es ist durchaus möglich, dass die Regulären Ausrücke hier nicht für den ganzen Include-String gelten, sondern dass es reicht, wenn der Audruck irgendwo in dem String gematcht werden kann, um einem Include eine bestimmte Kategorie zu geben. Das würde bedeuten, dass '.*/.*h' z.B. alle Includes matcht, die mindestens einen Slash enthalten, also z.B. auch #include <c/a/a.h> die nach deinem System eher Priorität 3 haben sollten, oder?

    Auch wird '.*h' wohl jedes deiner Includes matchen, da der Regex-Punkt eben auch den Slash beinhaltet. Wenn du Pech hast, landen also alle Includes in der Prorität-1-Kategorie.

    Ich habe keine Ahnung von der clang-format Regex-Engine, aber ich würde es (try-and-error) mal hiermit versuchen:

    Kategorie 1: '^(<|")[^/]*(>|")$' begint mit < oder ", beliebig langer String der keinen Slash enthält, endet auf > oder ".
    Kategorie 2: '^(<|")[^/]*/[^/]*(>|")$' Wie oben, nur dass exakt ein Slash im Include-String vorkommen muss.
    Kategorie 3: '^(<|")[^/]*/[^/]*/[^/]*(>|")$' Wie oben, allerdings mit 2 Slashes.
    ...
    etc.

    ^ am Anfang bedeutet Anfang des Strings und das $ am Ende steht für das Ende des Strings. Damit versuche ich zu erreichen, dass immer der komplette Include-Pfad gematcht wird und nicht nur ein Teilstring irgendwo in der Mitte. [^/]* Bedeutet ein beliebiges Zeichen mit Ausnahme eines Slash - beliegbig oft wiederholt (auch 0 mal!). Die <, > und " sind laut Doku Teil des Include-Strings, der gematch wird, daher habe ich sie hier ebenfalls eingebunden.



  • Genau, ich möchte sozusagen nach der Verzeichnis Hierarchie und dem Dateinamen sortieren. Habe es jetzt mal so versucht:

    IncludeCategories:
     - Regex: '^(<|")[^/]*(>|")$'
       Priority: 1
     - Regex: '^(<|")[^/]*/[^/]*(>|")$'
       Priority: 2
     - Regex: '^(<|")[^/]*/[^/]*/[^/]*(>|")$'
       Priority: 3
    

    Bzw. auch die inverse Reihenfolge, allerdings werden die Include-Verzeichnisse so noch falsch sortiert:

    #include "A.h"
    #include "B.h"
    #include "A/A.h"
    #include "A/B.h"
    #include "Z/A.h"
    #include "Z/B.h"
    #include "A/B/A.h"
    #include "A/B/B.h"
    #include "Z/Z/A.h"
    #include "Z/Z/B.h"
    

    So sollte es mal aussehen:

    #include "A.h"
    #include "B.h"
    #include "A/A.h"
    #include "A/B.h"
    #include "A/B/A.h"
    #include "A/B/B.h"
    #include "Z/A.h"
    #include "Z/B.h"
    #include "Z/Z/A.h"
    #include "Z/Z/B.h"
    

    Ich denke man bräuchte evtl. ein rekursives Regex :).


Anmelden zum Antworten