Make language bar chart determined by line count

927e7a1d868b65897ab546e3ac84fefbba4d4b64

Tucker McKnight <tmcknight@instructure.com> | Sun Jul 12 2026

Make language bar chart determined by line count

Previously, was based on the number of files for each language. Is
now based on the number of lines in those files.

Also, hides the "other" bar if there is nothing in the "other" category.
Will also show fewer bars if there are less than 5 languages. (Previously,
was hard-coded to 5.)
js_templates/repo.ts:22
Before
21
22
23


24

25
26
27
28
29
30
31
32

33

34
35
36
37

38
39
    currentRefType: data.flatRef.type,
  })

⁣
⁣
  const languageCounts = Array.from(ref.fileList.keys()).reduce((counts, currentFile) => {
⁣
    const fileParts = currentFile.split(".")
    const fileExtension = fileParts[fileParts.length - 1]
// todo: add more ignoreable extensions or specific files
// (like package-lock.json). Allow glob patterns?
    if (fileExtension === 'gitignore') {
      return counts
    }

⁣
    counts.set(fileExtension, (counts.get(fileExtension) + 1) || 1)
⁣
    return counts
  }, new Map<string, number>())

  // todo: this is probably broken for repos that use fewer than 6 languages
⁣
  let languagePercentages: Array<[string, number]> = []
  const total = Array.from(ref.fileList.keys()).length
After
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
    currentRefType: data.flatRef.type,
  })

  const languageCounts = new Map<string, number>()

  const countPromises = Array.from(ref.fileList.keys()).map(async (currentFile) => {
    return new Promise<void>(async (resolve) => {
      const fileParts = currentFile.split(".")
      const fileExtension = fileParts[fileParts.length - 1]
// todo: add more ignoreable extensions or specific files
// (like package-lock.json). Allow glob patterns?
      if (fileExtension === 'gitignore') {
        resolve()
      }

      const fileLineCount = (await repo.files(currentFile, data.currentRef.sha)).contents.split('\n').length
      languageCounts.set(fileExtension, (languageCounts.get(fileExtension) + fileLineCount) || fileLineCount)
      resolve()
    })
  })

  await Promise.all(countPromises)

  let languagePercentages: Array<[string, number]> = []
  const total = Array.from(ref.fileList.keys()).length
js_templates/repo.ts:45
Before
44
45
46



47
48
49
50
  languagePercentages.sort((a, b) => {
    return b[1] - a[1]
  })
⁣
⁣
⁣
  const topLanguagePercentages = languagePercentages.slice(0, 5)
  const otherLanguagePercent = languagePercentages.slice(6, languagePercentages.length - 6).reduce((sum, current) => {
    return sum + current[1]
  }, 0)
After
44
45
46
47
48
49
50
51
52
53
  languagePercentages.sort((a, b) => {
    return b[1] - a[1]
  })

  // Show graph for the top 5 languages, unless there are fewer than 5 total
  const numOfTopLanguages = Math.min(5, languagePercentages.length)
  const topLanguagePercentages = languagePercentages.slice(0, numOfTopLanguages)
  const otherLanguagePercent = languagePercentages.slice(numOfTopLanguages).reduce((sum, current) => {
    return sum + current[1]
  }, 0)
js_templates/repo.ts:75
Before
74
75
76


77
78
79


80
81
82
                      m('div', {class: "language-name text-light font-monospace"}, percentTuple[0]),
                      m('div', {class: "language-percent", style: `flex-grow: ${percentTuple[1] / largestPercent};`})
                    ])
⁣
⁣
                  }).concat([m('div', {class: 'language-col flex-grow-1 overflow-hidden'},
                    m('div', {class: "language-name text-light font-monospace"}, 'other'),
                      m('div', {class: "language-percent", style: `flex-grow: ${otherLanguagePercent / largestPercent};`})
⁣
⁣
                  )])),
                ])
              ]),
              m('div', {class: "row align-items-center"}, [
After
74
75
76
77
78
79
80
81
82
83
84
85
86
                      m('div', {class: "language-name text-light font-monospace"}, percentTuple[0]),
                      m('div', {class: "language-percent", style: `flex-grow: ${percentTuple[1] / largestPercent};`})
                    ])
                  }).concat(
                    otherLanguagePercent > 0
                    ? [m('div', {class: 'language-col flex-grow-1 overflow-hidden'},
                        m('div', {class: "language-name text-light font-monospace"}, 'other'),
                        m('div', {class: "language-percent", style: `flex-grow: ${otherLanguagePercent / largestPercent};`})
                      )]
                    : null
                  )),
                ])
              ]),
              m('div', {class: "row align-items-center"}, [
src/configTypes.ts:48
Before
47
48
49
50
51
52
53

export type GitConfig = {
  /* The absolute path to the repository.
  * @example location: "/home/alice/projects/git_repo"
  */
  location: string,
  description?: string,
  defaultBranch: string,
After
47
48
49
50
51
52
53

export type GitConfig = {
  /* The absolute path to the repository.
   * @example location: "/home/alice/projects/git_repo"
   */
  location: string,
  description?: string,
  defaultBranch: string,