Hello everybody,
As you may remember I was working on implementing an on-the-fly code
checker for Vim (CodeCheck). I am happy to release the first version of
it.
Unfortunately this initial version is highly restricted at the
moment: it only works on Linux with Pthreads and supports C/C++
standalone file editing with gcc or g++ compilers.
I am attaching a patch for CodeCheck. After patching your source tree, you can
get
more information by ``:he codecheck''.
Any comment is appreciated. My next step will be to port this code to
Windows.
Cheers,
--
Birgi
--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---
Index: runtime/doc/index.txt
===================================================================
--- runtime/doc/index.txt (revision 1164)
+++ runtime/doc/index.txt (working copy)
@@ -1108,7 +1108,9 @@
|:catch| :cat[ch] part of a :try command
|:cbuffer| :cb[uffer] parse error messages and jump to first error
|:cc| :cc go to specific error
+|:ccadd| :ccadd add current buffer to CodeCheck watchlist
|:cclose| :ccl[ose] close quickfix window
+|:ccrem| :ccrem remove current buffer from CodeCheck watchlist
|:cd| :cd change directory
|:center| :ce[nter] format lines at the center
|:cexpr| :cex[pr] read errors from expr and jump to first
Index: runtime/doc/tags
===================================================================
--- runtime/doc/tags (revision 1164)
+++ runtime/doc/tags (working copy)
@@ -1898,8 +1898,10 @@
:cb quickfix.txt /*:cb*
:cbuffer quickfix.txt /*:cbuffer*
:cc quickfix.txt /*:cc*
+:ccadd code_check.txt /*:ccadd*
:ccl quickfix.txt /*:ccl*
:cclose quickfix.txt /*:cclose*
+:ccrem code_check.txt /*:ccrem*
:cd editing.txt /*:cd*
:cd- editing.txt /*:cd-*
:ce change.txt /*:ce*
@@ -3192,6 +3194,7 @@
C change.txt /*C*
C-editing tips.txt /*C-editing*
C-indenting indent.txt /*C-indenting*
+CodeCheck code_check.txt /*CodeCheck*
COMSPEC starting.txt /*COMSPEC*
CR-used-for-NL pattern.txt /*CR-used-for-NL*
CTRL-6 editing.txt /*CTRL-6*
@@ -4829,6 +4832,8 @@
cmdwin-char cmdline.txt /*cmdwin-char*
cobol.vim syntax.txt /*cobol.vim*
codeset mbyte.txt /*codeset*
+codecheck code_check.txt /*codecheck*
+code_check code_check.txt /*code_check*
coding-style develop.txt /*coding-style*
col() eval.txt /*col()*
coldfusion.vim syntax.txt /*coldfusion.vim*
Index: runtime/doc/code_check.txt
===================================================================
--- runtime/doc/code_check.txt (revision 0)
+++ runtime/doc/code_check.txt (revision 0)
@@ -0,0 +1,62 @@
+*code_check.txt* For Vim version 7.2. Last change: 2008 Aug 18
+
+
+ VIM REFERENCE MANUAL by Bram Moolenaar
+
+
+CodeCheck [version 0.1] *codecheck* *CodeCheck* *code_check*
+
+1. What is CodeCheck |intro|
+2. Using :ccadd |ccadd|
+3. Using :ccrem |ccrem|
+4. Progress |progress|
+
+==============================================================================
+1. What is CodeCheck *intro*
+
+*** CodeCheck is sponsored by Google under Google Summer of Code program,
+mentored by Bram Moolenaar and implemented by Birgi Tamersoy. ***
+
+CodeCheck is an on-the-fly syntax checking tool implemented for Vim. It mimics
+the similar feature in Eclipse IDE. Whenever the user delays for a specified
+amount of time, a background thread:
+ - creates a temporary copy of the current buffer,
+ - compiles this temporary copy,
+ - parses the compiler output, and
+ - highlights the related lines in the buffer.
+
+As of today (last change date), CodeCheck is highly platform-dependent. It
+will work only on Linux with Pthreads. Current supported file types are; C/C++
+independent source files.
+
+==============================================================================
+2. Using :ccadd *ccadd*
+
+Usage:
+:ccadd <compile command> Adds current buffer to the CodeCheck
+ watchlist. <compile command> is required.
+ <compile command> must include the full path
+ of the corresponding buffer.
+
+Example:
+:ccadd gcc -Wall -c /home/birgi/tmp/a/b/main.c
+
+==============================================================================
+3. Using :ccrem *ccrem*
+
+Usage:
+:ccrem Removes current buffer from the CodeCheck
+ watchlist. This command does not take any
+ arguments. If the buffer is not being watched,
+ the command is simply ignored.
+
+==============================================================================
+4. Progress *progress*
+
+As of today (last change date) the maintainer works on the following issues:
+- Porting to Windows.
+- Specific parts highlighting rather than line highlighting.
+- Implementing a mechanism that would make new compiler/language support
+ easier.
+
+==============================================================================
Index: src/ex_docmd.c
===================================================================
--- src/ex_docmd.c (revision 1164)
+++ src/ex_docmd.c (working copy)
@@ -140,6 +140,7 @@
static char_u *repl_cmdline __ARGS((exarg_T *eap, char_u *src, int srclen, char_u *repl, char_u **cmdlinep));
static void ex_highlight __ARGS((exarg_T *eap));
static void ex_colorscheme __ARGS((exarg_T *eap));
+static void ex_codecheck __ARGS((exarg_T *eap));
static void ex_quit __ARGS((exarg_T *eap));
static void ex_cquit __ARGS((exarg_T *eap));
static void ex_quit_all __ARGS((exarg_T *eap));
@@ -6156,6 +6157,34 @@
}
static void
+ex_codecheck(eap)
+ exarg_T *eap;
+{
+ switch (eap->cmdidx) {
+ case CMD_ccadd:
+ if (!cc_get_is_started())
+ cc_init();
+ if (cc_is_buf_ok(curbuf)) {
+ if (*eap->arg != NUL)
+ cc_addbuf_setcmd(curbuf, eap->arg);
+ else
+ EMSG(_("You have to specify a valid compile command "
+ "in order to add this buffer to watchlist."));
+ } else
+ EMSG(_("The present version of CodeCheck does not support "
+ "this language."));
+ break;
+ case CMD_ccrem:
+ cc_rem_buf(curbuf);
+ if (cc_get_bufcount() == 0)
+ cc_exit();
+ break;
+ default:
+ return;
+ }
+}
+
+ static void
ex_highlight(eap)
exarg_T *eap;
{
Index: src/ex_cmds.h
===================================================================
--- src/ex_cmds.h (revision 1164)
+++ src/ex_cmds.h (working copy)
@@ -201,8 +201,12 @@
BANG|RANGE|NOTADR|WORD1|TRLBAR),
EX(CMD_cc, "cc", ex_cc,
RANGE|NOTADR|COUNT|TRLBAR|BANG),
+EX(CMD_ccadd, "ccadd", ex_codecheck,
+ EXTRA|NOTRLCOM|XFILE|MODIFY),
EX(CMD_cclose, "cclose", ex_cclose,
RANGE|NOTADR|COUNT|TRLBAR),
+EX(CMD_ccrem, "ccrem", ex_codecheck,
+ NOSPC|NOTRLCOM|MODIFY),
EX(CMD_cd, "cd", ex_cd,
BANG|FILE1|TRLBAR|CMDWIN),
EX(CMD_center, "center", ex_align,
Index: src/getchar.c
===================================================================
--- src/getchar.c (revision 1164)
+++ src/getchar.c (working copy)
@@ -1618,6 +1618,20 @@
c = K_IGNORE;
}
+ /* Handle redraw event here, this is used by CodeCheck.*/
+ if (c == K_REDRAW) {
+ /* gui_update_screen() does not work because of
+ * update_screen(0) inside it. */
+ update_topline();
+ validate_cursor();
+ update_screen(SOME_VALID);
+ setcursor();
+ cursor_on();
+ out_flush();
+
+ c = K_IGNORE;
+ }
+
/* Translate K_CSI to CSI. The special key is only used to avoid
* it being recognized as the start of a special key. */
if (c == K_CSI)
Index: src/code_check.c
===================================================================
--- src/code_check.c (revision 0)
+++ src/code_check.c (revision 0)
@@ -0,0 +1,1063 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ * CodeCheck extension by Birgi Tamersoy
+ * [EMAIL PROTECTED]
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * code_check.c: On-the-fly syntax checking tool.
+ */
+
+#include <pthread.h>
+#include <semaphore.h>
+#include "vim.h"
+
+#define CC_VERSION 0.1
+
+enum cc_ret_vals {
+ CC_FAIL = 0,
+ CC_SUCCESS,
+ CC_BUFEXISTS,
+ CC_NOSUCHBUF,
+};
+
+enum cc_ew_types {
+ CC_NOEW = 0,
+ CC_WARNING,
+ CC_ERROR
+};
+
+enum cc_search_mode {
+ CC_FOR_ADD = 0,
+ CC_FOR_REM,
+ CC_FOR_FIND
+};
+
+enum cc_print_direc {
+ CC_FWD = 0,
+ CC_REW
+};
+
+enum cc_copy_type {
+ CC_STANDALONE = 0,
+ CC_MASTER,
+ CC_PROJECT /* Makefile exists */
+};
+
+/*
+ * Multiple buffers can be syntax checked simultaneously and each buffer
+ * has its own list of errors and warnings. Consequently we have two list
+ * structures. One holds information about processed buffers and the other
+ * one holds information about buffer-specific errors & warnings.
+ *
+ * Both lists will be accessed frequently, so they will be sorted:
+ * buffer list will be sorted with respect to full file names, and
+ * error/warning list will be sorted with respect to line numbers.
+ */
+
+#define MAX_EW_TEXT 200
+
+/* compile command length */
+#define MAX_CMD_LENGTH 600
+
+#define MAX_PATH_LENGTH 300
+
+typedef struct cc_ewline_S cc_ewline_T;
+struct cc_ewline_S {
+ cc_ewline_T *prev;
+ cc_ewline_T *next;
+ int ew_type;
+ char_u ew_text[MAX_EW_TEXT];
+ linenr_T ew_lnum;
+};
+
+typedef struct cc_bufline_S cc_bufline_T;
+struct cc_bufline_S {
+ cc_bufline_T *prev;
+ cc_bufline_T *next;
+ char_u *buf_name;
+ cc_ewline_T *buf_ewlist_head;
+ pthread_mutex_t buf_mutex;
+ char_u buf_compile_cmd[MAX_CMD_LENGTH];
+};
+
+typedef struct cc_info_S cc_info_T;
+struct cc_info_S {
+ int cc_bufcount;
+ cc_bufline_T *cc_list_head;
+ cc_bufline_T *cc_list_curr;
+};
+
+/* global list of buffers. */
+static cc_info_T cc_list;
+#define MAX_BUFLINES 100
+static cc_bufline_T *cc_bufline_ptrs[MAX_BUFLINES];
+
+/* supported languages, the format should be ".<ext1>" ... */
+/* each extension should padded with ' ' characters, st. total is 5 chars */
+static char_u *cc_sup_exts = (char_u *) ".c "
+ ".cpp ";
+
+/* worker thread. */
+static pthread_t cc_slave;
+
+/* a queue for the pending jobs that the worker thread should handle.
+ * basically a list of buffer compile requests. */
+enum cc_pjob_types {
+ CC_COMPILE = 0
+};
+
+typedef struct cc_pjob_S cc_pjob_T;
+struct cc_pjob_S {
+ int cc_pjob_type;
+ buf_T *cc_pjob_buf;
+};
+
+#define MAX_PENDING_JOBS 10
+static cc_pjob_T *cc_pjobs[MAX_PENDING_JOBS];
+static int pindex = 0;
+static int cindex = 0;
+
+static sem_t full;
+static sem_t empty;
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* functions */
+static void cc_free_ewlist(cc_ewline_T *ewlist_head);
+static cc_bufline_T *cc_locate_buf(buf_T *buf);
+static cc_bufline_T *cc_locate_buf_bin(buf_T *buf, int *buf_idx,
+ int *put_before, int mode);
+static void cc_free_buflist(cc_info_T *cc_list_a,
+ cc_bufline_T *cc_bufline_ptrs_a[MAX_BUFLINES]);
+static int cc_start_slave_thread(void);
+static void *cc_slave_sroutine(void *args);
+static int cc_pjobs_produce(cc_pjob_T *tmp_pjob);
+static cc_pjob_T *cc_pjobs_consume(void);
+static int cc_pjobs_buf_exists(cc_pjob_T *tmp_pjob);
+static int cc_create_tmp_copy(buf_T *buf, char_u *tmp_copy_ffname,
+ int copy_type);
+static int cc_compile_tmp_copy(cc_bufline_T *bufline,
+ char_u *tmp_copy_ffname, int copy_type);
+static cc_ewline_T *cc_create_ewlist(char_u *tmp_out_ffname);
+void cc_sigalrm_handler(int signum);
+void cc_update_screen(void);
+static void cc_free(void);
+static cc_bufline_T* cc_add_buf(buf_T *buf);
+static int cc_set_tmp_copy_ffname(buf_T *buf, char_u *tmp_copy_ffname);
+
+static int cc_started = FALSE;
+static int old_p_ut = 0;
+
+/*
+ * TODO:
+ *
+ * 2. error/warning format should be added so that new languages are
+ * easily added to the code_check.c.
+ */
+
+/*
+ * Initializes the related structures.
+ */
+ int
+cc_init(void) {
+ int i;
+ int retval;
+
+ cc_list.cc_bufcount = 0;
+ cc_list.cc_list_head = NULL;
+ cc_list.cc_list_curr = NULL;
+
+ retval = sem_init(&full, 0, 0);
+ if (retval)
+ return CC_FAIL;
+
+ retval = sem_init(&empty, 0, MAX_PENDING_JOBS);
+ if (retval)
+ return CC_FAIL;
+
+ cc_start_slave_thread();
+
+ for (i = 0; i < MAX_BUFLINES; ++i)
+ cc_bufline_ptrs[i] = NULL;
+
+ for (i = 0; i < MAX_PENDING_JOBS; ++i)
+ cc_pjobs[i] = NULL;
+
+ old_p_ut = p_ut;
+ p_ut = 1000;
+
+ cc_started = TRUE;
+
+ return CC_SUCCESS;
+}
+
+/*
+ * Returns the current number of buffers in the watchlist.
+ */
+ int
+cc_get_bufcount(void) {
+ return cc_list.cc_bufcount;
+}
+
+/*
+ * Returns TRUE if CodeCheck is already started.
+ */
+ int
+cc_get_is_started(void) {
+ return cc_started;
+}
+
+/*
+ * CodeCheck creates a working thread which would run in the background.
+ * This thread is responsible of compiling the specified buffers, parsing
+ * the outputs and forming the corresponding error/warning lists.
+ */
+ static int
+cc_start_slave_thread(void) {
+ pthread_attr_t *attr = NULL;
+ int retval;
+
+ attr = (pthread_attr_t *) calloc(1, sizeof(pthread_attr_t));
+
+ retval = pthread_attr_init(attr);
+ if (retval)
+ goto sslave_fail;
+
+ retval = pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED);
+ if (retval)
+ goto sslave_fail;
+
+ retval = pthread_create(&cc_slave, attr, cc_slave_sroutine, NULL);
+ if (retval)
+ goto sslave_fail;
+
+ return CC_SUCCESS;
+
+sslave_fail:
+ free(attr);
+ return CC_FAIL;
+}
+
+/*
+ * Function updates the error/warning line numbers after new lines are inserted
+ * or some existing lines are removed. This is just to move the highlighted
+ * parts before an actual compile.
+ *
+ * TODO: This function is mainly copied from color_me.c (the preliminary
+ * project). So, double check if everything is OK or not!!!
+ */
+ int
+cc_update_ew_lnums(buf_T *buf, int lnum, int col, long xtra) {
+ if (!cc_started)
+ return CC_FAIL;
+
+ cc_bufline_T *tmp_bufline;
+ cc_ewline_T *tmp_ewline;
+ char_u *line = NULL;
+ int dummy;
+ int i;
+ int skip = FALSE;
+
+ tmp_bufline = cc_locate_buf_bin(buf, &dummy, &dummy, CC_FOR_FIND);
+ if (tmp_bufline == NULL)
+ return CC_FAIL;
+
+ pthread_mutex_lock(&(tmp_bufline->buf_mutex));
+
+ for (tmp_ewline = tmp_bufline->buf_ewlist_head;
+ tmp_ewline != NULL; tmp_ewline = tmp_ewline->next) {
+
+ skip = FALSE;
+ if (tmp_ewline->ew_lnum < lnum)
+ continue;
+ if (tmp_ewline->ew_lnum == lnum) {
+ /* is this the beginning of the line? */
+ line = ml_get_buf(buf, lnum, FALSE);
+ for (i = col - 1; i > 0 &&
+ (line[i] == '\t' || line[i] == ' '); --i)
+ ;
+ if (i > 0)
+ continue;
+ }
+ tmp_ewline->ew_lnum += xtra;
+ }
+
+ pthread_mutex_unlock(&(tmp_bufline->buf_mutex));
+ return CC_SUCCESS;
+}
+
+/*
+ * Worker thread function, which continuously checks for unserved requests.
+ */
+ static void *
+cc_slave_sroutine(void *args) {
+ cc_pjob_T *tmp_pjob;
+ cc_bufline_T *tmp_bufline;
+ int retval;
+ int dummy;
+ char_u tmp_copy_ffname[MAX_PATH_LENGTH];
+ char_u tmp_out_ffname[MAX_CMD_LENGTH];
+ cc_ewline_T *ew_head = NULL;
+
+ while (TRUE) {
+ /* consume a pending job */
+ tmp_pjob = cc_pjobs_consume();
+ if (tmp_pjob == NULL)
+ continue;
+
+ /* when gui mode is used, an other update screen is required. */
+ /* i don't know why :)!!!. */
+ if (gui.in_use)
+ cc_update_screen();
+
+ /* find the buffer line */
+
+ /* create the temporary copy */
+ retval = cc_create_tmp_copy(tmp_pjob->cc_pjob_buf,
+ tmp_copy_ffname, CC_STANDALONE);
+ if (retval == CC_FAIL)
+ continue;
+
+ tmp_bufline = cc_locate_buf_bin(tmp_pjob->cc_pjob_buf, &dummy,
+ &dummy, CC_FOR_FIND);
+ if (tmp_bufline == NULL)
+ continue;
+
+ /* compile the temporary copy */
+ retval = cc_compile_tmp_copy(tmp_bufline, tmp_copy_ffname,
+ CC_STANDALONE);
+ if (retval == CC_FAIL)
+ continue;
+
+ /* create the error list from the temporary copy */
+ sprintf((char *)tmp_out_ffname, "%s.out", (char *)tmp_copy_ffname);
+ ew_head = cc_create_ewlist(tmp_out_ffname);
+
+ /* having ew_head == NULL is not an error, since all
+ * the errors and the warnings may be cleaned. */
+
+ /* free the previous list & set the new list */
+ pthread_mutex_lock(&(tmp_bufline->buf_mutex));
+
+ cc_free_ewlist(tmp_bufline->buf_ewlist_head);
+ tmp_bufline->buf_ewlist_head = ew_head;
+
+ pthread_mutex_unlock(&(tmp_bufline->buf_mutex));
+
+ /* update the screen */
+ cc_update_screen();
+
+ /* that would be it for this buffer */
+ }
+}
+
+/*
+ * Function is used to update the screen properly.
+ */
+ void
+cc_update_screen(void) {
+ if (!gui.in_use) {
+ /* in console mode updating the screen from the worker thread
+ * does not cause any problems. */
+ update_topline();
+ validate_cursor();
+ update_screen(SOME_VALID);
+ setcursor();
+ cursor_on();
+ out_flush();
+ } else {
+ /* updating the screen in gui mode is troublesome. */
+ char_u bytes[3];
+
+ bytes[0] = CSI;
+ bytes[1] = KS_EXTRA;
+ bytes[2] = KE_REDRAW;
+
+ add_to_input_buf(bytes, 3);
+ }
+}
+
+/*
+ * Function returns the correct error/warning type for a specific lnum.
+ * Returns CC_NOEW if the lnum does not have an error or a warning.
+ */
+ int
+cc_get_ew_type(buf_T *buf, linenr_T lnum) {
+ if (!cc_started)
+ return CC_FAIL;
+
+ cc_bufline_T *tmp_bufline;
+ cc_ewline_T *tmp_ewline;
+ int dummy;
+ int ew_type = CC_NOEW;
+
+ tmp_bufline = cc_locate_buf_bin(buf, &dummy, &dummy, CC_FOR_FIND);
+ if (tmp_bufline == NULL)
+ return CC_NOEW;
+
+ pthread_mutex_lock(&(tmp_bufline->buf_mutex));
+ tmp_ewline = tmp_bufline->buf_ewlist_head;
+ while (tmp_ewline != NULL) {
+ if (tmp_ewline->ew_lnum != lnum) {
+ tmp_ewline = tmp_ewline->next;
+ continue;
+ }
+
+ if (ew_type < tmp_ewline->ew_type)
+ ew_type = tmp_ewline->ew_type;
+
+ tmp_ewline = tmp_ewline->next;
+ }
+ pthread_mutex_unlock(&(tmp_bufline->buf_mutex));
+ return ew_type;
+}
+
+/*
+ * Function creates a quickfix error list from the compiler output.
+ * TODO: Right now works only for .c files and the gcc compiler.
+ * Should be automated to work with multiple languages and multiple
+ * compilers.
+ */
+ static cc_ewline_T *
+cc_create_ewlist(char_u *tmp_out_ffname) {
+ FILE *err_file = NULL;
+ char_u *buf = NULL;
+ size_t len = 0;
+ size_t read;
+ cc_ewline_T *ew_head = NULL;
+ cc_ewline_T *ew_curr = NULL;
+ char_u *token1 = NULL;
+ char_u *token2 = NULL;
+ char_u *token3 = NULL;
+ char_u *token4 = NULL;
+ cc_ewline_T *tmp_ewline = NULL;
+
+
+ err_file = fopen((char *)tmp_out_ffname, "r");
+ if (err_file == NULL)
+ return NULL;
+
+ while ((read = getline((char **) &buf, &len, err_file)) != -1) {
+ token1 = (char_u *)strtok((char *) buf, ":");
+ token2 = (char_u *)strtok(NULL, ":");
+ token3 = (char_u *)strtok(NULL, ":");
+ token4 = (char_u *)strtok(NULL, ":");
+
+ /* TODO: be sure it is gXX type of output. */
+
+ if (!token4)
+ continue;
+
+ tmp_ewline = (cc_ewline_T *) calloc(1, sizeof(cc_ewline_T));
+ STRCPY(tmp_ewline->ew_text, token4);
+ /* remove the last '\n' from the error/warning message */
+ tmp_ewline->ew_text[STRLEN(tmp_ewline->ew_text) - 1] = '\0';
+ tmp_ewline->ew_lnum = (linenr_T) atoi((char *) token2);
+ tmp_ewline->ew_type = token3[1] == 'w' ? CC_WARNING : CC_ERROR;
+
+ if (!ew_head) {
+ ew_head = tmp_ewline;
+ ew_curr = tmp_ewline;
+ } else {
+ ew_curr->next = tmp_ewline;
+ tmp_ewline->prev = ew_curr;
+ ew_curr = ew_curr->next;
+ }
+ }
+
+ /* buf should be freed, because it is allocated in getline */
+ if (buf)
+ free(buf);
+
+ return ew_head;
+}
+
+/*
+ * Function compiles the temporary copy saving the output in a
+ * temporary file.
+ */
+ static int
+cc_compile_tmp_copy(cc_bufline_T *bufline, char_u *tmp_copy_ffname,
+ int copy_type) {
+ char_u cmd[MAX_CMD_LENGTH];
+
+ if (bufline->buf_compile_cmd[0] == NUL)
+ return CC_FAIL;
+
+ /* clear the arrays. */
+ memset(cmd, 0, MAX_CMD_LENGTH);
+
+ /* direct STDOUT & STDERR to the same file, so that the output of
+ * this will not interfer with the vim terminal */
+ sprintf((char *)cmd, "%s > %s.out 2>&1",
+ (char *)bufline->buf_compile_cmd, (char *)tmp_copy_ffname);
+#if 1
+ system((char *)cmd);
+#endif
+
+ return CC_SUCCESS;
+}
+
+/*
+ * Function creates a temporary copy of the buffer. copy_type determines
+ * the type of the copy process.
+ */
+ static int
+cc_create_tmp_copy(buf_T *buf, char_u *tmp_copy_ffname, int copy_type) {
+ int retval;
+ char_u cmd[MAX_CMD_LENGTH];
+
+ switch (copy_type) {
+ case CC_STANDALONE:
+ cc_set_tmp_copy_ffname(buf, tmp_copy_ffname);
+ sprintf((char *)cmd, "touch %s", (char *)tmp_copy_ffname);
+
+ system((char *)cmd);
+#if 1
+ retval = buf_write(buf, tmp_copy_ffname, NULL,
+ (linenr_T) 1, buf->b_ml.ml_line_count, NULL,
+ FALSE, FALSE, FALSE, TRUE);
+
+ if (retval == FAIL)
+ return CC_FAIL;
+ else
+#endif
+ return CC_SUCCESS;
+
+ /* these are not implemented yet. */
+ case CC_MASTER:
+ case CC_PROJECT:
+ default:
+ return CC_FAIL;
+ }
+}
+
+/*
+ * Outside accessible compile request for a specific buffer.
+ */
+ int
+cc_request_compile(buf_T *buf) {
+ if (!cc_started)
+ return CC_FAIL;
+
+ cc_pjob_T *tmp_pjob;
+
+ tmp_pjob = (cc_pjob_T *) calloc(1, sizeof(cc_pjob_T));
+ if (tmp_pjob == NULL)
+ return CC_FAIL;
+
+ tmp_pjob->cc_pjob_buf = buf;
+ tmp_pjob->cc_pjob_type = CC_COMPILE;
+
+ return cc_pjobs_produce(tmp_pjob);
+}
+
+/*
+ * Function required to populate the pending jobs array.
+ * TODO: FAILURE IN pthread_mutex_unlock is SERIOUS!!!
+ */
+ static int
+cc_pjobs_produce(cc_pjob_T *tmp_pjob) {
+ int retval;
+
+ sem_wait(&empty);
+
+ retval = pthread_mutex_lock(&mutex);
+ if (retval)
+ return CC_FAIL;
+
+ /* check if this buffer already has a pending job request. */
+ /* if so directly return. */
+ if (cc_pjobs_buf_exists(tmp_pjob)) {
+ sem_post(&empty);
+ retval = pthread_mutex_unlock(&mutex);
+ if (retval)
+ return CC_FAIL;
+ return CC_BUFEXISTS;
+ }
+
+ cc_pjobs[pindex % MAX_PENDING_JOBS] = tmp_pjob;
+ ++pindex;
+
+ retval = pthread_mutex_unlock(&mutex);
+ if (retval)
+ return CC_FAIL;
+
+ retval = sem_post(&full);
+ if (retval)
+ return CC_FAIL;
+
+ return CC_SUCCESS;
+}
+
+/*
+ * Returns TRUE if there is another pending job for this process.
+ */
+ static int
+cc_pjobs_buf_exists(cc_pjob_T *tmp_pjob) {
+ int i;
+
+ for (i = 0; i < MAX_PENDING_JOBS; ++i) {
+ if (cc_pjobs[i] == NULL ||
+ strcmp((char *) cc_pjobs[i]->cc_pjob_buf->b_ffname,
+ (char *) tmp_pjob->cc_pjob_buf->b_ffname))
+ continue;
+ else
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Removes the "top" element.
+ */
+ static cc_pjob_T *
+cc_pjobs_consume(void) {
+ int retval;
+ cc_pjob_T *tmp_pjob = NULL;
+
+ tmp_pjob = (cc_pjob_T *) calloc(1, sizeof(cc_pjob_T));
+ if (tmp_pjob == NULL)
+ return NULL;
+
+ sem_wait(&full);
+
+ retval = pthread_mutex_lock(&mutex);
+ if (retval)
+ goto con_fail;
+
+ tmp_pjob->cc_pjob_type = cc_pjobs[cindex % MAX_PENDING_JOBS]->cc_pjob_type;
+ tmp_pjob->cc_pjob_buf = cc_pjobs[cindex % MAX_PENDING_JOBS]->cc_pjob_buf;
+
+ /* free the old job!!! */
+ free(cc_pjobs[cindex % MAX_PENDING_JOBS]);
+ cc_pjobs[cindex % MAX_PENDING_JOBS] = NULL;
+
+ cindex++;
+
+ retval = pthread_mutex_unlock(&mutex);
+ if (retval)
+ goto con_fail;
+
+ retval = sem_post(&empty);
+ if (retval)
+ goto con_fail;
+
+ return tmp_pjob;
+
+con_fail:
+ free(tmp_pjob);
+ return NULL;
+}
+
+/*
+ * Checks if the specified buffer can be syntax checked or not.
+ */
+ int
+cc_is_buf_ok(buf_T *buf) {
+ if (!cc_started)
+ return CC_FAIL;
+
+ char_u *ffname = NULL;
+ char_u *p = NULL;
+ char_u *r = NULL;
+
+ if (buf == NULL || ((ffname = buf->b_ffname) == NULL))
+ return CC_FAIL;
+
+ /* find the file extension */
+ p = (char_u *) strrchr((char *) ffname, '.');
+
+ if (p == NULL)
+ return CC_FAIL;
+
+ /* check if this extension is supported or not */
+ r = (char_u *) strstr((char *) cc_sup_exts, (char *) p);
+
+ if (r == NULL)
+ return CC_FAIL;
+ else
+ return CC_SUCCESS;
+}
+
+/*
+ * Returns TRUE if the specified buffer is in the watchlist.
+ * TODO: THIS CAN RETURN THE FOUND BUFFER TO SAVE SOME TIME.
+ *
+ */
+ int
+cc_is_buf_watched(buf_T *buf) {
+ if (!cc_started)
+ return CC_FAIL;
+
+ cc_bufline_T *tmp_bufline;
+ int dummy;
+
+ tmp_bufline = cc_locate_buf_bin(buf, &dummy, &dummy, CC_FOR_FIND);
+ if (tmp_bufline == NULL)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/*
+ * Finds and sets a file name for the temporary copy of the buffer.
+ */
+ static int
+cc_set_tmp_copy_ffname(buf_T *buf, char_u *tmp_copy_ffname) {
+ char_u *buf_sfname;
+ char_u tmp_buf[MAX_PATH_LENGTH];
+ char_u *p;
+
+ /* clear the array. */
+ memset(tmp_buf, 0, MAX_PATH_LENGTH);
+
+ /* find the tmp_copy_ffname. */
+ /* uses gettail(buf->b_sfname) because sometimes b_sfname is
+ * actually b_ffname. */
+ buf_sfname = buf->b_sfname ? gettail(buf->b_sfname)
+ : gettail(buf->b_ffname);
+ if (buf_sfname == NULL || !strcmp((char *) buf_sfname, ""))
+ return CC_FAIL;
+
+ /* finds the folder path of the buffer. */
+ p = (char_u *)strstr((char *)buf->b_ffname, (char *)buf_sfname);
+ if (p == NULL)
+ return CC_FAIL;
+ STRNCPY(tmp_buf, buf->b_ffname, (p - buf->b_ffname));
+
+ /* rather than creating the copy in /tmp/, create it in the directory
+ * of the buffer. this solves a few issues without a lot of effort. */
+ sprintf((char *) tmp_copy_ffname, "%s.cc_%s",
+ (char *) tmp_buf, (char *) buf_sfname);
+ return CC_SUCCESS;
+}
+
+/*
+ * Adds the specified buffer and sets the compile command.
+ */
+ int
+cc_addbuf_setcmd(buf_T *buf, char_u *cmd) {
+ cc_bufline_T *tmp_bufline = NULL;
+ char_u tmp_compile_cmd[MAX_CMD_LENGTH];
+ char_u *p = NULL;
+ char_u tmp_copy_ffname[MAX_PATH_LENGTH];
+ size_t bname_len;
+
+ /* clear the arrays. */
+ memset(tmp_compile_cmd, 0, MAX_CMD_LENGTH);
+ memset(tmp_copy_ffname, 0, MAX_PATH_LENGTH);
+
+ tmp_bufline = cc_add_buf(buf);
+ if (tmp_bufline == NULL)
+ return CC_FAIL;
+
+ cc_set_tmp_copy_ffname(buf, tmp_copy_ffname);
+
+ /* at this point we should update the compile command, so
+ * that it works for the temporary copy, rather than the
+ * original copy.
+ * *** assumes that the user entered compile command has the
+ * full file name of the current buffer. */
+ p = (char_u *)strstr((char *)cmd,
+ (char *)tmp_bufline->buf_name);
+ if (p == NULL)
+ return CC_FAIL;
+ bname_len = STRLEN(tmp_bufline->buf_name);
+
+ STRNCPY(tmp_compile_cmd, cmd,
+ (p - cmd));
+ STRCAT(tmp_compile_cmd, tmp_copy_ffname);
+ STRCAT(tmp_compile_cmd, p + bname_len);
+
+ STRCPY(tmp_bufline->buf_compile_cmd, tmp_compile_cmd);
+
+ /* TODO: check if tmp_bufline->buf_compile_cmd is valid. */
+ return CC_SUCCESS;
+}
+
+/*
+ * Adds the specified buffer to the watch list.
+ *
+ * modified a little: returns a pointer to the inserted cc_bufline_T
+ * object, to make things a little faster.
+ *
+ */
+ static cc_bufline_T*
+cc_add_buf(buf_T *buf) {
+ if (!cc_started)
+ return NULL;
+
+ cc_bufline_T *tmp_bufline = NULL;
+ cc_bufline_T *tmp_insafter = NULL;
+ int buf_idx;
+ int put_before;
+ int i;
+ int retval;
+
+ /* check if this buffer is in the watch list, if so don't add */
+ tmp_bufline = cc_locate_buf_bin(buf, &buf_idx, &put_before, CC_FOR_FIND);
+ if (tmp_bufline != NULL)
+ return NULL;
+
+ if (cc_list.cc_bufcount == MAX_BUFLINES)
+ return NULL;
+
+ tmp_bufline = (cc_bufline_T *) calloc(1, sizeof(cc_bufline_T));
+
+ if (tmp_bufline == NULL)
+ return NULL;
+
+ tmp_bufline->buf_name = buf->b_ffname;
+ memset(tmp_bufline->buf_compile_cmd, 0, MAX_CMD_LENGTH);
+ pthread_mutex_init(&(tmp_bufline->buf_mutex), NULL);
+
+ if (cc_list.cc_bufcount == 0) {
+ /* this is the first buffer */
+ cc_list.cc_list_head = tmp_bufline;
+ tmp_bufline->prev = NULL;
+ tmp_bufline->next = NULL;
+ cc_bufline_ptrs[0] = tmp_bufline;
+ } else {
+ tmp_insafter = cc_locate_buf_bin(buf, &buf_idx, &put_before, CC_FOR_ADD);
+
+ if (tmp_insafter == NULL) {
+ free(tmp_bufline);
+ return NULL;
+ }
+
+ if (put_before) {
+ /* move elements in cc_bufline_ptrs */
+ for (i = cc_list.cc_bufcount; i > buf_idx; --i)
+ cc_bufline_ptrs[i] = cc_bufline_ptrs[i - 1];
+
+ cc_bufline_ptrs[buf_idx] = tmp_bufline;
+
+ tmp_bufline->prev = tmp_insafter->prev;
+ if (tmp_insafter->prev != NULL)
+ tmp_insafter->prev->next = tmp_bufline;
+ else
+ cc_list.cc_list_head = tmp_bufline;
+
+ tmp_bufline->next = tmp_insafter;
+ tmp_insafter->prev = tmp_bufline;
+ } else {
+ /* move elements in cc_bufline_ptrs */
+ for (i = cc_list.cc_bufcount; i > buf_idx + 1; --i)
+ cc_bufline_ptrs[i] = cc_bufline_ptrs[i - 1];
+
+ cc_bufline_ptrs[buf_idx + 1] = tmp_bufline;
+
+ tmp_bufline->next = tmp_insafter->next;
+ if (tmp_bufline->next != NULL)
+ tmp_bufline->next->prev = tmp_bufline;
+
+ tmp_insafter->next = tmp_bufline;
+ tmp_bufline->prev = tmp_insafter;
+ }
+ }
+
+ /* create an initial compile request for this buffer */
+ retval = cc_request_compile(buf);
+ if (retval == CC_FAIL)
+ return NULL;
+
+ cc_list.cc_list_curr = tmp_bufline;
+ cc_list.cc_bufcount++;
+
+ return tmp_bufline;
+}
+
+/*
+ * Removes the specified buffer from the watch list.
+ * TODO: current implementation does not have sorted lists.
+ */
+ int
+cc_rem_buf(buf_T *buf) {
+ if (!cc_started)
+ return CC_FAIL;
+
+ cc_bufline_T *tmp_bufline = NULL;
+ int buf_idx;
+ int put_before;
+ int i;
+
+ tmp_bufline = cc_locate_buf_bin(buf, &buf_idx, &put_before, CC_FOR_REM);
+
+ if (tmp_bufline == NULL)
+ return CC_NOSUCHBUF;
+
+ if (tmp_bufline->prev != NULL)
+ tmp_bufline->prev->next = tmp_bufline->next;
+
+ if (tmp_bufline->next != NULL)
+ tmp_bufline->next->prev = tmp_bufline->prev;
+
+ if (tmp_bufline->buf_ewlist_head != NULL)
+ cc_free_ewlist(tmp_bufline->buf_ewlist_head);
+
+ vim_free(tmp_bufline);
+
+ /* update cc_bufline_ptrs */
+ for (i = buf_idx; i < cc_list.cc_bufcount; ++i)
+ cc_bufline_ptrs[i] = cc_bufline_ptrs[i + 1];
+
+ cc_list.cc_bufcount--;
+
+ /* update the screen in case there are some highlighted lines. */
+ cc_update_screen();
+
+ return CC_SUCCESS;
+}
+
+/*
+ * Returns a pointer to the specified buffer node.
+ * Returns NULL if there is no such buffer.
+ * This is the linear search version.
+ * THIS SHOULD NOT BE USED ANYMORE!!!
+ */
+ static cc_bufline_T *
+cc_locate_buf(buf_T *buf) {
+ cc_bufline_T *tmp_bufline = NULL;
+ char_u *ffname = NULL;
+
+ ffname = buf->b_ffname;
+
+ for (tmp_bufline = cc_list.cc_list_head;
+ tmp_bufline != NULL; tmp_bufline = tmp_bufline->next) {
+ if (strcmp((char *) tmp_bufline->buf_name, (char *) ffname))
+ continue;
+ else
+ break;
+ }
+
+ return tmp_bufline;
+}
+
+/*
+ * Binary search of the buffer list. Has two modes:
+ * - search for addition (find the correct position in the list which the
+ * specified buffer should be inserted to)(return CC_BUFEXISTS if the
+ * buffer is already in the watch list),
+ * - search for removal (find the exact location of the buffer, return NULL
+ * if could not be found).
+ */
+ static cc_bufline_T *
+cc_locate_buf_bin(buf_T *buf, int *buf_idx, int *put_before, int mode) {
+ cc_bufline_T *tmp_bufline = NULL;
+ int start = 0;
+ int end = cc_list.cc_bufcount - 1;
+ int mid = (start + end) / 2;
+ int retval;
+
+ while (start <= end) {
+ mid = (start + end) / 2;
+
+ /* since this is a linked list we are using cc_bufline_ptrs
+ * to find the correct node. */
+ tmp_bufline = cc_bufline_ptrs[mid];
+
+ retval = strcmp((char *) buf->b_ffname, (char *) tmp_bufline->buf_name);
+
+ if (retval == 0) {
+ *buf_idx = mid;
+ if (mode == CC_FOR_ADD)
+ return NULL;
+ else if (mode == CC_FOR_REM || mode == CC_FOR_FIND)
+ return tmp_bufline;
+ } else if (retval < 0) {
+ end = mid - 1;
+ *put_before = TRUE;
+ } else {
+ start = mid + 1;
+ *put_before = FALSE;
+ }
+ }
+
+ /* the buffer is not inside the list */
+ *buf_idx = mid;
+ if (mode == CC_FOR_ADD)
+ return tmp_bufline;
+ else if (mode == CC_FOR_REM || mode == CC_FOR_FIND)
+ return NULL;
+ else /* should not be reached. */
+ return NULL;
+}
+
+/*
+ * Frees the specified error & warning list.
+ */
+ static void
+cc_free_ewlist(cc_ewline_T *ewlist_head) {
+ if (ewlist_head == NULL)
+ return;
+
+ cc_ewline_T *tmp_ewline = ewlist_head;
+ cc_ewline_T *tmp_ewlinep = ewlist_head;
+
+ while (tmp_ewline != NULL) {
+ tmp_ewline = tmp_ewline->next;
+ free(tmp_ewlinep);
+ tmp_ewlinep = tmp_ewline;
+ }
+}
+
+/*
+ * Frees the specified buffer watch list.
+ * TODO: CHECK THIS BEHAVIOR!!! IMPORTANT!!!
+ */
+ static void
+cc_free_buflist(cc_info_T *cc_list_a,
+ cc_bufline_T *cc_bufline_ptrs_a[MAX_BUFLINES]) {
+ int i;
+
+ if (cc_list_a->cc_list_head == NULL)
+ return;
+
+ cc_bufline_T *tmp_bufline = cc_list_a->cc_list_head;
+ cc_bufline_T *tmp_buflinep = cc_list_a->cc_list_head;
+
+ while (tmp_bufline != NULL) {
+ cc_free_ewlist(tmp_bufline->buf_ewlist_head);
+
+ tmp_bufline = tmp_bufline->next;
+ free(tmp_buflinep);
+ tmp_buflinep = tmp_bufline;
+ }
+
+ for (i = 0; i < cc_list_a->cc_bufcount; ++i)
+ cc_bufline_ptrs_a[i] = NULL;
+
+ cc_list_a->cc_list_head = NULL;
+ cc_list_a->cc_list_curr = NULL;
+ cc_list_a->cc_bufcount = 0;
+}
+
+/*
+ * Frees the memory.
+ */
+ static void
+cc_free(void) {
+ if (!cc_started)
+ return;
+
+ cc_list.cc_list_curr = NULL;
+ cc_free_buflist(&cc_list, cc_bufline_ptrs);
+}
+
+/*
+ * TODO: cc_exit() function.
+ * what else should it do???
+ */
+ void
+cc_exit(void) {
+ p_ut = old_p_ut;
+ cc_started = FALSE;
+ cc_free();
+}
+
Index: src/misc1.c
===================================================================
--- src/misc1.c (revision 1164)
+++ src/misc1.c (working copy)
@@ -2674,6 +2674,11 @@
#endif
changed_common(lnum, col, lnume, xtra);
+
+ /* update ew linenumbers for CodeCheck if the current buffer is
+ * being watched. */
+ if (cc_is_buf_watched(curbuf))
+ cc_update_ew_lnums(curbuf, lnum, col, xtra);
}
static void
Index: src/Makefile
===================================================================
--- src/Makefile (revision 1164)
+++ src/Makefile (working copy)
@@ -1366,6 +1366,7 @@
BASIC_SRC = \
buffer.c \
charset.c \
+ code_check.c \
diff.c \
digraph.c \
edit.c \
@@ -1436,6 +1437,7 @@
OBJ = \
objects/buffer.o \
objects/charset.o \
+ objects/code_check.o \
objects/diff.o \
objects/digraph.o \
objects/edit.o \
@@ -1495,6 +1497,7 @@
PRO_AUTO = \
buffer.pro \
charset.pro \
+ code_check.pro \
diff.pro \
digraph.pro \
edit.pro \
@@ -2327,6 +2330,9 @@
objects/charset.o: charset.c
$(CCC) -o $@ charset.c
+objects/code_check.o: code_check.c
+ $(CCC) -o $@ code_check.c
+
objects/diff.o: diff.c
$(CCC) -o $@ diff.c
@@ -2655,6 +2661,10 @@
ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \
gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \
arabic.h
+objects/code_check.o: code_check.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \
+ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \
+ gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \
+ arabic.h
objects/diff.o: diff.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \
ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \
gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \
Index: src/keymap.h
===================================================================
--- src/keymap.h (revision 1164)
+++ src/keymap.h (working copy)
@@ -256,6 +256,7 @@
, KE_NOP /* doesn't do something */
, KE_FOCUSGAINED /* focus gained */
, KE_FOCUSLOST /* focus lost */
+ , KE_REDRAW /* update the screen */
};
/*
@@ -452,6 +453,8 @@
#define K_CURSORHOLD TERMCAP2KEY(KS_EXTRA, KE_CURSORHOLD)
+#define K_REDRAW TERMCAP2KEY(KS_EXTRA, KE_REDRAW)
+
/* Bits for modifier mask */
/* 0x01 cannot be used, because the modifier must be 0x02 or higher */
#define MOD_MASK_SHIFT 0x02
Index: src/screen.c
===================================================================
--- src/screen.c (revision 1164)
+++ src/screen.c (working copy)
@@ -2709,6 +2709,26 @@
#else
extra_check = 0;
#endif
+
+ /*
+ * Highlight the line if this buffer is in the code_check.c watchlist,
+ * and there is an associated error in the corresponding error/warning
+ * list.
+ */
+ if (cc_is_buf_watched(curbuf)) {
+ switch (cc_get_ew_type(curbuf, lnum)) {
+ case /*CC_WARNING*/1:
+ line_attr = hl_attr(HLF_WM);
+ break;
+ case /*CC_ERROR*/2:
+ line_attr = hl_attr(HLF_E);
+ break;
+ default:
+ line_attr = 0;
+ break;
+ }
+ }
+
#ifdef FEAT_SYN_HL
if (syntax_present(wp->w_buffer) && !wp->w_buffer->b_syn_error)
{
Index: src/fileio.c
===================================================================
--- src/fileio.c (revision 1164)
+++ src/fileio.c (working copy)
@@ -8473,6 +8473,12 @@
{
int state;
+ /* trigger a compile request if the current buffer is in
+ * the watchlist and the current mode is NOT CMDLINE. */
+ if ((State & CMDLINE) == 0)
+ if (cc_is_buf_watched(curbuf))
+ cc_request_compile(curbuf);
+
if (!did_cursorhold && has_cursorhold() && !Recording
#ifdef FEAT_INS_EXPAND
&& !ins_compl_active()