repolish 0.2.0__tar.gz → 0.2.2__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.
- {repolish-0.2.0 → repolish-0.2.2}/PKG-INFO +93 -1
- {repolish-0.2.0 → repolish-0.2.2}/README.md +92 -0
- {repolish-0.2.0 → repolish-0.2.2}/repolish/cookiecutter.py +74 -15
- {repolish-0.2.0 → repolish-0.2.2}/repolish/processors.py +114 -3
- repolish-0.2.2/repolish/version.py +1 -0
- repolish-0.2.0/repolish/version.py +0 -1
- {repolish-0.2.0 → repolish-0.2.2}/.gitignore +0 -0
- {repolish-0.2.0 → repolish-0.2.2}/pyproject.toml +0 -0
- {repolish-0.2.0 → repolish-0.2.2}/repolish/__init__.py +0 -0
- {repolish-0.2.0 → repolish-0.2.2}/repolish/builder.py +0 -0
- {repolish-0.2.0 → repolish-0.2.2}/repolish/cli.py +0 -0
- {repolish-0.2.0 → repolish-0.2.2}/repolish/config.py +0 -0
- {repolish-0.2.0 → repolish-0.2.2}/repolish/loader.py +0 -0
- {repolish-0.2.0 → repolish-0.2.2}/repolish/py.typed +0 -0
- {repolish-0.2.0 → repolish-0.2.2}/repolish/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: repolish
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Maintain consistency across repositories
|
|
5
5
|
Author: hotdog-werx
|
|
6
6
|
License-Expression: MIT
|
|
@@ -119,6 +119,15 @@ like `python -c "open('file','w').write('x')"`.
|
|
|
119
119
|
- list/array of argv parts (e.g. `['prettier', '--write', '.']`) — recommended
|
|
120
120
|
when you want to avoid any ambiguity about argument parsing and quoting.
|
|
121
121
|
|
|
122
|
+
Platform note
|
|
123
|
+
|
|
124
|
+
- On Windows the tokenization rules differ; Repolish uses Python's `shlex` to
|
|
125
|
+
split string commands into argv lists before executing them without a shell.
|
|
126
|
+
Because quoting/escaping differs between POSIX shells and Windows, prefer the
|
|
127
|
+
argv/list form in `post_process` on Windows to avoid surprises. If you must
|
|
128
|
+
provide a single string entry, keep arguments simple (no shell metacharacters)
|
|
129
|
+
or call a committed script from the argv form.
|
|
130
|
+
|
|
122
131
|
Security note
|
|
123
132
|
|
|
124
133
|
We intentionally avoid running commands with `shell=True` to reduce shell
|
|
@@ -264,6 +273,89 @@ Notes and tips
|
|
|
264
273
|
- Anchors are processed before cookiecutter rendering, so template substitutions
|
|
265
274
|
still work around preserved sections.
|
|
266
275
|
|
|
276
|
+
## Regex anchors and capture groups
|
|
277
|
+
|
|
278
|
+
Regex anchors let you specify exactly what to preserve from a local file. Two
|
|
279
|
+
important behaviors to know:
|
|
280
|
+
|
|
281
|
+
- Capture group preference: If your regex includes a capturing group
|
|
282
|
+
(parentheses), Repolish will prefer the first capture group (group 1) as the
|
|
283
|
+
block to insert into the template. If there are no capture groups, Repolish
|
|
284
|
+
falls back to the entire match (group 0).
|
|
285
|
+
- Safeguard trimming: As a conservative safeguard Repolish trims captured blocks
|
|
286
|
+
to a contiguous region based on indentation so that incidental following
|
|
287
|
+
sections are not accidentally pulled into the template. However, the canonical
|
|
288
|
+
way to express intent is an explicit capture group — authors should prefer to
|
|
289
|
+
capture exactly what they mean.
|
|
290
|
+
|
|
291
|
+
Implementation note
|
|
292
|
+
|
|
293
|
+
- Repolish computes the absolute span of the selected capture (group 1 when
|
|
294
|
+
present, otherwise the full match) inside the template and trims that slice
|
|
295
|
+
using an indentation-aware heuristic before replacing it with the trimmed
|
|
296
|
+
content extracted from the local file. Also note that the
|
|
297
|
+
`## repolish-regex[...]` declaration line is removed from the template prior
|
|
298
|
+
to matching and replacement. This avoids accidentally removing unrelated
|
|
299
|
+
template sections when patterns include surrounding context.
|
|
300
|
+
|
|
301
|
+
Example
|
|
302
|
+
|
|
303
|
+
Template (excerpt):
|
|
304
|
+
|
|
305
|
+
```toml
|
|
306
|
+
cat1:
|
|
307
|
+
- line1
|
|
308
|
+
- line2
|
|
309
|
+
## repolish-regex[cat1-filter]: (^\s*# cat1-filter-additional-paths.*\n(?:\s+.*\n)*)
|
|
310
|
+
# cat1-filter-additional-paths
|
|
311
|
+
|
|
312
|
+
cat2:
|
|
313
|
+
- from-template
|
|
314
|
+
## repolish-regex[cat2-filter]: (^\s*# cat2-filter-additional-paths.*\n(?:\s+.*\n)*)
|
|
315
|
+
# cat2-filter-additional-paths
|
|
316
|
+
|
|
317
|
+
cat3:
|
|
318
|
+
- cat3-line
|
|
319
|
+
## repolish-regex[cat3-filter]: (^\s*# cat3-filter-additional-paths.*\n(?:\s+.*\n)*)
|
|
320
|
+
# cat3-filter-additional-paths
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Local file (excerpt):
|
|
324
|
+
|
|
325
|
+
```toml
|
|
326
|
+
cat1:
|
|
327
|
+
- line1
|
|
328
|
+
- line2
|
|
329
|
+
# cat1-filter-additional-paths
|
|
330
|
+
- extra
|
|
331
|
+
|
|
332
|
+
cat3:
|
|
333
|
+
- cat3-line
|
|
334
|
+
# cat3-filter-additional-paths
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Result after preprocessing:
|
|
338
|
+
|
|
339
|
+
```toml
|
|
340
|
+
cat1:
|
|
341
|
+
- line1
|
|
342
|
+
- line2
|
|
343
|
+
# cat1-filter-additional-paths
|
|
344
|
+
- extra
|
|
345
|
+
|
|
346
|
+
cat2:
|
|
347
|
+
- from-template
|
|
348
|
+
# cat2-filter-additional-paths
|
|
349
|
+
|
|
350
|
+
cat3:
|
|
351
|
+
- cat3-line
|
|
352
|
+
# cat3-filter-additional-paths
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
When your regex is too greedy, tighten it or add explicit parentheses around the
|
|
356
|
+
intended capture so Repolish can reliably hydrate the template without importing
|
|
357
|
+
unrelated content.
|
|
358
|
+
|
|
267
359
|
### Where anchors are declared and uniqueness
|
|
268
360
|
|
|
269
361
|
Anchors can come from three places (and are merged in this order):
|
|
@@ -107,6 +107,15 @@ like `python -c "open('file','w').write('x')"`.
|
|
|
107
107
|
- list/array of argv parts (e.g. `['prettier', '--write', '.']`) — recommended
|
|
108
108
|
when you want to avoid any ambiguity about argument parsing and quoting.
|
|
109
109
|
|
|
110
|
+
Platform note
|
|
111
|
+
|
|
112
|
+
- On Windows the tokenization rules differ; Repolish uses Python's `shlex` to
|
|
113
|
+
split string commands into argv lists before executing them without a shell.
|
|
114
|
+
Because quoting/escaping differs between POSIX shells and Windows, prefer the
|
|
115
|
+
argv/list form in `post_process` on Windows to avoid surprises. If you must
|
|
116
|
+
provide a single string entry, keep arguments simple (no shell metacharacters)
|
|
117
|
+
or call a committed script from the argv form.
|
|
118
|
+
|
|
110
119
|
Security note
|
|
111
120
|
|
|
112
121
|
We intentionally avoid running commands with `shell=True` to reduce shell
|
|
@@ -252,6 +261,89 @@ Notes and tips
|
|
|
252
261
|
- Anchors are processed before cookiecutter rendering, so template substitutions
|
|
253
262
|
still work around preserved sections.
|
|
254
263
|
|
|
264
|
+
## Regex anchors and capture groups
|
|
265
|
+
|
|
266
|
+
Regex anchors let you specify exactly what to preserve from a local file. Two
|
|
267
|
+
important behaviors to know:
|
|
268
|
+
|
|
269
|
+
- Capture group preference: If your regex includes a capturing group
|
|
270
|
+
(parentheses), Repolish will prefer the first capture group (group 1) as the
|
|
271
|
+
block to insert into the template. If there are no capture groups, Repolish
|
|
272
|
+
falls back to the entire match (group 0).
|
|
273
|
+
- Safeguard trimming: As a conservative safeguard Repolish trims captured blocks
|
|
274
|
+
to a contiguous region based on indentation so that incidental following
|
|
275
|
+
sections are not accidentally pulled into the template. However, the canonical
|
|
276
|
+
way to express intent is an explicit capture group — authors should prefer to
|
|
277
|
+
capture exactly what they mean.
|
|
278
|
+
|
|
279
|
+
Implementation note
|
|
280
|
+
|
|
281
|
+
- Repolish computes the absolute span of the selected capture (group 1 when
|
|
282
|
+
present, otherwise the full match) inside the template and trims that slice
|
|
283
|
+
using an indentation-aware heuristic before replacing it with the trimmed
|
|
284
|
+
content extracted from the local file. Also note that the
|
|
285
|
+
`## repolish-regex[...]` declaration line is removed from the template prior
|
|
286
|
+
to matching and replacement. This avoids accidentally removing unrelated
|
|
287
|
+
template sections when patterns include surrounding context.
|
|
288
|
+
|
|
289
|
+
Example
|
|
290
|
+
|
|
291
|
+
Template (excerpt):
|
|
292
|
+
|
|
293
|
+
```toml
|
|
294
|
+
cat1:
|
|
295
|
+
- line1
|
|
296
|
+
- line2
|
|
297
|
+
## repolish-regex[cat1-filter]: (^\s*# cat1-filter-additional-paths.*\n(?:\s+.*\n)*)
|
|
298
|
+
# cat1-filter-additional-paths
|
|
299
|
+
|
|
300
|
+
cat2:
|
|
301
|
+
- from-template
|
|
302
|
+
## repolish-regex[cat2-filter]: (^\s*# cat2-filter-additional-paths.*\n(?:\s+.*\n)*)
|
|
303
|
+
# cat2-filter-additional-paths
|
|
304
|
+
|
|
305
|
+
cat3:
|
|
306
|
+
- cat3-line
|
|
307
|
+
## repolish-regex[cat3-filter]: (^\s*# cat3-filter-additional-paths.*\n(?:\s+.*\n)*)
|
|
308
|
+
# cat3-filter-additional-paths
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Local file (excerpt):
|
|
312
|
+
|
|
313
|
+
```toml
|
|
314
|
+
cat1:
|
|
315
|
+
- line1
|
|
316
|
+
- line2
|
|
317
|
+
# cat1-filter-additional-paths
|
|
318
|
+
- extra
|
|
319
|
+
|
|
320
|
+
cat3:
|
|
321
|
+
- cat3-line
|
|
322
|
+
# cat3-filter-additional-paths
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Result after preprocessing:
|
|
326
|
+
|
|
327
|
+
```toml
|
|
328
|
+
cat1:
|
|
329
|
+
- line1
|
|
330
|
+
- line2
|
|
331
|
+
# cat1-filter-additional-paths
|
|
332
|
+
- extra
|
|
333
|
+
|
|
334
|
+
cat2:
|
|
335
|
+
- from-template
|
|
336
|
+
# cat2-filter-additional-paths
|
|
337
|
+
|
|
338
|
+
cat3:
|
|
339
|
+
- cat3-line
|
|
340
|
+
# cat3-filter-additional-paths
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
When your regex is too greedy, tighten it or add explicit parentheses around the
|
|
344
|
+
intended capture so Repolish can reliably hydrate the template without importing
|
|
345
|
+
unrelated content.
|
|
346
|
+
|
|
255
347
|
### Where anchors are declared and uniqueness
|
|
256
348
|
|
|
257
349
|
Anchors can come from three places (and are merged in this order):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import difflib
|
|
2
2
|
import filecmp
|
|
3
3
|
import json
|
|
4
|
+
import os
|
|
4
5
|
import shutil
|
|
5
6
|
from pathlib import Path, PurePosixPath
|
|
6
7
|
|
|
@@ -160,6 +161,57 @@ def collect_output_files(setup_output: Path) -> list[Path]:
|
|
|
160
161
|
return [p for p in setup_output.rglob('*') if p.is_file()]
|
|
161
162
|
|
|
162
163
|
|
|
164
|
+
def _preserve_line_endings() -> bool:
|
|
165
|
+
"""Return True when REPOLISH_PRESERVE_LINE_ENDINGS is truthy in env.
|
|
166
|
+
|
|
167
|
+
Centralized to make behavior testable and reduce complexity in the main
|
|
168
|
+
comparison function.
|
|
169
|
+
"""
|
|
170
|
+
val = os.getenv('REPOLISH_PRESERVE_LINE_ENDINGS', '')
|
|
171
|
+
return str(val).lower() in ('1', 'true', 'yes')
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _compare_and_prepare_diff(
|
|
175
|
+
out: Path,
|
|
176
|
+
dest: Path,
|
|
177
|
+
*,
|
|
178
|
+
preserve: bool,
|
|
179
|
+
) -> tuple[bool, list[str], list[str]]:
|
|
180
|
+
"""Compare two files and return (same, a_lines, b_lines).
|
|
181
|
+
|
|
182
|
+
- same: True when files are equal according to the chosen policy.
|
|
183
|
+
- a_lines, b_lines: lists of lines (with line endings) to be used in a
|
|
184
|
+
unified diff when same is False. When same is True these values are
|
|
185
|
+
empty lists.
|
|
186
|
+
"""
|
|
187
|
+
if preserve:
|
|
188
|
+
# fast-path equality check using filecmp (may be optimized by OS)
|
|
189
|
+
if filecmp.cmp(out, dest, shallow=False):
|
|
190
|
+
return True, [], []
|
|
191
|
+
a_raw = out.read_bytes()
|
|
192
|
+
b_raw = dest.read_bytes()
|
|
193
|
+
a_text = a_raw.decode('utf-8', errors='replace')
|
|
194
|
+
b_text = b_raw.decode('utf-8', errors='replace')
|
|
195
|
+
return (
|
|
196
|
+
False,
|
|
197
|
+
a_text.splitlines(keepends=True),
|
|
198
|
+
b_text.splitlines(keepends=True),
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# Normalized comparison (ignore CRLF vs LF)
|
|
202
|
+
a_raw = out.read_bytes()
|
|
203
|
+
b_raw = dest.read_bytes()
|
|
204
|
+
a_text = a_raw.decode('utf-8', errors='replace').replace('\r\n', '\n').replace('\r', '\n')
|
|
205
|
+
b_text = b_raw.decode('utf-8', errors='replace').replace('\r\n', '\n').replace('\r', '\n')
|
|
206
|
+
if a_text == b_text:
|
|
207
|
+
return True, [], []
|
|
208
|
+
return (
|
|
209
|
+
False,
|
|
210
|
+
a_text.splitlines(keepends=True),
|
|
211
|
+
b_text.splitlines(keepends=True),
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
163
215
|
def check_generated_output(
|
|
164
216
|
setup_output: Path,
|
|
165
217
|
providers: Providers,
|
|
@@ -171,27 +223,34 @@ def check_generated_output(
|
|
|
171
223
|
"""
|
|
172
224
|
output_files = collect_output_files(setup_output)
|
|
173
225
|
diffs: list[tuple[str, str]] = []
|
|
226
|
+
|
|
227
|
+
preserve = _preserve_line_endings()
|
|
228
|
+
|
|
174
229
|
for out in output_files:
|
|
175
230
|
rel = out.relative_to(setup_output / 'repolish')
|
|
176
231
|
dest = base_dir / rel
|
|
177
232
|
if not dest.exists():
|
|
178
233
|
diffs.append((str(rel), 'MISSING'))
|
|
179
234
|
continue
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
235
|
+
|
|
236
|
+
same, a_lines, b_lines = _compare_and_prepare_diff(
|
|
237
|
+
out,
|
|
238
|
+
dest,
|
|
239
|
+
preserve=preserve,
|
|
240
|
+
)
|
|
241
|
+
if same:
|
|
242
|
+
continue
|
|
243
|
+
|
|
244
|
+
ud = ''.join(
|
|
245
|
+
difflib.unified_diff(
|
|
246
|
+
b_lines,
|
|
247
|
+
a_lines,
|
|
248
|
+
fromfile=str(dest),
|
|
249
|
+
tofile=str(out),
|
|
250
|
+
lineterm='',
|
|
251
|
+
),
|
|
252
|
+
)
|
|
253
|
+
diffs.append((str(rel), ud))
|
|
195
254
|
|
|
196
255
|
# provider-declared deletions: if a path is expected deleted but exists in
|
|
197
256
|
# the project, surface that so devs know to run repolish
|
|
@@ -90,6 +90,67 @@ def replace_tags_in_content(content: str, tags: dict[str, str]) -> str:
|
|
|
90
90
|
return content
|
|
91
91
|
|
|
92
92
|
|
|
93
|
+
def _select_capture(match: re.Match) -> str:
|
|
94
|
+
"""Return the author's intended capture from a regex match.
|
|
95
|
+
|
|
96
|
+
If the regex contains capturing groups, prefer the first group
|
|
97
|
+
(group 1). Otherwise fall back to the full match (group 0).
|
|
98
|
+
This lets regex authors precisely specify what should be
|
|
99
|
+
extracted and inserted.
|
|
100
|
+
"""
|
|
101
|
+
if match.lastindex:
|
|
102
|
+
# prefer the first capture group when present
|
|
103
|
+
return match.group(1)
|
|
104
|
+
return match.group(0)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _trim_block_by_indent(block: str) -> str:
|
|
108
|
+
"""Trim a matched block to the contiguous, same-indentation region.
|
|
109
|
+
|
|
110
|
+
Keeps the first line and any immediately following lines that are
|
|
111
|
+
either blank or indented at least as far as the first line. Stops at
|
|
112
|
+
the first subsequent line with smaller indentation. This heuristic is
|
|
113
|
+
intentionally simple and works well for indentation-based formats
|
|
114
|
+
(YAML, Python-ish lists, etc.) but is only a safeguard — authors
|
|
115
|
+
should prefer explicit capture groups to precisely control what to
|
|
116
|
+
extract.
|
|
117
|
+
"""
|
|
118
|
+
lines = block.splitlines(keepends=True)
|
|
119
|
+
if not lines:
|
|
120
|
+
return block
|
|
121
|
+
first = lines[0]
|
|
122
|
+
anchor_indent = len(first) - len(first.lstrip(' '))
|
|
123
|
+
kept = [first]
|
|
124
|
+
for ln in lines[1:]:
|
|
125
|
+
if ln.strip() == '':
|
|
126
|
+
kept.append(ln)
|
|
127
|
+
continue
|
|
128
|
+
indent = len(ln) - len(ln.lstrip(' '))
|
|
129
|
+
if indent >= anchor_indent:
|
|
130
|
+
kept.append(ln)
|
|
131
|
+
else:
|
|
132
|
+
break
|
|
133
|
+
return ''.join(kept)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _extend_trimmed_region_to_include_whitespace(
|
|
137
|
+
content: str,
|
|
138
|
+
trimmed_end: int,
|
|
139
|
+
tpl_cap_end: int,
|
|
140
|
+
) -> int:
|
|
141
|
+
"""Preserve trailing whitespace-only content from the original capture."""
|
|
142
|
+
if trimmed_end < tpl_cap_end:
|
|
143
|
+
between = content[trimmed_end:tpl_cap_end]
|
|
144
|
+
# only extend if between contains only spaces/tabs (no other
|
|
145
|
+
# non-whitespace characters) and includes at least one newline
|
|
146
|
+
# so we don't accidentally swallow inline spaces.
|
|
147
|
+
# Consider all whitespace (including newlines) when deciding if the
|
|
148
|
+
# slice is empty of non-whitespace characters.
|
|
149
|
+
if between.strip() == '' and '\n' in between:
|
|
150
|
+
return tpl_cap_end
|
|
151
|
+
return trimmed_end
|
|
152
|
+
|
|
153
|
+
|
|
93
154
|
def apply_regex_replacements(
|
|
94
155
|
content: str,
|
|
95
156
|
regexes: dict[str, str],
|
|
@@ -105,9 +166,59 @@ def apply_regex_replacements(
|
|
|
105
166
|
# apply regex replacements
|
|
106
167
|
for regex_pattern in regexes.values():
|
|
107
168
|
pattern = re.compile(rf'{regex_pattern}', re.MULTILINE)
|
|
108
|
-
|
|
109
|
-
if
|
|
110
|
-
|
|
169
|
+
local_match = pattern.search(local_file_content)
|
|
170
|
+
if not local_match:
|
|
171
|
+
continue
|
|
172
|
+
|
|
173
|
+
# Prefer the author's explicit capture when present (group 1); if no
|
|
174
|
+
# capture is present fall back to the full match (group 0). This gives
|
|
175
|
+
# template authors precise control while remaining backwards
|
|
176
|
+
# compatible for patterns that don't use groups.
|
|
177
|
+
local_matched_raw = _select_capture(local_match)
|
|
178
|
+
local_matched = _trim_block_by_indent(local_matched_raw)
|
|
179
|
+
|
|
180
|
+
# Find where the pattern would match in the template content (after
|
|
181
|
+
# we've removed the declaration line). Trim the template match using
|
|
182
|
+
# the same indentation-aware heuristic so we only replace the
|
|
183
|
+
# anchor's block and don't remove following unrelated sections from
|
|
184
|
+
# the template.
|
|
185
|
+
template_match = pattern.search(content)
|
|
186
|
+
if not template_match:
|
|
187
|
+
# nothing to replace in template
|
|
188
|
+
continue
|
|
189
|
+
|
|
190
|
+
# Determine which group index we used (1 when the author provided a
|
|
191
|
+
# capture group, otherwise 0 for the full match). Compute the absolute
|
|
192
|
+
# span of that selected region in the template so replacements are
|
|
193
|
+
# performed at the correct indices even when the declared pattern
|
|
194
|
+
# includes surrounding context.
|
|
195
|
+
tpl_group_idx = 1 if template_match.lastindex else 0
|
|
196
|
+
tpl_cap_start, tpl_cap_end = template_match.span(tpl_group_idx)
|
|
197
|
+
|
|
198
|
+
tpl_matched_raw = content[tpl_cap_start:tpl_cap_end]
|
|
199
|
+
tpl_matched = _trim_block_by_indent(tpl_matched_raw)
|
|
200
|
+
|
|
201
|
+
# Replace only the trimmed matched region in the template with the
|
|
202
|
+
# trimmed local content. The trimmed region starts at the capture
|
|
203
|
+
# start and extends the length of the trimmed text. However, if the
|
|
204
|
+
# template contained only whitespace (spaces/newlines) between the
|
|
205
|
+
# end of the trimmed block and the original capture end (for
|
|
206
|
+
# example a blank line before the next section marker), preserve
|
|
207
|
+
# that whitespace so surrounding structure/spacing is unchanged.
|
|
208
|
+
trimmed_start = tpl_cap_start
|
|
209
|
+
trimmed_end = tpl_cap_start + len(tpl_matched)
|
|
210
|
+
|
|
211
|
+
# Potentially extend the trimmed end to include whitespace-only
|
|
212
|
+
# padding that was part of the original capture. Delegate to the
|
|
213
|
+
# helper so the logic is tested and `apply_regex_replacements` is
|
|
214
|
+
# easier to read.
|
|
215
|
+
trimmed_end = _extend_trimmed_region_to_include_whitespace(
|
|
216
|
+
content,
|
|
217
|
+
trimmed_end,
|
|
218
|
+
tpl_cap_end,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
content = content[:trimmed_start] + local_matched + content[trimmed_end:]
|
|
111
222
|
return content
|
|
112
223
|
|
|
113
224
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.2.2'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '0.2.0'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|