-
Notifications
You must be signed in to change notification settings - Fork 2
/
source_provider.go
182 lines (156 loc) · 5.08 KB
/
source_provider.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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package provider
import (
"context"
"flag"
"fmt"
"log"
"path/filepath"
"strings"
"time"
"github.com/bazelbuild/bazel-gazelle/config"
"github.com/bazelbuild/bazel-gazelle/label"
"github.com/bazelbuild/bazel-gazelle/rule"
sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse"
"github.com/stackb/scala-gazelle/pkg/parser"
"github.com/stackb/scala-gazelle/pkg/resolver"
)
type progressFunc func(msg string)
// NewSourceProvider constructs a new NewSourceProvider.
func NewSourceProvider(progress progressFunc) *SourceProvider {
return &SourceProvider{
progress: progress,
parser: parser.NewScalametaParser(),
}
}
// SourceProvider is provider for scala source files. If -scala_source_index_in
// is configured, the given source index will be used to bootstrap the internal
// cache. At runtime the .ParseScalaRule function can be used to parse scala
// files. If the cache already has an entry for the filename with matching
// sha256, the cache hit will be used.
type SourceProvider struct {
progress progressFunc
// scope is the target we provide symbols to
scope resolver.Scope
// parser is an instance of the scala source parser
parser *parser.ScalametaParser
}
// Name implements part of the resolver.SymbolProvider interface.
func (r *SourceProvider) Name() string {
return "source"
}
// RegisterFlags implements part of the resolver.SymbolProvider interface.
func (r *SourceProvider) RegisterFlags(flags *flag.FlagSet, cmd string, c *config.Config) {
}
// CheckFlags implements part of the resolver.SymbolProvider interface.
func (r *SourceProvider) CheckFlags(flags *flag.FlagSet, c *config.Config, scope resolver.Scope) error {
r.scope = scope
return r.start()
}
// OnResolve implements part of the resolver.SymbolProvider interface.
func (r *SourceProvider) OnResolve() error {
r.parser.Stop()
return nil
}
// OnEnd implements part of the resolver.SymbolProvider interface.
func (r *SourceProvider) OnEnd() error {
return nil
}
// CanProvide implements the resolver.SymbolProvider interface.
func (cr *SourceProvider) CanProvide(from label.Label, ruleIndex func(from label.Label) (*rule.Rule, bool)) bool {
// if the label points to a rule that was generated by this extension
if _, ok := ruleIndex(from); ok {
return true
}
return false
}
// start begins the parser process.
func (r *SourceProvider) start() error {
if err := r.parser.Start(); err != nil {
return fmt.Errorf("starting parser: %w", err)
}
return nil
}
// ParseScalaRule implements scalarule.Parser
func (r *SourceProvider) ParseScalaRule(kind string, from label.Label, dir string, srcs ...string) (*sppb.Rule, error) {
if len(srcs) == 0 {
return nil, nil
}
t1 := time.Now()
files, err := r.parseFiles(from, dir, srcs)
if err != nil {
return nil, err
}
for _, file := range files {
if err := r.loadScalaFile(from, kind, file); err != nil {
return nil, err
}
}
t2 := time.Since(t1).Round(1 * time.Millisecond)
if true {
log.Printf("Parsed %s (%d files, %v)", from, len(files), t2)
}
return &sppb.Rule{
Label: from.String(),
Kind: kind,
Files: files,
ParseTimeMillis: t2.Milliseconds(),
}, nil
}
func (r *SourceProvider) parseFiles(from label.Label, dir string, srcs []string) ([]*sppb.File, error) {
filenames := make([]string, len(srcs))
for i, src := range srcs {
filenames[i] = filepath.Join(dir, src)
}
response, err := r.parser.Parse(context.Background(), &sppb.ParseRequest{
Filenames: filenames,
})
if err != nil {
return nil, fmt.Errorf("parse error: %v", err)
}
if response.Error != "" {
return nil, fmt.Errorf("parser error: %s", response.Error)
}
// remove dir prefixes
for _, file := range response.Files {
// TODO(pcj): isn't there a stdlib function that does this?
file.Filename = strings.TrimPrefix(strings.TrimPrefix(file.Filename, dir), "/")
}
return response.Files, nil
}
// LoadScalaRule loads the given state.
func (r *SourceProvider) LoadScalaRule(from label.Label, rule *sppb.Rule) error {
for _, file := range rule.Files {
if err := r.loadScalaFile(from, rule.Kind, file); err != nil {
return err
}
}
return nil
}
func (r *SourceProvider) loadScalaFile(from label.Label, kind string, file *sppb.File) error {
for _, imp := range file.Classes {
r.putSymbol(from, kind, imp, sppb.ImportType_CLASS)
}
for _, imp := range file.Objects {
r.putSymbol(from, kind, imp, sppb.ImportType_OBJECT)
}
for _, imp := range file.Traits {
r.putSymbol(from, kind, imp, sppb.ImportType_TRAIT)
}
for _, imp := range file.Types {
r.putSymbol(from, kind, imp, sppb.ImportType_TYPE)
}
for _, imp := range file.Vals {
r.putSymbol(from, kind, imp, sppb.ImportType_VALUE)
}
// TODO(pcj): should sources advertise their package symbols? Currently
// this leads the sitations where a lib will erroneously take on a dep to
// a binary rule.
//
// for _, imp := range file.Packages {
// r.putSymbol(from, kind, imp, sppb.ImportType_PACKAGE)
// }
return nil
}
func (r *SourceProvider) putSymbol(from label.Label, kind, imp string, impType sppb.ImportType) {
r.scope.PutSymbol(resolver.NewSymbol(impType, imp, kind, from))
}