CI: Add C preproc indentation check to CI

Commit: 
https://github.com/vim/vim/commit/1d4fe89054ac15ce19c2ed884bf013640a836bb5
Author: Hirohito Higashi <[email protected]>
Date:   Tue Jan 13 21:22:27 2026 +0000

    CI: Add C preproc indentation check to CI
    
    closes: https://github.com/vim/vim/issues/19165
    
    Signed-off-by: Hirohito Higashi <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/.github/MAINTAINERS b/.github/MAINTAINERS
index 048445721..733c9af1c 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 b1faa0273..6759ec9a0 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 19976b325..1758ca653 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 000000000..035ca0e24
--- /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 <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

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1vflxs-00BVnc-RH%40256bit.org.

Raspunde prin e-mail lui