kaushalmodi / ox-hugo

A carefully crafted Org exporter back-end for Hugo
https://ox-hugo.scripter.co
GNU General Public License v3.0
867 stars 130 forks source link

Duplicated anchors cause conflict when there're same headings on one page #739

Closed clsty closed 2 months ago

clsty commented 2 months ago

I'm using the latest git version of ox-hugo.

Actual Behavior

In ox-hugo.el :

        (let* ((anchor (format "{#%s}" (org-hugo--get-anchor heading info))) ;https://gohugo.io/extras/crossreferences/

By default, the value here of the anchor is always the same (I know it by checking the exported markdown file), only if same heading on one page, which cause duplicated anchors.

Expected Behavior

  1. Auto rename the anchor, e.g. detect if anchor foo already exists, if yes, then rename the anchor to foo-1; detect again if foo-1 exists, if yes, then rename the anchor to foo-2; detect again... until foo-n does not exists, then use foo-n.

  2. Allow disabling this auto-add-anchor behavior. This will be especially helpful because the Hugo theme I'm using also support attribute for headings, e.g.

    ## An important heading
    {.h-danger}

    will rendered as a highlighted heading; but the auto added anchor {#foo} prevent this from functioning.

How to Reproduce the Issue

Example Org File

** a
*** foo
** b
*** foo

Generated Markdown File or Error

  ## a {#a}
  ### foo {#foo}
  ## b {#b}
  ### foo {#foo}

The two {#foo} cause conflict.

kaushalmodi commented 2 months ago

Hello, that's a known behavior. The only solution right now is that you set :CUSTOM_ID: uniquely for the duplicate heading. The general usecase envisioned was that it would be uncommon to have identical subheading on the same blog post, so any complicated solutions were not investigated for this.

clsty commented 2 months ago

The general usecase envisioned was that it would be uncommon to have identical subheading on the same blog post, so any complicated solutions were not investigated for this.

That is actually common for me, such as (just an example below, not real)

I've tried three well-known editors. Which one is the best? Just proceed reading.

## Emacs
### Basic info
...
### Package system
...

## Vim
### Basic info
...
### Package system
...

## VSCodium
### Basic info
...
### Package system
...

But how about other users? I don't know, maybe you're right.

However, I do hope that user is allowed to disable this auto-add-anchor behavior optionally. (I know that in such case some features like cross referencing may not work, that's OK, I'll use feature from Hugo to do that.)

One another reason is that, when I directly write the Markdown files, with same headings on one page without any anchors, the links on the generated HTML are automatically renamed to avoid duplicating, like #foo-1 #foo-2.

Since ox-hugo can not handle this auto-renaming, and Hugo (with the theme I'm using) can handle it, it's reasonable to allow user disable it on side of ox-hugo, and leave it to Hugo (with the theme they're using), right?

kaushalmodi commented 2 months ago

Can you try adding this to your emacs setup? This code forces the anchor to be an empty string "".

(defun my/org-hugo--heading-title--no-anchor (&rest args)
  "Force ANCHOR arg of `org-hugo--heading-title' to be an empty string.

List of `org-hugo--heading-title' args:

style level loffset title todo tags anchor numbers
    0     1       2     3    4    5      6
"
  (let ((empty-anchor ""))
    (setf (nth 6 (car args)) empty-anchor))
  (car args))
(advice-add 'org-hugo--heading-title :filter-args #'my/org-hugo--heading-title--no-anchor)

After evaluating this code, your example code will export to:

## a
### foo
## b
### foo

Note: This might result in a case where your Org link navigation works in Emacs, but they won't work in the Hugo generated HTML.


To remove the advice:

(advice-remove 'org-hugo--heading-title #'my/org-hugo--heading-title--no-anchor)

Reference for using advice combinators

clsty commented 2 months ago

Thanks a lot, this works just as you said.