mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Show syntax lexer name in file view/blame (#21814)
Show which Chroma Lexer is used to highlight the file in the file header. It's useful for development to see what was detected, and I think it's not bad info to have for the user: <img width="233" alt="Screenshot 2022-11-14 at 22 31 16" src="https://user-images.githubusercontent.com/115237/201770854-44933dfc-70a4-487c-8457-1bb3cc43ea62.png"> <img width="226" alt="Screenshot 2022-11-14 at 22 36 06" src="https://user-images.githubusercontent.com/115237/201770856-9260ce6f-6c0f-442c-92b5-201e5b113188.png"> <img width="194" alt="Screenshot 2022-11-14 at 22 36 26" src="https://user-images.githubusercontent.com/115237/201770857-6f56591b-80ea-42cc-8ea5-21b9156c018b.png"> Also, I improved the way this header overflows on small screens: <img width="354" alt="Screenshot 2022-11-14 at 22 44 36" src="https://user-images.githubusercontent.com/115237/201774828-2ddbcde1-da15-403f-bf7a-6248449fa2c5.png"> Co-authored-by: delvh <dev.lh@web.de> Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: John Olheiser <john.olheiser@gmail.com>
This commit is contained in:
		| @@ -18,6 +18,7 @@ import ( | |||||||
| 	"code.gitea.io/gitea/modules/analyze" | 	"code.gitea.io/gitea/modules/analyze" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 	"code.gitea.io/gitea/modules/util" | ||||||
|  |  | ||||||
| 	"github.com/alecthomas/chroma/v2" | 	"github.com/alecthomas/chroma/v2" | ||||||
| 	"github.com/alecthomas/chroma/v2/formatters/html" | 	"github.com/alecthomas/chroma/v2/formatters/html" | ||||||
| @@ -56,18 +57,18 @@ func NewContext() { | |||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Code returns a HTML version of code string with chroma syntax highlighting classes | // Code returns a HTML version of code string with chroma syntax highlighting classes and the matched lexer name | ||||||
| func Code(fileName, language, code string) string { | func Code(fileName, language, code string) (string, string) { | ||||||
| 	NewContext() | 	NewContext() | ||||||
|  |  | ||||||
| 	// diff view newline will be passed as empty, change to literal '\n' so it can be copied | 	// diff view newline will be passed as empty, change to literal '\n' so it can be copied | ||||||
| 	// preserve literal newline in blame view | 	// preserve literal newline in blame view | ||||||
| 	if code == "" || code == "\n" { | 	if code == "" || code == "\n" { | ||||||
| 		return "\n" | 		return "\n", "" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(code) > sizeLimit { | 	if len(code) > sizeLimit { | ||||||
| 		return code | 		return code, "" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var lexer chroma.Lexer | 	var lexer chroma.Lexer | ||||||
| @@ -103,7 +104,10 @@ func Code(fileName, language, code string) string { | |||||||
| 		} | 		} | ||||||
| 		cache.Add(fileName, lexer) | 		cache.Add(fileName, lexer) | ||||||
| 	} | 	} | ||||||
| 	return CodeFromLexer(lexer, code) |  | ||||||
|  | 	lexerName := formatLexerName(lexer.Config().Name) | ||||||
|  |  | ||||||
|  | 	return CodeFromLexer(lexer, code), lexerName | ||||||
| } | } | ||||||
|  |  | ||||||
| // CodeFromLexer returns a HTML version of code string with chroma syntax highlighting classes | // CodeFromLexer returns a HTML version of code string with chroma syntax highlighting classes | ||||||
| @@ -134,12 +138,12 @@ func CodeFromLexer(lexer chroma.Lexer, code string) string { | |||||||
| 	return strings.TrimSuffix(htmlbuf.String(), "\n") | 	return strings.TrimSuffix(htmlbuf.String(), "\n") | ||||||
| } | } | ||||||
|  |  | ||||||
| // File returns a slice of chroma syntax highlighted HTML lines of code | // File returns a slice of chroma syntax highlighted HTML lines of code and the matched lexer name | ||||||
| func File(fileName, language string, code []byte) ([]string, error) { | func File(fileName, language string, code []byte) ([]string, string, error) { | ||||||
| 	NewContext() | 	NewContext() | ||||||
|  |  | ||||||
| 	if len(code) > sizeLimit { | 	if len(code) > sizeLimit { | ||||||
| 		return PlainText(code), nil | 		return PlainText(code), "", nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	formatter := html.New(html.WithClasses(true), | 	formatter := html.New(html.WithClasses(true), | ||||||
| @@ -172,9 +176,11 @@ func File(fileName, language string, code []byte) ([]string, error) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	lexerName := formatLexerName(lexer.Config().Name) | ||||||
|  |  | ||||||
| 	iterator, err := lexer.Tokenise(nil, string(code)) | 	iterator, err := lexer.Tokenise(nil, string(code)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("can't tokenize code: %w", err) | 		return nil, "", fmt.Errorf("can't tokenize code: %w", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	tokensLines := chroma.SplitTokensIntoLines(iterator.Tokens()) | 	tokensLines := chroma.SplitTokensIntoLines(iterator.Tokens()) | ||||||
| @@ -185,13 +191,13 @@ func File(fileName, language string, code []byte) ([]string, error) { | |||||||
| 		iterator = chroma.Literator(tokens...) | 		iterator = chroma.Literator(tokens...) | ||||||
| 		err = formatter.Format(htmlBuf, styles.GitHub, iterator) | 		err = formatter.Format(htmlBuf, styles.GitHub, iterator) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, fmt.Errorf("can't format code: %w", err) | 			return nil, "", fmt.Errorf("can't format code: %w", err) | ||||||
| 		} | 		} | ||||||
| 		lines = append(lines, htmlBuf.String()) | 		lines = append(lines, htmlBuf.String()) | ||||||
| 		htmlBuf.Reset() | 		htmlBuf.Reset() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return lines, nil | 	return lines, lexerName, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // PlainText returns non-highlighted HTML for code | // PlainText returns non-highlighted HTML for code | ||||||
| @@ -212,3 +218,11 @@ func PlainText(code []byte) []string { | |||||||
| 	} | 	} | ||||||
| 	return m | 	return m | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func formatLexerName(name string) string { | ||||||
|  | 	if name == "fallback" { | ||||||
|  | 		return "Plaintext" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return util.ToTitleCaseNoLower(name) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -20,31 +20,49 @@ func TestFile(t *testing.T) { | |||||||
| 		name      string | 		name      string | ||||||
| 		code      string | 		code      string | ||||||
| 		want      []string | 		want      []string | ||||||
|  | 		lexerName string | ||||||
| 	}{ | 	}{ | ||||||
| 		{ | 		{ | ||||||
| 			name:      "empty.py", | 			name:      "empty.py", | ||||||
| 			code:      "", | 			code:      "", | ||||||
| 			want:      lines(""), | 			want:      lines(""), | ||||||
|  | 			lexerName: "Python", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:      "empty.js", | ||||||
|  | 			code:      "", | ||||||
|  | 			want:      lines(""), | ||||||
|  | 			lexerName: "JavaScript", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:      "empty.yaml", | ||||||
|  | 			code:      "", | ||||||
|  | 			want:      lines(""), | ||||||
|  | 			lexerName: "YAML", | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name:      "tags.txt", | 			name:      "tags.txt", | ||||||
| 			code:      "<>", | 			code:      "<>", | ||||||
| 			want:      lines("<>"), | 			want:      lines("<>"), | ||||||
|  | 			lexerName: "Plaintext", | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name:      "tags.py", | 			name:      "tags.py", | ||||||
| 			code:      "<>", | 			code:      "<>", | ||||||
| 			want:      lines(`<span class="o"><</span><span class="o">></span>`), | 			want:      lines(`<span class="o"><</span><span class="o">></span>`), | ||||||
|  | 			lexerName: "Python", | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name:      "eol-no.py", | 			name:      "eol-no.py", | ||||||
| 			code:      "a=1", | 			code:      "a=1", | ||||||
| 			want:      lines(`<span class="n">a</span><span class="o">=</span><span class="mi">1</span>`), | 			want:      lines(`<span class="n">a</span><span class="o">=</span><span class="mi">1</span>`), | ||||||
|  | 			lexerName: "Python", | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name:      "eol-newline1.py", | 			name:      "eol-newline1.py", | ||||||
| 			code:      "a=1\n", | 			code:      "a=1\n", | ||||||
| 			want:      lines(`<span class="n">a</span><span class="o">=</span><span class="mi">1</span>\n`), | 			want:      lines(`<span class="n">a</span><span class="o">=</span><span class="mi">1</span>\n`), | ||||||
|  | 			lexerName: "Python", | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "eol-newline2.py", | 			name: "eol-newline2.py", | ||||||
| @@ -54,6 +72,7 @@ func TestFile(t *testing.T) { | |||||||
| \n | \n | ||||||
| 			`, | 			`, | ||||||
| 			), | 			), | ||||||
|  | 			lexerName: "Python", | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "empty-line-with-space.py", | 			name: "empty-line-with-space.py", | ||||||
| @@ -73,17 +92,19 @@ c=2 | |||||||
|     \n |     \n | ||||||
| <span class="n">c</span><span class="o">=</span><span class="mi">2</span>`, | <span class="n">c</span><span class="o">=</span><span class="mi">2</span>`, | ||||||
| 			), | 			), | ||||||
|  | 			lexerName: "Python", | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, tt := range tests { | 	for _, tt := range tests { | ||||||
| 		t.Run(tt.name, func(t *testing.T) { | 		t.Run(tt.name, func(t *testing.T) { | ||||||
| 			out, err := File(tt.name, "", []byte(tt.code)) | 			out, lexerName, err := File(tt.name, "", []byte(tt.code)) | ||||||
| 			assert.NoError(t, err) | 			assert.NoError(t, err) | ||||||
| 			expected := strings.Join(tt.want, "\n") | 			expected := strings.Join(tt.want, "\n") | ||||||
| 			actual := strings.Join(out, "\n") | 			actual := strings.Join(out, "\n") | ||||||
| 			assert.Equal(t, strings.Count(actual, "<span"), strings.Count(actual, "</span>")) | 			assert.Equal(t, strings.Count(actual, "<span"), strings.Count(actual, "</span>")) | ||||||
| 			assert.EqualValues(t, expected, actual) | 			assert.EqualValues(t, expected, actual) | ||||||
|  | 			assert.Equal(t, tt.lexerName, lexerName) | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -94,6 +94,9 @@ func searchResult(result *SearchResult, startIndex, endIndex int) (*Result, erro | |||||||
| 		lineNumbers[i] = startLineNum + i | 		lineNumbers[i] = startLineNum + i | ||||||
| 		index += len(line) | 		index += len(line) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	highlighted, _ := highlight.Code(result.Filename, "", formattedLinesBuffer.String()) | ||||||
|  |  | ||||||
| 	return &Result{ | 	return &Result{ | ||||||
| 		RepoID:         result.RepoID, | 		RepoID:         result.RepoID, | ||||||
| 		Filename:       result.Filename, | 		Filename:       result.Filename, | ||||||
| @@ -102,7 +105,7 @@ func searchResult(result *SearchResult, startIndex, endIndex int) (*Result, erro | |||||||
| 		Language:       result.Language, | 		Language:       result.Language, | ||||||
| 		Color:          result.Color, | 		Color:          result.Color, | ||||||
| 		LineNumbers:    lineNumbers, | 		LineNumbers:    lineNumbers, | ||||||
| 		FormattedLines: highlight.Code(result.Filename, "", formattedLinesBuffer.String()), | 		FormattedLines: highlighted, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -186,13 +186,21 @@ func ToUpperASCII(s string) string { | |||||||
| 	return string(b) | 	return string(b) | ||||||
| } | } | ||||||
|  |  | ||||||
| var titleCaser = cases.Title(language.English) | var ( | ||||||
|  | 	titleCaser        = cases.Title(language.English) | ||||||
|  | 	titleCaserNoLower = cases.Title(language.English, cases.NoLower) | ||||||
|  | ) | ||||||
|  |  | ||||||
| // ToTitleCase returns s with all english words capitalized | // ToTitleCase returns s with all english words capitalized | ||||||
| func ToTitleCase(s string) string { | func ToTitleCase(s string) string { | ||||||
| 	return titleCaser.String(s) | 	return titleCaser.String(s) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ToTitleCaseNoLower returns s with all english words capitalized without lowercasing | ||||||
|  | func ToTitleCaseNoLower(s string) string { | ||||||
|  | 	return titleCaserNoLower.String(s) | ||||||
|  | } | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	whitespaceOnly    = regexp.MustCompile("(?m)^[ \t]+$") | 	whitespaceOnly    = regexp.MustCompile("(?m)^[ \t]+$") | ||||||
| 	leadingWhitespace = regexp.MustCompile("(?m)(^[ \t]*)(?:[^ \t\n])") | 	leadingWhitespace = regexp.MustCompile("(?m)(^[ \t]*)(?:[^ \t\n])") | ||||||
|   | |||||||
| @@ -100,6 +100,8 @@ func RefBlame(ctx *context.Context) { | |||||||
| 	ctx.Data["FileName"] = blob.Name() | 	ctx.Data["FileName"] = blob.Name() | ||||||
|  |  | ||||||
| 	ctx.Data["NumLines"], err = blob.GetBlobLineCount() | 	ctx.Data["NumLines"], err = blob.GetBlobLineCount() | ||||||
|  | 	ctx.Data["NumLinesSet"] = true | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.NotFound("GetBlobLineCount", err) | 		ctx.NotFound("GetBlobLineCount", err) | ||||||
| 		return | 		return | ||||||
| @@ -237,6 +239,8 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m | |||||||
| 	rows := make([]*blameRow, 0) | 	rows := make([]*blameRow, 0) | ||||||
| 	escapeStatus := &charset.EscapeStatus{} | 	escapeStatus := &charset.EscapeStatus{} | ||||||
|  |  | ||||||
|  | 	var lexerName string | ||||||
|  |  | ||||||
| 	i := 0 | 	i := 0 | ||||||
| 	commitCnt := 0 | 	commitCnt := 0 | ||||||
| 	for _, part := range blameParts { | 	for _, part := range blameParts { | ||||||
| @@ -278,7 +282,13 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m | |||||||
| 				line += "\n" | 				line += "\n" | ||||||
| 			} | 			} | ||||||
| 			fileName := fmt.Sprintf("%v", ctx.Data["FileName"]) | 			fileName := fmt.Sprintf("%v", ctx.Data["FileName"]) | ||||||
| 			line = highlight.Code(fileName, language, line) | 			line, lexerNameForLine := highlight.Code(fileName, language, line) | ||||||
|  |  | ||||||
|  | 			// set lexer name to the first detected lexer. this is certainly suboptimal and | ||||||
|  | 			// we should instead highlight the whole file at once | ||||||
|  | 			if lexerName == "" { | ||||||
|  | 				lexerName = lexerNameForLine | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			br.EscapeStatus, line = charset.EscapeControlHTML(line, ctx.Locale) | 			br.EscapeStatus, line = charset.EscapeControlHTML(line, ctx.Locale) | ||||||
| 			br.Code = gotemplate.HTML(line) | 			br.Code = gotemplate.HTML(line) | ||||||
| @@ -290,4 +300,5 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m | |||||||
| 	ctx.Data["EscapeStatus"] = escapeStatus | 	ctx.Data["EscapeStatus"] = escapeStatus | ||||||
| 	ctx.Data["BlameRows"] = rows | 	ctx.Data["BlameRows"] = rows | ||||||
| 	ctx.Data["CommitCnt"] = commitCnt | 	ctx.Data["CommitCnt"] = commitCnt | ||||||
|  | 	ctx.Data["LexerName"] = lexerName | ||||||
| } | } | ||||||
|   | |||||||
| @@ -568,7 +568,8 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st | |||||||
| 					language = "" | 					language = "" | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			fileContent, err := highlight.File(blob.Name(), language, buf) | 			fileContent, lexerName, err := highlight.File(blob.Name(), language, buf) | ||||||
|  | 			ctx.Data["LexerName"] = lexerName | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Error("highlight.File failed, fallback to plain text: %v", err) | 				log.Error("highlight.File failed, fallback to plain text: %v", err) | ||||||
| 				fileContent = highlight.PlainText(buf) | 				fileContent = highlight.PlainText(buf) | ||||||
|   | |||||||
| @@ -280,7 +280,8 @@ func DiffInlineWithUnicodeEscape(s template.HTML, locale translation.Locale) Dif | |||||||
|  |  | ||||||
| // DiffInlineWithHighlightCode makes a DiffInline with code highlight and hidden unicode characters escaped | // DiffInlineWithHighlightCode makes a DiffInline with code highlight and hidden unicode characters escaped | ||||||
| func DiffInlineWithHighlightCode(fileName, language, code string, locale translation.Locale) DiffInline { | func DiffInlineWithHighlightCode(fileName, language, code string, locale translation.Locale) DiffInline { | ||||||
| 	status, content := charset.EscapeControlHTML(highlight.Code(fileName, language, code), locale) | 	highlighted, _ := highlight.Code(fileName, language, code) | ||||||
|  | 	status, content := charset.EscapeControlHTML(highlighted, locale) | ||||||
| 	return DiffInline{EscapeStatus: status, Content: template.HTML(content)} | 	return DiffInline{EscapeStatus: status, Content: template.HTML(content)} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -91,8 +91,8 @@ func (hcd *highlightCodeDiff) diffWithHighlight(filename, language, codeA, codeB | |||||||
| 	hcd.collectUsedRunes(codeA) | 	hcd.collectUsedRunes(codeA) | ||||||
| 	hcd.collectUsedRunes(codeB) | 	hcd.collectUsedRunes(codeB) | ||||||
|  |  | ||||||
| 	highlightCodeA := highlight.Code(filename, language, codeA) | 	highlightCodeA, _ := highlight.Code(filename, language, codeA) | ||||||
| 	highlightCodeB := highlight.Code(filename, language, codeB) | 	highlightCodeB, _ := highlight.Code(filename, language, codeB) | ||||||
|  |  | ||||||
| 	highlightCodeA = hcd.convertToPlaceholders(highlightCodeA) | 	highlightCodeA = hcd.convertToPlaceholders(highlightCodeA) | ||||||
| 	highlightCodeB = hcd.convertToPlaceholders(highlightCodeB) | 	highlightCodeB = hcd.convertToPlaceholders(highlightCodeB) | ||||||
|   | |||||||
| @@ -1,14 +1,9 @@ | |||||||
| <div class="{{TabSizeClass .Editorconfig .FileName}} non-diff-file-content"> | <div class="{{TabSizeClass .Editorconfig .FileName}} non-diff-file-content"> | ||||||
| 	<h4 class="file-header ui top attached header df ac sb"> | 	<h4 class="file-header ui top attached header df ac sb fw"> | ||||||
| 		<div class="file-header-left df ac"> | 		<div class="file-header-left df ac py-3 pr-4"> | ||||||
| 			<div class="file-info text grey normal mono"> | 			{{template "repo/file_info" .}} | ||||||
| 				<div class="file-info-entry"> |  | ||||||
| 					{{.NumLines}} {{.locale.TrN .NumLines "repo.line" "repo.lines"}} |  | ||||||
| 		</div> | 		</div> | ||||||
| 				<div class="file-info-entry">{{FileSize .FileSize}}</div> | 		<div class="file-header-right file-actions df ac fw"> | ||||||
| 			</div> |  | ||||||
| 		</div> |  | ||||||
| 		<div class="file-header-right file-actions df ac"> |  | ||||||
| 			<div class="ui buttons"> | 			<div class="ui buttons"> | ||||||
| 				<a class="ui tiny button" href="{{$.RawFileLink}}">{{.locale.Tr "repo.file_raw"}}</a> | 				<a class="ui tiny button" href="{{$.RawFileLink}}">{{.locale.Tr "repo.file_raw"}}</a> | ||||||
| 				{{if not .IsViewCommit}} | 				{{if not .IsViewCommit}} | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								templates/repo/file_info.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								templates/repo/file_info.tmpl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | <div class="file-info text grey normal mono"> | ||||||
|  | 	{{if .FileIsSymlink}} | ||||||
|  | 		<div class="file-info-entry"> | ||||||
|  | 			{{.locale.Tr "repo.symbolic_link"}} | ||||||
|  | 		</div> | ||||||
|  | 	{{end}} | ||||||
|  | 	{{if .NumLinesSet}}{{/* Explicit attribute needed to show 0 line changes */}} | ||||||
|  | 		<div class="file-info-entry"> | ||||||
|  | 			{{.NumLines}} {{.locale.TrN .NumLines "repo.line" "repo.lines"}} | ||||||
|  | 		</div> | ||||||
|  | 	{{end}} | ||||||
|  | 	{{if .FileSize}} | ||||||
|  | 		<div class="file-info-entry"> | ||||||
|  | 			{{FileSize .FileSize}}{{if .IsLFSFile}} ({{.locale.Tr "repo.stored_lfs"}}){{end}} | ||||||
|  | 		</div> | ||||||
|  | 	{{end}} | ||||||
|  | 	{{if .LFSLock}} | ||||||
|  | 		<div class="file-info-entry ui tooltip" data-content="{{.LFSLockHint}}"> | ||||||
|  | 			{{svg "octicon-lock" 16 "mr-2"}} | ||||||
|  | 			<a href="{{.LFSLockOwnerHomeLink}}">{{.LFSLockOwner}}</a> | ||||||
|  | 		</div> | ||||||
|  | 	{{end}} | ||||||
|  | 	{{if .LexerName}} | ||||||
|  | 		<div class="file-info-entry"> | ||||||
|  | 			{{.LexerName}} | ||||||
|  | 		</div> | ||||||
|  | 	{{end}} | ||||||
|  | </div> | ||||||
| @@ -6,38 +6,16 @@ | |||||||
| 			</div> | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
| 	{{end}} | 	{{end}} | ||||||
| 	<h4 class="file-header ui top attached header df ac sb"> | 	<h4 class="file-header ui top attached header df ac sb fw"> | ||||||
| 		<div class="file-header-left df ac pr-4"> | 		<div class="file-header-left df ac py-3 pr-4"> | ||||||
| 			{{if .ReadmeInList}} | 			{{if .ReadmeInList}} | ||||||
| 				{{svg "octicon-book" 16 "mr-3"}} | 				{{svg "octicon-book" 16 "mr-3"}} | ||||||
| 				<strong>{{.FileName}}</strong> | 				<strong>{{.FileName}}</strong> | ||||||
| 			{{else}} | 			{{else}} | ||||||
| 				<div class="file-info text grey normal mono"> | 				{{template "repo/file_info" .}} | ||||||
| 					{{if .FileIsSymlink}} |  | ||||||
| 						<div class="file-info-entry"> |  | ||||||
| 							{{.locale.Tr "repo.symbolic_link"}} |  | ||||||
| 						</div> |  | ||||||
| 					{{end}} |  | ||||||
| 					{{if .NumLinesSet}} |  | ||||||
| 						<div class="file-info-entry"> |  | ||||||
| 							{{.NumLines}} {{.locale.TrN .NumLines "repo.line" "repo.lines"}} |  | ||||||
| 						</div> |  | ||||||
| 					{{end}} |  | ||||||
| 					{{if .FileSize}} |  | ||||||
| 						<div class="file-info-entry"> |  | ||||||
| 							{{FileSize .FileSize}}{{if .IsLFSFile}} ({{.locale.Tr "repo.stored_lfs"}}){{end}} |  | ||||||
| 						</div> |  | ||||||
| 					{{end}} |  | ||||||
| 					{{if .LFSLock}} |  | ||||||
| 						<div class="file-info-entry ui tooltip" data-content="{{.LFSLockHint}}"> |  | ||||||
| 							{{svg "octicon-lock" 16 "mr-2"}} |  | ||||||
| 							<a href="{{.LFSLockOwnerHomeLink}}">{{.LFSLockOwner}}</a> |  | ||||||
| 						</div> |  | ||||||
| 			{{end}} | 			{{end}} | ||||||
| 		</div> | 		</div> | ||||||
| 			{{end}} | 		<div class="file-header-right file-actions df ac fw"> | ||||||
| 		</div> |  | ||||||
| 		<div class="file-header-right file-actions df ac"> |  | ||||||
| 			{{if .HasSourceRenderedToggle}} | 			{{if .HasSourceRenderedToggle}} | ||||||
| 				<div class="ui compact icon buttons two-toggle-buttons"> | 				<div class="ui compact icon buttons two-toggle-buttons"> | ||||||
| 					<a href="{{$.Link}}?display=source" class="ui mini basic button tooltip {{if .IsDisplayingSource}}active{{end}}" data-content="{{.locale.Tr "repo.file_view_source"}}" data-position="bottom center">{{svg "octicon-code" 15}}</a> | 					<a href="{{$.Link}}?display=source" class="ui mini basic button tooltip {{if .IsDisplayingSource}}active{{end}}" data-content="{{.locale.Tr "repo.file_view_source"}}" data-position="bottom center">{{svg "octicon-code" 15}}</a> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user