@@ -88,6 +88,14 @@ type walker struct {
8888 // since their position is derived from its own. Reset with the block.
8989 inline []inlineCapture
9090
91+ // pending holds the attribute values of the element just opened, waiting
92+ // to be masked out of the context. They can't be masked on the start tag
93+ // itself: an autolink's `href` and its text are the same source bytes, so
94+ // masking the attribute leaves the text token to find its string somewhere
95+ // else in the document -- and `idx`, which only moves forward, then places
96+ // every later block on that line. See #847.
97+ pending []string
98+
9199 begin int
92100 end int
93101
@@ -126,7 +134,22 @@ func (w *walker) update(txt string, tokt html.TokenType) {
126134 }
127135}
128136
137+ // flush masks the attribute values held since the last start tag.
138+ //
139+ // `text` is the element's own text, if it has arrived; an attribute equal to it
140+ // is dropped rather than masked, because the two are one run of source and
141+ // masking it twice consumes an occurrence that belongs to something else.
142+ func (w * walker ) flush (text string ) {
143+ for _ , val := range w .pending {
144+ if val != text {
145+ w .update (val , html .TextToken )
146+ }
147+ }
148+ w .pending = nil
149+ }
150+
129151func (w * walker ) reset () {
152+ w .flush ("" )
130153 for _ , s := range w .queue {
131154 w .update (s , html .TextToken )
132155 }
@@ -143,6 +166,10 @@ func (w *walker) getCtx() string {
143166
144167func (w * walker ) append (text string ) {
145168 if text != "" {
169+ // Before the search below, so the text finds its own occurrence, and
170+ // after the drop, so an autolink's `href` doesn't take it first.
171+ w .flush (text )
172+
146173 pos := w .advance (text )
147174 if pos > - 1 {
148175 w .idx = pos
@@ -340,6 +367,9 @@ func (w *walker) walk() (html.TokenType, html.Token, string) {
340367func (w * walker ) replaceToks (tok html.Token ) {
341368 tags := core .StringInSlice (tok .Data , []string {
342369 "img" , "a" , "p" , "script" , "h1" , "h2" , "h3" , "h4" , "h5" , "h6" , "span" })
370+ // Anything still waiting belongs to an element that had no text of its own.
371+ w .flush ("" )
372+
343373 if tags {
344374 names := []string {"href" , "id" , "src" , "alt" }
345375 if w .ext == ".html" {
@@ -356,7 +386,7 @@ func (w *walker) replaceToks(tok html.Token) {
356386 if a .Key == "href" {
357387 a .Val , _ = url .QueryUnescape (a .Val )
358388 }
359- w .update ( a . Val , html . TextToken )
389+ w .pending = append ( w . pending , a . Val )
360390 }
361391 }
362392 }
0 commit comments