Implemented the JSON based highlighting styles files. jsonfiles

Thu, 28 Jan 2021 18:29:00 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Thu, 28 Jan 2021 18:29:00 +0100
branch
jsonfiles
changeset 8022
2da0139f4f91
parent 8021
a8ba35ce81ad
child 8023
56359a72ab10

Implemented the JSON based highlighting styles files.

eric6.e4p file | annotate | diff | comparison | revisions
eric6/E5XML/HighlightingStylesReader.py file | annotate | diff | comparison | revisions
eric6/Preferences/ConfigurationPages/EditorHighlightingStylesPage.py file | annotate | diff | comparison | revisions
eric6/Preferences/HighlightingStylesFile.py file | annotate | diff | comparison | revisions
eric6/Preferences/Shortcuts.py file | annotate | diff | comparison | revisions
eric6/QScintilla/Lexers/__init__.py file | annotate | diff | comparison | revisions
--- a/eric6.e4p	Thu Jan 28 16:36:29 2021 +0100
+++ b/eric6.e4p	Thu Jan 28 18:29:00 2021 +0100
@@ -771,12 +771,14 @@
     <Source>eric6/Preferences/ConfigurationPages/WebBrowserSpellCheckingPage.py</Source>
     <Source>eric6/Preferences/ConfigurationPages/WebBrowserVirusTotalPage.py</Source>
     <Source>eric6/Preferences/ConfigurationPages/__init__.py</Source>
+    <Source>eric6/Preferences/HighlightingStylesFile.py</Source>
     <Source>eric6/Preferences/MouseClickDialog.py</Source>
     <Source>eric6/Preferences/PreferencesLexer.py</Source>
     <Source>eric6/Preferences/ProgramsDialog.py</Source>
     <Source>eric6/Preferences/ShortcutDialog.py</Source>
     <Source>eric6/Preferences/Shortcuts.py</Source>
     <Source>eric6/Preferences/ShortcutsDialog.py</Source>
+    <Source>eric6/Preferences/ShortcutsFile.py</Source>
     <Source>eric6/Preferences/SubstyleDefinitionDialog.py</Source>
     <Source>eric6/Preferences/ToolConfigurationDialog.py</Source>
     <Source>eric6/Preferences/ToolGroupConfigurationDialog.py</Source>
--- a/eric6/E5XML/HighlightingStylesReader.py	Thu Jan 28 16:36:29 2021 +0100
+++ b/eric6/E5XML/HighlightingStylesReader.py	Thu Jan 28 18:29:00 2021 +0100
@@ -25,7 +25,8 @@
         Constructor
         
         @param device reference to the I/O device to read from (QIODevice)
-        @param lexers list of lexer objects for which to export the styles
+        @param lexers dictionary of lexer objects for which to import the
+            styles
         """
         XMLStreamReaderBase.__init__(self, device)
         
--- a/eric6/Preferences/ConfigurationPages/EditorHighlightingStylesPage.py	Thu Jan 28 16:36:29 2021 +0100
+++ b/eric6/Preferences/ConfigurationPages/EditorHighlightingStylesPage.py	Thu Jan 28 18:29:00 2021 +0100
@@ -7,6 +7,8 @@
 Module implementing the Editor Highlighting Styles configuration page.
 """
 
+import os
+
 from PyQt5.QtCore import pyqtSlot, Qt, QFileInfo, QFile, QIODevice
 from PyQt5.QtGui import QPalette, QFont
 from PyQt5.QtWidgets import (
@@ -28,6 +30,7 @@
 NoFontsOption = QFontDialog.FontDialogOptions(0)
 
 
+# TODO: add capability to export a list of selected highlighter styles
 class EditorHighlightingStylesPage(ConfigurationPageBase,
                                    Ui_EditorHighlightingStylesPage):
     """
@@ -517,12 +520,12 @@
         """
         self.__exportStyles(list(self.lexers.values()))
     
-    # TODO: do the JSON styles
     def __exportStyles(self, lexers):
         """
         Private method to export the styles of the given lexers.
         
         @param lexers list of lexer objects for which to export the styles
+        @type list of PreferencesLexer
         """
         from eric6config import getConfig
         stylesDir = getConfig("ericStylesDir")
@@ -531,7 +534,8 @@
             self,
             self.tr("Export Highlighting Styles"),
             stylesDir,
-            self.tr("Highlighting styles file (*.e6h)"),
+            self.tr("Highlighting Styles File (*.ehj);;"
+                    "XML Highlighting Styles File (*.e6h)"),
             "",
             E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
         
@@ -544,22 +548,39 @@
             if ex:
                 fn += ex
         
-        f = QFile(fn)
-        if f.open(QIODevice.WriteOnly):
-            from E5XML.HighlightingStylesWriter import HighlightingStylesWriter
-            HighlightingStylesWriter(f, lexers).writeXML()
-            f.close()
-        else:
-            E5MessageBox.critical(
+        if os.path.exists(fn):
+            ok = E5MessageBox.yesNo(
                 self,
                 self.tr("Export Highlighting Styles"),
-                self.tr(
-                    """<p>The highlighting styles could not be exported"""
-                    """ to file <b>{0}</b>.</p><p>Reason: {1}</p>""")
-                .format(fn, f.errorString())
-            )
+                self.tr("""<p>The highlighting styles file <b>{0}</b> exists"""
+                        """ already. Overwrite it?</p>""").format(fn))
+        else:
+            ok = True
+        
+        if ok:
+            if fn.endswith(".ehj"):
+                from Preferences.HighlightingStylesFile import (
+                    HighlightingStylesFile
+                )
+                highlightingStylesFile = HighlightingStylesFile()
+                highlightingStylesFile.writeFile(fn, lexers)
+            else:
+                f = QFile(fn)
+                if f.open(QIODevice.WriteOnly):
+                    from E5XML.HighlightingStylesWriter import (
+                        HighlightingStylesWriter
+                    )
+                    HighlightingStylesWriter(f, lexers).writeXML()
+                    f.close()
+                else:
+                    E5MessageBox.critical(
+                        self,
+                        self.tr("Export Highlighting Styles"),
+                        self.tr("<p>The highlighting styles file <b>{0}</b>"
+                                " could not be written.</p><p>Reason: {1}</p>")
+                        .format(fn, f.errorString())
+                    )
     
-    # TODO: do the JSON styles
     def __importStyles(self, lexers):
         """
         Private method to import the styles of the given lexers.
@@ -574,27 +595,41 @@
             self,
             self.tr("Import Highlighting Styles"),
             stylesDir,
-            self.tr("Highlighting styles file (*.e6h *.e4h)"))
+            self.tr("Highlighting Styles File (*.ehj);;"
+                    "XML Highlighting Styles File (*.e6h *.e4h)"))
         
         if not fn:
             return
         
-        f = QFile(fn)
-        if f.open(QIODevice.ReadOnly):
-            from E5XML.HighlightingStylesReader import HighlightingStylesReader
-            reader = HighlightingStylesReader(f, lexers)
-            reader.readXML()
-            f.close()
+        if fn.endswith(".ehj"):
+            # new JSON based file
+            from Preferences.HighlightingStylesFile import (
+                HighlightingStylesFile
+            )
+            highlightingStylesFile = HighlightingStylesFile()
+            res = highlightingStylesFile.readFile(fn, lexers)
+            if not res:
+                return
         else:
-            E5MessageBox.critical(
-                self,
-                self.tr("Import Highlighting Styles"),
-                self.tr(
-                    """<p>The highlighting styles could not be read"""
-                    """ from file <b>{0}</b>.</p><p>Reason: {1}</p>""")
-                .format(fn, f.errorString())
-            )
-            return
+            # old XML based file
+            f = QFile(fn)
+            if f.open(QIODevice.ReadOnly):
+                from E5XML.HighlightingStylesReader import (
+                    HighlightingStylesReader
+                )
+                reader = HighlightingStylesReader(f, lexers)
+                reader.readXML()
+                f.close()
+            else:
+                E5MessageBox.critical(
+                    self,
+                    self.tr("Import Highlighting Styles"),
+                    self.tr(
+                        "<p>The highlighting styles file <b>{0}</b> could not"
+                        " be read.</p><p>Reason: {1}</p>"
+                    ).format(fn, f.errorString())
+                )
+                return
         
         self.on_lexerLanguageComboBox_activated(
             self.lexerLanguageComboBox.currentText())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/Preferences/HighlightingStylesFile.py	Thu Jan 28 18:29:00 2021 +0100
@@ -0,0 +1,139 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class representing the highlighting styles JSON file.
+"""
+
+import json
+import time
+
+from PyQt5.QtCore import QObject
+from PyQt5.QtGui import QColor, QFont
+
+from E5Gui import E5MessageBox
+from E5Gui.E5OverrideCursor import E5OverridenCursor
+
+import Preferences
+
+
+class HighlightingStylesFile(QObject):
+    """
+    Class representing the highlighting styles JSON file.
+    """
+    def __init__(self, parent: QObject = None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (defaults to None)
+        @type QObject (optional)
+        """
+        super(HighlightingStylesFile, self).__init__(parent)
+    
+    def writeFile(self, filename: str, lexers: list) -> bool:
+        """
+        Public method to write the highlighting styles data to a highlighting
+        styles JSON file.
+        
+        @param filename name of the highlighting styles file
+        @type str
+        @param lexers list of lexers for which to export the styles
+        @type list of PreferencesLexer
+        @return flag indicating a successful write
+        @rtype bool
+        """
+        stylesDict = {}
+        # step 0: header
+        stylesDict["header"] = {
+            "comment": "eric highlighting styles file",
+            "saved": time.strftime('%Y-%m-%d, %H:%M:%S'),
+            "author": Preferences.getUser("Email"),
+        }
+        
+        # step 1: add the lexer style data
+        stylesDict["lexers"] = []
+        for lexer in lexers:
+            lexerDict = {
+                "name": lexer.language(),
+                "styles": [],
+            }
+            for description, style, substyle in lexer.getStyles():
+                lexerDict["styles"].append({
+                    "description": description,
+                    "style": style,
+                    "substyle": substyle,
+                    "color": lexer.color(style, substyle).name(),
+                    "paper": lexer.paper(style, substyle).name(),
+                    "font": lexer.font(style, substyle).toString(),
+                    "eolfill": lexer.eolFill(style, substyle),
+                    "words": lexer.words(style, substyle).strip(),
+                })
+            stylesDict["lexers"].append(lexerDict)
+        
+        try:
+            jsonString = json.dumps(stylesDict, indent=2)
+            with open(filename, "w") as f:
+                f.write(jsonString)
+        except (TypeError, EnvironmentError) as err:
+            with E5OverridenCursor():
+                E5MessageBox.critical(
+                    None,
+                    self.tr("Export Highlighting Styles"),
+                    self.tr(
+                        "<p>The highlighting styles file <b>{0}</b> could not"
+                        " be written.</p><p>Reason: {1}</p>"
+                    ).format(filename, str(err))
+                )
+                return False
+        
+        return True
+    
+    def readFile(self, filename: str, lexers: dict) -> bool:
+        """
+        Public method to read the highlighting styles data from a highlighting
+        styles JSON file.
+        
+        @param filename name of the highlighting styles file
+        @type str
+        @param lexers dictionary of lexer objects for which to import the
+            styles
+        @type dict of {str: PreferencesLexer}
+        @return flag indicating a successful read
+        @rtype bool
+        """
+        try:
+            with open(filename, "r") as f:
+                jsonString = f.read()
+            stylesDict = json.loads(jsonString)
+        except (EnvironmentError, json.JSONDecodeError) as err:
+            E5MessageBox.critical(
+                None,
+                self.tr("Import Highlighting Styles"),
+                self.tr(
+                    "<p>The highlighting styles file <b>{0}</b> could not be"
+                    " read.</p><p>Reason: {1}</p>"
+                ).format(filename, str(err))
+            )
+            return False
+        
+        for lexerDict in stylesDict["lexers"]:
+            if lexerDict["name"] in lexers:
+                lexer = lexers[lexerDict["name"]]
+                for styleDict in lexerDict["styles"]:
+                    style = styleDict["style"]
+                    substyle = styleDict["substyle"]
+                    lexer.setColor(QColor(styleDict["color"]), style, substyle)
+                    lexer.setPaper(QColor(styleDict["paper"]), style, substyle)
+                    font = QFont()
+                    font.fromString(styleDict["font"])
+                    lexer.setFont(font, style, substyle)
+                    lexer.setEolFill(styleDict["eolfill"], style, substyle)
+                    if substyle >= 0:
+                        # description and words can only be set for sub-styles
+                        lexer.setDescription(styleDict["description"],
+                                             style, substyle)
+                        lexer.setWords(styleDict["words"], style, substyle)
+        
+        return True
--- a/eric6/Preferences/Shortcuts.py	Thu Jan 28 16:36:29 2021 +0100
+++ b/eric6/Preferences/Shortcuts.py	Thu Jan 28 18:29:00 2021 +0100
@@ -233,7 +233,6 @@
                 .format(fn))
 
 
-# TODO: do the JSON shortcuts
 def importShortcuts(fn, helpViewer=None):
     """
     Module function to import the keyboard shortcuts for the defined actions.
--- a/eric6/QScintilla/Lexers/__init__.py	Thu Jan 28 16:36:29 2021 +0100
+++ b/eric6/QScintilla/Lexers/__init__.py	Thu Jan 28 18:29:00 2021 +0100
@@ -866,6 +866,7 @@
         '*.e6t': "XML",
         '*.ecj': "JSON",
         '*.edj': "JSON",
+        '*.ehj': "JSON",
         '*.ekj': "JSON",
         '*.emj': "JSON",
         '*.epj': "JSON",

eric ide

mercurial