Browse Source

Add the imports extraction

Signed-off-by: Vadim Markovtsev <vadim@sourced.tech>
Vadim Markovtsev 5 years ago
parent
commit
24d68998b9

+ 8 - 1
go.mod

@@ -26,16 +26,20 @@ require (
 	github.com/opentracing/opentracing-go v1.0.2 // indirect
 	github.com/pkg/errors v0.8.0
 	github.com/sergi/go-diff v1.0.0
+	github.com/smacker/go-tree-sitter v0.0.0-20191127230340-5368dabef05e // indirect
 	github.com/spf13/cobra v0.0.3
 	github.com/spf13/pflag v1.0.3
 	github.com/src-d/enry/v2 v2.1.0
-	github.com/stretchr/testify v1.3.0
+	github.com/src-d/imports v0.0.0-20191128152346-bf22b73550b0
+	github.com/stretchr/objx v0.2.0 // indirect
+	github.com/stretchr/testify v1.4.0
 	github.com/tensorflow/tensorflow v0.0.0-20180308082300-f73d7c90ed05 // indirect
 	golang.org/x/crypto v0.0.0-20180904163835-0709b304e793
 	google.golang.org/grpc v1.16.0 // indirect
 	gopkg.in/bblfsh/client-go.v3 v3.2.0
 	gopkg.in/bblfsh/sdk.v1 v1.17.0 // indirect
 	gopkg.in/bblfsh/sdk.v2 v2.14.1
+	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
 	gopkg.in/cheggaaa/pb.v1 v1.0.20
 	gopkg.in/neurosnap/sentences.v1 v1.0.6 // indirect
 	gopkg.in/src-d/go-billy-siva.v4 v4.3.0
@@ -44,4 +48,7 @@ require (
 	gopkg.in/src-d/go-git.v4 v4.10.0
 	gopkg.in/src-d/go-siva.v1 v1.4.0 // indirect
 	gopkg.in/vmarkovtsev/BiDiSentiment.v1 v1.0.0-20180311115214-75f168ddf161
+	gopkg.in/yaml.v2 v2.2.7 // indirect
 )
+
+replace github.com/smacker/go-tree-sitter => github.com/dennwc/go-tree-sitter v0.0.0-20191127160809-cea124db9399

+ 13 - 2
go.sum

@@ -19,6 +19,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dennwc/go-tree-sitter v0.0.0-20191127160809-cea124db9399 h1:s9UkbLl+1Wq+sQf0fe3qFRKB/J6Czag4PCW4Ut+/k4U=
+github.com/dennwc/go-tree-sitter v0.0.0-20191127160809-cea124db9399/go.mod h1:EiUuVMUfLQj8Sul+S8aKWJwQy7FRYnJCO2EWzf8F5hk=
 github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo=
 github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
@@ -29,8 +31,6 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjr
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/gliderlabs/ssh v0.1.1 h1:j3L6gSLQalDETeEg/Jg0mGY0/y/N6zI2xX1978P0Uqw=
 github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
-github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE=
 github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
@@ -87,6 +87,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/smacker/go-tree-sitter v0.0.0-20191120151204-b4adc5db3a99/go.mod h1:EiUuVMUfLQj8Sul+S8aKWJwQy7FRYnJCO2EWzf8F5hk=
 github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
@@ -97,10 +98,17 @@ github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
 github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
 github.com/src-d/go-oniguruma v1.1.0 h1:EG+Nm5n2JqWUaCjtM0NtutPxU7ZN5Tp50GWrrV8bTww=
 github.com/src-d/go-oniguruma v1.1.0/go.mod h1:chVbff8kcVtmrhxtZ3yBVLLquXbzCS6DrxQaAK/CeqM=
+github.com/src-d/imports v0.0.0-20191128121413-18ad3ce8f5f3 h1:Zz6PYzQw+Ks72Xj5L+xdfCOzFvAprVYJOu8IK4HpGo0=
+github.com/src-d/imports v0.0.0-20191128121413-18ad3ce8f5f3/go.mod h1:ISTW49VJmFByM3SNgvDoC/6iZCWoBGaEpsSw3XCArcg=
+github.com/src-d/imports v0.0.0-20191128152346-bf22b73550b0 h1:EAW7jKWZnLYRlBDvXI294w2R8yzXtMSPL+Q8XdJEST0=
+github.com/src-d/imports v0.0.0-20191128152346-bf22b73550b0/go.mod h1:ISTW49VJmFByM3SNgvDoC/6iZCWoBGaEpsSw3XCArcg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/tensorflow/tensorflow v0.0.0-20180308082300-f73d7c90ed05 h1:s3/JFc2zHAyc2QKv4yb7hbiFjx8g3F8DVr2HaP9kdBY=
 github.com/tensorflow/tensorflow v0.0.0-20180308082300-f73d7c90ed05/go.mod h1:itOSERT4trABok4UOoG+X4BoKds9F3rIsySdn+Lvu90=
 github.com/toqueteos/trie v1.0.0 h1:8i6pXxNUXNRAqP246iibb7w/pSFquNTQ+uNfriG7vlk=
@@ -138,6 +146,7 @@ gopkg.in/bblfsh/sdk.v2 v2.14.1/go.mod h1:H/uxybs1j7MNuEEoiht9VzYkuQ7aUUjTtEUBtKf
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/cheggaaa/pb.v1 v1.0.20 h1:kgQVoCjFPiI1fNjdWthabnG1rOAb+/7Z6KeGk2aeZ/w=
 gopkg.in/cheggaaa/pb.v1 v1.0.20/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 gopkg.in/neurosnap/sentences.v1 v1.0.6 h1:v7ElyP020iEZQONyLld3fHILHWOPs+ntzuQTNPkul8E=
@@ -162,4 +171,6 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
+gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

+ 236 - 102
internal/pb/pb.pb.go

@@ -1443,6 +1443,128 @@ func (m *TyposDataset) GetTypos() []*Typo {
 	return nil
 }
 
+type ImportsPerLanguage struct {
+	Counts               map[string]int64 `protobuf:"bytes,1,rep,name=counts,proto3" json:"counts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
+	XXX_NoUnkeyedLiteral struct{}         `json:"-"`
+	XXX_unrecognized     []byte           `json:"-"`
+	XXX_sizecache        int32            `json:"-"`
+}
+
+func (m *ImportsPerLanguage) Reset()         { *m = ImportsPerLanguage{} }
+func (m *ImportsPerLanguage) String() string { return proto.CompactTextString(m) }
+func (*ImportsPerLanguage) ProtoMessage()    {}
+func (*ImportsPerLanguage) Descriptor() ([]byte, []int) {
+	return fileDescriptor_f80abaa17e25ccc8, []int{26}
+}
+func (m *ImportsPerLanguage) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ImportsPerLanguage.Unmarshal(m, b)
+}
+func (m *ImportsPerLanguage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ImportsPerLanguage.Marshal(b, m, deterministic)
+}
+func (m *ImportsPerLanguage) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ImportsPerLanguage.Merge(m, src)
+}
+func (m *ImportsPerLanguage) XXX_Size() int {
+	return xxx_messageInfo_ImportsPerLanguage.Size(m)
+}
+func (m *ImportsPerLanguage) XXX_DiscardUnknown() {
+	xxx_messageInfo_ImportsPerLanguage.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ImportsPerLanguage proto.InternalMessageInfo
+
+func (m *ImportsPerLanguage) GetCounts() map[string]int64 {
+	if m != nil {
+		return m.Counts
+	}
+	return nil
+}
+
+type ImportsPerDeveloper struct {
+	Languages            map[string]*ImportsPerLanguage `protobuf:"bytes,1,rep,name=languages,proto3" json:"languages,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+	XXX_NoUnkeyedLiteral struct{}                       `json:"-"`
+	XXX_unrecognized     []byte                         `json:"-"`
+	XXX_sizecache        int32                          `json:"-"`
+}
+
+func (m *ImportsPerDeveloper) Reset()         { *m = ImportsPerDeveloper{} }
+func (m *ImportsPerDeveloper) String() string { return proto.CompactTextString(m) }
+func (*ImportsPerDeveloper) ProtoMessage()    {}
+func (*ImportsPerDeveloper) Descriptor() ([]byte, []int) {
+	return fileDescriptor_f80abaa17e25ccc8, []int{27}
+}
+func (m *ImportsPerDeveloper) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ImportsPerDeveloper.Unmarshal(m, b)
+}
+func (m *ImportsPerDeveloper) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ImportsPerDeveloper.Marshal(b, m, deterministic)
+}
+func (m *ImportsPerDeveloper) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ImportsPerDeveloper.Merge(m, src)
+}
+func (m *ImportsPerDeveloper) XXX_Size() int {
+	return xxx_messageInfo_ImportsPerDeveloper.Size(m)
+}
+func (m *ImportsPerDeveloper) XXX_DiscardUnknown() {
+	xxx_messageInfo_ImportsPerDeveloper.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ImportsPerDeveloper proto.InternalMessageInfo
+
+func (m *ImportsPerDeveloper) GetLanguages() map[string]*ImportsPerLanguage {
+	if m != nil {
+		return m.Languages
+	}
+	return nil
+}
+
+type ImportsPerDeveloperResults struct {
+	Imports              []*ImportsPerDeveloper `protobuf:"bytes,1,rep,name=imports,proto3" json:"imports,omitempty"`
+	AuthorIndex          []string               `protobuf:"bytes,2,rep,name=author_index,json=authorIndex,proto3" json:"author_index,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}               `json:"-"`
+	XXX_unrecognized     []byte                 `json:"-"`
+	XXX_sizecache        int32                  `json:"-"`
+}
+
+func (m *ImportsPerDeveloperResults) Reset()         { *m = ImportsPerDeveloperResults{} }
+func (m *ImportsPerDeveloperResults) String() string { return proto.CompactTextString(m) }
+func (*ImportsPerDeveloperResults) ProtoMessage()    {}
+func (*ImportsPerDeveloperResults) Descriptor() ([]byte, []int) {
+	return fileDescriptor_f80abaa17e25ccc8, []int{28}
+}
+func (m *ImportsPerDeveloperResults) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ImportsPerDeveloperResults.Unmarshal(m, b)
+}
+func (m *ImportsPerDeveloperResults) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ImportsPerDeveloperResults.Marshal(b, m, deterministic)
+}
+func (m *ImportsPerDeveloperResults) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ImportsPerDeveloperResults.Merge(m, src)
+}
+func (m *ImportsPerDeveloperResults) XXX_Size() int {
+	return xxx_messageInfo_ImportsPerDeveloperResults.Size(m)
+}
+func (m *ImportsPerDeveloperResults) XXX_DiscardUnknown() {
+	xxx_messageInfo_ImportsPerDeveloperResults.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ImportsPerDeveloperResults proto.InternalMessageInfo
+
+func (m *ImportsPerDeveloperResults) GetImports() []*ImportsPerDeveloper {
+	if m != nil {
+		return m.Imports
+	}
+	return nil
+}
+
+func (m *ImportsPerDeveloperResults) GetAuthorIndex() []string {
+	if m != nil {
+		return m.AuthorIndex
+	}
+	return nil
+}
+
 type AnalysisResults struct {
 	Header *Metadata `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
 	// the mapped values are dynamic messages which require the second parsing pass.
@@ -1456,7 +1578,7 @@ func (m *AnalysisResults) Reset()         { *m = AnalysisResults{} }
 func (m *AnalysisResults) String() string { return proto.CompactTextString(m) }
 func (*AnalysisResults) ProtoMessage()    {}
 func (*AnalysisResults) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f80abaa17e25ccc8, []int{26}
+	return fileDescriptor_f80abaa17e25ccc8, []int{29}
 }
 func (m *AnalysisResults) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_AnalysisResults.Unmarshal(m, b)
@@ -1526,6 +1648,11 @@ func init() {
 	proto.RegisterType((*CommitsAnalysisResults)(nil), "CommitsAnalysisResults")
 	proto.RegisterType((*Typo)(nil), "Typo")
 	proto.RegisterType((*TyposDataset)(nil), "TyposDataset")
+	proto.RegisterType((*ImportsPerLanguage)(nil), "ImportsPerLanguage")
+	proto.RegisterMapType((map[string]int64)(nil), "ImportsPerLanguage.CountsEntry")
+	proto.RegisterType((*ImportsPerDeveloper)(nil), "ImportsPerDeveloper")
+	proto.RegisterMapType((map[string]*ImportsPerLanguage)(nil), "ImportsPerDeveloper.LanguagesEntry")
+	proto.RegisterType((*ImportsPerDeveloperResults)(nil), "ImportsPerDeveloperResults")
 	proto.RegisterType((*AnalysisResults)(nil), "AnalysisResults")
 	proto.RegisterMapType((map[string][]byte)(nil), "AnalysisResults.ContentsEntry")
 }
@@ -1533,105 +1660,112 @@ func init() {
 func init() { proto.RegisterFile("pb.proto", fileDescriptor_f80abaa17e25ccc8) }
 
 var fileDescriptor_f80abaa17e25ccc8 = []byte{
-	// 1596 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x57, 0xcd, 0x6e, 0xdb, 0x46,
-	0x10, 0x06, 0xf5, 0x4b, 0x8d, 0x64, 0xb9, 0xde, 0xb8, 0x31, 0xa3, 0xc0, 0x89, 0x42, 0xb8, 0xad,
-	0xdb, 0x34, 0x4c, 0xe0, 0x20, 0x40, 0x9a, 0x5e, 0x6a, 0xcb, 0x0d, 0x62, 0x20, 0x3f, 0x2d, 0xe5,
-	0xa4, 0xe8, 0x25, 0x02, 0x4d, 0xae, 0x2d, 0x36, 0xd2, 0x92, 0xd8, 0x25, 0x25, 0x2b, 0x68, 0xaf,
-	0x7d, 0x8c, 0xde, 0x7a, 0x29, 0xd0, 0x53, 0x5f, 0xa1, 0x28, 0x50, 0xf4, 0xd6, 0x87, 0xe8, 0x73,
-	0x14, 0xfb, 0x47, 0x91, 0x0a, 0x9d, 0xa6, 0x37, 0xce, 0xcc, 0x37, 0xbb, 0x33, 0xdf, 0xce, 0xce,
-	0x2c, 0xc1, 0x8c, 0x4f, 0x9c, 0x98, 0x46, 0x49, 0x64, 0xff, 0x53, 0x01, 0xf3, 0x09, 0x4e, 0xbc,
-	0xc0, 0x4b, 0x3c, 0x64, 0x41, 0x73, 0x86, 0x29, 0x0b, 0x23, 0x62, 0x19, 0x7d, 0x63, 0xb7, 0xee,
-	0x6a, 0x11, 0x21, 0xa8, 0x8d, 0x3d, 0x36, 0xb6, 0x2a, 0x7d, 0x63, 0xb7, 0xe5, 0x8a, 0x6f, 0x74,
-	0x0d, 0x80, 0xe2, 0x38, 0x62, 0x61, 0x12, 0xd1, 0x85, 0x55, 0x15, 0x96, 0x9c, 0x06, 0x7d, 0x08,
-	0xeb, 0x27, 0xf8, 0x2c, 0x24, 0xa3, 0x94, 0x84, 0xe7, 0xa3, 0x24, 0x9c, 0x62, 0xab, 0xd6, 0x37,
-	0x76, 0xab, 0xee, 0x9a, 0x50, 0x3f, 0x27, 0xe1, 0xf9, 0x71, 0x38, 0xc5, 0xc8, 0x86, 0x35, 0x4c,
-	0x82, 0x1c, 0xaa, 0x2e, 0x50, 0x6d, 0x4c, 0x82, 0x0c, 0x63, 0x41, 0xd3, 0x8f, 0xa6, 0xd3, 0x30,
-	0x61, 0x56, 0x43, 0x46, 0xa6, 0x44, 0x74, 0x05, 0x4c, 0x9a, 0x12, 0xe9, 0xd8, 0x14, 0x8e, 0x4d,
-	0x9a, 0x12, 0xe1, 0xf4, 0x08, 0x36, 0xb4, 0x69, 0x14, 0x63, 0x3a, 0x0a, 0x13, 0x3c, 0xb5, 0xcc,
-	0x7e, 0x75, 0xb7, 0xbd, 0xb7, 0xed, 0xe8, 0xa4, 0x1d, 0x57, 0xa2, 0xbf, 0xc2, 0xf4, 0x28, 0xc1,
-	0xd3, 0x2f, 0x49, 0x42, 0x17, 0x6e, 0x97, 0x16, 0x94, 0xbd, 0x7d, 0xb8, 0x54, 0x02, 0x43, 0xef,
-	0x41, 0xf5, 0x15, 0x5e, 0x08, 0xae, 0x5a, 0x2e, 0xff, 0x44, 0x9b, 0x50, 0x9f, 0x79, 0x93, 0x14,
-	0x0b, 0xa2, 0x0c, 0x57, 0x0a, 0x0f, 0x2a, 0xf7, 0x0d, 0xfb, 0x2e, 0x6c, 0x1d, 0xa4, 0x94, 0x04,
-	0xd1, 0x9c, 0x0c, 0x63, 0x8f, 0x32, 0xfc, 0xc4, 0x4b, 0x68, 0x78, 0xee, 0x46, 0x73, 0x99, 0xdc,
-	0x24, 0x9d, 0x12, 0x66, 0x19, 0xfd, 0xea, 0xee, 0x9a, 0xab, 0x45, 0xfb, 0x17, 0x03, 0x36, 0xcb,
-	0xbc, 0xf8, 0x79, 0x10, 0x6f, 0x8a, 0xd5, 0xd6, 0xe2, 0x1b, 0xed, 0x40, 0x97, 0xa4, 0xd3, 0x13,
-	0x4c, 0x47, 0xd1, 0xe9, 0x88, 0x46, 0x73, 0x26, 0x82, 0xa8, 0xbb, 0x1d, 0xa9, 0x7d, 0x76, 0xea,
-	0x46, 0x73, 0x86, 0x3e, 0x81, 0x8d, 0x25, 0x4a, 0x6f, 0x5b, 0x15, 0xc0, 0x75, 0x0d, 0x1c, 0x48,
-	0x35, 0xfa, 0x14, 0x6a, 0x62, 0x9d, 0x9a, 0xe0, 0xcc, 0x72, 0x2e, 0x48, 0xc0, 0x15, 0x28, 0xfb,
-	0x7b, 0xe8, 0x3e, 0x0c, 0x27, 0x98, 0x3d, 0x9b, 0x13, 0x4c, 0xd9, 0x38, 0x8c, 0xd1, 0x1d, 0xcd,
-	0x86, 0x21, 0x16, 0xe8, 0x39, 0x45, 0xbb, 0xf3, 0x82, 0x1b, 0x25, 0xe3, 0x12, 0xd8, 0xbb, 0x0f,
-	0xb0, 0x54, 0xe6, 0xf9, 0xad, 0x97, 0xf0, 0x5b, 0xcf, 0xf3, 0xfb, 0x63, 0x75, 0x49, 0xf0, 0x3e,
-	0xf1, 0x26, 0x0b, 0x16, 0x32, 0x17, 0xb3, 0x74, 0x92, 0x30, 0xd4, 0x87, 0xf6, 0x19, 0xf5, 0x48,
-	0x3a, 0xf1, 0x68, 0x98, 0xe8, 0xf5, 0xf2, 0x2a, 0xd4, 0x03, 0x93, 0x79, 0xd3, 0x78, 0x12, 0x92,
-	0x33, 0xb5, 0x74, 0x26, 0xa3, 0xdb, 0xd0, 0x8c, 0x69, 0xf4, 0x1d, 0xf6, 0x13, 0xc1, 0x53, 0x7b,
-	0xef, 0xfd, 0x72, 0x22, 0x34, 0x0a, 0xdd, 0x84, 0xfa, 0x29, 0x4f, 0x54, 0xf1, 0x76, 0x01, 0x5c,
-	0x62, 0xd0, 0x2d, 0x68, 0xc4, 0x38, 0x8a, 0x27, 0xbc, 0xec, 0xdf, 0x82, 0x56, 0x20, 0x74, 0x04,
-	0x48, 0x7e, 0x8d, 0x42, 0x92, 0x60, 0xea, 0xf9, 0x09, 0xbf, 0xad, 0x0d, 0x11, 0x57, 0xcf, 0x19,
-	0x44, 0xd3, 0x98, 0x62, 0xc6, 0x70, 0x20, 0x9d, 0xdd, 0x68, 0xae, 0xfc, 0x37, 0xa4, 0xd7, 0xd1,
-	0xd2, 0x09, 0xdd, 0x87, 0x75, 0x11, 0xc2, 0x28, 0xd2, 0x07, 0x62, 0x35, 0x45, 0x08, 0xeb, 0x2b,
-	0xe7, 0xe4, 0x76, 0x4f, 0x8b, 0xe7, 0x7a, 0x15, 0x5a, 0x49, 0xe8, 0xbf, 0x1a, 0xb1, 0xf0, 0x35,
-	0xb6, 0x4c, 0x71, 0xe9, 0x4c, 0xae, 0x18, 0x86, 0xaf, 0xb1, 0xfd, 0x9b, 0x01, 0x57, 0x2e, 0x8c,
-	0xa3, 0xa4, 0x48, 0x8d, 0x77, 0x2d, 0xd2, 0x4a, 0x79, 0x91, 0x22, 0xa8, 0xf1, 0x7b, 0x6c, 0x55,
-	0xfb, 0xd5, 0xdd, 0xaa, 0x5b, 0xd3, 0x8d, 0x2c, 0x24, 0x41, 0xe8, 0xab, 0x33, 0xa8, 0xbb, 0x5a,
-	0x44, 0x97, 0xa1, 0x11, 0x92, 0x20, 0x4e, 0xa8, 0xa0, 0xbb, 0xea, 0x2a, 0xc9, 0x1e, 0x42, 0x73,
-	0x10, 0xa5, 0x31, 0x3f, 0x91, 0x4d, 0xa8, 0x87, 0x24, 0xc0, 0xe7, 0xa2, 0x6a, 0x5b, 0xae, 0x14,
-	0xd0, 0x1e, 0x34, 0xa6, 0x22, 0x05, 0x11, 0xc7, 0xdb, 0xc9, 0x56, 0x48, 0x7b, 0x07, 0x3a, 0xc7,
-	0x51, 0xea, 0x8f, 0x71, 0x20, 0x08, 0xe5, 0x2b, 0xcb, 0xc2, 0x30, 0x44, 0x50, 0x52, 0xb0, 0xff,
-	0x34, 0xe0, 0xb2, 0xda, 0x7b, 0xb5, 0x70, 0x6f, 0x42, 0x87, 0x63, 0x46, 0xbe, 0x34, 0xab, 0x73,
-	0x36, 0x1d, 0x05, 0x77, 0xdb, 0xdc, 0xaa, 0xe3, 0xbe, 0x0d, 0x5d, 0x55, 0x1a, 0x1a, 0xde, 0x5c,
-	0x81, 0xaf, 0x49, 0xbb, 0x76, 0xb8, 0x03, 0x1d, 0xe5, 0x20, 0xa3, 0x92, 0xad, 0x71, 0xcd, 0xc9,
-	0xc7, 0xec, 0xb6, 0x25, 0x44, 0x26, 0x70, 0x1d, 0xda, 0xb2, 0x64, 0x26, 0x21, 0xc1, 0xcc, 0x6a,
-	0x89, 0x34, 0x40, 0xa8, 0x1e, 0x73, 0x8d, 0xfd, 0xb3, 0x01, 0xf0, 0x7c, 0x7f, 0x78, 0x3c, 0x18,
-	0x7b, 0xe4, 0x0c, 0xf3, 0x42, 0x11, 0xf1, 0xe7, 0x7a, 0x95, 0xc9, 0x15, 0x4f, 0x79, 0xbf, 0xda,
-	0x06, 0x60, 0xd4, 0x1f, 0x9d, 0xe0, 0xd3, 0x88, 0x62, 0x35, 0x59, 0x5a, 0x8c, 0xfa, 0x07, 0x42,
-	0xc1, 0x7d, 0xb9, 0xd9, 0x3b, 0x4d, 0x30, 0x55, 0xd3, 0xc5, 0x64, 0xd4, 0xdf, 0xe7, 0x32, 0x0f,
-	0x24, 0xf5, 0x58, 0xa2, 0x9d, 0x6b, 0x72, 0xf8, 0x70, 0x95, 0xf2, 0xde, 0x06, 0x21, 0x29, 0xf7,
-	0xba, 0x5c, 0x9c, 0x6b, 0x84, 0xbf, 0xfd, 0x05, 0x6c, 0x2d, 0xc3, 0x64, 0x43, 0x6f, 0x86, 0xa9,
-	0xe6, 0xfc, 0x03, 0x68, 0xfa, 0x52, 0xad, 0xda, 0x56, 0xdb, 0x59, 0x42, 0x5d, 0x6d, 0xb3, 0x7f,
-	0x37, 0xa0, 0x3b, 0x1c, 0x47, 0x09, 0xc1, 0x8c, 0xb9, 0xd8, 0x8f, 0x68, 0xc0, 0x2b, 0x31, 0x59,
-	0xc4, 0x59, 0x53, 0xe6, 0xdf, 0x59, 0xa3, 0xae, 0xe4, 0x1a, 0x35, 0x82, 0x1a, 0x27, 0x41, 0x25,
-	0x25, 0xbe, 0xd1, 0x67, 0x60, 0xfa, 0x51, 0xca, 0x6f, 0xa7, 0x6e, 0x1b, 0xdb, 0x4e, 0x71, 0x79,
-	0x7e, 0x8a, 0xc2, 0x2e, 0x1b, 0x66, 0x06, 0xef, 0x7d, 0x0e, 0x6b, 0x05, 0xd3, 0xff, 0x6a, 0x9b,
-	0x87, 0xb0, 0xa5, 0xb7, 0x59, 0x2d, 0xbe, 0x8f, 0xa1, 0x49, 0xc5, 0xce, 0x9a, 0x88, 0xf5, 0x95,
-	0x88, 0x5c, 0x6d, 0xb7, 0xff, 0x36, 0xa0, 0xcd, 0x2b, 0xe4, 0x51, 0xc8, 0xc4, 0xe8, 0xcf, 0x8d,
-	0x6b, 0x79, 0x89, 0xb2, 0x71, 0xfd, 0x02, 0x36, 0x15, 0x83, 0xa3, 0x93, 0xc5, 0x28, 0xc0, 0x33,
-	0x3c, 0x89, 0x62, 0x4c, 0xad, 0x8a, 0xd8, 0x61, 0xc7, 0xc9, 0xad, 0xe2, 0xa8, 0xd3, 0x39, 0x58,
-	0x1c, 0x6a, 0x98, 0x4c, 0x1d, 0xf9, 0x6f, 0x18, 0x7a, 0x5f, 0xc3, 0xd6, 0x05, 0xf0, 0x12, 0x3a,
-	0xfa, 0x79, 0x3a, 0xda, 0x7b, 0xe0, 0xf0, 0xe2, 0x1d, 0x26, 0x5e, 0xc2, 0xf2, 0xd4, 0xfc, 0x64,
-	0x80, 0x95, 0x0b, 0x47, 0xd2, 0xf2, 0x04, 0x33, 0xe6, 0x9d, 0x61, 0xf4, 0x20, 0x7f, 0x95, 0x57,
-	0x02, 0x2f, 0x20, 0x65, 0x2f, 0x55, 0x43, 0x4e, 0xb8, 0xf4, 0x1e, 0x02, 0x2c, 0x95, 0x25, 0x8f,
-	0x08, 0xbb, 0x18, 0x5e, 0xa7, 0xb0, 0x76, 0x2e, 0xc0, 0xe7, 0xd0, 0xca, 0x02, 0xe7, 0x47, 0xec,
-	0x05, 0x01, 0x0e, 0x54, 0x9e, 0x52, 0xe0, 0x07, 0x41, 0xf1, 0x34, 0x9a, 0xe1, 0x40, 0x1d, 0xbd,
-	0x16, 0xc5, 0x11, 0x09, 0xc2, 0x02, 0x35, 0xfd, 0xb5, 0x68, 0xff, 0x61, 0x40, 0xf3, 0x10, 0xcf,
-	0x8e, 0x43, 0xff, 0x55, 0xf1, 0x20, 0x0b, 0xef, 0xae, 0x3e, 0xd4, 0x19, 0xdf, 0xb8, 0x8c, 0x43,
-	0x61, 0x40, 0xf7, 0xa0, 0x35, 0xf1, 0xc8, 0x59, 0xea, 0xf1, 0xab, 0x54, 0x15, 0x34, 0x6d, 0x39,
-	0x6a, 0x61, 0xe7, 0xb1, 0xb6, 0x48, 0x66, 0x96, 0xc8, 0xde, 0x23, 0xe8, 0x16, 0x8d, 0x25, 0x0c,
-	0xbd, 0xdb, 0x01, 0xce, 0xc0, 0xe4, 0x7b, 0x1d, 0xe2, 0x19, 0x43, 0x1f, 0x41, 0x2d, 0xc0, 0x33,
-	0x7d, 0x5c, 0x97, 0x1c, 0x6d, 0xe0, 0x01, 0xa9, 0x18, 0x04, 0xa0, 0xb7, 0x0f, 0xad, 0x4c, 0x55,
-	0x52, 0x3a, 0xd7, 0x8a, 0x3b, 0x9b, 0x3a, 0xa1, 0xfc, 0xbe, 0x7f, 0x19, 0x70, 0x89, 0xaf, 0xb1,
-	0x7a, 0xa1, 0xee, 0x41, 0x9d, 0x4f, 0x49, 0x1d, 0xc4, 0x75, 0xa7, 0x04, 0x24, 0x02, 0xd3, 0xe5,
-	0x22, 0xd0, 0xbc, 0x11, 0x06, 0x78, 0x36, 0x92, 0x33, 0xa9, 0x22, 0xae, 0x93, 0x19, 0xe0, 0xd9,
-	0x91, 0x18, 0x4b, 0x6f, 0x1b, 0xc5, 0xbd, 0x01, 0xc0, 0x72, 0xb9, 0x92, 0x64, 0xae, 0x17, 0x93,
-	0x69, 0x65, 0xac, 0xe4, 0xb3, 0xf9, 0x06, 0x5a, 0x43, 0x4c, 0xf8, 0x23, 0x9a, 0x24, 0xcb, 0x46,
-	0xc2, 0x57, 0xa9, 0x28, 0x18, 0x7f, 0x3d, 0xf1, 0xb2, 0xc0, 0x44, 0x94, 0x83, 0x08, 0x50, 0xcb,
-	0xf9, 0x0a, 0xaa, 0x16, 0x5a, 0x01, 0xef, 0xa0, 0x5b, 0x03, 0x09, 0xcb, 0x36, 0xd0, 0x54, 0x7d,
-	0x0b, 0x1b, 0x4c, 0xeb, 0x78, 0xa3, 0xe0, 0x29, 0x29, 0xda, 0x6e, 0x39, 0x17, 0x38, 0x39, 0x99,
-	0xe2, 0x60, 0xc1, 0x13, 0x91, 0x24, 0xae, 0xb3, 0xa2, 0xb6, 0xf7, 0x14, 0x36, 0xcb, 0x80, 0xef,
-	0xd2, 0x26, 0x96, 0x3b, 0xe6, 0xf8, 0x79, 0x09, 0x30, 0x10, 0x19, 0xf1, 0x5b, 0x5a, 0xfa, 0x30,
-	0xef, 0x81, 0xa9, 0xcb, 0x5b, 0x0f, 0x32, 0x2d, 0x2f, 0xaf, 0x51, 0xed, 0x82, 0x6b, 0x64, 0xff,
-	0x00, 0x0d, 0xb9, 0x7e, 0xf6, 0x13, 0x66, 0xe4, 0x7e, 0xc2, 0x76, 0xa0, 0x3b, 0x1f, 0xe3, 0xfc,
-	0x3f, 0x56, 0x45, 0x14, 0x41, 0x87, 0x6b, 0xb3, 0xdf, 0xa7, 0xcb, 0xd0, 0xf0, 0xd2, 0x64, 0x1c,
-	0x51, 0x75, 0xd7, 0x95, 0x84, 0x6e, 0x14, 0x5f, 0xaa, 0x6d, 0x67, 0x99, 0x89, 0x7e, 0x9d, 0xbc,
-	0xe4, 0x8f, 0x13, 0x71, 0x60, 0xab, 0xe5, 0x7c, 0xa3, 0xd8, 0xe4, 0xdb, 0x7b, 0x4d, 0xe5, 0xbe,
-	0x6c, 0x12, 0x37, 0xa0, 0x23, 0x77, 0x2a, 0x54, 0x6f, 0x5b, 0xea, 0x44, 0x01, 0xdb, 0x33, 0xa8,
-	0x1d, 0x2f, 0xe2, 0x88, 0x57, 0xd6, 0x9c, 0x46, 0xe4, 0x4c, 0x65, 0x27, 0x05, 0x59, 0x3d, 0x94,
-	0xf2, 0xb7, 0xb7, 0x9c, 0xa0, 0x5a, 0xe4, 0x29, 0xc9, 0x5d, 0x14, 0xa5, 0x4a, 0xca, 0x86, 0x6b,
-	0x2d, 0x37, 0x5c, 0x11, 0xd4, 0xf8, 0x83, 0x45, 0x3c, 0x03, 0xea, 0xae, 0xf8, 0xb6, 0x6f, 0x42,
-	0x87, 0xef, 0xcb, 0x0e, 0xbd, 0xc4, 0x63, 0x38, 0x41, 0x57, 0xa1, 0x9e, 0x70, 0x59, 0xe5, 0x52,
-	0x77, 0xb8, 0xd5, 0x95, 0x3a, 0xfb, 0x57, 0x03, 0xd6, 0xdf, 0x4c, 0xbf, 0x31, 0xc6, 0x5e, 0x80,
-	0xa9, 0x88, 0x98, 0xdf, 0x1e, 0xfd, 0x4b, 0xe9, 0x2a, 0x03, 0x7a, 0xc0, 0xef, 0x05, 0x49, 0xb2,
-	0x7b, 0xd1, 0xde, 0xbb, 0xe6, 0xac, 0xde, 0xf7, 0x81, 0x02, 0x64, 0x53, 0x5d, 0x8a, 0x72, 0xaa,
-	0xe7, 0x4c, 0xff, 0xf5, 0xb3, 0xd9, 0xc9, 0xd5, 0xe4, 0x49, 0x43, 0xfc, 0xdc, 0xdf, 0xfd, 0x37,
-	0x00, 0x00, 0xff, 0xff, 0x7b, 0x21, 0x7f, 0x0b, 0xe8, 0x0f, 0x00, 0x00,
+	// 1699 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x58, 0xcd, 0x6e, 0x1b, 0x47,
+	0x12, 0xc6, 0xf0, 0x9f, 0x45, 0x8a, 0x5a, 0xb5, 0xb4, 0xd6, 0x98, 0x86, 0x6c, 0x7a, 0x56, 0xbb,
+	0x2b, 0xaf, 0xd7, 0x63, 0x43, 0x86, 0xb1, 0xb6, 0xf7, 0xb2, 0x12, 0xb5, 0x86, 0x05, 0xf8, 0x77,
+	0x28, 0x7b, 0xb1, 0x17, 0x13, 0x23, 0x4e, 0x4b, 0x9c, 0x98, 0xec, 0x19, 0x74, 0xcf, 0x90, 0xa2,
+	0x91, 0x5c, 0x83, 0x3c, 0x45, 0x6e, 0xb9, 0x24, 0xc8, 0x29, 0xaf, 0x10, 0x04, 0x08, 0x72, 0xcb,
+	0x43, 0xe4, 0x39, 0x82, 0xfe, 0x9b, 0x1f, 0x7a, 0x24, 0x2b, 0xb7, 0xa9, 0xaa, 0xaf, 0xbb, 0xab,
+	0xbe, 0xaa, 0xae, 0x6a, 0x12, 0x1a, 0xe1, 0xb1, 0x1d, 0xd2, 0x20, 0x0a, 0xac, 0xdf, 0x4a, 0xd0,
+	0x78, 0x8e, 0x23, 0xd7, 0x73, 0x23, 0x17, 0x99, 0x50, 0x9f, 0x61, 0xca, 0xfc, 0x80, 0x98, 0x46,
+	0xcf, 0xd8, 0xa9, 0x3a, 0x5a, 0x44, 0x08, 0x2a, 0x63, 0x97, 0x8d, 0xcd, 0x52, 0xcf, 0xd8, 0x69,
+	0x3a, 0xe2, 0x1b, 0x5d, 0x07, 0xa0, 0x38, 0x0c, 0x98, 0x1f, 0x05, 0x74, 0x61, 0x96, 0x85, 0x25,
+	0xa3, 0x41, 0x7f, 0x83, 0xd5, 0x63, 0x7c, 0xea, 0x93, 0x61, 0x4c, 0xfc, 0xb3, 0x61, 0xe4, 0x4f,
+	0xb1, 0x59, 0xe9, 0x19, 0x3b, 0x65, 0x67, 0x45, 0xa8, 0xdf, 0x10, 0xff, 0xec, 0xc8, 0x9f, 0x62,
+	0x64, 0xc1, 0x0a, 0x26, 0x5e, 0x06, 0x55, 0x15, 0xa8, 0x16, 0x26, 0x5e, 0x82, 0x31, 0xa1, 0x3e,
+	0x0a, 0xa6, 0x53, 0x3f, 0x62, 0x66, 0x4d, 0x7a, 0xa6, 0x44, 0x74, 0x15, 0x1a, 0x34, 0x26, 0x72,
+	0x61, 0x5d, 0x2c, 0xac, 0xd3, 0x98, 0x88, 0x45, 0x4f, 0x61, 0x4d, 0x9b, 0x86, 0x21, 0xa6, 0x43,
+	0x3f, 0xc2, 0x53, 0xb3, 0xd1, 0x2b, 0xef, 0xb4, 0x76, 0xb7, 0x6c, 0x1d, 0xb4, 0xed, 0x48, 0xf4,
+	0x2b, 0x4c, 0x0f, 0x23, 0x3c, 0xfd, 0x2f, 0x89, 0xe8, 0xc2, 0xe9, 0xd0, 0x9c, 0xb2, 0xbb, 0x07,
+	0xeb, 0x05, 0x30, 0xf4, 0x27, 0x28, 0xbf, 0xc7, 0x0b, 0xc1, 0x55, 0xd3, 0xe1, 0x9f, 0x68, 0x03,
+	0xaa, 0x33, 0x77, 0x12, 0x63, 0x41, 0x94, 0xe1, 0x48, 0xe1, 0x71, 0xe9, 0xa1, 0x61, 0xdd, 0x87,
+	0xcd, 0xfd, 0x98, 0x12, 0x2f, 0x98, 0x93, 0x41, 0xe8, 0x52, 0x86, 0x9f, 0xbb, 0x11, 0xf5, 0xcf,
+	0x9c, 0x60, 0x2e, 0x83, 0x9b, 0xc4, 0x53, 0xc2, 0x4c, 0xa3, 0x57, 0xde, 0x59, 0x71, 0xb4, 0x68,
+	0x7d, 0x6b, 0xc0, 0x46, 0xd1, 0x2a, 0x9e, 0x0f, 0xe2, 0x4e, 0xb1, 0x3a, 0x5a, 0x7c, 0xa3, 0x6d,
+	0xe8, 0x90, 0x78, 0x7a, 0x8c, 0xe9, 0x30, 0x38, 0x19, 0xd2, 0x60, 0xce, 0x84, 0x13, 0x55, 0xa7,
+	0x2d, 0xb5, 0x2f, 0x4f, 0x9c, 0x60, 0xce, 0xd0, 0x3f, 0x60, 0x2d, 0x45, 0xe9, 0x63, 0xcb, 0x02,
+	0xb8, 0xaa, 0x81, 0x7d, 0xa9, 0x46, 0xff, 0x84, 0x8a, 0xd8, 0xa7, 0x22, 0x38, 0x33, 0xed, 0x73,
+	0x02, 0x70, 0x04, 0xca, 0xfa, 0x1c, 0x3a, 0x4f, 0xfc, 0x09, 0x66, 0x2f, 0xe7, 0x04, 0x53, 0x36,
+	0xf6, 0x43, 0x74, 0x4f, 0xb3, 0x61, 0x88, 0x0d, 0xba, 0x76, 0xde, 0x6e, 0xbf, 0xe5, 0x46, 0xc9,
+	0xb8, 0x04, 0x76, 0x1f, 0x02, 0xa4, 0xca, 0x2c, 0xbf, 0xd5, 0x02, 0x7e, 0xab, 0x59, 0x7e, 0xbf,
+	0x2c, 0xa7, 0x04, 0xef, 0x11, 0x77, 0xb2, 0x60, 0x3e, 0x73, 0x30, 0x8b, 0x27, 0x11, 0x43, 0x3d,
+	0x68, 0x9d, 0x52, 0x97, 0xc4, 0x13, 0x97, 0xfa, 0x91, 0xde, 0x2f, 0xab, 0x42, 0x5d, 0x68, 0x30,
+	0x77, 0x1a, 0x4e, 0x7c, 0x72, 0xaa, 0xb6, 0x4e, 0x64, 0x74, 0x17, 0xea, 0x21, 0x0d, 0x3e, 0xc3,
+	0xa3, 0x48, 0xf0, 0xd4, 0xda, 0xfd, 0x73, 0x31, 0x11, 0x1a, 0x85, 0x6e, 0x43, 0xf5, 0x84, 0x07,
+	0xaa, 0x78, 0x3b, 0x07, 0x2e, 0x31, 0xe8, 0x0e, 0xd4, 0x42, 0x1c, 0x84, 0x13, 0x5e, 0xf6, 0x17,
+	0xa0, 0x15, 0x08, 0x1d, 0x02, 0x92, 0x5f, 0x43, 0x9f, 0x44, 0x98, 0xba, 0xa3, 0x88, 0xdf, 0xd6,
+	0x9a, 0xf0, 0xab, 0x6b, 0xf7, 0x83, 0x69, 0x48, 0x31, 0x63, 0xd8, 0x93, 0x8b, 0x9d, 0x60, 0xae,
+	0xd6, 0xaf, 0xc9, 0x55, 0x87, 0xe9, 0x22, 0xf4, 0x10, 0x56, 0x85, 0x0b, 0xc3, 0x40, 0x27, 0xc4,
+	0xac, 0x0b, 0x17, 0x56, 0x97, 0xf2, 0xe4, 0x74, 0x4e, 0xf2, 0x79, 0xbd, 0x06, 0xcd, 0xc8, 0x1f,
+	0xbd, 0x1f, 0x32, 0xff, 0x03, 0x36, 0x1b, 0xe2, 0xd2, 0x35, 0xb8, 0x62, 0xe0, 0x7f, 0xc0, 0xd6,
+	0x0f, 0x06, 0x5c, 0x3d, 0xd7, 0x8f, 0x82, 0x22, 0x35, 0x2e, 0x5b, 0xa4, 0xa5, 0xe2, 0x22, 0x45,
+	0x50, 0xe1, 0xf7, 0xd8, 0x2c, 0xf7, 0xca, 0x3b, 0x65, 0xa7, 0xa2, 0x1b, 0x99, 0x4f, 0x3c, 0x7f,
+	0xa4, 0x72, 0x50, 0x75, 0xb4, 0x88, 0xae, 0x40, 0xcd, 0x27, 0x5e, 0x18, 0x51, 0x41, 0x77, 0xd9,
+	0x51, 0x92, 0x35, 0x80, 0x7a, 0x3f, 0x88, 0x43, 0x9e, 0x91, 0x0d, 0xa8, 0xfa, 0xc4, 0xc3, 0x67,
+	0xa2, 0x6a, 0x9b, 0x8e, 0x14, 0xd0, 0x2e, 0xd4, 0xa6, 0x22, 0x04, 0xe1, 0xc7, 0xc5, 0x64, 0x2b,
+	0xa4, 0xb5, 0x0d, 0xed, 0xa3, 0x20, 0x1e, 0x8d, 0xb1, 0x27, 0x08, 0xe5, 0x3b, 0xcb, 0xc2, 0x30,
+	0x84, 0x53, 0x52, 0xb0, 0x7e, 0x36, 0xe0, 0x8a, 0x3a, 0x7b, 0xb9, 0x70, 0x6f, 0x43, 0x9b, 0x63,
+	0x86, 0x23, 0x69, 0x56, 0x79, 0x6e, 0xd8, 0x0a, 0xee, 0xb4, 0xb8, 0x55, 0xfb, 0x7d, 0x17, 0x3a,
+	0xaa, 0x34, 0x34, 0xbc, 0xbe, 0x04, 0x5f, 0x91, 0x76, 0xbd, 0xe0, 0x1e, 0xb4, 0xd5, 0x02, 0xe9,
+	0x95, 0x6c, 0x8d, 0x2b, 0x76, 0xd6, 0x67, 0xa7, 0x25, 0x21, 0x32, 0x80, 0x1b, 0xd0, 0x92, 0x25,
+	0x33, 0xf1, 0x09, 0x66, 0x66, 0x53, 0x84, 0x01, 0x42, 0xf5, 0x8c, 0x6b, 0xac, 0x6f, 0x0c, 0x80,
+	0x37, 0x7b, 0x83, 0xa3, 0xfe, 0xd8, 0x25, 0xa7, 0x98, 0x17, 0x8a, 0xf0, 0x3f, 0xd3, 0xab, 0x1a,
+	0x5c, 0xf1, 0x82, 0xf7, 0xab, 0x2d, 0x00, 0x46, 0x47, 0xc3, 0x63, 0x7c, 0x12, 0x50, 0xac, 0x26,
+	0x4b, 0x93, 0xd1, 0xd1, 0xbe, 0x50, 0xf0, 0xb5, 0xdc, 0xec, 0x9e, 0x44, 0x98, 0xaa, 0xe9, 0xd2,
+	0x60, 0x74, 0xb4, 0xc7, 0x65, 0xee, 0x48, 0xec, 0xb2, 0x48, 0x2f, 0xae, 0xc8, 0xe1, 0xc3, 0x55,
+	0x6a, 0xf5, 0x16, 0x08, 0x49, 0x2d, 0xaf, 0xca, 0xcd, 0xb9, 0x46, 0xac, 0xb7, 0xfe, 0x03, 0x9b,
+	0xa9, 0x9b, 0x6c, 0xe0, 0xce, 0x30, 0xd5, 0x9c, 0xff, 0x15, 0xea, 0x23, 0xa9, 0x56, 0x6d, 0xab,
+	0x65, 0xa7, 0x50, 0x47, 0xdb, 0xac, 0x1f, 0x0d, 0xe8, 0x0c, 0xc6, 0x41, 0x44, 0x30, 0x63, 0x0e,
+	0x1e, 0x05, 0xd4, 0xe3, 0x95, 0x18, 0x2d, 0xc2, 0xa4, 0x29, 0xf3, 0xef, 0xa4, 0x51, 0x97, 0x32,
+	0x8d, 0x1a, 0x41, 0x85, 0x93, 0xa0, 0x82, 0x12, 0xdf, 0xe8, 0x11, 0x34, 0x46, 0x41, 0xcc, 0x6f,
+	0xa7, 0x6e, 0x1b, 0x5b, 0x76, 0x7e, 0x7b, 0x9e, 0x45, 0x61, 0x97, 0x0d, 0x33, 0x81, 0x77, 0xff,
+	0x0d, 0x2b, 0x39, 0xd3, 0x1f, 0x6a, 0x9b, 0x07, 0xb0, 0xa9, 0x8f, 0x59, 0x2e, 0xbe, 0x5b, 0x50,
+	0xa7, 0xe2, 0x64, 0x4d, 0xc4, 0xea, 0x92, 0x47, 0x8e, 0xb6, 0x5b, 0xbf, 0x1a, 0xd0, 0xe2, 0x15,
+	0xf2, 0xd4, 0x67, 0x62, 0xf4, 0x67, 0xc6, 0xb5, 0xbc, 0x44, 0xc9, 0xb8, 0x7e, 0x0b, 0x1b, 0x8a,
+	0xc1, 0xe1, 0xf1, 0x62, 0xe8, 0xe1, 0x19, 0x9e, 0x04, 0x21, 0xa6, 0x66, 0x49, 0x9c, 0xb0, 0x6d,
+	0x67, 0x76, 0xb1, 0x55, 0x76, 0xf6, 0x17, 0x07, 0x1a, 0x26, 0x43, 0x47, 0xa3, 0x8f, 0x0c, 0xdd,
+	0xd7, 0xb0, 0x79, 0x0e, 0xbc, 0x80, 0x8e, 0x5e, 0x96, 0x8e, 0xd6, 0x2e, 0xd8, 0xbc, 0x78, 0x07,
+	0x91, 0x1b, 0xb1, 0x2c, 0x35, 0x5f, 0x1b, 0x60, 0x66, 0xdc, 0x91, 0xb4, 0x3c, 0xc7, 0x8c, 0xb9,
+	0xa7, 0x18, 0x3d, 0xce, 0x5e, 0xe5, 0x25, 0xc7, 0x73, 0x48, 0xd9, 0x4b, 0xd5, 0x90, 0x13, 0x4b,
+	0xba, 0x4f, 0x00, 0x52, 0x65, 0xc1, 0x23, 0xc2, 0xca, 0xbb, 0xd7, 0xce, 0xed, 0x9d, 0x71, 0xf0,
+	0x0d, 0x34, 0x13, 0xc7, 0x79, 0x8a, 0x5d, 0xcf, 0xc3, 0x9e, 0x8a, 0x53, 0x0a, 0x3c, 0x11, 0x14,
+	0x4f, 0x83, 0x19, 0xf6, 0x54, 0xea, 0xb5, 0x28, 0x52, 0x24, 0x08, 0xf3, 0xd4, 0xf4, 0xd7, 0xa2,
+	0xf5, 0x93, 0x01, 0xf5, 0x03, 0x3c, 0x3b, 0xf2, 0x47, 0xef, 0xf3, 0x89, 0xcc, 0xbd, 0xbb, 0x7a,
+	0x50, 0x65, 0xfc, 0xe0, 0x22, 0x0e, 0x85, 0x01, 0x3d, 0x80, 0xe6, 0xc4, 0x25, 0xa7, 0xb1, 0xcb,
+	0xaf, 0x52, 0x59, 0xd0, 0xb4, 0x69, 0xab, 0x8d, 0xed, 0x67, 0xda, 0x22, 0x99, 0x49, 0x91, 0xdd,
+	0xa7, 0xd0, 0xc9, 0x1b, 0x0b, 0x18, 0xba, 0x5c, 0x02, 0x67, 0xd0, 0xe0, 0x67, 0x1d, 0xe0, 0x19,
+	0x43, 0x7f, 0x87, 0x8a, 0x87, 0x67, 0x3a, 0x5d, 0xeb, 0xb6, 0x36, 0x70, 0x87, 0x94, 0x0f, 0x02,
+	0xd0, 0xdd, 0x83, 0x66, 0xa2, 0x2a, 0x28, 0x9d, 0xeb, 0xf9, 0x93, 0x1b, 0x3a, 0xa0, 0xec, 0xb9,
+	0xbf, 0x18, 0xb0, 0xce, 0xf7, 0x58, 0xbe, 0x50, 0x0f, 0xa0, 0xca, 0xa7, 0xa4, 0x76, 0xe2, 0x86,
+	0x5d, 0x00, 0x12, 0x8e, 0xe9, 0x72, 0x11, 0x68, 0xde, 0x08, 0x3d, 0x3c, 0x1b, 0xca, 0x99, 0x54,
+	0x12, 0xd7, 0xa9, 0xe1, 0xe1, 0xd9, 0xa1, 0x18, 0x4b, 0x17, 0x8d, 0xe2, 0x6e, 0x1f, 0x20, 0xdd,
+	0xae, 0x20, 0x98, 0x1b, 0xf9, 0x60, 0x9a, 0x09, 0x2b, 0xd9, 0x68, 0xfe, 0x07, 0xcd, 0x01, 0x26,
+	0xfc, 0x11, 0x4d, 0xa2, 0xb4, 0x91, 0xf0, 0x5d, 0x4a, 0x0a, 0xc6, 0x5f, 0x4f, 0xbc, 0x2c, 0x30,
+	0x11, 0xe5, 0x20, 0x1c, 0xd4, 0x72, 0xb6, 0x82, 0xca, 0xb9, 0x56, 0xc0, 0x3b, 0xe8, 0x66, 0x5f,
+	0xc2, 0x92, 0x03, 0x34, 0x55, 0xff, 0x87, 0x35, 0xa6, 0x75, 0xbc, 0x51, 0xf0, 0x90, 0x14, 0x6d,
+	0x77, 0xec, 0x73, 0x16, 0xd9, 0x89, 0x62, 0x7f, 0xc1, 0x03, 0x91, 0x24, 0xae, 0xb2, 0xbc, 0xb6,
+	0xfb, 0x02, 0x36, 0x8a, 0x80, 0x97, 0x69, 0x13, 0xe9, 0x89, 0x19, 0x7e, 0xde, 0x01, 0xf4, 0x45,
+	0x44, 0xfc, 0x96, 0x16, 0x3e, 0xcc, 0xbb, 0xd0, 0xd0, 0xe5, 0xad, 0x07, 0x99, 0x96, 0xd3, 0x6b,
+	0x54, 0x39, 0xe7, 0x1a, 0x59, 0x5f, 0x40, 0x4d, 0xee, 0x9f, 0xfc, 0x08, 0x33, 0x32, 0x3f, 0xc2,
+	0xb6, 0xa1, 0x33, 0x1f, 0xe3, 0xec, 0x6f, 0xac, 0x92, 0x28, 0x82, 0x36, 0xd7, 0x26, 0x3f, 0x9f,
+	0xae, 0x40, 0xcd, 0x8d, 0xa3, 0x71, 0x40, 0xd5, 0x5d, 0x57, 0x12, 0xba, 0x99, 0x7f, 0xa9, 0xb6,
+	0xec, 0x34, 0x12, 0xfd, 0x3a, 0x79, 0xc7, 0x1f, 0x27, 0x22, 0x61, 0xcb, 0xe5, 0x7c, 0x33, 0xdf,
+	0xe4, 0x5b, 0xbb, 0x75, 0xb5, 0x3c, 0x6d, 0x12, 0x37, 0xa1, 0x2d, 0x4f, 0xca, 0x55, 0x6f, 0x4b,
+	0xea, 0x44, 0x01, 0x5b, 0x33, 0xa8, 0x1c, 0x2d, 0xc2, 0x80, 0x57, 0xd6, 0x9c, 0x06, 0xe4, 0x54,
+	0x45, 0x27, 0x05, 0x59, 0x3d, 0x94, 0xf2, 0xb7, 0xb7, 0x9c, 0xa0, 0x5a, 0xe4, 0x21, 0xc9, 0x53,
+	0x14, 0xa5, 0x4a, 0x4a, 0x86, 0x6b, 0x25, 0x33, 0x5c, 0x11, 0x54, 0xf8, 0x83, 0x45, 0x3c, 0x03,
+	0xaa, 0x8e, 0xf8, 0xb6, 0x6e, 0x43, 0x9b, 0x9f, 0xcb, 0x0e, 0xdc, 0xc8, 0x65, 0x38, 0x42, 0xd7,
+	0xa0, 0x1a, 0x71, 0x59, 0xc5, 0x52, 0xb5, 0xb9, 0xd5, 0x91, 0x3a, 0xeb, 0x2b, 0x03, 0xd0, 0xe1,
+	0x34, 0x0c, 0x68, 0xc4, 0x5e, 0x61, 0xaa, 0xdb, 0x13, 0xfa, 0x17, 0xf7, 0x21, 0x26, 0x51, 0x7a,
+	0xa3, 0x3f, 0x06, 0xc9, 0xb1, 0xad, 0x6e, 0xb4, 0x82, 0x77, 0x1f, 0x41, 0x2b, 0xa3, 0xfe, 0xd4,
+	0xef, 0xc8, 0x72, 0xb6, 0xdc, 0xbe, 0x33, 0x60, 0x3d, 0x3d, 0x25, 0x19, 0x75, 0x68, 0x2f, 0xdb,
+	0x6d, 0xa5, 0x3b, 0x7f, 0xb1, 0x0b, 0x80, 0x17, 0x74, 0xde, 0xd7, 0x97, 0xe8, 0xbc, 0xb7, 0xf2,
+	0x77, 0x62, 0xbd, 0x20, 0xe2, 0xac, 0xb7, 0x01, 0x74, 0x0b, 0x7c, 0xd0, 0x15, 0x64, 0x43, 0xdd,
+	0x97, 0x56, 0xe5, 0xf1, 0x46, 0x91, 0xc7, 0x8e, 0x06, 0x5d, 0xa6, 0x9c, 0xbe, 0x37, 0x60, 0xf5,
+	0xe3, 0x42, 0xad, 0x8d, 0xb1, 0xeb, 0x61, 0x2a, 0x02, 0xe1, 0x7d, 0x4e, 0xff, 0xf8, 0x77, 0x94,
+	0x01, 0x3d, 0xe6, 0x1d, 0x8c, 0x44, 0x49, 0x07, 0x6b, 0xed, 0x5e, 0xb7, 0x97, 0x3b, 0x73, 0x5f,
+	0x01, 0x92, 0xf7, 0x97, 0x14, 0xe5, 0xfb, 0x2b, 0x63, 0xfa, 0x54, 0x3a, 0xdb, 0x19, 0x82, 0x8e,
+	0x6b, 0xe2, 0x6f, 0x98, 0xfb, 0xbf, 0x07, 0x00, 0x00, 0xff, 0xff, 0x8e, 0x4f, 0xe3, 0x8d, 0x92,
+	0x11, 0x00, 0x00,
 }

+ 13 - 0
internal/pb/pb.proto

@@ -182,6 +182,19 @@ message TyposDataset {
     repeated Typo typos = 1;
 }
 
+message ImportsPerLanguage {
+    map<string, int64> counts = 1;
+}
+
+message ImportsPerDeveloper {
+    map<string, ImportsPerLanguage> languages = 1;
+}
+
+message ImportsPerDeveloperResults {
+    repeated ImportsPerDeveloper imports = 1;
+    repeated string author_index = 2;
+}
+
 message AnalysisResults {
     Metadata header = 1;
     // the mapped values are dynamic messages which require the second parsing pass.

+ 1 - 1
internal/plumbing/blob_cache.go

@@ -123,7 +123,7 @@ func (blobCache *BlobCache) ListConfigurationOptions() []core.ConfigurationOptio
 		Name: ConfigBlobCacheFailOnMissingSubmodules,
 		Description: "Specifies whether to panic if any referenced submodule does " +
 			"not exist in .gitmodules and thus the corresponding Git object cannot be loaded. " +
-			"Override this if you want to ensure that your repository is integral. ",
+			"Override this if you want to ensure that your repository is integral.",
 		Flag:    "fail-on-missing-submodules",
 		Type:    core.BoolConfigurationOption,
 		Default: false}}

+ 176 - 0
internal/plumbing/imports/extractor.go

@@ -0,0 +1,176 @@
+package imports
+
+import (
+	"runtime"
+	"sync"
+
+	"github.com/src-d/imports"
+	_ "github.com/src-d/imports/languages/all" // register the supported languages
+	"gopkg.in/src-d/go-git.v4"
+	gitplumbing "gopkg.in/src-d/go-git.v4/plumbing"
+	"gopkg.in/src-d/go-git.v4/plumbing/object"
+	"gopkg.in/src-d/go-git.v4/utils/merkletrie"
+	"gopkg.in/src-d/hercules.v10/internal/core"
+	"gopkg.in/src-d/hercules.v10/internal/plumbing"
+)
+
+// Extractor reports the imports in the changed files.
+type Extractor struct {
+	core.NoopMerger
+	// Goroutines is the number of goroutines to run for imports extraction.
+	Goroutines int
+	// MaxFileSize is the file size threshold. Files that exceed it are ignored.
+	MaxFileSize int
+
+	l core.Logger
+}
+
+const (
+	// DependencyImports is the name of the dependency provided by Extractor.
+	DependencyImports = "imports"
+	// ConfigImportsGoroutines is the name of the configuration option for
+	// Extractor.Configure() to set the number of parallel goroutines for imports extraction.
+	ConfigImportsGoroutines = "Imports.Goroutines"
+	// ConfigMaxFileSize is the name of the configuration option for
+	// Extractor.Configure() to set the file size threshold after which they are ignored.
+	ConfigMaxFileSize = "Imports.MaxFileSize"
+	// DefaultMaxFileSize is the default value for Extractor.MaxFileSize.
+	DefaultMaxFileSize = 1 << 20
+)
+
+// Name of this PipelineItem. Uniquely identifies the type, used for mapping keys, etc.
+func (ex *Extractor) Name() string {
+	return "Imports"
+}
+
+// Provides returns the list of names of entities which are produced by this PipelineItem.
+// Each produced entity will be inserted into `deps` of dependent Consume()-s according
+// to this list. Also used by core.Registry to build the global map of providers.
+func (ex *Extractor) Provides() []string {
+	return []string{DependencyImports}
+}
+
+// Requires returns the list of names of entities which are needed by this PipelineItem.
+// Each requested entity will be inserted into `deps` of Consume(). In turn, those
+// entities are Provides() upstream.
+func (ex *Extractor) Requires() []string {
+	return []string{plumbing.DependencyTreeChanges, plumbing.DependencyBlobCache}
+}
+
+// ListConfigurationOptions returns the list of changeable public properties of this PipelineItem.
+func (ex *Extractor) ListConfigurationOptions() []core.ConfigurationOption {
+	return []core.ConfigurationOption{{
+		Name:        ConfigImportsGoroutines,
+		Description: "Specifies the number of goroutines to run in parallel for the imports extraction.",
+		Flag:        "import-goroutines",
+		Type:        core.IntConfigurationOption,
+		Default:     runtime.NumCPU()}, {
+		Name:        ConfigMaxFileSize,
+		Description: "Specifies the file size threshold. Files that exceed it are ignored.",
+		Flag:        "import-max-file-size",
+		Type:        core.IntConfigurationOption,
+		Default:     DefaultMaxFileSize},
+	}
+}
+
+// Configure sets the properties previously published by ListConfigurationOptions().
+func (ex *Extractor) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		ex.l = l
+	}
+	if gr, exists := facts[ConfigImportsGoroutines].(int); exists {
+		if gr < 1 {
+			if ex.l != nil {
+				ex.l.Warnf("invalid number of goroutines for the imports extraction: %d. Set to %d.",
+					gr, runtime.NumCPU())
+			}
+			gr = runtime.NumCPU()
+		}
+		ex.Goroutines = gr
+	}
+	if size, exists := facts[ConfigMaxFileSize].(int); exists {
+		if size <= 0 {
+			if ex.l != nil {
+				ex.l.Warnf("invalid maximum file size: %d. Set to %d.", size, DefaultMaxFileSize)
+			}
+			size = DefaultMaxFileSize
+		}
+		ex.MaxFileSize = size
+	}
+	return nil
+}
+
+// Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
+// calls. The repository which is going to be analysed is supplied as an argument.
+func (ex *Extractor) Initialize(repository *git.Repository) error {
+	ex.l = core.NewLogger()
+	if ex.Goroutines < 1 {
+		ex.Goroutines = runtime.NumCPU()
+	}
+	if ex.MaxFileSize == 0 {
+		ex.MaxFileSize = DefaultMaxFileSize
+	}
+	return nil
+}
+
+// Consume runs this PipelineItem on the next commit data.
+// `deps` contain all the results from upstream PipelineItem-s as requested by Requires().
+// Additionally, DependencyCommit is always present there and represents the analysed *object.Commit.
+// This function returns the mapping with analysis results. The keys must be the same as
+// in Provides(). If there was an error, nil is returned.
+func (ex *Extractor) Consume(deps map[string]interface{}) (map[string]interface{}, error) {
+	changes := deps[plumbing.DependencyTreeChanges].(object.Changes)
+	cache := deps[plumbing.DependencyBlobCache].(map[gitplumbing.Hash]*plumbing.CachedBlob)
+	result := map[gitplumbing.Hash]imports.File{}
+	jobs := make(chan *object.Change, ex.Goroutines)
+	resultSync := sync.Mutex{}
+	wg := sync.WaitGroup{}
+	wg.Add(ex.Goroutines)
+	for i := 0; i < ex.Goroutines; i++ {
+		go func() {
+			for change := range jobs {
+				blob := cache[change.To.TreeEntry.Hash]
+				if blob.Size > int64(ex.MaxFileSize) {
+					ex.l.Warnf("skipped %s %s: size is too big: %d > %d",
+						change.To.TreeEntry.Name, change.To.TreeEntry.Hash.String(),
+						blob.Size, ex.MaxFileSize)
+					continue
+				}
+				file, err := imports.Extract(change.To.TreeEntry.Name, blob.Data)
+				if err != nil {
+					ex.l.Errorf("failed to extract imports from %s %s: %v",
+						change.To.TreeEntry.Name, change.To.TreeEntry.Hash.String(), err)
+				} else {
+					resultSync.Lock()
+					result[change.To.TreeEntry.Hash] = *file
+					resultSync.Unlock()
+				}
+			}
+			wg.Done()
+		}()
+	}
+	for _, change := range changes {
+		action, err := change.Action()
+		if err != nil {
+			return nil, err
+		}
+		switch action {
+		case merkletrie.Modify, merkletrie.Insert:
+			jobs <- change
+		case merkletrie.Delete:
+			continue
+		}
+	}
+	close(jobs)
+	wg.Wait()
+	return map[string]interface{}{DependencyImports: result}, nil
+}
+
+// Fork clones this PipelineItem.
+func (ex *Extractor) Fork(n int) []core.PipelineItem {
+	return core.ForkSamePipelineItem(ex, n)
+}
+
+func init() {
+	core.Registry.Register(&Extractor{})
+}

+ 107 - 0
internal/plumbing/imports/extractor_test.go

@@ -0,0 +1,107 @@
+package imports
+
+import (
+	"runtime"
+	"testing"
+
+	"github.com/src-d/imports"
+	"github.com/stretchr/testify/assert"
+	gitplumbing "gopkg.in/src-d/go-git.v4/plumbing"
+	"gopkg.in/src-d/go-git.v4/plumbing/object"
+	"gopkg.in/src-d/hercules.v10/internal/core"
+	"gopkg.in/src-d/hercules.v10/internal/plumbing"
+	"gopkg.in/src-d/hercules.v10/internal/test"
+)
+
+func fixtureExtractor() *Extractor {
+	ex := &Extractor{}
+	ex.Initialize(test.Repository)
+	return ex
+}
+
+func TestExtractorConfigureInitialize(t *testing.T) {
+	ex := fixtureExtractor()
+	assert.Equal(t, runtime.NumCPU(), ex.Goroutines)
+	facts := map[string]interface{}{}
+	facts[ConfigImportsGoroutines] = 7
+	facts[ConfigMaxFileSize] = 8
+	assert.NoError(t, ex.Configure(facts))
+	assert.Equal(t, 7, ex.Goroutines)
+	assert.Equal(t, 8, ex.MaxFileSize)
+	facts[ConfigImportsGoroutines] = -1
+	facts[ConfigMaxFileSize] = -8
+	assert.NoError(t, ex.Configure(facts))
+	assert.Equal(t, runtime.NumCPU(), ex.Goroutines)
+	assert.Equal(t, DefaultMaxFileSize, ex.MaxFileSize)
+	assert.NotNil(t, ex.l)
+}
+
+func TestExtractorMetadata(t *testing.T) {
+	ex := fixtureExtractor()
+	assert.Equal(t, ex.Name(), "Imports")
+	assert.Equal(t, len(ex.Provides()), 1)
+	assert.Equal(t, ex.Provides()[0], DependencyImports)
+	assert.Equal(t, []string{plumbing.DependencyTreeChanges, plumbing.DependencyBlobCache}, ex.Requires())
+	opts := ex.ListConfigurationOptions()
+	assert.Len(t, opts, 2)
+	assert.Equal(t, opts[0].Name, ConfigImportsGoroutines)
+	assert.Equal(t, opts[0].Default.(int), runtime.NumCPU())
+	assert.Equal(t, opts[1].Name, ConfigMaxFileSize)
+	assert.Equal(t, opts[1].Default.(int), DefaultMaxFileSize)
+}
+
+func TestExtractorRegistration(t *testing.T) {
+	summoned := core.Registry.Summon((&Extractor{}).Name())
+	assert.Len(t, summoned, 1)
+	assert.Equal(t, summoned[0].Name(), "Imports")
+	summoned = core.Registry.Summon((&Extractor{}).Provides()[0])
+	assert.Len(t, summoned, 1)
+	assert.Equal(t, summoned[0].Name(), "Imports")
+}
+
+func TestExtractorConsumeModification(t *testing.T) {
+	commit, _ := test.Repository.CommitObject(gitplumbing.NewHash(
+		"af2d8db70f287b52d2428d9887a69a10bc4d1f46"))
+	changes := make(object.Changes, 1)
+	treeFrom, _ := test.Repository.TreeObject(gitplumbing.NewHash(
+		"80fe25955b8e725feee25c08ea5759d74f8b670d"))
+	treeTo, _ := test.Repository.TreeObject(gitplumbing.NewHash(
+		"63076fa0dfd93e94b6d2ef0fc8b1fdf9092f83c4"))
+	changes[0] = &object.Change{From: object.ChangeEntry{
+		Name: "labours.py",
+		Tree: treeFrom,
+		TreeEntry: object.TreeEntry{
+			Name: "labours.py",
+			Mode: 0100644,
+			Hash: gitplumbing.NewHash("1cacfc1bf0f048eb2f31973750983ae5d8de647a"),
+		},
+	}, To: object.ChangeEntry{
+		Name: "labours.py",
+		Tree: treeTo,
+		TreeEntry: object.TreeEntry{
+			Name: "labours.py",
+			Mode: 0100644,
+			Hash: gitplumbing.NewHash("c872b8d2291a5224e2c9f6edd7f46039b96b4742"),
+		},
+	}}
+	deps := map[string]interface{}{}
+	deps[core.DependencyCommit] = commit
+	deps[plumbing.DependencyTreeChanges] = changes
+	cache := &plumbing.BlobCache{}
+	assert.NoError(t, cache.Initialize(test.Repository))
+	blobs, err := cache.Consume(deps)
+	assert.NoError(t, err)
+	deps[plumbing.DependencyBlobCache] = blobs[plumbing.DependencyBlobCache]
+	result, err := fixtureExtractor().Consume(deps)
+	assert.NoError(t, err)
+	assert.Equal(t, len(result), 1)
+	exIface, exists := result[DependencyImports]
+	assert.True(t, exists)
+	ex := exIface.(map[gitplumbing.Hash]imports.File)
+	assert.Len(t, ex, 1)
+	file := ex[gitplumbing.NewHash("c872b8d2291a5224e2c9f6edd7f46039b96b4742")]
+	assert.Equal(t, "labours.py", file.Path)
+	assert.Equal(t, "Python", file.Lang)
+	assert.Equal(t, []string{"argparse", "datetime", "matplotlib", "matplotlib.pyplot", "numpy",
+		"pandas", "sys", "warnings"}, file.Imports)
+}

+ 181 - 0
leaves/imports_printer.go

@@ -0,0 +1,181 @@
+package leaves
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"log"
+
+	"github.com/gogo/protobuf/proto"
+	imports2 "github.com/src-d/imports"
+	"gopkg.in/src-d/go-git.v4"
+	"gopkg.in/src-d/go-git.v4/plumbing"
+	"gopkg.in/src-d/hercules.v10/internal/core"
+	"gopkg.in/src-d/hercules.v10/internal/pb"
+	"gopkg.in/src-d/hercules.v10/internal/plumbing/identity"
+	"gopkg.in/src-d/hercules.v10/internal/plumbing/imports"
+	"gopkg.in/src-d/hercules.v10/internal/yaml"
+)
+
+// ImportsPerDeveloper collects imports per developer.
+type ImportsPerDeveloper struct {
+	core.NoopMerger
+	core.OneShotMergeProcessor
+	// imports is the mapping from dev indexes to languages to import names to usage counts.
+	imports map[int]map[string]map[string]int
+	// reversedPeopleDict references IdentityDetector.ReversedPeopleDict
+	reversedPeopleDict []string
+
+	l core.Logger
+}
+
+// ImportsPerDeveloperResult is returned by Finalize() and represents the analysis result.
+type ImportsPerDeveloperResult struct {
+	// Imports is the mapping from dev indexes to languages to import names to usage counts.
+	Imports map[int]map[string]map[string]int
+	// reversedPeopleDict references IdentityDetector.ReversedPeopleDict
+	reversedPeopleDict []string
+}
+
+// Name of this PipelineItem. Uniquely identifies the type, used for mapping keys, etc.
+func (ipd *ImportsPerDeveloper) Name() string {
+	return "ImportsPerDeveloper"
+}
+
+// Provides returns the list of names of entities which are produced by this PipelineItem.
+// Each produced entity will be inserted into `deps` of dependent Consume()-s according
+// to this list. Also used by core.Registry to build the global map of providers.
+func (ipd *ImportsPerDeveloper) Provides() []string {
+	return []string{}
+}
+
+// Requires returns the list of names of entities which are needed by this PipelineItem.
+// Each requested entity will be inserted into `deps` of Consume(). In turn, those
+// entities are Provides() upstream.
+func (ipd *ImportsPerDeveloper) Requires() []string {
+	return []string{imports.DependencyImports, identity.DependencyAuthor}
+}
+
+// ListConfigurationOptions returns the list of changeable public properties of this PipelineItem.
+func (ipd *ImportsPerDeveloper) ListConfigurationOptions() []core.ConfigurationOption {
+	return []core.ConfigurationOption{}
+}
+
+// Flag for the command line switch which enables this analysis.
+func (ipd *ImportsPerDeveloper) Flag() string {
+	return "imports-per-dev"
+}
+
+// Description returns the text which explains what the analysis is doing.
+func (ipd *ImportsPerDeveloper) Description() string {
+	return "Whenever a file is changed or added, we extract the imports from it and increment " +
+		"their usage for the commit author."
+}
+
+// Configure sets the properties previously published by ListConfigurationOptions().
+func (ipd *ImportsPerDeveloper) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		ipd.l = l
+	}
+	ipd.reversedPeopleDict = facts[identity.FactIdentityDetectorReversedPeopleDict].([]string)
+	return nil
+}
+
+// Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
+// calls. The repository which is going to be analysed is supplied as an argument.
+func (ipd *ImportsPerDeveloper) Initialize(repository *git.Repository) error {
+	ipd.l = core.NewLogger()
+	ipd.imports = map[int]map[string]map[string]int{}
+	ipd.OneShotMergeProcessor.Initialize()
+	return nil
+}
+
+// Consume runs this PipelineItem on the next commit data.
+// `deps` contain all the results from upstream PipelineItem-s as requested by Requires().
+// Additionally, DependencyCommit is always present there and represents the analysed *object.Commit.
+// This function returns the mapping with analysis results. The keys must be the same as
+// in Provides(). If there was an error, nil is returned.
+func (ipd *ImportsPerDeveloper) Consume(deps map[string]interface{}) (map[string]interface{}, error) {
+	if deps[core.DependencyIsMerge].(bool) {
+		// we ignore merge commits
+		// TODO(vmarkovtsev): handle them better
+		return nil, nil
+	}
+	author := deps[identity.DependencyAuthor].(int)
+	imps := deps[imports.DependencyImports].(map[plumbing.Hash]imports2.File)
+	aimps := ipd.imports[author]
+	if aimps == nil {
+		aimps = map[string]map[string]int{}
+		ipd.imports[author] = aimps
+	}
+	for _, file := range imps {
+		limps := aimps[file.Lang]
+		if limps == nil {
+			limps = map[string]int{}
+			aimps[file.Lang] = limps
+		}
+		for _, imp := range file.Imports {
+			limps[imp]++
+		}
+	}
+	return nil, nil
+}
+
+// Finalize returns the result of the analysis. Further Consume() calls are not expected.
+func (ipd *ImportsPerDeveloper) Finalize() interface{} {
+	return ImportsPerDeveloperResult{Imports: ipd.imports, reversedPeopleDict: ipd.reversedPeopleDict}
+}
+
+// Fork clones this PipelineItem.
+func (ipd *ImportsPerDeveloper) Fork(n int) []core.PipelineItem {
+	return core.ForkSamePipelineItem(ipd, n)
+}
+
+// Serialize converts the analysis result as returned by Finalize() to text or bytes.
+// The text format is YAML and the bytes format is Protocol Buffers.
+func (ipd *ImportsPerDeveloper) Serialize(result interface{}, binary bool, writer io.Writer) error {
+	importsResult := result.(ImportsPerDeveloperResult)
+	if binary {
+		return ipd.serializeBinary(&importsResult, writer)
+	}
+	ipd.serializeText(&importsResult, writer)
+	return nil
+}
+
+func (ipd *ImportsPerDeveloper) serializeText(result *ImportsPerDeveloperResult, writer io.Writer) {
+	for dev, imps := range result.Imports {
+		obj, err := json.Marshal(imps)
+		if err != nil {
+			log.Panicf("Could not serialize %v: %v", imps, err)
+		}
+		fmt.Fprintf(writer, "  - %s: %s\n", yaml.SafeString(result.reversedPeopleDict[dev]), string(obj))
+	}
+}
+
+func (ipd *ImportsPerDeveloper) serializeBinary(result *ImportsPerDeveloperResult, writer io.Writer) error {
+	message := pb.ImportsPerDeveloperResults{
+		Imports:     make([]*pb.ImportsPerDeveloper, len(result.Imports)),
+		AuthorIndex: result.reversedPeopleDict,
+	}
+	for key, vals := range result.Imports {
+		dev := &pb.ImportsPerDeveloper{Languages: map[string]*pb.ImportsPerLanguage{}}
+		message.Imports[key] = dev
+		for lang, counts := range vals {
+			counts64 := map[string]int64{}
+			dev.Languages[lang] = &pb.ImportsPerLanguage{Counts: counts64}
+			for imp, n := range counts {
+				counts64[imp] = int64(n)
+			}
+		}
+	}
+	serialized, err := proto.Marshal(&message)
+	if err != nil {
+		return err
+	}
+	_, err = writer.Write(serialized)
+	return err
+}
+
+func init() {
+	core.Registry.Register(&ImportsPerDeveloper{})
+}

+ 131 - 0
leaves/imports_printer_test.go

@@ -0,0 +1,131 @@
+package leaves
+
+import (
+	"bytes"
+	"fmt"
+	"testing"
+
+	"github.com/gogo/protobuf/proto"
+	imports2 "github.com/src-d/imports"
+	"github.com/stretchr/testify/assert"
+	gitplumbing "gopkg.in/src-d/go-git.v4/plumbing"
+	"gopkg.in/src-d/hercules.v10/internal/core"
+	"gopkg.in/src-d/hercules.v10/internal/pb"
+	"gopkg.in/src-d/hercules.v10/internal/plumbing/identity"
+	"gopkg.in/src-d/hercules.v10/internal/plumbing/imports"
+	"gopkg.in/src-d/hercules.v10/internal/test"
+)
+
+func fixtureImportsPerDev() *ImportsPerDeveloper {
+	d := ImportsPerDeveloper{}
+	d.Initialize(test.Repository)
+	people := [...]string{"one@srcd", "two@srcd"}
+	d.reversedPeopleDict = people[:]
+	return &d
+}
+
+func TestImportsPerDeveloperMeta(t *testing.T) {
+	ipd := fixtureImportsPerDev()
+	assert.Equal(t, ipd.Name(), "ImportsPerDeveloper")
+	assert.Equal(t, len(ipd.Provides()), 0)
+	assert.Equal(t, len(ipd.Requires()), 2)
+	assert.Equal(t, ipd.Requires()[0], imports.DependencyImports)
+	assert.Equal(t, ipd.Requires()[1], identity.DependencyAuthor)
+	assert.Equal(t, ipd.Flag(), "imports-per-dev")
+	assert.Len(t, ipd.ListConfigurationOptions(), 0)
+	assert.True(t, len(ipd.Description()) > 0)
+	logger := core.NewLogger()
+	assert.NoError(t, ipd.Configure(map[string]interface{}{
+		core.ConfigLogger: logger,
+		identity.FactIdentityDetectorReversedPeopleDict: []string{"1", "2"},
+	}))
+	assert.Equal(t, logger, ipd.l)
+	assert.Equal(t, []string{"1", "2"}, ipd.reversedPeopleDict)
+}
+
+func TestImportsPerDeveloperRegistration(t *testing.T) {
+	summoned := core.Registry.Summon((&ImportsPerDeveloper{}).Name())
+	assert.Len(t, summoned, 1)
+	assert.Equal(t, summoned[0].Name(), "ImportsPerDeveloper")
+	leaves := core.Registry.GetLeaves()
+	matched := false
+	for _, tp := range leaves {
+		if tp.Flag() == (&ImportsPerDeveloper{}).Flag() {
+			matched = true
+			break
+		}
+	}
+	assert.True(t, matched)
+}
+
+func TestImportsPerDeveloperInitialize(t *testing.T) {
+	ipd := fixtureImportsPerDev()
+	assert.NotNil(t, ipd.imports)
+}
+
+func TestImportsPerDeveloperConsumeFinalize(t *testing.T) {
+	deps := map[string]interface{}{}
+	deps[core.DependencyIsMerge] = false
+	deps[identity.DependencyAuthor] = 0
+	imps := map[gitplumbing.Hash]imports2.File{}
+	imps[gitplumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe")] =
+		imports2.File{Lang: "Go", Path: "test.go", Imports: []string{"sys"}}
+	imps[gitplumbing.NewHash("c29112dbd697ad9b401333b80c18a63951bc18d9")] =
+		imports2.File{Lang: "Python", Path: "test.py", Imports: []string{"sys"}}
+	deps[imports.DependencyImports] = imps
+	ipd := fixtureImportsPerDev()
+	ipd.reversedPeopleDict = []string{"1", "2"}
+	_, err := ipd.Consume(deps)
+	assert.NoError(t, err)
+	assert.Equal(t, map[int]map[string]map[string]int{
+		0: {"Go": {"sys": 1}, "Python": {"sys": 1}},
+	}, ipd.imports)
+	_, err = ipd.Consume(deps)
+	assert.NoError(t, err)
+	assert.Equal(t, map[int]map[string]map[string]int{
+		0: {"Go": {"sys": 2}, "Python": {"sys": 2}},
+	}, ipd.imports)
+	deps[identity.DependencyAuthor] = 1
+	_, err = ipd.Consume(deps)
+	assert.NoError(t, err)
+	assert.Equal(t, map[int]map[string]map[string]int{
+		0: {"Go": {"sys": 2}, "Python": {"sys": 2}},
+		1: {"Go": {"sys": 1}, "Python": {"sys": 1}},
+	}, ipd.imports)
+	deps[core.DependencyIsMerge] = true
+	_, err = ipd.Consume(deps)
+	assert.NoError(t, err)
+	assert.Equal(t, map[int]map[string]map[string]int{
+		0: {"Go": {"sys": 2}, "Python": {"sys": 2}},
+		1: {"Go": {"sys": 1}, "Python": {"sys": 1}},
+	}, ipd.imports)
+	result := ipd.Finalize().(ImportsPerDeveloperResult)
+	assert.Equal(t, ipd.reversedPeopleDict, result.reversedPeopleDict)
+	assert.Equal(t, ipd.imports, result.Imports)
+}
+
+func TestImportsPerDeveloperSerializeText(t *testing.T) {
+	ipd := fixtureImportsPerDev()
+	res := ImportsPerDeveloperResult{Imports: map[int]map[string]map[string]int{
+		0: {"Go": {"sys": 2}, "Python": {"sys": 2}},
+		1: {"Go": {"sys": 1}, "Python": {"sys": 1}},
+	}, reversedPeopleDict: []string{"one", "two"}}
+	buffer := &bytes.Buffer{}
+	assert.NoError(t, ipd.Serialize(res, false, buffer))
+	assert.Equal(t, `  - "one": {"Go":{"sys":2},"Python":{"sys":2}}
+  - "two": {"Go":{"sys":1},"Python":{"sys":1}}
+`, buffer.String())
+}
+
+func TestImportsPerDeveloperSerializeBinary(t *testing.T) {
+	ipd := fixtureImportsPerDev()
+	res := ImportsPerDeveloperResult{Imports: map[int]map[string]map[string]int{
+		0: {"Go": {"sys": 2}, "Python": {"sys": 2}},
+		1: {"Go": {"sys": 1}, "Python": {"sys": 1}},
+	}, reversedPeopleDict: []string{"one", "two"}}
+	buffer := &bytes.Buffer{}
+	assert.NoError(t, ipd.Serialize(res, true, buffer))
+	msg := pb.ImportsPerDeveloperResults{}
+	assert.Nil(t, proto.Unmarshal(buffer.Bytes(), &msg))
+	assert.Equal(t, `{[languages:<key:"Go" value:<counts:<key:"sys" value:2 > > > languages:<key:"Python" value:<counts:<key:"sys" value:2 > > >  languages:<key:"Go" value:<counts:<key:"sys" value:1 > > > languages:<key:"Python" value:<counts:<key:"sys" value:1 > > > ] [one two] {} [] 0}`, fmt.Sprint(msg))
+}

File diff suppressed because it is too large
+ 227 - 5
python/labours/pb_pb2.py