~sircmpwn/annotatec

6493f7c0c58738ebd28eb0cdd16110fb17c23584 — Drew DeVault 7 months ago d44b6a6
Emit JSON and look up git blobs
4 files changed, 202 insertions(+), 40 deletions(-)

M graph.c
M graph.h
M main.c
A tree.h
M graph.c => graph.c +45 -4
@@ 19,9 19,11 @@ static struct symbol_definition *get_or_cache_symbol(
	struct symbol_definition *symbol;
	/* Index symbol */
	unsigned int hash = djb2(name);
	size_t len = sizeof(graph->cache.buckets) / sizeof(graph->cache.buckets[0]);
	size_t len = (sizeof(graph->file_cache.buckets) /
			sizeof(graph->file_cache.buckets[0]));
	size_t index = hash % len;
	struct symtab_entry *previous = NULL, *bucket = graph->cache.buckets[index];
	struct scache_entry *previous = NULL, *bucket =
		graph->symbol_cache.buckets[index];
	while (bucket) {
		symbol = bucket->symbol;
		if (bucket->hash == hash && strcmp(symbol->name, name) == 0) {


@@ 78,11 80,11 @@ static struct symbol_definition *get_or_cache_symbol(
		graph->orphans = symbol;
	}

	bucket = calloc(1, sizeof(struct symtab_entry));
	bucket = calloc(1, sizeof(struct scache_entry));
	bucket->hash = hash;
	bucket->symbol = symbol;
	if (previous == NULL) {
		graph->cache.buckets[index] = bucket;
		graph->symbol_cache.buckets[index] = bucket;
	} else {
		previous->next = bucket;
	}


@@ 110,6 112,7 @@ struct symbol_reference *reference_symbol(
	reference->symbol = symbol;
	reference->snext = symbol->references;
	symbol->references = reference;
	++symbol->nrefs;

	reference->file = file;
	reference->fnext = file->references;


@@ 118,3 121,41 @@ struct symbol_reference *reference_symbol(
	++file->nrefs;
	return reference;
}

struct source_file *get_source_file(
		struct project_graph *graph, const char *path) {
	unsigned int hash = djb2(path);
	size_t len = (sizeof(graph->file_cache.buckets) /
			sizeof(graph->file_cache.buckets[0]));
	size_t index = hash % len;
	struct fcache_entry *bucket = graph->file_cache.buckets[index];
	while (bucket) {
		if (bucket->hash == hash && strcmp(path, bucket->file->path) == 0) {
			return bucket->file;
		}
		bucket = bucket->next;
	}
	return NULL;
}

void cache_source_file(struct project_graph *graph, struct source_file *file) {
	/* Note: doesn't address repeated filenames */
	unsigned int hash = djb2(file->path);
	size_t len = (sizeof(graph->file_cache.buckets) /
			sizeof(graph->file_cache.buckets[0]));
	size_t index = hash % len;
	struct fcache_entry *previous = NULL, *bucket =
		graph->file_cache.buckets[index];
	while (bucket) {
		previous = bucket;
		bucket = bucket->next;
	}
	bucket = calloc(1, sizeof(struct fcache_entry));
	bucket->hash = hash;
	bucket->file = file;
	if (previous == NULL) {
		graph->file_cache.buckets[index] = bucket;
	} else {
		previous->next = bucket;
	}
}

M graph.h => graph.h +25 -9
@@ 2,32 2,43 @@
#define _GRAPH_H
#include <stdbool.h>

struct symbol_definition;
struct symbol_reference;
struct source_file;

struct symbol_cache {
	struct symtab_entry *buckets[256];
	struct scache_entry *buckets[256];
};

struct symtab_entry {
	struct symtab_entry *next;
struct scache_entry {
	struct scache_entry *next;
	unsigned int hash;
	struct symbol_definition *symbol;
};

struct file_cache {
	struct fcache_entry *buckets[256];
};

struct fcache_entry {
	struct fcache_entry *next;
	unsigned int hash;
	struct source_file *file;
};

struct typedef_declaration {
	struct typedef_declaration *next;
	char *name;
	int lineno, colno;
};

struct symbol_reference;
struct source_file;

struct symbol_definition {
	struct symbol_definition *next, *prev;
	struct symbol_reference *references;
	struct source_file *file;
	char *name;
	int lineno, colno;
	int nreferences;
	int nrefs;
	bool is_static;
};



@@ 41,7 52,7 @@ struct symbol_reference {

struct source_file {
	struct source_file *next;
	char *path, *blob_sha;
	char *path, *oid;
	struct typedef_declaration *typedefs;
	struct symbol_definition *symbols;
	struct symbol_reference *references;


@@ 51,7 62,8 @@ struct source_file {
struct project_graph {
	struct source_file *files;
	struct symbol_definition *orphans;
	struct symbol_cache cache;
	struct symbol_cache symbol_cache;
	struct file_cache file_cache;
	int norphans;
};



@@ 65,4 77,8 @@ struct symbol_reference *reference_symbol(

extern struct project_graph graph;

struct source_file *get_source_file(
		struct project_graph *graph, const char *path);
void cache_source_file(struct project_graph *graph, struct source_file *file);

#endif

M main.c => main.c +118 -27
@@ 1,3 1,4 @@
#define _POSIX_C_SOURCE 200809L
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>


@@ 16,36 17,121 @@ static FILE *invoke_cpp(const char *cppcmd, const char *input) {
	return popen(cmd, "r");
}

static void emit_annotations(struct source_file *file) {
	printf("%s: %3d symbols %3d references %3d orphans\n",
			file->path, file->nsyms, file->nrefs, graph.norphans);
static void scan_git_tree(struct project_graph *graph) {
	FILE *git = popen("git ls-tree -zr HEAD", "r");
	char *line = NULL;
	size_t n = 0;
	/* TODO: Add incremental mode (filtered by modified in last commit) */
	while (getdelim(&line, &n, '\0', git) != -1) {
		const char *delims = " \t";
		/* const char *mode = */
		strtok(line, delims);
		/* const char *type = */
		strtok(NULL, delims);
		const char *oid = strtok(NULL, delims);
		const char *path = strtok(NULL, delims);
		struct source_file *file = get_source_file(graph, path);
		if (file != NULL) {
			file->oid = strdup(oid);
		}
	}
	free(line);
	pclose(git);
}

	struct symbol_reference *ref;
	struct symbol_definition *symbol;
static void print_jstring(const char *str) {
	while (*str) {
		if (*str < 0x20) {
			printf("\\u00%2x", *str);
			continue;
		}
		switch (*str) {
		case '\\':
			printf("\\\\");
			break;
		case '\n':
			printf("\\n");
		}
		printf("%c", *str);
		str++;
	}
}

static void emit_annotations(struct project_graph *graph, bool git) {
	struct source_file *file = graph->files;
	printf("{\n");
	while (file) {
		if (git && !file->oid) {
			fprintf(stderr, "Warning: no git tree entry for %s", file->path);
			return;
		}
		printf("\t\"");
		print_jstring(file->oid ? file->oid : file->path);
		printf("\": [\n");

	symbol = file->symbols;
	while (symbol) {
		printf("\tdefines: %s@%d:%d (static: %d)\n",
				symbol->name, symbol->lineno, symbol->colno, symbol->is_static);
		ref = symbol->references;
		struct symbol_reference *ref;
		struct symbol_definition *symbol;

		bool first = true;
		symbol = file->symbols;
		while (symbol) {
			if (symbol->references) {
				if (!first) {
					printf(",\n");
				} else {
					first = false;
				}
				printf("\t\t{\"type\":\"markdown\",\"lineno\":%d,"
						"\"title\": \"%d reference%s\",\n\t\t\"content\":\"",
						symbol->lineno, symbol->nrefs,
						symbol->nrefs > 1 ? "s" : "");
			}
			ref = symbol->references;
			while (ref) {
				// TODO: need to escape html as well
				printf("- [");
				print_jstring(ref->file->path);
				printf(":%d](", ref->lineno);
				print_jstring(ref->file->path);
				printf("#L%d)\\n", ref->lineno);
				ref = ref->snext;
			}
			if (symbol->references) {
				printf("\"}");
			}
			symbol = symbol->next;
		}

		ref = file->references;
		while (ref) {
			printf("\t\treference: %s:%d:%d\n",
					ref->file->path, ref->lineno, ref->colno);
			ref = ref->snext;
			symbol = ref->symbol;
			if (symbol->file) {
				if (!first) {
					printf(",\n");
				} else {
					first = false;
				}
				printf("\t\t{\"type\":\"link\","
						"\"lineno\": %d, \"colno\": %d, \"len\": %zd,"
						"\"to\": \"",
						ref->lineno, ref->colno, strlen(symbol->name));
				print_jstring(symbol->file->path);
				printf("#L%d", symbol->lineno);
				printf("\"}");
			}
			ref = ref->fnext;
		}
		symbol = symbol->next;
	}
		printf("\n");

	ref = file->references;
	while (ref) {
		symbol = ref->symbol;
		if (symbol->file) {
			printf("\treferences: %s@%d:%d via %s:%d:%d\n",
					symbol->name, ref->lineno, ref->colno,
					symbol->file->path, symbol->lineno, symbol->colno);
		if (file->next) {
			printf("\t],\n");
		} else {
			printf("\t]\n");
		}
		ref = ref->fnext;

		file = file->next;
	}
	printf("}");
}

extern int yydebug;


@@ 54,6 140,7 @@ extern bool preprocessed_linenos;
int main(int argc, char **argv) {
	char *cppcmd = "cpp -std=c11";

	bool git;
	int c;
	while ((c = getopt(argc, argv, "dDC:g")) != -1) {
		switch (c) {


@@ 66,6 153,9 @@ int main(int argc, char **argv) {
		case 'D':
			preprocessed_linenos = true;
			break;
		case 'g':
			git = true;
			break;
		}
	}



@@ 83,17 173,18 @@ int main(int argc, char **argv) {
		nsyms += file->nsyms, nrefs += file->nrefs;
		file->next = graph.files;
		graph.files = file;
		cache_source_file(&graph, file);
	}
	fprintf(stderr, "Scan complete: "
			"%d files, %d symbols, %d references, %d orphan refs\n",
			argc - optind, nsyms, nrefs, graph.norphans);

	struct source_file *file = graph.files;
	while (file) {
		emit_annotations(file);
		file = file->next;
	if (git) {
		scan_git_tree(&graph);
	}

	emit_annotations(&graph, git);

	// TODO: free state

	return 0;

A tree.h => tree.h +14 -0
@@ 0,0 1,14 @@
#ifndef _TREE_H
#define _TREE_H

struct git_tree_entry {
	struct git_tree_entry *next;
	unsigned int hash;
	char *path, *oid;
};

struct git_tree {
	struct git_tree_entry *buckets[512];
};

#endif