mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Add .gitattribute assisted language detection to blame, diff and render (#17590)
Use check attribute code to check the assigned language of a file and send that in to chroma as a hint for the language of the file. Signed-off-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
		| @@ -31,7 +31,6 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/process" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
|  | ||||
| 	"github.com/sergi/go-diff/diffmatchpatch" | ||||
| 	stdcharset "golang.org/x/net/html/charset" | ||||
| @@ -178,6 +177,7 @@ func getLineContent(content string) string { | ||||
|  | ||||
| // DiffSection represents a section of a DiffFile. | ||||
| type DiffSection struct { | ||||
| 	file     *DiffFile | ||||
| 	FileName string | ||||
| 	Name     string | ||||
| 	Lines    []*DiffLine | ||||
| @@ -546,6 +546,11 @@ func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) tem | ||||
| 		diff2           string | ||||
| 	) | ||||
|  | ||||
| 	language := "" | ||||
| 	if diffSection.file != nil { | ||||
| 		language = diffSection.file.Language | ||||
| 	} | ||||
|  | ||||
| 	// try to find equivalent diff line. ignore, otherwise | ||||
| 	switch diffLine.Type { | ||||
| 	case DiffLineSection: | ||||
| @@ -553,25 +558,25 @@ func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) tem | ||||
| 	case DiffLineAdd: | ||||
| 		compareDiffLine = diffSection.GetLine(DiffLineDel, diffLine.RightIdx) | ||||
| 		if compareDiffLine == nil { | ||||
| 			return template.HTML(highlight.Code(diffSection.FileName, diffLine.Content[1:])) | ||||
| 			return template.HTML(highlight.Code(diffSection.FileName, language, diffLine.Content[1:])) | ||||
| 		} | ||||
| 		diff1 = compareDiffLine.Content | ||||
| 		diff2 = diffLine.Content | ||||
| 	case DiffLineDel: | ||||
| 		compareDiffLine = diffSection.GetLine(DiffLineAdd, diffLine.LeftIdx) | ||||
| 		if compareDiffLine == nil { | ||||
| 			return template.HTML(highlight.Code(diffSection.FileName, diffLine.Content[1:])) | ||||
| 			return template.HTML(highlight.Code(diffSection.FileName, language, diffLine.Content[1:])) | ||||
| 		} | ||||
| 		diff1 = diffLine.Content | ||||
| 		diff2 = compareDiffLine.Content | ||||
| 	default: | ||||
| 		if strings.IndexByte(" +-", diffLine.Content[0]) > -1 { | ||||
| 			return template.HTML(highlight.Code(diffSection.FileName, diffLine.Content[1:])) | ||||
| 			return template.HTML(highlight.Code(diffSection.FileName, language, diffLine.Content[1:])) | ||||
| 		} | ||||
| 		return template.HTML(highlight.Code(diffSection.FileName, diffLine.Content)) | ||||
| 		return template.HTML(highlight.Code(diffSection.FileName, language, diffLine.Content)) | ||||
| 	} | ||||
|  | ||||
| 	diffRecord := diffMatchPatch.DiffMain(highlight.Code(diffSection.FileName, diff1[1:]), highlight.Code(diffSection.FileName, diff2[1:]), true) | ||||
| 	diffRecord := diffMatchPatch.DiffMain(highlight.Code(diffSection.FileName, language, diff1[1:]), highlight.Code(diffSection.FileName, language, diff2[1:]), true) | ||||
| 	diffRecord = diffMatchPatch.DiffCleanupEfficiency(diffRecord) | ||||
|  | ||||
| 	return diffToHTML(diffSection.FileName, diffRecord, diffLine.Type) | ||||
| @@ -597,6 +602,7 @@ type DiffFile struct { | ||||
| 	IsProtected             bool | ||||
| 	IsGenerated             bool | ||||
| 	IsVendored              bool | ||||
| 	Language                string | ||||
| } | ||||
|  | ||||
| // GetType returns type of diff file. | ||||
| @@ -1008,7 +1014,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio | ||||
| 			line := sb.String() | ||||
|  | ||||
| 			// Create a new section to represent this hunk | ||||
| 			curSection = &DiffSection{} | ||||
| 			curSection = &DiffSection{file: curFile} | ||||
| 			lastLeftIdx = -1 | ||||
| 			curFile.Sections = append(curFile.Sections, curSection) | ||||
|  | ||||
| @@ -1048,7 +1054,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio | ||||
| 			rightLine++ | ||||
| 			if curSection == nil { | ||||
| 				// Create a new section to represent this hunk | ||||
| 				curSection = &DiffSection{} | ||||
| 				curSection = &DiffSection{file: curFile} | ||||
| 				curFile.Sections = append(curFile.Sections, curSection) | ||||
| 				lastLeftIdx = -1 | ||||
| 			} | ||||
| @@ -1074,7 +1080,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio | ||||
| 			} | ||||
| 			if curSection == nil { | ||||
| 				// Create a new section to represent this hunk | ||||
| 				curSection = &DiffSection{} | ||||
| 				curSection = &DiffSection{file: curFile} | ||||
| 				curFile.Sections = append(curFile.Sections, curSection) | ||||
| 				lastLeftIdx = -1 | ||||
| 			} | ||||
| @@ -1094,7 +1100,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio | ||||
| 			lastLeftIdx = -1 | ||||
| 			if curSection == nil { | ||||
| 				// Create a new section to represent this hunk | ||||
| 				curSection = &DiffSection{} | ||||
| 				curSection = &DiffSection{file: curFile} | ||||
| 				curFile.Sections = append(curFile.Sections, curSection) | ||||
| 			} | ||||
| 			curSection.Lines = append(curSection.Lines, diffLine) | ||||
| @@ -1302,23 +1308,15 @@ func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID, | ||||
| 	var checker *git.CheckAttributeReader | ||||
|  | ||||
| 	if git.CheckGitVersionAtLeast("1.7.8") == nil { | ||||
| 		indexFilename, deleteTemporaryFile, err := gitRepo.ReadTreeToTemporaryIndex(afterCommitID) | ||||
| 		indexFilename, worktree, deleteTemporaryFile, err := gitRepo.ReadTreeToTemporaryIndex(afterCommitID) | ||||
| 		if err == nil { | ||||
| 			defer deleteTemporaryFile() | ||||
| 			workdir, err := os.MkdirTemp("", "empty-work-dir") | ||||
| 			if err != nil { | ||||
| 				log.Error("Unable to create temporary directory: %v", err) | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			defer func() { | ||||
| 				_ = util.RemoveAll(workdir) | ||||
| 			}() | ||||
|  | ||||
| 			checker = &git.CheckAttributeReader{ | ||||
| 				Attributes: []string{"linguist-vendored", "linguist-generated"}, | ||||
| 				Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"}, | ||||
| 				Repo:       gitRepo, | ||||
| 				IndexFile:  indexFilename, | ||||
| 				WorkTree:   workdir, | ||||
| 				WorkTree:   worktree, | ||||
| 			} | ||||
| 			ctx, cancel := context.WithCancel(git.DefaultContext) | ||||
| 			if err := checker.Init(ctx); err != nil { | ||||
| @@ -1361,6 +1359,11 @@ func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID, | ||||
| 						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 | ||||
| 				} | ||||
| 			} else { | ||||
| 				log.Error("Unexpected error: %v", err) | ||||
| 			} | ||||
|   | ||||
| @@ -533,7 +533,7 @@ func TestGetDiffRangeWithWhitespaceBehavior(t *testing.T) { | ||||
|  | ||||
| func TestDiffToHTML_14231(t *testing.T) { | ||||
| 	setting.Cfg = ini.Empty() | ||||
| 	diffRecord := diffMatchPatch.DiffMain(highlight.Code("main.v", "		run()\n"), highlight.Code("main.v", "		run(db)\n"), true) | ||||
| 	diffRecord := diffMatchPatch.DiffMain(highlight.Code("main.v", "", "		run()\n"), highlight.Code("main.v", "", "		run(db)\n"), true) | ||||
| 	diffRecord = diffMatchPatch.DiffCleanupEfficiency(diffRecord) | ||||
|  | ||||
| 	expected := `		<span class="n">run</span><span class="added-code"><span class="o">(</span><span class="n">db</span></span><span class="o">)</span>` | ||||
|   | ||||
		Reference in New Issue
	
	Block a user