📜 ⬆️ ⬇️

TODO Plugin for QtCreator

So, for a long time, the respected Eugene wrote to us about the QtCreator hacks, and also indicated in his blog a document with detailed instructions for creating a plugin. And this weekend, being in a country house, cut off from civilization and the Internet, I decided to try to write my own plugin. The plugin is quite simple; it lists all the TODO, FIXME, and so on comments in the current open document. Below I will talk about how this plugin is written, and generally about writing plugins for QtCreator.
TODO Plugin


Start


The first thing you need to create a plugin is to download the source code for QtCreator, this can be done here . Now, you need to unpack the resulting archive and create a new empty project in the / src / plugins / folder.

Create a plugin that does nothing


To do this, you need to add a new class to the project, which will be the main class of the plug-in, it must be inherited from the ExtensionSystem :: IPlugin interface .
To do this, add the following lines to the .pro file:
include (../../ qtcreatorplugin.pri)
include (../../ plugins / coreplugin / coreplugin.pri)

Coreplugin is needed because all user plugins depend on it, it provides the main interfaces for creating other plugins.
')
You will also need to create an xml-file <pluginname> .pluginspec
<plugin name = "todo" version = "0.0.1" compatVersion = "2.0.0">
<vendor> vsorokin </ vendor>
<copyright> Your copyright </ copyright>
<license> Your license </ license>
<description> Plugin description </ description>
<url> Project homepage </ url>
<dependencyList>
<dependency name = "Core" version = "2.0.0" />
</ dependencyList>
</ plugin>

The meaning of the fields is clear from their name.

So, the class itself that does nothing of the plugin should have the following structure:
#include <extensionsystem / iplugin.h>

class TodoPlugin: public ExtensionSystem :: IPlugin
{
Q_OBJECT
public :
TodoPlugin ();
~ TodoPlugin ();
void extensionsInitialized ();
bool initialize ( const QStringList & arguments, QString * errorString);
void shutdown ();
};

* This source code was highlighted with Source Code Highlighter .

The main thing here is the initialize method, which initializes the plugin. However, in a plugin that does nothing, all methods are empty.

Create a new Output Pane


My plug-in required a new output area, next to the existing ones, it is done very simply, for this it is enough to create a new class inherited from the Core :: IOutputPane interface , here is the title of this class:
#include <coreplugin / ioutputpane.h>
#include <QObject>
#include <QListWidget>

class TodoOutputPane: public Core :: IOutputPane
{
public :
TodoOutputPane (QObject * parent);
~ TodoOutputPane ();

QWidget * outputWidget (QWidget * parent); // Returns the main class widget, here it is QListWidget
QList <QWidget *> toolBarWidgets () const ; // Returns a list of toolbar widgets, they are not in this plugin
QString name () const ; // Just text panel name

int priorityInStatusBar () const ; // Returns a number from one to one hundred, the larger the number, the more the top position will be occupied by our pane

void clearContents (); // Performs cleaning of the main widget
void visibilityChanged ( bool visible);

void setFocus ();
bool hasFocus ();
bool canFocus ();

bool canNavigate ();
bool canNext ();
bool canPrevious ();
void goToNext ();
void goToPrev ();

void addItem (QString text, QString file, int rowNumber); // Method that inserts a new entry into the list
QListWidget * getTodoList () const ; // Pointer to the list

private :
QListWidget * todoList;
};

* This source code was highlighted with Source Code Highlighter .


Now we need to somehow register our newly created todoPane in the system, for this we write the following code in the initialize method:
outPane = new TodoOutputPane ( this );
addAutoReleasedObject (outPane);

* This source code was highlighted with Source Code Highlighter .

It's very simple, now our outPane will appear among the rest, but so far it can not do anything.

Get information about the open file and parse it


In order to find out which file is currently open in the editor, you need to somehow get to the editor ...
To do this, add the following headers:
#include <coreplugin / editormanager / editormanager.h>
#include <coreplugin / editormanager / ieditor.h>

* This source code was highlighted with Source Code Highlighter .

Add the following line to the initialize method:
connect (Core :: EditorManager :: instance (), SIGNAL (currentEditorChanged (Core :: IEditor *)), this , SLOT (currentEditorChanged (Core :: IEditor *)));

* This source code was highlighted with Source Code Highlighter .

And of course, declare the slot currentEditorChanged (Core :: IEditor *)
This slot will be called every time the current editor has changed, that is, the user has opened / closed the file, or has switched to another one that has already been opened.
Its implementation is as follows:
void TodoPlugin :: currentEditorChanged (Core :: IEditor * editor)
{
outPane-> clearContents (); // clear
if (! editor) // Check if there is an editor? at all
{
return ;
}
/ * Fine point, we connect the signal of changing the class of the file associated with the current editor, so that later the file can be re-read after the user has pressed Ctrl + S * /
connect (editor-> file (), SIGNAL (changed ()), this , SLOT (fileChanged ()));
QString fileName = editor-> file () -> fileName (); // Get the file name
readFile (fileName); // read and parse the file.
}

* This source code was highlighted with Source Code Highlighter .

Here the parsing method is called, it is simple:
void TodoPlugin :: readFile (QString fileName)
{
QFile file (fileName);
if (! file.open (QFile :: ReadOnly | QFile :: Text))
return ;
int i = 1;
while (! file.atEnd ())
{
QString currentLine = file.readLine ();
if (currentLine.contains (QRegExp (patternString, Qt :: CaseInsensitive)))
{
outPane-> addItem (currentLine, fileName, i);
}
++ i;
}
}

* This source code was highlighted with Source Code Highlighter .


You also need to describe the new slot, which will be called if the file is saved, here it is:
void TodoPlugin :: fileChanged ()
{
outPane-> clearContents ();
Core :: IFile * file = (Core :: IFile *) sender (); // This is a somewhat non-orthodox way, but it will be quite safe to work, since no more signals are connected to this slot.
if (file)
{
readFile (file-> fileName ());
}
}

* This source code was highlighted with Source Code Highlighter .


Adding lines


Now it is necessary to describe the addition of lines, that is, the method:
void TodoOutputPane :: addItem (QString text, QString file, int rowNumber)
{
QListWidgetItem * newItem = new QListWidgetItem (); // Create a new entry
QRegExp todoExp ( "// \\ s * TODO (: | \\ s)" , Qt :: CaseInsensitive); // Prepare regexps
QRegExp noteExp ( "// \\ s * NOTE (: | \\ s)" , Qt :: CaseInsensitive);
QRegExp fixmeExp ( "// \\ s * FIXME (: | \\ s)" , Qt :: CaseInsensitive);
QRegExp bugExp ( "// \\ s * BUG (: | \\ s)" , Qt :: CaseInsensitive);
QRegExp hackExp ( "// \\ s * HACK (: | \\ s)" , Qt :: CaseInsensitive);

text = text.replace ( "\ n" , "" );
text = text.replace ( "\ r" , "" );

newItem-> setTextColor (QColor ( "# 2F2F2F" ));
if (text.contains (todoExp)) // Looking for regexps
{
newItem-> setBackgroundColor (QColor ( "# BFFFC8" )); // Set Record Color
text = text.replace (todoExp, "TODO:" ); // Replace the “possibly crooked” spelling of the label with the correct one
newItem-> setIcon (QIcon ( ": / warning" )); // Set the icon
}
else if (text.contains (noteExp))
{
newItem-> setBackgroundColor (QColor ( "# E2DFFF" ));
text = text.replace (noteExp, “NOTE:„ );
newItem-> setIcon (QIcon ( “: / info” ));
}
else if (text.contains (bugExp))
{
newItem-> setBackgroundColor (QColor ( "#FFBFBF" ));
text = text.replace (bugExp, “BUG:„ );
newItem-> setIcon (QIcon ( “: / error” ));
}
else if (text.contains (fixmeExp))
{
newItem-> setBackgroundColor (QColor ( "#FFDFDF" ));
text = text.replace (fixmeExp, "FIXME:" );
newItem-> setIcon (QIcon ( “: / error” ));
}
else if (text.contains (hackExp))
{
newItem-> setBackgroundColor (QColor ( "#FFFFAA" ));
text = text.replace (hackExp, “HACK:„ );
newItem-> setIcon (QIcon ( “: / info” ));
}

newItem-> setText (text.trimmed () + "(" + tr ( "line„ ) + QString :: number (rowNumber) + “)" ); // Set the post text
newItem-> setToolTip (file + ":" + QString :: number (rowNumber)); // The tooltip will store the file name and line number, it's not just that, I will tell you why below.
todoList-> addItem (newItem); // Add an entry to the list
}

* This source code was highlighted with Source Code Highlighter .


Go to the desired line by clicking on the record


The only thing left for us to do is implement the transition to the desired line.
This will require another slot, which will be executed when the selected list item:
void TodoPlugin :: gotoToRowInFile (QListWidgetItem * item)
{
int row = 0; // Line number
QString file = "" ; //File name

/ * Get the information from the tooltip * /
QStringList tmpList = item-> toolTip (). Split ( ":" );
if (tmpList.size () == 2)
{
file = tmpList.at (0);
bool ok;
row = tmpList.at (1) .toInt (& ok);
if (! ok)
{
row = 0;
}
}
if (QFileInfo (file) .exists ()) // Check if the file exists.
{
TextEditor :: BaseTextEditor :: openEditorAt (file, row); // Forcibly show the file by setting the cursor in the row number row. In order for this line to work, you must specify the TextEditor plugin in the dependencies, and add the appropriate header
Core :: EditorManager :: instance () -> ensureEditorManagerVisible ();
}
}

* This source code was highlighted with Source Code Highlighter .


Conclusion


Well, that's all, the plugin is ready, thanks for reading.
You can download the plugin from here .

Source: https://habr.com/ru/post/101311/


All Articles