[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