支持代码完成和函数提示
来自http://www.cppprog.com/2009/1111/176_4.html
VS的代码完成和函数提示功能是很值得称道的,它们可以极大地提高我们的编程效率(造成我现在写代码时往往只记住前四个字母,如果在对象后面点了小数点后不出现提示就会心慌意乱的说-_-),尽管有时也会失效。
做为IDE这个功能是绝对不能少D。即使你只打算做个编辑器,如果有这个功能那也是一大亮点啊~~(目前很多代码编辑器都没这个功能的说)。
关于函数提示的几个命令以SCI_CALLTIP作为前缀,这里只介绍我们即将使用的几个命令(更多命令见:http://scintilla.sourceforge.net/ScintillaDoc.html#CallTips)
- SCI_CALLTIPSHOW(int posStart, const char *definition) 显示提示。posStart表示显示位置,definition是显示的内容
- SCI_CALLTIPCANCEL 取消提示
- SCI_CALLTIPACTIVE 如果当前编辑器中有提示信息,返回1,否则返回0
- SCI_CALLTIPSETHLT(int highlightStart, int highlightEnd) 设置提示中的高亮位置,在VS里我们输入函数实参时函数提示会高亮当前输入的参数名。
在我们程序中加入提示的最佳时机是SCN_CHARADDED(见上一节)事件。当用户输入左圆括号'('时,取得括号左边的函数名,然后显示出该函数的完整定义。
下面的代码实现了CreateWindow和MoveWindow两个API的函数提示
- //我们要高亮的两个函数
- const size_t FUNCSIZE=2;
- char* g_szFuncList[FUNCSIZE]={ //函数名
- "CreateWindow(",
- "MoveWindow("
- };
- char* g_szFuncDesc[FUNCSIZE]={ //函数信息
- "HWND CreateWindow("
- "LPCTSTR lpClassName,"
- " LPCTSTR lpWindowName,"
- " DWORD dwStyle, "
- " int x,"
- " int y,"
- " int nWidth,"
- " int nHeight, "
- " HWND hWndParent,"
- " HMENU hMenu,"
- " HANDLE hInstance,"
- " PVOID lpParam"
- ")",
- "BOOL MoveWindow("
- "HWND hWnd,"
- " int X,"
- " int Y,"
- " int nWidth,"
- " int nHeight,"
- " BOOL bRepaint"
- ")"
- };
- void __fastcall TForm1::WndProc(Messages::TMessage &Message)
- {
- TForm::WndProc(Message);
- if(Message.Msg == WM_NOTIFY)
- {
- SCNotification* notify = (SCNotification*)Message.LParam;
- ...
- if(notify->nmhdr.code == SCN_CHARADDED)
- {
- ...
- // 函数提示功能
- static const char* pCallTipNextWord = NULL;//下一个高亮位置
- static const char* pCallTipCurDesc = NULL;//当前提示的函数信息
- if(notify->ch == '(') //如果输入了左括号,显示函数提示
- {
- char word[1000]; //保存当前光标下的单词(函数名)
- TextRange tr; //用于SCI_GETTEXTRANGE命令
- int pos = SendEditor(SCI_GETCURRENTPOS); //取得当前位置(括号的位置)
- int startpos = SendEditor(SCI_WORDSTARTPOSITION,pos-1);//当前单词起始位置
- int endpos = SendEditor(SCI_WORDENDPOSITION,pos-1);//当前单词终止位置
- tr.chrg.cpMin = startpos; //设定单词区间,取出单词
- tr.chrg.cpMax = endpos;
- tr.lpstrText = word;
- SendEditor(SCI_GETTEXTRANGE,0, sptr_t(&tr));
- for(size_t i=0; i<FUNCSIZE; i++) //找找有没有我们认识的函数?
- {
- if(memcmp(g_szFuncList[i],word,sizeof(g_szFuncList[i])) == 0)
- { //找到啦,那么显示提示吧
- pCallTipCurDesc = g_szFuncDesc[i]; //当前提示的函数信息
- SendEditor(SCI_CALLTIPSHOW,pos,sptr_t(pCallTipCurDesc));//显示这个提示
- const char *pStart = strchr(pCallTipCurDesc,'(')+1; //高亮第一个参数
- const char *pEnd = strchr(pStart,',');//参数列表以逗号分隔
- if(pEnd == NULL) pEnd = strchr(pStart,')');//若是最后一个参数,后面是右括号
- SendEditor(SCI_CALLTIPSETHLT,
- pStart-pCallTipCurDesc, pEnd-pCallTipCurDesc);
- pCallTipNextWord = pEnd+1;//指向下一参数位置
- break;
- }
- }
- }
- else if(notify->ch == ')') //如果输入右括号,就关闭函数提示
- {
- SendEditor(SCI_CALLTIPCANCEL);
- pCallTipCurDesc = NULL;
- pCallTipNextWord = NULL;
- }
- else if(notify->ch == ',' && SendEditor(SCI_CALLTIPACTIVE) && pCallTipCurDesc)
- {
- //输入的是逗号,高亮下一个参数
- const char *pStart = pCallTipNextWord;
- const char *pEnd = strchr(pStart,',');
- if(pEnd == NULL) pEnd = strchr(pStart,')');
- if(pEnd == NULL) //没有下一个参数啦,关闭提示
- SendEditor(SCI_CALLTIPCANCEL);
- else
- {
- SendEditor(SCI_CALLTIPSETHLT,pStart-pCallTipCurDesc, pEnd-pCallTipCurDesc);
- pCallTipNextWord = pEnd+1;
- }
- }
- }//if(notify->nmhdr.code == SCN_CHARADDED)
- ...
- }//if(Message.Msg == WM_NOTIFY)
- }
当然,这个提示功能相当山寨啦。比如函数名和括号之间有空格提示就不出来了,函数嵌套调用时只会提示最后一个函数的参数。关于如果改进,大家各显神通吧。
另外,还有一个提外话,在实际的使用中,我们的函数信息不可能象这里一样是写死的,而是依据用户的输入动态生成的函数名及信息列表。这就涉及到一个C++代码解析的问题(还好,只要解析函数声明就行了),对于这一点,牛X的同学可以自己写解析代码;次牛X的可以考虑用WAVE,Spirit,Regex等库帮助解析;象偶这种不牛的同学,则可以考虑利用CTAGS工具在后台线程中生成tag文件,我们从tag文件里取就可以了(当然,效率嘛~~可以参考C++Builder的函数提示效率-_-)。
代码完成和函数提示的用法类似,前缀是SCI_AUTOC,具体命令见:http://scintilla.sourceforge.net/ScintillaDoc.html#Autocompletion
直接上代码:
- void __fastcall TForm1::WndProc(Messages::TMessage &Message)
- {
- TForm::WndProc(Message);
- if(Message.Msg == WM_NOTIFY)
- {
- ...
- if(notify->nmhdr.code == SCN_CHARADDED)
- {
- ...
- if(notify->ch == '.')
- {
- char word[1000]; //保存当前光标下的单词
- TextRange tr; //用于SCI_GETTEXTRANGE命令
- int pos = SendEditor(SCI_GETCURRENTPOS); //取得当前位置
- int startpos = SendEditor(SCI_WORDSTARTPOSITION,pos-1);//当前单词起始位置
- int endpos = SendEditor(SCI_WORDENDPOSITION,pos-1);//当前单词终止位置
- tr.chrg.cpMin = startpos; //设定单词区间,取出单词
- tr.chrg.cpMax = endpos;
- tr.lpstrText = word;
- SendEditor(SCI_GETTEXTRANGE,0, sptr_t(&tr));
- if(strcmp(word,"file.") == 0) //输入file.后提示file对象的几个方法
- {
- SendEditor(SCI_AUTOCSHOW,0,
- sptr_t(
- "close "
- "eof "
- "good "
- "open "
- "rdbuf "
- "size"
- ));
- }
- }
- ...
SCI_AUTOCSHOW命令的第一个参数表示已经输入了多少个字符。这对于代码自动完成是很有帮助的,比如我们可以用它帮助用户输入长串的单词,如:
- if(strcmp(word,"Create") == 0)
- {
- SendEditor(SCI_AUTOCSHOW,6,//已经输入了6位字符
- sptr_t(
- "CreateBitmap "
- "CreateDC "
- "CreateHandle "
- "CreateWindow "
- "CreateWindowEx"
- ));
- }