summaryrefslogtreecommitdiff
path: root/lang.c
diff options
context:
space:
mode:
Diffstat (limited to 'lang.c')
-rw-r--r--lang.c169
1 files changed, 169 insertions, 0 deletions
diff --git a/lang.c b/lang.c
new file mode 100644
index 0000000..c828be2
--- /dev/null
+++ b/lang.c
@@ -0,0 +1,169 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
+#include "cgit.h"
+#include "html.h"
+#include "ui-shared.h"
+#include "ui-tree.h"
+#include <string.h>
+#include <sys/stat.h>
+
+/* Mapping of file extensions to languages */
+struct lang_extension {
+ const char *ext;
+ const char *lang;
+};
+
+static const struct lang_extension lang_map[] = {{"c", "C"},
+ {"h", "C"},
+ {"js", "JavaScript"},
+ {"jsx", "JavaScript"},
+ {"py", "Python"},
+ {"go", "Go"},
+ {"rs", "Rust"},
+ {"rb", "Ruby"},
+ {"java", "Java"},
+ {"ts", "TypeScript"},
+ {"tsx", "TypeScript"},
+ {"cpp", "C++"},
+ {"cc", "C++"},
+ {"cxx", "C++"},
+ {"cs", "C#"},
+ {"php", "PHP"},
+ {"sh", "Shell"},
+ {"bash", "Shell"},
+ {"lua", "Lua"},
+ {"vim", "VimL"},
+ {"clj", "Clojure"},
+ {"scala", "Scala"},
+ {"kt", "Kotlin"},
+ {"swift", "Swift"},
+ {"m", "Objective-C"},
+ {"pl", "Perl"},
+ {"r", "R"},
+ {"sql", "SQL"},
+ {"html", "HTML"},
+ {"htm", "HTML"},
+ {"css", "CSS"},
+ {"scss", "SCSS"},
+ {"json", "JSON"},
+ {"yaml", "YAML"},
+ {"yml", "YAML"},
+ {"md", "Markdown"},
+ {"rst", "ReStructuredText"},
+ {NULL, NULL}};
+
+struct lang_count {
+ const char *lang;
+ int count;
+};
+
+struct lang_detect_context {
+ struct lang_count counts[100];
+ int count_nr;
+ int total_files;
+ int max_count;
+ const char *primary_lang;
+};
+
+static const char *get_lang_from_ext(const char *filename) {
+ const char *ext = strrchr(filename, '.');
+ if (!ext || ext == filename)
+ return NULL;
+
+ ext++; /* skip the dot */
+
+ for (int i = 0; lang_map[i].ext; i++) {
+ if (!strcasecmp(ext, lang_map[i].ext))
+ return lang_map[i].lang;
+ }
+ return NULL;
+}
+
+static void count_language(struct lang_detect_context *ctx, const char *lang) {
+ int i, found = 0;
+
+ if (!lang)
+ return;
+
+ ctx->total_files++;
+
+ /* Find or add language in counts */
+ for (i = 0; i < ctx->count_nr; i++) {
+ if (!strcmp(ctx->counts[i].lang, lang)) {
+ ctx->counts[i].count++;
+ if (ctx->counts[i].count > ctx->max_count) {
+ ctx->max_count = ctx->counts[i].count;
+ ctx->primary_lang = ctx->counts[i].lang;
+ }
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found && ctx->count_nr < 100) {
+ ctx->counts[ctx->count_nr].lang = lang;
+ ctx->counts[ctx->count_nr].count = 1;
+ if (1 > ctx->max_count) {
+ ctx->max_count = 1;
+ ctx->primary_lang = lang;
+ }
+ ctx->count_nr++;
+ }
+}
+
+static int count_files_cb(const struct object_id *oid, struct strbuf *base,
+ const char *pathname, unsigned mode, void *cbdata) {
+ struct lang_detect_context *ctx = cbdata;
+ const char *lang;
+
+ /* Skip directories */
+ if (S_ISDIR(mode))
+ return READ_TREE_RECURSIVE;
+
+ /* Skip hidden files */
+ if (pathname[0] == '.')
+ return 0;
+
+ lang = get_lang_from_ext(pathname);
+ count_language(ctx, lang);
+
+ return 0;
+}
+
+void detect_repo_languages(struct cgit_repo *repo) {
+ struct object_id oid;
+ struct commit *commit;
+ struct tree *tree;
+ struct pathspec paths = {.nr = 0};
+ struct lang_detect_context ctx;
+
+ if (!repo->defbranch)
+ return;
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ /* Get the commit for the default branch */
+ if (repo_get_oid(the_repository, repo->defbranch, &oid))
+ return;
+
+ commit = lookup_commit_reference(the_repository, &oid);
+ if (!commit)
+ return;
+
+ if (repo_parse_commit(the_repository, commit))
+ return;
+
+ tree = repo_get_commit_tree(the_repository, commit);
+ if (!tree)
+ return;
+
+ /* Walk the tree and count file extensions */
+ read_tree(the_repository, tree, &paths, count_files_cb, &ctx);
+
+ /* Store results in repo */
+ if (ctx.primary_lang) {
+ repo->primary_lang = xstrdup(ctx.primary_lang);
+ repo->lang_confidence =
+ ctx.total_files > 0 ? (100 * ctx.max_count) / ctx.total_files : 0;
+ }
+}