1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
vim9script
# Script to fix preprocessor indentation in Vim's C source code.
#
# Usage: Vim -S <this-file>
#
# Specifications:
# - If there is no indentation on the line containing the preprocessor
# directive (`#`) following the first `#if~`, the indentation amount is
# `nesting level - 1` spaces. Otherwise, the indentation amount is `nesting
# level` spaces.
# - However, if a preprocessor directive line is detected after the
# corresponding `#endif` of the above `#if~`, the indentation amount is
# fixed at `nesting level` and the line is reprocessed from the first line.
# - If the preprocessor directive line ends with a line continuation (`\`) and
# the next line is blank, the line continuation (`\`) and the next line are
# deleted.
#
# Author: Hirohito Higashi (@h-east)
# Last Update: 2026 Jan 12
def Get_C_source_files(): list<string>
var list_of_c_files: list<string> = []
if empty(list_of_c_files)
var fpath = '../../src'
var list = glob(fpath .. '/*.[ch]', 0, 1) + [fpath .. '/xxd/xxd.c']
# Some files are auto-generated, so skip those
list_of_c_files = filter(list, (i, v) => v !~ 'dlldata.c\|if_ole.h\|iid_ole.c')
endif
return list_of_c_files
enddef
def FixPreprocessorIndent(fname: string)
execute 'edit! ' .. fname
var nest: number = 0
var indent_offset: number = 0 # -1 if whole-file guard detected
var first_if_seen: bool = false
var offset_determined: bool = false
var whole_file_guard_ended = false
# First pass: remove trailing backslash + empty next line
var lnum = 1
while lnum <= line('$')
var line: string = getline(lnum)
if line =~# '^\s*#.*\\$'
var next_line: string = getline(lnum + 1)
if next_line =~# '^\s*$'
# Remove backslash from current line and delete next line
setline(lnum, substitute(line, '\s*\\$', '', ''))
deletebufline('%', lnum + 1)
continue # Don't increment, check same line again
endif
endif
lnum += 1
endwhile
# Second pass: fix preprocessor indent
while true
var is_reprocess: bool = false
for l in range(1, line('$'))
var line: string = getline(l)
# Skip if not a preprocessor directive
if line !~# '^\s*#'
continue
endif
# Extract directive and current indent
var match_li: list<string> = matchlist(line, '^\(\s*\)#\(\s*\)\(\w\+\)')
if empty(match_li)
continue
endif
var cur_spaces: string = !empty(match_li[1]) ? match_li[1] : match_li[2]
var directive: string = match_li[3]
# If indent_offset != 0 but we encounter indented #, it's not whole-file
# guard. Reprocess from line 1 with indent_offset=0
if whole_file_guard_ended && offset_determined && indent_offset != 0
indent_offset = 0
nest = 0
is_reprocess = true
break
endif
# After first #if, determine offset from first nested directive
# Only check if # is at column 1 (no leading spaces)
if first_if_seen && !offset_determined
offset_determined = true
if empty(cur_spaces)
# No indent after first `#if` --> whole-file guard style
indent_offset = -1
endif
endif
# Determine expected indent based on directive type
var expected_indent: number
if directive ==# 'if' || directive ==# 'ifdef' || directive ==# 'ifndef'
if !first_if_seen
first_if_seen = true
endif
expected_indent = nest + indent_offset
nest += 1
elseif directive ==# 'elif' || directive ==# 'else'
expected_indent = nest - 1 + indent_offset
elseif directive ==# 'endif'
nest -= 1
if nest <= 0
# Reset for next top-level block (but keep offset_determined)
nest = 0
whole_file_guard_ended = true
endif
expected_indent = nest + indent_offset
else
# Other directives (#define, #include, #error, #pragma, etc.)
expected_indent = nest + indent_offset
endif
if expected_indent < 0
expected_indent = 0
endif
# Build expected line
var rest = substitute(line, '^\s*#\s*', '', '')
var expected_line: string
expected_line = '#' .. repeat(' ', expected_indent) .. rest
# Update line if different
if line !=# expected_line
setline(l, expected_line)
endif
endfor
if !is_reprocess
break
endif
endwhile
update
enddef
# Main
for fname in Get_C_source_files()
FixPreprocessorIndent(fname)
endfor
qall!
# vim: et ts=2 sw=0
|