Refactor git attributes (#29356)

This commit is contained in:
KN4CK3R 2024-02-24 19:46:49 +01:00 committed by GitHub
parent 98ab9445d1
commit 4197e28100
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 101 additions and 100 deletions

35
modules/git/attribute.go Normal file
View File

@ -0,0 +1,35 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
import (
"code.gitea.io/gitea/modules/optional"
)
const (
AttributeLinguistVendored = "linguist-vendored"
AttributeLinguistGenerated = "linguist-generated"
AttributeLinguistDocumentation = "linguist-documentation"
AttributeLinguistDetectable = "linguist-detectable"
AttributeLinguistLanguage = "linguist-language"
AttributeGitlabLanguage = "gitlab-language"
)
// true if "set"/"true", false if "unset"/"false", none otherwise
func AttributeToBool(attr map[string]string, name string) optional.Option[bool] {
switch attr[name] {
case "set", "true":
return optional.Some(true)
case "unset", "false":
return optional.Some(false)
}
return optional.None[bool]()
}
func AttributeToString(attr map[string]string, name string) optional.Option[string] {
if value, has := attr[name]; has && value != "unspecified" {
return optional.Some(value)
}
return optional.None[string]()
}

View File

@ -11,7 +11,6 @@ import (
"os" "os"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
) )
// CheckAttributeOpts represents the possible options to CheckAttribute // CheckAttributeOpts represents the possible options to CheckAttribute
@ -292,7 +291,14 @@ func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeRe
} }
checker := &CheckAttributeReader{ checker := &CheckAttributeReader{
Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language", "linguist-documentation", "linguist-detectable"}, Attributes: []string{
AttributeLinguistVendored,
AttributeLinguistGenerated,
AttributeLinguistDocumentation,
AttributeLinguistDetectable,
AttributeLinguistLanguage,
AttributeGitlabLanguage,
},
Repo: repo, Repo: repo,
IndexFile: indexFilename, IndexFile: indexFilename,
WorkTree: worktree, WorkTree: worktree,
@ -317,23 +323,3 @@ func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeRe
return checker, deferable return checker, deferable
} }
// true if "set"/"true", false if "unset"/"false", none otherwise
func attributeToBool(attr map[string]string, name string) optional.Option[bool] {
if value, has := attr[name]; has && value != "unspecified" {
switch value {
case "set", "true":
return optional.Some(true)
case "unset", "false":
return optional.Some(false)
}
}
return optional.None[bool]()
}
func attributeToString(attr map[string]string, name string) optional.Option[string] {
if value, has := attr[name]; has && value != "unspecified" {
return optional.Some(value)
}
return optional.None[string]()
}

View File

@ -24,7 +24,7 @@ func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) {
select { select {
case attr := <-wr.ReadAttribute(): case attr := <-wr.ReadAttribute():
assert.Equal(t, ".gitignore\"\n", attr.Filename) assert.Equal(t, ".gitignore\"\n", attr.Filename)
assert.Equal(t, "linguist-vendored", attr.Attribute) assert.Equal(t, AttributeLinguistVendored, attr.Attribute)
assert.Equal(t, "unspecified", attr.Value) assert.Equal(t, "unspecified", attr.Value)
case <-time.After(100 * time.Millisecond): case <-time.After(100 * time.Millisecond):
assert.FailNow(t, "took too long to read an attribute from the list") assert.FailNow(t, "took too long to read an attribute from the list")
@ -38,7 +38,7 @@ func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) {
select { select {
case attr := <-wr.ReadAttribute(): case attr := <-wr.ReadAttribute():
assert.Equal(t, ".gitignore\"\n", attr.Filename) assert.Equal(t, ".gitignore\"\n", attr.Filename)
assert.Equal(t, "linguist-vendored", attr.Attribute) assert.Equal(t, AttributeLinguistVendored, attr.Attribute)
assert.Equal(t, "unspecified", attr.Value) assert.Equal(t, "unspecified", attr.Value)
case <-time.After(100 * time.Millisecond): case <-time.After(100 * time.Millisecond):
assert.FailNow(t, "took too long to read an attribute from the list") assert.FailNow(t, "took too long to read an attribute from the list")
@ -77,21 +77,21 @@ func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, attributeTriple{ assert.EqualValues(t, attributeTriple{
Filename: "shouldbe.vendor", Filename: "shouldbe.vendor",
Attribute: "linguist-vendored", Attribute: AttributeLinguistVendored,
Value: "set", Value: "set",
}, attr) }, attr)
attr = <-wr.ReadAttribute() attr = <-wr.ReadAttribute()
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, attributeTriple{ assert.EqualValues(t, attributeTriple{
Filename: "shouldbe.vendor", Filename: "shouldbe.vendor",
Attribute: "linguist-generated", Attribute: AttributeLinguistGenerated,
Value: "unspecified", Value: "unspecified",
}, attr) }, attr)
attr = <-wr.ReadAttribute() attr = <-wr.ReadAttribute()
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, attributeTriple{ assert.EqualValues(t, attributeTriple{
Filename: "shouldbe.vendor", Filename: "shouldbe.vendor",
Attribute: "linguist-language", Attribute: AttributeLinguistLanguage,
Value: "unspecified", Value: "unspecified",
}, attr) }, attr)
} }

View File

@ -6,6 +6,8 @@ package git
import ( import (
"strings" "strings"
"unicode" "unicode"
"code.gitea.io/gitea/modules/optional"
) )
const ( const (
@ -46,3 +48,20 @@ func mergeLanguageStats(stats map[string]int64) map[string]int64 {
} }
return res return res
} }
func TryReadLanguageAttribute(attrs map[string]string) optional.Option[string] {
language := AttributeToString(attrs, AttributeLinguistLanguage)
if language.Value() == "" {
language = AttributeToString(attrs, AttributeGitlabLanguage)
if language.Has() {
raw := language.Value()
// gitlab-language may have additional parameters after the language
// ignore them and just use the main language
// https://docs.gitlab.com/ee/user/project/highlighting.html#override-syntax-highlighting-for-a-file-type
if idx := strings.IndexByte(raw, '?'); idx >= 0 {
language = optional.Some(raw[:idx])
}
}
}
return language
}

View File

@ -8,7 +8,6 @@ package git
import ( import (
"bytes" "bytes"
"io" "io"
"strings"
"code.gitea.io/gitea/modules/analyze" "code.gitea.io/gitea/modules/analyze"
"code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/optional"
@ -66,36 +65,27 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
if checker != nil { if checker != nil {
attrs, err := checker.CheckPath(f.Name) attrs, err := checker.CheckPath(f.Name)
if err == nil { if err == nil {
isVendored = attributeToBool(attrs, "linguist-vendored") isVendored = AttributeToBool(attrs, AttributeLinguistVendored)
if isVendored.ValueOrDefault(false) { if isVendored.ValueOrDefault(false) {
return nil return nil
} }
isGenerated = attributeToBool(attrs, "linguist-generated") isGenerated = AttributeToBool(attrs, AttributeLinguistGenerated)
if isGenerated.ValueOrDefault(false) { if isGenerated.ValueOrDefault(false) {
return nil return nil
} }
isDocumentation = attributeToBool(attrs, "linguist-documentation") isDocumentation = AttributeToBool(attrs, AttributeLinguistDocumentation)
if isDocumentation.ValueOrDefault(false) { if isDocumentation.ValueOrDefault(false) {
return nil return nil
} }
isDetectable = attributeToBool(attrs, "linguist-detectable") isDetectable = AttributeToBool(attrs, AttributeLinguistDetectable)
if !isDetectable.ValueOrDefault(true) { if !isDetectable.ValueOrDefault(true) {
return nil return nil
} }
hasLanguage := attributeToString(attrs, "linguist-language") hasLanguage := TryReadLanguageAttribute(attrs)
if hasLanguage.Value() == "" {
hasLanguage = attributeToString(attrs, "gitlab-language")
if hasLanguage.Has() {
language := hasLanguage.Value()
if idx := strings.IndexByte(language, '?'); idx >= 0 {
hasLanguage = optional.Some(language[:idx])
}
}
}
if hasLanguage.Value() != "" { if hasLanguage.Value() != "" {
language := hasLanguage.Value() language := hasLanguage.Value()

View File

@ -8,7 +8,6 @@ package git
import ( import (
"bytes" "bytes"
"io" "io"
"strings"
"code.gitea.io/gitea/modules/analyze" "code.gitea.io/gitea/modules/analyze"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
@ -97,36 +96,27 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
if checker != nil { if checker != nil {
attrs, err := checker.CheckPath(f.Name()) attrs, err := checker.CheckPath(f.Name())
if err == nil { if err == nil {
isVendored = attributeToBool(attrs, "linguist-vendored") isVendored = AttributeToBool(attrs, AttributeLinguistVendored)
if isVendored.ValueOrDefault(false) { if isVendored.ValueOrDefault(false) {
continue continue
} }
isGenerated = attributeToBool(attrs, "linguist-generated") isGenerated = AttributeToBool(attrs, AttributeLinguistGenerated)
if isGenerated.ValueOrDefault(false) { if isGenerated.ValueOrDefault(false) {
continue continue
} }
isDocumentation = attributeToBool(attrs, "linguist-documentation") isDocumentation = AttributeToBool(attrs, AttributeLinguistDocumentation)
if isDocumentation.ValueOrDefault(false) { if isDocumentation.ValueOrDefault(false) {
continue continue
} }
isDetectable = attributeToBool(attrs, "linguist-detectable") isDetectable = AttributeToBool(attrs, AttributeLinguistDetectable)
if !isDetectable.ValueOrDefault(true) { if !isDetectable.ValueOrDefault(true) {
continue continue
} }
hasLanguage := attributeToString(attrs, "linguist-language") hasLanguage := TryReadLanguageAttribute(attrs)
if hasLanguage.Value() == "" {
hasLanguage = attributeToString(attrs, "gitlab-language")
if hasLanguage.Has() {
language := hasLanguage.Value()
if idx := strings.IndexByte(language, '?'); idx >= 0 {
hasLanguage = optional.Some(language[:idx])
}
}
}
if hasLanguage.Value() != "" { if hasLanguage.Value() != "" {
language := hasLanguage.Value() language := hasLanguage.Value()

View File

@ -635,11 +635,8 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
defer deferable() defer deferable()
attrs, err := checker.CheckPath(ctx.Repo.TreePath) attrs, err := checker.CheckPath(ctx.Repo.TreePath)
if err == nil { if err == nil {
vendored, has := attrs["linguist-vendored"] ctx.Data["IsVendored"] = git.AttributeToBool(attrs, git.AttributeLinguistVendored).Value()
ctx.Data["IsVendored"] = has && (vendored == "set" || vendored == "true") ctx.Data["IsGenerated"] = git.AttributeToBool(attrs, git.AttributeLinguistGenerated).Value()
generated, has := attrs["linguist-generated"]
ctx.Data["IsGenerated"] = has && (generated == "set" || generated == "true")
} }
} }
} }

View File

@ -29,6 +29,7 @@ import (
"code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/translation"
@ -1181,41 +1182,30 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
for _, diffFile := range diff.Files { for _, diffFile := range diff.Files {
gotVendor := false isVendored := optional.None[bool]()
gotGenerated := false isGenerated := optional.None[bool]()
if checker != nil { if checker != nil {
attrs, err := checker.CheckPath(diffFile.Name) attrs, err := checker.CheckPath(diffFile.Name)
if err == nil { if err == nil {
if vendored, has := attrs["linguist-vendored"]; has { isVendored = git.AttributeToBool(attrs, git.AttributeLinguistVendored)
if vendored == "set" || vendored == "true" { isGenerated = git.AttributeToBool(attrs, git.AttributeLinguistGenerated)
diffFile.IsVendored = true
gotVendor = true language := git.TryReadLanguageAttribute(attrs)
} else { if language.Has() {
gotVendor = vendored == "false" diffFile.Language = language.Value()
}
}
if generated, has := attrs["linguist-generated"]; has {
if generated == "set" || generated == "true" {
diffFile.IsGenerated = true
gotGenerated = true
} else {
gotGenerated = generated == "false"
}
}
if language, has := attrs["linguist-language"]; has && language != "unspecified" && language != "" {
diffFile.Language = language
} else if language, has := attrs["gitlab-language"]; has && language != "unspecified" && language != "" {
diffFile.Language = language
} }
} }
} }
if !gotVendor { if !isVendored.Has() {
diffFile.IsVendored = analyze.IsVendor(diffFile.Name) isVendored = optional.Some(analyze.IsVendor(diffFile.Name))
} }
if !gotGenerated { diffFile.IsVendored = isVendored.Value()
diffFile.IsGenerated = analyze.IsGenerated(diffFile.Name)
if !isGenerated.Has() {
isGenerated = optional.Some(analyze.IsGenerated(diffFile.Name))
} }
diffFile.IsGenerated = isGenerated.Value()
tailSection := diffFile.GetTailSection(gitRepo, opts.BeforeCommitID, opts.AfterCommitID) tailSection := diffFile.GetTailSection(gitRepo, opts.BeforeCommitID, opts.AfterCommitID)
if tailSection != nil { if tailSection != nil {

View File

@ -282,7 +282,7 @@ func TryGetContentLanguage(gitRepo *git.Repository, commitID, treePath string) (
filename2attribute2info, err := gitRepo.CheckAttribute(git.CheckAttributeOpts{ filename2attribute2info, err := gitRepo.CheckAttribute(git.CheckAttributeOpts{
CachedOnly: true, CachedOnly: true,
Attributes: []string{"linguist-language", "gitlab-language"}, Attributes: []string{git.AttributeLinguistLanguage, git.AttributeGitlabLanguage},
Filenames: []string{treePath}, Filenames: []string{treePath},
IndexFile: indexFilename, IndexFile: indexFilename,
WorkTree: worktree, WorkTree: worktree,
@ -291,13 +291,7 @@ func TryGetContentLanguage(gitRepo *git.Repository, commitID, treePath string) (
return "", err return "", err
} }
language := filename2attribute2info[treePath]["linguist-language"] language := git.TryReadLanguageAttribute(filename2attribute2info[treePath])
if language == "" || language == "unspecified" {
language = filename2attribute2info[treePath]["gitlab-language"]
}
if language == "unspecified" {
language = ""
}
return language, nil return language.Value(), nil
} }