Skip to content

Commit 9c4b709

Browse files
authored
Improvements languages indepth analysis (lowlighter#329)
1 parent 87b5ed3 commit 9c4b709

File tree

9 files changed

+91
-76
lines changed

9 files changed

+91
-76
lines changed

source/app/mocks/api/github/graphql/base.repositories.mjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ export default function({faker, query, login = faker.internet.userName()}) {
44
return /after: "MOCKED_CURSOR"/m.test(query)
55
? ({
66
user:{
7+
get repositoriesContributedTo() {
8+
return this.repositories
9+
},
710
repositories:{
811
edges:[],
912
nodes:[],
@@ -12,6 +15,9 @@ export default function({faker, query, login = faker.internet.userName()}) {
1215
})
1316
: ({
1417
user:{
18+
get repositoriesContributedTo() {
19+
return this.repositories
20+
},
1521
repositories:{
1622
edges:[
1723
{

source/plugins/base/index.mjs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,22 +34,24 @@ export default async function({login, graphql, data, q, queries, imports}, conf)
3434
Object.assign(data, {user:queried[account]})
3535
postprocess?.[account]({login, data})
3636
//Query repositories from GitHub API
37-
{
37+
data.user.repositoriesContributedTo.nodes = data.user.repositoriesContributedTo.nodes ?? []
38+
for (const type of ["repositories", "repositoriesContributedTo"]) {
3839
//Iterate through repositories
3940
let cursor = null
4041
let pushed = 0
42+
const options = {repositories:{forks, affiliations, constraints:""}, repositoriesContributedTo:{forks:"", affiliations:"", constraints:", includeUserRepositories: false, contributionTypes: COMMIT"}}[type] ?? null
4143
do {
42-
console.debug(`metrics/compute/${login}/base > retrieving repositories after ${cursor}`)
43-
const {[account]:{repositories:{edges, nodes}}} = await graphql(queries.base.repositories({login, account, after:cursor ? `after: "${cursor}"` : "", repositories:Math.min(repositories, {user:100, organization:25}[account]), forks, affiliations}))
44+
console.debug(`metrics/compute/${login}/base > retrieving ${type} after ${cursor}`)
45+
const {[account]:{[type]:{edges = [], nodes = []} = {}}} = await graphql(queries.base.repositories({login, account, type, after:cursor ? `after: "${cursor}"` : "", repositories:Math.min(repositories, {user:100, organization:25}[account]), ...options}))
4446
cursor = edges?.[edges?.length - 1]?.cursor
45-
data.user.repositories.nodes.push(...nodes)
47+
data.user[type].nodes.push(...nodes)
4648
pushed = nodes.length
47-
console.debug(`metrics/compute/${login}/base > retrieved ${pushed} repositories after ${cursor}`)
48-
} while ((pushed) && (cursor) && (data.user.repositories.nodes.length < repositories))
49+
console.debug(`metrics/compute/${login}/base > retrieved ${pushed} ${type} after ${cursor}`)
50+
} while ((pushed) && (cursor) && (data.user.repositories.nodes.length + data.user.repositoriesContributedTo.nodes.length < repositories))
4951
//Limit repositories
50-
console.debug(`metrics/compute/${login}/base > keeping only ${repositories} repositories`)
51-
data.user.repositories.nodes.splice(repositories)
52-
console.debug(`metrics/compute/${login}/base > loaded ${data.user.repositories.nodes.length} repositories`)
52+
console.debug(`metrics/compute/${login}/base > keeping only ${repositories} ${type}`)
53+
data.user[type].nodes.splice(repositories)
54+
console.debug(`metrics/compute/${login}/base > loaded ${data.user[type].nodes.length} ${type}`)
5355
}
5456
//Success
5557
console.debug(`metrics/compute/${login}/base > graphql query > account ${account} > success`)

source/plugins/base/queries/repositories.graphql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
query BaseRepositories {
22
$account(login: "$login") {
3-
repositories($after first: $repositories $forks $affiliations, orderBy: {field: UPDATED_AT, direction: DESC}) {
3+
$type($after first: $repositories $forks $affiliations $constraints, orderBy: {field: UPDATED_AT, direction: DESC}) {
44
edges {
55
cursor
66
}

source/plugins/languages/analyzers.mjs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/**Indepth analyzer */
2-
export async function indepth({login, data, imports}, {skipped}) {
2+
export async function indepth({login, data, imports, repositories}, {skipped}) {
33
//Check prerequisites
44
if (!await imports.which("github-linguist"))
55
throw new Error("Feature requires github-linguist")
66

77
//Compute repositories stats from fetched repositories
8-
const results = {total:0, stats:{}}
9-
for (const repository of data.user.repositories.nodes) {
8+
const results = {total:0, lines:{}, stats:{}}
9+
for (const repository of repositories) {
1010
//Skip repository if asked
1111
if ((skipped.includes(repository.name.toLocaleLowerCase())) || (skipped.includes(`${repository.owner.login}/${repository.name}`.toLocaleLowerCase()))) {
1212
console.debug(`metrics/compute/${login}/plugins > languages > skipped repository ${repository.owner.login}/${repository.name}`)
@@ -52,7 +52,7 @@ export async function recent({login, data, imports, rest, account}, {skipped}) {
5252

5353
//Get user recent activity
5454
console.debug(`metrics/compute/${login}/plugins > languages > querying api`)
55-
const commits = [], days = 14, pages = 3, results = {total:0, stats:{}}
55+
const commits = [], days = 14, pages = 3, results = {total:0, lines:{}, stats:{}}
5656
try {
5757
for (let page = 1; page <= pages; page++) {
5858
console.debug(`metrics/compute/${login}/plugins > languages > loading page ${page}`)
@@ -110,8 +110,6 @@ export async function recent({login, data, imports, rest, account}, {skipped}) {
110110
console.debug(`metrics/compute/${login}/plugins > languages > cleaning temp dir ${path}`)
111111
await imports.fs.rmdir(path, {recursive:true})
112112
}
113-
114-
console.log(results)
115113
return results
116114
}
117115

@@ -121,8 +119,6 @@ async function analyze({login, imports}, {results, path}) {
121119
console.debug(`metrics/compute/${login}/plugins > languages > indepth > running linguist`)
122120
const files = Object.fromEntries(Object.entries(JSON.parse(await imports.run("github-linguist --json", {cwd:path}, {log:false}))).flatMap(([lang, files]) => files.map(file => [file, lang])))
123121

124-
console.log(files)
125-
126122
//Processing diff
127123
const per_page = 10
128124
console.debug(`metrics/compute/${login}/plugins > languages > indepth > checking git log`)
@@ -149,9 +145,10 @@ async function analyze({login, imports}, {results, path}) {
149145
if (!lang)
150146
continue
151147
//Added line marker
152-
if (/^[+]\s(?<line>[\s\S]+)$/.test(line)) {
153-
const size = Buffer.byteLength(line.match(/^[+]\s(?<line>[\s\S]+)$/)?.groups?.line ?? "", "utf-8")
148+
if (/^[+]\s*(?<line>[\s\S]+)$/.test(line)) {
149+
const size = Buffer.byteLength(line.match(/^[+]\s*(?<line>[\s\S]+)$/)?.groups?.line ?? "", "utf-8")
154150
results.stats[lang] = (results.stats[lang] ?? 0) + size
151+
results.lines[lang] = (results.lines[lang] ?? 0) + 1
155152
results.total += size
156153
}
157154
}

source/plugins/languages/index.mjs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ export default async function({login, data, imports, q, rest, account}, {enabled
3131
console.debug(`metrics/compute/${login}/plugins > languages > custom colors ${JSON.stringify(colors)}`)
3232

3333
//Unique languages
34-
const unique = new Set(data.user.repositories.nodes.flatMap(repository => repository.languages.edges.map(({node:{name}}) => name))).size
34+
const repositories = [...data.user.repositories.nodes, ...data.user.repositoriesContributedTo.nodes]
35+
const unique = new Set(repositories.flatMap(repository => repository.languages.edges.map(({node:{name}}) => name))).size
3536

3637
//Iterate through user's repositories and retrieve languages data
3738
console.debug(`metrics/compute/${login}/plugins > languages > processing ${data.user.repositories.nodes.length} repositories`)
@@ -59,17 +60,18 @@ export default async function({login, data, imports, q, rest, account}, {enabled
5960
//Indepth mode
6061
if (indepth) {
6162
console.debug(`metrics/compute/${login}/plugins > languages > switching to indepth mode (this may take some time)`)
62-
Object.assign(languages, await indepth_analyzer({login, data, imports}, {skipped}))
63+
Object.assign(languages, await indepth_analyzer({login, data, imports, repositories}, {skipped}))
6364
}
6465

6566
//Compute languages stats
66-
for (const {section, stats = {}, total = 0} of [{section:"favorites", stats:languages.stats, total:languages.total}, {section:"recent", ...languages["stats.recent"]}]) {
67+
for (const {section, stats = {}, lines = {}, total = 0} of [{section:"favorites", stats:languages.stats, lines:languages.lines, total:languages.total}, {section:"recent", ...languages["stats.recent"]}]) {
6768
console.debug(`metrics/compute/${login}/plugins > languages > computing stats ${section}`)
6869
languages[section] = Object.entries(stats).filter(([name]) => !ignored.includes(name.toLocaleLowerCase())).sort(([_an, a], [_bn, b]) => b - a).slice(0, limit).map(([name, value]) => ({name, value, size:value, color:languages.colors[name], x:0})).filter(({value}) => value / total > threshold)
6970
const visible = {total:Object.values(languages[section]).map(({size}) => size).reduce((a, b) => a + b, 0)}
7071
for (let i = 0; i < languages[section].length; i++) {
7172
languages[section][i].value /= visible.total
7273
languages[section][i].x = (languages[section][i - 1]?.x ?? 0) + (languages[section][i - 1]?.value ?? 0)
74+
languages[section][i].lines = lines[languages[section][i].name] ?? 0
7375
if ((colors[i]) && (!colors[languages[section][i].name.toLocaleLowerCase()]))
7476
languages[section][i].color = colors[i]
7577
}

source/plugins/languages/metadata.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ inputs:
7373
values:
7474
- bytes-size # Languages total size written in bytes
7575
- percentage # Languages proportions in %
76+
- lines # Estimation of lines of code (plugin_languages_indepth must be enabled)
7677
default: ""
7778
example: bytes-size, percentage
7879

source/templates/classic/partials/languages.ejs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
<%= plugins.languages.unique %> Language<%= s(plugins.languages.unique) %>
66
</h2>
77
</section>
8-
<% for (const section of plugins.languages.sections) { const languages = {"most-used":plugins.languages.favorites, "recently-used":plugins.languages.recent}[section] %>
8+
<% for (const section of (plugins.languages.sections ?? ["error"])) { const languages = {"most-used":plugins.languages.favorites, "recently-used":plugins.languages.recent}[section] %>
99
<section class="column">
1010
<h3 class="field">
11-
<%= {"most-used":"Most used languages", "recently-used":"Recently used languages"}[section] %>
11+
<%= {"most-used":"Most used languages", "recently-used":"Recently used languages", error:""}[section] %>
1212
</h3>
1313
<% if (plugins.languages.error) { %>
1414
<section>
@@ -31,13 +31,14 @@
3131
<div class="row fill-width">
3232
<% for (const row of rows) { %>
3333
<section>
34-
<% for (const {name, value, color, size} of languages.filter((_, i) => i%rows.length === row)) { %>
34+
<% for (const {name, value, lines, color, size} of languages.filter((_, i) => i%rows.length === row)) { %>
3535
<div class="field language details">
3636
<div class="field">
3737
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="<%= color ?? "#959DA5" %>" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"></path></svg>
3838
<%= name %>
3939
</div>
4040
<small>
41+
<% if (plugins.languages.details.includes("lines")) { %> <div><%= f(lines) %> line<%= s(lines) %></div><% } %>
4142
<% if (plugins.languages.details.includes("bytes-size")) { %> <div><%= f.bytes(size) %></div><% } %>
4243
<% if (plugins.languages.details.includes("percentage")) { %> <div><%= f.percentage(value) %></div><% } %>
4344
</small>

source/templates/classic/style.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@
186186
.field.language.details > *, .field.language.details small > * {
187187
flex: 1 1 0;
188188
}
189+
.field.language.details small > *:not(:last-child) {
190+
margin-right: 6px;
191+
}
189192

190193
/* Follow-up */
191194
.followup.legend {

source/templates/repository/partials/languages.ejs

Lines changed: 53 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,56 +5,59 @@
55
<%= plugins.languages.unique %> Language<%= s(plugins.languages.unique) %>
66
</h2>
77
</section>
8-
<section class="column">
9-
<h3 class="field">
10-
Most used languages
11-
</h3>
12-
<% if (plugins.languages.error) { %>
13-
<section>
14-
<div class="field error">
15-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.343 13.657A8 8 0 1113.657 2.343 8 8 0 012.343 13.657zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"></path></svg>
16-
<%= plugins.languages.error.message %>
17-
</div>
18-
</section>
19-
<% } else { const width = 460 * (1 + large) %>
20-
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="<%= width %>" height="8">
21-
<mask id="languages-bar">
22-
<rect x="0" y="0" width="<%= width %>" height="8" fill="white" rx="5"/>
23-
</mask>
24-
<rect mask="url(#languages-bar)" x="0" y="0" width="<%= plugins.languages.favorites.length ? 0 : width %>" height="8" fill="#d1d5da"/>
25-
<% for (const {name, value, color, x} of plugins.languages.favorites) { %>
26-
<rect mask="url(#languages-bar)" x="<%= x*width %>" y="0" width="<%= value*width %>" height="8" fill="<%= color %>"/>
27-
<% } %>
28-
</svg>
29-
<% if (plugins.languages.details?.length) { const rows = large ? [0, 1, 2, 3] : [0, 1] %>
30-
<div class="row fill-width">
31-
<% for (const row of rows) { %>
32-
<section>
33-
<% for (const {name, value, color, size} of plugins.languages.favorites.filter((_, i) => i%rows.length === row)) { %>
34-
<div class="field language details">
35-
<div class="field">
36-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="<%= color %>" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"></path></svg>
37-
<%= name %>
38-
</div>
39-
<small>
40-
<% if (plugins.languages.details.includes("bytes-size")) { %> <div><%= f.bytes(size) %></div><% } %>
41-
<% if (plugins.languages.details.includes("percentage")) { %> <div><%= f.percentage(value) %></div><% } %>
42-
</small>
43-
</div>
44-
<% } %>
45-
</section>
46-
<% } %>
47-
</div>
48-
<% } else { %>
49-
<div class="field center horizontal-wrap fill-width">
50-
<% for (const {name, value, color} of plugins.languages.favorites) { %>
51-
<div class="field center no-wrap language">
52-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="<%= color %>" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"></path></svg>
53-
<%= name %>
54-
</div>
8+
<% for (const section of (plugins.languages.sections ?? ["error"])) { const languages = {"most-used":plugins.languages.favorites, "recently-used":plugins.languages.recent}[section] %>
9+
<section class="column">
10+
<h3 class="field">
11+
<%= {"most-used":"Most used languages", "recently-used":"Recently used languages", error:""}[section] %>
12+
</h3>
13+
<% if (plugins.languages.error) { %>
14+
<section>
15+
<div class="field error">
16+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.343 13.657A8 8 0 1113.657 2.343 8 8 0 012.343 13.657zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"></path></svg>
17+
<%= plugins.languages.error.message %>
18+
</div>
19+
</section>
20+
<% } else { const width = 460 * (1 + large) %>
21+
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="<%= width %>" height="8">
22+
<mask id="languages-bar">
23+
<rect x="0" y="0" width="<%= width %>" height="8" fill="white" rx="5"/>
24+
</mask>
25+
<rect mask="url(#languages-bar)" x="0" y="0" width="<%= languages.length ? 0 : width %>" height="8" fill="#d1d5da"/>
26+
<% for (const {name, value, color, x} of languages) { %>
27+
<rect mask="url(#languages-bar)" x="<%= x*width %>" y="0" width="<%= value*width %>" height="8" fill="<%= color ?? "#959DA5" %>"/>
5528
<% } %>
56-
</div>
29+
</svg>
30+
<% if (plugins.languages.details?.length) { const rows = large ? [0, 1, 2, 3] : [0, 1] %>
31+
<div class="row fill-width">
32+
<% for (const row of rows) { %>
33+
<section>
34+
<% for (const {name, value, lines, color, size} of languages.filter((_, i) => i%rows.length === row)) { %>
35+
<div class="field language details">
36+
<div class="field">
37+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="<%= color ?? "#959DA5" %>" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"></path></svg>
38+
<%= name %>
39+
</div>
40+
<small>
41+
<% if (plugins.languages.details.includes("lines")) { %> <div><%= f(lines) %> line<%= s(lines) %></div><% } %>
42+
<% if (plugins.languages.details.includes("bytes-size")) { %> <div><%= f.bytes(size) %></div><% } %>
43+
<% if (plugins.languages.details.includes("percentage")) { %> <div><%= f.percentage(value) %></div><% } %>
44+
</small>
45+
</div>
46+
<% } %>
47+
</section>
48+
<% } %>
49+
</div>
50+
<% } else { %>
51+
<div class="field center horizontal-wrap fill-width">
52+
<% for (const {name, value, color} of languages) { %>
53+
<div class="field center no-wrap language">
54+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="<%= color %>" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"></path></svg>
55+
<%= name %>
56+
</div>
57+
<% } %>
58+
</div>
59+
<% } %>
5760
<% } %>
58-
<% } %>
59-
</section>
61+
</section>
62+
<% } %>
6063
<% } %>

0 commit comments

Comments
 (0)