completion.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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 <kmessagebox.h>
  24. #include <kate/view.h>
  25. #include <kate/document.h>
  26. #include <ktexteditor/editinterfaceext.h>
  27. #include "completion.h"
  28. #include "parsing.h"
  29. #include "preferences.h"
  30. #include "mainform.h"
  31. #include "tpr.h"
  32. // Maps file name to a CompletionInfo.
  33. QMap<QString,CompletionInfo> systemHeaderCompletion, projectCompletion;
  34. static void resetSearchedFlags(void)
  35. {
  36. for (QMap<QString,CompletionInfo>::Iterator it=projectCompletion.begin();
  37. it!=projectCompletion.end(); ++it)
  38. (*it).searched=false;
  39. for (QMap<QString,CompletionInfo>::Iterator it=systemHeaderCompletion.begin();
  40. it!=systemHeaderCompletion.end(); ++it)
  41. (*it).searched=false;
  42. }
  43. static void findSymbolInSystemHeaders(const QString &symbol,
  44. const QStringList &systemHeaders,
  45. QString &symbolFile,
  46. unsigned &symbolLine,
  47. bool &systemHeader)
  48. {
  49. for (QStringList::ConstIterator it=systemHeaders.begin();
  50. it!=systemHeaders.end(); ++it) {
  51. const QString &headerName=*it;
  52. // Avoid infinite recursion.
  53. if (systemHeaderCompletion.contains(headerName)
  54. && !systemHeaderCompletion[headerName].searched) {
  55. CompletionInfo &completionInfo=systemHeaderCompletion[headerName];
  56. completionInfo.searched=true;
  57. if (completionInfo.lineNumbers.contains(symbol)) {
  58. symbolFile=headerName;
  59. symbolLine=completionInfo.lineNumbers[symbol];
  60. systemHeader=true;
  61. return;
  62. } else {
  63. findSymbolInSystemHeaders(symbol,completionInfo.includedSystem,
  64. symbolFile,symbolLine,systemHeader);
  65. if (!symbolFile.isNull()) return;
  66. }
  67. }
  68. }
  69. }
  70. static bool findSymbolInFileRecursive(const QString &symbol,
  71. const QString &fileText,
  72. const QString &fileName,
  73. MainForm *mainForm,
  74. QString &symbolFile,
  75. unsigned &symbolLine,
  76. bool &systemHeader)
  77. {
  78. symbolFile=QString::null;
  79. systemHeader=false;
  80. if (!projectCompletion.contains(fileName) || projectCompletion[fileName].dirty) {
  81. QFileInfo fileInfo(fileName);
  82. QString pathInProject=fileInfo.isRelative()?fileInfo.dirPath():".";
  83. CompletionInfo completionInfo=parseFileCompletion(fileText,pathInProject);
  84. if (completionInfo.dirty) return false;
  85. projectCompletion.insert(fileName,completionInfo);
  86. }
  87. CompletionInfo &completionInfo=projectCompletion[fileName];
  88. // Avoid infinite recursion.
  89. if (completionInfo.searched) return true;
  90. completionInfo.searched=true;
  91. if (completionInfo.lineNumbers.contains(symbol)) {
  92. symbolFile=fileName;
  93. symbolLine=completionInfo.lineNumbers[symbol];
  94. return true;
  95. }
  96. for (QStringList::ConstIterator it=completionInfo.included.begin();
  97. it!=completionInfo.included.end(); ++it) {
  98. const QString &headerName=*it;
  99. QString headerText=mainForm->textForHeader(headerName);
  100. if (!headerText.isNull()) {
  101. if (!findSymbolInFile(symbol,headerText,headerName,mainForm,symbolFile,
  102. symbolLine,systemHeader))
  103. return false;
  104. if (!symbolFile.isNull()) return true;
  105. }
  106. }
  107. findSymbolInSystemHeaders(symbol,completionInfo.includedSystem,symbolFile,
  108. symbolLine,systemHeader);
  109. return true;
  110. }
  111. bool findSymbolInFile(const QString &symbol,
  112. const QString &fileText,
  113. const QString &fileName,
  114. MainForm *mainForm,
  115. QString &symbolFile,
  116. unsigned &symbolLine,
  117. bool &systemHeader)
  118. {
  119. resetSearchedFlags();
  120. return findSymbolInFileRecursive(symbol,fileText,fileName,mainForm,symbolFile,
  121. symbolLine,systemHeader);
  122. }
  123. static void mergeCompletionEntries(QValueList<KTextEditor::CompletionEntry> &dest,
  124. const QValueList<KTextEditor::CompletionEntry> &src)
  125. {
  126. for (QValueList<KTextEditor::CompletionEntry>::ConstIterator it=src.begin();
  127. it!=src.end(); ++it)
  128. dest.append(*it);
  129. }
  130. bool completionEntriesForFile(const QString &fileText,
  131. const QString &fileName,
  132. MainForm *mainForm,
  133. QValueList<KTextEditor::CompletionEntry> &result)
  134. {
  135. if (!projectCompletion.contains(fileName) || projectCompletion[fileName].dirty) {
  136. QFileInfo fileInfo(fileName);
  137. QString pathInProject=fileInfo.isRelative()?fileInfo.dirPath():".";
  138. CompletionInfo completionInfo=parseFileCompletion(fileText,pathInProject);
  139. if (completionInfo.dirty) return false;
  140. projectCompletion.insert(fileName,completionInfo);
  141. }
  142. const CompletionInfo &completionInfo=projectCompletion[fileName];
  143. mergeCompletionEntries(result,completionInfo.entries);
  144. for (QStringList::ConstIterator it=completionInfo.includedSystem.begin();
  145. it!=completionInfo.includedSystem.end(); ++it) {
  146. const QString &headerName=*it;
  147. if (systemHeaderCompletion.contains(headerName))
  148. mergeCompletionEntries(result,systemHeaderCompletion[headerName].entries);
  149. }
  150. for (QStringList::ConstIterator it=completionInfo.included.begin();
  151. it!=completionInfo.included.end(); ++it) {
  152. const QString &headerName=*it;
  153. QString headerText=mainForm->textForHeader(headerName);
  154. if (!headerText.isNull())
  155. if (!completionEntriesForFile(headerText,headerName,mainForm,result))
  156. return false;
  157. }
  158. return true;
  159. }
  160. bool parseHelpSources(QWidget *parent, const QString &directory,
  161. QMap<QString,CompletionInfo> &sysHdrCompletion)
  162. {
  163. return true; // TODO
  164. }
  165. bool parseSystemHeaders(QWidget *parent, const QString &directory,
  166. QMap<QString,CompletionInfo> &sysHdrCompletion)
  167. {
  168. QDir qdir(directory);
  169. QStringList headers=qdir.entryList("*.h",QDir::Files);
  170. for (QStringList::ConstIterator it=headers.begin(); it!=headers.end(); ++it) {
  171. const QString &header=*it;
  172. QString fileText=loadFileText(QFileInfo(qdir,header).filePath());
  173. if (fileText.isNull()) {
  174. KMessageBox::error(parent,QString("Can't open \'%1\'.").arg(header));
  175. return false;
  176. }
  177. sysHdrCompletion[header]=parseFileCompletion(fileText,QString::null);
  178. if (sysHdrCompletion[header].dirty) return false;
  179. }
  180. return true;
  181. }
  182. TemplatePopup::TemplatePopup(Kate::View *parent)
  183. : QPopupMenu(parent), view(parent)
  184. {
  185. connect(this,SIGNAL(activated(int)),this,SLOT(QPopupMenu_activated(int)));
  186. unsigned i=0;
  187. for (QValueList<QPair<QString,QString> >::ConstIterator it=preferences.templates.begin();
  188. it!=preferences.templates.end(); ++it, i++)
  189. insertItem((*it).first,i);
  190. QPoint pos=parent->cursorCoordinates();
  191. if (pos.x()<0 || pos.y()<0) {
  192. // Cursor outside of the view, so center on view instead.
  193. QSize parentSize=parent->size();
  194. QSize popupSize=sizeHint();
  195. pos.setX((parentSize.width()-popupSize.width())>>1);
  196. pos.setY((parentSize.height()-popupSize.height())>>1);
  197. }
  198. exec(parent->mapToGlobal(pos));
  199. deleteLater();
  200. }
  201. void TemplatePopup::QPopupMenu_activated(int id)
  202. {
  203. QString code=preferences.templates[id].second;
  204. QString indent=view->currentTextLine();
  205. // Remove everything starting from the first non-whitespace character.
  206. indent=indent.remove(QRegExp("(?!\\s).*$"));
  207. indent.prepend('\n');
  208. code.replace('\n',indent);
  209. int cursorPos=code.find('|');
  210. if (cursorPos>=0) {
  211. QString left=code.left(cursorPos);
  212. QString right=code.mid(cursorPos+1);
  213. unsigned row, col;
  214. KTextEditor::EditInterfaceExt *editExt=KTextEditor::editInterfaceExt(view->getDoc());
  215. editExt->editBegin();
  216. view->insertText(left);
  217. view->cursorPositionReal(&row,&col);
  218. view->insertText(right);
  219. editExt->editEnd();
  220. view->setCursorPositionReal(row,col);
  221. } else view->insertText(code);
  222. }