diff --git a/build-support/bin/docs_templates/target_reference.md.mustache b/build-support/bin/docs_templates/target_reference.md.mustache index 4f520d58d84..18cf13b3dfe 100644 --- a/build-support/bin/docs_templates/target_reference.md.mustache +++ b/build-support/bin/docs_templates/target_reference.md.mustache @@ -1,5 +1,4 @@ -{{! Mark as a paragraph to render as plain text, rather than markdown. The spaces matter. }} -

{{{description}}}

+{{{description}}} {{#fields}} ## {{alias}} @@ -7,7 +6,6 @@ type: {{type_hint}} {{{default_or_required}}} -{{! Mark as a paragraph to render as plain text, rather than markdown. The spaces matter. }} -

{{{description}}}

+{{{description}}} {{/fields}} diff --git a/build-support/bin/generate_docs.py b/build-support/bin/generate_docs.py index fbe3b5e8f5f..10e301bd983 100644 --- a/build-support/bin/generate_docs.py +++ b/build-support/bin/generate_docs.py @@ -79,6 +79,18 @@ def html_safe(s: str) -> str: return _backtick_re.sub(r"\1", html.escape(s)) +def description_safe(s: str) -> str: + """Escapes special characters in description text. + + Descriptions are generally treated as plain text, not as markdown. But readme.com still renders + indented lines as blockquotes, so we must take care not to html-quote inside those. + """ + safe_lines = [line if line.startswith(" ") else html_safe(line) for line in s.splitlines()] + safe_lines_str = "\n".join(safe_lines) + # Mark as a paragraph to render as plain text, rather than markdown. The spaces matter. + return f"

{safe_lines_str}

" + + def create_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Generate the Pants reference markdown files.") parser.add_argument( @@ -218,11 +230,9 @@ def process_targets_input(cls, help_info: Dict) -> Dict[str, Dict[str, Any]]: field["default_or_required"] = ( "required" if field["required"] else f"default: {default_str}" ) - # Description is not rendered as markdown, so we don't want to escape - # markdown special characters here (or the escapes will be visible). - field["description"] = html_safe(str(field["description"])) + field["description"] = description_safe(str(field["description"])) target["fields"] = sorted(target["fields"], key=lambda fld: cast(str, fld["alias"])) - target["description"] = html_safe(str(target["description"])) + target["description"] = description_safe(str(target["description"])) return cast(Dict[str, Dict[str, Any]], target_info) diff --git a/src/python/pants/core/target_types.py b/src/python/pants/core/target_types.py index 12369e86a4f..ca5078b301c 100644 --- a/src/python/pants/core/target_types.py +++ b/src/python/pants/core/target_types.py @@ -80,9 +80,8 @@ class RelocatedFilesSrcField(StringField): required = True help = ( "The original prefix that you want to replace, such as `src/resources`.\n\nYou can set " - "this field to `" - "` to preserve the original path; the value in the `dest` field will " - "then be added to the beginning of this original path." + "this field to the empty string to preserve the original path; the value in the `dest` " + "field will then be added to the beginning of this original path." ) @@ -91,8 +90,8 @@ class RelocatedFilesDestField(StringField): required = True help = ( "The new prefix that you want to add to the beginning of the path, such as `data`.\n\nYou " - 'can set this field to "" to avoid adding any new values to the path; the value in the ' - "`src` field will then be stripped, rather than replaced." + "can set this field to the empty string to avoid adding any new values to the path; the " + "value in the `src` field will then be stripped, rather than replaced." )