From 86a271de7e6f042f5075e1e707645d51887e4a4b Mon Sep 17 00:00:00 2001 From: Evgeny Date: Fri, 13 Feb 2026 03:12:06 +0300 Subject: [PATCH] Add description command and improve toc output - Add 'description' command for showing items with comments - toc: functions show signatures only, struct/enum without comments - description: shows functions with pre-comments, struct/enum with full formatting - Fix single-line /* */ comment extraction for functions - Struct/enum in description: tabs for indentation, remove empty lines --- c_util | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 139 insertions(+), 9 deletions(-) diff --git a/c_util b/c_util index e48c83f9..baf20df3 100755 --- a/c_util +++ b/c_util @@ -3,6 +3,7 @@ c_util - C code navigation utility Commands: toc - Show table of contents for all files + description - Show selected items with comments func - Show function code struct - Show struct definition enum - Show enum definition @@ -570,7 +571,7 @@ def get_function_signature(lines, func): return sig -def get_declaration_text(lines, decl): +def get_declaration_text(lines, decl, include_comments=False): """Get normalized declaration text for struct/enum.""" start = decl['start'] end = decl['end'] @@ -579,21 +580,86 @@ def get_declaration_text(lines, decl): decl_lines = [] for i in range(start, min(end + 1, len(lines))): line = lines[i] - # Remove comments - if '//' in line: - line = line[:line.index('//')] + if not include_comments: + # Remove comments + if '//' in line: + line = line[:line.index('//')] decl_lines.append(line) - # Join and normalize - text = ' '.join(decl_lines) - text = normalize_whitespace(text) + if include_comments: + # Normalize: one tab indent, remove empty lines, normalize whitespace + result_lines = [] + for line in decl_lines: + # Skip empty lines + if not line.strip(): + continue + # Normalize whitespace and use one tab for indentation + normalized = normalize_whitespace(line) + if normalized: + result_lines.append('\t' + normalized) + return '\n'.join(result_lines) + else: + # Join and normalize for toc mode + text = ' '.join(decl_lines) + text = normalize_whitespace(text) + return text + + +def get_function_comments(pre_lines): + """Extract function comments from pre_lines. + + Returns: + - Last /* */ block if found (including single-line /* */) + - Or consecutive // lines (without empty lines or non-comment lines) + - Empty string if no suitable comments + """ + if not pre_lines: + return "" - return text + # Look for last /* */ block + last_block_comment = "" + in_block = False + block_start = -1 + + for i, line in enumerate(pre_lines): + stripped = line.strip() + if stripped.startswith('/*'): + # Check if it's a single-line comment /* ... */ + if stripped.endswith('*/'): + # Single-line block comment + last_block_comment = line + else: + # Start of multi-line block comment + in_block = True + block_start = i + elif in_block and stripped.endswith('*/'): + in_block = False + # Extract the block + block_lines = pre_lines[block_start:i+1] + last_block_comment = '\n'.join(block_lines) + elif stripped.startswith('*/'): + in_block = False + + if last_block_comment: + return last_block_comment + + # Look for consecutive // lines (from the end, backwards) + comment_lines = [] + for line in reversed(pre_lines): + stripped = line.strip() + if stripped.startswith('//'): + comment_lines.insert(0, stripped) + elif not stripped: # Skip empty lines + continue + else: # Non-comment line breaks the sequence + break + + return '\n'.join(comment_lines) if comment_lines else "" def main(): if len(sys.argv) < 2: - print("Usage: c_util toc | func | struct | enum ") + print("Usage: c_util toc | description [name2] ... | func | struct | enum ") sys.exit(1) cmd = sys.argv[1] @@ -654,6 +720,70 @@ def main(): for start, end, text in declarations: print(f"[{start + 1}-{end + 1}] {text}") + elif cmd == 'description': + # Get list of names to look for + if len(sys.argv) < 3: + print("Usage: c_util description [name2] ...") + sys.exit(1) + + target_names = set(sys.argv[2:]) + first_file = True + + for f in files: + if f not in contents: + continue + + # Collect all matching declarations with full info + declarations = [] + + # Check functions + for func in project_functions.get(f, []): + if func['name'] in target_names: + # Get signature + sig = get_function_signature(contents[f], func) + # Get comments + comments = get_function_comments(func['pre']) + declarations.append((func['start'], func['end'], 'function', sig, comments)) + + # Check structs + for s in project_structs.get(f, []): + if s['name'] in target_names: + # Get full text with formatting and comments + text = get_declaration_text(contents[f], s, include_comments=True) + declarations.append((s['start'], s['end'], 'struct', s['name'], text)) + + # Check enums + for e in project_enums.get(f, []): + if e['name'] in target_names: + # Get full text with formatting and comments + text = get_declaration_text(contents[f], e, include_comments=True) + declarations.append((e['start'], e['end'], 'enum', e['name'], text)) + + if not declarations: + continue + + # Sort by line number + declarations.sort(key=lambda x: x[0]) + + # Print empty line between files (except before first) + if not first_file: + print() + first_file = False + + # Print filename header + print(f"{f}:") + + # Print declarations with full formatting + for start, end, decl_type, content, extra in declarations: + if decl_type == 'function': + # Print comments first if any + if extra: + print(extra) + print(f"[{start + 1}-{end + 1}] {content}") + else: + # struct or enum - print with preserved formatting on multiple lines + print(f"[{start + 1}-{end + 1}] {extra}") + elif cmd == 'func': if len(sys.argv) < 4: print("Usage: c_util func ")