[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
This commit is contained in:
Daniel Brötzmann
2019-05-08 11:53:13 +02:00
parent 845faff240
commit c60deaea19
8 changed files with 511 additions and 509 deletions

View File

@@ -1,23 +1,22 @@
# Syntax Highlighting Plugin for Gajim # 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 ## Installation
The recommended way of installing this plugin is to use The recommended way of installing this plugin is to use Gajim's Plugin Installer.
Gajim's Plugin Installer.
For more information and instruction on how to install plugins manually, please 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 ## Usage
This plugin uses markdown-style syntax to identify which parts of a message 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`. 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 In case no language is specified with the opening tag or the specified language
could not be identified, the default languge configured in the settings is could not be identified, the default language configured in the settings is
used. used.
You can test it by copying and sending the following text to one of your 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' ## Relation to XEP-0393 - 'Message Styling'
https://xmpp.org/extensions/xep-0393.html#pre-block https://xmpp.org/extensions/xep-0393.html#pre-block
In [XEP-0393](https://xmpp.org/extensions/xep-0393.html), In [XEP-0393](https://xmpp.org/extensions/xep-0393.html),
the back-tick based syntax is defined as markup for preformatted the back-tick based syntax is defined as markup for preformatted
text blocks, respectively inline performatted text. text blocks, respectively inline preformatted text.
Formatting of such text blocks with monospaced fonts is recommended by the XEP. 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 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 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 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, 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) [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). 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. of the code markers.
In the configuration window, the current settings are displayed in an 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 look like in the message
window. window.
@@ -117,7 +115,7 @@ in a terminal to display the debug messages.
## Credits ## Credits
Since I had no experience in writing Plugins for Gajim, I used the 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 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 portion of initial code. Therefore, credits go to the authors of the Latex
Plugin for providing an example. Plugin for providing an example.

View File

@@ -4,15 +4,14 @@ import pygments
from gi.repository import Gtk from gi.repository import Gtk
from gajim.plugins.helpers import log from syntax_highlight.gtkformatter import GTKFormatter
from syntax_highlight.types import MatchType
from syntax_highlight.types import LineBreakOptions
from .gtkformatter import GTKFormatter from syntax_highlight.types import CodeMarkerOptions
from .types import MatchType, LineBreakOptions, CodeMarkerOptions
log = logging.getLogger('gajim.p.syntax_highlight') log = logging.getLogger('gajim.p.syntax_highlight')
class ChatSyntaxHighlighter: class ChatSyntaxHighlighter:
def hide_code_markup(self, buf, start, end): def hide_code_markup(self, buf, start, end):
tag = buf.get_tag_table().lookup('hide_code_markup') tag = buf.get_tag_table().lookup('hide_code_markup')
@@ -27,8 +26,7 @@ class ChatSyntaxHighlighter:
line_break = self.config.get_line_break_action() line_break = self.config.get_line_break_action()
return (line_break == LineBreakOptions.ALWAYS) \ return (line_break == LineBreakOptions.ALWAYS) \
or (is_multiline and line_break == LineBreakOptions.MULTILINE) or (is_multiline and line_break == LineBreakOptions.MULTILINE)
def format_code(self, buf, s_tag, s_code, e_tag, e_code, language): def format_code(self, buf, s_tag, s_code, e_tag, e_code, language):
style = self.config.get_style_name() style = self.config.get_style_name()
@@ -37,26 +35,26 @@ class ChatSyntaxHighlighter:
self.hide_code_markup(buf, e_code, e_tag) self.hide_code_markup(buf, e_code, e_tag)
else: else:
comment_tag = GTKFormatter.create_tag_for_token( comment_tag = GTKFormatter.create_tag_for_token(
pygments.token.Comment, pygments.token.Comment,
pygments.styles.get_style_by_name(style)) pygments.styles.get_style_by_name(style))
buf.get_tag_table().add(comment_tag) buf.get_tag_table().add(comment_tag)
buf.apply_tag(comment_tag, s_tag, s_code) buf.apply_tag(comment_tag, s_tag, s_code)
buf.apply_tag(comment_tag, e_tag, e_code) buf.apply_tag(comment_tag, e_tag, e_code)
code = s_code.get_text(e_code) code = s_code.get_text(e_code)
log.debug("full text to encode: %s.", code) log.debug('full text to encode: %s.', code)
start_mark = buf.create_mark(None, s_code, False)
start_mark = buf.create_mark(None, s_code, False)
lexer = None lexer = None
if language is None: if language is None:
lexer = self.config.get_default_lexer() lexer = self.config.get_default_lexer()
log.info("No Language specified. Falling back to default lexer: %s.", log.info('No Language specified. '
self.config.get_default_lexer_name()) 'Falling back to default lexer: %s.',
self.config.get_default_lexer_name())
else: else:
log.debug("Using lexer for %s.", str(language)) log.debug('Using lexer for %s.', str(language))
lexer = self.config.get_lexer_with_fallback(language) lexer = self.config.get_lexer_with_fallback(language)
if lexer is None: if lexer is None:
@@ -71,13 +69,15 @@ class ChatSyntaxHighlighter:
def find_multiline_matches(self, text): def find_multiline_matches(self, text):
start = None start = None
matches = [] matches = []
#Less strict, allow prefixed whitespaces: for i in re.finditer(r'(?:^|\n)[ |\t]*(```)\S*[ |\t]*(?:\n|$)', text, re.DOTALL): # Less strict, allow prefixed whitespaces:
# for i in re.finditer(r'(?:^|\n)[ |\t]*(```)\S*[ |\t]*(?:\n|$)',
# text, re.DOTALL):
for i in re.finditer(r'(?:^|\n)(```)\S*(?:\n|$)', text, re.DOTALL): for i in re.finditer(r'(?:^|\n)(```)\S*(?:\n|$)', text, re.DOTALL):
if start is None: if start is None:
start = i start = i
elif re.match(r'^\n```', i.group(0)) is not None: elif re.match(r'^\n```', i.group(0)) is not None:
matches.append( matches.append(
(start.start(), i.end(), text[start.start():i.end()])) (start.start(), i.end(), text[start.start():i.end()]))
start = None start = None
else: else:
# not an end... # not an end...
@@ -91,37 +91,40 @@ class ChatSyntaxHighlighter:
defined in XEP-0393. defined in XEP-0393.
The same applies mirrored to the end marker. The same applies mirrored to the end marker.
""" """
return [(i.start(1), i.end(1), i.group(1)) for i in \ return [(i.start(1), i.end(1), i.group(1)) for i in
re.finditer(r'(?:^|\s|\*|~|_)(`((?!`).+?)`)(?:\s|\*|~|_|$)', text)] re.finditer(r'(?:^|\s|\*|~|_)(`((?!`).+?)`)(?:\s|\*|~|_|$)',
text)]
def merge_match_groups(self, real_text, inline_matches, multiline_matches): def merge_match_groups(self, real_text, inline_matches, multiline_matches):
it_inline = iter(inline_matches) it_inline = iter(inline_matches)
it_multi = iter(multiline_matches) it_multi = iter(multiline_matches)
length = len(real_text) length = len(real_text)
# Just to get cleaner code below... # Just to get cleaner code below...
def get_next(iterator): def get_next(iterator):
return next(iterator, (length, length, "")) return next(iterator, (length, length, ''))
# In order to simplify the process, we use the 'length' here. # In order to simplify the process, we use the 'length' here.
cur_inline = get_next(it_inline) cur_inline = get_next(it_inline)
cur_multi = get_next(it_multi) cur_multi = get_next(it_multi)
pos = 0 pos = 0
# This will contain tuples with parts of the input and its classification # This will contain tuples with parts of the input and its
parts = [] # classification
parts = []
while pos < length: while pos < length:
log.debug("-> in: %s", str(cur_inline)) log.debug('-> in: %s', str(cur_inline))
log.debug("-> mu: %s", str(cur_multi)) log.debug('-> mu: %s', str(cur_multi))
# selected = (start, end, type) # selected = (start, end, type)
selected = (cur_inline[0], cur_inline[1], MatchType.INLINE) \ if cur_inline[0] < cur_multi[0]:
if cur_inline[0] < cur_multi[0] \ selected = (cur_inline[0], cur_inline[1], MatchType.INLINE)
else (cur_multi[0], cur_multi[1], MatchType.MULTILINE) \ elif cur_multi[0] < length:
if cur_multi[0] < length \ selected = (cur_multi[0], cur_multi[1], MatchType.MULTILINE)
else (pos, length, MatchType.TEXT) else:
log.debug("--> select: %s", str(selected)) selected = (pos, length, MatchType.TEXT)
log.debug('--> select: %s', str(selected))
# Handle plain text string parts (and unforseen errors...) # Handle plain text string parts (and unforseen errors...)
if pos < selected[0]: if pos < selected[0]:
@@ -129,7 +132,7 @@ class ChatSyntaxHighlighter:
parts.append((real_text[pos:end], MatchType.TEXT)) parts.append((real_text[pos:end], MatchType.TEXT))
pos = selected[0] pos = selected[0]
elif pos > selected[0]: elif pos > selected[0]:
log.error("Should not happen, position > found match.") log.error('Should not happen, position > found match.')
# Cut out and append selected text segment # Cut out and append selected text segment
parts.append((real_text[selected[0]:selected[1]], selected[2])) parts.append((real_text[selected[0]:selected[1]], selected[2]))
@@ -158,67 +161,76 @@ class ChatSyntaxHighlighter:
fixed = (marker_len_no_newline + 1, '\n') fixed = (marker_len_no_newline + 1, '\n')
return fixed return fixed
buf = self.textview.tv.get_buffer() buf = self.textview.tv.get_buffer()
# first, try to find inline or multiline code snippets # First, try to find inline or multiline code snippets
inline_matches = self.find_inline_matches(real_text) inline_matches = self.find_inline_matches(real_text)
multiline_matches = self.find_multiline_matches(real_text) multiline_matches = self.find_multiline_matches(real_text)
if not inline_matches and not multiline_matches: if not inline_matches and not multiline_matches:
log.debug("Stopping early, since there is no code block in it....") log.debug('Stopping early, since there is no code block in it...')
return return
iterator = iter_ if iter_ is not None else buf.get_end_iter() iterator = iter_ if iter_ is not None else buf.get_end_iter()
# Create a start marker with left gravity before inserting text. # Create a start marker with left gravity before inserting text.
start_mark = buf.create_mark("SHP_start", iterator, True) start_mark = buf.create_mark('SHP_start', iterator, True)
end_mark = buf.create_mark("SHP_end", iterator, False) end_mark = buf.create_mark('SHP_end', iterator, False)
insert_newline_for_multiline = self.check_line_break(True) insert_newline_for_multiline = self.check_line_break(True)
insert_newline_for_inline = self.check_line_break(False) insert_newline_for_inline = self.check_line_break(False)
split_text = self.merge_match_groups( split_text = self.merge_match_groups(
real_text, inline_matches, multiline_matches) real_text, inline_matches, multiline_matches)
buf.begin_user_action() buf.begin_user_action()
for num, (text_to_insert, match_type) in enumerate(split_text): for num, (text_to_insert, match_type) in enumerate(split_text):
language = None language = None
end_of_message = num == (len(split_text) - 1) end_of_message = num == (len(split_text) - 1)
if match_type == MatchType.TEXT: if match_type == MatchType.TEXT:
self.textview.detect_and_print_special_text( self.textview.detect_and_print_special_text(
text_to_insert, other_tags, graphics=_graphics, text_to_insert, other_tags, graphics=_graphics,
iter_=iterator, additional_data=_additional) iter_=iterator, additional_data=_additional)
else: else:
if match_type == MatchType.MULTILINE: if match_type == MatchType.MULTILINE:
language_match = re.search( language_match = re.search(
'\n*```([^\n]*)\n', text_to_insert, re.DOTALL) '\n*```([^\n]*)\n', text_to_insert, re.DOTALL)
language = None if language_match is None \ language = None if language_match is None \
else language_match.group(1) else language_match.group(1)
language_len = 0 if language is None else len(language) language_len = 0 if language is None else len(language)
# We account the language word width for the front marker # We account the language word width for the front marker
front = fix_newline(text_to_insert[0], 3 + language_len, front = fix_newline(
insert_newline_for_multiline) text_to_insert[0],
back = fix_newline(text_to_insert[-1], 3, 3 + language_len,
insert_newline_for_multiline and not end_of_message) insert_newline_for_multiline)
back = fix_newline(
text_to_insert[-1],
3,
insert_newline_for_multiline and not end_of_message)
else: else:
front = fix_newline(text_to_insert[0], 1, front = fix_newline(
insert_newline_for_inline) text_to_insert[0],
back = fix_newline(text_to_insert[-1], 1, 1,
insert_newline_for_inline and not end_of_message) insert_newline_for_inline)
back = fix_newline(
text_to_insert[-1],
1,
insert_newline_for_inline and not end_of_message)
marker_widths = (front[0], back[0]) marker_widths = (front[0], back[0])
text_to_insert = ''.join([front[1], text_to_insert, back[1]]) text_to_insert = ''.join([front[1], text_to_insert, back[1]])
# insertion invalidates iterator, let's use our start mark... # Insertion invalidates iterator, let's use our start mark...
self.insert_and_format_code(buf, text_to_insert, language, self.insert_and_format_code(buf, text_to_insert, language,
marker_widths, start_mark, end_mark, other_tags) marker_widths, start_mark, end_mark, other_tags)
iterator = buf.get_iter_at_mark(end_mark) iterator = buf.get_iter_at_mark(end_mark)
# the current end of the buffer's contents is the start for the # The current end of the buffer's contents is the start for the
# next iteration # next iteration
buf.move_mark(start_mark, iterator) buf.move_mark(start_mark, iterator)
@@ -235,7 +247,7 @@ class ChatSyntaxHighlighter:
def insert_and_format_code(self, buf, insert_text, language, marker, def insert_and_format_code(self, buf, insert_text, language, marker,
start_mark, end_mark, other_tags=None): start_mark, end_mark, other_tags=None):
start_iter = buf.get_iter_at_mark(start_mark) start_iter = buf.get_iter_at_mark(start_mark)
if other_tags: if other_tags:
buf.insert_with_tags_by_name(start_iter, insert_text, buf.insert_with_tags_by_name(start_iter, insert_text,
@@ -243,14 +255,14 @@ class ChatSyntaxHighlighter:
else: else:
buf.insert(start_iter, insert_text) buf.insert(start_iter, insert_text)
tag_start = buf.get_iter_at_mark(start_mark) tag_start = buf.get_iter_at_mark(start_mark)
tag_end = buf.get_iter_at_mark(end_mark) tag_end = buf.get_iter_at_mark(end_mark)
s_code = tag_start.copy() s_code = tag_start.copy()
e_code = tag_end.copy() e_code = tag_end.copy()
s_code.forward_chars(marker[0]) s_code.forward_chars(marker[0])
e_code.backward_chars(marker[1]) e_code.backward_chars(marker[1])
log.debug("full text between tags: %s.", tag_start.get_text(tag_end)) log.debug('full text between tags: %s.', tag_start.get_text(tag_end))
self.format_code(buf, tag_start, s_code, tag_end, e_code, language) self.format_code(buf, tag_start, s_code, tag_end, e_code, language)
@@ -266,6 +278,6 @@ class ChatSyntaxHighlighter:
buf.apply_tag(tag, tag_start, tag_end) buf.apply_tag(tag, tag_start, tag_end)
def __init__(self, config, textview): def __init__(self, config, textview):
self.last_end_mark = None self.last_end_mark = None
self.config = config self.config = config
self.textview = textview self.textview = textview

View File

@@ -42,271 +42,271 @@
; Test your highlighting here ; Test your highlighting here
&lt;!-- Test your highlighting here --&gt;</property> &lt;!-- Test your highlighting here --&gt;</property>
</object> </object>
<object class="GtkWindow" id="window1"> <object class="GtkBox" id="main_box">
<property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="border_width">18</property>
<property name="orientation">vertical</property>
<child> <child>
<placeholder/> <object class="GtkGrid">
</child>
<child>
<object class="GtkBox" id="mainBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="border_width">18</property> <property name="margin_bottom">40</property>
<property name="orientation">vertical</property> <property name="row_spacing">6</property>
<property name="column_spacing">12</property>
<child> <child>
<object class="GtkGrid"> <object class="GtkLabel">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_bottom">40</property> <property name="halign">end</property>
<property name="row_spacing">6</property> <property name="label" translatable="yes">Default language for syntax highlighting</property>
<property name="column_spacing">12</property> <property name="xalign">0</property>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Default language for syntax highlighting</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Insert line breaks around code blocks</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="default_lexer_combobox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="changed" handler="lexer_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="cellrenderertext1"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="line_break_combobox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">line_break_selection</property>
<signal name="changed" handler="line_break_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="cellrenderertext2"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="style_combobox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="changed" handler="style_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="cellrenderertext3"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Select syntax highlighting style</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Select code marker (the backticks) formatting</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="code_marker_combobox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">code_marker_selection</property>
<signal name="changed" handler="code_marker_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="cellrenderertext4"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Select font for code snippets</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkFontButton" id="font_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="font">Sans 12</property>
<property name="language">de-de</property>
<property name="preview_text"/>
<property name="use_font">True</property>
<signal name="font-set" handler="font_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="bg_color_checkbutton">
<property name="label" translatable="yes">Set background color</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">end</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="bg_color_enabled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkColorButton" id="bg_color_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">start</property>
<property name="title" translatable="yes">Choose the background color for code blocks</property>
<property name="show_editor">True</property>
<signal name="color-set" handler="bg_color_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
</packing>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="left_attach">0</property>
<property name="fill">False</property> <property name="top_attach">0</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">center</property> <property name="halign">end</property>
<property name="margin_top">6</property> <property name="label" translatable="yes">Insert line breaks around code blocks</property>
<property name="margin_bottom">6</property>
<property name="label" translatable="yes">Test here how code blocks will look like in the chat window</property>
<property name="xalign">0</property> <property name="xalign">0</property>
<style>
<class name="bold"/>
</style>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="left_attach">0</property>
<property name="fill">True</property> <property name="top_attach">1</property>
<property name="position">2</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkComboBox" id="default_lexer_combobox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="changed" handler="_lexer_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="cellrenderertext1"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="line_break_combobox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">line_break_selection</property>
<signal name="changed" handler="_line_break_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="cellrenderertext2"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="style_combobox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="changed" handler="_style_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="cellrenderertext3"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Select syntax highlighting style</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Select code marker (the backticks) formatting</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="code_marker_combobox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">code_marker_selection</property>
<signal name="changed" handler="_code_marker_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="cellrenderertext4"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Select font for code snippets</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkFontButton" id="font_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="font">Sans 12</property>
<property name="preview_text"/>
<property name="use_font">True</property>
<signal name="font-set" handler="_font_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="bg_color_checkbutton">
<property name="label" translatable="yes">Set background color</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">end</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="_bg_color_enabled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkColorButton" id="bg_color_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">start</property>
<property name="title" translatable="yes">Choose the background color for code blocks</property>
<property name="show_editor">True</property>
<signal name="color-set" handler="_bg_color_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="margin_top">6</property>
<property name="margin_bottom">6</property>
<property name="label" translatable="yes">Test here how code blocks will look like in the chat window</property>
<property name="xalign">0</property>
<style>
<class name="bold"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="height_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child> <child>
<object class="GtkTextView" id="preview_textview"> <object class="GtkTextView" id="preview_textview">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="wrap_mode">word-char</property> <property name="wrap_mode">word-char</property>
<property name="left_margin">6</property>
<property name="right_margin">6</property>
<property name="top_margin">6</property>
<property name="bottom_margin">6</property>
<property name="buffer">preview_textbuffer</property> <property name="buffer">preview_textbuffer</property>
</object> </object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">20</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<placeholder/>
</child> </child>
</object> </object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">20</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child> </child>
</object> </object>
<object class="GtkTextBuffer" id="textbuffer1"> <object class="GtkTextBuffer" id="textbuffer1">

View File

@@ -1,12 +1,13 @@
import logging import logging
from gi.repository import Gtk as gtk from gi.repository import Gtk
from gi.repository import Pango from gi.repository import Pango
from pygments.formatter import Formatter from pygments.formatter import Formatter
from gajim.plugins.helpers import log
log = logging.getLogger('gajim.p.syntax_highlight') log = logging.getLogger('gajim.p.syntax_highlight')
class GTKFormatter(Formatter): class GTKFormatter(Formatter):
name = 'GTK Formatter' name = 'GTK Formatter'
aliases = ['textbuffer', 'gtk'] aliases = ['textbuffer', 'gtk']
@@ -14,14 +15,14 @@ class GTKFormatter(Formatter):
def __init__(self, **options): def __init__(self, **options):
super(GTKFormatter, self).__init__(**options) super(GTKFormatter, self).__init__(**options)
#Formatter.__init__(self, **options) # Formatter.__init__(self, **options)
self.tags = {} self.tags = {}
self.mark = options.get('start_mark', None) self.mark = options.get('start_mark', None)
@staticmethod @staticmethod
def create_tag_for_token(ttype, highlighting_style): def create_tag_for_token(ttype, highlighting_style):
style = highlighting_style.style_for_token(ttype) 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: if 'bgcolor' in style and not style['bgcolor'] is None:
tag.set_property('background', '#%s' % style['bgcolor']) tag.set_property('background', '#%s' % style['bgcolor'])
if 'bold' in style and style['bold']: if 'bold' in style and style['bold']:
@@ -44,13 +45,13 @@ class GTKFormatter(Formatter):
tag.set_property('underline', 'single') tag.set_property('underline', 'single')
return tag return tag
def get_tag(self, ttype, buf): def get_tag(self, ttype, buf):
""" """
Creates, stores and returs a tag for a given token type. Creates, stores and returs a tag for a given token type.
This method ensures that a tag style is created only once. 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 tag = None
if ttype in self.tags: if ttype in self.tags:
@@ -65,23 +66,23 @@ class GTKFormatter(Formatter):
self.mark = mark self.mark = mark
def format(self, tokensource, outfile): 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...") log.warn("Did not get a buffer to format...")
return return
buf = outfile buf = outfile
end_iter = buf.get_end_iter() end_iter = buf.get_end_iter()
start_mark = self.mark start_mark = self.mark
start_iter = buf.get_iter_at_mark(start_mark) if not start_mark is None \ start_iter = buf.get_iter_at_mark(start_mark) if \
else end_iter start_mark is not None else end_iter
last_ttype = None last_ttype = None
last_start = start_iter last_start = start_iter
last_end = buf.get_end_iter() last_end = buf.get_end_iter()
last_fixed_start = last_start last_fixed_start = last_start
reset = True reset = True
for ttype, value in tokensource: for ttype, value in tokensource:
search = None search = None
@@ -90,24 +91,26 @@ class GTKFormatter(Formatter):
buf.apply_tag(tag, last_fixed_start, last_end) 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 reset = True
else: 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. # 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 # Prepare for next iteration
last_ttype = ttype last_ttype = ttype
if search is not None: if search is not None:
(last_start, last_end) = search (last_start, last_end) = search
# If we've found the end of a sequence of similar type tokens or if # If we've found the end of a sequence of similar type tokens
# we are in the first loop iteration, set the fixed point # or if we are in the first loop iteration, set the fixed point
if reset: if reset:
last_fixed_start = last_start last_fixed_start = last_start
reset = False reset = False
else: 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. # can do now.
break break

View File

@@ -1,22 +1,30 @@
from gajim.plugins.helpers import log import logging
from gi.repository import Gdk 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.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: 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): def _create_lexer_list(self):
# The list we create here contains the plain text name and the lexer's # The list we create here contains the plain text name and the lexer's
# id string # id string
lexers = [] 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() all_lexers = get_all_lexers()
for lexer in all_lexers: for lexer in all_lexers:
# We don't want to add lexers that we cant identify by name later # 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.append((lexer[0], lexer[1][0]))
lexers.sort() 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) lexers.insert(0, self.PLUGIN_INTERNAL_NONE_LEXER)
return lexers return lexers
@@ -38,24 +46,24 @@ class SyntaxHighlighterConfig:
lexer = None lexer = None
try: try:
lexer = get_lexer_by_name(name) lexer = get_lexer_by_name(name)
except: except ClassNotFound:
pass pass
return lexer return lexer
def get_lexer_with_fallback(self, language): def get_lexer_with_fallback(self, language):
lexer = self.get_lexer_by_name(language) lexer = self.get_lexer_by_name(language)
if lexer is None: if lexer is None:
log.info("Falling back to default lexer for %s.", log.info('Falling back to default lexer for %s.',
self.get_default_lexer_name()) self.get_default_lexer_name())
lexer = self.default_lexer[1] lexer = self.default_lexer[1]
return lexer return lexer
def set_font(self, font): def set_font(self, font):
if font is not None and font != "": if font is not None and font != '':
self.config['font'] = font self.config['font'] = font
def set_style(self, style): def set_style(self, style):
if style is not None and style != "": if style is not None and style != '':
self.config['style'] = style self.config['style'] = style
def set_line_break_action(self, option): def set_line_break_action(self, option):
@@ -68,14 +76,16 @@ class SyntaxHighlighterConfig:
lexer = get_lexer_by_name(name) lexer = get_lexer_by_name(name)
if lexer is None and self.default_lexer is None: if lexer is None and self.default_lexer is None:
log.error("Failed to get default lexer by name."\ log.error('Failed to get default lexer by name.'
"Falling back to simply using the first in the list.") 'Falling back to simply using the first lexer '
'in the list.')
lexer = self.lexer_list[0] lexer = self.lexer_list[0]
name = lexer[0] name = lexer[0]
self.default_lexer = (name, lexer) self.default_lexer = (name, lexer)
if lexer is None and self.default_lexer is not None: if lexer is None and self.default_lexer is not None:
log.info("Failed to get default lexer by name, keeping previous"\ log.info('Failed to get default lexer by name, keeping '
"setting (lexer = %s).", self.default_lexer[0]) 'previous setting (lexer = %s).',
self.default_lexer[0])
name = self.default_lexer[0] name = self.default_lexer[0]
else: else:
self.default_lexer = (name, lexer) self.default_lexer = (name, lexer)
@@ -88,7 +98,7 @@ class SyntaxHighlighterConfig:
self.config['bgcolor_override'] = state self.config['bgcolor_override'] = state
def set_bgcolor(self, color): def set_bgcolor(self, color):
if isinstance(color, Gdk.Color): if isinstance(color, Gdk.RGBA):
color = color.to_string() color = color.to_string()
self.config['bgcolor'] = color self.config['bgcolor'] = color
@@ -110,9 +120,9 @@ class SyntaxHighlighterConfig:
return self.lexer_list return self.lexer_list
def get_line_break_action(self): def get_line_break_action(self):
# return int only # Return int only
if isinstance(self.config['line_break'], int): if isinstance(self.config['line_break'], int):
# in case of legacy settings, convert. # In case of legacy settings, convert.
action = self.config['line_break'] action = self.config['line_break']
self.set_line_break_action(action) self.set_line_break_action(action)
else: else:
@@ -146,13 +156,13 @@ class SyntaxHighlighterConfig:
Initialize all config variables that depend directly on pygments being Initialize all config variables that depend directly on pygments being
available. available.
""" """
self.lexer_list = self._create_lexer_list() self.lexer_list = self._create_lexer_list()
self.style_list = [s for s in get_all_styles()] self.style_list = [s for s in get_all_styles()]
self.style_list.sort() self.style_list.sort()
self.set_default_lexer(self.config['default_lexer']) self.set_default_lexer(self.config['default_lexer'])
def __init__(self, config): def __init__(self, config):
self.lexer_list = [] self.lexer_list = []
self.style_list = [] self.style_list = []
self.config = config self.config = config
self.default_lexer = None self.default_lexer = None

View File

@@ -1,63 +1,47 @@
import re import re
import pygments import pygments
from gi.repository import Gtk, Gdk from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository.Pango import FontDescription from gi.repository.Pango import FontDescription
from gajim.plugins.gui import GajimPluginConfigDialog from gajim.plugins.gui import GajimPluginConfigDialog
from gajim.plugins.helpers import log_calls from gajim.plugins.helpers import get_builder
from syntax_highlight.gtkformatter import GTKFormatter
from syntax_highlight.types import LineBreakOptions
from syntax_highlight.types import CodeMarkerOptions
from .gtkformatter import GTKFormatter
from .types import LineBreakOptions, CodeMarkerOptions
class SyntaxHighlighterPluginConfiguration(GajimPluginConfigDialog): class SyntaxHighlighterPluginConfiguration(GajimPluginConfigDialog):
@log_calls('SyntaxHighlighterPluginConfiguration')
def init(self): def init(self):
self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path( path = self.plugin.local_file_path('config_dialog.ui')
'config_dialog.ui') self._ui = get_builder(path)
self.xml = Gtk.Builder() box = self.get_content_area()
self.xml.set_translation_domain('gajim_plugins') box.pack_start(self._ui.main_box, True, True, 0)
self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH,
['mainBox', 'line_break_selection', 'code_marker_selection', self._ui.set_translation_domain('gajim_plugins')
'preview_textbuffer'])
box = self.xml.get_object('mainBox')
self.get_child().pack_start(box, False, False, 0)
self.result_label = self.xml.get_object('result_label')
self.liststore = Gtk.ListStore(str) self.liststore = Gtk.ListStore(str)
self._ui.default_lexer_combobox.set_model(self.liststore)
self.default_lexer_combobox = self.xml.get_object('default_lexer_combobox')
self.default_lexer_combobox.set_property("model", self.liststore)
self.style_liststore = Gtk.ListStore(str) self.style_liststore = Gtk.ListStore(str)
self.style_combobox = self.xml.get_object('style_combobox') self._ui.style_combobox.set_model(self.style_liststore)
self.style_combobox.set_property("model", self.style_liststore)
self.bg_color_checkbox = self.xml.get_object('bg_color_checkbutton') self._ui.preview_textview.get_buffer().connect(
self.bg_color_colorbutton = self.xml.get_object('bg_color_colorbutton') 'insert-text', self._on_preview_text_inserted)
self.line_break_combobox = self.xml.get_object('line_break_combobox') self._ui.connect_signals(self)
self.code_marker_combobox = self.xml.get_object('code_marker_combobox') self.default_lexer_id = 0
self.style_id = 0
self.preview_textview = self.xml.get_object('preview_textview')
self.preview_textview.get_buffer().connect("insert-text", self.on_preview_text_inserted)
self.preview_textview.set_size_request(-1, 130)
self.font_button = self.xml.get_object('font_button')
self.xml.connect_signals(self)
self.default_lexer_id = 0
self.style_id = 0
def set_config(self, config): def set_config(self, config):
self.config = config self.config = config
self.lexers = self.config.get_lexer_list() self.lexers = self.config.get_lexer_list()
self.styles = self.config.get_styles_list() self.styles = self.config.get_styles_list()
default_lexer = self.config.get_default_lexer_name() default_lexer = self.config.get_default_lexer_name()
default_style = self.config.get_style_name() default_style = self.config.get_style_name()
for i, lexer in enumerate(self.lexers): for i, lexer in enumerate(self.lexers):
self.liststore.append([lexer[0]]) self.liststore.append([lexer[0]])
@@ -68,102 +52,100 @@ class SyntaxHighlighterPluginConfiguration(GajimPluginConfigDialog):
self.style_liststore.append([style]) self.style_liststore.append([style])
if style == default_style: if style == default_style:
self.style_id = i self.style_id = i
self.update_preview() self._update_preview()
def lexer_changed(self, _widget): def _lexer_changed(self, _widget):
new = self.default_lexer_combobox.get_active() new = self._ui.default_lexer_combobox.get_active()
if new != self.default_lexer_id: if new != self.default_lexer_id:
self.default_lexer_id = new self.default_lexer_id = new
self.config.set_default_lexer(self.lexers[self.default_lexer_id][1]) self.config.set_default_lexer(self.lexers[self.default_lexer_id][1])
self.update_preview() self._update_preview()
def line_break_changed(self, _widget): def _line_break_changed(self, _widget):
new = LineBreakOptions(self.line_break_combobox.get_active()) new = LineBreakOptions(self._ui.line_break_combobox.get_active())
if new != self.config.get_line_break_action(): if new != self.config.get_line_break_action():
self.config.set_line_break_action(new) self.config.set_line_break_action(new)
self.update_preview() self._update_preview()
def code_marker_changed(self, _widget): def _code_marker_changed(self, _widget):
new = CodeMarkerOptions(self.code_marker_combobox.get_active()) new = CodeMarkerOptions(self._ui.code_marker_combobox.get_active())
if new != self.config.get_code_marker_setting(): if new != self.config.get_code_marker_setting():
self.config.set_code_marker_setting(new) self.config.set_code_marker_setting(new)
def bg_color_enabled(self, _widget): def _bg_color_enabled(self, _widget):
new = self.bg_color_checkbox.get_active() new = self._ui.bg_color_checkbutton.get_active()
if new != self.config.is_bgcolor_override_enabled(): if new != self.config.is_bgcolor_override_enabled():
bg_override_enabled = new bg_override_enabled = new
self.config.set_bgcolor_override_enabled(bg_override_enabled) self.config.set_bgcolor_override_enabled(bg_override_enabled)
self.bg_color_colorbutton.set_sensitive(bg_override_enabled) self._ui.bg_color_colorbutton.set_sensitive(bg_override_enabled)
self.update_preview() self._update_preview()
def bg_color_changed(self, _widget): def _bg_color_changed(self, _widget):
new = self.bg_color_colorbutton.get_color() new = self._ui.bg_color_colorbutton.get_rgba()
if new != self.config.get_bgcolor(): if new != self.config.get_bgcolor():
self.config.set_bgcolor(new) self.config.set_bgcolor(new)
self.update_preview() self._update_preview()
def style_changed(self, _widget): def _style_changed(self, _widget):
new = self.style_combobox.get_active() new = self._ui.style_combobox.get_active()
if new != self.style_id: if new != self.style_id:
self.style_id = new self.style_id = new
self.config.set_style(self.styles[self.style_id]) self.config.set_style(self.styles[self.style_id])
self.update_preview() self._update_preview()
def font_changed(self, _widget): def _font_changed(self, _widget):
new = self.font_button.get_font() new = self._ui.font_button.get_font()
if new != self.config.get_font(): if new != self.config.get_font():
self.config.set_font(new) self.config.set_font(new)
self.update_preview() self._update_preview()
def update_preview(self): def _update_preview(self):
self.format_preview_text() self._format_preview_text()
def on_preview_text_inserted(self, _buf, _iterator, text, length, *_args): def _on_preview_text_inserted(self, _buf, _iterator, text, length, *_args):
if (length == 1 and re.match(r'\s', text)) or length > 1: if (length == 1 and re.match(r'\s', text)) or length > 1:
self.format_preview_text() self._format_preview_text()
def format_preview_text(self): def _format_preview_text(self):
buf = self.preview_textview.get_buffer() buf = self._ui.preview_textview.get_buffer()
start_iter = buf.get_start_iter() start_iter = buf.get_start_iter()
start_mark = buf.create_mark(None, start_iter, True) start_mark = buf.create_mark(None, start_iter, True)
buf.remove_all_tags(start_iter, buf.get_end_iter()) buf.remove_all_tags(start_iter, buf.get_end_iter())
formatter = GTKFormatter( formatter = GTKFormatter(
style=self.config.get_style_name(), style=self.config.get_style_name(), start_mark=start_mark)
start_mark=start_mark)
code = start_iter.get_text(buf.get_end_iter()) code = start_iter.get_text(buf.get_end_iter())
lexer = self.config.get_default_lexer() lexer = self.config.get_default_lexer()
if not self.config.is_internal_none_lexer(lexer): if not self.config.is_internal_none_lexer(lexer):
tokens = pygments.lex(code, lexer) tokens = pygments.lex(code, lexer)
pygments.format(tokens, formatter, buf) pygments.format(tokens, formatter, buf)
buf.delete_mark(start_mark) buf.delete_mark(start_mark)
self.preview_textview.override_font( self._ui.preview_textview.override_font(
FontDescription.from_string(self.config.get_font())) FontDescription.from_string(self.config.get_font()))
color = Gdk.RGBA() color = Gdk.RGBA()
if color.parse(self.config.get_bgcolor()): if color.parse(self.config.get_bgcolor()):
self.preview_textview.override_background_color( self._ui.preview_textview.override_background_color(
Gtk.StateFlags.NORMAL, color) Gtk.StateFlags.NORMAL, color)
def on_run(self): def on_run(self):
self.default_lexer_combobox.set_active(self.default_lexer_id) self._ui.default_lexer_combobox.set_active(self.default_lexer_id)
self.line_break_combobox.set_active(self.config.get_line_break_action()) self._ui.line_break_combobox.set_active(
self.code_marker_combobox.set_active(self.config.get_code_marker_setting()) self.config.get_line_break_action())
self.style_combobox.set_active(self.style_id) self._ui.code_marker_combobox.set_active(
self.config.get_code_marker_setting())
self._ui.style_combobox.set_active(self.style_id)
self.font_button.set_font(self.config.get_font()) self._ui.font_button.set_font(self.config.get_font())
bg_override_enabled = self.config.is_bgcolor_override_enabled() bg_override_enabled = self.config.is_bgcolor_override_enabled()
self.bg_color_checkbox.set_active(bg_override_enabled) self._ui.bg_color_checkbutton.set_active(bg_override_enabled)
self.bg_color_colorbutton.set_sensitive(bg_override_enabled)
parsed, color = Gdk.Color.parse(self.config.get_bgcolor())
if parsed:
self.bg_color_colorbutton.set_color(color)
self._ui.bg_color_colorbutton.set_sensitive(bg_override_enabled)
color = Gdk.RGBA()
if color.parse(self.config.get_bgcolor()):
self._ui.bg_color_colorbutton.set_rgba(color)

View File

@@ -1,29 +1,33 @@
import logging import logging
import sys 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): if sys.version_info >= (3, 4):
from importlib.util import find_spec as find_module from importlib.util import find_spec as find_module
else: else:
from importlib import find_loader as find_module from importlib import find_loader as find_module
PYGMENTS_MISSING = 'You are missing Python-Pygments.'
from gajim.plugins.helpers import log_calls, log
from gajim.plugins import GajimPlugin
from .types import MatchType, LineBreakOptions, CodeMarkerOptions, \
PLUGIN_INTERNAL_NONE_LEXER_ID
log = logging.getLogger('gajim.p.syntax_highlight') log = logging.getLogger('gajim.p.syntax_highlight')
def try_loading_pygments(): def try_loading_pygments():
success = find_module('pygments') is not None success = find_module('pygments') is not None
if success: if success:
try: try:
from .chat_syntax_highlighter import ChatSyntaxHighlighter from syntax_highlight.chat_syntax_highlighter import \
from .plugin_config_dialog import SyntaxHighlighterPluginConfiguration ChatSyntaxHighlighter
from .plugin_config import SyntaxHighlighterConfig from syntax_highlight.plugin_config_dialog import \
global SyntaxHighlighterPluginConfiguration, ChatSyntaxHighlighter, \ SyntaxHighlighterPluginConfiguration
SyntaxHighlighterConfig from syntax_highlight.plugin_config import SyntaxHighlighterConfig
global SyntaxHighlighterPluginConfiguration
global ChatSyntaxHighlighter
global SyntaxHighlighterConfig
success = True success = True
log.debug("pygments loaded.") log.debug("pygments loaded.")
except Exception as exception: except Exception as exception:
@@ -32,27 +36,21 @@ def try_loading_pygments():
return success return success
PYGMENTS_MISSING = 'You are missing Python-Pygments.'
class SyntaxHighlighterPlugin(GajimPlugin): class SyntaxHighlighterPlugin(GajimPlugin):
@log_calls('SyntaxHighlighterPlugin')
def on_connect_with_chat_control(self, chat_control): def on_connect_with_chat_control(self, chat_control):
account = chat_control.contact.account.name account = chat_control.contact.account.name
jid = chat_control.contact.jid jid = chat_control.contact.jid
if account not in self.ccontrol: if account not in self.ccontrol:
self.ccontrol[account] = {} self.ccontrol[account] = {}
self.ccontrol[account][jid] = ChatSyntaxHighlighter( 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): def on_disconnect_from_chat_control(self, chat_control):
account = chat_control.contact.account.name account = chat_control.contact.account.name
jid = chat_control.contact.jid jid = chat_control.contact.jid
del self.ccontrol[account][jid] del self.ccontrol[account][jid]
@log_calls('SyntaxHighlighterPlugin')
def on_print_real_text(self, text_view, real_text, other_tags, graphics, def on_print_real_text(self, text_view, real_text, other_tags, graphics,
iterator, additional): iterator, additional):
account = text_view.account account = text_view.account
@@ -78,36 +76,32 @@ class SyntaxHighlighterPlugin(GajimPlugin):
self.available_text = None self.available_text = None
self.config_dialog = SyntaxHighlighterPluginConfiguration(self) self.config_dialog = SyntaxHighlighterPluginConfiguration(self)
self.conf = SyntaxHighlighterConfig(self.config) self.conf = SyntaxHighlighterConfig(self.config)
# The following initialization requires pygments to be available. # The following initialization requires pygments to be available.
self.conf.init_pygments() self.conf.init_pygments()
self.config_dialog = SyntaxHighlighterPluginConfiguration(self) self.config_dialog = SyntaxHighlighterPluginConfiguration(self)
self.config_dialog.set_config(self.conf) self.config_dialog.set_config(self.conf)
self.gui_extension_points = { self.gui_extension_points = {
'chat_control_base': ( 'chat_control_base': (
self.on_connect_with_chat_control, self.on_connect_with_chat_control,
self.on_disconnect_from_chat_control self.on_disconnect_from_chat_control),
), 'print_real_text': (self.on_print_real_text, None), }
'print_real_text': (self.on_print_real_text, None),
}
return True return True
@log_calls('SyntaxHighlighterPlugin')
def init(self): def init(self):
self.ccontrol = {} self.ccontrol = {}
self.config_default_values = { self.config_default_values = {
'default_lexer' : (PLUGIN_INTERNAL_NONE_LEXER_ID, ''), 'default_lexer': (PLUGIN_INTERNAL_NONE_LEXER_ID, ''),
'line_break' : (LineBreakOptions.MULTILINE, ''), 'line_break': (LineBreakOptions.MULTILINE, ''),
'style' : ('default', ''), 'style': ('default', ''),
'font' : ('Monospace 10', ''), 'font': ('Monospace 10', ''),
'bgcolor' : ('#ccc', ''), 'bgcolor': ('#ccc', ''),
'bgcolor_override' : (True, ''), 'bgcolor_override': (True, ''),
'code_marker' : (CodeMarkerOptions.AS_COMMENT, ''), 'code_marker': (CodeMarkerOptions.AS_COMMENT, ''),
'pygments_path' : (None, ''), 'pygments_path': (None, ''), }
}
is_initialized = self.try_init() is_initialized = self.try_init()

View File

@@ -1,21 +1,24 @@
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): class MatchType(Enum):
INLINE = 0 INLINE = 0
MULTILINE = 1 MULTILINE = 1
TEXT = 2 TEXT = 2
@unique @unique
class LineBreakOptions(IntEnum): class LineBreakOptions(IntEnum):
NEVER = 0 NEVER = 0
ALWAYS = 1 ALWAYS = 1
MULTILINE = 2 MULTILINE = 2
@unique @unique
class CodeMarkerOptions(IntEnum): class CodeMarkerOptions(IntEnum):
AS_COMMENT = 0 AS_COMMENT = 0
HIDE = 1 HIDE = 1