Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support line number in fragment in MD051/link-fragments (e.g: '#L7') #1117

Merged
merged 2 commits into from
Feb 10, 2024
Merged

feat: support line number in fragment in MD051/link-fragments (e.g: '#L7') #1117

merged 2 commits into from
Feb 10, 2024

Conversation

theoludwig
Copy link
Contributor

@theoludwig theoludwig commented Jan 31, 2024

Hello! 😄
I'm the original author of the rule MD051, work started in this PR: #495.
I'm also the author of https://github.com/theoludwig/markdownlint-rule-relative-links custom rule.
This custom rule, verify if relative links exists in the file system using Node.js node:fs, it also checks for links fragments referencing other files (similar to the rule MD051, by trying to have as much as possible the same behavior as the MD051 built-in rule in this regard).

GitHub supports links fragments for referring to a specific line, e.g:

[Link](#L7)

#L7 means "Go To Line 7`.

More context: theoludwig/markdownlint-rule-relative-links#6

Currently MD051 reports an error for that kind of fragments, but it should not, as they are valid on GitHub.

This PR fixes the issue, similarly to how it is done in markdownlint-rule-relative-links.
In the custom rule, we can go even further and check if the file has enough lines to be a valid line number reference (e.g: #L7 in a empty file, is invalid, as line 7 doesn't exist), but that needs access to Node.js node:fs, which as far as I understand, you don't want to use for built-in rule for compatibility with the browser. So for now we're just considering it's valid without extra checks.

Feel free to suggest ideas, suggest better implementation, or maybe you have ideas of more test cases to add?

Thanks!

lib/md051.js Outdated Show resolved Hide resolved
lib/md051.js Outdated Show resolved Hide resolved
@theoludwig
Copy link
Contributor Author

Test Repos seems failing by unrelated changes. As the errors are:

test-repos/v8-v8-dev/src/features/import-attributes.md: 59: MD034/no-bare-urls Bare URL used [Context: "https://chromestatus.com/featu..."]␊
  - test-repos/v8-v8-dev/src/features/import-attributes.md: 61: MD034/no-bare-urls Bare URL used [Context: "https://developer.apple.com/do..."]␊
  - test-repos/v8-v8-dev/src/features/import-attributes.md: 63: MD034/no-bare-urls Bare URL used [Context: "https://babeljs.io/blog/2023/0..."]␊

Which MD034 is not updated with this PR.
I've updated the snaptshots in this commit: d19f30a (#1117)

@DavidAnson
Copy link
Owner

Cool, thanks, I'll start a review in a moment!

A couple of things:

  1. Is the GitHub behavior documented anywhere? It may be nice to mention this scenario in the docs for this rule and link to that page.
  2. Thanks for respecting the desire to work on the web and not use node:fs. :)

@DavidAnson
Copy link
Owner

  1. I fixed the test-repos issue. That specific test is sensitive because it pulls the latest from external repos and tests against them, failing if any violations have changed. This is deliberate on my part, so random failures in that test are a feature, not a bug.

Copy link
Owner

@DavidAnson DavidAnson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me! :) Please let me know if the simplification I propose works - I think it cuts out a lot of this code. Also, please mention the rule allows "#L123" because of GitHub in the docs. doc-build/md051.md is the file to edit and everything else will get generated by npm run ci.

lib/md051.js Outdated Show resolved Hide resolved
lib/md051.js Show resolved Hide resolved
lib/md051.js Outdated Show resolved Hide resolved
lib/md051.js Outdated Show resolved Hide resolved
test/link-fragments.md Outdated Show resolved Hide resolved
test/link-fragments.md Outdated Show resolved Hide resolved
test/link-fragments.md Outdated Show resolved Hide resolved
test/snapshots/markdownlint-test-repos-small.js.md Outdated Show resolved Hide resolved
lib/md051.js Fixed Show fixed Hide fixed
lib/md051.js Fixed Show fixed Hide fixed
@theoludwig
Copy link
Contributor Author

Is the GitHub behavior documented anywhere? It may be nice to mention this scenario in the docs for this rule and link to that page.

Yes. It is documented here: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-a-permanent-link-to-a-code-snippet#linking-to-markdown
I've added the explanation in md051.md. 😄

@theoludwig theoludwig changed the title feat: support line number in fragment in MD051/link-fragments feat: support line number in fragment in MD051/link-fragments (e.g: '#L7') Feb 1, 2024
@DavidAnson
Copy link
Owner

The GitHub "specification" is pretty light on detail, but it was enough to remind me this syntax is more capable than just a single line number. For example, the following represents the full form I'm aware of which links to starting line/column and ending line/column: https://github.com/DavidAnson/markdownlint/blob/9c77f92616c063a5d42f165cae2edb9e3418ac53/CHANGELOG.md?plain=1#L30C11-L31C9

So the regular expression should be something more like ^L\d+(?:C\d+)?(?:-L\d+(?:C\d+)?)?$. I'll look at the rest of the changes now, but this probably means we should update the code, tests, and docs.

Copy link
Owner

@DavidAnson DavidAnson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like how this change shrunk, thank you! The docs are good to have as well. Biggest item in this review pass is the full LC-LC GitHub syntax from my separate comment.

@@ -38,6 +38,14 @@ attribute can be used to define a fragment:
An `a` tag can be useful in scenarios where a heading is not appropriate or for
control over the text of the fragment identifier.

GitHub also supports links to [specific lines][github-lines-links].

For example, to link to line 12 of current Markdown file:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I want to suggest doing this because GitHub also says you need to do plain=1. I'd say let's say something like "That syntax looks like this is practice:".

doc-build/md051.md Outdated Show resolved Hide resolved
lib/md051.js Outdated
continue;
}

if (encodedText.startsWith('#L')) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this part. Why does this need special handling? I'd assume if this were not done then we could move the lineFragmentsRe.test into the if below along with the other checks as I originally proposed. There was no lowercase conversion before, so I think there should not be one after. Maybe this gets to the other point about case sensitivity - I consider the "L111" form to be the only allowed form by this rule since that's what GitHub produces. Anyone using "l111" should switch - which will help avoid confusing letters and numbers. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this condition, the following test case fails:

### L12 ABC

[Valid](#L12-abc)

From what I understand, with your previous lowercase related comment, it is to be expected, as the link should have been [Valid](#l12-abc) as it is a heading.
But when it's about #L12, the L should be uppercase.

Looks fine to me, let's be stricter than GitHub about case sensitivity. But we should maybe document it in md051.md? As it might be unexpected for users (me included) that the rule reports errors for valid fragments because of stricter case sensitivity management.

Will simplify the code and make the necessary changes whenever I have time. 👍

@theoludwig
Copy link
Contributor Author

@DavidAnson Thank you for reviewing the PR. 😄
Sorry for the delay.

I should have addressed most feedbacks from reviews, notably:

  • Updated the documentation, to specify that the rule mandates the use of lowercase for link fragments for headings.
  • Simplified the code, as we don't support anymore uppercase/lowercase, e.g: [Link](#L7abc), it should be [Link](#l7abc) with a valid heading.
  • Support specifying columns (e.g: [Valid](#L30C11-L31C9)).

@DavidAnson
Copy link
Owner

This looks good, thank you! I think I want to iterate on the documentation a little after thinking about it some more - I'll do that tomorrow by pushing to this branch and then accept the PR.

@DavidAnson
Copy link
Owner

For posterity since I tried the combinations out, these are all the possibilities and how GitHub handles them:

HIGHLIGHT
#L30C11-L31C9
#L30C11-L31
#L30-L31
#L30-L31C9
#L30

NO HIGHLIGHT
#L30C11-C9
#C11-L31C9
#L30C11

IGNORED
#C11
#C11-C9

@DavidAnson
Copy link
Owner

... and this regular expression matches ONLY the top group above: ^(?:#L\d+(?:C\d+)?-L\d+(?:C\d+)?)|(?:#L\d+)$

Copy link
Owner

@DavidAnson DavidAnson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied the tweaks and will accept this PR in a moment - thank you again!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants