diff --git a/language/codegen/applychangeswidget.cpp b/language/codegen/applychangeswidget.cpp index c5c0a5c3e..3e83ad3a2 100644 --- a/language/codegen/applychangeswidget.cpp +++ b/language/codegen/applychangeswidget.cpp @@ -1,322 +1,322 @@ /* Copyright 2008 Aleix Pol * Copyright 2009 Ramón Zarazúa * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "applychangeswidget.h" #include "komparesupport.h" #include // #include // #include #include #include #include #include #include #include #include #include #include #include #include #include #include "coderepresentation.h" #include #include #include #include #include namespace KDevelop { class ApplyChangesWidgetPrivate { public: ApplyChangesWidgetPrivate(ApplyChangesWidget * p) : parent(p), m_index(0) {} ~ApplyChangesWidgetPrivate() { qDeleteAll(m_temps); } void addItem(QStandardItemModel* mit, KTextEditor::Document *document, const KTextEditor::Range &range, const QString& type, const QString& removedText = QString()); void jump( const QModelIndex & idx); void createEditPart(const KDevelop::IndexedString& url); void updateButtonLabel(); ApplyChangesWidget * const parent; int m_index; QList m_editParts; QList m_changes; QList m_temps; QList m_files; KTabWidget * m_documentTabs; QLabel* m_info; KompareWidgets m_kompare; }; ApplyChangesWidget::ApplyChangesWidget(QWidget* parent) : KDialog(parent), d(new ApplyChangesWidgetPrivate(this)) { setSizeGripEnabled(true); setInitialSize(QSize(800, 400)); KDialog::setButtons(KDialog::Ok | KDialog::Cancel | KDialog::User1); KPushButton * switchButton(KDialog::button(KDialog::User1)); switchButton->setText(i18n("Edit Document")); switchButton->setEnabled(d->m_kompare.enabled); connect(switchButton, SIGNAL(released()), this, SLOT(switchEditView())); QWidget* w=new QWidget(this); d->m_info=new QLabel(w); d->m_documentTabs = new KTabWidget(w); connect(d->m_documentTabs, SIGNAL(currentChanged(int)), this, SLOT(indexChanged(int))); QVBoxLayout* l = new QVBoxLayout(w); l->addWidget(d->m_info); l->addWidget(d->m_documentTabs); setMainWidget(w); } ApplyChangesWidget::~ApplyChangesWidget() { delete d; } bool ApplyChangesWidget::hasDocuments() const { return d->m_editParts.size() > 0; } KTextEditor::Document* ApplyChangesWidget::document() const { return qobject_cast(d->m_editParts[d->m_index]); } void ApplyChangesWidget::setInformation(const QString & info) { d->m_info->setText(info); } void ApplyChangesWidget::addDocuments(const IndexedString & original) { int idx=d->m_files.indexOf(original); if(idx<0) { QWidget * w = new QWidget; - d->m_documentTabs->addTab(w, original.str()); + d->m_documentTabs->addTab(w, original.toString()); d->m_documentTabs->setCurrentWidget(w); d->m_files.insert(d->m_index, original); d->createEditPart(original); } else { d->m_index=idx; } switchEditView(); } bool ApplyChangesWidget::applyAllChanges() { /// @todo implement safeguard in case a file saving fails bool ret = true; for(int i = 0; i < d->m_files.size(); ++i ) if(d->m_editParts[i]->saveAs(d->m_files[i].toUrl())) { IDocument* doc = ICore::self()->documentController()->documentForUrl(d->m_files[i].toUrl()); if(doc && doc->state()==IDocument::Dirty) doc->reload(); } else ret = false; return ret; } } Q_DECLARE_METATYPE(KTextEditor::Range) namespace KDevelop { void ApplyChangesWidgetPrivate::addItem(QStandardItemModel* mit, KTextEditor::Document *document, const KTextEditor::Range &range, const QString& type, const QString& removedText) { bool isFirst=mit->rowCount()==0; QStringList edition=document->textLines(range); if(edition.first().isEmpty()) edition.removeFirst(); QStandardItem* it= new QStandardItem(edition.join("\n").append(removedText)); QStandardItem* action= new QStandardItem(type); it->setData(qVariantFromValue(range)); it->setEditable(false); action->setEditable(false); mit->appendRow(QList() << it << action); if(isFirst) jump(it->index()); } void ApplyChangesWidget::jump( const QModelIndex & idx) { d->jump(idx); } void ApplyChangesWidgetPrivate::jump( const QModelIndex & idx) { Q_ASSERT( m_index == m_documentTabs->currentIndex()); QStandardItem *it=m_changes[m_index]->itemFromIndex(idx); KTextEditor::View* view=qobject_cast(m_editParts[m_index]->widget()); KTextEditor::Range r=it->data().value(); view->setSelection(r); view->setCursorPosition(r.start()); } void ApplyChangesWidgetPrivate::updateButtonLabel() { KPushButton * switchButton(parent->button(KDialog::User1)); if(m_kompare.widgetActive(m_index)) switchButton->setText(i18n("Edit Document")); else switchButton->setText(i18n("View Differences")); } void ApplyChangesWidgetPrivate::createEditPart(const IndexedString & file) { QWidget * widget = m_documentTabs->currentWidget(); Q_ASSERT(widget); QVBoxLayout *m=new QVBoxLayout(widget); QSplitter *v=new QSplitter(widget); m->addWidget(v); KUrl url = file.toUrl(); KMimeType::Ptr mimetype = KMimeType::findByUrl( url, 0, true ); KParts::ReadWritePart* part=KMimeTypeTrader::self()->createPartInstanceFromQuery(mimetype->name(), widget, widget); KTextEditor::Document* document=qobject_cast(part); Q_ASSERT(document); Q_ASSERT(document->action("file_save")); document->action("file_save")->setEnabled(false); m_editParts.insert(m_index, part); //Open the best code representation, even if it is artificial CodeRepresentation::Ptr repr = createCodeRepresentation(file); if(!repr->fileExists()) { KTemporaryFile * temp(new KTemporaryFile); temp->setSuffix(url.fileName().split('.').last()); temp->open(); temp->write(repr->text().toUtf8()); temp->close(); url = temp->fileName(); m_temps << temp; } m_editParts[m_index]->openUrl(url); m_changes.insert(m_index, new QStandardItemModel(widget)); m_changes[m_index]->setHorizontalHeaderLabels(QStringList(i18n("Text")) << i18n("Action")); QTreeView *changesView=new QTreeView(widget); changesView->setRootIsDecorated(false); changesView->setModel(m_changes[m_index]); v->addWidget(m_editParts[m_index]->widget()); v->addWidget(changesView); v->setSizes(QList() << 400 << 100); QObject::connect(m_editParts[m_index], SIGNAL(textChanged(KTextEditor::Document*,KTextEditor::Range,KTextEditor::Range)), parent, SLOT(change(KTextEditor::Document*,KTextEditor::Range,KTextEditor::Range))); QObject::connect(m_editParts[m_index], SIGNAL(textInserted(KTextEditor::Document*,KTextEditor::Range)), parent, SLOT(insertion(KTextEditor::Document*,KTextEditor::Range))); QObject::connect(m_editParts[m_index], SIGNAL(textRemoved(KTextEditor::Document*,KTextEditor::Range,QString)), parent, SLOT(removal(KTextEditor::Document*,KTextEditor::Range,QString))); QObject::connect(changesView, SIGNAL(activated(QModelIndex)), parent, SLOT(jump(QModelIndex))); } void ApplyChangesWidget::change (KTextEditor::Document *document, const KTextEditor::Range &, const KTextEditor::Range &newRange) { d->addItem(d->m_changes[d->m_index], document, newRange, i18n("Change")); } void ApplyChangesWidget::insertion(KTextEditor::Document *document, const KTextEditor::Range &range) { d->addItem(d->m_changes[d->m_index], document, range, i18n("Insert")); } void ApplyChangesWidget::removal(KTextEditor::Document *document, const KTextEditor::Range &range, const QString &oldText) { d->addItem(d->m_changes[d->m_index], document, KTextEditor::Range(range.start(), range.start()), i18n("Remove"), oldText); } void ApplyChangesWidget::switchEditView() { if(d->m_kompare.widgetActive(d->m_index)) { //Chage into editPart d->m_editParts[d->m_index]->widget()->parentWidget()->setVisible(true); d->m_kompare.hideWidget(d->m_index); } else { d->m_editParts[d->m_index]->widget()->parentWidget()->setVisible(false); //Change into KomparePart d->m_kompare.compare(d->m_files[d->m_index], document()->text(), d->m_documentTabs->widget(d->m_index), d->m_index); } d->updateButtonLabel(); } void ApplyChangesWidget::indexChanged(int newIndex) { Q_ASSERT(newIndex != -1); d->m_index = newIndex; d->updateButtonLabel(); } void ApplyChangesWidget::updateDiffView(int index) { int prevIndex = d->m_index; d->m_index = index == -1 ? d->m_index : index; switchEditView(); switchEditView(); d->m_index = prevIndex; } } #include "applychangeswidget.moc" diff --git a/language/codegen/codedescription.cpp b/language/codegen/codedescription.cpp index 205e82726..9e62c2fd6 100644 --- a/language/codegen/codedescription.cpp +++ b/language/codegen/codedescription.cpp @@ -1,171 +1,171 @@ /* This file is part of KDevelop Copyright 2012 Miha Čančula This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "codedescription.h" #include #include #include #include #include #include #include using namespace KDevelop; /** * The access policy as a string, or an empty string * if the policy is set to default * * The DUChain must be locked when calling this function **/ QString accessPolicyName(const DeclarationPointer& declaration) { DUChainPointer member = declaration.dynamicCast(); if (member) { switch (member->accessPolicy()) { case Declaration::Private: return "private"; case Declaration::Protected: return "protected"; case Declaration::Public: return "public"; default: break; } } return QString(); } VariableDescription::VariableDescription() { } VariableDescription::VariableDescription(const QString& type, const QString& name) : name(name) , type(type) { } VariableDescription::VariableDescription(const DeclarationPointer& declaration) { DUChainReadLocker lock; if (declaration) { name = declaration->identifier().toString(); type = declaration->abstractType()->toString(); } access = accessPolicyName(declaration); } FunctionDescription::FunctionDescription() { } FunctionDescription::FunctionDescription(const QString& name, const VariableDescriptionList& arguments, const VariableDescriptionList& returnArguments) : name(name) , arguments(arguments) , returnArguments(returnArguments) { } FunctionDescription::FunctionDescription(const DeclarationPointer& declaration) { DUChainReadLocker lock; if (declaration) { name = declaration->identifier().toString(); DUContext* context = declaration->internalContext(); DUChainPointer function = declaration.dynamicCast(); if (function && function->internalFunctionContext()) { context = function->internalFunctionContext(); } DUChainPointer method = declaration.dynamicCast(); if (method) { isConstructor = method->isConstructor(); isDestructor = method->isDestructor(); isVirtual = method->isVirtual(); isStatic = method->isStatic(); isSlot = method->isSlot(); isSignal = method->isSignal(); } int i = 0; foreach (Declaration* arg, context->localDeclarations()) { VariableDescription var = VariableDescription(DeclarationPointer(arg)); if (function) { - var.value = function->defaultParameterForArgument(i).str(); + var.value = function->defaultParameterForArgument(i).toString(); kDebug() << var.name << var.value; } arguments << var; ++i; } FunctionType::Ptr functionType = declaration->abstractType().cast(); if (functionType) { isConst = (functionType->modifiers() & AbstractType::ConstModifier); } if (functionType && functionType->returnType()) { returnArguments << VariableDescription(functionType->returnType()->toString(), QString()); } access = accessPolicyName(declaration); } } QString FunctionDescription::returnType() const { if (returnArguments.isEmpty()) { return QString(); } return returnArguments.first().type; } ClassDescription::ClassDescription() { } ClassDescription::ClassDescription(const QString& name) : name(name) { } diff --git a/language/codegen/coderepresentation.cpp b/language/codegen/coderepresentation.cpp index c5b43be8b..16eb73121 100644 --- a/language/codegen/coderepresentation.cpp +++ b/language/codegen/coderepresentation.cpp @@ -1,412 +1,412 @@ /* Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "coderepresentation.h" #include #include #include #include #include #include #include #include namespace KDevelop { static bool onDiskChangesForbidden = false; QString CodeRepresentation::rangeText(const KTextEditor::Range& range) const { Q_ASSERT(range.end().line() < lines()); //Easier for single line ranges which should happen most of the time if(range.onSingleLine()) return QString( line( range.start().line() ).mid( range.start().column(), range.columnWidth() ) ); //Add up al the requested lines QString rangedText = line(range.start().line()).mid(range.start().column()); for(int i = range.start().line() + 1; i <= range.end().line(); ++i) rangedText += '\n' + ((i == range.end().line()) ? line(i).left(range.end().column()) : line(i)); return rangedText; } static void grepLine(const QString& identifier, const QString& lineText, int lineNumber, QVector& ret, bool surroundedByBoundary) { if (identifier.isEmpty()) return; int pos = 0; while(true) { pos = lineText.indexOf(identifier, pos); if(pos == -1) break; int start = pos; pos += identifier.length(); int end = pos; if(!surroundedByBoundary || ( (end == lineText.length() || !lineText[end].isLetterOrNumber() || lineText[end] != '_') && (start-1 < 0 || !lineText[start-1].isLetterOrNumber() || lineText[start-1] != '_')) ) { ret << SimpleRange(lineNumber, start, lineNumber, end); } } } //NOTE: this is ugly, but otherwise kate might remove tabs again :-/ // see also: https://bugs.kde.org/show_bug.cgi?id=291074 struct EditorDisableReplaceTabs { EditorDisableReplaceTabs(KTextEditor::Document* document) : m_iface(qobject_cast(document)), m_count(0) { } void start() { ++m_count; if( m_count > 1 ) return; if ( m_iface ) { m_oldReplaceTabs = m_iface->configValue( "replace-tabs" ); m_iface->setConfigValue( "replace-tabs", false ); } } void end() { --m_count; if( m_count > 0 ) return; Q_ASSERT( m_count == 0 ); if (m_iface) m_iface->setConfigValue("replace-tabs", m_oldReplaceTabs); } KTextEditor::ConfigInterface* m_iface; int m_count; QVariant m_oldReplaceTabs; }; class EditorCodeRepresentation : public DynamicCodeRepresentation { public: EditorCodeRepresentation(KTextEditor::Document* document) : m_document(document), m_replaceTabs(document) { m_url = IndexedString(m_document->url()); } virtual QVector< SimpleRange > grep ( const QString& identifier, bool surroundedByBoundary ) const { QVector< SimpleRange > ret; if (identifier.isEmpty()) return ret; for(int line = 0; line < m_document->lines(); ++line) grepLine(identifier, m_document->line(line), line, ret, surroundedByBoundary); return ret; } QString line(int line) const { if(line < 0 || line >= m_document->lines()) return QString(); return m_document->line(line); } virtual int lines() const { return m_document->lines(); } QString text() const { return m_document->text(); } bool setText(const QString& text) { startEdit(); bool ret = m_document->setText(text); endEdit(); ModificationRevision::clearModificationCache(m_url); return ret; } bool fileExists(){ return QFile(m_document->url().path()).exists(); } void startEdit() { m_document->startEditing(); m_replaceTabs.start(); } void endEdit() { m_document->endEditing(); m_replaceTabs.end(); } bool replace(const KTextEditor::Range& range, const QString& oldText, const QString& newText, bool ignoreOldText) { QString old = m_document->text(range); if(oldText != old && !ignoreOldText) { return false; } startEdit(); bool ret = m_document->replaceText(range, newText); endEdit(); ModificationRevision::clearModificationCache(m_url); return ret; } virtual QString rangeText(const KTextEditor::Range& range) const { return m_document->text(range); } private: KTextEditor::Document* m_document; IndexedString m_url; EditorDisableReplaceTabs m_replaceTabs; }; class FileCodeRepresentation : public CodeRepresentation { public: FileCodeRepresentation(const IndexedString& document) : m_document(document) { QString localFile(document.toUrl().toLocalFile()); QFile file( localFile ); if ( file.open(QIODevice::ReadOnly) ) { data = QString::fromLocal8Bit(file.readAll()); lineData = data.split('\n'); } m_exists = file.exists(); } QString line(int line) const { if(line < 0 || line >= lineData.size()) return QString(); return lineData.at(line); } virtual QVector< SimpleRange > grep ( const QString& identifier, bool surroundedByBoundary ) const { QVector< SimpleRange > ret; if (identifier.isEmpty()) return ret; for(int line = 0; line < lineData.count(); ++line) grepLine(identifier, lineData.at(line), line, ret, surroundedByBoundary); return ret; } virtual int lines() const { return lineData.count(); } QString text() const { return data; } bool setText(const QString& text) { Q_ASSERT(!onDiskChangesForbidden); QString localFile(m_document.toUrl().toLocalFile()); QFile file( localFile ); if ( file.open(QIODevice::WriteOnly) ) { QByteArray data = text.toLocal8Bit(); if(file.write(data) == data.size()) { ModificationRevision::clearModificationCache(m_document); return true; } } return false; } bool fileExists(){ return m_exists; } private: //We use QByteArray, because the column-numbers are measured in utf-8 IndexedString m_document; bool m_exists; QStringList lineData; QString data; }; class ArtificialStringData : public QSharedData { public: ArtificialStringData(const QString& data) { setData(data); } void setData(const QString& data) { m_data = data; m_lineData = m_data.split('\n'); } QString data() const { return m_data; } const QStringList& lines() const { return m_lineData; } private: QString m_data; QStringList m_lineData; }; class StringCodeRepresentation : public CodeRepresentation { public: StringCodeRepresentation(KSharedPtr _data) : data(_data) { Q_ASSERT(data); } QString line(int line) const { if(line < 0 || line >= data->lines().size()) return QString(); return data->lines().at(line); } virtual int lines() const { return data->lines().count(); } QString text() const { return data->data(); } bool setText(const QString& text) { data->setData(text); return true; } bool fileExists(){ return false; } virtual QVector< SimpleRange > grep ( const QString& identifier, bool surroundedByBoundary ) const { QVector< SimpleRange > ret; if (identifier.isEmpty()) return ret; for(int line = 0; line < data->lines().count(); ++line) grepLine(identifier, data->lines().at(line), line, ret, surroundedByBoundary); return ret; } private: KSharedPtr data; }; static QHash > artificialStrings; //Return the representation for the given URL if it exists, or an empty pointer otherwise KSharedPtr representationForUrl(const IndexedString& url) { if(artificialStrings.contains(url)) return artificialStrings[url]; else { - IndexedString constructedUrl(CodeRepresentation::artificialUrl(url.str())); + IndexedString constructedUrl(CodeRepresentation::artificialUrl(url.toString())); if(artificialStrings.contains(constructedUrl)) return artificialStrings[constructedUrl]; else return KSharedPtr(); } } bool artificialCodeRepresentationExists(const IndexedString& url) { return !representationForUrl(url).isNull(); } CodeRepresentation::Ptr createCodeRepresentation(const IndexedString& url) { if(artificialCodeRepresentationExists(url)) return CodeRepresentation::Ptr(new StringCodeRepresentation(representationForUrl(url))); IDocument* document = ICore::self()->documentController()->documentForUrl(url.toUrl()); if(document && document->textDocument()) return CodeRepresentation::Ptr(new EditorCodeRepresentation(document->textDocument())); else return CodeRepresentation::Ptr(new FileCodeRepresentation(url)); } void CodeRepresentation::setDiskChangesForbidden(bool changesForbidden) { onDiskChangesForbidden = changesForbidden; } KUrl CodeRepresentation::artificialUrl(const QString& name) { KUrl url(name); url.setScheme("artificial"); url.cleanPath(); return url; } InsertArtificialCodeRepresentation::InsertArtificialCodeRepresentation(const IndexedString& file, const QString& text) : m_file(file) { if(m_file.toUrl().isRelative()) { - m_file = IndexedString(CodeRepresentation::artificialUrl(file.str())); + m_file = IndexedString(CodeRepresentation::artificialUrl(file.toString())); int idx = 0; while(artificialStrings.contains(m_file)) { ++idx; - m_file = IndexedString(CodeRepresentation::artificialUrl(QString("%1_%2").arg(idx).arg(file.str()))); + m_file = IndexedString(CodeRepresentation::artificialUrl(QString("%1_%2").arg(idx).arg(file.toString()))); } } Q_ASSERT(!artificialStrings.contains(m_file)); artificialStrings.insert(m_file, KSharedPtr(new ArtificialStringData(text))); } IndexedString InsertArtificialCodeRepresentation::file() { return m_file; } InsertArtificialCodeRepresentation::~InsertArtificialCodeRepresentation() { Q_ASSERT(artificialStrings.contains(m_file)); artificialStrings.remove(m_file); } void InsertArtificialCodeRepresentation::setText(const QString& text) { Q_ASSERT(artificialStrings.contains(m_file)); artificialStrings[m_file]->setData(text); } QString InsertArtificialCodeRepresentation::text() { Q_ASSERT(artificialStrings.contains(m_file)); return artificialStrings[m_file]->data(); } } diff --git a/language/codegen/documentchangeset.cpp b/language/codegen/documentchangeset.cpp index a83163c95..9abbcc246 100644 --- a/language/codegen/documentchangeset.cpp +++ b/language/codegen/documentchangeset.cpp @@ -1,572 +1,572 @@ /* Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "documentchangeset.h" #include "coderepresentation.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KDevelop { typedef QList ChangesList; typedef QHash ChangesHash; struct DocumentChangeSetPrivate { DocumentChangeSet::ReplacementPolicy replacePolicy; DocumentChangeSet::FormatPolicy formatPolicy; DocumentChangeSet::DUChainUpdateHandling updatePolicy; DocumentChangeSet::ActivationPolicy activationPolicy; ChangesHash changes; QHash documentsRename; DocumentChangeSet::ChangeResult addChange(const DocumentChangePointer& change); DocumentChangeSet::ChangeResult replaceOldText(CodeRepresentation* repr, const QString& newText, const ChangesList& sortedChangesList); DocumentChangeSet::ChangeResult generateNewText(const IndexedString& file, ChangesList& sortedChanges, const CodeRepresentation* repr, QString& output); DocumentChangeSet::ChangeResult removeDuplicates(const IndexedString& file, ChangesList& filteredChanges); void formatChanges(); void updateFiles(); }; // Simple helpers to clear up code clutter namespace { inline bool changeIsValid(const DocumentChange& change, const QStringList& textLines) { return change.m_range.start <= change.m_range.end && change.m_range.end.line < textLines.size() && change.m_range.start.line >= 0 && change.m_range.start.column >= 0 && change.m_range.start.column <= textLines[change.m_range.start.line].length() && change.m_range.end.column >= 0 && change.m_range.end.column <= textLines[change.m_range.end.line].length() && change.m_range.start.line == change.m_range.end.line; } inline bool duplicateChanges(const DocumentChangePointer& previous, const DocumentChangePointer& current) { // Given the option of considering a duplicate two changes in the same range // but with different old texts to be ignored return previous->m_range == current->m_range && previous->m_newText == current->m_newText && (previous->m_oldText == current->m_oldText || (previous->m_ignoreOldText && current->m_ignoreOldText)); } } DocumentChangeSet::DocumentChangeSet() : d(new DocumentChangeSetPrivate) { d->replacePolicy = StopOnFailedChange; d->formatPolicy = AutoFormatChanges; d->updatePolicy = SimpleUpdate; d->activationPolicy = DoNotActivate; } DocumentChangeSet::DocumentChangeSet(const DocumentChangeSet& rhs) : d(new DocumentChangeSetPrivate(*rhs.d)) { } DocumentChangeSet& DocumentChangeSet::operator=(const DocumentChangeSet& rhs) { *d = *rhs.d; return *this; } DocumentChangeSet::~DocumentChangeSet() { delete d; } DocumentChangeSet::ChangeResult DocumentChangeSet::addChange(const DocumentChange& change) { return d->addChange(DocumentChangePointer(new DocumentChange(change))); } DocumentChangeSet::ChangeResult DocumentChangeSet::addChange(const DocumentChangePointer& change) { return d->addChange(change); } DocumentChangeSet::ChangeResult DocumentChangeSet::addDocumentRenameChange(const IndexedString& oldFile, const IndexedString& newname) { d->documentsRename.insert(oldFile, newname); return true; } DocumentChangeSet::ChangeResult DocumentChangeSetPrivate::addChange(const DocumentChangePointer& change) { if(change->m_range.start.line != change->m_range.end.line) { kWarning() << "Multi-line changes are not supported in DocumentChangeSet"; return DocumentChangeSet::ChangeResult("Multi-line ranges are not supported"); } changes[change->m_document].append(change); return true; } void DocumentChangeSet::setReplacementPolicy(DocumentChangeSet::ReplacementPolicy policy) { d->replacePolicy = policy; } void DocumentChangeSet::setFormatPolicy(DocumentChangeSet::FormatPolicy policy) { d->formatPolicy = policy; } void DocumentChangeSet::setUpdateHandling(DocumentChangeSet::DUChainUpdateHandling policy) { d->updatePolicy = policy; } void DocumentChangeSet::setActivationPolicy(DocumentChangeSet::ActivationPolicy policy) { d->activationPolicy = policy; } QHash DocumentChangeSet::temporaryCodeRepresentations() const { QHash ret; ChangeResult result(true); foreach(const IndexedString &file, d->changes.keys()) { CodeRepresentation::Ptr repr = createCodeRepresentation(file); if(!repr) { continue; } ChangesList sortedChangesList; result = d->removeDuplicates(file, sortedChangesList); if (!result) { continue; } QString newText; result = d->generateNewText(file, sortedChangesList, repr.data(), newText); if (!result) { continue; } InsertArtificialCodeRepresentationPointer code( new InsertArtificialCodeRepresentation(IndexedString(file.toUrl().fileName()), newText) ); ret.insert(file, code); } return ret; } DocumentChangeSet::ChangeResult DocumentChangeSet::applyAllChanges() { KUrl oldActiveDoc; if (IDocument* activeDoc = ICore::self()->documentController()->activeDocument()) { oldActiveDoc = activeDoc->url(); } // rename files QHash::const_iterator it = d->documentsRename.constBegin(); for(; it != d->documentsRename.constEnd(); ++it) { KUrl url = it.key().toUrl(); IProject* p = ICore::self()->projectController()->findProjectForUrl(url); if(p) { QList files = p->filesForUrl(url); if(!files.isEmpty()) { - ProjectBaseItem::RenameStatus renamed = files.first()->rename(it.value().str()); + ProjectBaseItem::RenameStatus renamed = files.first()->rename(it.value().toString()); if(renamed == ProjectBaseItem::RenameOk) { - const KUrl newUrl(url.upUrl(), it.value().str()); + const KUrl newUrl(url.upUrl(), it.value().toString()); if (url == oldActiveDoc) { oldActiveDoc = newUrl; } IndexedString idxNewDoc(newUrl); // ensure changes operate on new file name ChangesHash::iterator iter = d->changes.find(it.key()); if (iter != d->changes.end()) { // copy changes ChangesList value = iter.value(); // remove old entry d->changes.erase(iter); // adapt to new url ChangesList::iterator itChange = value.begin(); ChangesList::iterator itEnd = value.end(); for(; itChange != itEnd; ++itChange) { (*itChange)->m_document = idxNewDoc; } d->changes[idxNewDoc] = value; } } else { ///FIXME: share code with project manager for the error code string representation - return ChangeResult(i18n("Could not rename '%1' to '%2'", url.pathOrUrl(), it.value().str())); + return ChangeResult(i18n("Could not rename '%1' to '%2'", url.pathOrUrl(), it.value().toString())); } } else { //TODO: do it outside the project management? kWarning() << "tried to rename file not tracked by project - not implemented"; } } else { kWarning() << "tried to rename a file outside of a project - not implemented"; } } QMap codeRepresentations; QMap newTexts; ChangesHash filteredSortedChanges; ChangeResult result(true); QList files(d->changes.keys()); foreach(const IndexedString &file, files) { CodeRepresentation::Ptr repr = createCodeRepresentation(file); if(!repr) { - return ChangeResult(QString("Could not create a Representation for %1").arg(file.str())); + return ChangeResult(QString("Could not create a Representation for %1").arg(file.toString())); } codeRepresentations[file] = repr; QList& sortedChangesList(filteredSortedChanges[file]); { result = d->removeDuplicates(file, sortedChangesList); if(!result) return result; } { result = d->generateNewText(file, sortedChangesList, repr.data(), newTexts[file]); if(!result) return result; } } QMap oldTexts; //Apply the changes to the files foreach(const IndexedString &file, files) { oldTexts[file] = codeRepresentations[file]->text(); result = d->replaceOldText(codeRepresentations[file].data(), newTexts[file], filteredSortedChanges[file]); if(!result && d->replacePolicy == StopOnFailedChange) { //Revert all files foreach(const IndexedString &revertFile, oldTexts.keys()) { codeRepresentations[revertFile]->setText(oldTexts[revertFile]); } return result; } } d->updateFiles(); if(d->activationPolicy == Activate) { foreach(const IndexedString& file, files) { ICore::self()->documentController()->openDocument(file.toUrl()); } } // ensure the old document is still activated if (oldActiveDoc.isValid()) { ICore::self()->documentController()->openDocument(oldActiveDoc); } return result; } DocumentChangeSet::ChangeResult DocumentChangeSetPrivate::replaceOldText(CodeRepresentation* repr, const QString& newText, const ChangesList& sortedChangesList) { DynamicCodeRepresentation* dynamic = dynamic_cast(repr); if(dynamic) { dynamic->startEdit(); //Replay the changes one by one for(int pos = sortedChangesList.size()-1; pos >= 0; --pos) { const DocumentChange& change(*sortedChangesList[pos]); if(!dynamic->replace(change.m_range.textRange(), change.m_oldText, change.m_newText, change.m_ignoreOldText)) { QString warningString = QString("Inconsistent change in %1 at %2:%3 -> %4:%5 = %6(encountered \"%7\") -> \"%8\"") - .arg(change.m_document.str()) + .arg(change.m_document.toString()) .arg(change.m_range.start.line) .arg(change.m_range.start.column) .arg(change.m_range.end.line) .arg(change.m_range.end.column) .arg(change.m_oldText) .arg(dynamic->rangeText(change.m_range.textRange())) .arg(change.m_newText); if(replacePolicy == DocumentChangeSet::WarnOnFailedChange) { kWarning() << warningString; } else if(replacePolicy == DocumentChangeSet::StopOnFailedChange) { dynamic->endEdit(); return DocumentChangeSet::ChangeResult(warningString); } //If set to ignore failed changes just continue with the others } } dynamic->endEdit(); return true; } //For files on disk if (!repr->setText(newText)) { QString warningString = QString("Could not replace text in the document: %1") - .arg(sortedChangesList.begin()->data()->m_document.str()); + .arg(sortedChangesList.begin()->data()->m_document.toString()); if(replacePolicy == DocumentChangeSet::WarnOnFailedChange) { kWarning() << warningString; } return DocumentChangeSet::ChangeResult(warningString); } return true; } DocumentChangeSet::ChangeResult DocumentChangeSetPrivate::generateNewText(const IndexedString & file, ChangesList& sortedChanges, const CodeRepresentation * repr, QString & output) { ISourceFormatter* formatter = 0; if(ICore::self()) { formatter = ICore::self()->sourceFormatterController()->formatterForUrl(file.toUrl()); } //Create the actual new modified file QStringList textLines = repr->text().split('\n'); KUrl url = file.toUrl(); KMimeType::Ptr mime = KMimeType::findByUrl(url); for(int pos = sortedChanges.size()-1; pos >= 0; --pos) { DocumentChange& change(*sortedChanges[pos]); QString encountered; if(changeIsValid(change, textLines) && //We demand this, although it should be fixed ((encountered = textLines[change.m_range.start.line].mid(change.m_range.start.column, change.m_range.end.column-change.m_range.start.column)) == change.m_oldText || change.m_ignoreOldText)) { ///Problem: This does not work if the other changes significantly alter the context @todo Use the changed context QString leftContext = QStringList(textLines.mid(0, change.m_range.start.line+1)).join("\n"); leftContext.chop(textLines[change.m_range.start.line].length() - change.m_range.start.column); QString rightContext = QStringList(textLines.mid(change.m_range.end.line)).join("\n").mid(change.m_range.end.column); if(formatter && (formatPolicy == DocumentChangeSet::AutoFormatChanges || formatPolicy == DocumentChangeSet::AutoFormatChangesKeepIndentation)) { QString oldNewText = change.m_newText; change.m_newText = formatter->formatSource(change.m_newText, url, mime, leftContext, rightContext); if(formatPolicy == DocumentChangeSet::AutoFormatChangesKeepIndentation) { // Reproduce the previous indentation QStringList oldLines = oldNewText.split('\n'); QStringList newLines = change.m_newText.split('\n'); if(oldLines.size() == newLines.size()) { for(int line = 0; line < newLines.size(); ++line) { // Keep the previous indentation QString oldIndentation; for (int a = 0; a < oldLines[line].size(); ++a) { if (oldLines[line][a].isSpace()) { oldIndentation.append(oldLines[line][a]); } else { break; } } int newIndentationLength = 0; for(int a = 0; a < newLines[line].size(); ++a) { if(newLines[line][a].isSpace()) { newIndentationLength = a; } else { break; } } newLines[line].replace(0, newIndentationLength, oldIndentation); } change.m_newText = newLines.join("\n"); } else { kDebug() << "Cannot keep the indentation because the line count has changed" << oldNewText; } } } textLines[change.m_range.start.line].replace(change.m_range.start.column, change.m_range.end.column-change.m_range.start.column, change.m_newText); }else{ QString warningString = QString("Inconsistent change in %1 at %2:%3 -> %4:%5" " = \"%6\"(encountered \"%7\") -> \"%8\"") - .arg(file.str()) + .arg(file.toString()) .arg(change.m_range.start.line) .arg(change.m_range.start.column) .arg(change.m_range.end.line) .arg(change.m_range.end.column) .arg(change.m_oldText) .arg(encountered) .arg(change.m_newText); if(replacePolicy == DocumentChangeSet::IgnoreFailedChange) { //Just don't do the replacement } else if(replacePolicy == DocumentChangeSet::WarnOnFailedChange) { kWarning() << warningString; } else { return DocumentChangeSet::ChangeResult(warningString, sortedChanges[pos]); } } } output = textLines.join("\n"); return true; } //Removes all duplicate changes for a single file, and then returns (via filteredChanges) the filtered duplicates DocumentChangeSet::ChangeResult DocumentChangeSetPrivate::removeDuplicates(const IndexedString& file, ChangesList& filteredChanges) { typedef QMultiMap ChangesMap; ChangesMap sortedChanges; foreach(const DocumentChangePointer &change, changes[file]) { sortedChanges.insert(change->m_range.end, change); } //Remove duplicates ChangesMap::iterator previous = sortedChanges.begin(); for(ChangesMap::iterator it = ++sortedChanges.begin(); it != sortedChanges.end(); ) { if(( *previous ) && ( *previous )->m_range.end > (*it)->m_range.start) { //intersection if(duplicateChanges(( *previous ), *it)) { //duplicate, remove one it = sortedChanges.erase(it); continue; } //When two changes contain each other, and the container change is set to ignore old text, then it should be safe to //just ignore the contained change, and apply the bigger change else if((*it)->m_range.contains(( *previous )->m_range) && (*it)->m_ignoreOldText ) { kDebug() << "Removing change: " << ( *previous )->m_oldText << "->" << ( *previous )->m_newText << ", because it is contained by change: " << (*it)->m_oldText << "->" << (*it)->m_newText; sortedChanges.erase(previous); } //This case is for when both have the same end, either of them could be the containing range else if((*previous)->m_range.contains((*it)->m_range) && (*previous)->m_ignoreOldText ) { kDebug() << "Removing change: " << (*it)->m_oldText << "->" << (*it)->m_newText << ", because it is contained by change: " << ( *previous )->m_oldText << "->" << ( *previous )->m_newText; it = sortedChanges.erase(it); continue; } else { return DocumentChangeSet::ChangeResult( QString("Inconsistent change-request at %1; " "intersecting changes: " "\"%2\"->\"%3\"@%4:%5->%6:%7 & \"%8\"->\"%9\"@%10:%11->%12:%13 ") - .arg(file.str(), ( *previous )->m_oldText, ( *previous )->m_newText) + .arg(file.toString(), ( *previous )->m_oldText, ( *previous )->m_newText) .arg(( *previous )->m_range.start.line) .arg(( *previous )->m_range.start.column) .arg(( *previous )->m_range.end.line) .arg(( *previous )->m_range.end.column) .arg((*it)->m_oldText, (*it)->m_newText) .arg((*it)->m_range.start.line) .arg((*it)->m_range.start.column) .arg((*it)->m_range.end.line) .arg((*it)->m_range.end.column)); } } previous = it; ++it; } filteredChanges = sortedChanges.values(); return true; } void DocumentChangeSetPrivate::updateFiles() { ModificationRevisionSet::clearCache(); foreach(const IndexedString& file, changes.keys()) { ModificationRevision::clearModificationCache(file); } if(updatePolicy != DocumentChangeSet::NoUpdate && ICore::self()) { // The active document should be updated first, so that the user sees the results instantly if(IDocument* activeDoc = ICore::self()->documentController()->activeDocument()) { ICore::self()->languageController()->backgroundParser()->addDocument(IndexedString(activeDoc->url())); } // If there are currently open documents that now need an update, update them too foreach(const IndexedString& doc, ICore::self()->languageController()->backgroundParser()->managedDocuments()) { DUChainReadLocker lock(DUChain::lock()); TopDUContext* top = DUChainUtils::standardContextForUrl(doc.toUrl(), true); if((top && top->parsingEnvironmentFile() && top->parsingEnvironmentFile()->needsUpdate()) || !top) { lock.unlock(); ICore::self()->languageController()->backgroundParser()->addDocument(doc); } } // Eventually update _all_ affected files foreach(const IndexedString &file, changes.keys()) { if(!file.toUrl().isValid()) { kWarning() << "Trying to apply changes to an invalid document"; continue; } ICore::self()->languageController()->backgroundParser()->addDocument(file); } } } } diff --git a/language/codegen/duchainchangeset.cpp b/language/codegen/duchainchangeset.cpp index 43e6be6ca..cad7d6fe0 100644 --- a/language/codegen/duchainchangeset.cpp +++ b/language/codegen/duchainchangeset.cpp @@ -1,72 +1,72 @@ /* Copyright 2008 Ramón Zarazúa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "duchainchangeset.h" namespace KDevelop { //DUChainRef::DUChainRef(DUChainChangeSet* set, DUChainBase* object, bool newObject) : // m_changeSet(set), m_object(object), m_objectRef(0), m_newObject(newObject) //{ //} DUChainChangeSet::DUChainChangeSet(ReferencedTopDUContext topContext) : m_topContext(topContext) { } DUChainChangeSet::~DUChainChangeSet() { foreach(DUChainRef * reference, m_objectRefs) delete reference; } DUChainChangeSet & DUChainChangeSet::operator<<(DUChainChangeSet & rhs) { //Avoid merging into self if(this == &rhs) return *this; Q_ASSERT(m_topContext == rhs.m_topContext); - kDebug() << "Merging ChangeSets for context:" << m_topContext.data()->url().str(); + kDebug() << "Merging ChangeSets for context:" << m_topContext->url(); m_objectRefs << rhs.m_objectRefs; rhs.m_objectRefs.clear(); #ifndef NDEBUG //check for possible duplicates qSort >(m_objectRefs); for(QList::iterator i = m_objectRefs.begin(); i < m_objectRefs.end() - 1; ++i ) Q_ASSERT(*i != *(i + 1)); #endif return *this; } QList DUChainChangeSet::objectRefs() const { return m_objectRefs; } const ReferencedTopDUContext & DUChainChangeSet::topDuContext() const { return m_topContext; } } diff --git a/language/codegen/templaterenderer.cpp b/language/codegen/templaterenderer.cpp index 6b612e286..0ae4a61c8 100644 --- a/language/codegen/templaterenderer.cpp +++ b/language/codegen/templaterenderer.cpp @@ -1,344 +1,344 @@ /* * This file is part of KDevelop * Copyright 2012 Miha Čančula * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "templaterenderer.h" #include "documentchangeset.h" #include "sourcefiletemplate.h" #include "templateengine.h" #include "templateengine_p.h" #include "archivetemplateloader.h" #include #include #include #include #include #include #include #include using namespace Grantlee; class NoEscapeStream : public OutputStream { public: NoEscapeStream(); explicit NoEscapeStream (QTextStream* stream); virtual QString escape (const QString& input) const; virtual QSharedPointer< OutputStream > clone (QTextStream* stream) const; }; NoEscapeStream::NoEscapeStream() : OutputStream() { } NoEscapeStream::NoEscapeStream(QTextStream* stream) : OutputStream (stream) { } QString NoEscapeStream::escape(const QString& input) const { return input; } QSharedPointer NoEscapeStream::clone(QTextStream* stream) const { QSharedPointer clonedStream = QSharedPointer( new NoEscapeStream( stream ) ); return clonedStream; } using namespace KDevelop; namespace KDevelop { class TemplateRendererPrivate { public: Engine* engine; Context context; TemplateRenderer::EmptyLinesPolicy emptyLinesPolicy; QString errorString; }; } TemplateRenderer::TemplateRenderer() : d(new TemplateRendererPrivate) { d->engine = &TemplateEngine::self()->d->engine; d->emptyLinesPolicy = KeepEmptyLines; } TemplateRenderer::~TemplateRenderer() { delete d; } void TemplateRenderer::addVariables(const QVariantHash& variables) { QVariantHash::const_iterator it = variables.constBegin(); QVariantHash::const_iterator end = variables.constEnd(); for (; it != end; ++it) { d->context.insert(it.key(), it.value()); } } void TemplateRenderer::addVariable(const QString& name, const QVariant& value) { d->context.insert(name, value); } QVariantHash TemplateRenderer::variables() const { return d->context.stackHash(0); } QString TemplateRenderer::render(const QString& content, const QString& name) const { #if WITH_SMART_TRIM Template t = d->engine->newTemplate(content, name); #else /* * This code re-implements the functionality of Grantlee's smart trim feature, which was added in 0.1.8 * It follows the description from http://www.grantlee.org/apidox/classGrantlee_1_1Engine.html#smart_trim * and passes the unit tests. */ const QStringList lines = content.split('\n'); QStringList trimmedLines; trimmedLines << QString(); for (QStringList::const_iterator it = lines.constBegin(); it != lines.constEnd(); ++it) { QString s = (*it); const QString t = s.trimmed(); if ( (t.startsWith("{%") && t.endsWith("%}") && t.count('{') == 1 && t.count('}') == 1) || (t.startsWith("{{") && t.endsWith("}}") && t.count('{') == 2 && t.count('}') == 2) || (t.startsWith("{#") && t.endsWith("#}") && t.count('{') == 1 && t.count('}') == 1) ) { trimmedLines.last() += t; } else { trimmedLines << (*it); } } if (trimmedLines.first().isEmpty()) { trimmedLines.removeFirst(); } Template t = d->engine->newTemplate(trimmedLines.join("\n"), name); #endif QString output; QTextStream textStream(&output); NoEscapeStream stream(&textStream); t->render(&stream, &d->context); if (t->error() != Grantlee::NoError) { d->errorString = t->errorString(); } else { d->errorString.clear(); } if (d->emptyLinesPolicy == TrimEmptyLines && output.contains('\n')) { QStringList lines = output.split('\n', QString::KeepEmptyParts); QMutableStringListIterator it(lines); // Remove empty lines from the start of the document while (it.hasNext()) { if (it.next().trimmed().isEmpty()) { it.remove(); } else { break; } } // Remove single empty lines it.toFront(); bool prePreviousEmpty = false; bool previousEmpty = false; while (it.hasNext()) { bool currentEmpty = it.peekNext().trimmed().isEmpty(); if (!prePreviousEmpty && previousEmpty && !currentEmpty) { it.remove(); } prePreviousEmpty = previousEmpty; previousEmpty = currentEmpty; it.next(); } // Compress multiple empty lines it.toFront(); previousEmpty = false; while (it.hasNext()) { bool currentEmpty = it.next().trimmed().isEmpty(); if (currentEmpty && previousEmpty) { it.remove(); } previousEmpty = currentEmpty; } // Remove empty lines from the end it.toBack(); while (it.hasPrevious()) { if (it.previous().trimmed().isEmpty()) { it.remove(); } else { break; } } // Add a newline to the end of file it.toBack(); it.insert(QString()); output = lines.join("\n"); } else if (d->emptyLinesPolicy == RemoveEmptyLines) { QStringList lines = output.split('\n', QString::SkipEmptyParts); QMutableStringListIterator it(lines); while (it.hasNext()) { if (it.next().trimmed().isEmpty()) { it.remove(); } } it.toBack(); if (lines.size() > 1) { it.insert(QString()); } output = lines.join("\n"); } return output; } QString TemplateRenderer::renderFile(const KUrl& url, const QString& name) const { QFile file(url.toLocalFile()); file.open(QIODevice::ReadOnly); QString content(file.readAll()); kDebug() << content; return render(content, name); } QStringList TemplateRenderer::render(const QStringList& contents) const { kDebug() << d->context.stackHash(0); QStringList ret; foreach (const QString& content, contents) { ret << render(content); } return ret; } void TemplateRenderer::setEmptyLinesPolicy(TemplateRenderer::EmptyLinesPolicy policy) { d->emptyLinesPolicy = policy; } TemplateRenderer::EmptyLinesPolicy TemplateRenderer::emptyLinesPolicy() const { return d->emptyLinesPolicy; } DocumentChangeSet TemplateRenderer::renderFileTemplate(const SourceFileTemplate& fileTemplate, const KUrl& baseUrl, const QHash& fileUrls) { DocumentChangeSet changes; KUrl url(baseUrl); url.adjustPath(KUrl::AddTrailingSlash); QRegExp nonAlphaNumeric("\\W"); for (QHash::const_iterator it = fileUrls.constBegin(); it != fileUrls.constEnd(); ++it) { QString cleanName = it.key().toLower(); cleanName.replace(nonAlphaNumeric, "_"); addVariable("output_file_" + cleanName, KUrl::relativeUrl(url, it.value())); addVariable("output_file_" + cleanName + "_absolute", it.value().toLocalFile()); } const KArchiveDirectory* directory = fileTemplate.directory(); ArchiveTemplateLocation location(directory); foreach (const SourceFileTemplate::OutputFile& outputFile, fileTemplate.outputFiles()) { const KArchiveEntry* entry = directory->entry(outputFile.fileName); if (!entry) { kDebug() << "Entry" << outputFile.fileName << "is mentioned in group" << outputFile.identifier << "but is not present in the archive"; continue; } const KArchiveFile* file = dynamic_cast(entry); if (!file) { kDebug() << "Entry" << entry->name() << "is not a file"; continue; } KUrl url = fileUrls[outputFile.identifier]; IndexedString document(url); SimpleRange range(SimpleCursor(0, 0), 0); DocumentChange change(document, range, QString(), render(file->data(), outputFile.identifier)); changes.addChange(change); - kDebug() << "Added change for file" << document.str(); + kDebug() << "Added change for file" << document; } return changes; } QString TemplateRenderer::errorString() const { return d->errorString; } diff --git a/language/duchain/classfunctiondeclaration.cpp b/language/duchain/classfunctiondeclaration.cpp index 9adcd2bb4..f607e638f 100644 --- a/language/duchain/classfunctiondeclaration.cpp +++ b/language/duchain/classfunctiondeclaration.cpp @@ -1,221 +1,221 @@ /* This is part of KDevelop Copyright 2002-2005 Roberto Raggi Copyright 2006 Adam Treat Copyright 2006 Hamish Rodda Copyright 2007-2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "classfunctiondeclaration.h" #include "ducontext.h" #include "types/functiontype.h" #include "duchainregister.h" namespace KDevelop { static Identifier& conversionIdentifier() { static Identifier conversionIdentifierObject("operator{...cast...}"); return conversionIdentifierObject; } REGISTER_DUCHAIN_ITEM(ClassFunctionDeclaration); ClassFunctionDeclaration::ClassFunctionDeclaration(const ClassFunctionDeclaration& rhs) : ClassFunctionDeclarationBase(*new ClassFunctionDeclarationData( *rhs.d_func() )) { } void ClassFunctionDeclaration::setAbstractType(AbstractType::Ptr type) { ///TODO: write testcase for typealias case which used to trigger this warning: /// typedef bool (*EventFilter)(void *message, long *result); /// in e.g. qcoreapplication.h:172 if(type && !dynamic_cast(type.unsafeData()) && type->whichType() != AbstractType::TypeAlias) { kWarning(9505) << "WARNING: Non-function type assigned to function declaration. Type is: " << type->toString() << "whichType:" << type->whichType() << "Declaration is:" << toString() - << topContext()->url().str() << range().castToSimpleRange(); + << topContext()->url() << range(); } ClassMemberDeclaration::setAbstractType(type); } DEFINE_LIST_MEMBER_HASH(ClassFunctionDeclarationData, m_defaultParameters, IndexedString) ClassFunctionDeclaration::ClassFunctionDeclaration(ClassFunctionDeclarationData& data) : ClassFunctionDeclarationBase(data) { } ClassFunctionDeclaration::ClassFunctionDeclaration(const RangeInRevision& range, DUContext* context) : ClassFunctionDeclarationBase(*new ClassFunctionDeclarationData, range) { d_func_dynamic()->setClassId(this); if( context ) setContext( context ); } ClassFunctionDeclaration::ClassFunctionDeclaration(ClassFunctionDeclarationData& data, const RangeInRevision& range, DUContext* context) : ClassFunctionDeclarationBase(data, range) { if( context ) setContext( context ); } Declaration* ClassFunctionDeclaration::clonePrivate() const { return new ClassFunctionDeclaration(*this); } ClassFunctionDeclaration::~ClassFunctionDeclaration() { } bool ClassFunctionDeclaration::isFunctionDeclaration() const { return true; } QString ClassFunctionDeclaration::toString() const { if( !abstractType() ) return ClassMemberDeclaration::toString(); TypePtr function = type(); if(function) { return QString("%1 %2 %3").arg(function->partToString( FunctionType::SignatureReturn )).arg(identifier().toString()).arg(function->partToString( FunctionType::SignatureArguments )); } else { QString type = abstractType() ? abstractType()->toString() : QString(""); kDebug(9505) << "A function has a bad type attached:" << type; return QString("invalid member-function %1 type %2").arg(identifier().toString()).arg(type); } } /*bool ClassFunctionDeclaration::isSimilar(KDevelop::CodeItem *other, bool strict ) const { if (!CppClassMemberType::isSimilar(other,strict)) return false; FunctionModelItem func = dynamic_cast(other); if (isConstant() != func->isConstant()) return false; if (arguments().count() != func->arguments().count()) return false; for (int i=0; itype() != arg2->type()) return false; } return true; }*/ uint setFlag(bool enable, uint flag, uint flags) { if(enable) return flags | flag; else return flags & (~flag); } bool ClassFunctionDeclaration::isAbstract() const { return d_func()->m_functionFlags & AbstractFunctionFlag; } void ClassFunctionDeclaration::setIsAbstract(bool abstract) { d_func_dynamic()->m_functionFlags = (ClassFunctionFlags)setFlag(abstract, AbstractFunctionFlag, d_func()->m_functionFlags); } bool ClassFunctionDeclaration::isFinal() const { return d_func()->m_functionFlags & FinalFunctionFlag; } void ClassFunctionDeclaration::setIsFinal(bool final) { d_func_dynamic()->m_functionFlags = (ClassFunctionFlags)setFlag(final, FinalFunctionFlag, d_func()->m_functionFlags); } bool ClassFunctionDeclaration::isSignal() const { return d_func()->m_functionFlags & FunctionSignalFlag; } void ClassFunctionDeclaration::setIsSignal(bool isSignal) { d_func_dynamic()->m_functionFlags = (ClassFunctionFlags)setFlag(isSignal, FunctionSignalFlag, d_func()->m_functionFlags); } bool ClassFunctionDeclaration::isSlot() const { return d_func()->m_functionFlags & FunctionSlotFlag; } void ClassFunctionDeclaration::setIsSlot(bool isSlot) { d_func_dynamic()->m_functionFlags = (ClassFunctionFlags)setFlag(isSlot, FunctionSlotFlag, d_func()->m_functionFlags); } bool ClassFunctionDeclaration::isConversionFunction() const { return identifier() == conversionIdentifier(); } bool ClassFunctionDeclaration::isConstructor() const { DUContext* ctx = context(); if (ctx && ctx->type() == DUContext::Class && ctx->localScopeIdentifier().top().nameEquals(identifier())) return true; return false; } bool ClassFunctionDeclaration::isDestructor() const { DUContext* ctx = context(); QString id = identifier().toString(); return ctx && ctx->type() == DUContext::Class && id.startsWith('~') && id.mid(1) == ctx->localScopeIdentifier().top().toString(); } uint ClassFunctionDeclaration::additionalIdentity() const { if(abstractType()) return abstractType()->hash(); else return 0; } const IndexedString* ClassFunctionDeclaration::defaultParameters() const { return d_func()->m_defaultParameters(); } unsigned int ClassFunctionDeclaration::defaultParametersSize() const { return d_func()->m_defaultParametersSize(); } void ClassFunctionDeclaration::addDefaultParameter(const IndexedString& str) { d_func_dynamic()->m_defaultParametersList().append(str); } void ClassFunctionDeclaration::clearDefaultParameters() { d_func_dynamic()->m_defaultParametersList().clear(); } } // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/language/duchain/dumpchain.cpp b/language/duchain/dumpchain.cpp index 2266e3aea..79ac5bc2b 100644 --- a/language/duchain/dumpchain.cpp +++ b/language/duchain/dumpchain.cpp @@ -1,181 +1,181 @@ /* This file is part of KDevelop Copyright 2002-2005 Roberto Raggi Copyright 2006 Hamish Rodda Copyright 2010 Milian Wolff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dumpchain.h" #include #include #include #include "ducontext.h" #include "topducontext.h" #include "declaration.h" #include "duchainpointer.h" #include "identifier.h" #include "use.h" #include "indexedstring.h" #include "functiondefinition.h" #include using namespace KDevelop; //BEGIN: private QTextStream globalOut(stdout); // use a QDebug to utilize operator<<() overloads // but don't use kDebug() to make sure we always print it, no matter what // is set in kdebugdialog QDebug qout(globalOut.device()); class DumpChain { public: DumpChain(); ~DumpChain(); void dump( DUContext * context, int allowedDepth ); private: int indent; TopDUContext* top; QSet had; }; DumpChain::DumpChain() : indent(0), top(0) { } DumpChain::~DumpChain( ) { } class Indent { private: int m_level; public: Indent(int level): m_level(level) {} friend QDebug operator<<(QDebug debug, const Indent& ind) { for (int i=0; itopContext()) { top = context->topContext(); if (!top->problems().isEmpty()) { qout << "PROBLEMS:" << endl; foreach(const ProblemPointer& p, top->problems()) { qout << p->description() << p->explanation() << p->finalLocation().textRange() << endl; } } } enum ContextType { Global /**< A context that declares functions, namespaces or classes */, Namespace /**< A context that declares namespace members */, Class /**< A context that declares class members */, Function /**< A context that declares function-arguments */, Template /**< A context that declares template-parameters */, Enum /**< A context that contains a list of enumerators */, Helper /**< A helper context, used for language-specific tweaks */, Other /**< Represents executable code, like for example within a compound-statement */ }; QString type; switch(context->type()) { case DUContext::Global: type = "Global"; break; case DUContext::Namespace: type = "Namespace"; break; case DUContext::Class: type = "Class"; break; case DUContext::Function: type = "Function"; break; case DUContext::Template: type = "Template"; break; case DUContext::Enum: type = "Enum"; break; case DUContext::Helper: type = "Helper"; break; case DUContext::Other: type = "Other"; break; } qout << QString(indent * 2, ' ') << (indent ? "==import==> Context " : "New Context ") << type << context << "\"" << context->localScopeIdentifier() << "\" [" << context->scopeIdentifier() << "]" << context->range().castToSimpleRange().textRange() << ' ' << (dynamic_cast(context) ? "top-context" : "") << endl; if( !context ) return; if (allowedDepth >= 0) { foreach (Declaration* dec, context->localDeclarations(top)) { //IdentifiedType* idType = dynamic_cast(dec->abstractType().data()); qout << QString((indent+1) * 2, ' ') << "Declaration: " << dec->toString() << /*(idType ? (" (type-identity: " + idType->identifier().toString() + ")") : QString()) <<*/ " [" << dec->qualifiedIdentifier() << "]" << dec << "(internal ctx" << dec->internalContext() << ")" << dec->range().castToSimpleRange().textRange() << "," << (dec->isDefinition() ? "defined, " : (FunctionDefinition::definition(dec) ? "" : "no definition, ")) << dec->uses().count() << "use(s)." << endl; if (FunctionDefinition::definition(dec)) { qout << QString((indent+1) * 2 + 1, ' ') << "Definition:" << FunctionDefinition::definition(dec)->range().castToSimpleRange().textRange() << endl; } QMap > uses = dec->uses(); for(QMap >::const_iterator it = uses.constBegin(); it != uses.constEnd(); ++it) { - qout << QString((indent+2) * 2, ' ') << "File:" << it.key().str() << endl; + qout << QString((indent+2) * 2, ' ') << "File:" << it.key() << endl; foreach (const RangeInRevision& range, *it) - qout << QString((indent+2) * 2+1, ' ') << "Use:" << range.castToSimpleRange().textRange() << endl; + qout << QString((indent+2) * 2+1, ' ') << "Use:" << range << endl; } } } else { qout << QString((indent+1) * 2, ' ') << context->localDeclarations(top).count() << "Declarations, " << context->childContexts().size() << "child-contexts" << endl; } ++indent; { foreach (const DUContext::Import &parent, context->importedParentContexts()) { DUContext* import = parent.context(top); if(!import) { qout << QString((indent+2) * 2+1, ' ') << "Could not get parent, is it registered in the DUChain?" << endl; continue; } if(had.contains(import)) { qout << QString((indent+2) * 2+1, ' ') << "skipping" << import->scopeIdentifier(true) << "because it was already printed" << endl; continue; } had.insert(import); dump(import, allowedDepth-1); } foreach (DUContext* child, context->childContexts()) dump(child, allowedDepth-1); } --indent; if(indent == 0) { top = 0; had.clear(); } } //END: private //BEGIN: public void KDevelop::dumpDUContext(DUContext* context, int allowedDepth) { DumpChain dumper; dumper.dump(context, allowedDepth); } diff --git a/language/duchain/dumpdotgraph.cpp b/language/duchain/dumpdotgraph.cpp index a57cc34a0..c41fc0755 100644 --- a/language/duchain/dumpdotgraph.cpp +++ b/language/duchain/dumpdotgraph.cpp @@ -1,200 +1,200 @@ /*************************************************************************** Copyright 2007 David Nolden ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "dumpdotgraph.h" #include "ducontext.h" #include "topducontext.h" #include "declaration.h" #include "duchainpointer.h" #include "parsingenvironment.h" #include "identifier.h" #include "functiondefinition.h" namespace KDevelop { QString shortLabel(KDevelop::DUContext* context) { return QString("q%1").arg((quint64)context); } QString shortLabel(KDevelop::Declaration* declaration) { return QString("q%1").arg((quint64)declaration); } QString rangeToString( const KTextEditor::Range& r ) { return QString("%1:%2->%3:%4").arg(r.start().line()).arg(r.start().column()).arg(r.end().line()).arg(r.end().column()); } class DumpDotGraphPrivate { public: QString dotGraphInternal(KDevelop::DUContext* contex, bool isMaster, bool shortened); void addDeclaration(QTextStream& stream, Declaration* decl); QMap m_hadVersions; QMap m_hadObjects; TopDUContext* m_topContext; }; QString DumpDotGraph::dotGraph(KDevelop::DUContext* context, bool shortened ) { d->m_hadObjects.clear(); d->m_hadVersions.clear(); d->m_topContext = context->topContext(); ///@todo maybe get this as a parameter return d->dotGraphInternal(context, true, shortened); } DumpDotGraph::DumpDotGraph() : d(new DumpDotGraphPrivate()) { } DumpDotGraph::~DumpDotGraph() { delete d; } void DumpDotGraphPrivate::addDeclaration(QTextStream& stream, Declaration* dec) { if( m_hadObjects.contains(dec) ) return; m_hadObjects[dec] = true; Declaration* declarationForDefinition = 0; if(dynamic_cast(dec)) declarationForDefinition = static_cast(dec)->declaration(m_topContext); if(!declarationForDefinition) { //Declaration stream << shortLabel(dec) << "[shape=distortion,label=\"" << dec->toString() << " [" << dec->qualifiedIdentifier().toString() << "]" << " " << rangeToString(dec->range().castToSimpleRange().textRange()) << "\"];\n"; stream << shortLabel(dec->context()) << " -> " << shortLabel(dec) << "[color=green];\n"; if( dec->internalContext() ) stream << shortLabel(dec) << " -> " << shortLabel(dec->internalContext()) << "[label=\"internal\", color=blue];\n"; }else{ //Definition stream << shortLabel(dec) << "[shape=regular,color=yellow,label=\"" << declarationForDefinition->toString() << " "<< rangeToString(dec->range().castToSimpleRange().textRange()) << "\"];\n"; stream << shortLabel(dec->context()) << " -> " << shortLabel(dec) << ";\n"; stream << shortLabel(dec) << " -> " << shortLabel(declarationForDefinition) << "[label=\"defines\",color=green];\n"; addDeclaration(stream, declarationForDefinition); if( dec->internalContext() ) stream << shortLabel(dec) << " -> " << shortLabel(dec->internalContext()) << "[label=\"internal\", color=blue];\n"; } } QString DumpDotGraphPrivate::dotGraphInternal(KDevelop::DUContext* context, bool isMaster, bool shortened) { if( m_hadObjects.contains(context) ) return QString(); m_hadObjects[context] = true; QTextStream stream; QString ret; stream.setString(&ret, QIODevice::WriteOnly); if( isMaster ) stream << "Digraph chain {\n"; QString shape = "parallelogram"; //QString shape = "box"; QString label = "unknown"; if( dynamic_cast(context) ) { TopDUContext* topCtx = static_cast(context); if( topCtx->parsingEnvironmentFile() ) { - QString file( topCtx->parsingEnvironmentFile()->url().str() ); + QString file( topCtx->parsingEnvironmentFile()->url().toString() ); KUrl url = KUrl(file); if(topCtx->parsingEnvironmentFile() && topCtx->parsingEnvironmentFile()->isProxyContext()) url.addPath("_[proxy]_"); //Find the context this one is derived from. If there is one, connect it with a line, and shorten the url. if( m_hadVersions.contains(url) ) { stream << shortLabel(context) << " -> " << m_hadVersions[url] << "[color=blue,label=\"version\"];\n"; file = KUrl(file).fileName(); } else { m_hadVersions[url] = shortLabel(context); } label = file; if( topCtx->importers().count() != 0 ) label += QString(" imported by %1").arg(topCtx->importers().count()); } else { label = "unknown file"; } if(topCtx->parsingEnvironmentFile() && topCtx->parsingEnvironmentFile()->isProxyContext()) label = "Proxy-context " + label; }else{ label = /*"context " + */context->localScopeIdentifier().toString(); label += ' ' + rangeToString(context->range().castToSimpleRange().textRange()); } //label = QString("%1 ").arg((size_t)context) + label; if( isMaster && !dynamic_cast(context) ) { //Also draw contexts that import this one foreach( DUContext* ctx, context->importers() ) stream << dotGraphInternal(ctx, false, true); } foreach (const DUContext::Import &parent, context->importedParentContexts()) { if( parent.context(m_topContext) ) { stream << dotGraphInternal(parent.context(m_topContext), false, true); QString label = "imports"; if( (!dynamic_cast(parent.context(m_topContext)) || !dynamic_cast(context)) && !(parent.context(m_topContext)->url() == context->url()) ) { - label += " from " + KUrl(parent.context(m_topContext)->url().str()).fileName() + " to " + KUrl(context->url().str()).fileName(); + label += " from " + parent.context(m_topContext)->url().toUrl().fileName() + " to " + context->url().toUrl().fileName(); } stream << shortLabel(context) << " -> " << shortLabel(parent.context(m_topContext)) << "[style=dotted,label=\"" << label << "\"];\n"; } } if( !context->childContexts().isEmpty() ) label += QString(", %1 C.").arg(context->childContexts().count()); if( !shortened ) { foreach (DUContext* child, context->childContexts()) { stream << dotGraphInternal(child, false, false); stream << shortLabel(context) << " -> " << shortLabel(child) << "[style=dotted,color=green];\n"; } } if( !context->localDeclarations().isEmpty() ) label += QString(", %1 D.").arg(context->localDeclarations().count()); if(!shortened ) { foreach (Declaration* dec, context->localDeclarations()) addDeclaration(stream, dec); } if( context->owner() ) { addDeclaration(stream, context->owner()); } stream << shortLabel(context) << "[shape=" << shape << ",label=\"" << label << "\"" << (isMaster ? QString("color=red") : QString("color=blue")) << "];\n"; if( isMaster ) stream << "}\n"; return ret; } } diff --git a/language/duchain/identifier.cpp b/language/duchain/identifier.cpp index 4cfc90eea..dc508d0d8 100644 --- a/language/duchain/identifier.cpp +++ b/language/duchain/identifier.cpp @@ -1,1289 +1,1289 @@ /* This file is part of KDevelop Copyright 2006 Hamish Rodda Copyright 2007-2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "identifier.h" #include #include "stringhelpers.h" #include "indexedstring.h" #include "appendedlist_static.h" #include "repositories/itemrepository.h" #define ifDebug(x) namespace KDevelop { template class IdentifierPrivate { public: IdentifierPrivate() : m_unique(0), m_refCount(0), m_hash(0) { } template IdentifierPrivate(const IdentifierPrivate& rhs) : m_unique(rhs.m_unique), m_identifier(rhs.m_identifier), m_refCount(0), m_hash(rhs.m_hash) { copyListsFrom(rhs); } ~IdentifierPrivate() { templateIdentifiersList.free(const_cast(templateIdentifiers())); } //Flags the stored hash-value invalid void clearHash() { //This is always called on an object private to an Identifier, so there is no threading-problem. Q_ASSERT(dynamic); m_hash = 0; } uint hash() const { //Since this only needs reading and the data needs not to be private, this may be called by multiple threads simultaneously, so computeHash() must be thread-safe. if( !m_hash && dynamic ) computeHash(); return m_hash; } int m_unique; IndexedString m_identifier; uint m_refCount; START_APPENDED_LISTS_STATIC(IdentifierPrivate) APPENDED_LIST_FIRST_STATIC(IndexedTypeIdentifier, templateIdentifiers) END_APPENDED_LISTS_STATIC(templateIdentifiers) unsigned int itemSize() const { return sizeof(IdentifierPrivate) + lastOffsetBehind(); } void computeHash() const { Q_ASSERT(dynamic); //this must stay thread-safe(may be called by multiple threads at a time) //The thread-safety is given because all threads will have the same result, and it will only be written once at the end. uint hash = m_identifier.hash(); FOREACH_FUNCTION_STATIC(const IndexedTypeIdentifier& templateIdentifier, templateIdentifiers) hash = hash * 13 + IndexedTypeIdentifier(templateIdentifier).hash(); hash += m_unique; m_hash = hash; } mutable uint m_hash; }; typedef IdentifierPrivate DynamicIdentifierPrivate; typedef IdentifierPrivate ConstantIdentifierPrivate; struct IdentifierItemRequest { IdentifierItemRequest(const DynamicIdentifierPrivate& identifier) : m_identifier(identifier) { identifier.hash(); //Make sure the hash is valid by calling this } enum { AverageSize = sizeof(IdentifierPrivate)+4 }; //Should return the hash-value associated with this request(For example the hash of a string) unsigned int hash() const { return m_identifier.hash(); } //Should return the size of an item created with createItem unsigned int itemSize() const { return m_identifier.itemSize(); } //Should create an item where the information of the requested item is permanently stored. The pointer //@param item equals an allocated range with the size of itemSize(). void createItem(ConstantIdentifierPrivate* item) const { new (item) ConstantIdentifierPrivate(m_identifier); } static bool persistent(const ConstantIdentifierPrivate* item) { return (bool)item->m_refCount; } static void destroy(ConstantIdentifierPrivate* item, KDevelop::AbstractItemRepository&) { item->~ConstantIdentifierPrivate(); } //Should return whether the here requested item equals the given item bool equals(const ConstantIdentifierPrivate* item) const { return item->m_hash == m_identifier.m_hash && item->m_unique == m_identifier.m_unique && item->m_identifier == m_identifier.m_identifier && m_identifier.listsEqual(*item); } const DynamicIdentifierPrivate& m_identifier; }; static RepositoryManager< ItemRepository, false>& identifierRepository() { static RepositoryManager< ItemRepository, false> identifierRepositoryObject("Identifier Repository"); return identifierRepositoryObject; } ///This has to be initialized now, else we will get a crash when multiple threads try accessing it for the first time in the same moment uint emptyConstantIdentifierPrivateIndex() { static uint index = identifierRepository()->index(DynamicIdentifierPrivate()); if(index == 0) ///Just so the function is instantiated right now identifierRepository()->deleteItem(0); return index; } const ConstantIdentifierPrivate* emptyConstantIdentifierPrivate() { return identifierRepository()->itemFromIndex(emptyConstantIdentifierPrivateIndex()); } bool IndexedIdentifier::isEmpty() const { return index == emptyConstantIdentifierPrivateIndex(); } ///Before something is modified in QualifiedIdentifierPrivate, it must be made sure that it is private to the QualifiedIdentifier it is used in(@see QualifiedIdentifier::prepareWrite) template class QualifiedIdentifierPrivate { public: QualifiedIdentifierPrivate() : m_explicitlyGlobal(false), m_isExpression(false), m_hash(0), m_refCount(0) { } template QualifiedIdentifierPrivate(const QualifiedIdentifierPrivate& rhs) : m_explicitlyGlobal(rhs.m_explicitlyGlobal) , m_isExpression(rhs.m_isExpression) , m_hash(rhs.m_hash) , m_refCount(0) { copyListsFrom(rhs); } ~QualifiedIdentifierPrivate() { identifiersList.free(const_cast(identifiers())); } bool m_explicitlyGlobal:1; bool m_isExpression:1; mutable uint m_hash; uint m_refCount; START_APPENDED_LISTS_STATIC(QualifiedIdentifierPrivate) APPENDED_LIST_FIRST_STATIC(IndexedIdentifier, identifiers) END_APPENDED_LISTS_STATIC(identifiers) unsigned int itemSize() const { return sizeof(QualifiedIdentifierPrivate) + lastOffsetBehind(); } //Constructs m_identifiers void splitIdentifiers( const QString& str, int start ) { Q_ASSERT(dynamic); uint currentStart = start; while( currentStart < (uint)str.length() ) { identifiersList.append(IndexedIdentifier(Identifier( str, currentStart, ¤tStart ))); while( currentStart < (uint)str.length() && (str[currentStart] == ' ' ) ) ++currentStart; currentStart += 2; //Skip "::" } } inline void clearHash() const { m_hash = 0; } uint hash() const { if( m_hash == 0 ) { uint mhash = 0; FOREACH_FUNCTION_STATIC( const IndexedIdentifier& identifier, identifiers ) mhash = 11*mhash + Identifier(identifier).hash(); if(mhash != m_hash) m_hash = mhash;//The local class may be in read-only memory, so only change m_hash if it's actually a change } return m_hash; } }; typedef QualifiedIdentifierPrivate DynamicQualifiedIdentifierPrivate; typedef QualifiedIdentifierPrivate ConstantQualifiedIdentifierPrivate; struct QualifiedIdentifierItemRequest { QualifiedIdentifierItemRequest(const DynamicQualifiedIdentifierPrivate& identifier) : m_identifier(identifier) { identifier.hash(); //Make sure the hash is valid by calling this } enum { AverageSize = sizeof(QualifiedIdentifierPrivate)+8 }; //Should return the hash-value associated with this request(For example the hash of a string) unsigned int hash() const { return m_identifier.hash(); } //Should return the size of an item created with createItem unsigned int itemSize() const { return m_identifier.itemSize(); } //Should create an item where the information of the requested item is permanently stored. The pointer //@param item equals an allocated range with the size of itemSize(). void createItem(ConstantQualifiedIdentifierPrivate* item) const { Q_ASSERT(shouldDoDUChainReferenceCounting(item)); Q_ASSERT(shouldDoDUChainReferenceCounting(((char*)item) + (itemSize()-1))); new (item) ConstantQualifiedIdentifierPrivate(m_identifier); } static bool persistent(const ConstantQualifiedIdentifierPrivate* item) { return (bool)item->m_refCount; } static void destroy(ConstantQualifiedIdentifierPrivate* item, KDevelop::AbstractItemRepository&) { Q_ASSERT(shouldDoDUChainReferenceCounting(item)); item->~ConstantQualifiedIdentifierPrivate(); } //Should return whether the here requested item equals the given item bool equals(const ConstantQualifiedIdentifierPrivate* item) const { return item->m_explicitlyGlobal == m_identifier.m_explicitlyGlobal && item->m_isExpression == m_identifier.m_isExpression && item->m_hash == m_identifier.m_hash && m_identifier.listsEqual(*item); } const DynamicQualifiedIdentifierPrivate& m_identifier; }; AbstractRepositoryManager* returnIdentifierRepository() { return &identifierRepository(); } static RepositoryManager< ItemRepository, false>& qualifiedidentifierRepository() { static RepositoryManager< ItemRepository, false> qualifiedidentifierRepositoryObject("Qualified Identifier Repository", 1, &returnIdentifierRepository); return qualifiedidentifierRepositoryObject; } uint emptyConstantQualifiedIdentifierPrivateIndex() { static uint index = qualifiedidentifierRepository()->index(DynamicQualifiedIdentifierPrivate()); if(index == 0) ///Just so the function is instantiated right now identifierRepository()->deleteItem(0); return index; } const ConstantQualifiedIdentifierPrivate* emptyConstantQualifiedIdentifierPrivate() { return qualifiedidentifierRepository()->itemFromIndex(emptyConstantQualifiedIdentifierPrivateIndex()); } // uint QualifiedIdentifier::combineHash(uint leftHash, uint /*leftSize*/, Identifier appendIdentifier) { // return 11*leftHash + appendIdentifier.hash(); // } Identifier::Identifier(const Identifier& rhs) { rhs.makeConstant(); cd = rhs.cd; m_index = rhs.m_index; } Identifier::Identifier(uint index) : m_index(index) { Q_ASSERT(m_index); cd = identifierRepository()->itemFromIndex(index); } Identifier::~Identifier() { if(!m_index) delete dd; } bool Identifier::nameEquals(const Identifier& rhs) const { return identifier() == rhs.identifier(); } uint Identifier::hash() const { if(!m_index) return dd->hash(); else return cd->hash(); } bool Identifier::isEmpty() const { if(!m_index) return dd->m_identifier.isEmpty() && dd->m_unique == 0 && dd->templateIdentifiersSize() == 0; else return cd->m_identifier.isEmpty() && cd->m_unique == 0 && cd->templateIdentifiersSize() == 0; } Identifier::Identifier(const IndexedString& str) : m_index(0), dd(new IdentifierPrivate) { dd->m_identifier = str; } Identifier::Identifier(const QString& id, uint start, uint* takenRange) : m_index(0), dd(new IdentifierPrivate) { ///Extract template-parameters ParamIterator paramIt("<>:", id, start); dd->m_identifier = IndexedString(paramIt.prefix().trimmed()); while( paramIt ) { appendTemplateIdentifier( IndexedTypeIdentifier(IndexedQualifiedIdentifier(QualifiedIdentifier(*paramIt))) ); ++paramIt; } if( takenRange ) *takenRange = paramIt.position(); } Identifier::Identifier() : m_index(emptyConstantIdentifierPrivateIndex()), cd(emptyConstantIdentifierPrivate()) { } Identifier Identifier::unique(int token) { Identifier ret; ret.setUnique(token); return ret; } bool Identifier::isUnique() const { if(!m_index) return dd->m_unique; else return cd->m_unique; } int Identifier::uniqueToken() const { if(!m_index) return dd->m_unique; else return cd->m_unique; } void Identifier::setUnique(int token) { prepareWrite(); dd->m_unique = token; } const IndexedString Identifier::identifier() const { if(!m_index) return dd->m_identifier; else return cd->m_identifier; } void Identifier::setIdentifier(const QString& identifier) { prepareWrite(); dd->m_identifier = IndexedString(identifier); } void Identifier::setIdentifier(const IndexedString& identifier) { prepareWrite(); dd->m_identifier = identifier; } IndexedTypeIdentifier Identifier::templateIdentifier(int num) const { if(!m_index) return dd->templateIdentifiers()[num]; else return cd->templateIdentifiers()[num]; } uint Identifier::templateIdentifiersCount() const { if(!m_index) return dd->templateIdentifiersSize(); else return cd->templateIdentifiersSize(); } void Identifier::appendTemplateIdentifier(const IndexedTypeIdentifier& identifier) { prepareWrite(); dd->templateIdentifiersList.append(identifier); } void Identifier::clearTemplateIdentifiers() { prepareWrite(); dd->templateIdentifiersList.clear(); } uint Identifier::index() const { makeConstant(); Q_ASSERT(m_index); return m_index; } void Identifier::setTemplateIdentifiers(const QList& templateIdentifiers) { prepareWrite(); dd->templateIdentifiersList.clear(); foreach(const IndexedTypeIdentifier& id, templateIdentifiers) dd->templateIdentifiersList.append(id); } QString Identifier::toString() const { if (!this) { return "(null identifier)"; } - QString ret = identifier().str(); + QString ret = identifier().toString(); /* if(isUnique()) ret += "unique";*/ if (templateIdentifiersCount()) { ret.append("< "); for (uint i = 0; i < templateIdentifiersCount(); ++i) { ret.append(templateIdentifier(i).toString()); if (i != templateIdentifiersCount() - 1) ret.append(", "); } ret.append(" >"); } return ret; } bool Identifier::operator==(const Identifier& rhs) const { return index() == rhs.index(); } bool Identifier::operator!=(const Identifier& rhs) const { return !operator==(rhs); } Identifier& Identifier::operator=(const Identifier& rhs) { if(dd == rhs.dd && cd == rhs.cd) return *this; if(!m_index) delete dd; dd = 0; rhs.makeConstant(); cd = rhs.cd; m_index = rhs.m_index; Q_ASSERT(cd); return *this; } uint QualifiedIdentifier::index() const { makeConstant(); Q_ASSERT(m_index); return m_index; } void Identifier::makeConstant() const { if(m_index) return; m_index = identifierRepository()->index( IdentifierItemRequest(*dd) ); delete dd; cd = identifierRepository()->itemFromIndex( m_index ); } void Identifier::prepareWrite() { if(m_index) { const IdentifierPrivate* oldCc = cd; dd = new IdentifierPrivate; dd->m_hash = oldCc->m_hash; dd->m_unique = oldCc->m_unique; dd->m_identifier = oldCc->m_identifier; dd->copyListsFrom(*oldCc); m_index = 0; } dd->clearHash(); } bool QualifiedIdentifier::inRepository() const { if(m_index) return true; else return (bool)qualifiedidentifierRepository()->findIndex( QualifiedIdentifierItemRequest(*dd) ); } QualifiedIdentifier::QualifiedIdentifier(uint index) : m_index(index), cd( qualifiedidentifierRepository()->itemFromIndex(index) ) { } QualifiedIdentifier::QualifiedIdentifier(const QString& id, bool isExpression) : m_index(0), dd(new DynamicQualifiedIdentifierPrivate) { if(isExpression) { setIsExpression(true); if(!id.isEmpty()) { //Prevent tokenization, since we may lose information there Identifier finishedId; finishedId.setIdentifier(id); push(finishedId); } }else{ if (id.startsWith("::")) { dd->m_explicitlyGlobal = true; dd->splitIdentifiers(id, 2); } else { dd->m_explicitlyGlobal = false; dd->splitIdentifiers(id, 0); } } } QualifiedIdentifier::QualifiedIdentifier(const Identifier& id) : m_index(0), dd(new DynamicQualifiedIdentifierPrivate) { - if (id.dd->m_identifier.str().isEmpty()) { + if (id.dd->m_identifier.isEmpty()) { dd->m_explicitlyGlobal = true; } else { dd->m_explicitlyGlobal = false; dd->identifiersList.append(IndexedIdentifier(id)); } } QualifiedIdentifier::QualifiedIdentifier() : m_index(emptyConstantQualifiedIdentifierPrivateIndex()), cd(emptyConstantQualifiedIdentifierPrivate()) { } QualifiedIdentifier::~QualifiedIdentifier() { if(!m_index) delete dd; } QualifiedIdentifier::QualifiedIdentifier(const QualifiedIdentifier& id) { if(id.m_index) { m_index = id.m_index; cd = id.cd; }else{ m_index = 0; dd = new QualifiedIdentifierPrivate(*id.dd); } } QStringList QualifiedIdentifier::toStringList() const { QStringList ret; if (explicitlyGlobal()) ret.append(QString()); if(m_index) { FOREACH_FUNCTION_STATIC(const IndexedIdentifier& index, cd->identifiers) ret << index.identifier().toString(); }else{ FOREACH_FUNCTION_STATIC(const IndexedIdentifier& index, dd->identifiers) ret << index.identifier().toString(); } return ret; } QString QualifiedIdentifier::toString(bool ignoreExplicitlyGlobal) const { QString ret; if( !ignoreExplicitlyGlobal && explicitlyGlobal() ) ret = "::"; bool first = true; if(m_index) { FOREACH_FUNCTION_STATIC(const IndexedIdentifier& index, cd->identifiers) { if( !first ) ret += "::"; else first = false; ret += index.identifier().toString(); } }else{ FOREACH_FUNCTION_STATIC(const IndexedIdentifier& index, dd->identifiers) { if( !first ) ret += "::"; else first = false; ret += index.identifier().toString(); } } return ret; } QualifiedIdentifier QualifiedIdentifier::merge(const QualifiedIdentifier& base) const { QualifiedIdentifier ret(base); ret.prepareWrite(); if(m_index) ret.dd->identifiersList.append(cd->identifiers(), cd->identifiersSize()); else ret.dd->identifiersList.append(dd->identifiers(), dd->identifiersSize()); if( explicitlyGlobal() ) ret.setExplicitlyGlobal(true); return ret; } QualifiedIdentifier QualifiedIdentifier::operator+(const QualifiedIdentifier& rhs) const { return rhs.merge(*this); } QualifiedIdentifier& QualifiedIdentifier::operator+=(const QualifiedIdentifier& rhs) { push(rhs); return *this; } QualifiedIdentifier QualifiedIdentifier::operator+(const Identifier& rhs) const { QualifiedIdentifier ret(*this); ret.push(rhs); return ret; } QualifiedIdentifier& QualifiedIdentifier::operator+=(const Identifier& rhs) { push(rhs); return *this; } // QualifiedIdentifier QualifiedIdentifier::strip(const QualifiedIdentifier & unwantedBase) const // { // // Don't strip the top identifier // if (count() <= unwantedBase.count()) // return *this; // // //Make sure this one starts with unwantedBase // for( int a = 0; a < unwantedBase.count(); a++ ) // if( d->m_identifiers[a] != unwantedBase.d->m_identifiers[a] ) // return *this; // // // QualifiedIdentifier ret; // ret.setExplicitlyGlobal(false); // ret.prepareWrite(); // // int remove = unwantedBase.d->m_identifiers.count(); // // ret.d->m_identifiers.append(&d->m_identifiers[remove], d->m_identifiers.size() - remove); // // return ret; // } bool QualifiedIdentifier::isExpression() const { if(m_index) return cd->m_isExpression; else return dd->m_isExpression; } void QualifiedIdentifier::setIsExpression(bool is) { prepareWrite(); dd->m_isExpression = is; } bool QualifiedIdentifier::explicitlyGlobal() const { // True if started with "::" if(m_index) return cd->m_explicitlyGlobal; else return dd->m_explicitlyGlobal; } void QualifiedIdentifier::setExplicitlyGlobal(bool eg) { prepareWrite(); dd->m_explicitlyGlobal = eg; } bool QualifiedIdentifier::sameIdentifiers(const QualifiedIdentifier& rhs) const { if(m_index && rhs.m_index) return cd->listsEqual(*rhs.cd); else if(m_index && !rhs.m_index) return cd->listsEqual(*rhs.dd); else if(!m_index && !rhs.m_index) return dd->listsEqual(*rhs.dd); else return dd->listsEqual(*rhs.cd); } // bool QualifiedIdentifier::isSame(const QualifiedIdentifier& rhs, bool ignoreExplicitlyGlobal) const // { // if( cd == rhs.cd ) // return true; // // if (!ignoreExplicitlyGlobal && (explicitlyGlobal() != rhs.explicitlyGlobal())) // return false; // // if( isExpression() != rhs.isExpression() ) // return false; // // if( hash() != rhs.hash() ) // return false; // // return sameIdentifiers(rhs); // } bool QualifiedIdentifier::operator==(const QualifiedIdentifier& rhs) const { if( cd == rhs.cd ) return true; return hash() == rhs.hash() && sameIdentifiers(rhs); } bool QualifiedIdentifier::operator!=(const QualifiedIdentifier& rhs) const { return !operator==(rhs); } QualifiedIdentifier& QualifiedIdentifier::operator=(const QualifiedIdentifier& rhs) { if(!m_index) delete dd; rhs.makeConstant(); cd = rhs.cd; m_index = rhs.m_index; return *this; } bool QualifiedIdentifier::beginsWith(const QualifiedIdentifier& other) const { uint c = count(); uint oc = other.count(); for (uint i = 0; i < c && i < oc; ++i) if (at(i) == other.at(i)) { continue; } else { return false; } return true; } struct Visitor { Visitor(KDevVarLengthArray& _target, uint _hash) : target(_target), hash(_hash) { } bool operator()(const ConstantQualifiedIdentifierPrivate* item, uint index) const { if(item->m_hash == hash) target.append(QualifiedIdentifier(index)); return true; } KDevVarLengthArray& target; uint hash; }; void QualifiedIdentifier::findByHash(HashType hash, KDevVarLengthArray& target) { Visitor v(target, hash); qualifiedidentifierRepository()->visitItemsWithHash(v, hash); } uint QualifiedIdentifier::hash() const { if(m_index) return cd->hash(); else return dd->hash(); } uint qHash(const IndexedTypeIdentifier& id) { return id.hash(); } uint qHash(const QualifiedIdentifier& id) { return id.hash(); } uint qHash(const Identifier& id) { return id.hash(); } bool QualifiedIdentifier::isQualified() const { return count() > 1 || explicitlyGlobal(); } void QualifiedIdentifier::push(const Identifier& id) { if(id.isEmpty()) return; prepareWrite(); dd->identifiersList.append(IndexedIdentifier(id)); } void QualifiedIdentifier::push(const QualifiedIdentifier& id) { if(id.isEmpty()) return; prepareWrite(); id.makeConstant(); dd->identifiersList.append(id.cd->identifiers(), id.cd->identifiersSize()); } void QualifiedIdentifier::pop() { prepareWrite(); if(!dd->identifiersSize()) return; dd->identifiersList.resize(dd->identifiersList.size()-1); } void QualifiedIdentifier::clear() { prepareWrite(); dd->identifiersList.clear(); dd->m_explicitlyGlobal = false; dd->m_isExpression = false; } bool QualifiedIdentifier::isEmpty() const { if(m_index) return cd->identifiersSize() == 0; else return dd->identifiersSize() == 0; } int QualifiedIdentifier::count() const { if(m_index) return cd->identifiersSize(); else return dd->identifiersSize(); } Identifier QualifiedIdentifier::first() const { if( (m_index && cd->identifiersSize() == 0) || (!m_index && dd->identifiersSize() == 0) ) return Identifier(); else return at(0); } Identifier QualifiedIdentifier::last() const { uint c = count(); if(c) return at(c-1); else return Identifier(); } Identifier QualifiedIdentifier::top() const { return last(); } QualifiedIdentifier QualifiedIdentifier::mid(int pos, int len) const { QualifiedIdentifier ret; if( pos == 0 ) ret.setExplicitlyGlobal(explicitlyGlobal()); int cnt = (int)count(); if( len == -1 ) len = cnt - pos; if( pos+len > cnt ) len -= cnt - (pos+len); for( int a = pos; a < pos+len; a++ ) ret.push(at(a)); return ret; } const Identifier QualifiedIdentifier::at(int i) const { if(m_index) { Q_ASSERT(i >= 0 && i < (int)cd->identifiersSize()); return cd->identifiers()[i]; }else{ Q_ASSERT(i >= 0 && i < (int)dd->identifiersSize()); return dd->identifiers()[i]; } } void QualifiedIdentifier::makeConstant() const { if(m_index) return; m_index = qualifiedidentifierRepository()->index( QualifiedIdentifierItemRequest(*dd) ); delete dd; cd = qualifiedidentifierRepository()->itemFromIndex( m_index ); } void QualifiedIdentifier::prepareWrite() { if(m_index) { const QualifiedIdentifierPrivate* oldCc = cd; dd = new QualifiedIdentifierPrivate; dd->m_explicitlyGlobal = oldCc->m_explicitlyGlobal; dd->m_isExpression = oldCc->m_isExpression; dd->m_hash = oldCc->m_hash; dd->copyListsFrom(*oldCc); m_index = 0; } dd->clearHash(); } uint IndexedTypeIdentifier::hash() const { return m_identifier.getIndex() * 13 + (m_isConstant ? 17 : 0) + (m_isReference ? 12371 : 0) + (m_isRValue ? 4543 : 0) + m_pointerConstMask * 89321 + m_pointerDepth * 1023; } bool IndexedTypeIdentifier::operator==(const IndexedTypeIdentifier& rhs) const { return m_identifier == rhs.m_identifier && m_isConstant == rhs.m_isConstant && m_isReference == rhs.m_isReference && m_isRValue == rhs.m_isRValue && m_pointerConstMask == rhs.m_pointerConstMask && m_pointerDepth == rhs.m_pointerDepth; } bool IndexedTypeIdentifier::operator!=(const IndexedTypeIdentifier& rhs) const { return !operator==(rhs); } bool IndexedTypeIdentifier::isReference() const { return m_isReference; } void IndexedTypeIdentifier::setIsReference(bool isRef) { m_isReference = isRef; } bool IndexedTypeIdentifier::isRValue() const { return m_isRValue; } void IndexedTypeIdentifier::setIsRValue(bool isRVal) { m_isRValue = isRVal; } bool IndexedTypeIdentifier::isConstant() const { return m_isConstant; } void IndexedTypeIdentifier::setIsConstant(bool isConst) { m_isConstant = isConst; } ///Returns the pointer depth. Example for C++: "char*" has pointer-depth 1, "char***" has pointer-depth 3 int IndexedTypeIdentifier::pointerDepth() const { return m_pointerDepth; } /**Sets the pointer-depth to the specified count * For efficiency-reasons the maximum currently is 32. */ void IndexedTypeIdentifier::setPointerDepth(int depth) { ///Clear the mask in removed fields for(int s = depth; s < (int)m_pointerDepth; ++s) setIsConstPointer(s, false); m_pointerDepth = depth; } bool IndexedTypeIdentifier::isConstPointer(int depthNumber) const { return m_pointerConstMask & (1 << depthNumber); } void IndexedTypeIdentifier::setIsConstPointer(int depthNumber, bool constant) { if(constant) m_pointerConstMask |= (1 << depthNumber); else m_pointerConstMask &= (~(1 << depthNumber)); } QString IndexedTypeIdentifier::toString(bool ignoreExplicitlyGlobal) const { QString ret; if(isConstant()) ret += "const "; ret += m_identifier.identifier().toString(ignoreExplicitlyGlobal); for(int a = 0; a < pointerDepth(); ++a) { ret += '*'; if( isConstPointer(a) ) ret += "const"; } if(isRValue()) ret += "&&"; else if(isReference()) ret += '&'; return ret; } IndexedTypeIdentifier::IndexedTypeIdentifier(KDevelop::IndexedQualifiedIdentifier identifier) : m_identifier(identifier) , m_isConstant(false) , m_isReference(false) , m_isRValue(false) , m_pointerDepth(0) , m_pointerConstMask(0) { } IndexedTypeIdentifier::IndexedTypeIdentifier(const QString& identifier, bool isExpression) : m_identifier(QualifiedIdentifier(identifier, isExpression)) , m_isConstant(false) , m_isReference(false) , m_isRValue(false) , m_pointerDepth(0) , m_pointerConstMask(0) { } IndexedIdentifier::IndexedIdentifier() : index(emptyConstantIdentifierPrivateIndex()) { if(shouldDoDUChainReferenceCounting(this)) { QMutexLocker lock(identifierRepository()->mutex()); increase(identifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } } IndexedIdentifier::IndexedIdentifier(const Identifier& id) : index(id.index()) { if(shouldDoDUChainReferenceCounting(this)) { QMutexLocker lock(identifierRepository()->mutex()); increase(identifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } } IndexedIdentifier::IndexedIdentifier(const IndexedIdentifier& rhs) : index(rhs.index) { if(shouldDoDUChainReferenceCounting(this)) { QMutexLocker lock(identifierRepository()->mutex()); increase(identifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } } IndexedIdentifier::~IndexedIdentifier() { if(shouldDoDUChainReferenceCounting(this)) { QMutexLocker lock(identifierRepository()->mutex()); decrease(identifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } } IndexedIdentifier& IndexedIdentifier::operator=(const Identifier& id) { if(shouldDoDUChainReferenceCounting(this)) { QMutexLocker lock(identifierRepository()->mutex()); decrease(identifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } index = id.index(); if(shouldDoDUChainReferenceCounting(this)) { QMutexLocker lock(identifierRepository()->mutex()); increase(identifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } return *this; } IndexedIdentifier& IndexedIdentifier::operator=(const IndexedIdentifier& id) { if(shouldDoDUChainReferenceCounting(this)) { QMutexLocker lock(identifierRepository()->mutex()); decrease(identifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } index = id.index; if(shouldDoDUChainReferenceCounting(this)) { QMutexLocker lock(identifierRepository()->mutex()); increase(identifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } return *this; } bool IndexedIdentifier::operator==(const IndexedIdentifier& rhs) const { return index == rhs.index; } bool IndexedIdentifier::operator!=(const IndexedIdentifier& rhs) const { return index != rhs.index; } bool IndexedIdentifier::operator==(const Identifier& id) const { return index == id.index(); } Identifier IndexedIdentifier::identifier() const { return Identifier(index); } IndexedIdentifier::operator Identifier() const { return Identifier(index); } bool IndexedQualifiedIdentifier::isValid() const { return index != emptyConstantQualifiedIdentifierPrivateIndex(); } int cnt = 0; IndexedQualifiedIdentifier IndexedTypeIdentifier::identifier() const { return m_identifier; } void IndexedTypeIdentifier::setIdentifier(KDevelop::IndexedQualifiedIdentifier id) { m_identifier = id; } IndexedQualifiedIdentifier::IndexedQualifiedIdentifier() : index(emptyConstantQualifiedIdentifierPrivateIndex()) { ifDebug( kDebug() << "(" << ++cnt << ")" << identifier().toString() << index; ) if(shouldDoDUChainReferenceCounting(this)) { ifDebug( kDebug() << "increasing"; ) //kDebug() << "(" << ++cnt << ")" << this << identifier().toString() << "inc" << index; QMutexLocker lock(qualifiedidentifierRepository()->mutex()); increase(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } } IndexedQualifiedIdentifier::IndexedQualifiedIdentifier(const QualifiedIdentifier& id) : index(id.index()) { ifDebug( kDebug() << "(" << ++cnt << ")" << identifier().toString() << index; ) if(shouldDoDUChainReferenceCounting(this)) { ifDebug( kDebug() << "increasing"; ) QMutexLocker lock(qualifiedidentifierRepository()->mutex()); increase(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } } IndexedQualifiedIdentifier::IndexedQualifiedIdentifier(const IndexedQualifiedIdentifier& id) : index(id.index) { ifDebug( kDebug() << "(" << ++cnt << ")" << identifier().toString() << index; ) if(shouldDoDUChainReferenceCounting(this)) { ifDebug( kDebug() << "increasing"; ) QMutexLocker lock(qualifiedidentifierRepository()->mutex()); increase(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } } IndexedQualifiedIdentifier& IndexedQualifiedIdentifier::operator=(const QualifiedIdentifier& id) { ifDebug( kDebug() << "(" << ++cnt << ")" << identifier().toString() << index; ) if(shouldDoDUChainReferenceCounting(this)) { QMutexLocker lock(qualifiedidentifierRepository()->mutex()); ifDebug( kDebug() << "decreasing"; ) decrease(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); index = id.index(); ifDebug( kDebug() << index << "increasing"; ) increase(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } else { index = id.index(); } return *this; } IndexedQualifiedIdentifier& IndexedQualifiedIdentifier::operator=(const IndexedQualifiedIdentifier& rhs) { ifDebug( kDebug() << "(" << ++cnt << ")" << identifier().toString() << index; ) if(shouldDoDUChainReferenceCounting(this)) { QMutexLocker lock(qualifiedidentifierRepository()->mutex()); ifDebug( kDebug() << "decreasing"; ) decrease(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); index = rhs.index; ifDebug( kDebug() << index << "increasing"; ) increase(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } else { index = rhs.index; } return *this; } IndexedQualifiedIdentifier::~IndexedQualifiedIdentifier() { ifDebug( kDebug() << "(" << ++cnt << ")" << identifier().toString() << index; ) if(shouldDoDUChainReferenceCounting(this)) { ifDebug( kDebug() << index << "decreasing"; ) QMutexLocker lock(qualifiedidentifierRepository()->mutex()); decrease(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } } bool IndexedQualifiedIdentifier::operator==(const IndexedQualifiedIdentifier& rhs) const { return index == rhs.index; } bool IndexedQualifiedIdentifier::operator==(const QualifiedIdentifier& id) const { return index == id.index(); } QualifiedIdentifier IndexedQualifiedIdentifier::identifier() const { return QualifiedIdentifier(index); } IndexedQualifiedIdentifier::operator QualifiedIdentifier() const { return QualifiedIdentifier(index); } void initIdentifierRepository() { identifierRepository(); qualifiedidentifierRepository(); } } QDebug operator<<(QDebug s, const KDevelop::Identifier& identifier) { s.nospace() << identifier.toString(); return s.space(); } QDebug operator<<(QDebug s, const KDevelop::QualifiedIdentifier& identifier) { s.nospace() << identifier.toString(); return s.space(); } // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp b/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp index 6b3c26097..99086544d 100644 --- a/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp +++ b/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp @@ -1,693 +1,693 @@ /* Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "abstractdeclarationnavigationcontext.h" #include #include #include "../functiondeclaration.h" #include "../functiondefinition.h" #include "../classfunctiondeclaration.h" #include "../namespacealiasdeclaration.h" #include "../forwarddeclaration.h" #include "../types/enumeratortype.h" #include "../types/enumerationtype.h" #include "../types/functiontype.h" #include "../duchainutils.h" #include "../types/pointertype.h" #include "../types/referencetype.h" #include "../types/typeutils.h" #include "../persistentsymboltable.h" #include #include #include #include #include namespace KDevelop { AbstractDeclarationNavigationContext::AbstractDeclarationNavigationContext( DeclarationPointer decl, KDevelop::TopDUContextPointer topContext, AbstractNavigationContext* previousContext) : AbstractNavigationContext((topContext ? topContext : TopDUContextPointer(decl ? decl->topContext() : 0)), previousContext), m_declaration(decl), m_fullBackwardSearch(false) { //Jump from definition to declaration if possible FunctionDefinition* definition = dynamic_cast(m_declaration.data()); if(definition && definition->declaration()) m_declaration = DeclarationPointer(definition->declaration()); } QString AbstractDeclarationNavigationContext::name() const { if(m_declaration.data()) return prettyQualifiedIdentifier(m_declaration).toString(); else return declarationName(m_declaration); } QString AbstractDeclarationNavigationContext::html(bool shorten) { clear(); m_shorten = shorten; modifyHtml() += "

" + fontSizePrefix(shorten); addExternalHtml(m_prefix); if(!m_declaration.data()) { modifyHtml() += i18n("
lost declaration
"); return currentHtml(); } if( m_previousContext ) { QString link = createLink( m_previousContext->name(), m_previousContext->name(), NavigationAction(m_previousContext) ); modifyHtml() += navigationHighlight(i18n("Back to %1
", link)); } KSharedPtr doc; if( !shorten ) { doc = ICore::self()->documentationController()->documentationForDeclaration(m_declaration.data()); const AbstractFunctionDeclaration* function = dynamic_cast(m_declaration.data()); if( function ) { htmlFunction(); } else if( m_declaration->isTypeAlias() || m_declaration->kind() == Declaration::Instance ) { if( m_declaration->isTypeAlias() ) modifyHtml() += importantHighlight("typedef "); if(m_declaration->type()) modifyHtml() += i18n("enumerator "); AbstractType::Ptr useType = m_declaration->abstractType(); if(m_declaration->isTypeAlias()) { //Do not show the own name as type of typedefs if(useType.cast()) useType = useType.cast()->type(); } eventuallyMakeTypeLinks( useType ); modifyHtml() += ' ' + nameHighlight(Qt::escape(declarationName(m_declaration))) + "
"; }else{ if( m_declaration->kind() == Declaration::Type && m_declaration->abstractType().cast() ) { htmlClass(); } if ( m_declaration->kind() == Declaration::Namespace ) { modifyHtml() += i18n("namespace %1
", nameHighlight(Qt::escape(m_declaration->qualifiedIdentifier().toString()))); } if(m_declaration->type()) { EnumerationType::Ptr enumeration = m_declaration->type(); modifyHtml() += i18n("enumeration %1
", Qt::escape(m_declaration->identifier().toString()) ); } if(m_declaration->isForwardDeclaration()) { ForwardDeclaration* forwardDec = static_cast(m_declaration.data()); Declaration* resolved = forwardDec->resolve(m_topContext.data()); if(resolved) { modifyHtml() += i18n("( resolved forward-declaration: "); makeLink(resolved->identifier().toString(), KDevelop::DeclarationPointer(resolved), NavigationAction::NavigateDeclaration ); modifyHtml() += i18n(") "); }else{ modifyHtml() += i18n("(unresolved forward-declaration) "); QualifiedIdentifier id = forwardDec->qualifiedIdentifier(); uint count; const IndexedDeclaration* decls; PersistentSymbolTable::self().declarations(id, count, decls); bool had = false; for(uint a = 0; a < count; ++a) { if(decls[a].isValid() && !decls[a].data()->isForwardDeclaration()) { modifyHtml() += "
"; makeLink(i18n("possible resolution from"), KDevelop::DeclarationPointer(decls[a].data()), NavigationAction::NavigateDeclaration); - modifyHtml() += ' ' + decls[a].data()->url().str(); + modifyHtml() += ' ' + decls[a].data()->url().toString(); had = true; } } if(had) modifyHtml() += "
"; } } } }else{ AbstractType::Ptr showType = m_declaration->abstractType(); if(showType && showType.cast()) { showType = showType.cast()->returnType(); if(showType) modifyHtml() += labelHighlight(i18n("Returns: ")); }else if(showType) { modifyHtml() += labelHighlight(i18n("Type: ")); } if(showType) { eventuallyMakeTypeLinks(showType); modifyHtml() += " "; } } QualifiedIdentifier identifier = m_declaration->qualifiedIdentifier(); if( identifier.count() > 1 ) { if( m_declaration->context() && m_declaration->context()->owner() ) { Declaration* decl = m_declaration->context()->owner(); FunctionDefinition* definition = dynamic_cast(decl); if(definition && definition->declaration()) decl = definition->declaration(); if(decl->abstractType().cast()) modifyHtml() += labelHighlight(i18n("Enum: ")); else modifyHtml() += labelHighlight(i18n("Container: ")); makeLink( declarationName(DeclarationPointer(decl)), DeclarationPointer(decl), NavigationAction::NavigateDeclaration ); modifyHtml() += " "; } else { QualifiedIdentifier parent = identifier; parent.pop(); modifyHtml() += labelHighlight(i18n("Scope: %1 ", typeHighlight(Qt::escape(parent.toString())))); } } if( shorten && !m_declaration->comment().isEmpty() ) { QString comment = QString::fromUtf8(m_declaration->comment()); if( comment.length() > 60 ) { comment.truncate(60); comment += "..."; } comment.replace('\n', " "); comment.replace("
", " "); comment.replace("
", " "); modifyHtml() += commentHighlight(Qt::escape(comment)) + " "; } QString access = stringFromAccess(m_declaration); if( !access.isEmpty() ) modifyHtml() += labelHighlight(i18n("Access: %1 ", propertyHighlight(Qt::escape(access)))); ///@todo Enumerations QString detailsHtml; QStringList details = declarationDetails(m_declaration); if( !details.isEmpty() ) { bool first = true; foreach( const QString &str, details ) { if( !first ) detailsHtml += ", "; first = false; detailsHtml += propertyHighlight(str); } } QString kind = declarationKind(m_declaration); if( !kind.isEmpty() ) { if( !detailsHtml.isEmpty() ) modifyHtml() += labelHighlight(i18n("Kind: %1 %2 ", importantHighlight(Qt::escape(kind)), detailsHtml)); else modifyHtml() += labelHighlight(i18n("Kind: %1 ", importantHighlight(Qt::escape(kind)))); } else if( !detailsHtml.isEmpty() ) { modifyHtml() += labelHighlight(i18n("Modifiers: %1 ", importantHighlight(Qt::escape(kind)))); } modifyHtml() += "
"; if(!shorten) htmlAdditionalNavigation(); if( !shorten ) { if(dynamic_cast(m_declaration.data())) modifyHtml() += labelHighlight(i18n( "Def.: " )); else modifyHtml() += labelHighlight(i18n( "Decl.: " )); - makeLink( QString("%1 :%2").arg( KUrl(m_declaration->url().str()).fileName() ).arg( m_declaration->rangeInCurrentRevision().textRange().start().line()+1 ), m_declaration, NavigationAction::JumpToSource ); + makeLink( QString("%1 :%2").arg( m_declaration->url().toUrl().fileName() ).arg( m_declaration->rangeInCurrentRevision().textRange().start().line()+1 ), m_declaration, NavigationAction::JumpToSource ); modifyHtml() += " "; //modifyHtml() += "
"; if(!dynamic_cast(m_declaration.data())) { if( FunctionDefinition* definition = FunctionDefinition::definition(m_declaration.data()) ) { modifyHtml() += labelHighlight(i18n( " Def.: " )); - makeLink( QString("%1 :%2").arg( KUrl(definition->url().str()).fileName() ).arg( definition->rangeInCurrentRevision().textRange().start().line()+1 ), DeclarationPointer(definition), NavigationAction::JumpToSource ); + makeLink( QString("%1 :%2").arg( definition->url().toUrl().fileName() ).arg( definition->rangeInCurrentRevision().textRange().start().line()+1 ), DeclarationPointer(definition), NavigationAction::JumpToSource ); } } if( FunctionDefinition* definition = dynamic_cast(m_declaration.data()) ) { if(definition->declaration()) { modifyHtml() += labelHighlight(i18n( " Decl.: " )); - makeLink( QString("%1 :%2").arg( KUrl(definition->declaration()->url().str()).fileName() ).arg( definition->declaration()->rangeInCurrentRevision().textRange().start().line()+1 ), DeclarationPointer(definition->declaration()), NavigationAction::JumpToSource ); + makeLink( QString("%1 :%2").arg( definition->declaration()->url().toUrl().fileName() ).arg( definition->declaration()->rangeInCurrentRevision().textRange().start().line()+1 ), DeclarationPointer(definition->declaration()), NavigationAction::JumpToSource ); } } modifyHtml() += " "; //The action name _must_ stay "show_uses", since that is also used from outside makeLink(i18n("Show uses"), "show_uses", NavigationAction(m_declaration, NavigationAction::NavigateUses)); } if( !shorten && (!m_declaration->comment().isEmpty() || doc) ) { modifyHtml() += "
"; QString comment = QString::fromUtf8(m_declaration->comment()); if(comment.isEmpty() && doc) { comment = doc->description(); if(!comment.isEmpty()) { modifyHtml() += "
" + commentHighlight(comment); } } else if(!comment.isEmpty()) { comment.replace("
", "\n"); //do not escape html newlines within the comment comment.replace("
", "\n"); comment = Qt::escape(comment); comment.replace('\n', "
"); //Replicate newlines in html modifyHtml() += commentHighlight(comment); modifyHtml() += "
"; } } if(!shorten && doc) { modifyHtml() += "
" + i18n("Show documentation for "); makeLink( prettyQualifiedIdentifier(m_declaration).toString(), m_declaration, NavigationAction::ShowDocumentation ); } //modifyHtml() += "
"; addExternalHtml(m_suffix); modifyHtml() += fontSizeSuffix(shorten) + "

"; return currentHtml(); } KDevelop::AbstractType::Ptr AbstractDeclarationNavigationContext::typeToShow(KDevelop::AbstractType::Ptr type) { return type; } void AbstractDeclarationNavigationContext::htmlFunction() { const AbstractFunctionDeclaration* function = dynamic_cast(m_declaration.data()); Q_ASSERT(function); const ClassFunctionDeclaration* classFunDecl = dynamic_cast(m_declaration.data()); const FunctionType::Ptr type = m_declaration->abstractType().cast(); if( !type ) { modifyHtml() += errorHighlight("Invalid type
"); return; } if( !classFunDecl || (!classFunDecl->isConstructor() && !classFunDecl->isDestructor()) ) { // only print return type for global functions and non-ctor/dtor methods eventuallyMakeTypeLinks( type->returnType() ); } modifyHtml() += ' ' + nameHighlight(Qt::escape(prettyIdentifier(m_declaration).toString())); if( type->arguments().count() == 0 ) { modifyHtml() += "()"; } else { modifyHtml() += "( "; bool first = true; KDevelop::DUContext* argumentContext = DUChainUtils::getArgumentContext(m_declaration.data()); if(argumentContext) { int firstDefaultParam = argumentContext->localDeclarations(m_topContext.data()).count() - function->defaultParametersSize(); int currentArgNum = 0; foreach(Declaration* argument, argumentContext->localDeclarations(m_topContext.data())) { if( !first ) modifyHtml() += ", "; first = false; AbstractType::Ptr argType = argument->abstractType(); eventuallyMakeTypeLinks( argType ); modifyHtml() += ' ' + nameHighlight(Qt::escape(argument->identifier().toString())); if( currentArgNum >= firstDefaultParam ) - modifyHtml() += " = " + Qt::escape(function->defaultParameters()[ currentArgNum - firstDefaultParam ].str()); + modifyHtml() += " = " + Qt::escape(function->defaultParameters()[ currentArgNum - firstDefaultParam ].toString()); ++currentArgNum; } } modifyHtml() += " )"; } modifyHtml() += "
"; } Identifier AbstractDeclarationNavigationContext::prettyIdentifier(DeclarationPointer decl) const { Identifier ret; QualifiedIdentifier q = prettyQualifiedIdentifier(decl); if(!q.isEmpty()) ret = q.last(); return ret; } QualifiedIdentifier AbstractDeclarationNavigationContext::prettyQualifiedIdentifier(DeclarationPointer decl) const { if(decl) return decl->qualifiedIdentifier(); else return QualifiedIdentifier(); } void AbstractDeclarationNavigationContext::htmlAdditionalNavigation() { ///Check if the function overrides or hides another one const ClassFunctionDeclaration* classFunDecl = dynamic_cast(m_declaration.data()); if(classFunDecl) { Declaration* overridden = DUChainUtils::getOverridden(m_declaration.data()); if(overridden) { modifyHtml() += i18n("Overrides a "); makeLink(i18n("function"), QString("jump_to_overridden"), NavigationAction(DeclarationPointer(overridden), KDevelop::NavigationAction::NavigateDeclaration)); modifyHtml() += i18n(" from "); makeLink(prettyQualifiedIdentifier(DeclarationPointer(overridden->context()->owner())).toString(), QString("jump_to_overridden_container"), NavigationAction(DeclarationPointer(overridden->context()->owner()), KDevelop::NavigationAction::NavigateDeclaration)); modifyHtml() += "
"; }else{ //Check if this declarations hides other declarations QList decls; foreach(const DUContext::Import &import, m_declaration->context()->importedParentContexts()) if(import.context(m_topContext.data())) decls += import.context(m_topContext.data())->findDeclarations(QualifiedIdentifier(m_declaration->identifier()), CursorInRevision::invalid(), AbstractType::Ptr(), m_topContext.data(), DUContext::DontSearchInParent); uint num = 0; foreach(Declaration* decl, decls) { modifyHtml() += i18n("Hides a "); makeLink(i18n("function"), QString("jump_to_hide_%1").arg(num), NavigationAction(DeclarationPointer(decl), KDevelop::NavigationAction::NavigateDeclaration)); modifyHtml() += i18n(" from "); makeLink(prettyQualifiedIdentifier(DeclarationPointer(decl->context()->owner())).toString(), QString("jump_to_hide_container_%1").arg(num), NavigationAction(DeclarationPointer(decl->context()->owner()), KDevelop::NavigationAction::NavigateDeclaration)); modifyHtml() += "
"; ++num; } } ///Show all places where this function is overridden if(classFunDecl->isVirtual()) { Declaration* classDecl = m_declaration->context()->owner(); if(classDecl) { uint maxAllowedSteps = m_fullBackwardSearch ? (uint)-1 : 10; QList overriders = DUChainUtils::getOverriders(classDecl, classFunDecl, maxAllowedSteps); if(!overriders.isEmpty()) { modifyHtml() += i18n("Overridden in "); bool first = true; foreach(Declaration* overrider, overriders) { if(!first) modifyHtml() += ", "; first = false; QString name = prettyQualifiedIdentifier(DeclarationPointer(overrider->context()->owner())).toString(); makeLink(name, name, NavigationAction(DeclarationPointer(overrider), NavigationAction::NavigateDeclaration)); } modifyHtml() += "
"; } if(maxAllowedSteps == 0) createFullBackwardSearchLink(overriders.isEmpty() ? i18n("Overriders possible, show all") : i18n("More overriders possible, show all")); } } } ///Show all classes that inherit this one uint maxAllowedSteps = m_fullBackwardSearch ? (uint)-1 : 10; QList inheriters = DUChainUtils::getInheriters(m_declaration.data(), maxAllowedSteps); if(!inheriters.isEmpty()) { modifyHtml() += i18n("Inherited by "); bool first = true; foreach(Declaration* importer, inheriters) { if(!first) modifyHtml() += ", "; first = false; QString importerName = prettyQualifiedIdentifier(DeclarationPointer(importer)).toString(); makeLink(importerName, importerName, NavigationAction(DeclarationPointer(importer), KDevelop::NavigationAction::NavigateDeclaration)); } modifyHtml() += "
"; } if(maxAllowedSteps == 0) createFullBackwardSearchLink(inheriters.isEmpty() ? i18n("Inheriters possible, show all") : i18n("More inheriters possible, show all")); } void AbstractDeclarationNavigationContext::createFullBackwardSearchLink(QString string) { makeLink(string, "m_fullBackwardSearch=true", NavigationAction("m_fullBackwardSearch=true")); modifyHtml() += "
"; } NavigationContextPointer AbstractDeclarationNavigationContext::executeKeyAction( QString key ) { if(key == "m_fullBackwardSearch=true") { m_fullBackwardSearch = true; clear(); } return NavigationContextPointer(this); } void AbstractDeclarationNavigationContext::htmlClass() { StructureType::Ptr klass = m_declaration->abstractType().cast(); Q_ASSERT(klass); ClassDeclaration* classDecl = dynamic_cast(klass->declaration(m_topContext.data())); if(classDecl) { switch ( classDecl->classType() ) { case ClassDeclarationData::Class: modifyHtml() += "class "; break; case ClassDeclarationData::Struct: modifyHtml() += "struct "; break; case ClassDeclarationData::Union: modifyHtml() += "union "; break; case ClassDeclarationData::Interface: modifyHtml() += "interface "; break; default: modifyHtml() += " "; break; } eventuallyMakeTypeLinks( klass.cast() ); FOREACH_FUNCTION( const KDevelop::BaseClassInstance& base, classDecl->baseClasses ) { modifyHtml() += ", " + stringFromAccess(base.access) + " " + (base.virtualInheritance ? QString("virtual") : QString()) + " "; eventuallyMakeTypeLinks(base.baseClass.abstractType()); } modifyHtml() += " "; } else { /// @todo How can we get here? and should this really be a class? modifyHtml() += "class "; eventuallyMakeTypeLinks( klass.cast() ); } } void AbstractDeclarationNavigationContext::htmlIdentifiedType(AbstractType::Ptr type, const IdentifiedType* idType) { Q_ASSERT(type); Q_ASSERT(idType); if( Declaration* decl = idType->declaration(m_topContext.data()) ) { //Remove the last template-identifiers, because we create those directly QualifiedIdentifier id = prettyQualifiedIdentifier(DeclarationPointer(decl)); Identifier lastId = id.last(); id.pop(); lastId.clearTemplateIdentifiers(); id.push(lastId); if(decl->context() && decl->context()->owner()) { //Also create full type-links for the context around AbstractType::Ptr contextType = decl->context()->owner()->abstractType(); IdentifiedType* contextIdType = dynamic_cast(contextType.unsafeData()); if(contextIdType && !contextIdType->equals(idType)) { //Create full type information for the context if(!id.isEmpty()) id = id.mid(id.count()-1); htmlIdentifiedType(contextType, contextIdType); modifyHtml() += Qt::escape("::"); } } //We leave out the * and & reference and pointer signs, those are added to the end makeLink(id.toString() , DeclarationPointer(idType->declaration(m_topContext.data())), NavigationAction::NavigateDeclaration ); } else { - kDebug() << "could not resolve declaration:" << idType->declarationId().isDirect() << idType->qualifiedIdentifier().toString() << "in top-context" << m_topContext->url().str(); + kDebug() << "could not resolve declaration:" << idType->declarationId().isDirect() << idType->qualifiedIdentifier().toString() << "in top-context" << m_topContext->url(); modifyHtml() += typeHighlight(Qt::escape(type->toString())); } } void AbstractDeclarationNavigationContext::eventuallyMakeTypeLinks( AbstractType::Ptr type ) { type = typeToShow(type); if( !type ) { modifyHtml() += typeHighlight(Qt::escape("")); return; } AbstractType::Ptr target = TypeUtils::targetTypeKeepAliases( type, m_topContext.data() ); const IdentifiedType* idType = dynamic_cast( target.unsafeData() ); kDebug() << "making type-links for" << type->toString() << typeid(*type).name(); if( idType && idType->declaration(m_topContext.data()) ) { ///@todo This is C++ specific, move into subclass if(target->modifiers() & AbstractType::ConstModifier) modifyHtml() += typeHighlight("const "); htmlIdentifiedType(target, idType); //We need to exchange the target type, else template-parameters may confuse this SimpleTypeExchanger exchangeTarget(target, AbstractType::Ptr()); AbstractType::Ptr exchanged = exchangeTarget.exchange(type); if(exchanged) { QString typeSuffixString = exchanged->toString(); QRegExp suffixExp("\\&|\\*"); int suffixPos = typeSuffixString.indexOf(suffixExp); if(suffixPos != -1) modifyHtml() += typeHighlight(typeSuffixString.mid(suffixPos)); } } else { if(idType) { kDebug() << "identified type could not be resolved:" << idType->qualifiedIdentifier() << idType->declarationId().isValid() << idType->declarationId().isDirect(); } modifyHtml() += typeHighlight(Qt::escape(type->toString())); } } DeclarationPointer AbstractDeclarationNavigationContext::declaration() const { return m_declaration; } QString AbstractDeclarationNavigationContext::stringFromAccess(Declaration::AccessPolicy access) { switch(access) { case Declaration::Private: return "private"; case Declaration::Protected: return "protected"; case Declaration::Public: return "public"; default: break; } return ""; } QString AbstractDeclarationNavigationContext::stringFromAccess(DeclarationPointer decl) { const ClassMemberDeclaration* memberDecl = dynamic_cast(decl.data()); if( memberDecl ) { return stringFromAccess(memberDecl->accessPolicy()); } return QString(); } QString AbstractDeclarationNavigationContext::declarationName( DeclarationPointer decl ) const { if( NamespaceAliasDeclaration* alias = dynamic_cast(decl.data()) ) { if( alias->identifier().isEmpty() ) return "using namespace " + alias->importIdentifier().toString(); else return "namespace " + alias->identifier().toString() + " = " + alias->importIdentifier().toString(); } if( !decl ) return i18nc("A declaration that is unknown", "Unknown"); else return prettyIdentifier(decl).toString(); } QStringList AbstractDeclarationNavigationContext::declarationDetails(DeclarationPointer decl) { QStringList details; const AbstractFunctionDeclaration* function = dynamic_cast(decl.data()); const ClassMemberDeclaration* memberDecl = dynamic_cast(decl.data()); if( memberDecl ) { if( memberDecl->isMutable() ) details << "mutable"; if( memberDecl->isRegister() ) details << "register"; if( memberDecl->isStatic() ) details << "static"; if( memberDecl->isAuto() ) details << "auto"; if( memberDecl->isExtern() ) details << "extern"; if( memberDecl->isFriend() ) details << "friend"; } if( decl->isDefinition() ) details << i18nc("tells if a declaration is defining the variable's value", "definition"); if( decl->isExplicitlyDeleted() ) details << "deleted"; if( memberDecl && memberDecl->isForwardDeclaration() ) details << i18nc("as in c++ forward declaration", "forward"); AbstractType::Ptr t(decl->abstractType()); if( t ) { if( t->modifiers() & AbstractType::ConstModifier ) details << i18nc("a variable that won't change, const", "constant"); if( t->modifiers() & AbstractType::VolatileModifier ) details << "volatile"; } if( function ) { if( function->isInline() ) details << "inline"; if( function->isExplicit() ) details << "explicit"; if( function->isVirtual() ) details << "virtual"; const ClassFunctionDeclaration* classFunDecl = dynamic_cast(decl.data()); if( classFunDecl ) { if( classFunDecl->isSignal() ) details << "signal"; if( classFunDecl->isSlot() ) details << "slot"; if( classFunDecl->isConstructor() ) details << "constructor"; if( classFunDecl->isDestructor() ) details << "destructor"; if( classFunDecl->isConversionFunction() ) details << "conversion-function"; if( classFunDecl->isAbstract() ) details << "abstract"; } } return details; } } diff --git a/language/duchain/navigation/usescollector.cpp b/language/duchain/navigation/usescollector.cpp index d9a69ec1c..e05c810ad 100644 --- a/language/duchain/navigation/usescollector.cpp +++ b/language/duchain/navigation/usescollector.cpp @@ -1,426 +1,428 @@ /* Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "usescollector.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "../classmemberdeclaration.h" #include "../abstractfunctiondeclaration.h" #include "../functiondefinition.h" #include #include #include using namespace KDevelop; ///@todo make this language-neutral -static Identifier destructorForName(Identifier name) { - QString str = name.identifier().str(); - if(str.startsWith('~')) - return Identifier(str); +static Identifier destructorForName(const Identifier& name) { + QString str = name.identifier().toString(); + if (str.startsWith('~')) { + // already a dtor identifier + return name; + } return Identifier('~'+str); } ///@todo Only collect uses within currently loaded projects template void collectImporters(ImportanceChecker& checker, ParsingEnvironmentFile* current, QSet& visited, QSet& collected) { //Ignore proxy-contexts while collecting. Those build a parallel and much more complicated structure. if(current->isProxyContext()) return; if(visited.contains(current)) return; visited.insert(current); if(checker(current)) collected.insert(current); foreach(ParsingEnvironmentFilePointer importer, current->importers()) if(importer.data()) collectImporters(checker, importer.data(), visited, collected); else kDebug() << "missing environment-file, strange"; } ///The returned set does not include the file itself ///@parm visited should be empty on each call, used to prevent endless recursion void allImportedFiles(ParsingEnvironmentFilePointer file, QSet& set, QSet& visited) { foreach(const ParsingEnvironmentFilePointer &import, file->imports()) { if(!import) { kDebug() << "warning: missing import"; continue; } if(!visited.contains(import)) { visited.insert(import); set.insert(import->url()); allImportedFiles(import, set, visited); } } } void UsesCollector::setCollectConstructors(bool process) { m_collectConstructors = process; } void UsesCollector::setProcessDeclarations(bool process) { m_processDeclarations = process; } void UsesCollector::setCollectOverloads(bool collect) { m_collectOverloads = collect; } void UsesCollector::setCollectDefinitions(bool collect) { m_collectDefinitions = collect; } QList UsesCollector::declarations() { return m_declarations; } bool UsesCollector::isReady() const { return m_waitForUpdate.size() == m_updateReady.size(); } bool UsesCollector::shouldRespectFile(IndexedString document) { return (bool)ICore::self()->projectController()->findProjectForUrl(document.toUrl()) || (bool)ICore::self()->documentController()->documentForUrl(document.toUrl()); } struct ImportanceChecker { ImportanceChecker(UsesCollector& collector) : m_collector(collector) { } bool operator ()(ParsingEnvironmentFile* file) { return m_collector.shouldRespectFile(file->url()); } UsesCollector& m_collector; }; void UsesCollector::startCollecting() { DUChainReadLocker lock(DUChain::lock()); if(Declaration* decl = m_declaration.data()) { if(m_collectDefinitions) { if(FunctionDefinition* def = dynamic_cast(decl)) { //Jump from definition to declaration Declaration* declaration = def->declaration(); if(declaration) decl = declaration; } } ///Collect all overloads into "decls" QList decls; if(m_collectOverloads && decl->context()->owner() && decl->context()->type() == DUContext::Class) { //First find the overridden base, and then all overriders of that base. while(Declaration* overridden = DUChainUtils::getOverridden(decl)) decl = overridden; uint maxAllowedSteps = 10000; decls += DUChainUtils::getOverriders( decl->context()->owner(), decl, maxAllowedSteps ); if(maxAllowedSteps == 10000) { ///@todo Fail! } } decls << decl; ///Collect all "parsed versions" or forward-declarations etc. here, into allDeclarations QSet allDeclarations; foreach(Declaration* overload, decls) { m_declarations = DUChainUtils::collectAllVersions(overload); foreach(const IndexedDeclaration &d, m_declarations) { if(!d.data() || d.data()->id() != overload->id()) continue; allDeclarations.insert(d); if(m_collectConstructors && d.data() && d.data()->internalContext() && d.data()->internalContext()->type() == DUContext::Class) { QList constructors = d.data()->internalContext()->findLocalDeclarations(d.data()->identifier(), CursorInRevision::invalid(), 0, AbstractType::Ptr(), DUContext::OnlyFunctions); foreach(Declaration* constructor, constructors) { ClassFunctionDeclaration* classFun = dynamic_cast(constructor); if(classFun && classFun->isConstructor()) allDeclarations.insert(IndexedDeclaration(constructor)); } Identifier destructorId = destructorForName(d.data()->identifier()); QList destructors = d.data()->internalContext()->findLocalDeclarations(destructorId, CursorInRevision::invalid(), 0, AbstractType::Ptr(), DUContext::OnlyFunctions); foreach(Declaration* destructor, destructors) { ClassFunctionDeclaration* classFun = dynamic_cast(destructor); if(classFun && classFun->isDestructor()) allDeclarations.insert(IndexedDeclaration(destructor)); } } } } ///Collect definitions for declarations if(m_collectDefinitions) { foreach(const IndexedDeclaration &d, allDeclarations) { Declaration* definition = FunctionDefinition::definition(d.data()); if(definition) { kDebug() << "adding definition"; allDeclarations.insert(IndexedDeclaration(definition)); } } } m_declarations.clear(); ///Step 4: Copy allDeclarations into m_declarations, build top-context list, etc. QList candidateTopContexts; foreach(const IndexedDeclaration &d, allDeclarations) { m_declarations << d; m_declarationTopContexts.insert(d.indexedTopContext()); //We only collect declarations with the same type here.. candidateTopContexts << d.indexedTopContext().data(); } ImportanceChecker checker(*this); QSet visited; QSet collected; kDebug() << "count of source candidate top-contexts:" << candidateTopContexts.size(); ///We use ParsingEnvironmentFile to collect all the relevant importers, because loading those is very cheap, compared ///to loading a whole TopDUContext. if(decl->inSymbolTable()) { //The declaration can only be used from other contexts if it is in the symbol table foreach(const ReferencedTopDUContext &top, candidateTopContexts) { if(top->parsingEnvironmentFile()) { collectImporters(checker, top->parsingEnvironmentFile().data(), visited, collected); //In C++, visibility is not handled strictly through the import-structure. //It may happen that an object is visible because of an earlier include. //We can not perfectly handle that, but we can at least handle it if the header includes //the header that contains the declaration. That header may be parsed empty due to header-guards, //but we still need to pick it up here. QList allVersions = DUChain::self()->allEnvironmentFiles(top->url()); foreach(ParsingEnvironmentFilePointer version, allVersions) collectImporters(checker, version.data(), visited, collected); } } } KDevelop::ParsingEnvironmentFile* file=decl->topContext()->parsingEnvironmentFile().data(); if(!file) return; if(checker(file)) collected.insert(file); { QSet filteredCollected; QMap grepCache; // Filter the collected files by performing a grep foreach(ParsingEnvironmentFile* file, collected) { IndexedString url = file->url(); QMap< IndexedString, bool >::iterator grepCacheIt = grepCache.find(url); if(grepCacheIt == grepCache.end()) { CodeRepresentation::Ptr repr = KDevelop::createCodeRepresentation( url ); if(repr) { - QVector found = repr->grep(decl->identifier().identifier().str()); + QVector found = repr->grep(decl->identifier().identifier().toString()); grepCacheIt = grepCache.insert(url, !found.isEmpty()); } } if(grepCacheIt.value()) filteredCollected << file; } kDebug() << "Collected contexts for full re-parse, before filtering: " << collected.size() << " after filtering: " << filteredCollected.size(); collected = filteredCollected; } ///We have all importers now. However since we can tell parse-jobs to also update all their importers, we only need to ///update the "root" top-contexts that open the whole set with their imports. QSet rootFiles; QSet allFiles; foreach(ParsingEnvironmentFile* importer, collected) { QSet allImports; QSet visited; allImportedFiles(ParsingEnvironmentFilePointer(importer), allImports, visited); //Remove all files from the "root" set that are imported by this one ///@todo more intelligent rootFiles -= allImports; allFiles += allImports; allFiles.insert(importer->url()); rootFiles.insert(importer->url()); } emit maximumProgressSignal(rootFiles.size()); maximumProgress(rootFiles.size()); //If we used the AllDeclarationsContextsAndUsesRecursive flag here, we would compute way too much. This way we only //set the minimum-features selectively on the files we really require them on. foreach(ParsingEnvironmentFile* file, collected) m_staticFeaturesManipulated.insert(file->url()); m_staticFeaturesManipulated.insert(decl->url()); foreach(const IndexedString &file, m_staticFeaturesManipulated) ParseJob::setStaticMinimumFeatures(file, TopDUContext::AllDeclarationsContextsAndUses); m_waitForUpdate = rootFiles; foreach(const IndexedString &file, rootFiles) { - kDebug() << "updating root file:" << file.str(); + kDebug() << "updating root file:" << file; DUChain::self()->updateContextForUrl(file, TopDUContext::AllDeclarationsContextsAndUses, this); } }else{ emit maximumProgressSignal(0); maximumProgress(0); } } void UsesCollector::maximumProgress(uint max) { Q_UNUSED(max); } UsesCollector::UsesCollector(IndexedDeclaration declaration) : m_declaration(declaration), m_collectOverloads(true), m_collectDefinitions(true), m_collectConstructors(false), m_processDeclarations(true) { } UsesCollector::~UsesCollector() { ICore::self()->languageController()->backgroundParser()->revertAllRequests(this); foreach(const IndexedString &file, m_staticFeaturesManipulated) ParseJob::unsetStaticMinimumFeatures(file, TopDUContext::AllDeclarationsContextsAndUses); } void UsesCollector::progress(uint processed, uint total) { Q_UNUSED(processed); Q_UNUSED(total); } void UsesCollector::updateReady(KDevelop::IndexedString url, KDevelop::ReferencedTopDUContext topContext) { DUChainReadLocker lock(DUChain::lock()); if(!topContext) { - kDebug() << "failed updating" << url.str(); + kDebug() << "failed updating" << url; }else{ if(topContext->parsingEnvironmentFile() && topContext->parsingEnvironmentFile()->isProxyContext()) { ///Use the attached content-context instead foreach(const DUContext::Import &import, topContext->importedParentContexts()) { if(import.context(0) && import.context(0)->topContext()->parsingEnvironmentFile() && !import.context(0)->topContext()->parsingEnvironmentFile()->isProxyContext()) { if((import.context(0)->topContext()->features() & TopDUContext::AllDeclarationsContextsAndUses)) { ReferencedTopDUContext newTop(import.context(0)->topContext()); topContext = newTop; break; } } } if(topContext->parsingEnvironmentFile() && topContext->parsingEnvironmentFile()->isProxyContext()) { - kDebug() << "got bad proxy-context for" << url.str(); + kDebug() << "got bad proxy-context for" << url; topContext = 0; } } } if(m_waitForUpdate.contains(url) && !m_updateReady.contains(url)) { m_updateReady << url; m_checked.clear(); emit progressSignal(m_updateReady.size(), m_waitForUpdate.size()); progress(m_updateReady.size(), m_waitForUpdate.size()); } if(!topContext || !topContext->parsingEnvironmentFile()) { kDebug() << "bad top-context"; return; } if(!m_staticFeaturesManipulated.contains(url)) return; //Not interesting if(!(topContext->features() & TopDUContext::AllDeclarationsContextsAndUses)) { ///@todo With simplified environment-matching, the same file may have been imported multiple times, ///while only one of those was updated. We have to check here whether this file is just such an import, ///or whether we work on with it. ///@todo We will lose files that were edited right after their update here. - kWarning() << "WARNING: context" << topContext->url().str() << "does not have the required features!!"; + kWarning() << "WARNING: context" << topContext->url() << "does not have the required features!!"; ICore::self()->uiController()->showErrorMessage("Updating " + ICore::self()->projectController()->prettyFileName(topContext->url().toUrl(), KDevelop::IProjectController::FormatPlain) + " failed!", 5); return; } if(topContext->parsingEnvironmentFile()->needsUpdate()) { - kWarning() << "WARNING: context" << topContext->url().str() << "is not up to date!"; + kWarning() << "WARNING: context" << topContext->url() << "is not up to date!"; ICore::self()->uiController()->showErrorMessage(i18n("%1 still needs an update!", ICore::self()->projectController()->prettyFileName(topContext->url().toUrl(), KDevelop::IProjectController::FormatPlain)), 5); // return; } IndexedTopDUContext indexed(topContext.data()); if(m_checked.contains(indexed)) return; if(!topContext.data()) { - kDebug() << "updated top-context is zero:" << url.str(); + kDebug() << "updated top-context is zero:" << url; return; } m_checked.insert(indexed); if(m_declaration.data() && ((m_processDeclarations && m_declarationTopContexts.contains(indexed)) || DUChainUtils::contextHasUse(topContext.data(), m_declaration.data()))) { if(!m_processed.contains(topContext->url())) { m_processed.insert(topContext->url()); lock.unlock(); emit processUsesSignal(topContext); processUses(topContext); lock.lock(); } } else { if(!m_declaration.data()) { kDebug() << "declaration has become invalid"; } } QList imports; foreach(const DUContext::Import &imported, topContext->importedParentContexts()) if(imported.context(0) && imported.context(0)->topContext()) imports << KDevelop::ReferencedTopDUContext(imported.context(0)->topContext()); foreach(const KDevelop::ReferencedTopDUContext &import, imports) { IndexedString url = import->url(); lock.unlock(); updateReady(url, import); lock.lock(); } } IndexedDeclaration UsesCollector::declaration() const { return m_declaration; } diff --git a/language/duchain/navigation/useswidget.cpp b/language/duchain/navigation/useswidget.cpp index 5d6e2c35d..95c63fe4b 100644 --- a/language/duchain/navigation/useswidget.cpp +++ b/language/duchain/navigation/useswidget.cpp @@ -1,638 +1,638 @@ /* Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "useswidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "language/duchain/parsingenvironment.h" #include "language/duchain/types/indexedtype.h" #include #include #include #include using namespace KDevelop; const int tooltipContextSize = 2; //How many lines around the use are shown in the tooltip const bool showUsesHeader = false; ///The returned text is fully escaped ///@param cutOff The total count of characters that should be cut of, all in all on both sides together. ///@param range The range that is highlighted, and that will be preserved during cutting, given that there is enough room beside it. QString highlightAndEscapeUseText(QString line, uint cutOff, SimpleRange range) { uint leftCutRoom = range.start.column; uint rightCutRoom = line.length() - range.end.column; if(range.start.column < 0 || range.end.column > line.length() || cutOff > leftCutRoom + rightCutRoom) return QString(); //Not enough room for cutting off on sides uint leftCut = 0; uint rightCut = 0; if(leftCutRoom < rightCutRoom) { if(leftCutRoom * 2 >= cutOff) { //Enough room on both sides. Just cut. leftCut = cutOff / 2; rightCut = cutOff - leftCut; }else{ //Not enough room in left side, but enough room all together leftCut = leftCutRoom; rightCut = cutOff - leftCut; } }else{ if(rightCutRoom * 2 >= cutOff) { //Enough room on both sides. Just cut. rightCut = cutOff / 2; leftCut = cutOff - rightCut; }else{ //Not enough room in right side, but enough room all together rightCut = rightCutRoom; leftCut = cutOff - rightCut; } } Q_ASSERT(leftCut + rightCut <= cutOff); line = line.left(line.length() - rightCut); line = line.mid(leftCut); range.start.column -= leftCut; range.end.column -= leftCut; Q_ASSERT(range.start.column >= 0 && range.end.column <= line.length()); //TODO: share code with context browser // mixing (255, 255, 0, 100) with white yields this: const QColor background(251, 250, 150); const QColor foreground(0, 0, 0); return "" + Qt::escape(line.left(range.start.column)) + "" + Qt::escape(line.mid(range.start.column, range.end.column - range.start.column)) + "" + Qt::escape(line.mid(range.end.column, line.length() - range.end.column)) + ""; } OneUseWidget::OneUseWidget(IndexedDeclaration declaration, IndexedString document, SimpleRange range, const CodeRepresentation& code) : m_range(new PersistentMovingRange(range, document)), m_declaration(declaration), m_document(document) { //Make the sizing of this widget independent of the content, because we will adapt the content to the size setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); m_sourceLine = code.line(m_range->range().start.line); m_layout = new QHBoxLayout(this); setLayout(m_layout); m_label = new QLabel(this); m_icon = new QLabel(this); m_icon->setPixmap(KIcon("code-function").pixmap(16)); connect(m_label, SIGNAL(linkActivated(QString)), this, SLOT(jumpTo())); DUChainReadLocker lock(DUChain::lock()); QString text = "" + i18nc("refers to a line in source code", "Line %1:", range.start.line) + QString(""); if(!m_sourceLine.isEmpty() && m_sourceLine.length() > m_range->range().end.column) { text += "  " + highlightAndEscapeUseText(m_sourceLine, 0, m_range->range()); //Useful tooltip: int start = m_range->range().start.line - tooltipContextSize; int end = m_range->range().end.line + tooltipContextSize + 1; QString toolTipText; for(int a = start; a < end; ++a) { QString lineText = code.line(a); if (m_range->range().start.line <= a && m_range->range().end.line >= a) { lineText = QString("") + Qt::escape(lineText) + QString(""); } if(!lineText.trimmed().isEmpty()) { toolTipText += lineText + "
"; } } if ( toolTipText.endsWith("
") ) { toolTipText.remove(toolTipText.length() - 4, 4); } setToolTip(QString("
") + toolTipText + QString("
")); } m_label->setText(text); m_layout->addWidget(m_icon); m_layout->addWidget(m_label); m_layout->setAlignment(Qt::AlignLeft); } void OneUseWidget::jumpTo() { //This is used to execute the slot delayed in the event-loop, so crashes are avoided ICore::self()->documentController()->openDocument(m_document.toUrl(), m_range->range().start.textCursor()); } OneUseWidget::~OneUseWidget() { } void OneUseWidget::resizeEvent ( QResizeEvent * event ) { ///Adapt the content QSize size = event->size(); SimpleRange range = m_range->range(); int cutOff = 0; int maxCutOff = m_sourceLine.length() - (range.end.column - range.start.column); //Reset so we also get more context while up-sizing m_label->setText(QString("") + i18nc("Refers to a line in source code", "Line %1", range.start.line+1) + QString(" %2").arg(highlightAndEscapeUseText(m_sourceLine, cutOff, range))); while(sizeHint().width() > size.width() && cutOff < maxCutOff) { //We've got to save space m_label->setText(QString("") + i18nc("Refers to a line in source code", "Line %1", range.start.line+1) + QString(" %2").arg(highlightAndEscapeUseText(m_sourceLine, cutOff, range))); cutOff += 5; } event->accept(); QWidget::resizeEvent(event); } void NavigatableWidgetList::setShowHeader(bool show) { if(show && !m_headerLayout->parent()) m_layout->insertLayout(0, m_headerLayout); else m_headerLayout->setParent(0); } NavigatableWidgetList::~NavigatableWidgetList() { delete m_headerLayout; } NavigatableWidgetList::NavigatableWidgetList(bool allowScrolling, uint maxHeight, bool vertical) : m_maxHeight(maxHeight), m_allowScrolling(allowScrolling) { m_layout = new QVBoxLayout; m_layout->setMargin(0); m_layout->setSizeConstraint(QLayout::SetMinAndMaxSize); m_layout->setSpacing(0); setBackgroundRole(QPalette::Base); m_useArrows = false; if(vertical) m_itemLayout = new QVBoxLayout; else m_itemLayout = new QHBoxLayout; m_itemLayout->setMargin(0); m_itemLayout->setSpacing(0); // m_layout->setSizeConstraint(QLayout::SetMinAndMaxSize); // setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Maximum); setWidgetResizable(true); m_headerLayout = new QHBoxLayout; m_headerLayout->setMargin(0); m_headerLayout->setSpacing(0); if(m_useArrows) { m_previousButton = new QToolButton(); m_previousButton->setIcon(KIcon("go-previous")); m_nextButton = new QToolButton(); m_nextButton->setIcon(KIcon("go-next")); m_headerLayout->addWidget(m_previousButton); m_headerLayout->addWidget(m_nextButton); } //hide these buttons for now, they're senseless m_layout->addLayout(m_headerLayout); QHBoxLayout* spaceLayout = new QHBoxLayout; spaceLayout->addSpacing(10); spaceLayout->addLayout(m_itemLayout); m_layout->addLayout(spaceLayout); if(maxHeight) setMaximumHeight(maxHeight); if(m_allowScrolling) { QWidget* contentsWidget = new QWidget; contentsWidget->setLayout(m_layout); setWidget(contentsWidget); }else{ setLayout(m_layout); } } void NavigatableWidgetList::deleteItems() { foreach(QWidget* item, items()) delete item; } void NavigatableWidgetList::addItem(QWidget* widget, int pos) { if(pos == -1) m_itemLayout->addWidget(widget); else m_itemLayout->insertWidget(pos, widget); } QList NavigatableWidgetList::items() const { QList ret; for(int a = 0; a < m_itemLayout->count(); ++a) { QWidgetItem* widgetItem = dynamic_cast(m_itemLayout->itemAt(a)); if(widgetItem) { ret << widgetItem->widget(); } } return ret; } bool NavigatableWidgetList::hasItems() const { return (bool)m_itemLayout->count(); } void NavigatableWidgetList::addHeaderItem(QWidget* widget, Qt::Alignment alignment) { if(m_useArrows) { Q_ASSERT(m_headerLayout->count() >= 2); //At least the 2 back/next buttons m_headerLayout->insertWidget(m_headerLayout->count()-1, widget, alignment); }else{ //We need to do this so the header doesn't get stretched widget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); m_headerLayout->insertWidget(m_headerLayout->count(), widget, alignment); // widget->setMaximumHeight(20); } } ///Returns whether the uses in the child should be a new uses-group bool isNewGroup(DUContext* parent, DUContext* child) { if(parent->type() == DUContext::Other && child->type() == DUContext::Other) return false; else return true; } uint countUses(int usedDeclarationIndex, DUContext* context) { uint ret = 0; for(int useIndex = 0; useIndex < context->usesCount(); ++useIndex) if(context->uses()[useIndex].m_declarationIndex == usedDeclarationIndex) ++ret; foreach(DUContext* child, context->childContexts()) if(!isNewGroup(context, child)) ret += countUses(usedDeclarationIndex, child); return ret; } QList createUseWidgets(const CodeRepresentation& code, int usedDeclarationIndex, IndexedDeclaration decl, DUContext* context) { QList ret; VERIFY_FOREGROUND_LOCKED for(int useIndex = 0; useIndex < context->usesCount(); ++useIndex) if(context->uses()[useIndex].m_declarationIndex == usedDeclarationIndex) ret << new OneUseWidget(decl, context->url(), context->transformFromLocalRevision(context->uses()[useIndex].m_range), code); foreach(DUContext* child, context->childContexts()) if(!isNewGroup(context, child)) ret += createUseWidgets(code, usedDeclarationIndex, decl, child); return ret; } ContextUsesWidget::ContextUsesWidget(const CodeRepresentation& code, QList usedDeclarations, IndexedDUContext context) : m_context(context) { setFrameShape(NoFrame); DUChainReadLocker lock(DUChain::lock()); QString headerText = i18n("Unknown context"); setUpdatesEnabled(false); if(context.data()) { DUContext* ctx = context.data(); if(ctx->scopeIdentifier(true).isEmpty()) headerText = i18n("Global"); else { headerText = ctx->scopeIdentifier(true).toString(); if(ctx->type() == DUContext::Function || (ctx->owner() && ctx->owner()->isFunctionDeclaration())) headerText += "(...)"; } QSet hadIndices; foreach(const IndexedDeclaration &usedDeclaration, usedDeclarations) { int usedDeclarationIndex = ctx->topContext()->indexForUsedDeclaration(usedDeclaration.data(), false); if(hadIndices.contains(usedDeclarationIndex)) continue; hadIndices.insert(usedDeclarationIndex); if(usedDeclarationIndex != std::numeric_limits::max()) { foreach(OneUseWidget* widget, createUseWidgets(code, usedDeclarationIndex, usedDeclaration, ctx)) addItem(widget); } } } QLabel* headerLabel = new QLabel(i18nc("%1: source file", "In %1", "" + Qt::escape(headerText) + ": ")); addHeaderItem(headerLabel); setUpdatesEnabled(true); connect(headerLabel, SIGNAL(linkActivated(QString)), this, SLOT(linkWasActivated(QString))); } void ContextUsesWidget::linkWasActivated(QString link) { if ( link == "navigateToFunction" ) { DUChainReadLocker lock(DUChain::lock()); if(m_context.context()) { CursorInRevision contextStart = m_context.context()->range().start; KTextEditor::Cursor cursor(contextStart.line, contextStart.column); ForegroundLock lock; ICore::self()->documentController()->openDocument(m_context.context()->url().toUrl(), cursor); } } } DeclarationWidget::DeclarationWidget(const CodeRepresentation& code, const IndexedDeclaration& decl) { setFrameShape(NoFrame); DUChainReadLocker lock(DUChain::lock()); setUpdatesEnabled(false); if (Declaration* dec = decl.data()) { QLabel* headerLabel = new QLabel(dec->isDefinition() ? i18n("Definition") : i18n("Declaration")); addHeaderItem(headerLabel); addItem(new OneUseWidget(decl, dec->url(), dec->rangeInCurrentRevision(), code)); } setUpdatesEnabled(true); } TopContextUsesWidget::TopContextUsesWidget(IndexedDeclaration declaration, QList allDeclarations, IndexedTopDUContext topContext) : m_topContext(topContext) , m_declaration(declaration) , m_allDeclarations(allDeclarations) , m_usesCount(0) { m_itemLayout->setContentsMargins(10, 0, 0, 5); setFrameShape(NoFrame); setUpdatesEnabled(false); DUChainReadLocker lock(DUChain::lock()); QHBoxLayout * labelLayout = new QHBoxLayout; QWidget* headerWidget = new QWidget; headerWidget->setLayout(labelLayout); headerWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); QLabel* label = new QLabel(this); m_icon = new QLabel(this); m_toggleButton = new QLabel(this); m_icon->setPixmap(KIcon("code-class").pixmap(16)); labelLayout->addWidget(m_icon); labelLayout->addWidget(label); labelLayout->addWidget(m_toggleButton); labelLayout->setAlignment(Qt::AlignLeft); if(topContext.isLoaded()) m_usesCount = DUChainUtils::contextCountUses(topContext.data(), declaration.data()); QString labelText = i18ncp("%1: number of uses, %2: filename with uses", "%2: 1 use", "%2: %1 uses", m_usesCount, ICore::self()->projectController()->prettyFileName(topContext.url().toUrl())); label->setText(labelText); m_toggleButton->setText("   [" + i18nc("Refers to closing a UI element", "Collapse") + "]"); connect(m_toggleButton, SIGNAL(linkActivated(QString)), this, SLOT(labelClicked())); addHeaderItem(headerWidget); setUpdatesEnabled(true); } int TopContextUsesWidget::usesCount() const { return m_usesCount; } QList buildContextUses(const CodeRepresentation& code, QList declarations, DUContext* context) { QList ret; if(!context->parentContext() || isNewGroup(context->parentContext(), context)) { ContextUsesWidget* created = new ContextUsesWidget(code, declarations, context); if(created->hasItems()) ret << created; else delete created; } foreach(DUContext* child, context->childContexts()) ret += buildContextUses(code, declarations, child); return ret; } void TopContextUsesWidget::setExpanded(bool expanded) { if(!expanded) { m_toggleButton->setText("   [" + i18nc("Refers to opening a UI element", "Expand") + "]"); deleteItems(); }else{ m_toggleButton->setText("   [" + i18nc("Refers to closing a UI element", "Collapse") + "]"); if(hasItems()) return; DUChainReadLocker lock(DUChain::lock()); TopDUContext* topContext = m_topContext.data(); if(topContext && m_declaration.data()) { CodeRepresentation::Ptr code = createCodeRepresentation(topContext->url()); setUpdatesEnabled(false); IndexedTopDUContext localTopContext(topContext); foreach(const IndexedDeclaration &decl, m_allDeclarations) { if(decl.indexedTopContext() == localTopContext) { addItem(new DeclarationWidget(*code, decl)); } } foreach(ContextUsesWidget* usesWidget, buildContextUses(*code, m_allDeclarations, topContext)) { addItem(usesWidget); } setUpdatesEnabled(true); } } } void TopContextUsesWidget::labelClicked() { if(hasItems()) { setExpanded(false); }else{ setExpanded(true); } } UsesWidget::~UsesWidget() { delete m_collector; } UsesWidget::UsesWidget(IndexedDeclaration declaration, UsesWidgetCollector* customCollector) : NavigatableWidgetList(true) { DUChainReadLocker lock(DUChain::lock()); setUpdatesEnabled(false); m_headerLine = new QLabel; redrawHeaderLine(); connect(m_headerLine, SIGNAL(linkActivated(QString)), this, SLOT(headerLinkActivated(QString))); m_layout->insertWidget(0, m_headerLine, 0, Qt::AlignTop); m_layout->setAlignment(Qt::AlignTop); m_itemLayout->setAlignment(Qt::AlignTop); m_progressBar = new QProgressBar; addHeaderItem(m_progressBar); if(!customCollector) { m_collector = new UsesWidgetCollector(declaration); }else{ m_collector = customCollector; } m_collector->setProcessDeclarations(true); m_collector->setWidget(this); m_collector->startCollecting(); setUpdatesEnabled(true); } void UsesWidget::redrawHeaderLine() { m_headerLine->setText(headerLineText()); } const QString UsesWidget::headerLineText() const { return i18np("1 use found", "%1 uses found", countAllUses()) + " • " "[" + i18n("Expand all") + "] • " "[" + i18n("Collapse all") + "]"; } unsigned int UsesWidget::countAllUses() const { unsigned int totalUses = 0; foreach ( QWidget* w, items() ) { if ( TopContextUsesWidget* useWidget = dynamic_cast(w) ) { totalUses += useWidget->usesCount(); } } return totalUses; } void UsesWidget::setAllExpanded(bool expanded) { foreach ( QWidget* w, items() ) { if ( TopContextUsesWidget* useWidget = dynamic_cast(w) ) { useWidget->setExpanded(expanded); } } } void UsesWidget::headerLinkActivated(QString linkName) { if(linkName == "expandAll") { setAllExpanded(true); } else if(linkName == "collapseAll") { setAllExpanded(false); } } UsesWidget::UsesWidgetCollector::UsesWidgetCollector(IndexedDeclaration decl) : UsesCollector(decl), m_widget(0) { } void UsesWidget::UsesWidgetCollector::setWidget(UsesWidget* widget ) { m_widget = widget; } void UsesWidget::UsesWidgetCollector::maximumProgress(uint max) { if(m_widget->m_progressBar) { m_widget->m_progressBar->setMaximum(max); m_widget->m_progressBar->setMinimum(0); m_widget->m_progressBar->setValue(0); }else{ kWarning() << "maximumProgress called twice"; } } void UsesWidget::UsesWidgetCollector::progress(uint processed, uint total) { m_widget->redrawHeaderLine(); if(m_widget->m_progressBar) { m_widget->m_progressBar->setValue(processed); if(processed == total) { m_widget->setUpdatesEnabled(false); delete m_widget->m_progressBar; m_widget->m_progressBar = 0; m_widget->setShowHeader(false); m_widget->setUpdatesEnabled(true); } }else{ kWarning() << "progress() called too often"; } } void UsesWidget::UsesWidgetCollector::processUses( KDevelop::ReferencedTopDUContext topContext ) { DUChainReadLocker lock(DUChain::lock()); - kDebug() << "processing" << topContext->url().str(); + kDebug() << "processing" << topContext->url(); TopContextUsesWidget* widget = new TopContextUsesWidget(declaration(), declarations(), topContext.data()); // move to back if it's just the declaration/definition bool toBack = widget->usesCount() == 0; // move to front the item belonging to the current open document IDocument* doc = ICore::self()->documentController()->activeDocument(); bool toFront = doc && doc->url().equals(topContext->url().toUrl()); widget->setExpanded(true); m_widget->addItem(widget, toFront ? 0 : toBack ? widget->items().size() : -1); m_widget->redrawHeaderLine(); } QSize KDevelop::UsesWidget::sizeHint() const { QSize ret = QWidget::sizeHint(); if(ret.height() < 300) ret.setHeight(300); return ret; } #include "useswidget.moc" diff --git a/language/duchain/parsingenvironment.cpp b/language/duchain/parsingenvironment.cpp index 6cd044866..d8ffa596d 100644 --- a/language/duchain/parsingenvironment.cpp +++ b/language/duchain/parsingenvironment.cpp @@ -1,388 +1,388 @@ /* Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "parsingenvironment.h" #include "topducontext.h" #include "duchainregister.h" #include "topducontextdynamicdata.h" #include "duchain.h" #include "duchainlock.h" #include "topducontextdata.h" #include #include #define ENSURE_READ_LOCKED if(indexedTopContext().isValid()) { ENSURE_CHAIN_READ_LOCKED } #define ENSURE_WRITE_LOCKED if(indexedTopContext().isValid()) { ENSURE_CHAIN_READ_LOCKED } namespace KDevelop { StaticParsingEnvironmentData* ParsingEnvironmentFile::m_staticData = 0; #if 0 ///Wrapper class around objects that are managed through the DUChain, and may contain arbitrary objects ///that support duchain-like store (IndexedString, StorableSet, and the likes). The object must not contain pointers ///or other non-persistent data. /// ///The object is stored during the normal duchain storage/cleanup cycles. template struct PersistentDUChainObject { ///@param fileName File-name that will be used to store the data of the object in the duchain directory PersistentDUChainObject(QString fileName) { object = (T*) new char[sizeof(T)]; if(!DUChain::self()->addPersistentObject(object, fileName, sizeof(T))) { //The constructor is called only if the object did not exist yet new (object) T(); } } ~PersistentDUChainObject() { DUChain::self()->unregisterPersistentObject(object); delete[] object; } T* object; }; #endif REGISTER_DUCHAIN_ITEM(ParsingEnvironmentFile); TopDUContext::Features ParsingEnvironmentFile::features() const { ENSURE_READ_LOCKED return d_func()->m_features; } ParsingEnvironment::ParsingEnvironment() { } ParsingEnvironment::~ParsingEnvironment() { } IndexedString ParsingEnvironmentFile::url() const { ENSURE_READ_LOCKED return d_func()->m_url; } bool ParsingEnvironmentFile::needsUpdate(const ParsingEnvironment* /*environment*/) const { ENSURE_READ_LOCKED return d_func()->m_allModificationRevisions.needsUpdate(); } bool ParsingEnvironmentFile::matchEnvironment(const ParsingEnvironment* /*environment*/) const { ENSURE_READ_LOCKED return true; } void ParsingEnvironmentFile::setTopContext(KDevelop::IndexedTopDUContext context) { if(d_func()->m_topContext == context) return; ENSURE_WRITE_LOCKED d_func_dynamic()->m_topContext = context; //Enforce an update of the 'features satisfied' caches TopDUContext::Features oldFeatures = features(); setFeatures(TopDUContext::Empty); setFeatures(oldFeatures); } KDevelop::IndexedTopDUContext ParsingEnvironmentFile::indexedTopContext() const { return d_func()->m_topContext; } const ModificationRevisionSet& ParsingEnvironmentFile::allModificationRevisions() const { ENSURE_READ_LOCKED return d_func()->m_allModificationRevisions; } void ParsingEnvironmentFile::addModificationRevisions(const ModificationRevisionSet& revisions) { ENSURE_WRITE_LOCKED d_func_dynamic()->m_allModificationRevisions += revisions; } ParsingEnvironmentFile::ParsingEnvironmentFile(ParsingEnvironmentFileData& data, const IndexedString& url) : DUChainBase(data) { d_func_dynamic()->m_url = url; d_func_dynamic()->m_modificationTime = ModificationRevision::revisionForFile(url); addModificationRevision(url, d_func_dynamic()->m_modificationTime); Q_ASSERT(d_func()->m_allModificationRevisions.index()); } ParsingEnvironmentFile::ParsingEnvironmentFile(const IndexedString& url) : DUChainBase(*new ParsingEnvironmentFileData()) { d_func_dynamic()->setClassId(this); d_func_dynamic()->m_url = url; d_func_dynamic()->m_modificationTime = ModificationRevision::revisionForFile(url); addModificationRevision(url, d_func_dynamic()->m_modificationTime); Q_ASSERT(d_func()->m_allModificationRevisions.index()); } TopDUContext* ParsingEnvironmentFile::topContext() const { ENSURE_READ_LOCKED return indexedTopContext().data(); } ParsingEnvironmentFile::~ParsingEnvironmentFile() { } ParsingEnvironmentFile::ParsingEnvironmentFile(ParsingEnvironmentFileData& data) : DUChainBase(data) { //If this triggers, the item has most probably not been initialized with the correct constructor that takes an IndexedString. Q_ASSERT(d_func()->m_allModificationRevisions.index()); } int ParsingEnvironment::type() const { return StandardParsingEnvironment; } int ParsingEnvironmentFile::type() const { ENSURE_READ_LOCKED return StandardParsingEnvironment; } bool ParsingEnvironmentFile::isProxyContext() const { ENSURE_READ_LOCKED return d_func()->m_isProxyContext; } void ParsingEnvironmentFile::setIsProxyContext(bool is) { ENSURE_WRITE_LOCKED d_func_dynamic()->m_isProxyContext = is; } QList< KSharedPtr > ParsingEnvironmentFile::imports() const { ENSURE_READ_LOCKED QList imp; IndexedTopDUContext top = indexedTopContext(); if(top.isLoaded()) { TopDUContext* topCtx = top.data(); FOREACH_FUNCTION(const DUContext::Import& import, topCtx->d_func()->m_importedContexts) imp << import.indexedContext(); }else{ imp = TopDUContextDynamicData::loadImports(top.index()); } QList< KSharedPtr > ret; foreach(const IndexedDUContext &ctx, imp) { KSharedPtr item = DUChain::self()->environmentFileForDocument(ctx.topContextIndex()); if(item) { ret << item; }else{ - kDebug() << url().str() << indexedTopContext().index() << ": invalid import" << ctx.topContextIndex(); + kDebug() << url() << indexedTopContext().index() << ": invalid import" << ctx.topContextIndex(); } } return ret; } QList< KSharedPtr > ParsingEnvironmentFile::importers() const { ENSURE_READ_LOCKED QList imp; IndexedTopDUContext top = indexedTopContext(); if(top.isLoaded()) { TopDUContext* topCtx = top.data(); FOREACH_FUNCTION(const IndexedDUContext& ctx, topCtx->d_func()->m_importers) imp << ctx; }else{ imp = TopDUContextDynamicData::loadImporters(top.index()); } QList< KSharedPtr > ret; foreach(const IndexedDUContext &ctx, imp) { KSharedPtr f = DUChain::self()->environmentFileForDocument(ctx.topContextIndex()); if(f) ret << f; else - kDebug() << url().str() << indexedTopContext().index() << ": invalid importer context" << ctx.topContextIndex(); + kDebug() << url() << indexedTopContext().index() << ": invalid importer context" << ctx.topContextIndex(); } return ret; } QMutex featureSatisfactionMutex; inline bool satisfied(TopDUContext::Features features, TopDUContext::Features required) { return (features & required) == required; } ///Makes sure the the file has the correct features attached, and if minimumFeatures contains AllDeclarationsContextsAndUsesForRecursive, then also checks all imports. bool ParsingEnvironmentFile::featuresMatch(TopDUContext::Features minimumFeatures, QSet& checked) const { if(checked.contains(this)) return true; checked.insert(this); TopDUContext::Features localRequired = (TopDUContext::Features) (minimumFeatures | ParseJob::staticMinimumFeatures(url())); //Check other 'local' requirements localRequired = (TopDUContext::Features)(localRequired & (TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST)); if(!satisfied(features(), localRequired)) return false; if(ParseJob::hasStaticMinimumFeatures()) { //Do a manual recursion to check whether any of the relevant contexts has static minimum features set ///@todo Only do this if one of the imports actually has static features attached (by RecursiveImports set intersection) foreach(const ParsingEnvironmentFilePointer &import, imports()) if(!import->featuresMatch(minimumFeatures & TopDUContext::Recursive ? minimumFeatures : ((TopDUContext::Features)0), checked)) return false; }else if(minimumFeatures & TopDUContext::Recursive) { QMutexLocker lock(&featureSatisfactionMutex); TopDUContext::IndexedRecursiveImports recursiveImportIndices = d_func()->m_importsCache; if(recursiveImportIndices.isEmpty()) { //Unfortunately, we have to load the top-context TopDUContext* top = topContext(); if(top) recursiveImportIndices = top->recursiveImportIndices(); } ///@todo Do not create temporary intersected sets //Use the features-cache to efficiently check the recursive satisfaction of the features if(satisfied(minimumFeatures, TopDUContext::AST) && !((m_staticData->ASTSatisfied & recursiveImportIndices) == recursiveImportIndices)) return false; if(satisfied(minimumFeatures, TopDUContext::AllDeclarationsContextsAndUses)) return (m_staticData->allDeclarationsAndUsesSatisfied & recursiveImportIndices) == recursiveImportIndices; else if(satisfied(minimumFeatures, TopDUContext::AllDeclarationsAndContexts)) return (m_staticData->allDeclarationsSatisfied & recursiveImportIndices) == recursiveImportIndices; else if(satisfied(minimumFeatures, TopDUContext::VisibleDeclarationsAndContexts)) return (m_staticData->visibleDeclarationsSatisfied & recursiveImportIndices) == recursiveImportIndices; else if(satisfied(minimumFeatures, TopDUContext::SimplifiedVisibleDeclarationsAndContexts)) return (m_staticData->simplifiedVisibleDeclarationsSatisfied & recursiveImportIndices) == recursiveImportIndices; } return true; } void ParsingEnvironmentFile::setFeatures(TopDUContext::Features features) { if(d_func()->m_features == features) return; ENSURE_WRITE_LOCKED d_func_dynamic()->m_features = features; if(indexedTopContext().isValid()) { QMutexLocker lock(&featureSatisfactionMutex); if(!satisfied(features, TopDUContext::SimplifiedVisibleDeclarationsAndContexts)) m_staticData->simplifiedVisibleDeclarationsSatisfied.remove(indexedTopContext()); else m_staticData->simplifiedVisibleDeclarationsSatisfied.insert(indexedTopContext()); if(!satisfied(features, TopDUContext::VisibleDeclarationsAndContexts)) m_staticData->visibleDeclarationsSatisfied.remove(indexedTopContext()); else m_staticData->visibleDeclarationsSatisfied.insert(indexedTopContext()); if(!satisfied(features, TopDUContext::AllDeclarationsAndContexts)) m_staticData->allDeclarationsSatisfied.remove(indexedTopContext()); else m_staticData->allDeclarationsSatisfied.insert(indexedTopContext()); if(!satisfied(features, TopDUContext::AllDeclarationsContextsAndUses)) m_staticData->allDeclarationsAndUsesSatisfied.remove(indexedTopContext()); else m_staticData->allDeclarationsAndUsesSatisfied.insert(indexedTopContext()); if(!satisfied(features, TopDUContext::AST)) m_staticData->ASTSatisfied.remove(indexedTopContext()); else m_staticData->ASTSatisfied.insert(indexedTopContext()); } } bool ParsingEnvironmentFile::featuresSatisfied(KDevelop::TopDUContext::Features minimumFeatures) const { ENSURE_READ_LOCKED QSet checked; if(minimumFeatures & TopDUContext::ForceUpdate) return false; return featuresMatch(minimumFeatures, checked); } void ParsingEnvironmentFile::clearModificationRevisions() { ENSURE_WRITE_LOCKED d_func_dynamic()->m_allModificationRevisions.clear(); d_func_dynamic()->m_allModificationRevisions.addModificationRevision(d_func()->m_url, d_func()->m_modificationTime); } void ParsingEnvironmentFile::addModificationRevision(const IndexedString& url, const ModificationRevision& revision) { ENSURE_WRITE_LOCKED d_func_dynamic()->m_allModificationRevisions.addModificationRevision(url, revision); { //Test Q_ASSERT(d_func_dynamic()->m_allModificationRevisions.index()); bool result = d_func_dynamic()->m_allModificationRevisions.removeModificationRevision(url, revision); Q_UNUSED( result ); Q_ASSERT( result ); d_func_dynamic()->m_allModificationRevisions.addModificationRevision(url, revision); } } void ParsingEnvironmentFile::setModificationRevision( const KDevelop::ModificationRevision& rev ) { ENSURE_WRITE_LOCKED Q_ASSERT(d_func_dynamic()->m_allModificationRevisions.index()); bool result = d_func_dynamic()->m_allModificationRevisions.removeModificationRevision(d_func()->m_url, d_func()->m_modificationTime); Q_ASSERT( result ); Q_UNUSED( result ); #ifdef LEXERCACHE_DEBUG if(debugging()) { kDebug() << id(this) << "setting modification-revision" << rev.toString(); } #endif d_func_dynamic()->m_modificationTime = rev; #ifdef LEXERCACHE_DEBUG if(debugging()) { kDebug() << id(this) << "new modification-revision" << m_modificationTime; } #endif d_func_dynamic()->m_allModificationRevisions.addModificationRevision(d_func()->m_url, d_func()->m_modificationTime); } KDevelop::ModificationRevision ParsingEnvironmentFile::modificationRevision() const { ENSURE_READ_LOCKED return d_func()->m_modificationTime; } IndexedString ParsingEnvironmentFile::language() const { return d_func()->m_language; } void ParsingEnvironmentFile::setLanguage(IndexedString language) { d_func_dynamic()->m_language = language; } const KDevelop::TopDUContext::IndexedRecursiveImports& ParsingEnvironmentFile::importsCache() const { return d_func()->m_importsCache; } void ParsingEnvironmentFile::setImportsCache(const KDevelop::TopDUContext::IndexedRecursiveImports& importsCache) { d_func_dynamic()->m_importsCache = importsCache; } } //KDevelop diff --git a/language/duchain/persistentsymboltable.cpp b/language/duchain/persistentsymboltable.cpp index 2c30ef767..66e1142f6 100644 --- a/language/duchain/persistentsymboltable.cpp +++ b/language/duchain/persistentsymboltable.cpp @@ -1,399 +1,399 @@ /* This file is part of KDevelop Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "persistentsymboltable.h" #include #include #include "declarationid.h" #include "appendedlist.h" #include "repositories/itemrepository.h" #include "identifier.h" #include "ducontext.h" #include "topducontext.h" #include "duchain.h" #include "duchainlock.h" #include //For now, just _always_ use the cache const uint MinimumCountForCache = 1; namespace KDevelop { Utils::BasicSetRepository* RecursiveImportCacheRepository::repository() { static Utils::BasicSetRepository recursiveImportCacheRepositoryObject("Recursive Imports Cache", 0, false); return &recursiveImportCacheRepositoryObject; } DEFINE_LIST_MEMBER_HASH(PersistentSymbolTableItem, declarations, IndexedDeclaration) class PersistentSymbolTableItem { public: PersistentSymbolTableItem() : centralFreeItem(-1) { initializeAppendedLists(); } PersistentSymbolTableItem(const PersistentSymbolTableItem& rhs, bool dynamic = true) : id(rhs.id), centralFreeItem(rhs.centralFreeItem) { initializeAppendedLists(dynamic); copyListsFrom(rhs); } ~PersistentSymbolTableItem() { freeAppendedLists(); } inline unsigned int hash() const { //We only compare the declaration. This allows us implementing a map, although the item-repository //originally represents a set. return id.getIndex(); } unsigned int itemSize() const { return dynamicSize(); } uint classSize() const { return sizeof(PersistentSymbolTableItem); } IndexedQualifiedIdentifier id; int centralFreeItem; START_APPENDED_LISTS(PersistentSymbolTableItem); APPENDED_LIST_FIRST(PersistentSymbolTableItem, IndexedDeclaration, declarations); END_APPENDED_LISTS(PersistentSymbolTableItem, declarations); }; class PersistentSymbolTableRequestItem { public: PersistentSymbolTableRequestItem(const PersistentSymbolTableItem& item) : m_item(item) { } enum { AverageSize = 30 //This should be the approximate average size of an Item }; unsigned int hash() const { return m_item.hash(); } uint itemSize() const { return m_item.itemSize(); } void createItem(PersistentSymbolTableItem* item) const { new (item) PersistentSymbolTableItem(m_item, false); } static void destroy(PersistentSymbolTableItem* item, KDevelop::AbstractItemRepository&) { item->~PersistentSymbolTableItem(); } static bool persistent(const PersistentSymbolTableItem*) { return true; //Nothing to do } bool equals(const PersistentSymbolTableItem* item) const { return m_item.id == item->id; } const PersistentSymbolTableItem& m_item; }; template struct CacheEntry { typedef KDevVarLengthArray Data; typedef QHash DataHash; DataHash m_hash; }; class PersistentSymbolTablePrivate { public: PersistentSymbolTablePrivate() : m_declarations("Persistent Declaration Table") { } //Maps declaration-ids to declarations ItemRepository m_declarations; QHash > m_declarationsCache; //We cache the imports so the currently used nodes are very close in memory, which leads to much better CPU cache utilization QHash m_importsCache; }; void PersistentSymbolTable::clearCache() { ENSURE_CHAIN_WRITE_LOCKED { QMutexLocker lock(d->m_declarations.mutex()); d->m_importsCache.clear(); d->m_declarationsCache.clear(); } } PersistentSymbolTable::PersistentSymbolTable() : d(new PersistentSymbolTablePrivate()) { } PersistentSymbolTable::~PersistentSymbolTable() { //Workaround for a strange destruction-order related crash duing shutdown //We just let the data leak. This doesn't hurt, as there is no meaningful destructors. // delete d; } void PersistentSymbolTable::addDeclaration(const IndexedQualifiedIdentifier& id, const IndexedDeclaration& declaration) { QMutexLocker lock(d->m_declarations.mutex()); ENSURE_CHAIN_WRITE_LOCKED d->m_declarationsCache.remove(id); PersistentSymbolTableItem item; item.id = id; PersistentSymbolTableRequestItem request(item); uint index = d->m_declarations.findIndex(item); if(index) { //Check whether the item is already in the mapped list, else copy the list into the new created item const PersistentSymbolTableItem* oldItem = d->m_declarations.itemFromIndex(index); EmbeddedTreeAlgorithms alg(oldItem->declarations(), oldItem->declarationsSize(), oldItem->centralFreeItem); if(alg.indexOf(declaration) != -1) return; DynamicItem editableItem = d->m_declarations.dynamicItemFromIndex(index); EmbeddedTreeAddItem add(const_cast(editableItem->declarations()), editableItem->declarationsSize(), editableItem->centralFreeItem, declaration); uint newSize = add.newItemCount(); if(newSize != editableItem->declarationsSize()) { //We need to resize. Update and fill the new item, and delete the old item. item.declarationsList().resize(newSize); add.transferData(item.declarationsList().data(), newSize, &item.centralFreeItem); d->m_declarations.deleteItem(index); Q_ASSERT(!d->m_declarations.findIndex(request)); }else{ //We're fine, the item could be added to the existing list return; } }else{ item.declarationsList().append(declaration); } //This inserts the changed item d->m_declarations.index(request); } void PersistentSymbolTable::removeDeclaration(const IndexedQualifiedIdentifier& id, const IndexedDeclaration& declaration) { QMutexLocker lock(d->m_declarations.mutex()); ENSURE_CHAIN_WRITE_LOCKED d->m_declarationsCache.remove(id); Q_ASSERT(!d->m_declarationsCache.contains(id)); PersistentSymbolTableItem item; item.id = id; PersistentSymbolTableRequestItem request(item); uint index = d->m_declarations.findIndex(item); if(index) { //Check whether the item is already in the mapped list, else copy the list into the new created item const PersistentSymbolTableItem* oldItem = d->m_declarations.itemFromIndex(index); EmbeddedTreeAlgorithms alg(oldItem->declarations(), oldItem->declarationsSize(), oldItem->centralFreeItem); if(alg.indexOf(declaration) == -1) return; DynamicItem editableItem = d->m_declarations.dynamicItemFromIndex(index); EmbeddedTreeRemoveItem remove(const_cast(editableItem->declarations()), editableItem->declarationsSize(), editableItem->centralFreeItem, declaration); uint newSize = remove.newItemCount(); if(newSize != editableItem->declarationsSize()) { //We need to resize. Update and fill the new item, and delete the old item. item.declarationsList().resize(newSize); remove.transferData(item.declarationsList().data(), newSize, &item.centralFreeItem); d->m_declarations.deleteItem(index); Q_ASSERT(!d->m_declarations.findIndex(request)); }else{ //We're fine, the item could be added to the existing list return; } } //This inserts the changed item if(item.declarationsSize()) d->m_declarations.index(request); } struct DeclarationCacheVisitor { DeclarationCacheVisitor(KDevVarLengthArray& _cache) : cache(_cache) { } bool operator()(const IndexedDeclaration& decl) const { cache.append(decl); return true; } KDevVarLengthArray& cache; }; PersistentSymbolTable::FilteredDeclarationIterator PersistentSymbolTable::getFilteredDeclarations(const IndexedQualifiedIdentifier& id, const TopDUContext::IndexedRecursiveImports& visibility) const { QMutexLocker lock(d->m_declarations.mutex()); ENSURE_CHAIN_READ_LOCKED Declarations decls = getDeclarations(id).iterator(); CachedIndexedRecursiveImports cachedImports; QHash::const_iterator it = d->m_importsCache.constFind(visibility); if(it != d->m_importsCache.constEnd()) { cachedImports = *it; }else{ cachedImports = CachedIndexedRecursiveImports(visibility.set().stdSet()); d->m_importsCache.insert(visibility, cachedImports); } if(decls.dataSize() > MinimumCountForCache) { //Do visibility caching CacheEntry& cached(d->m_declarationsCache[id]); CacheEntry::DataHash::const_iterator cacheIt = cached.m_hash.constFind(visibility); if(cacheIt != cached.m_hash.constEnd()) return FilteredDeclarationIterator(Declarations::Iterator(cacheIt->constData(), cacheIt->size(), -1), cachedImports); CacheEntry::DataHash::iterator insertIt = cached.m_hash.insert(visibility, KDevVarLengthArray()); KDevVarLengthArray& cache(*insertIt); { typedef ConvenientEmbeddedSetTreeFilterVisitor FilteredDeclarationCacheVisitor; //The visitor visits all the declarations from within its constructor DeclarationCacheVisitor v(cache); FilteredDeclarationCacheVisitor visitor(v, decls.iterator(), cachedImports); } return FilteredDeclarationIterator(Declarations::Iterator(cache.constData(), cache.size(), -1), cachedImports, true); }else{ return FilteredDeclarationIterator(decls.iterator(), cachedImports); } } PersistentSymbolTable::Declarations PersistentSymbolTable::getDeclarations(const IndexedQualifiedIdentifier& id) const { QMutexLocker lock(d->m_declarations.mutex()); ENSURE_CHAIN_READ_LOCKED PersistentSymbolTableItem item; item.id = id; PersistentSymbolTableRequestItem request(item); uint index = d->m_declarations.findIndex(item); if(index) { const PersistentSymbolTableItem* repositoryItem = d->m_declarations.itemFromIndex(index); return PersistentSymbolTable::Declarations(repositoryItem->declarations(), repositoryItem->declarationsSize(), repositoryItem->centralFreeItem); }else{ return PersistentSymbolTable::Declarations(); } } void PersistentSymbolTable::declarations(const IndexedQualifiedIdentifier& id, uint& countTarget, const IndexedDeclaration*& declarationsTarget) const { QMutexLocker lock(d->m_declarations.mutex()); ENSURE_CHAIN_READ_LOCKED PersistentSymbolTableItem item; item.id = id; PersistentSymbolTableRequestItem request(item); uint index = d->m_declarations.findIndex(item); if(index) { const PersistentSymbolTableItem* repositoryItem = d->m_declarations.itemFromIndex(index); countTarget = repositoryItem->declarationsSize(); declarationsTarget = repositoryItem->declarations(); }else{ countTarget = 0; declarationsTarget = 0; } } struct AnalisysVisitor { bool operator() (const PersistentSymbolTableItem* item) { QualifiedIdentifier id(item->id.identifier()); if(identifiers.contains(id)) { kDebug() << "identifier" << id.toString() << "appears for" << identifiers[id] << "th time"; } ++identifiers[id]; for(uint a = 0; a < item->declarationsSize(); ++a) { IndexedDeclaration decl(item->declarations()[a]); if(!decl.isDummy()) { if(declarations.contains(decl)) { kDebug() << "declaration found for multiple identifiers. Previous identifier:" << declarations[decl].toString() << "current identifier:" << id.toString(); }else{ declarations.insert(decl, id); } } if(decl.data() && decl.data()->qualifiedIdentifier() != item->id.identifier()) { - kDebug() << decl.data()->url().str() << "declaration" << decl.data()->qualifiedIdentifier() << "is registered as" << item->id.identifier(); + kDebug() << decl.data()->url() << "declaration" << decl.data()->qualifiedIdentifier() << "is registered as" << item->id.identifier(); } if(!decl.data() && !decl.isDummy()) { - kDebug() << "Item in symbol-table is invalid:" << id.toString() << item->declarations()[a].localIndex() << IndexedTopDUContext(item->declarations()[a].topContextIndex()).url().str(); + kDebug() << "Item in symbol-table is invalid:" << id.toString() << item->declarations()[a].localIndex() << IndexedTopDUContext(item->declarations()[a].topContextIndex()).url(); } } return true; } QHash identifiers; QHash declarations; }; void PersistentSymbolTable::selfAnalysis() { { QMutexLocker lock(d->m_declarations.mutex()); AnalisysVisitor v; kDebug() << d->m_declarations.statistics(); d->m_declarations.visitAllItems(v); kDebug() << "visited" << v.identifiers.size() << "identifiers"; } } PersistentSymbolTable& PersistentSymbolTable::self() { static PersistentSymbolTable ret; return ret; } } diff --git a/language/duchain/tests/test_itemrepository.cpp b/language/duchain/tests/test_itemrepository.cpp index 6ec6b3db2..b794858bc 100644 --- a/language/duchain/tests/test_itemrepository.cpp +++ b/language/duchain/tests/test_itemrepository.cpp @@ -1,215 +1,215 @@ #include #include #include #include #include #include #include #include struct TestItem { TestItem(uint hash, uint dataSize) : m_hash(hash), m_dataSize(dataSize) { } //Every item has to implement this function, and return a valid hash. //Must be exactly the same hash value as ExampleItemRequest::hash() has returned while creating the item. unsigned int hash() const { return m_hash; } //Every item has to implement this function, and return the complete size this item takes in memory. //Must be exactly the same value as ExampleItemRequest::itemSize() has returned while creating the item. unsigned int itemSize() const { return sizeof(TestItem) + m_dataSize; } void verifySame(const TestItem* rhs) { QVERIFY(rhs->m_hash == m_hash); QCOMPARE(itemSize(), rhs->itemSize()); QVERIFY(memcmp((char*)this, rhs, itemSize()) == 0); } uint m_hash; uint m_dataSize; }; struct TestItemRequest { TestItem& m_item; TestItemRequest(TestItem& item) : m_item(item) { } enum { AverageSize = 700 //This should be the approximate average size of an Item }; uint hash() const { return m_item.hash(); } //Should return the size of an item created with createItem size_t itemSize() const { return m_item.itemSize(); } void createItem(TestItem* item) const { memcpy(item, &m_item, m_item.itemSize()); } static void destroy(TestItem* /*item*/, KDevelop::AbstractItemRepository&) { //Nothing to do } static bool persistent(const TestItem* /*item*/) { return true; } //Should return whether the here requested item equals the given item bool equals(const TestItem* item) const { return hash() == item->hash(); } }; uint smallItemsFraction = 20; //Fraction of items betwen 0 and 1 kb uint largeItemsFraction = 1; //Fraction of items between 0 and 200 kb uint cycles = 100000; uint deletionProbability = 1; //Percentual probability that a checked item is deleted. Per-cycle probability must be multiplied with checksPerCycle. uint checksPerCycle = 10; TestItem* createItem(uint id, uint size) { TestItem* ret; char* data = new char[size]; uint dataSize = size - sizeof(TestItem); ret = new (data) TestItem(id, dataSize); //Fill in same random pattern for(uint a = 0; a < dataSize; ++a) data[sizeof(TestItem) + a] = (char)(a + id); return ret; } ///@todo Add a test where the complete content is deleted again, and make sure the result has a nice structure ///@todo More consistency and lost-space tests, especially about monster-buckets. Make sure their space is re-claimed class TestItemRepository : public QObject { Q_OBJECT private slots: void initTestCase() { KDevelop::AutoTestShell::init(); KDevelop::TestCore* core = new KDevelop::TestCore(); core->initialize(KDevelop::Core::NoUi); } void cleanupTestCase() { KDevelop::TestCore::shutdown(); } void testItemRepository() { KDevelop::ItemRepository repository("TestItemRepository"); uint itemId = 0; QHash realItemsByIndex; QHash realItemsById; uint totalInsertions = 0, totalDeletions = 0; uint maxSize = 0; uint totalSize = 0; srand(time(NULL)); uint highestSeenIndex = 0; for(uint a = 0; a < cycles; ++a) { { //Insert an item uint itemDecision = rand() % (smallItemsFraction + largeItemsFraction); uint itemSize; if(itemDecision < largeItemsFraction) { //Create a large item: Up to 200kb itemSize = (rand() % 200000) + sizeof(TestItem); } else itemSize = (rand() % 1000) + sizeof(TestItem); TestItem* item = createItem(++itemId, itemSize); Q_ASSERT(item->hash() == itemId); item->verifySame(item); uint index = repository.index(TestItemRequest(*item)); if(index > highestSeenIndex) highestSeenIndex = index; Q_ASSERT(index); realItemsByIndex.insert(index, item); realItemsById.insert(itemId, item); ++totalInsertions; totalSize += itemSize; if(itemSize > maxSize) maxSize = itemSize; } for(uint a = 0; a < checksPerCycle; ++a) { //Check an item uint pick = rand() % itemId; if(realItemsById.contains(pick)) { uint index = repository.findIndex(*realItemsById[pick]); QVERIFY(index); QVERIFY(realItemsByIndex.contains(index)); realItemsByIndex[index]->verifySame(repository.itemFromIndex(index)); if((uint) (rand() % 100) < deletionProbability) { ++totalDeletions; //Delete the item repository.deleteItem(index); QVERIFY(!repository.findIndex(*realItemsById[pick])); uint newIndex = repository.index(*realItemsById[pick]); QVERIFY(newIndex); realItemsByIndex[index]->verifySame(repository.itemFromIndex(newIndex)); #ifdef POSITION_TEST //Since we have previously deleted the item, there must be enough space if(!((newIndex >> 16) <= (highestSeenIndex >> 16))) { kDebug() << "size:" << realItemsById[pick]->itemSize(); kDebug() << "previous highest seen bucket:" << (highestSeenIndex >> 16); kDebug() << "new bucket:" << (newIndex >> 16); } QVERIFY((newIndex >> 16) <= (highestSeenIndex >> 16)); #endif repository.deleteItem(newIndex); QVERIFY(!repository.findIndex(*realItemsById[pick])); delete realItemsById[pick]; realItemsById.remove(pick); realItemsByIndex.remove(index); } } } } kDebug() << "total insertions:" << totalInsertions << "total deletions:" << totalDeletions << "average item size:" << (totalSize / totalInsertions) << "biggest item size:" << maxSize; KDevelop::ItemRepository::Statistics stats = repository.statistics(); kDebug() << stats; QVERIFY(stats.freeUnreachableSpace < stats.freeSpaceInBuckets/100); // < 1% of the free space is unreachable QVERIFY(stats.freeSpaceInBuckets < stats.usedSpaceForBuckets); // < 20% free space } void testLeaks() { KDevelop::ItemRepository repository("TestItemRepository"); QList items; for(int i = 0; i < 10000; ++i) { TestItem* item = createItem(i, (rand() % 1000) + sizeof(TestItem)); items << item; repository.index(TestItemRequest(*item)); } qDeleteAll(items); items.clear(); } void testStringSharing() { QString qString; - qString.fill('.', 1000); + qString.fill('.', 1000); KDevelop::IndexedString indexedString(qString); const int repeat = 10000; QVector strings; strings.resize(repeat); for(int i = 0; i < repeat; ++i) { - strings[i] = indexedString.str(); + strings[i] = indexedString.toString(); QCOMPARE(qString, strings[i]); } } }; #include "test_itemrepository.moc" QTEST_MAIN(TestItemRepository) diff --git a/language/highlighting/codehighlighting.cpp b/language/highlighting/codehighlighting.cpp index f874c62e2..f590510a9 100644 --- a/language/highlighting/codehighlighting.cpp +++ b/language/highlighting/codehighlighting.cpp @@ -1,601 +1,601 @@ /* * This file is part of KDevelop * * Copyright 2007-2010 David Nolden * Copyright 2006 Hamish Rodda * Copyright 2009 Milian Wolff * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "codehighlighting.h" #include #include "../../interfaces/icore.h" #include "../../interfaces/ilanguagecontroller.h" #include "../../interfaces/icompletionsettings.h" #include "../../interfaces/foregroundlock.h" #include "../duchain/declaration.h" #include "../duchain/types/functiontype.h" #include "../duchain/types/enumeratortype.h" #include "../duchain/types/typealiastype.h" #include "../duchain/types/enumerationtype.h" #include "../duchain/types/structuretype.h" #include "../duchain/functiondefinition.h" #include "../duchain/use.h" #include "colorcache.h" #include "configurablecolors.h" #include #include #include #include #include using namespace KTextEditor; static const float highlightingZDepth = -500; #define ifDebug(x) namespace KDevelop { ///@todo Don't highlighting everything, only what is visible on-demand CodeHighlighting::CodeHighlighting( QObject * parent ) : QObject(parent), m_localColorization(true), m_globalColorization(true), m_dataMutex(QMutex::Recursive) { qRegisterMetaType("KDevelop::IndexedString"); adaptToColorChanges(); connect(ColorCache::self(), SIGNAL(colorsGotChanged()), this, SLOT(adaptToColorChanges())); } CodeHighlighting::~CodeHighlighting( ) { qDeleteAll(m_highlights.values()); } void CodeHighlighting::adaptToColorChanges() { QMutexLocker lock(&m_dataMutex); // disable local highlighting if the ratio is set to 0 m_localColorization = ICore::self()->languageController()->completionSettings()->localColorizationLevel() > 0; // disable global highlighting if the ratio is set to 0 m_globalColorization = ICore::self()->languageController()->completionSettings()->globalColorizationLevel() > 0; m_declarationAttributes.clear(); m_definitionAttributes.clear(); m_depthAttributes.clear(); m_referenceAttributes.clear(); } KTextEditor::Attribute::Ptr CodeHighlighting::attributeForType( Types type, Contexts context, const QColor &color ) const { QMutexLocker lock(&m_dataMutex); KTextEditor::Attribute::Ptr a; switch (context) { case DefinitionContext: a = m_definitionAttributes[type]; break; case DeclarationContext: a = m_declarationAttributes[type]; break; case ReferenceContext: a = m_referenceAttributes[type]; break; } if ( !a || color.isValid() ) { a = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute(*ColorCache::self()->defaultColors()->getAttribute(type))); if ( context == DefinitionContext || context == DeclarationContext ) { a->setFontBold(); } if( color.isValid() ) { a->setForeground(color); // a->setBackground(QColor(mix(0xffffff-color, backgroundColor(), 255-backgroundTinting))); } else { switch (context) { case DefinitionContext: m_definitionAttributes.insert(type, a); break; case DeclarationContext: m_declarationAttributes.insert(type, a); break; case ReferenceContext: m_referenceAttributes.insert(type, a); break; } } } return a; } ColorMap emptyColorMap() { ColorMap ret(ColorCache::self()->validColorCount()+1, 0); return ret; } CodeHighlightingInstance* CodeHighlighting::createInstance() const { return new CodeHighlightingInstance(this); } bool CodeHighlighting::hasHighlighting(IndexedString url) const { DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(url); if(tracker) { QMutexLocker lock(&m_dataMutex); return m_highlights.contains(tracker) && !m_highlights[tracker]->m_highlightedRanges.isEmpty(); } return false; } void CodeHighlighting::highlightDUChain(ReferencedTopDUContext context) { ENSURE_CHAIN_NOT_LOCKED IndexedString url; { DUChainReadLocker lock; url = context->url(); } // This prevents the background-parser from updating the top-context while we're working with it UrlParseLock urlLock(context->url()); DUChainReadLocker lock; qint64 revision = context->parsingEnvironmentFile()->modificationRevision().revision; kDebug() << "highlighting du chain" << url.toUrl(); if ( !m_localColorization && !m_globalColorization ) { kDebug() << "highlighting disabled"; QMetaObject::invokeMethod(this, "clearHighlightingForDocument", Qt::QueuedConnection, Q_ARG(KDevelop::IndexedString, url)); return; } CodeHighlightingInstance* instance = createInstance(); lock.unlock(); instance->highlightDUChain(context.data()); DocumentHighlighting* highlighting = new DocumentHighlighting; highlighting->m_document = url; highlighting->m_waitingRevision = revision; highlighting->m_waiting = instance->m_highlight; qSort(highlighting->m_waiting.begin(), highlighting->m_waiting.end()); QMetaObject::invokeMethod(this, "applyHighlighting", Qt::QueuedConnection, Q_ARG(void*, highlighting)); delete instance; } void CodeHighlightingInstance::highlightDUChain(TopDUContext* context) { m_contextClasses.clear(); m_useClassCache = true; //Highlight highlightDUChain(context, QHash(), emptyColorMap()); m_functionColorsForDeclarations.clear(); m_functionDeclarationsForColors.clear(); m_useClassCache = false; m_contextClasses.clear(); } void CodeHighlightingInstance::highlightDUChain(DUContext* context, QHash colorsForDeclarations, ColorMap declarationsForColors) { DUChainReadLocker lock; TopDUContext* top = context->topContext(); //Merge the colors from the function arguments foreach( const DUContext::Import &imported, context->importedParentContexts() ) { if(!imported.context(top) || (imported.context(top)->type() != DUContext::Other && imported.context(top)->type() != DUContext::Function)) continue; //For now it's enough simply copying them, because we only pass on colors within function bodies. if (m_functionColorsForDeclarations.contains(imported.context(top))) colorsForDeclarations = m_functionColorsForDeclarations[imported.context(top)]; if (m_functionDeclarationsForColors.contains(imported.context(top))) declarationsForColors = m_functionDeclarationsForColors[imported.context(top)]; } QList takeFreeColors; foreach (Declaration* dec, context->localDeclarations()) { if (!useRainbowColor(dec)) { highlightDeclaration(dec, QColor(QColor::Invalid)); continue; } //Initially pick a color using the hash, so the chances are good that the same identifier gets the same color always. uint colorNum = dec->identifier().hash() % ColorCache::self()->validColorCount(); if( declarationsForColors[colorNum] ) { takeFreeColors << dec; //Use one of the colors that stays free continue; } colorsForDeclarations[dec] = colorNum; declarationsForColors[colorNum] = dec; highlightDeclaration(dec, ColorCache::self()->generatedColor(colorNum)); } foreach( Declaration* dec, takeFreeColors ) { uint colorNum = dec->identifier().hash() % ColorCache::self()->validColorCount(); uint oldColorNum = colorNum; while( declarationsForColors[colorNum] ) { colorNum = (colorNum+1) % ColorCache::self()->validColorCount(); if( colorNum == oldColorNum ) { colorNum = ColorCache::self()->validColorCount(); break; } } if(colorNum != ColorCache::self()->validColorCount()) { //If no color could be found, use default color,, not black colorsForDeclarations[dec] = colorNum; declarationsForColors[colorNum] = dec; highlightDeclaration(dec, ColorCache::self()->generatedColor(colorNum)); }else{ highlightDeclaration(dec, QColor(QColor::Invalid)); } } for(int a = 0; a < context->usesCount(); ++a) { Declaration* decl = context->topContext()->usedDeclarationForIndex(context->uses()[a].m_declarationIndex); QColor color(QColor::Invalid); if( colorsForDeclarations.contains(decl) ) color = ColorCache::self()->generatedColor(colorsForDeclarations[decl]); highlightUse(context, a, color); } if(context->type() == DUContext::Other || context->type() == DUContext::Function) { m_functionColorsForDeclarations[IndexedDUContext(context)] = colorsForDeclarations; m_functionDeclarationsForColors[IndexedDUContext(context)] = declarationsForColors; } QVector< DUContext* > children = context->childContexts(); lock.unlock(); // Periodically release the lock, so that the UI won't be blocked too much foreach (DUContext* child, children) highlightDUChain(child, colorsForDeclarations, declarationsForColors ); } KTextEditor::Attribute::Ptr CodeHighlighting::attributeForDepth(int depth) const { while (depth >= m_depthAttributes.count()) { KTextEditor::Attribute::Ptr a(new KTextEditor::Attribute()); a->setBackground(QColor(Qt::white).dark(100 + (m_depthAttributes.count() * 25))); a->setBackgroundFillWhitespace(true); if (depth % 2) a->setOutline(Qt::red); m_depthAttributes.append(a); } return m_depthAttributes[depth]; } KDevelop::Declaration* CodeHighlightingInstance::localClassFromCodeContext(KDevelop::DUContext* context) const { if(!context) return 0; if(m_contextClasses.contains(context)) return m_contextClasses[context]; DUContext* startContext = context; while( context->parentContext() && context->type() == DUContext::Other && context->parentContext()->type() == DUContext::Other ) { //Move context to the top context of type "Other". This is needed because every compound-statement creates a new sub-context. context = context->parentContext(); } ///Step 1: Find the function-declaration for the function we are in Declaration* functionDeclaration = 0; if( FunctionDefinition* def = dynamic_cast(context->owner()) ) { if(m_contextClasses.contains(context)) return m_contextClasses[context]; functionDeclaration = def->declaration(startContext->topContext()); } if( !functionDeclaration && context->owner() ) functionDeclaration = context->owner(); if(!functionDeclaration) { if(m_useClassCache) m_contextClasses[context] = 0; return 0; } Declaration* decl = functionDeclaration->context()->owner(); if(m_useClassCache) m_contextClasses[context] = decl; return decl; } CodeHighlightingInstance::Types CodeHighlightingInstance::typeForDeclaration(Declaration * dec, DUContext* context) const { /** * We highlight in 3 steps by priority: * 1. Is the item in the local class or an inherited class? If yes, highlight. * 2. What kind of item is it? If it's a type/function/enumerator, highlight by type. * 3. Else, highlight by scope. * * */ // if(ClassMemberDeclaration* classMember = dynamic_cast(dec)) // if(!Cpp::isAccessible(context, classMember)) // return ErrorVariableType; if(!dec) return ErrorVariableType; Types type = LocalVariableType; if(dec->kind() == Declaration::Namespace) return NamespaceType; if (context && dec->context() && dec->context()->type() == DUContext::Class) { //It is a use. //Determine the class we're in Declaration* klass = localClassFromCodeContext(context); if(klass) { if (klass->internalContext() == dec->context()) type = LocalClassMemberType; //Using Member of the local class else if (dec->context()->type() == DUContext::Class && klass->internalContext() && klass->internalContext()->imports(dec->context())) type = InheritedClassMemberType; //Using Member of an inherited class } } if (type == LocalVariableType) { if (dec->kind() == Declaration::Type || dec->type() || dec->type()) { if (dec->isForwardDeclaration()) type = ForwardDeclarationType; else if (dec->type()) type = FunctionType; else if(dec->type()) type = ClassType; else if(dec->type()) type = TypeAliasType; else if(dec->type()) type = EnumType; else if(dec->type()) type = EnumeratorType; } } if (type == LocalVariableType) { switch (dec->context()->type()) { case DUContext::Namespace: type = NamespaceVariableType; break; case DUContext::Class: type = MemberVariableType; break; case DUContext::Function: type = FunctionVariableType; break; default: break; } } return type; } bool CodeHighlightingInstance::useRainbowColor(Declaration* dec) const { return dec->context()->type() == DUContext::Function || (dec->context()->type() == DUContext::Other && dec->context()->owner()); } void CodeHighlightingInstance::highlightDeclaration(Declaration * declaration, const QColor &color) { HighlightedRange h; h.range = declaration->range(); h.attribute = m_highlighting->attributeForType(typeForDeclaration(declaration, 0), DeclarationContext, color); m_highlight.push_back(h); } void CodeHighlightingInstance::highlightUse(DUContext* context, int index, const QColor &color) { Types type = ErrorVariableType; Declaration* decl = context->topContext()->usedDeclarationForIndex(context->uses()[index].m_declarationIndex); type = typeForDeclaration(decl, context); if(type != ErrorVariableType || ICore::self()->languageController()->completionSettings()->highlightSemanticProblems()) { HighlightedRange h; h.range = context->uses()[index].m_range; h.attribute = m_highlighting->attributeForType(type, ReferenceContext, color); m_highlight.push_back(h); } } void CodeHighlightingInstance::highlightUses(DUContext* context) { for(int a = 0; a < context->usesCount(); ++a) highlightUse(context, a, QColor(QColor::Invalid)); } void CodeHighlighting::clearHighlightingForDocument(IndexedString document) { VERIFY_FOREGROUND_LOCKED QMutexLocker lock(&m_dataMutex); DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(document); if(m_highlights.contains(tracker)) { disconnect(tracker, SIGNAL(destroyed(QObject*)), this, SLOT(trackerDestroyed(QObject*))); qDeleteAll(m_highlights[tracker]->m_highlightedRanges); delete m_highlights[tracker]; m_highlights.remove(tracker); } } void CodeHighlighting::applyHighlighting(void* _highlighting) { CodeHighlighting::DocumentHighlighting* highlighting = static_cast(_highlighting); VERIFY_FOREGROUND_LOCKED QMutexLocker lock(&m_dataMutex); DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(highlighting->m_document); if(!tracker) { - kDebug() << "no document found for the planned highlighting of" << highlighting->m_document.str(); + kDebug() << "no document found for the planned highlighting of" << highlighting->m_document; delete highlighting; return; } QVector< MovingRange* > oldHighlightedRanges; if(m_highlights.contains(tracker)) { oldHighlightedRanges = m_highlights[tracker]->m_highlightedRanges; delete m_highlights[tracker]; }else{ // we newly add this tracker, so add the connection connect(tracker, SIGNAL(destroyed(QObject*)), SLOT(trackerDestroyed(QObject*))); connect(tracker->document(), SIGNAL(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*)), this, SLOT(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*))); connect(tracker->document(), SIGNAL(aboutToRemoveText(KTextEditor::Range)), this, SLOT(aboutToRemoveText(KTextEditor::Range))); } m_highlights[tracker] = highlighting; // Now create MovingRanges (match old ones with the incoming ranges) KTextEditor::Range tempRange; QVector::iterator movingIt = oldHighlightedRanges.begin(); QVector::iterator rangeIt = highlighting->m_waiting.begin(); while(rangeIt != highlighting->m_waiting.end()) { // Translate the range into the current revision SimpleRange transformedRange = tracker->transformToCurrentRevision(rangeIt->range, highlighting->m_waitingRevision); while(movingIt != oldHighlightedRanges.end() && ((*movingIt)->start().line() < transformedRange.start.line || ((*movingIt)->start().line() == transformedRange.start.line && (*movingIt)->start().column() < transformedRange.start.column))) { delete *movingIt; // Skip ranges that are in front of the current matched range ++movingIt; } tempRange.start().setPosition(transformedRange.start.line, transformedRange.start.column); tempRange.end().setPosition(transformedRange.end.line, transformedRange.end.column); if(movingIt == oldHighlightedRanges.end() || transformedRange.start.line != (*movingIt)->start().line() || transformedRange.start.column != (*movingIt)->start().column() || transformedRange.end.line != (*movingIt)->end().line() || transformedRange.end.column != (*movingIt)->end().column()) { Q_ASSERT(rangeIt->attribute); // The moving range is behind or unequal, create a new range highlighting->m_highlightedRanges.push_back(tracker->documentMovingInterface()->newMovingRange(tempRange)); highlighting->m_highlightedRanges.back()->setAttribute(rangeIt->attribute); highlighting->m_highlightedRanges.back()->setZDepth(highlightingZDepth); } else { // Update the existing moving range (*movingIt)->setAttribute(rangeIt->attribute); (*movingIt)->setRange(tempRange); highlighting->m_highlightedRanges.push_back(*movingIt); ++movingIt; } ++rangeIt; } for(; movingIt != oldHighlightedRanges.end(); ++movingIt) delete *movingIt; // Delete unmatched moving ranges behind } void CodeHighlighting::trackerDestroyed(QObject* object) { // Called when a document is destroyed VERIFY_FOREGROUND_LOCKED QMutexLocker lock(&m_dataMutex); DocumentChangeTracker* tracker = static_cast(object); Q_ASSERT(m_highlights.contains(tracker)); delete m_highlights[tracker]; // No need to care about the individual ranges, as the document is being destroyed m_highlights.remove(tracker); } void CodeHighlighting::aboutToInvalidateMovingInterfaceContent(Document* doc) { clearHighlightingForDocument(IndexedString(doc->url())); } void CodeHighlighting::aboutToRemoveText( const KTextEditor::Range& range ) { if (range.onSingleLine()) // don't try to optimize this return; VERIFY_FOREGROUND_LOCKED QMutexLocker lock(&m_dataMutex); Q_ASSERT(dynamic_cast(sender())); KTextEditor::Document* doc = static_cast(sender()); DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser() ->trackerForUrl(IndexedString(doc->url())); if(m_highlights.contains(tracker)) { QVector& ranges = m_highlights.value(tracker)->m_highlightedRanges; QVector::iterator it = ranges.begin(); while(it != ranges.end()) { if (range.contains((*it)->toRange())) { delete (*it); it = ranges.erase(it); } else { ++it; } } } } } #include "codehighlighting.moc" // kate: space-indent on; indent-width 2; replace-trailing-space-save on; show-tabs on; tab-indents on; tab-width 2; diff --git a/language/interfaces/codecontext.cpp b/language/interfaces/codecontext.cpp index 1b1b03a91..959737e0a 100644 --- a/language/interfaces/codecontext.cpp +++ b/language/interfaces/codecontext.cpp @@ -1,136 +1,136 @@ /* This file is part of KDevelop Copyright 2001-2002 Matthias Hoelzer-Kluepfel Copyright 2001-2002 Bernd Gehrmann Copyright 2001 Sandy Meier Copyright 2002 Daniel Engelschalt Copyright 2002 Simon Hausmann Copyright 2002-2003 Roberto Raggi Copyright 2003 Mario Scalas Copyright 2003 Harald Fernengel Copyright 2003,2006,2008 Hamish Rodda Copyright 2004 Alexander Dymo Copyright 2006 Adam Treat This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "codecontext.h" #include #include #include #include #include #include #include namespace KDevelop { class DUContextContext::Private { public: Private( const IndexedDUContext& item ) : m_item( item ) {} IndexedDUContext m_item; }; DUContextContext::DUContextContext( const IndexedDUContext& item ) : Context(), d( new Private( item ) ) {} DUContextContext::~DUContextContext() { delete d; } int DUContextContext::type() const { return Context::CodeContext; } IndexedDUContext DUContextContext::context() const { return d->m_item; } void DUContextContext::setContext(IndexedDUContext context) { d->m_item = context; } class DeclarationContext::Private { public: Private( const IndexedDeclaration& declaration, const DocumentRange& use ) : m_declaration( declaration ), m_use(use) {} IndexedDeclaration m_declaration; DocumentRange m_use; }; DeclarationContext::DeclarationContext( const IndexedDeclaration& declaration, const DocumentRange& use, const IndexedDUContext& context ) : DUContextContext(context), d( new Private( declaration, use ) ) {} DeclarationContext::DeclarationContext(KTextEditor::View* view, KTextEditor::Cursor position) : DUContextContext(IndexedDUContext()) { DUChainReadLocker lock(DUChain::lock()); DocumentRange useRange = DocumentRange::invalid(); IndexedDeclaration declaration; IndexedDUContext context; TopDUContext* topContext = DUChainUtils::standardContextForUrl(view->document()->url()); if(topContext) { CursorInRevision localRevisionCursor = topContext->transformToLocalRevision(SimpleCursor(position)); DUContext* specific = topContext->findContextAt(localRevisionCursor); context = IndexedDUContext(specific); if(specific) { int use = specific->findUseAt(localRevisionCursor); if(use != -1) { //Found a use under the cursor: - useRange = DocumentRange(IndexedString(specific->url().str()), topContext->transformFromLocalRevision(specific->uses()[use].m_range)); + useRange = DocumentRange(specific->url(), topContext->transformFromLocalRevision(specific->uses()[use].m_range)); declaration = IndexedDeclaration(specific->topContext()->usedDeclarationForIndex( specific->uses()[use].m_declarationIndex )); }else{ declaration = IndexedDeclaration(specific->findDeclarationAt(localRevisionCursor)); } } } d = new Private(declaration, useRange); setContext(context); } DeclarationContext::~DeclarationContext() { delete d; } int DeclarationContext::type() const { return Context::CodeContext; } IndexedDeclaration DeclarationContext::declaration() const { return d->m_declaration; } DocumentRange DeclarationContext::use() const { return d->m_use; } } diff --git a/language/interfaces/iproblem.cpp b/language/interfaces/iproblem.cpp index 325472062..a79d034fc 100644 --- a/language/interfaces/iproblem.cpp +++ b/language/interfaces/iproblem.cpp @@ -1,174 +1,174 @@ /* This file is part of KDevelop Copyright 2007 Hamish Rodda This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "iproblem.h" #include #include #include namespace KDevelop { REGISTER_DUCHAIN_ITEM(Problem); } using namespace KDevelop; Problem::Problem() : DUChainBase(*new ProblemData) { d_func_dynamic()->setClassId(this); } KDevelop::Problem::Problem(KDevelop::ProblemData& data) : DUChainBase(data) { } KDevelop::Problem::~Problem() { } KDevelop::IndexedString KDevelop::Problem::url() const { return d_func()->url; } DocumentRange Problem::finalLocation() const { return DocumentRange(d_func()->url, rangeInCurrentRevision()); } void Problem::setFinalLocation(const DocumentRange & location) { setRange(transformToLocalRevision(location)); d_func_dynamic()->url = location.document; } QStack< DocumentCursor > Problem::locationStack() const { return QStack< DocumentCursor >(); // return d_func()->locationStack; } void Problem::addLocation(const DocumentCursor & cursor) { Q_UNUSED(cursor); // d_func()->locationStack.push(DocumentCursor(cursor)); } void Problem::clearLocationStack() { // d_func()->locationStack.clear(); } void Problem::setLocationStack(const QStack< DocumentCursor > & locationStack) { Q_UNUSED(locationStack); // d_func()->locationStack = locationStack; } QString Problem::description() const { - return d_func()->description.str(); + return d_func()->description.toString(); } void Problem::setDescription(const QString & description) { d_func_dynamic()->description = IndexedString(description); } QString Problem::explanation() const { - return d_func()->explanation.str(); + return d_func()->explanation.toString(); } void Problem::setExplanation(const QString & explanation) { d_func_dynamic()->explanation = IndexedString(explanation); } ProblemData::Source Problem::source() const { return d_func()->source; } void Problem::setSource(ProblemData::Source source) { d_func_dynamic()->source = source; } KSharedPtr< KDevelop::IAssistant > KDevelop::Problem::solutionAssistant() const { return m_solution; } void KDevelop::Problem::setSolutionAssistant(KSharedPtr< KDevelop::IAssistant > assistant) { m_solution = assistant; } KDevelop::ProblemData::Severity KDevelop::Problem::severity() const { return d_func()->severity; } void KDevelop::Problem::setSeverity(ProblemData::Severity severity) { d_func_dynamic()->severity = severity; } QString Problem::sourceString() const { switch (source()) { case ProblemData::Disk: return i18n("Disk"); case ProblemData::Preprocessor: return i18n("Preprocessor"); case ProblemData::Lexer: return i18n("Lexer"); case ProblemData::Parser: return i18n("Parser"); case ProblemData::DUChainBuilder: return i18n("Definition-Use Chain"); case ProblemData::SemanticAnalysis: return i18n("Semantic Analysis"); case ProblemData::ToDo: return i18n("TODO"); case ProblemData::Unknown: default: return i18n("Unknown"); } } QString Problem::toString() const { return QString("%1:%2 in %3:[(%4,%5),(%6,%7)] %8") .arg(description()) .arg(sourceString()) - .arg(url().str()) + .arg(url().toString()) .arg(range().start.line) .arg(range().start.column) .arg(range().end.line) .arg(range().end.column) .arg(explanation()); } QDebug operator<<(QDebug s, const Problem& problem) { s.nospace() << problem.toString(); return s.space(); } QDebug operator<<(QDebug s, const ProblemPointer& problem) { s.nospace() << problem->toString(); return s.space(); } diff --git a/plugins/classbrowser/classbrowserplugin.cpp b/plugins/classbrowser/classbrowserplugin.cpp index 1212b633a..22f8fa3a2 100644 --- a/plugins/classbrowser/classbrowserplugin.cpp +++ b/plugins/classbrowser/classbrowserplugin.cpp @@ -1,188 +1,188 @@ /* * This file is part of KDevelop * * Copyright 2006 Adam Treat * Copyright 2006-2008 Hamish Rodda * Copyright 2009 Lior Mualem * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "classbrowserplugin.h" #include #include #include #include #include #include "interfaces/icore.h" #include "interfaces/iuicontroller.h" #include "interfaces/idocumentcontroller.h" #include "interfaces/contextmenuextension.h" #include "language/interfaces/codecontext.h" #include "language/duchain/duchainbase.h" #include "language/duchain/duchain.h" #include "language/duchain/duchainlock.h" #include "language/duchain/declaration.h" #include "language/duchain/indexedstring.h" #include "classmodel.h" #include "classtree.h" #include "classwidget.h" #include #include #include #include #include #include K_PLUGIN_FACTORY(KDevClassBrowserFactory, registerPlugin(); ) K_EXPORT_PLUGIN(KDevClassBrowserFactory(KAboutData("kdevclassbrowser","kdevclassbrowser",ki18n("Class Browser"), "0.1", ki18n("Browser for all known classes"), KAboutData::License_GPL))) using namespace KDevelop; class ClassBrowserFactory: public KDevelop::IToolViewFactory { public: ClassBrowserFactory(ClassBrowserPlugin *plugin): m_plugin(plugin) {} virtual QWidget* create(QWidget *parent = 0) { return new ClassWidget(parent, m_plugin); } virtual Qt::DockWidgetArea defaultPosition() { return Qt::LeftDockWidgetArea; } virtual QString id() const { return "org.kdevelop.ClassBrowserView"; } private: ClassBrowserPlugin *m_plugin; }; ClassBrowserPlugin::ClassBrowserPlugin(QObject *parent, const QVariantList&) : KDevelop::IPlugin(KDevClassBrowserFactory::componentData(), parent) , m_factory(new ClassBrowserFactory(this)) , m_activeClassTree(0) { core()->uiController()->addToolView(i18n("Classes"), m_factory); setXMLFile( "kdevclassbrowser.rc" ); m_findInBrowser = new QAction(i18n("Find in &Class Browser"), this); connect(m_findInBrowser, SIGNAL(triggered(bool)), this, SLOT(findInClassBrowser())); } ClassBrowserPlugin::~ClassBrowserPlugin() { } void ClassBrowserPlugin::unload() { core()->uiController()->removeToolView(m_factory); } KDevelop::ContextMenuExtension ClassBrowserPlugin::contextMenuExtension( KDevelop::Context* context) { KDevelop::ContextMenuExtension menuExt = KDevelop::IPlugin::contextMenuExtension( context ); // No context menu if we don't have a class browser at hand. if ( m_activeClassTree == 0 ) return menuExt; KDevelop::DeclarationContext *codeContext = dynamic_cast(context); if (!codeContext) return menuExt; DUChainReadLocker readLock(DUChain::lock()); Declaration* decl(codeContext->declaration().data()); if (decl) { if(decl->inSymbolTable()) { if(!ClassTree::populatingClassBrowserContextMenu() && ICore::self()->projectController()->findProjectForUrl(decl->url().toUrl()) && decl->kind() == Declaration::Type && decl->internalContext() && decl->internalContext()->type() == DUContext::Class) { //Currently "Find in Class Browser" seems to only work for classes, so only show it in that case m_findInBrowser->setData(QVariant::fromValue(DUChainBasePointer(decl))); menuExt.addAction( KDevelop::ContextMenuExtension::ExtensionGroup, m_findInBrowser); } } } return menuExt; } void ClassBrowserPlugin::findInClassBrowser() { ICore::self()->uiController()->findToolView(i18n("Classes"), m_factory, KDevelop::IUiController::CreateAndRaise); Q_ASSERT(qobject_cast(sender())); if ( m_activeClassTree == 0 ) return; DUChainReadLocker readLock(DUChain::lock()); QAction* a = static_cast(sender()); Q_ASSERT(a->data().canConvert()); DeclarationPointer decl = qvariant_cast(a->data()).dynamicCast(); if (decl) m_activeClassTree->highlightIdentifier(decl->qualifiedIdentifier()); } void ClassBrowserPlugin::showDefinition(DeclarationPointer declaration) { DUChainReadLocker readLock(DUChain::lock()); if ( !declaration ) return; Declaration* decl = declaration.data(); // If it's a function, find the function definition to go to the actual declaration. if ( decl && decl->isFunctionDeclaration() ) { FunctionDefinition* funcDefinition = dynamic_cast(decl); if ( funcDefinition == 0 ) funcDefinition = FunctionDefinition::definition(decl); if ( funcDefinition ) decl = funcDefinition; } if (decl) { - KUrl url(decl->url().str()); + const KUrl url = decl->url().toUrl(); KTextEditor::Range range = decl->rangeInCurrentRevision().textRange(); readLock.unlock(); ICore::self()->documentController()->openDocument(url, range.start()); } } #include "classbrowserplugin.moc" // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/plugins/contextbrowser/contextbrowser.cpp b/plugins/contextbrowser/contextbrowser.cpp index a2124f723..e75a940ee 100644 --- a/plugins/contextbrowser/contextbrowser.cpp +++ b/plugins/contextbrowser/contextbrowser.cpp @@ -1,1365 +1,1365 @@ /* * This file is part of KDevelop * * Copyright 2007 David Nolden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "contextbrowser.h" #include "contextbrowserview.h" #include "browsemanager.h" ///TODO: remove unneeded includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const unsigned int highlightingTimeout = 150; static const float highlightingZDepth = -5000; static const int maxHistoryLength = 30; using KDevelop::ILanguage; using KTextEditor::Attribute; using KTextEditor::View; bool toolTipEnabled = true; // Helper that follows the QObject::parent() chain, and returns the highest widget that has no parent. QWidget* masterWidget(QWidget* w) { while(w && w->parent() && qobject_cast(w->parent())) w = qobject_cast(w->parent()); return w; } // Helper that determines the context to use for highlighting at a specific position DUContext* contextForHighlightingAt(const SimpleCursor& position, TopDUContext* topContext) { DUContext* ctx = topContext->findContextAt(topContext->transformToLocalRevision(position)); while(ctx && ctx->parentContext() && (ctx->type() == DUContext::Template || ctx->type() == DUContext::Helper || ctx->localScopeIdentifier().isEmpty())) { ctx = ctx->parentContext(); } return ctx; } ///Duchain must be locked DUContext* getContextAt(KUrl url, KTextEditor::Cursor cursor) { TopDUContext* topContext = DUChainUtils::standardContextForUrl(url); if (!topContext) return 0; return contextForHighlightingAt(SimpleCursor(cursor), topContext); } class ContextBrowserViewFactory: public KDevelop::IToolViewFactory { public: ContextBrowserViewFactory(ContextBrowserPlugin *plugin): m_plugin(plugin) {} virtual QWidget* create(QWidget *parent = 0) { ContextBrowserView* ret = new ContextBrowserView(m_plugin, parent); return ret; } virtual Qt::DockWidgetArea defaultPosition() { return Qt::BottomDockWidgetArea; } virtual QString id() const { return "org.kdevelop.ContextBrowser"; } private: ContextBrowserPlugin *m_plugin; }; KXMLGUIClient* ContextBrowserPlugin::createGUIForMainWindow( Sublime::MainWindow* window ) { KXMLGUIClient* ret = KDevelop::IPlugin::createGUIForMainWindow( window ); m_browseManager = new BrowseManager(this); connect(ICore::self()->documentController(), SIGNAL(documentJumpPerformed(KDevelop::IDocument*,KTextEditor::Cursor,KDevelop::IDocument*,KTextEditor::Cursor)), this, SLOT(documentJumpPerformed(KDevelop::IDocument*,KTextEditor::Cursor,KDevelop::IDocument*,KTextEditor::Cursor))); m_previousButton = new QToolButton(); m_previousButton->setToolTip(i18n("Go back in context history")); m_previousButton->setPopupMode(QToolButton::MenuButtonPopup); m_previousButton->setIcon(KIcon("go-previous")); m_previousButton->setEnabled(false); m_previousButton->setFocusPolicy(Qt::NoFocus); m_previousMenu = new QMenu(); m_previousButton->setMenu(m_previousMenu); connect(m_previousButton, SIGNAL(clicked(bool)), this, SLOT(historyPrevious())); connect(m_previousMenu, SIGNAL(aboutToShow()), this, SLOT(previousMenuAboutToShow())); m_nextButton = new QToolButton(); m_nextButton->setToolTip(i18n("Go forward in context history")); m_nextButton->setPopupMode(QToolButton::MenuButtonPopup); m_nextButton->setIcon(KIcon("go-next")); m_nextButton->setEnabled(false); m_nextButton->setFocusPolicy(Qt::NoFocus); m_nextMenu = new QMenu(); m_nextButton->setMenu(m_nextMenu); connect(m_nextButton, SIGNAL(clicked(bool)), this, SLOT(historyNext())); connect(m_nextMenu, SIGNAL(aboutToShow()), this, SLOT(nextMenuAboutToShow())); m_browseButton = new QToolButton(); m_browseButton->setIcon(KIcon("games-hint")); m_browseButton->setToolTip(i18n("Enable/disable source browse mode")); m_browseButton->setWhatsThis(i18n("When this is enabled, you can browse the source-code by clicking in the editor.")); m_browseButton->setCheckable(true); m_browseButton->setFocusPolicy(Qt::NoFocus); connect(m_browseButton, SIGNAL(clicked(bool)), m_browseManager, SLOT(setBrowsing(bool))); IQuickOpen* quickOpen = KDevelop::ICore::self()->pluginController()->extensionForPlugin("org.kdevelop.IQuickOpen"); if(quickOpen) { m_outlineLine = quickOpen->createQuickOpenLine(QStringList(), QStringList() << i18n("Outline"), IQuickOpen::Outline); m_outlineLine->setDefaultText(i18n("Outline...")); m_outlineLine->setToolTip(i18n("Navigate outline of active document, click to browse.")); } connect(m_browseManager, SIGNAL(startDelayedBrowsing(KTextEditor::View*)), this, SLOT(startDelayedBrowsing(KTextEditor::View*))); connect(m_browseManager, SIGNAL(stopDelayedBrowsing()), this, SLOT(stopDelayedBrowsing())); m_toolbarWidget = toolbarWidgetForMainWindow(window); m_toolbarWidgetLayout = new QHBoxLayout; m_toolbarWidgetLayout->setSizeConstraint(QLayout::SetMaximumSize); m_previousButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_nextButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_browseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_toolbarWidgetLayout->setMargin(0); m_toolbarWidgetLayout->addWidget(m_previousButton); m_toolbarWidgetLayout->addWidget(m_outlineLine); m_outlineLine->setMaximumWidth(600); m_toolbarWidgetLayout->addWidget(m_nextButton); m_toolbarWidgetLayout->addWidget(m_browseButton); if(m_toolbarWidget->children().isEmpty()) m_toolbarWidget->setLayout(m_toolbarWidgetLayout); connect(ICore::self()->documentController(), SIGNAL(documentClosed(KDevelop::IDocument*)), m_outlineLine, SLOT(clear())); connect(ICore::self()->documentController(), SIGNAL(documentActivated(KDevelop::IDocument*)), this, SLOT(documentActivated(KDevelop::IDocument*))); return ret; } void ContextBrowserPlugin::createActionsForMainWindow(Sublime::MainWindow* window, QString& xmlFile, KActionCollection& actions) { xmlFile = "kdevcontextbrowser.rc" ; KAction* previousContext = actions.addAction("previous_context"); previousContext->setText( i18n("&Previous Visited Context") ); previousContext->setIcon( KIcon("go-previous-context" ) ); previousContext->setShortcut( Qt::META | Qt::Key_Left ); QObject::connect(previousContext, SIGNAL(triggered(bool)), this, SLOT(previousContextShortcut())); KAction* nextContext = actions.addAction("next_context"); nextContext->setText( i18n("&Next Visited Context") ); nextContext->setIcon( KIcon("go-next-context" ) ); nextContext->setShortcut( Qt::META | Qt::Key_Right ); QObject::connect(nextContext, SIGNAL(triggered(bool)), this, SLOT(nextContextShortcut())); KAction* previousUse = actions.addAction("previous_use"); previousUse->setText( i18n("&Previous Use") ); previousUse->setIcon( KIcon("go-previous-use") ); previousUse->setShortcut( Qt::META | Qt::SHIFT | Qt::Key_Left ); QObject::connect(previousUse, SIGNAL(triggered(bool)), this, SLOT(previousUseShortcut())); KAction* nextUse = actions.addAction("next_use"); nextUse->setText( i18n("&Next Use") ); nextUse->setIcon( KIcon("go-next-use") ); nextUse->setShortcut( Qt::META | Qt::SHIFT | Qt::Key_Right ); QObject::connect(nextUse, SIGNAL(triggered(bool)), this, SLOT(nextUseShortcut())); KAction* outline = actions.addAction("outline_line"); outline->setText(i18n("Context Browser")); QWidget* w = toolbarWidgetForMainWindow(window); w->setHidden(false); outline->setDefaultWidget(w); // Add to the actioncollection so one can set global shortcuts for the action actions.addAction("find_uses", m_findUses); } void ContextBrowserPlugin::nextContextShortcut() { // TODO: cleanup historyNext(); } void ContextBrowserPlugin::previousContextShortcut() { // TODO: cleanup historyPrevious(); } K_PLUGIN_FACTORY(ContextBrowserFactory, registerPlugin(); ) K_EXPORT_PLUGIN(ContextBrowserFactory(KAboutData("kdevcontextbrowser","kdevcontextbrowser",ki18n("Context Browser"), "0.1", ki18n("Shows information for the current context"), KAboutData::License_GPL))) ContextBrowserPlugin::ContextBrowserPlugin(QObject *parent, const QVariantList&) : KDevelop::IPlugin(ContextBrowserFactory::componentData(), parent) , m_viewFactory(new ContextBrowserViewFactory(this)) , m_nextHistoryIndex(0) { KDEV_USE_EXTENSION_INTERFACE( IContextBrowser ) core()->uiController()->addToolView(i18n("Code Browser"), m_viewFactory); connect( core()->documentController(), SIGNAL(textDocumentCreated(KDevelop::IDocument*)), this, SLOT(textDocumentCreated(KDevelop::IDocument*)) ); connect( core()->languageController()->backgroundParser(), SIGNAL(parseJobFinished(KDevelop::ParseJob*)), this, SLOT(parseJobFinished(KDevelop::ParseJob*))); connect( DUChain::self(), SIGNAL(declarationSelected(DeclarationPointer)), this, SLOT(declarationSelectedInUI(DeclarationPointer)) ); m_updateTimer = new QTimer(this); m_updateTimer->setSingleShot(true); connect( m_updateTimer, SIGNAL(timeout()), this, SLOT(updateViews()) ); //Needed global action for the context-menu extensions m_findUses = new KAction(i18n("Find Uses"), this); connect(m_findUses, SIGNAL(triggered(bool)), this, SLOT(findUses())); } ContextBrowserPlugin::~ContextBrowserPlugin() { ///TODO: QObject inheritance should suffice? delete m_nextMenu; delete m_previousMenu; delete m_toolbarWidgetLayout; delete m_previousButton; delete m_outlineLine; delete m_nextButton; delete m_browseButton; } void ContextBrowserPlugin::unload() { core()->uiController()->removeToolView(m_viewFactory); } KDevelop::ContextMenuExtension ContextBrowserPlugin::contextMenuExtension(KDevelop::Context* context) { KDevelop::ContextMenuExtension menuExt = KDevelop::IPlugin::contextMenuExtension( context ); KDevelop::DeclarationContext *codeContext = dynamic_cast(context); if (!codeContext) return menuExt; DUChainReadLocker lock(DUChain::lock()); if(!codeContext->declaration().data()) return menuExt; qRegisterMetaType("KDevelop::IndexedDeclaration"); m_findUses->setData(QVariant::fromValue(codeContext->declaration())); menuExt.addAction(KDevelop::ContextMenuExtension::ExtensionGroup, m_findUses); return menuExt; } void ContextBrowserPlugin::showUses(const DeclarationPointer& declaration) { QMetaObject::invokeMethod(this, "showUsesDelayed", Qt::QueuedConnection, Q_ARG(KDevelop::DeclarationPointer, declaration)); } void ContextBrowserPlugin::showUsesDelayed(const DeclarationPointer& declaration) { DUChainReadLocker lock; Declaration* decl = declaration.data(); if(!decl) { return; } QWidget* toolView = ICore::self()->uiController()->findToolView(i18n("Code Browser"), m_viewFactory, KDevelop::IUiController::CreateAndRaise); if(!toolView) { return; } ContextBrowserView* view = dynamic_cast(toolView); Q_ASSERT(view); view->allowLockedUpdate(); view->setDeclaration(decl, decl->topContext(), true); //We may get deleted while the call to acceptLink, so make sure we don't crash in that case QPointer widget = dynamic_cast(view->navigationWidget()); if(widget && widget->context()) { NavigationContextPointer nextContext = widget->context()->execute( NavigationAction(declaration, KDevelop::NavigationAction::ShowUses)); if(widget) { widget->setContext( nextContext ); } } } void ContextBrowserPlugin::findUses() { QAction* action = qobject_cast(sender()); Q_ASSERT(action); KDevelop::IndexedDeclaration decl = action->data().value(); showUses(DeclarationPointer(decl.declaration())); } void ContextBrowserPlugin::textHintRequested(const KTextEditor::Cursor& cursor, QString&) { m_mouseHoverCursor = SimpleCursor(cursor); View* view = dynamic_cast(sender()); if(!view) { kWarning() << "could not cast to view"; }else{ m_mouseHoverDocument = view->document()->url(); m_updateViews << view; } m_updateTimer->start(1); // triggers updateViews() if(toolTipEnabled) showToolTip(view, cursor); } void ContextBrowserPlugin::stopDelayedBrowsing() { if(m_currentToolTip) { m_currentToolTip->deleteLater(); m_currentToolTip = 0; m_currentNavigationWidget = 0; } } void ContextBrowserPlugin::startDelayedBrowsing(KTextEditor::View* view) { if(!m_currentToolTip) { showToolTip(view, view->cursorPosition()); } } void ContextBrowserPlugin::hideTooTip() { if(m_currentToolTip) { m_currentToolTip->deleteLater(); m_currentToolTip = 0; m_currentNavigationWidget = 0; } } // TODO: this is a hack, but Kate does not provide interface for this static int getLineHeight(KTextEditor::View* view, int curLine) { KTextEditor::Cursor c(curLine, 0); int currentHeight = view->cursorToCoordinate(c).y(); c.setLine(curLine + 1); if (view->cursorToCoordinate(c).y() < 0) { c.setLine(curLine - 1); } return std::abs(view->cursorToCoordinate(c).y() - currentHeight); } static QRect getItemBoundingRect(const KUrl& viewUrl, KTextEditor::View* view, KTextEditor::Cursor itemPosition) { DUChainReadLocker lock; KTextEditor::Range itemRange = DUChainUtils::itemRangeUnderCursor(viewUrl, SimpleCursor(itemPosition)); QPoint startPoint = view->mapToGlobal(view->cursorToCoordinate(itemRange.start())); QPoint endPoint = view->mapToGlobal(view->cursorToCoordinate(itemRange.end())); endPoint.ry() += getLineHeight(view, itemPosition.line()); return QRect(startPoint, endPoint); } void ContextBrowserPlugin::showToolTip(KTextEditor::View* view, KTextEditor::Cursor position) { ContextBrowserView* contextView = browserViewForWidget(view); if(contextView && contextView->isVisible() && !contextView->isLocked()) return; // If the context-browser view is visible, it will care about updating by itself KUrl viewUrl(view->document()->url()); QList languages = ICore::self()->languageController()->languagesForUrl(viewUrl); QWidget* navigationWidget = 0; { DUChainReadLocker lock(DUChain::lock()); foreach( ILanguage* language, languages) { navigationWidget = language->languageSupport()->specialLanguageObjectNavigationWidget(viewUrl, SimpleCursor(position)); if(navigationWidget) break; } if(!navigationWidget) { Declaration* decl = DUChainUtils::declarationForDefinition( DUChainUtils::itemUnderCursor(viewUrl, SimpleCursor(position)) ); if (decl && decl->kind() == Declaration::Alias) { AliasDeclaration* alias = dynamic_cast(decl); Q_ASSERT(alias); DUChainReadLocker lock; decl = alias->aliasedDeclaration().declaration(); } if(decl) { if(m_currentToolTipDeclaration == IndexedDeclaration(decl) && m_currentToolTip) return; m_currentToolTipDeclaration = IndexedDeclaration(decl); navigationWidget = decl->context()->createNavigationWidget(decl, DUChainUtils::standardContextForUrl(viewUrl)); } } } if(navigationWidget) { // If we have an invisible context-view, assign the tooltip navigation-widget to it. // If the user makes the context-view visible, it will instantly contain the correct widget. if(contextView && !contextView->isLocked()) contextView->setNavigationWidget(navigationWidget); if(m_currentToolTip) { m_currentToolTip->deleteLater(); m_currentToolTip = 0; m_currentNavigationWidget = 0; } KDevelop::NavigationToolTip* tooltip = new KDevelop::NavigationToolTip(view, view->mapToGlobal(view->cursorToCoordinate(position)) + QPoint(20, 40), navigationWidget); tooltip->addExtendRect(getItemBoundingRect(viewUrl, view, position)); tooltip->resize( navigationWidget->sizeHint() + QSize(10, 10) ); kDebug() << "tooltip size" << tooltip->size(); m_currentToolTip = tooltip; m_currentNavigationWidget = navigationWidget; ActiveToolTip::showToolTip(tooltip); //First disconnect to prevent multiple connections disconnect(view, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), this, SLOT(hideTooTip())); disconnect(view, SIGNAL(focusOut(KTextEditor::View*)), this, SLOT(hideTooTip())); connect(view, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), this, SLOT(hideTooTip())); connect(view, SIGNAL(focusOut(KTextEditor::View*)), this, SLOT(hideTooTip())); }else{ kDebug() << "not showing tooltip, no navigation-widget"; } } void ContextBrowserPlugin::clearMouseHover() { m_mouseHoverCursor = SimpleCursor::invalid(); m_mouseHoverDocument.clear(); } Attribute::Ptr highlightedUseAttribute() { static Attribute::Ptr standardAttribute = Attribute::Ptr(); if( !standardAttribute ) { standardAttribute= Attribute::Ptr( new Attribute() ); standardAttribute->setBackgroundFillWhitespace(true); // mixing (255, 255, 0, 100) with white yields this: standardAttribute->setBackground(QColor(251, 250, 150)); // force a foreground color to overwrite default Kate highlighting, i.e. of Q_OBJECT or similar // foreground color could change, hence apply it everytime standardAttribute->setForeground(QColor(0, 0, 0, 255)); //Don't use alpha here, as kate uses the alpha only to blend with the document background color } return standardAttribute; } Attribute::Ptr highlightedSpecialObjectAttribute() { static Attribute::Ptr standardAttribute = Attribute::Ptr(); if( !standardAttribute ) { standardAttribute = Attribute::Ptr( new Attribute() ); standardAttribute->setBackgroundFillWhitespace(true); // mixing (90, 255, 0, 100) with white yields this: standardAttribute->setBackground(QColor(190, 255, 155)); // force a foreground color to overwrite default Kate highlighting, i.e. of Q_OBJECT or similar // foreground color could change, hence apply it everytime standardAttribute->setForeground(QColor(0, 0, 0, 255)); //Don't use alpha here, as kate uses the alpha only to blend with the document background color } return standardAttribute; } void ContextBrowserPlugin::addHighlight( View* view, KDevelop::Declaration* decl ) { if( !view || !decl ) { kDebug() << "invalid view/declaration"; return; } ViewHighlights& highlights(m_highlightedRanges[view]); KDevelop::DUChainReadLocker lock; // Highlight the declaration highlights.highlights << decl->createRangeMoving(); highlights.highlights.back()->setAttribute(highlightedUseAttribute()); highlights.highlights.back()->setZDepth(highlightingZDepth); // Highlight uses { QMap< IndexedString, QList< SimpleRange > > currentRevisionUses = decl->usesCurrentRevision(); for(QMap< IndexedString, QList< SimpleRange > >::iterator fileIt = currentRevisionUses.begin(); fileIt != currentRevisionUses.end(); ++fileIt) { for(QList< SimpleRange >::const_iterator useIt = (*fileIt).constBegin(); useIt != (*fileIt).constEnd(); ++useIt) { highlights.highlights << PersistentMovingRange::Ptr(new PersistentMovingRange(*useIt, fileIt.key())); highlights.highlights.back()->setAttribute(highlightedUseAttribute()); highlights.highlights.back()->setZDepth(highlightingZDepth); } } } if( FunctionDefinition* def = FunctionDefinition::definition(decl) ) { highlights.highlights << def->createRangeMoving(); highlights.highlights.back()->setAttribute(highlightedUseAttribute()); highlights.highlights.back()->setZDepth(highlightingZDepth); } } Declaration* ContextBrowserPlugin::findDeclaration(View* view, const SimpleCursor& position, bool mouseHighlight) { Q_UNUSED(mouseHighlight); Declaration* foundDeclaration = 0; if(m_useDeclaration.data()) { foundDeclaration = m_useDeclaration.data(); }else{ //If we haven't found a special language object, search for a use/declaration and eventually highlight it foundDeclaration = DUChainUtils::declarationForDefinition( DUChainUtils::itemUnderCursor(view->document()->url(), position) ); if (foundDeclaration && foundDeclaration->kind() == Declaration::Alias) { AliasDeclaration* alias = dynamic_cast(foundDeclaration); Q_ASSERT(alias); DUChainReadLocker lock; foundDeclaration = alias->aliasedDeclaration().declaration(); } } return foundDeclaration; } ContextBrowserView* ContextBrowserPlugin::browserViewForWidget(QWidget* widget) { foreach(ContextBrowserView* contextView, m_views) { if(masterWidget(contextView) == masterWidget(widget)) { return contextView; } } return 0; } void ContextBrowserPlugin::updateForView(View* view) { bool allowHighlight = true; if(view->selection()) { // If something is selected, we unhighlight everything, so that we don't conflict with the // kate plugin that highlights occurences of the selected string, and also to reduce the // overall amount of concurrent highlighting. allowHighlight = false; } if(m_highlightedRanges[view].keep) { m_highlightedRanges[view].keep = false; return; } // Clear all highlighting m_highlightedRanges.clear(); // Re-highlight ViewHighlights& highlights = m_highlightedRanges[view]; KUrl url = view->document()->url(); IDocument* activeDoc = core()->documentController()->activeDocument(); bool mouseHighlight = (url == m_mouseHoverDocument) && (m_mouseHoverCursor.isValid()); bool shouldUpdateBrowser = (mouseHighlight || (view->isActiveView() && activeDoc && activeDoc->textDocument() == view->document())); SimpleCursor highlightPosition; if (mouseHighlight) highlightPosition = m_mouseHoverCursor; else highlightPosition = SimpleCursor(view->cursorPosition()); ///Pick a language ILanguage* language = 0; if(ICore::self()->languageController()->languagesForUrl(url).isEmpty()) { kDebug() << "found no language for document" << url; return; }else{ language = ICore::self()->languageController()->languagesForUrl(url).front(); } ///Check whether there is a special language object to highlight (for example a macro) SimpleRange specialRange = language->languageSupport()->specialLanguageObjectRange(url, highlightPosition); ContextBrowserView* updateBrowserView = shouldUpdateBrowser ? browserViewForWidget(view) : 0; if(specialRange.isValid()) { // Highlight a special language object if(allowHighlight) { highlights.highlights << PersistentMovingRange::Ptr(new PersistentMovingRange(specialRange, IndexedString(url))); highlights.highlights.back()->setAttribute(highlightedSpecialObjectAttribute()); highlights.highlights.back()->setZDepth(highlightingZDepth); } if(updateBrowserView) updateBrowserView->setSpecialNavigationWidget(language->languageSupport()->specialLanguageObjectNavigationWidget(url, highlightPosition)); }else{ KDevelop::DUChainReadLocker lock( DUChain::lock(), 100 ); if(!lock.locked()) { kDebug() << "Failed to lock du-chain in time"; return; } TopDUContext* topContext = DUChainUtils::standardContextForUrl(view->document()->url()); if (!topContext) return; DUContext* ctx = contextForHighlightingAt(highlightPosition, topContext); if (!ctx) return; //Only update the history if this context is around the text cursor if(core()->documentController()->activeDocument() && highlightPosition == SimpleCursor(view->cursorPosition()) && view->document() == core()->documentController()->activeDocument()->textDocument()) { updateHistory(ctx, highlightPosition); } Declaration* foundDeclaration = findDeclaration(view, highlightPosition, mouseHighlight); if( foundDeclaration ) { m_lastHighlightedDeclaration = highlights.declaration = IndexedDeclaration(foundDeclaration); if(allowHighlight) addHighlight( view, foundDeclaration ); if(updateBrowserView) updateBrowserView->setDeclaration(foundDeclaration, topContext); }else{ if(updateBrowserView) updateBrowserView->setContext(ctx); } } } void ContextBrowserPlugin::updateViews() { foreach( View* view, m_updateViews ) { updateForView(view); } m_updateViews.clear(); m_useDeclaration = IndexedDeclaration(); } void ContextBrowserPlugin::declarationSelectedInUI(DeclarationPointer decl) { m_useDeclaration = IndexedDeclaration(decl.data()); if(core()->documentController()->activeDocument() && core()->documentController()->activeDocument()->textDocument() && core()->documentController()->activeDocument()->textDocument()->activeView()) m_updateViews << core()->documentController()->activeDocument()->textDocument()->activeView(); m_updateTimer->start(highlightingTimeout); // triggers updateViews() } void ContextBrowserPlugin::parseJobFinished(KDevelop::ParseJob* job) { for(QMap< View*, ViewHighlights >::iterator it = m_highlightedRanges.begin(); it != m_highlightedRanges.end(); ++it) { if(it.key()->document()->url() == job->document().toUrl()) { if(m_updateViews.isEmpty()) m_updateTimer->start(highlightingTimeout); if(!m_updateViews.contains(it.key())) { kDebug() << "adding view for update"; m_updateViews << it.key(); // Don't change the highlighted declaration after finished parse-jobs (*it).keep = true; } } } } void ContextBrowserPlugin::textDocumentCreated( KDevelop::IDocument* document ) { Q_ASSERT(document->textDocument()); connect( document->textDocument(), SIGNAL(viewCreated(KTextEditor::Document*,KTextEditor::View*)), this, SLOT(viewCreated(KTextEditor::Document*,KTextEditor::View*)) ); foreach( View* view, document->textDocument()->views() ) viewCreated( document->textDocument(), view ); } void ContextBrowserPlugin::documentActivated( IDocument* doc ) { m_outlineLine->clear(); if (doc->textDocument() && doc->textDocument()->activeView()) { cursorPositionChanged(doc->textDocument()->activeView(), doc->textDocument()->activeView()->cursorPosition()); } } void ContextBrowserPlugin::viewDestroyed( QObject* obj ) { m_highlightedRanges.remove(static_cast(obj)); m_updateViews.remove(static_cast(obj)); } void ContextBrowserPlugin::selectionChanged( View* view ) { clearMouseHover(); m_updateViews.insert(view); m_updateTimer->start(highlightingTimeout/2); // triggers updateViews() } void ContextBrowserPlugin::cursorPositionChanged( View* view, const KTextEditor::Cursor& newPosition ) { if(view->document() == m_lastInsertionDocument && newPosition == m_lastInsertionPos) { //Do not update the highlighting while typing m_lastInsertionDocument = 0; m_lastInsertionPos = KTextEditor::Cursor(); if(m_highlightedRanges.contains(view)) m_highlightedRanges[view].keep = true; }else{ if(m_highlightedRanges.contains(view)) m_highlightedRanges[view].keep = false; } clearMouseHover(); m_updateViews.insert(view); m_updateTimer->start(highlightingTimeout/2); // triggers updateViews() } void ContextBrowserPlugin::textInserted(KTextEditor::Document* doc, KTextEditor::Range range) { m_lastInsertionDocument = doc; m_lastInsertionPos = range.end(); } void ContextBrowserPlugin::viewCreated( KTextEditor::Document* , View* v ) { disconnect( v, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), this, SLOT(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)) ); ///Just to make sure that multiple connections don't happen connect( v, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), this, SLOT(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)) ); connect( v, SIGNAL(destroyed(QObject*)), this, SLOT(viewDestroyed(QObject*)) ); disconnect( v->document(), SIGNAL(textInserted(KTextEditor::Document*,KTextEditor::Range)), this, SLOT(textInserted(KTextEditor::Document*,KTextEditor::Range))); connect( v->document(), SIGNAL(textInserted(KTextEditor::Document*,KTextEditor::Range)), this, SLOT(textInserted(KTextEditor::Document*,KTextEditor::Range))); disconnect( v, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(selectionChanged(KTextEditor::View*))); KTextEditor::TextHintInterface *iface = dynamic_cast(v); if( !iface ) return; iface->enableTextHints(highlightingTimeout); connect(v, SIGNAL(needTextHint(KTextEditor::Cursor,QString&)), this, SLOT(textHintRequested(KTextEditor::Cursor,QString&))); } void ContextBrowserPlugin::registerToolView(ContextBrowserView* view) { m_views << view; } void ContextBrowserPlugin::previousUseShortcut() { switchUse(false); } void ContextBrowserPlugin::nextUseShortcut() { switchUse(true); } KTextEditor::Range cursorToRange(SimpleCursor cursor) { return KTextEditor::Range(cursor.textCursor(), cursor.textCursor()); } void ContextBrowserPlugin::switchUse(bool forward) { if(core()->documentController()->activeDocument() && core()->documentController()->activeDocument()->textDocument() && core()->documentController()->activeDocument()->textDocument()->activeView()) { KTextEditor::Document* doc = core()->documentController()->activeDocument()->textDocument(); KDevelop::DUChainReadLocker lock( DUChain::lock() ); KDevelop::TopDUContext* chosen = DUChainUtils::standardContextForUrl(doc->url()); if( chosen ) { SimpleCursor cCurrent(doc->activeView()->cursorPosition()); KDevelop::CursorInRevision c = chosen->transformToLocalRevision(cCurrent); Declaration* decl = 0; //If we have a locked declaration, use that for jumping foreach(ContextBrowserView* view, m_views) { decl = view->lockedDeclaration().data(); ///@todo Somehow match the correct context-browser view if there is multiple if(decl) break; } if(!decl) //Try finding a declaration under the cursor decl = DUChainUtils::itemUnderCursor(doc->url(), cCurrent); if (decl && decl->kind() == Declaration::Alias) { AliasDeclaration* alias = dynamic_cast(decl); Q_ASSERT(alias); DUChainReadLocker lock; decl = alias->aliasedDeclaration().declaration(); } if(decl) { Declaration* target = 0; if(forward) //Try jumping from definition to declaration target = DUChainUtils::declarationForDefinition(decl, chosen); else if(decl->url().toUrl() == doc->url() && decl->range().contains(c)) //Try jumping from declaration to definition target = FunctionDefinition::definition(decl); if(target && target != decl) { SimpleCursor jumpTo = target->rangeInCurrentRevision().start; KUrl document = target->url().toUrl(); lock.unlock(); core()->documentController()->openDocument( document, cursorToRange(jumpTo) ); return; }else{ //Always work with the declaration instead of the definition decl = DUChainUtils::declarationForDefinition(decl, chosen); } } if(!decl) { //Pick the last use we have highlighted decl = m_lastHighlightedDeclaration.data(); } if(decl) { KDevVarLengthArray usingFiles = DUChain::uses()->uses(decl->id()); if(DUChainUtils::contextHasUse(decl->topContext(), decl) && usingFiles.indexOf(decl->topContext()) == -1) usingFiles.insert(0, decl->topContext()); if(decl->range().contains(c) && decl->url() == chosen->url()) { //The cursor is directly on the declaration. Jump to the first or last use. if(!usingFiles.isEmpty()) { TopDUContext* top = (forward ? usingFiles[0] : usingFiles.back()).data(); if(top) { QList useRanges = allUses(top, decl, true); qSort(useRanges); if(!useRanges.isEmpty()) { KUrl url = top->url().toUrl(); SimpleRange selectUse = chosen->transformFromLocalRevision(forward ? useRanges.first() : useRanges.back()); lock.unlock(); core()->documentController()->openDocument(url, cursorToRange(selectUse.start)); } } } return; } //Check whether we are within a use QList localUses = allUses(chosen, decl, true); qSort(localUses); for(int a = 0; a < localUses.size(); ++a) { int nextUse = (forward ? a+1 : a-1); bool pick = localUses[a].contains(c); if(!pick && forward && a+1 < localUses.size() && localUses[a].end <= c && localUses[a+1].start > c) { //Special case: We aren't on a use, but we are jumping forward, and are behind this and the next use pick = true; } if(!pick && !forward && a-1 >= 0 && c < localUses[a].start && c >= localUses[a-1].end) { //Special case: We aren't on a use, but we are jumping backward, and are in front of this use, but behind the previous one pick = true; } if(!pick && a == 0 && c < localUses[a].start) { if(!forward) { //Will automatically jump to previous file }else{ nextUse = 0; //We are before the first use, so jump to it. } pick = true; } if(!pick && a == localUses.size()-1 && c >= localUses[a].end) { if(forward) { //Will automatically jump to next file }else{ //We are behind the last use, but moving backward. So pick the last use. nextUse = a; } pick = true; } if(pick) { //Make sure we end up behind the use if(nextUse != a) while(forward && nextUse < localUses.size() && (localUses[nextUse].start <= localUses[a].end || localUses[nextUse].isEmpty())) ++nextUse; //Make sure we end up before the use if(nextUse != a) while(!forward && nextUse >= 0 && (localUses[nextUse].start >= localUses[a].start || localUses[nextUse].isEmpty())) --nextUse; //Jump to the next use kDebug() << "count of uses:" << localUses.size() << "nextUse" << nextUse; if(nextUse < 0 || nextUse == localUses.size()) { kDebug() << "jumping to next file"; //Jump to the first use in the next using top-context int indexInFiles = usingFiles.indexOf(chosen); if(indexInFiles != -1) { int nextFile = (forward ? indexInFiles+1 : indexInFiles-1); kDebug() << "current file" << indexInFiles << "nextFile" << nextFile; if(nextFile < 0 || nextFile >= usingFiles.size()) { //Open the declaration, or the definition if(nextFile >= usingFiles.size()) { Declaration* definition = FunctionDefinition::definition(decl); if(definition) decl = definition; } - KUrl u(decl->url().str()); + const KUrl url = decl->url().toUrl(); SimpleRange range = decl->rangeInCurrentRevision(); range.end = range.start; lock.unlock(); - core()->documentController()->openDocument(u, range.textRange()); + core()->documentController()->openDocument(url, range.textRange()); return; }else{ TopDUContext* nextTop = usingFiles[nextFile].data(); - KUrl u(nextTop->url().str()); + const KUrl url = nextTop->url().toUrl(); QList nextTopUses = allUses(nextTop, decl, true); qSort(nextTopUses); if(!nextTopUses.isEmpty()) { SimpleRange range = chosen->transformFromLocalRevision(forward ? nextTopUses.front() : nextTopUses.back()); range.end = range.start; lock.unlock(); - core()->documentController()->openDocument(u, range.textRange()); + core()->documentController()->openDocument(url, range.textRange()); } return; } }else{ kDebug() << "not found own file in use list"; } }else{ - KUrl url(chosen->url().str()); + const KUrl url = chosen->url().toUrl(); SimpleRange range = chosen->transformFromLocalRevision(localUses[nextUse]); range.end = range.start; lock.unlock(); core()->documentController()->openDocument(url, range.textRange()); return; } } } } } } } void ContextBrowserPlugin::unRegisterToolView(ContextBrowserView* view) { m_views.removeAll(view); } // history browsing QWidget* ContextBrowserPlugin::toolbarWidgetForMainWindow( Sublime::MainWindow* window ) { //TODO: support multiple windows (if that ever gets revived) if (!m_toolbarWidget) { m_toolbarWidget = new QWidget(window); } return m_toolbarWidget; } void ContextBrowserPlugin::documentJumpPerformed( KDevelop::IDocument* newDocument, const KTextEditor::Cursor& newCursor, KDevelop::IDocument* previousDocument, const KTextEditor::Cursor& previousCursor) { DUChainReadLocker lock(DUChain::lock()); /*TODO: support multiple windows if that ever gets revived if(newDocument && newDocument->textDocument() && newDocument->textDocument()->activeView() && masterWidget(newDocument->textDocument()->activeView()) != masterWidget(this)) return; */ if(previousDocument && previousCursor.isValid()) { kDebug() << "updating jump source"; DUContext* context = getContextAt(previousDocument->url(), previousCursor); if(context) { updateHistory(context, SimpleCursor(previousCursor), true); }else{ //We just want this place in the history m_history.resize(m_nextHistoryIndex); // discard forward history m_history.append(HistoryEntry(DocumentCursor(IndexedString(previousDocument->url()), SimpleCursor(previousCursor)))); ++m_nextHistoryIndex; } } kDebug() << "new doc: " << newDocument << " new cursor: " << newCursor; if(newDocument && newCursor.isValid()) { kDebug() << "updating jump target"; DUContext* context = getContextAt(newDocument->url(), newCursor); if(context) { updateHistory(context, SimpleCursor(newCursor), true); }else{ //We just want this place in the history m_history.resize(m_nextHistoryIndex); // discard forward history m_history.append(HistoryEntry(DocumentCursor(IndexedString(newDocument->url()), SimpleCursor(newCursor)))); ++m_nextHistoryIndex; m_outlineLine->clear(); } } } void ContextBrowserPlugin::updateButtonState() { m_nextButton->setEnabled( m_nextHistoryIndex < m_history.size() ); m_previousButton->setEnabled( m_nextHistoryIndex >= 2 ); } void ContextBrowserPlugin::historyNext() { if(m_nextHistoryIndex >= m_history.size()) { return; } openDocument(m_nextHistoryIndex); // opening the document at given position // will update the widget for us ++m_nextHistoryIndex; updateButtonState(); } void ContextBrowserPlugin::openDocument(int historyIndex) { Q_ASSERT_X(historyIndex >= 0, "openDocument", "negative history index"); Q_ASSERT_X(historyIndex < m_history.size(), "openDocument", "history index out of range"); DocumentCursor c = m_history[historyIndex].computePosition(); - if (c.isValid() && !c.document.str().isEmpty()) { + if (c.isValid() && !c.document.isEmpty()) { disconnect(ICore::self()->documentController(), SIGNAL(documentJumpPerformed(KDevelop::IDocument*,KTextEditor::Cursor,KDevelop::IDocument*,KTextEditor::Cursor)), this, SLOT(documentJumpPerformed(KDevelop::IDocument*,KTextEditor::Cursor,KDevelop::IDocument*,KTextEditor::Cursor))); ICore::self()->documentController()->openDocument(c.document.toUrl(), c.textCursor()); connect(ICore::self()->documentController(), SIGNAL(documentJumpPerformed(KDevelop::IDocument*,KTextEditor::Cursor,KDevelop::IDocument*,KTextEditor::Cursor)), this, SLOT(documentJumpPerformed(KDevelop::IDocument*,KTextEditor::Cursor,KDevelop::IDocument*,KTextEditor::Cursor))); KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); updateDeclarationListBox(m_history[historyIndex].context.data()); } } void ContextBrowserPlugin::historyPrevious() { if(m_nextHistoryIndex < 2) { return; } --m_nextHistoryIndex; openDocument(m_nextHistoryIndex-1); // opening the document at given position // will update the widget for us updateButtonState(); } QString ContextBrowserPlugin::actionTextFor(int historyIndex) const { const HistoryEntry& entry = m_history.at(historyIndex); QString actionText = entry.context.data() ? entry.context.data()->scopeIdentifier(true).toString() : QString(); if(actionText.isEmpty()) actionText = entry.alternativeString; if(actionText.isEmpty()) actionText = ""; actionText += " @ "; QString fileName = entry.absoluteCursorPosition.document.toUrl().fileName(); actionText += QString("%1:%2").arg(fileName).arg(entry.absoluteCursorPosition.line+1); return actionText; } /* inline QDebug operator<<(QDebug debug, const ContextBrowserPlugin::HistoryEntry &he) { DocumentCursor c = he.computePosition(); - debug << "\n\tHistoryEntry " << c.line << " " << c.document.str(); + debug << "\n\tHistoryEntry " << c.line << " " << c.document; return debug; } */ void ContextBrowserPlugin::nextMenuAboutToShow() { QList indices; for(int a = m_nextHistoryIndex; a < m_history.size(); ++a) { indices << a; } fillHistoryPopup(m_nextMenu, indices); } void ContextBrowserPlugin::previousMenuAboutToShow() { QList indices; for(int a = m_nextHistoryIndex-2; a >= 0; --a) { indices << a; } fillHistoryPopup(m_previousMenu, indices); } void ContextBrowserPlugin::fillHistoryPopup(QMenu* menu, const QList& historyIndices) { menu->clear(); KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); foreach(int index, historyIndices) { QAction* action = new QAction(actionTextFor(index), menu); action->setData(index); menu->addAction(action); connect(action, SIGNAL(triggered(bool)), this, SLOT(actionTriggered())); } } bool ContextBrowserPlugin::isPreviousEntry(KDevelop::DUContext* context, const KDevelop::SimpleCursor& /*position*/) const { if (m_nextHistoryIndex == 0) return false; Q_ASSERT(m_nextHistoryIndex <= m_history.count()); const HistoryEntry& he = m_history.at(m_nextHistoryIndex-1); KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); // is this necessary?? Q_ASSERT(context); return IndexedDUContext(context) == he.context; } void ContextBrowserPlugin::updateHistory(KDevelop::DUContext* context, const KDevelop::SimpleCursor& position, bool force) { kDebug() << "updating history"; if(m_outlineLine->isVisible()) updateDeclarationListBox(context); if(!context || (!context->owner() && !force)) { return; //Only add history-entries for contexts that have owners, which in practice should be functions and classes //This keeps the history cleaner } if (isPreviousEntry(context, position)) { if(m_nextHistoryIndex) { HistoryEntry& he = m_history[m_nextHistoryIndex-1]; he.setCursorPosition(position); } return; } else { // Append new history entry m_history.resize(m_nextHistoryIndex); // discard forward history m_history.append(HistoryEntry(IndexedDUContext(context), position)); ++m_nextHistoryIndex; updateButtonState(); if(m_history.size() > (maxHistoryLength + 5)) { m_history = m_history.mid(m_history.size() - maxHistoryLength); m_nextHistoryIndex = m_history.size(); } } } void ContextBrowserPlugin::setAllowBrowsing(bool allow) { m_browseButton->setChecked(allow); } void ContextBrowserPlugin::updateDeclarationListBox(DUContext* context) { if(!context || !context->owner()) { kDebug() << "not updating box"; m_listUrl = IndexedString(); ///@todo Compute the context in the document here m_outlineLine->clear(); return; } Declaration* decl = context->owner(); m_listUrl = context->url(); Declaration* specialDecl = SpecializationStore::self().applySpecialization(decl, decl->topContext()); FunctionType::Ptr function = specialDecl->type(); QString text = specialDecl->qualifiedIdentifier().toString(); if(function) text += function->partToString(KDevelop::FunctionType::SignatureArguments); if(!m_outlineLine->hasFocus()) { m_outlineLine->setText(text); m_outlineLine->setCursorPosition(0); } kDebug() << "updated" << text; } void ContextBrowserPlugin::actionTriggered() { QAction* action = qobject_cast(sender()); Q_ASSERT(action); Q_ASSERT(action->data().type() == QVariant::Int); int historyPosition = action->data().toInt(); // kDebug() << "history pos" << historyPosition << m_history.size() << m_history; if(historyPosition >= 0 && historyPosition < m_history.size()) { m_nextHistoryIndex = historyPosition + 1; openDocument(historyPosition); updateButtonState(); } } void ContextBrowserPlugin::doNavigate(NavigationActionType action) { KTextEditor::View* view = qobject_cast(sender()); if(!view) { kWarning() << "sender is not a view"; return; } KTextEditor::CodeCompletionInterface* iface = qobject_cast(view); if(!iface || iface->isCompletionActive()) return; // If code completion is active, the actions should be handled by the completion widget QWidget* widget = m_currentNavigationWidget.data(); if(!widget || !widget->isVisible()) { ContextBrowserView* contextView = browserViewForWidget(view); if(contextView) widget = contextView->navigationWidget(); } if(widget) { AbstractNavigationWidget* navWidget = qobject_cast(widget); if (navWidget) { switch(action) { case Accept: navWidget->accept(); break; case Back: navWidget->back(); break; case Left: navWidget->previous(); break; case Right: navWidget->next(); break; case Up: navWidget->up(); break; case Down: navWidget->down(); break; } } } } void ContextBrowserPlugin::navigateAccept() { doNavigate(Accept); } void ContextBrowserPlugin::navigateBack() { doNavigate(Back); } void ContextBrowserPlugin::navigateDown() { doNavigate(Down); } void ContextBrowserPlugin::navigateLeft() { doNavigate(Left); } void ContextBrowserPlugin::navigateRight() { doNavigate(Right); } void ContextBrowserPlugin::navigateUp() { doNavigate(Up); } //BEGIN HistoryEntry ContextBrowserPlugin::HistoryEntry::HistoryEntry(KDevelop::DocumentCursor pos) : absoluteCursorPosition(pos) { } ContextBrowserPlugin::HistoryEntry::HistoryEntry(IndexedDUContext ctx, const KDevelop::SimpleCursor& cursorPosition) : context(ctx) { //Use a position relative to the context setCursorPosition(cursorPosition); if(ctx.data()) alternativeString = ctx.data()->scopeIdentifier(true).toString(); if(!alternativeString.isEmpty()) alternativeString += i18n("(changed)"); //This is used when the context was deleted in between } DocumentCursor ContextBrowserPlugin::HistoryEntry::computePosition() const { KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); DocumentCursor ret; if(context.data()) { ret = DocumentCursor(context.data()->url(), relativeCursorPosition); ret.line += context.data()->range().start.line; }else{ ret = absoluteCursorPosition; } return ret; } void ContextBrowserPlugin::HistoryEntry::setCursorPosition(const KDevelop::SimpleCursor& cursorPosition) { KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); if(context.data()) { absoluteCursorPosition = DocumentCursor(context.data()->url(), cursorPosition); relativeCursorPosition = cursorPosition; relativeCursorPosition.line -= context.data()->range().start.line; } } // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/plugins/problemreporter/problemmodel.cpp b/plugins/problemreporter/problemmodel.cpp index dd5f63167..782d4e490 100644 --- a/plugins/problemreporter/problemmodel.cpp +++ b/plugins/problemreporter/problemmodel.cpp @@ -1,397 +1,397 @@ /* * KDevelop Problem Reporter * * Copyright 2007 Hamish Rodda * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "problemmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "problemreporterplugin.h" #include "watcheddocumentset.h" using namespace KDevelop; ProblemModel::ProblemModel(ProblemReporterPlugin * parent) : QAbstractItemModel(parent), m_plugin(parent), m_lock(QReadWriteLock::Recursive), m_showImports(false), m_severity(ProblemData::Hint), m_documentSet(0) { m_minTimer = new QTimer(this); m_minTimer->setInterval(MinTimeout); m_minTimer->setSingleShot(true); connect(m_minTimer, SIGNAL(timeout()), SLOT(timerExpired())); m_maxTimer = new QTimer(this); m_maxTimer->setInterval(MaxTimeout); m_maxTimer->setSingleShot(true); connect(m_maxTimer, SIGNAL(timeout()), SLOT(timerExpired())); setScope(CurrentDocument); connect(ICore::self()->documentController(), SIGNAL(documentActivated(KDevelop::IDocument*)), SLOT(setCurrentDocument(KDevelop::IDocument*))); // CompletionSettings include a list of todo markers we care for, so need to update connect(ICore::self()->languageController()->completionSettings(), SIGNAL(settingsChanged(ICompletionSettings*)), SLOT(forceFullUpdate())); if (ICore::self()->documentController()->activeDocument()) { setCurrentDocument(ICore::self()->documentController()->activeDocument()); } } const int ProblemModel::MinTimeout = 1000; const int ProblemModel::MaxTimeout = 5000; ProblemModel::~ ProblemModel() { m_problems.clear(); } int ProblemModel::rowCount(const QModelIndex & parent) const { if (!parent.isValid()) return m_problems.count(); if (parent.internalId() && parent.column() == 0) return m_problems.at(parent.row())->locationStack().count(); return 0; } -QString getDisplayUrl(const QString &url, const KUrl &base) { - KUrl location(url); +QString getDisplayUrl(const IndexedString &url, const KUrl &base) { + const KUrl location = url.toUrl(); QString displayedUrl; if ( location.protocol() == base.protocol() && location.user() == base.user() && location.host() == base.host() ) { bool isParent; displayedUrl = KUrl::relativePath(base.path(), location.path(), &isParent ); if ( !isParent ) { displayedUrl = location.pathOrUrl(); } } else { displayedUrl = location.pathOrUrl(); } return displayedUrl; } QVariant ProblemModel::data(const QModelIndex & index, int role) const { if (!index.isValid()) return QVariant(); // Locking the duchain here leads to a deadlock, because kate triggers some paint to the outside while holding the smart-lock // DUChainReadLocker lock(DUChain::lock()); ProblemPointer p = problemForIndex(index); KUrl baseDirectory = m_currentDocument.upUrl(); if (!index.internalId()) { // Top level switch (role) { case Qt::DisplayRole: switch (index.column()) { case Source: return p->sourceString(); break; case Error: return p->description(); case File: { - return getDisplayUrl(p->finalLocation().document.str(), baseDirectory); + return getDisplayUrl(p->finalLocation().document, baseDirectory); } case Line: if (p->finalLocation().isValid()) return QString::number(p->finalLocation().start.line + 1); break; case Column: if (p->finalLocation().isValid()) return QString::number(p->finalLocation().start.column + 1); break; } break; case Qt::ToolTipRole: return p->explanation(); default: break; } } else { switch (role) { case Qt::DisplayRole: switch (index.column()) { case Error: return i18n("In file included from:"); case File: { - return getDisplayUrl(p->locationStack().at(index.row()).document.str(), baseDirectory); + return getDisplayUrl(p->locationStack().at(index.row()).document, baseDirectory); } case Line: if (p->finalLocation().isValid()) return QString::number(p->finalLocation().start.line + 1); break; case Column: if (p->finalLocation().isValid()) return QString::number(p->finalLocation().start.column + 1); break; } break; default: break; } } return QVariant(); } QModelIndex ProblemModel::parent(const QModelIndex & index) const { if (index.internalId()) return createIndex(m_problems.indexOf(problemForIndex(index)), 0, 0); return QModelIndex(); } QModelIndex ProblemModel::index(int row, int column, const QModelIndex & parent) const { DUChainReadLocker lock(DUChain::lock()); if (row < 0 || column < 0 || column >= LastColumn) return QModelIndex(); if (parent.isValid()) { if (parent.internalId()) return QModelIndex(); if (parent.column() != 0) return QModelIndex(); ProblemPointer problem = problemForIndex(parent); if (row >= problem->locationStack().count()) return QModelIndex(); ///@todo Make location-stack work again return createIndex(row, column, row); } if (row < m_problems.count()) return createIndex(row, column, 0); return QModelIndex(); } int ProblemModel::columnCount(const QModelIndex & parent) const { Q_UNUSED(parent) return LastColumn; } KDevelop::ProblemPointer ProblemModel::problemForIndex(const QModelIndex & index) const { if (index.internalId()) return m_problems.at(index.internalId()); else return m_problems.at(index.row()); } ProblemReporterPlugin* ProblemModel::plugin() { return m_plugin; } QVariant ProblemModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(orientation); if (role != Qt::DisplayRole) return QVariant(); switch (section) { case Source: return i18nc("@title:column source of problem", "Source"); case Error: return i18nc("@title:column problem description", "Problem"); case File: return i18nc("@title:column file where problem was found", "File"); case Line: return i18nc("@title:column line number with problem", "Line"); case Column: return i18nc("@title:column column number with problem", "Column"); } return QVariant(); } void ProblemModel::problemsUpdated(const KDevelop::IndexedString& url) { QReadLocker locker(&m_lock); if (m_documentSet->get().contains(url)) { // m_minTimer will expire in MinTimeout unless some other parsing job finishes in this period. m_minTimer->start(); // m_maxTimer will expire unconditionally in MaxTimeout if (!m_maxTimer->isActive()) { m_maxTimer->start(); } } } void ProblemModel::timerExpired() { m_minTimer->stop(); m_maxTimer->stop(); rebuildProblemList(); } QList ProblemModel::getProblems(IndexedString url, bool showImports) { QList result; QSet visitedContexts; DUChainReadLocker lock; getProblemsInternal(DUChain::self()->chainForDocument(url), showImports, visitedContexts, result); return result; } QList< ProblemPointer > ProblemModel::getProblems(QSet< IndexedString > urls, bool showImports) { QList result; QSet visitedContexts; DUChainReadLocker lock; foreach(const IndexedString& url, urls) { getProblemsInternal(DUChain::self()->chainForDocument(url), showImports, visitedContexts, result); } return result; } void ProblemModel::getProblemsInternal(TopDUContext* context, bool showImports, QSet& visitedContexts, QList& result) { if (!context || visitedContexts.contains(context)) { return; } foreach(ProblemPointer p, context->problems()) { if (p->severity() <= m_severity) { result.append(p); } } visitedContexts.insert(context); if (showImports) { bool isProxy = context->parsingEnvironmentFile() && context->parsingEnvironmentFile()->isProxyContext(); foreach(const DUContext::Import &ctx, context->importedParentContexts()) { if(!ctx.indexedContext().indexedTopContext().isLoaded()) continue; TopDUContext* topCtx = dynamic_cast(ctx.context(0)); if(topCtx) { //If we are starting at a proxy-context, only recurse into other proxy-contexts, //because those contain the problems. if(!isProxy || (topCtx->parsingEnvironmentFile() && topCtx->parsingEnvironmentFile()->isProxyContext())) getProblemsInternal(topCtx, showImports, visitedContexts, result); } } } } void ProblemModel::rebuildProblemList() { // No locking here, because it may be called from an already locked context m_problems = getProblems(m_documentSet->get(), m_showImports); reset(); } void ProblemModel::setCurrentDocument(KDevelop::IDocument* document) { QWriteLocker locker(&m_lock); m_currentDocument = document->url(); m_documentSet->setCurrentDocument(IndexedString(m_currentDocument)); reset(); } void ProblemModel::setShowImports(bool showImports) { if (m_showImports != showImports) { QWriteLocker locker(&m_lock); m_showImports = showImports; rebuildProblemList(); } } void ProblemModel::setScope(int scope) { Scope cast_scope = static_cast(scope); QWriteLocker locker(&m_lock); if (!m_documentSet || m_documentSet->getScope() != cast_scope) { if (m_documentSet) { delete m_documentSet; } switch (cast_scope) { case CurrentDocument: m_documentSet = new CurrentDocumentSet(IndexedString(m_currentDocument), this); break; case OpenDocuments: m_documentSet = new OpenDocumentSet(this); break; case CurrentProject: m_documentSet = new CurrentProjectSet(IndexedString(m_currentDocument), this); break; case AllProjects: m_documentSet = new AllProjectSet(this); break; } connect(m_documentSet, SIGNAL(changed()), this, SLOT(documentSetChanged())); rebuildProblemList(); } } void ProblemModel::setSeverity(int severity) { ProblemData::Severity cast_severity = static_cast(severity); if (m_severity != cast_severity) { QWriteLocker locker(&m_lock); m_severity = cast_severity; rebuildProblemList(); } } void ProblemModel::documentSetChanged() { rebuildProblemList(); } void ProblemModel::forceFullUpdate() { m_lock.lockForRead(); QSet documents = m_documentSet->get(); m_lock.unlock(); DUChainReadLocker lock(DUChain::lock()); foreach(const IndexedString& document, documents) { if (document.isEmpty()) continue; TopDUContext::Features updateType = TopDUContext::ForceUpdate; if(documents.size() == 1) updateType = TopDUContext::ForceUpdateRecursive; DUChain::self()->updateContextForUrl(document, (TopDUContext::Features)(updateType | TopDUContext::VisibleDeclarationsAndContexts)); } } diff --git a/plugins/problemreporter/problemwidget.cpp b/plugins/problemreporter/problemwidget.cpp index 2d475554f..cff332a5a 100644 --- a/plugins/problemreporter/problemwidget.cpp +++ b/plugins/problemreporter/problemwidget.cpp @@ -1,260 +1,260 @@ /* * KDevelop Problem Reporter * * Copyright (c) 2006-2007 Hamish Rodda * Copyright 2006 Adam Treat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "problemwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "problemreporterplugin.h" #include "problemmodel.h" //#include "modeltest.h" using namespace KDevelop; ProblemWidget::ProblemWidget(QWidget* parent, ProblemReporterPlugin* plugin) : QTreeView(parent) , m_plugin(plugin) { setObjectName("Problem Reporter Tree"); setWindowTitle(i18n("Problems")); setWindowIcon( KIcon("dialog-information") ); ///@todo Use a proper icon setRootIsDecorated(true); setWhatsThis( i18n( "Problems" ) ); setModel(m_plugin->getModel()); header()->setStretchLastSection(false); KAction* fullUpdateAction = new KAction(this); fullUpdateAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); fullUpdateAction->setText(i18n("Force Full Update")); fullUpdateAction->setToolTip(i18nc("@info:tooltip", "Re-parse all watched documents")); fullUpdateAction->setIcon(KIcon("view-refresh")); connect(fullUpdateAction, SIGNAL(triggered(bool)), model(), SLOT(forceFullUpdate())); addAction(fullUpdateAction); KAction* showImportsAction = new KAction(this); addAction(showImportsAction); showImportsAction->setCheckable(true); showImportsAction->setChecked(false); showImportsAction->setText(i18n("Show Imports")); showImportsAction->setToolTip(i18nc("@info:tooltip", "Display problems in imported files")); this->model()->setShowImports(false); connect(showImportsAction, SIGNAL(triggered(bool)), this->model(), SLOT(setShowImports(bool))); KActionMenu* scopeMenu = new KActionMenu(this); scopeMenu->setDelayed(false); scopeMenu->setText(i18n("Scope")); scopeMenu->setToolTip(i18nc("@info:tooltip", "Which files to display the problems for")); QActionGroup* scopeActions = new QActionGroup(this); KAction* currentDocumentAction = new KAction(this); currentDocumentAction->setText(i18n("Current Document")); currentDocumentAction->setToolTip(i18nc("@info:tooltip", "Display problems in current document")); KAction* openDocumentsAction = new KAction(this); openDocumentsAction->setText(i18n("Open documents")); openDocumentsAction->setToolTip(i18nc("@info:tooltip", "Display problems in all open documents")); KAction* currentProjectAction = new KAction(this); currentProjectAction->setText(i18n("Current Project")); currentProjectAction->setToolTip(i18nc("@info:tooltip", "Display problems in current project")); KAction* allProjectAction = new KAction(this); allProjectAction->setText(i18n("All Projects")); allProjectAction->setToolTip(i18nc("@info:tooltip", "Display problems in all projects")); KAction* scopeActionArray[] = {currentDocumentAction, openDocumentsAction, currentProjectAction, allProjectAction}; for (int i = 0; i < 4; ++i) { scopeActionArray[i]->setCheckable(true); scopeActions->addAction(scopeActionArray[i]); scopeMenu->addAction(scopeActionArray[i]); } addAction(scopeMenu); currentDocumentAction->setChecked(true); model()->setScope(ProblemModel::CurrentDocument); QSignalMapper * scopeMapper = new QSignalMapper(this); scopeMapper->setMapping(currentDocumentAction, ProblemModel::CurrentDocument); scopeMapper->setMapping(openDocumentsAction, ProblemModel::OpenDocuments); scopeMapper->setMapping(currentProjectAction, ProblemModel::CurrentProject); scopeMapper->setMapping(allProjectAction, ProblemModel::AllProjects); connect(currentDocumentAction, SIGNAL(triggered()), scopeMapper, SLOT(map())); connect(openDocumentsAction, SIGNAL(triggered()), scopeMapper, SLOT(map())); connect(currentProjectAction, SIGNAL(triggered()), scopeMapper, SLOT(map())); connect(allProjectAction, SIGNAL(triggered()), scopeMapper, SLOT(map())); connect(scopeMapper, SIGNAL(mapped(int)), model(), SLOT(setScope(int))); KActionMenu* severityMenu = new KActionMenu(i18n("Severity"), this); severityMenu->setDelayed(false); severityMenu->setToolTip(i18nc("@info:tooltip", "Select the lowest level of problem severity to be displayed")); QActionGroup* severityActions = new QActionGroup(this); KAction* errorSeverityAction = new KAction(i18n("Error"), this); errorSeverityAction->setToolTip(i18nc("@info:tooltip", "Display only errors")); KAction* warningSeverityAction = new KAction(i18n("Warning"), this); warningSeverityAction->setToolTip(i18nc("@info:tooltip", "Display errors and warnings")); KAction* hintSeverityAction = new KAction(i18n("Hint"), this); hintSeverityAction->setToolTip(i18nc("@info:tooltip", "Display errors, warnings and hints")); KAction* severityActionArray[] = {errorSeverityAction, warningSeverityAction, hintSeverityAction}; for (int i = 0; i < 3; ++i) { severityActionArray[i]->setCheckable(true); severityActions->addAction(severityActionArray[i]); severityMenu->addAction(severityActionArray[i]); } addAction(severityMenu); hintSeverityAction->setChecked(true); model()->setSeverity(ProblemData::Hint); QSignalMapper * severityMapper = new QSignalMapper(this); severityMapper->setMapping(errorSeverityAction, ProblemData::Error); severityMapper->setMapping(warningSeverityAction, ProblemData::Warning); severityMapper->setMapping(hintSeverityAction, ProblemData::Hint); connect(errorSeverityAction, SIGNAL(triggered()), severityMapper, SLOT(map())); connect(warningSeverityAction, SIGNAL(triggered()), severityMapper, SLOT(map())); connect(hintSeverityAction, SIGNAL(triggered()), severityMapper, SLOT(map())); connect(severityMapper, SIGNAL(mapped(int)), model(), SLOT(setSeverity(int))); connect(this, SIGNAL(activated(QModelIndex)), SLOT(itemActivated(QModelIndex))); } ProblemWidget::~ProblemWidget() { } void ProblemWidget::itemActivated(const QModelIndex& index) { if (!index.isValid()) return; KTextEditor::Cursor start; KUrl url; { // TODO: is this really necessary? DUChainReadLocker lock(DUChain::lock()); KDevelop::ProblemPointer problem = model()->problemForIndex(index); if (!index.internalPointer()) { - url = KUrl(problem->finalLocation().document.str()); + url = problem->finalLocation().document.toUrl(); start = problem->finalLocation().start.textCursor(); }else{ - url = KUrl(problem->locationStack().at(index.row()).document.str()); + url = problem->locationStack().at(index.row()).document.toUrl(); start = problem->locationStack().at(index.row()).textCursor(); } } m_plugin->core()->documentController()->openDocument(url, start); } void ProblemWidget::resizeColumns() { // Do actual resizing only if the widget is visible and there are not too many items const int ResizeRowLimit = 15; if (isVisible() && model()->rowCount() > 0 && model()->rowCount() < ResizeRowLimit) { const int columnCount = model()->columnCount(); QVector widthArray(columnCount); int totalWidth = 0; for (int i = 0; i < columnCount; ++i) { widthArray[i] = columnWidth(i); totalWidth += widthArray[i]; } for (int i = 0; i < columnCount; ++i) { int columnWidthHint = qMax(sizeHintForColumn(i), header()->sectionSizeHint(i)); if (columnWidthHint - widthArray[i] > 0) { if (columnWidthHint - widthArray[i] < width() - totalWidth) { // enough space to resize setColumnWidth(i, columnWidthHint); totalWidth += (columnWidthHint - widthArray[i]); } else { setColumnWidth(i, widthArray[i] + width() - totalWidth); break; } } } } } void ProblemWidget::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { QTreeView::dataChanged(topLeft, bottomRight); resizeColumns(); } void ProblemWidget::reset() { QTreeView::reset(); resizeColumns(); } ProblemModel * ProblemWidget::model() const { return static_cast(QTreeView::model()); } void ProblemWidget::contextMenuEvent(QContextMenuEvent* event) { QModelIndex index = indexAt(event->pos()); if(index.isValid()) { KDevelop::ProblemPointer problem = model()->problemForIndex(index); if(problem) { KSharedPtr solution = problem->solutionAssistant(); QList actions; if(solution) { foreach(KDevelop::IAssistantAction::Ptr action, solution->actions()) { actions << action->toKAction(); if(!solution->title().isEmpty()) actions.back()->setText(solution->title() + ' ' + actions.back()->text()); } } if(!actions.isEmpty()) QMenu::exec(actions, event->globalPos()); } } } void ProblemWidget::showEvent(QShowEvent * event) { Q_UNUSED(event) for (int i = 0; i < model()->columnCount(); ++i) resizeColumnToContents(i); } #include "problemwidget.moc" diff --git a/plugins/problemreporter/watcheddocumentset.cpp b/plugins/problemreporter/watcheddocumentset.cpp index 193f56b66..60e4097d1 100644 --- a/plugins/problemreporter/watcheddocumentset.cpp +++ b/plugins/problemreporter/watcheddocumentset.cpp @@ -1,186 +1,186 @@ /* * KDevelop Problem Reporter * * Copyright 2010 Dmitry Risenberg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "watcheddocumentset.h" #include #include #include #include #include #include #include #include "problemreporterplugin.h" using namespace KDevelop; WatchedDocumentSet::WatchedDocumentSet(ProblemModel* parent) :QObject(parent) { } void WatchedDocumentSet::setCurrentDocument(const KDevelop::IndexedString&) { } WatchedDocumentSet::DocumentSet WatchedDocumentSet::get() const { return m_documents; } ProblemModel* WatchedDocumentSet::model() const { return static_cast(parent()); } CurrentDocumentSet::CurrentDocumentSet(const KDevelop::IndexedString& document, ProblemModel * parent) : WatchedDocumentSet(parent) { m_documents.insert(document); } void CurrentDocumentSet::setCurrentDocument(const KDevelop::IndexedString& url) { m_documents.clear(); m_documents.insert(url); emit changed(); } ProblemModel::Scope CurrentDocumentSet::getScope() const { return ProblemModel::CurrentDocument; } OpenDocumentSet::OpenDocumentSet(ProblemModel* parent) : WatchedDocumentSet(parent) { QList docs = model()->plugin()->core()->documentController()->openDocuments(); foreach (KDevelop::IDocument* doc, docs) { m_documents.insert(KDevelop::IndexedString(doc->url())); } connect(model()->plugin()->core()->documentController(), SIGNAL(documentClosed(KDevelop::IDocument*)), this, SLOT(documentClosed(KDevelop::IDocument*))); connect(model()->plugin()->core()->documentController(), SIGNAL(textDocumentCreated(KDevelop::IDocument*)), this, SLOT(documentCreated(KDevelop::IDocument*))); } void OpenDocumentSet::documentClosed(KDevelop::IDocument* doc) { if (m_documents.remove(KDevelop::IndexedString(doc->url()))) { emit changed(); } } void OpenDocumentSet::documentCreated(KDevelop::IDocument* doc) { m_documents.insert(KDevelop::IndexedString(doc->url())); emit changed(); } ProblemModel::Scope OpenDocumentSet::getScope() const { return ProblemModel::OpenDocuments; } ProjectSet::ProjectSet(ProblemModel* parent) : WatchedDocumentSet(parent) { } void ProjectSet::fileAdded(ProjectFileItem* file) { m_documents.insert(IndexedString(file->url())); emit changed(); } void ProjectSet::fileRemoved(ProjectFileItem* file) { if (m_documents.remove(IndexedString(file->url()))) { emit changed(); } } void ProjectSet::fileRenamed(const KUrl& oldFile, ProjectFileItem* newFile) { if (m_documents.remove(IndexedString(oldFile))) { m_documents.insert(IndexedString(newFile->url())); } } void ProjectSet::trackProjectFiles(const IProject* project) { if (project) { // The implementation should derive from QObject somehow QObject* fileManager = dynamic_cast(project->projectFileManager()); if (fileManager) { connect(fileManager, SIGNAL(fileAdded(ProjectFileItem*)), this, SLOT(fileAdded(ProjectFileItem*))); connect(fileManager, SIGNAL(fileRemoved(ProjectFileItem*)), this, SLOT(fileRemoved(ProjectFileItem*))); connect(fileManager, SIGNAL(fileRenamed(KUrl,ProjectFileItem*)), this, SLOT(fileRenamed(KUrl,ProjectFileItem*))); } } } CurrentProjectSet::CurrentProjectSet(const KDevelop::IndexedString& document, ProblemModel* parent) : ProjectSet(parent), m_currentProject(0) { setCurrentDocumentInternal(document); trackProjectFiles(m_currentProject); } void CurrentProjectSet::setCurrentDocument(const KDevelop::IndexedString& url) { setCurrentDocumentInternal(url); } void CurrentProjectSet::setCurrentDocumentInternal(const KDevelop::IndexedString& url) { - IProject* projectForUrl = model()->plugin()->core()->projectController()->findProjectForUrl(url.str()); + IProject* projectForUrl = model()->plugin()->core()->projectController()->findProjectForUrl(url.toUrl()); if (projectForUrl && projectForUrl != m_currentProject) { m_documents.clear(); m_currentProject = projectForUrl; QList files = m_currentProject->files(); foreach (ProjectFileItem* file, files) { - m_documents.insert(IndexedString(file->url())); + m_documents.insert(file->indexedUrl()); } emit changed(); } } ProblemModel::Scope CurrentProjectSet::getScope() const { return ProblemModel::CurrentProject; } AllProjectSet::AllProjectSet(ProblemModel* parent) : ProjectSet(parent) { foreach(const IProject* project, model()->plugin()->core()->projectController()->projects()) { foreach (ProjectFileItem* file, project->files()) { - m_documents.insert(IndexedString(file->url())); + m_documents.insert(file->indexedUrl()); } trackProjectFiles(project); } } ProblemModel::Scope AllProjectSet::getScope() const { return ProblemModel::AllProjects; } diff --git a/plugins/quickopen/duchainitemquickopen.cpp b/plugins/quickopen/duchainitemquickopen.cpp index 73b632de1..6071246a1 100644 --- a/plugins/quickopen/duchainitemquickopen.cpp +++ b/plugins/quickopen/duchainitemquickopen.cpp @@ -1,254 +1,254 @@ /* This file is part of the KDE libraries Copyright (C) 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "projectitemquickopen.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; DUChainItemData::DUChainItemData( const DUChainItem& file, bool openDefinition ) : m_item(file) , m_openDefinition(openDefinition) { } QString DUChainItemData::text() const { DUChainReadLocker lock;; Declaration* decl = m_item.m_item.data(); if(!decl) return i18n("Not available any more: %1", m_item.m_text); if(FunctionDefinition* def = dynamic_cast(decl)) { if(def->declaration()) { decl = def->declaration(); } } QString text = decl->qualifiedIdentifier().toString(); if(!decl->abstractType()) { //With simplified representation, still mark functions as such by adding parens if(dynamic_cast(decl)) { text += "(...)"; } }else if(TypePtr function = decl->type()) { text += function->partToString( FunctionType::SignatureArguments ); } return text; } QList DUChainItemData::highlighting() const { DUChainReadLocker lock;; Declaration* decl = m_item.m_item.data(); if(!decl) { return QList(); } if(FunctionDefinition* def = dynamic_cast(decl)) { if(def->declaration()) { decl = def->declaration(); } } QTextCharFormat boldFormat; boldFormat.setFontWeight(QFont::Bold); QTextCharFormat normalFormat; int prefixLength = 0; QString signature; TypePtr function = decl->type(); if(function) { signature = function->partToString( FunctionType::SignatureArguments ); } //Only highlight the last part of the qualified identifier, so the scope doesn't distract too much QualifiedIdentifier id = decl->qualifiedIdentifier(); QString fullId = id.toString(); QString lastId; if(!id.isEmpty()) { lastId = id.last().toString(); } prefixLength += fullId.length() - lastId.length(); QList ret; ret << 0; ret << prefixLength; ret << QVariant(normalFormat); ret << prefixLength; ret << lastId.length(); ret << QVariant(boldFormat); if(!signature.isEmpty()) { ret << prefixLength + lastId.length(); ret << signature.length(); ret << QVariant(normalFormat); } return ret; } QString DUChainItemData::htmlDescription() const { if(m_item.m_noHtmlDestription) { return QString(); } DUChainReadLocker lock;; Declaration* decl = m_item.m_item.data(); if(!decl) { return i18n("Not available any more"); } TypePtr function = decl->type(); QString text; if( function && function->returnType() ) { text = i18nc("%1: function signature", "Return: %1", function->partToString(FunctionType::SignatureReturn)); } - text += ' ' + i18nc("%1: file path", "File: %1", decl->url().str()); + text += ' ' + i18nc("%1: file path", "File: %1", decl->url().toString()); QString ret = "" + text + ""; if(!m_item.m_project.isEmpty()) { ret.prepend(i18n("Project %1", m_item.m_project) + (ret.isEmpty() ? ", " : "")); } return ret; } bool DUChainItemData::execute( QString& /*filterText*/ ) { DUChainReadLocker lock;; Declaration* decl = m_item.m_item.data(); if(!decl) { return false; } if(m_openDefinition && FunctionDefinition::definition(decl)) { decl = FunctionDefinition::definition(decl); } - KUrl url = KUrl(decl->url().str()); + const KUrl url = decl->url().toUrl(); KTextEditor::Cursor cursor = decl->rangeInCurrentRevision().textRange().start(); DUContext* internal = decl->internalContext(); if(internal && (internal->type() == DUContext::Other || internal->type() == DUContext::Class)) { //Move into the body if(internal->range().end.line > internal->range().start.line) { cursor = KTextEditor::Cursor(internal->range().start.line+1, 0); //Move into the body } } lock.unlock(); ICore::self()->documentController()->openDocument( url, cursor ); return true; } bool DUChainItemData::isExpandable() const { return true; } QWidget* DUChainItemData::expandingWidget() const { DUChainReadLocker lock;; Declaration* decl = dynamic_cast(m_item.m_item.data()); if( !decl || !decl->context() ) { return 0; } return decl->context()->createNavigationWidget( decl, decl->topContext(), m_item.m_project.isEmpty() ? QString() : ("" + i18n("Project %1", m_item.m_project) + "
") ); } QIcon DUChainItemData::icon() const { return QIcon(); } DUChainItemDataProvider::DUChainItemDataProvider( IQuickOpen* quickopen, bool openDefinitions ) : m_quickopen(quickopen) , m_openDefinitions(openDefinitions) { reset(); } void DUChainItemDataProvider::setFilterText( const QString& text ) { Base::setFilter( text ); } uint DUChainItemDataProvider::itemCount() const { return Base::filteredItems().count(); } uint DUChainItemDataProvider::unfilteredItemCount() const { return Base::items().count(); } QuickOpenDataPointer DUChainItemDataProvider::data( uint row ) const { return KDevelop::QuickOpenDataPointer( createData( Base::filteredItems()[row] ) ); } DUChainItemData* DUChainItemDataProvider::createData( const DUChainItem& item ) const { return new DUChainItemData( item, m_openDefinitions ); } QString DUChainItemDataProvider::itemText( const DUChainItem& data ) const { return data.m_text; } void DUChainItemDataProvider::reset() { } diff --git a/plugins/quickopen/projectitemquickopen.cpp b/plugins/quickopen/projectitemquickopen.cpp index 08de12491..73d55ae06 100644 --- a/plugins/quickopen/projectitemquickopen.cpp +++ b/plugins/quickopen/projectitemquickopen.cpp @@ -1,350 +1,350 @@ /* This file is part of the KDE libraries Copyright (C) 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "projectitemquickopen.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; struct SubstringCache { SubstringCache( const QString& string = QString() ) : substring(string) { } inline int containedIn( const Identifier& id ) const { int index = id.index(); QHash::const_iterator it = cache.constFind(index); if(it != cache.constEnd()) { return *it; } - const QString idStr = id.identifier().str(); + const QString idStr = id.identifier().toString(); int result = idStr.lastIndexOf(substring, -1, Qt::CaseInsensitive); //here we shift the values if the matched string is bigger than the substring, //so closer matches will appear first if (result >= 0) { result = result + (idStr.size() - substring.size()); } cache[index] = result; return result; } QString substring; mutable QHash cache; }; struct ClosestMatchToText { ClosestMatchToText( const QHash& _cache ) : cache(_cache) { } /** @brief Calculates the distance to two pre-filtered match items * * @param a The CodeModelView witch represents the first item to be tested * @param b The CodeModelView witch represents the second item to be tested * * @b */ inline bool operator()( const CodeModelViewItem& a, const CodeModelViewItem& b ) const { const int height_a = cache.value(a.m_id.index(), -1); const int height_b = cache.value(b.m_id.index(), -1); Q_ASSERT(height_a != -1); Q_ASSERT(height_b != -1); if (height_a == height_b) { // stable sorting for equal items based on index // TODO: fast string-based sorting in such cases? return a.m_id.index() < b.m_id.index(); } return height_a < height_b; } private: const QHash& cache; }; ProjectItemDataProvider::ProjectItemDataProvider( KDevelop::IQuickOpen* quickopen ) : m_quickopen(quickopen) { } void ProjectItemDataProvider::setFilterText( const QString& text ) { m_addedItems.clear(); QStringList search(text.split("::", QString::SkipEmptyParts)); for(int a = 0; a < search.count(); ++a) { if(search[a].endsWith(':')) { //Don't get confused while the :: is being typed search[a] = search[a].left(search[a].length()-1); } } if(!search.isEmpty() && search.back().endsWith('(')) { search.back() = search.back().left(search.back().length()-1); } if(text.isEmpty() || search.isEmpty()) { m_filteredItems = m_currentItems; return; } KDevVarLengthArray cache; foreach(const QString& searchPart, search) { cache.append(SubstringCache(searchPart)); } if(!text.startsWith(m_currentFilter)) { m_filteredItems = m_currentItems; } m_currentFilter = text; QVector oldFiltered = m_filteredItems; QHash heights; m_filteredItems.clear(); foreach(const CodeModelViewItem& item, oldFiltered) { const QualifiedIdentifier& currentId = item.m_id; int last_pos = currentId.count() - 1; int current_height = 0; int distance = 0; //iter over each search item from last to first //this makes easier to calculate the distance based on where we hit the result or nothing //Iterating from the last item to the first is more efficient, as we want to match the //class/function name, which is the last item on the search fields and on the identifier. for(int b = search.count() - 1; b >= 0; --b) { //iter over each id for the current identifier, from last to first for(; last_pos >= 0; --last_pos, distance++) { // the more distant we are from the class definition, the less priority it will have current_height += distance * 10000; int result; //if the current search item is contained on the current identifier if((result = cache[b].containedIn( currentId.at(last_pos) )) >= 0) { //when we find a hit, whe add the distance to the searched word. //so the closest item will be displayed first current_height += result; if(b == 0) { heights[currentId.index()] = current_height; m_filteredItems << item; } break; } } } } //then, for the last part, we use the already built cache to sort the items according with their distance qSort(m_filteredItems.begin(), m_filteredItems.end(), ClosestMatchToText(heights)); } QList ProjectItemDataProvider::data( uint start, uint end ) const { QList ret; for(uint a = start; a < end; ++a) { ret << data(a); } return ret; } KDevelop::QuickOpenDataPointer ProjectItemDataProvider::data( uint pos ) const { //Check whether this position falls into an appended item-list, else apply the offset uint filteredItemOffset = 0; for(AddedItems::const_iterator it = m_addedItems.constBegin(); it != m_addedItems.constEnd(); ++it) { int offsetInAppended = pos - (it.key()+1); if(offsetInAppended >= 0 && offsetInAppended < it.value().count()) { return it.value()[offsetInAppended]; } if(it.key() >= pos) { break; } filteredItemOffset += it.value().count(); } uint a = pos - filteredItemOffset; if(a > (uint)m_filteredItems.size()) { return KDevelop::QuickOpenDataPointer(); } QList ret; KDevelop::DUChainReadLocker lock( DUChain::lock() ); TopDUContext* ctx = DUChainUtils::standardContextForUrl(m_filteredItems[a].m_file.toUrl()); if(ctx) { QList decls = ctx->findDeclarations(m_filteredItems[a].m_id, CursorInRevision::invalid(), AbstractType::Ptr(), 0, DUContext::DirectQualifiedLookup); //Filter out forward-declarations foreach(Declaration* decl, decls) { if(decl->isForwardDeclaration() && decls.size() > 1) { decls.removeAll(decl); } } foreach(Declaration* decl, decls) { DUChainItem item; item.m_item = decl; item.m_text = decl->qualifiedIdentifier().toString(); //item.m_project = .. @todo fill ret << QuickOpenDataPointer(new DUChainItemData(item, true)); } if(decls.isEmpty()) { DUChainItem item; item.m_text = m_filteredItems[a].m_id.toString(); //item.m_project = .. @todo fill ret << QuickOpenDataPointer(new DUChainItemData(item)); } } else { kDebug() << "Could not find standard-context"; } if(!ret.isEmpty()) { QList append = ret.mid(1); if(!append.isEmpty()) { AddedItems addMap; for(AddedItems::iterator it = m_addedItems.begin(); it != m_addedItems.end();) { if(it.key() == pos) { //There already is appended data stored, nothing to do return ret.first(); } else if(it.key() > pos) { addMap[it.key() + append.count()] = it.value(); it = m_addedItems.erase(it); } else { ++it; } } m_addedItems.insert(pos, append); for(AddedItems::const_iterator it = addMap.constBegin(); it != addMap.constEnd(); ++it) { m_addedItems.insert(it.key(), it.value()); } } return ret.first(); } else { return KDevelop::QuickOpenDataPointer(); } } void ProjectItemDataProvider::reset() { m_usingFiles = m_quickopen->fileSet(); m_currentItems.clear(); m_addedItems.clear(); KDevelop::DUChainReadLocker lock( DUChain::lock() ); foreach( const IndexedString& u, m_usingFiles ) { uint count; const KDevelop::CodeModelItem* items; CodeModel::self().items( u, count, items ); for(uint a = 0; a < count; ++a) { if(!items[a].id.isValid() || items[a].kind & CodeModelItem::ForwardDeclaration) { continue; } if(((m_itemTypes & Classes) && (items[a].kind & CodeModelItem::Class)) || ((m_itemTypes & Functions) && (items[a].kind & CodeModelItem::Function))) { QualifiedIdentifier id = items[a].id.identifier(); if (id.isEmpty() || id.at(0).identifier().isEmpty()) { // id.isEmpty() not always hit when .toString() is actually empty... // anyhow, this makes sure that we don't show duchain items without // any name that could be searched for. This happens e.g. in the c++ // plugin for anonymous structs or sometimes for declarations in macro // expressions continue; } m_currentItems << CodeModelViewItem(u, id); } } } m_filteredItems = m_currentItems; m_currentFilter.clear(); } uint addedItems(const AddedItems& items) { uint add = 0; for(AddedItems::const_iterator it = items.constBegin(); it != items.constEnd(); ++it) { add += it.value().count(); } return add; } uint ProjectItemDataProvider::itemCount() const { return m_filteredItems.count() + addedItems(m_addedItems); } uint ProjectItemDataProvider::unfilteredItemCount() const { return m_currentItems.count() + addedItems(m_addedItems); } QStringList ProjectItemDataProvider::supportedItemTypes() { QStringList ret; ret << i18n("Classes"); ret << i18n("Functions"); return ret; } void ProjectItemDataProvider::enableData( const QStringList& items, const QStringList& scopes ) { if(scopes.contains(i18n("Project"))) { m_itemTypes = NoItems; if(items.contains(i18n("Classes"))) { m_itemTypes = (ItemTypes)(m_itemTypes | Classes); } if( items.contains(i18n("Functions"))) { m_itemTypes = (ItemTypes)(m_itemTypes | Functions); } } else { m_itemTypes = NoItems; } } diff --git a/plugins/quickopen/quickopenplugin.cpp b/plugins/quickopen/quickopenplugin.cpp index edc743fa9..8e0d77d84 100644 --- a/plugins/quickopen/quickopenplugin.cpp +++ b/plugins/quickopen/quickopenplugin.cpp @@ -1,1585 +1,1585 @@ /* * This file is part of KDevelop * * Copyright 2007 David Nolden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "quickopenplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "expandingtree/expandingdelegate.h" #include "ui_quickopen.h" #include "quickopenmodel.h" #include "projectfilequickopen.h" #include "projectitemquickopen.h" #include "declarationlistquickopen.h" #include "customlistquickopen.h" #include #include #include #include #include #include #include #include using namespace KDevelop; const int rowCountForDisablingScrollBar = 5000; const bool noHtmlDestriptionInOutline = true; class QuickOpenWidgetCreator { public: virtual ~QuickOpenWidgetCreator() { } virtual QuickOpenWidget* createWidget() = 0; virtual QString objectNameForLine() = 0; virtual void widgetShown() { } }; class StandardQuickOpenWidgetCreator : public QuickOpenWidgetCreator { public: StandardQuickOpenWidgetCreator(const QStringList& items, const QStringList& scopes) : m_items(items) , m_scopes(scopes) { } virtual QString objectNameForLine() { return "Quickopen"; } void setItems(const QStringList& scopes, const QStringList& items) { m_scopes = scopes; m_items = items; } virtual QuickOpenWidget* createWidget() { QStringList useItems = m_items; if(useItems.isEmpty()) useItems = QuickOpenPlugin::self()->lastUsedItems; QStringList useScopes = m_scopes; if(useScopes.isEmpty()) useScopes = QuickOpenPlugin::self()->lastUsedScopes; return new QuickOpenWidget( i18n("Quick Open"), QuickOpenPlugin::self()->m_model, QuickOpenPlugin::self()->lastUsedItems, useScopes, false, true ); } QStringList m_items; QStringList m_scopes; }; class QuickOpenDelegate : public ExpandingDelegate { public: QuickOpenDelegate(ExpandingWidgetModel* model, QObject* parent = 0L) : ExpandingDelegate(model, parent) { } virtual QList createHighlighting(const QModelIndex& index, QStyleOptionViewItem& option) const { QList highlighting = index.data(KTextEditor::CodeCompletionModel::CustomHighlight).toList(); if(!highlighting.isEmpty()) return highlightingFromVariantList(highlighting); return ExpandingDelegate::createHighlighting( index, option ); } }; class OutlineFilter : public DUChainUtils::DUChainItemFilter { public: enum OutlineMode { Functions, FunctionsAndClasses }; OutlineFilter(QList& _items, OutlineMode _mode = FunctionsAndClasses) : items(_items), mode(_mode) { } virtual bool accept(Declaration* decl) { if(decl->range().isEmpty()) return false; bool collectable = mode == Functions ? decl->isFunctionDeclaration() : (decl->isFunctionDeclaration() || (decl->internalContext() && decl->internalContext()->type() == DUContext::Class)); if (collectable) { DUChainItem item; item.m_item = IndexedDeclaration(decl); item.m_text = decl->toString(); items << item; return true; } else return false; } virtual bool accept(DUContext* ctx) { if(ctx->type() == DUContext::Class || ctx->type() == DUContext::Namespace || ctx->type() == DUContext::Global || ctx->type() == DUContext::Other || ctx->type() == DUContext::Helper ) return true; else return false; } QList& items; OutlineMode mode; }; K_PLUGIN_FACTORY(KDevQuickOpenFactory, registerPlugin(); ) K_EXPORT_PLUGIN(KDevQuickOpenFactory(KAboutData("kdevquickopen","kdevquickopen", ki18n("Quick Open"), "0.1", ki18n("Quickly open resources such as files, classes and methods."), KAboutData::License_GPL))) Declaration* cursorDeclaration() { IDocument* doc = ICore::self()->documentController()->activeDocument(); if(!doc) return 0; KTextEditor::Document* textDoc = doc->textDocument(); if(!textDoc) return 0; KTextEditor::View* view = textDoc->activeView(); if(!view) return 0; KDevelop::DUChainReadLocker lock( DUChain::lock() ); return DUChainUtils::declarationForDefinition( DUChainUtils::itemUnderCursor( doc->url(), SimpleCursor(view->cursorPosition()) ) ); } ///The first definition that belongs to a context that surrounds the current cursor Declaration* cursorContextDeclaration() { IDocument* doc = ICore::self()->documentController()->activeDocument(); if(!doc) return 0; KTextEditor::Document* textDoc = doc->textDocument(); if(!textDoc) return 0; KTextEditor::View* view = textDoc->activeView(); if(!view) return 0; KDevelop::DUChainReadLocker lock( DUChain::lock() ); TopDUContext* ctx = DUChainUtils::standardContextForUrl(doc->url()); if(!ctx) return 0; SimpleCursor cursor(view->cursorPosition()); DUContext* subCtx = ctx->findContext(ctx->transformToLocalRevision(cursor)); while(subCtx && !subCtx->owner()) subCtx = subCtx->parentContext(); Declaration* definition = 0; if(!subCtx || !subCtx->owner()) definition = DUChainUtils::declarationInLine(cursor, ctx); else definition = subCtx->owner(); if(!definition) return 0; return definition; } //Returns only the name, no template-parameters or scope QString cursorItemText() { KDevelop::DUChainReadLocker lock( DUChain::lock() ); Declaration* decl = cursorDeclaration(); if(!decl) return QString(); IDocument* doc = ICore::self()->documentController()->activeDocument(); if(!doc) return QString(); TopDUContext* context = DUChainUtils::standardContextForUrl( doc->url() ); if( !context ) { kDebug() << "Got no standard context"; return QString(); } AbstractType::Ptr t = decl->abstractType(); IdentifiedType* idType = dynamic_cast(t.unsafeData()); if( idType && idType->declaration(context) ) decl = idType->declaration(context); if(!decl->qualifiedIdentifier().isEmpty()) - return decl->qualifiedIdentifier().last().identifier().str(); + return decl->qualifiedIdentifier().last().identifier().toString(); return QString(); } QuickOpenLineEdit* QuickOpenPlugin::createQuickOpenLineWidget() { return new QuickOpenLineEdit(new StandardQuickOpenWidgetCreator(QStringList(), QStringList())); } void QuickOpenWidget::showStandardButtons(bool show) { if(show) { o.okButton->show(); o.cancelButton->show(); }else{ o.okButton->hide(); o.cancelButton->hide(); } } QuickOpenWidget::QuickOpenWidget( QString title, QuickOpenModel* model, const QStringList& initialItems, const QStringList& initialScopes, bool listOnly, bool noSearchField ) : m_model(model), m_expandedTemporary(false) { m_filterTimer.setSingleShot(true); connect(&m_filterTimer, SIGNAL(timeout()), this, SLOT(applyFilter())); Q_UNUSED( title ); o.setupUi( this ); o.list->header()->hide(); o.list->setRootIsDecorated( false ); o.list->setVerticalScrollMode( QAbstractItemView::ScrollPerItem ); connect(o.list->verticalScrollBar(), SIGNAL(valueChanged(int)), m_model, SLOT(placeExpandingWidgets())); o.searchLine->setFocus(); o.list->setItemDelegate( new QuickOpenDelegate( m_model, o.list ) ); if(!listOnly) { QStringList allTypes = m_model->allTypes(); QStringList allScopes = m_model->allScopes(); QMenu* itemsMenu = new QMenu; foreach( const QString &type, allTypes ) { QAction* action = new QAction(type, itemsMenu); action->setCheckable(true); action->setChecked(initialItems.isEmpty() || initialItems.contains( type )); connect( action, SIGNAL(toggled(bool)), this, SLOT(updateProviders()), Qt::QueuedConnection ); itemsMenu->addAction(action); } o.itemsButton->setMenu(itemsMenu); QMenu* scopesMenu = new QMenu; foreach( const QString &scope, allScopes ) { QAction* action = new QAction(scope, scopesMenu); action->setCheckable(true); action->setChecked(initialScopes.isEmpty() || initialScopes.contains( scope ) ); connect( action, SIGNAL(toggled(bool)), this, SLOT(updateProviders()), Qt::QueuedConnection ); scopesMenu->addAction(action); } o.scopesButton->setMenu(scopesMenu); }else{ o.list->setFocusPolicy(Qt::StrongFocus); o.scopesButton->hide(); o.itemsButton->hide(); o.label->hide(); o.label_2->hide(); } showSearchField(!noSearchField); o.okButton->hide(); o.cancelButton->hide(); o.searchLine->installEventFilter( this ); o.list->installEventFilter( this ); o.list->setFocusPolicy(Qt::NoFocus); o.scopesButton->setFocusPolicy(Qt::NoFocus); o.itemsButton->setFocusPolicy(Qt::NoFocus); connect( o.searchLine, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString)) ); connect( o.list, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(doubleClicked(QModelIndex)) ); connect(o.okButton, SIGNAL(clicked(bool)), this, SLOT(accept())); connect(o.okButton, SIGNAL(clicked(bool)), SIGNAL(ready())); connect(o.cancelButton, SIGNAL(clicked(bool)), SIGNAL(ready())); updateProviders(); updateTimerInterval(true); // no need to call this, it's done by updateProviders already // m_model->restart(); } void QuickOpenWidget::updateTimerInterval(bool cheapFilterChange) { const int MAX_ITEMS = 10000; if ( cheapFilterChange && m_model->rowCount(QModelIndex()) < MAX_ITEMS ) { // cheap change and there are currently just a few items, // so apply filter instantly m_filterTimer.setInterval(0); } else if ( m_model->unfilteredRowCount() < MAX_ITEMS ) { // not a cheap change, but there are generally // just a few items in the list: apply filter instantly m_filterTimer.setInterval(0); } else { // otherwise use a timer to prevent sluggishness while typing m_filterTimer.setInterval(300); } } void QuickOpenWidget::showEvent(QShowEvent* e) { QWidget::showEvent(e); // The column width only has an effect _after_ the widget has been shown o.list->setColumnWidth( 0, 20 ); } void QuickOpenWidget::setAlternativeSearchField(KLineEdit* alterantiveSearchField) { o.searchLine = alterantiveSearchField; o.searchLine->installEventFilter( this ); connect( o.searchLine, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString)) ); } void QuickOpenWidget::showSearchField(bool b) { if(b){ o.searchLine->show(); o.searchLabel->show(); }else{ o.searchLine->hide(); o.searchLabel->hide(); } } void QuickOpenWidget::prepareShow() { o.list->setModel( 0 ); o.list->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); m_model->setTreeView( o.list ); o.list->setModel( m_model ); m_filterTimer.stop(); m_filter = QString(); if (!m_preselectedText.isEmpty()) { o.searchLine->setText(m_preselectedText); o.searchLine->selectAll(); } applyFilter(); connect( o.list->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), this, SLOT(currentChanged(QModelIndex,QModelIndex)) ); connect( o.list->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(currentChanged(QItemSelection,QItemSelection)) ); updateScrollBarState(); } void QuickOpenWidgetDialog::run() { m_widget->prepareShow(); m_dialog->show(); } QuickOpenWidget::~QuickOpenWidget() { m_model->setTreeView( 0 ); } QuickOpenWidgetDialog::QuickOpenWidgetDialog(QString title, QuickOpenModel* model, const QStringList& initialItems, const QStringList& initialScopes, bool listOnly, bool noSearchField) { m_widget = new QuickOpenWidget(title, model, initialItems, initialScopes, listOnly, noSearchField); // the QMenu might close on esc and we want to close the whole dialog then connect( m_widget, SIGNAL(aboutToHide()), this, SLOT(deleteLater()) ); //KDialog always sets the focus on the "OK" button, so we use QDialog m_dialog = new QDialog( ICore::self()->uiController()->activeMainWindow() ); m_dialog->resize(QSize(800, 400)); m_dialog->setWindowTitle(title); QVBoxLayout* layout = new QVBoxLayout(m_dialog); layout->addWidget(m_widget); m_widget->showStandardButtons(true); connect(m_widget, SIGNAL(ready()), m_dialog, SLOT(close())); connect( m_dialog, SIGNAL(accepted()), m_widget, SLOT(accept()) ); } QuickOpenWidgetDialog::~QuickOpenWidgetDialog() { delete m_dialog; } void QuickOpenWidget::setPreselectedText(const QString& text) { m_preselectedText = text; } void QuickOpenWidget::updateProviders() { if(QAction* action = qobject_cast(sender())) { QMenu* menu = qobject_cast(action->parentWidget()); if(menu) { menu->show(); menu->setActiveAction(action); } } QStringList checkedItems; if(o.itemsButton->menu()) { foreach( QObject* obj, o.itemsButton->menu()->children() ) { QAction* box = qobject_cast( obj ); if( box ) { if( box->isChecked() ) checkedItems << box->text().remove('&'); } } o.itemsButton->setText(checkedItems.join(", ")); } QStringList checkedScopes; if(o.scopesButton->menu()) { foreach( QObject* obj, o.scopesButton->menu()->children() ) { QAction* box = qobject_cast( obj ); if( box ) { if( box->isChecked() ) checkedScopes << box->text().remove('&'); } } o.scopesButton->setText(checkedScopes.join(", ")); } emit itemsChanged( checkedItems ); emit scopesChanged( checkedScopes ); m_model->enableProviders( checkedItems, checkedScopes ); } void QuickOpenWidget::updateScrollBarState() { if(m_model->rowCount(QModelIndex()) > rowCountForDisablingScrollBar) o.list->verticalScrollBar()->setEnabled(false); else o.list->verticalScrollBar()->setEnabled(true); } void QuickOpenWidget::textChanged( const QString& str ) { // "cheap" when something was just appended to the current filter updateTimerInterval(str.startsWith(m_filter)); m_filter = str; m_filterTimer.start(); } void QuickOpenWidget::applyFilter() { m_model->textChanged( m_filter ); updateScrollBarState(); QModelIndex currentIndex = m_model->index(0, 0, QModelIndex()); o.list->selectionModel()->setCurrentIndex( currentIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current ); callRowSelected(); } void QuickOpenWidget::callRowSelected() { QModelIndex currentIndex = o.list->selectionModel()->currentIndex(); if( currentIndex.isValid() ) m_model->rowSelected( currentIndex ); else kDebug() << "current index is not valid"; } void QuickOpenWidget::currentChanged( const QModelIndex& /*current*/, const QModelIndex& /*previous */) { callRowSelected(); } void QuickOpenWidget::currentChanged( const QItemSelection& /*current*/, const QItemSelection& /*previous */) { callRowSelected(); } void QuickOpenWidget::accept() { QString filterText = o.searchLine->text(); m_model->execute( o.list->currentIndex(), filterText ); } void QuickOpenWidget::doubleClicked ( const QModelIndex & index ) { // crash guard: https://bugs.kde.org/show_bug.cgi?id=297178 o.list->setCurrentIndex(index); QMetaObject::invokeMethod(this, "accept", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "ready", Qt::QueuedConnection); } bool QuickOpenWidget::eventFilter ( QObject * watched, QEvent * event ) { QKeyEvent *keyEvent = dynamic_cast(event); if( event->type() == QEvent::KeyRelease ) { if(keyEvent->key() == Qt::Key_Alt) { if((m_expandedTemporary && m_altDownTime.msecsTo( QTime::currentTime() ) > 300) || (!m_expandedTemporary && m_altDownTime.msecsTo( QTime::currentTime() ) < 300 && m_hadNoCommandSinceAlt)) { //Unexpand the item QModelIndex row = o.list->selectionModel()->currentIndex(); if( row.isValid() ) { row = row.sibling( row.row(), 0 ); if(m_model->isExpanded( row )) m_model->setExpanded( row, false ); } } m_expandedTemporary = false; } } if( event->type() == QEvent::KeyPress ) { m_hadNoCommandSinceAlt = false; if(keyEvent->key() == Qt::Key_Alt) { m_hadNoCommandSinceAlt = true; //Expand QModelIndex row = o.list->selectionModel()->currentIndex(); if( row.isValid() ) { row = row.sibling( row.row(), 0 ); m_altDownTime = QTime::currentTime(); if(!m_model->isExpanded( row )) { m_expandedTemporary = true; m_model->setExpanded( row, true ); } } } switch( keyEvent->key() ) { case Qt::Key_Tab: if ( keyEvent->modifiers() == Qt::NoModifier ) { // Tab should work just like Down QCoreApplication::sendEvent(o.list, new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier)); QCoreApplication::sendEvent(o.list, new QKeyEvent(QEvent::KeyRelease, Qt::Key_Down, Qt::NoModifier)); return true; } break; case Qt::Key_Backtab: if ( keyEvent->modifiers() == Qt::ShiftModifier ) { // Shift + Tab should work just like Up QCoreApplication::sendEvent(o.list, new QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier)); QCoreApplication::sendEvent(o.list, new QKeyEvent(QEvent::KeyRelease, Qt::Key_Up, Qt::NoModifier)); return true; } break; case Qt::Key_Down: case Qt::Key_Up: { if( keyEvent->modifiers() == Qt::AltModifier ) { QWidget* w = m_model->expandingWidget(o.list->selectionModel()->currentIndex()); if( KDevelop::QuickOpenEmbeddedWidgetInterface* interface = dynamic_cast( w ) ){ if( keyEvent->key() == Qt::Key_Down ) interface->down(); else interface->up(); return true; } return false; } } case Qt::Key_PageUp: case Qt::Key_PageDown: if(watched == o.list ) return false; QApplication::sendEvent( o.list, event ); //callRowSelected(); return true; case Qt::Key_Left: { //Expand/unexpand if( keyEvent->modifiers() == Qt::AltModifier ) { //Eventually Send action to the widget QWidget* w = m_model->expandingWidget(o.list->selectionModel()->currentIndex()); if( KDevelop::QuickOpenEmbeddedWidgetInterface* interface = dynamic_cast( w ) ){ interface->previous(); return true; } } else { QModelIndex row = o.list->selectionModel()->currentIndex(); if( row.isValid() ) { row = row.sibling( row.row(), 0 ); if( m_model->isExpanded( row ) ) { m_model->setExpanded( row, false ); return true; } } } return false; } case Qt::Key_Right: { //Expand/unexpand if( keyEvent->modifiers() == Qt::AltModifier ) { //Eventually Send action to the widget QWidget* w = m_model->expandingWidget(o.list->selectionModel()->currentIndex()); if( KDevelop::QuickOpenEmbeddedWidgetInterface* interface = dynamic_cast( w ) ){ interface->next(); return true; } } else { QModelIndex row = o.list->selectionModel()->currentIndex(); if( row.isValid() ) { row = row.sibling( row.row(), 0 ); if( !m_model->isExpanded( row ) ) { m_model->setExpanded( row, true ); return true; } } } return false; } case Qt::Key_Return: case Qt::Key_Enter: { if (m_filterTimer.isActive()) { m_filterTimer.stop(); applyFilter(); } if( keyEvent->modifiers() == Qt::AltModifier ) { //Eventually Send action to the widget QWidget* w = m_model->expandingWidget(o.list->selectionModel()->currentIndex()); if( KDevelop::QuickOpenEmbeddedWidgetInterface* interface = dynamic_cast( w ) ){ interface->accept(); return true; } } else { QString filterText = o.searchLine->text(); //Safety: Track whether this object is deleted. When execute() is called, a dialog may be opened, //which kills the quickopen widget. QPointer stillExists(this); if( m_model->execute( o.list->currentIndex(), filterText ) ) { if(!stillExists) return true; if(!(keyEvent->modifiers() & Qt::ShiftModifier)) emit ready(); } else { //Maybe the filter-text was changed: if( filterText != o.searchLine->text() ) { o.searchLine->setText( filterText ); } } } return true; } } } return false; } QuickOpenLineEdit* QuickOpenPlugin::quickOpenLine(QString name) { QList< QuickOpenLineEdit* > lines = ICore::self()->uiController()->activeMainWindow()->findChildren(name); foreach(QuickOpenLineEdit* line, lines) { if(line->isVisible()) { return line; } } return 0; } static QuickOpenPlugin* staticQuickOpenPlugin = 0; QuickOpenPlugin* QuickOpenPlugin::self() { return staticQuickOpenPlugin; } void QuickOpenPlugin::createActionsForMainWindow(Sublime::MainWindow* /*window*/, QString& xmlFile, KActionCollection& actions) { xmlFile ="kdevquickopen.rc"; KAction* quickOpen = actions.addAction("quick_open"); quickOpen->setText( i18n("&Quick Open") ); quickOpen->setIcon( KIcon("quickopen") ); quickOpen->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_Q ); connect(quickOpen, SIGNAL(triggered(bool)), this, SLOT(quickOpen())); KAction* quickOpenFile = actions.addAction("quick_open_file"); quickOpenFile->setText( i18n("Quick Open &File") ); quickOpenFile->setIcon( KIcon("quickopen-file") ); quickOpenFile->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_O ); connect(quickOpenFile, SIGNAL(triggered(bool)), this, SLOT(quickOpenFile())); KAction* quickOpenClass = actions.addAction("quick_open_class"); quickOpenClass->setText( i18n("Quick Open &Class") ); quickOpenClass->setIcon( KIcon("quickopen-class") ); quickOpenClass->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_C ); connect(quickOpenClass, SIGNAL(triggered(bool)), this, SLOT(quickOpenClass())); KAction* quickOpenFunction = actions.addAction("quick_open_function"); quickOpenFunction->setText( i18n("Quick Open &Function") ); quickOpenFunction->setIcon( KIcon("quickopen-function") ); quickOpenFunction->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_M ); connect(quickOpenFunction, SIGNAL(triggered(bool)), this, SLOT(quickOpenFunction())); m_quickOpenDeclaration = actions.addAction("quick_open_jump_declaration"); m_quickOpenDeclaration->setText( i18n("Jump to Declaration") ); m_quickOpenDeclaration->setIcon( KIcon("go-jump-declaration" ) ); m_quickOpenDeclaration->setShortcut( Qt::CTRL | Qt::Key_Period ); connect(m_quickOpenDeclaration, SIGNAL(triggered(bool)), this, SLOT(quickOpenDeclaration()), Qt::QueuedConnection); m_quickOpenDefinition = actions.addAction("quick_open_jump_definition"); m_quickOpenDefinition->setText( i18n("Jump to Definition") ); m_quickOpenDefinition->setIcon( KIcon("go-jump-definition" ) ); m_quickOpenDefinition->setShortcut( Qt::CTRL | Qt::Key_Comma ); connect(m_quickOpenDefinition, SIGNAL(triggered(bool)), this, SLOT(quickOpenDefinition()), Qt::QueuedConnection); KAction* quickOpenLine = actions.addAction("quick_open_line"); quickOpenLine->setText( i18n("Embedded Quick Open") ); // quickOpenLine->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_E ); // connect(quickOpenLine, SIGNAL(triggered(bool)), this, SLOT(quickOpenLine(bool))); quickOpenLine->setDefaultWidget(createQuickOpenLineWidget()); // KAction* quickOpenNavigate = actions.addAction("quick_open_navigate"); // quickOpenNavigate->setText( i18n("Navigate Declaration") ); // quickOpenNavigate->setShortcut( Qt::ALT | Qt::Key_Space ); // connect(quickOpenNavigate, SIGNAL(triggered(bool)), this, SLOT(quickOpenNavigate())); KAction* quickOpenNextFunction = actions.addAction("quick_open_next_function"); quickOpenNextFunction->setText( i18n("Next Function") ); quickOpenNextFunction->setShortcut( Qt::CTRL| Qt::ALT | Qt::Key_PageDown ); connect(quickOpenNextFunction, SIGNAL(triggered(bool)), this, SLOT(nextFunction())); KAction* quickOpenPrevFunction = actions.addAction("quick_open_prev_function"); quickOpenPrevFunction->setText( i18n("Previous Function") ); quickOpenPrevFunction->setShortcut( Qt::CTRL| Qt::ALT | Qt::Key_PageUp ); connect(quickOpenPrevFunction, SIGNAL(triggered(bool)), this, SLOT(previousFunction())); KAction* quickOpenNavigateFunctions = actions.addAction("quick_open_outline"); quickOpenNavigateFunctions->setText( i18n("Outline") ); quickOpenNavigateFunctions->setShortcut( Qt::CTRL| Qt::ALT | Qt::Key_N ); connect(quickOpenNavigateFunctions, SIGNAL(triggered(bool)), this, SLOT(quickOpenNavigateFunctions())); } QuickOpenPlugin::QuickOpenPlugin(QObject *parent, const QVariantList&) : KDevelop::IPlugin(KDevQuickOpenFactory::componentData(), parent) { staticQuickOpenPlugin = this; KDEV_USE_EXTENSION_INTERFACE( KDevelop::IQuickOpen ) m_model = new QuickOpenModel( 0 ); KConfigGroup quickopengrp = KGlobal::config()->group("QuickOpen"); lastUsedScopes = quickopengrp.readEntry("SelectedScopes", QStringList() << i18n("Project") << i18n("Includes") << i18n("Includers") << i18n("Currently Open") ); lastUsedItems = quickopengrp.readEntry("SelectedItems", QStringList() ); { m_openFilesData = new OpenFilesDataProvider(); QStringList scopes, items; scopes << i18n("Currently Open"); items << i18n("Files"); m_model->registerProvider( scopes, items, m_openFilesData ); } { m_projectFileData = new ProjectFileDataProvider(); QStringList scopes, items; scopes << i18n("Project"); items << i18n("Files"); m_model->registerProvider( scopes, items, m_projectFileData ); } { m_projectItemData = new ProjectItemDataProvider(this); QStringList scopes, items; scopes << i18n("Project"); items << ProjectItemDataProvider::supportedItemTypes(); m_model->registerProvider( scopes, items, m_projectItemData ); } } QuickOpenPlugin::~QuickOpenPlugin() { freeModel(); delete m_model; delete m_projectFileData; delete m_projectItemData; delete m_openFilesData; } void QuickOpenPlugin::unload() { } ContextMenuExtension QuickOpenPlugin::contextMenuExtension(Context* context) { KDevelop::ContextMenuExtension menuExt = KDevelop::IPlugin::contextMenuExtension( context ); KDevelop::DeclarationContext *codeContext = dynamic_cast(context); if (!codeContext) return menuExt; DUChainReadLocker readLock; Declaration* decl(codeContext->declaration().data()); if (decl) { const bool isDef = FunctionDefinition::definition(decl); if (codeContext->use().isValid() || !isDef) { menuExt.addAction( KDevelop::ContextMenuExtension::ExtensionGroup, m_quickOpenDeclaration); } if(isDef) { menuExt.addAction( KDevelop::ContextMenuExtension::ExtensionGroup, m_quickOpenDefinition); } } return menuExt; } void QuickOpenPlugin::showQuickOpen(const QStringList& items) { if(!freeModel()) return; QStringList initialItems = items; QStringList useScopes = lastUsedScopes; if (!useScopes.contains(i18n("Currently Open"))) useScopes << i18n("Currently Open"); showQuickOpenWidget(initialItems, useScopes, false); } void QuickOpenPlugin::showQuickOpen( ModelTypes modes ) { if(!freeModel()) return; QStringList initialItems; if( modes & Files || modes & OpenFiles ) initialItems << i18n("Files"); if( modes & Functions ) initialItems << i18n("Functions"); if( modes & Classes ) initialItems << i18n("Classes"); QStringList useScopes = lastUsedScopes; if((modes & OpenFiles) && !useScopes.contains(i18n("Currently Open"))) useScopes << i18n("Currently Open"); bool preselectText = (!(modes & Files) || modes == QuickOpenPlugin::All); showQuickOpenWidget(initialItems, useScopes, preselectText); } void QuickOpenPlugin::showQuickOpenWidget(const QStringList& items, const QStringList& scopes, bool preselectText) { QuickOpenWidgetDialog* dialog = new QuickOpenWidgetDialog( i18n("Quick Open"), m_model, items, scopes ); m_currentWidgetHandler = dialog; if (preselectText) { KDevelop::IDocument *currentDoc = core()->documentController()->activeDocument(); if (currentDoc && currentDoc->isTextDocument()) { QString preselected = currentDoc->textSelection().isEmpty() ? currentDoc->textWord() : currentDoc->textDocument()->text(currentDoc->textSelection()); dialog->widget()->setPreselectedText(preselected); } } connect( dialog->widget(), SIGNAL(scopesChanged(QStringList)), this, SLOT(storeScopes(QStringList)) ); //Not connecting itemsChanged to storeItems, as showQuickOpen doesn't use lastUsedItems and so shouldn't store item changes //connect( dialog->widget(), SIGNAL(itemsChanged(QStringList)), this, SLOT(storeItems(QStringList)) ); dialog->widget()->o.itemsButton->setEnabled(false); if(quickOpenLine()) { quickOpenLine()->showWithWidget(dialog->widget()); dialog->deleteLater(); }else{ dialog->run(); } } void QuickOpenPlugin::storeScopes( const QStringList& scopes ) { lastUsedScopes = scopes; KConfigGroup grp = KGlobal::config()->group("QuickOpen"); grp.writeEntry( "SelectedScopes", scopes ); } void QuickOpenPlugin::storeItems( const QStringList& items ) { lastUsedItems = items; KConfigGroup grp = KGlobal::config()->group("QuickOpen"); grp.writeEntry( "SelectedItems", items ); } void QuickOpenPlugin::quickOpen() { if(quickOpenLine()) //Same as clicking on Quick Open quickOpenLine()->setFocus(); else showQuickOpen( All ); } void QuickOpenPlugin::quickOpenFile() { showQuickOpen( (ModelTypes)(Files | OpenFiles) ); } void QuickOpenPlugin::quickOpenFunction() { showQuickOpen( Functions ); } void QuickOpenPlugin::quickOpenClass() { showQuickOpen( Classes ); } QSet QuickOpenPlugin::fileSet() const { return m_model->fileSet(); } void QuickOpenPlugin::registerProvider( const QStringList& scope, const QStringList& type, KDevelop::QuickOpenDataProviderBase* provider ) { m_model->registerProvider( scope, type, provider ); } bool QuickOpenPlugin::removeProvider( KDevelop::QuickOpenDataProviderBase* provider ) { m_model->removeProvider( provider ); return true; } void QuickOpenPlugin::quickOpenDeclaration() { if(jumpToSpecialObject()) return; KDevelop::DUChainReadLocker lock( DUChain::lock() ); Declaration* decl = cursorDeclaration(); if(!decl) { kDebug() << "Found no declaration for cursor, cannot jump"; return; } decl->activateSpecialization(); IndexedString u = decl->url(); SimpleCursor c = decl->rangeInCurrentRevision().start; - if(u.str().isEmpty()) { + if(u.isEmpty()) { kDebug() << "Got empty url for declaration" << decl->toString(); return; } lock.unlock(); - core()->documentController()->openDocument(KUrl(u.str()), c.textCursor()); + core()->documentController()->openDocument(u.toUrl(), c.textCursor()); } ///Returns all languages for that url that have a language support, and prints warnings for other ones. QList languagesWithSupportForUrl(KUrl url) { QList languages = ICore::self()->languageController()->languagesForUrl(url); QList ret; foreach( KDevelop::ILanguage* language, languages) { if(language->languageSupport()) { ret << language; }else{ kDebug() << "got no language-support for language" << language->name(); } } return ret; } QWidget* QuickOpenPlugin::specialObjectNavigationWidget() const { if( !ICore::self()->documentController()->activeDocument() || !ICore::self()->documentController()->activeDocument()->textDocument() || !ICore::self()->documentController()->activeDocument()->textDocument()->activeView() ) return 0; KUrl url = ICore::self()->documentController()->activeDocument()->url(); foreach( KDevelop::ILanguage* language, languagesWithSupportForUrl(url) ) { QWidget* w = language->languageSupport()->specialLanguageObjectNavigationWidget(url, SimpleCursor(ICore::self()->documentController()->activeDocument()->textDocument()->activeView()->cursorPosition()) ); if(w) return w; } return 0; } QPair QuickOpenPlugin::specialObjectJumpPosition() const { if( !ICore::self()->documentController()->activeDocument() || !ICore::self()->documentController()->activeDocument()->textDocument() || !ICore::self()->documentController()->activeDocument()->textDocument()->activeView() ) return qMakePair(KUrl(), SimpleCursor()); KUrl url = ICore::self()->documentController()->activeDocument()->url(); foreach( KDevelop::ILanguage* language, languagesWithSupportForUrl(url) ) { QPair pos = language->languageSupport()->specialLanguageObjectJumpCursor(url, SimpleCursor(ICore::self()->documentController()->activeDocument()->textDocument()->activeView()->cursorPosition()) ); if(pos.second.isValid()) { return pos; } } return qMakePair(KUrl(), SimpleCursor::invalid()); } bool QuickOpenPlugin::jumpToSpecialObject() { QPair pos = specialObjectJumpPosition(); if(pos.second.isValid()) { if(pos.first.isEmpty()) { kDebug() << "Got empty url for special language object"; return false; } ICore::self()->documentController()->openDocument(pos.first, pos.second.textCursor()); return true; } return false; } void QuickOpenPlugin::quickOpenDefinition() { if(jumpToSpecialObject()) return; KDevelop::DUChainReadLocker lock( DUChain::lock() ); Declaration* decl = cursorDeclaration(); if(!decl) { kDebug() << "Found no declaration for cursor, cannot jump"; return; } IndexedString u = decl->url(); SimpleCursor c = decl->rangeInCurrentRevision().start; if(FunctionDefinition* def = FunctionDefinition::definition(decl)) { def->activateSpecialization(); u = def->url(); c = def->rangeInCurrentRevision().start; }else{ kDebug() << "Found no definition for declaration"; decl->activateSpecialization(); } - if(u.str().isEmpty()) { + if(u.isEmpty()) { kDebug() << "Got empty url for declaration" << decl->toString(); return; } lock.unlock(); - core()->documentController()->openDocument(KUrl(u.str()), c.textCursor()); + core()->documentController()->openDocument(u.toUrl(), c.textCursor()); } void QuickOpenPlugin::quickOpenNavigate() { if(!freeModel()) return; KDevelop::DUChainReadLocker lock( DUChain::lock() ); QWidget* widget = specialObjectNavigationWidget(); Declaration* decl = cursorDeclaration(); if(widget || decl) { QuickOpenModel* model = new QuickOpenModel(0); model->setExpandingWidgetHeightIncrease(200); //Make the widget higher, since it's the only visible item if(widget) { QPair jumpPos = specialObjectJumpPosition(); CustomItem item; item.m_widget = widget; item.m_executeTargetPosition = jumpPos.second; item.m_executeTargetUrl = jumpPos.first; QList items; items << item; model->registerProvider( QStringList(), QStringList(), new CustomItemDataProvider(items) ); }else{ DUChainItem item; item.m_item = IndexedDeclaration(decl); item.m_text = decl->qualifiedIdentifier().toString(); QList items; items << item; model->registerProvider( QStringList(), QStringList(), new DeclarationListDataProvider(this, items) ); } //Change the parent so there are no conflicts in destruction order QuickOpenWidgetDialog* dialog = new QuickOpenWidgetDialog( i18n("Navigate"), model, QStringList(), QStringList(), true, true ); m_currentWidgetHandler = dialog; model->setParent(m_currentWidgetHandler); model->setExpanded(model->index(0,0, QModelIndex()), true); dialog->run(); } if(!decl) { kDebug() << "Found no declaration for cursor, cannot navigate"; return; } } bool QuickOpenPlugin::freeModel() { if(m_currentWidgetHandler) delete m_currentWidgetHandler; m_currentWidgetHandler = 0; return true; } void QuickOpenPlugin::nextFunction() { jumpToNearestFunction(NextFunction); } void QuickOpenPlugin::previousFunction() { jumpToNearestFunction(PreviousFunction); } void QuickOpenPlugin::jumpToNearestFunction(QuickOpenPlugin::FunctionJumpDirection direction) { IDocument* doc = ICore::self()->documentController()->activeDocument(); if(!doc) { kDebug() << "No active document"; return; } KDevelop::DUChainReadLocker lock( DUChain::lock() ); TopDUContext* context = DUChainUtils::standardContextForUrl( doc->url() ); if( !context ) { kDebug() << "Got no standard context"; return; } QList items; OutlineFilter filter(items, OutlineFilter::Functions); DUChainUtils::collectItems( context, filter ); CursorInRevision cursor = context->transformToLocalRevision(SimpleCursor(doc->cursorPosition())); if (!cursor.isValid()) return; Declaration *nearestDeclBefore = 0; int distanceBefore = INT_MIN; Declaration *nearestDeclAfter = 0; int distanceAfter = INT_MAX; for (int i = 0; i < items.count(); ++i) { Declaration *decl = items[i].m_item.data(); int distance = decl->range().start.line - cursor.line; if (distance < 0 && distance >= distanceBefore) { distanceBefore = distance; nearestDeclBefore = decl; } else if (distance > 0 && distance <= distanceAfter) { distanceAfter = distance; nearestDeclAfter = decl; } } CursorInRevision c = CursorInRevision::invalid(); if (direction == QuickOpenPlugin::NextFunction && nearestDeclAfter) c = nearestDeclAfter->range().start; else if (direction == QuickOpenPlugin::PreviousFunction && nearestDeclBefore) c = nearestDeclBefore->range().start; KTextEditor::Cursor textCursor = KTextEditor::Cursor::invalid(); if (c.isValid()) textCursor = context->transformFromLocalRevision(c).textCursor(); lock.unlock(); if (textCursor.isValid()) core()->documentController()->openDocument(doc->url(), textCursor); else kDebug() << "No declaration to jump to"; } struct CreateOutlineDialog { CreateOutlineDialog() : dialog(0), cursorDecl(0), model(0) { } void start() { if(!QuickOpenPlugin::self()->freeModel()) return; IDocument* doc = ICore::self()->documentController()->activeDocument(); if(!doc) { kDebug() << "No active document"; return; } KDevelop::DUChainReadLocker lock( DUChain::lock() ); TopDUContext* context = DUChainUtils::standardContextForUrl( doc->url() ); if( !context ) { kDebug() << "Got no standard context"; return; } model = new QuickOpenModel(0); OutlineFilter filter(items); DUChainUtils::collectItems( context, filter ); if(noHtmlDestriptionInOutline) { for(int a = 0; a < items.size(); ++a) items[a].m_noHtmlDestription = true; } cursorDecl = cursorContextDeclaration(); model->registerProvider( QStringList(), QStringList(), new DeclarationListDataProvider(QuickOpenPlugin::self(), items, true) ); dialog = new QuickOpenWidgetDialog( i18n("Outline"), model, QStringList(), QStringList(), true ); model->setParent(dialog->widget()); } void finish() { //Select the declaration that contains the cursor if(cursorDecl && dialog) { int num = 0; foreach(const DUChainItem& item, items) { if(item.m_item.data() == cursorDecl) { dialog->widget()->o.list->setCurrentIndex( model->index(num,0,QModelIndex()) ); dialog->widget()->o.list->scrollTo( model->index(num,0,QModelIndex()), QAbstractItemView::PositionAtCenter ); } ++num; } } } QPointer dialog; Declaration* cursorDecl; QList items; QuickOpenModel* model; }; class OutlineQuickopenWidgetCreator : public QuickOpenWidgetCreator { public: OutlineQuickopenWidgetCreator(QStringList /*scopes*/, QStringList /*items*/) : m_creator(0) { } ~OutlineQuickopenWidgetCreator() { delete m_creator; } virtual QuickOpenWidget* createWidget() { delete m_creator; m_creator = new CreateOutlineDialog; m_creator->start(); if(!m_creator->dialog) return 0; m_creator->dialog->deleteLater(); return m_creator->dialog->widget(); } virtual void widgetShown() { if(m_creator) { m_creator->finish(); delete m_creator; m_creator = 0; } } virtual QString objectNameForLine() { return "Outline"; } CreateOutlineDialog* m_creator; }; void QuickOpenPlugin::quickOpenNavigateFunctions() { CreateOutlineDialog create; create.start(); if(!create.dialog) return; m_currentWidgetHandler = create.dialog; QuickOpenLineEdit* line = quickOpenLine("Outline"); if(!line) line = quickOpenLine(); if(line) { line->showWithWidget(create.dialog->widget()); create.dialog->deleteLater(); }else create.dialog->run(); create.finish(); } QuickOpenLineEdit::QuickOpenLineEdit(QuickOpenWidgetCreator* creator) : m_widget(0), m_forceUpdate(false), m_widgetCreator(creator) { setMinimumWidth(200); setMaximumWidth(400); deactivate(); setDefaultText(i18n("Quick Open...")); setToolTip(i18n("Search for files, classes, functions and more," " allowing you to quickly navigate in your source code.")); setObjectName(m_widgetCreator->objectNameForLine()); setFocusPolicy(Qt::ClickFocus); } QuickOpenLineEdit::~QuickOpenLineEdit() { delete m_widget; delete m_widgetCreator; } bool QuickOpenLineEdit::insideThis(QObject* object) { while (object) { kDebug() << object; if (object == this || object == m_widget) { return true; } object = object->parent(); } return false; } void QuickOpenLineEdit::widgetDestroyed(QObject* obj) { Q_UNUSED(obj); deactivate(); } void QuickOpenLineEdit::showWithWidget(QuickOpenWidget* widget) { connect(widget, SIGNAL(destroyed(QObject*)), SLOT(widgetDestroyed(QObject*))); kDebug() << "storing widget" << widget; deactivate(); if(m_widget) { kDebug() << "deleting" << m_widget; delete m_widget; } m_widget = widget; m_forceUpdate = true; setFocus(); } void QuickOpenLineEdit::focusInEvent(QFocusEvent* ev) { QLineEdit::focusInEvent(ev); // delete m_widget; kDebug() << "got focus"; kDebug() << "old widget" << m_widget << "force update:" << m_forceUpdate; if (m_widget && !m_forceUpdate) return; if (!m_forceUpdate && !QuickOpenPlugin::self()->freeModel()) { deactivate(); return; } m_forceUpdate = false; if(!m_widget) { m_widget = m_widgetCreator->createWidget(); if(!m_widget) { deactivate(); return; } } activate(); m_widget->showStandardButtons(false); m_widget->showSearchField(false); m_widget->setParent(0, Qt::ToolTip); m_widget->setFocusPolicy(Qt::NoFocus); m_widget->setAlternativeSearchField(this); QuickOpenPlugin::self()->m_currentWidgetHandler = m_widget; connect(m_widget, SIGNAL(ready()), SLOT(deactivate())); connect( m_widget, SIGNAL(scopesChanged(QStringList)), QuickOpenPlugin::self(), SLOT(storeScopes(QStringList)) ); connect( m_widget, SIGNAL(itemsChanged(QStringList)), QuickOpenPlugin::self(), SLOT(storeItems(QStringList)) ); Q_ASSERT(m_widget->o.searchLine == this); m_widget->prepareShow(); QRect widgetGeometry = QRect(mapToGlobal(QPoint(0, height())), mapToGlobal(QPoint(width(), height() + 400))); widgetGeometry.setWidth(700); ///@todo Waste less space QRect screenGeom = QApplication::desktop()->screenGeometry(this); if (widgetGeometry.right() > screenGeom.right()) { widgetGeometry.moveRight(screenGeom.right()); } if (widgetGeometry.bottom() > screenGeom.bottom()) { widgetGeometry.moveBottom(mapToGlobal(QPoint(0, 0)).y()); } m_widget->setGeometry(widgetGeometry); m_widget->show(); m_widgetCreator->widgetShown(); } void QuickOpenLineEdit::hideEvent(QHideEvent* ev) { QWidget::hideEvent(ev); if(m_widget) QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection); // deactivate(); } bool QuickOpenLineEdit::eventFilter(QObject* obj, QEvent* e) { if (!m_widget) return false; switch (e->type()) { case QEvent::KeyPress: case QEvent::ShortcutOverride: if (static_cast(e)->key() == Qt::Key_Escape) { deactivate(); e->accept(); return true; } break; case QEvent::WindowActivate: case QEvent::WindowDeactivate: kDebug() << "closing because of window activation"; deactivate(); break; case QEvent::FocusIn: if (dynamic_cast(obj)) { QFocusEvent* focusEvent = dynamic_cast(e); Q_ASSERT(focusEvent); //Eat the focus event, keep the focus kDebug() << "focus change" << "inside this: " << insideThis(obj) << "this" << this << "obj" << obj; if(obj == this) return false; kDebug() << "reason" << focusEvent->reason(); if (focusEvent->reason() != Qt::MouseFocusReason && focusEvent->reason() != Qt::ActiveWindowFocusReason) { QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection); return false; } if (!insideThis(obj)) deactivate(); } break; default: break; } return false; } void QuickOpenLineEdit::activate() { kDebug() << "activating"; setText(""); setStyleSheet(""); qApp->installEventFilter(this); } void QuickOpenLineEdit::deactivate() { kDebug() << "deactivating"; clear(); if(m_widget || hasFocus()) QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection); if (m_widget) m_widget->deleteLater(); m_widget = 0; qApp->removeEventFilter(this); } void QuickOpenLineEdit::checkFocus() { kDebug() << "checking focus" << m_widget; if(m_widget) { if(isVisible() && !isHidden()) setFocus(); else deactivate(); }else{ if (ICore::self()->documentController()->activeDocument()) ICore::self()->documentController()->activateDocument(ICore::self()->documentController()->activeDocument()); //Make sure the focus is somewehre else, even if there is no active document setEnabled(false); setEnabled(true); } } IQuickOpenLine* QuickOpenPlugin::createQuickOpenLine(const QStringList& scopes, const QStringList& type, IQuickOpen::QuickOpenType kind) { if(kind == Outline) return new QuickOpenLineEdit(new OutlineQuickopenWidgetCreator(scopes, type)); else return new QuickOpenLineEdit(new StandardQuickOpenWidgetCreator(scopes, type)); } #include "quickopenplugin.moc" // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/shell/kross/xmltokross/duchainreader.cpp b/shell/kross/xmltokross/duchainreader.cpp index 860180c4c..3c4ffec9a 100644 --- a/shell/kross/xmltokross/duchainreader.cpp +++ b/shell/kross/xmltokross/duchainreader.cpp @@ -1,277 +1,277 @@ /*************************************************************************** * Copyright 2008 Aleix Pol * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "duchainreader.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; QList extractTypes(const DUContext* ctx, int ind=1) { QList ret; qDebug() << qPrintable(QString(ind*3, '-')) << "ctx" << ctx << ctx->childContexts(); foreach(const Declaration* decl, ctx->localDeclarations()) { // qDebug() << qPrintable(QString(ind*3, '-')) << "decl" << decl->toString() // << (decl->kind()==Declaration::Type); if(dynamic_cast(decl) && !decl->identifier().toString().isEmpty()) { ret += decl; } } qDebug() << qPrintable(QString(ind*3, '+')); foreach(const DUContext* ct, ctx->childContexts()) ret += extractTypes(ct, ind+1); return ret; } QString DUChainReader::printType(const TypePtr& type) { QString ret; if(type.cast()) { TypePtr del=type.cast(); ret=del->identifier().toString(); } else ret=type->toString(); // qDebug() << "yyyyyyyyyy" << type->toString() << ret; return ret; } bool isTemplateDeclaration(const KDevelop::Declaration* decl) { KDevelop::DUContext* current = decl->internalContext(); while(current) { if(current->type() == KDevelop::DUContext::Template) return true; if(current->importedParentContexts().isEmpty()) return false; current = current->importedParentContexts()[0].context(decl->topContext()); } return false; } void DUChainReader::foundClass(const Declaration* decl) { QString baseClass; const ClassDeclaration *cdecl=dynamic_cast(decl); // qDebug() << "lalalalalalalala" << cdecl->baseClassesSize() // << cdecl->baseClasses()[0].access << cdecl->baseClasses()[0].baseClass.type()->toString() // << definedClasses; if(cdecl->baseClassesSize()>=1 && cdecl->baseClasses()[0].access==KDevelop::Declaration::Public && definedClasses.contains(cdecl->baseClasses()[0].baseClass.abstractType()->toString())) baseClass=cdecl->baseClasses()[0].baseClass.abstractType()->toString(); QString inClass; bool isInClass=decl->context()->type() == DUContext::Class; inNamespace=decl->context()->scopeIdentifier(true).toString(); if(isInClass) { //Only 1 level int lastColons = inNamespace.lastIndexOf("::"), nssize=inNamespace.size(); inClass+=inNamespace.right(nssize-lastColons-2)+"::"; inNamespace.resize(lastColons); } QList enums; foreach(const Declaration* declEnum, decl->internalContext()->localDeclarations()) { if(declEnum->kind()==Declaration::Type && declEnum->internalContext()) { const AbstractType::Ptr t = declEnum->abstractType(); const EnumerationType* en = dynamic_cast(t.unsafeData()); if(en) { QStringList anEnum; if(!declEnum->identifier().identifier().isEmpty()) { anEnum+=en->qualifiedIdentifier().toString(); foreach(const Declaration* declFlag, declEnum->internalContext()->localDeclarations()) { const AbstractType::Ptr t = declFlag->abstractType(); const EnumeratorType* enor = dynamic_cast(t.unsafeData()); // Q_ASSERT(enor); if(!enor) { qDebug() << "found a null enumerator"; continue; } anEnum += enor->qualifiedIdentifier().toString(); } enums += anEnum; } } } } sonsPerClass[baseClass].append(cdecl->abstractType()->toString()); definedClasses.append(cdecl->abstractType()->toString()); writeClass(cdecl->abstractType()->toString(), baseClass, enums); //Looking for methods DUContext* ctx=decl->internalContext(); QSet forbidden; forbidden.insert("metaObject"); forbidden.insert("tr"); forbidden.insert("trUtf8"); forbidden.insert("operator{...cast...}"); forbidden.insert("operator<<"); foreach(const Declaration* func, ctx->localDeclarations()) { const ClassMemberDeclaration* memberDecl=dynamic_cast(func); // if(memberDecl) qDebug() << "bibibibibibi" << memberDecl->toString() // << memberDecl << memberDecl->accessPolicy() << (memberDecl->accessPolicy()==Declaration::Public); if(!memberDecl || memberDecl->accessPolicy()!=Declaration::Public) continue; // qDebug() << "++++++++++" << func->toString(); if(func->isFunctionDeclaration()) { QualifiedIdentifier qid=func->qualifiedIdentifier(); const AbstractType::Ptr atype=func->abstractType(); const FunctionType::Ptr ftype=atype.cast(); const ClassFunctionDeclaration* cdec=dynamic_cast(func); const AbstractFunctionDeclaration* dec=dynamic_cast(func); Q_ASSERT(dec && cdec && func && memberDecl); if(memberDecl->accessPolicy()!=Declaration::Public) continue; QString funcname=func->identifier().toString(); if(funcname.startsWith("qt_") || funcname.startsWith('~') || forbidden.contains(funcname)) continue; bool isConstructor=ftype->returnType().isNull(); QString rettype= isConstructor ? QString() : printType(ftype->returnType()); if(isTemplateDeclaration(func)) //we disable templated functions continue; bool isConst=ftype->modifiers() & AbstractType::ConstModifier; bool isVirtual=dec->isVirtual(); bool isAbstract=cdec->isAbstract(); const IndexedString* idx=dec->defaultParameters(); int notDefCount = ftype->arguments().count()-dec->defaultParametersSize(); int i=0; qDebug() << "foundfunc" << func->internalContext()->localDeclarations().size() << funcname << "isConst " << isConst << "isVirtual" << isVirtual; method currentMethod; currentMethod.funcname=funcname; currentMethod.returnType=rettype; currentMethod.isConst=isConst; currentMethod.isVirtual=isVirtual; currentMethod.isAbstract=isAbstract; currentMethod.isConstructor=isConstructor; foreach(const TypePtr& argtype, ftype->arguments()) { notDefCount--; method::argument arg; arg.type=printType(argtype); arg.name=QString("x%1").arg(i++); // qDebug() << "arg" << notDefCount << arg.name; if(notDefCount<0) { - arg.def=idx->str(); + arg.def=idx->toString(); QString scopeClass=decl->internalContext()->scopeIdentifier(true).toString(); QString scope=decl->internalContext()->scopeIdentifier().toString(); qDebug () << "default value" << arg.type << arg.def << scope << (!arg.def.startsWith(scope)) << (arg.type.startsWith(scope)) << dynamic_cast(argtype.unsafeData()); int end = arg.def.indexOf('('); if(end == -1) end = arg.def.length(); QString constr=arg.def.left(end); QList decls = ctx->findDeclarations(QualifiedIdentifier(constr)); if(!decls.isEmpty()) arg.def = decls.first()->qualifiedIdentifier().toString() + arg.def.mid(end); // if(dynamic_cast(argtype.unsafeData()) && !scopeClass.isEmpty() && // !arg.def.startsWith(scopeClass) && arg.type.startsWith(scopeClass)) // arg.def.prepend(scopeClass+"::"); // else if(arg.type.endsWith("::"+constr+'&') || arg.type.endsWith("::"+constr)) // arg.def.prepend(scope+"::"); idx++; } currentMethod.args += arg; } writeEndFunction(currentMethod); } else if(dynamic_cast(func) && func->kind() == Declaration::Instance) { const AbstractType::Ptr atype=func->abstractType(); const ClassMemberDeclaration* member=dynamic_cast(func); qDebug() << "member var" << member->toString() << "name" << member->identifier().toString() << "type" << member->abstractType()->toString(); bool isReadOnly=atype->modifiers() & AbstractType::ConstModifier; QString type=member->abstractType()->toString(); if(type.contains("const ")) { type=type.remove("const "); isReadOnly=true; } writeVariable(member->identifier().toString(), type, isReadOnly); } } writeEndClass(); } int DUChainReader::start() { inclass=0; QList decls=extractTypes(m_top); writeDocument(); foreach(const Declaration* decl, decls) { if(!decl->identifier().toString().startsWith("QMetaTypeId")) foundClass(decl); } writeEndDocument(); return 0; }