Sat, 03 Feb 2018 16:15:24 +0100
Finished implementing support for EditorConfig for the IDE editor component and the standalone editor.
--- a/Preferences/ConfigurationPages/EditorFilePage.py Sat Feb 03 10:45:52 2018 +0100 +++ b/Preferences/ConfigurationPages/EditorFilePage.py Sat Feb 03 16:15:24 2018 +0100 @@ -76,6 +76,8 @@ Preferences.getEditor("DefaultSaveFilter"))) self.automaticEolConversionCheckBox.setChecked( Preferences.getEditor("AutomaticEOLConversion")) + self.insertFinalNewlineCheckBox.setChecked( + Preferences.getEditor("InsertFinalNewline")) eolMode = Preferences.getEditor("EOLMode") if eolMode == QsciScintilla.EolWindows: @@ -143,6 +145,9 @@ Preferences.setEditor( "AutomaticEOLConversion", self.automaticEolConversionCheckBox.isChecked()) + Preferences.setEditor( + "InsertFinalNewline", + self.insertFinalNewlineCheckBox.isChecked()) if self.crlfRadioButton.isChecked(): Preferences.setEditor("EOLMode", QsciScintilla.EolWindows)
--- a/Preferences/ConfigurationPages/EditorFilePage.ui Sat Feb 03 10:45:52 2018 +0100 +++ b/Preferences/ConfigurationPages/EditorFilePage.ui Sat Feb 03 16:15:24 2018 +0100 @@ -10,7 +10,7 @@ <height>1622</height> </rect> </property> - <layout class="QVBoxLayout" name="verticalLayout_3"> + <layout class="QVBoxLayout" name="verticalLayout_4"> <item> <widget class="QLabel" name="headerLabel"> <property name="text"> @@ -173,7 +173,17 @@ <property name="title"> <string>Save</string> </property> - <layout class="QVBoxLayout" name="verticalLayout_4"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QCheckBox" name="insertFinalNewlineCheckBox"> + <property name="toolTip"> + <string>Select to insert a final newline if none is there</string> + </property> + <property name="text"> + <string>Insert final newline upon save</string> + </property> + </widget> + </item> <item> <widget class="QCheckBox" name="stripWhitespaceCheckBox"> <property name="toolTip"> @@ -632,6 +642,7 @@ <tabstop>crRadioButton</tabstop> <tabstop>crlfRadioButton</tabstop> <tabstop>automaticEolConversionCheckBox</tabstop> + <tabstop>insertFinalNewlineCheckBox</tabstop> <tabstop>stripWhitespaceCheckBox</tabstop> <tabstop>createBackupFileCheckBox</tabstop> <tabstop>autosaveSlider</tabstop>
--- a/Preferences/__init__.py Sat Feb 03 10:45:52 2018 +0100 +++ b/Preferences/__init__.py Sat Feb 03 16:15:24 2018 +0100 @@ -395,6 +395,7 @@ "WarnFilesize": 512, "ClearBreaksOnClose": True, "StripTrailingWhitespace": False, + "InsertFinalNewline": True, "CommentColumn0": True, "OverrideEditAreaColours": False,
--- a/QScintilla/Editor.py Sat Feb 03 10:45:52 2018 +0100 +++ b/QScintilla/Editor.py Sat Feb 03 16:15:24 2018 +0100 @@ -194,10 +194,11 @@ self.notcoveredMarkers = [] # just a list of marker handles self.showingNotcoveredMarkers = False + self.lexer_ = None + self.__loadEditorConfig() self.condHistory = [] - self.lexer_ = None self.__lexerReset = False self.completer = None self.encoding = self.__getEditorConfig("DefaultEncoding") @@ -2960,6 +2961,8 @@ """ QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) + self.__loadEditorConfig(fileName=fn) + try: if createIt and not os.path.exists(fn): f = open(fn, "w") @@ -2982,31 +2985,29 @@ .format(fn, str(why))) QApplication.restoreOverrideCursor() raise - fileEol = self.detectEolString(txt) modified = False - # TODO: editorconfig: indent_style - if (not Preferences.getEditor("TabForIndentation")) and \ + + if (not self.__getEditorConfig("TabForIndentation")) and \ Preferences.getEditor("ConvertTabsOnLoad") and \ not (self.lexer_ and self.lexer_.alwaysKeepTabs()): - txtExpanded = txt.expandtabs(Preferences.getEditor("TabWidth")) + txtExpanded = txt.expandtabs(self.__getEditorConfig("TabWidth")) if txtExpanded != txt: modified = True txt = txtExpanded - del txtExpanded self.setText(txt) # get eric specific flags self.__processFlags() - # perform automatic eol conversion - # TODO: editorconfig: end_of_line + # perform automatic EOL conversion if self.__getEditorConfig("EOLMode", nodefault=True) or \ Preferences.getEditor("AutomaticEOLConversion"): self.convertEols(self.eolMode()) else: + fileEol = self.detectEolString(txt) self.setEolModeByEolString(fileEol) self.extractTasks() @@ -3037,25 +3038,25 @@ @param backup flag indicating to save a backup (boolean) @return flag indicating success (boolean) """ - # TODO: editorconfig: apply end_of_line, trim_trailing_whitespace, - # insert_final_newline config = self.__loadEditorConfigObject(fn) - # TODO: editorconfig: trim_trailing_whitespace - if Preferences.getEditor("StripTrailingWhitespace"): + eol = self.__getEditorConfig("EOLMode", nodefault=True, config=config) + if eol is not None: + self.convertEols(eol) + + if self.__getEditorConfig("StripTrailingWhitespace", config=config): self.__removeTrailingWhitespace() txt = self.text() - # work around glitch in scintilla: always make sure, - # that the last line is terminated properly - # TODO: editorconfig: insert_final_newline - eol = self.getLineSeparator() - if eol: - if len(txt) >= len(eol): - if txt[-len(eol):] != eol: + + if self.__getEditorConfig("InsertFinalNewline", config=config): + eol = self.getLineSeparator() + if eol: + if len(txt) >= len(eol): + if txt[-len(eol):] != eol: + txt += eol + else: txt += eol - else: - txt += eol # create a backup file, if the option is set createBackup = backup and Preferences.getEditor("CreateBackupFile") @@ -3205,8 +3206,13 @@ # save to project, if a project is loaded if self.project.isOpen() and \ self.project.startswithProjectPath(fn): - # TODO: check against editorconfig - self.setEolModeByEolString(self.project.getEolString()) + editorConfigEol = self.__getEditorConfig( + "EOLMode", nodefault=True, + config=self.__loadEditorConfigObject(fn)) + if editorConfigEol is not None: + self.setEolMode(editorConfigEol) + else: + self.setEolModeByEolString(self.project.getEolString()) self.convertEols(self.eolMode()) else: fn = self.fileName @@ -4336,20 +4342,24 @@ self.setMarginWidth( self.__linenoMargin, '8' * (len(str(self.lines())) + 1)) - def __setTextDisplay(self): - """ - Private method to configure the text display. - """ - # TODO: editorconfig: tab_width - self.setTabWidth(Preferences.getEditor("TabWidth")) - # TODO: editorconfig: indent_size - self.setIndentationWidth(Preferences.getEditor("IndentWidth")) - # TODO: editorconfig: indent_style + def __setTabAndIndent(self): + """ + Private method to set indentation size and style and tab width. + """ + self.setTabWidth(self.__getEditorConfig("TabWidth")) + self.setIndentationWidth(self.__getEditorConfig("IndentWidth")) if self.lexer_ and self.lexer_.alwaysKeepTabs(): self.setIndentationsUseTabs(True) else: self.setIndentationsUseTabs( - Preferences.getEditor("TabForIndentation")) + self.__getEditorConfig("TabForIndentation")) + + def __setTextDisplay(self): + """ + Private method to configure the text display. + """ + self.__setTabAndIndent() + self.setTabIndents(Preferences.getEditor("TabIndents")) self.setBackspaceUnindents(Preferences.getEditor("TabIndents")) self.setIndentationGuides(Preferences.getEditor("IndentationGuides")) @@ -4466,11 +4476,12 @@ if self.fileName and \ self.project.isOpen() and \ self.project.isProjectFile(self.fileName): - # TODO: editorconfig: end_of_line - eolMode = self.__getEditorConfig("EOLMode") + eolMode = self.__getEditorConfig("EOLMode", nodefault=True) if eolMode is None: - eolMode = self.project.getEolString() - self.setEolModeByEolString(eolMode) + eolStr = self.project.getEolString() + self.setEolModeByEolString(eolStr) + else: + self.setEolMode(eolMode) else: eolMode = self.__getEditorConfig("EOLMode") eolMode = QsciScintilla.EolMode(eolMode) @@ -7279,7 +7290,11 @@ self.__setSpellingLanguage(self.project.getProjectSpellLanguage(), pwl=pwl, pel=pel) - self.setEolModeByEolString(self.project.getEolString()) + editorConfigEol = self.__getEditorConfig("EOLMode", nodefault=True) + if editorConfigEol is not None: + self.setEolMode(editorConfigEol) + else: + self.setEolModeByEolString(self.project.getEolString()) self.convertEols(self.eolMode()) def addedToProject(self): @@ -7315,7 +7330,7 @@ pass ####################################################################### - ## Spellchecking related methods + ## Spell checking related methods ####################################################################### def __setSpellingLanguage(self, language, pwl="", pel=""): @@ -8062,6 +8077,9 @@ fileName = self.fileName self.__editorConfig = self.__loadEditorConfigObject(fileName) + + if fileName: + self.__setTabAndIndent() def __loadEditorConfigObject(self, fileName): """ @@ -8130,6 +8148,20 @@ value = None elif option == "DefaultEncoding": value = config["charset"] + elif option == "InsertFinalNewline": + value = Utilities.toBool(config["insert_final_newline"]) + elif option == "StripTrailingWhitespace": + value = Utilities.toBool(config["trim_trailing_whitespace"]) + elif option == "TabWidth": + value = int(config["tab_width"]) + elif option == "IndentWidth": + value = config["indent_size"] + if value == "tab": + value = self.__getEditorConfig("TabWidth", config=config) + else: + value = int(value) + elif option == "TabForIndentation": + value = config["indent_style"] == "tab" except KeyError: value = None @@ -8138,3 +8170,14 @@ value = Preferences.getEditor(option) return value + + def getEditorConfig(self, option): + """ + Public method to get the requested option via EditorConfig. + + @param option Preferences option key + @type str + @return value of requested setting + @rtype any + """ + return self.__getEditorConfig(option)
--- a/QScintilla/Exporters/ExporterHTML.py Sat Feb 03 10:45:52 2018 +0100 +++ b/QScintilla/Exporters/ExporterHTML.py Sat Feb 03 16:15:24 2018 +0100 @@ -401,7 +401,7 @@ # export ReST to HTML html = self.__generateFromReSTDocutils() else: - tabSize = Preferences.getEditor("TabWidth") + tabSize = self.editor.getEditorConfig("TabWidth") if tabSize == 0: tabSize = 4 wysiwyg = Preferences.getEditorExporter("HTML/WYSIWYG")
--- a/QScintilla/Exporters/ExporterODT.py Sat Feb 03 10:45:52 2018 +0100 +++ b/QScintilla/Exporters/ExporterODT.py Sat Feb 03 16:15:24 2018 +0100 @@ -45,7 +45,7 @@ QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) QApplication.processEvents() - tabSize = Preferences.getEditor("TabWidth") + tabSize = self.editor.getEditorConfig("TabWidth") if tabSize == 0: tabSize = 4 wysiwyg = Preferences.getEditorExporter("ODT/WYSIWYG")
--- a/QScintilla/Exporters/ExporterPDF.py Sat Feb 03 10:45:52 2018 +0100 +++ b/QScintilla/Exporters/ExporterPDF.py Sat Feb 03 16:15:24 2018 +0100 @@ -427,7 +427,7 @@ self.editor.recolor(0, -1) lex = self.editor.getLexer() - tabSize = Preferences.getEditor("TabWidth") + tabSize = self.editor.getEditorConfig("TabWidth") if tabSize == 0: tabSize = 4
--- a/QScintilla/Exporters/ExporterRTF.py Sat Feb 03 10:45:52 2018 +0100 +++ b/QScintilla/Exporters/ExporterRTF.py Sat Feb 03 16:15:24 2018 +0100 @@ -128,7 +128,7 @@ self.editor.recolor(0, -1) lex = self.editor.getLexer() - tabSize = Preferences.getEditor("TabWidth") + tabSize = self.editor.getEditorConfig("TabWidth") if tabSize == 0: tabSize = 4 wysiwyg = Preferences.getEditorExporter("RTF/WYSIWYG")
--- a/QScintilla/Exporters/ExporterTEX.py Sat Feb 03 10:45:52 2018 +0100 +++ b/QScintilla/Exporters/ExporterTEX.py Sat Feb 03 16:15:24 2018 +0100 @@ -122,7 +122,7 @@ self.editor.recolor(0, -1) - tabSize = Preferences.getEditor("TabWidth") + tabSize = self.editor.getEditorConfig("TabWidth") if tabSize == 0: tabSize = 4
--- a/QScintilla/Lexers/LexerPython.py Sat Feb 03 10:45:52 2018 +0100 +++ b/QScintilla/Lexers/LexerPython.py Sat Feb 03 16:15:24 2018 +0100 @@ -83,7 +83,7 @@ @param editor QScintilla editor @return amount of difference in indentation (integer) """ - indent_width = Preferences.getEditor('IndentWidth') + indent_width = editor.getEditorConfig('IndentWidth') lead_spaces = editor.indentation(line)
--- a/QScintilla/MiniEditor.py Sat Feb 03 10:45:52 2018 +0100 +++ b/QScintilla/MiniEditor.py Sat Feb 03 16:15:24 2018 +0100 @@ -91,6 +91,19 @@ self.setCaretWidth(0) super(MiniScintilla, self).focusOutEvent(event) + + def removeTrailingWhitespace(self): + """ + Public method to remove trailing whitespace. + """ + searchRE = r"[ \t]+$" # whitespace at the end of a line + + ok = self.findFirstTarget(searchRE, True, False, False, 0, 0) + self.beginUndoAction() + while ok: + self.replaceTarget("") + ok = self.findNextTarget() + self.endUndoAction() class MiniEditor(E5MainWindow): @@ -146,6 +159,9 @@ self.lexer_ = None self.apiLanguage = "" self.filetype = "" + self.__curFile = filename + + self.__loadEditorConfig(filename) self.__createActions() self.__createMenus() @@ -2291,9 +2307,9 @@ @param fileName name of the file to load (string) @param filetype type of the source file (string) """ - self.__loadEditorConfig(fileName=fileName) + QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) - QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) + self.__loadEditorConfig(fileName=fileName) try: encoding = self.__getEditorConfig("DefaultEncoding", @@ -2313,18 +2329,38 @@ QApplication.restoreOverrideCursor() return + modified = False + + if (not self.__getEditorConfig("TabForIndentation")) and \ + Preferences.getEditor("ConvertTabsOnLoad") and \ + not (self.lexer_ and + self.lexer_.alwaysKeepTabs()): + txtExpanded = txt.expandtabs(self.__getEditorConfig("TabWidth")) + if txtExpanded != txt: + modified = True + txt = txtExpanded + self.__textEdit.setText(txt) QApplication.restoreOverrideCursor() + self.__textEdit.setModified(modified) + self.setWindowModified(modified) + if filetype is None: self.filetype = "" else: self.filetype = filetype self.__setCurrentFile(fileName) - fileEol = self.__textEdit.detectEolString(txt) - # TODO: editorconfig: end_of_line - self.__textEdit.setEolModeByEolString(fileEol) + self.__textEdit.setModified(modified) + self.setWindowModified(modified) + + eolMode = self.__getEditorConfig("EOLMode", nodefault=True) + if eolMode is None: + fileEol = self.__textEdit.detectEolString(txt) + self.__textEdit.setEolModeByEolString(fileEol) + else: + self.__textEdit.convertEols(eolMode) self.__statusBar.showMessage(self.tr("File loaded"), 2000) @@ -2356,13 +2392,29 @@ @return flag indicating success @rtype bool """ - # TODO: editorconfig: apply end_of_line, trim_trailing_whitespace, - # insert_final_newline + QApplication.setOverrideCursor(Qt.WaitCursor) + config = self.__loadEditorConfigObject(fileName) - QApplication.setOverrideCursor(Qt.WaitCursor) + eol = self.__getEditorConfig("EOLMode", nodefault=True, config=config) + if eol is not None: + self.__textEdit.convertEols(eol) + + if self.__getEditorConfig("StripTrailingWhitespace", config=config): + self.__textEdit.removeTrailingWhitespace() + txt = self.__textEdit.text() - # TODO: editorconfig: insert_final_newline + + if self.__getEditorConfig("InsertFinalNewline", config=config): + eol = self.__textEdit.getLineSeparator() + if eol: + if len(txt) >= len(eol): + if txt[-len(eol):] != eol: + txt += eol + else: + txt += eol + + # now write text to the file try: editorConfigEncoding = self.__getEditorConfig( "DefaultEncoding", nodefault=True, config=config) @@ -2521,23 +2573,25 @@ self.__textEdit.setMarginWidth( 0, '8' * (len(str(self.__textEdit.lines())) + 1)) + def __setTabAndIndent(self): + """ + Private method to set indentation size and style and tab width. + """ + self.__textEdit.setTabWidth(self.__getEditorConfig("TabWidth")) + self.__textEdit.setIndentationWidth( + self.__getEditorConfig("IndentWidth")) + if self.lexer_ and self.lexer_.alwaysKeepTabs(): + self.__textEdit.setIndentationsUseTabs(True) + else: + self.__textEdit.setIndentationsUseTabs( + self.__getCurrentWord("TabForIndentation")) + def __setTextDisplay(self): """ Private method to configure the text display. """ - # TODO: editorconfig: move these to separate method called, when - # EditorConfig has been loaded - # TODO: editorconfig: tab_width - self.__textEdit.setTabWidth(Preferences.getEditor("TabWidth")) - # TODO: editorconfig: indent_size - self.__textEdit.setIndentationWidth( - Preferences.getEditor("IndentWidth")) - # TODO: editorconfig: indent_style - if self.lexer_ and self.lexer_.alwaysKeepTabs(): - self.__textEdit.setIndentationsUseTabs(True) - else: - self.__textEdit.setIndentationsUseTabs( - Preferences.getEditor("TabForIndentation")) + self.__setTabAndIndent() + self.__textEdit.setTabIndents(Preferences.getEditor("TabIndents")) self.__textEdit.setBackspaceUnindents( Preferences.getEditor("TabIndents")) @@ -2634,8 +2688,7 @@ """ Private method to configure the eol mode of the editor. """ - # TODO: editorconfig: end_of_line - eolMode = Preferences.getEditor("EOLMode") + eolMode = self.__getEditorConfig("EOLMode") eolMode = QsciScintilla.EolMode(eolMode) self.__textEdit.setEolMode(eolMode) @@ -3264,11 +3317,15 @@ else: self.filetype = filetype - # TODO: editorconfig: end_of_line - fileEol = self.__textEdit.detectEolString(txt) - self.__textEdit.setEolModeByEolString(fileEol) + eolMode = self.__getEditorConfig("EOLMode", nodefault=True) + if eolMode is None: + fileEol = self.__textEdit.detectEolString(txt) + self.__textEdit.setEolModeByEolString(fileEol) + else: + self.__textEdit.convertEols(eolMode) self.__textEdit.setModified(False) + self.setWindowModified(False) ####################################################################### ## Methods implementing the interface to EditorConfig @@ -3285,6 +3342,9 @@ fileName = self.__curFile self.__editorConfig = self.__loadEditorConfigObject(fileName) + + if fileName: + self.__setTabAndIndent() def __loadEditorConfigObject(self, fileName): """ @@ -3353,11 +3413,25 @@ value = None elif option == "DefaultEncoding": value = config["charset"] + elif option == "InsertFinalNewline": + value = Utilities.toBool(config["insert_final_newline"]) + elif option == "StripTrailingWhitespace": + value = Utilities.toBool(config["trim_trailing_whitespace"]) + elif option == "TabWidth": + value = int(config["tab_width"]) + elif option == "IndentWidth": + value = config["indent_size"] + if value == "tab": + value = self.__getEditorConfig("TabWidth", config=config) + else: + value = int(value) + elif option == "TabForIndentation": + value = config["indent_style"] == "tab" except KeyError: value = None if value is None and not nodefault: - # use Preferences in case of error + # use Preferences as default in case of error value = Preferences.getEditor(option) return value
--- a/Utilities/__init__.py Sat Feb 03 10:45:52 2018 +0100 +++ b/Utilities/__init__.py Sat Feb 03 16:15:24 2018 +0100 @@ -591,7 +591,7 @@ def linesep(): """ - Function to return the lineseparator used by the editor. + Function to return the line separator used by the editor. @return line separator used by the editor (string) """