Add --source-map CLI option for source-map generation#62
Draft
elv3rs wants to merge 2 commits into
Draft
Conversation
Introduce SourceMapRenderer, a subclass of Renderer that records byte-level mappings between preprocessor output and original source files. The mapping tracks verbatim (copied) and expanded (macro/eval) regions, supports line folding with continuation markers, and handles #line directive insertions. Key additions: - SourceMapRenderer class with post-render fixup for folded/line-numbered output - --source-map CLI option to write JSON mapping files - Fypp.process_text_with_map() API for programmatic access - Parser augmented to track per-file char-to-byte and line-to-char tables
50 tests covering: - Mapping kinds (verbatim, expanded, generated, mixed) - Field completeness and source map structure - Include file handling - Edge cases: empty input, whitespace, line folding, line numbering, folding+linenums combined, special characters, escape sequences, continuous coverage, hash-in-verbatim, direct calls, muted regions - CLI integration (--source-map flag) - Public API (process_text_with_map)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR (coauthored by Claude) adds source-map support to fypp, enabling downstream tools to map byte
ranges in expanded Fortran output back to the corresponding byte ranges in the
original
.fypptemplate files. A new--source-map FILECLI option instructsfypp to write a JSON file alongside the normal output, describing how every
region of the generated code relates to the source template.
The implementation introduces a
SourceMapRendererclass (subclass ofRenderer), which tracks and records output byte offsets during rendering.All changes are purely additive. When
--source-mapis not passed (orFyppOptions.source_mapisNone), the existingRendereris used andbehaviour is identical to the current release.
Diff: 3 files changed:
src/fypp.py: +205/−5,test/test_source_map.py: +468,README.rst: +14.Motivation
Linters such as fortitude, cannot natively understand fypp directives and therefore need to preprocess the file.
Without a mapping from the expanded version back to the original source template features such as
go to definition and auto-fixes cannot work reliably.
By adding an option to output a json map, the expanded output can be mapped back to the
the original file and position.
Changes
CLI / Options
--source-map FILEoption added to theoptparse-based argument parser.FyppOptions.source_mapattribute (default:None).Parser
_cur_char_spaninstance variable that records(char_start, char_end)character offsets for each parsed region, set in_parse()before every
_process_textand directive handler call._parse_txt()now populates per-file lookup tables inline:_file_contents— raw text of each parsed file (for content-based fixups)._file_char_to_byte— character offset → byte offset tables._file_line_to_char— line number → character offset tables.Builder
_parser_reffield added toBuilder;handle_evalandhandle_textnowappend
getattr(self._parser_ref, '_cur_char_span', None)to node tuplesso the renderer can access sub-line byte ranges.
Renderer base class
_on_txt(node)hook (no-op in base class) called before appendingtext output, allowing subclasses to intercept verbatim text nodes.
_get_eval()signature extended with optionalchar_spanparameter;_render()now passes*node[1:5](was*node[1:4]) to forward the char span.New
SourceMapRendererclassrender()_on_txt()_span_to_byte_range()(start_line, end_line)span (with optionalchar_span) to(src_byte_start, src_byte_end)_record()_get_eval()_get_called_content()_get_muted_content()_fixup_out_byte_offsets()_build_insertion_patterns()_find_with_insertions()_merge_adjacent_verbatim()get_source_map(){ version, source_file, mappings }dictThe
_linenumdirmethod is wrapped in__init__to automatically recordgenerated content for line-number directives without modifying the base class.
Fypporchestrator changessource_mapis set, creates aSourceMapRendererinstead of thedefault
Renderer.process_file(): writes the JSON source map after processing.process_text_with_map(): returns(output, source_map_dict or None).Source Map JSON Format
{ "version": 1, "source_file": "example.fypp", "mappings": [ { "kind": "verbatim", "out_byte_start": 0, "out_byte_end": 42, "src_file": "example.fypp", "src_byte_start": 0, "src_byte_end": 42 }, { "kind": "expanded", "out_byte_start": 42, "out_byte_end": 70, "src_file": "example.fypp", "src_byte_start": 42, "src_byte_end": 85 }, { "kind": "generated", "out_byte_start": 70, "out_byte_end": 95 } ] }Mapping kinds
verbatimout_byte_start/end,src_file,src_byte_start/endexpanded${expr}$, macro call)out_byte_start/end,src_file,src_byte_start/endgenerated#linedirectives inserted by fypp)out_byte_start/endTesting
test/test_source_map.pycovering:escape sequences, continuation markers, hash-in-verbatim with folding)
process_text,process_text_with_map, CLI file output)coverage including folded and fold+linenum cases)
eval producing
#line-like output, very short fold length (7), CRLF,tabs near fold points, empty eval before verbatim, nested include chains,
multiple mute blocks,
#:call/#:endcallblock syntaxAPI Impact
Fypp.process_text_with_map(txt)New method that returns
(output_str, source_map_dict)whenFyppOptions.source_mapis set, or(output_str, None)otherwise.Fypp.process_text(txt)Unchanged — always returns a plain
str.Fypp.process_file(infile, outfile)Unchanged return value. When
source_mapis set, the JSON map is writtento the specified path as a side-effect.
Breaking Changes
None. All changes are additive and gated behind the
source_mapoption:FyppOptions.source_mapisNone.source_mapisNone, the standardRendereris used and the outputis byte-for-byte identical to the current release.
Checklist
--source-map FILECLI option added and documented in--help/ READMEFyppOptions.source_mapattribute withNonedefaultSourceMapRendererclass with verbatim/expanded/generated mapping kinds_cur_char_spanand per-file lookup tablesprocess_filewrites JSON source map when option is setprocess_text_with_mapreturns(output, map)tuple--source-mapis not used