Index: addons/symbolviewer/CMakeLists.txt
===================================================================
--- addons/symbolviewer/CMakeLists.txt
+++ addons/symbolviewer/CMakeLists.txt
@@ -1,7 +1,19 @@
add_definitions(-DTRANSLATION_DOMAIN=\"katesymbolviewer\")
########### next target ###############
-set(katesymbolviewerplugin_PART_SRCS cpp_parser.cpp tcl_parser.cpp fortran_parser.cpp perl_parser.cpp
-php_parser.cpp xslt_parser.cpp ruby_parser.cpp python_parser.cpp bash_parser.cpp ecma_parser.cpp plugin_katesymbolviewer.cpp )
+set(katesymbolviewerplugin_PART_SRCS
+ plugin_katesymbolviewer.cpp
+ bash_parser.cpp
+ cpp_parser.cpp
+ ecma_parser.cpp
+ fortran_parser.cpp
+ perl_parser.cpp
+ php_parser.cpp
+ plaintext_parser.cpp
+ python_parser.cpp
+ ruby_parser.cpp
+ tcl_parser.cpp
+ xslt_parser.cpp
+)
# resource for ui file and stuff
qt5_add_resources(katesymbolviewerplugin_PART_SRCS plugin.qrc)
Index: addons/symbolviewer/plaintext_parser.cpp
===================================================================
--- /dev/null
+++ addons/symbolviewer/plaintext_parser.cpp
@@ -0,0 +1,289 @@
+/***************************************************************************
+ plaintext_parser.cpp - description
+ -------------------
+ begin : Jul 2018
+ author : loh.tar
+ email : loh.tar@googlemail.com
+ ***************************************************************************/
+/***************************************************************************
+ 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 .
+***************************************************************************/
+
+#include
+
+#include "plugin_katesymbolviewer.h"
+
+namespace Plain_Text_Parser_Private {
+// Because there is no "ParserClass", adding a special member function only for
+// parser_foo looks very ugly, we do it in some less ugly way which requires
+// still ugly global variables and functions
+enum NodeType {
+ RootNode,
+ SectNode,
+ HeadNode,
+ ParaNode
+};
+
+KatePluginSymbolViewer *g_plugin(nullptr);
+QTreeWidget *g_symbols(nullptr);
+QTreeWidgetItem *g_lastNode(nullptr);
+int g_paraLineNumber; // Where the current paragraph begins
+bool g_structIsOff;
+bool g_funcIsOff;
+bool g_macroIsOff;
+
+void g_addNode(int nodeType, const QString &text, int lineNumber)
+{
+ QTreeWidgetItem *node(nullptr);
+
+ // Indicate, there is no paragraph waiting for completion
+ g_paraLineNumber = -1;
+
+ switch (nodeType) {
+ case SectNode:
+ if (g_structIsOff) return;
+ break;
+ case HeadNode:
+ if (g_funcIsOff) return;
+ break;
+ case ParaNode:
+ if (g_macroIsOff) return;
+ break;
+ default:
+ break;
+ };
+
+ if (g_plugin->treeOn) {
+ switch (nodeType) {
+ case RootNode:
+ node = new QTreeWidgetItem(g_symbols, nodeType);
+ g_symbols->setRootIsDecorated(1);
+ break;
+ default:
+ while (g_lastNode->type() >= nodeType) {
+ g_lastNode = g_lastNode->parent();
+ }
+ node = new QTreeWidgetItem(g_lastNode, nodeType);
+ break;
+ };
+ g_lastNode = node;
+
+ } else {
+ switch (nodeType) {
+ case RootNode:
+ g_symbols->setRootIsDecorated(1);
+ break;
+ default:
+ node = new QTreeWidgetItem(g_symbols, nodeType);
+ break;
+ };
+ }
+
+ QIcon icon;
+ switch (nodeType) {
+ case RootNode:
+ icon = QIcon(QPixmap(const_cast(method_xpm)));
+ break;
+ case SectNode:
+ icon = QIcon(QPixmap(const_cast(class_xpm)));
+ break;
+ case HeadNode:
+ icon = QIcon(QPixmap(const_cast(struct_xpm)));
+ break;
+ case ParaNode:
+ icon = QIcon(QPixmap(const_cast(macro_xpm)));
+ break;
+ };
+
+ if (node) {
+ node->setText(0, text);
+ node->setIcon(0, icon);
+ node->setText(1, QString::number(lineNumber, 10));
+
+ if (g_plugin->expandedOn) {
+ g_symbols->expandItem(node->parent());
+ }
+ }
+}
+
+enum LineType {
+ EmptyLine,
+ NormalLine,
+ HeaderLine,
+ SectionLine
+};
+
+QQueue g_lineTypeHistory;
+QQueue g_lineHistory;
+
+void g_initHistory()
+{
+ g_paraLineNumber = -1;
+ g_lineTypeHistory.clear();
+ g_lineHistory.clear();
+ g_lineTypeHistory.enqueue(EmptyLine);
+ g_lineHistory.enqueue(QLatin1String(""));
+ g_lineTypeHistory.enqueue(EmptyLine);
+ g_lineHistory.enqueue(QLatin1String(""));
+}
+
+} // [END] namespace Plain_Text_Parser_Private
+
+
+void KatePluginSymbolViewerView::parsePlainTextSymbols(void)
+{
+ if (!m_mainWindow->activeView())
+ return;
+
+ using namespace Plain_Text_Parser_Private;
+
+ g_plugin = m_plugin;
+ g_symbols = m_symbols;
+ // Must copy each, m_struct is private, g_this->m_struct would not work
+ g_structIsOff = !m_struct->isChecked();
+ g_funcIsOff = !m_func->isChecked();
+ g_macroIsOff = !m_macro->isChecked();
+
+ // Rename menu actions
+ // FIXME Make them full configurable, not limited to 3 and not fix named
+ // (not the caption but the access name) need review of all parser
+ m_struct->setText(i18n("Show Sections"));
+ m_func->setText(i18n("Show Header"));
+ m_macro->setText(i18n("Show Paragraphs"));
+
+ // We should also rename the header caption, but that is then not restored
+ // after a switch to an other document -> Needs a patch e.g. in parseSymbols()
+ //m_symbols->headerItem()->setText(0, i18n("Sections"));
+
+ g_addNode(RootNode, i18n("Document"), 0);
+
+ // Some flow/status control variables
+ QString currLine; // Current
+ QString paraLine; // First line of a paragraph
+
+ g_initHistory();
+
+ KTextEditor::Document *kDoc = m_mainWindow->activeView()->document();
+
+ // Run the loop one line more as usually needed...
+ for (int i = 0; i < kDoc->lines() + 1; i++) {
+
+ // ...to ensure a last paragraph is added properly...
+ if (i < kDoc->lines()) {
+ currLine = kDoc->line(i);
+ currLine = currLine.trimmed();
+ currLine = currLine.simplified();
+ currLine.truncate(80); // Limit the size to some acceptable length
+ } else {
+ if (g_paraLineNumber == -1) {
+ // Good, we are done!
+ break;
+ }
+ // ...when there was no \n at the last line
+ currLine.clear();
+ }
+
+ // Treat some lines as empty lines to avaoid to start a new paragraph
+ // or to break a continuation paragraph. Useful with some config files
+ if (currLine.size() == 1) {
+ currLine.clear();
+ } else if (currLine.contains(QRegExp(QLatin1String("^[-#*./]{1,2}$")))) {
+ currLine.clear();
+ }
+
+ if (currLine.isEmpty() && g_lineTypeHistory.last() == EmptyLine) {
+ // Ignore consecutive empty lines
+ continue;
+ }
+
+ // Let's start the investigation
+ bool currIsSection = currLine.contains(QRegExp(QLatin1String("^[=#*]{3,}$")));
+ bool currIsHeader = currLine.contains(QRegExp(QLatin1String("^[-~^]{3,}$")));
+
+ // Keep a record of the history...
+ g_lineHistory.enqueue(currLine);
+ if (currLine.isEmpty()) {
+ g_lineTypeHistory.enqueue(EmptyLine);
+ } else if (currIsHeader) {
+ g_lineTypeHistory.enqueue(HeaderLine);
+ } else if (currIsSection) {
+ g_lineTypeHistory.enqueue(SectionLine);
+ } else {
+ g_lineTypeHistory.enqueue(NormalLine);
+ }
+
+ // ...but not too much
+ if (g_lineHistory.size() > 3) {
+ g_lineHistory.dequeue();
+ g_lineTypeHistory.dequeue();
+ }
+
+ // Waste some memory to increase readability
+ const LineType lineType0 = g_lineTypeHistory.at(0); // Oldest line
+ const LineType lineType1 = g_lineTypeHistory.at(1);
+ const LineType lineType2 = g_lineTypeHistory.at(2); // Current line
+
+ // Check for Paragraph begin
+ if (g_paraLineNumber < 0) {
+ if (lineType1 == NormalLine) {
+ paraLine = g_lineHistory.at(1);
+ g_paraLineNumber = i - 1;
+ } else if (lineType2 == NormalLine) {
+ paraLine = g_lineHistory.at(2);
+ g_paraLineNumber = i;
+ } else {
+ continue;
+ }
+ }
+
+ // Special checks for ISO date header. They to add in a sane way and give them
+ // an own LineType proved to be surprisingly complicated, so it's done quirky.
+ // Thanks to https://stackoverflow.com/a/46362201
+ QRegExp isoDate(QLatin1String("^\\d{4}-([0]\\d|1[0-2])-([0-2]\\d|3[01])$"));
+ bool line0IsDate = g_lineHistory.at(0).contains(isoDate);
+ bool line1IsDate = g_lineHistory.at(1).contains(isoDate);
+
+ if (line0IsDate && lineType1 == SectionLine && lineType2 == NormalLine) {
+ QString mask(QLatin1String("%1 %2"));
+ g_lastNode->setText(0, mask.arg(g_lineHistory.at(0)).arg(g_lineHistory.at(2)));
+ g_initHistory();
+
+ } else if (lineType0 != NormalLine && line1IsDate && lineType2 == NormalLine) {
+ QString mask(QLatin1String("%1 %2"));
+ g_addNode(SectNode, mask.arg(g_lineHistory.at(1)).arg(g_lineHistory.at(2)), i - 1);
+ g_initHistory();
+ // [End] Special checks for ISO date header
+
+ // Check for Paragraph continuation
+ } else if (lineType0 == NormalLine && lineType1 == NormalLine && lineType2 == NormalLine) {
+ continue;
+
+ // Check for Paragraph - Single line
+ } else if (lineType0 != NormalLine && lineType1 == NormalLine && lineType2 == EmptyLine) {
+ g_addNode(ParaNode, paraLine, g_paraLineNumber);
+
+ // Check for Paragraph - Two or more lines
+ } else if (lineType0 == NormalLine && lineType1 == NormalLine && lineType2 != NormalLine) {
+ g_addNode(ParaNode, paraLine, g_paraLineNumber);
+
+ // Check for Header
+ } else if (lineType0 != NormalLine && lineType1 == NormalLine && lineType2 == HeaderLine) {
+ g_addNode(HeadNode, g_lineHistory.at(1), i - 1);
+
+ // Check for Section
+ } else if (lineType0 != NormalLine && lineType1 == NormalLine && lineType2 == SectionLine) {
+ g_addNode(SectNode, g_lineHistory.at(1), i - 1);
+ }
+ }
+}
Index: addons/symbolviewer/plugin_katesymbolviewer.h
===================================================================
--- addons/symbolviewer/plugin_katesymbolviewer.h
+++ addons/symbolviewer/plugin_katesymbolviewer.h
@@ -141,8 +141,8 @@
void parseXsltSymbols(void);
void parsePhpSymbols(void);
void parseBashSymbols(void);
+ void parsePlainTextSymbols(void);
void parseEcmaSymbols(void);
-
};
class KatePluginSymbolViewer : public KTextEditor::Plugin
Index: addons/symbolviewer/plugin_katesymbolviewer.cpp
===================================================================
--- addons/symbolviewer/plugin_katesymbolviewer.cpp
+++ addons/symbolviewer/plugin_katesymbolviewer.cpp
@@ -318,6 +318,9 @@
/** Get the current highlighting mode */
QString hlModeName = doc->mode();
+ // FIXME Best would be when each parser is own child class of some parser class
+ // and each parser is a plugin to the symbol view plugin, then could someone
+ // code and share his own parser without the need to re-compile Kate
if (hlModeName.contains(QLatin1String("C++")) || hlModeName == QLatin1String("C") || hlModeName == QLatin1String("ANSI C89"))
parseCppSymbols();
else if (hlModeName == QLatin1String("PHP (HTML)"))
@@ -338,6 +341,8 @@
parseXsltSymbols();
else if (hlModeName == QLatin1String("Bash"))
parseBashSymbols();
+ else if (hlModeName == QLatin1String("Normal"))
+ parsePlainTextSymbols();
else if (hlModeName == QLatin1String("ActionScript 2.0") ||
hlModeName == QLatin1String("JavaScript") ||
hlModeName == QLatin1String("QML"))
Index: addons/symbolviewer/testfile.txt
===================================================================
--- /dev/null
+++ addons/symbolviewer/testfile.txt
@@ -0,0 +1,182 @@
+Test And Demo File For The PlainText-Parser
+=============================================
+With this you have something like a table of contents when reading/edit a plain
+text file like a README.
+
+
+Section
+=========
+Recognized right now is only === as section and --- as header starting at a
+length of 3 characters, all other text is treated as paragraph.
+
+
+
+There can be any space before or after some recognized section/header/paragraph
+which must not affect the desired result.
+
+
+
+
+Header
+--------
+
+
+
+So this is a paragraph consist only of one line.
+
+ - This paragraph has
+ - two lines
+
+This is a new paragraph
+with a lengh
+of three lines.
+
+There is no lengh compare done to test if the underlining fit the title lengh.
+I had though about to add these check in a way that it is somehow fuzzy, because
+my style is to add to more underlining characters, others prefer to fill a
+complete line or 70%, 80% of a full line.
+
+The decision to use a min lengh of three fit to all these but of cause no
+shorter headers, because that looks to me rather unlikely.
+(Don't wonder, see also "Special Treatments" below)
+
+1
+-
+
+2)
+--
+
+For my taste works this all pretty charming when you have a well formatted file.
+It works so nicely that also a bad styled formatting is treated correctly.
+
+Bad Formatting Example
+========================
+Missed Empty Line
+-------------------
+Foo bar text
+
+^-- Well done as Section/Header/Paragraph
+
+
+But there could be things improved. As hint what I talk about I have
+inserted below some example lines.
+
+
+Special Treatments
+====================
+
+Avoided Paragraphs
+--------------------
+To keep the symbol list free from useless entries, will some lines treated as an
+empty line. So none of the following must treated as start of a new paragraph.
+
+A single line of section/header characters...
+
+---
+===
+
+...any line consists of only one charater...
+
+x
+y
+#
+?
+!
+
+...and any line with only two characters if there are as follows:
+
+--
+##
+**
+..
+//
+
+The sharp is obviously useful in some config file, the others I have added
+without an concrete example, just only by a gut feeling.
+
+
+ISO-Date Forced Sections
+--------------------------
+I have the habit to write down some notes like a diary, starting with a ISO
+date, sometimes underlined the date, sometimes not. So I added a special check
+that these ISO dates are treated as if written like so...
+
+2018-07-19 Made first commit of my Kate patches
+=================================================
+
+...but my entries looks this way:
+
+2018-07-19
+============
+Made first commit of my Kate patches to the symbol view plugin
+
+ - Fix broken toggle actions
+ - Avoid unneeded update of current item
+ - Add a plain-text-parser
+ - Fix: Find new current item when symbol is in first line
+
+2018-07-22
+ Contacted the "Kate-Devils" at there list
+ Nice people, but a bit taciturn
+
+2018-07-25
+============
+Very cool, my first patch is upstream
+That was lastly faster than thought
+
+
+Without this trick would only be the date appears in the symbol list, which is
+not a sufficient information for my taste, and in one case not a section
+created. What sadly not work is when an empty line is between the date and the
+next paragraph.
+
+2018-07-22
+
+ Contacted the "Kate-Devils" at there list
+
+
+2018-07-25
+============
+
+Very cool, my first patch is upstream
+
+
+The former could be added but I'm not sure if that may cause unexpected results,
+because there are only three consecutive lines compared. So, no check can by
+made if above the date is the line empty. To catch the ladder there would be make
+a check of five lines or some other special quirk.
+
+
+Other Seen Structuring Styles
+-------------------------------
+I can remeber that I have seen plain text files where was other characters used
+as underlining to indicate a section or header, e.g.
+
+Using Sharp
+###########
+
+Using Tilde
+~~~~~~~~~~~
+
+Using Asterisk
+**************
+
+Using Caret
+^^^^^^^^^^^
+
+I have quickly added these as you can see, but in a (too?) very lazy way:
+
+Feel Free
+#*=#*=#*=
+
+To Fix Me
+-^-~-^-~-
+
+Perhaps not a problem but a funny feature.
+
+
+Room For Improvements
+=======================
+There could be a list with items indented to the paragraph. But that's not only
+an issue of parsing, There are not enough menu options to control these. But,
+perhaps may this not a real problem.