2 * $Id: vim.c 485 2006-10-24 12:06:19Z dfishburn $
4 * Copyright (c) 2000-2003, Darren Hiebert
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
9 * Thanks are due to Jay Glanville for significant improvements.
11 * This module contains functions for generating tags for user-defined
12 * functions for the Vim editor.
18 #include "general.h" /* must always come first */
32 typedef struct sLineInfo
{
37 unsigned long lineNumber
;
53 static kindOption VimKinds
[] = {
54 { TRUE
, 'a', "augroup", "autocommand groups" },
55 { TRUE
, 'c', "command", "user-defined commands" },
56 { TRUE
, 'f', "function", "function definitions" },
57 { TRUE
, 'm', "map", "maps" },
58 { TRUE
, 'v', "variable", "variable definitions" },
66 typedef enum eException
{
67 ExceptionNone
, ExceptionEOF
76 static jmp_buf Exception
;
80 * FUNCTION DEFINITIONS
83 /* This function takes a char pointer, tries to find a scope separator in the
84 * string, and if it does, returns a pointer to the character after the colon,
85 * and the character defining the scope.
86 * If a colon is not found, it returns the original pointer.
88 static const unsigned char* skipPrefix (const unsigned char* name
, int *scope
)
90 const unsigned char* result
= name
;
93 length
= strlen((const char*)name
);
96 if (length
> 3 && name
[1] == ':')
102 else if (length
> 5 && strncasecmp ((const char*) name
, "<SID>", (size_t) 5) == 0)
111 * Vim7 check for dictionaries or autoload function names
116 switch ( name
[counter
] )
119 /* Set the scope to d - Dictionary */
123 /* Set the scope to a - autoload */
128 } while (isalnum ((int) name
[counter
]) ||
129 name
[counter
] == '_' ||
130 name
[counter
] == '.' ||
137 static boolean
isMap (const unsigned char* line
)
140 * There are many different short cuts for specifying a map.
141 * This routine should capture all the permutations.
144 strncmp ((const char*) line
, "map", (size_t) 3) == 0 ||
145 strncmp ((const char*) line
, "nm", (size_t) 2) == 0 ||
146 strncmp ((const char*) line
, "nma", (size_t) 3) == 0 ||
147 strncmp ((const char*) line
, "nmap", (size_t) 4) == 0 ||
148 strncmp ((const char*) line
, "vm", (size_t) 2) == 0 ||
149 strncmp ((const char*) line
, "vma", (size_t) 3) == 0 ||
150 strncmp ((const char*) line
, "vmap", (size_t) 4) == 0 ||
151 strncmp ((const char*) line
, "om", (size_t) 2) == 0 ||
152 strncmp ((const char*) line
, "oma", (size_t) 3) == 0 ||
153 strncmp ((const char*) line
, "omap", (size_t) 4) == 0 ||
154 strncmp ((const char*) line
, "im", (size_t) 2) == 0 ||
155 strncmp ((const char*) line
, "ima", (size_t) 3) == 0 ||
156 strncmp ((const char*) line
, "imap", (size_t) 4) == 0 ||
157 strncmp ((const char*) line
, "lm", (size_t) 2) == 0 ||
158 strncmp ((const char*) line
, "lma", (size_t) 3) == 0 ||
159 strncmp ((const char*) line
, "lmap", (size_t) 4) == 0 ||
160 strncmp ((const char*) line
, "cm", (size_t) 2) == 0 ||
161 strncmp ((const char*) line
, "cma", (size_t) 3) == 0 ||
162 strncmp ((const char*) line
, "cmap", (size_t) 4) == 0 ||
163 strncmp ((const char*) line
, "no", (size_t) 2) == 0 ||
164 strncmp ((const char*) line
, "nor", (size_t) 3) == 0 ||
165 strncmp ((const char*) line
, "nore", (size_t) 4) == 0 ||
166 strncmp ((const char*) line
, "norem", (size_t) 5) == 0 ||
167 strncmp ((const char*) line
, "norema", (size_t) 6) == 0 ||
168 strncmp ((const char*) line
, "noremap", (size_t) 7) == 0 ||
169 strncmp ((const char*) line
, "nno", (size_t) 3) == 0 ||
170 strncmp ((const char*) line
, "nnor", (size_t) 4) == 0 ||
171 strncmp ((const char*) line
, "nnore", (size_t) 5) == 0 ||
172 strncmp ((const char*) line
, "nnorem", (size_t) 6) == 0 ||
173 strncmp ((const char*) line
, "nnorema", (size_t) 7) == 0 ||
174 strncmp ((const char*) line
, "nnoremap", (size_t) 8) == 0 ||
175 strncmp ((const char*) line
, "vno", (size_t) 3) == 0 ||
176 strncmp ((const char*) line
, "vnor", (size_t) 4) == 0 ||
177 strncmp ((const char*) line
, "vnore", (size_t) 5) == 0 ||
178 strncmp ((const char*) line
, "vnorem", (size_t) 6) == 0 ||
179 strncmp ((const char*) line
, "vnorema", (size_t) 7) == 0 ||
180 strncmp ((const char*) line
, "vnoremap", (size_t) 8) == 0 ||
181 strncmp ((const char*) line
, "ono", (size_t) 3) == 0 ||
182 strncmp ((const char*) line
, "onor", (size_t) 4) == 0 ||
183 strncmp ((const char*) line
, "onore", (size_t) 5) == 0 ||
184 strncmp ((const char*) line
, "onorem", (size_t) 6) == 0 ||
185 strncmp ((const char*) line
, "onorema", (size_t) 7) == 0 ||
186 strncmp ((const char*) line
, "onoremap", (size_t) 8) == 0 ||
187 strncmp ((const char*) line
, "ino", (size_t) 3) == 0 ||
188 strncmp ((const char*) line
, "inor", (size_t) 4) == 0 ||
189 strncmp ((const char*) line
, "inore", (size_t) 5) == 0 ||
190 strncmp ((const char*) line
, "inorem", (size_t) 6) == 0 ||
191 strncmp ((const char*) line
, "inorema", (size_t) 7) == 0 ||
192 strncmp ((const char*) line
, "inoremap", (size_t) 8) == 0 ||
193 strncmp ((const char*) line
, "lno", (size_t) 3) == 0 ||
194 strncmp ((const char*) line
, "lnor", (size_t) 4) == 0 ||
195 strncmp ((const char*) line
, "lnore", (size_t) 5) == 0 ||
196 strncmp ((const char*) line
, "lnorem", (size_t) 6) == 0 ||
197 strncmp ((const char*) line
, "lnorema", (size_t) 7) == 0 ||
198 strncmp ((const char*) line
, "lnoremap", (size_t) 8) == 0 ||
199 strncmp ((const char*) line
, "cno", (size_t) 3) == 0 ||
200 strncmp ((const char*) line
, "cnor", (size_t) 4) == 0 ||
201 strncmp ((const char*) line
, "cnore", (size_t) 5) == 0 ||
202 strncmp ((const char*) line
, "cnorem", (size_t) 6) == 0 ||
203 strncmp ((const char*) line
, "cnorema", (size_t) 7) == 0 ||
204 strncmp ((const char*) line
, "cnoremap", (size_t) 8) == 0
211 static const unsigned char * readVimLine (void)
213 const unsigned char *line
;
215 while ((line
= fileReadLine ()) != NULL
)
217 while (isspace ((int) *line
))
220 if ((int) *line
== '"')
221 continue; /* skip comment */
229 static void parseFunction (const unsigned char *line
)
231 vString
*name
= vStringNew ();
232 /* boolean inFunction = FALSE; */
235 const unsigned char *cp
= line
+ 1;
237 if ((int) *++cp
== 'n' && (int) *++cp
== 'c' &&
238 (int) *++cp
== 't' && (int) *++cp
== 'i' &&
239 (int) *++cp
== 'o' && (int) *++cp
== 'n')
241 if ((int) *cp
== '!')
243 if (isspace ((int) *cp
))
245 while (*cp
&& isspace ((int) *cp
))
250 cp
= skipPrefix (cp
, &scope
);
251 if (isupper ((int) *cp
) ||
252 scope
== 's' || /* script scope */
253 scope
== '<' || /* script scope */
254 scope
== 'd' || /* dictionary */
255 scope
== 'a') /* autoload */
259 vStringPut (name
, (int) *cp
);
261 } while (isalnum ((int) *cp
) || *cp
== '_' || *cp
== '.' || *cp
== '#');
262 vStringTerminate (name
);
263 makeSimpleTag (name
, VimKinds
, K_FUNCTION
);
269 /* TODO - update struct to indicate inside function */
270 while ((line
= readVimLine ()) != NULL
)
273 * Vim7 added the for/endfo[r] construct, so we must first
274 * check for an "endfo", before a "endf"
276 if ( (!strncmp ((const char*) line
, "endfo", (size_t) 5) == 0) &&
277 (strncmp ((const char*) line
, "endf", (size_t) 4) == 0) )
279 /* TODO - call parseVimLine */
281 vStringDelete (name
);
284 static void parseAutogroup (const unsigned char *line
)
286 vString
*name
= vStringNew ();
288 /* Found Autocommand Group (augroup) */
289 const unsigned char *cp
= line
+ 2;
290 if ((int) *++cp
== 'r' && (int) *++cp
== 'o' &&
291 (int) *++cp
== 'u' && (int) *++cp
== 'p')
293 if (isspace ((int) *cp
))
295 while (*cp
&& isspace ((int) *cp
))
300 if (strncasecmp ((const char*) cp
, "end", (size_t) 3) != 0)
304 vStringPut (name
, (int) *cp
);
306 } while (isalnum ((int) *cp
) || *cp
== '_');
307 vStringTerminate (name
);
308 makeSimpleTag (name
, VimKinds
, K_AUGROUP
);
313 vStringDelete (name
);
316 static boolean
parseCommand (const unsigned char *line
)
318 vString
*name
= vStringNew ();
319 boolean cmdProcessed
= TRUE
;
322 * Found a user-defined command
324 * They can have many options preceeded by a dash
325 * command! -nargs=+ -complete Select :call s:DB_execSql("select " . <q-args>)
326 * The name of the command should be the first word not preceeded by a dash
329 const unsigned char *cp
= line
;
331 if ( (int) *cp
== '\\' )
334 * We are recursively calling this function is the command
335 * has been continued on to the next line
337 * Vim statements can be continued onto a newline using a \
338 * to indicate the previous line is continuing.
340 * com -nargs=1 -bang -complete=customlist,EditFileComplete
341 * \ EditFile edit<bang> <args>
343 * If the following lines do not have a line continuation
344 * the command must not be spanning multiple lines and should
345 * be synatically incorrect.
347 if ((int) *cp
== '\\')
350 while (*cp
&& isspace ((int) *cp
))
353 else if ( (!strncmp ((const char*) line
, "comp", (size_t) 4) == 0) &&
354 (!strncmp ((const char*) line
, "comc", (size_t) 4) == 0) &&
355 (strncmp ((const char*) line
, "com", (size_t) 3) == 0) )
358 if ((int) *++cp
== 'm' && (int) *++cp
== 'a' &&
359 (int) *++cp
== 'n' && (int) *++cp
== 'd')
362 if ((int) *cp
== '!')
365 while (*cp
&& isspace ((int) *cp
))
371 * We are recursively calling this function. If it does not start
372 * with "com" or a line continuation character, we have moved off
373 * the command line and should let the other routines parse this file.
375 cmdProcessed
= FALSE
;
380 * Strip off any spaces and options which are part of the command.
381 * These should preceed the command name.
385 if (isspace ((int) *cp
))
392 * Read until the next space which sparates options or the name
394 while (*cp
&& !isspace ((int) *cp
))
397 } while ( *cp
&& !isalnum ((int) *cp
) );
402 * We have reached the end of the line without finding the command name.
403 * Read the next line and continue processing it as a command.
405 line
= readVimLine();
412 vStringPut (name
, (int) *cp
);
414 } while (isalnum ((int) *cp
) || *cp
== '_');
416 vStringTerminate (name
);
417 makeSimpleTag (name
, VimKinds
, K_COMMAND
);
421 vStringDelete (name
);
426 static void parseLet (const unsigned char *line
)
428 vString
*name
= vStringNew ();
430 /* we've found a variable declared outside of a function!! */
431 const unsigned char *cp
= line
+ 3;
432 const unsigned char *np
= line
;
434 if (isspace ((int) *cp
))
436 while (*cp
&& isspace ((int) *cp
))
440 * Ignore lets which set:
441 * & - local buffer vim settings
443 * [ - Lists or Dictionaries
445 if (!*cp
|| *cp
== '&' || *cp
== '@' || *cp
== '[' )
449 * Ignore vim variables which are read only
450 * v: - Vim variables.
454 if ((int) *cp
== 'v' && (int) *np
== ':' )
457 /* deal with spaces, $, @ and & */
458 while (*cp
&& *cp
!= '$' && !isalnum ((int) *cp
))
464 /* cp = skipPrefix (cp, &scope); */
470 vStringPut (name
, (int) *cp
);
472 } while (isalnum ((int) *cp
) || *cp
== '_' || *cp
== '#' || *cp
== ':' || *cp
== '$');
473 vStringTerminate (name
);
474 makeSimpleTag (name
, VimKinds
, K_VARIABLE
);
479 vStringDelete (name
);
482 static boolean
parseMap (const unsigned char *line
)
484 vString
*name
= vStringNew ();
486 const unsigned char *cp
= line
;
489 while (*cp
&& isalnum ((int) *cp
))
492 if ((int) *cp
== '!')
496 * Maps follow this basic format
498 * nnoremap <silent> <F8> :Tlist<CR>
499 * map <unique> <Leader>scdt <Plug>GetColumnDataType
500 * inoremap ,,, <esc>diwi<<esc>pa><cr></<esc>pa><esc>kA
501 * inoremap <buffer> ( <C-R>=PreviewFunctionSignature()<LF>
503 * The Vim help shows the various special arguments available to a map:
504 * 1.2 SPECIAL ARGUMENTS *:map-arguments*
512 * Strip the special arguments from the map command, this should leave
513 * the map name which we will use as the "name".
518 while (*cp
&& isspace ((int) *cp
))
521 if (strncmp ((const char*) cp
, "<Leader>", (size_t) 8) == 0)
525 strncmp ((const char*) cp
, "<buffer>", (size_t) 8) == 0 ||
526 strncmp ((const char*) cp
, "<silent>", (size_t) 8) == 0 ||
527 strncmp ((const char*) cp
, "<script>", (size_t) 8) == 0 ||
528 strncmp ((const char*) cp
, "<unique>", (size_t) 8) == 0
535 if (strncmp ((const char*) cp
, "<expr>", (size_t) 6) == 0)
541 if (strncmp ((const char*) cp
, "<special>", (size_t) 9) == 0)
552 vStringPut (name
, (int) *cp
);
554 } while (*cp
&& *cp
!= ' ');
556 vStringTerminate (name
);
557 makeSimpleTag (name
, VimKinds
, K_MAP
);
560 vStringDelete (name
);
565 static boolean
parseVimLine (const unsigned char *line
)
567 boolean readNextLine
= TRUE
;
569 if ( (!strncmp ((const char*) line
, "comp", (size_t) 4) == 0) &&
570 (!strncmp ((const char*) line
, "comc", (size_t) 4) == 0) &&
571 (strncmp ((const char*) line
, "com", (size_t) 3) == 0) )
573 readNextLine
= parseCommand(line
);
574 /* TODO - Handle parseCommand returning FALSE */
582 if (strncmp ((const char*) line
, "fu", (size_t) 2) == 0)
587 if (strncmp ((const char*) line
, "aug", (size_t) 3) == 0)
589 parseAutogroup(line
);
592 if ( strncmp ((const char*) line
, "let", (size_t) 3) == 0 )
600 static void parseVimFile (const unsigned char *line
)
602 boolean readNextLine
= TRUE
;
603 line
= readVimLine();
607 readNextLine
= parseVimLine(line
);
610 line
= readVimLine();
615 static void findVimTags (void)
617 const unsigned char *line
;
618 /* TODO - change this into a structure */
625 extern parserDefinition
* VimParser (void)
627 static const char *const extensions
[] = { "vim", NULL
};
628 parserDefinition
* def
= parserNew ("Vim");
629 def
->kinds
= VimKinds
;
630 def
->kindCount
= KIND_COUNT (VimKinds
);
631 def
->extensions
= extensions
;
632 def
->parser
= findVimTags
;
636 /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */