/*************************************************************************** * * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ***************************************************************************/ #include #include #include "symbolcompletion.h" bool SymbolCompletion::s_bACEnabled; uint SymbolCompletion::s_nACMinChars; uint SymbolCompletion::s_nACDelay; uint SymbolCompletion::s_nACMaxEntries; /** * Class constructor. * @param pEditor The editor object for which symbol completion is required * @param pParent Parent object * @param szName Optional object name */ SymbolCompletion::SymbolCompletion(SymbolCompletion::Interface* pEditor, TQObject* pParent, const char* szName) : TQObject(pParent, szName), m_pEditor(pEditor), m_pCCObject(NULL) { // Initialise member objects m_pCscope = new CscopeFrontend(); m_pAutoCompTimer = new TQTimer(this); // Add entries to the completion list when they are available connect(m_pCscope, TQ_SIGNAL(dataReady(FrontendToken*)), this, TQ_SLOT(slotAddEntry(FrontendToken*))); // Show the completion list when the query finishes connect(m_pCscope, TQ_SIGNAL(finished(uint)), this, TQ_SLOT(slotQueryFinished(uint))); // Initiate automatic symbol completion when timer expires connect(m_pAutoCompTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotAutoCompleteTimeout())); } /** * Class destructor. */ SymbolCompletion::~SymbolCompletion() { delete m_pCscope; } /** * Stops a completion process. * This includes killing a running query, and stoping the auto-completion * timer. */ void SymbolCompletion::abort() { if (m_pCscope->isRunning()) m_pCscope->kill(); m_pAutoCompTimer->stop(); } /** * Configures auto-completion parameters. * @param bEnabled true to enable auto-completion, false otherwise * @param nMinChars Minimal number of characters a symbol needs to start * auto-completion * @param nDelay Auto-completion time interval (in milliseconds) * @param nMaxEntries The maximal number of completion entries */ void SymbolCompletion::initAutoCompletion(bool bEnabled, uint nMinChars, uint nDelay, uint nMaxEntries) { s_bACEnabled = bEnabled; s_nACMinChars = nMinChars; s_nACDelay = nDelay; s_nACMaxEntries = nMaxEntries; } /** * Starts a completion process immediately for the symbol currently under the * cursor in the editor object. * Symbol completion is only available if the cursor is positioned at the end * of the symbol. */ void SymbolCompletion::slotComplete() { TQString sSymbol; uint nPosInWord; // Read the symbol currently under the cursor sSymbol = m_pEditor->getWordUnderCursor(&nPosInWord); // The completion was triggered by user m_bAutoCompletion = false; // start completion process, prefix is only on the left from the cursor complete(sSymbol.left(nPosInWord)); } /** * Initiates an auto-completion timer. * When the timer times-out, is starts the symbol completion process. */ void SymbolCompletion::slotAutoComplete() { if (s_bACEnabled) m_pAutoCompTimer->start(s_nACDelay, true); } /** * Creates a list of possible completions to the symbol currently being * edited. * @param sPrefix The symbol to complete * @param nMaxEntries The maximal number of entries to display */ void SymbolCompletion::complete(const TQString& sPrefix, int nMaxEntries) { // Create a regular expression to extract symbol names from the query // results m_reSymbol.setPattern(sPrefix + "[a-zA-Z0-9_]*"); // If the new prefix is itself a prefix of the old one, we only need to // filter the current entries if (!m_sPrefix.isEmpty() && sPrefix.startsWith(m_sPrefix)) { filterEntries(); m_sPrefix = sPrefix; slotQueryFinished(0); return; } // Prepare member variables m_sPrefix = sPrefix; m_nMaxEntries = nMaxEntries; m_elEntries.clear(); // Run the code-completion query m_pCscope->query(CscopeFrontend::Definition, sPrefix + ".*"); } /** * Removes from the current completion list all symbols that do not match * the current regular expression. * This function is used to aviod requerying the database on certain * situations. */ void SymbolCompletion::filterEntries() { EntryList::Iterator itr; // Iterate over the list and check each entry against the current RE for (itr = m_elEntries.begin(); itr != m_elEntries.end();) { if (m_reSymbol.search((*itr).text) == -1) itr = m_elEntries.erase(itr); else ++itr; } } /** * Conevrts the completion list into a single-entry one, containing the given * message. * @param sMsg The text of the message to include in the list. */ void SymbolCompletion::makeErrMsg(const TQString& sMsg) { Entry entry; // Clear the current list m_elEntries.clear(); // Create the message item and add it to the list entry.text = sMsg; entry.userdata = "NO_INSERT"; // The message should not be insertable m_elEntries.append(entry); // Make sure a new completion request will start a new query m_sPrefix = ""; } /** * Creates a new entry in the list when a query record is available. * This slot is connected to the dataReady() signal of the CscopeFrontend * object. * @param pToken Points to the head of a record's linked-list */ void SymbolCompletion::slotAddEntry(FrontendToken* pToken) { Entry entry; TQString sText; // Do not add entries beyond the requested limit if (m_elEntries.count() > m_nMaxEntries) return; // Get the line text pToken = pToken->getNext()->getNext()->getNext(); sText = pToken->getData(); // Find the symbol within the line if (m_reSymbol.search(sText) == -1) return; // Add the new entry to the completion list entry.text = m_reSymbol.capturedTexts().first(); entry.userdata = ""; entry.comment = sText; m_elEntries.append(entry); } /** * Displays a code completion list, based on the results of the last query. * @param nRecords (ingnored) */ void SymbolCompletion::slotQueryFinished(uint /* nRecords */) { KTextEditor::CodeCompletionInterface* pCCI; uint nEntryCount; EntryList::Iterator itr; TQString sPrevText; // Get the number of entries nEntryCount = m_elEntries.count(); // Do not show the box the only completion option is the prefix itself if (m_bAutoCompletion && (nEntryCount == 1) && (m_elEntries.first().text == m_sPrefix)) { return; } // Get a pointer to the CC interface m_pCCObject = m_pEditor->getCCObject(); pCCI = dynamic_cast(m_pCCObject); if (!pCCI) return; // Insert the correct part of the completed symbol, when chosen by the // user connect(m_pCCObject, TQ_SIGNAL(filterInsertString(KTextEditor::CompletionEntry*, TQString*)), this, TQ_SLOT(slotFilterInsert(KTextEditor::CompletionEntry*, TQString*))); // Check the number of entries in the list if (nEntryCount == 0) { // No completion options, display an appropriate message makeErrMsg(i18n("No matching completion found...")); } else if (nEntryCount > m_nMaxEntries) { // The query has resulted in too many entries, display an // appropriate message makeErrMsg(i18n("Too many options...")); } else { // Sort the entries m_elEntries.sort(); // Make sure entries are unique for (itr = m_elEntries.begin(); itr != m_elEntries.end();) { if ((*itr).text == sPrevText) { itr = m_elEntries.erase(itr); } else { sPrevText = (*itr).text; ++itr; } } } // Display the completion list pCCI->showCompletionBox(m_elEntries); } /** * Determines which part of the completion entry should be added to the code * when that entry is selected. * @param pEntry Points to the selected entry * @param pTextToInsert Contains the string to insert, upon return */ void SymbolCompletion::slotFilterInsert(KTextEditor::CompletionEntry* pEntry, TQString* pTextToInsert) { // Insert the completed entry, unless it contains an error message if (pEntry->userdata.isEmpty()) *pTextToInsert = pEntry->text.mid(m_sPrefix.length()); else *pTextToInsert = ""; // Disconnect the CC object signals disconnect(m_pCCObject, 0, this, 0); m_pCCObject = NULL; } /** * Checks if the current symbol is eligible for auto-completion, and if so, * starts the completion process. * Auto-completion is performed for symbols that have the required minimal * number of entries, and the cursor is positioned at the end of the word. * This slot is connected to the timeout() signal of the auto-completion * timer. */ void SymbolCompletion::slotAutoCompleteTimeout() { TQString sPrefix; uint nPosInWord, nLength; // Read the symbol currently under the cursor sPrefix = m_pEditor->getWordUnderCursor(&nPosInWord); nLength = sPrefix.length(); // Check conditions, and start the completion process if ((nLength >= s_nACMinChars) && (nPosInWord == nLength)) { // The completion was triggered by auto-completion m_bAutoCompletion = true; complete(sPrefix, s_nACMaxEntries); } } #include "symbolcompletion.moc"