diff options
Diffstat (limited to 'src/symbolcompletion.cpp')
-rw-r--r-- | src/symbolcompletion.cpp | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/src/symbolcompletion.cpp b/src/symbolcompletion.cpp new file mode 100644 index 0000000..2ec8194 --- /dev/null +++ b/src/symbolcompletion.cpp @@ -0,0 +1,344 @@ +/*************************************************************************** + * + * 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 <qtimer.h> +#include <klocale.h> +#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, + QObject* pParent, const char* szName) : + QObject(pParent, szName), + m_pEditor(pEditor), + m_pCCObject(NULL) +{ + // Initialise member objects + m_pCscope = new CscopeFrontend(); + m_pAutoCompTimer = new QTimer(this); + + // Add entries to the completion list when they are available + connect(m_pCscope, SIGNAL(dataReady(FrontendToken*)), this, + SLOT(slotAddEntry(FrontendToken*))); + + // Show the completion list when the query finishes + connect(m_pCscope, SIGNAL(finished(uint)), this, + SLOT(slotQueryFinished(uint))); + + // Initiate automatic symbol completion when timer expires + connect(m_pAutoCompTimer, SIGNAL(timeout()), this, + 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() +{ + QString 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 QString& 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 QString& 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; + QString 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; + QString 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<KTextEditor::CodeCompletionInterface*>(m_pCCObject); + if (!pCCI) + return; + + // Insert the correct part of the completed symbol, when chosen by the + // user + connect(m_pCCObject, + SIGNAL(filterInsertString(KTextEditor::CompletionEntry*, QString*)), + this, + SLOT(slotFilterInsert(KTextEditor::CompletionEntry*, QString*))); + + // 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, + QString* 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() +{ + QString 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" |