Commit c60deaea authored by Daniel Brötzmann's avatar Daniel Brötzmann

[syntax_highlight] Fix deprecation warnings, fix pylint errors

This fixes some deprecation warnings related to Gtk.Color and restructures
code and formatting according to pylint suggestions
parent 845faff2
# Syntax Highlighting Plugin for Gajim
[Gajim](https://gajim.org) Plugin that highlights source code blocks in chatbox.
[Gajim](https://gajim.org) Plugin that highlights source code blocks in the chat window.
## Installation
The recommended way of installing this plugin is to use
Gajim's Plugin Installer.
The recommended way of installing this plugin is to use Gajim's Plugin Installer.
For more information and instruction on how to install plugins manually, please
refer to the [Gajim Plugin Wiki seite](https://dev.gajim.org/gajim/gajim-plugins/wikis/home#how-to-install-plugins).
refer to the [Gajim Plugin Wiki site](https://dev.gajim.org/gajim/gajim-plugins/wikis/home#how-to-install-plugins).
## Usage
This plugin uses markdown-style syntax to identify which parts of a message
should be formatted as code in the chatbox.
should be formatted as code in the chat window.
```
Inline source code will be highlighted when placed in between `two single
Inline source code will be highlighted when placed in between `two single
back-ticks`.
```
......@@ -35,8 +34,8 @@ i.e. there must be a newline here.
```
````
In case no languge is specified with the opening tag or the specified language
could not be identified, the default languge configured in the settings is
In case no language is specified with the opening tag or the specified language
could not be identified, the default language configured in the settings is
used.
You can test it by copying and sending the following text to one of your
......@@ -53,20 +52,19 @@ using the plugin.)
## Relation to XEP-0393 - 'Message Styling'
https://xmpp.org/extensions/xep-0393.html#pre-block
In [XEP-0393](https://xmpp.org/extensions/xep-0393.html),
the back-tick based syntax is defined as markup for preformatted
text blocks, respectively inline performatted text.
Formatting of such text blocks with monospaced fonts is recommended by the XEP.
text blocks, respectively inline preformatted text.
Formatting of such text blocks with mono-spaced fonts is recommended by the XEP.
By using the same syntax as defined in XEP-0393 XMPP clients with only XEP-0393
support but without syntax highlighting can at least present their users blocks
of pre-formatted text.
of preformatted text.
Since text in between the back-tick markers is not further formatted by this
plugin, it can be considered "pre-formatted".
plugin, it can be considered "preformatted".
Hence, this plugin is compatible to the formatting options defined by XEP-0393,
[section 5.1.2, "Preformatted Text"](https://xmpp.org/extensions/xep-0393.html#pre-block)
and [section 5.2.5, "Preformatted Span"](https://xmpp.org/extensions/xep-0393.html#mono).
......@@ -84,7 +82,7 @@ including default language, style, font settings, background color and formattin
of the code markers.
In the configuration window, the current settings are displayed in an
interactive preview pannel. This allows you to directly check how code would
interactive preview panel. This allows you to directly check how code would
look like in the message
window.
......@@ -117,7 +115,7 @@ in a terminal to display the debug messages.
## Credits
Since I had no experience in writing Plugins for Gajim, I used the
[Latex Plugin](https://trac-plugins.gajim.org/wiki/LatexPlugin)
[Latex Plugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/LatexPlugin)
written by Yves Fischer and Yann Leboulanger as an example and copied a big
portion of initial code. Therefore, credits go to the authors of the Latex
Plugin for providing an example.
......
This diff is collapsed.
This diff is collapsed.
import logging
from gi.repository import Gtk as gtk
from gi.repository import Gtk
from gi.repository import Pango
from pygments.formatter import Formatter
from gajim.plugins.helpers import log
log = logging.getLogger('gajim.p.syntax_highlight')
class GTKFormatter(Formatter):
name = 'GTK Formatter'
aliases = ['textbuffer', 'gtk']
......@@ -14,14 +15,14 @@ class GTKFormatter(Formatter):
def __init__(self, **options):
super(GTKFormatter, self).__init__(**options)
#Formatter.__init__(self, **options)
# Formatter.__init__(self, **options)
self.tags = {}
self.mark = options.get('start_mark', None)
@staticmethod
def create_tag_for_token(ttype, highlighting_style):
style = highlighting_style.style_for_token(ttype)
tag = gtk.TextTag.new()
tag = Gtk.TextTag.new()
if 'bgcolor' in style and not style['bgcolor'] is None:
tag.set_property('background', '#%s' % style['bgcolor'])
if 'bold' in style and style['bold']:
......@@ -44,13 +45,13 @@ class GTKFormatter(Formatter):
tag.set_property('underline', 'single')
return tag
def get_tag(self, ttype, buf):
"""
Creates, stores and returs a tag for a given token type.
This method ensures that a tag style is created only once.
Furthermore, the tag will be added to the given Gtk.TextBuffer's tag table.
Furthermore, the tag will be added to the given Gtk.TextBuffer's
tag table.
"""
tag = None
if ttype in self.tags:
......@@ -65,23 +66,23 @@ class GTKFormatter(Formatter):
self.mark = mark
def format(self, tokensource, outfile):
if not isinstance(outfile, gtk.TextBuffer) or outfile is None:
if not isinstance(outfile, Gtk.TextBuffer) or outfile is None:
log.warn("Did not get a buffer to format...")
return
buf = outfile
end_iter = buf.get_end_iter()
end_iter = buf.get_end_iter()
start_mark = self.mark
start_iter = buf.get_iter_at_mark(start_mark) if not start_mark is None \
else end_iter
start_mark = self.mark
start_iter = buf.get_iter_at_mark(start_mark) if \
start_mark is not None else end_iter
last_ttype = None
last_start = start_iter
last_end = buf.get_end_iter()
last_ttype = None
last_start = start_iter
last_end = buf.get_end_iter()
last_fixed_start = last_start
reset = True
reset = True
for ttype, value in tokensource:
search = None
......@@ -90,24 +91,26 @@ class GTKFormatter(Formatter):
buf.apply_tag(tag, last_fixed_start, last_end)
search = last_end.forward_search(value, gtk.TextSearchFlags.TEXT_ONLY, end_iter)
search = last_end.forward_search(
value, Gtk.TextSearchFlags.TEXT_ONLY, end_iter)
reset = True
else:
# in case last_ttype is None, i.e. first loop walkthrough:
# In case last_ttype is None, i.e. first loop walkthrough:
# last_start to end_iter is the full code block.
search = last_start.forward_search(value, gtk.TextSearchFlags.TEXT_ONLY, end_iter)
search = last_start.forward_search(
value, Gtk.TextSearchFlags.TEXT_ONLY, end_iter)
# Prepare for next iteration
last_ttype = ttype
if search is not None:
(last_start, last_end) = search
# If we've found the end of a sequence of similar type tokens or if
# we are in the first loop iteration, set the fixed point
# If we've found the end of a sequence of similar type tokens
# or if we are in the first loop iteration, set the fixed point
if reset:
last_fixed_start = last_start
reset = False
else:
# Hm... Nothing found, but tags left? Seams there's nothing we
# Hm... Nothing found, but tags left? Seems there's nothing we
# can do now.
break
from gajim.plugins.helpers import log
import logging
from gi.repository import Gdk
from pygments.lexers import get_lexer_by_name, get_all_lexers
from pygments.lexers import get_lexer_by_name
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles
from pygments.util import ClassNotFound
from syntax_highlight.types import LineBreakOptions
from syntax_highlight.types import CodeMarkerOptions
from syntax_highlight.types import PLUGIN_INTERNAL_NONE_LEXER_ID
log = logging.getLogger('gajim.p.syntax_highlight')
from .types import LineBreakOptions, CodeMarkerOptions, \
PLUGIN_INTERNAL_NONE_LEXER_ID
class SyntaxHighlighterConfig:
PLUGIN_INTERNAL_NONE_LEXER=('None (monospace only)', PLUGIN_INTERNAL_NONE_LEXER_ID)
PLUGIN_INTERNAL_NONE_LEXER = ('None (monospace only)',
PLUGIN_INTERNAL_NONE_LEXER_ID)
def _create_lexer_list(self):
# The list we create here contains the plain text name and the lexer's
# id string
lexers = []
# Iteration over get_all_lexers() seems to be broken somehow. Workarround
# Iteration over get_all_lexers() seems to be broken somehow
# Workaround
all_lexers = get_all_lexers()
for lexer in all_lexers:
# We don't want to add lexers that we cant identify by name later
......@@ -24,7 +32,7 @@ class SyntaxHighlighterConfig:
lexers.append((lexer[0], lexer[1][0]))
lexers.sort()
# Insert our internal "none" type at top of the list.
# Insert our internal 'none' type at top of the list
lexers.insert(0, self.PLUGIN_INTERNAL_NONE_LEXER)
return lexers
......@@ -38,24 +46,24 @@ class SyntaxHighlighterConfig:
lexer = None
try:
lexer = get_lexer_by_name(name)
except:
except ClassNotFound:
pass
return lexer
def get_lexer_with_fallback(self, language):
lexer = self.get_lexer_by_name(language)
if lexer is None:
log.info("Falling back to default lexer for %s.",
self.get_default_lexer_name())
log.info('Falling back to default lexer for %s.',
self.get_default_lexer_name())
lexer = self.default_lexer[1]
return lexer
def set_font(self, font):
if font is not None and font != "":
if font is not None and font != '':
self.config['font'] = font
def set_style(self, style):
if style is not None and style != "":
if style is not None and style != '':
self.config['style'] = style
def set_line_break_action(self, option):
......@@ -68,14 +76,16 @@ class SyntaxHighlighterConfig:
lexer = get_lexer_by_name(name)
if lexer is None and self.default_lexer is None:
log.error("Failed to get default lexer by name."\
"Falling back to simply using the first in the list.")
log.error('Failed to get default lexer by name.'
'Falling back to simply using the first lexer '
'in the list.')
lexer = self.lexer_list[0]
name = lexer[0]
name = lexer[0]
self.default_lexer = (name, lexer)
if lexer is None and self.default_lexer is not None:
log.info("Failed to get default lexer by name, keeping previous"\
"setting (lexer = %s).", self.default_lexer[0])
log.info('Failed to get default lexer by name, keeping '
'previous setting (lexer = %s).',
self.default_lexer[0])
name = self.default_lexer[0]
else:
self.default_lexer = (name, lexer)
......@@ -88,7 +98,7 @@ class SyntaxHighlighterConfig:
self.config['bgcolor_override'] = state
def set_bgcolor(self, color):
if isinstance(color, Gdk.Color):
if isinstance(color, Gdk.RGBA):
color = color.to_string()
self.config['bgcolor'] = color
......@@ -110,9 +120,9 @@ class SyntaxHighlighterConfig:
return self.lexer_list
def get_line_break_action(self):
# return int only
# Return int only
if isinstance(self.config['line_break'], int):
# in case of legacy settings, convert.
# In case of legacy settings, convert.
action = self.config['line_break']
self.set_line_break_action(action)
else:
......@@ -146,13 +156,13 @@ class SyntaxHighlighterConfig:
Initialize all config variables that depend directly on pygments being
available.
"""
self.lexer_list = self._create_lexer_list()
self.style_list = [s for s in get_all_styles()]
self.lexer_list = self._create_lexer_list()
self.style_list = [s for s in get_all_styles()]
self.style_list.sort()
self.set_default_lexer(self.config['default_lexer'])
def __init__(self, config):
self.lexer_list = []
self.style_list = []
self.config = config
self.default_lexer = None
self.lexer_list = []
self.style_list = []
self.config = config
self.default_lexer = None
This diff is collapsed.
import logging
import sys
from gajim.plugins import GajimPlugin
from syntax_highlight.types import LineBreakOptions
from syntax_highlight.types import CodeMarkerOptions
from syntax_highlight.types import PLUGIN_INTERNAL_NONE_LEXER_ID
if sys.version_info >= (3, 4):
from importlib.util import find_spec as find_module
else:
from importlib import find_loader as find_module
from gajim.plugins.helpers import log_calls, log
from gajim.plugins import GajimPlugin
from .types import MatchType, LineBreakOptions, CodeMarkerOptions, \
PLUGIN_INTERNAL_NONE_LEXER_ID
PYGMENTS_MISSING = 'You are missing Python-Pygments.'
log = logging.getLogger('gajim.p.syntax_highlight')
def try_loading_pygments():
success = find_module('pygments') is not None
if success:
try:
from .chat_syntax_highlighter import ChatSyntaxHighlighter
from .plugin_config_dialog import SyntaxHighlighterPluginConfiguration
from .plugin_config import SyntaxHighlighterConfig
global SyntaxHighlighterPluginConfiguration, ChatSyntaxHighlighter, \
SyntaxHighlighterConfig
from syntax_highlight.chat_syntax_highlighter import \
ChatSyntaxHighlighter
from syntax_highlight.plugin_config_dialog import \
SyntaxHighlighterPluginConfiguration
from syntax_highlight.plugin_config import SyntaxHighlighterConfig
global SyntaxHighlighterPluginConfiguration
global ChatSyntaxHighlighter
global SyntaxHighlighterConfig
success = True
log.debug("pygments loaded.")
except Exception as exception:
......@@ -32,27 +36,21 @@ def try_loading_pygments():
return success
PYGMENTS_MISSING = 'You are missing Python-Pygments.'
class SyntaxHighlighterPlugin(GajimPlugin):
@log_calls('SyntaxHighlighterPlugin')
def on_connect_with_chat_control(self, chat_control):
account = chat_control.contact.account.name
jid = chat_control.contact.jid
jid = chat_control.contact.jid
if account not in self.ccontrol:
self.ccontrol[account] = {}
self.ccontrol[account][jid] = ChatSyntaxHighlighter(
self.conf, chat_control.conv_textview)
self.conf, chat_control.conv_textview)
@log_calls('SyntaxHighlighterPlugin')
def on_disconnect_from_chat_control(self, chat_control):
account = chat_control.contact.account.name
jid = chat_control.contact.jid
del self.ccontrol[account][jid]
@log_calls('SyntaxHighlighterPlugin')
def on_print_real_text(self, text_view, real_text, other_tags, graphics,
iterator, additional):
account = text_view.account
......@@ -78,36 +76,32 @@ class SyntaxHighlighterPlugin(GajimPlugin):
self.available_text = None
self.config_dialog = SyntaxHighlighterPluginConfiguration(self)
self.conf = SyntaxHighlighterConfig(self.config)
self.conf = SyntaxHighlighterConfig(self.config)
# The following initialization requires pygments to be available.
self.conf.init_pygments()
self.config_dialog = SyntaxHighlighterPluginConfiguration(self)
self.config_dialog = SyntaxHighlighterPluginConfiguration(self)
self.config_dialog.set_config(self.conf)
self.gui_extension_points = {
'chat_control_base': (
self.on_connect_with_chat_control,
self.on_disconnect_from_chat_control
),
'print_real_text': (self.on_print_real_text, None),
}
'chat_control_base': (
self.on_connect_with_chat_control,
self.on_disconnect_from_chat_control),
'print_real_text': (self.on_print_real_text, None), }
return True
@log_calls('SyntaxHighlighterPlugin')
def init(self):
self.ccontrol = {}
self.ccontrol = {}
self.config_default_values = {
'default_lexer' : (PLUGIN_INTERNAL_NONE_LEXER_ID, ''),
'line_break' : (LineBreakOptions.MULTILINE, ''),
'style' : ('default', ''),
'font' : ('Monospace 10', ''),
'bgcolor' : ('#ccc', ''),
'bgcolor_override' : (True, ''),
'code_marker' : (CodeMarkerOptions.AS_COMMENT, ''),
'pygments_path' : (None, ''),
}
'default_lexer': (PLUGIN_INTERNAL_NONE_LEXER_ID, ''),
'line_break': (LineBreakOptions.MULTILINE, ''),
'style': ('default', ''),
'font': ('Monospace 10', ''),
'bgcolor': ('#ccc', ''),
'bgcolor_override': (True, ''),
'code_marker': (CodeMarkerOptions.AS_COMMENT, ''),
'pygments_path': (None, ''), }
is_initialized = self.try_init()
......
from enum import Enum, IntEnum, unique
from enum import Enum
from enum import IntEnum
from enum import unique
PLUGIN_INTERNAL_NONE_LEXER_ID = '_syntax_highlight_internal_none_type'
PLUGIN_INTERNAL_NONE_LEXER_ID='_syntax_highlight_internal_none_type'
class MatchType(Enum):
INLINE = 0
MULTILINE = 1
TEXT = 2
INLINE = 0
MULTILINE = 1
TEXT = 2
@unique
class LineBreakOptions(IntEnum):
NEVER = 0
ALWAYS = 1
MULTILINE = 2
NEVER = 0
ALWAYS = 1
MULTILINE = 2
@unique
class CodeMarkerOptions(IntEnum):
AS_COMMENT = 0
HIDE = 1
AS_COMMENT = 0
HIDE = 1
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment