Skip to content

Commit 32cd425

Browse files
committed
fix: don't pad dashes adjacent to inline markup (#1029)
Signed-off-by: Joseph Kato <joseph@jdkato.io>
1 parent fa67a3e commit 32cd425

5 files changed

Lines changed: 46 additions & 16 deletions

File tree

internal/lint/ast.go

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,11 @@ func (l *Linter) lintHTMLTokens(f *core.File, raw []byte, offset int) error { //
109109
txt, skip = clean(txt, attr, skip || skipClass, inline)
110110
// `clean` prefixes inline content with a space so it doesn't
111111
// fuse with the preceding text. When that content directly
112-
// follows an opening bracket (e.g., a link inside parentheses,
113-
// `([HNSW](...))`), the space is spurious and breaks patterns
114-
// like `(ACRONYM)`. See #1056.
115-
if strings.HasPrefix(txt, " ") && endsWithOpenBracket(buf) {
112+
// follows a tight boundary -- an opening bracket (a link in
113+
// parentheses, `([HNSW](...))`, #1056) or a dash
114+
// (`Triggers—**Article**`, #1029) -- the space is spurious and
115+
// produces false positives like ` —` / `( ACRONYM)`.
116+
if strings.HasPrefix(txt, " ") && endsWithTightBoundary(buf) {
116117
txt = txt[1:]
117118
}
118119
buf.WriteString(txt)
@@ -230,16 +231,15 @@ func shouldBeSkipped(tagHistory []string, ext string) bool {
230231
return false
231232
}
232233

233-
// endsWithOpenBracket reports whether the buffer's last byte is an opening
234-
// bracket, meaning inline content that follows it shouldn't be padded with a
235-
// leading space. See #1056.
236-
func endsWithOpenBracket(buf *bytes.Buffer) bool {
237-
b := buf.Bytes()
238-
if len(b) == 0 {
239-
return false
240-
}
241-
switch b[len(b)-1] {
242-
case '(', '[', '{':
234+
// endsWithTightBoundary reports whether the buffer ends with a character that
235+
// binds tightly to what follows -- an opening bracket or a dash -- so inline
236+
// content placed after it shouldn't be padded with a leading space. Handles
237+
// "([HNSW](...))" (#1056) and "Triggers—**Article**" (#1029). We decode the
238+
// last rune because dashes are multi-byte.
239+
func endsWithTightBoundary(buf *bytes.Buffer) bool {
240+
r, _ := utf8.DecodeLastRune(buf.Bytes())
241+
switch r {
242+
case '(', '[', '{', '—', '–':
243243
return true
244244
default:
245245
return false
@@ -249,8 +249,10 @@ func endsWithOpenBracket(buf *bytes.Buffer) bool {
249249
func clean(txt, attr string, skip, inline bool) (string, bool) {
250250
// Closing brackets are included so that inline content immediately
251251
// followed by one (e.g., a link inside parentheses) doesn't get a spurious
252-
// space inserted before it -- "(HNSW)" rather than "(HNSW )". See #1056.
253-
punct := []string{".", "?", "!", ",", ":", ";", ")", "]", "}"}
252+
// space inserted before it -- "(HNSW)" rather than "(HNSW )" (#1056). Dashes
253+
// are included so text like "—This" right after a link isn't padded to
254+
// " —This" (#1029).
255+
punct := []string{".", "?", "!", ",", ":", ";", ")", "]", "}", "—", "–"}
254256
first, _ := utf8.DecodeRuneInString(txt)
255257
starter := core.StringInSlice(string(first), punct) && !skip
256258

testdata/features/misc.feature

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,15 @@ Feature: Misc
112112
test.md:50:11:Markup.SentSpacing:"d.A" must contain one and only one space.
113113
"""
114114

115+
Scenario: Dashes adjacent to inline markup (#1029)
116+
# A dash touching a link/bold (`[x](y)—z`, `**x**—z`, `x—**y**`) must not
117+
# gain a spurious surrounding space; a genuinely spaced dash still does.
118+
When I test "misc/dashes"
119+
Then the output should contain exactly:
120+
"""
121+
test.md:1:24:Test.Dashes:Remove the spaces around the dash.
122+
"""
123+
115124
Scenario: Spelling
116125
When I test "spelling"
117126
Then the output should contain exactly:
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
StylesPath = styles
2+
MinAlertLevel = suggestion
3+
4+
[*.md]
5+
BasedOnStyles = Test
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
extends: existence
2+
message: "Remove the spaces around the dash."
3+
level: error
4+
scope: text
5+
nonword: true
6+
tokens:
7+
- '\s—|—\s'
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
A genuinely spaced word — dash should be flagged on this line.
2+
3+
A [linked term](./url)—joined to following text on this line.
4+
5+
Bold **term**—joined to following text here on this line.
6+
7+
The Triggers—**Article Published** connector on this line.

0 commit comments

Comments
 (0)