diff --git a/debugger/breakpoint/breakpoint.h b/debugger/breakpoint/breakpoint.h index bae4b971f..6837ebcbd 100644 --- a/debugger/breakpoint/breakpoint.h +++ b/debugger/breakpoint/breakpoint.h @@ -1,147 +1,147 @@ /* This file is part of the KDE project Copyright (C) 2002 Matthias Hoelzer-Kluepfel Copyright (C) 2002 John Firebaugh Copyright (C) 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, see . */ #ifndef KDEVPLATFORM_BREAKPOINT_H #define KDEVPLATFORM_BREAKPOINT_H -#include +#include #include #include class QVariant; class KConfigGroup; namespace KTextEditor { class MovingCursor; } namespace KDevelop { class BreakpointModel; class KDEVPLATFORMDEBUGGER_EXPORT Breakpoint { public: enum BreakpointKind { CodeBreakpoint = 0, WriteBreakpoint, ReadBreakpoint, AccessBreakpoint, LastBreakpointKind }; enum BreakpointState { NotStartedState, DirtyState, PendingState, CleanState }; ///Custom roles for retrieving data from breakpoint model. enum BreakpointRole{ LocationRole = Qt::UserRole + 1 ///< Retrieves breakpoint's full path unlike Qt::DisplayRole. Note: it's only appliable to LocationColumn. }; Breakpoint(BreakpointModel *model, BreakpointKind kind); Breakpoint(BreakpointModel *model, const KConfigGroup& config); bool setData(int index, const QVariant& value); void setDeleted(); ///Note: to retrieve the full path use LocationRole, Qt::DisplayRole return only a file's name QVariant data(int column, int role) const; void save(KConfigGroup& config); // Moved to BreakpointModel; temporarily left here to ease API transition enum Column { EnableColumn, StateColumn, TypeColumn, LocationColumn, ConditionColumn, HitCountColumn, IgnoreHitsColumn }; void setUrl(const QUrl &url); QUrl url() const; void setLine(int line); int line() const; void setLocation(const QUrl& url, int line); QString location(); BreakpointKind kind() const; void setAddress(const QString& address); QString address() const; int hitCount() const; bool deleted() const; bool enabled() const; void setMovingCursor(KTextEditor::MovingCursor *cursor); KTextEditor::MovingCursor *movingCursor() const; void setIgnoreHits(int c); int ignoreHits() const; void setCondition(const QString &c); QString condition() const; void setExpression(const QString &c); QString expression() const; BreakpointState state() const; QString errorText() const; protected: friend class BreakpointModel; friend class IBreakpointController; /** * Return the model this breakpoint is attached to * * @note This might be null, e.g. after the breakpoint has been marked as deleted */ BreakpointModel *breakpointModel(); BreakpointModel *m_model; bool m_enabled; bool m_deleted; BreakpointState m_state; BreakpointKind m_kind; /* For watchpoints, the address it is set at. */ QString m_address; QUrl m_url; int m_line; QString m_condition; KTextEditor::MovingCursor *m_movingCursor; int m_hitCount; int m_ignoreHits; QString m_expression; QString m_errorText; void reportChange(Column c); }; } #endif diff --git a/debugger/breakpoint/breakpointmodel.h b/debugger/breakpoint/breakpointmodel.h index fd42eee96..162b7e1a9 100644 --- a/debugger/breakpoint/breakpointmodel.h +++ b/debugger/breakpoint/breakpointmodel.h @@ -1,202 +1,202 @@ /* This file is part of the KDE project Copyright (C) 2002 Matthias Hoelzer-Kluepfel Copyright (C) 2002 John Firebaugh Copyright (C) 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, see . */ #ifndef KDEVPLATFORM_BREAKPOINTMODEL_H #define KDEVPLATFORM_BREAKPOINTMODEL_H -#include +#include #include #include "breakpoint.h" class QUrl; namespace KParts { class Part; } namespace KTextEditor { class Cursor; } namespace KDevelop { class IDocument; class Breakpoint; class KDEVPLATFORMDEBUGGER_EXPORT BreakpointModel : public QAbstractTableModel { Q_OBJECT public: enum Column { /** * Whether the breakpoint is active or not (settable by user): value is Qt::Checked * or Qt::Unchecked. */ EnableColumn, /** * Synchronization state of the breakpoint (not settable by user): value is one of the * BreakpointState enum values. */ StateColumn, /** * Kind/type of breakpoint (never changes): value is one of the BreakpointKind * enum values. */ KindColumn, /** * Location of the breakpoint (modifiable by user); value is a string describing the * location; note that the formatting of retrieved data can be affected by a custom * BreakpointRole. */ LocationColumn, /** * Condition for conditional breakpoints (modifiable by user). */ ConditionColumn, /** * The number of times this breakpoint has been hit (cannot be modified by the user). */ HitCountColumn, /** * How many hits of the breakpoint will be ignored before the breakpoint actually stops * the program (can be modified by the user and is updated by the debugger backend). */ IgnoreHitsColumn, NumColumns }; enum ColumnFlag { EnableColumnFlag = 1 << EnableColumn, StateColumnFlag = 1 << StateColumn, KindColumnFlag = 1 << KindColumn, LocationColumnFlag = 1 << LocationColumn, ConditionColumnFlag = 1 << ConditionColumn, HitCountColumnFlag = 1 << HitCountColumn, IgnoreHitsColumnFlag = 1 << IgnoreHitsColumn }; Q_DECLARE_FLAGS(ColumnFlags, ColumnFlag) explicit BreakpointModel(QObject* parent); virtual ~BreakpointModel(); QVariant headerData(int section, Qt::Orientation orientation, int role) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QModelIndex breakpointIndex(Breakpoint *b, int column); bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; ///Note: to retrieve the full path use Breakpoint::LocationRole, Qt::DisplayRole returns only a file's name QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; void toggleBreakpoint(const QUrl &url, const KTextEditor::Cursor& cursor); KDevelop::Breakpoint* addCodeBreakpoint(); KDevelop::Breakpoint* addCodeBreakpoint(const QUrl& location, int line); KDevelop::Breakpoint* addCodeBreakpoint(const QString& expression); KDevelop::Breakpoint* addWatchpoint(); KDevelop::Breakpoint* addWatchpoint(const QString& expression); KDevelop::Breakpoint* addReadWatchpoint(); KDevelop::Breakpoint* addReadWatchpoint(const QString& expression); KDevelop::Breakpoint* addAccessWatchpoint(); KDevelop::Breakpoint* addAccessWatchpoint(const QString& expression); Breakpoint *breakpoint(int row); QList breakpoints() const; Q_SIGNALS: void error(int row, const QString& errorText); void hit(int row); public Q_SLOTS: void save(); void load(); private: enum MarkType { BreakpointMark = KTextEditor::MarkInterface::BreakpointActive, ReachedBreakpointMark = KTextEditor::MarkInterface::BreakpointReached, DisabledBreakpointMark = KTextEditor::MarkInterface::BreakpointDisabled, PendingBreakpointMark = KTextEditor::MarkInterface::markType08, AllBreakpointMarks = BreakpointMark | ReachedBreakpointMark | DisabledBreakpointMark | PendingBreakpointMark }; private Q_SLOTS: void updateMarks(); void slotPartAdded(KParts::Part* part); /** * Called by the TextEditor interface when the marks have changed position * because the user has added or removed source. * In here we figure out if we need to reset the breakpoints due to * these source changes. */ void markChanged(KTextEditor::Document *document, KTextEditor::Mark mark, KTextEditor::MarkInterface::MarkChangeAction action); void textDocumentCreated(KDevelop::IDocument*); void documentSaved(KDevelop::IDocument*); void aboutToDeleteMovingInterfaceContent(KTextEditor::Document *document); void markContextMenuRequested( KTextEditor::Document* document, KTextEditor::Mark mark, const QPoint &pos, bool& handled ); private: static const QPixmap* breakpointPixmap(); static const QPixmap* pendingBreakpointPixmap(); static const QPixmap* reachedBreakpointPixmap(); static const QPixmap* disabledBreakpointPixmap(); private: friend class Breakpoint; friend class IBreakpointController; void updateState(int row, Breakpoint::BreakpointState state); void updateHitCount(int row, int hitCount); void updateErrorText(int row, const QString& errorText); void notifyHit(int row); void registerBreakpoint(Breakpoint* breakpoint); void scheduleSave(); void reportChange(Breakpoint *breakpoint, Breakpoint::Column column); uint breakpointType(Breakpoint *breakpoint); Breakpoint *breakpoint(const QUrl& url, int line); bool m_dirty = false; bool m_dontUpdateMarks; QList m_breakpoints; }; } Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::BreakpointModel::ColumnFlags) #endif diff --git a/debugger/framestack/framestackmodel.h b/debugger/framestack/framestackmodel.h index bf8aaa5cb..f6a184025 100644 --- a/debugger/framestack/framestackmodel.h +++ b/debugger/framestack/framestackmodel.h @@ -1,103 +1,103 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2009 Niko Sams * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_FRAMESTACKMODEL_H #define KDEVPLATFORM_FRAMESTACKMODEL_H -#include +#include #include #include #include namespace KDevelop { class FrameStackModelPrivate; /** FIXME: This class needs rework, since at present it is not true model. Client cannot just obtain frames by grabbing a thread and listing children. It should first setCurrentThread beforehand, and it is the method that will actually fetch threads. Therefore, if this model is submitted to plain QTreeView, it won't work at all. Ideally, this should hold current thread and current frame numbers, and only fetch the list of threads, and list of frames inside thread when asked for by the view. */ class KDEVPLATFORMDEBUGGER_EXPORT FrameStackModel : public IFrameStackModel { Q_OBJECT public: explicit FrameStackModel(IDebugSession* session); ~FrameStackModel() override; struct ThreadItem { int nr; QString name; }; void setThreads(const QList &threads); /** * Update frames for thread @p threadNumber * * @note The currentFrame property will be set to the first frame * containing debug information */ void setFrames(int threadNumber, QList frames); void insertFrames(int threadNumber, const QList &frames); void setHasMoreFrames(int threadNumber, bool hasMoreFrames); FrameItem frame(const QModelIndex &index) override; QList frames(int threadNumber) const; //ItemModel implementation QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex& child) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; void setCurrentThread(int threadNumber) override; void setCurrentThread(const QModelIndex &index) override; int currentThread() const override; QModelIndex currentThreadIndex() const override; int currentFrame() const override; QModelIndex currentFrameIndex() const override; void setCurrentFrame(int frame) override; void fetchMoreFrames() override; void setCrashedThreadIndex(int index); int crashedThreadIndex() const; private Q_SLOTS: void stateChanged(KDevelop::IDebugSession::DebuggerState state); private: QScopedPointer d; void handleEvent(IDebugSession::event_t event) override; }; } #endif diff --git a/debugger/interfaces/ibreakpointcontroller.h b/debugger/interfaces/ibreakpointcontroller.h index 5526276f3..0c0b4b772 100644 --- a/debugger/interfaces/ibreakpointcontroller.h +++ b/debugger/interfaces/ibreakpointcontroller.h @@ -1,104 +1,104 @@ /* This file is part of the KDE project Copyright (C) 2002 Matthias Hoelzer-Kluepfel Copyright (C) 2002 John Firebaugh Copyright (C) 2007 Hamish Rodda Copyright (C) 2009 Niko Sams 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. */ #ifndef KDEVPLATFORM_IBREAKPOINTCONTROLLER_H #define KDEVPLATFORM_IBREAKPOINTCONTROLLER_H -#include -#include -#include +#include +#include +#include #include #include "idebugsession.h" #include "../breakpoint/breakpoint.h" #include "../breakpoint/breakpointmodel.h" namespace KDevelop { class IDebugSession; class KDEVPLATFORMDEBUGGER_EXPORT IBreakpointController : public QObject { Q_OBJECT public: explicit IBreakpointController(IDebugSession* parent); /** * Called just after a breakpoint is added in the given row. * The breakpoint which was previously at the given row and all later breakpoints have * been moved. * * Implementations may implement this function to maintain their internal data structures. * Note, however, that breakpoints are typically still empty (i.e. without a useful location) * when this method is called. */ virtual void breakpointAdded(int row); /** * Implementors must handle changes to the breakpoint model, which are signaled via * this method, by forwarding the changes to the backend debugger. */ virtual void breakpointModelChanged(int row, BreakpointModel::ColumnFlags columns); /** * Called when a breakpoint is about to be deleted from the model. */ virtual void breakpointAboutToBeDeleted(int row); /** * Called when the debugger state changed. * * Note: this method exists to ease the API transition; it should probably go away eventually, * since controller implementations that do want to listen to debugger state changes probably * have better ways to do so. */ virtual void debuggerStateChanged(KDevelop::IDebugSession::DebuggerState); protected: IDebugSession *debugSession() const; BreakpointModel *breakpointModel() const; void updateState(int row, Breakpoint::BreakpointState state); void updateHitCount(int row, int hitCount); void updateErrorText(int row, const QString& errorText); void notifyHit(int row, const QString & msg); // The API below is obsolete and will be removed soon protected: void breakpointStateChanged(Breakpoint* breakpoint); void setHitCount(Breakpoint* breakpoint, int count); void error(Breakpoint* breakpoint, const QString& msg, Breakpoint::Column column); void hit(Breakpoint* breakpoint, const QString& msg = QString()); void sendMaybeAll(); virtual void sendMaybe(Breakpoint *breakpoint) = 0; QMap > m_dirty; QSet m_pending; QMap > m_errors; int m_dontSendChanges; }; } #endif // KDEVPLATFORM_IBREAKPOINTCONTROLLER_H diff --git a/debugger/interfaces/idebugsession.h b/debugger/interfaces/idebugsession.h index 699af7d0c..82cd3e5d5 100644 --- a/debugger/interfaces/idebugsession.h +++ b/debugger/interfaces/idebugsession.h @@ -1,197 +1,197 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2009 Niko Sams * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_IDEBUGSESSION_H #define KDEVPLATFORM_IDEBUGSESSION_H #include -#include +#include #include namespace KDevelop { class IDebugSessionPrivate; class IVariableController; class IBreakpointController; class IFrameStackModel; class Breakpoint; class StackModel; class KDEVPLATFORMDEBUGGER_EXPORT IDebugSession : public QObject { Q_OBJECT Q_ENUMS(DebuggerState) public: IDebugSession(); ~IDebugSession() override; enum DebuggerState { NotStartedState, StartingState, ActiveState, PausedState, StoppingState, StoppedState, EndedState }; enum event_t { program_state_changed = 1, program_exited, debugger_exited, // Emitted when the thread or frame that is selected in UI // changes. thread_or_frame_changed, debugger_busy, debugger_ready, // Raised when debugger believe that program start running. // Can be used to hide current line indicator. // Don't count on this being raise in all cases where // program is running. program_running, // Raise when the debugger is in touch with the program, // and should have access to its debug symbols. The program // is not necessary running yet, or might already exited, // or be otherwise dead. connected_to_program }; public: /** * Current state of the debug session */ virtual DebuggerState state() const = 0; /** * Should return if restart is currently available */ virtual bool restartAvaliable() const = 0; /** * Returns if the debugee is currently running. This includes paused. */ bool isRunning() const; /** * Returns the local Url for a source file used in the current debug session. * * The default implementation just returns the url and is sufficient for * local debuggers. Remote debuggers can implement a path mapping mechanism. */ virtual QPair convertToLocalUrl(const QPair &remoteUrl) const; /** * Returns the remote Url for a source file used in the current debug session. * * The default implementation just returns the url and is sufficient for * local debuggers. Remote debuggers can implement a path mapping mechanism. */ virtual QPair convertToRemoteUrl(const QPair &localUrl) const; /** * @return the breakpoint controller of this session * * @note Implementations must ensure that a breakpoint controller always exists (even if it * is a dummy stub implementation that does nothing), and that it does not change during * the lifetime of a session. */ virtual IBreakpointController* breakpointController() const = 0; /** * @return the variable controller of this session * * @note Implementations must ensure that a variable controller always exists (even if it * is a dummy stub implementation that does nothing), and that it does not change during * the lifetime of a session. */ virtual IVariableController* variableController() const = 0; /** * @return the frame stack model of this session * * @note Implementations must ensure that a frame stack model always exists (even if it * is a dummy stub implementation that does nothing), and that it does not change during * the lifetime of a session. */ virtual IFrameStackModel* frameStackModel() const = 0; public Q_SLOTS: virtual void restartDebugger() = 0; virtual void stopDebugger() = 0; virtual void interruptDebugger() = 0; virtual void run() = 0; virtual void runToCursor() = 0; virtual void jumpToCursor() = 0; virtual void stepOver() = 0; virtual void stepIntoInstruction() = 0; virtual void stepInto() = 0; virtual void stepOverInstruction() = 0; virtual void stepOut() = 0; Q_SIGNALS: void stateChanged(KDevelop::IDebugSession::DebuggerState state); void showStepInSource(const QUrl& file, int line, const QString &addr); void showStepInDisassemble(const QString &addr); void clearExecutionPoint(); void finished(); void raiseFramestackViews(); /** This signal is emitted whenever the given event in a program happens. See DESIGN.txt for expected handled of each event. NOTE: this signal should never be emitted directly. Instead, use raiseEvent. */ void event(IDebugSession::event_t e); public: using QObject::event; // prevent hiding of base method. QUrl currentUrl() const; int currentLine() const; QString currentAddr() const; protected: // Clear the position before running code void clearCurrentPosition(); /// Sets new position and emits showStepInSource or showStepInDisassemble (if source file is unavailable) signal void setCurrentPosition(const QUrl& url, int line, const QString& addr); /** Raises the specified event. Should be used instead of emitting 'event' directly, since this method can perform additional book-keeping for events. FIXME: it might make sense to automatically route events to all debugger components, as opposed to requiring that they connect to any signal. */ virtual void raiseEvent(event_t e); friend class FrameStackModel; private: friend IDebugSessionPrivate; QScopedPointer d; }; } #endif diff --git a/debugger/interfaces/iframestackmodel.h b/debugger/interfaces/iframestackmodel.h index 3c0a3120f..a02b8a435 100644 --- a/debugger/interfaces/iframestackmodel.h +++ b/debugger/interfaces/iframestackmodel.h @@ -1,97 +1,97 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2009 Vladimir Prus * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_IFRAMESTACKMODEL_H #define KDEVPLATFORM_IFRAMESTACKMODEL_H #include "idebugsession.h" #include -#include -#include +#include +#include namespace KDevelop { class IFrameStackModelPrivate; class KDEVPLATFORMDEBUGGER_EXPORT IFrameStackModel : public QAbstractItemModel { Q_OBJECT public: /** Stack frame */ struct FrameItem { int nr; QString name; QUrl file; /* If -1, it means that file is not necessary a source file, but possibly a solib name. */ int line; }; explicit IFrameStackModel(IDebugSession *session); ~IFrameStackModel() override; IDebugSession* session() const; /** Sets the current thread to the specified number, and sets the current frame to 0. Note that nothing prevents us from introducing setCurrentThreadAndFrame, but for all the cases when we switch to a different thread we want frame 0. */ virtual void setCurrentThread(int threadNumber) = 0; virtual void setCurrentThread(const QModelIndex &index) = 0; virtual int currentThread() const = 0; virtual QModelIndex currentThreadIndex() const = 0; /** Return the frame we wish to operate on. This is always in context of the current thread. This may be -1 if no frame is selected. This should only the be the case if the thread has no stack as such -- e.g. because it's running, or because it's exited. */ virtual int currentFrame() const = 0; virtual QModelIndex currentFrameIndex() const = 0; virtual void setCurrentFrame(int frame) = 0; virtual FrameItem frame(const QModelIndex &index) = 0; virtual void fetchThreads() = 0; virtual void fetchFrames(int threadNumber, int from, int to) = 0; virtual void fetchMoreFrames() = 0; Q_SIGNALS: /* FIXME: It might make for a more conscise interface if those two were removed, and the clients react to thread_or_frame_changed event and compare the current thread/frame in the framestack model with the one they are displaying. */ void currentThreadChanged(int thread); void currentFrameChanged(int frame); private: friend class IDebugSession; QScopedPointer d; virtual void handleEvent(IDebugSession::event_t event) = 0; }; } #endif diff --git a/debugger/interfaces/ivariablecontroller.h b/debugger/interfaces/ivariablecontroller.h index a03851573..010f4ab57 100644 --- a/debugger/interfaces/ivariablecontroller.h +++ b/debugger/interfaces/ivariablecontroller.h @@ -1,99 +1,99 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2009 Niko Sams * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_IVARIABLECONTROLLER_H #define KDEVPLATFORM_IVARIABLECONTROLLER_H -#include +#include #include #include "idebugsession.h" namespace KTextEditor { class Document; class Cursor; } namespace KDevelop { class VariableCollection; class Variable; class TreeModel; class TreeItem; class KDEVPLATFORMDEBUGGER_EXPORT IVariableController : public QObject { Q_OBJECT public: explicit IVariableController(IDebugSession* parent); /* Create a variable for the specified expression in the currentl thread and frame. */ virtual Variable* createVariable(TreeModel* model, TreeItem* parent, const QString& expression, const QString& display = {}) = 0; virtual KTextEditor::Range expressionRangeUnderCursor(KTextEditor::Document* doc, const KTextEditor::Cursor& cursor) = 0; virtual void addWatch(Variable* variable) = 0; virtual void addWatchpoint(Variable* variable) = 0; enum UpdateType { UpdateNone = 0x0, UpdateLocals = 0x1, UpdateWatches = 0x2 }; Q_DECLARE_FLAGS(UpdateTypes, UpdateType) void setAutoUpdate(QFlags autoUpdate); QFlags autoUpdate(); protected: /** * Convenience function that returns the VariableCollection **/ VariableCollection *variableCollection(); /** * Convenience function that returns the used DebugSession **/ IDebugSession *session() const; virtual void update() = 0; virtual void handleEvent(IDebugSession::event_t event); friend class IDebugSession; private Q_SLOTS: void stateChanged(KDevelop::IDebugSession::DebuggerState); private: void updateIfFrameOrThreadChanged(); QFlags m_autoUpdate; int m_activeThread; int m_activeFrame; }; } // namespace KDevelop Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::IVariableController::UpdateTypes) #endif diff --git a/debugger/util/treeitem.h b/debugger/util/treeitem.h index 9edeb0be3..57ffef449 100644 --- a/debugger/util/treeitem.h +++ b/debugger/util/treeitem.h @@ -1,126 +1,126 @@ /* * This file is part of KDevelop * * Copyright 2008 Vladimir Prus * * 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. */ #ifndef KDEVPLATFORM_TREEITEM_H #define KDEVPLATFORM_TREEITEM_H -#include -#include +#include +#include #include #include #include namespace KDevelop { class TreeModel; class KDEVPLATFORMDEBUGGER_EXPORT TreeItem: public QObject { Q_OBJECT public: ~TreeItem() override; // FIXME: should be protected public: // Methods that the derived classes should implement /** Fetches more children, and adds them by calling appendChild. The amount of children to fetch is up to the implementation. After fetching, should call setHasMore. */ virtual void fetchMoreChildren()=0; virtual void setColumn(int index, const QVariant& data) { Q_UNUSED(index); Q_UNUSED(data); } void emitAllChildrenFetched(); protected: // Interface for derived classes /** Creates a tree item with the specified data. FIXME: do we actually have to have the model pointer. */ explicit TreeItem(TreeModel* model, TreeItem *parent = nullptr); /** Set the data to be shown for the item itself. */ void setData(const QVector &data); /** Adds a new child and notifies the interested parties. Clears the "hasMore" flag. */ void appendChild(TreeItem *child, bool initial = false); void insertChild(int position, TreeItem *child, bool initial = false); void removeChild(int index); void removeSelf(); void deleteChildren(); /** Report change in data of this item. */ void reportChange(); void reportChange(int column); /** Clears all children. */ void clear(); /** Sets a flag that tells if we have some more children that are not fetched yet. */ void setHasMore(bool more); void setHasMoreInitial(bool more); TreeModel* model() { return model_; } bool isExpanded() const { return expanded_; } Q_SIGNALS: void expanded(); void collapsed(); void allChildrenFetched(); protected: // Backend implementation of Model/View friend class TreeModel; TreeItem *child(int row); int childCount() const; int columnCount() const; virtual QVariant data(int column, int role) const; int row() const; TreeItem *parent(); bool hasMore() const { return more_; } void setExpanded(bool b); virtual void clicked() {} virtual QVariant icon(int column) const; protected: QVector childItems; QVector itemData; TreeItem *parentItem; TreeModel *model_; bool more_; TreeItem *ellipsis_; bool expanded_; }; } #endif diff --git a/debugger/util/treemodel.h b/debugger/util/treemodel.h index 326a8e590..c23c45e3d 100644 --- a/debugger/util/treemodel.h +++ b/debugger/util/treemodel.h @@ -1,90 +1,90 @@ /* * This file is part of KDevelop * * Copyright 2008 Vladimir Prus * * 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. */ #ifndef KDEVPLATFORM_TREEMODEL_H #define KDEVPLATFORM_TREEMODEL_H -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include namespace KDevelop { class TreeItem; class KDEVPLATFORMDEBUGGER_EXPORT TreeModel : public QAbstractItemModel { Q_OBJECT public: explicit TreeModel(const QVector& headers, QObject *parent = nullptr); void setRootItem(TreeItem *item); ~TreeModel() override; void expanded(const QModelIndex &index); void collapsed(const QModelIndex &index); void clicked(const QModelIndex &index); void setEditable(bool); TreeItem* root() const; enum { ItemRole = Qt::UserRole, }; public: // QAbstractItemModel overrides QVariant data(const QModelIndex &index, int role) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; bool setData(const QModelIndex& index, const QVariant& value, int role) override; Q_SIGNALS: void itemChildrenReady(); public: TreeItem* itemForIndex(const QModelIndex& index) const; QModelIndex indexForItem(TreeItem *item, int column) const; using QAbstractItemModel::beginInsertRows; using QAbstractItemModel::endInsertRows; using QAbstractItemModel::beginRemoveRows; using QAbstractItemModel::endRemoveRows; using QAbstractItemModel::dataChanged; private: QVector headers_; TreeItem *root_; }; } #endif diff --git a/debugger/variable/variablecollection.h b/debugger/variable/variablecollection.h index aaeb88a7c..81bcce3c4 100644 --- a/debugger/variable/variablecollection.h +++ b/debugger/variable/variablecollection.h @@ -1,253 +1,253 @@ /* * KDevelop Debugger Support * * Copyright 2007 Hamish Rodda * Copyright 2008 Vladimir Prus * Copyright 2009 Niko Sams * * 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. */ #ifndef KDEVPLATFORM_VARIABLECOLLECTION_H #define KDEVPLATFORM_VARIABLECOLLECTION_H -#include +#include #include #include #include #include "../util/treemodel.h" #include "../util/treeitem.h" #include "../../interfaces/idocument.h" #include "../../interfaces/idebugcontroller.h" namespace KDevMI { namespace GDB { class GdbTest; } } namespace KDevelop { class VariableToolTip; class IDebugSession; class KDEVPLATFORMDEBUGGER_EXPORT Variable : public TreeItem { Q_OBJECT friend class KDevMI::GDB::GdbTest; public: protected: Variable(TreeModel* model, TreeItem* parent, const QString& expression, const QString& display = {}); public: enum format_t { Natural, Binary, Octal, Decimal, Hexadecimal }; static format_t str2format(const QString& str); static QString format2str(format_t format); QString expression() const; bool inScope() const; void setInScope(bool v); void setValue(const QString &v); QString value() const; void setType(const QString& type); QString type() const; void setTopLevel(bool v); void setShowError(bool v); bool showError(); using TreeItem::setHasMore; using TreeItem::setHasMoreInitial; using TreeItem::appendChild; using TreeItem::deleteChildren; using TreeItem::isExpanded; using TreeItem::parent; using TreeItem::model; ~Variable() override; /* Connects this variable to debugger, fetching the current value and otherwise preparing this variable to be displayed everywhere. The attempt may fail, for example if the expression is invalid. Calls slot 'callbackMethod' in 'callback' to notify of the result. The slot should be taking 'bool ok' parameter. */ virtual void attachMaybe(QObject *callback = nullptr, const char *callbackMethod = nullptr) = 0; virtual bool canSetFormat() const { return false; } void setFormat(format_t format); format_t format() const { return m_format; } virtual void formatChanged(); // get/set 'changed' state, if the variable changed it will be highlighted bool isChanged() const { return m_changed; } void setChanged(bool c); void resetChanged(); public slots: void die(); protected: bool topLevel() const { return m_topLevel; } private: // TreeItem overrides QVariant data(int column, int role) const override; private: bool isPotentialProblematicValue() const; QString m_expression; bool m_inScope; bool m_topLevel; bool m_changed; bool m_showError; format_t m_format; }; class KDEVPLATFORMDEBUGGER_EXPORT TooltipRoot : public TreeItem { Q_OBJECT public: explicit TooltipRoot(TreeModel* model) : TreeItem(model) {} void init(Variable *var) { appendChild(var); } void fetchMoreChildren() override {} }; class KDEVPLATFORMDEBUGGER_EXPORT Watches : public TreeItem { Q_OBJECT friend class KDevMI::GDB::GdbTest; public: Watches(TreeModel* model, TreeItem* parent); Variable* add(const QString& expression); void reinstall(); Variable *addFinishResult(const QString& convenienceVarible); void removeFinishResult(); void resetChanged(); using TreeItem::childCount; friend class VariableCollection; friend class IVariableController; private: QVariant data(int column, int role) const override; void fetchMoreChildren() override {} Variable* finishResult_; }; class KDEVPLATFORMDEBUGGER_EXPORT Locals : public TreeItem { Q_OBJECT public: Locals(TreeModel* model, TreeItem* parent, const QString &name); QList updateLocals(QStringList locals); void resetChanged(); using TreeItem::deleteChildren; using TreeItem::setHasMore; friend class VariableCollection; friend class IVariableController; private: void fetchMoreChildren() override {} }; class KDEVPLATFORMDEBUGGER_EXPORT VariablesRoot : public TreeItem { Q_OBJECT public: explicit VariablesRoot(TreeModel* model); Watches *watches() const { return m_watches; } Locals *locals(const QString &name = QStringLiteral("Locals")); QHash allLocals() const; void fetchMoreChildren() override {} void resetChanged(); private: Watches *m_watches; QHash m_locals; }; class VariableProvider : public KTextEditor::TextHintProvider { public: explicit VariableProvider(VariableCollection* collection); QString textHint(KTextEditor::View* view, const KTextEditor::Cursor& position) override; private: VariableCollection* m_collection; }; class KDEVPLATFORMDEBUGGER_EXPORT VariableCollection : public TreeModel { Q_OBJECT public: enum Column { NameColumn, ValueColumn, TypeColumn }; explicit VariableCollection(IDebugController* parent); ~VariableCollection() override; VariablesRoot* root() const { return m_universe; } Watches* watches() const { return m_universe->watches(); } Locals* locals(const QString &name = i18n("Locals")) const { return m_universe->locals(name); } QHash allLocals() const { return m_universe->allLocals(); } public Q_SLOTS: void variableWidgetShown(); void variableWidgetHidden(); private Q_SLOTS: void updateAutoUpdate(KDevelop::IDebugSession* session = nullptr); void textDocumentCreated( KDevelop::IDocument*); void viewCreated(KTextEditor::Document*, KTextEditor::View*); private: VariablesRoot* m_universe; QPointer m_activeTooltip; bool m_widgetVisible; friend class VariableProvider; VariableProvider m_textHintProvider; }; } #endif diff --git a/interfaces/contextmenuextension.cpp b/interfaces/contextmenuextension.cpp index 2a5507dd8..daf95bce3 100644 --- a/interfaces/contextmenuextension.cpp +++ b/interfaces/contextmenuextension.cpp @@ -1,138 +1,138 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2008 Andreas Pakulat * * * * 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 "contextmenuextension.h" -#include +#include #include #include #include namespace KDevelop { const QString ContextMenuExtension::FileGroup = QStringLiteral("FileGroup"); const QString ContextMenuExtension::RefactorGroup = QStringLiteral("RefactorGroup"); const QString ContextMenuExtension::BuildGroup = QStringLiteral("BuildGroup"); const QString ContextMenuExtension::RunGroup = QStringLiteral("RunGroup"); const QString ContextMenuExtension::DebugGroup = QStringLiteral("DebugGroup"); const QString ContextMenuExtension::EditGroup = QStringLiteral("EditGroup"); const QString ContextMenuExtension::VcsGroup = QStringLiteral("VcsGroup"); const QString ContextMenuExtension::ProjectGroup = QStringLiteral("ProjectGroup"); const QString ContextMenuExtension::OpenEmbeddedGroup = QStringLiteral("OpenEmbeddedGroup"); const QString ContextMenuExtension::OpenExternalGroup = QStringLiteral("OpenExternalGroup"); const QString ContextMenuExtension::AnalyzeGroup = QStringLiteral("AnalyzeGroup"); const QString ContextMenuExtension::NavigationGroup = QStringLiteral("NavigationGroup"); const QString ContextMenuExtension::ExtensionGroup = QStringLiteral("ExtensionGroup"); class ContextMenuExtensionPrivate { public: QMap > extensions; }; ContextMenuExtension::ContextMenuExtension() : d(new ContextMenuExtensionPrivate) { } ContextMenuExtension::~ContextMenuExtension() { delete d; } ContextMenuExtension::ContextMenuExtension( const ContextMenuExtension& rhs ) : d( new ContextMenuExtensionPrivate ) { d->extensions = rhs.d->extensions; } ContextMenuExtension& ContextMenuExtension::operator=( const ContextMenuExtension& rhs ) { if( this == &rhs ) return *this; d->extensions = rhs.d->extensions; return *this; } QList ContextMenuExtension::actions( const QString& group ) const { return d->extensions.value( group, QList() ); } void ContextMenuExtension::addAction( const QString& group, QAction* action ) { if( !d->extensions.contains( group ) ) { d->extensions.insert( group, QList() << action ); } else { d->extensions[group].append( action ); } } void populateMenuWithGroup( QMenu* menu, const QList& extensions, const QString& groupName, const QString& groupDisplayName = QString()) { QList groupActions; for (const ContextMenuExtension& extension : extensions) { groupActions += extension.actions(groupName); } // remove NULL QActions, if any. Those can end up in groupActions if plugins // like the debugger plugins are not loaded. groupActions.removeAll(nullptr); if (groupActions.isEmpty()) { return; } QMenu* groupMenu = menu; if (groupActions.count() > 1 && !groupDisplayName.isEmpty()) { groupMenu = menu->addMenu(groupDisplayName); } for (QAction* action : groupActions) { groupMenu->addAction(action); } menu->addSeparator(); } void ContextMenuExtension::populateMenu(QMenu* menu, const QList& extensions) { populateMenuWithGroup(menu, extensions, BuildGroup); populateMenuWithGroup(menu, extensions, FileGroup); populateMenuWithGroup(menu, extensions, EditGroup); populateMenuWithGroup(menu, extensions, DebugGroup, i18n("Debug")); populateMenuWithGroup(menu, extensions, RefactorGroup, i18n("Refactor")); populateMenuWithGroup(menu, extensions, NavigationGroup); populateMenuWithGroup(menu, extensions, AnalyzeGroup, i18n("Analyze With")); populateMenuWithGroup(menu, extensions, VcsGroup); populateMenuWithGroup(menu, extensions, ExtensionGroup); } } diff --git a/interfaces/contextmenuextension.h b/interfaces/contextmenuextension.h index 7a45cf502..a789e4c5b 100644 --- a/interfaces/contextmenuextension.h +++ b/interfaces/contextmenuextension.h @@ -1,107 +1,107 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2008 Andreas Pakulat * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_CONTEXTMENUEXTENSION_H #define KDEVPLATFORM_CONTEXTMENUEXTENSION_H -#include -#include +#include +#include #include "interfacesexport.h" class QAction; class QMenu; namespace KDevelop { /** * For documentation on how to use this class, @see interfaces/context.h */ class KDEVPLATFORMINTERFACES_EXPORT ContextMenuExtension { public: /** The menu group containing file actions */ static const QString FileGroup; /** menu group containing refactoring actions */ static const QString RefactorGroup; /** menu group to contain build support actions */ static const QString BuildGroup; /** menu group to contain run actions */ static const QString RunGroup; /** menu group to contain debug actions */ static const QString DebugGroup; /** menu group to contain editing actions */ static const QString EditGroup; /** menu group to contain version control actions */ static const QString VcsGroup; /** menu group to contain project actions */ static const QString ProjectGroup; /** menu group to contain open in embedded editor actions */ static const QString OpenEmbeddedGroup; /** menu group to contain open with external application actions */ static const QString OpenExternalGroup; /** menu group to contain analyze actions */ static const QString AnalyzeGroup; /** The menu group containing navigation actions */ static const QString NavigationGroup; /** menu group that can contain any extension menu. * Actions for this extension will always be at the end * of the menu. Plugins using this should think about * providing a submenu, so the context menu doesn't get cluttered. */ static const QString ExtensionGroup; /** * create new context menu extension object */ ContextMenuExtension(); ~ContextMenuExtension(); ContextMenuExtension( const ContextMenuExtension& rhs ); ContextMenuExtension& operator=( const ContextMenuExtension& rhs ); /** * Add an action to the given menu group * @param group the menu group to which the action should be added * @param action the action to add to the menu group */ void addAction( const QString& group, QAction* action ); /** * Return all actions that are in the menu group * @param group the menu group from which to get the actions * @returns a list of actions for that menu group */ QList actions( const QString& group ) const; /** * Populate a QMenu with the actions in the given context menu extensions. */ static void populateMenu(QMenu* menu, const QList& extensions); private: class ContextMenuExtensionPrivate* const d; }; } #endif diff --git a/interfaces/ibuddydocumentfinder.cpp b/interfaces/ibuddydocumentfinder.cpp index 1afe69d90..aa378e7e7 100644 --- a/interfaces/ibuddydocumentfinder.cpp +++ b/interfaces/ibuddydocumentfinder.cpp @@ -1,53 +1,53 @@ /*************************************************************************** * Copyright 2011 Yannick Motta * * Martin Heide * * * * 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 "ibuddydocumentfinder.h" -#include +#include namespace KDevelop { //Our private data is entirely static, so don't need to create an //instance of the private data class struct IBuddyDocumentFinder::Private { static QMap& finders() { static QMap finders; return finders; } }; // ---------------- "Registry" interface -------------------------------------------- void IBuddyDocumentFinder::addFinder(const QString& mimeType, IBuddyDocumentFinder *finder) { Private::finders()[mimeType] = finder; } void IBuddyDocumentFinder::removeFinder(const QString& mimeType) { Private::finders().remove(mimeType); } IBuddyDocumentFinder* IBuddyDocumentFinder::finderForMimeType(const QString& mimeType) { return Private::finders().value(mimeType, nullptr); } } diff --git a/interfaces/icompletionsettings.h b/interfaces/icompletionsettings.h index 8066360b4..086a6a8cd 100644 --- a/interfaces/icompletionsettings.h +++ b/interfaces/icompletionsettings.h @@ -1,65 +1,65 @@ /*************************************************************************** * Copyright 2008 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 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_ICOMPLETIONSETTINGS_H #define KDEVPLATFORM_ICOMPLETIONSETTINGS_H -#include +#include #include "interfacesexport.h" namespace KDevelop { class KDEVPLATFORMINTERFACES_EXPORT ICompletionSettings : public QObject { Q_OBJECT public: ~ICompletionSettings() override; enum CompletionLevel { Minimal, MinimalWhenAutomatic, AlwaysFull, LAST_LEVEL }; Q_SCRIPTABLE virtual int minFilesForSimplifiedParsing() const = 0; Q_SCRIPTABLE virtual CompletionLevel completionLevel() const = 0; Q_SCRIPTABLE virtual bool automaticCompletionEnabled() const = 0; Q_SCRIPTABLE virtual int localColorizationLevel() const = 0; Q_SCRIPTABLE virtual int globalColorizationLevel() const = 0; Q_SCRIPTABLE virtual bool highlightSemanticProblems() const = 0; Q_SCRIPTABLE virtual bool highlightProblematicLines() const = 0; Q_SCRIPTABLE virtual bool boldDeclarations() const = 0; Q_SCRIPTABLE virtual bool showMultiLineSelectionInformation() const = 0; Q_SCRIPTABLE virtual QStringList todoMarkerWords() const = 0; Q_SIGNALS: void settingsChanged(ICompletionSettings*); }; } #endif diff --git a/interfaces/icore.h b/interfaces/icore.h index d647bfcc9..a1bd15194 100644 --- a/interfaces/icore.h +++ b/interfaces/icore.h @@ -1,149 +1,149 @@ /* This file is part of KDevelop Copyright 2007 Alexander Dymo Copyright 2007 Kris Wong 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. */ #ifndef KDEVPLATFORM_ICORE_H #define KDEVPLATFORM_ICORE_H -#include +#include #include "interfacesexport.h" #include "isessionlock.h" class KAboutData; /** * The KDevelop namespace contains all classes provided by the KDevelop * platform libraries. */ namespace KDevelop { class IUiController; class IPluginController; class IProjectController; class ILanguageController; class IDocumentController; class ISessionController; class IRunController; class ISourceFormatterController; class ISession; class ISelectionController; class IDocumentationController; class IDebugController; class IPartController; class IDashboardController; class ITestController; /** * ICore is the container class for all the various objects in use by * KDevelop. If access is needed to a particular controller, then this class * should be used. * * ICore can provide the user with instances of the following things: * - the main window(s) * - the document controller(s) * - the plugin controller * - the project controller * - the language controller * - the KPart manager * * When an object is provided to ICore so it can be used later, ICore * will take ownership of the object and upon application shutdown will take * responsibility for deleting the objects stored by ICore. */ class KDEVPLATFORMINTERFACES_EXPORT ICore: public QObject { Q_OBJECT public: ~ICore() override; /** @return the static ICore instance */ static ICore *self(); /** @return ui controller */ Q_SCRIPTABLE virtual KDevelop::IUiController *uiController() = 0; /** @return plugin controller */ Q_SCRIPTABLE virtual KDevelop::IPluginController *pluginController() = 0; /** @return project controller */ Q_SCRIPTABLE virtual KDevelop::IProjectController *projectController() = 0; /** @return language controller */ Q_SCRIPTABLE virtual KDevelop::ILanguageController *languageController() = 0; /** @return part manager */ Q_SCRIPTABLE virtual KDevelop::IPartController *partController() = 0; /** @return document controller */ Q_SCRIPTABLE virtual KDevelop::IDocumentController *documentController() = 0; /** @return run controller */ Q_SCRIPTABLE virtual KDevelop::IRunController *runController() = 0; /** @return the active session */ Q_SCRIPTABLE virtual KDevelop::ISession *activeSession() = 0; /** @return the session lock for the active session */ Q_SCRIPTABLE virtual KDevelop::ISessionLock::Ptr activeSessionLock() = 0; /** @return the sourceformatter controller */ Q_SCRIPTABLE virtual KDevelop::ISourceFormatterController *sourceFormatterController() = 0; /** @return the selection controller */ Q_SCRIPTABLE virtual KDevelop::ISelectionController* selectionController() = 0; /** @return the documentation controller */ Q_SCRIPTABLE virtual KDevelop::IDocumentationController* documentationController() = 0; /** @return the debug controller */ Q_SCRIPTABLE virtual KDevelop::IDebugController* debugController() = 0; /** @return the test controller */ Q_SCRIPTABLE virtual KDevelop::ITestController* testController() = 0; /** @return the about data of the framework, different from the main about data which is created by the application */ virtual KAboutData aboutData() const = 0; /** @return true if the application is currently being shut down */ virtual bool shuttingDown() const = 0; Q_SIGNALS: /** Emitted when the initialization of the core components has been completed */ void initializationCompleted(); /** * Emitted immediately before tearing down the session and UI. Useful when performing any last minute * preparations such as saving settings. */ void aboutToShutdown(); /** * Emitted when the teardown of the core components has been completed. */ void shutdownCompleted(); protected: explicit ICore(QObject *parent = nullptr); static ICore *m_self; }; } #endif diff --git a/interfaces/idebugcontroller.h b/interfaces/idebugcontroller.h index 1eaab0a96..d15c5a0d8 100644 --- a/interfaces/idebugcontroller.h +++ b/interfaces/idebugcontroller.h @@ -1,64 +1,64 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2009 Niko Sams * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_IDEBUGCONTROLLER_H #define KDEVPLATFORM_IDEBUGCONTROLLER_H -#include +#include #include "interfacesexport.h" namespace KDevelop { class VariableCollection; class BreakpointModel; class IDebugSession; /** Top level debugger object. Exists as long as KDevelop exists and holds some global debugger state, like breakpoints. Also holds the IDebugSession for the specific application that is being debugged. */ class KDEVPLATFORMINTERFACES_EXPORT IDebugController : public QObject { Q_OBJECT public: explicit IDebugController(QObject *parent = nullptr); ~IDebugController() override; /** Each plugin using the debugger actions needs to call this function to setup the gui */ virtual void initializeUi() = 0; virtual void addSession(IDebugSession* session) = 0; /** Return the current debug session. At present, only one session may be active at a time. */ virtual IDebugSession *currentSession() = 0; virtual BreakpointModel *breakpointModel() = 0; virtual VariableCollection *variableCollection() = 0; Q_SIGNALS: void currentSessionChanged(KDevelop::IDebugSession* session); }; } #endif diff --git a/interfaces/idocumentation.h b/interfaces/idocumentation.h index aff73d12d..3add12403 100644 --- a/interfaces/idocumentation.h +++ b/interfaces/idocumentation.h @@ -1,72 +1,72 @@ /* This file is part of KDevelop Copyright 2009 Aleix Pol 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. */ #ifndef KDEVPLATFORM_IDOCUMENTATION_H #define KDEVPLATFORM_IDOCUMENTATION_H -#include +#include #include #include "interfacesexport.h" #include class QWidget; namespace KDevelop { class DocumentationFindWidget; class IDocumentationProvider; class KDEVPLATFORMINTERFACES_EXPORT IDocumentation : public QObject, public KSharedObject { Q_OBJECT Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString description READ description NOTIFY descriptionChanged) Q_PROPERTY(IDocumentationProvider* provider READ provider CONSTANT) public: using Ptr = QExplicitlySharedDataPointer; IDocumentation(); virtual ~IDocumentation(); /** @returns the name of the documented information*/ virtual QString name() const = 0; /** @returns a HTML-formatted short description. */ virtual QString description() const = 0; /** @returns a widget with all the needed documentation information. @param parent defines the widget's parent @param findWidget can be used to tell how do we want to deal with Search inside the documentation widget. The implementation will have to enable the widget if it means to support the search feature. */ virtual QWidget* documentationWidget(DocumentationFindWidget* findWidget, QWidget* parent = nullptr) = 0; virtual IDocumentationProvider* provider() const = 0; Q_SIGNALS: void descriptionChanged(); }; } Q_DECLARE_METATYPE(KDevelop::IDocumentation::Ptr) #endif diff --git a/interfaces/idocumentationcontroller.h b/interfaces/idocumentationcontroller.h index 1d0e77729..629f11250 100644 --- a/interfaces/idocumentationcontroller.h +++ b/interfaces/idocumentationcontroller.h @@ -1,62 +1,62 @@ /* Copyright 2009 Aleix Pol Gonzalez Copyright 2010 Benjamin Port 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. */ #ifndef KDEVPLATFORM_IDOCUMENTATIONCONTROLLER_H #define KDEVPLATFORM_IDOCUMENTATIONCONTROLLER_H #include -#include +#include namespace KDevelop { class IDocumentationProvider; class Declaration; /** * Allows to access the documentation. * * @author Aleix Pol */ class KDEVPLATFORMINTERFACES_EXPORT IDocumentationController: public QObject { Q_OBJECT public: IDocumentationController(); ~IDocumentationController() override; /** Return the documentation provider plugin instances. */ virtual QList documentationProviders() const = 0; /** Return the corresponding documentation instance for a determinate declaration. */ virtual IDocumentation::Ptr documentationForDeclaration(Declaration* declaration) = 0; /** Show the documentation specified by @p doc. */ virtual void showDocumentation(const IDocumentation::Ptr& doc) = 0; public Q_SLOTS: /** Emit signal when the documentation providers list changed. */ virtual void changedDocumentationProviders() = 0; Q_SIGNALS: /** Emitted when providers list changed */ void providersChanged(); }; } #endif diff --git a/interfaces/idocumentationproviderprovider.h b/interfaces/idocumentationproviderprovider.h index ceff3e375..7cc5cb969 100644 --- a/interfaces/idocumentationproviderprovider.h +++ b/interfaces/idocumentationproviderprovider.h @@ -1,47 +1,47 @@ /* This file is part of KDevelop Copyright 2010 Benjamin Port 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. */ #ifndef KDEVPLATFORM_IDOCUMENTATIONPROVIDERPROVIDER_H #define KDEVPLATFORM_IDOCUMENTATIONPROVIDERPROVIDER_H -#include +#include #include "interfacesexport.h" namespace KDevelop { class IDocumentationProvider; class KDEVPLATFORMINTERFACES_EXPORT IDocumentationProviderProvider { public: virtual ~IDocumentationProviderProvider(); /** @returns a list of providers provided by this class. */ virtual QList providers() = 0; Q_SIGNALS: virtual void changedProvidersList() const=0; }; } Q_DECLARE_INTERFACE( KDevelop::IDocumentationProviderProvider, "org.kdevelop.IDocumentationProviderProvider") #endif diff --git a/interfaces/idocumentcontroller.h b/interfaces/idocumentcontroller.h index 49278bcc8..e093a64f4 100644 --- a/interfaces/idocumentcontroller.h +++ b/interfaces/idocumentcontroller.h @@ -1,234 +1,234 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_IDOCUMENTCONTROLLER_H #define KDEVPLATFORM_IDOCUMENTCONTROLLER_H -#include -#include +#include +#include #include #include #include #include "interfacesexport.h" #include "idocument.h" namespace KTextEditor { class View; } namespace KDevelop { class ICore; class KDEVPLATFORMINTERFACES_EXPORT IDocumentFactory { public: virtual ~IDocumentFactory() {} virtual IDocument* create(const QUrl&, ICore* ) = 0; }; /** * * Allows to access the open documents and also open new ones * * @class IDocumentController */ class KDEVPLATFORMINTERFACES_EXPORT IDocumentController: public QObject { Q_OBJECT public: enum DocumentActivation { DefaultMode = 0, /**Activate document and create a view if no other flags passed.*/ DoNotActivate = 1, /**Don't activate the Document.*/ DoNotCreateView = 2, /**Don't create and show the view for the Document.*/ DoNotFocus = 4, /**Don't change the keyboard focus.*/ DoNotAddToRecentOpen = 8, /**Don't add the document to the File/Open Recent menu.*/ }; Q_DECLARE_FLAGS(DocumentActivationParams, DocumentActivation) explicit IDocumentController(QObject *parent); /** * Finds the first document object corresponding to a given url. * * @param url The Url of the document. * @return The corresponding document, or null if not found. */ Q_SCRIPTABLE virtual KDevelop::IDocument* documentForUrl( const QUrl & url ) const = 0; /// @return The list of all open documents Q_SCRIPTABLE virtual QList openDocuments() const = 0; /** * Returns the curently active or focused document. * * @return The active document. */ Q_SCRIPTABLE virtual KDevelop::IDocument* activeDocument() const = 0; Q_SCRIPTABLE virtual void activateDocument( KDevelop::IDocument * document, const KTextEditor::Range& range = KTextEditor::Range::invalid() ) = 0; virtual void registerDocumentForMimetype( const QString&, KDevelop::IDocumentFactory* ) = 0; Q_SCRIPTABLE virtual bool saveAllDocuments(KDevelop::IDocument::DocumentSaveMode mode = KDevelop::IDocument::Default) = 0; Q_SCRIPTABLE virtual bool saveSomeDocuments(const QList& list, KDevelop::IDocument::DocumentSaveMode mode = KDevelop::IDocument::Default) = 0; Q_SCRIPTABLE virtual bool saveAllDocumentsForWindow(KParts::MainWindow* mw, IDocument::DocumentSaveMode mode, bool currentAreaOnly = false) = 0; /// Opens a text document containing the @p data text. Q_SCRIPTABLE virtual KDevelop::IDocument* openDocumentFromText( const QString& data ) = 0; virtual IDocumentFactory* factory(const QString& mime) const = 0; Q_SCRIPTABLE virtual KTextEditor::Document* globalTextEditorInstance()=0; /** * @returns the KTextEditor::View of the current document, in case it is a text document */ virtual KTextEditor::View* activeTextDocumentView() const = 0; public Q_SLOTS: /** * Opens a new or existing document. * * @param url The full Url of the document to open. * @param cursor The location information, if applicable. * @param activationParams Indicates whether to fully activate the document. */ KDevelop::IDocument* openDocument( const QUrl &url, const KTextEditor::Cursor& cursor, DocumentActivationParams activationParams = nullptr, const QString& encoding = {}); /** * Opens a new or existing document. * * @param url The full Url of the document to open. * @param range The range of text to select, if applicable. * @param activationParams Indicates whether to fully activate the document * @param buddy Optional buddy document. If 0, the registered IBuddyDocumentFinder * for the URL's mimetype will be queried to find a fitting buddy. * If a buddy was found (or passed) @p url will be opened next * to its buddy. * * @return The opened document */ virtual KDevelop::IDocument* openDocument( const QUrl &url, const KTextEditor::Range& range = KTextEditor::Range::invalid(), DocumentActivationParams activationParams = nullptr, const QString& encoding = {}, IDocument* buddy = nullptr) = 0; /** * Opens a document from the IDocument instance. * * @param doc The IDocument to add * @param range The location information, if applicable. * @param activationParams Indicates whether to fully activate the document. * @param buddy Optional buddy document. If 0, the registered IBuddyDocumentFinder * for the Documents mimetype will be queried to find a fitting buddy. * If a buddy was found (or passed) @p url will be opened next * to its buddy. */ virtual Q_SCRIPTABLE bool openDocument(IDocument* doc, const KTextEditor::Range& range = KTextEditor::Range::invalid(), DocumentActivationParams activationParams = nullptr, IDocument* buddy = nullptr) = 0; /** * Opens a new or existing document. * * @param url The full Url of the document to open. * @param prefName The name of the preferred KPart to open that document */ virtual KDevelop::IDocument* openDocument( const QUrl &url, const QString& prefName ) = 0; virtual bool closeAllDocuments() = 0; Q_SIGNALS: /// Emitted when the document has been activated. void documentActivated( KDevelop::IDocument* document ); /** * Emitted whenever the active cursor jumps from one document+cursor to another, * as e.g. caused by a call to openDocument(..). * * This is also emitted when a document is only activated. Then previousDocument is zero. */ void documentJumpPerformed( KDevelop::IDocument* newDocument, KTextEditor::Cursor newCursor, KDevelop::IDocument* previousDocument, KTextEditor::Cursor previousCursor); /// Emitted when a document has been saved. void documentSaved( KDevelop::IDocument* document ); /** * Emitted when a document has been opened. * * NOTE: The document may not be loaded from disk/network at this point. * NOTE: No views exist for the document at the time this signal is emitted. */ void documentOpened( KDevelop::IDocument* document ); /** * Emitted when a document has been loaded. * * NOTE: No views exist for the document at the time this signal is emitted. */ void documentLoaded( KDevelop::IDocument* document ); /** * Emitted when a text document has been loaded, and the text document created. * * NOTE: no views exist for the document at the time this signal is emitted. */ void textDocumentCreated( KDevelop::IDocument* document ); /// Emitted when a document has been closed. void documentClosed( KDevelop::IDocument* document ); /** * This is emitted when the document state(the relationship * between the file in the editor and the file stored on disk) changes. */ void documentStateChanged( KDevelop::IDocument* document ); /// This is emitted when the document content changed. void documentContentChanged( KDevelop::IDocument* document ); /** * Emitted when a document has been loaded, but before documentLoaded(..) is emitted. * * This allows parts of kdevplatform to prepare data-structures that can be used by other parts * during documentLoaded(..). */ void documentLoadedPrepare( KDevelop::IDocument* document ); /// Emitted when a document url has changed. void documentUrlChanged( KDevelop::IDocument* document ); friend class IDocument; }; } // namespace KDevelop Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::IDocumentController::DocumentActivationParams) #endif diff --git a/interfaces/ilanguagecontroller.h b/interfaces/ilanguagecontroller.h index bd58b33e3..5eed97a7f 100644 --- a/interfaces/ilanguagecontroller.h +++ b/interfaces/ilanguagecontroller.h @@ -1,84 +1,84 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_ILANGUAGECONTROLLER_H #define KDEVPLATFORM_ILANGUAGECONTROLLER_H -#include -#include +#include +#include #include #include "interfacesexport.h" namespace KDevelop { class ILanguageSupport; class BackgroundParser; class ICompletionSettings; class StaticAssistantsManager; class ProblemModelSet; /** * @class ILanguageController */ class KDEVPLATFORMINTERFACES_EXPORT ILanguageController: public QObject { Q_OBJECT public: explicit ILanguageController(QObject *parent = nullptr); /** * @return the currently active languages loaded for the currently active file. * * The list is empty if the file's language is unsupported. */ Q_SCRIPTABLE virtual QList activeLanguages() = 0; /** *@return the language for given @p name. */ Q_SCRIPTABLE virtual ILanguageSupport* language(const QString &name) const = 0; /** * @return the languages that support the MIME type of @p url. * @warning If this is called from within the foreground thread, * the language support is loaded if required. * If it is called from a background thread, it can not * be loaded, and thus zero will be returned. */ Q_SCRIPTABLE virtual QList languagesForUrl(const QUrl &url) = 0; /** @return All languages currently loaded */ Q_SCRIPTABLE virtual QList loadedLanguages() const = 0; /** @return the background parser used to parse source files */ Q_SCRIPTABLE virtual BackgroundParser *backgroundParser() const = 0; /** @return The global code assistant manager (manages assistants such as the RenameAssistant) */ Q_SCRIPTABLE virtual StaticAssistantsManager *staticAssistantsManager() const = 0; /** Access to the completion settings */ Q_SCRIPTABLE virtual ICompletionSettings *completionSettings() const = 0; virtual ProblemModelSet* problemModelSet() const = 0; }; } #endif diff --git a/interfaces/ilauncher.h b/interfaces/ilauncher.h index c51d7814b..66e4b50e9 100644 --- a/interfaces/ilauncher.h +++ b/interfaces/ilauncher.h @@ -1,88 +1,88 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat 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. */ #ifndef KDEVPLATFORM_ILAUNCHER_H #define KDEVPLATFORM_ILAUNCHER_H #include "interfacesexport.h" -#include +#include class KJob; class QString; namespace KDevelop { class ILaunchConfiguration; class LaunchConfigurationPageFactory; /** * The ILauncher interface allows one to actually run launch configurations. * Additionally it allows one to provide configuration pages specific to the * launcher and to identify for which modes this launcher may be used */ class KDEVPLATFORMINTERFACES_EXPORT ILauncher { public: virtual ~ILauncher(); /** * Returns a unique identifier for the launcher, * used for example for ILaunchConfigurationType::launcherForId(). */ virtual QString id() = 0; /** * a user visible name for the launcher * @returns a translatable string as description for the launcher */ virtual QString name() const = 0; /** * provides a short description about this launcher * @returns a description of what the launcher can do */ virtual QString description() const = 0; /** * returns the ids of the supported launch modes * @returns a list of id's for ILaunchMode's that are supported */ virtual QStringList supportedModes() const = 0; /** * provide a list of config pages for this launcher * @returns the config pages that this launcher needs */ virtual QList configPages() const = 0; /** * create a KJob that executes @p cfg in @p launchMode when started. * @param launchMode the mode in which the launcher should execute the configuration * @param cfg the launch configuration to be executed * @returns a KJob that starts the configured launchable */ virtual KJob* start( const QString& launchMode, ILaunchConfiguration* cfg ) = 0; }; } #endif diff --git a/interfaces/iplugin.h b/interfaces/iplugin.h index 6f56b2a1f..d9851b7c1 100644 --- a/interfaces/iplugin.h +++ b/interfaces/iplugin.h @@ -1,280 +1,280 @@ /* This file is part of the KDE project Copyright 1999-2001 Bernd Gehrmann Copyright 2004,2007 Alexander Dymo Copyright 2006 Adam Treat Copyright 2007 Andreas Pakulat 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. */ #ifndef KDEVPLATFORM_IPLUGIN_H #define KDEVPLATFORM_IPLUGIN_H -#include +#include #include #include "interfacesexport.h" namespace Sublime { class MainWindow; } /** * This macro adds an extension interface to register with the extension manager * Call this macro for all interfaces your plugin implements in its constructor */ #define KDEV_USE_EXTENSION_INTERFACE(Extension) \ _Pragma("message(\"Using deprecated function: KDEV_USE_EXTENSION_INTERFACE. Just remove the use of it.\")") namespace KDevelop { class ICore; class ConfigPage; class Context; class ContextMenuExtension; struct ProjectConfigOptions; /** * The base class for all KDevelop plugins. * * Plugin is a component which is loaded into KDevelop shell at startup or by * request. Each plugin should have corresponding .desktop file with a * description. The .desktop file template looks like: * @code * [Desktop Entry] * Type=Service * Exec=blubb * Name= * GenericName= * Comment= * Icon= * ServiceTypes=KDevelop/Plugin * X-KDE-Library= * X-KDE-PluginInfo-Name= * X-KDE-PluginInfo-Author= * X-KDE-PluginInfo-Version= * X-KDE-PluginInfo-License= * X-KDE-PluginInfo-Category= * X-KDevelop-Version= * X-KDevelop-Category= * X-KDevelop-Mode=GUI * X-KDevelop-LoadMode= * X-KDevelop-Languages= * X-KDevelop-SupportedMimeTypes= * X-KDevelop-Interfaces= * X-KDevelop-IOptional= * X-KDevelop-IRequired= * @endcode * Description of parameters in .desktop file: * - Name is a translatable name of a plugin, it is used in the plugin * selection list (required); * - GenericName is a translatable generic name of a plugin, it should * describe in general what the plugin can do (required); * - Comment is a short description about the plugin (optional); * - Icon is a plugin icon (preferred); * X-KDE-librarythis is the name of the .so file to load for this plugin (required); * - X-KDE-PluginInfo-Name is a non-translatable user-readable plugin * identifier used in KTrader queries (required); * - X-KDE-PluginInfo-Author is a non-translateable name of the plugin * author (optional); * - X-KDE-PluginInfo-Version is version number of the plugin (optional); * - X-KDE-PluginInfo-License is a license (optional). can be: GPL, * LGPL, BSD, Artistic, QPL or Custom. If this property is not set, license is * considered as unknown; * - X-KDE-PluginInfo-Category is used to categorize plugins (optional). can be: * Core, Project Management, Version Control, Utilities, Documentation, * Language Support, Debugging, Other * If this property is not set, "Other" is assumed * - X-KDevelop-Version is the KDevPlatform API version this plugin * works with (required); * - X-KDevelop-Interfaces is a list of extension interfaces that this * plugin implements (optional); * - X-KDevelop-IRequired is a list of extension interfaces that this * plugin depends on (optional); A list entry can also be of the form @c interface@pluginname, * in which case a plugin of the given name is required which implements the interface. * - X-KDevelop-IOptional is a list of extension interfaces that this * plugin will use if they are available (optional); * - X-KDevelop-Languages is a list of the names of the languages the plugin provides * support for (optional); * - X-KDevelop-SupportedMimeTypes is a list of mimetypes that the * language-parser in this plugin supports (optional); * - X-KDevelop-Mode is either GUI or NoGUI to indicate whether a plugin can run * with the GUI components loaded or not (required); * - X-KDevelop-Category is a scope of a plugin (see below for * explanation) (required); * - X-KDevelop-LoadMode can be set to AlwaysOn in which case the plugin will * never be unloaded even if requested via the API. (optional); * * Plugin scope can be either: * - Global * - Project * . * Global plugins are plugins which require only the shell to be loaded and do not operate on * the Project interface and/or do not use project wide information.\n * Core plugins are global plugins which offer some important "core" functionality and thus * are not selectable by user in plugin configuration pages.\n * Project plugins require a project to be loaded and are usually loaded/unloaded along with * the project. * If your plugin uses the Project interface and/or operates on project-related * information then this is a project plugin. * * * @sa Core class documentation for information about features available to * plugins from shell applications. */ class KDEVPLATFORMINTERFACES_EXPORT IPlugin: public QObject, public KXMLGUIClient { Q_OBJECT public: /**Constructs a plugin. * @param componentName The component name for this plugin. * @param parent The parent object for the plugin. */ IPlugin(const QString &componentName, QObject *parent); /**Destructs a plugin.*/ ~IPlugin() override; /** * Signal the plugin that it should cleanup since it will be unloaded soon. */ Q_SCRIPTABLE virtual void unload(); /** * Provides access to the ICore implementation */ Q_SCRIPTABLE ICore *core() const; /** * Convenience API to access an interface inherited by this plugin * * @return Instance to the specified interface, or nullptr */ template inline Extension* extension() { return qobject_cast(this); } /** * Ask the plugin for a ContextActionContainer, which contains actions * that will be merged into the context menu. * @param context the context describing where the context menu was requested * @returns a container describing which actions to merge into which context menu part */ virtual ContextMenuExtension contextMenuExtension( KDevelop::Context* context ); /** * Can create a new KXMLGUIClient, and set it up correctly with all the plugins per-window GUI actions. * * The caller owns the created object, including all contained actions. The object is destroyed as soon as * the mainwindow is closed. * * The default implementation calls the convenience function @ref createActionsForMainWindow and uses it to fill a custom KXMLGUIClient. * * Only override this version if you need more specific features of KXMLGUIClient, or other special per-window handling. * * @param window The main window the actions are created for */ virtual KXMLGUIClient* createGUIForMainWindow( Sublime::MainWindow* window ); /** * This function allows allows setting up plugin actions conveniently. Unless createGUIForMainWindow was overridden, * this is called for each new mainwindow, and the plugin should add its actions to @p actions, and write its KXMLGui xml file * into @p xmlFile. * * @param window The main window the actions are created for * @param xmlFile Change this to the xml file that needed for merging the actions into the GUI * @param actions Add your actions here. A new set of actions has to be created for each mainwindow. */ virtual void createActionsForMainWindow( Sublime::MainWindow* window, QString& xmlFile, KActionCollection& actions ); /** * This function is necessary because there is no proper way to signal errors from plugin constructor. * @returns True if the plugin has encountered an error (and therefore has an error description), * false otherwise. */ bool hasError() const; /** * Description of the last encountered error, of an empty string if none. */ QString errorDescription() const; /** * Set a @p description of the error encountered. An empty error * description implies no error in the plugin. */ void setErrorDescription(QString const& description); /** * Get the global config page with the \p number, config pages from 0 to * configPages()-1 are available if configPages() > 0. * * @param number index of config page * @param parent parent widget for config page * @return newly created config page or NULL, if the number is out of bounds, default implementation returns NULL. * This config page should inherit from ProjectConfigPage, but it is not a strict requirement. * The default implementation returns @c nullptr. * @see perProjectConfigPages(), ProjectConfigPage */ virtual ConfigPage* configPage(int number, QWidget *parent); /** * Get the number of available config pages for global settings. * @return number of global config pages. The default implementation returns zero. * @see configPage() */ virtual int configPages() const; /** * Get the number of available config pages for per project settings. * @return number of per project config pages. The default implementation returns zero. * @see perProjectConfigPage() */ virtual int perProjectConfigPages() const; /** * Get the per project config page with the \p number, config pages from 0 to * perProjectConfigPages()-1 are available if perProjectConfigPages() > 0. * * @param number index of config page * @param options The options used to initialize the ProjectConfigPage * @param parent parent widget for config page * @return newly created config page or NULL, if the number is out of bounds, default implementation returns NULL. * This config page should inherit from ProjectConfigPage, but it is not a strict requirement. * The default implementation returns @c nullptr. * @see perProjectConfigPages(), ProjectConfigPage */ virtual ConfigPage* perProjectConfigPage(int number, const KDevelop::ProjectConfigOptions& options, QWidget* parent); protected: /** * Initialize the XML GUI State. */ virtual void initializeGuiState(); private: friend class IPluginPrivate; class IPluginPrivate* const d; }; } #endif diff --git a/interfaces/iplugincontroller.h b/interfaces/iplugincontroller.h index e4eb96d8f..ea6b532df 100644 --- a/interfaces/iplugincontroller.h +++ b/interfaces/iplugincontroller.h @@ -1,200 +1,200 @@ /* This file is part of the KDE project Copyright 2004, 2007 Alexander Dymo Copyright 2006 Matt Rogers Copyright 2007 Andreas Pakulat Based on code from Kopete Copyright 2002-2003 Martijn Klingens 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. */ #ifndef KDEVPLATFORM_IPLUGINCONTROLLER_H #define KDEVPLATFORM_IPLUGINCONTROLLER_H -#include -#include +#include +#include #include #include #include "iplugin.h" #include "interfacesexport.h" class QExtensionManager; namespace KDevelop { class ProfileEngine; /** * The KDevelop plugin controller. * The Plugin controller is responsible for querying, loading and unloading * available plugins. * * Most of the time if you want to get at a plugin you should be using * extensionForPlugin with the extension interface name. If you need to get at * the actual \c IPlugin* pointer to connect signals/slots you should use * \c pluginForExtension() and then the IPlugin's extension member function to get * at the extension interface if necessary. * * If you have the need to load a specific plugin for a given extension both * functions have an optional second parameter that allows one to specify the * name of the plugin as declared in the \c .desktop file under the * \c X-KDE-PluginInfo-Name property. This should be used only very seldomly in * real code and is mostly meant for testing and for implementation in the * shell as it makes the code dependent on the plugin name which may change and * also the actual plugin implementation so users cannot exchange one plugin * with another also implementing the same interface. * */ class KDEVPLATFORMINTERFACES_EXPORT IPluginController : public QObject { Q_OBJECT public: /** * \brief Indicates the plugin type * This is used to determine how the plugin is loaded */ enum PluginType { Global = 0, ///< Indicates that the plugin is loaded at startup Project ///< Indicates that the plugin is loaded with the first opened project }; explicit IPluginController( QObject* parent = nullptr ); ~IPluginController() override; /** * Get the plugin info for a loaded plugin */ virtual KPluginMetaData pluginInfo(const IPlugin*) const = 0; /** * Find the KPluginMetaData structure for the given @p pluginId. */ virtual KPluginMetaData infoForPluginId(const QString &pluginId) const = 0; /** * Get a list of currently loaded plugins */ Q_SCRIPTABLE virtual QList loadedPlugins() const = 0; /** * @brief Unloads the plugin specified by @p plugin * * @param plugin The name of the plugin as specified by the * X-KDE-PluginInfo-Name key of the .desktop file for the plugin */ Q_SCRIPTABLE virtual bool unloadPlugin( const QString & plugin ) = 0; /** * @brief Loads the plugin specified by @p pluginName * * @param pluginName the name of the plugin, as given in the X-KDE-PluginInfo-Name property * @returns a pointer to the plugin instance or 0 */ Q_SCRIPTABLE virtual IPlugin* loadPlugin( const QString & pluginName ) = 0; /** * Retrieve a plugin which supports the given extension interface. * * All already loaded plugins will be queried and the first one to support the extension interface * will be returned. Any plugin can be an extension, only the "ServiceTypes=..." entry is * required in .desktop file for that plugin. * * If no already-loaded plugin was found, we try to load a plugin for the given extension. * * If no plugin was found, a nullptr will be returned. * * @param extension The extension interface. Can be empty if you want to find a plugin by name or other constraint. * @param pluginName The name of the plugin to load if multiple plugins for the extension exist, corresponds to the X-KDE-PluginInfo-Name * @param constraints A map of constraints on other plugin info properties. * @return A KDevelop extension plugin for given service type or 0 if no plugin supports it */ Q_SCRIPTABLE virtual IPlugin *pluginForExtension(const QString &extension, const QString& pluginName = {}, const QVariantMap& constraints = QVariantMap()) = 0; /** * Retrieve a list of plugins which supports the given extension interface. * All already loaded plugins will be queried and the first one to support the extension interface * will be returned. Any plugin can be an extension, only the "ServiceTypes=..." entry is * required in .desktop file for that plugin. * @param extension The extension interface * @param constraints A map of constraints on other plugin info properties. * @return A KDevelop extension plugin for given service type or 0 if no plugin supports it */ virtual QList allPluginsForExtension(const QString &extension, const QVariantMap& constraints = QVariantMap()) = 0; /** * Retrieve the plugin which supports given extension interface and * returns a pointer to the extension interface. * * All already loaded plugins will be queried and the first one to support the extension interface * will be returned. Any plugin can be an extension, only "ServiceTypes=..." entry is * required in .desktop file for that plugin. * @param extension The extension interface * @param pluginName The name of the plugin to load if multiple plugins for the extension exist, corresponds to the X-KDE-PluginInfo-Name * @return Pointer to the extension interface or 0 if no plugin supports it */ template Extension* extensionForPlugin( const QString &extension = {}, const QString &pluginName = {}) { QString ext; if( extension.isEmpty() ) { ext = qobject_interface_iid(); } else { ext = extension; } IPlugin *plugin = pluginForExtension(ext, pluginName); if (plugin) { return plugin->extension(); } return nullptr; } /** * Query for plugin information on KDevelop plugins implementing the given extension. * * The service version is checked for automatically and the only serviceType * searched for is "KDevelop/Plugin" * * @param extension The extension that should be implemented by the plugin, i.e. listed in X-KDevelop-Interfaces. * @param constraints A map of constraints on other plugin info properties. * @return The list of plugin offers. */ virtual QVector queryExtensionPlugins(const QString &extension, const QVariantMap& constraints = QVariantMap()) const = 0; virtual QList queryPluginsForContextMenuExtensions( KDevelop::Context* context ) const = 0; Q_SIGNALS: void loadingPlugin( const QString& ); void pluginLoaded( KDevelop::IPlugin* ); void unloadingPlugin( KDevelop::IPlugin* ); /** * This signal is emitted whenever a plugin is unloaded. * @note: that you shouldn't use the pointer anymore * except for comparing it against against other pointers. The plugin instance can already have been completely * deleted when this signal is emitted. */ void pluginUnloaded( KDevelop::IPlugin* ); private: friend class IPlugin; }; } #endif diff --git a/interfaces/iproject.h b/interfaces/iproject.h index a22f122dc..05f7a2e73 100644 --- a/interfaces/iproject.h +++ b/interfaces/iproject.h @@ -1,182 +1,182 @@ /* This file is part of the KDE project Copyright 2001 Matthias Hoelzer-Kluepfel Copyright 2001-2002 Bernd Gehrmann Copyright 2002-2003 Roberto Raggi Copyright 2002 Simon Hausmann Copyright 2003 Jens Dagerbo Copyright 2003 Mario Scalas Copyright 2003-2004 Alexander Dymo Copyright 2006 Matt Rogers Copyright 2007 Andreas Pakulat 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. */ #ifndef KDEVPLATFORM_IPROJECT_H #define KDEVPLATFORM_IPROJECT_H -#include +#include #include #include "interfacesexport.h" class KJob; template class QList; template class QSet; namespace KDevelop { class IPlugin; class IProjectFileManager; class IBuildSystemManager; class Path; class ProjectBaseItem; class ProjectFileItem; class ProjectFolderItem; class IndexedString; /** * \brief Object which represents a KDevelop project * * Provide better descriptions */ class KDEVPLATFORMINTERFACES_EXPORT IProject : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kdevelop.Project") Q_PROPERTY(QString projectName READ name CONSTANT) public: /** * Constructs a project. * * @param parent The parent object for the plugin. */ explicit IProject(QObject *parent = nullptr); /// Destructor. ~IProject() override; /** * Get the file manager for the project * * @return the file manager for the project, if one exists; otherwise null */ Q_SCRIPTABLE virtual IProjectFileManager* projectFileManager() const = 0; /** * Get the build system manager for the project * * @return the build system manager for the project, if one exists; otherwise null */ Q_SCRIPTABLE virtual IBuildSystemManager* buildSystemManager() const = 0; /** * Get the plugin that manages the project * This can be used to get other interfaces like IBuildSystemManager */ Q_SCRIPTABLE virtual IPlugin* managerPlugin() const = 0; /** * Get the version control plugin for this project * This may return 0 if the project is not under version control * or version control has been disabled for this project */ Q_SCRIPTABLE virtual IPlugin* versionControlPlugin() const = 0; /** * With this the top-level project item can be retrieved */ Q_SCRIPTABLE virtual ProjectFolderItem* projectItem() const = 0; /** * @return all items with the corresponding @p path */ Q_SCRIPTABLE virtual QList itemsForPath( const IndexedString& path ) const = 0; /** * @return all file items with the corresponding @p file path */ Q_SCRIPTABLE virtual QList filesForPath( const IndexedString& file ) const = 0; /** * @return all folder items with the corresponding @p folder path */ Q_SCRIPTABLE virtual QList foldersForPath( const IndexedString& folder ) const = 0; /** * @return the path to the project file */ virtual Path projectFile() const = 0; /** Get the url of the project file.*/ virtual KSharedConfigPtr projectConfiguration() const = 0; virtual void addToFileSet( ProjectFileItem* item ) = 0; virtual void removeFromFileSet( ProjectFileItem* item ) = 0; virtual QSet fileSet() const = 0; /** Returns whether the project is ready to be used or not. A project won't be ready for use when it's being reloaded or still loading */ virtual bool isReady() const=0; /** * @brief Get the project path * @return The canonical absolute directory of the project. */ virtual Path path() const = 0; /** Returns the name of the project. */ virtual Q_SCRIPTABLE QString name() const = 0; /** * @brief Check if the project contains an item with the given @p path. * * @param path the path to check * * @return true if the path @a path is a part of the project. */ virtual Q_SCRIPTABLE bool inProject(const IndexedString &path) const = 0; /** * @brief Tells the project what job is reloading it * * It's useful so that we can tell whether the project manager is busy or not. */ virtual void setReloadJob(KJob* job) = 0; Q_SIGNALS: /** * Gets emitted whenever a file was added to the project. */ void fileAddedToSet( KDevelop::ProjectFileItem* item ); /** * Gets emitted whenever a file was removed from the project. */ void fileRemovedFromSet( KDevelop::ProjectFileItem* item ); public Q_SLOTS: /** Make the model to reload */ virtual void reloadModel() = 0; /** This method is invoked when the project needs to be closed. */ virtual void close() = 0; }; } #endif diff --git a/interfaces/iprojectcontroller.h b/interfaces/iprojectcontroller.h index fb697922a..d90b82bf6 100644 --- a/interfaces/iprojectcontroller.h +++ b/interfaces/iprojectcontroller.h @@ -1,204 +1,204 @@ /* This file is part of KDevelop Copyright 2006 Adam Treat Copyright 2007 Andreas Pakulat 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. */ #ifndef KDEVPLATFORM_IPROJECTCONTROLLER_H #define KDEVPLATFORM_IPROJECTCONTROLLER_H -#include -#include +#include +#include #include #include "interfacesexport.h" namespace KDevelop { class IProject; class ProjectBuildSetModel; class ProjectModel; class ProjectBaseItem; class ProjectChangesModel; /** * @class IProjectController */ class KDEVPLATFORMINTERFACES_EXPORT IProjectController : public QObject { Q_OBJECT public: explicit IProjectController( QObject *parent = nullptr ); ~IProjectController() override; Q_SCRIPTABLE virtual KDevelop::IProject* projectAt( int ) const = 0; Q_SCRIPTABLE virtual int projectCount() const = 0; Q_SCRIPTABLE virtual QList projects() const = 0; /** * Provides access to the model representing the open projects * @returns the model containing the projects and their items */ Q_SCRIPTABLE virtual ProjectModel* projectModel() = 0; /** * @returns an instance to the model that keeps track of the state * of the files per project. */ Q_SCRIPTABLE virtual ProjectChangesModel* changesModel() = 0; Q_SCRIPTABLE virtual ProjectBuildSetModel* buildSetModel() = 0; /** * Find an open project using the name of the project * @param name the name of the project to be found * @returns the project or null if no project with that name is open */ Q_SCRIPTABLE virtual KDevelop::IProject* findProjectByName( const QString& name ) = 0; /** * Finding an open project for a given file or folder in the project * @param url the url of a file/folder belonging to an open project * @returns the first open project containing the url or null if no such * project can be found */ Q_SCRIPTABLE virtual IProject* findProjectForUrl( const QUrl& url ) const = 0; /** * Checks whether the given project name is used already or not. The project * controller supports only 1 project with a given name to be open at any time * @param name the name of the project to be opened or created * @returns whether the name is already used for an open project */ Q_SCRIPTABLE virtual bool isProjectNameUsed( const QString& name ) const = 0; virtual QUrl projectsBaseDirectory() const = 0; enum FormattingOptions { FormatHtml, FormatPlain }; /** * Returns a pretty short representation of the base path of the url, considering the currently loaded projects: * When the file is part of a currently loaded project, that projects name is shown as prefix instead of the * the full file path. * The returned path always has a training slash. * @param format formatting used for the string */ Q_SCRIPTABLE virtual QString prettyFilePath(const QUrl& url, FormattingOptions format = FormatHtml) const = 0; /** * Returns a pretty short representation of the given url, considering the currently loaded projects: * When the file is part of a currently loaded project, that projects name is shown as prefix instead of the * the full file path. * @param format formatting used for the string */ Q_SCRIPTABLE virtual QString prettyFileName(const QUrl& url, FormattingOptions format = FormatHtml) const = 0; /** * @returns whether project files should be parsed or not */ static bool parseAllProjectSources(); public Q_SLOTS: /** * Tries finding a project-file for the given source-url and opens it. * If no .kdev4 project file is found, the user is asked to import a project. */ virtual void openProjectForUrl( const QUrl &sourceUrl ) = 0; /** * open the project from the given kdev4 project file. This only reads * the file and starts creating the project model from it. The opening process * is finished once @ref projectOpened signal is emitted. * @param url a kdev4 project file top open */ virtual void openProject( const QUrl & url = QUrl() ) = 0; /** * close the given project. Closing the project is done in steps and * the @ref projectClosing and @ref projectClosed signals are emitted. Only when * the latter signal is emitted it is guaranteed that the project has been closed. * The @ref IProject object will be deleted after the closing has finished. */ virtual void closeProject( IProject* ) = 0; /** * Close all projects * * This usually calls closeProject() on all controlled projects * @sa closeProject() */ virtual void closeAllProjects() = 0; virtual void configureProject( IProject* ) = 0; /// Schedules all files of the @p project for reparsing by @see BackgroundParser virtual void reparseProject( IProject* project, bool ForceUpdate = false ) = 0; // virtual void changeCurrentProject( KDevelop::ProjectBaseItem* ) = 0; Q_SIGNALS: /** * Emitted right before a project is started to be loaded. * * At this point all sanity checks have been done, so the project * is really going to be loaded. Will be followed by @ref projectOpened signal * when loading completes or by @ref projectOpeningAborted if there are errors during loading * or it is aborted. * * @param project the project that is about to be opened. */ void projectAboutToBeOpened( KDevelop::IProject* project ); /** * emitted after a project is completely opened and the project model * has been populated. * @param project the project that has been opened. */ void projectOpened( KDevelop::IProject* project ); /** * emitted when starting to close a project that has been completely loaded before * (the @ref projectOpened signal has been emitted). * @param project the project that is going to be closed. */ void projectClosing( KDevelop::IProject* project ); /** * emitted when a project has been closed completely. * The project object is still valid, the deletion will be done * delayed during the next run of the event loop. * @param project the project that has been closed. */ void projectClosed( KDevelop::IProject* project ); /** * emitted when a project could not be loaded correctly or loading was aborted. * @ref project contents may not be initialized properly. * @param project the project which loading has been aborted. */ void projectOpeningAborted( KDevelop::IProject* project ); /** * emitted whenever the project configuration dialog accepted * changes * @param project the project whose configuration has changed */ void projectConfigurationChanged( KDevelop::IProject* project ); }; } #endif diff --git a/interfaces/iselectioncontroller.h b/interfaces/iselectioncontroller.h index a6035c2d9..90b831c45 100644 --- a/interfaces/iselectioncontroller.h +++ b/interfaces/iselectioncontroller.h @@ -1,83 +1,83 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat 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. */ #ifndef KDEVPLATFORM_ISELECTIONCONTROLLER_H #define KDEVPLATFORM_ISELECTIONCONTROLLER_H -#include +#include #include "interfacesexport.h" namespace KDevelop { class Context; /** * The main controller for selection updates in the GUI. * * This controller can be used by selection consumers and selection providers. * * A selection provider (for example a treeview for the projects) should * call updateSelection() with an appropriate context filled with the details * of the selection * * A selection consumer who is interested in notifications when the selection has * been changed, should connect to the selectionChanged signal. The consumer should * retrieve all necessary information that it might need to do something with the * selection and store those. Storing the whole Context* might be dangerous as it * will be deleted on the next selection change. * */ class KDEVPLATFORMINTERFACES_EXPORT ISelectionController : public QObject { Q_OBJECT public: ///Constructor. explicit ISelectionController(QObject *parent); ~ISelectionController() override; /** * Provides the current selection, note that this might be 0 */ virtual Context* currentSelection() = 0; public Q_SLOTS: /** * updates the current selection. * The SelectionController takes ownership of the given context and * deletes it when the selection is updated the next time. * @param context the new selection */ virtual void updateSelection( Context* context ) = 0; Q_SIGNALS: /** * Notify that the current selection changed. * @note The context might be deleted behind the back (when a new selection * is set) of the receivers of this signal, so make sure to copy all the * needed information. */ void selectionChanged( KDevelop::Context* ); }; } #endif // KDEVPLATFORM_ISELECTIONCONTROLLER_H diff --git a/interfaces/isession.h b/interfaces/isession.h index 8b6075790..7cd87fed1 100644 --- a/interfaces/isession.h +++ b/interfaces/isession.h @@ -1,77 +1,77 @@ /* This file is part of KDevelop Copyright 2008 Andreas Pakulat 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. */ #ifndef KDEVPLATFORM_ISESSION_H #define KDEVPLATFORM_ISESSION_H #include "interfacesexport.h" -#include +#include #include #include class QUuid; class QString; namespace KDevelop { class IPlugin; /** * @class ISession */ class KDEVPLATFORMINTERFACES_EXPORT ISession : public QObject { Q_OBJECT public: explicit ISession( QObject* parent = nullptr ); ~ISession() override; /** * A short string nicely identifying the session, including contained projects * * The string is empty if the session is empty and has no name. */ virtual QString description() const = 0; virtual QString name() const = 0; virtual QList containedProjects() const = 0; virtual void setContainedProjects( const QList& projects ) = 0; virtual QUrl pluginDataArea( const IPlugin* ) = 0; virtual KSharedConfigPtr config() = 0; virtual QUuid id() const = 0; /** * Mark session as temporary. It will then be deleted on close. * * This is mainly useful for unit tests etc. */ virtual void setTemporary(bool temp) = 0; virtual bool isTemporary() const = 0; Q_SIGNALS: void sessionUpdated( KDevelop::ISession* session ); }; } Q_DECLARE_METATYPE( KDevelop::ISession* ) #endif diff --git a/interfaces/istatus.h b/interfaces/istatus.h index e1ea6220f..dbfcba380 100644 --- a/interfaces/istatus.h +++ b/interfaces/istatus.h @@ -1,86 +1,86 @@ /*************************************************************************** * 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 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_ISTATUS_H #define KDEVPLATFORM_ISTATUS_H #include "interfacesexport.h" -#include +#include namespace KDevelop { /** * An interface for plugins, languages etc. to provide status updates. * Plugins only have to implement this extension interface, registration * happens automagically. * Regular QObjects can register themselves through IUiController like so: * @code * Foo* f; // some QObject that implements IStatus * ICore::self()->uiController()->registerStatus(f); * @endcode */ class KDEVPLATFORMINTERFACES_EXPORT IStatus { public: virtual ~IStatus(); /** * Return a name for the status object */ virtual QString statusName() const = 0; Q_SIGNALS: /** * Request the current message for this plugin to be cleared. */ virtual void clearMessage( IStatus* ) = 0; /** * Request a status \a message to be shown for this plugin, with a given \a timeout. * * \param message Message to display * \param timeout Timeout in milliseconds, or pass 0 for no timeout. */ virtual void showMessage( IStatus*, const QString & message, int timeout = 0) = 0; /** * Request an error \a message to be shown for this plugin, with a given \a timeout. * * \param message Message to display * \param timeout Timeout in milliseconds, or pass 0 for no timeout. */ virtual void showErrorMessage(const QString & message, int timeout = 0) = 0; /** * Hide the progress bar. */ virtual void hideProgress( IStatus* ) = 0; /** * Show a progress bar, with the given \a percentage. */ virtual void showProgress( IStatus*, int minimum, int maximum, int value) = 0; }; } Q_DECLARE_INTERFACE( KDevelop::IStatus, "org.kdevelop.IStatus" ) #endif diff --git a/interfaces/itemplateprovider.h b/interfaces/itemplateprovider.h index 0162766a2..a05f724c0 100644 --- a/interfaces/itemplateprovider.h +++ b/interfaces/itemplateprovider.h @@ -1,112 +1,112 @@ /* 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. */ #ifndef KDEVPLATFORM_ITEMPLATEPROVIDER_H #define KDEVPLATFORM_ITEMPLATEPROVIDER_H -#include +#include #include "interfacesexport.h" class QIcon; class QAbstractItemModel; class QStringList; namespace KDevelop { /** * @brief A provider of templates * * A template provider loads any kind of source code templates and presents them in a model * via the tempatesModel() function. This model will usually, but not necessarily, be connected * to a tree view, so a tree structure is recommended. * * If the templates have a similar structure as those used by the AppWizard plugin, * the TemplatesModel class may be used for convenience. * * The provider can also support downloading and uploading additional templates with * Get Hot New Stuff. If this is the case, return the name of the configuration file * (ending in .knsrc) from the knsConfigurationFile() function. * * If templates can be loaded from local files, the supportedMimeTypes() should return * all file types the provider can load. If loading is not supported, return an empty list. * * @sa TemplatesModel **/ class KDEVPLATFORMINTERFACES_EXPORT ITemplateProvider { public: /** * Destructor **/ virtual ~ITemplateProvider(); /** * @return The name of this provider. **/ virtual QString name() const = 0; /** * @return An icon associated with this provider. **/ virtual QIcon icon() const = 0; /** * @return A model containing all available templates. * * The called does not take ownership of the model. **/ virtual QAbstractItemModel* templatesModel() = 0; /** * @return The configuration file for Get Hot New Stuff. * * If GHNS is not used by this provider, return an empty string. **/ virtual QString knsConfigurationFile() const = 0; /** * @return Types of files this provider can load. * * If loading is not supported, return an empty list. **/ virtual QStringList supportedMimeTypes() const = 0; /** * Load a template from the file @p fileName. * * This function will only be called if @c supportedMimeTypes() returns * a non-empty list. * * @param fileName the name of the file to load. **/ virtual void loadTemplate(const QString& fileName) = 0; /** * Reloads all template data. * * This is usually called after loading or updating new templates. **/ virtual void reload() = 0; }; } Q_DECLARE_INTERFACE( KDevelop::ITemplateProvider, "org.kdevelop.ITemplateProvider") #endif // KDEVPLATFORM_ITEMPLATEPROVIDER_H diff --git a/interfaces/itestcontroller.h b/interfaces/itestcontroller.h index febd38793..9a1eba6d0 100644 --- a/interfaces/itestcontroller.h +++ b/interfaces/itestcontroller.h @@ -1,141 +1,141 @@ /* 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. */ #ifndef KDEVPLATFORM_ITESTCONTROLLER_H #define KDEVPLATFORM_ITESTCONTROLLER_H #include "interfacesexport.h" -#include -#include -#include +#include +#include +#include #include class QStringList; namespace KDevelop { struct TestResult; class IProject; class ITestSuite; /** * The result of a single unit test run **/ struct KDEVPLATFORMINTERFACES_EXPORT TestResult { /** * Enumeration of possible test case results **/ enum TestCaseResult { NotRun, ///< The test case was not selected for running. Skipped, ///< The test case was skipped. Passed, ///< The test case was run and passed. Failed, ///< The test case was run and failed. ExpectedFail, ///< The test case was expected to fail, and did. UnexpectedPass, ///< The test case was expected to fail, but passed. Error, ///< There was an error while trying to run the test case. }; /** * The individual results of all test cases. **/ QHash testCaseResults; /** * The total result of the entire suite. * * This is usually the worst outcome of the individual test cases, * but can be different especially when dealing with errors. */ TestCaseResult suiteResult; }; class KDEVPLATFORMINTERFACES_EXPORT ITestController : public QObject { Q_OBJECT public: explicit ITestController(QObject* parent = nullptr); ~ITestController() override; /** * Add a new test suite. * * No ownership is taken, the caller stays responsible for the suite. * * If a suite with the same project and same name exists, the old one will be removed and deleted. */ virtual void addTestSuite(ITestSuite* suite) = 0; /** * Remove a test suite from the controller. * * This does not delete the test suite. */ virtual void removeTestSuite(ITestSuite* suite) = 0; /** * Returns the list of all known test suites. */ virtual QList testSuites() const = 0; /** * Find a test suite in @p project with @p name. */ virtual ITestSuite* findTestSuite(IProject* project, const QString& name) const = 0; /** * Return the list of all test suites in @p project. */ virtual QList testSuitesForProject(IProject* project) const = 0; /** * Notify the controller that a test run for @p suite was finished with result @p result */ virtual void notifyTestRunFinished(ITestSuite* suite, const KDevelop::TestResult& result) = 0; /** * Notify the controller that a test run for @p suite was started */ virtual void notifyTestRunStarted(KDevelop::ITestSuite* suite, const QStringList& test_cases) = 0; Q_SIGNALS: /** * Emitted whenever a new test suite gets added. */ void testSuiteAdded(KDevelop::ITestSuite* suite) const; /** * Emitted whenever a test suite gets removed. */ void testSuiteRemoved(KDevelop::ITestSuite* suite) const; /** * Emitted after a test suite was run. */ void testRunFinished(KDevelop::ITestSuite* suite, const KDevelop::TestResult& result) const; /** * Emitted when a test suite starts. */ void testRunStarted(KDevelop::ITestSuite* suite, const QStringList& test_cases) const; }; } Q_DECLARE_INTERFACE( KDevelop::ITestController, "org.kdevelop.ITestController") #endif // KDEVPLATFORM_ITESTCONTROLLER_H diff --git a/interfaces/itoolviewactionlistener.h b/interfaces/itoolviewactionlistener.h index 0cf2b4d2d..c63b82a83 100644 --- a/interfaces/itoolviewactionlistener.h +++ b/interfaces/itoolviewactionlistener.h @@ -1,53 +1,53 @@ /*************************************************************************** * Copyright 2015 Kevin Funk * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_ITOOLVIEWACTIONLISTENER_H #define KDEVPLATFORM_ITOOLVIEWACTIONLISTENER_H #include "interfacesexport.h" -#include +#include namespace KDevelop { /** * An interface for tool view widgets * * Implement this interface for the widgets which are being created by KDevelop::IToolViewFactory::create() * * This interface allows global shortcuts, such as "Jump to Next Output Mark", to map to the tool view widget * which was last recently activated ("raised"). * * @sa KDevelop::IToolViewFactory::create() */ class KDEVPLATFORMINTERFACES_EXPORT IToolViewActionListener { public: virtual ~IToolViewActionListener(); public Q_SLOTS: virtual void selectPreviousItem() = 0; virtual void selectNextItem() = 0; }; } Q_DECLARE_INTERFACE(KDevelop::IToolViewActionListener, "org.kdevelop.IToolViewActionListener") #endif diff --git a/language/classmodel/classmodel.h b/language/classmodel/classmodel.h index f2c808d59..f568ede2f 100644 --- a/language/classmodel/classmodel.h +++ b/language/classmodel/classmodel.h @@ -1,153 +1,153 @@ /* * KDevelop Class Browser * * Copyright 2007-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. */ #ifndef KDEVPLATFORM_CLASSMODEL_H #define KDEVPLATFORM_CLASSMODEL_H -#include +#include #include "classmodelnode.h" #include class ClassBrowserPlugin; namespace KDevelop { class TopDUContext; class IDocument; class DUContext; class IProject; class DUChainBase; class IndexedQualifiedIdentifier; } namespace ClassModelNodes { class Node; class FilteredAllClassesFolder; class FilteredProjectFolder; class FolderNode; class IdentifierNode; } /// The model interface accessible from the nodes. class NodesModelInterface { public: virtual ~NodesModelInterface(); public: enum Feature { AllProjectsClasses = 0x1, BaseAndDerivedClasses = 0x2, ClassInternals = 0x4 }; Q_DECLARE_FLAGS(Features, Feature) virtual void nodesLayoutAboutToBeChanged(ClassModelNodes::Node* a_parent) = 0; virtual void nodesLayoutChanged(ClassModelNodes::Node* a_parent) = 0; virtual void nodesRemoved(ClassModelNodes::Node* a_parent, int a_first, int a_last) = 0; virtual void nodesAboutToBeAdded(ClassModelNodes::Node* a_parent, int a_pos, int a_size) = 0; virtual void nodesAdded(ClassModelNodes::Node* a_parent) = 0; virtual Features features() const = 0; }; /** * @short A model that holds a convinient representation of the defined class in the project * * This model doesn't have much code in it, it mostly acts as a glue between the different * nodes and the tree view. * * The nodes are defined in the namespace @ref ClassModelNodes */ class KDEVPLATFORMLANGUAGE_EXPORT ClassModel : public QAbstractItemModel, public NodesModelInterface { Q_OBJECT public: ClassModel(); ~ClassModel() override; public: /// Retrieve the DU object related to the specified index. /// @note DUCHAINS READER LOCK MUST BE TAKEN! KDevelop::DUChainBase* duObjectForIndex(const QModelIndex& a_index); /// Call this to retrieve the index for the node associated with the specified id. QModelIndex getIndexForIdentifier(const KDevelop::IndexedQualifiedIdentifier& a_id); /// Return the model index associated with the given node. QModelIndex index(ClassModelNodes::Node* a_node) const; inline void setFeatures(NodesModelInterface::Features features); inline NodesModelInterface::Features features() const override { return m_features; } public Q_SLOTS: /// Call this to update the filter string for the search results folder. void updateFilterString(QString a_newFilterString); /// removes the project-specific node void removeProjectNode(KDevelop::IProject* project); /// adds the project-specific node void addProjectNode(KDevelop::IProject* project); private: // NodesModelInterface overrides void nodesLayoutAboutToBeChanged(ClassModelNodes::Node* a_parent) override; void nodesLayoutChanged(ClassModelNodes::Node* a_parent) override; void nodesRemoved(ClassModelNodes::Node* a_parent, int a_first, int a_last) override; void nodesAboutToBeAdded(ClassModelNodes::Node* a_parent, int a_pos, int a_size) override; void nodesAdded(ClassModelNodes::Node* a_parent) override; private: /// Main level node - it's usually invisible. ClassModelNodes::Node* m_topNode; ClassModelNodes::FilteredAllClassesFolder* m_allClassesNode; QMap m_projectNodes; NodesModelInterface::Features m_features; public Q_SLOTS: /// This slot needs to be attached to collapsed signal in the tree view. void collapsed(const QModelIndex& index); /// This slot needs to be attached to expanded signal in the tree view. void expanded(const QModelIndex& index); public: // QAbstractItemModel overrides QFlags< Qt::ItemFlag > flags(const QModelIndex&) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; bool hasChildren(const QModelIndex& parent = QModelIndex()) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex& child) const override; }; inline void ClassModel::setFeatures(Features features) { m_features = features; } Q_DECLARE_OPERATORS_FOR_FLAGS(NodesModelInterface::Features) #endif // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/language/codecompletion/abstractincludefilecompletionitem.h b/language/codecompletion/abstractincludefilecompletionitem.h index 8081e9ced..e748fe24f 100644 --- a/language/codecompletion/abstractincludefilecompletionitem.h +++ b/language/codecompletion/abstractincludefilecompletionitem.h @@ -1,98 +1,98 @@ /* * KDevelop Code Completion Support for file includes * * Copyright 2007-2008 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. */ #ifndef KDEVPLATFORM_ABSTRACTINCLUDEFILECOMPLETIONITEM_H #define KDEVPLATFORM_ABSTRACTINCLUDEFILECOMPLETIONITEM_H #include "codecompletionitem.h" #include #include "../util/includeitem.h" #include "../duchain/duchain.h" #include "../duchain/duchainlock.h" #include "codecompletionmodel.h" -#include "QtCore/QDebug" +#include namespace KDevelop { //A completion item used for completing include-files template class AbstractIncludeFileCompletionItem : public CompletionTreeItem { public: AbstractIncludeFileCompletionItem(const IncludeItem& include) : includeItem(include) { } virtual QVariant data(const QModelIndex& index, int role, const KDevelop::CodeCompletionModel* model) const { DUChainReadLocker lock(DUChain::lock(), 500); if(!lock.locked()) { qDebug() << "Failed to lock the du-chain in time"; return QVariant(); } const IncludeItem& item( includeItem ); switch (role) { case CodeCompletionModel::IsExpandable: return QVariant(true); case CodeCompletionModel::ExpandingWidget: { NavigationWidget* nav = new NavigationWidget(item, model->currentTopContext()); model->addNavigationWidget(this, nav); QVariant v; v.setValue((QWidget*)nav); return v; } case Qt::DisplayRole: switch (index.column()) { case CodeCompletionModel::Prefix: if(item.isDirectory) return QStringLiteral("directory"); else return QStringLiteral("file"); case CodeCompletionModel::Name: { return item.isDirectory ? (item.name + QLatin1Char('/')) : item.name; } } break; case CodeCompletionModel::ItemSelected: { return QVariant( NavigationWidget::shortDescription(item) ); } } return QVariant(); } virtual void execute(KTextEditor::View* view, const KTextEditor::Range& word) = 0; virtual int inheritanceDepth() const { return includeItem.pathNumber; } virtual int argumentHintDepth() const { return 0; } IncludeItem includeItem; }; } #endif // KDEVPLATFORM_ABSTRACTINCLUDEFILECOMPLETIONITEM_H diff --git a/language/codecompletion/codecompletion.h b/language/codecompletion/codecompletion.h index c8dd44cfb..14548b68d 100644 --- a/language/codecompletion/codecompletion.h +++ b/language/codecompletion/codecompletion.h @@ -1,78 +1,78 @@ /* * KDevelop Generic Code Completion Support * * Copyright 2006 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. */ #ifndef KDEVPLATFORM_CODECOMPLETION_H #define KDEVPLATFORM_CODECOMPLETION_H -#include +#include #include namespace KTextEditor { class Document; class View; class CodeCompletionModel; } namespace KDevelop { class IDocument; class ILanguage; // TODO: cleanup this class for 5.1 class KDEVPLATFORMLANGUAGE_EXPORT CodeCompletion : public QObject { Q_OBJECT public: /** CodeCompletion will be the @p aModel parent. * If @p language is empty, the completion model will work for all files, * otherwise only for ones that contain the selected language. */ CodeCompletion(QObject* parent, KTextEditor::CodeCompletionModel* aModel, const QString& language); ~CodeCompletion() override; private Q_SLOTS: void textDocumentCreated(KDevelop::IDocument*); void viewCreated(KTextEditor::Document *document, KTextEditor::View *view); void documentUrlChanged(KDevelop::IDocument*); /** * check already opened documents, * needs to be done via delayed call to prevent infinite loop in * checkDocument() -> load lang plugin -> register CodeCompletion -> checkDocument() -> ... */ void checkDocuments(); signals: void registeredToView(KTextEditor::View* view); void unregisteredFromView(KTextEditor::View* view); private: void unregisterDocument(KTextEditor::Document*); void checkDocument(KTextEditor::Document*); KTextEditor::CodeCompletionModel* m_model; QString m_language; }; } #endif diff --git a/language/codecompletion/codecompletioncontext.cpp b/language/codecompletion/codecompletioncontext.cpp index 005a50132..1d8b7198b 100644 --- a/language/codecompletion/codecompletioncontext.cpp +++ b/language/codecompletion/codecompletioncontext.cpp @@ -1,93 +1,93 @@ /* 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 "codecompletioncontext.h" #include "util/debug.h" #include #include -#include +#include using namespace KDevelop; typedef PushValue IntPusher; ///Extracts the last line from the given string QString CodeCompletionContext::extractLastLine(const QString& str) { int prevLineEnd = str.lastIndexOf('\n'); if(prevLineEnd != -1) return str.mid(prevLineEnd+1); else return str; } int completionRecursionDepth = 0; CodeCompletionContext::CodeCompletionContext(DUContextPointer context, const QString& text, const KDevelop::CursorInRevision& position, int depth) : m_text(text), m_depth(depth), m_valid(true), m_position(position), m_duContext(context), m_parentContext(nullptr) { IntPusher( completionRecursionDepth, completionRecursionDepth+1 ); if( depth > 10 ) { qCWarning(LANGUAGE) << "too much recursion"; m_valid = false; return; } if( completionRecursionDepth > 10 ) { qCWarning(LANGUAGE) << "too much recursion"; m_valid = false; return; } } CodeCompletionContext::~CodeCompletionContext() { } int CodeCompletionContext::depth() const { return m_depth; } bool CodeCompletionContext::isValid() const { return m_valid; } void KDevelop::CodeCompletionContext::setParentContext(QExplicitlySharedDataPointer< KDevelop::CodeCompletionContext > newParent) { m_parentContext = newParent; int newDepth = m_depth+1; while(newParent) { newParent->m_depth = newDepth; ++newDepth; newParent = newParent->m_parentContext; } } CodeCompletionContext* CodeCompletionContext::parentContext() { return m_parentContext.data(); } QList< QExplicitlySharedDataPointer< KDevelop::CompletionTreeElement > > KDevelop::CodeCompletionContext::ungroupedElements() { return QList< QExplicitlySharedDataPointer< KDevelop::CompletionTreeElement > >(); } KDevelop::DUContext* KDevelop::CodeCompletionContext::duContext() const { return m_duContext.data(); } diff --git a/language/codecompletion/codecompletionmodel.h b/language/codecompletion/codecompletionmodel.h index 4f3585510..310b28ee6 100644 --- a/language/codecompletion/codecompletionmodel.h +++ b/language/codecompletion/codecompletionmodel.h @@ -1,148 +1,148 @@ /* * KDevelop Generic Code Completion Support * * Copyright 2006-2008 Hamish Rodda * Copyright 2007-2008 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. */ #ifndef KDEVPLATFORM_CODECOMPLETIONMODEL_H #define KDEVPLATFORM_CODECOMPLETIONMODEL_H -#include -#include -#include +#include +#include +#include #include #include #include "../duchain/duchainpointer.h" #include #include "codecompletioncontext.h" #include "codecompletionitem.h" #include #include class QMutex; namespace KDevelop { class DUContext; class Declaration; class CodeCompletionWorker; class CompletionWorkerThread; class KDEVPLATFORMLANGUAGE_EXPORT CodeCompletionModel : public KTextEditor::CodeCompletionModel , public KTextEditor::CodeCompletionModelControllerInterface { Q_OBJECT Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface) public: explicit CodeCompletionModel(QObject* parent); ~CodeCompletionModel() override; ///This MUST be called after the creation of this completion-model. ///If you use use the KDevelop::CodeCompletion helper-class, that one cares about it. virtual void initialize(); ///Entry-point for code-completion. This determines ome settings, clears the model, and then calls completionInvokedInternal for further processing. void completionInvoked(KTextEditor::View* view, const KTextEditor::Range& range, KTextEditor::CodeCompletionModel::InvocationType invocationType) override; QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int rowCount ( const QModelIndex & parent = QModelIndex() ) const override; QModelIndex parent ( const QModelIndex & index ) const override; ///Use this to set whether the code-completion widget should wait for this model until it's shown ///This makes sense when the model takes some time but not too much time, to make the UI less flickering and ///annoying. ///The default is false ///@todo Remove this option, make it true by default, and make sure in CodeCompletionWorker that the whole thing cannot break void setForceWaitForModel(bool wait); bool forceWaitForModel(); ///Convenience-storage for use by the inherited completion model void setCompletionContext(QExplicitlySharedDataPointer completionContext); QExplicitlySharedDataPointer completionContext() const; ///Convenience-storage for use by the inherited completion model KDevelop::TopDUContextPointer currentTopContext() const; void setCurrentTopContext(KDevelop::TopDUContextPointer topContext); ///Tracks navigation widget so they can be interactive with through the keyboard later on void addNavigationWidget(const CompletionTreeElement* element, QWidget* widget) const; ///Whether the completion should be fully detailed. If false, it should be simplifed, so no argument-hints, ///no expanding information, no type-information, etc. bool fullCompletion() const; MatchReaction matchingItem(const QModelIndex& matched) override; QString filterString(KTextEditor::View* view, const KTextEditor::Range& range, const KTextEditor::Cursor& position) override; void clear(); ///Returns the tree-element that belogns to the index, or zero QExplicitlySharedDataPointer itemForIndex(QModelIndex index) const; Q_SIGNALS: ///Connection from this completion-model into the background worker thread. You should emit this from within completionInvokedInternal. void completionsNeeded(KDevelop::DUContextPointer context, const KTextEditor::Cursor& position, KTextEditor::View* view); ///Additional signal that allows directly stepping into the worker-thread, bypassing computeCompletions(..) etc. ///doSpecialProcessing(data) will be executed in the background thread. void doSpecialProcessingInBackground(uint data); protected Q_SLOTS: ///Connection from the background-thread into the model: This is called when the background-thread is ready virtual void foundDeclarations(const QList>& item, const QExplicitlySharedDataPointer& completionContext); protected: ///Eventually override this, determine the context or whatever, and then emit completionsNeeded(..) to continue processing in the background tread. ///The default-implementation does this completely, so if you don't need to do anything special, you can just leave it. virtual void completionInvokedInternal(KTextEditor::View* view, const KTextEditor::Range& range, KTextEditor::CodeCompletionModel::InvocationType invocationType, const QUrl& url); void executeCompletionItem(KTextEditor::View* view, const KTextEditor::Range& word, const QModelIndex& index) const override; QExplicitlySharedDataPointer m_completionContext; typedef QPair > DeclarationContextPair; mutable QMap > m_navigationWidgets; QList< QExplicitlySharedDataPointer > m_completionItems; /// Should create a completion-worker. The worker must have no parent object, /// because else its thread-affinity can not be changed. virtual CodeCompletionWorker* createCompletionWorker() = 0; friend class CompletionWorkerThread; CodeCompletionWorker* worker() const; private: bool m_forceWaitForModel; bool m_fullCompletion; QMutex* m_mutex; CompletionWorkerThread* m_thread; QString m_filterString; KDevelop::TopDUContextPointer m_currentTopContext; }; } #endif diff --git a/language/codecompletion/codecompletionworker.h b/language/codecompletion/codecompletionworker.h index bd4ca8f06..464a8717e 100644 --- a/language/codecompletion/codecompletionworker.h +++ b/language/codecompletion/codecompletionworker.h @@ -1,110 +1,110 @@ /* * KDevelop Generic Code Completion Support * * Copyright 2006-2008 Hamish Rodda * Copyright 2007-2008 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. */ #ifndef KDEVPLATFORM_CODECOMPLETIONWORKER_H #define KDEVPLATFORM_CODECOMPLETIONWORKER_H -#include +#include #include #include "../duchain/duchainpointer.h" #include "../codecompletion/codecompletioncontext.h" class QMutex; namespace KTextEditor { class Range; class View; class Cursor; } namespace KDevelop { class CompletionTreeElement; class CodeCompletionModel; class KDEVPLATFORMLANGUAGE_EXPORT CodeCompletionWorker : public QObject { Q_OBJECT public: explicit CodeCompletionWorker(CodeCompletionModel* model); ~CodeCompletionWorker() override; virtual void abortCurrentCompletion(); void setFullCompletion(bool); bool fullCompletion() const; KDevelop::CodeCompletionModel* model() const; ///When this is called, the result is shown in the completion-list. ///Call this from within your code void foundDeclarations(const QList>&, const CodeCompletionContext::Ptr& completionContext); Q_SIGNALS: ///Internal connections into the foreground completion model void foundDeclarationsReal(const QList>&, const QExplicitlySharedDataPointer& completionContext); protected: virtual void computeCompletions(DUContextPointer context, const KTextEditor::Cursor& position, QString followingText, const KTextEditor::Range& contextRange, const QString& contextText); ///This can be overridden to compute an own grouping in the completion-list. ///The default implementation groups items in a way that improves the efficiency of the completion-model, thus the default-implementation should be preferred. virtual QList > computeGroups(QList items, QExplicitlySharedDataPointer completionContext); ///If you don't need to reimplement computeCompletions, you can implement only this. virtual KDevelop::CodeCompletionContext* createCompletionContext(KDevelop::DUContextPointer context, const QString &contextText, const QString &followingText, const CursorInRevision &position) const; ///Override this to change the text-range which is used as context-information for the completion context ///The foreground-lock and a DUChain read lock are held when this is called virtual void updateContextRange(KTextEditor::Range& contextRange, KTextEditor::View* view, DUContextPointer context) const; ///Can be used to retrieve and set the aborting flag(Enabling it is equivalent to caling abortCompletion()) ///Is always reset from within computeCompletions bool& aborting(); ///Emits foundDeclarations() with an empty list. Always call this when you abort the process of computing completions void failed(); public Q_SLOTS: ///Connection from the foreground thread within CodeCompletionModel void computeCompletions(KDevelop::DUContextPointer context, const KTextEditor::Cursor& position, KTextEditor::View* view); ///This can be used to do special processing within the background, completely bypassing the normal computeCompletions(..) etc. system. ///It will be executed within the background when the model emits doSpecialProcessingInBackground virtual void doSpecialProcessing(uint data); private: bool m_hasFoundDeclarations; QMutex* m_mutex; bool m_abort; bool m_fullCompletion; KDevelop::CodeCompletionModel* m_model; }; } #endif // KDEVPLATFORM_CODECOMPLETIONWORKER_H diff --git a/language/codegen/astchangeset.h b/language/codegen/astchangeset.h index a8d8a8d2c..1f7bb9be3 100644 --- a/language/codegen/astchangeset.h +++ b/language/codegen/astchangeset.h @@ -1,249 +1,249 @@ /* Copyright 2008 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 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. */ #ifndef KDEVPLATFORM_ASTCHANGESET_H #define KDEVPLATFORM_ASTCHANGESET_H -#include +#include namespace KDevelop { template class AstChangeSet; class AstChange; /** * \short A reference to an existing read-only AST node. * * This class represents an AST node, and allows changes to be planned * for that node. * * \warning you must not create cyclic references. * * \author Hamish Rodda */ template class AstNodeRef { friend class AstChangeSet; public: /// Destructor. virtual ~AstNodeRef() { qDeleteAll(m_changes); if (m_newNode) delete m_node; } typedef QList AstNodeList; /** * \short Container class for a change to an AST node. * * \author Hamish Rodda */ class AstChange { public: enum ChangeTypes { ListRewrite, ListClear, ItemReplace, ItemMove }; explicit AstChange(ChangeTypes t) : type(t) , newNode(0) , listOffset(-1) , newValue(-1) { } ChangeTypes type; // The index of the item in the node to be changed int nodeIndex; // The new node to occupy this position, if relevant AstNodeRef* newNode; // The list of nodes to occupy this position, if relevant AstNodeList newList; // The position to apply the node(s) in the list, if relevant int listOffset; // The value of the position, if relevant QVariant newValue; }; virtual const AstNode* node() const { return m_newNode ? 0 : m_node; } virtual AstNodeRef* nodeRef() const { return m_nodeRef; } virtual AstNode* newNode() const { return m_newNode ? m_node : 0; } const QList& changes() const { return m_changes; } /// Adds a change to this node reference. Takes ownership of the \a change. AstChange* newChange(AstChange* change) { m_changes.append(change); return change; } /// Removes a change from this node reference, and deletes it. void deleteChange(AstChange* change) { Q_ASSERT(m_changes.contains(change)); m_changes.removeAll(change); delete change; } protected: /// Default constructor. \todo is this needed? AstNodeRef(AstChangeSet* set) : m_changeSet(set) , m_node(0) , m_nodeRef(0) , m_newNode(false) { } /// Constructor. Either takes an existing \a node (\a newNode = false), or a newly created \a node (\a newNode = true) AstNodeRef(AstChangeSet* set, AstNode* node, bool newNode) : m_changeSet(set) , m_node(node) , m_nodeRef(0) , m_newNode(newNode) { } /// Constructor. Takes another node reference. AstNodeRef(AstChangeSet* set, AstNodeRef* original) : m_changeSet(set) , m_node(0) , m_nodeRef(original) , m_newNode(false) { } AstNode* m_nodeChanges; private: AstChangeSet* m_changeSet; AstNode* m_node; AstNodeRef* m_nodeRef; bool m_newNode; QList m_changes; }; /** * \short A set of changes to an AST. * * This class holds a set of all changes to an AST. * * \author Hamish Rodda */ template class AstChangeSet { public: /** * Constructor. * * \param topNode the top node of the read-only Ast to modify, or set to null if creating * a new Ast from scratch. */ AstChangeSet(const AstNode* topNode = 0) : m_topNode(topNode) { } /** * Destructor, deletes all nodes owned by this change set. */ virtual ~AstChangeSet() { qDeleteAll(m_nodeRefs); } /** * Register a new node that you have created to insert at some point in this Ast. * You may modify this node directly. The change set takes ownership, so that * the new node will be deleted when the change set is no longer needed. * * \returns the new node that has been registered. */ AstNodeRef* registerNewNode(AstNode* node) { AstNodeRef* newRef = new AstNodeRef(this, node, true); m_nodeRefs.append(newRef); return newRef; } /** * Create a blank reference to a node. * * The change set takes ownership, so that * the new node will be deleted when the change set is no longer needed. * * \returns the new node reference */ AstNodeRef* registerNewRef(AstNodeRef* ref) { m_nodeRefs.append(ref); return ref; } /** * Copy an existing node (whether from the Ast or from the change set). * * You may then modify this reference, and the modifications will be applied to the node when the change set is finalised. * * \returns a copy of \a source, which you may modify directly. */ AstNodeRef* copyNode(AstNode* source) { AstNodeRef* newRef = new AstNodeRef(this, source, false); m_nodeRefs.append(newRef); return newRef; } private: const AstNode* m_topNode; QList*> m_nodeRefs; }; } #endif // KDEVPLATFORM_ASTCHANGESET_H diff --git a/language/codegen/basicrefactoring.h b/language/codegen/basicrefactoring.h index 6ef5f9477..f2d4fffb0 100644 --- a/language/codegen/basicrefactoring.h +++ b/language/codegen/basicrefactoring.h @@ -1,162 +1,162 @@ /* This file is part of KDevelop * * Copyright 2014 Miquel Sabaté * * 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. */ #ifndef BASICREFACTORING_H_ #define BASICREFACTORING_H_ -#include +#include #include #include #include #include class CppLanguageSupport; namespace KDevelop { class ContextMenuExtension; class IndexedDeclaration; class Context; class Declaration; class DUContext; /** * A widget that show the uses that it has collected for * the given declaration. */ class KDEVPLATFORMLANGUAGE_EXPORT BasicRefactoringCollector : public UsesWidget::UsesWidgetCollector { Q_OBJECT public: explicit BasicRefactoringCollector(const IndexedDeclaration &decl); QVector allUsingContexts() const; protected: /// Process the uses for the given TopDUContext. void processUses(KDevelop::ReferencedTopDUContext topContext) override; private: QVector m_allUsingContexts; }; /// The base class for Refactoring classes from Language plugins. class KDEVPLATFORMLANGUAGE_EXPORT BasicRefactoring : public QObject { Q_OBJECT public: explicit BasicRefactoring(QObject *parent = nullptr); /// Update the context menu with the "Rename" action. virtual void fillContextMenu(KDevelop::ContextMenuExtension &extension, KDevelop::Context *context); struct NameAndCollector { QString newName; QSharedPointer collector; }; /** * @return Suggestion for new filename based on the current file's name @p current and new identifer @p newName */ virtual QString newFileName(const QUrl& current, const QString& newName); /** * Add the change(s) related to renaming @p file to @p newName to @p changes and return the result. * * @param current The URL for the file you want to rename. * @param newName The new name of the file *without* the file extension. * @param changes The change set to add the rename changes to. */ virtual KDevelop::DocumentChangeSet::ChangeResult addRenameFileChanges(const QUrl& current, const QString& newName, KDevelop::DocumentChangeSet* changes); virtual bool shouldRenameUses(Declaration* declaration) const; /** * @return true if the declaration's file should be renamed if the declaration * was renamed. */ virtual bool shouldRenameFile(KDevelop::Declaration* declaration); public slots: void executeRenameAction(); protected: /** * Apply the changes to the uses that can be found inside the given * context and its children. * NOTE: the DUChain must be locked. */ virtual DocumentChangeSet::ChangeResult applyChanges(const QString &oldName, const QString &newName, DocumentChangeSet &changes, DUContext *context, int usedDeclarationIndex); /** * Apply the changes to the given declarations. * NOTE: the DUChain must be locked. */ virtual DocumentChangeSet::ChangeResult applyChangesToDeclarations(const QString &oldName, const QString &newName, DocumentChangeSet &changes, const QList &declarations); /** * Get the declaration under the current position of the cursor. * * @param allowUse Set to false if the declarations to be returned * cannot come from uses. */ virtual IndexedDeclaration declarationUnderCursor(bool allowUse = true); /** * Start the renaming of a declaration. * This function retrieves the new name for declaration @p decl and if approved renames all instances of it. */ virtual void startInteractiveRename(const KDevelop::IndexedDeclaration &decl); /** * Asks user to input a new name for @p declaration * @return new name or empty string if user changed his mind or new name contains inappropriate symbols (e.g. spaces, points, braces e.t.c) and the collector used for collecting information about @p declaration. * NOTE: unlock the DUChain before calling this. */ virtual BasicRefactoring::NameAndCollector newNameForDeclaration(const KDevelop::DeclarationPointer& declaration); /** * Renames all declarations collected by @p collector from @p oldName to @p newName * @param apply - if changes should be applied immediately * @return all changes if @p apply is false and empty set otherwise. */ DocumentChangeSet renameCollectedDeclarations(KDevelop::BasicRefactoringCollector* collector, const QString& replacementName, const QString& originalName, bool apply = true); /** * @returns true if we can show the interactive rename widget for the * given declaration. The default implementation just returns true. */ virtual bool acceptForContextMenu(const Declaration *decl); }; } // End of namespace KDevelop #endif /* BASICREFACTORING_H_ */ diff --git a/language/codegen/duchainchangeset.h b/language/codegen/duchainchangeset.h index 272d912e7..2bccba177 100644 --- a/language/codegen/duchainchangeset.h +++ b/language/codegen/duchainchangeset.h @@ -1,260 +1,260 @@ /* Copyright 2008 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 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. */ #ifndef KDEVPLATFORM_DUCHAINCHANGESET_H #define KDEVPLATFORM_DUCHAINCHANGESET_H -#include +#include #include "../duchain/identifier.h" #include "../duchain/topducontext.h" #include "../duchain/declaration.h" namespace KDevelop { class DUChainChangeSet; class DUChainChange; class DUChainBase; class DUContextRef; template class AstNodeRef; /** * \short A reference to an existing read-only DUChain object. * * This class represents a duchain object (eg, a KDevelop::DUContext), * and allows changes to be planned for that object. * * \todo Evaluate usefulness of changing child contexts - needed? * * \warning you must not create cyclic references. * \author Hamish Rodda */ class KDEVPLATFORMLANGUAGE_EXPORT DUChainRef { friend class DUChainChangeSet; public: /*virtual ~DUChainRef(); virtual const DUChainBase* object() const; virtual const DUContext* context() const; virtual const Declaration* declaration() const; virtual DUChainRef* objectRef() const; virtual DUChainBase* newObject() const; virtual DUContext* newContext() const; virtual Declaration* newDeclaration() const; const QList& changes() const; /// Rename this object, if applicable void renameObject(const QualifiedIdentifier& newIdentifier); /// Change the access policy void setAccessPolicy(Declaration::AccessPolicy newPolicy); void deleteChildContext(DUContext* child); void insertChildContext(DUContextRef* newChild); void deleteDeclaration(Declaration* declaration); void insertDeclaration(Declaration* declaration, DUChainBase* afterObject); void appendDeclaration(Declaration* declaration); AbstractType::Ptr currentType() const; void changeType(AbstractType::Ptr newType); */ /** * Rewrite the AST which created this duchain object. Eg: * - for declarations, the entire declaration. * - for contexts, the contents of the context. * - for types, the type declaration. * * \returns a reference to the AST which represents this object as it currently * exists (after any existing duchain changes are applied). Changes * made to the AST will be applied along with the duchain change set. */ /* template AstNodeRef * rewriteAst(); /// Removes a change from this object reference, and deletes it. void deleteChange(DUChainChange* change); protected: /// Constructor. Either takes an existing \a object (\a newObject = false), or a newly created \a object (\a newObject = true) DUChainRef(DUChainChangeSet* set, DUChainBase* object, bool newObject); /// Constructor. Takes another object reference. DUChainRef(DUChainChangeSet* set, DUChainRef* original); /// Adds a change to this object reference. Takes ownership of the \a change. DUChainChange* addChange(DUChainChange* change); private: DUChainChangeSet* m_changeSet; DUChainBase* m_object; DUChainRef* m_objectRef; bool m_newObject; QList m_changes;*/ }; typedef QList DUChainBaseList; /** * \short Container class for a change to a duchain object. * * \author Hamish Rodda */ class KDEVPLATFORMLANGUAGE_EXPORT DUChainChange { public: enum ChangeTypes { Rename, ListInsert, ListRemove, ListClear, ItemReplace, ItemMove, TypeChange } type; explicit DUChainChange(ChangeTypes t) : type(t) {} enum ItemToChange { ContextChildren, ContextDeclarations } itemToChange; /// New local identifier (eg. for contexts, the new DUContext::localScopeIdentifier() ) QualifiedIdentifier newIdentifier; /// The new object to occupy this position, if relevant DUChainRef* newObject; /// The list of objects to occupy this position, if relevant DUChainBaseList newList; /// The position to apply the object(s) in the list, if relevant int listOffset; /// The value of the position, if relevant QVariant newValue; AbstractType::Ptr newType; }; /** * \short A set of changes to a DUChain. * * This class holds a set of all changes to a DU Chain, and provides an interface * to convenience functions provided by the specific language support involved. * * \author Hamish Rodda */ class KDEVPLATFORMLANGUAGE_EXPORT DUChainChangeSet { public: /** * Constructor. * * \param topContext the top context of the read-only DUChain to modify, or set to null if creating * a new DUChain from scratch. */ explicit DUChainChangeSet(ReferencedTopDUContext topContext); /** * Destructor, deletes all objects, references and changes owned by this change set. */ virtual ~DUChainChangeSet(); /** * Create a new declaration to be managed by this change set. * * \returns the new declaration reference */ virtual DUChainRef* newDeclaration() = 0; /** * Create a new class to be managed by this change set. * * \returns the new declaration reference */ virtual DUChainRef* newClass() = 0; /** * Create a new function to be managed by this change set. * * \returns the new declaration reference */ virtual DUChainRef* newFunction() = 0; /** * Copy an existing object from a change set. * * This change set takes ownership, so that * the new object will be deleted when the change set is no longer needed. * * \returns the new object reference */ DUChainRef* copyRef(DUChainRef* ref); /** * Merge another changeset with this one. This changeset * takes ownership of all the objects in the other changeset. * After the merge, the merged object becomes empty. * * Both changesets must reference the same TopDuContext. */ DUChainChangeSet & operator<<(DUChainChangeSet & rhs); /** * Produce a reference to an existing object in this chain, and replace the * object with the reference so that modifications to the reference are already * integrated into the change set. * * You may then modify this reference, and the modifications will be applied * to the chain when the change set is finalised. * * \returns a reference to \a source, which you may modify directly. */ DUChainRef* modifyObject(DUChainBase* source); /** * Copy an existing object (whether from the DUChain or from the change set). * Does not insert the object into the chain. * * You may then modify this reference, and the modifications will be applied to the object when the change set is finalised. * * \returns a copy of \a source, which you may modify directly. */ DUChainRef* copyObject(DUChainBase* source); /** * Retrieve the list of object references and changes. */ QList objectRefs() const; const ReferencedTopDUContext & topDuContext() const; private: ReferencedTopDUContext m_topContext; QList m_objectRefs; }; } #endif // KDEVPLATFORM_DUCHAINCHANGESET_H diff --git a/language/codegen/tests/test_documentchangeset.cpp b/language/codegen/tests/test_documentchangeset.cpp index 9f8877239..e853b713d 100644 --- a/language/codegen/tests/test_documentchangeset.cpp +++ b/language/codegen/tests/test_documentchangeset.cpp @@ -1,74 +1,74 @@ /* * This file is part of KDevelop * Copyright 2013 Milian Wolff * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #include "test_documentchangeset.h" #include #include #include #include -#include +#include QTEST_GUILESS_MAIN(TestDocumentchangeset); using namespace KDevelop; void TestDocumentchangeset::initTestCase() { AutoTestShell::init(); TestCore::initialize(Core::NoUi); } void TestDocumentchangeset::cleanupTestCase() { TestCore::shutdown(); } void TestDocumentchangeset::testReplaceSameLine() { TestFile file(QStringLiteral("abc def ghi"), QStringLiteral("cpp")); qDebug() << file.fileContents(); DocumentChangeSet changes; changes.addChange( DocumentChange( file.url(), KTextEditor::Range(0, 0, 0, 3), QStringLiteral("abc"), QStringLiteral("foobar") )); changes.addChange( DocumentChange( file.url(), KTextEditor::Range(0, 4, 0, 7), QStringLiteral("def"), QStringLiteral("foobar") )); changes.addChange( DocumentChange( file.url(), KTextEditor::Range(0, 8, 0, 11), QStringLiteral("ghi"), QStringLiteral("foobar") )); DocumentChangeSet::ChangeResult result = changes.applyAllChanges(); qDebug() << result.m_failureReason << result.m_success; QVERIFY(result); } diff --git a/language/duchain/declaration.cpp b/language/duchain/declaration.cpp index a7dfb0a42..968e9151e 100644 --- a/language/duchain/declaration.cpp +++ b/language/duchain/declaration.cpp @@ -1,798 +1,798 @@ /* 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 "declaration.h" #include "declarationdata.h" -#include +#include #include #include "topducontext.h" #include "topducontextdynamicdata.h" #include "use.h" #include "forwarddeclaration.h" #include "duchain.h" #include "duchainlock.h" #include "ducontextdata.h" #include "declarationid.h" #include "uses.h" #include #include "duchainregister.h" #include "persistentsymboltable.h" #include "types/identifiedtype.h" #include "types/structuretype.h" #include "functiondefinition.h" #include "codemodel.h" #include "specializationstore.h" #include "types/typeutils.h" #include "types/typealiastype.h" #include "classdeclaration.h" #include "serialization/stringrepository.h" #include "ducontextdynamicdata.h" namespace KDevelop { REGISTER_DUCHAIN_ITEM(Declaration); DeclarationData::DeclarationData() : m_comment(0) , m_kind(Declaration::Instance) , m_isDefinition(false) , m_inSymbolTable(false) , m_isTypeAlias(false) , m_anonymousInContext(false) , m_isDeprecated(false) , m_alwaysForceDirect(false) , m_isAutoDeclaration(false) , m_isExplicitlyDeleted(false) { } DeclarationData::DeclarationData( const DeclarationData& rhs ) : DUChainBaseData(rhs), m_internalContext(rhs.m_internalContext), m_type(rhs.m_type), m_identifier(rhs.m_identifier), m_declaration(rhs.m_declaration), m_comment(rhs.m_comment), m_kind(rhs.m_kind), m_isDefinition(rhs.m_isDefinition), m_inSymbolTable(rhs.m_inSymbolTable), m_isTypeAlias(rhs.m_isTypeAlias), m_anonymousInContext(rhs.m_anonymousInContext), m_isDeprecated(rhs.m_isDeprecated), m_alwaysForceDirect(rhs.m_alwaysForceDirect), m_isAutoDeclaration(rhs.m_isAutoDeclaration), m_isExplicitlyDeleted(rhs.m_isExplicitlyDeleted) { } ///@todo Use reference counting static Repositories::StringRepository& commentRepository() { static Repositories::StringRepository commentRepositoryObject(QStringLiteral("Comment Repository")); return commentRepositoryObject; } void initDeclarationRepositories() { commentRepository(); } Declaration::Kind Declaration::kind() const { DUCHAIN_D(Declaration); return d->m_kind; } void Declaration::setKind(Kind kind) { DUCHAIN_D_DYNAMIC(Declaration); d->m_kind = kind; updateCodeModel(); } bool Declaration::inDUChain() const { DUCHAIN_D(Declaration); if( d->m_anonymousInContext ) return false; if( !context() ) return false; TopDUContext* top = topContext(); return top && top->inDUChain(); } Declaration::Declaration( const RangeInRevision& range, DUContext* context ) : DUChainBase(*new DeclarationData, range) { d_func_dynamic()->setClassId(this); m_topContext = nullptr; m_context = nullptr; m_indexInTopContext = 0; if(context) setContext(context); } uint Declaration::ownIndex() const { ENSURE_CAN_READ return m_indexInTopContext; } Declaration::Declaration(const Declaration& rhs) : DUChainBase(*new DeclarationData( *rhs.d_func() )) { m_topContext = nullptr; m_context = nullptr; m_indexInTopContext = 0; } Declaration::Declaration( DeclarationData & dd ) : DUChainBase(dd) { m_topContext = nullptr; m_context = nullptr; m_indexInTopContext = 0; } Declaration::Declaration( DeclarationData & dd, const RangeInRevision& range ) : DUChainBase(dd, range) { m_topContext = nullptr; m_context = nullptr; m_indexInTopContext = 0; } bool Declaration::persistentlyDestroying() const { TopDUContext* topContext = this->topContext(); return !topContext->deleting() || !topContext->isOnDisk(); } Declaration::~Declaration() { uint oldOwnIndex = m_indexInTopContext; TopDUContext* topContext = this->topContext(); //Only perform the actions when the top-context isn't being deleted, or when it hasn't been stored to disk if(persistentlyDestroying()) { DUCHAIN_D_DYNAMIC(Declaration); // Inserted by the builder after construction has finished. if( d->m_internalContext.context() ) d->m_internalContext.context()->setOwner(nullptr); setInSymbolTable(false); } // If the parent-context already has dynamic data, like for example any temporary context, // always delete the declaration, to not create crashes within more complex code like C++ template stuff. if (context() && !d_func()->m_anonymousInContext) { if(!topContext->deleting() || !topContext->isOnDisk() || context()->d_func()->isDynamic()) context()->m_dynamicData->removeDeclaration(this); } clearOwnIndex(); if(!topContext->deleting() || !topContext->isOnDisk()) { setContext(nullptr); setAbstractType(AbstractType::Ptr()); } Q_ASSERT(d_func()->isDynamic() == (!topContext->deleting() || !topContext->isOnDisk() || topContext->m_dynamicData->isTemporaryDeclarationIndex(oldOwnIndex))); Q_UNUSED(oldOwnIndex); } QByteArray Declaration::comment() const { DUCHAIN_D(Declaration); if(!d->m_comment) return nullptr; else return Repositories::arrayFromItem(commentRepository().itemFromIndex(d->m_comment)); } void Declaration::setComment(const QByteArray& str) { DUCHAIN_D_DYNAMIC(Declaration); if(str.isEmpty()) d->m_comment = 0; else d->m_comment = commentRepository().index(Repositories::StringRepositoryItemRequest(str, IndexedString::hashString(str, str.length()), str.length())); } void Declaration::setComment(const QString& str) { setComment(str.toUtf8()); } Identifier Declaration::identifier( ) const { //ENSURE_CAN_READ Commented out for performance reasons return d_func()->m_identifier.identifier(); } const IndexedIdentifier& Declaration::indexedIdentifier( ) const { //ENSURE_CAN_READ Commented out for performance reasons return d_func()->m_identifier; } void Declaration::rebuildDynamicData(DUContext* parent, uint ownIndex) { DUChainBase::rebuildDynamicData(parent, ownIndex); m_context = parent; m_topContext = parent->topContext(); m_indexInTopContext = ownIndex; } void Declaration::setIdentifier(const Identifier& identifier) { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(Declaration); bool wasInSymbolTable = d->m_inSymbolTable; setInSymbolTable(false); d->m_identifier = identifier; setInSymbolTable(wasInSymbolTable); } IndexedType Declaration::indexedType() const { return d_func()->m_type; } AbstractType::Ptr Declaration::abstractType( ) const { //ENSURE_CAN_READ Commented out for performance reasons return d_func()->m_type.abstractType(); } void Declaration::setAbstractType(AbstractType::Ptr type) { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(Declaration); d->m_type = type ? type->indexed() : IndexedType(); updateCodeModel(); } Declaration* Declaration::specialize(const IndexedInstantiationInformation& /*specialization*/, const TopDUContext* topContext, int /*upDistance*/) { if(!topContext) return nullptr; return this; } QualifiedIdentifier Declaration::qualifiedIdentifier() const { ENSURE_CAN_READ QualifiedIdentifier ret; DUContext* ctx = m_context; if(ctx) ret = ctx->scopeIdentifier(true); ret.push(d_func()->m_identifier); return ret; } DUContext * Declaration::context() const { //ENSURE_CAN_READ Commented out for performance reasons return m_context; } bool Declaration::isAnonymous() const { return d_func()->m_anonymousInContext; } void Declaration::setContext(DUContext* context, bool anonymous) { Q_ASSERT(!context || context->topContext()); DUCHAIN_D_DYNAMIC(Declaration); if (context == m_context && anonymous == d->m_anonymousInContext) { // skip costly operations below when the same context is set // this happens often when updating a TopDUContext from the cache return; } setInSymbolTable(false); //We don't need to clear, because it's not allowed to move from one top-context into another // clearOwnIndex(); if (m_context && context) { Q_ASSERT(m_context->topContext() == context->topContext()); } if (m_context) { if( !d->m_anonymousInContext ) { m_context->m_dynamicData->removeDeclaration(this); } } if(context) m_topContext = context->topContext(); else m_topContext = nullptr; d->m_anonymousInContext = anonymous; m_context = context; if (context) { if(!m_indexInTopContext) allocateOwnIndex(); if(!d->m_anonymousInContext) { context->m_dynamicData->addDeclaration(this); } if(context->inSymbolTable() && !anonymous) setInSymbolTable(true); } } void Declaration::clearOwnIndex() { if(!m_indexInTopContext) return; if(!context() || (!d_func()->m_anonymousInContext && !context()->isAnonymous())) { ENSURE_CAN_WRITE } if(m_indexInTopContext) { Q_ASSERT(m_topContext); m_topContext->m_dynamicData->clearDeclarationIndex(this); } m_indexInTopContext = 0; } void Declaration::allocateOwnIndex() { ///@todo Fix multithreading stuff with template instantiation, preferably using some internal mutexes // if(context() && (!context()->isAnonymous() && !d_func()->m_anonymousInContext)) { // ENSURE_CAN_WRITE // } Q_ASSERT(m_topContext); m_indexInTopContext = m_topContext->m_dynamicData->allocateDeclarationIndex(this, d_func()->m_anonymousInContext || !context() || context()->isAnonymous()); Q_ASSERT(m_indexInTopContext); if(!m_topContext->m_dynamicData->getDeclarationForIndex(m_indexInTopContext)) qFatal("Could not re-retrieve declaration\nindex: %d", m_indexInTopContext); } const Declaration* Declaration::logicalDeclaration(const TopDUContext* topContext) const { ENSURE_CAN_READ if(isForwardDeclaration()) { const auto dec = static_cast(this); Declaration* ret = dec->resolve(topContext); if(ret) return ret; } return this; } Declaration* Declaration::logicalDeclaration(const TopDUContext* topContext) { ENSURE_CAN_READ if(isForwardDeclaration()) { const auto dec = static_cast(this); Declaration* ret = dec->resolve(topContext); if(ret) return ret; } return this; } DUContext * Declaration::logicalInternalContext(const TopDUContext* topContext) const { ENSURE_CAN_READ if(!isDefinition()) { Declaration* def = FunctionDefinition::definition(this); if( def ) return def->internalContext(); } if( d_func()->m_isTypeAlias ) { ///If this is a type-alias, return the internal context of the actual type. TypeAliasType::Ptr t = type(); if(t) { AbstractType::Ptr target = t->type(); IdentifiedType* idType = dynamic_cast(target.data()); if( idType ) { Declaration* decl = idType->declaration(topContext); if(decl && decl != this) { return decl->logicalInternalContext( topContext ); } } } } return internalContext(); } DUContext * Declaration::internalContext() const { // ENSURE_CAN_READ return d_func()->m_internalContext.context(); } void Declaration::setInternalContext(DUContext* context) { if(this->context()) { ENSURE_CAN_WRITE } DUCHAIN_D_DYNAMIC(Declaration); if( context == d->m_internalContext.context() ) return; if(!m_topContext) { //Take the top-context from the other side. We need to allocate an index, so we can safely call setOwner(..) m_topContext = context->topContext(); allocateOwnIndex(); } DUContext* oldInternalContext = d->m_internalContext.context(); d->m_internalContext = context; //Q_ASSERT( !oldInternalContext || oldInternalContext->owner() == this ); if( oldInternalContext && oldInternalContext->owner() == this ) oldInternalContext->setOwner(nullptr); if( context ) context->setOwner(this); } bool Declaration::operator ==(const Declaration & other) const { ENSURE_CAN_READ return this == &other; } QString Declaration::toString() const { return QStringLiteral("%3 %4").arg(abstractType() ? abstractType()->toString() : QStringLiteral(""), identifier().toString()); } bool Declaration::isDefinition() const { ENSURE_CAN_READ DUCHAIN_D(Declaration); return d->m_isDefinition; } void Declaration::setDeclarationIsDefinition(bool dd) { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(Declaration); d->m_isDefinition = dd; // if (d->m_isDefinition && definition()) { // setDefinition(0); // } } bool Declaration::isAutoDeclaration() const { return d_func()->m_isAutoDeclaration; } void Declaration::setAutoDeclaration(bool _auto) { d_func_dynamic()->m_isAutoDeclaration = _auto; } bool Declaration::isDeprecated() const { return d_func()->m_isDeprecated; } void Declaration::setDeprecated(bool deprecated) { d_func_dynamic()->m_isDeprecated = deprecated; } bool Declaration::alwaysForceDirect() const { return d_func()->m_alwaysForceDirect; } void Declaration::setAlwaysForceDirect(bool direct) { d_func_dynamic()->m_alwaysForceDirect = direct; } bool Declaration::isExplicitlyDeleted() const { return d_func()->m_isExplicitlyDeleted; } void Declaration::setExplicitlyDeleted(bool deleted) { d_func_dynamic()->m_isExplicitlyDeleted = deleted; } ///@todo see whether it would be useful to create an own TypeAliasDeclaration sub-class for this bool Declaration::isTypeAlias() const { DUCHAIN_D(Declaration); return d->m_isTypeAlias; } void Declaration::setIsTypeAlias(bool isTypeAlias) { DUCHAIN_D_DYNAMIC(Declaration); d->m_isTypeAlias = isTypeAlias; } IndexedInstantiationInformation Declaration::specialization() const { return IndexedInstantiationInformation(); } void Declaration::activateSpecialization() { if(specialization().index()) { DeclarationId baseId(id()); baseId.setSpecialization(IndexedInstantiationInformation()); SpecializationStore::self().set(baseId, specialization()); } } DeclarationId Declaration::id(bool forceDirect) const { ENSURE_CAN_READ if(inSymbolTable() && !forceDirect && !alwaysForceDirect()) return DeclarationId(qualifiedIdentifier(), additionalIdentity(), specialization()); else return DeclarationId(IndexedDeclaration(const_cast(this)), specialization()); } bool Declaration::inSymbolTable() const { DUCHAIN_D(Declaration); return d->m_inSymbolTable; } CodeModelItem::Kind kindForDeclaration(Declaration* decl) { CodeModelItem::Kind kind = CodeModelItem::Unknown; if(decl->kind() == Declaration::Namespace) return CodeModelItem::Namespace; if(decl->isFunctionDeclaration()) { kind = CodeModelItem::Function; } if(decl->kind() == Declaration::Type && (decl->type() || dynamic_cast(decl))) kind = CodeModelItem::Class; if(kind == CodeModelItem::Unknown && decl->kind() == Declaration::Instance) kind = CodeModelItem::Variable; if(decl->isForwardDeclaration()) kind = (CodeModelItem::Kind)(kind | CodeModelItem::ForwardDeclaration); if ( decl->context() && decl->context()->type() == DUContext::Class ) kind = (CodeModelItem::Kind)(kind | CodeModelItem::ClassMember); return kind; } void Declaration::updateCodeModel() { DUCHAIN_D(Declaration); if(!d->m_identifier.isEmpty() && d->m_inSymbolTable) { QualifiedIdentifier id(qualifiedIdentifier()); CodeModel::self().updateItem(url(), id, kindForDeclaration(this)); } } void Declaration::setInSymbolTable(bool inSymbolTable) { DUCHAIN_D_DYNAMIC(Declaration); if(!d->m_identifier.isEmpty()) { if(!d->m_inSymbolTable && inSymbolTable) { QualifiedIdentifier id(qualifiedIdentifier()); PersistentSymbolTable::self().addDeclaration(id, this); CodeModel::self().addItem(url(), id, kindForDeclaration(this)); } else if(d->m_inSymbolTable && !inSymbolTable) { QualifiedIdentifier id(qualifiedIdentifier()); PersistentSymbolTable::self().removeDeclaration(id, this); CodeModel::self().removeItem(url(), id); } } d->m_inSymbolTable = inSymbolTable; } TopDUContext * Declaration::topContext() const { return m_topContext; } Declaration* Declaration::clonePrivate() const { return new Declaration(*this); } Declaration* Declaration::clone() const { Declaration* ret = clonePrivate(); ret->d_func_dynamic()->m_inSymbolTable = false; return ret; } bool Declaration::isForwardDeclaration() const { return false; } bool Declaration::isFunctionDeclaration() const { return false; } uint Declaration::additionalIdentity() const { return 0; } bool Declaration::equalQualifiedIdentifier(const Declaration* rhs) const { ENSURE_CAN_READ DUCHAIN_D(Declaration); if(d->m_identifier != rhs->d_func()->m_identifier) return false; return m_context->equalScopeIdentifier(m_context); } QMap > Declaration::uses() const { ENSURE_CAN_READ QMap > tempUses; //First, search for uses within the own context { QMap& ranges(tempUses[topContext()->url()]); foreach(const RangeInRevision range, allUses(topContext(), const_cast(this))) ranges[range] = true; } DeclarationId _id = id(); KDevVarLengthArray useContexts = DUChain::uses()->uses(_id); if (!_id.isDirect()) { // also check uses based on direct IDs KDevVarLengthArray directUseContexts = DUChain::uses()->uses(id(true)); useContexts.append(directUseContexts.data(), directUseContexts.size()); } foreach (const IndexedTopDUContext indexedContext, useContexts) { TopDUContext* context = indexedContext.data(); if(context) { QMap& ranges(tempUses[context->url()]); foreach(const RangeInRevision range, allUses(context, const_cast(this))) ranges[range] = true; } } QMap > ret; for(QMap >::const_iterator it = tempUses.constBegin(); it != tempUses.constEnd(); ++it) { if(!(*it).isEmpty()) { QList& list(ret[it.key()]); for(QMap::const_iterator it2 = (*it).constBegin(); it2 != (*it).constEnd(); ++it2) list << it2.key(); } } return ret; } bool hasDeclarationUse(DUContext* context, int declIdx) { bool ret=false; int usescount=context->usesCount(); const Use* uses=context->uses(); for(int i=0; !ret && ichildContexts()) { ret = ret || hasDeclarationUse(child, declIdx); if(ret) break; } return ret; } bool Declaration::hasUses() const { ENSURE_CAN_READ int idx = topContext()->indexForUsedDeclaration(const_cast(this), false); bool ret = idx != std::numeric_limits::max() && (idx>=0 || hasDeclarationUse(topContext(), idx)); //hasLocalUses DeclarationId myId = id(); if (!ret && DUChain::uses()->hasUses(myId)) { ret = true; } if (!ret && !myId.isDirect() && DUChain::uses()->hasUses(id(true))) { ret = true; } return ret; } QMap > Declaration::usesCurrentRevision() const { ENSURE_CAN_READ QMap > tempUses; //First, search for uses within the own context { QMap& ranges(tempUses[topContext()->url()]); foreach(const RangeInRevision range, allUses(topContext(), const_cast(this))) { ranges[topContext()->transformFromLocalRevision(range)] = true; } } DeclarationId _id = id(); KDevVarLengthArray useContexts = DUChain::uses()->uses(_id); if (!_id.isDirect()) { // also check uses based on direct IDs KDevVarLengthArray directUseContexts = DUChain::uses()->uses(id(true)); useContexts.append(directUseContexts.data(), directUseContexts.size()); } foreach (const IndexedTopDUContext indexedContext, useContexts) { TopDUContext* context = indexedContext.data(); if(context) { QMap& ranges(tempUses[context->url()]); foreach(const RangeInRevision range, allUses(context, const_cast(this))) ranges[context->transformFromLocalRevision(range)] = true; } } QMap > ret; for(QMap >::const_iterator it = tempUses.constBegin(); it != tempUses.constEnd(); ++it) { if(!(*it).isEmpty()) { QList& list(ret[it.key()]); for(QMap::const_iterator it2 = (*it).constBegin(); it2 != (*it).constEnd(); ++it2) list << it2.key(); } } return ret; } } diff --git a/language/duchain/declaration.h b/language/duchain/declaration.h index 9f29bbd3c..1fe5bef57 100644 --- a/language/duchain/declaration.h +++ b/language/duchain/declaration.h @@ -1,570 +1,570 @@ /* 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. */ #ifndef KDEVPLATFORM_DECLARATION_H #define KDEVPLATFORM_DECLARATION_H -#include -#include +#include +#include #include "types/abstracttype.h" #include "duchainbase.h" #include "instantiationinformation.h" #include "identifier.h" #include "indexeddeclaration.h" class QByteArray; namespace KDevelop { class AbstractType; class DUContext; class IndexedString; class DeclarationData; class DeclarationId; class Declaration; class IndexedTopDUContext; class TopDUContext; /** * \short Represents a single declaration in a definition-use chain. * * \note A du-context can be freely edited as long as it's parent-context is zero. * In the moment the parent-context is set, the context may only be edited when it * is allowed to edited it's top-level context (@see TopLevelContext::inDUChain()) */ class KDEVPLATFORMLANGUAGE_EXPORT Declaration : public DUChainBase { public: /// Access types enum AccessPolicy : quint8 { Public /**< a public declaration */, Protected /**< a protected declaration */, Private /**< a private declaration */, DefaultAccess /** TypePtr type() const { return TypePtr::dynamicCast(abstractType()); } /** * Access this declaration's type. * * \note You should not compare or permanently store instances of AbstractType::Ptr. Use IndexedType instead. * \returns this declaration's type, or null if none has been assigned. */ AbstractType::Ptr abstractType() const; /** * Set this declaration's type. * * \param type the type to assign. */ template void setType(TypePtr type) { setAbstractType(AbstractType::Ptr::staticCast(type)); } /** * Set this declaration's \a type. * * \param type this declaration's new type. */ virtual void setAbstractType(AbstractType::Ptr type); /** * Return an indexed form of this declaration's type. * Should be preferred, this is the fastest way, and the correct way for doing equality-comparsion. * * \returns the declaration's type. */ IndexedType indexedType() const; /** * Set this declaration's \a identifier. * * \param identifier this declaration's new identifier */ void setIdentifier(const Identifier& identifier); /** * Access this declaration's \a identifier. * * \returns this declaration's identifier. */ Identifier identifier() const; /** * Access this declaration's \a identifier. * * \return this declaration's identifier in indexed form. This is faster than identifier(), because it * equals the internal representation. Use this for example to do equality-comparison. */ const IndexedIdentifier& indexedIdentifier() const; /** * Determine the global qualified identifier of this declaration. * * \note This function is expensive, equalQualifiedIdentifier() is preferred if you * just want to compare equality. */ QualifiedIdentifier qualifiedIdentifier() const; /** * Compares the qualified identifier of this declaration with the other one, without needing to compute it. * This is more efficient than comparing the results of qualifiedIdentifier(). * * \param rhs declaration to compare identifiers with * \returns true if the identifiers are equal, otherwise false. */ bool equalQualifiedIdentifier(const Declaration* rhs) const; /** * Returns the kind of this declaration. @see Kind */ Kind kind() const; /** * Set the kind. * * \param kind new kind */ void setKind(Kind kind); /** * Returns the comment associated to this declaration in the source-code, * or an invalid string if there is none. * * Stored in utf-8 encoding. */ QByteArray comment() const; /** * Sets the comment for this declaration. * * @note Should be utf-8 encoded. */ void setComment(const QByteArray& str); /** * Sets the comment for this declaration. */ void setComment(const QString& str); /** * Access whether this declaration is in the symbol table. * * \returns true if this declaration is in the symbol table, otherwise false. */ bool inSymbolTable() const; /** * Adds or removes this declaration to/from the symbol table. * * \param inSymbolTable true to add this declaration to the symbol table, false to remove it. */ virtual void setInSymbolTable(bool inSymbolTable); /** * Equivalence operator. * * \param other Other declaration to compare. * \returns true if the declarations are equal, otherwise false. */ bool operator==(const Declaration& other) const; /** * Determine this declaration as a string. \returns this declaration as a string. */ virtual QString toString() const; /** * Returns a map of files to use-ranges. * * The key of the returned map is an url of a file. The value * is a list with all use-ranges of this declaration in that file. * * \note The ranges are in the documents local revision, * use \c DUChainUtils::transformFromRevision or \c usesCurrentRevision() * * \note The uses are unique, no 2 uses are returend that have the same range within the same file. * * \note This is a non-trivial operation and hence expensive. */ QMap > uses() const; /** * Determines whether the declaration has any uses or not. * * Cheaper than calling uses(). */ bool hasUses() const; /** * Returns a map of files to use-ranges. * * The key of the returned map is an url of a file. The value * is a list with all use-ranges of this declaration in that file. * * \note The uses are unique, no 2 uses are returend that have the same range within the same file. * * \warning This must be called only from within the foreground, or with the foreground lock locked. * * \note This is a non-trivial operation and hence expensive. */ QMap > usesCurrentRevision() const; /** * This hash-value should differentiate between multiple different * declarations that have the same qualifiedIdentifier, but should have a different * identity, and thus own Definitions and own Uses assigned. * * Affected by function-arguments, whether this is a template-declaration, etc.. */ virtual uint additionalIdentity() const; /** * TODO document * */ virtual IndexedInstantiationInformation specialization() const; /** * \see DeclarationId * * \param forceDirect When this is true, the DeclarationId is force to be direct, * and can be resolved without a symbol-table and top-context. * The same goes for Declarations that have \c alwaysForceDirect() * set to true. */ virtual DeclarationId id(bool forceDirect = false) const; /** * Returns an index that uniquely identifies this declaration within its surrounding top-context. * * That index can be passed to \c TopDUContext::declarationFromIndex(index) to get the declaration. * This is only valid when the declaration is not a specialization (\c specialization() returns 0), * and if it is not anonymous in its context. * * \note for this to be valid, allocateOwnIndex() must have been called first. * \note the highest big of the index is always zero! * \returns the index of the declaration within its TopDUContext. */ uint ownIndex() const; /** * Whether this declaration has been inserted anonymously into its parent-context */ bool isAnonymous() const; /** * Clear the index for this declaration in the top context that was allocated with allocateOwnIndex(). */ void clearOwnIndex(); /** * Create an index to this declaration from the topContext(). Needed to be able to retrieve ownIndex(). */ void allocateOwnIndex(); /** * Returns a clone of this declaration, with the difference that the returned declaration * has no context set, i.e. \c context() returns zero. * * The declaration will not be registered anywhere, so you must care about its deletion. * * This declaration's text-range will be referenced from the clone, so the clone must not * live longer than the original. */ Declaration* clone() const; /** * Signalized that among multiple possible specializations, this one should be used in the UI from now on. * * Currently mainly used in C++ for template support. The default-implementation registers the current * specialization of this declaration to SpecializationStore if it is nonzero. */ virtual void activateSpecialization(); enum { Identity = 7 }; protected: /** * Constructor for copy constructors in subclasses. * * \param dd data to copy. * \param range text range which this object covers. */ Declaration( DeclarationData & dd, const RangeInRevision& range ); /** * Returns true if this declaration is being currently destroyed persistently, * which means that it should eventually deregister itself from persistent storage facilities. * * Only call this from destructors. */ bool persistentlyDestroying() const; DUCHAIN_DECLARE_DATA(Declaration) private: /** * Sub-classes should implement this and should copy as much information into the clone as possible without breaking the du-chain. * Sub-classes should also implement a public copy-constructor that can be used for cloning by sub-classes. * * \note You do not have to implement this for your language if you are not going to use it(the du-chain itself does not and should not depend on it). * */ virtual Declaration* clonePrivate() const; void updateCodeModel(); void rebuildDynamicData(DUContext* parent, uint ownIndex) override; friend class DUContext; friend class IndexedDeclaration; friend class LocalIndexedDeclaration; friend class TopDUContextDynamicData; DUContext* m_context; TopDUContext* m_topContext; int m_indexInTopContext; }; } #endif // KDEVPLATFORM_DECLARATION_H diff --git a/language/duchain/duchain.cpp b/language/duchain/duchain.cpp index 4a4c21506..8212dad43 100644 --- a/language/duchain/duchain.cpp +++ b/language/duchain/duchain.cpp @@ -1,1749 +1,1749 @@ /* This is part of KDevelop Copyright 2006-2008 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 "duchain.h" #include "duchainlock.h" #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include #include "../interfaces/ilanguagesupport.h" #include "../interfaces/icodehighlighting.h" #include "../backgroundparser/backgroundparser.h" #include "util/debug.h" #include "language-features.h" #include "topducontext.h" #include "topducontextdata.h" #include "topducontextdynamicdata.h" #include "parsingenvironment.h" #include "declaration.h" #include "definitions.h" #include "duchainutils.h" #include "use.h" #include "uses.h" #include "abstractfunctiondeclaration.h" #include "duchainregister.h" #include "persistentsymboltable.h" #include "serialization/itemrepository.h" #include "waitforupdate.h" #include "importers.h" #if HAVE_MALLOC_TRIM #include "malloc.h" #endif namespace { //Additional "soft" cleanup steps that are done before the actual cleanup. //During "soft" cleanup, the consistency is not guaranteed. The repository is //marked to be updating during soft cleanup, so if kdevelop crashes, it will be cleared. //The big advantage of the soft cleanup steps is, that the duchain is always only locked for //short times, which leads to no lockup in the UI. const int SOFT_CLEANUP_STEPS = 1; const uint cleanupEverySeconds = 200; ///Approximate maximum count of top-contexts that are checked during final cleanup const uint maxFinalCleanupCheckContexts = 2000; const uint minimumFinalCleanupCheckContextsPercentage = 10; //Check at least n% of all top-contexts during cleanup //Set to true as soon as the duchain is deleted } namespace KDevelop { bool DUChain::m_deleted = false; ///Must be locked through KDevelop::SpinLock before using chainsByIndex ///This lock should be locked only for very short times QMutex DUChain::chainsByIndexLock; std::vector DUChain::chainsByIndex; //This thing is not actually used, but it's needed for compiling DEFINE_LIST_MEMBER_HASH(EnvironmentInformationListItem, items, uint) //An entry for the item-repository that holds some meta-data. Behind this entry, the actual ParsingEnvironmentFileData is stored. class EnvironmentInformationItem { public: EnvironmentInformationItem(uint topContext, uint size) : m_topContext(topContext), m_size(size) { } ~EnvironmentInformationItem() { } unsigned int hash() const { return m_topContext; } unsigned int itemSize() const { return sizeof(*this) + m_size; } uint m_topContext; uint m_size;//Size of the data behind, that holds the actual item }; struct ItemRepositoryIndexHash { uint operator()(unsigned int __x) const { return 173*(__x>>2) + 11 * (__x >> 16); } }; class EnvironmentInformationRequest { public: ///This constructor should only be used for lookup EnvironmentInformationRequest(uint topContextIndex) : m_file(nullptr), m_index(topContextIndex) { } EnvironmentInformationRequest(const ParsingEnvironmentFile* file) : m_file(file), m_index(file->indexedTopContext().index()) { } enum { AverageSize = 32 //This should be the approximate average size of an Item }; unsigned int hash() const { return m_index; } uint itemSize() const { return sizeof(EnvironmentInformationItem) + DUChainItemSystem::self().dynamicSize(*m_file->d_func()); } void createItem(EnvironmentInformationItem* item) const { new (item) EnvironmentInformationItem(m_index, DUChainItemSystem::self().dynamicSize(*m_file->d_func())); Q_ASSERT(m_file->d_func()->m_dynamic); DUChainBaseData* data = reinterpret_cast(reinterpret_cast(item) + sizeof(EnvironmentInformationItem)); DUChainItemSystem::self().copy(*m_file->d_func(), *data, true); Q_ASSERT(data->m_range == m_file->d_func()->m_range); Q_ASSERT(data->classId == m_file->d_func()->classId); Q_ASSERT(data->m_dynamic == false); } static void destroy(EnvironmentInformationItem* item, KDevelop::AbstractItemRepository&) { item->~EnvironmentInformationItem(); //We don't need to call the destructor, because that's done in DUChainBase::makeDynamic() //We just need to make sure that every environment-file is dynamic when it's deleted // DUChainItemSystem::self().callDestructor((DUChainBaseData*)(((char*)item) + sizeof(EnvironmentInformationItem))); } static bool persistent(const EnvironmentInformationItem* ) { //Cleanup done separately return true; } bool equals(const EnvironmentInformationItem* item) const { return m_index == item->m_topContext; } const ParsingEnvironmentFile* m_file; uint m_index; }; ///A list of environment-information/top-contexts mapped to a file-name class EnvironmentInformationListItem { public: EnvironmentInformationListItem() { initializeAppendedLists(true); } EnvironmentInformationListItem(const EnvironmentInformationListItem& rhs, bool dynamic = true) { initializeAppendedLists(dynamic); m_file = rhs.m_file; copyListsFrom(rhs); } ~EnvironmentInformationListItem() { freeAppendedLists(); } unsigned int hash() const { //We only compare the declaration. This allows us implementing a map, although the item-repository //originally represents a set. return m_file.hash(); } unsigned short int itemSize() const { return dynamicSize(); } IndexedString m_file; uint classSize() const { return sizeof(*this); } START_APPENDED_LISTS(EnvironmentInformationListItem); ///Contains the index of each contained environment-item APPENDED_LIST_FIRST(EnvironmentInformationListItem, uint, items); END_APPENDED_LISTS(EnvironmentInformationListItem, items); }; class EnvironmentInformationListRequest { public: ///This constructor should only be used for lookup EnvironmentInformationListRequest(const IndexedString& file) : m_file(file), m_item(nullptr) { } ///This is used to actually construct the information in the repository EnvironmentInformationListRequest(const IndexedString& file, const EnvironmentInformationListItem& item) : m_file(file), m_item(&item) { } enum { AverageSize = 160 //This should be the approximate average size of an Item }; unsigned int hash() const { return m_file.hash(); } uint itemSize() const { return m_item->itemSize(); } void createItem(EnvironmentInformationListItem* item) const { Q_ASSERT(m_item->m_file == m_file); new (item) EnvironmentInformationListItem(*m_item, false); } static void destroy(EnvironmentInformationListItem* item, KDevelop::AbstractItemRepository&) { item->~EnvironmentInformationListItem(); } static bool persistent(const EnvironmentInformationListItem*) { //Cleanup is done separately return true; } bool equals(const EnvironmentInformationListItem* item) const { return m_file == item->m_file; } IndexedString m_file; const EnvironmentInformationListItem* m_item; }; class DUChainPrivate; static DUChainPrivate* duChainPrivateSelf = nullptr; class DUChainPrivate { class CleanupThread : public QThread { public: explicit CleanupThread(DUChainPrivate* data) : m_stopRunning(false), m_data(data) { } void stopThread() { { QMutexLocker lock(&m_waitMutex); m_stopRunning = true; m_wait.wakeAll(); //Wakes the thread up, so it notices it should exit } wait(); } private: void run() override { while(1) { for(uint s = 0; s < cleanupEverySeconds; ++s) { if(m_stopRunning) break; QMutexLocker lock(&m_waitMutex); m_wait.wait(&m_waitMutex, 1000); } if(m_stopRunning) break; //Just to make sure the cache is cleared periodically ModificationRevisionSet::clearCache(); m_data->doMoreCleanup(SOFT_CLEANUP_STEPS, TryLock); if(m_stopRunning) break; } } bool m_stopRunning; QWaitCondition m_wait; QMutex m_waitMutex; DUChainPrivate* m_data; }; public: DUChainPrivate() : m_chainsMutex(QMutex::Recursive), m_cleanupMutex(QMutex::Recursive), instance(nullptr), m_cleanupDisabled(false), m_destroyed(false), m_environmentListInfo(QStringLiteral("Environment Lists")), m_environmentInfo(QStringLiteral("Environment Information")) { #if defined(TEST_NO_CLEANUP) m_cleanupDisabled = true; #endif duChainPrivateSelf = this; qRegisterMetaType("KDevelop::DUChainBasePointer"); qRegisterMetaType("KDevelop::DUContextPointer"); qRegisterMetaType("KDevelop::TopDUContextPointer"); qRegisterMetaType("KDevelop::DeclarationPointer"); qRegisterMetaType("KDevelop::FunctionDeclarationPointer"); qRegisterMetaType("KDevelop::IndexedString"); qRegisterMetaType("KDevelop::IndexedTopDUContext"); qRegisterMetaType("KDevelop::ReferencedTopDUContext"); instance = new DUChain(); m_cleanup = new CleanupThread(this); m_cleanup->start(); DUChain::m_deleted = false; ///Loading of some static data: { ///@todo Solve this more duchain-like QFile f(globalItemRepositoryRegistry().path() + "/parsing_environment_data"); bool opened = f.open(QIODevice::ReadOnly); ///FIXME: ugh, so ugly ParsingEnvironmentFile::m_staticData = reinterpret_cast( new char[sizeof(StaticParsingEnvironmentData)]); if(opened) { qCDebug(LANGUAGE) << "reading parsing-environment static data"; //Read f.read((char*)ParsingEnvironmentFile::m_staticData, sizeof(StaticParsingEnvironmentData)); }else{ qCDebug(LANGUAGE) << "creating new parsing-environment static data"; //Initialize new (ParsingEnvironmentFile::m_staticData) StaticParsingEnvironmentData(); } } ///Read in the list of available top-context indices { QFile f(globalItemRepositoryRegistry().path() + "/available_top_context_indices"); bool opened = f.open(QIODevice::ReadOnly); if(opened) { Q_ASSERT( (f.size() % sizeof(uint)) == 0); m_availableTopContextIndices.resize(f.size()/(int)sizeof(uint)); f.read((char*)m_availableTopContextIndices.data(), f.size()); } } } ~DUChainPrivate() { qCDebug(LANGUAGE) << "Destroying"; DUChain::m_deleted = true; m_cleanup->stopThread(); delete m_cleanup; delete instance; } void clear() { if(!m_cleanupDisabled) doMoreCleanup(); DUChainWriteLocker writeLock(DUChain::lock()); QMutexLocker l(&m_chainsMutex); foreach(TopDUContext* top, m_chainsByUrl) removeDocumentChainFromMemory(top); m_indexEnvironmentInformations.clear(); m_fileEnvironmentInformations.clear(); Q_ASSERT(m_fileEnvironmentInformations.isEmpty()); Q_ASSERT(m_chainsByUrl.isEmpty()); } ///DUChain must be write-locked ///Also removes from the environment-manager if the top-context is not on disk void removeDocumentChainFromMemory(TopDUContext* context) { QMutexLocker l(&m_chainsMutex); { QMutexLocker l(&m_referenceCountsMutex); if(m_referenceCounts.contains(context)) { //This happens during shutdown, since everything is unloaded qCDebug(LANGUAGE) << "removed a top-context that was reference-counted:" << context->url().str() << context->ownIndex(); m_referenceCounts.remove(context); } } uint index = context->ownIndex(); // qCDebug(LANGUAGE) << "duchain: removing document" << context->url().str(); Q_ASSERT(hasChainForIndex(index)); Q_ASSERT(m_chainsByUrl.contains(context->url(), context)); m_chainsByUrl.remove(context->url(), context); if(!context->isOnDisk()) instance->removeFromEnvironmentManager(context); l.unlock(); //DUChain is write-locked, so we can do whatever we want on the top-context, including deleting it context->deleteSelf(); l.relock(); Q_ASSERT(hasChainForIndex(index)); QMutexLocker lock(&DUChain::chainsByIndexLock); DUChain::chainsByIndex[index] = nullptr; } ///Must be locked before accessing content of this class. ///Should be released during expensive disk-operations and such. QMutex m_chainsMutex; QMutex m_cleanupMutex; CleanupThread* m_cleanup; DUChain* instance; DUChainLock lock; QMultiMap m_chainsByUrl; //Must be locked before accessing m_referenceCounts QMutex m_referenceCountsMutex; QHash m_referenceCounts; Definitions m_definitions; Uses m_uses; QSet m_loading; bool m_cleanupDisabled; //List of available top-context indices, protected by m_chainsMutex QVector m_availableTopContextIndices; ///Used to keep alive the top-context that belong to documents loaded in the editor QSet m_openDocumentContexts; bool m_destroyed; ///The item must not be stored yet ///m_chainsMutex should not be locked, since this can trigger I/O void addEnvironmentInformation(ParsingEnvironmentFilePointer info) { Q_ASSERT(!findInformation(info->indexedTopContext().index())); Q_ASSERT(m_environmentInfo.findIndex(info->indexedTopContext().index()) == 0); QMutexLocker lock(&m_chainsMutex); m_fileEnvironmentInformations.insert(info->url(), info); m_indexEnvironmentInformations.insert(info->indexedTopContext().index(), info); Q_ASSERT(info->d_func()->classId); } ///The item must be managed currently ///m_chainsMutex does not need to be locked void removeEnvironmentInformation(ParsingEnvironmentFilePointer info) { info->makeDynamic(); //By doing this, we make sure the data is actually being destroyed in the destructor bool removed = false; bool removed2 = false; { QMutexLocker lock(&m_chainsMutex); removed = m_fileEnvironmentInformations.remove(info->url(), info); removed2 = m_indexEnvironmentInformations.remove(info->indexedTopContext().index()); } { //Remove it from the environment information lists if it was there QMutexLocker lock(m_environmentListInfo.mutex()); uint index = m_environmentListInfo.findIndex(info->url()); if(index) { EnvironmentInformationListItem item(*m_environmentListInfo.itemFromIndex(index)); if(item.itemsList().removeOne(info->indexedTopContext().index())) { m_environmentListInfo.deleteItem(index); if(!item.itemsList().isEmpty()) m_environmentListInfo.index(EnvironmentInformationListRequest(info->url(), item)); } } } QMutexLocker lock(m_environmentInfo.mutex()); uint index = m_environmentInfo.findIndex(info->indexedTopContext().index()); if(index) { m_environmentInfo.deleteItem(index); } Q_UNUSED(removed); Q_UNUSED(removed2); Q_ASSERT(index || (removed && removed2)); Q_ASSERT(!findInformation(info->indexedTopContext().index())); } ///m_chainsMutex should _not_ be locked, because this may trigger I/O QList getEnvironmentInformation(IndexedString url) { QList ret; uint listIndex = m_environmentListInfo.findIndex(url); if(listIndex) { KDevVarLengthArray topContextIndices; { //First store all the possible intices into the KDevVarLengthArray, so we can unlock the mutex before processing them. QMutexLocker lock(m_environmentListInfo.mutex()); //Lock the mutex to make sure the item isn't changed while it's being iterated const EnvironmentInformationListItem* item = m_environmentListInfo.itemFromIndex(listIndex); FOREACH_FUNCTION(uint topContextIndex, item->items) topContextIndices << topContextIndex; } //Process the indices in a separate step after copying them from the array, so we don't need m_environmentListInfo.mutex locked, //and can call loadInformation(..) safely, which else might lead to a deadlock. foreach (uint topContextIndex, topContextIndices) { QExplicitlySharedDataPointer< ParsingEnvironmentFile > p = ParsingEnvironmentFilePointer(loadInformation(topContextIndex)); if(p) { ret << p; }else{ qCDebug(LANGUAGE) << "Failed to load enviromment-information for" << TopDUContextDynamicData::loadUrl(topContextIndex).str(); } } } QMutexLocker l(&m_chainsMutex); //Add those information that have not been added to the stored lists yet foreach(const ParsingEnvironmentFilePointer& file, m_fileEnvironmentInformations.values(url)) if(!ret.contains(file)) ret << file; return ret; } ///Must be called _without_ the chainsByIndex spin-lock locked static inline bool hasChainForIndex(uint index) { QMutexLocker lock(&DUChain::chainsByIndexLock); return (DUChain::chainsByIndex.size() > index) && DUChain::chainsByIndex[index]; } ///Must be called _without_ the chainsByIndex spin-lock locked. Returns the top-context if it is loaded. static inline TopDUContext* readChainForIndex(uint index) { QMutexLocker lock(&DUChain::chainsByIndexLock); if(DUChain::chainsByIndex.size() > index) return DUChain::chainsByIndex[index]; else return nullptr; } ///Makes sure that the chain with the given index is loaded ///@warning m_chainsMutex must NOT be locked when this is called void loadChain(uint index, QSet& loaded) { QMutexLocker l(&m_chainsMutex); if(!hasChainForIndex(index)) { if(m_loading.contains(index)) { //It's probably being loaded by another thread. So wait until the load is ready while(m_loading.contains(index)) { l.unlock(); qCDebug(LANGUAGE) << "waiting for another thread to load index" << index; QThread::usleep(50000); l.relock(); } loaded.insert(index); return; } m_loading.insert(index); loaded.insert(index); l.unlock(); qCDebug(LANGUAGE) << "loading top-context" << index; TopDUContext* chain = TopDUContextDynamicData::load(index); if(chain) { chain->setParsingEnvironmentFile(loadInformation(chain->ownIndex())); if(!chain->usingImportsCache()) { //Eventually also load all the imported chains, so the import-structure is built foreach(const DUContext::Import &import, chain->DUContext::importedParentContexts()) { if(!loaded.contains(import.topContextIndex())) { loadChain(import.topContextIndex(), loaded); } } } chain->rebuildDynamicImportStructure(); chain->setInDuChain(true); instance->addDocumentChain(chain); } l.relock(); m_loading.remove(index); } } ///Stores all environment-information ///Also makes sure that all information that stays is referenced, so it stays alive. ///@param atomic If this is false, the write-lock will be released time by time void storeAllInformation(bool atomic, DUChainWriteLocker& locker) { uint cnt = 0; QList urls; { QMutexLocker lock(&m_chainsMutex); urls += m_fileEnvironmentInformations.keys(); } foreach(const IndexedString &url, urls) { QList check; { QMutexLocker lock(&m_chainsMutex); check = m_fileEnvironmentInformations.values(url); } foreach(ParsingEnvironmentFilePointer file, check) { EnvironmentInformationRequest req(file.data()); QMutexLocker lock(m_environmentInfo.mutex()); uint index = m_environmentInfo.findIndex(req); if(file->d_func()->isDynamic()) { //This item has been changed, or isn't in the repository yet //Eventually remove an old entry if(index) m_environmentInfo.deleteItem(index); //Add the new entry to the item repository index = m_environmentInfo.index(req); Q_ASSERT(index); EnvironmentInformationItem* item = const_cast(m_environmentInfo.itemFromIndex(index)); DUChainBaseData* theData = reinterpret_cast(reinterpret_cast(item) + sizeof(EnvironmentInformationItem)); Q_ASSERT(theData->m_range == file->d_func()->m_range); Q_ASSERT(theData->m_dynamic == false); Q_ASSERT(theData->classId == file->d_func()->classId); file->setData( theData ); ++cnt; }else{ m_environmentInfo.itemFromIndex(index); //Prevent unloading of the data, by accessing the item } } ///We must not release the lock while holding a reference to a ParsingEnvironmentFilePointer, else we may miss the deletion of an ///information, and will get crashes. if(!atomic && (cnt % 100 == 0)) { //Release the lock on a regular basis locker.unlock(); locker.lock(); } storeInformationList(url); //Access the data in the repository, so the bucket isn't unloaded uint index = m_environmentListInfo.findIndex(EnvironmentInformationListRequest(url)); if(index) { m_environmentListInfo.itemFromIndex(index); }else{ QMutexLocker lock(&m_chainsMutex); qCDebug(LANGUAGE) << "Did not find stored item for" << url.str() << "count:" << m_fileEnvironmentInformations.values(url); } if(!atomic) { locker.unlock(); locker.lock(); } } } QMutex& cleanupMutex() { return m_cleanupMutex; } /// defines how we interact with the ongoing language parse jobs enum LockFlag { /// no locking required, only used when we locked previously NoLock = 0, /// lock all parse jobs and block until we succeeded. required at shutdown BlockingLock = 1, /// only try to lock and abort on failure, good for the intermittent cleanups TryLock = 2, }; ///@param retries When this is nonzero, then doMoreCleanup will do the specified amount of cycles ///doing the cleanup without permanently locking the du-chain. During these steps the consistency ///of the disk-storage is not guaranteed, but only few changes will be done during these steps, ///so the final step where the duchain is permanently locked is much faster. void doMoreCleanup(int retries = 0, LockFlag lockFlag = BlockingLock) { if(m_cleanupDisabled) return; //This mutex makes sure that there's never 2 threads at he same time trying to clean up QMutexLocker lockCleanupMutex(&cleanupMutex()); if(m_destroyed || m_cleanupDisabled) return; Q_ASSERT(!instance->lock()->currentThreadHasReadLock() && !instance->lock()->currentThreadHasWriteLock()); DUChainWriteLocker writeLock(instance->lock()); //This is used to stop all parsing before starting to do the cleanup. This way less happens during the //soft cleanups, and we have a good chance that during the "hard" cleanup only few data has to be written. QList locked; if (lockFlag != NoLock) { QList languages; if (ICore* core = ICore::self()) if (ILanguageController* lc = core->languageController()) languages = lc->loadedLanguages(); writeLock.unlock(); //Here we wait for all parsing-threads to stop their processing foreach(const auto language, languages) { if (lockFlag == TryLock) { if (!language->parseLock()->tryLockForWrite()) { qCDebug(LANGUAGE) << "Aborting cleanup because language plugin is still parsing:" << language->name(); // some language is still parsing, don't interfere with the cleanup foreach(auto* lock, locked) { lock->unlock(); } return; } } else { language->parseLock()->lockForWrite(); } locked << language->parseLock(); } writeLock.lock(); globalItemRepositoryRegistry().lockForWriting(); qCDebug(LANGUAGE) << "starting cleanup"; } QTime startTime = QTime::currentTime(); PersistentSymbolTable::self().clearCache(); storeAllInformation(!retries, writeLock); //Puts environment-information into a repository //We don't need to increase the reference-count, since the cleanup-mutex is locked QSet workOnContexts; { QMutexLocker l(&m_chainsMutex); workOnContexts.reserve(m_chainsByUrl.size()); foreach(TopDUContext* top, m_chainsByUrl) { workOnContexts << top; Q_ASSERT(hasChainForIndex(top->ownIndex())); } } foreach(TopDUContext* context, workOnContexts) { context->m_dynamicData->store(); if(retries) { //Eventually give other threads a chance to access the duchain writeLock.unlock(); //Sleep to give the other threads a realistic chance to get a read-lock in between QThread::usleep(500); writeLock.lock(); } } //Unload all top-contexts that don't have a reference-count and that are not imported by a referenced one QSet unloadedNames; bool unloadedOne = true; bool unloadAllUnreferenced = !retries; //Now unload contexts, but only ones that are not imported by any other currently loaded context //The complication: Since during the lock-break new references may be added, we must never keep //the du-chain in an invalid state. Thus we can only unload contexts that are not imported by any //currently loaded contexts. In case of loops, we have to unload everything at once. while(unloadedOne) { unloadedOne = false; int hadUnloadable = 0; unloadContexts: foreach(TopDUContext* unload, workOnContexts) { bool hasReference = false; { QMutexLocker l(&m_referenceCountsMutex); //Test if the context is imported by a referenced one for (auto it = m_referenceCounts.constBegin(), end = m_referenceCounts.constEnd(); it != end; ++it) { auto* context = it.key(); if(context == unload || context->imports(unload, CursorInRevision())) { workOnContexts.remove(unload); hasReference = true; } } } if(!hasReference) ++hadUnloadable; //We have found a context that is not referenced else continue; //This context is referenced bool isImportedByLoaded = !unload->loadedImporters().isEmpty(); //If we unload a context that is imported by other contexts, we create a bad loaded state if(isImportedByLoaded && !unloadAllUnreferenced) continue; unloadedNames.insert(unload->url()); //Since we've released the write-lock in between, we've got to call store() again to be sure that none of the data is dynamic //If nothing has changed, it is only a low-cost call. unload->m_dynamicData->store(); Q_ASSERT(!unload->d_func()->m_dynamic); removeDocumentChainFromMemory(unload); workOnContexts.remove(unload); unloadedOne = true; if(!unloadAllUnreferenced) { //Eventually give other threads a chance to access the duchain writeLock.unlock(); //Sleep to give the other threads a realistic chance to get a read-lock in between QThread::usleep(500); writeLock.lock(); } } if(hadUnloadable && !unloadedOne) { Q_ASSERT(!unloadAllUnreferenced); //This can happen in case of loops. We have o unload everything at one time. qCDebug(LANGUAGE) << "found" << hadUnloadable << "unloadable contexts, but could not unload separately. Unloading atomically."; unloadAllUnreferenced = true; hadUnloadable = 0; //Reset to 0, so we cannot loop forever goto unloadContexts; } } if(retries == 0) { QMutexLocker lock(&m_chainsMutex); //Do this atomically, since we must be sure that _everything_ is already saved for(QMultiMap::iterator it = m_fileEnvironmentInformations.begin(); it != m_fileEnvironmentInformations.end(); ) { ParsingEnvironmentFile* f = it->data(); Q_ASSERT(f->d_func()->classId); if(f->ref.load() == 1) { Q_ASSERT(!f->d_func()->isDynamic()); //It cannot be dynamic, since we have stored before //The ParsingEnvironmentFilePointer is only referenced once. This means that it does not belong to any //loaded top-context, so just remove it to save some memory and processing time. ///@todo use some kind of timeout before removing it = m_fileEnvironmentInformations.erase(it); }else{ ++it; } } } if(retries) writeLock.unlock(); //This must be the last step, due to the on-disk reference counting globalItemRepositoryRegistry().store(); //Stores all repositories { //Store the static parsing-environment file data ///@todo Solve this more elegantly, using a general mechanism to store static duchain-like data Q_ASSERT(ParsingEnvironmentFile::m_staticData); QFile f(globalItemRepositoryRegistry().path() + "/parsing_environment_data"); bool opened = f.open(QIODevice::WriteOnly); Q_ASSERT(opened); Q_UNUSED(opened); f.write((char*)ParsingEnvironmentFile::m_staticData, sizeof(StaticParsingEnvironmentData)); } ///Write out the list of available top-context indices { QMutexLocker lock(&m_chainsMutex); QFile f(globalItemRepositoryRegistry().path() + "/available_top_context_indices"); bool opened = f.open(QIODevice::WriteOnly); Q_ASSERT(opened); Q_UNUSED(opened); f.write((char*)m_availableTopContextIndices.data(), m_availableTopContextIndices.size() * sizeof(uint)); } if(retries) { doMoreCleanup(retries-1, NoLock); writeLock.lock(); } if(lockFlag != NoLock) { globalItemRepositoryRegistry().unlockForWriting(); int elapsedSeconds = startTime.secsTo(QTime::currentTime()); qCDebug(LANGUAGE) << "seconds spent doing cleanup: " << elapsedSeconds << "top-contexts still open:" << m_chainsByUrl.size(); } if(!retries) { int elapesedMilliSeconds = startTime.msecsTo(QTime::currentTime()); qCDebug(LANGUAGE) << "milliseconds spent doing cleanup with locked duchain: " << elapesedMilliSeconds; } foreach(QReadWriteLock* lock, locked) lock->unlock(); #if HAVE_MALLOC_TRIM // trim unused memory but keep a pad buffer of about 50 MB // this can greatly decrease the perceived memory consumption of kdevelop // see: https://sourceware.org/bugzilla/show_bug.cgi?id=14827 malloc_trim(50 * 1024 * 1024); #endif } ///Checks whether the information is already loaded. ParsingEnvironmentFile* findInformation(uint topContextIndex) { QMutexLocker lock(&m_chainsMutex); QHash::iterator it = m_indexEnvironmentInformations.find(topContextIndex); if(it != m_indexEnvironmentInformations.end()) return (*it).data(); return nullptr; } ///Loads/gets the environment-information for the given top-context index, or returns zero if none exists ///@warning m_chainsMutex should NOT be locked when this is called, because it triggers I/O ///@warning no other mutexes should be locked, as that may lead to a dedalock ParsingEnvironmentFile* loadInformation(uint topContextIndex) { ParsingEnvironmentFile* alreadyLoaded = findInformation(topContextIndex); if(alreadyLoaded) return alreadyLoaded; //Step two: Check if it is on disk, and if is, load it uint dataIndex = m_environmentInfo.findIndex(EnvironmentInformationRequest(topContextIndex)); if(!dataIndex) { //No environment-information stored for this top-context return nullptr; } const EnvironmentInformationItem& item(*m_environmentInfo.itemFromIndex(dataIndex)); QMutexLocker lock(&m_chainsMutex); //Due to multi-threading, we must do this check after locking the mutex, so we can be sure we don't create the same item twice at the same time alreadyLoaded = findInformation(topContextIndex); if(alreadyLoaded) return alreadyLoaded; ///FIXME: ugly, and remove const_cast ParsingEnvironmentFile* ret = dynamic_cast(DUChainItemSystem::self().create( const_cast(reinterpret_cast(reinterpret_cast(&item) + sizeof(EnvironmentInformationItem))) )); if(ret) { Q_ASSERT(ret->d_func()->classId); Q_ASSERT(ret->indexedTopContext().index() == topContextIndex); ParsingEnvironmentFilePointer retPtr(ret); m_fileEnvironmentInformations.insert(ret->url(), retPtr); Q_ASSERT(!m_indexEnvironmentInformations.contains(ret->indexedTopContext().index())); m_indexEnvironmentInformations.insert(ret->indexedTopContext().index(), retPtr); } return ret; } struct CleanupListVisitor { QList checkContexts; bool operator()(const EnvironmentInformationItem* item) { checkContexts << item->m_topContext; return true; } }; ///Will check a selection of all top-contexts for up-to-date ness, and remove them if out of date void cleanupTopContexts() { DUChainWriteLocker lock( DUChain::lock() ); qCDebug(LANGUAGE) << "cleaning top-contexts"; CleanupListVisitor visitor; uint startPos = 0; m_environmentInfo.visitAllItems(visitor); int checkContextsCount = maxFinalCleanupCheckContexts; int percentageOfContexts = (visitor.checkContexts.size() * 100) / minimumFinalCleanupCheckContextsPercentage; if(checkContextsCount < percentageOfContexts) checkContextsCount = percentageOfContexts; if(visitor.checkContexts.size() > (int)checkContextsCount) startPos = qrand() % (visitor.checkContexts.size() - checkContextsCount); int endPos = startPos + maxFinalCleanupCheckContexts; if(endPos > visitor.checkContexts.size()) endPos = visitor.checkContexts.size(); QSet< uint > check; for(int a = startPos; a < endPos && check.size() < checkContextsCount; ++a) if(check.size() < checkContextsCount) addContextsForRemoval(check, IndexedTopDUContext(visitor.checkContexts[a])); foreach(uint topIndex, check) { IndexedTopDUContext top(topIndex); if(top.data()) { qCDebug(LANGUAGE) << "removing top-context for" << top.data()->url().str() << "because it is out of date"; instance->removeDocumentChain(top.data()); } } qCDebug(LANGUAGE) << "check ready"; } private: void addContextsForRemoval(QSet& topContexts, IndexedTopDUContext top) { if(topContexts.contains(top.index())) return; QExplicitlySharedDataPointer info( instance->environmentFileForDocument(top) ); ///@todo Also check if the context is "useful"(Not a duplicate context, imported by a useful one, ...) if(info && info->needsUpdate()) { //This context will be removed }else{ return; } topContexts.insert(top.index()); if(info) { //Check whether importers need to be removed as well QList< QExplicitlySharedDataPointer > importers = info->importers(); QSet< QExplicitlySharedDataPointer > checkNext; //Do breadth first search, so less imports/importers have to be loaded, and a lower depth is reached for(QList< QExplicitlySharedDataPointer >::iterator it = importers.begin(); it != importers.end(); ++it) { IndexedTopDUContext c = (*it)->indexedTopContext(); if(!topContexts.contains(c.index())) { topContexts.insert(c.index()); //Prevent useless recursion checkNext.insert(*it); } } for(QSet< QExplicitlySharedDataPointer >::const_iterator it = checkNext.begin(); it != checkNext.end(); ++it) { topContexts.remove((*it)->indexedTopContext().index()); //Enable full check again addContextsForRemoval(topContexts, (*it)->indexedTopContext()); } } } ///Stores the environment-information for the given url void storeInformationList(IndexedString url) { QMutexLocker lock(m_environmentListInfo.mutex()); EnvironmentInformationListItem newItem; newItem.m_file = url; QSet newItems; { QMutexLocker lock(&m_chainsMutex); QMultiMap::iterator start = m_fileEnvironmentInformations.lowerBound(url); QMultiMap::iterator end = m_fileEnvironmentInformations.upperBound(url); for(QMultiMap::iterator it = start; it != end; ++it) { uint topContextIndex = (*it)->indexedTopContext().index(); newItems.insert(topContextIndex); newItem.itemsList().append(topContextIndex); } } uint index = m_environmentListInfo.findIndex(url); if(index) { //We only handle adding items here, since we can never be sure whether everything is loaded //Removal is handled directly in removeEnvironmentInformation const EnvironmentInformationListItem* item = m_environmentListInfo.itemFromIndex(index); QSet oldItems; FOREACH_FUNCTION(uint topContextIndex, item->items) { oldItems.insert(topContextIndex); if(!newItems.contains(topContextIndex)) { newItems.insert(topContextIndex); newItem.itemsList().append(topContextIndex); } } if(oldItems == newItems) return; ///Update/insert a new list m_environmentListInfo.deleteItem(index); //Remove the previous item } Q_ASSERT(m_environmentListInfo.findIndex(EnvironmentInformationListRequest(url)) == 0); //Insert the new item m_environmentListInfo.index(EnvironmentInformationListRequest(url, newItem)); Q_ASSERT(m_environmentListInfo.findIndex(EnvironmentInformationListRequest(url))); } //Loaded environment information. Protected by m_chainsMutex QMultiMap m_fileEnvironmentInformations; QHash m_indexEnvironmentInformations; ///The following repositories are thread-safe, and m_chainsMutex should not be locked when using them, because ///they may trigger I/O. Still it may be required to lock their local mutexes. ///Maps filenames to a list of top-contexts/environment-information. ItemRepository m_environmentListInfo; ///Maps top-context-indices to environment-information item. ItemRepository m_environmentInfo; }; Q_GLOBAL_STATIC(DUChainPrivate, sdDUChainPrivate) DUChain::DUChain() { Q_ASSERT(ICore::self()); connect(ICore::self()->documentController(), &IDocumentController::documentLoadedPrepare, this, &DUChain::documentLoadedPrepare); connect(ICore::self()->documentController(), &IDocumentController::documentUrlChanged, this, &DUChain::documentRenamed); connect(ICore::self()->documentController(), &IDocumentController::documentActivated, this, &DUChain::documentActivated); connect(ICore::self()->documentController(), &IDocumentController::documentClosed, this, &DUChain::documentClosed); } DUChain::~DUChain() { DUChain::m_deleted = true; } DUChain* DUChain::self() { return sdDUChainPrivate->instance; } extern void initModificationRevisionSetRepository(); extern void initDeclarationRepositories(); extern void initIdentifierRepository(); extern void initTypeRepository(); extern void initInstantiationInformationRepository(); void DUChain::initialize() { // Initialize the global item repository as first thing after loading the session Q_ASSERT(ICore::self()); Q_ASSERT(ICore::self()->activeSession()); ItemRepositoryRegistry::initialize(ICore::self()->activeSessionLock()); initReferenceCounting(); // This needs to be initialized here too as the function is not threadsafe, but can // sometimes be called from different threads. This results in the underlying QFile // being 0 and hence crashes at some point later when accessing the contents via // read. See https://bugs.kde.org/show_bug.cgi?id=250779 RecursiveImportRepository::repository(); RecursiveImportCacheRepository::repository(); // similar to above, see https://bugs.kde.org/show_bug.cgi?id=255323 initDeclarationRepositories(); initModificationRevisionSetRepository(); initIdentifierRepository(); initTypeRepository(); initInstantiationInformationRepository(); Importers::self(); globalImportIdentifier(); globalIndexedImportIdentifier(); globalAliasIdentifier(); globalIndexedAliasIdentifier(); } DUChainLock* DUChain::lock() { return &sdDUChainPrivate->lock; } QList DUChain::allChains() const { QMutexLocker l(&sdDUChainPrivate->m_chainsMutex); return sdDUChainPrivate->m_chainsByUrl.values(); } void DUChain::updateContextEnvironment( TopDUContext* context, ParsingEnvironmentFile* file ) { QMutexLocker l(&sdDUChainPrivate->m_chainsMutex); removeFromEnvironmentManager( context ); context->setParsingEnvironmentFile( file ); addToEnvironmentManager( context ); } void DUChain::removeDocumentChain( TopDUContext* context ) { ENSURE_CHAIN_WRITE_LOCKED; IndexedTopDUContext indexed(context->indexed()); Q_ASSERT(indexed.data() == context); ///This assertion fails if you call removeDocumentChain(..) on a document that has not been added to the du-chain context->m_dynamicData->deleteOnDisk(); Q_ASSERT(indexed.data() == context); sdDUChainPrivate->removeDocumentChainFromMemory(context); Q_ASSERT(!indexed.data()); Q_ASSERT(!environmentFileForDocument(indexed)); QMutexLocker lock(&sdDUChainPrivate->m_chainsMutex); sdDUChainPrivate->m_availableTopContextIndices.push_back(indexed.index()); } void DUChain::addDocumentChain( TopDUContext * chain ) { QMutexLocker l(&sdDUChainPrivate->m_chainsMutex); // qCDebug(LANGUAGE) << "duchain: adding document" << chain->url().str() << " " << chain; Q_ASSERT(chain); Q_ASSERT(!sdDUChainPrivate->hasChainForIndex(chain->ownIndex())); { QMutexLocker lock(&DUChain::chainsByIndexLock); if(DUChain::chainsByIndex.size() <= chain->ownIndex()) DUChain::chainsByIndex.resize(chain->ownIndex() + 100, nullptr); DUChain::chainsByIndex[chain->ownIndex()] = chain; } { Q_ASSERT(DUChain::chainsByIndex[chain->ownIndex()]); } Q_ASSERT(sdDUChainPrivate->hasChainForIndex(chain->ownIndex())); sdDUChainPrivate->m_chainsByUrl.insert(chain->url(), chain); Q_ASSERT(sdDUChainPrivate->hasChainForIndex(chain->ownIndex())); chain->setInDuChain(true); l.unlock(); addToEnvironmentManager(chain); // This function might be called during shutdown by stale parse jobs // Make sure we don't access null-pointers here if (ICore::self() && ICore::self()->languageController() && ICore::self()->languageController()->backgroundParser()->trackerForUrl(chain->url())) { //Make sure the context stays alive at least as long as the context is open ReferencedTopDUContext ctx(chain); sdDUChainPrivate->m_openDocumentContexts.insert(ctx); } } void DUChain::addToEnvironmentManager( TopDUContext * chain ) { ParsingEnvironmentFilePointer file = chain->parsingEnvironmentFile(); if( !file ) return; //We don't need to manage Q_ASSERT(file->indexedTopContext().index() == chain->ownIndex()); if(ParsingEnvironmentFile* alreadyHave = sdDUChainPrivate->findInformation(file->indexedTopContext().index())) { ///If this triggers, there has already been another environment-information registered for this top-context. ///removeFromEnvironmentManager should have been called before to remove the old environment-information. Q_ASSERT(alreadyHave == file.data()); Q_UNUSED(alreadyHave); return; } sdDUChainPrivate->addEnvironmentInformation(file); } void DUChain::removeFromEnvironmentManager( TopDUContext * chain ) { ParsingEnvironmentFilePointer file = chain->parsingEnvironmentFile(); if( !file ) return; //We don't need to manage sdDUChainPrivate->removeEnvironmentInformation(file); } TopDUContext* DUChain::chainForDocument(const QUrl& document, bool proxyContext) const { return chainForDocument(IndexedString(document), proxyContext); } bool DUChain::isInMemory(uint topContextIndex) const { return DUChainPrivate::hasChainForIndex(topContextIndex); } IndexedString DUChain::urlForIndex(uint index) const { { TopDUContext* chain = DUChainPrivate::readChainForIndex(index); if(chain) return chain->url(); } return TopDUContextDynamicData::loadUrl(index); } TopDUContext* DUChain::loadChain(uint index) { QSet loaded; sdDUChainPrivate->loadChain(index, loaded); { QMutexLocker lock(&chainsByIndexLock); if(chainsByIndex.size() > index) { TopDUContext* top = chainsByIndex[index]; if(top) return top; } } return nullptr; } TopDUContext* DUChain::chainForDocument(const KDevelop::IndexedString& document, bool proxyContext) const { ENSURE_CHAIN_READ_LOCKED; if(sdDUChainPrivate->m_destroyed) return nullptr; QList list = sdDUChainPrivate->getEnvironmentInformation(document); foreach(const ParsingEnvironmentFilePointer &file, list) if(isInMemory(file->indexedTopContext().index()) && file->isProxyContext() == proxyContext) { return file->topContext(); } foreach(const ParsingEnvironmentFilePointer &file, list) if(proxyContext == file->isProxyContext()) { return file->topContext(); } //Allow selecting a top-context even if there is no ParsingEnvironmentFile QList< TopDUContext* > ret = chainsForDocument(document); foreach(TopDUContext* ctx, ret) { if(!ctx->parsingEnvironmentFile() || (ctx->parsingEnvironmentFile()->isProxyContext() == proxyContext)) return ctx; } return nullptr; } QList DUChain::chainsForDocument(const QUrl& document) const { return chainsForDocument(IndexedString(document)); } QList DUChain::chainsForDocument(const IndexedString& document) const { QList chains; if(sdDUChainPrivate->m_destroyed) return chains; QMutexLocker l(&sdDUChainPrivate->m_chainsMutex); // Match all parsed versions of this document for (auto it = sdDUChainPrivate->m_chainsByUrl.lowerBound(document); it != sdDUChainPrivate->m_chainsByUrl.end(); ++it) { if (it.key() == document) chains << it.value(); else break; } return chains; } TopDUContext* DUChain::chainForDocument( const QUrl& document, const KDevelop::ParsingEnvironment* environment, bool proxyContext ) const { return chainForDocument( IndexedString(document), environment, proxyContext ); } ParsingEnvironmentFilePointer DUChain::environmentFileForDocument( const IndexedString& document, const ParsingEnvironment* environment, bool proxyContext ) const { ENSURE_CHAIN_READ_LOCKED; if(sdDUChainPrivate->m_destroyed) return ParsingEnvironmentFilePointer(); QList< ParsingEnvironmentFilePointer> list = sdDUChainPrivate->getEnvironmentInformation(document); // qCDebug(LANGUAGE) << document.str() << ": matching" << list.size() << (onlyProxyContexts ? "proxy-contexts" : (noProxyContexts ? "content-contexts" : "contexts")); auto it = list.constBegin(); while(it != list.constEnd()) { if(*it && ((*it)->isProxyContext() == proxyContext) && (*it)->matchEnvironment(environment) && // Verify that the environment-file and its top-context are "good": The top-context must exist, // and there must be a content-context associated to the proxy-context. (*it)->topContext() && (!proxyContext || DUChainUtils::contentContextFromProxyContext((*it)->topContext())) ) { return *it; } ++it; } return ParsingEnvironmentFilePointer(); } QList DUChain::allEnvironmentFiles(const IndexedString& document) { return sdDUChainPrivate->getEnvironmentInformation(document); } ParsingEnvironmentFilePointer DUChain::environmentFileForDocument(IndexedTopDUContext topContext) const { if(topContext.index() == 0) return ParsingEnvironmentFilePointer(); return ParsingEnvironmentFilePointer(sdDUChainPrivate->loadInformation(topContext.index())); } TopDUContext* DUChain::chainForDocument( const IndexedString& document, const ParsingEnvironment* environment, bool proxyContext ) const { if(sdDUChainPrivate->m_destroyed) return nullptr; ParsingEnvironmentFilePointer envFile = environmentFileForDocument(document, environment, proxyContext); if(envFile) { return envFile->topContext(); }else{ return nullptr; } } QList DUChain::documents() const { QMutexLocker l(&sdDUChainPrivate->m_chainsMutex); QList ret; ret.reserve(sdDUChainPrivate->m_chainsByUrl.count()); foreach(TopDUContext* top, sdDUChainPrivate->m_chainsByUrl) { ret << top->url().toUrl(); } return ret; } QList DUChain::indexedDocuments() const { QMutexLocker l(&sdDUChainPrivate->m_chainsMutex); QList ret; ret.reserve(sdDUChainPrivate->m_chainsByUrl.count()); foreach(TopDUContext* top, sdDUChainPrivate->m_chainsByUrl) { ret << top->url(); } return ret; } void DUChain::documentActivated(KDevelop::IDocument* doc) { if(sdDUChainPrivate->m_destroyed) return; DUChainReadLocker lock( DUChain::lock() ); QMutexLocker l(&sdDUChainPrivate->m_chainsMutex); auto backgroundParser = ICore::self()->languageController()->backgroundParser(); auto addWithHighPriority = [backgroundParser, doc]() { backgroundParser->addDocument(IndexedString(doc->url()), TopDUContext::VisibleDeclarationsAndContexts, BackgroundParser::BestPriority); }; TopDUContext* ctx = DUChainUtils::standardContextForUrl(doc->url(), true); //Check whether the document has an attached environment-manager, and whether that one thinks the document needs to be updated. //If yes, update it. if (ctx && ctx->parsingEnvironmentFile() && ctx->parsingEnvironmentFile()->needsUpdate()) { qCDebug(LANGUAGE) << "Document needs update, using best priority since it just got activated:" << doc->url(); addWithHighPriority(); } else if (backgroundParser->managedDocuments().contains(IndexedString(doc->url()))) { // increase priority if there's already parse job of this document in the queue qCDebug(LANGUAGE) << "Prioritizing activated document:" << doc->url(); addWithHighPriority(); } } void DUChain::documentClosed(IDocument* document) { if(sdDUChainPrivate->m_destroyed) return; IndexedString url(document->url()); foreach(const ReferencedTopDUContext &top, sdDUChainPrivate->m_openDocumentContexts) if(top->url() == url) sdDUChainPrivate->m_openDocumentContexts.remove(top); } void DUChain::documentLoadedPrepare(KDevelop::IDocument* doc) { if(sdDUChainPrivate->m_destroyed) return; const IndexedString url(doc->url()); DUChainWriteLocker lock( DUChain::lock() ); QMutexLocker l(&sdDUChainPrivate->m_chainsMutex); TopDUContext* standardContext = DUChainUtils::standardContextForUrl(doc->url()); QList chains = chainsForDocument(url); auto languages = ICore::self()->languageController()->languagesForUrl(doc->url()); if(standardContext) { Q_ASSERT(chains.contains(standardContext)); //We have just loaded it Q_ASSERT((standardContext->url() == url)); sdDUChainPrivate->m_openDocumentContexts.insert(standardContext); bool needsUpdate = standardContext->parsingEnvironmentFile() && standardContext->parsingEnvironmentFile()->needsUpdate(); if(!needsUpdate) { //Only apply the highlighting if we don't need to update, else we might highlight total crap //Do instant highlighting only if all imports are loaded, to make sure that we don't block the user-interface too long //Else the highlighting will be done in the background-thread //This is not exactly right, as the direct imports don't necessarily equal the real imports used by uses //but it approximates the correct behavior. bool allImportsLoaded = true; foreach(const DUContext::Import& import, standardContext->importedParentContexts()) if(!import.indexedContext().indexedTopContext().isLoaded()) allImportsLoaded = false; if(allImportsLoaded) { l.unlock(); lock.unlock(); foreach(const auto language, languages) { if(language->codeHighlighting()) { language->codeHighlighting()->highlightDUChain(standardContext); } } qCDebug(LANGUAGE) << "highlighted" << doc->url() << "in foreground"; return; } }else{ qCDebug(LANGUAGE) << "not highlighting the duchain because the documents needs an update"; } if(needsUpdate || !(standardContext->features() & TopDUContext::AllDeclarationsContextsAndUses)) { ICore::self()->languageController()->backgroundParser()->addDocument(IndexedString(doc->url()), (TopDUContext::Features)(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdate)); return; } } //Add for highlighting etc. ICore::self()->languageController()->backgroundParser()->addDocument(IndexedString(doc->url()), TopDUContext::AllDeclarationsContextsAndUses); } void DUChain::documentRenamed(KDevelop::IDocument* doc) { if(sdDUChainPrivate->m_destroyed) return; if(!doc->url().isValid()) { ///Maybe this happens when a file was deleted? qCWarning(LANGUAGE) << "Strange, url of renamed document is invalid!"; }else{ ICore::self()->languageController()->backgroundParser()->addDocument(IndexedString(doc->url()), (TopDUContext::Features)(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdate)); } } Uses* DUChain::uses() { return &sdDUChainPrivate->m_uses; } Definitions* DUChain::definitions() { return &sdDUChainPrivate->m_definitions; } static void finalCleanup() { DUChainWriteLocker writeLock(DUChain::lock()); qCDebug(LANGUAGE) << "doing final cleanup"; int cleaned = 0; while((cleaned = globalItemRepositoryRegistry().finalCleanup())) { qCDebug(LANGUAGE) << "cleaned" << cleaned << "B"; if(cleaned < 1000) { qCDebug(LANGUAGE) << "cleaned enough"; break; } } qCDebug(LANGUAGE) << "final cleanup ready"; } void DUChain::shutdown() { // if core is not shutting down, we can end up in deadlocks or crashes // since language plugins might still try to access static duchain stuff Q_ASSERT(!ICore::self() || ICore::self()->shuttingDown()); qCDebug(LANGUAGE) << "Cleaning up and shutting down DUChain"; QMutexLocker lock(&sdDUChainPrivate->cleanupMutex()); { //Acquire write-lock of the repository, so when kdevelop crashes in that process, the repository is discarded //Crashes here may happen in an inconsistent state, thus this makes sense, to protect the user from more crashes globalItemRepositoryRegistry().lockForWriting(); sdDUChainPrivate->cleanupTopContexts(); globalItemRepositoryRegistry().unlockForWriting(); } sdDUChainPrivate->doMoreCleanup(); //Must be done _before_ finalCleanup, else we may be deleting yet needed data sdDUChainPrivate->m_openDocumentContexts.clear(); sdDUChainPrivate->m_destroyed = true; sdDUChainPrivate->clear(); { //Acquire write-lock of the repository, so when kdevelop crashes in that process, the repository is discarded //Crashes here may happen in an inconsistent state, thus this makes sense, to protect the user from more crashes globalItemRepositoryRegistry().lockForWriting(); finalCleanup(); globalItemRepositoryRegistry().unlockForWriting(); } globalItemRepositoryRegistry().shutdown(); } uint DUChain::newTopContextIndex() { { QMutexLocker lock(&sdDUChainPrivate->m_chainsMutex); if(!sdDUChainPrivate->m_availableTopContextIndices.isEmpty()) { uint ret = sdDUChainPrivate->m_availableTopContextIndices.back(); sdDUChainPrivate->m_availableTopContextIndices.pop_back(); if(TopDUContextDynamicData::fileExists(ret)) { qCWarning(LANGUAGE) << "Problem in the management of availalbe top-context indices"; return newTopContextIndex(); } return ret; } } static QAtomicInt& currentId( globalItemRepositoryRegistry().getCustomCounter(QStringLiteral("Top-Context Counter"), 1) ); return currentId.fetchAndAddRelaxed(1); } void DUChain::refCountUp(TopDUContext* top) { QMutexLocker l(&sdDUChainPrivate->m_referenceCountsMutex); // note: value is default-constructed to zero if it does not exist ++sdDUChainPrivate->m_referenceCounts[top]; } bool DUChain::deleted() { return m_deleted; } void DUChain::refCountDown(TopDUContext* top) { QMutexLocker l(&sdDUChainPrivate->m_referenceCountsMutex); auto it = sdDUChainPrivate->m_referenceCounts.find(top); if (it == sdDUChainPrivate->m_referenceCounts.end()) { //qCWarning(LANGUAGE) << "tried to decrease reference-count for" << top->url().str() << "but this top-context is not referenced"; return; } auto& refCount = *it; --refCount; if (!refCount) { sdDUChainPrivate->m_referenceCounts.erase(it); } } void DUChain::emitDeclarationSelected(const DeclarationPointer& decl) { if(sdDUChainPrivate->m_destroyed) return; emit declarationSelected(decl); } void DUChain::emitUpdateReady(const IndexedString& url, const ReferencedTopDUContext& topContext) { if(sdDUChainPrivate->m_destroyed) return; emit updateReady(url, topContext); } KDevelop::ReferencedTopDUContext DUChain::waitForUpdate(const KDevelop::IndexedString& document, KDevelop::TopDUContext::Features minFeatures, bool proxyContext) { Q_ASSERT(!lock()->currentThreadHasReadLock() && !lock()->currentThreadHasWriteLock()); WaitForUpdate waiter; updateContextForUrl(document, minFeatures, &waiter); // waiter.m_waitMutex.lock(); // waiter.m_dataMutex.unlock(); while(!waiter.m_ready) { // we might have been shut down in the meanwhile if (!ICore::self()) { return nullptr; } QMetaObject::invokeMethod(ICore::self()->languageController()->backgroundParser(), "parseDocuments"); QApplication::processEvents(); QThread::usleep(1000); } if(!proxyContext) { DUChainReadLocker readLock(DUChain::lock()); return DUChainUtils::contentContextFromProxyContext(waiter.m_topContext); } return waiter.m_topContext; } void DUChain::updateContextForUrl(const IndexedString& document, TopDUContext::Features minFeatures, QObject* notifyReady, int priority) const { DUChainReadLocker lock( DUChain::lock() ); TopDUContext* standardContext = DUChainUtils::standardContextForUrl(document.toUrl()); if(standardContext && standardContext->parsingEnvironmentFile() && !standardContext->parsingEnvironmentFile()->needsUpdate() && standardContext->parsingEnvironmentFile()->featuresSatisfied(minFeatures)) { lock.unlock(); if(notifyReady) QMetaObject::invokeMethod(notifyReady, "updateReady", Qt::DirectConnection, Q_ARG(KDevelop::IndexedString, document), Q_ARG(KDevelop::ReferencedTopDUContext, ReferencedTopDUContext(standardContext))); }else{ ///Start a parse-job for the given document ICore::self()->languageController()->backgroundParser()->addDocument(document, minFeatures, priority, notifyReady); } } void DUChain::disablePersistentStorage(bool disable) { sdDUChainPrivate->m_cleanupDisabled = disable; } void DUChain::storeToDisk() { bool wasDisabled = sdDUChainPrivate->m_cleanupDisabled; sdDUChainPrivate->m_cleanupDisabled = false; sdDUChainPrivate->doMoreCleanup(); sdDUChainPrivate->m_cleanupDisabled = wasDisabled; } bool DUChain::compareToDisk() { DUChainWriteLocker writeLock(DUChain::lock()); ///Step 1: Compare the repositories return true; } } diff --git a/language/duchain/duchain.h b/language/duchain/duchain.h index a523a0aaa..68fa5bc62 100644 --- a/language/duchain/duchain.h +++ b/language/duchain/duchain.h @@ -1,315 +1,315 @@ /* This file is part of KDevelop Copyright 2006-2008 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. */ #ifndef KDEVPLATFORM_DUCHAIN_H #define KDEVPLATFORM_DUCHAIN_H -#include +#include #include "topducontext.h" #include "parsingenvironment.h" class QUrl; namespace KDevelop { class IDocument; class TopDUContext; class DUChainLock; class ParsingEnvironmentManager; class ParsingEnvironment; class ParsingEnvironmentFile; typedef QExplicitlySharedDataPointer ParsingEnvironmentFilePointer; class Definitions; class Uses; /** * \short Holds references to all top level source file contexts. * * The DUChain is a global static class which manages the definition-use * chains. It performs the following functions: * \li registers chains with addDocumentChain() and deregisters with removeDocumentChain() * \li allows querying for existing chains * \li watches text editors, registering and deregistering them with the BackgroundParser when files * are opened and closed. */ class KDEVPLATFORMLANGUAGE_EXPORT DUChain : public QObject { Q_OBJECT public: /** * Initializes common static item repositories. * Must be called once for multi threaded applications to work reliably. */ static void initialize(); /** * Return a list of all chains available */ Q_SCRIPTABLE QList allChains() const; /** * Makes sure the standard-context for the given url is up-to-date. * This may trigger a parsing in background, so a QObject can be given that will be notified * asyonchronously once the update is ready. * If the context is already up to date, the given QObject is notified directly. * * @param document Document to update * @param minFeatures The requested features. If you want to force a full update of the context, give TopDUContext::ForceUpdate. * If you want to force an update including all imports, use TopDUContext::ForceUpdateRecursive. * @param notifyReady An optional pointer to a QObject that should contain a slot * "void updateReady(KDevelop::IndexedString url, KDevelop::ReferencedTopDUContext topContext)". * The notification is guaranteed to be called once for each call to updateContextForUrl. The given top-context * may be invalid if the update failed. A queued connection is used if a re-parse has to be done. The duchain * will _not_ be locked when updateReady is called. * @param priority An optional priority for the job. The lower the value, the higher it's priority. * @note The duchain must _not_ be locked when this is called! */ Q_SCRIPTABLE void updateContextForUrl(const IndexedString& document, TopDUContext::Features minFeatures, QObject* notifyReady = nullptr, int priority = 1) const; /** * Convenience-function similar to updateContextForUrl that blocks this thread until the update of the given document is ready, * and returns the top-context. * @param document The document to update * @param minFeatures The requested features. If you want to force a full update of the context, give TopDUContext::ForceUpdate. * If you want to force an update including all imports, use TopDUContext::ForceUpdateRecursive. * @return The up-to-date top-context, or zero if the update failed * * @note The duchain must _not_ be locked when this is called! * */ KDevelop::ReferencedTopDUContext waitForUpdate(const KDevelop::IndexedString& document, KDevelop::TopDUContext::Features minFeatures, bool proxyContext = false); /** * Return any chain for the given document * If available, the version accepting IndexedString should be used instead of this, for performance reasons. * When no fitting chain is in memory, one may be loaded from disk. * * @note The duchain must be at least read-locked locked when this is called! * */ Q_SCRIPTABLE TopDUContext* chainForDocument(const QUrl& document, bool proxyContext = false) const; Q_SCRIPTABLE TopDUContext* chainForDocument(const IndexedString& document, bool proxyContext = false) const; /** * Return all chains for the given document that are currently in memory. * This does not load any chains from disk. * */ Q_SCRIPTABLE QList chainsForDocument(const QUrl& document) const; /** * Return all chains for the given document that are currently in memory. * This does not load any chains from disk. * Should be preferred over the QUrl version. * */ Q_SCRIPTABLE QList chainsForDocument(const IndexedString& document) const; /** * Find a chain that fits into the given environment. If no fitting chain is found, 0 is returned. * When no fitting chain is in memory, one may be loaded from disk. * @param proxyContext If this is true, only contexts are found that have an ParsingEnvironmentFile that has the proxy-flag set. Else, only content-contexts will be returned. * * @note The duchain must be at least read-locked locked when this is called! * */ Q_SCRIPTABLE TopDUContext* chainForDocument(const QUrl& document, const ParsingEnvironment* environment, bool proxyContext = false) const; /** * Find a chain that fits into the given environment. If no fitting chain is found, 0 is returned. * When no fitting chain is in memory, one may be loaded from disk. * @param proxyContext If this is true, only contexts are found that have an ParsingEnvironmentFile that has the proxy-flag set. Else, only content-contexts will be returned. * * Prefer this over the QUrl version. * * @note The duchain must be at least read-locked locked when this is called! * */ Q_SCRIPTABLE TopDUContext* chainForDocument(const IndexedString& document, const ParsingEnvironment* environment, bool proxyContext = false) const; /** * Find the environment-file of a chain that fits into the given environment. If no fitting chain is found, 0 is returned. * When no fitting chain is in memory, one may be loaded from disk. * * This should be preferred over chainForDocument when only the environment-info is needed, because the TopDUContext is not loaded in this function. * ** @param proxyContext If this is true, only contexts are found that have an ParsingEnvironmentFile that has the proxy-flag set. Else, only content-contexts will be returned. * * Prefer this over the QUrl version. * * @note The duchain must be at least read-locked locked when this is called! * */ Q_SCRIPTABLE ParsingEnvironmentFilePointer environmentFileForDocument(const IndexedString& document, const ParsingEnvironment* environment, bool proxyContext = false) const; Q_SCRIPTABLE ParsingEnvironmentFilePointer environmentFileForDocument(IndexedTopDUContext topContext) const; /** * Returns the list of the environment-infos of all versions of the given document. */ Q_SCRIPTABLE QList allEnvironmentFiles(const IndexedString& document); ///Returns the top-context that has the given index assigned, or zero if it doesn't exist. @see TopDUContext::ownIndex ///The duchain must be read-locked when this is called ///This function is inlined because it is called in a very high frequency Q_SCRIPTABLE inline TopDUContext* chainForIndex(uint index) { if(m_deleted) return nullptr; { QMutexLocker lock(&chainsByIndexLock); if(chainsByIndex.size() > index) { TopDUContext* top = chainsByIndex[index]; if(top) return top; } } //Load the top-context return loadChain(index); } ///Returns the url for the given top-context index if available. This does have some cost, so avoid it when possible. Q_SCRIPTABLE IndexedString urlForIndex(uint index) const; /// Only used for debugging at the moment Q_SCRIPTABLE QList documents() const; /// Only used for debugging at the moment /// Prefer that over the QUrl version for performance reasons Q_SCRIPTABLE QList indexedDocuments() const; /** * Registers a new definition-use \a chain for the given \a document. */ Q_SCRIPTABLE void addDocumentChain(TopDUContext* chain); /// Returns true if the global duchain instance has already been deleted Q_SCRIPTABLE static bool deleted(); /// Returns the global static instance. Q_SCRIPTABLE static DUChain* self(); /// Returns the structure that manages mapping between definitions and declarations Q_SCRIPTABLE static Definitions* definitions(); /// Returns the structure that manages mapping between declarations, and which top level contexts contain uses of them. static Uses* uses(); /** * Retrieve the read write lock for the entire definition-use chain. * To call non-const methods, you must be holding a write lock. * * Evaluations made prior to holding a lock (including which objects * exist) must be verified once the lock is held, as they may have changed * or been deleted. * * \threadsafe */ Q_SCRIPTABLE static DUChainLock* lock(); /// Returns whether the top-context with the given index is currently loaded in memory Q_SCRIPTABLE bool isInMemory(uint topContextIndex) const; /** * Changes the environment attached to the given top-level context, and updates the management-structures to reflect that * */ Q_SCRIPTABLE void updateContextEnvironment( TopDUContext* context, ParsingEnvironmentFile* file ); ///Allocates a new identity for a new top-context, no lock needed. The returned value is never zero static uint newTopContextIndex(); ///If you call this, the persistent disk-storage structure will stay unaffected, and no duchain cleanup will be done. ///Call this from within tests. void disablePersistentStorage(bool disable = true); ///Stores the whole duchain and all its repositories in the current state to disk ///The duchain must not be locked in any way void storeToDisk(); ///Compares the whole duchain and all its repositories in the current state to disk ///When the comparison fails, debug-output will show why ///The duchain must not be locked when calling this ///@return true If the current memory state equals the disk state, else false bool compareToDisk(); Q_SIGNALS: ///Is emitted when the declaration has been selected somewhere in the user-interface, for example in the completion-list void declarationSelected(const KDevelop::DeclarationPointer& decl); /** * This signal is emitted whenever the DUChain data associated with @p url was updated. * * You can connect to this signal to get notified when the DUChain for a given file was updated. */ void updateReady(const KDevelop::IndexedString& url, const KDevelop::ReferencedTopDUContext& topContext); public Q_SLOTS: ///Removes the given top-context from the duchain, and deletes it. void removeDocumentChain(KDevelop::TopDUContext* document); ///Emits the declarationSelected signal, so other parties can notice it. void emitDeclarationSelected(const KDevelop::DeclarationPointer& decl); /** * Call this after you have modified the DUChain data associated with the file @p url. * * This triggers an emit of the @c updateReady signal. */ void emitUpdateReady(const KDevelop::IndexedString& url, const KDevelop::ReferencedTopDUContext& topContext); /** * Shutdown and cleanup the DUChain. */ void shutdown(); private Q_SLOTS: void documentActivated(KDevelop::IDocument* doc); void documentLoadedPrepare(KDevelop::IDocument* document); void documentRenamed(KDevelop::IDocument* document); void documentClosed(KDevelop::IDocument*); private: TopDUContext* loadChain(uint index); //These two are exported here so that the extremely frequently called chainForIndex(..) can be inlined static bool m_deleted; static std::vector chainsByIndex; static QMutex chainsByIndexLock; /// Increases the reference-count for the given top-context. The result: It will not be unloaded. /// Do this to prevent KDevelop from unloading a top-context that you plan to use. Don't forget calling unReferenceToContext again, /// else the top-context will stay in memory for ever. void refCountUp(TopDUContext* top); /// Decreases the reference-count for the given top-context. When it reaches zero, KDevelop is free to unload it at any time, /// also invalidating all the contained declarations and contexts. void refCountDown(TopDUContext* top); void addToEnvironmentManager( TopDUContext * chain ); void removeFromEnvironmentManager( TopDUContext * chain ); DUChain(); ~DUChain() override; friend class DUChainPrivate; friend class ReferencedTopDUContext; }; } #endif // KDEVPLATFORM_DUCHAIN_H diff --git a/language/duchain/duchaindumper.cpp b/language/duchain/duchaindumper.cpp index 65260e492..a86c51ec4 100644 --- a/language/duchain/duchaindumper.cpp +++ b/language/duchain/duchaindumper.cpp @@ -1,204 +1,204 @@ /* This file is part of KDevelop Copyright 2002-2005 Roberto Raggi Copyright 2006 Hamish Rodda Copyright 2010 Milian Wolff Copyright 2014 Kevin Funk 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 "duchaindumper.h" -#include +#include #include #include "definitions.h" #include "ducontext.h" #include "topducontext.h" #include "declaration.h" #include "duchainpointer.h" #include "identifier.h" #include "use.h" #include "problem.h" #include #include "functiondefinition.h" #include #include using namespace KDevelop; namespace { QDebug fromTextStream(const QTextStream& out) { if (out.device()) return {out.device()}; return {out.string()}; } QString typeToString(DUContext::ContextType type) { switch(type) { case DUContext::Global: return QStringLiteral("Global"); case DUContext::Namespace: return QStringLiteral("Namespace"); case DUContext::Class: return QStringLiteral("Class"); case DUContext::Function: return QStringLiteral("Function"); case DUContext::Template: return QStringLiteral("Template"); case DUContext::Enum: return QStringLiteral("Enum"); case DUContext::Helper: return QStringLiteral("Helper"); case DUContext::Other: return QStringLiteral("Other"); } Q_ASSERT(false); return QString(); } } struct DUChainDumper::Private { Private() : m_indent(0) {} void dumpProblems(TopDUContext* top, QTextStream& out); void dump(DUContext* context, int allowedDepth, bool isFromImport, QTextStream& out); int m_indent; Features m_features; QSet m_visitedContexts; }; DUChainDumper::DUChainDumper(Features features) : d(new Private) { d->m_features = features; } DUChainDumper::~DUChainDumper( ) { } class Indent { public: explicit Indent(int level): m_level(level) {} friend QDebug& operator<<(QDebug& debug, const Indent& ind) { for (int i=0; iproblems().isEmpty()) { qout << top->problems().size() << "problems encountered:" << endl; foreach(const ProblemPointer& p, top->problems()) { qout << Indent(m_indent * 2) << p->description() << p->explanation() << p->finalLocation() << endl; } qout << endl; } } void DUChainDumper::Private::dump(DUContext * context, int allowedDepth, bool isFromImport, QTextStream& out) { QDebug qout = fromTextStream(out); qout << Indent(m_indent * 2) << (isFromImport ? " ==import==>" : "") << (dynamic_cast(context) ? "Top-Context" : "Context") << typeToString(context->type()) << "(owner: " << context->owner() << ")" << context << context->localScopeIdentifier() << "[" << context->scopeIdentifier(true) << "]" << context->range().castToSimpleRange() << (dynamic_cast(context) ? static_cast(context)->url().byteArray(): "") << endl; if(m_visitedContexts.contains(context)) { qout << Indent((m_indent+2) * 2) << "(Skipping" << context->scopeIdentifier(true) << "because it was already printed)" << endl; return; } m_visitedContexts.insert(context); auto top = context->topContext(); if (allowedDepth >= 0) { foreach (Declaration* dec, context->localDeclarations(top)) { //IdentifiedType* idType = dynamic_cast(dec->abstractType().data()); qout << Indent((m_indent+2) * 2) << "Declaration:" << dec->toString() << "[" << dec->qualifiedIdentifier() << "]" << dec << "(internal ctx:" << dec->internalContext() << ")" << dec->range().castToSimpleRange() << "," << (dec->isDefinition() ? "defined, " : (FunctionDefinition::definition(dec) ? "" : "no definition, ")) << dec->uses().count() << "use(s)." << endl; if (FunctionDefinition::definition(dec)) { qout << Indent((m_indent+2) * 2 + 1) << "Definition:" << FunctionDefinition::definition(dec)->range().castToSimpleRange() << endl; } QMap > uses = dec->uses(); for(QMap >::const_iterator it = uses.constBegin(); it != uses.constEnd(); ++it) { qout << Indent((m_indent+3) * 2) << "File:" << it.key().str() << endl; foreach (const RangeInRevision range, *it) qout << Indent((m_indent+4) * 2) << "Use:" << range.castToSimpleRange() << endl; } } } else { qout << Indent((m_indent+2) * 2) << context->localDeclarations(top).count() << "Declarations," << context->childContexts().size() << "child-contexts" << endl; } ++m_indent; { /* foreach (const DUContext::Import &parent, context->importedParentContexts()) { DUContext* import = parent.context(top); if(!import) { qout << Indent((m_indent+2) * 2+1) << "Could not get parent, is it registered in the DUChain?" << endl; continue; } dump(import, allowedDepth-1, true, out); } */ foreach (DUContext* child, context->childContexts()) dump(child, allowedDepth-1, false, out); } --m_indent; } void DUChainDumper::dump(DUContext* context, int allowedDepth, QTextStream& out) { d->m_visitedContexts.clear(); if (!context) { out << "Error: Null context" << endl; return; } auto top = context->topContext(); if (d->m_features.testFlag(DumpProblems)) { d->dumpProblems(top, out); } if (d->m_features.testFlag(DumpContext)) { d->dump(context, allowedDepth, false, out); } } void DUChainDumper::dump(DUContext* context, int allowedDepth) { QTextStream out(stdout); dump(context, allowedDepth, out); } diff --git a/language/duchain/duchainpointer.h b/language/duchain/duchainpointer.h index 4a8f23e36..7863a1615 100644 --- a/language/duchain/duchainpointer.h +++ b/language/duchain/duchainpointer.h @@ -1,192 +1,192 @@ /* 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. */ #ifndef KDEVPLATFORM_DUCHAINPOINTER_H #define KDEVPLATFORM_DUCHAINPOINTER_H -#include -#include +#include +#include #include #include //krazy:excludeall=dpointer namespace KDevelop { class DUContext; class TopDUContext; class DUChainBase; class Declaration; class AbstractFunctionDeclaration; /** * Whenever the du-chain is unlocked and locked again, any du-chain item may have been deleted in between. * For that reason, the following class should be used to make sure that no deleted objects are accessed. It contains a pointer * that will be reset to zero once the pointed object is deleted. * * Access to the data must still be serialized through duchain-locking. Using this comes with no additional cost. * * In practice this means: * Store an instance of DUChainPointer instead of a pointer to the du-chain object. * Then, access the eventually still existing object by calling pointer->base(). * * To make it even more convenient see DUChainPointer * */ class KDEVPLATFORMLANGUAGE_EXPORT DUChainPointerData : public QSharedData { public: /** * Will return zero once the pointed-to object was deleted * */ DUChainBase* base(); /** * Will return zero once the pointed-to object was deleted * */ DUChainBase* base() const; ///Default-initialization of an invalid reference DUChainPointerData(); ~DUChainPointerData(); private: ///Should not be used from outside, but is needed sometimes to construct an invalid dummy-pointer explicit DUChainPointerData( DUChainBase* base ); friend class DUChainBase; DUChainBase * m_base; Q_DISABLE_COPY(DUChainPointerData) }; /** * A smart-pointer similar class that conveniently wraps around DUChainPointerData without * too many dynamic casts. * * It can be used like a normal pointer. In order to cast between pointer types, you should * use the staticCast() and dynamicCast() functions as appropriate. * * Access must be serialized by holding the KDevelop::DUChain::lock() as appropriate for the * function(s) being called. **/ template class DUChainPointer { template friend class DUChainPointer; public: DUChainPointer() : d(QExplicitlySharedDataPointer(nullptr)) { } DUChainPointer(const DUChainPointer& rhs) : d(rhs.d) { } ///This constructor includes dynamic casting. If the object cannot be casted to the type, the constructed DUChainPointer will have value zero. template explicit DUChainPointer( OtherType* rhs ) { if( dynamic_cast(rhs) ) d = rhs->weakPointer(); } template explicit DUChainPointer( DUChainPointer rhs ) { if( dynamic_cast(rhs.data()) ) d = rhs.d; } explicit DUChainPointer( QExplicitlySharedDataPointer rhs ) { if( dynamic_cast(rhs->base()) ) d = rhs; } explicit DUChainPointer( Type* rhs ) { if( rhs ) d = rhs->weakPointer(); } bool operator ==( const DUChainPointer& rhs ) const { return d.data() == rhs.d.data(); } bool operator !=( const DUChainPointer& rhs ) const { return d.data() != rhs.d.data(); } ///Returns whether the pointed object is still existing operator bool() const { return d && d->base(); } Type& operator* () const { Q_ASSERT(d); return *static_cast(d->base()); } Type* operator->() const { Q_ASSERT(d); return static_cast(d->base()); } bool operator<(const DUChainPointer& rhs) const { return d.data() < rhs.d.data(); } template DUChainPointer dynamicCast() const { if( d && dynamic_cast( d->base() ) ) //When the reference to the pointer is constant that doesn't mean that the pointed object needs to be constant return DUChainPointer( static_cast(d->base()) ); else return DUChainPointer(); } Type* data() const { if( !d ) return nullptr; return static_cast(d->base()); } DUChainPointer& operator= ( Type* rhs ) { if( rhs ) d = rhs->weakPointer(); else d = nullptr; return *this; } private: QExplicitlySharedDataPointer d; }; typedef DUChainPointer DUChainBasePointer; typedef DUChainPointer DUContextPointer; typedef DUChainPointer TopDUContextPointer; typedef DUChainPointer DeclarationPointer; typedef DUChainPointer FunctionDeclarationPointer; } Q_DECLARE_METATYPE( KDevelop::DUChainBasePointer ) Q_DECLARE_METATYPE( KDevelop::DeclarationPointer ) Q_DECLARE_METATYPE( KDevelop::DUContextPointer ) Q_DECLARE_METATYPE( KDevelop::TopDUContextPointer ) Q_DECLARE_METATYPE( QList ) #endif diff --git a/language/duchain/ducontext.h b/language/duchain/ducontext.h index f4d78f3c6..9355c6979 100644 --- a/language/duchain/ducontext.h +++ b/language/duchain/ducontext.h @@ -1,954 +1,954 @@ /* This file is part of KDevelop Copyright 2006 Hamish Rodda Copyright 2007-2009 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. */ #ifndef KDEVPLATFORM_DUCONTEXT_H #define KDEVPLATFORM_DUCONTEXT_H -#include -#include -#include -#include +#include +#include +#include +#include #include #include "identifier.h" #include "duchainbase.h" #include "types/abstracttype.h" #include "duchainpointer.h" #include "declarationid.h" #include "indexedducontext.h" #include "navigation/abstractnavigationwidget.h" class QWidget; namespace KDevelop { class Declaration; class DUChain; class Use; class TopDUContext; class DUContext; class DUContextData; class KDEVPLATFORMLANGUAGE_EXPORT DUChainVisitor { public: virtual void visit(DUContext* context) = 0; virtual void visit(Declaration* declaration) = 0; virtual ~DUChainVisitor(); }; typedef DUChainPointer DUContextPointer; /** * A single context in source code, represented as a node in a * directed acyclic graph. * * Access to context objects must be serialised by holding the * chain lock, ie. DUChain::lock(). * * NOTE: A du-context can be freely edited as long as it's parent-context is zero. * In the moment the parent-context is set, the context may only be edited when it * is allowed to edited it's top-level context(@see TopLevelContext::inDUChain() * * @todo change child relationships to a linked list within the context? */ class KDEVPLATFORMLANGUAGE_EXPORT DUContext : public DUChainBase { friend class Use; friend class Declaration; friend class DeclarationData; friend class DUContextData; friend class DUContextDynamicData; friend class Definition; friend class VisibleDeclarationIterator; public: /** * Constructor. No convenience methods, as the initialisation order is important, * * @param anonymous Whether the context should be added as an anonymous context to the parent. That way the context can never be found through any of the parent's member-functions. * * If the parent is in the symbol table and the context is not anonymous, it will also be added to the symbol table. You nead a write-lock to the DUChain then */ explicit DUContext(const RangeInRevision& range, DUContext* parent = nullptr, bool anonymous = false); explicit DUContext(DUContextData&); /** * Destructor. Will delete all child contexts which are defined within * the same file as this context. */ ~DUContext() override; enum ContextType : quint8 { 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. This context is treated specially during search: * when searching within the imports of a context, and that context's parent * is a context of type DUContext::Helper, then the upwards search is continued * into that helper(The decision happens in shouldSearchInParent) */, Other /**< Represents executable code, like for example within a compound-statement */ }; enum SearchFlag { NoSearchFlags = 0 /**< Searching for everything */, InImportedParentContext = 1 /**< Internal, do not use from outside */, OnlyContainerTypes = 2 /**< Not implemented yet */, DontSearchInParent = 4 /**< IF this flag is set, findDeclarations(..) will not search for the identifier in parent-contexts(which does not include imported parent-contexts) */, NoUndefinedTemplateParams = 8 /**< For languages that support templates(like C++). If this is set, the search should fail as soon as undefined template-parameters are involved. */, DirectQualifiedLookup = 16 /**< When this flag is used, the searched qualified identifier should NOT be split up into it's components and looked up one by one. Currently only plays a role in C++ specific parts. */, NoFiltering = 32 /**< Should be set when no filtering at all is wished, not even filtering that is natural for the underlying language(For example in C++, constructors are filtered out be default) */, OnlyFunctions = 64 /**< When this is given, only function-declarations are returned. In case of C++, this also means that constructors can be retrieved, while normally they are filtered out. */, NoImportsCheck = 128 /**< With this parameter, a global search will return all matching items, from all contexts, not only from imported ones. */, NoSelfLookUp = 256 /**< With this parameter, the special-treatment during search that allows finding the context-class by its name is disabled. */, DontResolveAliases = 512 /**< Disables the resolution of alias declarations in the returned list*/, LastSearchFlag = 1024 }; Q_DECLARE_FLAGS(SearchFlags, SearchFlag) ContextType type() const; void setType(ContextType type); /** * If this context was opened by a declaration or definition, this returns that item. * * The returned declaration/definition will have this context set as @c internalContext() */ Declaration* owner() const; /** * Sets the declaration/definition, and also updates it's internal context (they are strictly paired together). * * The declaration has to be part of the same top-context. */ void setOwner(Declaration* decl); /** * Calculate the depth of this context, from the top level context in the file. */ int depth() const; /** * Find the top context. */ TopDUContext* topContext() const override; /** * Visits all duchain objects in the whole duchain. * * Classes that hold a unique link to duchain objects like instantiations * have to pass the visitor over to those classes. * */ virtual void visit(DUChainVisitor& visitor); /** * Find the context which most specifically covers @p position. * * The search is recursive, so the most specific context is found. * * @param includeBorders When this is true, contexts will also be found that * have the position on their borders. * * @warning This uses the ranges in the local revision of the document (at last parsing time). * Use DUChainBase::transformToLocalRevision to transform the cursor into that revision first. */ DUContext* findContextAt(const CursorInRevision& position, bool includeBorders = false) const; /** * Find a child declaration that has a rang that covers the given @p position. * * The search is local, not recursive. * * @warning This uses the ranges in the local revision of the document (at last parsing time). * Use DUChainBase::transformToLocalRevision to transform the cursor into that revision first. */ Declaration* findDeclarationAt(const CursorInRevision& position) const; /** * Find the context which most specifically covers @a range. * * @warning This uses the ranges in the local revision of the document (at last parsing time). * Use DUChainBase::transformToLocalRevision to transform the cursor into that revision first. */ DUContext* findContextIncluding(const RangeInRevision& range) const; /** * Calculate the fully qualified scope identifier. */ QualifiedIdentifier scopeIdentifier(bool includeClasses = false) const; /** * Returns true if this context has the same scope identifier as the given one. * * @note This is much more efficient than computing the identifiers through @c scopeIdentifier(..) * and comparing them */ bool equalScopeIdentifier(const DUContext* rhs) const; /** * Scope identifier, used to qualify the identifiers occurring in each context. * * This is the part relative to the parent context. */ QualifiedIdentifier localScopeIdentifier() const; /** * Same as @c localScopeIdentifier(), but faster. */ IndexedQualifiedIdentifier indexedLocalScopeIdentifier() const; /** * Scope identifier, used to qualify the identifiers occurring in each context * This must not be called once this context has children. */ void setLocalScopeIdentifier(const QualifiedIdentifier& identifier); /** * Returns whether this context is listed in the symbol table (Namespaces and classes) */ bool inSymbolTable() const; /** * Move this object into/out of the symbol table. * * @note You need to have a duchain write lock, unless this is a TopDUContext. */ void setInSymbolTable(bool inSymbolTable); /** * Returns the immediate parent context of this context. */ DUContext* parentContext() const; /** * Represents an imported parent context. */ struct KDEVPLATFORMLANGUAGE_EXPORT Import { /** * @note DUChain must be read-locked when this is called */ Import(DUContext* context, const DUContext* importer, const CursorInRevision& position = CursorInRevision::invalid()); Import() : position(CursorInRevision::invalid()) { } explicit Import(const DeclarationId& id, const CursorInRevision& position = CursorInRevision::invalid()); bool operator==(const Import& rhs) const { return m_context == rhs.m_context && m_declaration == rhs.m_declaration; } /** * @param topContext The top-context from where to start searching. * This is important to find the correct imports * in the case of templates or similar structures. */ DUContext* context(const TopDUContext* topContext, bool instantiateIfRequired = true) const; /** * Returns the top-context index, if this import is not a specialization import. */ uint topContextIndex() const { return m_context.topContextIndex(); } IndexedDUContext indexedContext() const { return m_context; } /** * Returns true if this import is direct. * * That is, the import is not referred to by its identifier, * but rather directly by its index. */ bool isDirect() const; /** * If this import is indirect, returns the imported declaration-id */ DeclarationId indirectDeclarationId() const { return m_declaration; } CursorInRevision position; private: //Either we store m_declaration, or m_context. That way we can resolve specialized contexts. ///@todo Compress using union DeclarationId m_declaration; IndexedDUContext m_context; }; /** * Returns the list of imported parent contexts for this context. * * @warning The list may contain objects that are not valid any more, * i.e. data() returns zero, @see addImportedParentContext) * @warning The import structure may contain loops if this is a TopDUContext, * so be careful when traversing the tree. * @note This is expensive. */ virtual QVector importedParentContexts() const; /** * If the given context is directly imported into this one, and * @c addImportedParentContext(..) was called with a valid cursor, * this will return that position. Otherwise an invalid cursor is returned. */ virtual CursorInRevision importPosition(const DUContext* target) const; /** * Returns true if this context imports @param origin at any depth, else false. */ virtual bool imports(const DUContext* origin, const CursorInRevision& position = CursorInRevision::invalid()) const; /** * Adds an imported context. * * @param anonymous If this is true, the import will not be registered at the imported context. * This allows du-chain contexts importing without having a write-lock. * @param position Position where the context is imported. This is mainly important in C++ with included files. * * If the context is already imported, only the position is updated. * * @note Be sure to have set the text location first, so that the chain is sorted correctly. */ virtual void addImportedParentContext(DUContext* context, const CursorInRevision& position = CursorInRevision::invalid(), bool anonymous = false, bool temporary = false); /** * Adds an imported context, which may be indirect. * * @warning This is only allowed if this context is _NOT_ a top-context. * @warning When using this mechanism, this context will not be registered as importer to the other one. * @warning The given import _must_ be indirect. * * @return true if the import was already imported before, else false. */ bool addIndirectImport(const DUContext::Import& import); /** * Removes a child context. */ virtual void removeImportedParentContext(DUContext* context); /** * Clear all imported parent contexts. */ virtual void clearImportedParentContexts(); /** * If this is set to true, all declarations that are added to this context * will also be visible in the parent-context. * * They will be visible in the parent using @c findDeclarations(...) and * @c findLocalDeclarations(...), but will not be in the list of @c localDeclarations(...). */ void setPropagateDeclarations(bool propagate); bool isPropagateDeclarations() const; /** * Returns the list of contexts importing this context. * * @note Very expensive, since the importers top-contexts need to be loaded. */ virtual QVector importers() const; /** * Returns the list of indexed importers. * * Cheap, because nothing needs to be loaded. */ KDevVarLengthArray indexedImporters() const; /** * Returns the list of immediate child contexts for this context. * * @note This is expensive. */ QVector childContexts() const; /** * Clears and deletes all child contexts recursively. * * This will not cross file boundaries. */ void deleteChildContextsRecursively(); /** * Resort the child contexts by their range. * * You must call this when you manually change the range of child contexts in a way * that could break the internal range sorting. */ void resortChildContexts(); /** * Returns true if this declaration is accessible through the du-chain, * and thus cannot be edited without a du-chain write lock */ virtual bool inDUChain() const; /** * Retrieve the context which is specialized with the given * @a specialization as seen from the given @a topContext. * * @param specialization the specialization index (see DeclarationId) * @param topContext the top context representing the perspective from which to specialize. * if @p topContext is zero, only already existing specializations are returned, * and if none exists, zero is returned. * @param upDistance upwards distance in the context-structure of the * given specialization-info. This allows specializing children. */ virtual DUContext* specialize(const IndexedInstantiationInformation& specialization, const TopDUContext* topContext, int upDistance = 0); /** * Searches for and returns a declaration with a given @p identifier in this context, which * is currently active at the given text @p position, with the given type @p dataType. * In fact, only items are returned that are declared BEFORE that position. * * @param identifier the identifier of the definition to search for * @param position the text position to search for * @param topContext the top-context from where a completion is triggered. * This is needed so delayed types (templates in C++) can be resolved in the correct context. * @param dataType the type to match, or null for no type matching. * * @returns the requested declaration if one was found, otherwise null. * * @warning this may return declarations which are not in this tree, you may need to lock them too... */ QList findDeclarations(const QualifiedIdentifier& identifier, const CursorInRevision& position = CursorInRevision::invalid(), const AbstractType::Ptr& dataType = AbstractType::Ptr(), const TopDUContext* topContext = nullptr, SearchFlags flags = NoSearchFlags) const; /** * Searches for and returns a declaration with a given @a identifier in this context, which * is currently active at the given text @a position. * * @param identifier the identifier of the definition to search for * @param topContext the top-context from where a completion is triggered. * This is needed so delayed types(templates in C++) can be resolved in the correct context. * @param position the text position to search for * * @returns the requested declaration if one was found, otherwise null. * * @warning this may return declarations which are not in this tree, you may need to lock them too... * * @overload */ QList findDeclarations(const IndexedIdentifier& identifier, const CursorInRevision& position = CursorInRevision::invalid(), const TopDUContext* topContext = nullptr, SearchFlags flags = NoSearchFlags) const; /** * Prefer the version above for speed reasons. */ QList findDeclarations(const Identifier& identifier, const CursorInRevision& position = CursorInRevision::invalid(), const TopDUContext* topContext = nullptr, SearchFlags flags = NoSearchFlags) const; /** * Returns the type of any @a identifier defined in this context, or * null if one is not found. * * Does not search imported parent-contexts(like base-classes). */ QList findLocalDeclarations(const IndexedIdentifier& identifier, const CursorInRevision& position = CursorInRevision::invalid(), const TopDUContext* topContext = nullptr, const AbstractType::Ptr& dataType = AbstractType::Ptr(), SearchFlags flags = NoSearchFlags) const; /** * Prefer the version above for speed reasons. */ QList findLocalDeclarations(const Identifier& identifier, const CursorInRevision& position = CursorInRevision::invalid(), const TopDUContext* topContext = nullptr, const AbstractType::Ptr& dataType = AbstractType::Ptr(), SearchFlags flags = NoSearchFlags) const; /** * Clears all local declarations. * * Does not delete the declaration; the caller assumes ownership. */ QVector clearLocalDeclarations(); /** * Clears all local declarations. * * Deletes these declarations, as the context has ownership. */ void deleteLocalDeclarations(); /** * Returns all local declarations * * @param source A source-context that is needed to instantiate template-declarations in some cases. * If it is zero, that signalizes that missing members should not be instantiated. */ virtual QVector localDeclarations(const TopDUContext* source = nullptr) const; /** * Resort the local declarations by their range. * * You must call this when you manually change the range of declarations in a way * that could break the internal range sorting. */ void resortLocalDeclarations(); /** * Searches for the most specific context for the given cursor @p position in the given @p url. * * @param position the text position to search for * @param parent the parent context to search from (this is mostly an internal detail, but if you only * want to search in a subbranch of the chain, you may specify the parent here) * * @returns the requested context if one was found, otherwise null. */ DUContext* findContext(const CursorInRevision& position, DUContext* parent = nullptr) const; /** * Iterates the tree to see if the provided @a context is a subcontext of this context. * * @returns true if @a context is a subcontext, otherwise false. */ bool parentContextOf(DUContext* context) const; /** * Return a list of all reachable declarations for a given cursor @p position in a given @p url. * * @param position the text position to search for * @param topContext the top-context from where a completion is triggered. * This is needed so delayed types(templates in C++) can be resolved * in the correct context. * @param searchInParents should declarations from parent-contexts be listed? * If false, only declarations from this and imported contexts will be returned. * * The returned declarations are paired together with their inheritance-depth, * which is the count of steps to into other contexts that were needed to find the declaration. * Declarations reached through a namespace- or global-context are offsetted by 1000. * * This also includes Declarations from sub-contexts that were propagated upwards * using @c setPropagateDeclarations(true). * * @returns the requested declarations, if any were active at that location. * Declarations propagated into this context(@c setPropagateDeclarations) are included. */ QList< QPair > allDeclarations(const CursorInRevision& position, const TopDUContext* topContext, bool searchInParents = true) const; /** * Delete and remove all slaves (uses, declarations, definitions, contexts) that are not in the given set. */ void cleanIfNotEncountered(const QSet& encountered); /** * Uses: * A "Use" represents any position in a document where a Declaration is used literally. * For efficiency, since there can be many many uses, they are managed efficiently by * TopDUContext and DUContext. In TopDUContext, the used declarations are registered * and assigned a "Declaration-Index" while calling TopDUContext::indexForUsedDeclaration. * From such a declaration-index, the declaration can be retrieved back by calling * @c TopDUContext::usedDeclarationForIndex. * * The actual uses are stored within DUContext, where each use consists of a range and * the declaration-index of the used declaration. * */ /** * Return a vector of all uses which occur in this context. * * To get the actual declarations, use @c TopDUContext::usedDeclarationForIndex(..) * with the declarationIndex. */ const Use* uses() const; /** * Returns the count of uses that can be accessed through @c uses() */ int usesCount() const; /** * Determines whether the given declaration has uses or not */ static bool declarationHasUses(Declaration* decl); /** * Find the use which encompasses @a position, if one exists. * @return The local index of the use, or -1 */ int findUseAt(const CursorInRevision& position) const; /** * @note The change must not break the ordering */ void changeUseRange(int useIndex, const RangeInRevision& range); /** * Assigns the declaration represented by @p declarationIndex * to the use with index @p useIndex. */ void setUseDeclaration(int useIndex, int declarationIndex); /** * Creates a new use of the declaration given through @p declarationIndex. * The index must be retrieved through @c TopDUContext::indexForUsedDeclaration(..). * * @param range The range of the use * @param insertBefore A hint where in the vector of uses to insert the use. * Must be correct so the order is preserved(ordered by position), * or -1 to automatically choose the position. * * @return Local index of the created use */ int createUse(int declarationIndex, const RangeInRevision& range, int insertBefore = -1); /** * Deletes the use number @p index. * * @param index is the position in the vector of uses, not a used declaration index. */ void deleteUse(int index); /** * Clear and delete all uses in this context. */ virtual void deleteUses(); /** * Recursively delete all uses in this context and all its child-contexts */ virtual void deleteUsesRecursively(); /** * Can be specialized by languages to create a navigation/information-widget. * * Ideally, the widget would be based on @c KDevelop::QuickOpenEmbeddedWidgetInterface * for user-interaction within the quickopen list. * * The returned widget will be owned by the caller. * * @param decl A member-declaration of this context the navigation-widget should be created for. * Zero to create a widget for this context. * @param topContext Top-context from where the navigation-widget is triggered. * In C++, this is needed to resolve forward-declarations. * @param htmlPrefix Html-formatted text that should be prepended before any information shown by this widget * @param htmlSuffix Html-formatted text that should be appended to any information shown by this widget * * Can return zero which disables the navigation widget. * * If you setProperty("DoNotCloseOnCursorMove", true) on the widget returned, * then the widget will not close when the cursor moves in the document, which * enables you to change the document contents from the widget without immediately closing the widget. */ virtual QWidget* createNavigationWidget(Declaration* decl = nullptr, TopDUContext* topContext = nullptr, const QString& htmlPrefix = QString(), const QString& htmlSuffix = QString(), AbstractNavigationWidget::DisplayHints hints = AbstractNavigationWidget::NoHints) const; enum { Identity = 2 }; /** * Represents multiple qualified identifiers in a way that is better * to manipulate and allows applying namespace-aliases or -imports easily. * * A SearchItem generally represents a tree of identifiers, and represents * all the qualified identifiers that can be constructed by walking * along the tree starting at an arbitrary root-node into the depth using the "next" pointers. * * The insertion order in the hierarchy determines the order of the represented list. */ struct KDEVPLATFORMLANGUAGE_EXPORT SearchItem : public QSharedData { typedef QExplicitlySharedDataPointer Ptr; typedef KDevVarLengthArray PtrList; /** * Constructs a representation of the given @p id qualified identifier, * starting at its index @p start. * * @param nextItem is set as next item to the last item in the chain */ explicit SearchItem(const QualifiedIdentifier& id, const Ptr& nextItem = Ptr(), int start = 0); /** * Constructs a representation of the given @p id qualified identifier, * starting at its index @p start. * * @param nextItems is set as next item to the last item in the chain */ SearchItem(const QualifiedIdentifier& id, const PtrList& nextItems, int start = 0); SearchItem(bool explicitlyGlobal, const IndexedIdentifier& id, const PtrList& nextItems); SearchItem(bool explicitlyGlobal, const IndexedIdentifier& id, const Ptr& nextItem); bool isEmpty() const; bool hasNext() const; /** * Appends the given item to every item that can be reached from this item, * and not only to the end items. * * The effect to search is that the given item is searched with all prefixes * contained in this earch-item prepended. * * @warning This changes all contained sub-nodes, but they can be shared with * other SearchItem trees. You should not use this on SearchItem trees * that have shared nodes with other trees. * * @note These functions ignore explicitly global items. */ void addToEachNode(const Ptr& item); void addToEachNode(const PtrList& items); /** * Returns true if the given identifier matches one of the identifiers * represented by this SearchItem. Does not respect the explicitlyGlobal flag */ bool match(const QualifiedIdentifier& id, int offset = 0) const; /** * @note expensive */ QList toList(const QualifiedIdentifier& prefix = QualifiedIdentifier()) const; void addNext(const Ptr& other); bool isExplicitlyGlobal; IndexedIdentifier identifier; PtrList next; }; ///@todo Should be protected, moved here temporarily until I have figured ///out why the gcc 4.1.3 fails in cppducontext.h:212, which should work (within kdevelop) /// Declaration search implementation typedef QList DeclarationList; /** * This is a more complex interface to the declaration search engine. * * Always prefer @c findDeclarations(..) when possible. * * Advantage of this interface: * - You can search multiple identifiers at one time. * However, those should be aliased identifiers for one single item, because * search might stop as soon as one item is found. * - The source top-context is needed to correctly resolve template-parameters * * @param position A valid position, if in doubt use textRange().end() * * @warning @p position must be valid! * * @param depth Depth of the search in parents. This is used to prevent endless * recursions in endless import loops. * * * @return whether the search was successful. If it is false, it had to be stopped * for special reasons (like some flags) */ virtual bool findDeclarationsInternal(const SearchItem::PtrList& identifiers, const CursorInRevision& position, const AbstractType::Ptr& dataType, DeclarationList& ret, const TopDUContext* source, SearchFlags flags, uint depth ) const; /** * Returns the qualified identifier @p id with all aliases (for example namespace imports) applied * * @example: If the namespace 'Foo' is imported, and id is 'Bar', * then the returned list is 'Bar' and 'Foo::Bar' */ QList fullyApplyAliases(const QualifiedIdentifier& id, const TopDUContext* source) const; protected: /** * After one scope was searched, this function is asked whether more * results should be collected. Override it, for example to collect overloaded functions. * * The default-implementation returns true as soon as @p decls is not empty. */ virtual bool foundEnough( const DeclarationList& decls , SearchFlags flags ) const; /** * Merges definitions and their inheritance-depth up all branches of the * definition-use chain into one hash. * * This includes declarations propagated from sub-contexts. * * @param hadContexts is used to count together all contexts that already were * visited, so they are not visited again. */ virtual void mergeDeclarationsInternal(QList< QPair >& definitions, const CursorInRevision& position, QHash& hadContexts, const TopDUContext* source, bool searchInParents = true, int currentDepth = 0) const; void findLocalDeclarationsInternal(const Identifier& identifier, const CursorInRevision & position, const AbstractType::Ptr& dataType, DeclarationList& ret, const TopDUContext* source, SearchFlags flags ) const; virtual void findLocalDeclarationsInternal(const IndexedIdentifier& identifier, const CursorInRevision & position, const AbstractType::Ptr& dataType, DeclarationList& ret, const TopDUContext* source, SearchFlags flags ) const; /** * Applies namespace-imports and namespace-aliases and returns * possible absolute identifiers that need to be searched. * * @param targetIdentifiers will be filled with all identifiers that should * be searched for, instead of identifier. * @param onlyImports if this is true, namespace-aliases will not be respected, * but only imports. This is faster. */ void applyAliases(const SearchItem::PtrList& identifiers, SearchItem::PtrList& targetIdentifiers, const CursorInRevision& position, bool canBeNamespace, bool onlyImports = false) const; /** * Applies the aliases that need to be applied when moving the search * from this context up to the parent-context. * * The default-implementation adds a set of identifiers with the own local * identifier prefixed, if this is a namespace. * * For C++, this is needed when searching out of a namespace, so the item * can be found within that namespace in another place. */ virtual void applyUpwardsAliases(SearchItem::PtrList& identifiers, const TopDUContext* source) const; DUContext(DUContextData& dd, const RangeInRevision& range, DUContext* parent = nullptr, bool anonymous = false); /** * Just uses the data from the given context. Doesn't copy or change anything, * and the data will not be deleted on this contexts destruction. */ DUContext(DUContext& useDataFrom); /** * Whether this context, or any of its parent contexts, has been inserte * anonymously into the du-chain * * @see DUContext::DUContext */ bool isAnonymous() const; /** * This is called whenever the search needs to do the decision whether it * should be continued in the parent context. * * It is not called when the DontSearchInParent flag is set. Else this should * be overridden to do language-specific logic. * * The default implementation returns false if the flag InImportedParentContext is set. */ virtual bool shouldSearchInParent(SearchFlags flags) const; private: void rebuildDynamicData(DUContext* parent, uint ownIndex) override; friend class TopDUContext; friend class IndexedDUContext; friend class LocalIndexedDUContext; friend class TopDUContextDynamicData; DUCHAIN_DECLARE_DATA(DUContext) class DUContextDynamicData* m_dynamicData; }; /** * This is the identifier that can be used to search namespace-import declarations, * and should be used to store namespace-imports. * * It is stored statically for performance-reasons, so it doesn't need to be * constructed every time it is used. * * @see NamespaceAliasDeclaration. */ KDEVPLATFORMLANGUAGE_EXPORT const Identifier& globalImportIdentifier(); /** * This is the identifier that can be used to search namespace-alias declarations. * * It is stored statically for performance-reasons, so it doesn't need to be * constructed every time it is used. * * @see NamespaceAliasDeclaration. */ KDEVPLATFORMLANGUAGE_EXPORT const Identifier& globalAliasIdentifier(); /** * This is the identifier that can be used to search namespace-import declarations, * and should be used to store namespace-imports. * * It is stored statically for performance-reasons, so it doesn't need to be * constructed every time it is used. * * @see NamespaceAliasDeclaration. */ KDEVPLATFORMLANGUAGE_EXPORT const IndexedIdentifier& globalIndexedImportIdentifier(); /** * This is the identifier that can be used to search namespace-alias declarations. * * It is stored statically for performance-reasons, so it doesn't need to be * constructed every time it is used. * * @see NamespaceAliasDeclaration. */ KDEVPLATFORMLANGUAGE_EXPORT const IndexedIdentifier& globalIndexedAliasIdentifier(); /** * Collects all uses of the given @p declarationIndex */ KDEVPLATFORMLANGUAGE_EXPORT QList allUses(DUContext* context, int declarationIndex, bool noEmptyRanges = false); } Q_DECLARE_TYPEINFO(KDevelop::DUContext::Import, Q_MOVABLE_TYPE); KDEVPLATFORMLANGUAGE_EXPORT QDebug operator<<(QDebug dbg, const KDevelop::DUContext::Import& import); #endif // KDEVPLATFORM_DUCONTEXT_H diff --git a/language/duchain/identifier.cpp b/language/duchain/identifier.cpp index 48138620d..92eef8de6 100644 --- a/language/duchain/identifier.cpp +++ b/language/duchain/identifier.cpp @@ -1,1604 +1,1604 @@ /* 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 #include "stringhelpers.h" #include "appendedlist_static.h" #include "serialization/itemrepository.h" #include "util/kdevhash.h" #include "util/debug.h" #include #include #define ifDebug(x) namespace KDevelop { template class IdentifierPrivate { public: IdentifierPrivate() : m_unique(0) , m_refCount(0) , m_hash(0) { } template explicit 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) uint 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. KDevHash kdevhash; kdevhash << m_identifier.hash() << m_unique; FOREACH_FUNCTION_STATIC(const IndexedTypeIdentifier& templateIdentifier, templateIdentifiers) kdevhash << templateIdentifier.hash(); m_hash = kdevhash; } 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) uint hash() const { return m_identifier.hash(); } //Should return the size of an item created with createItem uint 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, 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; }; using IdentifierRepository = RepositoryManager< ItemRepository, false>; static IdentifierRepository& identifierRepository() { static IdentifierRepository identifierRepositoryObject(QStringLiteral("Identifier Repository")); return identifierRepositoryObject; } static uint emptyConstantIdentifierPrivateIndex() { static const uint index = identifierRepository()->index(DynamicIdentifierPrivate()); return index; } static const ConstantIdentifierPrivate* emptyConstantIdentifierPrivate() { static const ConstantIdentifierPrivate item; return &item; } 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 explicit 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) uint 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 ) { KDevHash hash; quint32 bitfields = m_explicitlyGlobal | (m_isExpression << 1); hash << bitfields << identifiersSize(); FOREACH_FUNCTION_STATIC( const IndexedIdentifier& identifier, identifiers ) { hash << identifier.getIndex(); } m_hash = hash; } 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) uint hash() const { return m_identifier.hash(); } //Should return the size of an item created with createItem uint 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, 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; }; using QualifiedIdentifierRepository = RepositoryManager< ItemRepository, false>; static QualifiedIdentifierRepository& qualifiedidentifierRepository() { static QualifiedIdentifierRepository repo(QStringLiteral("Qualified Identifier Repository"), 1, [] () -> AbstractRepositoryManager* { return &identifierRepository(); }); return repo; } static uint emptyConstantQualifiedIdentifierPrivateIndex() { static const uint index = qualifiedidentifierRepository()->index(DynamicQualifiedIdentifierPrivate()); return index; } static const ConstantQualifiedIdentifierPrivate* emptyConstantQualifiedIdentifierPrivate() { static const ConstantQualifiedIdentifierPrivate item; return &item; } 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(const IndexedString& str) { if (str.isEmpty()) { m_index = emptyConstantIdentifierPrivateIndex(); cd = emptyConstantIdentifierPrivate(); } else { m_index = 0; dd = new IdentifierPrivate; dd->m_identifier = str; } } Identifier::Identifier(const QString& id, uint start, uint* takenRange) { if (id.isEmpty()) { m_index = emptyConstantIdentifierPrivateIndex(); cd = emptyConstantIdentifierPrivate(); return; } m_index = 0; dd = new IdentifierPrivate; ///Extract template-parameters ParamIterator paramIt(QStringLiteral("<>:"), 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::operator=(const Identifier& rhs) { if(dd == rhs.dd && cd == rhs.cd) return *this; if(!m_index) delete dd; dd = nullptr; rhs.makeConstant(); cd = rhs.cd; m_index = rhs.m_index; Q_ASSERT(cd); return *this; } Identifier::Identifier(Identifier&& rhs) Q_DECL_NOEXCEPT : m_index(rhs.m_index) { if (m_index) { cd = rhs.cd; } else { dd = rhs.dd; } rhs.cd = emptyConstantIdentifierPrivate(); rhs.m_index = emptyConstantIdentifierPrivateIndex(); } Identifier& Identifier::operator=(Identifier&& rhs) Q_DECL_NOEXCEPT { if(dd == rhs.dd && cd == rhs.cd) return *this; if (!m_index) { delete dd; dd = nullptr; } m_index = rhs.m_index; if (m_index) { cd = rhs.cd; } else { dd = rhs.dd; } rhs.cd = emptyConstantIdentifierPrivate(); rhs.m_index = emptyConstantIdentifierPrivateIndex(); return *this; } 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::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) { if (token != uniqueToken()) { 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) { IndexedString id(identifier); if (id != this->identifier()) { prepareWrite(); dd->m_identifier = std::move(id); } } void Identifier::setIdentifier(const IndexedString& identifier) { if (identifier != this->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; } bool Identifier::inRepository() const { 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(IdentifierStringFormattingOptions options) const { QString ret = identifier().str(); if (!options.testFlag(RemoveTemplateInformation) && templateIdentifiersCount()) { ret.append("< "); for (uint i = 0; i < templateIdentifiersCount(); ++i) { ret.append(templateIdentifier(i).toString(options)); 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); } 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) { if (id.isEmpty()) { m_index = emptyConstantQualifiedIdentifierPrivateIndex(); cd = emptyConstantQualifiedIdentifierPrivate(); return; } 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(QStringLiteral("::"))) { dd->m_explicitlyGlobal = true; dd->splitIdentifiers(id, 2); } else { dd->m_explicitlyGlobal = false; dd->splitIdentifiers(id, 0); } } } QualifiedIdentifier::QualifiedIdentifier(const Identifier& id) { if (id.isEmpty()) { m_index = emptyConstantQualifiedIdentifierPrivateIndex(); cd = emptyConstantQualifiedIdentifierPrivate(); return; } m_index = 0; dd = new DynamicQualifiedIdentifierPrivate; if (id.dd->m_identifier.str().isEmpty()) { dd->m_explicitlyGlobal = true; } else { dd->m_explicitlyGlobal = false; dd->identifiersList.append(IndexedIdentifier(id)); } } QualifiedIdentifier::QualifiedIdentifier() : m_index(emptyConstantQualifiedIdentifierPrivateIndex()) , cd(emptyConstantQualifiedIdentifierPrivate()) { } 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); } } QualifiedIdentifier::QualifiedIdentifier(QualifiedIdentifier&& rhs) Q_DECL_NOEXCEPT : m_index(rhs.m_index) { if (m_index) { cd = rhs.cd; } else { dd = rhs.dd; } rhs.m_index = emptyConstantQualifiedIdentifierPrivateIndex(); rhs.cd = emptyConstantQualifiedIdentifierPrivate(); } QualifiedIdentifier& QualifiedIdentifier::operator=(const QualifiedIdentifier& rhs) { if(dd == rhs.dd && cd == rhs.cd) return *this; if(!m_index) delete dd; rhs.makeConstant(); cd = rhs.cd; m_index = rhs.m_index; return *this; } QualifiedIdentifier& QualifiedIdentifier::operator=(QualifiedIdentifier&& rhs) Q_DECL_NOEXCEPT { if(!m_index) delete dd; m_index = rhs.m_index; if (m_index) { cd = rhs.cd; } else { dd = rhs.dd; } rhs.cd = emptyConstantQualifiedIdentifierPrivate(); rhs.m_index = emptyConstantQualifiedIdentifierPrivateIndex(); return *this; } QualifiedIdentifier::~QualifiedIdentifier() { if(!m_index) delete dd; } QStringList QualifiedIdentifier::toStringList(IdentifierStringFormattingOptions options) const { QStringList ret; ret.reserve(explicitlyGlobal() + count()); if (explicitlyGlobal()) ret.append(QString()); if(m_index) { FOREACH_FUNCTION_STATIC(const IndexedIdentifier& index, cd->identifiers) ret << index.identifier().toString(options); }else{ FOREACH_FUNCTION_STATIC(const IndexedIdentifier& index, dd->identifiers) ret << index.identifier().toString(options); } return ret; } QString QualifiedIdentifier::toString(IdentifierStringFormattingOptions options) const { const QString doubleColon = QStringLiteral("::"); QString ret; if( !options.testFlag(RemoveExplicitlyGlobalPrefix) && explicitlyGlobal() ) ret = doubleColon; bool first = true; if(m_index) { FOREACH_FUNCTION_STATIC(const IndexedIdentifier& index, cd->identifiers) { if( !first ) ret += doubleColon; else first = false; ret += index.identifier().toString(options); } }else{ FOREACH_FUNCTION_STATIC(const IndexedIdentifier& index, dd->identifiers) { if( !first ) ret += doubleColon; else first = false; ret += index.identifier().toString(options); } } return ret; } QualifiedIdentifier QualifiedIdentifier::merge(const QualifiedIdentifier& base) const { QualifiedIdentifier ret(base); ret.push(*this); 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::operator+(const IndexedIdentifier& rhs) const { QualifiedIdentifier ret(*this); ret.push(rhs); return ret; } QualifiedIdentifier& QualifiedIdentifier::operator+=(const IndexedIdentifier& rhs) { push(rhs); return *this; } bool QualifiedIdentifier::isExpression() const { if(m_index) return cd->m_isExpression; else return dd->m_isExpression; } void QualifiedIdentifier::setIsExpression(bool is) { if (is != isExpression()) { 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) { if (eg != explicitlyGlobal()) { 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::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); } 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; const uint 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; push(IndexedIdentifier(id)); } void QualifiedIdentifier::push(const IndexedIdentifier& id) { if (id.isEmpty()) { return; } prepareWrite(); dd->identifiersList.append(id); } void QualifiedIdentifier::push(const QualifiedIdentifier& id) { if (id.isEmpty()) { return; } prepareWrite(); if (id.m_index) { dd->identifiersList.append(id.cd->identifiers(), id.cd->identifiersSize()); } else { dd->identifiersList.append(id.dd->identifiers(), id.dd->identifiersSize()); } if (id.explicitlyGlobal()) { setExplicitlyGlobal(true); } } 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 { return indexedFirst().identifier(); } IndexedIdentifier QualifiedIdentifier::indexedFirst() const { if( (m_index && cd->identifiersSize() == 0) || (!m_index && dd->identifiersSize() == 0) ) return IndexedIdentifier(); else return indexedAt(0); } Identifier QualifiedIdentifier::last() const { return indexedLast().identifier(); } IndexedIdentifier QualifiedIdentifier::indexedLast() const { uint c = count(); if(c) return indexedAt(c-1); else return IndexedIdentifier(); } 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; } Identifier QualifiedIdentifier::at(int i) const { return indexedAt(i).identifier(); } IndexedIdentifier QualifiedIdentifier::indexedAt(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 { quint32 bitfields = m_isConstant | (m_isReference << 1) | (m_isRValue << 2) | (m_isVolatile << 3) | (m_pointerDepth << 4) | (m_pointerConstMask << 9); return KDevHash() << m_identifier.getIndex() << bitfields; } 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_isVolatile == rhs.m_isVolatile && 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; } bool IndexedTypeIdentifier::isVolatile() const { return m_isVolatile; } void IndexedTypeIdentifier::setIsVolatile(bool isVolatile) { m_isVolatile = isVolatile; } int IndexedTypeIdentifier::pointerDepth() const { return m_pointerDepth; } void IndexedTypeIdentifier::setPointerDepth(int depth) { Q_ASSERT(depth <= 23 && depth >= 0); ///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(IdentifierStringFormattingOptions options) const { QString ret; if(isConstant()) ret += QLatin1String("const "); if(isVolatile()) ret += QLatin1String("volatile "); ret += m_identifier.identifier().toString(options); for(int a = 0; a < pointerDepth(); ++a) { ret += '*'; if( isConstPointer(a) ) ret += QLatin1String("const"); } if(isRValue()) ret += QLatin1String("&&"); else if(isReference()) ret += '&'; return ret; } IndexedTypeIdentifier::IndexedTypeIdentifier(const IndexedQualifiedIdentifier& identifier) : m_identifier(identifier) , m_isConstant(false) , m_isReference(false) , m_isRValue(false) , m_isVolatile(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_isVolatile(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(IndexedIdentifier&& rhs) Q_DECL_NOEXCEPT : index(rhs.index) { rhs.index = emptyConstantIdentifierPrivateIndex(); } 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=(IndexedIdentifier&& rhs) Q_DECL_NOEXCEPT { if(shouldDoDUChainReferenceCounting(this)) { QMutexLocker lock(identifierRepository()->mutex()); ifDebug( qCDebug(LANGUAGE) << "decreasing"; ) decrease(identifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } else if (shouldDoDUChainReferenceCounting(&rhs)) { QMutexLocker lock(identifierRepository()->mutex()); ifDebug( qCDebug(LANGUAGE) << "decreasing"; ) decrease(identifierRepository()->dynamicItemFromIndexSimple(rhs.index)->m_refCount, rhs.index); } index = rhs.index; rhs.index = emptyConstantIdentifierPrivateIndex(); if(shouldDoDUChainReferenceCounting(this) && !(shouldDoDUChainReferenceCounting(&rhs))) { QMutexLocker lock(identifierRepository()->mutex()); ifDebug( qCDebug(LANGUAGE) << "increasing"; ) 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(); } bool IndexedQualifiedIdentifier::isEmpty() const { return index == emptyConstantQualifiedIdentifierPrivateIndex(); } int cnt = 0; IndexedQualifiedIdentifier IndexedTypeIdentifier::identifier() const { return m_identifier; } void IndexedTypeIdentifier::setIdentifier(const IndexedQualifiedIdentifier& id) { m_identifier = id; } IndexedQualifiedIdentifier::IndexedQualifiedIdentifier() : index(emptyConstantQualifiedIdentifierPrivateIndex()) { ifDebug( qCDebug(LANGUAGE) << "(" << ++cnt << ")" << identifier().toString() << index; ) if(shouldDoDUChainReferenceCounting(this)) { ifDebug( qCDebug(LANGUAGE) << "increasing"; ) //qCDebug(LANGUAGE) << "(" << ++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( qCDebug(LANGUAGE) << "(" << ++cnt << ")" << identifier().toString() << index; ) if(shouldDoDUChainReferenceCounting(this)) { ifDebug( qCDebug(LANGUAGE) << "increasing"; ) QMutexLocker lock(qualifiedidentifierRepository()->mutex()); increase(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } } IndexedQualifiedIdentifier::IndexedQualifiedIdentifier(const IndexedQualifiedIdentifier& id) : index(id.index) { ifDebug( qCDebug(LANGUAGE) << "(" << ++cnt << ")" << identifier().toString() << index; ) if(shouldDoDUChainReferenceCounting(this)) { ifDebug( qCDebug(LANGUAGE) << "increasing"; ) QMutexLocker lock(qualifiedidentifierRepository()->mutex()); increase(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } } IndexedQualifiedIdentifier::IndexedQualifiedIdentifier(IndexedQualifiedIdentifier&& rhs) Q_DECL_NOEXCEPT : index(rhs.index) { rhs.index = emptyConstantQualifiedIdentifierPrivateIndex(); } IndexedQualifiedIdentifier& IndexedQualifiedIdentifier::operator=(const QualifiedIdentifier& id) { ifDebug( qCDebug(LANGUAGE) << "(" << ++cnt << ")" << identifier().toString() << index; ) if(shouldDoDUChainReferenceCounting(this)) { QMutexLocker lock(qualifiedidentifierRepository()->mutex()); ifDebug( qCDebug(LANGUAGE) << "decreasing"; ) decrease(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); index = id.index(); ifDebug( qCDebug(LANGUAGE) << index << "increasing"; ) increase(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } else { index = id.index(); } return *this; } IndexedQualifiedIdentifier& IndexedQualifiedIdentifier::operator=(const IndexedQualifiedIdentifier& rhs) { ifDebug( qCDebug(LANGUAGE) << "(" << ++cnt << ")" << identifier().toString() << index; ) if(shouldDoDUChainReferenceCounting(this)) { QMutexLocker lock(qualifiedidentifierRepository()->mutex()); ifDebug( qCDebug(LANGUAGE) << "decreasing"; ) decrease(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); index = rhs.index; ifDebug( qCDebug(LANGUAGE) << index << "increasing"; ) increase(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } else { index = rhs.index; } return *this; } IndexedQualifiedIdentifier& IndexedQualifiedIdentifier::operator=(IndexedQualifiedIdentifier&& rhs) Q_DECL_NOEXCEPT { if(shouldDoDUChainReferenceCounting(this)) { QMutexLocker lock(qualifiedidentifierRepository()->mutex()); ifDebug( qCDebug(LANGUAGE) << "decreasing"; ) decrease(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } else if (shouldDoDUChainReferenceCounting(&rhs)) { QMutexLocker lock(qualifiedidentifierRepository()->mutex()); ifDebug( qCDebug(LANGUAGE) << "decreasing"; ) decrease(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(rhs.index)->m_refCount, rhs.index); } index = rhs.index; rhs.index = emptyConstantQualifiedIdentifierPrivateIndex(); if(shouldDoDUChainReferenceCounting(this) && !(shouldDoDUChainReferenceCounting(&rhs))) { QMutexLocker lock(qualifiedidentifierRepository()->mutex()); ifDebug( qCDebug(LANGUAGE) << "increasing"; ) increase(qualifiedidentifierRepository()->dynamicItemFromIndexSimple(index)->m_refCount, index); } return *this; } IndexedQualifiedIdentifier::~IndexedQualifiedIdentifier() { ifDebug( qCDebug(LANGUAGE) << "(" << ++cnt << ")" << identifier().toString() << index; ) if(shouldDoDUChainReferenceCounting(this)) { ifDebug( qCDebug(LANGUAGE) << 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() { emptyConstantIdentifierPrivateIndex(); emptyConstantIdentifierPrivate(); emptyConstantQualifiedIdentifierPrivateIndex(); emptyConstantQualifiedIdentifierPrivate(); } } 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(); } diff --git a/language/duchain/identifier.h b/language/duchain/identifier.h index bfcd67e76..adce601c8 100644 --- a/language/duchain/identifier.h +++ b/language/duchain/identifier.h @@ -1,483 +1,483 @@ /* 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. */ #ifndef KDEVPLATFORM_IDENTIFIER_H #define KDEVPLATFORM_IDENTIFIER_H -#include -#include +#include +#include #include #include #include //We use shared d-pointers, which is even better than a d-pointer, but krazy probably won't get it, so exclude the test. //krazy:excludeall=dpointer class QStringList; namespace KDevelop { class IndexedTypeIdentifier; class Identifier; class QualifiedIdentifier; template class QualifiedIdentifierPrivate; template class IdentifierPrivate; class IndexedString; /** * A helper-class to store an identifier by index in a type-safe way. * * The difference to Identifier is that this class only stores the index of an identifier that is in the repository, without any dynamic * abilities or access to the contained data. * * This class does "disk reference counting" * * @warning Do not use this after QCoreApplication::aboutToQuit() has been emitted, items that are not disk-referenced will be invalid at that point. */ class KDEVPLATFORMLANGUAGE_EXPORT IndexedIdentifier : public ReferenceCountManager { public: IndexedIdentifier(); explicit IndexedIdentifier(const Identifier& id); IndexedIdentifier(const IndexedIdentifier& rhs); IndexedIdentifier(IndexedIdentifier&& rhs) Q_DECL_NOEXCEPT; IndexedIdentifier& operator=(const Identifier& id); IndexedIdentifier& operator=(const IndexedIdentifier& rhs); IndexedIdentifier& operator=(IndexedIdentifier&& rhs) Q_DECL_NOEXCEPT; ~IndexedIdentifier(); bool operator==(const IndexedIdentifier& rhs) const; bool operator!=(const IndexedIdentifier& rhs) const; bool operator==(const Identifier& id) const; bool isEmpty() const; Identifier identifier() const; operator Identifier() const; uint getIndex() const { return index; } private: unsigned int index; }; /** * A helper-class to store an identifier by index in a type-safe way. * * The difference to QualifiedIdentifier is that this class only stores the index of an identifier that is in the repository, without any dynamic * abilities or access to the contained data. * * This class does "disk reference counting" * * @warning Do not use this after QCoreApplication::aboutToQuit() has been emitted, items that are not disk-referenced will be invalid at that point. */ class KDEVPLATFORMLANGUAGE_EXPORT IndexedQualifiedIdentifier : public ReferenceCountManager { public: IndexedQualifiedIdentifier(); IndexedQualifiedIdentifier(const QualifiedIdentifier& id); IndexedQualifiedIdentifier(const IndexedQualifiedIdentifier& rhs); IndexedQualifiedIdentifier(IndexedQualifiedIdentifier&& rhs) Q_DECL_NOEXCEPT; IndexedQualifiedIdentifier& operator=(const QualifiedIdentifier& id); IndexedQualifiedIdentifier& operator=(const IndexedQualifiedIdentifier& id); IndexedQualifiedIdentifier& operator=(IndexedQualifiedIdentifier&& rhs) Q_DECL_NOEXCEPT; ~IndexedQualifiedIdentifier(); bool operator==(const IndexedQualifiedIdentifier& rhs) const; bool operator==(const QualifiedIdentifier& id) const; bool operator<(const IndexedQualifiedIdentifier& rhs) const { return index < rhs.index; } bool isValid() const; bool isEmpty() const; QualifiedIdentifier identifier() const; operator QualifiedIdentifier() const; uint getIndex() const { return index; } private: uint index; }; /** * Flags to control the string representation of identifiers. */ enum IdentifierStringFormattingOption { NoOptions = 0x0, /// Removes explicit global prefix from the result. /// When enabled, global identifiers will be formatted as "globalIdentifierFormattedString" /// instead "::globalIdentifierFormattedString". RemoveExplicitlyGlobalPrefix = 0x1, /// Removes template information from the result. /// When enabled, TemplateClass< someDataType > will be formatted as plain "TemplateClass". RemoveTemplateInformation = 0x2 }; Q_DECLARE_FLAGS(IdentifierStringFormattingOptions, IdentifierStringFormattingOption) /** * Represents a single unqualified identifier */ class KDEVPLATFORMLANGUAGE_EXPORT Identifier { friend class QualifiedIdentifier; public: /** * @param start The position in the given string where to start searching for the identifier. (optional) * @param takenRange If this is nonzero, it will be filled with the length of the range from the beginning * of the given string, that was used to construct this identifier. (optional) * * @warning The identifier is parsed in a C++-similar way, and the result may not be what you expect. * If you want to prevent that parsing, use the constructor that takes IndexedString. */ explicit Identifier(const QString& str, uint start = 0, uint* takenRange = nullptr); /** * Preferred constructor, use this if you already have an IndexedString available. This does not decompose the given string. */ explicit Identifier(const IndexedString& str); Identifier(const Identifier& rhs); explicit Identifier(uint index); Identifier(); Identifier(Identifier&& rhs) Q_DECL_NOEXCEPT; ~Identifier(); Identifier& operator=(const Identifier& rhs); Identifier& operator=(Identifier&& rhs) Q_DECL_NOEXCEPT; static Identifier unique(int token); bool isUnique() const; int uniqueToken() const; /** * If \a token is non-zero, turns this Identifier into the special per-document unique identifier. * * This is used e.g. for anonymous namespaces. * * Pass a token which is specific to the document to allow correct equality comparison. */ void setUnique(int token); const IndexedString identifier() const; void setIdentifier(const QString& identifier); /** * Should be preferred over the other version */ void setIdentifier(const IndexedString& identifier); uint hash() const; /** * Comparison ignoring the template-identifiers */ bool nameEquals(const Identifier& rhs) const; /** * @warning This is expensive. */ IndexedTypeIdentifier templateIdentifier(int num) const; uint templateIdentifiersCount() const; void appendTemplateIdentifier(const IndexedTypeIdentifier& identifier); void clearTemplateIdentifiers(); void setTemplateIdentifiers(const QList& templateIdentifiers); QString toString(IdentifierStringFormattingOptions options = NoOptions) const; bool operator==(const Identifier& rhs) const; bool operator!=(const Identifier& rhs) const; bool isEmpty() const; /** * @return a unique index within the global identifier repository for this identifier. * * If the identifier isn't in the repository yet, it is added to the repository. */ uint index() const; bool inRepository() const; private: void makeConstant() const; void prepareWrite(); //Only one of the following pointers is valid at a given time mutable uint m_index; //Valid if cd is valid union { mutable IdentifierPrivate* dd; //Dynamic, owned by this identifier mutable const IdentifierPrivate* cd; //Constant, owned by the repository }; }; /** * Represents a qualified identifier * * QualifiedIdentifier has it's hash-values stored, so using the hash-values is very efficient. */ class KDEVPLATFORMLANGUAGE_EXPORT QualifiedIdentifier { public: explicit QualifiedIdentifier(const QString& id, bool isExpression = false); explicit QualifiedIdentifier(const Identifier& id); QualifiedIdentifier(const QualifiedIdentifier& id); explicit QualifiedIdentifier(uint index); QualifiedIdentifier(); QualifiedIdentifier(QualifiedIdentifier&& rhs) Q_DECL_NOEXCEPT; ~QualifiedIdentifier(); QualifiedIdentifier& operator=(const QualifiedIdentifier& rhs); QualifiedIdentifier& operator=(QualifiedIdentifier&& rhs) Q_DECL_NOEXCEPT; /** * Append @p id to this qualified identifier. */ void push(const IndexedIdentifier& id); /** * Append @p id to this qualified identifier. * * NOTE: If you have an indexed identifier available, use the above method instead. */ void push(const Identifier& id); /** * Append all identifiers of @p id to this qualified identifier. */ void push(const QualifiedIdentifier& id); /** * Pops one identifier from back: */ void pop(); void clear(); bool isEmpty() const; int count() const; Identifier first() const; IndexedIdentifier indexedFirst() const; Identifier last() const; IndexedIdentifier indexedLast() const; Identifier top() const; Identifier at(int i) const; IndexedIdentifier indexedAt(int i) const; /** * @param pos Position where to start the copy. * @param len If this is -1, the whole following part will be returned. */ QualifiedIdentifier mid(int pos, int len = -1) const; /** * Copy the leftmost \a len number of identifiers. * * @param len The number of identifiers to copy, or if negative, the number of identifiers to omit from the right */ inline QualifiedIdentifier left(int len) const { return mid(0, len > 0 ? len : count() + len); } ///@todo Remove this flag bool explicitlyGlobal() const; void setExplicitlyGlobal(bool eg); bool isQualified() const; /** * A flag that can be set by setIsExpression */ bool isExpression() const; /** * Set the expression-flag, that can be retrieved by isExpression(). * This flag is not respected while creating the hash-value and while operator==() comparison. * It is respected while isSame(..) comparison. */ void setIsExpression(bool); QString toString(IdentifierStringFormattingOptions options = NoOptions) const; QStringList toStringList(IdentifierStringFormattingOptions options = NoOptions) const; QualifiedIdentifier operator+(const QualifiedIdentifier& rhs) const; QualifiedIdentifier& operator+=(const QualifiedIdentifier& rhs); /** * Nicer interfaces to merge */ QualifiedIdentifier operator+(const Identifier& rhs) const; QualifiedIdentifier& operator+=(const Identifier& rhs); QualifiedIdentifier operator+(const IndexedIdentifier& rhs) const; QualifiedIdentifier& operator+=(const IndexedIdentifier& rhs); /** * @return a QualifiedIdentifier with this one appended to the other. * * It is explicitly global if either this or base is. */ QualifiedIdentifier merge(const QualifiedIdentifier& base) const; /** * The comparison-operators do not respect explicitlyGlobal and isExpression, they only respect the real scope. * This is for convenient use in hash-tables etc. */ bool operator==(const QualifiedIdentifier& rhs) const; bool operator!=(const QualifiedIdentifier& rhs) const; bool beginsWith(const QualifiedIdentifier& other) const; uint index() const; /** * @return true if this qualified identifier is already in the persistent identifier repository */ bool inRepository() const; /** * The hash does not respect explicitlyGlobal, only the real scope. */ uint hash() const; protected: bool sameIdentifiers(const QualifiedIdentifier& rhs) const; void makeConstant() const; void prepareWrite(); mutable uint m_index; union { mutable QualifiedIdentifierPrivate* dd; mutable const QualifiedIdentifierPrivate* cd; }; }; /** * Extends IndexedQualifiedIdentifier by: * - Arbitrary count of pointer-poperators with cv-qualifiers * - Reference operator * All the properties set here are respected in the hash value. */ class KDEVPLATFORMLANGUAGE_EXPORT IndexedTypeIdentifier { public: /** * Variables like pointerDepth, isReference, etc. are not parsed from the string, so this parsing is quite limited. */ explicit IndexedTypeIdentifier(const IndexedQualifiedIdentifier& identifier = IndexedQualifiedIdentifier()); explicit IndexedTypeIdentifier(const QString& identifer, bool isExpression = false); bool isReference() const; void setIsReference(bool); bool isRValue() const; void setIsRValue(bool); bool isConstant() const; void setIsConstant(bool); bool isVolatile() const; void setIsVolatile(bool); IndexedQualifiedIdentifier identifier() const ; void setIdentifier(const IndexedQualifiedIdentifier& id); /** * @return the pointer depth. Example for C++: "char*" has pointer-depth 1, "char***" has pointer-depth 3 */ int pointerDepth() const; /** * Sets the pointer-depth to the specified count. * * When the pointer-depth is increased, the "isConstPointer" values for new depths will be initialized with false. * * For efficiency-reasons the maximum currently is 23. */ void setPointerDepth(int); /** * Whether the target of pointer 'depthNumber' is constant */ bool isConstPointer(int depthNumber) const; void setIsConstPointer(int depthNumber, bool constant); QString toString(IdentifierStringFormattingOptions options = NoOptions) const; uint hash() const; /** * The comparison-operators do not respect explicitlyGlobal and isExpression, they only respect the real scope. * This is for convenient use in hash-tables etc. */ bool operator==(const IndexedTypeIdentifier& rhs) const; bool operator!=(const IndexedTypeIdentifier& rhs) const; private: IndexedQualifiedIdentifier m_identifier; // The overall number of bits shared by these bit-fields should not exceed 32, // so that we don't waste space. IndexedTypeIdentifer should be as compact as possible. bool m_isConstant : 1; bool m_isReference : 1; bool m_isRValue : 1; bool m_isVolatile : 1; uint m_pointerDepth : 5; uint m_pointerConstMask : 23; }; KDEVPLATFORMLANGUAGE_EXPORT uint qHash(const IndexedTypeIdentifier& id); KDEVPLATFORMLANGUAGE_EXPORT uint qHash(const QualifiedIdentifier& id); KDEVPLATFORMLANGUAGE_EXPORT uint qHash(const Identifier& id); inline uint qHash(const IndexedIdentifier& id) { return id.getIndex(); } inline uint qHash(const IndexedQualifiedIdentifier& id) { return id.getIndex(); } } Q_DECLARE_TYPEINFO(KDevelop::IndexedQualifiedIdentifier, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(KDevelop::IndexedIdentifier, Q_MOVABLE_TYPE); Q_DECLARE_METATYPE(KDevelop::IndexedQualifiedIdentifier) Q_DECLARE_METATYPE(KDevelop::IndexedIdentifier) Q_DECLARE_TYPEINFO(KDevelop::QualifiedIdentifier, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(KDevelop::Identifier, Q_MOVABLE_TYPE); Q_DECLARE_METATYPE(KDevelop::QualifiedIdentifier) Q_DECLARE_METATYPE(KDevelop::Identifier) /** * {q,k}Debug() stream operator: Writes the Identifier to the debug output. */ KDEVPLATFORMLANGUAGE_EXPORT QDebug operator<<(QDebug s, const KDevelop::Identifier& identifier); /** * {q,k}Debug() stream operator: Writes the QualifiedIdentifier to the debug output. */ KDEVPLATFORMLANGUAGE_EXPORT QDebug operator<<(QDebug s, const KDevelop::QualifiedIdentifier& identifier); #endif // KDEVPLATFORM_IDENTIFIER_H diff --git a/language/duchain/navigation/abstractnavigationwidget.h b/language/duchain/navigation/abstractnavigationwidget.h index b73ea5024..c200d19c9 100644 --- a/language/duchain/navigation/abstractnavigationwidget.h +++ b/language/duchain/navigation/abstractnavigationwidget.h @@ -1,102 +1,102 @@ /* 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. */ #ifndef KDEVPLATFORM_ABSTRACTNAVIGATIONWIDGET_H #define KDEVPLATFORM_ABSTRACTNAVIGATIONWIDGET_H #include -#include +#include #include #include "../../interfaces/quickopendataprovider.h" #include "abstractnavigationcontext.h" class QTextBrowser; namespace KDevelop { class AbstractNavigationWidgetPrivate; /** * This class deleted itself when its part is deleted, so always use a QPointer when referencing it. * The duchain must be read-locked for most operations * */ class KDEVPLATFORMLANGUAGE_EXPORT AbstractNavigationWidget : public QWidget, public QuickOpenEmbeddedWidgetInterface { Q_OBJECT public: enum DisplayHint { NoHints = 0x0, // < Normal display EmbeddableWidget = 0x1, // < Omit parts which are only useful for the navigation popup }; Q_DECLARE_FLAGS(DisplayHints, DisplayHint) AbstractNavigationWidget(); ~AbstractNavigationWidget() override; void setContext(NavigationContextPointer context, int initBrowser = 400); void setDisplayHints(DisplayHints hints); QSize sizeHint() const override; public slots: ///Keyboard-action "next" void next() override; ///Keyboard-action "previous" void previous() override; ///Keyboard-action "accept" void accept() override; void up() override; void down() override; virtual void back(); ///These are temporarily for gettings these events directly from kate ///@todo Do this through a public interface post 4.2 void embeddedWidgetRight(); ///Keyboard-action "previous" void embeddedWidgetLeft(); ///Keyboard-action "accept" void embeddedWidgetAccept(); void embeddedWidgetUp(); void embeddedWidgetDown(); NavigationContextPointer context(); void navigateDeclaration(const KDevelop::IndexedDeclaration& decl); Q_SIGNALS: void sizeHintChanged(); /// Emitted whenever the current navigation-context has changed /// @param wasInitial whether the previous context was the initial context /// @param isInitial whether the current context is the initial context void contextChanged(bool wasInitial, bool isInitial); protected: void wheelEvent(QWheelEvent* ) override; void updateIdealSize() const; void initBrowser(int height); void update(); private: QScopedPointer d; Q_PRIVATE_SLOT(d, void anchorClicked(const QUrl&)) }; } #endif diff --git a/language/duchain/navigation/usescollector.h b/language/duchain/navigation/usescollector.h index e8ce7a13a..0c7e16749 100644 --- a/language/duchain/navigation/usescollector.h +++ b/language/duchain/navigation/usescollector.h @@ -1,130 +1,130 @@ /* 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. */ #ifndef KDEVPLATFORM_USESCOLLECTOR_H #define KDEVPLATFORM_USESCOLLECTOR_H -#include -#include +#include +#include #include #include namespace KDevelop { class IndexedDeclaration; ///A helper base-class for collecting the top-contexts that contain all uses of a declaration ///The most important part is that this also updates the duchain if it's not up-to-date or doesn't contain ///the required features. The virtual function processUses(..) is called with each up-to-date top-context found ///that contains uses of the declaration. class KDEVPLATFORMLANGUAGE_EXPORT UsesCollector : public QObject { Q_OBJECT public: explicit UsesCollector(IndexedDeclaration declaration); ~UsesCollector() override; ///@warning For most tasks, you should use declarations() instead, and respect all of them! IndexedDeclaration declaration() const; ///This must be called to start the actual collecting! void startCollecting(); ///Override this to decide whether a file should be respect while computation. ///If true is returned, the uses will be computed for this file, and for all files ///in the import-chain between that file, and the file where the declaration occurs. ///The default-implementation returns true if the file is part of an open project, or ///if no project is opened and the file is open in an editor. virtual bool shouldRespectFile(IndexedString url); bool isReady() const; ///If this is true, the complete overload-chain is computed, and the uses of all overloaded functions together ///are computed. ///They are also returned in declarations(): ///The default is "true" void setCollectOverloads(bool collect); ///If this is true, all definitions are loaded too, and part of the processed declarations. ///This also means that the collector will first jump from any definition to its declaration, and start ///collecting from there. ///They are also returned in declarations(): ///The default is "true" void setCollectDefinitions(bool collectDefinition); ///Use this to set whether processUses should also be called on contexts that only contain ///a declaration that was used for searching the uses ///The default is "true". void setProcessDeclarations(bool process); ///If the searched declaration is a class, this can be used to decide whether constructors ///searched as well. The constructors and destructors will also be part of the declarations() list. ///The default is "true". This only works with constructors that have the same name as the class. ///If this is set to true, also destructors are searched and eventually renamed. void setCollectConstructors(bool process); ///The declarations that were used as base for the search ///For classes this contains forward-declarations etc. ///@warning When doing refactoring, you have to respect all of these as possible used declarations, /// even within the same context. Multiple different of them may have been used in the same top-context, /// with different local use-indices. QList declarations(); Q_SIGNALS: ///@see maximumProgress() void maximumProgressSignal(uint); ///@see progress() void progressSignal(uint, uint); ///@see processUses() void processUsesSignal(KDevelop::ReferencedTopDUContext); private slots: void updateReady(KDevelop::IndexedString url, KDevelop::ReferencedTopDUContext topContext); private: ///Called with every top-context that can contain uses of the declaration, or if setProcessDeclarations(false) ///has not been called also with all contexts that contain declarations used as base for the search. ///Override this to do your custom processing. You do not need to recurse into imports, that's done for you. ///The duchain is not locked when this is called. virtual void processUses(KDevelop::ReferencedTopDUContext topContext) = 0; ///Indicates the maximum progress this collector can make. If zero is given, this collector is not going to ///do any progress, and progress(..) is never called. virtual void maximumProgress(uint max); ///Called whenever progress has been made. max equals the value given initially by maximumProgress virtual void progress(uint processed, uint max); IndexedDeclaration m_declaration; QSet m_waitForUpdate; QSet m_updateReady; //All files that already have been feed to processUses QSet m_processed; //To prevent endless recursion in updateReady() QSet m_checked; ///Set of all files where the features were manipulated statically through ParseJob QSet m_staticFeaturesManipulated; QList m_declarations; QSet m_declarationTopContexts; bool m_collectOverloads; bool m_collectDefinitions; bool m_collectConstructors; bool m_processDeclarations; }; } #endif diff --git a/language/duchain/safetycounter.h b/language/duchain/safetycounter.h index 0a6b4da59..72e07e610 100644 --- a/language/duchain/safetycounter.h +++ b/language/duchain/safetycounter.h @@ -1,57 +1,57 @@ /*************************************************************************** Copyright 2006 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. * * * ***************************************************************************/ #ifndef __SAFETYCOUNTER_H__ #define __SAFETYCOUNTER_H__ -#include +#include struct SafetyCounter { int safetyCounter; const int maxSafetyCounter; explicit SafetyCounter( int max = 40000 ) : safetyCounter(0), maxSafetyCounter(max) { } void init() { safetyCounter = 0; } SafetyCounter& operator ++() { safetyCounter++; return *this; } ///Returns whether the counter is ok, but without increasing it bool ok() const { return safetyCounter < maxSafetyCounter; } operator bool() { safetyCounter++; bool ret = safetyCounter < maxSafetyCounter; if( !ret ) { if( safetyCounter == maxSafetyCounter ) { #ifdef DEPTHBACKTRACE qDebug() << "WARNING: Safety-counter reached count > " << maxSafetyCounter << ", operation stopped"; #endif } } return ret; } }; #endif diff --git a/language/duchain/specializationstore.h b/language/duchain/specializationstore.h index 30d7939ac..a375e9745 100644 --- a/language/duchain/specializationstore.h +++ b/language/duchain/specializationstore.h @@ -1,95 +1,95 @@ /* 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. */ #ifndef KDEVPLATFORM_SPECIALIZATIONSTORE_H #define KDEVPLATFORM_SPECIALIZATIONSTORE_H -#include +#include #include "instantiationinformation.h" #include namespace KDevelop { class DeclarationId; class Declaration; class DUContext; class TopDUContext; /** * This class allows dynamic management of "current" specializations for declarations. * * The specializations will be applied in editors, and wherever it makes sense. * For example, this is used in C++ to get code-completion and use-building within * instantiated template-classes/functions. */ class KDEVPLATFORMLANGUAGE_EXPORT SpecializationStore { public: static SpecializationStore& self(); /** * Adds/updates the current specialization for the given declaration-id * */ void set(const DeclarationId& declaration, const IndexedInstantiationInformation& specialization); /** * Gets the registered specialization for the given declaration-id, or zero. */ IndexedInstantiationInformation get(const DeclarationId& declaration); /** * Clears the specialization registered for the given declaration-id */ void clear(const DeclarationId& declaration); /** * Clears all registered specializations */ void clear(); /** * Applies the known specializations for the given declaration using the Declaration::specialize() function. * * If no specializations are known, the original declaration is returned. * * @param declaration The declaration to specialize * @param source The top-context from where to start searching * @param recursive Whether parent-contexts should be checked for known specializations, and those applied. * This is a bit more expensive then just doing a local check. */ KDevelop::Declaration* applySpecialization(KDevelop::Declaration* declaration, KDevelop::TopDUContext* source, bool recursive = true); /** * Applies the known specializations for the given context using the DUContext::specialize() function. * * If no specializations are known, returns the original context. * * @param context The context to specialize * @param source The top-context from where to start searching * @param recursive Whether parent-contexts should be checked for known specializations, and those applied. * This is a bit more expensive then just doing a local check. */ DUContext* applySpecialization(KDevelop::DUContext* context, KDevelop::TopDUContext* source, bool recursive = true); private: SpecializationStore(); ~SpecializationStore(); QHash m_specializations; }; } #endif diff --git a/language/duchain/stringhelpers.h b/language/duchain/stringhelpers.h index 62745a436..eb3faf1db 100644 --- a/language/duchain/stringhelpers.h +++ b/language/duchain/stringhelpers.h @@ -1,140 +1,140 @@ /* 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. */ #ifndef KDEVPLATFORM_STRINGHELPERS_H #define KDEVPLATFORM_STRINGHELPERS_H #include -#include +#include class QByteArray; class QString; class QStringList; namespace KDevelop { /** * Searches a fitting closing brace from left to right: a ')' for '(', ']' for '[', ... */ int KDEVPLATFORMLANGUAGE_EXPORT findClose( const QString& str , int pos ); /** * Searches in the given string for a ',' or closing brace, * while skipping everything between opened braces. * @param str string to search * @param pos position where to start searching * @param validEnd when this is set differently, the function will stop when it finds a comma or the given character, and not at closing-braces. * @return On fail, str.length() is returned, else the position of the closing character. */ int KDEVPLATFORMLANGUAGE_EXPORT findCommaOrEnd( const QString& str , int pos, QChar validEnd = QLatin1Char(' ') ); /** * Skips in the string backwards over function-arguments, and stops at the right side of a "(" * @param skippedArguments Will contain all skipped arguments * @param argumentsStart Should be set to the position where the seeking should start, will be changed to the right side of a "(" when found. Should be at the right side of a '(', and may be max. str.length() */ void KDEVPLATFORMLANGUAGE_EXPORT skipFunctionArguments(QString str, QStringList& skippedArguments, int& argumentsStart ); /** * Removes white space at the beginning and end, and replaces contiguous inner white-spaces with single white-spaces. Newlines are treated as whitespaces, the returned text will have no more newlines. */ QString KDEVPLATFORMLANGUAGE_EXPORT reduceWhiteSpace(QString str); QString KDEVPLATFORMLANGUAGE_EXPORT stripFinalWhitespace(QString str); // /** * Fills all c++-style comments within the given code with the given 'replacement' character * Newlines are preserved. */ QString KDEVPLATFORMLANGUAGE_EXPORT clearComments( QString str, QChar replacement = QLatin1Char(' ') ); /** * Fills all c++-strings within the given code with the given 'replacement' character * Comments should have been removed before. */ QString KDEVPLATFORMLANGUAGE_EXPORT clearStrings( QString str, QChar replacement = QLatin1Char(' ') ); /** * Extracts the interesting information out of a comment. * For example it removes all the stars at the beginning, and re-indents the text. */ QString KDEVPLATFORMLANGUAGE_EXPORT formatComment( const QString& comment ); /** * Extracts the interesting information out of a comment. * For example it removes all the stars at the beginning, and re-indents the text. */ QByteArray KDEVPLATFORMLANGUAGE_EXPORT formatComment( const QByteArray& comment ); /** * Remove characters in @ref str from the end of @ref from * * @return number of stripped characters */ int KDEVPLATFORMLANGUAGE_EXPORT rStrip( const QByteArray& str, QByteArray& from ); /** * Remove characters in @ref str from the beginning of @ref from * * @return number of stripped characters */ int KDEVPLATFORMLANGUAGE_EXPORT strip( const QByteArray& str, QByteArray& from ); /** * Can be used to iterate through different kinds of parameters, for example template-parameters */ class KDEVPLATFORMLANGUAGE_EXPORT ParamIterator { public: /** * @param parens Should be a string containing the two parens between which the parameters are searched. * Example: "<>" or "()" Optionally it can also contain one third end-character. * If that end-character is encountered in the prefix, the iteration will be stopped. * * Example: When "<>:" is given, ParamIterator will only parse the first identifier of a C++ scope */ ParamIterator( QString parens, QString source, int start = 0); ~ParamIterator(); ParamIterator& operator ++(); /** * Returns current found parameter */ QString operator *(); /** * Returns whether there is a current found parameter */ operator bool() const; /** * Returns the text in front of the first opening-paren(if none found then the whole text) */ QString prefix() const; uint position() const; private: class ParamIteratorPrivate* const d; }; } #endif diff --git a/language/duchain/tests/bench_hashes.cpp b/language/duchain/tests/bench_hashes.cpp index 77b771080..808ed4151 100644 --- a/language/duchain/tests/bench_hashes.cpp +++ b/language/duchain/tests/bench_hashes.cpp @@ -1,315 +1,315 @@ /* * This file is part of KDevelop * Copyright 2012 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 "bench_hashes.h" #include #include #include #include #include -#include +#include #include // similar to e.g. modificationrevision.cpp struct DataT { QDateTime a; QDateTime b; }; typedef QPair DataPair; typedef QVector InputData; struct IndexedStringHash { inline uint operator() (const KDevelop::IndexedString& str) const { return str.hash(); } }; typedef std::unordered_map StlHash; inline void insertData(StlHash& hash, const InputData& data) { foreach(const DataPair& pair, data) { hash.insert(std::make_pair(pair.first, pair.second)); } } typedef QHash QStringHash; inline void insertData(QStringHash& hash, const InputData& data) { foreach(const DataPair& pair, data) { hash.insert(pair.first, pair.second); } } QTEST_GUILESS_MAIN(BenchHashes); using namespace KDevelop; Q_DECLARE_METATYPE(InputData) void BenchHashes::initTestCase() { AutoTestShell::init(); TestCore::initialize(Core::NoUi); qRegisterMetaType(); } void BenchHashes::cleanupTestCase() { TestCore::shutdown(); } void BenchHashes::feedData() { QTest::addColumn("useStl"); QTest::addColumn("data"); InputData data; QVector sizes = QVector() << 100 << 1000 << 10000 << 100000; foreach(int size, sizes) { for(int i = data.size(); i < size; ++i) { data << qMakePair(IndexedString(QString::number(i)), DataT()); } QCOMPARE(data.size(), size); QTest::newRow(qPrintable(QStringLiteral("unordered_map-%1").arg(size))) << true << data; QTest::newRow(qPrintable(QStringLiteral("qhash-%1").arg(size))) << false << data; } } void BenchHashes::insert() { QFETCH(bool, useStl); QFETCH(InputData, data); if (useStl) { QBENCHMARK { StlHash hash; insertData(hash, data); } } else { QBENCHMARK { QStringHash hash; insertData(hash, data); } } } void BenchHashes::insert_data() { feedData(); } void BenchHashes::find() { QFETCH(bool, useStl); QFETCH(InputData, data); if(useStl) { StlHash hash; insertData(hash, data); QBENCHMARK { foreach(const DataPair& pair, data) { (void) hash.find(pair.first); } } } else { QStringHash hash; insertData(hash, data); QBENCHMARK { foreach(const DataPair& pair, data) { (void) hash.find(pair.first); } } } } void BenchHashes::find_data() { feedData(); } void BenchHashes::constFind() { QFETCH(bool, useStl); QFETCH(InputData, data); if(useStl) { StlHash hash; insertData(hash, data); const StlHash& constHash = hash; QBENCHMARK { foreach(const DataPair& pair, data) { (void) constHash.find(pair.first); } } } else { QStringHash hash; insertData(hash, data); QBENCHMARK { foreach(const DataPair& pair, data) { (void) hash.constFind(pair.first); } } } } void BenchHashes::constFind_data() { feedData(); } void BenchHashes::remove() { QFETCH(bool, useStl); QFETCH(InputData, data); if(useStl) { StlHash hash; insertData(hash, data); QBENCHMARK { foreach(const DataPair& pair, data) { hash.erase(pair.first); } } } else { QStringHash hash; insertData(hash, data); QBENCHMARK { foreach(const DataPair& pair, data) { hash.remove(pair.first); } } } } void BenchHashes::remove_data() { feedData(); } struct TypeRepoTestData { size_t size; void* ptr; }; /** * somewhat artificial benchmark to test speed impact if we'd ever change * the underlying data type of the TypeSystem / TypeRegister. */ void BenchHashes::typeRepo() { QFETCH(int, type); if (type == 1 || type == 2) { QVector v; for(int i = 0; i < 100; ++i) { v.append(new TypeRepoTestData); } if (type == 1) { QBENCHMARK { for(int i = 0; i < 100; ++i) { v.at(i)->size++; } } } else if (type == 2) { TypeRepoTestData** a = v.data(); QBENCHMARK { for(int i = 0; i < 100; ++i) { a[i]->size++; } } } } else if (type == 3) { QHash v; for(int i = 0; i < 100; ++i) { v[i] = new TypeRepoTestData; } QBENCHMARK { for(int i = 0; i < 100; ++i) { v.value(i)->size++; } } } else if (type == 4) { QMap v; for(int i = 0; i < 100; ++i) { v[i] = new TypeRepoTestData; } QBENCHMARK { for(int i = 0; i < 100; ++i) { v.value(i)->size++; } } } else if (type == 5) { std::unordered_map v; for(int i = 0; i < 100; ++i) { v[i] = new TypeRepoTestData; } QBENCHMARK { for(int i = 0; i < 100; ++i) { v.at(i)->size++; } } } else if (type == 6) { // for the idea, look at c++'s lexer.cpp const int vectors = 5; typedef QPair Pair; typedef QVarLengthArray InnerVector; QVarLengthArray v; v.resize(vectors); for(int i = 0; i < 100; ++i) { v[i % vectors] << qMakePair(i, new TypeRepoTestData); } QBENCHMARK { for(int i = 0; i < 100; ++i) { foreach(const Pair& p, v.at(i % vectors)) { if (p.first == i) { p.second->size++; break; } } } } } else if (type == 0) { QBENCHMARK {} } } void BenchHashes::typeRepo_data() { QTest::addColumn("type"); QTest::newRow("noop") << 0; QTest::newRow("vector") << 1; QTest::newRow("vector-raw") << 2; QTest::newRow("qhash") << 3; QTest::newRow("qmap") << 4; QTest::newRow("unordered_map") << 5; QTest::newRow("nested-vector") << 6; } diff --git a/language/duchain/tests/test_identifier.cpp b/language/duchain/tests/test_identifier.cpp index 57f31f2bb..21bc7c018 100644 --- a/language/duchain/tests/test_identifier.cpp +++ b/language/duchain/tests/test_identifier.cpp @@ -1,215 +1,215 @@ /* * This file is part of KDevelop * Copyright 2012-2013 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 "test_identifier.h" #include #include #include #include #include -#include +#include QTEST_GUILESS_MAIN(TestIdentifier); using namespace KDevelop; void TestIdentifier::initTestCase() { AutoTestShell::init(); TestCore::initialize(Core::NoUi); } void TestIdentifier::cleanupTestCase() { TestCore::shutdown(); } void TestIdentifier::testIdentifier() { QFETCH(QString, stringId); const IndexedString indexedStringId(stringId); Identifier id(stringId); QCOMPARE(id.isEmpty(), stringId.isEmpty()); QCOMPARE(id, Identifier(stringId)); QVERIFY(!(id != Identifier(stringId))); QCOMPARE(id, Identifier(stringId)); QCOMPARE(id, Identifier(indexedStringId)); QCOMPARE(id.identifier(), indexedStringId); QCOMPARE(id.toString(), stringId); QVERIFY(id.nameEquals(Identifier(stringId))); QVERIFY(!id.isUnique()); if (stringId.isEmpty()) { QVERIFY(id.inRepository()); QVERIFY(Identifier(id).inRepository()); QVERIFY(Identifier(indexedStringId).inRepository()); } Identifier copy = id; QCOMPARE(copy, id); copy = copy; QCOMPARE(copy, id); copy = Identifier(); QVERIFY(copy.isEmpty()); copy = id; QCOMPARE(copy, id); IndexedIdentifier indexedId(id); QVERIFY(indexedId == id); QCOMPARE(indexedId, IndexedIdentifier(id)); QCOMPARE(indexedId.isEmpty(), stringId.isEmpty()); QCOMPARE(indexedId.identifier(), id); IndexedIdentifier indexedCopy = indexedId; QCOMPARE(indexedCopy, indexedId); indexedCopy = indexedCopy; QCOMPARE(indexedCopy, indexedId); indexedCopy = IndexedIdentifier(); QVERIFY(indexedCopy.isEmpty()); indexedCopy = indexedId; QCOMPARE(indexedCopy, indexedId); Identifier moved = std::move(id); QVERIFY(id.isEmpty()); QVERIFY(id.inRepository()); QCOMPARE(moved, copy); IndexedIdentifier movedIndexed = std::move(indexedId); QVERIFY(indexedId.isEmpty()); QCOMPARE(movedIndexed, indexedCopy); } void TestIdentifier::testIdentifier_data() { QTest::addColumn("stringId"); QTest::newRow("empty") << QString(); QTest::newRow("foo") << QStringLiteral("foo"); QTest::newRow("bar") << QStringLiteral("bar"); //TODO: test template identifiers } void TestIdentifier::testQualifiedIdentifier() { QFETCH(QString, stringId); const QStringList list = stringId.split(QStringLiteral("::"), QString::SkipEmptyParts); QualifiedIdentifier id(stringId); QCOMPARE(id.isEmpty(), stringId.isEmpty()); QCOMPARE(id, QualifiedIdentifier(stringId)); QVERIFY(!(id != QualifiedIdentifier(stringId))); QCOMPARE(id, QualifiedIdentifier(stringId)); if (list.size() == 1) { QCOMPARE(id, QualifiedIdentifier(Identifier(list.last()))); } else if (list.isEmpty()) { QualifiedIdentifier empty{Identifier()}; QCOMPARE(id, empty); QVERIFY(empty.isEmpty()); QVERIFY(empty.inRepository()); } QCOMPARE(id.toString(), stringId); QCOMPARE(id.toStringList(), list); QCOMPARE(id.top(), Identifier(list.isEmpty() ? QString() : list.last())); if (stringId.isEmpty()) { QVERIFY(id.inRepository()); QVERIFY(QualifiedIdentifier(id).inRepository()); } QualifiedIdentifier copy = id; QCOMPARE(copy, id); copy = copy; QCOMPARE(copy, id); copy = QualifiedIdentifier(); QVERIFY(copy.isEmpty()); copy = id; QCOMPARE(copy, id); IndexedQualifiedIdentifier indexedId(id); QVERIFY(indexedId == id); QCOMPARE(indexedId, IndexedQualifiedIdentifier(id)); QCOMPARE(indexedId.isValid(), !stringId.isEmpty()); QCOMPARE(indexedId.identifier(), id); IndexedQualifiedIdentifier indexedCopy = indexedId; QCOMPARE(indexedCopy, indexedId); indexedCopy = indexedCopy; QCOMPARE(indexedCopy, indexedId); indexedCopy = IndexedQualifiedIdentifier(); QVERIFY(!indexedCopy.isValid()); indexedCopy = indexedId; QCOMPARE(indexedCopy, indexedId); QualifiedIdentifier moved = std::move(id); QVERIFY(id.isEmpty()); QCOMPARE(moved, copy); IndexedQualifiedIdentifier movedIndexed = std::move(indexedId); QVERIFY(indexedId.isEmpty()); QCOMPARE(movedIndexed, indexedCopy); copy.clear(); QVERIFY(copy.isEmpty()); copy.push(moved); QCOMPARE(copy, moved); copy.push(Identifier(QStringLiteral("lalala"))); QCOMPARE(copy.count(), moved.count() + 1); } void TestIdentifier::testQualifiedIdentifier_data() { QTest::addColumn("stringId"); QTest::newRow("empty") << QString(); QTest::newRow("foo") << "foo"; QTest::newRow("foo::bar") << "foo::bar"; //TODO: test template identifiers } void TestIdentifier::benchIdentifierCopyConstant() { QBENCHMARK { Identifier identifier(QStringLiteral("Asdf")); identifier.index(); Identifier copy(identifier); } } void TestIdentifier::benchIdentifierCopyDynamic() { QBENCHMARK { Identifier identifier(QStringLiteral("Asdf")); Identifier copy(identifier); } } void TestIdentifier::benchQidCopyPush() { QBENCHMARK { Identifier id(QStringLiteral("foo")); QualifiedIdentifier base(id); QualifiedIdentifier copy(base); copy.push(id); } } diff --git a/language/duchain/topducontextdynamicdata.cpp b/language/duchain/topducontextdynamicdata.cpp index 687ee559c..eb753b4ef 100644 --- a/language/duchain/topducontextdynamicdata.cpp +++ b/language/duchain/topducontextdynamicdata.cpp @@ -1,841 +1,841 @@ /* This is part of KDevelop Copyright 2014 Milian Wolff 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 "topducontextdynamicdata.h" #include -#include -#include +#include +#include #include "declaration.h" #include "declarationdata.h" #include "ducontext.h" #include "topducontext.h" #include "topducontextdata.h" #include "ducontextdata.h" #include "ducontextdynamicdata.h" #include "duchainregister.h" #include "serialization/itemrepository.h" #include "problem.h" #include "util/debug.h" //#define DEBUG_DATA_INFO //This might be problematic on some systems, because really many mmaps are created #define USE_MMAP using namespace KDevelop; namespace { /** * Serialize @p item into @p data and update @p totalDataOffset. * * If @p isSharedDataItem is true, then the item's internal data pointer is not updated * to point to the serialized data. Otherwise the dynamic data is deleted and the items * data will point to the constant serialized data. * * NOTE: The above is required to support serialization of shared-data such as from ProblemPointer. * If we'd set the data to point to the constant region, we'd get crashes due to use-after-free when * we unmap the data and a shared pointer outlives that. */ void saveDUChainItem(QVector& data, DUChainBase& item, uint& totalDataOffset, bool isSharedDataItem) { if(!item.d_func()->classId) { //If this triggers, you have probably created an own DUChainBase based class, but haven't called setClassId(this) in the constructor. qCritical() << "no class-id set for data attached to a declaration of type" << typeid(item).name(); Q_ASSERT(0); } int size = DUChainItemSystem::self().dynamicSize(*item.d_func()); if(data.back().array.size() - int(data.back().position) < size) //Create a new data item data.append({QByteArray(size > 10000 ? size : 10000, 0), 0u}); uint pos = data.back().position; data.back().position += size; totalDataOffset += size; DUChainBaseData& target(*(reinterpret_cast(data.back().array.data() + pos))); if(item.d_func()->isDynamic()) { //Change from dynamic data to constant data enableDUChainReferenceCounting(data.back().array.data(), data.back().array.size()); DUChainItemSystem::self().copy(*item.d_func(), target, true); Q_ASSERT(!target.isDynamic()); if (!isSharedDataItem) { item.setData(&target); } disableDUChainReferenceCounting(data.back().array.data()); }else{ //Just copy the data into another place, expensive copy constructors are not needed memcpy(&target, item.d_func(), size); if (!isSharedDataItem) { item.setData(&target, false); } } if (!isSharedDataItem) { Q_ASSERT(item.d_func() == &target); Q_ASSERT(!item.d_func()->isDynamic()); } } uint indexForParentContext(DUContext* context) { return LocalIndexedDUContext(context->parentContext()).localIndex(); } uint indexForParentContext(Declaration* declaration) { return LocalIndexedDUContext(declaration->context()).localIndex(); } uint indexForParentContext(const ProblemPointer& /*problem*/) { // always stored in the top context return 0; } #ifndef QT_NO_DEBUG void validateItem(const DUChainBaseData* const data, const uchar* const mappedData, const size_t mappedDataSize) { Q_ASSERT(!data->isDynamic()); if (mappedData) { Q_ASSERT(((size_t)data) < ((size_t)mappedData) || ((size_t)data) > ((size_t)mappedData) + mappedDataSize); } } #endif const char* pointerInData(const QVector& data, uint totalOffset) { for(int a = 0; a < data.size(); ++a) { if(totalOffset < data[a].position) return data[a].array.constData() + totalOffset; totalOffset -= data[a].position; } Q_ASSERT_X(false, Q_FUNC_INFO, "Offset doesn't exist in the data."); return nullptr; } void verifyDataInfo(const TopDUContextDynamicData::ItemDataInfo& info, const QVector& data) { Q_UNUSED(info); Q_UNUSED(data); #ifdef DEBUG_DATA_INFO DUChainBaseData* item = (DUChainBaseData*)(pointerInData(data, info.dataOffset)); int size = DUChainItemSystem::self().dynamicSize(*item); Q_ASSERT(size); #endif } QString basePath() { return globalItemRepositoryRegistry().path() + "/topcontexts/"; } QString pathForTopContext(const uint topContextIndex) { return basePath() + QString::number(topContextIndex); } enum LoadType { PartialLoad, ///< Only load the direct member data FullLoad ///< Load everything, including appended lists }; template void loadTopDUContextData(const uint topContextIndex, LoadType loadType, F callback) { QFile file(pathForTopContext(topContextIndex)); if (!file.open(QIODevice::ReadOnly)) { return; } uint readValue; file.read((char*)&readValue, sizeof(uint)); // now readValue is filled with the top-context data size Q_ASSERT(readValue >= sizeof(TopDUContextData)); const QByteArray data = file.read(loadType == FullLoad ? readValue : sizeof(TopDUContextData)); const TopDUContextData* topData = reinterpret_cast(data.constData()); callback(topData); } template struct PtrType; template struct PtrType { using value = T*; }; template struct PtrType> { using value = T*; }; template Q_DECL_CONSTEXPR bool isSharedDataItem() { return false; } template<> Q_DECL_CONSTEXPR bool isSharedDataItem() { return true; } } //BEGIN DUChainItemStorage template TopDUContextDynamicData::DUChainItemStorage::DUChainItemStorage(TopDUContextDynamicData* data) : data(data) { } template TopDUContextDynamicData::DUChainItemStorage::~DUChainItemStorage() { clearItems(); } template void TopDUContextDynamicData::DUChainItemStorage::clearItems() { //Due to template specialization it's possible that a declaration is not reachable through the normal context structure. //For that reason we have to check here, and delete all remaining declarations. qDeleteAll(temporaryItems); temporaryItems.clear(); qDeleteAll(items); items.clear(); } namespace KDevelop { template<> void TopDUContextDynamicData::DUChainItemStorage::clearItems() { // don't delete anything - the problem is shared items.clear(); } } template void TopDUContextDynamicData::DUChainItemStorage::clearItemIndex(const Item& item, const uint index) { if(!data->m_dataLoaded) data->loadData(); if (index < (0x0fffffff/2)) { if (index == 0 || index > uint(items.size())) { return; } else { const uint realIndex = index - 1; Q_ASSERT(items[realIndex] == item); items[realIndex] = nullptr; if (realIndex < (uint)offsets.size()) { offsets[realIndex] = ItemDataInfo(); } } } else { const uint realIndex = 0x0fffffff - index; //We always keep the highest bit at zero if (realIndex == 0 || realIndex > uint(temporaryItems.size())) { return; } else { Q_ASSERT(temporaryItems[realIndex-1] == item); temporaryItems[realIndex-1] = nullptr; } } Q_UNUSED(item); } template void TopDUContextDynamicData::DUChainItemStorage::storeData(uint& currentDataOffset, const QVector& oldData) { auto const oldOffsets = offsets; offsets.clear(); for (int a = 0; a < items.size(); ++a) { auto item = items[a]; if (!item) { if (oldOffsets.size() > a && oldOffsets[a].dataOffset) { //Directly copy the old data range into the new data const DUChainBaseData* itemData = nullptr; if (data->m_mappedData) { itemData = reinterpret_cast(data->m_mappedData + oldOffsets[a].dataOffset); } else { itemData = reinterpret_cast(::pointerInData(oldData, oldOffsets[a].dataOffset)); } offsets << data->writeDataInfo(oldOffsets[a], itemData, currentDataOffset); } else { offsets << ItemDataInfo(); } } else { offsets << ItemDataInfo{currentDataOffset, indexForParentContext(item)}; saveDUChainItem(data->m_data, *item, currentDataOffset, isSharedDataItem()); } } #ifndef QT_NO_DEBUG if (!isSharedDataItem()) { for (auto item : items) { if (item) { validateItem(item->d_func(), data->m_mappedData, data->m_mappedDataSize); } } } #endif } template bool TopDUContextDynamicData::DUChainItemStorage::itemsHaveChanged() const { for (auto item : items) { if (item && item->d_func()->m_dynamic) { return true; } } return false; } template uint TopDUContextDynamicData::DUChainItemStorage::allocateItemIndex(const Item& item, const bool temporary) { if (!data->m_dataLoaded) { data->loadData(); } if (!temporary) { items.append(item); return items.size(); } else { temporaryItems.append(item); return 0x0fffffff - temporaryItems.size(); //We always keep the highest bit at zero } } template bool TopDUContextDynamicData::DUChainItemStorage::isItemForIndexLoaded(uint index) const { if (!data->m_dataLoaded) { return false; } if (index < (0x0fffffff/2)) { if (index == 0 || index > uint(items.size())) { return false; } return items[index-1]; } else { // temporary item return true; } } template Item TopDUContextDynamicData::DUChainItemStorage::getItemForIndex(uint index) const { if (index >= (0x0fffffff/2)) { index = 0x0fffffff - index; //We always keep the highest bit at zero if(index == 0 || index > uint(temporaryItems.size())) return {}; else return temporaryItems.at(index-1); } if (index == 0 || index > static_cast(items.size())) { qCWarning(LANGUAGE) << "item index out of bounds:" << index << "count:" << items.size(); return {}; } const uint realIndex = index - 1;; const auto& item = items.at(realIndex); if (item) { //Shortcut, because this is the most common case return item; } if (realIndex < (uint)offsets.size() && offsets[realIndex].dataOffset) { Q_ASSERT(!data->m_itemRetrievalForbidden); //Construct the context, and eventuall its parent first ///TODO: ugly, remove need for const_cast auto itemData = const_cast( reinterpret_cast(data->pointerInData(offsets[realIndex].dataOffset)) ); auto& item = items[realIndex]; item = dynamic_cast::value>(DUChainItemSystem::self().create(itemData)); if (!item) { //When this happens, the item has not been registered correctly. //We can stop here, because else we will get crashes later. qCritical() << "Failed to load item with identity" << itemData->classId; return {}; } if (isSharedDataItem()) { // NOTE: shared data must never point to mmapped data regions as otherwise we might end up with // use-after-free or double-deletions etc. pp. // thus, make the item always dynamic after deserialization item->makeDynamic(); } auto parent = data->getContextForIndex(offsets[realIndex].parentContext); Q_ASSERT_X(parent, Q_FUNC_INFO, "Could not find parent context for loaded item.\n" "Potentially, the context has been deleted without deleting its children."); item->rebuildDynamicData(parent, index); } else { qCWarning(LANGUAGE) << "invalid item for index" << index << offsets.size() << offsets.value(realIndex).dataOffset; } return item; } template void TopDUContextDynamicData::DUChainItemStorage::deleteOnDisk() { for (auto& item : items) { if (item) { item->makeDynamic(); } } } template void TopDUContextDynamicData::DUChainItemStorage::loadData(QFile* file) const { Q_ASSERT(offsets.isEmpty()); Q_ASSERT(items.isEmpty()); uint readValue; file->read((char*)&readValue, sizeof(uint)); offsets.resize(readValue); file->read((char*)offsets.data(), sizeof(ItemDataInfo) * offsets.size()); //Fill with zeroes for now, will be initialized on-demand items.resize(offsets.size()); } template void TopDUContextDynamicData::DUChainItemStorage::writeData(QFile* file) { uint writeValue = offsets.size(); file->write((char*)&writeValue, sizeof(uint)); file->write((char*)offsets.data(), sizeof(ItemDataInfo) * offsets.size()); } //END DUChainItemStorage const char* TopDUContextDynamicData::pointerInData(uint totalOffset) const { Q_ASSERT(!m_mappedData || m_data.isEmpty()); if(m_mappedData && m_mappedDataSize) return (char*)m_mappedData + totalOffset; return ::pointerInData(m_data, totalOffset); } TopDUContextDynamicData::TopDUContextDynamicData(TopDUContext* topContext) : m_deleting(false) , m_topContext(topContext) , m_contexts(this) , m_declarations(this) , m_problems(this) , m_onDisk(false) , m_dataLoaded(true) , m_mappedFile(nullptr) , m_mappedData(nullptr) , m_mappedDataSize(0) , m_itemRetrievalForbidden(false) { } void KDevelop::TopDUContextDynamicData::clear() { m_contexts.clearItems(); m_declarations.clearItems(); m_problems.clearItems(); } TopDUContextDynamicData::~TopDUContextDynamicData() { unmap(); } void KDevelop::TopDUContextDynamicData::unmap() { delete m_mappedFile; m_mappedFile = nullptr; m_mappedData = nullptr; m_mappedDataSize = 0; } bool TopDUContextDynamicData::fileExists(uint topContextIndex) { return QFile::exists(pathForTopContext(topContextIndex)); } QList TopDUContextDynamicData::loadImporters(uint topContextIndex) { QList ret; loadTopDUContextData(topContextIndex, FullLoad, [&ret] (const TopDUContextData* topData) { ret.reserve(topData->m_importersSize()); FOREACH_FUNCTION(const IndexedDUContext& importer, topData->m_importers) ret << importer; }); return ret; } QList TopDUContextDynamicData::loadImports(uint topContextIndex) { QList ret; loadTopDUContextData(topContextIndex, FullLoad, [&ret] (const TopDUContextData* topData) { ret.reserve(topData->m_importedContextsSize()); FOREACH_FUNCTION(const DUContext::Import& import, topData->m_importedContexts) ret << import.indexedContext(); }); return ret; } IndexedString TopDUContextDynamicData::loadUrl(uint topContextIndex) { IndexedString url; loadTopDUContextData(topContextIndex, PartialLoad, [&url] (const TopDUContextData* topData) { Q_ASSERT(topData->m_url.isEmpty() || topData->m_url.index() >> 16); url = topData->m_url; }); return url; } void TopDUContextDynamicData::loadData() const { //This function has to be protected by an additional mutex, since it can be triggered from multiple threads at the same time static QMutex mutex; QMutexLocker lock(&mutex); if(m_dataLoaded) return; Q_ASSERT(!m_dataLoaded); Q_ASSERT(m_data.isEmpty()); QFile* file = new QFile(pathForTopContext(m_topContext->ownIndex())); bool open = file->open(QIODevice::ReadOnly); Q_UNUSED(open); Q_ASSERT(open); Q_ASSERT(file->size()); //Skip the offsets, we're already read them //Skip top-context data uint readValue; file->read((char*)&readValue, sizeof(uint)); file->seek(readValue + file->pos()); m_contexts.loadData(file); m_declarations.loadData(file); m_problems.loadData(file); #ifdef USE_MMAP m_mappedData = file->map(file->pos(), file->size() - file->pos()); if(m_mappedData) { m_mappedFile = file; m_mappedDataSize = file->size() - file->pos(); file->close(); //Close the file, so there is less open file descriptors(May be problematic) }else{ qCDebug(LANGUAGE) << "Failed to map" << file->fileName(); } #endif if(!m_mappedFile) { QByteArray data = file->readAll(); m_data.append({data, (uint)data.size()}); delete file; } m_dataLoaded = true; } TopDUContext* TopDUContextDynamicData::load(uint topContextIndex) { QFile file(pathForTopContext(topContextIndex)); if(file.open(QIODevice::ReadOnly)) { if(file.size() == 0) { qCWarning(LANGUAGE) << "Top-context file is empty" << file.fileName(); return nullptr; } QVector contextDataOffsets; QVector declarationDataOffsets; uint readValue; file.read((char*)&readValue, sizeof(uint)); //now readValue is filled with the top-context data size QByteArray topContextData = file.read(readValue); DUChainBaseData* topData = reinterpret_cast(topContextData.data()); TopDUContext* ret = dynamic_cast(DUChainItemSystem::self().create(topData)); if(!ret) { qCWarning(LANGUAGE) << "Cannot load a top-context from file" << file.fileName() << "- the required language-support for handling ID" << topData->classId << "is probably not loaded"; return nullptr; } TopDUContextDynamicData& target(*ret->m_dynamicData); target.m_data.clear(); target.m_dataLoaded = false; target.m_onDisk = true; ret->rebuildDynamicData(nullptr, topContextIndex); target.m_topContextData.append({topContextData, (uint)0}); return ret; }else{ return nullptr; } } bool TopDUContextDynamicData::isOnDisk() const { return m_onDisk; } void TopDUContextDynamicData::deleteOnDisk() { if(!isOnDisk()) return; qCDebug(LANGUAGE) << "deleting" << m_topContext->ownIndex() << m_topContext->url().str(); if(!m_dataLoaded) loadData(); m_contexts.deleteOnDisk(); m_declarations.deleteOnDisk(); m_problems.deleteOnDisk(); m_topContext->makeDynamic(); m_onDisk = false; bool successfullyRemoved = QFile::remove(filePath()); Q_UNUSED(successfullyRemoved); Q_ASSERT(successfullyRemoved); qCDebug(LANGUAGE) << "deletion ready"; } QString KDevelop::TopDUContextDynamicData::filePath() const { return pathForTopContext(m_topContext->ownIndex()); } bool TopDUContextDynamicData::hasChanged() const { return !m_onDisk || m_topContext->d_func()->m_dynamic || m_contexts.itemsHaveChanged() || m_declarations.itemsHaveChanged() || m_problems.itemsHaveChanged(); } void TopDUContextDynamicData::store() { // qCDebug(LANGUAGE) << "storing" << m_topContext->url().str() << m_topContext->ownIndex() << "import-count:" << m_topContext->importedParentContexts().size(); //Check if something has changed. If nothing has changed, don't store to disk. bool contentDataChanged = hasChanged(); if (!contentDataChanged) { return; } ///@todo Save the meta-data into a repository, and only the actual content data into a file. /// This will make saving+loading more efficient, and will reduce the disk-usage. /// Then we also won't need to load the data if only the meta-data changed. if(!m_dataLoaded) loadData(); ///If the data is mapped, and we re-write the file, we must make sure that the data is copied out of the map, ///even if only metadata is changed. ///@todo If we split up data and metadata, we don't need to do this if(m_mappedData) contentDataChanged = true; m_topContext->makeDynamic(); m_topContextData.clear(); Q_ASSERT(m_topContext->d_func()->m_ownIndex == m_topContext->ownIndex()); uint topContextDataSize = DUChainItemSystem::self().dynamicSize(*m_topContext->d_func()); m_topContextData.append({QByteArray(DUChainItemSystem::self().dynamicSize(*m_topContext->d_func()), topContextDataSize), 0u}); uint actualTopContextDataSize = 0; if (contentDataChanged) { //We don't need these structures any more, since we have loaded all the declarations/contexts, and m_data //will be reset which these structures pointed into //Load all lazy declarations/contexts const auto oldData = m_data; //Keep the old data alive until everything is stored into a new data structure m_data.clear(); uint newDataSize = 0; foreach(const ArrayWithPosition &array, oldData) newDataSize += array.position; newDataSize = std::max(newDataSize, 10000u); //We always put 1 byte to the front, so we don't have zero data-offsets, since those are used for "invalid". uint currentDataOffset = 1; m_data.append({QByteArray(newDataSize, 0), currentDataOffset}); m_itemRetrievalForbidden = true; m_contexts.storeData(currentDataOffset, oldData); m_declarations.storeData(currentDataOffset, oldData); m_problems.storeData(currentDataOffset, oldData); m_itemRetrievalForbidden = false; } saveDUChainItem(m_topContextData, *m_topContext, actualTopContextDataSize, false); Q_ASSERT(actualTopContextDataSize == topContextDataSize); Q_ASSERT(m_topContextData.size() == 1); Q_ASSERT(!m_topContext->d_func()->isDynamic()); unmap(); QDir().mkpath(basePath()); QFile file(filePath()); if(file.open(QIODevice::WriteOnly)) { file.resize(0); file.write((char*)&topContextDataSize, sizeof(uint)); foreach(const ArrayWithPosition& pos, m_topContextData) file.write(pos.array.constData(), pos.position); m_contexts.writeData(&file); m_declarations.writeData(&file); m_problems.writeData(&file); foreach(const ArrayWithPosition& pos, m_data) file.write(pos.array.constData(), pos.position); m_onDisk = true; if (file.size() == 0) { qCWarning(LANGUAGE) << "Saving zero size top ducontext data"; } file.close(); } else { qCWarning(LANGUAGE) << "Cannot open top-context for writing"; } // qCDebug(LANGUAGE) << "stored" << m_topContext->url().str() << m_topContext->ownIndex() << "import-count:" << m_topContext->importedParentContexts().size(); } TopDUContextDynamicData::ItemDataInfo TopDUContextDynamicData::writeDataInfo(const ItemDataInfo& info, const DUChainBaseData* data, uint& totalDataOffset) { ItemDataInfo ret(info); Q_ASSERT(info.dataOffset); const auto size = DUChainItemSystem::self().dynamicSize(*data); Q_ASSERT(size); if(m_data.back().array.size() - m_data.back().position < size) { //Create a new m_data item m_data.append({QByteArray(std::max(size, 10000u), 0), 0u}); } ret.dataOffset = totalDataOffset; uint pos = m_data.back().position; m_data.back().position += size; totalDataOffset += size; auto target = reinterpret_cast(m_data.back().array.data() + pos); memcpy(target, data, size); verifyDataInfo(ret, m_data); return ret; } uint TopDUContextDynamicData::allocateDeclarationIndex(Declaration* decl, bool temporary) { return m_declarations.allocateItemIndex(decl, temporary); } uint TopDUContextDynamicData::allocateContextIndex(DUContext* context, bool temporary) { return m_contexts.allocateItemIndex(context, temporary); } uint TopDUContextDynamicData::allocateProblemIndex(ProblemPointer problem) { return m_problems.allocateItemIndex(problem, false); } bool TopDUContextDynamicData::isDeclarationForIndexLoaded(uint index) const { return m_declarations.isItemForIndexLoaded(index); } bool TopDUContextDynamicData::isContextForIndexLoaded(uint index) const { return m_contexts.isItemForIndexLoaded(index); } bool TopDUContextDynamicData::isTemporaryContextIndex(uint index) const { return !(index < (0x0fffffff/2)); } bool TopDUContextDynamicData::isTemporaryDeclarationIndex(uint index) const { return !(index < (0x0fffffff/2)); } DUContext* TopDUContextDynamicData::getContextForIndex(uint index) const { if(!m_dataLoaded) loadData(); if (index == 0) { return m_topContext; } return m_contexts.getItemForIndex(index); } Declaration* TopDUContextDynamicData::getDeclarationForIndex(uint index) const { if(!m_dataLoaded) loadData(); return m_declarations.getItemForIndex(index); } ProblemPointer TopDUContextDynamicData::getProblemForIndex(uint index) const { if(!m_dataLoaded) loadData(); return m_problems.getItemForIndex(index); } void TopDUContextDynamicData::clearDeclarationIndex(Declaration* decl) { m_declarations.clearItemIndex(decl, decl->m_indexInTopContext); } void TopDUContextDynamicData::clearContextIndex(DUContext* context) { m_contexts.clearItemIndex(context, context->m_dynamicData->m_indexInTopContext); } void TopDUContextDynamicData::clearProblems() { m_problems.clearItems(); } diff --git a/language/duchain/topducontextdynamicdata.h b/language/duchain/topducontextdynamicdata.h index 8d021440a..e19b763dc 100644 --- a/language/duchain/topducontextdynamicdata.h +++ b/language/duchain/topducontextdynamicdata.h @@ -1,186 +1,186 @@ /* This 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. */ #ifndef KDEVPLATFORM_TOPDUCONTEXTDYNAMICDATA_H #define KDEVPLATFORM_TOPDUCONTEXTDYNAMICDATA_H -#include -#include +#include +#include #include "problem.h" class QFile; namespace KDevelop { class TopDUContext; class DUContext; class Declaration; class IndexedString; class IndexedDUContext; class DUChainBaseData; ///This class contains dynamic data of a top-context, and also the repository that contains all the data within this top-context. class TopDUContextDynamicData { public: explicit TopDUContextDynamicData(TopDUContext* topContext); ~TopDUContextDynamicData(); void clear(); /** * Allocates an index for the given declaration in this top-context. * The returned index is never zero. * @param temporary whether the declaration is temporary. If it is, it will be stored separately, not stored to disk, * and a duchain write-lock is not needed. Else, you need a write-lock when calling this. */ uint allocateDeclarationIndex(Declaration* decl, bool temporary); Declaration* getDeclarationForIndex(uint index) const; bool isDeclarationForIndexLoaded(uint index) const; void clearDeclarationIndex(Declaration* decl); /** * Allocates an index for the given context in this top-context. * The returned index is never zero. * @param temporary whether the context is temporary. If it is, it will be stored separately, not stored to disk, * and a duchain write-lock is not needed. Else, you need a write-lock when calling this. */ uint allocateContextIndex(DUContext* ctx, bool temporary); DUContext* getContextForIndex(uint index) const; bool isContextForIndexLoaded(uint index) const; void clearContextIndex(DUContext* ctx); /** * Allocates an index for the given problem in this top-context. * The returned index is never zero. */ uint allocateProblemIndex(ProblemPointer problem); ProblemPointer getProblemForIndex(uint index) const; void clearProblems(); ///Stores this top-context to disk void store(); ///Stores all remainings of this top-context that are on disk. The top-context will be fully dynamic after this. void deleteOnDisk(); ///Whether this top-context is on disk(Either has been loaded, or has been stored) bool isOnDisk() const; ///Loads the top-context from disk, or returns zero on failure. The top-context will not be registered anywhere, and will have no ParsingEnvironmentFile assigned. ///Also loads all imported contexts. The Declarations/Contexts will be correctly initialized, and put into the symbol tables if needed. static TopDUContext* load(uint topContextIndex); ///Loads only the url out of the data stored on disk for the top-context. static IndexedString loadUrl(uint topContextIndex); static bool fileExists(uint topContextIndex); ///Loads only the list of importers out of the data stored on disk for the top-context. static QList loadImporters(uint topContextIndex); static QList loadImports(uint topContextIndex); bool isTemporaryContextIndex(uint index) const; bool isTemporaryDeclarationIndex(uint index) const ; bool m_deleting; ///Flag used during destruction struct ItemDataInfo { uint dataOffset; /// Offset of the data uint parentContext; /// Parent context of the data (0 means the global context) }; struct ArrayWithPosition { QByteArray array; uint position; }; private: bool hasChanged() const; void unmap(); //Converts away from an mmap opened file to a data array QString filePath() const; void loadData() const; const char* pointerInData(uint offset) const; ItemDataInfo writeDataInfo(const ItemDataInfo& info, const DUChainBaseData* data, uint& totalDataOffset); TopDUContext* m_topContext; template struct DUChainItemStorage { explicit DUChainItemStorage(TopDUContextDynamicData* data); ~DUChainItemStorage(); void clearItems(); bool itemsHaveChanged() const; void storeData(uint& currentDataOffset, const QVector& oldData); Item getItemForIndex(uint index) const; void clearItemIndex(const Item& item, const uint index); uint allocateItemIndex(const Item& item, const bool temporary); void deleteOnDisk(); bool isItemForIndexLoaded(uint index) const; void loadData(QFile* file) const; void writeData(QFile* file); //May contain zero items if they were deleted mutable QVector items; mutable QVector offsets; QVector temporaryItems; TopDUContextDynamicData* const data; }; DUChainItemStorage m_contexts; DUChainItemStorage m_declarations; DUChainItemStorage m_problems; //For temporary declarations that will not be stored to disk, like template instantiations mutable QVector m_data; mutable QVector m_topContextData; bool m_onDisk; mutable bool m_dataLoaded; mutable QFile* m_mappedFile; mutable uchar* m_mappedData; mutable size_t m_mappedDataSize; mutable bool m_itemRetrievalForbidden; }; } Q_DECLARE_TYPEINFO(KDevelop::TopDUContextDynamicData::ItemDataInfo, Q_PRIMITIVE_TYPE); #endif diff --git a/language/duchain/types/typepointer.h b/language/duchain/types/typepointer.h index 3643b5b53..71f8f26a9 100644 --- a/language/duchain/types/typepointer.h +++ b/language/duchain/types/typepointer.h @@ -1,88 +1,88 @@ /* * This file is part of the KDE libraries. * * Copyright 2005 Frerich Raabe * Copyright 2007-2008 David Nolden * Copyright 2014 Kevin Funk * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef KDEVPLATFORM_TYPEPOINTER_H #define KDEVPLATFORM_TYPEPOINTER_H -#include +#include namespace KDevelop { /** * @brief QExplicitlySharedDataPointer wrapper with convenience functions attached */ template class TypePtr : public QExplicitlySharedDataPointer { using Base = QExplicitlySharedDataPointer; public: using Base::QExplicitlySharedDataPointer; using Base::operator=; ///Uses dynamic_cast to cast this pointer to the given type template TypePtr cast(U * /*dummy*/ = nullptr) const { return TypePtr(dynamic_cast(Base::data())); } /** * Convert TypePtr to TypePtr, using a static_cast. * This will compile whenever T* and U* are compatible, i.e. * T is a subclass of U or vice-versa. * Example syntax: * * TypePtr tPtr; * TypePtr uPtr = TypePtr::staticCast( tPtr ); * */ template static TypePtr staticCast( const TypePtr& o ) { return TypePtr( static_cast( o.data() ) ); } /** * Convert TypePtr to TypePtr, using a dynamic_cast. * This will compile whenever T* and U* are compatible, i.e. * T is a subclass of U or vice-versa. * Example syntax: * * TypePtr tPtr; * TypePtr uPtr = TypePtr::dynamicCast( tPtr ); * * Since a dynamic_cast is used, if U derives from T, and tPtr isn't an instance of U, uPtr will be 0. */ template static TypePtr dynamicCast( const TypePtr& o ) { return TypePtr( dynamic_cast( o.data() ) ); } }; } #endif diff --git a/language/duchain/waitforupdate.h b/language/duchain/waitforupdate.h index 0b3a8c7d4..2db83802b 100644 --- a/language/duchain/waitforupdate.h +++ b/language/duchain/waitforupdate.h @@ -1,48 +1,48 @@ /* 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. */ #ifndef KDEVPLATFORM_WAITFORUPDATE_H #define KDEVPLATFORM_WAITFORUPDATE_H #include "duchain/topducontext.h" #include -#include +#include namespace KDevelop { struct WaitForUpdate : public QObject { Q_OBJECT public: WaitForUpdate(); public slots: void updateReady(const KDevelop::IndexedString& url, const KDevelop::ReferencedTopDUContext& topContext); public: bool m_ready; ReferencedTopDUContext m_topContext; }; } #endif // KDEVPLATFORM_WAITFORUPDATE_H diff --git a/language/editor/modificationrevision.h b/language/editor/modificationrevision.h index b52e441e6..c17c848df 100644 --- a/language/editor/modificationrevision.h +++ b/language/editor/modificationrevision.h @@ -1,79 +1,79 @@ /* 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. */ #ifndef KDEVPLATFORM_MODIFICATIONREVISION_H #define KDEVPLATFORM_MODIFICATIONREVISION_H -#include +#include #include #include "../backgroundparser/documentchangetracker.h" class QString; namespace KDevelop { class IndexedString; KDEVPLATFORMLANGUAGE_EXPORT extern const int cacheModificationTimesForSeconds; /** * Pairs together a date and a revision-number, for simpler moving around and comparison. Plus some convenience-functions. * Use this to track changes to files, by storing the file-modification time and the editor-revision if applicable (@see KTextEditor::MovingInterface) * * All member-functions except the IndexedString constructor directly act on the two members, without additional logic. * * Does not need a d-pointer, is only a container-class. * * It is safe to store this class in the disk-mapped internal duchain data structures. * * */ class KDEVPLATFORMLANGUAGE_EXPORT ModificationRevision { public: ///Constructs a ModificationRevision for the file referenced by the given string, which should have been constructed using QUrl::pathOrUrl at some point ///This is efficient, because it uses a cache to look up the modification-revision, caching file-system stats for some time static ModificationRevision revisionForFile(const IndexedString& fileName); ///You can use this when you want to make sure that any cached on-disk modification-time is discarded ///and it's re-read from disk on the next access. ///Otherwise, the on-disk modification-times are re-used for a specific amount of time static void clearModificationCache(const IndexedString& fileName); ///The default-revision is 0, because that is the kate moving-revision for cleanly opened documents explicit ModificationRevision( const QDateTime& modTime = QDateTime(), int revision_ = 0 ); bool operator <( const ModificationRevision& rhs ) const; bool operator==( const ModificationRevision& rhs ) const; bool operator!=( const ModificationRevision& rhs ) const; QString toString() const; uint modificationTime; //On-disk modification-time of a document in time_t format int revision; //MovingInterface revision of a document private: friend class DocumentChangeTracker; static void setEditorRevisionForFile(const IndexedString& filename, int revision); static void clearEditorRevisionForFile(const IndexedString& filename); }; } #endif diff --git a/language/highlighting/codehighlighting.h b/language/highlighting/codehighlighting.h index b4217d18a..95e6a79d0 100644 --- a/language/highlighting/codehighlighting.h +++ b/language/highlighting/codehighlighting.h @@ -1,220 +1,220 @@ /* * 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. */ #ifndef KDEVPLATFORM_CODEHIGHLIGHTING_H #define KDEVPLATFORM_CODEHIGHLIGHTING_H -#include -#include +#include +#include #include #include #include #include #include #include namespace KDevelop { class DUContext; class Declaration; typedef QVector ColorMap; class CodeHighlighting; struct HighlightingEnumContainer { enum Types { UnknownType, //Primary highlighting: LocalClassMemberType, InheritedClassMemberType, LocalVariableType, //Other highlighting: ClassType, FunctionType, ForwardDeclarationType, EnumType, EnumeratorType, TypeAliasType, MacroType, /// Declaration of a macro such as "#define FOO" MacroFunctionLikeType, /// Declaration of a function like macro such as "#define FOO()" //If none of the above match: MemberVariableType, NamespaceVariableType, GlobalVariableType, //Most of these are currently not used: ArgumentType, CodeType, FileType, NamespaceType, ScopeType, TemplateType, TemplateParameterType, FunctionVariableType, ErrorVariableType }; enum Contexts { DefinitionContext, DeclarationContext, ReferenceContext }; }; struct HighlightedRange { RangeInRevision range; KTextEditor::Attribute::Ptr attribute; bool operator<(const HighlightedRange& rhs) const { return range.start < rhs.range.start; } }; /** * Code highlighting instance that is used to apply code highlighting to one specific top context * */ class KDEVPLATFORMLANGUAGE_EXPORT CodeHighlightingInstance : public HighlightingEnumContainer { public: explicit CodeHighlightingInstance(const CodeHighlighting* highlighting) : m_useClassCache(false), m_highlighting(highlighting) { } virtual ~CodeHighlightingInstance() { } virtual void highlightDeclaration(KDevelop::Declaration* declaration, const QColor &color); virtual void highlightUse(KDevelop::DUContext* context, int index, const QColor &color); virtual void highlightUses(KDevelop::DUContext* context); void highlightDUChain(KDevelop::TopDUContext* context); void highlightDUChain(KDevelop::DUContext* context, QHash colorsForDeclarations, ColorMap); KDevelop::Declaration* localClassFromCodeContext(KDevelop::DUContext* context) const; /** * @param context Should be the context from where the declaration is used, if a use is highlighted. * */ virtual Types typeForDeclaration(KDevelop::Declaration* dec, KDevelop::DUContext* context) const; /** * Decides whether to apply auto-generated rainbow colors to @p dec. * Default implementation only applies that to local variables in functions. */ virtual bool useRainbowColor(KDevelop::Declaration* dec) const; //A temporary hash for speedup mutable QHash m_contextClasses; //Here the colors of function context are stored until they are merged into the function body mutable QMap > m_functionColorsForDeclarations; mutable QMap m_functionDeclarationsForColors; mutable bool m_useClassCache; const CodeHighlighting* m_highlighting; QVector m_highlight; }; /** * General class representing the code highlighting for one language * */ class KDEVPLATFORMLANGUAGE_EXPORT CodeHighlighting : public QObject, public KDevelop::ICodeHighlighting, public HighlightingEnumContainer { Q_OBJECT Q_INTERFACES(KDevelop::ICodeHighlighting) public: explicit CodeHighlighting(QObject* parent); ~CodeHighlighting() override; /// This function is thread-safe /// @warning The duchain must not be locked when this is called (->possible deadlock) void highlightDUChain(ReferencedTopDUContext context) override; //color should be zero when undecided KTextEditor::Attribute::Ptr attributeForType(Types type, Contexts context, const QColor &color) const; KTextEditor::Attribute::Ptr attributeForDepth(int depth) const; /// This function is thread-safe /// Returns whether a highlighting is already given for the given url bool hasHighlighting(IndexedString url) const override; private: //Returns whether the given attribute was set by the code highlighting, and not by something else //Always returns true when the attribute is zero bool isCodeHighlight(KTextEditor::Attribute::Ptr attr) const; protected: //Can be overridden to create an own instance type virtual CodeHighlightingInstance* createInstance() const; private: /// Highlighting of one specific document struct DocumentHighlighting { IndexedString m_document; qint64 m_waitingRevision; // The ranges are sorted by range start, so they can easily be matched QVector m_waiting; QVector m_highlightedRanges; }; QMap m_highlights; friend class CodeHighlightingInstance; mutable QHash m_definitionAttributes; mutable QHash m_declarationAttributes; mutable QHash m_referenceAttributes; mutable QList m_depthAttributes; // Should be used to enable/disable the colorization of local variables and their uses bool m_localColorization; // Should be used to enable/disable the colorization of global types and their uses bool m_globalColorization; mutable QMutex m_dataMutex; private Q_SLOTS: void clearHighlightingForDocument(KDevelop::IndexedString document); void applyHighlighting(void* highlighting); void trackerDestroyed(QObject* object); /// when the colors change we must invalidate our local caches void adaptToColorChanges(); void aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*); void aboutToRemoveText(const KTextEditor::Range&); }; } #endif // kate: space-indent on; indent-width 2; remove-trailing-spaces all; show-tabs on; tab-indents on; tab-width 2; diff --git a/language/highlighting/colorcache.h b/language/highlighting/colorcache.h index 118a1e24e..e08f26dfc 100644 --- a/language/highlighting/colorcache.h +++ b/language/highlighting/colorcache.h @@ -1,182 +1,182 @@ /* * This file is part of KDevelop * * 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. */ #ifndef KDEVPLATFORM_COLORCACHE_H #define KDEVPLATFORM_COLORCACHE_H -#include -#include +#include +#include #include -#include +#include #include namespace KTextEditor { class Document; class View; } namespace KDevelop { class CodeHighlightingColors; class IDocument; /** * A singleton which holds the global default colors, adapted to the current color scheme */ class KDEVPLATFORMLANGUAGE_EXPORT ColorCache : public QObject { Q_OBJECT public: ~ColorCache() override; /// access the global color cache static ColorCache* self(); /// adapt a given foreground color to the current color scheme /// @p ratio between 0 and 255 where 0 gives @see m_foregroundColor /// and 255 gives @p color /// /// @note if you are looking for a background color, simply setting an alpha /// value should work. QColor blend(QColor color, uchar ratio) const; /// adapt a given background color to the current color scheme /// @p ratio between 0 and 255 where 0 gives @see m_foregroundColor /// and 255 gives @p color /// /// @note if you are looking for a background color, simply setting an alpha /// value should work. QColor blendBackground(QColor color, uchar ratio) const; /// blend a color for local colorization according to the user settings /// @see blend() QColor blendLocalColor(QColor color) const; /// blend a color for global colorization according to the user settings /// @see blend() QColor blendGlobalColor(QColor color) const; /// access the default colors CodeHighlightingColors* defaultColors() const; /** * @returns a primary color if @p num less primaryColorCount and a supplementary color if @p num >= primaryColorCount and < validColorCount * @see validColorCount() * @see primaryColorCount() */ QColor generatedColor(uint num) const; /** * @returns the number of primary and supplementary colors * * @see generatedColor() * @see primaryColorCount() */ uint validColorCount() const; /** * @returns number of primary colors * * When you run out of primary colors use supplementary colors */ uint primaryColorCount() const; /// access the foreground color QColor foregroundColor() const; signals: /// will be emitted whenever the colors got changed /// @see update() void colorsGotChanged(); private slots: /// if necessary, adapt to the colors of this document void slotDocumentActivated(); /// settings got changed, update to the settings of the sender void slotViewSettingsChanged(); /// will regenerate colors from global KDE color scheme void updateColorsFromScheme(); /// will regenerate colors with the proper intensity settings void updateColorsFromSettings(); /// regenerate colors and emits @p colorsGotChanged() /// and finally triggers a rehighlight of the opened documents void updateInternal(); bool tryActiveDocument(); private: explicit ColorCache(QObject *parent = nullptr); static ColorCache* m_self; /// get @p totalGeneratedColors colors from the color wheel and adapt them to the current color scheme void generateColors(); /// calls @c updateInternal() delayed to prevent double loading of language plugins. void update(); /// try to access the KatePart settings for the given doc or fallback to the global KDE scheme /// and update the colors if necessary /// @see generateColors(), updateColorsFromScheme() void updateColorsFromView(KTextEditor::View* view); /// the default colors for the different types CodeHighlightingColors* m_defaultColors; /// the generated colors QList m_colors; uint m_validColorCount; uint m_primaryColorCount; /// Maybe make this configurable: An offset where to start stepping through the color wheel uint m_colorOffset; /// the text color for the current color scheme QColor m_foregroundColor; /// the editor background color color for the current color scheme QColor m_backgroundColor; /// How generated colors for local variables should be mixed with the foreground color. /// Between 0 and 255, where 255 means only foreground color, and 0 only the chosen color. uchar m_localColorRatio; /// How global colors (i.e. for types, uses, etc.) should be mixed with the foreground color. /// Between 0 and 255, where 255 means only foreground color, and 0 only the chosen color. uchar m_globalColorRatio; /// Whether declarations have to be rendered with a bold style or not. bool m_boldDeclarations; /// The view we are listening to for setting changes. QPointer m_view; }; } #endif // KDEVPLATFORM_COLORCACHE_H // kate: space-indent on; indent-width 2; remove-trailing-spaces all; show-tabs on; tab-indents on; tab-width 2; diff --git a/language/interfaces/iquickopen.h b/language/interfaces/iquickopen.h index 3a04fb9d4..a26302ba6 100644 --- a/language/interfaces/iquickopen.h +++ b/language/interfaces/iquickopen.h @@ -1,109 +1,109 @@ /* This file is part of the KDE project Copyright 2006 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 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. */ #ifndef KDEVPLATFORM_IQUICKOPEN_H #define KDEVPLATFORM_IQUICKOPEN_H -#include +#include #include #include class QStringList; namespace KDevelop { class QuickOpenDataProviderBase; class IndexedString; class KDEVPLATFORMLANGUAGE_EXPORT IQuickOpenLine : public QLineEdit { Q_OBJECT public: /** * Returns a non-zero declaration if it has been explicitly selected * and executed through the quickopen line * \code IndexedDeclaration selectedDeclaration() = 0; \endcode * * Returns a non-empty string if the file has been explicitly selected * and executed through the quickopen line * \code IndexedString selectedFile() = 0; \endcode */ virtual void setDefaultText(const QString& text) = 0; Q_SIGNALS: void itemSelected(); }; /** * Interface to quickopen */ class KDEVPLATFORMLANGUAGE_EXPORT IQuickOpen { public: virtual ~IQuickOpen(); /** * Shows the quickopen dialog with the entries of specified types * Default types are: Files, Functions, Classes * There might be other quick open providers with custom items. * Note, the item name has to be translated, for example i18n("Files") should be passed. */ virtual void showQuickOpen( const QStringList &types ) = 0; /** * Registers a new provider under a specified name. * There may be multiple providers with the same type/scope, they will be used simultaneously in that case. * type and scope will be shown in the GUI, so they should be translated. * @param scopes Different scopes supported by this data-provider, Examples: "Project", "Imports", etc. * @param type Types of the provided data, Examples: "Files", "Functions", "Classes", etc. * @param provider The provider. It does not need to be explicitly removed before its destruction. */ virtual void registerProvider( const QStringList& scopes, const QStringList& type, QuickOpenDataProviderBase* provider ) = 0; /** * Remove provider. * @param provider The provider to remove * @return Whether a provider was removed. If false, the provider was not attached. */ virtual bool removeProvider( QuickOpenDataProviderBase* provider ) = 0; /** * Queries a set of files merged from all active data-providers that implement QuickOpenFileSetInterface. * This should not be queried by data-providers that implement QuickOpenFileSetInterface during their * initialization(set() and enableData()) */ virtual QSet fileSet() const = 0; enum QuickOpenType { Standard, Outline }; virtual IQuickOpenLine* createQuickOpenLine(const QStringList& scopes, const QStringList& types, QuickOpenType type = Standard) = 0; }; } Q_DECLARE_INTERFACE(KDevelop::IQuickOpen, "org.kdevelop.IQuickOpen") #endif // KDEVPLATFORM_IQUICKOPEN_H diff --git a/language/interfaces/quickopendataprovider.h b/language/interfaces/quickopendataprovider.h index 2e7d0ce5f..b7b1f7e46 100644 --- a/language/interfaces/quickopendataprovider.h +++ b/language/interfaces/quickopendataprovider.h @@ -1,206 +1,206 @@ /* * 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. */ #ifndef KDEVPLATFORM_QUICKOPENDATAPROVIDER_H #define KDEVPLATFORM_QUICKOPENDATAPROVIDER_H -#include -#include -#include +#include +#include +#include #include #include class QString; class QStringList; class QIcon; namespace KDevelop { class IndexedString; /** * Hint: When implementing a data-provider, do not forget to export it! Else it won't work. * */ /** * If your plugin manages a list of files, you can use this to return that list. * The file-list can be queried by other data-providers(for example functions/methods) so they * can manipulate their content based on those file-lists. The file-list should not be filtered at all, * it should only depend on the enabled models/items * * Example: A list of files in the include-path, a list of files in the project, etc. * */ class KDEVPLATFORMLANGUAGE_EXPORT QuickOpenFileSetInterface { public: virtual QSet files() const = 0; virtual ~QuickOpenFileSetInterface(); }; /** * You can use this as additional base-class for your embedded widgets to get additional interaction * */ class KDEVPLATFORMLANGUAGE_EXPORT QuickOpenEmbeddedWidgetInterface { public: virtual ~QuickOpenEmbeddedWidgetInterface(); ///Is called when the keyboard-shortcut "next" is triggered on the widget, which currently is SHIFT+Right virtual void next() = 0; ///Is called when the keyboard-shortcut "previous" is triggered on the widget, which currently is SHIFT+Left virtual void previous() = 0; ///Is called when the keyboard-shortcut "accept" is triggered on the widget, which currently is SHIFT+Return virtual void accept() = 0; ///Is called when the keyboard-shortcut "scroll up" is triggered on the widget, which currently is SHIFT+Up virtual void up() = 0; ///Is called when the keyboard-shortcut "scroll down" is triggered on the widget, which currently is SHIFT+Down virtual void down() = 0; }; /** * Reimplement this to represent single entries within the quickopen list. * */ class KDEVPLATFORMLANGUAGE_EXPORT QuickOpenDataBase : public QSharedData { public: virtual ~QuickOpenDataBase(); ///Return the text to be shown in the list for this item virtual QString text() const = 0; virtual QString htmlDescription() const = 0; /**Can return Custom highlighting triplets as explained in * the kde header ktexteditor/codecompletionmodel.h * The default-implementation returns an empty list, which means no * special highlighting will be applied. * */ virtual QList highlighting() const; /** * May return an icon to mark the item in the quickopen-list. * The standard-implementation returns an invalid item, which means that * no icon will be shown. * */ virtual QIcon icon() const; /** * Is called when the item should be executed. * * @param filterText Current content of the quickopen-dialogs filter line-edit. * If this is changed, and false is returned, the content of the * line-edit will be changed according to the new text. * @return Whether the dialog should be closed. * */ virtual bool execute( QString& filterText ) = 0; /** * Return true here if this data-item should be expandable with * an own embedded widget. * The default-implementation returns false. * */ virtual bool isExpandable() const; /** * This will be called if isExpandable() returns true. * * A widget should be returned that will be embedded into the quickopen-list. * The widget will be owned by the quickopen-list and will be deleted at will. * * If the widget can be dynamic_cast'ed to QuickOpenEmbeddedWidgetInterface, * the additional interaction defined there will be possible. * * The default-implementation returns 0, which means no widget will be shown. * */ virtual QWidget* expandingWidget() const; }; typedef QExplicitlySharedDataPointer QuickOpenDataPointer; /** * Use this interface to provide custom quickopen-data to the quickopen-widget. * * If possible, you should use KDevelop::Filter (@file quickopenfilter.h ) * to implement the actual filtering, so it is consistent. * */ class KDEVPLATFORMLANGUAGE_EXPORT QuickOpenDataProviderBase : public QObject { Q_OBJECT public: ~QuickOpenDataProviderBase() override; /** * For efficiency, all changes to the filter-text are provided by the following 3 difference-operations. * */ /** * Search-text was changed. * This is called whenever the search-text was changed, and the UI should be updated. * Store the text to track the exact difference. * */ virtual void setFilterText( const QString& text ) = 0; /** * Filter-text should be completely reset and the context re-computed. * */ virtual void reset() = 0; /** * Returns the count of items this provider currently represents * */ virtual uint itemCount() const = 0; /** * Returns the count of *unfiltered* items this provider currently represents */ virtual uint unfilteredItemCount() const = 0; /** * Returns the data-item for a given row. * * Generally, the items must addressed alphabetically, * they will be displayed in the same order in the * quickopen list. * * For performance-reasons the underlying models should * create the QuickOpenDataBase items on demand, because only * those that will really be shown will be requested. * * @param row Index of item to be returned. * */ virtual QuickOpenDataPointer data( uint row ) const = 0; /** * If the data-provider supports multiple different scopes/items, this will be called * with the enabled scopes/items. * If the data-provider supports only one scope/item, this can be ignored. * The lists contains all scopes/items, even those that are not supported by this provider. * */ virtual void enableData( const QStringList& items, const QStringList& scopes ); }; /** * Try parsing string according to "path_to_file":"line number" template. "line number" may be empty. * @param from Source string * @param path Set to parsed path to file, or left unchanged if @ref from doesn't match the template. May refer to the same object as @ref from * @param lineNumber Set to parsed line number, zero if "line number" is empty or left unchanged if @ref from doesn't match the template. * @return Whether @ref from did match the expected template. * */ bool KDEVPLATFORMLANGUAGE_EXPORT extractLineNumber(const QString& from, QString& path, uint& lineNumber); } #endif diff --git a/language/interfaces/quickopenfilter.h b/language/interfaces/quickopenfilter.h index bc755b496..02cd69434 100644 --- a/language/interfaces/quickopenfilter.h +++ b/language/interfaces/quickopenfilter.h @@ -1,280 +1,280 @@ /* * 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. */ #ifndef KDEVPLATFORM_QUICKOPEN_FILTER_H #define KDEVPLATFORM_QUICKOPEN_FILTER_H -#include +#include #include "abbreviations.h" #include namespace KDevelop { /** * This is a simple filter-implementation that helps you implementing own quickopen data-providers. * You should use it when possible, because that way additional features(like regexp filtering) can * be implemented in a central place. * * This implementation does incremental filtering while * typing text, so it quite efficient for the most common case. * * The simplest way of using this is by reimplementing your data-provider * based on QuickOpenDataProviderBase and KDevelop::Filter\. * * What you need to do to use it: * * Reimplement itemText(..) to provide the text filtering * should be performend on(This must be efficient). * * Call setItems(..) when starting a new quickopen session, or when the content * changes, to initialize the filter with your data. * * Call setFilter(..) with the text that should be filtered for on user-input. * * Use filteredItems() to provide data to quickopen. * * @tparam Item should be the type that holds all the information you need. * The filter will hold the data, and you can access it through "items()". */ template class Filter { public: virtual ~Filter() { } ///Clears the filter, but not the data. void clearFilter() { m_filtered = m_items; m_oldFilterText.clear(); } ///Clears the filter and sets new data. The filter-text will be lost. void setItems( const QList& data ) { m_items = data; clearFilter(); } const QList& items() const { return m_items; } ///Returns the data that is left after the filtering const QList& filteredItems() const { return m_filtered; } ///Changes the filter-text and refilters the data void setFilter( const QString& text ) { if (m_oldFilterText == text) { return; } if (text.isEmpty()) { clearFilter(); return; } QList filterBase = m_filtered; if( !text.startsWith( m_oldFilterText ) ) { filterBase = m_items; //Start filtering based on the whole data } m_filtered.clear(); QStringList typedFragments = text.split(QStringLiteral("::"), QString::SkipEmptyParts); if (typedFragments.isEmpty()) { clearFilter(); return; } if ( typedFragments.last().endsWith(':') ) { // remove the trailing colon if there's only one; otherwise, // this breaks incremental filtering typedFragments.last().chop(1); } if (typedFragments.size() == 1 && typedFragments.last().isEmpty()) { clearFilter(); return; } foreach( const Item& data, filterBase ) { const QString& itemData = itemText( data ); if( itemData.contains(text, Qt::CaseInsensitive) || matchesAbbreviationMulti(itemData, typedFragments) ) { m_filtered << data; } } m_oldFilterText = text; } protected: ///Should return the text an item should be filtered by. virtual QString itemText( const Item& data ) const = 0; private: QString m_oldFilterText; QList m_filtered; QList m_items; }; } namespace KDevelop { template class PathFilter { public: ///Clears the filter, but not the data. void clearFilter() { m_filtered = m_items; m_oldFilterText.clear(); } ///Clears the filter and sets new data. The filter-text will be lost. void setItems( const QList& data ) { m_items = data; clearFilter(); } const QList& items() const { return m_items; } ///Returns the data that is left after the filtering const QList& filteredItems() const { return m_filtered; } ///Changes the filter-text and refilters the data void setFilter( const QStringList& text ) { if (m_oldFilterText == text) { return; } if (text.isEmpty()) { clearFilter(); return; } const QString joinedText = text.join(QString()); QList filterBase = m_filtered; if ( m_oldFilterText.isEmpty()) { filterBase = m_items; } else if (m_oldFilterText.mid(0, m_oldFilterText.count() - 1) == text.mid(0, text.count() - 1) && text.last().startsWith(m_oldFilterText.last())) { //Good, the prefix is the same, and the last item has been extended } else if (m_oldFilterText.size() == text.size() - 1 && m_oldFilterText == text.mid(0, text.size() - 1)) { //Good, an item has been added } else { //Start filtering based on the whole data, there was a big change to the filter filterBase = m_items; } // filterBase is correctly sorted, to keep it that way we add // exact matches to this list in sorted way and then prepend the whole list in one go. QList exactMatches; // similar for starting matches QList startMatches; // all other matches QList otherMatches; foreach( const Item& data, filterBase ) { const Path toFilter = static_cast(this)->itemPath(data); const QVector& segments = toFilter.segments(); if (text.count() > segments.count()) { // number of segments mismatches, thus item cannot match continue; } { bool allMatched = true; // try to put exact matches up front for(int i = segments.count() - 1, j = text.count() - 1; i >= 0 && j >= 0; --i, --j) { if (segments.at(i) != text.at(j)) { allMatched = false; break; } } if (allMatched) { exactMatches << data; continue; } } int searchIndex = 0; int pathIndex = 0; int lastMatchIndex = -1; // stop early if more search fragments remain than available after path index while (pathIndex < segments.size() && searchIndex < text.size() && (pathIndex + text.size() - searchIndex - 1) < segments.size() ) { const QString& segment = segments.at(pathIndex); const QString& typedSegment = text.at(searchIndex); lastMatchIndex = segment.indexOf(typedSegment, 0, Qt::CaseInsensitive); if (lastMatchIndex == -1 && !matchesAbbreviation(segment.midRef(0), typedSegment)) { // no match, try with next path segment ++pathIndex; continue; } // else we matched ++searchIndex; ++pathIndex; } if (searchIndex != text.size()) { if ( ! matchesPath(segments.last(), joinedText) ) { continue; } } // prefer matches whose last element starts with the filter if (pathIndex == segments.size() && lastMatchIndex == 0) { startMatches << data; } else { otherMatches << data; } } m_filtered = exactMatches + startMatches + otherMatches; m_oldFilterText = text; } private: QStringList m_oldFilterText; QList m_filtered; QList m_items; }; } #endif diff --git a/language/util/setrepository.cpp b/language/util/setrepository.cpp index cc8a89b6b..ead7a25b5 100644 --- a/language/util/setrepository.cpp +++ b/language/util/setrepository.cpp @@ -1,1122 +1,1122 @@ /*************************************************************************** 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 "setrepository.h" #include "util/debug.h" #include -#include #include #include #include #include #include -#include +#include +#include #include //#define DEBUG_SETREPOSITORY #ifdef DEBUG_SETREPOSITORY #define ifDebug(X) X #else #define ifDebug(x) #undef Q_ASSERT #define Q_ASSERT(x) #endif #ifndef DEBUG_SETREPOSITORY #define CHECK_SPLIT_POSITION(Node) #else #define CHECK_SPLIT_POSITION(node) Q_ASSERT(!(node).leftNode || (getLeftNode(&node)->end() <= splitPositionForRange((node).start, (node).end) && getRightNode(&node)->start() >= splitPositionForRange((node).start, (node).end))) #endif namespace Utils { /** * To achieve a maximum re-usage of nodes, we make sure that sub-nodes of a node always split at specific boundaries. * For each range we can compute a position where that range should be split into its child-nodes. * When creating a new node with 2 sub-nodes, we re-create those child-nodes if their boundaries don't represent those split-positions. * * We pick the split-positions deterministically, they are in order of priority: * ((1<<31)*n, n = [0,...] * ((1<<30)*n, n = [0,...] * ((1<<29)*n, n = [0,...] * ((1<<...)*n, n = [0,...] * ... * */ typedef BasicSetRepository::Index Index; ///The returned split position shall be the end of the first sub-range, and the start of the second ///@param splitBit should be initialized with 31, unless you know better. The value can then be used on while computing child split positions. ///In the end, it will contain the bit used to split the range. It will also contain zero if no split-position exists(length 1) uint splitPositionForRange(uint start, uint end, uchar& splitBit) { if(end-start == 1) { splitBit = 0; return 0; } while(true) { uint position = ((end-1) >> splitBit) << splitBit; //Round to the split-position in this interval that is smaller than end if(position > start && position < end) return position; Q_ASSERT(splitBit != 0); --splitBit; } return 0; } uint splitPositionForRange(uint start, uint end) { uchar splitBit = 31; return splitPositionForRange(start, end, splitBit); } class SetNodeDataRequest; #define getLeftNode(node) repository.itemFromIndex(node->leftNode()) #define getRightNode(node) repository.itemFromIndex(node->rightNode()) #define nodeFromIndex(index) repository.itemFromIndex(index) struct SetRepositoryAlgorithms { SetRepositoryAlgorithms(SetDataRepository& _repository, BasicSetRepository* _setRepository) : repository(_repository), setRepository(_setRepository) { } ///Expensive Index count(const SetNodeData* node) const; void localCheck(const SetNodeData* node); void check(uint node); void check(const SetNodeData* node); QString shortLabel(const SetNodeData& node) const; uint set_union(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit = 31); uint createSetFromNodes(uint leftNode, uint rightNode, const SetNodeData* left = nullptr, const SetNodeData* right = nullptr); uint computeSetFromNodes(uint leftNode, uint rightNode, const SetNodeData* left, const SetNodeData* right, uchar splitBit); uint set_intersect(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit = 31); bool set_contains(const SetNodeData* node, Index index); uint set_subtract(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit = 31); //Required both nodes to be split correctly bool set_equals(const SetNodeData* lhs, const SetNodeData* rhs); QString dumpDotGraph(uint node) const; ///Finds or inserts the given ranges into the repository, and returns the set-index that represents them uint setForIndices(std::vector::const_iterator begin, std::vector::const_iterator end, uchar splitBit = 31) { Q_ASSERT(begin != end); uint startIndex = *begin; uint endIndex = *(end-1)+1; if(endIndex == startIndex+1) { SetNodeData data(startIndex, endIndex); return repository.index( SetNodeDataRequest(&data, repository, setRepository) ); } uint split = splitPositionForRange(startIndex, endIndex, splitBit); Q_ASSERT(split); std::vector::const_iterator splitIterator = std::lower_bound(begin, end, split); Q_ASSERT(*splitIterator >= split); Q_ASSERT(splitIterator > begin); Q_ASSERT(*(splitIterator-1) < split); return createSetFromNodes(setForIndices(begin, splitIterator, splitBit), setForIndices(splitIterator, end, splitBit)); } private: QString dumpDotGraphInternal(uint node, bool master=false) const; SetDataRepository& repository; BasicSetRepository* setRepository; }; void SetNodeDataRequest::destroy(SetNodeData* data, KDevelop::AbstractItemRepository& _repository) { SetDataRepository& repository(static_cast(_repository)); if(repository.setRepository->delayedDeletion()) { if(data->leftNode()){ SetDataRepositoryBase::MyDynamicItem left = repository.dynamicItemFromIndex(data->leftNode()); SetDataRepositoryBase::MyDynamicItem right = repository.dynamicItemFromIndex(data->rightNode()); Q_ASSERT(left->m_refCount > 0); --left->m_refCount; Q_ASSERT(right->m_refCount > 0); --right->m_refCount; }else { //Deleting a leaf Q_ASSERT(data->end() - data->start() == 1); repository.setRepository->itemRemovedFromSets(data->start()); } } } SetNodeDataRequest::SetNodeDataRequest(const SetNodeData* _data, SetDataRepository& _repository, BasicSetRepository* _setRepository) : data(*_data), m_hash(_data->hash()), repository(_repository), setRepository(_setRepository), m_created(false) { ifDebug( SetRepositoryAlgorithms alg(repository); alg.check(_data) ); } SetNodeDataRequest::~SetNodeDataRequest() { //Eventually increase the reference-count of direct children if(m_created) { if(data.leftNode()) ++repository.dynamicItemFromIndex(data.leftNode())->m_refCount; if(data.rightNode()) ++repository.dynamicItemFromIndex(data.rightNode())->m_refCount; } } //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 SetNodeDataRequest::createItem(SetNodeData* item) const { Q_ASSERT((data.rightNode() && data.leftNode()) || (!data.rightNode() && !data.leftNode())); m_created = true; *item = data; Q_ASSERT((item->rightNode() && item->leftNode()) || (!item->rightNode() && !item->leftNode())); #ifdef DEBUG_SETREPOSITORY //Make sure we split at the correct split position if(item->hasSlaves()) { uint split = splitPositionForRange(data.start, data.end); const SetNodeData* left = repository.itemFromIndex(item->leftNode()); const SetNodeData* right = repository.itemFromIndex(item->rightNode()); Q_ASSERT(split >= left->end() && split <= right->start()); } #endif if(!data.leftNode() && setRepository) { for(uint a = item->start(); a < item->end(); ++a) setRepository->itemAddedToSets(a); } } bool SetNodeDataRequest::equals(const SetNodeData* item) const { Q_ASSERT((item->rightNode() && item->leftNode()) || (!item->rightNode() && !item->leftNode())); //Just compare child nodes, since data must be correctly split, this is perfectly ok //Since this happens in very tight loops, we don't call an additional function here, but just do the check. return item->leftNode() == data.leftNode() && item->rightNode() == data.rightNode() && item->start() == data.start() && item->end() == data.end(); } class BasicSetRepository::Private { public: explicit Private(QString _name) : name(_name) { } ~Private() { } QString name; private: }; Set::Set() : m_tree(0), m_repository(nullptr) { } Set::~Set() { } unsigned int Set::count() const { if(!m_repository || !m_tree) return 0; QMutexLocker lock(m_repository->m_mutex); SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository); return alg.count(m_repository->dataRepository.itemFromIndex(m_tree)); } Set::Set(uint treeNode, BasicSetRepository* repository) : m_tree(treeNode), m_repository(repository) { } Set::Set(const Set& rhs) { m_repository = rhs.m_repository; m_tree = rhs.m_tree; } Set& Set::operator=(const Set& rhs) { m_repository = rhs.m_repository; m_tree = rhs.m_tree; return *this; } QString Set::dumpDotGraph() const { if(!m_repository || !m_tree) return QString(); QMutexLocker lock(m_repository->m_mutex); SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository); return alg.dumpDotGraph(m_tree); } Index SetRepositoryAlgorithms::count(const SetNodeData* node) const { if(node->leftNode() && node->rightNode()) return count(getLeftNode(node)) + count(getRightNode(node)); else return node->end() - node->start(); } void SetRepositoryAlgorithms::localCheck(const SetNodeData* ifDebug(node) ) { // Q_ASSERT(node->start() > 0); Q_ASSERT(node->start() < node->end()); Q_ASSERT((node->leftNode() && node->rightNode()) || (!node->leftNode() && !node->rightNode())); Q_ASSERT(!node->leftNode() || (getLeftNode(node())->start() == node->start() && getRightNode(node)->end() == node->end())); Q_ASSERT(!node->leftNode() || (getLeftNode(node())->end() <= getRightNode(node)->start())); } void SetRepositoryAlgorithms::check(uint node) { if(!node) return; check(nodeFromIndex(node)); } void SetRepositoryAlgorithms::check(const SetNodeData* node) { localCheck(node); if(node->leftNode()) check(getLeftNode(node)); if(node->rightNode()) check(getRightNode(node)); // CHECK_SPLIT_POSITION(*node); Re-enable this } QString SetRepositoryAlgorithms::shortLabel(const SetNodeData& node) const { return QStringLiteral("n%1_%2").arg(node.start()).arg(node.end()); } QString SetRepositoryAlgorithms::dumpDotGraphInternal(uint nodeIndex, bool master) const { if(!nodeIndex) return QStringLiteral("empty node"); const SetNodeData& node(*repository.itemFromIndex(nodeIndex)); QString color = QStringLiteral("blue"); if(master) color = QStringLiteral("red"); QString label = QStringLiteral("%1 -> %2").arg(node.start()).arg(node.end()); if(!node.contiguous()) label += QLatin1String(", with gaps"); QString ret = QStringLiteral("%1[label=\"%2\", color=\"%3\"];\n").arg(shortLabel(node), label, color); if(node.leftNode()) { const SetNodeData& left(*repository.itemFromIndex(node.leftNode())); const SetNodeData& right(*repository.itemFromIndex(node.rightNode())); Q_ASSERT(node.rightNode()); ret += QStringLiteral("%1 -> %2;\n").arg(shortLabel(node), shortLabel(left)); ret += QStringLiteral("%1 -> %2;\n").arg(shortLabel(node), shortLabel(right)); ret += dumpDotGraphInternal(node.leftNode()); ret += dumpDotGraphInternal(node.rightNode()); } return ret; } QString SetRepositoryAlgorithms::dumpDotGraph(uint nodeIndex) const { QString ret = QStringLiteral("digraph Repository {\n"); ret += dumpDotGraphInternal(nodeIndex, true); ret += QLatin1String("}\n"); return ret; } const int nodeStackAlloc = 500; class Set::Iterator::IteratorPrivate { public: IteratorPrivate() : nodeStackSize(0), currentIndex(0), repository(nullptr) { nodeStackData.resize(nodeStackAlloc); nodeStack = nodeStackData.data(); } IteratorPrivate(const IteratorPrivate& rhs) : nodeStackData(rhs.nodeStackData), nodeStackSize(rhs.nodeStackSize), currentIndex(rhs.currentIndex), repository(rhs.repository) { nodeStack = nodeStackData.data(); } void resizeNodeStack() { nodeStackData.resize(nodeStackSize + 1); nodeStack = nodeStackData.data(); } KDevVarLengthArray nodeStackData; const SetNodeData** nodeStack; int nodeStackSize; Index currentIndex; BasicSetRepository* repository; /** * Pushes the noed on top of the stack, changes currentIndex, and goes as deep as necessary for iteration. * */ void startAtNode(const SetNodeData* node) { Q_ASSERT(node->start() != node->end()); currentIndex = node->start(); do { nodeStack[nodeStackSize++] = node; if(nodeStackSize >= nodeStackAlloc) resizeNodeStack(); if(node->contiguous()) break; //We need no finer granularity, because the range is contiguous node = Set::Iterator::getDataRepository(repository).itemFromIndex(node->leftNode()); } while(node); Q_ASSERT(currentIndex >= nodeStack[0]->start()); } }; std::set Set::stdSet() const { Set::Iterator it = iterator(); std::set ret; while(it) { Q_ASSERT(ret.find(*it) == ret.end()); ret.insert(*it); ++it; } return ret; } Set::Iterator::Iterator(const Iterator& rhs) : d(new IteratorPrivate(*rhs.d)) { } Set::Iterator& Set::Iterator::operator=(const Iterator& rhs) { delete d; d = new IteratorPrivate(*rhs.d); return *this; } Set::Iterator::Iterator() : d(new IteratorPrivate) { } Set::Iterator::~Iterator() { delete d; } Set::Iterator::operator bool() const { return d->nodeStackSize; } Set::Iterator& Set::Iterator::operator++() { Q_ASSERT(d->nodeStackSize); if(d->repository->m_mutex) d->repository->m_mutex->lock(); ++d->currentIndex; //const SetNodeData** currentNode = &d->nodeStack[d->nodeStackSize - 1]; if(d->currentIndex >= d->nodeStack[d->nodeStackSize - 1]->end()) { //Advance to the next node while(d->nodeStackSize && d->currentIndex >= d->nodeStack[d->nodeStackSize - 1]->end()) { --d->nodeStackSize; } if(!d->nodeStackSize) { //ready }else{ //++d->nodeStackSize; //We were iterating the left slave of the node, now continue with the right. ifDebug( const SetNodeData& left = *d->repository->dataRepository.itemFromIndex(d->nodeStack[d->nodeStackSize - 1]->leftNode()); Q_ASSERT(left.end == d->currentIndex); ) const SetNodeData& right = *d->repository->dataRepository.itemFromIndex(d->nodeStack[d->nodeStackSize - 1]->rightNode()); d->startAtNode(&right); } } Q_ASSERT(d->nodeStackSize == 0 || d->currentIndex < d->nodeStack[0]->end()); if(d->repository->m_mutex) d->repository->m_mutex->unlock(); return *this; } BasicSetRepository::Index Set::Iterator::operator*() const { return d->currentIndex; } Set::Iterator Set::iterator() const { if(!m_tree || !m_repository) return Iterator(); QMutexLocker lock(m_repository->m_mutex); Iterator ret; ret.d->repository = m_repository; if(m_tree) ret.d->startAtNode(m_repository->dataRepository.itemFromIndex(m_tree)); return ret; } //Creates a set item with the given children., they must be valid, and they must be split around their split-position. uint SetRepositoryAlgorithms::createSetFromNodes(uint leftNode, uint rightNode, const SetNodeData* left, const SetNodeData* right) { if(!left) left = nodeFromIndex(leftNode); if(!right) right = nodeFromIndex(rightNode); Q_ASSERT(left->end() <= right->start()); SetNodeData set(left->start(), right->end(), leftNode, rightNode); Q_ASSERT(set.start() < set.end()); uint ret = repository.index(SetNodeDataRequest(&set, repository, setRepository)); Q_ASSERT(set.leftNode() >= 0x10000); Q_ASSERT(set.rightNode() >= 0x10000); Q_ASSERT(ret == repository.findIndex(SetNodeDataRequest(&set, repository, setRepository))); ifDebug( check(ret) ); return ret; } //Constructs a set node from the given two sub-nodes. Those must be valid, they must not intersect, and they must have a correct split-hierarchy. //The do not need to be split around their computed split-position. uint SetRepositoryAlgorithms::computeSetFromNodes(uint leftNode, uint rightNode, const SetNodeData* left, const SetNodeData* right, uchar splitBit) { Q_ASSERT(left->end() <= right->start()); uint splitPosition = splitPositionForRange(left->start(), right->end(), splitBit); Q_ASSERT(splitPosition); if(splitPosition < left->end()) { //The split-position intersects the left node uint leftLeftNode = left->leftNode(); uint leftRightNode = left->rightNode(); const SetNodeData* leftLeft = this->getLeftNode(left); const SetNodeData* leftRight = this->getRightNode(left); Q_ASSERT(splitPosition >= leftLeft->end() && splitPosition <= leftRight->start()); //Create a new set from leftLeft, and from leftRight + right. That set will have the correct split-position. uint newRightNode = computeSetFromNodes(leftRightNode, rightNode, leftRight, right, splitBit); return createSetFromNodes(leftLeftNode, newRightNode, leftLeft); }else if(splitPosition > right->start()) { //The split-position intersects the right node uint rightLeftNode = right->leftNode(); uint rightRightNode = right->rightNode(); const SetNodeData* rightLeft = this->getLeftNode(right); const SetNodeData* rightRight = this->getRightNode(right); Q_ASSERT(splitPosition >= rightLeft->end() && splitPosition <= rightRight->start()); //Create a new set from left + rightLeft, and from rightRight. That set will have the correct split-position. uint newLeftNode = computeSetFromNodes(leftNode, rightLeftNode, left, rightLeft, splitBit); return createSetFromNodes(newLeftNode, rightRightNode, nullptr, rightRight); }else{ return createSetFromNodes(leftNode, rightNode, left, right); } } uint SetRepositoryAlgorithms::set_union(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit) { if(firstNode == secondNode) return firstNode; uint firstStart = first->start(), secondEnd = second->end(); if(firstStart >= secondEnd) return computeSetFromNodes(secondNode, firstNode, second, first, splitBit); uint firstEnd = first->end(), secondStart = second->start(); if(secondStart >= firstEnd) return computeSetFromNodes(firstNode, secondNode, first, second, splitBit); //The ranges of first and second do intersect uint newStart = firstStart < secondStart ? firstStart : secondStart; uint newEnd = firstEnd > secondEnd ? firstEnd : secondEnd; //Compute the split-position for the resulting merged node uint splitPosition = splitPositionForRange(newStart, newEnd, splitBit); //Since the ranges overlap, we can be sure that either first or second contain splitPosition. //The node that contains it, will also be split by it. if(splitPosition > firstStart && splitPosition < firstEnd && splitPosition > secondStart && splitPosition < secondEnd) { //The split-position intersect with both first and second. Continue the union on both sides of the split-position, and merge it. uint firstLeftNode = first->leftNode(); uint firstRightNode = first->rightNode(); uint secondLeftNode = second->leftNode(); uint secondRightNode = second->rightNode(); const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode); const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode); const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode); const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode); Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start()); Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start()); return createSetFromNodes( set_union(firstLeftNode, secondLeftNode, firstLeft, secondLeft, splitBit), set_union(firstRightNode, secondRightNode, firstRight, secondRight, splitBit) ); }else if(splitPosition > firstStart && splitPosition < firstEnd) { uint firstLeftNode = first->leftNode(); uint firstRightNode = first->rightNode(); const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode); const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode); Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start()); //splitPosition does not intersect second. That means that second is completely on one side of it. //So we only need to union that side of first with second. if(secondEnd <= splitPosition) { return createSetFromNodes( set_union(firstLeftNode, secondNode, firstLeft, second, splitBit), firstRightNode, nullptr, firstRight ); }else{ Q_ASSERT(secondStart >= splitPosition); return createSetFromNodes( firstLeftNode, set_union(firstRightNode, secondNode, firstRight, second, splitBit), firstLeft ); } }else if(splitPosition > secondStart && splitPosition < secondEnd) { uint secondLeftNode = second->leftNode(); uint secondRightNode = second->rightNode(); const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode); const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode); Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start()); if(firstEnd <= splitPosition) { return createSetFromNodes( set_union(secondLeftNode, firstNode, secondLeft, first, splitBit), secondRightNode, nullptr, secondRight ); }else{ Q_ASSERT(firstStart >= splitPosition); return createSetFromNodes( secondLeftNode, set_union(secondRightNode, firstNode, secondRight, first, splitBit), secondLeft ); } }else{ //We would have stopped earlier of first and second don't intersect ifDebug( uint test = repository.findIndex(SetNodeDataRequest(first, repository, setRepository)); qCDebug(LANGUAGE) << "found index:" << test; ) Q_ASSERT(0); return 0; } } bool SetRepositoryAlgorithms::set_equals(const SetNodeData* lhs, const SetNodeData* rhs) { if(lhs->leftNode() != rhs->leftNode() || lhs->rightNode() != rhs->rightNode()) return false; else return true; } uint SetRepositoryAlgorithms::set_intersect(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit) { if(firstNode == secondNode) return firstNode; if(first->start() >= second->end()) return 0; if(second->start() >= first->end()) return 0; //The ranges of first and second do intersect uint firstStart = first->start(), firstEnd = first->end(), secondStart = second->start(), secondEnd = second->end(); uint newStart = firstStart < secondStart ? firstStart : secondStart; uint newEnd = firstEnd > secondEnd ? firstEnd : secondEnd; //Compute the split-position for the resulting merged node uint splitPosition = splitPositionForRange(newStart, newEnd, splitBit); //Since the ranges overlap, we can be sure that either first or second contain splitPosition. //The node that contains it, will also be split by it. if(splitPosition > firstStart && splitPosition < firstEnd && splitPosition > secondStart && splitPosition < secondEnd) { //The split-position intersect with both first and second. Continue the intersection on both sides uint firstLeftNode = first->leftNode(); uint firstRightNode = first->rightNode(); uint secondLeftNode = second->leftNode(); uint secondRightNode = second->rightNode(); const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode); const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode); const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode); const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode); Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start()); Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start()); uint newLeftNode = set_intersect(firstLeftNode, secondLeftNode, firstLeft, secondLeft, splitBit); uint newRightNode = set_intersect(firstRightNode, secondRightNode, firstRight, secondRight, splitBit); if(newLeftNode && newRightNode) return createSetFromNodes( newLeftNode, newRightNode ); else if(newLeftNode) return newLeftNode; else return newRightNode; }else if(splitPosition > firstStart && splitPosition < firstEnd) { uint firstLeftNode = first->leftNode(); uint firstRightNode = first->rightNode(); const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode); const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode); Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start()); //splitPosition does not intersect second. That means that second is completely on one side of it. //So we can completely ignore the other side of first. if(secondEnd <= splitPosition) { return set_intersect(firstLeftNode, secondNode, firstLeft, second, splitBit); }else{ Q_ASSERT(secondStart >= splitPosition); return set_intersect(firstRightNode, secondNode, firstRight, second, splitBit); } }else if(splitPosition > secondStart && splitPosition < secondEnd) { uint secondLeftNode = second->leftNode(); uint secondRightNode = second->rightNode(); const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode); const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode); Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start()); if(firstEnd <= splitPosition) { return set_intersect(secondLeftNode, firstNode, secondLeft, first, splitBit); }else{ Q_ASSERT(firstStart >= splitPosition); return set_intersect(secondRightNode, firstNode, secondRight, first, splitBit); } }else{ //We would have stopped earlier of first and second don't intersect Q_ASSERT(0); return 0; } Q_ASSERT(0); } bool SetRepositoryAlgorithms::set_contains(const SetNodeData* node, Index index) { while(true) { if(node->start() > index || node->end() <= index) return false; if(node->contiguous()) return true; const SetNodeData* leftNode = nodeFromIndex(node->leftNode()); if(index < leftNode->end()) node = leftNode; else { const SetNodeData* rightNode = nodeFromIndex(node->rightNode()); node = rightNode; } } return false; } uint SetRepositoryAlgorithms::set_subtract(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit) { if(firstNode == secondNode) return 0; if(first->start() >= second->end() || second->start() >= first->end()) return firstNode; //The ranges of first and second do intersect uint firstStart = first->start(), firstEnd = first->end(), secondStart = second->start(), secondEnd = second->end(); uint newStart = firstStart < secondStart ? firstStart : secondStart; uint newEnd = firstEnd > secondEnd ? firstEnd : secondEnd; //Compute the split-position for the resulting merged node uint splitPosition = splitPositionForRange(newStart, newEnd, splitBit); //Since the ranges overlap, we can be sure that either first or second contain splitPosition. //The node that contains it, will also be split by it. if(splitPosition > firstStart && splitPosition < firstEnd && splitPosition > secondStart && splitPosition < secondEnd) { //The split-position intersect with both first and second. Continue the subtract on both sides of the split-position, and merge it. uint firstLeftNode = first->leftNode(); uint firstRightNode = first->rightNode(); uint secondLeftNode = second->leftNode(); uint secondRightNode = second->rightNode(); const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode); const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode); const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode); const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode); Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start()); Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start()); uint newLeftNode = set_subtract(firstLeftNode, secondLeftNode, firstLeft, secondLeft, splitBit); uint newRightNode = set_subtract(firstRightNode, secondRightNode, firstRight, secondRight, splitBit); if(newLeftNode && newRightNode) return createSetFromNodes(newLeftNode, newRightNode); else if(newLeftNode) return newLeftNode; else return newRightNode; }else if(splitPosition > firstStart && splitPosition < firstEnd) { // Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start()); uint firstLeftNode = first->leftNode(); uint firstRightNode = first->rightNode(); const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode); const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode); //splitPosition does not intersect second. That means that second is completely on one side of it. //So we only need to subtract that side of first with second. uint newLeftNode = firstLeftNode, newRightNode = firstRightNode; if(secondEnd <= splitPosition) { newLeftNode = set_subtract(firstLeftNode, secondNode, firstLeft, second, splitBit); }else{ Q_ASSERT(secondStart >= splitPosition); newRightNode = set_subtract(firstRightNode, secondNode, firstRight, second, splitBit); } if(newLeftNode && newRightNode) return createSetFromNodes(newLeftNode, newRightNode); else if(newLeftNode) return newLeftNode; else return newRightNode; }else if(splitPosition > secondStart && splitPosition < secondEnd) { uint secondLeftNode = second->leftNode(); uint secondRightNode = second->rightNode(); const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode); const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode); Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start()); if(firstEnd <= splitPosition) { return set_subtract(firstNode, secondLeftNode, first, secondLeft, splitBit); }else{ Q_ASSERT(firstStart >= splitPosition); return set_subtract(firstNode, secondRightNode, first, secondRight, splitBit); } }else{ //We would have stopped earlier of first and second don't intersect Q_ASSERT(0); return 0; } Q_ASSERT(0); } Set BasicSetRepository::createSetFromIndices(const std::vector& indices) { QMutexLocker lock(m_mutex); if(indices.empty()) return Set(); SetRepositoryAlgorithms alg(dataRepository, this); return Set(alg.setForIndices(indices.begin(), indices.end()), this); } Set BasicSetRepository::createSet(Index i) { QMutexLocker lock(m_mutex); SetNodeData data(i, i+1); return Set(dataRepository.index( SetNodeDataRequest(&data, dataRepository, this) ), this); } Set BasicSetRepository::createSet(const std::set& indices) { if(indices.empty()) return Set(); QMutexLocker lock(m_mutex); std::vector indicesVector; indicesVector.reserve(indices.size()); for( std::set::const_iterator it = indices.begin(); it != indices.end(); ++it ) indicesVector.push_back(*it); return createSetFromIndices(indicesVector); } BasicSetRepository::BasicSetRepository(QString name, KDevelop::ItemRepositoryRegistry* registry, bool delayedDeletion) : d(new Private(name)), dataRepository(this, name, registry), m_mutex(nullptr), m_delayedDeletion(delayedDeletion) { m_mutex = dataRepository.mutex(); } struct StatisticsVisitor { explicit StatisticsVisitor(const SetDataRepository& _rep) : nodeCount(0), badSplitNodeCount(0), zeroRefCountNodes(0), rep(_rep) { } bool operator() (const SetNodeData* item) { if(item->m_refCount == 0) ++zeroRefCountNodes; ++nodeCount; uint split = splitPositionForRange(item->start(), item->end()); if(item->hasSlaves()) if(split < rep.itemFromIndex(item->leftNode())->end() || split > rep.itemFromIndex(item->rightNode())->start()) ++badSplitNodeCount; return true; } uint nodeCount; uint badSplitNodeCount; uint zeroRefCountNodes; const SetDataRepository& rep; }; void BasicSetRepository::printStatistics() const { StatisticsVisitor stats(dataRepository); dataRepository.visitAllItems(stats); qCDebug(LANGUAGE) << "count of nodes:" << stats.nodeCount << "count of nodes with bad split:" << stats.badSplitNodeCount << "count of nodes with zero reference-count:" << stats.zeroRefCountNodes; } BasicSetRepository::~BasicSetRepository() { delete d; } void BasicSetRepository::itemRemovedFromSets(uint /*index*/) { } void BasicSetRepository::itemAddedToSets(uint /*index*/) { } ////////////Set convenience functions////////////////// bool Set::contains(Index index) const { if(!m_tree || !m_repository) return false; QMutexLocker lock(m_repository->m_mutex); SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository); return alg.set_contains(m_repository->dataRepository.itemFromIndex(m_tree), index); } Set Set::operator +(const Set& first) const { if(!first.m_tree) return *this; else if(!m_tree || !m_repository) return first; Q_ASSERT(m_repository == first.m_repository); QMutexLocker lock(m_repository->m_mutex); SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository); uint retNode = alg.set_union(m_tree, first.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(first.m_tree)); ifDebug(alg.check(retNode)); return Set(retNode, m_repository); } Set& Set::operator +=(const Set& first) { if(!first.m_tree) return *this; else if(!m_tree || !m_repository) { m_tree = first.m_tree; m_repository = first.m_repository; return *this; } QMutexLocker lock(m_repository->m_mutex); SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository); m_tree = alg.set_union(m_tree, first.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(first.m_tree)); ifDebug(alg.check(m_tree)); return *this; } Set Set::operator &(const Set& first) const { if(!first.m_tree || !m_tree) return Set(); Q_ASSERT(m_repository); QMutexLocker lock(m_repository->m_mutex); SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository); Set ret( alg.set_intersect(m_tree, first.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(first.m_tree)), m_repository ); ifDebug(alg.check(ret.m_tree)); return ret; } Set& Set::operator &=(const Set& first) { if(!first.m_tree || !m_tree) { m_tree = 0; return *this; } Q_ASSERT(m_repository); QMutexLocker lock(m_repository->m_mutex); SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository); m_tree = alg.set_intersect(m_tree, first.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(first.m_tree)); ifDebug(alg.check(m_tree)); return *this; } Set Set::operator -(const Set& rhs) const { if(!m_tree || !rhs.m_tree) return *this; Q_ASSERT(m_repository); QMutexLocker lock(m_repository->m_mutex); SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository); Set ret( alg.set_subtract(m_tree, rhs.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(rhs.m_tree)), m_repository ); ifDebug( alg.check(ret.m_tree) ); return ret; } Set& Set::operator -=(const Set& rhs) { if(!m_tree || !rhs.m_tree) return *this; Q_ASSERT(m_repository); QMutexLocker lock(m_repository->m_mutex); SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository); m_tree = alg.set_subtract(m_tree, rhs.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(rhs.m_tree)); ifDebug(alg.check(m_tree)); return *this; } BasicSetRepository* Set::repository() const { return m_repository; } void Set::staticRef() { if(!m_tree) return; QMutexLocker lock(m_repository->m_mutex); SetNodeData* data = m_repository->dataRepository.dynamicItemFromIndexSimple(m_tree); ++data->m_refCount; } ///Mutex must be locked void Set::unrefNode(uint current) { SetNodeData* data = m_repository->dataRepository.dynamicItemFromIndexSimple(current); Q_ASSERT(data->m_refCount); --data->m_refCount; if(!m_repository->delayedDeletion()) { if(data->m_refCount == 0) { if(data->leftNode()){ Q_ASSERT(data->rightNode()); unrefNode(data->rightNode()); unrefNode(data->leftNode()); }else { //Deleting a leaf Q_ASSERT(data->end() - data->start() == 1); m_repository->itemRemovedFromSets(data->start()); } m_repository->dataRepository.deleteItem(current); } } } ///Decrease the static reference-count of this set by one. This set must have a reference-count > 1. ///If this set reaches the reference-count zero, it will be deleted, and all sub-nodes that also reach the reference-count zero ///will be deleted as well. @warning Either protect ALL your sets by using reference-counting, or don't use it at all. void Set::staticUnref() { if(!m_tree) return; QMutexLocker lock(m_repository->m_mutex); unrefNode(m_tree); } StringSetRepository::StringSetRepository(QString name) : Utils::BasicSetRepository(name) { } void StringSetRepository::itemRemovedFromSets(uint index) { ///Call the IndexedString destructor with enabled reference-counting KDevelop::IndexedString string = KDevelop::IndexedString::fromIndex(index); KDevelop::enableDUChainReferenceCounting(&string, sizeof(KDevelop::IndexedString)); string.~IndexedString(); //Call destructor with enabled reference-counting KDevelop::disableDUChainReferenceCounting(&string); } void StringSetRepository::itemAddedToSets(uint index) { ///Call the IndexedString constructor with enabled reference-counting KDevelop::IndexedString string = KDevelop::IndexedString::fromIndex(index); char data[sizeof(KDevelop::IndexedString)]; KDevelop::enableDUChainReferenceCounting(data, sizeof(KDevelop::IndexedString)); new (data) KDevelop::IndexedString(string); //Call constructor with enabled reference-counting KDevelop::disableDUChainReferenceCounting(data); } } diff --git a/language/util/setrepository.h b/language/util/setrepository.h index 0437c5d9b..88b63169f 100644 --- a/language/util/setrepository.h +++ b/language/util/setrepository.h @@ -1,482 +1,482 @@ /*************************************************************************** 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. * * * ***************************************************************************/ #ifndef KDEVPLATFORM_SETREPOSITORY_H #define KDEVPLATFORM_SETREPOSITORY_H #include "basicsetrepository.h" -#include +#include #include /** * This header defines convenience-class that allow handling set-repositories using the represented higher-level objects instead * of indices to them. * */ namespace Utils { /** * Use this class to conveniently iterate over the items in a set. * @tparam T The type the indices will be converted to * @tparam Conversion Should be a class that has a toIndex member function that takes an object of type T as parameter, and returns an index, * and a toItem member function that takes an index, and returns an item of type T. * */ template class ConvenientIterator : public Conversion { public: explicit ConvenientIterator(Set::Iterator it=Set::Iterator()) : m_it(it) { } explicit ConvenientIterator(const Set& set) : m_it(set.iterator()) { } operator bool() const { return m_it; } ConvenientIterator& operator++() { ++m_it; return *this; } T operator*() const { return Conversion::toItem(*m_it); } uint index() const { return *m_it; } private: Set::Iterator m_it; }; struct DummyLocker { }; template struct IdentityConversion { static T toIndex(const T& t) { return t; } static T toItem(const T& t) { return t; } }; ///This is a virtual set-node that allows conveniently iterating through the tree-structure, ///accessing the contained items directly, and accessing the ranges. template class VirtualSetNode { private: using ClassType = VirtualSetNode; public: inline explicit VirtualSetNode(const SetNodeData* data = nullptr) : m_data(data) { } inline bool isValid() const { return (bool)m_data; } ///If this returns false, a left and a right node are available. ///If this returns true, this node represents a single item, that can be retrieved by calling item() or operator*. inline bool isFinalNode() const { return m_data->leftNode() == 0; } inline T firstItem() const { return Conversion::toItem(start()); } inline T lastItem() const { return Conversion::toItem(end()-1); } inline T operator*() const { return Conversion::toItem(start()); } inline ClassType leftChild() const { if(m_data->leftNode()) return ClassType(StaticRepository::repository()->nodeFromIndex(m_data->leftNode())); else return ClassType(nullptr); } inline ClassType rightChild() const { if(m_data->rightNode()) return ClassType(StaticRepository::repository()->nodeFromIndex(m_data->rightNode())); else return ClassType(nullptr); } ///Returns the start of this node's range. If this is a final node, the length of the range is 1. inline uint start() const { return m_data->start(); } ///Returns the end of this node's range. inline uint end() const { return m_data->end(); } private: const SetNodeData* m_data; }; template class StorableSet : public Conversion { public: typedef VirtualSetNode Node; StorableSet(const StorableSet& rhs) : m_setIndex(rhs.m_setIndex) { StaticAccessLocker lock; Q_UNUSED(lock); if(doReferenceCounting) set().staticRef(); } explicit StorableSet(const std::set& indices) { StaticAccessLocker lock; Q_UNUSED(lock); m_setIndex = StaticRepository::repository()->createSet(indices).setIndex(); if(doReferenceCounting) set().staticRef(); } StorableSet() : m_setIndex(0) { } ~StorableSet() { StaticAccessLocker lock; Q_UNUSED(lock); if(doReferenceCounting) set().staticUnref(); } void insert(const T& t) { insertIndex(Conversion::toIndex(t)); } bool isEmpty() const { return m_setIndex == 0; } uint count() const { return set().count(); } void insertIndex(uint index) { StaticAccessLocker lock; Q_UNUSED(lock); Set set(m_setIndex, StaticRepository::repository()); Set oldSet(set); Set addedSet = StaticRepository::repository()->createSet(index); if(doReferenceCounting) addedSet.staticRef(); set += addedSet; m_setIndex = set.setIndex(); if(doReferenceCounting) { set.staticRef(); oldSet.staticUnref(); addedSet.staticUnref(); } } void remove(const T& t) { removeIndex(Conversion::toIndex(t)); } void removeIndex(uint index) { StaticAccessLocker lock; Q_UNUSED(lock); Set set(m_setIndex, StaticRepository::repository()); Set oldSet(set); Set removedSet = StaticRepository::repository()->createSet(index); if(doReferenceCounting) { removedSet.staticRef(); } set -= removedSet; m_setIndex = set.setIndex(); if(doReferenceCounting) { set.staticRef(); oldSet.staticUnref(); removedSet.staticUnref(); } } Set set() const { return Set(m_setIndex, StaticRepository::repository()); } bool contains(const T& item) const { return containsIndex(Conversion::toIndex(item)); } bool containsIndex(uint index) const { StaticAccessLocker lock; Q_UNUSED(lock); Set set(m_setIndex, StaticRepository::repository()); return set.contains(index); } StorableSet& operator +=(const StorableSet& rhs) { StaticAccessLocker lock; Q_UNUSED(lock); Set set(m_setIndex, StaticRepository::repository()); Set oldSet(set); Set otherSet(rhs.m_setIndex, StaticRepository::repository()); set += otherSet; m_setIndex = set.setIndex(); if(doReferenceCounting) { set.staticRef(); oldSet.staticUnref(); } return *this; } StorableSet& operator -=(const StorableSet& rhs) { StaticAccessLocker lock; Q_UNUSED(lock); Set set(m_setIndex, StaticRepository::repository()); Set oldSet(set); Set otherSet(rhs.m_setIndex, StaticRepository::repository()); set -= otherSet; m_setIndex = set.setIndex(); if(doReferenceCounting) { set.staticRef(); oldSet.staticUnref(); } return *this; } StorableSet& operator &=(const StorableSet& rhs) { StaticAccessLocker lock; Q_UNUSED(lock); Set set(m_setIndex, StaticRepository::repository()); Set oldSet(set); Set otherSet(rhs.m_setIndex, StaticRepository::repository()); set &= otherSet; m_setIndex = set.setIndex(); if(doReferenceCounting) { set.staticRef(); oldSet.staticUnref(); } return *this; } StorableSet& operator=(const StorableSet& rhs) { StaticAccessLocker lock; Q_UNUSED(lock); if(doReferenceCounting) set().staticUnref(); m_setIndex = rhs.m_setIndex; if(doReferenceCounting) set().staticRef(); return *this; } StorableSet operator +(const StorableSet& rhs) const { StorableSet ret(*this); ret += rhs; return ret; } StorableSet operator -(const StorableSet& rhs) const { StorableSet ret(*this); ret -= rhs; return ret; } StorableSet operator &(const StorableSet& rhs) const { StorableSet ret(*this); ret &= rhs; return ret; } bool operator==(const StorableSet& rhs) const { return m_setIndex == rhs.m_setIndex; } typedef ConvenientIterator Iterator; Iterator iterator() const { return ConvenientIterator(set()); } Node node() const { return Node(StaticRepository::repository()->nodeFromIndex(m_setIndex)); } uint setIndex() const { return m_setIndex; } private: uint m_setIndex; }; template uint qHash(const StorableSet& set) { return set.setIndex(); } /** This is a helper-class that helps inserting a bunch of items into a set without caring about grouping them together. * * It creates a much better tree-structure if many items are inserted at one time, and this class helps doing that in * cases where there is no better choice then storing a temporary list of items and inserting them all at once. * * This set will then care about really inserting them into the repository once the real set is requested. * * @todo eventually make this unnecessary * * @tparam T Should be the type that should be dealt * @tparam Conversion Should be a class that has a toIndex member function that takes an object of type T as parameter, and returns an index, * and a toItem member function that takes an index, and returns an item of type T. **/ template class LazySet : public Conversion { public: /** @param rep The repository the set should belong/belongs to * @param lockBeforeAccess If this is nonzero, the given mutex will be locked before each modification to the repository. * @param basicSet If this is explicitly given, the given set will be used as base. However it will not be changed. * * @warning Watch for deadlocks, never use this class while the mutex given through lockBeforeAccess is locked */ explicit LazySet(BasicSetRepository* rep, QMutex* lockBeforeAccess = 0, const Set& basicSet = Set()) : m_rep(rep), m_set(basicSet), m_lockBeforeAccess(lockBeforeAccess) { } void insert(const T& t) { if(!m_temporaryRemoveIndices.empty()) apply(); m_temporaryIndices.insert(Conversion::toIndex(t)); } void insertIndex(uint index) { if(!m_temporaryRemoveIndices.empty()) apply(); m_temporaryIndices.insert(index); } void remove(const T& t) { if(!m_temporaryIndices.empty()) apply(); m_temporaryRemoveIndices.insert(Conversion::toIndex(t)); } ///Returns the set this LazySet represents. When this is called, the set is constructed in the repository. Set set() const { apply(); return m_set; } ///@warning this is expensive, because the set is constructed bool contains(const T& item) const { QMutexLocker l(m_lockBeforeAccess); uint index = Conversion::toIndex(item); if( m_temporaryRemoveIndices.empty() ) { //Simplification without creating the set if(m_temporaryIndices.find(index) != m_temporaryIndices.end()) return true; return m_set.contains(index); } return set().contains(index); } LazySet& operator +=(const Set& set) { if(!m_temporaryRemoveIndices.empty()) apply(); QMutexLocker l(m_lockBeforeAccess); m_set += set; return *this; } LazySet& operator -=(const Set& set) { if(!m_temporaryIndices.empty()) apply(); QMutexLocker l(m_lockBeforeAccess); m_set -= set; return *this; } LazySet operator +(const Set& set) const { apply(); QMutexLocker l(m_lockBeforeAccess); Set ret = m_set + set; return LazySet(m_rep, m_lockBeforeAccess, ret); } LazySet operator -(const Set& set) const { apply(); QMutexLocker l(m_lockBeforeAccess); Set ret = m_set - set; return LazySet(m_rep, m_lockBeforeAccess, ret); } void clear() { QMutexLocker l(m_lockBeforeAccess); m_set = Set(); m_temporaryIndices.clear(); m_temporaryRemoveIndices.clear(); } ConvenientIterator iterator() const { apply(); return ConvenientIterator(set()); } private: void apply() const { if(!m_temporaryIndices.empty()) { QMutexLocker l(m_lockBeforeAccess); Set tempSet = m_rep->createSet(m_temporaryIndices); m_temporaryIndices.clear(); m_set += tempSet; } if(!m_temporaryRemoveIndices.empty()) { QMutexLocker l(m_lockBeforeAccess); Set tempSet = m_rep->createSet(m_temporaryRemoveIndices); m_temporaryRemoveIndices.clear(); m_set -= tempSet; } } BasicSetRepository* m_rep; mutable Set m_set; QMutex* m_lockBeforeAccess; typedef std::set IndexList; mutable IndexList m_temporaryIndices; mutable IndexList m_temporaryRemoveIndices; }; ///Persistent repository that manages string-sets, also correctly increasing the string reference-counts as needed struct KDEVPLATFORMLANGUAGE_EXPORT StringSetRepository : public Utils::BasicSetRepository { explicit StringSetRepository(QString name); void itemRemovedFromSets(uint index) override; void itemAddedToSets(uint index) override; }; } #endif diff --git a/language/util/tests/test_kdevhash.cpp b/language/util/tests/test_kdevhash.cpp index 28d1ffe6a..e976ca5fd 100644 --- a/language/util/tests/test_kdevhash.cpp +++ b/language/util/tests/test_kdevhash.cpp @@ -1,82 +1,82 @@ /* This file is part of KDevelop Copyright 2015 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 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 -#include +#include #include #include #include "../kdevhash.h" namespace { template std::vector generateData(std::size_t size) { auto ret = std::vector(size); std::iota(ret.begin(), ret.end(), T(0)); return ret; } template void runBench() { const auto data = generateData(10000); KDevHash hash; QBENCHMARK { for (T i : data) { hash << i; } } QVERIFY(static_cast(hash)); } } class TestKDevHash : public QObject { Q_OBJECT private slots: void benchHash_int() { runBench(); } void benchHash_uint() { runBench(); } void benchHash_quint64() { runBench(); } void benchHash_bool() { runBench(); } }; QTEST_MAIN(TestKDevHash) #include "test_kdevhash.moc" diff --git a/outputview/filtereditem.h b/outputview/filtereditem.h index b567671cc..f02b2e9ac 100644 --- a/outputview/filtereditem.h +++ b/outputview/filtereditem.h @@ -1,73 +1,73 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * * Copyright (C) 2012 Morten Danielsen Volden mvolden2@gmail.com * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_FILTEREDITEM_H #define KDEVPLATFORM_FILTEREDITEM_H -#include -#include +#include +#include #include #include namespace KDevelop { /** * Holds all metadata of a given compiler/script/whatever output line. E.g. if it is an error or * a warning, if it is clickable or not, line number, column number, etc. * Note that line and column number are stored as starting with with 0 (Your compiler/whatever tool * may count from 1 ). **/ struct KDEVPLATFORMOUTPUTVIEW_EXPORT FilteredItem { enum FilteredOutputItemType { InvalidItem = 0, ErrorItem = 1, WarningItem = 2, ActionItem = 3, CustomItem = 4, StandardItem = 5, InformationItem = 6 }; explicit FilteredItem( const QString& line = QString() ); FilteredItem( const QString& line, FilteredOutputItemType type ); QString originalLine; FilteredOutputItemType type; bool isActivatable; QUrl url; /// lineNo starts with 0 int lineNo; /// columnNo starts with 0 int columnNo; }; } // namespace KDevelop Q_DECLARE_METATYPE( KDevelop::FilteredItem::FilteredOutputItemType ) Q_DECLARE_TYPEINFO( KDevelop::FilteredItem, Q_MOVABLE_TYPE ); #endif // KDEVPLATFORM_FILTEREDITEM_H diff --git a/outputview/ioutputview.h b/outputview/ioutputview.h index 1bd346f34..992b7ed14 100644 --- a/outputview/ioutputview.h +++ b/outputview/ioutputview.h @@ -1,184 +1,184 @@ /* KDevelop Output View * * Copyright 2006-2007 Andreas Pakulat * * 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. */ #ifndef KDEVPLATFORM_IOUTPUTVIEW_H #define KDEVPLATFORM_IOUTPUTVIEW_H -#include -#include +#include +#include #include #include "outputviewexport.h" class QString; class QAbstractItemModel; class QModelIndex; class QAbstractItemDelegate; class QAction; /** @author Andreas Pakulat */ namespace KDevelop { class KDEVPLATFORMOUTPUTVIEW_EXPORT IOutputView { public: enum Behaviour { AllowUserClose = 0x1 /**< allow the user to close the view */, AlwaysShowView = 0x2 /**< always show the view */, AutoScroll = 0x4 /**< automatically scroll the view */ }; Q_DECLARE_FLAGS(Behaviours, Behaviour) enum Option { NoOptions = 0x0, ShowItemsButton = 0x1 /**< show the two buttons (select and focus) */, AddFilterAction = 0x2 /**< add a filter action */ }; Q_DECLARE_FLAGS(Options, Option) enum ViewType { OneView = 0 /**< there's only one outputview, newly registered outputs will replace existing ones */, HistoryView = 1 /**< The toolview will have a history with forward/backward buttons */, MultipleView = 2 /**< show multiples outputs in a toolview at the same time */ }; enum StandardToolView { BuildView = 0 /**< the standard outputview for building output */, RunView = 1 /**< the standard outputview for running apps */, DebugView = 2 /**< the standard outputview for debugging apps */, TestView = 4 /**< the standard outputview for verbose test output */, VcsView = 8 /**< the standard outputview for VCS commands */ }; virtual ~IOutputView(); /** * fetch the identifier for one of the standard toolviews * This will automatically create the toolview if it doesn't exist yet * @param view the standard toolview to get the identifier for * @returns the identifier for the standard toolview */ virtual int standardToolView( StandardToolView view ) = 0; /** * Register a new toolview for output with the given title, behaviour and type * If there already exists a toolview with this title and type return the existing id * @param title the Title to be displayed on the toolview * @param type the type of view that should be created * @param icon the icon of the toolview * @param option the options of the toolview * @param actionList list of actions adding to the toolbar * @returns an toolview id that identifies the new view and is used in the other * methods */ virtual int registerToolView( const QString& title, ViewType type = OneView, const QIcon& icon = QIcon(), Options option = ShowItemsButton, const QList& actionList = QList()) = 0; /** * Register a new output view in a given toolview. How this new view is created depends * on the type of the toolview. * @param toolviewId the id of the toolview, created by registerToolView * @param title the title to use for the new output in the toolview * @param behaviour the Behaviour of the output * @returns the id of the output to supply to the other methods */ virtual int registerOutputInToolView( int toolviewId, const QString& title, Behaviours behaviour = AllowUserClose ) = 0; /** * Raise a given view */ virtual void raiseOutput( int outputId ) = 0; virtual void scrollOutputTo( int outputId, const QModelIndex& ) = 0; /** * Sets the model of the registered output identified by @p outputId to @p model. * * Does nothing if the id doesn't exist. The output view takes ownership of the model. * * NOTE: Do not reuse the same model for different views. */ virtual void setModel( int outputId, QAbstractItemModel* model ) = 0; /** * Sets the item delegate of the registered output identified by @p outputId to @p delegate. * * Does nothing if the id doesn't exist. The output view takes ownership of the delegate. * * NOTE: Do not reuse the same delegate for different views. */ virtual void setDelegate( int outputId, QAbstractItemDelegate* model ) = 0; /** * Sets a @p title for the specified @p outputIds */ virtual void setTitle( int outputId, const QString& title ) = 0; /** * remove a toolview, don't forget to emit toolViewRemoved when you implement this * * @param toolviewId identifies the view to remove */ virtual void removeToolView( int toolviewId ) = 0; /** * remove an output view from a toolview. Don't forget to emit outputRemoved * when you implement this. * @param outputId the id of the outputview to remove */ virtual void removeOutput( int outputId ) = 0; Q_SIGNALS: /** * emitted after a toolview was removed * * @param toolviewId identifies the removed toolview */ void toolViewRemoved( int toolviewId ); /** * emitted after a toolview was removed * * @param toolviewId identifies the removed toolview * @param outputId identifies the removed output */ void outputRemoved( int toolviewId, int outputId ); }; } // namespace KDevelop Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::IOutputView::Behaviours) Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::IOutputView::Options) Q_DECLARE_METATYPE(KDevelop::IOutputView::StandardToolView) Q_DECLARE_INTERFACE( KDevelop::IOutputView, "org.kdevelop.IOutputView" ) #endif diff --git a/outputview/outputjob.h b/outputview/outputjob.h index 7770044c4..306224d29 100644 --- a/outputview/outputjob.h +++ b/outputview/outputjob.h @@ -1,106 +1,106 @@ /* This file is part of KDevelop Copyright 2007-2008 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. */ #ifndef KDEVPLATFORM_OUTPUTJOB_H #define KDEVPLATFORM_OUTPUTJOB_H #include -#include +#include #include #include #include namespace KDevelop { class KDEVPLATFORMOUTPUTVIEW_EXPORT OutputJob : public KJob { Q_OBJECT public: enum { FailedShownError = UserDefinedError + 100 //job failed and failure is shown in OutputView }; enum OutputJobVerbosity { Silent, Verbose }; explicit OutputJob(QObject* parent = nullptr, OutputJobVerbosity verbosity = OutputJob::Verbose); void startOutput(); OutputJobVerbosity verbosity() const; void setVerbosity(OutputJobVerbosity verbosity); QAbstractItemModel* model() const; /// Set the \a title for this job's output tab. If not set, will default to the job's objectName(). void setTitle(const QString& title); protected: void setStandardToolView(IOutputView::StandardToolView standard); void setToolTitle(const QString& title); void setToolIcon(const QIcon& icon); void setViewType(IOutputView::ViewType type); void setBehaviours(IOutputView::Behaviours behaviours); void setKillJobOnOutputClose(bool killJobOnOutputClose); /** * Sets the model for the view that shows this jobs output. * * The view takes ownership of the model, but it is safe to * use the model while the job is running. * * NOTE: Do not reuse the same model for different jobs. */ void setModel(QAbstractItemModel* model); /** * Sets the delegate for the view that shows this jobs output. * * The view takes ownership of the delegate, but it is safe to * use the delegate while the job is running. * * NOTE: Do not reuse the same delegate for different jobs. */ void setDelegate(QAbstractItemDelegate* delegate); int outputId() const; private Q_SLOTS: void outputViewRemoved(int , int id); private: int m_standardToolView; QString m_title, m_toolTitle; QIcon m_toolIcon; IOutputView::ViewType m_type; IOutputView::Behaviours m_behaviours; bool m_killJobOnOutputClose; OutputJobVerbosity m_verbosity; int m_outputId; QPointer m_outputModel; QAbstractItemDelegate* m_outputDelegate; }; } #endif diff --git a/outputview/outputmodel.cpp b/outputview/outputmodel.cpp index aa5758fb8..bb45cb132 100644 --- a/outputview/outputmodel.cpp +++ b/outputview/outputmodel.cpp @@ -1,475 +1,475 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * * Copyright 2010 Aleix Pol Gonzalez * * Copyright (C) 2012 Morten Danielsen Volden mvolden2@gmail.com * * * * 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 "outputmodel.h" #include "filtereditem.h" #include "outputfilteringstrategies.h" #include "debug.h" #include #include #include -#include -#include +#include +#include #include #include #include #include #include Q_DECLARE_METATYPE(QVector) namespace KDevelop { /** * Number of lines that are processed in one go before we notify the GUI thread * about the result. It is generally faster to add multiple items to a model * in one go compared to adding each item independently. */ static const int BATCH_SIZE = 50; /** * Time in ms that we wait in the parse worker for new incoming lines before * actually processing them. If we already have enough for one batch though * we process immediately. */ static const int BATCH_AGGREGATE_TIME_DELAY = 50; class ParseWorker : public QObject { Q_OBJECT public: ParseWorker() : QObject(nullptr) , m_filter(new NoFilterStrategy) , m_timer(new QTimer(this)) { m_timer->setInterval(BATCH_AGGREGATE_TIME_DELAY); m_timer->setSingleShot(true); connect(m_timer, &QTimer::timeout, this, &ParseWorker::process); } public slots: void changeFilterStrategy( KDevelop::IFilterStrategy* newFilterStrategy ) { m_filter = QSharedPointer( newFilterStrategy ); } void addLines( const QStringList& lines ) { m_cachedLines << lines; if (m_cachedLines.size() >= BATCH_SIZE) { // if enough lines were added, process immediately m_timer->stop(); process(); } else if (!m_timer->isActive()) { m_timer->start(); } } void flushBuffers() { m_timer->stop(); process(); emit allDone(); } signals: void parsedBatch(const QVector& filteredItems); void progress(const KDevelop::IFilterStrategy::Progress& progress); void allDone(); private slots: /** * Process *all* cached lines, emit parsedBatch for each batch */ void process() { QVector filteredItems; filteredItems.reserve(qMin(BATCH_SIZE, m_cachedLines.size())); // apply pre-filtering functions std::transform(m_cachedLines.constBegin(), m_cachedLines.constEnd(), m_cachedLines.begin(), &KDevelop::stripAnsiSequences); // apply filtering strategy foreach(const QString& line, m_cachedLines) { FilteredItem item = m_filter->errorInLine(line); if( item.type == FilteredItem::InvalidItem ) { item = m_filter->actionInLine(line); } filteredItems << item; auto progress = m_filter->progressInLine(line); if (progress.percent >= 0 && m_progress.percent != progress.percent) { m_progress = progress; emit this->progress(m_progress); } if( filteredItems.size() == BATCH_SIZE ) { emit parsedBatch(filteredItems); filteredItems.clear(); filteredItems.reserve(qMin(BATCH_SIZE, m_cachedLines.size())); } } // Make sure to emit the rest as well if( !filteredItems.isEmpty() ) { emit parsedBatch(filteredItems); } m_cachedLines.clear(); } private: QSharedPointer m_filter; QStringList m_cachedLines; QTimer* m_timer; IFilterStrategy::Progress m_progress; }; class ParsingThread { public: ParsingThread() { m_thread.setObjectName(QStringLiteral("OutputFilterThread")); } virtual ~ParsingThread() { if (m_thread.isRunning()) { m_thread.quit(); m_thread.wait(); } } void addWorker(ParseWorker* worker) { if (!m_thread.isRunning()) { m_thread.start(); } worker->moveToThread(&m_thread); } private: QThread m_thread; }; Q_GLOBAL_STATIC(ParsingThread, s_parsingThread); struct OutputModelPrivate { explicit OutputModelPrivate( OutputModel* model, const QUrl& builddir = QUrl() ); ~OutputModelPrivate(); bool isValidIndex( const QModelIndex&, int currentRowCount ) const; OutputModel* model; ParseWorker* worker; QVector m_filteredItems; // We use std::set because that is ordered std::set m_errorItems; // Indices of all items that we want to move to using previous and next QUrl m_buildDir; void linesParsed(const QVector& items) { model->beginInsertRows( QModelIndex(), model->rowCount(), model->rowCount() + items.size() - 1); foreach( const FilteredItem& item, items ) { if( item.type == FilteredItem::ErrorItem ) { m_errorItems.insert(m_filteredItems.size()); } m_filteredItems << item; } model->endInsertRows(); } }; OutputModelPrivate::OutputModelPrivate( OutputModel* model_, const QUrl& builddir) : model(model_) , worker(new ParseWorker ) , m_buildDir( builddir ) { qRegisterMetaType >(); qRegisterMetaType(); qRegisterMetaType(); s_parsingThread->addWorker(worker); model->connect(worker, &ParseWorker::parsedBatch, model, [=] (const QVector& items) { linesParsed(items); }); model->connect(worker, &ParseWorker::allDone, model, &OutputModel::allDone); model->connect(worker, &ParseWorker::progress, model, &OutputModel::progress); } bool OutputModelPrivate::isValidIndex( const QModelIndex& idx, int currentRowCount ) const { return ( idx.isValid() && idx.row() >= 0 && idx.row() < currentRowCount && idx.column() == 0 ); } OutputModelPrivate::~OutputModelPrivate() { worker->deleteLater(); } OutputModel::OutputModel( const QUrl& builddir, QObject* parent ) : QAbstractListModel(parent) , d( new OutputModelPrivate( this, builddir ) ) { } OutputModel::OutputModel( QObject* parent ) : QAbstractListModel(parent) , d( new OutputModelPrivate( this ) ) { } OutputModel::~OutputModel() { delete d; } QVariant OutputModel::data(const QModelIndex& idx , int role ) const { if( d->isValidIndex(idx, rowCount()) ) { switch( role ) { case Qt::DisplayRole: return d->m_filteredItems.at( idx.row() ).originalLine; break; case OutputModel::OutputItemTypeRole: return static_cast(d->m_filteredItems.at( idx.row() ).type); break; case Qt::FontRole: return QFontDatabase::systemFont(QFontDatabase::FixedFont); break; default: break; } } return QVariant(); } int OutputModel::rowCount( const QModelIndex& parent ) const { if( !parent.isValid() ) return d->m_filteredItems.count(); return 0; } QVariant OutputModel::headerData( int, Qt::Orientation, int ) const { return QVariant(); } void OutputModel::activate( const QModelIndex& index ) { if( index.model() != this || !d->isValidIndex(index, rowCount()) ) { return; } qCDebug(OUTPUTVIEW) << "Model activated" << index.row(); FilteredItem item = d->m_filteredItems.at( index.row() ); if( item.isActivatable ) { qCDebug(OUTPUTVIEW) << "activating:" << item.lineNo << item.url; KTextEditor::Cursor range( item.lineNo, item.columnNo ); KDevelop::IDocumentController *docCtrl = KDevelop::ICore::self()->documentController(); QUrl url = item.url; if (item.url.isEmpty()) { qCWarning(OUTPUTVIEW) << "trying to open empty url"; return; } if(url.isRelative()) { url = d->m_buildDir.resolved(url); } Q_ASSERT(!url.isRelative()); docCtrl->openDocument( url, range ); } else { qCDebug(OUTPUTVIEW) << "not an activateable item"; } } QModelIndex OutputModel::firstHighlightIndex() { if( !d->m_errorItems.empty() ) { return index( *d->m_errorItems.begin(), 0, QModelIndex() ); } for( int row = 0; row < rowCount(); ++row ) { if( d->m_filteredItems.at( row ).isActivatable ) { return index( row, 0, QModelIndex() ); } } return QModelIndex(); } QModelIndex OutputModel::nextHighlightIndex( const QModelIndex ¤tIdx ) { int startrow = d->isValidIndex(currentIdx, rowCount()) ? currentIdx.row() + 1 : 0; if( !d->m_errorItems.empty() ) { qCDebug(OUTPUTVIEW) << "searching next error"; // Jump to the next error item std::set< int >::const_iterator next = d->m_errorItems.lower_bound( startrow ); if( next == d->m_errorItems.end() ) next = d->m_errorItems.begin(); return index( *next, 0, QModelIndex() ); } for( int row = 0; row < rowCount(); ++row ) { int currow = (startrow + row) % rowCount(); if( d->m_filteredItems.at( currow ).isActivatable ) { return index( currow, 0, QModelIndex() ); } } return QModelIndex(); } QModelIndex OutputModel::previousHighlightIndex( const QModelIndex ¤tIdx ) { //We have to ensure that startrow is >= rowCount - 1 to get a positive value from the % operation. int startrow = rowCount() + (d->isValidIndex(currentIdx, rowCount()) ? currentIdx.row() : rowCount()) - 1; if(!d->m_errorItems.empty()) { qCDebug(OUTPUTVIEW) << "searching previous error"; // Jump to the previous error item std::set< int >::const_iterator previous = d->m_errorItems.lower_bound( currentIdx.row() ); if( previous == d->m_errorItems.begin() ) previous = d->m_errorItems.end(); --previous; return index( *previous, 0, QModelIndex() ); } for ( int row = 0; row < rowCount(); ++row ) { int currow = (startrow - row) % rowCount(); if( d->m_filteredItems.at( currow ).isActivatable ) { return index( currow, 0, QModelIndex() ); } } return QModelIndex(); } QModelIndex OutputModel::lastHighlightIndex() { if( !d->m_errorItems.empty() ) { return index( *d->m_errorItems.rbegin(), 0, QModelIndex() ); } for( int row = rowCount()-1; row >=0; --row ) { if( d->m_filteredItems.at( row ).isActivatable ) { return index( row, 0, QModelIndex() ); } } return QModelIndex(); } void OutputModel::setFilteringStrategy(const OutputFilterStrategy& currentStrategy) { // TODO: Turn into factory, decouple from OutputModel IFilterStrategy* filter = nullptr; switch( currentStrategy ) { case NoFilter: filter = new NoFilterStrategy; break; case CompilerFilter: filter = new CompilerFilterStrategy( d->m_buildDir ); break; case ScriptErrorFilter: filter = new ScriptErrorFilterStrategy; break; case NativeAppErrorFilter: filter = new NativeAppErrorFilterStrategy; break; case StaticAnalysisFilter: filter = new StaticAnalysisFilterStrategy; break; default: // assert(false); filter = new NoFilterStrategy; break; } Q_ASSERT(filter); QMetaObject::invokeMethod(d->worker, "changeFilterStrategy", Q_ARG(KDevelop::IFilterStrategy*, filter)); } void OutputModel::setFilteringStrategy(IFilterStrategy* filterStrategy) { QMetaObject::invokeMethod(d->worker, "changeFilterStrategy", Q_ARG(KDevelop::IFilterStrategy*, filterStrategy)); } void OutputModel::appendLines( const QStringList& lines ) { if( lines.isEmpty() ) return; QMetaObject::invokeMethod(d->worker, "addLines", Q_ARG(QStringList, lines)); } void OutputModel::appendLine( const QString& line ) { appendLines( QStringList() << line ); } void OutputModel::ensureAllDone() { QMetaObject::invokeMethod(d->worker, "flushBuffers"); } void OutputModel::clear() { ensureAllDone(); beginResetModel(); d->m_filteredItems.clear(); endResetModel(); } } #include "outputmodel.moc" #include "moc_outputmodel.cpp" diff --git a/outputview/outputmodel.h b/outputview/outputmodel.h index d34c592c0..24a5eaf9f 100644 --- a/outputview/outputmodel.h +++ b/outputview/outputmodel.h @@ -1,96 +1,96 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * * Copyright (C) 2012 Morten Danielsen Volden mvolden2@gmail.com * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_OUTPUTMODEL_H #define KDEVPLATFORM_OUTPUTMODEL_H #include "outputviewexport.h" #include "ioutputviewmodel.h" #include "ifilterstrategy.h" -#include +#include #include namespace KDevelop { struct OutputModelPrivate; class KDEVPLATFORMOUTPUTVIEW_EXPORT OutputModel : public QAbstractListModel, public KDevelop::IOutputViewModel { Q_OBJECT public: enum CustomRoles { OutputItemTypeRole = Qt::UserRole + 1 }; enum OutputFilterStrategy { NoFilter, CompilerFilter, ScriptErrorFilter, NativeAppErrorFilter, StaticAnalysisFilter }; explicit OutputModel( const QUrl& builddir , QObject* parent = nullptr ); explicit OutputModel( QObject* parent = nullptr ); ~OutputModel() override; /// IOutputViewModel interfaces void activate( const QModelIndex& index ) override; QModelIndex firstHighlightIndex() override; QModelIndex nextHighlightIndex( const QModelIndex ¤t ) override; QModelIndex previousHighlightIndex( const QModelIndex ¤t ) override; QModelIndex lastHighlightIndex() override; /// QAbstractItemModel interfaces QVariant data( const QModelIndex&, int = Qt::DisplayRole ) const override; int rowCount( const QModelIndex& = QModelIndex() ) const override; QVariant headerData( int, Qt::Orientation, int = Qt::DisplayRole ) const override; void setFilteringStrategy(const OutputFilterStrategy& currentStrategy); void setFilteringStrategy(IFilterStrategy* filterStrategy); public Q_SLOTS: void appendLine( const QString& ); void appendLines( const QStringList& ); void ensureAllDone(); void clear(); signals: /// If the current filter strategy supports it, reports progress information void progress(const KDevelop::IFilterStrategy::Progress& progress); void allDone(); private: OutputModelPrivate* const d; friend struct OutputModelPrivate; }; } Q_DECLARE_METATYPE( KDevelop::OutputModel::OutputFilterStrategy ) #endif diff --git a/plugins/bazaar/bazaarplugin.cpp b/plugins/bazaar/bazaarplugin.cpp index 02a7dfb20..89052c6ef 100644 --- a/plugins/bazaar/bazaarplugin.cpp +++ b/plugins/bazaar/bazaarplugin.cpp @@ -1,343 +1,343 @@ /*************************************************************************** * Copyright 2013-2014 Maciej Poleski * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #include "bazaarplugin.h" -#include +#include #include #include #include #include #include #include #include #include #include #include "bazaarutils.h" #include "bzrannotatejob.h" #include "copyjob.h" #include "diffjob.h" using namespace KDevelop; BazaarPlugin::BazaarPlugin(QObject* parent, const QVariantList& args) : IPlugin(QStringLiteral("kdevbazaar"), parent), m_vcsPluginHelper(new KDevelop::VcsPluginHelper(this, this)) { Q_UNUSED(args); // What is this? if (QStandardPaths::findExecutable(QStringLiteral("bzr")).isEmpty()) { setErrorDescription(i18n("Unable to find Bazaar (bzr) executable Is it installed on the system?")); return; } setObjectName(QStringLiteral("Bazaar")); } BazaarPlugin::~BazaarPlugin() { } QString BazaarPlugin::name() const { return QStringLiteral("Bazaar"); } bool BazaarPlugin::isValidRemoteRepositoryUrl(const QUrl& remoteLocation) { const QString scheme = remoteLocation.scheme(); if (scheme == QLatin1String("bzr") || scheme == QLatin1String("bzr+ssh")) { return true; } return false; } VcsJob* BazaarPlugin::add(const QList& localLocations, IBasicVersionControl::RecursionMode recursion) { DVcsJob* job = new DVcsJob(BazaarUtils::workingCopy(localLocations[0]), this); job->setType(VcsJob::Add); *job << "bzr" << "add"; if(recursion == NonRecursive) *job << "--no-recurse"; *job << localLocations; return job; } VcsJob* BazaarPlugin::annotate(const QUrl& localLocation, const VcsRevision& rev) { VcsJob* job = new BzrAnnotateJob(BazaarUtils::workingCopy(localLocation), BazaarUtils::getRevisionSpec(rev), localLocation, this, KDevelop::OutputJob::Silent); return job; } VcsJob* BazaarPlugin::commit(const QString& message, const QList& localLocations, IBasicVersionControl::RecursionMode recursion) { QDir dir = BazaarUtils::workingCopy(localLocations[0]); DVcsJob* job = new DVcsJob(dir, this); job->setType(VcsJob::Commit); *job << "bzr" << "commit" << BazaarUtils::handleRecursion(localLocations, recursion) << "-m" << message; return job; } VcsJob* BazaarPlugin::copy(const QUrl& localLocationSrc, const QUrl& localLocationDstn) { return new CopyJob(localLocationSrc, localLocationDstn, this); } VcsImportMetadataWidget* BazaarPlugin::createImportMetadataWidget(QWidget* parent) { return new DvcsImportMetadataWidget(parent); } VcsJob* BazaarPlugin::createWorkingCopy(const VcsLocation& sourceRepository, const QUrl& destinationDirectory, IBasicVersionControl::RecursionMode recursion) { Q_UNUSED(recursion); // What is the purpose of recursion parameter? DVcsJob* job = new DVcsJob(BazaarUtils::toQDir(sourceRepository.localUrl()), this); job->setType(VcsJob::Import); *job << "bzr" << "branch" << sourceRepository.localUrl().url() << destinationDirectory; return job; } VcsJob* BazaarPlugin::diff(const QUrl& fileOrDirectory, const VcsRevision& srcRevision, const VcsRevision& dstRevision, VcsDiff::Type, IBasicVersionControl::RecursionMode recursion) { Q_UNUSED(recursion); VcsJob* job = new DiffJob(BazaarUtils::workingCopy(fileOrDirectory), BazaarUtils::getRevisionSpecRange(srcRevision, dstRevision), fileOrDirectory, this); return job; } VcsJob* BazaarPlugin::init(const QUrl& localRepositoryRoot) { DVcsJob* job = new DVcsJob(BazaarUtils::toQDir(localRepositoryRoot), this); job->setType(VcsJob::Import); *job << "bzr" << "init"; return job; } bool BazaarPlugin::isVersionControlled(const QUrl& localLocation) { QDir workCopy = BazaarUtils::workingCopy(localLocation); DVcsJob* job = new DVcsJob(workCopy, this, OutputJob::Silent); job->setType(VcsJob::Unknown); job->setIgnoreError(true); *job << "bzr" << "ls" << "--from-root" << "-R" << "-V"; job->exec(); if (job->status() == VcsJob::JobSucceeded) { QList filesAndDirectoriesList; foreach (const QString& fod, job->output().split('\n')) { filesAndDirectoriesList.append(QFileInfo(workCopy.absolutePath() + QDir::separator() + fod)); } QFileInfo fi(localLocation.toLocalFile()); if (fi.isDir() || fi.isFile()) { QFileInfo file(localLocation.toLocalFile()); return filesAndDirectoriesList.contains(file); } } return false; } VcsJob* BazaarPlugin::log(const QUrl& localLocation, const VcsRevision& rev, long unsigned int limit) { DVcsJob* job = new DVcsJob(BazaarUtils::workingCopy(localLocation), this); job->setType(VcsJob::Log); *job << "bzr" << "log" << "--long" << "-v" << localLocation << BazaarUtils::getRevisionSpecRange(rev) << "-l" << QString::number(limit); connect(job, &DVcsJob::readyForParsing, this, &BazaarPlugin::parseBzrLog); return job; } VcsJob* BazaarPlugin::log(const QUrl& localLocation, const VcsRevision& rev, const VcsRevision& limit) { DVcsJob* job = new DVcsJob(BazaarUtils::workingCopy(localLocation), this); job->setType(VcsJob::Log); *job << "bzr" << "log" << "--long" << "-v" << localLocation << BazaarUtils::getRevisionSpecRange(limit, rev); connect(job, &DVcsJob::readyForParsing, this, &BazaarPlugin::parseBzrLog); return job; } void BazaarPlugin::parseBzrLog(DVcsJob* job) { QVariantList result; auto parts = job->output().split(QStringLiteral("------------------------------------------------------------"), QString::SkipEmptyParts); foreach (const QString& part, parts) { auto event = BazaarUtils::parseBzrLogPart(part); if (event.revision().revisionType() != VcsRevision::Invalid) result.append(QVariant::fromValue(event)); } job->setResults(result); } VcsJob* BazaarPlugin::move(const QUrl& localLocationSrc, const QUrl& localLocationDst) { DVcsJob* job = new DVcsJob(BazaarUtils::workingCopy(localLocationSrc), this); job->setType(VcsJob::JobType::Move); *job << "bzr" << "move" << localLocationSrc << localLocationDst; return job; } VcsJob* BazaarPlugin::pull(const VcsLocation& localOrRepoLocationSrc, const QUrl& localRepositoryLocation) { // API describes hg pull which is git fetch equivalent // bzr has pull, but it succeeds only if fast-forward is possible // in other cases bzr merge should be used instead (bzr pull would fail) // Information about repository must be provided at least once. DVcsJob* job = new DVcsJob(BazaarUtils::workingCopy(localRepositoryLocation), this); job->setType(VcsJob::JobType::Pull); *job << "bzr" << "pull"; if (!localOrRepoLocationSrc.localUrl().isEmpty()) { *job << localOrRepoLocationSrc.localUrl(); } // localUrl always makes sense. Even on remote repositores which are handled // transparently. return job; } VcsJob* BazaarPlugin::push(const QUrl& localRepositoryLocation, const VcsLocation& localOrRepoLocationDst) { DVcsJob* job = new DVcsJob(BazaarUtils::workingCopy(localRepositoryLocation), this); job->setType(VcsJob::JobType::Push); *job << "bzr" << "push" << localOrRepoLocationDst.localUrl(); // localUrl always makes sense. Even on remote repositores which are handled // transparently. return job; } VcsJob* BazaarPlugin::remove(const QList& localLocations) { DVcsJob* job = new DVcsJob(BazaarUtils::workingCopy(localLocations[0]), this); job->setType(VcsJob::JobType::Remove); *job << "bzr" << "remove" << localLocations; return job; } VcsJob* BazaarPlugin::repositoryLocation(const QUrl& localLocation) { DVcsJob* job = new DVcsJob(BazaarUtils::workingCopy(localLocation), this); job->setType(VcsJob::JobType::Unknown); *job << "bzr" << "root" << localLocation; // It is only to make sure connect(job, &DVcsJob::readyForParsing, this, &BazaarPlugin::parseBzrRoot); return job; } void BazaarPlugin::parseBzrRoot(DVcsJob* job) { QString filename = job->dvcsCommand().at(2); QString rootDirectory = job->output(); QString localFilename = QFileInfo(QUrl::fromLocalFile(filename).toLocalFile()).absoluteFilePath(); QString result = localFilename.mid(localFilename.indexOf(rootDirectory) + rootDirectory.length()); job->setResults(QVariant::fromValue(result)); } VcsJob* BazaarPlugin::resolve(const QList& localLocations, IBasicVersionControl::RecursionMode recursion) { return add(localLocations, recursion); // How to provide "a conflict solving dialog to the user"? // In any case this plugin is unable to make any conflict. } VcsJob* BazaarPlugin::revert(const QList& localLocations, IBasicVersionControl::RecursionMode recursion) { DVcsJob* job = new DVcsJob(BazaarUtils::workingCopy(localLocations[0]), this); job->setType(VcsJob::JobType::Revert); *job << "bzr" << "revert" << BazaarUtils::handleRecursion(localLocations, recursion); return job; } VcsJob* BazaarPlugin::status(const QList& localLocations, IBasicVersionControl::RecursionMode recursion) { Q_UNUSED(recursion); DVcsJob* job = new DVcsJob(BazaarUtils::workingCopy(localLocations[0]), this); job->setType(VcsJob::Status); *job << "bzr" << "status" << "--short" << "--no-pending" << "--no-classify" << localLocations; connect(job, &DVcsJob::readyForParsing, this, &BazaarPlugin::parseBzrStatus); return job; } void BazaarPlugin::parseBzrStatus(DVcsJob* job) { QVariantList result; QSet filesWithStatus; QDir workingCopy = job->directory(); foreach (const QString& line, job->output().split('\n')) { auto status = BazaarUtils::parseVcsStatusInfoLine(line); result.append(QVariant::fromValue(status)); filesWithStatus.insert(BazaarUtils::concatenatePath(workingCopy, status.url())); } QStringList command = job->dvcsCommand(); for (auto it = command.constBegin() + command.indexOf(QStringLiteral("--no-classify")) + 1, itEnd = command.constEnd(); it != itEnd; ++it) { QString path = QFileInfo(*it).absoluteFilePath(); if (!filesWithStatus.contains(path)) { filesWithStatus.insert(path); KDevelop::VcsStatusInfo status; status.setState(VcsStatusInfo::ItemUpToDate); status.setUrl(QUrl::fromLocalFile(*it)); result.append(QVariant::fromValue(status)); } } job->setResults(result); } VcsJob* BazaarPlugin::update(const QList& localLocations, const VcsRevision& rev, IBasicVersionControl::RecursionMode recursion) { // bzr update is stronger than API (it's effectively merge) // the best approximation is bzr pull DVcsJob* job = new DVcsJob(BazaarUtils::workingCopy(localLocations[0]), this); Q_UNUSED(recursion); // recursion and file locations are ignored - we can update only whole // working copy job->setType(VcsJob::JobType::Update); *job << "bzr" << "pull" << BazaarUtils::getRevisionSpec(rev); return job; } VcsLocationWidget* BazaarPlugin::vcsLocation(QWidget* parent) const { return new KDevelop::StandardVcsLocationWidget(parent); } ContextMenuExtension BazaarPlugin::contextMenuExtension(Context* context) { m_vcsPluginHelper->setupFromContext(context); QList const& ctxUrlList = m_vcsPluginHelper->contextUrlList(); bool isWorkingDirectory = false; for (const QUrl & url : ctxUrlList) { if (BazaarUtils::isValidDirectory(url)) { isWorkingDirectory = true; break; } } if (!isWorkingDirectory) { // Not part of a repository return ContextMenuExtension(); } QMenu* menu = m_vcsPluginHelper->commonActions(); ContextMenuExtension menuExt; menuExt.addAction(ContextMenuExtension::VcsGroup, menu->menuAction()); return menuExt; } diff --git a/plugins/bazaar/bazaarplugin.h b/plugins/bazaar/bazaarplugin.h index c5fee4615..81956c0de 100644 --- a/plugins/bazaar/bazaarplugin.h +++ b/plugins/bazaar/bazaarplugin.h @@ -1,79 +1,79 @@ /*************************************************************************** * Copyright 2013-2014 Maciej Poleski * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #ifndef BAZAAR_BAZAARPLUGIN_H #define BAZAAR_BAZAARPLUGIN_H -#include +#include #include #include #include namespace KDevelop { class DVcsJob; } class BazaarPlugin : public KDevelop::IPlugin, public KDevelop::IDistributedVersionControl { Q_OBJECT Q_INTERFACES(KDevelop::IBasicVersionControl KDevelop::IDistributedVersionControl) public: explicit BazaarPlugin(QObject* parent, const QVariantList& args = QVariantList()); ~BazaarPlugin() override; QString name() const override; bool isValidRemoteRepositoryUrl(const QUrl& remoteLocation) override; KDevelop::VcsJob* add(const QList& localLocations, RecursionMode recursion=Recursive) override; KDevelop::VcsJob* annotate(const QUrl& localLocation, const KDevelop::VcsRevision& rev) override; KDevelop::VcsJob* commit(const QString& message, const QList& localLocations, RecursionMode recursion=Recursive) override; KDevelop::VcsJob* copy(const QUrl& localLocationSrc, const QUrl& localLocationDstn) override; KDevelop::VcsImportMetadataWidget* createImportMetadataWidget(QWidget* parent) override; KDevelop::VcsJob* createWorkingCopy(const KDevelop::VcsLocation& sourceRepository, const QUrl& destinationDirectory, RecursionMode recursion=Recursive) override; KDevelop::VcsJob* diff(const QUrl& fileOrDirectory, const KDevelop::VcsRevision& srcRevision, const KDevelop::VcsRevision& dstRevision, KDevelop::VcsDiff::Type, RecursionMode recursion=Recursive) override; KDevelop::VcsJob* init(const QUrl& localRepositoryRoot) override; bool isVersionControlled(const QUrl& localLocation) override; KDevelop::VcsJob* log(const QUrl& localLocation, const KDevelop::VcsRevision& rev, long unsigned int limit) override; KDevelop::VcsJob* log(const QUrl& localLocation, const KDevelop::VcsRevision& rev, const KDevelop::VcsRevision& limit) override; KDevelop::VcsJob* move(const QUrl& localLocationSrc, const QUrl& localLocationDst) override; KDevelop::VcsJob* pull(const KDevelop::VcsLocation& localOrRepoLocationSrc, const QUrl& localRepositoryLocation) override; KDevelop::VcsJob* push(const QUrl& localRepositoryLocation, const KDevelop::VcsLocation& localOrRepoLocationDst) override; KDevelop::VcsJob* remove(const QList& localLocations) override; KDevelop::VcsJob* repositoryLocation(const QUrl& localLocation) override; KDevelop::VcsJob* resolve(const QList& localLocations, RecursionMode recursio=Recursive) override; KDevelop::VcsJob* revert(const QList& localLocations, RecursionMode recursion=Recursive) override; KDevelop::VcsJob* status(const QList& localLocations, RecursionMode recursion=Recursive) override; KDevelop::VcsJob* update(const QList& localLocations, const KDevelop::VcsRevision& rev, RecursionMode recursion=Recursive) override; KDevelop::VcsLocationWidget* vcsLocation(QWidget* parent) const override; KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context* context) override; private slots: void parseBzrStatus(KDevelop::DVcsJob* job); void parseBzrLog(KDevelop::DVcsJob* job); void parseBzrRoot(KDevelop::DVcsJob* job); private: KDevelop::VcsPluginHelper* m_vcsPluginHelper; }; #endif // BAZAAR_BAZAARPLUGIN_H diff --git a/plugins/bazaar/bazaarutils.cpp b/plugins/bazaar/bazaarutils.cpp index 527bd2cf4..418d742a9 100644 --- a/plugins/bazaar/bazaarutils.cpp +++ b/plugins/bazaar/bazaarutils.cpp @@ -1,244 +1,244 @@ /*************************************************************************** * Copyright 2013-2014 Maciej Poleski * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #include "bazaarutils.h" -#include -#include +#include +#include #include #include #include QDir BazaarUtils::toQDir(const QUrl& url) { return QDir(url.toLocalFile()); } QDir BazaarUtils::workingCopy(const QUrl& path) { QDir dir = BazaarUtils::toQDir(path); while (!dir.exists(QStringLiteral(".bzr")) && dir.cdUp()); return dir; } QString BazaarUtils::getRevisionSpec(const KDevelop::VcsRevision& revision) { if (revision.revisionType() == KDevelop::VcsRevision::Special) { if (revision.specialType() == KDevelop::VcsRevision::Head) return QStringLiteral("-rlast:1"); else if (revision.specialType() == KDevelop::VcsRevision::Base) return QString(); // Workaround strange KDevelop behaviour else if (revision.specialType() == KDevelop::VcsRevision::Working) return QString(); else if (revision.specialType() == KDevelop::VcsRevision::Start) return QStringLiteral("-r1"); else return QString(); // Don't know how to handle this situation } else if (revision.revisionType() == KDevelop::VcsRevision::GlobalNumber) return QStringLiteral("-r") + QString::number(revision.revisionValue().toLongLong()); else return QString(); // Don't know how to handle this situation } QString BazaarUtils::getRevisionSpecRange(const KDevelop::VcsRevision& end) { if (end.revisionType() == KDevelop::VcsRevision::Special) { if (end.specialType() == KDevelop::VcsRevision::Head) { return QStringLiteral("-r..last:1"); } else if (end.specialType() == KDevelop::VcsRevision::Base) { return QStringLiteral("-r..last:1"); // Workaround strange KDevelop behaviour } else if (end.specialType() == KDevelop::VcsRevision::Working) { return QString(); } else if (end.specialType() == KDevelop::VcsRevision::Start) { return QStringLiteral("-..r1"); } else { return QString(); // Don't know how to handle this situation } } else if (end.revisionType() == KDevelop::VcsRevision::GlobalNumber) { return QStringLiteral("-r") + QString::number(end.revisionValue().toLongLong()); } return QString(); // Don't know how to handle this situation } QString BazaarUtils::getRevisionSpecRange(const KDevelop::VcsRevision& begin, const KDevelop::VcsRevision& end) { if (begin.revisionType() == KDevelop::VcsRevision::Special) { if (begin.specialType() == KDevelop::VcsRevision::Previous) { if (end.revisionType() == KDevelop::VcsRevision::Special) { if (end.specialType() == KDevelop::VcsRevision::Base || end.specialType() == KDevelop::VcsRevision::Head) return QStringLiteral("-rlast:2..last:1"); else if (end.specialType() == KDevelop::VcsRevision::Working) return QString(); else if (end.specialType() == KDevelop::VcsRevision::Start) return QStringLiteral("-r0..1"); // That's wrong revision range } else if (end.revisionType() == KDevelop::VcsRevision::GlobalNumber) return QStringLiteral("-r") + QString::number(end.revisionValue().toLongLong() - 1) + ".." + QString::number(end.revisionValue().toLongLong()); else return QString(); // Don't know how to handle this situation } else if (begin.specialType() == KDevelop::VcsRevision::Base || begin.specialType() == KDevelop::VcsRevision::Head) { // Only one possibility: comparing working copy to last commit return QString(); } } else if (begin.revisionType() == KDevelop::VcsRevision::GlobalNumber) { if (end.revisionType() == KDevelop::VcsRevision::Special) { // Assuming working copy return QStringLiteral("-r") + QString::number(begin.revisionValue().toLongLong()); } else { return QStringLiteral("-r") + QString::number(begin.revisionValue().toLongLong()) + ".." + QString::number(end.revisionValue().toLongLong()); } } return QString(); // Don't know how to handle this situation } bool BazaarUtils::isValidDirectory(const QUrl& dirPath) { QDir dir = BazaarUtils::workingCopy(dirPath); return dir.cd(QStringLiteral(".bzr")) && dir.exists(QStringLiteral("branch")); } KDevelop::VcsStatusInfo BazaarUtils::parseVcsStatusInfoLine(const QString& line) { QStringList tokens = line.split(' ', QString::SkipEmptyParts); KDevelop::VcsStatusInfo result; if (tokens.size() < 2) // Don't know how to handle this situation (it is an error) return result; result.setUrl(QUrl::fromLocalFile(tokens.back())); if (tokens[0] == QLatin1String("M")) { result.setState(KDevelop::VcsStatusInfo::ItemModified); } else if (tokens[0] == QLatin1String("C")) { result.setState(KDevelop::VcsStatusInfo::ItemHasConflicts); } else if (tokens[0] == QLatin1String("+N")) { result.setState(KDevelop::VcsStatusInfo::ItemAdded); } else if (tokens[0] == QLatin1String("?")) { result.setState(KDevelop::VcsStatusInfo::ItemUnknown); } else if (tokens[0] == QLatin1String("D")) { result.setState(KDevelop::VcsStatusInfo::ItemDeleted); } else { result.setState(KDevelop::VcsStatusInfo::ItemUserState); qWarning() << "Unsupported status: " << tokens[0]; } return result; } QString BazaarUtils::concatenatePath(const QDir& workingCopy, const QUrl& pathInWorkingCopy) { return QFileInfo(workingCopy.absolutePath() + QDir::separator() + pathInWorkingCopy.toLocalFile()).absoluteFilePath(); } KDevelop::VcsEvent BazaarUtils::parseBzrLogPart(const QString& output) { const QStringList outputLines = output.split('\n'); KDevelop::VcsEvent commitInfo; bool atMessage = false; QString message; bool afterMessage = false; QHash fileToActionsMapping; KDevelop::VcsItemEvent::Action currentAction; for (const QString &line : outputLines) { if (!atMessage) { if (line.startsWith(QStringLiteral("revno"))) { QString revno = line.mid(QStringLiteral("revno: ").length()); revno = revno.left(revno.indexOf(' ')); KDevelop::VcsRevision revision; revision.setRevisionValue(revno.toLongLong(), KDevelop::VcsRevision::GlobalNumber); commitInfo.setRevision(revision); } else if (line.startsWith(QStringLiteral("committer: "))) { QString commiter = line.mid(QStringLiteral("committer: ").length()); commitInfo.setAuthor(commiter); // Author goes after commiter, but only if is different } else if (line.startsWith(QStringLiteral("author"))) { QString author = line.mid(QStringLiteral("author: ").length()); commitInfo.setAuthor(author); // It may override commiter (In fact commiter is not supported by VcsEvent) } else if (line.startsWith(QStringLiteral("timestamp"))) { const QString formatString = QStringLiteral("yyyy-MM-dd hh:mm:ss"); QString timestamp = line.mid(QStringLiteral("timestamp: ddd ").length(), formatString.length()); commitInfo.setDate(QDateTime::fromString(timestamp, formatString)); } else if (line.startsWith(QStringLiteral("message"))) { atMessage = true; } } else if (atMessage && !afterMessage) { if (!line.isEmpty() && line[0].isSpace()) { message += line.trimmed() + "\n"; } else if (!line.isEmpty()) { afterMessage = true; // leave atMessage = true currentAction = BazaarUtils::parseActionDescription(line); } // if line is empty - ignore and get next } else if (afterMessage) { if (!line.isEmpty() && !line[0].isSpace()) { currentAction = BazaarUtils::parseActionDescription(line); } else if (!line.isEmpty()) { fileToActionsMapping[line.trimmed()] |= currentAction; } // if line is empty - ignore and get next } } if (atMessage) commitInfo.setMessage(message.trimmed()); for (auto i = fileToActionsMapping.begin(); i != fileToActionsMapping.end(); ++i) { KDevelop::VcsItemEvent itemEvent; itemEvent.setRepositoryLocation(i.key()); itemEvent.setActions(i.value()); commitInfo.addItem(itemEvent); } return commitInfo; } KDevelop::VcsItemEvent::Action BazaarUtils::parseActionDescription(const QString& action) { if (action == QLatin1String("added:")) { return KDevelop::VcsItemEvent::Added; } else if (action == QLatin1String("modified:")) { return KDevelop::VcsItemEvent::Modified; } else if (action == QLatin1String("removed:")) { return KDevelop::VcsItemEvent::Deleted; } else if (action == QLatin1String("kind changed:")) { return KDevelop::VcsItemEvent::Replaced; // Best approximation } else if (action.startsWith(QStringLiteral("renamed"))) { return KDevelop::VcsItemEvent::Modified; // Best approximation } else { qCritical("Unsupported action: %s", action.toLocal8Bit().constData()); return KDevelop::VcsItemEvent::Action(); } } QList BazaarUtils::handleRecursion(const QList& listOfUrls, KDevelop::IBasicVersionControl::RecursionMode recursion) { if (recursion == KDevelop::IBasicVersionControl::Recursive) { return listOfUrls; // Nothing to do } else { QList result; foreach (const auto& url, listOfUrls) { if (url.isLocalFile() && QFileInfo(url.toLocalFile()).isFile()) { result.push_back(url); } } return result; } } diff --git a/plugins/bazaar/bazaarutils.h b/plugins/bazaar/bazaarutils.h index ea717db9c..9318893d4 100644 --- a/plugins/bazaar/bazaarutils.h +++ b/plugins/bazaar/bazaarutils.h @@ -1,116 +1,116 @@ /*************************************************************************** * Copyright 2013-2014 Maciej Poleski * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #ifndef BAZAAR_BAZAARUTILS_H #define BAZAAR_BAZAARUTILS_H -#include +#include #include #include #include namespace KDevelop { class VcsRevision; class VcsStatusInfo; class VcsEvent; } namespace BazaarUtils { /** * Converts @p url to \code QDir \endcode instance assuming file is local */ QDir toQDir(const QUrl& url); /** * @return working copy location of working copy which contains @p path. */ QDir workingCopy(const QUrl& path); /** * Translate VcsRevision into Revision Identifier accepted by Bazaar. This * function is designed for translating signle revision ids. */ QString getRevisionSpec(const KDevelop::VcsRevision& revision); /** * Translate VcsRevision into revision range option accepted by Bazaar. This * function is designed for translating end point into range from begin to * given end point. */ QString getRevisionSpecRange(const KDevelop::VcsRevision& end); /** * Translate VcsRevision range into revision range option accepted by Bazaar. * This function translates VcsRevision range into best possible approximation * of revision range accepted by Bazaar. */ QString getRevisionSpecRange(const KDevelop::VcsRevision& begin, const KDevelop::VcsRevision& end); /** * Checks if @p dirPath is valid working directory location. * @return true if @p dirPath is valid working directory location, false * otherwise. */ bool isValidDirectory(const QUrl& dirPath); /** * Parses signle status info line to \code KDevelop::VcsStatusInfo \endcode. */ KDevelop::VcsStatusInfo parseVcsStatusInfoLine(const QString& line); /** * Concatenates path of working copy location and relative file in working copy * location (@p pathInWorkingCopy) and returns absolute path of this file. * @return Absolute path of file designated by @p pathInWorkingCopy relative to * @p workingCopy. */ QString concatenatePath(const QDir& workingCopy, const QUrl& pathInWorkingCopy); /** * Parses informations about single commit from @p action (which is signle part * of \code bzr log \endcode output). * @return Informations about single commit in instance of * \code KDevelop::VcsEvent \endcode class. */ KDevelop::VcsEvent parseBzrLogPart(const QString& output); /** * Parses information about single action on single file in some * \code bzr log \endcode output. */ KDevelop::VcsItemEvent::Action parseActionDescription(const QString& action); /** * Some methods in interface provides @p recursion parameter. In general * Bazaar don't support this (only part of interface has native recursion * handling support). This function removes directiories from list if * we are in NonRecursive mode (as directory for self is not versioned). */ QList handleRecursion(const QList& listOfUrls, KDevelop::IBasicVersionControl::RecursionMode recursion); } #endif // BAZAAR_BAZAARUTILS_H diff --git a/plugins/bazaar/bzrannotatejob.cpp b/plugins/bazaar/bzrannotatejob.cpp index bbbaaa0a2..2ad8f4358 100644 --- a/plugins/bazaar/bzrannotatejob.cpp +++ b/plugins/bazaar/bzrannotatejob.cpp @@ -1,197 +1,197 @@ /*************************************************************************** * Copyright 2013-2014 Maciej Poleski * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #include "bzrannotatejob.h" #include -#include -#include -#include +#include +#include +#include #include #include #include #include using namespace KDevelop; BzrAnnotateJob::BzrAnnotateJob(const QDir& workingDir, const QString& revisionSpec, const QUrl& localLocation, KDevelop::IPlugin* parent, KDevelop::OutputJob::OutputJobVerbosity verbosity) : VcsJob(parent, verbosity), m_workingDir(workingDir), m_revisionSpec(revisionSpec), m_localLocation(localLocation), m_vcsPlugin(parent), m_status(KDevelop::VcsJob::JobNotStarted) { setType(JobType::Annotate); setCapabilities(Killable); } bool BzrAnnotateJob::doKill() { m_status = KDevelop::VcsJob::JobCanceled; if (m_job) return m_job->kill(KJob::Quietly); else return true; } void BzrAnnotateJob::start() { if (m_status != KDevelop::VcsJob::JobNotStarted) return; DVcsJob* job = new KDevelop::DVcsJob(m_workingDir, m_vcsPlugin, KDevelop::OutputJob::Silent); *job << "bzr" << "annotate" << "--all" << m_revisionSpec << m_localLocation; connect(job, &DVcsJob::readyForParsing, this, &BzrAnnotateJob::parseBzrAnnotateOutput); m_status = VcsJob::JobRunning; m_job = job; job->start(); } void BzrAnnotateJob::parseBzrAnnotateOutput(KDevelop::DVcsJob* job) { m_outputLines = job->output().split('\n'); m_currentLine = 0; if (m_status == KDevelop::VcsJob::JobRunning) QTimer::singleShot(0, this, SLOT(parseNextLine())); } void BzrAnnotateJob::parseNextLine() { for(;;) { Q_ASSERT(m_currentLine<=m_outputLines.size()); if (m_currentLine == m_outputLines.size()) { m_status = KDevelop::VcsJob::JobSucceeded; emitResult(); emit resultsReady(this); break; } QString currentLine = m_outputLines[m_currentLine]; if (currentLine.isEmpty()) { ++m_currentLine; continue; } bool revOk; auto revision = currentLine.leftRef(currentLine.indexOf(' ')).toULong(&revOk); if (!revOk) { // Future compatibility - not a revision yet ++m_currentLine; continue; } auto i = m_commits.find(revision); if (i != m_commits.end()) { KDevelop::VcsAnnotationLine line; line.setAuthor(i.value().author()); line.setCommitMessage(i.value().message()); line.setDate(i.value().date()); line.setLineNumber(m_currentLine); line.setRevision(i.value().revision()); m_results.append(QVariant::fromValue(line)); ++m_currentLine; continue; } else { prepareCommitInfo(revision); break; //Will reenter this function when commit info will be ready } } } void BzrAnnotateJob::prepareCommitInfo(std::size_t revision) { if (m_status != KDevelop::VcsJob::JobRunning) return; KDevelop::DVcsJob* job = new KDevelop::DVcsJob(m_workingDir, m_vcsPlugin, KDevelop::OutputJob::Silent); job->setType(KDevelop::VcsJob::Log); *job << "bzr" << "log" << "--long" << "-r" << QString::number(revision); connect(job, &DVcsJob::readyForParsing, this, &BzrAnnotateJob::parseBzrLog); m_job = job; job->start(); } /* * This is slightly different from BazaarUtils::parseBzrLogPart(...). * This function parses only commit general info. It does not parse signle * actions. In fact output parsed by this function is slightly different * from output parsed by BazaarUtils. As a result parsing this output using * BazaarUtils would yield different results. * NOTE: This is all about parsing 'message'. */ void BzrAnnotateJob::parseBzrLog(KDevelop::DVcsJob* job) { QStringList outputLines = job->output().split('\n'); KDevelop::VcsEvent commitInfo; int revision=-1; bool atMessage = false; QString message; foreach (const QString &line, outputLines) { if (!atMessage) { if (line.startsWith(QStringLiteral("revno"))) { QString revno = line.mid(QStringLiteral("revno: ").length()); // In future there is possibility that "revno: " will change to // "revno??". If that's all, then we recover matching only // "revno" prefix and assuming placeholder of length 2 (": " or // "??"). // The same below with exception of "commiter" which possibly // can have also some suffix which changes meaning like // "commiter-some_property: "... revno = revno.left(revno.indexOf(' ')); revision = revno.toInt(); KDevelop::VcsRevision revision; revision.setRevisionValue(revno.toLongLong(), KDevelop::VcsRevision::GlobalNumber); commitInfo.setRevision(revision); } else if (line.startsWith(QStringLiteral("committer: "))) { QString commiter = line.mid(QStringLiteral("committer: ").length()); commitInfo.setAuthor(commiter); // Author goes after commiter, but only if is different } else if (line.startsWith(QStringLiteral("author"))) { QString author = line.mid(QStringLiteral("author: ").length()); commitInfo.setAuthor(author); // It may override commiter (In fact commiter is not supported by VcsEvent) } else if (line.startsWith(QStringLiteral("timestamp"))) { const QString formatString = QStringLiteral("yyyy-MM-dd hh:mm:ss"); QString timestamp = line.mid(QStringLiteral("timestamp: ddd ").length(), formatString.length()); commitInfo.setDate(QDateTime::fromString(timestamp, formatString)); } else if (line.startsWith(QStringLiteral("message"))) { atMessage = true; } } else { message += line.trimmed() + "\n"; } } if (atMessage) commitInfo.setMessage(message.trimmed()); Q_ASSERT(revision!=-1); m_commits[revision] = commitInfo; // Invoke from event loop to protect against stack overflow (it could happen // on very big files with very big history of changes if tail-recursion // optimization had failed here). QTimer::singleShot(0, this, SLOT(parseNextLine())); } QVariant BzrAnnotateJob::fetchResults() { return m_results; } KDevelop::VcsJob::JobStatus BzrAnnotateJob::status() const { return m_status; } KDevelop::IPlugin* BzrAnnotateJob::vcsPlugin() const { return m_vcsPlugin; } diff --git a/plugins/bazaar/bzrannotatejob.h b/plugins/bazaar/bzrannotatejob.h index 0d5c26890..1b1b9a98a 100644 --- a/plugins/bazaar/bzrannotatejob.h +++ b/plugins/bazaar/bzrannotatejob.h @@ -1,74 +1,74 @@ /*************************************************************************** * Copyright 2013-2014 Maciej Poleski * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #ifndef BAZAAR_BZRANNOTATEJOB_H #define BAZAAR_BZRANNOTATEJOB_H -#include -#include -#include +#include +#include +#include #include #include #include class QDir; namespace KDevelop { class DVcsJob; } class BzrAnnotateJob : public KDevelop::VcsJob { Q_OBJECT public: explicit BzrAnnotateJob(const QDir& workingDir, const QString& revisionSpec, const QUrl& localLocation, KDevelop::IPlugin* parent = nullptr, OutputJobVerbosity verbosity = OutputJob::Verbose); QVariant fetchResults() override; void start() override; JobStatus status() const override; KDevelop::IPlugin* vcsPlugin() const override; protected: bool doKill() override; private slots: void parseBzrAnnotateOutput(KDevelop::DVcsJob* job); void parseNextLine(); void prepareCommitInfo(std::size_t revision); void parseBzrLog(KDevelop::DVcsJob* job); private: QDir m_workingDir; QString m_revisionSpec; QUrl m_localLocation; KDevelop::IPlugin* m_vcsPlugin; JobStatus m_status; QPointer m_job; QStringList m_outputLines; int m_currentLine; QHash m_commits; QVariantList m_results; }; #endif // BAZAAR_BZRANNOTATEJOB_H diff --git a/plugins/bazaar/copyjob.cpp b/plugins/bazaar/copyjob.cpp index 0763e4ef3..1ce216aa2 100644 --- a/plugins/bazaar/copyjob.cpp +++ b/plugins/bazaar/copyjob.cpp @@ -1,99 +1,99 @@ /*************************************************************************** * Copyright 2013-2014 Maciej Poleski * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #include "copyjob.h" -#include +#include #include #include #include "bazaarplugin.h" using namespace KDevelop; CopyJob::CopyJob(const QUrl& localLocationSrc, const QUrl& localLocationDstn, BazaarPlugin* parent, KDevelop::OutputJob::OutputJobVerbosity verbosity) : VcsJob(parent, verbosity), m_plugin(parent), m_source(localLocationSrc), m_destination(localLocationDstn), m_status(KDevelop::VcsJob::JobNotStarted) { setType(JobType::Copy); setCapabilities(Killable); } KDevelop::IPlugin* CopyJob::vcsPlugin() const { return m_plugin; } KDevelop::VcsJob::JobStatus CopyJob::status() const { return m_status; } QVariant CopyJob::fetchResults() { return QVariant(); } void CopyJob::start() { if (m_status != KDevelop::VcsJob::JobNotStarted) return; KIO::CopyJob* job = KIO::copy(m_source, m_destination, KIO::HideProgressInfo); connect(job, &KIO::CopyJob::copyingDone, this, &CopyJob::addToVcs); m_status = KDevelop::VcsJob::JobRunning; m_job = job; job->start(); } bool CopyJob::doKill() { m_status = KDevelop::VcsJob::JobCanceled; if (m_job) return m_job->kill(KJob::Quietly); else return true; } void CopyJob::addToVcs(KIO::Job* job, const QUrl& from, const QUrl& to, const QDateTime& mtime, bool directory, bool renamed) { Q_UNUSED(job); Q_UNUSED(from); Q_UNUSED(mtime); Q_UNUSED(directory); Q_UNUSED(renamed); if (m_status != KDevelop::VcsJob::JobRunning) return; KDevelop::VcsJob* job2 = m_plugin->add(QList() << to, KDevelop::IBasicVersionControl::Recursive); connect(job2, &VcsJob::result, this, &CopyJob::finish); m_job = job2; job2->start(); } void CopyJob::finish(KJob*) { m_status = KDevelop::VcsJob::JobSucceeded; emitResult(); emit resultsReady(this); } diff --git a/plugins/bazaar/diffjob.cpp b/plugins/bazaar/diffjob.cpp index 0788a4c81..a36dab4b8 100644 --- a/plugins/bazaar/diffjob.cpp +++ b/plugins/bazaar/diffjob.cpp @@ -1,96 +1,96 @@ /*************************************************************************** * Copyright 2013-2014 Maciej Poleski * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #include "diffjob.h" -#include +#include #include #include #include "bazaarplugin.h" using namespace KDevelop; DiffJob::DiffJob(const QDir& workingDir, const QString& revisionSpecRange, const QUrl& fileOrDirectory, BazaarPlugin* parent, KDevelop::OutputJob::OutputJobVerbosity verbosity) : VcsJob(parent, verbosity), m_plugin(parent), m_status(KDevelop::VcsJob::JobNotStarted) { setType(KDevelop::VcsJob::Diff); setCapabilities(Killable); m_job = new KDevelop::DVcsJob(workingDir, parent, verbosity); m_job->setType(VcsJob::Diff); *m_job << "bzr" << "diff" << "-p1" << revisionSpecRange << fileOrDirectory; } KDevelop::IPlugin* DiffJob::vcsPlugin() const { return m_plugin; } KDevelop::VcsJob::JobStatus DiffJob::status() const { return m_status; } QVariant DiffJob::fetchResults() { return m_result; } void DiffJob::start() { if (m_status != KDevelop::VcsJob::JobNotStarted) return; if (m_job) { connect(m_job.data(), &DVcsJob::finished, this, &DiffJob::prepareResult); m_status = KDevelop::VcsJob::JobRunning; m_job->start(); } } bool DiffJob::doKill() { m_status = KDevelop::VcsJob::JobCanceled; if (m_job) return m_job->kill(KJob::Quietly); else return true; } void DiffJob::prepareResult(KJob*) { if (m_job->process()->exitStatus() == QProcess::NormalExit) { KDevelop::VcsDiff diff; diff.setDiff(m_job->output()); diff.setBaseDiff(QUrl::fromLocalFile(m_job->directory().absolutePath())); m_result.setValue(diff); m_status = KDevelop::VcsJob::JobSucceeded; } else { setError(m_job->process()->exitStatus()); m_status = KDevelop::VcsJob::JobFailed; } emitResult(); emit resultsReady(this); } diff --git a/plugins/bazaar/diffjob.h b/plugins/bazaar/diffjob.h index 9ac06e766..87becaaed 100644 --- a/plugins/bazaar/diffjob.h +++ b/plugins/bazaar/diffjob.h @@ -1,62 +1,62 @@ /*************************************************************************** * Copyright 2013-2014 Maciej Poleski * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #ifndef BAZAAR_DIFFJOB_H #define BAZAAR_DIFFJOB_H -#include +#include #include namespace KDevelop { class DVcsJob; } class QDir; class BazaarPlugin; class DiffJob : public KDevelop::VcsJob { Q_OBJECT public: DiffJob(const QDir& workingDir, const QString& revisionSpecRange, const QUrl& fileOrDirectory, BazaarPlugin* parent = nullptr, OutputJobVerbosity verbosity = OutputJob::Silent); KDevelop::IPlugin* vcsPlugin() const override; KDevelop::VcsJob::JobStatus status() const override; QVariant fetchResults() override; void start() override; protected: bool doKill() override; private slots: void prepareResult(KJob*); private: BazaarPlugin* m_plugin; QVariant m_result; JobStatus m_status; QPointer m_job; }; #endif // BAZAAR_DIFFJOB_H diff --git a/plugins/bazaar/tests/test_bazaar.cpp b/plugins/bazaar/tests/test_bazaar.cpp index 7ebbd6ceb..c6b1355e1 100644 --- a/plugins/bazaar/tests/test_bazaar.cpp +++ b/plugins/bazaar/tests/test_bazaar.cpp @@ -1,362 +1,362 @@ /*************************************************************************** * This file was partly taken from KDevelop's cvs plugin * * Copyright 2007 Robert Gruber * * * * Adapted for Git * * Copyright 2008 Evgeniy Ivanov * * * * Adapted for Bazaar * * Copyright 2014 Maciej Poleski * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #include "test_bazaar.h" #include #include -#include -#include +#include +#include #include -#include +#include #include #include #include #include "../bazaarplugin.h" #include #define VERIFYJOB(j) \ do { QVERIFY(j); QVERIFY(j->exec()); QVERIFY((j)->status() == KDevelop::VcsJob::JobSucceeded); } while(0) const QString tempDir = QDir::tempPath(); const QString bazaarTest_BaseDir(tempDir + "/kdevBazaar_testdir/"); const QString bazaarTest_BaseDir2(tempDir + "/kdevBazaar_testdir2/"); const QString bazaarRepo(bazaarTest_BaseDir + ".bzr"); const QString bazaarSrcDir(bazaarTest_BaseDir + "src/"); const QString bazaarTest_FileName("testfile"); const QString bazaarTest_FileName2("foo"); const QString bazaarTest_FileName3("bar"); using namespace KDevelop; void TestBazaar::initTestCase() { AutoTestShell::init({QStringLiteral("kdevbazaar")}); TestCore::initialize(Core::NoUi); m_plugin = new BazaarPlugin(TestCore::self()); } void TestBazaar::cleanupTestCase() { delete m_plugin; TestCore::shutdown(); } void TestBazaar::init() { // Now create the basic directory structure QDir tmpdir(tempDir); tmpdir.mkdir(bazaarTest_BaseDir); tmpdir.mkdir(bazaarSrcDir); tmpdir.mkdir(bazaarTest_BaseDir2); } void TestBazaar::cleanup() { removeTempDirs(); } void TestBazaar::repoInit() { qDebug() << "Trying to init repo"; // make job that creates the local repository VcsJob* j = m_plugin->init(QUrl::fromLocalFile(bazaarTest_BaseDir)); VERIFYJOB(j); //check if the .bzr directory in the new local repository exists now QVERIFY(QFileInfo::exists(bazaarRepo)); } void TestBazaar::addFiles() { qDebug() << "Adding files to the repo"; //we start it after repoInit, so we still have empty bazaar repo QFile f(bazaarTest_BaseDir + bazaarTest_FileName); if (f.open(QIODevice::WriteOnly)) { QTextStream input(&f); input << "HELLO WORLD"; } f.close(); f.setFileName(bazaarTest_BaseDir + bazaarTest_FileName2); if (f.open(QIODevice::WriteOnly)) { QTextStream input(&f); input << "No, bar()!"; } f.close(); //test bzr-status exitCode (see DVcsJob::setExitCode). VcsJob* j = m_plugin->status(QList() << QUrl::fromLocalFile(bazaarTest_BaseDir)); VERIFYJOB(j); // /tmp/kdevBazaar_testdir/ and testfile j = m_plugin->add(QList() << QUrl::fromLocalFile(bazaarTest_BaseDir + bazaarTest_FileName)); VERIFYJOB(j); f.setFileName(bazaarSrcDir + bazaarTest_FileName3); if (f.open(QIODevice::WriteOnly)) { QTextStream input(&f); input << "No, foo()! It's bar()!"; } f.close(); //test bzr-status exitCode again j = m_plugin->status(QList() << QUrl::fromLocalFile(bazaarTest_BaseDir)); VERIFYJOB(j); //repository path without trailing slash and a file in a parent directory // /tmp/repo and /tmp/repo/src/bar j = m_plugin->add(QList() << QUrl::fromLocalFile(bazaarSrcDir + bazaarTest_FileName3)); VERIFYJOB(j); //let's use absolute path, because it's used in ContextMenus j = m_plugin->add(QList() << QUrl::fromLocalFile(bazaarTest_BaseDir + bazaarTest_FileName2)); VERIFYJOB(j); //Now let's create several files and try "bzr add file1 file2 file3" QStringList files = QStringList() << "file1" << "file2" << "la la"; QList multipleFiles; foreach(const QString& file, files) { QFile f(bazaarTest_BaseDir + file); QVERIFY(f.open(QIODevice::WriteOnly)); QTextStream input(&f); input << file; f.close(); multipleFiles << QUrl::fromLocalFile(bazaarTest_BaseDir + file); } j = m_plugin->add(multipleFiles); VERIFYJOB(j); } void TestBazaar::prepareWhoamiInformations() { DVcsJob *whoamiJob = new DVcsJob(bazaarTest_BaseDir, m_plugin); *whoamiJob<<"bzr"<<"whoami"<<"--branch"<<"kdevbazaar-test identity <>"; VERIFYJOB(whoamiJob); } void TestBazaar::commitFiles() { prepareWhoamiInformations(); qDebug() << "Committing..."; //we start it after addFiles, so we just have to commit VcsJob* j = m_plugin->commit(QStringLiteral("Test commit"), QList() << QUrl::fromLocalFile(bazaarTest_BaseDir)); VERIFYJOB(j); //test bzr-status exitCode one more time. j = m_plugin->status(QList() << QUrl::fromLocalFile(bazaarTest_BaseDir)); VERIFYJOB(j); //since we committed the file to the "pure" repository, .bzr/repository/indices should exist //TODO: maybe other method should be used QString headRefName(bazaarRepo + "/repository/indices"); QVERIFY(QFileInfo::exists(headRefName)); //Test the results of the "bzr add" DVcsJob* jobLs = new DVcsJob(bazaarTest_BaseDir, m_plugin); *jobLs << "bzr" << "ls" << "-R"; if (jobLs->exec() && jobLs->status() == KDevelop::VcsJob::JobSucceeded) { QStringList files = jobLs->output().split('\n'); QVERIFY(files.contains(bazaarTest_FileName)); QVERIFY(files.contains(bazaarTest_FileName2)); QVERIFY(files.contains("src/" + bazaarTest_FileName3)); } qDebug() << "Committing one more time"; //let's try to change the file and test "bzr commit" QFile f(bazaarTest_BaseDir + bazaarTest_FileName); if (f.open(QIODevice::WriteOnly)) { QTextStream input(&f); input << "Just another HELLO WORLD\n"; } f.close(); //add changes j = m_plugin->add(QList() << QUrl::fromLocalFile(bazaarTest_BaseDir + bazaarTest_FileName)); VERIFYJOB(j); j = m_plugin->commit(QStringLiteral("KDevelop's Test commit2"), QList() << QUrl::fromLocalFile(bazaarTest_BaseDir)); VERIFYJOB(j); } void TestBazaar::testInit() { repoInit(); } void TestBazaar::testAdd() { repoInit(); addFiles(); } void TestBazaar::testCommit() { repoInit(); addFiles(); commitFiles(); } void TestBazaar::testAnnotation() { repoInit(); addFiles(); commitFiles(); // called after commitFiles QFile f(bazaarTest_BaseDir + bazaarTest_FileName); QVERIFY(f.open(QIODevice::Append)); QTextStream input(&f); input << "An appended line"; f.close(); VcsJob* j = m_plugin->commit(QStringLiteral("KDevelop's Test commit3"), QList() << QUrl::fromLocalFile(bazaarTest_BaseDir)); VERIFYJOB(j); j = m_plugin->annotate(QUrl::fromLocalFile(bazaarTest_BaseDir + bazaarTest_FileName), VcsRevision::createSpecialRevision(VcsRevision::Head)); VERIFYJOB(j); QList results = j->fetchResults().toList(); QCOMPARE(results.size(), 2); QVERIFY(results.at(0).canConvert()); VcsAnnotationLine annotation = results.at(0).value(); QCOMPARE(annotation.lineNumber(), 0); QCOMPARE(annotation.commitMessage(), QStringLiteral("KDevelop's Test commit2")); QVERIFY(results.at(1).canConvert()); annotation = results.at(1).value(); QCOMPARE(annotation.lineNumber(), 1); QCOMPARE(annotation.commitMessage(), QStringLiteral("KDevelop's Test commit3")); } void TestBazaar::testRemoveEmptyFolder() { repoInit(); QDir d(bazaarTest_BaseDir); d.mkdir("emptydir"); VcsJob* j = m_plugin->remove(QList() << QUrl::fromLocalFile(bazaarTest_BaseDir+"emptydir/")); if (j) VERIFYJOB(j); QVERIFY(!d.exists("emptydir")); } void TestBazaar::testRemoveEmptyFolderInFolder() { repoInit(); QDir d(bazaarTest_BaseDir); d.mkdir("dir"); QDir d2(bazaarTest_BaseDir+"dir"); d2.mkdir("emptydir"); VcsJob* j = m_plugin->remove(QList() << QUrl::fromLocalFile(bazaarTest_BaseDir+"dir/")); if (j) VERIFYJOB(j); QVERIFY(!d.exists("dir")); } void TestBazaar::testRemoveUnindexedFile() { repoInit(); QFile f(bazaarTest_BaseDir + bazaarTest_FileName); QVERIFY(f.open(QIODevice::Append)); QTextStream input(&f); input << "An appended line"; f.close(); VcsJob* j = m_plugin->remove(QList() << QUrl::fromLocalFile(bazaarTest_BaseDir + bazaarTest_FileName)); if (j) VERIFYJOB(j); QVERIFY(!QFile::exists(bazaarTest_BaseDir + bazaarTest_FileName)); } void TestBazaar::testRemoveFolderContainingUnversionedFiles() { repoInit(); QDir d(bazaarTest_BaseDir); d.mkdir("dir"); { QFile f(bazaarTest_BaseDir + "dir/foo"); QVERIFY(f.open(QIODevice::Append)); QTextStream input(&f); input << "An appended line"; f.close(); } VcsJob* j = m_plugin->add(QList() << QUrl::fromLocalFile(bazaarTest_BaseDir+"dir"), IBasicVersionControl::NonRecursive); VERIFYJOB(j); prepareWhoamiInformations(); j = m_plugin->commit("initial commit", QList() << QUrl::fromLocalFile(bazaarTest_BaseDir)); VERIFYJOB(j); { QFile f(bazaarTest_BaseDir + "dir/bar"); QVERIFY(f.open(QIODevice::Append)); QTextStream input(&f); input << "An appended line"; f.close(); } j = m_plugin->remove(QList() << QUrl::fromLocalFile(bazaarTest_BaseDir + "dir")); if (j) VERIFYJOB(j); QVERIFY(!QFile::exists(bazaarTest_BaseDir + "dir")); } void TestBazaar::removeTempDirs() { if (QFileInfo::exists(bazaarTest_BaseDir)) if (!(KIO::del(QUrl::fromLocalFile(bazaarTest_BaseDir))->exec())) qDebug() << "KIO::del(" << bazaarTest_BaseDir << ") returned false"; if (QFileInfo::exists(bazaarTest_BaseDir2)) if (!(KIO::del(QUrl::fromLocalFile(bazaarTest_BaseDir2))->exec())) qDebug() << "KIO::del(" << bazaarTest_BaseDir2 << ") returned false"; } QTEST_MAIN(TestBazaar) diff --git a/plugins/bazaar/tests/test_bazaar.h b/plugins/bazaar/tests/test_bazaar.h index a555e7147..a116249cb 100644 --- a/plugins/bazaar/tests/test_bazaar.h +++ b/plugins/bazaar/tests/test_bazaar.h @@ -1,70 +1,70 @@ /*************************************************************************** * This file was partly taken from KDevelop's cvs plugin * * Copyright 2007 Robert Gruber * * * * Adapted for Git * * Copyright 2008 Evgeniy Ivanov * * * * Adapted for Bazaar * * Copyright 2014 Maciej Poleski * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #ifndef KDEVPLATFORM_PLUGIN_TEST_BAZAAR_H #define KDEVPLATFORM_PLUGIN_TEST_BAZAAR_H -#include +#include class BazaarPlugin; namespace KDevelop { class TestCore; } class TestBazaar: public QObject { Q_OBJECT private: void repoInit(); void addFiles(); void prepareWhoamiInformations(); void commitFiles(); private slots: void initTestCase(); void cleanupTestCase(); void init(); void cleanup(); void testInit(); void testAdd(); void testCommit(); void testAnnotation(); void testRemoveEmptyFolder(); void testRemoveEmptyFolderInFolder(); void testRemoveUnindexedFile(); void testRemoveFolderContainingUnversionedFiles(); private: BazaarPlugin* m_plugin; void removeTempDirs(); }; #endif // KDEVPLATFORM_PLUGIN_TEST_BAZAAR_H diff --git a/plugins/classbrowser/classbrowserplugin.h b/plugins/classbrowser/classbrowserplugin.h index 73d078c2b..133675018 100644 --- a/plugins/classbrowser/classbrowserplugin.h +++ b/plugins/classbrowser/classbrowserplugin.h @@ -1,65 +1,65 @@ /* * 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. */ #ifndef KDEVPLATFORM_PLUGIN_CLASSBROWSERPLUGIN_H #define KDEVPLATFORM_PLUGIN_CLASSBROWSERPLUGIN_H #include -#include +#include #include class ClassTree; namespace KDevelop { class Declaration; } class ClassBrowserPlugin : public KDevelop::IPlugin { Q_OBJECT public: explicit ClassBrowserPlugin(QObject *parent, const QVariantList & = QVariantList() ); ~ClassBrowserPlugin() override; void setActiveClassTree(ClassTree* a_classTree) { m_activeClassTree = a_classTree; } public: // KDevelop::Plugin overrides void unload() override; KDevelop::ContextMenuExtension contextMenuExtension( KDevelop::Context* ) override; // The duchain must not be locked when this is called! void showDefinition(KDevelop::DeclarationPointer declaration); private Q_SLOTS: void findInClassBrowser(); private: class ClassBrowserFactory* m_factory; ClassTree* m_activeClassTree; QAction* m_findInBrowser; }; #endif // KDEVPLATFORM_PLUGIN_CLASSBROWSERPLUGIN_H // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/plugins/classbrowser/classtree.h b/plugins/classbrowser/classtree.h index c406c798b..5f1d4072f 100644 --- a/plugins/classbrowser/classtree.h +++ b/plugins/classbrowser/classtree.h @@ -1,65 +1,65 @@ /* * KDevelop Class viewer * * Copyright (c) 2007 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. */ #ifndef KDEVPLATFORM_PLUGIN_CLASSTREE_H #define KDEVPLATFORM_PLUGIN_CLASSTREE_H #include -#include +#include #include #include "language/util/navigationtooltip.h" class ClassBrowserPlugin; class ClassModel; class ClassTree : public QTreeView { Q_OBJECT public: ClassTree(QWidget* parent, ClassBrowserPlugin* plugin); ~ClassTree() override; public: /// Find the given a_id in the tree and highlight it. void highlightIdentifier(KDevelop::IndexedQualifiedIdentifier a_id); static bool populatingClassBrowserContextMenu(); protected: void contextMenuEvent(QContextMenuEvent* e) override; ClassModel* model(); bool event(QEvent* event) override; private Q_SLOTS: void itemActivated(const QModelIndex& index); private: ClassBrowserPlugin* m_plugin; QPointer m_tooltip; }; #endif // kate: space-indent on; indent-width 2; tab-width: 4; replace-tabs on; auto-insert-doxygen on diff --git a/plugins/codeutils/codeutilsplugin.h b/plugins/codeutils/codeutilsplugin.h index 7cf6e959a..6049de604 100644 --- a/plugins/codeutils/codeutilsplugin.h +++ b/plugins/codeutils/codeutilsplugin.h @@ -1,43 +1,43 @@ /* * This file is part of KDevelop * Copyright 2010 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. */ #ifndef KDEVPLATFORM_PLUGIN_CODEUTILSPLUGIN_H #define KDEVPLATFORM_PLUGIN_CODEUTILSPLUGIN_H #include #include -#include +#include #include Q_DECLARE_LOGGING_CATEGORY(PLUGIN_CODEUTILS) class CodeUtilsPlugin : public KDevelop::IPlugin { Q_OBJECT public: explicit CodeUtilsPlugin( QObject *parent, const QVariantList & = QVariantList() ); ~CodeUtilsPlugin() override; private slots: void documentDeclaration(); }; #endif // KDEVPLATFORM_PLUGIN_CODEUTILSPLUGIN_H diff --git a/plugins/cvs/cvsgenericoutputview.cpp b/plugins/cvs/cvsgenericoutputview.cpp index 941d6ec05..3302f4a6b 100644 --- a/plugins/cvs/cvsgenericoutputview.cpp +++ b/plugins/cvs/cvsgenericoutputview.cpp @@ -1,49 +1,49 @@ /*************************************************************************** * Copyright 2007 Robert Gruber * * * * 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 "cvsgenericoutputview.h" #include "cvsjob.h" -#include +#include CvsGenericOutputView::CvsGenericOutputView(CvsJob* job, QWidget* parent) : QWidget(parent), Ui::CvsGenericOutputViewBase() { Ui::CvsGenericOutputViewBase::setupUi(this); if (job) { connect(job, &CvsJob::result, this, &CvsGenericOutputView::slotJobFinished); } } CvsGenericOutputView::~CvsGenericOutputView() { } void CvsGenericOutputView::appendText(const QString& text) { textArea->append(text); } void CvsGenericOutputView::slotJobFinished(KJob * job) { CvsJob * cvsjob = dynamic_cast(job); if (cvsjob) { appendText( cvsjob->cvsCommand() ); appendText( cvsjob->output() ); if (job->error() == 0) { appendText( i18n("Job exited normally") ); } else { appendText( job->errorText() ); } } } diff --git a/plugins/cvs/cvsplugin.cpp b/plugins/cvs/cvsplugin.cpp index b37980b20..0b2f7ceba 100644 --- a/plugins/cvs/cvsplugin.cpp +++ b/plugins/cvs/cvsplugin.cpp @@ -1,493 +1,493 @@ /*************************************************************************** * Copyright 2007 Robert Gruber * * * * 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 "cvsplugin.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 "cvsmainview.h" #include "cvsproxy.h" #include "cvsjob.h" #include "editorsview.h" #include "commitdialog.h" #include "cvsgenericoutputview.h" #include "checkoutdialog.h" #include "importdialog.h" #include "importmetadatawidget.h" #include "debug.h" #include #include #include Q_LOGGING_CATEGORY(PLUGIN_CVS, "kdevplatform.plugins.cvs") K_PLUGIN_FACTORY(KDevCvsFactory, registerPlugin();) // K_EXPORT_PLUGIN(KDevCvsFactory(KAboutData("kdevcvs", "kdevcvs", ki18n("CVS"), "0.1", ki18n("Support for CVS version control system"), KAboutData::License_GPL))) class KDevCvsViewFactory: public KDevelop::IToolViewFactory { public: explicit KDevCvsViewFactory(CvsPlugin *plugin): m_plugin(plugin) {} QWidget* create(QWidget *parent = nullptr) override { return new CvsMainView(m_plugin, parent); } Qt::DockWidgetArea defaultPosition() override { return Qt::BottomDockWidgetArea; } QString id() const override { return QStringLiteral("org.kdevelop.CVSView"); } private: CvsPlugin *m_plugin; }; class CvsPluginPrivate { public: explicit CvsPluginPrivate(CvsPlugin *pThis) : m_factory(new KDevCvsViewFactory(pThis)) , m_proxy(new CvsProxy(pThis)) , m_common(new KDevelop::VcsPluginHelper(pThis, pThis)) {} KDevCvsViewFactory* m_factory; QPointer m_proxy; QScopedPointer m_common; }; CvsPlugin::CvsPlugin(QObject *parent, const QVariantList &) : KDevelop::IPlugin(QStringLiteral("kdevcvs"), parent) , d(new CvsPluginPrivate(this)) { core()->uiController()->addToolView(i18n("CVS"), d->m_factory); setXMLFile(QStringLiteral("kdevcvs.rc")); setupActions(); } CvsPlugin::~CvsPlugin() { } void CvsPlugin::unload() { core()->uiController()->removeToolView( d->m_factory ); } CvsProxy* CvsPlugin::proxy() { return d->m_proxy; } void CvsPlugin::setupActions() { QAction *action; action = actionCollection()->addAction(QStringLiteral("cvs_import")); action->setText(i18n("Import Directory...")); connect(action, &QAction::triggered, this, &CvsPlugin::slotImport); action = actionCollection()->addAction(QStringLiteral("cvs_checkout")); action->setText(i18n("Checkout...")); connect(action, &QAction::triggered, this, &CvsPlugin::slotCheckout); action = actionCollection()->addAction(QStringLiteral("cvs_status")); action->setText(i18n("Status...")); connect(action, &QAction::triggered, this, &CvsPlugin::slotStatus); } const QUrl CvsPlugin::urlFocusedDocument() const { KParts::ReadOnlyPart *plugin = dynamic_cast(core()->partController()->activePart()); if (plugin) { if (plugin->url().isLocalFile()) { return plugin->url(); } } return QUrl(); } void CvsPlugin::slotImport() { QUrl url = urlFocusedDocument(); ImportDialog dlg(this, url); dlg.exec(); } void CvsPlugin::slotCheckout() { ///@todo don't use proxy directly; use interface instead CheckoutDialog dlg(this); dlg.exec(); } void CvsPlugin::slotStatus() { QUrl url = urlFocusedDocument(); QList urls; urls << url; KDevelop::VcsJob* j = status(urls, KDevelop::IBasicVersionControl::Recursive); CvsJob* job = dynamic_cast(j); if (job) { CvsGenericOutputView* view = new CvsGenericOutputView(job); emit addNewTabToMainView(view, i18n("Status")); KDevelop::ICore::self()->runController()->registerJob(job); } } KDevelop::ContextMenuExtension CvsPlugin::contextMenuExtension(KDevelop::Context* context) { d->m_common->setupFromContext(context); QList const & ctxUrlList = d->m_common->contextUrlList(); bool hasVersionControlledEntries = false; foreach(const QUrl &url, ctxUrlList) { if (d->m_proxy->isValidDirectory(url)) { hasVersionControlledEntries = true; break; } } qCDebug(PLUGIN_CVS) << "version controlled?" << hasVersionControlledEntries; if (!hasVersionControlledEntries) return IPlugin::contextMenuExtension(context); QMenu* menu = d->m_common->commonActions(); menu->addSeparator(); QAction *action; // Just add actions which are not covered by the cvscommon plugin action = new QAction(i18n("Edit"), this); connect(action, &QAction::triggered, this, &CvsPlugin::ctxEdit); menu->addAction(action); action = new QAction(i18n("Unedit"), this); connect(action, &QAction::triggered, this, &CvsPlugin::ctxUnEdit); menu->addAction(action); action = new QAction(i18n("Show Editors"), this); connect(action, &QAction::triggered, this, &CvsPlugin::ctxEditors); menu->addAction(action); KDevelop::ContextMenuExtension menuExt; menuExt.addAction(KDevelop::ContextMenuExtension::VcsGroup, menu->menuAction()); return menuExt; } void CvsPlugin::ctxEdit() { QList const & urls = d->m_common->contextUrlList(); Q_ASSERT(!urls.empty()); KDevelop::VcsJob* j = edit(urls.front()); CvsJob* job = dynamic_cast(j); if (job) { connect(job, &CvsJob::result, this, &CvsPlugin::jobFinished); KDevelop::ICore::self()->runController()->registerJob(job); } } void CvsPlugin::ctxUnEdit() { QList const & urls = d->m_common->contextUrlList(); Q_ASSERT(!urls.empty()); KDevelop::VcsJob* j = unedit(urls.front()); CvsJob* job = dynamic_cast(j); if (job) { connect(job, &CvsJob::result, this, &CvsPlugin::jobFinished); KDevelop::ICore::self()->runController()->registerJob(job); } } void CvsPlugin::ctxEditors() { QList const & urls = d->m_common->contextUrlList(); Q_ASSERT(!urls.empty()); CvsJob* job = d->m_proxy->editors(findWorkingDir(urls.front()), urls); if (job) { KDevelop::ICore::self()->runController()->registerJob(job); EditorsView* view = new EditorsView(job); emit addNewTabToMainView(view, i18n("Editors")); } } QString CvsPlugin::findWorkingDir(const QUrl& location) { QFileInfo fileInfo(location.toLocalFile()); // find out correct working directory if (fileInfo.isFile()) { return fileInfo.absolutePath(); } else { return fileInfo.absoluteFilePath(); } } // Begin: KDevelop::IBasicVersionControl bool CvsPlugin::isValidRemoteRepositoryUrl(const QUrl& remoteLocation) { Q_UNUSED(remoteLocation); // TODO return false; } bool CvsPlugin::isVersionControlled(const QUrl & localLocation) { return d->m_proxy->isVersionControlled(localLocation); } KDevelop::VcsJob * CvsPlugin::repositoryLocation(const QUrl & localLocation) { Q_UNUSED(localLocation); return nullptr; } KDevelop::VcsJob * CvsPlugin::add(const QList & localLocations, KDevelop::IBasicVersionControl::RecursionMode recursion) { CvsJob* job = d->m_proxy->add(findWorkingDir(localLocations[0]), localLocations, (recursion == KDevelop::IBasicVersionControl::Recursive) ? true : false); return job; } KDevelop::VcsJob * CvsPlugin::remove(const QList & localLocations) { CvsJob* job = d->m_proxy->remove(findWorkingDir(localLocations[0]), localLocations); return job; } KDevelop::VcsJob * CvsPlugin::localRevision(const QUrl & localLocation, KDevelop::VcsRevision::RevisionType) { Q_UNUSED(localLocation) return nullptr; } KDevelop::VcsJob * CvsPlugin::status(const QList & localLocations, KDevelop::IBasicVersionControl::RecursionMode recursion) { CvsJob* job = d->m_proxy->status(findWorkingDir(localLocations[0]), localLocations, (recursion == KDevelop::IBasicVersionControl::Recursive) ? true : false); return job; } KDevelop::VcsJob * CvsPlugin::unedit(const QUrl& localLocation) { CvsJob* job = d->m_proxy->unedit(findWorkingDir(localLocation), QList() << localLocation); return job; } KDevelop::VcsJob * CvsPlugin::edit(const QUrl& localLocation) { CvsJob* job = d->m_proxy->edit(findWorkingDir(localLocation), QList() << localLocation); return job; } KDevelop::VcsJob * CvsPlugin::copy(const QUrl & localLocationSrc, const QUrl & localLocationDstn) { bool ok = QFile::copy(localLocationSrc.toLocalFile(), localLocationDstn.path()); if (!ok) { return nullptr; } QList listDstn; listDstn << localLocationDstn; CvsJob* job = d->m_proxy->add(findWorkingDir(localLocationDstn), listDstn, true); return job; } KDevelop::VcsJob * CvsPlugin::move(const QUrl &, const QUrl &) { return nullptr; } KDevelop::VcsJob * CvsPlugin::revert(const QList & localLocations, KDevelop::IBasicVersionControl::RecursionMode recursion) { KDevelop::VcsRevision rev; CvsJob* job = d->m_proxy->update(findWorkingDir(localLocations[0]), localLocations, rev, QStringLiteral("-C"), (recursion == KDevelop::IBasicVersionControl::Recursive) ? true : false, false, false); return job; } KDevelop::VcsJob * CvsPlugin::update(const QList & localLocations, const KDevelop::VcsRevision & rev, KDevelop::IBasicVersionControl::RecursionMode recursion) { CvsJob* job = d->m_proxy->update(findWorkingDir(localLocations[0]), localLocations, rev, QString(), (recursion == KDevelop::IBasicVersionControl::Recursive) ? true : false, false, false); return job; } KDevelop::VcsJob * CvsPlugin::commit(const QString & message, const QList & localLocations, KDevelop::IBasicVersionControl::RecursionMode recursion) { Q_UNUSED(recursion); QString msg = message; if (msg.isEmpty()) { CommitDialog dlg; if (dlg.exec() == QDialog::Accepted) { msg = dlg.message(); } } CvsJob* job = d->m_proxy->commit(findWorkingDir(localLocations[0]), localLocations, msg); return job; } KDevelop::VcsJob * CvsPlugin::diff(const QUrl & fileOrDirectory, const KDevelop::VcsRevision & srcRevision, const KDevelop::VcsRevision & dstRevision, KDevelop::VcsDiff::Type, KDevelop::IBasicVersionControl::RecursionMode) { CvsJob* job = d->m_proxy->diff(fileOrDirectory, srcRevision, dstRevision, QStringLiteral("-uN")/*always unified*/); return job; } KDevelop::VcsJob * CvsPlugin::log(const QUrl & localLocation, const KDevelop::VcsRevision & rev, unsigned long limit) { Q_UNUSED(limit) CvsJob* job = d->m_proxy->log(localLocation, rev); return job; } KDevelop::VcsJob * CvsPlugin::log(const QUrl & localLocation, const KDevelop::VcsRevision & rev, const KDevelop::VcsRevision & limit) { Q_UNUSED(limit) return log(localLocation, rev, 0); } KDevelop::VcsJob * CvsPlugin::annotate(const QUrl & localLocation, const KDevelop::VcsRevision & rev) { CvsJob* job = d->m_proxy->annotate(localLocation, rev); return job; } KDevelop::VcsJob * CvsPlugin::resolve(const QList & localLocations, KDevelop::IBasicVersionControl::RecursionMode recursion) { Q_UNUSED(localLocations); Q_UNUSED(recursion); return nullptr; } KDevelop::VcsJob * CvsPlugin::import(const QString& commitMessage, const QUrl& sourceDirectory, const KDevelop::VcsLocation& destinationRepository) { if (commitMessage.isEmpty() || !sourceDirectory.isLocalFile() || !destinationRepository.isValid() || destinationRepository.type() != KDevelop::VcsLocation::RepositoryLocation) { return nullptr; } qCDebug(PLUGIN_CVS) << "CVS Import requested " << "src:" << sourceDirectory.toLocalFile() << "srv:" << destinationRepository.repositoryServer() << "module:" << destinationRepository.repositoryModule(); CvsJob* job = d->m_proxy->import(sourceDirectory, destinationRepository.repositoryServer(), destinationRepository.repositoryModule(), destinationRepository.userData().toString(), destinationRepository.repositoryTag(), commitMessage); return job; } KDevelop::VcsJob * CvsPlugin::createWorkingCopy(const KDevelop::VcsLocation & sourceRepository, const QUrl & destinationDirectory, KDevelop::IBasicVersionControl::RecursionMode recursion) { Q_UNUSED(recursion); if (!destinationDirectory.isLocalFile() || !sourceRepository.isValid() || sourceRepository.type() != KDevelop::VcsLocation::RepositoryLocation) { return nullptr; } qCDebug(PLUGIN_CVS) << "CVS Checkout requested " << "dest:" << destinationDirectory.toLocalFile() << "srv:" << sourceRepository.repositoryServer() << "module:" << sourceRepository.repositoryModule() << "branch:" << sourceRepository.repositoryBranch() << endl; CvsJob* job = d->m_proxy->checkout(destinationDirectory, sourceRepository.repositoryServer(), sourceRepository.repositoryModule(), QString(), sourceRepository.repositoryBranch(), true, true); return job; } QString CvsPlugin::name() const { return i18n("CVS"); } KDevelop::VcsImportMetadataWidget* CvsPlugin::createImportMetadataWidget(QWidget* parent) { return new ImportMetadataWidget(parent); } KDevelop::VcsLocationWidget* CvsPlugin::vcsLocation(QWidget* parent) const { return new KDevelop::StandardVcsLocationWidget(parent); } // End: KDevelop::IBasicVersionControl #include "cvsplugin.moc" diff --git a/plugins/cvs/editorsview.cpp b/plugins/cvs/editorsview.cpp index 00aba0d72..c144862fd 100644 --- a/plugins/cvs/editorsview.cpp +++ b/plugins/cvs/editorsview.cpp @@ -1,120 +1,120 @@ /*************************************************************************** * Copyright 2007 Robert Gruber * * * * 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 "editorsview.h" #include #include #include #include -#include +#include #include "cvsplugin.h" #include "cvsjob.h" #include "cvsproxy.h" EditorsView::EditorsView(CvsJob* job, QWidget *parent) : QWidget(parent), Ui::EditorsViewBase() { Ui::EditorsViewBase::setupUi(this); if (job) { connect(job, &CvsJob::result, this, &EditorsView::slotJobFinished); } } EditorsView::~EditorsView() { } void EditorsView::slotJobFinished(KJob* job) { if ( job->error() ) { textbrowser->append( i18n("Listing editors failed") ); return; } CvsJob * cvsjob = dynamic_cast(job); if (!cvsjob) { return; } QMultiMap lockedFiles; parseOutput(cvsjob->output(), lockedFiles); if (lockedFiles.size() == 0) { textbrowser->append(i18n("No files from your query are marked as being edited.")); } else { QString html; foreach (const QString &key, lockedFiles.uniqueKeys()) { html += "

"+key+"


"; foreach(const CvsLocker &item, lockedFiles.values( key )) { html += ""+i18n("User")+": "+item.user+"
"; html += ""+i18n("Date")+": "+item.date+"
"; html += ""+i18n("Machine")+": "+item.machine+"
"; html += ""+i18n("Repository")+": "+item.localrepo+"
"; html += QLatin1String("
"); } html += QLatin1String("
"); } textbrowser->setHtml( html ); } } void EditorsView::parseOutput(const QString& jobOutput, QMultiMap& editorsInfo) { // the first line contains the filename and than the locker information static QRegExp re("([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+" "([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+(.*)"); // if there are more than one locker of a single file, the second line for a file // only contains the locker information (no filename) static QRegExp subre("\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+" "([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+(.*)"); QString lastfilename; QStringList lines = jobOutput.split('\n'); for (int i=0; i * * * * 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 "test_cvs.h" -#include +#include +#include #include #include #include #include #include #include #define CVSTEST_BASEDIR "/tmp/kdevcvs_testdir/" #define CVS_REPO CVSTEST_BASEDIR "repo/" #define CVS_IMPORT CVSTEST_BASEDIR "import/" #define CVS_TESTFILE_NAME "testfile" #define CVS_CHECKOUT CVSTEST_BASEDIR "working/" // we need to add this since it is declared in cvsplugin.cpp which we don't compile here Q_LOGGING_CATEGORY(PLUGIN_CVS, "kdevplatform.plugins.cvs") void TestCvs::initTestCase() { KDevelop::AutoTestShell::init(); KDevelop::TestCore::initialize(KDevelop::Core::NoUi); m_proxy = new CvsProxy; // If the basedir for this cvs test exists from a // previous run; remove it... cleanup(); } void TestCvs::cleanupTestCase() { KDevelop::TestCore::shutdown(); delete m_proxy; } void TestCvs::init() { // Now create the basic directory structure QDir tmpdir("/tmp"); tmpdir.mkdir(CVSTEST_BASEDIR); tmpdir.mkdir(CVS_REPO); tmpdir.mkdir(CVS_IMPORT); } void TestCvs::cleanup() { if ( QFileInfo::exists(CVSTEST_BASEDIR) ) KIO::del(QUrl::fromLocalFile(QString(CVSTEST_BASEDIR)))->exec(); } void TestCvs::repoInit() { // make job that creates the local repository CvsJob* j = new CvsJob(0); QVERIFY( j ); j->setDirectory(CVSTEST_BASEDIR); *j << "cvs" << "-d" << CVS_REPO << "init"; // try to start the job QVERIFY( j->exec() ); //check if the CVSROOT directory in the new local repository exists now QVERIFY( QFileInfo::exists(QString(CVS_REPO "/CVSROOT")) ); } void TestCvs::importTestData() { // create a file so we don't import an empty dir QFile f(CVS_IMPORT "" CVS_TESTFILE_NAME); if(f.open(QIODevice::WriteOnly)) { QTextStream input( &f ); input << "HELLO WORLD"; } f.flush(); CvsJob* j = m_proxy->import(QUrl::fromLocalFile(CVS_IMPORT), CVS_REPO, "test", "vendor", "release", "test import message"); QVERIFY( j ); // try to start the job QVERIFY( j->exec() ); //check if the directory has been added to the repository QString testdir(CVS_REPO "/test"); QVERIFY( QFileInfo::exists(testdir) ); //check if the file has been added to the repository QString testfile(CVS_REPO "/test/" CVS_TESTFILE_NAME ",v"); QVERIFY( QFileInfo::exists(testfile) ); } void TestCvs::checkoutTestData() { CvsJob* j = m_proxy->checkout(QUrl::fromLocalFile(CVS_CHECKOUT), CVS_REPO, "test"); QVERIFY( j ); // try to start the job QVERIFY( j->exec() ); //check if the directory is there QString testdir(CVS_CHECKOUT); QVERIFY( QFileInfo::exists(testdir) ); //check if the file is there QString testfile(CVS_CHECKOUT "" CVS_TESTFILE_NAME); QVERIFY( QFileInfo::exists(testfile) ); } void TestCvs::testInitAndImport() { repoInit(); importTestData(); checkoutTestData(); } void TestCvs::testLogFolder() { repoInit(); importTestData(); checkoutTestData(); QString testdir(CVS_CHECKOUT); KDevelop::VcsRevision rev = KDevelop::VcsRevision::createSpecialRevision(KDevelop::VcsRevision::Head); CvsJob* job = m_proxy->log(QUrl::fromLocalFile(testdir), rev); QVERIFY(job); } QTEST_MAIN(TestCvs) diff --git a/plugins/cvs/tests/test_cvs.h b/plugins/cvs/tests/test_cvs.h index 21240cb66..ab32080c9 100644 --- a/plugins/cvs/tests/test_cvs.h +++ b/plugins/cvs/tests/test_cvs.h @@ -1,41 +1,41 @@ /*************************************************************************** * Copyright 2007 Robert Gruber * * * * 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. * * * ***************************************************************************/ #ifndef KDEVPLATFORM_PLUGIN_TEST_CVS_H #define KDEVPLATFORM_PLUGIN_TEST_CVS_H -#include +#include class CvsProxy; class TestCvs: public QObject { Q_OBJECT private: void repoInit(); void importTestData(); void checkoutTestData(); private slots: void initTestCase(); void cleanupTestCase(); void init(); void cleanup(); void testInitAndImport(); void testLogFolder(); private: CvsProxy* m_proxy; }; #endif diff --git a/plugins/documentswitcher/documentswitcherplugin.h b/plugins/documentswitcher/documentswitcherplugin.h index 675351b2c..8f5eb1a2c 100644 --- a/plugins/documentswitcher/documentswitcherplugin.h +++ b/plugins/documentswitcher/documentswitcherplugin.h @@ -1,78 +1,78 @@ /*************************************************************************** * Copyright 2009 Andreas Pakulat * * * * 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 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_PLUGIN_DOCUMENTSWITCHERPLUGIN_H #define KDEVPLATFORM_PLUGIN_DOCUMENTSWITCHERPLUGIN_H #include -#include +#include #include Q_DECLARE_LOGGING_CATEGORY(PLUGIN_DOCUMENTSWITCHER) class QStandardItemModel; namespace Sublime { class View; class MainWindow; class Area; class MainWindow; } class DocumentSwitcherTreeView; class QModelIndex; class QAction; class DocumentSwitcherPlugin: public KDevelop::IPlugin { Q_OBJECT public: explicit DocumentSwitcherPlugin( QObject *parent, const QVariantList &args = QVariantList() ); ~DocumentSwitcherPlugin() override; void unload() override; public slots: void itemActivated( const QModelIndex& ); void switchToClicked(const QModelIndex& ); private slots: void addView( Sublime::View* ); void changeView( Sublime::View* ); void addMainWindow( Sublime::MainWindow* ); void changeArea( Sublime::Area* ); void removeView( Sublime::View* ); void removeMainWindow(QObject*); void walkForward(); void walkBackward(); protected: bool eventFilter( QObject*, QEvent* ) override; private: void setViewGeometry(Sublime::MainWindow* window); void storeAreaViewList( Sublime::MainWindow* mainwindow, Sublime::Area* area ); void enableActions(); void fillModel( Sublime::MainWindow* window ); void walk(const int from, const int to); // Need to use QObject here as we only have a QObject* in // the removeMainWindow method and cannot cast it to the mainwindow anymore QMap > > documentLists; DocumentSwitcherTreeView* view; QStandardItemModel* model; QAction* forwardAction; QAction* backwardAction; }; #endif diff --git a/plugins/execute/executeplugin.h b/plugins/execute/executeplugin.h index c5b1fb5b4..ce6d78d1d 100644 --- a/plugins/execute/executeplugin.h +++ b/plugins/execute/executeplugin.h @@ -1,72 +1,72 @@ /* * This file is part of KDevelop * * 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. */ #ifndef KDEVPLATFORM_PLUGIN_EXECUTEPLUGIN_H #define KDEVPLATFORM_PLUGIN_EXECUTEPLUGIN_H #include -#include +#include #include "iexecuteplugin.h" class QUrl; class KJob; class NativeAppConfigType; class ExecutePlugin : public KDevelop::IPlugin, public IExecutePlugin { Q_OBJECT Q_INTERFACES( IExecutePlugin ) public: explicit ExecutePlugin(QObject *parent, const QVariantList & = QVariantList() ); ~ExecutePlugin() override; static QString _nativeAppConfigTypeId; static QString workingDirEntry; static QString executableEntry; static QString argumentsEntry; static QString isExecutableEntry; static QString dependencyEntry; static QString environmentProfileEntry; static QString useTerminalEntry; static QString terminalEntry; static QString userIdToRunEntry; static QString dependencyActionEntry; static QString projectTargetEntry; void unload() override; QUrl executable( KDevelop::ILaunchConfiguration*, QString& err ) const override; QStringList arguments( KDevelop::ILaunchConfiguration*, QString& err ) const override; QUrl workingDirectory( KDevelop::ILaunchConfiguration* ) const override; KJob* dependencyJob( KDevelop::ILaunchConfiguration* ) const override; QString environmentProfileName(KDevelop::ILaunchConfiguration*) const override; bool useTerminal( KDevelop::ILaunchConfiguration* ) const override; QString terminal( KDevelop::ILaunchConfiguration* ) const override; QString nativeAppConfigTypeId() const override; NativeAppConfigType* m_configType; }; #endif // KDEVPLATFORM_PLUGIN_EXECUTEPLUGIN_H // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/plugins/execute/iexecuteplugin.h b/plugins/execute/iexecuteplugin.h index 751ed9a75..170c9558b 100644 --- a/plugins/execute/iexecuteplugin.h +++ b/plugins/execute/iexecuteplugin.h @@ -1,57 +1,57 @@ /* KDevelop * * Copyright 2009 Andreas Pakulat * * 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. */ #ifndef KDEVPLATFORM_PLUGIN_IEXECUTEPLUGIN_H #define KDEVPLATFORM_PLUGIN_IEXECUTEPLUGIN_H -#include +#include namespace KDevelop { class ILaunchConfiguration; } class KJob; class QUrl; class QStringList; /** @author Andreas Pakulat */ class IExecutePlugin { public: virtual ~IExecutePlugin() {} virtual QUrl executable( KDevelop::ILaunchConfiguration* config, QString& error) const = 0; virtual QStringList arguments( KDevelop::ILaunchConfiguration* config, QString& error) const = 0; virtual QUrl workingDirectory( KDevelop::ILaunchConfiguration* config) const = 0; virtual KJob* dependencyJob( KDevelop::ILaunchConfiguration* config) const = 0; virtual QString environmentProfileName(KDevelop::ILaunchConfiguration* config) const = 0; virtual bool useTerminal( KDevelop::ILaunchConfiguration* config) const = 0; virtual QString terminal( KDevelop::ILaunchConfiguration* config) const = 0; virtual QString nativeAppConfigTypeId() const = 0; }; Q_DECLARE_INTERFACE( IExecutePlugin, "org.kdevelop.IExecutePlugin" ) #endif diff --git a/plugins/executescript/executescriptplugin.h b/plugins/executescript/executescriptplugin.h index b12ad092a..39f2df6ec 100644 --- a/plugins/executescript/executescriptplugin.h +++ b/plugins/executescript/executescriptplugin.h @@ -1,75 +1,75 @@ /* * This file is part of KDevelop * * Copyright 2007 Hamish Rodda * Copyright 2009 Niko Sams * * 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. */ #ifndef KDEVPLATFORM_PLUGIN_EXECUTESCRIPTPLUGIN_H #define KDEVPLATFORM_PLUGIN_EXECUTESCRIPTPLUGIN_H #include -#include +#include #include "iexecutescriptplugin.h" class ScriptAppConfigType; class QUrl; class ExecuteScriptPlugin : public KDevelop::IPlugin, public IExecuteScriptPlugin { Q_OBJECT Q_INTERFACES( IExecuteScriptPlugin ) public: explicit ExecuteScriptPlugin(QObject *parent, const QVariantList & = QVariantList() ); ~ExecuteScriptPlugin() override; static QString _scriptAppConfigTypeId; static QString interpreterEntry; static QString workingDirEntry; static QString executableEntry; static QString executeOnRemoteHostEntry; static QString remoteHostEntry; static QString runCurrentFileEntry; static QString argumentsEntry; static QString isExecutableEntry; static QString environmentProfileEntry; //static QString useTerminalEntry; static QString userIdToRunEntry; static QString projectTargetEntry; static QString outputFilteringEntry; void unload() override; QString interpreter( KDevelop::ILaunchConfiguration*, QString& err ) const override; QUrl script( KDevelop::ILaunchConfiguration*, QString& err ) const override; QString remoteHost(KDevelop::ILaunchConfiguration* , QString& err) const override; QStringList arguments( KDevelop::ILaunchConfiguration*, QString& err ) const override; QUrl workingDirectory( KDevelop::ILaunchConfiguration* ) const override; QString environmentProfileName(KDevelop::ILaunchConfiguration*) const override; //bool useTerminal( KDevelop::ILaunchConfiguration* ) const; QString scriptAppConfigTypeId() const override; int outputFilterModeId( KDevelop::ILaunchConfiguration* ) const override; bool runCurrentFile(KDevelop::ILaunchConfiguration*) const override; ScriptAppConfigType* m_configType; }; #endif // KDEVPLATFORM_PLUGIN_EXECUTESCRIPTPLUGIN_H // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/plugins/executescript/scriptappjob.h b/plugins/executescript/scriptappjob.h index c82b3595a..08d7d3872 100644 --- a/plugins/executescript/scriptappjob.h +++ b/plugins/executescript/scriptappjob.h @@ -1,55 +1,55 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat Copyright 2009 Niko Sams 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. */ #ifndef KDEVPLATFORM_PLUGIN_SCRIPTAPPJOB_H #define KDEVPLATFORM_PLUGIN_SCRIPTAPPJOB_H -#include +#include #include class ExecuteScriptPlugin; namespace KDevelop { class ILaunchConfiguration; class ProcessLineMaker; class OutputModel; } class KProcess; class ScriptAppJob : public KDevelop::OutputJob { Q_OBJECT public: ScriptAppJob( ExecuteScriptPlugin* parent, KDevelop::ILaunchConfiguration* cfg ); void start() override; bool doKill() override; KDevelop::OutputModel* model(); private slots: void processError(QProcess::ProcessError); void processFinished(int,QProcess::ExitStatus); private: void appendLine(const QString &l); KProcess* proc; KDevelop::ProcessLineMaker* lineMaker; }; #endif diff --git a/plugins/externalscript/externalscriptjob.h b/plugins/externalscript/externalscriptjob.h index adec6ec49..6a1fee65b 100644 --- a/plugins/externalscript/externalscriptjob.h +++ b/plugins/externalscript/externalscriptjob.h @@ -1,87 +1,87 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat 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 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. */ #ifndef KDEVPLATFORM_PLUGIN_EXTERNALSCRIPTJOB_H #define KDEVPLATFORM_PLUGIN_EXTERNALSCRIPTJOB_H -#include +#include #include #include "externalscriptitem.h" #include #include namespace KDevelop { class ProcessLineMaker; class OutputModel; class Document; } namespace KTextEditor { class Document; } class KProcess; class ExternalScriptPlugin; class ExternalScriptJob : public KDevelop::OutputJob { Q_OBJECT public: ExternalScriptJob( ExternalScriptItem* item, const QUrl &url, ExternalScriptPlugin* parent ); void start() override; KDevelop::OutputModel* model(); protected: bool doKill() override; private slots: void processError( QProcess::ProcessError ); void processFinished( int, QProcess::ExitStatus ); void receivedStdoutLines(const QStringList& lines); void receivedStderrLines(const QStringList& lines); private: void appendLine( const QString &l ); KProcess* m_proc; KDevelop::ProcessLineMaker* m_lineMaker; ExternalScriptItem::OutputMode m_outputMode; ExternalScriptItem::InputMode m_inputMode; ExternalScriptItem::ErrorMode m_errorMode; int m_filterMode; KTextEditor::Document* m_document; QUrl m_url; /// invalid when whole doc should be replaced KTextEditor::Range m_selectionRange; KTextEditor::Cursor m_cursorPosition; bool m_showOutput; QStringList m_stdout; QStringList m_stderr; }; #endif // KDEVPLATFORM_PLUGIN_EXTERNALSCRIPTJOB_H // kate: indent-mode cstyle; space-indent on; indent-width 2; replace-tabs on; diff --git a/plugins/filemanager/filemanager.cpp b/plugins/filemanager/filemanager.cpp index 423242f98..21ca0d55e 100644 --- a/plugins/filemanager/filemanager.cpp +++ b/plugins/filemanager/filemanager.cpp @@ -1,214 +1,214 @@ /*************************************************************************** * Copyright 2006-2007 Alexander Dymo * * Copyright 2006 Andreas Pakulat * * Copyright 2016 Imran Tatriev * * * * 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 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 "filemanager.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 "../openwith/iopenwith.h" #include "kdevfilemanagerplugin.h" #include "bookmarkhandler.h" #include "debug.h" Q_LOGGING_CATEGORY(PLUGIN_FILEMANAGER, "kdevplatform.plugins.filemanager") FileManager::FileManager(KDevFileManagerPlugin *plugin, QWidget* parent) : QWidget(parent), m_plugin(plugin) { setObjectName(QStringLiteral("FileManager")); setWindowIcon(QIcon::fromTheme(QStringLiteral("folder-sync"), windowIcon())); setWindowTitle(i18n("File System")); KConfigGroup cg = KDevelop::ICore::self()->activeSession()->config()->group( "Filesystem" ); QVBoxLayout *l = new QVBoxLayout(this); l->setMargin(0); l->setSpacing(0); KFilePlacesModel* model = new KFilePlacesModel( this ); urlnav = new KUrlNavigator(model, QUrl(cg.readEntry( "LastLocation", QUrl::fromLocalFile( QDir::homePath() ) )), this ); connect(urlnav, &KUrlNavigator::urlChanged, this, &FileManager::gotoUrl); l->addWidget(urlnav); dirop = new KDirOperator( urlnav->locationUrl(), this); dirop->setView( KFile::Tree ); dirop->setupMenu( KDirOperator::SortActions | KDirOperator::FileActions | KDirOperator::NavActions | KDirOperator::ViewActions ); connect(dirop, &KDirOperator::urlEntered, this, &FileManager::updateNav); connect(dirop, &KDirOperator::contextMenuAboutToShow, this, &FileManager::fillContextMenu); l->addWidget(dirop); connect( dirop, &KDirOperator::fileSelected, this, &FileManager::openFile ); setFocusProxy(dirop); // includes some actions, but not hooked into the shortcut dialog atm m_actionCollection = new KActionCollection(this); m_actionCollection->addAssociatedWidget(this); setupActions(); // Connect the bookmark handler connect(m_bookmarkHandler, &BookmarkHandler::openUrl, this, &FileManager::gotoUrl); connect(m_bookmarkHandler, &BookmarkHandler::openUrl, this, &FileManager::updateNav); } FileManager::~FileManager() { KConfigGroup cg = KDevelop::ICore::self()->activeSession()->config()->group( "Filesystem" ); cg.writeEntry( "LastLocation", urlnav->locationUrl() ); cg.sync(); } void FileManager::fillContextMenu(KFileItem item, QMenu* menu) { foreach(QAction* a, contextActions){ if(menu->actions().contains(a)){ menu->removeAction(a); } } contextActions.clear(); contextActions.append(menu->addSeparator()); menu->addAction(newFileAction); contextActions.append(newFileAction); KDevelop::FileContext context(QList() << item.url()); QList extensions = KDevelop::ICore::self()->pluginController()->queryPluginsForContextMenuExtensions( &context ); KDevelop::ContextMenuExtension::populateMenu(menu, extensions); QMenu* tmpMenu = new QMenu(); KDevelop::ContextMenuExtension::populateMenu(tmpMenu, extensions); contextActions.append(tmpMenu->actions()); delete tmpMenu; } void FileManager::openFile(const KFileItem& file) { KDevelop::IOpenWith::openFiles(QList() << file.url()); } void FileManager::gotoUrl( const QUrl& url ) { dirop->setUrl( url, true ); } void FileManager::updateNav( const QUrl& url ) { urlnav->setLocationUrl( url ); } void FileManager::setupActions() { KActionMenu *acmBookmarks = new KActionMenu(QIcon::fromTheme(QStringLiteral("bookmarks")), i18n("Bookmarks"), this); acmBookmarks->setDelayed(false); m_bookmarkHandler = new BookmarkHandler(this, acmBookmarks->menu()); acmBookmarks->setShortcutContext(Qt::WidgetWithChildrenShortcut); QAction* action = new QAction(this); action->setShortcutContext(Qt::WidgetWithChildrenShortcut); action->setText(i18n("Current Document Directory")); action->setIcon(QIcon::fromTheme(QStringLiteral("dirsync"))); connect(action, &QAction::triggered, this, &FileManager::syncCurrentDocumentDirectory); tbActions << (dirop->actionCollection()->action(QStringLiteral("back"))); tbActions << (dirop->actionCollection()->action(QStringLiteral("up"))); tbActions << (dirop->actionCollection()->action(QStringLiteral("home"))); tbActions << (dirop->actionCollection()->action(QStringLiteral("forward"))); tbActions << (dirop->actionCollection()->action(QStringLiteral("reload"))); tbActions << acmBookmarks; tbActions << action; tbActions << (dirop->actionCollection()->action(QStringLiteral("sorting menu"))); tbActions << (dirop->actionCollection()->action(QStringLiteral("show hidden"))); newFileAction = new QAction(this); newFileAction->setText(i18n("New File...")); newFileAction->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); connect(newFileAction, &QAction::triggered, this, &FileManager::createNewFile); } void FileManager::createNewFile() { QUrl destUrl = QFileDialog::getSaveFileUrl(KDevelop::ICore::self()->uiController()->activeMainWindow(), i18n("Create New File")); if (destUrl.isEmpty()) { return; } KJob* job = KIO::storedPut(QByteArray(), destUrl, -1); KJobWidgets::setWindow(job, this); connect(job, &KJob::result, this, &FileManager::fileCreated); } void FileManager::fileCreated(KJob* job) { auto transferJob = qobject_cast(job); Q_ASSERT(transferJob); if (!transferJob->error()) { KDevelop::ICore::self()->documentController()->openDocument( transferJob->url() ); } else { KMessageBox::error(KDevelop::ICore::self()->uiController()->activeMainWindow(), i18n("Unable to create file '%1'", transferJob->url().toDisplayString(QUrl::PreferLocalFile))); } } void FileManager::syncCurrentDocumentDirectory() { if( KDevelop::IDocument* activeDoc = KDevelop::ICore::self()->documentController()->activeDocument() ) updateNav( activeDoc->url().adjusted(QUrl::RemoveFilename) ); } QList FileManager::toolBarActions() const { return tbActions; } KActionCollection* FileManager::actionCollection() const { return m_actionCollection; } KDirOperator* FileManager::dirOperator() const { return dirop; } KDevFileManagerPlugin* FileManager::plugin() const { return m_plugin; } #include "moc_filemanager.cpp" diff --git a/plugins/filemanager/filemanager.h b/plugins/filemanager/filemanager.h index 10f8f6a08..dd2d68a97 100644 --- a/plugins/filemanager/filemanager.h +++ b/plugins/filemanager/filemanager.h @@ -1,68 +1,68 @@ /*************************************************************************** * Copyright 2006 Alexander Dymo * * Copyright 2006 Andreas Pakulat * * * * 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 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_PLUGIN_FILEMANAGER_H #define KDEVPLATFORM_PLUGIN_FILEMANAGER_H -#include +#include -#include -#include -#include +#include +#include +#include class KActionCollection; class QAction; class QString; class QMenu; class KDevFileManagerPlugin; class BookmarkHandler; class FileManager: public QWidget { Q_OBJECT public: FileManager(KDevFileManagerPlugin *plugin, QWidget* parent); ~FileManager() override; QList toolBarActions() const; KActionCollection* actionCollection() const; KDirOperator* dirOperator() const; KDevFileManagerPlugin* plugin() const; private slots: void fileCreated(KJob *job); void openFile(const KFileItem&); void gotoUrl(const QUrl&); void updateNav( const QUrl& url ); void syncCurrentDocumentDirectory(); void fillContextMenu(KFileItem item, QMenu *menu); void createNewFile(); private: void setupActions(); QList tbActions; QAction* newFileAction; QList contextActions; KDirOperator* dirop; KUrlNavigator* urlnav; BookmarkHandler *m_bookmarkHandler; KActionCollection *m_actionCollection; KDevFileManagerPlugin *m_plugin; }; #endif diff --git a/plugins/filetemplates/classidentifierpage.cpp b/plugins/filetemplates/classidentifierpage.cpp index 1d443eba3..909bc356a 100644 --- a/plugins/filetemplates/classidentifierpage.cpp +++ b/plugins/filetemplates/classidentifierpage.cpp @@ -1,95 +1,95 @@ /* This file is part of KDevelop Copyright 2008 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 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 "classidentifierpage.h" #include "language/duchain/identifier.h" -#include +#include #include #include "ui_newclass.h" using namespace KDevelop; struct KDevelop::ClassIdentifierPagePrivate { ClassIdentifierPagePrivate() : classid(nullptr) { } Ui::NewClassDialog* classid; }; ClassIdentifierPage::ClassIdentifierPage(QWidget* parent) : QWidget(parent) , d(new ClassIdentifierPagePrivate()) { d->classid = new Ui::NewClassDialog; d->classid->setupUi(this); d->classid->identifierLineEdit->setPlaceholderText(i18n("Class name, including any namespaces")); d->classid->keditlistwidget->lineEdit()->setPlaceholderText(i18n("Inheritance type and base class name")); d->classid->inheritanceLabel->setBuddy(d->classid->keditlistwidget->lineEdit()); connect(d->classid->identifierLineEdit, &QLineEdit::textChanged, this, &ClassIdentifierPage::checkIdentifier); #if KWIDGETSADDONS_VERSION < QT_VERSION_CHECK(5,32,0) // workaround for KEditListWidget bug: // ensure keyboard focus is returned to edit line connect(d->classid->keditlistwidget, &KEditListWidget::added, d->classid->keditlistwidget->lineEdit(), static_cast(&QWidget::setFocus)); connect(d->classid->keditlistwidget, &KEditListWidget::removed, d->classid->keditlistwidget->lineEdit(), static_cast(&QWidget::setFocus)); #endif emit isValid(false); } ClassIdentifierPage::~ClassIdentifierPage() { delete d->classid; delete d; } QString ClassIdentifierPage::identifier() const { return d->classid->identifierLineEdit->text(); } void ClassIdentifierPage::checkIdentifier() { emit isValid(!identifier().isEmpty()); } QStringList ClassIdentifierPage::inheritanceList() const { return d->classid->keditlistwidget->items(); } void ClassIdentifierPage::setInheritanceList (const QStringList& list) { d->classid->keditlistwidget->setItems(list); } void ClassIdentifierPage::setFocusToFirstEditWidget() { d->classid->identifierLineEdit->setFocus(); } diff --git a/plugins/filetemplates/licensepage.cpp b/plugins/filetemplates/licensepage.cpp index e5db09506..2024158c1 100644 --- a/plugins/filetemplates/licensepage.cpp +++ b/plugins/filetemplates/licensepage.cpp @@ -1,282 +1,283 @@ /* This file is part of KDevelop Copyright 2008 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 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 "licensepage.h" -#include + #include "ui_licensechooser.h" #include "debug.h" +#include #include #include #include #include #include #include namespace KDevelop { struct LicensePagePrivate { struct LicenseInfo { QString name; QString path; QString contents; bool operator< (const LicenseInfo& o) const { return name.localeAwareCompare(o.name) < 0; } }; typedef QList LicenseList; explicit LicensePagePrivate(LicensePage* page_) : license(nullptr) , page(page_) { } // methods void initializeLicenses(); QString readLicense(int licenseIndex); bool saveLicense(); // slots void licenseComboChanged(int license); Ui::LicenseChooserDialog* license; LicenseList availableLicenses; LicensePage* page; }; //! Read all the license files in the global and local config dirs void LicensePagePrivate::initializeLicenses() { qCDebug(PLUGIN_FILETEMPLATES) << "Searching for available licenses"; QStringList licenseDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kdevcodegen/licenses"), QStandardPaths::LocateDirectory); //Iterate through the possible directories that contain licenses, and load their names foreach(const QString& currentDir, licenseDirs) { QDirIterator it(currentDir, QDir::Files | QDir::Readable); while(it.hasNext()) { LicenseInfo newLicense; newLicense.path = it.next(); newLicense.name = it.fileName(); qCDebug(PLUGIN_FILETEMPLATES) << "Found License: " << newLicense.name; availableLicenses.push_back(newLicense); } } std::sort(availableLicenses.begin(), availableLicenses.end()); foreach(const LicenseInfo& info, availableLicenses) { license->licenseComboBox->addItem(info.name); } //Finally add the option other for user specified licenses LicenseInfo otherLicense; availableLicenses.push_back(otherLicense); license->licenseComboBox->addItem(i18n("Other")); } // Read a license index, if it is not loaded, open it from the file QString LicensePagePrivate::readLicense(int licenseIndex) { //If the license is not loaded into memory, read it in if(availableLicenses[licenseIndex].contents.isEmpty()) { QString licenseText; //If we are dealing with the last option "other" just return a new empty string if(licenseIndex != (availableLicenses.size() - 1)) { qCDebug(PLUGIN_FILETEMPLATES) << "Reading license: " << availableLicenses[licenseIndex].name ; QFile newLicense(availableLicenses[licenseIndex].path); if(newLicense.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream newLicenseText(&newLicense); newLicenseText.setAutoDetectUnicode(true); licenseText = newLicenseText.readAll(); newLicense.close(); } else licenseText = QStringLiteral("Error, could not open license file.\n Was it deleted?"); } availableLicenses[licenseIndex].contents = licenseText; } return availableLicenses[licenseIndex].contents; } // ---Slots--- void LicensePagePrivate::licenseComboChanged(int selectedLicense) { //If the last slot is selected enable the save license combobox if(selectedLicense == (availableLicenses.size() - 1)) { license->licenseTextEdit->clear(); license->licenseTextEdit->setReadOnly(false); license->saveLicense->setEnabled(true); } else { license->saveLicense->setEnabled(false); license->licenseTextEdit->setReadOnly(true); } if(selectedLicense < 0 || selectedLicense >= availableLicenses.size()) license->licenseTextEdit->setText(i18n("Could not load previous license")); else license->licenseTextEdit->setText(readLicense(selectedLicense)); } bool LicensePagePrivate::saveLicense() { qCDebug(PLUGIN_FILETEMPLATES) << "Attempting to save custom license: " << license->licenseName->text(); QString localDataDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+"/kdevcodegen/licenses/"; QString fullPath = localDataDir + license->licenseName->text(); QFile newFile(fullPath); if(newFile.exists()) { KMessageBox::sorry(page, i18n("The specified license already exists. " "Please provide a different name.")); return false; } QDir().mkpath(localDataDir); newFile.open(QIODevice::WriteOnly); qint64 result = newFile.write(license->licenseTextEdit->toPlainText().toUtf8()); newFile.close(); if(result == -1) { KMessageBox::sorry(page, i18n("Failed to write custom license template to file %1.", fullPath)); return false; } // also add to our data structures, this esp. needed for proper saving // of the license index so it can be restored the next time we show up LicenseInfo info; info.name = license->licenseName->text(); info.path = localDataDir; availableLicenses << info; // find index of the new the license, omitting the very last item ('Other') int idx = availableLicenses.count() - 1; for(int i = 0; i < availableLicenses.size() - 1; ++i) { if (info < availableLicenses.at(i)) { idx = i; break; } } availableLicenses.insert(idx, info); license->licenseComboBox->insertItem(idx, info.name); license->licenseComboBox->setCurrentIndex(idx); return true; } LicensePage::LicensePage(QWidget* parent) : QWidget(parent) , d(new LicensePagePrivate(this)) { d->license = new Ui::LicenseChooserDialog; d->license->setupUi(this); connect(d->license->licenseComboBox, static_cast(&KComboBox::currentIndexChanged), this, [&] (int selectedLicense) { d->licenseComboChanged(selectedLicense); }); connect(d->license->saveLicense, &QCheckBox::clicked, d->license->licenseName, &QLineEdit::setEnabled); // Read all the available licenses from the standard dirs d->initializeLicenses(); //Set the license selection to the previous one KConfigGroup config(KSharedConfig::openConfig()->group("CodeGeneration")); d->license->licenseComboBox->setCurrentIndex(config.readEntry( "LastSelectedLicense", 0 )); // Needed to avoid a bug where licenseComboChanged doesn't get // called by QComboBox if the past selection was 0 d->licenseComboChanged(d->license->licenseComboBox->currentIndex()); } LicensePage::~LicensePage() { if (d->license->saveLicense->isChecked() && !d->license->licenseName->text().isEmpty()) { d->saveLicense(); } KConfigGroup config(KSharedConfig::openConfig()->group("CodeGeneration")); //Do not save invalid license numbers' int index = d->license->licenseComboBox->currentIndex(); if( index >= 0 || index < d->availableLicenses.size() ) { config.writeEntry("LastSelectedLicense", index); config.config()->sync(); } else { qWarning() << "Attempted to save an invalid license number: " << index << ". Number of licenses:" << d->availableLicenses.size(); } delete d->license; delete d; } QString LicensePage::license() const { QString licenseText = d->license->licenseTextEdit->document()->toPlainText(); /* Add date, name and email to license text */ licenseText.replace(QLatin1String(""), QDate::currentDate().toString(QStringLiteral("yyyy"))); licenseText.replace(QLatin1String(""), QDate::currentDate().toString(QStringLiteral("MM"))); licenseText.replace(QLatin1String(""), QDate::currentDate().toString(QStringLiteral("dd"))); QString developer(QStringLiteral("%1 <%2>")); KEMailSettings emailSettings; QString name = emailSettings.getSetting(KEMailSettings::RealName); if (name.isEmpty()) { name = QStringLiteral(""); } developer = developer.arg(name); QString email = emailSettings.getSetting(KEMailSettings::EmailAddress); if (email.isEmpty()) { email = QStringLiteral("email"); //no < > as they are already through the email field } developer = developer.arg(email); licenseText.replace(QLatin1String(""), developer); return licenseText; } void LicensePage::setFocusToFirstEditWidget() { d->license->licenseComboBox->setFocus(); } } Q_DECLARE_TYPEINFO(KDevelop::LicensePagePrivate::LicenseInfo, Q_MOVABLE_TYPE); #include "moc_licensepage.cpp" diff --git a/plugins/filetemplates/outputpage.cpp b/plugins/filetemplates/outputpage.cpp index afe053cd7..ed03a6963 100644 --- a/plugins/filetemplates/outputpage.cpp +++ b/plugins/filetemplates/outputpage.cpp @@ -1,279 +1,279 @@ /* This file is part of KDevelop Copyright 2008 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 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 "outputpage.h" #include "ui_outputlocation.h" #include "debug.h" #include #include -#include -#include -#include +#include +#include +#include #include #include #include #include namespace KDevelop { struct OutputPagePrivate { explicit OutputPagePrivate(OutputPage* page_) : page(page_) , output(nullptr) { } OutputPage* page; Ui::OutputLocationDialog* output; QSignalMapper urlChangedMapper; QHash outputFiles; QHash outputLines; QHash outputColumns; QList labels; QHash defaultUrls; QHash lowerCaseUrls; QStringList fileIdentifiers; void updateRanges(QSpinBox* line, QSpinBox* column, bool enable); void updateFileRange(const QString& field); void updateFileNames(); void validate(); }; void OutputPagePrivate::updateRanges(QSpinBox* line, QSpinBox* column, bool enable) { qCDebug(PLUGIN_FILETEMPLATES) << "Updating Ranges, file exists: " << enable; line->setEnabled(enable); column->setEnabled(enable); } void OutputPagePrivate::updateFileRange(const QString& field) { if (!outputFiles.contains(field)) { return; } QString url = outputFiles[field]->url().toLocalFile(); QFileInfo info(url); updateRanges(outputLines[field], outputColumns[field], info.exists() && !info.isDir()); validate(); } void OutputPagePrivate::updateFileNames() { bool lower = output->lowerFilenameCheckBox->isChecked(); const QHash urls = lower ? lowerCaseUrls : defaultUrls; for (QHash::const_iterator it = outputFiles.constBegin(); it != outputFiles.constEnd(); ++it) { const QUrl url = urls.value(it.key()); if (!url.isEmpty()) { it.value()->setUrl(url); } } //Save the setting for next time KConfigGroup codegenGroup( KSharedConfig::openConfig(), "CodeGeneration" ); codegenGroup.writeEntry( "LowerCaseFilenames", output->lowerFilenameCheckBox->isChecked() ); validate(); } void OutputPagePrivate::validate() { QStringList invalidFiles; for(QHash< QString, KUrlRequester* >::const_iterator it = outputFiles.constBegin(); it != outputFiles.constEnd(); ++it) { if (!it.value()->url().isValid()) { invalidFiles << it.key(); } else if (it.value()->url().isLocalFile() && !QFileInfo(it.value()->url().adjusted(QUrl::RemoveFilename).toLocalFile()).isWritable()) { invalidFiles << it.key(); } } bool valid = invalidFiles.isEmpty(); if (valid) { output->messageWidget->animatedHide(); } else { std::sort(invalidFiles.begin(), invalidFiles.end()); output->messageWidget->setMessageType(KMessageWidget::Error); output->messageWidget->setCloseButtonVisible(false); output->messageWidget->setText(i18np("Invalid output file: %2", "Invalid output files: %2", invalidFiles.count(), invalidFiles.join(QStringLiteral(", ")))); output->messageWidget->animatedShow(); } emit page->isValid(valid); } OutputPage::OutputPage(QWidget* parent) : QWidget(parent) , d(new OutputPagePrivate(this)) { d->output = new Ui::OutputLocationDialog; d->output->setupUi(this); d->output->messageWidget->setVisible(false); connect(&d->urlChangedMapper, static_cast(&QSignalMapper::mapped), this, [&] (const QString& field) { d->updateFileRange(field); }); connect(d->output->lowerFilenameCheckBox, &QCheckBox::stateChanged, this, [&] { d->updateFileNames(); }); } OutputPage::~OutputPage() { delete d->output; delete d; } void OutputPage::prepareForm(const SourceFileTemplate& fileTemplate) { // First clear any existing file configurations // This can happen when going back and forth between assistant pages d->fileIdentifiers.clear(); d->defaultUrls.clear(); d->lowerCaseUrls.clear(); while (d->output->urlFormLayout->count() > 0) { d->output->urlFormLayout->takeAt(0); } while (d->output->positionFormLayout->count() > 0) { d->output->positionFormLayout->takeAt(0); } foreach (KUrlRequester* req, d->outputFiles) { d->urlChangedMapper.removeMappings(req); } qDeleteAll(d->outputFiles); qDeleteAll(d->outputLines); qDeleteAll(d->outputColumns); qDeleteAll(d->labels); d->outputFiles.clear(); d->outputLines.clear(); d->outputColumns.clear(); d->labels.clear(); const int outputFilesCount = fileTemplate.outputFiles().count(); d->output->urlGroupBox->setTitle(i18np("Output file", "Output files", outputFilesCount)); d->output->positionGroupBox->setTitle(i18np("Location within existing file", "Location within existing files", outputFilesCount)); foreach (const SourceFileTemplate::OutputFile& file, fileTemplate.outputFiles()) { d->fileIdentifiers << file.identifier; const QString fileLabelText = i18n("%1:", file.label); QLabel* label = new QLabel(fileLabelText, this); d->labels << label; KUrlRequester* requester = new KUrlRequester(this); requester->setMode( KFile::File | KFile::LocalOnly ); d->urlChangedMapper.setMapping(requester, file.identifier); connect(requester, &KUrlRequester::textChanged, &d->urlChangedMapper, static_cast(&QSignalMapper::map)); d->output->urlFormLayout->addRow(label, requester); d->outputFiles.insert(file.identifier, requester); label = new QLabel(fileLabelText, this); d->labels << label; QHBoxLayout* layout = new QHBoxLayout; auto line = new QSpinBox(this); line->setPrefix(i18n("Line: ")); line->setValue(0); line->setMinimum(0); layout->addWidget(line); auto column = new QSpinBox(this); column->setPrefix(i18n("Column: ")); column->setValue(0); column->setMinimum(0); layout->addWidget(column); d->output->positionFormLayout->addRow(label, layout); d->outputLines.insert(file.identifier, line); d->outputColumns.insert(file.identifier, column); } } void OutputPage::loadFileTemplate(const SourceFileTemplate& fileTemplate, const QUrl& _baseUrl, TemplateRenderer* renderer) { QUrl baseUrl = _baseUrl; if (!baseUrl.path().endsWith('/')) { baseUrl.setPath(baseUrl.path()+'/'); } KConfigGroup codegenGroup( KSharedConfig::openConfig(), "CodeGeneration" ); bool lower = codegenGroup.readEntry( "LowerCaseFilenames", true ); d->output->lowerFilenameCheckBox->setChecked(lower); foreach (const SourceFileTemplate::OutputFile& file, fileTemplate.outputFiles()) { d->fileIdentifiers << file.identifier; QUrl url = baseUrl.resolved(QUrl(renderer->render(file.outputName))); d->defaultUrls.insert(file.identifier, url); url = baseUrl.resolved(QUrl(renderer->render(file.outputName).toLower())); d->lowerCaseUrls.insert(file.identifier, url); } d->updateFileNames(); } QHash< QString, QUrl > OutputPage::fileUrls() const { QHash urls; for (QHash::const_iterator it = d->outputFiles.constBegin(); it != d->outputFiles.constEnd(); ++it) { urls.insert(it.key(), it.value()->url()); } return urls; } QHash< QString, KTextEditor::Cursor > OutputPage::filePositions() const { QHash positions; foreach (const QString& identifier, d->fileIdentifiers) { positions.insert(identifier, KTextEditor::Cursor(d->outputLines[identifier]->value(), d->outputColumns[identifier]->value())); } return positions; } void OutputPage::setFocusToFirstEditWidget() { d->output->lowerFilenameCheckBox->setFocus(); } } #include "moc_outputpage.cpp" diff --git a/plugins/genericprojectmanager/test/test_projectload.cpp b/plugins/genericprojectmanager/test/test_projectload.cpp index d9bd8a8de..995203753 100644 --- a/plugins/genericprojectmanager/test/test_projectload.cpp +++ b/plugins/genericprojectmanager/test/test_projectload.cpp @@ -1,410 +1,410 @@ /* This file is part of KDevelop Copyright 2010 Niko Sams 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 "test_projectload.h" -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QTEST_MAIN(TestProjectLoad) Q_DECLARE_METATYPE(KDevelop::IProject*) using namespace KDevelop; ///FIXME: get rid of this, use temporary dir+file classes! void exec(const QString &cmd) { QProcess proc; proc.setProcessChannelMode(QProcess::ForwardedChannels); proc.start(cmd); proc.waitForFinished(); Q_ASSERT(proc.exitStatus() == QProcess::NormalExit); Q_ASSERT(proc.exitStatus() == 0); } void TestProjectLoad::initTestCase() { AutoTestShell::init({QStringLiteral("KDevGenericManager")}); TestCore::initialize(); ICore::self()->languageController()->backgroundParser()->disableProcessing(); qRegisterMetaType(); foreach(IProject* p, ICore::self()->projectController()->projects()) { ICore::self()->projectController()->closeProject(p); } } void TestProjectLoad::cleanupTestCase() { TestCore::shutdown(); } void TestProjectLoad::init() { foreach(IProject* p, ICore::self()->projectController()->projects()) { ICore::self()->projectController()->closeProject(p); } QCOMPARE(ICore::self()->projectController()->projects().size(), 0); } struct TestProject { // temp directory of project QTemporaryDir* dir; // name of the project (random) QString name; // project file (*.kdev4) QUrl file; ~TestProject() { IProject* p = ICore::self()->projectController()->findProjectByName(name); if (p) { ICore::self()->projectController()->closeProject(p); } delete dir; } }; TestProject makeProject() { TestProject ret; ret.dir = new QTemporaryDir(); QFileInfo dir(ret.dir->path()); Q_ASSERT(dir.exists()); ret.name = dir.fileName(); QStringList projectFileContents; projectFileContents << QStringLiteral("[Project]") << QStringLiteral("Name=") + ret.name << QStringLiteral("Manager=KDevGenericManager"); QUrl projecturl = QUrl::fromLocalFile( dir.absoluteFilePath() + "/simpleproject.kdev4" ); QFile projectFile(projecturl.toLocalFile()); projectFile.open(QIODevice::WriteOnly); projectFile.write(projectFileContents.join(QStringLiteral("\n")).toLatin1()); projectFile.close(); ret.file = projecturl; Q_ASSERT(ret.dir->isValid()); Q_ASSERT(projecturl.adjusted(QUrl::RemoveFilename).toLocalFile() == ret.dir->path() + '/'); return ret; } void TestProjectLoad::addRemoveFiles() { const TestProject p = makeProject(); QFile f(p.dir->path()+"/sdf"); f.open(QIODevice::WriteOnly); f.close(); ICore::self()->projectController()->openProject(p.file); QSignalSpy spy(KDevelop::ICore::self()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*))); QVERIFY(spy.wait(2000)); IProject* project = ICore::self()->projectController()->projects().first(); QCOMPARE(project->projectFile().toUrl(), p.file); //KDirWatch adds/removes the file automatically for (int i=0; i<100; ++i) { QFile f2(p.dir->path()+"/blub"+QString::number(i)); f2.open(QIODevice::WriteOnly); f2.close(); } for (int i=0; i<50; ++i) { QFile f2(p.dir->path()+"/blub"+QString::number(i)); QVERIFY(f2.exists()); f2.remove(); } QTest::qWait(500); QUrl url = QUrl::fromLocalFile(p.dir->path()+"/blub"+QString::number(50)).adjusted(QUrl::NormalizePathSegments); QCOMPARE(project->filesForPath(IndexedString(url)).count(), 1); ProjectFileItem* file = project->filesForPath(IndexedString(url)).at(0); project->projectFileManager()->removeFilesAndFolders(QList() << file ); //message box has to be accepted manually :( for (int i=51; i<100; ++i) { QFile f2(p.dir->path()+"/blub"+QString::number(i)); f2.remove(); } QTest::qWait(2000); QCOMPARE(project->projectItem()->fileList().count(), 1); } void TestProjectLoad::removeDirRecursive() { const TestProject p = makeProject(); { QFile f(p.dir->path()+"/sdf"); f.open(QIODevice::WriteOnly); f.close(); } { QDir(p.dir->path()).mkdir(QStringLiteral("blub")); for (int i=0; i<10; ++i) { QFile f(p.dir->path()+"/blub/file"+QString::number(i)); f.open(QIODevice::WriteOnly); f.close(); } } //close previously opened projects QTest::qWait(1000); //wait for projects to load foreach ( IProject* p, ICore::self()->projectController()->projects()) { ICore::self()->projectController()->closeProject(p); QTest::qWait(100); } QVERIFY(ICore::self()->projectController()->projects().isEmpty()); ICore::self()->projectController()->openProject(p.file); QSignalSpy spy(KDevelop::ICore::self()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*))); QVERIFY(spy.wait(20000)); IProject* project = ICore::self()->projectController()->projects().first(); QCOMPARE(project->projectFile().toUrl(), p.file); for (int i=0; i<1; ++i) { QUrl url = QUrl::fromLocalFile(p.dir->path()+"/blub").adjusted(QUrl::NormalizePathSegments); QCOMPARE(project->foldersForPath(IndexedString(url)).count(), 1); ProjectFolderItem* file = project->foldersForPath(IndexedString(url)).at(0); project->projectFileManager()->removeFilesAndFolders(QList() << file ); } QTest::qWait(2000); QCOMPARE(project->projectItem()->fileList().count(), 1); } bool createFile(const QString& path) { QFile f(path); if (!f.open(QIODevice::WriteOnly)) { qWarning() << f.errorString() << path; return false; } f.write(QByteArray::number(qrand())); f.write(QByteArray::number(qrand())); f.write(QByteArray::number(qrand())); f.write(QByteArray::number(qrand())); if (!f.flush()) { qWarning() << f.errorString() << path; return false; } f.close(); return true; } bool writeRandomStructure(QString path, int files) { QDir p(path); QString name = QString::number(qrand()); if (qrand() < RAND_MAX / 5) { if (!p.mkdir(name)) { return false; } //qDebug() << "wrote path" << path; path += '/' + name; } else { if (!createFile(path+'/'+name)) { return false; } //qDebug() << "wrote file" << path+"/"+name; } files--; if (files > 0) { if (!writeRandomStructure(path, files)) { return false; } } return true; } bool fillProject(int filesPerDir, int dirs, const TestProject& project, bool wait) { for(int i=0; i < dirs; ++i) { const QString name = "foox" + QString::number(i); if (!QDir(project.dir->path()).mkdir(name)) { return false; } if (!writeRandomStructure(project.dir->path() + "/" + name, filesPerDir)) { return false; } if (wait) { QTest::qWait(100); } } return true; } void TestProjectLoad::addLotsOfFiles() { TestProject p = makeProject(); ICore::self()->projectController()->openProject(p.file); QVERIFY(QSignalSpy(KDevelop::ICore::self()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*))).wait(2000)); QCOMPARE(ICore::self()->projectController()->projects().size(), 1); IProject* project = ICore::self()->projectController()->projects().first(); QCOMPARE(project->projectFile().toUrl(), p.file); QVERIFY(fillProject(50, 25, p, true)); QTest::qWait(2000); } void TestProjectLoad::addMultipleJobs() { const TestProject p1 = makeProject(); QVERIFY(fillProject(10, 25, p1, false)); const TestProject p2 = makeProject(); QVERIFY(fillProject(10, 25, p2, false)); QSignalSpy spy(ICore::self()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*))); ICore::self()->projectController()->openProject(p1.file); ICore::self()->projectController()->openProject(p2.file); const int wait = 25; const int maxWait = 2000; int waited = 0; while(waited < maxWait && spy.count() != 2) { QTest::qWait(wait); waited += wait; } QCOMPARE(ICore::self()->projectController()->projects().size(), 2); } void TestProjectLoad::raceJob() { // our goal here is to try to reproduce https://bugs.kde.org/show_bug.cgi?id=260741 // my idea is that this can be triggered by the following: // - list dir foo/bar containing lots of files // - remove dir foo while listjob is still running TestProject p = makeProject(); QDir dir(p.dir->path()); QVERIFY(dir.mkpath(QStringLiteral("test/zzzzz"))); for(int i = 0; i < 1000; ++i) { createFile(QString(p.dir->path() + "/test/zzzzz/%1").arg(i)); createFile(QString(p.dir->path() + "/test/%1").arg(i)); } ICore::self()->projectController()->openProject(p.file); QVERIFY(QSignalSpy(KDevelop::ICore::self()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*))).wait(2000)); QCOMPARE(ICore::self()->projectController()->projectCount(), 1); IProject *project = ICore::self()->projectController()->projectAt(0); QCOMPARE(project->projectFile().toUrl(), p.file); ProjectFolderItem* root = project->projectItem(); QCOMPARE(root->rowCount(), 1); ProjectBaseItem* testItem = root->child(0); QVERIFY(testItem->folder()); QCOMPARE(testItem->baseName(), QStringLiteral("test")); QCOMPARE(testItem->rowCount(), 1001); int last = testItem->children().size() - 1; ProjectBaseItem* asdfItem = testItem->children().at(last); QVERIFY(asdfItem->folder()); // move dir dir.rename(QStringLiteral("test"), QStringLiteral("test2")); // move sub dir dir.rename(QStringLiteral("test2/zzzzz"), QStringLiteral("test2/bla")); QTest::qWait(500); QCOMPARE(root->rowCount(), 1); testItem = root->child(0); QVERIFY(testItem->folder()); QCOMPARE(testItem->baseName(), QStringLiteral("test2")); } void TestProjectLoad::addDuringImport() { // our goal here is to try to reproduce an issue in the optimized filesForPath implementation // which requires the project to be associated to the model to function properly // to trigger this we create a big project, import it and then call filesForPath during // the import action TestProject p = makeProject(); QDir dir(p.dir->path()); QVERIFY(dir.mkpath(QStringLiteral("test/zzzzz"))); for(int i = 0; i < 1000; ++i) { createFile(QString(p.dir->path() + "/test/zzzzz/%1").arg(i)); createFile(QString(p.dir->path() + "/test/%1").arg(i)); } QSignalSpy spy(ICore::self()->projectController(), SIGNAL(projectAboutToBeOpened(KDevelop::IProject*))); ICore::self()->projectController()->openProject(p.file); // not yet ready QCOMPARE(ICore::self()->projectController()->projectCount(), 0); // but about to be opened QCOMPARE(spy.count(), 1); IProject* project = spy.value(0).at(0).value(); QVERIFY(project); QCOMPARE(project->path(), Path(KIO::upUrl(p.file))); QUrl file = p.file.resolved(QUrl(QStringLiteral("test/zzzzz/999"))); QVERIFY(QFile::exists(file.toLocalFile())); // this most probably is not yet loaded // and this should not crash QCOMPARE(project->itemsForPath(IndexedString(file)).size(), 0); // now delete that file and don't crash QFile::remove(file.toLocalFile()); // now create another file QUrl file2 = file.adjusted(QUrl::RemoveFilename); file2.setPath(file2.path() + "999v2"); createFile(file2.toLocalFile()); QVERIFY(!project->isReady()); // now wait for finish QVERIFY(QSignalSpy(KDevelop::ICore::self()->projectController(),\ SIGNAL(projectOpened(KDevelop::IProject*))).wait(2000)); QVERIFY(project->isReady()); // make sure our file removal + addition was properly tracked QCOMPARE(project->filesForPath(IndexedString(file)).size(), 0); QCOMPARE(project->filesForPath(IndexedString(file2)).size(), 1); //NOTE: this test is probabably incomplete, I bet there are some race conditions left, // esp. when adding a file at a point where the parent folder was already imported // or removing a file that was already imported } diff --git a/plugins/genericprojectmanager/test/test_projectload.h b/plugins/genericprojectmanager/test/test_projectload.h index 5729578e9..73c1e91bf 100644 --- a/plugins/genericprojectmanager/test/test_projectload.h +++ b/plugins/genericprojectmanager/test/test_projectload.h @@ -1,49 +1,49 @@ /* This file is part of KDevelop Copyright 2010 Niko Sams 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. */ #ifndef KDEVPLATFORM_PLUGIN_PROJECTLOADTEST_H #define KDEVPLATFORM_PLUGIN_PROJECTLOADTEST_H -#include +#include namespace KDevelop { class TestCore; } class TestProjectLoad : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void init(); void addRemoveFiles(); void removeDirRecursive(); void addLotsOfFiles(); void addMultipleJobs(); void raceJob(); void addDuringImport(); }; #endif diff --git a/plugins/git/gitmessagehighlighter.cpp b/plugins/git/gitmessagehighlighter.cpp index da7660d3e..078fdcbaf 100644 --- a/plugins/git/gitmessagehighlighter.cpp +++ b/plugins/git/gitmessagehighlighter.cpp @@ -1,115 +1,115 @@ /*************************************************************************** * Copyright 2011 Sergey Vidyuk * * Copyright 2012 Aleix Pol Gonzalez * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #include "gitmessagehighlighter.h" -#include +#include #include #include #include /// Recomended summary limit from http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html static const int summarySoftLimit = 50; /// Summary length limit causing warning messages from 'git push' static const int summaryHardLimit = 65; /// Recommended line kength from http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html static const int lineLenLimit = 72; void GitMessageHighlighter::applyErrorFormat(GitMessageHighlighter* text, bool warning, const QString& tooltip, int startPos, int endPos) { QTextCharFormat format; format.setFontUnderline(true); format.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline); format.setUnderlineColor(warning ? Qt::yellow : Qt::red); format.setToolTip(tooltip); text->setFormat(startPos, endPos, format); } GitMessageHighlighter::GitMessageHighlighter(QTextEdit* parent): Sonnet::Highlighter(parent) { } GitMessageHighlighter::~GitMessageHighlighter() { } void GitMessageHighlighter::highlightBlock(const QString& text) { int blockState = previousBlockState(); if (blockState < DetailedMessage) ++blockState; const int textLength = text.length(); int startPos = 0; int endPos = 0; while (startPos < textLength) { // Switch block state for multiline blocks if (startPos != 0 && blockState < DetailedMessage) ++blockState; endPos = text.indexOf('\n', startPos); if (endPos < 0) endPos = textLength; const int lineLength = endPos - startPos; Highlighter::highlightBlock( text ); switch (blockState) { case Summary: if (lineLength > summarySoftLimit) { applyErrorFormat(this, lineLength <= summaryHardLimit, i18n("Try to keep summary length below %1 characters.", summarySoftLimit), startPos, endPos ); } else { for(int i=startPos; i lineLenLimit) { applyErrorFormat(this, false, i18n("Try to keep line length below %1 characters.", lineLenLimit), startPos+lineLenLimit, endPos ); } break; } startPos = endPos; } setCurrentBlockState(blockState); } diff --git a/plugins/git/tests/test_git.h b/plugins/git/tests/test_git.h index faab55e26..d154e54ca 100644 --- a/plugins/git/tests/test_git.h +++ b/plugins/git/tests/test_git.h @@ -1,72 +1,72 @@ /*************************************************************************** * This file was partly taken from KDevelop's cvs plugin * * Copyright 2007 Robert Gruber * * * * Adapted for Git * * Copyright 2008 Evgeniy Ivanov * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #ifndef KDEVPLATFORM_PLUGIN_GIT_INIT_H #define KDEVPLATFORM_PLUGIN_GIT_INIT_H -#include +#include class GitPlugin; namespace KDevelop { class TestCore; } class GitInitTest: public QObject { Q_OBJECT private: void repoInit(); void addFiles(); void commitFiles(); private slots: void initTestCase(); void cleanupTestCase(); void init(); void cleanup(); void testInit(); void testReadAndSetConfigOption(); void testAdd(); void testCommit(); void testBranching(); void testBranch(const QString &branchName); void testMerge(); void revHistory(); void testAnnotation(); void testRemoveEmptyFolder(); void testRemoveEmptyFolderInFolder(); void testRemoveUnindexedFile(); void testRemoveFolderContainingUnversionedFiles(); void testDiff(); private: GitPlugin* m_plugin; void removeTempDirs(); }; #endif diff --git a/plugins/grepview/grepoutputdelegate.cpp b/plugins/grepview/grepoutputdelegate.cpp index 54b5f22d1..95a11f08e 100644 --- a/plugins/grepview/grepoutputdelegate.cpp +++ b/plugins/grepview/grepoutputdelegate.cpp @@ -1,174 +1,174 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright (C) 2007 Andreas Pakulat * * Copyright 2010 Julien Desgats * * * * 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 "grepoutputdelegate.h" #include "grepoutputmodel.h" #include -#include +#include #include #include #include #include -#include +#include #include #include #include GrepOutputDelegate* GrepOutputDelegate::m_self = nullptr; GrepOutputDelegate* GrepOutputDelegate::self() { Q_ASSERT(m_self); return m_self; } GrepOutputDelegate::GrepOutputDelegate( QObject* parent ) : QStyledItemDelegate(parent) { Q_ASSERT(!m_self); m_self = this; } GrepOutputDelegate::~GrepOutputDelegate() { m_self = nullptr; } void GrepOutputDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { // there is no function in QString to left-trim. A call to remove this this regexp does the job static const QRegExp leftspaces("^\\s*", Qt::CaseSensitive, QRegExp::RegExp); // rich text component const GrepOutputModel *model = dynamic_cast(index.model()); const GrepOutputItem *item = dynamic_cast(model->itemFromIndex(index)); QStyleOptionViewItem options = option; initStyleOption(&options, index); // building item representation QTextDocument doc; QTextCursor cur(&doc); QPalette::ColorGroup cg = options.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; QPalette::ColorRole cr = options.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text; QTextCharFormat fmt = cur.charFormat(); fmt.setFont(options.font); if(item && item->isText()) { // Use custom manual highlighting const KTextEditor::Range rng = item->change()->m_range; // the line number appears grayed fmt.setForeground(options.palette.brush(QPalette::Disabled, cr)); cur.insertText(i18n("Line %1: ",item->lineNumber()), fmt); // switch to normal color fmt.setForeground(options.palette.brush(cg, cr)); cur.insertText(item->text().left(rng.start().column()).remove(leftspaces), fmt); fmt.setFontWeight(QFont::Bold); if ( !(options.state & QStyle::State_Selected) ) { QColor bgHighlight = option.palette.color(QPalette::AlternateBase); fmt.setBackground(bgHighlight); } cur.insertText(item->text().mid(rng.start().column(), rng.end().column() - rng.start().column()), fmt); fmt.clearBackground(); fmt.setFontWeight(QFont::Normal); cur.insertText(item->text().right(item->text().length() - rng.end().column()), fmt); }else{ QString text; if(item) text = item->text(); else text = index.data().toString(); // Simply insert the text as html. We use this for the titles. doc.setHtml(text); } painter->save(); options.text = QString(); // text will be drawn separately options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter, options.widget); // set correct draw area QRect clip = options.widget->style()->subElementRect(QStyle::SE_ItemViewItemText, &options); QFontMetrics metrics(options.font); painter->translate(clip.topLeft() - QPoint(0, metrics.descent())); // We disable the clipping for now, as it leads to strange clipping errors // clip.setTopLeft(QPoint(0,0)); // painter->setClipRect(clip); QAbstractTextDocumentLayout::PaintContext ctx; // ctx.clip = clip; painter->setBackground(Qt::transparent); doc.documentLayout()->draw(painter, ctx); painter->restore(); } QSize GrepOutputDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { const GrepOutputModel *model = dynamic_cast(index.model()); const GrepOutputItem *item = model ? dynamic_cast(model->itemFromIndex(index)) : nullptr; QSize ret = QStyledItemDelegate::sizeHint(option, index); //take account of additional width required for highlighting (bold text) //and line numbers. These are not included in the default Qt size calculation. if(item && item->isText()) { QFont font = option.font; QFontMetrics metrics(font); font.setBold(true); QFontMetrics bMetrics(font); const KTextEditor::Range rng = item->change()->m_range; int width = metrics.width(item->text().left(rng.start().column())) + metrics.width(item->text().right(item->text().length() - rng.end().column())) + bMetrics.width(item->text().mid(rng.start().column(), rng.end().column() - rng.start().column())) + option.fontMetrics.width(i18n("Line %1: ",item->lineNumber())) + std::max(option.decorationSize.width(), 0); ret.setWidth(width); }else{ // This is only used for titles, so not very performance critical QString text; if(item) text = item->text(); else text = index.data().toString(); QTextDocument doc; doc.setDocumentMargin(0); doc.setHtml(text); QSize newSize = doc.size().toSize(); if(newSize.height() > ret.height()) ret.setHeight(newSize.height()); } return ret; } diff --git a/plugins/grepview/tests/test_findreplace.h b/plugins/grepview/tests/test_findreplace.h index 236885e0b..a78c22135 100644 --- a/plugins/grepview/tests/test_findreplace.h +++ b/plugins/grepview/tests/test_findreplace.h @@ -1,61 +1,61 @@ /*************************************************************************** * Copyright 1999-2001 Bernd Gehrmann and the KDevelop Team * * bernd@kdevelop.org * * Copyright 2010 Julien Desgats * * * * 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. * * * ***************************************************************************/ #ifndef KDEVPLATFORM_PLUGIN_REPLACETEST_H #define KDEVPLATFORM_PLUGIN_REPLACETEST_H -#include +#include #include #include #include namespace KDevelop { class TestCore; } class GrepViewPlugin; class FindReplaceTest : public QObject { Q_OBJECT public: struct Match { Match() {} Match(int l,int s,int e) : line(l), start(s), end(e) {} int line; int start; int end; }; typedef QList MatchList; typedef QPair File; /// Represent a file with name => content typedef QList FileList; private: GrepViewPlugin* m_plugin; private slots: void initTestCase(); void cleanupTestCase(); void testFind(); void testFind_data(); void testReplace(); void testReplace_data(); }; Q_DECLARE_METATYPE(FindReplaceTest::MatchList) Q_DECLARE_METATYPE(FindReplaceTest::FileList) #endif // KDEVPLATFORM_PLUGIN_REPLACETEST_H diff --git a/plugins/konsole/kdevkonsoleviewplugin.h b/plugins/konsole/kdevkonsoleviewplugin.h index 4092caaa7..ce18f554f 100644 --- a/plugins/konsole/kdevkonsoleviewplugin.h +++ b/plugins/konsole/kdevkonsoleviewplugin.h @@ -1,40 +1,40 @@ /*************************************************************************** * Copyright 2003, 2006 Adam Treat * * Copyright 2007 Andreas Pakulat * * * * 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. * * * ***************************************************************************/ #ifndef KDEVPLATFORM_PLUGIN_KDEVKONSOLEVIEWPLUGIN_H #define KDEVPLATFORM_PLUGIN_KDEVKONSOLEVIEWPLUGIN_H #include -#include +#include class KDevKonsoleViewFactory; class KPluginFactory; class KDevKonsoleViewPlugin : public KDevelop::IPlugin { Q_OBJECT public: KDevKonsoleViewPlugin( KPluginFactory*, QObject *parent, const QVariantList & = QVariantList() ); ~KDevKonsoleViewPlugin() override; void unload() override; KPluginFactory* konsoleFactory() const; private: KPluginFactory* m_konsoleFactory; KDevKonsoleViewFactory *m_viewFactory; }; #endif diff --git a/plugins/perforce/test/test_perforce.h b/plugins/perforce/test/test_perforce.h index a41ed3b8f..70438d9dc 100644 --- a/plugins/perforce/test/test_perforce.h +++ b/plugins/perforce/test/test_perforce.h @@ -1,58 +1,58 @@ /*************************************************************************** * This file was inspired by KDevelop's git plugin * * Copyright 2008 Evgeniy Ivanov * * * * Adapted for Perforce * * Copyright 2011 Morten Danielsen Volden * * * * 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, see . * ***************************************************************************/ #ifndef PERFORCEPLUGIN_TEST_H #define PERFORCEPLUGIN_TEST_H -#include +#include class PerforcePlugin; namespace KDevelop { class TestCore; } class PerforcePluginTest : public QObject { Q_OBJECT private slots: void init(); void cleanup(); void testAdd(); void testEdit(); void testEditMultipleFiles(); void testStatus(); void testAnnotate(); void testHistory(); void testRevert(); void testUpdateFile(); void testUpdateDir(); void testCommit(); void testDiff(); private: PerforcePlugin* m_plugin; KDevelop::TestCore* m_core; void removeTempDirsIfAny(); void createNewTempDirs(); }; #endif diff --git a/plugins/problemreporter/problemreporterplugin.h b/plugins/problemreporter/problemreporterplugin.h index 142af2f76..272d3b26c 100644 --- a/plugins/problemreporter/problemreporterplugin.h +++ b/plugins/problemreporter/problemreporterplugin.h @@ -1,83 +1,83 @@ /* * KDevelop Problem Reporter * * Copyright 2006 Adam Treat * Copyright 2006-2007 Hamish Rodda * Copyright 2007-2008 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. */ #ifndef KDEVPLATFORM_PLUGIN_PROBLEMREPORTERPLUGIN_H #define KDEVPLATFORM_PLUGIN_PROBLEMREPORTERPLUGIN_H #include -#include +#include #include #include #include Q_DECLARE_LOGGING_CATEGORY(PLUGIN_PROBLEMREPORTER) namespace KTextEditor { class Document; } namespace KDevelop { class IDocument; } class ProblemHighlighter; class ProblemReporterModel; class ProblemReporterPlugin : public KDevelop::IPlugin { Q_OBJECT public: explicit ProblemReporterPlugin(QObject* parent, const QVariantList& = QVariantList()); ~ProblemReporterPlugin() override; KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context* context) override; // KDevelop::Plugin methods void unload() override; ProblemReporterModel* model() const; private Q_SLOTS: void updateReady(const KDevelop::IndexedString& url, const KDevelop::ReferencedTopDUContext&); void updateHighlight(const KDevelop::IndexedString& url); void textDocumentCreated(KDevelop::IDocument* document); void documentActivated(KDevelop::IDocument* document); void showModel(const QString& id); private: void updateOpenedDocumentsHighlight(); class ProblemReporterFactory* m_factory; ProblemReporterModel* m_model; QHash m_highlighters; QSet m_reHighlightNeeded; public slots: void documentClosed(KDevelop::IDocument*); }; #endif // KDEVPLATFORM_PLUGIN_PROBLEMREPORTERPLUGIN_H // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/plugins/projectfilter/tests/test_projectfilter.cpp b/plugins/projectfilter/tests/test_projectfilter.cpp index 6accdce0a..618a67254 100644 --- a/plugins/projectfilter/tests/test_projectfilter.cpp +++ b/plugins/projectfilter/tests/test_projectfilter.cpp @@ -1,398 +1,398 @@ /* * This file is part of KDevelop * Copyright 2013 Milian Wolff * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #include "test_projectfilter.h" -#include +#include #include #include #include #include #include "../projectfilter.h" QTEST_GUILESS_MAIN(TestProjectFilter); using namespace KDevelop; typedef QSharedPointer TestFilter; Q_DECLARE_METATYPE(TestFilter) namespace { const bool Invalid = false; const bool Valid = true; const bool Folder = true; const bool File = false; struct MatchTest { QString path; bool isFolder; bool shouldMatch; }; void addTests(const QString& tag, const TestProject& project, const TestFilter& filter, MatchTest* tests, uint numTests) { for (uint i = 0; i < numTests; ++i) { const MatchTest& test = tests[i]; QTest::newRow(qstrdup(qPrintable(tag + ':' + test.path))) << filter << Path(project.path(), test.path) << test.isFolder << test.shouldMatch; if (test.isFolder) { // also test folder with trailing slash - should not make a difference QTest::newRow(qstrdup(qPrintable(tag + ':' + test.path + '/'))) << filter << Path(project.path(), test.path) << test.isFolder << test.shouldMatch; } } } ///FIXME: remove once we can use c++11 #define ADD_TESTS(tag, project, filter, tests) addTests(QStringLiteral(tag), project, filter, tests, sizeof(tests) / sizeof(tests[0])) struct BenchData { BenchData(const Path &path = Path(), bool isFolder = false) : path(path) , isFolder(isFolder) {} Path path; bool isFolder; }; } Q_DECLARE_METATYPE(QVector) void TestProjectFilter::initTestCase() { AutoTestShell::init(); TestCore::initialize(Core::NoUi); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType >(); } void TestProjectFilter::cleanupTestCase() { TestCore::shutdown(); } void TestProjectFilter::match() { QFETCH(TestFilter, filter); QFETCH(KDevelop::Path, path); QFETCH(bool, isFolder); QFETCH(bool, expectedIsValid); QCOMPARE(filter->isValid(path, isFolder), expectedIsValid); } void TestProjectFilter::match_data() { QTest::addColumn("filter"); QTest::addColumn("path"); QTest::addColumn("isFolder"); QTest::addColumn("expectedIsValid"); { // test default filters const TestProject project; TestFilter filter(new ProjectFilter(&project, deserialize(defaultFilters()))); QTest::newRow("projectRoot") << filter << project.path() << Folder << Valid; QTest::newRow("project.kdev4") << filter << project.projectFile() << File << Invalid; MatchTest tests[] = { //{path, isFolder, isValid} {QStringLiteral(".kdev4"), Folder, Invalid}, {QStringLiteral("folder"), Folder, Valid}, {QStringLiteral("folder/folder"), Folder, Valid}, {QStringLiteral("file"), File, Valid}, {QStringLiteral("folder/file"), File, Valid}, {QStringLiteral(".file"), File, Invalid}, {QStringLiteral(".folder"), Folder, Invalid}, {QStringLiteral("folder/.folder"), Folder, Invalid}, {QStringLiteral("folder/.file"), File, Invalid}, {QStringLiteral(".git"), Folder, Invalid}, {QStringLiteral(".gitignore"), File, Valid}, {QStringLiteral(".gitmodules"), File, Valid}, {QStringLiteral("_darcs"), Folder, Invalid}, {QStringLiteral("_svn"), Folder, Invalid}, {QStringLiteral(".svn"), Folder, Invalid}, {QStringLiteral("CVS"), Folder, Invalid}, {QStringLiteral("SCCS"), Folder, Invalid}, {QStringLiteral(".hg"), Folder, Invalid}, {QStringLiteral(".bzr"), Folder, Invalid}, {QStringLiteral("foo.o"), File, Invalid}, {QStringLiteral("foo.so"), File, Invalid}, {QStringLiteral("foo.so.1"), File, Invalid}, {QStringLiteral("foo.a"), File, Invalid}, {QStringLiteral("moc_foo.cpp"), File, Invalid}, {QStringLiteral("ui_foo.h"), File, Invalid}, {QStringLiteral("qrc_foo.cpp"), File, Invalid}, {QStringLiteral("foo.cpp~"), File, Invalid}, {QStringLiteral(".foo.cpp.kate-swp"), File, Invalid}, {QStringLiteral(".foo.cpp.swp"), File, Invalid} }; ADD_TESTS("default", project, filter, tests); } { // test exclude files, basename const TestProject project; const Filters filters = Filters() << Filter(SerializedFilter(QStringLiteral("*.cpp"), Filter::Files)); TestFilter filter(new ProjectFilter(&project, filters)); QTest::newRow("projectRoot") << filter << project.path() << Folder << Valid; QTest::newRow("project.kdev4") << filter << project.projectFile() << File << Invalid; MatchTest tests[] = { //{path, isFolder, isValid} {QStringLiteral(".kdev4"), Folder, Invalid}, {QStringLiteral("folder"), Folder, Valid}, {QStringLiteral("file"), File, Valid}, {QStringLiteral("file.cpp"), File, Invalid}, {QStringLiteral("folder.cpp"), Folder, Valid}, {QStringLiteral("folder/file.cpp"), File, Invalid}, {QStringLiteral("folder/folder.cpp"), Folder, Valid} }; ADD_TESTS("exclude:*.cpp", project, filter, tests); } { // test excludes on folders const TestProject project; const Filters filters = Filters() << Filter(SerializedFilter(QStringLiteral("foo"), Filter::Folders)); TestFilter filter(new ProjectFilter(&project, filters)); QTest::newRow("projectRoot") << filter << project.path() << Folder << Valid; QTest::newRow("project.kdev4") << filter << project.projectFile() << File << Invalid; MatchTest tests[] = { //{path, isFolder, isValid} {QStringLiteral(".kdev4"), Folder, Invalid}, {QStringLiteral("folder"), Folder, Valid}, {QStringLiteral("file"), File, Valid}, {QStringLiteral("foo"), Folder, Invalid}, {QStringLiteral("folder/file"), File, Valid}, {QStringLiteral("folder/foo"), Folder, Invalid}, {QStringLiteral("folder/foo"), File, Valid} }; ADD_TESTS("exclude:foo", project, filter, tests); } { // test includes const TestProject project; const Filters filters = Filters() << Filter(SerializedFilter(QStringLiteral("*"), Filter::Files)) << Filter(SerializedFilter(QStringLiteral("*.cpp"), Filter::Files, Filter::Inclusive)); TestFilter filter(new ProjectFilter(&project, filters)); QTest::newRow("projectRoot") << filter << project.path() << Folder << Valid; QTest::newRow("project.kdev4") << filter << project.projectFile() << File << Invalid; MatchTest tests[] = { //{path, isFolder, isValid} {QStringLiteral(".kdev4"), Folder, Invalid}, {QStringLiteral("folder"), Folder, Valid}, {QStringLiteral("file"), File, Invalid}, {QStringLiteral("file.cpp"), File, Valid}, {QStringLiteral(".file.cpp"), File, Valid}, {QStringLiteral("folder/file.cpp"), File, Valid}, {QStringLiteral("folder/.file.cpp"), File, Valid} }; ADD_TESTS("include:*.cpp", project, filter, tests); project.projectConfiguration(); } { // test mixed stuff const TestProject project; const Filters filters = Filters() << Filter(SerializedFilter(QStringLiteral("*"), Filter::Files, Filter::Exclusive)) << Filter(SerializedFilter(QStringLiteral("*.inc"), Filter::Files, Filter::Inclusive)) << Filter(SerializedFilter(QStringLiteral("*ex.inc"), Filter::Files, Filter::Exclusive)) << Filter(SerializedFilter(QStringLiteral("bar"), Filter::Folders, Filter::Exclusive)); TestFilter filter(new ProjectFilter(&project, filters)); QTest::newRow("projectRoot") << filter << project.path() << Folder << Valid; QTest::newRow("project.kdev4") << filter << project.projectFile() << File << Invalid; MatchTest tests[] = { //{path, isFolder, isValid} {QStringLiteral(".kdev4"), Folder, Invalid}, {QStringLiteral("folder"), Folder, Valid}, {QStringLiteral("file"), File, Invalid}, {QStringLiteral("file.inc"), File, Valid}, {QStringLiteral("file.ex.inc"), File, Invalid}, {QStringLiteral("folder/file"), File, Invalid}, {QStringLiteral("folder/file.inc"), File, Valid}, {QStringLiteral("folder/file.ex.inc"), File, Invalid}, {QStringLiteral("bar"), Folder, Invalid}, }; ADD_TESTS("mixed", project, filter, tests); } { // relative path const TestProject project; const Filters filters = Filters() << Filter(SerializedFilter(QStringLiteral("/foo/*bar"), Filter::Targets(Filter::Files | Filter::Folders))); TestFilter filter(new ProjectFilter(&project, filters)); QTest::newRow("projectRoot") << filter << project.path() << Folder << Valid; QTest::newRow("project.kdev4") << filter << project.projectFile() << File << Invalid; MatchTest tests[] = { //{path, isFolder, isValid} {QStringLiteral(".kdev4"), Folder, Invalid}, {QStringLiteral("foo"), Folder, Valid}, {QStringLiteral("bar"), File, Valid}, {QStringLiteral("foo/bar"), Folder, Invalid}, {QStringLiteral("foo/bar"), File, Invalid}, {QStringLiteral("foo/asdf/bar"), Folder, Invalid}, {QStringLiteral("foo/asdf/bar"), File, Invalid}, {QStringLiteral("foo/asdf_bar"), Folder, Invalid}, {QStringLiteral("foo/asdf_bar"), File, Invalid}, {QStringLiteral("asdf/bar"), File, Valid}, {QStringLiteral("asdf/foo/bar"), File, Valid}, }; ADD_TESTS("relative", project, filter, tests); } { // trailing slash const TestProject project; const Filters filters = Filters() << Filter(SerializedFilter(QStringLiteral("bar/"), Filter::Targets(Filter::Files | Filter::Folders))); TestFilter filter(new ProjectFilter(&project, filters)); QTest::newRow("projectRoot") << filter << project.path() << Folder << Valid; QTest::newRow("project.kdev4") << filter << project.projectFile() << File << Invalid; MatchTest tests[] = { //{path, isFolder, isValid} {QStringLiteral(".kdev4"), Folder, Invalid}, {QStringLiteral("foo"), Folder, Valid}, {QStringLiteral("bar"), File, Valid}, {QStringLiteral("bar"), Folder, Invalid}, {QStringLiteral("foo/bar"), File, Valid}, {QStringLiteral("foo/bar"), Folder, Invalid} }; ADD_TESTS("trailingslash", project, filter, tests); } { // escaping const TestProject project; const Filters filters = Filters() << Filter(SerializedFilter(QStringLiteral("foo\\*bar"), Filter::Files)); TestFilter filter(new ProjectFilter(&project, filters)); QTest::newRow("projectRoot") << filter << project.path() << Folder << Valid; QTest::newRow("project.kdev4") << filter << project.projectFile() << File << Invalid; MatchTest tests[] = { //{path, isFolder, isValid} {QStringLiteral(".kdev4"), Folder, Invalid}, {QStringLiteral("foobar"), Folder, Valid}, {QStringLiteral("fooasdfbar"), File, Valid}, {QStringLiteral("foo*bar"), File, Invalid}, {QStringLiteral("foo/bar"), Folder, Valid} }; ADD_TESTS("escaping", project, filter, tests); } } static QVector createBenchData(const Path& base, int folderDepth, int foldersPerFolder, int filesPerFolder) { QVector data; data << BenchData(base, true); for(int i = 0; i < filesPerFolder; ++i) { if (i % 2) { data << BenchData(Path(base, QStringLiteral("file%1.cpp").arg(i)), false); } else { data << BenchData(Path(base, QStringLiteral("file%1.h").arg(i)), true); } } for(int i = 0; i < foldersPerFolder && folderDepth > 0; ++i) { data += createBenchData(Path(base, QStringLiteral("folder%1").arg(i)), folderDepth - 1, foldersPerFolder, filesPerFolder); } return data; } void TestProjectFilter::bench() { QFETCH(TestFilter, filter); QFETCH(QVector, data); QBENCHMARK { foreach(const BenchData& bench, data) { filter->isValid(bench.path, bench.isFolder); } } } void TestProjectFilter::bench_data() { QTest::addColumn("filter"); QTest::addColumn >("data"); const TestProject project; QVector > dataSets = QVector >() << createBenchData(project.path(), 3, 5, 10) << createBenchData(project.path(), 3, 5, 20) << createBenchData(project.path(), 4, 5, 10) << createBenchData(project.path(), 3, 10, 10); { TestFilter filter(new ProjectFilter(&project, Filters())); foreach(const QVector& data, dataSets) { QTest::newRow(qstrdup(QByteArray("baseline-") + QByteArray::number(data.size()))) << filter << data; } } { TestFilter filter(new ProjectFilter(&project, deserialize(defaultFilters()))); foreach(const QVector& data, dataSets) { QTest::newRow(qstrdup(QByteArray("defaults-") + QByteArray::number(data.size()))) << filter << data; } } } diff --git a/plugins/projectmanagerview/projectmodelsaver.cpp b/plugins/projectmanagerview/projectmodelsaver.cpp index e2f2ea51d..c1fe18a9b 100644 --- a/plugins/projectmanagerview/projectmodelsaver.cpp +++ b/plugins/projectmanagerview/projectmodelsaver.cpp @@ -1,73 +1,73 @@ /* This file is part of KDevelop Copyright 2012 Andrew Fuller 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 "projectmodelsaver.h" #include "projecttreeview.h" #include "project/projectmodel.h" #include #include -#include +#include #include namespace KDevelop { ProjectModelSaver::ProjectModelSaver() : m_project(nullptr) { } void ProjectModelSaver::setProject(IProject* project) { m_project = project; } QModelIndex ProjectModelSaver::indexFromConfigString(const QAbstractItemModel *viewModel, const QString &key) const { const KDevelop::ProjectModel *projectModel = KDevelop::ICore::self()->projectController()->projectModel(); const QModelIndex sourceIndex = projectModel->pathToIndex(key.split('/')); if ( m_project && sourceIndex.isValid() ) { ProjectBaseItem* item = projectModel->itemFromIndex(sourceIndex); if ( item && item->project() == m_project ) { return ProjectTreeView::mapFromSource(qobject_cast(viewModel), sourceIndex); } } return QModelIndex(); } QString ProjectModelSaver::indexToConfigString(const QModelIndex& index) const { if( !index.isValid() || !m_project ) { return QString(); } ProjectBaseItem* item = index.data(ProjectModel::ProjectItemRole).value(); if ( !item || item->project() != m_project ) { return QString(); } return ICore::self()->projectController()->projectModel()->pathFromIndex( item->index() ).join(QLatin1Char('/')); } } diff --git a/plugins/projectmanagerview/projecttreeview.h b/plugins/projectmanagerview/projecttreeview.h index 0c9f576ac..98f88ce6a 100644 --- a/plugins/projectmanagerview/projecttreeview.h +++ b/plugins/projectmanagerview/projecttreeview.h @@ -1,79 +1,79 @@ /* This file is part of KDevelop Copyright 2005 Roberto Raggi Copyright 2007 Andreas Pakulat 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. */ #ifndef KDEVPLATFORM_PLUGIN_PROJECTTREEVIEW_H #define KDEVPLATFORM_PLUGIN_PROJECTTREEVIEW_H #include -#include +#include class QAbstractProxyModel; namespace KDevelop { class IProject; class ProjectBaseItem; class NavigationToolTip; class Path; } class ProjectTreeView: public QTreeView { Q_OBJECT public: explicit ProjectTreeView( QWidget *parent = nullptr ); ~ProjectTreeView() override; static QModelIndex mapFromSource(const QAbstractProxyModel* proxy, const QModelIndex& sourceIdx); bool event(QEvent* event) override; Q_SIGNALS: void activate( const KDevelop::Path &url ); protected Q_SLOTS: void slotActivated( const QModelIndex &index ); void popupContextMenu( const QPoint &pos ); void openProjectConfig(); void saveState(KDevelop::IProject* project = nullptr); void restoreState(KDevelop::IProject* project = nullptr); void aboutToShutdown(); void projectClosed(KDevelop::IProject* project); void rowsInserted(const QModelIndex& parent, int start, int end) override; void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override; protected: void keyPressEvent(QKeyEvent *event) override; void dropEvent(QDropEvent* event) override; void drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const override; private: QModelIndex mapFromItem(const KDevelop::ProjectBaseItem* item); KDevelop::ProjectBaseItem* itemAtPos(QPoint pos); QList selectedProjects(); KDevelop::IProject* getCurrentProject(); QPointer m_previousSelection; QPointer m_tooltip; QPersistentModelIndex m_idx; }; #endif // KDEVPLATFORM_PLUGIN_PROJECTTREEVIEW_H diff --git a/plugins/quickopen/quickopenplugin.cpp b/plugins/quickopen/quickopenplugin.cpp index 59df2fc82..c2beba655 100644 --- a/plugins/quickopen/quickopenplugin.cpp +++ b/plugins/quickopen/quickopenplugin.cpp @@ -1,1170 +1,1170 @@ /* * 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 "quickopenwidget.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 "quickopenmodel.h" #include "projectfilequickopen.h" #include "projectitemquickopen.h" #include "declarationlistquickopen.h" #include "documentationquickopenprovider.h" #include "actionsquickopenprovider.h" #include "debug.h" #include #include #include #include #include #include Q_LOGGING_CATEGORY(PLUGIN_QUICKOPEN, "kdevplatform.plugins.quickopen") using namespace KDevelop; 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) { } QString objectNameForLine() override { return QStringLiteral("Quickopen"); } void setItems(const QStringList& scopes, const QStringList& items) { m_scopes = scopes; m_items = items; } QuickOpenWidget* createWidget() override { 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 OutlineFilter : public DUChainUtils::DUChainItemFilter { public: enum OutlineMode { Functions, FunctionsAndClasses }; explicit OutlineFilter(QList& _items, OutlineMode _mode = FunctionsAndClasses) : items(_items) , mode(_mode) { } bool accept(Declaration* decl) override { 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; } } bool accept(DUContext* ctx) override { 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_WITH_JSON(KDevQuickOpenFactory, "kdevquickopen.json", registerPlugin(); ) Declaration * cursorDeclaration() { KTextEditor::View* view = ICore::self()->documentController()->activeTextDocumentView(); if (!view) { return nullptr; } KDevelop::DUChainReadLocker lock(DUChain::lock()); return DUChainUtils::declarationForDefinition(DUChainUtils::itemUnderCursor(view->document()->url(), KTextEditor::Cursor(view->cursorPosition())).declaration); } ///The first definition that belongs to a context that surrounds the current cursor Declaration* cursorContextDeclaration() { KTextEditor::View* view = ICore::self()->documentController()->activeTextDocumentView(); if (!view) { return nullptr; } KDevelop::DUChainReadLocker lock(DUChain::lock()); TopDUContext* ctx = DUChainUtils::standardContextForUrl(view->document()->url()); if (!ctx) { return nullptr; } KTextEditor::Cursor cursor(view->cursorPosition()); DUContext* subCtx = ctx->findContext(ctx->transformToLocalRevision(cursor)); while (subCtx && !subCtx->owner()) subCtx = subCtx->parentContext(); Declaration* definition = nullptr; if (!subCtx || !subCtx->owner()) { definition = DUChainUtils::declarationInLine(cursor, ctx); } else { definition = subCtx->owner(); } if (!definition) { return nullptr; } 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) { qCDebug(PLUGIN_QUICKOPEN) << "Got no standard context"; return QString(); } AbstractType::Ptr t = decl->abstractType(); IdentifiedType* idType = dynamic_cast(t.data()); if (idType && idType->declaration(context)) { decl = idType->declaration(context); } if (!decl->qualifiedIdentifier().isEmpty()) { return decl->qualifiedIdentifier().last().identifier().str(); } return QString(); } QuickOpenLineEdit* QuickOpenPlugin::createQuickOpenLineWidget() { return new QuickOpenLineEdit(new StandardQuickOpenWidgetCreator(QStringList(), QStringList())); } QuickOpenLineEdit* QuickOpenPlugin::quickOpenLine(QString name) { QList lines = ICore::self()->uiController()->activeMainWindow()->findChildren(name); foreach (QuickOpenLineEdit* line, lines) { if (line->isVisible()) { return line; } } return nullptr; } static QuickOpenPlugin* staticQuickOpenPlugin = nullptr; QuickOpenPlugin* QuickOpenPlugin::self() { return staticQuickOpenPlugin; } void QuickOpenPlugin::createActionsForMainWindow(Sublime::MainWindow* /*window*/, QString& xmlFile, KActionCollection& actions) { xmlFile = QStringLiteral("kdevquickopen.rc"); QAction* quickOpen = actions.addAction(QStringLiteral("quick_open")); quickOpen->setText(i18n("&Quick Open")); quickOpen->setIcon(QIcon::fromTheme(QStringLiteral("quickopen"))); actions.setDefaultShortcut(quickOpen, Qt::CTRL | Qt::ALT | Qt::Key_Q); connect(quickOpen, &QAction::triggered, this, &QuickOpenPlugin::quickOpen); QAction* quickOpenFile = actions.addAction(QStringLiteral("quick_open_file")); quickOpenFile->setText(i18n("Quick Open &File")); quickOpenFile->setIcon(QIcon::fromTheme(QStringLiteral("quickopen-file"))); actions.setDefaultShortcut(quickOpenFile, Qt::CTRL | Qt::ALT | Qt::Key_O); connect(quickOpenFile, &QAction::triggered, this, &QuickOpenPlugin::quickOpenFile); QAction* quickOpenClass = actions.addAction(QStringLiteral("quick_open_class")); quickOpenClass->setText(i18n("Quick Open &Class")); quickOpenClass->setIcon(QIcon::fromTheme(QStringLiteral("quickopen-class"))); actions.setDefaultShortcut(quickOpenClass, Qt::CTRL | Qt::ALT | Qt::Key_C); connect(quickOpenClass, &QAction::triggered, this, &QuickOpenPlugin::quickOpenClass); QAction* quickOpenFunction = actions.addAction(QStringLiteral("quick_open_function")); quickOpenFunction->setText(i18n("Quick Open &Function")); quickOpenFunction->setIcon(QIcon::fromTheme(QStringLiteral("quickopen-function"))); actions.setDefaultShortcut(quickOpenFunction, Qt::CTRL | Qt::ALT | Qt::Key_M); connect(quickOpenFunction, &QAction::triggered, this, &QuickOpenPlugin::quickOpenFunction); QAction* quickOpenAlreadyOpen = actions.addAction(QStringLiteral("quick_open_already_open")); quickOpenAlreadyOpen->setText(i18n("Quick Open &Already Open File")); quickOpenAlreadyOpen->setIcon(QIcon::fromTheme(QStringLiteral("quickopen-file"))); connect(quickOpenAlreadyOpen, &QAction::triggered, this, &QuickOpenPlugin::quickOpenOpenFile); QAction* quickOpenDocumentation = actions.addAction(QStringLiteral("quick_open_documentation")); quickOpenDocumentation->setText(i18n("Quick Open &Documentation")); quickOpenDocumentation->setIcon(QIcon::fromTheme(QStringLiteral("quickopen-documentation"))); actions.setDefaultShortcut(quickOpenDocumentation, Qt::CTRL | Qt::ALT | Qt::Key_D); connect(quickOpenDocumentation, &QAction::triggered, this, &QuickOpenPlugin::quickOpenDocumentation); QAction* quickOpenActions = actions.addAction(QStringLiteral("quick_open_actions")); quickOpenActions->setText(i18n("Quick Open &Actions")); actions.setDefaultShortcut(quickOpenActions, Qt::CTRL | Qt::ALT | Qt::Key_A); connect(quickOpenActions, &QAction::triggered, this, &QuickOpenPlugin::quickOpenActions); m_quickOpenDeclaration = actions.addAction(QStringLiteral("quick_open_jump_declaration")); m_quickOpenDeclaration->setText(i18n("Jump to Declaration")); m_quickOpenDeclaration->setIcon(QIcon::fromTheme(QStringLiteral("go-jump-declaration"))); actions.setDefaultShortcut(m_quickOpenDeclaration, Qt::CTRL | Qt::Key_Period); connect(m_quickOpenDeclaration, &QAction::triggered, this, &QuickOpenPlugin::quickOpenDeclaration, Qt::QueuedConnection); m_quickOpenDefinition = actions.addAction(QStringLiteral("quick_open_jump_definition")); m_quickOpenDefinition->setText(i18n("Jump to Definition")); m_quickOpenDefinition->setIcon(QIcon::fromTheme(QStringLiteral("go-jump-definition"))); actions.setDefaultShortcut(m_quickOpenDefinition, Qt::CTRL | Qt::Key_Comma); connect(m_quickOpenDefinition, &QAction::triggered, this, &QuickOpenPlugin::quickOpenDefinition, Qt::QueuedConnection); QWidgetAction* quickOpenLine = new QWidgetAction(this); quickOpenLine->setText(i18n("Embedded Quick Open")); // actions.setDefaultShortcut( quickOpenLine, Qt::CTRL | Qt::ALT | Qt::Key_E ); // connect(quickOpenLine, SIGNAL(triggered(bool)), this, SLOT(quickOpenLine(bool))); quickOpenLine->setDefaultWidget(createQuickOpenLineWidget()); actions.addAction(QStringLiteral("quick_open_line"), quickOpenLine); QAction* quickOpenNextFunction = actions.addAction(QStringLiteral("quick_open_next_function")); quickOpenNextFunction->setText(i18n("Next Function")); actions.setDefaultShortcut(quickOpenNextFunction, Qt::CTRL | Qt::ALT | Qt::Key_PageDown); connect(quickOpenNextFunction, &QAction::triggered, this, &QuickOpenPlugin::nextFunction); QAction* quickOpenPrevFunction = actions.addAction(QStringLiteral("quick_open_prev_function")); quickOpenPrevFunction->setText(i18n("Previous Function")); actions.setDefaultShortcut(quickOpenPrevFunction, Qt::CTRL | Qt::ALT | Qt::Key_PageUp); connect(quickOpenPrevFunction, &QAction::triggered, this, &QuickOpenPlugin::previousFunction); QAction* quickOpenNavigateFunctions = actions.addAction(QStringLiteral("quick_open_outline")); quickOpenNavigateFunctions->setText(i18n("Outline")); actions.setDefaultShortcut(quickOpenNavigateFunctions, Qt::CTRL | Qt::ALT | Qt::Key_N); connect(quickOpenNavigateFunctions, &QAction::triggered, this, &QuickOpenPlugin::quickOpenNavigateFunctions); } QuickOpenPlugin::QuickOpenPlugin(QObject* parent, const QVariantList&) : KDevelop::IPlugin(QStringLiteral("kdevquickopen"), parent) { staticQuickOpenPlugin = this; m_model = new QuickOpenModel(nullptr); KConfigGroup quickopengrp = KSharedConfig::openConfig()->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); } { m_documentationItemData = new DocumentationQuickOpenProvider; QStringList scopes, items; scopes << i18n("Includes"); items << i18n("Documentation"); m_model->registerProvider(scopes, items, m_documentationItemData); } { m_actionsItemData = new ActionsQuickOpenProvider; QStringList scopes, items; scopes << i18n("Includes"); items << i18n("Actions"); m_model->registerProvider(scopes, items, m_actionsItemData); } } QuickOpenPlugin::~QuickOpenPlugin() { freeModel(); delete m_model; delete m_projectFileData; delete m_projectItemData; delete m_openFilesData; delete m_documentationItemData; delete m_actionsItemData; } 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::NavigationGroup, m_quickOpenDeclaration); } if (isDef) { menuExt.addAction(KDevelop::ContextMenuExtension::NavigationGroup, 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; if (modes != OpenFiles) { 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(), &QuickOpenWidget::scopesChanged, this, &QuickOpenPlugin::storeScopes); //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()->ui.itemsButton->setEnabled(false); if (quickOpenLine()) { quickOpenLine()->showWithWidget(dialog->widget()); dialog->deleteLater(); } else { dialog->run(); } } void QuickOpenPlugin::storeScopes(const QStringList& scopes) { lastUsedScopes = scopes; KConfigGroup grp = KSharedConfig::openConfig()->group("QuickOpen"); grp.writeEntry("SelectedScopes", scopes); } void QuickOpenPlugin::storeItems(const QStringList& items) { lastUsedItems = items; KConfigGroup grp = KSharedConfig::openConfig()->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); } void QuickOpenPlugin::quickOpenOpenFile() { showQuickOpen(OpenFiles); } void QuickOpenPlugin::quickOpenDocumentation() { showQuickOpenWidget(QStringList(i18n("Documentation")), QStringList(i18n("Includes")), true); } void QuickOpenPlugin::quickOpenActions() { showQuickOpenWidget(QStringList(i18n("Actions")), QStringList(i18n("Includes")), true); } 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) { qCDebug(PLUGIN_QUICKOPEN) << "Found no declaration for cursor, cannot jump"; return; } decl->activateSpecialization(); IndexedString u = decl->url(); KTextEditor::Cursor c = decl->rangeInCurrentRevision().start(); if (u.isEmpty()) { qCDebug(PLUGIN_QUICKOPEN) << "Got empty url for declaration" << decl->toString(); return; } lock.unlock(); core()->documentController()->openDocument(u.toUrl(), c); } QWidget* QuickOpenPlugin::specialObjectNavigationWidget() const { KTextEditor::View* view = ICore::self()->documentController()->activeTextDocumentView(); if (!view) { return nullptr; } QUrl url = ICore::self()->documentController()->activeDocument()->url(); const auto languages = ICore::self()->languageController()->languagesForUrl(url); foreach (const auto language, languages) { QWidget* w = language->specialLanguageObjectNavigationWidget(url, KTextEditor::Cursor(view->cursorPosition())); if (w) { return w; } } return nullptr; } QPair QuickOpenPlugin::specialObjectJumpPosition() const { KTextEditor::View* view = ICore::self()->documentController()->activeTextDocumentView(); if (!view) { return qMakePair(QUrl(), KTextEditor::Cursor()); } QUrl url = ICore::self()->documentController()->activeDocument()->url(); const auto languages = ICore::self()->languageController()->languagesForUrl(url); foreach (const auto language, languages) { QPair pos = language->specialLanguageObjectJumpCursor(url, KTextEditor::Cursor(view->cursorPosition())); if (pos.second.isValid()) { return pos; } } return qMakePair(QUrl(), KTextEditor::Cursor::invalid()); } bool QuickOpenPlugin::jumpToSpecialObject() { QPair pos = specialObjectJumpPosition(); if (pos.second.isValid()) { if (pos.first.isEmpty()) { qCDebug(PLUGIN_QUICKOPEN) << "Got empty url for special language object"; return false; } ICore::self()->documentController()->openDocument(pos.first, pos.second); return true; } return false; } void QuickOpenPlugin::quickOpenDefinition() { if (jumpToSpecialObject()) { return; } KDevelop::DUChainReadLocker lock(DUChain::lock()); Declaration* decl = cursorDeclaration(); if (!decl) { qCDebug(PLUGIN_QUICKOPEN) << "Found no declaration for cursor, cannot jump"; return; } IndexedString u = decl->url(); KTextEditor::Cursor c = decl->rangeInCurrentRevision().start(); if (FunctionDefinition* def = FunctionDefinition::definition(decl)) { def->activateSpecialization(); u = def->url(); c = def->rangeInCurrentRevision().start(); } else { qCDebug(PLUGIN_QUICKOPEN) << "Found no definition for declaration"; decl->activateSpecialization(); } if (u.isEmpty()) { qCDebug(PLUGIN_QUICKOPEN) << "Got empty url for declaration" << decl->toString(); return; } lock.unlock(); core()->documentController()->openDocument(u.toUrl(), c); } bool QuickOpenPlugin::freeModel() { if (m_currentWidgetHandler) { delete m_currentWidgetHandler; } m_currentWidgetHandler = nullptr; 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) { qCDebug(PLUGIN_QUICKOPEN) << "No active document"; return; } KDevelop::DUChainReadLocker lock(DUChain::lock()); TopDUContext* context = DUChainUtils::standardContextForUrl(doc->url()); if (!context) { qCDebug(PLUGIN_QUICKOPEN) << "Got no standard context"; return; } QList items; OutlineFilter filter(items, OutlineFilter::Functions); DUChainUtils::collectItems(context, filter); CursorInRevision cursor = context->transformToLocalRevision(KTextEditor::Cursor(doc->cursorPosition())); if (!cursor.isValid()) { return; } Declaration* nearestDeclBefore = nullptr; int distanceBefore = INT_MIN; Declaration* nearestDeclAfter = nullptr; 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); } lock.unlock(); if (textCursor.isValid()) { core()->documentController()->openDocument(doc->url(), textCursor); } else { qCDebug(PLUGIN_QUICKOPEN) << "No declaration to jump to"; } } struct CreateOutlineDialog { CreateOutlineDialog() : dialog(nullptr) , cursorDecl(nullptr) , model(nullptr) { } void start() { if (!QuickOpenPlugin::self()->freeModel()) { return; } IDocument* doc = ICore::self()->documentController()->activeDocument(); if (!doc) { qCDebug(PLUGIN_QUICKOPEN) << "No active document"; return; } KDevelop::DUChainReadLocker lock(DUChain::lock()); TopDUContext* context = DUChainUtils::standardContextForUrl(doc->url()); if (!context) { qCDebug(PLUGIN_QUICKOPEN) << "Got no standard context"; return; } model = new QuickOpenModel(nullptr); 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); dialog->widget()->setSortingEnabled(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) { QModelIndex index(model->index(num, 0, QModelIndex())); // Need to invoke the scrolling later. If we did it now, then it wouldn't have any effect, // apparently because the widget internals aren't initialized yet properly (although we've // already called 'widget->show()'. auto list = dialog->widget()->ui.list; QMetaObject::invokeMethod(list, "setCurrentIndex", Qt::QueuedConnection, Q_ARG(QModelIndex, index)); QMetaObject::invokeMethod(list, "scrollTo", Qt::QueuedConnection, Q_ARG(QModelIndex, index), Q_ARG(QAbstractItemView::ScrollHint, QAbstractItemView::PositionAtCenter)); } ++num; } } } QPointer dialog; Declaration* cursorDecl; QList items; QuickOpenModel* model; }; class OutlineQuickopenWidgetCreator : public QuickOpenWidgetCreator { public: OutlineQuickopenWidgetCreator(QStringList /*scopes*/, QStringList /*items*/) : m_creator(nullptr) { } ~OutlineQuickopenWidgetCreator() override { delete m_creator; } QuickOpenWidget* createWidget() override { delete m_creator; m_creator = new CreateOutlineDialog; m_creator->start(); if (!m_creator->dialog) { return nullptr; } m_creator->dialog->deleteLater(); return m_creator->dialog->widget(); } void widgetShown() override { if (m_creator) { m_creator->finish(); delete m_creator; m_creator = nullptr; } } QString objectNameForLine() override { return QStringLiteral("Outline"); } CreateOutlineDialog* m_creator; }; void QuickOpenPlugin::quickOpenNavigateFunctions() { CreateOutlineDialog create; create.start(); if (!create.dialog) { return; } m_currentWidgetHandler = create.dialog; QuickOpenLineEdit* line = quickOpenLine(QStringLiteral("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(nullptr) , 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) { qCDebug(PLUGIN_QUICKOPEN) << object; if (object == this || object == m_widget) { return true; } object = object->parent(); } return false; } void QuickOpenLineEdit::widgetDestroyed(QObject* obj) { Q_UNUSED(obj); // need to use a queued connection here, because this function is called in ~QWidget! // => QuickOpenWidget instance is half-destructed => connections are not yet cleared // => clear() will trigger signals which will operate on the invalid QuickOpenWidget // So, just wait until properly destructed QMetaObject::invokeMethod(this, "deactivate", Qt::QueuedConnection); } void QuickOpenLineEdit::showWithWidget(QuickOpenWidget* widget) { connect(widget, &QuickOpenWidget::destroyed, this, &QuickOpenLineEdit::widgetDestroyed); qCDebug(PLUGIN_QUICKOPEN) << "storing widget" << widget; deactivate(); if (m_widget) { qCDebug(PLUGIN_QUICKOPEN) << "deleting" << m_widget; delete m_widget; } m_widget = widget; m_forceUpdate = true; setFocus(); } void QuickOpenLineEdit::focusInEvent(QFocusEvent* ev) { QLineEdit::focusInEvent(ev); // delete m_widget; qCDebug(PLUGIN_QUICKOPEN) << "got focus"; qCDebug(PLUGIN_QUICKOPEN) << "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(nullptr, Qt::ToolTip); m_widget->setFocusPolicy(Qt::NoFocus); m_widget->setAlternativeSearchField(this); QuickOpenPlugin::self()->m_currentWidgetHandler = m_widget; connect(m_widget.data(), &QuickOpenWidget::ready, this, &QuickOpenLineEdit::deactivate); connect(m_widget.data(), &QuickOpenWidget::scopesChanged, QuickOpenPlugin::self(), &QuickOpenPlugin::storeScopes); connect(m_widget.data(), &QuickOpenWidget::itemsChanged, QuickOpenPlugin::self(), &QuickOpenPlugin::storeItems); Q_ASSERT(m_widget->ui.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: QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection); break; // handle bug 260657 - "Outline menu doesn't follow main window on its move" case QEvent::Move: { if (QWidget* widget = qobject_cast(obj)) { // close the outline menu in case a parent widget moved if (widget->isAncestorOf(this)) { qCDebug(PLUGIN_QUICKOPEN) << "closing because of parent widget move"; deactivate(); } break; } } case QEvent::FocusIn: if (dynamic_cast(obj)) { QFocusEvent* focusEvent = dynamic_cast(e); Q_ASSERT(focusEvent); //Eat the focus event, keep the focus qCDebug(PLUGIN_QUICKOPEN) << "focus change" << "inside this: " << insideThis(obj) << "this" << this << "obj" << obj; if (obj == this) { return false; } qCDebug(PLUGIN_QUICKOPEN) << "reason" << focusEvent->reason(); if (focusEvent->reason() != Qt::MouseFocusReason && focusEvent->reason() != Qt::ActiveWindowFocusReason) { QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection); return false; } if (!insideThis(obj)) { deactivate(); } } else if (obj != this) { QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection); } break; default: break; } return false; } void QuickOpenLineEdit::activate() { qCDebug(PLUGIN_QUICKOPEN) << "activating"; setText(QString()); setStyleSheet(QString()); qApp->installEventFilter(this); } void QuickOpenLineEdit::deactivate() { qCDebug(PLUGIN_QUICKOPEN) << "deactivating"; clear(); if (m_widget || hasFocus()) { QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection); } if (m_widget) { m_widget->deleteLater(); } m_widget = nullptr; qApp->removeEventFilter(this); } void QuickOpenLineEdit::checkFocus() { qCDebug(PLUGIN_QUICKOPEN) << "checking focus" << m_widget; if (m_widget) { QWidget* focusWidget = QApplication::focusWidget(); bool focusWidgetInsideThis = focusWidget ? insideThis(focusWidget) : false; if (QApplication::focusWindow() && isVisible() && !isHidden() && (!focusWidget || (focusWidget && focusWidgetInsideThis))) { qCDebug(PLUGIN_QUICKOPEN) << "setting focus to line edit"; activateWindow(); setFocus(); } else { qCDebug(PLUGIN_QUICKOPEN) << "deactivating because check failed, focusWidget" << focusWidget << "insideThis" << focusWidgetInsideThis; 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" diff --git a/plugins/quickopen/tests/bench_quickopen.cpp b/plugins/quickopen/tests/bench_quickopen.cpp index 797f2d957..a39021be6 100644 --- a/plugins/quickopen/tests/bench_quickopen.cpp +++ b/plugins/quickopen/tests/bench_quickopen.cpp @@ -1,161 +1,161 @@ /* * Copyright Milian Wolff * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #include "bench_quickopen.h" #include #include #include -#include +#include QTEST_MAIN(BenchQuickOpen); using namespace KDevelop; BenchQuickOpen::BenchQuickOpen(QObject* parent) : QuickOpenTestBase(Core::NoUi, parent) { } void BenchQuickOpen::getData() { QTest::addColumn("files"); QTest::addColumn("filter"); QTest::newRow("0100-___") << 100 << ""; QTest::newRow("0500-___") << 500 << ""; QTest::newRow("0100-bar") << 100 << "bar"; QTest::newRow("0500-bar") << 500 << "bar"; QTest::newRow("0100-1__") << 100 << "1"; QTest::newRow("0500-1__") << 500 << "1"; QTest::newRow("0100-f/b") << 100 << "f/b"; QTest::newRow("0500-f/b") << 500 << "f/b"; } void BenchQuickOpen::benchProjectFileFilter_addRemoveProject() { QFETCH(int, files); QFETCH(QString, filter); ProjectFileDataProvider provider; QScopedPointer project(getProjectWithFiles(files)); QBENCHMARK { projectController->addProject(project.data()); projectController->takeProject(project.data()); } } void BenchQuickOpen::benchProjectFileFilter_addRemoveProject_data() { getData(); } void BenchQuickOpen::benchProjectFileFilter_reset() { QFETCH(int, files); QFETCH(QString, filter); ProjectFileDataProvider provider; TestProject* project = getProjectWithFiles(files); provider.setFilterText(filter); projectController->addProject(project); QBENCHMARK { provider.reset(); } } void BenchQuickOpen::benchProjectFileFilter_reset_data() { getData(); } void BenchQuickOpen::benchProjectFileFilter_setFilter() { QFETCH(int, files); QFETCH(QString, filter); ProjectFileDataProvider provider; TestProject* project = getProjectWithFiles(files); projectController->addProject(project); provider.reset(); QBENCHMARK { provider.setFilterText(filter); provider.setFilterText(QString()); } } void BenchQuickOpen::benchProjectFileFilter_setFilter_data() { getData(); } void BenchQuickOpen::benchProjectFileFilter_providerData() { QFETCH(int, files); QFETCH(QString, filter); ProjectFileDataProvider provider; TestProject* project = getProjectWithFiles(files); projectController->addProject(project); provider.reset(); QCOMPARE(provider.itemCount(), uint(files)); provider.setFilterText(filter); QVERIFY(provider.itemCount()); const int itemIdx = provider.itemCount() - 1; QBENCHMARK { QuickOpenDataPointer data = provider.data(itemIdx); data->text(); } } void BenchQuickOpen::benchProjectFileFilter_providerData_data() { getData(); } void BenchQuickOpen::benchProjectFileFilter_providerDataIcon() { QFETCH(int, files); QFETCH(QString, filter); ProjectFileDataProvider provider; TestProject* project = getProjectWithFiles(files); projectController->addProject(project); provider.reset(); QCOMPARE(provider.itemCount(), uint(files)); provider.setFilterText(filter); QVERIFY(provider.itemCount()); const int itemIdx = provider.itemCount() - 1; QBENCHMARK { QuickOpenDataPointer data = provider.data(itemIdx); data->icon(); } } void BenchQuickOpen::benchProjectFileFilter_providerDataIcon_data() { getData(); } diff --git a/plugins/quickopen/tests/test_quickopen.cpp b/plugins/quickopen/tests/test_quickopen.cpp index 492fed964..752dfaf54 100644 --- a/plugins/quickopen/tests/test_quickopen.cpp +++ b/plugins/quickopen/tests/test_quickopen.cpp @@ -1,300 +1,300 @@ /* * Copyright Milian Wolff * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #include "test_quickopen.h" #include #include -#include +#include #include QTEST_MAIN(TestQuickOpen); using namespace KDevelop; TestQuickOpen::TestQuickOpen(QObject* parent) : QuickOpenTestBase(Core::Default, parent) { } void TestQuickOpen::testDuchainFilter() { using ItemList = QList; QFETCH(ItemList, items); QFETCH(QString, filter); QFETCH(ItemList, filtered); auto toStringList = [](const ItemList& items) { QStringList result; for (const DUChainItem& item: items) { result << item.m_text; } return result; }; TestFilter filterItems; filterItems.setItems(items); filterItems.setFilter(filter); QCOMPARE(toStringList(filterItems.filteredItems()), toStringList(filtered)); } void TestQuickOpen::testDuchainFilter_data() { using ItemList = QList; QTest::addColumn("items"); QTest::addColumn("filter"); QTest::addColumn("filtered"); auto i = [](const QString& text) { auto item = DUChainItem(); item.m_text = text; return item; }; auto items = ItemList() << i(QStringLiteral("KTextEditor::Cursor")) << i(QStringLiteral("void KTextEditor::Cursor::explode()")) << i(QStringLiteral("QVector SomeNamespace::SomeClass::func(int)")); QTest::newRow("prefix") << items << "KTE" << (ItemList() << items.at(0) << items.at(1)); QTest::newRow("prefix_mismatch") << items << "KTEY" << (ItemList()); QTest::newRow("prefix_colon") << items << "KTE:" << (ItemList() << items.at(0) << items.at(1)); QTest::newRow("prefix_colon_mismatch") << items << "KTE:Y" << (ItemList()); QTest::newRow("prefix_colon_mismatch2") << items << "XKTE:" << (ItemList()); QTest::newRow("prefix_two_colon") << items << "KTE::" << (ItemList() << items.at(0) << items.at(1)); QTest::newRow("prefix_two_colon_mismatch") << items << "KTE::Y" << (ItemList()); QTest::newRow("prefix_two_colon_mismatch2") << items << "XKTE::" << (ItemList()); QTest::newRow("suffix") << items << "Curs" << (ItemList() << items.at(0) << items.at(1)); QTest::newRow("suffix2") << items << "curs" << (ItemList() << items.at(0) << items.at(1)); QTest::newRow("mid") << items << "SomeClass" << (ItemList() << items.at(2)); QTest::newRow("mid_abbrev") << items << "SClass" << (ItemList() << items.at(2)); } void TestQuickOpen::testAbbreviations() { QFETCH(QStringList, items); QFETCH(QString, filter); QFETCH(QStringList, filtered); PathTestFilter filterItems; filterItems.setItems(items); filterItems.setFilter(filter.split('/', QString::SkipEmptyParts)); QCOMPARE(QStringList(filterItems.filteredItems()), filtered); } void TestQuickOpen::testAbbreviations_data() { QTest::addColumn("items"); QTest::addColumn("filter"); QTest::addColumn("filtered"); const QStringList items = QStringList() << QStringLiteral("/foo/bar/caz/a.h") << QStringLiteral("/KateThing/CMakeLists.txt") << QStringLiteral("/FooBar/FooBar/Footestfoo.h"); QTest::newRow("path_segments") << items << "fbc" << (QStringList()); QTest::newRow("path_segment_abbrev") << items << "cmli" << (QStringList() << items.at(1)); QTest::newRow("path_segment_old") << items << "kate/cmake" << (QStringList() << items.at(1)); QTest::newRow("path_segment_multi_mixed") << items << "ftfoo.h" << (QStringList() << items.at(2)); } void TestQuickOpen::testSorting() { QFETCH(QStringList, items); QFETCH(QString, filter); QFETCH(QStringList, filtered); PathTestFilter filterItems; filterItems.setItems(items); filterItems.setFilter(filter.split('/', QString::SkipEmptyParts)); QEXPECT_FAIL("bar7", "empty parts are skipped", Continue); QCOMPARE(QStringList(filterItems.filteredItems()), filtered); } void TestQuickOpen::testSorting_data() { QTest::addColumn("items"); QTest::addColumn("filter"); QTest::addColumn("filtered"); const QStringList items = QStringList() << QStringLiteral("/foo/a.h") << QStringLiteral("/foo/ab.h") << QStringLiteral("/foo/bc.h") << QStringLiteral("/bar/a.h"); { QTest::newRow("no-filter") << items << QString() << items; } { const QStringList filtered = QStringList() << QStringLiteral("/bar/a.h"); QTest::newRow("bar1") << items << QStringLiteral("bar") << filtered; QTest::newRow("bar2") << items << QStringLiteral("/bar") << filtered; QTest::newRow("bar3") << items << QStringLiteral("/bar/") << filtered; QTest::newRow("bar4") << items << QStringLiteral("bar/") << filtered; QTest::newRow("bar5") << items << QStringLiteral("ar/") << filtered; QTest::newRow("bar6") << items << QStringLiteral("r/") << filtered; QTest::newRow("bar7") << items << QStringLiteral("b/") << filtered; QTest::newRow("bar8") << items << QStringLiteral("b/a") << filtered; QTest::newRow("bar9") << items << QStringLiteral("b/a.h") << filtered; QTest::newRow("bar10") << items << QStringLiteral("b/a.") << filtered; } { const QStringList filtered = QStringList() << QStringLiteral("/foo/a.h") << QStringLiteral("/foo/ab.h"); QTest::newRow("foo_a1") << items << QStringLiteral("foo/a") << filtered; QTest::newRow("foo_a2") << items << QStringLiteral("/f/a") << filtered; } { // now matches ab.h too because of abbreviation matching, but should be sorted last const QStringList filtered = QStringList() << QStringLiteral("/foo/a.h") << QStringLiteral("/bar/a.h") << QStringLiteral("/foo/ab.h"); QTest::newRow("a_h") << items << QStringLiteral("a.h") << filtered; } { const QStringList base = QStringList() << QStringLiteral("/foo/a_test") << QStringLiteral("/foo/test_b_1") << QStringLiteral("/foo/test_b"); const QStringList sorted = QStringList() << QStringLiteral("/foo/test_b") << QStringLiteral("/foo/test_b_1"); QTest::newRow("prefer_exact") << base << QStringLiteral("test_b") << sorted; } { // from commit: 769491f06a4560a4798592ff060675ffb0d990a6 const QString file = QStringLiteral("/myProject/someStrangePath/anItem.cpp"); const QStringList base = QStringList() << QStringLiteral("/foo/a") << file; const QStringList filtered = QStringList() << file; QTest::newRow("strange") << base << QStringLiteral("strange/item") << filtered; } { const QStringList base = QStringList() << QStringLiteral("/foo/a_test") << QStringLiteral("/foo/test_b_1") << QStringLiteral("/foo/test_b") << QStringLiteral("/foo/test/a"); const QStringList sorted = QStringList() << QStringLiteral("/foo/test_b_1") << QStringLiteral("/foo/test_b") << QStringLiteral("/foo/a_test") << QStringLiteral("/foo/test/a"); QTest::newRow("prefer_start1") << base << QStringLiteral("test") << sorted; QTest::newRow("prefer_start2") << base << QStringLiteral("foo/test") << sorted; } { const QStringList base = QStringList() << QStringLiteral("/muh/kuh/asdf/foo") << QStringLiteral("/muh/kuh/foo/asdf"); const QStringList reverse = QStringList() << QStringLiteral("/muh/kuh/foo/asdf") << QStringLiteral("/muh/kuh/asdf/foo"); QTest::newRow("prefer_start3") << base << QStringLiteral("f") << base; QTest::newRow("prefer_start4") << base << QStringLiteral("/fo") << base; QTest::newRow("prefer_start5") << base << QStringLiteral("/foo") << base; QTest::newRow("prefer_start6") << base << QStringLiteral("a") << reverse; QTest::newRow("prefer_start7") << base << QStringLiteral("/a") << reverse; QTest::newRow("prefer_start8") << base << QStringLiteral("uh/as") << reverse; QTest::newRow("prefer_start9") << base << QStringLiteral("asdf") << reverse; } { QTest::newRow("duplicate") << (QStringList() << QStringLiteral("/muh/kuh/asdf/foo")) << QStringLiteral("kuh/kuh") << QStringList(); } } void TestQuickOpen::testProjectFileFilter() { QTemporaryDir dir; TestProject* project = new TestProject(Path(dir.path())); ProjectFolderItem* foo = createChild(project->projectItem(), QStringLiteral("foo")); createChild(foo, QStringLiteral("bar")); createChild(foo, QStringLiteral("asdf")); createChild(foo, QStringLiteral("space bar")); ProjectFolderItem* asdf = createChild(project->projectItem(), QStringLiteral("asdf")); createChild(asdf, QStringLiteral("bar")); QTemporaryFile tmpFile; tmpFile.setFileName(dir.path() + "/aaaa"); QVERIFY(tmpFile.open()); ProjectFileItem* aaaa = new ProjectFileItem(QStringLiteral("aaaa"), project->projectItem()); QCOMPARE(project->fileSet().size(), 5); ProjectFileDataProvider provider; QCOMPARE(provider.itemCount(), 0u); projectController->addProject(project); const QStringList original = QStringList() << QStringLiteral("aaaa") << QStringLiteral("asdf/bar") << QStringLiteral("foo/asdf") << QStringLiteral("foo/bar") << QStringLiteral("foo/space bar"); // lazy load QCOMPARE(provider.itemCount(), 0u); provider.reset(); QCOMPARE(items(provider), original); QCOMPARE(provider.itemPath(provider.items().first()), aaaa->path()); QCOMPARE(provider.data(0)->text(), QStringLiteral("aaaa")); // don't show opened file QVERIFY(core->documentController()->openDocument(QUrl::fromLocalFile(tmpFile.fileName()))); // lazy load again QCOMPARE(items(provider), original); provider.reset(); QCOMPARE(items(provider), QStringList() << QStringLiteral("asdf/bar") << QStringLiteral("foo/asdf") << QStringLiteral("foo/bar") << QStringLiteral("foo/space bar")); // prefer files starting with filter provider.setFilterText(QStringLiteral("as")); qDebug() << items(provider); QCOMPARE(items(provider), QStringList() << QStringLiteral("foo/asdf") << QStringLiteral("asdf/bar")); // clear filter provider.reset(); QCOMPARE(items(provider), QStringList() << QStringLiteral("asdf/bar") << QStringLiteral("foo/asdf") << QStringLiteral("foo/bar") << QStringLiteral("foo/space bar")); // update on document close, lazy load again core->documentController()->closeAllDocuments(); QCOMPARE(items(provider), QStringList() << QStringLiteral("asdf/bar") << QStringLiteral("foo/asdf") << QStringLiteral("foo/bar") << QStringLiteral("foo/space bar")); provider.reset(); QCOMPARE(items(provider), original); ProjectFileItem* blub = createChild(project->projectItem(), QStringLiteral("blub")); // lazy load QCOMPARE(provider.itemCount(), 5u); provider.reset(); QCOMPARE(provider.itemCount(), 6u); // ensure we don't add stuff multiple times QMetaObject::invokeMethod(&provider, "fileAddedToSet", Q_ARG(KDevelop::IProject*, project), Q_ARG(KDevelop::IndexedString, blub->indexedPath())); QCOMPARE(provider.itemCount(), 6u); provider.reset(); QCOMPARE(provider.itemCount(), 6u); // lazy load in this implementation delete blub; QCOMPARE(provider.itemCount(), 6u); provider.reset(); QCOMPARE(provider.itemCount(), 5u); QCOMPARE(items(provider), original); // allow filtering by path to project provider.setFilterText(dir.path()); QCOMPARE(items(provider), original); Path buildFolderItem(project->path().parent(), QStringLiteral(".build/generated.h")); new ProjectFileItem(project, buildFolderItem, project->projectItem()); // lazy load QCOMPARE(items(provider), original); provider.reset(); QCOMPARE(items(provider), QStringList() << QStringLiteral("aaaa") << QStringLiteral("asdf/bar") << QStringLiteral("foo/asdf") << QStringLiteral("foo/bar") << QStringLiteral("foo/space bar") << QStringLiteral("../.build/generated.h")); projectController->closeProject(project); provider.reset(); QVERIFY(!provider.itemCount()); } diff --git a/plugins/standardoutputview/tests/test_standardoutputview.cpp b/plugins/standardoutputview/tests/test_standardoutputview.cpp index 7d3b82064..0e7380a74 100644 --- a/plugins/standardoutputview/tests/test_standardoutputview.cpp +++ b/plugins/standardoutputview/tests/test_standardoutputview.cpp @@ -1,230 +1,230 @@ /* Copyright (C) 2011 Silvère Lestang 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 #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include "test_standardoutputview.h" #include "../outputwidget.h" #include "../toolviewdata.h" namespace KDevelop { class IUiController; } QTEST_MAIN(StandardOutputViewTest) const QString StandardOutputViewTest::toolviewTitle = QStringLiteral("my_toolview"); void StandardOutputViewTest::initTestCase() { KDevelop::AutoTestShell::init(); m_testCore = new KDevelop::TestCore(); m_testCore->initialize(KDevelop::Core::Default); m_controller = m_testCore->uiControllerInternal(); QTest::qWait(500); // makes sure that everything is loaded (don't know if it's required) m_stdOutputView = nullptr; KDevelop::IPluginController* plugin_controller = m_testCore->pluginController(); QList plugins = plugin_controller->loadedPlugins(); // make sure KDevStandardOutputView is loaded KDevelop::IPlugin* plugin = plugin_controller->loadPlugin(QStringLiteral("KDevStandardOutputView")); QVERIFY(plugin); m_stdOutputView = dynamic_cast(plugin); QVERIFY(m_stdOutputView); } void StandardOutputViewTest::cleanupTestCase() { m_testCore->cleanup(); delete m_testCore; } OutputWidget* StandardOutputViewTest::toolviewPointer(QString toolviewTitle) { QList< Sublime::View* > views = m_controller->activeArea()->toolViews(); foreach(Sublime::View* view, views) { Sublime::ToolDocument *doc = dynamic_cast(view->document()); if(doc) { if(doc->title() == toolviewTitle && view->hasWidget()) { return dynamic_cast(view->widget()); } } } return nullptr; } void StandardOutputViewTest::testRegisterAndRemoveToolView() { toolviewId = m_stdOutputView->registerToolView(toolviewTitle, KDevelop::IOutputView::HistoryView); QVERIFY(toolviewPointer(toolviewTitle)); // re-registering should return the same tool view instead of creating a new one QCOMPARE(toolviewId, m_stdOutputView->registerToolView(toolviewTitle, KDevelop::IOutputView::HistoryView)); m_stdOutputView->removeToolView(toolviewId); QVERIFY(!toolviewPointer(toolviewTitle)); } void StandardOutputViewTest::testActions() { toolviewId = m_stdOutputView->registerToolView(toolviewTitle, KDevelop::IOutputView::MultipleView, QIcon()); OutputWidget* outputWidget = toolviewPointer(toolviewTitle); QVERIFY(outputWidget); QList actions = outputWidget->actions(); QCOMPARE(actions.size(), 11); m_stdOutputView->removeToolView(toolviewId); QVERIFY(!toolviewPointer(toolviewTitle)); QList addedActions; addedActions.append(new QAction(QStringLiteral("Action1"), nullptr)); addedActions.append(new QAction(QStringLiteral("Action2"), nullptr)); toolviewId = m_stdOutputView->registerToolView(toolviewTitle, KDevelop::IOutputView::HistoryView, QIcon(), KDevelop::IOutputView::ShowItemsButton | KDevelop::IOutputView::AddFilterAction, addedActions); outputWidget = toolviewPointer(toolviewTitle); QVERIFY(outputWidget); actions = outputWidget->actions(); QCOMPARE(actions.size(), 16); QCOMPARE(actions[actions.size()-2]->text(), addedActions[0]->text()); QCOMPARE(actions[actions.size()-1]->text(), addedActions[1]->text()); m_stdOutputView->removeToolView(toolviewId); QVERIFY(!toolviewPointer(toolviewTitle)); } void StandardOutputViewTest::testRegisterAndRemoveOutput() { toolviewId = m_stdOutputView->registerToolView(toolviewTitle, KDevelop::IOutputView::MultipleView, QIcon()); OutputWidget* outputWidget = toolviewPointer(toolviewTitle); QVERIFY(outputWidget); for(int i = 0; i < 5; i++) { outputId[i] = m_stdOutputView->registerOutputInToolView(toolviewId, QStringLiteral("output%1").arg(i)); } for(int i = 0; i < 5; i++) { QCOMPARE(outputWidget->data->outputdata.value(outputId[i])->title, QStringLiteral("output%1").arg(i)); QCOMPARE(outputWidget->m_tabwidget->tabText(i), QStringLiteral("output%1").arg(i)); } for(int i = 0; i < 5; i++) { m_stdOutputView->removeOutput(outputId[i]); QVERIFY(!outputWidget->data->outputdata.contains(outputId[i])); } QCOMPARE(outputWidget->m_tabwidget->count(), 0); m_stdOutputView->removeToolView(toolviewId); QVERIFY(!toolviewPointer(toolviewTitle)); toolviewId = m_stdOutputView->registerToolView(toolviewTitle, KDevelop::IOutputView::HistoryView, QIcon(), KDevelop::IOutputView::ShowItemsButton | KDevelop::IOutputView::AddFilterAction); outputWidget = toolviewPointer(toolviewTitle); QVERIFY(outputWidget); for(int i = 0; i < 5; i++) { outputId[i] = m_stdOutputView->registerOutputInToolView(toolviewId, QStringLiteral("output%1").arg(i)); } for(int i = 0; i < 5; i++) { QCOMPARE(outputWidget->data->outputdata.value(outputId[i])->title, QStringLiteral("output%1").arg(i)); } for(int i = 0; i < 5; i++) { m_stdOutputView->removeOutput(outputId[i]); QVERIFY(!outputWidget->data->outputdata.contains(outputId[i])); } QCOMPARE(outputWidget->m_stackwidget->count(), 0); m_stdOutputView->removeToolView(toolviewId); QVERIFY(!toolviewPointer(toolviewTitle)); } void StandardOutputViewTest::testSetModelAndDelegate() { toolviewId = m_stdOutputView->registerToolView(toolviewTitle, KDevelop::IOutputView::MultipleView, QIcon()); OutputWidget* outputWidget = toolviewPointer(toolviewTitle); QVERIFY(outputWidget); QAbstractItemModel* model = new QStandardItemModel; QPointer checkModel(model); QAbstractItemDelegate* delegate = new QItemDelegate; QPointer checkDelegate(delegate); outputId[0] = m_stdOutputView->registerOutputInToolView(toolviewId, QStringLiteral("output")); m_stdOutputView->setModel(outputId[0], model); m_stdOutputView->setDelegate(outputId[0], delegate); QCOMPARE(outputWidget->m_views.value(outputId[0])->model(), model); QCOMPARE(outputWidget->m_views.value(outputId[0])->itemDelegate(), delegate); QVERIFY(model->parent()); // they have a parent (the outputdata), so parent() != 0x0 QVERIFY(delegate->parent()); m_stdOutputView->removeToolView(toolviewId); QVERIFY(!toolviewPointer(toolviewTitle)); // view deleted, hence model + delegate deleted QVERIFY(!checkModel.data()); QVERIFY(!checkDelegate.data()); } void StandardOutputViewTest::testStandardToolViews() { QFETCH(KDevelop::IOutputView::StandardToolView, view); int id = m_stdOutputView->standardToolView(view); QVERIFY(id); QCOMPARE(id, m_stdOutputView->standardToolView(view)); } void StandardOutputViewTest::testStandardToolViews_data() { QTest::addColumn("view"); QTest::newRow("build") << KDevelop::IOutputView::BuildView; QTest::newRow("run") << KDevelop::IOutputView::RunView; QTest::newRow("debug") << KDevelop::IOutputView::DebugView; QTest::newRow("test") << KDevelop::IOutputView::TestView; QTest::newRow("vcs") << KDevelop::IOutputView::VcsView; } diff --git a/plugins/standardoutputview/tests/test_standardoutputview.h b/plugins/standardoutputview/tests/test_standardoutputview.h index c5127b903..34948feba 100644 --- a/plugins/standardoutputview/tests/test_standardoutputview.h +++ b/plugins/standardoutputview/tests/test_standardoutputview.h @@ -1,64 +1,64 @@ /* Copyright (C) 2011 Silvère Lestang 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. */ #ifndef KDEVPLATFORM_PLUGIN_TEST_STANDARDOUTPUTVIEW_H #define KDEVPLATFORM_PLUGIN_TEST_STANDARDOUTPUTVIEW_H -#include +#include #include namespace KDevelop { class TestCore; class IOutputView; } namespace Sublime { class View; class Controller; } class OutputWidget; class StandardOutputViewTest: public QObject { Q_OBJECT private: OutputWidget* toolviewPointer(QString toolviewTitle); KDevelop::TestCore* m_testCore; KDevelop::IOutputView* m_stdOutputView; KDevelop::UiController* m_controller; int toolviewId; int outputId[5]; static const QString toolviewTitle; private slots: void initTestCase(); void cleanupTestCase(); void testRegisterAndRemoveToolView(); void testActions(); void testRegisterAndRemoveOutput(); void testSetModelAndDelegate(); void testStandardToolViews(); void testStandardToolViews_data(); }; #endif // KDEVPLATFORM_PLUGIN_TEST_STANDARDOUTPUTVIEW_H diff --git a/plugins/subversion/tests/test_svnimport.h b/plugins/subversion/tests/test_svnimport.h index bd1b81c91..2e1ac780f 100644 --- a/plugins/subversion/tests/test_svnimport.h +++ b/plugins/subversion/tests/test_svnimport.h @@ -1,47 +1,47 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2009 Fabian Wiesel * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_PLUGIN_SVNIMPORT_H #define KDEVPLATFORM_PLUGIN_SVNIMPORT_H -#include +#include class QTemporaryDir; namespace KDevelop { class TestCore; class ICentralizedVersionControl; } class TestSvnImport : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testBasic(); void testImportIntoDir(); void testImportWithMissingDirs(); private: void validateImport( const QString& repo, QTemporaryDir& checkout, const QString& origcontent ); KDevelop::ICentralizedVersionControl* vcs = nullptr; }; #endif // KDEVPLATFORM_PLUGIN_SVNRECURSIVEADD_H diff --git a/plugins/subversion/tests/test_svnrecursiveadd.h b/plugins/subversion/tests/test_svnrecursiveadd.h index fab45bb48..4873c2024 100644 --- a/plugins/subversion/tests/test_svnrecursiveadd.h +++ b/plugins/subversion/tests/test_svnrecursiveadd.h @@ -1,36 +1,36 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2009 Fabian Wiesel * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_PLUGIN_SVNRECURSIVEADD_H #define KDEVPLATFORM_PLUGIN_SVNRECURSIVEADD_H -#include +#include class TestSvnRecursiveAdd : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void test(); }; #endif // KDEVPLATFORM_PLUGIN_SVNRECURSIVEADD_H diff --git a/plugins/testview/testviewplugin.h b/plugins/testview/testviewplugin.h index 925226145..66fbde706 100644 --- a/plugins/testview/testviewplugin.h +++ b/plugins/testview/testviewplugin.h @@ -1,46 +1,46 @@ /* This file is part of KDevelop Copyright 2012 Miha ?an?ula 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_PLUGIN_TESTVIEWPLUGIN_H #define KDEVPLATFORM_PLUGIN_TESTVIEWPLUGIN_H #include "interfaces/iplugin.h" -#include +#include class TestToolViewFactory; class TestViewPlugin : public KDevelop::IPlugin { Q_OBJECT public: explicit TestViewPlugin(QObject* parent, const QVariantList& args = QVariantList()); ~TestViewPlugin() override; void unload() override; private: TestToolViewFactory* m_viewFactory; private slots: void runAllTests(); void stopRunningTests(); void jobStateChanged(); }; #endif // KDEVPLATFORM_PLUGIN_TESTVIEWPLUGIN_H diff --git a/project/filemanagerlistjob.h b/project/filemanagerlistjob.h index 75c15319a..fcd6547b1 100644 --- a/project/filemanagerlistjob.h +++ b/project/filemanagerlistjob.h @@ -1,80 +1,80 @@ /* This file is part of KDevelop Copyright 2009 Radu Benea 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. */ #ifndef KDEVPLATFORM_FILEMANAGERLISTJOB_H #define KDEVPLATFORM_FILEMANAGERLISTJOB_H #include -#include +#include // uncomment to time imort jobs // #define TIME_IMPORT_JOB #ifdef TIME_IMPORT_JOB #include #endif namespace KDevelop { class ProjectFolderItem; class FileManagerListJob : public KIO::Job { Q_OBJECT public: explicit FileManagerListJob(ProjectFolderItem* item); ProjectFolderItem* item() const; void addSubDir(ProjectFolderItem* item); void removeSubDir(ProjectFolderItem* item); void abort(); void start() override; signals: void entries(FileManagerListJob* job, ProjectFolderItem* baseItem, const KIO::UDSEntryList& entries); void nextJob(); private slots: void slotEntries(KIO::Job* job, const KIO::UDSEntryList& entriesIn ); void slotResult(KJob* job) override; void handleResults(const KIO::UDSEntryList& entries); void startNextJob(); private: QQueue m_listQueue; /// current base dir ProjectFolderItem* m_item; KIO::UDSEntryList entryList; // kill does not delete the job instantaniously QAtomicInt m_aborted; #ifdef TIME_IMPORT_JOB QElapsedTimer m_timer; QElapsedTimer m_subTimer; qint64 m_subWaited = 0; #endif }; } #endif // KDEVPLATFORM_FILEMANAGERLISTJOB_H diff --git a/project/interfaces/iprojectbuilder.h b/project/interfaces/iprojectbuilder.h index a75e2dfe0..81a654134 100644 --- a/project/interfaces/iprojectbuilder.h +++ b/project/interfaces/iprojectbuilder.h @@ -1,133 +1,133 @@ /* This file is part of KDevelop Copyright 2004 Roberto Raggi Copyright 2007 Andreas Pakulat 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. */ #ifndef KDEVPLATFORM_IPROJECTBUILDER_H #define KDEVPLATFORM_IPROJECTBUILDER_H #include #include -#include +#include class KJob; namespace KDevelop { class IProject; class ProjectBaseItem; /** @author Roberto Raggi @short IProjectBuilder Base class for the Project Builders Provides an interface that will bre useful to perform tasks upon projects, inspired on the unix-style way to build projects: configure -> make -> make install It will provide a job for each of the tasks and a signal to make sure it completed successfully. */ class KDEVPLATFORMPROJECT_EXPORT IProjectBuilder { public: virtual ~IProjectBuilder(); /** * Installs the given project @p item, exact behaviour depends * on the implementation. * * @param specificPrefix defines where the project will be installed. */ virtual KJob* install(ProjectBaseItem* item, const QUrl &specificPrefix = {}) = 0; /** * Builds the given project @p item, exact behaviour depends * on the implementation */ virtual KJob* build(ProjectBaseItem *item) = 0; /** * Cleans the given project @p item, exact behaviour depends * on the implementation. The cleaning should only include * output files, like C/C++ object files, binaries, files * that the builder needs shouldn't be removed. */ virtual KJob* clean(ProjectBaseItem *item) = 0; /** * Configures the given @p project, exact behaviour depends * on the implementation. After calling this a build() call should * succeed (given the code doesn't have errors). * * This function is optional, the default implementation does nothing. */ virtual KJob* configure(IProject* project); /** * Prunes the given @p project, exact behaviour depends * on the implementation. Additionally to what clean() does this may * also remove files used for the builder (or a "subbuilder"), for example * generated Makefiles in QMake/CMake/Automake projects * * This function is optional, the default implementation does nothing. */ virtual KJob* prune(IProject* project); /** * Provide access to the builders related to the @p project. * The list returned by this method is used to select the appropriate config pages for a project. * This method may safely return an empty list, as does the default implementation. */ virtual QList additionalBuilderPlugins( IProject* project ) const; Q_SIGNALS: /** * Emitted when the build for the given item was finished */ void built(ProjectBaseItem *dom); /** * Emitted when the install for the given item was finished */ void installed(ProjectBaseItem*); /** * Emitted when the clean for the given item was finished */ void cleaned(ProjectBaseItem*); /** * Emitted when any of the scheduled actions for the given item was failed */ void failed(ProjectBaseItem *dom); /** * Emitted when the configure for the given item was finished */ void configured(IProject*); /** * Emitted when the pruning for the given item was finished */ void pruned(IProject*); }; } Q_DECLARE_INTERFACE( KDevelop::IProjectBuilder, "org.kdevelop.IProjectBuilder" ) #endif diff --git a/project/projectmodel.h b/project/projectmodel.h index 20beecf57..a3e0ccf20 100644 --- a/project/projectmodel.h +++ b/project/projectmodel.h @@ -1,490 +1,490 @@ /* This file is part of KDevelop Copyright 2005 Roberto Raggi Copyright 2007 Andreas Pakulat Copyright 2007 Aleix Pol 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. */ #ifndef KDEVPLATFORM_PROJECTMODEL_H #define KDEVPLATFORM_PROJECTMODEL_H -#include +#include #include #include "projectexport.h" template struct QPair; template class QList; namespace KDevelop { class IProject; class ProjectFolderItem; class ProjectBuildFolderItem; class ProjectFileItem; class ProjectTargetItem; class ProjectExecutableTargetItem; class ProjectLibraryTargetItem; class ProjectModel; class IndexedString; class Path; /** * Base class to implement the visitor pattern for the project item tree. * * Feel free to subclass it and add overloads for the methods corresponding * to the items you are interested in. * * Start visiting using one of the visit methods. */ class KDEVPLATFORMPROJECT_EXPORT ProjectVisitor { public: ProjectVisitor(); virtual ~ProjectVisitor(); /** * Visit the whole project model tree. */ virtual void visit( ProjectModel* ); /** * Visit the tree starting from the project root item. */ virtual void visit( IProject* ); /** * Visit the folder and anything it contains. */ virtual void visit( ProjectFolderItem* ); /** * Visit the file. */ virtual void visit( ProjectFileItem* ); /** * Visit the build folder and anything it contains. */ virtual void visit( ProjectBuildFolderItem* ); /** * Visit the target and all children it may contain. */ virtual void visit( ProjectExecutableTargetItem* ); /** * Visit the target and all children it may contain. */ virtual void visit( ProjectLibraryTargetItem* ); }; /** * Interface that allows a developer to implement the three basic types of * items you would see in a multi-project * \li Folder * \li Project * \li Custom Target * \li Library Target * \li Executable Target * \li File */ class KDEVPLATFORMPROJECT_EXPORT ProjectBaseItem { public: ProjectBaseItem( IProject*, const QString &name, ProjectBaseItem *parent = nullptr ); virtual ~ProjectBaseItem(); enum ProjectItemType { BaseItem = 0 /** item is a base item */, BuildFolder = 1 /** item is a buildable folder */, Folder = 2 /** item is a folder */, ExecutableTarget = 3 /** item is an executable target */, LibraryTarget = 4 /** item is a library target */, Target = 5 /** item is a target */, File = 6 /** item is a file */, CustomProjectItemType = 100 /** type which should be used as base for custom types */ }; enum RenameStatus { RenameOk = 0, ExistingItemSameName = 1, ProjectManagerRenameFailed = 2, InvalidNewName = 3 }; /** @returns Returns the project that the item belongs to. */ IProject* project() const; /** @returns If this item is a folder, it returns a pointer to the folder, otherwise returns a 0 pointer. */ virtual ProjectFolderItem *folder() const; /** @returns If this item is a target, it returns a pointer to the target, otherwise returns a 0 pointer. */ virtual ProjectTargetItem *target() const; /** @returns If this item is a file, it returns a pointer to the file, otherwise returns a 0 pointer. */ virtual ProjectFileItem *file() const; /** @returns If this item is a file, it returns a pointer to the file, otherwise returns a 0 pointer. */ virtual ProjectExecutableTargetItem *executable() const; /** @returns Returns a list of the folders that have this object as the parent. */ QList folderList() const; /** @returns Returns a list of the targets that have this object as the parent. */ QList targetList() const; /** @returns Returns a list of the files that have this object as the parent. */ QList fileList() const; virtual bool lessThan( const KDevelop::ProjectBaseItem* ) const; static bool pathLessThan(KDevelop::ProjectBaseItem* item1, KDevelop::ProjectBaseItem* item2); /** @returns the @p row item in the list of children of this item or 0 if there is no such child. */ ProjectBaseItem* child( int row ) const; /** @returns the list of children of this item. */ QList children() const; /** @returns a valid QModelIndex for usage with the model API for this item. */ QModelIndex index() const; /** @returns The parent item if this item has one, else it return 0. */ virtual ProjectBaseItem* parent() const; /** @returns the displayed text of this item. */ QString text() const; /** @returns the row in the list of children of this items parent, or -1. */ int row() const; /** @returns the number of children of this item, or 0 if there are none. */ int rowCount() const; /** @returns the model to which this item belongs, or 0 if its not associated to a model. */ ProjectModel* model() const; /** * Adds a new child item to this item. */ void appendRow( ProjectBaseItem* item ); /** * Removes and deletes the item at the given @p row if there is one. */ void removeRow( int row ); /** * Removes and deletes the @p count items after the given @p row if there is one. */ void removeRows( int row, int count ); /** * Returns and removes the item at the given @p row if there is one. */ ProjectBaseItem* takeRow( int row ); /** @returns RTTI info, allows one to know the type of item */ virtual int type() const; /** @returns a string to pass to QIcon::fromTheme() as icon-name suitable to represent this item. */ virtual QString iconName() const; /** * Set the path of this item. * * @note This function never renames the item in the project manager or * on the filesystem, it only changes the path and possibly the text nothing else. */ virtual void setPath( const Path& ); /** * @returns the path of this item. */ Path path() const; /** * @returns the basename of this items path (if any) * * Convenience function, returns the same as @c text() for most items. */ QString baseName() const; /** * Renames the item to the new name. * @returns status information whether the renaming succeeded. */ virtual RenameStatus rename( const QString& newname ); bool isProjectRoot() const; /** * Default flags: Qt::ItemIsEnabled | Qt::ItemIsSelectable * * @returns the flags supported by the item */ virtual Qt::ItemFlags flags(); /** * Sets what flags should be returned by ::flags() method. */ void setFlags(Qt::ItemFlags flags); protected: /** * Allows to change the displayed text of this item. * * Most items assume text == baseName so this is *not* public. * * @param text the new text */ void setText( const QString& text ); class ProjectBaseItemPrivate* const d_ptr; void setRow( int row ); void setModel( ProjectModel* model ); private: Q_DECLARE_PRIVATE(ProjectBaseItem) friend class ProjectModel; }; /** * Implementation of the ProjectBaseItem interface that is specific to a * folder */ class KDEVPLATFORMPROJECT_EXPORT ProjectFolderItem: public ProjectBaseItem { public: /** * Create a new ProjectFolderItem with the given @p path and an optional @p parent * in the given @p project. */ ProjectFolderItem( IProject* project, const Path& path, ProjectBaseItem* parent = nullptr ); /** * Create a child ProjectFolderItem of @p parent with @p name. * * The path is set automatically. */ ProjectFolderItem( const QString& name, ProjectBaseItem *parent ); ~ProjectFolderItem() override; void setPath(const Path& ) override; ProjectFolderItem *folder() const override; ///Reimplemented from QStandardItem int type() const override; /** * Get the folder name, equal to path().fileName() or text(). */ QString folderName() const; /** @returns Returns whether this folder directly contains the specified file or folder. */ bool hasFileOrFolder(const QString& name) const; QString iconName() const override; RenameStatus rename(const QString& newname) override; private: void propagateRename( const Path& newBase ) const; }; /** * Folder which contains buildable targets as part of a buildable project */ class KDEVPLATFORMPROJECT_EXPORT ProjectBuildFolderItem: public ProjectFolderItem { public: /** * Create a new ProjectBuildFolderItem with the given @p path with the optional * parent @p parent in the given @p project. */ ProjectBuildFolderItem( IProject* project, const Path &path, ProjectBaseItem* parent = nullptr ); /** * Create a child ProjectBuildFolderItem of @p parent with @p name. * * The path is set automatically. */ ProjectBuildFolderItem( const QString &name, ProjectBaseItem *parent ); ///Reimplemented from QStandardItem int type() const override; QString iconName() const override; }; /** * Object which represents a target in a build system. * * This object contains all properties specific to a target. */ class KDEVPLATFORMPROJECT_EXPORT ProjectTargetItem: public ProjectBaseItem { public: ProjectTargetItem( IProject*, const QString &name, ProjectBaseItem *parent = nullptr ); ///Reimplemented from QStandardItem int type() const override; ProjectTargetItem *target() const override; QString iconName() const override; void setPath(const Path& path ) override; }; /** * Object which represents an executable target in a build system. * * This object contains all properties specific to an executable. */ class KDEVPLATFORMPROJECT_EXPORT ProjectExecutableTargetItem: public ProjectTargetItem { public: ProjectExecutableTargetItem( IProject*, const QString &name, ProjectBaseItem *parent = nullptr ); ProjectExecutableTargetItem *executable() const override; int type() const override; virtual QUrl builtUrl() const=0; virtual QUrl installedUrl() const=0; }; /** * Object which represents a library target in a build system. * * This object contains all properties specific to a library. */ class KDEVPLATFORMPROJECT_EXPORT ProjectLibraryTargetItem: public ProjectTargetItem { public: ProjectLibraryTargetItem(IProject* project, const QString &name, ProjectBaseItem *parent = nullptr ); int type() const override; }; /** * Object which represents a file. */ class KDEVPLATFORMPROJECT_EXPORT ProjectFileItem: public ProjectBaseItem { public: /** * Create a new ProjectFileItem with the given @p path and an optional @p parent * in the given @p project. */ ProjectFileItem( IProject* project, const Path& path, ProjectBaseItem* parent = nullptr ); /** * Create a child ProjectFileItem of @p parent with the given @p name. * * The path is set automatically. */ ProjectFileItem( const QString& name, ProjectBaseItem *parent ); ~ProjectFileItem() override; ///Reimplemented from QStandardItem int type() const override; ProjectFileItem *file() const override; /** * @returns the file name, equal to path().fileName() or text() */ QString fileName() const; void setPath( const Path& ) override; QString iconName() const override; RenameStatus rename(const QString& newname) override; /** * @return the items indexed path, which is often required for performant * lookups or memory efficient storage. */ IndexedString indexedPath() const; }; /** * Class providing some convenience methods for accessing the project model * @todo: maybe switch to QAbstractItemModel, would make the implementation * for at least the checkbox-behaviour easier */ class KDEVPLATFORMPROJECT_EXPORT ProjectModel: public QAbstractItemModel { Q_OBJECT public: enum Roles { ProjectRole = Qt::UserRole+1 , ProjectItemRole , UrlRole , LastRole }; explicit ProjectModel( QObject *parent = nullptr ); ~ProjectModel() override; void clear(); void appendRow( ProjectBaseItem* item ); void removeRow( int row ); ProjectBaseItem* takeRow( int row ); ProjectBaseItem* itemAt( int row ) const; QList topItems() const; QModelIndex pathToIndex(const QStringList& tofetch) const; QStringList pathFromIndex(const QModelIndex& index) const; QModelIndex indexFromItem( const ProjectBaseItem* item ) const; ProjectBaseItem* itemFromIndex( const QModelIndex& ) const; int columnCount( const QModelIndex& parent = QModelIndex() ) const override; QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const override; QModelIndex parent( const QModelIndex& child ) const override; int rowCount( const QModelIndex& parent = QModelIndex() ) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; bool insertColumns(int column, int count, const QModelIndex& parent = QModelIndex()) override; bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()) override; Qt::ItemFlags flags(const QModelIndex& index) const override; Qt::DropActions supportedDropActions() const override; /** * @return all items for the given indexed path. */ QList itemsForPath(const IndexedString& path) const; /** * Returns the first item for the given indexed path. */ ProjectBaseItem* itemForPath(const IndexedString& path) const; private: class ProjectModelPrivate* const d; friend class ProjectBaseItem; }; KDEVPLATFORMPROJECT_EXPORT QStringList joinProjectBasePath( const QStringList& partialpath, KDevelop::ProjectBaseItem* item ); KDEVPLATFORMPROJECT_EXPORT QStringList removeProjectBasePath( const QStringList& fullpath, KDevelop::ProjectBaseItem* item ); } Q_DECLARE_METATYPE(KDevelop::ProjectBaseItem*) Q_DECLARE_METATYPE(KDevelop::ProjectFolderItem*) Q_DECLARE_METATYPE(KDevelop::ProjectFileItem*) Q_DECLARE_METATYPE(KDevelop::ProjectLibraryTargetItem*) Q_DECLARE_METATYPE(KDevelop::ProjectExecutableTargetItem*) Q_DECLARE_METATYPE(KDevelop::ProjectTargetItem*) Q_DECLARE_METATYPE(KDevelop::ProjectBuildFolderItem*) Q_DECLARE_METATYPE(QList) #endif // KDEVPLATFORM_PROJECTMODEL_H diff --git a/project/tests/projectmodelperformancetest.h b/project/tests/projectmodelperformancetest.h index 04f447e29..a7230395e 100644 --- a/project/tests/projectmodelperformancetest.h +++ b/project/tests/projectmodelperformancetest.h @@ -1,52 +1,52 @@ /*************************************************************************** * Copyright 2010 Andreas Pakulat * * * * 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. * ***************************************************************************/ #ifndef KDEVELOP_PROJECT_PROJECTMODELPERFORMANCETEST_INCLUDED #define KDEVELOP_PROJECT_PROJECTMODELPERFORMANCETEST_INCLUDED #include -#include +#include namespace KDevelop { class ProjectModel; class ProjectBaseItem; } class QTreeView; class ProjectModelPerformanceTest : public QWidget { Q_OBJECT public: explicit ProjectModelPerformanceTest(QWidget* parent = nullptr); ~ProjectModelPerformanceTest() override; private slots: void init(); void addSmallTree(); void addBigTree(); void addBigTreeDelayed(); void addItemDelayed(); private: QStack currentParent; int originalWidth; KDevelop::ProjectModel* model; QTreeView* view; }; #endif diff --git a/project/tests/test_projectmodel.cpp b/project/tests/test_projectmodel.cpp index 79cc7f229..fece99a44 100644 --- a/project/tests/test_projectmodel.cpp +++ b/project/tests/test_projectmodel.cpp @@ -1,545 +1,545 @@ /*************************************************************************** * Copyright 2010 Andreas Pakulat * * * * 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 "test_projectmodel.h" -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; void debugItemModel(QAbstractItemModel* m, const QModelIndex& parent=QModelIndex(), int depth=0) { Q_ASSERT(m); qDebug() << QByteArray(depth*2, '-') << m->data(parent).toString(); for(int i=0; irowCount(parent); i++) { debugItemModel(m, m->index(i, 0, parent), depth+1); } } void TestProjectModel::initTestCase() { AutoTestShell::init(); TestCore::initialize(Core::NoUi); qRegisterMetaType("QModelIndex"); model = ICore::self()->projectController()->projectModel(); new ModelTest( model, this ); proxy = new ProjectProxyModel( model ); new ModelTest(proxy, proxy); proxy->setSourceModel(model); } void TestProjectModel::init() { model->clear(); } void TestProjectModel::cleanupTestCase() { TestCore::shutdown(); } void TestProjectModel::testCreateFileSystemItems() { QFETCH( int, itemType ); QFETCH( Path, itemPath ); QFETCH( Path, expectedItemPath ); QFETCH( QString, expectedItemText ); QFETCH( QStringList, expectedRelativeItemPath ); QFETCH( int, expectedItemRow ); ProjectBaseItem* newitem = nullptr; switch( itemType ) { case ProjectBaseItem::Folder: newitem = new ProjectFolderItem( nullptr, itemPath ); break; case ProjectBaseItem::BuildFolder: newitem = new ProjectBuildFolderItem( nullptr, itemPath ); break; case ProjectBaseItem::File: newitem = new ProjectFileItem( nullptr, itemPath ); break; } int origRowCount = model->rowCount(); model->appendRow( newitem ); QCOMPARE( model->rowCount(), origRowCount+1 ); QCOMPARE( newitem->row(), expectedItemRow ); QModelIndex idx = model->index( expectedItemRow, 0, QModelIndex() ); QVERIFY( model->itemFromIndex( idx ) ); QCOMPARE( model->itemFromIndex( idx ), newitem ); QCOMPARE( newitem->text(), expectedItemText ); QCOMPARE( newitem->path(), expectedItemPath ); if( itemType == ProjectBaseItem::File ) { QCOMPARE( dynamic_cast( newitem )->fileName(), expectedItemText ); } if( itemType == ProjectBaseItem::Folder || itemType == ProjectBaseItem::BuildFolder ) { QCOMPARE( dynamic_cast( newitem )->folderName(), expectedItemText ); } QCOMPARE( newitem->type(), itemType ); QCOMPARE( model->data( idx ).toString(), expectedItemText ); QCOMPARE( model->indexFromItem( newitem ), idx ); QCOMPARE( model->pathFromIndex( idx ), expectedRelativeItemPath ); QCOMPARE( model->pathToIndex( expectedRelativeItemPath ), idx ); } void TestProjectModel::testCreateFileSystemItems_data() { QTest::addColumn( "itemType" ); QTest::addColumn( "itemPath" ); QTest::addColumn( "expectedItemPath" ); QTest::addColumn( "expectedItemText" ); QTest::addColumn( "expectedRelativeItemPath" ); QTest::addColumn( "expectedItemRow" ); QTest::newRow("RootFolder") << (int)ProjectBaseItem::Folder << Path(QUrl::fromLocalFile(QStringLiteral("/rootdir"))) << Path(QUrl::fromLocalFile(QStringLiteral("/rootdir/"))) << QStringLiteral("rootdir") << ( QStringList() << QStringLiteral("rootdir") ) << 0; QTest::newRow("RootBuildFolder") << (int)ProjectBaseItem::BuildFolder << Path(QUrl::fromLocalFile(QStringLiteral("/rootdir"))) << Path(QUrl::fromLocalFile(QStringLiteral("/rootdir/"))) << QStringLiteral("rootdir") << ( QStringList() << QStringLiteral("rootdir") ) << 0; QTest::newRow("RootFile") << (int)ProjectBaseItem::File << Path(QUrl::fromLocalFile(QStringLiteral("/rootfile"))) << Path(QUrl::fromLocalFile(QStringLiteral("/rootfile"))) << QStringLiteral("rootfile") << ( QStringList() << QStringLiteral("rootfile") ) << 0; } void TestProjectModel::testCreateTargetItems() { QFETCH( int, itemType ); QFETCH( QString, itemText ); QFETCH( QString, expectedItemText ); QFETCH( QStringList, expectedItemPath ); QFETCH( int, expectedItemRow ); ProjectBaseItem* newitem = nullptr; switch( itemType ) { case ProjectBaseItem::Target: newitem = new ProjectTargetItem( nullptr, itemText ); break; case ProjectBaseItem::LibraryTarget: newitem = new ProjectLibraryTargetItem( nullptr, itemText ); break; } int origRowCount = model->rowCount(); model->appendRow( newitem ); QCOMPARE( model->rowCount(), origRowCount+1 ); QCOMPARE( newitem->row(), expectedItemRow ); QModelIndex idx = model->index( expectedItemRow, 0, QModelIndex() ); QVERIFY( model->itemFromIndex( idx ) ); QCOMPARE( model->itemFromIndex( idx ), newitem ); QCOMPARE( newitem->text(), expectedItemText ); QCOMPARE( newitem->type(), itemType ); QCOMPARE( model->data( idx ).toString(), expectedItemText ); QCOMPARE( model->indexFromItem( newitem ), idx ); QCOMPARE( model->pathFromIndex( idx ), expectedItemPath ); QCOMPARE( model->pathToIndex( expectedItemPath ), idx ); } void TestProjectModel::testCreateTargetItems_data() { QTest::addColumn( "itemType" ); QTest::addColumn( "itemText" ); QTest::addColumn( "expectedItemText" ); QTest::addColumn( "expectedItemPath" ); QTest::addColumn( "expectedItemRow" ); QTest::newRow("RootTarget") << (int)ProjectBaseItem::Target << "target" << QStringLiteral("target") << ( QStringList() << QStringLiteral("target") ) << 0; QTest::newRow("RootLibraryTarget") << (int)ProjectBaseItem::LibraryTarget << "libtarget" << QStringLiteral("libtarget") << ( QStringList() << QStringLiteral("libtarget") ) << 0; } void TestProjectModel::testChangeWithProxyModel() { QSortFilterProxyModel* proxy = new QSortFilterProxyModel( this ); proxy->setSourceModel( model ); ProjectFolderItem* root = new ProjectFolderItem( nullptr, Path(QUrl::fromLocalFile(QStringLiteral("/folder1"))) ); root->appendRow( new ProjectFileItem( nullptr, Path(QUrl::fromLocalFile(QStringLiteral("/folder1/file1"))) ) ); model->appendRow( root ); QCOMPARE( model->rowCount(), 1 ); QCOMPARE( proxy->rowCount(), 1 ); model->removeRow( 0 ); QCOMPARE( model->rowCount(), 0 ); QCOMPARE( proxy->rowCount(), 0 ); } void TestProjectModel::testCreateSimpleHierarchy() { QString folderName = QStringLiteral("rootfolder"); QString fileName = QStringLiteral("file"); QString targetName = QStringLiteral("testtarged"); QString cppFileName = QStringLiteral("file.cpp"); ProjectFolderItem* rootFolder = new ProjectFolderItem( nullptr, Path(QUrl::fromLocalFile("/"+folderName)) ); QCOMPARE(rootFolder->baseName(), folderName); ProjectFileItem* file = new ProjectFileItem( fileName, rootFolder ); QCOMPARE(file->baseName(), fileName); ProjectTargetItem* target = new ProjectTargetItem( nullptr, targetName ); rootFolder->appendRow( target ); ProjectFileItem* targetfile = new ProjectFileItem( nullptr, Path(rootFolder->path(), cppFileName), target ); model->appendRow( rootFolder ); QCOMPARE( model->rowCount(), 1 ); QModelIndex folderIdx = model->index( 0, 0, QModelIndex() ); QCOMPARE( model->data( folderIdx ).toString(), folderName ); QCOMPARE( model->rowCount( folderIdx ), 2 ); QCOMPARE( model->itemFromIndex( folderIdx ), rootFolder ); QVERIFY( rootFolder->hasFileOrFolder( fileName ) ); QModelIndex fileIdx = model->index( 0, 0, folderIdx ); QCOMPARE( model->data( fileIdx ).toString(), fileName ); QCOMPARE( model->rowCount( fileIdx ), 0 ); QCOMPARE( model->itemFromIndex( fileIdx ), file ); QModelIndex targetIdx = model->index( 1, 0, folderIdx ); QCOMPARE( model->data( targetIdx ).toString(), targetName ); QCOMPARE( model->rowCount( targetIdx ), 1 ); QCOMPARE( model->itemFromIndex( targetIdx ), target ); QModelIndex targetFileIdx = model->index( 0, 0, targetIdx ); QCOMPARE( model->data( targetFileIdx ).toString(), cppFileName ); QCOMPARE( model->rowCount( targetFileIdx ), 0 ); QCOMPARE( model->itemFromIndex( targetFileIdx ), targetfile ); rootFolder->removeRow( 1 ); QCOMPARE( model->rowCount( folderIdx ), 1 ); delete file; file = nullptr; // Check that we also find a folder with the fileName new ProjectFolderItem( fileName, rootFolder ); QVERIFY( rootFolder->hasFileOrFolder( fileName ) ); delete rootFolder; QCOMPARE( model->rowCount(), 0 ); } void TestProjectModel::testItemSanity() { ProjectBaseItem* parent = new ProjectBaseItem( nullptr, QStringLiteral("test") ); ProjectBaseItem* child = new ProjectBaseItem( nullptr, QStringLiteral("test"), parent ); ProjectBaseItem* child2 = new ProjectBaseItem( nullptr, QStringLiteral("ztest"), parent ); ProjectFileItem* child3 = new ProjectFileItem( nullptr, Path(QUrl(QStringLiteral("file:///bcd"))), parent ); ProjectFileItem* child4 = new ProjectFileItem( nullptr, Path(QUrl(QStringLiteral("file:///abcd"))), parent ); // Just some basic santiy checks on the API QCOMPARE( parent->child( 0 ), child ); QCOMPARE( parent->row(), -1 ); QVERIFY( !parent->child( -1 ) ); QVERIFY( !parent->file() ); QVERIFY( !parent->folder() ); QVERIFY( !parent->project() ); QVERIFY( !parent->child( parent->rowCount() ) ); QCOMPARE( parent->iconName(), QString() ); QCOMPARE( parent->index(), QModelIndex() ); QCOMPARE( child->type(), (int)ProjectBaseItem::BaseItem ); QCOMPARE( child->lessThan( child2 ), true ); QCOMPARE( child3->lessThan( child4 ), false ); // Check that model is properly emitting data-changes model->appendRow( parent ); QCOMPARE( parent->index(), model->index(0, 0, QModelIndex()) ); QSignalSpy s( model, SIGNAL(dataChanged(QModelIndex,QModelIndex)) ); parent->setPath( Path(QStringLiteral("/newtest")) ); QCOMPARE( s.count(), 1 ); QCOMPARE( model->data( parent->index() ).toString(), QStringLiteral("newtest") ); parent->removeRow( child->row() ); } void TestProjectModel::testTakeRow() { ProjectBaseItem* parent = new ProjectBaseItem( nullptr, QStringLiteral("test") ); ProjectBaseItem* child = new ProjectBaseItem( nullptr, QStringLiteral("test"), parent ); ProjectBaseItem* subchild = new ProjectBaseItem( nullptr, QStringLiteral("subtest"), child ); model->appendRow( parent ); QCOMPARE( parent->model(), model ); QCOMPARE( child->model(), model ); QCOMPARE( subchild->model(), model ); parent->takeRow( child->row() ); QCOMPARE( child->model(), static_cast(nullptr) ); QCOMPARE( subchild->model(), static_cast(nullptr) ); } void TestProjectModel::testRename() { QFETCH( int, itemType ); QFETCH( QString, itemText ); QFETCH( QString, newName ); QFETCH( bool, datachangesignal ); QFETCH( QString, expectedItemText ); QFETCH( int, expectedRenameCode ); const Path projectFolder = Path(QUrl::fromLocalFile(QStringLiteral("/dummyprojectfolder"))); TestProject* proj = new TestProject; ProjectFolderItem* rootItem = new ProjectFolderItem( proj, projectFolder, nullptr); proj->setProjectItem( rootItem ); new ProjectFileItem(QStringLiteral("existing"), rootItem); ProjectBaseItem* item = nullptr; if( itemType == ProjectBaseItem::Target ) { item = new ProjectTargetItem( proj, itemText, rootItem ); } else if( itemType == ProjectBaseItem::File ) { item = new ProjectFileItem( itemText, rootItem ); } else if( itemType == ProjectBaseItem::Folder ) { item = new ProjectFolderItem( itemText, rootItem ); } else if( itemType == ProjectBaseItem::BuildFolder ) { item = new ProjectBuildFolderItem( itemText, rootItem ); } Q_ASSERT( item ); QCOMPARE(item->model(), model); QSignalSpy s( model, SIGNAL(dataChanged(QModelIndex,QModelIndex)) ); ProjectBaseItem::RenameStatus stat = item->rename( newName ); QCOMPARE( (int)stat, expectedRenameCode ); if( datachangesignal ) { QCOMPARE( s.count(), 1 ); QCOMPARE( qvariant_cast( s.takeFirst().at(0) ), item->index() ); } else { QCOMPARE( s.count(), 0 ); } QCOMPARE( item->text(), expectedItemText ); } void TestProjectModel::testRename_data() { QTest::addColumn( "itemType" ); QTest::addColumn( "itemText" ); QTest::addColumn( "newName" ); QTest::addColumn( "datachangesignal" ); QTest::addColumn( "expectedItemText" ); QTest::addColumn( "expectedRenameCode" ); QTest::newRow("RenameableTarget") << (int)ProjectBaseItem::Target << QStringLiteral("target") << QStringLiteral("othertarget") << true << QStringLiteral("othertarget") << (int)ProjectBaseItem::RenameOk; QTest::newRow("RenameableFile") << (int)ProjectBaseItem::File << QStringLiteral("newfile.cpp") << QStringLiteral("otherfile.cpp") << true << QStringLiteral("otherfile.cpp") << (int)ProjectBaseItem::RenameOk; QTest::newRow("SourceAndDestinationFileEqual") << (int)ProjectBaseItem::File << QStringLiteral("newfile.cpp") << QStringLiteral("newfile.cpp") << false << QStringLiteral("newfile.cpp") << (int)ProjectBaseItem::RenameOk; QTest::newRow("RenameableFolder") << (int)ProjectBaseItem::Folder << QStringLiteral("newfolder") << QStringLiteral("otherfolder") << true << QStringLiteral("otherfolder") << (int)ProjectBaseItem::RenameOk; QTest::newRow("SourceAndDestinationFolderEqual") << (int)ProjectBaseItem::Folder << QStringLiteral("newfolder") << QStringLiteral("newfolder") << false << QStringLiteral("newfolder") << (int)ProjectBaseItem::RenameOk; QTest::newRow("RenameableBuildFolder") << (int)ProjectBaseItem::BuildFolder << QStringLiteral("newbfolder") << QStringLiteral("otherbfolder") << true << QStringLiteral("otherbfolder") << (int)ProjectBaseItem::RenameOk; QTest::newRow("SourceAndDestinationBuildFolderEqual") << (int)ProjectBaseItem::BuildFolder << QStringLiteral("newbfolder") << QStringLiteral("newbfolder") << false << QStringLiteral("newbfolder") << (int)ProjectBaseItem::RenameOk; QTest::newRow("ExistingFileError") << (int)ProjectBaseItem::Folder << QStringLiteral("mynew") << QStringLiteral("existing") << false << QStringLiteral("mynew") << (int)ProjectBaseItem::ExistingItemSameName; QTest::newRow("InvalidNameError") << (int)ProjectBaseItem::File << QStringLiteral("mynew") << QStringLiteral("other/bash") << false << QStringLiteral("mynew") << (int)ProjectBaseItem::InvalidNewName; } void TestProjectModel::testWithProject() { TestProject* proj = new TestProject(); ProjectFolderItem* rootItem = new ProjectFolderItem( proj, Path(QUrl::fromLocalFile(QStringLiteral("/dummyprojectfolder"))), nullptr); proj->setProjectItem( rootItem ); ProjectBaseItem* item = model->itemFromIndex( model->index( 0, 0 ) ); QCOMPARE( item, rootItem ); QCOMPARE( item->text(), proj->name() ); QCOMPARE( item->path(), proj->path() ); } void TestProjectModel::testItemsForPath() { QFETCH(Path, path); QFETCH(ProjectBaseItem*, root); QFETCH(int, matches); model->appendRow(root); auto items = model->itemsForPath(IndexedString(path.pathOrUrl())); QCOMPARE(items.size(), matches); foreach(ProjectBaseItem* item, items) { QVERIFY(item->path() == path); } model->clear(); } void TestProjectModel::testItemsForPath_data() { QTest::addColumn("path"); QTest::addColumn("root"); QTest::addColumn("matches"); { ProjectFolderItem* root = new ProjectFolderItem(nullptr, Path(QUrl::fromLocalFile(QStringLiteral("/tmp/")))); ProjectFileItem* file = new ProjectFileItem(QStringLiteral("a"), root); QTest::newRow("find one") << file->path() << static_cast(root) << 1; } { ProjectFolderItem* root = new ProjectFolderItem(nullptr, Path(QUrl::fromLocalFile(QStringLiteral("/tmp/")))); ProjectFolderItem* folder = new ProjectFolderItem(QStringLiteral("a"), root); ProjectFileItem* file = new ProjectFileItem(QStringLiteral("foo"), folder); ProjectTargetItem* target = new ProjectTargetItem(nullptr, QStringLiteral("b"), root); ProjectFileItem* file2 = new ProjectFileItem(nullptr, file->path(), target); Q_UNUSED(file2); QTest::newRow("find two") << file->path() << static_cast(root) << 2; } } void TestProjectModel::testProjectProxyModel() { ProjectFolderItem* root = new ProjectFolderItem(nullptr, Path(QUrl::fromLocalFile(QStringLiteral("/tmp/")))); new ProjectFileItem(QStringLiteral("b1"), root); new ProjectFileItem(QStringLiteral("a1"), root); new ProjectFileItem(QStringLiteral("d1"), root); new ProjectFileItem(QStringLiteral("c1"), root); model->appendRow(root); QModelIndex proxyRoot = proxy->mapFromSource(root->index()); QCOMPARE(model->rowCount(root->index()), 4); QCOMPARE(proxy->rowCount(proxyRoot), 4); QCOMPARE(proxy->index(0, 0, proxy->index(0, 0)).data().toString(), QStringLiteral("a1")); QCOMPARE(proxy->index(1, 0, proxy->index(0, 0)).data().toString(), QStringLiteral("b1")); QCOMPARE(proxy->index(2, 0, proxy->index(0, 0)).data().toString(), QStringLiteral("c1")); QCOMPARE(proxy->index(3, 0, proxy->index(0, 0)).data().toString(), QStringLiteral("d1")); model->clear(); } void TestProjectModel::testProjectFileSet() { TestProject* project = new TestProject; QVERIFY(project->fileSet().isEmpty()); Path path(QUrl::fromLocalFile(QStringLiteral("/tmp/a"))); ProjectFileItem* item = new ProjectFileItem(project, path, project->projectItem()); QCOMPARE(project->fileSet().size(), 1); qDebug() << path << project->fileSet().toList().at(0).toUrl(); QCOMPARE(Path(project->fileSet().toList().at(0).toUrl()), path); delete item; QVERIFY(project->fileSet().isEmpty()); } void TestProjectModel::testProjectFileIcon() { QMimeDatabase db; ProjectFileItem* item = new ProjectFileItem(nullptr, Path(QStringLiteral("/tmp/foo.txt"))); const QString txtIcon = db.mimeTypeForUrl(item->path().toUrl()).iconName(); QCOMPARE(item->iconName(), txtIcon); item->setPath(Path(QStringLiteral("/tmp/bar.cpp"))); QCOMPARE(item->iconName(), db.mimeTypeForUrl(item->path().toUrl()).iconName()); QVERIFY(item->iconName() != txtIcon); } QTEST_MAIN( TestProjectModel) diff --git a/project/tests/test_projectmodel.h b/project/tests/test_projectmodel.h index c3459899c..a98fd76a3 100644 --- a/project/tests/test_projectmodel.h +++ b/project/tests/test_projectmodel.h @@ -1,64 +1,64 @@ /*************************************************************************** * Copyright 2010 Andreas Pakulat * * * * 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. * ***************************************************************************/ #ifndef KDEVELOP_PROJECT_TEST_PROJECTMODEL #define KDEVELOP_PROJECT_TEST_PROJECTMODEL -#include -#include +#include +#include class ProjectProxyModel; namespace KDevelop { class ProjectModel; } class ModelTest; class TestProjectModel : public QObject { Q_OBJECT private slots: void initTestCase(); void init(); void cleanupTestCase(); void testCreateFileSystemItems(); void testCreateFileSystemItems_data(); void testCreateTargetItems(); void testCreateTargetItems_data(); void testCreateSimpleHierarchy(); void testItemSanity(); void testRename(); void testRename_data(); void testChangeWithProxyModel(); void testWithProject(); void testTakeRow(); void testItemsForPath(); void testItemsForPath_data(); void testProjectProxyModel(); void testProjectFileSet(); void testProjectFileIcon(); private: KDevelop::ProjectModel* model; ProjectProxyModel* proxy; }; Q_DECLARE_METATYPE( QModelIndex ) #endif diff --git a/serialization/abstractitemrepository.h b/serialization/abstractitemrepository.h index d5982e2ce..7f9a1c02d 100644 --- a/serialization/abstractitemrepository.h +++ b/serialization/abstractitemrepository.h @@ -1,68 +1,68 @@ /* 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. */ #ifndef ABSTRACTITEMREPOSITORY_H #define ABSTRACTITEMREPOSITORY_H -#include +#include #include "serializationexport.h" class QString; namespace KDevelop { /// Returns a version-number that is used to reset the item-repository after incompatible layout changes. KDEVPLATFORMSERIALIZATION_EXPORT uint staticItemRepositoryVersion(); /// The interface class for an item-repository object. class KDEVPLATFORMSERIALIZATION_EXPORT AbstractItemRepository { public: virtual ~AbstractItemRepository(); /// @param path A shared directory-name that the item-repository is to be loaded from. /// @returns Whether the repository has been opened successfully. virtual bool open(const QString& path) = 0; virtual void close(bool doStore = false) = 0; /// Stores the repository contents to disk, eventually unloading unused data to save memory. virtual void store() = 0; /// Does a big cleanup, removing all non-persistent items in the repositories. /// @returns Count of bytes of data that have been removed. virtual int finalCleanup() = 0; virtual QString repositoryName() const = 0; virtual QString printStatistics() const = 0; }; /// Internal helper class that wraps around a repository object and manages its lifetime. class KDEVPLATFORMSERIALIZATION_EXPORT AbstractRepositoryManager { public: AbstractRepositoryManager(); virtual ~AbstractRepositoryManager(); void deleteRepository(); virtual QMutex* repositoryMutex() const = 0; protected: mutable AbstractItemRepository* m_repository; }; } #endif // ABSTRACTITEMREPOSITORY_H diff --git a/serialization/indexedstring.h b/serialization/indexedstring.h index 9e24c675d..6b6563838 100644 --- a/serialization/indexedstring.h +++ b/serialization/indexedstring.h @@ -1,254 +1,254 @@ /* This file is part of KDevelop Copyright 2008 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. 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, see . */ #ifndef KDEVPLATFORM_INDEXED_STRING_H #define KDEVPLATFORM_INDEXED_STRING_H //krazy:excludeall=dpointer,inline -#include +#include #include #include "referencecounting.h" #include "serializationexport.h" namespace KDevelop { /** * This string does "disk reference-counting", which means that reference-counts are maintainted, * but only when the string is in a disk-stored location. The file referencecounting.h is used * to manage this condition. * * Whenever reference-counting is enabled for a range that contains the IndexedString, it will * manipulate the reference-counts. * * The duchain storage mechanisms automatically are about correctly managing that condition, * so you don't need to care, and can just use this class in every duchain data type without * restrictions. * * @warning Do not use IndexedString after QCoreApplication::aboutToQuit() has been emitted, * items that are not disk-referenced will be invalid at that point. * * @note Empty strings have an index of zero. * * @note Strings of length one are not put into the repository, but are encoded directly within * the index: They are encoded like @c 0xffff00bb where @c bb is the byte of the character. */ class KDEVPLATFORMSERIALIZATION_EXPORT IndexedString { public: IndexedString(); /** * @param str must be a utf8 encoded string, does not need to be 0-terminated. * @param length must be its length in bytes. * @param hash must be a hash as constructed with the here defined hash functions. * If it is zero, it will be computed. */ explicit IndexedString( const char* str, unsigned short length, unsigned int hash = 0 ); /** * Needs a zero terminated string. When the information is already available, * try using the other constructor. * * WARNING There is a UTF8-related issue when attempting to retrieve the string * using str from an IndexedString built from this constructor */ explicit IndexedString( const char* str ); explicit IndexedString( char c ); /** * When the information is already available, try using the other constructor. * * @note This is expensive. */ explicit IndexedString( const QString& str ); /** * When the information is already available, try using the other constructor. * * @note This is expensive. */ explicit IndexedString( const QByteArray& str ); IndexedString( IndexedString&& o ) Q_DECL_NOEXCEPT : m_index(o.m_index) { o.m_index = 0; } /** * Returns a not reference-counted IndexedString that represents the given index. * * @warning It is dangerous dealing with indices directly, because it may break * the reference counting logic. Never store pure indices to disk. */ static IndexedString fromIndex( unsigned int index ) { IndexedString ret; ret.m_index = index; return ret; } /** * @warning This is relatively expensive: needs a mutex lock, hash lookups, and eventual loading, * so avoid it when possible. */ static int lengthFromIndex(unsigned int index); IndexedString( const IndexedString& ); ~IndexedString(); /** * Creates an indexed string from a QUrl, this is expensive. */ explicit IndexedString( const QUrl& url ); /** * Re-construct a QUrl from this indexed string, the result can be used with the * QUrl-using constructor. * * @note This is expensive. */ QUrl toUrl() const; inline unsigned int hash() const { return m_index; } /** * The string is uniquely identified by this index. You can use it for comparison. * * @warning It is dangerous dealing with indices directly, because it may break the * reference counting logic. never store pure indices to disk */ inline unsigned int index() const { return m_index; } bool isEmpty() const { return m_index == 0; } /** * @note This is relatively expensive: needs a mutex lock, hash lookups, and eventual loading, * so avoid it when possible. */ int length() const; /** * Returns the underlying c string, in utf-8 encoding. * * @warning The string is not 0-terminated, consider length()! */ const char* c_str() const; /** * Convenience function, avoid using it, it's relatively expensive */ QString str() const; /** * Convenience function, avoid using it, it's relatively expensive (less expensive then str() though) */ QByteArray byteArray() const; IndexedString& operator=(const IndexedString&); IndexedString& operator=(IndexedString&& o) Q_DECL_NOEXCEPT { m_index = o.m_index; o.m_index = 0; return *this; } /** * Fast index-based comparison */ bool operator == ( const IndexedString& rhs ) const { return m_index == rhs.m_index; } /** * Fast index-based comparison */ bool operator != ( const IndexedString& rhs ) const { return m_index != rhs.m_index; } /** * Does not compare alphabetically, uses the index for ordering. */ bool operator < ( const IndexedString& rhs ) const { return m_index < rhs.m_index; } /** * Use this to construct a hash-value on-the-fly * * To read it, just use the hash member, and when a new string is started, call @c clear(). * * This needs very fast performance(per character operation), so it must stay inlined. */ struct RunningHash { enum { HashInitialValue = 5381 }; RunningHash() : hash(HashInitialValue) { //We initialize the hash with zero, because we want empty strings to create a zero hash(invalid) } inline void append(const char c) { hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ } inline void clear() { hash = HashInitialValue; } unsigned int hash; }; static unsigned int hashString(const char* str, unsigned short length); /** * Optimized function that only computes the index of a string * removes the overhead of the IndexedString ref counting */ static uint indexForString(const char* str, unsigned short length, uint hash = 0); static uint indexForString(const QString& str, uint hash = 0); private: explicit IndexedString(bool); uint m_index; }; // the following function would need to be exported in case you'd remove the inline keyword. inline uint qHash( const KDevelop::IndexedString& str ) { return str.index(); } } /** * qDebug() stream operator. Writes the string to the debug output. */ KDEVPLATFORMSERIALIZATION_EXPORT QDebug operator<<(QDebug s, const KDevelop::IndexedString& string); Q_DECLARE_METATYPE(KDevelop::IndexedString) Q_DECLARE_TYPEINFO(KDevelop::IndexedString, Q_MOVABLE_TYPE); #endif diff --git a/serialization/itemrepositoryregistry.cpp b/serialization/itemrepositoryregistry.cpp index a4f8f7c01..f0dd3d75b 100644 --- a/serialization/itemrepositoryregistry.cpp +++ b/serialization/itemrepositoryregistry.cpp @@ -1,410 +1,410 @@ /* 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 "itemrepositoryregistry.h" -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include #include #include "abstractitemrepository.h" #include "debug.h" using namespace KDevelop; namespace { //If KDevelop crashed this many times consicutively, clean up the repository const int crashesBeforeCleanup = 1; void setCrashCounter(QFile& crashesFile, int count) { crashesFile.close(); crashesFile.open(QIODevice::WriteOnly | QIODevice::Truncate); QDataStream writeStream(&crashesFile); writeStream << count; } QString repositoryPathForSession(const KDevelop::ISessionLock::Ptr& session) { QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation); cacheDir += QStringLiteral("/kdevduchain"); QString baseDir = QProcessEnvironment::systemEnvironment().value(QStringLiteral("KDEV_DUCHAIN_DIR"), cacheDir); baseDir += QStringLiteral("/%1-%2").arg(qApp->applicationName(), session->id()); return baseDir; } bool shouldClear(const QString& path) { QDir dir(path); if (!dir.exists()) { return false; } if (getenv("CLEAR_DUCHAIN_DIR")) { qCDebug(SERIALIZATION) << "clearing duchain directory because CLEAR_DUCHAIN_DIR is set"; return true; } if (dir.exists(QStringLiteral("is_writing"))) { qCWarning(SERIALIZATION) << "repository" << path << "was write-locked, it probably is inconsistent"; return true; } if (!dir.exists(QStringLiteral("version_%1").arg(staticItemRepositoryVersion()))) { qCWarning(SERIALIZATION) << "version-hint not found, seems to be an old version"; return true; } QFile crashesFile(dir.filePath(QStringLiteral("crash_counter"))); if (crashesFile.open(QIODevice::ReadOnly)) { int count; QDataStream stream(&crashesFile); stream >> count; qCDebug(SERIALIZATION) << "current count of crashes: " << count; if (count >= crashesBeforeCleanup && !getenv("DONT_CLEAR_DUCHAIN_DIR")) { bool userAnswer = askUser(i18np("The previous session crashed", "Session crashed %1 times in a row", count), i18nc("@action", "Clear cache"), i18nc("@title", "Session crashed"), i18n("The crash may be caused by a corruption of cached data.\n\n" "Press OK if you want KDevelop to clear the cache, otherwise press Cancel if you are sure the crash has another origin.")); if (userAnswer) { qCDebug(SERIALIZATION) << "User chose to clean repository"; return true; } else { setCrashCounter(crashesFile, 1); qCDebug(SERIALIZATION) << "User chose to reset crash counter"; } } else { ///Increase the crash-count. It will be reset if kdevelop is shut down cleanly. setCrashCounter(crashesFile, ++count); } } else { setCrashCounter(crashesFile, 1); } return false; } } namespace KDevelop { struct ItemRepositoryRegistryPrivate { ItemRepositoryRegistry* m_owner; bool m_shallDelete; QString m_path; ISessionLock::Ptr m_sessionLock; QMap m_repositories; QMap m_customCounters; mutable QMutex m_mutex; explicit ItemRepositoryRegistryPrivate(ItemRepositoryRegistry* owner) : m_owner(owner) , m_shallDelete(false) , m_mutex(QMutex::Recursive) { } void lockForWriting(); void unlockForWriting(); void deleteDataDirectory(const QString& path, bool recreate = true); /// @param path A shared directory-path that the item-repositories are to be loaded from. /// @returns Whether the repository registry has been opened successfully. /// If @c false, then all registered repositories should have been deleted. /// @note Currently the given path must reference a hidden directory, just to make sure we're /// not accidentally deleting something important. bool open(const QString& path); /// Close all contained repositories. /// @warning The current state is not stored to disk. void close(); }; //The global item-reposity registry ItemRepositoryRegistry* ItemRepositoryRegistry::m_self = nullptr; ItemRepositoryRegistry::ItemRepositoryRegistry(const ISessionLock::Ptr& session) : d(new ItemRepositoryRegistryPrivate(this)) { Q_ASSERT(session); d->open(repositoryPathForSession(session)); } void ItemRepositoryRegistry::initialize(const ISessionLock::Ptr& session) { if (!m_self) { ///We intentionally leak the registry, to prevent problems in the destruction order, where ///the actual repositories might get deleted later than the repository registry. m_self = new ItemRepositoryRegistry(session); } } ItemRepositoryRegistry* ItemRepositoryRegistry::self() { Q_ASSERT(m_self); return m_self; } void ItemRepositoryRegistry::deleteRepositoryFromDisk(const ISessionLock::Ptr& session) { // Now, as we have only the global item-repository registry, assume that if and only if // the given session is ours, its cache path is used by the said global item-repository registry. const QString repositoryPath = repositoryPathForSession(session); if(m_self && m_self->d->m_path == repositoryPath) { // remove later m_self->d->m_shallDelete = true; } else { // Otherwise, given session is not ours. // remove its item-repository directory directly. QDir(repositoryPath).removeRecursively(); } } QMutex& ItemRepositoryRegistry::mutex() { return d->m_mutex; } QAtomicInt& ItemRepositoryRegistry::getCustomCounter(const QString& identity, int initialValue) { if(!d->m_customCounters.contains(identity)) d->m_customCounters.insert(identity, new QAtomicInt(initialValue)); return *d->m_customCounters[identity]; } ///The global item-repository registry that is used by default ItemRepositoryRegistry& globalItemRepositoryRegistry() { return *ItemRepositoryRegistry::self(); } void ItemRepositoryRegistry::registerRepository(AbstractItemRepository* repository, AbstractRepositoryManager* manager) { QMutexLocker lock(&d->m_mutex); d->m_repositories.insert(repository, manager); if(!d->m_path.isEmpty()) { if(!repository->open(d->m_path)) { d->deleteDataDirectory(d->m_path); qCritical() << "failed to open a repository"; abort(); } } } QString ItemRepositoryRegistry::path() const { //We cannot lock the mutex here, since this may be called with one of the repositories locked, //and that may lead to a deadlock when at the same time a storing is requested return d->m_path; } void ItemRepositoryRegistryPrivate::lockForWriting() { QMutexLocker lock(&m_mutex); //Create is_writing QFile f(m_path + "/is_writing"); f.open(QIODevice::WriteOnly); f.close(); } void ItemRepositoryRegistry::lockForWriting() { d->lockForWriting(); } void ItemRepositoryRegistryPrivate::unlockForWriting() { QMutexLocker lock(&m_mutex); //Delete is_writing QFile::remove(m_path + "/is_writing"); } void ItemRepositoryRegistry::unlockForWriting() { d->unlockForWriting(); } void ItemRepositoryRegistry::unRegisterRepository(AbstractItemRepository* repository) { QMutexLocker lock(&d->m_mutex); Q_ASSERT(d->m_repositories.contains(repository)); repository->close(); d->m_repositories.remove(repository); } //After calling this, the data-directory may be a new one void ItemRepositoryRegistryPrivate::deleteDataDirectory(const QString& path, bool recreate) { QMutexLocker lock(&m_mutex); //lockForWriting creates a file, that prevents any other KDevelop instance from using the directory as it is. //Instead, the other instance will try to delete the directory as well. lockForWriting(); bool result = QDir(path).removeRecursively(); Q_ASSERT(result); Q_UNUSED(result); // Just recreate the directory then; leave old path (as it is dependent on appname and session only). if(recreate) { QDir().mkpath(path); } } bool ItemRepositoryRegistryPrivate::open(const QString& path) { QMutexLocker mlock(&m_mutex); if(m_path == path) { return true; } // Check if the repository shall be cleared if (shouldClear(path)) { qCWarning(SERIALIZATION) << QStringLiteral("The data-repository at %1 has to be cleared.").arg(path); deleteDataDirectory(path); } QDir().mkpath(path); foreach(AbstractItemRepository* repository, m_repositories.keys()) { if(!repository->open(path)) { deleteDataDirectory(path); qCritical() << "failed to open a repository"; abort(); } } QFile f(path + "/Counters"); if(f.open(QIODevice::ReadOnly)) { QDataStream stream(&f); while(!stream.atEnd()) { //Read in all custom counter values QString counterName; stream >> counterName; int counterValue; stream >> counterValue; m_owner->getCustomCounter(counterName, 0) = counterValue; } } m_path = path; return true; } void ItemRepositoryRegistry::store() { QMutexLocker lock(&d->m_mutex); foreach(AbstractItemRepository* repository, d->m_repositories.keys()) { repository->store(); } QFile versionFile(d->m_path + QStringLiteral("/version_%1").arg(staticItemRepositoryVersion())); if(versionFile.open(QIODevice::WriteOnly)) { versionFile.close(); } else { qCWarning(SERIALIZATION) << "Could not open version file for writing"; } //Store all custom counter values QFile f(d->m_path + "/Counters"); if(f.open(QIODevice::WriteOnly)) { f.resize(0); QDataStream stream(&f); for(QMap::const_iterator it = d->m_customCounters.constBegin(); it != d->m_customCounters.constEnd(); ++it) { stream << it.key(); stream << it.value()->fetchAndAddRelaxed(0); } } else { qCWarning(SERIALIZATION) << "Could not open counter file for writing"; } } void ItemRepositoryRegistry::printAllStatistics() const { QMutexLocker lock(&d->m_mutex); foreach(AbstractItemRepository* repository, d->m_repositories.keys()) { qCDebug(SERIALIZATION) << "statistics in" << repository->repositoryName() << ":"; qCDebug(SERIALIZATION) << repository->printStatistics(); } } int ItemRepositoryRegistry::finalCleanup() { QMutexLocker lock(&d->m_mutex); int changed = false; foreach(AbstractItemRepository* repository, d->m_repositories.keys()) { int added = repository->finalCleanup(); changed += added; qCDebug(SERIALIZATION) << "cleaned in" << repository->repositoryName() << ":" << added; } return changed; } void ItemRepositoryRegistryPrivate::close() { QMutexLocker lock(&m_mutex); foreach(AbstractItemRepository* repository, m_repositories.keys()) { repository->close(); } m_path.clear(); } ItemRepositoryRegistry::~ItemRepositoryRegistry() { QMutexLocker lock(&d->m_mutex); d->close(); foreach(QAtomicInt * counter, d->m_customCounters) { delete counter; } delete d; } void ItemRepositoryRegistry::shutdown() { QMutexLocker lock(&d->m_mutex); QString path = d->m_path; // FIXME: we don't close since this can trigger crashes at shutdown // since some items are still referenced, e.g. in static variables // d->close(); if(d->m_shallDelete) { d->deleteDataDirectory(path, false); } else { QFile::remove(path + QLatin1String("/crash_counter")); } } } diff --git a/serialization/stringrepository.h b/serialization/stringrepository.h index 3ecd35a55..8589f27e7 100644 --- a/serialization/stringrepository.h +++ b/serialization/stringrepository.h @@ -1,107 +1,107 @@ /* 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. */ #ifndef KDEVPLATFORM_STRINGREPOSITORY_H #define KDEVPLATFORM_STRINGREPOSITORY_H -#include +#include #include "itemrepository.h" #include "indexedstring.h" namespace Repositories { using namespace KDevelop; struct StringData { unsigned short length; unsigned int itemSize() const { return sizeof(StringData) + length; } unsigned int hash() const { IndexedString::RunningHash running; const char* str = ((const char*)this) + sizeof(StringData); for(int a = length-1; a >= 0; --a) { running.append(*str); ++str; } return running.hash; } }; struct StringRepositoryItemRequest { //The text is supposed to be utf8 encoded StringRepositoryItemRequest(const char* text, unsigned int hash, unsigned short length) : m_hash(hash), m_length(length), m_text(text) { } enum { AverageSize = 10 //This should be the approximate average size of an Item }; typedef unsigned int HashType; //Should return the hash-value associated with this request(For example the hash of a string) HashType hash() const { return m_hash; } //Should return the size of an item created with createItem uint itemSize() const { return sizeof(StringData) + m_length; } //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(StringData* item) const { item->length = m_length; ++item; memcpy(item, m_text, m_length); } static void destroy(StringData*, KDevelop::AbstractItemRepository&) { } static bool persistent(const StringData*) { //Reference-counting not supported in the normal string repository return true; } //Should return whether the here requested item equals the given item bool equals(const StringData* item) const { return item->length == m_length && (memcmp(++item, m_text, m_length) == 0); } unsigned int m_hash; unsigned short m_length; const char* m_text; }; typedef ItemRepository StringRepository; ///@param item must be valid(nonzero) inline QString stringFromItem(const StringData* item) { const unsigned short* textPos = (unsigned short*)(item+1); return QString::fromUtf8((char*)textPos, item->length); } inline QByteArray arrayFromItem(const StringData* item) { const unsigned short* textPos = (unsigned short*)(item+1); return QByteArray((char*)textPos, item->length); } } #endif diff --git a/serialization/tests/bench_itemrepository.cpp b/serialization/tests/bench_itemrepository.cpp index 23e7721f1..d96c9ddfd 100644 --- a/serialization/tests/bench_itemrepository.cpp +++ b/serialization/tests/bench_itemrepository.cpp @@ -1,223 +1,223 @@ /* * This file is part of KDevelop * Copyright 2012-2013 Milian Wolff * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #include "bench_itemrepository.h" #include #include #include #include #include -#include +#include QTEST_GUILESS_MAIN(TestItemRepository); using namespace KDevelop; struct TestData { uint length; uint itemSize() const { return sizeof(TestData) + length; } uint hash() const { const char* str = ((const char*)this) + sizeof(TestData); return IndexedString::hashString(str, length); } }; struct TestDataRepositoryItemRequest { //The text is supposed to be utf8 encoded TestDataRepositoryItemRequest(const char* text, uint length) : m_length(length) , m_text(text) , m_hash(IndexedString::hashString(text, length)) { } enum { AverageSize = 10 //This should be the approximate average size of an Item }; typedef uint HashType; //Should return the hash-value associated with this request(For example the hash of a string) HashType hash() const { return m_hash; } //Should return the size of an item created with createItem uint itemSize() const { return sizeof(TestData) + m_length; } //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(TestData* item) const { item->length = m_length; ++item; memcpy(item, m_text, m_length); } static void destroy(TestData* item, KDevelop::AbstractItemRepository&) { Q_UNUSED(item); //Nothing to do here (The object is not intelligent) } static bool persistent(const TestData* item) { Q_UNUSED(item); return true; } //Should return whether the here requested item equals the given item bool equals(const TestData* item) const { return item->length == m_length && (memcmp(++item, m_text, m_length) == 0); } unsigned short m_length; const char* m_text; unsigned int m_hash; }; typedef ItemRepository TestDataRepository; void TestItemRepository::initTestCase() { AutoTestShell::init(); TestCore::initialize(Core::NoUi); } void TestItemRepository::cleanupTestCase() { TestCore::shutdown(); } static QVector generateData() { QVector data; static const int NUM_ITEMS = 100000; data.resize(NUM_ITEMS); for(int i = 0; i < NUM_ITEMS; ++i) { data[i] = QStringLiteral("/foo/%1").arg(i); } return data; } static QVector insertData(const QVector& data, TestDataRepository& repo) { QVector indices; indices.reserve(data.size()); foreach(const QString& item, data) { const QByteArray byteArray = item.toUtf8(); indices << repo.index(TestDataRepositoryItemRequest(byteArray.constData(), byteArray.length())); } return indices; } void TestItemRepository::insert() { TestDataRepository repo("TestDataRepositoryInsert"); QVector data = generateData(); QVector indices; QBENCHMARK_ONCE { indices = insertData(data, repo); repo.store(); } Q_ASSERT(indices.size() == data.size()); QCOMPARE(repo.statistics().totalItems, uint(data.size())); } void TestItemRepository::remove() { TestDataRepository repo("TestDataRepositoryRemove"); QVector data = generateData(); QVector indices = insertData(data, repo); repo.store(); QVERIFY(indices.size() == indices.toList().toSet().size()); QVERIFY(indices.size() == data.size()); QBENCHMARK_ONCE { foreach(uint index, indices) { repo.deleteItem(index); } repo.store(); } QCOMPARE(repo.statistics().totalItems, 0u); } void TestItemRepository::removeDisk() { QVector data = generateData(); QVector indices; { TestDataRepository repo("TestDataRepositoryRemoveDisk"); indices = insertData(data, repo); repo.store(); } TestDataRepository repo("TestDataRepositoryRemoveDisk"); QVERIFY(repo.statistics().totalItems == static_cast(data.size())); QBENCHMARK_ONCE { foreach(uint index, indices) { repo.deleteItem(index); } repo.store(); } QCOMPARE(repo.statistics().totalItems, 0u); } void TestItemRepository::lookupKey() { TestDataRepository repo("TestDataRepositoryLookupKey"); QVector data = generateData(); QVector indices = insertData(data, repo); srand(0); std::random_shuffle(indices.begin(), indices.end()); QBENCHMARK { foreach(uint index, indices) { repo.itemFromIndex(index); } } } void TestItemRepository::lookupValue() { TestDataRepository repo("TestDataRepositoryLookupValue"); QVector data = generateData(); QVector indices = insertData(data, repo); srand(0); std::random_shuffle(indices.begin(), indices.end()); QBENCHMARK { foreach(const QString& item, data) { const QByteArray byteArray = item.toUtf8(); repo.findIndex(TestDataRepositoryItemRequest(byteArray.constData(), byteArray.length())); } } } diff --git a/serialization/tests/test_indexedstring.cpp b/serialization/tests/test_indexedstring.cpp index cd43c245d..e0362f448 100644 --- a/serialization/tests/test_indexedstring.cpp +++ b/serialization/tests/test_indexedstring.cpp @@ -1,251 +1,251 @@ /* * This file is part of KDevelop * Copyright 2012-2013 Milian Wolff * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #include "test_indexedstring.h" #include #include #include #include -#include +#include #include QTEST_GUILESS_MAIN(TestIndexedString); using namespace KDevelop; void TestIndexedString::initTestCase() { AutoTestShell::init(); TestCore::initialize(Core::NoUi); } void TestIndexedString::cleanupTestCase() { TestCore::shutdown(); } void TestIndexedString::testUrl_data() { QTest::addColumn("url"); QTest::addColumn("string"); QTest::newRow("empty") << QUrl() << QString(); QTest::newRow("/") << QUrl::fromLocalFile(QStringLiteral("/")) << QStringLiteral("/"); QTest::newRow("/foo/bar") << QUrl::fromLocalFile(QStringLiteral("/foo/bar")) << QStringLiteral("/foo/bar"); QTest::newRow("http://foo.com/") << QUrl(QStringLiteral("http://foo.com/")) << QStringLiteral("http://foo.com/"); QTest::newRow("http://foo.com/bar/asdf") << QUrl(QStringLiteral("http://foo.com/bar/asdf")) << QStringLiteral("http://foo.com/bar/asdf"); QTest::newRow("file:///bar/asdf") << QUrl(QStringLiteral("file:///bar/asdf")) << QStringLiteral("/bar/asdf"); #ifdef Q_OS_WIN // Make sure we're not running into https://bugreports.qt.io/browse/QTBUG-41729 QTest::newRow("file:///C:/bar/asdf") << QUrl("file:///C:/bar/asdf") << QStringLiteral("C:/bar/asdf"); #endif } void TestIndexedString::testUrl() { QFETCH(QUrl, url); IndexedString indexed(url); QCOMPARE(indexed.toUrl(), url); QTEST(indexed.str(), "string"); } static QVector generateData() { QVector data; static const int NUM_ITEMS = 100000; data.resize(NUM_ITEMS); for(int i = 0; i < NUM_ITEMS; ++i) { data[i] = QStringLiteral("/foo/%1").arg(i); } return data; } void TestIndexedString::bench_index() { QVector data = generateData(); QBENCHMARK { foreach(const QString& item, data) { IndexedString idx(item); Q_UNUSED(idx); } } } static QVector setupTest() { QVector data = generateData(); QVector indices; indices.reserve(data.size()); foreach(const QString& item, data) { IndexedString idx(item); indices << idx.index(); } return indices; } void TestIndexedString::bench_length() { QVector indices = setupTest(); QBENCHMARK { foreach(uint index, indices) { IndexedString str = IndexedString::fromIndex(index); str.length(); } } } void TestIndexedString::bench_qstring() { QVector indices = setupTest(); QBENCHMARK { foreach(uint index, indices) { IndexedString str = IndexedString::fromIndex(index); str.str(); } } } void TestIndexedString::bench_kurl() { QVector indices = setupTest(); QBENCHMARK { foreach(uint index, indices) { IndexedString str = IndexedString::fromIndex(index); str.toUrl(); } } } void TestIndexedString::bench_qhashQString() { QVector data = generateData(); quint64 sum = 0; QBENCHMARK { foreach (const auto& string, data) { sum += qHash(string); } } QVERIFY(sum > 0); } void TestIndexedString::bench_qhashIndexedString() { QVector indices = setupTest(); quint64 sum = 0; QBENCHMARK { foreach(uint index, indices) { sum += qHash(IndexedString::fromIndex(index)); } } QVERIFY(sum > 0); } void TestIndexedString::bench_hashString() { QVector strings = generateData(); QVector byteArrays; byteArrays.reserve(strings.size()); foreach (const auto& string, strings) { byteArrays << string.toUtf8(); } quint64 sum = 0; QBENCHMARK { foreach (const auto& array, byteArrays) { sum += IndexedString::hashString(array.constData(), array.length()); } } QVERIFY(sum > 0); } void TestIndexedString::bench_kdevhash() { QVector strings = generateData(); QVector byteArrays; byteArrays.reserve(strings.size()); foreach (const auto& string, strings) { byteArrays << string.toUtf8(); } quint64 sum = 0; QBENCHMARK { foreach (const auto& array, byteArrays) { sum += KDevHash() << array; } } QVERIFY(sum > 0); } void TestIndexedString::bench_qSet() { QVector indices = setupTest(); QSet set; QBENCHMARK { foreach(uint index, indices) { set.insert(IndexedString::fromIndex(index)); } } } void TestIndexedString::test() { QFETCH(QString, data); IndexedString indexed(data); QCOMPARE(indexed.str(), data); QCOMPARE(indexed.index(), IndexedString::indexForString(data)); const auto byteArrayData = data.toUtf8(); QEXPECT_FAIL("char-utf8", "UTF-8 gets decoded and the char data is stored internally", Continue); QEXPECT_FAIL("string-utf8", "UTF-8 gets decoded and the char data is stored internally", Continue); QCOMPARE(indexed.length(), data.length()); // fallback until we rely on internal utf8 encoding QCOMPARE(indexed.length(), byteArrayData.length()); QCOMPARE(indexed.byteArray(), byteArrayData); QVERIFY(!strncmp(indexed.c_str(), byteArrayData.data(), byteArrayData.length())); QCOMPARE(indexed.index(), IndexedString::indexForString(byteArrayData.data(), byteArrayData.length())); IndexedString moved = std::move(indexed); QCOMPARE(indexed, IndexedString()); QVERIFY(indexed.isEmpty()); QCOMPARE(moved.str(), data); } void TestIndexedString::test_data() { QTest::addColumn("data"); QTest::newRow("empty") << QString(); QTest::newRow("char-ascii") << QStringLiteral("a"); QTest::newRow("char-utf8") << QStringLiteral("ä"); QTest::newRow("string-ascii") << QStringLiteral("asdf()?="); QTest::newRow("string-utf8") << QStringLiteral("æſðđäöü"); } void TestIndexedString::testCString() { IndexedString str(nullptr); QCOMPARE(str.index(), 0u); QVERIFY(str.isEmpty()); } diff --git a/shell/core.cpp b/shell/core.cpp index e2eddb88b..656aa2a01 100644 --- a/shell/core.cpp +++ b/shell/core.cpp @@ -1,586 +1,586 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * Copyright 2007 Kris Wong * * * * 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 "core.h" #include "core_p.h" #include -#include +#include #include #include #include #include "mainwindow.h" #include "sessioncontroller.h" #include "uicontroller.h" #include "plugincontroller.h" #include "projectcontroller.h" #include "partcontroller.h" #include "languagecontroller.h" #include "documentcontroller.h" #include "runcontroller.h" #include "session.h" #include "documentationcontroller.h" #include "sourceformattercontroller.h" #include "progresswidget/progressmanager.h" #include "selectioncontroller.h" #include "debugcontroller.h" #include "kdevplatform_version.h" #include "workingsetcontroller.h" #include "testcontroller.h" #include "debug.h" #include #include #include namespace { void shutdownGracefully(int sig) { static volatile std::sig_atomic_t handlingSignal = 0; if ( !handlingSignal ) { handlingSignal = 1; qCDebug(SHELL) << "signal " << sig << " received, shutting down gracefully"; QCoreApplication* app = QCoreApplication::instance(); if (QApplication* guiApp = qobject_cast(app)) { guiApp->closeAllWindows(); } app->quit(); return; } // re-raise signal with default handler and trigger program termination std::signal(sig, SIG_DFL); std::raise(sig); } void installSignalHandler() { #ifdef SIGHUP std::signal(SIGHUP, shutdownGracefully); #endif #ifdef SIGINT std::signal(SIGINT, shutdownGracefully); #endif #ifdef SIGTERM std::signal(SIGTERM, shutdownGracefully); #endif } } namespace KDevelop { Core *Core::m_self = nullptr; KAboutData createAboutData() { KAboutData aboutData( QStringLiteral("kdevplatform"), i18n("KDevelop Platform"), QStringLiteral(KDEVPLATFORM_VERSION_STRING), i18n("Development Platform for IDE-like Applications"), KAboutLicense::LGPL_V2, i18n("Copyright 2004-2017, The KDevelop developers"), QString(), QStringLiteral("https://www.kdevelop.org/")); aboutData.addAuthor( i18n("Andreas Pakulat"), i18n( "Architecture, VCS Support, Project Management Support, QMake Projectmanager" ), QStringLiteral("apaku@gmx.de") ); aboutData.addAuthor( i18n("Alexander Dymo"), i18n( "Architecture, Sublime UI, Ruby support" ), QStringLiteral("adymo@kdevelop.org") ); aboutData.addAuthor( i18n("David Nolden"), i18n( "Definition-Use Chain, C++ Support" ), QStringLiteral("david.nolden.kdevelop@art-master.de") ); aboutData.addAuthor( i18n("Aleix Pol Gonzalez"), i18n( "CMake Support, Run Support, Kross Support" ), QStringLiteral("aleixpol@kde.org") ); aboutData.addAuthor( i18n("Vladimir Prus"), i18n( "GDB integration" ), QStringLiteral("ghost@cs.msu.su") ); aboutData.addAuthor( i18n("Hamish Rodda"), i18n( "Text editor integration, definition-use chain" ), QStringLiteral("rodda@kde.org") ); aboutData.addCredit( i18n("Matt Rogers"), QString(), QStringLiteral("mattr@kde.org")); aboutData.addCredit( i18n("Cédric Pasteur"), i18n("astyle and indent support"), QStringLiteral("cedric.pasteur@free.fr") ); aboutData.addCredit( i18n("Evgeniy Ivanov"), i18n("Distributed VCS, Git, Mercurial"), QStringLiteral("powerfox@kde.ru") ); //Veritas is outside in playground currently. //aboutData.addCredit( i18n("Manuel Breugelmanns"), i18n( "Veritas, QTest integraton"), "mbr.nxi@gmail.com" ); aboutData.addCredit( i18n("Robert Gruber") , i18n( "SnippetPart, debugger and usability patches" ), QStringLiteral("rgruber@users.sourceforge.net") ); aboutData.addCredit( i18n("Dukju Ahn"), i18n( "Subversion plugin, Custom Make Manager, Overall improvements" ), QStringLiteral("dukjuahn@gmail.com") ); aboutData.addAuthor( i18n("Niko Sams"), i18n( "GDB integration, Webdevelopment Plugins" ), QStringLiteral("niko.sams@gmail.com") ); aboutData.addAuthor( i18n("Milian Wolff"), i18n( "Generic manager, Webdevelopment Plugins, Snippets, Performance" ), QStringLiteral("mail@milianw.de") ); aboutData.addAuthor( i18n("Kevin Funk"), i18n( "Co-maintainer, C++/Clang, QA, Windows Support, Performance, Website" ), QStringLiteral("kfunk@kde.org") ); aboutData.addAuthor( i18n("Sven Brauch"), i18n( "Co-maintainer, AppImage, Python Support, User Interface improvements" ), QStringLiteral("svenbrauch@gmx.de") ); return aboutData; } CorePrivate::CorePrivate(Core *core): m_aboutData( createAboutData() ), m_core(core), m_cleanedUp(false), m_shuttingDown(false) { } bool CorePrivate::initialize(Core::Setup mode, QString session ) { m_mode=mode; qCDebug(SHELL) << "Creating controllers"; if( !sessionController ) { sessionController = new SessionController(m_core); } if( !workingSetController && !(mode & Core::NoUi) ) { workingSetController = new WorkingSetController(); } qCDebug(SHELL) << "Creating ui controller"; if( !uiController ) { uiController = new UiController(m_core); } qCDebug(SHELL) << "Creating plugin controller"; if( !pluginController ) { pluginController = new PluginController(m_core); const auto pluginInfos = pluginController->allPluginInfos(); if (pluginInfos.isEmpty()) { QMessageBox::critical(nullptr, i18n("Could not find any plugins"), i18n("

Could not find any plugins during startup.
" "Please make sure QT_PLUGIN_PATH is set correctly.

" "Refer to
this article for more information."), QMessageBox::Abort, QMessageBox::Abort); qCWarning(SHELL) << "Could not find any plugins, aborting"; return false; } } if( !partController && !(mode & Core::NoUi)) { partController = new PartController(m_core, uiController.data()->defaultMainWindow()); } if( !projectController ) { projectController = new ProjectController(m_core); } if( !documentController ) { documentController = new DocumentController(m_core); } if( !languageController ) { // Must be initialized after documentController, because the background parser depends // on the document controller. languageController = new LanguageController(m_core); } if( !runController ) { runController = new RunController(m_core); } if( !sourceFormatterController ) { sourceFormatterController = new SourceFormatterController(m_core); } if ( !progressController) { progressController = ProgressManager::instance(); } if( !selectionController ) { selectionController = new SelectionController(m_core); } if( !documentationController && !(mode & Core::NoUi) ) { documentationController = new DocumentationController(m_core); } if( !debugController ) { debugController = new DebugController(m_core); } if( !testController ) { testController = new TestController(m_core); } qCDebug(SHELL) << "Done creating controllers"; qCDebug(SHELL) << "Initializing controllers"; sessionController.data()->initialize( session ); if( !sessionController.data()->activeSessionLock() ) { return false; } // TODO: Is this early enough, or should we put the loading of the session into // the controller construct DUChain::initialize(); if (!(mode & Core::NoUi)) { uiController.data()->initialize(); } languageController.data()->initialize(); if (partController) { partController.data()->initialize(); } projectController.data()->initialize(); documentController.data()->initialize(); /* This is somewhat messy. We want to load the areas before loading the plugins, so that when each plugin is loaded we know if an area wants some of the tool view from that plugin. OTOH, loading of areas creates documents, and some documents might require that a plugin is already loaded. Probably, the best approach would be to plugins to just add tool views to a list of available tool view, and then grab those tool views when loading an area. */ qCDebug(SHELL) << "Initializing plugin controller (loading session plugins)"; pluginController.data()->initialize(); qCDebug(SHELL) << "Initializing working set controller"; if(!(mode & Core::NoUi)) { workingSetController.data()->initialize(); /* Need to do this after everything else is loaded. It's too hard to restore position of views, and toolbars, and whatever that are not created yet. */ uiController.data()->loadAllAreas(KSharedConfig::openConfig()); uiController.data()->defaultMainWindow()->show(); } qCDebug(SHELL) << "Initializing remaining controllers"; runController.data()->initialize(); sourceFormatterController.data()->initialize(); selectionController.data()->initialize(); if (documentationController) { documentationController.data()->initialize(); } debugController.data()->initialize(); testController.data()->initialize(); installSignalHandler(); qCDebug(SHELL) << "Done initializing controllers"; return true; } CorePrivate::~CorePrivate() { delete selectionController.data(); delete projectController.data(); delete languageController.data(); delete pluginController.data(); delete uiController.data(); delete partController.data(); delete documentController.data(); delete runController.data(); delete sessionController.data(); delete sourceFormatterController.data(); delete documentationController.data(); delete debugController.data(); delete workingSetController.data(); delete testController.data(); selectionController.clear(); projectController.clear(); languageController.clear(); pluginController.clear(); uiController.clear(); partController.clear(); documentController.clear(); runController.clear(); sessionController.clear(); sourceFormatterController.clear(); documentationController.clear(); debugController.clear(); workingSetController.clear(); testController.clear(); } bool Core::initialize(QObject* splash, Setup mode, const QString& session ) { if (splash) { QTimer::singleShot( 200, splash, SLOT(deleteLater()) ); } return initialize(mode, session); } bool Core::initialize(Setup mode, const QString& session) { if (m_self) return true; m_self = new Core(); bool ret = m_self->d->initialize(mode, session); if(ret) emit m_self->initializationCompleted(); return ret; } Core *KDevelop::Core::self() { return m_self; } Core::Core(QObject *parent) : ICore(parent) { d = new CorePrivate(this); connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &Core::shutdown); } Core::Core(CorePrivate* dd, QObject* parent) : ICore(parent), d(dd) { connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &Core::shutdown); } Core::~Core() { qCDebug(SHELL); //Cleanup already called before mass destruction of GUI delete d; m_self = nullptr; } Core::Setup Core::setupFlags() const { return d->m_mode; } void Core::shutdown() { qCDebug(SHELL); if (!d->m_shuttingDown) { cleanup(); deleteLater(); } qCDebug(SHELL) << "Shutdown done"; } bool Core::shuttingDown() const { return d->m_shuttingDown; } void Core::cleanup() { qCDebug(SHELL); d->m_shuttingDown = true; emit aboutToShutdown(); if (!d->m_cleanedUp) { // first of all: stop background jobs d->languageController->backgroundParser()->abortAllJobs(); d->languageController->backgroundParser()->suspend(); d->debugController.data()->cleanup(); d->selectionController.data()->cleanup(); // Save the layout of the ui here, so run it first d->uiController.data()->cleanup(); if (d->workingSetController) d->workingSetController.data()->cleanup(); /* Must be called before projectController.data()->cleanup(). */ // Closes all documents (discards, as already saved if the user wished earlier) d->documentController.data()->cleanup(); d->runController.data()->cleanup(); if (d->partController) { d->partController->cleanup(); } d->projectController.data()->cleanup(); d->sourceFormatterController.data()->cleanup(); d->pluginController.data()->cleanup(); d->sessionController.data()->cleanup(); d->testController.data()->cleanup(); //Disable the functionality of the language controller d->languageController.data()->cleanup(); DUChain::self()->shutdown(); } d->m_cleanedUp = true; emit shutdownCompleted(); } KAboutData Core::aboutData() const { return d->m_aboutData; } IUiController *Core::uiController() { return d->uiController.data(); } ISession* Core::activeSession() { return sessionController()->activeSession(); } ISessionLock::Ptr Core::activeSessionLock() { return sessionController()->activeSessionLock(); } SessionController *Core::sessionController() { return d->sessionController.data(); } UiController *Core::uiControllerInternal() { return d->uiController.data(); } IPluginController *Core::pluginController() { return d->pluginController.data(); } PluginController *Core::pluginControllerInternal() { return d->pluginController.data(); } IProjectController *Core::projectController() { return d->projectController.data(); } ProjectController *Core::projectControllerInternal() { return d->projectController.data(); } IPartController *Core::partController() { return d->partController.data(); } PartController *Core::partControllerInternal() { return d->partController.data(); } ILanguageController *Core::languageController() { return d->languageController.data(); } LanguageController *Core::languageControllerInternal() { return d->languageController.data(); } IDocumentController *Core::documentController() { return d->documentController.data(); } DocumentController *Core::documentControllerInternal() { return d->documentController.data(); } IRunController *Core::runController() { return d->runController.data(); } RunController *Core::runControllerInternal() { return d->runController.data(); } ISourceFormatterController* Core::sourceFormatterController() { return d->sourceFormatterController.data(); } SourceFormatterController* Core::sourceFormatterControllerInternal() { return d->sourceFormatterController.data(); } ProgressManager *Core::progressController() { return d->progressController.data(); } ISelectionController* Core::selectionController() { return d->selectionController.data(); } IDocumentationController* Core::documentationController() { return d->documentationController.data(); } DocumentationController* Core::documentationControllerInternal() { return d->documentationController.data(); } IDebugController* Core::debugController() { return d->debugController.data(); } DebugController* Core::debugControllerInternal() { return d->debugController.data(); } ITestController* Core::testController() { return d->testController.data(); } TestController* Core::testControllerInternal() { return d->testController.data(); } WorkingSetController* Core::workingSetControllerInternal() { return d->workingSetController.data(); } QString Core::version() { return QStringLiteral(KDEVPLATFORM_VERSION_STRING); } } diff --git a/shell/documentcontroller.h b/shell/documentcontroller.h index 04d79039e..cb6f74a1b 100644 --- a/shell/documentcontroller.h +++ b/shell/documentcontroller.h @@ -1,180 +1,180 @@ /* This document is part of the KDE project Copyright 2002 Matthias Hoelzer-Kluepfel Copyright 2002 Bernd Gehrmann Copyright 2003 Roberto Raggi Copyright 2003 Hamish Rodda Copyright 2003 Harald Fernengel Copyright 2003 Jens Dagerbo Copyright 2005 Adam Treat Copyright 2004-2007 Alexander Dymo Copyright 2007 Andreas Pakulat 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 document COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_DOCUMENTCONTROLLER_H #define KDEVPLATFORM_DOCUMENTCONTROLLER_H -#include +#include #include #include "shellexport.h" namespace KTextEditor { class View; } namespace Sublime { class Document; class Area; class AreaIndex; } namespace KDevelop { class MainWindow; /** * \short Interface to control open documents. * The document controller manages open documents in the IDE. * Open documents are usually editors, GUI designers, html documentation etc. * * Please note that this interface gives access to documents and not to their views. * It is possible that more than 1 view is shown in KDevelop for a document. */ class KDEVPLATFORMSHELL_EXPORT DocumentController: public IDocumentController { Q_OBJECT Q_CLASSINFO( "D-Bus Interface", "org.kdevelop.DocumentController" ) public: /**Constructor. @param parent The parent object.*/ explicit DocumentController( QObject *parent = nullptr ); ~DocumentController() override; /**Finds the first document object corresponding to a given url. @param url The Url of the document. @return The corresponding document, or null if not found.*/ IDocument* documentForUrl( const QUrl & url ) const override; /**@return The list of open documents*/ QList openDocuments() const override; /**Refers to the document currently active or focused. @return The active document.*/ IDocument* activeDocument() const override; KTextEditor::View* activeTextDocumentView() const override; /// Activate the given \a document. This convenience function does not add the document /// to the File/Recent Open menu. Use DocumentController::openDocument if that is desired. void activateDocument( IDocument * document, const KTextEditor::Range& range = KTextEditor::Range::invalid() ) override; void registerDocumentForMimetype( const QString&, KDevelop::IDocumentFactory* ) override; /// Request the document controller to save all documents. /// If the \a mode is not IDocument::Silent, ask the user which documents to save. /// Returns false if the user cancels the save dialog. bool saveAllDocuments(IDocument::DocumentSaveMode mode) override; bool saveAllDocumentsForWindow(KParts::MainWindow* mw, IDocument::DocumentSaveMode mode, bool currentAreaOnly = false) override; void initialize(); void cleanup(); virtual QStringList documentTypes() const; QString documentType(Sublime::Document* document) const; using IDocumentController::openDocument; /**checks that url is an url of empty document*/ static bool isEmptyDocumentUrl(const QUrl &url); static QUrl nextEmptyDocumentUrl(); IDocumentFactory* factory(const QString& mime) const override; bool openDocument(IDocument* doc, const KTextEditor::Range& range = KTextEditor::Range::invalid(), DocumentActivationParams activationParams = nullptr, IDocument* buddy = nullptr) override; KTextEditor::Document* globalTextEditorInstance() override; public Q_SLOTS: /**Opens a new or existing document. @param url The full Url of the document to open. If it is empty, a dialog to choose the document will be opened. @param range The location information, if applicable. @param activationParams Indicates whether to fully activate the document. @param buddy The buddy document @return The opened document */ Q_SCRIPTABLE IDocument* openDocument( const QUrl &url, const KTextEditor::Range& range = KTextEditor::Range::invalid(), DocumentActivationParams activationParams = nullptr, const QString& encoding = {}, IDocument* buddy = nullptr ) override; Q_SCRIPTABLE IDocument* openDocumentFromText( const QString& data ) override; KDevelop::IDocument* openDocument( const QUrl &url, const QString& prefName ) override; virtual bool closeDocument( const QUrl &url ); void fileClose(); void slotSaveAllDocuments(); bool closeAllDocuments() override; void closeAllOtherDocuments(); void reloadAllDocuments(); // DBUS-compatible versions of openDocument virtual Q_SCRIPTABLE bool openDocumentSimple( QString url, int line = -1, int column = 0 ); // Opens a list of documents, with optional split-view separators, like: "file1 / [ file2 - fil3 ]" (see kdevplatform_shell_environment.sh) virtual Q_SCRIPTABLE bool openDocumentsSimple( QStringList urls ); virtual Q_SCRIPTABLE bool openDocumentFromTextSimple( QString text ); // If 'target' is empty, returns the currently active document, or // the currently selected project-item if no document is active. // If 'target' is "[selection]", returns the path of the currently active selection. // If 'target' is the name of a project, returns the root-path of that project. // Whenever the returned path corresponds to a directory, a '/.' suffix is appended. Q_SCRIPTABLE QString activeDocumentPath(QString target = {}) const; // Returns all open documents in the current area Q_SCRIPTABLE QStringList activeDocumentPaths() const; void vcsAnnotateCurrentDocument(); private Q_SLOTS: virtual void slotOpenDocument(const QUrl &url); void notifyDocumentClosed(Sublime::Document* doc); private: bool openDocumentsWithSplitSeparators( Sublime::AreaIndex* index, QStringList urlsWithSeparators, bool& isFirstView ); QList visibleDocumentsInWindow(MainWindow* mw) const; QList documentsExclusivelyInWindow(MainWindow* mw, bool currentAreaOnly = false) const; QList modifiedDocuments(const QList& list) const; bool saveSomeDocuments(const QList& list, IDocument::DocumentSaveMode mode) override; void setupActions(); friend struct DocumentControllerPrivate; struct DocumentControllerPrivate *d; }; } #endif diff --git a/shell/problemmodel.h b/shell/problemmodel.h index 18a70870f..273c5a71a 100644 --- a/shell/problemmodel.h +++ b/shell/problemmodel.h @@ -1,210 +1,210 @@ /* * KDevelop Problem Reporter * * Copyright 2007 Hamish Rodda * Copyright 2015 Laszlo Kis-Adam * * 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. */ #ifndef PROBLEMMODEL_H #define PROBLEMMODEL_H #include -#include +#include #include struct ProblemModelPrivate; namespace KDevelop { class IDocument; class IndexedString; class ProblemStore; /** * @brief Wraps a ProblemStore and adds the QAbstractItemModel interface, so the it can be used in a model/view architecture. * * By default ProblemModel instantiates a FilteredProblemStore, with the following features on: * \li ScopeFilter * \li SeverityFilter * \li Grouping * \li CanByPassScopeFilter * * Has to following columns: * \li Error * \li Source * \li File * \li Line * \li Column * \li LastColumn * * Possible ProblemModel features * \li NoFeatures * \li CanDoFullUpdate * \li CanShowImports * \li ScopeFilter * \li SeverityFilter * \li Grouping * \li CanByPassScopeFilter * * Scope, severity, grouping, imports can be set using the slots named after these features. * * Usage example: * @code * IProblem::Ptr problem(new DetectedProblem); * problem->setDescription(QStringLiteral("Problem")); * ProblemModel *model = new ProblemModel(nullptr); * model->addProblem(problem); * model->rowCount(); // returns 1 * QModelIndex idx = model->index(0, 0); * model->data(index); // "Problem" * @endcode * */ class KDEVPLATFORMSHELL_EXPORT ProblemModel : public QAbstractItemModel { Q_OBJECT public: /// List of supportable features enum FeatureCode { NoFeatures = 0, /// No features :( CanDoFullUpdate = 1, /// Reload/Reparse problems CanShowImports = 2, /// Show problems from imported files. E.g.: Header files in C/C++ ScopeFilter = 4, /// Filter problems by scope. E.g.: current document, open documents, etc SeverityFilter = 8, /// Filter problems by severity. E.g.: hint, warning, error, etc Grouping = 16, /// Can group problems CanByPassScopeFilter = 32, /// Can bypass scope filter ShowSource = 64 /// Show problem's source. Set if problems can have different sources. }; Q_DECLARE_FLAGS(Features, FeatureCode) explicit ProblemModel(QObject *parent, ProblemStore *store = nullptr); ~ProblemModel() override; enum Columns { Error, Source, File, Line, Column, LastColumn }; enum Roles { ProblemRole = Qt::UserRole + 1, SeverityRole }; int columnCount(const QModelIndex & parent = QModelIndex()) const override; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex & index) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex & parent = QModelIndex()) const override; QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override; IProblem::Ptr problemForIndex(const QModelIndex& index) const; /// Adds a new problem to the model void addProblem(const IProblem::Ptr &problem); /// Clears the problems, then adds a new set of them void setProblems(const QVector &problems); /// Clears the problems void clearProblems(); /// Retrieve problems for selected document QVector problems(const KDevelop::IndexedString& document); /// Retrieve the supported features Features features() const; /// Retrieve 'show imports' filter setting bool showImports(); /// Set the supported features void setFeatures(Features features); /// Tooltip for "Force Full Update" action in the Problems View when the model /// is active (correspondent tab is selected) QString fullUpdateTooltip() const; /// Set the "Force Full Update" action tooltip void setFullUpdateTooltip(const QString& tooltip); signals: /// Emitted when the stored problems are changed with addProblem(), setProblems() and /// clearProblems() methods. This signal emitted only when internal problems storage is /// really changed: for example, it is not emitted when we call clearProblems() method /// for empty model. void problemsChanged(); /// Emitted when the "Force Full Update" action tooltip is changed with setFullUpdateTooltip(). /// This signal emitted only when tooltip is really changed. void fullUpdateTooltipChanged(); public slots: /// Show imports void setShowImports(bool showImports); /// Sets the scope filter. Uses int to be able to use QSignalMapper void setScope(int scope); /// Sets the severity filter. Uses int to be able to use QSignalMapper void setSeverity(int severity);///old-style severity filtering void setSeverities(KDevelop::IProblem::Severities severities);///new-style severity filtering void setGrouping(int grouping); /** * Force a full problem update. * E.g.: Reparse the source code. * Obviously it doesn't make sense for run-time problem checkers. */ virtual void forceFullUpdate(){} protected slots: /// Triggered when problems change virtual void onProblemsChanged(){} private slots: /// Triggered when the current document changes virtual void setCurrentDocument(IDocument* doc); virtual void closedDocument(IDocument* doc); /// Triggered before the problems are rebuilt void onBeginRebuild(); /// Triggered once the problems have been rebuilt void onEndRebuild(); protected: ProblemStore *store() const; private: QScopedPointer d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(ProblemModel::Features) } #endif // PROBLEMMODEL_H diff --git a/shell/session.cpp b/shell/session.cpp index 5729abcf1..ba63df054 100644 --- a/shell/session.cpp +++ b/shell/session.cpp @@ -1,231 +1,231 @@ /* This file is part of KDevelop Copyright 2008 Andreas Pakulat 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 "session.h" -#include +#include #include #include #include #include #include "core.h" #include "sessioncontroller.h" #include #include #include namespace KDevelop { const QString Session::cfgSessionNameEntry = QStringLiteral("SessionName"); const QString Session::cfgSessionDescriptionEntry = QStringLiteral("SessionPrettyContents"); const QString Session::cfgSessionProjectsEntry = QStringLiteral("Open Projects"); const QString Session::cfgSessionOptionsGroup = QStringLiteral("General Options"); class SessionPrivate { public: SessionInfo info; Session* q; bool isTemporary; QUrl pluginArea( const IPlugin* plugin ) { QString name = Core::self()->pluginController()->pluginInfo(plugin).pluginId(); QUrl url = QUrl::fromLocalFile( info.path + '/' + name ); if( !QFile::exists( url.toLocalFile() ) ) { QDir( info.path ).mkdir( name ); } return url; } SessionPrivate( Session* session, const QString& id ) : info( Session::parse( id, true ) ) , q( session ) , isTemporary( false ) { } void updateDescription() { buildDescription( info ); emit q->sessionUpdated( q ); } static QString generatePrettyContents( const SessionInfo& info ); static QString generateDescription( const SessionInfo& info ); static void buildDescription( SessionInfo& info ); }; Session::Session( const QString& id, QObject* parent ) : ISession(parent) , d( new SessionPrivate( this, id ) ) { } Session::~Session() { delete d; } QString Session::name() const { return d->info.name; } QList Session::containedProjects() const { return d->info.projects; } QString Session::description() const { return d->info.description; } QUrl Session::pluginDataArea( const IPlugin* p ) { return d->pluginArea( p ); } KSharedConfigPtr Session::config() { return d->info.config; } QUuid Session::id() const { return d->info.uuid; } void Session::setName( const QString& newname ) { d->info.name = newname; d->info.config->group( QString() ).writeEntry( cfgSessionNameEntry, newname ); d->updateDescription(); } void Session::setContainedProjects( const QList& projects ) { d->info.projects = projects; d->info.config->group( cfgSessionOptionsGroup ).writeEntry( cfgSessionProjectsEntry, projects ); d->updateDescription(); } void Session::setTemporary(bool temp) { d->isTemporary = temp; } bool Session::isTemporary() const { return d->isTemporary; } QString Session::path() const { return d->info.path; } QString SessionPrivate::generatePrettyContents( const SessionInfo& info ) { if( info.projects.isEmpty() ) return QString(); QStringList projectNames; projectNames.reserve( info.projects.size() ); foreach( const QUrl& url, info.projects ) { IProject* project = nullptr; if( ICore::self() && ICore::self()->projectController() ) { project = ICore::self()->projectController()->findProjectForUrl( url ); } if( project ) { projectNames << project->name(); } else { QString projectName = url.fileName(); projectName.remove( QRegExp( "\\.kdev4$", Qt::CaseInsensitive ) ); projectNames << projectName; } } if( projectNames.isEmpty() ) { return i18n("(no projects)"); } else { return projectNames.join( QStringLiteral(", ") ); } } QString SessionPrivate::generateDescription( const SessionInfo& info ) { QString prettyContentsFormatted = generatePrettyContents( info ); QString description; if( info.name.isEmpty() ) { description = prettyContentsFormatted; } else { description = info.name + ": " + prettyContentsFormatted; } return description; } void SessionPrivate::buildDescription( SessionInfo& info ) { QString description = generateDescription( info ); info.description = description; info.config->group( QString() ).writeEntry( Session::cfgSessionDescriptionEntry, description ); info.config->sync(); } SessionInfo Session::parse( const QString& id, bool mkdir ) { SessionInfo ret; QString sessionPath = SessionController::sessionDirectory(id); QDir sessionDir( sessionPath ); if( !sessionDir.exists() ) { if( mkdir ) { sessionDir.mkpath(sessionPath); Q_ASSERT( sessionDir.exists() ); } else { return ret; } } ret.uuid = id; ret.path = sessionPath; ret.config = KSharedConfig::openConfig( sessionPath + "/sessionrc" ); KConfigGroup cfgRootGroup = ret.config->group( QString() ); KConfigGroup cfgOptionsGroup = ret.config->group( cfgSessionOptionsGroup ); ret.name = cfgRootGroup.readEntry( cfgSessionNameEntry, QString() ); ret.projects = cfgOptionsGroup.readEntry( cfgSessionProjectsEntry, QList() ); SessionPrivate::buildDescription( ret ); return ret; } } diff --git a/shell/session.h b/shell/session.h index a7d3db38a..46cb49877 100644 --- a/shell/session.h +++ b/shell/session.h @@ -1,88 +1,88 @@ /* This file is part of KDevelop Copyright 2008 Andreas Pakulat 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. */ #ifndef KDEVPLATFORM_SESSION_H #define KDEVPLATFORM_SESSION_H #include "shellexport.h" -#include +#include #include #include namespace KDevelop { struct SessionInfo { QString name; QUuid uuid; QString description; QList projects; QString path; KSharedConfigPtr config; }; using SessionInfos = QVector; class KDEVPLATFORMSHELL_EXPORT Session : public ISession { Q_OBJECT public: static const QString cfgSessionNameEntry; static const QString cfgSessionDescriptionEntry; static const QString cfgSessionProjectsEntry; static const QString cfgSessionOptionsGroup; explicit Session( const QString& id, QObject * parent = nullptr ); ~Session() override; QUrl pluginDataArea( const IPlugin* ) override; KSharedConfigPtr config() override; QList containedProjects() const override; void setContainedProjects( const QList& projects ) override; QString name() const override; void setName( const QString& ); QUuid id() const override; QString description() const override; bool isTemporary() const override; void setTemporary(bool temp) override; QString path() const; /** * Generates a @ref SessionInfo by a session @p id. * @param mkdir Whether to create a session directory if one does not exist. */ static SessionInfo parse( const QString& id, bool mkdir = false ); private: class SessionPrivate* const d; friend class SessionPrivate; }; } Q_DECLARE_METATYPE( KDevelop::Session* ) #endif diff --git a/shell/sessioncontroller.cpp b/shell/sessioncontroller.cpp index 1ae18556f..35e05beae 100644 --- a/shell/sessioncontroller.cpp +++ b/shell/sessioncontroller.cpp @@ -1,659 +1,659 @@ /* This file is part of KDevelop Copyright 2008 Andreas Pakulat Copyright 2010 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 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 "sessioncontroller.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 "session.h" #include "core.h" #include "uicontroller.h" #include "shellextension.h" #include "sessionlock.h" #include "sessionchooserdialog.h" #include "debug.h" #include #include #include namespace KDevelop { namespace { int argc = 0; char** argv = nullptr; }; void SessionController::setArguments(int _argc, char** _argv) { argc = _argc; argv = _argv; } static QStringList standardArguments() { QStringList ret; for(int a = 0; a < argc; ++a) { QString arg = QString::fromLocal8Bit(argv[a]); if(arg.startsWith(QLatin1String("-graphicssystem")) || arg.startsWith(QLatin1String("-style"))) { ret << '-' + arg; if(a+1 < argc) ret << QString::fromLocal8Bit(argv[a+1]); } } return ret; } class SessionControllerPrivate : public QObject { Q_OBJECT public: explicit SessionControllerPrivate( SessionController* s ) : q(s) , activeSession(nullptr) , grp(nullptr) { } ~SessionControllerPrivate() override { } Session* findSessionForName( const QString& name ) const { foreach( Session* s, sessionActions.keys() ) { if( s->name() == name ) return s; } return nullptr; } Session* findSessionForId(QString idString) { QUuid id(idString); foreach( Session* s, sessionActions.keys() ) { if( s->id() == id) return s; } return nullptr; } void newSession() { qsrand(QDateTime::currentDateTimeUtc().toTime_t()); Session* session = new Session( QUuid::createUuid().toString() ); KProcess::startDetached(ShellExtension::getInstance()->executableFilePath(), QStringList() << QStringLiteral("-s") << session->id().toString() << standardArguments()); delete session; #if 0 //Terminate this instance of kdevelop if the user agrees foreach(Sublime::MainWindow* window, Core::self()->uiController()->controller()->mainWindows()) window->close(); #endif } void deleteCurrentSession() { int choice = KMessageBox::warningContinueCancel(Core::self()->uiController()->activeMainWindow(), i18n("The current session and all contained settings will be deleted. The projects will stay unaffected. Do you really want to continue?")); if(choice == KMessageBox::Continue) { q->deleteSessionFromDisk(sessionLock); q->emitQuitSession(); } } void renameSession() { bool ok; auto newSessionName = QInputDialog::getText(Core::self()->uiController()->activeMainWindow(), i18n("Rename Session"), i18n("New Session Name:"), QLineEdit::Normal, q->activeSession()->name(), &ok); if (ok) { static_cast(q->activeSession())->setName(newSessionName); } q->updateXmlGuiActionList(); // resort } bool loadSessionExternally( Session* s ) { Q_ASSERT( s ); KProcess::startDetached(ShellExtension::getInstance()->executableFilePath(), QStringList() << QStringLiteral("-s") << s->id().toString() << standardArguments()); return true; } TryLockSessionResult activateSession( Session* s ) { Q_ASSERT( s ); activeSession = s; TryLockSessionResult result = SessionController::tryLockSession( s->id().toString()); if( !result.lock ) { activeSession = nullptr; return result; } Q_ASSERT(s->id().toString() == result.lock->id()); sessionLock = result.lock; KConfigGroup grp = KSharedConfig::openConfig()->group( SessionController::cfgSessionGroup() ); grp.writeEntry( SessionController::cfgActiveSessionEntry(), s->id().toString() ); grp.sync(); if (Core::self()->setupFlags() & Core::NoUi) return result; QHash::iterator it = sessionActions.find(s); Q_ASSERT( it != sessionActions.end() ); (*it)->setCheckable(true); (*it)->setChecked(true); for(it = sessionActions.begin(); it != sessionActions.end(); ++it) { if(it.key() != s) (*it)->setCheckable(false); } return result; } void loadSessionFromAction(QAction* action) { auto session = action->data().value(); loadSessionExternally(session); } void addSession( Session* s ) { if (Core::self()->setupFlags() & Core::NoUi) { sessionActions[s] = nullptr; return; } QAction* a = new QAction( grp ); a->setText( s->description() ); a->setCheckable( false ); a->setData(QVariant::fromValue(s)); sessionActions[s] = a; q->actionCollection()->addAction( "session_"+s->id().toString(), a ); connect( s, &Session::sessionUpdated, this, &SessionControllerPrivate::sessionUpdated ); sessionUpdated( s ); } SessionController* q; QHash sessionActions; ISession* activeSession; QActionGroup* grp; ISessionLock::Ptr sessionLock; static QString sessionBaseDirectory() { return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +'/'+ qApp->applicationName() + "/sessions/"; } QString ownSessionDirectory() const { Q_ASSERT(activeSession); return q->sessionDirectory( activeSession->id().toString() ); } private slots: void sessionUpdated( KDevelop::ISession* s ) { sessionActions[static_cast( s )]->setText( KStringHandler::rsqueeze(s->description()) ); } }; SessionController::SessionController( QObject *parent ) : QObject( parent ), d(new SessionControllerPrivate(this)) { setObjectName(QStringLiteral("SessionController")); setComponentName(QStringLiteral("kdevsession"), i18n("Session Manager")); setXMLFile(QStringLiteral("kdevsessionui.rc")); QDBusConnection::sessionBus().registerObject( QStringLiteral("/org/kdevelop/SessionController"), this, QDBusConnection::ExportScriptableSlots ); if (Core::self()->setupFlags() & Core::NoUi) return; QAction* action = actionCollection()->addAction( QStringLiteral("new_session"), this, SLOT(newSession()) ); action->setText( i18nc("@action:inmenu", "Start New Session") ); action->setToolTip( i18nc("@info:tooltip", "Start a new KDevelop instance with an empty session") ); action->setIcon(QIcon::fromTheme(QStringLiteral("window-new"))); action = actionCollection()->addAction( QStringLiteral("rename_session"), this, SLOT(renameSession()) ); action->setText( i18n("Rename Current Session...") ); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); action = actionCollection()->addAction( QStringLiteral("delete_session"), this, SLOT(deleteCurrentSession()) ); action->setText( i18n("Delete Current Session...") ); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); action = actionCollection()->addAction( QStringLiteral("quit"), this, SIGNAL(quitSession()) ); action->setText( i18n("Quit") ); action->setMenuRole( QAction::NoRole ); // OSX: prevent QT from hiding this due to conflict with 'Quit KDevelop...' actionCollection()->setDefaultShortcut( action, Qt::CTRL | Qt::Key_Q ); action->setIcon(QIcon::fromTheme(QStringLiteral("application-exit"))); d->grp = new QActionGroup( this ); connect( d->grp, &QActionGroup::triggered, this, [&] (QAction* a) { d->loadSessionFromAction(a); } ); } SessionController::~SessionController() { delete d; } void SessionController::startNewSession() { d->newSession(); } void SessionController::cleanup() { if (d->activeSession) { Q_ASSERT(d->activeSession->id().toString() == d->sessionLock->id()); if (d->activeSession->isTemporary()) { deleteSessionFromDisk(d->sessionLock); } d->activeSession = nullptr; } d->sessionLock.clear(); qDeleteAll(d->sessionActions); d->sessionActions.clear(); } void SessionController::initialize( const QString& session ) { QDir sessiondir( SessionControllerPrivate::sessionBaseDirectory() ); foreach( const QString& s, sessiondir.entryList( QDir::AllDirs | QDir::NoDotAndDotDot ) ) { QUuid id( s ); if( id.isNull() ) continue; // Only create sessions for directories that represent proper uuid's Session* ses = new Session( id.toString(), this ); //Delete sessions that have no name and are empty if( ses->containedProjects().isEmpty() && ses->name().isEmpty() && (session.isEmpty() || (ses->id().toString() != session && ses->name() != session)) ) { TryLockSessionResult result = tryLockSession(s); if (result.lock) { deleteSessionFromDisk(result.lock); } delete ses; } else { d->addSession( ses ); } } loadDefaultSession( session ); updateXmlGuiActionList(); } ISession* SessionController::activeSession() const { return d->activeSession; } ISessionLock::Ptr SessionController::activeSessionLock() const { return d->sessionLock; } void SessionController::loadSession( const QString& nameOrId ) { d->loadSessionExternally( session( nameOrId ) ); } QList SessionController::sessionNames() const { QStringList l; foreach( const Session* s, d->sessionActions.keys() ) { l << s->name(); } return l; } QList< const KDevelop::Session* > SessionController::sessions() const { QList< const KDevelop::Session* > ret; foreach( const Session* s, d->sessionActions.keys() ) { ret << s; } return ret; } Session* SessionController::createSession( const QString& name ) { Session* s; if(name.startsWith('{')) { s = new Session( QUuid(name).toString(), this ); }else{ qsrand(QDateTime::currentDateTimeUtc().toTime_t()); s = new Session( QUuid::createUuid().toString(), this ); s->setName( name ); } d->addSession( s ); updateXmlGuiActionList(); return s; } void SessionController::deleteSession( const ISessionLock::Ptr& lock ) { Session* s = session(lock->id()); QHash::iterator it = d->sessionActions.find(s); Q_ASSERT( it != d->sessionActions.end() ); unplugActionList( QStringLiteral("available_sessions") ); actionCollection()->removeAction(*it); if (d->grp) { // happens in unit tests d->grp->removeAction(*it); plugActionList( QStringLiteral("available_sessions"), d->grp->actions() ); } if (s == d->activeSession) { d->activeSession = nullptr; } deleteSessionFromDisk(lock); emit sessionDeleted( s->id().toString() ); d->sessionActions.remove(s); delete s; } void SessionController::deleteSessionFromDisk( const ISessionLock::Ptr& lock ) { qCDebug(SHELL) << "Deleting session:" << lock->id(); static_cast(lock.data())->removeFromDisk(); ItemRepositoryRegistry::deleteRepositoryFromDisk( lock ); } void SessionController::loadDefaultSession( const QString& session ) { QString load = session; if (load.isEmpty()) { KConfigGroup grp = KSharedConfig::openConfig()->group( cfgSessionGroup() ); load = grp.readEntry( cfgActiveSessionEntry(), "default" ); } // Iteratively try to load the session, asking user what to do in case of failure // If showForceOpenDialog() returns empty string, stop trying Session* s = nullptr; do { s = this->session( load ); if( !s ) { s = createSession( load ); } TryLockSessionResult result = d->activateSession( s ); if( result.lock ) { Q_ASSERT(d->activeSession == s); Q_ASSERT(d->sessionLock = result.lock); break; } load = handleLockedSession( s->name(), s->id().toString(), result.runInfo ); } while( !load.isEmpty() ); } Session* SessionController::session( const QString& nameOrId ) const { Session* ret = d->findSessionForName( nameOrId ); if(ret) return ret; return d->findSessionForId( nameOrId ); } QString SessionController::cloneSession( const QString& nameOrid ) { Session* origSession = session( nameOrid ); qsrand(QDateTime::currentDateTimeUtc().toTime_t()); QUuid id = QUuid::createUuid(); auto copyJob = KIO::copy(QUrl::fromLocalFile(sessionDirectory(origSession->id().toString())), QUrl::fromLocalFile(sessionDirectory( id.toString()))); KJobWidgets::setWindow(copyJob, Core::self()->uiController()->activeMainWindow()); copyJob->exec(); Session* newSession = new Session( id.toString() ); newSession->setName( i18n( "Copy of %1", origSession->name() ) ); d->addSession(newSession); updateXmlGuiActionList(); return newSession->name(); } void SessionController::updateXmlGuiActionList() { unplugActionList( QStringLiteral("available_sessions") ); if (d->grp) { auto actions = d->grp->actions(); std::sort(actions.begin(), actions.end(), [](const QAction* lhs, const QAction* rhs) { auto s1 = lhs->data().value(); auto s2 = rhs->data().value(); return QString::localeAwareCompare(s1->description(), s2->description()) < 0; }); plugActionList(QStringLiteral("available_sessions"), actions); } } QString SessionController::cfgSessionGroup() { return QStringLiteral("Sessions"); } QString SessionController::cfgActiveSessionEntry() { return QStringLiteral("Active Session ID"); } QList SessionController::availableSessionInfo() { return availableSessionInfos().toList(); } SessionInfos SessionController::availableSessionInfos() { SessionInfos sessionInfos; foreach( const QString& sessionId, QDir( SessionControllerPrivate::sessionBaseDirectory() ).entryList( QDir::AllDirs ) ) { if( !QUuid( sessionId ).isNull() ) { sessionInfos << Session::parse( sessionId ); } } return sessionInfos; } QString SessionController::sessionDirectory(const QString& sessionId) { return SessionControllerPrivate::sessionBaseDirectory() + sessionId; } TryLockSessionResult SessionController::tryLockSession(const QString& id, bool doLocking) { return SessionLock::tryLockSession(id, doLocking); } bool SessionController::isSessionRunning(const QString& id) { return sessionRunInfo(id).isRunning; } SessionRunInfo SessionController::sessionRunInfo(const QString& id) { return SessionLock::tryLockSession(id, false).runInfo; } QString SessionController::showSessionChooserDialog(QString headerText, bool onlyRunning) { ///FIXME: move this code into sessiondialog.cpp QListView* view = new QListView; QLineEdit* filter = new QLineEdit; filter->setClearButtonEnabled( true ); filter->setPlaceholderText(i18n("Search")); QStandardItemModel* model = new QStandardItemModel(view); QSortFilterProxyModel *proxy = new QSortFilterProxyModel(model); proxy->setSourceModel(model); proxy->setFilterKeyColumn( 1 ); proxy->setFilterCaseSensitivity( Qt::CaseInsensitive ); connect(filter, &QLineEdit::textChanged, proxy, &QSortFilterProxyModel::setFilterFixedString); SessionChooserDialog dialog(view, proxy, filter); view->setEditTriggers(QAbstractItemView::NoEditTriggers); QVBoxLayout layout(dialog.mainWidget()); if(!headerText.isEmpty()) { QLabel* heading = new QLabel(headerText); QFont font = heading->font(); font.setBold(true); heading->setFont(font); layout.addWidget(heading); } model->setColumnCount(3); model->setHeaderData(0, Qt::Horizontal,i18n("Identity")); model->setHeaderData(1, Qt::Horizontal, i18n("Contents")); model->setHeaderData(2, Qt::Horizontal,i18n("State")); view->setModel(proxy); view->setModelColumn(1); QHBoxLayout* filterLayout = new QHBoxLayout(); filterLayout->addWidget(new QLabel(i18n("Filter:"))); filterLayout->addWidget(filter); layout.addLayout(filterLayout); layout.addWidget(view); filter->setFocus(); int row = 0; QString defaultSession = KSharedConfig::openConfig()->group( cfgSessionGroup() ).readEntry( cfgActiveSessionEntry(), "default" ); foreach(const KDevelop::SessionInfo& si, KDevelop::SessionController::availableSessionInfos()) { if ( si.name.isEmpty() && si.projects.isEmpty() ) { continue; } bool running = KDevelop::SessionController::isSessionRunning(si.uuid.toString()); if(onlyRunning && !running) continue; model->setItem(row, 0, new QStandardItem(si.uuid.toString())); model->setItem(row, 1, new QStandardItem(si.description)); model->setItem(row, 2, new QStandardItem); ++row; } model->sort(1); if(!onlyRunning) { model->setItem(row, 0, new QStandardItem); model->setItem(row, 1, new QStandardItem(QIcon::fromTheme(QStringLiteral("window-new")), i18n("Create New Session"))); } dialog.updateState(); dialog.mainWidget()->layout()->setContentsMargins(0,0,0,0); const QModelIndex defaultSessionIndex = model->match(model->index(0, 0), Qt::DisplayRole, defaultSession, 1, Qt::MatchExactly).value(0); view->selectionModel()->setCurrentIndex(proxy->mapFromSource(defaultSessionIndex), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); view->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); ///@todo We need a way to get a proper size-hint from the view, but unfortunately, that only seems possible after the view was shown. dialog.resize(QSize(900, 600)); if(dialog.exec() != QDialog::Accepted) { return QString(); } QModelIndex selected = view->selectionModel()->currentIndex(); if (!selected.isValid()) return QString(); const QString selectedSessionId = selected.sibling(selected.row(), 0).data().toString(); if (selectedSessionId.isEmpty()) { // "Create New Session" item selected, return a fresh UUID qsrand(QDateTime::currentDateTimeUtc().toTime_t()); return QUuid::createUuid().toString(); } return selectedSessionId; } QString SessionController::handleLockedSession( const QString& sessionName, const QString& sessionId, const SessionRunInfo& runInfo ) { return SessionLock::handleLockedSession(sessionName, sessionId, runInfo); } QString SessionController::sessionDir() { if( !activeSession() ) return QString(); return d->ownSessionDirectory(); } QString SessionController::sessionName() { if(!activeSession()) return QString(); return activeSession()->description(); } } #include "sessioncontroller.moc" #include "moc_sessioncontroller.cpp" diff --git a/shell/settings/editstyledialog.cpp b/shell/settings/editstyledialog.cpp index 5f96a9cdc..90603388b 100644 --- a/shell/settings/editstyledialog.cpp +++ b/shell/settings/editstyledialog.cpp @@ -1,124 +1,124 @@ /* This file is part of KDevelop * Copyright (C) 2008 Cédric Pasteur 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "editstyledialog.h" #include #include #include #include #include #include #include #include -#include +#include #include using namespace KDevelop; EditStyleDialog::EditStyleDialog(ISourceFormatter* formatter, const QMimeType& mime, const SourceFormatterStyle& style, QWidget* parent) : QDialog(parent) , m_sourceFormatter(formatter) , m_mimeType(mime) , m_style(style) { m_content = new QWidget(); m_ui.setupUi(m_content); QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->addWidget(m_content); auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); auto okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); connect(buttonBox, &QDialogButtonBox::accepted, this, &EditStyleDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &EditStyleDialog::reject); mainLayout->addWidget(buttonBox); m_settingsWidget = m_sourceFormatter->editStyleWidget(mime); init(); if (m_settingsWidget) { m_settingsWidget->load(style); } } EditStyleDialog::~EditStyleDialog() { } void EditStyleDialog::init() { // add plugin settings widget if (m_settingsWidget) { QVBoxLayout* layout = new QVBoxLayout(m_ui.settingsWidgetParent); layout->addWidget(m_settingsWidget); m_ui.settingsWidgetParent->setLayout(layout); connect(m_settingsWidget, &SettingsWidget::previewTextChanged, this, &EditStyleDialog::updatePreviewText); } m_document = KTextEditor::Editor::instance()->createDocument(this); m_document->setReadWrite(false); m_document->setHighlightingMode(m_style.modeForMimetype(m_mimeType)); m_view = m_document->createView(m_ui.textEditor); QVBoxLayout* layout2 = new QVBoxLayout(m_ui.textEditor); layout2->addWidget(m_view); m_ui.textEditor->setLayout(layout2); m_view->setStatusBarEnabled(false); m_view->show(); KTextEditor::ConfigInterface* iface = qobject_cast(m_view); if (iface) { iface->setConfigValue(QStringLiteral("dynamic-word-wrap"), false); iface->setConfigValue(QStringLiteral("icon-bar"), false); } if (m_sourceFormatter) { QString text = m_sourceFormatter->previewText(m_style, m_mimeType); updatePreviewText(text); } } void EditStyleDialog::updatePreviewText(const QString &text) { m_document->setReadWrite(true); m_style.setContent(content()); if (m_sourceFormatter) { m_document->setText(m_sourceFormatter->formatSourceWithStyle(m_style, text, QUrl(), m_mimeType)); } else { m_document->setText(i18n("No Source Formatter available")); } m_view->setCursorPosition(KTextEditor::Cursor(0, 0)); m_document->setReadWrite(false); } QString EditStyleDialog::content() { if (m_settingsWidget) { return m_settingsWidget->save(); } return QString(); } diff --git a/shell/settings/sourceformattersettings.cpp b/shell/settings/sourceformattersettings.cpp index 0c56819ab..4057fb5dd 100644 --- a/shell/settings/sourceformattersettings.cpp +++ b/shell/settings/sourceformattersettings.cpp @@ -1,538 +1,538 @@ /* This file is part of KDevelop * Copyright (C) 2008 Cédric Pasteur 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sourceformattersettings.h" #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include "editstyledialog.h" #include "../debug.h" #define STYLE_ROLE (Qt::UserRole+1) using KDevelop::Core; using KDevelop::ISourceFormatter; using KDevelop::SourceFormatterStyle; using KDevelop::SourceFormatterController; using KDevelop::SourceFormatter; namespace { namespace Strings { QString userStylePrefix() { return QStringLiteral("User"); } } } LanguageSettings::LanguageSettings() : selectedFormatter(nullptr), selectedStyle(nullptr) { } SourceFormatterSettings::SourceFormatterSettings(QWidget* parent) : KDevelop::ConfigPage(nullptr, nullptr, parent) { setupUi(this); connect( cbLanguages, static_cast(&KComboBox::currentIndexChanged), this, &SourceFormatterSettings::selectLanguage ); connect( cbFormatters, static_cast(&KComboBox::currentIndexChanged), this, &SourceFormatterSettings::selectFormatter ); connect( chkKateModelines, &QCheckBox::toggled, this, &SourceFormatterSettings::somethingChanged ); connect( chkKateOverrideIndentation, &QCheckBox::toggled, this, &SourceFormatterSettings::somethingChanged ); connect( styleList, &QListWidget::currentRowChanged, this, &SourceFormatterSettings::selectStyle ); connect( btnDelStyle, &QPushButton::clicked, this, &SourceFormatterSettings::deleteStyle ); connect( btnNewStyle, &QPushButton::clicked, this, &SourceFormatterSettings::newStyle ); connect( btnEditStyle, &QPushButton::clicked, this, &SourceFormatterSettings::editStyle ); connect( styleList, &QListWidget::itemChanged, this, &SourceFormatterSettings::styleNameChanged ); m_document = KTextEditor::Editor::instance()->createDocument(this); m_document->setReadWrite(false); m_view = m_document->createView(textEditor); m_view->setStatusBarEnabled(false); QVBoxLayout *layout2 = new QVBoxLayout(textEditor); layout2->addWidget(m_view); textEditor->setLayout(layout2); m_view->show(); KTextEditor::ConfigInterface *iface = qobject_cast(m_view); if (iface) { iface->setConfigValue(QStringLiteral("dynamic-word-wrap"), false); iface->setConfigValue(QStringLiteral("icon-bar"), false); } } SourceFormatterSettings::~SourceFormatterSettings() { qDeleteAll(formatters); } void selectAvailableStyle(LanguageSettings& lang) { Q_ASSERT(!lang.selectedFormatter->styles.empty()); lang.selectedStyle = *lang.selectedFormatter->styles.begin(); } void SourceFormatterSettings::reset() { SourceFormatterController* fmtctrl = Core::self()->sourceFormatterControllerInternal(); QList plugins = KDevelop::ICore::self()->pluginController()->allPluginsForExtension( QStringLiteral("org.kdevelop.ISourceFormatter") ); foreach( KDevelop::IPlugin* plugin, plugins ) { KDevelop::ISourceFormatter* ifmt = plugin->extension(); auto info = KDevelop::Core::self()->pluginControllerInternal()->pluginInfo( plugin ); KDevelop::SourceFormatter* formatter; FormatterMap::const_iterator iter = formatters.constFind(ifmt->name()); if (iter == formatters.constEnd()) { formatter = fmtctrl->createFormatterForPlugin(ifmt); formatters[ifmt->name()] = formatter; } else { formatter = iter.value(); } foreach ( const SourceFormatterStyle* style, formatter->styles ) { foreach ( const SourceFormatterStyle::MimeHighlightPair& item, style->mimeTypes() ) { QMimeType mime = QMimeDatabase().mimeTypeForName(item.mimeType); if (!mime.isValid()) { qCWarning(SHELL) << "plugin" << info.name() << "supports unknown mimetype entry" << item.mimeType; continue; } QString languageName = item.highlightMode; LanguageSettings& l = languages[languageName]; l.mimetypes.append(mime); l.formatters.insert( formatter ); } } } // Sort the languages, preferring firstly active, then loaded languages QList sortedLanguages; foreach(const auto language, KDevelop::ICore::self()->languageController()->activeLanguages() + KDevelop::ICore::self()->languageController()->loadedLanguages()) { if( languages.contains( language->name() ) && !sortedLanguages.contains(language->name()) ) { sortedLanguages.push_back( language->name() ); } } foreach( const QString& name, languages.keys() ) if( !sortedLanguages.contains( name ) ) sortedLanguages.push_back( name ); foreach( const QString& name, sortedLanguages ) { // Pick the first appropriate mimetype for this language KConfigGroup grp = fmtctrl->sessionConfig(); LanguageSettings& l = languages[name]; const QList mimetypes = l.mimetypes; foreach (const QMimeType& mimetype, mimetypes) { QStringList formatterAndStyleName = grp.readEntry(mimetype.name(), QString()).split(QStringLiteral("||"), QString::KeepEmptyParts); FormatterMap::const_iterator formatterIter = formatters.constFind(formatterAndStyleName.first()); if (formatterIter == formatters.constEnd()) { qCDebug(SHELL) << "Reference to unknown formatter" << formatterAndStyleName.first(); Q_ASSERT(!l.formatters.empty()); // otherwise there should be no entry for 'name' l.selectedFormatter = *l.formatters.begin(); selectAvailableStyle(l); } else { l.selectedFormatter = formatterIter.value(); SourceFormatter::StyleMap::const_iterator styleIter = l.selectedFormatter->styles.constFind(formatterAndStyleName.at( 1 )); if (styleIter == l.selectedFormatter->styles.constEnd()) { qCDebug(SHELL) << "No style" << formatterAndStyleName.at( 1 ) << "found for formatter" << formatterAndStyleName.first(); selectAvailableStyle(l); } else { l.selectedStyle = styleIter.value(); } } } if (!l.selectedFormatter) { Q_ASSERT(!l.formatters.empty()); l.selectedFormatter = *l.formatters.begin(); } if (!l.selectedStyle) { selectAvailableStyle(l); } } bool b = blockSignals( true ); cbLanguages->blockSignals( !b ); cbFormatters->blockSignals( !b ); styleList->blockSignals( !b ); chkKateModelines->blockSignals( !b ); chkKateOverrideIndentation->blockSignals( !b ); cbLanguages->clear(); cbFormatters->clear(); styleList->clear(); chkKateModelines->setChecked( fmtctrl->sessionConfig().readEntry( SourceFormatterController::kateModeLineConfigKey(), false ) ); chkKateOverrideIndentation->setChecked( fmtctrl->sessionConfig().readEntry( SourceFormatterController::kateOverrideIndentationConfigKey(), false ) ); foreach( const QString& name, sortedLanguages ) { cbLanguages->addItem( name ); } if( cbLanguages->count() == 0 ) { cbLanguages->setEnabled( false ); selectLanguage( -1 ); } else { cbLanguages->setCurrentIndex( 0 ); selectLanguage( 0 ); } updatePreview(); blockSignals( b ); cbLanguages->blockSignals( b ); cbFormatters->blockSignals( b ); styleList->blockSignals( b ); chkKateModelines->blockSignals( b ); chkKateOverrideIndentation->blockSignals( b ); } void SourceFormatterSettings::apply() { KConfigGroup globalConfig = Core::self()->sourceFormatterControllerInternal()->globalConfig(); foreach( SourceFormatter* fmt, formatters ) { KConfigGroup fmtgrp = globalConfig.group( fmt->formatter->name() ); // delete all styles so we don't leave any behind when all user styles are deleted foreach( const QString& subgrp, fmtgrp.groupList() ) { if( subgrp.startsWith( Strings::userStylePrefix() ) ) { fmtgrp.deleteGroup( subgrp ); } } foreach( const SourceFormatterStyle* style, fmt->styles ) { if( style->name().startsWith( Strings::userStylePrefix() ) ) { KConfigGroup stylegrp = fmtgrp.group( style->name() ); stylegrp.writeEntry( SourceFormatterController::styleCaptionKey(), style->caption() ); stylegrp.writeEntry( SourceFormatterController::styleContentKey(), style->content() ); stylegrp.writeEntry( SourceFormatterController::styleMimeTypesKey(), style->mimeTypesVariant() ); stylegrp.writeEntry( SourceFormatterController::styleSampleKey(), style->overrideSample() ); } } } KConfigGroup sessionConfig = Core::self()->sourceFormatterControllerInternal()->sessionConfig(); for ( LanguageMap::const_iterator iter = languages.constBegin(); iter != languages.constEnd(); ++iter ) { foreach(const QMimeType& mime, iter.value().mimetypes) { sessionConfig.writeEntry(mime.name(), QStringLiteral("%1||%2").arg(iter.value().selectedFormatter->formatter->name(), iter.value().selectedStyle->name())); } } sessionConfig.writeEntry( SourceFormatterController::kateModeLineConfigKey(), chkKateModelines->isChecked() ); sessionConfig.writeEntry( SourceFormatterController::kateOverrideIndentationConfigKey(), chkKateOverrideIndentation->isChecked() ); sessionConfig.sync(); globalConfig.sync(); Core::self()->sourceFormatterControllerInternal()->settingsChanged(); } void SourceFormatterSettings::defaults() { // do nothing } void SourceFormatterSettings::enableStyleButtons() { bool userEntry = styleList->currentItem() && styleList->currentItem()->data( STYLE_ROLE ).toString().startsWith( Strings::userStylePrefix() ); QString languageName = cbLanguages->currentText(); QMap< QString, LanguageSettings >::const_iterator it = languages.constFind(languageName); bool hasEditWidget = false; if (it != languages.constEnd()) { const LanguageSettings& l = it.value(); Q_ASSERT(l.selectedFormatter); ISourceFormatter* fmt = l.selectedFormatter->formatter; hasEditWidget = ( fmt && QScopedPointer(fmt->editStyleWidget( l.mimetypes.first() )) ); } btnDelStyle->setEnabled( userEntry ); btnEditStyle->setEnabled( userEntry && hasEditWidget ); btnNewStyle->setEnabled( cbFormatters->currentIndex() >= 0 && hasEditWidget ); } void SourceFormatterSettings::selectLanguage( int idx ) { cbFormatters->clear(); if( idx < 0 ) { cbFormatters->setEnabled( false ); selectFormatter( -1 ); return; } cbFormatters->setEnabled( true ); { QSignalBlocker blocker(cbFormatters); LanguageSettings& l = languages[cbLanguages->itemText( idx )]; foreach( const SourceFormatter* fmt, l.formatters ) { cbFormatters->addItem( fmt->formatter->caption(), fmt->formatter->name() ); } cbFormatters->setCurrentIndex(cbFormatters->findData(l.selectedFormatter->formatter->name())); } selectFormatter( cbFormatters->currentIndex() ); emit changed(); } void SourceFormatterSettings::selectFormatter( int idx ) { styleList->clear(); if( idx < 0 ) { styleList->setEnabled( false ); enableStyleButtons(); return; } styleList->setEnabled( true ); LanguageSettings& l = languages[ cbLanguages->currentText() ]; Q_ASSERT( idx < l.formatters.size() ); FormatterMap::const_iterator formatterIter = formatters.constFind(cbFormatters->itemData( idx ).toString()); Q_ASSERT( formatterIter != formatters.constEnd() ); Q_ASSERT( l.formatters.contains(formatterIter.value()) ); if (l.selectedFormatter != formatterIter.value()) { l.selectedFormatter = formatterIter.value(); l.selectedStyle = nullptr; // will hold 0 until a style is picked } foreach( const SourceFormatterStyle* style, formatterIter.value()->styles ) { if ( ! style->supportsLanguage(cbLanguages->currentText())) { // do not list items which do not support the selected language continue; } QListWidgetItem* item = addStyle( *style ); if (style == l.selectedStyle) { styleList->setCurrentItem(item); } } if (l.selectedStyle == nullptr) { styleList->setCurrentRow(0); } enableStyleButtons(); emit changed(); } void SourceFormatterSettings::selectStyle( int row ) { if( row < 0 ) { enableStyleButtons(); return; } styleList->setCurrentRow( row ); LanguageSettings& l = languages[ cbLanguages->currentText() ]; l.selectedStyle = l.selectedFormatter->styles[styleList->item( row )->data( STYLE_ROLE ).toString()]; enableStyleButtons(); updatePreview(); emit changed(); } void SourceFormatterSettings::deleteStyle() { Q_ASSERT( styleList->currentRow() >= 0 ); QListWidgetItem* item = styleList->currentItem(); LanguageSettings& l = languages[ cbLanguages->currentText() ]; SourceFormatter* fmt = l.selectedFormatter; SourceFormatter::StyleMap::iterator styleIter = fmt->styles.find(item->data( STYLE_ROLE ).toString()); QStringList otherLanguageNames; QList otherlanguages; for ( LanguageMap::iterator languageIter = languages.begin(); languageIter != languages.end(); ++languageIter ) { if ( &languageIter.value() != &l && languageIter.value().selectedStyle == styleIter.value() ) { otherLanguageNames.append(languageIter.key()); otherlanguages.append(&languageIter.value()); } } if (!otherLanguageNames.empty() && KMessageBox::warningContinueCancel(this, i18n("The style %1 is also used for the following languages:\n%2.\nAre you sure you want to delete it?", styleIter.value()->caption(), otherLanguageNames.join(QStringLiteral("\n"))), i18n("Style being deleted")) != KMessageBox::Continue) { return; } styleList->takeItem( styleList->currentRow() ); fmt->styles.erase(styleIter); delete item; selectStyle( styleList->count() > 0 ? 0 : -1 ); foreach (LanguageSettings* lang, otherlanguages) { selectAvailableStyle(*lang); } updatePreview(); emit changed(); } void SourceFormatterSettings::editStyle() { QString language = cbLanguages->currentText(); Q_ASSERT( languages.contains( language ) ); LanguageSettings& l = languages[ language ]; SourceFormatter* fmt = l.selectedFormatter; QMimeType mimetype = l.mimetypes.first(); if( QScopedPointer(fmt->formatter->editStyleWidget( mimetype )) ) { EditStyleDialog dlg( fmt->formatter, mimetype, *l.selectedStyle, this ); if( dlg.exec() == QDialog::Accepted ) { l.selectedStyle->setContent(dlg.content()); } updatePreview(); emit changed(); } } void SourceFormatterSettings::newStyle() { QListWidgetItem* item = styleList->currentItem(); LanguageSettings& l = languages[ cbLanguages->currentText() ]; SourceFormatter* fmt = l.selectedFormatter; int idx = 0; for( int i = 0; i < styleList->count(); i++ ) { QString name = styleList->item( i )->data( STYLE_ROLE ).toString(); if( name.startsWith( Strings::userStylePrefix() ) && name.midRef( Strings::userStylePrefix().length() ).toInt() >= idx ) { idx = name.midRef( Strings::userStylePrefix().length() ).toInt(); } } // Increase number for next style idx++; SourceFormatterStyle* s = new SourceFormatterStyle( QStringLiteral( "%1%2" ).arg( Strings::userStylePrefix() ).arg( idx ) ); if( item ) { SourceFormatterStyle* existstyle = fmt->styles[ item->data( STYLE_ROLE ).toString() ]; s->setCaption( i18n( "New %1", existstyle->caption() ) ); s->copyDataFrom( existstyle ); } else { s->setCaption( i18n( "New Style" ) ); } fmt->styles[ s->name() ] = s; QListWidgetItem* newitem = addStyle( *s ); selectStyle( styleList->row( newitem ) ); styleList->editItem( newitem ); emit changed(); } void SourceFormatterSettings::styleNameChanged( QListWidgetItem* item ) { if ( !item->isSelected() ) { return; } LanguageSettings& l = languages[ cbLanguages->currentText() ]; l.selectedStyle->setCaption( item->text() ); emit changed(); } QListWidgetItem* SourceFormatterSettings::addStyle( const SourceFormatterStyle& s ) { QListWidgetItem* item = new QListWidgetItem( styleList ); item->setText( s.caption() ); item->setData( STYLE_ROLE, s.name() ); if( s.name().startsWith( Strings::userStylePrefix() ) ) { item->setFlags( item->flags() | Qt::ItemIsEditable ); } styleList->addItem( item ); return item; } void SourceFormatterSettings::updatePreview() { m_document->setReadWrite( true ); QString langName = cbLanguages->itemText( cbLanguages->currentIndex() ); if( !langName.isEmpty() ) { LanguageSettings& l = languages[ langName ]; SourceFormatter* fmt = l.selectedFormatter; SourceFormatterStyle* style = l.selectedStyle; descriptionLabel->setText( style->description() ); if( style->usePreview() ) { ISourceFormatter* ifmt = fmt->formatter; QMimeType mime = l.mimetypes.first(); m_document->setHighlightingMode( style->modeForMimetype( mime ) ); //NOTE: this is ugly, but otherwise kate might remove tabs again :-/ // see also: https://bugs.kde.org/show_bug.cgi?id=291074 KTextEditor::ConfigInterface* iface = qobject_cast(m_document); QVariant oldReplaceTabs; if (iface) { oldReplaceTabs = iface->configValue(QStringLiteral("replace-tabs")); iface->setConfigValue(QStringLiteral("replace-tabs"), false); } m_document->setText( ifmt->formatSourceWithStyle( *style, ifmt->previewText( *style, mime ), QUrl(), mime ) ); if (iface) { iface->setConfigValue(QStringLiteral("replace-tabs"), oldReplaceTabs); } previewLabel->show(); textEditor->show(); }else{ previewLabel->hide(); textEditor->hide(); } } else { m_document->setText( i18n( "No Language selected" ) ); } m_view->setCursorPosition( KTextEditor::Cursor( 0, 0 ) ); m_document->setReadWrite( false ); } void SourceFormatterSettings::somethingChanged() { // Widgets are managed manually, so we have to explicitly tell KCModule // that we have some changes, otherwise it won't call "save" and/or will not activate // "Appy" emit changed(); } QString SourceFormatterSettings::name() const { return i18n("Source Formatter"); } QString SourceFormatterSettings::fullName() const { return i18n("Configure Source Formatter"); } QIcon SourceFormatterSettings::icon() const { return QIcon::fromTheme(QStringLiteral("text-field")); } diff --git a/shell/shellextension.h b/shell/shellextension.h index 1948760c8..3190c32c0 100644 --- a/shell/shellextension.h +++ b/shell/shellextension.h @@ -1,83 +1,83 @@ /*************************************************************************** * Copyright 2004 Alexander Dymo * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_SHELLEXTENSION_H #define KDEVPLATFORM_SHELLEXTENSION_H -#include +#include #include "shellexport.h" namespace KDevelop { /**Default area parameters collection.*/ struct AreaParams { /**Unique name for the area.*/ QString name; /**User-visible area title.*/ QString title; }; /** Shell extension. Provides application-dependent and shell-independent functionality. Shell uses extensions to perform application dependent actions. */ class KDEVPLATFORMSHELL_EXPORT ShellExtension { public: virtual ~ShellExtension() {} /**Returns an instance of a shell. Subclasses must create an instance of a shell by themselves. For example they could provide static init() method like: @code static void init() { s_instance = new MyExtension(); } @endcode*/ static ShellExtension *getInstance(); /**Reimplement to return the path to the executable that needs to be executed for new sessions.*/ virtual QString executableFilePath() = 0; /**Reimplement to return the name of KXMLGUI resource file for an application.*/ virtual QString xmlFile() = 0; /**Reimplement to return the name of the default ui area.*/ virtual AreaParams defaultArea() = 0; /**Reimplement to return the filename extension for project files.*/ virtual QString projectFileExtension() = 0; /**Reimplement to return the description for project files.*/ virtual QString projectFileDescription() = 0; /** * Reimplement to return the list of plugins that should * automatically be loaded */ virtual QStringList defaultPlugins() = 0; protected: ShellExtension(); static ShellExtension *s_instance; }; } #endif diff --git a/shell/sourceformattercontroller.h b/shell/sourceformattercontroller.h index 5db2e80db..5773337ae 100644 --- a/shell/sourceformattercontroller.h +++ b/shell/sourceformattercontroller.h @@ -1,164 +1,164 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat Copyright (C) 2008 Cédric Pasteur 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. */ #ifndef KDEVPLATFORM_SOURCEFORMATTERCONTROLLER_H #define KDEVPLATFORM_SOURCEFORMATTERCONTROLLER_H #include #include -#include -#include +#include +#include #include #include #include #include #include "shellexport.h" namespace KTextEditor { class Document; } class QAction; namespace KDevelop { class Context; class ContextMenuExtension; class ProjectBaseItem; class IDocument; class ISourceFormatter; class IPlugin; struct SourceFormatter { KDevelop::ISourceFormatter* formatter; // style name -> style. style objects owned by this typedef QMap StyleMap; StyleMap styles; // Get a list of supported mime types from the style map. QSet supportedMimeTypes() const { QSet supported; for ( auto style: styles ) { foreach ( auto& item, style->mimeTypes() ) { supported.insert(item.mimeType); } } return supported; } ~SourceFormatter() { qDeleteAll(styles); }; }; /** \short A singleton class managing all source formatter plugins */ class KDEVPLATFORMSHELL_EXPORT SourceFormatterController : public ISourceFormatterController, public KXMLGUIClient { Q_OBJECT public: static QString kateModeLineConfigKey(); static QString kateOverrideIndentationConfigKey(); static QString styleCaptionKey(); static QString styleContentKey(); static QString styleMimeTypesKey(); static QString styleSampleKey(); explicit SourceFormatterController(QObject *parent = nullptr); ~SourceFormatterController() override; void initialize(); void cleanup(); //----------------- Public API defined in interfaces ------------------- /** \return The formatter corresponding to the language * of the document corresponding to the \arg url. */ ISourceFormatter* formatterForUrl(const QUrl &url) override; /** Loads and returns a source formatter for this mime type. * The language is then activated and the style is loaded. * The source formatter is then ready to use on a file. */ ISourceFormatter* formatterForMimeType(const QMimeType& mime) override; /** \return Whether this mime type is supported by any plugin. */ bool isMimeTypeSupported(const QMimeType& mime) override; /** * @brief Instantiate a Formatter for the given plugin and load its configuration. * * @param ifmt The ISourceFormatter interface of the plugin * @return KDevelop::SourceFormatter* the SourceFormatter instance for the plugin, including config items */ SourceFormatter* createFormatterForPlugin(KDevelop::ISourceFormatter* ifmt) const; /** * @brief Find the first formatter which supports a given mime type. */ ISourceFormatter* findFirstFormatterForMimeType(const QMimeType& mime) const; KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context* context); KDevelop::SourceFormatterStyle styleForMimeType(const QMimeType& mime) override; KConfigGroup sessionConfig() const; KConfigGroup globalConfig() const; void settingsChanged(); void disableSourceFormatting(bool disable) override; bool sourceFormattingEnabled() override; private Q_SLOTS: void updateFormatTextAction(); void beautifySource(); void beautifyLine(); void formatFiles(); void documentLoaded( KDevelop::IDocument* ); private: /** \return A modeline string (to add at the end or the beginning of a file) * corresponding to the settings of the active language. */ QString addModelineForCurrentLang(QString input, const QUrl& url, const QMimeType&); /** \return The name of kate indentation mode for the mime type. * examples are cstyle, python, etc. */ QString indentationMode(const QMimeType& mime); void formatDocument(KDevelop::IDocument* doc, ISourceFormatter* formatter, const QMimeType& mime); // Adapts the mode of the editor regarding indentation-style void adaptEditorIndentationMode(KTextEditor::Document* doc, KDevelop::ISourceFormatter* formatter, bool ignoreModeline = false); void formatFiles(QList &list); // GUI actions QAction* m_formatTextAction; QAction* m_formatFilesAction; QAction* m_formatLine; QList m_prjItems; QList m_urls; bool m_enabled; }; } #endif // KDEVPLATFORM_SOURCEFORMATTERMANAGER_H diff --git a/shell/statusbar.cpp b/shell/statusbar.cpp index 9de8185a5..f2911a47a 100644 --- a/shell/statusbar.cpp +++ b/shell/statusbar.cpp @@ -1,264 +1,264 @@ /* This file is part of the KDE project 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 "statusbar.h" #include "progresswidget/statusbarprogresswidget.h" #include "progresswidget/progressmanager.h" #include "progresswidget/progressdialog.h" -#include -#include +#include +#include #include #include #include #include #include #include #include "plugincontroller.h" #include "core.h" namespace KDevelop { StatusBar::StatusBar(QWidget* parent) : QStatusBar(parent) , m_timer(new QTimer(this)) , m_currentView(nullptr) , m_errorRemovalMapper(new QSignalMapper(this)) { #ifdef Q_OS_MAC /* At time of writing this is only required for OSX and only has effect on OSX. ifdef for robustness to future platform dependent theme/widget changes https://phabricator.kde.org/D656 */ setStyleSheet("QStatusBar{background:transparent;}"); #endif m_timer->setSingleShot(true); connect(m_timer, &QTimer::timeout, this, &StatusBar::slotTimeout); connect(Core::self()->pluginController(), &IPluginController::pluginLoaded, this, &StatusBar::pluginLoaded); QList plugins = Core::self()->pluginControllerInternal()->allPluginsForExtension(QStringLiteral("IStatus")); foreach (IPlugin* plugin, plugins) registerStatus(plugin); registerStatus(Core::self()->languageController()->backgroundParser()); connect(m_errorRemovalMapper, static_cast(&QSignalMapper::mapped), this, &StatusBar::removeError); m_progressController = Core::self()->progressController(); m_progressDialog = new ProgressDialog(this, parent); // construct this first, then progressWidget m_progressDialog->setVisible(false); m_progressWidget = new StatusbarProgressWidget(m_progressDialog, this); addPermanentWidget(m_progressWidget, 0); } void StatusBar::removeError(QWidget* w) { removeWidget(w); w->deleteLater(); } void StatusBar::viewChanged(Sublime::View* view) { if (m_currentView) m_currentView->disconnect(this); m_currentView = view; if (view) { connect(view, &Sublime::View::statusChanged, this, &StatusBar::viewStatusChanged); QStatusBar::showMessage(view->viewStatus(), 0); } } void StatusBar::viewStatusChanged(Sublime::View* view) { QStatusBar::showMessage(view->viewStatus(), 0); } void StatusBar::pluginLoaded(IPlugin* plugin) { if (qobject_cast(plugin)) registerStatus(plugin); } void StatusBar::registerStatus(QObject* status) { Q_ASSERT(qobject_cast(status)); // can't convert this to new signal slot syntax, IStatus is not a QObject connect(status, SIGNAL(clearMessage(KDevelop::IStatus*)), SLOT(clearMessage(KDevelop::IStatus*)), Qt::QueuedConnection); connect(status, SIGNAL(showMessage(KDevelop::IStatus*,QString,int)), SLOT(showMessage(KDevelop::IStatus*,QString,int)), Qt::QueuedConnection); connect(status, SIGNAL(hideProgress(KDevelop::IStatus*)), SLOT(hideProgress(KDevelop::IStatus*)), Qt::QueuedConnection); connect(status, SIGNAL(showProgress(KDevelop::IStatus*,int,int,int)), SLOT(showProgress(KDevelop::IStatus*,int,int,int)), Qt::QueuedConnection); // Don't try to connect when the status object doesn't provide an error message signal (ie. avoid warning) if (status->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("showErrorMessage(QString,int)")) != -1) { connect(status, SIGNAL(showErrorMessage(QString,int)), SLOT(showErrorMessage(QString,int)), Qt::QueuedConnection); } } QWidget* errorMessage(QWidget* parent, const QString& text) { KSqueezedTextLabel* label = new KSqueezedTextLabel(parent); KStatefulBrush red(KColorScheme::Window, KColorScheme::NegativeText); QPalette pal = label->palette(); pal.setBrush(QPalette::WindowText, red.brush(label)); label->setPalette(pal); label->setAlignment(Qt::AlignRight); label->setText(text); label->setToolTip(text); return label; } QTimer* StatusBar::errorTimeout(QWidget* error, int timeout) { QTimer* timer = new QTimer(error); timer->setSingleShot(true); timer->setInterval(1000*timeout); m_errorRemovalMapper->setMapping(timer, error); connect(timer, &QTimer::timeout, m_errorRemovalMapper, static_cast(&QSignalMapper::map)); return timer; } void StatusBar::showErrorMessage(const QString& message, int timeout) { QWidget* error = errorMessage(this, message); QTimer* timer = errorTimeout(error, timeout); addWidget(error); timer->start(); // triggers removeError() } void StatusBar::slotTimeout() { QMutableMapIterator it = m_messages; while (it.hasNext()) { it.next(); if (it.value().timeout) { it.value().timeout -= m_timer->interval(); if (it.value().timeout == 0) it.remove(); } } updateMessage(); } void StatusBar::updateMessage() { if (m_timer->isActive()) { m_timer->stop(); m_timer->setInterval(m_time.elapsed()); slotTimeout(); } QString ret; int timeout = 0; foreach (const Message& m, m_messages) { if (!ret.isEmpty()) ret += QLatin1String("; "); ret += m.text; if (timeout) timeout = qMin(timeout, m.timeout); else timeout = m.timeout; } if (!ret.isEmpty()) QStatusBar::showMessage(ret); else QStatusBar::clearMessage(); if (timeout) { m_time.start(); m_timer->start(timeout); } } void StatusBar::clearMessage( IStatus* status ) { if (m_messages.contains(status)) { m_messages.remove(status); updateMessage(); } } void StatusBar::showMessage( IStatus* status, const QString & message, int timeout) { if ( m_progressItems.contains(status) ) { ProgressItem* i = m_progressItems[status]; i->setStatus(message); } else { Message m; m.text = message; m.timeout = timeout; m_messages.insert(status, m); updateMessage(); } } void StatusBar::hideProgress( IStatus* status ) { if (m_progressItems.contains(status)) { m_progressItems[status]->setComplete(); m_progressItems.remove(status); } } void StatusBar::showProgress( IStatus* status, int minimum, int maximum, int value) { if (!m_progressItems.contains(status)) { bool canBeCanceled = false; m_progressItems[status] = m_progressController->createProgressItem( ProgressManager::getUniqueID(), status->statusName(), QString(), canBeCanceled);; } ProgressItem* i = m_progressItems[status]; m_progressWidget->raise(); m_progressDialog->raise(); if( minimum == 0 && maximum == 0 ) { i->setUsesBusyIndicator( true ); } else { i->setUsesBusyIndicator( false ); i->setProgress( 100*value/maximum ); } } } diff --git a/shell/statusbar.h b/shell/statusbar.h index 3863fa098..f79df8836 100644 --- a/shell/statusbar.h +++ b/shell/statusbar.h @@ -1,99 +1,99 @@ /* This file is part of the KDE project 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. */ #ifndef KDEVPLATFORM_STATUSBAR_H #define KDEVPLATFORM_STATUSBAR_H -#include -#include +#include +#include #include class QSignalMapper; class QTimer; namespace Sublime { class View; } namespace KDevelop { class IStatus; class IPlugin; class ProgressItem; class ProgressDialog; class StatusbarProgressWidget; class ProgressManager; /** * Status bar */ class StatusBar : public QStatusBar { Q_OBJECT public: /** * Constructs a status bar. */ explicit StatusBar(QWidget* parent); void registerStatus(QObject* status); void updateMessage(); void viewChanged(Sublime::View* view); public Q_SLOTS: void showErrorMessage(const QString& message, int timeout); private Q_SLOTS: void clearMessage( KDevelop::IStatus* ); void showMessage( KDevelop::IStatus*, const QString & message, int timeout); void hideProgress( KDevelop::IStatus* ); void showProgress( KDevelop::IStatus*, int minimum, int maximum, int value); void slotTimeout(); void viewStatusChanged(Sublime::View* view); void pluginLoaded(KDevelop::IPlugin*); void removeError(QWidget*); private: QTimer* errorTimeout(QWidget* error, int timeout); private: struct Message { QString text; int timeout; }; QMap m_messages; QTimer* m_timer; QTime m_time; Sublime::View* m_currentView; QSignalMapper* m_errorRemovalMapper; QMap m_progressItems; StatusbarProgressWidget* m_progressWidget; // embedded in the statusbar, shows a single progressbar & button to expand the overlay widget ProgressDialog* m_progressDialog; // the actual overlay widget that contains multiple progressbars and status messages ProgressManager* m_progressController; // progress item model }; } #endif // KDEVPLATFORM_STATUSBAR_H diff --git a/shell/tests/nonguiinterfaceplugin.h b/shell/tests/nonguiinterfaceplugin.h index 54881a1c2..e7db4a8c2 100644 --- a/shell/tests/nonguiinterfaceplugin.h +++ b/shell/tests/nonguiinterfaceplugin.h @@ -1,47 +1,47 @@ /* * This file is part of KDevelop * * 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. */ #ifndef NONGUIINTERFACEPLUGIN #define NONGUIINTERFACEPLUGIN #include -#include +#include class ITestNonGuiInterface { public: virtual ~ITestNonGuiInterface() {} }; Q_DECLARE_INTERFACE( ITestNonGuiInterface, "org.kdevelop.ITestNonGuiInterface" ) class NonGuiInterfacePlugin : public KDevelop::IPlugin, ITestNonGuiInterface { Q_OBJECT Q_INTERFACES(ITestNonGuiInterface) public: explicit NonGuiInterfacePlugin( QObject* parent, const QVariantList& = QVariantList() ); }; #endif diff --git a/shell/tests/test_documentcontroller.cpp b/shell/tests/test_documentcontroller.cpp index 930dd5afd..d833e959c 100644 --- a/shell/tests/test_documentcontroller.cpp +++ b/shell/tests/test_documentcontroller.cpp @@ -1,197 +1,197 @@ /* Unit tests for DocumentController.* Copyright 2011 Damien Flament 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 "test_documentcontroller.h" #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include using namespace KDevelop; void TestDocumentController::initTestCase() { AutoTestShell::init(); TestCore::initialize(); Core::self()->languageController()->backgroundParser()->disableProcessing(); m_subject = Core::self()->documentController(); } void TestDocumentController::init() { Core::self()->documentControllerInternal()->initialize(); // create temp files m_file1.setFileTemplate(m_tempDir.path() + "/tmp_XXXXXX.txt"); m_file2.setFileTemplate(m_tempDir.path() + "/tmp_XXXXXX.txt"); if(!m_file1.open() || !m_file2.open()) { QFAIL("Can't create temp files"); } // pre-conditions QVERIFY(m_subject->openDocuments().empty()); QVERIFY(m_subject->documentForUrl(QUrl()) == nullptr); QVERIFY(m_subject->activeDocument() == nullptr); } void TestDocumentController::cleanup() { // ensure there are not opened documents for next test foreach(IDocument* document, m_subject->openDocuments()) { document->close(IDocument::Discard); } Core::self()->documentControllerInternal()->cleanup(); } void TestDocumentController::cleanupTestCase() { TestCore::shutdown(); m_tempDir.remove(); } void TestDocumentController::testOpeningNewDocumentFromText() { qRegisterMetaType("KDevelop::IDocument*"); QSignalSpy createdSpy(m_subject, SIGNAL(textDocumentCreated(KDevelop::IDocument*))); QVERIFY(createdSpy.isValid()); QSignalSpy openedSpy(m_subject, SIGNAL(documentOpened(KDevelop::IDocument*))); QVERIFY(openedSpy.isValid()); IDocument* document = m_subject->openDocumentFromText(QLatin1String("")); QVERIFY(document != nullptr); QCOMPARE(createdSpy.count(), 1); QCOMPARE(openedSpy.count(), 1); QVERIFY(!m_subject->openDocuments().empty()); QVERIFY(m_subject->documentForUrl(document->url()) == document); QVERIFY(m_subject->activeDocument() == document); } void TestDocumentController::testOpeningDocumentFromUrl() { QUrl url = QUrl::fromLocalFile(m_file1.fileName()); IDocument* document = m_subject->openDocument(url); QVERIFY(document != nullptr); } void TestDocumentController::testSaveSomeDocuments() { // create documents QTemporaryDir dir; IDocument *document1 = m_subject->openDocument(createFile(dir, QStringLiteral("foo"))); IDocument *document2 = m_subject->openDocument(createFile(dir, QStringLiteral("bar"))); QCOMPARE(document1->state(), IDocument::Clean); QCOMPARE(document2->state(), IDocument::Clean); // edit both documents document1->textDocument()->insertText(KTextEditor::Cursor(), QStringLiteral("some text")); document2->textDocument()->insertText(KTextEditor::Cursor(), QStringLiteral("some text")); QCOMPARE(document1->state(), IDocument::Modified); QCOMPARE(document2->state(), IDocument::Modified); // save one document (Silent == don't ask user) m_subject->saveSomeDocuments(QList() << document1, IDocument::Silent); QCOMPARE(document1->state(), IDocument::Clean); QCOMPARE(document2->state(), IDocument::Modified); } void TestDocumentController::testSaveAllDocuments() { // create documents QTemporaryDir dir; IDocument *document1 = m_subject->openDocument(createFile(dir, QStringLiteral("foo"))); IDocument *document2 = m_subject->openDocument(createFile(dir, QStringLiteral("bar"))); QCOMPARE(document1->state(), IDocument::Clean); QCOMPARE(document2->state(), IDocument::Clean); // edit documents document1->textDocument()->insertText(KTextEditor::Cursor(), QStringLiteral("some text")); document2->textDocument()->insertText(KTextEditor::Cursor(), QStringLiteral("some text")); QCOMPARE(document1->state(), IDocument::Modified); QCOMPARE(document2->state(), IDocument::Modified); // save documents m_subject->saveAllDocuments(IDocument::Silent); QCOMPARE(document1->state(), IDocument::Clean); QCOMPARE(document2->state(), IDocument::Clean); } void TestDocumentController::testCloseAllDocuments() { // create documents m_subject->openDocumentFromText(QLatin1String("")); m_subject->openDocumentFromText(QLatin1String("")); QVERIFY(!m_subject->openDocuments().empty()); m_subject->closeAllDocuments(); QVERIFY(m_subject->openDocuments().empty()); } QUrl TestDocumentController::createFile(const QTemporaryDir& dir, const QString& filename) { QFile file(dir.path() + filename); bool success = file.open(QIODevice::WriteOnly | QIODevice::Text); if(!success) { QWARN(QString("Failed to create file: " + dir.path() + filename).toLatin1().data()); return QUrl(); } file.close(); return QUrl::fromLocalFile(dir.path() + filename); } void TestDocumentController::testEmptyUrl() { const auto first = DocumentController::nextEmptyDocumentUrl(); QVERIFY(DocumentController::isEmptyDocumentUrl(first)); QCOMPARE(DocumentController::nextEmptyDocumentUrl(), first); auto doc = m_subject->openDocumentFromText(QString()); QCOMPARE(doc->url(), first); const auto second = DocumentController::nextEmptyDocumentUrl(); QVERIFY(first != second); QVERIFY(DocumentController::isEmptyDocumentUrl(second)); QVERIFY(!DocumentController::isEmptyDocumentUrl(QUrl())); QVERIFY(!DocumentController::isEmptyDocumentUrl(QUrl(QStringLiteral("http://foo.org")))); QVERIFY(!DocumentController::isEmptyDocumentUrl(QUrl(QStringLiteral("http://foo.org/test")))); QVERIFY(!DocumentController::isEmptyDocumentUrl(QUrl::fromLocalFile(QStringLiteral("/")))); QVERIFY(!DocumentController::isEmptyDocumentUrl(QUrl::fromLocalFile(QStringLiteral("/test")))); } QTEST_MAIN(TestDocumentController); diff --git a/shell/tests/test_documentcontroller.h b/shell/tests/test_documentcontroller.h index 470a5527a..58d985671 100644 --- a/shell/tests/test_documentcontroller.h +++ b/shell/tests/test_documentcontroller.h @@ -1,62 +1,62 @@ /* Unit tests for DocumentController. Copyright 2011 Damien Flament 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. */ #ifndef KDEVPLATFORM_TEST_DOCUMENTCONTROLLER_H #define KDEVPLATFORM_TEST_DOCUMENTCONTROLLER_H -#include +#include #include #include #include namespace KDevelop { class IDocument; class IDocumentController; } using namespace KDevelop; class TestDocumentController : public QObject { Q_OBJECT private slots : void initTestCase(); void init(); void cleanup(); void cleanupTestCase(); void testOpeningNewDocumentFromText(); void testOpeningDocumentFromUrl(); void testSaveSomeDocuments(); void testSaveAllDocuments(); void testCloseAllDocuments(); void testEmptyUrl(); private : QUrl createFile(const QTemporaryDir& dir, const QString& filename); IDocumentController* m_subject; QTemporaryDir m_tempDir; QTemporaryFile m_file1; QTemporaryFile m_file2; }; #endif // KDEVPLATFORM_TEST_DOCUMENTCONTROLLER_H diff --git a/shell/tests/test_ktexteditorpluginintegration.cpp b/shell/tests/test_ktexteditorpluginintegration.cpp index f228c310f..00a22f663 100644 --- a/shell/tests/test_ktexteditorpluginintegration.cpp +++ b/shell/tests/test_ktexteditorpluginintegration.cpp @@ -1,198 +1,198 @@ /* Copyright 2015 Milian Wolff 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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, see . */ #include "test_ktexteditorpluginintegration.h" -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; namespace { template QPointer makeQPointer(T *ptr) { return {ptr}; } IToolViewFactory *findToolView(const QString &id) { const auto uiController = Core::self()->uiControllerInternal(); const auto map = uiController->factoryDocuments(); for (auto it = map.begin(); it != map.end(); ++it) { if (it.key()->id() == id) { return it.key(); } } return nullptr; } class TestPlugin : public KTextEditor::Plugin { Q_OBJECT public: explicit TestPlugin(QObject *parent) : Plugin(parent) { } QObject *createView(KTextEditor::MainWindow * mainWindow) override { return new QObject(mainWindow); } }; } void TestKTextEditorPluginIntegration::initTestCase() { QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false\ndefault.debug=true\n")); AutoTestShell::init({QStringLiteral("katesnippetsplugin")}); TestCore::initialize(); QVERIFY(KTextEditor::Editor::instance()); } void TestKTextEditorPluginIntegration::cleanupTestCase() { auto controller = Core::self()->pluginController(); const auto id = QStringLiteral("katesnippetsplugin"); auto plugin = makeQPointer(controller->loadPlugin(id)); const auto editor = makeQPointer(KTextEditor::Editor::instance()); const auto application = makeQPointer(editor->application()); const auto window = makeQPointer(application->activeMainWindow()); TestCore::shutdown(); QVERIFY(!plugin); QVERIFY(!window); QVERIFY(!application); } void TestKTextEditorPluginIntegration::testApplication() { auto app = KTextEditor::Editor::instance()->application(); QVERIFY(app); QVERIFY(app->parent()); QCOMPARE(app->parent()->metaObject()->className(), "KTextEditorIntegration::Application"); QVERIFY(app->activeMainWindow()); QCOMPARE(app->mainWindows().size(), 1); QVERIFY(app->mainWindows().contains(app->activeMainWindow())); } void TestKTextEditorPluginIntegration::testMainWindow() { auto window = KTextEditor::Editor::instance()->application()->activeMainWindow(); QVERIFY(window); QVERIFY(window->parent()); QCOMPARE(window->parent()->metaObject()->className(), "KTextEditorIntegration::MainWindow"); const auto id = QStringLiteral("kte_integration_toolview"); const auto icon = QIcon::fromTheme(QStringLiteral("kdevelop")); const auto text = QStringLiteral("some text"); QVERIFY(!findToolView(id)); auto plugin = new TestPlugin(this); auto toolView = makeQPointer(window->createToolView(plugin, id, KTextEditor::MainWindow::Bottom, icon, text)); QVERIFY(toolView); auto factory = findToolView(id); QVERIFY(factory); // we reuse the same view QWidget parent; auto kdevToolview = makeQPointer(factory->create(&parent)); QCOMPARE(kdevToolview->parentWidget(), &parent); QCOMPARE(toolView->parentWidget(), kdevToolview.data()); // the children are kept alive when the toolview gets destroyed delete kdevToolview; QVERIFY(toolView); kdevToolview = factory->create(&parent); // and we reuse the ktexteditor toolview for the new kdevelop toolview QCOMPARE(toolView->parentWidget(), kdevToolview.data()); delete toolView; delete kdevToolview; delete plugin; QVERIFY(!findToolView(id)); } void TestKTextEditorPluginIntegration::testPlugin() { auto controller = Core::self()->pluginController(); const auto id = QStringLiteral("katesnippetsplugin"); auto plugin = makeQPointer(controller->loadPlugin(id)); if (!plugin) { QSKIP("Cannot continue without katesnippetsplugin, install Kate"); } auto app = KTextEditor::Editor::instance()->application(); auto ktePlugin = makeQPointer(app->plugin(id)); QVERIFY(ktePlugin); auto view = makeQPointer(app->activeMainWindow()->pluginView(id)); QVERIFY(view); const auto rawView = view.data(); QSignalSpy spy(app->activeMainWindow(), &KTextEditor::MainWindow::pluginViewDeleted); QVERIFY(controller->unloadPlugin(id)); QVERIFY(!ktePlugin); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().count(), 2); QCOMPARE(spy.first().at(0), QVariant::fromValue(id)); QCOMPARE(spy.first().at(1), QVariant::fromValue(rawView)); QVERIFY(!view); } void TestKTextEditorPluginIntegration::testPluginUnload() { auto controller = Core::self()->pluginController(); const auto id = QStringLiteral("katesnippetsplugin"); auto plugin = makeQPointer(controller->loadPlugin(id)); if (!plugin) { QSKIP("Cannot continue without katesnippetsplugin, install Kate"); } auto app = KTextEditor::Editor::instance()->application(); auto ktePlugin = makeQPointer(app->plugin(id)); QVERIFY(ktePlugin); delete ktePlugin; // don't crash plugin->unload(); } QTEST_MAIN(TestKTextEditorPluginIntegration); #include \ No newline at end of file diff --git a/shell/tests/test_plugincontroller.cpp b/shell/tests/test_plugincontroller.cpp index e969b2ef9..2a45fa30f 100644 --- a/shell/tests/test_plugincontroller.cpp +++ b/shell/tests/test_plugincontroller.cpp @@ -1,108 +1,108 @@ /*************************************************************************** * Copyright 2008 Andreas Pakulat * * * * 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 "test_plugincontroller.h" #include "nonguiinterfaceplugin.h" #include "testfilepaths.h" -#include -#include +#include +#include #include #include #include "../core.h" #include "../plugincontroller.h" using namespace KDevelop; void TestPluginController::initTestCase() { qApp->addLibraryPath(QStringLiteral(TEST_PLUGIN_DIR)); AutoTestShell::init({QStringLiteral("kdevnonguiinterface")}); TestCore::initialize( Core::NoUi ); m_pluginCtrl = Core::self()->pluginControllerInternal(); } void TestPluginController::cleanupTestCase() { TestCore::shutdown(); } void TestPluginController::init() { } void TestPluginController::cleanup() { } void TestPluginController::pluginInfo() { IPlugin* plugin = m_pluginCtrl->loadPlugin( QStringLiteral("kdevnonguiinterface") ); QVERIFY(plugin); KPluginMetaData pluginInfo = m_pluginCtrl->pluginInfo(plugin); QCOMPARE(pluginInfo.pluginId(), QStringLiteral("kdevnonguiinterface")); } void TestPluginController::loadUnloadPlugin() { QSignalSpy spy(m_pluginCtrl, SIGNAL(pluginLoaded(KDevelop::IPlugin*))); QSignalSpy spyloading(m_pluginCtrl, SIGNAL(loadingPlugin(QString))); QVERIFY(spy.isValid()); QVERIFY(spyloading.isValid()); m_pluginCtrl->loadPlugin( QStringLiteral( "kdevnonguiinterface" ) ); QVERIFY( m_pluginCtrl->plugin( QStringLiteral( "kdevnonguiinterface" ) ) ); QCOMPARE(spy.size(), 1); QCOMPARE(spyloading.size(), 1); QList args = spyloading.takeFirst(); QCOMPARE( args.at(0).toString(), QStringLiteral( "kdevnonguiinterface" ) ); QSignalSpy spy2(m_pluginCtrl, SIGNAL(pluginUnloaded(KDevelop::IPlugin*)) ); QSignalSpy spy3(m_pluginCtrl, SIGNAL(unloadingPlugin(KDevelop::IPlugin*)) ); QVERIFY(spy2.isValid()); QVERIFY(spy3.isValid()); m_pluginCtrl->unloadPlugin( QStringLiteral("kdevnonguiinterface") ); QVERIFY( !m_pluginCtrl->plugin( QStringLiteral( "kdevnonguiinterface" ) ) ); QCOMPARE(spy2.size(), 1); QCOMPARE(spy3.size(), 1); } void TestPluginController::loadFromExtension() { IPlugin* plugin = m_pluginCtrl->pluginForExtension( QStringLiteral("org.kdevelop.ITestNonGuiInterface") ); QVERIFY( plugin ); QVERIFY( plugin->inherits("org.kdevelop.ITestNonGuiInterface") ); QVERIFY( plugin->extension()); } void TestPluginController::benchPluginForExtension() { QBENCHMARK { IPlugin* plugin = m_pluginCtrl->pluginForExtension( QStringLiteral("org.kdevelop.ITestNonGuiInterface") ); QVERIFY( plugin ); } } QTEST_MAIN( TestPluginController) diff --git a/shell/tests/test_plugincontroller.h b/shell/tests/test_plugincontroller.h index 01d798e10..6941fc046 100644 --- a/shell/tests/test_plugincontroller.h +++ b/shell/tests/test_plugincontroller.h @@ -1,49 +1,49 @@ /*************************************************************************** * Copyright 2008 Andreas Pakulat * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_TEST_PLUGINCONTROLLER_H #define KDEVPLATFORM_TEST_PLUGINCONTROLLER_H -#include +#include namespace KDevelop { class Core; class PluginController; } class TestPluginController : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void init(); void cleanup(); void loadUnloadPlugin(); void loadFromExtension(); void pluginInfo(); void benchPluginForExtension(); private: KDevelop::PluginController* m_pluginCtrl; }; #endif // KDEVPLATFORM_TEST_PLUGINCONTROLLER_H diff --git a/shell/tests/test_projectcontroller.cpp b/shell/tests/test_projectcontroller.cpp index bd5bf3489..0dd166efb 100644 --- a/shell/tests/test_projectcontroller.cpp +++ b/shell/tests/test_projectcontroller.cpp @@ -1,577 +1,577 @@ /*************************************************************************** * Copyright 2008 Manuel Breugelmans * * * * 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 "test_projectcontroller.h" #include -#include -#include +#include +#include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; Q_DECLARE_METATYPE(KDevelop::IProject*) namespace { class DialogProviderFake : public IProjectDialogProvider { Q_OBJECT public: DialogProviderFake() : m_reopen(true) {} ~DialogProviderFake() override {} bool m_reopen; public slots: QUrl askProjectConfigLocation(bool /*fetch*/, const QUrl& /*startUrl*/, const QUrl& /*repoUrl*/, IPlugin* /*plugin*/) override { return QUrl(); } bool userWantsReopen() override { return m_reopen; } }; } /*! A Filemanager plugin that allows you to setup a file & directory structure */ class FakeFileManager : public IPlugin, public IProjectFileManager { Q_OBJECT Q_INTERFACES(KDevelop::IProjectFileManager) public: FakeFileManager(QObject*, const QVariantList&) : IPlugin(ICore::self()->aboutData().componentName(), Core::self()) { } FakeFileManager() : IPlugin(ICore::self()->aboutData().componentName(), Core::self()) { } ~FakeFileManager() override {} Features features() const override { return IProjectFileManager::Files | IProjectFileManager::Folders; } QMap m_filesInFolder; // initialize QMap m_subFoldersInFolder; /*! Setup this manager such that @p folder contains @p file */ void addFileToFolder(const Path& folder, const Path& file) { if (!m_filesInFolder.contains(folder)) { m_filesInFolder[folder] = Path::List(); } m_filesInFolder[folder] << file; } /*! Setup this manager such that @p folder has @p subFolder */ void addSubFolderTo(const Path& folder, Path subFolder) { if (!m_subFoldersInFolder.contains(folder)) { m_subFoldersInFolder[folder] = Path::List(); } m_subFoldersInFolder[folder] << subFolder; } QList parse(ProjectFolderItem *dom) override { Path::List files = m_filesInFolder[dom->path()]; foreach (const Path& file, files) { new ProjectFileItem(dom->project(), file, dom); } Path::List folderPaths = m_subFoldersInFolder[dom->path()]; QList folders; foreach (const Path& folderPath, folderPaths) { folders << new ProjectFolderItem(dom->project(), folderPath, dom); } return folders; } ProjectFolderItem *import(IProject *project) override { ProjectFolderItem* it = new ProjectFolderItem(project, project->path()); return it; } ProjectFolderItem* addFolder(const Path& /*folder*/, ProjectFolderItem */*parent*/) override { return nullptr; } ProjectFileItem* addFile(const Path& /*file*/, ProjectFolderItem */*parent*/) override { return nullptr; } bool removeFilesAndFolders(const QList &/*items*/) override { return false; } bool moveFilesAndFolders(const QList< KDevelop::ProjectBaseItem* > &/*items*/, KDevelop::ProjectFolderItem* /*newParent*/) override { return false; } bool copyFilesAndFolders(const Path::List &/*items*/, KDevelop::ProjectFolderItem* /*newParent*/) override { return false; } bool renameFile(ProjectFileItem* /*file*/, const Path& /*newPath*/) override { return false; } bool renameFolder(ProjectFolderItem* /*oldFolder*/, const Path& /*newPath*/ ) override { return false; } bool reload(ProjectFolderItem* /*item*/) override { return false; } }; class FakePluginController : public PluginController { Q_OBJECT public: using PluginController::PluginController; IPlugin* pluginForExtension(const QString& extension, const QString& pluginName = {}, const QVariantMap& constraints = QVariantMap()) override { if (extension == qobject_interface_iid()) { if (!m_fakeFileManager) { // Can't initialize in the constructor, because the pluginController must be setup // before constructing a plugin, and this _is_ the pluginController. m_fakeFileManager = new FakeFileManager; } return m_fakeFileManager; } return PluginController::pluginForExtension(extension, pluginName, constraints); } private: FakeFileManager* m_fakeFileManager = nullptr; }; ////////////////////// Fixture /////////////////////////////////////////////// void TestProjectController::initTestCase() { AutoTestShell::init({{}}); TestCore* testCore = new TestCore; testCore->setPluginController( new FakePluginController(testCore) ); testCore->initialize(); qRegisterMetaType(); m_core = Core::self(); m_scratchDir = QDir(QDir::tempPath()); m_scratchDir.mkdir(QStringLiteral("prjctrltest")); m_scratchDir.cd(QStringLiteral("prjctrltest")); } void TestProjectController::cleanupTestCase() { TestCore::shutdown(); } void TestProjectController::init() { m_projName = QStringLiteral("foo"); m_projFilePath = writeProjectConfig(m_projName); m_projCtrl = m_core->projectControllerInternal(); m_tmpConfigs << m_projFilePath; m_projFolder = Path(m_scratchDir.absolutePath() + '/'); } void TestProjectController::cleanup() { // also close any opened projects as we do not get a clean fixture, // following tests should start off clean. foreach(IProject* p, m_projCtrl->projects()) { m_projCtrl->closeProject(p); } foreach(const Path &cfg, m_tmpConfigs) { QFile::remove(cfg.pathOrUrl()); } qDeleteAll(m_fileManagerGarbage); m_fileManagerGarbage.clear(); } ////////////////////// Commands ////////////////////////////////////////////// #define WAIT_FOR_OPEN_SIGNAL \ {\ QSignalSpy signal(m_projCtrl, SIGNAL(projectOpened(KDevelop::IProject*)));\ QVERIFY2(signal.wait(30000), "Timeout while waiting for opened signal");\ } void(0) void TestProjectController::openProject() { QSignalSpy* spy = createOpenedSpy(); QVERIFY(!m_projCtrl->isProjectNameUsed(m_projName)); m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; QCOMPARE(m_projCtrl->projectCount(), 1); IProject* proj; assertProjectOpened(m_projName, proj);QVERIFY(proj); assertSpyCaughtProject(spy, proj); QCOMPARE(proj->projectFile(), m_projFilePath); QCOMPARE(proj->path(), Path(m_scratchDir.absolutePath()+'/')); QVERIFY(m_projCtrl->isProjectNameUsed(m_projName)); } void TestProjectController::closeProject() { m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; IProject* proj = m_projCtrl->findProjectByName(m_projName); Q_ASSERT(proj); QSignalSpy* spy1 = createClosedSpy(); QSignalSpy* spy2 = createClosingSpy(); m_projCtrl->closeProject(proj); QVERIFY(!m_projCtrl->isProjectNameUsed(m_projName)); QCOMPARE(m_projCtrl->projectCount(), 0); assertProjectClosed(proj); assertSpyCaughtProject(spy1, proj); assertSpyCaughtProject(spy2, proj); } void TestProjectController::openCloseOpen() { m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; IProject* proj; assertProjectOpened(m_projName, proj); m_projCtrl->closeProject(proj); QSignalSpy* spy = createOpenedSpy(); m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; QVERIFY(m_projCtrl->isProjectNameUsed(m_projName)); QCOMPARE(m_projCtrl->projectCount(), 1); assertProjectOpened(m_projName, proj); assertSpyCaughtProject(spy, proj); } void TestProjectController::reopen() { m_projCtrl->setDialogProvider(new DialogProviderFake); m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; QSignalSpy* spy = createOpenedSpy(); m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; QCOMPARE(m_projCtrl->projectCount(), 1); QVERIFY(m_projCtrl->isProjectNameUsed(m_projName)); IProject* proj; assertProjectOpened(m_projName, proj); assertSpyCaughtProject(spy, proj); } void TestProjectController::reopenWhileLoading() { // Open the same project again while the first is still // loading. The second open request should be blocked. m_projCtrl->setDialogProvider(new DialogProviderFake); QSignalSpy* spy = createOpenedSpy(); m_projCtrl->openProject(m_projFilePath.toUrl()); //m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; // wait a bit for a second signal, this should timeout QSignalSpy signal(m_projCtrl, SIGNAL(projectOpened(KDevelop::IProject*))); QVERIFY2(!signal.wait(100), "Received 2 projectOpened signals."); QCOMPARE(m_projCtrl->projectCount(), 1); IProject* proj; assertProjectOpened(m_projName, proj); assertSpyCaughtProject(spy, proj); } void TestProjectController::openMultiple() { QString secondProj(QStringLiteral("bar")); Path secondCfgUrl = writeProjectConfig(secondProj); QSignalSpy* spy = createOpenedSpy(); m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; m_projCtrl->openProject(secondCfgUrl.toUrl()); WAIT_FOR_OPEN_SIGNAL; QCOMPARE(m_projCtrl->projectCount(), 2); IProject *proj1, *proj2; assertProjectOpened(m_projName, proj1); assertProjectOpened(secondProj, proj2); QVERIFY(m_projCtrl->isProjectNameUsed(m_projName)); QVERIFY(m_projCtrl->isProjectNameUsed(QStringLiteral("bar"))); QCOMPARE(spy->size(), 2); IProject* emittedProj1 = (*spy)[0][0].value(); IProject* emittedProj2 = (*spy)[1][0].value(); QCOMPARE(emittedProj1, proj1); QCOMPARE(emittedProj2, proj2); m_tmpConfigs << secondCfgUrl; } /*! Verify that the projectmodel contains a single project. Put this project's * ProjectFolderItem in the output parameter @p RootItem */ #define ASSERT_SINGLE_PROJECT_IN_MODEL(rootItem) \ {\ QCOMPARE(m_projCtrl->projectModel()->rowCount(), 1); \ QModelIndex projIndex = m_projCtrl->projectModel()->index(0,0); \ QVERIFY(projIndex.isValid()); \ ProjectBaseItem* i = m_projCtrl->projectModel()->itemFromIndex( projIndex ); \ QVERIFY(i); \ QVERIFY(i->folder()); \ rootItem = i->folder();\ } void(0) /*! Verify that the projectitem @p item has a single child item * named @p name with url @p url. @p subFolder is an output parameter * that contains the sub-folder projectitem. */ #define ASSERT_SINGLE_SUBFOLDER_IN(item, name, path__, subFolder) \ {\ QCOMPARE(item->rowCount(), 1);\ QCOMPARE(item->folderList().size(), 1);\ ProjectFolderItem* fo = item->folderList().at(0);\ QVERIFY(fo);\ QCOMPARE(fo->path(), path__);\ QCOMPARE(fo->folderName(), QStringLiteral(name));\ subFolder = fo;\ } void(0) #define ASSERT_SINGLE_FILE_IN(rootFolder, name, path__, fileItem)\ {\ QCOMPARE(rootFolder->rowCount(), 1);\ QCOMPARE(rootFolder->fileList().size(), 1);\ fileItem = rootFolder->fileList().at(0);\ QVERIFY(fileItem);\ QCOMPARE(fileItem->path(), path__);\ QCOMPARE(fileItem->fileName(), QStringLiteral(name));\ } void(0) // command void TestProjectController::emptyProject() { // verify that the project model contains a single top-level folder after loading // an empty project assertEmptyProjectModel(); m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; Project* proj; assertProjectOpened(m_projName, (KDevelop::IProject*&)proj); FakeFileManager* fileMng = createFileManager(); Q_ASSERT(fileMng); proj->setManagerPlugin(fileMng); proj->reloadModel(); QTest::qWait(100); ProjectFolderItem* rootFolder; ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder); // check that the project is empty QCOMPARE(rootFolder->rowCount(), 0); QCOMPARE(rootFolder->project()->name(), m_projName); QCOMPARE(rootFolder->path(), m_projFolder); } // command void TestProjectController::singleFile() { // verify that the project model contains a single file in the // top folder. First setup a FakeFileManager with this file m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; Project* proj; assertProjectOpened(m_projName, (KDevelop::IProject*&)proj); FakeFileManager* fileMng = createFileManager(); proj->setManagerPlugin(fileMng); Path filePath = Path(m_projFolder, QStringLiteral("foobar")); fileMng->addFileToFolder(m_projFolder, filePath); proj->reloadModel(); QTest::qWait(100); // NO signals for reload ... ProjectFolderItem* rootFolder; ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder); ProjectFileItem* fi; ASSERT_SINGLE_FILE_IN(rootFolder, "foobar", filePath, fi); QCOMPARE(fi->rowCount(), 0); ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder); ASSERT_SINGLE_FILE_IN(rootFolder, "foobar", filePath, fi); } // command void TestProjectController::singleDirectory() { // verify that the project model contains a single folder in the // top folder. First setup a FakeFileManager with this folder m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; Project* proj; assertProjectOpened(m_projName, (KDevelop::IProject*&)proj); Path folderPath = Path(m_projFolder, QStringLiteral("foobar/")); FakeFileManager* fileMng = createFileManager(); fileMng->addSubFolderTo(m_projFolder, folderPath); proj->setManagerPlugin(fileMng); proj->reloadModel(); QTest::qWait(100); ProjectFolderItem* rootFolder; ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder); // check that the project contains a single subfolder ProjectFolderItem* sub; ASSERT_SINGLE_SUBFOLDER_IN(rootFolder, "foobar", folderPath, sub); QCOMPARE(sub->rowCount(), 0); } // command void TestProjectController::fileInSubdirectory() { // verify that the project model contains a single file in a subfolder // First setup a FakeFileManager with this folder + file m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; Project* proj; assertProjectOpened(m_projName, (KDevelop::IProject*&)proj); Path folderPath = Path(m_projFolder, QStringLiteral("foobar/")); FakeFileManager* fileMng = createFileManager(); fileMng->addSubFolderTo(m_projFolder, folderPath); Path filePath = Path(folderPath, QStringLiteral("zoo")); fileMng->addFileToFolder(folderPath, filePath); proj->setManagerPlugin(fileMng); ProjectFolderItem* rootFolder = nullptr; ProjectFolderItem* sub = nullptr; ProjectFileItem* file = nullptr; proj->reloadModel(); QTest::qWait(100); ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder); ASSERT_SINGLE_SUBFOLDER_IN(rootFolder, "foobar", folderPath, sub); ASSERT_SINGLE_FILE_IN(sub,"zoo",filePath,file); ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder); ASSERT_SINGLE_SUBFOLDER_IN(rootFolder, "foobar", folderPath, sub); ASSERT_SINGLE_FILE_IN(sub,"zoo",filePath,file); } void TestProjectController::prettyFileName_data() { QTest::addColumn("relativeFilePath"); QTest::newRow("basic") << "foobar.txt"; QTest::newRow("subfolder") << "sub/foobar.txt"; } void TestProjectController::prettyFileName() { QFETCH(QString, relativeFilePath); m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; Project* proj; assertProjectOpened(m_projName, (KDevelop::IProject*&)proj); FakeFileManager* fileMng = createFileManager(); proj->setManagerPlugin(fileMng); Path filePath = Path(m_projFolder, relativeFilePath); fileMng->addFileToFolder(m_projFolder, filePath); QCOMPARE(m_projCtrl->prettyFileName(filePath.toUrl(), ProjectController::FormattingOptions::FormatPlain), QString(m_projName + ':' + relativeFilePath)); } ////////////////////// Helpers /////////////////////////////////////////////// Path TestProjectController::writeProjectConfig(const QString& name) { Path configPath = Path(m_scratchDir.absolutePath() + '/' + name + ".kdev4"); QFile f(configPath.pathOrUrl()); f.open(QIODevice::WriteOnly); QTextStream str(&f); str << "[Project]\n" << "Name=" << name << "\n"; f.close(); return configPath; } ////////////////// Custom assertions ///////////////////////////////////////// void TestProjectController::assertProjectOpened(const QString& name, IProject*& proj) { QVERIFY(proj = m_projCtrl->findProjectByName(name)); QVERIFY(m_projCtrl->projects().contains(proj)); } void TestProjectController::assertSpyCaughtProject(QSignalSpy* spy, IProject* proj) { QCOMPARE(spy->size(), 1); IProject* emittedProj = (*spy)[0][0].value(); QCOMPARE(proj, emittedProj); } void TestProjectController::assertProjectClosed(IProject* proj) { IProject* p = m_projCtrl->findProjectByName(proj->name()); QVERIFY(p == nullptr); QVERIFY(!m_projCtrl->projects().contains(proj)); } void TestProjectController::assertEmptyProjectModel() { ProjectModel* m = m_projCtrl->projectModel(); Q_ASSERT(m); QCOMPARE(m->rowCount(), 0); } ///////////////////// Creation stuff ///////////////////////////////////////// QSignalSpy* TestProjectController::createOpenedSpy() { return new QSignalSpy(m_projCtrl, SIGNAL(projectOpened(KDevelop::IProject*))); } QSignalSpy* TestProjectController::createClosedSpy() { return new QSignalSpy(m_projCtrl, SIGNAL(projectClosed(KDevelop::IProject*))); } QSignalSpy* TestProjectController::createClosingSpy() { return new QSignalSpy(m_projCtrl, SIGNAL(projectClosing(KDevelop::IProject*))); } FakeFileManager* TestProjectController::createFileManager() { FakeFileManager* fileMng = new FakeFileManager; m_fileManagerGarbage << fileMng; return fileMng; } QTEST_MAIN(TestProjectController) #include "moc_test_projectcontroller.cpp" #include "test_projectcontroller.moc" diff --git a/shell/tests/test_projectcontroller.h b/shell/tests/test_projectcontroller.h index 00b768eb5..36c34fa84 100644 --- a/shell/tests/test_projectcontroller.h +++ b/shell/tests/test_projectcontroller.h @@ -1,87 +1,87 @@ /*************************************************************************** * Copyright 2008 Manuel Breugelmans * * * * 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. * ***************************************************************************/ #ifndef KDEVELOP_SHELL_TEST_PROJECTCONTROLLER_H #define KDEVELOP_SHELL_TEST_PROJECTCONTROLLER_H -#include -#include +#include +#include #include "util/path.h" class QSignalSpy; namespace KDevelop { class Core; class IProject; class ProjectController; } class FakeFileManager; class TestProjectController : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void init(); void cleanup(); void openProject(); void closeProject(); void openCloseOpen(); void openMultiple(); void reopen(); void reopenWhileLoading(); void emptyProject(); void singleFile(); void singleDirectory(); void fileInSubdirectory(); void prettyFileName_data(); void prettyFileName(); private: KDevelop::Path writeProjectConfig(const QString& name); QSignalSpy* createOpenedSpy(); QSignalSpy* createClosedSpy(); QSignalSpy* createClosingSpy(); void assertProjectOpened(const QString& name, KDevelop::IProject*& proj); void assertSpyCaughtProject(QSignalSpy* spy, KDevelop::IProject* proj); void assertProjectClosed(KDevelop::IProject* proj); void assertEmptyProjectModel(); FakeFileManager* createFileManager(); private: KDevelop::Path m_projFilePath; KDevelop::Core* m_core; KDevelop::ProjectController* m_projCtrl; QString m_projName; QList m_tmpConfigs; QDir m_scratchDir; KDevelop::Path m_projFolder; QList m_fileManagerGarbage; }; #endif // KDEVELOP_SHELL_TEST_PROJECTCONTROLLER_H diff --git a/shell/tests/test_sessioncontroller.cpp b/shell/tests/test_sessioncontroller.cpp index 5761be7e0..942d1d61d 100644 --- a/shell/tests/test_sessioncontroller.cpp +++ b/shell/tests/test_sessioncontroller.cpp @@ -1,270 +1,270 @@ /*************************************************************************** * Copyright 2008 Andreas Pakulat * * * * 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 "test_sessioncontroller.h" #include #include #include #include #include "../core.h" #include "../sessioncontroller.h" #include "../session.h" #include #include -#include -#include +#include +#include using namespace KDevelop; //////////////////// Helper Functions //////////////////////////////////////// QString sessionDir( ISession* s ) { return SessionController::sessionDirectory(s->id().toString()); } void verifySessionDir( const QString& sessiondir, const QString& name, bool exists ) { if( exists ) { qDebug() << "checking existing session" << sessiondir; QVERIFY( QFileInfo::exists( sessiondir ) ); QVERIFY( QFileInfo( sessiondir ).isDir() ); QVERIFY( QFileInfo::exists( sessiondir+"/sessionrc" ) ); KSharedConfigPtr cfg = KSharedConfig::openConfig( sessiondir+"/sessionrc" ); QCOMPARE( name, cfg->group("").readEntry( Session::cfgSessionNameEntry, "" ) ); } else { qDebug() << "checking not-existing dir: " << sessiondir; QVERIFY( !QFileInfo::exists( sessiondir ) ); } } void verifySessionDir( ISession* s, bool exists = true ) { verifySessionDir(sessionDir(s), s->name(), exists); } ////////////////////// Fixture /////////////////////////////////////////////// void TestSessionController::initTestCase() { AutoTestShell::init(); TestCore::initialize(Core::NoUi); m_core = Core::self(); qRegisterMetaType(); qRegisterMetaType(); } void TestSessionController::init() { m_sessionCtrl = m_core->sessionController(); } void TestSessionController::cleanupTestCase() { foreach( const Session* session, m_sessionCtrl->sessions() ) { TryLockSessionResult lock = m_sessionCtrl->tryLockSession(session->id().toString()); if (lock.lock) m_sessionCtrl->deleteSession( lock.lock ); } TestCore::shutdown(); } void TestSessionController::createSession_data() { QTest::addColumn( "sessionName" ); QTest::newRow("SimpleName") << "TestSession"; QTest::newRow("NonLetterChars") << "Test%$Session"; QTest::newRow("NonAsciiChars") << QStringLiteral("TöstSession"); } void TestSessionController::createSession() { QFETCH(QString, sessionName); int sessionCount = m_sessionCtrl->sessionNames().count(); Session* s = m_sessionCtrl->createSession( sessionName ); QVERIFY( m_sessionCtrl->sessionNames().contains( sessionName ) ); QCOMPARE( sessionCount+1, m_sessionCtrl->sessionNames().count() ); verifySessionDir( s ); } void TestSessionController::renameSession() { const QString sessionName = QStringLiteral("TestSession4"); const QString newSessionName = QStringLiteral("TestOtherSession4"); KDevelop::Session *s = m_sessionCtrl->createSession( sessionName ); QCOMPARE( sessionName, s->name() ); verifySessionDir( s ); QSignalSpy spy(s, SIGNAL(sessionUpdated(KDevelop::ISession*))); s->setName( newSessionName ); QCOMPARE( newSessionName, s->name() ); QCOMPARE( spy.size(), 1 ); verifySessionDir( s ); } void TestSessionController::canRenameActiveSession() { const QString sessionName = QStringLiteral("TestSession5"); const QString newSessionName = QStringLiteral("TestOtherSession5"); KDevelop::Session *s = m_sessionCtrl->createSession( sessionName ); QCOMPARE( sessionName, s->name() ); m_sessionCtrl->loadSession( sessionName ); QSignalSpy spy(s, SIGNAL(sessionUpdated(KDevelop::ISession*))); s->setName( newSessionName ); QCOMPARE( newSessionName, s->name() ); QCOMPARE( spy.size(), 1 ); verifySessionDir( s ); } void TestSessionController::deleteSession() { const QString sessionName = QStringLiteral("TestSession3"); int sessionCount = m_sessionCtrl->sessionNames().count(); QPointer s = m_sessionCtrl->createSession( sessionName ); QString sessionId = s->id().toString(); QCOMPARE( sessionCount+1, m_sessionCtrl->sessionNames().count() ); verifySessionDir( s.data() ); const QString sessionDir = ::sessionDir(s); QSignalSpy spy(m_sessionCtrl, SIGNAL(sessionDeleted(QString))); { TryLockSessionResult lock = m_sessionCtrl->tryLockSession(sessionId); QVERIFY(lock.lock); m_sessionCtrl->deleteSession( lock.lock ); } QCOMPARE( sessionCount, m_sessionCtrl->sessionNames().count() ); QVERIFY( !m_sessionCtrl->sessionNames().contains(sessionId) ); QCOMPARE(spy.size(), 1); QList arguments = spy.takeFirst(); QString emittedSession = arguments.at(0).toString(); QCOMPARE( sessionId, emittedSession ); verifySessionDir( sessionDir, sessionName, false ); } void TestSessionController::cloneSession() { QString sessionName = QStringLiteral("CloneableSession"); QString testgrp = QStringLiteral("TestGroup"); QString testentry = QStringLiteral("TestEntry"); QString testval = QStringLiteral("TestValue"); int sessionCount = m_sessionCtrl->sessionNames().count(); m_sessionCtrl->createSession( sessionName ); Session* s = m_sessionCtrl->session( sessionName ); s->config()->group( testgrp ).writeEntry( testentry, testval ); s->config()->sync(); QCOMPARE( sessionCount+1, m_sessionCtrl->sessionNames().count() ); QVERIFY( m_sessionCtrl->session( sessionName ) ); QString newSession = m_sessionCtrl->cloneSession( sessionName ); QVERIFY( m_sessionCtrl->session( newSession ) ); QCOMPARE( sessionCount+2, m_sessionCtrl->sessionNames().count() ); Session* news = m_sessionCtrl->session( newSession ); QCOMPARE( testval, news->config()->group( testgrp ).readEntry( testentry, "" ) ); QCOMPARE( i18n( "Copy of %1", sessionName ), news->name() ); verifySessionDir( news ); } void TestSessionController::readFromConfig() { ISession* s = Core::self()->activeSession(); KConfigGroup grp( s->config(), "TestGroup" ); grp.writeEntry( "TestEntry", "Test1" ); KConfigGroup grp2( s->config(), "TestGroup" ); QCOMPARE(grp.readEntry( "TestEntry", "" ), QStringLiteral( "Test1" ) ); } void TestSessionController::temporary() { ISession* s = Core::self()->activeSession(); s->setTemporary(true); const QString oldName = s->name(); const QString dir = sessionDir(s); verifySessionDir(s, true); Core::self()->sessionController()->cleanup(); verifySessionDir(dir, oldName, false); Core::self()->sessionController()->initialize(oldName); QCOMPARE(Core::self()->activeSession()->name(), oldName); // dir / UID can be different, hence don't verifySessionDir } void TestSessionController::tryLockSession() { const QString id1 = QUuid::createUuid().toString(); m_sessionCtrl->createSession( id1 )->setTemporary(true); const QString id2 = QUuid::createUuid().toString(); m_sessionCtrl->createSession( id2 )->setTemporary(true); { // acquired scoped lock QVERIFY(!SessionController::isSessionRunning(id1)); QCOMPARE(SessionController::sessionRunInfo(id1), SessionRunInfo()); TryLockSessionResult initial = SessionController::tryLockSession(id1); QVERIFY(initial.lock); QCOMPARE(initial.lock->id(), id1); QCOMPARE(initial.runInfo, SessionRunInfo()); QVERIFY(SessionController::isSessionRunning(id1)); SessionRunInfo info = SessionController::sessionRunInfo(id1); QVERIFY(info != initial.runInfo); QVERIFY(info.isRunning); QCOMPARE(info.holderApp, QCoreApplication::applicationName()); QCOMPARE(info.holderPid, static_cast(QCoreApplication::applicationPid())); // this should fail TryLockSessionResult repeated = SessionController::tryLockSession(id1); QVERIFY(!repeated.lock); QCOMPARE(repeated.runInfo, info); // this should pass (different id) QVERIFY(!SessionController::isSessionRunning(id2)); TryLockSessionResult other = SessionController::tryLockSession(id2); QVERIFY(other.lock); QCOMPARE(other.lock->id(), id2); QCOMPARE(other.runInfo, SessionRunInfo()); QVERIFY(SessionController::isSessionRunning(id2)); } // scope left, sessions are now unlocked again QVERIFY(!SessionController::isSessionRunning(id1)); QCOMPARE(SessionController::sessionRunInfo(id1), SessionRunInfo()); QVERIFY(!SessionController::isSessionRunning(id2)); QCOMPARE(SessionController::sessionRunInfo(id2), SessionRunInfo()); // can re-lock it here TryLockSessionResult final = SessionController::tryLockSession(id1); QVERIFY(SessionController::isSessionRunning(id1)); QVERIFY(final.lock); QCOMPARE(final.lock->id(), id1); QCOMPARE(final.runInfo, SessionRunInfo()); } QTEST_GUILESS_MAIN(TestSessionController) diff --git a/shell/tests/test_sessioncontroller.h b/shell/tests/test_sessioncontroller.h index 263c69a99..2b1388ea5 100644 --- a/shell/tests/test_sessioncontroller.h +++ b/shell/tests/test_sessioncontroller.h @@ -1,54 +1,54 @@ /*************************************************************************** * Copyright 2008 Andreas Pakulat * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_TEST_SESSIONCONTROLLER_H #define KDEVPLATFORM_TEST_SESSIONCONTROLLER_H -#include +#include namespace KDevelop { class Core; class Session; class SessionController; } class TestSessionController : public QObject { Q_OBJECT private slots: void initTestCase(); void init(); void cleanupTestCase(); void createSession_data(); void createSession(); void deleteSession(); void cloneSession(); void renameSession(); void canRenameActiveSession(); void readFromConfig(); void temporary(); void tryLockSession(); private: KDevelop::Core* m_core; KDevelop::SessionController* m_sessionCtrl; }; #endif // KDEVPLATFORM_TEST_SESSIONCONTROLLER_H diff --git a/shell/tests/test_testcontroller.cpp b/shell/tests/test_testcontroller.cpp index cc7c3e286..c1e5cfbb2 100644 --- a/shell/tests/test_testcontroller.cpp +++ b/shell/tests/test_testcontroller.cpp @@ -1,243 +1,243 @@ /* Unit tests for TestController. Copyright 2012 Miha Čančula 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 "test_testcontroller.h" #include -#include +#include #include #include #include #include #include #include #include using namespace KDevelop; const char* TestSuiteName = "TestTestSuite"; const char* TestSuiteNameTwo = "TestTestSuiteTwo"; const char* TestCaseNameOne = "TestTestCaseOne"; const char* TestCaseNameTwo = "TestTestCaseTwo"; Q_DECLARE_METATYPE(KDevelop::TestResult) Q_DECLARE_METATYPE(KDevelop::ITestSuite*) class FakeTestSuite : public KDevelop::ITestSuite { public: FakeTestSuite(const QString& name, IProject* project, const QStringList& cases = QStringList()) : m_name(name), m_project(project), m_cases(cases) {} ~FakeTestSuite() override {} IProject* project() const override {return m_project;} QString name() const override {return m_name;} QStringList cases() const override {return m_cases;} IndexedDeclaration declaration() const override; IndexedDeclaration caseDeclaration(const QString& testCase) const override; KJob* launchAllCases(TestJobVerbosity verbosity) override; KJob* launchCase(const QString& testCase, TestJobVerbosity verbosity) override; KJob* launchCases(const QStringList& testCases, TestJobVerbosity verbosity) override; private: QString m_name; IProject* m_project; QStringList m_cases; }; IndexedDeclaration FakeTestSuite::declaration() const { return IndexedDeclaration(); } IndexedDeclaration FakeTestSuite::caseDeclaration(const QString& testCase) const { Q_UNUSED(testCase); return IndexedDeclaration(); } KJob* FakeTestSuite::launchAllCases(ITestSuite::TestJobVerbosity verbosity) { Q_UNUSED(verbosity); return nullptr; } KJob* FakeTestSuite::launchCase(const QString& testCase, ITestSuite::TestJobVerbosity verbosity) { Q_UNUSED(testCase); Q_UNUSED(verbosity); return nullptr; } KJob* FakeTestSuite::launchCases(const QStringList& testCases, ITestSuite::TestJobVerbosity verbosity) { Q_UNUSED(testCases); Q_UNUSED(verbosity); return nullptr; } void TestTestController::emitTestResult(ITestSuite* suite, TestResult::TestCaseResult caseResult) { TestResult result; result.suiteResult = caseResult; foreach (const QString& testCase, suite->cases()) { result.testCaseResults.insert(testCase, caseResult); } m_testController->notifyTestRunFinished(suite, result); } void TestTestController::initTestCase() { AutoTestShell::init(); TestCore::initialize( Core::NoUi ); m_testController = Core::self()->testControllerInternal(); m_project = new TestProject(Path(), this); qRegisterMetaType("KDevelop::ITestSuite*"); qRegisterMetaType("KDevelop::TestResult"); } void TestTestController::cleanupTestCase() { delete m_project; TestCore::shutdown(); } void TestTestController::addSuite() { FakeTestSuite suite(TestSuiteName, m_project); m_testController->addTestSuite(&suite); ITestSuite* found = m_testController->findTestSuite(m_project, TestSuiteName); QVERIFY(found); QCOMPARE(found->name(), QString(TestSuiteName)); QCOMPARE(found->project(), m_project); m_testController->removeTestSuite(&suite); } void TestTestController::removeSuite() { FakeTestSuite suite(TestSuiteName, m_project); m_testController->addTestSuite(&suite); QVERIFY(m_testController->findTestSuite(m_project, TestSuiteName)); m_testController->removeTestSuite(&suite); QCOMPARE(m_testController->findTestSuite(m_project, TestSuiteName), (ITestSuite*)nullptr); QVERIFY(m_testController->testSuites().isEmpty()); } void TestTestController::replaceSuite() { FakeTestSuite* suiteOne = new FakeTestSuite(TestSuiteName, m_project, QStringList() << TestCaseNameOne); m_testController->addTestSuite(suiteOne); QCOMPARE(m_testController->findTestSuite(m_project, TestSuiteName)->name(), QString(TestSuiteName)); QCOMPARE(m_testController->findTestSuite(m_project, TestSuiteName)->cases().size(), 1); FakeTestSuite* suiteTwo = new FakeTestSuite(TestSuiteName, m_project, QStringList() << TestCaseNameOne << TestCaseNameTwo); m_testController->addTestSuite(suiteTwo); QCOMPARE(m_testController->testSuites().size(), 1); QCOMPARE(m_testController->findTestSuite(m_project, TestSuiteName)->name(), QString(TestSuiteName)); QCOMPARE(m_testController->findTestSuite(m_project, TestSuiteName)->cases().size(), 2); // TestController deletes the old suite when replacing it, so make sure we don't delete suiteOne manually m_testController->removeTestSuite(suiteTwo); delete suiteTwo; } void TestTestController::findByProject() { IProject* otherProject = new TestProject(Path(), this); ITestSuite* suiteOne = new FakeTestSuite(TestSuiteName, m_project); ITestSuite* suiteTwo = new FakeTestSuite(TestSuiteName, otherProject); m_testController->addTestSuite(suiteOne); m_testController->addTestSuite(suiteTwo); QCOMPARE(m_testController->testSuites().size(), 2); QCOMPARE(m_testController->testSuitesForProject(m_project).size(), 1); QCOMPARE(m_testController->testSuitesForProject(m_project).at(0), suiteOne); m_testController->removeTestSuite(suiteOne); m_testController->removeTestSuite(suiteTwo); delete suiteOne; delete suiteTwo; delete otherProject; } void TestTestController::testResults() { ITestSuite* suite = new FakeTestSuite(TestSuiteName, m_project); m_testController->addTestSuite(suite); QSignalSpy spy(m_testController, SIGNAL(testRunFinished(KDevelop::ITestSuite*,KDevelop::TestResult))); QVERIFY(spy.isValid()); QList results; results << TestResult::Passed << TestResult::Failed << TestResult::Error << TestResult::Skipped << TestResult::NotRun; foreach (const TestResult::TestCaseResult result, results) { emitTestResult(suite, result); QCOMPARE(spy.size(), 1); QVariantList arguments = spy.takeFirst(); QCOMPARE(arguments.size(), 2); QVERIFY(arguments.first().canConvert()); QCOMPARE(arguments.first().value(), suite); QVERIFY(arguments.at(1).canConvert()); QCOMPARE(arguments.at(1).value().suiteResult, result); foreach (const QString& testCase, suite->cases()) { QCOMPARE(arguments.at(1).value().testCaseResults[testCase], result); } } QCOMPARE(spy.size(), 0); ITestSuite* suiteTwo = new FakeTestSuite(TestSuiteNameTwo, m_project); m_testController->addTestSuite(suiteTwo); // Verify that only one signal gets emitted even with more suites present emitTestResult(suiteTwo, TestResult::Passed); QCOMPARE(spy.size(), 1); m_testController->removeTestSuite(suite); m_testController->removeTestSuite(suiteTwo); delete suite; delete suiteTwo; } QTEST_GUILESS_MAIN(TestTestController) diff --git a/sublime/aggregatemodel.h b/sublime/aggregatemodel.h index cc5d4edce..0bbe492cd 100644 --- a/sublime/aggregatemodel.h +++ b/sublime/aggregatemodel.h @@ -1,97 +1,97 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_SUBLIMEAGGREGATEMODEL_H #define KDEVPLATFORM_SUBLIMEAGGREGATEMODEL_H -#include +#include #include "sublimeexport.h" class QStandardItemModel; namespace Sublime { /** @short A model to combine several QStandardItemModel's into one. Combine standard models into the aggregate model to display them in the one view. Each new model gets its own parent item to differentiate items between different models, for example: Tea Model: @code - Black - Green - White @endcode Coffee Model: @code - Arabica - Robusta @endcode When aggregated with @code AggregateModel model; model->addModel("Tea", teaModel); model->addModel("Coffee", coffeeModel); @endcode they will look as: @code - Tea - Black - Green - White - Coffee - Arabica - Robusta @endcode @note It is impossible to aggregate any model, aggregation works only for standard models. @note Currently aggregate model displays only 1 column. */ class KDEVPLATFORMSUBLIME_EXPORT AggregateModel: public QAbstractItemModel { Q_OBJECT public: explicit AggregateModel(QObject *parent = nullptr); ~AggregateModel() override; /**Adds the model and creates a parent item with given @p name in the aggregated model.*/ void addModel(const QString &name, QStandardItemModel *model); /**Removes the model from aggregation.*/ void removeModel(QStandardItemModel *model); //reimplemented methods from QAbstractItemModel Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QModelIndex parent(const QModelIndex &index) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; private: struct AggregateModelPrivate *d; }; } #endif diff --git a/sublime/area.h b/sublime/area.h index 2544c356d..83eb05c23 100644 --- a/sublime/area.h +++ b/sublime/area.h @@ -1,287 +1,287 @@ /*************************************************************************** * Copyright 2006-2007 Alexander Dymo * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_SUBLIMEAREA_H #define KDEVPLATFORM_SUBLIMEAREA_H -#include +#include #include "sublimeexport.h" #include "areaindex.h" #include "sublimedefs.h" #include class QAction; namespace Sublime { class AreaIndex; class RootAreaIndex; class Controller; class View; /** @short Area - the universal view container Area contains views and toolviews, knows about their positions and provides functionality to add new (tool)views and remove existing. Area takes care of all placement/configuration details so that in order for @ref MainWindow to show the area it just needs to reconstruct itself according to the area's rules. Usual way of creating an area is: @code Controller *controller = new Controller(); ... //document creation code here Area *area = new Area(controller, "My Area"); area->addView(document->createView()); MainWindow *mw = new MainWindow(controller); controller->show(area, mw); @endcode */ class KDEVPLATFORMSUBLIME_EXPORT Area: public QObject { Q_OBJECT public: /**Creates and area with given @p name and adds it to the @p controller. @param controller is the controller in which this area will be available. @param name should identify this area and be unique for all areas in the controller. @ref QObject::objectName shall be used to get this name after area creation. @param title is the user-visible (translatable) title for the area. Use @ref title and @ref setTitle to operate on the title. This parameter can be omitted and then name will be used as title.*/ Area(Controller *controller, const QString &name, const QString &title = {}); Area(const Area &area); ~Area() override; QString title() const; void setTitle(const QString &title); QString iconName() const; void setIconName(const QString &iconName); /**Adds the @p view to the list of views at the given area index, after the given view @p after. If @p after == 0 && controller()->openAfterCurrent(): @p view is inserted after current view If @p after == 0 && !controller()->openAfterCurrent(): @p view is inserted at the last position. */ void addView(View *view, AreaIndex *index, View *after = nullptr); /**Adds the @p view to the list of views at the current area index.*/ void addView(View *view, View *after = nullptr); /**Adds the @p view to the area splitting the @p viewToSplit using given @p orientation. @p view Will be in the second child index of the area-index containing the view. */ void addView(View *view, View *viewToSplit, Qt::Orientation orientation); /**Adds the @p view to the area splitting the area index @p indexToSplit using given @p orientation. @p view Will be in the second child index of the area-index containing the view. */ void addView(View *view, AreaIndex *indexToSplit, Qt::Orientation orientation); /**Removes the @p view from the area. Does not delete it. */ View* removeView(View *view); /**@return the list of all views in this area in no particular order. To process the views in ordered manner (tree walk) use @ref walkViews method. This method is added only for convenience.*/ QList views(); /** Removes all views from this area and deletes them. * If an open document has changes, and it is the last view of that document, * the user may push 'Cancel', and the view will stay active. * @param silent If this is true, the user is never asked for feedback. */ void clearViews(bool silent = false); /** * Returns the view that was last stored through setActiveView(view), or zero * if the view was deleted or it was never set. */ View* activeView(); /** * Allows marking a view as active that can later be retrieved through activeView() */ void setActiveView(View* view); /** Closes and deletes the view, asking the user for feedback if needed. * Closes the document if it is the last view. * Does allow breaking the closing process. * If it is the last view of the document that has changes, and the user pushed 'Cancel', * false will be returned, and the view will not be closed. * @param silent If this is false, the user will be asked for feedback. Otherwise he won't. */ bool closeView(View* view, bool silent = false); /**@return the index of view or 0 if it can not be found.*/ AreaIndex *indexOf(View *view); /**@return the root index of the area. Root index always exists so this method will never return 0.*/ RootAreaIndex *rootIndex() const; /**Adds the toolview to the area. Area will use its configuration and restore the proper position for the toolview when necessary. If it has no configuration for this view, it will use @p defaultPosition.*/ void addToolView(View *toolView, Position defaultPosition); /**Removes the toolview from the area.*/ View* removeToolView(View *toolView); /**Moves the toolview to a different position. */ void moveToolView(View *toolView, Position newPosition); /**Raise tool view.*/ void raiseToolView(View *toolView); /**@return the list of toolviews in the area. No particular sort order is guaranteed.*/ QList &toolViews() const; /**@return the current position of @p toolView in the area.*/ Position toolViewPosition(View *toolView) const; /* Returns true if this area actively desires to show a tool view with id of 'id'. The area, of course, will show any tool view added with 'addToolView', however, this method can be used to guess a set of tool views that make most sense to be added. */ bool wantToolView(const QString& id); void setShownToolViews(Sublime::Position pos, const QStringList& ids); QStringList shownToolViews(Sublime::Position pos) const; void setDesiredToolViews( const QMap& desiredToolViews); void save(KConfigGroup& group) const; void load(const KConfigGroup& group); /**@return the controller for this area.*/ Controller *controller() const; ///Returns the currently set working-set for this area. The working-set is persistent QString workingSet() const; ///Sets the working-set for this area. The working-set is just a marker, and does nothing ///within Area. ///The actual view management has to be implemented in the entity that knows more ///about possible views, documents, etc. (kdevplatform/shell) ///@warning (KDevelop): Before calling this, make sure that all views are saved! (see IDocumentController::saveAllDocumentsForWindow) void setWorkingSet(QString name); /**Walker mode to determine the behavior of area walkers.*/ enum WalkerMode { StopWalker, /**< Stop after processing this area index or toolview */ ContinueWalker /**< Continue walking */ }; /**Walks the tree of area indices and executes the operator. It will always walk the tree of views from top to bottom from left to right. Operator should be the class with WalkerResult operator()(AreaIndex *index) method. That method should return Area::StopWalker if the walker has to stop at current index or Area::ContinueWalker to continue. Example (operator to print the indices, assumes hypothetical operator <<()): @code struct MyOperator { WalkerMode operator()(AreaIndex *index) { std::cerr << index << std::endl; return Area::ContinueWalker; } }; ... MyOperator op; walkViews(op, rootIndex()) @endcode*/ template void walkViews(Operator &op, AreaIndex *index); /**Walks the list of toolviews. The order in which toolviews are walked is not specified. Operator should be the class with bool operator()(View *view, Sublime::Position position) method. That method should return Area::StopWalker if the walker has to stop at current index or Area::ContinueWalker to continue. Example (operator to print the list of views): @code struct MyOperator { WalkerMode operator()(View *view, Sublime::Position position) { std::cerr << view << " at position " << position << std::endl; return Area::ContinueWalker; } }; ... MyOperator op; walkToolViews(op, Sublime::AllPositions) @endcode */ template void walkToolViews(Operator &op, Positions positions); /** Adds an action to the area. They will be made available from different places, like the Area Display*/ void addAction(QAction* action); /** @returns the actions related to the area */ QList actions() const; /** * Closes all the views and requests the working set to be cleared. * Works even though the area isn't opened yet */ void clearDocuments(); Q_SIGNALS: /**Emitted when a new view is added to the area.*/ void viewAdded(Sublime::AreaIndex*, Sublime::View*); /**Emitted when a view is going to be removed from the area.*/ void aboutToRemoveView(Sublime::AreaIndex*, Sublime::View*); /**Emitted when a view was removed from the area.*/ void viewRemoved(Sublime::AreaIndex*, Sublime::View*); /**Emitted when a new toolview is added to the area.*/ void toolViewAdded(Sublime::View*, Sublime::Position); /**Emitted when a toolview is requesting to be raised.*/ void requestToolViewRaise(Sublime::View*); /**Emitted when a toolview is going to be removed from the area.*/ void aboutToRemoveToolView(Sublime::View*, Sublime::Position); /**Emitted when a toolview is moved to a different position.*/ void toolViewMoved(Sublime::View*, Sublime::Position); /**Emitted before the working-set is changed.*/ void changingWorkingSet(Sublime::Area* area, QString from, QString to); /**Emitted after the working-set was changed.*/ void changedWorkingSet(Sublime::Area* area, QString from, QString to); /** notifies the working set that it should clear */ void clearWorkingSet(Sublime::Area* area); private Q_SLOTS: void positionChanged(Sublime::View*, int); void actionDestroyed(QObject* action); private: template WalkerMode walkViewsInternal(Operator &op, AreaIndex *index); void initialize(); struct AreaPrivate *const d; }; } #include "areawalkers.h" #endif diff --git a/sublime/areaindex.h b/sublime/areaindex.h index 7b0ff6acd..bb8ecf52d 100644 --- a/sublime/areaindex.h +++ b/sublime/areaindex.h @@ -1,194 +1,194 @@ /*************************************************************************** * Copyright 2006-2007 Alexander Dymo * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_SUBLIMEAREAINDEX_H #define KDEVPLATFORM_SUBLIMEAREAINDEX_H -#include -#include +#include +#include #include "sublimeexport.h" namespace Sublime { class View; /** @short Index denotes the position of the view in the split area. B-Tree alike structure is used to represent an area with split views. Area has a root index which can either contain one view or contain two child nodes (@p first and @p second). In the later case area is considered to be split into two parts. Each of those parts can in turn contain a view or be split (with first/second children). When a view at given index is split, then its index becomes an index of the splitter and the original view goes into the @p first part of the splitter. The new view goes as @p second part. For example, consider an area which was split once horizontally and then the second part of it was split vertically: @code 1. initial state: one view in the area |----------------| | | | 1 | | | |----------------| Indices: root_index (view 1) 2. the view is split horizontally |----------------| | | | | 1 | 2 | | | | |----------------| Indices: root_index (no view) | ---------------- | | view 1 view 2 3. the second view is split vertically |----------------| | | 2 | | 1 |--------| | | 3 | |----------------| Indices: root_index (horizontal splitter) | ---------------- | | view 1 vertical_splitter | ----------------- | | view 2 view 3 @endcode It is possible that several "stacked" views will have the same area index. Those views can be considered as the view stack from which only one view is visible at the time. @code |----------------| | | | 1,2,3,4 | | | |----------------| Indices: root_index (view1, view2, view3, view4) @endcode */ class KDEVPLATFORMSUBLIME_EXPORT AreaIndex { public: ~AreaIndex(); AreaIndex(const AreaIndex &index); /**@return the parent index, returns 0 for root index.*/ AreaIndex *parent() const; /**@return the first child index if there're any.*/ AreaIndex *first() const; /**@return the second child index if there're any.*/ AreaIndex *second() const; /**@return true if the index is split.*/ bool isSplit() const; /**@return the orientation of the splitter for this index.*/ Qt::Orientation orientation() const; /**Set the orientation of the splitter for this index.*/ void setOrientation(Qt::Orientation orientation) const; /**Adds view to the list of views in this position. Does nothing if the view is already split. @param after if not 0, new view will be placed after this one. @param view the view to be added.*/ void add(View *view, View *after = nullptr); /**Removes view and unsplits the parent index when no views are left at the current index.*/ void remove(View *view); /**Splits the view in this position by given @p orientation and adds the @p newView into the splitter. Does nothing if the view is already split. @p newView will be in the second child index.*/ void split(View *newView, Qt::Orientation orientation); /**Splits the view in this position by given @p orientation. * @p moveViewsToSecondChild Normally, the existing views in this index are moved to the first sub-index. * If this is true, the views are moved to the _second_ sub-index instead. * Does nothing if the view is already split.*/ void split(Qt::Orientation orientation, bool moveViewsToSecondChild = false); /**Unsplits the index removing the given @p childToRemove and moving the contents of another child to this index.*/ void unsplit(AreaIndex *childToRemove); /** Returns a text-representation of the architecture of this area index and sub-indices. */ QString print() const; /**@return the stacked view in @p position, returns 0 for splitter's indices and when there's no view at the @p position.*/ View *viewAt(int position) const; /**@return the number of stacked views.*/ int viewCount() const; /**@return true if there's a stacked @p view at this index.*/ bool hasView(View *view) const; /**@return the list of views at this index.*/ QList &views() const; protected: /**Constructor for Root index.*/ AreaIndex(); private: /**Constructor for indices other than root.*/ explicit AreaIndex(AreaIndex *parent); /**Sets the parent for this index.*/ void setParent(AreaIndex *parent); /**Copies the data from this index to @p target.*/ void moveViewsTo(AreaIndex *target); /**Copies the children indices from this index to @p target.*/ void copyChildrenTo(AreaIndex *target); struct AreaIndexPrivate * const d; }; /** @short Root Area Index This is the special index class returned by @ref Area::rootIndex(). Doesn't provide any additional functionality beyond AreaIndex. */ class KDEVPLATFORMSUBLIME_EXPORT RootAreaIndex: public AreaIndex { public: RootAreaIndex(); private: class RootAreaIndexPrivate* const d; }; } #endif diff --git a/sublime/areawalkers.h b/sublime/areawalkers.h index 32aec0cfd..330c76bcd 100644 --- a/sublime/areawalkers.h +++ b/sublime/areawalkers.h @@ -1,67 +1,67 @@ /*************************************************************************** * Copyright 2006-2007 Alexander Dymo * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_SUBLIMEAREAWALKERS_H #define KDEVPLATFORM_SUBLIMEAREAWALKERS_H -#include +#include namespace Sublime { //area walkers implementations template Area::WalkerMode Area::walkViewsInternal(Operator &op, AreaIndex *index) { Area::WalkerMode mode = op(index); if (mode == Area::StopWalker) return mode; else if (index->first() && index->second()) { mode = walkViewsInternal(op, index->first()); if (mode == Area::StopWalker) return mode; mode = walkViewsInternal(op, index->second()); } return mode; } template void Area::walkViews(Operator &op, AreaIndex *index) { walkViewsInternal(op, index); } template void Area::walkToolViews(Operator &op, Positions positions) { QList currViews = toolViews(); foreach (View* view, currViews) { Sublime::Position position = toolViewPosition(view); if (position & positions) if (op(view, position) == Area::StopWalker) break; } } } #endif diff --git a/sublime/controller.h b/sublime/controller.h index afb6f372b..3551ce38a 100644 --- a/sublime/controller.h +++ b/sublime/controller.h @@ -1,200 +1,200 @@ /*************************************************************************** * Copyright 2006-2007 Alexander Dymo * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_SUBLIMECONTROLLER_H #define KDEVPLATFORM_SUBLIMECONTROLLER_H -#include +#include #include "sublimedefs.h" #include "sublimeexport.h" #include "mainwindowoperator.h" namespace Sublime { class Area; class AreaIndex; class Document; class MainWindow; /** @short Handles association of areas to main windows. Whereas the MainWindow class can be told to show arbitrary Area instance, this class establishes more high-level rules based on the following assumptions: 1. It's desirable to have a list of "area types" -- basically string, and be able to switch each main window between those "area types". For example, to switch main window between "Code" and "Debug" 2. It's also desirable to save the state of area -- like the set of toolviews, position of toolviews, etc. This need to be done per-main window, so that "code" area of one window is allowed to be different from "code" area of another window. 3. Is it desirable to be able to reset an area of given type in a given main window to a default state. The current implementation achieves those goals as follows. 1. Controller keeps a list of default areas. Those areas are not shown by any main window, and never modified as result of user actions. They are directly constructed by kdevelop core. Those areas are returned by the ::defaultAreas method. Each Area instance in the list provides area type id, and human name of the area -- via Area::objectName and Area::title methods respectively. All methods in this class accept area id, and human name of the area is only used to present the area type to the user, for selection. 2. Controller also keeps a list of MainWindow instances that it manages. For each instance, it keeps a list of areas private to the MainWindow instance. There's one area for each area in defaultAreas. That is, for each area type, there's one area in defaultAreas, and one area per each main window 3. It's possible to switch a given main window to display an area of the given type -- which finds the area with the given id in the list of area private to that main window, and switches the main window to the found area. When we create a new main window, we create fresh set of private areas by cloning the default areas. An alternative approach would be to create a clone only when we try to show a specific area type in a main window. However, I think that knowing that each main window has its Area instance for each area type simplifies the code. For example, most of the time, during restoring areas we'd need per-window area instances anyway. Of course, we can introduce a method demand_area_type(MainWindow*, QString) that clones the default area of the necessary type, but I don't see what that will buy us. Controller has to exist before any area, document or mainwindow can be created. There's no point in having two controllers for one application unless they need to show completely different sets of areas. */ class KDEVPLATFORMSUBLIME_EXPORT Controller: public QObject, public MainWindowOperator { Q_OBJECT public: explicit Controller(QObject *parent = nullptr); ~Controller() override; /** Add the area to the set of default areas in this controller. */ void addDefaultArea(Area *area); /** Return the list of default areas. */ const QList &defaultAreas() const; /** Return the default area with given @p id.*/ Area *defaultArea(const QString &id) const; /** Add a main window to the set of of windows managed by this controller. The ownership of the window is passed to the controller. The window will be associated with a set of areas created by cloning the current defaultAreas. */ void addMainWindow(MainWindow* mainWindow); /** Return the set of MainWindow instances managed by *this. */ const QList &mainWindows() const; /** Return all areas associated with the main window with the specified index. */ const QList &areas(int mainWindow) const; /** Return all areas associated with the main window with the specified index. */ const QList &areas(MainWindow* mainWindow) const; /** Return the area with the given in main window specified by its index, @p mainWindow. */ Area *area(int mainWindow, const QString& id) const; /** Returns the area that contains the given view. * */ Area* areaForView(View* view) const; /**Shows an @p area in @p mainWindow. @todo Remove this method */ void showArea(Area *area, MainWindow *mainWindow); /** Show area with the id of @p areaTypeId in @p mainWindow. */ void showArea(const QString& areaTypeId, MainWindow *mainWindow); /** Make the tool configuration of the area currently shown in @p mainWindow match those of default area with the same area type. */ void resetCurrentArea(MainWindow *mainWindow); /** Return the list of all areas, including default area and area private to each main window. */ const QList &allAreas() const; /**@return the list of documents created in this controller.*/ const QList &documents() const; void setStatusIcon(Document* document, const QIcon& icon); bool openAfterCurrent() const; bool arrangeBuddies() const; void loadSettings(); public Q_SLOTS: //@todo adymo: this should not be a part of public API /**Area can connect to this slot to release itself from its mainwindow.*/ void areaReleased(); /**Releases @p area from its mainwindow.*/ void areaReleased(Sublime::Area *area); protected: bool eventFilter(QObject *obj, QEvent *ev) override; void showAreaInternal(Area* area, MainWindow *mainWindow); private Q_SLOTS: void notifyToolViewRemoved(Sublime::View *view, Sublime::Position); void notifyToolViewAdded(Sublime::View *view, Sublime::Position); void notifyViewRemoved(Sublime::AreaIndex*, Sublime::View *view); void notifyViewAdded(Sublime::AreaIndex*, Sublime::View *view); Q_SIGNALS: void aboutToRemoveToolView(Sublime::View*); void toolViewAdded(Sublime::View*); void aboutToRemoveView(Sublime::View*); void viewAdded(Sublime::View*); void toolViewMoved(Sublime::View*); void mainWindowAdded(Sublime::MainWindow*); void areaCreated(Sublime::Area* area); private: void init(); void removeArea(Sublime::Area*); // called by Sublime::Area void removeDocument(Sublime::Document*); // called by Sublime::Document /**Adds the document to the controller, used by Document class. @todo adymo: refactor*/ void addDocument(Document *document); struct ControllerPrivate *const d; friend class Area; friend class Document; }; } #endif diff --git a/sublime/document.h b/sublime/document.h index 00ff6679e..5a94c86e8 100644 --- a/sublime/document.h +++ b/sublime/document.h @@ -1,146 +1,146 @@ /*************************************************************************** * Copyright 2006-2007 Alexander Dymo * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_SUBLIMEDOCUMENT_H #define KDEVPLATFORM_SUBLIMEDOCUMENT_H -#include -#include +#include +#include #include "sublimeexport.h" class QIcon; class QWidget; namespace Sublime { class Area; class View; class Controller; /** @short Abstract base class for all Sublime documents Subclass from Document and implement createViewWidget() method to return a new widget for a view. */ class KDEVPLATFORMSUBLIME_EXPORT Document: public QObject { Q_OBJECT public: /**Creates a document and adds it to a @p controller.*/ Document(const QString &title, Controller *controller); ~Document() override; /**@return the new view for this document. @note it will not create a widget, just return a view object.*/ View *createView(); /**@return the list of all views in all areas for this document.*/ const QList &views() const; /**@return the controller for this document.*/ Controller *controller() const; enum TitleType { Normal, Extended}; /**@return the document title.*/ virtual QString title(TitleType type = Normal) const; /**Set the document title.*/ void setTitle(const QString& newTitle); void setToolTip(const QString& newToolTip); QString toolTip() const; /**@return the type of document which can be written to config.*/ virtual QString documentType() const = 0; /**@return the specifics of this document which can be written to config.*/ virtual QString documentSpecifier() const = 0; /** * If the document is in a state where data may be lost while closking, * asks the user whether he really wants to close the document. * * This function may also take actions like saving the document before closing * if the user desires so. * * @return true if the document is allowed to be closed, otherwise false. * * The default implementation always returns true. * * */ virtual bool askForCloseFeedback(); /**Should try closing the document, eventually asking the user for feedback. * *If closing is successful, all views should be deleted, and the document itself *be scheduled for deletion using deleteLater(). * * @param silent If this is true, the user must not be asked. * * Returns whether closing was successful (The user did not push 'Cancel') */ virtual bool closeDocument(bool silent = false); void setStatusIcon(QIcon icon); /** * @return The status icon of the document. */ QIcon statusIcon() const; /** * @return The status icon of the document, or, if none is present, an icon * that resembles the document, i.e. based on its mime type. * @see defaultIcon() */ QIcon icon() const; /** * Optionally override this to return a default icon when no status * icon is set for the document. The default returns an invalid icon. */ virtual QIcon defaultIcon() const; Q_SIGNALS: /**Emitted when the document is about to be deleted but is still in valid state.*/ void aboutToDelete(Sublime::Document *doc); /**Emitted when the document's title is changed.*/ void titleChanged(Sublime::Document *doc); /**Emitted when the document status-icon has changed */ void statusIconChanged(Sublime::Document *doc); protected: /**Creates and returns the new view. Reimplement in subclasses to instantiate views of derived from Sublime::View classes.*/ virtual View *newView(Document *doc); /**Reimplement this to create and return the new widget to display this document in the view. This method is used by View class when it is asked for its widget.*/ virtual QWidget *createViewWidget(QWidget *parent = nullptr) = 0; /** Closes all views associated to this document */ virtual void closeViews(); private: struct DocumentPrivate *const d; friend struct DocumentPrivate; friend class View; }; } #endif diff --git a/sublime/sublimedefs.h b/sublime/sublimedefs.h index 7c3d3c860..149db6a3f 100644 --- a/sublime/sublimedefs.h +++ b/sublime/sublimedefs.h @@ -1,53 +1,53 @@ /*************************************************************************** * Copyright 2006-2007 Alexander Dymo * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_SUBLIMESUBLIMEDEFS_H #define KDEVPLATFORM_SUBLIMESUBLIMEDEFS_H -#include -#include +#include +#include #include "sublimeexport.h" /** @file sublimedefs.h @brief Sublime UI enums and other common definitions */ namespace Sublime { /**Positions for toolviews in Sublime UI. To combine them, use Positions class (QFlags based).*/ enum Position { Left = 1 /**< left toolview position */, Right = 2 /**< right toolview position */, Top = 4 /**< top toolview position */, Bottom = 8 /**< bottom toolview position */, AllPositions = Left|Right|Top|Bottom /**< all toolview positions */ }; Q_DECLARE_FLAGS(Positions, Position) KDEVPLATFORMSUBLIME_EXPORT Position dockAreaToPosition(Qt::DockWidgetArea dockArea); } Q_DECLARE_OPERATORS_FOR_FLAGS(Sublime::Positions) #endif diff --git a/sublime/view.h b/sublime/view.h index 7ec3fc5a9..e8e320cf7 100644 --- a/sublime/view.h +++ b/sublime/view.h @@ -1,109 +1,109 @@ /*************************************************************************** * Copyright 2006-2007 Alexander Dymo * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_SUBLIMEVIEW_H #define KDEVPLATFORM_SUBLIMEVIEW_H -#include -#include +#include +#include #include "sublimeexport.h" class QAction; namespace Sublime { class Document; /** @short View - the wrapper to the widget that knows about its document Views are the convenient way to manage a widget. It is specifically designed to be light and fast. Use @ref Document::createView() to get the new view for the document and call @ref View::widget() to create and get the actual widget. It is not possible to create a view by hand. You need either subclass it or use a Document. If you create a subclass of View you need to override Sublime::View::createWidget to provide a custom widget for your view. */ class KDEVPLATFORMSUBLIME_EXPORT View: public QObject { Q_OBJECT public: enum WidgetOwnership { TakeOwnership, DoNotTakeOwnerShip }; ~View() override; /**@return the toolbar actions for this view, this needs to be called _after_ the first call to widget() */ QList toolBarActions() const; /**@return the toolbar actions for this view, this needs to be called _after_ the first call to widget() */ QList contextMenuActions() const; /**@return the document for this view.*/ Document *document() const; /**@return widget for this view (creates it if it's not yet created).*/ QWidget *widget(QWidget *parent = nullptr); /**@return true if this view has an initialized widget.*/ bool hasWidget() const; /// Retrieve information to be placed in the status bar. virtual QString viewStatus() const; /// Retrieve view state for saving into configuration. virtual QString viewState() const; /// Restore view state from configuration virtual void setState(const QString& state); void notifyPositionChanged(int newPositionInArea); Q_SIGNALS: void raise(Sublime::View*); /// Notify that the status for this document has changed void statusChanged(Sublime::View*); void positionChanged(Sublime::View*, int); public Q_SLOTS: void requestRaise(); protected: explicit View(Document *doc, WidgetOwnership ws = DoNotTakeOwnerShip ); /** * override this function to create a custom widget in your View subclass * @param parent the parent widget * @returns a new widget which is used for this view */ virtual QWidget *createWidget(QWidget *parent); private: //copy is not allowed, create a new view from the document instead View(const View &v); struct ViewPrivate *const d; friend class Document; }; } Q_DECLARE_METATYPE(Sublime::View*) #endif diff --git a/tests/autotestshell.h b/tests/autotestshell.h index a58fbdb7b..93a9fb915 100644 --- a/tests/autotestshell.h +++ b/tests/autotestshell.h @@ -1,81 +1,81 @@ /*************************************************************************** * Copyright 2008 Harald Fernengel * * Copyright 2013 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 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_AUTOTESTSHELL_H #define KDEVPLATFORM_AUTOTESTSHELL_H -#include +#include #include "testsexport.h" #include #include /* This is a dummy shell for unit tests. It basically does nothing :) You can initialize it in initTestCase() to get a minimal shell to run your autotests. Example of a minimal KDevPlatform unit test: void Mytest::initTestCase() { AutoTestShell::init(); TestCore::initialize(); } */ namespace KDevelop { class KDEVPLATFORMTESTS_EXPORT AutoTestShell : public KDevelop::ShellExtension { public: explicit AutoTestShell(const QStringList& plugins); QString xmlFile() override { return QString(); } QString executableFilePath() override { return QString(); }; QString defaultProfile() { return QStringLiteral("kdevtest"); } KDevelop::AreaParams defaultArea() override { KDevelop::AreaParams params; params.name = QStringLiteral("test"); params.title = QStringLiteral("Test"); return params; } QString projectFileExtension() override { return QString(); } QString projectFileDescription() override { return QString(); } QStringList defaultPlugins() override { return m_plugins; } /** * Initialize the AutoTestShell and set the global instance. * * @p plugins A list of default global plugins which should be loaded. * By default, all global plugins are loaded. */ static void init(const QStringList& plugins = QStringList()); private: QStringList m_plugins; }; } #endif diff --git a/tests/kdevsignalspy.h b/tests/kdevsignalspy.h index 9886fc988..37ce1cdcb 100644 --- a/tests/kdevsignalspy.h +++ b/tests/kdevsignalspy.h @@ -1,61 +1,61 @@ /* * This file is part of KDevelop * Copyright 2008 Manuel Breugelmans * * 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. */ #ifndef KDEVPLATFORM_KDEVSIGNALSPY_H #define KDEVPLATFORM_KDEVSIGNALSPY_H -#include +#include #include "testsexport.h" class QEventLoop; class QTimer; namespace KDevelop { /*! A signal spy which exits the event loop when the signal is called, * and remembers that the signal was emitted. * adapted version of kdelibs/kdecore/utils/qtest_kde.cpp */ class KDEVPLATFORMTESTS_EXPORT KDevSignalSpy : public QObject { Q_OBJECT public: /*! Constructor. @p obj the object that is expected to emit @p signal. */ KDevSignalSpy(QObject *obj, const char *signal, Qt::ConnectionType ct = Qt::AutoConnection ); /*! Blocks until either the expected signal has been emitted or * @p timeout milliseconds have passed. */ bool wait(int timeout); private Q_SLOTS: void signalEmitted(); private: QObject* m_obj; bool m_emitted; QEventLoop* m_loop; QTimer* m_timer; }; } // KDevelop #endif diff --git a/tests/modeltest.h b/tests/modeltest.h index cdd558d4f..65781b22b 100644 --- a/tests/modeltest.h +++ b/tests/modeltest.h @@ -1,78 +1,78 @@ /**************************************************************************** ** ** Copyright (C) 2007 Trolltech ASA. All rights reserved. ** ** This file is part of the Qt Concurrent project on Trolltech Labs. ** ** This file may be used under the terms of the GNU General Public ** License version 2.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of ** this file. Please review the following information to ensure GNU ** General Public Licensing requirements will be met: ** http://www.trolltech.com/products/qt/opensource.html ** ** If you are unsure which license is appropriate for your use, please ** review the following information: ** http://www.trolltech.com/products/qt/licensing.html or contact the ** sales department at sales@trolltech.com. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ****************************************************************************/ #ifndef KDEVPLATFORM_MODELTEST_H #define KDEVPLATFORM_MODELTEST_H -#include -#include -#include +#include +#include +#include #include "testsexport.h" class KDEVPLATFORMTESTS_EXPORT ModelTest : public QObject { Q_OBJECT public: explicit ModelTest(QAbstractItemModel *model, QObject *parent = nullptr); private Q_SLOTS: void nonDestructiveBasicTest(); void rowCount(); void columnCount(); void hasIndex(); void index(); void parent(); void data(); protected Q_SLOTS: void runAllTests(); void layoutAboutToBeChanged(); void layoutChanged(); void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); void rowsInserted(const QModelIndex & parent, int start, int end); void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); void rowsRemoved(const QModelIndex & parent, int start, int end); private: void checkChildren(const QModelIndex &parent, int currentDepth = 0); QAbstractItemModel *model; struct Changing { QModelIndex parent; int oldSize; QVariant last; QVariant next; }; QStack insert; QStack remove; bool fetchingMore; QList changing; }; #endif diff --git a/util/commandexecutor.cpp b/util/commandexecutor.cpp index 5da92c915..f543d2fd1 100644 --- a/util/commandexecutor.cpp +++ b/util/commandexecutor.cpp @@ -1,167 +1,167 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat 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 "commandexecutor.h" #include "processlinemaker.h" -#include -#include -#include +#include +#include +#include #include #include namespace KDevelop { class CommandExecutorPrivate { public: explicit CommandExecutorPrivate( CommandExecutor* cmd ) : m_exec(cmd), m_useShell(false) { } CommandExecutor* m_exec; KProcess* m_process; ProcessLineMaker* m_lineMaker; QString m_command; QStringList m_args; QString m_workDir; QMap m_env; bool m_useShell; void procError( QProcess::ProcessError error ) { Q_UNUSED(error) m_lineMaker->flushBuffers(); emit m_exec->failed( error ); } void procFinished( int code, QProcess::ExitStatus status ) { m_lineMaker->flushBuffers(); if( status == QProcess::NormalExit ) emit m_exec->completed(code); } }; CommandExecutor::CommandExecutor( const QString& command, QObject* parent ) : QObject(parent), d(new CommandExecutorPrivate(this)) { d->m_process = new KProcess(this); d->m_process->setOutputChannelMode( KProcess::SeparateChannels ); d->m_lineMaker = new ProcessLineMaker( d->m_process ); d->m_command = command; connect( d->m_lineMaker, &ProcessLineMaker::receivedStdoutLines, this, &CommandExecutor::receivedStandardOutput ); connect( d->m_lineMaker, &ProcessLineMaker::receivedStderrLines, this, &CommandExecutor::receivedStandardError ); connect( d->m_process, static_cast(&KProcess::error), this, [&] (QProcess::ProcessError error) { d->procError(error); } ); connect( d->m_process, static_cast(&KProcess::finished), this, [&] (int code, QProcess::ExitStatus status) { d->procFinished(code, status); } ); } CommandExecutor::~CommandExecutor() { delete d->m_process; delete d->m_lineMaker; delete d; } void CommandExecutor::setEnvironment( const QMap& env ) { d->m_env = env; } void CommandExecutor::setEnvironment( const QStringList& env ) { QMap envmap; foreach( const QString& var, env ) { int sep = var.indexOf( '=' ); envmap.insert( var.left( sep ), var.mid( sep + 1 ) ); } d->m_env = envmap; } void CommandExecutor::setArguments( const QStringList& args ) { d->m_args = args; } void CommandExecutor::setWorkingDirectory( const QString& dir ) { d->m_workDir = dir; } bool CommandExecutor::useShell() const { return d->m_useShell; } void CommandExecutor::setUseShell( bool shell ) { d->m_useShell = shell; } void CommandExecutor::start() { for(auto it = d->m_env.constBegin(), itEnd = d->m_env.constEnd(); it!=itEnd; ++it) { d->m_process->setEnv( it.key(), it.value() ); } d->m_process->setWorkingDirectory( d->m_workDir ); if( !d->m_useShell ) { d->m_process->setProgram( d->m_command, d->m_args ); } else { QStringList arguments; Q_FOREACH( const QString &a, d->m_args ) arguments << KShell::quoteArg( a ); d->m_process->setShellCommand(d->m_command + ' ' + arguments.join(QLatin1Char(' '))); } d->m_process->start(); } void CommandExecutor::setCommand( const QString& command ) { d->m_command = command; } void CommandExecutor::kill() { d->m_process->kill(); } QString CommandExecutor::command() const { return d->m_command; } QStringList CommandExecutor::arguments() const { return d->m_args; } QString CommandExecutor::workingDirectory() const { return d->m_workDir; } } #include "moc_commandexecutor.cpp" diff --git a/util/commandexecutor.h b/util/commandexecutor.h index 61333da29..c96469709 100644 --- a/util/commandexecutor.h +++ b/util/commandexecutor.h @@ -1,141 +1,141 @@ /* This file is part of KDevelop Copyright 2007 Andreas Pakulat 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. */ #ifndef KDEVPLATFORM_COMMANDEXECUTOR_H #define KDEVPLATFORM_COMMANDEXECUTOR_H -#include -#include +#include +#include #include "utilexport.h" namespace KDevelop { /** * Simplifying the exeuction of a Command through (QK)Process. * * This class emits only very basic signals when the process writes something * to stdout or stderr and for signaling completed and failed status of running * the process. This means that a process that is executed without a crash or so * is considered to be completed, even if it indicates an error during execution * using a non-zero return value. This needs to be handled by the user of the class * using the argument in the completed signal * * If you need more fine-grained control use (QK)Process directly and also * check whether you can use \ref KDevelop::ProcessLineMaker to use properly * terminated lines of output. * * Also this class provides only asynchronous operation, it doesn't allow to * wait for the program to finish. * * @author Andreas Pakulat * TODO: Should this be a KJob?? */ class KDEVPLATFORMUTIL_EXPORT CommandExecutor : public QObject { Q_OBJECT public: /** * Create a command using the given executable, arguments and environment * * The process is not started immediately, instead start() has to be called. */ explicit CommandExecutor( const QString& command, QObject* parent = nullptr ); ~CommandExecutor() override; /** * set additional arguments to be used when executing the command */ void setArguments( const QStringList& args ); /** * set additional environment variables to be used when executing the command */ void setEnvironment( const QMap& env ); /** * set additional environment variables to be used when executing the command */ void setEnvironment( const QStringList& env ); /** * Sets the working directory of the command */ void setWorkingDirectory( const QString& dir ); /** * start the command, after this has been called signals may be emitted */ void start(); /** * kill the process, failed() will likely be emitted */ void kill(); /** * set the Command that should be started, now a commandexecutor can be reused */ void setCommand( const QString& command ); /** * whether the commands are executed from a shell */ bool useShell() const; /** * if @p shell is true, the command is executed from a shell */ void setUseShell( bool shell ); /** * @returns the arguments */ QStringList arguments() const; /** * @returns the command */ QString command() const; /** * @returns the working directory */ QString workingDirectory() const; Q_SIGNALS: void receivedStandardError( const QStringList& ); void receivedStandardOutput( const QStringList& ); /** * Emitted when there was a severe problem executing the process, for example it * could not be started or crashed during execution. */ void failed( QProcess::ProcessError ); /** * Emitted when the process was successfully started and finished without crashing * The @p code parameter indicates the return value from executing the process */ void completed(int code); private: class CommandExecutorPrivate* const d; friend class CommandExecutorPrivate; }; } #endif diff --git a/util/embeddedfreetree.h b/util/embeddedfreetree.h index c8366cd80..c30ab774a 100644 --- a/util/embeddedfreetree.h +++ b/util/embeddedfreetree.h @@ -1,889 +1,889 @@ /* 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. */ #ifndef EMBEDDED_FREE_TREE #define EMBEDDED_FREE_TREE #include "kdevvarlengtharray.h" -#include +#include #include #include #include //Uncomment this to search for tree-inconsistencies, however it's very expensive // #define DEBUG_FREEITEM_COUNT debugFreeItemCount(); verifyTreeConsistent(*m_centralFreeItem, 0, m_itemCount); #define DEBUG_FREEITEM_COUNT /** * This file implements algorithms that allow managing a sorted list of items, and managing "free" items * for reuse efficiently in that list. Among those free items a tree is built, and they are traversed * on insertion/removal to manage free items in the tree. * * There is specific needs on the embedded items: * - They must be markable "invalid", so after they are deleted they can stay in their place as invalid items. * - While they are invalid, they still must be able to hold 2 integers, needed for managing the tree of free items. * - One integer is needed for each list to hold a pointer to the central free item. * * Only these functions must be used to manipulate the lists, from the beginning up. First create an empty list * and initialize centralFreeItem with -1, and then you start adding items. * * Since the list is sorted, and each item can be contained only once, these lists actually represent a set. * * EmbeddedTreeAlgorithms implements an efficient "contains" function that uses binary search within the list. */ namespace KDevelop { ///Responsible for handling the items in the list ///This is an example. ItemHandler::rightChild(..) and ItemHandler::leftChild(..) must be values that must be able to hold the count of positive ///values that will be the maximum size of the list, and additionally -1. // template // class ExampleItemHandler { // public: // ExampleItemHandler(const Data& data) : m_data(data) { // } // int ItemHandler::rightChild() const { // Q_ASSERT(0); // } // int ItemHandler::leftChild() const { // Q_ASSERT(0); // } // void ItemHandler::setLeftChild(int child) { // Q_ASSERT(0); // } // void setRightChild(int child) { // Q_ASSERT(0); // } // bool operator<(const StandardItemHandler& rhs) const { // Q_ASSERT(0); // } // //Copies this item into the given one // void copyTo(Data& data) const { // data = m_data; // } // // static void createFreeItem(Data& data) { // data = Data(); // } // // bool isFree() const { // Q_ASSERT(0); // } // // const Data& data() { // } // // private: // const Data& m_data; // }; /** * Use this for several constant algorithms on sorted lists with free-trees * */ template class EmbeddedTreeAlgorithms { public: EmbeddedTreeAlgorithms(const Data* items, uint itemCount, const int& centralFreeItem) : m_items(items), m_itemCount(itemCount), m_centralFreeItem(¢ralFreeItem) { } ~EmbeddedTreeAlgorithms() { } ///Efficiently checks whether the item is contained in the set. ///If it is contained, returns the index. Else, returns -1. int indexOf(const Data& data) { return indexOf(data, 0, m_itemCount); } ///Searches the given item within the specified bounds. int indexOf(const Data& data, uint start, uint end) { while(1) { if(start >= end) return -1; int center = (start + end)/2; //Skip free items, since they cannot be used for ordering for(; center < (int)end; ) { if(!ItemHandler::isFree(m_items[center])) break; ++center; } if(center == (int)end) { end = (start + end)/2; //No non-free items found in second half, so continue search in the other }else{ if(ItemHandler::equals(data, m_items[center])) { return center; }else if(data < m_items[center]) { end = (start + end)/2; }else{ //Continue search in second half start = center+1; } } } } ///Returns the first valid index that has a data-value larger or equal to @p data. ///Returns -1 if nothing is found. int lowerBound(const Data& data, int start, int end) { int currentBound = -1; while(1) { if(start >= end) return currentBound; int center = (start + end)/2; //Skip free items, since they cannot be used for ordering for(; center < end; ) { if(!ItemHandler::isFree(m_items[center])) break; ++center; } if(center == end) { end = (start + end)/2; //No non-free items found in second half, so continue search in the other }else{ if(ItemHandler::equals(data, m_items[center])) { return center; }else if(data < m_items[center]) { currentBound = center; end = (start + end)/2; }else{ //Continue search in second half start = center+1; } } } } uint countFreeItems() const { return countFreeItemsInternal(*m_centralFreeItem); } uint countFreeItemsNaive() const { uint ret = 0; for(uint a = 0; a < m_itemCount; ++a) { if(ItemHandler::isFree(m_items[a])) ++ret; } return ret; } void verifyOrder() { Data last; for(uint a = 0; a < m_itemCount; ++a) { if(!ItemHandler::isFree(m_items[a])) { if(!ItemHandler::isFree(last)) Q_ASSERT(last < m_items[a]); last = m_items[a]; } } } void verifyTreeConsistent() { verifyTreeConsistentInternal(*m_centralFreeItem, 0, m_itemCount); Q_ASSERT(countFreeItems() == countFreeItemsNaive()); } private: void verifyTreeConsistentInternal(int position, int lowerBound, int upperBound) { if(position == -1) return; Q_ASSERT(lowerBound <= position && position < upperBound); verifyTreeConsistentInternal(ItemHandler::leftChild(m_items[position]), lowerBound, position); verifyTreeConsistentInternal(ItemHandler::rightChild(m_items[position]), position+1, upperBound); } uint countFreeItemsInternal(int item) const { if(item == -1) return 0; return 1 + countFreeItemsInternal(ItemHandler::leftChild(m_items[item])) + countFreeItemsInternal(ItemHandler::rightChild(m_items[item])); } const Data* m_items; uint m_itemCount; const int* m_centralFreeItem; }; /**Use this to add items. * The added item must not be in the set yet! * General usage: * - Construct the object * - Check if newItemCount() equals the previous item-count. If not, construct * a new list as large as newItemCount, and call object.transferData to transfer the data * into the new list. The new size must match the returned newItemCount. * - Either call object.apply(), or let it be called automatically by the destructor. * @tparam increaseFraction By what fraction the list is increased when it needs to. For example the size will be increased by 1/5 if it's 5. * @tparam rebuildIfInsertionMoreExpensive The structure is rebuilt completely when an insertion needs a moving around of more than rebuildIfInsertionMoreExpensive times the count of items needed to be moved in worst case in a fresh tree. * After rebuilding the tree, the free space is evenly distributed, and thus insertions require much less moving. * */ template class EmbeddedTreeAddItem { public: EmbeddedTreeAddItem(Data* items, uint itemCount, int& centralFreeItem, const Data& add) : m_add(add), m_items(items), m_itemCount(itemCount), m_centralFreeItem(¢ralFreeItem), m_applied(false), m_needResize(false) { m_needResize = !apply(); } ~EmbeddedTreeAddItem() { if(!m_applied) apply(true); } ///Check this to see whether a new item-count is needed. If this does not equal the given itemCount, then ///the data needs to be transferred into a new list using transferData uint newItemCount() const { if(!m_applied) { if(*m_centralFreeItem == -1) { uint realItemCount = m_itemCount - countFreeItems(*m_centralFreeItem); uint newItemCount = realItemCount + (realItemCount/increaseFraction); if(newItemCount <= m_itemCount) newItemCount = m_itemCount+1; return newItemCount; }else if(m_needResize) { uint realItemCount = m_itemCount - countFreeItems(*m_centralFreeItem); uint newItemCount = realItemCount + (realItemCount/increaseFraction); return newItemCount; } } return m_itemCount; } ///Transfers the data into a new item-list. The size of the new item-list must equal newItemCount() void transferData(Data* newItems, uint newCount, int* newCentralFree = nullptr) { DEBUG_FREEITEM_COUNT uint currentRealCount = m_itemCount - countFreeItems(*m_centralFreeItem); // Q_ASSERT(currentRealCount + (currentRealCount/increaseFraction) == newCount); //Create a new list where the items from m_items are put into newItems, with the free items evenly //distributed, and a clean balanced free-tree. uint newFreeCount = newCount - currentRealCount; volatile uint freeItemRaster; if(newFreeCount) freeItemRaster = newCount / newFreeCount; else { freeItemRaster = newCount+1; //No free items } ///@todo Do not iterate through all items, instead use the free-tree and memcpy for the ranges between free items. ///Ideally, even the free-tree would be built on-the-fly. Q_ASSERT(freeItemRaster); uint offset = 0; uint insertedValidCount = 0; for(uint a = 0; a < newCount; ++a) { //Create new free items at the end of their raster range if(a % freeItemRaster == (freeItemRaster-1)) { //We need to insert a free item ItemHandler::createFreeItem(newItems[a]); ++offset; }else{ ++insertedValidCount; while(ItemHandler::isFree(m_items[a-offset]) && a-offset < m_itemCount) --offset; Q_ASSERT(a-offset < m_itemCount); newItems[a] = m_items[a-offset]; // Q_ASSERT(!ItemHandler::isFree(newItems[a])); } } Q_ASSERT(insertedValidCount == m_itemCount - countFreeItems(*m_centralFreeItem)); // qCDebug(UTIL) << m_itemCount << newCount << offset; // Q_ASSERT(m_itemCount == newCount-offset); m_items = newItems; m_itemCount = newCount; if(newCentralFree) m_centralFreeItem = newCentralFree; *m_centralFreeItem = buildFreeTree(newFreeCount, freeItemRaster, freeItemRaster-1); // qCDebug(UTIL) << "count of new free items:" << newFreeCount; // Q_ASSERT(countFreeItems( *m_centralFreeItem ) == newFreeCount); DEBUG_FREEITEM_COUNT } ///Tries to put the item into the list. If the insertion would be too inefficient or is not possible, returns false, unless @param force is true bool apply(bool force = false) { if(m_applied) return true; if(*m_centralFreeItem == -1) { Q_ASSERT(!force); return false; } //Find the free item that is nearest to the target position in the item order int previousItem = -1; int currentItem = *m_centralFreeItem; int replaceCurrentWith = -1; //In currentLowerBound and currentUpperBound, we count the smallest contiguous range between free //items that the added items needs to be sorted into. If the range is empty, the item can just be inserted. int currentLowerBound = 0; int currentUpperBound = m_itemCount; //Now go down the chain, always into the items direction while(1) { QPair freeBounds = leftAndRightRealItems(currentItem); const Data& current(m_items[currentItem]); if(freeBounds.first != -1 && m_add < m_items[freeBounds.first]) { //Follow left child currentUpperBound = freeBounds.first+1; if(ItemHandler::leftChild(current) != -1) { //Continue traversing previousItem = currentItem; currentItem = ItemHandler::leftChild(current); }else{ replaceCurrentWith = ItemHandler::rightChild(current); break; } }else if(freeBounds.second != -1 && m_items[freeBounds.second] < m_add) { //Follow right child currentLowerBound = freeBounds.second; if(ItemHandler::rightChild(current) != -1) { //Continue traversing previousItem = currentItem; currentItem = ItemHandler::rightChild(current); }else{ replaceCurrentWith = ItemHandler::leftChild(current); break; } }else{ //We will use this item! So find a replacement for it in the tree, and update the structure force = true; currentLowerBound = currentUpperBound = currentItem; int leftReplaceCandidate = -1, rightReplaceCandidate = -1; if(ItemHandler::leftChild(current) != -1) leftReplaceCandidate = rightMostChild(ItemHandler::leftChild(current)); if(ItemHandler::rightChild(current) != -1) rightReplaceCandidate = leftMostChild(ItemHandler::rightChild(current)); ///@todo it's probably better using lowerBound and upperBound like in the "remove" version //Left and right bounds of all children of current int leftChildBound = leftMostChild(currentItem), rightChildBound = rightMostChild(currentItem); Q_ASSERT(leftChildBound != -1 && rightChildBound != -1); int childCenter = (leftChildBound + rightChildBound)/2; if(leftReplaceCandidate == -1 && rightReplaceCandidate == -1) { //We don't have a replace candidate, since there is no children Q_ASSERT(ItemHandler::leftChild(current) == -1); Q_ASSERT(ItemHandler::rightChild(current) == -1); }else if(rightReplaceCandidate == -1 || abs(leftReplaceCandidate - childCenter) < abs(rightReplaceCandidate - childCenter)) { //pick the left replacement, since it's more central Q_ASSERT(leftReplaceCandidate != -1); replaceCurrentWith = leftReplaceCandidate; Data& replaceWith(m_items[replaceCurrentWith]); if(replaceCurrentWith == ItemHandler::leftChild(current)) { //The left child of replaceWith can just stay as it is, and we just need to add the right child Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); }else{ takeRightMostChild(ItemHandler::leftChild(current)); //Since we'll be clearing the item, we have to put this childsomewhere else. // Either make it our new "left" child, or make it the new left children "rightmost" child. int addRightMostLeftChild = ItemHandler::leftChild(replaceWith); ItemHandler::setLeftChild(replaceWith, -1); Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); if(ItemHandler::leftChild(current) != -1) { Q_ASSERT(rightMostChild(ItemHandler::leftChild(current)) != replaceCurrentWith); Q_ASSERT(ItemHandler::leftChild(current) == -1 || ItemHandler::leftChild(current) < replaceCurrentWith); ItemHandler::setLeftChild(replaceWith, ItemHandler::leftChild(current)); if(addRightMostLeftChild != -1) { int rightMostLeft = rightMostChild(ItemHandler::leftChild(replaceWith)); Q_ASSERT(rightMostLeft != -1); // Q_ASSERT(item(rightMostLeft).ItemHandler::rightChild() == -1); Q_ASSERT(rightMostLeft < addRightMostLeftChild); ItemHandler::setRightChild(m_items[rightMostLeft], addRightMostLeftChild); } }else{ Q_ASSERT(addRightMostLeftChild == -1 || addRightMostLeftChild < replaceCurrentWith); ItemHandler::setLeftChild(replaceWith, addRightMostLeftChild); } } Q_ASSERT(ItemHandler::rightChild(current) == -1 || replaceCurrentWith < ItemHandler::rightChild(current)); ItemHandler::setRightChild(replaceWith, ItemHandler::rightChild(current)); }else{ //pick the right replacement, since it's more central Q_ASSERT(rightReplaceCandidate != -1); replaceCurrentWith = rightReplaceCandidate; Data& replaceWith(m_items[replaceCurrentWith]); if(replaceCurrentWith == ItemHandler::rightChild(current)) { //The right child of replaceWith can just stay as it is, and we just need to add the left child Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); }else{ takeLeftMostChild(ItemHandler::rightChild(current)); //Since we'll be clearing the item, we have to put this childsomewhere else. // Either make it our new "right" child, or make it the new right children "leftmost" child. int addLeftMostRightChild = ItemHandler::rightChild(replaceWith); ItemHandler::setRightChild(replaceWith, -1); Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); if(ItemHandler::rightChild(current) != -1) { Q_ASSERT(leftMostChild(ItemHandler::rightChild(current)) != replaceCurrentWith); Q_ASSERT(ItemHandler::rightChild(current) == -1 || replaceCurrentWith < ItemHandler::rightChild(current)); ItemHandler::setRightChild(replaceWith, ItemHandler::rightChild(current)); if(addLeftMostRightChild != -1) { int leftMostRight = leftMostChild(ItemHandler::rightChild(replaceWith)); Q_ASSERT(leftMostRight != -1); Q_ASSERT(ItemHandler::leftChild(m_items[leftMostRight]) == -1); Q_ASSERT(addLeftMostRightChild < leftMostRight); ItemHandler::setLeftChild(m_items[leftMostRight], addLeftMostRightChild); } }else{ Q_ASSERT(addLeftMostRightChild == -1 || replaceCurrentWith < addLeftMostRightChild); ItemHandler::setRightChild(replaceWith, addLeftMostRightChild); } } Q_ASSERT(ItemHandler::leftChild(current) == -1 || ItemHandler::leftChild(current) < replaceCurrentWith); ItemHandler::setLeftChild(replaceWith, ItemHandler::leftChild(current)); } break; } } //We can insert now //currentItem and previousItem are the two items that best enclose the target item // for(int a = currentLowerBound; a < currentUpperBound; ++a) { // Q_ASSERT(!ItemHandler::isFree(m_items[a])); // } Q_ASSERT(currentItem < currentLowerBound || currentItem >= currentUpperBound); //If the current item is on a border of the bounds, it needs to be inserted in the right position. //Else, the current position already is right, and we only need to copy it in. if(currentLowerBound < currentUpperBound && (currentItem == currentLowerBound-1 || currentItem == currentUpperBound)) { if(!insertSorted(m_add, currentItem, currentLowerBound, currentUpperBound, force)) { return false; } }else{ ItemHandler::copyTo(m_add, m_items[currentItem]); } m_applied = true; //First, take currentItem out of the chain, by replacing it with current.rightChild in the parent if(previousItem != -1) { Data& previous(m_items[previousItem]); if(ItemHandler::leftChild(previous) == currentItem) { Q_ASSERT(replaceCurrentWith == -1 || replaceCurrentWith < previousItem); ItemHandler::setLeftChild(previous, replaceCurrentWith); } else if(ItemHandler::rightChild(previous) == currentItem) { Q_ASSERT(replaceCurrentWith == -1 || previousItem < replaceCurrentWith); ItemHandler::setRightChild(previous, replaceCurrentWith); } else { Q_ASSERT(0); } } else { *m_centralFreeItem = replaceCurrentWith; } return true; DEBUG_FREEITEM_COUNT } private: void verifyTreeConsistent(int position, int lowerBound, int upperBound) { if(position == -1) return; Q_ASSERT(lowerBound <= position && position < upperBound); verifyTreeConsistent(ItemHandler::leftChild(m_items[position]), lowerBound, position); verifyTreeConsistent(ItemHandler::rightChild(m_items[position]), position+1, upperBound); } void debugFreeItemCount() { uint count = 0; for(uint a = 0; a < m_itemCount; ++a) { if(isFree(m_items[a])) ++count; } uint counted = countFreeItems(*m_centralFreeItem); Q_ASSERT(count == counted); Q_UNUSED(counted); } QPair leftAndRightRealItems(int pos) { Q_ASSERT(ItemHandler::isFree(m_items[pos])); int left = -1, right = -1; for(int a = pos-1; a >= 0; --a) { if(!ItemHandler::isFree(m_items[a])) { left = a; break; } } for(uint a = pos+1; a < m_itemCount; ++a) { if(!ItemHandler::isFree(m_items[a])) { right = a; break; } } return qMakePair(left, right); } int buildFreeTree(int count, uint raster, int start) { Q_ASSERT((start % raster) == (raster-1)); Q_ASSERT(count != 0); Q_ASSERT(count <= (int)m_itemCount); if(count == 1) { ItemHandler::createFreeItem(m_items[start]); return start; }else{ int central = start + (count / 2) * raster; int leftCount = count / 2; int midCount = 1; int rightCount = count - leftCount - midCount; Q_ASSERT(leftCount + midCount <= count); ItemHandler::createFreeItem(m_items[central]); Q_ASSERT(ItemHandler::isFree(m_items[central])); int leftFreeTree = buildFreeTree(leftCount, raster, start); Q_ASSERT(leftFreeTree == -1 || leftFreeTree < central); ItemHandler::setLeftChild(m_items[central], leftFreeTree ); if(rightCount > 0) { int rightFreeTree = buildFreeTree(rightCount, raster, central+raster); Q_ASSERT(rightFreeTree == -1 || central < rightFreeTree); ItemHandler::setRightChild(m_items[central], rightFreeTree ); } return central; } } uint countFreeItems(int item) const { if(item == -1) return 0; const Data& current(m_items[item]); return 1 + countFreeItems(ItemHandler::leftChild(current)) + countFreeItems(ItemHandler::rightChild(current)); } int leftMostChild(int pos) const { while(1) { if(ItemHandler::leftChild(m_items[pos]) != -1) pos = ItemHandler::leftChild(m_items[pos]); else return pos; } } int takeLeftMostChild(int pos) const { int parent = -1; while(1) { if(ItemHandler::leftChild(m_items[pos]) != -1) { parent = pos; pos = ItemHandler::leftChild(m_items[pos]); } else { ItemHandler::setLeftChild(m_items[parent], -1); return pos; } } } int rightMostChild(int pos) const { while(1) { if(ItemHandler::rightChild(m_items[pos]) != -1) pos = ItemHandler::rightChild(m_items[pos]); else return pos; } } int takeRightMostChild(int pos) const { int parent = -1; while(1) { if(ItemHandler::rightChild(m_items[pos]) != -1) { parent = pos; pos = ItemHandler::rightChild(m_items[pos]); } else { ItemHandler::setRightChild(m_items[parent], -1); return pos; } } } ///Maximum "moving" out of the way of items without forcing a complete rebuild of the list inline int maxMoveAround() const { return increaseFraction * rebuildIfInsertionMoreExpensive; } ///Inserts the given data item into position @p pos, and updates the sorting ///@param data can be another empty item, that together with @p pos represents the closest enclosure of the target position ///@return Whether the item could be inserted. It is not inserted if bool insertSorted(const Data& data, int pos, int start, int end, bool force) { if(pos < start) start = pos; if(pos >= end) end = pos+1; /* for(int a = start; a < end; ++a) { if(a != pos) { Q_ASSERT(!ItemHandler::isFree(m_items[a])); } }*/ EmbeddedTreeAlgorithms alg(m_items, m_itemCount, *m_centralFreeItem); int bound = alg.lowerBound(data, start, end); //Now find the position that should be taken if(bound == -1) bound = end; //Now our item should end up right before bound int target; //bound cannot be pos, because pos is invalid Q_ASSERT(bound != pos); //Shuffle around the item at the free pos, so reference counting in constructors/destructors is not screwed up char backup[sizeof(Data)]; memcpy(backup, m_items+pos, sizeof(Data)); if(bound < pos) { if(!force && pos-bound > maxMoveAround()) { // qCDebug(UTIL) << "increasing because" << pos-bound << ">" << maxMoveAround() << "left free items:" << countFreeItems(*m_centralFreeItem) << "target free items:" << (m_itemCount-countFreeItems(*m_centralFreeItem))/increaseFraction; return false; } //Move [bound, pos) one to right, and insert at bound memmove(m_items+bound+1, m_items+bound, sizeof(Data)*(pos-bound)); target = bound; }else { Q_ASSERT(bound > pos); if(!force && bound-pos-1 > maxMoveAround()) { // qCDebug(UTIL) << "increasing because" << bound-pos-1 << ">" << maxMoveAround() << "left free items:" << countFreeItems(*m_centralFreeItem)<< "target free items:" << (m_itemCount-countFreeItems(*m_centralFreeItem))/increaseFraction; return false; } //Move (pos, bound) one to left, and insert at bound-1 memmove(m_items+pos, m_items+pos+1, sizeof(Data)*(bound-pos-1)); target = bound-1; } memcpy(m_items+target, backup, sizeof(Data)); ItemHandler::copyTo(data, m_items[target]); return true; } const Data& m_add; Data* m_items; uint m_itemCount; int* m_centralFreeItem; bool m_applied, m_needResize; }; /**Use this to add items. * The removed item must be in the set! * General usage: * - Construct the object * - Check if newItemCount() equals the previous item-count. If not, construct * a new list as large as newItemCount, and call object.transferData to transfer the data * into the new list. The new size must match the returned newItemCount. * However this may also be ignored if the memory-saving is not wanted in that moment. * */ template class EmbeddedTreeRemoveItem { public: EmbeddedTreeRemoveItem(Data* items, uint itemCount, int& centralFreeItem, const Data& remove) : m_remove(remove), m_items(items), m_itemCount(itemCount), m_centralFreeItem(¢ralFreeItem), m_insertedAtDepth(0) { apply(); } ~EmbeddedTreeRemoveItem() { } ///Check this to see whether a new item-count is needed. If this does not equal the given itemCount, then ///the data needs to be transferred into a new list using transferData uint newItemCount() const { uint maxFreeItems = ((m_itemCount / increaseFraction)*3)/2 + 1; //First we approximate the count of free items using the insertion depth if((1u << m_insertedAtDepth) >= maxFreeItems) { uint freeCount = countFreeItems(*m_centralFreeItem); if(freeCount > maxFreeItems || freeCount == m_itemCount) { return m_itemCount - freeCount; } } return m_itemCount; } ///Transfers the data into a new item-list. The size of the new item-list must equal newItemCount() void transferData(Data* newItems, uint newCount, int* newCentralFree = nullptr) { DEBUG_FREEITEM_COUNT //We only transfer into a new list when all the free items are used up //Create a list where only the non-free items exist uint offset = 0; for(uint a = 0; a < m_itemCount; ++a) { if(!ItemHandler::isFree(m_items[a])) { newItems[offset] = m_items[a]; ++offset; } } Q_ASSERT(offset == newCount); if(newCentralFree) m_centralFreeItem = newCentralFree; *m_centralFreeItem = -1; m_items = newItems; m_itemCount = newCount; DEBUG_FREEITEM_COUNT } private: void verifyTreeConsistent(int position, int lowerBound, int upperBound) { if(position == -1) return; Q_ASSERT(lowerBound <= position && position < upperBound); verifyTreeConsistent(ItemHandler::leftChild(m_items[position]), lowerBound, position); verifyTreeConsistent(ItemHandler::rightChild(m_items[position]), position+1, upperBound); } uint countFreeItems(int item) const { if(item == -1) return 0; const Data& current(m_items[item]); return 1 + countFreeItems(ItemHandler::leftChild(current)) + countFreeItems(ItemHandler::rightChild(current)); } int findItem(const Data& data, uint start, uint end) { EmbeddedTreeAlgorithms alg(m_items, m_itemCount, *m_centralFreeItem); return alg.indexOf(data, start, end); } void apply() { DEBUG_FREEITEM_COUNT int removeIndex = findItem(m_remove, 0, m_itemCount); Q_ASSERT(removeIndex != -1); Q_ASSERT(!ItemHandler::isFree(m_items[removeIndex])); //Find the free item that is nearest to the target position in the item order int currentItem = *m_centralFreeItem; int lowerBound = 0; //The minimum position the searched item can have int upperBound = m_itemCount; //The lowest known position the searched item can _not_ have if(*m_centralFreeItem == -1) { *m_centralFreeItem = removeIndex; Q_ASSERT(*m_centralFreeItem != -1); ItemHandler::createFreeItem(m_items[*m_centralFreeItem]); return; } //Now go down the chain, always into the items direction ///@todo make the structure better: Don't just put left/right child, but also swap when neede /// to balance the tree while(1) { Q_ASSERT(removeIndex != currentItem); Data& current(m_items[currentItem]); ++m_insertedAtDepth; if(removeIndex < currentItem) { upperBound = currentItem; //Follow left child if(ItemHandler::leftChild(current) != -1) { //Continue traversing currentItem = ItemHandler::leftChild(current); Q_ASSERT(currentItem >= lowerBound && currentItem < upperBound); }else{ //The to-be deleted item is before current, and can be added as left child to current int item = findItem(m_remove, lowerBound, upperBound); Q_ASSERT(item == removeIndex); ItemHandler::createFreeItem(m_items[item]); Q_ASSERT(item == -1 || item < currentItem); ItemHandler::setLeftChild(current, item); Q_ASSERT(item >= lowerBound && item < upperBound); break; } }else{ lowerBound = currentItem+1; //Follow right child if(ItemHandler::rightChild(current) != -1) { //Continue traversing currentItem = ItemHandler::rightChild(current); Q_ASSERT(currentItem >= lowerBound && currentItem < upperBound); }else{ //The to-be deleted item is behind current, and can be added as right child to current int item = findItem(m_remove, lowerBound, upperBound); Q_ASSERT(item == removeIndex); ItemHandler::createFreeItem(m_items[item]); Q_ASSERT(item == -1 || currentItem < item); ItemHandler::setRightChild(current, item); Q_ASSERT(item >= lowerBound && item < upperBound); break; } } } DEBUG_FREEITEM_COUNT } void debugFreeItemCount() { uint count = 0; for(uint a = 0; a < m_itemCount; ++a) { if(ItemHandler::isFree(m_items[a])) ++count; } uint counted = countFreeItems(*m_centralFreeItem); Q_ASSERT(count == counted); Q_UNUSED(counted); } const Data& m_remove; Data* m_items; uint m_itemCount; int* m_centralFreeItem; int m_insertedAtDepth; }; } #endif diff --git a/util/kdevvarlengtharray.h b/util/kdevvarlengtharray.h index 14d575e1e..133ef326d 100644 --- a/util/kdevvarlengtharray.h +++ b/util/kdevvarlengtharray.h @@ -1,85 +1,85 @@ /* Copyright 2009 David Nolden Copyright 2011 Milian Wolff 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. */ #ifndef KDEVPLATFORM_KDEVVARLENGTHARRAY_H #define KDEVPLATFORM_KDEVVARLENGTHARRAY_H -#include -#include +#include +#include /** * Extended QVarLengthArray with additional convenience API. */ template class KDevVarLengthArray : public QVarLengthArray { using Base = QVarLengthArray; public: using Base::QVarLengthArray; ///Removes exactly one occurrence of the given value from the array. Returns false if none was found. inline bool removeOne(const T& value); /// @return QList of items in this array QList toList() const; /// @return QVector of items in this array QVector toVector() const; }; template Q_INLINE_TEMPLATE bool KDevVarLengthArray::removeOne(const T& value) { const int idx = Base::indexOf(value); if (idx == -1) { return false; } Base::remove(idx); return true; } template Q_OUTOFLINE_TEMPLATE QList< T > KDevVarLengthArray::toList() const { QList ret; ret.reserve(Base::size()); const T* const end = Base::constEnd(); for(const T* it = Base::constBegin(); it != end; ++it) { ret << *it; } return ret; } template Q_OUTOFLINE_TEMPLATE QVector< T > KDevVarLengthArray::toVector() const { QVector ret; ret.reserve(Base::size()); const T* const end = Base::constEnd(); for(const T* it = Base::constBegin(); it != end; ++it) { ret << *it; } return ret; } #endif // KDEVPLATFORM_KDEVVARLENGTHARRAY_H diff --git a/util/ksharedobject.h b/util/ksharedobject.h index 5a4511ddc..be48139ac 100644 --- a/util/ksharedobject.h +++ b/util/ksharedobject.h @@ -1,74 +1,74 @@ /* Copyright 2009 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. */ #ifndef KDEVPLATFORM_KSHAREDOBJECT_H #define KDEVPLATFORM_KSHAREDOBJECT_H #include -#include +#include namespace KDevelop { struct FakeAtomic { inline FakeAtomic(QObject& object, QSharedData& real) : m_object(object), m_real(real) { } inline operator int() const { const int value = m_real.ref.loadAcquire(); if(value == 0) return 1; //Always return true, because we handle the deleting by ourself using deleteLater return value; } inline bool ref() { return m_real.ref.ref(); } inline bool deref() { bool ret = m_real.ref.deref(); if(!ret) m_object.deleteLater(); return true; //Always return true, because we handle the deleting by ourself } inline int load() const { return m_real.ref.load(); } QObject& m_object; QSharedData& m_real; }; /** * Wrapper around QSharedData for use with KSharedPtr when the object is based on QObject as well. * Instead of deleting the object once the reference-count reaches zero, QObject::deleteLater() is called. * This prevents a possible crash when the reference-count reaches zero during event-processing while the queue * contains events to be delivered to that object. * * Notice however that the object will not be deleted immediately, which may lead to unintended behavior. */ struct KSharedObject : public QSharedData { inline explicit KSharedObject(QObject& object) : ref(object, *this) { } mutable FakeAtomic ref; }; } #endif diff --git a/util/processlinemaker.cpp b/util/processlinemaker.cpp index ed84a7aae..d444a4e2b 100644 --- a/util/processlinemaker.cpp +++ b/util/processlinemaker.cpp @@ -1,132 +1,132 @@ /* This file is part of the KDE project Copyright 2002 John Firebaugh Copyright 2007 Andreas Pakulat Copyright 2007 Oswald Buddenhagen 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 "processlinemaker.h" -#include -#include +#include +#include namespace KDevelop { class ProcessLineMakerPrivate { public: QByteArray stdoutbuf; QByteArray stderrbuf; ProcessLineMaker* p; QProcess* m_proc; explicit ProcessLineMakerPrivate( ProcessLineMaker* maker ) : p(maker) { } void slotReadyReadStdout() { stdoutbuf += m_proc->readAllStandardOutput(); processStdOut(); } static QStringList streamToStrings(QByteArray &data) { QStringList lineList; int pos; while ( (pos = data.indexOf('\n')) != -1) { if (pos > 0 && data.at(pos - 1) == '\r') lineList << QString::fromLocal8Bit(data, pos - 1); else lineList << QString::fromLocal8Bit(data, pos); data.remove(0, pos+1); } return lineList; } void processStdOut() { emit p->receivedStdoutLines(streamToStrings(stdoutbuf)); } void slotReadyReadStderr() { stderrbuf += m_proc->readAllStandardError(); processStdErr(); } void processStdErr() { emit p->receivedStderrLines(streamToStrings(stderrbuf)); } }; ProcessLineMaker::ProcessLineMaker(QObject* parent) : QObject(parent) , d( new ProcessLineMakerPrivate( this ) ) { } ProcessLineMaker::ProcessLineMaker( QProcess* proc, QObject* parent ) : QObject(parent) , d( new ProcessLineMakerPrivate( this ) ) { d->m_proc = proc; connect(proc, &QProcess::readyReadStandardOutput, this, [&] { d->slotReadyReadStdout(); } ); connect(proc, &QProcess::readyReadStandardError, this, [&] { d->slotReadyReadStderr(); } ); } ProcessLineMaker::~ProcessLineMaker() { delete d; } void ProcessLineMaker::slotReceivedStdout( const QByteArray& buffer ) { d->stdoutbuf += buffer; d->processStdOut(); } void ProcessLineMaker::slotReceivedStderr( const QByteArray& buffer ) { d->stderrbuf += buffer; d->processStdErr(); } void ProcessLineMaker::discardBuffers( ) { d->stderrbuf.truncate(0); d->stdoutbuf.truncate(0); } void ProcessLineMaker::flushBuffers() { if (!d->stdoutbuf.isEmpty()) emit receivedStdoutLines(QStringList(QString::fromLocal8Bit(d->stdoutbuf))); if (!d->stderrbuf.isEmpty()) emit receivedStderrLines(QStringList(QString::fromLocal8Bit(d->stderrbuf))); discardBuffers(); } } #include "moc_processlinemaker.cpp" diff --git a/util/processlinemaker.h b/util/processlinemaker.h index b9cdf871e..ff718f551 100644 --- a/util/processlinemaker.h +++ b/util/processlinemaker.h @@ -1,118 +1,118 @@ /* This file is part of the KDE project Copyright 2002 John Firebaugh Copyright 2007 Andreas Pakulat Copyright 2007 Oswald Buddenhagen 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. */ #ifndef _PROCESSLINEMAKER_H_ #define _PROCESSLINEMAKER_H_ -#include +#include #include "utilexport.h" /** @file processlinemaker.h Utility objects for process output views. */ class QProcess; class QStringList; /** Convenience class to catch output of QProcess. */ namespace KDevelop { class KDEVPLATFORMUTIL_EXPORT ProcessLineMaker : public QObject { Q_OBJECT public: explicit ProcessLineMaker( QObject* parent = nullptr ); explicit ProcessLineMaker( QProcess* process, QObject* parent = nullptr ); ~ProcessLineMaker() override; /** * clears out the internal buffers, this drops any data without * emitting the related signal */ void discardBuffers(); /** * Flush the data from the buffers and then clear them. * This should be called once when the process has * exited to make sure all data that was received from the * process is properly converted and emitted. * * Note: Connecting this class to the process finished signal * is not going to work, as the user of this class will do * that itself too and possibly delete the process, making * it impossible to fetch the last output. */ void flushBuffers(); public Q_SLOTS: /** * This should be used (instead of hand-crafted code) when * you need to do custom things with the process output * before feeding it to the linemaker and have it convert * it to QString lines. * @param buffer the output from the process */ void slotReceivedStdout( const QByteArray& buffer ); /** * This should be used (instead of hand-crafted code) when * you need to do custom things with the process error output * before feeding it to the linemaker and have it convert * it to QString lines. * @param buffer the output from the process */ void slotReceivedStderr( const QByteArray& buffer ); Q_SIGNALS: /** * Emitted whenever the process prints something * to its standard output. The output is converted * to a QString using fromLocal8Bit() and will * be split on '\n'. * @param lines the lines that the process printed */ void receivedStdoutLines( const QStringList& lines ); /** * Emitted whenever the process prints something * to its error output. The output is converted * to a QString using fromLocal8Bit() and will * be split on '\n'. * @param lines the lines that the process printed */ void receivedStderrLines( const QStringList& lines ); private: class ProcessLineMakerPrivate* const d; friend class ProcessLineMakerPrivate; }; } #endif diff --git a/util/shellutils.cpp b/util/shellutils.cpp index bad302514..ce391e5db 100644 --- a/util/shellutils.cpp +++ b/util/shellutils.cpp @@ -1,109 +1,109 @@ /* * This file is part of KDevelop * * Copyright 2012 Ivan Shapovalov * * 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 "shellutils.h" #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include #include #include namespace KDevelop { bool askUser( const QString& mainText, const QString& ttyPrompt, const QString& mboxTitle, const QString& mboxAdditionalText, bool ttyDefaultToYes ) { if( !qobject_cast(qApp) ) { // no ui-mode e.g. for duchainify and other tools QTextStream out( stdout ); out << mainText << endl; QTextStream in( stdin ); QString input; forever { if( ttyDefaultToYes ) { out << QStringLiteral( "%1: [Y/n] " ).arg( ttyPrompt ) << flush; } else { out << QStringLiteral( "%1: [y/N] ").arg( ttyPrompt ) << flush; } input = in.readLine().trimmed(); if( input.isEmpty() ) { return ttyDefaultToYes; } else if( input.toLower() == QLatin1String("y") ) { return true; } else if( input.toLower() == QLatin1String("n") ) { return false; } } } else { int userAnswer = KMessageBox::questionYesNo( ICore::self()->uiController()->activeMainWindow(), mainText + "\n\n" + mboxAdditionalText, mboxTitle, KStandardGuiItem::ok(), KStandardGuiItem::cancel() ); return userAnswer == KMessageBox::Yes; } } bool ensureWritable( const QList &urls ) { QStringList notWritable; foreach (QUrl url, urls) { if (url.isLocalFile()) { QFile file(url.toLocalFile()); if (file.exists() && !(file.permissions() & QFileDevice::WriteOwner) && !(file.permissions() & QFileDevice::WriteGroup)) { notWritable << url.toLocalFile(); } } } if (!notWritable.isEmpty()) { int answer = KMessageBox::questionYesNoCancel(ICore::self()->uiController()->activeMainWindow(), i18n("You don't have write permissions for the following files; add write permissions for owner before saving?")+"\n\n"+notWritable.join("\n"), i18n("Some files are write-protected"), KStandardGuiItem::yes(), KStandardGuiItem::no(), KStandardGuiItem::cancel()); if (answer == KMessageBox::Yes) { bool success = true; foreach (QString filename, notWritable) { QFile file(filename); QFileDevice::Permissions permissions = file.permissions(); permissions |= QFileDevice::WriteOwner; success &= file.setPermissions(permissions); } if (!success) { KMessageBox::error(ICore::self()->uiController()->activeMainWindow(), i18n("Failed adding write permissions for some files."), i18n("Failed setting permissions")); return false; } } return answer != KMessageBox::Cancel; } return true; } } diff --git a/util/shellutils.h b/util/shellutils.h index fec3e43dc..387bfa746 100644 --- a/util/shellutils.h +++ b/util/shellutils.h @@ -1,53 +1,53 @@ /* * This file is part of KDevelop * * Copyright 2012 Ivan Shapovalov * * 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. */ #ifndef SHELLUTILS_H #define SHELLUTILS_H #include "utilexport.h" -#include +#include class QString; class QUrl; namespace KDevelop { /** * Asks user of an arbitrary question by using either a \ref KMessageBox or stdin/stderr. * * @return @c true if user chose "Yes" and @c false otherwise. */ bool KDEVPLATFORMUTIL_EXPORT askUser( const QString& mainText, const QString& ttyPrompt, const QString& mboxTitle, const QString& mboxAdditionalText, bool ttyDefaultToYes = true ); /** * Ensures that the given list of files is writable. If some files are not writable, * asks the user whether they should be made writable. If the user disagrees, * or if the operation failed, returns false. * */ bool KDEVPLATFORMUTIL_EXPORT ensureWritable( const QList &urls ); } #endif // SHELLUTILS_H diff --git a/vcs/dvcs/dvcsevent.h b/vcs/dvcs/dvcsevent.h index 1c5166282..f6d3fb6dc 100644 --- a/vcs/dvcs/dvcsevent.h +++ b/vcs/dvcs/dvcsevent.h @@ -1,148 +1,148 @@ /* This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * Copyright 2007 Matthew Woehlke * * 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. */ #ifndef KDEVPLATFORM_DVCSEVENT_H #define KDEVPLATFORM_DVCSEVENT_H -#include +#include /** * This class is used to store all required Commit(Revision) data: commit (sha string), * log (log[0] is used as shortlog), author, date (in QString), CommitType, and a special properties mask. * It's mostly used in CommitViewDelegate. Maybe this class can be merged with * something like KDevelop::VcsEvent. * This is also a helper class for the LogView::parseOutput() method. * * @note DVcsEvent is used just to store the data, it doesn't change any of it (for example with QString::trimmed()) * * @see GitExecutor::getAllCommits() * @see end of CommitViewDelegate::paintGraph() for details of how properties are used * @see dvcsEcecutor::parseOutput() * * @author Evgeniy Ivanov * */ //TODO: properties should be used instead of type class DVcsEvent { public: /** * The CommitType namespace specifies the type of commit. It's mostly used in CommitViewDelegate to * choose what to draw in each graph (and for creation of HEADs labels in shortlog). * Every commit has a properties array of CommitType (which can be called mask), * which shows its state in every branch. for example: * if branch has this commit (after merge several branches can have one commit) properties[branch] is set to BRANCH. */ enum CommitType { INITIAL, /**< Initial (first) commit, we should'n draw bottom line to connect with parent */ HEAD, /**< HEAD commit, we should use its for branch label, and shouldn't draw head line to connect with child */ BRANCH, /**< draw a circle in the branch column */ MERGE, /**< draw a square (current implementation requires drawing connections */ CROSS, /**< just draw a cross-line */ HCROSS, /**< draw horizontal cross (it's used to connect merge with parent/child */ MERGE_RIGHT, /**< draw connection lines, two lines (like a clock 9-center-12) */ MERGE_LEFT, /**< draw connection lines, 9-center-6) */ EMPTY /**< draw nothing */ }; /** Returns sha string of the commit. */ QString getCommit() const {return commit;} /** Sets sha string. */ void setCommit(const QString &_commit_) { commit = _commit_; } /** Returns QStringList with parents (sha strings). */ QStringList getParents() const {return parents;} /** Sets parents (sha strings). */ void setParents(const QStringList &_parents_) { parents = _parents_; switch (parents.count()) { case 0: { setType(INITIAL); break; } case 1: { setType(BRANCH); break; } default: // > 1 { setType(MERGE); break; } } } /** Returns commit date (stored in QString). */ QString getDate() const {return date;} /** Sets date. */ void setDate(const QString &_date_) {date = _date_;} /** Returns author (committer) name. */ QString getAuthor() const {return author;} /** Sets author (committer) name. */ void setAuthor(const QString &_author_) {author = _author_;} /** Returns full log in one QString. */ QString getLog() const {return log;} /** Sets full log in one QString. */ void setLog(const QString &_log_) {log = _log_;} /** Returns CommitType */ int getType() const {return type;} /** Sets CommitType */ void setType(CommitType t) {type = t;} /** Returns list of properties */ QListgetProperties() const {return properties;} /** Sets properties */ void setProperties(const QList &prop) {properties = prop;} /** Sets property * @param index index in the properties array. * @param prop value to be set */ void setPropetry(const int index, const int prop) {if (index >= 0 && index < properties.count())properties[index] = prop;} private: int type; private: QString commit; QStringList parents; QString date; QString author; QString log; QListproperties; //used to describe graph columns in every row (MERGE, HEAD, CROSS, etc) }; #endif diff --git a/vcs/dvcs/dvcsjob.cpp b/vcs/dvcs/dvcsjob.cpp index 11945a5d9..67d6a12a2 100644 --- a/vcs/dvcs/dvcsjob.cpp +++ b/vcs/dvcs/dvcsjob.cpp @@ -1,331 +1,331 @@ /*************************************************************************** * This file was partly taken from KDevelop's cvs plugin * * Copyright 2002-2003 Christian Loose * * Copyright 2007 Robert Gruber * * * * Adapted for DVCS * * Copyright 2008 Evgeniy Ivanov * * Copyright Aleix Pol Gonzalez * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #include "dvcsjob.h" #include "../debug.h" -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include #include #include using namespace KDevelop; struct DVcsJobPrivate { DVcsJobPrivate() : childproc(new KProcess), vcsplugin(nullptr), ignoreError(false) {} ~DVcsJobPrivate() { delete childproc; } KProcess* childproc; VcsJob::JobStatus status; QByteArray output; QByteArray errorOutput; IPlugin* vcsplugin; QVariant results; OutputModel* model; bool ignoreError; }; DVcsJob::DVcsJob(const QDir& workingDir, IPlugin* parent, OutputJob::OutputJobVerbosity verbosity) : VcsJob(parent, verbosity), d(new DVcsJobPrivate) { Q_ASSERT(workingDir.exists()); d->status = JobNotStarted; d->vcsplugin = parent; d->childproc->setWorkingDirectory(workingDir.absolutePath()); d->model = new OutputModel; d->ignoreError = false; setModel(d->model); setCapabilities(Killable); connect(d->childproc, static_cast(&KProcess::finished), this, &DVcsJob::slotProcessExited); connect(d->childproc, static_cast(&KProcess::error), this, &DVcsJob::slotProcessError); connect(d->childproc, &KProcess::readyReadStandardOutput, this, &DVcsJob::slotReceivedStdout); } DVcsJob::~DVcsJob() { delete d; } QDir DVcsJob::directory() const { return QDir(d->childproc->workingDirectory()); } DVcsJob& DVcsJob::operator<<(const QString& arg) { *d->childproc << arg; return *this; } DVcsJob& DVcsJob::operator<<(const char* arg) { *d->childproc << arg; return *this; } DVcsJob& DVcsJob::operator<<(const QStringList& args) { *d->childproc << args; return *this; } QStringList DVcsJob::dvcsCommand() const { return d->childproc->program(); } QString DVcsJob::output() const { QByteArray stdoutbuf = rawOutput(); int endpos = stdoutbuf.size(); if (d->status==JobRunning) { // We may have received only part of a code-point. apol: ASSERT? endpos = stdoutbuf.lastIndexOf('\n')+1; // Include the final newline or become 0, when there is no newline } return QString::fromLocal8Bit(stdoutbuf, endpos); } QByteArray DVcsJob::rawOutput() const { return d->output; } QByteArray DVcsJob::errorOutput() const { return d->errorOutput; } void DVcsJob::setIgnoreError(bool ignore) { d->ignoreError = ignore; } void DVcsJob::setResults(const QVariant &res) { d->results = res; } QVariant DVcsJob::fetchResults() { return d->results; } void DVcsJob::start() { Q_ASSERT_X(d->status != JobRunning, "DVCSjob::start", "Another proccess was started using this job class"); const QDir& workingdir = directory(); if( !workingdir.exists() ) { QString error = i18n( "Working Directory does not exist: %1", d->childproc->workingDirectory() ); d->model->appendLine(error); setError( 255 ); setErrorText(error); d->status = JobFailed; emitResult(); return; } if( !workingdir.isAbsolute() ) { QString error = i18n( "Working Directory is not absolute: %1", d->childproc->workingDirectory() ); d->model->appendLine(error); setError( 255 ); setErrorText(error); d->status = JobFailed; emitResult(); return; } QString commandDisplay = KShell::joinArgs(dvcsCommand()); qCDebug(VCS) << "Execute dvcs command:" << commandDisplay; QString service; if(d->vcsplugin) service = d->vcsplugin->objectName(); setObjectName(service+": "+commandDisplay); d->status = JobRunning; d->childproc->setOutputChannelMode(KProcess::SeparateChannels); //the started() and error() signals may be delayed! It causes crash with deferred deletion!!! d->childproc->start(); d->model->appendLine(directory().path() + "> " + commandDisplay); } void DVcsJob::setCommunicationMode(KProcess::OutputChannelMode comm) { d->childproc->setOutputChannelMode(comm); } void DVcsJob::cancel() { d->childproc->kill(); } void DVcsJob::slotProcessError( QProcess::ProcessError err ) { d->status = JobFailed; setError(OutputJob::FailedShownError); //we don't want to trigger a message box d->errorOutput = d->childproc->readAllStandardError(); QString displayCommand = KShell::joinArgs(dvcsCommand()); QString completeErrorText = i18n("Process '%1' exited with status %2\n%3", displayCommand, d->childproc->exitCode(), QString::fromLocal8Bit(d->errorOutput) ); setErrorText( completeErrorText ); QString errorValue; //if trolls add Q_ENUMS for QProcess, then we can use better solution than switch: //QMetaObject::indexOfEnumerator(char*), QQStringLiteral(QMetaEnum::valueToKey())... switch (err) { case QProcess::FailedToStart: errorValue = QStringLiteral("FailedToStart"); break; case QProcess::Crashed: errorValue = QStringLiteral("Crashed"); break; case QProcess::Timedout: errorValue = QStringLiteral("Timedout"); break; case QProcess::WriteError: errorValue = QStringLiteral("WriteError"); break; case QProcess::ReadError: errorValue = QStringLiteral("ReadError"); break; case QProcess::UnknownError: errorValue = QStringLiteral("UnknownError"); break; } qCDebug(VCS) << "Found an error while running" << displayCommand << ":" << errorValue << "Exit code is:" << d->childproc->exitCode(); qCDebug(VCS) << "Error:" << completeErrorText; displayOutput(QString::fromLocal8Bit(d->errorOutput)); d->model->appendLine(i18n("Command finished with error %1.", errorValue)); if(verbosity()==Silent) { setVerbosity(Verbose); startOutput(); } emitResult(); } void DVcsJob::slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus) { d->status = JobSucceeded; d->model->appendLine(i18n("Command exited with value %1.", exitCode)); if (exitStatus == QProcess::CrashExit) slotProcessError(QProcess::Crashed); else if (exitCode != 0 && !d->ignoreError) slotProcessError(QProcess::UnknownError); else jobIsReady(); } void DVcsJob::displayOutput(const QString& data) { d->model->appendLines(data.split('\n')); } void DVcsJob::slotReceivedStdout() { QByteArray output = d->childproc->readAllStandardOutput(); // accumulate output d->output.append(output); displayOutput(QString::fromLocal8Bit(output)); } VcsJob::JobStatus DVcsJob::status() const { return d->status; } IPlugin* DVcsJob::vcsPlugin() const { return d->vcsplugin; } DVcsJob& DVcsJob::operator<<(const QUrl& url) { *d->childproc << url.toLocalFile(); return *this; } DVcsJob& DVcsJob::operator<<(const QList< QUrl >& urls) { foreach(const QUrl &url, urls) operator<<(url); return *this; } bool DVcsJob::doKill() { if (d->childproc->state() == QProcess::NotRunning) { return true; } static const int terminateKillTimeout = 1000; // ms d->childproc->terminate(); bool terminated = d->childproc->waitForFinished( terminateKillTimeout ); if( !terminated ) { d->childproc->kill(); terminated = d->childproc->waitForFinished( terminateKillTimeout ); } return terminated; } void DVcsJob::jobIsReady() { emit readyForParsing(this); //let parsers to set status emitResult(); //KJob emit resultsReady(this); //VcsJob } KProcess* DVcsJob::process() {return d->childproc;} diff --git a/vcs/dvcs/dvcsjob.h b/vcs/dvcs/dvcsjob.h index 9eeb6b159..1a8c1d14c 100644 --- a/vcs/dvcs/dvcsjob.h +++ b/vcs/dvcs/dvcsjob.h @@ -1,230 +1,230 @@ /*************************************************************************** * This file was partly taken from KDevelop's cvs plugin * * Copyright 2002-2003 Christian Loose * * Copyright 2007 Robert Gruber * * * * Adapted for DVCS * * Copyright 2008 Evgeniy Ivanov * * Copyright 2010 Aleix Pol Gonzalez * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #ifndef KDEVPLATFORM_DVCS_JOB_H #define KDEVPLATFORM_DVCS_JOB_H -#include +#include #include #include #include "../vcsjob.h" class QDir; class QStringList; struct DVcsJobPrivate; /** * This class is capable of running our dvcs commands. * Most of all DVcsJob are created in DVCS executors, but executed in DistributedVersionControlPlugin or * any managers like BranchManager. * @note Connect to KJob::result(KJob*) to be notified when the job finished. * * How to create DVcsJob: * @code * DVcsJob* job = new DVcsJob(vcsplugin); * * job->setDirectory(workDir); * *job << "git-rev-parse"; * foreach(const QString &arg, args) // *job << args can be used instead! * *job << arg; * return job; * * return error_cmd(i18n("could not create the job")); * @endcode * * Usage example 1: * @code * VcsJob* j = add(QList() << a << b << c, IBasicVersionControl::Recursive); * DVcsJob* job = qobject_cast(j); * connect(job, SIGNAL(result(KJob*) ), * this, SIGNAL(jobFinished(KJob*) )); * ICore::self()->runController()->registerJob(job); * @endcode * * Usage example 2, asyunchronous: * @code * DVcsJob* branchJob = d->branch(repo, baseBranch, newBranch); * * if (job->exec() && job->status() == KDevelop::VcsJob::JobSucceeded) * return true; * else * //something, maybe even just * return false * @endcode * * @author Robert Gruber * @author Evgeniy Ivanov */ namespace KDevelop { class KDEVPLATFORMVCS_EXPORT DVcsJob : public KDevelop::VcsJob { Q_OBJECT public: explicit DVcsJob(const QDir& workingDir, KDevelop::IPlugin* parent=nullptr, KDevelop::OutputJob::OutputJobVerbosity verbosity = KDevelop::OutputJob::Verbose); ~DVcsJob() override; /** * Returns current working directory. */ QDir directory() const; /** * Call this method to set command to execute and its arguments. * @note Don't forget <<"one two"; is not the same as <<"one"<<"two"; Use one word(command, arg) per one QString! */ DVcsJob& operator<<(const QString& arg); /** * Overloaded convenience function. * @see operator<<(const QString& arg). */ DVcsJob& operator<<(const char* arg); /** * Overloaded convenience function. * @see operator<<(const QString& arg). */ DVcsJob& operator<<(const QStringList& args); /** * Overloaded operator << for url's, can be used to pass files and * makes arguments absolute to the process working directory * * Override if you need to treat paths beffore adding them as parameters. */ virtual DVcsJob& operator<<(const QUrl& arg); /** * @see operator<<(const QUrl& arg). */ DVcsJob& operator<<(const QList& args); /** * Call this method to start this job. * @note Default communication mode is KProcess::AllOutput. * @see Use setCommunicationMode() to override the default communication mode. */ void start() override; /** * In some cases it's needed to specify the communication mode between the * process and the job object. This is for instance done for the "git status" * command. If stdout and stderr are processed as separate streams, their signals * do not always get emitted in correct order by KProcess, which will lead to a * screwed up output. * @note Default communication mode is KProcess::SeparateChannels. */ void setCommunicationMode(KProcess::OutputChannelMode comm); /** * @return The command that is executed when calling start(). */ QStringList dvcsCommand() const; /** * @return The whole output of the job as a string. (Might fail on binary data) */ QString output() const; /** * @return The whole binary output of the job */ QByteArray rawOutput() const; /** * @return The whole binary stderr output of the job. */ QByteArray errorOutput() const; /** * Ignore a non-zero exit code depending on @p ignore. */ void setIgnoreError(bool ignore); // Begin: KDevelop::VcsJob /** * Sets executions results. * In most cases this method is used by IDVCSexecutor * @see fetchResults() */ virtual void setResults(const QVariant &res); /** * Returns execution results stored in QVariant. * Mostly used in vcscommitdialog. * @see setResults(const QVariant &res) */ QVariant fetchResults() override; /** * Returns JobStatus * @see KDevelop::VcsJob::JobStatus */ KDevelop::VcsJob::JobStatus status() const override; /** * Returns pointer to IPlugin (which was used to create a job). */ KDevelop::IPlugin* vcsPlugin() const override; // End: KDevelop::VcsJob KProcess *process(); void displayOutput(const QString& output); public Q_SLOTS: /** * Cancel slot. */ void cancel(); Q_SIGNALS: void readyForParsing(KDevelop::DVcsJob *job); protected Q_SLOTS: virtual void slotProcessError( QProcess::ProcessError ); private Q_SLOTS: void slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus); void slotReceivedStdout(); protected: bool doKill() override; private: void jobIsReady(); DVcsJobPrivate* const d; }; } #endif diff --git a/vcs/dvcs/dvcsplugin.cpp b/vcs/dvcs/dvcsplugin.cpp index 6f7bbd06c..d1be44f40 100644 --- a/vcs/dvcs/dvcsplugin.cpp +++ b/vcs/dvcs/dvcsplugin.cpp @@ -1,138 +1,138 @@ /*************************************************************************** * This file was partly taken from KDevelop's cvs plugin * * Copyright 2007 Robert Gruber * * * * Adapted for DVCS (added templates) * * Copyright 2008 Evgeniy Ivanov * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #ifndef DVCS_PLUGIN_CC #define DVCS_PLUGIN_CC #include "dvcsplugin.h" #include -#include -#include +#include +#include #include #include #include #include #include #include #include #include "dvcsjob.h" #include "ui/dvcsimportmetadatawidget.h" #include "ui/branchmanager.h" #include namespace KDevelop { struct DistributedVersionControlPluginPrivate { explicit DistributedVersionControlPluginPrivate(DistributedVersionControlPlugin * pThis) : m_common(new VcsPluginHelper(pThis, pThis)) {} ~DistributedVersionControlPluginPrivate() { delete m_common; } VcsPluginHelper* m_common; }; //class DistributedVersionControlPlugin DistributedVersionControlPlugin::DistributedVersionControlPlugin(QObject *parent, const QString& componentName) : IPlugin(componentName, parent) , d(new DistributedVersionControlPluginPrivate(this)) {} DistributedVersionControlPlugin::~DistributedVersionControlPlugin() { //TODO: Find out why this crashes on the svn tests delete d->m_factory; delete d; } // End: KDevelop::IBasicVersionControl // Begin: KDevelop::IDistributedVersionControl // End: KDevelop::IDistributedVersionControl KDevelop::VcsImportMetadataWidget* DistributedVersionControlPlugin::createImportMetadataWidget(QWidget* parent) { return new DvcsImportMetadataWidget(parent); } KDevelop::ContextMenuExtension DistributedVersionControlPlugin::contextMenuExtension(Context* context) { d->m_common->setupFromContext(context); QList const & ctxUrlList = d->m_common->contextUrlList(); bool isWorkingDirectory = false; foreach(const QUrl &url, ctxUrlList) { if (isValidDirectory(url)) { isWorkingDirectory = true; break; } } if (!isWorkingDirectory) { // Not part of a repository return ContextMenuExtension(); } QMenu * menu = d->m_common->commonActions(); menu->addSeparator(); menu->addAction(i18n("Branches..."), this, SLOT(ctxBranchManager()))->setEnabled(ctxUrlList.count()==1); additionalMenuEntries(menu, ctxUrlList); ContextMenuExtension menuExt; menuExt.addAction(ContextMenuExtension::VcsGroup, menu->menuAction()); return menuExt; } void DistributedVersionControlPlugin::additionalMenuEntries(QMenu* /*menu*/, const QList& /*urls*/) {} static QString stripPathToDir(const QString &path) { QFileInfo info = QFileInfo(path); return info.isDir() ? info.absoluteFilePath() : info.absolutePath(); } void DistributedVersionControlPlugin::ctxBranchManager() { QList const & ctxUrlList = d->m_common->contextUrlList(); Q_ASSERT(!ctxUrlList.isEmpty()); ICore::self()->documentController()->saveAllDocuments(); BranchManager branchManager(stripPathToDir(ctxUrlList.front().toLocalFile()), this, core()->uiController()->activeMainWindow()); branchManager.exec(); } } #endif diff --git a/vcs/dvcs/tests/test_dvcsjob.h b/vcs/dvcs/tests/test_dvcsjob.h index 72924c6b4..7ae3e4086 100644 --- a/vcs/dvcs/tests/test_dvcsjob.h +++ b/vcs/dvcs/tests/test_dvcsjob.h @@ -1,36 +1,36 @@ /*************************************************************************** * Copyright 2008 Evgeniy Ivanov * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #ifndef KDEVPLATFORM_TEST_DVCSJOB_H #define KDEVPLATFORM_TEST_DVCSJOB_H -#include +#include class TestDVcsJob: public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testJob(); }; #endif diff --git a/vcs/models/vcseventmodel.h b/vcs/models/vcseventmodel.h index e1ac7e578..a6c8431e7 100644 --- a/vcs/models/vcseventmodel.h +++ b/vcs/models/vcseventmodel.h @@ -1,96 +1,96 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_VCSEVENTMODEL_H #define KDEVPLATFORM_VCSEVENTMODEL_H -#include +#include #include #include class QUrl; class KJob; namespace KDevelop { class VcsRevision; class IBasicVersionControl; class VcsEvent; /** * This is a generic model to store a list of VcsEvents. * * To add events use @c addEvents */ class KDEVPLATFORMVCS_EXPORT VcsBasicEventModel : public QAbstractTableModel { Q_OBJECT public: enum Column { RevisionColumn, SummaryColumn, AuthorColumn, DateColumn, ColumnCount, }; explicit VcsBasicEventModel(QObject* parent); ~VcsBasicEventModel() override; int rowCount(const QModelIndex& = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const override; QVariant headerData(int, Qt::Orientation, int role = Qt::DisplayRole) const override; KDevelop::VcsEvent eventForIndex(const QModelIndex&) const; protected: void addEvents(const QList&); private: QScopedPointer d; }; /** * This model stores a list of VcsEvents corresponding to the log obtained * via IBasicVersionControl::log for a given revision. The model is populated * lazily via @c fetchMore. */ class KDEVPLATFORMVCS_EXPORT VcsEventLogModel : public VcsBasicEventModel { Q_OBJECT public: VcsEventLogModel(KDevelop::IBasicVersionControl* iface, const KDevelop::VcsRevision& rev, const QUrl& url, QObject* parent); ~VcsEventLogModel() override; /// Adds events to the model via @sa IBasicVersionControl::log void fetchMore(const QModelIndex& parent) override; bool canFetchMore(const QModelIndex& parent) const override; private slots: void jobReceivedResults( KJob* job ); private: QScopedPointer d; }; } #endif diff --git a/vcs/vcsdiff.cpp b/vcs/vcsdiff.cpp index 9211dfdaa..492f3def0 100644 --- a/vcs/vcsdiff.cpp +++ b/vcs/vcsdiff.cpp @@ -1,188 +1,188 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * * * * 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 "vcsdiff.h" -#include -#include -#include +#include +#include +#include namespace KDevelop { class VcsDiffPrivate { public: QHash leftBinaries; QHash rightBinaries; QHash leftTexts; QHash rightTexts; QUrl baseDiff; QString diff; VcsDiff::Type type = VcsDiff::DiffDontCare; VcsDiff::Content content = VcsDiff::Text; uint depth = 0; }; VcsDiff::VcsDiff() : d(new VcsDiffPrivate) { } VcsDiff::~VcsDiff() { delete d; } VcsDiff::VcsDiff( const VcsDiff& rhs ) : d(new VcsDiffPrivate) { *d = *rhs.d; } bool VcsDiff::isEmpty() const { return d->diff.isEmpty() && d->leftBinaries.isEmpty() && d->rightBinaries.isEmpty() && d->leftTexts.isEmpty() && d->rightTexts.isEmpty(); } VcsDiff::Type VcsDiff::type() const { return d->type; } VcsDiff::Content VcsDiff::contentType() const { return d->content; } QHash VcsDiff::leftBinaries() const { return d->leftBinaries; } QHash VcsDiff::rightBinaries() const { return d->rightBinaries; } QHash VcsDiff::leftTexts() const { return d->leftTexts; } QHash VcsDiff::rightTexts() const { return d->rightTexts; } QString VcsDiff::diff() const { return d->diff; } void VcsDiff::setDiff( const QString& s ) { d->diff = s; } void VcsDiff::addLeftBinary( const VcsLocation& loc, const QByteArray& b ) { d->leftBinaries[loc] = b; } void VcsDiff::addRightBinary( const VcsLocation& loc, const QByteArray& b ) { d->rightBinaries[loc] = b; } void VcsDiff::removeLeftBinary( const VcsLocation& loc ) { d->leftBinaries.remove( loc ); } void VcsDiff::removeRightBinary( const VcsLocation& loc ) { d->rightBinaries.remove( loc ); } void VcsDiff::addLeftText( const VcsLocation& loc, const QString& b ) { d->leftTexts[loc] = b; } void VcsDiff::addRightText( const VcsLocation& loc, const QString& b ) { d->rightTexts[loc] = b; } void VcsDiff::removeLeftText( const VcsLocation& loc ) { d->leftTexts.remove( loc ); } void VcsDiff::removeRightText( const VcsLocation& loc ) { d->rightTexts.remove( loc ); } void VcsDiff::setType( VcsDiff::Type t ) { d->type = t; } void VcsDiff::setContentType( VcsDiff::Content c ) { d->content = c; } VcsDiff& VcsDiff::operator=( const VcsDiff& rhs) { if (this != &rhs) { *d = *rhs.d; } return *this; } QUrl VcsDiff::baseDiff() const { return d->baseDiff; } uint VcsDiff::depth() const { return d->depth; } void VcsDiff::setBaseDiff(const QUrl& url) const { d->baseDiff=url; } void VcsDiff::setDepth(const uint depth) const { d->depth = depth; } } diff --git a/vcs/vcsdiff.h b/vcs/vcsdiff.h index aadc6931c..0cfa29ce2 100644 --- a/vcs/vcsdiff.h +++ b/vcs/vcsdiff.h @@ -1,145 +1,145 @@ /* This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * Copyright 2007 Matthew Woehlke * * 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. */ #ifndef KDEVPLATFORM_VCSDIFF_H #define KDEVPLATFORM_VCSDIFF_H //Needed first as it provides a hash-function for QHash #include "vcslocation.h" -#include +#include #include "vcsexport.h" class QString; class QByteArray; namespace KDevelop { class KDEVPLATFORMVCS_EXPORT VcsDiff { public: /** * Specify the type of difference the diff() method should create. Note that a * request for DiffUnified may not be honored, e.g. if the items being diffed are * binary rather than text. */ enum Type { DiffRaw /**< Request complete copies of both items. */, DiffUnified /**< Request copy of first item with diff. */, DiffDontCare /**< Don't care; plugin will return whichever is easiest. */ }; enum Content { Binary /**< Binary diff, using the full content of both files.*/, Text /**< Textual diff.*/ }; VcsDiff(); virtual ~VcsDiff(); VcsDiff( const VcsDiff& ); /** * @returns the type of diff, i.e. raw or unified */ Type type() const; /** * @returns the content type, i.e. binary or text */ Content contentType() const; /** * @returns the binary content of the first file of the difference or * an empty QByteArray if this is a textual diff */ QHash leftBinaries() const; /** * @returns the binary content of the second file of the difference or * an empty QByteArray if this is a textual diff */ QHash rightBinaries() const; /** * @returns the textual content of the first file of the difference or * an empty QString if this is a binary diff */ QHash leftTexts() const; /** * @returns the textual content of the second file of the difference or * an empty QString if this is a unified or binary diff */ QHash rightTexts() const; /** * @returns the difference between the first and the second file in * unified diff format or an empty QString if this is a binary diff * or a textual diff using raw format */ QString diff() const; /** @returns the base directory of the diff. */ QUrl baseDiff() const; /** * Depth - number of directories to left-strip from paths in the patch - see "patch -p" * Defaults to 0 */ uint depth() const; /** Sets the base directory of the diff to the @p url */ void setBaseDiff(const QUrl& url) const; /** Sets the depth of the diff to @p depth */ void setDepth(const uint depth) const; void setDiff( const QString& ); void addLeftBinary( const KDevelop::VcsLocation&, const QByteArray& ); void removeLeftBinary( const KDevelop::VcsLocation& ); void addRightBinary( const KDevelop::VcsLocation&, const QByteArray& ); void removeRightBinary( const KDevelop::VcsLocation& ); void addLeftText( const KDevelop::VcsLocation&, const QString& ); void removeLeftText( const KDevelop::VcsLocation& ); void addRightText( const KDevelop::VcsLocation&, const QString& ); void removeRightText( const KDevelop::VcsLocation& ); void setType( Type ); void setContentType( Content ); VcsDiff& operator=( const VcsDiff& rhs); /** @returns whether there are not changes on the diff */ bool isEmpty() const; private: class VcsDiffPrivate* const d; }; } Q_DECLARE_METATYPE( KDevelop::VcsDiff ) #endif diff --git a/vcs/vcsevent.cpp b/vcs/vcsevent.cpp index f7afecd79..c26b01f68 100644 --- a/vcs/vcsevent.cpp +++ b/vcs/vcsevent.cpp @@ -1,209 +1,209 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * * * * 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 "vcsevent.h" -#include -#include -#include +#include +#include +#include #include #include "vcsrevision.h" namespace KDevelop { class VcsItemEventPrivate { public: QString location; QString sourceLocation; VcsRevision sourceRevision; VcsItemEvent::Actions actions; }; VcsItemEvent::VcsItemEvent() : d(new VcsItemEventPrivate) { } VcsItemEvent::~VcsItemEvent() { delete d; } VcsItemEvent::VcsItemEvent(const VcsItemEvent& rhs ) : d(new VcsItemEventPrivate) { d->actions = rhs.d->actions; d->sourceRevision = rhs.d->sourceRevision; d->sourceLocation = rhs.d->sourceLocation; d->location = rhs.d->location; } QString VcsItemEvent::repositoryLocation() const { return d->location; } QString VcsItemEvent::repositoryCopySourceLocation() const { return d->sourceLocation; } VcsRevision VcsItemEvent::repositoryCopySourceRevision() const { return d->sourceRevision; } VcsItemEvent::Actions VcsItemEvent::actions() const { return d->actions; } void VcsItemEvent::setRepositoryLocation( const QString& l ) { d->location = l; } void VcsItemEvent::setRepositoryCopySourceLocation( const QString& l ) { d->sourceLocation = l; } void VcsItemEvent::setRepositoryCopySourceRevision( const KDevelop::VcsRevision& rev ) { d->sourceRevision = rev; } void VcsItemEvent::setActions( VcsItemEvent::Actions a ) { d->actions = a; } VcsItemEvent& VcsItemEvent::operator=( const VcsItemEvent& rhs) { if(this == &rhs) return *this; d->actions = rhs.d->actions; d->sourceRevision = rhs.d->sourceRevision; d->sourceLocation = rhs.d->sourceLocation; d->location = rhs.d->location; return *this; } class VcsEventPrivate { public: VcsRevision revision; QString author; QString message; QDateTime date; QList items; }; VcsEvent::VcsEvent() : d(new VcsEventPrivate) { } VcsEvent::~VcsEvent() { delete d; } VcsEvent::VcsEvent( const VcsEvent& rhs ) : d(new VcsEventPrivate) { d->revision = rhs.d->revision; d->author = rhs.d->author; d->message = rhs.d->message; d->date = rhs.d->date; d->items = rhs.d->items; } VcsRevision VcsEvent::revision() const { return d->revision; } QString VcsEvent::author() const { return d->author; } QDateTime VcsEvent::date() const { return d->date; } QString VcsEvent::message() const { return d->message; } QList VcsEvent::items() const { return d->items; } void VcsEvent::setRevision( const VcsRevision& rev ) { d->revision = rev; } void VcsEvent::setAuthor( const QString& a ) { d->author = a; } void VcsEvent::setDate( const QDateTime& date ) { d->date = date; } void VcsEvent::setMessage(const QString& m ) { d->message = m; } void VcsEvent::setItems( const QList& l ) { d->items = l; } void VcsEvent::addItem(const VcsItemEvent& item) { d->items.append(item); } VcsEvent& VcsEvent::operator=( const VcsEvent& rhs) { if(this == &rhs) return *this; d->revision = rhs.d->revision; d->message = rhs.d->message; d->items = rhs.d->items; d->date = rhs.d->date; d->author = rhs.d->author; return *this; } } diff --git a/vcs/vcsevent.h b/vcs/vcsevent.h index 0c7288784..cb85f9e29 100644 --- a/vcs/vcsevent.h +++ b/vcs/vcsevent.h @@ -1,120 +1,120 @@ /* This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * Copyright 2007 Matthew Woehlke * * 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. */ #ifndef KDEVPLATFORM_VCSEVENT_H #define KDEVPLATFORM_VCSEVENT_H -#include +#include #include "vcsexport.h" class QString; class QDateTime; template class QList; namespace KDevelop { class VcsRevision; /** * Small container class that contains information about a history event of a * single repository item. */ class KDEVPLATFORMVCS_EXPORT VcsItemEvent { public: /** * Class that tells you what happened to a given repository location in a * specific revision. * * Combinations of some of the flags are possible, for example Add|Modified, * Copy|Modified or Merge|Modified, or when returned from VcsEvent::actions(). */ enum Action { Added = 1<<0 /**< Item was added. */, Deleted = 1<<1 /**< Item was deleted. */, Modified = 1<<2 /**< Item was modified, for example by editing. */, Copied = 1<<3 /**< Item was copied. */, Merged = 1<<4 /**< Item had changes merged into it. */, ContentsModified = 1<<5 /**< Directory was not changed (only contents changed). */, Replaced = 1<<6 /**< Item was replaced. */ }; Q_DECLARE_FLAGS( Actions, Action ) VcsItemEvent(); virtual ~VcsItemEvent(); VcsItemEvent(const VcsItemEvent& ); QString repositoryLocation() const; QString repositoryCopySourceLocation() const; // may be empty VcsRevision repositoryCopySourceRevision() const; // may be invalid, even if rCSL is not Actions actions() const; void setRepositoryLocation( const QString& ); void setRepositoryCopySourceLocation( const QString& ); void setRepositoryCopySourceRevision( const KDevelop::VcsRevision& ); void setActions( Actions ); VcsItemEvent& operator=( const VcsItemEvent& rhs); private: class VcsItemEventPrivate* const d; }; /** * Small container class that contains information about a single revision. * * @note log() only returns information about the specific item that was asked * about. When working with a VCS that supports atomic commits (i.e. where a * revision might affect more than one item), use change() to retrieve * information about all items affected by a particular revision. */ class KDEVPLATFORMVCS_EXPORT VcsEvent { public: VcsEvent(); virtual ~VcsEvent(); VcsEvent( const VcsEvent& ); VcsRevision revision() const; QString author() const; QDateTime date() const; QString message() const; QList items() const; void setRevision( const VcsRevision& ); void setAuthor( const QString& ); void setDate( const QDateTime& ); void setMessage(const QString& ); void setItems( const QList& ); void addItem(const VcsItemEvent& item); VcsEvent& operator=( const VcsEvent& rhs); private: class VcsEventPrivate* const d; }; } Q_DECLARE_OPERATORS_FOR_FLAGS( KDevelop::VcsItemEvent::Actions ) Q_DECLARE_METATYPE( KDevelop::VcsEvent ) Q_DECLARE_METATYPE( KDevelop::VcsItemEvent ) #endif diff --git a/vcs/vcslocation.cpp b/vcs/vcslocation.cpp index b1f9252b1..d24a69741 100644 --- a/vcs/vcslocation.cpp +++ b/vcs/vcslocation.cpp @@ -1,207 +1,207 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * * * * 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 "vcslocation.h" -#include +#include namespace KDevelop { class VcsLocationPrivate { public: QUrl m_localUrl; QString m_repoServer; QString m_repoPath; QString m_repoModule; QString m_repoBranch; QString m_repoTag; VcsLocation::LocationType m_type; QVariant m_userData; }; VcsLocation::VcsLocation() : d(new VcsLocationPrivate) { d->m_type = VcsLocation::LocalLocation; } VcsLocation::VcsLocation( const QUrl& u ) : d(new VcsLocationPrivate) { setLocalUrl( u ); } VcsLocation::VcsLocation( const QString& s ) : d(new VcsLocationPrivate) { setRepositoryServer( s ); } VcsLocation::~VcsLocation() { delete d; } VcsLocation::VcsLocation( const VcsLocation& rhs ) : d(new VcsLocationPrivate) { d->m_type = rhs.d->m_type; d->m_localUrl = rhs.d->m_localUrl; d->m_repoServer = rhs.d->m_repoServer; d->m_repoPath = rhs.d->m_repoPath; d->m_repoModule = rhs.d->m_repoModule; d->m_repoBranch = rhs.d->m_repoBranch; d->m_repoTag = rhs.d->m_repoTag; d->m_userData = rhs.d->m_userData; } VcsLocation& VcsLocation::operator=( const VcsLocation& rhs ) { if( &rhs == this ) return *this; d->m_type = rhs.d->m_type; d->m_localUrl = rhs.d->m_localUrl; d->m_repoServer = rhs.d->m_repoServer; d->m_repoPath = rhs.d->m_repoPath; d->m_repoModule = rhs.d->m_repoModule; d->m_repoBranch = rhs.d->m_repoBranch; d->m_repoTag = rhs.d->m_repoTag; d->m_userData = rhs.d->m_userData; return *this; } QUrl VcsLocation::localUrl() const { return d->m_localUrl; } QString VcsLocation::repositoryServer() const { return d->m_repoServer; } VcsLocation::LocationType VcsLocation::type() const { return d->m_type; } bool VcsLocation::isValid() const { return( ( d->m_localUrl.isValid() && d->m_type == VcsLocation::LocalLocation ) || ( !d->m_repoServer.isEmpty() && d->m_type == VcsLocation::RepositoryLocation ) ); } void VcsLocation::setLocalUrl( const QUrl& url ) { d->m_repoServer.clear(); d->m_repoModule.clear(); d->m_repoBranch.clear(); d->m_repoTag.clear(); d->m_repoPath.clear(); d->m_type = VcsLocation::LocalLocation; d->m_localUrl = url; } void VcsLocation::setRepositoryServer( const QString& location ) { d->m_repoServer = location; d->m_type = VcsLocation::RepositoryLocation; d->m_localUrl = QUrl(); } bool VcsLocation::operator==( const KDevelop::VcsLocation& rhs ) { return( type() == rhs.type() && repositoryServer() == rhs.repositoryServer() && localUrl() == rhs.localUrl() && repositoryPath() == rhs.repositoryPath() && repositoryModule() == rhs.repositoryModule() && repositoryBranch() == rhs.repositoryBranch() && repositoryTag() == rhs.repositoryTag() && userData() == rhs.userData() ); } QString VcsLocation::repositoryModule( ) const { return d->m_repoModule; } QString VcsLocation::repositoryTag( ) const { return d->m_repoTag; } QString VcsLocation::repositoryBranch( ) const { return d->m_repoBranch; } QString VcsLocation::repositoryPath( ) const { return d->m_repoPath; } void VcsLocation::setRepositoryModule( const QString & module ) { d->m_repoModule = module; d->m_type = VcsLocation::RepositoryLocation; d->m_localUrl.clear(); } void VcsLocation::setRepositoryBranch( const QString & branch ) { d->m_repoBranch = branch; d->m_type = VcsLocation::RepositoryLocation; d->m_localUrl.clear(); } void VcsLocation::setRepositoryTag( const QString & tag ) { d->m_repoTag = tag; d->m_type = VcsLocation::RepositoryLocation; d->m_localUrl.clear(); } void VcsLocation::setRepositoryPath( const QString & path ) { d->m_repoPath = path; d->m_type = VcsLocation::RepositoryLocation; d->m_localUrl.clear(); } QVariant VcsLocation::userData( ) const { return d->m_userData; } void VcsLocation::setUserData( const QVariant& data ) { d->m_type = VcsLocation::RepositoryLocation; d->m_localUrl.clear(); d->m_userData = data; } } diff --git a/vcs/vcsrevision.cpp b/vcs/vcsrevision.cpp index 887d5db37..fbc61fded 100644 --- a/vcs/vcsrevision.cpp +++ b/vcs/vcsrevision.cpp @@ -1,183 +1,183 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * * * * 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 "vcsrevision.h" -#include -#include -#include -#include +#include +#include +#include +#include namespace KDevelop { VcsRevision VcsRevision::createSpecialRevision( KDevelop::VcsRevision::RevisionSpecialType _type ) { VcsRevision rev; rev.setRevisionValue( QVariant::fromValue( _type ), VcsRevision::Special ); return rev; } class VcsRevisionPrivate { public: QVariant value; VcsRevision::RevisionType type; QMap internalValues; }; VcsRevision::VcsRevision() : d(new VcsRevisionPrivate) { d->type = VcsRevision::Invalid; } VcsRevision::VcsRevision( const VcsRevision& rhs ) : d(new VcsRevisionPrivate) { d->value = rhs.d->value; d->internalValues = rhs.d->internalValues; d->type = rhs.d->type; } VcsRevision::~VcsRevision() { delete d; } VcsRevision& VcsRevision::operator=( const VcsRevision& rhs) { if(this == &rhs) return *this; d->value = rhs.d->value; d->type = rhs.d->type; d->internalValues = rhs.d->internalValues; return *this; } void VcsRevision::setRevisionValue( const QVariant& rev, VcsRevision::RevisionType type ) { d->value = rev; d->type = type; } VcsRevision::RevisionType VcsRevision::revisionType() const { return d->type; } VcsRevision::RevisionSpecialType VcsRevision::specialType() const { Q_ASSERT(d->type==Special); return d->value.value(); } QVariant VcsRevision::revisionValue() const { return d->value; } QStringList VcsRevision::keys() const { return d->internalValues.keys(); } QVariant VcsRevision::getValue( const QString& key ) const { if( d->internalValues.contains(key) ) { return d->internalValues[key]; } return QVariant(); } void VcsRevision::setValue( const QString& key, const QVariant& value ) { d->internalValues[key] = value; } void VcsRevision::setType( RevisionType t) { d->type = t; } void VcsRevision::setSpecialType( RevisionSpecialType t) { d->value = QVariant(t); } void VcsRevision::setValue( const QVariant& v ) { d->value = v; } bool VcsRevision::operator==( const KDevelop::VcsRevision& rhs ) const { return ( d->type == rhs.d->type && d->value == rhs.d->value && d->internalValues == rhs.d->internalValues ); } QString VcsRevision::prettyValue() const { switch( revisionType() ) { case GlobalNumber: case FileNumber: return (revisionValue().type() == QVariant::String ? revisionValue().toString() : QString::number(revisionValue().toLongLong())); break; case Special: switch( revisionValue().value( ) ) { case VcsRevision::Head: return QStringLiteral("Head"); break; case VcsRevision::Base: return QStringLiteral("Base"); break; case VcsRevision::Working: return QStringLiteral("Working"); break; case VcsRevision::Previous: return QStringLiteral("Previous"); break; case VcsRevision::Start: return QStringLiteral("Start"); break; default: return QStringLiteral("User"); break; } break; case Date: return revisionValue().toDateTime().toString( Qt::LocalDate ); break; default: return revisionValue().toString(); break; } } } uint KDevelop::qHash( const KDevelop::VcsRevision& rev) { return rev.revisionValue().toULongLong(); } diff --git a/vcs/vcsrevision.h b/vcs/vcsrevision.h index 39702cbde..6e048e7d0 100644 --- a/vcs/vcsrevision.h +++ b/vcs/vcsrevision.h @@ -1,171 +1,171 @@ /* This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * Copyright 2007 Matthew Woehlke * * 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. */ #ifndef KDEVPLATFORM_VCSREVISION_H #define KDEVPLATFORM_VCSREVISION_H #include "vcsexport.h" -#include +#include class QStringList; class QString; namespace KDevelop { /** * Encapsulates a vcs revision number, date or range of revisions. * * The type of the QVariant value depends on the type of the revision, * the following table lists the standard types and the according datatype * in the QVariant: * * * * * * * *
Revision typeQVariant type
GlobalNumberqlonglong/QString
FileNumberqlonglong/QString
DateQDateTime
SpecialKDevelop::VcsRevision::RevisionSpecialType or int, see explanation below
* * The vcs plugins need to register the Revision and RevisionSpecialType with * qRegisterMetaType. * * Also Users of this class should set RevisionSpecialType QVariant values via * * setRevisionValue( qVariantFromValue( val ), KDevelop::VcsRevision::Special); * * instead of * * setRevisionValue( qVariantFromValue( val ), KDevelop::VcsRevision::Special); * * * If the latter method is used the QVariant will be an Integer, which might not * be handled by the vcs plugin and is possibly ambiguous with the qlonglong * parameters. * */ class KDEVPLATFORMVCS_EXPORT VcsRevision { public: /** * @note Not all VCS's support both FileNumber and GlobalNumber. For those * that don't, asking for one may give you the other, therefore you should * check which is returned. For example, CVS does not support GlobalNumber, * and Subversion does not support FileNumber, while Perforce supports both. */ enum RevisionType { Special = 0 /**< One of the special versions in RevisionSpecialType. */, GlobalNumber = 1 /**< Global repository version when item was last changed. */, FileNumber = 2 /**< Item's independent version number. */, Date = 3, /**< The date of the revision to check out */ Invalid = 4 /**< The type is not set, this is an invalid revision. */, UserType = 1000 /**< This should be used by subclasses as base for their own types. */ }; enum RevisionSpecialType { Head = 0 /**< Latest revision in the repository. */, Working = 1 /**< The local copy (including any changes made). */, Base = 2 /**< The repository source of the local copy. */, Previous = 3 /**< The version prior the other one (only valid in functions that take two revisions). */, Start = 4, /**< The first commit in a repository. */ UserSpecialType = 1000 /**< This should be used by subclasses as base for their own special types. */ }; VcsRevision(); virtual ~VcsRevision(); VcsRevision( const VcsRevision& ); VcsRevision& operator=( const VcsRevision& ); /** * Set the value of this revision */ void setRevisionValue( const QVariant& rev, RevisionType type ); /** * returns the type of the revision */ RevisionType revisionType() const; RevisionSpecialType specialType() const; /** * Return the value of this revision. * * See the class documentation for the different QVariant types */ QVariant revisionValue() const; /** * This returns the value of the revision, suitable for displaying to the * user. For numbers it just returns the number converted to a string, for * the special types it returns the literal value of the special type and * for a datetime value it returns a localized string of the datetime value. */ QString prettyValue() const; bool operator==( const KDevelop::VcsRevision&) const; /** * Helper function to create a vcs revision for one of the special types */ static VcsRevision createSpecialRevision( KDevelop::VcsRevision::RevisionSpecialType type ); protected: /** * Get the keys that make up the internal data of this revision instance */ QStringList keys() const; /** * get the value for a given key, this retrieves internal data and is * meant to be used by subclasses */ QVariant getValue( const QString& key ) const; /** * change the value of the given internal data */ void setValue( const QString& key, const QVariant& value ); /** * write methods for subclasses to easily set the type and value */ void setType( RevisionType t); void setSpecialType( RevisionSpecialType t); void setValue( const QVariant& ); private: class VcsRevisionPrivate* const d; }; KDEVPLATFORMVCS_EXPORT uint qHash( const KDevelop::VcsRevision& rev); } Q_DECLARE_METATYPE(KDevelop::VcsRevision) Q_DECLARE_METATYPE(KDevelop::VcsRevision::RevisionSpecialType) #endif diff --git a/vcs/vcsstatusinfo.cpp b/vcs/vcsstatusinfo.cpp index 35986495c..bcadf8e30 100644 --- a/vcs/vcsstatusinfo.cpp +++ b/vcs/vcsstatusinfo.cpp @@ -1,109 +1,109 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * * * * 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 "vcsstatusinfo.h" -#include +#include #include namespace KDevelop { class VcsStatusInfoPrivate { public: int state; QUrl url; }; VcsStatusInfo::VcsStatusInfo() : d( new VcsStatusInfoPrivate) { d->state = VcsStatusInfo::ItemUnknown; } VcsStatusInfo::~VcsStatusInfo() { delete d; } VcsStatusInfo::VcsStatusInfo( const VcsStatusInfo& rhs ) : d(new VcsStatusInfoPrivate) { d->state = rhs.d->state; d->url = rhs.d->url; } VcsStatusInfo& VcsStatusInfo::operator=( const VcsStatusInfo& rhs) { if(this == &rhs) return *this; d->state = rhs.d->state; d->url = rhs.d->url; return *this; } bool VcsStatusInfo::operator==( const KDevelop::VcsStatusInfo& rhs) const { return ( d->state == rhs.d->state && d->url == rhs.d->url ); } bool VcsStatusInfo::operator!=( const KDevelop::VcsStatusInfo& rhs) const { return !(operator==(rhs)); } void VcsStatusInfo::setUrl( const QUrl& url ) { d->url = url; } void VcsStatusInfo::setExtendedState( int newstate ) { d->state = newstate; } void VcsStatusInfo::setState( VcsStatusInfo::State state ) { d->state = state; } int VcsStatusInfo::extendedState() const { return d->state; } QUrl VcsStatusInfo::url() const { return d->url; } VcsStatusInfo::State VcsStatusInfo::state() const { return VcsStatusInfo::State(d->state); } } QDebug operator<<(QDebug s, const KDevelop::VcsStatusInfo& statusInfo) { s.nospace() << statusInfo.state() << "@" << statusInfo.url(); return s.space(); }