Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add word auto completion to snippets completer #194

Merged
merged 1 commit into from Dec 25, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions app-static/app-static.pro
Expand Up @@ -19,6 +19,7 @@ SOURCES += \
converter/revealmarkdownconverter.cpp \
template/htmltemplate.cpp \
template/presentationtemplate.cpp \
completionlistmodel.cpp \
datalocation.cpp \
slidelinemapping.cpp \
viewsynchronizer.cpp \
Expand All @@ -39,6 +40,7 @@ HEADERS += \
template/template.h \
template/htmltemplate.h \
template/presentationtemplate.h \
completionlistmodel.h \
datalocation.h \
slidelinemapping.h \
viewsynchronizer.h \
Expand Down
112 changes: 112 additions & 0 deletions app-static/completionlistmodel.cpp
@@ -0,0 +1,112 @@
/*
* Copyright 2013-2014 Christian Loose <christian.loose@hamburg.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "completionlistmodel.h"

#include <QFont>
#include <QIcon>


CompletionListModel::CompletionListModel(QObject *parent) :
QAbstractListModel(parent)
{
}

int CompletionListModel::rowCount(const QModelIndex &parent) const
{
return snippets.count() + words.count();
}

QVariant CompletionListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();

if (index.row() > rowCount())
return QVariant();

if (index.row() < snippets.count()) {
const Snippet snippet = snippets.at(index.row());

switch (role) {
case Qt::DecorationRole:
return QIcon("fa-puzzle-piece.fontawesome");

case Qt::DisplayRole:
return QString("%1 %2").arg(snippet.trigger, -15).arg(snippet.description);

case Qt::EditRole:
return snippet.trigger;

case Qt::ToolTipRole:
return snippet.snippet.toHtmlEscaped();

case Qt::FontRole:
{
QFont font("Monospace", 8);
font.setStyleHint(QFont::TypeWriter);
return font;
}
break;
}
} else {
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole:
return words.at(index.row() - snippets.count());
}
}

return QVariant();
}

void CompletionListModel::setWords(const QStringList &words)
{
beginInsertRows(QModelIndex(), snippets.count(), snippets.count() + words.count());
this->words = words;
endInsertRows();
}

void CompletionListModel::snippetCollectionChanged(SnippetCollection::CollectionChangedType changedType, const Snippet &snippet)
{
switch (changedType) {
case SnippetCollection::ItemAdded:
{
QList<Snippet>::iterator it = qLowerBound(snippets.begin(), snippets.end(), snippet);
int row = std::distance(snippets.begin(), it);
beginInsertRows(QModelIndex(), row, row);
snippets.insert(it, snippet);
endInsertRows();
}
break;
case SnippetCollection::ItemChanged:
{
int row = snippets.indexOf(snippet);
snippets.replace(row, snippet);
}
break;
case SnippetCollection::ItemDeleted:
{
int row = snippets.indexOf(snippet);
beginRemoveRows(QModelIndex(), row, row);
snippets.removeAt(row);
endRemoveRows();
}
break;
default:
break;
}
}
44 changes: 44 additions & 0 deletions app-static/completionlistmodel.h
@@ -0,0 +1,44 @@
/*
* Copyright 2013-2014 Christian Loose <christian.loose@hamburg.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef COMPLETIONLISTMODEL_H
#define COMPLETIONLISTMODEL_H

#include <QAbstractListModel>
#include <snippets/snippetcollection.h>
struct Snippet;


class CompletionListModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit CompletionListModel(QObject *parent = 0);

int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;

void setWords(const QStringList &words);

public slots:
void snippetCollectionChanged(SnippetCollection::CollectionChangedType changedType, const Snippet &snippet);

private:
QList<Snippet> snippets;
QStringList words;
};

#endif // COMPLETIONLISTMODEL_H
42 changes: 40 additions & 2 deletions app/markdowneditor.cpp
@@ -1,5 +1,5 @@
/*
* Copyright 2013 Christian Loose <christian.loose@hamburg.de>
* Copyright 2013-2014 Christian Loose <christian.loose@hamburg.de>
*
* 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
Expand Down Expand Up @@ -315,7 +315,8 @@ void MarkdownEditor::performCompletion()
QRect popupRect = cursorRect();
popupRect.setLeft(popupRect.left() + lineNumberAreaWidth());

completer->performCompletion(textUnderCursor(), popupRect);
QStringList words = extractDistinctWordsFromDocument();
completer->performCompletion(textUnderCursor(), words, popupRect);
}

void MarkdownEditor::insertSnippet(const QString &completionPrefix, const QString &completion, int newCursorPos)
Expand Down Expand Up @@ -510,3 +511,40 @@ QString MarkdownEditor::textUnderCursor() const

return cursor.selectedText();
}

bool GreaterThanMinimumWordLength(const QString &word)
{
static const int MINIMUM_WORD_LENGTH = 3;
return word.length() > MINIMUM_WORD_LENGTH;
}

QStringList MarkdownEditor::extractDistinctWordsFromDocument() const
{
QStringList allWords = retrieveAllWordsFromDocument();
allWords.removeDuplicates();

QStringList words = filterWordList(allWords, GreaterThanMinimumWordLength);
words.sort(Qt::CaseInsensitive);

return words;
}

QStringList MarkdownEditor::retrieveAllWordsFromDocument() const
{
return toPlainText().split(QRegExp("\\W+"), QString::SkipEmptyParts);
}

template <class UnaryPredicate>
QStringList MarkdownEditor::filterWordList(const QStringList &words, UnaryPredicate predicate) const
{
QStringList filteredWordList;

foreach (const QString &word, words) {
if (predicate(word))
{
filteredWordList << word;
}
}

return filteredWordList;
}
6 changes: 5 additions & 1 deletion app/markdowneditor.h
@@ -1,5 +1,5 @@
/*
* Copyright 2013 Christian Loose <christian.loose@hamburg.de>
* Copyright 2013-2014 Christian Loose <christian.loose@hamburg.de>
*
* 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
Expand Down Expand Up @@ -76,6 +76,10 @@ private slots:
bool isUrlToLocalFile(const QMimeData *source) const;
void drawLineEndMarker(QPaintEvent *e);
QString textUnderCursor() const;
QStringList extractDistinctWordsFromDocument() const;
QStringList retrieveAllWordsFromDocument() const;
template <class UnaryPredicate>
QStringList filterWordList(const QStringList &words, UnaryPredicate predicate) const;

private:
QWidget *lineNumberArea;
Expand Down
15 changes: 11 additions & 4 deletions app/snippetcompleter.cpp
@@ -1,5 +1,5 @@
/*
* Copyright 2013 Christian Loose <christian.loose@hamburg.de>
* Copyright 2013-2014 Christian Loose <christian.loose@hamburg.de>
*
* 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
Expand All @@ -25,6 +25,7 @@
#include <snippets/snippet.h>
#include <snippets/snippetcollection.h>
#include <snippets/snippetlistmodel.h>
#include <completionlistmodel.h>


SnippetCompleter::SnippetCompleter(SnippetCollection *collection, QWidget *parentWidget) :
Expand All @@ -39,16 +40,19 @@ SnippetCompleter::SnippetCompleter(SnippetCollection *collection, QWidget *paren
connect(completer, SIGNAL(activated(QString)),
this, SLOT(insertSnippet(QString)));

SnippetListModel *model = new SnippetListModel(completer);
CompletionListModel *model = new CompletionListModel(completer);
connect(collection, SIGNAL(collectionChanged(SnippetCollection::CollectionChangedType,Snippet)),
model, SLOT(snippetCollectionChanged(SnippetCollection::CollectionChangedType,Snippet)));
completer->setModel(model);
}

void SnippetCompleter::performCompletion(const QString &textUnderCursor, const QRect &popupRect)
void SnippetCompleter::performCompletion(const QString &textUnderCursor, const QStringList &words, const QRect &popupRect)
{
const QString completionPrefix = textUnderCursor;

// TODO: find more elegant solution
qobject_cast<CompletionListModel*>(completer->model())->setWords(words);

if (completionPrefix != completer->completionPrefix()) {
completer->setCompletionPrefix(completionPrefix);
completer->popup()->setCurrentIndex(completer->completionModel()->index(0, 0));
Expand Down Expand Up @@ -76,8 +80,11 @@ void SnippetCompleter::hidePopup()

void SnippetCompleter::insertSnippet(const QString &trigger)
{
if (!snippetCollection || !snippetCollection->contains(trigger))
if (!snippetCollection || !snippetCollection->contains(trigger)) {
// insert word directly
emit snippetSelected(completer->completionPrefix(), trigger, trigger.length());
return;
}

const Snippet snippet = snippetCollection->snippet(trigger);

Expand Down
4 changes: 2 additions & 2 deletions app/snippetcompleter.h
@@ -1,5 +1,5 @@
/*
* Copyright 2013 Christian Loose <christian.loose@hamburg.de>
* Copyright 2013-2014 Christian Loose <christian.loose@hamburg.de>
*
* 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
Expand Down Expand Up @@ -29,7 +29,7 @@ class SnippetCompleter : public QObject
public:
explicit SnippetCompleter(SnippetCollection *collection, QWidget *parentWidget);

void performCompletion(const QString &textUnderCursor, const QRect &popupRect);
void performCompletion(const QString &textUnderCursor, const QStringList &words, const QRect &popupRect);

bool isPopupVisible() const;
void hidePopup();
Expand Down