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

Change interlinks specification. #315

Closed
has2k1 opened this issue Dec 11, 2023 · 8 comments
Closed

Change interlinks specification. #315

has2k1 opened this issue Dec 11, 2023 · 8 comments

Comments

@has2k1
Copy link
Contributor

has2k1 commented Dec 11, 2023

The current way interlinks are specified is adapted directly from the Intersphinx extension. This format uses reStructuredText Interpreted Text Role where, the colons and the backticks matter because the combination triggers the processing.

quartodoc implements interlinks using pandoc filters and the processing is triggered on markdown links where what matters is:

  1. The link-target matches our spec
  2. We can process the tokens in the target from right to left

So we do not need the combination of colons and backticks. We can do with only colons. Here is a comparison

Intersphinx Current Interlink Proposed Interlink
1 [](`target`) [](:target:)
2 :reftype:`target` [](:reftype:`target`) [](:reftype:target:)
3 :domain:reftype:`target` [](:domain:reftype:`target`) [](:domain:reftype:target:)
4 :external:domain:reftype:`target` [](:external:domain:reftype:`target`) [](:external:domain:reftype:target:)
5 :external+invname:domain:reftype:`target` [](:external+invname:domain:reftype:`target`) [](:external+invname:domain:reftype:target:)
6 :domain:reftype:`text<target>` [text](:domain:reftype:`target`) [text](:domain:reftype:target:)

Given the differences in processing Intersphinx format cannot (usually) represent only a target (case 1). And the translation rule to copy the Intersphinx stuff into the braces of a markdown link does not apply to all cases. We have a more natural solution for case 6.

While at best this saves us a single character, backticks are also a markdown syntax element and the proposed change invites less visual scrutiny and looks better without them.

This change can be implemented without affecting the current users. The current style may be dropped silently at first then deprecated with a warning.

@machow
Copy link
Owner

machow commented Dec 11, 2023

Thanks for taking the time to write this up, and make explicit comparisons across the 2 existing syntaxes and the proposed one.

The link syntax I most often think about is [some name](`some.target`). In this case, the hope with backticks was to differentiate it from everything else that can go in target.

It seems like there are two important reasons to differentiate the target:

  1. To avoid quarto mistaking the target as a file or url (e.g. [](elephant.png) could be referring to a file).
  2. To signal to users that something special is happening (an interlink).

It seems like backticks are nice for signaling an interlink, since they are often used to mark code related stuff. (See mkdocstrings cross ref docs for another example, where filters aren't used) .

I'm partial to keeping backticks, but open to changes. @wch and @schloerke any thoughts on interlink syntax?

@has2k1
Copy link
Contributor Author

has2k1 commented Dec 12, 2023

The link syntax I most often think about is [some name](`some.target`).

The backticks are okay if you think of them that way (this is code to be processed).

But that does not cover all the cases. We have these too:

[some name](:class:`some.target`)
[some name](:func:`some.target`)
[some name](:label:`some-ref`)
[some name](:py:label:`some-ref`)

which are RST roles. So we have inherited a syntax in an environment were some of its features are not required. The backticks no-longer mark all the code that will be processed and we are left with quirky thing with colons and backticks.

We can move the "sometimes" middle backtick to the beginning. This rescues the consistency of "this is a code to be processed".

[some name](`some.target`)
[some name](`:class:some.target`)
[some name](`:func:some.target`)
[some name](`:ref:some-ref`)
[some name](`:py:ref:some-ref`)

Also, noting that most interlinks do not change the link text, most of the time we would endup with.

[](`some.target`)
[](`:class:some.target`)
[](`:func:some.target`)
[](`:ref:some-ref`)
[](`:py:ref:some-ref`)

And when the reftype is specified we can use a shorter form (with only quotes).

[](`some.target`)
`:class:some.target`
`:func:some.target`
`:ref:some-ref`
`:py:ref:some-ref`

The full syntax that uses links would remain valid.

That means all these would link to the same location

[](`dict`)
[](`:class:dict`)
`:class:dict`
`:py:class:dict`

@machow
Copy link
Owner

machow commented Dec 12, 2023

But that does not cover all the cases. We have these too:

So we have inherited a syntax in an environment were some of its features are not required. The backticks no-longer mark all the code that will be processed and we are left with quirky thing with colons and backticks.

In my mind, the colons are a filtering syntax, which hopefully is not too commonly used (except in some standard library ambiguity cases).

If I understand, it sounds like the appeal to using all colons is consistency (since every part of the interlink would use the same separator :). Does that sound right?

@has2k1
Copy link
Contributor Author

has2k1 commented Dec 13, 2023

If I understand, it sounds like the appeal to using all colons is consistency (since every part of the interlink would use the same separator :). Does that sound right?

Yes.

Though now the second option of having the backticks around everything looks better because if there is a reference type (reftype), and no link text we can have the option to omit the link syntax and have an alternate short form.

The revised comparison becomes.

Intersphinx Current Interlink Proposed Interlink Proposed Short Form
1 [](`target`) [](`target`) `:target`
2 :reftype:`target` [](:reftype:`target`) [](`:reftype:target`) `:reftype:target`
3 :domain:reftype:`target` [](:domain:reftype:`target`) [](`:domain:reftype:target`) `:domain:reftype:target`
4 :external:domain:reftype:`target` [](:external:domain:reftype:`target`) [](`:external:domain:reftype:target`) `:external:domain:reftype:target`
5 :external+invname:domain:reftype:`target` [](:external+invname:domain:reftype:`target`) [](`:external+invname:domain:reftype:target`) `:external+invname:domain:reftype:target`
6 :domain:reftype:`text<target>` [text](:domain:reftype:`target`) [text](`:domain:reftype:target`)

Also note that, we can do interlinking with only [](`target`) and no reftype because the link syntax gives us something to process in the filter. On the other hand sphinx cannot get into processing mode with only `target`, it needs :reftype:`target`.

@gadenbuie
Copy link

It'd be super nice if you only had to use brackets and parens when you need to change the link name, so it'd be awesome to have a short form for [](`target`). Maybe `:target`?

@machow
Copy link
Owner

machow commented Dec 13, 2023

@gadenbuie agreed -- I think the trickiest thing here is figuring out something that is unambiguously an interlink in the pandoc AST.

For example, I think my original hope was that :py:target might resolve to something simple, but it becomes:

# quarto render test.qmd --to native --output --

[ Para [ Str ":py:" , Code ( "" , [] , [] ) "target" ] ]

I could see how @has2k1's proposed above shortform `:reftype:target` might help, since its AST is...

[ Para [ Code ( "" , [] , [] ) ":reftype:target" ] ]

Which would be pretty easy to identify from a filter. Would be nice if there were some easier way to add classes, etc.. to inline code elements.

AFAIK a way to get classes on inline code elements is to do things like `target`{.py}, which produces:

[ Para [ Code ( "" , [ "py" ] , [] ) "target" ] ]

@has2k1
Copy link
Contributor Author

has2k1 commented Dec 14, 2023

so it'd be awesome to have a short form for [](`target`). Maybe `:target`?

Yes. That can work. I have included it.

Then for consistency, should [](`target`) become [](`:target`)?

@machow
Copy link
Owner

machow commented Jan 22, 2024

I'm not too concerned about consistency, or removing the backtick in its own right. I'm going to close for now, and open an issue to focus exclusively on allowing an interlinks short-hand (e.g. using `:target` rather than the cumbersome ).

Changes to the interlinks syntax seem fair game if they (1) enable a easy short-hand and (2) don't risk conflicting quarto rendering. I'm open to the changes here described here!

Thanks for digging into this topic so deeply. It's super helpful context for thinking about short-hand options!

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

No branches or pull requests

3 participants