.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.0 / internal / lsp / tests / normalizer.go
1 // Copyright 2019 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package tests
6
7 import (
8         "path/filepath"
9         "strconv"
10         "strings"
11
12         "golang.org/x/tools/go/packages/packagestest"
13 )
14
15 type Normalizer struct {
16         path     string
17         slashed  string
18         escaped  string
19         fragment string
20 }
21
22 func CollectNormalizers(exported *packagestest.Exported) []Normalizer {
23         // build the path normalizing patterns
24         var normalizers []Normalizer
25         for _, m := range exported.Modules {
26                 for fragment := range m.Files {
27                         n := Normalizer{
28                                 path:     exported.File(m.Name, fragment),
29                                 fragment: fragment,
30                         }
31                         if n.slashed = filepath.ToSlash(n.path); n.slashed == n.path {
32                                 n.slashed = ""
33                         }
34                         quoted := strconv.Quote(n.path)
35                         if n.escaped = quoted[1 : len(quoted)-1]; n.escaped == n.path {
36                                 n.escaped = ""
37                         }
38                         normalizers = append(normalizers, n)
39                 }
40         }
41         return normalizers
42 }
43
44 // NormalizePrefix normalizes a single path at the front of the input string.
45 func NormalizePrefix(s string, normalizers []Normalizer) string {
46         for _, n := range normalizers {
47                 if t := strings.TrimPrefix(s, n.path); t != s {
48                         return n.fragment + t
49                 }
50                 if t := strings.TrimPrefix(s, n.slashed); t != s {
51                         return n.fragment + t
52                 }
53                 if t := strings.TrimPrefix(s, n.escaped); t != s {
54                         return n.fragment + t
55                 }
56         }
57         return s
58 }
59
60 // Normalize replaces all paths present in s with just the fragment portion
61 // this is used to make golden files not depend on the temporary paths of the files
62 func Normalize(s string, normalizers []Normalizer) string {
63         type entry struct {
64                 path     string
65                 index    int
66                 fragment string
67         }
68         var match []entry
69         // collect the initial state of all the matchers
70         for _, n := range normalizers {
71                 index := strings.Index(s, n.path)
72                 if index >= 0 {
73                         match = append(match, entry{n.path, index, n.fragment})
74                 }
75                 if n.slashed != "" {
76                         index := strings.Index(s, n.slashed)
77                         if index >= 0 {
78                                 match = append(match, entry{n.slashed, index, n.fragment})
79                         }
80                 }
81                 if n.escaped != "" {
82                         index := strings.Index(s, n.escaped)
83                         if index >= 0 {
84                                 match = append(match, entry{n.escaped, index, n.fragment})
85                         }
86                 }
87         }
88         // result should be the same or shorter than the input
89         var b strings.Builder
90         last := 0
91         for {
92                 // find the nearest path match to the start of the buffer
93                 next := -1
94                 nearest := len(s)
95                 for i, c := range match {
96                         if c.index >= 0 && nearest > c.index {
97                                 nearest = c.index
98                                 next = i
99                         }
100                 }
101                 // if there are no matches, we copy the rest of the string and are done
102                 if next < 0 {
103                         b.WriteString(s[last:])
104                         return b.String()
105                 }
106                 // we have a match
107                 n := &match[next]
108                 // copy up to the start of the match
109                 b.WriteString(s[last:n.index])
110                 // skip over the filename
111                 last = n.index + len(n.path)
112
113                 // Hack: In multi-module mode, we add a "testmodule/" prefix, so trim
114                 // it from the fragment.
115                 fragment := n.fragment
116                 if strings.HasPrefix(fragment, "testmodule") {
117                         split := strings.Split(filepath.ToSlash(fragment), "/")
118                         fragment = filepath.FromSlash(strings.Join(split[1:], "/"))
119                 }
120
121                 // add in the fragment instead
122                 b.WriteString(fragment)
123                 // see what the next match for this path is
124                 n.index = strings.Index(s[last:], n.path)
125                 if n.index >= 0 {
126                         n.index += last
127                 }
128         }
129 }