• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mlange-42 / modo / 12858520582

20 Jan 2025 12:21AM CUT coverage: 56.633% (-0.8%) from 57.418%
12858520582

Pull #73

github

web-flow
Merge 76145dc26 into ef546a39d
Pull Request #73: Fix cross-refs to package and module aliases

8 of 34 new or added lines in 7 files covered. (23.53%)

2 existing lines in 2 files now uncovered.

841 of 1485 relevant lines covered (56.63%)

8.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.7
/document/links.go
1
package document
2

3
import (
4
        "fmt"
5
        "path"
6
        "regexp"
7
        "strings"
8
)
9

10
const regexString = `(?s)(?:(` + "```.*?```)|(`.*?`" + `))|(\[.*?\])`
11

12
// Replaces cross-refs by placeholders, recursively.
13
//
14
// Runs on the original docs, to packages can't have structs, traits or function yet.
15
func (proc *Processor) processLinksPackage(p *Package, elems []string) error {
4✔
16
        newElems := appendNew(elems, p.GetName())
4✔
17

4✔
18
        var err error
4✔
19
        p.Summary, err = proc.replaceRefs(p.Summary, newElems, len(newElems))
4✔
20
        if err != nil {
4✔
21
                return err
×
22
        }
×
23
        p.Description, err = proc.replaceRefs(p.Description, newElems, len(newElems))
4✔
24
        if err != nil {
4✔
25
                return err
×
26
        }
×
27

28
        for _, pkg := range p.Packages {
4✔
29
                proc.processLinksPackage(pkg, newElems)
×
30
        }
×
31
        for _, mod := range p.Modules {
9✔
32
                proc.processLinksModule(mod, newElems)
5✔
33
        }
5✔
34
        // Runs on the original docs, to packages can't have structs, traits or function yet.
35
        return nil
4✔
36
}
37

38
func (proc *Processor) processLinksModule(m *Module, elems []string) error {
5✔
39
        newElems := appendNew(elems, m.GetName())
5✔
40

5✔
41
        var err error
5✔
42
        m.Summary, err = proc.replaceRefs(m.Summary, newElems, len(newElems))
5✔
43
        if err != nil {
5✔
44
                return err
×
45
        }
×
46
        m.Description, err = proc.replaceRefs(m.Description, newElems, len(newElems))
5✔
47
        if err != nil {
5✔
48
                return err
×
49
        }
×
50

51
        for _, a := range m.Aliases {
6✔
52
                err := proc.processLinksModuleAlias(a, newElems)
1✔
53
                if err != nil {
1✔
54
                        return err
×
55
                }
×
56
        }
57
        for _, f := range m.Functions {
7✔
58
                err := proc.processLinksFunction(f, newElems)
2✔
59
                if err != nil {
2✔
60
                        return err
×
61
                }
×
62
        }
63
        for _, s := range m.Structs {
8✔
64
                err := proc.processLinksStruct(s, newElems)
3✔
65
                if err != nil {
3✔
66
                        return err
×
67
                }
×
68
        }
69
        for _, tr := range m.Traits {
7✔
70
                err := proc.processLinksTrait(tr, newElems)
2✔
71
                if err != nil {
2✔
72
                        return err
×
73
                }
×
74
        }
75

76
        return nil
5✔
77
}
78

79
func (proc *Processor) processLinksStruct(s *Struct, elems []string) error {
3✔
80
        newElems := appendNew(elems, s.GetName())
3✔
81

3✔
82
        var err error
3✔
83
        s.Summary, err = proc.replaceRefs(s.Summary, newElems, len(elems))
3✔
84
        if err != nil {
3✔
85
                return err
×
86
        }
×
87
        s.Description, err = proc.replaceRefs(s.Description, newElems, len(elems))
3✔
88
        if err != nil {
3✔
89
                return err
×
90
        }
×
91
        s.Deprecated, err = proc.replaceRefs(s.Deprecated, newElems, len(elems))
3✔
92
        if err != nil {
3✔
93
                return err
×
94
        }
×
95

96
        for _, a := range s.Aliases {
4✔
97
                a.Summary, err = proc.replaceRefs(a.Summary, newElems, len(elems))
1✔
98
                if err != nil {
1✔
99
                        return err
×
100
                }
×
101
                a.Description, err = proc.replaceRefs(a.Description, newElems, len(elems))
1✔
102
                if err != nil {
1✔
103
                        return err
×
104
                }
×
105
                a.Deprecated, err = proc.replaceRefs(a.Deprecated, newElems, len(elems))
1✔
106
                if err != nil {
1✔
107
                        return err
×
108
                }
×
109
        }
110
        for _, p := range s.Parameters {
4✔
111
                p.Description, err = proc.replaceRefs(p.Description, newElems, len(elems))
1✔
112
                if err != nil {
1✔
113
                        return err
×
114
                }
×
115
        }
116
        for _, f := range s.Fields {
5✔
117
                f.Summary, err = proc.replaceRefs(f.Summary, newElems, len(elems))
2✔
118
                if err != nil {
2✔
119
                        return err
×
120
                }
×
121
                f.Description, err = proc.replaceRefs(f.Description, newElems, len(elems))
2✔
122
                if err != nil {
2✔
123
                        return err
×
124
                }
×
125
        }
126
        for _, f := range s.Functions {
4✔
127
                if err := proc.processLinksMethod(f, elems); err != nil {
1✔
128
                        return err
×
129
                }
×
130
        }
131

132
        return nil
3✔
133
}
134

135
func (proc *Processor) processLinksTrait(tr *Trait, elems []string) error {
2✔
136
        newElems := appendNew(elems, tr.GetName())
2✔
137

2✔
138
        var err error
2✔
139
        tr.Summary, err = proc.replaceRefs(tr.Summary, newElems, len(elems))
2✔
140
        if err != nil {
2✔
141
                return err
×
142
        }
×
143
        tr.Description, err = proc.replaceRefs(tr.Description, newElems, len(elems))
2✔
144
        if err != nil {
2✔
145
                return err
×
146
        }
×
147
        tr.Deprecated, err = proc.replaceRefs(tr.Deprecated, newElems, len(elems))
2✔
148
        if err != nil {
2✔
149
                return err
×
150
        }
×
151

152
        // TODO: add when traits support parameters
153
        /*for _, p := range tr.Parameters {
154
                p.Description, err = replaceLinks(p.Description, newElems, len(elems), lookup, t)
155
                if err != nil {
156
                        return err
157
                }
158
        }*/
159
        for _, f := range tr.Fields {
3✔
160
                f.Summary, err = proc.replaceRefs(f.Summary, newElems, len(elems))
1✔
161
                if err != nil {
1✔
162
                        return err
×
163
                }
×
164
                f.Description, err = proc.replaceRefs(f.Description, newElems, len(elems))
1✔
165
                if err != nil {
1✔
166
                        return err
×
167
                }
×
168
        }
169
        for _, f := range tr.Functions {
3✔
170
                if err := proc.processLinksMethod(f, elems); err != nil {
1✔
171
                        return err
×
172
                }
×
173
        }
174

175
        return nil
2✔
176
}
177

178
func (proc *Processor) processLinksFunction(f *Function, elems []string) error {
4✔
179
        newElems := appendNew(elems, f.GetName())
4✔
180

4✔
181
        var err error
4✔
182
        f.Summary, err = proc.replaceRefs(f.Summary, newElems, len(elems))
4✔
183
        if err != nil {
4✔
184
                return err
×
185
        }
×
186
        f.Description, err = proc.replaceRefs(f.Description, newElems, len(elems))
4✔
187
        if err != nil {
4✔
188
                return err
×
189
        }
×
190
        f.Deprecated, err = proc.replaceRefs(f.Deprecated, newElems, len(elems))
4✔
191
        if err != nil {
4✔
192
                return err
×
193
        }
×
194
        f.ReturnsDoc, err = proc.replaceRefs(f.ReturnsDoc, newElems, len(elems))
4✔
195
        if err != nil {
4✔
196
                return err
×
197
        }
×
198
        f.RaisesDoc, err = proc.replaceRefs(f.RaisesDoc, newElems, len(elems))
4✔
199
        if err != nil {
4✔
200
                return err
×
201
        }
×
202

203
        for _, a := range f.Args {
5✔
204
                a.Description, err = proc.replaceRefs(a.Description, newElems, len(elems))
1✔
205
                if err != nil {
1✔
206
                        return err
×
207
                }
×
208
        }
209
        for _, p := range f.Parameters {
5✔
210
                p.Description, err = proc.replaceRefs(p.Description, newElems, len(elems))
1✔
211
                if err != nil {
1✔
212
                        return err
×
213
                }
×
214
        }
215

216
        for _, o := range f.Overloads {
6✔
217
                err := proc.processLinksFunction(o, elems)
2✔
218
                if err != nil {
2✔
219
                        return err
×
220
                }
×
221
        }
222

223
        return nil
4✔
224
}
225

226
func (proc *Processor) processLinksModuleAlias(a *Alias, elems []string) error {
1✔
227
        newElems := appendNew(elems, a.GetName())
1✔
228

1✔
229
        var err error
1✔
230
        a.Summary, err = proc.replaceRefs(a.Summary, newElems, len(elems))
1✔
231
        if err != nil {
1✔
232
                return err
×
233
        }
×
234
        a.Description, err = proc.replaceRefs(a.Description, newElems, len(elems))
1✔
235
        if err != nil {
1✔
236
                return err
×
237
        }
×
238
        a.Deprecated, err = proc.replaceRefs(a.Deprecated, newElems, len(elems))
1✔
239
        if err != nil {
1✔
240
                return err
×
241
        }
×
242
        return nil
1✔
243
}
244

245
func (proc *Processor) processLinksMethod(f *Function, elems []string) error {
4✔
246
        var err error
4✔
247
        f.Summary, err = proc.replaceRefs(f.Summary, elems, len(elems))
4✔
248
        if err != nil {
4✔
249
                return err
×
250
        }
×
251
        f.Description, err = proc.replaceRefs(f.Description, elems, len(elems))
4✔
252
        if err != nil {
4✔
253
                return err
×
254
        }
×
255
        f.Deprecated, err = proc.replaceRefs(f.Deprecated, elems, len(elems))
4✔
256
        if err != nil {
4✔
257
                return err
×
258
        }
×
259
        f.ReturnsDoc, err = proc.replaceRefs(f.ReturnsDoc, elems, len(elems))
4✔
260
        if err != nil {
4✔
261
                return err
×
262
        }
×
263
        f.RaisesDoc, err = proc.replaceRefs(f.RaisesDoc, elems, len(elems))
4✔
264
        if err != nil {
4✔
265
                return err
×
266
        }
×
267

268
        for _, a := range f.Args {
6✔
269
                a.Description, err = proc.replaceRefs(a.Description, elems, len(elems))
2✔
270
                if err != nil {
2✔
271
                        return err
×
272
                }
×
273
        }
274
        for _, p := range f.Parameters {
6✔
275
                p.Description, err = proc.replaceRefs(p.Description, elems, len(elems))
2✔
276
                if err != nil {
2✔
277
                        return err
×
278
                }
×
279
        }
280

281
        for _, o := range f.Overloads {
6✔
282
                err := proc.processLinksMethod(o, elems)
2✔
283
                if err != nil {
2✔
284
                        return err
×
285
                }
×
286
        }
287

288
        return nil
4✔
289
}
290

291
func (proc *Processor) replaceRefs(text string, elems []string, modElems int) (string, error) {
93✔
292
        indices, err := findLinks(text)
93✔
293
        if err != nil {
93✔
294
                return "", err
×
295
        }
×
296
        if len(indices) == 0 {
184✔
297
                return text, nil
91✔
298
        }
91✔
299
        for i := len(indices) - 2; i >= 0; i -= 2 {
10✔
300
                start, end := indices[i], indices[i+1]
8✔
301
                link := text[start+1 : end-1]
8✔
302

8✔
303
                content, ok, err := proc.refToPlaceholder(link, elems, modElems)
8✔
304
                if err != nil {
8✔
305
                        return "", err
×
306
                }
×
307
                if !ok {
8✔
308
                        continue
×
309
                }
310
                text = fmt.Sprintf("%s[%s]%s", text[:start], content, text[end:])
8✔
311
        }
312
        return text, nil
2✔
313
}
314

315
func (proc *Processor) ReplacePlaceholders(text string, elems []string, modElems int) (string, error) {
16✔
316
        indices, err := findLinks(text)
16✔
317
        if err != nil {
16✔
318
                return "", err
×
319
        }
×
320
        if len(indices) == 0 {
30✔
321
                return text, nil
14✔
322
        }
14✔
323
        for i := len(indices) - 2; i >= 0; i -= 2 {
10✔
324
                start, end := indices[i], indices[i+1]
8✔
325
                link := text[start+1 : end-1]
8✔
326

8✔
327
                entry, linkText, parts, ok, err := proc.placeholderToLink(link, elems, modElems, proc.Config.ShortLinks)
8✔
328
                if err != nil {
8✔
329
                        return "", err
×
330
                }
×
331
                if !ok {
8✔
332
                        continue
×
333
                }
334

335
                var basePath string
8✔
336
                if entry.IsSection {
10✔
337
                        basePath = path.Join(parts[:len(parts)-1]...)
2✔
338
                } else {
8✔
339
                        basePath = path.Join(parts...)
6✔
340
                }
6✔
341

342
                pathStr, err := proc.Formatter.ToLinkPath(basePath, entry.Kind)
8✔
343
                if err != nil {
8✔
344
                        return "", err
×
345
                }
×
346
                if entry.IsSection {
10✔
347
                        pathStr += parts[len(parts)-1]
2✔
348
                }
2✔
349
                text = fmt.Sprintf("%s[%s](%s)%s", text[:start], linkText, pathStr, text[end:])
8✔
350
        }
351
        return text, nil
2✔
352
}
353

354
func (proc *Processor) placeholderToLink(link string, elems []string, modElems int, shorten bool) (entry *elemPath, text string, parts []string, ok bool, err error) {
8✔
355
        linkParts := strings.SplitN(link, " ", 2)
8✔
356
        entry, text, parts, ok, err = proc.placeholderToRelLink(linkParts[0], elems, modElems)
8✔
357
        if err != nil {
8✔
358
                return
×
359
        }
×
360
        if !ok {
8✔
361
                return
×
362
        }
×
363
        if len(linkParts) > 1 {
9✔
364
                text = linkParts[1]
1✔
365
        } else {
8✔
366
                if shorten {
14✔
367
                        textParts := strings.Split(text, ".")
7✔
368
                        if entry.IsSection {
8✔
369
                                text = strings.Join(textParts[len(textParts)-2:], ".")
1✔
370
                        } else {
7✔
371
                                text = textParts[len(textParts)-1]
6✔
372
                        }
6✔
373
                }
374
                text = fmt.Sprintf("`%s`", text)
7✔
375
        }
376
        return
8✔
377
}
378

379
func (proc *Processor) placeholderToRelLink(link string, elems []string, modElems int) (*elemPath, string, []string, bool, error) {
8✔
380
        elemPath, ok := proc.linkTargets[link]
8✔
381
        if !ok {
8✔
382
                err := proc.warnOrError("Can't resolve cross ref placeholder '%s' in %s", link, strings.Join(elems, "."))
×
383
                return nil, "", nil, false, err
×
384
        }
×
385
        skip := 0
8✔
386
        for range modElems {
22✔
387
                if skip >= len(elemPath.Elements) {
15✔
388
                        break
1✔
389
                }
390
                if elemPath.Elements[skip] == elems[skip] {
24✔
391
                        skip++
11✔
392
                } else {
13✔
393
                        break
2✔
394
                }
395
        }
396
        fullPath := []string{}
8✔
397
        for range modElems - skip {
11✔
398
                fullPath = append(fullPath, "..")
3✔
399
        }
3✔
400
        fullPath = append(fullPath, elemPath.Elements[skip:]...)
8✔
401
        if len(fullPath) == 0 {
8✔
NEW
402
                fullPath = []string{"."}
×
403
        }
×
404
        return &elemPath, link, fullPath, true, nil
8✔
405
}
406

407
func (proc *Processor) refToPlaceholder(link string, elems []string, modElems int) (string, bool, error) {
8✔
408
        linkParts := strings.SplitN(link, " ", 2)
8✔
409

8✔
410
        var placeholder string
8✔
411
        var ok bool
8✔
412
        var err error
8✔
413
        if strings.HasPrefix(link, ".") {
14✔
414
                placeholder, ok, err = proc.refToPlaceholderRel(linkParts[0], elems, modElems)
6✔
415
        } else {
8✔
416
                placeholder, ok, err = proc.refToPlaceholderAbs(linkParts[0], elems)
2✔
417
        }
2✔
418
        if err != nil {
8✔
419
                return "", false, err
×
420
        }
×
421
        if !ok {
8✔
422
                return "", false, nil
×
423
        }
×
424

425
        if len(linkParts) > 1 {
9✔
426
                return fmt.Sprintf("%s %s", placeholder, linkParts[1]), true, nil
1✔
427
        } else {
8✔
428
                return placeholder, true, nil
7✔
429
        }
7✔
430
}
431

432
func (proc *Processor) refToPlaceholderRel(link string, elems []string, modElems int) (string, bool, error) {
6✔
433
        dots := 0
6✔
434
        for strings.HasPrefix(link[dots:], ".") {
13✔
435
                dots++
7✔
436
        }
7✔
437
        if dots > modElems {
6✔
438
                err := proc.warnOrError("Too many leading dots in cross ref '%s' in %s", link, strings.Join(elems, "."))
×
439
                return "", false, err
×
440
        }
×
441
        linkText := link[dots:]
6✔
442
        subElems := elems[:modElems-(dots-1)]
6✔
443
        var fullLink string
6✔
444
        if len(subElems) == 0 {
6✔
445
                fullLink = linkText
×
446
        } else {
6✔
447
                fullLink = strings.Join(subElems, ".") + "." + linkText
6✔
448
        }
6✔
449

450
        placeholder, ok := proc.linkExports[fullLink]
6✔
451
        if !ok {
6✔
452
                err := proc.warnOrError("Can't resolve cross ref (rel) '%s' (%s) in %s", link, fullLink, strings.Join(elems, "."))
×
453
                return "", false, err
×
454
        }
×
455
        return placeholder, true, nil
6✔
456
}
457

458
func (proc *Processor) refToPlaceholderAbs(link string, elems []string) (string, bool, error) {
2✔
459
        placeholder, ok := proc.linkExports[link]
2✔
460
        if !ok {
2✔
461
                err := proc.warnOrError("Can't resolve cross ref (abs) '%s' in %s", link, strings.Join(elems, "."))
×
462
                return "", false, err
×
463
        }
×
464
        return placeholder, true, nil
2✔
465
}
466

467
func findLinks(text string) ([]int, error) {
110✔
468
        re, err := regexp.Compile(regexString)
110✔
469
        if err != nil {
110✔
470
                return nil, err
×
471
        }
×
472
        links := []int{}
110✔
473
        results := re.FindAllStringSubmatchIndex(text, -1)
110✔
474
        for _, r := range results {
179✔
475
                if r[6] >= 0 {
100✔
476
                        if len(text) > r[7] && string(text[r[7]]) == "(" {
44✔
477
                                continue
13✔
478
                        }
479
                        links = append(links, r[6], r[7])
18✔
480
                }
481
        }
482

483
        return links, nil
110✔
484
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc