|
|
@ -6,10 +6,12 @@ import (
|
|
6
|
6
|
"html/template"
|
|
7
|
7
|
"io"
|
|
8
|
8
|
"log"
|
|
|
9
|
"math"
|
|
9
|
10
|
"net/http"
|
|
10
|
11
|
"os"
|
|
11
|
12
|
"path"
|
|
12
|
13
|
"path/filepath"
|
|
|
14
|
"sort"
|
|
13
|
15
|
"strings"
|
|
14
|
16
|
"time"
|
|
15
|
17
|
|
|
|
@ -52,11 +54,12 @@ func main() {
|
|
52
|
54
|
}
|
|
53
|
55
|
|
|
54
|
56
|
type FancyRepo struct {
|
|
55
|
|
repo *git.Repository
|
|
56
|
|
commit *FancyCommit
|
|
57
|
|
commitCount int
|
|
58
|
|
branches []*git.Branch
|
|
59
|
|
contributors []*git.Signature
|
|
|
57
|
repo *git.Repository
|
|
|
58
|
commit *FancyCommit
|
|
|
59
|
commitCount int
|
|
|
60
|
branches []*git.Branch
|
|
|
61
|
contributors []*git.Signature
|
|
|
62
|
languageStats *LanguageStats
|
|
60
|
63
|
}
|
|
61
|
64
|
|
|
62
|
65
|
func NewFancyRepo(repo *git.Repository) *FancyRepo {
|
|
|
@ -166,6 +169,134 @@ func (fr *FancyRepo) Tags() ([]string, error) {
|
|
166
|
169
|
return fr.repo.Tags.List()
|
|
167
|
170
|
}
|
|
168
|
171
|
|
|
|
172
|
var languageSuffixes = map[string]string{
|
|
|
173
|
".c": "C",
|
|
|
174
|
".h": "C",
|
|
|
175
|
".cpp": "C++",
|
|
|
176
|
"CMakeList.txt": "CMake",
|
|
|
177
|
".css": "CSS",
|
|
|
178
|
".go": "Go",
|
|
|
179
|
".html": "HTML",
|
|
|
180
|
".js": "JavaScript",
|
|
|
181
|
".java": "Java",
|
|
|
182
|
"Makefile": "Makefile",
|
|
|
183
|
".py": "Python",
|
|
|
184
|
".rb": "Ruby",
|
|
|
185
|
".rs": "Rust",
|
|
|
186
|
".sh": "Shell",
|
|
|
187
|
".bash": "Shell",
|
|
|
188
|
}
|
|
|
189
|
|
|
|
190
|
var languageColors = map[string]string{
|
|
|
191
|
"C": "#555555",
|
|
|
192
|
"C++": "#f34b7d",
|
|
|
193
|
//"CMake": "",
|
|
|
194
|
"CSS": "#563d7c",
|
|
|
195
|
"Go": "#375eab",
|
|
|
196
|
"HTML": "#e34c26",
|
|
|
197
|
"JavaScript": "#f1e05a",
|
|
|
198
|
"Java": "#b07219",
|
|
|
199
|
"Makefile": "#427819",
|
|
|
200
|
"Python": "#3572A5",
|
|
|
201
|
"Ruby": "#701516",
|
|
|
202
|
"Rust": "#dea584",
|
|
|
203
|
"Shell": "#89e051",
|
|
|
204
|
}
|
|
|
205
|
|
|
|
206
|
func fileToLanguage(filename string) string {
|
|
|
207
|
filename = strings.ToLower(filename)
|
|
|
208
|
for suffix, lang := range languageSuffixes {
|
|
|
209
|
if strings.HasSuffix(filename, suffix) {
|
|
|
210
|
return lang
|
|
|
211
|
}
|
|
|
212
|
}
|
|
|
213
|
return ""
|
|
|
214
|
}
|
|
|
215
|
|
|
|
216
|
type LanguageStats struct {
|
|
|
217
|
percentages map[string]float64
|
|
|
218
|
Languages []*LanguageInfo
|
|
|
219
|
}
|
|
|
220
|
|
|
|
221
|
type LanguageInfo struct {
|
|
|
222
|
Language string
|
|
|
223
|
Percentage float64
|
|
|
224
|
}
|
|
|
225
|
|
|
|
226
|
func (li *LanguageInfo) String() string {
|
|
|
227
|
return fmt.Sprintf("%s (%.2f%%)", li.Language, li.Percentage)
|
|
|
228
|
}
|
|
|
229
|
|
|
|
230
|
type languageByPercent []*LanguageInfo
|
|
|
231
|
|
|
|
232
|
func (l languageByPercent) Len() int { return len(l) }
|
|
|
233
|
func (l languageByPercent) Less(i, j int) bool { return l[i].Percentage < l[j].Percentage }
|
|
|
234
|
func (l languageByPercent) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
|
|
|
235
|
|
|
|
236
|
func NewLanguageStats(percentages map[string]float64) *LanguageStats {
|
|
|
237
|
for lang, percentage := range percentages {
|
|
|
238
|
if percentage < 0.001 {
|
|
|
239
|
delete(percentages, lang)
|
|
|
240
|
continue
|
|
|
241
|
}
|
|
|
242
|
|
|
|
243
|
percentage = math.Trunc(percentage*10000) / 100
|
|
|
244
|
percentages[lang] = percentage
|
|
|
245
|
}
|
|
|
246
|
|
|
|
247
|
languages := make([]*LanguageInfo, 0, len(percentages))
|
|
|
248
|
for lang, percentage := range percentages {
|
|
|
249
|
languages = append(languages, &LanguageInfo{Language: lang, Percentage: percentage})
|
|
|
250
|
}
|
|
|
251
|
sort.Sort(sort.Reverse(languageByPercent(languages)))
|
|
|
252
|
|
|
|
253
|
return &LanguageStats{
|
|
|
254
|
percentages: percentages,
|
|
|
255
|
Languages: languages,
|
|
|
256
|
}
|
|
|
257
|
}
|
|
|
258
|
|
|
|
259
|
func (li *LanguageStats) String() string {
|
|
|
260
|
return fmt.Sprintf("%v", li.percentages)
|
|
|
261
|
}
|
|
|
262
|
|
|
|
263
|
func (fr *FancyRepo) LanguageStats() (*LanguageStats, error) {
|
|
|
264
|
if fr.languageStats == nil {
|
|
|
265
|
commit, err := fr.Commit()
|
|
|
266
|
if err != nil {
|
|
|
267
|
return nil, err
|
|
|
268
|
}
|
|
|
269
|
|
|
|
270
|
files, err := commit.Files(true)
|
|
|
271
|
if err != nil {
|
|
|
272
|
return nil, err
|
|
|
273
|
}
|
|
|
274
|
|
|
|
275
|
total := 0
|
|
|
276
|
languages := make(map[string]int)
|
|
|
277
|
for _, f := range files {
|
|
|
278
|
if f.entry.Type != git.ObjectBlob {
|
|
|
279
|
continue
|
|
|
280
|
}
|
|
|
281
|
|
|
|
282
|
lang := fileToLanguage(f.Name())
|
|
|
283
|
if lang == "" {
|
|
|
284
|
continue
|
|
|
285
|
}
|
|
|
286
|
total += 1
|
|
|
287
|
languages[lang] += 1
|
|
|
288
|
|
|
|
289
|
}
|
|
|
290
|
|
|
|
291
|
percentages := make(map[string]float64)
|
|
|
292
|
for lang, count := range languages {
|
|
|
293
|
percentages[lang] = float64(count) / float64(total)
|
|
|
294
|
}
|
|
|
295
|
fr.languageStats = NewLanguageStats(percentages)
|
|
|
296
|
}
|
|
|
297
|
return fr.languageStats, nil
|
|
|
298
|
}
|
|
|
299
|
|
|
169
|
300
|
type FancyCommit struct {
|
|
170
|
301
|
commit *git.Commit
|
|
171
|
302
|
files []*FancyFile
|
|
|
@ -227,16 +358,20 @@ func (fc *FancyCommit) Description() string {
|
|
227
|
358
|
return fc.commit.Message()
|
|
228
|
359
|
}
|
|
229
|
360
|
|
|
230
|
|
func (fc *FancyCommit) Files() ([]*FancyFile, error) {
|
|
|
361
|
func (fc *FancyCommit) Files(recursive bool) ([]*FancyFile, error) {
|
|
231
|
362
|
if fc.files == nil {
|
|
232
|
363
|
tree, err := fc.commit.Tree()
|
|
233
|
364
|
if err != nil {
|
|
234
|
365
|
return nil, err
|
|
235
|
366
|
}
|
|
236
|
367
|
|
|
|
368
|
recurse := 1
|
|
|
369
|
if recursive {
|
|
|
370
|
recurse = 0
|
|
|
371
|
}
|
|
237
|
372
|
err = tree.Walk(func(s string, entry *git.TreeEntry) int {
|
|
238
|
373
|
fc.files = append(fc.files, NewFancyFile(fc.commit.Owner(), entry))
|
|
239
|
|
return 1
|
|
|
374
|
return recurse
|
|
240
|
375
|
})
|
|
241
|
376
|
if err != nil {
|
|
242
|
377
|
return nil, err
|
|
|
@ -331,7 +466,16 @@ func (fc *FancyFile) Contents() (template.HTML, error) {
|
|
331
|
466
|
return template.HTML(buf.String()), nil
|
|
332
|
467
|
}
|
|
333
|
468
|
|
|
334
|
|
var repoTmpl = template.Must(template.New("").Parse(`<!doctype html>
|
|
|
469
|
var repoFuncs = template.FuncMap{
|
|
|
470
|
"languageToColor": func(l string) string {
|
|
|
471
|
c, ok := languageColors[l]
|
|
|
472
|
if !ok {
|
|
|
473
|
return "black"
|
|
|
474
|
}
|
|
|
475
|
return c
|
|
|
476
|
},
|
|
|
477
|
}
|
|
|
478
|
var repoTmpl = template.Must(template.New("").Funcs(repoFuncs).Parse(`<!doctype html>
|
|
335
|
479
|
<html>
|
|
336
|
480
|
<head>
|
|
337
|
481
|
<meta charset="utf-8" />
|
|
|
@ -347,6 +491,23 @@ var repoTmpl = template.Must(template.New("").Parse(`<!doctype html>
|
|
347
|
491
|
<a id="contributors" class="repo-stat" href="#"><span class="icon">👥</span><span class="count">{{ len .Repo.Contributors }}</span> contributors</a>
|
|
348
|
492
|
</section>
|
|
349
|
493
|
|
|
|
494
|
<div id="languages">
|
|
|
495
|
<ol class="language-list">
|
|
|
496
|
{{ range $language := .Repo.LanguageStats.Languages }}
|
|
|
497
|
<li>
|
|
|
498
|
<span class="language">{{ $language.Language }}</span>
|
|
|
499
|
<span class="percentage">{{ $language.Percentage }}%</span>
|
|
|
500
|
</li>
|
|
|
501
|
{{ end }}
|
|
|
502
|
</ol>
|
|
|
503
|
|
|
|
504
|
<div id="language-bar">
|
|
|
505
|
{{ range $language := .Repo.LanguageStats.Languages }}
|
|
|
506
|
<span class="language" title="{{ $language }}" style="width: {{ $language.Percentage }}%; background-color: {{ languageToColor $language.Language }}"><!--{{ $language.Language }}--></span>
|
|
|
507
|
{{ end }}
|
|
|
508
|
</div>
|
|
|
509
|
</div>
|
|
|
510
|
|
|
350
|
511
|
<section id="commit" class="commit-info">
|
|
351
|
512
|
<div class="commit-author">{{ .Repo.Commit.Author }}</div>
|
|
352
|
513
|
<div class="commit-summary" title="{{ .Repo.Commit.Description }}">{{ .Repo.Commit.Summary }}</div>
|
|
|
@ -355,7 +516,7 @@ var repoTmpl = template.Must(template.New("").Parse(`<!doctype html>
|
|
355
|
516
|
</section>
|
|
356
|
517
|
|
|
357
|
518
|
<table id="files">
|
|
358
|
|
{{ range $file := .Repo.Commit.Files }}
|
|
|
519
|
{{ range $file := .Repo.Commit.Files false }}
|
|
359
|
520
|
<tr class="file">
|
|
360
|
521
|
<td class="file-name file-type-{{ $file.Type }}">{{ $file.Name }}</td>
|
|
361
|
522
|
</tr>
|
|
|
@ -416,6 +577,17 @@ body {
|
|
416
|
577
|
padding-right: 0.2em;
|
|
417
|
578
|
}
|
|
418
|
579
|
|
|
|
580
|
.language-list {
|
|
|
581
|
display: none;
|
|
|
582
|
}
|
|
|
583
|
|
|
|
584
|
#language-bar {
|
|
|
585
|
display: flex;
|
|
|
586
|
width: 70vw;
|
|
|
587
|
height: 0.5em;
|
|
|
588
|
margin: -1em 0 1em;
|
|
|
589
|
}
|
|
|
590
|
|
|
419
|
591
|
#commit {
|
|
420
|
592
|
display: flex;
|
|
421
|
593
|
justify-content: space-between;
|