# -*- coding: utf-8 -*- # # Copyright © Spyder Project Contributors # Licensed under the terms of the MIT License # (see spyder/__init__.py for details) """ Text snippets configuration tabs. """ # Standard library imports import copy import os.path as osp # Third party imports from qtpy.compat import getsavefilename, getopenfilename from qtpy.QtCore import Qt, Slot from qtpy.QtWidgets import (QComboBox, QGroupBox, QGridLayout, QLabel, QMessageBox, QPushButton, QTabWidget, QVBoxLayout, QWidget, QFileDialog) # Local imports from spyder.config.base import _ from spyder.config.snippets import SNIPPETS from spyder.plugins.completion.providers.snippets.widgets import ( SnippetModelsProxy, SnippetTable, SUPPORTED_LANGUAGES_PY, PYTHON_POS) from spyder.api.preferences import SpyderPreferencesTab LSP_URL = "https://microsoft.github.io/language-server-protocol" class SnippetsConfigTab(SpyderPreferencesTab): TITLE = _('Snippets') def __init__(self, parent): super().__init__(parent) self.snippets_language = 'python' grammar_url = ( "" "{1}".format(LSP_URL, _('the LSP grammar'))) snippets_info_label = QLabel( _("Spyder allows to define custom completion snippets to use " "in addition to the ones offered by the Language Server " "Protocol (LSP). Each snippet should follow {}.

" "Note: All changes will be effective only when applying " "the settings").format(grammar_url)) snippets_info_label.setOpenExternalLinks(True) snippets_info_label.setWordWrap(True) snippets_info_label.setAlignment(Qt.AlignJustify) self.snippets_language_cb = QComboBox(self) self.snippets_language_cb.setToolTip( _('Programming language provided by the LSP server')) self.snippets_language_cb.addItems(SUPPORTED_LANGUAGES_PY) self.snippets_language_cb.setCurrentIndex(PYTHON_POS) self.snippets_language_cb.currentTextChanged.connect( self.change_language_snippets) snippet_lang_group = QGroupBox(_('Language')) snippet_lang_layout = QVBoxLayout() snippet_lang_layout.addWidget(self.snippets_language_cb) snippet_lang_group.setLayout(snippet_lang_layout) self.snippets_proxy = SnippetModelsProxy(self) self.snippets_table = SnippetTable( self, self.snippets_proxy, language=self.snippets_language) self.snippets_table.setMaximumHeight(180) snippet_table_group = QGroupBox(_('Available snippets')) snippet_table_layout = QVBoxLayout() snippet_table_layout.addWidget(self.snippets_table) snippet_table_group.setLayout(snippet_table_layout) # Buttons self.reset_snippets_btn = QPushButton(_("Reset to default values")) self.new_snippet_btn = QPushButton(_("Create a new snippet")) self.delete_snippet_btn = QPushButton( _("Delete currently selected snippet")) self.delete_snippet_btn.setEnabled(False) self.export_snippets_btn = QPushButton(_("Export snippets to JSON")) self.import_snippets_btn = QPushButton(_("Import snippets from JSON")) # Slots connected to buttons self.new_snippet_btn.clicked.connect(self.create_new_snippet) self.reset_snippets_btn.clicked.connect(self.reset_default_snippets) self.delete_snippet_btn.clicked.connect(self.delete_snippet) self.export_snippets_btn.clicked.connect(self.export_snippets) self.import_snippets_btn.clicked.connect(self.import_snippets) # Buttons layout btns = [self.new_snippet_btn, self.delete_snippet_btn, self.reset_snippets_btn, self.export_snippets_btn, self.import_snippets_btn] sn_buttons_layout = QGridLayout() for i, btn in enumerate(btns): sn_buttons_layout.addWidget(btn, i, 1) sn_buttons_layout.setColumnStretch(0, 1) sn_buttons_layout.setColumnStretch(1, 2) sn_buttons_layout.setColumnStretch(2, 1) # Snippets layout snippets_layout = QVBoxLayout() snippets_layout.addWidget(snippets_info_label) snippets_layout.addWidget(snippet_lang_group) snippets_layout.addWidget(snippet_table_group) snippets_layout.addLayout(sn_buttons_layout) self.setLayout(snippets_layout) def create_new_snippet(self): self.snippets_table.show_editor(new_snippet=True) def delete_snippet(self): idx = self.snippets_table.currentIndex().row() self.snippets_table.delete_snippet(idx) self.set_modified(True) self.delete_snippet_btn.setEnabled(False) def reset_default_snippets(self): language = self.snippets_language_cb.currentText() default_snippets_lang = copy.deepcopy( SNIPPETS.get(language.lower(), {})) self.snippets_proxy.reload_model( language.lower(), default_snippets_lang) self.snippets_table.reset_plain() self.set_modified(True) def change_language_snippets(self, language): self.snippets_table.update_language_model(language) def export_snippets(self): filename, _selfilter = getsavefilename( self, _("Save snippets"), 'spyder_snippets.json', filters='JSON (*.json)', selectedfilter='', options=QFileDialog.HideNameFilterDetails) if filename: filename = osp.normpath(filename) self.snippets_proxy.export_snippets(filename) def import_snippets(self): filename, _sf = getopenfilename( self, _("Load snippets"), filters='JSON (*.json)', selectedfilter='', options=QFileDialog.HideNameFilterDetails, ) if filename: filename = osp.normpath(filename) valid, total, errors = self.snippets_proxy.import_snippets( filename) modified = True if len(errors) == 0: QMessageBox.information( self, _('All snippets imported'), _('{0} snippets were loaded successfully').format(valid), QMessageBox.Ok) else: if 'loading' in errors: modified = False QMessageBox.critical( self, _('JSON malformed'), _('There was an error when trying to load the ' 'provided JSON file: {0}').format( errors['loading']), QMessageBox.Ok ) elif 'validation' in errors: modified = False QMessageBox.critical( self, _('Invalid snippet file'), _('The provided snippet file does not comply with ' 'the Spyder JSON snippets spec and therefore it ' 'cannot be loaded.

{}').format( errors['validation']), QMessageBox.Ok ) elif 'syntax' in errors: syntax_errors = errors['syntax'] msg = [] for syntax_key in syntax_errors: syntax_err = syntax_errors[syntax_key] msg.append('{0}: {1}'.format( syntax_key, syntax_err)) err_msg = '
'.join(msg) QMessageBox.warning( self, _('Incorrect snippet format'), _('Spyder was able to load {0}/{1} snippets ' 'correctly, please check the following snippets ' 'for any syntax errors: ' '

{2}').format(valid, total, err_msg), QMessageBox.Ok ) self.set_modified(modified) def apply_settings(self): return self.snippets_proxy.save_snippets()