rolfedh-doc-utils 0.1.40__tar.gz → 0.1.41__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/PKG-INFO +1 -1
  2. rolfedh_doc_utils-0.1.41/doc_utils/insert_abstract_role.py +220 -0
  3. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/version.py +1 -1
  4. rolfedh_doc_utils-0.1.41/insert_abstract_role.py +163 -0
  5. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/pyproject.toml +3 -2
  6. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/rolfedh_doc_utils.egg-info/PKG-INFO +1 -1
  7. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/rolfedh_doc_utils.egg-info/SOURCES.txt +2 -0
  8. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/rolfedh_doc_utils.egg-info/entry_points.txt +1 -0
  9. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/rolfedh_doc_utils.egg-info/top_level.txt +1 -0
  10. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/LICENSE +0 -0
  11. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/README.md +0 -0
  12. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/archive_unused_files.py +0 -0
  13. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/archive_unused_images.py +0 -0
  14. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/callout_lib/__init__.py +0 -0
  15. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/callout_lib/converter_bullets.py +0 -0
  16. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/callout_lib/converter_comments.py +0 -0
  17. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/callout_lib/converter_deflist.py +0 -0
  18. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/callout_lib/detector.py +0 -0
  19. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/callout_lib/table_parser.py +0 -0
  20. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/check_published_links.py +0 -0
  21. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/check_scannability.py +0 -0
  22. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/check_source_directives.py +0 -0
  23. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/convert_callouts_interactive.py +0 -0
  24. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/convert_callouts_to_deflist.py +0 -0
  25. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/convert_freemarker_to_asciidoc.py +0 -0
  26. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/convert_tables_to_deflists.py +0 -0
  27. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/__init__.py +0 -0
  28. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/convert_freemarker_to_asciidoc.py +0 -0
  29. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/duplicate_content.py +0 -0
  30. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/duplicate_includes.py +0 -0
  31. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/extract_link_attributes.py +0 -0
  32. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/file_utils.py +0 -0
  33. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/format_asciidoc_spacing.py +0 -0
  34. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/inventory_conditionals.py +0 -0
  35. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/missing_source_directive.py +0 -0
  36. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/replace_link_attributes.py +0 -0
  37. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/scannability.py +0 -0
  38. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/spinner.py +0 -0
  39. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/topic_map_parser.py +0 -0
  40. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/unused_adoc.py +0 -0
  41. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/unused_attributes.py +0 -0
  42. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/unused_images.py +0 -0
  43. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/validate_links.py +0 -0
  44. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/version_check.py +0 -0
  45. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils/warnings_report.py +0 -0
  46. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/doc_utils_cli.py +0 -0
  47. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/extract_link_attributes.py +0 -0
  48. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/find_duplicate_content.py +0 -0
  49. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/find_duplicate_includes.py +0 -0
  50. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/find_unused_attributes.py +0 -0
  51. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/format_asciidoc_spacing.py +0 -0
  52. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/inventory_conditionals.py +0 -0
  53. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/replace_link_attributes.py +0 -0
  54. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/rolfedh_doc_utils.egg-info/dependency_links.txt +0 -0
  55. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/rolfedh_doc_utils.egg-info/requires.txt +0 -0
  56. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/setup.cfg +0 -0
  57. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/setup.py +0 -0
  58. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_archive_unused_files.py +0 -0
  59. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_archive_unused_images.py +0 -0
  60. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_auto_discovery.py +0 -0
  61. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_check_scannability.py +0 -0
  62. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_cli_entry_points.py +0 -0
  63. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_convert_tables_to_deflists.py +0 -0
  64. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_extract_link_attributes.py +0 -0
  65. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_file_utils.py +0 -0
  66. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_fixture_archive_unused_files.py +0 -0
  67. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_fixture_archive_unused_images.py +0 -0
  68. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_fixture_check_scannability.py +0 -0
  69. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_inventory_conditionals.py +0 -0
  70. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_parse_exclude_list.py +0 -0
  71. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_replace_link_attributes.py +0 -0
  72. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_symlink_handling.py +0 -0
  73. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_table_callout_conversion.py +0 -0
  74. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_table_parser.py +0 -0
  75. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_topic_map_parser.py +0 -0
  76. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_unused_attributes.py +0 -0
  77. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_validate_links.py +0 -0
  78. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/tests/test_version_check.py +0 -0
  79. {rolfedh_doc_utils-0.1.40 → rolfedh_doc_utils-0.1.41}/validate_links.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rolfedh-doc-utils
3
- Version: 0.1.40
3
+ Version: 0.1.41
4
4
  Summary: CLI tools for AsciiDoc documentation projects
5
5
  Author: Rolfe Dlugy-Hegwer
6
6
  License: MIT License
@@ -0,0 +1,220 @@
1
+ """
2
+ Insert abstract role - ensures AsciiDoc files have [role="_abstract"] above the first paragraph.
3
+
4
+ Core logic for adding the [role="_abstract"] attribute required for DITA short description conversion.
5
+ """
6
+
7
+ import re
8
+ from pathlib import Path
9
+ from typing import List, Tuple, Optional
10
+
11
+
12
+ def find_first_paragraph_after_title(lines: List[str]) -> Optional[int]:
13
+ """
14
+ Find the line index of the first paragraph after the document title.
15
+
16
+ The first paragraph is the first non-empty line that:
17
+ - Comes after a level 1 heading (= Title)
18
+ - Is not an attribute definition (starts with :)
19
+ - Is not a comment (starts with //)
20
+ - Is not a block attribute (starts with [)
21
+ - Is not another heading
22
+
23
+ Args:
24
+ lines: List of lines from the file (without trailing newlines)
25
+
26
+ Returns:
27
+ Line index of the first paragraph, or None if not found
28
+ """
29
+ title_found = False
30
+ title_index = -1
31
+
32
+ for i, line in enumerate(lines):
33
+ # Check for level 1 heading (document title)
34
+ if re.match(r'^=\s+[^=]', line):
35
+ title_found = True
36
+ title_index = i
37
+ continue
38
+
39
+ # Only look for first paragraph after we've found the title
40
+ if not title_found:
41
+ continue
42
+
43
+ # Skip empty lines
44
+ if re.match(r'^\s*$', line):
45
+ continue
46
+
47
+ # Skip attribute definitions
48
+ if re.match(r'^:', line):
49
+ continue
50
+
51
+ # Skip comments (single line)
52
+ if re.match(r'^//', line):
53
+ continue
54
+
55
+ # Skip block attributes like [role=...], [id=...], etc.
56
+ if re.match(r'^\[', line):
57
+ continue
58
+
59
+ # Skip other headings
60
+ if re.match(r'^=+\s+', line):
61
+ continue
62
+
63
+ # Skip include directives
64
+ if re.match(r'^include::', line):
65
+ continue
66
+
67
+ # This is the first paragraph
68
+ return i
69
+
70
+ return None
71
+
72
+
73
+ def has_abstract_role(lines: List[str], paragraph_index: int) -> bool:
74
+ """
75
+ Check if there's already a [role="_abstract"] before the paragraph.
76
+
77
+ Args:
78
+ lines: List of lines from the file
79
+ paragraph_index: Index of the first paragraph
80
+
81
+ Returns:
82
+ True if [role="_abstract"] already exists before the paragraph
83
+ """
84
+ # Look at the lines immediately before the paragraph
85
+ for i in range(paragraph_index - 1, -1, -1):
86
+ line = lines[i].strip()
87
+
88
+ # Skip empty lines
89
+ if not line:
90
+ continue
91
+
92
+ # Found abstract role
93
+ if re.match(r'^\[role=["\']_abstract["\']\]$', line):
94
+ return True
95
+
96
+ # If we hit any other non-empty content, stop looking
97
+ # (could be attribute, heading, etc.)
98
+ break
99
+
100
+ return False
101
+
102
+
103
+ def process_file(file_path: Path, dry_run: bool = False, verbose: bool = False) -> Tuple[bool, List[str]]:
104
+ """
105
+ Process a single AsciiDoc file to add [role="_abstract"] if needed.
106
+
107
+ Args:
108
+ file_path: Path to the file to process
109
+ dry_run: If True, show what would be changed without modifying
110
+ verbose: If True, show detailed output
111
+
112
+ Returns:
113
+ Tuple of (changes_made, messages) where messages is a list of verbose output
114
+ """
115
+ messages = []
116
+
117
+ if verbose:
118
+ messages.append(f"Processing: {file_path}")
119
+
120
+ try:
121
+ with open(file_path, 'r', encoding='utf-8') as f:
122
+ lines = f.readlines()
123
+ except (IOError, UnicodeDecodeError) as e:
124
+ raise IOError(f"Error reading {file_path}: {e}")
125
+
126
+ # Remove trailing newlines from lines for processing
127
+ lines = [line.rstrip('\n\r') for line in lines]
128
+
129
+ # Find the first paragraph after the title
130
+ paragraph_index = find_first_paragraph_after_title(lines)
131
+
132
+ if paragraph_index is None:
133
+ if verbose:
134
+ messages.append(" No paragraph found after title")
135
+ return False, messages
136
+
137
+ # Check if abstract role already exists
138
+ if has_abstract_role(lines, paragraph_index):
139
+ if verbose:
140
+ messages.append(" [role=\"_abstract\"] already present")
141
+ return False, messages
142
+
143
+ # Insert [role="_abstract"] before the first paragraph
144
+ # We need to add it with a blank line before it if there isn't one
145
+ new_lines = lines[:paragraph_index]
146
+
147
+ # Check if we need to add a blank line before the role
148
+ if paragraph_index > 0 and lines[paragraph_index - 1].strip():
149
+ new_lines.append('')
150
+
151
+ new_lines.append('[role="_abstract"]')
152
+ new_lines.extend(lines[paragraph_index:])
153
+
154
+ if verbose:
155
+ preview = lines[paragraph_index][:60] + "..." if len(lines[paragraph_index]) > 60 else lines[paragraph_index]
156
+ messages.append(f" Adding [role=\"_abstract\"] before line {paragraph_index + 1}: {preview}")
157
+
158
+ if not dry_run:
159
+ try:
160
+ with open(file_path, 'w', encoding='utf-8') as f:
161
+ for line in new_lines:
162
+ f.write(line + '\n')
163
+ except IOError as e:
164
+ raise IOError(f"Error writing {file_path}: {e}")
165
+
166
+ return True, messages
167
+
168
+
169
+ def find_adoc_files(path: Path, exclude_dirs: List[str] = None, exclude_files: List[str] = None) -> List[Path]:
170
+ """
171
+ Find all .adoc files in the given path.
172
+
173
+ Args:
174
+ path: File or directory path to search
175
+ exclude_dirs: List of directory paths to exclude
176
+ exclude_files: List of file paths to exclude
177
+
178
+ Returns:
179
+ List of Path objects for .adoc files
180
+ """
181
+ exclude_dirs = exclude_dirs or []
182
+ exclude_files = exclude_files or []
183
+
184
+ # Normalize exclusion paths to absolute
185
+ exclude_dirs_abs = [Path(d).resolve() for d in exclude_dirs]
186
+ exclude_files_abs = [Path(f).resolve() for f in exclude_files]
187
+
188
+ adoc_files = []
189
+
190
+ if path.is_file():
191
+ if path.suffix == '.adoc':
192
+ path_abs = path.resolve()
193
+ if path_abs not in exclude_files_abs:
194
+ adoc_files.append(path)
195
+ elif path.is_dir():
196
+ for adoc_path in path.rglob('*.adoc'):
197
+ # Skip symlinks
198
+ if adoc_path.is_symlink():
199
+ continue
200
+
201
+ path_abs = adoc_path.resolve()
202
+
203
+ # Check if file is excluded
204
+ if path_abs in exclude_files_abs:
205
+ continue
206
+
207
+ # Check if any parent directory is excluded
208
+ skip = False
209
+ for exclude_dir in exclude_dirs_abs:
210
+ try:
211
+ path_abs.relative_to(exclude_dir)
212
+ skip = True
213
+ break
214
+ except ValueError:
215
+ pass
216
+
217
+ if not skip:
218
+ adoc_files.append(adoc_path)
219
+
220
+ return sorted(adoc_files)
@@ -1,7 +1,7 @@
1
1
  """Version information for doc-utils."""
2
2
 
3
3
  # This should match the version in pyproject.toml
4
- __version__ = "0.1.40"
4
+ __version__ = "0.1.41"
5
5
 
6
6
  def get_version():
7
7
  """Return the current version string."""
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ insert-abstract-role - Insert [role="_abstract"] above the first paragraph after the title.
4
+
5
+ Ensures AsciiDoc files have the [role="_abstract"] attribute required for DITA short description
6
+ conversion, as enforced by the AsciiDocDITA.ShortDescription vale rule.
7
+ """
8
+
9
+ import argparse
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ from doc_utils.insert_abstract_role import process_file, find_adoc_files
14
+ from doc_utils.version_check import check_version_on_startup
15
+ from doc_utils.version import __version__
16
+ from doc_utils.file_utils import parse_exclude_list_file
17
+
18
+
19
+ # Colors for output
20
+ class Colors:
21
+ RED = '\033[0;31m'
22
+ GREEN = '\033[0;32m'
23
+ YELLOW = '\033[1;33m'
24
+ NC = '\033[0m' # No Color
25
+
26
+
27
+ def print_colored(message: str, color: str = Colors.NC) -> None:
28
+ """Print message with color"""
29
+ print(f"{color}{message}{Colors.NC}")
30
+
31
+
32
+ def main():
33
+ # Check for updates (non-blocking, won't interfere with tool operation)
34
+ check_version_on_startup()
35
+
36
+ parser = argparse.ArgumentParser(
37
+ description="Insert [role=\"_abstract\"] above the first paragraph after the document title",
38
+ formatter_class=argparse.RawDescriptionHelpFormatter,
39
+ epilog="""
40
+ Insert [role="_abstract"] above the first paragraph after the document title in AsciiDoc files.
41
+ This attribute is required for DITA short description conversion.
42
+
43
+ The tool identifies the first paragraph after a level 1 heading (= Title) and inserts
44
+ the [role="_abstract"] attribute on the line immediately before it.
45
+
46
+ Examples:
47
+ %(prog)s # Process all .adoc files in current directory
48
+ %(prog)s modules/ # Process all .adoc files in modules/
49
+ %(prog)s modules/rn/my-release-note.adoc # Process single file
50
+ %(prog)s --dry-run modules/ # Preview changes without modifying
51
+ %(prog)s --exclude-dir .archive modules/ # Exclude .archive directories
52
+ """
53
+ )
54
+
55
+ parser.add_argument(
56
+ 'path',
57
+ nargs='?',
58
+ default='.',
59
+ help='File or directory to process (default: current directory)'
60
+ )
61
+ parser.add_argument(
62
+ '-n', '--dry-run',
63
+ action='store_true',
64
+ help='Show what would be changed without modifying files'
65
+ )
66
+ parser.add_argument(
67
+ '-v', '--verbose',
68
+ action='store_true',
69
+ help='Show detailed output'
70
+ )
71
+ parser.add_argument(
72
+ '--exclude-dir',
73
+ action='append',
74
+ default=[],
75
+ help='Directory to exclude (can be specified multiple times)'
76
+ )
77
+ parser.add_argument(
78
+ '--exclude-file',
79
+ action='append',
80
+ default=[],
81
+ help='File to exclude (can be specified multiple times)'
82
+ )
83
+ parser.add_argument(
84
+ '--exclude-list',
85
+ help='Path to file containing list of files/directories to exclude (one per line)'
86
+ )
87
+ parser.add_argument('--version', action='version', version=f'%(prog)s {__version__}')
88
+
89
+ args = parser.parse_args()
90
+
91
+ # Convert path to Path object
92
+ target_path = Path(args.path)
93
+
94
+ # Check if path exists
95
+ if not target_path.exists():
96
+ print_colored(f"Error: Path does not exist: {target_path}", Colors.RED)
97
+ sys.exit(1)
98
+
99
+ # Parse exclusion list file if provided
100
+ exclude_dirs = list(args.exclude_dir)
101
+ exclude_files = list(args.exclude_file)
102
+
103
+ if args.exclude_list:
104
+ list_dirs, list_files = parse_exclude_list_file(args.exclude_list)
105
+ exclude_dirs.extend(list_dirs)
106
+ exclude_files.extend(list_files)
107
+
108
+ # Display dry-run mode message
109
+ if args.dry_run:
110
+ print_colored("DRY RUN MODE - No files will be modified", Colors.YELLOW)
111
+
112
+ # Find all AsciiDoc files
113
+ adoc_files = find_adoc_files(target_path, exclude_dirs, exclude_files)
114
+
115
+ if not adoc_files:
116
+ if target_path.is_file():
117
+ print_colored(f"Warning: {target_path} is not an AsciiDoc file (.adoc)", Colors.YELLOW)
118
+ print(f"Processed 0 AsciiDoc file(s)")
119
+ print("Insert abstract role complete!")
120
+ return
121
+
122
+ # Process each file
123
+ files_processed = 0
124
+ files_modified = 0
125
+
126
+ for file_path in adoc_files:
127
+ try:
128
+ changes_made, messages = process_file(file_path, args.dry_run, args.verbose)
129
+
130
+ # Print verbose messages
131
+ if args.verbose:
132
+ for msg in messages:
133
+ print(msg)
134
+
135
+ if changes_made:
136
+ files_modified += 1
137
+ if args.dry_run:
138
+ print_colored(f"Would modify: {file_path}", Colors.YELLOW)
139
+ else:
140
+ print_colored(f"Modified: {file_path}", Colors.GREEN)
141
+ elif args.verbose:
142
+ print(f" No changes needed for: {file_path}")
143
+
144
+ files_processed += 1
145
+
146
+ except KeyboardInterrupt:
147
+ print_colored("\nOperation cancelled by user", Colors.YELLOW)
148
+ sys.exit(1)
149
+ except IOError as e:
150
+ print_colored(f"{e}", Colors.RED)
151
+ except Exception as e:
152
+ print_colored(f"Unexpected error processing {file_path}: {e}", Colors.RED)
153
+
154
+ print(f"Processed {files_processed} AsciiDoc file(s)")
155
+ if args.dry_run and files_modified > 0:
156
+ print(f"Would modify {files_modified} file(s)")
157
+ elif files_modified > 0:
158
+ print(f"Modified {files_modified} file(s)")
159
+ print("Insert abstract role complete!")
160
+
161
+
162
+ if __name__ == "__main__":
163
+ main()
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "rolfedh-doc-utils"
7
- version = "0.1.40"
7
+ version = "0.1.41"
8
8
  description = "CLI tools for AsciiDoc documentation projects"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -33,10 +33,11 @@ convert-freemarker-to-asciidoc = "convert_freemarker_to_asciidoc:main"
33
33
  inventory-conditionals = "inventory_conditionals:main"
34
34
  find-duplicate-content = "find_duplicate_content:main"
35
35
  find-duplicate-includes = "find_duplicate_includes:main"
36
+ insert-abstract-role = "insert_abstract_role:main"
36
37
 
37
38
  [tool.setuptools.packages.find]
38
39
  where = ["."]
39
40
  include = ["doc_utils*", "callout_lib*"]
40
41
 
41
42
  [tool.setuptools]
42
- py-modules = ["doc_utils_cli", "find_unused_attributes", "check_scannability", "archive_unused_files", "archive_unused_images", "format_asciidoc_spacing", "replace_link_attributes", "extract_link_attributes", "validate_links", "convert_callouts_to_deflist", "convert_callouts_interactive", "check_source_directives", "convert_tables_to_deflists", "check_published_links", "convert_freemarker_to_asciidoc", "inventory_conditionals", "find_duplicate_content", "find_duplicate_includes"]
43
+ py-modules = ["doc_utils_cli", "find_unused_attributes", "check_scannability", "archive_unused_files", "archive_unused_images", "format_asciidoc_spacing", "replace_link_attributes", "extract_link_attributes", "validate_links", "convert_callouts_to_deflist", "convert_callouts_interactive", "check_source_directives", "convert_tables_to_deflists", "check_published_links", "convert_freemarker_to_asciidoc", "inventory_conditionals", "find_duplicate_content", "find_duplicate_includes", "insert_abstract_role"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rolfedh-doc-utils
3
- Version: 0.1.40
3
+ Version: 0.1.41
4
4
  Summary: CLI tools for AsciiDoc documentation projects
5
5
  Author: Rolfe Dlugy-Hegwer
6
6
  License: MIT License
@@ -15,6 +15,7 @@ find_duplicate_content.py
15
15
  find_duplicate_includes.py
16
16
  find_unused_attributes.py
17
17
  format_asciidoc_spacing.py
18
+ insert_abstract_role.py
18
19
  inventory_conditionals.py
19
20
  pyproject.toml
20
21
  replace_link_attributes.py
@@ -33,6 +34,7 @@ doc_utils/duplicate_includes.py
33
34
  doc_utils/extract_link_attributes.py
34
35
  doc_utils/file_utils.py
35
36
  doc_utils/format_asciidoc_spacing.py
37
+ doc_utils/insert_abstract_role.py
36
38
  doc_utils/inventory_conditionals.py
37
39
  doc_utils/missing_source_directive.py
38
40
  doc_utils/replace_link_attributes.py
@@ -14,6 +14,7 @@ find-duplicate-content = find_duplicate_content:main
14
14
  find-duplicate-includes = find_duplicate_includes:main
15
15
  find-unused-attributes = find_unused_attributes:main
16
16
  format-asciidoc-spacing = format_asciidoc_spacing:main
17
+ insert-abstract-role = insert_abstract_role:main
17
18
  inventory-conditionals = inventory_conditionals:main
18
19
  replace-link-attributes = replace_link_attributes:main
19
20
  validate-links = validate_links:main
@@ -15,6 +15,7 @@ find_duplicate_content
15
15
  find_duplicate_includes
16
16
  find_unused_attributes
17
17
  format_asciidoc_spacing
18
+ insert_abstract_role
18
19
  inventory_conditionals
19
20
  replace_link_attributes
20
21
  validate_links