completion.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. /*
  2. ktigcc - TIGCC IDE for KDE
  3. Copyright (C) 2006 Kevin Kofler
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software Foundation,
  14. Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include <qstring.h>
  17. #include <qvaluelist.h>
  18. #include <qpair.h>
  19. #include <qpoint.h>
  20. #include <qregexp.h>
  21. #include <qfileinfo.h>
  22. #include <qdir.h>
  23. #include <qapplication.h>
  24. #include <qwidget.h>
  25. #include <qwidgetlist.h>
  26. #include <qevent.h>
  27. #include <kmessagebox.h>
  28. #include <kate/view.h>
  29. #include <kate/document.h>
  30. #include <ktexteditor/editinterfaceext.h>
  31. #include <kconfig.h>
  32. #include "completion.h"
  33. #include "parsing.h"
  34. #include "preferences.h"
  35. #include "mainform.h"
  36. #include "tpr.h"
  37. // Maps file name to a CompletionInfo.
  38. QMap<QString,CompletionInfo> systemHeaderCompletion, projectCompletion;
  39. static void resetSearchedFlags(void)
  40. {
  41. for (QMap<QString,CompletionInfo>::Iterator it=projectCompletion.begin();
  42. it!=projectCompletion.end(); ++it)
  43. (*it).searched=false;
  44. for (QMap<QString,CompletionInfo>::Iterator it=systemHeaderCompletion.begin();
  45. it!=systemHeaderCompletion.end(); ++it)
  46. (*it).searched=false;
  47. }
  48. static void findSymbolInSystemHeaders(const QString &symbol,
  49. const QStringList &systemHeaders,
  50. QString &symbolFile,
  51. unsigned &symbolLine,
  52. bool &systemHeader)
  53. {
  54. for (QStringList::ConstIterator it=systemHeaders.begin();
  55. it!=systemHeaders.end(); ++it) {
  56. const QString &headerName=*it;
  57. // Avoid infinite recursion.
  58. if (systemHeaderCompletion.contains(headerName)
  59. && !systemHeaderCompletion[headerName].searched) {
  60. CompletionInfo &completionInfo=systemHeaderCompletion[headerName];
  61. completionInfo.searched=true;
  62. if (completionInfo.lineNumbers.contains(symbol)) {
  63. symbolFile=headerName;
  64. symbolLine=completionInfo.lineNumbers[symbol];
  65. systemHeader=true;
  66. return;
  67. } else {
  68. findSymbolInSystemHeaders(symbol,completionInfo.includedSystem,
  69. symbolFile,symbolLine,systemHeader);
  70. if (!symbolFile.isNull()) return;
  71. }
  72. }
  73. }
  74. }
  75. static bool findSymbolInFileRecursive(const QString &symbol,
  76. const QString &fileText,
  77. const QString &fileName,
  78. MainForm *mainForm,
  79. QString &symbolFile,
  80. unsigned &symbolLine,
  81. bool &systemHeader)
  82. {
  83. symbolFile=QString::null;
  84. systemHeader=false;
  85. if (!projectCompletion.contains(fileName) || projectCompletion[fileName].dirty) {
  86. QFileInfo fileInfo(fileName);
  87. QString pathInProject=fileInfo.isRelative()?fileInfo.dirPath():".";
  88. CompletionInfo completionInfo=parseFileCompletion(fileText,pathInProject);
  89. if (completionInfo.dirty) return false;
  90. projectCompletion.insert(fileName,completionInfo);
  91. }
  92. CompletionInfo &completionInfo=projectCompletion[fileName];
  93. // Avoid infinite recursion.
  94. if (completionInfo.searched) return true;
  95. completionInfo.searched=true;
  96. if (completionInfo.lineNumbers.contains(symbol)) {
  97. symbolFile=fileName;
  98. symbolLine=completionInfo.lineNumbers[symbol];
  99. return true;
  100. }
  101. for (QStringList::ConstIterator it=completionInfo.included.begin();
  102. it!=completionInfo.included.end(); ++it) {
  103. const QString &headerName=*it;
  104. QString headerText=mainForm->textForHeader(headerName);
  105. if (!headerText.isNull()) {
  106. if (!findSymbolInFile(symbol,headerText,headerName,mainForm,symbolFile,
  107. symbolLine,systemHeader))
  108. return false;
  109. if (!symbolFile.isNull()) return true;
  110. }
  111. }
  112. findSymbolInSystemHeaders(symbol,completionInfo.includedSystem,symbolFile,
  113. symbolLine,systemHeader);
  114. return true;
  115. }
  116. bool findSymbolInFile(const QString &symbol,
  117. const QString &fileText,
  118. const QString &fileName,
  119. MainForm *mainForm,
  120. QString &symbolFile,
  121. unsigned &symbolLine,
  122. bool &systemHeader)
  123. {
  124. resetSearchedFlags();
  125. return findSymbolInFileRecursive(symbol,fileText,fileName,mainForm,symbolFile,
  126. symbolLine,systemHeader);
  127. }
  128. static void mergeCompletionEntries(QValueList<KTextEditor::CompletionEntry> &dest,
  129. const QValueList<KTextEditor::CompletionEntry> &src)
  130. {
  131. for (QValueList<KTextEditor::CompletionEntry>::ConstIterator it=src.begin();
  132. it!=src.end(); ++it)
  133. dest.append(*it);
  134. }
  135. static void completionEntriesForSystemHeaders(const QStringList &systemHeaders,
  136. QValueList<KTextEditor::CompletionEntry> &result)
  137. {
  138. for (QStringList::ConstIterator it=systemHeaders.begin();
  139. it!=systemHeaders.end(); ++it) {
  140. const QString &headerName=*it;
  141. // Avoid infinite recursion.
  142. if (systemHeaderCompletion.contains(headerName)
  143. && !systemHeaderCompletion[headerName].searched) {
  144. CompletionInfo &completionInfo=systemHeaderCompletion[headerName];
  145. completionInfo.searched=true;
  146. mergeCompletionEntries(result,completionInfo.entries);
  147. completionEntriesForSystemHeaders(completionInfo.includedSystem,result);
  148. }
  149. }
  150. }
  151. static bool completionEntriesForFileRecursive(const QString &fileText,
  152. const QString &fileName,
  153. MainForm *mainForm,
  154. QValueList<KTextEditor::CompletionEntry> &result)
  155. {
  156. if (!projectCompletion.contains(fileName) || projectCompletion[fileName].dirty) {
  157. QFileInfo fileInfo(fileName);
  158. QString pathInProject=fileInfo.isRelative()?fileInfo.dirPath():".";
  159. CompletionInfo completionInfo=parseFileCompletion(fileText,pathInProject);
  160. if (completionInfo.dirty) return false;
  161. projectCompletion.insert(fileName,completionInfo);
  162. }
  163. CompletionInfo &completionInfo=projectCompletion[fileName];
  164. // Avoid infinite recursion.
  165. if (completionInfo.searched) return true;
  166. completionInfo.searched=true;
  167. mergeCompletionEntries(result,completionInfo.entries);
  168. completionEntriesForSystemHeaders(completionInfo.includedSystem,result);
  169. for (QStringList::ConstIterator it=completionInfo.included.begin();
  170. it!=completionInfo.included.end(); ++it) {
  171. const QString &headerName=*it;
  172. QString headerText=mainForm->textForHeader(headerName);
  173. if (!headerText.isNull())
  174. if (!completionEntriesForFile(headerText,headerName,mainForm,result))
  175. return false;
  176. }
  177. return true;
  178. }
  179. bool completionEntriesForFile(const QString &fileText,
  180. const QString &fileName,
  181. MainForm *mainForm,
  182. QValueList<KTextEditor::CompletionEntry> &result)
  183. {
  184. resetSearchedFlags();
  185. return completionEntriesForFileRecursive(fileText,fileName,mainForm,result);
  186. }
  187. static QValueList<KTextEditor::CompletionEntry> sortCompletionEntries(
  188. const QValueList<KTextEditor::CompletionEntry> &entries)
  189. {
  190. QMap<QString,QValueList<KTextEditor::CompletionEntry> > map;
  191. for (QValueList<KTextEditor::CompletionEntry>::ConstIterator it=entries.begin();
  192. it!=entries.end(); ++it) {
  193. const KTextEditor::CompletionEntry &entry=*it;
  194. QValueList<KTextEditor::CompletionEntry> &list=map[entry.text];
  195. if (list.find(entry)==list.end()) list.append(entry);
  196. }
  197. QValueList<KTextEditor::CompletionEntry> result;
  198. for (QMap<QString,QValueList<KTextEditor::CompletionEntry> >::ConstIterator
  199. it=map.begin(); it!=map.end(); ++it)
  200. mergeCompletionEntries(result,*it);
  201. return result;
  202. }
  203. static QStringList prototypesForIdentifier(const QString &identifier,
  204. const QValueList<KTextEditor::CompletionEntry> &entries)
  205. {
  206. QStringList result;
  207. for (QValueList<KTextEditor::CompletionEntry>::ConstIterator it=entries.begin();
  208. it!=entries.end(); ++it) {
  209. const KTextEditor::CompletionEntry &entry=*it;
  210. if (entry.text==identifier) {
  211. QString prototype=entry.prefix+' '+entry.text+entry.postfix;
  212. if (result.find(prototype)==result.end()) result.append(prototype);
  213. }
  214. }
  215. return result;
  216. }
  217. bool parseHelpSources(QWidget *parent, const QString &directory,
  218. QMap<QString,CompletionInfo> &sysHdrCompletion)
  219. {
  220. QDir qdir(directory);
  221. QStringList headers=qdir.entryList("*.h",QDir::Dirs);
  222. for (QStringList::ConstIterator it=headers.begin(); it!=headers.end(); ++it) {
  223. const QString &header=*it;
  224. CompletionInfo &completionInfo=sysHdrCompletion[header];
  225. QValueList<KTextEditor::CompletionEntry> &entries=completionInfo.entries;
  226. QDir hdrQdir(QFileInfo(qdir,header).filePath());
  227. QStringList hsfs=hdrQdir.entryList("*.hsf *.ref",QDir::Files);
  228. for (QStringList::ConstIterator it=hsfs.begin(); it!=hsfs.end(); ++it) {
  229. const QString &hsf=*it;
  230. QString fileText=loadFileText(QFileInfo(hdrQdir,hsf).filePath());
  231. if (fileText.isNull()) {
  232. KMessageBox::error(parent,QString("Can't open \'%1/%2\'.").arg(header)
  233. .arg(hsf));
  234. return false;
  235. }
  236. if (hsf.endsWith(".ref")) {
  237. QString realHeader=fileText.stripWhiteSpace();
  238. QDir realHdrQdir(QFileInfo(qdir,realHeader).filePath());
  239. QString realHsf=hsf;
  240. realHsf.replace(realHsf.length()-3,3,"hsf");
  241. fileText=loadFileText(QFileInfo(realHdrQdir,realHsf).filePath());
  242. if (fileText.isNull()) {
  243. KMessageBox::error(parent,QString("Can't open \'%1/%2\'.").arg(realHeader)
  244. .arg(realHsf));
  245. return false;
  246. }
  247. }
  248. KTextEditor::CompletionEntry entry;
  249. QStringList lines=QStringList::split('\n',fileText,TRUE);
  250. for (QStringList::ConstIterator it=lines.begin(); it!=lines.end(); ++it) {
  251. const QString &line=*it;
  252. if (line.startsWith("Name=")) {
  253. entry.text=line.mid(5);
  254. break;
  255. }
  256. }
  257. bool isType=false;
  258. for (QStringList::ConstIterator it=lines.begin(); it!=lines.end(); ++it) {
  259. const QString &line=*it;
  260. if (line.startsWith("Type=")) {
  261. QString hsfType=line.mid(5);
  262. if (hsfType=="Type") isType=true;
  263. entry.prefix=isType?"type"
  264. :(hsfType=="Function")?"func"
  265. :(hsfType=="Constant")?"const"
  266. :(hsfType=="Variable")?"var":hsfType;
  267. break;
  268. }
  269. }
  270. QRegExp comments("/\\*.*\\*/");
  271. comments.setMinimal(true);
  272. QString definition;
  273. for (QStringList::ConstIterator it=lines.begin(); it!=lines.end(); ++it) {
  274. const QString &line=*it;
  275. if (line.startsWith("Definition=")) {
  276. definition=line.mid(11);
  277. definition.remove(comments);
  278. int pos=definition.find(entry.text);
  279. QString left=(pos>=0)?definition.left(pos).stripWhiteSpace()
  280. :QString::null;
  281. QString right;
  282. if (left.startsWith("typedef")) {
  283. entry.postfix=left.mid(8).simplifyWhiteSpace();
  284. left=QString::null;
  285. } else if (left=="unknown_retval") left="?";
  286. else if (left=="#define") left=QString::null;
  287. if (!left.isEmpty()) {
  288. left.prepend(' ');
  289. entry.prefix+=left;
  290. }
  291. entry.postfix+=definition.mid(pos+entry.text.length()).simplifyWhiteSpace();
  292. break;
  293. }
  294. }
  295. QStringList::ConstIterator desc=lines.find("[Description]");
  296. QString description;
  297. if (desc!=lines.end() && ++desc!=lines.end()) description=*desc;
  298. description.remove(QRegExp("<A [^>]*>",FALSE)).remove("</A>",FALSE);
  299. if (description.isEmpty()) description=QString::null;
  300. entry.comment=description;
  301. if (isType) {
  302. for (QStringList::ConstIterator it=lines.begin(); it!=lines.end(); ++it) {
  303. const QString &line=*it;
  304. if (line.startsWith("Subtype=") || (line[0]=='[' && line!="[Main]")) {
  305. if (line=="Subtype=Enumeration") {
  306. int pos1=definition.find('{');
  307. if (pos1>=0) {
  308. QString left=definition.left(pos1).stripWhiteSpace();
  309. int pos2=definition.find('}',++pos1);
  310. if (pos2>=0) {
  311. QString itemList=definition.mid(pos1,pos2-pos1);
  312. if (itemList=="...") {
  313. for (QStringList::ConstIterator it=lines.begin(); it!=lines.end(); ++it) {
  314. const QString &line=*it;
  315. if (line.startsWith("Real Definition=")) {
  316. QString realDefinition=line.mid(16);
  317. realDefinition.remove(comments);
  318. pos1=realDefinition.find('{');
  319. if (pos1>=0) {
  320. left=realDefinition.left(pos1).stripWhiteSpace();
  321. pos2=realDefinition.find('}',++pos1);
  322. if (pos2>=0) {
  323. itemList=realDefinition.mid(pos1,pos2-pos1);
  324. goto foundDefinition;
  325. }
  326. }
  327. break;
  328. }
  329. }
  330. } else {
  331. foundDefinition:
  332. QStringList enumItems=QStringList::split(',',itemList);
  333. for (QStringList::ConstIterator it=enumItems.begin();
  334. it!=enumItems.end(); ++it) {
  335. const QString &enumItem=*it;
  336. KTextEditor::CompletionEntry enumEntry;
  337. int pos=enumItem.find('=');
  338. if (pos>=0) {
  339. enumEntry.text=enumItem.left(pos).stripWhiteSpace();
  340. enumEntry.postfix=enumItem.mid(pos+1).stripWhiteSpace();
  341. } else enumEntry.text=enumItem.stripWhiteSpace();
  342. enumEntry.prefix=left;
  343. enumEntry.comment=description;
  344. entries.append(enumEntry);
  345. }
  346. }
  347. }
  348. }
  349. }
  350. break;
  351. }
  352. }
  353. }
  354. if (entry.text.stripWhiteSpace().isEmpty()) {
  355. // No function name, so use HSF name. Can happen for _ROM_CALL_*.
  356. if (!hsf.startsWith("_ROM_CALL_"))
  357. KMessageBox::sorry(parent,QString("No name found in %1/%2").arg(header)
  358. .arg(hsf),
  359. "Warning");
  360. entry.text=hsf.left(hsf.length()-4);
  361. }
  362. entries.append(entry);
  363. }
  364. }
  365. return true;
  366. }
  367. bool parseSystemHeaders(QWidget *parent, const QString &directory,
  368. QMap<QString,CompletionInfo> &sysHdrCompletion)
  369. {
  370. QDir qdir(directory);
  371. QStringList headers=qdir.entryList("*.h",QDir::Files);
  372. for (QStringList::ConstIterator it=headers.begin(); it!=headers.end(); ++it) {
  373. const QString &header=*it;
  374. QString fileText=loadFileText(QFileInfo(qdir,header).filePath());
  375. if (fileText.isNull()) {
  376. KMessageBox::error(parent,QString("Can't open \'%1\'.").arg(header));
  377. return false;
  378. }
  379. sysHdrCompletion[header]=parseFileCompletion(fileText,QString::null,
  380. sysHdrCompletion[header]);
  381. if (sysHdrCompletion[header].dirty) return false;
  382. }
  383. return true;
  384. }
  385. void loadSystemHeaderCompletion(void)
  386. {
  387. KConfig config("ktigcc/completion",true,false,"data");
  388. QStringList groupList=config.groupList();
  389. if (groupList.isEmpty()) {
  390. KMessageBox::queuedMessageBox(0,KMessageBox::Sorry,
  391. "<p>No completion data found for TIGCCLIB headers. KTIGCC will not be "
  392. "able to show completion entries for system headers. You have 2 options "
  393. "to fix this:</p>"
  394. "<p>1. Download ktigcc-completion-data from "
  395. "<a href=\"http://sourceforge.net/project/showfiles.php?group_id=31034"
  396. "&amp;package_id=200501\">http://sourceforge.net/project/showfiles.php?"
  397. "group_id=31034&amp;package_id=200501</a> (recommended).</p>"
  398. "<p>2. Regenerate the data yourself through File/Preferences/Coding "
  399. "(TIGCC source code required).</p>","No Completion Data",
  400. KMessageBox::Notify|KMessageBox::AllowLink);
  401. }
  402. systemHeaderCompletion.clear();
  403. for (QStringList::ConstIterator it=groupList.begin(); it!=groupList.end(); ++it) {
  404. const QString &key=*it;
  405. if (key.endsWith(" Lines")) continue;
  406. CompletionInfo completionInfo;
  407. config.setGroup(key);
  408. completionInfo.includedSystem=config.readListEntry("Included");
  409. unsigned numEntries=config.readUnsignedNumEntry("Num Entries");
  410. for (unsigned i=0; i<numEntries; i++) {
  411. KTextEditor::CompletionEntry entry;
  412. entry.type=config.readEntry(QString("Entry %1 Type").arg(i));
  413. entry.text=config.readEntry(QString("Entry %1 Text").arg(i));
  414. entry.prefix=config.readEntry(QString("Entry %1 Prefix").arg(i));
  415. entry.postfix=config.readEntry(QString("Entry %1 Postfix").arg(i));
  416. entry.comment=config.readEntry(QString("Entry %1 Comment").arg(i));
  417. entry.userdata=config.readEntry(QString("Entry %1 User Data").arg(i));
  418. completionInfo.entries.append(entry);
  419. }
  420. QMap<QString,QString> entryMap=config.entryMap(key+" Lines");
  421. for (QMap<QString,QString>::ConstIterator it=entryMap.begin();
  422. it!=entryMap.end(); ++it)
  423. completionInfo.lineNumbers.insert(it.key(),(*it).toUInt());
  424. systemHeaderCompletion.insert(key,completionInfo);
  425. }
  426. }
  427. void saveSystemHeaderCompletion(void)
  428. {
  429. KConfig config("ktigcc/completion",false,false,"data");
  430. for (QMap<QString,CompletionInfo>::ConstIterator it=systemHeaderCompletion.begin();
  431. it!=systemHeaderCompletion.end(); ++it) {
  432. const QString &key=it.key();
  433. const CompletionInfo &completionInfo=*it;
  434. config.setGroup(key);
  435. config.writeEntry("Included",completionInfo.includedSystem);
  436. unsigned i=0;
  437. for (QValueList<KTextEditor::CompletionEntry>::ConstIterator it
  438. =completionInfo.entries.begin(); it!=completionInfo.entries.end();
  439. ++it, i++) {
  440. const KTextEditor::CompletionEntry &entry=*it;
  441. config.writeEntry(QString("Entry %1 Type").arg(i),entry.type);
  442. config.writeEntry(QString("Entry %1 Text").arg(i),entry.text);
  443. config.writeEntry(QString("Entry %1 Prefix").arg(i),entry.prefix);
  444. config.writeEntry(QString("Entry %1 Postfix").arg(i),entry.postfix);
  445. config.writeEntry(QString("Entry %1 Comment").arg(i),entry.comment);
  446. config.writeEntry(QString("Entry %1 User Data").arg(i),entry.userdata);
  447. }
  448. config.writeEntry("Num Entries",i);
  449. config.setGroup(key+" Lines");
  450. for (QMap<QString,unsigned>::ConstIterator it=completionInfo.lineNumbers.begin();
  451. it!=completionInfo.lineNumbers.end(); ++it)
  452. config.writeEntry(it.key(),*it);
  453. }
  454. config.sync();
  455. }
  456. TemplatePopup::TemplatePopup(Kate::View *parent)
  457. : QPopupMenu(parent), view(parent)
  458. {
  459. connect(this,SIGNAL(activated(int)),this,SLOT(QPopupMenu_activated(int)));
  460. unsigned i=0;
  461. for (QValueList<QPair<QString,QString> >::ConstIterator it=preferences.templates.begin();
  462. it!=preferences.templates.end(); ++it, i++)
  463. insertItem((*it).first,i);
  464. QPoint pos=parent->cursorCoordinates();
  465. if (pos.x()<0 || pos.y()<0) {
  466. // Cursor outside of the view, so center on view instead.
  467. QSize parentSize=parent->size();
  468. QSize popupSize=sizeHint();
  469. pos.setX((parentSize.width()-popupSize.width())>>1);
  470. pos.setY((parentSize.height()-popupSize.height())>>1);
  471. }
  472. exec(parent->mapToGlobal(pos));
  473. deleteLater();
  474. }
  475. void TemplatePopup::QPopupMenu_activated(int id)
  476. {
  477. QString code=preferences.templates[id].second;
  478. QString indent=view->currentTextLine();
  479. // Remove everything starting from the first non-whitespace character.
  480. indent=indent.remove(QRegExp("(?!\\s).*$"));
  481. indent.prepend('\n');
  482. code.replace('\n',indent);
  483. int cursorPos=code.find('|');
  484. if (cursorPos>=0) {
  485. QString left=code.left(cursorPos);
  486. QString right=code.mid(cursorPos+1);
  487. unsigned row, col;
  488. KTextEditor::EditInterfaceExt *editExt=KTextEditor::editInterfaceExt(view->getDoc());
  489. editExt->editBegin();
  490. view->insertText(left);
  491. view->cursorPositionReal(&row,&col);
  492. view->insertText(right);
  493. editExt->editEnd();
  494. view->setCursorPositionReal(row,col);
  495. } else view->insertText(code);
  496. }
  497. CompletionPopup::CompletionPopup(Kate::View *parent, const QString &fileName,
  498. MainForm *mainForm, QObject *receiver)
  499. : QObject(parent), done(false), completionPopup(0)
  500. {
  501. connect(this,SIGNAL(closed()),receiver,SLOT(completionPopup_closed()));
  502. QValueList<KTextEditor::CompletionEntry> entries;
  503. if (!completionEntriesForFile(parent->getDoc()->text(),fileName,mainForm,
  504. entries)) {
  505. emit closed();
  506. deleteLater();
  507. return;
  508. }
  509. entries=sortCompletionEntries(entries);
  510. unsigned column=parent->cursorColumnReal();
  511. int offset=0;
  512. if (column) {
  513. QString textLine=parent->currentTextLine();
  514. if (column<=textLine.length()) {
  515. while (column && (textLine[--column].isLetterOrNumber()
  516. || textLine[column]=='_' || textLine[column]=='$'))
  517. offset++;
  518. }
  519. }
  520. connect(parent,SIGNAL(completionAborted()),this,SLOT(slotDone()));
  521. connect(parent,SIGNAL(completionDone()),this,SLOT(slotDone()));
  522. parent->showCompletionBox(entries,offset);
  523. // Unfortunately, Kate doesn't always send the completionAborted or
  524. // completionDone event when it closes its popup. Work around that.
  525. QWidgetList *list=QApplication::topLevelWidgets();
  526. QWidgetListIt it(*list);
  527. while (QWidget *w=it.current()) {
  528. ++it;
  529. if (w->isVisible() && w->testWFlags(Qt::WType_Popup)) {
  530. completionPopup=w;
  531. break;
  532. }
  533. }
  534. delete list;
  535. if (completionPopup)
  536. completionPopup->installEventFilter(this);
  537. }
  538. void CompletionPopup::slotDone()
  539. {
  540. if (!done) {
  541. done=true;
  542. emit closed();
  543. deleteLater();
  544. }
  545. }
  546. bool CompletionPopup::eventFilter(QObject *o, QEvent *e)
  547. {
  548. if (!done && o==completionPopup && e->type()==QEvent::Hide) {
  549. done=true;
  550. emit closed();
  551. deleteLater();
  552. }
  553. return false;
  554. }
  555. ArgHintPopup::ArgHintPopup(Kate::View *parent, const QString &fileName,
  556. MainForm *mainForm)
  557. : QObject(parent), done(false), argHintPopup(0)
  558. {
  559. QValueList<KTextEditor::CompletionEntry> entries;
  560. if (!completionEntriesForFile(parent->getDoc()->text(),fileName,mainForm,
  561. entries)) {
  562. nothingFound:
  563. deleteLater();
  564. return;
  565. }
  566. unsigned column=parent->cursorColumnReal();
  567. if (!column || !--column) goto nothingFound;
  568. QString textLine=parent->currentTextLine();
  569. if (column>textLine.length() || textLine[column]!='(') goto nothingFound;
  570. unsigned startColumn=column, endColumn=column;
  571. while (column && (textLine[--column].isLetterOrNumber()
  572. || textLine[column]=='_' || textLine[column]=='$'))
  573. startColumn--;
  574. QString identifier=textLine.mid(startColumn,endColumn-startColumn);
  575. QStringList prototypes=prototypesForIdentifier(identifier,entries);
  576. if (prototypes.isEmpty()) goto nothingFound;
  577. connect(parent,SIGNAL(argHintHidden()),this,SLOT(slotDone()));
  578. parent->showArgHint(prototypes,"()",",");
  579. // Unfortunately, Kate doesn't always send the argHintHidden event when it
  580. // closes its popup. Work around that.
  581. QWidgetList *list=QApplication::topLevelWidgets();
  582. QWidgetListIt it(*list);
  583. while (QWidget *w=it.current()) {
  584. ++it;
  585. if (w->isVisible() && w->testWFlags(Qt::WType_Popup)) {
  586. argHintPopup=w;
  587. break;
  588. }
  589. }
  590. delete list;
  591. if (argHintPopup)
  592. argHintPopup->installEventFilter(this);
  593. }
  594. void ArgHintPopup::slotDone()
  595. {
  596. if (!done) {
  597. done=true;
  598. deleteLater();
  599. }
  600. }
  601. bool ArgHintPopup::eventFilter(QObject *o, QEvent *e)
  602. {
  603. if (!done && o==argHintPopup && e->type()==QEvent::Hide) {
  604. done=true;
  605. deleteLater();
  606. }
  607. return false;
  608. }