...
 
Commits (19)
  • Friedrich W. H. Kossebau's avatar
    b4cf84ef
  • Script Kiddy's avatar
    GIT_SILENT made messages (after extraction) · 566b98a9
    Script Kiddy authored
    566b98a9
  • Christian Tacke's avatar
    DVCS Branch Manager with filtering and sorting proposal · 8f77f44b
    Christian Tacke authored
    Summary:
    The porposal is to utilize QSortFilterProxyModel for sorting and filtering.
    
    When working with on a bigger project with multiple branches some means to find a desired branch for switching or creating a new branch may be considered helpful.
    So I added a line edit and a QSortFilterProxyModel to do that for me.
    
    Additionally: new branches are currently appended. Though this makes them easy to find, I think it's counter intuitive and a tad ugly. While the sort mechanism of the Proxy does not help initially, it does upon adding branches.
    
    Drawback: the line edit for filtering may be associated with the "New" button by users. This could make the workflow for that less intuitive.
    
    Test Plan:
    Fire up Git->Branches on a git project.
    
    Make sure all branches are there.
    Type in the line edit to filter. Only branches that match should be included.
    Clearing the line edit should result in all elements to become visible again.
    Create New branch, that should be added to list in alphabetical order.
    
    Reviewers: apol
    
    Reviewed By: apol
    
    Subscribers: kfunk, apol, kdevelop-devel
    
    Tags: #vdg, #kdevelop
    
    Differential Revision: https://phabricator.kde.org/D20142
    8f77f44b
  • Milian Wolff's avatar
    Rename c++1z to C++17 · 742cef92
    Milian Wolff authored
    742cef92
  • Milian Wolff's avatar
  • Kevin Funk's avatar
    Merge remote-tracking branch 'origin/5.3' · ab962476
    Kevin Funk authored
    ab962476
  • Script Kiddy's avatar
    GIT_SILENT made messages (after extraction) · e3ea1a25
    Script Kiddy authored
    e3ea1a25
  • Script Kiddy's avatar
    GIT_SILENT made messages (after extraction) · 76b2df37
    Script Kiddy authored
    76b2df37
  • Kai Uwe Broulik's avatar
    Add DesktopEntry to notifyrc · e9c489eb
    Kai Uwe Broulik authored
    Summary: This allows the notification KCM to identify KDevelop as an application.
    
    Test Plan:
    {F6801449}
    This can probably go to stable since older Plasma/Frameworks will just ignore that field
    
    Reviewers: #kdevelop, kfunk
    
    Reviewed By: #kdevelop, kfunk
    
    Subscribers: kfunk, kdevelop-devel
    
    Tags: #kdevelop
    
    Differential Revision: https://phabricator.kde.org/D20920
    e9c489eb
  • Friedrich W. H. Kossebau's avatar
    Merge branch '5.3' · 84bd62b6
    Friedrich W. H. Kossebau authored
    84bd62b6
  • Thomas Schöps's avatar
    4c1a5182
  • Script Kiddy's avatar
    SVN_SILENT made messages (.desktop file) - always resolve ours · 3d8fa9b4
    Script Kiddy authored
    In case of conflict in i18n, keep the version of the branch "ours"
    To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
    3d8fa9b4
  • Amish Naidu's avatar
    clang: Use DocumentChangeSet for code completion in some places · 34c34c77
    Amish Naidu authored
    Using DocumentChangeSet enables auto formatting.
    34c34c77
  • Amish Naidu's avatar
    clang: Fix completion tests by using documentController to create view · 6dca2d31
    Amish Naidu authored
    Implement/override completion tests now use DocumentChangeSet which
    relies on CodeRepresentation to make changes, if we don't use a view
    through the global document controller, a disk file code representation
    is created which doesn't update the kate view.
    6dca2d31
  • Gleb Popov's avatar
    Fix deadlock exception in FileManagerListJob. · 73fb3cd6
    Gleb Popov authored
    It was visible on FreeBSD with following backtrace:
    
        frame #0: 0x0000000802b8b370 libcxxrt.so.1`::__cxa_throw(void *, std::type_info *, void (*)(void *)) at exception.cc:790
        frame #1: 0x0000000802b59c8c libc++.so.1`std::__1::__throw_system_error at system_error.cpp:289
        frame #2: 0x0000000802b4a9cd libc++.so.1`std::__1::mutex::lock at mutex.cpp:34
        frame #3: 0x00000008017b3f9e libKDevPlatformProject.so.54`KDevelop::FileManagerListJob::startNextJob at filemanagerlistjob.cpp:114
        frame #4: 0x00000008017b5b2e libKDevPlatformProject.so.54`_ZN9QtPrivate11FunctorCallINS_11IndexesListIJEEENS_4ListIJEEEvMN8KDevelop18FileManagerListJobEFvvEE4callES8_PS6_PPv at qobjectdefs_impl.h:152
        frame #5: 0x00000008017b5aa3 libKDevPlatformProject.so.54`_ZN9QtPrivate15FunctionPointerIMN8KDevelop18FileManagerListJobEFvvEE4callINS_4ListIJEEEvEEvS4_PS2_PPv at qobjectdefs_impl.h:185
        frame #6: 0x00000008017b59c6 libKDevPlatformProject.so.54`_ZN9QtPrivate11QSlotObjectIMN8KDevelop18FileManagerListJobEFvvENS_4ListIJEEEvE4implEiPNS_15QSlotObjectBaseEP7QObjectPPvPb at qobjectdefs_impl.h:414
        frame #7: 0x0000000803e2caa6 libQt5Core.so.5`QtPrivate::QSlotObjectBase::call at qobjectdefs_impl.h:394
        frame #8: 0x0000000803e66648 libQt5Core.so.5`QMetaCallEvent::placeMetaCall at qobject.cpp:518
        frame #9: 0x0000000803e68249 libQt5Core.so.5`QObject::event at qobject.cpp:1260
        frame #10: 0x0000000802e819e1 libQt5Widgets.so.5`QApplicationPrivate::notify_helper + 273
        frame #11: 0x0000000802e82f28 libQt5Widgets.so.5`QApplication::notify + 632
        frame #12: 0x0000000803e1640e libQt5Core.so.5`QCoreApplication::notifyInternal2 at qcoreapplication.cpp:1060
        frame #13: 0x0000000803e17048 libQt5Core.so.5`QCoreApplication::sendEvent at qcoreapplication.cpp:1450
        frame #14: 0x0000000803e17cce libQt5Core.so.5`QCoreApplicationPrivate::sendPostedEvents at qcoreapplication.cpp:1799
        frame #15: 0x0000000803e16f4f libQt5Core.so.5`QCoreApplication::sendPostedEvents at qcoreapplication.cpp:1653
        frame #16: 0x0000000803ec630e libQt5Core.so.5`postEventSourceDispatch at qeventdispatcher_glib.cpp:276
        frame #17: 0x0000000805b52b77 libglib-2.0.so.0`g_main_context_dispatch + 311
        frame #18: 0x0000000805b52f03 libglib-2.0.so.0`___lldb_unnamed_symbol117$$libglib-2.0.so.0 + 515
        frame #19: 0x0000000805b52fb4 libglib-2.0.so.0`g_main_context_iteration + 100
        frame #20: 0x0000000803ec5485 libQt5Core.so.5`QEventDispatcherGlib::processEvents at qeventdispatcher_glib.cpp:422
        frame #21: 0x0000000803e112f9 libQt5Core.so.5`QEventLoop::processEvents at qeventloop.cpp:138
        frame #22: 0x0000000803e1154f libQt5Core.so.5`QEventLoop::exec at qeventloop.cpp:225
        frame #23: 0x000000080027e464 libKDevPlatformTests.so.54`KDevelop::KDevSignalSpy::wait at kdevsignalspy.cpp:48
        frame #24: 0x00000000002114ad test_definesandincludes`(anonymous namespace)::loadProject at projectsgenerator.cpp:47
        frame #25: 0x0000000000211047 test_definesandincludes`ProjectsGenerator::GenerateSimpleProject at projectsgenerator.cpp:105
        frame #26: 0x0000000000208914 test_definesandincludes`TestDefinesAndIncludes::loadSimpleProject at test_definesandincludes.cpp:56
        frame #27: 0x00000000002083d9 test_definesandincludes`TestDefinesAndIncludes::qt_static_metacall at moc_test_definesandincludes.cpp:96
        frame #28: 0x0000000803e25ed9 libQt5Core.so.5`QMetaMethod::invoke at qmetaobject.cpp:2295
        frame #29: 0x00000008002c31f9 libQt5Test.so.5`___lldb_unnamed_symbol8$$libQt5Test.so.5 + 825
        frame #30: 0x00000008002c3bef libQt5Test.so.5`___lldb_unnamed_symbol9$$libQt5Test.so.5 + 815
        frame #31: 0x00000008002c4989 libQt5Test.so.5`___lldb_unnamed_symbol12$$libQt5Test.so.5 + 745
        frame #32: 0x00000008002c517c libQt5Test.so.5`QTest::qRun + 156
        frame #33: 0x00000008002c4ef4 libQt5Test.so.5`QTest::qExec + 20
        frame #34: 0x000000000020a562 test_definesandincludes`main at test_definesandincludes.cpp:152
        frame #35: 0x000000000020811b test_definesandincludes`_start at crt1.c:76
    73fb3cd6
  • Daniel Mensinger's avatar
    meson: implemented project reloding · 0596b384
    Daniel Mensinger authored
    0596b384
  • Daniel Mensinger's avatar
    meson: Only configure if necessary · b306b0f7
    Daniel Mensinger authored
    b306b0f7
  • Daniel Mensinger's avatar
    f6b9c447
  • Morten Volden's avatar
    revert commit 75af1881. Besides having some... · 610c3094
    Morten Volden authored
    revert commit 75af1881. Besides having some bad sideeffects on the unittest of path, it is also not the way to remove asserts on Windows when compiled in debug mode. In order to fix that we need to do something in Documentcontroller
    610c3094
[Global]
IconName=kdevelop
DesktopEntry=org.kde.kdevelop
Comment=KDevelop
Comment[ar]=مطوّرك
Comment[br]=KDevelop
......
......@@ -31,7 +31,7 @@ KF5_VERSION=v5.51.0
KDE_PLASMA_VERSION=v5.13.4 # note: need libksysguard commit a0e69617442d720c76da5ebe3323e7a977929db4 (patch which makes plasma dep optional)
KDE_APPLICATION_VERSION=v18.12.1
GRANTLEE_VERSION=v5.1.0
OKTETA_VERSION=v0.25.5
OKTETA_VERSION=v0.26.0
export LLVM_ROOT=/opt/llvm/
export PATH=/opt/rh/python27/root/usr/bin/:$PATH
......
......@@ -67,7 +67,7 @@ FileManagerListJob::FileManagerListJob(ProjectFolderItem* item)
FileManagerListJob::~FileManagerListJob()
{
// lock and abort to ensure our background list job is stopped
std::lock_guard<std::mutex> lock(m_listing);
std::lock_guard<std::recursive_mutex> lock(m_listing);
m_aborted = true;
}
......@@ -113,7 +113,7 @@ void FileManagerListJob::startNextJob()
// start locking to ensure we don't get destroyed while waiting for the list to finish
m_listing.lock();
QtConcurrent::run([this] (const Path& path) {
std::lock_guard<std::mutex> lock(m_listing, std::adopt_lock);
std::lock_guard<std::recursive_mutex> lock(m_listing, std::adopt_lock);
if (m_aborted) {
return;
}
......
......@@ -70,7 +70,7 @@ private:
KIO::UDSEntryList entryList;
// kill does not delete the job instantaneously
QAtomicInt m_aborted;
std::mutex m_listing;
std::recursive_mutex m_listing;
#ifdef TIME_IMPORT_JOB
QElapsedTimer m_timer;
......
......@@ -138,7 +138,7 @@ static QString generatePathOrUrl(bool onlyPath, bool isLocalFile, const QVector<
// more or less a copy of QtPrivate::QStringList_join
const int size = data.size();
if (size == 0 || data.at(0).isEmpty()) {
if (size == 0) {
return QString();
}
......
......@@ -38,6 +38,7 @@
#include <QDialogButtonBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <QSortFilterProxyModel>
#include <KParts/MainWindow>
using namespace KDevelop;
......@@ -65,7 +66,20 @@ BranchManager::BranchManager(const QString& repository, KDevelop::DistributedVer
m_model = new BranchesListModel(this);
m_model->initialize(m_dvcPlugin, QUrl::fromLocalFile(repository));
m_ui->branchView->setModel(m_model);
// Filter Model
m_filterModel = new QSortFilterProxyModel();
m_filterModel->setSourceModel(m_model);
m_filterModel->setFilterWildcard(QString());
m_filterModel->sort(0, Qt::AscendingOrder);
//Changes in filter edit trigger filtering
connect(m_ui->branchFilterEdit,
&QLineEdit::textChanged,
m_filterModel,
&QSortFilterProxyModel::setFilterWildcard);
m_ui->branchView->setModel(m_filterModel);
QString branchName = m_model->currentBranch();
// apply initial selection
......
......@@ -27,6 +27,8 @@ class KJob;
namespace Ui { class BranchDialogBase; }
class QSortFilterProxyModel;
namespace KDevelop
{
class BranchesListModel;
......@@ -58,6 +60,7 @@ private:
Ui::BranchDialogBase* m_ui;
KDevelop::BranchesListModel* m_model;
QSortFilterProxyModel* m_filterModel;
};
#endif
......@@ -23,7 +23,7 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="1">
<item row="0" column="2">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="newButton">
......@@ -144,12 +144,26 @@
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QListView" name="branchView">
<property name="editTriggers">
<set>QAbstractItemView::EditKeyPressed</set>
</property>
</widget>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0">
<item>
<widget class="QLineEdit" name="branchFilterEdit">
<property name="text">
<string/>
</property>
<property name="placeholderText">
<string>Search...</string>
</property>
</widget>
</item>
<item>
<widget class="QListView" name="branchView">
<property name="editTriggers">
<set>QAbstractItemView::EditKeyPressed</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
......
......@@ -43,6 +43,7 @@
<summary xml:lang="gl">Potente ambiente de desenvolvemento integrado para C, C++ e outras linguaxes de programación. As súas funcionalidades poden estenderse mediante complementos</summary>
<summary xml:lang="id">Penuh fitur, IDE plugin yang dapat diekstensi C/C++ dan bahasa pemrograman lain</summary>
<summary xml:lang="it">IDE per C/C++ e altri linguaggi di programmazione completo ed estensibile</summary>
<summary xml:lang="ko">C/C++ 및 기타 프로그래밍 언어를 위한 기능적이고 확장 가능한 IDE</summary>
<summary xml:lang="nl">IDE voor C/C++ en andere programmeertalen, vol functies en uit te breiden met plug-ins.</summary>
<summary xml:lang="pl">Jest to w pełni funkcjonalne, z możliwością rozszerzenia przy użyciu wtyczek, zintegrowane środowisko programistyczne dla C/C++ i innych języków programowania.</summary>
<summary xml:lang="pt">IDE pleno de funcionalidades e modular para o C/C++ e outras linguagens de programação</summary>
......@@ -66,6 +67,7 @@
<p xml:lang="gl">KDevelop é un ambiente integrado de desenvolvemento (IDE) libre e de código aberto.</p>
<p xml:lang="id">KDevelop adalah sebuah IDE (integrated development environment) lingkungan pengembangan terintegrasi yang Gratis dan Bebas Terbuka.</p>
<p xml:lang="it">KDevelop è un ambiente di sviluppo integrato (IDE) libero e open source.</p>
<p xml:lang="ko">KDevelop은 무료 오픈 소스 통합 개발 환경(IDE)입니다.</p>
<p xml:lang="nl">KDevelop is een vrije geïntegreerde ontwikkelomgeving (IDE) binnen het open source principe.</p>
<p xml:lang="pl">KDevelop to darmowe i otwartoźródłowe zintegrowane środkowisko programistyczne (IDE).</p>
<p xml:lang="pt">O KDevelop é um ambiente de desenvolvimento integrado (IDE) livre e em código aberto.</p>
......@@ -88,6 +90,7 @@
<p xml:lang="gl">Fornece funcionalidades de edición, navegación e depuración para varias linguaxes de programación, así como integración con varios sistemas de construción e sistemas de control de versións, usando unha arquitectura baseada en complementos.</p>
<p xml:lang="id">Ini menyediakan fitur pengeditan, navigasi dan debugging untuk beberapa bahasa pemrograman, serta integrasi dengan sistem build multipel dan sistem kontrol versi, menggunakan arsitektur berbasis plugin.</p>
<p xml:lang="it">Fornisce funzionalità di modifica, navigazione debug per vari linguaggi di programmazione, così come l'integrazione con vari sistemi di compilazione e di controllo versione, tramite l'uso di architettura basata sulle estensioni.</p>
<p xml:lang="ko">또한 플러그인 기반 아키텍처를 사용하여 여러 프로그래밍 언어에 대한 편집, 탐색 및 디버깅 기능을 제공하고 여러 빌드 시스템 및 버전 제어 시스템과의 통합도 제공합니다.</p>
<p xml:lang="nl">Het biedt functies voor bewerking, navigatie en debugging voor verschillende programmeertalen, evenals integratie met meerdere bouwsystemen en versiecontrole systemen, met gebruik van een op plug-ins gebaseerde architectuur.</p>
<p xml:lang="pl">Zapewnia możliwość edycji, poruszania się po projekcie i diagnozowania błędów dla kilku języków programistycznych, a także integrację z wieloma systemami budowania i zarządzania wersjami poprzez wbudowaną architekturę wtyczek.</p>
<p xml:lang="pt">Oferece capacidades de edição, navegação e depuração para diversas linguagens de programação, assim como a integração com diversos sistemas de compilação e de controlo de versões, usando uma arquitectura baseada em 'plugins'.</p>
......@@ -110,6 +113,7 @@
<p xml:lang="gl">KDevelop ten infraestruturas de análise para C, C++ e JavaScript/QML, con complementos externos adicionais que fornecen compatibilidade, por exemplo, con PHP e Python.</p>
<p xml:lang="id">KDevelop memiliki pengurai backend untuk C, C++ dan Javascript/QML, dengan mendukung plugin eksternal lebih lanjut misal PHP atau Python.</p>
<p xml:lang="it">KDevelop ha dei motori di analisi per C, C++ e JavaScript/QML, come ulteriori estensioni esterne che supportano, ad esempio, PHP o Python.</p>
<p xml:lang="ko">KDevelop에는 C, C++ 및 Javascript/QML에 대한 파서 백엔드가 있으며, PHP 또는 Python과 같은 추가 외부 플러그인이 지원됩니다.</p>
<p xml:lang="nl">KDevelop heef backends voor ontleden van C, C++ en Javascript/QML, met verdere externe plug-ins die bijv. PHP of Python ondersteunen.</p>
<p xml:lang="pl">KDevelop ma silniki do przetwarzania składni C, C++ oraz Javascript/QML, a także inne jak np. PHP lub Python dodawane przy użyciu zewnętrznych wtyczek.</p>
<p xml:lang="pt">O KDevelop tem infra-estruturas de processamento para C, C++ e Javascript/QML, com mais 'plugins' externos que suportam p.ex. PHP ou Python.</p>
......
......@@ -46,6 +46,7 @@
#include <language/duchain/stringhelpers.h>
#include <language/codecompletion/codecompletionmodel.h>
#include <language/codecompletion/normaldeclarationcompletionitem.h>
#include <language/codegen/documentchangeset.h>
#include <util/foregroundlock.h>
#include <custom-definesandincludes/idefinesandincludesmanager.h>
#include <project/projectmodel.h>
......@@ -197,7 +198,14 @@ public:
replacement.append(QLatin1Char(';'));
}
view->document()->replaceText(word, replacement);
DocumentChange overrideChange(IndexedString(view->document()->url()),
word,
QString{},
replacement);
overrideChange.m_ignoreOldText = true;
DocumentChangeSet changes;
changes.addChange(overrideChange);
changes.applyAllChanges();
}
private:
......@@ -367,6 +375,9 @@ public:
{
auto* const document = view->document();
DocumentChangeSet changes;
KTextEditor::Cursor rangeStart = word.start();
// try and replace leading typed text that match the proposed implementation
const QString leading = document->line(word.end().line()).left(word.end().column());
const QString leadingNoSpace = removeWhitespace(leading);
......@@ -374,14 +385,24 @@ public:
|| removeWhitespace(m_replacement).startsWith(leadingNoSpace))) {
const int removeSize = leading.end() - std::find_if_not(leading.begin(), leading.end(),
[](QChar c){ return c.isSpace(); });
const KTextEditor::Cursor newStart = {word.end().line(), word.end().column() - removeSize};
document->replaceText({newStart, word.end()}, m_replacement);
} else {
document->replaceText(word, m_replacement);
rangeStart = {word.end().line(), word.end().column() - removeSize};
}
// place cursor after the opening brace
view->setCursorPosition(view->cursorPosition() + KTextEditor::Cursor{-2, 1});
DocumentChange change(IndexedString(view->document()->url()),
KTextEditor::Range(rangeStart, word.end()),
QString(),
m_replacement);
change.m_ignoreOldText = true;
changes.addChange(change);
changes.applyAllChanges();
// Place cursor after the opening brace
// arbitrarily chose 4, as it would accomodate the template and return types on their own line
const auto searchRange = KTextEditor::Range(rangeStart, rangeStart.line() + 4, 0);
const auto results = view->document()->searchText(searchRange, QStringLiteral("{"));
if (!results.isEmpty()) {
view->setCursorPosition(results.first().end());
}
}
};
......
......@@ -96,6 +96,28 @@ QDebug operator<<(QDebug debug, const ClangFixit& fixit)
return debug;
}
ClangProblem::ClangProblem() = default;
ClangProblem::ClangProblem(const ClangProblem& other)
: Problem(),
m_fixits(other.m_fixits)
{
setSource(other.source());
setFinalLocation(other.finalLocation());
setFinalLocationMode(other.finalLocationMode());
setDescription(other.description());
setExplanation(other.explanation());
setSeverity(other.severity());
auto diagnostics = other.diagnostics();
for (auto& diagnostic : diagnostics) {
auto* clangDiagnostic = dynamic_cast<ClangProblem*>(diagnostic.data());
Q_ASSERT(clangDiagnostic);
diagnostic = ClangProblem::Ptr(new ClangProblem(*clangDiagnostic));
}
setDiagnostics(diagnostics);
}
ClangProblem::ClangProblem(CXDiagnostic diagnostic, CXTranslationUnit unit)
{
const QString diagnosticOption = ClangString(clang_getDiagnosticOption(diagnostic, nullptr)).toString();
......
......@@ -71,6 +71,16 @@ public:
using Ptr = QExplicitlySharedDataPointer<ClangProblem>;
using ConstPtr = QExplicitlySharedDataPointer<const ClangProblem>;
/**
* Creates an empty ClangProblem.
*/
ClangProblem();
/**
* Creates a deep copy of a ClangProblem.
*/
ClangProblem(const ClangProblem& other);
/**
* Import @p diagnostic into a ClangProblem object
*
......
......@@ -23,7 +23,6 @@
#include "parsesession.h"
#include <QStandardPaths>
#include "clangproblem.h"
#include "clangdiagnosticevaluator.h"
#include "todoextractor.h"
#include "clanghelpers.h"
......@@ -432,6 +431,77 @@ IndexedString ParseSession::languageString()
return lang;
}
ClangProblem::Ptr ParseSession::getOrCreateProblem(int indexInTU, CXDiagnostic diagnostic) const
{
auto& problem = d->m_diagnosticsCache[indexInTU];
if (!problem) {
problem = ClangDiagnosticEvaluator::createProblem(diagnostic, d->m_unit);
}
return problem;
}
ClangProblem::Ptr ParseSession::createExternalProblem(int indexInTU,
CXDiagnostic diagnostic,
const KLocalizedString& descriptionTemplate,
int childProblemFinalLocationIndex) const
{
// Make a copy of the original (cached) problem since it is modified later
auto problem = ClangProblem::Ptr(new ClangProblem(*getOrCreateProblem(indexInTU, diagnostic)));
// Insert a copy of the parent problem (without child problems) as the first
// child problem to preserve its location.
auto* problemCopy = new ClangProblem();
problemCopy->setSource(problem->source());
problemCopy->setFinalLocation(problem->finalLocation());
problemCopy->setFinalLocationMode(problem->finalLocationMode());
problemCopy->setDescription(problem->description());
problemCopy->setExplanation(problem->explanation());
problemCopy->setSeverity(problem->severity());
auto childProblems = problem->diagnostics();
childProblems.prepend(IProblem::Ptr(problemCopy));
problem->setDiagnostics(childProblems);
// Override the problem's finalLocation with that of the child problem in this document.
// This is required to make the problem show up in the problem reporter for this
// file, since it filters by finalLocation. It will also lead the user to the correct
// location when clicking the problem and cause proper error highlighting.
int index = (childProblemFinalLocationIndex >= 0) ?
(1 + childProblemFinalLocationIndex) :
(childProblems.size() - 1);
problem->setFinalLocation(childProblems[index]->finalLocation());
problem->setDescription(descriptionTemplate.subs(problem->description()).toString());
return problem;
}
QList<ClangProblem::Ptr> ParseSession::createRequestedHereProblems(int indexInTU, CXDiagnostic diagnostic, CXFile file) const
{
QList<ClangProblem::Ptr> results;
auto childDiagnostics = clang_getChildDiagnostics(diagnostic);
auto numChildDiagnostics = clang_getNumDiagnosticsInSet(childDiagnostics);
for (uint j = 0; j < numChildDiagnostics; ++j) {
auto childDiagnostic = clang_getDiagnosticInSet(childDiagnostics, j);
CXSourceLocation childLocation = clang_getDiagnosticLocation(childDiagnostic);
CXFile childDiagnosticFile;
clang_getFileLocation(childLocation, &childDiagnosticFile, nullptr, nullptr, nullptr);
if (childDiagnosticFile == file) {
QString description = ClangString(clang_getDiagnosticSpelling(childDiagnostic)).toString();
if (description.endsWith(QLatin1String("requested here"))) {
// Note: Using the index j here assumes a 1:1 mapping from clang child diagnostics to KDevelop
// problem diagnostics (i.e., child problems). If we wanted to avoid making this assumption, we'd have
// to use ClangDiagnosticEvaluator::createProblem() first and then search within its
// child problems to find the correct index.
results << createExternalProblem(indexInTU, diagnostic, ki18n("Requested here: %1"), j);
}
}
}
return results;
}
QList<ProblemPointer> ParseSession::problemsForFile(CXFile file) const
{
if (!d) {
......@@ -451,18 +521,21 @@ QList<ProblemPointer> ParseSession::problemsForFile(CXFile file) const
CXSourceLocation location = clang_getDiagnosticLocation(diagnostic);
CXFile diagnosticFile;
clang_getFileLocation(location, &diagnosticFile, nullptr, nullptr, nullptr);
auto requestedHereProblems = createRequestedHereProblems(i, diagnostic, file);
for (const auto& ptr : requestedHereProblems) {
problems.append(static_cast<const ProblemPointer&>(ptr));
}
// missing-include problems are so severe in clang that we always propagate
// them to this document, to ensure that the user will see the error.
if (diagnosticFile != file && ClangDiagnosticEvaluator::diagnosticType(diagnostic) != ClangDiagnosticEvaluator::IncludeFileNotFoundProblem) {
continue;
}
auto& problem = d->m_diagnosticsCache[i];
if (!problem) {
problem = ClangDiagnosticEvaluator::createProblem(diagnostic, d->m_unit);
}
problems << problem;
problems << ((diagnosticFile == file) ?
getOrCreateProblem(i, diagnostic) :
createExternalProblem(i, diagnostic, ki18n("In included file: %1")));
clang_disposeDiagnostic(diagnostic);
}
......
......@@ -36,7 +36,7 @@
#include <language/interfaces/iastcontainer.h>
#include "clangprivateexport.h"
#include "clangproblem.h"
#include "clangparsingenvironment.h"
#include "unsavedfile.h"
......@@ -80,7 +80,7 @@ private:
/// best would be a PCH, if possible
QTemporaryFile m_definesFile;
// cached ProblemPointer representation for diagnostics
QVector<KDevelop::ProblemPointer> m_diagnosticsCache;
QVector<ClangProblem::Ptr> m_diagnosticsCache;
};
/**
......@@ -133,6 +133,15 @@ public:
private:
Q_DISABLE_COPY(ParseSession)
ClangProblem::Ptr getOrCreateProblem(int indexInTU, CXDiagnostic diagnostic) const;
ClangProblem::Ptr createExternalProblem(int indexInTU,
CXDiagnostic diagnostic,
const KLocalizedString& descriptionTemplate,
int childProblemFinalLocationIndex = -1) const;
QList<ClangProblem::Ptr> createRequestedHereProblems(int indexInTU, CXDiagnostic diagnostic, CXFile file) const;
ParseSessionData::Ptr d;
};
......
......@@ -41,21 +41,20 @@
void DeleteDocument::operator()(KTextEditor::View* view) const
{
delete view->document();
// explicitly close the document when all references are valid, otherwise we have problems during cleanup
const auto url = view->document()->url();
ICore::self()->documentController()->documentForUrl(url)->close(IDocument::Discard);
}
std::unique_ptr<KTextEditor::View, DeleteDocument> CodeCompletionTestBase::createView(const QUrl& url, QObject* parent) const
std::unique_ptr<KTextEditor::View, DeleteDocument> CodeCompletionTestBase::createView(const QUrl& url) const
{
KTextEditor::Editor* editor = KTextEditor::Editor::instance();
Q_ASSERT(editor);
auto doc = editor->createDocument(parent);
auto doc = KDevelop::ICore::self()
->documentController()
->openDocument(url, KTextEditor::Range::invalid(), KDevelop::IDocumentController::DoNotActivate)
->textDocument();
Q_ASSERT(doc);
bool opened = doc->openUrl(url);
Q_ASSERT(opened);
Q_UNUSED(opened);
auto view = doc->createView(nullptr);
auto view = doc->views().first();
Q_ASSERT(view);
return std::unique_ptr<KTextEditor::View, DeleteDocument>(view);
}
......
......@@ -46,7 +46,7 @@ class CodeCompletionTestBase : public QObject
Q_OBJECT
public:
std::unique_ptr<KTextEditor::View, DeleteDocument> createView(const QUrl& url, QObject* parent) const;
std::unique_ptr<KTextEditor::View, DeleteDocument> createView(const QUrl& url) const;
private Q_SLOTS:
void initTestCase();
......
......@@ -182,6 +182,7 @@ void executeCompletionTest(const ReferencedTopDUContext& top, const CompletionIt
}
QCOMPARE(tester.names, expectedCompletionItems.completions);
lock.unlock(); // customTestFunction should lock appropriately
customTestFunction(tester);
}
......@@ -636,7 +637,8 @@ void TestCodeCompletion::testOverrideExecute()
auto executeItem = [=] (const ClangCodeCompletionItemTester& tester) {
auto item = tester.findItem(itemToExecute);
QVERIFY(item);
auto view = createView(tester.completionContext->duContext()->url().toUrl(), this);
auto view = createView(tester.completionContext->duContext()->url().toUrl());
DUChainReadLocker lock;
item->execute(view.get(), view->document()->wordRangeAt(expectedItems.position));
QCOMPARE(view->document()->text(), expectedCode);
};
......@@ -672,7 +674,7 @@ void TestCodeCompletion::testOverrideExecute_data()
<< "class Foo { virtual int bar(char c); };\nclass Baz : Foo\n{\n};"
<< CompletionItems{{3, 0}, {"Baz", "Foo", "bar(char c)"}}
<< "bar(char c)"
<< "-std=c++1z"
<< "-std=c++17"
<< "class Foo { virtual int bar(char c); };\n"
"class Baz : Foo\n{\nint bar(char c) override;};";
}
......@@ -1046,7 +1048,7 @@ void TestCodeCompletion::testIncludePathCompletion()
auto item = tester.findItem(itemId);
QVERIFY(item);
auto view = createView(file.url().toUrl(), this);
auto view = createView(file.url().toUrl());
QVERIFY(view.get());
auto doc = view->document();
item->execute(view.get(), KTextEditor::Range(cursor, cursor));
......@@ -1233,6 +1235,7 @@ void TestCodeCompletion::testArgumentHintCompletion()
QFETCH(HintItemList, hints);
executeCompletionTest(code, expectedItems, NoMacroOrBuiltin, [&](const ClangCodeCompletionItemTester& tester) {
DUChainReadLocker lock;
HintItemList actualHints;
for (const auto& item : tester.items) {
if (item->argumentHintDepth() == 1) {
......@@ -1403,7 +1406,8 @@ void TestCodeCompletion::testCompleteFunction()
auto executeItem = [=] (const ClangCodeCompletionItemTester& tester) {
auto item = tester.findItem(itemToExecute);
QVERIFY(item);
auto view = createView(tester.completionContext->duContext()->url().toUrl(), this);
auto view = createView(tester.completionContext->duContext()->url().toUrl());
DUChainReadLocker lock;
item->execute(view.get(), view->document()->wordRangeAt(expectedItems.position));
QCOMPARE(view->document()->text(), expectedCode);
};
......
......@@ -1902,6 +1902,129 @@ void TestDUChain::testVariadicTemplateArguments()
}
}
void TestDUChain::testProblemRequestedHere()
{
auto headerCode = QStringLiteral(R"(
#pragma once
template <typename T>
T AddObjects(const T& a, const T& b)
{
return a + b;
}
)");
TestFile header(headerCode, QStringLiteral("h"));
QString sourceCode = QStringLiteral(R"(
#include "%1"
struct A {};
int main()
{
A a, b;
AddObjects(a, b);
return 0;
}
)").arg(header.url().str());
TestFile impl(sourceCode, QStringLiteral("cpp"), &header);
QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses));
DUChainReadLocker lock;
auto top = impl.topContext();
QVERIFY(top);
auto* headerCtx = dynamic_cast<TopDUContext*>(top->importedParentContexts().first().context(top));
QVERIFY(headerCtx);
QCOMPARE(headerCtx->url(), header.url());
QCOMPARE(headerCtx->problems().count(), 1);
QCOMPARE(headerCtx->localDeclarations().count(), 1);
// Verify that the problem is reported for the source file.
QCOMPARE(top->problems().count(), 1);
QCOMPARE(top->localDeclarations().count(), 2);
}
void TestDUChain::testProblemRequestedHereSameFile()
{
auto sourceCode = QStringLiteral(R"(
struct A {};
template <typename T>
T AddObjects(const T& a, const T& b)
{
return a + b;
}
int main()
{
A a, b;
AddObjects(a, b);
return 0;
}
)");
TestFile impl(sourceCode, QStringLiteral("cpp"));
QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses));
DUChainReadLocker lock;
auto top = impl.topContext();
QVERIFY(top);
QCOMPARE(top->problems().count(), 2);
}
void TestDUChain::testProblemRequestedHereChain()
{
auto headerCode = QStringLiteral(R"(
#pragma once
template <typename T>
T AddObjects(const T& a, const T& b)
{
return a + b;
}
)");
TestFile header(headerCode, QStringLiteral("h"));
QString sourceCode = QStringLiteral(R"(
#include "%1"
struct A {};
template <typename T>
T AddObjects2(const T& a, const T& b)
{
return AddObjects(a, b);
}
int main()
{
A a, b;
AddObjects2(a, b);
return 0;
}
)").arg(header.url().str());
TestFile impl(sourceCode, QStringLiteral("cpp"), &header);
QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses));
DUChainReadLocker lock;
auto top = impl.topContext();
QVERIFY(top);
auto* headerCtx = dynamic_cast<TopDUContext*>(top->importedParentContexts().first().context(top));
QVERIFY(headerCtx);
QCOMPARE(headerCtx->url(), header.url());
QCOMPARE(headerCtx->problems().count(), 1);
QCOMPARE(headerCtx->localDeclarations().count(), 1);
// Verify that the problem is reported for the source file.
QCOMPARE(top->problems().count(), 2);
QCOMPARE(top->localDeclarations().count(), 3);
}
void TestDUChain::testGccCompatibility()
{
// TODO: make it easier to change the compiler provider for testing purposes
......
......@@ -97,6 +97,9 @@ private Q_SLOTS:
void testTemplateFunctionParameterName();
void testFriendDeclaration();
void testVariadicTemplateArguments();
void testProblemRequestedHere();
void testProblemRequestedHereSameFile();
void testProblemRequestedHereChain();
void testGccCompatibility();
void testQtIntegration();
......
......@@ -78,7 +78,12 @@
</item>
<item>
<property name="text">
<string>c++1z</string>
<string>c++17</string>
</property>
</item>
<item>
<property name="text">
<string>c++2a</string>
</property>
</item>
</widget>
......
......@@ -4,6 +4,7 @@
"Description": "Imports and edits Meson projects",
"Description[ca@valencia]": "Importa i edita projectes del Meson",
"Description[ca]": "Importa i edita projectes del Meson",
"Description[cs]": "Importuje a upravuje vlastní projekty Meson",
"Description[en_GB]": "Imports and edits Meson projects",
"Description[es]": "Importa y edita proyectos Meson",
"Description[fr]": "Importe et édite des projets Meson",
......@@ -21,6 +22,7 @@
"Name": "Meson Project Manager",
"Name[ca@valencia]": "Gestor de projectes Meson",
"Name[ca]": "Gestor de projectes Meson",
"Name[cs]": "Správce projektů Meson",
"Name[en_GB]": "Meson Project Manager",
"Name[es]": "Gestor de proyectos Meson",
"Name[fr]": "Gestionnaire de projet Meson",
......
......@@ -20,22 +20,25 @@
#include "mesonmanager.h"
#include "debug.h"
#include "mesonbuilder.h"
#include "mesonconfig.h"
#include "mesonintrospectjob.h"
#include "mesontargets.h"
#include "debug.h"
#include "settings/mesonconfigpage.h"
#include "settings/mesonnewbuilddir.h"
#include <interfaces/icore.h>
#include <interfaces/iproject.h>
#include <interfaces/iprojectcontroller.h>
#include <interfaces/iruncontroller.h>
#include <interfaces/itestcontroller.h>
#include <project/projectconfigpage.h>
#include <project/projectmodel.h>
#include <util/executecompositejob.h>
#include <KConfigGroup>
#include <KDirWatch>
#include <KLocalizedString>
#include <KPluginFactory>
#include <QFileDialog>
......@@ -109,6 +112,34 @@ ProjectFolderItem* MesonManager::createFolderItem(IProject* project, const Path&
return AbstractFileManagerPlugin::createFolderItem(project, path, parent);
}
bool MesonManager::reload(KDevelop::ProjectFolderItem* item)
{
// "Inspired" by CMakeManager::reload
IProject* project = item->project();
if (!project->isReady()) {
return false;
}
qCDebug(KDEV_Meson) << "reloading meson project" << project->name() << "; Path:" << item->path();
KJob* job = createImportJob(item);
project->setReloadJob(job);
ICore::self()->runController()->registerJob(job);
if (item == project->projectItem()) {
connect(job, &KJob::finished, this, [project](KJob* job) -> void {
if (job->error()) {
return;
}
KDevelop::ICore::self()->projectController()->projectConfigurationChanged(project);
KDevelop::ICore::self()->projectController()->reparseProject(project);
});
}
return true;
}
// ***************************
// * IBuildSystemManager API *
// ***************************
......@@ -160,14 +191,55 @@ QList<ProjectTargetItem*> MesonManager::targets(ProjectFolderItem* item) const
return res;
}
void MesonManager::onMesonInfoChanged(QString path, QString projectName)
{
qCDebug(KDEV_Meson) << "File" << path << "changed --> reparsing project";
IProject* foundProject = ICore::self()->projectController()->findProjectByName(projectName);
if (!foundProject) {
return;
}
KJob* job = createImportJob(foundProject->projectItem());
foundProject->setReloadJob(job);
ICore::self()->runController()->registerJob(job);
connect(job, &KJob::finished, this, [foundProject](KJob* job) -> void {
if (job->error()) {
return;
}
KDevelop::ICore::self()->projectController()->projectConfigurationChanged(foundProject);
KDevelop::ICore::self()->projectController()->reparseProject(foundProject);
});
}
KJob* MesonManager::createImportJob(ProjectFolderItem* item)
{
IProject* project = item->project();
Q_ASSERT(project);
auto buildDir = Meson::currentBuildDir(project);
auto introJob
= new MesonIntrospectJob(project, buildDir, { MesonIntrospectJob::TARGETS, MesonIntrospectJob::TESTS },
MesonIntrospectJob::BUILD_DIR, this);
KDirWatchPtr watcher = m_projectWatchers[project];
if (!watcher) {
// Create a new watcher
watcher = m_projectWatchers[project] = make_shared<KDirWatch>(nullptr);
QString projectName = project->name();
connect(watcher.get(), &KDirWatch::dirty, this, [=](QString p) { onMesonInfoChanged(p, projectName); });
connect(watcher.get(), &KDirWatch::created, this, [=](QString p) { onMesonInfoChanged(p, projectName); });
}
Path watchFile = buildDir.buildDir;
watchFile.addPath(QStringLiteral("meson-info"));
watchFile.addPath(QStringLiteral("meson-info.json"));
if (!watcher->contains(watchFile.path())) {
qCDebug(KDEV_Meson) << "Start watching file" << watchFile;
watcher->addFile(watchFile.path());
}
connect(introJob, &KJob::result, this, [this, introJob, item, project]() {
auto targets = introJob->targets();
auto tests = introJob->tests();
......@@ -175,6 +247,13 @@ KJob* MesonManager::createImportJob(ProjectFolderItem* item)
return;
}
// Remove old test suites before deleting them
if (m_projectTestSuites[project]) {
for (auto i : m_projectTestSuites[project]->testSuites()) {
ICore::self()->testController()->removeTestSuite(i.get());
}
}
m_projectTargets[project] = targets;
m_projectTestSuites[project] = tests;
auto tgtList = targets->targets();
......@@ -190,11 +269,15 @@ KJob* MesonManager::createImportJob(ProjectFolderItem* item)
}
});
const QList<KJob*> jobs = {
builder()->configure(project), // Make sure the project is configured
AbstractFileManagerPlugin::createImportJob(item), // generate the file system listing
introJob // Load targets from the build directory introspection files
};
QList<KJob*> jobs;
// Configure the project if necessary
if (m_builder->evaluateBuildDirectory(buildDir.buildDir, buildDir.mesonBackend) != MesonBuilder::MESON_CONFIGURED) {
jobs << builder()->configure(project);
}
jobs << AbstractFileManagerPlugin::createImportJob(item); // generate the file system listing
jobs << introJob;
Q_ASSERT(!jobs.contains(nullptr));
auto composite = new ExecuteCompositeJob(this, jobs);
......
......@@ -33,8 +33,11 @@ class MesonTarget;
class MesonTargets;
class MesonTargetSources;
class KDirWatch;
using MesonSourcePtr = std::shared_ptr<MesonTargetSources>;
using MesonTargetsPtr = std::shared_ptr<MesonTargets>;
using KDirWatchPtr = std::shared_ptr<KDirWatch>;
class MesonManager : public KDevelop::AbstractFileManagerPlugin, public KDevelop::IBuildSystemManager
{
......@@ -69,6 +72,8 @@ public:
KDevelop::ProjectFolderItem* createFolderItem(KDevelop::IProject* project, const KDevelop::Path& path,
KDevelop::ProjectBaseItem* parent = nullptr) override;
bool reload(KDevelop::ProjectFolderItem* item) override;
// ***********
// * IPlugin *
// ***********
......@@ -108,12 +113,16 @@ public:
}
bool removeFilesFromTargets(const QList<KDevelop::ProjectFileItem*>& /*files*/) override { return false; }
KDevelop::Path compiler(KDevelop::ProjectTargetItem * p) const override;
KDevelop::Path compiler(KDevelop::ProjectTargetItem* p) const override;
private:
void onMesonInfoChanged(QString path, QString projectName);
private:
MesonBuilder* m_builder;
QHash<KDevelop::IProject*, MesonTargetsPtr> m_projectTargets;
QHash<KDevelop::IProject*, MesonTestSuitesPtr> m_projectTestSuites;
QHash<KDevelop::IProject*, KDirWatchPtr> m_projectWatchers;
MesonSourcePtr sourceFromItem(KDevelop::ProjectBaseItem* item) const;
void populateTargets(KDevelop::ProjectFolderItem* item, QVector<MesonTarget*> targets);
......