[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:
@@ -1,23 +1,22 @@
|
||||
# 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.
|
||||
|
||||
@@ -4,15 +4,14 @@ import pygments
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from gajim.plugins.helpers import log
|
||||
|
||||
|
||||
from .gtkformatter import GTKFormatter
|
||||
from .types import MatchType, LineBreakOptions, CodeMarkerOptions
|
||||
|
||||
from syntax_highlight.gtkformatter import GTKFormatter
|
||||
from syntax_highlight.types import MatchType
|
||||
from syntax_highlight.types import LineBreakOptions
|
||||
from syntax_highlight.types import CodeMarkerOptions
|
||||
|
||||
log = logging.getLogger('gajim.p.syntax_highlight')
|
||||
|
||||
|
||||
class ChatSyntaxHighlighter:
|
||||
def hide_code_markup(self, buf, start, end):
|
||||
tag = buf.get_tag_table().lookup('hide_code_markup')
|
||||
@@ -27,8 +26,7 @@ class ChatSyntaxHighlighter:
|
||||
line_break = self.config.get_line_break_action()
|
||||
|
||||
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):
|
||||
style = self.config.get_style_name()
|
||||
@@ -37,26 +35,26 @@ class ChatSyntaxHighlighter:
|
||||
self.hide_code_markup(buf, e_code, e_tag)
|
||||
else:
|
||||
comment_tag = GTKFormatter.create_tag_for_token(
|
||||
pygments.token.Comment,
|
||||
pygments.styles.get_style_by_name(style))
|
||||
pygments.token.Comment,
|
||||
pygments.styles.get_style_by_name(style))
|
||||
buf.get_tag_table().add(comment_tag)
|
||||
buf.apply_tag(comment_tag, s_tag, s_code)
|
||||
buf.apply_tag(comment_tag, e_tag, 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
|
||||
|
||||
if language is None:
|
||||
lexer = self.config.get_default_lexer()
|
||||
log.info("No Language specified. Falling back to default lexer: %s.",
|
||||
self.config.get_default_lexer_name())
|
||||
log.info('No Language specified. '
|
||||
'Falling back to default lexer: %s.',
|
||||
self.config.get_default_lexer_name())
|
||||
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)
|
||||
|
||||
if lexer is None:
|
||||
@@ -71,13 +69,15 @@ class ChatSyntaxHighlighter:
|
||||
def find_multiline_matches(self, text):
|
||||
start = None
|
||||
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):
|
||||
if start is None:
|
||||
start = i
|
||||
elif re.match(r'^\n```', i.group(0)) is not None:
|
||||
matches.append(
|
||||
(start.start(), i.end(), text[start.start():i.end()]))
|
||||
(start.start(), i.end(), text[start.start():i.end()]))
|
||||
start = None
|
||||
else:
|
||||
# not an end...
|
||||
@@ -91,37 +91,40 @@ class ChatSyntaxHighlighter:
|
||||
defined in XEP-0393.
|
||||
The same applies mirrored to the end marker.
|
||||
"""
|
||||
return [(i.start(1), i.end(1), i.group(1)) for i in \
|
||||
re.finditer(r'(?:^|\s|\*|~|_)(`((?!`).+?)`)(?:\s|\*|~|_|$)', text)]
|
||||
return [(i.start(1), i.end(1), i.group(1)) for i in
|
||||
re.finditer(r'(?:^|\s|\*|~|_)(`((?!`).+?)`)(?:\s|\*|~|_|$)',
|
||||
text)]
|
||||
|
||||
def merge_match_groups(self, real_text, inline_matches, multiline_matches):
|
||||
it_inline = iter(inline_matches)
|
||||
it_multi = iter(multiline_matches)
|
||||
length = len(real_text)
|
||||
it_multi = iter(multiline_matches)
|
||||
length = len(real_text)
|
||||
|
||||
# Just to get cleaner code below...
|
||||
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.
|
||||
cur_inline = get_next(it_inline)
|
||||
cur_multi = get_next(it_multi)
|
||||
cur_multi = get_next(it_multi)
|
||||
|
||||
pos = 0
|
||||
|
||||
# This will contain tuples with parts of the input and its classification
|
||||
parts = []
|
||||
# This will contain tuples with parts of the input and its
|
||||
# classification
|
||||
parts = []
|
||||
while pos < length:
|
||||
log.debug("-> in: %s", str(cur_inline))
|
||||
log.debug("-> mu: %s", str(cur_multi))
|
||||
log.debug('-> in: %s', str(cur_inline))
|
||||
log.debug('-> mu: %s', str(cur_multi))
|
||||
|
||||
# selected = (start, end, type)
|
||||
selected = (cur_inline[0], cur_inline[1], MatchType.INLINE) \
|
||||
if cur_inline[0] < cur_multi[0] \
|
||||
else (cur_multi[0], cur_multi[1], MatchType.MULTILINE) \
|
||||
if cur_multi[0] < length \
|
||||
else (pos, length, MatchType.TEXT)
|
||||
log.debug("--> select: %s", str(selected))
|
||||
if cur_inline[0] < cur_multi[0]:
|
||||
selected = (cur_inline[0], cur_inline[1], MatchType.INLINE)
|
||||
elif cur_multi[0] < length:
|
||||
selected = (cur_multi[0], cur_multi[1], MatchType.MULTILINE)
|
||||
else:
|
||||
selected = (pos, length, MatchType.TEXT)
|
||||
log.debug('--> select: %s', str(selected))
|
||||
|
||||
# Handle plain text string parts (and unforseen errors...)
|
||||
if pos < selected[0]:
|
||||
@@ -129,7 +132,7 @@ class ChatSyntaxHighlighter:
|
||||
parts.append((real_text[pos:end], MatchType.TEXT))
|
||||
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
|
||||
parts.append((real_text[selected[0]:selected[1]], selected[2]))
|
||||
@@ -158,67 +161,76 @@ class ChatSyntaxHighlighter:
|
||||
fixed = (marker_len_no_newline + 1, '\n')
|
||||
return fixed
|
||||
|
||||
|
||||
buf = self.textview.tv.get_buffer()
|
||||
|
||||
# first, try to find inline or multiline code snippets
|
||||
inline_matches = self.find_inline_matches(real_text)
|
||||
multiline_matches = self.find_multiline_matches(real_text)
|
||||
# First, try to find inline or multiline code snippets
|
||||
inline_matches = self.find_inline_matches(real_text)
|
||||
multiline_matches = self.find_multiline_matches(real_text)
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
start_mark = buf.create_mark("SHP_start", iterator, True)
|
||||
end_mark = buf.create_mark("SHP_end", iterator, False)
|
||||
start_mark = buf.create_mark('SHP_start', iterator, True)
|
||||
end_mark = buf.create_mark('SHP_end', iterator, False)
|
||||
|
||||
insert_newline_for_multiline = self.check_line_break(True)
|
||||
insert_newline_for_inline = self.check_line_break(False)
|
||||
insert_newline_for_multiline = self.check_line_break(True)
|
||||
insert_newline_for_inline = self.check_line_break(False)
|
||||
|
||||
split_text = self.merge_match_groups(
|
||||
real_text, inline_matches, multiline_matches)
|
||||
real_text, inline_matches, multiline_matches)
|
||||
|
||||
buf.begin_user_action()
|
||||
|
||||
for num, (text_to_insert, match_type) in enumerate(split_text):
|
||||
language = None
|
||||
end_of_message = num == (len(split_text) - 1)
|
||||
language = None
|
||||
end_of_message = num == (len(split_text) - 1)
|
||||
|
||||
if match_type == MatchType.TEXT:
|
||||
self.textview.detect_and_print_special_text(
|
||||
text_to_insert, other_tags, graphics=_graphics,
|
||||
iter_=iterator, additional_data=_additional)
|
||||
text_to_insert, other_tags, graphics=_graphics,
|
||||
iter_=iterator, additional_data=_additional)
|
||||
else:
|
||||
if match_type == MatchType.MULTILINE:
|
||||
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 \
|
||||
else language_match.group(1)
|
||||
else language_match.group(1)
|
||||
|
||||
language_len = 0 if language is None else len(language)
|
||||
|
||||
# We account the language word width for the front marker
|
||||
front = fix_newline(text_to_insert[0], 3 + language_len,
|
||||
insert_newline_for_multiline)
|
||||
back = fix_newline(text_to_insert[-1], 3,
|
||||
insert_newline_for_multiline and not end_of_message)
|
||||
front = fix_newline(
|
||||
text_to_insert[0],
|
||||
3 + language_len,
|
||||
insert_newline_for_multiline)
|
||||
back = fix_newline(
|
||||
text_to_insert[-1],
|
||||
3,
|
||||
insert_newline_for_multiline and not end_of_message)
|
||||
else:
|
||||
front = fix_newline(text_to_insert[0], 1,
|
||||
insert_newline_for_inline)
|
||||
back = fix_newline(text_to_insert[-1], 1,
|
||||
insert_newline_for_inline and not end_of_message)
|
||||
front = fix_newline(
|
||||
text_to_insert[0],
|
||||
1,
|
||||
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])
|
||||
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,
|
||||
marker_widths, start_mark, end_mark, other_tags)
|
||||
|
||||
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
|
||||
buf.move_mark(start_mark, iterator)
|
||||
|
||||
@@ -235,7 +247,7 @@ class ChatSyntaxHighlighter:
|
||||
def insert_and_format_code(self, buf, insert_text, language, marker,
|
||||
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:
|
||||
buf.insert_with_tags_by_name(start_iter, insert_text,
|
||||
@@ -243,14 +255,14 @@ class ChatSyntaxHighlighter:
|
||||
else:
|
||||
buf.insert(start_iter, insert_text)
|
||||
|
||||
tag_start = buf.get_iter_at_mark(start_mark)
|
||||
tag_end = buf.get_iter_at_mark(end_mark)
|
||||
s_code = tag_start.copy()
|
||||
e_code = tag_end.copy()
|
||||
tag_start = buf.get_iter_at_mark(start_mark)
|
||||
tag_end = buf.get_iter_at_mark(end_mark)
|
||||
s_code = tag_start.copy()
|
||||
e_code = tag_end.copy()
|
||||
s_code.forward_chars(marker[0])
|
||||
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)
|
||||
|
||||
@@ -266,6 +278,6 @@ class ChatSyntaxHighlighter:
|
||||
buf.apply_tag(tag, tag_start, tag_end)
|
||||
|
||||
def __init__(self, config, textview):
|
||||
self.last_end_mark = None
|
||||
self.config = config
|
||||
self.textview = textview
|
||||
self.last_end_mark = None
|
||||
self.config = config
|
||||
self.textview = textview
|
||||
|
||||
@@ -42,271 +42,271 @@
|
||||
; Test your highlighting here
|
||||
<!-- Test your highlighting here --></property>
|
||||
</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="border_width">18</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="mainBox">
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">18</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="margin_bottom">40</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkGrid">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">40</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">12</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>
|
||||
<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="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>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</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="halign">end</property>
|
||||
<property name="label" translatable="yes">Insert line breaks around code blocks</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>
|
||||
<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">
|
||||
<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>
|
||||
<object class="GtkTextView" id="preview_textview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</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>
|
||||
</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>
|
||||
</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>
|
||||
</object>
|
||||
<object class="GtkTextBuffer" id="textbuffer1">
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
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
|
||||
|
||||
@@ -1,22 +1,30 @@
|
||||
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
|
||||
|
||||
@@ -1,63 +1,47 @@
|
||||
import re
|
||||
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 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):
|
||||
@log_calls('SyntaxHighlighterPluginConfiguration')
|
||||
def init(self):
|
||||
self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path(
|
||||
'config_dialog.ui')
|
||||
self.xml = Gtk.Builder()
|
||||
self.xml.set_translation_domain('gajim_plugins')
|
||||
self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH,
|
||||
['mainBox', 'line_break_selection', 'code_marker_selection',
|
||||
'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')
|
||||
path = self.plugin.local_file_path('config_dialog.ui')
|
||||
self._ui = get_builder(path)
|
||||
box = self.get_content_area()
|
||||
box.pack_start(self._ui.main_box, True, True, 0)
|
||||
|
||||
self._ui.set_translation_domain('gajim_plugins')
|
||||
|
||||
self.liststore = Gtk.ListStore(str)
|
||||
|
||||
self.default_lexer_combobox = self.xml.get_object('default_lexer_combobox')
|
||||
self.default_lexer_combobox.set_property("model", self.liststore)
|
||||
self._ui.default_lexer_combobox.set_model(self.liststore)
|
||||
|
||||
self.style_liststore = Gtk.ListStore(str)
|
||||
self.style_combobox = self.xml.get_object('style_combobox')
|
||||
self.style_combobox.set_property("model", self.style_liststore)
|
||||
self._ui.style_combobox.set_model(self.style_liststore)
|
||||
|
||||
self.bg_color_checkbox = self.xml.get_object('bg_color_checkbutton')
|
||||
self.bg_color_colorbutton = self.xml.get_object('bg_color_colorbutton')
|
||||
self._ui.preview_textview.get_buffer().connect(
|
||||
'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.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
|
||||
self.default_lexer_id = 0
|
||||
self.style_id = 0
|
||||
|
||||
def set_config(self, config):
|
||||
self.config = config
|
||||
self.lexers = self.config.get_lexer_list()
|
||||
self.styles = self.config.get_styles_list()
|
||||
default_lexer = self.config.get_default_lexer_name()
|
||||
default_style = self.config.get_style_name()
|
||||
self.config = config
|
||||
self.lexers = self.config.get_lexer_list()
|
||||
self.styles = self.config.get_styles_list()
|
||||
default_lexer = self.config.get_default_lexer_name()
|
||||
default_style = self.config.get_style_name()
|
||||
|
||||
for i, lexer in enumerate(self.lexers):
|
||||
self.liststore.append([lexer[0]])
|
||||
@@ -68,102 +52,100 @@ class SyntaxHighlighterPluginConfiguration(GajimPluginConfigDialog):
|
||||
self.style_liststore.append([style])
|
||||
if style == default_style:
|
||||
self.style_id = i
|
||||
self.update_preview()
|
||||
self._update_preview()
|
||||
|
||||
def lexer_changed(self, _widget):
|
||||
new = self.default_lexer_combobox.get_active()
|
||||
def _lexer_changed(self, _widget):
|
||||
new = self._ui.default_lexer_combobox.get_active()
|
||||
if new != self.default_lexer_id:
|
||||
self.default_lexer_id = new
|
||||
self.config.set_default_lexer(self.lexers[self.default_lexer_id][1])
|
||||
self.update_preview()
|
||||
self._update_preview()
|
||||
|
||||
def line_break_changed(self, _widget):
|
||||
new = LineBreakOptions(self.line_break_combobox.get_active())
|
||||
def _line_break_changed(self, _widget):
|
||||
new = LineBreakOptions(self._ui.line_break_combobox.get_active())
|
||||
if new != self.config.get_line_break_action():
|
||||
self.config.set_line_break_action(new)
|
||||
self.update_preview()
|
||||
self._update_preview()
|
||||
|
||||
def code_marker_changed(self, _widget):
|
||||
new = CodeMarkerOptions(self.code_marker_combobox.get_active())
|
||||
def _code_marker_changed(self, _widget):
|
||||
new = CodeMarkerOptions(self._ui.code_marker_combobox.get_active())
|
||||
if new != self.config.get_code_marker_setting():
|
||||
self.config.set_code_marker_setting(new)
|
||||
|
||||
def bg_color_enabled(self, _widget):
|
||||
new = self.bg_color_checkbox.get_active()
|
||||
def _bg_color_enabled(self, _widget):
|
||||
new = self._ui.bg_color_checkbutton.get_active()
|
||||
if new != self.config.is_bgcolor_override_enabled():
|
||||
bg_override_enabled = new
|
||||
self.config.set_bgcolor_override_enabled(bg_override_enabled)
|
||||
self.bg_color_colorbutton.set_sensitive(bg_override_enabled)
|
||||
self.update_preview()
|
||||
self._ui.bg_color_colorbutton.set_sensitive(bg_override_enabled)
|
||||
self._update_preview()
|
||||
|
||||
def bg_color_changed(self, _widget):
|
||||
new = self.bg_color_colorbutton.get_color()
|
||||
def _bg_color_changed(self, _widget):
|
||||
new = self._ui.bg_color_colorbutton.get_rgba()
|
||||
if new != self.config.get_bgcolor():
|
||||
self.config.set_bgcolor(new)
|
||||
self.update_preview()
|
||||
self._update_preview()
|
||||
|
||||
def style_changed(self, _widget):
|
||||
new = self.style_combobox.get_active()
|
||||
def _style_changed(self, _widget):
|
||||
new = self._ui.style_combobox.get_active()
|
||||
if new != self.style_id:
|
||||
self.style_id = new
|
||||
self.config.set_style(self.styles[self.style_id])
|
||||
self.update_preview()
|
||||
self._update_preview()
|
||||
|
||||
def font_changed(self, _widget):
|
||||
new = self.font_button.get_font()
|
||||
def _font_changed(self, _widget):
|
||||
new = self._ui.font_button.get_font()
|
||||
if new != self.config.get_font():
|
||||
self.config.set_font(new)
|
||||
self.update_preview()
|
||||
self._update_preview()
|
||||
|
||||
def update_preview(self):
|
||||
self.format_preview_text()
|
||||
def _update_preview(self):
|
||||
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:
|
||||
self.format_preview_text()
|
||||
self._format_preview_text()
|
||||
|
||||
def format_preview_text(self):
|
||||
buf = self.preview_textview.get_buffer()
|
||||
def _format_preview_text(self):
|
||||
buf = self._ui.preview_textview.get_buffer()
|
||||
start_iter = buf.get_start_iter()
|
||||
start_mark = buf.create_mark(None, start_iter, True)
|
||||
buf.remove_all_tags(start_iter, buf.get_end_iter())
|
||||
|
||||
formatter = GTKFormatter(
|
||||
style=self.config.get_style_name(),
|
||||
start_mark=start_mark)
|
||||
style=self.config.get_style_name(), start_mark=start_mark)
|
||||
|
||||
code = start_iter.get_text(buf.get_end_iter())
|
||||
lexer = self.config.get_default_lexer()
|
||||
code = start_iter.get_text(buf.get_end_iter())
|
||||
lexer = self.config.get_default_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)
|
||||
|
||||
buf.delete_mark(start_mark)
|
||||
|
||||
self.preview_textview.override_font(
|
||||
FontDescription.from_string(self.config.get_font()))
|
||||
self._ui.preview_textview.override_font(
|
||||
FontDescription.from_string(self.config.get_font()))
|
||||
|
||||
color = Gdk.RGBA()
|
||||
if color.parse(self.config.get_bgcolor()):
|
||||
self.preview_textview.override_background_color(
|
||||
Gtk.StateFlags.NORMAL, color)
|
||||
self._ui.preview_textview.override_background_color(
|
||||
Gtk.StateFlags.NORMAL, color)
|
||||
|
||||
def on_run(self):
|
||||
self.default_lexer_combobox.set_active(self.default_lexer_id)
|
||||
self.line_break_combobox.set_active(self.config.get_line_break_action())
|
||||
self.code_marker_combobox.set_active(self.config.get_code_marker_setting())
|
||||
self.style_combobox.set_active(self.style_id)
|
||||
self._ui.default_lexer_combobox.set_active(self.default_lexer_id)
|
||||
self._ui.line_break_combobox.set_active(
|
||||
self.config.get_line_break_action())
|
||||
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()
|
||||
self.bg_color_checkbox.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_checkbutton.set_active(bg_override_enabled)
|
||||
|
||||
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)
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
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()
|
||||
|
||||
|
||||
@@ -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):
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user