def test_preferred_encodings(): cache = Cache(suffix='-pytest') cache.clear() encodings = cache.preferred_encodings assert len(encodings) == 1 assert encodings[0] == convert_to_codec_key( locale.getpreferredencoding()) encodings.append('utf_16') cache.preferred_encodings = encodings cache2 = Cache(suffix='-pytest') assert 'utf_16' in cache2.preferred_encodings
def test_cached_encodings(): s = Cache(suffix='-pytest') s.clear() # not in cache with pytest.raises(KeyError): s.get_file_encoding(__file__) s.set_file_encoding(__file__, 'utf_16') s = Cache(suffix='-pytest') assert s.get_file_encoding(__file__) == 'utf_16'
def test_cached_encodings(): s = Cache(suffix='-pytest') s.clear() # not in cache with pytest.raises(KeyError): s.get_file_encoding(os.path.join(os.getcwd(), 'test', 'files', 'big5hkscs.txt')) s.set_file_encoding(__file__, 'utf_16') s = Cache(suffix='-pytest') assert s.get_file_encoding(__file__) == 'utf_16'
def edit_encoding(cls, parent): """ Static helper method that shows the encoding editor dialog If the dialog was accepted the new encodings are added to the settings. :param parent: parent widget :return: True in case of succes, False otherwise """ dlg = cls(parent) if dlg.exec_() == dlg.Accepted: settings = Cache() settings.preferred_encodings = dlg.get_preferred_encodings() return True return False
def search_file(path, search_settings): """ Search occurrences in a file. :param path: path of the file to search """ results = [] string = '' for encoding in Cache().preferred_encodings: try: with open(path, encoding=encoding) as f: string = f.read() except OSError: return except UnicodeDecodeError: print('failed to open file %r with encoding %s' % (path, encoding)) continue else: print('file %r opened with encoding %s' % (path, encoding)) break if string: data = { 'string': None, 'sub': search_settings['find'], 'regex': search_settings['regex'], 'whole_word': search_settings['whole_words_only'], 'case_sensitive': search_settings['case_sensitive'] } for line_nbr, line in enumerate(string.splitlines()): data['string'] = line occurrences = findall(data) if occurrences: results.append((line_nbr, line, occurrences)) return results
def _restore_cached_pos(self): pos = Cache().get_cursor_position(self.path) max_pos = self.editor.document().characterCount() if pos > max_pos: pos = max_pos - 1 tc = self.editor.textCursor() tc.setPosition(pos) self.editor.setTextCursor(tc) QtCore.QTimer.singleShot(1, self.editor.centerCursor)
def inner_action(*args): """ Inner action: open file """ # cache cursor position before reloading so that the cursor # position is restored automatically after reload has finished. # See OpenCobolIDE/OpenCobolIDE#97 Cache().set_cursor_position(self.editor.file.path, self.editor.textCursor().position()) self.editor.file.open(self.editor.file.path) self.file_reloaded.emit()
def inner_action(*args): """ Inner action: open file """ # cache cursor position before reloading so that the cursor # position is restored automatically after reload has finished. # See OpenCobolIDE/OpenCobolIDE#97 Cache().set_cursor_position(self.editor.file.path, self.editor.textCursor().position()) if os.path.exists(self.editor.file.path): self.editor.file.open(self.editor.file.path) self.file_reloaded.emit() else: # file moved just after a change, see OpenCobolIDE/OpenCobolIDE#337 self._notify_deleted_file()
def test_preferred_encodings(): cache = Cache(suffix='-pytest') cache.clear() encodings = cache.preferred_encodings assert len(encodings) == 1 assert encodings[0] == convert_to_codec_key(locale.getpreferredencoding()) encodings.append('utf_16') cache.preferred_encodings = encodings cache2 = Cache(suffix='-pytest') assert 'utf_16' in cache2.preferred_encodings
def close(self, clear=True): """ Close the file open in the editor: - clear editor content - reset file attributes to their default values :param clear: True to clear the editor content. Default is True. """ Cache().set_cursor_position(self.path, self.editor.textCursor().position()) self.editor._original_text = '' if clear: self.editor.clear() self._path = '' self.mimetype = '' self._encoding = locale.getpreferredencoding()
def _get_encoding(filename): """ Gets a filename encoding from the pyqode.core cache. If the requested file path could not be found in cache, the locale preferred encoding is used. :param filename: path of the file in cache. :return: cached encoding """ try: encoding = Cache().get_file_encoding(filename) except KeyError: encoding = locale.getpreferredencoding() _logger().warning( 'encoding for %s not found in cache, using locale preferred ' 'encoding instead: %s', filename, encoding) return encoding
def close(self, clear=True): """ Closes the editor, stops the backend and removes any installed mode/panel. This is also where we cache the cursor position. :param clear: True to clear the editor content before closing. """ self.decorations.clear() self.modes.clear() self.panels.clear() self.backend.stop() Cache().set_cursor_position( self.file.path, TextHelper(self).cursor_position()) super(CodeEdit, self).close() _logger().info('closed')
def _refresh_items(self): self._lock = True self.clear() for i, encoding in enumerate(sorted(Cache().preferred_encodings)): encoding = convert_to_codec_key(encoding) try: alias, lang = ENCODINGS_MAP[encoding] except KeyError: _logger().warning('KeyError with encoding: %s', encoding) else: self.addItem('%s (%s)' % (alias, lang)) self.setItemData(i, encoding, QtCore.Qt.UserRole) if encoding == self._current_encoding or \ convert_to_codec_key(alias) == self._current_encoding: self.setCurrentIndex(i) self.insertSeparator(self.count()) self.addItem('Add or remove') self._lock = False
def close(self, clear=True): """ Closes the editor, stops the backend and removes any installed mode/panel. This is also where we cache the cursor position. :param clear: True to clear the editor content before closing. """ if self._tooltips_runner: self._tooltips_runner.cancel_requests() self._tooltips_runner = None self.decorations.clear() self.modes.clear() self.panels.clear() self.backend.stop() Cache().set_cursor_position(self.file.path, self.textCursor().position()) super(CodeEdit, self).close()
def _load_preferred(self): self._load_available() # setup preferred encodings self.ui.tableWidgetPreferred.setColumnCount(2) self.ui.tableWidgetPreferred.setSelectionMode( self.ui.tableWidgetPreferred.SingleSelection) self.ui.tableWidgetPreferred.setSelectionBehavior( self.ui.tableWidgetPreferred.SelectRows) self.ui.tableWidgetPreferred.setHorizontalHeaderLabels( ['Encoding', 'Language']) self.ui.tableWidgetPreferred.verticalHeader().hide() self.ui.tableWidgetPreferred.setSortingEnabled(True) for i, encoding in enumerate(Cache().preferred_encodings): encoding = encodings.convert_to_codec_key(encoding) value = encodings.ENCODINGS_MAP[encoding] row = self.ui.tableWidgetPreferred.rowCount() self.ui.tableWidgetPreferred.insertRow(row) for column in range(2): item = QtWidgets.QTableWidgetItem(value[column].strip()) item.setData(QtCore.Qt.UserRole, encoding) self.ui.tableWidgetPreferred.setItem(row, column, item) self.ui.tableWidgetPreferred.sortByColumn(0, QtCore.Qt.AscendingOrder)
def reset_editor(editor): assert isinstance(editor, CodeEdit) Cache().clear() editor.modes.clear() editor.panels.clear() setup_editor(editor) editor.font_name = CodeEdit._DEFAULT_FONT editor.font_size = 10 editor.use_spaces_instead_of_tabs = True editor.tab_length = 4 editor.save_on_focus_out = False pal = QtWidgets.QApplication.instance().palette() editor.selection_background = pal.highlight().color() editor.selection_foreground = pal.highlightedText().color() editor.syntax_highlighter.color_scheme = ColorScheme('qt') editor.zoom_level = 0 editor.resize(800, 600) editor.backend.stop() assert not editor.backend.running editor.backend.start(server_path()) wait_for_connected(editor) assert editor.backend.running
def _refresh(self): self._clear_actions() for i, encoding in enumerate(sorted(Cache().preferred_encodings)): encoding = convert_to_codec_key(encoding) try: alias, lang = ENCODINGS_MAP[encoding] except KeyError: _logger().debug('KeyError with encoding:', encoding) else: action = QtWidgets.QAction('%s (%s)' % (alias, lang), self) action.setData(encoding) action.setCheckable(True) if encoding == self._current_encoding or \ convert_to_codec_key(alias) == self._current_encoding: action.setChecked(True) self.addAction(action) self._group.addAction(action) self._group.triggered.connect(self._on_encoding_triggered) self.addSeparator() self._edit_action = QtWidgets.QAction('Add or remove', self) self._edit_action.triggered.connect(self._on_edit_requested) self.addAction(self._edit_action)
def _load_available(self): self.ui.tableWidgetAvailable.setColumnCount(2) self.ui.tableWidgetAvailable.setSelectionMode( self.ui.tableWidgetAvailable.SingleSelection) self.ui.tableWidgetAvailable.setSelectionBehavior( self.ui.tableWidgetAvailable.SelectRows) self.ui.tableWidgetAvailable.setHorizontalHeaderLabels( ['Encoding', 'Language']) self.ui.tableWidgetAvailable.verticalHeader().hide() self.ui.tableWidgetAvailable.setSortingEnabled(True) preferred = Cache().preferred_encodings for key in sorted(encodings.ENCODINGS_MAP.keys()): value = encodings.ENCODINGS_MAP[key] if key not in preferred: # lang_item.setData(QtCore.Qt.UserRole, key) row = self.ui.tableWidgetAvailable.rowCount() self.ui.tableWidgetAvailable.insertRow(row) for column in range(2): item = QtWidgets.QTableWidgetItem(value[column].strip()) item.setData(QtCore.Qt.UserRole, key) # item.setData(QtCore.Qt.UserRole, key) self.ui.tableWidgetAvailable.setItem(row, column, item) self.ui.tableWidgetAvailable.sortByColumn(0, QtCore.Qt.AscendingOrder)
def parse(path): if path.endswith('_rc.py'): return [] try: encoding = Cache().get_file_encoding(path) except KeyError: encoding = locale.getpreferredencoding() try: with open(path, encoding=encoding) as f: code = f.read() except (UnicodeDecodeError, OSError): try: with open(path, encoding='utf-8') as f: code = f.read() except (UnicodeDecodeError, OSError): # could not deduce encoding return [] request_data = { 'path': path, 'code': code } results = defined_names(request_data) return [Definition.from_dict(ddict) for ddict in results]
def save(self, path=None, encoding=None, fallback_encoding=None): """ Save the editor content to a file. :param path: optional file path. Set it to None to save using the current path (save), set a new path to save as. :param encoding: optional encoding, will use the current file encoding if None. :param fallback_encoding: Fallback encoding to use in case of encoding error. None to use the locale preferred encoding """ if fallback_encoding is None: fallback_encoding = locale.getpreferredencoding() self.saving = True _logger().debug("saving %r to %r with %r encoding", self.path, path, encoding) if path is None: if self.path: path = self.path else: _logger().debug( 'failed to save file, path argument cannot be None if ' 'FileManager.path is also None') return False # use cached encoding if None were specified if encoding is None: encoding = self._encoding self.editor.text_saving.emit(str(path)) # remember cursor position (clean_document might mess up the # cursor pos) if self.clean_trailing_whitespaces: sel_end, sel_start = self._get_selection() TextHelper(self.editor).clean_document() plain_text = self.editor.toPlainText() # perform a safe save: we first save to a temporary file, if the save # succeeded we just rename the temporary file to the final file name # and remove it. if self.safe_save: tmp_path = path + '~' else: tmp_path = path try: _logger().debug('saving editor content to temp file: %s', path) with open(tmp_path, 'w', encoding=encoding) as file: file.write(plain_text) except UnicodeEncodeError: # fallback to utf-8 in case of error. with open(tmp_path, 'w', encoding=fallback_encoding) as file: file.write(plain_text) except (IOError, OSError) as e: self._rm(tmp_path) self.saving = False self.editor.text_saved.emit(str(path)) raise e else: _logger().debug('save to temp file succeeded') Cache().set_file_encoding(path, encoding) self._encoding = encoding if self.safe_save: # remove path and rename temp file, if safe save is on _logger().debug('rename %s to %s', tmp_path, path) self._rm(path) os.rename(tmp_path, path) self._rm(tmp_path) # reset dirty flags self.editor._original_text = plain_text self.editor.document().setModified(False) # remember path for next save self._path = os.path.normpath(path) # reset selection if self.clean_trailing_whitespaces: if sel_start != sel_end: self._reset_selection(sel_end, sel_start) self.editor.text_saved.emit(str(path)) self.saving = False
def open(self, path, encoding=None, use_cached_encoding=True): """ Open a file and set its content on the editor widget. pyqode does not try to guess encoding. It's up to the client code to handle encodings. You can either use a charset detector to detect encoding or rely on a settings in your application. It is also up to you to handle UnicodeDecodeError, unless you've added class:`pyqode.core.panels.EncodingPanel` on the editor. pyqode automatically caches file encoding that you can later reuse it automatically. :param path: Path of the file to open. :param encoding: Default file encoding. Default is to use the locale encoding. :param use_cached_encoding: True to use the cached encoding instead of ``encoding``. Set it to True if you want to force reload with a new encoding. :raises: UnicodeDecodeError in case of error if no EncodingPanel were set on the editor. """ ret_val = False if encoding is None: encoding = locale.getpreferredencoding() self.opening = True settings = Cache() self._path = path # get encoding from cache if use_cached_encoding: try: cached_encoding = settings.get_file_encoding(path) except KeyError: pass else: encoding = cached_encoding enable_modes = os.path.getsize(path) < self._limit for m in self.editor.modes: m.enabled = enable_modes if not enable_modes: self.editor.modes.clear() # open file and get its content try: with open(path, 'Ur', encoding=encoding) as file: content = file.read() if self.autodetect_eol: self._eol = file.newlines if isinstance(self._eol, tuple): self._eol = self._eol[0] if self._eol is None: # empty file has no newlines self._eol = self.EOL.string(self.preferred_eol) else: self._eol = self.EOL.string(self.preferred_eol) except (UnicodeDecodeError, UnicodeError) as e: try: from pyqode.core.panels import EncodingPanel panel = self.editor.panels.get(EncodingPanel) except KeyError: raise e # panel not found, not automatic error management else: panel.on_open_failed(path, encoding) else: # success! Cache the encoding settings.set_file_encoding(path, encoding) self._encoding = encoding # replace tabs by spaces if self.replace_tabs_by_spaces: content = content.replace("\t", " " * self.editor.tab_length) # set plain text self.editor.setPlainText(content, self.get_mimetype(path), self.encoding) self.editor.setDocumentTitle(self.editor.file.name) ret_val = True _logger().debug('file open: %s', path) self.opening = False if self.restore_cursor: self._restore_cached_pos() return ret_val
def test_cached_encodings(): s = Cache(suffix='-pytest') s.clear() # not in cache with pytest.raises(KeyError): s.get_file_encoding( os.path.join(os.getcwd(), 'test', 'files', 'big5hkscs.txt')) s.set_file_encoding(__file__, 'utf_16') s = Cache(suffix='-pytest') assert s.get_file_encoding(__file__) == 'utf_16'
def _restore_cached_pos(self): pos = Cache().get_cursor_position(self.path) tc = self.editor.textCursor() tc.setPosition(pos) self.editor.setTextCursor(tc) QtCore.QTimer.singleShot(1, self.editor.centerCursor)
def save(self, path=None, encoding=None, fallback_encoding=None): """ Save the editor content to a file. :param path: optional file path. Set it to None to save using the current path (save), set a new path to save as. :param encoding: optional encoding, will use the current file encoding if None. :param fallback_encoding: Fallback encoding to use in case of encoding error. None to use the locale preferred encoding """ if not self.editor.dirty and \ (encoding is None or encoding == self.encoding) and \ (path is None or path == self.path): return if fallback_encoding is None: fallback_encoding = locale.getpreferredencoding() _logger().debug("saving %r to %r with %r encoding", self.path, path, encoding) if path is None: if self.path: path = self.path else: _logger().debug( 'failed to save file, path argument cannot be None if ' 'FileManager.path is also None') return False # use cached encoding if None were specified if encoding is None: encoding = self._encoding self.saving = True self.editor.text_saving.emit(str(path)) # perform a safe save: we first save to a temporary file, if the save # succeeded we just rename the temporary file to the final file name # and remove it. if self.safe_save: tmp_path = path + '~' else: tmp_path = path try: _logger().debug('saving editor content to temp file: %s', path) with open(tmp_path, 'wb') as file: file.write(self._get_text(encoding)) except UnicodeEncodeError: # fallback to utf-8 in case of error. with open(tmp_path, 'wb') as file: file.write(self._get_text(fallback_encoding)) except (IOError, OSError) as e: self._rm(tmp_path) self.saving = False self.editor.text_saved.emit(str(path)) raise e else: # cache update encoding Cache().set_file_encoding(path, encoding) self._encoding = encoding # remove path and rename temp file, if safe save is on if self.safe_save: _logger().debug('rename %s to %s', tmp_path, path) self._rm(path) os.rename(tmp_path, path) self._rm(tmp_path) # reset dirty flags self.editor.document().setModified(False) # remember path for next save self._path = os.path.normpath(path) self.editor.text_saved.emit(str(path)) self.saving = False _logger().debug('file saved: %s', path)
def _restore_cached_pos(self): pos = Cache().get_cursor_position(self.path) TextHelper(self.editor).goto_line(pos[0], pos[1]) QtCore.QTimer.singleShot(1, self.editor.centerCursor)
def open(self, path, encoding=None, use_cached_encoding=True): """ Open a file and set its content on the editor widget. pyqode does not try to guess encoding. It's up to the client code to handle encodings. You can either use a charset detector to detect encoding or rely on a settings in your application. It is also up to you to handle UnicodeDecodeError, unless you've added class:`pyqode.core.panels.EncodingPanel` on the editor. pyqode automatically caches file encoding that you can later reuse it automatically. :param path: Path of the file to open. :param encoding: Default file encoding. Default is to use the locale encoding. :param use_cached_encoding: True to use the cached encoding instead of ``encoding``. Set it to True if you want to force reload with a new encoding. :raises: UnicodeDecodeError in case of error if no EncodingPanel were set on the editor. """ ret_val = False if encoding is None: encoding = locale.getpreferredencoding() self.opening = True settings = Cache() self._path = path # get encoding from cache if use_cached_encoding: try: cached_encoding = settings.get_file_encoding(path) except KeyError: pass else: encoding = cached_encoding # open file and get its content try: with open(path, 'Ur', encoding=encoding) as file: content = file.read() if self.autodetect_eol: self._eol = file.newlines if isinstance(self._eol, tuple): self._eol = self._eol[0] if self._eol is None: # empty file has no newlines self._eol = self.EOL.string(self.preferred_eol) else: self._eol = self.EOL.string(self.preferred_eol) except (UnicodeDecodeError, UnicodeError) as e: try: from pyqode.core.panels import EncodingPanel panel = self.editor.panels.get(EncodingPanel) except KeyError: raise e # panel not found, not automatic error management else: panel.on_open_failed(path, encoding) else: # success! Cache the encoding settings.set_file_encoding(path, encoding) self._encoding = encoding # replace tabs by spaces if self.replace_tabs_by_spaces: content = content.replace("\t", " " * self.editor.tab_length) # set plain text self.editor.setPlainText( content, self.get_mimetype(path), self.encoding) self.editor.setDocumentTitle(self.editor.file.name) ret_val = True self.opening = False if self.restore_cursor: self._restore_cached_pos() return ret_val