[geany/geany-plugins] f81769: New plugin: Pair Tag Highlighter

Volodymyr Kononenko git-noreply at xxxxx
Fri Apr 5 15:46:20 UTC 2013


Branch:      refs/heads/master
Author:      Volodymyr Kononenko <vm at kononenko.ws>
Committer:   Volodymyr Kononenko <volodymyr.kononenko at globallogic.com>
Date:        Fri, 05 Apr 2013 15:46:20 UTC
Commit:      f81769ef36281b2f4d2e09c5757a2605aae5b5cd
             https://github.com/geany/geany-plugins/commit/f81769ef36281b2f4d2e09c5757a2605aae5b5cd

Log Message:
-----------
New plugin: Pair Tag Highlighter

The plugin finds and highlights matching opening/closing
HTML tag by clicking or moving cursor inside a tag.


Modified Paths:
--------------
    Makefile.am
    build/pairtaghighlighter.m4
    configure.ac
    pairtaghighlighter/AUTHORS
    pairtaghighlighter/COPYING
    pairtaghighlighter/ChangeLog
    pairtaghighlighter/Makefile.am
    pairtaghighlighter/NEWS
    pairtaghighlighter/README
    pairtaghighlighter/src/Makefile.am
    pairtaghighlighter/src/pair_tag_highlighter.c

Modified: Makefile.am
4 files changed, 4 insertions(+), 0 deletions(-)
===================================================================
@@ -94,6 +94,10 @@ if ENABLE_MULTITERM
 SUBDIRS += multiterm
 endif
 
+if ENABLE_PAIRTAGHIGHLIGHTER
+SUBDIRS += pairtaghighlighter
+endif
+
 if ENABLE_POHELPER
 SUBDIRS += pohelper
 endif


Modified: build/pairtaghighlighter.m4
9 files changed, 9 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,9 @@
+AC_DEFUN([GP_CHECK_PAIRTAGHIGHLIGHTER],
+[
+    GP_ARG_DISABLE([PairTagHighlighter], [auto])
+    GP_COMMIT_PLUGIN_STATUS([PairTagHighlighter])
+    AC_CONFIG_FILES([
+        pairtaghighlighter/Makefile
+        pairtaghighlighter/src/Makefile
+    ])
+])


Modified: configure.ac
1 files changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -48,6 +48,7 @@ GP_CHECK_GENIUSPASTE
 GP_CHECK_GPROJECT
 GP_CHECK_MARKDOWN
 GP_CHECK_MULTITERM
+GP_CHECK_PAIRTAGHIGHLIGHTER
 GP_CHECK_POHELPER
 GP_CHECK_PRETTYPRINTER
 GP_CHECK_SCOPE


Modified: pairtaghighlighter/AUTHORS
1 files changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1 @@
+Volodymyr Kononenko <vm at kononenko.ws>


Modified: pairtaghighlighter/COPYING
25 files changed, 25 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,25 @@
+Copyright (c) 2013, Volodymyr Kononenko
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+    - Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    - 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+"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 COPYRIGHT
+HOLDER OR CONTRIBUTORS 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.


Modified: pairtaghighlighter/ChangeLog
3 files changed, 3 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,3 @@
+Changes in v1.0                         Apr 5, 2013
+
+  * The first public version.


Modified: pairtaghighlighter/Makefile.am
4 files changed, 4 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,4 @@
+include $(top_srcdir)/build/vars.auxfiles.mk
+
+SUBDIRS = src
+plugin = pairtaghighlighter


Modified: pairtaghighlighter/NEWS
0 files changed, 0 insertions(+), 0 deletions(-)
===================================================================
No diff available, check online


Modified: pairtaghighlighter/README
28 files changed, 28 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,28 @@
+Pair Tag Highlighter
+====================
+
+About
+-----
+
+Finds and highlights matching opening/closing HTML tag by clicking or
+moving cursor inside a tag.
+
+Usage
+-----
+
+Just enable plugin through Geany's plugin manager.
+
+Licence
+-------
+
+This plugin is distributed under the terms of the BSD 2-Clause License.
+You should have received a copy of the the BSD 2-Clause License in the
+file COPYING included with the source code of this plugin. If not, find
+it at <http://opensource.org/licenses/BSD-2-Clause>.
+
+Contact developer
+-----------------
+
+You may contact developer (Volodymyr Kononenko) via e-mail:
+<vm(at)kononenko(dot)ws>. The complete up to date list of contacts
+can be found at <http://kononenko.ws/en/contacts>.


Modified: pairtaghighlighter/src/Makefile.am
9 files changed, 9 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,9 @@
+include $(top_srcdir)/build/vars.build.mk
+
+geanyplugins_LTLIBRARIES = pairtaghighlighter.la
+
+pairtaghighlighter_la_SOURCES = pair_tag_highlighter.c
+
+pairtaghighlighter_la_LIBADD = $(COMMONLIBS)
+
+include $(top_srcdir)/build/cppcheck.mk


Modified: pairtaghighlighter/src/pair_tag_highlighter.c
349 files changed, 349 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,349 @@
+/*
+ * Pair Tag Highlighter
+ *
+ * highlights matching opening/closing HTML tags
+ *
+ * Author:  Volodymyr Kononenko aka kvm
+ * Email:   vm at kononenko.ws
+ *
+ */
+
+#include "config.h"
+#include <geanyplugin.h>
+#include <string.h>
+#include "Scintilla.h"  /* for the SCNotification struct */
+#include "SciLexer.h"
+
+/* If to set indicator >8, highlighting will be of grey color.
+ * Light grey line highlighter covers higher values of indicator. */
+#define INDICATOR_TAGMATCH 0
+#define MAX_TAG_NAME 64
+
+/* These items are set by Geany before plugin_init() is called. */
+GeanyPlugin     *geany_plugin;
+GeanyData       *geany_data;
+GeanyFunctions  *geany_functions;
+
+static ScintillaObject *sci;
+
+/* Is needed for clearing highlighting after moving cursor out
+ * from the tag */
+static gint highlightedBrackets[] = {0, 0, 0, 0};
+
+PLUGIN_VERSION_CHECK(211)
+
+PLUGIN_SET_TRANSLATABLE_INFO(LOCALEDIR, GETTEXT_PACKAGE, _("Pair Tag Highlighter"),
+                            _("Finds and highlights matching opening/closing HTML tag"),
+                            "1.0", "Volodymyr Kononenko <vm at kononenko.ws>")
+
+
+/* Searches tag brackets.
+ * direction variable shows sets search direction:
+ * TRUE  - to the right
+ * FALSE - to the left
+ * from the current cursor position to the start of the line.
+ */
+static gint findBracket(gint position, gint endOfSearchPos,
+                        gchar searchedBracket, gchar breakBracket, gboolean direction)
+{
+    gint foundBracket = -1;
+    gint pos;
+
+    if(TRUE == direction)
+    {
+        /* search to the right */
+        for(pos=position; pos<=endOfSearchPos; pos++)
+        {
+            gchar charAtCurPosition = sci_get_char_at(sci, pos);
+            if(charAtCurPosition == searchedBracket)
+            {
+                foundBracket = pos;
+                break;
+            }
+            if(charAtCurPosition == breakBracket)
+                break;
+        }
+    }
+    else
+    {
+        /* search to the left */
+        for(pos=position-1; pos>=endOfSearchPos; pos--)
+        {
+            gchar charAtCurPosition = sci_get_char_at(sci, pos);
+            if(charAtCurPosition == searchedBracket)
+            {
+                foundBracket = pos;
+                break;
+            }
+            if(charAtCurPosition == breakBracket)
+                break;
+        }
+    }
+
+    return foundBracket;
+}
+
+
+static void highlight_tag(gint openingBracket, gint closingBracket)
+{
+    scintilla_send_message(sci, SCI_SETINDICATORCURRENT, INDICATOR_TAGMATCH, 0);
+    scintilla_send_message(sci, SCI_INDICSETSTYLE,
+                            INDICATOR_TAGMATCH, INDIC_ROUNDBOX);
+    scintilla_send_message(sci, SCI_INDICSETFORE, 0, 0x00d000); // green
+    scintilla_send_message(sci, SCI_INDICSETALPHA, INDICATOR_TAGMATCH, 60);
+    scintilla_send_message(sci, SCI_INDICATORFILLRANGE,
+                            openingBracket, closingBracket-openingBracket+1);
+}
+
+
+static void clear_previous_highlighting(gint rangeStart, gint rangeEnd)
+{
+    scintilla_send_message(sci, SCI_SETINDICATORCURRENT, INDICATOR_TAGMATCH, 0);
+    scintilla_send_message(sci, SCI_INDICATORCLEARRANGE, rangeStart, rangeEnd+1);
+}
+
+
+static gboolean is_tag_self_closing(gint closingBracket)
+{
+    gboolean isTagSelfClosing = FALSE;
+    gchar charBeforeBracket = sci_get_char_at(sci, closingBracket-1);
+
+    if('/' == charBeforeBracket)
+        isTagSelfClosing = TRUE;
+    return isTagSelfClosing;
+}
+
+
+static gboolean is_tag_opening(gint openingBracket)
+{
+    gboolean isTagOpening = TRUE;
+    gchar charAfterBracket = sci_get_char_at(sci, openingBracket+1);
+
+    if('/' == charAfterBracket)
+        isTagOpening = FALSE;
+    return isTagOpening;
+}
+
+
+static void get_tag_name(gint openingBracket, gint closingBracket,
+                    gchar tagName[], gboolean isTagOpening)
+{
+    gint nameStart = openingBracket + (TRUE == isTagOpening ? 1 : 2);
+    gint nameEnd = nameStart;
+    gchar charAtCurPosition = sci_get_char_at(sci, nameStart);
+
+    while(' ' != charAtCurPosition && '>' != charAtCurPosition &&
+        '\t' != charAtCurPosition && '\r' != charAtCurPosition && '\n' != charAtCurPosition)
+    {
+        charAtCurPosition = sci_get_char_at(sci, nameEnd);
+        nameEnd++;
+        if(nameEnd-nameStart > MAX_TAG_NAME)
+            break;
+    }
+    sci_get_text_range(sci, nameStart, nameEnd-1, tagName);
+}
+
+
+static void findMatchingOpeningTag(gchar *tagName, gint openingBracket)
+{
+    gint pos;
+    gint openingTagsCount = 0;
+    gint closingTagsCount = 1;
+
+    for(pos=openingBracket; pos>0; pos--)
+    {
+        /* are we inside tag? */
+        gint lineNumber = sci_get_line_from_position(sci, pos);
+        gint lineStart = sci_get_position_from_line(sci, lineNumber);
+        gint matchingOpeningBracket = findBracket(pos, lineStart, '<', '\0', FALSE);
+        gint matchingClosingBracket = findBracket(pos, lineStart, '>', '\0', FALSE);
+
+        if(-1 != matchingOpeningBracket && -1 != matchingClosingBracket
+            && (matchingClosingBracket > matchingOpeningBracket))
+        {
+            /* we are inside of some tag. Let us check what tag*/
+            gchar matchingTagName[MAX_TAG_NAME];
+            gboolean isMatchingTagOpening = is_tag_opening(matchingOpeningBracket);
+            get_tag_name(matchingOpeningBracket, matchingClosingBracket,
+                            matchingTagName, isMatchingTagOpening);
+            if(strcmp(tagName, matchingTagName) == 0)
+            {
+                if(TRUE == isMatchingTagOpening)
+                    openingTagsCount++;
+                else
+                    closingTagsCount++;
+            }
+            pos = matchingOpeningBracket+1;
+        }
+        /* Speed up search: if findBracket returns -1, that means start of line
+         * is reached. There is no need to go through the same positions again.
+         * Jump to the start of line */
+        else if(-1 == matchingOpeningBracket || -1 == matchingClosingBracket)
+        {
+            pos = lineStart;
+            continue;
+        }
+        if(openingTagsCount == closingTagsCount)
+        {
+            /* matching tag is found */
+            highlight_tag(matchingOpeningBracket, matchingClosingBracket);
+            highlightedBrackets[2] = matchingOpeningBracket;
+            highlightedBrackets[3] = matchingClosingBracket;
+            break;
+        }
+    }
+}
+
+
+static void findMatchingClosingTag(gchar *tagName, gint closingBracket)
+{
+    gint pos;
+    gint linesInDocument = sci_get_line_count(sci);
+    gint endOfDocument = sci_get_position_from_line(sci, linesInDocument);
+    gint openingTagsCount = 1;
+    gint closingTagsCount = 0;
+
+    for(pos=closingBracket; pos<endOfDocument; pos++)
+    {
+        /* are we inside tag? */
+        gint lineNumber = sci_get_line_from_position(sci, pos);
+        gint lineEnd = sci_get_line_end_position(sci, lineNumber);
+        gint matchingOpeningBracket = findBracket(pos, endOfDocument, '<', '\0', TRUE);
+        gint matchingClosingBracket = findBracket(pos, endOfDocument, '>', '\0', TRUE);
+
+        if(-1 != matchingOpeningBracket && -1 != matchingClosingBracket
+            && (matchingClosingBracket > matchingOpeningBracket))
+        {
+            /* we are inside of some tag. Let us check what tag*/
+            gchar matchingTagName[64];
+            gboolean isMatchingTagOpening = is_tag_opening(matchingOpeningBracket);
+            get_tag_name(matchingOpeningBracket, matchingClosingBracket,
+                            matchingTagName, isMatchingTagOpening);
+            if(strcmp(tagName, matchingTagName) == 0)
+            {
+                if(TRUE == isMatchingTagOpening)
+                    openingTagsCount++;
+                else
+                    closingTagsCount++;
+            }
+            pos = matchingClosingBracket;
+        }
+        /* Speed up search: if findBracket returns -1, that means end of line
+         * is reached. There is no need to go through the same positions again.
+         * Jump to the end of line */
+        else if(-1 == matchingOpeningBracket || -1 == matchingClosingBracket)
+        {
+            pos = lineEnd;
+            continue;
+        }
+        if(openingTagsCount == closingTagsCount)
+        {
+            /* matching tag is found */
+            highlight_tag(matchingOpeningBracket, matchingClosingBracket);
+            highlightedBrackets[2] = matchingOpeningBracket;
+            highlightedBrackets[3] = matchingClosingBracket;
+            break;
+        }
+    }
+}
+
+
+static void findMatchingTag(gint openingBracket, gint closingBracket)
+{
+    gchar tagName[MAX_TAG_NAME];
+    gboolean isTagOpening = is_tag_opening(openingBracket);
+    get_tag_name(openingBracket, closingBracket, tagName, isTagOpening);
+
+    if(TRUE == isTagOpening)
+        findMatchingClosingTag(tagName, closingBracket);
+    else
+        findMatchingOpeningTag(tagName, openingBracket);
+}
+
+
+static void run_tag_highlighter(void)
+{
+    gint position = sci_get_current_position(sci);
+    gint lineNumber = sci_get_current_line(sci);
+    gint lineStart = sci_get_position_from_line(sci, lineNumber);
+    gint lineEnd = sci_get_line_end_position(sci, lineNumber);
+    gint openingBracket = findBracket(position, lineStart, '<', '>', FALSE);
+    gint closingBracket = findBracket(position, lineEnd, '>', '<', TRUE);
+    int i;
+
+    if(-1 == openingBracket || -1 == closingBracket)
+    {
+        clear_previous_highlighting(highlightedBrackets[0], highlightedBrackets[1]);
+        clear_previous_highlighting(highlightedBrackets[2], highlightedBrackets[3]);
+        for(i=0; i<3; i++)
+            highlightedBrackets[i] = 0;
+        return;
+    }
+
+    /* If the cursor jumps from one tag into another, clear
+     * previous highlighted tags*/
+    if(openingBracket != highlightedBrackets[0] ||
+        closingBracket != highlightedBrackets[1])
+    {
+        clear_previous_highlighting(highlightedBrackets[0], highlightedBrackets[1]);
+        clear_previous_highlighting(highlightedBrackets[2], highlightedBrackets[3]);
+    }
+
+    highlightedBrackets[0] = openingBracket;
+    highlightedBrackets[1] = closingBracket;
+
+    /* Highlight current tag. Matching tag will be highlighted from
+     * findMatchingTag() functiong */
+    highlight_tag(openingBracket, closingBracket);
+
+    /* Find matching tag only if a tag is not self-closing */
+    if(FALSE == is_tag_self_closing(closingBracket))
+        findMatchingTag(openingBracket, closingBracket);
+}
+
+
+/* Notification handler for editor-notify */
+static gboolean on_editor_notify(GObject *obj, GeanyEditor *editor,
+                                SCNotification *nt, gpointer user_data)
+{
+    gint lexer;
+
+    /* setting global sci variable to be available in other functions */
+    sci = editor->sci;
+
+    lexer = sci_get_lexer(sci);
+    if(lexer != SCLEX_HTML)
+    {
+        return FALSE;
+    }
+
+    /* nmhdr is a structure containing information about the event */
+    switch (nt->nmhdr.code)
+    {
+        case SCN_UPDATEUI:
+            run_tag_highlighter();
+            break;
+    }
+
+    /* returning FALSE to allow Geany processing the event */
+    return FALSE;
+}
+
+
+PluginCallback plugin_callbacks[] =
+{
+    { "editor-notify", (GCallback) &on_editor_notify, FALSE, NULL },
+    { NULL, NULL, FALSE, NULL }
+};
+
+
+void plugin_init(GeanyData *data)
+{
+}
+
+
+void plugin_cleanup(void)
+{
+    clear_previous_highlighting(highlightedBrackets[0], highlightedBrackets[1]);
+    clear_previous_highlighting(highlightedBrackets[2], highlightedBrackets[3]);
+}



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Plugins-Commits mailing list