From: Hirohito Higashi Date: Tue, 13 Jan 2026 21:22:27 +0000 (+0000) Subject: CI: Add C preproc indentation check to CI X-Git-Tag: v9.1.2084~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1d4fe89054ac15ce19c2ed884bf013640a836bb5;p=thirdparty%2Fvim.git CI: Add C preproc indentation check to CI closes: #19165 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- diff --git a/.github/MAINTAINERS b/.github/MAINTAINERS index 048445721b..733c9af1c9 100644 --- a/.github/MAINTAINERS +++ b/.github/MAINTAINERS @@ -699,6 +699,7 @@ runtime/syntax/xs.vim @petdance runtime/syntax/xslt.vim @Boobies runtime/syntax/zserio.vim @dpelle runtime/syntax/zsh.vim @chrisbra +runtime/tools/preproc_indent.vim @h-east runtime/tutor/tutor1.eo @dpelle runtime/tutor/tutor1.fr @dpelle runtime/tutor/tutor1.ru @RestorerZ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1faa02737..6759ec9a02 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,7 +84,7 @@ jobs: architecture: arm64 - features: normal compiler: gcc - extra: [vimtags, proto] + extra: [vimtags, proto, preproc_indent] - features: huge compiler: gcc extra: [no_x11_wl] @@ -363,6 +363,16 @@ jobs: true ) + - name: Check preprocessor indent + if: contains(matrix.extra, 'preproc_indent') + run: | + # This will exit with an error code if the files differ from source + ( + "${SRCDIR}"/vim -u NONE --not-a-term -esNX +"cd runtime/tools" -S preproc_indent.vim + git diff --exit-code -- src/*.[ch] src/xxd/xxd.c + true + ) + - name: Generate gcov files if: matrix.coverage run: | diff --git a/runtime/tools/README.txt b/runtime/tools/README.txt index 19976b325c..1758ca6533 100644 --- a/runtime/tools/README.txt +++ b/runtime/tools/README.txt @@ -16,6 +16,9 @@ pltags.pl: Perl script to create a tags file from Perl scripts. ref: Shell script for the K command. +preproc_indent.vim: + Fix preprocessor indentation in Vim's C source code. + shtags.*: Perl script to create a tags file from a shell script. vim132: Shell script to edit in 132 column mode on vt100 compatible diff --git a/runtime/tools/preproc_indent.vim b/runtime/tools/preproc_indent.vim new file mode 100644 index 0000000000..035ca0e247 --- /dev/null +++ b/runtime/tools/preproc_indent.vim @@ -0,0 +1,148 @@ +vim9script +# Script to fix preprocessor indentation in Vim's C source code. +# +# Usage: Vim -S +# +# 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 + var list_of_c_files: list = [] + 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 = 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