Handle template expressions that may use the << or >> operators
[arduino-ctags.git] / vim.c
blob951ee5f6d2e54e4ae24f4873e1890e21cba4834c
1 /*
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.
16 * INCLUDE FILES
18 #include "general.h" /* must always come first */
20 #include <string.h>
21 #include <setjmp.h>
22 #ifdef DEBUG
23 #include <stdio.h>
24 #endif
27 #include "parse.h"
28 #include "read.h"
29 #include "vstring.h"
31 #if 0
32 typedef struct sLineInfo {
33 tokenType type;
34 keywordId keyword;
35 vString * string;
36 vString * scope;
37 unsigned long lineNumber;
38 fpos_t filePosition;
39 } lineInfo;
40 #endif
43 * DATA DEFINITIONS
45 typedef enum {
46 K_AUGROUP,
47 K_COMMAND,
48 K_FUNCTION,
49 K_MAP,
50 K_VARIABLE
51 } vimKind;
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" },
62 * DATA DECLARATIONS
65 #if 0
66 typedef enum eException {
67 ExceptionNone, ExceptionEOF
68 } exception_t;
69 #endif
72 * DATA DEFINITIONS
75 #if 0
76 static jmp_buf Exception;
77 #endif
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;
91 int counter;
92 size_t length;
93 length = strlen((const char*)name);
94 if (scope != NULL)
95 *scope = '\0';
96 if (length > 3 && name[1] == ':')
98 if (scope != NULL)
99 *scope = *name;
100 result = name + 2;
102 else if (length > 5 && strncasecmp ((const char*) name, "<SID>", (size_t) 5) == 0)
104 if (scope != NULL)
105 *scope = *name;
106 result = name + 5;
108 else
111 * Vim7 check for dictionaries or autoload function names
113 counter = 0;
116 switch ( name[counter] )
118 case '.':
119 /* Set the scope to d - Dictionary */
120 *scope = 'd';
121 break;
122 case '#':
123 /* Set the scope to a - autoload */
124 *scope = 'a';
125 break;
127 ++counter;
128 } while (isalnum ((int) name[counter]) ||
129 name[counter] == '_' ||
130 name[counter] == '.' ||
131 name[counter] == '#'
134 return result;
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.
143 if (
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
206 return TRUE;
208 return FALSE;
211 static const unsigned char * readVimLine (void)
213 const unsigned char *line;
215 while ((line = fileReadLine ()) != NULL)
217 while (isspace ((int) *line))
218 ++line;
220 if ((int) *line == '"')
221 continue; /* skip comment */
223 break;
226 return line;
229 static void parseFunction (const unsigned char *line)
231 vString *name = vStringNew ();
232 /* boolean inFunction = FALSE; */
233 int scope;
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')
240 ++cp;
241 if ((int) *cp == '!')
242 ++cp;
243 if (isspace ((int) *cp))
245 while (*cp && isspace ((int) *cp))
246 ++cp;
248 if (*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);
260 ++cp;
261 } while (isalnum ((int) *cp) || *cp == '_' || *cp == '.' || *cp == '#');
262 vStringTerminate (name);
263 makeSimpleTag (name, VimKinds, K_FUNCTION);
264 vStringClear (name);
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) )
278 break;
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')
292 ++cp;
293 if (isspace ((int) *cp))
295 while (*cp && isspace ((int) *cp))
296 ++cp;
298 if (*cp)
300 if (strncasecmp ((const char*) cp, "end", (size_t) 3) != 0)
304 vStringPut (name, (int) *cp);
305 ++cp;
306 } while (isalnum ((int) *cp) || *cp == '_');
307 vStringTerminate (name);
308 makeSimpleTag (name, VimKinds, K_AUGROUP);
309 vStringClear (name);
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 == '\\')
348 ++cp;
350 while (*cp && isspace ((int) *cp))
351 ++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) )
357 cp += 2;
358 if ((int) *++cp == 'm' && (int) *++cp == 'a' &&
359 (int) *++cp == 'n' && (int) *++cp == 'd')
360 ++cp;
362 if ((int) *cp == '!')
363 ++cp;
365 while (*cp && isspace ((int) *cp))
366 ++cp;
368 else
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;
376 goto cleanUp;
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))
387 ++cp;
389 else if (*cp == '-')
392 * Read until the next space which sparates options or the name
394 while (*cp && !isspace ((int) *cp))
395 ++cp;
397 } while ( *cp && !isalnum ((int) *cp) );
399 if ( ! *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();
406 parseCommand(line);
407 goto cleanUp;
412 vStringPut (name, (int) *cp);
413 ++cp;
414 } while (isalnum ((int) *cp) || *cp == '_');
416 vStringTerminate (name);
417 makeSimpleTag (name, VimKinds, K_COMMAND);
418 vStringClear (name);
420 cleanUp:
421 vStringDelete (name);
423 return cmdProcessed;
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;
433 /* get the name */
434 if (isspace ((int) *cp))
436 while (*cp && isspace ((int) *cp))
437 ++cp;
440 * Ignore lets which set:
441 * & - local buffer vim settings
442 * @ - registers
443 * [ - Lists or Dictionaries
445 if (!*cp || *cp == '&' || *cp == '@' || *cp == '[' )
446 goto cleanUp;
449 * Ignore vim variables which are read only
450 * v: - Vim variables.
452 np = cp;
453 ++np;
454 if ((int) *cp == 'v' && (int) *np == ':' )
455 goto cleanUp;
457 /* deal with spaces, $, @ and & */
458 while (*cp && *cp != '$' && !isalnum ((int) *cp))
459 ++cp;
461 if (!*cp)
462 goto cleanUp;
464 /* cp = skipPrefix (cp, &scope); */
467 if (!*cp)
468 break;
470 vStringPut (name, (int) *cp);
471 ++cp;
472 } while (isalnum ((int) *cp) || *cp == '_' || *cp == '#' || *cp == ':' || *cp == '$');
473 vStringTerminate (name);
474 makeSimpleTag (name, VimKinds, K_VARIABLE);
475 vStringClear (name);
478 cleanUp:
479 vStringDelete (name);
482 static boolean parseMap (const unsigned char *line)
484 vString *name = vStringNew ();
486 const unsigned char *cp = line;
488 /* Remove map */
489 while (*cp && isalnum ((int) *cp))
490 ++cp;
492 if ((int) *cp == '!')
493 ++cp;
496 * Maps follow this basic format
497 * map
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*
505 * <buffer>
506 * <silent>
507 * <script>
508 * <unique>
509 * <special>
510 * <expr>
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))
519 ++cp;
521 if (strncmp ((const char*) cp, "<Leader>", (size_t) 8) == 0)
522 break;
524 if (
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
531 cp += 8;
532 continue;
535 if (strncmp ((const char*) cp, "<expr>", (size_t) 6) == 0)
537 cp += 6;
538 continue;
541 if (strncmp ((const char*) cp, "<special>", (size_t) 9) == 0)
543 cp += 9;
544 continue;
547 break;
548 } while (*cp);
552 vStringPut (name, (int) *cp);
553 ++cp;
554 } while (*cp && *cp != ' ');
556 vStringTerminate (name);
557 makeSimpleTag (name, VimKinds, K_MAP);
558 vStringClear (name);
560 vStringDelete (name);
562 return TRUE;
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 */
577 if (isMap(line))
579 parseMap(line);
582 if (strncmp ((const char*) line, "fu", (size_t) 2) == 0)
584 parseFunction(line);
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 )
594 parseLet(line);
597 return readNextLine;
600 static void parseVimFile (const unsigned char *line)
602 boolean readNextLine = TRUE;
603 line = readVimLine();
605 while (line != NULL)
607 readNextLine = parseVimLine(line);
609 if ( readNextLine )
610 line = readVimLine();
615 static void findVimTags (void)
617 const unsigned char *line;
618 /* TODO - change this into a structure */
620 line = '\0';
622 parseVimFile (line);
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;
633 return def;
636 /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */