Skip to content

Commit

Permalink
fix: edge-case switchable misses
Browse files Browse the repository at this point in the history
  • Loading branch information
gerardroche committed Jan 26, 2024
1 parent 662827e commit fe6943b
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 143 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.

## 3.19.2 - Unreleased

### Fixed

- Edge-case switchable misses

## 3.19.1 - 2024-01-23

### Fixed
Expand Down
207 changes: 119 additions & 88 deletions lib/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,126 +342,157 @@ def file_encoded_position(self, view):
return file + encoded_postion


def find_switchable(view, on_select=None):
# Args:
# view (View)
# on_select (callable)
#
# Returns:
# void
window = view.window()

if on_select is None:
raise ValueError('a callable is required')

file = view.file_name()
debug_message('switch: %s', file)
def _get_switchable_lookup_symbols(window, classes: list) -> list:
locations = [] # type: list
for _class in classes:
if _class['class'][-4:] == 'Test':
symbol = _class['class'][:-4]
else:
symbol = _class['class'] + 'Test'

classes = find_php_classes(view, with_namespace=True)
if len(classes) == 0:
return message('could not find a test case or class under test for %s', file)
locations += window.lookup_symbol_in_index(symbol)

debug_message('found %s class in switch: %s', len(classes), classes)
locations = _unique_lookup_symbols(locations)

locations = [] # type: list
for _class in classes:
class_name = _class['class']
return locations

if class_name[-4:] == 'Test':
symbol = class_name[:-4]
else:
symbol = class_name + 'Test'

symbol_locations = window.lookup_symbol_in_index(symbol)
locations += symbol_locations
def find_switchable(view, on_select) -> None:
window = view.window()

debug_message('found %s possible locations to switch to: %s', len(locations), locations)
classes = find_php_classes(view, with_namespace=True)
debug_message('found %s class(s): %s in %s', len(classes), classes, view.file_name() if view.file_name() else view)

def unique_locations(locations):
locs = []
seen = set() # type: set
for location in locations:
if location[0] not in seen:
seen.add(location[0])
locs.append(location)
if not len(classes):
message('Could not find a class in %s', view.file_name() if view.file_name() else view)
return

return locs
lookup_symbols = _get_switchable_lookup_symbols(window, classes)
debug_message('found %s lookup symbol(s): %s', len(lookup_symbols), lookup_symbols)

locations = unique_locations(locations)
if not len(lookup_symbols):
message('Could not find a switchable for %s', view.file_name() if view.file_name() else view)
return

if len(locations) == 0:
if has_test(view):
return message('could not find class under test for %s', file)
else:
return message('could not find test case for %s', file)
def _on_select(index: int) -> None:
if index >= 0:
on_select(Switchable(lookup_symbols[index]))

def _on_select(index):
if index == -1:
return
if len(lookup_symbols) == 1:
_on_select(0)
return

switchable = Switchable(locations[index])
lookup_symbols, is_exact = _find_switchable_in_lookup_symbols(view.file_name(), lookup_symbols)

if on_select is not None:
on_select(switchable)
debug_message('found %d lookup symbol(s): %s', len(lookup_symbols), lookup_symbols)

if len(locations) == 1:
if is_exact and len(lookup_symbols) == 1:
return _on_select(0)

locations, is_exact = refine_switchable_locations(locations=locations, file=file)
window.show_quick_panel(['{}:{}'.format(loc[1], loc[2][0]) for loc in lookup_symbols], _on_select)

debug_message('is_exact=%s', is_exact)
debug_message('locations(%s)=%s', len(locations), locations)

if is_exact and len(locations) == 1:
return _on_select(0)
def _unique_lookup_symbols(locations: list) -> list:
locs = []
seen = set() # type: set
for location in locations:
if location[0] not in seen:
seen.add(location[0])
locs.append(location)

window.show_quick_panel(['{}:{}'.format(loc[1], loc[2][0]) for loc in locations], _on_select)
return locs


def refine_switchable_locations(locations, file):
debug_message('refine location')
def _find_switchable_in_lookup_symbols(file, lookup_symbols: list) -> tuple:
if not file:
return locations, False
return lookup_symbols, False

debug_message('file=%s', file)
debug_message('locations=%s', locations)
switchable_files, is_test = _get_switchable_files(file)
debug_message('switchable_files=%s', switchable_files)

files = []
if file.endswith('Test.php'):
file_is_test_case = True
file = file.replace('Test.php', '.php')
files.append(re.sub('(\\/)?[tT]ests\\/([uU]nit\\/)?', '/', file))
files.append(re.sub('(\\/)?[tT]ests\\/', '/src/', file))
files.append(re.sub('(\\/)?[tT]ests\\/Unit/', '/app/', file))
files.append(re.sub('(\\/)?[tT]ests\\/Integration/', '/app/', file))
else:
file_is_test_case = False
file = file.replace('.php', 'Test.php')
files.append(file)
files.append(re.sub('(\\/)?app\\/', '/tests/', file))
files.append(re.sub('(\\/)?app\\/', '/tests/Unit/', file))
files.append(re.sub('(\\/)?app\\/', '/tests/Integration/', file))
files.append(re.sub('(\\/)?src\\/', '/', file))
files.append(re.sub('(\\/)?src\\/', '/test/', file))

files = list(set(files))
debug_message('files=%s', files)

if len(locations) > 1:
common_prefix = os.path.commonprefix([loc[0] for loc in locations])
# print('->', file)
# for f in switchable_files:
# print('=>', f)
# print('')
# for lookup_symbol in lookup_symbols:
# print('=>', lookup_symbol[0], lookup_symbol[1])

for switchable_file in switchable_files:
for lookup_symbol in lookup_symbols:
if lookup_symbol[0] == switchable_file:
return [lookup_symbol], True

if len(lookup_symbols) > 1:
common_prefix = os.path.commonprefix([loc[0] for loc in lookup_symbols])
if common_prefix != '/':
files = [file.replace(common_prefix, '') for file in files]
switchable_files = [file.replace(common_prefix, '') for file in switchable_files]

for location in locations:
for location in lookup_symbols:
loc_file = location[0]
if not file_is_test_case:
if not is_test:
loc_file = re.sub('\\/[tT]ests\\/([uU]nit\\/)?', '/', loc_file)

for file in files:
for file in switchable_files:
if loc_file.endswith(file):
return [location], True

return locations, False
return lookup_symbols, False


def _get_switchable_files(file: str) -> tuple:
switchable_files = []

if file.endswith('Test.php'):
is_test = True
file = file.replace('Test.php', '.php')

switchable_file = re.sub('(\\/)?[tT]ests\\/([uU]nit\\/)?', '/', file, count=1)
if switchable_file not in switchable_files:
switchable_files.append(switchable_file)
switchable_file = re.sub('(\\/)?[tT]ests\\/Unit/', '/app/', file, count=1)
if switchable_file not in switchable_files:
switchable_files.append(switchable_file)
switchable_file = re.sub('(\\/)?[tT]ests\\/Integration/', '/app/', file, count=1)
if switchable_file not in switchable_files:
switchable_files.append(switchable_file)
switchable_file = re.sub('(\\/)?[tT]ests\\/', '/src/', file, count=1)
if switchable_file not in switchable_files:
switchable_files.append(switchable_file)

else:
is_test = False
file = file.replace('.php', 'Test.php')

# app/
switchable_file = re.sub('app\\/(?!.*app\\/)', 'tests/', file, count=1)
if switchable_file not in switchable_files:
switchable_files.append(switchable_file)
switchable_file = re.sub('app\\/(?!.*app\\/)', 'tests/Unit/', file, count=1)
if switchable_file not in switchable_files:
switchable_files.append(switchable_file)
switchable_file = re.sub('app\\/(?!.*app\\/)', 'tests/Integration/', file, count=1)
if switchable_file not in switchable_files:
switchable_files.append(switchable_file)
switchable_file = re.sub('app\\/(?!.*app\\/)', '', file, count=1)
if switchable_file not in switchable_files:
switchable_files.append(switchable_file)
switchable_file = re.sub('app\\/(?!.*app\\/)', 'test/', file, count=1)
if switchable_file not in switchable_files:
switchable_files.append(switchable_file)

# src/
switchable_file = re.sub('(\\/)?src\\/', '/', file, count=1)
if switchable_file not in switchable_files:
switchable_files.append(switchable_file)
switchable_file = re.sub('(\\/)?src\\/', '/test/', file, count=1)
if switchable_file not in switchable_files:
switchable_files.append(switchable_file)

# 1:1
if file not in switchable_files:
switchable_files.append(file)

return switchable_files, is_test


def put_views_side_by_side(view_a, view_b) -> None:
Expand Down
Loading

0 comments on commit fe6943b

Please sign in to comment.