diff --git a/go.mod b/go.mod index 80f59a10d74..2028cb69385 100644 --- a/go.mod +++ b/go.mod @@ -35,6 +35,7 @@ require ( github.com/pborman/uuid v1.2.0 github.com/pkg/errors v0.9.1 github.com/posener/complete v1.1.1 + github.com/redhat-developer/alizer/go v0.0.0-20220215154256-33df7feef4ae github.com/redhat-developer/service-binding-operator v0.9.0 github.com/securego/gosec/v2 v2.8.0 github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect diff --git a/go.sum b/go.sum index 2d06432e2f6..b715097d1aa 100644 --- a/go.sum +++ b/go.sum @@ -1007,6 +1007,10 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA= github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/redhat-developer/alizer/go v0.0.0-20220204121940-dec463ed3af3 h1:PFDkd6xGpa1SwWMj4bKrHc2xPAwA0x2PKZOlo1LfWYQ= +github.com/redhat-developer/alizer/go v0.0.0-20220204121940-dec463ed3af3/go.mod h1:18H8Trq+vkpxqek82e7SimdNTdFfOGGjN7gAUGDO3jU= +github.com/redhat-developer/alizer/go v0.0.0-20220215154256-33df7feef4ae h1:N2wsIYtziHQ51GNcJY5YcB0YldpR5BwPoTvby+l0vy8= +github.com/redhat-developer/alizer/go v0.0.0-20220215154256-33df7feef4ae/go.mod h1:EKkrP0Am7Xt/yg3dF8uH1SSoOcaZmBom8Iy6CJPPDok= github.com/redhat-developer/service-binding-operator v0.9.0 h1:CS+eEtzu/PtWuyvYQFQpZXd6ukSuFtN+U0EKKtTsvlA= github.com/redhat-developer/service-binding-operator v0.9.0/go.mod h1:D415gZQiz5Q8zyRbmrNrlieb6Xp73oFtCb+nCuTL6GA= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= @@ -1299,6 +1303,8 @@ golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hM golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/pkg/catalog/catalog.go b/pkg/catalog/catalog.go index 637102abd45..8f98aa83271 100644 --- a/pkg/catalog/catalog.go +++ b/pkg/catalog/catalog.go @@ -242,6 +242,7 @@ func createRegistryDevfiles(registry Registry, devfileIndex []indexSchema.Schema Registry: registry, Language: devfileIndexEntry.Language, Tags: devfileIndexEntry.Tags, + ProjectType: devfileIndexEntry.ProjectType, } registryDevfiles = append(registryDevfiles, stackDevfile) } diff --git a/pkg/catalog/types.go b/pkg/catalog/types.go index 34a5b0a6875..e7758ccdea2 100644 --- a/pkg/catalog/types.go +++ b/pkg/catalog/types.go @@ -16,6 +16,7 @@ type DevfileComponentType struct { Registry Registry Language string Tags []string + ProjectType string } // DevfileComponentTypeList lists all the DevfileComponentType's diff --git a/pkg/devfile/location/location.go b/pkg/devfile/location/location.go index 43bb190334d..db53ea525c0 100644 --- a/pkg/devfile/location/location.go +++ b/pkg/devfile/location/location.go @@ -1,8 +1,10 @@ package location import ( + "os" "path/filepath" + "github.com/redhat-developer/odo/pkg/testingutil/filesystem" "github.com/redhat-developer/odo/pkg/util" ) @@ -30,3 +32,47 @@ func DevfileLocation(contexDir string) string { devFile := DevfileFilenamesProvider(contexDir) return filepath.Join(contexDir, devFile) } + +// IsDevfileName returns true if name is a supported name for a devfile +func IsDevfileName(name string) bool { + for _, devFile := range possibleDevfileNames { + if devFile == name { + return true + } + } + return false +} + +// DirectoryContainsDevfile returns true if the given directory contains a devfile with a supported name +func DirectoryContainsDevfile(fsys filesystem.Filesystem, dir string) (bool, error) { + for _, devFile := range possibleDevfileNames { + _, err := fsys.Stat(filepath.Join(dir, devFile)) + if os.IsNotExist(err) { + continue + } else if err != nil { + return false, err + } + // path to file does exist + return true, nil + } + return false, nil +} + +// DirIsEmpty returns true if the given directory contains no file +func DirIsEmpty(fsys filesystem.Filesystem, path string) (bool, error) { + files, err := fsys.ReadDir(path) + if err != nil { + return false, err + } + return len(files) == 0, nil +} + +// DirContainsOnlyDevfile returns true if the directory contains only one file which is a devfile +// with a supported name +func DirContainsOnlyDevfile(fsys filesystem.Filesystem, path string) (bool, error) { + files, err := fsys.ReadDir(path) + if err != nil { + return false, err + } + return len(files) == 1 && IsDevfileName(files[0].Name()), nil +} diff --git a/pkg/init/asker/asker.go b/pkg/init/asker/asker.go index d28eba412ae..ef0d0d16c54 100644 --- a/pkg/init/asker/asker.go +++ b/pkg/init/asker/asker.go @@ -77,3 +77,16 @@ func (o *Survey) AskName(defaultName string) (string, error) { } return answer, nil } + +func (o *Survey) AskCorrect() (bool, error) { + question := &survey.Confirm{ + Message: "Is this correct?", + Default: true, + } + var answer bool + err := survey.AskOne(question, &answer) + if err != nil { + return false, err + } + return answer, nil +} diff --git a/pkg/init/asker/interface.go b/pkg/init/asker/interface.go index 143d7278489..efdc10fb29d 100644 --- a/pkg/init/asker/interface.go +++ b/pkg/init/asker/interface.go @@ -19,4 +19,7 @@ type Asker interface { // AskName asks for a devfile component name AskName(defaultName string) (string, error) + + // AskCorrect asks for confirmation + AskCorrect() (bool, error) } diff --git a/pkg/init/asker/mock.go b/pkg/init/asker/mock.go index f87c6efadbf..212ba819705 100644 --- a/pkg/init/asker/mock.go +++ b/pkg/init/asker/mock.go @@ -34,6 +34,21 @@ func (m *MockAsker) EXPECT() *MockAskerMockRecorder { return m.recorder } +// AskCorrect mocks base method. +func (m *MockAsker) AskCorrect() (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AskCorrect") + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AskCorrect indicates an expected call of AskCorrect. +func (mr *MockAskerMockRecorder) AskCorrect() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AskCorrect", reflect.TypeOf((*MockAsker)(nil).AskCorrect)) +} + // AskLanguage mocks base method. func (m *MockAsker) AskLanguage(langs []string) (string, error) { m.ctrl.T.Helper() diff --git a/pkg/init/backend/alizer.go b/pkg/init/backend/alizer.go new file mode 100644 index 00000000000..0b744091077 --- /dev/null +++ b/pkg/init/backend/alizer.go @@ -0,0 +1,92 @@ +package backend + +import ( + "fmt" + "reflect" + + "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "github.com/devfile/library/pkg/devfile/parser" + "github.com/redhat-developer/alizer/go/pkg/apis/recognizer" + "github.com/redhat-developer/odo/pkg/catalog" + "github.com/redhat-developer/odo/pkg/init/asker" + "github.com/redhat-developer/odo/pkg/testingutil/filesystem" +) + +type AlizerBackend struct { + askerClient asker.Asker + catalogClient catalog.Client +} + +func NewAlizerBackend(askerClient asker.Asker, catalogClient catalog.Client) *AlizerBackend { + return &AlizerBackend{ + askerClient: askerClient, + catalogClient: catalogClient, + } +} + +func (o *AlizerBackend) Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error { + return nil +} + +// detectFramework uses the alizer library in order to detect the devfile +// to use depending on the files in the path +func (o *AlizerBackend) detectFramework(path string) (recognizer.DevFileType, catalog.Registry, error) { + types := []recognizer.DevFileType{} + components, err := o.catalogClient.ListDevfileComponents("") + if err != nil { + return recognizer.DevFileType{}, catalog.Registry{}, err + } + for _, component := range components.Items { + types = append(types, recognizer.DevFileType{ + Name: component.Name, + Language: component.Language, + ProjectType: component.ProjectType, + Tags: component.Tags, + }) + } + typ, err := recognizer.SelectDevFileFromTypes(path, types) + if err != nil { + return recognizer.DevFileType{}, catalog.Registry{}, err + } + + // TODO(feloy): This part won't be necessary when SelectDevFileFromTypes returns the index + var indexOfDetected int + for i, typeFromList := range types { + if reflect.DeepEqual(typeFromList, typ) { + indexOfDetected = i + break + } + } + registry := components.Items[indexOfDetected].Registry + return typ, registry, nil +} + +// SelectDevfile calls thz Alizer to detect the devfile and asks for confirmation to the user +func (o *AlizerBackend) SelectDevfile(flags map[string]string, fs filesystem.Filesystem, dir string) (location *DevfileLocation, err error) { + selected, registry, err := o.detectFramework(dir) + if err != nil { + return nil, err + } + + fmt.Printf("Based on the files in the current directory odo detected\nLanguage: %s\nProject type: %s\n", selected.Language, selected.ProjectType) + fmt.Printf("The devfile %q from the registry %q will be downloaded.\n", selected.Name, registry.Name) + confirm, err := o.askerClient.AskCorrect() + if err != nil { + return nil, err + } + if !confirm { + return nil, nil + } + return &DevfileLocation{ + Devfile: selected.Name, + DevfileRegistry: registry.Name, + }, nil +} + +func (o *AlizerBackend) SelectStarterProject(devfile parser.DevfileObj, flags map[string]string) (starter *v1alpha2.StarterProject, err error) { + return nil, nil +} + +func (o *AlizerBackend) PersonalizeName(devfile parser.DevfileObj, flags map[string]string) error { + return nil +} diff --git a/pkg/init/backend/alizer_test.go b/pkg/init/backend/alizer_test.go new file mode 100644 index 00000000000..f10dfee5410 --- /dev/null +++ b/pkg/init/backend/alizer_test.go @@ -0,0 +1,218 @@ +package backend + +import ( + "path/filepath" + "reflect" + "runtime" + "testing" + + "github.com/golang/mock/gomock" + "github.com/redhat-developer/odo/pkg/catalog" + "github.com/redhat-developer/odo/pkg/init/asker" + "github.com/redhat-developer/odo/pkg/testingutil/filesystem" +) + +// Below functions are from: +// https://github.com/redhat-developer/alizer/blob/main/go/test/apis/language_recognizer_test.go +func GetTestProjectPath(folder string) string { + _, b, _, _ := runtime.Caller(0) + basepath := filepath.Dir(b) + return filepath.Join(basepath, "..", "..", "..", "tests/examples/source/", folder) +} + +var types = []catalog.DevfileComponentType{ + { + Name: "java-maven", + Language: "java", + ProjectType: "maven", + Tags: []string{"Java", "Maven"}, + Registry: catalog.Registry{ + Name: "registry1", + }, + }, + { + Name: "java-quarkus", + Language: "java", + ProjectType: "quarkus", + Tags: []string{"Java", "Quarkus"}, + Registry: catalog.Registry{ + Name: "registry1", + }, + }, + { + Name: "java-wildfly", + Language: "java", + ProjectType: "wildfly", + Tags: []string{"Java", "WildFly"}, + Registry: catalog.Registry{ + Name: "registry2", + }, + }, + { + Name: "nodejs", + Language: "javascript", + ProjectType: "nodejs", + Tags: []string{"NodeJS", "Express", "ubi8"}, + Registry: catalog.Registry{ + Name: "registry2", + }, + }, + { + Name: "python", + Language: "python", + ProjectType: "python", + Tags: []string{"Python", "pip"}, + Registry: catalog.Registry{ + Name: "registry3", + }, + }, +} +var list = catalog.DevfileComponentTypeList{ + Items: types, +} + +func TestDetectFramework(t *testing.T) { + + type args struct { + path string + } + tests := []struct { + name string + args args + wantedDevfile string + wantedRegistry string + wantErr bool + }{ + { + name: "Detect Node.JS example", + args: args{ + path: GetTestProjectPath("nodejs"), + }, + wantedDevfile: "nodejs", + wantedRegistry: "registry2", + wantErr: false, + }, + { + name: "Detect java openjdk example", + args: args{ + path: GetTestProjectPath("openjdk"), + }, + wantedDevfile: "java-maven", + wantedRegistry: "registry1", + wantErr: false, + }, + { + name: "Detect python example", + args: args{ + path: GetTestProjectPath("python"), + }, + wantedDevfile: "python", + wantedRegistry: "registry3", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + askerClient := asker.NewMockAsker(ctrl) + catalogClient := catalog.NewMockClient(ctrl) + catalogClient.EXPECT().ListDevfileComponents("").Return(list, nil) + alizerClient := NewAlizerBackend(askerClient, catalogClient) + // Run function DetectFramework + detected, registry, err := alizerClient.detectFramework(tt.args.path) + + if !tt.wantErr == (err != nil) { + t.Errorf("unexpected error %v, wantErr %v", err, tt.wantErr) + return + } + + if detected.Name != tt.wantedDevfile { + t.Errorf("unexpected devfile %v, wantedDevfile %v", detected, tt.wantedDevfile) + } + if registry.Name != tt.wantedRegistry { + t.Errorf("unexpected registry %v, wantedRegistry %v", registry, tt.wantedRegistry) + } + }) + } +} + +func TestAlizerBackend_SelectDevfile(t *testing.T) { + type fields struct { + askerClient func(ctrl *gomock.Controller) asker.Asker + catalogClient func(ctrl *gomock.Controller) catalog.Client + } + type args struct { + flags map[string]string + fs filesystem.Filesystem + dir string + } + tests := []struct { + name string + fields fields + args args + wantLocation *DevfileLocation + wantErr bool + }{ + { + name: "devfile found and accepted", + fields: fields{ + askerClient: func(ctrl *gomock.Controller) asker.Asker { + askerClient := asker.NewMockAsker(ctrl) + askerClient.EXPECT().AskCorrect().Return(true, nil) + return askerClient + }, + catalogClient: func(ctrl *gomock.Controller) catalog.Client { + catalogClient := catalog.NewMockClient(ctrl) + catalogClient.EXPECT().ListDevfileComponents("").Return(list, nil) + return catalogClient + }, + }, + args: args{ + fs: filesystem.DefaultFs{}, + dir: GetTestProjectPath("nodejs"), + }, + wantLocation: &DevfileLocation{ + Devfile: "nodejs", + DevfileRegistry: "registry2", + }, + }, + { + name: "devfile found but not accepted", + fields: fields{ + askerClient: func(ctrl *gomock.Controller) asker.Asker { + askerClient := asker.NewMockAsker(ctrl) + askerClient.EXPECT().AskCorrect().Return(false, nil) + return askerClient + }, + catalogClient: func(ctrl *gomock.Controller) catalog.Client { + catalogClient := catalog.NewMockClient(ctrl) + catalogClient.EXPECT().ListDevfileComponents("").Return(list, nil) + return catalogClient + }, + }, + args: args{ + fs: filesystem.DefaultFs{}, + dir: GetTestProjectPath("nodejs"), + }, + wantLocation: nil, + }, + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + o := &AlizerBackend{ + askerClient: tt.fields.askerClient(ctrl), + catalogClient: tt.fields.catalogClient(ctrl), + } + gotLocation, err := o.SelectDevfile(tt.args.flags, tt.args.fs, tt.args.dir) + if (err != nil) != tt.wantErr { + t.Errorf("AlizerBackend.SelectDevfile() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotLocation, tt.wantLocation) { + t.Errorf("AlizerBackend.SelectDevfile() = %v, want %v", gotLocation, tt.wantLocation) + } + }) + } +} diff --git a/pkg/init/backend/flags.go b/pkg/init/backend/flags.go index 552541ae9c2..6829e556bed 100644 --- a/pkg/init/backend/flags.go +++ b/pkg/init/backend/flags.go @@ -9,7 +9,9 @@ import ( "github.com/devfile/library/pkg/devfile/parser/data/v2/common" dfutil "github.com/devfile/library/pkg/util" + "github.com/redhat-developer/odo/pkg/devfile/location" "github.com/redhat-developer/odo/pkg/preference" + "github.com/redhat-developer/odo/pkg/testingutil/filesystem" ) const ( @@ -31,7 +33,7 @@ func NewFlagsBackend(preferenceClient preference.Client) *FlagsBackend { } } -func (o *FlagsBackend) Validate(flags map[string]string) error { +func (o *FlagsBackend) Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error { if flags[FLAG_NAME] == "" { return errors.New("missing --name parameter: please add --name to specify a name for the component") } @@ -54,10 +56,19 @@ func (o *FlagsBackend) Validate(flags map[string]string) error { if err != nil { return err } + + empty, err := location.DirIsEmpty(fs, dir) + if err != nil { + return err + } + if !empty && flags[FLAG_STARTER] != "" { + return errors.New("--starter parameter cannot be used when the directory is not empty") + } + return nil } -func (o *FlagsBackend) SelectDevfile(flags map[string]string) (*DevfileLocation, error) { +func (o *FlagsBackend) SelectDevfile(flags map[string]string, _ filesystem.Filesystem, _ string) (*DevfileLocation, error) { return &DevfileLocation{ Devfile: flags[FLAG_DEVFILE], DevfileRegistry: flags[FLAG_DEVFILE_REGISTRY], diff --git a/pkg/init/backend/flags_test.go b/pkg/init/backend/flags_test.go index a55670d67ac..8e1f647a6bf 100644 --- a/pkg/init/backend/flags_test.go +++ b/pkg/init/backend/flags_test.go @@ -10,9 +10,10 @@ import ( "github.com/devfile/library/pkg/devfile/parser" parsercontext "github.com/devfile/library/pkg/devfile/parser/context" "github.com/devfile/library/pkg/devfile/parser/data" - "github.com/devfile/library/pkg/testingutil/filesystem" + dffilesystem "github.com/devfile/library/pkg/testingutil/filesystem" "github.com/redhat-developer/odo/pkg/preference" + "github.com/redhat-developer/odo/pkg/testingutil/filesystem" ) func TestFlagsBackend_SelectDevfile(t *testing.T) { @@ -45,7 +46,7 @@ func TestFlagsBackend_SelectDevfile(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { o := &FlagsBackend{} - got, err := o.SelectDevfile(tt.fields.flags) + got, err := o.SelectDevfile(tt.fields.flags, nil, "") if (err != nil) != tt.wantErr { t.Errorf("FlagsBackend.SelectDevfile() error = %v, wantErr %v", err, tt.wantErr) return @@ -62,6 +63,8 @@ func TestFlagsBackend_Validate(t *testing.T) { } type args struct { flags map[string]string + fsys func() filesystem.Filesystem + dir string } tests := []struct { name string @@ -77,6 +80,12 @@ func TestFlagsBackend_Validate(t *testing.T) { flags: map[string]string{ "name": "", }, + fsys: func() filesystem.Filesystem { + fs := filesystem.NewFakeFs() + _ = fs.MkdirAll("/tmp", 0644) + return fs + }, + dir: "/tmp", }, wantErr: true, }, @@ -86,6 +95,12 @@ func TestFlagsBackend_Validate(t *testing.T) { flags: map[string]string{ "name": "aname", }, + fsys: func() filesystem.Filesystem { + fs := filesystem.NewFakeFs() + _ = fs.MkdirAll("/tmp", 0644) + return fs + }, + dir: "/tmp", }, wantErr: true, }, @@ -96,6 +111,12 @@ func TestFlagsBackend_Validate(t *testing.T) { "name": "aname", "devfile": "adevfile", }, + fsys: func() filesystem.Filesystem { + fs := filesystem.NewFakeFs() + _ = fs.MkdirAll("/tmp", 0644) + return fs + }, + dir: "/tmp", }, registryList: []preference.Registry{ { @@ -113,6 +134,12 @@ func TestFlagsBackend_Validate(t *testing.T) { "devfile": "adevfile", "devfile-path": "apath", }, + fsys: func() filesystem.Filesystem { + fs := filesystem.NewFakeFs() + _ = fs.MkdirAll("/tmp", 0644) + return fs + }, + dir: "/tmp", }, wantErr: true, }, @@ -124,6 +151,12 @@ func TestFlagsBackend_Validate(t *testing.T) { "devfile": "adevfile", "devfile-registry": "aregistry", }, + fsys: func() filesystem.Filesystem { + fs := filesystem.NewFakeFs() + _ = fs.MkdirAll("/tmp", 0644) + return fs + }, + dir: "/tmp", }, registryNameExists: true, wantErr: false, @@ -136,6 +169,12 @@ func TestFlagsBackend_Validate(t *testing.T) { "devfile": "adevfile", "devfile-registry": "aregistry", }, + fsys: func() filesystem.Filesystem { + fs := filesystem.NewFakeFs() + _ = fs.MkdirAll("/tmp", 0644) + return fs + }, + dir: "/tmp", }, registryNameExists: false, wantErr: true, @@ -148,6 +187,12 @@ func TestFlagsBackend_Validate(t *testing.T) { "devfile-path": "apath", "devfile-registry": "aregistry", }, + fsys: func() filesystem.Filesystem { + fs := filesystem.NewFakeFs() + _ = fs.MkdirAll("/tmp", 0644) + return fs + }, + dir: "/tmp", }, registryNameExists: true, wantErr: true, @@ -159,6 +204,12 @@ func TestFlagsBackend_Validate(t *testing.T) { "name": "1234", "devfile": "adevfile", }, + fsys: func() filesystem.Filesystem { + fs := filesystem.NewFakeFs() + _ = fs.MkdirAll("/tmp", 0644) + return fs + }, + dir: "/tmp", }, wantErr: true, }, @@ -169,6 +220,47 @@ func TestFlagsBackend_Validate(t *testing.T) { "name": "WrongName", "devfile": "adevfile", }, + fsys: func() filesystem.Filesystem { + fs := filesystem.NewFakeFs() + _ = fs.MkdirAll("/tmp", 0644) + return fs + }, + dir: "/tmp", + }, + wantErr: true, + }, + { + name: "starter flag with an empty directory", + args: args{ + flags: map[string]string{ + "name": "aname", + "devfile": "adevfile", + "starter": "astarter", + }, + fsys: func() filesystem.Filesystem { + fs := filesystem.NewFakeFs() + _ = fs.MkdirAll("/tmp", 0644) + return fs + }, + dir: "/tmp", + }, + wantErr: false, + }, + { + name: "starter flag with a non empty directory", + args: args{ + flags: map[string]string{ + "name": "aname", + "devfile": "adevfile", + "starter": "astarter", + }, + fsys: func() filesystem.Filesystem { + fs := filesystem.NewFakeFs() + _ = fs.MkdirAll("/tmp", 0644) + _ = fs.WriteFile("/tmp/main.go", []byte("package main"), 0644) + return fs + }, + dir: "/tmp", }, wantErr: true, }, @@ -184,7 +276,7 @@ func TestFlagsBackend_Validate(t *testing.T) { o := &FlagsBackend{ preferenceClient: prefClient, } - if err := o.Validate(tt.args.flags); (err != nil) != tt.wantErr { + if err := o.Validate(tt.args.flags, tt.args.fsys(), tt.args.dir); (err != nil) != tt.wantErr { t.Errorf("FlagsBackend.Validate() error = %v, wantErr %v", err, tt.wantErr) } }) @@ -258,9 +350,6 @@ func TestFlagsBackend_SelectStarterProject(t *testing.T) { { Name: "starter1", }, - { - Name: "starter2", - }, { Name: "starter3", }, @@ -301,7 +390,7 @@ func TestFlagsBackend_PersonalizeName(t *testing.T) { preferenceClient preference.Client } type args struct { - devfile func(fs filesystem.Filesystem) parser.DevfileObj + devfile func(fs dffilesystem.Filesystem) parser.DevfileObj flags map[string]string } tests := []struct { @@ -314,7 +403,7 @@ func TestFlagsBackend_PersonalizeName(t *testing.T) { { name: "name flag", args: args{ - devfile: func(fs filesystem.Filesystem) parser.DevfileObj { + devfile: func(fs dffilesystem.Filesystem) parser.DevfileObj { devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion200)) obj := parser.DevfileObj{ Ctx: parsercontext.FakeContext(fs, "/tmp/devfile.yaml"), @@ -338,7 +427,7 @@ func TestFlagsBackend_PersonalizeName(t *testing.T) { o := &FlagsBackend{ preferenceClient: tt.fields.preferenceClient, } - fs := filesystem.NewFakeFs() + fs := dffilesystem.NewFakeFs() devfile := tt.args.devfile(fs) err := o.PersonalizeName(devfile, tt.args.flags) if (err != nil) != tt.wantErr { diff --git a/pkg/init/backend/interactive.go b/pkg/init/backend/interactive.go index ea21ae67e75..d95390c472b 100644 --- a/pkg/init/backend/interactive.go +++ b/pkg/init/backend/interactive.go @@ -9,6 +9,7 @@ import ( "github.com/redhat-developer/odo/pkg/catalog" "github.com/redhat-developer/odo/pkg/init/asker" + "github.com/redhat-developer/odo/pkg/testingutil/filesystem" ) const ( @@ -19,22 +20,22 @@ const ( // InteractiveBackend is a backend that will ask information interactively using the `asker` package type InteractiveBackend struct { - asker asker.Asker + askerClient asker.Asker catalogClient catalog.Client } -func NewInteractiveBackend(asker asker.Asker, catalogClient catalog.Client) *InteractiveBackend { +func NewInteractiveBackend(askerClient asker.Asker, catalogClient catalog.Client) *InteractiveBackend { return &InteractiveBackend{ - asker: asker, + askerClient: askerClient, catalogClient: catalogClient, } } -func (o *InteractiveBackend) Validate(flags map[string]string) error { +func (o *InteractiveBackend) Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error { return nil } -func (o *InteractiveBackend) SelectDevfile(flags map[string]string) (*DevfileLocation, error) { +func (o *InteractiveBackend) SelectDevfile(flags map[string]string, _ filesystem.Filesystem, _ string) (*DevfileLocation, error) { result := &DevfileLocation{} devfileEntries, _ := o.catalogClient.ListDevfileComponents("") @@ -48,7 +49,7 @@ loop: switch state { case STATE_ASK_LANG: - lang, err = o.asker.AskLanguage(langs) + lang, err = o.askerClient.AskLanguage(langs) if err != nil { return nil, err } @@ -57,7 +58,7 @@ loop: case STATE_ASK_TYPE: types := devfileEntries.GetProjectTypes(lang) var back bool - back, details, err = o.asker.AskType(types) + back, details, err = o.askerClient.AskType(types) if err != nil { return nil, err } @@ -86,7 +87,7 @@ func (o *InteractiveBackend) SelectStarterProject(devfile parser.DevfileObj, fla names = append(names, starterProject.Name) } - ok, starter, err := o.asker.AskStarterProject(names) + ok, starter, err := o.askerClient.AskStarterProject(names) if err != nil { return nil, err } @@ -97,7 +98,7 @@ func (o *InteractiveBackend) SelectStarterProject(devfile parser.DevfileObj, fla } func (o *InteractiveBackend) PersonalizeName(devfile parser.DevfileObj, flags map[string]string) error { - name, err := o.asker.AskName(fmt.Sprintf("my-%s-app", devfile.Data.GetMetadata().Name)) + name, err := o.askerClient.AskName(fmt.Sprintf("my-%s-app", devfile.Data.GetMetadata().Name)) if err != nil { return err } diff --git a/pkg/init/backend/interactive_test.go b/pkg/init/backend/interactive_test.go index d88a3f0e629..78f9d26ca41 100644 --- a/pkg/init/backend/interactive_test.go +++ b/pkg/init/backend/interactive_test.go @@ -84,10 +84,10 @@ func TestInteractiveBackend_SelectDevfile(t *testing.T) { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) o := &InteractiveBackend{ - asker: tt.fields.buildAsker(ctrl), + askerClient: tt.fields.buildAsker(ctrl), catalogClient: tt.fields.buildCatalogClient(ctrl), } - got, err := o.SelectDevfile(map[string]string{}) + got, err := o.SelectDevfile(map[string]string{}, nil, "") if (err != nil) != tt.wantErr { t.Errorf("InteractiveBuilder.ParamsBuild() error = %v, wantErr %v", err, tt.wantErr) return @@ -180,7 +180,7 @@ func TestInteractiveBackend_SelectStarterProject(t *testing.T) { askerClient = tt.fields.asker(ctrl) } o := &InteractiveBackend{ - asker: askerClient, + askerClient: askerClient, catalogClient: tt.fields.catalogClient, } got1, err := o.SelectStarterProject(tt.args.devfile(), tt.args.flags) @@ -245,7 +245,7 @@ func TestInteractiveBackend_PersonalizeName(t *testing.T) { askerClient = tt.fields.asker(ctrl) } o := &InteractiveBackend{ - asker: askerClient, + askerClient: askerClient, catalogClient: tt.fields.catalogClient, } fs := filesystem.NewFakeFs() diff --git a/pkg/init/backend/interface.go b/pkg/init/backend/interface.go index 11260d7f4fa..1e9b1c6165c 100644 --- a/pkg/init/backend/interface.go +++ b/pkg/init/backend/interface.go @@ -6,15 +6,16 @@ package backend import ( "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" "github.com/devfile/library/pkg/devfile/parser" + "github.com/redhat-developer/odo/pkg/testingutil/filesystem" ) // InitBackend is a specialized backend for steps of initiating a project, based on various input (either from CLI flags or interactively from user) type InitBackend interface { - // Validate returns an error if it does not validate the flags - Validate(flags map[string]string) error + // Validate returns an error if it does not validate the flags based on the directory content + Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error // SelectDevfile selects a devfile and returns its location information, depending on the flags - SelectDevfile(flags map[string]string) (location *DevfileLocation, err error) + SelectDevfile(flags map[string]string, fs filesystem.Filesystem, dir string) (location *DevfileLocation, err error) // SelectStarterProject selects a starter project from the devfile and returns information about the starter project, // depending on the flags. If not starter project is selected, a nil starter is returned diff --git a/pkg/init/backend/mock.go b/pkg/init/backend/mock.go index 5bdb58ab424..a41901535f3 100644 --- a/pkg/init/backend/mock.go +++ b/pkg/init/backend/mock.go @@ -10,6 +10,7 @@ import ( v1alpha2 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" parser "github.com/devfile/library/pkg/devfile/parser" gomock "github.com/golang/mock/gomock" + filesystem "github.com/redhat-developer/odo/pkg/testingutil/filesystem" ) // MockInitBackend is a mock of InitBackend interface. @@ -50,18 +51,18 @@ func (mr *MockInitBackendMockRecorder) PersonalizeName(devfile, flags interface{ } // SelectDevfile mocks base method. -func (m *MockInitBackend) SelectDevfile(flags map[string]string) (*DevfileLocation, error) { +func (m *MockInitBackend) SelectDevfile(flags map[string]string, fs filesystem.Filesystem, dir string) (*DevfileLocation, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SelectDevfile", flags) + ret := m.ctrl.Call(m, "SelectDevfile", flags, fs, dir) ret0, _ := ret[0].(*DevfileLocation) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectDevfile indicates an expected call of SelectDevfile. -func (mr *MockInitBackendMockRecorder) SelectDevfile(flags interface{}) *gomock.Call { +func (mr *MockInitBackendMockRecorder) SelectDevfile(flags, fs, dir interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDevfile", reflect.TypeOf((*MockInitBackend)(nil).SelectDevfile), flags) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDevfile", reflect.TypeOf((*MockInitBackend)(nil).SelectDevfile), flags, fs, dir) } // SelectStarterProject mocks base method. @@ -80,15 +81,15 @@ func (mr *MockInitBackendMockRecorder) SelectStarterProject(devfile, flags inter } // Validate mocks base method. -func (m *MockInitBackend) Validate(flags map[string]string) error { +func (m *MockInitBackend) Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Validate", flags) + ret := m.ctrl.Call(m, "Validate", flags, fs, dir) ret0, _ := ret[0].(error) return ret0 } // Validate indicates an expected call of Validate. -func (mr *MockInitBackendMockRecorder) Validate(flags interface{}) *gomock.Call { +func (mr *MockInitBackendMockRecorder) Validate(flags, fs, dir interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockInitBackend)(nil).Validate), flags) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockInitBackend)(nil).Validate), flags, fs, dir) } diff --git a/pkg/init/init.go b/pkg/init/init.go index 5a0d4bc124f..61ab53c142a 100644 --- a/pkg/init/init.go +++ b/pkg/init/init.go @@ -1,6 +1,7 @@ package init import ( + "errors" "fmt" "net/url" "path/filepath" @@ -11,6 +12,7 @@ import ( dfutil "github.com/devfile/library/pkg/util" "github.com/redhat-developer/odo/pkg/catalog" + "github.com/redhat-developer/odo/pkg/devfile/location" "github.com/redhat-developer/odo/pkg/init/asker" "github.com/redhat-developer/odo/pkg/init/backend" "github.com/redhat-developer/odo/pkg/init/registry" @@ -24,43 +26,71 @@ type InitClient struct { // Backends flagsBackend *backend.FlagsBackend interactiveBackend *backend.InteractiveBackend + alizerBackend *backend.AlizerBackend // Clients fsys filesystem.Filesystem preferenceClient preference.Client registryClient registry.Client + catalogClient catalog.Client } -func NewInitClient(fsys filesystem.Filesystem, preferenceClient preference.Client, registryClient registry.Client) *InitClient { +func NewInitClient(fsys filesystem.Filesystem, preferenceClient preference.Client, registryClient registry.Client, catalogClient catalog.Client) *InitClient { + // We create the asker client and the backends here and not at the CLI level, as we want to hide these details to the CLI + askerClient := asker.NewSurveyAsker() return &InitClient{ flagsBackend: backend.NewFlagsBackend(preferenceClient), - interactiveBackend: backend.NewInteractiveBackend(asker.NewSurveyAsker(), catalog.NewCatalogClient(fsys, preferenceClient)), + interactiveBackend: backend.NewInteractiveBackend(askerClient, catalogClient), + alizerBackend: backend.NewAlizerBackend(askerClient, catalogClient), fsys: fsys, preferenceClient: preferenceClient, registryClient: registryClient, + catalogClient: catalogClient, } } // Validate calls Validate method of the adequate backend -func (o *InitClient) Validate(flags map[string]string) error { +func (o *InitClient) Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error { var backend backend.InitBackend if len(flags) == 0 { backend = o.interactiveBackend } else { backend = o.flagsBackend } - return backend.Validate(flags) + return backend.Validate(flags, fs, dir) } // SelectDevfile calls SelectDevfile methods of the adequate backend -func (o *InitClient) SelectDevfile(flags map[string]string) (*backend.DevfileLocation, error) { +func (o *InitClient) SelectDevfile(flags map[string]string, fs filesystem.Filesystem, dir string) (*backend.DevfileLocation, error) { var backend backend.InitBackend - if len(flags) == 0 { + + empty, err := location.DirIsEmpty(fs, dir) + if err != nil { + return nil, err + } + if empty && len(flags) == 0 { backend = o.interactiveBackend + } else if len(flags) == 0 { + backend = o.alizerBackend } else { backend = o.flagsBackend } - return backend.SelectDevfile(flags) + location, err := backend.SelectDevfile(flags, fs, dir) + if err != nil { + return nil, err + } + + // If Alizer failed to determine the devfile, run interactively + if location == nil { + if backend == o.alizerBackend { + backend = o.interactiveBackend + return backend.SelectDevfile(flags, fs, dir) + } else { + return nil, errors.New("unable to determine the devfile location") + } + } + + return location, err } func (o *InitClient) DownloadDevfile(devfileLocation *backend.DevfileLocation, destDir string) (string, error) { @@ -152,10 +182,17 @@ func (o *InitClient) downloadFromRegistry(registryName string, devfile string, d } // SelectStarterProject calls SelectStarterProject methods of the adequate backend -func (o *InitClient) SelectStarterProject(devfile parser.DevfileObj, flags map[string]string) (*v1alpha2.StarterProject, error) { +func (o *InitClient) SelectStarterProject(devfile parser.DevfileObj, flags map[string]string, fs filesystem.Filesystem, dir string) (*v1alpha2.StarterProject, error) { var backend backend.InitBackend - if len(flags) == 0 { + + onlyDevfile, err := location.DirContainsOnlyDevfile(fs, dir) + if err != nil { + return nil, err + } + if onlyDevfile && len(flags) == 0 { backend = o.interactiveBackend + } else if len(flags) == 0 { + backend = o.alizerBackend } else { backend = o.flagsBackend } diff --git a/pkg/init/interface.go b/pkg/init/interface.go index 1ea190de164..07086bb4bad 100644 --- a/pkg/init/interface.go +++ b/pkg/init/interface.go @@ -12,15 +12,17 @@ import ( "github.com/devfile/library/pkg/devfile/parser" "github.com/redhat-developer/odo/pkg/init/backend" + "github.com/redhat-developer/odo/pkg/testingutil/filesystem" ) type Client interface { // Validate checks for each backend if flags are valid - Validate(flags map[string]string) error + Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error - // SelectDevfile returns information about a devfile selected based on the flags, or + // SelectDevfile returns information about a devfile selected based on Alizer if the directory content, + // or based on the flags if the directory is empty, or // interactively if flags is empty - SelectDevfile(flags map[string]string) (*backend.DevfileLocation, error) + SelectDevfile(flags map[string]string, fs filesystem.Filesystem, dir string) (*backend.DevfileLocation, error) // DownloadDevfile downloads a devfile given its location information and a destination directory // and returns the path of the downloaded file @@ -28,7 +30,7 @@ type Client interface { // SelectStarterProject selects a starter project from the devfile and returns information about the starter project, // depending on the flags. If not starter project is selected, a nil starter is returned - SelectStarterProject(devfile parser.DevfileObj, flags map[string]string) (*v1alpha2.StarterProject, error) + SelectStarterProject(devfile parser.DevfileObj, flags map[string]string, fs filesystem.Filesystem, dir string) (*v1alpha2.StarterProject, error) // DownloadStarterProject downloads the starter project referenced in devfile and stores it in dest directory // WARNING: This will first remove all the content of dest. diff --git a/pkg/init/mock.go b/pkg/init/mock.go index 0dba6051fa0..99cedb8d277 100644 --- a/pkg/init/mock.go +++ b/pkg/init/mock.go @@ -11,6 +11,7 @@ import ( parser "github.com/devfile/library/pkg/devfile/parser" gomock "github.com/golang/mock/gomock" backend "github.com/redhat-developer/odo/pkg/init/backend" + filesystem "github.com/redhat-developer/odo/pkg/testingutil/filesystem" ) // MockClient is a mock of Client interface. @@ -80,45 +81,45 @@ func (mr *MockClientMockRecorder) PersonalizeName(devfile, flags interface{}) *g } // SelectDevfile mocks base method. -func (m *MockClient) SelectDevfile(flags map[string]string) (*backend.DevfileLocation, error) { +func (m *MockClient) SelectDevfile(flags map[string]string, fs filesystem.Filesystem, dir string) (*backend.DevfileLocation, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SelectDevfile", flags) + ret := m.ctrl.Call(m, "SelectDevfile", flags, fs, dir) ret0, _ := ret[0].(*backend.DevfileLocation) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectDevfile indicates an expected call of SelectDevfile. -func (mr *MockClientMockRecorder) SelectDevfile(flags interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) SelectDevfile(flags, fs, dir interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDevfile", reflect.TypeOf((*MockClient)(nil).SelectDevfile), flags) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDevfile", reflect.TypeOf((*MockClient)(nil).SelectDevfile), flags, fs, dir) } // SelectStarterProject mocks base method. -func (m *MockClient) SelectStarterProject(devfile parser.DevfileObj, flags map[string]string) (*v1alpha2.StarterProject, error) { +func (m *MockClient) SelectStarterProject(devfile parser.DevfileObj, flags map[string]string, fs filesystem.Filesystem, dir string) (*v1alpha2.StarterProject, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SelectStarterProject", devfile, flags) + ret := m.ctrl.Call(m, "SelectStarterProject", devfile, flags, fs, dir) ret0, _ := ret[0].(*v1alpha2.StarterProject) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectStarterProject indicates an expected call of SelectStarterProject. -func (mr *MockClientMockRecorder) SelectStarterProject(devfile, flags interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) SelectStarterProject(devfile, flags, fs, dir interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectStarterProject", reflect.TypeOf((*MockClient)(nil).SelectStarterProject), devfile, flags) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectStarterProject", reflect.TypeOf((*MockClient)(nil).SelectStarterProject), devfile, flags, fs, dir) } // Validate mocks base method. -func (m *MockClient) Validate(flags map[string]string) error { +func (m *MockClient) Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Validate", flags) + ret := m.ctrl.Call(m, "Validate", flags, fs, dir) ret0, _ := ret[0].(error) return ret0 } // Validate indicates an expected call of Validate. -func (mr *MockClientMockRecorder) Validate(flags interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) Validate(flags, fs, dir interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockClient)(nil).Validate), flags) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockClient)(nil).Validate), flags, fs, dir) } diff --git a/pkg/init/registry/mock.go b/pkg/init/registry/mock.go index 2e10f9e34b1..5c281afba1e 100644 --- a/pkg/init/registry/mock.go +++ b/pkg/init/registry/mock.go @@ -7,11 +7,10 @@ package registry import ( reflect "reflect" - gomock "github.com/golang/mock/gomock" - v1alpha2 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" - dfutil "github.com/devfile/library/pkg/util" + util "github.com/devfile/library/pkg/util" library "github.com/devfile/registry-support/registry-library/library" + gomock "github.com/golang/mock/gomock" ) // MockClient is a mock of Client interface. @@ -38,7 +37,7 @@ func (m *MockClient) EXPECT() *MockClientMockRecorder { } // DownloadFileInMemory mocks base method. -func (m *MockClient) DownloadFileInMemory(params dfutil.HTTPRequestParams) ([]byte, error) { +func (m *MockClient) DownloadFileInMemory(params util.HTTPRequestParams) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DownloadFileInMemory", params) ret0, _ := ret[0].([]byte) diff --git a/pkg/odo/cli/deploy/deploy.go b/pkg/odo/cli/deploy/deploy.go index e9b8c699226..879a1688f25 100644 --- a/pkg/odo/cli/deploy/deploy.go +++ b/pkg/odo/cli/deploy/deploy.go @@ -2,19 +2,25 @@ package deploy import ( "fmt" + "os" "path/filepath" + "github.com/devfile/library/pkg/devfile" + "github.com/devfile/library/pkg/devfile/parser" "github.com/pkg/errors" "github.com/redhat-developer/odo/pkg/devfile/adapters" "github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes" + "github.com/redhat-developer/odo/pkg/devfile/location" "github.com/redhat-developer/odo/pkg/envinfo" "github.com/redhat-developer/odo/pkg/odo/cli/component" "github.com/redhat-developer/odo/pkg/odo/cmdline" "github.com/redhat-developer/odo/pkg/odo/genericclioptions" "github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset" odoutil "github.com/redhat-developer/odo/pkg/odo/util" + "github.com/redhat-developer/odo/pkg/testingutil/filesystem" "github.com/spf13/cobra" "k8s.io/kubectl/pkg/util/templates" + "k8s.io/utils/pointer" ) // RecommendedCommandName is the recommended command name @@ -25,6 +31,9 @@ type DeployOptions struct { // Context *genericclioptions.Context + // Clients + clientset *clientset.Clientset + // Flags contextFlag string } @@ -40,10 +49,43 @@ func NewDeployOptions() *DeployOptions { } func (o *DeployOptions) SetClientset(clientset *clientset.Clientset) { + o.clientset = clientset } // Complete DeployOptions after they've been created func (o *DeployOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) { + + cwd, err := os.Getwd() + if err != nil { + return err + } + containsDevfile, err := location.DirectoryContainsDevfile(filesystem.DefaultFs{}, cwd) + if err != nil { + return err + } + if !containsDevfile { + devfileLocation, err2 := o.clientset.InitClient.SelectDevfile(map[string]string{}, o.clientset.FS, cwd) + if err2 != nil { + return err2 + } + + devfilePath, err2 := o.clientset.InitClient.DownloadDevfile(devfileLocation, cwd) + if err2 != nil { + return fmt.Errorf("unable to download devfile: %w", err2) + } + + devfileObj, _, err2 := devfile.ParseDevfileAndValidate(parser.ParserArgs{Path: devfilePath, FlattenedDevfile: pointer.BoolPtr(false)}) + if err2 != nil { + return fmt.Errorf("unable to download devfile: %w", err2) + } + + // Set the name in the devfile and writes the devfile back to the disk + err = o.clientset.InitClient.PersonalizeName(devfileObj, map[string]string{}) + if err != nil { + return fmt.Errorf("failed to update the devfile's name: %w", err) + } + + } o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.contextFlag)) if err != nil { return err @@ -105,10 +147,10 @@ func NewCmdDeploy(name, fullName string) *cobra.Command { genericclioptions.GenericRun(o, cmd, args) }, } + clientset.Add(deployCmd, clientset.INIT) // Add a defined annotation in order to appear in the help menu - deployCmd.Annotations = map[string]string{"command": "utility"} + deployCmd.Annotations["command"] = "utility" deployCmd.SetUsageTemplate(odoutil.CmdUsageTemplate) - odoutil.AddContextFlag(deployCmd, &o.contextFlag) return deployCmd } diff --git a/pkg/odo/cli/init/init.go b/pkg/odo/cli/init/init.go index e4923c9da81..af9ea070969 100644 --- a/pkg/odo/cli/init/init.go +++ b/pkg/odo/cli/init/init.go @@ -2,15 +2,16 @@ package init import ( "context" + "errors" "fmt" - "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/devfile/library/pkg/devfile" "github.com/devfile/library/pkg/devfile/parser" "github.com/redhat-developer/odo/pkg/component" + "github.com/redhat-developer/odo/pkg/devfile/location" "github.com/redhat-developer/odo/pkg/init/backend" "github.com/redhat-developer/odo/pkg/log" "github.com/redhat-developer/odo/pkg/odo/cmdline" @@ -18,7 +19,6 @@ import ( "github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset" odoutil "github.com/redhat-developer/odo/pkg/odo/util" scontext "github.com/redhat-developer/odo/pkg/segment/context" - "github.com/redhat-developer/odo/pkg/testingutil/filesystem" "k8s.io/kubectl/pkg/util/templates" "k8s.io/utils/pointer" @@ -85,30 +85,23 @@ func (o *InitOptions) Complete(cmdline cmdline.Cmdline, args []string) (err erro return err } - empty, err := isEmpty(o.clientset.FS, o.contextDir) - if err != nil { - return err - } - if !empty { - return errors.New("The current directory is not empty. You can bootstrap new component only in empty directory.\nIf you have existing code that you want to deploy use `odo deploy` or use `odo dev` command to quickly iterate on your component.") - } - o.flags = cmdline.GetFlags() return nil } -func isEmpty(fsys filesystem.Filesystem, path string) (bool, error) { - files, err := fsys.ReadDir(path) +// Validate validates the InitOptions based on completed values +func (o *InitOptions) Validate() error { + + devfilePresent, err := location.DirectoryContainsDevfile(o.clientset.FS, o.contextDir) if err != nil { - return false, err + return err + } + if devfilePresent { + return errors.New("a devfile already exists in the current directory") } - return len(files) == 0, nil -} -// Validate validates the InitOptions based on completed values -func (o *InitOptions) Validate() error { - err := o.clientset.InitClient.Validate(o.flags) + err = o.clientset.InitClient.Validate(o.flags, o.clientset.FS, o.contextDir) if err != nil { return err } @@ -125,21 +118,21 @@ func (o *InitOptions) Run() (err error) { return } if starterDownloaded { - err = fmt.Errorf("%w\nThe command failed after downloading the starter project. By security, the directory is not cleaned up.", err) + err = fmt.Errorf("%w\nthe command failed after downloading the starter project. By security, the directory is not cleaned up", err) } else { _ = o.clientset.FS.Remove("devfile.yaml") - err = fmt.Errorf("%w\nThe command failed, the devfile has been removed from current directory.", err) + err = fmt.Errorf("%w\nthe command failed, the devfile has been removed from current directory", err) } }() - o.devfileLocation, err = o.clientset.InitClient.SelectDevfile(o.flags) + o.devfileLocation, err = o.clientset.InitClient.SelectDevfile(o.flags, o.clientset.FS, o.contextDir) if err != nil { return err } devfilePath, err := o.clientset.InitClient.DownloadDevfile(o.devfileLocation, o.contextDir) if err != nil { - return fmt.Errorf("Unable to download devfile: %w", err) + return fmt.Errorf("unable to download devfile: %w", err) } devfileObj, _, err := devfile.ParseDevfileAndValidate(parser.ParserArgs{Path: devfilePath, FlattenedDevfile: pointer.BoolPtr(false)}) @@ -149,7 +142,7 @@ func (o *InitOptions) Run() (err error) { scontext.SetComponentType(o.ctx, component.GetComponentTypeFromDevfileMetadata(devfileObj.Data.GetMetadata())) - starterInfo, err := o.clientset.InitClient.SelectStarterProject(devfileObj, o.flags) + starterInfo, err := o.clientset.InitClient.SelectStarterProject(devfileObj, o.flags, o.clientset.FS, o.contextDir) if err != nil { return err } diff --git a/pkg/odo/cli/init/init_test.go b/pkg/odo/cli/init/init_test.go index 4bf640d8ecb..25a35b57d79 100644 --- a/pkg/odo/cli/init/init_test.go +++ b/pkg/odo/cli/init/init_test.go @@ -25,11 +25,12 @@ func TestInitOptions_Complete(t *testing.T) { name: "directory not empty", cmdlineExpects: func(mock *cmdline.MockCmdline) { mock.EXPECT().Context().Return(context.Background()) + mock.EXPECT().GetFlags().Times(1) }, fsysPopulate: func(fsys filesystem.Filesystem) { _ = fsys.WriteFile(".emptyfile", []byte(""), 0644) }, - wantErr: true, + wantErr: false, }, { name: "directory empty", diff --git a/pkg/odo/genericclioptions/clientset/clientset.go b/pkg/odo/genericclioptions/clientset/clientset.go index 7d89342e5fa..fc91d3a7a78 100644 --- a/pkg/odo/genericclioptions/clientset/clientset.go +++ b/pkg/odo/genericclioptions/clientset/clientset.go @@ -48,7 +48,7 @@ const ( // Clients will be created only once and be reused for sub-dependencies var subdeps map[string][]string = map[string][]string{ CATALOG: {FILESYSTEM, PREFERENCE}, - INIT: {FILESYSTEM, PREFERENCE, REGISTRY}, + INIT: {FILESYSTEM, PREFERENCE, REGISTRY, CATALOG}, PROJECT: {KUBERNETES_NULLABLE}, /* Add sub-dependencies here, if any */ } @@ -112,7 +112,7 @@ func Fetch(command *cobra.Command) (*Clientset, error) { dep.CatalogClient = catalog.NewCatalogClient(dep.FS, dep.PreferenceClient) } if isDefined(command, INIT) { - dep.InitClient = _init.NewInitClient(dep.FS, dep.PreferenceClient, dep.RegistryClient) + dep.InitClient = _init.NewInitClient(dep.FS, dep.PreferenceClient, dep.RegistryClient, dep.CatalogClient) } if isDefined(command, PROJECT) { dep.ProjectClient = project.NewClient(dep.KubernetesClient) diff --git a/pkg/odo/genericclioptions/context.go b/pkg/odo/genericclioptions/context.go index 8ecf26c691d..bb2874c50cc 100644 --- a/pkg/odo/genericclioptions/context.go +++ b/pkg/odo/genericclioptions/context.go @@ -1,6 +1,7 @@ package genericclioptions import ( + "errors" "fmt" "github.com/redhat-developer/odo/pkg/devfile" @@ -133,18 +134,22 @@ func New(parameters CreateParameters) (*Context, error) { } ctx.devfilePath = location.DevfileLocation(parameters.componentContext) - isDevfile := odoutil.CheckPathExists(ctx.devfilePath) - if parameters.devfile && isDevfile { - // Parse devfile and validate - devObj, err := devfile.ParseAndValidateFromFile(ctx.devfilePath) - if err != nil { - return nil, fmt.Errorf("failed to parse the devfile %s, with error: %s", ctx.devfilePath, err) - } - err = validate.ValidateDevfileData(devObj.Data) - if err != nil { - return nil, err + if parameters.devfile { + isDevfile := odoutil.CheckPathExists(ctx.devfilePath) + if isDevfile { + // Parse devfile and validate + devObj, err := devfile.ParseAndValidateFromFile(ctx.devfilePath) + if err != nil { + return nil, fmt.Errorf("failed to parse the devfile %s, with error: %s", ctx.devfilePath, err) + } + err = validate.ValidateDevfileData(devObj.Data) + if err != nil { + return nil, err + } + ctx.EnvSpecificInfo.SetDevfileObj(devObj) + } else { + return nil, errors.New("no devfile found") } - ctx.EnvSpecificInfo.SetDevfileObj(devObj) } return &Context{ diff --git a/pkg/odo/genericclioptions/context_test.go b/pkg/odo/genericclioptions/context_test.go index 1a1516aa8c2..a34dcacd357 100644 --- a/pkg/odo/genericclioptions/context_test.go +++ b/pkg/odo/genericclioptions/context_test.go @@ -241,7 +241,7 @@ func TestNew(t *testing.T) { _ = fs.WriteFile(filepath.Join(prefixDir, "myapp", ".odo", "env", "env.yaml"), []byte{}, 0644) }, }, - expectedErr: "", + expectedErr: "no devfile found", expected: Context{ internalCxt: internalCxt{ project: "myproject", diff --git a/pkg/preference/mock.go b/pkg/preference/mock.go index 89b550e1223..76573953357 100644 --- a/pkg/preference/mock.go +++ b/pkg/preference/mock.go @@ -187,32 +187,32 @@ func (mr *MockClientMockRecorder) NewPreferenceList() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewPreferenceList", reflect.TypeOf((*MockClient)(nil).NewPreferenceList)) } -// RegistryCacheTime mocks base method. -func (m *MockClient) RegistryCacheTime() *int { +// PushTimeout mocks base method. +func (m *MockClient) PushTimeout() *int { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RegistryCacheTime") + ret := m.ctrl.Call(m, "PushTimeout") ret0, _ := ret[0].(*int) return ret0 } -// RegistryCacheTime indicates an expected call of RegistryCacheTime. -func (mr *MockClientMockRecorder) RegistryCacheTime() *gomock.Call { +// PushTimeout indicates an expected call of PushTimeout. +func (mr *MockClientMockRecorder) PushTimeout() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegistryCacheTime", reflect.TypeOf((*MockClient)(nil).RegistryCacheTime)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PushTimeout", reflect.TypeOf((*MockClient)(nil).PushTimeout)) } -// PushTimeout mocks base method. -func (m *MockClient) PushTimeout() *int { +// RegistryCacheTime mocks base method. +func (m *MockClient) RegistryCacheTime() *int { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PushTimeout") + ret := m.ctrl.Call(m, "RegistryCacheTime") ret0, _ := ret[0].(*int) return ret0 } -// PushTimeout indicates an expected call of PushTimeout. -func (mr *MockClientMockRecorder) PushTimeout() *gomock.Call { +// RegistryCacheTime indicates an expected call of RegistryCacheTime. +func (mr *MockClientMockRecorder) RegistryCacheTime() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PushTimeout", reflect.TypeOf((*MockClient)(nil).PushTimeout)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegistryCacheTime", reflect.TypeOf((*MockClient)(nil).RegistryCacheTime)) } // RegistryHandler mocks base method. diff --git a/tests/integration/devfile/cmd_devfile_init_test.go b/tests/integration/devfile/cmd_devfile_init_test.go index 637dafd4c7a..f44b7b10440 100644 --- a/tests/integration/devfile/cmd_devfile_init_test.go +++ b/tests/integration/devfile/cmd_devfile_init_test.go @@ -33,6 +33,16 @@ var _ = Describe("odo devfile init command tests", func() { files := helper.ListFilesInDir(commonVar.Context) Expect(len(files)).To(Equal(0)) }) + By("running odo init in a directory containing a devfile.yaml", func() { + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-registry.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + err := helper.Cmd("odo", "init").ShouldFail().Err() + Expect(err).To(ContainSubstring("a devfile already exists in the current directory")) + }) + By("running odo init in a directory containing a .devfile.yaml", func() { + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-registry.yaml"), filepath.Join(commonVar.Context, ".devfile.yaml")) + err := helper.Cmd("odo", "init").ShouldFail().Err() + Expect(err).To(ContainSubstring("a devfile already exists in the current directory")) + }) }) When("running odo init with valid flags", func() { @@ -46,6 +56,19 @@ var _ = Describe("odo devfile init command tests", func() { }) }) + When("running odo init from a directory with sources", func() { + BeforeEach(func() { + helper.CopyExample(filepath.Join("source", "nodejs"), commonVar.Context) + }) + It("should work without --starter flag", func() { + helper.Cmd("odo", "init", "--name", "aname", "--devfile", "nodejs").ShouldPass() + }) + It("should not accept --starter flag", func() { + err := helper.Cmd("odo", "init", "--name", "aname", "--devfile", "nodejs", "--starter", "nodejs-starter").ShouldFail().Err() + Expect(err).To(ContainSubstring("--starter parameter cannot be used when the directory is not empty")) + }) + }) + When("devfile contains parent URI", func() { var originalKeyList []string var srcDevfile string diff --git a/vendor/github.com/redhat-developer/alizer/go/LICENSE b/vendor/github.com/redhat-developer/alizer/go/LICENSE new file mode 100644 index 00000000000..e48e0963459 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/LICENSE @@ -0,0 +1,277 @@ +Eclipse Public License - v 2.0 + + THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE + PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION + OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a) in the case of the initial Contributor, the initial content + Distributed under this Agreement, and + + b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + where such changes and/or additions to the Program originate from + and are Distributed by that particular Contributor. A Contribution + "originates" from a Contributor if it was added to the Program by + such Contributor itself or anyone acting on such Contributor's behalf. + Contributions do not include changes or additions to the Program that + are not Modified Works. + +"Contributor" means any person or entity that Distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which +are necessarily infringed by the use or sale of its Contribution alone +or when combined with the Program. + +"Program" means the Contributions Distributed in accordance with this +Agreement. + +"Recipient" means anyone who receives the Program under this Agreement +or any Secondary License (as applicable), including Contributors. + +"Derivative Works" shall mean any work, whether in Source Code or other +form, that is based on (or derived from) the Program and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. + +"Modified Works" shall mean any work in Source Code or other form that +results from an addition to, deletion from, or modification of the +contents of the Program, including, for purposes of clarity any new file +in Source Code form that contains any contents of the Program. Modified +Works shall not include works that contain only declarations, +interfaces, types, classes, structures, or files of the Program solely +in each case in order to link to, bind by name, or subclass the Program +or Modified Works thereof. + +"Distribute" means the acts of a) distributing or b) making available +in any manner that enables the transfer of a copy. + +"Source Code" means the form of a Program preferred for making +modifications, including but not limited to software source code, +documentation source, and configuration files. + +"Secondary License" means either the GNU General Public License, +Version 2.0, or any later versions of that license, including any +exceptions or additional permissions as identified by the initial +Contributor. + +2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare Derivative Works of, publicly display, + publicly perform, Distribute and sublicense the Contribution of such + Contributor, if any, and such Derivative Works. + + b) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, + if any, in Source Code or other form. This patent license shall + apply to the combination of the Contribution and the Program if, at + the time the Contribution is added by the Contributor, such addition + of the Contribution causes such combination to be covered by the + Licensed Patents. The patent license shall not apply to any other + combinations which include the Contribution. No hardware per se is + licensed hereunder. + + c) Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. + Each Contributor disclaims any liability to Recipient for claims + brought by any other entity based on infringement of intellectual + property rights or otherwise. As a condition to exercising the + rights and licenses granted hereunder, each Recipient hereby + assumes sole responsibility to secure any other intellectual + property rights needed, if any. For example, if a third party + patent license is required to allow Recipient to Distribute the + Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + + d) Each Contributor represents that to its knowledge it has + sufficient copyright rights in its Contribution, if any, to grant + the copyright license set forth in this Agreement. + + e) Notwithstanding the terms of any Secondary License, no + Contributor makes additional grants to any Recipient (other than + those set forth in this Agreement) as a result of such Recipient's + receipt of the Program under the terms of a Secondary License + (if permitted under the terms of Section 3). + +3. REQUIREMENTS + +3.1 If a Contributor Distributes the Program in any form, then: + + a) the Program must also be made available as Source Code, in + accordance with section 3.2, and the Contributor must accompany + the Program with a statement that the Source Code for the Program + is available under this Agreement, and informs Recipients how to + obtain it in a reasonable manner on or through a medium customarily + used for software exchange; and + + b) the Contributor may Distribute the Program under a license + different than this Agreement, provided that such license: + i) effectively disclaims on behalf of all other Contributors all + warranties and conditions, express and implied, including + warranties or conditions of title and non-infringement, and + implied warranties or conditions of merchantability and fitness + for a particular purpose; + + ii) effectively excludes on behalf of all other Contributors all + liability for damages, including direct, indirect, special, + incidental and consequential damages, such as lost profits; + + iii) does not attempt to limit or alter the recipients' rights + in the Source Code under section 3.2; and + + iv) requires any subsequent distribution of the Program by any + party to be under a license that satisfies the requirements + of this section 3. + +3.2 When the Program is Distributed as Source Code: + + a) it must be made available under this Agreement, or if the + Program (i) is combined with other material in a separate file or + files made available under a Secondary License, and (ii) the initial + Contributor attached to the Source Code the notice described in + Exhibit A of this Agreement, then the Program may be made available + under the terms of such Secondary Licenses, and + + b) a copy of this Agreement must be included with each copy of + the Program. + +3.3 Contributors may not remove or alter any copyright, patent, +trademark, attribution notices, disclaimers of warranty, or limitations +of liability ("notices") contained within the Program from any copy of +the Program which they Distribute, provided that Contributors may add +their own appropriate notices. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities +with respect to end users, business partners and the like. While this +license is intended to facilitate the commercial use of the Program, +the Contributor who includes the Program in a commercial product +offering should do so in a manner which does not create potential +liability for other Contributors. Therefore, if a Contributor includes +the Program in a commercial product offering, such Contributor +("Commercial Contributor") hereby agrees to defend and indemnify every +other Contributor ("Indemnified Contributor") against any losses, +damages and costs (collectively "Losses") arising from claims, lawsuits +and other legal actions brought by a third party against the Indemnified +Contributor to the extent caused by the acts or omissions of such +Commercial Contributor in connection with its distribution of the Program +in a commercial product offering. The obligations in this section do not +apply to any claims or Losses relating to any actual or alleged +intellectual property infringement. In order to qualify, an Indemnified +Contributor must: a) promptly notify the Commercial Contributor in +writing of such claim, and b) allow the Commercial Contributor to control, +and cooperate with the Commercial Contributor in, the defense and any +related settlement negotiations. The Indemnified Contributor may +participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Contributor's responsibility +alone. Under this section, the Commercial Contributor would have to +defend claims against the other Contributors related to those performance +claims and warranties, and if a court requires any other Contributor to +pay any damages as a result, the Commercial Contributor must pay +those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT +PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" +BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF +TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR +PURPOSE. Each Recipient is solely responsible for determining the +appropriateness of using and distributing the Program and assumes all +risks associated with its exercise of rights under this Agreement, +including but not limited to the risks and costs of program errors, +compliance with applicable laws, damage to or loss of data, programs +or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT +PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS +SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST +PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE +EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further +action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other software +or hardware) infringes such Recipient's patent(s), then such Recipient's +rights granted under Section 2(b) shall terminate as of the date such +litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of +time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use +and distribution of the Program as soon as reasonably practicable. +However, Recipient's obligations under this Agreement and any licenses +granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, +but in order to avoid inconsistency the Agreement is copyrighted and +may only be modified in the following manner. The Agreement Steward +reserves the right to publish new versions (including revisions) of +this Agreement from time to time. No one other than the Agreement +Steward has the right to modify this Agreement. The Eclipse Foundation +is the initial Agreement Steward. The Eclipse Foundation may assign the +responsibility to serve as the Agreement Steward to a suitable separate +entity. Each new version of the Agreement will be given a distinguishing +version number. The Program (including Contributions) may always be +Distributed subject to the version of the Agreement under which it was +received. In addition, after a new version of the Agreement is published, +Contributor may elect to Distribute the Program (including its +Contributions) under the new version. + +Except as expressly stated in Sections 2(a) and 2(b) above, Recipient +receives no rights or licenses to the intellectual property of any +Contributor under this Agreement, whether expressly, by implication, +estoppel or otherwise. All rights in the Program not expressly granted +under this Agreement are reserved. Nothing in this Agreement is intended +to be enforceable by any entity that is not a Contributor or Recipient. +No third-party beneficiary rights are created under this Agreement. + +Exhibit A - Form of Secondary Licenses Notice + +"This Source Code may also be made available under the following +Secondary Licenses when the conditions for such availability set forth +in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), +version(s), and exceptions or additional permissions here}." + + Simply including a copy of this Agreement, including this Exhibit A + is not sufficient to license the Source Code under Secondary Licenses. + + If it is not possible or desirable to put the notice in a particular + file, then You may include the notice in a location (such as a LICENSE + file in a relevant directory) where a recipient would be likely to + look for such a notice. + + You may add additional accurate notices of copyright ownership. diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/dotnet_enricher.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/dotnet_enricher.go new file mode 100644 index 00000000000..982092d16a1 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/dotnet_enricher.go @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + framework "github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/dotnet" + "github.com/redhat-developer/alizer/go/pkg/apis/language" + utils "github.com/redhat-developer/alizer/go/pkg/utils" +) + +type DotNetEnricher struct{} + +func (j DotNetEnricher) GetSupportedLanguages() []string { + return []string{"c#", "f#", "visual basic .net"} +} + +func getDotNetFrameworkDetectors() []FrameworkDetectorWithConfigFile { + return []FrameworkDetectorWithConfigFile{ + &framework.DotNetDetector{}, + } +} + +func (j DotNetEnricher) DoEnrichLanguage(language *language.Language, files *[]string) { + configFiles := utils.GetFilesByRegex(files, ".*\\.\\w+proj") + for _, configFile := range configFiles { + getDotNetFrameworks(language, configFile) + } +} + +func (j DotNetEnricher) IsConfigValidForComponentDetection(language string, config string) bool { + return IsConfigurationValidForLanguage(language, config) +} + +func getDotNetFrameworks(language *language.Language, configFile string) { + for _, detector := range getDotNetFrameworkDetectors() { + detector.DoFrameworkDetection(language, configFile) + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/enricher.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/enricher.go new file mode 100644 index 00000000000..1b587ce6e1b --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/enricher.go @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/redhat-developer/alizer/go/pkg/apis/language" + "github.com/redhat-developer/alizer/go/pkg/utils/langfiles" +) + +type Enricher interface { + GetSupportedLanguages() []string + DoEnrichLanguage(language *language.Language, files *[]string) + IsConfigValidForComponentDetection(language string, configFile string) bool +} + +type FrameworkDetectorWithConfigFile interface { + DoFrameworkDetection(language *language.Language, config string) +} + +type FrameworkDetectorWithoutConfigFile interface { + DoFrameworkDetection(language *language.Language, files *[]string) +} + +/* + IsConfigurationValidForLanguage check whether the configuration file is valid for current language. + For example when analyzing a nodejs project, we could find a package.json + within the node_modules folder. That is not to be considered valid + for component detection. + Paramenters: + language: language name + file: configuration file name + Returns: + bool: true if config file is valid for current language + +*/ +func IsConfigurationValidForLanguage(language string, file string) bool { + languageItem, err := langfiles.Get().GetLanguageByName(language) + if err != nil { + return false + } + for _, excludeFolder := range languageItem.ExcludeFolders { + if isFolderNameIncludedInPath(file, excludeFolder) { + return false + } + } + return true +} + +/* + isFolderNameIncludedInPath check if fullpath contains potentialSubFolderName + Parameters: + fullPath: complete path of a file + potentialSubFolderName: folder name + Returns: + bool: true if potentialSubFolderName is included in fullPath +*/ +func isFolderNameIncludedInPath(fullPath string, potentialSubFolderName string) bool { + pathSeparator := fmt.Sprintf("%c", os.PathSeparator) + dir, _ := filepath.Split(fullPath) + + subDirectories := strings.Split(dir, pathSeparator) + for _, subDir := range subDirectories { + if strings.EqualFold(subDir, potentialSubFolderName) { + return true + } + } + return false +} + +func getEnrichers() []Enricher { + return []Enricher{ + &JavaEnricher{}, + &JavaScriptEnricher{}, + &PythonEnricher{}, + &DotNetEnricher{}, + &GoEnricher{}, + } +} + +func GetEnricherByLanguage(language string) Enricher { + for _, enricher := range getEnrichers() { + if isLanguageSupportedByEnricher(language, enricher) { + return enricher + } + } + return nil +} + +func isLanguageSupportedByEnricher(nameLanguage string, enricher Enricher) bool { + for _, language := range enricher.GetSupportedLanguages() { + if strings.EqualFold(language, nameLanguage) { + return true + } + } + return false +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/dotnet/dotnet_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/dotnet/dotnet_detector.go new file mode 100644 index 00000000000..f83f72635ef --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/dotnet/dotnet_detector.go @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "encoding/xml" + "io/ioutil" + "os" + "strings" + + "github.com/redhat-developer/alizer/go/pkg/apis/language" + "github.com/redhat-developer/alizer/go/pkg/schema" + "github.com/redhat-developer/alizer/go/pkg/utils" +) + +type DotNetDetector struct{} + +func (m DotNetDetector) DoFrameworkDetection(language *language.Language, configFilePath string) { + framework := getFrameworks(configFilePath) + if framework == "" { + return + } + var frameworks []string + if strings.Contains(framework, ";") { + frameworks = strings.Split(framework, ";") + } else { + frameworks = []string{framework} + } + + for _, frm := range frameworks { + if !utils.Contains(language.Frameworks, frm) { + language.Frameworks = append(language.Frameworks, frm) + } + } +} + +func getFrameworks(configFilePath string) string { + xmlFile, err := os.Open(configFilePath) + if err != nil { + return "" + } + byteValue, _ := ioutil.ReadAll(xmlFile) + + var proj schema.DotNetProject + xml.Unmarshal(byteValue, &proj) + + defer xmlFile.Close() + if proj.PropertyGroup.TargetFramework != "" { + return proj.PropertyGroup.TargetFramework + } else if proj.PropertyGroup.TargetFrameworkVersion != "" { + return proj.PropertyGroup.TargetFrameworkVersion + } else if proj.PropertyGroup.TargetFrameworks != "" { + return proj.PropertyGroup.TargetFrameworks + } + return "" +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/beego_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/beego_detector.go new file mode 100644 index 00000000000..6275a771042 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/beego_detector.go @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "github.com/redhat-developer/alizer/go/pkg/apis/language" + "golang.org/x/mod/modfile" +) + +type BeegoDetector struct{} + +func (e BeegoDetector) DoFrameworkDetection(language *language.Language, goMod *modfile.File) { + if hasFramework(goMod.Require, "github.com/beego/beego") { + language.Frameworks = append(language.Frameworks, "Beego") + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/echo_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/echo_detector.go new file mode 100644 index 00000000000..cb219058eca --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/echo_detector.go @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "github.com/redhat-developer/alizer/go/pkg/apis/language" + "golang.org/x/mod/modfile" +) + +type EchoDetector struct{} + +func (e EchoDetector) DoFrameworkDetection(language *language.Language, goMod *modfile.File) { + if hasFramework(goMod.Require, "github.com/labstack/echo") { + language.Frameworks = append(language.Frameworks, "Echo") + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/fasthttp_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/fasthttp_detector.go new file mode 100644 index 00000000000..1c0401fda88 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/fasthttp_detector.go @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "github.com/redhat-developer/alizer/go/pkg/apis/language" + "golang.org/x/mod/modfile" +) + +type FastHttpDetector struct{} + +func (e FastHttpDetector) DoFrameworkDetection(language *language.Language, goMod *modfile.File) { + if hasFramework(goMod.Require, "github.com/valyala/fasthttp") { + language.Frameworks = append(language.Frameworks, "FastHttp") + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/gin_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/gin_detector.go new file mode 100644 index 00000000000..80fcbe40d86 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/gin_detector.go @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "github.com/redhat-developer/alizer/go/pkg/apis/language" + "golang.org/x/mod/modfile" +) + +type GinDetector struct{} + +func (e GinDetector) DoFrameworkDetection(language *language.Language, goMod *modfile.File) { + if hasFramework(goMod.Require, "github.com/gin-gonic/gin") { + language.Frameworks = append(language.Frameworks, "Gin") + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/go_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/go_detector.go new file mode 100644 index 00000000000..b742b93eb7e --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/go_detector.go @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "strings" + + "golang.org/x/mod/modfile" +) + +func hasFramework(modules []*modfile.Require, tag string) bool { + for _, module := range modules { + if strings.EqualFold(module.Mod.Path, tag) || strings.HasPrefix(module.Mod.Path, tag) { + return true + } + } + return false +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/gofiber_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/gofiber_detector.go new file mode 100644 index 00000000000..bee1c7225ce --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/gofiber_detector.go @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "github.com/redhat-developer/alizer/go/pkg/apis/language" + "golang.org/x/mod/modfile" +) + +type GoFiberDetector struct{} + +func (e GoFiberDetector) DoFrameworkDetection(language *language.Language, goMod *modfile.File) { + if hasFramework(goMod.Require, "github.com/gofiber/fiber") { + language.Frameworks = append(language.Frameworks, "GoFiber") + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/mux_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/mux_detector.go new file mode 100644 index 00000000000..4a47b52ca13 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/mux_detector.go @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "github.com/redhat-developer/alizer/go/pkg/apis/language" + "golang.org/x/mod/modfile" +) + +type MuxDetector struct{} + +func (e MuxDetector) DoFrameworkDetection(language *language.Language, goMod *modfile.File) { + if hasFramework(goMod.Require, "github.com/gorilla/mux") { + language.Frameworks = append(language.Frameworks, "Mux") + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/java_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/java_detector.go new file mode 100644 index 00000000000..ecf3d449b22 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/java_detector.go @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + utils "github.com/redhat-developer/alizer/go/pkg/utils" +) + +func hasFramework(configFile string, tag string) (bool, error) { + if utils.IsPathOfWantedFile(configFile, "build.gradle") { + return utils.IsTagInFile(configFile, tag) + } else { + return utils.IsTagInPomXMLFile(configFile, tag) + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/micronaut_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/micronaut_detector.go new file mode 100644 index 00000000000..4c7bc1be822 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/micronaut_detector.go @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "github.com/redhat-developer/alizer/go/pkg/apis/language" +) + +type MicronautDetector struct{} + +func (m MicronautDetector) DoFrameworkDetection(language *language.Language, config string) { + if hasFwk, _ := hasFramework(config, "io.micronaut"); hasFwk { + language.Frameworks = append(language.Frameworks, "Micronaut") + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/openliberty_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/openliberty_detector.go new file mode 100644 index 00000000000..2688deedebf --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/openliberty_detector.go @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "github.com/redhat-developer/alizer/go/pkg/apis/language" +) + +type OpenLibertyDetector struct{} + +func (o OpenLibertyDetector) DoFrameworkDetection(language *language.Language, config string) { + if hasFwk, _ := hasFramework(config, "io.openliberty"); hasFwk { + language.Frameworks = append(language.Frameworks, "OpenLiberty") + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/quarkus_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/quarkus_detector.go new file mode 100644 index 00000000000..d9f8f14e50b --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/quarkus_detector.go @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "github.com/redhat-developer/alizer/go/pkg/apis/language" +) + +type QuarkusDetector struct{} + +func (q QuarkusDetector) DoFrameworkDetection(language *language.Language, config string) { + if hasFwk, _ := hasFramework(config, "io.quarkus"); hasFwk { + language.Frameworks = append(language.Frameworks, "Quarkus") + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/spring_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/spring_detector.go new file mode 100644 index 00000000000..104be694255 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/spring_detector.go @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "github.com/redhat-developer/alizer/go/pkg/apis/language" +) + +type SpringDetector struct{} + +func (s SpringDetector) DoFrameworkDetection(language *language.Language, config string) { + if hasFwk, _ := hasFramework(config, "org.springframework"); hasFwk { + language.Frameworks = append(language.Frameworks, "Spring") + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/vertx_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/vertx_detector.go new file mode 100644 index 00000000000..814dd5c9d3b --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/vertx_detector.go @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "github.com/redhat-developer/alizer/go/pkg/apis/language" +) + +type VertxDetector struct{} + +func (v VertxDetector) DoFrameworkDetection(language *language.Language, config string) { + if hasFwk, _ := hasFramework(config, "io.vertx"); hasFwk { + language.Frameworks = append(language.Frameworks, "Vertx") + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/express_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/express_detector.go new file mode 100644 index 00000000000..669ca7e47ce --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/express_detector.go @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "github.com/redhat-developer/alizer/go/pkg/apis/language" +) + +type ExpressDetector struct{} + +func (e ExpressDetector) DoFrameworkDetection(language *language.Language, config string) { + if hasFramework(config, "express") { + language.Frameworks = append(language.Frameworks, "Express") + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/nodejs_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/nodejs_detector.go new file mode 100644 index 00000000000..ee9cd34076a --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/nodejs_detector.go @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + utils "github.com/redhat-developer/alizer/go/pkg/utils" +) + +func hasFramework(configFile string, tag string) bool { + return utils.IsTagInPackageJsonFile(configFile, tag) +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/reactjs_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/reactjs_detector.go new file mode 100644 index 00000000000..0ba454f5d35 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/reactjs_detector.go @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "github.com/redhat-developer/alizer/go/pkg/apis/language" +) + +type ReactJsDetector struct{} + +func (r ReactJsDetector) DoFrameworkDetection(language *language.Language, config string) { + if hasFramework(config, "react") { + language.Frameworks = append(language.Frameworks, "React") + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/python/django_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/python/django_detector.go new file mode 100644 index 00000000000..4806c65d696 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/python/django_detector.go @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "github.com/redhat-developer/alizer/go/pkg/apis/language" + "github.com/redhat-developer/alizer/go/pkg/utils" +) + +type DjangoDetector struct{} + +func (d DjangoDetector) DoFrameworkDetection(language *language.Language, files *[]string) { + managePy := utils.GetFile(files, "manage.py") + urlsPy := utils.GetFile(files, "urls.py") + wsgiPy := utils.GetFile(files, "wsgi.py") + asgiPy := utils.GetFile(files, "asgi.py") + + djangoFiles := []string{} + utils.AddToArrayIfValueExist(&djangoFiles, managePy) + utils.AddToArrayIfValueExist(&djangoFiles, urlsPy) + utils.AddToArrayIfValueExist(&djangoFiles, wsgiPy) + utils.AddToArrayIfValueExist(&djangoFiles, asgiPy) + + if hasFramework(&djangoFiles, "from django.") { + language.Frameworks = append(language.Frameworks, "Django") + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/python/python_detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/python/python_detector.go new file mode 100644 index 00000000000..248e24f4997 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/python/python_detector.go @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + utils "github.com/redhat-developer/alizer/go/pkg/utils" +) + +func hasFramework(files *[]string, tag string) bool { + for _, file := range *files { + if hasTag, _ := utils.IsTagInFile(file, tag); hasTag { + return true + } + } + return false +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/go_enricher.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/go_enricher.go new file mode 100644 index 00000000000..b57130c894e --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/go_enricher.go @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "errors" + "io/ioutil" + + framework "github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go" + "github.com/redhat-developer/alizer/go/pkg/apis/language" + utils "github.com/redhat-developer/alizer/go/pkg/utils" + "golang.org/x/mod/modfile" +) + +type GoEnricher struct{} + +type GoFrameworkDetector interface { + DoFrameworkDetection(language *language.Language, goMod *modfile.File) +} + +func getGoFrameworkDetectors() []GoFrameworkDetector { + return []GoFrameworkDetector{ + &framework.GinDetector{}, + &framework.BeegoDetector{}, + &framework.EchoDetector{}, + &framework.FastHttpDetector{}, + &framework.GoFiberDetector{}, + &framework.MuxDetector{}, + } +} + +func (j GoEnricher) GetSupportedLanguages() []string { + return []string{"go"} +} + +func (j GoEnricher) DoEnrichLanguage(language *language.Language, files *[]string) { + goModPath := utils.GetFile(files, "go.mod") + + if goModPath != "" { + goModFile, err := getGoModFile(goModPath) + if err != nil { + return + } + language.Tools = []string{goModFile.Go.Version} + detectGoFrameworks(language, goModFile) + } +} + +func (j GoEnricher) IsConfigValidForComponentDetection(language string, config string) bool { + return IsConfigurationValidForLanguage(language, config) +} + +func getGoModFile(filePath string) (*modfile.File, error) { + b, err := ioutil.ReadFile(filePath) + if err != nil { + return nil, errors.New("unable to read go.mod file") + } + return modfile.Parse(filePath, b, nil) +} + +func detectGoFrameworks(language *language.Language, configFile *modfile.File) { + for _, detector := range getGoFrameworkDetectors() { + detector.DoFrameworkDetection(language, configFile) + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/java_enricher.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/java_enricher.go new file mode 100644 index 00000000000..6188f46266d --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/java_enricher.go @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "path/filepath" + "strings" + + framework "github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java" + "github.com/redhat-developer/alizer/go/pkg/apis/language" + utils "github.com/redhat-developer/alizer/go/pkg/utils" +) + +type JavaEnricher struct{} + +func getJavaFrameworkDetectors() []FrameworkDetectorWithConfigFile { + return []FrameworkDetectorWithConfigFile{ + &framework.MicronautDetector{}, + &framework.OpenLibertyDetector{}, + &framework.QuarkusDetector{}, + &framework.SpringDetector{}, + &framework.VertxDetector{}, + } +} + +func (j JavaEnricher) GetSupportedLanguages() []string { + return []string{"java"} +} + +func (j JavaEnricher) DoEnrichLanguage(language *language.Language, files *[]string) { + gradle := utils.GetFile(files, "build.gradle") + maven := utils.GetFile(files, "pom.xml") + ant := utils.GetFile(files, "build.xml") + + if gradle != "" { + language.Tools = []string{"Gradle"} + detectJavaFrameworks(language, gradle) + } else if maven != "" { + language.Tools = []string{"Maven"} + detectJavaFrameworks(language, maven) + } else if ant != "" { + language.Tools = []string{"Ant"} + } +} + +func (j JavaEnricher) IsConfigValidForComponentDetection(language string, config string) bool { + return IsConfigurationValidForLanguage(language, config) && !isParentModuleMaven(config) +} + +/* + isParentModuleMaven checks if configuration file is a parent pom.xml + Parameters: + configPath: configuration file path + Returns: + bool: true if config file is parent +*/ +func isParentModuleMaven(configPath string) bool { + _, file := filepath.Split(configPath) + if !strings.EqualFold(file, "pom.xml") { + return false + } + + hasTag, _ := utils.IsTagInPomXMLFile(configPath, "modules") + return hasTag +} + +func detectJavaFrameworks(language *language.Language, configFile string) { + for _, detector := range getJavaFrameworkDetectors() { + detector.DoFrameworkDetection(language, configFile) + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/javascript_enricher.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/javascript_enricher.go new file mode 100644 index 00000000000..bc2b64436db --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/javascript_enricher.go @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + framework "github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs" + "github.com/redhat-developer/alizer/go/pkg/apis/language" + utils "github.com/redhat-developer/alizer/go/pkg/utils" +) + +type JavaScriptEnricher struct{} + +func getJavaScriptFrameworkDetectors() []FrameworkDetectorWithConfigFile { + return []FrameworkDetectorWithConfigFile{ + &framework.ExpressDetector{}, + &framework.ReactJsDetector{}, + } +} + +func (j JavaScriptEnricher) GetSupportedLanguages() []string { + return []string{"javascript", "typescript"} +} + +func (j JavaScriptEnricher) DoEnrichLanguage(language *language.Language, files *[]string) { + packageJson := utils.GetFile(files, "package.json") + + if packageJson != "" { + language.Tools = []string{"NodeJs"} + detectJavaScriptFrameworks(language, packageJson) + } +} + +func (j JavaScriptEnricher) IsConfigValidForComponentDetection(language string, config string) bool { + return IsConfigurationValidForLanguage(language, config) +} + +func detectJavaScriptFrameworks(language *language.Language, configFile string) { + for _, detector := range getJavaScriptFrameworkDetectors() { + detector.DoFrameworkDetection(language, configFile) + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/python_enricher.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/python_enricher.go new file mode 100644 index 00000000000..d60a55cb429 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/python_enricher.go @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + framework "github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/python" + "github.com/redhat-developer/alizer/go/pkg/apis/language" +) + +type PythonEnricher struct{} + +func getPythonFrameworkDetectors() []FrameworkDetectorWithoutConfigFile { + return []FrameworkDetectorWithoutConfigFile{ + &framework.DjangoDetector{}, + } +} + +func (p PythonEnricher) GetSupportedLanguages() []string { + return []string{"python"} +} + +func (p PythonEnricher) DoEnrichLanguage(language *language.Language, files *[]string) { + language.Tools = []string{} + detectPythonFrameworks(language, files) +} + +func (p PythonEnricher) IsConfigValidForComponentDetection(language string, config string) bool { + return IsConfigurationValidForLanguage(language, config) +} + +func detectPythonFrameworks(language *language.Language, files *[]string) { + for _, detector := range getPythonFrameworkDetectors() { + detector.DoFrameworkDetection(language, files) + } +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/language/language.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/language/language.go new file mode 100644 index 00000000000..7435ab4311c --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/language/language.go @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package language + +type Language struct { + Name string + Aliases []string + UsageInPercentage float64 + Frameworks []string + Tools []string + CanBeComponent bool +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/component_recognizer.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/component_recognizer.go new file mode 100644 index 00000000000..bcfee2b8400 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/component_recognizer.go @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "io/fs" + "path/filepath" + "strings" + + enricher "github.com/redhat-developer/alizer/go/pkg/apis/enricher" + "github.com/redhat-developer/alizer/go/pkg/apis/language" + "github.com/redhat-developer/alizer/go/pkg/utils/langfiles" +) + +type Component struct { + Path string + Languages []language.Language +} + +func DetectComponents(path string) ([]Component, error) { + files, err := getFilePaths(path) + if err != nil { + return []Component{}, err + } + components, err := detectComponents(files) + if err != nil { + return []Component{}, err + } + + // it may happen that a language has no a specific configuration file (e.g opposite to JAVA -> pom.xml and Nodejs -> package.json) + // we then rely on the language recognizer + directoriesNotBelongingToExistingComponent := getDirectoriesWithoutConfigFile(path, components) + components = append(components, getComponentsWithoutConfigFile(directoriesNotBelongingToExistingComponent)...) + + return components, nil +} + +/* + getComponentsWithoutConfigFile retrieves the components which are written with a language that does not require a config file + Parameters: + directories: list of directories to analyze + Returns: + components found +*/ +func getComponentsWithoutConfigFile(directories []string) []Component { + var components []Component + for _, dir := range directories { + component, _ := detectComponent(dir, "") + if component.Path != "" && isLangForNoConfigComponent(component.Languages) { + components = append(components, component) + } + } + return components +} + +/* + isLangForNoConfigComponent verify if main language requires any config file + Parameters: + component: + Returns: + bool: true if language does not require any config file +*/ +func isLangForNoConfigComponent(languages []language.Language) bool { + if len(languages) == 0 { + return false + } + + lang, err := langfiles.Get().GetLanguageByNameOrAlias(languages[0].Name) + if err != nil { + return false + } + + return len(lang.ConfigurationFiles) == 0 +} + +/* + getDirectoriesPathsWithoutConfigFile retrieves all directories that do not contain any Component + Parameters: + root: root folder where to start the search + components: list of components already detected + Returns: + list of directories path that does not contain any component +*/ +func getDirectoriesWithoutConfigFile(root string, components []Component) []string { + if len(components) == 0 { + return []string{root} + } + directories := []string{} + err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + if !strings.EqualFold(root, path) && d.IsDir() && !isAnyComponentInPath(path, components) { + directories = getParentFolders(path, directories) + } + return nil + }) + if err != nil { + return []string{} + } + return directories +} + +/** +* Return all paths which are not sub-folders of some other path within the list +* Target will be added to the list if it is not a sub-folder of any other path within the list +* If a path in the list is sub-folder of Target, that path will be removed. +* +* @param target new path to be added +* @param directories list of all previously added paths +* @return the list containing all paths which are not sub-folders of any other + */ +func getParentFolders(target string, directories []string) []string { + updatedDirectories := []string{} + for _, dir := range directories { + if isFirstPathParentOfSecond(dir, target) { + return directories + } + + if isFirstPathParentOfSecond(target, dir) { + continue + } + updatedDirectories = append(updatedDirectories, dir) + } + + updatedDirectories = append(updatedDirectories, target) + return updatedDirectories +} + +/* + isAnyComponentInPath checks if a component is present in path + Parameters: + path: path where to search for component + components: list of components + Returns: + true if a component is found starting from path +*/ +func isAnyComponentInPath(path string, components []Component) bool { + for _, component := range components { + if strings.EqualFold(path, component.Path) || isFirstPathParentOfSecond(component.Path, path) || isFirstPathParentOfSecond(path, component.Path) { + return true + } + } + return false +} + +/* + isFirstPathParentOfSecond check if first path is parent (direct or not) of second path + Parameters: + firstPath: path to be used as parent + secondPath: path to be used as child + Returns: + true if firstPath is part of secondPath +*/ +func isFirstPathParentOfSecond(firstPath string, secondPath string) bool { + return strings.Contains(secondPath, firstPath) +} + +/* + detectComponents detect components by analyzing all files + Parameters: + files: list of files to analyze + Returns: + list of components detected or err if any error occurs +*/ +func detectComponents(files []string) ([]Component, error) { + configurationPerLanguage := langfiles.Get().GetConfigurationPerLanguageMapping() + var components []Component + for _, file := range files { + dir, fileName := filepath.Split(file) + if language, isConfig := configurationPerLanguage[fileName]; isConfig && isConfigurationValid(language, file) { + component, err := detectComponent(dir, language) + if err != nil { + return []Component{}, err + } + if component.Path != "" { + components = append(components, component) + } + } + } + return components, nil +} + +/* + detectComponent returns a Component if found: + - language must be enabled for component detection + - there should be at least one framework detected + , error otherwise + Parameters: + root: path to be used as root where to start the detection + language: language to be used as target for detection + Returns: + component detected or error if any error occurs +*/ +func detectComponent(root string, language string) (Component, error) { + languages, err := Analyze(root) + if err != nil { + return Component{}, err + } + languages = getLanguagesWeightedByConfigFile(languages, language) + if len(languages) > 0 { + if mainLang := languages[0]; mainLang.CanBeComponent && len(mainLang.Frameworks) > 0 { + return Component{ + Path: root, + Languages: languages, + }, nil + } + } + + return Component{}, nil + +} + +/* + getLanguagesWeightedByConfigFile returns the list of languages reordered by importance per config file. + Language found by analyzing the config file is used as target. + Parameters: + languages: list of languages to be reordered + languageByConfig: target language + Returns: + list of languages reordered +*/ +func getLanguagesWeightedByConfigFile(languages []language.Language, languageByConfig string) []language.Language { + if languageByConfig == "" { + return languages + } + + for index, lang := range languages { + if strings.EqualFold(lang.Name, languageByConfig) { + sliceWithoutLang := append(languages[:index], languages[index+1:]...) + return append([]language.Language{lang}, sliceWithoutLang...) + } + } + return languages +} + +func isConfigurationValid(language string, file string) bool { + langEnricher := enricher.GetEnricherByLanguage(language) + if langEnricher != nil { + return langEnricher.IsConfigValidForComponentDetection(language, file) + } + return false +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/devfile_recognizer.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/devfile_recognizer.go new file mode 100644 index 00000000000..c01048d7481 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/devfile_recognizer.go @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "errors" + "strings" + + "github.com/redhat-developer/alizer/go/pkg/apis/language" +) + +type DevFileType struct { + Name string + Language string + ProjectType string + Tags []string +} + +func SelectDevFileFromTypes(path string, devFileTypes []DevFileType) (DevFileType, error) { + languages, err := Analyze(path) + if err != nil { + return DevFileType{}, err + } + devfile, err := SelectDevFileUsingLanguagesFromTypes(languages, devFileTypes) + if err != nil { + return DevFileType{}, errors.New("No valid devfile found for project in " + path) + } + return devfile, nil +} + +func SelectDevFileUsingLanguagesFromTypes(languages []language.Language, devFileTypes []DevFileType) (DevFileType, error) { + for _, language := range languages { + devfile, err := selectDevFileByLanguage(language, devFileTypes) + if err == nil { + return devfile, nil + } + } + return DevFileType{}, errors.New("no valid devfile found by using those languages") +} + +func selectDevFileByLanguage(language language.Language, devFileTypes []DevFileType) (DevFileType, error) { + scoreTarget := 0 + devfileTarget := DevFileType{} + FRAMEWORK_WEIGHT := 10 + TOOL_WEIGHT := 5 + for _, devFile := range devFileTypes { + score := 0 + if strings.EqualFold(devFile.Language, language.Name) || matches(language.Aliases, devFile.Language) { + score++ + if matches(language.Frameworks, devFile.ProjectType) { + score += FRAMEWORK_WEIGHT + } + for _, tag := range devFile.Tags { + if matches(language.Frameworks, tag) { + score += FRAMEWORK_WEIGHT + } + if matches(language.Tools, tag) { + score += TOOL_WEIGHT + } + } + } + if score > scoreTarget { + scoreTarget = score + devfileTarget = devFile + } + } + + if scoreTarget == 0 { + return devfileTarget, errors.New("No valid devfile found for current language " + language.Name) + } + return devfileTarget, nil +} + +func matches(values []string, valueToFind string) bool { + for _, value := range values { + if strings.EqualFold(value, valueToFind) { + return true + } + } + return false +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/language_recognizer.go b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/language_recognizer.go new file mode 100644 index 00000000000..43ac4795380 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/language_recognizer.go @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package recognizer + +import ( + "os" + "path/filepath" + "sort" + + enricher "github.com/redhat-developer/alizer/go/pkg/apis/enricher" + "github.com/redhat-developer/alizer/go/pkg/apis/language" + langfile "github.com/redhat-developer/alizer/go/pkg/utils/langfiles" +) + +type languageItem struct { + item langfile.LanguageItem + percentage int +} + +func Analyze(path string) ([]language.Language, error) { + languagesFile := langfile.Get() + languagesDetected := make(map[string]languageItem) + + paths, err := getFilePaths(path) + if err != nil { + return []language.Language{}, err + } + extensionsGrouped := extractExtensions(paths) + extensionHasProgrammingLanguage := false + totalProgrammingOccurrences := 0 + for extension := range extensionsGrouped { + languages := languagesFile.GetLanguagesByExtension(extension) + if len(languages) == 0 { + continue + } + for _, language := range languages { + if language.Kind == "programming" { + var languageFileItem langfile.LanguageItem + var err error + if len(language.Group) == 0 { + languageFileItem = language + } else { + languageFileItem, err = languagesFile.GetLanguageByName(language.Group) + if err != nil { + continue + } + } + tmpLanguageItem := languageItem{languageFileItem, 0} + percentage := languagesDetected[tmpLanguageItem.item.Name].percentage + extensionsGrouped[extension] + tmpLanguageItem.percentage = percentage + languagesDetected[tmpLanguageItem.item.Name] = tmpLanguageItem + extensionHasProgrammingLanguage = true + } + } + if extensionHasProgrammingLanguage { + totalProgrammingOccurrences += extensionsGrouped[extension] + extensionHasProgrammingLanguage = false + } + } + + var languagesFound []language.Language + for name, item := range languagesDetected { + tmpPercentage := float64(item.percentage) / float64(totalProgrammingOccurrences) + tmpPercentage = float64(int(tmpPercentage*10000)) / 10000 + if tmpPercentage > 0.02 { + tmpLanguage := language.Language{ + Name: name, + Aliases: item.item.Aliases, + UsageInPercentage: tmpPercentage * 100, + Frameworks: []string{}, + Tools: []string{}, + CanBeComponent: item.item.Component} + langEnricher := enricher.GetEnricherByLanguage(name) + if langEnricher != nil { + langEnricher.DoEnrichLanguage(&tmpLanguage, &paths) + } + languagesFound = append(languagesFound, tmpLanguage) + } + } + + sort.SliceStable(languagesFound, func(i, j int) bool { + return languagesFound[i].UsageInPercentage > languagesFound[j].UsageInPercentage + }) + + return languagesFound, nil +} + +func extractExtensions(paths []string) map[string]int { + extensions := make(map[string]int) + for _, path := range paths { + extension := filepath.Ext(path) + if len(extension) == 0 { + continue + } + count := extensions[extension] + 1 + extensions[extension] = count + } + return extensions +} + +func getFilePaths(root string) ([]string, error) { + var files []string + err := filepath.Walk(root, + func(path string, info os.FileInfo, err error) error { + files = append(files, path) + return nil + }) + return files, err +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/schema/dotnet_proj.go b/vendor/github.com/redhat-developer/alizer/go/pkg/schema/dotnet_proj.go new file mode 100644 index 00000000000..34d27d8915e --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/schema/dotnet_proj.go @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package schema + +type DotNetProject struct { + PropertyGroup struct { + TargetFramework string `xml:"TargetFramework"` + TargetFrameworkVersion string `xml:"TargetFrameworkVersion"` + TargetFrameworks string `xml:"TargetFrameworks"` + } `xml:"PropertyGroup"` +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/schema/languages.go b/vendor/github.com/redhat-developer/alizer/go/pkg/schema/languages.go new file mode 100644 index 00000000000..5fb5c1d6a71 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/schema/languages.go @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package schema + +type LanguageProperties struct { + Type string `yaml:"type,omitempty"` + Color string `yaml:"color,omitempty"` + Extensions []string `yaml:"extensions,omitempty"` + TmScope string `yaml:"tm_scope,omitempty"` + AceMode string `yaml:"ace_mode,omitempty"` + LanguageID int `yaml:"language_id,omitempty"` + Aliases []string `yaml:"aliases,omitempty"` + CodemirrorMode string `yaml:"codemirror_mode,omitempty"` + CodemirrorMimeType string `yaml:"codemirror_mime_type,omitempty"` + Group string `yaml:"group"` + Filenames []string `yaml:"filenames"` +} + +type LanguagesProperties map[string]LanguageProperties + +type LanguageCustomization struct { + ConfigurationFiles []string `yaml:"configuration_files"` + Component bool `yaml:"component"` + ExcludeFolders []string `yaml:"exclude_folders,omitempty"` +} + +type LanguagesCustomizations map[string]LanguageCustomization diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/schema/package_json.go b/vendor/github.com/redhat-developer/alizer/go/pkg/schema/package_json.go new file mode 100644 index 00000000000..6cddfda7646 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/schema/package_json.go @@ -0,0 +1,15 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package schema + +type PackageJson struct { + Dependencies map[string]string `json:"dependencies"` +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/schema/pom_xml.go b/vendor/github.com/redhat-developer/alizer/go/pkg/schema/pom_xml.go new file mode 100644 index 00000000000..d2f82500ab9 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/schema/pom_xml.go @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package schema + +type Pom struct { + Dependencies struct { + Text string `xml:",chardata"` + Dependency []struct { + Text string `xml:",chardata"` + GroupId string `xml:"groupId"` + ArtifactId string `xml:"artifactId"` + Version string `xml:"version"` + Scope string `xml:"scope"` + } `xml:"dependency"` + } `xml:"dependencies"` + Modules struct { + Module string `xml:"module"` + } `xml:"modules,omitempty"` +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/utils/detector.go b/vendor/github.com/redhat-developer/alizer/go/pkg/utils/detector.go new file mode 100644 index 00000000000..25f0fb05541 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/utils/detector.go @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package utils + +import ( + "encoding/json" + "encoding/xml" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/redhat-developer/alizer/go/pkg/schema" +) + +func GetFilesByRegex(filePaths *[]string, regexFile string) []string { + matchedPaths := []string{} + for _, path := range *filePaths { + if isPathOfWantedRegex(path, regexFile) { + matchedPaths = append(matchedPaths, path) + } + } + return matchedPaths +} + +func isPathOfWantedRegex(path string, regexFile string) bool { + _, file := filepath.Split(path) + matched, _ := regexp.MatchString(regexFile, file) + return matched +} + +func GetFile(filePaths *[]string, wantedFile string) string { + for _, path := range *filePaths { + if IsPathOfWantedFile(path, wantedFile) { + return path + } + } + return "" +} + +func HasFile(files *[]string, wantedFile string) bool { + for _, path := range *files { + if IsPathOfWantedFile(path, wantedFile) { + return true + } + } + return false +} + +func IsPathOfWantedFile(path string, wantedFile string) bool { + _, file := filepath.Split(path) + return strings.EqualFold(file, wantedFile) +} + +func IsTagInFile(file string, tag string) (bool, error) { + contentInByte, err := ioutil.ReadFile(file) + if err != nil { + return false, err + } + content := string(contentInByte) + return strings.Contains(content, tag), nil +} + +func IsTagInPomXMLFile(pomFilePath string, tag string) (bool, error) { + pom, err := GetPomFileContent(pomFilePath) + if err != nil { + return false, err + } + for _, dependency := range pom.Dependencies.Dependency { + if strings.Contains(dependency.GroupId, tag) { + return true, nil + } + } + return false, nil +} + +func GetPomFileContent(pomFilePath string) (schema.Pom, error) { + xmlFile, err := os.Open(pomFilePath) + if err != nil { + return schema.Pom{}, err + } + byteValue, _ := ioutil.ReadAll(xmlFile) + + var pom schema.Pom + xml.Unmarshal(byteValue, &pom) + + defer xmlFile.Close() + return pom, nil +} + +func IsTagInPackageJsonFile(file string, tag string) bool { + jsonFile, err := os.Open(file) + if err != nil { + return false + } + byteValue, _ := ioutil.ReadAll(jsonFile) + + var packageJson schema.PackageJson + json.Unmarshal(byteValue, &packageJson) + + defer jsonFile.Close() + if packageJson.Dependencies != nil { + for dependency := range packageJson.Dependencies { + if strings.Contains(dependency, tag) { + return true + } + } + } + return false +} + +func AddToArrayIfValueExist(arr *[]string, val string) { + if val != "" { + *arr = append(*arr, val) + } +} + +func Contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + + return false +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/languages_file_handler.go b/vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/languages_file_handler.go new file mode 100644 index 00000000000..296f1aa07f6 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/languages_file_handler.go @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. + ******************************************************************************/ +package langfiles + +import ( + "embed" + "errors" + "strings" + + "github.com/redhat-developer/alizer/go/pkg/schema" + "gopkg.in/yaml.v3" +) + +type LanguageItem struct { + Name string + Aliases []string + Kind string + Group string + ConfigurationFiles []string + ExcludeFolders []string + Component bool +} + +type LanguageFile struct { + languages map[string]LanguageItem + extensionsXLanguage map[string][]LanguageItem +} + +var ( + instance *LanguageFile + + //go:embed resources + res embed.FS +) + +func Get() *LanguageFile { + if instance == nil { + instance = create() + } + return instance +} + +func create() *LanguageFile { + languages := make(map[string]LanguageItem) + extensionsXLanguage := make(map[string][]LanguageItem) + + languagesProperties := getLanguagesProperties() + + for name, properties := range languagesProperties { + languageItem := LanguageItem{ + Name: name, + Aliases: properties.Aliases, + Kind: properties.Type, + Group: properties.Group, + } + customizeLanguage(&languageItem) + languages[name] = languageItem + extensions := properties.Extensions + for _, ext := range extensions { + languagesByExtension := extensionsXLanguage[ext] + languagesByExtension = append(languagesByExtension, languageItem) + extensionsXLanguage[ext] = languagesByExtension + } + } + + return &LanguageFile{ + languages: languages, + extensionsXLanguage: extensionsXLanguage, + } +} + +func customizeLanguage(languageItem *LanguageItem) { + languagesCustomizations := getLanguageCustomizations() + if customization, hasCustomization := languagesCustomizations[(*languageItem).Name]; hasCustomization { + (*languageItem).ConfigurationFiles = customization.ConfigurationFiles + (*languageItem).ExcludeFolders = customization.ExcludeFolders + (*languageItem).Component = customization.Component + } +} + +func getLanguagesProperties() schema.LanguagesProperties { + yamlFile, err := res.ReadFile("resources/languages.yml") + if err != nil { + return schema.LanguagesProperties{} + } + var data schema.LanguagesProperties + yaml.Unmarshal(yamlFile, &data) + return data +} + +func getLanguageCustomizations() schema.LanguagesCustomizations { + yamlFile, err := res.ReadFile("resources/languages-customization.yml") + if err != nil { + return schema.LanguagesCustomizations{} + } + + var data schema.LanguagesCustomizations + yaml.Unmarshal(yamlFile, &data) + return data +} + +func (l *LanguageFile) GetLanguagesByExtension(extension string) []LanguageItem { + return l.extensionsXLanguage[extension] +} + +func (l *LanguageFile) GetLanguageByName(name string) (LanguageItem, error) { + for langName, langItem := range l.languages { + if langName == name { + return langItem, nil + } + } + return LanguageItem{}, errors.New("no language found with this name") +} + +func (l *LanguageFile) GetLanguageByAlias(alias string) (LanguageItem, error) { + for _, langItem := range l.languages { + for _, aliasItem := range langItem.Aliases { + if strings.EqualFold(alias, aliasItem) { + return langItem, nil + } + } + } + return LanguageItem{}, errors.New("no language found with this alias") +} + +func (l *LanguageFile) GetLanguageByNameOrAlias(name string) (LanguageItem, error) { + langItem, err := l.GetLanguageByName(name) + if err == nil { + return langItem, nil + } + + return l.GetLanguageByAlias(name) +} + +func (l *LanguageFile) GetConfigurationPerLanguageMapping() map[string]string { + configurationPerLanguage := make(map[string]string) + for langName, langItem := range l.languages { + configurationFiles := langItem.ConfigurationFiles + for _, configFile := range configurationFiles { + configurationPerLanguage[configFile] = langName + } + } + return configurationPerLanguage +} diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/resources/languages-customization.yml b/vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/resources/languages-customization.yml new file mode 100644 index 00000000000..1aca28a0c69 --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/resources/languages-customization.yml @@ -0,0 +1,43 @@ +C#: + aliases: + - "dotnet" + configuration_files: + - ".*\\.\\w+proj" + - "appsettings.json" + component: true +F#: + aliases: + - "dotnet" + configuration_files: + - ".*\\.\\w+proj" + - "appsettings.json" + component: true +Go: + configuration_files: + - "go.mod" + - "go.sum" + component: true +Java: + configuration_files: + - "pom.xml" + - "build.gradle" + component: true +JavaScript: + exclude_folders: + - "node_modules" + configuration_files: + - "package.json" + component: true +Python: + component: true +Rust: + configuration_files: + - "Cargo.toml" + component: true +Visual Basic .NET: + aliases: + - "dotnet" + configuration_files: + - ".*\\.\\w+proj" + - "appsettings.json" + component: true diff --git a/vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/resources/languages.yml b/vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/resources/languages.yml new file mode 100644 index 00000000000..edbbc2a78ae --- /dev/null +++ b/vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/resources/languages.yml @@ -0,0 +1,6623 @@ +# Defines all Languages known to GitHub. +# +# fs_name - Optional field. Only necessary as a replacement for the sample directory name if the +# language name is not a valid filename under the Windows filesystem (e.g., if it +# contains an asterisk). +# type - Either data, programming, markup, prose, or nil +# aliases - An Array of additional aliases (implicitly +# includes name.downcase) +# ace_mode - A String name of the Ace Mode used for highlighting whenever +# a file is edited. This must match one of the filenames in http://git.io/3XO_Cg. +# Use "text" if a mode does not exist. +# codemirror_mode - A String name of the CodeMirror Mode used for highlighting whenever a file is edited. +# This must match a mode from https://git.io/vi9Fx +# codemirror_mime_type - A String name of the file mime type used for highlighting whenever a file is edited. +# This should match the `mime` associated with the mode from https://git.io/f4SoQ +# wrap - Boolean wrap to enable line wrapping (default: false) +# extensions - An Array of associated extensions (the first one is +# considered the primary extension, the others should be +# listed alphabetically) +# filenames - An Array of filenames commonly associated with the language +# interpreters - An Array of associated interpreters +# searchable - Boolean flag to enable searching (defaults to true) +# language_id - Integer used as a language-name-independent indexed field so that we can rename +# languages in Linguist without reindexing all the code on GitHub. Must not be +# changed for existing languages without the explicit permission of GitHub staff. +# color - CSS hex color to represent the language. Only used if type is "programming" or "markup". +# tm_scope - The TextMate scope that represents this programming +# language. This should match one of the scopes listed in +# the grammars.yml file. Use "none" if there is no grammar +# for this language. +# group - Name of the parent language. Languages in a group are counted +# in the statistics as the parent language. +# +# Any additions or modifications (even trivial) should have corresponding +# test changes in `test/test_blob.rb`. +# +# Please keep this list alphabetized. Capitalization comes before lowercase. + +--- +1C Enterprise: + type: programming + color: "#814CCC" + extensions: + - ".bsl" + - ".os" + tm_scope: source.bsl + ace_mode: text + language_id: 0 +4D: + type: programming + extensions: + - ".4dm" + tm_scope: source.4dm + ace_mode: text + language_id: 577529595 +ABAP: + type: programming + color: "#E8274B" + extensions: + - ".abap" + tm_scope: source.abap + ace_mode: abap + language_id: 1 +ABNF: + type: data + ace_mode: text + extensions: + - ".abnf" + tm_scope: source.abnf + language_id: 429 +AGS Script: + type: programming + color: "#B9D9FF" + aliases: + - ags + extensions: + - ".asc" + - ".ash" + tm_scope: source.c++ + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-c++src + language_id: 2 +AL: + type: programming + color: "#3AA2B5" + extensions: + - ".al" + tm_scope: source.al + ace_mode: text + language_id: 658971832 +AMPL: + type: programming + color: "#E6EFBB" + extensions: + - ".ampl" + - ".mod" + tm_scope: source.ampl + ace_mode: text + language_id: 3 +ANTLR: + type: programming + color: "#9DC3FF" + extensions: + - ".g4" + tm_scope: source.antlr + ace_mode: text + language_id: 4 +API Blueprint: + type: markup + color: "#2ACCA8" + ace_mode: markdown + extensions: + - ".apib" + tm_scope: text.html.markdown.source.gfm.apib + language_id: 5 +APL: + type: programming + color: "#5A8164" + extensions: + - ".apl" + - ".dyalog" + interpreters: + - apl + - aplx + - dyalog + tm_scope: source.apl + ace_mode: text + codemirror_mode: apl + codemirror_mime_type: text/apl + language_id: 6 +ASL: + type: programming + ace_mode: text + extensions: + - ".asl" + - ".dsl" + tm_scope: source.asl + language_id: 124996147 +ASN.1: + type: data + extensions: + - ".asn" + - ".asn1" + tm_scope: source.asn + ace_mode: text + codemirror_mode: asn.1 + codemirror_mime_type: text/x-ttcn-asn + language_id: 7 +ASP.NET: + type: programming + tm_scope: text.html.asp + color: "#9400ff" + aliases: + - aspx + - aspx-vb + extensions: + - ".asax" + - ".ascx" + - ".ashx" + - ".asmx" + - ".aspx" + - ".axd" + ace_mode: text + codemirror_mode: htmlembedded + codemirror_mime_type: application/x-aspx + language_id: 564186416 +ATS: + type: programming + color: "#1ac620" + aliases: + - ats2 + extensions: + - ".dats" + - ".hats" + - ".sats" + tm_scope: source.ats + ace_mode: ocaml + language_id: 9 +ActionScript: + type: programming + tm_scope: source.actionscript.3 + color: "#882B0F" + aliases: + - actionscript 3 + - actionscript3 + - as3 + extensions: + - ".as" + ace_mode: actionscript + language_id: 10 +Ada: + type: programming + color: "#02f88c" + extensions: + - ".adb" + - ".ada" + - ".ads" + aliases: + - ada95 + - ada2005 + tm_scope: source.ada + ace_mode: ada + language_id: 11 +Adobe Font Metrics: + type: data + tm_scope: source.afm + extensions: + - ".afm" + aliases: + - acfm + - adobe composite font metrics + - adobe multiple font metrics + - amfm + ace_mode: text + language_id: 147198098 +Agda: + type: programming + color: "#315665" + extensions: + - ".agda" + tm_scope: source.agda + ace_mode: text + language_id: 12 +Alloy: + type: programming + color: "#64C800" + extensions: + - ".als" + tm_scope: source.alloy + ace_mode: text + language_id: 13 +Alpine Abuild: + type: programming + group: Shell + aliases: + - abuild + - apkbuild + filenames: + - APKBUILD + tm_scope: source.shell + ace_mode: sh + codemirror_mode: shell + codemirror_mime_type: text/x-sh + language_id: 14 +Altium Designer: + type: data + aliases: + - altium + extensions: + - ".OutJob" + - ".PcbDoc" + - ".PrjPCB" + - ".SchDoc" + tm_scope: source.ini + ace_mode: ini + language_id: 187772328 +AngelScript: + type: programming + color: "#C7D7DC" + extensions: + - ".as" + - ".angelscript" + tm_scope: source.angelscript + ace_mode: text + codemirror_mode: clike + codemirror_mime_type: text/x-c++src + language_id: 389477596 +Ant Build System: + type: data + tm_scope: text.xml.ant + filenames: + - ant.xml + - build.xml + ace_mode: xml + codemirror_mode: xml + codemirror_mime_type: application/xml + language_id: 15 +ApacheConf: + type: data + aliases: + - aconf + - apache + extensions: + - ".apacheconf" + - ".vhost" + filenames: + - ".htaccess" + - apache2.conf + - httpd.conf + tm_scope: source.apache-config + ace_mode: apache_conf + language_id: 16 +Apex: + type: programming + color: "#1797c0" + extensions: + - ".cls" + tm_scope: source.java + ace_mode: java + codemirror_mode: clike + codemirror_mime_type: text/x-java + language_id: 17 +Apollo Guidance Computer: + type: programming + color: "#0B3D91" + group: Assembly + extensions: + - ".agc" + tm_scope: source.agc + ace_mode: assembly_x86 + language_id: 18 +AppleScript: + type: programming + aliases: + - osascript + extensions: + - ".applescript" + - ".scpt" + interpreters: + - osascript + tm_scope: source.applescript + ace_mode: applescript + color: "#101F1F" + language_id: 19 +Arc: + type: programming + color: "#aa2afe" + extensions: + - ".arc" + tm_scope: none + ace_mode: text + language_id: 20 +AsciiDoc: + type: prose + ace_mode: asciidoc + wrap: true + extensions: + - ".asciidoc" + - ".adoc" + - ".asc" + tm_scope: text.html.asciidoc + language_id: 22 +AspectJ: + type: programming + color: "#a957b0" + extensions: + - ".aj" + tm_scope: source.aspectj + ace_mode: text + language_id: 23 +Assembly: + type: programming + color: "#6E4C13" + aliases: + - asm + - nasm + extensions: + - ".asm" + - ".a51" + - ".i" + - ".inc" + - ".nasm" + tm_scope: source.assembly + ace_mode: assembly_x86 + language_id: 24 +Asymptote: + type: programming + color: "#ff0000" + extensions: + - ".asy" + interpreters: + - asy + tm_scope: source.c++ + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-kotlin + language_id: 591605007 +Augeas: + type: programming + extensions: + - ".aug" + tm_scope: none + ace_mode: text + language_id: 25 +AutoHotkey: + type: programming + color: "#6594b9" + aliases: + - ahk + extensions: + - ".ahk" + - ".ahkl" + tm_scope: source.ahk + ace_mode: autohotkey + language_id: 26 +AutoIt: + type: programming + color: "#1C3552" + aliases: + - au3 + - AutoIt3 + - AutoItScript + extensions: + - ".au3" + tm_scope: source.autoit + ace_mode: autohotkey + language_id: 27 +Avro IDL: + type: data + extensions: + - ".avdl" + tm_scope: source.avro + ace_mode: text + language_id: 785497837 +Awk: + type: programming + extensions: + - ".awk" + - ".auk" + - ".gawk" + - ".mawk" + - ".nawk" + interpreters: + - awk + - gawk + - mawk + - nawk + tm_scope: source.awk + ace_mode: text + language_id: 28 +Ballerina: + type: programming + extensions: + - ".bal" + tm_scope: source.ballerina + ace_mode: text + color: "#FF5000" + language_id: 720859680 +Batchfile: + type: programming + aliases: + - bat + - batch + - dosbatch + - winbatch + extensions: + - ".bat" + - ".cmd" + tm_scope: source.batchfile + ace_mode: batchfile + color: "#C1F12E" + language_id: 29 +Befunge: + type: programming + extensions: + - ".befunge" + tm_scope: source.befunge + ace_mode: text + language_id: 30 +BibTeX: + type: markup + group: TeX + extensions: + - ".bib" + - ".bibtex" + tm_scope: text.bibtex + ace_mode: tex + codemirror_mode: stex + codemirror_mime_type: text/x-stex + language_id: 982188347 +Bison: + type: programming + color: "#6A463F" + group: Yacc + tm_scope: source.yacc + extensions: + - ".bison" + ace_mode: text + language_id: 31 +BitBake: + type: programming + tm_scope: none + extensions: + - ".bb" + ace_mode: text + language_id: 32 +Blade: + type: markup + color: "#f7523f" + extensions: + - ".blade" + - ".blade.php" + tm_scope: text.html.php.blade + ace_mode: text + language_id: 33 +BlitzBasic: + type: programming + aliases: + - b3d + - blitz3d + - blitzplus + - bplus + extensions: + - ".bb" + - ".decls" + tm_scope: source.blitzmax + ace_mode: text + language_id: 34 +BlitzMax: + type: programming + color: "#cd6400" + extensions: + - ".bmx" + aliases: + - bmax + tm_scope: source.blitzmax + ace_mode: text + language_id: 35 +Bluespec: + type: programming + extensions: + - ".bsv" + tm_scope: source.bsv + ace_mode: verilog + language_id: 36 +Boo: + type: programming + color: "#d4bec1" + extensions: + - ".boo" + ace_mode: text + tm_scope: source.boo + language_id: 37 +Brainfuck: + type: programming + color: "#2F2530" + extensions: + - ".b" + - ".bf" + tm_scope: source.bf + ace_mode: text + codemirror_mode: brainfuck + codemirror_mime_type: text/x-brainfuck + language_id: 38 +Brightscript: + type: programming + extensions: + - ".brs" + tm_scope: source.brightscript + ace_mode: text + language_id: 39 +C: + type: programming + color: "#555555" + extensions: + - ".c" + - ".cats" + - ".h" + - ".idc" + interpreters: + - tcc + tm_scope: source.c + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-csrc + language_id: 41 +C#: + type: programming + ace_mode: csharp + codemirror_mode: clike + codemirror_mime_type: text/x-csharp + tm_scope: source.cs + color: "#178600" + aliases: + - csharp + extensions: + - ".cs" + - ".cake" + - ".csx" + - ".linq" + language_id: 42 +C++: + type: programming + tm_scope: source.c++ + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-c++src + color: "#f34b7d" + aliases: + - cpp + extensions: + - ".cpp" + - ".c++" + - ".cc" + - ".cp" + - ".cxx" + - ".h" + - ".h++" + - ".hh" + - ".hpp" + - ".hxx" + - ".inc" + - ".inl" + - ".ino" + - ".ipp" + - ".re" + - ".tcc" + - ".tpp" + language_id: 43 +C-ObjDump: + type: data + extensions: + - ".c-objdump" + tm_scope: objdump.x86asm + ace_mode: assembly_x86 + language_id: 44 +C2hs Haskell: + type: programming + group: Haskell + aliases: + - c2hs + extensions: + - ".chs" + tm_scope: source.haskell + ace_mode: haskell + codemirror_mode: haskell + codemirror_mime_type: text/x-haskell + language_id: 45 +CLIPS: + type: programming + extensions: + - ".clp" + tm_scope: source.clips + ace_mode: text + language_id: 46 +CMake: + type: programming + extensions: + - ".cmake" + - ".cmake.in" + filenames: + - CMakeLists.txt + tm_scope: source.cmake + ace_mode: text + codemirror_mode: cmake + codemirror_mime_type: text/x-cmake + language_id: 47 +COBOL: + type: programming + extensions: + - ".cob" + - ".cbl" + - ".ccp" + - ".cobol" + - ".cpy" + tm_scope: source.cobol + ace_mode: cobol + codemirror_mode: cobol + codemirror_mime_type: text/x-cobol + language_id: 48 +COLLADA: + type: data + extensions: + - ".dae" + tm_scope: text.xml + ace_mode: xml + codemirror_mode: xml + codemirror_mime_type: text/xml + language_id: 49 +CSON: + type: data + color: "#244776" + tm_scope: source.coffee + ace_mode: coffee + codemirror_mode: coffeescript + codemirror_mime_type: text/x-coffeescript + extensions: + - ".cson" + language_id: 424 +CSS: + type: markup + tm_scope: source.css + ace_mode: css + codemirror_mode: css + codemirror_mime_type: text/css + color: "#563d7c" + extensions: + - ".css" + language_id: 50 +CSV: + type: data + ace_mode: text + tm_scope: none + extensions: + - ".csv" + language_id: 51 +CWeb: + type: programming + extensions: + - ".w" + tm_scope: none + ace_mode: text + language_id: 657332628 +Cabal Config: + type: data + aliases: + - Cabal + extensions: + - ".cabal" + filenames: + - cabal.config + - cabal.project + ace_mode: haskell + codemirror_mode: haskell + codemirror_mime_type: text/x-haskell + tm_scope: source.cabal + language_id: 677095381 +Cap'n Proto: + type: programming + tm_scope: source.capnp + extensions: + - ".capnp" + ace_mode: text + language_id: 52 +CartoCSS: + type: programming + aliases: + - Carto + extensions: + - ".mss" + ace_mode: text + tm_scope: source.css.mss + language_id: 53 +Ceylon: + type: programming + color: "#dfa535" + extensions: + - ".ceylon" + tm_scope: source.ceylon + ace_mode: text + language_id: 54 +Chapel: + type: programming + color: "#8dc63f" + aliases: + - chpl + extensions: + - ".chpl" + tm_scope: source.chapel + ace_mode: text + language_id: 55 +Charity: + type: programming + extensions: + - ".ch" + tm_scope: none + ace_mode: text + language_id: 56 +ChucK: + type: programming + extensions: + - ".ck" + tm_scope: source.java + ace_mode: java + codemirror_mode: clike + codemirror_mime_type: text/x-java + language_id: 57 +Cirru: + type: programming + color: "#ccccff" + tm_scope: source.cirru + ace_mode: cirru + extensions: + - ".cirru" + language_id: 58 +Clarion: + type: programming + color: "#db901e" + ace_mode: text + extensions: + - ".clw" + tm_scope: source.clarion + language_id: 59 +Classic ASP: + type: programming + color: "#6a40fd" + tm_scope: text.html.asp + aliases: + - asp + extensions: + - ".asp" + ace_mode: text + language_id: 8 +Clean: + type: programming + color: "#3F85AF" + extensions: + - ".icl" + - ".dcl" + tm_scope: source.clean + ace_mode: text + language_id: 60 +Click: + type: programming + color: "#E4E6F3" + extensions: + - ".click" + tm_scope: source.click + ace_mode: text + language_id: 61 +Clojure: + type: programming + tm_scope: source.clojure + ace_mode: clojure + codemirror_mode: clojure + codemirror_mime_type: text/x-clojure + color: "#db5855" + extensions: + - ".clj" + - ".boot" + - ".cl2" + - ".cljc" + - ".cljs" + - ".cljs.hl" + - ".cljscm" + - ".cljx" + - ".hic" + filenames: + - riemann.config + language_id: 62 +Closure Templates: + type: markup + group: HTML + ace_mode: soy_template + codemirror_mode: soy + codemirror_mime_type: text/x-soy + aliases: + - soy + extensions: + - ".soy" + tm_scope: text.html.soy + language_id: 357046146 +Cloud Firestore Security Rules: + type: data + ace_mode: less + codemirror_mode: css + codemirror_mime_type: text/css + tm_scope: source.firestore + filenames: + - firestore.rules + language_id: 407996372 +CoNLL-U: + type: data + extensions: + - ".conllu" + - ".conll" + tm_scope: text.conllu + ace_mode: text + aliases: + - CoNLL + - CoNLL-X + language_id: 421026389 +CodeQL: + type: programming + extensions: + - ".ql" + - ".qll" + tm_scope: source.ql + ace_mode: text + language_id: 424259634 + aliases: + - ql +CoffeeScript: + type: programming + tm_scope: source.coffee + ace_mode: coffee + codemirror_mode: coffeescript + codemirror_mime_type: text/x-coffeescript + color: "#244776" + aliases: + - coffee + - coffee-script + extensions: + - ".coffee" + - "._coffee" + - ".cake" + - ".cjsx" + - ".iced" + filenames: + - Cakefile + interpreters: + - coffee + language_id: 63 +ColdFusion: + type: programming + ace_mode: coldfusion + color: "#ed2cd6" + aliases: + - cfm + - cfml + - coldfusion html + extensions: + - ".cfm" + - ".cfml" + tm_scope: text.html.cfm + language_id: 64 +ColdFusion CFC: + type: programming + color: "#ed2cd6" + group: ColdFusion + ace_mode: coldfusion + aliases: + - cfc + extensions: + - ".cfc" + tm_scope: source.cfscript + language_id: 65 +Common Lisp: + type: programming + tm_scope: source.lisp + color: "#3fb68b" + aliases: + - lisp + extensions: + - ".lisp" + - ".asd" + - ".cl" + - ".l" + - ".lsp" + - ".ny" + - ".podsl" + - ".sexp" + interpreters: + - lisp + - sbcl + - ccl + - clisp + - ecl + ace_mode: lisp + codemirror_mode: commonlisp + codemirror_mime_type: text/x-common-lisp + language_id: 66 +Common Workflow Language: + aliases: + - cwl + type: programming + ace_mode: yaml + codemirror_mode: yaml + codemirror_mime_type: text/x-yaml + extensions: + - ".cwl" + interpreters: + - cwl-runner + color: "#B5314C" + tm_scope: source.cwl + language_id: 988547172 +Component Pascal: + type: programming + color: "#B0CE4E" + extensions: + - ".cp" + - ".cps" + tm_scope: source.pascal + aliases: + - delphi + - objectpascal + ace_mode: pascal + codemirror_mode: pascal + codemirror_mime_type: text/x-pascal + language_id: 67 +Cool: + type: programming + extensions: + - ".cl" + tm_scope: source.cool + ace_mode: text + language_id: 68 +Coq: + type: programming + extensions: + - ".coq" + - ".v" + tm_scope: source.coq + ace_mode: text + language_id: 69 +Cpp-ObjDump: + type: data + extensions: + - ".cppobjdump" + - ".c++-objdump" + - ".c++objdump" + - ".cpp-objdump" + - ".cxx-objdump" + tm_scope: objdump.x86asm + aliases: + - c++-objdump + ace_mode: assembly_x86 + language_id: 70 +Creole: + type: prose + wrap: true + extensions: + - ".creole" + tm_scope: text.html.creole + ace_mode: text + language_id: 71 +Crystal: + type: programming + color: "#000100" + extensions: + - ".cr" + ace_mode: ruby + codemirror_mode: crystal + codemirror_mime_type: text/x-crystal + tm_scope: source.crystal + interpreters: + - crystal + language_id: 72 +Csound: + type: programming + aliases: + - csound-orc + extensions: + - ".orc" + - ".udo" + tm_scope: source.csound + ace_mode: csound_orchestra + language_id: 73 +Csound Document: + type: programming + aliases: + - csound-csd + extensions: + - ".csd" + tm_scope: source.csound-document + ace_mode: csound_document + language_id: 74 +Csound Score: + type: programming + aliases: + - csound-sco + extensions: + - ".sco" + tm_scope: source.csound-score + ace_mode: csound_score + language_id: 75 +Cuda: + type: programming + extensions: + - ".cu" + - ".cuh" + tm_scope: source.cuda-c++ + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-c++src + color: "#3A4E3A" + language_id: 77 +Cycript: + type: programming + extensions: + - ".cy" + tm_scope: source.js + ace_mode: javascript + codemirror_mode: javascript + codemirror_mime_type: text/javascript + language_id: 78 +Cython: + type: programming + group: Python + extensions: + - ".pyx" + - ".pxd" + - ".pxi" + aliases: + - pyrex + tm_scope: source.cython + ace_mode: text + codemirror_mode: python + codemirror_mime_type: text/x-cython + language_id: 79 +D: + type: programming + color: "#ba595e" + extensions: + - ".d" + - ".di" + tm_scope: source.d + ace_mode: d + codemirror_mode: d + codemirror_mime_type: text/x-d + language_id: 80 +D-ObjDump: + type: data + extensions: + - ".d-objdump" + tm_scope: objdump.x86asm + ace_mode: assembly_x86 + language_id: 81 +DIGITAL Command Language: + type: programming + aliases: + - dcl + extensions: + - ".com" + tm_scope: none + ace_mode: text + language_id: 82 +DM: + type: programming + color: "#447265" + extensions: + - ".dm" + aliases: + - byond + tm_scope: source.dm + ace_mode: c_cpp + language_id: 83 +DNS Zone: + type: data + extensions: + - ".zone" + - ".arpa" + tm_scope: text.zone_file + ace_mode: text + language_id: 84 +DTrace: + type: programming + aliases: + - dtrace-script + extensions: + - ".d" + interpreters: + - dtrace + tm_scope: source.c + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-csrc + language_id: 85 +Dafny: + type: programming + color: "#FFEC25" + extensions: + - ".dfy" + interpreters: + - dafny + tm_scope: text.dfy.dafny + ace_mode: text + language_id: 969323346 +Darcs Patch: + type: data + aliases: + - dpatch + extensions: + - ".darcspatch" + - ".dpatch" + tm_scope: none + ace_mode: text + language_id: 86 +Dart: + type: programming + color: "#00B4AB" + extensions: + - ".dart" + interpreters: + - dart + tm_scope: source.dart + ace_mode: dart + codemirror_mode: dart + codemirror_mime_type: application/dart + language_id: 87 +DataWeave: + type: programming + color: "#003a52" + extensions: + - ".dwl" + ace_mode: text + tm_scope: source.data-weave + language_id: 974514097 +Dhall: + type: programming + color: "#dfafff" + extensions: + - ".dhall" + tm_scope: source.haskell + ace_mode: haskell + codemirror_mode: haskell + codemirror_mime_type: text/x-haskell + language_id: 793969321 +Diff: + type: data + extensions: + - ".diff" + - ".patch" + aliases: + - udiff + tm_scope: source.diff + ace_mode: diff + codemirror_mode: diff + codemirror_mime_type: text/x-diff + language_id: 88 +DirectX 3D File: + type: data + extensions: + - ".x" + ace_mode: text + tm_scope: none + language_id: 201049282 +Dockerfile: + type: programming + color: "#384d54" + tm_scope: source.dockerfile + extensions: + - ".dockerfile" + filenames: + - Dockerfile + ace_mode: dockerfile + codemirror_mode: dockerfile + codemirror_mime_type: text/x-dockerfile + language_id: 89 +Dogescript: + type: programming + color: "#cca760" + extensions: + - ".djs" + tm_scope: none + ace_mode: text + language_id: 90 +Dylan: + type: programming + color: "#6c616e" + extensions: + - ".dylan" + - ".dyl" + - ".intr" + - ".lid" + tm_scope: source.dylan + ace_mode: text + codemirror_mode: dylan + codemirror_mime_type: text/x-dylan + language_id: 91 +E: + type: programming + color: "#ccce35" + extensions: + - ".E" + interpreters: + - rune + tm_scope: none + ace_mode: text + language_id: 92 +EBNF: + type: data + extensions: + - ".ebnf" + tm_scope: source.ebnf + ace_mode: text + codemirror_mode: ebnf + codemirror_mime_type: text/x-ebnf + language_id: 430 +ECL: + type: programming + color: "#8a1267" + extensions: + - ".ecl" + - ".eclxml" + tm_scope: none + ace_mode: text + codemirror_mode: ecl + codemirror_mime_type: text/x-ecl + language_id: 93 +ECLiPSe: + type: programming + group: prolog + extensions: + - ".ecl" + tm_scope: source.prolog.eclipse + ace_mode: prolog + language_id: 94 +EJS: + type: markup + color: "#a91e50" + group: HTML + extensions: + - ".ejs" + - ".ect" + - ".jst" + tm_scope: text.html.js + ace_mode: ejs + language_id: 95 +EML: + type: data + extensions: + - ".eml" + - ".mbox" + tm_scope: text.eml.basic + ace_mode: text + language_id: 529653389 +EQ: + type: programming + color: "#a78649" + extensions: + - ".eq" + tm_scope: source.cs + ace_mode: csharp + codemirror_mode: clike + codemirror_mime_type: text/x-csharp + language_id: 96 +Eagle: + type: data + extensions: + - ".sch" + - ".brd" + tm_scope: text.xml + ace_mode: xml + codemirror_mode: xml + codemirror_mime_type: text/xml + language_id: 97 +Easybuild: + type: data + group: Python + ace_mode: python + codemirror_mode: python + codemirror_mime_type: text/x-python + tm_scope: source.python + extensions: + - ".eb" + language_id: 342840477 +Ecere Projects: + type: data + group: JavaScript + extensions: + - ".epj" + tm_scope: source.json + ace_mode: json + codemirror_mode: javascript + codemirror_mime_type: application/json + language_id: 98 +EditorConfig: + type: data + group: INI + filenames: + - ".editorconfig" + aliases: + - editor-config + ace_mode: ini + codemirror_mode: properties + codemirror_mime_type: text/x-properties + tm_scope: source.editorconfig + language_id: 96139566 +Edje Data Collection: + type: data + extensions: + - ".edc" + tm_scope: source.c++ + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-c++src + language_id: 342840478 +Eiffel: + type: programming + color: "#4d6977" + extensions: + - ".e" + tm_scope: source.eiffel + ace_mode: eiffel + codemirror_mode: eiffel + codemirror_mime_type: text/x-eiffel + language_id: 99 +Elixir: + type: programming + color: "#6e4a7e" + extensions: + - ".ex" + - ".exs" + tm_scope: source.elixir + ace_mode: elixir + filenames: + - mix.lock + interpreters: + - elixir + language_id: 100 +Elm: + type: programming + color: "#60B5CC" + extensions: + - ".elm" + tm_scope: source.elm + ace_mode: elm + codemirror_mode: elm + codemirror_mime_type: text/x-elm + language_id: 101 +Emacs Lisp: + type: programming + tm_scope: source.emacs.lisp + color: "#c065db" + aliases: + - elisp + - emacs + filenames: + - ".abbrev_defs" + - ".emacs" + - ".emacs.desktop" + - ".gnus" + - ".spacemacs" + - ".viper" + - Cask + - Project.ede + - _emacs + - abbrev_defs + extensions: + - ".el" + - ".emacs" + - ".emacs.desktop" + ace_mode: lisp + codemirror_mode: commonlisp + codemirror_mime_type: text/x-common-lisp + language_id: 102 +EmberScript: + type: programming + color: "#FFF4F3" + extensions: + - ".em" + - ".emberscript" + tm_scope: source.coffee + ace_mode: coffee + codemirror_mode: coffeescript + codemirror_mime_type: text/x-coffeescript + language_id: 103 +Erlang: + type: programming + color: "#B83998" + extensions: + - ".erl" + - ".app.src" + - ".es" + - ".escript" + - ".hrl" + - ".xrl" + - ".yrl" + filenames: + - Emakefile + - rebar.config + - rebar.config.lock + - rebar.lock + tm_scope: source.erlang + ace_mode: erlang + codemirror_mode: erlang + codemirror_mime_type: text/x-erlang + interpreters: + - escript + language_id: 104 +F#: + type: programming + color: "#b845fc" + aliases: + - fsharp + extensions: + - ".fs" + - ".fsi" + - ".fsx" + tm_scope: source.fsharp + ace_mode: text + codemirror_mode: mllike + codemirror_mime_type: text/x-fsharp + language_id: 105 +F*: + fs_name: Fstar + type: programming + color: "#572e30" + aliases: + - fstar + extensions: + - ".fst" + tm_scope: source.fstar + ace_mode: text + language_id: 336943375 +FIGlet Font: + type: data + aliases: + - FIGfont + extensions: + - ".flf" + tm_scope: source.figfont + ace_mode: text + language_id: 686129783 +FLUX: + type: programming + color: "#88ccff" + extensions: + - ".fx" + - ".flux" + tm_scope: none + ace_mode: text + language_id: 106 +Factor: + type: programming + color: "#636746" + extensions: + - ".factor" + filenames: + - ".factor-boot-rc" + - ".factor-rc" + tm_scope: source.factor + ace_mode: text + codemirror_mode: factor + codemirror_mime_type: text/x-factor + language_id: 108 +Fancy: + type: programming + color: "#7b9db4" + extensions: + - ".fy" + - ".fancypack" + filenames: + - Fakefile + tm_scope: source.fancy + ace_mode: text + language_id: 109 +Fantom: + type: programming + color: "#14253c" + extensions: + - ".fan" + tm_scope: source.fan + ace_mode: text + language_id: 110 +Faust: + type: programming + color: "#c37240" + extensions: + - ".dsp" + tm_scope: source.faust + ace_mode: text + language_id: 622529198 +Filebench WML: + type: programming + extensions: + - ".f" + tm_scope: none + ace_mode: text + language_id: 111 +Filterscript: + type: programming + group: RenderScript + extensions: + - ".fs" + tm_scope: none + ace_mode: text + language_id: 112 +Formatted: + type: data + extensions: + - ".for" + - ".eam.fs" + tm_scope: none + ace_mode: text + language_id: 113 +Forth: + type: programming + color: "#341708" + extensions: + - ".fth" + - ".4th" + - ".f" + - ".for" + - ".forth" + - ".fr" + - ".frt" + - ".fs" + tm_scope: source.forth + ace_mode: forth + codemirror_mode: forth + codemirror_mime_type: text/x-forth + language_id: 114 +Fortran: + group: Fortran + type: programming + color: "#4d41b1" + extensions: + - ".f" + - ".f77" + - ".for" + - ".fpp" + tm_scope: source.fortran + ace_mode: text + codemirror_mode: fortran + codemirror_mime_type: text/x-fortran + language_id: 107 +Fortran Free Form: + group: Fortran + type: programming + extensions: + - ".f90" + - ".f03" + - ".f08" + - ".f95" + tm_scope: source.fortran.modern + ace_mode: text + codemirror_mode: fortran + codemirror_mime_type: text/x-fortran + language_id: 761352333 +FreeMarker: + type: programming + color: "#0050b2" + aliases: + - ftl + extensions: + - ".ftl" + tm_scope: text.html.ftl + ace_mode: ftl + language_id: 115 +Frege: + type: programming + color: "#00cafe" + extensions: + - ".fr" + tm_scope: source.haskell + ace_mode: haskell + language_id: 116 +Futhark: + type: programming + color: "#5f021f" + extensions: + - ".fut" + tm_scope: source.futhark + ace_mode: text + language_id: 97358117 +G-code: + type: programming + color: "#D08CF2" + extensions: + - ".g" + - ".cnc" + - ".gco" + - ".gcode" + tm_scope: source.gcode + ace_mode: gcode + language_id: 117 +GAML: + type: programming + color: "#FFC766" + extensions: + - ".gaml" + tm_scope: none + ace_mode: text + language_id: 290345951 +GAMS: + type: programming + extensions: + - ".gms" + tm_scope: none + ace_mode: text + language_id: 118 +GAP: + type: programming + extensions: + - ".g" + - ".gap" + - ".gd" + - ".gi" + - ".tst" + tm_scope: source.gap + ace_mode: text + language_id: 119 +GCC Machine Description: + type: programming + extensions: + - ".md" + tm_scope: source.lisp + ace_mode: lisp + codemirror_mode: commonlisp + codemirror_mime_type: text/x-common-lisp + language_id: 121 +GDB: + type: programming + extensions: + - ".gdb" + - ".gdbinit" + tm_scope: source.gdb + ace_mode: text + language_id: 122 +GDScript: + type: programming + color: "#355570" + extensions: + - ".gd" + tm_scope: source.gdscript + ace_mode: text + language_id: 123 +GEDCOM: + type: data + ace_mode: text + extensions: + - ".ged" + tm_scope: source.gedcom + language_id: 459577965 +GLSL: + type: programming + extensions: + - ".glsl" + - ".fp" + - ".frag" + - ".frg" + - ".fs" + - ".fsh" + - ".fshader" + - ".geo" + - ".geom" + - ".glslf" + - ".glslv" + - ".gs" + - ".gshader" + - ".shader" + - ".tesc" + - ".tese" + - ".vert" + - ".vrx" + - ".vsh" + - ".vshader" + tm_scope: source.glsl + ace_mode: glsl + language_id: 124 +GN: + type: data + extensions: + - ".gn" + - ".gni" + interpreters: + - gn + filenames: + - ".gn" + tm_scope: source.gn + ace_mode: python + codemirror_mode: python + codemirror_mime_type: text/x-python + language_id: 302957008 +Game Maker Language: + type: programming + color: "#71b417" + extensions: + - ".gml" + tm_scope: source.c++ + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-c++src + language_id: 125 +Genie: + type: programming + ace_mode: text + extensions: + - ".gs" + color: "#fb855d" + tm_scope: none + language_id: 792408528 +Genshi: + type: programming + extensions: + - ".kid" + tm_scope: text.xml.genshi + aliases: + - xml+genshi + - xml+kid + ace_mode: xml + codemirror_mode: xml + codemirror_mime_type: text/xml + language_id: 126 +Gentoo Ebuild: + type: programming + group: Shell + extensions: + - ".ebuild" + tm_scope: source.shell + ace_mode: sh + codemirror_mode: shell + codemirror_mime_type: text/x-sh + language_id: 127 +Gentoo Eclass: + type: programming + group: Shell + extensions: + - ".eclass" + tm_scope: source.shell + ace_mode: sh + codemirror_mode: shell + codemirror_mime_type: text/x-sh + language_id: 128 +Gerber Image: + type: data + aliases: + - rs-274x + extensions: + - ".gbr" + - ".gbl" + - ".gbo" + - ".gbp" + - ".gbs" + - ".gko" + - ".gml" + - ".gpb" + - ".gpt" + - ".gtl" + - ".gto" + - ".gtp" + - ".gts" + - ".sol" + interpreters: + - gerbv + - gerbview + tm_scope: source.gerber + ace_mode: text + language_id: 404627610 +Gettext Catalog: + type: prose + searchable: false + aliases: + - pot + extensions: + - ".po" + - ".pot" + tm_scope: source.po + ace_mode: text + language_id: 129 +Gherkin: + type: programming + extensions: + - ".feature" + - ".story" + tm_scope: text.gherkin.feature + aliases: + - cucumber + ace_mode: text + color: "#5B2063" + language_id: 76 +Git Attributes: + type: data + group: INI + aliases: + - gitattributes + filenames: + - ".gitattributes" + tm_scope: source.gitattributes + ace_mode: gitignore + codemirror_mode: shell + codemirror_mime_type: text/x-sh + language_id: 956324166 +Git Config: + type: data + group: INI + aliases: + - gitconfig + - gitmodules + extensions: + - ".gitconfig" + filenames: + - ".gitconfig" + - ".gitmodules" + ace_mode: ini + codemirror_mode: properties + codemirror_mime_type: text/x-properties + tm_scope: source.gitconfig + language_id: 807968997 +Glyph: + type: programming + color: "#c1ac7f" + extensions: + - ".glf" + tm_scope: source.tcl + ace_mode: tcl + codemirror_mode: tcl + codemirror_mime_type: text/x-tcl + language_id: 130 +Glyph Bitmap Distribution Format: + type: data + extensions: + - ".bdf" + tm_scope: source.bdf + ace_mode: text + language_id: 997665271 +Gnuplot: + type: programming + color: "#f0a9f0" + extensions: + - ".gp" + - ".gnu" + - ".gnuplot" + - ".p" + - ".plot" + - ".plt" + interpreters: + - gnuplot + tm_scope: source.gnuplot + ace_mode: text + language_id: 131 +Go: + type: programming + color: "#00ADD8" + aliases: + - golang + extensions: + - ".go" + tm_scope: source.go + ace_mode: golang + codemirror_mode: go + codemirror_mime_type: text/x-go + language_id: 132 +Golo: + type: programming + color: "#88562A" + extensions: + - ".golo" + tm_scope: source.golo + ace_mode: text + language_id: 133 +Gosu: + type: programming + color: "#82937f" + extensions: + - ".gs" + - ".gst" + - ".gsx" + - ".vark" + tm_scope: source.gosu.2 + ace_mode: text + language_id: 134 +Grace: + type: programming + extensions: + - ".grace" + tm_scope: source.grace + ace_mode: text + language_id: 135 +Gradle: + type: data + extensions: + - ".gradle" + tm_scope: source.groovy.gradle + ace_mode: text + language_id: 136 +Grammatical Framework: + type: programming + aliases: + - gf + extensions: + - ".gf" + color: "#ff0000" + tm_scope: source.gf + ace_mode: haskell + codemirror_mode: haskell + codemirror_mime_type: text/x-haskell + language_id: 137 +Graph Modeling Language: + type: data + extensions: + - ".gml" + tm_scope: none + ace_mode: text + language_id: 138 +GraphQL: + type: data + color: "#e10098" + extensions: + - ".graphql" + - ".gql" + - ".graphqls" + tm_scope: source.graphql + ace_mode: text + language_id: 139 +Graphviz (DOT): + type: data + tm_scope: source.dot + extensions: + - ".dot" + - ".gv" + ace_mode: text + language_id: 140 +Groovy: + type: programming + tm_scope: source.groovy + ace_mode: groovy + codemirror_mode: groovy + codemirror_mime_type: text/x-groovy + color: "#e69f56" + extensions: + - ".groovy" + - ".grt" + - ".gtpl" + - ".gvy" + interpreters: + - groovy + filenames: + - Jenkinsfile + language_id: 142 +Groovy Server Pages: + type: programming + group: Groovy + aliases: + - gsp + - java server page + extensions: + - ".gsp" + tm_scope: text.html.jsp + ace_mode: jsp + codemirror_mode: htmlembedded + codemirror_mime_type: application/x-jsp + language_id: 143 +HAProxy: + type: data + extensions: + - ".cfg" + filenames: + - haproxy.cfg + tm_scope: source.haproxy-config + ace_mode: text + language_id: 366607477 +HCL: + type: programming + extensions: + - ".hcl" + - ".nomad" + - ".tf" + - ".tfvars" + - ".workflow" + aliases: + - terraform + ace_mode: ruby + codemirror_mode: ruby + codemirror_mime_type: text/x-ruby + tm_scope: source.terraform + language_id: 144 +HLSL: + type: programming + extensions: + - ".hlsl" + - ".cginc" + - ".fx" + - ".fxh" + - ".hlsli" + ace_mode: text + tm_scope: source.hlsl + language_id: 145 +HTML: + type: markup + tm_scope: text.html.basic + ace_mode: html + codemirror_mode: htmlmixed + codemirror_mime_type: text/html + color: "#e34c26" + aliases: + - xhtml + extensions: + - ".html" + - ".htm" + - ".html.hl" + - ".inc" + - ".st" + - ".xht" + - ".xhtml" + language_id: 146 +HTML+Django: + type: markup + tm_scope: text.html.django + group: HTML + extensions: + - ".jinja" + - ".j2" + - ".jinja2" + - ".njk" + aliases: + - django + - html+django/jinja + - html+jinja + - htmldjango + - njk + - nunjucks + ace_mode: django + codemirror_mode: django + codemirror_mime_type: text/x-django + language_id: 147 +HTML+ECR: + type: markup + tm_scope: text.html.ecr + group: HTML + aliases: + - ecr + extensions: + - ".ecr" + ace_mode: text + codemirror_mode: htmlmixed + codemirror_mime_type: text/html + language_id: 148 +HTML+EEX: + type: markup + tm_scope: text.html.elixir + group: HTML + aliases: + - eex + extensions: + - ".eex" + ace_mode: text + codemirror_mode: htmlmixed + codemirror_mime_type: text/html + language_id: 149 +HTML+ERB: + type: markup + tm_scope: text.html.erb + group: HTML + aliases: + - erb + - rhtml + - html+ruby + extensions: + - ".erb" + - ".erb.deface" + - ".rhtml" + ace_mode: text + codemirror_mode: htmlembedded + codemirror_mime_type: application/x-erb + language_id: 150 +HTML+PHP: + type: markup + tm_scope: text.html.php + group: HTML + extensions: + - ".phtml" + ace_mode: php + codemirror_mode: php + codemirror_mime_type: application/x-httpd-php + language_id: 151 +HTML+Razor: + type: markup + tm_scope: text.html.cshtml + group: HTML + aliases: + - razor + extensions: + - ".cshtml" + - ".razor" + ace_mode: razor + codemirror_mode: htmlmixed + codemirror_mime_type: text/html + language_id: 479039817 +HTTP: + type: data + extensions: + - ".http" + tm_scope: source.httpspec + ace_mode: text + codemirror_mode: http + codemirror_mime_type: message/http + language_id: 152 +HXML: + type: data + ace_mode: text + extensions: + - ".hxml" + tm_scope: source.hxml + language_id: 786683730 +Hack: + type: programming + ace_mode: php + codemirror_mode: php + codemirror_mime_type: application/x-httpd-php + extensions: + - ".hack" + - ".hh" + - ".hhi" + - ".php" + tm_scope: source.hack + color: "#878787" + language_id: 153 +Haml: + type: markup + color: "#ece2a9" + extensions: + - ".haml" + - ".haml.deface" + tm_scope: text.haml + ace_mode: haml + codemirror_mode: haml + codemirror_mime_type: text/x-haml + language_id: 154 +Handlebars: + type: markup + color: "#f7931e" + aliases: + - hbs + - htmlbars + extensions: + - ".handlebars" + - ".hbs" + tm_scope: text.html.handlebars + ace_mode: handlebars + language_id: 155 +Harbour: + type: programming + color: "#0e60e3" + extensions: + - ".hb" + tm_scope: source.harbour + ace_mode: text + language_id: 156 +Haskell: + type: programming + color: "#5e5086" + extensions: + - ".hs" + - ".hs-boot" + - ".hsc" + interpreters: + - runghc + - runhaskell + - runhugs + tm_scope: source.haskell + ace_mode: haskell + codemirror_mode: haskell + codemirror_mime_type: text/x-haskell + language_id: 157 +Haxe: + type: programming + ace_mode: haxe + codemirror_mode: haxe + codemirror_mime_type: text/x-haxe + color: "#df7900" + extensions: + - ".hx" + - ".hxsl" + tm_scope: source.hx + language_id: 158 +HiveQL: + type: programming + extensions: + - ".q" + - ".hql" + color: "#dce200" + tm_scope: source.hql + ace_mode: sql + language_id: 931814087 +HolyC: + type: programming + color: "#ffefaf" + extensions: + - ".hc" + tm_scope: source.hc + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-csrc + language_id: 928121743 +Hy: + type: programming + ace_mode: text + color: "#7790B2" + extensions: + - ".hy" + interpreters: + - hy + aliases: + - hylang + tm_scope: source.hy + language_id: 159 +HyPhy: + type: programming + ace_mode: text + extensions: + - ".bf" + tm_scope: none + language_id: 160 +IDL: + type: programming + color: "#a3522f" + extensions: + - ".pro" + - ".dlm" + tm_scope: source.idl + ace_mode: text + codemirror_mode: idl + codemirror_mime_type: text/x-idl + language_id: 161 +IGOR Pro: + type: programming + color: "#0000cc" + extensions: + - ".ipf" + aliases: + - igor + - igorpro + tm_scope: source.igor + ace_mode: text + language_id: 162 +INI: + type: data + extensions: + - ".ini" + - ".cfg" + - ".dof" + - ".lektorproject" + - ".prefs" + - ".pro" + - ".properties" + filenames: + - buildozer.spec + tm_scope: source.ini + aliases: + - dosini + ace_mode: ini + codemirror_mode: properties + codemirror_mime_type: text/x-properties + language_id: 163 +IRC log: + type: data + aliases: + - irc + - irc logs + extensions: + - ".irclog" + - ".weechatlog" + tm_scope: none + ace_mode: text + codemirror_mode: mirc + codemirror_mime_type: text/mirc + language_id: 164 +Idris: + type: programming + color: "#b30000" + extensions: + - ".idr" + - ".lidr" + ace_mode: text + tm_scope: source.idris + language_id: 165 +Ignore List: + type: data + group: INI + aliases: + - ignore + - gitignore + - git-ignore + extensions: + - ".gitignore" + filenames: + - ".atomignore" + - ".babelignore" + - ".bzrignore" + - ".coffeelintignore" + - ".cvsignore" + - ".dockerignore" + - ".eslintignore" + - ".gitignore" + - ".nodemonignore" + - ".npmignore" + - ".prettierignore" + - ".stylelintignore" + - ".vscodeignore" + - gitignore-global + - gitignore_global + ace_mode: gitignore + tm_scope: source.gitignore + codemirror_mode: shell + codemirror_mime_type: text/x-sh + language_id: 74444240 +Inform 7: + type: programming + wrap: true + extensions: + - ".ni" + - ".i7x" + tm_scope: source.inform7 + aliases: + - i7 + - inform7 + ace_mode: text + language_id: 166 +Inno Setup: + type: programming + extensions: + - ".iss" + - ".isl" + tm_scope: source.inno + ace_mode: text + language_id: 167 +Io: + type: programming + color: "#a9188d" + extensions: + - ".io" + interpreters: + - io + tm_scope: source.io + ace_mode: io + language_id: 168 +Ioke: + type: programming + color: "#078193" + extensions: + - ".ik" + interpreters: + - ioke + tm_scope: source.ioke + ace_mode: text + language_id: 169 +Isabelle: + type: programming + color: "#FEFE00" + extensions: + - ".thy" + tm_scope: source.isabelle.theory + ace_mode: text + language_id: 170 +Isabelle ROOT: + type: programming + group: Isabelle + filenames: + - ROOT + tm_scope: source.isabelle.root + ace_mode: text + language_id: 171 +J: + type: programming + color: "#9EEDFF" + extensions: + - ".ijs" + interpreters: + - jconsole + tm_scope: source.j + ace_mode: text + language_id: 172 +JFlex: + type: programming + color: "#DBCA00" + group: Lex + extensions: + - ".flex" + - ".jflex" + tm_scope: source.jflex + ace_mode: text + language_id: 173 +JSON: + type: data + tm_scope: source.json + ace_mode: json + codemirror_mode: javascript + codemirror_mime_type: application/json + searchable: false + extensions: + - ".json" + - ".avsc" + - ".geojson" + - ".gltf" + - ".har" + - ".ice" + - ".JSON-tmLanguage" + - ".jsonl" + - ".mcmeta" + - ".tfstate" + - ".tfstate.backup" + - ".topojson" + - ".webapp" + - ".webmanifest" + - ".yy" + - ".yyp" + filenames: + - ".arcconfig" + - ".htmlhintrc" + - ".tern-config" + - ".tern-project" + - ".watchmanconfig" + - composer.lock + - mcmod.info + language_id: 174 +JSON with Comments: + type: data + group: JSON + tm_scope: source.js + ace_mode: javascript + codemirror_mode: javascript + codemirror_mime_type: text/javascript + aliases: + - jsonc + extensions: + - ".jsonc" + - ".sublime-build" + - ".sublime-commands" + - ".sublime-completions" + - ".sublime-keymap" + - ".sublime-macro" + - ".sublime-menu" + - ".sublime-mousemap" + - ".sublime-project" + - ".sublime-settings" + - ".sublime-theme" + - ".sublime-workspace" + - ".sublime_metrics" + - ".sublime_session" + filenames: + - ".babelrc" + - ".eslintrc.json" + - ".jscsrc" + - ".jshintrc" + - ".jslintrc" + - jsconfig.json + - language-configuration.json + - tsconfig.json + - tslint.json + language_id: 423 +JSON5: + type: data + extensions: + - ".json5" + tm_scope: source.js + ace_mode: javascript + codemirror_mode: javascript + codemirror_mime_type: application/json + language_id: 175 +JSONLD: + type: data + extensions: + - ".jsonld" + tm_scope: source.js + ace_mode: javascript + codemirror_mode: javascript + codemirror_mime_type: application/json + language_id: 176 +JSONiq: + color: "#40d47e" + type: programming + ace_mode: jsoniq + codemirror_mode: javascript + codemirror_mime_type: application/json + extensions: + - ".jq" + tm_scope: source.jq + language_id: 177 +JSX: + type: programming + group: JavaScript + extensions: + - ".jsx" + tm_scope: source.js.jsx + ace_mode: javascript + codemirror_mode: jsx + codemirror_mime_type: text/jsx + language_id: 178 +Jasmin: + type: programming + ace_mode: java + extensions: + - ".j" + tm_scope: source.jasmin + language_id: 180 +Java: + type: programming + tm_scope: source.java + ace_mode: java + codemirror_mode: clike + codemirror_mime_type: text/x-java + color: "#b07219" + extensions: + - ".java" + language_id: 181 +Java Properties: + type: data + extensions: + - ".properties" + tm_scope: source.java-properties + ace_mode: properties + codemirror_mode: properties + codemirror_mime_type: text/x-properties + language_id: 519377561 +Java Server Pages: + type: programming + group: Java + aliases: + - jsp + extensions: + - ".jsp" + tm_scope: text.html.jsp + ace_mode: jsp + codemirror_mode: htmlembedded + codemirror_mime_type: application/x-jsp + language_id: 182 +JavaScript: + type: programming + tm_scope: source.js + ace_mode: javascript + codemirror_mode: javascript + codemirror_mime_type: text/javascript + color: "#f1e05a" + aliases: + - js + - node + - nodejs + extensions: + - ".js" + - "._js" + - ".bones" + - ".cjs" + - ".es" + - ".es6" + - ".frag" + - ".gs" + - ".jake" + - ".jsb" + - ".jscad" + - ".jsfl" + - ".jsm" + - ".jss" + - ".mjs" + - ".njs" + - ".pac" + - ".sjs" + - ".ssjs" + - ".xsjs" + - ".xsjslib" + filenames: + - Jakefile + interpreters: + - chakra + - d8 + - gjs + - js + - node + - nodejs + - qjs + - rhino + - v8 + - v8-shell + language_id: 183 +JavaScript+ERB: + type: programming + tm_scope: source.js + group: JavaScript + extensions: + - ".js.erb" + ace_mode: javascript + codemirror_mode: javascript + codemirror_mime_type: application/javascript + language_id: 914318960 +Jison: + type: programming + group: Yacc + extensions: + - ".jison" + tm_scope: source.jison + ace_mode: text + language_id: 284531423 +Jison Lex: + type: programming + group: Lex + extensions: + - ".jisonlex" + tm_scope: source.jisonlex + ace_mode: text + language_id: 406395330 +Jolie: + type: programming + extensions: + - ".ol" + - ".iol" + interpreters: + - jolie + color: "#843179" + ace_mode: text + tm_scope: source.jolie + language_id: 998078858 +Jsonnet: + color: "#0064bd" + type: programming + ace_mode: text + extensions: + - ".jsonnet" + - ".libsonnet" + tm_scope: source.jsonnet + language_id: 664885656 +Julia: + type: programming + extensions: + - ".jl" + interpreters: + - julia + color: "#a270ba" + tm_scope: source.julia + ace_mode: julia + codemirror_mode: julia + codemirror_mime_type: text/x-julia + language_id: 184 +Jupyter Notebook: + type: markup + ace_mode: json + codemirror_mode: javascript + codemirror_mime_type: application/json + tm_scope: source.json + color: "#DA5B0B" + extensions: + - ".ipynb" + filenames: + - Notebook + aliases: + - IPython Notebook + language_id: 185 +KRL: + type: programming + color: "#28430A" + extensions: + - ".krl" + tm_scope: none + ace_mode: text + language_id: 186 +Kaitai Struct: + type: programming + aliases: + - ksy + ace_mode: yaml + codemirror_mode: yaml + codemirror_mime_type: text/x-yaml + color: "#773b37" + extensions: + - ".ksy" + tm_scope: source.yaml + language_id: 818804755 +KiCad Layout: + type: data + aliases: + - pcbnew + extensions: + - ".kicad_pcb" + - ".kicad_mod" + - ".kicad_wks" + filenames: + - fp-lib-table + tm_scope: source.pcb.sexp + ace_mode: lisp + codemirror_mode: commonlisp + codemirror_mime_type: text/x-common-lisp + language_id: 187 +KiCad Legacy Layout: + type: data + extensions: + - ".brd" + tm_scope: source.pcb.board + ace_mode: text + language_id: 140848857 +KiCad Schematic: + type: data + aliases: + - eeschema schematic + extensions: + - ".sch" + tm_scope: source.pcb.schematic + ace_mode: text + language_id: 622447435 +Kit: + type: markup + ace_mode: html + codemirror_mode: htmlmixed + codemirror_mime_type: text/html + extensions: + - ".kit" + tm_scope: text.html.basic + language_id: 188 +Kotlin: + type: programming + color: "#F18E33" + extensions: + - ".kt" + - ".ktm" + - ".kts" + tm_scope: source.kotlin + ace_mode: text + codemirror_mode: clike + codemirror_mime_type: text/x-kotlin + language_id: 189 +LFE: + type: programming + color: "#4C3023" + extensions: + - ".lfe" + tm_scope: source.lisp + ace_mode: lisp + codemirror_mode: commonlisp + codemirror_mime_type: text/x-common-lisp + language_id: 190 +LLVM: + type: programming + extensions: + - ".ll" + tm_scope: source.llvm + ace_mode: text + color: "#185619" + language_id: 191 +LOLCODE: + type: programming + extensions: + - ".lol" + color: "#cc9900" + tm_scope: none + ace_mode: text + language_id: 192 +LSL: + type: programming + tm_scope: source.lsl + ace_mode: lsl + extensions: + - ".lsl" + - ".lslp" + interpreters: + - lsl + color: "#3d9970" + language_id: 193 +LTspice Symbol: + type: data + extensions: + - ".asy" + tm_scope: source.ltspice.symbol + ace_mode: text + codemirror_mode: spreadsheet + codemirror_mime_type: text/x-spreadsheet + language_id: 1013566805 +LabVIEW: + type: programming + extensions: + - ".lvproj" + tm_scope: text.xml + ace_mode: xml + codemirror_mode: xml + codemirror_mime_type: text/xml + language_id: 194 +Lark: + type: data + group: EBNF + color: "#0b130f" + extensions: + - ".lark" + tm_scope: source.lark + ace_mode: text + codemirror_mode: ebnf + codemirror_mime_type: text/x-ebnf + language_id: 758480799 +Lasso: + type: programming + color: "#999999" + extensions: + - ".lasso" + - ".las" + - ".lasso8" + - ".lasso9" + tm_scope: file.lasso + aliases: + - lassoscript + ace_mode: text + language_id: 195 +Latte: + type: markup + color: "#f2a542" + extensions: + - ".latte" + tm_scope: text.html.smarty + ace_mode: smarty + codemirror_mode: smarty + codemirror_mime_type: text/x-smarty + language_id: 196 +Lean: + type: programming + extensions: + - ".lean" + - ".hlean" + tm_scope: source.lean + ace_mode: text + language_id: 197 +Less: + type: markup + color: "#1d365d" + extensions: + - ".less" + tm_scope: source.css.less + ace_mode: less + codemirror_mode: css + codemirror_mime_type: text/css + language_id: 198 +Lex: + type: programming + color: "#DBCA00" + aliases: + - flex + extensions: + - ".l" + - ".lex" + filenames: + - Lexer.x + - lexer.x + tm_scope: source.lex + ace_mode: text + language_id: 199 +LilyPond: + type: programming + extensions: + - ".ly" + - ".ily" + tm_scope: source.lilypond + ace_mode: text + language_id: 200 +Limbo: + type: programming + extensions: + - ".b" + - ".m" + tm_scope: none + ace_mode: text + language_id: 201 +Linker Script: + type: data + extensions: + - ".ld" + - ".lds" + - ".x" + filenames: + - ld.script + tm_scope: none + ace_mode: text + language_id: 202 +Linux Kernel Module: + type: data + extensions: + - ".mod" + tm_scope: none + ace_mode: text + language_id: 203 +Liquid: + type: markup + extensions: + - ".liquid" + tm_scope: text.html.liquid + ace_mode: liquid + language_id: 204 +Literate Agda: + type: programming + group: Agda + extensions: + - ".lagda" + tm_scope: none + ace_mode: text + language_id: 205 +Literate CoffeeScript: + type: programming + tm_scope: source.litcoffee + group: CoffeeScript + ace_mode: text + wrap: true + aliases: + - litcoffee + extensions: + - ".litcoffee" + - ".coffee.md" + language_id: 206 +Literate Haskell: + type: programming + group: Haskell + aliases: + - lhaskell + - lhs + extensions: + - ".lhs" + tm_scope: text.tex.latex.haskell + ace_mode: text + codemirror_mode: haskell-literate + codemirror_mime_type: text/x-literate-haskell + language_id: 207 +LiveScript: + type: programming + color: "#499886" + aliases: + - live-script + - ls + extensions: + - ".ls" + - "._ls" + filenames: + - Slakefile + tm_scope: source.livescript + ace_mode: livescript + codemirror_mode: livescript + codemirror_mime_type: text/x-livescript + language_id: 208 +Logos: + type: programming + extensions: + - ".xm" + - ".x" + - ".xi" + ace_mode: text + tm_scope: source.logos + language_id: 209 +Logtalk: + type: programming + extensions: + - ".lgt" + - ".logtalk" + tm_scope: source.logtalk + ace_mode: text + language_id: 210 +LookML: + type: programming + ace_mode: yaml + codemirror_mode: yaml + codemirror_mime_type: text/x-yaml + color: "#652B81" + extensions: + - ".lookml" + - ".model.lkml" + - ".view.lkml" + tm_scope: source.yaml + language_id: 211 +LoomScript: + type: programming + extensions: + - ".ls" + tm_scope: source.loomscript + ace_mode: text + language_id: 212 +Lua: + type: programming + tm_scope: source.lua + ace_mode: lua + codemirror_mode: lua + codemirror_mime_type: text/x-lua + color: "#000080" + extensions: + - ".lua" + - ".fcgi" + - ".nse" + - ".p8" + - ".pd_lua" + - ".rbxs" + - ".rockspec" + - ".wlua" + filenames: + - ".luacheckrc" + interpreters: + - lua + language_id: 213 +M: + type: programming + aliases: + - mumps + extensions: + - ".mumps" + - ".m" + ace_mode: text + codemirror_mode: mumps + codemirror_mime_type: text/x-mumps + language_id: 214 + tm_scope: none +M4: + type: programming + extensions: + - ".m4" + tm_scope: source.m4 + ace_mode: text + language_id: 215 +M4Sugar: + type: programming + group: M4 + aliases: + - autoconf + extensions: + - ".m4" + filenames: + - configure.ac + tm_scope: source.m4 + ace_mode: text + language_id: 216 +MATLAB: + type: programming + color: "#e16737" + aliases: + - octave + extensions: + - ".matlab" + - ".m" + tm_scope: source.matlab + ace_mode: matlab + codemirror_mode: octave + codemirror_mime_type: text/x-octave + language_id: 225 +MAXScript: + type: programming + color: "#00a6a6" + extensions: + - ".ms" + - ".mcr" + tm_scope: source.maxscript + ace_mode: text + language_id: 217 +MLIR: + type: programming + color: "#5EC8DB" + extensions: + - ".mlir" + tm_scope: source.mlir + ace_mode: text + language_id: 448253929 +MQL4: + type: programming + color: "#62A8D6" + extensions: + - ".mq4" + - ".mqh" + tm_scope: source.mql5 + ace_mode: c_cpp + language_id: 426 +MQL5: + type: programming + color: "#4A76B8" + extensions: + - ".mq5" + - ".mqh" + tm_scope: source.mql5 + ace_mode: c_cpp + language_id: 427 +MTML: + type: markup + color: "#b7e1f4" + extensions: + - ".mtml" + tm_scope: text.html.basic + ace_mode: html + codemirror_mode: htmlmixed + codemirror_mime_type: text/html + language_id: 218 +MUF: + type: programming + group: Forth + extensions: + - ".muf" + - ".m" + tm_scope: none + ace_mode: forth + codemirror_mode: forth + codemirror_mime_type: text/x-forth + language_id: 219 +Macaulay2: + type: programming + extensions: + - ".m2" + aliases: + - m2 + interpreters: + - M2 + ace_mode: text + tm_scope: source.m2 + color: "#d8ffff" + language_id: 34167825 +Makefile: + type: programming + color: "#427819" + aliases: + - bsdmake + - make + - mf + extensions: + - ".mak" + - ".d" + - ".make" + - ".mk" + - ".mkfile" + filenames: + - BSDmakefile + - GNUmakefile + - Kbuild + - Makefile + - Makefile.am + - Makefile.boot + - Makefile.frag + - Makefile.in + - Makefile.inc + - Makefile.wat + - makefile + - makefile.sco + - mkfile + interpreters: + - make + tm_scope: source.makefile + ace_mode: makefile + codemirror_mode: cmake + codemirror_mime_type: text/x-cmake + language_id: 220 +Mako: + type: programming + extensions: + - ".mako" + - ".mao" + tm_scope: text.html.mako + ace_mode: text + language_id: 221 +Markdown: + type: prose + color: "#083fa1" + aliases: + - pandoc + ace_mode: markdown + codemirror_mode: gfm + codemirror_mime_type: text/x-gfm + wrap: true + extensions: + - ".md" + - ".markdown" + - ".mdown" + - ".mdwn" + - ".mdx" + - ".mkd" + - ".mkdn" + - ".mkdown" + - ".ronn" + - ".workbook" + filenames: + - contents.lr + tm_scope: source.gfm + language_id: 222 +Marko: + type: markup + color: "#42bff2" + tm_scope: text.marko + extensions: + - ".marko" + aliases: + - markojs + ace_mode: text + codemirror_mode: htmlmixed + codemirror_mime_type: text/html + language_id: 932782397 +Mask: + type: markup + color: "#f97732" + ace_mode: mask + extensions: + - ".mask" + tm_scope: source.mask + language_id: 223 +Mathematica: + type: programming + extensions: + - ".mathematica" + - ".cdf" + - ".m" + - ".ma" + - ".mt" + - ".nb" + - ".nbp" + - ".wl" + - ".wlt" + aliases: + - mma + tm_scope: source.mathematica + ace_mode: text + codemirror_mode: mathematica + codemirror_mime_type: text/x-mathematica + language_id: 224 +Maven POM: + type: data + tm_scope: text.xml.pom + filenames: + - pom.xml + ace_mode: xml + codemirror_mode: xml + codemirror_mime_type: text/xml + language_id: 226 +Max: + type: programming + color: "#c4a79c" + aliases: + - max/msp + - maxmsp + extensions: + - ".maxpat" + - ".maxhelp" + - ".maxproj" + - ".mxt" + - ".pat" + tm_scope: source.json + ace_mode: json + codemirror_mode: javascript + codemirror_mime_type: application/json + language_id: 227 +MediaWiki: + type: prose + wrap: true + extensions: + - ".mediawiki" + - ".wiki" + tm_scope: text.html.mediawiki + ace_mode: text + language_id: 228 +Mercury: + type: programming + color: "#ff2b2b" + ace_mode: prolog + interpreters: + - mmi + extensions: + - ".m" + - ".moo" + tm_scope: source.mercury + language_id: 229 +Meson: + type: programming + color: "#007800" + filenames: + - meson.build + - meson_options.txt + tm_scope: source.meson + ace_mode: text + language_id: 799141244 +Metal: + type: programming + color: "#8f14e9" + extensions: + - ".metal" + tm_scope: source.c++ + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-c++src + language_id: 230 +Microsoft Developer Studio Project: + type: data + extensions: + - ".dsp" + tm_scope: none + ace_mode: text + language_id: 800983837 +MiniD: + type: programming + searchable: false + extensions: + - ".minid" + tm_scope: none + ace_mode: text + language_id: 231 +Mirah: + type: programming + color: "#c7a938" + extensions: + - ".druby" + - ".duby" + - ".mirah" + tm_scope: source.ruby + ace_mode: ruby + codemirror_mode: ruby + codemirror_mime_type: text/x-ruby + language_id: 232 +Modelica: + type: programming + extensions: + - ".mo" + tm_scope: source.modelica + ace_mode: text + codemirror_mode: modelica + codemirror_mime_type: text/x-modelica + language_id: 233 +Modula-2: + type: programming + extensions: + - ".mod" + tm_scope: source.modula2 + ace_mode: text + language_id: 234 +Modula-3: + type: programming + extensions: + - ".i3" + - ".ig" + - ".m3" + - ".mg" + color: "#223388" + ace_mode: text + tm_scope: source.modula-3 + language_id: 564743864 +Module Management System: + type: programming + extensions: + - ".mms" + - ".mmk" + filenames: + - descrip.mmk + - descrip.mms + tm_scope: none + ace_mode: text + language_id: 235 +Monkey: + type: programming + extensions: + - ".monkey" + - ".monkey2" + ace_mode: text + tm_scope: source.monkey + language_id: 236 +Moocode: + type: programming + extensions: + - ".moo" + tm_scope: none + ace_mode: text + language_id: 237 +MoonScript: + type: programming + extensions: + - ".moon" + interpreters: + - moon + tm_scope: source.moonscript + ace_mode: text + language_id: 238 +Motorola 68K Assembly: + type: programming + group: Assembly + aliases: + - m68k + extensions: + - ".asm" + - ".i" + - ".inc" + - ".s" + - ".x68" + tm_scope: source.m68k + ace_mode: assembly_x86 + language_id: 477582706 +Muse: + type: prose + extensions: + - ".muse" + tm_scope: text.muse + ace_mode: text + wrap: true + language_id: 474864066 + aliases: + - amusewiki + - emacs muse +Mustache: + type: markup + group: HTML + extensions: + - ".mustache" + tm_scope: text.html.smarty + ace_mode: smarty + codemirror_mode: smarty + codemirror_mime_type: text/x-smarty + language_id: 638334590 +Myghty: + type: programming + extensions: + - ".myt" + tm_scope: none + ace_mode: text + language_id: 239 +NASL: + type: programming + extensions: + - ".nasl" + - ".inc" + tm_scope: source.nasl + ace_mode: text + language_id: 171666519 +NCL: + type: programming + color: "#28431f" + extensions: + - ".ncl" + tm_scope: source.ncl + ace_mode: text + language_id: 240 +NEON: + type: data + extensions: + - ".neon" + tm_scope: source.neon + ace_mode: text + aliases: + - nette object notation + - ne-on + language_id: 481192983 +NL: + type: data + extensions: + - ".nl" + tm_scope: none + ace_mode: text + language_id: 241 +NPM Config: + type: data + group: INI + aliases: + - npmrc + filenames: + - ".npmrc" + tm_scope: source.ini.npmrc + ace_mode: text + language_id: 685022663 +NSIS: + type: programming + extensions: + - ".nsi" + - ".nsh" + tm_scope: source.nsis + ace_mode: text + codemirror_mode: nsis + codemirror_mime_type: text/x-nsis + language_id: 242 +Nearley: + type: programming + ace_mode: text + color: "#990000" + extensions: + - ".ne" + - ".nearley" + tm_scope: source.ne + language_id: 521429430 +Nemerle: + type: programming + color: "#3d3c6e" + extensions: + - ".n" + tm_scope: source.nemerle + ace_mode: text + language_id: 243 +NetLinx: + type: programming + color: "#0aa0ff" + extensions: + - ".axs" + - ".axi" + tm_scope: source.netlinx + ace_mode: text + language_id: 244 +NetLinx+ERB: + type: programming + color: "#747faa" + extensions: + - ".axs.erb" + - ".axi.erb" + tm_scope: source.netlinx.erb + ace_mode: text + language_id: 245 +NetLogo: + type: programming + color: "#ff6375" + extensions: + - ".nlogo" + tm_scope: source.lisp + ace_mode: lisp + codemirror_mode: commonlisp + codemirror_mime_type: text/x-common-lisp + language_id: 246 +NewLisp: + type: programming + color: "#87AED7" + extensions: + - ".nl" + - ".lisp" + - ".lsp" + interpreters: + - newlisp + tm_scope: source.lisp + ace_mode: lisp + codemirror_mode: commonlisp + codemirror_mime_type: text/x-common-lisp + language_id: 247 +Nextflow: + type: programming + ace_mode: groovy + tm_scope: source.nextflow + color: "#3ac486" + extensions: + - ".nf" + filenames: + - nextflow.config + interpreters: + - nextflow + language_id: 506780613 +Nginx: + type: data + extensions: + - ".nginx" + - ".nginxconf" + - ".vhost" + filenames: + - nginx.conf + tm_scope: source.nginx + aliases: + - nginx configuration file + ace_mode: text + codemirror_mode: nginx + codemirror_mime_type: text/x-nginx-conf + language_id: 248 +Nim: + type: programming + color: "#ffc200" + extensions: + - ".nim" + - ".nim.cfg" + - ".nimble" + - ".nimrod" + - ".nims" + filenames: + - nim.cfg + ace_mode: text + tm_scope: source.nim + language_id: 249 +Ninja: + type: data + tm_scope: source.ninja + extensions: + - ".ninja" + ace_mode: text + language_id: 250 +Nit: + type: programming + color: "#009917" + extensions: + - ".nit" + tm_scope: source.nit + ace_mode: text + language_id: 251 +Nix: + type: programming + color: "#7e7eff" + extensions: + - ".nix" + aliases: + - nixos + tm_scope: source.nix + ace_mode: nix + language_id: 252 +Nu: + type: programming + color: "#c9df40" + aliases: + - nush + extensions: + - ".nu" + filenames: + - Nukefile + tm_scope: source.nu + ace_mode: scheme + codemirror_mode: scheme + codemirror_mime_type: text/x-scheme + interpreters: + - nush + language_id: 253 +NumPy: + type: programming + color: "#9C8AF9" + group: Python + extensions: + - ".numpy" + - ".numpyw" + - ".numsc" + tm_scope: none + ace_mode: text + codemirror_mode: python + codemirror_mime_type: text/x-python + language_id: 254 +OCaml: + type: programming + ace_mode: ocaml + codemirror_mode: mllike + codemirror_mime_type: text/x-ocaml + color: "#3be133" + extensions: + - ".ml" + - ".eliom" + - ".eliomi" + - ".ml4" + - ".mli" + - ".mll" + - ".mly" + interpreters: + - ocaml + - ocamlrun + - ocamlscript + tm_scope: source.ocaml + language_id: 255 +ObjDump: + type: data + extensions: + - ".objdump" + tm_scope: objdump.x86asm + ace_mode: assembly_x86 + language_id: 256 +Object Data Instance Notation: + type: data + extensions: + - ".odin" + tm_scope: source.odin-ehr + ace_mode: text + language_id: 985227236 +ObjectScript: + type: programming + extensions: + - ".cls" + language_id: 202735509 + tm_scope: source.objectscript + color: "#424893" + ace_mode: text +Objective-C: + type: programming + tm_scope: source.objc + color: "#438eff" + aliases: + - obj-c + - objc + - objectivec + extensions: + - ".m" + - ".h" + ace_mode: objectivec + codemirror_mode: clike + codemirror_mime_type: text/x-objectivec + language_id: 257 +Objective-C++: + type: programming + tm_scope: source.objc++ + color: "#6866fb" + aliases: + - obj-c++ + - objc++ + - objectivec++ + extensions: + - ".mm" + ace_mode: objectivec + codemirror_mode: clike + codemirror_mime_type: text/x-objectivec + language_id: 258 +Objective-J: + type: programming + color: "#ff0c5a" + aliases: + - obj-j + - objectivej + - objj + extensions: + - ".j" + - ".sj" + tm_scope: source.js.objj + ace_mode: text + language_id: 259 +Odin: + type: programming + color: "#60AFFE" + aliases: + - odinlang + - odin-lang + extensions: + - ".odin" + tm_scope: source.odin + ace_mode: text + language_id: 889244082 +Omgrofl: + type: programming + extensions: + - ".omgrofl" + color: "#cabbff" + tm_scope: none + ace_mode: text + language_id: 260 +Opa: + type: programming + extensions: + - ".opa" + tm_scope: source.opa + ace_mode: text + language_id: 261 +Opal: + type: programming + color: "#f7ede0" + extensions: + - ".opal" + tm_scope: source.opal + ace_mode: text + language_id: 262 +Open Policy Agent: + type: programming + ace_mode: text + extensions: + - ".rego" + language_id: 840483232 + tm_scope: source.rego +OpenCL: + type: programming + group: C + extensions: + - ".cl" + - ".opencl" + tm_scope: source.c + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-csrc + language_id: 263 +OpenEdge ABL: + type: programming + aliases: + - progress + - openedge + - abl + extensions: + - ".p" + - ".cls" + - ".w" + tm_scope: source.abl + ace_mode: text + language_id: 264 +OpenQASM: + type: programming + extensions: + - ".qasm" + color: "#AA70FF" + tm_scope: source.qasm + ace_mode: text + language_id: 153739399 +OpenRC runscript: + type: programming + group: Shell + aliases: + - openrc + interpreters: + - openrc-run + tm_scope: source.shell + ace_mode: sh + codemirror_mode: shell + codemirror_mime_type: text/x-sh + language_id: 265 +OpenSCAD: + type: programming + extensions: + - ".scad" + tm_scope: source.scad + ace_mode: scad + language_id: 266 +OpenStep Property List: + type: data + extensions: + - ".plist" + tm_scope: source.plist + ace_mode: text + language_id: 598917541 +OpenType Feature File: + type: data + aliases: + - AFDKO + extensions: + - ".fea" + tm_scope: source.opentype + ace_mode: text + language_id: 374317347 +Org: + type: prose + wrap: true + extensions: + - ".org" + tm_scope: none + ace_mode: text + language_id: 267 +Ox: + type: programming + extensions: + - ".ox" + - ".oxh" + - ".oxo" + tm_scope: source.ox + ace_mode: text + language_id: 268 +Oxygene: + type: programming + color: "#cdd0e3" + extensions: + - ".oxygene" + tm_scope: none + ace_mode: text + language_id: 269 +Oz: + type: programming + color: "#fab738" + extensions: + - ".oz" + tm_scope: source.oz + ace_mode: text + codemirror_mode: oz + codemirror_mime_type: text/x-oz + language_id: 270 +P4: + type: programming + color: "#7055b5" + extensions: + - ".p4" + tm_scope: source.p4 + ace_mode: text + language_id: 348895984 +PHP: + type: programming + tm_scope: text.html.php + ace_mode: php + codemirror_mode: php + codemirror_mime_type: application/x-httpd-php + color: "#4F5D95" + extensions: + - ".php" + - ".aw" + - ".ctp" + - ".fcgi" + - ".inc" + - ".php3" + - ".php4" + - ".php5" + - ".phps" + - ".phpt" + filenames: + - ".php" + - ".php_cs" + - ".php_cs.dist" + - Phakefile + interpreters: + - php + aliases: + - inc + language_id: 272 +PLSQL: + type: programming + ace_mode: sql + codemirror_mode: sql + codemirror_mime_type: text/x-plsql + tm_scope: none + color: "#dad8d8" + extensions: + - ".pls" + - ".bdy" + - ".ddl" + - ".fnc" + - ".pck" + - ".pkb" + - ".pks" + - ".plb" + - ".plsql" + - ".prc" + - ".spc" + - ".sql" + - ".tpb" + - ".tps" + - ".trg" + - ".vw" + language_id: 273 +PLpgSQL: + type: programming + ace_mode: pgsql + codemirror_mode: sql + codemirror_mime_type: text/x-sql + tm_scope: source.sql + extensions: + - ".pgsql" + - ".sql" + language_id: 274 +POV-Ray SDL: + type: programming + aliases: + - pov-ray + - povray + extensions: + - ".pov" + - ".inc" + tm_scope: source.pov-ray sdl + ace_mode: text + language_id: 275 +Pan: + type: programming + color: "#cc0000" + extensions: + - ".pan" + tm_scope: source.pan + ace_mode: text + language_id: 276 +Papyrus: + type: programming + color: "#6600cc" + extensions: + - ".psc" + tm_scope: source.papyrus.skyrim + ace_mode: text + language_id: 277 +Parrot: + type: programming + color: "#f3ca0a" + extensions: + - ".parrot" + tm_scope: none + ace_mode: text + language_id: 278 +Parrot Assembly: + group: Parrot + type: programming + aliases: + - pasm + extensions: + - ".pasm" + interpreters: + - parrot + tm_scope: none + ace_mode: text + language_id: 279 +Parrot Internal Representation: + group: Parrot + tm_scope: source.parrot.pir + type: programming + aliases: + - pir + extensions: + - ".pir" + interpreters: + - parrot + ace_mode: text + language_id: 280 +Pascal: + type: programming + color: "#E3F171" + extensions: + - ".pas" + - ".dfm" + - ".dpr" + - ".inc" + - ".lpr" + - ".pascal" + - ".pp" + interpreters: + - instantfpc + tm_scope: source.pascal + ace_mode: pascal + codemirror_mode: pascal + codemirror_mime_type: text/x-pascal + language_id: 281 +Pawn: + type: programming + color: "#dbb284" + extensions: + - ".pwn" + - ".inc" + - ".sma" + tm_scope: source.pawn + ace_mode: text + language_id: 271 +Pep8: + type: programming + color: "#C76F5B" + extensions: + - ".pep" + ace_mode: text + tm_scope: source.pep8 + language_id: 840372442 +Perl: + type: programming + tm_scope: source.perl + ace_mode: perl + codemirror_mode: perl + codemirror_mime_type: text/x-perl + color: "#0298c3" + extensions: + - ".pl" + - ".al" + - ".cgi" + - ".fcgi" + - ".perl" + - ".ph" + - ".plx" + - ".pm" + - ".psgi" + - ".t" + filenames: + - Makefile.PL + - Rexfile + - ack + - cpanfile + interpreters: + - cperl + - perl + aliases: + - cperl + language_id: 282 +Pic: + type: markup + group: Roff + tm_scope: source.pic + extensions: + - ".pic" + - ".chem" + ace_mode: text + codemirror_mode: troff + codemirror_mime_type: text/troff + language_id: 425 +Pickle: + type: data + extensions: + - ".pkl" + tm_scope: none + ace_mode: text + language_id: 284 +PicoLisp: + type: programming + extensions: + - ".l" + interpreters: + - picolisp + - pil + tm_scope: source.lisp + ace_mode: lisp + language_id: 285 +PigLatin: + type: programming + color: "#fcd7de" + extensions: + - ".pig" + tm_scope: source.pig_latin + ace_mode: text + language_id: 286 +Pike: + type: programming + color: "#005390" + extensions: + - ".pike" + - ".pmod" + interpreters: + - pike + tm_scope: source.pike + ace_mode: text + language_id: 287 +PlantUML: + type: data + extensions: + - ".puml" + - ".iuml" + - ".plantuml" + tm_scope: source.wsd + ace_mode: text + language_id: 833504686 +Pod: + type: prose + ace_mode: perl + codemirror_mode: perl + codemirror_mime_type: text/x-perl + wrap: true + extensions: + - ".pod" + interpreters: + - perl + tm_scope: none + language_id: 288 +Pod 6: + type: prose + ace_mode: perl + tm_scope: source.perl6fe + wrap: true + extensions: + - ".pod" + - ".pod6" + interpreters: + - perl6 + language_id: 155357471 +PogoScript: + type: programming + color: "#d80074" + extensions: + - ".pogo" + tm_scope: source.pogoscript + ace_mode: text + language_id: 289 +Pony: + type: programming + extensions: + - ".pony" + tm_scope: source.pony + ace_mode: text + language_id: 290 +PostCSS: + type: markup + tm_scope: source.postcss + group: CSS + extensions: + - ".pcss" + - ".postcss" + ace_mode: text + language_id: 262764437 +PostScript: + type: markup + color: "#da291c" + extensions: + - ".ps" + - ".eps" + - ".epsi" + - ".pfa" + tm_scope: source.postscript + aliases: + - postscr + ace_mode: text + language_id: 291 +PowerBuilder: + type: programming + color: "#8f0f8d" + extensions: + - ".pbt" + - ".sra" + - ".sru" + - ".srw" + tm_scope: none + ace_mode: text + language_id: 292 +PowerShell: + type: programming + color: "#012456" + tm_scope: source.powershell + ace_mode: powershell + codemirror_mode: powershell + codemirror_mime_type: application/x-powershell + aliases: + - posh + - pwsh + extensions: + - ".ps1" + - ".psd1" + - ".psm1" + interpreters: + - pwsh + language_id: 293 +Prisma: + type: data + color: "#0c344b" + extensions: + - ".prisma" + tm_scope: source.prisma + ace_mode: text + language_id: 499933428 +Processing: + type: programming + color: "#0096D8" + extensions: + - ".pde" + tm_scope: source.processing + ace_mode: text + language_id: 294 +Proguard: + type: data + extensions: + - ".pro" + tm_scope: none + ace_mode: text + language_id: 716513858 +Prolog: + type: programming + color: "#74283c" + extensions: + - ".pl" + - ".pro" + - ".prolog" + - ".yap" + interpreters: + - swipl + - yap + tm_scope: source.prolog + ace_mode: prolog + language_id: 295 +Propeller Spin: + type: programming + color: "#7fa2a7" + extensions: + - ".spin" + tm_scope: source.spin + ace_mode: text + language_id: 296 +Protocol Buffer: + type: data + aliases: + - protobuf + - Protocol Buffers + extensions: + - ".proto" + tm_scope: source.protobuf + ace_mode: protobuf + codemirror_mode: protobuf + codemirror_mime_type: text/x-protobuf + language_id: 297 +Public Key: + type: data + extensions: + - ".asc" + - ".pub" + tm_scope: none + ace_mode: text + codemirror_mode: asciiarmor + codemirror_mime_type: application/pgp + language_id: 298 +Pug: + type: markup + color: "#a86454" + extensions: + - ".jade" + - ".pug" + tm_scope: text.jade + ace_mode: jade + codemirror_mode: pug + codemirror_mime_type: text/x-pug + language_id: 179 +Puppet: + type: programming + color: "#302B6D" + extensions: + - ".pp" + filenames: + - Modulefile + ace_mode: text + codemirror_mode: puppet + codemirror_mime_type: text/x-puppet + tm_scope: source.puppet + language_id: 299 +Pure Data: + type: data + extensions: + - ".pd" + tm_scope: none + ace_mode: text + language_id: 300 +PureBasic: + type: programming + color: "#5a6986" + extensions: + - ".pb" + - ".pbi" + tm_scope: none + ace_mode: text + language_id: 301 +PureScript: + type: programming + color: "#1D222D" + extensions: + - ".purs" + tm_scope: source.purescript + ace_mode: haskell + codemirror_mode: haskell + codemirror_mime_type: text/x-haskell + language_id: 302 +Python: + type: programming + tm_scope: source.python + ace_mode: python + codemirror_mode: python + codemirror_mime_type: text/x-python + color: "#3572A5" + extensions: + - ".py" + - ".cgi" + - ".fcgi" + - ".gyp" + - ".gypi" + - ".lmi" + - ".py3" + - ".pyde" + - ".pyi" + - ".pyp" + - ".pyt" + - ".pyw" + - ".rpy" + - ".smk" + - ".spec" + - ".tac" + - ".wsgi" + - ".xpy" + filenames: + - ".gclient" + - DEPS + - SConscript + - SConstruct + - Snakefile + - wscript + interpreters: + - python + - python2 + - python3 + aliases: + - python3 + - rusthon + language_id: 303 +Python console: + type: programming + group: Python + searchable: false + aliases: + - pycon + tm_scope: text.python.console + ace_mode: text + language_id: 428 +Python traceback: + type: data + group: Python + searchable: false + extensions: + - ".pytb" + tm_scope: text.python.traceback + ace_mode: text + language_id: 304 +Q#: + type: programming + extensions: + - ".qs" + aliases: + - qsharp + color: "#fed659" + ace_mode: text + tm_scope: source.qsharp + language_id: 697448245 +QML: + type: programming + color: "#44a51c" + extensions: + - ".qml" + - ".qbs" + tm_scope: source.qml + ace_mode: text + language_id: 305 +QMake: + type: programming + extensions: + - ".pro" + - ".pri" + interpreters: + - qmake + tm_scope: source.qmake + ace_mode: text + language_id: 306 +Qt Script: + type: programming + ace_mode: javascript + codemirror_mode: javascript + codemirror_mime_type: text/javascript + extensions: + - ".qs" + filenames: + - installscript.qs + - toolchain_installscript.qs + color: "#00b841" + tm_scope: source.js + language_id: 558193693 +Quake: + type: programming + filenames: + - m3makefile + - m3overrides + color: "#882233" + ace_mode: text + tm_scope: source.quake + language_id: 375265331 +R: + type: programming + color: "#198CE7" + aliases: + - R + - Rscript + - splus + extensions: + - ".r" + - ".rd" + - ".rsx" + filenames: + - ".Rprofile" + - expr-dist + interpreters: + - Rscript + tm_scope: source.r + ace_mode: r + codemirror_mode: r + codemirror_mime_type: text/x-rsrc + language_id: 307 +RAML: + type: markup + ace_mode: yaml + codemirror_mode: yaml + codemirror_mime_type: text/x-yaml + tm_scope: source.yaml + color: "#77d9fb" + extensions: + - ".raml" + language_id: 308 +RDoc: + type: prose + ace_mode: rdoc + wrap: true + extensions: + - ".rdoc" + tm_scope: text.rdoc + language_id: 309 +REALbasic: + type: programming + extensions: + - ".rbbas" + - ".rbfrm" + - ".rbmnu" + - ".rbres" + - ".rbtbar" + - ".rbuistate" + tm_scope: source.vbnet + ace_mode: text + language_id: 310 +REXX: + type: programming + aliases: + - arexx + extensions: + - ".rexx" + - ".pprx" + - ".rex" + interpreters: + - regina + - rexx + tm_scope: source.rexx + ace_mode: text + language_id: 311 +RMarkdown: + type: prose + wrap: true + ace_mode: markdown + codemirror_mode: gfm + codemirror_mime_type: text/x-gfm + extensions: + - ".rmd" + tm_scope: source.gfm + language_id: 313 +RPC: + type: programming + aliases: + - rpcgen + - oncrpc + - xdr + ace_mode: c_cpp + extensions: + - ".x" + tm_scope: source.c + language_id: 1031374237 +RPM Spec: + type: data + tm_scope: source.rpm-spec + extensions: + - ".spec" + aliases: + - specfile + ace_mode: text + codemirror_mode: rpm + codemirror_mime_type: text/x-rpm-spec + language_id: 314 +RUNOFF: + type: markup + color: "#665a4e" + extensions: + - ".rnh" + - ".rno" + wrap: true + tm_scope: text.runoff + ace_mode: text + language_id: 315 +Racket: + type: programming + color: "#3c5caa" + extensions: + - ".rkt" + - ".rktd" + - ".rktl" + - ".scrbl" + interpreters: + - racket + tm_scope: source.racket + ace_mode: lisp + language_id: 316 +Ragel: + type: programming + color: "#9d5200" + extensions: + - ".rl" + aliases: + - ragel-rb + - ragel-ruby + tm_scope: none + ace_mode: text + language_id: 317 +Raku: + type: programming + color: "#0000fb" + extensions: + - ".6pl" + - ".6pm" + - ".nqp" + - ".p6" + - ".p6l" + - ".p6m" + - ".pl" + - ".pl6" + - ".pm" + - ".pm6" + - ".t" + interpreters: + - perl6 + - raku + - rakudo + aliases: + - perl6 + - perl-6 + tm_scope: source.perl6fe + ace_mode: perl + codemirror_mode: perl + codemirror_mime_type: text/x-perl + language_id: 283 +Rascal: + type: programming + color: "#fffaa0" + extensions: + - ".rsc" + tm_scope: source.rascal + ace_mode: text + language_id: 173616037 +Raw token data: + type: data + aliases: + - raw + extensions: + - ".raw" + tm_scope: none + ace_mode: text + language_id: 318 +Readline Config: + type: data + group: INI + aliases: + - inputrc + - readline + filenames: + - ".inputrc" + - inputrc + tm_scope: source.inputrc + ace_mode: text + language_id: 538732839 +Reason: + type: programming + color: "#ff5847" + ace_mode: rust + codemirror_mode: rust + codemirror_mime_type: text/x-rustsrc + extensions: + - ".re" + - ".rei" + tm_scope: source.reason + language_id: 869538413 +Rebol: + type: programming + color: "#358a5b" + extensions: + - ".reb" + - ".r" + - ".r2" + - ".r3" + - ".rebol" + ace_mode: text + tm_scope: source.rebol + language_id: 319 +Red: + type: programming + color: "#f50000" + extensions: + - ".red" + - ".reds" + aliases: + - red/system + tm_scope: source.red + ace_mode: text + language_id: 320 +Redcode: + type: programming + extensions: + - ".cw" + tm_scope: none + ace_mode: text + language_id: 321 +Regular Expression: + type: data + extensions: + - ".regexp" + - ".regex" + aliases: + - regexp + - regex + ace_mode: text + tm_scope: source.regexp + language_id: 363378884 +Ren'Py: + type: programming + aliases: + - renpy + color: "#ff7f7f" + extensions: + - ".rpy" + tm_scope: source.renpy + ace_mode: python + language_id: 322 +RenderScript: + type: programming + extensions: + - ".rs" + - ".rsh" + tm_scope: none + ace_mode: text + language_id: 323 +Rich Text Format: + type: markup + extensions: + - ".rtf" + tm_scope: text.rtf + ace_mode: text + language_id: 51601661 +Ring: + type: programming + color: "#2D54CB" + extensions: + - ".ring" + tm_scope: source.ring + ace_mode: text + language_id: 431 +Riot: + type: markup + color: "#A71E49" + ace_mode: html + extensions: + - ".riot" + tm_scope: text.html.riot + language_id: 878396783 +RobotFramework: + type: programming + extensions: + - ".robot" + tm_scope: text.robot + ace_mode: text + language_id: 324 +Roff: + type: markup + color: "#ecdebe" + extensions: + - ".roff" + - ".1" + - ".1in" + - ".1m" + - ".1x" + - ".2" + - ".3" + - ".3in" + - ".3m" + - ".3p" + - ".3pm" + - ".3qt" + - ".3x" + - ".4" + - ".5" + - ".6" + - ".7" + - ".8" + - ".9" + - ".l" + - ".man" + - ".mdoc" + - ".me" + - ".ms" + - ".n" + - ".nr" + - ".rno" + - ".tmac" + filenames: + - eqnrc + - mmn + - mmt + - troffrc + - troffrc-end + tm_scope: text.roff + aliases: + - groff + - man + - manpage + - man page + - man-page + - mdoc + - nroff + - troff + wrap: true + ace_mode: text + codemirror_mode: troff + codemirror_mime_type: text/troff + language_id: 141 +Roff Manpage: + type: markup + group: Roff + extensions: + - ".1" + - ".1in" + - ".1m" + - ".1x" + - ".2" + - ".3" + - ".3in" + - ".3m" + - ".3p" + - ".3pm" + - ".3qt" + - ".3x" + - ".4" + - ".5" + - ".6" + - ".7" + - ".8" + - ".9" + - ".man" + - ".mdoc" + wrap: true + tm_scope: text.roff + ace_mode: text + codemirror_mode: troff + codemirror_mime_type: text/troff + language_id: 612669833 +Rouge: + type: programming + ace_mode: clojure + codemirror_mode: clojure + codemirror_mime_type: text/x-clojure + color: "#cc0088" + extensions: + - ".rg" + tm_scope: source.clojure + language_id: 325 +Ruby: + type: programming + tm_scope: source.ruby + ace_mode: ruby + codemirror_mode: ruby + codemirror_mime_type: text/x-ruby + color: "#701516" + aliases: + - jruby + - macruby + - rake + - rb + - rbx + extensions: + - ".rb" + - ".builder" + - ".eye" + - ".fcgi" + - ".gemspec" + - ".god" + - ".jbuilder" + - ".mspec" + - ".pluginspec" + - ".podspec" + - ".rabl" + - ".rake" + - ".rbi" + - ".rbuild" + - ".rbw" + - ".rbx" + - ".ru" + - ".ruby" + - ".spec" + - ".thor" + - ".watchr" + interpreters: + - ruby + - macruby + - rake + - jruby + - rbx + filenames: + - ".irbrc" + - ".pryrc" + - ".simplecov" + - Appraisals + - Berksfile + - Brewfile + - Buildfile + - Capfile + - Dangerfile + - Deliverfile + - Fastfile + - Gemfile + - Gemfile.lock + - Guardfile + - Jarfile + - Mavenfile + - Podfile + - Puppetfile + - Rakefile + - Snapfile + - Thorfile + - Vagrantfile + - buildfile + language_id: 326 +Rust: + type: programming + color: "#dea584" + extensions: + - ".rs" + - ".rs.in" + tm_scope: source.rust + ace_mode: rust + codemirror_mode: rust + codemirror_mime_type: text/x-rustsrc + language_id: 327 +SAS: + type: programming + color: "#B34936" + extensions: + - ".sas" + tm_scope: source.sas + ace_mode: text + codemirror_mode: sas + codemirror_mime_type: text/x-sas + language_id: 328 +SCSS: + type: markup + color: "#c6538c" + tm_scope: source.css.scss + ace_mode: scss + codemirror_mode: css + codemirror_mime_type: text/x-scss + extensions: + - ".scss" + language_id: 329 +SMT: + type: programming + extensions: + - ".smt2" + - ".smt" + interpreters: + - boolector + - cvc4 + - mathsat5 + - opensmt + - smtinterpol + - smt-rat + - stp + - verit + - yices2 + - z3 + tm_scope: source.smt + ace_mode: text + language_id: 330 +SPARQL: + type: data + tm_scope: source.sparql + ace_mode: text + codemirror_mode: sparql + codemirror_mime_type: application/sparql-query + extensions: + - ".sparql" + - ".rq" + language_id: 331 +SQF: + type: programming + color: "#3F3F3F" + extensions: + - ".sqf" + - ".hqf" + tm_scope: source.sqf + ace_mode: text + language_id: 332 +SQL: + type: data + tm_scope: source.sql + ace_mode: sql + codemirror_mode: sql + codemirror_mime_type: text/x-sql + extensions: + - ".sql" + - ".cql" + - ".ddl" + - ".inc" + - ".mysql" + - ".prc" + - ".tab" + - ".udf" + - ".viw" + language_id: 333 +SQLPL: + type: programming + ace_mode: sql + codemirror_mode: sql + codemirror_mime_type: text/x-sql + tm_scope: source.sql + extensions: + - ".sql" + - ".db2" + language_id: 334 +SRecode Template: + type: markup + color: "#348a34" + tm_scope: source.lisp + ace_mode: lisp + codemirror_mode: commonlisp + codemirror_mime_type: text/x-common-lisp + extensions: + - ".srt" + language_id: 335 +SSH Config: + type: data + group: INI + filenames: + - ssh-config + - ssh_config + - sshconfig + - sshconfig.snip + - sshd-config + - sshd_config + ace_mode: text + tm_scope: source.ssh-config + language_id: 554920715 +STON: + type: data + group: Smalltalk + extensions: + - ".ston" + tm_scope: source.smalltalk + ace_mode: text + language_id: 336 +SVG: + type: data + color: "#ff9900" + extensions: + - ".svg" + tm_scope: text.xml.svg + ace_mode: xml + codemirror_mode: xml + codemirror_mime_type: text/xml + language_id: 337 +SWIG: + type: programming + extensions: + - ".i" + tm_scope: source.c++ + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-c++src + language_id: 1066250075 +Sage: + type: programming + extensions: + - ".sage" + - ".sagews" + tm_scope: source.python + ace_mode: python + codemirror_mode: python + codemirror_mime_type: text/x-python + language_id: 338 +SaltStack: + type: programming + color: "#646464" + aliases: + - saltstate + - salt + extensions: + - ".sls" + tm_scope: source.yaml.salt + ace_mode: yaml + codemirror_mode: yaml + codemirror_mime_type: text/x-yaml + language_id: 339 +Sass: + type: markup + color: "#a53b70" + tm_scope: source.sass + extensions: + - ".sass" + ace_mode: sass + codemirror_mode: sass + codemirror_mime_type: text/x-sass + language_id: 340 +Scala: + type: programming + tm_scope: source.scala + ace_mode: scala + codemirror_mode: clike + codemirror_mime_type: text/x-scala + color: "#c22d40" + extensions: + - ".scala" + - ".kojo" + - ".sbt" + - ".sc" + interpreters: + - scala + language_id: 341 +Scaml: + group: HTML + type: markup + extensions: + - ".scaml" + tm_scope: source.scaml + ace_mode: text + language_id: 342 +Scheme: + type: programming + color: "#1e4aec" + extensions: + - ".scm" + - ".sch" + - ".sld" + - ".sls" + - ".sps" + - ".ss" + interpreters: + - scheme + - guile + - bigloo + - chicken + - csi + - gosh + - r6rs + tm_scope: source.scheme + ace_mode: scheme + codemirror_mode: scheme + codemirror_mime_type: text/x-scheme + language_id: 343 +Scilab: + type: programming + extensions: + - ".sci" + - ".sce" + - ".tst" + tm_scope: source.scilab + ace_mode: text + language_id: 344 +Self: + type: programming + color: "#0579aa" + extensions: + - ".self" + tm_scope: none + ace_mode: text + language_id: 345 +ShaderLab: + type: programming + extensions: + - ".shader" + ace_mode: text + tm_scope: source.shaderlab + language_id: 664257356 +Shell: + type: programming + color: "#89e051" + aliases: + - sh + - shell-script + - bash + - zsh + extensions: + - ".sh" + - ".bash" + - ".bats" + - ".cgi" + - ".command" + - ".env" + - ".fcgi" + - ".ksh" + - ".sh.in" + - ".tmux" + - ".tool" + - ".zsh" + filenames: + - ".bash_aliases" + - ".bash_history" + - ".bash_logout" + - ".bash_profile" + - ".bashrc" + - ".cshrc" + - ".env" + - ".env.example" + - ".flaskenv" + - ".login" + - ".profile" + - ".zlogin" + - ".zlogout" + - ".zprofile" + - ".zshenv" + - ".zshrc" + - 9fs + - PKGBUILD + - bash_aliases + - bash_logout + - bash_profile + - bashrc + - cshrc + - gradlew + - login + - man + - profile + - zlogin + - zlogout + - zprofile + - zshenv + - zshrc + interpreters: + - ash + - bash + - dash + - ksh + - mksh + - pdksh + - rc + - sh + - zsh + tm_scope: source.shell + ace_mode: sh + codemirror_mode: shell + codemirror_mime_type: text/x-sh + language_id: 346 +ShellSession: + type: programming + extensions: + - ".sh-session" + aliases: + - bash session + - console + tm_scope: text.shell-session + ace_mode: sh + codemirror_mode: shell + codemirror_mime_type: text/x-sh + language_id: 347 +Shen: + type: programming + color: "#120F14" + extensions: + - ".shen" + tm_scope: source.shen + ace_mode: text + language_id: 348 +Sieve: + type: programming + tm_scope: source.sieve + ace_mode: text + extensions: + - ".sieve" + codemirror_mode: sieve + codemirror_mime_type: application/sieve + language_id: 208976687 +Slash: + type: programming + color: "#007eff" + extensions: + - ".sl" + tm_scope: text.html.slash + ace_mode: text + language_id: 349 +Slice: + type: programming + color: "#003fa2" + tm_scope: source.slice + ace_mode: text + extensions: + - ".ice" + language_id: 894641667 +Slim: + type: markup + color: "#2b2b2b" + extensions: + - ".slim" + tm_scope: text.slim + ace_mode: text + codemirror_mode: slim + codemirror_mime_type: text/x-slim + language_id: 350 +SmPL: + type: programming + extensions: + - ".cocci" + aliases: + - coccinelle + ace_mode: text + tm_scope: source.smpl + color: "#c94949" + language_id: 164123055 +Smali: + type: programming + extensions: + - ".smali" + ace_mode: text + tm_scope: source.smali + language_id: 351 +Smalltalk: + type: programming + color: "#596706" + extensions: + - ".st" + - ".cs" + aliases: + - squeak + tm_scope: source.smalltalk + ace_mode: text + codemirror_mode: smalltalk + codemirror_mime_type: text/x-stsrc + language_id: 352 +Smarty: + type: programming + extensions: + - ".tpl" + ace_mode: smarty + codemirror_mode: smarty + codemirror_mime_type: text/x-smarty + tm_scope: text.html.smarty + language_id: 353 +Solidity: + type: programming + color: "#AA6746" + ace_mode: text + tm_scope: source.solidity + extensions: + - ".sol" + language_id: 237469032 +SourcePawn: + type: programming + color: "#f69e1d" + aliases: + - sourcemod + extensions: + - ".sp" + - ".inc" + tm_scope: source.sourcepawn + ace_mode: text + language_id: 354 +Spline Font Database: + type: data + extensions: + - ".sfd" + tm_scope: text.sfd + ace_mode: yaml + language_id: 767169629 +Squirrel: + type: programming + color: "#800000" + extensions: + - ".nut" + tm_scope: source.c++ + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-c++src + language_id: 355 +Stan: + type: programming + color: "#b2011d" + extensions: + - ".stan" + ace_mode: text + tm_scope: source.stan + language_id: 356 +Standard ML: + type: programming + color: "#dc566d" + aliases: + - sml + extensions: + - ".ml" + - ".fun" + - ".sig" + - ".sml" + tm_scope: source.ml + ace_mode: text + codemirror_mode: mllike + codemirror_mime_type: text/x-ocaml + language_id: 357 +Starlark: + type: programming + tm_scope: source.python + ace_mode: python + codemirror_mode: python + codemirror_mime_type: text/x-python + color: "#76d275" + extensions: + - ".bzl" + filenames: + - BUCK + - BUILD + - BUILD.bazel + - Tiltfile + - WORKSPACE + aliases: + - bazel + - bzl + language_id: 960266174 +Stata: + type: programming + extensions: + - ".do" + - ".ado" + - ".doh" + - ".ihlp" + - ".mata" + - ".matah" + - ".sthlp" + tm_scope: source.stata + ace_mode: text + language_id: 358 +Stylus: + type: markup + color: "#ff6347" + extensions: + - ".styl" + tm_scope: source.stylus + ace_mode: stylus + language_id: 359 +SubRip Text: + type: data + extensions: + - ".srt" + ace_mode: text + tm_scope: text.srt + language_id: 360 +SugarSS: + type: markup + tm_scope: source.css.postcss.sugarss + group: CSS + extensions: + - ".sss" + ace_mode: text + language_id: 826404698 +SuperCollider: + type: programming + color: "#46390b" + extensions: + - ".sc" + - ".scd" + interpreters: + - sclang + - scsynth + tm_scope: source.supercollider + ace_mode: text + language_id: 361 +Svelte: + type: markup + color: "#ff3e00" + tm_scope: source.svelte + ace_mode: html + codemirror_mode: htmlmixed + codemirror_mime_type: text/html + extensions: + - ".svelte" + language_id: 928734530 +Swift: + type: programming + color: "#ffac45" + extensions: + - ".swift" + tm_scope: source.swift + ace_mode: text + codemirror_mode: swift + codemirror_mime_type: text/x-swift + language_id: 362 +SystemVerilog: + type: programming + color: "#DAE1C2" + extensions: + - ".sv" + - ".svh" + - ".vh" + tm_scope: source.systemverilog + ace_mode: verilog + codemirror_mode: verilog + codemirror_mime_type: text/x-systemverilog + language_id: 363 +TI Program: + type: programming + ace_mode: text + color: "#A0AA87" + extensions: + - ".8xp" + - ".8xk" + - ".8xk.txt" + - ".8xp.txt" + language_id: 422 + tm_scope: none +TLA: + type: programming + extensions: + - ".tla" + tm_scope: source.tla + ace_mode: text + language_id: 364 +TOML: + type: data + extensions: + - ".toml" + filenames: + - Cargo.lock + - Gopkg.lock + - poetry.lock + tm_scope: source.toml + ace_mode: toml + codemirror_mode: toml + codemirror_mime_type: text/x-toml + language_id: 365 +TSQL: + type: programming + extensions: + - ".sql" + ace_mode: sql + tm_scope: source.tsql + language_id: 918334941 +TSV: + type: data + ace_mode: text + tm_scope: source.generic-db + extensions: + - ".tsv" + language_id: 1035892117 +TSX: + type: programming + group: TypeScript + extensions: + - ".tsx" + tm_scope: source.tsx + ace_mode: javascript + codemirror_mode: jsx + codemirror_mime_type: text/jsx + language_id: 94901924 +TXL: + type: programming + extensions: + - ".txl" + tm_scope: source.txl + ace_mode: text + language_id: 366 +Tcl: + type: programming + color: "#e4cc98" + extensions: + - ".tcl" + - ".adp" + - ".tm" + filenames: + - owh + - starfield + interpreters: + - tclsh + - wish + tm_scope: source.tcl + ace_mode: tcl + codemirror_mode: tcl + codemirror_mime_type: text/x-tcl + language_id: 367 +Tcsh: + type: programming + group: Shell + extensions: + - ".tcsh" + - ".csh" + interpreters: + - tcsh + - csh + tm_scope: source.shell + ace_mode: sh + codemirror_mode: shell + codemirror_mime_type: text/x-sh + language_id: 368 +TeX: + type: markup + color: "#3D6117" + ace_mode: tex + codemirror_mode: stex + codemirror_mime_type: text/x-stex + tm_scope: text.tex.latex + wrap: true + aliases: + - latex + extensions: + - ".tex" + - ".aux" + - ".bbx" + - ".cbx" + - ".cls" + - ".dtx" + - ".ins" + - ".lbx" + - ".ltx" + - ".mkii" + - ".mkiv" + - ".mkvi" + - ".sty" + - ".toc" + language_id: 369 +Tea: + type: markup + extensions: + - ".tea" + tm_scope: source.tea + ace_mode: text + language_id: 370 +Terra: + type: programming + extensions: + - ".t" + color: "#00004c" + tm_scope: source.terra + ace_mode: lua + codemirror_mode: lua + codemirror_mime_type: text/x-lua + interpreters: + - lua + language_id: 371 +Texinfo: + type: prose + wrap: true + extensions: + - ".texinfo" + - ".texi" + - ".txi" + ace_mode: text + tm_scope: text.texinfo + interpreters: + - makeinfo + language_id: 988020015 +Text: + type: prose + wrap: true + aliases: + - fundamental + extensions: + - ".txt" + - ".fr" + - ".nb" + - ".ncl" + - ".no" + filenames: + - COPYING + - COPYING.regex + - COPYRIGHT.regex + - FONTLOG + - INSTALL + - INSTALL.mysql + - LICENSE + - LICENSE.mysql + - NEWS + - README.1ST + - README.me + - README.mysql + - click.me + - delete.me + - go.mod + - go.sum + - keep.me + - package.mask + - package.use.mask + - package.use.stable.mask + - read.me + - readme.1st + - test.me + - use.mask + - use.stable.mask + tm_scope: none + ace_mode: text + language_id: 372 +Textile: + type: prose + ace_mode: textile + codemirror_mode: textile + codemirror_mime_type: text/x-textile + wrap: true + extensions: + - ".textile" + tm_scope: none + language_id: 373 +Thrift: + type: programming + tm_scope: source.thrift + extensions: + - ".thrift" + ace_mode: text + language_id: 374 +Turing: + type: programming + color: "#cf142b" + extensions: + - ".t" + - ".tu" + tm_scope: source.turing + ace_mode: text + language_id: 375 +Turtle: + type: data + extensions: + - ".ttl" + tm_scope: source.turtle + ace_mode: text + codemirror_mode: turtle + codemirror_mime_type: text/turtle + language_id: 376 +Twig: + type: markup + color: "#c1d026" + extensions: + - ".twig" + tm_scope: text.html.twig + ace_mode: twig + codemirror_mode: twig + codemirror_mime_type: text/x-twig + language_id: 377 +Type Language: + type: data + aliases: + - tl + extensions: + - ".tl" + tm_scope: source.tl + ace_mode: text + language_id: 632765617 +TypeScript: + type: programming + color: "#2b7489" + aliases: + - ts + interpreters: + - deno + - ts-node + extensions: + - ".ts" + tm_scope: source.ts + ace_mode: typescript + codemirror_mode: javascript + codemirror_mime_type: application/typescript + language_id: 378 +Unified Parallel C: + type: programming + color: "#4e3617" + group: C + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-csrc + extensions: + - ".upc" + tm_scope: source.c + language_id: 379 +Unity3D Asset: + type: data + ace_mode: yaml + codemirror_mode: yaml + codemirror_mime_type: text/x-yaml + extensions: + - ".anim" + - ".asset" + - ".mask" + - ".mat" + - ".meta" + - ".prefab" + - ".unity" + tm_scope: source.yaml + language_id: 380 +Unix Assembly: + type: programming + group: Assembly + extensions: + - ".s" + - ".ms" + tm_scope: source.x86 + ace_mode: assembly_x86 + language_id: 120 +Uno: + type: programming + color: "#9933cc" + extensions: + - ".uno" + ace_mode: csharp + codemirror_mode: clike + codemirror_mime_type: text/x-csharp + tm_scope: source.cs + language_id: 381 +UnrealScript: + type: programming + color: "#a54c4d" + extensions: + - ".uc" + tm_scope: source.java + ace_mode: java + codemirror_mode: clike + codemirror_mime_type: text/x-java + language_id: 382 +UrWeb: + type: programming + aliases: + - Ur/Web + - Ur + extensions: + - ".ur" + - ".urs" + tm_scope: source.ur + ace_mode: text + language_id: 383 +V: + type: programming + color: "#4f87c4" + aliases: + - vlang + extensions: + - ".v" + tm_scope: source.v + ace_mode: golang + codemirror_mode: go + codemirror_mime_type: text/x-go + language_id: 603371597 +VBA: + type: programming + color: "#867db1" + extensions: + - ".bas" + - ".cls" + - ".frm" + - ".frx" + - ".vba" + tm_scope: source.vbnet + aliases: + - vb6 + - visual basic 6 + - visual basic for applications + ace_mode: text + codemirror_mode: vb + codemirror_mime_type: text/x-vb + language_id: 399230729 +VBScript: + type: programming + color: "#15dcdc" + extensions: + - ".vbs" + tm_scope: source.vbnet + ace_mode: text + codemirror_mode: vbscript + codemirror_mime_type: text/vbscript + language_id: 408016005 +VCL: + type: programming + color: "#148AA8" + extensions: + - ".vcl" + tm_scope: source.varnish.vcl + ace_mode: text + language_id: 384 +VHDL: + type: programming + color: "#adb2cb" + extensions: + - ".vhdl" + - ".vhd" + - ".vhf" + - ".vhi" + - ".vho" + - ".vhs" + - ".vht" + - ".vhw" + tm_scope: source.vhdl + ace_mode: vhdl + codemirror_mode: vhdl + codemirror_mime_type: text/x-vhdl + language_id: 385 +Vala: + type: programming + color: "#fbe5cd" + extensions: + - ".vala" + - ".vapi" + tm_scope: source.vala + ace_mode: vala + language_id: 386 +Verilog: + type: programming + color: "#b2b7f8" + extensions: + - ".v" + - ".veo" + tm_scope: source.verilog + ace_mode: verilog + codemirror_mode: verilog + codemirror_mime_type: text/x-verilog + language_id: 387 +Vim Help File: + type: prose + aliases: + - vimhelp + extensions: + - ".txt" + tm_scope: text.vim-help + ace_mode: text + language_id: 508563686 +Vim Snippet: + type: markup + aliases: + - SnipMate + - UltiSnip + - UltiSnips + - NeoSnippet + extensions: + - ".snip" + - ".snippet" + - ".snippets" + tm_scope: source.vim-snippet + ace_mode: text + language_id: 81265970 +Vim script: + type: programming + color: "#199f4b" + tm_scope: source.viml + aliases: + - vim + - viml + - nvim + extensions: + - ".vim" + - ".vba" + - ".vmb" + filenames: + - ".exrc" + - ".gvimrc" + - ".nvimrc" + - ".vimrc" + - _vimrc + - gvimrc + - nvimrc + - vimrc + ace_mode: text + language_id: 388 +Visual Basic .NET: + type: programming + color: "#945db7" + extensions: + - ".vb" + - ".vbhtml" + aliases: + - visual basic + - vbnet + - vb .net + - vb.net + tm_scope: source.vbnet + ace_mode: text + codemirror_mode: vb + codemirror_mime_type: text/x-vb + language_id: 389 +Volt: + type: programming + color: "#1F1F1F" + extensions: + - ".volt" + tm_scope: source.d + ace_mode: d + codemirror_mode: d + codemirror_mime_type: text/x-d + language_id: 390 +Vue: + type: markup + color: "#2c3e50" + extensions: + - ".vue" + tm_scope: text.html.vue + ace_mode: html + language_id: 391 +Wavefront Material: + type: data + extensions: + - ".mtl" + tm_scope: source.wavefront.mtl + ace_mode: text + language_id: 392 +Wavefront Object: + type: data + extensions: + - ".obj" + tm_scope: source.wavefront.obj + ace_mode: text + language_id: 393 +Web Ontology Language: + type: data + extensions: + - ".owl" + tm_scope: text.xml + ace_mode: xml + language_id: 394 +WebAssembly: + type: programming + color: "#04133b" + extensions: + - ".wast" + - ".wat" + aliases: + - wast + - wasm + tm_scope: source.webassembly + ace_mode: lisp + codemirror_mode: commonlisp + codemirror_mime_type: text/x-common-lisp + language_id: 956556503 +WebIDL: + type: programming + extensions: + - ".webidl" + tm_scope: source.webidl + ace_mode: text + codemirror_mode: webidl + codemirror_mime_type: text/x-webidl + language_id: 395 +WebVTT: + type: data + wrap: true + extensions: + - ".vtt" + tm_scope: source.vtt + ace_mode: text + language_id: 658679714 +Wget Config: + type: data + group: INI + aliases: + - wgetrc + filenames: + - ".wgetrc" + tm_scope: source.wgetrc + ace_mode: text + language_id: 668457123 +Windows Registry Entries: + type: data + extensions: + - ".reg" + tm_scope: source.reg + ace_mode: ini + codemirror_mode: properties + codemirror_mime_type: text/x-properties + language_id: 969674868 +Wollok: + type: programming + color: "#a23738" + extensions: + - ".wlk" + ace_mode: text + tm_scope: source.wollok + language_id: 632745969 +World of Warcraft Addon Data: + type: data + extensions: + - ".toc" + tm_scope: source.toc + ace_mode: text + language_id: 396 +X BitMap: + type: data + group: C + aliases: + - xbm + extensions: + - ".xbm" + ace_mode: c_cpp + tm_scope: source.c + codemirror_mode: clike + codemirror_mime_type: text/x-csrc + language_id: 782911107 +X Font Directory Index: + type: data + filenames: + - encodings.dir + - fonts.alias + - fonts.dir + - fonts.scale + tm_scope: source.fontdir + ace_mode: text + language_id: 208700028 +X PixMap: + type: data + group: C + aliases: + - xpm + extensions: + - ".xpm" + - ".pm" + ace_mode: c_cpp + tm_scope: source.c + codemirror_mode: clike + codemirror_mime_type: text/x-csrc + language_id: 781846279 +X10: + type: programming + aliases: + - xten + ace_mode: text + extensions: + - ".x10" + color: "#4B6BEF" + tm_scope: source.x10 + language_id: 397 +XC: + type: programming + color: "#99DA07" + extensions: + - ".xc" + tm_scope: source.xc + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-csrc + language_id: 398 +XCompose: + type: data + filenames: + - ".XCompose" + - XCompose + - xcompose + tm_scope: config.xcompose + ace_mode: text + language_id: 225167241 +XML: + type: data + tm_scope: text.xml + ace_mode: xml + codemirror_mode: xml + codemirror_mime_type: text/xml + aliases: + - rss + - xsd + - wsdl + extensions: + - ".xml" + - ".adml" + - ".admx" + - ".ant" + - ".axml" + - ".builds" + - ".ccproj" + - ".ccxml" + - ".clixml" + - ".cproject" + - ".cscfg" + - ".csdef" + - ".csl" + - ".csproj" + - ".ct" + - ".depproj" + - ".dita" + - ".ditamap" + - ".ditaval" + - ".dll.config" + - ".dotsettings" + - ".filters" + - ".fsproj" + - ".fxml" + - ".glade" + - ".gml" + - ".gmx" + - ".grxml" + - ".gst" + - ".iml" + - ".ivy" + - ".jelly" + - ".jsproj" + - ".kml" + - ".launch" + - ".mdpolicy" + - ".mjml" + - ".mm" + - ".mod" + - ".mxml" + - ".natvis" + - ".ncl" + - ".ndproj" + - ".nproj" + - ".nuspec" + - ".odd" + - ".osm" + - ".pkgproj" + - ".pluginspec" + - ".proj" + - ".props" + - ".ps1xml" + - ".psc1" + - ".pt" + - ".rdf" + - ".resx" + - ".rss" + - ".sch" + - ".scxml" + - ".sfproj" + - ".shproj" + - ".srdf" + - ".storyboard" + - ".sublime-snippet" + - ".targets" + - ".tml" + - ".ts" + - ".tsx" + - ".ui" + - ".urdf" + - ".ux" + - ".vbproj" + - ".vcxproj" + - ".vsixmanifest" + - ".vssettings" + - ".vstemplate" + - ".vxml" + - ".wixproj" + - ".workflow" + - ".wsdl" + - ".wsf" + - ".wxi" + - ".wxl" + - ".wxs" + - ".x3d" + - ".xacro" + - ".xaml" + - ".xib" + - ".xlf" + - ".xliff" + - ".xmi" + - ".xml.dist" + - ".xproj" + - ".xsd" + - ".xspec" + - ".xul" + - ".zcml" + filenames: + - ".classpath" + - ".cproject" + - ".project" + - App.config + - NuGet.config + - Settings.StyleCop + - Web.Debug.config + - Web.Release.config + - Web.config + - packages.config + language_id: 399 +XML Property List: + type: data + group: XML + extensions: + - ".plist" + - ".stTheme" + - ".tmCommand" + - ".tmLanguage" + - ".tmPreferences" + - ".tmSnippet" + - ".tmTheme" + tm_scope: text.xml.plist + ace_mode: xml + codemirror_mode: xml + codemirror_mime_type: text/xml + language_id: 75622871 +XPages: + type: data + extensions: + - ".xsp-config" + - ".xsp.metadata" + tm_scope: text.xml + ace_mode: xml + codemirror_mode: xml + codemirror_mime_type: text/xml + language_id: 400 +XProc: + type: programming + extensions: + - ".xpl" + - ".xproc" + tm_scope: text.xml + ace_mode: xml + codemirror_mode: xml + codemirror_mime_type: text/xml + language_id: 401 +XQuery: + type: programming + color: "#5232e7" + extensions: + - ".xquery" + - ".xq" + - ".xql" + - ".xqm" + - ".xqy" + ace_mode: xquery + codemirror_mode: xquery + codemirror_mime_type: application/xquery + tm_scope: source.xq + language_id: 402 +XS: + type: programming + extensions: + - ".xs" + tm_scope: source.c + ace_mode: c_cpp + codemirror_mode: clike + codemirror_mime_type: text/x-csrc + language_id: 403 +XSLT: + type: programming + aliases: + - xsl + extensions: + - ".xslt" + - ".xsl" + tm_scope: text.xml.xsl + ace_mode: xml + codemirror_mode: xml + codemirror_mime_type: text/xml + color: "#EB8CEB" + language_id: 404 +Xojo: + type: programming + extensions: + - ".xojo_code" + - ".xojo_menu" + - ".xojo_report" + - ".xojo_script" + - ".xojo_toolbar" + - ".xojo_window" + tm_scope: source.xojo + ace_mode: text + language_id: 405 +Xtend: + type: programming + extensions: + - ".xtend" + tm_scope: source.xtend + ace_mode: text + language_id: 406 +YAML: + type: data + color: "#cb171e" + tm_scope: source.yaml + aliases: + - yml + extensions: + - ".yml" + - ".mir" + - ".reek" + - ".rviz" + - ".sublime-syntax" + - ".syntax" + - ".yaml" + - ".yaml-tmlanguage" + - ".yaml.sed" + - ".yml.mysql" + filenames: + - ".clang-format" + - ".clang-tidy" + - ".gemrc" + - glide.lock + - yarn.lock + ace_mode: yaml + codemirror_mode: yaml + codemirror_mime_type: text/x-yaml + language_id: 407 +YANG: + type: data + extensions: + - ".yang" + tm_scope: source.yang + ace_mode: text + language_id: 408 +YARA: + type: programming + color: "#220000" + ace_mode: text + extensions: + - ".yar" + - ".yara" + tm_scope: source.yara + language_id: 805122868 +YASnippet: + type: markup + aliases: + - snippet + - yas + color: "#32AB90" + extensions: + - ".yasnippet" + tm_scope: source.yasnippet + ace_mode: text + language_id: 378760102 +Yacc: + type: programming + extensions: + - ".y" + - ".yacc" + - ".yy" + tm_scope: source.yacc + ace_mode: text + color: "#4B6C4B" + language_id: 409 +ZAP: + type: programming + color: "#0d665e" + extensions: + - ".zap" + - ".xzap" + tm_scope: source.zap + ace_mode: text + language_id: 952972794 +ZIL: + type: programming + color: "#dc75e5" + extensions: + - ".zil" + - ".mud" + tm_scope: source.zil + ace_mode: text + language_id: 973483626 +Zeek: + type: programming + aliases: + - bro + extensions: + - ".zeek" + - ".bro" + tm_scope: source.zeek + ace_mode: text + language_id: 40 +ZenScript: + type: programming + color: "#00BCD1" + extensions: + - ".zs" + tm_scope: source.zenscript + ace_mode: text + language_id: 494938890 +Zephir: + type: programming + color: "#118f9e" + extensions: + - ".zep" + tm_scope: source.php.zephir + ace_mode: php + language_id: 410 +Zig: + type: programming + color: "#ec915c" + extensions: + - ".zig" + tm_scope: source.zig + ace_mode: text + language_id: 646424281 +Zimpl: + type: programming + extensions: + - ".zimpl" + - ".zmpl" + - ".zpl" + tm_scope: none + ace_mode: text + language_id: 411 +cURL Config: + type: data + group: INI + aliases: + - curlrc + filenames: + - ".curlrc" + - _curlrc + tm_scope: source.curlrc + ace_mode: text + language_id: 992375436 +desktop: + type: data + extensions: + - ".desktop" + - ".desktop.in" + tm_scope: source.desktop + ace_mode: text + language_id: 412 +dircolors: + type: data + extensions: + - ".dircolors" + filenames: + - ".dir_colors" + - ".dircolors" + - DIR_COLORS + - _dir_colors + - _dircolors + - dir_colors + tm_scope: source.dircolors + ace_mode: text + language_id: 691605112 +eC: + type: programming + color: "#913960" + extensions: + - ".ec" + - ".eh" + tm_scope: source.c.ec + ace_mode: text + language_id: 413 +edn: + type: data + ace_mode: clojure + codemirror_mode: clojure + codemirror_mime_type: text/x-clojure + extensions: + - ".edn" + tm_scope: source.clojure + language_id: 414 +fish: + type: programming + group: Shell + interpreters: + - fish + extensions: + - ".fish" + tm_scope: source.fish + ace_mode: text + language_id: 415 +mIRC Script: + type: programming + color: "#3d57c3" + extensions: + - ".mrc" + tm_scope: source.msl + ace_mode: text + language_id: 517654727 +mcfunction: + type: programming + color: "#E22837" + extensions: + - ".mcfunction" + tm_scope: source.mcfunction + ace_mode: text + language_id: 462488745 +mupad: + type: programming + extensions: + - ".mu" + tm_scope: source.mupad + ace_mode: text + language_id: 416 +nanorc: + type: data + group: INI + extensions: + - ".nanorc" + filenames: + - ".nanorc" + - nanorc + tm_scope: source.nanorc + ace_mode: text + language_id: 775996197 +nesC: + type: programming + color: "#94B0C7" + extensions: + - ".nc" + ace_mode: text + tm_scope: source.nesc + language_id: 417 +ooc: + type: programming + color: "#b0b77e" + extensions: + - ".ooc" + tm_scope: source.ooc + ace_mode: text + language_id: 418 +q: + type: programming + extensions: + - ".q" + tm_scope: source.q + ace_mode: text + color: "#0040cd" + language_id: 970539067 +reStructuredText: + type: prose + wrap: true + aliases: + - rst + extensions: + - ".rst" + - ".rest" + - ".rest.txt" + - ".rst.txt" + tm_scope: text.restructuredtext + ace_mode: text + codemirror_mode: rst + codemirror_mime_type: text/x-rst + language_id: 419 +sed: + type: programming + color: "#64b970" + extensions: + - ".sed" + interpreters: + - gsed + - minised + - sed + - ssed + ace_mode: text + tm_scope: source.sed + language_id: 847830017 +wdl: + type: programming + color: "#42f1f4" + extensions: + - ".wdl" + tm_scope: source.wdl + ace_mode: text + language_id: 374521672 +wisp: + type: programming + ace_mode: clojure + codemirror_mode: clojure + codemirror_mime_type: text/x-clojure + color: "#7582D1" + extensions: + - ".wisp" + tm_scope: source.clojure + language_id: 420 +xBase: + type: programming + color: "#403a40" + aliases: + - advpl + - clipper + - foxpro + extensions: + - ".prg" + - ".ch" + - ".prw" + tm_scope: source.harbour + ace_mode: text + language_id: 421 \ No newline at end of file diff --git a/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go b/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go new file mode 100644 index 00000000000..2681af35af1 --- /dev/null +++ b/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go @@ -0,0 +1,78 @@ +// Copyright 2018 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 lazyregexp is a thin wrapper over regexp, allowing the use of global +// regexp variables without forcing them to be compiled at init. +package lazyregexp + +import ( + "os" + "regexp" + "strings" + "sync" +) + +// Regexp is a wrapper around regexp.Regexp, where the underlying regexp will be +// compiled the first time it is needed. +type Regexp struct { + str string + once sync.Once + rx *regexp.Regexp +} + +func (r *Regexp) re() *regexp.Regexp { + r.once.Do(r.build) + return r.rx +} + +func (r *Regexp) build() { + r.rx = regexp.MustCompile(r.str) + r.str = "" +} + +func (r *Regexp) FindSubmatch(s []byte) [][]byte { + return r.re().FindSubmatch(s) +} + +func (r *Regexp) FindStringSubmatch(s string) []string { + return r.re().FindStringSubmatch(s) +} + +func (r *Regexp) FindStringSubmatchIndex(s string) []int { + return r.re().FindStringSubmatchIndex(s) +} + +func (r *Regexp) ReplaceAllString(src, repl string) string { + return r.re().ReplaceAllString(src, repl) +} + +func (r *Regexp) FindString(s string) string { + return r.re().FindString(s) +} + +func (r *Regexp) FindAllString(s string, n int) []string { + return r.re().FindAllString(s, n) +} + +func (r *Regexp) MatchString(s string) bool { + return r.re().MatchString(s) +} + +func (r *Regexp) SubexpNames() []string { + return r.re().SubexpNames() +} + +var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test") + +// New creates a new lazy regexp, delaying the compiling work until it is first +// needed. If the code is being run as part of tests, the regexp compiling will +// happen immediately. +func New(str string) *Regexp { + lr := &Regexp{str: str} + if inTest { + // In tests, always compile the regexps early. + lr.re() + } + return lr +} diff --git a/vendor/golang.org/x/mod/modfile/print.go b/vendor/golang.org/x/mod/modfile/print.go new file mode 100644 index 00000000000..524f93022ac --- /dev/null +++ b/vendor/golang.org/x/mod/modfile/print.go @@ -0,0 +1,174 @@ +// Copyright 2018 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. + +// Module file printer. + +package modfile + +import ( + "bytes" + "fmt" + "strings" +) + +// Format returns a go.mod file as a byte slice, formatted in standard style. +func Format(f *FileSyntax) []byte { + pr := &printer{} + pr.file(f) + return pr.Bytes() +} + +// A printer collects the state during printing of a file or expression. +type printer struct { + bytes.Buffer // output buffer + comment []Comment // pending end-of-line comments + margin int // left margin (indent), a number of tabs +} + +// printf prints to the buffer. +func (p *printer) printf(format string, args ...interface{}) { + fmt.Fprintf(p, format, args...) +} + +// indent returns the position on the current line, in bytes, 0-indexed. +func (p *printer) indent() int { + b := p.Bytes() + n := 0 + for n < len(b) && b[len(b)-1-n] != '\n' { + n++ + } + return n +} + +// newline ends the current line, flushing end-of-line comments. +func (p *printer) newline() { + if len(p.comment) > 0 { + p.printf(" ") + for i, com := range p.comment { + if i > 0 { + p.trim() + p.printf("\n") + for i := 0; i < p.margin; i++ { + p.printf("\t") + } + } + p.printf("%s", strings.TrimSpace(com.Token)) + } + p.comment = p.comment[:0] + } + + p.trim() + p.printf("\n") + for i := 0; i < p.margin; i++ { + p.printf("\t") + } +} + +// trim removes trailing spaces and tabs from the current line. +func (p *printer) trim() { + // Remove trailing spaces and tabs from line we're about to end. + b := p.Bytes() + n := len(b) + for n > 0 && (b[n-1] == '\t' || b[n-1] == ' ') { + n-- + } + p.Truncate(n) +} + +// file formats the given file into the print buffer. +func (p *printer) file(f *FileSyntax) { + for _, com := range f.Before { + p.printf("%s", strings.TrimSpace(com.Token)) + p.newline() + } + + for i, stmt := range f.Stmt { + switch x := stmt.(type) { + case *CommentBlock: + // comments already handled + p.expr(x) + + default: + p.expr(x) + p.newline() + } + + for _, com := range stmt.Comment().After { + p.printf("%s", strings.TrimSpace(com.Token)) + p.newline() + } + + if i+1 < len(f.Stmt) { + p.newline() + } + } +} + +func (p *printer) expr(x Expr) { + // Emit line-comments preceding this expression. + if before := x.Comment().Before; len(before) > 0 { + // Want to print a line comment. + // Line comments must be at the current margin. + p.trim() + if p.indent() > 0 { + // There's other text on the line. Start a new line. + p.printf("\n") + } + // Re-indent to margin. + for i := 0; i < p.margin; i++ { + p.printf("\t") + } + for _, com := range before { + p.printf("%s", strings.TrimSpace(com.Token)) + p.newline() + } + } + + switch x := x.(type) { + default: + panic(fmt.Errorf("printer: unexpected type %T", x)) + + case *CommentBlock: + // done + + case *LParen: + p.printf("(") + case *RParen: + p.printf(")") + + case *Line: + p.tokens(x.Token) + + case *LineBlock: + p.tokens(x.Token) + p.printf(" ") + p.expr(&x.LParen) + p.margin++ + for _, l := range x.Line { + p.newline() + p.expr(l) + } + p.margin-- + p.newline() + p.expr(&x.RParen) + } + + // Queue end-of-line comments for printing when we + // reach the end of the line. + p.comment = append(p.comment, x.Comment().Suffix...) +} + +func (p *printer) tokens(tokens []string) { + sep := "" + for _, t := range tokens { + if t == "," || t == ")" || t == "]" || t == "}" { + sep = "" + } + p.printf("%s%s", sep, t) + sep = " " + if t == "(" || t == "[" || t == "{" { + sep = "" + } + } +} diff --git a/vendor/golang.org/x/mod/modfile/read.go b/vendor/golang.org/x/mod/modfile/read.go new file mode 100644 index 00000000000..956f30cbb39 --- /dev/null +++ b/vendor/golang.org/x/mod/modfile/read.go @@ -0,0 +1,959 @@ +// Copyright 2018 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 modfile + +import ( + "bytes" + "errors" + "fmt" + "os" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +// A Position describes an arbitrary source position in a file, including the +// file, line, column, and byte offset. +type Position struct { + Line int // line in input (starting at 1) + LineRune int // rune in line (starting at 1) + Byte int // byte in input (starting at 0) +} + +// add returns the position at the end of s, assuming it starts at p. +func (p Position) add(s string) Position { + p.Byte += len(s) + if n := strings.Count(s, "\n"); n > 0 { + p.Line += n + s = s[strings.LastIndex(s, "\n")+1:] + p.LineRune = 1 + } + p.LineRune += utf8.RuneCountInString(s) + return p +} + +// An Expr represents an input element. +type Expr interface { + // Span returns the start and end position of the expression, + // excluding leading or trailing comments. + Span() (start, end Position) + + // Comment returns the comments attached to the expression. + // This method would normally be named 'Comments' but that + // would interfere with embedding a type of the same name. + Comment() *Comments +} + +// A Comment represents a single // comment. +type Comment struct { + Start Position + Token string // without trailing newline + Suffix bool // an end of line (not whole line) comment +} + +// Comments collects the comments associated with an expression. +type Comments struct { + Before []Comment // whole-line comments before this expression + Suffix []Comment // end-of-line comments after this expression + + // For top-level expressions only, After lists whole-line + // comments following the expression. + After []Comment +} + +// Comment returns the receiver. This isn't useful by itself, but +// a Comments struct is embedded into all the expression +// implementation types, and this gives each of those a Comment +// method to satisfy the Expr interface. +func (c *Comments) Comment() *Comments { + return c +} + +// A FileSyntax represents an entire go.mod file. +type FileSyntax struct { + Name string // file path + Comments + Stmt []Expr +} + +func (x *FileSyntax) Span() (start, end Position) { + if len(x.Stmt) == 0 { + return + } + start, _ = x.Stmt[0].Span() + _, end = x.Stmt[len(x.Stmt)-1].Span() + return start, end +} + +// addLine adds a line containing the given tokens to the file. +// +// If the first token of the hint matches the first token of the +// line, the new line is added at the end of the block containing hint, +// extracting hint into a new block if it is not yet in one. +// +// If the hint is non-nil buts its first token does not match, +// the new line is added after the block containing hint +// (or hint itself, if not in a block). +// +// If no hint is provided, addLine appends the line to the end of +// the last block with a matching first token, +// or to the end of the file if no such block exists. +func (x *FileSyntax) addLine(hint Expr, tokens ...string) *Line { + if hint == nil { + // If no hint given, add to the last statement of the given type. + Loop: + for i := len(x.Stmt) - 1; i >= 0; i-- { + stmt := x.Stmt[i] + switch stmt := stmt.(type) { + case *Line: + if stmt.Token != nil && stmt.Token[0] == tokens[0] { + hint = stmt + break Loop + } + case *LineBlock: + if stmt.Token[0] == tokens[0] { + hint = stmt + break Loop + } + } + } + } + + newLineAfter := func(i int) *Line { + new := &Line{Token: tokens} + if i == len(x.Stmt) { + x.Stmt = append(x.Stmt, new) + } else { + x.Stmt = append(x.Stmt, nil) + copy(x.Stmt[i+2:], x.Stmt[i+1:]) + x.Stmt[i+1] = new + } + return new + } + + if hint != nil { + for i, stmt := range x.Stmt { + switch stmt := stmt.(type) { + case *Line: + if stmt == hint { + if stmt.Token == nil || stmt.Token[0] != tokens[0] { + return newLineAfter(i) + } + + // Convert line to line block. + stmt.InBlock = true + block := &LineBlock{Token: stmt.Token[:1], Line: []*Line{stmt}} + stmt.Token = stmt.Token[1:] + x.Stmt[i] = block + new := &Line{Token: tokens[1:], InBlock: true} + block.Line = append(block.Line, new) + return new + } + + case *LineBlock: + if stmt == hint { + if stmt.Token[0] != tokens[0] { + return newLineAfter(i) + } + + new := &Line{Token: tokens[1:], InBlock: true} + stmt.Line = append(stmt.Line, new) + return new + } + + for j, line := range stmt.Line { + if line == hint { + if stmt.Token[0] != tokens[0] { + return newLineAfter(i) + } + + // Add new line after hint within the block. + stmt.Line = append(stmt.Line, nil) + copy(stmt.Line[j+2:], stmt.Line[j+1:]) + new := &Line{Token: tokens[1:], InBlock: true} + stmt.Line[j+1] = new + return new + } + } + } + } + } + + new := &Line{Token: tokens} + x.Stmt = append(x.Stmt, new) + return new +} + +func (x *FileSyntax) updateLine(line *Line, tokens ...string) { + if line.InBlock { + tokens = tokens[1:] + } + line.Token = tokens +} + +// markRemoved modifies line so that it (and its end-of-line comment, if any) +// will be dropped by (*FileSyntax).Cleanup. +func (line *Line) markRemoved() { + line.Token = nil + line.Comments.Suffix = nil +} + +// Cleanup cleans up the file syntax x after any edit operations. +// To avoid quadratic behavior, (*Line).markRemoved marks the line as dead +// by setting line.Token = nil but does not remove it from the slice +// in which it appears. After edits have all been indicated, +// calling Cleanup cleans out the dead lines. +func (x *FileSyntax) Cleanup() { + w := 0 + for _, stmt := range x.Stmt { + switch stmt := stmt.(type) { + case *Line: + if stmt.Token == nil { + continue + } + case *LineBlock: + ww := 0 + for _, line := range stmt.Line { + if line.Token != nil { + stmt.Line[ww] = line + ww++ + } + } + if ww == 0 { + continue + } + if ww == 1 { + // Collapse block into single line. + line := &Line{ + Comments: Comments{ + Before: commentsAdd(stmt.Before, stmt.Line[0].Before), + Suffix: commentsAdd(stmt.Line[0].Suffix, stmt.Suffix), + After: commentsAdd(stmt.Line[0].After, stmt.After), + }, + Token: stringsAdd(stmt.Token, stmt.Line[0].Token), + } + x.Stmt[w] = line + w++ + continue + } + stmt.Line = stmt.Line[:ww] + } + x.Stmt[w] = stmt + w++ + } + x.Stmt = x.Stmt[:w] +} + +func commentsAdd(x, y []Comment) []Comment { + return append(x[:len(x):len(x)], y...) +} + +func stringsAdd(x, y []string) []string { + return append(x[:len(x):len(x)], y...) +} + +// A CommentBlock represents a top-level block of comments separate +// from any rule. +type CommentBlock struct { + Comments + Start Position +} + +func (x *CommentBlock) Span() (start, end Position) { + return x.Start, x.Start +} + +// A Line is a single line of tokens. +type Line struct { + Comments + Start Position + Token []string + InBlock bool + End Position +} + +func (x *Line) Span() (start, end Position) { + return x.Start, x.End +} + +// A LineBlock is a factored block of lines, like +// +// require ( +// "x" +// "y" +// ) +// +type LineBlock struct { + Comments + Start Position + LParen LParen + Token []string + Line []*Line + RParen RParen +} + +func (x *LineBlock) Span() (start, end Position) { + return x.Start, x.RParen.Pos.add(")") +} + +// An LParen represents the beginning of a parenthesized line block. +// It is a place to store suffix comments. +type LParen struct { + Comments + Pos Position +} + +func (x *LParen) Span() (start, end Position) { + return x.Pos, x.Pos.add(")") +} + +// An RParen represents the end of a parenthesized line block. +// It is a place to store whole-line (before) comments. +type RParen struct { + Comments + Pos Position +} + +func (x *RParen) Span() (start, end Position) { + return x.Pos, x.Pos.add(")") +} + +// An input represents a single input file being parsed. +type input struct { + // Lexing state. + filename string // name of input file, for errors + complete []byte // entire input + remaining []byte // remaining input + tokenStart []byte // token being scanned to end of input + token token // next token to be returned by lex, peek + pos Position // current input position + comments []Comment // accumulated comments + + // Parser state. + file *FileSyntax // returned top-level syntax tree + parseErrors ErrorList // errors encountered during parsing + + // Comment assignment state. + pre []Expr // all expressions, in preorder traversal + post []Expr // all expressions, in postorder traversal +} + +func newInput(filename string, data []byte) *input { + return &input{ + filename: filename, + complete: data, + remaining: data, + pos: Position{Line: 1, LineRune: 1, Byte: 0}, + } +} + +// parse parses the input file. +func parse(file string, data []byte) (f *FileSyntax, err error) { + // The parser panics for both routine errors like syntax errors + // and for programmer bugs like array index errors. + // Turn both into error returns. Catching bug panics is + // especially important when processing many files. + in := newInput(file, data) + defer func() { + if e := recover(); e != nil && e != &in.parseErrors { + in.parseErrors = append(in.parseErrors, Error{ + Filename: in.filename, + Pos: in.pos, + Err: fmt.Errorf("internal error: %v", e), + }) + } + if err == nil && len(in.parseErrors) > 0 { + err = in.parseErrors + } + }() + + // Prime the lexer by reading in the first token. It will be available + // in the next peek() or lex() call. + in.readToken() + + // Invoke the parser. + in.parseFile() + if len(in.parseErrors) > 0 { + return nil, in.parseErrors + } + in.file.Name = in.filename + + // Assign comments to nearby syntax. + in.assignComments() + + return in.file, nil +} + +// Error is called to report an error. +// Error does not return: it panics. +func (in *input) Error(s string) { + in.parseErrors = append(in.parseErrors, Error{ + Filename: in.filename, + Pos: in.pos, + Err: errors.New(s), + }) + panic(&in.parseErrors) +} + +// eof reports whether the input has reached end of file. +func (in *input) eof() bool { + return len(in.remaining) == 0 +} + +// peekRune returns the next rune in the input without consuming it. +func (in *input) peekRune() int { + if len(in.remaining) == 0 { + return 0 + } + r, _ := utf8.DecodeRune(in.remaining) + return int(r) +} + +// peekPrefix reports whether the remaining input begins with the given prefix. +func (in *input) peekPrefix(prefix string) bool { + // This is like bytes.HasPrefix(in.remaining, []byte(prefix)) + // but without the allocation of the []byte copy of prefix. + for i := 0; i < len(prefix); i++ { + if i >= len(in.remaining) || in.remaining[i] != prefix[i] { + return false + } + } + return true +} + +// readRune consumes and returns the next rune in the input. +func (in *input) readRune() int { + if len(in.remaining) == 0 { + in.Error("internal lexer error: readRune at EOF") + } + r, size := utf8.DecodeRune(in.remaining) + in.remaining = in.remaining[size:] + if r == '\n' { + in.pos.Line++ + in.pos.LineRune = 1 + } else { + in.pos.LineRune++ + } + in.pos.Byte += size + return int(r) +} + +type token struct { + kind tokenKind + pos Position + endPos Position + text string +} + +type tokenKind int + +const ( + _EOF tokenKind = -(iota + 1) + _EOLCOMMENT + _IDENT + _STRING + _COMMENT + + // newlines and punctuation tokens are allowed as ASCII codes. +) + +func (k tokenKind) isComment() bool { + return k == _COMMENT || k == _EOLCOMMENT +} + +// isEOL returns whether a token terminates a line. +func (k tokenKind) isEOL() bool { + return k == _EOF || k == _EOLCOMMENT || k == '\n' +} + +// startToken marks the beginning of the next input token. +// It must be followed by a call to endToken, once the token's text has +// been consumed using readRune. +func (in *input) startToken() { + in.tokenStart = in.remaining + in.token.text = "" + in.token.pos = in.pos +} + +// endToken marks the end of an input token. +// It records the actual token string in tok.text. +// A single trailing newline (LF or CRLF) will be removed from comment tokens. +func (in *input) endToken(kind tokenKind) { + in.token.kind = kind + text := string(in.tokenStart[:len(in.tokenStart)-len(in.remaining)]) + if kind.isComment() { + if strings.HasSuffix(text, "\r\n") { + text = text[:len(text)-2] + } else { + text = strings.TrimSuffix(text, "\n") + } + } + in.token.text = text + in.token.endPos = in.pos +} + +// peek returns the kind of the the next token returned by lex. +func (in *input) peek() tokenKind { + return in.token.kind +} + +// lex is called from the parser to obtain the next input token. +func (in *input) lex() token { + tok := in.token + in.readToken() + return tok +} + +// readToken lexes the next token from the text and stores it in in.token. +func (in *input) readToken() { + // Skip past spaces, stopping at non-space or EOF. + for !in.eof() { + c := in.peekRune() + if c == ' ' || c == '\t' || c == '\r' { + in.readRune() + continue + } + + // Comment runs to end of line. + if in.peekPrefix("//") { + in.startToken() + + // Is this comment the only thing on its line? + // Find the last \n before this // and see if it's all + // spaces from there to here. + i := bytes.LastIndex(in.complete[:in.pos.Byte], []byte("\n")) + suffix := len(bytes.TrimSpace(in.complete[i+1:in.pos.Byte])) > 0 + in.readRune() + in.readRune() + + // Consume comment. + for len(in.remaining) > 0 && in.readRune() != '\n' { + } + + // If we are at top level (not in a statement), hand the comment to + // the parser as a _COMMENT token. The grammar is written + // to handle top-level comments itself. + if !suffix { + in.endToken(_COMMENT) + return + } + + // Otherwise, save comment for later attachment to syntax tree. + in.endToken(_EOLCOMMENT) + in.comments = append(in.comments, Comment{in.token.pos, in.token.text, suffix}) + return + } + + if in.peekPrefix("/*") { + in.Error("mod files must use // comments (not /* */ comments)") + } + + // Found non-space non-comment. + break + } + + // Found the beginning of the next token. + in.startToken() + + // End of file. + if in.eof() { + in.endToken(_EOF) + return + } + + // Punctuation tokens. + switch c := in.peekRune(); c { + case '\n', '(', ')', '[', ']', '{', '}', ',': + in.readRune() + in.endToken(tokenKind(c)) + return + + case '"', '`': // quoted string + quote := c + in.readRune() + for { + if in.eof() { + in.pos = in.token.pos + in.Error("unexpected EOF in string") + } + if in.peekRune() == '\n' { + in.Error("unexpected newline in string") + } + c := in.readRune() + if c == quote { + break + } + if c == '\\' && quote != '`' { + if in.eof() { + in.pos = in.token.pos + in.Error("unexpected EOF in string") + } + in.readRune() + } + } + in.endToken(_STRING) + return + } + + // Checked all punctuation. Must be identifier token. + if c := in.peekRune(); !isIdent(c) { + in.Error(fmt.Sprintf("unexpected input character %#q", c)) + } + + // Scan over identifier. + for isIdent(in.peekRune()) { + if in.peekPrefix("//") { + break + } + if in.peekPrefix("/*") { + in.Error("mod files must use // comments (not /* */ comments)") + } + in.readRune() + } + in.endToken(_IDENT) +} + +// isIdent reports whether c is an identifier rune. +// We treat most printable runes as identifier runes, except for a handful of +// ASCII punctuation characters. +func isIdent(c int) bool { + switch r := rune(c); r { + case ' ', '(', ')', '[', ']', '{', '}', ',': + return false + default: + return !unicode.IsSpace(r) && unicode.IsPrint(r) + } +} + +// Comment assignment. +// We build two lists of all subexpressions, preorder and postorder. +// The preorder list is ordered by start location, with outer expressions first. +// The postorder list is ordered by end location, with outer expressions last. +// We use the preorder list to assign each whole-line comment to the syntax +// immediately following it, and we use the postorder list to assign each +// end-of-line comment to the syntax immediately preceding it. + +// order walks the expression adding it and its subexpressions to the +// preorder and postorder lists. +func (in *input) order(x Expr) { + if x != nil { + in.pre = append(in.pre, x) + } + switch x := x.(type) { + default: + panic(fmt.Errorf("order: unexpected type %T", x)) + case nil: + // nothing + case *LParen, *RParen: + // nothing + case *CommentBlock: + // nothing + case *Line: + // nothing + case *FileSyntax: + for _, stmt := range x.Stmt { + in.order(stmt) + } + case *LineBlock: + in.order(&x.LParen) + for _, l := range x.Line { + in.order(l) + } + in.order(&x.RParen) + } + if x != nil { + in.post = append(in.post, x) + } +} + +// assignComments attaches comments to nearby syntax. +func (in *input) assignComments() { + const debug = false + + // Generate preorder and postorder lists. + in.order(in.file) + + // Split into whole-line comments and suffix comments. + var line, suffix []Comment + for _, com := range in.comments { + if com.Suffix { + suffix = append(suffix, com) + } else { + line = append(line, com) + } + } + + if debug { + for _, c := range line { + fmt.Fprintf(os.Stderr, "LINE %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte) + } + } + + // Assign line comments to syntax immediately following. + for _, x := range in.pre { + start, _ := x.Span() + if debug { + fmt.Fprintf(os.Stderr, "pre %T :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte) + } + xcom := x.Comment() + for len(line) > 0 && start.Byte >= line[0].Start.Byte { + if debug { + fmt.Fprintf(os.Stderr, "ASSIGN LINE %q #%d\n", line[0].Token, line[0].Start.Byte) + } + xcom.Before = append(xcom.Before, line[0]) + line = line[1:] + } + } + + // Remaining line comments go at end of file. + in.file.After = append(in.file.After, line...) + + if debug { + for _, c := range suffix { + fmt.Fprintf(os.Stderr, "SUFFIX %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte) + } + } + + // Assign suffix comments to syntax immediately before. + for i := len(in.post) - 1; i >= 0; i-- { + x := in.post[i] + + start, end := x.Span() + if debug { + fmt.Fprintf(os.Stderr, "post %T :%d:%d #%d :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte, end.Line, end.LineRune, end.Byte) + } + + // Do not assign suffix comments to end of line block or whole file. + // Instead assign them to the last element inside. + switch x.(type) { + case *FileSyntax: + continue + } + + // Do not assign suffix comments to something that starts + // on an earlier line, so that in + // + // x ( y + // z ) // comment + // + // we assign the comment to z and not to x ( ... ). + if start.Line != end.Line { + continue + } + xcom := x.Comment() + for len(suffix) > 0 && end.Byte <= suffix[len(suffix)-1].Start.Byte { + if debug { + fmt.Fprintf(os.Stderr, "ASSIGN SUFFIX %q #%d\n", suffix[len(suffix)-1].Token, suffix[len(suffix)-1].Start.Byte) + } + xcom.Suffix = append(xcom.Suffix, suffix[len(suffix)-1]) + suffix = suffix[:len(suffix)-1] + } + } + + // We assigned suffix comments in reverse. + // If multiple suffix comments were appended to the same + // expression node, they are now in reverse. Fix that. + for _, x := range in.post { + reverseComments(x.Comment().Suffix) + } + + // Remaining suffix comments go at beginning of file. + in.file.Before = append(in.file.Before, suffix...) +} + +// reverseComments reverses the []Comment list. +func reverseComments(list []Comment) { + for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 { + list[i], list[j] = list[j], list[i] + } +} + +func (in *input) parseFile() { + in.file = new(FileSyntax) + var cb *CommentBlock + for { + switch in.peek() { + case '\n': + in.lex() + if cb != nil { + in.file.Stmt = append(in.file.Stmt, cb) + cb = nil + } + case _COMMENT: + tok := in.lex() + if cb == nil { + cb = &CommentBlock{Start: tok.pos} + } + com := cb.Comment() + com.Before = append(com.Before, Comment{Start: tok.pos, Token: tok.text}) + case _EOF: + if cb != nil { + in.file.Stmt = append(in.file.Stmt, cb) + } + return + default: + in.parseStmt() + if cb != nil { + in.file.Stmt[len(in.file.Stmt)-1].Comment().Before = cb.Before + cb = nil + } + } + } +} + +func (in *input) parseStmt() { + tok := in.lex() + start := tok.pos + end := tok.endPos + tokens := []string{tok.text} + for { + tok := in.lex() + switch { + case tok.kind.isEOL(): + in.file.Stmt = append(in.file.Stmt, &Line{ + Start: start, + Token: tokens, + End: end, + }) + return + + case tok.kind == '(': + if next := in.peek(); next.isEOL() { + // Start of block: no more tokens on this line. + in.file.Stmt = append(in.file.Stmt, in.parseLineBlock(start, tokens, tok)) + return + } else if next == ')' { + rparen := in.lex() + if in.peek().isEOL() { + // Empty block. + in.lex() + in.file.Stmt = append(in.file.Stmt, &LineBlock{ + Start: start, + Token: tokens, + LParen: LParen{Pos: tok.pos}, + RParen: RParen{Pos: rparen.pos}, + }) + return + } + // '( )' in the middle of the line, not a block. + tokens = append(tokens, tok.text, rparen.text) + } else { + // '(' in the middle of the line, not a block. + tokens = append(tokens, tok.text) + } + + default: + tokens = append(tokens, tok.text) + end = tok.endPos + } + } +} + +func (in *input) parseLineBlock(start Position, token []string, lparen token) *LineBlock { + x := &LineBlock{ + Start: start, + Token: token, + LParen: LParen{Pos: lparen.pos}, + } + var comments []Comment + for { + switch in.peek() { + case _EOLCOMMENT: + // Suffix comment, will be attached later by assignComments. + in.lex() + case '\n': + // Blank line. Add an empty comment to preserve it. + in.lex() + if len(comments) == 0 && len(x.Line) > 0 || len(comments) > 0 && comments[len(comments)-1].Token != "" { + comments = append(comments, Comment{}) + } + case _COMMENT: + tok := in.lex() + comments = append(comments, Comment{Start: tok.pos, Token: tok.text}) + case _EOF: + in.Error(fmt.Sprintf("syntax error (unterminated block started at %s:%d:%d)", in.filename, x.Start.Line, x.Start.LineRune)) + case ')': + rparen := in.lex() + x.RParen.Before = comments + x.RParen.Pos = rparen.pos + if !in.peek().isEOL() { + in.Error("syntax error (expected newline after closing paren)") + } + in.lex() + return x + default: + l := in.parseLine() + x.Line = append(x.Line, l) + l.Comment().Before = comments + comments = nil + } + } +} + +func (in *input) parseLine() *Line { + tok := in.lex() + if tok.kind.isEOL() { + in.Error("internal parse error: parseLine at end of line") + } + start := tok.pos + end := tok.endPos + tokens := []string{tok.text} + for { + tok := in.lex() + if tok.kind.isEOL() { + return &Line{ + Start: start, + Token: tokens, + End: end, + InBlock: true, + } + } + tokens = append(tokens, tok.text) + end = tok.endPos + } +} + +var ( + slashSlash = []byte("//") + moduleStr = []byte("module") +) + +// ModulePath returns the module path from the gomod file text. +// If it cannot find a module path, it returns an empty string. +// It is tolerant of unrelated problems in the go.mod file. +func ModulePath(mod []byte) string { + for len(mod) > 0 { + line := mod + mod = nil + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, mod = line[:i], line[i+1:] + } + if i := bytes.Index(line, slashSlash); i >= 0 { + line = line[:i] + } + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, moduleStr) { + continue + } + line = line[len(moduleStr):] + n := len(line) + line = bytes.TrimSpace(line) + if len(line) == n || len(line) == 0 { + continue + } + + if line[0] == '"' || line[0] == '`' { + p, err := strconv.Unquote(string(line)) + if err != nil { + return "" // malformed quoted string or multiline module path + } + return p + } + + return string(line) + } + return "" // missing module path +} diff --git a/vendor/golang.org/x/mod/modfile/rule.go b/vendor/golang.org/x/mod/modfile/rule.go new file mode 100644 index 00000000000..ca03e70ea11 --- /dev/null +++ b/vendor/golang.org/x/mod/modfile/rule.go @@ -0,0 +1,1468 @@ +// Copyright 2018 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 modfile implements a parser and formatter for go.mod files. +// +// The go.mod syntax is described in +// https://golang.org/cmd/go/#hdr-The_go_mod_file. +// +// The Parse and ParseLax functions both parse a go.mod file and return an +// abstract syntax tree. ParseLax ignores unknown statements and may be used to +// parse go.mod files that may have been developed with newer versions of Go. +// +// The File struct returned by Parse and ParseLax represent an abstract +// go.mod file. File has several methods like AddNewRequire and DropReplace +// that can be used to programmatically edit a file. +// +// The Format function formats a File back to a byte slice which can be +// written to a file. +package modfile + +import ( + "errors" + "fmt" + "path/filepath" + "sort" + "strconv" + "strings" + "unicode" + + "golang.org/x/mod/internal/lazyregexp" + "golang.org/x/mod/module" + "golang.org/x/mod/semver" +) + +// A File is the parsed, interpreted form of a go.mod file. +type File struct { + Module *Module + Go *Go + Require []*Require + Exclude []*Exclude + Replace []*Replace + Retract []*Retract + + Syntax *FileSyntax +} + +// A Module is the module statement. +type Module struct { + Mod module.Version + Deprecated string + Syntax *Line +} + +// A Go is the go statement. +type Go struct { + Version string // "1.23" + Syntax *Line +} + +// An Exclude is a single exclude statement. +type Exclude struct { + Mod module.Version + Syntax *Line +} + +// A Replace is a single replace statement. +type Replace struct { + Old module.Version + New module.Version + Syntax *Line +} + +// A Retract is a single retract statement. +type Retract struct { + VersionInterval + Rationale string + Syntax *Line +} + +// A VersionInterval represents a range of versions with upper and lower bounds. +// Intervals are closed: both bounds are included. When Low is equal to High, +// the interval may refer to a single version ('v1.2.3') or an interval +// ('[v1.2.3, v1.2.3]'); both have the same representation. +type VersionInterval struct { + Low, High string +} + +// A Require is a single require statement. +type Require struct { + Mod module.Version + Indirect bool // has "// indirect" comment + Syntax *Line +} + +func (r *Require) markRemoved() { + r.Syntax.markRemoved() + *r = Require{} +} + +func (r *Require) setVersion(v string) { + r.Mod.Version = v + + if line := r.Syntax; len(line.Token) > 0 { + if line.InBlock { + // If the line is preceded by an empty line, remove it; see + // https://golang.org/issue/33779. + if len(line.Comments.Before) == 1 && len(line.Comments.Before[0].Token) == 0 { + line.Comments.Before = line.Comments.Before[:0] + } + if len(line.Token) >= 2 { // example.com v1.2.3 + line.Token[1] = v + } + } else { + if len(line.Token) >= 3 { // require example.com v1.2.3 + line.Token[2] = v + } + } + } +} + +// setIndirect sets line to have (or not have) a "// indirect" comment. +func (r *Require) setIndirect(indirect bool) { + r.Indirect = indirect + line := r.Syntax + if isIndirect(line) == indirect { + return + } + if indirect { + // Adding comment. + if len(line.Suffix) == 0 { + // New comment. + line.Suffix = []Comment{{Token: "// indirect", Suffix: true}} + return + } + + com := &line.Suffix[0] + text := strings.TrimSpace(strings.TrimPrefix(com.Token, string(slashSlash))) + if text == "" { + // Empty comment. + com.Token = "// indirect" + return + } + + // Insert at beginning of existing comment. + com.Token = "// indirect; " + text + return + } + + // Removing comment. + f := strings.TrimSpace(strings.TrimPrefix(line.Suffix[0].Token, string(slashSlash))) + if f == "indirect" { + // Remove whole comment. + line.Suffix = nil + return + } + + // Remove comment prefix. + com := &line.Suffix[0] + i := strings.Index(com.Token, "indirect;") + com.Token = "//" + com.Token[i+len("indirect;"):] +} + +// isIndirect reports whether line has a "// indirect" comment, +// meaning it is in go.mod only for its effect on indirect dependencies, +// so that it can be dropped entirely once the effective version of the +// indirect dependency reaches the given minimum version. +func isIndirect(line *Line) bool { + if len(line.Suffix) == 0 { + return false + } + f := strings.Fields(strings.TrimPrefix(line.Suffix[0].Token, string(slashSlash))) + return (len(f) == 1 && f[0] == "indirect" || len(f) > 1 && f[0] == "indirect;") +} + +func (f *File) AddModuleStmt(path string) error { + if f.Syntax == nil { + f.Syntax = new(FileSyntax) + } + if f.Module == nil { + f.Module = &Module{ + Mod: module.Version{Path: path}, + Syntax: f.Syntax.addLine(nil, "module", AutoQuote(path)), + } + } else { + f.Module.Mod.Path = path + f.Syntax.updateLine(f.Module.Syntax, "module", AutoQuote(path)) + } + return nil +} + +func (f *File) AddComment(text string) { + if f.Syntax == nil { + f.Syntax = new(FileSyntax) + } + f.Syntax.Stmt = append(f.Syntax.Stmt, &CommentBlock{ + Comments: Comments{ + Before: []Comment{ + { + Token: text, + }, + }, + }, + }) +} + +type VersionFixer func(path, version string) (string, error) + +// errDontFix is returned by a VersionFixer to indicate the version should be +// left alone, even if it's not canonical. +var dontFixRetract VersionFixer = func(_, vers string) (string, error) { + return vers, nil +} + +// Parse parses and returns a go.mod file. +// +// file is the name of the file, used in positions and errors. +// +// data is the content of the file. +// +// fix is an optional function that canonicalizes module versions. +// If fix is nil, all module versions must be canonical (module.CanonicalVersion +// must return the same string). +func Parse(file string, data []byte, fix VersionFixer) (*File, error) { + return parseToFile(file, data, fix, true) +} + +// ParseLax is like Parse but ignores unknown statements. +// It is used when parsing go.mod files other than the main module, +// under the theory that most statement types we add in the future will +// only apply in the main module, like exclude and replace, +// and so we get better gradual deployments if old go commands +// simply ignore those statements when found in go.mod files +// in dependencies. +func ParseLax(file string, data []byte, fix VersionFixer) (*File, error) { + return parseToFile(file, data, fix, false) +} + +func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (parsed *File, err error) { + fs, err := parse(file, data) + if err != nil { + return nil, err + } + f := &File{ + Syntax: fs, + } + var errs ErrorList + + // fix versions in retract directives after the file is parsed. + // We need the module path to fix versions, and it might be at the end. + defer func() { + oldLen := len(errs) + f.fixRetract(fix, &errs) + if len(errs) > oldLen { + parsed, err = nil, errs + } + }() + + for _, x := range fs.Stmt { + switch x := x.(type) { + case *Line: + f.add(&errs, nil, x, x.Token[0], x.Token[1:], fix, strict) + + case *LineBlock: + if len(x.Token) > 1 { + if strict { + errs = append(errs, Error{ + Filename: file, + Pos: x.Start, + Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")), + }) + } + continue + } + switch x.Token[0] { + default: + if strict { + errs = append(errs, Error{ + Filename: file, + Pos: x.Start, + Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")), + }) + } + continue + case "module", "require", "exclude", "replace", "retract": + for _, l := range x.Line { + f.add(&errs, x, l, x.Token[0], l.Token, fix, strict) + } + } + } + } + + if len(errs) > 0 { + return nil, errs + } + return f, nil +} + +var GoVersionRE = lazyregexp.New(`^([1-9][0-9]*)\.(0|[1-9][0-9]*)$`) +var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9].*)$`) + +func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, args []string, fix VersionFixer, strict bool) { + // If strict is false, this module is a dependency. + // We ignore all unknown directives as well as main-module-only + // directives like replace and exclude. It will work better for + // forward compatibility if we can depend on modules that have unknown + // statements (presumed relevant only when acting as the main module) + // and simply ignore those statements. + if !strict { + switch verb { + case "go", "module", "retract", "require": + // want these even for dependency go.mods + default: + return + } + } + + wrapModPathError := func(modPath string, err error) { + *errs = append(*errs, Error{ + Filename: f.Syntax.Name, + Pos: line.Start, + ModPath: modPath, + Verb: verb, + Err: err, + }) + } + wrapError := func(err error) { + *errs = append(*errs, Error{ + Filename: f.Syntax.Name, + Pos: line.Start, + Err: err, + }) + } + errorf := func(format string, args ...interface{}) { + wrapError(fmt.Errorf(format, args...)) + } + + switch verb { + default: + errorf("unknown directive: %s", verb) + + case "go": + if f.Go != nil { + errorf("repeated go statement") + return + } + if len(args) != 1 { + errorf("go directive expects exactly one argument") + return + } else if !GoVersionRE.MatchString(args[0]) { + fixed := false + if !strict { + if m := laxGoVersionRE.FindStringSubmatch(args[0]); m != nil { + args[0] = m[1] + fixed = true + } + } + if !fixed { + errorf("invalid go version '%s': must match format 1.23", args[0]) + return + } + } + + f.Go = &Go{Syntax: line} + f.Go.Version = args[0] + + case "module": + if f.Module != nil { + errorf("repeated module statement") + return + } + deprecated := parseDeprecation(block, line) + f.Module = &Module{ + Syntax: line, + Deprecated: deprecated, + } + if len(args) != 1 { + errorf("usage: module module/path") + return + } + s, err := parseString(&args[0]) + if err != nil { + errorf("invalid quoted string: %v", err) + return + } + f.Module.Mod = module.Version{Path: s} + + case "require", "exclude": + if len(args) != 2 { + errorf("usage: %s module/path v1.2.3", verb) + return + } + s, err := parseString(&args[0]) + if err != nil { + errorf("invalid quoted string: %v", err) + return + } + v, err := parseVersion(verb, s, &args[1], fix) + if err != nil { + wrapError(err) + return + } + pathMajor, err := modulePathMajor(s) + if err != nil { + wrapError(err) + return + } + if err := module.CheckPathMajor(v, pathMajor); err != nil { + wrapModPathError(s, err) + return + } + if verb == "require" { + f.Require = append(f.Require, &Require{ + Mod: module.Version{Path: s, Version: v}, + Syntax: line, + Indirect: isIndirect(line), + }) + } else { + f.Exclude = append(f.Exclude, &Exclude{ + Mod: module.Version{Path: s, Version: v}, + Syntax: line, + }) + } + + case "replace": + arrow := 2 + if len(args) >= 2 && args[1] == "=>" { + arrow = 1 + } + if len(args) < arrow+2 || len(args) > arrow+3 || args[arrow] != "=>" { + errorf("usage: %s module/path [v1.2.3] => other/module v1.4\n\t or %s module/path [v1.2.3] => ../local/directory", verb, verb) + return + } + s, err := parseString(&args[0]) + if err != nil { + errorf("invalid quoted string: %v", err) + return + } + pathMajor, err := modulePathMajor(s) + if err != nil { + wrapModPathError(s, err) + return + } + var v string + if arrow == 2 { + v, err = parseVersion(verb, s, &args[1], fix) + if err != nil { + wrapError(err) + return + } + if err := module.CheckPathMajor(v, pathMajor); err != nil { + wrapModPathError(s, err) + return + } + } + ns, err := parseString(&args[arrow+1]) + if err != nil { + errorf("invalid quoted string: %v", err) + return + } + nv := "" + if len(args) == arrow+2 { + if !IsDirectoryPath(ns) { + errorf("replacement module without version must be directory path (rooted or starting with ./ or ../)") + return + } + if filepath.Separator == '/' && strings.Contains(ns, `\`) { + errorf("replacement directory appears to be Windows path (on a non-windows system)") + return + } + } + if len(args) == arrow+3 { + nv, err = parseVersion(verb, ns, &args[arrow+2], fix) + if err != nil { + wrapError(err) + return + } + if IsDirectoryPath(ns) { + errorf("replacement module directory path %q cannot have version", ns) + return + } + } + f.Replace = append(f.Replace, &Replace{ + Old: module.Version{Path: s, Version: v}, + New: module.Version{Path: ns, Version: nv}, + Syntax: line, + }) + + case "retract": + rationale := parseDirectiveComment(block, line) + vi, err := parseVersionInterval(verb, "", &args, dontFixRetract) + if err != nil { + if strict { + wrapError(err) + return + } else { + // Only report errors parsing intervals in the main module. We may + // support additional syntax in the future, such as open and half-open + // intervals. Those can't be supported now, because they break the + // go.mod parser, even in lax mode. + return + } + } + if len(args) > 0 && strict { + // In the future, there may be additional information after the version. + errorf("unexpected token after version: %q", args[0]) + return + } + retract := &Retract{ + VersionInterval: vi, + Rationale: rationale, + Syntax: line, + } + f.Retract = append(f.Retract, retract) + } +} + +// fixRetract applies fix to each retract directive in f, appending any errors +// to errs. +// +// Most versions are fixed as we parse the file, but for retract directives, +// the relevant module path is the one specified with the module directive, +// and that might appear at the end of the file (or not at all). +func (f *File) fixRetract(fix VersionFixer, errs *ErrorList) { + if fix == nil { + return + } + path := "" + if f.Module != nil { + path = f.Module.Mod.Path + } + var r *Retract + wrapError := func(err error) { + *errs = append(*errs, Error{ + Filename: f.Syntax.Name, + Pos: r.Syntax.Start, + Err: err, + }) + } + + for _, r = range f.Retract { + if path == "" { + wrapError(errors.New("no module directive found, so retract cannot be used")) + return // only print the first one of these + } + + args := r.Syntax.Token + if args[0] == "retract" { + args = args[1:] + } + vi, err := parseVersionInterval("retract", path, &args, fix) + if err != nil { + wrapError(err) + } + r.VersionInterval = vi + } +} + +// IsDirectoryPath reports whether the given path should be interpreted +// as a directory path. Just like on the go command line, relative paths +// and rooted paths are directory paths; the rest are module paths. +func IsDirectoryPath(ns string) bool { + // Because go.mod files can move from one system to another, + // we check all known path syntaxes, both Unix and Windows. + return strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, "/") || + strings.HasPrefix(ns, `.\`) || strings.HasPrefix(ns, `..\`) || strings.HasPrefix(ns, `\`) || + len(ns) >= 2 && ('A' <= ns[0] && ns[0] <= 'Z' || 'a' <= ns[0] && ns[0] <= 'z') && ns[1] == ':' +} + +// MustQuote reports whether s must be quoted in order to appear as +// a single token in a go.mod line. +func MustQuote(s string) bool { + for _, r := range s { + switch r { + case ' ', '"', '\'', '`': + return true + + case '(', ')', '[', ']', '{', '}', ',': + if len(s) > 1 { + return true + } + + default: + if !unicode.IsPrint(r) { + return true + } + } + } + return s == "" || strings.Contains(s, "//") || strings.Contains(s, "/*") +} + +// AutoQuote returns s or, if quoting is required for s to appear in a go.mod, +// the quotation of s. +func AutoQuote(s string) string { + if MustQuote(s) { + return strconv.Quote(s) + } + return s +} + +func parseVersionInterval(verb string, path string, args *[]string, fix VersionFixer) (VersionInterval, error) { + toks := *args + if len(toks) == 0 || toks[0] == "(" { + return VersionInterval{}, fmt.Errorf("expected '[' or version") + } + if toks[0] != "[" { + v, err := parseVersion(verb, path, &toks[0], fix) + if err != nil { + return VersionInterval{}, err + } + *args = toks[1:] + return VersionInterval{Low: v, High: v}, nil + } + toks = toks[1:] + + if len(toks) == 0 { + return VersionInterval{}, fmt.Errorf("expected version after '['") + } + low, err := parseVersion(verb, path, &toks[0], fix) + if err != nil { + return VersionInterval{}, err + } + toks = toks[1:] + + if len(toks) == 0 || toks[0] != "," { + return VersionInterval{}, fmt.Errorf("expected ',' after version") + } + toks = toks[1:] + + if len(toks) == 0 { + return VersionInterval{}, fmt.Errorf("expected version after ','") + } + high, err := parseVersion(verb, path, &toks[0], fix) + if err != nil { + return VersionInterval{}, err + } + toks = toks[1:] + + if len(toks) == 0 || toks[0] != "]" { + return VersionInterval{}, fmt.Errorf("expected ']' after version") + } + toks = toks[1:] + + *args = toks + return VersionInterval{Low: low, High: high}, nil +} + +func parseString(s *string) (string, error) { + t := *s + if strings.HasPrefix(t, `"`) { + var err error + if t, err = strconv.Unquote(t); err != nil { + return "", err + } + } else if strings.ContainsAny(t, "\"'`") { + // Other quotes are reserved both for possible future expansion + // and to avoid confusion. For example if someone types 'x' + // we want that to be a syntax error and not a literal x in literal quotation marks. + return "", fmt.Errorf("unquoted string cannot contain quote") + } + *s = AutoQuote(t) + return t, nil +} + +var deprecatedRE = lazyregexp.New(`(?s)(?:^|\n\n)Deprecated: *(.*?)(?:$|\n\n)`) + +// parseDeprecation extracts the text of comments on a "module" directive and +// extracts a deprecation message from that. +// +// A deprecation message is contained in a paragraph within a block of comments +// that starts with "Deprecated:" (case sensitive). The message runs until the +// end of the paragraph and does not include the "Deprecated:" prefix. If the +// comment block has multiple paragraphs that start with "Deprecated:", +// parseDeprecation returns the message from the first. +func parseDeprecation(block *LineBlock, line *Line) string { + text := parseDirectiveComment(block, line) + m := deprecatedRE.FindStringSubmatch(text) + if m == nil { + return "" + } + return m[1] +} + +// parseDirectiveComment extracts the text of comments on a directive. +// If the directive's line does not have comments and is part of a block that +// does have comments, the block's comments are used. +func parseDirectiveComment(block *LineBlock, line *Line) string { + comments := line.Comment() + if block != nil && len(comments.Before) == 0 && len(comments.Suffix) == 0 { + comments = block.Comment() + } + groups := [][]Comment{comments.Before, comments.Suffix} + var lines []string + for _, g := range groups { + for _, c := range g { + if !strings.HasPrefix(c.Token, "//") { + continue // blank line + } + lines = append(lines, strings.TrimSpace(strings.TrimPrefix(c.Token, "//"))) + } + } + return strings.Join(lines, "\n") +} + +type ErrorList []Error + +func (e ErrorList) Error() string { + errStrs := make([]string, len(e)) + for i, err := range e { + errStrs[i] = err.Error() + } + return strings.Join(errStrs, "\n") +} + +type Error struct { + Filename string + Pos Position + Verb string + ModPath string + Err error +} + +func (e *Error) Error() string { + var pos string + if e.Pos.LineRune > 1 { + // Don't print LineRune if it's 1 (beginning of line). + // It's always 1 except in scanner errors, which are rare. + pos = fmt.Sprintf("%s:%d:%d: ", e.Filename, e.Pos.Line, e.Pos.LineRune) + } else if e.Pos.Line > 0 { + pos = fmt.Sprintf("%s:%d: ", e.Filename, e.Pos.Line) + } else if e.Filename != "" { + pos = fmt.Sprintf("%s: ", e.Filename) + } + + var directive string + if e.ModPath != "" { + directive = fmt.Sprintf("%s %s: ", e.Verb, e.ModPath) + } else if e.Verb != "" { + directive = fmt.Sprintf("%s: ", e.Verb) + } + + return pos + directive + e.Err.Error() +} + +func (e *Error) Unwrap() error { return e.Err } + +func parseVersion(verb string, path string, s *string, fix VersionFixer) (string, error) { + t, err := parseString(s) + if err != nil { + return "", &Error{ + Verb: verb, + ModPath: path, + Err: &module.InvalidVersionError{ + Version: *s, + Err: err, + }, + } + } + if fix != nil { + fixed, err := fix(path, t) + if err != nil { + if err, ok := err.(*module.ModuleError); ok { + return "", &Error{ + Verb: verb, + ModPath: path, + Err: err.Err, + } + } + return "", err + } + t = fixed + } else { + cv := module.CanonicalVersion(t) + if cv == "" { + return "", &Error{ + Verb: verb, + ModPath: path, + Err: &module.InvalidVersionError{ + Version: t, + Err: errors.New("must be of the form v1.2.3"), + }, + } + } + t = cv + } + *s = t + return *s, nil +} + +func modulePathMajor(path string) (string, error) { + _, major, ok := module.SplitPathVersion(path) + if !ok { + return "", fmt.Errorf("invalid module path") + } + return major, nil +} + +func (f *File) Format() ([]byte, error) { + return Format(f.Syntax), nil +} + +// Cleanup cleans up the file f after any edit operations. +// To avoid quadratic behavior, modifications like DropRequire +// clear the entry but do not remove it from the slice. +// Cleanup cleans out all the cleared entries. +func (f *File) Cleanup() { + w := 0 + for _, r := range f.Require { + if r.Mod.Path != "" { + f.Require[w] = r + w++ + } + } + f.Require = f.Require[:w] + + w = 0 + for _, x := range f.Exclude { + if x.Mod.Path != "" { + f.Exclude[w] = x + w++ + } + } + f.Exclude = f.Exclude[:w] + + w = 0 + for _, r := range f.Replace { + if r.Old.Path != "" { + f.Replace[w] = r + w++ + } + } + f.Replace = f.Replace[:w] + + w = 0 + for _, r := range f.Retract { + if r.Low != "" || r.High != "" { + f.Retract[w] = r + w++ + } + } + f.Retract = f.Retract[:w] + + f.Syntax.Cleanup() +} + +func (f *File) AddGoStmt(version string) error { + if !GoVersionRE.MatchString(version) { + return fmt.Errorf("invalid language version string %q", version) + } + if f.Go == nil { + var hint Expr + if f.Module != nil && f.Module.Syntax != nil { + hint = f.Module.Syntax + } + f.Go = &Go{ + Version: version, + Syntax: f.Syntax.addLine(hint, "go", version), + } + } else { + f.Go.Version = version + f.Syntax.updateLine(f.Go.Syntax, "go", version) + } + return nil +} + +// AddRequire sets the first require line for path to version vers, +// preserving any existing comments for that line and removing all +// other lines for path. +// +// If no line currently exists for path, AddRequire adds a new line +// at the end of the last require block. +func (f *File) AddRequire(path, vers string) error { + need := true + for _, r := range f.Require { + if r.Mod.Path == path { + if need { + r.Mod.Version = vers + f.Syntax.updateLine(r.Syntax, "require", AutoQuote(path), vers) + need = false + } else { + r.Syntax.markRemoved() + *r = Require{} + } + } + } + + if need { + f.AddNewRequire(path, vers, false) + } + return nil +} + +// AddNewRequire adds a new require line for path at version vers at the end of +// the last require block, regardless of any existing require lines for path. +func (f *File) AddNewRequire(path, vers string, indirect bool) { + line := f.Syntax.addLine(nil, "require", AutoQuote(path), vers) + r := &Require{ + Mod: module.Version{Path: path, Version: vers}, + Syntax: line, + } + r.setIndirect(indirect) + f.Require = append(f.Require, r) +} + +// SetRequire updates the requirements of f to contain exactly req, preserving +// the existing block structure and line comment contents (except for 'indirect' +// markings) for the first requirement on each named module path. +// +// The Syntax field is ignored for the requirements in req. +// +// Any requirements not already present in the file are added to the block +// containing the last require line. +// +// The requirements in req must specify at most one distinct version for each +// module path. +// +// If any existing requirements may be removed, the caller should call Cleanup +// after all edits are complete. +func (f *File) SetRequire(req []*Require) { + type elem struct { + version string + indirect bool + } + need := make(map[string]elem) + for _, r := range req { + if prev, dup := need[r.Mod.Path]; dup && prev.version != r.Mod.Version { + panic(fmt.Errorf("SetRequire called with conflicting versions for path %s (%s and %s)", r.Mod.Path, prev.version, r.Mod.Version)) + } + need[r.Mod.Path] = elem{r.Mod.Version, r.Indirect} + } + + // Update or delete the existing Require entries to preserve + // only the first for each module path in req. + for _, r := range f.Require { + e, ok := need[r.Mod.Path] + if ok { + r.setVersion(e.version) + r.setIndirect(e.indirect) + } else { + r.markRemoved() + } + delete(need, r.Mod.Path) + } + + // Add new entries in the last block of the file for any paths that weren't + // already present. + // + // This step is nondeterministic, but the final result will be deterministic + // because we will sort the block. + for path, e := range need { + f.AddNewRequire(path, e.version, e.indirect) + } + + f.SortBlocks() +} + +// SetRequireSeparateIndirect updates the requirements of f to contain the given +// requirements. Comment contents (except for 'indirect' markings) are retained +// from the first existing requirement for each module path. Like SetRequire, +// SetRequireSeparateIndirect adds requirements for new paths in req, +// updates the version and "// indirect" comment on existing requirements, +// and deletes requirements on paths not in req. Existing duplicate requirements +// are deleted. +// +// As its name suggests, SetRequireSeparateIndirect puts direct and indirect +// requirements into two separate blocks, one containing only direct +// requirements, and the other containing only indirect requirements. +// SetRequireSeparateIndirect may move requirements between these two blocks +// when their indirect markings change. However, SetRequireSeparateIndirect +// won't move requirements from other blocks, especially blocks with comments. +// +// If the file initially has one uncommented block of requirements, +// SetRequireSeparateIndirect will split it into a direct-only and indirect-only +// block. This aids in the transition to separate blocks. +func (f *File) SetRequireSeparateIndirect(req []*Require) { + // hasComments returns whether a line or block has comments + // other than "indirect". + hasComments := func(c Comments) bool { + return len(c.Before) > 0 || len(c.After) > 0 || len(c.Suffix) > 1 || + (len(c.Suffix) == 1 && + strings.TrimSpace(strings.TrimPrefix(c.Suffix[0].Token, string(slashSlash))) != "indirect") + } + + // moveReq adds r to block. If r was in another block, moveReq deletes + // it from that block and transfers its comments. + moveReq := func(r *Require, block *LineBlock) { + var line *Line + if r.Syntax == nil { + line = &Line{Token: []string{AutoQuote(r.Mod.Path), r.Mod.Version}} + r.Syntax = line + if r.Indirect { + r.setIndirect(true) + } + } else { + line = new(Line) + *line = *r.Syntax + if !line.InBlock && len(line.Token) > 0 && line.Token[0] == "require" { + line.Token = line.Token[1:] + } + r.Syntax.Token = nil // Cleanup will delete the old line. + r.Syntax = line + } + line.InBlock = true + block.Line = append(block.Line, line) + } + + // Examine existing require lines and blocks. + var ( + // We may insert new requirements into the last uncommented + // direct-only and indirect-only blocks. We may also move requirements + // to the opposite block if their indirect markings change. + lastDirectIndex = -1 + lastIndirectIndex = -1 + + // If there are no direct-only or indirect-only blocks, a new block may + // be inserted after the last require line or block. + lastRequireIndex = -1 + + // If there's only one require line or block, and it's uncommented, + // we'll move its requirements to the direct-only or indirect-only blocks. + requireLineOrBlockCount = 0 + + // Track the block each requirement belongs to (if any) so we can + // move them later. + lineToBlock = make(map[*Line]*LineBlock) + ) + for i, stmt := range f.Syntax.Stmt { + switch stmt := stmt.(type) { + case *Line: + if len(stmt.Token) == 0 || stmt.Token[0] != "require" { + continue + } + lastRequireIndex = i + requireLineOrBlockCount++ + if !hasComments(stmt.Comments) { + if isIndirect(stmt) { + lastIndirectIndex = i + } else { + lastDirectIndex = i + } + } + + case *LineBlock: + if len(stmt.Token) == 0 || stmt.Token[0] != "require" { + continue + } + lastRequireIndex = i + requireLineOrBlockCount++ + allDirect := len(stmt.Line) > 0 && !hasComments(stmt.Comments) + allIndirect := len(stmt.Line) > 0 && !hasComments(stmt.Comments) + for _, line := range stmt.Line { + lineToBlock[line] = stmt + if hasComments(line.Comments) { + allDirect = false + allIndirect = false + } else if isIndirect(line) { + allDirect = false + } else { + allIndirect = false + } + } + if allDirect { + lastDirectIndex = i + } + if allIndirect { + lastIndirectIndex = i + } + } + } + + oneFlatUncommentedBlock := requireLineOrBlockCount == 1 && + !hasComments(*f.Syntax.Stmt[lastRequireIndex].Comment()) + + // Create direct and indirect blocks if needed. Convert lines into blocks + // if needed. If we end up with an empty block or a one-line block, + // Cleanup will delete it or convert it to a line later. + insertBlock := func(i int) *LineBlock { + block := &LineBlock{Token: []string{"require"}} + f.Syntax.Stmt = append(f.Syntax.Stmt, nil) + copy(f.Syntax.Stmt[i+1:], f.Syntax.Stmt[i:]) + f.Syntax.Stmt[i] = block + return block + } + + ensureBlock := func(i int) *LineBlock { + switch stmt := f.Syntax.Stmt[i].(type) { + case *LineBlock: + return stmt + case *Line: + block := &LineBlock{ + Token: []string{"require"}, + Line: []*Line{stmt}, + } + stmt.Token = stmt.Token[1:] // remove "require" + stmt.InBlock = true + f.Syntax.Stmt[i] = block + return block + default: + panic(fmt.Sprintf("unexpected statement: %v", stmt)) + } + } + + var lastDirectBlock *LineBlock + if lastDirectIndex < 0 { + if lastIndirectIndex >= 0 { + lastDirectIndex = lastIndirectIndex + lastIndirectIndex++ + } else if lastRequireIndex >= 0 { + lastDirectIndex = lastRequireIndex + 1 + } else { + lastDirectIndex = len(f.Syntax.Stmt) + } + lastDirectBlock = insertBlock(lastDirectIndex) + } else { + lastDirectBlock = ensureBlock(lastDirectIndex) + } + + var lastIndirectBlock *LineBlock + if lastIndirectIndex < 0 { + lastIndirectIndex = lastDirectIndex + 1 + lastIndirectBlock = insertBlock(lastIndirectIndex) + } else { + lastIndirectBlock = ensureBlock(lastIndirectIndex) + } + + // Delete requirements we don't want anymore. + // Update versions and indirect comments on requirements we want to keep. + // If a requirement is in last{Direct,Indirect}Block with the wrong + // indirect marking after this, or if the requirement is in an single + // uncommented mixed block (oneFlatUncommentedBlock), move it to the + // correct block. + // + // Some blocks may be empty after this. Cleanup will remove them. + need := make(map[string]*Require) + for _, r := range req { + need[r.Mod.Path] = r + } + have := make(map[string]*Require) + for _, r := range f.Require { + path := r.Mod.Path + if need[path] == nil || have[path] != nil { + // Requirement not needed, or duplicate requirement. Delete. + r.markRemoved() + continue + } + have[r.Mod.Path] = r + r.setVersion(need[path].Mod.Version) + r.setIndirect(need[path].Indirect) + if need[path].Indirect && + (oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastDirectBlock) { + moveReq(r, lastIndirectBlock) + } else if !need[path].Indirect && + (oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastIndirectBlock) { + moveReq(r, lastDirectBlock) + } + } + + // Add new requirements. + for path, r := range need { + if have[path] == nil { + if r.Indirect { + moveReq(r, lastIndirectBlock) + } else { + moveReq(r, lastDirectBlock) + } + f.Require = append(f.Require, r) + } + } + + f.SortBlocks() +} + +func (f *File) DropRequire(path string) error { + for _, r := range f.Require { + if r.Mod.Path == path { + r.Syntax.markRemoved() + *r = Require{} + } + } + return nil +} + +// AddExclude adds a exclude statement to the mod file. Errors if the provided +// version is not a canonical version string +func (f *File) AddExclude(path, vers string) error { + if err := checkCanonicalVersion(path, vers); err != nil { + return err + } + + var hint *Line + for _, x := range f.Exclude { + if x.Mod.Path == path && x.Mod.Version == vers { + return nil + } + if x.Mod.Path == path { + hint = x.Syntax + } + } + + f.Exclude = append(f.Exclude, &Exclude{Mod: module.Version{Path: path, Version: vers}, Syntax: f.Syntax.addLine(hint, "exclude", AutoQuote(path), vers)}) + return nil +} + +func (f *File) DropExclude(path, vers string) error { + for _, x := range f.Exclude { + if x.Mod.Path == path && x.Mod.Version == vers { + x.Syntax.markRemoved() + *x = Exclude{} + } + } + return nil +} + +func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error { + need := true + old := module.Version{Path: oldPath, Version: oldVers} + new := module.Version{Path: newPath, Version: newVers} + tokens := []string{"replace", AutoQuote(oldPath)} + if oldVers != "" { + tokens = append(tokens, oldVers) + } + tokens = append(tokens, "=>", AutoQuote(newPath)) + if newVers != "" { + tokens = append(tokens, newVers) + } + + var hint *Line + for _, r := range f.Replace { + if r.Old.Path == oldPath && (oldVers == "" || r.Old.Version == oldVers) { + if need { + // Found replacement for old; update to use new. + r.New = new + f.Syntax.updateLine(r.Syntax, tokens...) + need = false + continue + } + // Already added; delete other replacements for same. + r.Syntax.markRemoved() + *r = Replace{} + } + if r.Old.Path == oldPath { + hint = r.Syntax + } + } + if need { + f.Replace = append(f.Replace, &Replace{Old: old, New: new, Syntax: f.Syntax.addLine(hint, tokens...)}) + } + return nil +} + +func (f *File) DropReplace(oldPath, oldVers string) error { + for _, r := range f.Replace { + if r.Old.Path == oldPath && r.Old.Version == oldVers { + r.Syntax.markRemoved() + *r = Replace{} + } + } + return nil +} + +// AddRetract adds a retract statement to the mod file. Errors if the provided +// version interval does not consist of canonical version strings +func (f *File) AddRetract(vi VersionInterval, rationale string) error { + var path string + if f.Module != nil { + path = f.Module.Mod.Path + } + if err := checkCanonicalVersion(path, vi.High); err != nil { + return err + } + if err := checkCanonicalVersion(path, vi.Low); err != nil { + return err + } + + r := &Retract{ + VersionInterval: vi, + } + if vi.Low == vi.High { + r.Syntax = f.Syntax.addLine(nil, "retract", AutoQuote(vi.Low)) + } else { + r.Syntax = f.Syntax.addLine(nil, "retract", "[", AutoQuote(vi.Low), ",", AutoQuote(vi.High), "]") + } + if rationale != "" { + for _, line := range strings.Split(rationale, "\n") { + com := Comment{Token: "// " + line} + r.Syntax.Comment().Before = append(r.Syntax.Comment().Before, com) + } + } + return nil +} + +func (f *File) DropRetract(vi VersionInterval) error { + for _, r := range f.Retract { + if r.VersionInterval == vi { + r.Syntax.markRemoved() + *r = Retract{} + } + } + return nil +} + +func (f *File) SortBlocks() { + f.removeDups() // otherwise sorting is unsafe + + for _, stmt := range f.Syntax.Stmt { + block, ok := stmt.(*LineBlock) + if !ok { + continue + } + less := lineLess + if block.Token[0] == "retract" { + less = lineRetractLess + } + sort.SliceStable(block.Line, func(i, j int) bool { + return less(block.Line[i], block.Line[j]) + }) + } +} + +// removeDups removes duplicate exclude and replace directives. +// +// Earlier exclude directives take priority. +// +// Later replace directives take priority. +// +// require directives are not de-duplicated. That's left up to higher-level +// logic (MVS). +// +// retract directives are not de-duplicated since comments are +// meaningful, and versions may be retracted multiple times. +func (f *File) removeDups() { + kill := make(map[*Line]bool) + + // Remove duplicate excludes. + haveExclude := make(map[module.Version]bool) + for _, x := range f.Exclude { + if haveExclude[x.Mod] { + kill[x.Syntax] = true + continue + } + haveExclude[x.Mod] = true + } + var excl []*Exclude + for _, x := range f.Exclude { + if !kill[x.Syntax] { + excl = append(excl, x) + } + } + f.Exclude = excl + + // Remove duplicate replacements. + // Later replacements take priority over earlier ones. + haveReplace := make(map[module.Version]bool) + for i := len(f.Replace) - 1; i >= 0; i-- { + x := f.Replace[i] + if haveReplace[x.Old] { + kill[x.Syntax] = true + continue + } + haveReplace[x.Old] = true + } + var repl []*Replace + for _, x := range f.Replace { + if !kill[x.Syntax] { + repl = append(repl, x) + } + } + f.Replace = repl + + // Duplicate require and retract directives are not removed. + + // Drop killed statements from the syntax tree. + var stmts []Expr + for _, stmt := range f.Syntax.Stmt { + switch stmt := stmt.(type) { + case *Line: + if kill[stmt] { + continue + } + case *LineBlock: + var lines []*Line + for _, line := range stmt.Line { + if !kill[line] { + lines = append(lines, line) + } + } + stmt.Line = lines + if len(lines) == 0 { + continue + } + } + stmts = append(stmts, stmt) + } + f.Syntax.Stmt = stmts +} + +// lineLess returns whether li should be sorted before lj. It sorts +// lexicographically without assigning any special meaning to tokens. +func lineLess(li, lj *Line) bool { + for k := 0; k < len(li.Token) && k < len(lj.Token); k++ { + if li.Token[k] != lj.Token[k] { + return li.Token[k] < lj.Token[k] + } + } + return len(li.Token) < len(lj.Token) +} + +// lineRetractLess returns whether li should be sorted before lj for lines in +// a "retract" block. It treats each line as a version interval. Single versions +// are compared as if they were intervals with the same low and high version. +// Intervals are sorted in descending order, first by low version, then by +// high version, using semver.Compare. +func lineRetractLess(li, lj *Line) bool { + interval := func(l *Line) VersionInterval { + if len(l.Token) == 1 { + return VersionInterval{Low: l.Token[0], High: l.Token[0]} + } else if len(l.Token) == 5 && l.Token[0] == "[" && l.Token[2] == "," && l.Token[4] == "]" { + return VersionInterval{Low: l.Token[1], High: l.Token[3]} + } else { + // Line in unknown format. Treat as an invalid version. + return VersionInterval{} + } + } + vii := interval(li) + vij := interval(lj) + if cmp := semver.Compare(vii.Low, vij.Low); cmp != 0 { + return cmp > 0 + } + return semver.Compare(vii.High, vij.High) > 0 +} + +// checkCanonicalVersion returns a non-nil error if vers is not a canonical +// version string or does not match the major version of path. +// +// If path is non-empty, the error text suggests a format with a major version +// corresponding to the path. +func checkCanonicalVersion(path, vers string) error { + _, pathMajor, pathMajorOk := module.SplitPathVersion(path) + + if vers == "" || vers != module.CanonicalVersion(vers) { + if pathMajor == "" { + return &module.InvalidVersionError{ + Version: vers, + Err: fmt.Errorf("must be of the form v1.2.3"), + } + } + return &module.InvalidVersionError{ + Version: vers, + Err: fmt.Errorf("must be of the form %s.2.3", module.PathMajorPrefix(pathMajor)), + } + } + + if pathMajorOk { + if err := module.CheckPathMajor(vers, pathMajor); err != nil { + if pathMajor == "" { + // In this context, the user probably wrote "v2.3.4" when they meant + // "v2.3.4+incompatible". Suggest that instead of "v0 or v1". + return &module.InvalidVersionError{ + Version: vers, + Err: fmt.Errorf("should be %s+incompatible (or module %s/%v)", vers, path, semver.Major(vers)), + } + } + return err + } + } + + return nil +} diff --git a/vendor/golang.org/x/mod/module/module.go b/vendor/golang.org/x/mod/module/module.go new file mode 100644 index 00000000000..ba97ac356e9 --- /dev/null +++ b/vendor/golang.org/x/mod/module/module.go @@ -0,0 +1,844 @@ +// Copyright 2018 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 module defines the module.Version type along with support code. +// +// The module.Version type is a simple Path, Version pair: +// +// type Version struct { +// Path string +// Version string +// } +// +// There are no restrictions imposed directly by use of this structure, +// but additional checking functions, most notably Check, verify that +// a particular path, version pair is valid. +// +// Escaped Paths +// +// Module paths appear as substrings of file system paths +// (in the download cache) and of web server URLs in the proxy protocol. +// In general we cannot rely on file systems to be case-sensitive, +// nor can we rely on web servers, since they read from file systems. +// That is, we cannot rely on the file system to keep rsc.io/QUOTE +// and rsc.io/quote separate. Windows and macOS don't. +// Instead, we must never require two different casings of a file path. +// Because we want the download cache to match the proxy protocol, +// and because we want the proxy protocol to be possible to serve +// from a tree of static files (which might be stored on a case-insensitive +// file system), the proxy protocol must never require two different casings +// of a URL path either. +// +// One possibility would be to make the escaped form be the lowercase +// hexadecimal encoding of the actual path bytes. This would avoid ever +// needing different casings of a file path, but it would be fairly illegible +// to most programmers when those paths appeared in the file system +// (including in file paths in compiler errors and stack traces) +// in web server logs, and so on. Instead, we want a safe escaped form that +// leaves most paths unaltered. +// +// The safe escaped form is to replace every uppercase letter +// with an exclamation mark followed by the letter's lowercase equivalent. +// +// For example, +// +// github.com/Azure/azure-sdk-for-go -> github.com/!azure/azure-sdk-for-go. +// github.com/GoogleCloudPlatform/cloudsql-proxy -> github.com/!google!cloud!platform/cloudsql-proxy +// github.com/Sirupsen/logrus -> github.com/!sirupsen/logrus. +// +// Import paths that avoid upper-case letters are left unchanged. +// Note that because import paths are ASCII-only and avoid various +// problematic punctuation (like : < and >), the escaped form is also ASCII-only +// and avoids the same problematic punctuation. +// +// Import paths have never allowed exclamation marks, so there is no +// need to define how to escape a literal !. +// +// Unicode Restrictions +// +// Today, paths are disallowed from using Unicode. +// +// Although paths are currently disallowed from using Unicode, +// we would like at some point to allow Unicode letters as well, to assume that +// file systems and URLs are Unicode-safe (storing UTF-8), and apply +// the !-for-uppercase convention for escaping them in the file system. +// But there are at least two subtle considerations. +// +// First, note that not all case-fold equivalent distinct runes +// form an upper/lower pair. +// For example, U+004B ('K'), U+006B ('k'), and U+212A ('K' for Kelvin) +// are three distinct runes that case-fold to each other. +// When we do add Unicode letters, we must not assume that upper/lower +// are the only case-equivalent pairs. +// Perhaps the Kelvin symbol would be disallowed entirely, for example. +// Or perhaps it would escape as "!!k", or perhaps as "(212A)". +// +// Second, it would be nice to allow Unicode marks as well as letters, +// but marks include combining marks, and then we must deal not +// only with case folding but also normalization: both U+00E9 ('é') +// and U+0065 U+0301 ('e' followed by combining acute accent) +// look the same on the page and are treated by some file systems +// as the same path. If we do allow Unicode marks in paths, there +// must be some kind of normalization to allow only one canonical +// encoding of any character used in an import path. +package module + +// IMPORTANT NOTE +// +// This file essentially defines the set of valid import paths for the go command. +// There are many subtle considerations, including Unicode ambiguity, +// security, network, and file system representations. +// +// This file also defines the set of valid module path and version combinations, +// another topic with many subtle considerations. +// +// Changes to the semantics in this file require approval from rsc. + +import ( + "fmt" + "path" + "sort" + "strings" + "unicode" + "unicode/utf8" + + "golang.org/x/mod/semver" + errors "golang.org/x/xerrors" +) + +// A Version (for clients, a module.Version) is defined by a module path and version pair. +// These are stored in their plain (unescaped) form. +type Version struct { + // Path is a module path, like "golang.org/x/text" or "rsc.io/quote/v2". + Path string + + // Version is usually a semantic version in canonical form. + // There are three exceptions to this general rule. + // First, the top-level target of a build has no specific version + // and uses Version = "". + // Second, during MVS calculations the version "none" is used + // to represent the decision to take no version of a given module. + // Third, filesystem paths found in "replace" directives are + // represented by a path with an empty version. + Version string `json:",omitempty"` +} + +// String returns a representation of the Version suitable for logging +// (Path@Version, or just Path if Version is empty). +func (m Version) String() string { + if m.Version == "" { + return m.Path + } + return m.Path + "@" + m.Version +} + +// A ModuleError indicates an error specific to a module. +type ModuleError struct { + Path string + Version string + Err error +} + +// VersionError returns a ModuleError derived from a Version and error, +// or err itself if it is already such an error. +func VersionError(v Version, err error) error { + var mErr *ModuleError + if errors.As(err, &mErr) && mErr.Path == v.Path && mErr.Version == v.Version { + return err + } + return &ModuleError{ + Path: v.Path, + Version: v.Version, + Err: err, + } +} + +func (e *ModuleError) Error() string { + if v, ok := e.Err.(*InvalidVersionError); ok { + return fmt.Sprintf("%s@%s: invalid %s: %v", e.Path, v.Version, v.noun(), v.Err) + } + if e.Version != "" { + return fmt.Sprintf("%s@%s: %v", e.Path, e.Version, e.Err) + } + return fmt.Sprintf("module %s: %v", e.Path, e.Err) +} + +func (e *ModuleError) Unwrap() error { return e.Err } + +// An InvalidVersionError indicates an error specific to a version, with the +// module path unknown or specified externally. +// +// A ModuleError may wrap an InvalidVersionError, but an InvalidVersionError +// must not wrap a ModuleError. +type InvalidVersionError struct { + Version string + Pseudo bool + Err error +} + +// noun returns either "version" or "pseudo-version", depending on whether +// e.Version is a pseudo-version. +func (e *InvalidVersionError) noun() string { + if e.Pseudo { + return "pseudo-version" + } + return "version" +} + +func (e *InvalidVersionError) Error() string { + return fmt.Sprintf("%s %q invalid: %s", e.noun(), e.Version, e.Err) +} + +func (e *InvalidVersionError) Unwrap() error { return e.Err } + +// An InvalidPathError indicates a module, import, or file path doesn't +// satisfy all naming constraints. See CheckPath, CheckImportPath, +// and CheckFilePath for specific restrictions. +type InvalidPathError struct { + Kind string // "module", "import", or "file" + Path string + Err error +} + +func (e *InvalidPathError) Error() string { + return fmt.Sprintf("malformed %s path %q: %v", e.Kind, e.Path, e.Err) +} + +func (e *InvalidPathError) Unwrap() error { return e.Err } + +// Check checks that a given module path, version pair is valid. +// In addition to the path being a valid module path +// and the version being a valid semantic version, +// the two must correspond. +// For example, the path "yaml/v2" only corresponds to +// semantic versions beginning with "v2.". +func Check(path, version string) error { + if err := CheckPath(path); err != nil { + return err + } + if !semver.IsValid(version) { + return &ModuleError{ + Path: path, + Err: &InvalidVersionError{Version: version, Err: errors.New("not a semantic version")}, + } + } + _, pathMajor, _ := SplitPathVersion(path) + if err := CheckPathMajor(version, pathMajor); err != nil { + return &ModuleError{Path: path, Err: err} + } + return nil +} + +// firstPathOK reports whether r can appear in the first element of a module path. +// The first element of the path must be an LDH domain name, at least for now. +// To avoid case ambiguity, the domain name must be entirely lower case. +func firstPathOK(r rune) bool { + return r == '-' || r == '.' || + '0' <= r && r <= '9' || + 'a' <= r && r <= 'z' +} + +// modPathOK reports whether r can appear in a module path element. +// Paths can be ASCII letters, ASCII digits, and limited ASCII punctuation: - . _ and ~. +// +// This matches what "go get" has historically recognized in import paths, +// and avoids confusing sequences like '%20' or '+' that would change meaning +// if used in a URL. +// +// TODO(rsc): We would like to allow Unicode letters, but that requires additional +// care in the safe encoding (see "escaped paths" above). +func modPathOK(r rune) bool { + if r < utf8.RuneSelf { + return r == '-' || r == '.' || r == '_' || r == '~' || + '0' <= r && r <= '9' || + 'A' <= r && r <= 'Z' || + 'a' <= r && r <= 'z' + } + return false +} + +// modPathOK reports whether r can appear in a package import path element. +// +// Import paths are intermediate between module paths and file paths: we allow +// disallow characters that would be confusing or ambiguous as arguments to +// 'go get' (such as '@' and ' ' ), but allow certain characters that are +// otherwise-unambiguous on the command line and historically used for some +// binary names (such as '++' as a suffix for compiler binaries and wrappers). +func importPathOK(r rune) bool { + return modPathOK(r) || r == '+' +} + +// fileNameOK reports whether r can appear in a file name. +// For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters. +// If we expand the set of allowed characters here, we have to +// work harder at detecting potential case-folding and normalization collisions. +// See note about "escaped paths" above. +func fileNameOK(r rune) bool { + if r < utf8.RuneSelf { + // Entire set of ASCII punctuation, from which we remove characters: + // ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ + // We disallow some shell special characters: " ' * < > ? ` | + // (Note that some of those are disallowed by the Windows file system as well.) + // We also disallow path separators / : and \ (fileNameOK is only called on path element characters). + // We allow spaces (U+0020) in file names. + const allowed = "!#$%&()+,-.=@[]^_{}~ " + if '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' { + return true + } + for i := 0; i < len(allowed); i++ { + if rune(allowed[i]) == r { + return true + } + } + return false + } + // It may be OK to add more ASCII punctuation here, but only carefully. + // For example Windows disallows < > \, and macOS disallows :, so we must not allow those. + return unicode.IsLetter(r) +} + +// CheckPath checks that a module path is valid. +// A valid module path is a valid import path, as checked by CheckImportPath, +// with three additional constraints. +// First, the leading path element (up to the first slash, if any), +// by convention a domain name, must contain only lower-case ASCII letters, +// ASCII digits, dots (U+002E), and dashes (U+002D); +// it must contain at least one dot and cannot start with a dash. +// Second, for a final path element of the form /vN, where N looks numeric +// (ASCII digits and dots) must not begin with a leading zero, must not be /v1, +// and must not contain any dots. For paths beginning with "gopkg.in/", +// this second requirement is replaced by a requirement that the path +// follow the gopkg.in server's conventions. +// Third, no path element may begin with a dot. +func CheckPath(path string) (err error) { + defer func() { + if err != nil { + err = &InvalidPathError{Kind: "module", Path: path, Err: err} + } + }() + + if err := checkPath(path, modulePath); err != nil { + return err + } + i := strings.Index(path, "/") + if i < 0 { + i = len(path) + } + if i == 0 { + return fmt.Errorf("leading slash") + } + if !strings.Contains(path[:i], ".") { + return fmt.Errorf("missing dot in first path element") + } + if path[0] == '-' { + return fmt.Errorf("leading dash in first path element") + } + for _, r := range path[:i] { + if !firstPathOK(r) { + return fmt.Errorf("invalid char %q in first path element", r) + } + } + if _, _, ok := SplitPathVersion(path); !ok { + return fmt.Errorf("invalid version") + } + return nil +} + +// CheckImportPath checks that an import path is valid. +// +// A valid import path consists of one or more valid path elements +// separated by slashes (U+002F). (It must not begin with nor end in a slash.) +// +// A valid path element is a non-empty string made up of +// ASCII letters, ASCII digits, and limited ASCII punctuation: - . _ and ~. +// It must not end with a dot (U+002E), nor contain two dots in a row. +// +// The element prefix up to the first dot must not be a reserved file name +// on Windows, regardless of case (CON, com1, NuL, and so on). The element +// must not have a suffix of a tilde followed by one or more ASCII digits +// (to exclude paths elements that look like Windows short-names). +// +// CheckImportPath may be less restrictive in the future, but see the +// top-level package documentation for additional information about +// subtleties of Unicode. +func CheckImportPath(path string) error { + if err := checkPath(path, importPath); err != nil { + return &InvalidPathError{Kind: "import", Path: path, Err: err} + } + return nil +} + +// pathKind indicates what kind of path we're checking. Module paths, +// import paths, and file paths have different restrictions. +type pathKind int + +const ( + modulePath pathKind = iota + importPath + filePath +) + +// checkPath checks that a general path is valid. kind indicates what +// specific constraints should be applied. +// +// checkPath returns an error describing why the path is not valid. +// Because these checks apply to module, import, and file paths, +// and because other checks may be applied, the caller is expected to wrap +// this error with InvalidPathError. +func checkPath(path string, kind pathKind) error { + if !utf8.ValidString(path) { + return fmt.Errorf("invalid UTF-8") + } + if path == "" { + return fmt.Errorf("empty string") + } + if path[0] == '-' && kind != filePath { + return fmt.Errorf("leading dash") + } + if strings.Contains(path, "//") { + return fmt.Errorf("double slash") + } + if path[len(path)-1] == '/' { + return fmt.Errorf("trailing slash") + } + elemStart := 0 + for i, r := range path { + if r == '/' { + if err := checkElem(path[elemStart:i], kind); err != nil { + return err + } + elemStart = i + 1 + } + } + if err := checkElem(path[elemStart:], kind); err != nil { + return err + } + return nil +} + +// checkElem checks whether an individual path element is valid. +func checkElem(elem string, kind pathKind) error { + if elem == "" { + return fmt.Errorf("empty path element") + } + if strings.Count(elem, ".") == len(elem) { + return fmt.Errorf("invalid path element %q", elem) + } + if elem[0] == '.' && kind == modulePath { + return fmt.Errorf("leading dot in path element") + } + if elem[len(elem)-1] == '.' { + return fmt.Errorf("trailing dot in path element") + } + for _, r := range elem { + ok := false + switch kind { + case modulePath: + ok = modPathOK(r) + case importPath: + ok = importPathOK(r) + case filePath: + ok = fileNameOK(r) + default: + panic(fmt.Sprintf("internal error: invalid kind %v", kind)) + } + if !ok { + return fmt.Errorf("invalid char %q", r) + } + } + + // Windows disallows a bunch of path elements, sadly. + // See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file + short := elem + if i := strings.Index(short, "."); i >= 0 { + short = short[:i] + } + for _, bad := range badWindowsNames { + if strings.EqualFold(bad, short) { + return fmt.Errorf("%q disallowed as path element component on Windows", short) + } + } + + if kind == filePath { + // don't check for Windows short-names in file names. They're + // only an issue for import paths. + return nil + } + + // Reject path components that look like Windows short-names. + // Those usually end in a tilde followed by one or more ASCII digits. + if tilde := strings.LastIndexByte(short, '~'); tilde >= 0 && tilde < len(short)-1 { + suffix := short[tilde+1:] + suffixIsDigits := true + for _, r := range suffix { + if r < '0' || r > '9' { + suffixIsDigits = false + break + } + } + if suffixIsDigits { + return fmt.Errorf("trailing tilde and digits in path element") + } + } + + return nil +} + +// CheckFilePath checks that a slash-separated file path is valid. +// The definition of a valid file path is the same as the definition +// of a valid import path except that the set of allowed characters is larger: +// all Unicode letters, ASCII digits, the ASCII space character (U+0020), +// and the ASCII punctuation characters +// “!#$%&()+,-.=@[]^_{}~”. +// (The excluded punctuation characters, " * < > ? ` ' | / \ and :, +// have special meanings in certain shells or operating systems.) +// +// CheckFilePath may be less restrictive in the future, but see the +// top-level package documentation for additional information about +// subtleties of Unicode. +func CheckFilePath(path string) error { + if err := checkPath(path, filePath); err != nil { + return &InvalidPathError{Kind: "file", Path: path, Err: err} + } + return nil +} + +// badWindowsNames are the reserved file path elements on Windows. +// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file +var badWindowsNames = []string{ + "CON", + "PRN", + "AUX", + "NUL", + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9", + "LPT1", + "LPT2", + "LPT3", + "LPT4", + "LPT5", + "LPT6", + "LPT7", + "LPT8", + "LPT9", +} + +// SplitPathVersion returns prefix and major version such that prefix+pathMajor == path +// and version is either empty or "/vN" for N >= 2. +// As a special case, gopkg.in paths are recognized directly; +// they require ".vN" instead of "/vN", and for all N, not just N >= 2. +// SplitPathVersion returns with ok = false when presented with +// a path whose last path element does not satisfy the constraints +// applied by CheckPath, such as "example.com/pkg/v1" or "example.com/pkg/v1.2". +func SplitPathVersion(path string) (prefix, pathMajor string, ok bool) { + if strings.HasPrefix(path, "gopkg.in/") { + return splitGopkgIn(path) + } + + i := len(path) + dot := false + for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') { + if path[i-1] == '.' { + dot = true + } + i-- + } + if i <= 1 || i == len(path) || path[i-1] != 'v' || path[i-2] != '/' { + return path, "", true + } + prefix, pathMajor = path[:i-2], path[i-2:] + if dot || len(pathMajor) <= 2 || pathMajor[2] == '0' || pathMajor == "/v1" { + return path, "", false + } + return prefix, pathMajor, true +} + +// splitGopkgIn is like SplitPathVersion but only for gopkg.in paths. +func splitGopkgIn(path string) (prefix, pathMajor string, ok bool) { + if !strings.HasPrefix(path, "gopkg.in/") { + return path, "", false + } + i := len(path) + if strings.HasSuffix(path, "-unstable") { + i -= len("-unstable") + } + for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9') { + i-- + } + if i <= 1 || path[i-1] != 'v' || path[i-2] != '.' { + // All gopkg.in paths must end in vN for some N. + return path, "", false + } + prefix, pathMajor = path[:i-2], path[i-2:] + if len(pathMajor) <= 2 || pathMajor[2] == '0' && pathMajor != ".v0" { + return path, "", false + } + return prefix, pathMajor, true +} + +// MatchPathMajor reports whether the semantic version v +// matches the path major version pathMajor. +// +// MatchPathMajor returns true if and only if CheckPathMajor returns nil. +func MatchPathMajor(v, pathMajor string) bool { + return CheckPathMajor(v, pathMajor) == nil +} + +// CheckPathMajor returns a non-nil error if the semantic version v +// does not match the path major version pathMajor. +func CheckPathMajor(v, pathMajor string) error { + // TODO(jayconrod): return errors or panic for invalid inputs. This function + // (and others) was covered by integration tests for cmd/go, and surrounding + // code protected against invalid inputs like non-canonical versions. + if strings.HasPrefix(pathMajor, ".v") && strings.HasSuffix(pathMajor, "-unstable") { + pathMajor = strings.TrimSuffix(pathMajor, "-unstable") + } + if strings.HasPrefix(v, "v0.0.0-") && pathMajor == ".v1" { + // Allow old bug in pseudo-versions that generated v0.0.0- pseudoversion for gopkg .v1. + // For example, gopkg.in/yaml.v2@v2.2.1's go.mod requires gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405. + return nil + } + m := semver.Major(v) + if pathMajor == "" { + if m == "v0" || m == "v1" || semver.Build(v) == "+incompatible" { + return nil + } + pathMajor = "v0 or v1" + } else if pathMajor[0] == '/' || pathMajor[0] == '.' { + if m == pathMajor[1:] { + return nil + } + pathMajor = pathMajor[1:] + } + return &InvalidVersionError{ + Version: v, + Err: fmt.Errorf("should be %s, not %s", pathMajor, semver.Major(v)), + } +} + +// PathMajorPrefix returns the major-version tag prefix implied by pathMajor. +// An empty PathMajorPrefix allows either v0 or v1. +// +// Note that MatchPathMajor may accept some versions that do not actually begin +// with this prefix: namely, it accepts a 'v0.0.0-' prefix for a '.v1' +// pathMajor, even though that pathMajor implies 'v1' tagging. +func PathMajorPrefix(pathMajor string) string { + if pathMajor == "" { + return "" + } + if pathMajor[0] != '/' && pathMajor[0] != '.' { + panic("pathMajor suffix " + pathMajor + " passed to PathMajorPrefix lacks separator") + } + if strings.HasPrefix(pathMajor, ".v") && strings.HasSuffix(pathMajor, "-unstable") { + pathMajor = strings.TrimSuffix(pathMajor, "-unstable") + } + m := pathMajor[1:] + if m != semver.Major(m) { + panic("pathMajor suffix " + pathMajor + "passed to PathMajorPrefix is not a valid major version") + } + return m +} + +// CanonicalVersion returns the canonical form of the version string v. +// It is the same as semver.Canonical(v) except that it preserves the special build suffix "+incompatible". +func CanonicalVersion(v string) string { + cv := semver.Canonical(v) + if semver.Build(v) == "+incompatible" { + cv += "+incompatible" + } + return cv +} + +// Sort sorts the list by Path, breaking ties by comparing Version fields. +// The Version fields are interpreted as semantic versions (using semver.Compare) +// optionally followed by a tie-breaking suffix introduced by a slash character, +// like in "v0.0.1/go.mod". +func Sort(list []Version) { + sort.Slice(list, func(i, j int) bool { + mi := list[i] + mj := list[j] + if mi.Path != mj.Path { + return mi.Path < mj.Path + } + // To help go.sum formatting, allow version/file. + // Compare semver prefix by semver rules, + // file by string order. + vi := mi.Version + vj := mj.Version + var fi, fj string + if k := strings.Index(vi, "/"); k >= 0 { + vi, fi = vi[:k], vi[k:] + } + if k := strings.Index(vj, "/"); k >= 0 { + vj, fj = vj[:k], vj[k:] + } + if vi != vj { + return semver.Compare(vi, vj) < 0 + } + return fi < fj + }) +} + +// EscapePath returns the escaped form of the given module path. +// It fails if the module path is invalid. +func EscapePath(path string) (escaped string, err error) { + if err := CheckPath(path); err != nil { + return "", err + } + + return escapeString(path) +} + +// EscapeVersion returns the escaped form of the given module version. +// Versions are allowed to be in non-semver form but must be valid file names +// and not contain exclamation marks. +func EscapeVersion(v string) (escaped string, err error) { + if err := checkElem(v, filePath); err != nil || strings.Contains(v, "!") { + return "", &InvalidVersionError{ + Version: v, + Err: fmt.Errorf("disallowed version string"), + } + } + return escapeString(v) +} + +func escapeString(s string) (escaped string, err error) { + haveUpper := false + for _, r := range s { + if r == '!' || r >= utf8.RuneSelf { + // This should be disallowed by CheckPath, but diagnose anyway. + // The correctness of the escaping loop below depends on it. + return "", fmt.Errorf("internal error: inconsistency in EscapePath") + } + if 'A' <= r && r <= 'Z' { + haveUpper = true + } + } + + if !haveUpper { + return s, nil + } + + var buf []byte + for _, r := range s { + if 'A' <= r && r <= 'Z' { + buf = append(buf, '!', byte(r+'a'-'A')) + } else { + buf = append(buf, byte(r)) + } + } + return string(buf), nil +} + +// UnescapePath returns the module path for the given escaped path. +// It fails if the escaped path is invalid or describes an invalid path. +func UnescapePath(escaped string) (path string, err error) { + path, ok := unescapeString(escaped) + if !ok { + return "", fmt.Errorf("invalid escaped module path %q", escaped) + } + if err := CheckPath(path); err != nil { + return "", fmt.Errorf("invalid escaped module path %q: %v", escaped, err) + } + return path, nil +} + +// UnescapeVersion returns the version string for the given escaped version. +// It fails if the escaped form is invalid or describes an invalid version. +// Versions are allowed to be in non-semver form but must be valid file names +// and not contain exclamation marks. +func UnescapeVersion(escaped string) (v string, err error) { + v, ok := unescapeString(escaped) + if !ok { + return "", fmt.Errorf("invalid escaped version %q", escaped) + } + if err := checkElem(v, filePath); err != nil { + return "", fmt.Errorf("invalid escaped version %q: %v", v, err) + } + return v, nil +} + +func unescapeString(escaped string) (string, bool) { + var buf []byte + + bang := false + for _, r := range escaped { + if r >= utf8.RuneSelf { + return "", false + } + if bang { + bang = false + if r < 'a' || 'z' < r { + return "", false + } + buf = append(buf, byte(r+'A'-'a')) + continue + } + if r == '!' { + bang = true + continue + } + if 'A' <= r && r <= 'Z' { + return "", false + } + buf = append(buf, byte(r)) + } + if bang { + return "", false + } + return string(buf), true +} + +// MatchPrefixPatterns reports whether any path prefix of target matches one of +// the glob patterns (as defined by path.Match) in the comma-separated globs +// list. This implements the algorithm used when matching a module path to the +// GOPRIVATE environment variable, as described by 'go help module-private'. +// +// It ignores any empty or malformed patterns in the list. +func MatchPrefixPatterns(globs, target string) bool { + for globs != "" { + // Extract next non-empty glob in comma-separated list. + var glob string + if i := strings.Index(globs, ","); i >= 0 { + glob, globs = globs[:i], globs[i+1:] + } else { + glob, globs = globs, "" + } + if glob == "" { + continue + } + + // A glob with N+1 path elements (N slashes) needs to be matched + // against the first N+1 path elements of target, + // which end just before the N+1'th slash. + n := strings.Count(glob, "/") + prefix := target + // Walk target, counting slashes, truncating at the N+1'th slash. + for i := 0; i < len(target); i++ { + if target[i] == '/' { + if n == 0 { + prefix = target[:i] + break + } + n-- + } + } + if n > 0 { + // Not enough prefix elements. + continue + } + matched, _ := path.Match(glob, prefix) + if matched { + return true + } + } + return false +} diff --git a/vendor/golang.org/x/mod/module/pseudo.go b/vendor/golang.org/x/mod/module/pseudo.go new file mode 100644 index 00000000000..f04ad378869 --- /dev/null +++ b/vendor/golang.org/x/mod/module/pseudo.go @@ -0,0 +1,250 @@ +// Copyright 2018 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. + +// Pseudo-versions +// +// Code authors are expected to tag the revisions they want users to use, +// including prereleases. However, not all authors tag versions at all, +// and not all commits a user might want to try will have tags. +// A pseudo-version is a version with a special form that allows us to +// address an untagged commit and order that version with respect to +// other versions we might encounter. +// +// A pseudo-version takes one of the general forms: +// +// (1) vX.0.0-yyyymmddhhmmss-abcdef123456 +// (2) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456 +// (3) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456+incompatible +// (4) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456 +// (5) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456+incompatible +// +// If there is no recently tagged version with the right major version vX, +// then form (1) is used, creating a space of pseudo-versions at the bottom +// of the vX version range, less than any tagged version, including the unlikely v0.0.0. +// +// If the most recent tagged version before the target commit is vX.Y.Z or vX.Y.Z+incompatible, +// then the pseudo-version uses form (2) or (3), making it a prerelease for the next +// possible semantic version after vX.Y.Z. The leading 0 segment in the prerelease string +// ensures that the pseudo-version compares less than possible future explicit prereleases +// like vX.Y.(Z+1)-rc1 or vX.Y.(Z+1)-1. +// +// If the most recent tagged version before the target commit is vX.Y.Z-pre or vX.Y.Z-pre+incompatible, +// then the pseudo-version uses form (4) or (5), making it a slightly later prerelease. + +package module + +import ( + "errors" + "fmt" + "strings" + "time" + + "golang.org/x/mod/internal/lazyregexp" + "golang.org/x/mod/semver" +) + +var pseudoVersionRE = lazyregexp.New(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$`) + +const PseudoVersionTimestampFormat = "20060102150405" + +// PseudoVersion returns a pseudo-version for the given major version ("v1") +// preexisting older tagged version ("" or "v1.2.3" or "v1.2.3-pre"), revision time, +// and revision identifier (usually a 12-byte commit hash prefix). +func PseudoVersion(major, older string, t time.Time, rev string) string { + if major == "" { + major = "v0" + } + segment := fmt.Sprintf("%s-%s", t.UTC().Format(PseudoVersionTimestampFormat), rev) + build := semver.Build(older) + older = semver.Canonical(older) + if older == "" { + return major + ".0.0-" + segment // form (1) + } + if semver.Prerelease(older) != "" { + return older + ".0." + segment + build // form (4), (5) + } + + // Form (2), (3). + // Extract patch from vMAJOR.MINOR.PATCH + i := strings.LastIndex(older, ".") + 1 + v, patch := older[:i], older[i:] + + // Reassemble. + return v + incDecimal(patch) + "-0." + segment + build +} + +// ZeroPseudoVersion returns a pseudo-version with a zero timestamp and +// revision, which may be used as a placeholder. +func ZeroPseudoVersion(major string) string { + return PseudoVersion(major, "", time.Time{}, "000000000000") +} + +// incDecimal returns the decimal string incremented by 1. +func incDecimal(decimal string) string { + // Scan right to left turning 9s to 0s until you find a digit to increment. + digits := []byte(decimal) + i := len(digits) - 1 + for ; i >= 0 && digits[i] == '9'; i-- { + digits[i] = '0' + } + if i >= 0 { + digits[i]++ + } else { + // digits is all zeros + digits[0] = '1' + digits = append(digits, '0') + } + return string(digits) +} + +// decDecimal returns the decimal string decremented by 1, or the empty string +// if the decimal is all zeroes. +func decDecimal(decimal string) string { + // Scan right to left turning 0s to 9s until you find a digit to decrement. + digits := []byte(decimal) + i := len(digits) - 1 + for ; i >= 0 && digits[i] == '0'; i-- { + digits[i] = '9' + } + if i < 0 { + // decimal is all zeros + return "" + } + if i == 0 && digits[i] == '1' && len(digits) > 1 { + digits = digits[1:] + } else { + digits[i]-- + } + return string(digits) +} + +// IsPseudoVersion reports whether v is a pseudo-version. +func IsPseudoVersion(v string) bool { + return strings.Count(v, "-") >= 2 && semver.IsValid(v) && pseudoVersionRE.MatchString(v) +} + +// IsZeroPseudoVersion returns whether v is a pseudo-version with a zero base, +// timestamp, and revision, as returned by ZeroPseudoVersion. +func IsZeroPseudoVersion(v string) bool { + return v == ZeroPseudoVersion(semver.Major(v)) +} + +// PseudoVersionTime returns the time stamp of the pseudo-version v. +// It returns an error if v is not a pseudo-version or if the time stamp +// embedded in the pseudo-version is not a valid time. +func PseudoVersionTime(v string) (time.Time, error) { + _, timestamp, _, _, err := parsePseudoVersion(v) + if err != nil { + return time.Time{}, err + } + t, err := time.Parse("20060102150405", timestamp) + if err != nil { + return time.Time{}, &InvalidVersionError{ + Version: v, + Pseudo: true, + Err: fmt.Errorf("malformed time %q", timestamp), + } + } + return t, nil +} + +// PseudoVersionRev returns the revision identifier of the pseudo-version v. +// It returns an error if v is not a pseudo-version. +func PseudoVersionRev(v string) (rev string, err error) { + _, _, rev, _, err = parsePseudoVersion(v) + return +} + +// PseudoVersionBase returns the canonical parent version, if any, upon which +// the pseudo-version v is based. +// +// If v has no parent version (that is, if it is "vX.0.0-[…]"), +// PseudoVersionBase returns the empty string and a nil error. +func PseudoVersionBase(v string) (string, error) { + base, _, _, build, err := parsePseudoVersion(v) + if err != nil { + return "", err + } + + switch pre := semver.Prerelease(base); pre { + case "": + // vX.0.0-yyyymmddhhmmss-abcdef123456 → "" + if build != "" { + // Pseudo-versions of the form vX.0.0-yyyymmddhhmmss-abcdef123456+incompatible + // are nonsensical: the "vX.0.0-" prefix implies that there is no parent tag, + // but the "+incompatible" suffix implies that the major version of + // the parent tag is not compatible with the module's import path. + // + // There are a few such entries in the index generated by proxy.golang.org, + // but we believe those entries were generated by the proxy itself. + return "", &InvalidVersionError{ + Version: v, + Pseudo: true, + Err: fmt.Errorf("lacks base version, but has build metadata %q", build), + } + } + return "", nil + + case "-0": + // vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456 → vX.Y.Z + // vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456+incompatible → vX.Y.Z+incompatible + base = strings.TrimSuffix(base, pre) + i := strings.LastIndexByte(base, '.') + if i < 0 { + panic("base from parsePseudoVersion missing patch number: " + base) + } + patch := decDecimal(base[i+1:]) + if patch == "" { + // vX.0.0-0 is invalid, but has been observed in the wild in the index + // generated by requests to proxy.golang.org. + // + // NOTE(bcmills): I cannot find a historical bug that accounts for + // pseudo-versions of this form, nor have I seen such versions in any + // actual go.mod files. If we find actual examples of this form and a + // reasonable theory of how they came into existence, it seems fine to + // treat them as equivalent to vX.0.0 (especially since the invalid + // pseudo-versions have lower precedence than the real ones). For now, we + // reject them. + return "", &InvalidVersionError{ + Version: v, + Pseudo: true, + Err: fmt.Errorf("version before %s would have negative patch number", base), + } + } + return base[:i+1] + patch + build, nil + + default: + // vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456 → vX.Y.Z-pre + // vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456+incompatible → vX.Y.Z-pre+incompatible + if !strings.HasSuffix(base, ".0") { + panic(`base from parsePseudoVersion missing ".0" before date: ` + base) + } + return strings.TrimSuffix(base, ".0") + build, nil + } +} + +var errPseudoSyntax = errors.New("syntax error") + +func parsePseudoVersion(v string) (base, timestamp, rev, build string, err error) { + if !IsPseudoVersion(v) { + return "", "", "", "", &InvalidVersionError{ + Version: v, + Pseudo: true, + Err: errPseudoSyntax, + } + } + build = semver.Build(v) + v = strings.TrimSuffix(v, build) + j := strings.LastIndex(v, "-") + v, rev = v[:j], v[j+1:] + i := strings.LastIndex(v, "-") + if j := strings.LastIndex(v, "."); j > i { + base = v[:j] // "vX.Y.Z-pre.0" or "vX.Y.(Z+1)-0" + timestamp = v[j+1:] + } else { + base = v[:i] // "vX.0.0" + timestamp = v[i+1:] + } + return base, timestamp, rev, build, nil +} diff --git a/vendor/golang.org/x/mod/semver/semver.go b/vendor/golang.org/x/mod/semver/semver.go index 4338f351774..7be398f80d3 100644 --- a/vendor/golang.org/x/mod/semver/semver.go +++ b/vendor/golang.org/x/mod/semver/semver.go @@ -22,6 +22,8 @@ // as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0. package semver +import "sort" + // parsed returns the parsed form of a semantic version string. type parsed struct { major string @@ -150,6 +152,24 @@ func Max(v, w string) string { return w } +// ByVersion implements sort.Interface for sorting semantic version strings. +type ByVersion []string + +func (vs ByVersion) Len() int { return len(vs) } +func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] } +func (vs ByVersion) Less(i, j int) bool { + cmp := Compare(vs[i], vs[j]) + if cmp != 0 { + return cmp < 0 + } + return vs[i] < vs[j] +} + +// Sort sorts a list of semantic version strings using ByVersion. +func Sort(list []string) { + sort.Sort(ByVersion(list)) +} + func parse(v string) (p parsed, ok bool) { if v == "" || v[0] != 'v' { p.err = "missing v prefix" diff --git a/vendor/modules.txt b/vendor/modules.txt index d0ec065a6fc..5c121e66f96 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -515,6 +515,19 @@ github.com/prometheus/common/model github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util +# github.com/redhat-developer/alizer/go v0.0.0-20220215154256-33df7feef4ae +## explicit +github.com/redhat-developer/alizer/go/pkg/apis/enricher +github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/dotnet +github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go +github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java +github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs +github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/python +github.com/redhat-developer/alizer/go/pkg/apis/language +github.com/redhat-developer/alizer/go/pkg/apis/recognizer +github.com/redhat-developer/alizer/go/pkg/schema +github.com/redhat-developer/alizer/go/pkg/utils +github.com/redhat-developer/alizer/go/pkg/utils/langfiles # github.com/redhat-developer/service-binding-operator v0.9.0 ## explicit github.com/redhat-developer/service-binding-operator/apis @@ -633,7 +646,10 @@ golang.org/x/crypto/ssh golang.org/x/crypto/ssh/agent golang.org/x/crypto/ssh/internal/bcrypt_pbkdf golang.org/x/crypto/ssh/knownhosts -# golang.org/x/mod v0.4.2 +# golang.org/x/mod v0.5.1 +golang.org/x/mod/internal/lazyregexp +golang.org/x/mod/modfile +golang.org/x/mod/module golang.org/x/mod/semver # golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 golang.org/x/net/context