From 2faf8daf7960a164707e677e690ca164ddbad69c Mon Sep 17 00:00:00 2001 From: Cherti mehdi Date: Tue, 20 Nov 2012 23:15:04 +0000 Subject: [PATCH 1/5] Extension to insert code with syntax highlighting. --- testing/syntaxhighlight/README | 11 + testing/syntaxhighlight/__init__.py | 86 ++++++++ .../syntaxhighlight/dialog_syntaxhighlight.py | 190 ++++++++++++++++++ testing/syntaxhighlight/info.xml | 20 ++ 4 files changed, 307 insertions(+) create mode 100644 testing/syntaxhighlight/README create mode 100644 testing/syntaxhighlight/__init__.py create mode 100644 testing/syntaxhighlight/dialog_syntaxhighlight.py create mode 100644 testing/syntaxhighlight/info.xml diff --git a/testing/syntaxhighlight/README b/testing/syntaxhighlight/README new file mode 100644 index 0000000..f1ace23 --- /dev/null +++ b/testing/syntaxhighlight/README @@ -0,0 +1,11 @@ +Syntax highlight extension +========================== + +Author : Cherti Mehdi (mehdi@cherti.name) + +This extension provides inserting code with +syntax highlighting of any language. + +Note that this extension requires pygments python +library to be installed. +(http://pygments.org/) diff --git a/testing/syntaxhighlight/__init__.py b/testing/syntaxhighlight/__init__.py new file mode 100644 index 0000000..abd18bf --- /dev/null +++ b/testing/syntaxhighlight/__init__.py @@ -0,0 +1,86 @@ +""" + KeepNote + Syntax highlight code insertion +""" +# -*- coding: utf-8 -*- +# +# KeepNote +# Copyright (c) 2008-2009 Matt Rasmussen +# Author: Matt Rasmussen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +# +import sys +import os + +# python imports +import gettext +_ = gettext.gettext + + +# keepnote imports +from keepnote.gui import extension + + +sys.path.append(os.path.join(os.path.dirname(__file__), ".")) +from dialog_syntaxhighlight import DialogSyntaxHighlight + + +class Extension (extension.Extension): + + def __init__(self, app): + """Initialize extension""" + extension.Extension.__init__(self, app) + self.dialogs_syntax_highlight = {} + + #================================ + # UI setup + + def on_add_ui(self, window): + + menu_label = "Insert code with syntax highlighting..." + # add menu options + self.add_action(window, menu_label, + menu_label, + lambda w: self.on_insert_syntax_highlight(window)) + + self.add_ui(window, + """ + + + + + + + + + + + + + + + """ % (menu_label,)) + + #================================ + # actions + + def on_insert_syntax_highlight(self, window): + dialog = self.dialogs_syntax_highlight.get(window, + DialogSyntaxHighlight(window)) + self.dialogs_syntax_highlight[window] = dialog + + dialog.reinitialize() + dialog.show() + diff --git a/testing/syntaxhighlight/dialog_syntaxhighlight.py b/testing/syntaxhighlight/dialog_syntaxhighlight.py new file mode 100644 index 0000000..5f1fa82 --- /dev/null +++ b/testing/syntaxhighlight/dialog_syntaxhighlight.py @@ -0,0 +1,190 @@ +""" + KeepNote + Syntax highlight code insertion +""" + +# -*- coding: utf-8 -*- +# KeepNote +# Copyright (c) 2008-2009 Matt Rasmussen +# Author: Matt Rasmussen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +# + +import pygtk +pygtk.require('2.0') +import gtk + +from pygments import highlight +from pygments.lexers import guess_lexer, guess_lexer_for_filename +from pygments.lexers import get_lexer_by_name, get_all_lexers +from pygments.formatters import HtmlFormatter +from pygments.util import ClassNotFound + +def get_file_content(filename): + with open(filename, "r") as fd: + content = fd.read() + return content + +def error_dialog(window, error_message): + message_dialog = gtk.MessageDialog(window, + gtk.DIALOG_DESTROY_WITH_PARENT, + gtk.MESSAGE_ERROR, + gtk.BUTTONS_CLOSE, + error_message) + message_dialog.run() + message_dialog.destroy() + +class DialogSyntaxHighlight(object): + """ + """ + automatic = "Automatic" + + def __init__(self, window): + self.window = window + self.create_dialog() + self.reinitialize() + + def create_dialog(self): + self.dialog = gtk.Window(gtk.WINDOW_TOPLEVEL) + self.dialog.set_default_size(400, 400) + self.dialog.set_title("") + + vbox = gtk.VBox() + + vbox.pack_start(gtk.Label("Select Language:"), False) + + self.lang_selector = self.create_lang_selector() + vbox.pack_start(self.lang_selector, False) + + self.from_file_button = gtk.Button("From file...") + vbox.pack_start(self.from_file_button, False) + + scrolled_window, self.textview = self.create_scrolled_textview() + vbox.pack_start(scrolled_window) + + self.insert_button = gtk.Button("Insert it!") + vbox.pack_start(self.insert_button, False) + + self.dialog.add(vbox) + + self.do_signal_connections() + + def create_lang_selector(self): + lang_selector = gtk.combo_box_entry_new_text() + lang_selector.append_text(self.automatic) + + completion = gtk.EntryCompletion() + completion.set_model(lang_selector.get_model()) + completion.set_minimum_key_length(1) + completion.set_text_column(0) + + lang_selector.child.set_completion(completion) + + for name in self.get_lang_names(): + lang_selector.append_text(name) + return lang_selector + + def get_lang_names(self): + l = [] + for (longname, aliases, _, _) in get_all_lexers(): + if len(aliases) > 0: + l.append(aliases[0]) + l.sort() + return l + + def create_scrolled_textview(self): + textview = gtk.TextView(gtk.TextBuffer()) + scrolledwindow = gtk.ScrolledWindow() + scrolledwindow.add_with_viewport(textview) + return scrolledwindow, textview + + def do_signal_connections(self): + self.insert_button.connect("clicked", self.on_insert_button_clicked) + self.from_file_button.connect("clicked", + self.on_from_file_button_clicked) + self.dialog.connect("delete-event", self.on_delete_dialog) + + def on_insert_button_clicked(self, button): + start, end = self.textview.get_buffer().get_bounds() + text = self.textview.get_buffer().get_text(start, end) + lang = self.lang_selector.get_active_text() + self.insert_it(text.decode(), lang) + self.hide() + + def insert_it(self, text, lang): + editor = self.window.get_viewer().get_editor() + textview = editor._textview + + try: + if lang == self.automatic: + lexer = guess_lexer(text) + else: + lexer = get_lexer_by_name(lang, stripall=True) + except ClassNotFound: + textview.insert_html(text) + else: + formatter = HtmlFormatter(noclasses=True, lineseparator="
") + result = highlight(text, lexer, formatter) + textview.insert_html(result) + + def on_from_file_button_clicked(self, button): + chooser = self.create_file_chooser() + response = chooser.run() + if response == gtk.RESPONSE_OK: + filename = chooser.get_filename() + chooser.destroy() + + try: + content = get_file_content(filename) + except IOError: + error_dialog(self.window, + "File not found : %s" % (filename,)) + return + + self.load_textview_from_str(content) + try: + lexer = guess_lexer_for_filename(filename, content) + except ClassNotFound: + pass + else: + if len(lexer.aliases) > 0: + self.lang_selector.child.set_text(lexer.aliases[0]) + + def create_file_chooser(self): + chooser = gtk.FileChooserDialog("Open...", + None, + gtk.FILE_CHOOSER_ACTION_OPEN, + (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN, + gtk.RESPONSE_OK)) + chooser.set_default_response(gtk.RESPONSE_OK) + return chooser + + def load_textview_from_str(self, s): + self.textview.get_buffer().set_text(s) + + def on_delete_dialog(self, window, eventt): + window.hide() + return True + + def reinitialize(self): + self.textview.get_buffer().set_text("") + self.lang_selector.set_active(0) + + def show(self): + self.dialog.show_all() + + def hide(self): + self.dialog.hide() + diff --git a/testing/syntaxhighlight/info.xml b/testing/syntaxhighlight/info.xml new file mode 100644 index 0000000..c1a0bbe --- /dev/null +++ b/testing/syntaxhighlight/info.xml @@ -0,0 +1,20 @@ + + + + version + 1.0 + name + Syntax highlight + author + Cherti Mehdi + email + mehdi@cherti.name + website + http://mehdi.cherti.name + description + Used for inserting code with syntax highlighting. + The pygments python library is required by this extension. + (http://pygments.org/) + + + From d11fe7a39e56ff0eef8ce666333782ce2b85bed9 Mon Sep 17 00:00:00 2001 From: Tiago Sintra Date: Tue, 20 Sep 2016 18:05:08 +0200 Subject: [PATCH 2/5] Update dialog_syntaxhighlight.py Fixes 3 things: - Keepnote doesn't support HMTL entities, so after retrieving the output from the highlight function, we need to convert back the entities to plaintext. Without this, many characters were just ignored such as quotes - Keepnote doesn't support TABs or multiple spaces so we need to replace them with with non breaking spaces so that indentation works correctly - Auto focus on the language combo box every time we open the syntax highlighter --- testing/syntaxhighlight/dialog_syntaxhighlight.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/testing/syntaxhighlight/dialog_syntaxhighlight.py b/testing/syntaxhighlight/dialog_syntaxhighlight.py index 5f1fa82..c8c1e5d 100644 --- a/testing/syntaxhighlight/dialog_syntaxhighlight.py +++ b/testing/syntaxhighlight/dialog_syntaxhighlight.py @@ -25,6 +25,7 @@ import pygtk pygtk.require('2.0') import gtk +from HTMLParser import HTMLParser from pygments import highlight from pygments.lexers import guess_lexer, guess_lexer_for_filename @@ -126,7 +127,6 @@ def on_insert_button_clicked(self, button): def insert_it(self, text, lang): editor = self.window.get_viewer().get_editor() textview = editor._textview - try: if lang == self.automatic: lexer = guess_lexer(text) @@ -137,6 +137,12 @@ def insert_it(self, text, lang): else: formatter = HtmlFormatter(noclasses=True, lineseparator="
") result = highlight(text, lexer, formatter) + #fix encoding + html=HTMLParser() + result = html.unescape(result) + #fix leading spaces + result = result.replace("\x09","    ") + result = result.replace(" ","    ") textview.insert_html(result) def on_from_file_button_clicked(self, button): @@ -181,6 +187,8 @@ def on_delete_dialog(self, window, eventt): def reinitialize(self): self.textview.get_buffer().set_text("") self.lang_selector.set_active(0) + #always focus on the combo box + self.lang_selector.grab_focus() def show(self): self.dialog.show_all() From 68f9ec016c94cde6af600a6c1cbe6396f98fc256 Mon Sep 17 00:00:00 2001 From: Tiago Sintra Date: Wed, 21 Sep 2016 18:52:13 +0200 Subject: [PATCH 3/5] Update dialog_syntaxhighlight.py Added style dropdown option --- .../syntaxhighlight/dialog_syntaxhighlight.py | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/testing/syntaxhighlight/dialog_syntaxhighlight.py b/testing/syntaxhighlight/dialog_syntaxhighlight.py index c8c1e5d..fe4df1c 100644 --- a/testing/syntaxhighlight/dialog_syntaxhighlight.py +++ b/testing/syntaxhighlight/dialog_syntaxhighlight.py @@ -31,6 +31,7 @@ from pygments.lexers import guess_lexer, guess_lexer_for_filename from pygments.lexers import get_lexer_by_name, get_all_lexers from pygments.formatters import HtmlFormatter +from pygments.styles import get_all_styles from pygments.util import ClassNotFound def get_file_content(filename): @@ -69,6 +70,10 @@ def create_dialog(self): self.lang_selector = self.create_lang_selector() vbox.pack_start(self.lang_selector, False) + vbox.pack_start(gtk.Label("Select Style:"), False) + self.style_selector = self.create_style_selector() + vbox.pack_start(self.style_selector, False) + self.from_file_button = gtk.Button("From file...") vbox.pack_start(self.from_file_button, False) @@ -104,7 +109,31 @@ def get_lang_names(self): l.append(aliases[0]) l.sort() return l - + + def create_style_selector(self): + style_selector = gtk.combo_box_entry_new_text() + + completion = gtk.EntryCompletion() + completion.set_model(style_selector.get_model()) + completion.set_minimum_key_length(1) + completion.set_text_column(0) + + style_selector.child.set_completion(completion) + default=0 + for idx,name in self.get_style_names(): + style_selector.append_text(name) + if(name=="default"): + default=idx + style_selector.set_active(defaultidx) + + return style_selector + + def get_style_names(self): + l = [] + for idx,name in enumerate(get_all_styles()): + l.append(idx,name) + return l.sort() + def create_scrolled_textview(self): textview = gtk.TextView(gtk.TextBuffer()) scrolledwindow = gtk.ScrolledWindow() @@ -121,10 +150,11 @@ def on_insert_button_clicked(self, button): start, end = self.textview.get_buffer().get_bounds() text = self.textview.get_buffer().get_text(start, end) lang = self.lang_selector.get_active_text() - self.insert_it(text.decode(), lang) + style = self.style_selector.get_active_text() + self.insert_it(text.decode(), lang, style) self.hide() - def insert_it(self, text, lang): + def insert_it(self, text, lang, style): editor = self.window.get_viewer().get_editor() textview = editor._textview try: @@ -135,7 +165,7 @@ def insert_it(self, text, lang): except ClassNotFound: textview.insert_html(text) else: - formatter = HtmlFormatter(noclasses=True, lineseparator="
") + formatter = HtmlFormatter(noclasses=True, lineseparator="
",style=style) result = highlight(text, lexer, formatter) #fix encoding html=HTMLParser() From e4585442b7f46acb67c3d32e7e0f1d641b7a79f7 Mon Sep 17 00:00:00 2001 From: Tiago Sintra Date: Thu, 22 Sep 2016 12:35:31 +0200 Subject: [PATCH 4/5] Update dialog_syntaxhighlight.py fixed default --- testing/syntaxhighlight/dialog_syntaxhighlight.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/testing/syntaxhighlight/dialog_syntaxhighlight.py b/testing/syntaxhighlight/dialog_syntaxhighlight.py index fe4df1c..0359111 100644 --- a/testing/syntaxhighlight/dialog_syntaxhighlight.py +++ b/testing/syntaxhighlight/dialog_syntaxhighlight.py @@ -119,20 +119,21 @@ def create_style_selector(self): completion.set_text_column(0) style_selector.child.set_completion(completion) - default=0 - for idx,name in self.get_style_names(): + defaultidx=0 + for idx,name in enumerate(self.get_style_names()): style_selector.append_text(name) if(name=="default"): - default=idx + defaultidx=idx style_selector.set_active(defaultidx) return style_selector def get_style_names(self): l = [] - for idx,name in enumerate(get_all_styles()): - l.append(idx,name) - return l.sort() + for name in get_all_styles(): + l.append(name) + l.sort() + return l def create_scrolled_textview(self): textview = gtk.TextView(gtk.TextBuffer()) From 3df4d16fbe06357936ad435cbb740230f99ab222 Mon Sep 17 00:00:00 2001 From: Tiago Sintra Date: Fri, 23 Sep 2016 11:44:29 +0200 Subject: [PATCH 5/5] Update dialog_syntaxhighlight.py We cannot unescape everything or it will cause some problems for example for PHP or HTML. This way we can escape only the minimum necessary --- testing/syntaxhighlight/dialog_syntaxhighlight.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testing/syntaxhighlight/dialog_syntaxhighlight.py b/testing/syntaxhighlight/dialog_syntaxhighlight.py index 0359111..668a852 100644 --- a/testing/syntaxhighlight/dialog_syntaxhighlight.py +++ b/testing/syntaxhighlight/dialog_syntaxhighlight.py @@ -169,10 +169,10 @@ def insert_it(self, text, lang, style): formatter = HtmlFormatter(noclasses=True, lineseparator="
",style=style) result = highlight(text, lexer, formatter) #fix encoding - html=HTMLParser() - result = html.unescape(result) + result = result.replace(""",'"'); + result = result.replace("'","'"); #fix leading spaces - result = result.replace("\x09","    ") + result = result.replace("\x09",' ') result = result.replace(" ","    ") textview.insert_html(result)