Sublime Forum

Seeking Recommendations: Identifying Python Functions/Classes with Single References for Code Refactoring

#1

Before developing a custom plugin, could you kindly suggest an existing package or solution that facilitates identifying Python functions or classes with only one reference? This would greatly assist in refactoring and improving the organization of large codebases.

Thanks!

0 Likes

#2

I’ve come across a related thread (Show number of references of a function beside its name) with no response. I believe adding the ability to show/hide references in the view could be valuable for assessing dead code, optimizing codebase structure, and more. Your insights on this would be appreciated.

Manually inspecting the code by hovering the definitions is not ideal when dealing with large codebases or major refactorings

0 Likes

#3

Since you didn’t mention it, so LSP + LSP-pyright is my recommendation.

1 Like

#5

I’ve developed a straightforward solution that sorts symbols by the number of references and facilitates navigation to them. However, I’ve encountered an issue where it doesn’t navigate to the specified column as expected. If anyone is interested in trying it out and making improvements, your contributions are more than welcome! Additionally, it would be great to have the ability to navigate to the definitions. Feel free to share your insights and collaborate on enhancing this solution. Your assistance and suggestions are highly appreciated! :blush:

import sublime
import sublime_plugin


class OutputPanel:
    def __init__(self, window, file_regex=""):
        name = "exec"

        view = window.create_output_panel(name)
        view.settings().set("result_file_regex", file_regex)
        view.settings().set("result_line_regex", "")
        view.settings().set("result_base_dir", "")
        view.settings().set("word_wrap", True)
        view.settings().set("line_numbers", False)
        view.settings().set("gutter", False)
        view.settings().set("scroll_past_end", False)
        view.assign_syntax("Packages/Text/Plain text.tmLanguage")
        window.run_command("show_panel", {"panel": f"output.{name}"})

        self.window = window
        self.view = view

    def print(self, *args, sep=" ", end="\n"):
        characters = self._to_str(*args, sep=sep, end=end)
        self._append_string(characters)
        return characters

    # -------- Private --------
    def _to_str(self, *args, sep=" ", end="\n"):
        characters = f"{sep.join([str(a) for a in args])}{end}"
        return characters

    def _append_string(self, characters):
        self.view.run_command(
            "append", {"characters": characters, "force": True, "scroll_to_end": True}
        )


class ShowReferencesCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        self.output_panel = OutputPanel(
            window=self.view.window(), file_regex=r"(.*?\.py):(.*?):(.*?)"
        )

        symbol_references = {}

        for region in self.view.sel():
            symbol, locations = self._reference_at_point(region)
            symbol_references[symbol] = locations

        sorted_symbols = sorted(symbol_references.items(), key=lambda x: len(x[1]))

        for symbol, locations in sorted_symbols:
            self._print(f"{symbol}: ({len(locations)})")

            sorted_locations = sorted(locations, key=lambda loc: (loc.row, loc.col))

            for loc in sorted_locations:
                self._print_location(loc.path, loc.row, loc.col)
            self._print()

    # -------- Private --------
    def _print(self, *args, **kwargs):
        self.output_panel.print(*args, **kwargs)

    def _print_location(self, file, line, col):
        self._print(f"{file}:{line}:{col}")

    def _lookup_references(self, symbol):
        return self.view.window().symbol_locations(
            symbol, sublime.SYMBOL_SOURCE_ANY, sublime.SYMBOL_TYPE_REFERENCE
        )

    def _reference_at_point(self, region):
        symbol = self.view.substr(self.view.word(region.a))
        locations = self._lookup_references(symbol)
        return symbol, locations
0 Likes

#6

I think the more general file_regex=r"^(.*):(\d+):(\d+)" is the better fit. And you should choose your own panel name. “exec” is used by Sublime itself. Maybe name = "references"?

1 Like

#7

I do agree and you’re absolutely right… thanks for the feedback!

0 Likes