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

mlange-42 / modo / 12829003556

17 Jan 2025 12:24PM CUT coverage: 56.552% (+20.0%) from 36.563%
12829003556

Pull #54

github

web-flow
Merge 1a1b11cc2 into 41ff6f833
Pull Request #54: More unit tests

32 of 49 new or added lines in 4 files covered. (65.31%)

13 existing lines in 2 files now uncovered.

725 of 1282 relevant lines covered (56.55%)

6.69 hits per line

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

71.43
/document/links.go
1
package document
2

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

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

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

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

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

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

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

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

77
        return nil
5✔
78
}
79

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

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

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

121
        return nil
3✔
122
}
123

124
func (proc *Processor) processLinksTrait(tr *Trait, elems []string) error {
2✔
125
        newElems := AppendNew(elems, tr.GetName())
2✔
126

2✔
127
        var err error
2✔
128
        tr.Summary, err = proc.replaceRefs(tr.Summary, newElems, len(elems))
2✔
129
        if err != nil {
2✔
130
                return err
×
131
        }
×
132
        tr.Description, err = proc.replaceRefs(tr.Description, newElems, len(elems))
2✔
133
        if err != nil {
2✔
134
                return err
×
135
        }
×
136

137
        // TODO: add when traits support parameters
138
        /*for _, p := range tr.Parameters {
139
                p.Description, err = replaceLinks(p.Description, newElems, len(elems), lookup, t)
140
                if err != nil {
141
                        return err
142
                }
143
        }*/
144
        for _, f := range tr.Fields {
3✔
145
                f.Summary, err = proc.replaceRefs(f.Summary, newElems, len(elems))
1✔
146
                if err != nil {
1✔
147
                        return err
×
148
                }
×
149
                f.Description, err = proc.replaceRefs(f.Description, newElems, len(elems))
1✔
150
                if err != nil {
1✔
151
                        return err
×
152
                }
×
153
        }
154
        for _, f := range tr.Functions {
3✔
155
                if err := proc.processLinksMethod(f, elems); err != nil {
1✔
156
                        return err
×
157
                }
×
158
        }
159

160
        return nil
2✔
161
}
162

163
func (proc *Processor) processLinksFunction(f *Function, elems []string) error {
4✔
164
        newElems := AppendNew(elems, f.GetName())
4✔
165

4✔
166
        var err error
4✔
167
        f.Summary, err = proc.replaceRefs(f.Summary, newElems, len(elems))
4✔
168
        if err != nil {
4✔
169
                return err
×
170
        }
×
171
        f.Description, err = proc.replaceRefs(f.Description, newElems, len(elems))
4✔
172
        if err != nil {
4✔
173
                return err
×
174
        }
×
175
        f.ReturnsDoc, err = proc.replaceRefs(f.ReturnsDoc, newElems, len(elems))
4✔
176
        if err != nil {
4✔
177
                return err
×
178
        }
×
179
        f.RaisesDoc, err = proc.replaceRefs(f.RaisesDoc, newElems, len(elems))
4✔
180
        if err != nil {
4✔
181
                return err
×
182
        }
×
183

184
        for _, a := range f.Args {
5✔
185
                a.Description, err = proc.replaceRefs(a.Description, newElems, len(elems))
1✔
186
                if err != nil {
1✔
187
                        return err
×
188
                }
×
189
        }
190
        for _, p := range f.Parameters {
5✔
191
                p.Description, err = proc.replaceRefs(p.Description, newElems, len(elems))
1✔
192
                if err != nil {
1✔
193
                        return err
×
194
                }
×
195
        }
196

197
        for _, o := range f.Overloads {
6✔
198
                err := proc.processLinksFunction(o, elems)
2✔
199
                if err != nil {
2✔
200
                        return err
×
201
                }
×
202
        }
203

204
        return nil
4✔
205
}
206

207
func (proc *Processor) processLinksAlias(a *Alias, elems []string) error {
1✔
208
        newElems := AppendNew(elems, a.GetName())
1✔
209

1✔
210
        var err error
1✔
211
        a.Summary, err = proc.replaceRefs(a.Summary, newElems, len(elems))
1✔
212
        if err != nil {
1✔
213
                return err
×
214
        }
×
215
        a.Description, err = proc.replaceRefs(a.Description, newElems, len(elems))
1✔
216
        if err != nil {
1✔
217
                return err
×
218
        }
×
219
        return nil
1✔
220
}
221

222
func (proc *Processor) processLinksMethod(f *Function, elems []string) error {
4✔
223
        var err error
4✔
224
        f.Summary, err = proc.replaceRefs(f.Summary, elems, len(elems))
4✔
225
        if err != nil {
4✔
226
                return err
×
227
        }
×
228
        f.Description, err = proc.replaceRefs(f.Description, elems, len(elems))
4✔
229
        if err != nil {
4✔
230
                return err
×
231
        }
×
232
        f.ReturnsDoc, err = proc.replaceRefs(f.ReturnsDoc, elems, len(elems))
4✔
233
        if err != nil {
4✔
234
                return err
×
235
        }
×
236
        f.RaisesDoc, err = proc.replaceRefs(f.RaisesDoc, elems, len(elems))
4✔
237
        if err != nil {
4✔
238
                return err
×
239
        }
×
240

241
        for _, a := range f.Args {
6✔
242
                a.Description, err = proc.replaceRefs(a.Description, elems, len(elems))
2✔
243
                if err != nil {
2✔
244
                        return err
×
245
                }
×
246
        }
247
        for _, p := range f.Parameters {
6✔
248
                p.Description, err = proc.replaceRefs(p.Description, elems, len(elems))
2✔
249
                if err != nil {
2✔
250
                        return err
×
251
                }
×
252
        }
253

254
        for _, o := range f.Overloads {
6✔
255
                err := proc.processLinksMethod(o, elems)
2✔
256
                if err != nil {
2✔
257
                        return err
×
258
                }
×
259
        }
260

261
        return nil
4✔
262
}
263

264
func (proc *Processor) replaceRefs(text string, elems []string, modElems int) (string, error) {
77✔
265
        indices, err := findLinks(text)
77✔
266
        if err != nil {
77✔
267
                return "", err
×
268
        }
×
269
        if len(indices) == 0 {
152✔
270
                return text, nil
75✔
271
        }
75✔
272
        for i := len(indices) - 2; i >= 0; i -= 2 {
9✔
273
                start, end := indices[i], indices[i+1]
7✔
274
                link := text[start+1 : end-1]
7✔
275

7✔
276
                content, ok := proc.refToPlaceholder(link, elems, modElems)
7✔
277
                if !ok {
12✔
278
                        continue
5✔
279
                }
280
                text = fmt.Sprintf("%s[%s]%s", text[:start], content, text[end:])
2✔
281
        }
282
        return text, nil
2✔
283
}
284

285
func (proc *Processor) ReplacePlaceholders(text string, elems []string, modElems int) (string, error) {
15✔
286
        indices, err := findLinks(text)
15✔
287
        if err != nil {
15✔
288
                return "", err
×
289
        }
×
290
        if len(indices) == 0 {
29✔
291
                return text, nil
14✔
292
        }
14✔
293
        for i := len(indices) - 2; i >= 0; i -= 2 {
3✔
294
                start, end := indices[i], indices[i+1]
2✔
295
                link := text[start+1 : end-1]
2✔
296

2✔
297
                entry, linkText, parts, ok := proc.placeholderToLink(link, elems, modElems, proc.ShortLinks)
2✔
298
                if !ok {
2✔
299
                        continue
×
300
                }
301

302
                var basePath string
2✔
303
                if entry.IsSection {
3✔
304
                        basePath = path.Join(parts[:len(parts)-1]...)
1✔
305
                } else {
2✔
306
                        basePath = path.Join(parts...)
1✔
307
                }
1✔
308

309
                pathStr, err := proc.Formatter.ToLinkPath(basePath, entry.Kind)
2✔
310
                if err != nil {
2✔
311
                        return "", err
×
312
                }
×
313
                if entry.IsSection {
3✔
314
                        pathStr += parts[len(parts)-1]
1✔
315
                }
1✔
316
                text = fmt.Sprintf("%s[%s](%s)%s", text[:start], linkText, pathStr, text[end:])
2✔
317
        }
318
        return text, nil
1✔
319
}
320

321
func (proc *Processor) placeholderToLink(link string, elems []string, modElems int, shorten bool) (entry *elemPath, text string, parts []string, ok bool) {
2✔
322
        linkParts := strings.SplitN(link, " ", 2)
2✔
323
        entry, text, parts, ok = proc.placeholderToAbsLink(linkParts[0], elems, modElems)
2✔
324
        if !ok {
2✔
325
                return
×
326
        }
×
327
        if len(linkParts) > 1 {
3✔
328
                text = linkParts[1]
1✔
329
        } else {
2✔
330
                if shorten {
2✔
331
                        textParts := strings.Split(text, ".")
1✔
332
                        if entry.IsSection {
1✔
333
                                text = strings.Join(textParts[len(textParts)-2:], ".")
×
334
                        } else {
1✔
335
                                text = textParts[len(textParts)-1]
1✔
336
                        }
1✔
337
                }
338
                text = fmt.Sprintf("`%s`", text)
1✔
339
        }
340
        return
2✔
341
}
342

343
func (proc *Processor) placeholderToAbsLink(link string, elems []string, modElems int) (*elemPath, string, []string, bool) {
2✔
344
        elemPath, ok := proc.linkTargets[link]
2✔
345
        if !ok {
2✔
346
                log.Printf("WARNING: Can't resolve cross ref '%s' in %s", link, strings.Join(elems, "."))
×
347
                return nil, "", nil, false
×
348
        }
×
349
        skip := 0
2✔
350
        for range modElems {
4✔
351
                if len(elemPath.Elements) <= skip {
2✔
352
                        break
×
353
                }
354
                if elemPath.Elements[skip] == elems[skip] {
4✔
355
                        skip++
2✔
356
                } else {
2✔
357
                        break
×
358
                }
359
        }
360
        fullPath := []string{}
2✔
361
        for range modElems - skip {
2✔
362
                fullPath = append(fullPath, "..")
×
363
        }
×
364
        fullPath = append(fullPath, elemPath.Elements[skip:]...)
2✔
365
        return &elemPath, link, fullPath, true
2✔
366
}
367

368
func (proc *Processor) refToPlaceholder(link string, elems []string, modElems int) (string, bool) {
7✔
369
        linkParts := strings.SplitN(link, " ", 2)
7✔
370

7✔
371
        var placeholder string
7✔
372
        var ok bool
7✔
373
        if strings.HasPrefix(link, ".") {
13✔
374
                placeholder, ok = proc.refToPlaceholderRel(linkParts[0], elems, modElems)
6✔
375
        } else {
7✔
376
                placeholder, ok = proc.refToPlaceholderAbs(linkParts[0], elems)
1✔
377
        }
1✔
378
        if !ok {
12✔
379
                return "", false
5✔
380
        }
5✔
381
        if len(linkParts) > 1 {
3✔
382
                return fmt.Sprintf("%s %s", placeholder, linkParts[1]), true
1✔
383
        } else {
2✔
384
                return placeholder, true
1✔
385
        }
1✔
386
}
387

388
func (proc *Processor) refToPlaceholderRel(link string, elems []string, modElems int) (string, bool) {
6✔
389
        dots := 0
6✔
390
        for strings.HasPrefix(link[dots:], ".") {
13✔
391
                dots++
7✔
392
        }
7✔
393
        if dots > modElems {
6✔
394
                log.Printf("WARNING: Too many leading dots in cross ref '%s' in %s", link, strings.Join(elems, "."))
×
395
                return "", false
×
396
        }
×
397
        linkText := link[dots:]
6✔
398
        subElems := elems[:modElems-(dots-1)]
6✔
399
        var fullLink string
6✔
400
        if len(subElems) == 0 {
6✔
401
                fullLink = linkText
×
402
        } else {
6✔
403
                fullLink = strings.Join(subElems, ".") + "." + linkText
6✔
404
        }
6✔
405

406
        placeholder, ok := proc.linkExports[fullLink]
6✔
407
        if !ok {
10✔
408
                log.Printf("WARNING: Can't resolve cross ref '%s' (%s) in %s", link, fullLink, strings.Join(elems, "."))
4✔
409
                return "", false
4✔
410
        }
4✔
411
        return placeholder, true
2✔
412
}
413

414
func (proc *Processor) refToPlaceholderAbs(link string, elems []string) (string, bool) {
1✔
415
        placeholder, ok := proc.linkExports[link]
1✔
416
        if !ok {
2✔
417
                log.Printf("WARNING: Can't resolve cross ref '%s' in %s", link, strings.Join(elems, "."))
1✔
418
                return "", false
1✔
419
        }
1✔
420
        return placeholder, true
×
421
}
422

423
func findLinks(text string) ([]int, error) {
93✔
424
        re, err := regexp.Compile(regexString)
93✔
425
        if err != nil {
93✔
NEW
426
                return nil, err
×
NEW
427
        }
×
428
        links := []int{}
93✔
429
        results := re.FindAllStringSubmatchIndex(text, -1)
93✔
430
        for _, r := range results {
154✔
431
                if r[6] >= 0 {
84✔
432
                        if len(text) > r[7] && string(text[r[7]]) == "(" {
35✔
433
                                continue
12✔
434
                        }
435
                        links = append(links, r[6], r[7])
11✔
436
                }
437
        }
438

439
        return links, nil
93✔
440
}
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