From d9257624c14c2783bfb980afbcf4ea8b59e66fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Diego=20Aur=C3=A9lio=20Mesquita?= Date: Sun, 20 May 2018 18:43:17 -0300 Subject: [PATCH] Initial implementation of external completer. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marco Diego Aurélio Mesquita --- src/nano.h | 2 + src/rcfile.c | 3 + src/text.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 222 insertions(+), 1 deletion(-) diff --git a/src/nano.h b/src/nano.h index 15eadd5d..a51eb9e7 100644 --- a/src/nano.h +++ b/src/nano.h @@ -229,6 +229,8 @@ typedef struct syntaxtype { /* The list of libmagic results that this syntax applies to. */ char *linter; /* The command with which to lint this type of file. */ + char *completer; + /* The completer command to this type of file. */ char *formatter; /* The formatting command (for programming languages mainly). */ #ifdef ENABLE_COMMENT diff --git a/src/rcfile.c b/src/rcfile.c index 2e7c2ab6..6f092474 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -301,6 +301,7 @@ void parse_syntax(char *ptr) live_syntax->headers = NULL; live_syntax->magics = NULL; live_syntax->linter = NULL; + live_syntax->completer = NULL; live_syntax->formatter = NULL; #ifdef ENABLE_COMMENT live_syntax->comment = mallocstrcpy(NULL, GENERAL_COMMENT_CHARACTER); @@ -997,6 +998,8 @@ void parse_rcfile(FILE *rcstream, bool syntax_only) parse_colors(ptr, NANO_REG_EXTENDED | REG_ICASE); else if (strcasecmp(keyword, "linter") == 0) pick_up_name("linter", ptr, &live_syntax->linter); + else if (strcasecmp(keyword, "completer") == 0) + pick_up_name("completer", ptr, &live_syntax->completer); else if (strcasecmp(keyword, "formatter") == 0) #ifdef ENABLE_SPELLER pick_up_name("formatter", ptr, &live_syntax->formatter); diff --git a/src/text.c b/src/text.c index 12203b4a..5a6434ee 100644 --- a/src/text.c +++ b/src/text.c @@ -3540,6 +3540,210 @@ char *copy_completion(char *check_line, int start) return word; } +/* Gets a competion dictionary for shard from external command */ +filestruct *get_dict(char *shard) +{ + //fprintf(stderr, "Will generate dicionary for %s\n", shard); + /*static filestruct dict1, dict2; + dict1.data = "Brasilia"; + dict1.lineno = 1; + dict1.multidata = NULL; + dict1.prev = NULL; + dict1.next = &dict2; + dict2.data = "Brasil"; + dict2.lineno = 2; + dict2.multidata = NULL; + dict2.prev = &dict1; + dict2.next = NULL; + return &dict1;*/ + + char *read_buff, *read_buff_ptr, *read_buff_word; + size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread; + size_t parsesuccess = 0; + int lint_status, lint_fd[2]; + pid_t pid_lint; + static char **lintargs = NULL; + //char *lintargs[3]; + //lintstruct *lints = NULL, *tmplint = NULL, *curlint = NULL; + filestruct *lints = NULL, *tmplint = NULL, *curlint = NULL; + + /* Create a pipe up front. */ + if (pipe(lint_fd) == -1) { + statusbar(_("Could not create pipe")); + return NULL; + } + + statusbar(_("Invoking dictionary, please wait")); + + //construct_argument_list(&lintargs, openfile->syntax->linter, openfile->filename); + construct_argument_list(&lintargs, openfile->syntax->completer, NULL); + fprintf(stderr, "current completer is: %s\n", openfile->syntax->completer); + char *current_arg; + int i = 0; + for(current_arg = lintargs[i]; current_arg != NULL; i++) { + fprintf(stderr, "Arg %d: %s\n", i, lintargs[i]); + current_arg = lintargs[i]; + if(current_arg == NULL) + break; + if(strcmp(current_arg, "$shard") == 0) { + lintargs[i] = strdup(shard); + } + } + /*lintargs[0] = "grep"; + lintargs[1] = shard; + //lintargs[2] = "/usr/share/dict/words"; + lintargs[2] = "tags";*/ + + /* Start a new process to run the linter in. */ + if ((pid_lint = fork()) == 0) { + + /* Child continues here (i.e. the future linting process). */ + close(lint_fd[0]); + + /* Send the linter's standard output + err to the pipe. */ + if (dup2(lint_fd[1], STDOUT_FILENO) != STDOUT_FILENO) + exit(9); + if (dup2(lint_fd[1], STDERR_FILENO) != STDERR_FILENO) + exit(9); + + close(lint_fd[1]); + + /* Start the linter program; we are using $PATH. */ + execvp(lintargs[0], lintargs); + + /* This is only reached when the linter is not found. */ + exit(9); + } + + /* Parent continues here. */ + close(lint_fd[1]); + + /* If the child process was not forked successfully... */ + if (pid_lint < 0) { + close(lint_fd[0]); + statusbar(_("Could not fork")); + return NULL; + } + + /* Get the system pipe buffer size. */ + if ((pipe_buff_size = fpathconf(lint_fd[0], _PC_PIPE_BUF)) < 1) { + close(lint_fd[0]); + statusbar(_("Could not get size of pipe buffer")); + return NULL; + } + + /* Read in the returned syntax errors. */ + read_buff_read = 0; + read_buff_size = pipe_buff_size + 1; + read_buff = read_buff_ptr = charalloc(read_buff_size); + + while ((bytesread = read(lint_fd[0], read_buff_ptr, pipe_buff_size)) > 0) { + read_buff_read += bytesread; + read_buff_size += pipe_buff_size; + read_buff = read_buff_ptr = charealloc(read_buff, read_buff_size); + read_buff_ptr += read_buff_read; + } + + *read_buff_ptr = '\0'; + close(lint_fd[0]); + + /* Process the linter output. */ + read_buff_word = read_buff_ptr = read_buff; + + while (*read_buff_ptr != '\0') { + if ((*read_buff_ptr == '\r') || (*read_buff_ptr == '\n')) { + *read_buff_ptr = '\0'; + if (read_buff_word != read_buff_ptr) { + char *filename = NULL, *linestr = NULL, *maybecol = NULL; + char *message = mallocstrcpy(NULL, read_buff_word); + fprintf(stderr, "Got dict line: %s\n", message); + + parsesuccess++; + tmplint = curlint; + curlint = nmalloc(sizeof(filestruct)); + curlint->next = NULL; + curlint->prev = tmplint; + if (curlint->prev != NULL) + curlint->prev->next = curlint; + curlint->data = mallocstrcpy(NULL, message); + curlint->lineno = parsesuccess; + curlint->multidata = NULL; + + if (lints == NULL) + lints = curlint; + /* At the moment we handle the following formats: + * + * filenameorcategory:line:column:message (e.g. splint) + * filenameorcategory:line,column:message (e.g. pylint) + * filenameorcategory:line:message (e.g. pyflakes) */ + /*if (strstr(message, ": ") != NULL) { + filename = strtok(read_buff_word, ":"); + if ((linestr = strtok(NULL, ":")) != NULL) { + if ((maybecol = strtok(NULL, ":")) != NULL) { + ssize_t tmplineno = 0, tmpcolno = 0; + char *tmplinecol; + + tmplineno = strtol(linestr, NULL, 10); + if (tmplineno <= 0) { + read_buff_ptr++; + free(message); + continue; + } + + tmpcolno = strtol(maybecol, NULL, 10); + //* Check if the middle field is in comma format. * + if (tmpcolno <= 0) { + strtok(linestr, ","); + if ((tmplinecol = strtok(NULL, ",")) != NULL) + tmpcolno = strtol(tmplinecol, NULL, 10); + else + tmpcolno = 1; + } + + //* Nice. We have a lint message we can use. * + parsesuccess++; + tmplint = curlint; + curlint = nmalloc(sizeof(lintstruct)); + curlint->next = NULL; + curlint->prev = tmplint; + if (curlint->prev != NULL) + curlint->prev->next = curlint; + curlint->msg = mallocstrcpy(NULL, message); + curlint->lineno = tmplineno; + curlint->colno = tmpcolno; + curlint->filename = mallocstrcpy(NULL, filename); + + if (lints == NULL) + lints = curlint; + } + } + } else + free(message);*/ + } + read_buff_word = read_buff_ptr + 1; + } + read_buff_ptr++; + } + + /* Process the end of the linting process. */ + waitpid(pid_lint, &lint_status, 0); + + if (!WIFEXITED(lint_status) || WEXITSTATUS(lint_status) > 2) { + statusbar(invocation_error(openfile->syntax->linter)); + return NULL; + } + + free(read_buff); + + if (parsesuccess == 0) { + statusline(HUSH, _("Got 0 parsable lines from command: %s"), + openfile->syntax->linter); + return NULL; + } + + return lints; +} + /* Look at the fragment the user has typed, then search the current buffer for * the first word that starts with this fragment, and tentatively complete the * fragment. If the user types 'Complete' again, search and paste the next @@ -3553,6 +3757,7 @@ void complete_a_word(void) #ifdef ENABLE_WRAPPING bool was_set_wrapping = !ISSET(NO_WRAP); #endif + //openfilestruct *dict = openfile; /* If this is a fresh completion attempt... */ if (pletion_line == NULL) { @@ -3568,6 +3773,8 @@ void complete_a_word(void) openfile->last_action = OTHER; /* Initialize the starting point for searching. */ + //pletion_line = dict->fileage; + //pletion_line = get_dict(shard); pletion_line = openfile->fileage; pletion_x = 0; @@ -3601,13 +3808,21 @@ void complete_a_word(void) shard[shard_length++] = openfile->current->data[start_of_shard++]; shard[shard_length] = '\0'; + if (pletion_line == openfile->fileage) { + //fprintf(stderr, "This is a entire new completion...\n"); + pletion_line = get_dict(shard); + pletion_line = pletion_line == NULL ? openfile->fileage : pletion_line; + } + /* Run through all of the lines in the buffer, looking for shard. */ while (pletion_line != NULL) { int threshold = strlen(pletion_line->data) - shard_length - 1; /* The point where we can stop searching for shard. */ - + //fprintf(stderr, "pletion_x: %d, thresholhd: %d\n", pletion_x, threshold); + //fprintf(stderr, "Will compare %s with %s\n", pletion_line->data, shard); /* Traverse the whole line, looking for shard. */ for (i = pletion_x; (int)i < threshold; i++) { + //fprintf(stderr, "Entered for loop\n"); /* If the first byte doesn't match, run on. */ if (pletion_line->data[i] != shard[0]) continue; @@ -3675,6 +3890,7 @@ void complete_a_word(void) return; } + //fprintf(stderr, "Address of the next line in dictionary: %p\n", pletion_line->next); pletion_line = pletion_line->next; pletion_x = 0; } -- 2.11.0