@@ -137,59 +137,40 @@ def extract_file_relpath(line: str) -> str:
137137 'stdatomic.h' ,
138138 'stdnoreturn.h' ,
139139 'threads.h' ,
140- 'uchar.h' ,
141- #
142- # POSIX
143- #
140+ 'uchar.h'
141+ ]
142+
143+ HEADERS_POSIX = [
144144 'aio.h' ,
145145 'libgen.h' ,
146146 'spawn.h' ,
147147 'sys/time.h' ,
148148 'arpa/inet.h' ,
149- 'limits.h' ,
150- 'stdarg.h' ,
151149 'sys/times.h' ,
152- 'assert.h' ,
153- 'locale.h' ,
154- 'stdbool.h' ,
155150 'sys/types.h' ,
156- 'complex.h' ,
157- 'math.h' ,
158- 'stddef.h' ,
159151 'sys/uio.h' ,
160152 'cpio.h' ,
161153 'monetary.h' ,
162- 'stdint.h' ,
163154 'sys/un.h' ,
164- 'ctype.h' ,
165155 'mqueue.h' ,
166- 'stdio.h' ,
167156 'sys/utsname.h' ,
168157 'dirent.h' ,
169158 'ndbm.h' ,
170- 'stdlib.h' ,
171159 'sys/wait.h' ,
172160 'dlfcn.h' ,
173161 'net/if.h' ,
174- 'string.h' ,
175162 'syslog.h' ,
176- 'errno.h' ,
177163 'netdb.h' ,
178164 'strings.h' ,
179165 'tar.h' ,
180166 'fcntl.h' ,
181167 'netinet/in.h' ,
182168 'stropts.h' ,
183169 'termios.h' ,
184- 'fenv.h' ,
185170 'netinet/tcp.h' ,
186171 'sys/ipc.h' ,
187- 'tgmath.h' ,
188- 'float.h' ,
189172 'nl_types.h' ,
190173 'sys/mman.h' ,
191- 'time.h' ,
192- 'fmtmsg.h' ,
193174 'poll.h' ,
194175 'sys/msg.h' ,
195176 'trace.h' ,
@@ -212,13 +193,8 @@ def extract_file_relpath(line: str) -> str:
212193 'iconv.h' ,
213194 'search.h' ,
214195 'sys/socket.h' ,
215- 'wchar.h' ,
216- 'inttypes.h' ,
217196 'semaphore.h' ,
218197 'sys/stat.h' ,
219- 'wctype.h' ,
220- 'iso646.h' ,
221- 'setjmp.h' ,
222198 'sys/statvfs.h' ,
223199 'wordexp.h' ,
224200 'langinfo.h' ,
@@ -287,6 +263,7 @@ def extract_file_relpath(line: str) -> str:
287263 'functional' ,
288264 'new' ,
289265 'string_view' ,
266+ 'format'
290267 #
291268 # C compatible headers in C++
292269 #
@@ -408,6 +385,7 @@ def select_pair_header(
408385def sort_includes (
409386 includes : typing .List [Include ], my_filename : str , config : 'Config' ,
410387) -> typing .List [typing .List [str ]]:
388+
411389 res : typing .List [typing .List [str ]] = [[] for _ in config .rules ]
412390
413391 if config .has_pair_header :
@@ -417,6 +395,7 @@ def sort_includes(
417395 line = inc .include_line
418396
419397 match = None
398+
420399 for i , matchers in enumerate (config .rules ):
421400 match = None
422401 for matcher in matchers :
@@ -432,6 +411,11 @@ def sort_includes(
432411 break
433412 if match :
434413 # print(f'write {line}')
414+ quote = config .rule_quotes [i ]
415+ if quote :
416+ current_quote = get_include_quote_style (line )
417+ if current_quote != quote :
418+ line = replace_include_quotes (line , quote )
435419 res [i ].append (line )
436420 break
437421
@@ -440,7 +424,8 @@ def sort_includes(
440424 # print(res)
441425
442426 for group in res :
443- group .sort (key = lambda x : (not x .endswith ('.h>' ), x ))
427+ group .sort (key = lambda line : (get_include_quote_style (line ), extract_file_relpath (line )))
428+
444429 return res
445430
446431
@@ -553,6 +538,26 @@ def include_realpath(
553538 f'broken compile_commands.json?' ,
554539 )
555540
541+ def get_include_quote_style (line : str ) -> str :
542+ if '<' in line and '>' in line :
543+ return 'angle'
544+ elif '"' in line :
545+ return 'quote'
546+ else :
547+ raise ValueError (f'Unknown quote style in line: { line } ' )
548+
549+ def replace_include_quotes (line : str , quote : str ) -> str :
550+ match = re .match (r'^\s*#include\s*([<"])([^>"]+)[>"]' , line )
551+ if not match :
552+ return line # невалидный #include — возвращаем как есть
553+
554+ filename = match .group (2 )
555+ if quote == 'angle' :
556+ return f'#include <{ filename } >'
557+ elif quote == 'quote' :
558+ return f'#include "{ filename } "'
559+ else :
560+ raise ValueError (f'Invalid quote specifier: { quote } ' )
556561
557562class Config :
558563 def __init__ (self , contents : dict ):
@@ -573,6 +578,8 @@ def __init__(self, contents: dict):
573578 out .append (MatcherPairHeader ())
574579 elif vname == '@std-cpp' :
575580 out .append (MatcherHardcoded (HEADERS_CXX ))
581+ elif vname == '@posix' :
582+ out .append (MatcherHardcoded (HEADERS_POSIX ))
576583 else :
577584 raise Exception (f'Unknown "virtual: { vname } ' )
578585 else :
@@ -581,6 +588,7 @@ def __init__(self, contents: dict):
581588
582589 self .rules = result
583590 self ._has_pair_header = has_pair_header
591+ self .rule_quotes = [rules .get ("quote" ) for rules in rules_matrix ]
584592
585593 def has_pair_header (self ) -> bool :
586594 return self ._has_pair_header
@@ -635,37 +643,39 @@ def do_handle_single_file(
635643
636644 assert orig_file_contents
637645
638- i = - 1 # for pylint
639646 has_pragma_once = False
640647 includes = []
648+ includes_end_line = len (orig_file_contents )
649+
641650 for i , line in enumerate (orig_file_contents ):
642- line = line .strip ()
651+ stripped = line .strip ()
643652
644- if is_pragma_once (line ):
653+ if is_pragma_once (stripped ):
645654 has_pragma_once = True
646655 continue
647656
648- if not is_include_or_empty (line ):
657+ if not is_include_or_empty (stripped ):
658+ includes_end_line = i
649659 break
650660
651- if not line . strip () :
661+ if not stripped :
652662 continue
663+
653664 abs_include = include_realpath_cached (
654665 filename ,
655666 filename_for_cc , line , compile_commands , realpath_cache ,
656667 )
657-
658668 orig_path = extract_file_relpath (line )
659669 includes .append (
660670 Include (
661- include_line = line , orig_path = orig_path , real_path = abs_include ,
671+ include_line = line ,
672+ orig_path = orig_path ,
673+ real_path = abs_include ,
662674 ),
663675 )
664676 if abs_include not in include_map .data :
665677 include_map .data [abs_include ] = filename
666678
667- assert i != - 1
668-
669679 sorted_includes = sort_includes (includes , filename , config )
670680
671681 tmp_filename = filename + '.tmp'
@@ -674,7 +684,7 @@ def do_handle_single_file(
674684 ofile .write ('#pragma once\n \n ' )
675685 write_includes (sorted_includes , ofile )
676686
677- for line in orig_file_contents [i + 1 :]:
687+ for line in orig_file_contents [includes_end_line :]:
678688 ofile .write (line )
679689 ofile .write ('\n ' )
680690 os .rename (src = tmp_filename , dst = filename )
@@ -740,16 +750,17 @@ def main():
740750 'Path to sort. Can be a file or a directory. If it is '
741751 'a directory, recursively traverse it. Can be used multiple times.'
742752 ),
753+ default = ["." ]
743754 )
744755 parser .add_argument (
745756 '--compile-commands' ,
746757 '-c' ,
747758 type = str ,
748- default = 'compile_commands.json' ,
759+ default = 'build_debug/ compile_commands.json' ,
749760 help = 'Path to "compile_commands.json" file.' ,
750761 )
751762 parser .add_argument (
752- '--config' , '-d' , type = str , help = 'Path to config file.' ,
763+ '--config' , '-d' , type = str , help = 'Path to config file.' , default = "/usr/local/bin/.sort-cpp-includes"
753764 )
754765 parser .add_argument (
755766 '--hpp-suffixes' , '-p' , type = str , default = '.hpp,.h' , help = 'TODO' ,
@@ -776,14 +787,15 @@ def process(args):
776787 config = read_config (args .config )
777788 include_map = IncludeMap (data = {})
778789
779- headers = collect_all_files (args .paths , suffixes + hpp_suffixes )
790+ files = collect_all_files (args .paths , suffixes + hpp_suffixes )
791+ print (f'files: { files } \n ' )
780792
781793 # process .cpp
782- for hdr in headers :
783- if has_suffix (hdr , suffixes ):
794+ for file in files :
795+ if has_suffix (file , suffixes ):
784796 handle_single_file (
785- hdr ,
786- hdr ,
797+ file ,
798+ file ,
787799 compile_commands ,
788800 args ,
789801 realpath_cache ,
@@ -792,16 +804,16 @@ def process(args):
792804 )
793805
794806 # process .hpp
795- for hdr in headers :
796- if has_suffix (hdr , hpp_suffixes ):
797- abs_path = os .path .abspath (hdr )
807+ for file in files :
808+ if has_suffix (file , hpp_suffixes ):
809+ abs_path = os .path .abspath (file )
798810 init_cpp = include_map .data .get (abs_path )
799811 if not init_cpp :
800- print (f'Error: no .cpp file includes "{ hdr } "' )
812+ print (f'Error: no .cpp file includes "{ file } "' )
801813 continue
802814
803815 handle_single_file (
804- hdr ,
816+ file ,
805817 init_cpp ,
806818 compile_commands ,
807819 args ,
0 commit comments