📄 tags.c
字号:
static const char CVSID[] = "$Id: tags.c,v 1.51.2.1 2003/07/31 22:35:17 n8gray Exp $";/******************************************************************************** ** tags.c -- Nirvana editor tag file handling ** ** Copyright (C) 1999 Mark Edel ** ** This is free software; you can redistribute it and/or modify it under the ** terms of the GNU General Public License as published by the Free Software ** Foundation; either version 2 of the License, or (at your option) any later ** version. ** ** This software is distributed in the hope that it will be useful, but WITHOUT ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ** for more details. ** ** You should have received a copy of the GNU General Public License along with ** software; if not, write to the Free Software Foundation, Inc., 59 Temple ** Place, Suite 330, Boston, MA 02111-1307 USA ** ** Nirvana Text Editor ** July, 1993 ** ** Written by Mark Edel ** ********************************************************************************/#ifdef HAVE_CONFIG_H#include "../config.h"#endif#include "tags.h"#include "textBuf.h"#include "text.h"#include "nedit.h"#include "window.h"#include "file.h"#include "preferences.h"#include "search.h"#include "selection.h"#include "calltips.h"#include "../util/DialogF.h"#include "../util/fileUtils.h"#include "../util/misc.h"#include "../util/utils.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#ifdef VMS#include "../util/VMSparam.h"#else#ifndef __MVS__#include <sys/param.h>#endif#endif /*VMS*/#include <Xm/PrimitiveP.h> /* For Calltips */#include <Xm/Xm.h>#include <Xm/SelectioB.h>#include <X11/Xatom.h>#ifdef HAVE_DEBUG_H#include "../debug.h"#endif#define MAXLINE 2048#define MAX_TAG_LEN 256#define MAXDUPTAGS 100#define MAX_TAG_INCLUDE_RECURSION_LEVEL 5 /* Take this many lines when making a tip from a tag. (should probably be a language-dependent option, but...) */#define TIP_DEFAULT_LINES 4#define STRSAVE(a) ((a!=NULL)?strcpy(malloc(strlen(a)+1),(a)):strcpy(malloc(1),""))typedef struct _tag { struct _tag *next; const char *path; const char *name; const char *file; int language; const char *searchString; /* see comment below */ int posInf; /* see comment below */ short index;} tag;/*** contents of tag->searchString | tag->posInf** ctags, line num specified: "" | line num** ctags, search expr specfd: ctags search expr | -1** etags (emacs tags) etags search string | search start pos*/enum searchDirection {FORWARD, BACKWARD};static int loadTagsFile(const char *tagSpec, int index, int recLevel);static void findDefCB(Widget widget, WindowInfo *window, Atom *sel, Atom *type, char *value, int *length, int *format);static void setTag(tag *t, const char *name, const char *file, int language, const char *searchString, int posInf, const char * tag);static int fakeRegExSearch(WindowInfo *window, char *buffer, const char *searchString, int *startPos, int *endPos);static unsigned hashAddr(const char *key);static void updateMenuItems();static int addTag(const char *name, const char *file, int lang, const char *search, int posInf, const char *path, int index);static int delTag(const char *name, const char *file, int lang, const char *search, int posInf, int index);static tag *getTag(const char *name, int search_type);static int findDef(WindowInfo *window, const char *value, int search_type);static int findAllMatches(WindowInfo *window, const char *string);static void findAllCB(Widget parent, XtPointer client_data, XtPointer call_data);static Widget createSelectMenu(Widget parent, char *label, int nArgs, char *args[]);static void editTaggedLocation( Widget parent, int i );static void showMatchingCalltip( Widget parent, int i );static const char *rcs_strdup(const char *str);static void rcs_free(const char *str);static int searchLine(char *line, const char *regex);static void rstrip( char *dst, const char *src );static int nextTFBlock(FILE *fp, char *header, char **tiptext, int *lineAt, int *lineNo);static int loadTipsFile(const char *tipsFile, int index, int recLevel);/* Hash table of tags, implemented as an array. Each bin contains a NULL-terminated linked list of parsed tags */static tag **Tags = NULL;static int DefTagHashSize = 10000;/* list of loaded tags files */tagFile *TagsFileList = NULL;/* Hash table of calltip tags */static tag **Tips = NULL;tagFile *TipsFileList = NULL;/* These are all transient global variables -- they don't hold any state between tag/tip lookups */static int searchMode = TAG;static const char *tagName;static char tagFiles[MAXDUPTAGS][MAXPATHLEN];static char tagSearch[MAXDUPTAGS][MAXPATHLEN];static int tagPosInf[MAXDUPTAGS];static Boolean globAnchored;static int globPos;static int globHAlign;static int globVAlign;static int globAlignMode;/* A wrapper for calling TextDShowCalltip */static int tagsShowCalltip( WindowInfo *window, char *text ) { if (text) return ShowCalltip( window, text, globAnchored, globPos, globHAlign, globVAlign, globAlignMode); else return 0;}/* Set the head of the proper file list (Tags or Tips) to t */static tagFile *setFileListHead(tagFile *t, int file_type ) { if (file_type == TAG) TagsFileList = t; else TipsFileList = t; return t;}/* Compute hash address from a string key */static unsigned hashAddr(const char *key){ unsigned s=strlen(key); unsigned a=0,x=0,i; for (i=0; (i+3)<s; i += 4) { strncpy((char*)&a,&key[i],4); x += a; } for (a=1; i<(s+1); i++, a *= 256) x += key[i] * a; return x;}/* Retrieve a tag structure from the hash table */static tag *getTag(const char *name, int search_type){ static char lastName[MAXLINE]; static tag *t; static int addr; tag **table; if (search_type == TIP) table = Tips; else table = Tags; if (table == NULL) return NULL; if (name) { addr = hashAddr(name) % DefTagHashSize; t = table[addr]; strcpy(lastName,name); } else if (t) { name = lastName; t = t->next; } else return NULL; for (;t; t = t->next) if (!strcmp(name,t->name)) return t; return NULL;}/* Add a tag specification to the hash table ** Return Value: 0 ... tag already existing, spec not added** 1 ... tag spec is new, added.** (We don't return boolean as the return value is used as counter increment!)***/static int addTag(const char *name, const char *file, int lang, const char *search, int posInf, const char *path, int index){ int addr = hashAddr(name) % DefTagHashSize; tag *t; char newfile[MAXPATHLEN]; tag **table; if (searchMode == TIP) { if (Tips == NULL) Tips = (tag **)calloc(DefTagHashSize, sizeof(tag*)); table = Tips; } else { if (Tags == NULL) Tags = (tag **)calloc(DefTagHashSize, sizeof(tag*)); table = Tags; } if (*file == '/') strcpy(newfile,file); else sprintf(newfile,"%s%s", path, file); NormalizePathname(newfile); CompressPathname(newfile); for (t = table[addr]; t; t = t->next) { if (strcmp(name,t->name)) continue; if (lang != t->language) continue; if (strcmp(search,t->searchString)) continue; if (posInf != t->posInf) continue; if (*t->file == '/' && strcmp(newfile,t->file)) continue; if (*t->file != '/') { char tmpfile[MAXPATHLEN]; sprintf(tmpfile, "%s%s", t->path, t->file); NormalizePathname(tmpfile); CompressPathname(tmpfile); if (strcmp(newfile, tmpfile)) continue; } return 0; } t = (tag *) malloc(sizeof(tag)); setTag(t, name, file, lang, search, posInf, path); t->index = index; t->next = table[addr]; table[addr] = t; return 1;}/* Delete a tag from the cache. * Search is limited to valid matches of 'name','file', 'search', posInf, and 'index'. * EX: delete all tags matching index 2 ==> * delTag(tagname,NULL,-2,NULL,-2,2); * (posInf = -2 is an invalid match, posInf range: -1 .. +MAXINT, lang = -2 is also an invalid match) */static int delTag(const char *name, const char *file, int lang, const char *search, int posInf, int index){ tag *t, *last; int start,finish,i,del=0; tag **table; if (searchMode == TIP) table = Tips; else table = Tags; if (table == NULL) return FALSE; if (name) start = finish = hashAddr(name) % DefTagHashSize; else { start = 0; finish = DefTagHashSize; } for (i = start; i<finish; i++) { for (last = NULL, t = table[i]; t; last = t, t = t?t->next:table[i]) { if (name && strcmp(name,t->name)) continue; if (index && index != t->index) continue; if (file && strcmp(file,t->file)) continue; if (lang >= PLAIN_LANGUAGE_MODE && lang != t->language) continue; if (search && strcmp(search,t->searchString)) continue; if (posInf == t->posInf) continue; if (last) last->next = t->next; else table[i] = t->next; rcs_free(t->name); rcs_free(t->file); rcs_free(t->searchString); rcs_free(t->path); free(t); t = NULL; del++; } } return del>0;}/* used in AddRelTagsFile and AddTagsFile */static int tagFileIndex = 0; /* ** AddRelTagsFile(): Rescan tagSpec for relative tag file specs ** (not starting with [/~]) and extend the tag files list if in** windowPath a tags file matching the relative spec has been found.*/int AddRelTagsFile(const char *tagSpec, const char *windowPath, int file_type) { tagFile *t; int added=0; struct stat statbuf; char *filename; char pathName[MAXPATHLEN]; char *tmptagSpec; tagFile *FileList; searchMode = file_type; if (searchMode == TAG) FileList = TagsFileList; else FileList = TipsFileList; tmptagSpec = (char *) malloc(strlen(tagSpec)+1); strcpy(tmptagSpec, tagSpec); for (filename = strtok(tmptagSpec, ":"); filename; filename = strtok(NULL, ":")){ if (*filename == '/' || *filename == '~') continue; if (windowPath && *windowPath) { strcpy(pathName, windowPath); } else { strcpy(pathName, GetCurrentDir()); } strcat(pathName, "/"); strcat(pathName, filename); NormalizePathname(pathName); CompressPathname(pathName); for (t = FileList; t && strcmp(t->filename, pathName); t = t->next); if (t) { added=1; continue; } if (stat(pathName, &statbuf) != 0) continue; t = (tagFile *) malloc(sizeof(tagFile)); t->filename = STRSAVE(pathName); t->loaded = 0; t->date = statbuf.st_mtime; t->index = ++tagFileIndex; t->next = FileList; FileList = setFileListHead(t, file_type); added=1; } free(tmptagSpec); updateMenuItems(); if (added) return TRUE; else return FALSE;} /* ** AddTagsFile(): Set up the the list of tag files to manage from a file spec.** The file spec comes from the X-Resource Nedit.tags: It can list multiple ** tags files, specified by separating them with colons. The .Xdefaults would ** look like this:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -