File: C:/Users/fred/anaconda3/Lib/site-packages/spyder/plugins/editor/utils/switcher.py
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)
"""
Editor Switcher manager.
"""
# Standard library imports
import os.path as osp
# Local imports
from spyder.config.base import _
from spyder.config.manager import CONF
from spyder.utils.icon_manager import ima
from spyder.utils.switcher import shorten_paths, get_file_icon
from spyder.plugins.completion.api import SymbolKind, SYMBOL_KIND_ICON
class EditorSwitcherManager(object):
"""
Switcher instance manager to handle base modes for an Editor.
Symbol mode -> '@'
Line mode -> ':'
Files mode -> ''
"""
SYMBOL_MODE = '@'
LINE_MODE = ':'
FILES_MODE = ''
def __init__(self, plugin, switcher_instance, get_codeeditor,
get_editorstack, section=_("Editor")):
"""
'get_codeeditor' and 'get_editorstack' params should be callables
to get the current CodeEditor or EditorStack instance as needed.
As an example:
current_codeeditor = get_codeditor()
current_editorstack = get_editorstack()
"""
self._plugin = plugin
self._switcher = switcher_instance
self._editor = get_codeeditor
self._editorstack = get_editorstack
self._section = section
self._current_line = None
self.setup_switcher()
def setup_switcher(self):
"""Setup switcher modes and signals."""
self._switcher.add_mode(self.LINE_MODE, _('Go to Line'))
self._switcher.add_mode(self.SYMBOL_MODE, _('Go to Symbol in File'))
self._switcher.sig_mode_selected.connect(self.handle_switcher_modes)
self._switcher.sig_item_selected.connect(
self.handle_switcher_selection)
self._switcher.sig_text_changed.connect(self.handle_switcher_text)
self._switcher.sig_rejected.connect(self.handle_switcher_rejection)
self._switcher.sig_item_changed.connect(
self.handle_switcher_item_change)
def handle_switcher_modes(self, mode):
"""Handle switcher for registered modes."""
if mode == self.SYMBOL_MODE:
self.create_symbol_switcher()
elif mode == self.LINE_MODE:
self.create_line_switcher()
elif mode == self.FILES_MODE:
# Each plugin that wants to attach to the switcher should do this?
self.create_editor_switcher()
def create_editor_switcher(self):
"""Populate switcher with open files."""
self._switcher.set_placeholder_text(
_('Start typing the name of an open file'))
editorstack = self._editorstack()
paths = [data.filename.lower()
for data in editorstack.data]
save_statuses = [data.newly_created
for data in editorstack.data]
short_paths = shorten_paths(paths, save_statuses)
for idx, data in enumerate(editorstack.data):
path = data.filename
title = osp.basename(path)
icon = get_file_icon(path)
# TODO: Handle of shorten paths based on font size
# and available space per item
if len(paths[idx]) > 75:
path = short_paths[idx]
else:
path = osp.dirname(data.filename.lower())
last_item = idx + 1 == len(editorstack.data)
self._switcher.add_item(title=title,
description=path,
icon=icon,
section=self._section,
data=data,
last_item=last_item)
def create_line_switcher(self):
"""Populate switcher with line info."""
editor = self._editor()
editorstack = self._editorstack()
self._current_line = editor.get_cursor_line_number()
self._switcher.clear()
self._switcher.set_placeholder_text(_('Select line'))
data = editorstack.get_current_finfo()
path = data.filename
title = osp.basename(path)
lines = data.editor.get_line_count()
icon = get_file_icon(path)
line_template_title = u"{title} [{lines} {text}]"
title = line_template_title.format(title=title, lines=lines,
text=_("lines"))
description = _('Go to line')
self._switcher.add_item(title=title,
description=description,
icon=icon,
section=self._section,
data=data,
action_item=True)
def create_symbol_switcher(self):
"""Populate switcher with symbol info."""
editor = self._editor()
language = editor.language
editor.update_whitespace_count(0, 0)
self._current_line = editor.get_cursor_line_number()
self._switcher.clear()
self._switcher.set_placeholder_text(_('Select symbol'))
oe_symbols = editor.oe_proxy.info or []
display_variables = CONF.get('outline_explorer', 'display_variables')
idx = 0
total_symbols = len(oe_symbols)
oe_symbols = sorted(
oe_symbols, key=lambda x: x['location']['range']['start']['line'])
for symbol in oe_symbols:
symbol_name = symbol['name']
symbol_kind = symbol['kind']
if language.lower() == 'python':
if symbol_kind == SymbolKind.MODULE:
total_symbols -= 1
continue
if (symbol_kind == SymbolKind.VARIABLE and
not display_variables):
total_symbols -= 1
continue
if symbol_kind == SymbolKind.FIELD and not display_variables:
total_symbols -= 1
continue
symbol_range = symbol['location']['range']
symbol_start = symbol_range['start']['line']
fold_level = editor.leading_whitespaces[symbol_start]
space = ' ' * fold_level
formated_title = '{space}{title}'.format(title=symbol_name,
space=space)
icon = ima.icon(SYMBOL_KIND_ICON.get(symbol_kind, 'no_match'))
data = {'title': symbol_name,
'line_number': symbol_start + 1}
last_item = idx + 1 == total_symbols
self._switcher.add_item(title=formated_title,
icon=icon,
section=self._section,
data=data,
last_item=last_item)
idx += 1
# Needed to update fold spaces for items titles
self._switcher.setup()
def handle_switcher_selection(self, item, mode, search_text):
"""Handle item selection of the switcher."""
data = item.get_data()
if mode == '@':
self.symbol_switcher_handler(data)
elif mode == ':':
self.line_switcher_handler(data, search_text)
elif mode == '':
# Each plugin that wants to attach to the switcher should do this?
if item.get_section() == self._section:
self.editor_switcher_handler(data)
self._plugin.switch_to_plugin()
def handle_switcher_text(self, search_text):
"""Handle switcher search text for line mode."""
editorstack = self._editorstack()
mode = self._switcher.get_mode()
if mode == ':':
item = self._switcher.current_item()
self.line_switcher_handler(item.get_data(), search_text,
visible=True)
elif self._current_line and mode == '':
editorstack.go_to_line(self._current_line)
self._current_line = None
def handle_switcher_rejection(self):
"""Do actions when the Switcher is rejected."""
# Reset current cursor line
if self._current_line:
editorstack = self._editorstack()
editorstack.go_to_line(self._current_line)
self._current_line = None
def handle_switcher_item_change(self, current):
"""Handle item selection change."""
editorstack = self._editorstack()
mode = self._switcher.get_mode()
if mode == '@' and current is not None:
line_number = int(current.get_data()['line_number'])
editorstack.go_to_line(line_number)
def editor_switcher_handler(self, data):
"""Populate switcher with FileInfo data."""
editorstack = self._editorstack()
editorstack.set_current_filename(data.filename)
self._switcher.hide()
def line_switcher_handler(self, data, search_text, visible=False):
"""Handle line switcher selection."""
editorstack = self._editorstack()
editorstack.set_current_filename(data.filename)
line_number = search_text.split(':')[-1]
try:
line_number = int(line_number)
editorstack.go_to_line(line_number)
self._switcher.setVisible(visible)
# Closing the switcher
if not visible:
self._current_line = None
self._switcher.set_search_text('')
except Exception:
# Invalid line number
pass
def symbol_switcher_handler(self, data):
"""Handle symbol switcher selection."""
editorstack = self._editorstack()
line_number = data['line_number']
editorstack.go_to_line(int(line_number))
self._current_line = None
self._switcher.hide()
self._switcher.set_search_text('')