TDE core libraries
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

docwordcompletion.cpp 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. /*
  2. This library is free software; you can redistribute it and/or
  3. modify it under the terms of the GNU Library General Public
  4. License version 2 as published by the Free Software Foundation.
  5. This library is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  8. Library General Public License for more details.
  9. You should have received a copy of the GNU Library General Public License
  10. along with this library; see the file COPYING.LIB. If not, write to
  11. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  12. Boston, MA 02110-1301, USA.
  13. ---
  14. file: docwordcompletion.cpp
  15. KTextEditor plugin to autocompletion with document words.
  16. Copyright Anders Lund <anders.lund@lund.tdcadsl.dk>, 2003
  17. The following completion methods are supported:
  18. * Completion with bigger matching words in
  19. either direction (backward/forward).
  20. * NOT YET Pop up a list of all bigger matching words in document
  21. */
  22. //BEGIN includes
  23. #include "docwordcompletion.h"
  24. #include <tdetexteditor/document.h>
  25. #include <tdetexteditor/viewcursorinterface.h>
  26. #include <tdetexteditor/editinterface.h>
  27. #include <tdetexteditor/variableinterface.h>
  28. #include <tdeapplication.h>
  29. #include <tdeconfig.h>
  30. #include <kdialog.h>
  31. #include <kgenericfactory.h>
  32. #include <tdelocale.h>
  33. #include <tdeaction.h>
  34. #include <knotifyclient.h>
  35. #include <tdeparts/part.h>
  36. #include <kiconloader.h>
  37. #include <tqregexp.h>
  38. #include <tqstring.h>
  39. #include <tqdict.h>
  40. #include <tqspinbox.h>
  41. #include <tqlabel.h>
  42. #include <tqlayout.h>
  43. #include <tqhbox.h>
  44. #include <tqwhatsthis.h>
  45. #include <tqcheckbox.h>
  46. // #include <kdebug.h>
  47. //END
  48. //BEGIN DocWordCompletionPlugin
  49. K_EXPORT_COMPONENT_FACTORY( tdetexteditor_docwordcompletion, KGenericFactory<DocWordCompletionPlugin>( "tdetexteditor_docwordcompletion" ) )
  50. DocWordCompletionPlugin::DocWordCompletionPlugin( TQObject *parent,
  51. const char* name,
  52. const TQStringList& /*args*/ )
  53. : KTextEditor::Plugin ( (KTextEditor::Document*) parent, name )
  54. {
  55. readConfig();
  56. }
  57. void DocWordCompletionPlugin::readConfig()
  58. {
  59. TDEConfig *config = kapp->config();
  60. config->setGroup( "DocWordCompletion Plugin" );
  61. m_treshold = config->readNumEntry( "treshold", 3 );
  62. m_autopopup = config->readBoolEntry( "autopopup", true );
  63. }
  64. void DocWordCompletionPlugin::writeConfig()
  65. {
  66. TDEConfig *config = kapp->config();
  67. config->setGroup("DocWordCompletion Plugin");
  68. config->writeEntry("autopopup", m_autopopup );
  69. config->writeEntry("treshold", m_treshold );
  70. }
  71. void DocWordCompletionPlugin::addView(KTextEditor::View *view)
  72. {
  73. DocWordCompletionPluginView *nview = new DocWordCompletionPluginView (m_treshold, m_autopopup, view, "Document word completion");
  74. m_views.append (nview);
  75. }
  76. void DocWordCompletionPlugin::removeView(KTextEditor::View *view)
  77. {
  78. for (uint z=0; z < m_views.count(); z++)
  79. if (m_views.at(z)->parentClient() == view)
  80. {
  81. DocWordCompletionPluginView *nview = m_views.at(z);
  82. m_views.remove (nview);
  83. delete nview;
  84. }
  85. }
  86. KTextEditor::ConfigPage* DocWordCompletionPlugin::configPage( uint, TQWidget *parent, const char *name )
  87. {
  88. return new DocWordCompletionConfigPage( this, parent, name );
  89. }
  90. TQString DocWordCompletionPlugin::configPageName( uint ) const
  91. {
  92. return i18n("Word Completion Plugin");
  93. }
  94. TQString DocWordCompletionPlugin::configPageFullName( uint ) const
  95. {
  96. return i18n("Configure the Word Completion Plugin");
  97. }
  98. // FIXME provide sucn a icon
  99. TQPixmap DocWordCompletionPlugin::configPagePixmap( uint, int size ) const
  100. {
  101. return UserIcon( "kte_wordcompletion", size );
  102. }
  103. //END
  104. //BEGIN DocWordCompletionPluginView
  105. struct DocWordCompletionPluginViewPrivate
  106. {
  107. uint line, col; // start position of last match (where to search from)
  108. uint cline, ccol; // cursor position
  109. uint lilen; // length of last insertion
  110. TQString last; // last word we were trying to match
  111. TQString lastIns; // latest applied completion
  112. TQRegExp re; // hrm
  113. TDEToggleAction *autopopup; // for accessing state
  114. uint treshold; // the required length of a word before popping up the completion list automatically
  115. int directionalPos; // be able to insert "" at the correct time
  116. };
  117. DocWordCompletionPluginView::DocWordCompletionPluginView( uint treshold, bool autopopup, KTextEditor::View *view, const char *name )
  118. : TQObject( view, name ),
  119. KXMLGUIClient( view ),
  120. m_view( view ),
  121. d( new DocWordCompletionPluginViewPrivate )
  122. {
  123. d->treshold = treshold;
  124. view->insertChildClient( this );
  125. setInstance( KGenericFactory<DocWordCompletionPlugin>::instance() );
  126. (void) new TDEAction( i18n("Reuse Word Above"), CTRL+Key_8, this,
  127. TQT_SLOT(completeBackwards()), actionCollection(), "doccomplete_bw" );
  128. (void) new TDEAction( i18n("Reuse Word Below"), CTRL+Key_9, this,
  129. TQT_SLOT(completeForwards()), actionCollection(), "doccomplete_fw" );
  130. (void) new TDEAction( i18n("Pop Up Completion List"), 0, this,
  131. TQT_SLOT(popupCompletionList()), actionCollection(), "doccomplete_pu" );
  132. (void) new TDEAction( i18n("Shell Completion"), 0, this,
  133. TQT_SLOT(shellComplete()), actionCollection(), "doccomplete_sh" );
  134. d->autopopup = new TDEToggleAction( i18n("Automatic Completion Popup"), 0, this,
  135. TQT_SLOT(toggleAutoPopup()), actionCollection(), "enable_autopopup" );
  136. d->autopopup->setChecked( autopopup );
  137. toggleAutoPopup();
  138. setXMLFile("docwordcompletionui.rc");
  139. KTextEditor::VariableInterface *vi = KTextEditor::variableInterface( view->document() );
  140. if ( vi )
  141. {
  142. TQString e = vi->variable("wordcompletion-autopopup");
  143. if ( ! e.isEmpty() )
  144. d->autopopup->setEnabled( e == "true" );
  145. connect( view->document(), TQT_SIGNAL(variableChanged(const TQString &, const TQString &)),
  146. this, TQT_SLOT(slotVariableChanged(const TQString &, const TQString &)) );
  147. }
  148. }
  149. void DocWordCompletionPluginView::settreshold( uint t )
  150. {
  151. d->treshold = t;
  152. }
  153. void DocWordCompletionPluginView::completeBackwards()
  154. {
  155. complete( false );
  156. }
  157. void DocWordCompletionPluginView::completeForwards()
  158. {
  159. complete();
  160. }
  161. // Pop up the editors completion list if applicable
  162. void DocWordCompletionPluginView::popupCompletionList( TQString w )
  163. {
  164. if ( w.isEmpty() )
  165. w = word();
  166. if ( w.isEmpty() )
  167. return;
  168. KTextEditor::CodeCompletionInterface *cci = codeCompletionInterface( m_view );
  169. cci->showCompletionBox( allMatches( w ), w.length() );
  170. }
  171. void DocWordCompletionPluginView::toggleAutoPopup()
  172. {
  173. if ( d->autopopup->isChecked() ) {
  174. if ( ! connect( m_view->document(), TQT_SIGNAL(charactersInteractivelyInserted(int ,int ,const TQString&)),
  175. this, TQT_SLOT(autoPopupCompletionList()) ))
  176. {
  177. connect( m_view->document(), TQT_SIGNAL(textChanged()), this, TQT_SLOT(autoPopupCompletionList()) );
  178. }
  179. } else {
  180. disconnect( m_view->document(), TQT_SIGNAL(textChanged()), this, TQT_SLOT(autoPopupCompletionList()) );
  181. disconnect( m_view->document(), TQT_SIGNAL(charactersInteractivelyInserted(int ,int ,const TQString&)),
  182. this, TQT_SLOT(autoPopupCompletionList()) );
  183. }
  184. }
  185. // for autopopup FIXME - don't pop up if reuse word is inserting
  186. void DocWordCompletionPluginView::autoPopupCompletionList()
  187. {
  188. if ( ! m_view->hasFocus() ) return;
  189. TQString w = word();
  190. if ( w.length() >= d->treshold )
  191. {
  192. popupCompletionList( w );
  193. }
  194. }
  195. // Contributed by <brain@hdsnet.hu>
  196. void DocWordCompletionPluginView::shellComplete()
  197. {
  198. // setup
  199. KTextEditor::EditInterface * ei = KTextEditor::editInterface(m_view->document());
  200. // find the word we are typing
  201. uint cline, ccol;
  202. viewCursorInterface(m_view)->cursorPositionReal(&cline, &ccol);
  203. TQString wrd = word();
  204. if (wrd.isEmpty())
  205. return;
  206. TQValueList < KTextEditor::CompletionEntry > matches = allMatches(wrd);
  207. if (matches.size() == 0)
  208. return;
  209. TQString partial = findLongestUnique(matches);
  210. if (partial.length() == wrd.length())
  211. {
  212. KTextEditor::CodeCompletionInterface * cci = codeCompletionInterface(m_view);
  213. cci->showCompletionBox(matches, wrd.length());
  214. }
  215. else
  216. {
  217. partial.remove(0, wrd.length());
  218. ei->insertText(cline, ccol, partial);
  219. }
  220. }
  221. // Do one completion, searching in the desired direction,
  222. // if possible
  223. void DocWordCompletionPluginView::complete( bool fw )
  224. {
  225. // setup
  226. KTextEditor::EditInterface *ei = KTextEditor::editInterface( m_view->document() );
  227. // find the word we are typing
  228. uint cline, ccol;
  229. viewCursorInterface( m_view )->cursorPositionReal( &cline, &ccol );
  230. TQString wrd = word();
  231. if ( wrd.isEmpty() )
  232. return;
  233. int inc = fw ? 1 : -1;
  234. /* IF the current line is equal to the previous line
  235. AND the position - the length of the last inserted string
  236. is equal to the old position
  237. AND the lastinsertedlength last characters of the word is
  238. equal to the last inserted string
  239. */
  240. if ( cline == d-> cline &&
  241. ccol - d->lilen == d->ccol &&
  242. wrd.endsWith( d->lastIns ) )
  243. {
  244. // this is a repeted activation
  245. // if we are back to where we started, reset.
  246. if ( ( fw && d->directionalPos == -1 ) ||
  247. ( !fw && d->directionalPos == 1 ) )
  248. {
  249. if ( d->lilen )
  250. ei->removeText( d->cline, d->ccol, d->cline, d->ccol + d->lilen );
  251. d->lastIns = "";
  252. d->lilen = 0;
  253. d->line = d->cline;
  254. d->col = d->ccol;
  255. d->directionalPos = 0;
  256. return;
  257. }
  258. if ( fw )
  259. d->col += d->lilen;
  260. ccol = d->ccol;
  261. wrd = d->last;
  262. d->directionalPos += inc;
  263. }
  264. else
  265. {
  266. d->cline = cline;
  267. d->ccol = ccol;
  268. d->last = wrd;
  269. d->lastIns = "";
  270. d->line = cline;
  271. d->col = ccol - wrd.length();
  272. d->lilen = 0;
  273. d->directionalPos = inc;
  274. }
  275. d->re.setPattern( "\\b" + wrd + "(\\w+)" );
  276. int pos ( 0 );
  277. TQString ln = ei->textLine( d->line );
  278. while ( true )
  279. {
  280. pos = fw ?
  281. d->re.search( ln, d->col ) :
  282. d->re.searchRev( ln, d->col );
  283. if ( pos > -1 ) // we matched a word
  284. {
  285. TQString m = d->re.cap( 1 );
  286. if ( m != d->lastIns )
  287. {
  288. // we got good a match! replace text and return.
  289. if ( d->lilen )
  290. ei->removeText( d->cline, d->ccol, d->cline, d->ccol + d->lilen );
  291. ei->insertText( d->cline, d->ccol, m );
  292. d->lastIns = m;
  293. d->lilen = m.length();
  294. d->col = pos; // for next try
  295. return;
  296. }
  297. // equal to last one, continue
  298. else
  299. {
  300. d->col = pos; // for next try
  301. if ( fw )
  302. d->col += d->re.matchedLength();
  303. else
  304. {
  305. if ( pos == 0 )
  306. {
  307. if ( d->line > 0 )
  308. {
  309. d->line += inc;
  310. ln = ei->textLine( d->line );
  311. d->col = ln.length();
  312. }
  313. else
  314. {
  315. KNotifyClient::beep();
  316. return;
  317. }
  318. }
  319. else
  320. d->col--;
  321. }
  322. }
  323. }
  324. else // no match
  325. {
  326. if ( (! fw && d->line == 0 ) || ( fw && d->line >= (uint)ei->numLines() ) )
  327. {
  328. KNotifyClient::beep();
  329. return;
  330. }
  331. d->line += inc;
  332. ln = ei->textLine( d->line );
  333. d->col = fw ? 0 : ln.length();
  334. }
  335. } // while true
  336. }
  337. // Contributed by <brain@hdsnet.hu>
  338. TQString DocWordCompletionPluginView::findLongestUnique(const TQValueList < KTextEditor::CompletionEntry > &matches)
  339. {
  340. TQString partial = matches.front().text;
  341. TQValueList < KTextEditor::CompletionEntry >::const_iterator i = matches.begin();
  342. for (++i; i != matches.end(); ++i)
  343. {
  344. if (!(*i).text.startsWith(partial))
  345. {
  346. while(partial.length() > 0)
  347. {
  348. partial.remove(partial.length() - 1, 1);
  349. if ((*i).text.startsWith(partial))
  350. {
  351. break;
  352. }
  353. }
  354. if (partial.length() == 0)
  355. return TQString();
  356. }
  357. }
  358. return partial;
  359. }
  360. // Return the string to complete (the letters behind the cursor)
  361. TQString DocWordCompletionPluginView::word()
  362. {
  363. uint cline, ccol;
  364. viewCursorInterface( m_view )->cursorPositionReal( &cline, &ccol );
  365. if ( ! ccol ) return TQString::null; // no word
  366. KTextEditor::EditInterface *ei = KTextEditor::editInterface( m_view->document() );
  367. d->re.setPattern( "\\b(\\w+)$" );
  368. if ( d->re.searchRev(
  369. ei->text( cline, 0, cline, ccol )
  370. ) < 0 )
  371. return TQString::null; // no word
  372. return d->re.cap( 1 );
  373. }
  374. // Scan throught the entire document for possible completions,
  375. // ignoring any dublets
  376. TQValueList<KTextEditor::CompletionEntry> DocWordCompletionPluginView::allMatches( const TQString &word )
  377. {
  378. TQValueList<KTextEditor::CompletionEntry> l;
  379. uint i( 0 );
  380. int pos( 0 );
  381. d->re.setPattern( "\\b("+word+"\\w+)" );
  382. TQString s, m;
  383. KTextEditor::EditInterface *ei = KTextEditor::editInterface( m_view->document() );
  384. TQDict<int> seen; // maybe slow with > 17 matches
  385. int sawit(1); // to ref for the dict
  386. uint cline, ccol;// needed to avoid constructing a word at cursor position
  387. viewCursorInterface( m_view )->cursorPositionReal( &cline, &ccol );
  388. while( i < ei->numLines() )
  389. {
  390. s = ei->textLine( i );
  391. pos = 0;
  392. while ( pos >= 0 )
  393. {
  394. pos = d->re.search( s, pos );
  395. if ( pos >= 0 )
  396. {
  397. // do not construct a new word!
  398. if ( i == cline && pos + word.length() == ccol )
  399. {
  400. pos += word.length();
  401. continue;
  402. }
  403. m = d->re.cap( 1 );
  404. if ( ! seen[ m ] ) {
  405. seen.insert( m, &sawit );
  406. KTextEditor::CompletionEntry e;
  407. e.text = m;
  408. l.append( e );
  409. }
  410. pos += d->re.matchedLength();
  411. }
  412. }
  413. i++;
  414. }
  415. return l;
  416. }
  417. void DocWordCompletionPluginView::slotVariableChanged( const TQString &var, const TQString &val )
  418. {
  419. if ( var == "wordcompletion-autopopup" )
  420. d->autopopup->setEnabled( val == "true" );
  421. else if ( var == "wordcompletion-treshold" )
  422. d->treshold = val.toInt();
  423. }
  424. //END
  425. //BEGIN DocWordCompletionConfigPage
  426. DocWordCompletionConfigPage::DocWordCompletionConfigPage( DocWordCompletionPlugin *completion, TQWidget *parent, const char *name )
  427. : KTextEditor::ConfigPage( parent, name )
  428. , m_completion( completion )
  429. {
  430. TQVBoxLayout *lo = new TQVBoxLayout( this );
  431. lo->setSpacing( KDialog::spacingHint() );
  432. cbAutoPopup = new TQCheckBox( i18n("Automatically &show completion list"), this );
  433. lo->addWidget( cbAutoPopup );
  434. TQHBox *hb = new TQHBox( this );
  435. hb->setSpacing( KDialog::spacingHint() );
  436. lo->addWidget( hb );
  437. TQLabel *l = new TQLabel( i18n(
  438. "Translators: This is the first part of two strings wich will comprise the "
  439. "sentence 'Show completions when a word is at least N characters'. The first "
  440. "part is on the right side of the N, which is represented by a spinbox "
  441. "widget, followed by the second part: 'characters long'. Characters is a "
  442. "ingeger number between and including 1 and 30. Feel free to leave the "
  443. "second part of the sentence blank if it suits your language better. ",
  444. "Show completions &when a word is at least"), hb );
  445. sbAutoPopup = new TQSpinBox( 1, 30, 1, hb );
  446. l->setBuddy( sbAutoPopup );
  447. lSbRight = new TQLabel( i18n(
  448. "This is the second part of two strings that will comprise teh sentence "
  449. "'Show completions when a word is at least N characters'",
  450. "characters long."), hb );
  451. TQWhatsThis::add( cbAutoPopup, i18n(
  452. "Enable the automatic completion list popup as default. The popup can "
  453. "be disabled on a view basis from the 'Tools' menu.") );
  454. TQWhatsThis::add( sbAutoPopup, i18n(
  455. "Define the length a word should have before the completion list "
  456. "is displayed.") );
  457. cbAutoPopup->setChecked( m_completion->autoPopupEnabled() );
  458. sbAutoPopup->setValue( m_completion->treshold() );
  459. lo->addStretch();
  460. }
  461. void DocWordCompletionConfigPage::apply()
  462. {
  463. m_completion->setAutoPopupEnabled( cbAutoPopup->isChecked() );
  464. m_completion->setTreshold( sbAutoPopup->value() );
  465. m_completion->writeConfig();
  466. }
  467. void DocWordCompletionConfigPage::reset()
  468. {
  469. cbAutoPopup->setChecked( m_completion->autoPopupEnabled() );
  470. sbAutoPopup->setValue( m_completion->treshold() );
  471. }
  472. void DocWordCompletionConfigPage::defaults()
  473. {
  474. cbAutoPopup->setChecked( true );
  475. sbAutoPopup->setValue( 3 );
  476. }
  477. //END DocWordCompletionConfigPage
  478. #include "docwordcompletion.moc"
  479. // kate: space-indent on; indent-width 2; replace-tabs on; mixed-indent off;