Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 1h ago 4b8c9e1d
history blame raw 4.6 KiB
1// Package lang names the language a filename suggests and picks the language a
2// tree is mostly made of.
3//
4// It is shared by the web pages and by the index's head summary: the home card
5// shows the same answer the repository overview groups by, and the summary is
6// computed when a push lands rather than on every page read.
7
8package lang
9
10import (
11 "path"
12 "sort"
13 "strings"
14
15 "rodent.software/vole/core/git"
16)
17
18// For names the language a filename suggests. Unknown types are "Other"; the
19// map is a hint, not a classifier.
20func For(name string) string {
21 switch strings.ToLower(path.Ext(name)) {
22 case ".go":
23 return "Go"
24 case ".js", ".mjs", ".cjs":
25 return "JavaScript"
26 case ".ts", ".tsx":
27 return "TypeScript"
28 case ".jsx":
29 return "JavaScript"
30 case ".py":
31 return "Python"
32 case ".rs":
33 return "Rust"
34 case ".rb":
35 return "Ruby"
36 case ".java":
37 return "Java"
38 case ".kt", ".kts":
39 return "Kotlin"
40 case ".c", ".h":
41 return "C"
42 case ".cc", ".cpp", ".hpp":
43 return "C++"
44 case ".cs":
45 return "C#"
46 case ".sh", ".bash", ".zsh":
47 return "Shell"
48 case ".md", ".markdown":
49 return "Markdown"
50 case ".html", ".htm":
51 return "HTML"
52 case ".css", ".scss", ".sass":
53 return "CSS"
54 case ".json":
55 return "JSON"
56 case ".yml", ".yaml":
57 return "YAML"
58 case ".toml":
59 return "TOML"
60 case ".sql":
61 return "SQL"
62 case ".proto":
63 return "Protocol Buffers"
64 case ".lua":
65 return "Lua"
66 case ".php":
67 return "PHP"
68 case ".swift":
69 return "Swift"
70 case ".ex", ".exs":
71 return "Elixir"
72 case ".zig":
73 return "Zig"
74 }
75 return "Other"
76}
77
78// Class is the CSS class a filename's language is drawn with. It is empty for a
79// name the table does not know.
80func Class(name string) string {
81 switch strings.ToLower(path.Ext(name)) {
82 case ".go":
83 return "lang-go"
84 case ".rs":
85 return "lang-rust"
86 case ".js", ".mjs", ".cjs", ".jsx":
87 return "lang-js"
88 case ".ts", ".tsx":
89 return "lang-ts"
90 case ".py":
91 return "lang-py"
92 case ".rb":
93 return "lang-rb"
94 case ".java", ".kt", ".scala":
95 return "lang-jvm"
96 case ".c", ".h":
97 return "lang-c"
98 case ".cc", ".cpp", ".cxx", ".hpp":
99 return "lang-cpp"
100 case ".cs":
101 return "lang-cs"
102 case ".php":
103 return "lang-php"
104 case ".sh", ".bash", ".zsh", ".fish":
105 return "lang-sh"
106 case ".html", ".htm":
107 return "lang-html"
108 case ".css", ".scss", ".sass":
109 return "lang-css"
110 case ".json", ".yaml", ".yml", ".toml", ".ini", ".xml", ".conf":
111 return "lang-config"
112 case ".md", ".markdown", ".mdown", ".mkd", ".txt", ".rst":
113 return "lang-text"
114 case ".sql":
115 return "lang-sql"
116 case ".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp", ".ico":
117 return "lang-image"
118 case ".lock":
119 return "lang-lock"
120 }
121 switch strings.ToLower(name) {
122 case "dockerfile", "containerfile", "makefile", "makefile.am", "cmakelists.txt":
123 return "lang-build"
124 case "go.mod", "go.sum":
125 return "lang-go"
126 }
127 return ""
128}
129
130// Share is how many bytes one language accounts for in a tree. The overview's
131// bar is about size; a card's label is about file count, so the two use
132// different counts on purpose.
133type Share struct {
134 Name string
135 Bytes int64
136}
137
138// Breakdown groups a tree's blobs by language, largest first, by bytes. Unknown
139// types stay in the list under "Other" so the percentages add up.
140func Breakdown(tree []git.EntrySize) []Share {
141 byName := map[string]int64{}
142 for _, e := range tree {
143 if e.Type != "blob" {
144 continue
145 }
146 byName[For(e.Name)] += e.Size
147 }
148 list := make([]Share, 0, len(byName))
149 for name, n := range byName {
150 list = append(list, Share{Name: name, Bytes: n})
151 }
152 sort.Slice(list, func(i, j int) bool {
153 if list[i].Bytes != list[j].Bytes {
154 return list[i].Bytes > list[j].Bytes
155 }
156 return list[i].Name < list[j].Name
157 })
158 return list
159}
160
161// TopSizes is Top for a sized listing, which is what a push already reads for
162// the language breakdown.
163func TopSizes(tree []git.EntrySize) (name, class string) {
164 entries := make([]git.Entry, len(tree))
165 for i, e := range tree {
166 entries[i] = e.Entry
167 }
168 return Top(entries)
169}
170
171// Top picks the language with the most files in a tree. Counting files rather
172// than bytes is deliberate: one generated binary or lock file should not decide
173// the label. Unknown types are ignored unless the tree contains nothing else.
174func Top(tree []git.Entry) (name, class string) {
175 counts := map[string]int{}
176 sample := map[string]string{}
177 for _, e := range tree {
178 if e.Type != "blob" {
179 continue
180 }
181 l := For(e.Name)
182 counts[l]++
183 if _, ok := sample[l]; !ok {
184 sample[l] = e.Name
185 }
186 }
187 best := ""
188 for l, n := range counts {
189 if l == "Other" {
190 continue
191 }
192 if best == "" || n > counts[best] || (n == counts[best] && l < best) {
193 best = l
194 }
195 }
196 if best == "" {
197 return "", ""
198 }
199 return best, Class(sample[best])
200}
201