digitalmars.D.announce - a "go to definition" tool like visual studio for D
- yidabu (454/454) Jul 05 2007 Since D havenot a good IDE, How tedious to find the definition of window...
Since D havenot a good IDE, How tedious to find the definition of windows api? How to quickly determine which windows header must import? So I write this. code: /* a 'go to definition' tool fo D programming language Copyright: Placed into public domain. function: 1 auto open import module in scite 2 open and send D keywords to dhelp.chm (thanks Vladimir Panteleev, http://thecybershadow.net/d/docs/d.chm) 3 go to definition of identifier, like visual studio, and open the file in scite, go to the line of definition Author: yidabu, D china, http://bbs.yidabu.com/forum-10-1.html Date: 20070706 DMD version: 1.018 Dhelp version: 0.1 Thread: http://bbs.yidabu.com/thread-631-1.html (Chinese) compile: 1 download ini.d from http://www.dprogramming.com/ini.php (thanks Chris Miller) 2 download win32 header from http://www.prowiki.org/wiki4d/wiki.cgi?WindowsAPI 3 download dfl from http://www.dprogramming.com/dfl.php (thanks Chris Miller) 4 compile instruction: dmd dhelp.d shellapi.d -O -release useage: step1 first run dhelp.exe will create dhelp.ini and dkeys.txt, edit the config to fit you need. searchdir=directory to search definition searchexclude=regular expression pattern of excluded search path scite=fullpath of scite.exe pattern=regular expression pattern of search definition, except for 'searchword' dchm=fullpath of dchm.chm dkeys.txt = D and phobos keywords step2 add to d.properties of scite command.3.*.d=yourpath\dhelp.exe $(FilePath) $(CurrentWord) command.name.3.*.d=go to definition command.subsystem.3.*.d=2 command.shortcut.3.*.d=Ctrl+F1 command.save.before.3.*.d=1 step3 add to SciTEUser.properties of scite check.if.already.open=1 commandline: args[1] = fullpath of current D sourcr file args[2] = selected keyword exe version: http://down.yidabu.com/d/dhelp.exe todo: how to keep dhelp.chm window ? */ import ydb.ini; import std.file; import std.regexp; import std.string; import std.stream : File; import std.utf : toUTF16z; import win32.shellapi : ShellExecuteW; import win32.winuser; //for SW_SHOWMAXIMIZED import win32.winbase; //for FreeLibrary,LoadLibraryW import win32.winnt; //HANDLE import std.path; import std.c.stdio; //for getch(); import dfl.application; //for startupPath import std.typetuple; //dllimport import std.traits; //dllimport //for htmlhelp, from htmlhelp.h const uint HH_DISPLAY_TOPIC = 0x0000; const uint HH_HELP_FINDER = 0x0000; // WinHelp equivalent const uint HH_DISPLAY_TOC = 0x0001; const uint HH_DISPLAY_INDEX = 0x0002; const uint HH_DISPLAY_SEARCH = 0x0003; const uint HH_SET_WIN_TYPE = 0x0004; const uint HH_GET_WIN_TYPE = 0x0005; const uint HH_GET_WIN_HANDLE = 0x0006; const uint HH_ENUM_INFO_TYPE = 0x0007; // Get Info type name, call repeatedly to enumerate, -1 at end const uint HH_SET_INFO_TYPE = 0x0008; // Add Info type to filter. const uint HH_SYNC = 0x0009; const uint HH_DISPLAY_TEXT_POPUP = 0x000E; // display string resource id or text in a popup window const uint HH_HELP_CONTEXT = 0x000F; // display mapped numeric value in dwData const uint HH_TP_HELP_CONTEXTMENU = 0x0010; // text popup help, same as WinHelp HELP_CONTEXTMENU const uint HH_TP_HELP_WM_HELP = 0x0011; // text popup help, same as WinHelp HELP_WM_HELP pragma(lib,"ydb.lib"); //for ydb.ini, you can comment this and add ini.d to compile instruction pragma(lib,"dfl_debug.lib"); //for dfl.application int main(string[] args) { int result; try { assert(args.length > 2, "args length < 3, now exit"); Dhelp sf = new Dhelp(args[1], args[2]); result = sf.dhelp(); getch(); //for keep dhelp.chm window only, need a better solution } catch(Exception e) { printf("catch %.*s\n",e.msg); result = 1; } return result; } //=main class Dhelp { // from dhelp.ini string searchdir; //directory to search string searchexclude; //regular expression of excluded path string scite; //fullpath of scite.exe string dkeys; // //regular expression of search pattern string pattern = r"^\s*([\w\[\]*]+\s+)+([\w\[\]*]+\s*,\s*)*(searchword)\s*[:,{;=(]"; string dchm; //full path of D help chm string exedir; string inipath; string dkeyspath; //fullpath of dkeys.txt Ini ini; string filename; //current d source file full path; string searchword; //identifier to search string postfixword; //postfix of searchword this(string filename, string searchword) { dkeys = r" abstract alias align asm assert auto body bool break byte case cast catch cdouble cent cfloat char class const continue creal dchar debug default delegate delete deprecated do double else enum export extern false final finally float for foreach function goto idouble if ifloat import in inout int interface invariant ireal is lazy long macro mixin module new null out override package pragma private protected public real ref return scope short static struct super switch synchronized template this throw true try typedef typeid typeof ubyte ucent uint ulong union unittest ushort version void volatile wchar while with string wstring dstring loader path demangle invariant time utf etc switch format syserror dmain2 arraycat com bind testgc outofmemory typetuple zip ctype math2 random regexp signals fenv object asserterror traits memset linuxextern typeinfo trace boxer date string intrinsic locale stdlib stdio stdint windows moduleinit adi unittest socket deh2 switcherr internal phobos uni system gclinux arraycast qsort cpuid bitarray compiler conv std charset pthread aaA obj openrj gcstats zlib cast iunknown gc llmath array stdarg registry dateparse gcstub outbuffer perf cover gcold linux gcbits md5 winsock file stat math socketstream cmath2 win32 thread crc32 gcx alloca gamma uri aApplyR base64 stream process mmfile qsort2 stddef cstream metastrings opCmp opEquals factory find getHash equals compare swap encodeLength decodeLength Tuple append prepend BoundFunc opIndex init opAnd opOr opXor opSub opAndAssign opOrAssign opXorAssign opSubAssign opCatAssign unboxable boxArray boxArrayToArguments toInt setSourceDir setDestDir setMerge isalnum isalpha iscntrl isdigit islower ispunct isspace isupper isxdigit isgraph isprint isascii tolower toupper fetestexcept feraiseexcept feclearexcept fesetround fesetprec fegetenv fesetenv fegetexceptflag fesetexceptflag feholdexcept feupdateenv setlocale acos asin atan atan2 acosh asinh atanh exp exp2 expm1 frexp ilogb ldexp log10 log1p log2 modf scalbn scalbln cbrt fabs hypot pow sqrt erf erfc lgamma tgamma ceil floor nearbyint lrint llrint lround llround trunc fmod remainder remquo copysign nan nextafter nexttoward fdim fmax fmin isgreater isgreaterequal isless islessequal islessgreater isunordered tmpnam fopen _fsopen freopen fseek ftell fgets fgetc fflush fclose fputs fputc _fputchar ungetc fread fwrite fprintf vfprintf vprintf sprintf vsprintf scanf fscanf sscanf setbuf setvbuf remove rename perror fgetpos fsetpos getw putw ferror feof clearerr rewind fileno unlink fdopen filesize tempnam _wtmpnam fgetws fputws wprintf fwprintf vwprintf vfwprintf swprintf vswprintf wscanf fwscanf swscanf fgetwc fputwc ungetwc fwide div alloca calloc unsetenv atof itoa mblen memcpy memmove strcpy strncpy strncat strcoll strncmp strxfrm memchr strchr strcspn strpbrk strrchr strspn strstr strtok memset strerror strlen strcmp strcat memcmp memicmp parse toISO8601YearWeek YearFromTime inLeapYear MonthFromTime DateFromTime WeekDay UTCtoLocalTime LocalTimetoUTC toString toUTCString toDateString toTimeString toDtime toDosFileTime demangle getSize getTimes exists getAttributes isfile isdir chdir mkdir rmdir listdir toMBSz addRoot removeRoot addRange removeRange hasPointers hasNoPointers setTypeInfo malloc realloc extend capacity setGCHandle bsf bsr bt btc btr bts bswap inp outp conj rndtol rndtonl isnan isfinite isnormal issubnormal isinf signbit feqrel poly sum printDigest digestToString finish opSlice opIndexAssign getField findField hasField getRecordsContainingField reserve fill0 alignSize spread getExt getName getBaseName getDirName getDrive defaultExt addExt isabs join fncharmatch fnmatch expandTilde system execv rfind split search opCall replace replaceOld emit connect disconnect getProtocolByName getProtocolByType getServiceByName getHostByName getHostByAddr isSet bind listen shutdown send sendTo receive receiveFrom getOption setOption select readBlock writeBlock writef writefln fwritef fwritefln readln readExact readString readStringW vreadf writeExact writeLine writeLineW writeString writeStringW copyFrom seekSet position source create readBOM fixBO fixBlockBO writeBOM iswhite atoi toStringz capitalize capwords repeat splitlines stripl chomp chop ljustify zfill replaceSlice insert count expandtabs entab maketrans translate format sformat inPattern countchars removechars squeeze succ isNumeric soundex abbrev column wrap isEmail isURL wait setPriority isUniLower isUniUpper toUniLower toUniUpper isUniAlpha decodeComponent encodeComponent isValidDchar stride toUCSindex toUTFindex validate toUTF8 toUTF16 toUTF32 fromMBSz addMember deleteMember adler32 crc32 compress uncompress "; exedir = Application.startupPath(); //dfl.application. inipath = exedir ~ r"\dhelp.ini"; dkeyspath = exedir ~ r"\dkeys.txt"; if (std.file.exists(dkeyspath)) { dkeys = cast(string) std.file.read(dkeyspath); } else { std.file.write(dkeyspath, dkeys); } string t; //temp ini = new Ini(inipath); if (ini["config"] is null) ini.addSection("config"); if ( ini["config"]["searchdir"] is null ) ini["config"]["searchdir"] = ""; if ( ini["config"]["searchexclude"] is null ) ini["config"]["searchexclude"] = ""; if ( ini["config"]["scite"] is null ) ini["config"]["scite"] = ""; t = ini["config"]["pattern"]; if (t is null || t.length < 10 ) ini["config"]["pattern"] = pattern; if ( ini["config"]["dchm"] is null ) ini["config"]["dchm"] = ""; ini.save(); searchdir = ini["config"]["searchdir"]; assert( searchdir && searchdir.length > 2 && std.file.exists(searchdir), "searchdir not exists" ); t = ini["config"]["searchexclude"]; if ( t && t.length > 2 ) searchexclude = t; scite = ini["config"]["scite"]; assert(scite && scite.length > 2 && std.file.exists(scite), "scite not exists"); t = ini["config"]["pattern"]; if ( t && t.length > 10 ) pattern = t; t = ini["config"]["dchm"]; if (t && t.length > 5 && std.file.exists(t)) dchm = t; assert(filename && std.file.exists(filename), "not exists" ~ filename); assert(searchword && searchword.length > 2, "searchword length < 3"); if (std.string.find(searchword,".") == 0) searchword = searchword[1..$]; //.word replace to word this.filename = filename; this.searchword = searchword; auto m = std.regexp.search(searchword, r"\w+$"); assert(m,"wrong searchword " ~ searchword); postfixword = m.match(0); printf("filename %.*s \n",filename); printf("searchword %.*s \n",searchword); //~ printf("postfix of searchword %.*s \n",postfixword); } this(){} ~this() { delete ini; } int dhelp() { int result; string src; //source of file string searchpath; //path of import module string pattern2; //regular expression pattern of searchword try { //if a import module if ( std.string.find(searchword,".") > 0 ) { src = cast(string) std.file.read(filename); string p = r"^\s*import.*?[\W]" ~ std.string.replace(searchword,".",r"\.") ~ r"\s*[,:;]"; if (std.regexp.search(src, p, "mig")) { searchpath = std.string.replace(searchword, ".",r"\"); } } //if dhelp.chm exists, check if searchword is a d keyword if (dchm && !searchpath) { DllImport!("hhctrl.ocx", "HtmlHelpW", void* function(void* hwndCaller, wchar* pszFile, uint uCommand, uint dwData)) HtmlHelp; //if a d keywords, if (std.regexp.search(dkeys, r"\s" ~ postfixword ~ r"\s")) { //open dhelp.chm //how to keep the window of dhelp.chm? HtmlHelp(cast(void*)0,toUTF16z(dchm), HH_DISPLAY_INDEX , cast(uint)toUTF16z(postfixword)); debug printf("a D keyword %.*s \n", postfixword); return 0; } } bool callback(DirEntry* de) { if (de.isdir) { if ( searchexclude && std.regexp.search(de.name, searchexclude,"i") ) { return true; } std.file.listdir(de.name, &callback); } else { if ( std.string.icmp(getExt(de.name),"d") ) { return true; // .d file only } if ( searchpath ) //if searchword is a import module { if (std.string.ifind(de.name, searchpath ~ ".d") != -1 )//open import file { string dirname = std.path.getDirName(de.name); string basename = std.path.getBaseName(de.name); ShellExecuteW(null, toUTF16z("open"), toUTF16z(scite), toUTF16z(" -open:" ~ basename), toUTF16z(dirname), SW_SHOWMAXIMIZED); return false; } else return true;//continue search } pattern2 = std.string.replace( pattern,"searchword",postfixword); int i; //line index std.stream.File src = new std.stream.File(de.name); do { i++; if (auto m = std.regexp.search(src.readLine(), pattern2)) { string dirname = std.path.getDirName(de.name); string basename = std.path.getBaseName(de.name); ShellExecuteW(null, toUTF16z("open"), toUTF16z(scite), toUTF16z("-open:" ~ basename ~ " -goto:" ~ std.string.toString(i)), toUTF16z(dirname), SW_SHOWMAXIMIZED); printf("%.*s %.*s %d \n", de.name, m.match(0), i); break; } }while(!src.eof); }//de.isfile return true; }//callback std.file.listdir(searchdir, &callback); }//try catch(Exception e) { printf("dhelp catch %.*s\n", e.msg); result = 1; } finally { printf("end \n"); } return result; } //=dhelp unittest{ printf("dhelp unittest begin \n"); Dhelp sf = new Dhelp(); string pattern; void test(string s, string searchword, bool direction = true) { if (std.string.find(searchword,".") >= 0) return; //not test module here string pattern = std.string.replace(sf.pattern, "searchword", searchword); //~ debug printf("pattern %.*s \n",pattern); auto m = std.regexp.search(s,pattern); if (!direction) { assert(!m, searchword); printf("ok, not matched %.*s \n", searchword); } else { assert(m && (m.match(3) == searchword), searchword); printf("ok, matched %.*s \n", m.match(1)); } } test("DWORD numread;", "numread"); test(" const FVIRTKEY = 1;", "FVIRTKEY"); test(" template MAKEINTRESOURCE_T (WORD i)", "MAKEINTRESOURCE_T"); test(" const CREATEPROCESS_MANIFEST_RESOURCE_ID = MAKEINTRESOURCE_T!(1);", "CREATEPROCESS_MANIFEST_RESOURCE_ID"); test("struct TRACKMOUSEEVENT {", "TRACKMOUSEEVENT"); test("alias TRACKMOUSEEVENT* LPTRACKMOUSEEVENT;", "LPTRACKMOUSEEVENT"); test("alias MEASUREITEMSTRUCT* PMEASUREITEMSTRUCT, LPMEASUREITEMSTRUCT;", "PMEASUREITEMSTRUCT"); test("alias MEASUREITEMSTRUCT* PMEASUREITEMSTRUCT, LPMEASUREITEMSTRUCT;", "LPMEASUREITEMSTRUCT"); test("HWND CreateDialogA(HINSTANCE h, LPCSTR n, HWND w, DLGPROC f)", "CreateDialogA"); test("interface IEnumFORMATETC : public IUnknown {", "IEnumFORMATETC"); test("struct BYTE_SIZEDARR {", "BYTE_SIZEDARR"); test("struct GUID { ", "GUID"); test("PMEASUREITEMSTRUCT, LPMEASUREITEMSTRUCT;", "LPMEASUREITEMSTRUCT", false); test("result.lenght = MultiByteToWideChar(", "MultiByteToWideChar", false); test(" ERROR_DS_DUP_SCHEMA_ID_GUID,", "GUID", false); test(" char[] getGuid (),", "Guid", false); test("struct BYTE_SIZEDARR {", "BYTE", false); test("interface IEnumFORMATETC : public IUnknown {", "IUnknown",false); test("extern IID IID_IGlobalInterfaceTable;", "IID", false); test("int DlgDirListA(HWND, LPSTR, int, int, UINT);", "UINT", false); test("HWND CreateDialogA(HINSTANCE h, LPCSTR n, HWND w, DLGPROC f)", "HINSTANCE", false); printf("dhelp unittest end \n\n"); }//unittest } //Dhelp //dynamic load dll //by oldrev private static class ModuleManager { private static HANDLE[string] m_modules; private this() {} static public ~this() { foreach(h; m_modules) { FreeLibrary(h);//winbase } } private static HANDLE registerModule(string name) { string lname = tolower(name); HANDLE h = LoadLibraryW(toUTF16z(lname)); if(h is null) throw new Exception("Failed to load DLL: " ~ name); m_modules[lname] = h; return h; } public static HANDLE getHandle(string name) { return m_modules[name]; } public static ProcType getSymbol(ProcType)(string moduleName, string procName) { HANDLE handle = null; //winnt if(moduleName in m_modules) handle = m_modules[moduleName]; else handle = registerModule(moduleName); assert(handle !is null); ProcType proc = cast(ProcType)GetProcAddress(handle, toStringz(procName));//winbase if(proc is null) throw new Exception("Cannot to get the address of " ~ procName); return proc; } } struct DllImport(string ModuleName, string ProcName, FT) { extern(Windows) alias ReturnType!(FT) function(ParameterTypeTuple!(FT)) FunctionType; alias DllImport!(ModuleName, ProcName, FT) SelfType; //FIXME: avoid the CTFE? private FunctionType m_funcPtr = null; public ReturnType!(FunctionType) opCall(ParameterTypeTuple!(FunctionType) args) { if(m_funcPtr is null) m_funcPtr = ModuleManager.getSymbol!(FunctionType)(ModuleName, ProcName); return m_funcPtr(args); } } -- yidabu <yidabu.nospam gmail.com> D China: http://bbs.yidabu.com/forum-10-1.html
Jul 05 2007