发新话题
打印

D语言一键打开标识符identifier所在文件

D语言一键打开标识符identifier所在文件

知识若不分享 实在没有意义 http://www.d-programming-language-china.org 20070703

点击下面网址查看原文:
http://bbs.d-programming-language-china.org/thread-631-1.html

D语言一键打开标识符identifier所在文件代码

d-programming-language-china.org 下载了D语言关于windows api的头文件:
http://www.prowiki.org/wiki4d/wiki.cgi?WindowsAPI

然后打算把一段C++的windows程序翻译为D格式,但windows api函数定义在哪个头文件里呢?逐个去手工搜索是很讨厌的事情。
于是立即写了一个D程序来自动搜索定义identifer所在的文件。

/*
Author: yidabu, D china, http://www.d-programming-language-china.org
Date:    20070706
DMD version:    1.018
Dhelp version:    0.1
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

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);
    }
}

实际应用

把光标置于要D源文件某处,然后按预定义的快捷键:
如果是导入模块,就自动在scite打开对应的文件
如果是d的关键词,就自动打开dhelp.chm
否则就go to definition file

现在 http://bbs.d-programming-language-china.org 写windows api相关程序就方便多了,找一段参考c++代码,用dhelp.exe快速查出要导入的header,再转换成D语法。
或者在写程序时要查看定义出处,或者要查帮助文档,都是非常方便。

( lastupdate:20070706 最新文章请访问http://www.d-programming-language-china.org )

关于一大步成功社区:
yidabu提倡在交流中学习,在分享中提高
收集感兴趣的知识,写下心得,通过网络与别人一起分享
理解一点就实践一步,收获什么就分享什么,成功就是这样一点点一步步累积起来的
网络只是一个工具,只有自己身心提高才是实实在在的。d-programming-language-china.org为大家提供一个学习交流各种知识的平台

TOP

这个网站的广告真是要命哦

TOP

发新话题