Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redesign file switcher (a la Sublime Text) #2590

Merged
merged 61 commits into from
Aug 20, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
2fbe8f5
bugfix - fileListDialog signal/handler name clash
Apr 20, 2015
7d2a745
feature - fileListDialog live switch files and cancel switch
Apr 20, 2015
c07f5ef
bugfix - previous
Apr 20, 2015
b2a88c2
feature - fileListDialog up/down keys work from text
Apr 20, 2015
45f7177
style - fileListDialog frameless
Apr 20, 2015
6d4830f
feature - fileListDialog focus set on text
Apr 20, 2015
451f0b4
feature - fileListDialog highlights matching filter text
Apr 20, 2015
4917e27
fix - editor menu update action label
Apr 20, 2015
49b9a17
style - fileListDialog positioned at top of editor
Apr 20, 2015
6713075
clean up dirty code
Apr 21, 2015
4a6dd21
feature - fileListDialog case insensitive matching and...
Apr 21, 2015
81dacc6
style - fileListDialog alpha transparency
Apr 21, 2015
a617c72
bugfix - fileListDialog initial selection
Apr 21, 2015
46243d9
trying to make global shortcut
Apr 21, 2015
e625d13
global shortcut works
Apr 21, 2015
df80f06
fileListDialog misc...
Apr 21, 2015
5dc1304
feature - fileListDialog uses ";" for goto line
Apr 21, 2015
bc9207e
improve previous - make goto line cancelable
Apr 21, 2015
4cc92a7
bugfix - launch file switcher for current editor stack not 0th
Apr 21, 2015
bc6452a
remove old gotoline action
Apr 21, 2015
5fb24d7
hint label - reinstated/new hint in fileListDialog
Apr 21, 2015
d4a4663
feature - can close tabs from fileListDialog
Apr 22, 2015
699dd52
refactor previous - now using path str rather than tab idx
Apr 22, 2015
8959adf
bugfix - linenum now canceled when moving off from a given tab
Apr 22, 2015
87e2928
fix regression
Apr 22, 2015
1be4b27
first draft - nearly working
Apr 22, 2015
41b987d
bugfix previous
Apr 23, 2015
8904e94
style - file info on same line & larger width
Apr 23, 2015
a784c5d
bugfix - path works with only dir
Apr 23, 2015
289bf59
simplify tokenising of path
Apr 23, 2015
ad995c0
bugfix previous
Apr 23, 2015
c8c16f7
feature - display line numbers on request
Apr 23, 2015
640227e
style - path on separate line with smaller font
Apr 23, 2015
57f0de4
refactor - file_switcher now in own file
Apr 23, 2015
dec3d8c
restore goto line stuff
Apr 23, 2015
ce0384e
remove print shortcut (previous commit started this)
Apr 23, 2015
e850671
clean up previous commits and bugfix
Apr 23, 2015
cfa55fd
converted hint label to hint tooltip with button
Apr 23, 2015
9da4991
pep8 is the boss
Apr 26, 2015
9f82939
cleanup- len=0, iter, sig_*, mv HelperToolButton
May 1, 2015
a8721af
remove print shortcut (previous commit started this)
Apr 23, 2015
c1537e8
fix rebase
Jun 15, 2015
2b4ab6c
hide path for unsaved files and fix rebase
Jun 15, 2015
650137a
close on focusOut
Jun 15, 2015
a21b5b4
config - fix rebase and bump version
Jun 15, 2015
3cf512b
move helperwidget styling to class def
Jun 15, 2015
5ee06ff
remove redundant handler
Jun 15, 2015
a25ef8a
fuzzy matching
Jun 17, 2015
a3eb679
Fix config
goanpeca Aug 3, 2015
9dfd346
Remove HTMLDelegate, use the on in helperwidgets
goanpeca Aug 3, 2015
46ac6f5
Fix comments and some docstrings
goanpeca Aug 3, 2015
38e5265
Fix uppercase marching
goanpeca Aug 3, 2015
0a644b2
Add fuzzy ordered search and size autoadjust
goanpeca Aug 3, 2015
f92f2ba
Add symbol search, refactor codebase
goanpeca Aug 10, 2015
b87f999
Add new style config
goanpeca Aug 10, 2015
56b19d5
Upadte string matching and update shortcuts editor with change
goanpeca Aug 10, 2015
62e7933
Clean up and refactor code
goanpeca Aug 15, 2015
86d8b88
Fix style and add errased code
goanpeca Aug 15, 2015
d75eb09
Fix tab error
goanpeca Aug 16, 2015
72b6969
Fix py3 compatibility issues
goanpeca Aug 17, 2015
b41e5d5
Fix bug with cells in outline explorer data
goanpeca Aug 17, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions spyderlib/config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,9 +534,9 @@ def is_ubuntu():
# -- In widgets/editor
'editor/inspect current object': 'Ctrl+I',
'editor/go to line': 'Ctrl+L',
'editor/file list management': 'Ctrl+E',
'editor/go to previous file': 'Ctrl+Tab',
'editor/go to next file': 'Ctrl+Shift+Tab',
'_/file switcher': 'Ctrl+P',
# -- In spyder.py
'editor/find text': "Ctrl+F",
'editor/find next': "F3",
Expand All @@ -557,7 +557,6 @@ def is_ubuntu():
'editor/save file': "Ctrl+S",
'editor/save all': "Ctrl+Alt+S",
'editor/save as': 'Ctrl+Shift+S',
'editor/print': "Ctrl+P",
'editor/close all': "Ctrl+Shift+W",
'editor/breakpoint': 'F12',
'editor/conditional breakpoint': 'Shift+F12',
Expand Down Expand Up @@ -742,7 +741,7 @@ def is_ubuntu():
# 2. If you want to *remove* options that are no longer needed in our codebase,
# you need to do a MAJOR update in version, e.g. from 3.0.0 to 4.0.0
# 3. You don't need to touch this value if you're just adding a new option
CONF_VERSION = '20.0.0'
CONF_VERSION = '21.0.0'

# XXX: Previously we had load=(not DEV) here but DEV was set to *False*.
# Check if it *really* needs to be updated or not
Expand Down
2 changes: 0 additions & 2 deletions spyderlib/plugins/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,8 +665,6 @@ def get_plugin_actions(self):
self.print_action = create_action(self, _("&Print..."),
icon=ima.icon('print'), tip=_("Print current file..."),
triggered=self.print_file)
self.register_shortcut(self.print_action, context="Editor",
name="Print")
# Shortcut for close_action is defined in widgets/editor.py
self.close_action = create_action(self, _("&Close"),
icon=ima.icon('fileclose'), tip=_("Close current file"),
Expand Down
3 changes: 2 additions & 1 deletion spyderlib/plugins/shortcuts.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ def __init__(self, parent):
self.shortcuts = []
self.scores = []
self.rich_text = []
self.normal_text = []
self.letters = ''
self.label = QLabel()
self.widths = []
Expand Down Expand Up @@ -545,7 +546,7 @@ def update_search_letters(self, text):
self.letters = text
names = [shortcut.name for shortcut in self.shortcuts]
results = get_search_scores(text, names, template='<b>{0}</b>')
self.rich_text, self.scores = zip(*results)
self.normal_text, self.rich_text, self.scores = zip(*results)
self.reset()

def update_active_row(self):
Expand Down
43 changes: 42 additions & 1 deletion spyderlib/py3compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@

from __future__ import print_function

import sys
import operator
import os
import sys

PY2 = sys.version[0] == '2'
PY3 = sys.version[0] == '3'
Expand Down Expand Up @@ -243,6 +244,46 @@ def qbytearray_to_str(qba):
"""Convert QByteArray object to str in a way compatible with Python 2/3"""
return str(bytes(qba.toHex().data()).decode())

# =============================================================================
# Dict funcs
# =============================================================================
if PY3:
def iterkeys(d, **kw):
return iter(d.keys(**kw))

def itervalues(d, **kw):
return iter(d.values(**kw))

def iteritems(d, **kw):
return iter(d.items(**kw))

def iterlists(d, **kw):
return iter(d.lists(**kw))

viewkeys = operator.methodcaller("keys")

viewvalues = operator.methodcaller("values")

viewitems = operator.methodcaller("items")
else:
def iterkeys(d, **kw):
return d.iterkeys(**kw)

def itervalues(d, **kw):
return d.itervalues(**kw)

def iteritems(d, **kw):
return d.iteritems(**kw)

def iterlists(d, **kw):
return d.iterlists(**kw)

viewkeys = operator.methodcaller("viewkeys")

viewvalues = operator.methodcaller("viewvalues")

viewitems = operator.methodcaller("viewitems")


if __name__ == '__main__':
pass
13 changes: 12 additions & 1 deletion spyderlib/spyder.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,14 @@ def setup(self):
context=Qt.WidgetShortcut)
self.register_shortcut(self.replace_action, "Editor",
"Replace text")

self.file_switcher_action = create_action(self, _('File switcher...'),
icon=ima.icon('filelist'),
tip=_('Fast switch between files'),
triggered=self.call_file_switcher,
context=Qt.ApplicationShortcut)
self.register_shortcut(self.file_switcher_action, "_",
"file switcher")
self.file_menu_actions.append(self.file_switcher_action)
def create_edit_action(text, tr_text, icon):
textseq = text.split(' ')
method_name = textseq[0].lower()+"".join(textseq[1:])
Expand Down Expand Up @@ -2459,6 +2466,10 @@ def global_callback(self):
if isinstance(widget, TextEditBaseWidget):
getattr(widget, callback)()

def call_file_switcher(self):
if self.editor.editorstacks:
self.editor.get_current_editorstack().open_fileswitcher_dlg()

def redirect_internalshell_stdio(self, state):
if state:
self.console.shell.interpreter.redirect_stds()
Expand Down
46 changes: 30 additions & 16 deletions spyderlib/utils/stringmatching.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@


NOT_FOUND_SCORE = -1
NO_SCORE = 0


def get_search_regex(query, ignore_case=True):
Expand Down Expand Up @@ -80,20 +81,22 @@ def get_search_score(query, choice, ignore_case=True, apply_regex=True,
- Letters in one word and no spaces with exact match.
Example: 'up' in 'up stroke'
- Letters in one word and no spaces with partial match.
Example: 'up' in 'upstream stroke'
Example: 'up' in 'upstream stroke'
- Letters in one word but with skip letters.
Example: 'cls' in 'close up'
- Letters in two or more words.
Example: 'cls' in 'car lost'
Example: 'cls' in 'close up'
- Letters in two or more words
Example: 'cls' in 'car lost'
"""
result = (choice, NOT_FOUND_SCORE)

original_choice = choice
result = (original_choice, NOT_FOUND_SCORE)

# Handle empty string case
if not query:
return result

if ignore_case:
query = query.lower()
choice = choice.lower()

if apply_regex:
pattern = get_search_regex(query, ignore_case=ignore_case)
Expand All @@ -109,9 +112,14 @@ def get_search_score(query, choice, ignore_case=True, apply_regex=True,
partial_words = [query in word for word in choice.split(u' ')]

if any(exact_words) or any(partial_words):
score += choice.find(query)
pos_start = choice.find(query)
pos_end = pos_start + len(query)
score += pos_start
text = choice.replace(query, sep*len(query), 1)
enriched_text = choice.replace(query, template.format(query), 1)

enriched_text = original_choice[:pos_start] +\
template.format(original_choice[pos_start:pos_end]) +\
original_choice[pos_end:]

if any(exact_words):
# Check if the query words exists in a word with exact match
Expand All @@ -121,17 +129,18 @@ def get_search_score(query, choice, ignore_case=True, apply_regex=True,
score += 100
else:
# Check letter by letter
text = [l for l in choice]
text = [l for l in original_choice]
if ignore_case:
temp_text = text[:]
temp_text = [l.lower() for l in original_choice]
else:
temp_text = [l.lower() for l in choice]
temp_text = text[:]

# Give points to start of string
score += temp_text.index(query[0])

# Find the query letters and replace them by `sep`, also apply
# template as needed for enricching the letters in the text
enriched_text = temp_text[:]
enriched_text = text[:]
for char in query:
if char != u'' and char in temp_text:
index = temp_text.index(char)
Expand Down Expand Up @@ -164,13 +173,13 @@ def get_search_score(query, choice, ignore_case=True, apply_regex=True,
score += pat.count(u' ')*10000
score += pat.count(let)*100

return enriched_text, score
return original_choice, enriched_text, score


def get_search_scores(query, choices, ignore_case=True, template='{}',
valid_only=False, sort=False):
"""Search for query inside choices and return a list of tuples.

Returns a list of tuples of text with the enriched text (if a template is
provided) and a score for the match. Lower scores imply a better match.

Expand All @@ -193,16 +202,21 @@ def get_search_scores(query, choices, ignore_case=True, template='{}',
List of tuples where the first item is the text (enriched if a
template was used) and a search score. Lower scores means better match.
"""
# First remove spaces from query
query = query.replace(' ', '')
pattern = get_search_regex(query, ignore_case)
results = []

for choice in choices:
r = re.search(pattern, choice)
if r:
if query and r:
result = get_search_score(query, choice, ignore_case=ignore_case,
apply_regex=False, template=template)
else:
result = (choice, NOT_FOUND_SCORE)
if query:
result = (choice, choice, NOT_FOUND_SCORE)
else:
result = (choice, choice, NO_SCORE)

if valid_only:
if result[-1] != NOT_FOUND_SCORE:
Expand Down
Loading