support word count

This commit is contained in:
Le Tan 2018-03-06 19:33:40 +08:00
parent 113a1455de
commit 9710659a00
15 changed files with 447 additions and 14 deletions

View File

@ -701,6 +701,8 @@ var insertImageCaption = function() {
// markdown-specifi handle logics, such as Mermaid, MathJax.
var finishLogics = function() {
content.finishLogics();
calculateWordCount();
};
// Escape @text to Html.
@ -1107,3 +1109,52 @@ var postProcessMathJax = function() {
finishLogics();
};
function getNodeText(el) {
ret = "";
var length = el.childNodes.length;
for(var i = 0; i < length; i++) {
var node = el.childNodes[i];
if(node.nodeType != 8) {
ret += node.nodeType != 1 ? node.nodeValue : getNodeText(node);
}
}
return ret;
}
var calculateWordCount = function() {
var words = getNodeText(placeholder);
// Char without spaces.
var cns = 0;
var wc = 0;
var cc = words.length;
// 0 - not in word;
// 1 - in English word;
// 2 - in non-English word;
var state = 0;
for (var i = 0; i < cc; ++i) {
var ch = words[i];
if (/\s/.test(ch)) {
if (state != 0) {
state = 0;
}
continue;
} else if (ch.charCodeAt() < 128) {
if (state != 1) {
state = 1;
++wc;
}
} else {
state = 2;
++wc;
}
++cns;
}
content.updateWordCountInfo(wc, cns, cc);
};

View File

@ -213,7 +213,8 @@ HEADERS += vmainwindow.h \
vstyleditemdelegate.h \
vtreewidget.h \
dialog/vexportdialog.h \
vexporter.h
vexporter.h \
vwordcountinfo.h
RESOURCES += \
vnote.qrc \

View File

@ -120,3 +120,15 @@ void VDocument::htmlContentCB(const QString &p_head,
{
emit htmlContentFinished(p_head, p_style, p_body);
}
void VDocument::updateWordCountInfo(int p_wordCount,
int p_charWithoutSpacesCount,
int p_charWithSpacesCount)
{
m_wordCountInfo.m_mode = VWordCountInfo::Read;
m_wordCountInfo.m_wordCount = p_wordCount;
m_wordCountInfo.m_charWithoutSpacesCount = p_charWithoutSpacesCount;
m_wordCountInfo.m_charWithSpacesCount = p_charWithSpacesCount;
emit wordCountInfoUpdated();
}

View File

@ -4,6 +4,8 @@
#include <QObject>
#include <QString>
#include "vwordcountinfo.h"
class VFile;
class VDocument : public QObject
@ -41,6 +43,8 @@ public:
// Request to get the HTML content.
void getHtmlContentAsync();
const VWordCountInfo &getWordCountInfo() const;
public slots:
// Will be called in the HTML side
@ -74,6 +78,10 @@ public slots:
const QString &p_style,
const QString &p_body);
void updateWordCountInfo(int p_wordCount,
int p_charWithoutSpacesCount,
int p_charWithSpacesCount);
signals:
void textChanged(const QString &text);
@ -108,6 +116,8 @@ signals:
const QString &p_styleContent,
const QString &p_bodyContent);
void wordCountInfoUpdated();
private:
QString m_toc;
QString m_header;
@ -125,6 +135,8 @@ private:
// Whether the web side is ready to convert text to html.
bool m_readyToTextToHtml;
VWordCountInfo m_wordCountInfo;
};
inline bool VDocument::isReadyToHighlight() const
@ -136,4 +148,10 @@ inline bool VDocument::isReadyToTextToHtml() const
{
return m_readyToTextToHtml;
}
inline const VWordCountInfo &VDocument::getWordCountInfo() const
{
return m_wordCountInfo;
}
#endif // VDOCUMENT_H

View File

@ -10,6 +10,7 @@
#include "veditconfig.h"
#include "vconstants.h"
#include "vfile.h"
#include "vwordcountinfo.h"
class QWidget;
class VEditorObject;
@ -149,6 +150,8 @@ public:
// @p_modified: if true, delete the whole content and insert the new content.
virtual void setContent(const QString &p_content, bool p_modified = false) = 0;
virtual VWordCountInfo fetchWordCountInfo() const = 0;
// Wrapper functions for QPlainTextEdit/QTextEdit.
// Ends with W to distinguish it from the original interfaces.
public:

View File

@ -236,3 +236,10 @@ QString VEditTab::handleVimCmdRequestRegister(int p_key, int p_modifiers)
return QString();
}
VWordCountInfo VEditTab::fetchWordCountInfo(bool p_editMode) const
{
Q_UNUSED(p_editMode);
return VWordCountInfo();
}

View File

@ -8,6 +8,7 @@
#include "vfile.h"
#include "utils/vvim.h"
#include "vedittabinfo.h"
#include "vwordcountinfo.h"
class VEditArea;
class VSnippet;
@ -114,6 +115,9 @@ public:
// Handle the change of file or directory, such as the file has been moved.
virtual void handleFileOrDirectoryChange(bool p_isFile, UpdateAction p_act);
// Fetch tab stat info.
virtual VWordCountInfo fetchWordCountInfo(bool p_editMode) const;
public slots:
// Enter edit mode
virtual void editFile() = 0;

View File

@ -1,6 +1,8 @@
#ifndef VEDITTABINFO_H
#define VEDITTABINFO_H
#include "vwordcountinfo.h"
class VEditTab;
struct VEditTabInfo
@ -31,6 +33,7 @@ struct VEditTabInfo
m_cursorBlockNumber = -1;
m_cursorPositionInBlock = -1;
m_blockCount = -1;
m_wordCountInfo.clear();
m_headerIndex = -1;
}
@ -43,6 +46,8 @@ struct VEditTabInfo
int m_cursorPositionInBlock;
int m_blockCount;
VWordCountInfo m_wordCountInfo;
// Header index in outline.
int m_headerIndex;
};

View File

@ -1233,3 +1233,45 @@ void VMdEditor::insertImageLink(const QString &p_text, const QString &p_url)
}
}
VWordCountInfo VMdEditor::fetchWordCountInfo() const
{
VWordCountInfo info;
QTextDocument *doc = document();
// Char without spaces.
int cns = 0;
int wc = 0;
// Remove th ending new line.
int cc = doc->characterCount() - 1;
// 0 - not in word;
// 1 - in English word;
// 2 - in non-English word;
int state = 0;
for (int i = 0; i < cc; ++i) {
QChar ch = doc->characterAt(i);
if (ch.isSpace()) {
if (state) {
state = 0;
}
continue;
} else if (ch.unicode() < 128) {
if (state != 1) {
state = 1;
++wc;
}
} else {
state = 2;
++wc;
}
++cns;
}
info.m_mode = VWordCountInfo::Edit;
info.m_wordCount = wc;
info.m_charWithoutSpacesCount = cns;
info.m_charWithSpacesCount = cc;
return info;
}

View File

@ -67,6 +67,8 @@ public:
// Update m_initImages and m_insertedImages to handle the change of the note path.
void updateInitAndInsertedImages(bool p_fileChanged, UpdateAction p_act);
VWordCountInfo fetchWordCountInfo() const Q_DECL_OVERRIDE;
public slots:
bool jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) Q_DECL_OVERRIDE;

View File

@ -85,17 +85,22 @@ void VMdTab::showFileReadMode()
// Will recover the header when web side is ready.
m_headerFromEditMode = m_currentHeader;
updateWebView();
m_stacks->setCurrentWidget(m_webViewer);
clearSearchedWordHighlight();
updateStatus();
}
void VMdTab::updateWebView()
{
if (m_mdConType == MarkdownConverterType::Hoedown) {
viewWebByConverter();
} else {
m_document->updateText();
updateOutlineFromHtml(m_document->getToc());
}
m_stacks->setCurrentWidget(m_webViewer);
clearSearchedWordHighlight();
updateStatus();
}
bool VMdTab::scrollWebViewToHeader(const VHeaderPointer &p_header)
@ -424,6 +429,15 @@ void VMdTab::setupMarkdownViewer()
Q_ASSERT(m_editor);
m_editor->textToHtmlFinished(p_text, m_webViewer->url(), p_html);
});
connect(m_document, &VDocument::wordCountInfoUpdated,
this, [this]() {
VEditTabInfo info = fetchTabInfo(VEditTabInfo::InfoType::All);
if (m_isEditMode) {
info.m_wordCountInfo = m_document->getWordCountInfo();
}
emit statusUpdated(info);
});
page->setWebChannel(channel);
@ -790,6 +804,16 @@ VEditTabInfo VMdTab::fetchTabInfo(VEditTabInfo::InfoType p_type) const
info.m_blockCount = m_editor->document()->blockCount();
}
if (m_isEditMode) {
if (m_editor) {
// We do not get the full word count info in edit mode.
info.m_wordCountInfo.m_mode = VWordCountInfo::Edit;
info.m_wordCountInfo.m_charWithSpacesCount = m_editor->document()->characterCount() - 1;
}
} else {
info.m_wordCountInfo = m_document->getWordCountInfo();
}
info.m_headerIndex = m_currentHeader.m_index;
return info;
@ -1257,3 +1281,21 @@ void VMdTab::handleSavePageRequested()
m_webViewer->page()->save(fileName, format);
}
VWordCountInfo VMdTab::fetchWordCountInfo(bool p_editMode) const
{
if (p_editMode) {
if (m_editor) {
return m_editor->fetchWordCountInfo();
}
} else {
// Request to update with current text.
if (m_isEditMode) {
const_cast<VMdTab *>(this)->updateWebView();
}
return m_document->getWordCountInfo();
}
return VWordCountInfo();
}

View File

@ -88,6 +88,9 @@ public:
void handleFileOrDirectoryChange(bool p_isFile, UpdateAction p_act) Q_DECL_OVERRIDE;
// Fetch tab stat info.
VWordCountInfo fetchWordCountInfo(bool p_editMode) const Q_DECL_OVERRIDE;
public slots:
// Enter edit mode.
void editFile() Q_DECL_OVERRIDE;
@ -207,6 +210,9 @@ private:
bool executeVimCommandInWebView(const QString &p_cmd);
// Update web view by current content.
void updateWebView();
VMdEditor *m_editor;
VWebView *m_webViewer;
VDocument *m_document;

View File

@ -1,13 +1,114 @@
#include "vtabindicator.h"
#include <QLabel>
#include <QHBoxLayout>
#include <QtWidgets>
#include "vedittab.h"
#include "vorphanfile.h"
#include "vbuttonwithwidget.h"
#include "vwordcountinfo.h"
class VWordCountPanel : public QWidget
{
public:
VWordCountPanel(QWidget *p_parent)
: QWidget(p_parent)
{
m_wordLabel = new QLabel();
m_wordLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
m_charWithoutSpacesLabel = new QLabel();
m_charWithoutSpacesLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
m_charWithSpacesLabel = new QLabel();
m_charWithSpacesLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
QFormLayout *readLayout = new QFormLayout();
readLayout->addRow(tr("Words"), m_wordLabel);
readLayout->addRow(tr("Characters (no spaces)"), m_charWithoutSpacesLabel);
readLayout->addRow(tr("Characters (with spaces)"), m_charWithSpacesLabel);
m_readBox = new QGroupBox(tr("Read"));
m_readBox->setLayout(readLayout);
m_wordEditLabel = new QLabel();
m_wordEditLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
m_charWithoutSpacesEditLabel = new QLabel();
m_charWithoutSpacesEditLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
m_charWithSpacesEditLabel = new QLabel();
m_charWithSpacesEditLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
QLabel *cwsLabel = new QLabel(tr("Characters (with spaces)"));
cwsLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
QFormLayout *editLayout = new QFormLayout();
editLayout->addRow(tr("Words"), m_wordEditLabel);
editLayout->addRow(tr("Characters (no spaces)"), m_charWithoutSpacesEditLabel);
editLayout->addRow(cwsLabel, m_charWithSpacesEditLabel);
m_editBox = new QGroupBox(tr("Edit"));
m_editBox->setLayout(editLayout);
QLabel *titleLabel = new QLabel(tr("Word Count"));
titleLabel->setProperty("TitleLabel", true);
QVBoxLayout *mainLayout = new QVBoxLayout();
mainLayout->addWidget(titleLabel);
mainLayout->addWidget(m_readBox);
mainLayout->addWidget(m_editBox);
setLayout(mainLayout);
setMinimumWidth(300);
}
void updateReadInfo(const VWordCountInfo &p_readInfo)
{
if (p_readInfo.isNull()) {
m_wordLabel->clear();
m_charWithoutSpacesLabel->clear();
m_charWithSpacesLabel->clear();
} else {
m_wordLabel->setText(QString::number(p_readInfo.m_wordCount));
m_charWithoutSpacesLabel->setText(QString::number(p_readInfo.m_charWithoutSpacesCount));
m_charWithSpacesLabel->setText(QString::number(p_readInfo.m_charWithSpacesCount));
}
}
void updateEditInfo(const VWordCountInfo &p_editInfo)
{
if (p_editInfo.isNull()) {
m_wordEditLabel->clear();
m_charWithoutSpacesEditLabel->clear();
m_charWithSpacesEditLabel->clear();
} else {
m_wordEditLabel->setText(QString::number(p_editInfo.m_wordCount));
m_charWithoutSpacesEditLabel->setText(QString::number(p_editInfo.m_charWithoutSpacesCount));
m_charWithSpacesEditLabel->setText(QString::number(p_editInfo.m_charWithSpacesCount));
}
}
void clear()
{
m_wordLabel->clear();
m_charWithoutSpacesLabel->clear();
m_charWithSpacesLabel->clear();
m_wordEditLabel->clear();
m_charWithoutSpacesEditLabel->clear();
m_charWithSpacesEditLabel->clear();
}
private:
QLabel *m_wordLabel;
QLabel *m_charWithoutSpacesLabel;
QLabel *m_charWithSpacesLabel;
QLabel *m_wordEditLabel;
QLabel *m_charWithoutSpacesEditLabel;
QLabel *m_charWithSpacesEditLabel;
QGroupBox *m_readBox;
QGroupBox *m_editBox;
};
VTabIndicator::VTabIndicator(QWidget *p_parent)
: QWidget(p_parent)
: QWidget(p_parent),
m_editTab(NULL)
{
setupUI();
}
@ -33,8 +134,17 @@ void VTabIndicator::setupUI()
m_cursorLabel = new QLabel(this);
m_cursorLabel->setProperty("TabIndicatorLabel", true);
m_wordCountPanel = new VWordCountPanel(this);
m_wordCountBtn = new VButtonWithWidget(tr("[W]"), m_wordCountPanel, this);
m_wordCountBtn->setToolTip(tr("Word Count Information"));
m_wordCountBtn->setProperty("StatusBtn", true);
m_wordCountBtn->setFocusPolicy(Qt::NoFocus);
connect(m_wordCountBtn, &VButtonWithWidget::popupWidgetAboutToShow,
this, &VTabIndicator::updateWordCountInfo);
QHBoxLayout *mainLayout = new QHBoxLayout(this);
mainLayout->addWidget(m_cursorLabel);
mainLayout->addWidget(m_wordCountBtn);
mainLayout->addWidget(m_externalLabel);
mainLayout->addWidget(m_systemLabel);
mainLayout->addWidget(m_readonlyLabel);
@ -75,7 +185,6 @@ static QString docTypeToString(DocType p_type)
void VTabIndicator::update(const VEditTabInfo &p_info)
{
const VEditTab *editTab = NULL;
const VFile *file = NULL;
DocType docType = DocType::Html;
bool readonly = false;
@ -83,16 +192,17 @@ void VTabIndicator::update(const VEditTabInfo &p_info)
bool system = false;
QString cursorStr;
if (p_info.m_editTab)
m_editTab = p_info.m_editTab;
if (m_editTab)
{
editTab = p_info.m_editTab;
file = editTab->getFile();
file = m_editTab->getFile();
docType = file->getDocType();
readonly = !file->isModifiable();
external = file->getType() == FileType::Orphan;
system = external && dynamic_cast<const VOrphanFile *>(file)->isSystemFile();
if (editTab->isEditMode()) {
if (m_editTab->isEditMode()) {
int line = p_info.m_cursorBlockNumber + 1;
int col = p_info.m_cursorPositionInBlock;
if (col < 0) {
@ -113,8 +223,70 @@ void VTabIndicator::update(const VEditTabInfo &p_info)
}
}
updateWordCountBtn(p_info);
if (p_info.m_wordCountInfo.m_mode == VWordCountInfo::Read) {
m_wordCountPanel->updateReadInfo(p_info.m_wordCountInfo);
}
m_docTypeLabel->setText(docTypeToString(docType));
m_readonlyLabel->setVisible(readonly);
m_externalLabel->setVisible(external);
m_systemLabel->setVisible(system);
}
void VTabIndicator::updateWordCountInfo(QWidget *p_widget)
{
VWordCountPanel *wcp = dynamic_cast<VWordCountPanel *>(p_widget);
if (!m_editTab) {
wcp->clear();
return;
}
wcp->updateReadInfo(m_editTab->fetchWordCountInfo(false));
wcp->updateEditInfo(m_editTab->fetchWordCountInfo(true));
}
void VTabIndicator::updateWordCountBtn(const VEditTabInfo &p_info)
{
const VEditTab *editTab = p_info.m_editTab;
if (!editTab) {
m_wordCountBtn->setText(tr("[W]"));
return;
}
const VWordCountInfo &wci = p_info.m_wordCountInfo;
bool editMode = editTab->isEditMode();
int wc = -1;
bool needUpdate = false;
switch (wci.m_mode) {
case VWordCountInfo::Read:
if (!editMode) {
wc = wci.m_wordCount;
needUpdate = true;
}
break;
case VWordCountInfo::Edit:
if (editMode) {
wc = wci.m_charWithSpacesCount;
needUpdate = true;
}
break;
case VWordCountInfo::Invalid:
needUpdate = true;
break;
default:
break;
}
if (needUpdate) {
QString text = tr("[%1]%2").arg(editMode ? tr("C") : tr("W"))
.arg(wc > -1 ? QString::number(wc) : "");
m_wordCountBtn->setText(text);
}
}

View File

@ -5,6 +5,9 @@
#include "vedittabinfo.h"
class QLabel;
class VButtonWithWidget;
class VEditTab;
class VWordCountPanel;
class VTabIndicator : public QWidget
{
@ -16,9 +19,14 @@ public:
// Update indicator.
void update(const VEditTabInfo &p_info);
private slots:
void updateWordCountInfo(QWidget *p_widget);
private:
void setupUI();
void updateWordCountBtn(const VEditTabInfo &p_info);
// Indicate the doc type.
QLabel *m_docTypeLabel;
@ -33,6 +41,13 @@ private:
// Indicate the position of current cursor.
QLabel *m_cursorLabel;
// Indicate the word count.
VButtonWithWidget *m_wordCountBtn;
VEditTab *m_editTab;
VWordCountPanel *m_wordCountPanel;
};
#endif // VTABINDICATOR_H

53
src/vwordcountinfo.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef VWORDCOUNTINFO_H
#define VWORDCOUNTINFO_H
#include <QString>
#include <QDebug>
struct VWordCountInfo
{
enum Mode
{
Read = 0,
Edit,
Invalid
};
VWordCountInfo()
: m_mode(Mode::Invalid),
m_wordCount(-1),
m_charWithoutSpacesCount(-1),
m_charWithSpacesCount(-1)
{
}
bool isNull() const
{
return m_mode == Mode::Invalid;
}
void clear()
{
m_mode = Mode::Invalid;
m_wordCount = -1;
m_charWithoutSpacesCount = -1;
m_charWithSpacesCount = -1;
}
QString toString() const
{
return QString("VWordCountInfo mode %1 WC %2 CNSC %3 CSC %4")
.arg(m_mode)
.arg(m_wordCount)
.arg(m_charWithoutSpacesCount)
.arg(m_charWithSpacesCount);
}
Mode m_mode;
int m_wordCount;
int m_charWithoutSpacesCount;
int m_charWithSpacesCount;
};
#endif // VWORDCOUNTINFO_H