From 2151df75cb8646381d3c4bf95379c8f568dcd9eb Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Fri, 19 Feb 2021 12:58:37 -0500 Subject: tags starting to work --- tags.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 107 insertions(+), 37 deletions(-) (limited to 'tags.c') diff --git a/tags.c b/tags.c index b1fac23..5d3fca5 100644 --- a/tags.c +++ b/tags.c @@ -1,33 +1,57 @@ -void tags_free(TagsFile *f) { - free(f->file_data); - arr_free(f->tags); - memset(f, 0, sizeof *f); +static int tag_try(FILE *fp, char const *tag) { + if (ftell(fp) != 0) { + while (1) { + int c = getc(fp); + if (c == EOF || c == '\n') + break; + } + } + + size_t tag_len = strlen(tag); + if (!feof(fp)) { + char line[256]; + long pos = ftell(fp); + if (fgets(line, sizeof line, fp)) { + fseek(fp, pos, SEEK_SET); + size_t len = strspn(line, "\t"); + if (tag_len > len) + len = tag_len; + return strncmp(tag, line, len); + } + } + return -1; } -void tags_read(Ted *ted, TagsFile *f) { +bool tag_goto(Ted *ted, char const *tag) { change_directory(ted->cwd); Settings const *settings = &ted->settings; char const *tags_filename = settings->tags_filename; FILE *file = fopen(tags_filename, "rb"); - tags_free(f); - if (file) { - fseek(file, 0, SEEK_END); - size_t file_size = (size_t)ftell(file); - fseek(file, 0, SEEK_SET); - char *file_data = ted_calloc(ted, file_size + 1, 1); - if (file_data) { - f->file_data = file_data; - fread(file_data, 1, file_size, file); - char *p = file_data; - while (1) { - // each line in the file is of the format: + fseek(file, 0, SEEK_END); + size_t file_size = (size_t)ftell(file); + + size_t lo = 0; + size_t hi = file_size; + bool success = false; + while (lo < hi) { + size_t mid = (lo + hi)/2; + fseek(file, (long)mid, SEEK_SET); + int cmp = tag_try(file, tag); + if (cmp > 0) { + lo = mid+1; + } else if (cmp < 0) { + hi = mid; + } else { + // we found it! + char tag_enty[1024]; + if (fgets(tag_enty, sizeof tag_enty, file)) { + + // the tag is of the format: // tag name\tfile name\taddress // or // tag name\tfile name\taddress;" additional information - char *end = p + strcspn(p, "\n"); - bool eof = *end == '\0'; - char *name = p; + char *name = tag_enty; char *name_end = strchr(name, '\t'); if (name_end) { *name_end = '\0'; @@ -61,26 +85,72 @@ void tags_read(Ted *ted, TagsFile *f) { if (address_end - address > 2 && address_end[-2] == ';' && address_end[-1] == '"') { address_end[-2] = '\0'; } - Tag tag = {.name = name, .file = filename, .address = address}; - arr_add(f->tags, tag); + assert(streq(name, tag)); + char path[TED_PATH_MAX]; + ted_full_path(ted, filename, path, sizeof path); + if (ted_open_file(ted, path)) { + TextBuffer *buffer = ted->active_buffer; + int line_number = atoi(address); + if (line_number > 0) { + // the tags file gives us a line number + BufferPos pos = {.line = (u32)line_number, .index = 0}; + buffer_cursor_move_to_pos(buffer, pos); + success = true; + } else if (address[0] == '/') { + // the tags file gives us a pattern to look for + char *pattern = address + 1; + // the patterns seem to be always literal (not regex-y), except for ^ and $ + bool start_anchored = false, end_anchored = false; + if (*pattern == '^') { + start_anchored = true; + ++pattern; + } + char *dollar = strchr(pattern, '$'); + if (dollar) { + end_anchored = true; + *dollar = '\0'; + } + + String32 pattern32 = str32_from_utf8(pattern); + u32 options = PCRE2_LITERAL; + if (start_anchored) options |= PCRE2_ANCHORED; + if (end_anchored) options |= PCRE2_ENDANCHORED; + int error_code; + PCRE2_SIZE error_offset; + pcre2_code *code = pcre2_compile(pattern32.str, pattern32.len, + options, &error_code, &error_offset, NULL); + if (code) { + pcre2_match_data *match_data = pcre2_match_data_create(10, NULL); + if (match_data) { + for (u32 line_idx = 0; line_idx < buffer->nlines; ++line_idx) { + Line *line = &buffer->lines[line_idx]; + int n = pcre2_match(code, line->str, line->len, 0, PCRE2_NOTEMPTY, + match_data, NULL); + if (n == 1) { + // found it! + PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data); + PCRE2_SIZE index = ovector[0]; + BufferPos pos = {line_idx, (u32)index}; + buffer_cursor_move_to_pos(buffer, pos); + buffer->center_cursor_next_frame = true; + break; + } + } + pcre2_match_data_free(match_data); + } + pcre2_code_free(code); + } + str32_free(&pattern32); + } else { + ted_seterr(ted, "Unrecognized tag address: %s", address); + } + break; + } } } - if (eof) break; // end of file - p = end + 1; } } - f->last_modified = time_last_modified(tags_filename); - fclose(file); - } else { - ted_seterr(ted, "tags file does not exist."); } + fclose(file); + return success; } - -void tags_read_if_changed(Ted *ted, TagsFile *f) { - Settings const *settings = &ted->settings; - char const *tags_filename = settings->tags_filename; - struct timespec modify_time = time_last_modified(tags_filename); - if (!timespec_eq(modify_time, f->last_modified)) { - tags_read(ted, f); - } -} \ No newline at end of file -- cgit v1.2.3