a "go to definition" tool like visual studio for D
yidabu
yidabu.nospam at gmail.com
Thu Jul 5 17:17:48 PDT 2007
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 at gmail.com>
D China:
http://bbs.yidabu.com/forum-10-1.html
More information about the Digitalmars-d-announce
mailing list