-
Notifications
You must be signed in to change notification settings - Fork 1
/
section.go
139 lines (126 loc) · 4.26 KB
/
section.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package frontend
import (
"strings"
"github.com/khulnasoft-lab/godep/internal"
)
// A Section represents a collection of lines with a common prefix. The
// collection is itself divided into sections by prefix, forming a tree.
type Section struct {
Prefix string // prefix for this section, or if Subs==nil, a single line
Subs []*Section // subsections
NumLines int // total number of lines in subsections
}
func newSection(prefix string) *Section {
return &Section{Prefix: prefix, NumLines: 0}
}
func (s *Section) add(sub *Section) {
s.Subs = append(s.Subs, sub)
if sub.Subs == nil {
s.NumLines++
} else {
s.NumLines += sub.NumLines
}
}
// A prefixFunc returns the next prefix of s, given the current prefix.
// It should return the empty string if there are no more prefixes.
type prefixFunc func(s, prefix string) string
// Sections transforms a list of lines, which must be sorted, into a list
// of Sections. Each Section in the result contains all the contiguous lines
// with the same prefix.
//
// The nextPrefix function is responsible for extracting prefixes from lines.
func Sections(lines []string, nextPrefix prefixFunc) []*Section {
s, _ := section("", lines, nextPrefix)
return s.Subs
}
// section collects all lines with the same prefix into a section. It assumes
// that lines is sorted. It returns the section along with the remaining lines.
func section(prefix string, lines []string, nextPrefix prefixFunc) (*Section, []string) {
s := newSection(prefix)
for len(lines) > 0 {
l := lines[0]
if !strings.HasPrefix(l, prefix) {
break
}
np := nextPrefix(l, prefix)
var sub *Section
if np == "" {
sub = newSection(l)
lines = lines[1:]
} else {
sub, lines = section(np, lines, nextPrefix)
}
s.add(sub)
}
// Collapse a section with a single subsection, except at top level.
if len(s.Subs) == 1 && prefix != "" {
s = s.Subs[0]
}
return s, lines
}
// nextPrefixAccount is a prefixFunc (see above). Its first argument
// is an import path, and its second is the previous prefix that it returned
// for that path, or "" if this is the first prefix.
//
// nextPrefixAccount tries to return an initial prefix for path
// that consists of the "account": the entity controlling the
// remainder of the path. In the most common case, paths beginning
// with "github.com", the account is the second path element, the GitHub user or org.
// So for example, the first prefix of "github.com/google/go-cmp/cmp" is
// "github.com/google/".
//
// nextPrefixAccount returns a second prefix that is one path element past the
// account. For github.com paths, this is the repo. Continuing the above example,
// the second prefix is "github.com/google/go-cmp/".
//
// nextPrefixAccount does not return any prefixes beyond those two.
func nextPrefixAccount(path, pre string) string {
// If the last prefix consisted of the entire path, then
// there is no next prefix.
if path == pre {
return ""
}
parts := strings.Split(path, "/")
prefix1, acctParts := accountPrefix(parts)
if pre == "" {
return prefix1
}
if pre == prefix1 {
// Second prefix: one element past the first.
// The +1 is safe because we know that pre is shorter than path from
// the first test of the function.
prefix2 := strings.Join(parts[:len(acctParts)+1], "/")
if prefix2 != path {
prefix2 += "/"
}
return prefix2
}
// No more prefixes after the first two.
return ""
}
// accountPrefix guesses the prefix of the path (split into parts at "/")
// that corresponds to the account.
func accountPrefix(parts []string) (string, []string) {
// TODO(jba): handle repo import paths like "example.org/foo/bar.hg".
var n int // index of account in parts
// The first two cases below handle the special cases that the go command does.
// See "go help importpath".
switch {
case internal.VCSHostWithThreeElementRepoName(parts[0]):
n = 1
case parts[0] == "gopkg.in":
n = 1
case parts[0] == "hub.jazz.net":
n = 2
default:
// For custom import paths, use the host as the first prefix.
n = 0
}
if n >= len(parts)-1 {
return strings.Join(parts, "/"), parts
}
return strings.Join(parts[:n+1], "/") + "/", parts[:n+1]
}