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.
kscope/src/symbolcompletion.cpp

345 lines
10 KiB

/***************************************************************************
*
* 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 <tqtimer.h>
#include <tdelocale.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,
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<KTextEditor::CodeCompletionInterface*>(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"